# Status — integration ## Round 1 — 2026-07-16 — STATUS: ready_to_merge **Directives acknowledged:** round 1 of plan/DIRECTIVES.md (all lanes merged & verified; integration begins; CR-1 approved; drop debris stash + remove laneA/B worktrees; real-browser sync check; test floor 96). **Scope done: M8 + M9 + README + cross-lane wiring + cleanup.** ### Change requests adjudicated (plan/CHANGE_REQUESTS.md) - **CR-1 (lane D): APPLIED** — confirmed `POST /api/events/detect` returns `{result, events}`; renamed test asserts it. - **CR-2 (integration, filed + APPLIED):** M8 needs an annotation↔event link and event-type correction, neither of which the frozen contract had. Added — strictly additive: - `annotations.event_id` (nullable FK) + `AnnotationIn.event_id` — two-view grouping. - `db.update_event` + `db.get_event` + `PATCH /api/events/{id}` — the correction dropdown. Every pre-existing route/shape/column and the timebase + pose contracts are unchanged. - A second foundation stub assertion (`test_annotation_stored`: `anchor_id is None`) was, like CR-1, only valid pre-M8; renamed to `test_annotation_resolves_to_anchor` for the landed shape. ### M8 — correction + annotation → 3D - New `backend/festival4d/resolve.py`: ray from bbox center (pose interpolated at t_video via `geometry.slerp_pose`), then triangulate across two same-event views (reject near-parallel / gap > 0.5), else nearest point-cloud point (0.3 cyl), else ray at centroid depth. Pure orchestration over lane B's frozen primitives. - `POST /api/annotations` now resolves + creates+links an anchor (label = event description). - Frontend `annotate.js`: click a timeline marker → correction panel (type dropdown → PATCH, source→user; live marker recolor); **Annotate location** → drag a letterbox-correct box on any video → POST. Resolved anchors pushed live into overlays **and** the 3D scene (new anchor spheres + labels in `scene3d.js`). - **Acceptance (spec M8): PASS.** `tests/test_resolve.py::test_two_view_triangulation_recovers_corner` — two-view stage-corner annotation lands **0.00000 units** from ground truth (< 0.2). Verified live in-browser too: two-view annotation triangulated with gap 0.21 through the full frontend→API→geometry path; correction changed pyro→confetti with source=user and recolored the marker. ### M9 — keyframed camera paths - New `camPath.js`: **+ Key**/`K` captures the free-roam pose at the current t_global; gizmos + Catmull-Rom preview line; **▶ Path** follows position (Catmull-Rom) + orientation (slerp) by t_global while the transport plays; ⤓/⤒ export/import JSON. `scene3d` gained `enterPathMode`/`exitPathMode`/`applyCameraPose`; snap-to-camera and Free-roam/`Esc` cancel a playing path. - **Verified live:** 3 keyframes → path mode on → at t=6.0 s the viewer camera sat exactly on keyframe #2 `[-10,6,12]` (Catmull-Rom driven). - Polish present from lanes + this phase: loading state, per-video enable/disable, keyboard (`Space`,`←/→`,`Shift`,`1–9`,`Esc/0`,`K`). ### Full-system pass - **Backend: 101 passed** (`../.venv/bin/python -m pytest`) — up from the 96 merge-day floor (+5: 4 resolve + 1 event-patch; annotation test renamed). - **Frontend: `npm run build` clean** (18 modules). - End-to-end on the synthetic fixture: `synthetic → ingest → sync → events → serve` + frontend loads with 0 console errors; M4 sync error ~16 ms at t=8 s (deterministic pump; half-frame residual, < 50 ms). - Cleanup: dropped the shared-worktree debris stash; removed `../festifun-laneA` and `../festifun-laneB` worktrees; `frontend/dist/` gitignored; pristine demo DB regenerated. **Blockers / notes for coordinator:** - **Real-focused-browser M4 check is the one item I could not fully close from here.** The automated browser pane runs `document.hidden=true`, so rAF is throttled and continuous playback can't be observed (same limitation lane C hit). I verified sync deterministically (pump → ~16 ms) and every interaction via real handlers, but a human should open the app in a normal focused window once to eyeball continuous multi-cam sync. Not code-blocking. **Next:** merge `integration` → `main`; tag `v0.1.0`.