Add EveElectron plugin projection shells

This commit is contained in:
Meta Vens 2026-07-09 13:20:30 +01:00
commit 88068c35ae
11 changed files with 260 additions and 53 deletions

View file

@ -392,8 +392,9 @@ Recently cut:
- Runtime/plugin projection claims are now exported as root
`runtimePluginProjectionCoverage[]` records. Runtime-owner smokes assert
Unity UI Toolkit support for `sai.vn`, `norn.graph`, and `tex.math`, Unity
scene support for the same three plugin capabilities, and the pending
Electron unsupported Sai projection until its generic lowerer exists.
scene support for the same three plugin capabilities, and Electron shell
support for the same sidecar plugin projection shells without importing
plugin semantics.
- Provider/runtime/plugin projection coverage is now exported as
`providerRuntimePluginProjectionCoverage[]`. The runtime-owner smoke proves
the Sai VN provider surface lowers fully in web, lowers in Unity UI Toolkit
@ -402,10 +403,10 @@ Recently cut:
impersonate required Sai VN, optional Norn graph, or optional TeX math
projection.
- Runtime/plugin projection gaps are now exported as
`runtimePluginProjectionGaps[]`. The runtime-owner smoke asserts Unity and
Electron plugin gaps as typed records, so missing Sai, Norn, or TeX projection
support is owned by the runtime split target and does not imply those plugin
semantics should move into Eve core or Aetheria product code.
`runtimePluginProjectionGaps[]`. The runtime-owner smoke asserts remaining
device-edge and TUI plugin gaps as typed records, so missing Sai, Norn, or TeX
projection support is owned by the runtime split target and does not imply
those plugin semantics should move into Eve core or Aetheria product code.
- Split target blockers are now exported as `splitTargetBlockers[]` and as
per-target `blockerRecords[]` with blocker kind, subject, owner, and source
text. The split-target consumer smoke asserts Unity scene status/plugin
@ -468,11 +469,11 @@ Recently cut:
- EveElectron now has a pending split target and handoff at
`runtimes/incubating/eve-electron/eveelectron-split-handoff.json`. It does
claim a provider-agnostic Electron shell surface-tree projection and command
surface, but not packaged rendering, embedded document rendering, plugin
projection, or captured window artifacts. 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 artifact proofs visible as blockers.
surface with Sai, Norn, and TeX sidecar plugin projection shells, but not
packaged rendering or captured window artifacts. 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, and
capture artifact proofs visible as blockers.
- EveElectron now has a direct split handoff smoke at
`scripts/run-eveelectron-split-handoff-smoke.ps1`. The smoke verifies the
current generic shell, command, and world-surface lowering paths, the observed
@ -483,14 +484,15 @@ Recently cut:
`runtimes/incubating/eve-electron/eve-runtime-capability.json` and a
lifecycle smoke at `scripts/run-eveelectron-lifecycle-smoke.ps1`. The
manifest declares provider advertisement consumption, command transport, and
surface-tree projection, claims `electron-shell` world/editor lowering, and
now carries executable release and capture request contracts. Release uses
surface-tree/plugin projection, claims `electron-shell` world/editor
lowering, and now carries executable release and capture request contracts.
Release uses
`tools/eveelectron/eveelectron-release-contract.mjs` and
`scripts/run-eveelectron-release-contract-smoke.ps1`; capture uses
`tools/eveelectron/eveelectron-capture-contract.mjs` and
`scripts/run-eveelectron-capture-contract-smoke.ps1`. The packaged Electron
app, packaged window rendering, plugin projection, and actual Electron window
capture artifacts remain pending EveElectron work.
app, packaged window rendering, and actual Electron window capture artifacts
remain 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
@ -864,10 +866,11 @@ Recently cut:
- The parity harness now emits an `EveConformance`-shaped export under
`artifacts/conformance/latest`, with a top-level index and per-pack JSON
files for core, plugin, provider, and runtime consumers.
- Unity's plugin projection capability gap is now explicitly demoted. The UI
Toolkit runtime owns generic projection and command requests, while Sai, Norn,
and TeX semantics remain sidecar-plugin responsibilities. Unity only adds
projection adapters for capabilities a plugin advertises through Eve.
- Unity and Electron plugin projection capability gaps are now explicitly
demoted. UI Toolkit, Unity scene, and Electron runtimes own generic projection
and command requests, while Sai, Norn, and TeX semantics remain sidecar-plugin
responsibilities. Runtimes only add projection shells for capabilities a
plugin advertises through Eve.
- Flutter and Unity now have runtime command-transport smoke evidence in the
parity report. Flutter emits `gamecult.eve.command.v1` intents from lowered
controls, and Unity command requests now carry the same command schema.

