Add EveTui plugin fallback projections

This commit is contained in:
Meta Vens 2026-07-09 13:25:20 +01:00
commit e1c635d033
12 changed files with 319 additions and 49 deletions

View file

@ -393,8 +393,8 @@ Recently cut:
`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 Electron shell
support for the same sidecar plugin projection shells without importing
plugin semantics.
and TUI 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
@ -404,7 +404,7 @@ Recently cut:
projection.
- Runtime/plugin projection gaps are now exported as
`runtimePluginProjectionGaps[]`. The runtime-owner smoke asserts remaining
device-edge and TUI plugin gaps as typed records, so missing Sai, Norn, or TeX
device-edge 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
@ -501,23 +501,25 @@ Recently cut:
- EveTui now has a pending split target and handoff at
`runtimes/incubating/eve-tui/evetui-split-handoff.json`. It claims the
advertised `tui` world-surface lowering target as a lossy terminal-grid
command surface. The handoff draws the line between the provider-agnostic
terminal/grid lowerer and provider-owned TUI surfaces, and the split-target
consumer smoke keeps runtime graduation and transcript/cell-grid capture
visible as blockers.
command surface and carries compact Sai, Norn, and TeX sidecar plugin
fallback shells. The handoff draws the line between the provider-agnostic
terminal/grid lowerer, plugin-owned semantics, and provider-owned TUI
surfaces, while the split-target consumer smoke keeps runtime graduation and
transcript/cell-grid capture ownership visible as blockers.
- EveTui now has a direct split handoff smoke at
`scripts/run-evetui-split-handoff-smoke.ps1`, a pending runtime capability
manifest at `runtimes/incubating/eve-tui/eve-runtime-capability.json`, and a
lifecycle smoke at `scripts/run-evetui-lifecycle-smoke.ps1`. The manifest
declares provider advertisement consumption, command transport, terminal-grid
summary, terminal-grid lowering, and the `tui` world/editor lowering claim;
it now carries an executable terminal capture request contract through
summary, terminal-grid lowering, sidecar plugin fallback projection, and the
`tui` world/editor lowering claim; it now carries an executable terminal
capture request contract through
`tools/evetui/evetui-capture-contract.mjs` and
`scripts/run-evetui-capture-contract-smoke.ps1`, plus a deterministic
`gamecult.eve.tui_grid.v1` JSON capture artifact through
`tools/evetui/evetui-capture-artifact.mjs` and
`scripts/run-evetui-capture-smoke.ps1`. Plugin projection, terminal package
release, and owner-repo production of the capture path remain pending.
`scripts/run-evetui-capture-smoke.ps1`. Terminal package release and
owner-repo production of the capture path remain pending.
- EveTui release lifecycle now carries a structured pending release request
contract. The runtime capability manifest reads version truth from
`runtimes/incubating/eve-tui/package.json`, names the
@ -866,11 +868,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 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.
- Unity, Electron, and TUI plugin projection capability gaps are now explicitly
demoted. UI Toolkit, Unity scene, Electron, and compact TUI runtimes own
generic projection and command requests, while Sai, Norn, and TeX semantics
remain sidecar-plugin responsibilities. Runtimes only add projection shells or
lossy terminal fallbacks 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

@ -18,23 +18,41 @@
"commandTransport",
"terminalGridSummary",
"terminalGridLowering",
"embeddedDocuments"
"embeddedDocuments",
"pluginProjection"
],
"supportedPlugins": [],
"unsupportedPlugins": [
"supportedPlugins": [
{
"pluginId": "sai.vn",
"reason": "TUI provider-shell skeleton exists, but no terminal adapter exists for Sai sidecar-advertised VN projection in this runtime."
"capabilities": [
"vn.stage",
"story.choose",
"story.continue",
"story.jump"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "terminal-summary"
},
{
"pluginId": "norn.graph",
"reason": "TUI provider-shell skeleton exists, but no terminal fallback exists for Norn sidecar-advertised graph projection in this runtime."
"capabilities": [
"embed.norn"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "terminal-outline"
},
{
"pluginId": "tex.math",
"reason": "TUI provider-shell skeleton exists, but no terminal fallback exists for TeX sidecar-advertised math projection in this runtime."
"capabilities": [
"embed.tex",
"tex.inline",
"tex.block"
],
"projectionAdapter": "EveTuiShell.buildPluginProjection",
"lossiness": "source-text"
}
],
"unsupportedPlugins": [],
"worldSurfaceLowering": [
{
"targetId": "tui",
@ -46,6 +64,7 @@
"evidencePaths": [
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"runtimes/incubating/eve-tui/test/eve-tui-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-tui/package.json",
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs"
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
"web/fixtures/sai-vn-surface.json"
],
"destinationOwner": "EveTui",
"replacementProof": "EveTui hosts a provider-agnostic TUI lowerer that consumes provider advertisements and Eve surface documents without importing provider internals."
@ -41,6 +42,18 @@
"destinationOwner": "EveTui",
"replacementProof": "TUI actions emit gamecult.eve.command.v1 intents through provider-advertised command boundaries and render provider receipts instead of mutating local state."
},
{
"id": "tui-plugin-projection",
"stage": "plugin-projection",
"currentPaths": [
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
"runtimes/incubating/eve-tui/eve-runtime-capability.json",
"web/fixtures/sai-vn-surface.json"
],
"destinationOwner": "EveTui",
"replacementProof": "EveTui owns lossy terminal fallback shells for Sai, Norn, and TeX sidecar-advertised components; Sai keeps story state, Norn keeps graph layout/state, and EvePlugins keeps TeX rendering semantics."
},
{
"id": "tui-capture-lifecycle",
"stage": "capture",
@ -68,12 +81,15 @@
"provider internals as TUI runtime authority",
"renderer-local product state as provider truth",
"terminal-only command routes that bypass gamecult.eve.command.v1",
"plugin semantic internals",
"Sai story state, VN/Ink semantics, or story command interpretation as TUI runtime authority",
"Norn graph layout, graph state, or command semantics as TUI runtime authority",
"TeX parsing, typesetting, baseline metrics, or render-cache semantics as TUI runtime authority",
"lossy text summaries pretending to be provider state"
],
"requiredExternalProofs": [
"EveTui publishes a runtime capability manifest with terminal/grid capabilities.",
"EveTui owns a provider-shell contract skeleton that selects advertised provider surfaces, lowers Eve surface trees, emits gamecult.eve.command.v1 intents, and produces an explicitly lossy terminal grid.",
"EveTui projects Sai, Norn, and TeX sidecar plugin fallback shells without importing plugin semantic internals.",
"EveTui owns the provider-agnostic terminal grid lowerer outside Eve incubation.",
"EveTui consumes at least one non-Aetheria provider advertisement.",
"EveTui consumes Aetheria's agent-facing TUI surface through provider advertisements without importing Aetheria internals.",

View file

@ -82,6 +82,7 @@ export class EveTuiShell {
width: this.width,
lines,
embeddedDocumentSlots: collectEmbeddedDocumentSlots(document.root),
pluginProjections: collectPluginProjections(document.root),
lossiness: "terminal-grid-command-surface; summarizes provider-authored world surface without owning provider state",
};
}
@ -154,7 +155,11 @@ function buildComponentLines(component, width, depth = 0) {
const props = objectValue(source.props);
const label = firstString(props.label, props.title, props.text, props.bind, props.command, props.fieldId);
const indent = " ".repeat(Math.min(depth, 6));
const ownLine = fitLine(`${indent}${terminalElementKind(kind)} ${id}${label ? ` ${label}` : ""}`, width);
const pluginProjection = buildPluginProjection(kind, source);
const pluginSuffix = pluginProjection
? ` [${pluginProjection.pluginId}:${pluginProjection.semanticOwner}]`
: "";
const ownLine = fitLine(`${indent}${terminalElementKind(kind)} ${id}${label ? ` ${label}` : ""}${pluginSuffix}`, width);
const children = Array.isArray(source.children) ? source.children : [];
const embeddedDocuments = normalizeEmbeddedDocuments(source.embeddedDocuments);
return [
@ -176,6 +181,21 @@ function collectEmbeddedDocumentSlots(component, slots = []) {
return slots;
}
function collectPluginProjections(component, projections = []) {
const source = objectValue(component);
const projection = buildPluginProjection(firstString(source.kind), source);
if (projection) {
projections.push({
ownerId: firstString(source.id),
...projection,
});
}
for (const child of Array.isArray(source.children) ? source.children : []) {
collectPluginProjections(child, projections);
}
return projections;
}
function normalizeEmbeddedDocuments(value) {
return Array.isArray(value)
? value
@ -192,6 +212,9 @@ function normalizeEmbeddedDocuments(value) {
function terminalElementKind(componentKind) {
if (!componentKind) return "empty";
if (componentKind === "vn.stage") return "sai-vn";
if (componentKind === "embed.norn") return "norn";
if (componentKind === "embed.tex") return "tex";
if (componentKind.startsWith("control.")) return "command";
if (componentKind.startsWith("embed.")) return "plugin";
if (componentKind === "surface.slot") return "slot";
@ -201,6 +224,57 @@ function terminalElementKind(componentKind) {
return componentKind;
}
function buildPluginProjection(componentKind, component) {
const props = objectValue(component.props);
if (componentKind === "vn.stage") {
return {
pluginId: "sai.vn",
projectionKind: "sai-vn-terminal-stage-summary",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["vn.stage", "story.choose", "story.continue", "story.jump"],
documentId: firstString(props.storyId, component.id),
semanticOwner: "Sai",
fallbackKind: "terminal-summary",
};
}
if (componentKind === "embed.norn") {
return {
pluginId: "norn.graph",
projectionKind: "norn-graph-terminal-outline",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.norn"],
documentId: firstString(props.sourceUri, props.documentId, component.id),
semanticOwner: "Norn",
fallbackKind: "terminal-outline",
};
}
if (componentKind === "embed.tex") {
return {
pluginId: "tex.math",
projectionKind: texProjectionKind(props),
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.tex", "tex.inline", "tex.block"],
documentId: firstString(props.sourceUri, props.source, component.id),
semanticOwner: "EvePlugins",
fallbackKind: "source-text",
};
}
return null;
}
function texProjectionKind(props) {
const display = firstString(props.display, "inline");
if (display === "block") return "tex-math-terminal-block-source";
if (display === "page") return "tex-math-terminal-page-source";
return "tex-math-terminal-inline-source";
}
function fitLine(value, width) {
const text = String(value || "");
if (text.length <= width) return text;

View file

@ -74,8 +74,21 @@ test("lowers provider surface trees into a terminal grid", () => {
assert.ok(grid.lines.every(line => line.length <= 44));
assert.ok(grid.lines.some(line => line.includes("world aetheria.daemon.game.gravity")));
assert.ok(grid.lines.some(line => line.includes("command aetheria.daemon.game.focus")));
assert.ok(grid.lines.some(line => line.includes("plugin aetheria.daemon.game.norn")));
assert.ok(grid.lines.some(line => line.includes("norn aetheria.daemon.game.norn")));
assert.ok(grid.lines.some(line => line.includes("slot norn.map")));
assert.deepEqual(grid.pluginProjections, [
{
ownerId: "aetheria.daemon.game.norn",
pluginId: "norn.graph",
projectionKind: "norn-graph-terminal-outline",
abiSchema: "gamecult.eve.plugin_abi.v1",
commandBoundary: "sidecar-advertised-plugin-abi",
capabilities: ["embed.norn"],
documentId: "aetheria.daemon.game.norn",
semanticOwner: "Norn",
fallbackKind: "terminal-outline",
},
]);
assert.deepEqual(grid.embeddedDocumentSlots, [
{
ownerId: "aetheria.daemon.game.norn",
@ -95,6 +108,36 @@ test("rejects surface documents that do not match the advertised TUI target", ()
);
});
test("lowers Sai, Norn, and TeX plugin surfaces into compact terminal fallbacks", () => {
const shell = new EveTuiShell({ width: 72 });
const saiSurface = JSON.parse(readFileSync(path.join(repoRoot, "web/fixtures/sai-vn-surface.json"), "utf8"));
const grid = shell.lowerSurface(saiSurface, saiAdvertisement(), "sai.visual_novel.surface");
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
assert.equal(grid.providerId, "gamecult.home.vn");
assert.equal(grid.surfaceId, "sai.visual_novel.surface");
assert.ok(grid.lines.every(line => line.length <= 72));
assert.ok(grid.lines.some(line => line.includes("sai-vn sai.root")));
assert.ok(grid.lines.some(line => line.includes("norn sai.graph")));
assert.ok(grid.lines.some(line => line.includes("tex sai.tex.log-power")));
const byPlugin = new Map(grid.pluginProjections.map(projection => [projection.pluginId, projection]));
assert.equal(byPlugin.get("sai.vn").semanticOwner, "Sai");
assert.equal(byPlugin.get("sai.vn").projectionKind, "sai-vn-terminal-stage-summary");
assert.deepEqual(byPlugin.get("sai.vn").capabilities, [
"vn.stage",
"story.choose",
"story.continue",
"story.jump",
]);
assert.equal(byPlugin.get("norn.graph").semanticOwner, "Norn");
assert.equal(byPlugin.get("norn.graph").fallbackKind, "terminal-outline");
assert.equal(byPlugin.get("tex.math").semanticOwner, "EvePlugins");
assert.equal(byPlugin.get("tex.math").projectionKind, "tex-math-terminal-block-source");
assert.equal(byPlugin.get("tex.math").fallbackKind, "source-text");
assert.equal(byPlugin.get("tex.math").documentId, "\\\\mathrm{votes}(p)=1+\\\\lfloor\\\\log_b(1+p)\\\\rfloor");
});
function advertisement() {
return {
schema: "gamecult.eve.provider_advertisement.v1",
@ -127,6 +170,27 @@ function advertisement() {
};
}
function saiAdvertisement() {
return {
schema: "gamecult.eve.provider_advertisement.v1",
providerId: "gamecult.home.vn",
title: "GameCult Compound 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",

View file

@ -81,6 +81,57 @@
"additionalProperties": true
}
},
"pluginProjections": {
"type": "array",
"items": {
"type": "object",
"required": [
"ownerId",
"pluginId",
"projectionKind",
"semanticOwner",
"fallbackKind"
],
"properties": {
"ownerId": {
"type": "string",
"minLength": 1
},
"pluginId": {
"type": "string",
"minLength": 1
},
"projectionKind": {
"type": "string",
"minLength": 1
},
"abiSchema": {
"type": "string"
},
"commandBoundary": {
"type": "string"
},
"capabilities": {
"type": "array",
"items": {
"type": "string"
}
},
"documentId": {
"type": "string"
},
"semanticOwner": {
"type": "string",
"minLength": 1
},
"fallbackKind": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true
}
},
"lossiness": {
"type": "string",
"minLength": 1

View file

@ -36,13 +36,30 @@ if ($manifest.incubation.splitTarget -ne "EveTui") {
throw "Unexpected EveTui split target: $($manifest.incubation.splitTarget)"
}
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments")) {
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments", "pluginProjection")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveTui manifest missing provider-shell feature: $feature"
}
}
if (@($manifest.supportedPlugins).Count -ne 0) {
throw "EveTui must not claim plugin projection before terminal 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 "EveTui manifest missing supported plugin declaration: $pluginId"
}
foreach ($capability in $expectedPlugins[$pluginId]) {
if (-not (@($supported.capabilities) -contains $capability)) {
throw "EveTui supported plugin $pluginId missing capability: $capability"
}
}
if ($supported.projectionAdapter -ne "EveTuiShell.buildPluginProjection") {
throw "EveTui supported plugin $pluginId has unexpected projection adapter: $($supported.projectionAdapter)"
}
}
$worldSurfaceLoweringClaims = @()
@ -69,10 +86,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 "EveTui manifest missing unsupported plugin declaration: $pluginId"
if ($unsupported) {
throw "EveTui manifest must not report $pluginId unsupported while terminal fallback projection is declared"
}
}

View file

@ -24,12 +24,19 @@ if ($manifest.lifecycle.test.status -ne "provider-shell-contract-skeleton") {
throw "Unexpected EveTui provider shell status: $($manifest.lifecycle.test.status)"
}
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering")) {
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "pluginProjection")) {
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
throw "EveTui 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 "EveTui provider shell missing supported plugin projection: $pluginId"
}
}
$worldSurfaceLoweringClaims = @()
if ($null -ne $manifest.worldSurfaceLowering) {
$worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering)
@ -78,14 +85,14 @@ foreach ($relativePath in $expectedFiles) {
}
$shellSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-tui\src\eve-tui-shell.mjs") -Raw
foreach ($symbol in @("EveTuiShell", "selectSurface", "lowerSurface", "normalizeSurfaceDocument", "buildComponentLines", "terminalElementKind", "createCommandIntent", "renderSummary", "gamecult.eve.tui_grid.v1", "commandBoundary", "receiptSchema")) {
foreach ($symbol in @("EveTuiShell", "selectSurface", "lowerSurface", "normalizeSurfaceDocument", "buildComponentLines", "buildPluginProjection", "collectPluginProjections", "terminalElementKind", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "createCommandIntent", "renderSummary", "gamecult.eve.tui_grid.v1", "commandBoundary", "receiptSchema")) {
if (-not $shellSource.Contains($symbol)) {
throw "EveTui provider shell source missing symbol: $symbol"
}
}
$testSource = Get-Content -LiteralPath (Join-Path $projectRoot "runtimes\incubating\eve-tui\test\eve-tui-shell.test.mjs") -Raw
foreach ($symbol in @("selects advertised provider surfaces", "command intents carry provider-advertised boundaries", "summary grid is explicitly lossy", "lowers provider surface trees into a terminal grid", "rejects surface documents that do not match the advertised TUI target", "aetheria.daemon.commands")) {
foreach ($symbol in @("selects advertised provider surfaces", "command intents carry provider-advertised boundaries", "summary grid is explicitly lossy", "lowers provider surface trees into a terminal grid", "lowers Sai, Norn, and TeX plugin surfaces into compact terminal fallbacks", "pluginProjections", "sai-vn-terminal-stage-summary", "norn-graph-terminal-outline", "tex-math-terminal-block-source", "rejects surface documents that do not match the advertised TUI target", "aetheria.daemon.commands")) {
if (-not $testSource.Contains($symbol)) {
throw "EveTui provider shell test missing symbol: $symbol"
}

View file

@ -42,6 +42,7 @@ $expectedMoveSets = @{
"tui-runtime-body" = "runtime-body"
"tui-world-surface-lowering" = "world-surface-lowering"
"tui-command-transport" = "command"
"tui-plugin-projection" = "plugin-projection"
"tui-capture-lifecycle" = "capture"
}
$moveSets = @($handoff.moveSets)
@ -61,7 +62,7 @@ foreach ($id in $expectedMoveSets.Keys) {
}
$currentPaths = if ($null -eq $moveSet.currentPaths) { @() } else { @($moveSet.currentPaths) }
$observedProviderPaths = if ($null -eq $moveSet.observedProviderPaths) { @() } else { @($moveSet.observedProviderPaths) }
if ($id -in @("tui-runtime-body", "tui-world-surface-lowering", "tui-command-transport", "tui-capture-lifecycle")) {
if ($id -in @("tui-runtime-body", "tui-world-surface-lowering", "tui-command-transport", "tui-plugin-projection", "tui-capture-lifecycle")) {
if ($currentPaths.Count -eq 0) {
throw "EveTui split handoff move set $id must name provider-shell skeleton paths"
}
@ -94,7 +95,9 @@ foreach ($forbiddenImport in @(
"provider internals as TUI runtime authority",
"renderer-local product state as provider truth",
"terminal-only command routes that bypass gamecult.eve.command.v1",
"plugin semantic internals",
"Sai story state, VN/Ink semantics, or story command interpretation as TUI runtime authority",
"Norn graph layout, graph state, or command semantics as TUI runtime authority",
"TeX parsing, typesetting, baseline metrics, or render-cache semantics as TUI runtime authority",
"lossy text summaries pretending to be provider state"
)) {
if (-not (@($handoff.forbiddenImports) -contains $forbiddenImport)) {
@ -102,6 +105,17 @@ foreach ($forbiddenImport in @(
}
}
$pluginMoveSet = $moveSets | Where-Object { $_.id -eq "tui-plugin-projection" } | Select-Object -First 1
foreach ($relativePath in @(
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
"web/fixtures/sai-vn-surface.json"
)) {
if (-not (@($pluginMoveSet.currentPaths) -contains $relativePath)) {
throw "EveTui plugin projection move set missing current path: $relativePath"
}
}
foreach ($proof in @($handoff.requiredExternalProofs)) {
if (-not $proof) {
throw "EveTui split handoff contains an empty required external proof"

View file

@ -74,6 +74,7 @@ node $consumerScript $consumerExport `
--expect-runtime-feature tui:terminalGridSummary `
--expect-runtime-feature tui:terminalGridLowering `
--expect-runtime-feature tui:embeddedDocuments `
--expect-runtime-feature tui:pluginProjection `
--expect-runtime-feature direct2d:embeddedDocuments `
--expect-runtime-world-target web:web-reference `
--expect-runtime-world-field web:web-reference:supportLevel:reference-dom-canvas-command-boundary `
@ -99,6 +100,9 @@ node $consumerScript $consumerExport `
--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-runtime-plugin-projection tui:sai.vn:supported:EveTui `
--expect-runtime-plugin-projection tui:norn.graph:supported:EveTui `
--expect-runtime-plugin-projection tui:tex.math:supported:EveTui `
--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 `
@ -110,11 +114,11 @@ node $consumerScript $consumerExport `
--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:tui:sai.vn:supported:EveTui `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:tui:norn.graph:optional-supported:EveTui `
--expect-provider-runtime-plugin-projection gamecult.home.vn:sai.visual_novel.surface:tui:tex.math:optional-supported:EveTui `
--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 tui:sai.vn:EveTui `
--expect-runtime-plugin-gap tui:norn.graph:EveTui `
--expect-runtime-plugin-gap tui:tex.math:EveTui `
--expect-runtime-handoff unity-uitoolkit `
--expect-runtime-command-schema web:gamecult.eve.command.v1 `
--expect-runtime-command-schema windows-flutter:gamecult.eve.command.v1 `

