Files
XZBT/docs/evidence/phase4/2026-09-06-phase4c-contract.md
T
LabyricornandClaude Opus 5 d3cd688a3c docs(visual): close the visual contract with the phase 4c automation, lifecycle, camera, effects, and ceiling rules
Format Specification revision 0.7 adds section 19, the third and final
visual contract section, covering PRD 85-89 and 119-120.

Visual automation reuses the audio track shape of 16.1 and adds only
`loop`. It is declared in two scopes: `visuals.automation`, measured from
activation, and `visuals.systems.<id>.automation`, measured from that
system's instantiation boundary, so a track on a spawned system behaves
identically however late the spawn happens.

Section 8.1 gains exactly four visual rows, all system-level: camera
fields, layer opacity, system visibility, and post-effect parameters. No
per-object geometry, transform, or style property becomes externally
addressable in 0.1, so the statements in 17.14 and trace 19 of 18.10
survive unchanged. This is the decision 17.14 and 18.6 both deferred.

Systems are persistent or spawned by an explicit `lifecycle` enum. A
spawned system is a template, instantiated only by a spawn action, with a
five-state machine plus FAILED, a `0ms` default release, and a spawn
ceiling that refuses rather than evicts.

The camera matrix is normative, including that parallax multiplies the
camera translation only. `projection` is authored once and is neither
automatable nor bindable.

Seven post-effects, applied in normative array order with a stated
approximation boundary. `grain` is explicitly exempt from reproducibility
and consumes no procedural stream.

Section 19.5 is the centralized ceiling table PRD 120 requires. It
separates authoring bounds, which reject an exhibit at import, from
runtime ceilings, which shed work by a documented deterministic rule and
keep the exhibit running, and it restates every ceiling first fixed in
sections 10 and 14 through 18. Its aggregate values are provisional
pending the slice 4h GC6 measurement.

Two diagnostic codes added: ERR_INVALID_EFFECT_TYPE and
WARN_VISUAL_CEILING. ERR_AUTOMATION_CONFLICT widens to cover a visual
scope and the behavior collision rather than minting a second code.

Documentation only: no runtime, schema, fixture, or test file was
touched. The visual contract now contains no unresolved forward
reference, and npm test still passes 102 tests with zero failures.

Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AzX3848PwqYwzdUCWvjegL
2026-09-06 04:29:40 +00:00

15 KiB

Phase 4c evidence — Visual subsystem contract, part III (Format Specification section 19)

Date: September 6, 2026 Slice: 4c — Contract III (documentation only; no runtime change) Baseline: main at 2deec01 (Phase 4a and 4b devlog) Specification revision: 0.6 → 0.7 PRD coverage: 85-89, 119-120

What this slice fixes

Section 19 closes the Visual contract. It covers visual automation and its loop modes, the visual lifecycle and ownership of persistent and spawned systems, the camera and its two projection modes, the seven-effect post-processing chain and the exact boundary of permitted renderer approximation, and visual safety limits together with the centralized runtime ceilings of the whole engine.

With this slice the visual contract contains no unresolved forward reference. Every reference to 19.1, 19.2, and 19.5 left open by sections 17 and 18 now resolves, and the section 11 register's Visuals row reads Complete (Rev 0.7 / Phase 4a-4c).

