17 KiB
XZBT-NGN Step 6.7B — SciFi Observation Surface Implementation
Status: PARTIAL PASS at the time of writing. Superseded 2026-09-15 by the Step 6.7 closure report: the handshake defect recorded in §J/§M is fixed (full suite now 154/154) and the standalone packaged-build check in §H/§K.14 has been performed. Live NGN browser verification (report §15, steps 1–13) remains outstanding at that date.
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.jsfromwindow.locationbefore 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:
BroadcastChannelnamedxzbt-scifi-surface-v1:<instanceId>, owned by the console document; the presentation document readsinstanceIdfrom 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:
attachalways receives the livecontractAdapter.getContractState()snapshot plusstateRevisionandregistryRevision; a reload is a freshattachwith a freshparticipantId. - Presentation mutations (
view.pillars,view.warp-flight,view.viewport-frame,alert.active,preset.selected) are sent asmutaterequests and applied by the owner exclusively throughcontractAdapter.applyMutation(...)with source'ui'— the same chokepoint NGN's ownsetuses. 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 intobindings.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 inapp.js's setter rather than adding a newobservationEngine.setWarpFlight()method (the report's suggested name doesn't exist inobservation-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
Update (2026-09-15): performed — see the closure report and
test-fixtures/evidence/step6.7-packaging.txt. The paragraph below retains the
pre-verification state of this session.
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 |
Update (2026-09-15): full suite 154/154 and tests/postmessage-interop.test.js
7/7 — the five failures are resolved (see the closure report). The
paragraphs below are retained as this session's historical record.
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:
- Connect NGN to
http://127.0.0.1:4173/test-fixtures/reference-exhibits/scifi/index.html; confirmconnected · synchronized, Contract 5/3, target catalog. - Confirm the Surfaces panel shows exactly two entries —
surface.console(Primary, no Open button) andsurface.observation(?surface=observation&xi=…). - Open the Observation pane; confirm it renders the current universe/preset within ~1s, shows no RETURN ✕ / return pill, and background click does nothing.
- Confirm the console pane does not black out —
view.observationstaysfalse. - From NGN,
setuniverse.selected,preset.selected,alert.active,view.viewport-frame,view.activity,view.pillars,view.warp-flightand confirm both console and pane converge, withstateRevisionadvancing. - Reload the pane; confirm it returns to current (not default) state. Close/reopen; confirm the participant count does not ratchet.
- Press WATCH EXPERIENCE on the console; confirm the pane is unaffected and
view.observationtoggles only the console overlay. - DevTools checks on the pane's frame: no WebLLM/Kokoro network activity,
window.generativeExperience === undefined, noAudioContext. - Confirm transient activity events and AI announcements appear on console and pane at the same moment.
- Disconnect/reconnect NGN; confirm clean pane release and rediscovery.
- Standalone: open
index.htmldirectly (no NGN) — full console, WATCH EXPERIENCE, hotkeys, audio. - Standalone two-window: open
index.html?surface=observation&xi=<real id>in a second window with no NGN present; confirm it attaches and mirrors. - Open
?surface=observation&xi=nonexistentalone; confirm a waiting state, no invented authority. - Run
tools/package.ps1(requires PowerShell — unavailable in this session's Linux shell) and open the packageddist/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 inapp.js's setter instead, touching only the two dock-routing lines inobservation-engine.js. - No single
bootConsole()function. The guarded constructions/listeners remain in their existing positions inapp.js(each wrapped in anisConsoleModecheck) rather than being physically lifted into one named function, to respectagents.md's "surgical, scoped edits" rule on a ~4,900-line file. Source-structure tests instead assertisConsoleModeappears 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-existingam.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-existing5.2/5.3handshake-gating mismatch between this SciFi-XZBT change and an independent, priorsrc/host.jschange, outside this step's edit scope. Needs separate resolution — likely a version-gating fix incontract-adapter.js's_setupWindowBridge, but that edit was withheld here because the report marks it must-not-change for 6.7B. Resolved 2026-09-15 as a separate, explicitly authorized task: the inbound filter is now a presence/type check on the advisory field, and the adapter advertises contract minor 3 / version 5.3.0. See the closure report.- Live browser verification and the packaged single-file build were not exercised in this session (§K) — recommended before treating 6.7B as fully accepted. Update (2026-09-15): the packaged build is regenerated and verified; live browser verification (report §15, steps 1–13) is still outstanding.
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.
Update (2026-09-15): the handshake mismatch is resolved and the full suite is green (154/154); the packaged single-file build has been regenerated and verified (fixture-consistent, structurally and behaviorally checked). See the Step 6.7 closure report. What remains before Step 6.7 can be marked complete is the live NGN Observation verification in report §15, steps 1–13.