Files
XZBT/reviews/.completed-artifacts/visual-contract-review-abacus-ai-agent-20260905-214821-deepseekv4.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

47 KiB
Raw Blame History

Visual contract review — Abacus AI Agent

Independent adversarial read of XZBT Format Specification rev 0.7, sections 17–19, before Phase 4 slice 4d (renderer core). Existing files under reviews/ were not opened.

Standing decisions were treated as locked. This pass checks whether the spec text actually states them and whether it contradicts itself elsewhere.

Severity:

  • Blocker — 4d cannot implement a unique, testable behavior without guessing.
  • Major — two conforming 4d implementations can diverge, or a later slice will have to unwind 4d choices.
  • Minor — underspecified edge, wrong diagnostic, or local inconsistency that 4d can paper over with a default.

Defects

1. visuals.automation is both required and forbidden

  • Severity: Blocker
  • Location: §17.3 The visuals block vs §19.1 Two declaration scopes
  • What's wrong: §17.3 enumerates scene, layers, systems, fields, camera, effects and then: “any other property of visuals is ERR_UNKNOWN_FIELD.” §19.1 places an automation array on visuals itself (visuals.automation). Under the strict unknown-field policy that array is illegal at import.
  • Why it matters for 4d: The renderer core will parse visuals. If it follows §17.3 it rejects every exhibit that uses the §19.1 example. If it follows §19.1 it violates the §17.3 table. Slice 4d must know the legal key set before it writes a schema or a loader.

2. Trace 14 of §17.16 forbids the four §8.1 visual rows §19.1 adds

  • Severity: Blocker
  • Location: §17.16 trace 14 vs §17.14 last paragraph vs §19.1 The visual rows of the section 8.1 target-capability table
  • What's wrong: Trace 14: “A binding, set, or override addressing any visual property is ERR_UNSUPPORTED_TARGET, and the section 8.1 table is unchanged by this slice.” §19.1 then adds four families (visuals.camera.<field>, visuals.layers.<id>.opacity, visuals.systems.<id>.visible, visuals.effects[<index>].<param>) that are binding/override targets. §17.14 already describes those four rows as if they exist. The acceptance gate for 4d therefore requires the opposite of the closed contract.
  • Why it matters for 4d: 4d is the first code that will implement (or stub) target resolution. Implementing trace 14 literally means rejecting camera/layer/system/effect bindings that 4f traces 7 require to succeed. The 4d gate must be restated as “any visual property outside the four §19.1 rows.”

3. Automatable registry names a repeater step block that does not exist

  • Severity: Blocker
  • Location: §19.1 The automatable registry (“for repeater, the numeric fields of its step block”) vs §18.5 field table
  • What's wrong: §18.5 has repeat, count, distribution, position, inputs, behaviors, fields, links. There is no step field, no step block, and no definition of what “numeric fields of its step block” are. Repeaters also have no rate (explicitly ERR_UNKNOWN_FIELD).
  • Why it matters for 4d: Less for the first draw path than for the property-path resolver 4d will share with automation. A registry row with no corresponding schema is an unimplementable target. Either define step or delete the row.

4. Y-down scene vs “positive angles toward +y”

  • Severity: Major
  • Location: §17.2 Canonical units (“0 points along +x; positive angles turn toward +y”) vs §17.4 (x and y run 0 to 1 from the top-left; virtual origin at top-left)
  • What's wrong: Top-left origin with y increasing downward is a left-handed screen space. “Positive toward +y” is then clockwise on the display (standard canvas), not the mathematical CCW convention a reader of “toward +y” in a Cartesian plane will assume. The spec never says whether rotation is screen-clockwise or math-CCW, and never says whether +y in local object space is down.
  • Why it matters for 4d: Transform order R × K × S is useless if the sign of R is ambiguous. Trace 3 of §17.16 (“a known local point maps to the documented device point”) cannot be written until this is fixed. Canvas 2D rotate is clockwise for y-down; a naive cos/sin toward +y with y-down will mirror every exhibit.

5. Local origin of box primitives is unspecified

  • Severity: Major
  • Location: §17.9 rectangle, rounded-rectangle, ellipse, ring, arc; §17.11 origin “in the object's own local space”; §17.13 text align “relative to the object's origin”
  • What's wrong: For rectangle / rounded-rectangle, is position the top-left of size, the center, or something else? For ellipse / ring / arc, is position the center (implied by radius but never stated)? For point, is the dot centered on the origin? Text has align/baseline, which implies the origin is an anchor, but boxes have no equivalent. Default origin is {0,0}, so if a rectangle’s local space has its corner at 0, rotation is about the corner; if centered, about the center. These produce different pixels.
  • Why it matters for 4d: First thing the primitive rasterizer needs. Two implementations will disagree on every rotated panel and every contain/cover fixture in trace 1/3.

6. Perspective factor vs camera matrix: two post-multiplies, no combined formula

  • Severity: Major
  • Location: §17.6 Perspective scaling (“object's transform is post-multiplied by focalLength / (focalLength + z) about the camera's projection center”) vs §19.3 (“applied after V”)
  • What's wrong: §17.11 already post-multiplies M_parent × M_local. “Post-multiplied” in §17.6 does not say whether the factor is applied in local space, after M_local, after hierarchy, or after V. §19.3 says after V, about the display projection center. Applying a uniform scale about the display center to an already-view-transformed point is not the same as scaling the object about the camera look-at in scene space. z used in the factor is never named as effective z (z_parent + z_local from §17.11), though that is the only reading that matches hierarchy.
  • Why it matters for 4d: Trace 6 of §17.16 and trace 14 of §19.7 are the 4d/4f camera tests. Without a single matrix (or a worked numeric example: one point, one z, one V, one output), 4d will pick an order that 4f then has to break.

