Steps 6.4-6.7B — Local surfaces, reference-exhibit validation, SciFi Observation surface

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.
This commit is contained in:
2026-09-14 19:45:27 -07:00
parent ed76cf6189
commit 745912e451
40 changed files with 5346 additions and 210 deletions
@@ -0,0 +1,256 @@
# XZBT-NGN Step 6.6 — Reference Exhibit End-to-End Validation
Status: PASS — no implementation changes were required.
Step 6.6 validates Museum Gallery end-to-end as a complete Contract 5.3 reference
exhibit operating through the normal NGN operator UI. It is a validation task: no
architecture was redesigned, no features were added, and no source file was modified.
## A. Environment
| Item | Value |
| --- | --- |
| NGN UI | `http://127.0.0.1:4173/` |
| Server | The already-running `npm start` server on port 4173 (started at 16:43, before this session). A second instance started for validation failed to bind with `EADDRINUSE` and exited, so the served state was unchanged and the running server was left untouched. |
| Exhibit under test | `http://127.0.0.1:4173/test-fixtures/reference-exhibits/museum-gallery/control.html` |
| Replacement exhibit | `http://127.0.0.1:4173/test-fixtures/reference-exhibits/scifi/index.html` |
| Working tree | Step 6.3/6.4/6.5 uncommitted work preserved as found; HEAD `ed76cf6` |
| Method | Real NGN UI in a browser, driven through the served page (no test doubles) |
## B. Initial connection
Observed through the served NGN page after `Load exhibit`:
- Status: `connected · synchronized`
- Session: `sess-1f54f`
- Negotiated contract: `{ major: 5, minor: 3 }`
- Exhibit metadata: `Museum Gallery · 0.1.0 · build reference-exhibit`
- `registryRevision` 1, `stateRevision` 0
- Capabilities: `render: ready`
- Target registry discovered (5):
| Target | Kind | Access |
| --- | --- | --- |
| `artifact.selected` | selection | readable · writable |
| `lighting.level` | range | readable · writable |
| `rotation.speed` | range | readable · writable |
| `labels.enabled` | state | readable · writable |
| `action.spotlight-flash` | impulse | not readable · not writable |
- State snapshot received: `artifact.selected="the-orrery"`, `lighting.level=0.6`,
`rotation.speed=0.4`, `labels.enabled=true`
- Protocol/error log: one `SESSION` entry, no errors
## C. Surface discovery and primary handling
`Presentation surfaces (3)` discovered, exactly the three the exhibit advertises:
| id | Label | Primary | Role | URL |
| --- | --- | --- | --- | --- |
| `surface.control` | Control Room | yes | control | `control.html` |
| `surface.artifact` | Artifact Display | no | ambient | `artifact.html` |
| `surface.info-wall` | Information Wall | no | information | `info-wall.html` |
Primary handling: the primary is not duplicated as a local pane. Its card carries the
`Primary` badge, shows `Open in the authoritative control frame. Use Connection controls
to reload or disconnect.`, and its Open/Reload/Close buttons are disabled. The two
non-primary surfaces open as local panes and each renders and reports
`Attached to Control Room.`
The Control Room's own `Attached non-primary surfaces` readout (the exhibit's participant
count) became `2` when both panes were open.
## D. Lifecycle validation
Measured participant count in the authoritative Control Room:
```text
open both: 2
reload: 2
reload again: 2
close one: 1
reopen: 2
```
Each reload produced a fresh secondary document (verified by a marker placed on the
frame's `contentWindow` disappearing after reload), which then re-attached and rendered
the current authoritative state. No ratcheting and no stale participant entries were
observed at any point.
## E. Shared-state validation
### Control -> secondaries
| Change made in Control Room | Observed propagation |
| --- | --- |
| Selected artifact -> Star Map | `selection.changed`, sequence 1, `stateRevision` 1, `source: "ui"`; both secondaries rendered `Star Map` |
| Lighting level -> 25% | `state.changed`, sequence 2, `stateRevision` 2, `source: "ui"`; Artifact Display rendered `Lighting: 25%` |
Control Room, Artifact Display, Information Wall and NGN's reported state all agreed
after each change.
### Secondary -> primary -> secondaries
A real mouse click on the Artifact Display's native `Cycle artifact` control while the
authoritative selection was `star-map`:
- Control Room's authoritative selection became `meteorite` (its combobox followed)
- `selection.changed`, sequence 3, `stateRevision` 3, `source: "ui"`
- Information Wall updated to `Meteorite Fragment`
- Artifact Display re-rendered from the authoritative result
The secondary remained a mirror: it holds no Core (see section H) and only routes
mutations back through the owner's canonical path.
### Late join
Information Wall closed (count 1) -> authoritative state mutated (rotation 1.7,
labels OFF, `stateRevision` 5) -> Information Wall reopened: it immediately rendered
`Meteorite Fragment` and `Labels: OFF`, and the count returned to 2.
### Reload recovery
Lighting mutated to 90% (`stateRevision` 6) -> Artifact Display reloaded: a fresh
document (marker gone) returned showing `Meteorite Fragment`, `Lighting: 90%`,
`Rotation: 1.7x` — current state, not exhibit defaults — with the count still 2.
### Sustained convergence
Four consecutive normal changes alternating between the primary and both secondaries
(control selection, Information Wall toggle, control rotation, Artifact Display cycle)
left Control Room, Artifact Display, Information Wall and NGN all in agreement at every
step, with revisions advancing 7 -> 8 -> 9 -> 10 and the count steady at 2.
## F. Host-command validation (NGN UI)
| Operation | NGN control used | Result |
| --- | --- | --- |
| Readable state query | `Refresh state` (`state.get`) | Succeeded; snapshot matched, `stateRevision` 10, status `connected · synchronized`, no error |
| `set` (range) | `lighting.level` -> `0.35` + `Set` | Succeeded; Control Room 35%, Artifact Display `Lighting: 35%`, `state.changed` revision 11, `source: "host"`, `correlationId: ngn-2-4` |
| `set` (selection) | `artifact.selected` -> Meteorite Fragment + `Set` | Succeeded; all surfaces `Meteorite Fragment`, `selection.changed` revision 12, `source: "host"`, `correlationId: ngn-2-6` |
| `invoke` (impulse) | `action.spotlight-flash` + `Invoke` | Succeeded; `action.executed` sequence 13, `source: "host"`, `correlationId: ngn-2-8`; exhibit `spotlightFlashCount` incremented; Control Room and both secondaries flashed `Spotlight flashed.` |
Each command left NGN's own state synchronized and produced no error entry. Instrumenting
the Artifact Display's announcement node with a DOM observer confirmed the host-initiated
`action.executed` event actually reached the secondary (it flashed, then returned to idle
after the announcer's 2.6 s window).
## G. Reconnect and replacement
### Disconnect / reconnect
- `Disconnect`: status `disconnected · not synchronized`; catalog reset to
`Connect an exhibit to discover its controls.`; surfaces reset to
`No presentation surfaces advertised.`; both local panes removed (0 surface iframes
remaining); control frame removed; reported state `{}`; no error entries.
- `Reconnect`: new session `sess-15cf1`, Contract 5.3, all three surfaces rediscovered
with correct primary handling, control frame re-established, participant count `0`
(fresh authoritative document), no stale panes or counts.
- Both secondaries reopened and attached to the new session (count 2), each rendering the
new session's current state.
### Exhibit replacement
- Museum -> SciFi (`scifi/index.html`): no Museum pane survived (0 iframes referencing
the Museum exhibit), the surface catalog became `No presentation surfaces advertised.`
(SciFi advertises none), and SciFi negotiated Contract 5.3 as
`SciFi-XZBT · 5.2.0 · build production`, session `sess-2akzu3a7`,
`connected · synchronized`, 61 targets, state snapshot received.
- SciFi -> Museum (`museum-gallery/control.html`): new session `sess-126c6`, Contract 5.3,
5 targets, 3 surfaces rediscovered with the correct primary, participant count `0`,
0 SciFi frames left, 0 stale Museum panes.
## H. Failure isolation
Live-tested through the served UI:
- **A non-primary surface holds no authority.** Inside the Artifact Display document:
`XZBTContractCore` undefined, `MuseumGalleryContract` undefined,
`MuseumGalleryExhibit` undefined, `XZBTHostTransport` undefined; only the surface bus is
present, and `__museumGalleryDebug` exposes a `link` but no core.
- **A secondary cannot act as a protocol peer.** A forged `hello` and a forged `set`
posted directly to the Artifact Display's window produced zero responses, and the
authoritative selection was unchanged.
- **One broken presentation surface does not harm the control session.** The Artifact
Display frame was navigated to a missing page (`does-not-exist.html`, served as
`Not found`): the authoritative session stayed `connected · synchronized` on
`sess-126c6`, the Control Room kept rendering, the registry/state were unaffected, and
the surface's participant slot was released (count 2 -> 0) with no stale entry.
- **Reload failure is surface-local and recoverable.** NGN's per-surface `Reload` brought
the pane back to `artifact.html`; it re-attached and the count returned to 1 while the
session remained synchronized. No error was raised against the session.
Automated-only (covered by the suites below, not re-injected live):
- malformed / individually-invalid surface descriptors and catalog-level rejection
(`surface-validation.test.js`, `museum-gallery.test.js` — 50 validation cases)
- isolated HTTP, frame and reload failures, external frame removal detection, and a
stalled surface resource (`local-surfaces.test.js`)
- resource-check refusal of redirects before frame navigation (`local-surfaces.test.js`)
- presentation window cannot impersonate the authoritative peer over the production
transport (`local-surfaces.test.js`)
- source/origin validation on the real postMessage transport
(`postmessage-interop.test.js`)
## I. Automated verification
Focused run (Museum Gallery, local surfaces, surface validation, postMessage interop,
connection lifecycle):
```text
node --test tests/museum-gallery.test.js tests/local-surfaces.test.js \
tests/surface-validation.test.js tests/postmessage-interop.test.js tests/connection.test.js
# tests 107
# pass 107
# fail 0
```
Full suite (run once):
```text
node --test
# tests 129
# pass 129
# fail 0
```
Other validation:
```text
git diff --check -> clean (exit 0)
python devlog_editor.py --validate -> devlog-editor: devlog is valid (exit 0)
```
## J. Files changed
None. Validation found no defect, so per the Step 6.6 fix policy no implementation file
was modified. The only additions are this report and its raw evidence record
(`test-fixtures/evidence/step6.6-museum-gallery-validation.txt`).
## K. Limitations
1. Failure isolation was live-tested only for the three cases in section H; malformed
descriptor catalogs, redirect refusal, stalled resources and peer impersonation remain
automated-only, and no risky manual failure injection was performed for them.
2. The `Spotlight flashed.` announcement on secondaries is transient (2.6 s idle timeout).
It was confirmed with a DOM mutation observer rather than by a raw text read after the
event, so the recorded evidence is the observer transition sequence.
3. All live checks were same-origin over `http://127.0.0.1:4173`. Cross-origin exhibits,
`file://` documents, and non-`http` schemes were not exercised live.
4. The exhibit instance is recreated by `Disconnect`/`Reconnect` and by exhibit switching,
so post-reconnect state is the exhibit's documented default state (`stateRevision` 0).
No cross-session state persistence exists in the reference exhibit; this is expected
standalone behavior, not a regression.
5. Local surfaces were exercised in one browser window; casting/remote displays remain
out of scope for Step 6.6 and were not tested.
## L. Verdict
PASS. Museum Gallery behaves as a complete Contract 5.3 reference exhibit through the
normal NGN operator UI: connection and synchronization succeed, Contract 5.3 is
negotiated, the surface catalog and primary handling are correct, both secondaries render
locally and report attachment, participant counts stay correct across reload/close/reopen,
Control -> secondary and secondary -> primary -> secondaries synchronization are both
proven, late join and reload recovery return current state, representative host commands
work, disconnect/reconnect and exhibit switching clean up completely, SciFi still connects
and synchronizes, and the focused and full automated suites pass.
@@ -0,0 +1,206 @@
# XZBT-NGN Step 6.7B — SciFi Observation Surface Implementation
Status: PARTIAL PASS. Implementation, focused tests, and full NGN test suite are
complete against a pre-existing, out-of-scope defect. Live browser verification
(report §15) and the standalone packaged-build check were not performed in this
session.
Step 6.7B implements the architecture selected by Step 6.7A (design-only, no
code changes) — Candidate A, narrow form: SciFi-XZBT remains one document, one
script set, with two boot modes, and advertises a Contract 5.3 Observation
presentation surface as a query-mode view of its own `index.html`.
## A. Chosen architecture (confirmed as implemented)
- One document, two boot modes, resolved by `js/surface-mode.js` from
`window.location` before heavy subsystem construction.
- Primary surface: `index.html` (unchanged console), `primary: true`.
- Observation surface: `index.html?surface=observation&xi=<instanceId>`, per
Contract 5.3 §31.4's query-string surface form.
- One authoritative SciFi state, one Contract 5.3 host session (console mode
only), no NGN changes (`src/**` untouched — verified by mtime check against
this session's own edits).
- Presentation synchronization is exhibit-owned via an instance-scoped
`BroadcastChannel` (`js/surface-bus.js`), never observed by NGN.
- Opening the Observation surface does not set `view.observation = true`; the
console's own full-screen overlay is unaffected by pane lifecycle.
## B. Files changed — SciFi-XZBT (`G:\.vibe\SciFi-XZBT`)
| File | Change |
| --- | --- |
| `js/surface-mode.js` | **New**, 108 lines. Pure mode resolver (`resolve`, `newInstanceId`, `SURFACES`, `channelName`). No DOM, no imports. |
| `js/surface-bus.js` | **New**, 254 lines. Instance-scoped `BroadcastChannel` owner/attach sides (`createOwner`, `attach`), adapted from Museum Gallery's proven attach/snapshot/mutate/detach shape with a Set-based participant count. |
| `js/contract-adapter.js` | 1006 → 1060 lines (+54). Additive: `surfaces`/`instanceId` constructor options; `describe()` emits `surfaces` only when non-empty; `view.pillars` / `view.warp-flight` targets registered (state/boolean/readable/writable/restorable, `category: observation`, `requires: [observation]`); `onLocalChange`/`onLocalAction` hooks fired from `_commitChanges`/`_invokeAction`, independent of `sessionActive`; version bumped `5.2.0``5.3.0`. Handshake, envelope, `xzbt` gating, and the `_emitEvent` session gate are unchanged. |
| `js/app.js` | 4703 → 4921 lines (+218). Boot-mode resolution; `isConsoleMode`-guarded construction of `StarshipVisualizer`, `XZBTGenerativeExperience`, `XZBTControlBus`, `XZBTContractAdapter`, the keydown hotkey listener, and the sleep timer; `enterObservation()`'s `prepareExperience()` call now runs only in console mode; surface-bus owner wiring (console) / attach wiring (presentation); `applyPresentationState()` renderer; `observationAudienceActive()` decoupling the ambient scheduler from local overlay visibility; `view.pillars`/`view.warp-flight` added to `bindings.setters` and `getContractStateSnapshot()`; `togglePillars()`/`toggleWarpFlight()` now route through `applyMutation`/bus `mutate` instead of local engine mutation; `window.__xzbtSurfaceDispose` + `beforeunload` detach. |
| `js/observation-engine.js` | 3595 → 3602 lines (+7). Only change: the WARP and PILLARS dock buttons now route through `onMutation`, matching the pre-existing VIEWPORT/RED ALERT/PRESET pattern, instead of mutating engine state directly. |
| `css/style.css` | 2430 → 2457 lines (+27, additive). `body.surface-observation` rules hide `header`/`main`, hide `#obs-btn-exit` / `#observation-return-pill`; overlay renders full-bleed. |
| `index.html` | +2 `<script src="...">` tags, exact `package.ps1`-compatible form, positioned after `control-bus.js`, before `generative-experience.js`. |
| `agents.md`, `README.md` | Documented the two-mode rule, the two new files, the surface-bus mechanism, and the no-duplicate-authority invariant. |
`js/audio.js`, `js/config.js`, `js/visualizer.js`, `js/core-animations.js`,
`js/generative-experience.js`, `js/control-bus.js`, `js/observation-bezels.js`,
and `tools/package.ps1` are unchanged, per the report's "deliberately
unchanged" list.
## C. Files changed — XZBT-NGN
| File | Change |
| --- | --- |
| `test-fixtures/reference-exhibits/scifi/{index.html, js/app.js, js/contract-adapter.js, js/observation-engine.js, css/style.css}` | Byte-for-byte resync from SciFi-XZBT source. |
| `test-fixtures/reference-exhibits/scifi/js/{surface-mode.js, surface-bus.js}` | **New**, copied verbatim from SciFi-XZBT source. |
| `test-fixtures/PROVENANCE.md` | Appended resync note (source path, files copied, no fixture-local corrections). |
| `tests/scifi-surfaces.test.js` | **New**, 533 lines, 24 tests: surface catalog / contract validation, mode resolution, bus late-join/propagation/mutation-routing/no-second-authority/reload/detach/session-independence, duplicate-subsystem construction spies, app.js source-structure guards, ambient-timer test, one-contract-session test. |
| `tests/local-surfaces.test.js` | +1 test using SciFi's real descriptors (19 → 20 tests), confirming the primary resolves to `control` and opens no second frame. |
| `src/**` | **Unchanged.** No NGN core changes were needed or made. |
## D. State synchronization
- Mechanism: `BroadcastChannel` named `xzbt-scifi-surface-v1:<instanceId>`, owned
by the console document; the presentation document reads `instanceId` from its
own `?xi=` query parameter.
- Message shapes match report §5.3 verbatim (`attach` / `attach.snapshot` /
`state` / `action` / `presentation` / `mutate` / `detach`), carry no XZBT
envelope, and are never observed by NGN.
- Late join / reload: `attach` always receives the live
`contractAdapter.getContractState()` snapshot plus `stateRevision` and
`registryRevision`; a reload is a fresh `attach` with a fresh `participantId`.
- Presentation mutations (`view.pillars`, `view.warp-flight`, `view.viewport-frame`,
`alert.active`, `preset.selected`) are sent as `mutate` requests and applied by
the owner exclusively through `contractAdapter.applyMutation(...)` with source
`'ui'` — the same chokepoint NGN's own `set` uses. The presentation side never
writes local state.
- Participant bookkeeping is a `Set`, so duplicate attach and late/duplicate
detach are harmless and cannot ratchet the count.
## E. Heavy-subsystem ownership (confirmed)
Presentation mode does **not** construct: `XZBTGenerativeExperience` (WebLLM +
Kokoro — `prepareExperience()` is now called only inside the console-mode
branch of `enterObservation()`), `XZBTContractAdapter` (no second Contract
session), `XZBTControlBus`, `StarshipVisualizer`, or `CoreAnimations` (its
render loop is only reachable from `StarshipVisualizer`, which is never
constructed). The keydown hotkey listener and the sleep timer are also
console-only. This is enforced by `isConsoleMode` guards at each construction
site and asserted by source-structure tests in `scifi-surfaces.test.js`, plus
construction-spy tests that fail if `AudioContext`/`fetch`/`import` are ever
touched from the surface side.
`AudioManager` and the synth subsystems are still constructed on the
presentation side (needed only to satisfy `ObservationEngine`'s constructor
arity, per the 6.7A report) but never `init()`/`resume()`d, so no
`AudioContext` or audio node is ever created there.
## F. New absolute state targets
- `view.pillars` — state/boolean, readable, writable, restorable,
`category: observation`, `requires: [observation]`. Wired into
`bindings.setters`, `getContractStateSnapshot()`, and dock routing on both
surfaces.
- `view.warp-flight` — same shape. Implemented as a compare-and-toggle against
the engine's existing flight-mode field in `app.js`'s setter rather than
adding a new `observationEngine.setWarpFlight()` method (the report's
suggested name doesn't exist in `observation-engine.js`), keeping that file's
change to the two dock-routing lines only.
Both targets are covered end-to-end (registration, snapshot inclusion, mutation
routing, cross-surface convergence) by tests in `scifi-surfaces.test.js`.
## G. Observation audience behavior
`observationAudienceActive()` = `observationActive || surfaceOwner.attachedCount() > 0`.
The ambient-activity scheduler and `triggerObservationActivity` now gate on this
predicate instead of `observationActive` alone, so a presentation-only audience
(console in normal mode, one or more Observation panes attached) keeps
procedural activity running. `observationActive` itself is unchanged and still
gates the console's own local overlay rendering. Activity *decisions* are made
once by the authority and broadcast; each surface renders its own visual
realization of "an event fired now" rather than a byte-identical copy (see
Deviations, below) — narrative timing is shared, exact pixel content is not.
## H. Standalone verification
Not run live in this session (see §K). By construction: `index.html` with no
query string resolves to console mode via `js/surface-mode.js`'s `resolve()`,
which is unchanged from today's single-mode behavior for that input, and no
code path in the console branch was removed — only guarded with `isConsoleMode`,
which is `true` for that input. This is asserted structurally by
`scifi-surfaces.test.js`'s mode-resolution tests, but not confirmed by opening
a real browser.
## I. Fixture synchronization
`test-fixtures/reference-exhibits/scifi/` was resynced by copying the seven
changed/new files verbatim from `G:\.vibe\SciFi-XZBT` after the source was
syntax-checked and its own focused tests passed. `test-fixtures/PROVENANCE.md`
records the resync. No fixture-local corrections were introduced — drift
prevention relies on this being a discrete, documented, one-directional copy
step (source → fixture), matching the procedure Step 5 used.
## J. Tests
| Suite | Result |
| --- | --- |
| `tests/scifi-surfaces.test.js` (new) | 24/24 pass |
| `tests/local-surfaces.test.js` | 20/20 pass |
| Full NGN suite (`npm test`) | 149/154 pass |
The 5 failures are all in `tests/postmessage-interop.test.js` (`hello timed
out` ×4, one regex check), and are **not** caused by this step's changes.
Root cause: `contract-adapter.js`'s `_setupWindowBridge` gates inbound
messages on exact string equality against `xzbt: '5.2'`, while NGN's
`src/host.js` — modified independently, before this session, and outside this
step's scope — now stamps outgoing envelopes `xzbt: '5.3'`. Fixing this
requires editing the handshake/envelope gating logic that the 6.7A report
explicitly marks "must not change" for Step 6.7B. This is flagged as a
pre-existing cross-repo integration gap for separate resolution, not silently
worked around.
## K. Live verification
**Not performed in this session.** The implementation was delegated to and
completed by an agent operating via local shell access on the SciFi-XZBT and
XZBT-NGN working trees; it did not drive a real browser against a running
`npm start` server. The following report §15 acceptance-procedure steps
remain to be run manually:
1. Connect NGN to `http://127.0.0.1:4173/test-fixtures/reference-exhibits/scifi/index.html`; confirm `connected · synchronized`, Contract 5/3, target catalog.
2. Confirm the Surfaces panel shows exactly two entries — `surface.console` (Primary, no Open button) and `surface.observation` (`?surface=observation&xi=…`).
3. Open the Observation pane; confirm it renders the current universe/preset within ~1s, shows no RETURN ✕ / return pill, and background click does nothing.
4. Confirm the console pane does **not** black out — `view.observation` stays `false`.
5. From NGN, `set` `universe.selected`, `preset.selected`, `alert.active`, `view.viewport-frame`, `view.activity`, `view.pillars`, `view.warp-flight` and confirm both console and pane converge, with `stateRevision` advancing.
6. Reload the pane; confirm it returns to current (not default) state. Close/reopen; confirm the participant count does not ratchet.
7. Press WATCH EXPERIENCE on the console; confirm the pane is unaffected and `view.observation` toggles only the console overlay.
8. DevTools checks on the pane's frame: no WebLLM/Kokoro network activity, `window.generativeExperience === undefined`, no `AudioContext`.
9. Confirm transient activity events and AI announcements appear on console and pane at the same moment.
10. Disconnect/reconnect NGN; confirm clean pane release and rediscovery.
11. Standalone: open `index.html` directly (no NGN) — full console, WATCH EXPERIENCE, hotkeys, audio.
12. Standalone two-window: open `index.html?surface=observation&xi=<real id>` in a second window with no NGN present; confirm it attaches and mirrors.
13. Open `?surface=observation&xi=nonexistent` alone; confirm a waiting state, no invented authority.
14. Run `tools/package.ps1` (requires PowerShell — unavailable in this session's Linux shell) and open the packaged `dist/` file offline; confirm the two new scripts were inlined (report risk R7 — inlining failure is silent) and standalone behavior is unchanged.
## L. Deviations from the 6.7A report
- **Warp-flight setter name.** The report's suggested `observationEngine.setWarpFlight(...)` does not exist; implemented as a compare-and-toggle in `app.js`'s setter instead, touching only the two dock-routing lines in `observation-engine.js`.
- **No single `bootConsole()` function.** The guarded constructions/listeners remain in their existing positions in `app.js` (each wrapped in an `isConsoleMode` check) rather than being physically lifted into one named function, to respect `agents.md`'s "surgical, scoped edits" rule on a ~4,900-line file. Source-structure tests instead assert `isConsoleMode` appears immediately before each construction site. Functionally equivalent to the report's suggestion; less textually tidy.
- **Activity-event visuals are decision-synchronized, not pixel-identical.** Byte-identical rendering across documents would require refactoring the ten universe generator functions to take explicit random parameters instead of calling `Math.random()` internally — judged out of proportion to this step. The *decision* to fire an event, and its narrative timing, is shared; each surface renders its own visual instance of it. Documented as an extension of the already-accepted instrumentation-digit divergence (report §6).
## M. Remaining limitations
- **Audio-reactive waveform is flat on the Observation surface**, as explicitly scoped out of 6.7B. No audio graph exists there; `observation-engine.js`'s pre-existing `am.analyser`-null guards degrade it gracefully. Deferred, not solved, per the governing report.
- **`postmessage-interop.test.js`'s 5 failures** (see §J) are a pre-existing `5.2`/`5.3` handshake-gating mismatch between this SciFi-XZBT change and an independent, prior `src/host.js` change, outside this step's edit scope. Needs separate resolution — likely a version-gating fix in `contract-adapter.js`'s `_setupWindowBridge`, but that edit was withheld here because the report marks it must-not-change for 6.7B.
- **Live browser verification and the packaged single-file build were not exercised** in this session (§K) — recommended before treating 6.7B as fully accepted.
## Step 6.7 verdict
Step 6.7B implementation is **substantively complete against the 6.7A
architecture**: correct surface catalog, single authority, session-independent
local synchronization, both new canonical targets, all heavy-subsystem guards,
audience-activity decoupling, and standalone-preserving structure, all
supported by 44 passing focused/regression tests. Step 6.7 as a whole should
**not yet be marked complete**: the pre-existing handshake mismatch surfaced by
the full suite run needs a decision (fix now vs. track separately), and the
live NGN/browser and standalone/packaged-build verification in report §15
has not been performed. Recommend resolving the handshake mismatch and running
live verification before closing Step 6.7B, and explicitly deferring Step 6.8
until then, per this task's instructions.