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.
8.9 KiB
Step 6.4 — Local Multi-Surface Rendering
Verified 2026-09-14. Step 6.4 complete. Contract 5.3 presentation surfaces can be rendered locally through the normal NGN operator UI.
Pre-change architecture
ExhibitConnection owned one iframe, remembered its normalized same-origin
URL in connection.url, and passed it to ExhibitHost.connect().
ExhibitHost stored that URL as exhibitBaseUrl; discovery called
validateSurfaceCatalog() and stored the surviving descriptors in host.surfaces.
The UI displayed descriptive surface cards only. The primary iframe alone used
postMessageTransport, which checked both source window and origin.
There was no separate window/frame lifecycle helper for presentation views.
Museum Gallery's control.html is both its entry point and declared primary.
Only this page constructs its Contract Core. artifact.html and info-wall.html
attach through the fixture's existing BroadcastChannel bus and construct no
independent Core. NGN does not participate in that bus. SciFi is an external
reference implementation, not a source of NGN architecture.
The working tree already contained Step 6.3 contract/fixture/test/documentation edits, including an untracked postMessage interoperability test. They were preserved; this task did not commit, push or modify publishing records.
Files changed for this step
| File | Change |
|---|---|
src/surface-url.js |
Canonical standards-based resolver |
src/validation.js |
Reuse resolver; validate optional metadata types |
src/local-surfaces.js |
Local frame ownership, resource checks and lifecycle |
src/connection.js |
Establish base from final same-origin control document after navigation |
src/ui.js |
Descriptor-driven lifecycle controls, state and pane rendering |
public/index.html |
Operator explanation of local surfaces |
tests/local-surfaces.test.js |
17 URL, lifecycle, failure, trust and resource tests |
tests/connection.test.js |
Final-document URL/navigation regression test |
README.md |
Current functionality and deferred scope |
| This report | Architecture, behavior and verification |
test-fixtures/evidence/step6.4-*.txt |
Live Museum and SciFi DOM snapshots |
No framework, dependencies, exhibit-specific host branches or schema fields were added. Existing CSS supplies pane sizing and button styles.
Resolution and trust
resolveSurfaceURL(value, exhibitBaseUrl) uses new URL() and returns a
normalized absolute URL. Both discovery and opening use it. Sibling paths,
./, parent paths, query-only and fragment-only references resolve against the
supplying exhibit document, never NGN's administrative page. Absolute URLs
(including same-origin ones), protocol-relative URLs, invalid values, control
characters, surrounding whitespace and cross-origin results are rejected.
HTTP(S) and credential-free results are required. The existing static server
continues to enforce mount and filesystem containment for both control and
presentation resources.
Individual invalid entries produce discovery diagnostics without invalidating targets/state. The exactly-one-primary rule is applied to the surviving set. Malformed optional metadata cannot reach the UI's string/array operations.
Each open/reload first performs a same-origin HEAD resource check with
redirect: 'error'. Missing pages become surface errors; redirects are refused
before the iframe is created. Frame loads additionally check the resulting
origin. Descriptor text is inserted as text, never evaluated. No presentation
frame receives a host transport or session, and the existing postMessage
source/origin checks are unchanged. A test demonstrates that a same-origin
presentation window cannot impersonate the control peer through this transport.
Rendering, primary handling and lifecycle
NGN uses inline iframe panes, one per opened descriptor ID. Nothing is automatically opened beyond the existing control frame. A primary descriptor whose complete normalized URL matches the control document is shown as already open there, with separate surface actions disabled. Its lifecycle belongs to the Connection panel. A separate primary URL is offered through the same Open action as any other presentation view. Queries/fragments remain significant.
States are closed, loading, open, error, and control (already rendered
by the authoritative frame). Open during loading/open is a no-op. Reload
replaces only the selected presentation document. Close removes it and clears
its diagnostic; Open recreates it. UI cards show labels, primary/non-primary
status, capability availability, lifecycle state and failures.
Disconnect, session reconnect, exhibit replacement and host page departure release owned frames and abort pending checks. Late load/check completions cannot recreate abandoned panes. Removed/changed descriptors release their resources on rediscovery; unchanged frames survive metadata updates. An observer detects externally removed pane frames and updates their state to closed.
HTTP failures, frame errors, origin failures and a 15-second load timeout stay local to the affected surface. Reload can retry them. The authoritative control session and synchronized host state remain intact.
Automated verification
Final focused command:
node --test tests/local-surfaces.test.js tests/surface-validation.test.js tests/postmessage-interop.test.js tests/connection.test.js
80 passed, 0 failed
Coverage includes every requested relative URL form and rejection category; real Museum descriptors loaded from its actual adapter; duplicate-open, reload, close, reopen, disconnect/switch cleanup, pending-load cancellation, registry refresh, distinct primary pages, malformed metadata, timeouts, external removal, cross-origin loads, source impersonation and real HTTP success/404/redirect cases. Lifecycle tests use the production manager and ExhibitHost with EventTarget frames; the existing Step 6.3 suite retains the real production host and exhibit postMessage bridges in browser-like window realms.
Full npm test: 118 passed, 0 failed, including Museum Gallery and SciFi
connection/synchronization regressions and existing Step 6.3 tests.
Node test workers required execution outside the Windows sandbox after its
initial spawn EPERM denial. python devlog_editor.py --validate passed.
git diff --check passed.
Live browser verification
Performed in the Codex in-app browser against npm start at
http://127.0.0.1:4173/, using the ordinary operator UI:
- Loaded the requested full Museum
control.htmlURL: connected, synchronized, Contract 5.3, three surface descriptors. - Primary card reported the existing control frame and disabled duplicate Open.
- Opened Artifact Display and Information Wall: both reported
open, rendered their respective page headings/content and displayed “Attached to Control Room.” - Reloaded Artifact Display; closed Information Wall (confirmed
closed), then reopened it (confirmedopen). - Switched to SciFi: both Museum cards/panes disappeared; SciFi reported connected, synchronized, Contract 5.3.
- Reloaded Museum: both secondary cards began closed and opened cleanly.
- Explicit Disconnect cleared all frames/catalog; Reconnect rediscovered the closed surfaces and Artifact Display reopened successfully.
- Loaded SciFi again after the final UI changes: connected and synchronized; no Museum surface cards remained.
Saved observations:
An early batched browser Close action did not change the page; after inspecting the unchanged state, individual Close/Reopen actions passed, including a repeat on the final UI. Failure injection is automated coverage, not a claim of live missing-page or timeout testing.
Limitations and deferred work
openmeans the document loaded, not that its application scripts are healthy or that cross-surface state synchronization has been proven.- HEAD support is required. HTTP redirects, including same-origin redirects, are conservatively refused for presentation resources. The local static server supports HEAD and does not issue redirects.
- These are trusted same-origin exhibit pages under the existing trust model, not a sandbox for hostile exhibit code. A page's later script-driven navigation can only be detected at load; resource checks do not certify application code.
- Museum's existing initial shared-state attachment was observed. Its Control Room counter/footer text did not consistently reflect that attachment. No fixture behavior was changed; mutation propagation proof remains Step 6.5.
- No remote/cast display assignment, device discovery, networking clients, fullscreen orchestration, SciFi Observation adaptation or synchronization architecture was introduced.
All Step 6.4 acceptance categories pass. Step 6.5 has not been implemented.