generated from Labyricorn/labyricorn-project-template
One commit for the work accumulated in the working tree since Step 6.3, which had never been split into per-step commits: - src/local-surfaces.js + src/surface-url.js (new); src/ui.js, src/validation.js, src/connection.js and public/index.html updated for local-surface hosting and generic surface rendering - tests: local-surfaces (20), scifi-surfaces (24) and postmessage-interop (7) new; connection/museum-gallery/surface-validation suites updated - reference exhibits: shared/contract-core.js defaults to Contract 5.3 (major 5, minor 3, xzbt 5.3); museum-gallery advertises its surface catalog; aquarium/haunted-house/planetarium adapters updated - SciFi-XZBT (Step 6.7A/6.7B): surface-mode.js + surface-bus.js, Observation-surface boot branch, local-change hooks, view.pillars / view.warp-flight targets; fixture byte-identical to G:/.vibe/SciFi-XZBT - SciFi-XZBT contract adapter handshake fix: the inbound bridge filter no longer gates on an exact advisory xzbt value (Contract 5.3 §6.5), only on its presence/type, matching the host's own envelope validation; the adapter now advertises contract minor 3 / version 5.3.0, which it already implemented via the 5.3 surfaces field. Root cause of the five failing postmessage-interop tests (host hello was silently dropped). - docs: architecture 6.4 and 6.7A, reference 6.6 and 6.7; evidence logs; test-fixtures/PROVENANCE.md resync record Test results: NGN 154/154 (was 149/154); postmessage-interop 7/7 (was 2/7); SciFi contract harness 21/21, real-adapter suite 32/32. git diff --check clean for changed files; two pre-existing trailing-whitespace lines remain in test-fixtures/reference-exhibits/scifi/index.html, copied verbatim from the authoritative SciFi source. Step 6.7 live verification (browser Observation, packaged standalone) is still pending and is not claimed here.
1099 lines
24 KiB
Markdown
1099 lines
24 KiB
Markdown
# XZBT Exhibit Contract Specification
|
|
## Version 5.2
|
|
|
|
> **Historical:** Superseded, additively, by
|
|
> [Contract Specification 5.3](XZBT-Exhibit-Contract-Specification-v5.3.md),
|
|
> the current maintained contract. No normative requirement in this document
|
|
> was removed, weakened, or reinterpreted by 5.3; it is retained here for
|
|
> historical context, not as a second currently-maintained contract version.
|
|
> XZBT-NGN and its maintained reference exhibits target Contract 5.3.
|
|
|
|
**Status:** Proposed normative specification
|
|
**Document version:** 5.2
|
|
**Contract family:** XZBT Exhibit Contract
|
|
**Compatibility major:** 5
|
|
**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.2",
|
|
"type": "hello",
|
|
"requestId": "req-001",
|
|
"host": {
|
|
"name": "XZBT-NGN",
|
|
"version": "0.1.0"
|
|
},
|
|
"supportedContractMajors": [5]
|
|
}
|
|
```
|
|
|
|
### 5.2 Exhibit hello response
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"type": "hello.result",
|
|
"requestId": "req-001",
|
|
"sessionId": "sess-7f2a",
|
|
"exhibit": {
|
|
"product": "scifi-xzbt",
|
|
"version": "implementation-defined",
|
|
"build": "implementation-defined"
|
|
},
|
|
"contract": {
|
|
"major": 5,
|
|
"minor": 2
|
|
}
|
|
}
|
|
```
|
|
|
|
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.
|
|
|
|
---
|
|
|
|
## 6. Message Envelope
|
|
|
|
Every request, response, and event transmitted through the contract MUST use a normalized envelope.
|
|
|
|
### 6.1 Request
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"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.2",
|
|
"type": "invoke.result",
|
|
"requestId": "req-1042",
|
|
"sessionId": "sess-7f2a",
|
|
"ok": true
|
|
}
|
|
```
|
|
|
|
### 6.3 Error response
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"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.2",
|
|
"type": "describe",
|
|
"requestId": "req-010",
|
|
"sessionId": "sess-7f2a"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"type": "describe.result",
|
|
"requestId": "req-010",
|
|
"sessionId": "sess-7f2a",
|
|
"exhibit": {
|
|
"product": "scifi-xzbt",
|
|
"version": "implementation-defined",
|
|
"build": "implementation-defined"
|
|
},
|
|
"contract": {
|
|
"major": 5,
|
|
"minor": 2
|
|
},
|
|
"registryRevision": 1,
|
|
"stateRevision": 27,
|
|
"capabilities": [],
|
|
"targets": []
|
|
}
|
|
```
|
|
|
|
`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.
|
|
|
|
---
|
|
|
|
## 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-]*)+
|
|
```
|
|
|
|
### 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.
|
|
|
|
### 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.2",
|
|
"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.2",
|
|
"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.2",
|
|
"type": "state.get",
|
|
"requestId": "req-300",
|
|
"sessionId": "sess-7f2a"
|
|
}
|
|
```
|
|
|
|
Response:
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"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.
|
|
|
|
---
|
|
|
|
## 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`.
|
|
|
|
### 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.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## 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.2",
|
|
"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.2",
|
|
"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.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## 18. Speech
|
|
|
|
An exhibit with speech support SHOULD expose:
|
|
|
|
```text
|
|
speech.say
|
|
```
|
|
|
|
Example:
|
|
|
|
```json
|
|
{
|
|
"xzbt": "5.2",
|
|
"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.
|
|
|
|
---
|
|
|
|
## 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.2",
|
|
"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.
|
|
|
|
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.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## 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.
|
|
|
|
---
|
|
|
|
## 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.**
|