docs: raise the format specification to revision 0.9 and land the reconciliation
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
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user