7. translate.z is excluded from M_local but perspective needs a 3D point

  • Severity: Major
  • Location: §17.11 “The z component of translate adds to the object's z and … is not part of M_local.”
  • What's wrong: M_local is therefore a 2D affine matrix. Perspective uses z. Hierarchy adds z. Particle integration moves p in 3D. The spec never says whether parent M rotates/scales child z (it cannot, if M is 2D) or only adds it. A child with local z under a rotated group: is depth the scalar sum, or a transformed coordinate? §17.11 says sum, which means rotation never tilts depth — consistent with 2.5D, but then “perspective about projection center” on a 2D matrix is a uniform XY scale, not a projective transform. That should be stated as “uniform XY scale, no vanishing-point projection.”
  • Why it matters for 4d: Implementers will reach for a 4×4 perspective matrix. The contract wants a 2D scale. If 4d ships a real projection, every later fixture fails.

8. Depth sort unit is inconsistent (object vs system vs particle cloud)

  • Severity: Major
  • Location: §17.6 “Within one layer, objects are drawn farthest first” vs §18.2 “Particles of one system draw as one unit … The system's own position in its layer's depth sort is its lowest-z particle” vs §17.8 document key order of content
  • What's wrong: Graphic systems contain many objects with independent z. Are those objects sorted across systems in the layer, or is each system an atomic band like particles? §17.6 says objects; §18.2 special-cases particles so they never interleave with unrelated objects. Emitters create full objects — §18.4 never says whether those objects join the layer object-sort or stay an atomic emitter band. Repeaters: same gap. A graphic system with z=0 group and a child at z=100 vs a sibling system at z=50 has two legal draw orders.
  • Why it matters for 4d: Painter’s algorithm is the entire 2.5D model. 4d must pick a sort key. Wrong choice is a visual bug in every multi-system scene and is expensive to change.

9. Layer visible is a ValueSpec but is not automatable, bindable, or in §8.1 — and still “advances”

  • Severity: Major
  • Location: §17.5 visible ValueSpec<boolean>, default true; §19.1 “visuals.layers.<id>.visible is deliberately not in the table”; §17.14 “every such field is resolved once, at its owning object's instantiation boundary”
  • What's wrong: If layer visible resolves once, a ValueSpec is pointless except random/choose at activation. If it is meant to change, there is no writer: not automation (boolean), not binding (absent from §8.1), not set/override. The field is therefore a once-at-import flag dressed as a ValueSpec. Meanwhile system visible is an §8.1 binding target and is explicitly not automatable. The two visible flags look alike in §17 and behave unlike in §19.
  • Why it matters for 4d: 4d will implement layer skip. It needs to know: evaluate once at activation, or subscribe to the pipeline? Implementing a live binding for layer visible would violate the locked “deliberately absent” decision; treating it as constant makes the ValueSpec type a lie.

10. §17.16 trace 9 cites “9 layer nesting levels” — layers do not nest

  • Severity: Major
  • Location: §17.16 trace 9 vs §17.5 (layers composite in document key order, no parent field) vs §17.9 (group nesting 8)
  • What's wrong: Trace 9: “9 layer nesting levels … are each ERR_VISUAL_LIMIT_EXCEEDED.” There is no layer nesting in §17.5. Group nesting is 8. Layer count is 16. The acceptance test for 4d is unrunnable as written.
  • Why it matters for 4d: 4d’s exit gate includes a test that cannot be authored. Likely intent: group nesting 9 / 8, and separately 17 layers.

11. Fit mapping is underspecified for cover crop and letterbox placement

  • Severity: Major
  • Location: §17.4 fit; §17.16 trace 1
  • What's wrong: contain letterboxes “the remainder with background” but does not say the scene is centered in the display (vs aligned top-left). cover “cropping the overflow” does not say which region is kept (centered crop vs origin-aligned). viewport ignores fit except that non-stretch is an error — so the only legal fit under viewport is stretch, which is also ignored. Device-pixel-ratio (§19.5) is applied after fit; the spec never gives the composed scene→CSS→device transform.
  • Why it matters for 4d: Trace 1 is the first 4d test and requires “expected display point” including letterbox offsets. Without centering/crop rules those points are not determined.

12. Camera default “scene center” under viewport is not a scene quantity

  • Severity: Major
  • Location: §19.3 “the display center for viewport” as default x,y; camera is in “scene units”
  • What's wrong: Viewport scene units are CSS pixels of the display, origin top-left, and the scene has no intrinsic size. Defaulting the camera to “display center” makes the default camera depend on the window size. Two machines, same exhibit, different default view. That contradicts “an exhibit that never mentions the camera” looking identical (§19.3’s own rationale) and contradicts reproducibility across display sizes.
  • Why it matters for 4d: Identity camera vs “centered on display” are different matrices as soon as the canvas is not the design size. 4d will bake this into every frame.

