Files
XZBT-NGN/docs/architecture/XZBT-NGN-Step6.4-Local-Surfaces.md
T
Labyricorn 745912e451 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.
2026-09-14 19:45:27 -07:00

166 lines
8.9 KiB
Markdown

# 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:
```text
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:
1. Loaded the requested full Museum `control.html` URL: connected,
synchronized, Contract 5.3, three surface descriptors.
2. Primary card reported the existing control frame and disabled duplicate Open.
3. Opened Artifact Display and Information Wall: both reported `open`, rendered
their respective page headings/content and displayed “Attached to Control Room.”
4. Reloaded Artifact Display; closed Information Wall (confirmed `closed`),
then reopened it (confirmed `open`).
5. Switched to SciFi: both Museum cards/panes disappeared; SciFi reported
connected, synchronized, Contract 5.3.
6. Reloaded Museum: both secondary cards began closed and opened cleanly.
7. Explicit Disconnect cleared all frames/catalog; Reconnect rediscovered the
closed surfaces and Artifact Display reopened successfully.
8. Loaded SciFi again after the final UI changes: connected and synchronized;
no Museum surface cards remained.
Saved observations:
- [Museum rendered panes](../../test-fixtures/evidence/step6.4-museum-local.txt)
- [SciFi regression](../../test-fixtures/evidence/step6.4-scifi-regression.txt)
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
- `open` means 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.