View file

@ -80,6 +80,7 @@ try {
--expect-split-handoff-move EveTui:tui:tui-runtime-body:current:exists:eve-tui-shell.mjs `
--expect-split-handoff-move EveTui:tui:tui-world-surface-lowering:current:exists:eve-tui-shell.mjs `
--expect-split-handoff-move EveTui:tui:tui-command-transport:current:exists:eve-tui-shell.mjs `
--expect-split-handoff-move EveTui:tui:tui-plugin-projection:current:exists:sai-vn-surface.json `
--expect-split-handoff-move EveTui:tui:tui-capture-lifecycle:current:exists:evetui-capture-contract.mjs `
--expect-split-target-blocker "EveTui:TUI runtime body graduates to EveTui outside provider product code"
if ($LASTEXITCODE -ne 0) {

View file

@ -830,23 +830,23 @@
},
"splitHandoffPath": "runtimes/incubating/eve-tui/evetui-split-handoff.json",
"requiredFixtures": ["embedded-surface", "aetheria-world"],
"pluginFixtures": [],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments"],
"supportedPlugins": [],
"unsupportedPlugins": [
"pluginFixtures": ["sai-vn"],
"supportedFeatures": ["providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments", "pluginProjection"],
"supportedPlugins": [
{
"pluginId": "sai.vn",
"reason": "TUI provider-shell skeleton exists, but no terminal adapter exists for Sai sidecar-advertised VN projection in this runtime."
"capabilities": ["vn.stage", "story.choose", "story.continue", "story.jump"]
},
{
"pluginId": "norn.graph",
"reason": "TUI provider-shell skeleton exists, but no terminal fallback exists for Norn sidecar-advertised graph projection in this runtime."
"capabilities": ["embed.norn"]
},
{
"pluginId": "tex.math",
"reason": "TUI provider-shell skeleton exists, but no terminal fallback exists for TeX sidecar-advertised math projection in this runtime."
"capabilities": ["embed.tex", "tex.inline", "tex.block"]
}
],
"unsupportedPlugins": [],
"worldSurfaceLowering": [
{
"targetId": "tui",
@ -858,6 +858,7 @@
"evidencePaths": [
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
"web/fixtures/sai-vn-surface.json",
"web/fixtures/aetheria.provider-advertisement.json"
]
}
@ -884,11 +885,11 @@
"expectedSourceSymbols": [
{
"path": "runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
"contains": ["EveTuiShell", "selectSurface", "lowerSurface", "normalizeSurfaceDocument", "buildComponentLines", "collectEmbeddedDocumentSlots", "normalizeEmbeddedDocuments", "embeddedDocumentSlots", "terminalElementKind", "createCommandIntent", "renderSummary", "gamecult.eve.tui_grid.v1", "commandBoundary", "receiptSchema"]
"contains": ["EveTuiShell", "selectSurface", "lowerSurface", "normalizeSurfaceDocument", "buildComponentLines", "buildPluginProjection", "collectPluginProjections", "collectEmbeddedDocumentSlots", "normalizeEmbeddedDocuments", "embeddedDocumentSlots", "pluginProjections", "terminalElementKind", "sai.vn", "norn.graph", "tex.math", "sidecar-advertised-plugin-abi", "createCommandIntent", "renderSummary", "gamecult.eve.tui_grid.v1", "commandBoundary", "receiptSchema"]
},
{
"path": "runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
"contains": ["selects advertised provider surfaces", "command intents carry provider-advertised boundaries", "summary grid is explicitly lossy", "lowers provider surface trees into a terminal grid", "embeddedDocumentSlots", "norn.map", "rejects surface documents that do not match the advertised TUI target", "aetheria.daemon.commands"]
"contains": ["selects advertised provider surfaces", "command intents carry provider-advertised boundaries", "summary grid is explicitly lossy", "lowers provider surface trees into a terminal grid", "lowers Sai, Norn, and TeX plugin surfaces into compact terminal fallbacks", "embeddedDocumentSlots", "pluginProjections", "norn.map", "sai-vn-terminal-stage-summary", "norn-graph-terminal-outline", "tex-math-terminal-block-source", "rejects surface documents that do not match the advertised TUI target", "aetheria.daemon.commands"]
},
{
"path": "tools/evetui/evetui-capture-contract.mjs",