13. Conic-gradient fallback axis is not a unique segment

  • Severity: Major
  • Location: §17.12 Conic gradient fallback
  • What's wrong: Fallback is “a linear-gradient with the same stops along the axis from center at angle to the paint's bounding-box edge.” A ray from center at angle intersects a bounding box at one point only if center is inside the box; if center is outside there may be two intersections or none. “Bounding-box edge” does not name which edge. Stop mapping from a 360° conic onto a 1D axis is lossy; the spec does not say whether offsets are used as-is (wrong) or the 0°–180° half is taken.
  • Why it matters for 4d: This is the only appearance fallback 4d must implement, and trace 10 requires a documented linear gradient. Without a unique segment, the warning can fire and the pixels still differ.

14. Fog blends “resolved fill, stroke, glow, and shadow colors” — gradients and alpha unspecified

  • Severity: Major
  • Location: §17.6 Depth fog
  • What's wrong: Fog fraction is exact. What is “blended toward color”? For a gradient, every stop? The rasterized samples? Glow/shadow have their own colors; is the fog color’s alpha used? Premultiplied? density * clamp(...) as a lerp in sRGB or linear? Particle color ramps already interpolate sRGB; fog does not say. Objects with fill: null still have glow/shadow.
  • Why it matters for 4d: Trace 7 asks for blended colors at near/mid/far. Without a color space and a rule for paints, the trace has no oracle.

15. Stroke-only primitives vs point fill; style.fill on line vs table

  • Severity: Minor
  • Location: §17.9 line “style.fill on a line is ERR_UNKNOWN_FIELD” vs §17.12 Stroke-only primitives (line, polyline, arc, bezier — not point); point is “a filled dot”
  • What's wrong: point uses fill, not stroke, but is not listed as fill-only (style.stroke on a point?). polyline is stroke-only in §17.9 notes but the stroke-only ERR list in §17.12 omits nothing it listed — OK — yet path/spline can be open and still accept fill. Open path with fill: legal? Canvas fills open subpaths by closing them; the spec is silent.
  • Why it matters for 4d: Open-path fill is a classic renderer fork.

16. Arc sweep, direction, and “magnitude exceeds 360”

  • Severity: Major
  • Location: §17.9 arc / ring; §17.2 angles
  • What's wrong: startAngle, endAngle, direction clockwise | counter-clockwise. Is the sweep the directed difference following direction, or the linear difference end - start with direction flipping the canvas arc flag? A sweep of 0: nothing, or a full ring (ring says “angles default to a full ring” — default values never given numerically)? ERR_OUT_OF_BOUNDS when magnitude exceeds 360: is that |end-start| or the directed sweep after wrapping? end = start + 360 with clockwise: full circle or error?
  • Why it matters for 4d: Arc/ring are in Primitive Set 0.1; 4d must emit a unique path.

17. Path arc command vs SVG sweep/largeArc in y-down space

  • Severity: Minor
  • Location: §17.13 path op: arc
  • What's wrong: Endpoint-parameterized elliptical arc with largeArc and sweep booleans. SVG’s sweep is “positive angle” in y-down (clockwise). The visual contract’s positive angle is “toward +y.” If those disagree (defect 4), path arcs and arc primitives will rotate opposite ways.
  • Why it matters for 4d: Path vs primitive inconsistency will show up in Exhibit geometry immediately.

18. Spline Catmull-Rom: phantom points, closed form, and tension mapping

  • Severity: Major
  • Location: §17.13 Spline
  • What's wrong: Open spline: “first and last points are duplicated as the phantom endpoints.” That is one CR parameterization (uniform, centripetal?). tension 0 to 1 default 0.5 is not mapped to a formula (Kochanek–Bartels? tau = 1 - tension?). Closed spline: no phantoms specified; standard is wrap indices. bezier mode count = 3n+1 is for open cubics; a closed bezier spline with that count cannot close smoothly without extra points — interaction of closed + bezier is undefined.
  • Why it matters for 4d: Organic geometry and morph (equal point count) depend on a unique curve. 4d will pick a CR variant; 4e morph interpolation will bake it in.

19. Text: maxWidth “condensed horizontally” without a scale rule; missing font on the 17.9 geometry row

  • Severity: Minor
  • Location: §17.13 text; §17.9 text geometry fields omit weight, italic, letterSpacing that §17.13 adds
  • What's wrong: Condensing to maxWidth — uniform scale of glyphs? Tracking only? What if the run is already shorter? Overflow of 256 characters after resolution: diagnostic? Generic CSS families: metrics differ per platform, so text layout is not reproducible even though 17.14 claims procedural identity. That may be acceptable (like grain) but is not exempted.
  • Why it matters for 4d: 4d will measure text. Platform font metrics will fail any pixel fixture. The spec should either exempt text metrics from reproducibility or require a measurement rule (e.g. no pixel tests on text bounds).

20. Graphic system has no system-level transform/position; camera and particles do

  • Severity: Minor
  • Location: §17.7 / §17.8 vs §18.2 position on particles vs §19.3 camera
  • What's wrong: A graphic system’s objects sit in scene space with no system origin. Particles add a system position offset to the distribution. Authors cannot move a whole graphic system without grouping. Not a contradiction, but 4d must not invent a system transform for graphic.
  • Why it matters for 4d: Tempting to put a system matrix in the renderer. Spec does not allow it for graphic.

