Fixes #69: Add append-only notes on equipment records#74
Conversation
Implementation-ready spec for arbitrary notes on Equipment (issue #69): an EquipmentNote model, service layer, add-note form/route, and a Notes section on the equipment detail page. Hardened through three adversarial review rounds — 12 dependency-ordered tasks and 12 Given/When/Then ACs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzHqw96wXCi9i6aK349yZa
Add an EquipmentNote child table, service layer, form, add-note route, and a Notes section on the equipment detail page. Notes are append-only, newest-first, attributed to an author (id + cached author_name) with a UTC timestamp, and gated by the existing doc-edit permission (staff always; technicians when tech_doc_edit_enabled), blocked on archived equipment. - Model EquipmentNote (+ Alembic migration) with a deterministic created_at DESC, id DESC ordering and an equipment_id index. - equipment_service.add_equipment_note / get_equipment_notes, with log_mutation auditing and a shared NOTE_MAX_LENGTH (5000) limit enforced authoritatively on stripped content. - EquipmentNoteForm validates stripped length so it never rejects a note the service would store. - add_note route re-renders the detail page (via _render_equipment_detail) on validation failure, preserving the typed note instead of discarding it. - Notes render with white-space: pre-wrap under Jinja autoescape (XSS-safe). - Migration downgrade drops the table directly (avoids MariaDB error 1553 on the FK-backing index). - Model, service, and view tests mirroring the external-links suite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzHqw96wXCi9i6aK349yZa
|
@claude please review this change |
|
Claude finished @jantman's task in 3m 40s —— View job Reviewing PR #74
SummaryThis is a clean, well-scoped implementation that closely follows the tech-spec at Verification performed:
Things I specifically checked and found solid:
Minor, non-blocking observations (not requesting changes):
No blocking issues. The PR does what it says: append-only, permission-gated, XSS-safe equipment notes, with thorough tests mirroring the existing |
There was a problem hiding this comment.
Pull request overview
Adds an append-only “Notes” capability to equipment records, including persistence, service-layer APIs, permission-gated creation, and UI rendering on the equipment detail page (newest-first), with comprehensive tests.
Changes:
- Introduces
EquipmentNotemodel + Alembic migration forequipment_notes(FK to equipment, optional author FK + cached author name, content, created_at, deterministic ordering). - Adds
add_equipment_note()/get_equipment_notes()service functions with length enforcement andlog_mutationauditing; wires a new POST route and detail-page rendering helper. - Adds an always-visible Notes section + add-note form to the equipment detail template, plus new model/service/view tests.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/test_views/test_equipment_views.py | Adds end-to-end view tests for note creation, permissions, archived blocking, escaping, and append-only behavior. |
| tests/test_services/test_equipment_service.py | Adds unit tests for note service validation, ordering, and mutation logging. |
| tests/test_models/test_equipment_note.py | Adds model tests for persistence, defaults, and relationships/backrefs. |
| migrations/versions/77b248bd052d_add_equipment_notes_table.py | Adds equipment_notes table with FKs + equipment_id index; downgrade drops table directly. |
| esb/views/equipment.py | Adds _render_equipment_detail helper and /equipment/<id>/notes POST handler; includes notes in detail context. |
| esb/templates/equipment/detail.html | Adds Notes card with add form (permission-gated) and newest-first list rendering with pre-wrap and autoescape. |
| esb/services/equipment_service.py | Adds NOTE_MAX_LENGTH constant plus note create/list service functions with auditing. |
| esb/models/equipment_note.py | Adds EquipmentNote model, relationships, and ordering/backref behavior. |
| esb/models/init.py | Registers EquipmentNote for import/discovery. |
| esb/forms/equipment_forms.py | Adds EquipmentNoteForm with stripped-length validation aligned to the service constant. |
| _bmad-output/implementation-artifacts/tech-spec-equipment-notes.md | Adds the feature tech spec and acceptance criteria context. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Code reviewNo issues found. Checked for bugs and CLAUDE.md compliance. |
Compare note_rules with sorted() instead of relying on app.url_map.iter_rules() iteration order, which is not an API guarantee. Addresses PR #74 review feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QzHqw96wXCi9i6aK349yZa
Summary
Adds free-form, append-only notes on equipment records (closes #69). Each note is attributed to an author with a UTC timestamp and shown newest-first in a new "Notes" section at the bottom of the equipment detail page.
Implemented from the tech-spec at
_bmad-output/implementation-artifacts/tech-spec-equipment-notes.md.Changes
EquipmentNote(equipment_notestable): FK to equipment, nullable author FK + cachedauthor_name,content,created_at; deterministiccreated_at DESC, id DESCordering and anequipment_idindex. Registered inmodels/__init__.py.add_equipment_note()/get_equipment_notes()withlog_mutationauditing and a sharedNOTE_MAX_LENGTH(5000) limit enforced authoritatively on stripped content.EquipmentNoteFormvalidates stripped length, so it never rejects a note the service would store._require_doc_edit), archived-blocked, 404/validation handling. On validation failureadd_notere-renders the detail page (new_render_equipment_detailhelper) so the typed note is preserved rather than discarded.white-space: pre-wrapunder Jinja autoescape (XSS-safe, nonl2br/|safe).77b248bd052d_add_equipment_notes_table.py. Downgrade drops the table directly to avoid MariaDB error 1553 on the FK-backing index.Testing
make lintclean; full suite: 1840 passed.Reviewer notes
🤖 Generated with Claude Code
https://claude.ai/code/session_01QzHqw96wXCi9i6aK349yZa