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.
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
postMessageto a specificwindowreference, which breaks if the owner window reference is lost or the surface was opened independently rather than viawindow.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, andinfo-wall.htmlfully 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 intocore.applyMutation/core.invokeActionwithsource: '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.