21. lifetime on a visual object vs system lifecycle vs particle lifetime

  • Severity: Major
  • Location: §17.10 object lifetime; §17.7 / §19.2 system lifecycle; §18.2 particle lifetime
  • What's wrong: Object-level lifetime “removed from its container” has no state machine, no release, no ownership, no diagnostic, and is legal on persistent graphic content. Is that a 4d concern (remove from draw list on logical clock) or 4f? Removing a group’s child mid-run changes document key order used for equal-z ties. Spawned systems forbid lifecycle fields on persistent systems, but object lifetime remains legal on persistent content — a back door that looks like lifecycle.
  • Why it matters for 4d: The draw list is 4d’s. If objects can disappear without 19.2, 4d needs a removal path now.

22. Filters “after the object is drawn and before it composites into its layer”

  • Severity: Minor
  • Location: §17.12 Filters vs Cost (offscreen buffer)
  • What's wrong: CSS filter on a raster vs filter on vector before raster is different. Hue-rotate amount in degrees — range unset (unlike 0–4 / 0–1). Blend vs filter vs opacity vs layer opacity order: object opacity multiplies inherited and layer; blend requires offscreen; filters after draw. Glow/shadow vs filter order unset. blur style field vs filters vs post-effect blur — three blurs.
  • Why it matters for 4d: Offscreen pass graph is 4d. Ambiguous order → different pixels and different pass counts vs §19.5.

23. Clip inheritance and mask vs draw order

  • Severity: Minor
  • Location: §17.12 clipping / masking
  • What's wrong: Mask child “is not drawn; its rendered alpha multiplies the alpha of the group's remaining children.” Remaining children are still depth-sorted among themselves? Mask is rasterized untransformed? With the group’s transform? Does the mask include the mask child’s own style opacity? Clip intersected with ancestor — in which space after nested transforms?
  • Why it matters for 4d: Mask/clip are in traces 12. Need a unique raster definition.

24. Particle size ramp “multiplier on render's own size” for point / ellipse / component

  • Severity: Minor
  • Location: §18.2 size
  • What's wrong: point has pointSize, not size. ellipse has radius. component has no size. Multiplying “size” is undefined for those. rotation at creation vs angularVelocity vs align on emitters.
  • Why it matters for 4d: Particle drawing is likely in 4d even if emission is 4e; the instance transform must be defined.

25. Particle / emitter motion in 3D vs 2D draw

  • Severity: Minor
  • Location: §18.2 integrator (p, v, a have z) vs §17.11 2D M_local
  • What's wrong: p.z updates; draw uses z for sort/fog/perspective scale only. Fine, but align “rotation … to its velocity direction” — 2D atan2(vy,vx) or 3D? Unspecified.
  • Why it matters for 4d: Emitter align is a 4d rotation if 4d draws emitted objects.

26. Index-driven even with count === 1 and i / (count - 1)

  • Severity: Minor
  • Location: §18.3 line mode even; §18.5 repeat.fraction already special-cases count === 1
  • What's wrong: i / (count - 1) is division by zero when a burst or repeater has count: 1. Fraction documents the fix; distribution even does not.
  • Why it matters for 4d: Only if 4d implements distributions; still a landmine for 4e that the contract should close now.

27. Ring distribution: “not below radius is ERR_INVALID_RANGE_ORDER”

  • Severity: Minor
  • Location: §18.3 ring vs §17.9 ring primitive (innerRadius must be less than radius)
  • What's wrong: “not below radius” reads as innerRadius >= radius is the error, i.e. inner must be below radius — OK — but the wording is easy to invert. Primitive uses strict less-than; distribution does not say whether innerRadius === radius is a legal zero-width ring (perimeter) or an error. Primitive would error on equality.
  • Why it matters for 4d: Shared geometry helpers.

28. grid item count vs repeater.count / columns * rows

  • Severity: Major
  • Location: §18.3 grid; §18.5 count
  • What's wrong: Grid is columns × rows cells. Repeater has an independent count up to 1024. If count != columns * rows, extra items wrap? Truncate? Error? Unspecified. jitter consumes samples even though “grid without jitter consumes no samples” — with jitter, order of x/y jitter vs depth sub-block is only “field order of its row,” and jitter is not ordered relative to depth.
  • Why it matters for 4d/4e: Placement fixtures (trace 10 of §18.10).

29. Behaviors: oscillate formula phase / 360 with frequency in Hz

  • Severity: Minor
  • Location: §18.6 oscillate center + amplitude * w(frequency * t + phase / 360)
  • What's wrong: If w expects cycles, frequency * t is cycles and phase/360 is cycles — OK. If w is sin(2π · …) that must be stated. square/sawtooth polarity (does square start high?) unset. orbit does not say whether it sets position or adds to it; composition says accumulate on position, which implies orbit is an offset — then center is absolute or relative?
  • Why it matters for 4d: 4d may stub behaviors; if it draws static poses only, less urgent. Still, orbit + position composition must be known before any motion lands.

30. follow-path path as sibling key vs component encapsulation

  • Severity: Minor
  • Location: §18.6 follow-path; §18.1 encapsulation
  • What's wrong: Sibling key inside a component is OK; from outside, ERR_INVALID_REFERENCE. Cross-system path follow: not mentioned. path as inline commands on a behavior duplicates 17.13 without vertex limits.
  • Why it matters for 4d: Low until 4e.

31. Morph “equal type, point count, and spline mode” vs rectangles / text / groups

  • Severity: Minor
  • Location: §18.6 Morph compatibility vs locked decision
  • What's wrong: Locked decision matches the spline rule. Morph of two rectangles (no points) — same type, point count 0=0, no spline mode: legal lerp of size? Morph of groups? Morph of text? “Point count” for primitives without points is undefined. path command morph: equal command count or equal endpoint count?
  • Why it matters for 4d: Morph is 4e, but 4d may store geometry in a form that cannot lerp.

