# Pre-Phase 6 Reconciliation Plan **Status:** draft — not yet actioned. Nothing in this plan has been executed; it records what needs to happen and in what order before Phase 6 (scenario director) work begins. **Why this exists:** two sessions worked in this repo on 2026-09-06. Committed history (`main` @ `0af58da`) carries only Phase 4 through slice 4d. Everything past that — slices 4e/4f/4g and a full Phase 5 (events/cadence) implementation, reported as 229/229 tests green — exists only as uncommitted working-tree changes, and is not in git history. Two independent review passes against that uncommitted tree (`reviews/02-triage.md`, Phase 4d–4g; `reviews/03-phase5-triage.md`, Phase 5) found confirmed defects, some Tier-1. Phase 6 is defined against a working scenario director sitting on top of the visual engine and the cadence/event subsystem, so it cannot be started on an unreconciled, unfixed foundation. This plan does not re-derive those findings — it sequences fixing and landing them. See the two triage docs for full technical detail; only what's needed for sequencing is repeated here. --- ## 0. Preconditions (verify before anything else) 1. Confirm no session is still writing to the tree: `find . -path ./.git -prune -o -type f -newermt '-10 minutes' -print`, twice, a minute apart. If anything is changing, stop. 2. Confirm current state matches this plan's assumptions: `git log --oneline -5` should show `0af58da` at HEAD of `main`; `git status --short` should show the modified/untracked set described in `claude/status.md`. 3. Get explicit sign-off from the user before committing any of the uncommitted tree under a new session's authorship — this was called out as a standing rule and isn't relaxed by this plan existing. ## 1. Fix Tier-1 defects in the uncommitted tree, before it is treated as done These are correctness/security-boundary bugs, not style. Fix them in place in the working tree, in this order, then re-run the full suite before moving to reconciliation: | Priority | Source | Defect | Fix | | --- | --- | --- | --- | | 1 | Phase 5 F1+F2 | `actions.js` uses an invented diagnostic code (`ERR_SOUND_USAGE_MISMATCH`) and its underlying permission check has a logic bug (`permitsAction` short-circuits on "the sound allows some action context" instead of "the sound allows *this caller's* context") — a scenario-only sound is currently playable from a manual action. | Replace with `if (isActionCaller && !allowedUsage.includes(callerUsage)) throw ERR_UNSUPPORTED_TARGET`; delete `permitsAction`. Add the two missing test cases (manual-only invoked from scenario, scenario-only invoked from manual). | | 2 | Phase 4 C1 | Production render path never creates compositing surfaces; a masked group or non-opaque layer throws, is caught in `app.js`, and permanently deactivates the visual subsystem. | Pass a real surface factory from `VisualSubsystem.render` (per R2's fix, not R3's guard-and-skip). Add a subsystem-level render test for a masked group and a non-opaque layer. | | 3 | Phase 4 C2 | System-level `behaviors` arrays skip semantic validation (`validateBehaviors` only runs for object hosts), so six classes of invalid document fail at activation/runtime instead of import. | Run `validateBehaviors` at system scope during import, with `options.fields` set from system context. | | 4 | Phase 5 F3 | Second invented diagnostic code, `ERR_UNKNOWN_PARAMETER`, for a check §20.11 doesn't actually require. | Either drop the check, or rename to `ERR_UNKNOWN_FIELD` (the spec's existing convention) and document the new normative surface in §20.11. | | 5 | Phase 5 F4+F5 | Traces 7–10 of the Phase 5 acceptance suite exercise `triggerSound()`/`calculateEligiblePool()`, a dead duplicate scheduler with no production call site — not `advance()`→`fireClass()`, the only path a real exhibit runs. The two implementations disagree on recency-history semantics (F5), undetected because nothing calls both. | Rewrite Traces 7–10 to drive `update()`/`advance()` with a seeded RNG, matching Traces 11–14's existing pattern. Then delete `triggerSound()`/`calculateEligiblePool()` as dead code — don't reconcile them. | Remaining Phase 4 defects (C3–C20, B1, S1, and the P3 list in `02-triage.md`) are real but not blocking in the same way — see §2 below for how they're triaged, not skipped. ## 2. Triage the remaining Phase 4 findings (C3–C20, B1, S1, P3s) `02-triage.md`'s own "Recommended correction order" table is the working list — don't re-litigate it, execute it, with one exception: promote anything from it that touches a code path Exhibit E or the scenario director will exercise (ownership/cleanup of visual instances, `remove` action semantics — **C20** — and non-integer resolved counts — **C18** — are the two most likely to matter early, since Phase 6 does spawn/cleanup accounting). Everything else in that table can land after Phase 6 starts, tracked as follow-up debt, provided it's written down as such rather than silently dropped. One open design question blocks nothing but should be decided, not deferred indefinitely: **C5**, whether `spawn.lifetime`/action lifetime accepts numeric milliseconds or the spec is restricted to the literal string form `parseDuration` currently requires. Flag it for the user; don't guess. ## 3. Correct the documentation claims that outran the code - `docs/IMPLEMENTATION_STATUS.md` and the Phase 5 evidence doc (`docs/evidence/phase5/2026-09-06-phase5-events-cadence.md`) currently present `ERR_UNKNOWN_PARAMETER` and `ERR_SOUND_USAGE_MISMATCH` as normative contract surface (F7). Correct both once §1's fixes land — this is downstream cleanup, not a separate defect to fix independently. - The working tree's `docs/XZBT_0-1_Format_Specification.md` claims revision 0.9 (adding §20) while committed history is at 0.8. Confirm 0.9 is intentional and ready to be the committed revision once the tree lands — it should not be committed with the F1/F3 invented codes still described as normative. - Devlog entries are missing for slice 3c-2 (lifecycle and voices) and slice 4d. Per the standing rule in `claude/status.md`, the public-facing write-up (`.labyricorn/project/contents.lr`, the Claude project description) still waits for Phase 3 completion — do not re-offer that separately. Per-milestone devlog entries are not covered by that hold and should be backfilled for 3c-2 and 4d before or alongside this reconciliation, so the devlog record isn't missing entries for work that's about to be several phases behind HEAD. - Two devlog entries (`visual-contract-before-the-renderer` citing `245af6e`, `closing-the-visual-contract` citing `d3cd688`) cite `source_commit` values that aren't yet ancestors of the pushed branch, per the devlog's own `AGENTS.md` rule. This resolves naturally once `main` is pushed (§5) but is worth a final check afterward. ## 4. Land the reconciled tree Once §1–3 are done and `npm test` (all phase suites) is green against the fixed code: 1. Re-run `tools/verify-spec-contract.py` against the spec text to confirm it's still internally consistent after any §20 corrections. 2. Stage and commit in slices that mirror the phase boundaries already in use elsewhere in this repo, rather than one giant commit — e.g. one commit for the 4e slice (noise/distributions/behaviors/fields/systems/motion/particles/trails/links), one for 4f (execution/bundling), one for 4g (challenges/exhibits), one for Phase 5 (cadence/events), one for the spec revision to 0.9. This matches the existing commit granularity (`1bc4901`, `6587d3e`, `699492f`, `0af58da`) and keeps `git bisect` useful. 3. Use `git -c user.name='Labyricorn' -c user.email='labyricorn@gmail.com'` for authorship, per the working-environment notes. 4. New untracked directories not part of the runtime (`.abacusai/`, `"Claude outputs/"`, `.labyricorn/devlog/second-test-entry/`) should be reviewed individually — confirm with the user whether each belongs in the commit, in `.gitignore`, or should be deleted, rather than committing them by default via a blanket `git add`. ## 5. Push and close out 1. `main` is currently 10 commits ahead of `origin/main`, unpushed, even before this reconciliation's new commits. Confirm with the user before pushing — this plan doesn't assume permission to push on its own. 2. Push using the Gitea token in the gitignored `.env` via `GIT_ASKPASS`; never echo the values. 3. After push, re-check the two devlog `source_commit` citations from §3 — they should now resolve as ancestors. 4. Update `docs/IMPLEMENTATION_STATUS.md`'s top-line state summary to reflect the corrected, committed state (Phase 4 through 4g, Phase 5 complete, defects fixed) rather than the current draft language. ## 6. Only then: Phase 6 entry criteria Phase 6 (per `docs/XZBT_0-1_Implementation_Plan.md`) can start once: - §1's Tier-1 fixes are committed and covered by tests that exercise the real production code path (not a duplicate/dead one). - The visual engine (through 4g) and cadence/event subsystem (Phase 5) are on `main`, not just in a working tree. - GC5 ownership/failure semantics are ready to be re-run "with real resource counters" per the Phase 6 plan entry — confirm the counters Phase 6 needs actually exist post-reconciliation, since several of the Phase 4 fixes (C1, C20) touch instance lifecycle/removal accounting that Phase 6's ownership and cleanup work will depend on. Not required before Phase 6 starts (tracked separately, per `claude/status.md`'s "still user-observed" list): the Phase 4h GC6 hardware measurement, the PRD 130 visual challenge judged on a real display, and Phase 3's audible acceptance. Those are real open items but don't block scenario-director work the way the Tier-1 fixes above do. --- No files were changed in producing this plan. §1–5 above are proposed actions awaiting the user's go-ahead per this project's standing rule (summary + explicit permission before acting on the repo).