generated from Labyricorn/labyricorn-project-template
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:
@@ -0,0 +1,165 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user