32. Noise: 12 edge-midpoint gradients of a cube, 256 permutation, Fisher–Yates

  • Severity: Major (for 4e, but the algorithm is in-scope for this read)
  • Location: §18.7 Coherent noise is normative
  • What's wrong: Classic Perlin uses 12 edge vectors of a cube (the 12 midpoints of cube edges, yes). Permutation of 256 shuffled with one sample per entry — Fisher–Yates on 256 entries needs 256 samples if specified that way, but standard FY uses i from 255..1 with j = floor(u * (i+1)). The spec does not give the exact FY index formula or how a stream sample in [0,1) becomes j. Duplicate permutation (Perlin often repeats 0..255 twice to 512) unset. curl of a scalar 3D noise is zero if you take ∇ × (N, N, N) or needs three offset noise fields; “curl of the noise potential” with one scalar is not a unique vector. This is the hardest reproducibility trap in §18.
  • Why it matters for 4d: If 4d does not implement fields, still: do not invent a noise helper in 4d that 4e must match. Spec should give a tabulated sample (trace 15 of §18.10 promises “documented sample values” that are not in the spec).
  • Gap: Trace 15 requires documented sample values; the spec contains none. That is a missing fixture, not just an algorithm gap.

33. visuals.fields vs §17.3 — fields are listed; OK. Field IDs vs system fields array order

  • Severity: Minor
  • Location: §18.7
  • What's wrong: Sum in system fields array order; addition commutative. Fine. enabled ValueSpec on a field — once at instantiation (17.14), so cannot be automated. Strength of a field is not in the automatable registry (locked). OK.

34. Trail interval default “one logical tick” — tick length is a runtime, not an exhibit constant

  • Severity: Major
  • Location: §18.8 interval default; §9.1 logical tick
  • What's wrong: History sampled every tick by default means trail shape depends on tick rate, contradicting “a trail has the same shape at any render rate” (frame rate, not tick rate). Trace 17 of §18.10 only varies frame rate. If tick length differs across runtimes, trails diverge. Default should be a DurationSpec literal (e.g. the 9.1 tick) named explicitly.
  • Why it matters for 4d: If 4d allocates trail buffers, stride is unknown.
  • Severity: Minor
  • Location: §18.8 vs §18.2 lowest-z particle as system sort key
  • What's wrong: Links at system depth vs particles sorted among themselves: links can draw in front of far particles of the same system. Maybe intended. index rule with stride on a changing particle pool — emitters cannot have links; particles can, and creation ordinals change under eviction. After oldest-first eviction, indices are not stable. Unspecified.
  • Why it matters for 4d: Draw order inside a system.

36. Automatable registry vs §8.1: emitter rate automatable but not bindable — registry allows it; graphic per-object properties automatable from system scope

  • Severity: Major
  • Location: §19.1 registry “for a graphic system, any numeric transform, style, or geometry property of an object in its content tree”
  • What's wrong: Locked decision: automatable registry is broader than §8.1 — confirmed. But §17.14 said visual object fields resolve once and are constant for the object’s lifetime, with time variation from behaviors, automation, and external control. Automation writing content.band.style.opacity is therefore an exception to “constant for lifetime.” 4d must keep those fields mutable even though 17.14 sounds like they are baked. Also: style.fill as color is non-numeric and excluded; strokeWidth is included. visible on an object is boolean — not automatable; system visible is bindable only. Object visible has no writer except a once-resolved ValueSpec — same trap as layer visible.
  • Why it matters for 4d: 4d must not intern style as immutable GPU constants. Per-object opacity must remain a frame-time input for 4f automation.

37. Cross-scope target strings: relative paths vs camera.zoom vs visuals.camera.zoom

  • Severity: Major
  • Location: §19.1 example "target": "camera.zoom"; §8.1 rows visuals.camera.<field>; §1.3 prefixes
  • What's wrong: Example targets are scope-relative (camera.zoom). §8.1 uses absolute visuals.camera.zoom. Bindings presumably need the absolute form. Automation in system scope “addressed relative to the system” — is rate or content.hull.transform.rotation the form? No ABNF. effects[<index>] vs effects.0 — §1.3 says indexed form only.
  • Why it matters for 4d: Path parser is shared. Wrong grammar → every 4f track fails.

38. loop on visual tracks vs “audio track shape verbatim”

  • Severity: Minor
  • Location: §19.1 vs locked decision
  • What's wrong: Locked: audio shape plus loop. Text agrees. Ping-pong count = complete round trip: agrees. infinite loop on spawned system with finite lifetime is legal — OK. infinite “legal only on a persistent scope” in one sentence, then immediately allowed on spawned with finite lifetime — contradiction in two consecutive sentences.
  • Why it matters for 4d: Validation of loop.count.

39. ERR_AUTOMATION_CONFLICT for behavior + track on the same object channel

  • Severity: Minor
  • Location: §19.1 vs §18.6 composition
  • What's wrong: Locked decision matches. Unclear whether oscillate on transform.scale.x conflicts with a track on transform.scale (vector) or only .x. Unclear whether a system-level track on emitter position.x conflicts with a drift on each emitted item (different objects).
  • Why it matters for 4d: Conflict detection may live next to the property store 4d creates.

