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
25 KiB
XZBT 0.1 Format Specification §§14–16 — review triage
Inputs: 01-grok-4.6.md, 02-kimi-k3.md (two outputs, same seven findings), 03-deepseek-v4-flash.md.
GLM review not received.
Every claim below was checked against docs/XZBT_0-1_Format_Specification.md as it stands on disk
(1499 lines). Line numbers are that file's. Nothing in the spec was edited.
Result: 13 distinct defects. All 21 reported findings are real — none is a false positive — but only 4 of 13 were found by all three reviewers, and 5 of 13 were found by exactly one. Four proposed fixes are wrong or incomplete and are corrected below. Two defects that no reviewer found are added.
Scorecard
| Grok 4.6 | Kimi K3 | DeepSeek v4 Flash | |
|---|---|---|---|
| Findings reported | 6 | 7 | 8 |
| Real | 6 | 7 | 8 |
| Overstated | 0 | 0 | 1 (D8) |
| Unique catches | 0 | 1 | 2 |
| Of the 13 confirmed | 6 | 7 | 8 |
| Proposed fix wrong/incomplete | 2 | 1 | 2 |
DeepSeek is the strongest pass: it is the only reviewer to unify the bus-gain defect (T1) instead of splitting it, and the only one to catch the eviction defect (T3) and the modulated-delay bound (T8). Kimi is the only one to catch the component-parameter defect (T2), which is the second-largest hole in the section. Grok found nothing the other two missed, and split T1 into two half-findings whose fixes contradict each other.
The overlap pattern is the argument for having run multiple models: 9 of 13 defects would have been missed by any single reviewer.
Tier 1 — Unimplementable: a required acceptance trace cannot pass as written
T1. Bus gain claims automation and modulation with no authoring surface; no target in the document exposes both automation and override; trace 16.11.8 cannot be implemented
Reported by: DeepSeek F1 (complete). Grok #4 and #5 are the same defect split in two. Kimi missed it.
Confirmed. Three independent statements are orphaned:
- 8.1 (line 314) gives
audio.buses.<id>.gainAutomation = Yes and Additive modulation = Yes. 15.17 (line 1230) and 16.2 (line 1330) repeat both claims. - Automation tracks live only in a graph object's
automationarray (14.3 line 606, 16.1 line 1265). Graph objects exist at exactly three places (lines 610–612); a bus is not one of them, and 15.17's bus table (line 1228) allows onlygain, so anautomationkey on a bus isERR_UNKNOWN_FIELDunder the strict unknown-field policy. - 16.1 line 1286 restricts
targetto<node-key>.<property>"resolved in the same graph", and line 1293 restricts it to the 15.13 registry. A bus is not a node and bus gain is not in that registry (lines 1073–1085). No legaltargetcan name a bus. - Modulation has the same problem: the only modulation syntax is a 15.12 route whose
tois<node-key>.<property>in the same graph. Nothing can modulate a bus gain either.
Consequences:
- 16.2's second consequence (line 1332, override masks automation, releases to the track's current
value) describes a situation no legal document can produce. Node properties take automation
(16.1) but are barred from override (14.4 line 631,
ERR_UNSUPPORTED_TARGET);parameters.<id>andstate.<id>take override but have Automation = No (8.1 lines 312–313); bus gain is the only row with both, and its automation is unauthorable. No target exposes both stages. - Required trace 16.11.8 (line 1485) is therefore unimplementable, and Phase 3c cannot be accepted as written.
Fix — all three reviewers get this wrong.
- Grok #4 proposes keeping override-vs-automation "only for
audio.buses.<id>.gain". That fails for the same reason: bus gain has no automation surface. - Grok #5 offers "add an
automationarray on the bus object, or strike automation from 16.2" and says not to leave both standing — correct as far as it goes, but it treats only the automation half and leaves the modulation half untouched. - DeepSeek F1 correctly identifies both halves and both options, then says "Option (a) is the smaller change." That is wrong. Deleting additive modulation from bus gain would orphan §8.5 required trace 6 (line 409, "A target has legal additive modulation → Modulation follows the winning override; safety clamp runs last"), because bus gain is the only row in the entire 8.1 table with Additive modulation = Yes. Option (a) does not shrink the problem; it moves it into §8, a contract §11 line 513 already marks Complete (Rev 0.3 / GC3). §8.3 line 370 ("Underlying binding and automation stages continue to evaluate while masked") is orphaned by the same gap.
Recommended fix: option (b). Give audio.buses.<id>.gain a real authoring surface for automation
and modulation, and make 16.1's target rule name it. Concretely: allow an automation array on the
bus object with target fixed to gain, and add a bus-addressed modulation form. This keeps 8.1,
8.3, 8.5, 15.17, 16.2 and traces 16.11.8 and 8.5-6 all consistent, and it is the only option that
does not reopen a completed contract.
Out of scope but worth recording: §8.5 trace 6 and §8.3's masking sentence are already unimplementable today, independent of §§14–16. All three reviewers scoped to 14–16 and so could not see this. It should be logged against §8.
T2. Component exposed parameters are legal modulation and automation targets with no defined effect; trace 15.19.4 cannot be implemented
Reported by: Kimi #1 only. Grok and DeepSeek both missed it.
Confirmed. 15.13 line 1085 lists component / <exposed-parameter-id> in the modulatable
registry; 15.11 line 1045 repeats it; 16.1 line 1293 inherits it for automation. But the only path by
which a component's internals read a parameter is { "ref": "inputs.<id>" } in a node field
(15.15 line 1166), and 14.4 line 629 resolves every node field once at instantiation, "constant
for that node instance's lifetime". 15.11 line 1039 resolves the supplied values once as well.
So a modulation sum or automation curve applied to an exposed parameter changes a value that nothing ever reads again. Required trace 15.19.4 (line 1250, "an exposed parameter is a legal modulation target") cannot be satisfied without inventing behavior.
Fix — Kimi offers two options and correctly says the first is smaller. It is also the only correct
one, for a reason Kimi does not give. Option two (make { "ref": "inputs.<id>" } fields track the
parameter live, as an exception to 14.4) would let a modulation route reach any internal node field
through a parameter — including reverb.decay, compressor.threshold, waveshaper.amount and every
other property 15.13 line 1087 deliberately excludes ("Merely being numeric does not grant modulation
support"). That silently defeats the registry's whole purpose and breaks 15.11's encapsulation claim.
Recommended fix: delete the component row from the 15.13 registry (line 1085), delete the
modulation-target sentence in 15.11 (line 1045), and drop the exposed-parameter clause from trace
15.19.4.
T3. 16.6 eviction step 3 frees no budget slot; the outcome is undefined and trace 16.11.10 is ambiguous
Reported by: DeepSeek F2 only.
Confirmed. Line 1406: "A voice counts against its ceiling from CREATED until DISPOSED."
The eviction policy (lines 1408–1413) "stops at the first candidate":
- Dispose the oldest
FINISHEDinstance — frees a slot. - Evict the oldest
RELEASINGinstance by advancing its ramp to completion and disposing it — frees a slot. - For a one-shot request only: evict the oldest
ACTIVEone-shot by starting its release — moves itACTIVE→RELEASING, which still counts (line 1406, reinforced by line 1372 and trace 16.11.11). Frees nothing. - Otherwise refuse.
With the ceiling full of ACTIVE one-shots, the policy stops at step 3 having freed no slot.
Admitting the new instance puts the count at 65; refusing it contradicts having stopped before step 4.
Neither is defined. Step 2's explicit "and disposing it" makes step 3's omission conspicuous rather
than incidental.
A weak defense exists — line 1399 calls the ceilings "approximate" — but line 1406's counting rule and trace 16.11.11 both insist on exact accounting, so this does not resolve it.
Fix — DeepSeek offers two options without ranking them; the first is correct. Making step 3 behave like step 2 (complete the release immediately and dispose) contradicts line 1415, "Eviction always releases; it never hard-stops an active voice" — an instant ramp on an audible voice is the click that 16.4's single-code-path design exists to prevent. Step 2 does it only to voices already decaying.
Recommended fix: state that an ACTIVE one-shot evicted under step 3 stops counting against the
ceiling at the moment of eviction, its release running outside the budget, so the new instance is
admitted and the ceiling is never exceeded. Add the matching exception to line 1406.
Tier 2 — Real defects with clear fixes
T4. ERR_INVALID_RANGE_ORDER is filed Semantic-only but the sample-hold rule needs instantiation-time values
Reported by: all three (Grok #1, Kimi #2, DeepSeek F4). Unanimous, and unanimously right.
Confirmed. 14.12 lines 815–816 make min/max full ValueSpecs in 14.4 scope — resolved once at
instantiation. Line 819 makes inverted resolution ERR_INVALID_RANGE_ORDER. §7 line 286 files that
code Semantic only, with a cause that says "after resolution" — the table concedes the problem in
its own text. 14.5 line 645 states semantic validation checks only values that resolve to a literal
and never opens an AudioContext; 9.3 line 445 puts the root seed at performance start, not import.
{ "min": { "random": {...} }, "max": { "random": {...} } } is import-legal and its order is
undecidable at the only stage the code is filed under.
Fix: all three converge and all three are right. Refile as Semantic / Runtime in §7 (mirroring
ERR_OUT_OF_BOUNDS, line 272, which is already dual-filed), and state in 14.12 that literal bounds
are rejected at import and resolved bounds at instantiation.
Note: §7's cause text mentions only sample-hold min/max. 16.1 line 1299 uses the same code
for automation at ordering, which is purely semantic because at is a literal by construction
(line 1297). The revised cause text must cover both without implying at is a runtime check.
T5. Automation point value draws from a seeded stream with no documented sampling boundary or order
Reported by: all three (Grok #3, Kimi #4, DeepSeek F6).
Confirmed. 16.1 line 1289 makes each point { "at": <literal>, "value": ValueSpec<number> }, and
line 1297 says "A track's values remain full ValueSpecs and may be random."
9.3 line 457 requires sampling at "the containing object's documented instantiation or invocation
boundary". No such documentation exists for automation:
- 14.4 line 629 documents the boundary for fields on node objects. A track is on the graph object.
- 15.12 line 1067 documents the order for route
depthonly. - 14.12 line 825 is the only documented child key, and it is
sample-holdonly.
Two conforming implementations can consume the sound instance's stream in different orders and diverge for the same seed, breaking 9.3's reproducibility promise and 14.6's restatement of it. 16.8 line 1445's guarantee that a partially elapsed continuous sound has "a procedural sequence identical to an unbroken run" has no defined meaning for automated tracks.
Fix: all three propose the same one-sentence addition to 16.1 and all are adequate. Kimi's and
DeepSeek's are the more precise, because they fix the position of automation in the depth-first
order relative to nodes and routes, which Grok's leaves open:
A track's point values are resolved once at the owning sound instance's instantiation boundary, sampled from that instance's stream in depth-first, property-document order, with
automationtaken afternodesandroutes.
T6. 15.14 calls the automation track/point limits runtime ceilings; 16.1 enforces them with a Semantic code
Reported by: all three (Grok #6, Kimi #3, DeepSeek F5).
Confirmed. Line 1132 groups automation tracks (64) and points (256) with the voice ceilings as
"runtime ceilings rather than document properties". Line 1318 makes exceeding either
ERR_NODE_LIMIT_EXCEEDED, filed Semantic at §7 line 285, and trace 16.11.6 (line 1483) sits in
the "Automated, and executable without an audio device" group. Track and point counts are decidable
after component expansion, which trace 15.19.7 confirms runs with no AudioContext. The voice
ceilings in the same sentence are correctly runtime.
Fix — Grok's first option is wrong. Grok #6 offers "move tracks/points into the authoring-limit
table with expanded nodes/routes". That table is headed "The authoring-time limits enforced in
Phase 3b" (line 1122), and automation is Phase 3c — 16.1 line 1261 states that the current
validator rejects any document declaring automation with ERR_UNKNOWN_FIELD. Putting them in the
Phase 3b table asserts enforcement that cannot exist. DeepSeek F5 offers the same wrong option
alongside the right one.
Recommended fix (Kimi's): delete "automation tracks (64), and automation points (256)" from
line 1132, leaving the voice ceilings. Optionally add a pointer that 16.1 enforces them semantically.
Separately, extend §7 line 285's cause parenthetical — it currently reads "(oscillator partials,
resonator modes, expanded nodes or routes per sound)" and omits the automation limits entirely.
T7. 16.5's resonator contribution is undefined when every mode is omitted at instantiation
Reported by: all three (Grok #2, Kimi #5, DeepSeek F7).
Confirmed. 16.5 line 1387 gives a resonator "the longest decay among its retained modes".
15.10 line 1024 omits, at instantiation and with no diagnostic, any mode whose resolved frequency
exceeds audioMaxFrequency. Mode entries are omitted rather than clamped — 14.7 line 690 is explicit
that omission "is not the WARN_AUDIO_RATE_CLAMP case of 14.5, which applies to the node's own
frequency".
A single-mode resonator can therefore retain nothing, and "longest decay" becomes a maximum over an empty set — feeding the one-shot ending bound, which 16.5 line 1376 requires to be computable "from the resolved graph alone".
All three examples check out arithmetically. The cleanest is Grok's: a mode with absolute
frequency: 20000 is import-legal against the device-independent ceiling 24000 (14.5 line 645) and
omitted on a 44.1 kHz device, where audioMaxFrequency = min(24000, 19845) = 19845.
Fix: DeepSeek's wording is the tightest — extend the row to "the longest decay among its
retained modes, or zero when no mode is retained".
Downgrade: Kimi #5 and DeepSeek F7 also raise the all-partials-omitted custom oscillator. That one
is materially weaker: an empty sum of partials is silence by construction, and oscillator is
unbounded in the 16.5 table regardless, so no bound is left undefined. Worth one clarifying
sentence in 14.7, not a Tier 2 defect.
T8. The 16.5 ending bound is not an upper bound when delay.time is modulated
Reported by: DeepSeek F3 only.
Confirmed. 16.5 line 1376 defines "determinable" as a finite upper bound computed at instantiation
"from the resolved graph alone"; line 1385 computes the delay contribution from the resolved
time. But delay.time is modulatable (15.6 line 943; 15.13 line 1082) and 15.6 line 949 clamps a
modulated delay only to [0ms, 10s]. A legal oneshot may route an lfo or sample-hold at
delay.time; the actual feedback tail can then run far past the computed bound, and the runtime tears
the instance down while it is still ringing — contradicting 16.3 line 1360, which asserts that at a
determinable ending "the envelope has already returned to zero". Trace 16.11.9 passes only for graphs
whose bound-contributing properties are unmodulated.
delay.feedback is not modulatable (line 944), so time is the only exposure.
Fix — DeepSeek's is incomplete, and T9 is why. It proposes bounding the contribution by the
largest reachable time, "source outputs are bounded — lfo by its resolved amplitude, sample-hold
by its resolved min/max". That list omits oscillator, which 15.13 line 1089 also permits as a
modulation source and whose output magnitude the spec never states (see T9). Until T9 is fixed, the
proposed bound cannot be computed for an oscillator-modulated delay.
Recommended fix: fix T9 first, then bound the delay contribution over the property's reachable
range given its modulation routes. Alternatively — and this is the genuinely smaller change — exclude
a oneshot whose bound-contributing property is modulated from determinable-ending status, i.e.
ERR_INDETERMINATE_ONESHOT. Note that an automated delay.time is exactly boundable once T5 lands,
since at values are literals and point values resolve at instantiation; only modulation is open.
T9. An oscillator used as a modulation source has no defined output magnitude
Not reported by any reviewer.
Confirmed. 15.13 line 1089 permits a modulation route's from to be "a control source
(constant, lfo, sample-hold) or an oscillator used at audio rate (PRD 41)". The summation
rule at line 1094 is value = clamp(b + sum(s_i x d_i), ...), and line 1097 defines s_i only for
control sources: "A control source's output is its own value in its own units before scaling: an
lfo with amplitude: 1 and polarity: "bipolar" contributes [-d, +d]."
An oscillator is not a control source (14.7 line 678; 15.14 rule 5 lists only constant, lfo,
sample-hold), and 14.7 gives it no amplitude field and no stated output range. For a custom
waveform with up to 64 partials at gain: 1 each (line 685), the peak is not even conventionally
[-1, 1]. So s_i is undefined for the one source type the registry explicitly admits alongside the
control sources.
Recommended fix: state the oscillator's modulation output range in 15.13 — e.g. that a non-custom
oscillator contributes [-d, +d], and a custom oscillator's output is normalized to peak 1 before
scaling by depth. Normalization is the safer choice; without it, depth means something different
for every harmonic table.
Tier 3 — Editorial and low-severity
T10. 16.2's "For a node property" pipeline includes an override stage that is always absent
Grok #4's residue, after T1 absorbs the rest. Line 1322 introduces the pipeline diagram at line 1325
with "For a node property:", and the diagram includes winning override. 14.4 line 631 makes an
override on a node field ERR_UNSUPPORTED_TARGET, and 16.2 line 1330 reaffirms it for binding but is
silent on override. Per 8.1 line 308 the stage is "absent, not an identity hook", so the diagram
should say so. Fix: annotate the diagram or add one clause to line 1330 noting that the override
stage is absent for node properties.
T11. 14.9's "Value at p = 1" column contradicts the exponential row's own annotation
DeepSeek F8 — real, but overstated. Line 741 defines the envelope over 0 <= p < 1; line 749 then
says "The envelope is exactly 0 for p >= 1", which makes the column's three zeros defensible for
the piecewise function. The genuine inconsistency is narrower and internal to one row: the
exponential row's Envelope cell is annotated "(-60 dB at p = 1)", i.e. 0.001a, while its
"Value at p = 1" cell says 0. The same table then supports line 749's "Because flat and
exponential do not reach zero on their own" — which is false of a column reporting 0 for both.
No implementation ambiguity survives, because the terminal fade is normative either way.
Fix: relabel the column "Value as p → 1⁻" with a, 0, a x 0.001, or delete it.
T12. sounds.<id>.name and tags length caps name no diagnostic
Kimi #6. Lines 1197–1198 cap name at 128 characters and tags at 16 entries of 32 characters
with no code. Every other quantitative limit in §§14–16 names one, and no §7 entry covers string or
array lengths: ERR_INVALID_ID (line 268) is identifier-scoped, ERR_OUT_OF_BOUNDS (line 272) is
numeric min/max, and ERR_SCHEMA_VALIDATION (line 265) covers "malformed top-level shapes", which
sounds.<id> is not. Fix: name the code — ERR_SCHEMA_VALIDATION is the closest fit.
T13. 16.2 line 1332 mis-cites §8.1
Kimi #7. The cited "parameter-masking rule of section 8.1" is not in 8.1 — its only masked-override
text (line 318) is about the UI override indicator. The behavior lives in 8.3 (line 370, masked
stages continue) and 8.4 (line 392, currentLowerValue recomputed every tick), with the trace at
8.5 line 406. Fix: cite 8.3/8.4.
T14. An exposed component parameter with no min/max has no clamp range
Not reported by any reviewer. 15.15 line 1163 makes min and max optional on an exposed
parameter, but 15.13 line 1094 clamps a modulated property to "the property's declared range from its
node contract". For { "type": "number", "default": 220 } there is no such range.
Moot if T2's recommended fix lands (deleting the component row removes the only path here); if
option two is chosen instead, this must be fixed alongside it.
Findings checked and cleared
- Class 1 (prose vs JSON in the same section): all three reviewers reported none, and that holds.
Every example and invalid case in 14.3–14.13, 15.2–15.17 and 16.1 matches its section's tables.
One editorial note nobody raised: 14.3's canonical graph example (lines 589–600) is
oscillator → gain → output, which is not a legaloneshotrecipe — as a recipe it would beERR_INDETERMINATE_ONESHOT(16.5 line 1393), andmodedefaults tooneshot(line 1208). It is a bare graph-object example, so this is not a defect, but it is an unfortunate thing to copy. - Class 2 (field missing from its container's allowed-field table): none, confirmed. The 15.13 registry matches the "Modulatable" columns of 15.2–15.10 exactly; T1's bus-gain case is a missing surface, not a missing table row.
- Diagnostic codes used but absent from §7: none. All codes in §§14–16 appear in the §7 table, and the 15.18/16.10 tables match it. Mis-staging is T4 and T6; an incomplete cause list is folded into T6.
- Cross-references: all internal references resolve. The only defect is a mis-reference, T13.
- Non-termination: 16.5's longest-path terminates on the acyclic expanded graph (15.14 rules 7 and
11). The delay formula
ceil(log(1/1000)/log(feedback))is well-defined on the legal(0, 0.95]and correctly branches atfeedback > 0. - Modulation-only
oscillatorand the one-shot bound: an oscillator used purely as a modulation source has no audio-route path tooutput, and 16.5 line 1393 keysERR_INDETERMINATE_ONESHOTto the audible path, so it does not make aoneshotindeterminate. Correct as written. - Deliberate-design exclusions (node
id, the 14.4 bindable surface, noise/impulse non-reproducibility, provisional master-protection values, absentaudio.master, literal automationat, the GC4 stall bound, bus processing beyond gain) were respected by all three reviewers and are not defects.
Recommended edit list
Ordered by severity. Items marked (design) need a decision before an edit can be written.
| # | Location | Change | Kind |
|---|---|---|---|
| T1 | 8.1, 15.17, 16.1, 16.2, 16.11.8 | Give bus gain an automation/modulation authoring surface and name it in 16.1's target rule | (design) |
| T2 | 15.13 line 1085, 15.11 line 1045, 15.19.4 | Delete the component modulation-target row and its two restatements |
Delete |
| T3 | 16.6 lines 1406, 1412 | Evicted ACTIVE one-shot leaves the ceiling count immediately |
One sentence |
| T4 | §7 line 286, 14.12 | Refile ERR_INVALID_RANGE_ORDER as Semantic / Runtime; split literal vs resolved |
Restage |
| T5 | 16.1 | Document the automation point value sampling boundary and order |
One sentence |
| T6 | 15.14 line 1132, §7 line 285 | Remove the two automation limits from the runtime-ceiling sentence; extend the cause list | Delete + edit |
| T7 | 16.5 line 1387 | "…or zero when no mode is retained" | Clause |
| T8 | 16.5 line 1385 | Bound the delay contribution over the reachable time, or exclude modulated bound-contributors |
(design), blocked on T9 |
| T9 | 15.13 line 1097 | State an oscillator's modulation output magnitude and custom normalization |
(design) |
| T10 | 16.2 lines 1325, 1330 | Note that the override stage is absent for node properties | Clause |
| T11 | 14.9 lines 743–747 | Relabel or delete the "Value at p = 1" column |
Editorial |
| T12 | 15.16 lines 1197–1198 | Name the diagnostic for the name/tags caps |
Editorial |
| T13 | 16.2 line 1332 | Cite 8.3/8.4 instead of 8.1 | Editorial |
| T14 | 15.15 line 1163 / 15.13 line 1094 | Only if T2 takes option two | Conditional |
Outside §§14–16, log separately: §8.5 trace 6 and §8.3's masked-automation sentence are already unimplementable for want of a target with those stages (see T1).