generated from Labyricorn/labyricorn-project-template
1092 lines
24 KiB
Markdown
1092 lines
24 KiB
Markdown
# XZBT Exhibit Contract Specification
|
|
## Version 5.2
|
|
|
|
**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.**
|