40. Lifecycle: CREATED -> ACTIVE | FINISHED | FAILED — when FINISHED from CREATED?

  • Severity: Minor
  • Location: §19.2 state table
  • What's wrong: No SCHEDULED (locked, OK). CREATED -> FINISHED is allowed but no cause (zero-lifetime spawn before first draw?). release default 0ms still one tick in RELEASING (locked, OK). Spawn at ceiling refused not evicted (locked, OK). WARN_VISUAL_CEILING once naming template — vs §19.5 “once per ceiling per second.” A refused spawn could warn every spawn or once per second; 19.2 says once (per event?), 19.5 says once per ceiling per second.
  • Why it matters for 4d: 4d may not spawn yet, but the warning rate is a shared diagnostic policy.

41. cancelWithScenario: false without persistent ownership is ERR_UNSUPPORTED_TARGET

  • Severity: Minor
  • Location: §19.2
  • What's wrong: That is a schema/semantic conflict, not an unsupported target. ERR_SCHEMA_VALIDATION or ERR_UNKNOWN_FIELD would fit; ERR_UNSUPPORTED_TARGET is the 8.1 capability miss. Wrong code will be wired into tests.
  • Why it matters for 4d: Low; 4f traces.

42. Camera matrix R(-rotation) vs object R(rotation)

  • Severity: Major
  • Location: §19.3 V = T(c) × S(zoom) × R(-rotation) × T(-c) × T(-p·Δ)
  • What's wrong: Negating rotation is correct for a view matrix if object rotation uses the same convention. Combined with defect 4, the extra minus may double-correct. T(-p * (x - c_x), …) — camera x,y are scene-center defaults; c is display projection center after fit. Subtracting a scene coordinate from a display coordinate is a unit/space mix unless fit has already mapped them into one space. The formula never says whether x,y are converted to display pixels first.
  • Why it matters for 4d: This is the normative camera matrix 4d must implement. As written it mixes scene units and display units. Trace 13 of §19.7 cannot have a unique documented display point.

43. Parallax and perspective both claimed to affect “the layer”

  • Severity: Minor
  • Location: §17.6 parallax on camera translation; §19.3 perspective per object after V
  • What's wrong: Locked: parallax multiplies translation only — text agrees. Perspective is per-object z, so two objects on one layer at different z scale differently while sharing one V. A layer parallax does not change object z. OK if intended; worth stating that parallax is not a function of object z (only layer field).
  • Why it matters for 4d: Do not implement depth-based parallax.

44. Post-effects: scanlines.spacing in device pixels vs everything else in scene units

  • Severity: Major
  • Location: §19.4 scanlines.spacing, grain.scale in device pixels; blur.radius scene units scaled by zoom
  • What's wrong: Reproducibility: scanline period changes with DPR and window size. Grain is exempt; scanlines are not. Two machines at different DPR get different line density. speed of scanlines has range “—” (unlimited?). color-adjust.saturation vs filter type saturate naming. Disabled effect “costs nothing that frame” vs pass budget counted how when enabled is a ValueSpec resolved once — cannot toggle unless automated; enabled is boolean so not automatable. So enabled is another once-only ValueSpec that cannot be bound (not in §8.1 except .<param> numeric). Cannot turn effects off at runtime despite the field.
  • Why it matters for 4d: Effect chain is after the renderer; 4d may still allocate the composited frame. Scanlines/grain in device pixels must be a conscious 4d/4f choice or exhibits are resolution-dependent without a warning.

45. WARN_VISUAL_APPROXIMATION used for three different events

  • Severity: Minor
  • Location: §17.1 / §17.12 conic fallback; §19.4 reduced-res blur/bloom and skipped unavailable effect; §19.5 shedding offscreen buffers also raises WARN_VISUAL_APPROXIMATION (not WARN_VISUAL_CEILING)
  • What's wrong: Buffer shedding is a ceiling event but uses the approximation warning. Authors cannot distinguish “GPU cannot conic” from “too many glow buffers.” Locked decision said reduced-res blur/bloom raises approximation once per effect instance — text agrees. Buffer shed using the same code is extra.
  • Why it matters for 4d: 4d implements glow/blur buffers and must pick a diagnostic.

46. §19.5 offscreen-buffer shed “farthest-z first” vs draw order farthest first

  • Severity: Minor
  • Location: §19.5 compositing buffers
  • What's wrong: Shed farthest-z first means distant fancy objects lose glow first (keep near effects). Opposite of painter priority. Fine if intended. “Draw the excess objects without their non-default blend/mask/blur/glow/filters” — dropping mask changes silhouette, which is a material substitution PRD 89 forbids, while the table claims neither kind of limit is silently transformed into something materially different.
  • Why it matters for 4d: Pass budget enforcement.

47. Authoring bound vs runtime ceiling: automation records listed in both tables

  • Severity: Minor
  • Location: §19.5 aggregate table “Automation records … Authoring bound (19.1), not a shed” inside the runtime-ceiling table
  • What's wrong: Mixes kinds. Spawned instances appear in both tables. Harmless but 4d/4f readers will implement a shed for automation that must not exist.
  • Why it matters for 4d: Don’t shed tracks.

48. §17.1 pipeline order vs actual data flow

  • Severity: Minor
  • Location: §17.1 “primitives → components → procedural systems → layers → camera → post effects → display”
  • What's wrong: Components and primitives are inside systems; systems belong to layers. Pipeline as a feed-forward list will mislead a 4d architecture (there is no “component pass” after primitives). Real order: tick systems → sort objects in layers → apply camera per layer → composite layers → effects.
  • Why it matters for 4d: Don’t build seven sequential passes matching that list.