View file

@ -17,23 +17,38 @@
"providerAdvertisements",
"commandTransport",
"surfaceTreeProjection",
"embeddedDocuments"
"embeddedDocuments",
"pluginProjection"
],
"supportedPlugins": [],
"unsupportedPlugins": [
"supportedPlugins": [
{
"pluginId": "sai.vn",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for Sai sidecar-advertised VN projection in this runtime."
"capabilities": [
"vn.stage",
"story.choose",
"story.continue",
"story.jump"
],
"projectionAdapter": "EveElectronShell.buildPluginProjection"
},
{
"pluginId": "norn.graph",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for Norn sidecar-advertised graph projection in this runtime."
"capabilities": [
"embed.norn"
],
"projectionAdapter": "EveElectronShell.buildPluginProjection"
},
{
"pluginId": "tex.math",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for TeX sidecar-advertised math projection in this runtime."
"capabilities": [
"embed.tex",
"tex.inline",
"tex.block"
],
"projectionAdapter": "EveElectronShell.buildPluginProjection"
}
],
"unsupportedPlugins": [],
"worldSurfaceLowering": [
{
"targetId": "electron-shell",
@ -44,6 +59,7 @@
"evidencePaths": [
"runtimes/incubating/eve-electron/src/eve-electron-shell.mjs",
"runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs",
"web/fixtures/sai-vn-surface.json",
"web/fixtures/aetheria.provider-advertisement.json"
]
}

View file

@ -15,7 +15,8 @@
"currentPaths": [
"runtimes/incubating/eve-electron/package.json",
"runtimes/incubating/eve-electron/src/eve-electron-shell.mjs",
"runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs"
"runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs",
"web/fixtures/sai-vn-surface.json"
],
"observedProviderPaths": [
"E:\\Projects\\Aetheria\\Aetheria.Rts.Web\\Electron",
@ -54,6 +55,18 @@
"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-plugin-projection",
"stage": "plugin-projection",
"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",
"web/fixtures/sai-vn-surface.json"
],
"destinationOwner": "EveElectron",
"replacementProof": "EveElectron owns shell projection metadata for Sai, Norn, and TeX sidecar-advertised components; Sai keeps story state, Norn keeps graph layout/state, and EvePlugins keeps TeX rendering semantics."
},
{
"id": "electron-capture-lifecycle",
"stage": "capture",
@ -78,12 +91,15 @@
"Aetheria RTS client modules as EveElectron runtime authority",
"Aetheria Starbridge state as Electron shell truth",
"provider-specific IPC contracts that bypass gamecult.eve.command.v1",
"plugin semantic internals",
"Sai story state, VN/Ink semantics, or story command interpretation as Electron shell authority",
"Norn graph layout, graph state, or command semantics as Electron shell authority",
"TeX parsing, typesetting, baseline metrics, or render-cache semantics as Electron shell authority",
"renderer-local product state as daemon truth"
],
"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, projects Eve surface trees, and emits gamecult.eve.command.v1 intents.",
"EveElectron projects Sai, Norn, and TeX sidecar plugin shells without importing plugin semantic internals.",
"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.",

View file

@ -116,16 +116,19 @@ function buildShellNode(component) {
const source = objectValue(component);
const children = Array.isArray(source.children) ? source.children.map(buildShellNode) : [];
const embeddedDocuments = normalizeEmbeddedDocuments(source.embeddedDocuments);
const componentKind = firstString(source.kind);
const pluginProjection = buildPluginProjection(componentKind, source);
return {
id: firstString(source.id),
componentKind: firstString(source.kind),
shellElementKind: shellElementKind(firstString(source.kind)),
componentKind,
shellElementKind: shellElementKind(componentKind),
props: objectValue(source.props),
layout: objectValue(source.layout),
style: objectValue(source.style),
stateBindingCount: Array.isArray(source.stateBindings) ? source.stateBindings.length : 0,
embeddedDocumentCount: embeddedDocuments.length,
embeddedDocuments,
...(pluginProjection ? { pluginProjection } : {}),
children,
};
}
@ -146,6 +149,9 @@ function normalizeEmbeddedDocuments(value) {
function shellElementKind(componentKind) {
if (!componentKind) return "empty";
if (componentKind === "vn.stage") return "sai-vn-stage-shell";
if (componentKind === "embed.norn") return "norn-graph-shell";
if (componentKind === "embed.tex") return "tex-math-shell";
if (componentKind.startsWith("control.")) return "command-control";
if (componentKind.startsWith("embed.")) return "plugin-placeholder";
if (componentKind === "surface.slot") return "embedded-surface-slot";
@ -154,6 +160,53 @@ function shellElementKind(componentKind) {
return "shell-node";
}
function buildPluginProjection(componentKind, component) {
if (componentKind === "vn.stage") {
return {
pluginId: "sai.vn",
projectionKind: "sai-vn-electron-stage-shell",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["vn.stage", "story.choose", "story.continue", "story.jump"],
documentId: firstString(component.props?.storyId, component.id),
semanticOwner: "Sai",
};
}
if (componentKind === "embed.norn") {
return {
pluginId: "norn.graph",
projectionKind: "norn-graph-electron-overlay-shell",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.norn"],
documentId: firstString(component.props?.sourceUri, component.props?.documentId, component.id),
semanticOwner: "Norn",
};
}
if (componentKind === "embed.tex") {
return {
pluginId: "tex.math",
projectionKind: texProjectionKind(component),
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.tex", "tex.inline", "tex.block"],
documentId: firstString(component.props?.sourceUri, component.props?.source, component.id),
semanticOwner: "EvePlugins",
};
}
return null;
}
function texProjectionKind(component) {
const display = firstString(component.props?.display, "inline");
if (display === "block") return "tex-math-electron-block-source-shell";
if (display === "page") return "tex-math-electron-page-source-shell";
return "tex-math-electron-inline-source-shell";
}
function normalizeWorldInteraction(value) {
const source = objectValue(value);
return {

View file

@ -64,7 +64,9 @@ test("lowers provider surface trees into an Electron shell projection", () => {
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].shellElementKind, "norn-graph-shell");
assert.equal(projection.root.children[2].pluginProjection.pluginId, "norn.graph");
assert.equal(projection.root.children[2].pluginProjection.semanticOwner, "Norn");
assert.equal(projection.root.children[2].embeddedDocumentCount, 1);
assert.deepEqual(projection.root.children[2].embeddedDocuments, [
{
@ -76,6 +78,41 @@ test("lowers provider surface trees into an Electron shell projection", () => {
]);
});
test("lowers Sai, Norn, and TeX sidecar plugin shells without owning semantics", () => {
const shell = new EveElectronShell();
const saiSurface = JSON.parse(readFileSync(path.join(repoRoot, "web/fixtures/sai-vn-surface.json"), "utf8"));
const projection = shell.lowerSurface(saiSurface, saiAdvertisement(), "sai.visual_novel.surface");
assert.deepEqual(validateSchemaSubset(electronProjectionSchema, projection), []);
assert.equal(projection.providerId, "gamecult.home.vn");
assert.equal(projection.surfaceId, "sai.visual_novel.surface");
assert.equal(projection.root.shellElementKind, "sai-vn-stage-shell");
assert.equal(projection.root.pluginProjection.pluginId, "sai.vn");
assert.equal(projection.root.pluginProjection.semanticOwner, "Sai");
assert.equal(projection.root.pluginProjection.commandBoundary, "sidecar-advertised-plugin-abi");
assert.deepEqual(projection.root.pluginProjection.capabilities, [
"vn.stage",
"story.choose",
"story.continue",
"story.jump",
]);
const norn = findNode(projection.root, "sai.graph");
assert.equal(norn.shellElementKind, "norn-graph-shell");
assert.equal(norn.pluginProjection.pluginId, "norn.graph");
assert.equal(norn.pluginProjection.projectionKind, "norn-graph-electron-overlay-shell");
assert.equal(norn.pluginProjection.semanticOwner, "Norn");
assert.deepEqual(norn.pluginProjection.capabilities, ["embed.norn"]);
const tex = findNode(projection.root, "sai.tex.log-power");
assert.equal(tex.shellElementKind, "tex-math-shell");
assert.equal(tex.pluginProjection.pluginId, "tex.math");
assert.equal(tex.pluginProjection.projectionKind, "tex-math-electron-block-source-shell");
assert.equal(tex.pluginProjection.semanticOwner, "EvePlugins");
assert.equal(tex.pluginProjection.documentId, "\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor");
assert.deepEqual(tex.pluginProjection.capabilities, ["embed.tex", "tex.inline", "tex.block"]);
});
test("rejects surface documents that do not match the advertised target", () => {
const shell = new EveElectronShell();
assert.throws(
@ -115,6 +152,26 @@ function advertisement() {
};
}
function saiAdvertisement() {
return {
schema: "gamecult.eve.provider_advertisement.v1",
providerId: "gamecult.home.vn",
surfaces: [
{
surfaceId: "sai.visual_novel.surface",
transport: "local-json",
url: "web/fixtures/sai-vn-surface.json",
worldInteraction: {
projectionKind: "provider-authored-world-surface",
commandBoundary: "sai.vn.plugin.commands",
receiptSchema: "gamecult.eve.command_receipt.v1",
ownership: "provider-owns-story-state-plugin-sidecars-own-nested-semantics",
},
},
],
};
}
function surfaceDocument(surfaceId = "aetheria.daemon.game") {
return {
type: "surface-state",
@ -175,3 +232,12 @@ function surfaceDocument(surfaceId = "aetheria.daemon.game") {
commands: [],
};
}
function findNode(root, id) {
if (root.id === id) return root;
for (const child of root.children || []) {
const found = findNode(child, id);
if (found) return found;
}
return null;
}

View file

@ -39,13 +39,30 @@ if (-not $manifest.incubation.splitHandoff.manifestPath) {
throw "EveElectron manifest missing split handoff path"
}
foreach ($feature in @("providerAdvertisements", "commandTransport", "surfaceTreeProjection", "embeddedDocuments")) {
foreach ($feature in @("providerAdvertisements", "commandTransport", "surfaceTreeProjection", "embeddedDocuments", "pluginProjection")) {
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 runtime projection adapters for sidecar-advertised capabilities exist"
$expectedPlugins = @{
"sai.vn" = @("vn.stage", "story.choose", "story.continue", "story.jump")
"norn.graph" = @("embed.norn")
"tex.math" = @("embed.tex", "tex.inline", "tex.block")
}
foreach ($pluginId in $expectedPlugins.Keys) {
$supported = @($manifest.supportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if (-not $supported) {
throw "EveElectron manifest missing supported plugin declaration: $pluginId"
}
foreach ($capability in $expectedPlugins[$pluginId]) {
if (-not (@($supported.capabilities) -contains $capability)) {
throw "EveElectron supported plugin $pluginId missing capability: $capability"
}
}
if ($supported.projectionAdapter -ne "EveElectronShell.buildPluginProjection") {
throw "EveElectron supported plugin $pluginId has unexpected projection adapter: $($supported.projectionAdapter)"
}
}
$worldSurfaceLoweringClaims = @()
@ -72,10 +89,10 @@ foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) {
}
}
foreach ($pluginId in @("sai.vn", "norn.graph", "tex.math")) {
foreach ($pluginId in $expectedPlugins.Keys) {
$unsupported = @($manifest.unsupportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if (-not $unsupported) {
throw "EveElectron manifest missing unsupported plugin declaration: $pluginId"
if ($unsupported) {
throw "EveElectron manifest must not report $pluginId unsupported while the shell projection is declared"
}
}

View file

@ -24,12 +24,19 @@ 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", "surfaceTreeProjection")) {
foreach ($feature in @("providerAdvertisements", "commandTransport", "surfaceTreeProjection", "pluginProjection")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveElectron provider shell missing supported feature: $feature"
}
}
foreach ($pluginId in @("sai.vn", "norn.graph", "tex.math")) {
$supported = @($manifest.supportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
if (-not $supported) {
throw "EveElectron provider shell missing supported plugin projection: $pluginId"
}
}
$worldSurfaceLoweringClaims = @()
if ($null -ne $manifest.worldSurfaceLowering) {
$worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering)
@ -80,14 +87,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", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "shellElementKind", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell")) {
foreach ($symbol in @("EveElectronShell", "selectSurface", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "buildPluginProjection", "shellElementKind", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "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", "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")) {
foreach ($symbol in @("selects the active advertised provider surface", "lowers provider surface trees into an Electron shell projection", "lowers Sai, Norn, and TeX sidecar plugin shells without owning semantics", "sai-vn-stage-shell", "norn-graph-shell", "tex-math-shell", "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"
}

View file

@ -34,7 +34,7 @@ if ($handoff.runtimeId -ne "electron-shell") {
}
$moveSets = @($handoff.moveSets)
foreach ($stage in @("runtime-body", "command", "world-surface-lowering", "capture")) {
foreach ($stage in @("runtime-body", "command", "world-surface-lowering", "plugin-projection", "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"
@ -77,6 +77,20 @@ if (@($captureMoveSet.currentPaths).Count -eq 0) {
throw "EveElectron capture lifecycle move set must declare currentPaths"
}
$pluginMoveSet = $moveSets | Where-Object { $_.id -eq "electron-plugin-projection" } | Select-Object -First 1
if (-not $pluginMoveSet) {
throw "EveElectron split handoff missing move set: electron-plugin-projection"
}
foreach ($relativePath in @(
"runtimes/incubating/eve-electron/src/eve-electron-shell.mjs",
"runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs",
"web/fixtures/sai-vn-surface.json"
)) {
if (-not (@($pluginMoveSet.currentPaths) -contains $relativePath)) {
throw "EveElectron plugin projection move set missing current path: $relativePath"
}
}
foreach ($contract in @(
"gamecult.eve.surface.v1",
"gamecult.eve.command.v1",
@ -96,4 +110,14 @@ foreach ($proof in @($handoff.requiredExternalProofs)) {
}
}
foreach ($forbiddenImport in @(
"Sai story state, VN/Ink semantics, or story command interpretation as Electron shell authority",
"Norn graph layout, graph state, or command semantics as Electron shell authority",
"TeX parsing, typesetting, baseline metrics, or render-cache semantics as Electron shell authority"
)) {
if (-not (@($handoff.forbiddenImports) -contains $forbiddenImport)) {
throw "EveElectron split handoff missing forbidden import: $forbiddenImport"
}
}
Write-Host "EveElectron split handoff smoke passed: $absoluteHandoffPath"

View file

@ -67,6 +67,7 @@ node $consumerScript $consumerExport `
--expect-runtime-feature electron-shell:commandTransport `
--expect-runtime-feature electron-shell:surfaceTreeProjection `
--expect-runtime-feature electron-shell:embeddedDocuments `
--expect-runtime-feature electron-shell:pluginProjection `
--expect-runtime-feature unity-scene:embeddedDocuments `
--expect-runtime-feature tui:providerAdvertisements `
--expect-runtime-feature tui:commandTransport `
@ -95,7 +96,9 @@ node $consumerScript $consumerExport `
--expect-runtime-plugin-projection unity-scene:sai.vn:supported:EveUnity `
--expect-runtime-plugin-projection unity-scene:norn.graph:supported:EveUnity `
--expect-runtime-plugin-projection unity-scene:tex.math:supported:EveUnity `
--expect-runtime-plugin-projection electron-shell:sai.vn:unsupported:EveElectron `
--expect-runtime-plugin-projection electron-shell:sai.vn:supported:EveElectron `
--expect-runtime-plugin-projection electron-shell:norn.graph:supported:EveElectron `
--expect-runtime-plugin-projection electron-shell:tex.math:supported:EveElectron `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:web:sai.vn:supported:Eve `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:web:tex.math:optional-supported:Eve `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:unity-uitoolkit:sai.vn:supported:EveUnity `
@ -104,11 +107,11 @@ node $consumerScript $consumerExport `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:unity-scene:sai.vn:supported:EveUnity `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:unity-scene:norn.graph:optional-supported:EveUnity `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:unity-scene:tex.math:optional-supported:EveUnity `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:electron-shell:sai.vn:supported:EveElectron `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:electron-shell:norn.graph:optional-supported:EveElectron `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:electron-shell:tex.math:optional-supported:EveElectron `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:ios-uikit:sai.vn:unsupported-runtime-projection:EveIOS `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:ios-uikit:norn.graph:optional-unsupported-runtime-projection:EveIOS `
--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-plugin-gap tui:sai.vn:EveTui `
--expect-runtime-plugin-gap tui:norn.graph:EveTui `
--expect-runtime-plugin-gap tui:tex.math:EveTui `

View file

@ -66,6 +66,7 @@ try {
--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-plugin-projection:current:exists:sai-vn-surface.json `
--expect-split-handoff-move EveElectron:electron-shell:electron-capture-lifecycle:current:exists:eveelectron-capture-contract.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 `

View file

@ -738,23 +738,23 @@
},
"splitHandoffPath": "runtimes/incubating/eve-electron/eveelectron-split-handoff.json",
"requiredFixtures": ["embedded-surface", "aetheria-world"],
"pluginFixtures": [],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "surfaceTreeProjection", "embeddedDocuments"],
"supportedPlugins": [],
"unsupportedPlugins": [
"pluginFixtures": ["sai-vn"],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "surfaceTreeProjection", "embeddedDocuments", "pluginProjection"],
"supportedPlugins": [
{
"pluginId": "sai.vn",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for Sai sidecar-advertised VN projection in this runtime."
"capabilities": ["vn.stage", "story.choose", "story.continue", "story.jump"]
},
{
"pluginId": "norn.graph",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for Norn sidecar-advertised graph projection in this runtime."
"capabilities": ["embed.norn"]
},
{
"pluginId": "tex.math",
"reason": "Electron provider-shell skeleton exists, but no adapter exists for TeX sidecar-advertised math projection in this runtime."
"capabilities": ["embed.tex", "tex.inline", "tex.block"]
}
],
"unsupportedPlugins": [],
"worldSurfaceLowering": [
{
"targetId": "electron-shell",
@ -765,6 +765,7 @@
"evidencePaths": [
"runtimes/incubating/eve-electron/src/eve-electron-shell.mjs",
"runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs",
"web/fixtures/sai-vn-surface.json",
"web/fixtures/aetheria.provider-advertisement.json"
]
}
@ -787,11 +788,11 @@
"expectedSourceSymbols": [
{
"path": "runtimes/incubating/eve-electron/src/eve-electron-shell.mjs",
"contains": ["EveElectronShell", "selectSurface", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "normalizeEmbeddedDocuments", "embeddedDocuments", "shellElementKind", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell"]
"contains": ["EveElectronShell", "selectSurface", "lowerSurface", "createCommandIntent", "normalizeSurfaceDocument", "buildShellNode", "buildPluginProjection", "normalizeEmbeddedDocuments", "embeddedDocuments", "shellElementKind", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "worldInteraction", "commandBoundary", "receiptSchema", "electron-shell"]
},
{
"path": "runtimes/incubating/eve-electron/test/eve-electron-shell.test.mjs",
"contains": ["selects the active advertised provider surface", "lowers provider surface trees into an Electron shell projection", "embeddedDocuments", "norn.map", "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"]
"contains": ["selects the active advertised provider surface", "lowers provider surface trees into an Electron shell projection", "lowers Sai, Norn, and TeX sidecar plugin shells without owning semantics", "embeddedDocuments", "norn.map", "sai-vn-stage-shell", "norn-graph-shell", "tex-math-shell", "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"]
},
{
"path": "tools/eveelectron/eveelectron-capture-contract.mjs",