Files
XZBT-NGN/docs/contract/XZBT-Exhibit-Contract-Specification-v5.3.md
T

1448 lines
41 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# XZBT Exhibit Contract Specification
## Version 5.3
**Status:** Proposed normative specification
**Document version:** 5.3
**Contract family:** XZBT Exhibit Contract
**Compatibility major:** 5
**Supersedes:** Version 5.2, additively. No existing normative requirement
from 5.2 has been removed, weakened, or reinterpreted. Contract 5.3 adds
presentation-surface provisions and surface-related cross-references as
documented in Section 39.
**Primary consumers:** XZBT-compatible exhibits and XZBT-NGN Exhibit Engine
---
## 1. Purpose
The XZBT Exhibit Contract defines a stable, discoverable language through which an external host can inspect, observe, and control an XZBT-compatible exhibit.
The architecture is deliberately separated into three concerns:
- **Exhibit:** presentation, native controls, audio, visuals, simulation, and exhibit-specific behavior.
- **Contract:** the shared control and observation language.
- **Exhibit Engine:** orchestration, recording, integration, networking, authoring, packaging, and administration.
The contract does not require XZBT-NGN to exist for an exhibit to operate.
An exhibit implementing this contract remains a complete independently usable artifact.
---
## 2. Governing Architectural Rule
An XZBT-compatible exhibit MUST remain operable without XZBT-NGN.
Connection to XZBT-NGN is additive.
The exhibit exposes addressable capabilities and state.
XZBT-NGN decides how to coordinate, automate, integrate, record, schedule, and package those capabilities.
The contract MUST NOT be expanded merely to move Exhibit Engine responsibilities into the exhibit.
---
## 3. Roles
### 3.1 Exhibit responsibilities
An exhibit owns:
- visual presentation;
- audio presentation;
- simulation and ambience;
- native UI;
- local input handling;
- internal state;
- exhibit-specific actions;
- standalone behavior;
- exhibit-native speech rendering;
- exhibit-native sound rendering;
- exhibit-native visual effects.
### 3.2 Exhibit Engine responsibilities
XZBT-NGN may:
- discover an exhibit;
- inspect capabilities and targets;
- request state;
- invoke actions;
- set exposed values;
- observe normalized events;
- record operator activity;
- execute scenarios;
- author timelines;
- expose webhooks;
- manage authentication and authorization;
- connect external systems;
- map telemetry;
- package authored experiences;
- administer one or more exhibits.
---
## 4. Transport Independence
The contract defines semantics and normalized message envelopes, not one mandatory transport.
Permitted transports may include:
- same-origin `postMessage`;
- trusted wrapper bindings;
- local WebSocket transport;
- local application IPC;
- future host-specific bindings.
The exhibit MUST NOT need to know whether a request originated from a webhook, Streamer.bot, simulator, telemetry source, administrative UI, scenario engine, or other integration.
NGN translates external sources into contract operations.
---
## 5. Session and Handshake
A host MUST establish a contract session before sending control operations through a negotiated transport.
A standalone exhibit MUST NOT depend on this handshake for normal operation.
`hello` is the only contract message that MAY be sent without a `sessionId`.
Every other request MUST carry a valid `sessionId` issued by the exhibit. Missing or unknown session identifiers MUST be rejected with `INVALID_SESSION`.
### 5.1 Host hello
```json
{
"xzbt": "5.3",
"type": "hello",
"requestId": "req-001",
"host": {
"name": "XZBT-NGN",
"version": "0.1.0"
},
"supportedContractMajors": [5]
}
```
### 5.2 Exhibit hello response
```json
{
"xzbt": "5.3",
"type": "hello.result",
"requestId": "req-001",
"sessionId": "sess-7f2a",
"exhibit": {
"product": "scifi-xzbt",
"version": "implementation-defined",
"build": "implementation-defined"
},
"contract": {
"major": 5,
"minor": 3
}
}
```
If no compatible major version exists, the exhibit MUST respond with an `error` carrying code `UNSUPPORTED_VERSION`, MUST echo the `requestId` when recoverable, and MUST NOT create a session.
A 5.2-only exhibit reporting `{"major": 5, "minor": 2}` remains fully compatible with a 5.3-aware host under the ordinary contract-major compatibility rule (Section 28.1); nothing in this handshake requires either party to implement 5.3-specific behavior.
---
## 6. Message Envelope
Every request, response, and event transmitted through the contract MUST use a normalized envelope.
### 6.1 Request
```json
{
"xzbt": "5.3",
"type": "invoke",
"requestId": "req-1042",
"sessionId": "sess-7f2a",
"target": "sfx.console-beep",
"args": {}
}
```
A `set` request MAY additionally contain an `options` object when the target explicitly declares supported options.
### 6.2 Response
```json
{
"xzbt": "5.3",
"type": "invoke.result",
"requestId": "req-1042",
"sessionId": "sess-7f2a",
"ok": true
}
```
### 6.3 Error response
```json
{
"xzbt": "5.3",
"type": "error",
"requestId": "req-1042",
"sessionId": "sess-7f2a",
"ok": false,
"error": {
"code": "UNKNOWN_TARGET",
"message": "The requested target is not registered."
}
}
```
When a malformed message cannot be parsed well enough to recover `requestId` or `sessionId`, either field MAY be `null`.
### 6.4 Correlation
`requestId` MUST be unique within a host session.
Responses MUST echo the corresponding `requestId`.
Events are asynchronous and do not require a `requestId`, but SHOULD include a `correlationId` when directly caused by a specific request.
### 6.5 Version field
The `xzbt` field is advisory metadata for diagnostics.
The negotiated session contract major/minor is authoritative after handshake.
---
## 7. Describe
The host MUST be able to request a complete machine-readable description.
Request:
```json
{
"xzbt": "5.3",
"type": "describe",
"requestId": "req-010",
"sessionId": "sess-7f2a"
}
```
Response:
```json
{
"xzbt": "5.3",
"type": "describe.result",
"requestId": "req-010",
"sessionId": "sess-7f2a",
"exhibit": {
"product": "scifi-xzbt",
"version": "implementation-defined",
"build": "implementation-defined"
},
"contract": {
"major": 5,
"minor": 3
},
"registryRevision": 1,
"stateRevision": 27,
"capabilities": [],
"targets": [],
"surfaces": []
}
```
`describe` is authoritative for the current session.
The `capabilities` array is a snapshot of current capability states at the time of the response.
NGN MUST discover targets instead of assuming that an exhibit exposes a fixed science-fiction vocabulary.
**`surfaces` (introduced in Contract 5.3).** An OPTIONAL array of presentation
surface descriptors, normatively defined in Section 31. Its absence, or an
empty array, both mean the exhibit has not adopted multi-surface presentation
and is functionally equivalent to a Contract 5.2 `describe.result`, which
never contained this field. A 5.3-aware host MUST treat a `describe.result`
lacking `surfaces` identically to one where `surfaces` is present and empty
(Section 31.5).
---
## 8. Canonical Target IDs
External contract target IDs use a canonical dotted namespace.
Examples:
```text
transport.playing
transport.muted
mix.master
view.observation
view.viewport-frame
event.alert-red
alert.active
sfx.console-beep
speech.say
telemetry.engine.temperature
```
### 8.1 Grammar
A target ID MUST:
- use lowercase ASCII letters, digits, hyphens, and dots;
- begin with a lowercase letter;
- contain one or more dot-separated segments;
- not contain whitespace;
- not contain empty segments;
- not be interpreted as a JavaScript property path.
Recommended grammar:
```text
[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+
```
This grammar also governs presentation surface identifiers (Section 31.2);
no separate identifier grammar is defined for surfaces.
### 8.2 Stability
Once published as part of an exhibit's external contract, a canonical target ID becomes part of that exhibit's compatibility surface.
Target IDs SHOULD NOT be renamed casually.
Deprecated targets SHOULD remain discoverable long enough for hosts to migrate.
### 8.3 Internal mapping and normalization
An exhibit MAY map canonical external IDs to existing internal IDs.
Example:
```text
mix.master -> master-vol
```
Mapping is not always a rename.
Example:
```text
transport.playing -> absolute-state adapter around existing transport logic
```
An exhibit MUST NOT implement an externally visible persistent state target merely by blindly toggling an existing control.
A conforming exhibit may need to build a normalization layer before its existing internals can satisfy the contract.
---
## 9. Target Descriptor
Each target returned by `describe` MUST include enough metadata for a generic host to inspect and operate it.
Base descriptor:
```json
{
"id": "mix.master",
"label": "Master Volume",
"kind": "range",
"readable": true,
"writable": true,
"restorable": true,
"category": "mix",
"unit": null,
"requires": ["audio"]
}
```
`requires` references capability IDs from Section 17.
When a required capability is not usable, the target remains discoverable but MUST NOT be operated successfully. A generic host SHOULD present it as unavailable rather than removing it.
Additional fields depend on kind.
### 9.1 Range
```json
{
"id": "mix.master",
"kind": "range",
"min": 0,
"max": 1,
"step": 0.01
}
```
### 9.2 State
```json
{
"id": "view.observation",
"kind": "state",
"valueType": "boolean"
}
```
### 9.3 Selection
```json
{
"id": "universe.selected",
"kind": "selection",
"options": [
{"value": "starfleet", "label": "Starflight Command"},
{"value": "whataverse", "label": "Whataverse"}
]
}
```
Values in examples are illustrative except where a profile document explicitly declares them normative.
### 9.4 Impulse
```json
{
"id": "event.alert-red",
"kind": "impulse",
"readable": false,
"writable": false,
"restorable": false
}
```
A target with `kind: "impulse"` is invokable.
`kind` is authoritative for invokability. No separate `invokable` field is defined.
Presentation surfaces (Section 31) are a structurally distinct descriptor
family from targets and are never expressed using any target `kind`,
including `impulse`. A host MUST NOT infer surface existence from any target
descriptor.
### 9.5 Impulse arguments (authoritative Contract 5.2 clarification)
Impulse descriptors use `arguments`, an array of argument descriptors. This
clarifies Contract 5.2; it does not introduce Contract 5.3. Missing `arguments`
MUST be tolerated as an empty array for backward compatibility. New 5.2 exhibits
SHOULD explicitly emit `arguments: []` for argumentless impulses.
```json
{
"id": "speech.say",
"kind": "impulse",
"readable": false,
"writable": false,
"restorable": false,
"category": "speech",
"requires": ["speech"],
"arguments": [
{"name": "text", "type": "string", "required": true, "maxLength": 2000}
]
}
```
Each argument descriptor has:
| Field | Requirement |
| --- | --- |
| `name` | REQUIRED string, unique within the target |
| `type` | REQUIRED: `string`, `number`, `integer`, or `boolean` |
| `required` | REQUIRED boolean |
| `label` | OPTIONAL human-readable string |
| `description` | OPTIONAL human-readable string |
| `enum` | OPTIONAL array of allowed literal values |
| `min`, `max` | OPTIONAL number |
| `step` | OPTIONAL positive number |
| `minLength`, `maxLength` | OPTIONAL non-negative integer |
Invoke `args` MUST be an object. Every argument with `required: true` MUST be
present. Undeclared keys MUST be rejected with `INVALID_VALUE`. Values MUST match
the declared type, and all declared enum, bounds, step, and length constraints
MUST be enforced.
NGN MAY generate controls directly from this metadata and MUST NOT infer
undocumented arguments. NGN MUST still surface an impulse with an unsupported
future argument type and report its schema as unsupported rather than inventing
behavior.
Established examples: `speech.say` declares required string `text` with
`maxLength: 2000`; the established impulse form of `display.ticker` declares
required string `text` with `maxLength: 512`. Argumentless actions, including
`speech.generate-announcement` and argumentless event/sound impulses, declare
`arguments: []`. These examples do not establish a host target inventory.
---
## 10. Target Kinds
The base contract defines four kinds.
### 10.1 State
Persistent value such as boolean, string, or bounded scalar.
### 10.2 Range
Persistent bounded numeric state.
### 10.3 Selection
Persistent value selected from a declared set.
### 10.4 Impulse
Non-persistent action or event.
Impulse targets MUST NOT appear in a restorable state snapshot as current state.
---
## 11. Invoke
`invoke` performs an impulse.
```json
{
"xzbt": "5.3",
"type": "invoke",
"requestId": "req-200",
"sessionId": "sess-7f2a",
"target": "sfx.console-beep",
"args": {}
}
```
The exhibit MUST validate the target and arguments before execution.
Arguments MUST be validated against the declared target schema where such a schema exists.
Unexpected argument keys SHOULD be rejected.
A successful `invoke` response means that the operation was accepted for execution, not necessarily that a long-running effect has completed.
---
## 12. Set
`set` changes writable persistent state.
```json
{
"xzbt": "5.3",
"type": "set",
"requestId": "req-201",
"sessionId": "sess-7f2a",
"target": "mix.master",
"value": 0.75
}
```
A successful state-changing set MUST result in a normalized state update.
A successful no-op set MUST NOT increment `stateRevision`.
---
## 13. State Snapshot
The host MUST be able to request externally meaningful persistent state.
Request:
```json
{
"xzbt": "5.3",
"type": "state.get",
"requestId": "req-300",
"sessionId": "sess-7f2a"
}
```
Response:
```json
{
"xzbt": "5.3",
"type": "state.result",
"requestId": "req-300",
"sessionId": "sess-7f2a",
"stateRevision": 28,
"values": {
"transport.playing": true,
"transport.muted": false,
"mix.master": 0.75,
"view.observation": true,
"universe.selected": "starfleet",
"preset.selected": "tng-bridge"
}
}
```
Only readable persistent targets belong in `values`.
A state snapshot MUST NOT imply that every internal exhibit variable is externally exposed.
State exposed through `values` is scoped to the one logical exhibit instance
and its one session, regardless of how many presentation surfaces (Section
31) are currently open. `state.get` MUST NOT be parameterized by surface, and
no surface-specific state view is defined by this contract.
---
## 14. State Revision and Mutation Transactions
The exhibit MUST maintain a monotonically increasing `stateRevision` for externally observable persistent state.
### 14.1 Mutation transaction
One top-level contract-visible mutation operation is one **mutation transaction**, unless the exhibit explicitly opens a larger transaction for a coherent multi-value operation.
Examples of one transaction may include:
- one host `set`;
- one UI slider change;
- one MIDI mutation;
- one universe change that also selects a default preset;
- one preset selection that updates several exposed values.
All contract-visible state changes committed by one transaction share one resulting `stateRevision`.
A native interaction originating on any presentation surface (Section 31) is
one mutation transaction, subject to this same rule, regardless of which
surface it originated on.
### 14.2 Ordering
The exhibit MUST:
1. compute and commit the transaction;
2. increment `stateRevision` exactly once if externally visible state changed;
3. emit resulting `state.changed` / `selection.changed` events carrying that revision.
### 14.3 No-op behavior
A transaction that produces no externally visible state change MUST NOT increment `stateRevision`.
### 14.4 Why both revision and sequence exist
`stateRevision` tracks committed persistent-state history.
Event `sequence` tracks delivery order of all emitted contract events, including impulses and errors.
The counters serve different purposes and MUST NOT be treated as interchangeable.
Multi-surface presentation does not introduce a second revision or sequence
counter of any kind. Every presentation surface of one exhibit instance
observes the same `stateRevision` history and the same event `sequence`
stream defined here.
---
## 15. Source and Origin
Normalized events MUST identify the authoritative source of the action when known.
Base values:
```text
ui
midi
hotkey
host
scenario
internal
system
```
A `source` property supplied by a host command MUST be ignored.
The receiving bridge assigns the authoritative source.
This distinction is required for recording and feedback-loop prevention.
An interaction originating on a presentation surface other than the primary
surface (Section 31.3) uses the same `source` vocabulary as an interaction on
the primary surface — typically `ui`. This contract does not define a
per-surface source value; which surface an interaction originated on is not
contract-visible.
---
## 16. Events
A conforming XZBT 5.2 exhibit MUST publish normalized events for contract-visible operations.
Base event types include:
```text
state.changed
action.executed
selection.changed
capability.changed
registry.changed
error
```
Example:
```json
{
"xzbt": "5.3",
"type": "state.changed",
"sessionId": "sess-7f2a",
"sequence": 144,
"timestamp": 1799781200123,
"stateRevision": 29,
"target": "mix.master",
"value": 0.82,
"source": "midi"
}
```
Impulse example:
```json
{
"xzbt": "5.3",
"type": "action.executed",
"sessionId": "sess-7f2a",
"sequence": 145,
"timestamp": 1799781200234,
"target": "sfx.console-beep",
"args": {},
"source": "ui"
}
```
### 16.1 Sequence
Event `sequence` MUST increase monotonically within one session.
It is session-scoped and resets when a new `sessionId` is issued.
A host MUST reset its expected sequence after reconnect.
### 16.2 Correlation
When an event is caused directly by a host request, it SHOULD include the request's `requestId` as `correlationId`.
### 16.3 Selection events
A successful change to a Selection-kind target SHOULD emit `selection.changed`.
### 16.4 Errors
Request-scoped errors MUST be returned as responses.
An exhibit MAY additionally publish an `error` event for observability.
If published, it SHOULD carry the same source and `correlationId` as the rejected request.
### 16.5 Recorder behavior
NGN recording SHOULD normally record operator-originated events such as `ui`, `midi`, and `hotkey`.
It SHOULD NOT automatically record its own `scenario` or host playback events back into the scenario being recorded unless explicitly configured.
### 16.6 `registry.changed` and surfaces (Contract 5.3)
`registry.changed` (Section 23) covers changes to `surfaces` in addition to
`targets` and capability metadata. A single `registryRevision` governs both;
this contract does not define a separate surface-registry revision. See
Section 31.6.
---
## 17. Capability Model
Capabilities are discoverable and stateful.
Descriptor example:
```json
{
"id": "speech",
"state": "available"
}
```
Base states:
```text
unsupported
available
loading
ready
busy
error
```
Definitions:
- `unsupported`: the exhibit does not implement the capability.
- `available`: implemented, but not necessarily initialized.
- `loading`: initialization is in progress.
- `ready`: usable now.
- `busy`: currently occupied but still operational.
- `error`: initialization or operation failed.
A capability may change state during a session.
Capability changes MUST emit `capability.changed`.
A host MUST treat the current discovered state as authoritative.
A presentation surface (Section 31) MAY declare `requires` against
capability IDs defined here, using identical semantics to target `requires`
(Section 9).
---
## 18. Speech
An exhibit with speech support SHOULD expose:
```text
speech.say
```
Example:
```json
{
"xzbt": "5.3",
"type": "invoke",
"requestId": "req-410",
"sessionId": "sess-7f2a",
"target": "speech.say",
"args": {
"text": "Engineering reports reactor output at eighty-seven percent."
}
}
```
The exhibit controls synthesis and playback.
NGN supplies content.
The contract does not require a specific TTS engine or a speech queue.
If the exhibit has a usable fallback synthesis path while its preferred path is loading, it MAY accept `speech.say` through that fallback.
A successful `speech.say` response means accepted for playback, not playback completed.
---
## 19. Sound and Visual Events
Exhibit-native sound and visual actions SHOULD be exposed as impulse targets where useful.
Examples:
```text
sfx.console-beep
sfx.warning-tone
event.alert-red
event.ship-arrival
```
State and impulse semantics MUST remain distinct.
For example:
```text
event.alert-red
```
may be an impulse, while:
```text
alert.active
```
may represent current persistent alert state.
NGN requests the action.
The exhibit owns its implementation.
---
## 20. Safe Text Injection
An exhibit MAY expose safe text surfaces such as:
```text
display.ticker
```
Text MUST be handled as data.
The contract MUST NOT permit arbitrary:
- HTML;
- JavaScript;
- CSS;
- selectors;
- executable expressions.
Each text target SHOULD declare a maximum accepted length.
This restriction applies equally to any text rendered by a presentation
surface (Section 31); surfaces introduce no new text-injection surface area.
---
## 21. Telemetry
Telemetry support is OPTIONAL.
The contract defines how telemetry is represented if an exhibit exposes structured telemetry. It does not require every exhibit to implement telemetry.
A telemetry target descriptor SHOULD declare:
- identifier;
- label;
- value type;
- unit;
- minimum and maximum where applicable;
- writable status;
- update expectations if useful.
Example:
```json
{
"id": "telemetry.engine.temperature",
"label": "Engine Temperature",
"kind": "range",
"valueType": "number",
"unit": "celsius",
"readable": true,
"writable": true,
"restorable": false
}
```
Existing SciFi-XZBT telemetry-like presentation is primarily audio activity and ticker text rather than a structured numeric telemetry model.
Therefore structured writable telemetry in SciFi-XZBT is a new exhibit feature, not merely exposure of an existing data object.
It may be implemented incrementally and is not required for the first contract-compatible milestone.
---
## 22. Telemetry Ownership
Writable telemetry MAY support source ownership.
Recommended modes:
```text
simulation
external
```
An external update MAY include a lease duration:
```json
{
"xzbt": "5.3",
"type": "set",
"requestId": "req-520",
"sessionId": "sess-7f2a",
"target": "telemetry.engine.temperature",
"value": 482.7,
"options": {
"ownership": "external",
"ttlMs": 5000
}
}
```
After lease expiration, the exhibit MAY return to simulated ownership.
The exact telemetry lease mechanism remains optional in 5.2, unchanged in 5.3.
A simple explicit release mechanism is also acceptable.
---
## 23. Registry Revision
`registryRevision` represents the current set and metadata of discoverable targets.
It SHOULD increment when targets or descriptor metadata actually change during a session.
A `registry.changed` event MUST notify the host when the revision changes.
The host then re-runs `describe`.
Registry revision SHOULD NOT change merely because a fixed target becomes contextually unavailable.
As of Contract 5.3, `registryRevision` also governs the `surfaces` array
(Section 31.6). One counter covers both; this contract does not define an
independent surface-registry revision.
---
## 24. Error Codes
Implementations SHOULD use stable machine-readable error codes.
Recommended base codes include:
```text
UNSUPPORTED_VERSION
INVALID_MESSAGE
INVALID_SESSION
UNKNOWN_TARGET
INVALID_VALUE
INVALID_ARGUMENTS
CAPABILITY_UNAVAILABLE
TARGET_READ_ONLY
TARGET_NOT_INVOKABLE
TARGET_NOT_SETTABLE
INTERNAL_ERROR
```
Human-readable error text is advisory.
Hosts SHOULD branch on codes, not message strings.
---
## 25. Security Boundary
The contract MUST NOT provide arbitrary code execution.
Target IDs MUST NOT be interpreted as property traversal.
Contract payloads MUST NOT permit raw executable markup.
The exhibit contract MUST NOT provide a primitive for fetching arbitrary URLs.
External networking belongs to the host or to explicitly defined exhibit-native functionality.
Both host and exhibit MUST validate incoming messages.
Internet-facing authentication and authorization belong to XZBT-NGN, not the standalone exhibit.
Same-origin `postMessage` transports MUST validate both `event.origin` and `event.source` against the expected host relationship.
The same-origin requirement in this section extends to presentation surface
resolution and to any exhibit-internal attachment channel used between an
exhibit's own documents (Section 31.7). Neither introduces a new trust
boundary beyond the one already defined here.
---
## 26. Scenario Independence
The XZBT Exhibit Contract does not require a general-purpose scenario engine in the standalone exhibit.
A scenario is an orchestration concept.
XZBT-NGN may execute scenarios by issuing contract operations over time.
A separately packaged exhibit may contain:
- authored scenario data;
- a compact runtime;
- only the functionality necessary to execute that packaged scenario.
The ordinary standalone exhibit does not need to load arbitrary scenario files.
Scenario data MUST remain inert data and MUST NOT be interpreted as arbitrary executable code.
---
## 27. Extensibility
The base contract is exhibit-generic.
A castle exhibit could expose:
```text
weather.rain
fireplace.intensity
event.drawbridge
sfx.thunder
```
A train exhibit could expose:
```text
engine.throttle
brake.pressure
event.whistle
telemetry.speed
```
NGN discovers these targets dynamically.
---
## 28. Versioning
### 28.1 Contract major
Breaking protocol changes require a new contract major.
### 28.2 Contract minor
Backward-compatible additions may increase the minor version.
### 28.3 Exhibit version
Exhibit product version is independent from contract version.
### 28.4 Registry revision
Registry revision is runtime target metadata and does not imply a contract version change.
### 28.5 State revision
`stateRevision` is a runtime state-history counter and is not a protocol version.
### 28.6 Scenario format
Scenario format versioning is owned by XZBT-NGN and is outside the Exhibit Contract.
### 28.7 Additive targets
An exhibit may add targets without changing the contract version, provided existing target semantics remain compatible.
### 28.8 Contract 5.3 (presentation surfaces)
Contract 5.3 adds the optional `surfaces` field to `describe.result`
(Section 7) and the normative Presentation Surfaces model (Section 31), per
the rule in 28.2: this is a backward-compatible addition, not a breaking
change. Contract major remains 5. An exhibit reporting `{major: 5, minor: 2}`
is unaffected by this addition and remains fully conformant; a 5.3-aware host
MUST continue to interoperate with it exactly as under Contract 5.2 (Section
31.5).
---
## 29. Conformance Minimum
An exhibit is minimally XZBT Contract 5.2 compatible when it supports:
- compatible handshake;
- normalized message envelope;
- `describe`;
- at least one discoverable target;
- `state.get` for readable persistent targets;
- `stateRevision`;
- normalized responses;
- normalized event emission;
- session event sequencing;
- command validation;
- version reporting.
Speech, telemetry, MIDI, scenarios, Web3D, and any particular exhibit namespace are optional capabilities.
This conformance minimum is unchanged by Contract 5.3. Presentation surfaces
(Section 31) are an optional capability of the same kind: an exhibit that
implements none of Section 31 and never emits `surfaces` remains fully
conformant, at either contract minor.
---
## 30. Architectural Summary
Version 5.2 keeps the Version 5 architecture intact while tightening implementation semantics.
The key rule remains:
**The exhibit exposes what it can do.**
**The contract defines how that capability is described, observed, and invoked.**
**XZBT-NGN decides how to orchestrate and integrate it.**
Version 5.3 preserves this architecture without modification and adds exactly
one optional capability class — presentation surfaces (Section 31) — built
entirely on the existing session, state-revision, event, and registry-revision
mechanisms defined above.
---
## 31. Presentation Surfaces (introduced in Contract 5.3)
This section is new in Contract 5.3. It formalizes the multi-surface
presentation model approved in XZBT-NGN's Step 6.1 architecture document
(`docs/architecture/XZBT-Multi-Surface-Model-Step6.1.md`, Revision 2). Its
scope is narrowly the discovery and identification of presentation surfaces
and the contract-visible guarantees around them; it does not define display
transport, casting, or remote endpoints (explicitly out of scope — see
Section 31.9).
### 31.1 Definition
A **presentation surface** is a renderable, full-screen-capable visual view
of one logical exhibit instance, addressable by a stable identifier, that an
exhibit advertises as independently viewable. Every presentation surface of
one exhibit instance is driven by that exhibit's one authoritative
`stateRevision` history and one event `sequence` stream (Sections 13, 14,
16); this contract defines no mechanism by which two surfaces of the same
exhibit instance could observe divergent state.
A presentation surface is not a target (Section 9), not a capability
(Section 17), and not the host's own control/administration interface. It
MUST NOT be represented using any target `kind`.
### 31.2 Surface descriptor
When an exhibit advertises presentation surfaces, each entry in the
`surfaces` array (Section 7) MUST be an object with the following fields:
| Field | Requirement | Notes |
| --- | --- | --- |
| `id` | REQUIRED string | MUST conform to the canonical target-ID grammar (Section 8.1). No separate identifier grammar is defined for surfaces. |
| `label` | REQUIRED string | Human-readable. |
| `kind` | REQUIRED, constant `"surface"` | Identifies the descriptor type. |
| `primary` | REQUIRED boolean | Governed by Section 31.3. |
| `url` | REQUIRED string | Governed by Section 31.4. |
The following fields are OPTIONAL:
| Field | Purpose |
| --- | --- |
| `description` | Longer human-readable text. |
| `role` | Advisory grouping hint (e.g. `"control"`, `"ambient"`, `"information"`). No contract behavior depends on this field's value in Contract 5.3. |
| `aspectRatio` | Advisory sizing hint (e.g. `"16:9"`). |
| `category` | Mirrors target `category` (Section 9) for consistent grouping. |
| `requires` | Array of capability IDs (Section 17), with identical semantics to target `requires`: the surface remains discoverable but SHOULD be presented as degraded or unavailable when a required capability is not `ready`. |
No field of a surface descriptor MAY reference a display transport, casting
protocol, network endpoint, or device class (Section 31.9).
### 31.3 The `primary` invariant and validation order
`surfaces` (Section 7) has exactly three conformant top-level forms:
1. **Absent.** The exhibit does not advertise multi-surface presentation.
Functionally equivalent to a Contract 5.2 `describe.result`.
2. **Present and empty (`[]`).** MUST be treated identically to form 1 by a
conformant host.
3. **Present and non-empty.** Evaluated using the deterministic validation
order below.
A host MUST evaluate a non-empty `surfaces` array in the following order:
1. Validate each individual surface descriptor against Section 31.2 and
Section 31.4.
2. Discard individually invalid entries (Section 31.5); retain only the
valid entries as the working set.
3. Evaluate the `primary` invariant against the working set, not against the
original array:
- If the working set is empty (every entry was individually invalid),
treat `surfaces` as if it were absent (form 1).
- If the working set contains exactly one entry with `primary: true`,
that entry is the primary surface and the working set is the
conformant surface catalog for this `describe.result`.
- If the working set contains zero entries with `primary: true`, or more
than one, the surface catalog as a whole is malformed: the host MUST
reject the entire working set (not just the offending entries), fall
back to treating `surfaces` as absent (form 1), and SHOULD report a
diagnostic.
This order is intentional and normative: the `primary` invariant is always
evaluated after individually-invalid entries have already been discarded, so
that one malformed entry cannot be mistaken for a missing or duplicate
`primary` among otherwise-valid entries, and so that discarding a malformed
entry can deterministically change whether the invariant holds.
The primary surface, once determined by this process, is the surface a
Section-31-aware host that provides no operator surface selection opens by
default, and is the surface whose standalone-use guarantee is unconditional
(Section 31.8).
### 31.4 URL resolution
`url` MUST be one of:
- a path relative to the exhibit's own base document location — the same
base URL already used to establish the exhibit's session;
- such a relative path with an appended query string and/or fragment;
- a bare query string and/or fragment, resolving against the exhibit's own
base document, for a single-page exhibit whose surfaces are views within
one already-served document.
A host resolving `url` MUST:
1. resolve it against the exhibit's already-established base URL, not
against the host's own administrative interface location;
2. reject the entry (Section 31.5) if the resolved URL is not same-origin
with that base, per the same-origin requirement in Section 25;
3. apply the same path-containment validation the host already applies to
the exhibit's primary document, if any such validation exists in that
host implementation.
Absolute, protocol-relative, or cross-origin `url` values MUST be rejected as
malformed individual entries; they do not invalidate the rest of the array.
### 31.5 Individual entry validation
A host MUST validate each surface entry independently, as step 1 of the
order defined in Section 31.3. An entry missing a required field, using an
invalid `id` (Section 8.1), or specifying a `url` that fails Section 31.4 is
individually invalid; the host SHOULD skip only that entry, log a
diagnostic, and continue evaluating the remainder of `surfaces`. Individual
entry validation never by itself invalidates the whole array — only the
structural `primary` evaluation performed afterward, against the surviving
valid entries (Section 31.3, steps 23), can do that.
A host that does not implement any Section 31 behavior MAY safely ignore the
`surfaces` field entirely; doing so is fully conformant with the
`describe.result` schema, since the field is OPTIONAL (Section 7).
An exhibit implementing only Contract 5.2 behavior is unaffected: it never
emits `surfaces`, and no 5.3-only requirement applies to it.
### 31.6 Registry governance
`surfaces` is governed by `registryRevision` (Section 23) exactly as
`targets` is. There is no independent surface-registry counter. A
`registry.changed` event (Section 16.6) requires the host to re-run
`describe` and re-read both `targets` and `surfaces`.
### 31.7 State and interaction
All presentation surfaces of one exhibit instance share that instance's one
session, one `stateRevision` history, and one event `sequence` stream
(Sections 13, 14, 16); this contract defines no per-surface state channel
and no per-surface session. How an exhibit internally propagates state to
each surface's rendering code is an exhibit implementation detail outside
this contract's normative scope.
The normative requirement on interaction is narrower than "every native
interaction becomes a public target": any surface-originated interaction
that changes contract-visible persistent state or executes a contract-visible
action MUST obey the same mutation, revision, source, and event semantics
(Sections 14, 15, 16) as the equivalent operation originating from the
exhibit's primary UI or through a contract `set`/`invoke` (Section 14.1). A
surface-local interaction that does not change contract-visible state and
does not execute a contract-visible action — purely presentational
interaction confined to that surface — remains exhibit-internal and is
outside this contract's scope; this contract does not require it to have a
corresponding target.
An exhibit's internal mechanism for connecting a surface's rendering code to
its own state, including any same-origin channel used between the exhibit's
own documents, is not part of the message envelope defined in Section 6 and
is never observed by the host.
### 31.8 Standalone behavior
Per the governing rule in Section 2, an exhibit's `primary` surface (Section
31.3) — or, when `surfaces` is absent or empty, the exhibit's ordinary entry
point — MUST remain fully and unconditionally usable standalone, with no
dependency on XZBT-NGN, on a contract session, or on any other surface.
A non-primary surface SHOULD remain directly usable without XZBT-NGN. This
contract does not require every non-primary surface to be usable in complete
isolation from the exhibit's other documents; an exhibit MAY have a
non-primary surface depend on another of its own documents being present, as
an exhibit-internal implementation consequence of Section 31.7, provided that
dependency is never on XZBT-NGN itself.
### 31.9 Non-goals
This section defines discovery and identification of presentation surfaces
only. It does not define, and MUST NOT be extended by implementations to
imply:
- casting or remote display protocols;
- network display endpoints or device classes;
- an assignment mechanism between a surface and a physical or logical
display;
- any change to session, state, revision, or event semantics beyond the
cross-references added in Sections 7, 9.4, 13, 14.1, 14.4, 15, 16.6, 17,
20, 23, 25, and 29 of this document.
Those concerns are reserved for future contract or XZBT-NGN work and are
explicitly out of scope for Contract 5.3.
---
## 32. Version 5.3 Summary
Contract 5.3 is Contract 5.2 plus one optional, additive capability:
presentation surfaces (Section 31). No existing normative requirement is
weakened, removed, or reinterpreted. An exhibit or host that implements
nothing in Section 31 is unaffected and remains conformant. The architectural
rule from Section 30 is unchanged: the exhibit exposes what it can do, the
contract defines how that is described and invoked, and XZBT-NGN decides how
to orchestrate it — now including, optionally, orchestrating which of an
exhibit's several presentation surfaces is currently shown.
---
## 39. Changelog (5.2 → 5.3)
This section exists only in 5.3 and has no 5.2 counterpart.
**Added:**
- `surfaces` OPTIONAL field on `describe.result` (Section 7).
- Section 31, Presentation Surfaces: definition, descriptor schema, the
`primary` invariant and its three conformant forms, URL resolution,
validation/backward-compatibility rules, registry governance, the
state/interaction guarantee, standalone-use requirements, and explicit
non-goals.
- Section 28.8, recording the versioning rationale for this addition.
- Cross-reference sentences in Sections 5.2 (hello response commentary),
9.4, 13, 14.1, 14.4, 15, 16.6, 17, 20, 23, 25, 29, and 30, each noting how
the existing normative rule in that section extends to, or is unaffected
by, presentation surfaces. None of these cross-references change the
normative requirement already stated in 5.2 for that section.
**Changed:**
- Header metadata (document version, compatibility statement) — the
supersedes wording was tightened during the pre-6.2 cleanup pass to state
plainly that no existing 5.2 requirement was removed, weakened, or
reinterpreted, rather than characterizing which sections happened to
receive cross-references.
- Illustrative `"xzbt"` and `contract.minor` values in JSON examples updated
from `"5.2"` / `2` to `"5.3"` / `3` throughout, for internal consistency
within this document. This is cosmetic within the example payloads and
does not alter any example's normative meaning.
- Section 31.3 was restructured, during the same cleanup pass, into an
explicit, deterministic validation order (validate individual entries →
discard invalid ones → evaluate the `primary` invariant against the
surviving set) so that a discarded individually-invalid entry cannot leave
the `primary` determination ambiguous. Section 31.5 was narrowed to cover
only individual-entry validation and now cross-references 31.3 for the
structural `primary` evaluation, rather than restating it. This is a
clarification of Revision 2's intent, not a new rule.
- Section 31.7's interaction requirement was narrowed from "every successful
surface interaction behaves like `set`/`invoke`" to the intended scope:
this requirement applies only to interactions that change contract-visible
state or execute a contract-visible action. Purely surface-local,
non-contract-visible interaction is explicitly out of scope and is not
required to have a corresponding target.
- Section 31.3's reference to "a host with no surface-specific UI" was
reworded to "a Section-31-aware host that provides no operator surface
selection," to avoid it being read as covering a host that ignores Section
31 entirely.
**Removed:** nothing. No 5.2 requirement is weakened, deleted, or
reinterpreted by this document.
**Not changed:** Sections 14, 6, 8.28.3, 1012, 1819, 2122, 24, 2627,
28.128.7 carry no 5.3-specific content and are reproduced from 5.2
unmodified except for the cosmetic example-version updates noted above.