Revision 0.9 adds section 20, the cadence and event subsystems contract, and carries two corrections the implementation forced. Section 6.1 now states that a duration is the authored literal or a non-negative finite number already in milliseconds, since a DurationSpec may be the resolved output of a ValueSpec or a bounded TimeSpec, with the one documented exception of an automation track's `at`, which 19.1 keeps literal-only so that point ordering stays decidable at import. Section 20.11 documents the rejection of an undeclared input name in an event action's `with` map as ERR_UNKNOWN_FIELD — the section's own convention for that shape of error, replacing an invented code that appeared nowhere in the registry. The review record is committed with the code it describes: the two code triages that found these defects, the reconciliation plan that sequenced the fixes, and a follow-up debt record listing what was deliberately left open — the unchecked JSON Schema artifact, degenerate path arcs, post-effect transient allocation, the window-traffic fixture's per-copy wrap bounds, and the unstated `ownership: "persistent"` value on a sound action. None of the five blocks phase 6; all five are written down rather than dropped. Devlog entries are backfilled for the two milestones that had none: phase 3c slice 2, the audio lifecycle and voice ceilings, and slice 4d, the renderer core. The implementation status summary now reflects the reconciled state rather than the in-flight one. 231 tests pass. tools/verify-spec-contract.py reports 46 declared diagnostic codes with every used code resolving and its two long-standing unresolved cross-references unchanged. Co-Authored-By: Claude Opus 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01ShxxFqFmCUDQnQvFNm4TKy
9.8 KiB
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)
- 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. - Confirm current state matches this plan's assumptions:
git log --oneline -5should show0af58daat HEAD ofmain;git status --shortshould show the modified/untracked set described inclaude/status.md. - 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.mdand the Phase 5 evidence doc (docs/evidence/phase5/2026-09-06-phase5-events-cadence.md) currently presentERR_UNKNOWN_PARAMETERandERR_SOUND_USAGE_MISMATCHas 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.mdclaims 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-rendererciting245af6e,closing-the-visual-contractcitingd3cd688) citesource_commitvalues that aren't yet ancestors of the pushed branch, per the devlog's ownAGENTS.mdrule. This resolves naturally oncemainis 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:
- Re-run
tools/verify-spec-contract.pyagainst the spec text to confirm it's still internally consistent after any §20 corrections. - 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 keepsgit bisectuseful. - Use
git -c user.name='Labyricorn' -c user.email='[email protected]'for authorship, per the working-environment notes. - 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 blanketgit add.
5. Push and close out
mainis currently 10 commits ahead oforigin/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.- Push using the Gitea token in the gitignored
.envviaGIT_ASKPASS; never echo the values. - After push, re-check the two devlog
source_commitcitations from §3 — they should now resolve as ancestors. - 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).