diff --git a/plan/status/lane-G.md b/plan/status/lane-G.md new file mode 100644 index 0000000..37eceaa --- /dev/null +++ b/plan/status/lane-G.md @@ -0,0 +1,101 @@ +# Status — lane G (persistence & capsule, branch `lane/g-capsule`) + +## Round 1 — 2026-07-17 — STATUS: ready_to_merge + +**Directives acknowledged:** Round 4 of plan/DIRECTIVES.md (phase 5 begins; foundation2 merged; +worktree rule followed — this lane runs in its own git worktree). + +**Acceptance checklist** + +M16 — server-side camera paths: +- [x] Routes implemented — by foundation2 (per the phase-5 ownership table); lane G deepened + coverage only, per scope. +- [x] `backend/tests/test_paths.py` — 30 tests: roundtrip deep-equal incl. float precision + + a 500-keyframe path, multiple paths coexist / id-ordered listing / summary-only shape, + duplicate + unicode + whitespace + 300-char names verbatim, ISO `created_at`, empty-keyframes + path valid (director degrade must be saveable), extra JSON keys ride along, get/delete 404s, + delete idempotence, an 18-case parametrized 422 matrix over the frozen camPath contract, + 422 creates no row, missing/aliased request fields 422. Cleans up its rows (test_phase5_api + runs later and asserts an empty table). +- [x] `frontend/src/pathsStore.js` filled: 💾 save = name prompt + POST `camPath.toJSON()`; + dropdown load = GET `/api/paths/{id}` + `camPath.fromJSON(data.json)`; 🗑 delete with + confirm; dropdown refreshed after every mutation (new save left selected); controls + unhidden + `ready = true`. Capsule mode: save/del hide via `write-ui`; the load dropdown + stays (paths are baked read-only — below) and hides itself if the listing fetch fails. +- [ ] Live save → reload page → load reproduces keyframes exactly — NEEDS LIVE BROWSER (list + for coordinator below). The exact-roundtrip property is locked server-side by + `test_roundtrip_preserves_float_precision` / `test_roundtrip_large_keyframe_list`. + +M17 — memory capsule: +- [x] `backend/festival4d/capsule.py::build_capsule` per the frozen contract: copies + `frontend/dist` (clear "run `cd frontend && npm run build`" error if missing), copies the + manifest-referenced media to `media/`, bakes every read-only API response extension-less at + the live API's relative paths (`api/manifest` with `capsule: true` + `has_capture: false`, + `api/events`, `api/anchors`, `api/videos/{id}/poses`, `api/beats`/`api/pointcloud`/ + `api/splat` when present, `api/audio/{id}` via the same lazy `ingest.extract_audio_hq` + cache the live route uses), injects `` ahead of + the app `