# 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"): - [x] DB: `paths` table (contract #3) + helpers `add/get/get_one/delete_path`, plus `patch_anchor` (label/color only) — `db.py` - [x] 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` - [x] Manifest gained `"capsule": false` — `test_api.py` exact-set lock updated in the same commit (pitfall #3) - [x] CLI: `features`, `direct`, `capsule` subcommands dispatch into stubs; house degradation (exit 2, no traceback) — `cli.py` - [x] Backend stubs raise NotImplementedError with the frozen contract in the docstring: `audio_features.py`, `director.py`, `capsule.py` - [x] 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` - [x] 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}` - [x] 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) - [x] `state.js`: `window.__F4D_API_BASE__` ?? `VITE_API_BASE` ?? localhost (contract #5); `state.capsule` from the manifest toggles `body.capsule` - [x] Suite ≥ 121 + new tests: **127 passed** (121 floor + 6 in `test_phase5_api.py`) - [x] 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-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).