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

62 lines
5.7 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.
# 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.