festifun/plan/status/foundation2.md
type-two 51650c728e foundation2 (phase 5a): contracts, stubs, hooks & UI wiring for lanes E/F/G
- DB: paths table (contract #3) + helpers; patch_anchor (label/color)
- API: GET /api/beats, POST /api/director (valid-empty degradation),
  /api/paths CRUD (validated POST -> 422), PATCH /api/anchors/{id};
  manifest gains capsule:false (test_api.py key-set lock updated consciously)
- CLI: features | direct | capsule subcommands -> lane stubs (exit-2 degradation)
- Backend stubs with frozen contracts in docstrings: audio_features, director, capsule
- Frontend: six lane stub modules imported from main.js; all phase-5 buttons
  pre-wired hidden in index.html; write-ui/body.capsule read-only mode;
  __F4D_API_BASE__ runtime override in state.js
- Hooks (contract #6): transport.captureAudioStream/releaseAudioStream,
  scene3d.focusOn, scene3d.setHelpersVisible (+registerHelper; camPath gizmos)
- Suite 121 -> 127 passed; npm build clean; live-browser verified (status file)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 17:31:08 +10:00

3.7 KiB

Status — foundation2

Round 1 — 2026-07-17 — STATUS: ready_to_merge

Directives acknowledged: Round 4 of plan/DIRECTIVES.md. Single-agent phase 5a — main working directory on branch foundation2 (per directive 3).

Acceptance checklist (plan/20-phase5.md "foundation2 scope"):

  • DB: paths table (contract #3) + helpers add/get/get_one/delete_path, plus patch_anchor (label/color only) — db.py
  • API: GET /api/beats (serves config.BEATS_JSON, 404 when absent), POST /api/director (degrades to a VALID empty camPath + note while lane E is stubbed), GET/POST/DELETE /api/paths* (implemented — CRUD over the helpers was cheaper than stubbing; POST validates the camPath shape → 422), PATCH /api/anchors/{id} (contract #4) — api.py
  • Manifest gained "capsule": falsetest_api.py exact-set lock updated in the same commit (pitfall #3)
  • CLI: features, direct, capsule subcommands dispatch into stubs; house degradation (exit 2, no traceback) — cli.py
  • Backend stubs raise NotImplementedError with the frozen contract in the docstring: audio_features.py, director.py, capsule.py
  • Frontend stub modules created + imported from main.js, each wiring its own hidden button in init(): director.js, exportVideo.js, anchorPanel.js, photoMode.js (+ per-frame fx.update() call in the master loop), fx.js, pathsStore.js
  • index.html: ALL phase-5 buttons pre-wired hidden — #btn-director, #btn-path-save, #path-load-select, #btn-path-del, #btn-export, #btn-photo, #btn-fx, #btn-anchors, #anchor-panel container; .write-ui class + body.capsule .write-ui{display:none}
  • Hooks (contract #6): transport.captureAudioStream()/releaseAudioStream() (post-gain tap, spatial included, tracks re-join across seeks), scene3d.focusOn(x,y,z), scene3d.setHelpersVisible(bool) (+ registerHelper; camPath gizmos registered)
  • state.js: window.__F4D_API_BASE__ ?? VITE_API_BASE ?? localhost (contract #5); state.capsule from the manifest toggles body.capsule
  • Suite ≥ 121 + new tests: 127 passed (121 floor + 6 in test_phase5_api.py)
  • Ownership table updated (paths-route bodies → foundation2; noted in 20-phase5.md)

Done this round — evidence:

  • uv run pytest backend/tests127 passed (was 121).
  • npm run build → clean (pre-existing chunk-size warning only).
  • Live browser (Vite :5173 + uvicorn :8000, synthetic fixture): app boots (3 videos, sync +0 ms); all 9 new controls present and display:none; setHelpersVisible(false) hides grid (and restores); focusOn(1,0.5,0) retargets OrbitControls to exactly [1,0.5,0]; captureAudioStream() returns a MediaStream with 1 audio track, release clean; POST /api/director{version:1, keyframes:[], note:…} and round-trips camPath.fromJSON (0 keyframes, no throw); paths save→load→fromJSON reproduces 2 keyframes, DELETE → 200 then 404; PATCH /api/anchors/1 {color} → updated dict; GET /api/beats → 404; body.capsule hides #annot-panel/#btn-path-save/#btn-path-del.

Blockers / questions for coordinator: none. One conscious deviation, self-adjudicated (coordinator == foundation2 agent this round): paths route bodies implemented here instead of stub-marked for lane G — they were one-line calls into the db helpers I had to write anyway; stubbing them would have been more code than implementing. Lane G scope shrinks to capsule + pathsStore.js + deepening test_paths.py.

Next: merge to main, then lanes E, G, F in parallel (each in its own worktree — directive 3 is mandatory for 5b).