# Follow-up debt after the pre-Phase 6 reconciliation **Status:** current as of the reconciliation commits landing on `main` (2026-09-06). This is the written-down remainder that [`00-pre-phase6-reconciliation.md`](00-pre-phase6-reconciliation.md) §2 asks for: findings from the two triages that were *not* dropped silently, recorded here so Phase 6 can start without losing them. ## What was closed Every Tier-1 finding of both triages is fixed in the code that shipped in these commits, and each is covered by a test that drives the production path rather than a duplicate of it: | Finding | Source | Closed by | | --- | --- | --- | | F1 + F2 — invented `ERR_SOUND_USAGE_MISMATCH`, and the `permitsAction` short-circuit that let a scenario-only sound play from a manual action | `03-phase5-triage.md` | `actions.js` now checks `allowedUsage.includes(callerUsage)` against the caller's own context and raises `ERR_UNSUPPORTED_TARGET` per §20.2.2/§20.12. `permitsAction` is deleted. | | F3 — the second invented code, `ERR_UNKNOWN_PARAMETER` | `03-phase5-triage.md` | Resolved as the *rename* option, not the drop: the check raises `ERR_UNKNOWN_FIELD`, the section's own convention, and §20.11 documents the behavior as normative surface. | | F4 + F5 — Traces 7–10 certifying a duplicate scheduler that disagreed with the shipping one on recency semantics | `03-phase5-triage.md` | Traces 7–10 now drive `CadenceSubsystem.update()`/`advance()` with a seeded RNG, as Traces 11–14 already did. `triggerSound()` and `calculateEligiblePool()` are deleted rather than reconciled. | | C1 — production render path never created compositing surfaces; the first masked group permanently deactivated the visual subsystem | `02-triage.md` | `VisualSubsystem.render` passes a real `createSurface` factory (`OffscreenCanvas`, falling back to a detached canvas element), per R2's fix rather than R3's guard-and-skip. | | C2 — system-level `behaviors` arrays skipped semantic validation, so six classes of invalid document failed at activation instead of import | `02-triage.md` | `validateBehaviors` runs at system scope during import with `options.fields` set from the system context. | The two items §2 asked to be **promoted** because Phase 6 will exercise them are also closed: **C20** (`remove` on an unknown instance ID now returns `refused`, not `executed`) and **C18** (a resolved non-integer `count` or burst count now raises `ERR_TYPE_MISMATCH` at the boundary instead of being rounded). Both touch the spawn/cleanup accounting the scenario director's ownership work depends on. **C5** — the one open design question — was decided by the user rather than guessed: a DurationSpec accepts the authored **duration literal** *or* a **non-negative finite number already in milliseconds**. The numeric form exists because a DurationSpec may be the resolved output of a ValueSpec or a bounded `TimeSpec`. Runtime (`types.js`), validator (`visual-validation.js`), and specification (§6.1) now agree, with the single documented exception of an automation track's `at`, which §19.1 keeps literal-only so point ordering stays decidable at import. Beyond the reconciliation plan's required set, the working tree also carries fixes for C3, C4, C6, C8, C9, C10, C11, C12, C13, C14, C15, C16, C17, C19, B1, S1, and P3.1. These were verified present in the source before the reconciliation commits; they are not separately re-certified here. ## What remains open None of the following blocks Phase 6. Each is real, and each should be picked up rather than forgotten. 1. **C7 — the JSON Schema is not a checked artifact.** The schema has no consumer: nothing in `src/`, `tools/`, or `test/` validates against it, so it can drift from the validator without any test noticing (P3.5 makes the same point from the other direction). The concrete fix is to validate the exhibit fixtures against the schema in CI; until then, treat `validator.js` as the single source of truth and the schema as documentation. 2. **P3.2 — degenerate path arcs.** A coincident-endpoint `arc` forces `acos(0)` and emits a phantom bulge; a negative radius is drawn mirrored rather than raising `ERR_OUT_OF_BOUNDS`. Early-return on coincident endpoints and check raw radii before `Math.abs`. 3. **P3.3 — post-effect transient memory.** Each blur pass allocates full-frame copies at up to 4096 device pixels, with no tie-in to the §19.5 pass budget. The device radius is now capped at 128, which bounds the work but not the allocation. Tiling the blur is the real fix. 4. **P3.4 — the `window-traffic` challenge fixture.** Per-copy `wrap` bounds are ±30px around each copy's own origin, so cars cycle in place instead of crossing the window. Either use the window rect as bounds, or document the cell-stutter as intended. The 4g suite only asserts finiteness and equality, so it passes either way. 5. **F6 — `ownership: "persistent"` on a sound action.** The schema and `cadence-validation.js` accept it; §20.12 documents only `"performance"` and `"scenario"`. This is probably deliberate carry-over from the §10.1 ownership vocabulary, but it is currently unstated. One line in §20.12 settles it either way — and it is worth settling before Phase 6, which is where scenario ownership becomes load-bearing. ## Still user-observed, unchanged by this reconciliation Carried forward verbatim from the project status, because nothing automated can close them: the slice 4h / GC6 hardware measurement (every §19.5 aggregate stays **provisional** until trace 21 of §19.7 runs on recorded hardware), the PRD 130 visual challenge judged on a real display, Phase 3's audible acceptance — no sound has yet been heard from a production build — and Phase 1's direct-file two-fixture restart observation.