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
73 lines
9.8 KiB
Markdown
73 lines
9.8 KiB
Markdown
# 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='[email protected]'` 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).
|