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:
2026-09-06 21:54:09 +00:00
co-authored by Claude Opus 5
parent 3db1df058f
commit c4332363a9
30 changed files with 7892 additions and 137 deletions
+20 -4
View File
@@ -1,10 +1,10 @@
# XZBT implementation status
**Updated:** September 6, 2026
**State:** Phase 3 audio engine implemented through slice 3c-4 and automatically verified; the Phase 4 visual subsystem contract is complete across slices 4a, 4b, and 4c, and no visual runtime code exists yet. Slice 3c-4 hardware measurement/listening acceptance, the Phase 3 audible gates, and the Phase 1 direct-file import/restart observation remain pending
**State:** Phase 3 audio engine implemented through slice 3c-4 and automatically verified; Phase 4 visual implementation is complete through 4g, with automated verification and a standalone visual challenge page; Phase 5 Events and Cadence implemented and verified across all 14 traces and reference exhibits AD. The Phase 4d4g and Phase 5 review triages ([reviews/02-triage.md](../reviews/02-triage.md), [reviews/03-phase5-triage.md](../reviews/03-phase5-triage.md)) have been worked through: every Tier-1 finding is fixed and covered by a test on the production code path, and the residual items are tracked in [reviews/04-followup-debt.md](../reviews/04-followup-debt.md). Slices 3c-4/4h hardware measurement/listening acceptance and Phase 1 direct-file import/restart observation remain pending; Phase 6 (scenario director) is unblocked
**Planning baseline:** `05fe2b4e021ba86e4a290d05b63c7cae0e386128`
**Exact demarcation:** GC1 direct-file feasibility (10/10 checks), GC2 shared format contracts, GC3 resolution semantics, GC4 clock/PRNG semantics, and GC5 ownership/failure semantics are complete at the Phase 0 contract-oracle level. The Phase 1 production runtime skeleton, Phase 2 common grammar, and audio engine through Phase 3c slice 4's implementation pass automated checks. Phase 1's direct-file two-fixture restart observation, Phase 3's audible acceptance (no sound has been heard from a production build), and real-browser measured master protection remain open. The visual subsystem contract is complete at Phase 4a-4c and its aggregate ceilings are provisional pending the slice 4h GC6 measurement; visual implementation, cadence/event, scenario, final generated-UI, performance, and soak work remains assigned to later phases.
**Exact demarcation:** GC1 direct-file feasibility (10/10 checks), GC2 shared format contracts, GC3 resolution semantics, GC4 clock/PRNG semantics, and GC5 ownership/failure semantics are complete at the Phase 0 contract-oracle level. The Phase 1 production runtime skeleton, Phase 2 common grammar, audio engine through Phase 3c slice 4's implementation, visual engine through 4g, and Phase 5 Events and Cadence pass automated checks. Phase 1's direct-file two-fixture restart observation, Phase 3's audible acceptance (no sound has been heard from a production build), and real-browser measured master protection remain open. The visual subsystem contract is complete at Phase 4a-4c and its aggregate ceilings are provisional pending the slice 4h GC6 measurement; visual implementation through 4g is present; scenario director, final generated-UI, measured performance, and soak work remain assigned to later phases.
The user requested sequential implementation with a stop on problems. The [manual version 3 evidence](evidence/phase0/2026-09-04-user-run-v3.md) verifies embedded data-URL worklet loading in direct-file Chrome. The subsequent [user-performed restart test](evidence/phase0/2026-09-04-user-restart.md) restored Blue Study activity 0.37 and master volume 0.19 immediately on reopening. Native tone output and AudioContext suspend/resume are also observed. Ordinary file import, selection of both exhibits, regular Chrome mode, and [directory cancellation/denial fallback](evidence/phase0/2026-09-05-user-directory-fallback.md) have been confirmed.
@@ -14,8 +14,8 @@ The user requested sequential implementation with a stop on problems. The [manua
| 1 — Runtime skeleton | Implemented; acceptance pending | [Automated evidence](evidence/phase1/2026-09-05-runtime-skeleton.md) passes production-module, lifecycle, PRNG-vector, cache/restore, fixture-validation, and deterministic-build tests. Direct-file two-fixture import/restart remains a user-observed gate. |
| 2 — Common grammar | Complete | [Automated evidence](evidence/phase2/2026-09-05-common-grammar.md) covers production GC2 conformance, typed values, signals, actions, same-tick bindings, transitions, override precedence/release, and parameter restoration. |
| 3 — Audio engine | Slices 3c-13 complete; 3c-4 implementation verified, user acceptance pending | [Contract](evidence/phase3/2026-09-05-phase3c-contract.md), [lifecycle/voices](evidence/phase3/2026-09-05-phase3c-lifecycle-voices.md), [automation](evidence/phase3/2026-09-05-phase3c-automation.md), and [protection evidence](evidence/phase3/2026-09-05-phase3c-protection.md). Slice 4 requires real-browser measurement and user listening; PRD 129 and real GC4 audio acceptance remain open. |
| 4 — Visual engine | Contract slices 4a, 4b, and 4c complete; 4d4h pending | [Slice-4a](evidence/phase4/2026-09-06-phase4a-contract.md), [slice-4b](evidence/phase4/2026-09-06-phase4b-contract.md), and [slice-4c contract evidence](evidence/phase4/2026-09-06-phase4c-contract.md). Format Specification section 17 covers PRD 6976, section 18 covers PRD 7784, and section 19 covers PRD 8589 and 119120. Slice 4d is the first slice that writes renderer code. Slice 4h and the PRD 130 visual challenge are user-observed. |
| 5 — Events and cadence | Not started | Earlier phases and event/cadence contracts |
| 4 — Visual engine | Implemented through 4g; user acceptance pending | [4e procedural evidence](evidence/phase4/2026-09-06-phase4e-procedural.md), [4f execution evidence](evidence/phase4/2026-09-06-phase4f-execution.md), [4g fixtures](evidence/phase4/2026-09-06-phase4g-challenges.md). Fourteen challenge fixtures and first visuals for AD execute without subject-specific renderer code. Slice 4h and PRD 130 display judgment remain open. |
| 5 — Events and cadence | Complete | [Automated evidence](evidence/phase5/2026-09-06-phase5-events-cadence.md) covers contract Section 20, schema/validation with static cycle detection, 1024-action budget, 16-level nesting, priority cadence clocks, minGap, recency relaxation, ambient voices, and manual SAMPLE PRNG isolation. |
| 6 — Scenario director | Not started | Earlier phases and scenario contracts |
| 7 — Dynamic UI | Not started | Earlier phases and UI contracts |
| 8 — Library and persistence hardening | Not started | Earlier phases and library contracts |
@@ -141,3 +141,19 @@ The visual contract now contains no unresolved forward reference, and `npm test`
with zero failures — all three slices are documentation-only. `src/runtime` has no renderer module, the
JSON Schema has no visual definitions, and nothing has been drawn from any build. Whether sections 17-19
pass a multi-model review before slice 4d begins remains the one open decision, and 4d is the next slice.
## Phase 4e4g implementation
Final verification: **223 checks pass**, including **109 Phase 4 tests**. Both standalone builds are deterministic; see the [4f evidence](evidence/phase4/2026-09-06-phase4f-execution.md) for exact test execution and artifact hashes.
The procedural runtime advances graphic, component, emitter and repeater object behaviors on logical ticks, including point motion and morph geometry. System-local automation updates the render inputs; exhibit automation uses the shared resolution pipeline for public visual targets. Spawned instances have bounded ownership, release, disposal and atomic refusal. Seven post-effects execute on frame pixels; aggregate ceilings remain provisional pending measurement.
All fourteen PRD 130 challenge fixtures and first visual compositions for Exhibits AD are authored data. The standalone [visual review page](../prototypes/phase4/XZBT-visual-acceptance.html) supplies reproducible test signals and observation export. Automated checks establish execution and deterministic decisions, not visual quality, live audio acceptance, full reference-exhibit completion, or the 4h benchmark. Earlier contract-only checkpoints below remain historical evidence.
## Phase 5 events and cadence
Final verification: **229 checks pass**, including **14 Phase 5 tests**. Both standalone builds are deterministic; the production artifact hash is `f3f634d7d33b2f8a57cae541fc31b4dcedce88b2ee2648093f92a2cfc168d718`. See the [Phase 5 evidence](evidence/phase5/2026-09-06-phase5-events-cadence.md) for detailed trace coverage and implementation details.
Format Specification revision 0.9 adds Section 20, completing the Cadence and Event subsystems contract with 14 acceptance traces. The JSON Schema and semantic validator enforce DAG event cycle detection (`ERR_CYCLIC_DEPENDENCY`), cadence clock range ordering (`ERR_INVALID_RANGE_ORDER`), and sound cadence metadata. The Action Model enforces the 1024-unit/tick dispatch budget (`ERR_DISPATCH_BUDGET`), 16-level event nesting depth limit, event parameter scoping and strict type validation (`ERR_TYPE_MISMATCH`, `ERR_UNKNOWN_FIELD`), and sound usage gating (`ERR_UNSUPPORTED_TARGET`). The CadenceSubsystem schedules four class clocks (`routine`, `intermittent`, `occasional`, `rare`), enforces the 1.5s minimum gap with class priority ordering, applies anti-repetition recency multipliers (`[0.0, 0.25, 0.50, 0.75]`) with pool relaxation on deadlock, maintains continuous ambient voices, and runs on an isolated PRNG stream (`cadence` domain). Exhibits AD include complete audio buses, sound recipes, cadence clocks, and lifecycle events.
+305 -9
View File
@@ -1,8 +1,8 @@
# XZBT Format Specification 0.1
**XZBT format version:** 0.1
**Document revision:** 0.8
**Status:** Normative specification for shared contracts (document, types, ValueSpec, ConditionSpec, resolution, bindings, and transitions); the Audio subsystem contract is complete (Phase 3a sources/control sources, Phase 3b processing/routing/components/buses, Phase 3c automation/lifecycle/protection); master-protection values remain provisional pending GC6 measurement; the Visual subsystem contract is complete (Phase 4a scene/primitives/transforms/appearance in section 17, Phase 4b components/procedural systems/behaviors/fields in section 18, and Phase 4c automation/lifecycle/camera/post-effects/ceilings in section 19); the aggregate visual ceilings of 19.5 remain provisional pending the slice 4h GC6 measurement; revision 0.8 applies the §§17-19 multi-model review triage in full, resolving thirty repair packages and three additional defects across the visual contract before renderer implementation begins; and the remaining subsystem contracts are in progress
**Document revision:** 0.9
**Status:** Normative specification for shared contracts (document, types, ValueSpec, ConditionSpec, resolution, bindings, and transitions); the Audio subsystem contract is complete (Phase 3a sources/control sources, Phase 3b processing/routing/components/buses, Phase 3c automation/lifecycle/protection); master-protection values remain provisional pending GC6 measurement; the Visual subsystem contract is complete (Phase 4a scene/primitives/transforms/appearance in section 17, Phase 4b components/procedural systems/behaviors/fields in section 18, and Phase 4c automation/lifecycle/camera/post-effects/ceilings in section 19); the aggregate visual ceilings of 19.5 remain provisional pending the slice 4h GC6 measurement; the Cadence and Event Subsystems contract is complete in section 20 (Phase 5); and the remaining subsystem contracts are in progress
**Related resources:** [PRD](../XZBT_0-1_MVP_Product_Requirements_Document.md), [decisions](XZBT_0-1_Gap_Closure_Decisions.md), [verification](XZBT_0-1_Verification_Gates.md)
This document defines normative syntax and runtime semantics for XZBT 0.1 exhibits. An exhibit is a UTF-8 JSON document that configures generic procedural visual, audio, cadence, and orchestration primitives. It does not contain executable JavaScript.
@@ -243,6 +243,8 @@ Durations are expressed as strings containing a non-negative finite number and a
Compound formats (e.g., `"1m30s"`) are strictly invalid (`ERR_INVALID_DURATION`). Internally, the runtime converts all durations to millisecond floating-point numbers.
A duration that is already a **non-negative finite number** is that millisecond value and is accepted wherever this contract says DurationSpec. The literal string is the authored form and the only form a hand-written document uses; the numeric form exists because a DurationSpec may be the resolved output of a ValueSpec or a bounded `TimeSpec` (6.2), which produces milliseconds rather than a literal. A negative or non-finite number is `ERR_INVALID_DURATION`, as is any other type. This applies at every DurationSpec field, `spawn.lifetime` and `spawn.release` (19.2) included; the one exception is an automation track's `at`, which 19.1 fixes as a duration literal only so that point ordering stays decidable at import.
### 6.2 Procedural Bounded Duration (`TimeSpec`)
Where procedural timing is permitted (e.g., cadence intervals, scenario wait steps), a bounded random TimeSpec may be used:
```json
@@ -531,9 +533,9 @@ Complete shared contracts before implementing dependent subsystems. Use PRD sect
| References and bindings | 13, 17, 31-32 | Target-capability table, instance/input scope, evaluation order, cycles, disabled bindings, exact smoothing | **Complete (Rev 0.3 / GC3)** |
| Actions and transitions | 22-30 | Shared `set`/`override` fields and defaults, override target matrix, interrupted transitions, instance IDs; subsystem action matrices remain with their subsystems | **Shared contract complete (Rev 0.3 / GC3)** |
| Audio | 34-60 | Complete recipe/component shapes, node defaults, automation timing, clamp implementation, one-shot endings and tails, unlock behavior, master protection contract | **Complete (Rev 0.4 / Phase 3a-3c):** units, graph objects, all sixteen node types, routing, modulation, graph legality, authoring limits, components, sounds/recipes, buses, automation tracks and precedence, lifecycle states and release, determinable one-shot endings, voice ceilings, unlock and pause behavior, and the master-protection contract shape. Master-protection *values* (peak ceiling, tolerance, release behavior) are provisional pending GC6 measurement |
| Cadence | 61-68 | Exact selection/cooldown/overlap fields, clocks, pool collisions, pending audio, manual sampling isolation and continuous-sample termination | Subsystem contract (Phase 5) |
| Cadence | 61-68 | Exact selection/cooldown/overlap fields, clocks, pool collisions, pending audio, manual sampling isolation and continuous-sample termination | **Complete (Rev 0.9 / Phase 5):** section 20 fixes cadence classes, clocks, interval ranges, selection algorithm, cooldown, overlap policy, anti-repetition relaxation, minimum automatic gap, ambient maintenance, and manual SAMPLE PRNG stream isolation |
| Visuals | 69-89 | Primitive/system/behavior fields, angle and motion units, transform order, depth projection, field behavior, morph compatibility, effect approximation and limits | **Complete (Rev 0.7 / Phase 4a-4c):** section 17 fixes the pipeline, canonical units and the angle convention, the `visuals` container, layers, the scene model and coordinate/fit modes, depth sign and sorting, the fourteen primitives, common properties, transform composition order, appearance and the safe blend set, paths and splines, and the once-at-instantiation resolution boundary. Section 18 fixes visual components and their `inputs` scope, particle systems and their normative integrator, the nine placement distributions, emitters and exact emission timing, repeaters and the `repeat.*` scope, the seventeen-behavior vocabulary and its channel set, the six procedural fields and their normative coherent-noise function, and trails, ribbons, and links. Section 19 fixes visual automation and its two declaration scopes and loop modes, the four visual rows it adds to the section 8.1 table and nothing beyond them, the persistent and spawned system lifecycle and its ownership, the camera matrix and both projection modes, the seven post-effects and the boundary of permitted approximation, and the centralized runtime ceilings for the whole engine. Aggregate ceiling *values* are provisional pending the slice 4h GC6 measurement |
| Events/scenarios | 90-102 | Shared ownership, hooks/failure ordering, condition rearming, deferred ordering/expiry, dispatch limits; full trigger/timeline shapes remain for Phase 6 | **Shared lifecycle contract complete (Rev 0.3 / GC5)** |
| Events/scenarios | 90-102 | Shared ownership, hooks/failure ordering, condition rearming, deferred ordering/expiry, dispatch limits; full trigger/timeline shapes remain for Phase 6 | **Shared lifecycle contract complete (Rev 0.3 / GC5); Event Model 0.1 complete (Rev 0.9 / Phase 5 in section 20); scenario triggers/timelines Phase 6** |
| Generated UI | 103-107 | Widget compatibility, button actions, parameter validation and override display, group/control ordering | Subsystem contract (Phase 7) |
| Runtime/library | 108-112, 117-127 | Clock/audio synchronization, stalls, seed/stream derivation complete in GC4; import equality, update compatibility, transactions, failure recovery, persistence schema remain | **Shared clock/PRNG contract complete (Rev 0.3 / GC4); subsystem remainder Phase 1/8** |
@@ -2010,7 +2012,7 @@ Because `w` comes from the viewer's own generic font, the condensation factor is
### 17.14 Visual field resolution scope
Numeric, boolean, string, and color fields on visual objects are authored with ValueSpec 0.1 (section 4) and DurationSpec 0.1 (section 6) exactly where each field table says so. Every such field is resolved **once**, at its owning object's instantiation boundary, producing that field's **sampled base**, which is constant for the object's lifetime. The base is not necessarily the value the frame draws: a behavior (18.6) or an automation track (19.1) composes over it, and for the four external target families of 8.1 the shared pipeline does. What is fixed at instantiation is the base — the sampling, and therefore every procedural decision — not the effective value. Nested ValueSpecs within one system are sampled in depth-first, property-document order from the system instance's own stream, per section 9.3 — the same rule section 14.4 fixes for audio node fields.
Numeric, boolean, string, and color fields on visual objects are authored with ValueSpec 0.1 (section 4) and DurationSpec 0.1 (section 6) exactly where each field table says so. Every such field is resolved **once**, at its owning object's instantiation boundary, producing that field's **sampled base**, which is constant for the object's lifetime. The base is not necessarily the value the frame draws: a behavior (18.6) or an automation track (19.1) composes over it, and for the four external target families of 8.1 the shared pipeline does. What is fixed at instantiation is the base — the sampling, and therefore every procedural decision — not the effective value. Nested ValueSpecs within one system are sampled in depth-first, property-document order from the system instance's own stream, per section 9.3 — the same rule section 14.4 fixes for audio node fields. One tie-break completes that rule for containers: an object's **own** fields are sampled in document order, and its `children` (or, for a `component`, the expanded `content`) are sampled **after** all of them, wherever the container key sits in the object. A group's style scope has to exist before its descendants can inherit it (17.12), so the two orders cannot be interleaved, and stating the tie-break is what keeps two renderers at the same stream position.
The instantiation boundary is: activation, for a persistent system declared in `visuals.systems`; the spawn moment, for a spawned system (19.2); and the emission or repetition moment, for an object created by an emitter or repeater (18.4, 18.5).
@@ -2161,7 +2163,7 @@ A `particles` system maintains a bounded pool of lightweight items that share on
| --- | --- | :---: | --- | --- |
| `render` | visual object or `component` object | Yes | — | The object drawn for each particle (17.9, 18.1). Its own `position`, `z`, and `visible` are `ERR_UNKNOWN_FIELD`; the particle owns those. |
| `capacity` | number | No | `256` | Integer, `1` to `4096`. Maximum concurrent particles in the pool. |
| `count` | ValueSpec\<number\> | No | `0` | Integer. Particles created at the system's instantiation boundary (17.14). |
| `count` | ValueSpec\<number\> | No | `0` | Integer, `0` through `capacity`; a larger resolved initial population is `ERR_VISUAL_LIMIT_EXCEEDED`. Particles created at the system's instantiation boundary (17.14). |
| `rate` | ValueSpec\<number\> | No | `0` | Particles per logical second, continuous. Emission timing is 18.4. |
| `burst` | array | No | `[]` | Burst entries (18.4), `0` to `16`. |
| `limit` | number | No | — | Integer. Total particles this system may ever create. Absent is unbounded. |
@@ -2539,7 +2541,8 @@ with `curve` the four-curve set of 18.2 evaluated from `0` to `1`. The contribut
| `drift` | **Accumulating.** Its contribution grows by `velocity * dt` each tick, with `velocity` scaled by `(1 - damping)^dt` first. |
| `rotate` | **Accumulating** on `transform.rotation`, by `speed * dt` each tick. |
| `wander`, `bounce`, `attract`, `repel`, `field-follow` in `force` or `velocity` mode | **Accumulating** through velocity: they write the item's velocity, which the integrator of 18.2 turns into displacement. |
| `oscillate`, `pulse`, `twinkle`, `orbit`, `follow-path`, `noise-displace`, `point-wander`, `field-follow` in `direct` mode, `morph` | **Fresh.** The contribution is a pure function of `t` and the resolved configuration, recomputed from scratch every tick and never integrated. |
| `wrap` | **Accumulating.** A wrap is a permanent relocation, not a per-tick displacement: crossing an edge adds the bounds extent to the object's accumulated offset once and it stays added. |
| `oscillate`, `pulse`, `twinkle`, `orbit`, `follow-path`, `noise-displace`, `point-wander`, `face-motion`, `field-follow` in `direct` mode, `morph` | **Fresh.** The contribution is a pure function of `t` and the resolved configuration, recomputed from scratch every tick and never integrated. `field-follow` in `direct` mode displaces by `field x strength` scene units with **no** `dt` factor — an instantaneous displacement, which is what makes it the mode that does not integrate. |
For `orbit` in particular the contribution is an absolute placement expressed as an offset from the object's resolved base position `p0`:
@@ -2821,7 +2824,7 @@ This section closes the Visual contract sections 17 and 18 opened. It covers vis
Sections 17 and 18 define what an exhibit may *declare*. This section defines what the runtime *does* with it over time and what it refuses to do, which is the same division sections 14-15 and 16 use for audio. Every forward reference to 19.1, 19.2, and 19.5 left open by sections 17 and 18 resolves here; after this section the visual contract contains no unresolved forward reference.
**Implementation status.** No visual runtime code exists. Sections 17, 18, and 19 are contract only: the JSON Schema has no visual definitions, `src/runtime` has no renderer module, and nothing has been drawn from any build. Slice 4d begins the renderer; 4f implements this section. The ceilings of 19.5 are provisional in the same sense the master-protection values of 16.7 are provisional their *shape* is normative now, their *values* are confirmed by the slice 4h GC6 measurement.
**Implementation status.** Slices 4d4g implement sections 1719 in the production visual runtime, with automated contract/execution tests and authored challenge fixtures. Display judgment and slice 4h measurement remain pending. The ceilings of 19.5 remain provisional: their shape is normative, and their values require GC6 measurement.
### 19.1 Visual automation
@@ -3115,7 +3118,7 @@ r_device = r_scene * zoom * sqrt(sx * sy) * dpr_effective
This is the uniform appearance factor `g` of 19.3 with `dpr_effective` (19.5) folded in, and with the two terms a post-effect **cannot** have deliberately absent: an effect has no object, so the perspective factor `k` is `1`; and it runs after every layer is composited, so no layer's `parallax` enters it. Borrowing either — taking the depth of the nearest object, or the parallax of the last layer drawn — would make a full-frame effect depend on scene content and is specifically excluded. Under nonuniform `stretch` the geometric mean `sqrt(sx * sy)` is what a radius with no axis can take, exactly as for a stroke width. Where the effect is computed at a reduced resolution factor `q` (below), the radius used inside that buffer is `r_device * q`, so the upsampled result matches the full-resolution one to within the approximation the warning already declares.
**Effect parameters resolve once, at activation.** Every ValueSpec in `visuals.effects`, including each entry's `enabled`, is resolved at exhibit activation — the effects chain is exhibit-scoped, so activation is its instantiation boundary (17.14). Sampling order is array order, and within one entry document field order after `type`, so a fixture's stream position after the chain is fixed. `color` parameters and `type` are authored literals, not ValueSpecs (17.12). The resolved value is the base the shared pipeline of 8.1 and the automation of 19.1 then compose over; `enabled` has no automation stage, being boolean, but is a binding and `override` target through its entry's row.
**Effect parameters resolve once, at activation.** Every ValueSpec in `visuals.effects`, including each entry's `enabled`, is resolved at exhibit activation — the effects chain is exhibit-scoped, so activation is its instantiation boundary (17.14). Sampling order is array order, and within one entry document field order after `type`, so a fixture's stream position after the chain is fixed. `color` parameters and `type` are authored literals, not ValueSpecs (17.12). The resolved value is the base the shared pipeline of 8.1 and the automation of 19.1 then compose over; `enabled` resolves once and has no external target capability. Section 19.1 exposes only numeric effect parameters; enabling an effect dynamically requires a future contract revision.
**Filter parameter names map to the 17.12 operations without renaming.** `color-adjust`'s `brightness`, `contrast`, and `saturation` are the `brightness`, `contrast`, and `saturate` operations of 17.12, and its `hueRotate` is `hue-rotate`, with identical ranges and semantics. The authored spellings differ because one is an effect parameter and the other a filter `type` token; the operations are the same and a runtime implements them once.
@@ -3280,3 +3283,296 @@ User-observed, and **not** satisfiable by the above:
Trace 6 of 17.16 — the perspective factor at three depths — stays in 4d. Depending on 19.3's static contract is what makes it testable; dropping it to avoid the dependency would remove the one automated check on the composition chain, which is where the unit-mismatch defects of this review lived. A parsed stub is never a passed runtime trace: a slice reports a trace as passed only when the behavior it names actually ran.
Traces 1-19 belong to slice 4f. Traces 20 and 21 close slice 4h. Phase 4 is not accepted until both do, no matter how many automated traces pass, and the ceilings of 19.5 are not measured until trace 21 says so.
---
## 20. Cadence and Event Subsystems Contract (Phase 5)
### 20.1 Pipeline and scope
The Cadence and Event subsystems provide the temporal orchestration and procedural trigger mechanisms that animate an exhibit between continuous modulation and autonomous scenarios.
Cadence operates on the engine's fixed logical clock ($60\text{ Hz}$, $1000/60\text{ ms}$ fixed step, section 9.1). It drives automatic recurring sound activity through independent class schedulers, maintaining aesthetic variety through weighted random selection, cooldowns, overlap prevention, and recency-based anti-repetition penalties.
Events provide reusable, parameterized bundles of actions that execute atomically within a logical tick. Events may be invoked by cadence, by manual UI interactions (soundboard / buttons), by scenarios, or by other events up to a bounded nesting depth.
### 20.2 Canonical cadence classes and usage separation
#### 20.2.1 Cadence classes (PRD 61)
Every sound in an exhibit that participates in automatic scheduling or ambient maintenance is assigned a canonical cadence class in its `cadence.class` field:
| Class | Semantic Meaning | Scheduling Policy |
| :--- | :--- | :--- |
| `ambient` | Persistent background audio beds, drones, or continuous atmospheres. | Maintained automatically while audio is unlocked. Not scheduled by pulse intervals. |
| `routine` | Frequent, predictable automatic background activity. | Independent periodic scheduler (default 10s45s). |
| `intermittent` | Periodic recurring activity with noticeable gaps. | Independent periodic scheduler (default 45s4m). |
| `occasional` | Infrequent, prominent exhibit events. | Independent periodic scheduler (default 3m15m). |
| `rare` | Unusual or special exhibit events. | Independent periodic scheduler (default 15m60m). Highest priority during simultaneous firings. |
| `scenario` | Orchestrated audio reserved exclusively for scenario timelines. | **Never** scheduled by the automatic cadence engine. |
Any other class value is a semantic validation error (`ERR_SCHEMA_VALIDATION`).
#### 20.2.2 Usage and cadence separation (PRD 62)
A sound's `usage` array defines which execution surfaces are authorized to invoke it:
* `"automatic"`: Authorized for invocation by the automatic cadence engine.
* `"manual"`: Authorized for invocation by direct user actions (soundboard / UI buttons).
* `"scenario"`: Authorized for invocation by scenario actions or timelines.
Defaults to `["automatic"]` if omitted.
A sound action executed from a manual context naming a sound whose `usage` does not include `"manual"` is rejected with `ERR_UNSUPPORTED_TARGET`. A sound action executed from a scenario naming a sound whose `usage` does not include `"scenario"` is rejected with `ERR_UNSUPPORTED_TARGET`. The automatic cadence engine considers only sounds whose `usage` includes `"automatic"`.
### 20.3 Cadence configuration container (`cadence`)
The top-level `cadence` object configures exhibit-wide cadence behavior:
```json
{
"cadence": {
"intensity": { "ref": "parameters.activity" },
"minGap": "1.5s",
"clocks": {
"routine": { "min": "8s", "max": "30s" },
"intermittent": { "min": "30s", "max": "3m" },
"occasional": { "min": "2m", "max": "10m" },
"rare": { "min": "10m", "max": "45m" }
}
}
}
```
| Field | Type | Required | Default | Description |
| :--- | :--- | :---: | :--- | :--- |
| `intensity` | ValueSpec 0.1 | No | `1.0` | Global cadence intensity. Resolves to a number clamped to `[0, 1]`. |
| `minGap` | DurationSpec 0.1 | No | `"1.5s"` | Minimum time separation between consecutive automatic one-shot sound starts. |
| `clocks` | object | No | Default ranges | Map of class ID (`routine`, `intermittent`, `occasional`, `rare`) to `{ min, max }` ranges. |
Unrecognized fields in `cadence` produce `ERR_UNKNOWN_FIELD`. In `clocks.<class>`, `min` and `max` must be valid positive DurationSpec 0.1 strings with `min <= max`; inverted bounds produce `ERR_INVALID_RANGE_ORDER`.
### 20.4 Sound cadence metadata (`sounds.<id>.cadence`)
A sound's optional `cadence` property configures its behavior within the cadence engine:
```json
{
"sounds": {
"relay-click": {
"name": "Relay Click",
"usage": ["automatic", "manual"],
"cadence": {
"class": "routine",
"weight": 1.5,
"cooldown": "5s",
"overlap": false,
"when": { "op": ">", "left": { "ref": "state.pressure" }, "right": 0.2 }
},
"recipe": { ... }
}
}
}
```
| Field | Type | Required | Default | Description |
| :--- | :--- | :---: | :--- | :--- |
| `class` | enum | **Yes** | — | One of the six canonical classes. |
| `weight` | numeric ValueSpec 0.1 | No | `1.0` | Base selection weight. Evaluated from the cadence stream. Must be $\ge 0$. |
| `cooldown` | DurationSpec 0.1 | No | `"0ms"` | Minimum time after firing before this sound may be selected again. |
| `overlap` | boolean | No | `true` | If `false`, cannot be selected if any voice of this sound is active in the engine. |
| `when` | ConditionSpec 0.1 | No | — | Optional eligibility condition. Must evaluate true for selection. |
Unrecognized fields in `sounds.<id>.cadence` produce `ERR_UNKNOWN_FIELD`. Negative literal weight produces `ERR_OUT_OF_BOUNDS`.
### 20.5 Cadence selection algorithm and anti-repetition (PRD 64, 65)
Each automatic class (`routine`, `intermittent`, `occasional`, `rare`) maintains an independent timer and a recency history queue of depth $4$.
#### 20.5.1 Firing step
When a class timer expires:
1. **Eligible Pool:** Collect all declared sounds where `cadence.class` matches the firing class, `usage` contains `"automatic"`, and any declared `when` condition evaluates to `true`.
2. **Cooldown Filter:** Exclude any sound where $\text{currentLogicalTime} - \text{lastFiredTime} < \text{cooldown}$.
3. **Overlap Filter:** For each sound with `overlap === false`, inspect the audio subsystem's active voices (`SCHEDULED`, `ACTIVE`, `RELEASING`). Exclude the sound if any voice of that sound is currently active.
4. **Base Weights:** Evaluate `weight` for each remaining sound using `rng.stream('cadence', '<class>:weight:<ordinal>')`. Clamp to $\ge 0$.
5. **Anti-Repetition Multipliers:** For each remaining candidate, look up its position in the class's recency queue:
* Most recently selected ($1$ selection ago): multiplier $0.0$
* $2$ selections ago: multiplier $0.25$
* $3$ selections ago: multiplier $0.50$
* $4$ selections ago: multiplier $0.75$
* Older / not in recency queue: multiplier $1.0$
$\text{effectiveWeight} = \text{baseWeight} \times \text{multiplier}$.
6. **Pool Relaxation (PRD 65):** If the sum of effective weights for all remaining candidates is $0$ (which occurs when all eligible sounds are penalized to zero, such as in single-sound pools):
* Relax anti-repetition penalties: set all multipliers to $1.0$ ($\text{effectiveWeight} = \text{baseWeight}$).
* If the sum of base weights is still $0$, the class firing is skipped with no sound played.
7. **Weighted Selection:** Draw a pseudo-random value $u \in [0, 1)$ from `rng.stream('cadence', '<class>:select:<ordinal>')` and pick the winning sound proportional to its effective weight.
8. **Execution:** Instantiate and play the winning sound via the audio subsystem.
9. **History Update:** Record the winning sound's firing timestamp $\text{lastFiredTime} = \text{currentLogicalTime}$. Append the winning sound ID to the front of the class recency queue, trimming the queue to maximum depth $4$.
10. **Reschedule:** Sample the next interval uniformly from $[T_{\min}, T_{\max}]$ using `rng.stream('cadence', '<class>:interval:<ordinal>')`, scale by $1 / \text{intensity}$, and set the next class timer.
### 20.6 Minimum automatic gap and priority scheduling (PRD 67)
To prevent simultaneous or jarringly close auditory collisions between different automatic classes:
* The runtime tracks $\text{lastAutomaticSoundTime}$, the logical timestamp of the most recent automatic one-shot start.
* A class firing is permitted only if $\text{currentLogicalTime} - \text{lastAutomaticSoundTime} \ge \text{minGap}$ (default $1.5\text{s}$).
* If multiple classes become due simultaneously or while a gap hold is in effect, they are queued and serviced in strict canonical priority order:
$$\text{rare} > \text{occasional} > \text{intermittent} > \text{routine}$$
* Deferred classes retain their firing opportunity: when the required $\text{minGap}$ has elapsed, the highest-priority deferred class fires immediately and resets the gap timer.
### 20.7 Cadence intensity (PRD 66)
The `cadence.intensity` ValueSpec resolves on each logical tick to a finite number clamped to $[0, 1]$.
* Effective interval: $T_{\text{effective}} = T_{\text{base}} / \text{intensity}$.
* When $\text{intensity} \le 0$ (or $< 10^{-6}$):
* All automatic one-shot class scheduling pauses.
* No class timers advance or fire.
* Existing playing voices play out their natural release or ending.
* When $\text{intensity}$ rises above zero, timers resume with intervals scaled by the new intensity.
* Continuous `ambient` sounds are unaffected by cadence intensity unless explicitly bound to a parameter.
### 20.8 Ambient sound maintenance (PRD 61, 139)
Sounds with `cadence.class === "ambient"` and `usage` containing `"automatic"` represent persistent exhibit beds:
* When the audio subsystem is active and unlocked, the runtime ensures that continuous ambient sounds are instantiated and playing.
* If an ambient voice finishes, releases, or fails unexpectedly, the runtime restarts it at the next logical tick.
* Pausing the performance pauses ambient voices; stopping or deactivating disposes them cleanly.
### 20.9 Manual SAMPLE isolation semantics (PRD 68)
Manual soundboard playback (SAMPLE buttons in the UI) allows auditioning sounds independently of the performance:
* Manual SAMPLE playback uses exclusively the isolated PRNG stream `sample` (`rng.stream('sample', '<soundId>:<ordinal>')`).
* SAMPLE playback:
* Does **not** advance cadence clocks.
* Does **not** alter class recency history.
* Does **not** alter sound cooldown timestamps.
* Does **not** alter automatic selection weights.
* Does **not** reset or perturb the minimum automatic gap timer.
* Two runs of an exhibit with arbitrary manual SAMPLE buttons clicked in between produce identical automatic cadence sequences and visual frames.
### 20.10 Event Model 0.1 (`events.<id>`) (PRD 90, 91)
The top-level `events` container defines reusable, named action bundles:
```json
{
"events": {
"minor-disturbance": {
"inputs": {
"intensity": { "type": "number", "default": 0.5 },
"pitch": { "type": "number", "default": 440 }
},
"actions": [
{
"type": "sound",
"sound": "warning-tone",
"with": { "pitch": { "ref": "inputs.pitch" } }
},
{
"type": "override",
"target": "parameters.activity",
"value": { "ref": "inputs.intensity" },
"scope": "duration",
"duration": "2s"
}
]
}
}
}
```
* `inputs`: Optional map of input identifier to parameter-like declaration (`type`, `default`, optional `min`, `max`, `step`, `values`).
* `actions`: Required array of Action Model 0.1 action objects. Must contain at least one action.
Unrecognized properties in `events.<id>` or `inputs.<id>` produce `ERR_UNKNOWN_FIELD`. Invalid identifiers produce `ERR_INVALID_ID`.
### 20.11 Event action execution (`type: "event"`) (PRD 27, 91)
An `event` action invokes a declared event:
```json
{
"type": "event",
"event": "minor-disturbance",
"with": {
"intensity": 0.8,
"pitch": 880
},
"when": { "op": ">", "left": { "ref": "state.power" }, "right": 0.5 },
"chance": 0.9,
"critical": false
}
```
* `event`: ID of the target declared in `events`. Undeclared target is `ERR_INVALID_REFERENCE`.
* `with`: Map of input values. Evaluated in the caller's context before event entry. Values must match declared input types without implicit coercion (`ERR_TYPE_MISMATCH`). Undeclared input names in `with` produce `ERR_UNKNOWN_FIELD`. Omitted inputs take declared defaults; missing required inputs without defaults fail with `ERR_SCHEMA_VALIDATION`.
* **Input Scoping:** Within the invoked event actions, references matching `inputs.<name>` resolve to the evaluated input value. Inputs are strictly local to that event invocation instance.
* **Context Inheritance:** Nested actions inherit the caller's ownership context (scenario instance owner or performance root) and critical propagation setting (section 10.1).
### 20.12 Sound action execution (`type: "sound"`) (PRD 26)
A `sound` action triggers an audio voice:
```json
{
"type": "sound",
"sound": "relay-click",
"with": {
"pitch": 440
},
"ownership": "scenario"
}
```
* `sound`: ID of the target declared in `sounds`. Undeclared target is `ERR_INVALID_REFERENCE`.
* `with`: Optional map of input values for sound component/recipe expressions.
* `ownership`: Optional ownership assignment (`"performance"` | `"scenario"` | `"persistent"`). Continuous sounds invoked by scenarios inherit scenario ownership and release at scenario completion (section 10.1); `"persistent"` keeps resources active across scenario boundaries.
* **Usage Check:** The sound must permit the caller's execution mode:
* Dispatched from manual UI: sound `usage` must include `"manual"`.
* Dispatched from a scenario: sound `usage` must include `"scenario"`.
* Dispatched from cadence: sound `usage` must include `"automatic"`.
Violations produce `ERR_UNSUPPORTED_TARGET`.
### 20.13 Dispatch budget, nesting limits, and static cycle detection (PRD 91, Section 10.5)
#### 20.13.1 Static cycle detection
The static dependency graph of event-to-event invocations (direct or indirect) must be a Directed Acyclic Graph (DAG). Any cycle (e.g. $A \to B \to A$) is rejected during semantic validation at import with `ERR_CYCLIC_DEPENDENCY`.
#### 20.13.2 Runtime nesting depth limit
Event execution tracks call stack nesting depth. If an event invocation reaches depth $> 16$, execution halts with `ERR_DISPATCH_BUDGET`.
#### 20.13.3 Per-tick ordinary dispatch budget
To prevent infinite data-dependent feedback loops between events, state mutations, and triggers:
* A hard ceiling of $1024$ ordinary dispatch units is enforced per logical tick.
* Entering an event consumes $1$ dispatch unit.
* Attempting an action consumes $1$ dispatch unit (including actions skipped due to `when` or `chance`).
* When the $1025\text{th}$ unit would be consumed, the runtime emits `ERR_DISPATCH_BUDGET`, discards the remaining ordinary queue for that tick, and terminates any affected scenario owner as `FAILED`.
* Mandatory termination hooks (`onComplete`, `onCancel`) are exempt from the ordinary budget and use their own isolated $256$-action ceiling (section 10.2).
### 20.14 Diagnostic codes
Phase 5 reuses existing standard codes from the Section 7 table:
* `ERR_SCHEMA_VALIDATION`: Missing required fields, invalid types, or invalid enum values (e.g. unknown cadence class).
* `ERR_UNKNOWN_FIELD`: Undeclared property in `cadence`, `sounds.<id>.cadence`, `events.<id>`, or event action `with` map.
* `ERR_INVALID_ID`: Invalid event identifier.
* `ERR_INVALID_REFERENCE`: Target sound or event does not exist, or dangling reference in input expression.
* `ERR_UNSUPPORTED_TARGET`: Sound action invokes a sound whose `usage` excludes the caller's context.
* `ERR_TYPE_MISMATCH`: Action `with` value type does not match declared event input type.
* `ERR_CYCLIC_DEPENDENCY`: Static event invocation dependency cycle detected.
* `ERR_INVALID_RANGE_ORDER`: `clocks.<class>` `min` duration exceeds `max` duration.
* `ERR_OUT_OF_BOUNDS`: Negative cadence weight.
* `ERR_DISPATCH_BUDGET`: Per-tick ordinary dispatch budget ($1024$) or event nesting depth ($16$) exceeded.
### 20.15 Required traces before Phase 5 implementation is accepted
1. **Schema & Validation:** Valid and malformed `cadence`, `sounds.<id>.cadence`, and `events` blocks validate or reject with documented error codes.
2. **Static Cycle Detection:** Direct self-invocation ($A \to A$) and transitive cycles ($A \to B \to C \to A$) fail semantic validation with `ERR_CYCLIC_DEPENDENCY`. Valid acyclic event hierarchies pass.
3. **Event Execution & Scoping:** Event action executes member actions in order, resolves `inputs.<name>` from passed `with` values, falls back to declared defaults, and isolates inputs across concurrent invocations.
4. **Event Nesting & Depth Limit:** Nested events pass inputs through multiple levels up to depth 16; depth 17 raises `ERR_DISPATCH_BUDGET`.
5. **Per-Tick Dispatch Budget:** Ordinary actions and events exceeding $1024$ units in a single tick halt with `ERR_DISPATCH_BUDGET` and discard remaining ordinary queue while preserving scenario termination cleanup.
6. **Sound Action Execution:** Sound actions validate sound references, pass inputs to recipes, check caller usage permissions, and reject unauthorized usage with `ERR_UNSUPPORTED_TARGET`.
7. **Cadence Eligibility & Cooldown:** A sound with `when: false` or active cooldown ($\text{elapsed} < \text{cooldown}$) is excluded from selection.
8. **Cadence Overlap Policy:** A sound with `overlap: false` is excluded from selection while any voice of that sound is active in the audio engine; a sound with `overlap: true` admits overlapping voices up to voice ceilings.
9. **Cadence Anti-Repetition Multipliers:** Consecutive firings verify multipliers $0.0$, $0.25$, $0.50$, $0.75$, $1.0$ against the class recency history.
10. **Pool Relaxation:** When all eligible sounds have effective weight zero, anti-repetition penalties relax to base weights and selection succeeds rather than permanently stalling.
11. **Cadence Intensity Scaling:** Decreasing intensity increases interval spacing ($T / \text{intensity}$); setting intensity to $0$ stops one-shot class scheduling completely.
12. **Minimum Automatic Gap & Priority:** Simultaneous class firings enforce `minGap` (default $1.5\text{s}$) between audio starts and service deferred classes in priority order ($\text{rare} > \text{occasional} > \text{intermittent} > \text{routine}$).
13. **Ambient Maintenance:** Ambient sounds (`cadence.class: "ambient"`) auto-start on audio unlock and are maintained by the runtime.
14. **Manual SAMPLE Isolation:** Manual SAMPLE playback draws exclusively from `rng.stream('sample', ...)` and leaves cadence clocks, cooldowns, recency history, and automatic PRNG sequences byte-identical.
+5 -5
View File
@@ -44,15 +44,15 @@ Phase 4 is the largest milestone in this plan: twenty-one PRD sections (69-89) p
| 4a — Contract I | **Complete at Revision 0.5.** Format Specification section 17: pipeline and canonical units, the `visuals` container, layers, scene model and coordinate/fit modes (PRD 70), 2.5D depth (PRD 71), the fourteen geometry primitives (PRD 72), common visual properties (PRD 73), the transform model (PRD 74), appearance and the safe blend set (PRD 75), and paths and splines (PRD 76). Documentation only; no runtime change. | Sections 1-13 | — |
| 4b — Contract II | Format Specification section 18: visual components (PRD 77), particle systems (PRD 78), placement distributions (PRD 79), emitters (PRD 80), repeaters (PRD 81), the behavior vocabulary (PRD 82), procedural fields (PRD 83), and trails, ribbons, and links (PRD 84). Documentation only. **Complete at Revision 0.6.** | 4a | — |
| 4c — Contract III | **Complete at Revision 0.7.** Format Specification section 19: visual automation and loop modes (PRD 85), visual lifecycle and ownership (PRD 86), camera and projection (PRD 87), post-processing (PRD 88), visual safety limits (PRD 89), the centralized runtime ceilings of PRD 120, and the four system-level visual rows added to the section 8.1 target-capability table. Documentation only. | 4a, 4b | — |
| 4d — Renderer core | Canvas 2D backend, the scene/coordinate/fit resolution, layers, the transform stack, the fourteen primitives, appearance and paint, paths and splines, and depth sorting. Widest new-code surface in the phase. | 4a | — |
| 4e — Procedural systems | Visual components, particles, distributions, emitters, repeaters, behaviors, fields, trails/ribbons, and distance links. | 4b, 4d | — |
| 4f — Automation, lifecycle, and effects | Visual automation into the shared section 8 resolution path, spawned-system lifecycle and ownership, camera projection, post-effect passes, and enforcement of the centralized ceilings. Touches the Phase 2 and Phase 3c resolution path, so it carries the widest regression surface in Phase 4. | 4c, 4e | — |
| 4g — Challenge fixtures and exhibit visuals | The fourteen PRD 130 challenge fixtures, proven to need no subject-specific renderer code, and the first visuals for Exhibits A-D. | 4d, 4e, 4f | — |
| 4d — Renderer core | **Implemented; automated checks pass.** Canvas 2D backend, the scene/coordinate/fit resolution, layers, the transform stack, the fourteen primitives, appearance and paint, paths and splines, and depth sorting. Widest new-code surface in the phase. | 4a | — |
| 4e — Procedural systems | **Implemented; automated checks pass.** Visual components, particles, distributions, emitters, repeaters, behaviors, fields, trails/ribbons, and distance links. | 4b, 4d | — |
| 4f — Automation, lifecycle, and effects | **Implemented; automated checks pass.** Visual automation into the shared section 8 resolution path, spawned-system lifecycle and ownership, camera projection, post-effect passes, and enforcement of the centralized ceilings. Touches the Phase 2 and Phase 3c resolution path, so it carries the widest regression surface in Phase 4. | 4c, 4e | — |
| 4g — Challenge fixtures and exhibit visuals | **Implemented; automated checks pass.** The fourteen PRD 130 challenge fixtures, proven to need no subject-specific renderer code, and the first visuals for Exhibits A-D. | 4d, 4e, 4f | — |
| 4h — Early combined GC6 benchmark | Freeze the benchmark ID, version, seed, and counts; run the 30-second warm-up and 120-second measurement window; record frame intervals, runtime counters, and memory samples; derive the workload ceilings that fix later optimization strategy. | 4g | **User-observed.** Requires a real reference computer and browser at 1920 x 1080; cannot be closed by automated tests. |
Slices 4a through 4g are implementable and verifiable without a display measurement. Slice 4h and the PRD 130 visual acceptance challenge are not: the challenge's judgement is visual, and the benchmark is a hardware measurement, which the recorded test boundary assigns to user-performed runs. Phase 4 is not accepted until 4h and the visual challenge close, regardless of the state of the first seven slices.
The three contract slices are complete. They landed before any implementation slice because the section 17-19 contract fixes decisions that 4d through 4f would otherwise have to relitigate: the resolution boundary for visual ValueSpec fields, the transform composition order, depth semantics, and which visual properties are externally addressable at all — which section 19.1 settles at exactly four system-level target families and nothing per-object. Whether the finished contract also passes a multi-model review before 4d begins — the pass that found thirteen defects in sections 14-16, nine of which any single reviewer would have missed — is the one open decision, and it is now due rather than pending, because 4d is the next slice.
The contract and implementation slices through 4g are complete. The renderer baseline and procedural work were extended with executable automation, lifecycle, effects, and eighteen authored compositions. Evidence records under `docs/evidence/phase4` distinguish automated execution from pending display judgment and 4h hardware measurement.
Phase 3 remains unaccepted while its user-observed gates (traces 15-17 of section 16.11 and the Phase 1 direct-file restart observation) are open. Those gates are pending measurement, not failed, and no Phase 4 slice depends on them; Phase 4 nonetheless carries that debt forward and cannot close it.