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
8.8 KiB
8.8 KiB
Specification review — 2026-09-06 (America/Los_Angeles)
Scope
- Local date: 2026-09-06 (America/Los_Angeles, PDT / UTC−7). Written 2026-09-06 17:38 UTC.
- Branch:
main - HEAD:
0af58da89dd6095fa9ca3ed546a2e48d7c7971b7(feat(visual): implement the slice 4d renderer core) - Today’s commits (since 2026-09-06 07:00 UTC):
1bc4901(spec §§17–19 rev 0.8),6587d3e(schema/validator/resolution),699492f(standalone bundle),0af58da(4d renderer). - Uncommitted work: Phase 4e–4g runtime (
src/runtime/visual-*.js, engine/actions/app/resolution/performance), tests, exhibits, evidence. Git cannot date uncommitted files; they are in scope as the current tree. - Authoritative sources:
docs/XZBT_0-1_Format_Specification.mdrev 0.8, especially §§6, 8.1, 9.1, 10.1–10.2, 17–19 (19.1–19.7). Supporting:docs/XZBT_0-1_Implementation_Plan.mdPhase 4 slice table. PRD cited only where the spec restates it. - Not used: any file under
/reviews. - Limitations: Action Model (spawn/remove shapes) is explicitly not fixed in §19.2; Phase 6 scenarios and 4h/GC6 (traces 20–21) are deferred and not treated as defects. Display judgment not run.
Traceability (today’s visual work → spec)
| Item | Spec | Implementation | Status |
|---|---|---|---|
| Scene / fit / primitives / compositing | 17.4–17.13 | visual-engine.js, visual-geometry.js, visual-canvas2d.js |
Satisfied (4d tests 17.16.*) |
| Camera matrix, parallax, perspective, focalLength clamp | 19.3 | cameraMatrix, planFrame |
Satisfied (static + execution tests) |
| Components, particles, emitters, repeaters, behaviors, fields, trails, links | 18.1–18.8 | visual-systems.js, visual-behaviors.js, visual-fields.js, visual-distributions.js |
Satisfied (18.10 traces in phase4-procedural.test.mjs) |
| Exhibit + system automation, shared curves/modes, live vs authoring budgets | 19.1, 16.1 | resolution.js, visual-automation.js, audio-automation.js |
Partial (curves/modes/budgets tested; local-track target walk unsafe; 19.7.3/4 coverage incomplete) |
| Lifecycle, spawn/remove, ownership, release factor | 19.2 | visual-lifecycle.js, VisualEngine.spawn/remove/cleanup |
Partial (DurationSpec after evaluation vs parseDuration; action lifetime not specified in §19) |
| Post-effects chain, grain isolation, pass ceiling | 19.4 | visual-effects.js, effectPlan |
Partial (pixel tests exist; radius/energy vs 19.4 table not fully asserted) |
| Aggregate ceilings + cadence | 19.5–19.6 | enforcePopulations, visual-diagnostics.js |
Satisfied for automated 19.7.18 cases; unverified vs measured 4h values (provisional by spec) |
| 19.7 traces 1–19 | 19.7 | test/phase4-execution.test.mjs |
Partial (many named tests; 19.7.3/4/8/13 not all matching the prose) |
| 19.7 traces 20–21 | 19.7 | — | Deferred (4h / display); not a defect |
Findings (confirmed gaps)
1. spawn.lifetime / action lifetime after evaluation is not a duration literal
- Priority: P1
- Spec: §6.1 — durations are unit strings converted internally to milliseconds. §19.2 —
spawn.lifetimeandspawn.releaseare DurationSpec; instance duration is that logical duration from instantiation. - Impl:
visual-engine.js:466-468alwaysparseDuration(sampleTree(...)), which requirestypeof value === 'string'(types.js:55-56).actions.js:48-50mayevaluateValue(action.lifetime)first. - Expected: Authored DurationSpec remains a duration literal through sampling, then converts to ms. If evaluation yields a number (ms), it must still be accepted or rejected with
ERR_TYPE_MISMATCH, notERR_INVALID_DURATION. - Actual: Any non-string (including a resolved number) throws
ERR_INVALID_DURATIONand aborts spawn. - Trigger:
spawnaction with evaluatedlifetime, or a DurationSpec that the value resolver does not leave as a string. - Impact: Legal instance lifetimes fail at runtime; §19.2 “absent means until remove” path is the only reliably tested path.
- Correction: Convert DurationSpec with
parseDurationonly when the value is still a string; if already a finite number, treat as milliseconds (or reject per strict coercion ban §2 — pick one and match §6.1).
2. Local automation target walk throws TypeError instead of ERR_INVALID_REFERENCE
- Priority: P1
- Spec: §19.1 — target naming an undeclared object is
ERR_INVALID_REFERENCE; outside the registry isERR_UNSUPPORTED_TARGET. Import should reject; runtime must not invent a third failure mode. - Impl:
visual-automation.js:11-13:while (object?.children.some(...))— iffindreturnsundefined,.someis called onundefined. - Expected: Validator fault at import, or runtime
RuntimeFaultwith those codes. - Actual: Uncaught
TypeErrorduringcreateSystem/ instantiate (outside the per-ticktryinadvance). - Trigger: Dangling graphic-object key on a system
automationtrack, or exhibit-scope fallback pathvisual-engine.js:403-406. - Impact: Tick/activation abort rather than a documented diagnostic.
- Correction: Guard
object?.children?.some; if missing, throwRuntimeFault('ERR_INVALID_REFERENCE', ...).
3. Required traces 19.7.3 and 19.7.4 are not implemented as specified
- Priority: P2
- Spec: §19.7 items 3–4 (lines ~3255–3258 of the format spec): (3) system-scope
atfrom instantiation — two spawns 4s apart produce identical curves offset by 4s; (4) cross-scope / undeclared / out-of-registry targets raiseERR_INVALID_REFERENCEvsERR_UNSUPPORTED_TARGETas specified. - Impl:
test/phase4-execution.test.mjs“19.7.3” only checks one exhibit-scope stream; “19.7.4” only checks behavior/automationERR_AUTOMATION_CONFLICT. - Expected: The behaviors named in 19.7.3–4 actually run (spec: “a parsed stub is never a passed runtime trace”).
- Actual: Adjacent properties are tested; the named traces are not.
- Trigger: Acceptance of slice 4f against 19.7.
- Impact: Spawn-relative automation offset and target-error taxonomy can regress without a failing test.
- Correction: Add the two-spawn 4s offset assertion and the four negative target cases from 19.7.4.
4. Blur/bloom radius vs 19.4 “32 scene units” is not enforced in the pixel filter
- Priority: P3
- Spec: §19.5 authoring table / 19.4 — blur and bloom radius 32 scene units; pipeline clamp of 8.1 on effect parameters.
- Impl:
visual-effects.js:4-5caps box-blur radius atmax(width,height)pixels (device radius fromeffectPlan). Contract numeric clamp is ineffectPlanviaPOST_EFFECTS; the filter can still run at huge projected radii. - Expected: Authored/resolved radius in scene units stays in the 19.4 table; device radius is a projection of that, not an unbounded pixel kernel.
- Actual: Device kernel can equal the long edge of the backing store.
- Trigger: Large
zoom×deviceRadiuswith radius still inside 32 scene units. - Impact: Frame hitch; not a silent spec violation of the authored 32 if
effectPlanclamps scene units — partial. ConfirmPOST_EFFECTSmax is 32 and that tests assert it (19.7.15 does not). - Correction: Assert 19.4 ranges in execution tests; cap device radius by a pixel budget derived from the scene-unit max.
Specification ambiguities (not defects)
- §19.2 vs Action Model: spawn/remove action shapes are explicitly not fixed in 19.2. Whether
action.lifetimeis DurationSpec, ValueSpec<number>, or either is undecided. Finding 1 is a defect only forspawn.lifetimeaftersampleTree; action-lifetime typing needs a decision in the Action Model (Phase 5/6), not a silent assumption. - DurationSpec vs TimeSpec (§6.2): 19.2 names DurationSpec, not the random TimeSpec object. Whether
spawn.lifetime: { "random": { "min": "1s", "max": "2s" } }is legal is unspecified. Implementation would currently failparseDurationon an object. - 19.5 values are provisional until 4h/trace 21; using 8192 particles / 2048 emitters in tests matches the current table, not a measured GC6 result.
instances.*addressing is runtime-only (19.2 / 8.1). Tests useinstances.s#N; the Action Model still owns the publicremovetarget grammar.
Verification
- Read spec §§6, 17–19.7 and traced
spawn,advance, automation, lifecycle, effects, resolutionvisualValues. node --test test/phase4-*.test.mjs test/phase1-runtime.test.mjs: 116 pass, 0 fail (prior turn). Tests are evidence of implemented behavior, not of 19.7.3/4 completeness.- Not verified: real-display 19.7.20, GC6 19.7.21, full Action Model document, audio 16.11 cross-check of every visual curve sample.
Actionable specification/implementation gaps exist (P1–P3 above). Traces 20–21 remain deferred by the spec itself.