Decisions taken, so 4f does not relitigate them

  1. Automation has two declaration scopes, each with its own time origin. visuals.automation is measured from activation and reaches scene, layer, camera, and effect properties; visuals.systems.<id>.automation is measured from that system's instantiation boundary and reaches that system's own properties. The second scope is what makes a track on a spawned system behave identically however late the spawn happens — the property 16.1 gives an audio track measured from its sound instance's start. A track naming a target in the other scope is ERR_INVALID_REFERENCE.
  2. The track shape is the audio track shape. target, mode, interpolation, points, the three modes, the four curves, the exponential fallback and its WARN_AUTOMATION_FALLBACK, the two-to-256 point bound, and the strictly-increasing duration-literal at are all reused verbatim from 16.1 rather than restated differently. loop is the only field visual automation adds, because it is the only thing PRD 85 asks for that PRD 54 did not.
  3. ping-pong counts complete round trips. One count unit is forward and back, so a finite count always ends where it began. repeat restarts from the first point and may step discontinuously at the wrap; that is the author's choice, not a defect.
  4. The section 8.1 table gains exactly four visual rows, all system-level: visuals.camera.<field>, visuals.layers.<id>.opacity, visuals.systems.<id>.visible, and visuals.effects[<index>].<param>. Nothing per-object becomes externally addressable in 0.1. This is the decision 17.14 and 18.6 both deferred here, and it is deliberately the smallest surface that expresses what the reference exhibits need: Exhibit D's instrument displays are state-driven geometry inside a system, which automation and behaviors reach with no external capability at all. A later revision can widen the surface compatibly; nothing can narrow it.
  5. visuals.layers.<id>.visible is deliberately absent from that table. A layer hides by animating opacity to 0, which is continuous and needs no second mechanism.
  6. The automatable registry is broader than the section 8.1 rows, and that is the point. Automation is declared inside the subsystem that owns the property, so adding it never widens the subsystem's external surface — the rule 16.2 states for audio. An emitter's rate is automatable and is still ERR_UNSUPPORTED_TARGET for a binding, set, or override, so trace 19 of 18.10 passes unchanged.
  7. A behavior and an automation track on the same object channel is ERR_AUTOMATION_CONFLICT. A behavior accumulates or multiplies onto a channel while an automation mode displaces the base; two writers with different composition rules on one scalar have no defined answer. Where they do not collide, the shared pipeline resolves first and behaviors compose over the resolved value in array order. This is the composition rule 18.6 defers here.
  8. lifecycle is an explicit enum, not an inference. A system is persistent unless it says spawned; lifecycle fields on a persistent system are ERR_UNKNOWN_FIELD. Making the discriminator the presence of a field would have been implicit magic that no validator could report clearly.
  9. A spawned system is a template. It is declared, validated, and counted at import, and is not instantiated or drawn at activation. Its instantiation boundary is the spawn moment and its stream child key is <system-id>#<spawn-ordinal>, mirroring the particle rule of 18.2.
  10. The visual lifecycle has no SCHEDULED state. The audio machine has one because a voice is committed to the audio clock before it sounds; a visual system has no second clock to commit to. Five states plus FAILED, with the same terminal and no-op-on-second-stop rules as 16.3.
  11. Visual release defaults to 0ms, where audio defaults to 50ms. An audio release prevents a click, which is a defect; a visual pop is merely abrupt and is sometimes what the author wants. A 0ms release still passes through RELEASING for one tick, so a single teardown path handles every removal — the property 16.4 exists to protect.
  12. A spawn at the ceiling is refused, never evicted — the opposite of the voice policy of 16.6, and for a stated reason: an evicted voice fades out in milliseconds and is forgiven, while a visual system evicted mid-scene disappears in front of the viewer. A refused spawn is not a scenario failure.
  13. The camera matrix is normative, including where parallax enters it: parallax multiplies the camera translation only, never its zoom or rotation, so a distant layer drifts more slowly without also being scaled or tilted differently. The projection center is the center of the display rectangle after fit resolution, and every camera operation and the perspective factor of 17.6 act about that one point.
  14. projection is authored once and is neither automatable nor bindable. There is no value between orthographic and perspective: an interpolating mechanism cannot express the change and a stepping one would snap the whole scene. PRD 87's automatable camera properties are the five numeric ones.
  15. Post-effect order is normative and the runtime does not reorder the chain. bloom before color-adjust blooms authored colors; after it, adjusted ones. These operations do not commute, so array order is contract rather than convenience.
  16. grain is exempt from reproducibility and consumes no procedural stream. Its per-pixel noise comes from a frame counter and pixel coordinate, not a seeded stream. Section 9.3 promises identical decisions; grain drives no decision, and pulling per-pixel samples from the exhibit's stream would make stream position depend on resolution and frame rate — breaking every other reproducibility guarantee to protect one nobody needs. This is the only such exemption in the visual contract, and it is stated rather than discovered.
  17. Approximation has a boundary. blur and bloom may be computed at reduced resolution and upsampled, raising WARN_VISUAL_APPROXIMATION once per effect instance and never once per frame. An effect a renderer cannot perform is skipped, never silently substituted with a different one.
  18. Authoring bounds fail; runtime ceilings shed. This is the dividing line PRD 89 implies and 19.5 states: a value the author wrote that is out of range rejects the exhibit, while an aggregate the document never named sheds work by a documented rule and keeps running, because PRD 119 requires the runtime to remain usable when performance degrades. Neither is ever silently transformed into something materially different.
  19. Every shedding rule is deterministic and normative, so a degraded frame is the same degraded frame everywhere: oldest-particle-first from the largest system, the ascending pair order of 18.8 for links, farthest-z first for compositing buffers, and the trailing effects of the chain for passes.
  20. A document can never raise a ceiling. There is no field, parameter, or action by which an exhibit requests a larger budget; a runtime may lower any ceiling on a weaker device, exactly as 16.6 permits for voices.

Amendments to sections 1, 7, 8, 11, 17, and 18

All consequences of this slice rather than corrections. No decision recorded in section 17 or 18 was reversed.

  • 1.3 adds visuals.layers.*, visuals.camera.*, and visuals.effects.* to the canonical namespace prefixes, and records that an ordered container is indexed rather than keyed — the visuals.effects[<index>] and points[<index>] forms, which are the only two in 0.1.
  • 7 gains ERR_INVALID_EFFECT_TYPE and WARN_VISUAL_CEILING; ERR_AUTOMATION_CONFLICT's cause text widens from "in a recipe instance" to cover a visual scope and the behavior collision; ERR_VISUAL_LIMIT_EXCEEDED's cause text names automation records and post-effect entries.
  • 8.1 gains the four visual rows, and its closing paragraph now states that those four are the whole visual surface rather than that visual properties are unsupported.
  • 11's Visuals row reads Complete (Rev 0.7 / Phase 4a-4c) and names what section 19 fixes and what remains provisional.
  • 17.7's system field table gains lifecycle and automation rows, and its lifecycle-field deferral is replaced by the rule that those fields are legal only when lifecycle is spawned.
  • 17.14's "Until section 19.1 lands, no visual property is in that table" paragraph is replaced by the four rows that now exist and the statement that no per-object property is among them.
  • 18's opening paragraph no longer describes 19.1, 19.2, and 19.5 as unresolved forward references.