49. §17.14 “Rendering consumes no procedural stream” vs grain vs wander offsets

  • Severity: Minor
  • Location: §17.14; §18.6 wander consumes stream once at instantiation; §19.4 grain exempt
  • What's wrong: Locked grain exemption is stated. 17.14’s “no visual analogue” of 14.6 is slightly overstated once grain exists. Not a 4d draw-loop sample bug if 4d respects 17.14.
  • Why it matters for 4d: Frame loop must not call the PRNG.

50. components root vs components.visual

  • Severity: Minor
  • Location: §1.1 components; §18.1 components.visual.<id>
  • What's wrong: Audio components live under the same components object (presumably components.audio from 15.x). 4d loader must not treat every components entry as visual. Out of scope to fully check 15.x; flag if 18.1 is the first mention of the visual subkey.
  • Why it matters for 4d: Schema.

51. Implicit layer vs layer field required-by-reference

  • Severity: Minor
  • Location: §17.5 “When visuals.layers is absent, one implicit layer … and a system's layer field is ERR_INVALID_REFERENCE.”
  • What's wrong: Clear: you may not name a layer if none were declared. Default layer on §17.7 is “implicit layer.” Consistent. If layers is present, omitting layer on a system — §17.7 default “implicit layer” which does not exist. So every system must set layer whenever layers is declared. Never stated as required-conditional.
  • Why it matters for 4d: Validation.

52. cover/contain and camera projection center

  • Severity: Major (related to 11 and 42)
  • Location: §19.3 “projection center is the center of the display rectangle after fit”
  • What's wrong: Under contain, letterbox is background; projection center is display center, which may not be scene center on screen if letterboxing is not centered (defect 11). Under cover, cropped scene center may not match display center. Perspective about display center while scene origin is top-left is a specific choice that should have a numeric example.
  • Why it matters for 4d: Same as camera matrix.

53. Color alpha “multiplies the applicable opacity” vs fog vs layer

  • Severity: Minor
  • Location: §17.2 color; §17.12 opacity
  • What's wrong: #rrggbbaa × object opacity × group opacity × layer opacity × fog. Premultiply when, relative to blend add? Unspecified. Canvas 2D globalAlpha vs fillStyle alpha differ.
  • Why it matters for 4d: Every translucent primitive.

54. ERR_INVALID_PRIMITIVE_TYPE for fifteen types vs “Primitive Set 0.1 is fourteen”

  • Severity: Minor
  • Location: §17.9 vs §18.1 vs §17.15 table
  • What's wrong: Locked: fourteen closed; component is fifteenth object type. Text agrees. Diagnostic table §17.15 still says “not a member of Visual Primitive Set 0.1” without mentioning component. After 18.1, unknown type is “outside the fifteen.” 4d if implemented before 4e might reject component correctly as invalid primitive; 4e then accepts it. Trace 2 of 17.16 says unknown type is ERR_INVALID_PRIMITIVE_TYPE — component in a 4d-only renderer would fail that if exhibits use it.
  • Why it matters for 4d: 4d scope: parse component as a stub group or reject? Spec does not give a 4d subset.

55. Slice 4d is not given a subset of §17–19

  • Severity: Blocker
  • Location: §19 opening “Slice 4d begins the renderer”; §17.16 traces 1–14 as 4d acceptance; traces 15–16 are 4h; §18.10 traces belong to 4e; §19.7 to 4f
  • What's wrong: 4d acceptance traces include perspective, fog, masks, conic fallback, bindings (trace 14), and “section 8.1 table unchanged.” They do not include camera matrix §19.3, yet §17.6 perspective is defined in terms of 19.3. 4d cannot implement trace 6 without 19.3, and 19.3 is a 4c/4f section. Circular implementation dependency.
  • Why it matters for 4d: The renderer core must implement a camera to pass 17.16.6, but camera is specified in 19.3 and tested in 19.7.13–14. 4d needs an explicit borrow of 19.3 or a reduced orthographic-only 4d gate.

Standing decisions — compliance check

