Files
XZBT-NGN/docs/reference/XZBT-NGN-Step6.7-SciFi-Surface-Adaptation.md
T

17 KiB
Raw Blame History

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 113) 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.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.05.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

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:

  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. 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 113) 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 113.