generated from Labyricorn/labyricorn-project-template
122 lines
5.8 KiB
Markdown
122 lines
5.8 KiB
Markdown
# Museum Gallery — Step 6.2 multi-surface reference exhibit
|
||
|
||
A deliberately small XZBT Contract 5.3 exhibit built to prove the
|
||
multi-surface presentation model from
|
||
`docs/architecture/XZBT-Multi-Surface-Model-Step6.1.md` (Revision 2) and
|
||
`docs/contract/XZBT-Exhibit-Contract-Specification-v5.3.md` Section 31 — not
|
||
to be visually elaborate. See
|
||
`docs/reference/Museum-Gallery-Step6.2-Verification.md` for the full
|
||
verification writeup; this file documents the exhibit's own structure and
|
||
its chosen internal attachment transport.
|
||
|
||
## Entry point
|
||
|
||
`control.html` is the primary surface (`surface.control`, `primary: true`)
|
||
and the exhibit's canonical entry point — open it directly for full
|
||
standalone operation, exactly like any other reference exhibit's
|
||
`index.html`. `index.html` in this directory is a plain redirect to
|
||
`control.html`, kept only for directory-listing consistency; it has no
|
||
behavior of its own.
|
||
|
||
## Surfaces
|
||
|
||
| id | label | primary | url | role |
|
||
| --- | --- | --- | --- | --- |
|
||
| `surface.control` | Control Room | `true` | `control.html` | control |
|
||
| `surface.artifact` | Artifact Display | `false` | `artifact.html` | ambient |
|
||
| `surface.info-wall` | Information Wall | `false` | `info-wall.html` | information |
|
||
|
||
## Exactly one Exhibit State Core
|
||
|
||
`control.html` is the only document that loads `contract-adapter.js` (and
|
||
therefore the only document that constructs an `XZBTContractCore.ContractCore`
|
||
instance). `artifact.html` and `info-wall.html` load only `surface-bus.js` —
|
||
they are structurally incapable of constructing a second Core; the shared
|
||
`tests/museum-gallery.test.js` suite asserts this directly (see "Exactly one
|
||
Exhibit State Core exists" in that file).
|
||
|
||
## Chosen internal attachment transport: `BroadcastChannel`
|
||
|
||
`surface-bus.js` implements the generic attachment sequence from Step 6.1
|
||
§7.2 using a same-origin `BroadcastChannel` named `xzbt-museum-gallery-core-v1`,
|
||
with `control.html` as the single, clearly identified authoritative owner
|
||
document (Contract 5.3 §31.7's "cross-document attachment" shape).
|
||
|
||
Why `BroadcastChannel` over the other two permitted shapes:
|
||
|
||
- it needs no window-handle bookkeeping (unlike `postMessage` to a specific
|
||
`window` reference, which breaks if the owner window reference is lost or
|
||
the surface was opened independently rather than via `window.open`);
|
||
- it needs no separate worker lifecycle (unlike `SharedWorker`, which is
|
||
unsupported in some embedding contexts and adds a process to reason
|
||
about for a reference fixture this small);
|
||
- every participant — owner or surface — only ever needs to know one
|
||
string (the channel name), which keeps `control.html`, `artifact.html`,
|
||
and `info-wall.html` fully decoupled from each other; none of them
|
||
reference the other documents by name or handle.
|
||
|
||
This transport is exhibit-internal. It carries no XZBT contract envelope, is
|
||
never observed by a host, and is not mentioned anywhere in Contract 5.3 —
|
||
per Step 6.1 §7.2 and Contract 5.3 §31.7/§31.9, the contract only needs to
|
||
know that surfaces exist and how a host opens one. A different exhibit is
|
||
free to choose `SharedWorker`, in-process attachment, or another same-origin
|
||
mechanism entirely.
|
||
|
||
### Message shapes (informal, exhibit-internal only)
|
||
|
||
- surface → owner: `{ type: 'attach', requestId }`
|
||
- owner → surface: `{ type: 'attach.snapshot', inReplyTo, snapshot, registryRevision }`
|
||
- owner → all: `{ type: 'core-event', event }` — one relayed copy of every
|
||
normalized event the Core already emits (`state.changed`,
|
||
`selection.changed`, `action.executed`, …)
|
||
- surface → owner: `{ type: 'mutate', kind: 'set'|'invoke', target, value|args }`
|
||
— routed straight into `core.applyMutation` / `core.invokeAction` with
|
||
`source: 'ui'`, the same chokepoint the primary UI's own controls use
|
||
- surface → owner: `{ type: 'detach' }` — bookkeeping only; never mutates
|
||
state
|
||
|
||
## Shared state
|
||
|
||
`artifact.selected` (selection), `lighting.level` (range 0–1),
|
||
`rotation.speed` (range 0–2), `labels.enabled` (state/boolean), plus one
|
||
impulse, `action.spotlight-flash`, to prove `action.executed` propagation
|
||
across surfaces.
|
||
|
||
## Native interactions proving the canonical mutation path
|
||
|
||
- Artifact Display's "Cycle artifact" button calls `link.mutate('set',
|
||
'artifact.selected', …)`.
|
||
- Information Wall's "Toggle labels" button calls `link.mutate('set',
|
||
'labels.enabled', …)`.
|
||
|
||
Both are relayed by the bus into `core.applyMutation` on the one Core that
|
||
`control.html` owns — the same call the Control Room's own controls and a
|
||
contract `set` from NGN would make. See the verification document for the
|
||
event-sequence and stateRevision proof.
|
||
|
||
## Running it directly (no test harness)
|
||
|
||
Serve this repository with `npm start` and open:
|
||
|
||
```
|
||
http://127.0.0.1:4173/test-fixtures/reference-exhibits/museum-gallery/control.html
|
||
```
|
||
|
||
Then open `artifact.html` and `info-wall.html` in separate tabs/windows from
|
||
the same origin. Changing state in any one window updates the other two.
|
||
Closing and reopening a non-primary surface reflects current state
|
||
immediately. Opening `artifact.html` or `info-wall.html` alone, with
|
||
`control.html` not open anywhere, shows a "Waiting for the Control Room
|
||
surface to be open…" state rather than inventing its own state.
|
||
|
||
## Known limitation: NGN attachment is not exercised in Step 6.2
|
||
|
||
`control.html` includes the optional `XZBTHostTransport`, matching every
|
||
other reference exhibit. Because Museum Gallery declares `xzbtVersion:
|
||
'5.3'` while the current (pre-6.3) NGN host always sends the envelope tag
|
||
`xzbt: '5.2'`, an actual attach attempt from today's NGN would be rejected
|
||
at the envelope-version check rather than negotiating a Contract-major-5
|
||
session. This is expected and intentional: NGN's generic surface discovery
|
||
and version negotiation are Phase 6.3+ work, explicitly out of scope for
|
||
Step 6.2. See the verification document's known-limitations section.
|