Decision Spec actually says it? Contradicted elsewhere?
Angles degrees, 0 along +x, positive toward +y Yes, §17.2 Yes — y-down top-left (§17.4) makes “toward +y” clockwise; not acknowledged
Increasing z farther; sort greater z first; ties document key order Yes, §17.6 Partially — particle systems are atomic (§18.2); emitters/repeaters unspecified
Perspective focalLength / (focalLength + z); ortho: z for sort/parallax/fog only Yes, §17.6, §19.3 Formula vs V space mix (§19.3)
Visual objects keyed, no id Yes, §17.8 No
Transform order T(pos+translate) × T(origin) × R × K × S × T(−origin); M_parent × M_local Yes, §17.11 z not in M_local; camera V separate
ValueSpec once at instantiation; render consumes no stream Yes, §17.14 Automation mutates later (§19.1); grain exempt (§19.4)
layer system property not object Yes, §17.10 No
Depth fog per object, exact, no diagnostic Yes, §17.6 Color-space unspecified
Conic only appearance fallback, warn once per paint Yes, §17.12 Fallback segment not unique
font sans-serif/serif/monospace Yes, §17.13 No
Live numeric text deferred Yes, §17.13 No
Fourteen primitives closed; component fifteenth Yes, §17.9, §18.1 §17.15 cause text stale
Component parameters number/boolean/string/color Yes, §18.1 No
inputs.* / repeat.* construct-scoped Yes, §18.1, §18.5 No
Expansion path is §9.3 key Yes, §18.1 No
Integrator v ← (v+a·dt)·(1−drag)^dt; p ← p+v·dt Yes, §18.2 No
Life ramps endpoints once Yes, §18.2 No
Emission accumulator; floor(rate·t); no reset Yes, §18.4 Particles say “Emission timing is 18.4” — OK
ERR_UNBOUNDED_EMISSION; count alone legal Yes, §18.2, §18.4 No
Pool oldest-first; aggregate §19.5 Yes No
Index-driven + continuous rate = ERR_INVALID_DISTRIBUTION Yes, §18.3 No
Sample order x,y,z; angle before radius Yes, §18.3 Polar ring field order in table is radius before angles — sample order vs field order clash
Coherent noise 3D, 12 gradients, 256 perm, quintic, curl default Yes, §18.7 Curl of scalar potential not unique; no sample table
Ribbons = trail.mode; links on emitter = ERR_UNKNOWN_FIELD Yes, §18.8 No
Morph equal type, points, spline mode Yes, §18.6 Non-point primitives underspecified
Exactly four §8.1 visual rows; no layer.visible Yes, §19.1 §17.16.14 and §17.3 contradict
Two automation scopes; cross-scope ERR_INVALID_REFERENCE Yes, §19.1 visuals.automation vs unknown-field
Track shape = §16.1 plus loop; ping-pong count = round trip Yes infinite only-persistent vs spawned-legal clash
Automatable ⊃ bindable Yes Repeater step does not exist
Behavior + track = ERR_AUTOMATION_CONFLICT; else pipeline then behaviors Yes, §19.1 Channel granularity fuzzy
Lifecycle enum; fields on persistent = ERR_UNKNOWN_FIELD; spawn key id#ordinal Yes, §19.2 No
Five states + FAILED; no SCHEDULED; release 0ms still one RELEASING tick Yes CREATED→FINISHED unexplained
Spawn at ceiling refused, not evicted Yes Warn-once vs once-per-second
Camera matrix normative; parallax translation only; projection authored once Yes, §19.3 Matrix mixes scene and display spaces
Seven effects, array order; blur/bloom two passes; approx warn; skip not substitute Yes, §19.4 No
Grain exempt, no stream Yes Scanlines also device-pixel but not exempt
§19.5 centralized; values provisional Yes Automation row in runtime table

Sample-order clash (extra): §18.3 “field order of its row” for ring is center, radius, innerRadius, startAngle, endAngle, direction, mode, but “polar forms angle before radius.” A fixture cannot satisfy both. Severity: Major for 4e; 4d should not implement distributions until this is fixed.


What was checked and found sound

  • Identifier regex and keyed content/children with no id field (§1.3, §17.8).
  • Strict unknown-field policy as a general rule (the automation hole is the exception, not the rule).
  • Three coordinate spaces named; width/height only for virtual; viewport rejects non-stretch fit.
  • Layer cap 16; group nest 8; polygon/polyline 512; spline 256; path commands 512; gradient stops 16; filters 4.
  • Transform composition string matches the locked order; hierarchy M_parent × M_local and z_parent + z_local.
  • Negative scale = mirror; scale 0 legal and draws nothing; skew magnitude ≥ 90 is ERR_OUT_OF_BOUNDS.
  • Safe blend set closed; stroke-only declared-fill vs inherited-fill distinction.
  • Path must start with move; zero-radius path arc is ERR_OUT_OF_BOUNDS not a line; bezier spline 3n+1.
  • Fonts limited to three generic families; live numeric text explicitly deferred with rationale.
  • Component parameter types (four scalars) vs audio number-only; inputs.* not a §8.1 row.
  • Particle integrator formula and dt-correct drag; life-ramp-without-lifetime = ERR_SCHEMA_VALIDATION; exponential-through-zero reuses WARN_AUTOMATION_FALLBACK.
  • Unbounded emission diagnostic; initial count without lifetime legal; oldest-first eviction.
  • links on emitter = ERR_UNKNOWN_FIELD; ribbons as trail mode.
  • Four §8.1 rows only; visuals.layers.<id>.visible absent on purpose; system visible bindable not automatable.
  • Spawn refuse vs voice evict, with rationale; no SCHEDULED state; 0ms release still enters RELEASING.
  • Projection not automatable; grain stream exemption and rationale.
  • Centralized ceiling table present; values labeled provisional pending 4h; runtimes may lower never raise.
  • Diagnostic reuse policy (ERR_COMPONENT_RECURSION, ERR_AUTOMATION_CONFLICT widened, etc.) is consistent in intent.

4d-specific recommendation (not a spec change, a sequencing note)

Do not start the renderer against §17.16 as a closed gate. Minimum contract fixes before 4d code:

  1. Add automation to the §17.3 visuals key list.
  2. Rewrite §17.16.14 to match §19.1’s four rows.
  3. State Y-down + rotation sign in one sentence.
  4. State box/ellipse local origin.
  5. Give one numeric camera example (scene point → display point) that uses a single space for V.
  6. Borrow §19.3 into the 4d scope list, or drop perspective from 4d traces.
  7. Delete or define repeater step.
  8. Fix “layer nesting” in trace 9 to group nesting / layer count.

Until (3)–(5) land, two renderer cores can both “conform” and disagree on every rotated, zoomed, or letterboxed frame.