From bdd103090c0757b2c558bea69cd8d960e3a7c935 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 9 Jul 2026 11:04:29 +0100 Subject: [PATCH] Add Electron surface tree lowering claim --- docs/eve-migration-roadmap.md | 51 ++++++----- docs/parity-testing-harness.md | 5 +- docs/world-state-lowering.md | 5 +- .../eve-electron/eve-runtime-capability.json | 21 ++++- .../eveelectron-split-handoff.json | 16 +++- .../eve-electron/src/eve-electron-shell.mjs | 61 +++++++++++++ .../test/eve-electron-shell.test.mjs | 90 +++++++++++++++++++ ...un-aetheria-conformance-consumer-smoke.ps1 | 8 ++ scripts/run-conformance-consumer-smoke.ps1 | 4 + scripts/run-eveelectron-lifecycle-smoke.ps1 | 28 +++++- .../run-eveelectron-provider-shell-smoke.ps1 | 40 ++++++++- .../run-eveelectron-split-handoff-smoke.ps1 | 4 +- ...ntime-owner-conformance-consumer-smoke.ps1 | 5 ++ ...plit-target-conformance-consumer-smoke.ps1 | 2 + tools/parity/parity-manifest.json | 26 ++++-- .../aetheria.provider-advertisement.json | 9 +- 16 files changed, 327 insertions(+), 48 deletions(-) diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 525c8f4..ed812f0 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -294,11 +294,11 @@ Recently cut: lower provider-owned world state without owning it. Parity compares those claims against the ledger, exports them through runtime records and `packs/runtime.json`, the runtime-owner smoke asserts them, and the generic - conformance smoke now treats `unity-scene` as a narrow Unity scene graph - command-surface claim while capture and release remain split blockers. + conformance smoke now treats `unity-scene` and `electron-shell` as narrow + runtime-owned command-surface claims while capture and release remain split + blockers. - Missing provider-advertised world-surface lowering targets are now assigned - to their pending runtime owner when one exists. `unity-scene` gaps point at - EveUnity's pending `unity-scene` runtime, and `tui` gaps point at EveTui's + to their pending runtime owner when one exists. `tui` gaps point at EveTui's pending `tui` runtime. Eve only owns a world-lowering gap when no runtime target has been declared. - The conformance export now carries `worldSurfaceLoweringGaps[]` as typed @@ -308,14 +308,15 @@ Recently cut: - The conformance export also carries `worldSurfaceLoweringCoverage[]`, a full provider-surface-to-target ledger that marks each advertised lowering target as `claimed`, `missing-claim`, or `missing-runtime`. The generic conformance - consumer now asserts Aetheria's claimed web, UI Toolkit, and Unity scene graph - targets plus the missing TUI targets from the same typed surface. + consumer now asserts Aetheria's claimed web, UI Toolkit, Unity scene graph, + and Electron shell targets plus the missing TUI targets from the same typed + surface. - The conformance export carries `commandBoundaryCoverage[]`, joining each provider-owned interactive world surface target to its runtime owner, - provider command boundary, receipt schema, and runtime command envelope. Web - Unity UI Toolkit, and Unity scene are `covered` because they advertise - `gamecult.eve.command.v1`; TUI stays visible as `missing-runtime-claim` - until its generic terminal lowerer exists. + provider command boundary, receipt schema, and runtime command envelope. Web, + Unity UI Toolkit, Unity scene, and Electron shell are `covered` because they + advertise `gamecult.eve.command.v1`; TUI stays visible as + `missing-runtime-claim` until its generic terminal lowerer exists. - The browser reference lowerer now carries the active provider surface's advertised `worldInteraction.commandBoundary` and `worldInteraction.receiptSchema` into each `gamecult.eve.command.v1` intent. @@ -415,23 +416,25 @@ Recently cut: external proof until EveUnity owns it. - EveElectron now has a pending split target and handoff at `runtimes/incubating/eve-electron/eveelectron-split-handoff.json`. It does - not claim an Electron runtime body yet. The handoff draws the line between a - future provider-agnostic Electron shell and Aetheria's Starbridge RTS product - client, and the split-target consumer smoke keeps the missing shell, - provider-advertisement consumption, command transport, and capture proofs - visible as blockers. + claim a provider-agnostic Electron shell surface-tree projection and command + surface, but not packaged rendering, embedded document rendering, plugin + projection, or capture. The handoff draws the line between the generic shell + and Aetheria's Starbridge RTS product client, and the split-target consumer + smoke keeps release, packaged rendering, embedded document rendering, and + capture proofs visible as blockers. - EveElectron now has a direct split handoff smoke at `scripts/run-eveelectron-split-handoff-smoke.ps1`. The smoke verifies the - observed Aetheria Electron/client paths that must be replaced, the required - Eve contract inputs, and the pending external proofs without treating - Aetheria's product client as the generic Electron runtime. + current generic shell, command, and world-surface lowering paths, the observed + Aetheria Electron/client paths that must be replaced, the required Eve + contract inputs, and the pending external proofs without treating Aetheria's + product client as the generic Electron runtime. - EveElectron now has a pending runtime capability manifest at `runtimes/incubating/eve-electron/eve-runtime-capability.json` and a lifecycle smoke at `scripts/run-eveelectron-lifecycle-smoke.ps1`. The - manifest declares no supported runtime features or plugin projection yet; it - only types the pending command boundary, package release, provider-shell - smoke, and Electron window capture contracts so EveElectron can consume the - conformance export without pretending the generic shell already exists. + manifest declares provider advertisement consumption, command transport, and + surface-tree projection, claims `electron-shell` world/editor lowering, and + still keeps package release, packaged window rendering, plugin projection, + and Electron window capture as pending EveElectron work. - Electron and TUI pending lifecycle details now live in their runtime capability manifests rather than being duplicated in `tools/parity/parity-manifest.json`. The parity ledger points at the @@ -717,8 +720,8 @@ Recently cut: provider entry, and `aetheria-world-command-replay` scenario without reading Eve's parity manifest or dirtying the Aetheria worktree. - The Aetheria consumer smoke now also asserts frontend extraction boundaries: - web and Unity UI Toolkit are covered generic lowerings, Unity scene remains a - typed EveUnity gap, and the observed Aetheria Electron shell/client paths are + web, Unity UI Toolkit, Unity scene, and Electron shell are covered generic + lowerings, while the observed Aetheria Electron shell/client paths remain split-handoff evidence to replace with a provider-agnostic EveElectron body. - The parity harness now runs a conformance consumer smoke against a copied `artifacts/conformance/latest` layout, proving the export can be consumed diff --git a/docs/parity-testing-harness.md b/docs/parity-testing-harness.md index 9b0e286..8c388d0 100644 --- a/docs/parity-testing-harness.md +++ b/docs/parity-testing-harness.md @@ -302,7 +302,10 @@ Starbridge RTS client remains Aetheria-owned product code until a provider-agnostic shell exists. The same smoke asserts that the Aetheria Electron source paths named by the handoff currently exist as `observed-provider` sources, so the extraction pressure is visible without -turning those paths into EveElectron authority. EveTui is also intentionally +turning those paths into EveElectron authority. The Electron runtime now claims +a narrow provider-advertised surface-tree command surface, while packaged +rendering, embedded document rendering, capture, and plugin projection remain +blockers. EveTui is also intentionally pending: Aetheria advertises `tui` as an agent-facing lowering target, but Eve should not claim that target until a real terminal/grid lowerer and transcript capture exist. EveUnity includes both the active `unity-uitoolkit` package proof diff --git a/docs/world-state-lowering.md b/docs/world-state-lowering.md index 6e5e7c7..d76526b 100644 --- a/docs/world-state-lowering.md +++ b/docs/world-state-lowering.md @@ -148,8 +148,9 @@ The incubating Aetheria fixture advertises `aetheria.daemon.game` as `interactive-world` with `worldInteraction.projectionKind: provider-authored-world-surface`, `commandBoundary: aetheria.daemon.commands`, and `receiptSchema: aetheria.eve_command_acceptance_status.v1`. Web, Unity UI -Toolkit, Unity scene, and TUI lowerers are lowering targets; Aetheria remains -the owner of world state, assets, command acceptance, and receipts. +Toolkit, Unity scene, Electron shell, and TUI lowerers are lowering targets; +Aetheria remains the owner of world state, assets, command acceptance, and +receipts. ## Non-Goals diff --git a/runtimes/incubating/eve-electron/eve-runtime-capability.json b/runtimes/incubating/eve-electron/eve-runtime-capability.json index d8e3cfe..c8279c3 100644 --- a/runtimes/incubating/eve-electron/eve-runtime-capability.json +++ b/runtimes/incubating/eve-electron/eve-runtime-capability.json @@ -15,7 +15,8 @@ }, "supportedFeatures": [ "providerAdvertisements", - "commandTransport" + "commandTransport", + "surfaceTreeProjection" ], "supportedPlugins": [], "unsupportedPlugins": [ @@ -32,10 +33,24 @@ "reason": "Electron provider-shell skeleton exists, but no TeX projection adapter exists in this runtime." } ], + "worldSurfaceLowering": [ + { + "targetId": "electron-shell", + "surfaceKinds": ["interactive-world", "interactive-world-editor"], + "projectionKinds": ["provider-authored-world-surface", "provider-authored-world-editor-surface"], + "supportLevel": "electron-shell-surface-tree-command-surface", + "ownership": "runtime-lowers-provider-world-surface-without-owning-world-state", + "evidencePaths": [ + "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", + "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", + "web/fixtures/aetheria.provider-advertisement.json" + ] + } + ], "commandTransport": { "schema": "gamecult.eve.command.v1", "status": "provider-shell-contract-skeleton", - "emitter": "EveElectronShell.EmitCommand", + "emitter": "EveElectronShell.createCommandIntent", "clientId": "electron-shell", "authority": "Electron emits command intents through provider-advertised command boundaries; providers accept, deny, and publish receipts." }, @@ -112,6 +127,6 @@ }, "capture": { "status": "missing", - "note": "Electron capture is a split blocker until EveElectron owns a provider-agnostic shell." + "note": "Electron capture is a split blocker until EveElectron attaches provider-agnostic shell window artifacts." } } diff --git a/runtimes/incubating/eve-electron/eveelectron-split-handoff.json b/runtimes/incubating/eve-electron/eveelectron-split-handoff.json index d24c991..c0ae8b6 100644 --- a/runtimes/incubating/eve-electron/eveelectron-split-handoff.json +++ b/runtimes/incubating/eve-electron/eveelectron-split-handoff.json @@ -40,6 +40,20 @@ "destinationOwner": "EveElectron", "replacementProof": "Electron emits gamecult.eve.command.v1 intents through the provider-advertised command boundary and receives provider receipts instead of mutating local product state." }, + { + "id": "electron-world-surface-lowering", + "stage": "world-surface-lowering", + "currentPaths": [ + "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", + "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", + "runtimes/incubating/eve-electron/eve-runtime-capability.json" + ], + "observedProviderPaths": [ + "E:\\Projects\\Aetheria\\Aetheria.Rts.Web\\Client" + ], + "destinationOwner": "EveElectron", + "replacementProof": "Electron claims provider-advertised world/editor surface targets with provider-agnostic surface-tree projection and command boundary evidence; packaged rendering and capture remain separate lifecycle proofs." + }, { "id": "electron-capture-lifecycle", "stage": "capture", @@ -65,7 +79,7 @@ ], "requiredExternalProofs": [ "EveElectron publishes a runtime capability manifest with provider advertisement consumption and command transport.", - "EveElectron owns a provider-shell contract skeleton that selects advertised provider surfaces and emits gamecult.eve.command.v1 intents.", + "EveElectron owns a provider-shell contract skeleton that selects advertised provider surfaces, projects Eve surface trees, and emits gamecult.eve.command.v1 intents.", "EveElectron lowers at least one generic Eve provider surface that is not Aetheria.", "EveElectron lowers Aetheria world/editor surfaces through provider advertisements without importing Aetheria internals.", "Aetheria keeps Starbridge-specific UI, assets, authored flows, and receipts in the Aetheria repo.", diff --git a/runtimes/incubating/eve-electron/src/eve-electron-shell.mjs b/runtimes/incubating/eve-electron/src/eve-electron-shell.mjs index 9597779..97c4956 100644 --- a/runtimes/incubating/eve-electron/src/eve-electron-shell.mjs +++ b/runtimes/incubating/eve-electron/src/eve-electron-shell.mjs @@ -23,6 +23,26 @@ export class EveElectronShell { }; } + lowerSurface(surfaceDocument, providerAdvertisement, requestedSurfaceId = "") { + const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId); + const document = normalizeSurfaceDocument(surfaceDocument); + if (document.surfaceId !== selected.surfaceId) { + throw new Error(`Surface document ${document.surfaceId} does not match advertised Electron surface ${selected.surfaceId}.`); + } + + return { + type: "electron-shell-projection", + schema: "gamecult.eve.electron_shell_projection.v1", + providerId: selected.providerId, + surfaceId: selected.surfaceId, + projectionKind: selected.worldInteraction.projectionKind, + commandBoundary: selected.worldInteraction.commandBoundary, + receiptSchema: selected.worldInteraction.receiptSchema, + ownership: selected.worldInteraction.ownership, + root: buildShellNode(document.root), + }; + } + createCommandIntent(providerAdvertisement, requestedSurfaceId, command, payload = {}) { if (!command) throw new Error("Command is required."); const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId); @@ -77,6 +97,47 @@ export function normalizeProviderAdvertisement(providerAdvertisement) { return { ...providerAdvertisement, providerId, surfaces }; } +export function normalizeSurfaceDocument(surfaceDocument) { + if (!surfaceDocument || typeof surfaceDocument !== "object") { + throw new Error("Surface document is required."); + } + if (surfaceDocument.schema !== "gamecult.eve.surface.v1") { + throw new Error(`Unexpected surface schema: ${surfaceDocument.schema || ""}`); + } + const surface = objectValue(surfaceDocument.surface); + const surfaceId = firstString(surface.id); + if (!surfaceId) throw new Error("Surface document missing surface.id."); + const root = objectValue(surface.root); + if (!root.id) throw new Error(`Surface document ${surfaceId} missing surface.root.`); + return { ...surfaceDocument, surfaceId, root }; +} + +function buildShellNode(component) { + const source = objectValue(component); + const children = Array.isArray(source.children) ? source.children.map(buildShellNode) : []; + return { + id: firstString(source.id), + componentKind: firstString(source.kind), + shellElementKind: shellElementKind(firstString(source.kind)), + props: objectValue(source.props), + layout: objectValue(source.layout), + style: objectValue(source.style), + stateBindingCount: Array.isArray(source.stateBindings) ? source.stateBindings.length : 0, + embeddedDocumentCount: Array.isArray(source.embeddedDocuments) ? source.embeddedDocuments.length : 0, + children, + }; +} + +function shellElementKind(componentKind) { + if (!componentKind) return "empty"; + if (componentKind.startsWith("control.")) return "command-control"; + if (componentKind.startsWith("embed.")) return "plugin-placeholder"; + if (componentKind === "surface.slot") return "embedded-surface-slot"; + if (componentKind.startsWith("field.") || componentKind.startsWith("world.")) return "world-projection-node"; + if (componentKind.startsWith("text.") || componentKind === "text" || componentKind === "label") return "text"; + return "shell-node"; +} + function normalizeWorldInteraction(value) { const source = objectValue(value); return { diff --git a/runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs b/runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs index f567486..0047db7 100644 --- a/runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs +++ b/runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs @@ -36,6 +36,35 @@ test("command intents carry provider-advertised boundaries over component fallba assert.equal(intent.clientId, "electron-shell"); }); +test("lowers provider surface trees into an Electron shell projection", () => { + const shell = new EveElectronShell(); + const projection = shell.lowerSurface(surfaceDocument(), advertisement(), "aetheria.daemon.game"); + + assert.equal(projection.schema, "gamecult.eve.electron_shell_projection.v1"); + assert.equal(projection.providerId, "aetheria"); + assert.equal(projection.surfaceId, "aetheria.daemon.game"); + assert.equal(projection.projectionKind, "provider-authored-world-surface"); + assert.equal(projection.commandBoundary, "aetheria.daemon.commands"); + assert.equal(projection.receiptSchema, "aetheria.eve_command_acceptance_status.v1"); + assert.equal(projection.root.id, "aetheria.daemon.game.root"); + assert.equal(projection.root.children.length, 3); + assert.equal(projection.root.children[0].shellElementKind, "world-projection-node"); + assert.equal(projection.root.children[0].props.binding, "cultmesh://aetheria/world/entities"); + assert.equal(projection.root.children[0].stateBindingCount, 1); + assert.equal(projection.root.children[1].shellElementKind, "command-control"); + assert.equal(projection.root.children[1].props.command, "aetheria.daemon.commands"); + assert.equal(projection.root.children[2].shellElementKind, "plugin-placeholder"); + assert.equal(projection.root.children[2].embeddedDocumentCount, 1); +}); + +test("rejects surface documents that do not match the advertised target", () => { + const shell = new EveElectronShell(); + assert.throws( + () => shell.lowerSurface(surfaceDocument("aetheria.daemon.game"), advertisement(), "aetheria.daemon.editor"), + /does not match advertised Electron surface/, + ); +}); + function advertisement() { return { schema: "gamecult.eve.provider_advertisement.v1", @@ -66,3 +95,64 @@ function advertisement() { ], }; } + +function surfaceDocument(surfaceId = "aetheria.daemon.game") { + return { + type: "surface-state", + schema: "gamecult.eve.surface.v1", + providerId: "aetheria", + providerKind: "game.runtime", + title: "Aetheria world", + version: 1, + updatedAtUtc: "2026-07-09T00:00:00Z", + surface: { + id: surfaceId, + root: { + id: `${surfaceId}.root`, + kind: "surface", + props: {}, + children: [ + { + id: `${surfaceId}.entities`, + kind: "field.surface2d", + props: { + binding: "cultmesh://aetheria/world/entities", + }, + stateBindings: [ + { + target: "entities", + pointerId: "cultmesh://aetheria/world/entities", + }, + ], + children: [], + }, + { + id: `${surfaceId}.focus`, + kind: "control.button", + props: { + command: "aetheria.daemon.commands", + commandId: "aetheria.daemon.focus", + }, + children: [], + }, + { + id: `${surfaceId}.norn`, + kind: "embed.norn", + props: {}, + embeddedDocuments: [ + { + slotId: "norn.map", + documentId: "cultmesh://aetheria/norn/map", + schemaId: "gamecult.eve.surface.v1", + presentationKind: "electron-overlay", + }, + ], + children: [], + }, + ], + }, + styles: [], + }, + commands: [], + }; +} diff --git a/scripts/run-aetheria-conformance-consumer-smoke.ps1 b/scripts/run-aetheria-conformance-consumer-smoke.ps1 index 8b9e496..3566d28 100644 --- a/scripts/run-aetheria-conformance-consumer-smoke.ps1 +++ b/scripts/run-aetheria-conformance-consumer-smoke.ps1 @@ -55,18 +55,24 @@ try { --expect-provider-surface-field aetheria:aetheria.daemon.editor:worldInteraction.ownership:provider-owns-editor-state-assets-command-acceptance-and-receipts ` --expect-interactive-world-surface aetheria:aetheria.daemon.game:web-reference:Aetheria ` --expect-interactive-world-surface aetheria:aetheria.daemon.game:unity-scene:Aetheria ` + --expect-interactive-world-surface aetheria:aetheria.daemon.game:electron-shell:Aetheria ` --expect-interactive-world-surface aetheria:aetheria.daemon.editor:unity-uitoolkit:Aetheria ` + --expect-interactive-world-surface aetheria:aetheria.daemon.editor:electron-shell:Aetheria ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:web-reference:claimed:Eve:web ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-scene:claimed:EveUnity:unity-scene ` + --expect-world-lowering-coverage aetheria:aetheria.daemon.game:electron-shell:claimed:EveElectron:electron-shell ` --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:web-reference:claimed:Eve:web ` --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit ` + --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:electron-shell:claimed:EveElectron:electron-shell ` --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:tui:missing-claim:EveTui:tui ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:web-reference:covered:Eve:web ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:covered:EveUnity:unity-uitoolkit ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-scene:covered:EveUnity:unity-scene ` + --expect-command-boundary-coverage aetheria:aetheria.daemon.game:electron-shell:covered:EveElectron:electron-shell ` --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:web-reference:covered:Eve:web ` --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:unity-uitoolkit:covered:EveUnity:unity-uitoolkit ` + --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:electron-shell:covered:EveElectron:electron-shell ` --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:tui:missing-runtime-claim:EveTui:tui ` --expect-world-lowering-gap aetheria:aetheria.daemon.editor:tui:EveTui:tui ` --expect-split-handoff-move EveTui:tui:tui-runtime-body:current:exists:eve-tui-shell.mjs ` @@ -79,8 +85,10 @@ try { --expect-split-handoff-move EveUnity:unity-scene:unity-scene-command-transport:current:exists:EveUnitySceneSurfaceLowerer.cs ` --expect-split-handoff-move EveElectron:electron-shell:electron-shell-runtime:current:exists:eve-electron-shell.mjs ` --expect-split-handoff-move EveElectron:electron-shell:electron-command-transport:current:exists:eve-electron-shell.mjs ` + --expect-split-handoff-move EveElectron:electron-shell:electron-world-surface-lowering:current:exists:eve-electron-shell.mjs ` --expect-split-handoff-move EveElectron:electron-shell:electron-shell-runtime:observed-provider:exists:Aetheria.Rts.Web/Electron ` --expect-split-handoff-move EveElectron:electron-shell:electron-command-transport:observed-provider:exists:Aetheria.Rts.Web/Client/app.ts ` + --expect-split-handoff-move EveElectron:electron-shell:electron-world-surface-lowering:observed-provider:exists:Aetheria.Rts.Web/Client ` --expect-provider-command aetheria:aetheria.daemon.commands ` --expect-provider-receipt-state aetheria:accepted ` --expect-provider-receipt-state aetheria:pending ` diff --git a/scripts/run-conformance-consumer-smoke.ps1 b/scripts/run-conformance-consumer-smoke.ps1 index efd1e93..4f185b7 100644 --- a/scripts/run-conformance-consumer-smoke.ps1 +++ b/scripts/run-conformance-consumer-smoke.ps1 @@ -37,10 +37,14 @@ try { --expect-world-lowering-coverage aetheria:aetheria.daemon.game:web-reference:claimed:Eve:web ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit ` --expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-scene:claimed:EveUnity:unity-scene ` + --expect-world-lowering-coverage aetheria:aetheria.daemon.game:electron-shell:claimed:EveElectron:electron-shell ` + --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:electron-shell:claimed:EveElectron:electron-shell ` --expect-world-lowering-coverage aetheria:aetheria.daemon.editor:tui:missing-claim:EveTui:tui ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:web-reference:covered:Eve:web ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:covered:EveUnity:unity-uitoolkit ` --expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-scene:covered:EveUnity:unity-scene ` + --expect-command-boundary-coverage aetheria:aetheria.daemon.game:electron-shell:covered:EveElectron:electron-shell ` + --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:electron-shell:covered:EveElectron:electron-shell ` --expect-command-boundary-coverage aetheria:aetheria.daemon.editor:tui:missing-runtime-claim:EveTui:tui ` --expect-world-lowering-gap aetheria:aetheria.daemon.game:tui:EveTui:tui ` --expect-world-lowering-gap aetheria:aetheria.daemon.editor:tui:EveTui:tui ` diff --git a/scripts/run-eveelectron-lifecycle-smoke.ps1 b/scripts/run-eveelectron-lifecycle-smoke.ps1 index 1e7da20..e12180b 100644 --- a/scripts/run-eveelectron-lifecycle-smoke.ps1 +++ b/scripts/run-eveelectron-lifecycle-smoke.ps1 @@ -39,13 +39,37 @@ if (-not $manifest.incubation.splitHandoff.manifestPath) { throw "EveElectron manifest missing split handoff path" } -foreach ($feature in @("providerAdvertisements", "commandTransport")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "surfaceTreeProjection")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveElectron manifest missing provider-shell feature: $feature" } } if (@($manifest.supportedPlugins).Count -ne 0) { - throw "EveElectron must not claim plugin projection before the generic shell body exists" + throw "EveElectron must not claim plugin projection before runtime adapters or sidecar bridges exist" +} + +$worldSurfaceLoweringClaims = @() +if ($null -ne $manifest.worldSurfaceLowering) { + $worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering) +} +if ($worldSurfaceLoweringClaims.Count -ne 1) { + throw "EveElectron must claim exactly one world-surface lowering target" +} +$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "electron-shell" } | Select-Object -First 1 +if (-not $worldSurfaceLoweringClaim) { + throw "EveElectron manifest missing electron-shell world-surface lowering claim" +} +if ($worldSurfaceLoweringClaim.supportLevel -ne "electron-shell-surface-tree-command-surface") { + throw "Unexpected EveElectron world-surface support level: $($worldSurfaceLoweringClaim.supportLevel)" +} +if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") { + throw "Unexpected EveElectron world-surface ownership: $($worldSurfaceLoweringClaim.ownership)" +} +foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) { + $absoluteEvidencePath = Join-Path $projectRoot $evidencePath + if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) { + throw "EveElectron world-surface claim missing evidence path: $evidencePath" + } } foreach ($pluginId in @("sai.vn", "norn.graph", "tex.math")) { diff --git a/scripts/run-eveelectron-provider-shell-smoke.ps1 b/scripts/run-eveelectron-provider-shell-smoke.ps1 index 4b49394..c757034 100644 --- a/scripts/run-eveelectron-provider-shell-smoke.ps1 +++ b/scripts/run-eveelectron-provider-shell-smoke.ps1 @@ -24,12 +24,46 @@ if ($manifest.lifecycle.test.status -ne "provider-shell-contract-skeleton") { throw "Unexpected EveElectron provider shell status: $($manifest.lifecycle.test.status)" } -foreach ($feature in @("providerAdvertisements", "commandTransport")) { +foreach ($feature in @("providerAdvertisements", "commandTransport", "surfaceTreeProjection")) { if (-not (@($manifest.supportedFeatures) -contains $feature)) { throw "EveElectron provider shell missing supported feature: $feature" } } +$worldSurfaceLoweringClaims = @() +if ($null -ne $manifest.worldSurfaceLowering) { + $worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering) +} +if ($worldSurfaceLoweringClaims.Count -ne 1) { + throw "EveElectron must claim exactly one provider world-surface lowering target" +} +$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "electron-shell" } | Select-Object -First 1 +if (-not $worldSurfaceLoweringClaim) { + throw "EveElectron missing electron-shell world-surface lowering claim" +} +foreach ($surfaceKind in @("interactive-world", "interactive-world-editor")) { + if (-not (@($worldSurfaceLoweringClaim.surfaceKinds) -contains $surfaceKind)) { + throw "EveElectron world-surface claim missing surface kind: $surfaceKind" + } +} +foreach ($projectionKind in @("provider-authored-world-surface", "provider-authored-world-editor-surface")) { + if (-not (@($worldSurfaceLoweringClaim.projectionKinds) -contains $projectionKind)) { + throw "EveElectron world-surface claim missing projection kind: $projectionKind" + } +} +if ($worldSurfaceLoweringClaim.supportLevel -ne "electron-shell-surface-tree-command-surface") { + throw "Unexpected EveElectron support level: $($worldSurfaceLoweringClaim.supportLevel)" +} +if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") { + throw "Unexpected EveElectron ownership: $($worldSurfaceLoweringClaim.ownership)" +} +foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) { + $absoluteEvidencePath = Join-Path $projectRoot $evidencePath + if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) { + throw "EveElectron world-surface claim missing evidence path: $evidencePath" + } +} + $expectedFiles = @( "runtimes\incubating\eve-electron\package.json", "runtimes\incubating\eve-electron\src\eve-electron-shell.mjs", @@ -44,14 +78,14 @@ foreach ($relativePath in $expectedFiles) { } $shellSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-electron\src\eve-electron-shell.mjs") -Raw -foreach ($symbol in @("EveElectronShell", "selectSurface", "createCommandIntent", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell")) { +foreach ($symbol in @("EveElectronShell", "selectSurface", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "shellElementKind", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell")) { if (-not $shellSource.Contains($symbol)) { throw "EveElectron provider shell source missing symbol: $symbol" } } $testSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-electron\test\eve-electron-shell.test.mjs") -Raw -foreach ($symbol in @("selects the active advertised provider surface", "command intents carry provider-advertised boundaries", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1")) { +foreach ($symbol in @("selects the active advertised provider surface", "lowers provider surface trees into an Electron shell projection", "rejects surface documents that do not match the advertised target", "command intents carry provider-advertised boundaries", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1")) { if (-not $testSource.Contains($symbol)) { throw "EveElectron provider shell test missing symbol: $symbol" } diff --git a/scripts/run-eveelectron-split-handoff-smoke.ps1 b/scripts/run-eveelectron-split-handoff-smoke.ps1 index 92d1bf4..b955be9 100644 --- a/scripts/run-eveelectron-split-handoff-smoke.ps1 +++ b/scripts/run-eveelectron-split-handoff-smoke.ps1 @@ -34,7 +34,7 @@ if ($handoff.runtimeId -ne "electron-shell") { } $moveSets = @($handoff.moveSets) -foreach ($stage in @("runtime-body", "command", "capture")) { +foreach ($stage in @("runtime-body", "command", "world-surface-lowering", "capture")) { $moveSet = $moveSets | Where-Object { $_.stage -eq $stage } | Select-Object -First 1 if (-not $moveSet) { throw "EveElectron split handoff missing move set for stage: $stage" @@ -53,7 +53,7 @@ foreach ($stage in @("runtime-body", "command", "capture")) { } } -foreach ($id in @("electron-shell-runtime", "electron-command-transport")) { +foreach ($id in @("electron-shell-runtime", "electron-command-transport", "electron-world-surface-lowering")) { $moveSet = $moveSets | Where-Object { $_.id -eq $id } | Select-Object -First 1 if (-not $moveSet) { throw "EveElectron split handoff missing move set: $id" diff --git a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 index fcca0a1..1313bea 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -58,6 +58,7 @@ node $consumerScript $consumerExport ` --expect-runtime-feature unity-scene:sceneGraphProjection ` --expect-runtime-feature electron-shell:providerAdvertisements ` --expect-runtime-feature electron-shell:commandTransport ` + --expect-runtime-feature electron-shell:surfaceTreeProjection ` --expect-runtime-feature tui:providerAdvertisements ` --expect-runtime-feature tui:commandTransport ` --expect-runtime-feature tui:terminalGridSummary ` @@ -71,6 +72,9 @@ node $consumerScript $consumerExport ` --expect-runtime-world-target unity-scene:unity-scene ` --expect-runtime-world-field unity-scene:unity-scene:supportLevel:unity-scene-graph-command-surface ` --expect-runtime-world-field unity-scene:unity-scene:ownership:runtime-lowers-provider-world-surface-without-owning-world-state ` + --expect-runtime-world-target electron-shell:electron-shell ` + --expect-runtime-world-field electron-shell:electron-shell:supportLevel:electron-shell-surface-tree-command-surface ` + --expect-runtime-world-field electron-shell:electron-shell:ownership:runtime-lowers-provider-world-surface-without-owning-world-state ` --expect-runtime-plugin-projection unity-uitoolkit:sai.vn:supported:EveUnity ` --expect-runtime-plugin-projection unity-uitoolkit:norn.graph:supported:EveUnity ` --expect-runtime-plugin-projection unity-uitoolkit:tex.math:unsupported:EveUnity ` @@ -89,6 +93,7 @@ node $consumerScript $consumerExport ` --expect-runtime-plugin-gap unity-scene:tex.math:EveUnity ` --expect-runtime-plugin-gap electron-shell:sai.vn:EveElectron ` --expect-runtime-plugin-gap electron-shell:norn.graph:EveElectron ` + --expect-runtime-plugin-gap electron-shell:tex.math:EveElectron ` --expect-runtime-handoff unity-uitoolkit ` --expect-runtime-command-schema web:gamecult.eve.command.v1 ` --expect-runtime-command-schema windows-flutter:gamecult.eve.command.v1 ` diff --git a/scripts/run-split-target-conformance-consumer-smoke.ps1 b/scripts/run-split-target-conformance-consumer-smoke.ps1 index c8b9faf..9c04e6f 100644 --- a/scripts/run-split-target-conformance-consumer-smoke.ps1 +++ b/scripts/run-split-target-conformance-consumer-smoke.ps1 @@ -63,8 +63,10 @@ try { --expect-split-target-blocker-record EveElectron:runtime-feature:electron-shell:embeddedDocuments ` --expect-split-handoff-move EveElectron:electron-shell:electron-shell-runtime:current:exists:eve-electron-shell.mjs ` --expect-split-handoff-move EveElectron:electron-shell:electron-command-transport:current:exists:eve-electron-shell.mjs ` + --expect-split-handoff-move EveElectron:electron-shell:electron-world-surface-lowering:current:exists:eve-electron-shell.mjs ` --expect-split-handoff-move EveElectron:electron-shell:electron-shell-runtime:observed-provider:exists:Aetheria.Rts.Web/Electron ` --expect-split-handoff-move EveElectron:electron-shell:electron-command-transport:observed-provider:exists:Aetheria.Rts.Web/Client/app.ts ` + --expect-split-handoff-move EveElectron:electron-shell:electron-world-surface-lowering:observed-provider:exists:Aetheria.Rts.Web/Client ` --expect-split-target-blocker "EveElectron:Electron shell runtime body graduates to EveElectron outside Aetheria product code" ` --expect-split-target EveTui ` --expect-split-target-status EveTui:incubating ` diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index 5cd1df1..0533e19 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -620,7 +620,7 @@ "status": "pending", "runtimes": ["electron-shell"], "requiredRuntimeStatuses": ["active"], - "requiredFeatures": ["providerAdvertisements", "embeddedDocuments"], + "requiredFeatures": ["providerAdvertisements", "surfaceTreeProjection", "embeddedDocuments"], "requiredPlugins": [], "proofs": [ { @@ -643,7 +643,7 @@ ] }, { - "description": "EveElectron provider-shell skeleton selects advertised surfaces and emits Eve command intents.", + "description": "EveElectron provider-shell skeleton selects advertised surfaces, projects Eve surface trees, and emits Eve command intents.", "status": "passed", "evidencePaths": [ "runtimes/incubating/eve-electron/package.json", @@ -722,7 +722,7 @@ "splitHandoffPath": "runtimes/incubating/eve-electron/eveelectron-split-handoff.json", "requiredFixtures": ["embedded-surface", "aetheria-world"], "pluginFixtures": [], - "supportedFeatures": ["providerAdvertisements", "commandTransport"], + "supportedFeatures": ["providerAdvertisements", "commandTransport", "surfaceTreeProjection"], "supportedPlugins": [], "unsupportedPlugins": [ { @@ -738,6 +738,20 @@ "reason": "Electron provider-shell skeleton exists, but no TeX projection adapter exists in this runtime." } ], + "worldSurfaceLowering": [ + { + "targetId": "electron-shell", + "surfaceKinds": ["interactive-world", "interactive-world-editor"], + "projectionKinds": ["provider-authored-world-surface", "provider-authored-world-editor-surface"], + "supportLevel": "electron-shell-surface-tree-command-surface", + "ownership": "runtime-lowers-provider-world-surface-without-owning-world-state", + "evidencePaths": [ + "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", + "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", + "web/fixtures/aetheria.provider-advertisement.json" + ] + } + ], "expectedPaths": [ "runtimes/incubating/eve-electron/package.json", "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", @@ -750,16 +764,16 @@ "expectedSourceSymbols": [ { "path": "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs", - "contains": ["EveElectronShell", "selectSurface", "createCommandIntent", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell"] + "contains": ["EveElectronShell", "selectSurface", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "shellElementKind", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell"] }, { "path": "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs", - "contains": ["selects the active advertised provider surface", "command intents carry provider-advertised boundaries", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] + "contains": ["selects the active advertised provider surface", "lowers provider surface trees into an Electron shell projection", "rejects surface documents that do not match the advertised target", "command intents carry provider-advertised boundaries", "aetheria.daemon.commands", "aetheria.eve_command_acceptance_status.v1"] } ], "capture": { "status": "missing", - "note": "Electron capture is a split blocker until EveElectron owns a provider-agnostic shell." + "note": "Electron capture is a split blocker until EveElectron attaches provider-agnostic shell window artifacts." } }, { diff --git a/web/fixtures/aetheria.provider-advertisement.json b/web/fixtures/aetheria.provider-advertisement.json index 59bf2cb..d9bbd84 100644 --- a/web/fixtures/aetheria.provider-advertisement.json +++ b/web/fixtures/aetheria.provider-advertisement.json @@ -105,10 +105,10 @@ ], "commandBoundary": "aetheria.daemon.commands", "receiptSchema": "aetheria.eve_command_acceptance_status.v1", - "loweringTargets": ["web-reference", "unity-uitoolkit", "unity-scene", "tui"], + "loweringTargets": ["web-reference", "unity-uitoolkit", "unity-scene", "electron-shell", "tui"], "ownership": "provider-owns-world-state-assets-command-acceptance-and-receipts" }, - "summary": "Authoritative daemon game surface consumed by Unity and web lowerers." + "summary": "Authoritative daemon game surface consumed by Unity, Electron, and web lowerers." }, { "schema": "gamecult.eve.surface.v1", @@ -138,7 +138,7 @@ ], "commandBoundary": "aetheria.daemon.commands", "receiptSchema": "aetheria.eve_command_acceptance_status.v1", - "loweringTargets": ["web-reference", "unity-uitoolkit", "tui"], + "loweringTargets": ["web-reference", "unity-uitoolkit", "electron-shell", "tui"], "ownership": "provider-owns-editor-state-assets-command-acceptance-and-receipts" } } @@ -176,10 +176,11 @@ "styleCapabilities": [ { "styleProfile": "aetheria.daemon.world", - "preferredLowerings": ["unity-uitoolkit", "web-reference", "tui"], + "preferredLowerings": ["unity-uitoolkit", "web-reference", "electron-shell", "tui"], "tokenGroups": ["aetheria.world", "aetheria.daemon", "aetheria.commandBoundary"], "lossiness": { "web-reference": "proves generic Eve lowering and command/binding shape; does not own Aetheria assets or simulation truth", + "electron-shell": "projects the same Eve surface tree inside a generic Electron shell; does not own Aetheria assets, IPC truth, or simulation truth", "tui": "dense command/state inspection only" } }