New diagnostic codes

Two, added to the section 7 table, which remains the single authoritative list: ERR_INVALID_EFFECT_TYPE and WARN_VISUAL_CEILING. Section 19.6 repeats them for locality, exactly as 15.18, 16.10, 17.15, and 18.9 do.

Eleven existing codes are reused rather than duplicated under visual-specific names: ERR_AUTOMATION_CONFLICT, WARN_AUTOMATION_FALLBACK, ERR_UNSUPPORTED_TARGET, ERR_INVALID_REFERENCE, ERR_VISUAL_LIMIT_EXCEEDED, ERR_NODE_LIMIT_EXCEEDED, ERR_OUT_OF_BOUNDS, ERR_UNKNOWN_FIELD, ERR_SCHEMA_VALIDATION, WARN_VISUAL_APPROXIMATION, and WARN_CLEANUP_FORCED.

Limits fixed in this slice

Authoring bounds — checked at import, rejecting the exhibit:

Limit Bound Diagnostic
Automation tracks per exhibit, both scopes 128 ERR_VISUAL_LIMIT_EXCEEDED
Automation points per exhibit, both scopes 2048 ERR_VISUAL_LIMIT_EXCEEDED
Post-effect entries 4 ERR_VISUAL_LIMIT_EXCEEDED
Blur and bloom radius 32 scene units ERR_OUT_OF_BOUNDS
Camera zoom 0.01 to 100 ERR_OUT_OF_BOUNDS
Camera focalLength above 0 ERR_OUT_OF_BOUNDS
Spawned release 0ms to 10s ERR_OUT_OF_BOUNDS

Aggregate runtime ceilings — checked every tick or frame, shedding work and continuing:

Ceiling Value Shed
Live particles, all systems 8192 Oldest in the largest system
Live emitted items, all emitters 2048 Oldest in the largest emitter
Live spawned system instances 64 Refuse the spawn
Link segments per tick 4096 Tail of the ascending pair order
Trail history samples 32768 Oldest samples of the longest histories
Field evaluations per tick 32768 Nearest items first; remainder is zero force
Offscreen compositing buffers per frame 16 Drop the excess objects' buffered appearance, farthest-z first
Post-effect passes per frame 8 Skip trailing effects
Device-pixel-ratio multiplier min(dpr, 2) Clamp
Backing store 4096 x 4096 device pixels Lower the multiplier to a floor of 1

Section 19.5 also restates, in one table, every ceiling first fixed in sections 10, 14, 15, 16, 17, and 18, which is what PRD 120 means by centralizing limits rather than scattering them. Scenario instances and timeline expansions are the two entries of PRD 120's list no contract has yet fixed; the gap is named in the table so it stays a center rather than a snapshot.

These values are provisional. Their relationship to slice 4h is the relationship 16.7's master-protection values have to GC6: the shape is normative now, the numbers are confirmed or replaced by measurement. No evidence record may describe them as measured until trace 21 of 19.7 runs.

Verification performed

This slice changes documentation only. No runtime source, schema, fixture, or test file was touched.

  • Every diagnostic code used in section 19 (13 distinct codes) resolves to a row in the section 7 table; zero unresolved, checked programmatically.
  • Every numeric cross-reference in the whole specification (107 distinct, 48 of them made by section 19) resolves to an existing heading. Zero unresolved forward references remain anywhere in the document — the first time that has been true since section 17 was written.
  • All 116 code fences in the document are balanced, 6 of them in section 19, and every table row in the document is well formed once escaped pipes are discounted.
  • npm test passes 102 tests with zero failures, confirming the existing suite is unaffected.

Test boundary

Traces 1-19 of section 19.7 are automated and belong to slice 4f, where automation, lifecycle, camera, and effects exist to run them. Traces 20 and 21 are user-observed and close slice 4h: the PRD 130 visual challenge judged on a real display — the same gate as trace 15 of 17.16 and trace 20 of 18.10, restated for locality rather than added as a third gate — and the early combined GC6 benchmark at 1920 x 1080, which is what decides whether the ceilings above are the right ones.

State at this checkpoint

The visual contract is complete and no visual code exists. src/runtime has no renderer module, the JSON Schema has no visual definitions, exhibits/ has no visual fixture, and nothing has been drawn from any build. Slice 4d is the first slice of Phase 4 that writes runtime code.

Phase 3 remains unaccepted while traces 15-17 of section 16.11 and the Phase 1 direct-file restart observation are open. No Phase 4 slice depends on them and no Phase 4 slice can close them.

Whether sections 17-19 pass a multi-model review before slice 4d begins remains open, and it is now a decision with a deadline rather than a standing question: 4d is the next slice, the review's whole value is in catching contradictions before implementation defends them, and the surface has grown from the sections 14-16 pass that found thirteen defects to three sections of comparable size.