Files
XZBT/reviews/review-2026-09-06-unknown-model-173154-a7c3e91b.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

5.7 KiB
Raw Blame History

Independent review — 2026-09-06 (America/Los_Angeles)

Review scope

  • Local date: 2026-09-06 (America/Los_Angeles, PDT / UTC−7). Review written 2026-09-06 17:31 UTC.
  • Branch: main
  • HEAD: 0af58da89dd6095fa9ca3ed546a2e48d7c7971b7 — feat(visual): implement the slice 4d renderer core (2026-09-06 16:38:44 +0000)
  • Today’s commits (LA calendar day, since 2026-09-06 07:00 UTC):
    • 1bc49018 docs(visual): apply the sections 17-19 review triage in full at revision 0.8
    • 6587d3e4 feat(visual): align the schema, validator, and resolution engine with the 0.8 visual contract
    • 699492f8 build(visual): bundle the visual contract and validation modules into the standalone artifact
    • 0af58da8 feat(visual): implement the slice 4d renderer core
  • Uncommitted work (staged: none): modified runtime/docs/build files plus untracked Phase 4e–4g modules, tests, exhibits, evidence, and tools. When those uncommitted edits were made cannot be established from Git; they are included because they are the current tree.
  • Out of scope: existing files under /reviews, .abacusai/, .labyricorn/devlog/second-test-entry/, Claude outputs/. No implementation files were modified for this review.
  • Limitation: review is of the working tree vs HEAD plus today’s commits; Phase 4a–4c docs commits from late 2026-09-05 PDT are not treated as “today.”

Findings (by severity)

1. Spawn lifetime is passed through parseDuration, which only accepts duration strings

  • Priority: P1
  • Where: src/runtime/visual-engine.js:466-468, src/runtime/actions.js:48-50, src/runtime/types.js:55-56
  • Trigger: ActionExecutor evaluates action.lifetime (or spawn-input substitution yields a number) and VisualEngine.spawn always does parseDuration(sampleTree(value, …)). parseDuration throws ERR_INVALID_DURATION unless typeof value === 'string'.
  • Impact: A legal numeric or already-resolved lifetime cannot spawn; the action fails as a runtime fault instead of applying the duration. Graphic-object lifetime in visual-motion.js:70 has the same string-only assumption after sampling.
  • Evidence: spawn helper duration = (value, fallback) => value === undefined ? fallback : parseDuration(...). Tests in test/phase4-execution.test.mjs never pass action.lifetime or a numeric lifetime.
  • Correction: Accept already-numeric milliseconds (and duration strings) in one helper; evaluate action lifetime to that type consistently with the spec.

2. Local visual automation crashes if the target object is missing

  • Priority: P1
  • Where: src/runtime/visual-automation.js:11-13
  • Trigger: visualLocalTracks walks root.find(...) then while (object?.children.some(...)). If find returns undefined, object?.children is undefined and .some is still invoked.
  • Impact: Instantiating or advancing a system with a dangling automation target throws TypeError instead of a RuntimeFault / import error, taking down the visual tick (failSystem only catches errors inside the per-system try after tracks are built at createSystem).
  • Evidence: Optional chaining stops at children, not at .some. Validator is supposed to reject bad targets, but runtime still concatenates tracks for exhibit-scope fallbacks (visual-engine.js:403-406) and spawned substitutes.
  • Correction: Use object?.children?.some(...) and no-op or fault when object is missing.

3. Bloom / color-adjust write unclamped channel math into Uint8ClampedArray without documenting wrap vs clamp; blur radius is clamped to max(width,height)

  • Priority: P2
  • Where: src/runtime/visual-effects.js:4-5, 38, 74
  • Trigger: bloom adds pixels + glow * intensity; color-adjust can emit values outside [0,1] before * 255. visualBoxBlur sets r = min(max(width,height), round(radius)).
  • Impact: Clamped arrays hide overflow (no crash), but a full-frame-radius blur is O(pixels × max(edge)) and can hitch on large backing stores when deviceRadius is large. Tests only assert “pixels changed,” not energy conservation or radius semantics (test/phase4-execution.test.mjs 19.7.15).
  • Evidence: No Math.min(255, …) before assignment; radius cap is the long edge, not a contract ceiling.
  • Correction: Clamp processed channels explicitly; cap blur radius to the post-effect numeric range / a small pixel budget.

4. remove actions always report executed even when the instance id is unknown

  • Priority: P3
  • Where: src/runtime/actions.js:53-55, src/runtime/visual-engine.js:475
  • Trigger: this.visual.remove(action.target) → this.instances.get(id)?.remove(this.time).
  • Impact: Callers cannot distinguish a successful release from a no-op (typo’d id). Low severity because spawn returns the id and tests use that id.
  • Correction: Return refused / failed when the map has no entry.

Verification

  • node --test test/phase4-*.test.mjs test/phase1-runtime.test.mjs: 116 pass, 0 fail (≈2.5s).
  • Inspected Git status, today’s LA-window commits, unstaged diffs, and untracked src/runtime/visual-*.js plus tests.
  • Traced spawn/remove, tick (performance.js onTick → VisualSubsystem.advance → VisualEngine.advance), automation loop, effects, lifecycle, and resolution effect sampling.
  • Not verified: browser/Canvas getImageData on the real XZBT.html artifact; audio regression for automationValueAt loop (shared with visual); exact wall-clock of uncommitted files; full test/*.test.mjs outside phase1/phase4.

No P0 defects were confirmed. Remaining risk is concentrated in uncommitted Phase 4e–4g runtime (lifetime typing, automation target walk) rather than the four committed 4d/contract commits.