- Record user evidence for directory fallback to complete GC1 (10/10 checks passed) - Expand Format Specification 0.1 to Revision 0.2 with normative shared contracts - Author JSON Schema Draft-07 at schema/xzbt-0.1.schema.json - Implement zero-dependency semantic validator at tools/validate-exhibit.mjs - Create 12-case conformance fixture suite and automated test runner (12/12 passing) - Update implementation status, verification gates, and gap closure decisions - Add devlog entry covering stall recovery and Phase 0 current state
20 KiB
XZBT Format Specification 0.1
XZBT format version: 0.1
Document revision: 0.2
Status: Normative specification for shared contracts (document, types, ValueSpec, ConditionSpec, resolution); subsystem contracts in progress
Related resources: PRD, decisions, verification
This document defines normative syntax and runtime semantics for XZBT 0.1 exhibits. An exhibit is a UTF-8 JSON document that configures generic procedural visual, audio, cadence, and orchestration primitives. It does not contain executable JavaScript.
1. Document Structure & Metadata
1.1 Root Structure
A conforming .xzbt exhibit document consists of a top-level JSON object with the following properties:
| Field | Type | Required | Description |
|---|---|---|---|
xzbt |
string |
Yes | Must be exactly "0.1". Mismatched versions fail validation immediately. |
meta |
object |
Yes | Exhibit identity and descriptive metadata. |
runtime |
object |
No | Initial simulation and PRNG seed configuration. |
parameters |
object |
No | User-tunable configuration definitions. Map of ID -> ParameterSpec. |
state |
object |
No | Simulation-owned mutable variables. Map of ID -> StateSpec. |
signals |
object |
No | Read-only runtime environmental signals. |
ui |
object |
No | Generated control grouping, widgets, and layout preferences. |
components |
object |
No | Reusable audio and visual sub-assemblies. |
visuals |
object |
No | Visual canvas, camera, rendering passes, and primitive systems. |
audio |
object |
No | Master bus, auxiliary buses, routing, and synthesizer definitions. |
sounds |
object |
No | One-shot sound event templates. |
cadence |
object |
No | Procedural pulse clocks, rhythm pools, and recurring trigger policies. |
modulators |
object |
No | Continuous low-frequency oscillators, noise, and sample-and-hold generators. |
bindings |
array |
No | Directed value-propagation links between sources and target properties. |
events |
object |
No | Discrete state-change and lifecycle trigger handlers. |
scenarios |
object |
No | Autonomous orchestrated sequences, timelines, and temporary overrides. |
Strict unknown-field policy: Any unrecognized property in behavior-bearing sections (parameters, state, visuals, audio, cadence, modulators, bindings, events, scenarios) is a fatal validation error (ERR_UNKNOWN_FIELD).
1.2 Metadata (meta)
Metadata provides provenance and UI presentation details. It never executes or modifies runtime logic:
| Field | Type | Required | Description |
|---|---|---|---|
id |
string |
Yes | Unique exhibit identifier. Pattern: ^[a-z][a-z0-9_-]*$. Max 64 chars. |
name |
string |
Yes | Human-readable title displayed in the library and header. Max 128 chars. |
version |
string |
No | Exhibit semantic version string (e.g., "1.0.0"). |
author |
string |
No | Author or creator attribution string. Max 128 chars. |
description |
string |
No | Brief narrative description of the exhibit. Max 1024 chars. |
license |
string |
No | License terms (e.g., "CC-BY-4.0", "All Rights Reserved"). |
tags |
array[string] |
No | Array of category or aesthetic keywords. Max 16 tags. |
1.3 Identifiers and Namespaces
- Identifier Syntax: All resource IDs (exhibit ID, parameter IDs, state IDs, bus IDs, scenario IDs) must strictly match:
^[a-z][a-z0-9_-]*$ - Reference Path Syntax: Dot-delimited path referencing a target property or namespace:
Valid canonical namespace prefixes:
<namespace>.<resource_id>[.<property>]parameters.*,state.*,signals.*,modulators.*,audio.buses.*,visuals.systems.*. Dots are strictly forbidden within identifier names themselves.
2. Shared Type System & Coercion Rules
Every value in XZBT belongs to one of the following concrete primitive types:
| Type | Definition & Constraints | Serialization Example |
|---|---|---|
number |
IEEE 754 64-bit float. Must be finite (Number.isFinite(v) === true). NaN, +Infinity, and -Infinity are strictly forbidden and fail validation/evaluation. |
440.0, -0.5, 1e3 |
integer |
IEEE 754 64-bit float restricted to integer values (Number.isInteger(v) === true). |
1, 42, -8 |
boolean |
Logical truth value: true or false. |
true, false |
string |
Valid UTF-8 text string. | "drift", "sine" |
color |
CSS-compatible color: #rgb, #rrggbb, #rrggbbaa, or standard CSS color keyword. |
"#e8ad57", "#11151c" |
enum |
String constrained to an explicitly declared set of allowed tokens. | "triangle" in ["sine", "triangle", "saw"] |
Strict Coercion Ban: The runtime performs no implicit type coercion. A string containing digits (e.g. "440") will not be coerced into a number; a non-zero number will not be coerced into a boolean. Type mismatches produce ERR_TYPE_MISMATCH.
3. Parameter and State Specifications
3.1 Parameters (parameters.<id>)
Parameters represent user-configurable settings. They have documented defaults and bounds:
{
"activity": {
"type": "number",
"default": 0.5,
"min": 0.0,
"max": 1.0,
"step": 0.01,
"label": "Activity Level"
}
}
- Storage Distinction: User edits to parameters are persisted in client storage separately from default exhibit definitions.
- Protection from
set: Actions triggered by scenarios or events cannot usesetto mutate parameters. Temporary programmatic modifications must useoverride.
3.2 State Variables (state.<id>)
State variables represent simulation-owned state. They are initialized at launch and manipulated by runtime events and scenarios:
{
"machine-load": {
"type": "number",
"initial": 0.25,
"min": 0.0,
"max": 1.0
}
}
- Persistence Distinction: State variables are transient simulation variables and are not stored across browser restarts unless explicitly configured.
4. ValueSpec 0.1 Normative Specification
A ValueSpec is the universal declarative expression used wherever a dynamic or configurable value is accepted. A ValueSpec must match one of the following five forms:
4.1 Literal Constant
A raw JSON number, boolean, string, or color:
440.0
4.2 Reference (ref)
Reads the currently resolved value of a declared target:
{ "ref": "parameters.activity" }
References are evaluated dynamically during each simulation tick unless used within a construct with documented static sampling timing.
4.3 Random Range (random)
Samples a pseudo-random value from a uniform bounded range:
{
"random": {
"min": 200.0,
"max": 800.0,
"integer": false
}
}
min(number, required): Lower bound.max(number, required): Upper bound (max >= min).integer(boolean, optional, defaultfalse): Iftrue, output is rounded to an integer viaMath.floor(min + prng() * (max - min + 1)).- Sampling Boundary: Sampled only at object instantiation or event invocation. It is never sampled per-frame.
4.4 Weighted Selection (choose)
Selects one item from an array of weighted options:
{
"choose": [
{ "value": "sine", "weight": 6 },
{ "value": "triangle", "weight": 3 },
{ "value": "square", "weight": 1 }
]
}
choose(array[object], required): Non-empty array of choice objects.- Each entry requires
value(literalor nestedValueSpec) andweight(number > 0).
4.5 Calculation Operator (op)
Evaluates an arithmetic or mathematical operation over argument operands:
{
"op": "multiply",
"args": [
{ "ref": "parameters.activity" },
1.5
]
}
Supported Operators and Arity
| Operator | Arity | Description | Domain Rules & Safe Fallback |
|---|---|---|---|
abs |
1 | Absolute value: |a| |
Finite number. |
negate |
1 | Arithmetic negation: -a |
Finite number. |
round |
1 | Nearest integer: Math.round(a) |
Finite number. |
floor |
1 | Floor integer: Math.floor(a) |
Finite number. |
ceil |
1 | Ceiling integer: Math.ceil(a) |
Finite number. |
add |
2 | Addition: a + b |
Finite number. |
subtract |
2 | Subtraction: a - b |
Finite number. |
multiply |
2 | Multiplication: a * b |
Finite number. |
divide |
2 | Division: a / b |
Division-by-zero protection: If b === 0, evaluates safely to 0.0 (never NaN or Infinity). |
min |
2 | Minimum: Math.min(a, b) |
Finite numbers. |
max |
2 | Maximum: Math.max(a, b) |
Finite numbers. |
clamp |
3 | Range clamp: [val, min, max] |
Evaluates to Math.min(max, Math.max(min, val)). Requires min <= max. |
lerp |
3 | Linear interpolation: [a, b, t] |
Evaluates to a + (b - a) * t. Unclamped t unless combined with clamp. |
5. ConditionSpec 0.1 Normative Specification
A ConditionSpec evaluates to a boolean (true or false) and controls scenario triggers, conditional actions, and branching logic.
5.1 Comparison Expressions
Compares two ValueSpec expressions:
{
"op": "gt",
"left": { "ref": "state.machine-load" },
"right": 0.85
}
- Supported comparison operators:
eq: Equality (left === right)ne: Inequality (left !== right)gt: Greater than (left > right)gte: Greater than or equal to (left >= right)lt: Less than (left < right)lte: Less than or equal to (left <= right)
leftandrightmust evaluate to compatible primitive types (numberwithnumber,booleanwithboolean,stringwithstring). Cross-type comparison producesERR_TYPE_MISMATCH.
5.2 Logical Combinators
Combines child condition expressions:
- Conjunction (
and): All child conditions must evaluate totrue. Short-circuits on firstfalse.{ "and": [ { "op": "gt", "left": { "ref": "state.machine-load" }, "right": 0.5 }, { "op": "eq", "left": { "ref": "parameters.enabled" }, "right": true } ] } - Disjunction (
or): At least one child condition must evaluate totrue. Short-circuits on firsttrue.{ "or": [ { "op": "lt", "left": { "ref": "state.energy" }, "right": 0.1 }, { "op": "eq", "left": { "ref": "state.alarm" }, "right": true } ] } - Negation (
not): Inverts the child condition.{ "not": { "op": "eq", "left": { "ref": "parameters.mute" }, "right": true } }
5.3 Edge-Triggering & Re-Arming Semantics
- Rising-Edge Trigger: When a condition is used as an event trigger or scenario trigger, it fires only when its evaluation transitions from
falseon tickT-1totrueon tickT. - Re-Arming Rule: As long as the condition remains continuously
true, it will not fire again. It must evaluate tofalseon at least one tick to re-arm before it can fire on a subsequenttrueevaluation.
6. TimeSpec and DurationSpec 0.1
6.1 Duration Literals
Durations are expressed as strings containing a non-negative finite number and a single explicit unit:
ms: Milliseconds (e.g.,"250ms","16.67ms")s: Seconds (e.g.,"4s","0.5s")m: Minutes (e.g.,"2.5m","1m")h: Hours (e.g.,"1.5h","8h")
Compound formats (e.g., "1m30s") are strictly invalid (ERR_INVALID_DURATION). Internally, the runtime converts all durations to millisecond floating-point numbers.
6.2 Procedural Bounded Duration (TimeSpec)
Where procedural timing is permitted (e.g., cadence intervals, scenario wait steps), a bounded random TimeSpec may be used:
{
"random": {
"min": "500ms",
"max": "2.5s"
}
}
7. Diagnostic Error Code Standard
To ensure consistent error reporting between structural schema validation, semantic validation, and runtime execution, all diagnostic errors must use the following standard codes:
| Error Code | Stage | Cause |
|---|---|---|
ERR_SCHEMA_VALIDATION |
Structural | Missing required fields, invalid JSON types, or malformed top-level shapes. |
ERR_UNKNOWN_FIELD |
Structural / Semantic | Behavior-bearing block contains an undeclared property. |
ERR_UNSUPPORTED_VERSION |
Structural | Document xzbt attribute is not "0.1". |
ERR_INVALID_ID |
Structural / Semantic | Identifier fails ^[a-z][a-z0-9_-]*$ regex or exceeds length limits. |
ERR_INVALID_REFERENCE |
Semantic | Reference path does not resolve to an existing declared resource or property. |
ERR_TYPE_MISMATCH |
Semantic / Runtime | Provided value or expression type does not match target property type. |
ERR_CYCLIC_DEPENDENCY |
Semantic | Directed cycle detected in bindings or reference dependencies. |
ERR_OUT_OF_BOUNDS |
Semantic / Runtime | Constant or default value violates min/max clamps. |
ERR_INVALID_OPERATOR |
Semantic | ValueSpec op or ConditionSpec op is not in the recognized operator set. |
ERR_INVALID_ARITY |
Semantic | ValueSpec args array length does not match operator requirement. |
ERR_INVALID_DURATION |
Structural / Semantic | Duration string violates single-unit regex or contains negative values. |
8. Shared value resolution
For each supported target, evaluate:
base -> binding -> automation -> winning override -> modulation -> safety clamp
Skip stages not exposed by the target contract. A target cannot accept automation or modulation merely because it is numeric. Additive modulation is summed only where explicitly supported.
Parameters store user configuration separately from exhibit defaults. State stores simulation values separately from parameters. A property may obtain its base through ValueSpec. State is not an implicit layer overwriting every parameter.
References ordinarily read resolved values. Parameter controls read and edit stored user values, and display an override indicator when appropriate. Underlying bindings and automation continue to evaluate while masked by an override.
Override lifetime and priority are independent. Use explicit priority when supported, otherwise inherit the originating scenario's priority; equal priorities resolve by activation order. Duration scope confers no additional priority. The complete action contract must define non-scenario default priority, permitted explicit priority fields, and ordering identifiers before implementation.
On release, blend toward the current lower resolved value rather than a snapshot taken when the override started. User edits and changing bindings remain visible to that lower evaluation. Numeric release interpolation, interruptions by another override, and nonnumeric release behavior require exact contracts below.
Reject conflicting ordinary bindings and dependency cycles that cannot be evaluated under documented semantics. Do not introduce an implicit previous-frame delay to make a cycle appear legal.
Required resolution examples
| Case | Expected behavior |
|---|---|
| Bus gain is bound to activity, then directly overridden | The override supplies the pre-modulation value until release; the binding continues underneath |
| Activity is overridden and referenced by a binding | The binding observes resolved activity |
| User edits stored activity while its override is active | The stored edit persists; release approaches the updated lower value |
| Two overrides compete | Higher priority wins; equal priority uses activation order |
| Duration and scenario overrides compete | Priority and activation order decide, not scope |
| A target has legal additive modulation | Modulation follows the winning override, then the safety clamp applies |
9. Time and random evaluation
The initial logical simulation step is 1/60 second. Rendering does not own simulation time. Audio scheduling maps logical time to the audio clock with a bounded horizon.
Application pause and document visibility loss suspend logical progression and audio. Resume continues the same logical performance without a wall-clock catch-up burst. Visibility resume does not clear a user pause. Audio unlock does not replay expired sound invocations.
Random and weighted-choice ValueSpecs are sampled at the containing object's documented instantiation or invocation boundary, not every render frame. Evolving randomness belongs to modulators. Nested ValueSpec evaluation boundaries and random time sampling must be specified per construct.
Separate random streams isolate cadence, scenario instances, visual systems, sound instances, and manual sampling. Reproducibility is scoped to a runtime version, recorded numeric seed, and logical input sequence. Tests that use external or analysed signals must supply deterministic input traces.
10. Ownership and termination
Ownership propagates through nested events and actions. Scenario resources inherit the scenario owner unless the resource type allows an explicit persistent owner. Scenario-created duration overrides cannot outlive the owner, apart from their bounded release cleanup.
Persistent set changes survive scenario failure. Termination cancels future work and guarantees cleanup, including when a termination hook fails. Release work transfers to a bounded cleanup owner and ultimately disposes all temporary resources.
Condition triggers require a false condition before rearming after a successful firing. Each scenario definition may hold at most one deferred start request. Requests expire and recheck eligibility when dispatched. Exact timeout and ordering contracts remain required.
Statically check event/scenario feedback where possible and bound runtime dispatch. Resource cleanup must remain possible after the ordinary dispatch budget is exhausted.
11. Contract completion register
Complete shared contracts before implementing dependent subsystems. Use PRD section numbers as stable lookup references.
| Contract | Existing PRD input | Required completion | Status |
|---|---|---|---|
| Document/schema | 9-14, 113-116, 121 | Top-level shapes, unknown-field policy, identifier regex, reference paths, standard diagnostic codes | Complete (Rev 0.2) |
| Values and conditions | 15-21, 33 | Operator arity/table, division-by-zero protection, sampling timing boundaries, edge-trigger re-arming | Complete (Rev 0.2) |
| References and bindings | 13, 17, 31-32 | Target-capability table, instance/input scope, evaluation order, cycles, disabled bindings, exact smoothing | Planned (Phase 0 / GC3) |
| Actions and transitions | 22-30 | Fields and defaults per action, override priorities outside scenarios, target/command matrix, interrupted transitions, instance IDs | Planned (Phase 0 / GC3) |
| Audio | 34-60 | Complete recipe/component shapes, node defaults, automation timing, clamp implementation, one-shot endings and tails, unlock behavior, master protection contract | Subsystem contract (Phase 3) |
| Cadence | 61-68 | Exact selection/cooldown/overlap fields, clocks, pool collisions, pending audio, manual sampling isolation and continuous-sample termination | Subsystem contract (Phase 5) |
| Visuals | 69-89 | Primitive/system/behavior fields, angle and motion units, transform order, depth projection, field behavior, morph compatibility, effect approximation and limits | Subsystem contract (Phase 4) |
| Events/scenarios | 90-102 | Trigger shapes, hooks and failure ordering, scope inheritance, deferred ordering/expiry, relative/repeated timeline semantics and termination boundaries | Subsystem contract (Phase 6) |
| Generated UI | 103-107 | Widget compatibility, button actions, parameter validation and override display, group/control ordering | Subsystem contract (Phase 7) |
| Runtime/library | 108-112, 117-127 | Clock/audio synchronization and stalls, import equality, update compatibility, transactions, failure recovery, persistence schema | Subsystem contract (Phase 1/8) |
12. Contract template and conformance artifacts
Each construct must record its JSON shape; required and optional fields; types, units, ranges, and defaults; supported ValueSpec fields and evaluation timing; read/write namespaces; lifecycle and ownership; precedence; validation errors; runtime failure behavior; and resource costs or limits.
Supply a valid minimal example, a meaningful composition example, invalid cases with expected diagnostics, and expected semantic traces where timing or ordering matters. Two contrasting complete exhibits must exercise parameters, sound, visuals, and a temporary scenario override early in development.
Structural JSON Schema does not replace semantic validation. The internal schema and fixtures belong to 0.1 implementation work; public schema distribution and editor integration may follow later.