Conflicts resolved: (a) test_phase5_api.py — with lane G's capsule AND lane E's features/direct all landed, the CLI stub loop is empty, so test_cli_dispatches_stubs_gracefully is retired with a pointer comment to the real dispatch coverage (test_capsule.py / test_audio_features.py / test_director.py); (b) CHANGE_REQUESTS.md — lane G keeps the ratified CR-4; lane E's entry renumbered to CR-5 and updated for the merged reality. Merged tree: 189 passed (169 main + 21 lane E - 1 retired), frontend build clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
7.9 KiB
7.9 KiB
Change Requests
Lanes that believe a frozen file must change (db.py schema, api.py routes/shapes,
cli.py, config.py, synthetic.py, pyproject.toml, the pose contract) do not edit it.
Instead append an entry here and work around it locally; the integration agent adjudicates.
Format (one entry per request, newest at the bottom):
### CR-<n> — <lane> — <date> — <one-line title>
- **File:** <frozen file + symbol>
- **Problem:** why the current contract blocks you
- **Proposed change:** the minimal change you need
- **Workaround in place:** what you did locally so you're not blocked
- **Decision:** (integration fills this: applied | rejected — rationale)
CR-1 — lane D — 2026-07-16 — Update obsolete stub assertion in test_api.py
- File:
backend/tests/test_api.py::test_detect_degrades_gracefully - Problem: That foundation test asserted the stub contract of
POST /api/events/detect("note" in data), which was correct only while lane D was unimplemented. With M7 landed,run_eventsno longer raisesNotImplementedError, soapi.pyreturns the real{result, events}shape (spec M3) and the stub assertion fails. - Proposed change: rename to
test_detect_events_endpointand assert the real landed contract (200;resultis the run_events summary with candidates/classified/… keys;eventsis a list; idempotent on repeat). - Workaround in place: applied the minimal edit directly (test file, not a frozen contract
file). The frozen
api.pyapp is unchanged. Only lane D's owned modules + this obsolete stub assertion were touched. Full suite green (89 passed). - Decision: APPLIED (integration, 2026-07-16). Confirmed against the frozen api.py
docstring:
POST /api/events/detectreturns{result, events}(spec M3). The renamedtest_detect_events_endpointasserts exactly that shape and is idempotent. No frozen file changed; approval also recorded in DIRECTIVES round 1.
CR-2 — integration — 2026-07-16 — M8 needs annotation↔event link + event-type correction
- File:
db.py(Annotationschema),api.py(AnnotationIn, events routes) - Problem: Spec M8 requires (a) triangulating "another annotation for the same event
from a different video" — but the frozen
annotationsschema has no event reference, andAnnotationIncarries no event id, so annotations can't be grouped per event; and (b) a correction dropdown that "writessource='user'" to an event's type — but there is no event-update helper or endpoint (only add/clear). - Proposed change (additive only — no existing shape altered):
annotations.event_id— new nullableForeignKey("events.id")column. Nullable ⇒ existing rows/inserts unaffected; the synthetic fixture creates annotations = none, so no migration. Serves the two-view grouping.AnnotationIn.event_id: int | None = None— additive optional request field.db.update_event(event_id, event_type=?, source=?, description=?)helper +PATCH /api/events/{event_id}— additive route; existing GET/POST/detect unchanged.
- Workaround in place: none needed — adjudicated and applied directly in this integration phase (the phase where the ownership matrix ends and frozen contracts may change on an approved CR). All changes are strictly additive; every pre-existing route/shape/column and the timebase + pose contracts are byte-for-byte unchanged. Full suite stays green.
- Decision: APPLIED (integration, 2026-07-16). Additive, backward-compatible, and required for M8 acceptance. Frontend and backend M8 both depend on it.
CR-3 — phase4 — 2026-07-16 — Anchor lifecycle: supersede + delete
- File:
api.py(create_annotation, newDELETE /api/anchors/{id}),db.py(get_anchor/update_anchor/delete_anchor,get_annotationsevent filter) - Problem: Round-3 review found each annotation created a NEW anchor, so the two-view "refine" flow left two identically-labeled anchors (a rough fallback + the triangulation), and there was no way to remove an anchor at all.
- Change (additive — no existing shape altered): one anchor per event — a later view that
triangulates supersedes the fallback anchor in place (same id;
create_annotationresponse gains asupersededbool); a later fallback links to the existing anchor without moving it. NewDELETE /api/anchors/{id}(unlinks annotations first, 404 on unknown).dbgainsget_anchor/update_anchor/delete_anchorand anevent_idfilter onget_annotations. - Workaround in place: none — applied directly (Phase 4, ownership matrix ended). Every pre-existing route/shape/column and the timebase + pose contracts are unchanged; the annotation response only gained a field. Suite 101 → 103 (added supersede + delete tests).
- Decision: APPLIED (phase4, 2026-07-16). Fixes the sole Round-3 wart; strictly additive.
CR-4 — lane G — 2026-07-17 — Drop obsolete capsule stub assertion in test_phase5_api.py
- File:
backend/tests/test_phase5_api.py::test_cli_dispatches_stubs_gracefully - Problem: The foundation2 test asserts
cli.main(["capsule"]) == 2, i.e. the stub contract (build_capsuleraisesNotImplementedError). With M17 landed the entrypoint is real, so the CLI no longer exits 2 — the assertion fails by design, exactly like CR-1 when lane D landed M7. - Proposed change: remove only
["capsule"]from the loop (features/direct stay — lane E still stubbed) with a comment pointing at the landed coverage. The capsule CLI dispatch is now covered for real inbackend/tests/test_capsule.py(builds a bundle viacli.main(["capsule", "--out", ...]), and asserts the clear no-dist error path). - Workaround in place: none possible without perverting
build_capsulesemantics (the spec requires a clear "run npm run build" error, notNotImplementedError); applied the minimal edit directly per the CR-1 precedent, flagged here for integration2 to ratify. - Decision: APPLIED — RATIFIED by coordinator at merge (2026-07-17). Exact CR-1 situation: a stub-contract assertion superseded by the milestone landing; the real CLI dispatch is covered in test_capsule.py. Suite 127 → 169 on the merged tree.
CR-5 — lane E — 2026-07-17 — Retire remaining stub-era assertions in test_phase5_api.py (M10/M11 landed)
- File:
backend/tests/test_phase5_api.py::test_cli_dispatches_stubs_gracefullyand::test_director_degrades_to_valid_empty_campath - Problem: Both assert lane E's stub contracts, correct only while M10/M11 were
unimplemented. With
run_features/generate_pathlanded: (a)cli.main(["features"])/["direct"]no longer raiseNotImplementedError, so they exit 0, not 2; (b)POST /api/directoron the synthetic fixture (events + poses present) returns a real non-empty camPath, sokeyframes == []/"note" in datafail. Identical in kind to CR-1 and to CR-4 (lane G, coordinator-ratified the same day forcapsule). - Proposed change: (a) with CR-4 already applied, lane E's landing empties the CLI stub
loop entirely — retire
test_cli_dispatches_stubs_gracefully(a comment points at the real dispatch coverage: test_capsule.py / test_audio_features.py / test_director.py); (b) rename the director test totest_director_returns_valid_campath, asserting the frozen camPath shape (version 1, exact keyframe key-set — holds stubbed or implemented); the empty+note degradation is now covered by lane E's owntest_director.py(no-events / no-poses cases). - Workaround in place: applied the minimal edit directly per the CR-1/CR-4 precedent (test
file, not a frozen contract file;
api.py/cli.pybyte-for-byte unchanged), including the merge reconciliation with CR-4's side of the same loop. Suite green on the merged tree. - Decision: (pending — coordinator/integration2)