Files
XZBT/reviews/spec-review-2026-09-06-unknown-model-173816-c4f8a2d1.md
T
LabyricornandClaude Opus 5 c4332363a9 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
2026-09-06 21:54:09 +00:00

88 lines
8.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.md` rev **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.md` Phase 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.lifetime` and `spawn.release` are **DurationSpec**; instance duration is that logical duration from instantiation.
- **Impl:** `visual-engine.js:466-468` always `parseDuration(sampleTree(...))`, which requires `typeof value === 'string'` (`types.js:55-56`). `actions.js:48-50` may `evaluateValue(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`, not `ERR_INVALID_DURATION`.
- **Actual:** Any non-string (including a resolved number) throws `ERR_INVALID_DURATION` and aborts spawn.
- **Trigger:** `spawn` action with evaluated `lifetime`, 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 `parseDuration` only 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 is `ERR_UNSUPPORTED_TARGET`. Import should reject; runtime must not invent a third failure mode.
- **Impl:** `visual-automation.js:11-13`: `while (object?.children.some(...))` — if `find` returns `undefined`, `.some` is called on `undefined`.
- **Expected:** Validator fault at import, or runtime `RuntimeFault` with those codes.
- **Actual:** Uncaught `TypeError` during `createSystem` / instantiate (outside the per-tick `try` in `advance`).
- **Trigger:** Dangling graphic-object key on a system `automation` track, or exhibit-scope fallback path `visual-engine.js:403-406`.
- **Impact:** Tick/activation abort rather than a documented diagnostic.
- **Correction:** Guard `object?.children?.some`; if missing, throw `RuntimeFault('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 `at` from instantiation — two spawns 4s apart produce identical curves offset by 4s; (4) cross-scope / undeclared / out-of-registry targets raise `ERR_INVALID_REFERENCE` vs `ERR_UNSUPPORTED_TARGET` as specified.
- **Impl:** `test/phase4-execution.test.mjs` “19.7.3” only checks one exhibit-scope stream; “19.7.4” only checks behavior/automation `ERR_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-5` caps box-blur radius at `max(width,height)` **pixels** (device radius from `effectPlan`). Contract numeric clamp is in `effectPlan` via `POST_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` × `deviceRadius` with radius still inside 32 scene units.
- **Impact:** Frame hitch; not a silent spec violation of the authored 32 if `effectPlan` clamps scene units — **partial**. Confirm `POST_EFFECTS` max 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)
1. **§19.2 vs Action Model:** spawn/remove *action shapes* are explicitly not fixed in 19.2. Whether `action.lifetime` is DurationSpec, ValueSpec\<number\>, or either is **undecided**. Finding 1 is a defect only for `spawn.lifetime` after `sampleTree`; action-lifetime typing needs a decision in the Action Model (Phase 5/6), not a silent assumption.
2. **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 fail `parseDuration` on an object.
3. **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.
4. **`instances.*` addressing** is runtime-only (19.2 / 8.1). Tests use `instances.s#N`; the Action Model still owns the public `remove` target grammar.
## Verification
- Read spec §§6, 17–19.7 and traced `spawn`, `advance`, automation, lifecycle, effects, resolution `visualValues`.
- `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.