- 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>
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:
pathstable (contract #3) + helpersadd/get/get_one/delete_path, pluspatch_anchor(label/color only) —db.py - API:
GET /api/beats(servesconfig.BEATS_JSON, 404 when absent),POST /api/director(degrades to a VALID empty camPath +notewhile 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": false—test_api.pyexact-set lock updated in the same commit (pitfall #3) - CLI:
features,direct,capsulesubcommands 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-framefx.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-uiclass +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.capsulefrom the manifest togglesbody.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/tests→ 127 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-tripscamPath.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.capsulehides #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).