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
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user