Graduate TUI runtime from Eve
This commit is contained in:
parent
294b3ec5b4
commit
afdbbc4375
23 changed files with 9 additions and 2168 deletions
|
|
@ -155,12 +155,14 @@ advertisements and typed command/receipt documents are the boundary.
|
||||||
- EveConformance CI runs that locked public workspace on every push and uploads
|
- EveConformance CI runs that locked public workspace on every push and uploads
|
||||||
a commit-keyed evidence bundle. Version tags attach the same lock, reports,
|
a commit-keyed evidence bundle. Version tags attach the same lock, reports,
|
||||||
capability matrix, packs, captures, and typed witnesses to a GitHub release.
|
capability matrix, packs, captures, and typed witnesses to a GitHub release.
|
||||||
|
- EveTui owns the terminal lowerer, command-intent projection, package and test
|
||||||
|
lifecycle, and durable Aetheria/generic-world grid captures. Its `0.1.1`
|
||||||
|
package is built from the owner repo; Eve no longer contains a TUI runtime.
|
||||||
|
|
||||||
## Remaining Work
|
## Remaining Work
|
||||||
|
|
||||||
### Runtime Graduation
|
### Runtime Graduation
|
||||||
|
|
||||||
- Extract EveTui when its process lifecycle and release surface are stable.
|
|
||||||
- Move Android Kotlin and iOS UIKit lowerers into runtime owner repos or retire
|
- Move Android Kotlin and iOS UIKit lowerers into runtime owner repos or retire
|
||||||
them when Flutter is the intended native owner.
|
them when Flutter is the intended native owner.
|
||||||
- Tag EveFlutter releases and remove remaining incubation claims.
|
- Tag EveFlutter releases and remove remaining incubation claims.
|
||||||
|
|
|
||||||
|
|
@ -464,7 +464,7 @@ exist. The current lifecycle proof is deliberately split:
|
||||||
rendered Unity frame.
|
rendered Unity frame.
|
||||||
|
|
||||||
TUI capture evidence is already typed even though EveTui remains incubating.
|
TUI capture evidence is already typed even though EveTui remains incubating.
|
||||||
`tools/evetui/evetui-capture-artifact.mjs` lowers the Aetheria world surface
|
`EveTui/tools/evetui/evetui-capture-artifact.mjs` lowers the Aetheria world surface
|
||||||
through `EveTuiShell` into `gamecult.eve.tui_grid.v1` and writes
|
through `EveTuiShell` into `gamecult.eve.tui_grid.v1` and writes
|
||||||
`artifacts/evetui-capture/latest/tui-grid.json`. The parity export includes
|
`artifacts/evetui-capture/latest/tui-grid.json`. The parity export includes
|
||||||
that record as `runtime.captureArtifacts[]`; runtime-owner consumers can assert
|
that record as `runtime.captureArtifacts[]`; runtime-owner consumers can assert
|
||||||
|
|
|
||||||
|
|
@ -1,195 +0,0 @@
|
||||||
{
|
|
||||||
"schema": "gamecult.eve.runtime_capability.v1",
|
|
||||||
"runtimeId": "tui",
|
|
||||||
"title": "Compact TUI",
|
|
||||||
"owner": "EveTui",
|
|
||||||
"incubation": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"currentHostRepo": "Eve",
|
|
||||||
"splitTarget": "EveTui",
|
|
||||||
"graduationTrigger": "Split when TUI has a provider-agnostic terminal/grid lowerer, provider advertisement consumption, command transport, terminal capability manifest, and transcript/cell-grid capture evidence.",
|
|
||||||
"splitHandoff": {
|
|
||||||
"manifestPath": "runtimes/incubating/eve-tui/evetui-split-handoff.json",
|
|
||||||
"smokeCommand": "powershell -ExecutionPolicy Bypass -File .\\scripts\\run-evetui-split-handoff-smoke.ps1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"supportedFeatures": [
|
|
||||||
"providerAdvertisements",
|
|
||||||
"commandTransport",
|
|
||||||
"terminalGridSummary",
|
|
||||||
"terminalGridLowering",
|
|
||||||
"embeddedDocuments",
|
|
||||||
"pluginProjection"
|
|
||||||
],
|
|
||||||
"supportedPlugins": [
|
|
||||||
{
|
|
||||||
"pluginId": "sai.vn",
|
|
||||||
"capabilities": [
|
|
||||||
"vn.stage",
|
|
||||||
"story.choose",
|
|
||||||
"story.continue",
|
|
||||||
"story.jump"
|
|
||||||
],
|
|
||||||
"projectionAdapter": "EveTuiShell.buildPluginProjection",
|
|
||||||
"lossiness": "terminal-summary"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pluginId": "norn.graph",
|
|
||||||
"capabilities": [
|
|
||||||
"embed.norn"
|
|
||||||
],
|
|
||||||
"projectionAdapter": "EveTuiShell.buildPluginProjection",
|
|
||||||
"lossiness": "terminal-outline"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pluginId": "tex.math",
|
|
||||||
"capabilities": [
|
|
||||||
"embed.tex",
|
|
||||||
"tex.inline",
|
|
||||||
"tex.block"
|
|
||||||
],
|
|
||||||
"projectionAdapter": "EveTuiShell.buildPluginProjection",
|
|
||||||
"lossiness": "source-text"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"unsupportedPlugins": [],
|
|
||||||
"worldSurfaceLowering": [
|
|
||||||
{
|
|
||||||
"targetId": "tui",
|
|
||||||
"surfaceKinds": ["interactive-world", "interactive-world-editor"],
|
|
||||||
"projectionKinds": ["provider-authored-world-surface", "provider-authored-world-editor-surface"],
|
|
||||||
"supportLevel": "terminal-grid-command-surface",
|
|
||||||
"ownership": "runtime-lowers-provider-world-surface-without-owning-world-state",
|
|
||||||
"lossiness": "terminal-grid-summary-only",
|
|
||||||
"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",
|
|
||||||
"../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
|
|
||||||
"web/fixtures/eve-world-smoke.provider-advertisement.json"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"commandTransport": {
|
|
||||||
"schema": "gamecult.eve.command_invocation.v1",
|
|
||||||
"status": "provider-shell-contract-skeleton",
|
|
||||||
"emitter": "EveTuiShell.createCommandIntent",
|
|
||||||
"clientId": "tui",
|
|
||||||
"authority": "TUI emits command intents through provider-advertised command boundaries; providers accept, deny, and publish receipts."
|
|
||||||
},
|
|
||||||
"lifecycle": {
|
|
||||||
"release": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"status": "pending-tui-package",
|
|
||||||
"releaseContract": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"packageName": "eve-tui",
|
|
||||||
"packageRoot": "runtimes/incubating/eve-tui",
|
|
||||||
"versionSource": "runtimes/incubating/eve-tui/package.json",
|
|
||||||
"tagPattern": "evetui-v{version}",
|
|
||||||
"artifactKind": "terminal-runtime",
|
|
||||||
"requestSchema": "gamecult.eve.runtime_release_request.v1",
|
|
||||||
"requestBuilder": "tools/evetui/evetui-release-contract.mjs",
|
|
||||||
"artifactPattern": "artifacts/evetui-release/{version}/eve-tui-{version}.tgz",
|
|
||||||
"publishProof": "Terminal runtime release is created from the EveTui repo and references the provider-agnostic TUI package root."
|
|
||||||
},
|
|
||||||
"evidencePaths": [
|
|
||||||
"runtimes/incubating/eve-tui/package.json",
|
|
||||||
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
|
|
||||||
"runtimes/incubating/eve-tui/evetui-split-handoff.json",
|
|
||||||
"tools/evetui/evetui-release-contract.mjs",
|
|
||||||
"tools/evetui/evetui-release-contract.test.mjs",
|
|
||||||
"scripts/run-evetui-release-contract-smoke.ps1",
|
|
||||||
"scripts/run-evetui-split-handoff-smoke.ps1"
|
|
||||||
],
|
|
||||||
"pendingProofs": [
|
|
||||||
"TUI package release is cut from EveTui rather than Eve incubation."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"status": "provider-shell-contract-skeleton",
|
|
||||||
"testContract": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"runnerKind": "terminal-provider-shell-smoke",
|
|
||||||
"runnerScript": "scripts/run-evetui-provider-shell-smoke.ps1",
|
|
||||||
"consumerProject": "E:\\Projects\\Aetheria",
|
|
||||||
"packageName": "eve-tui",
|
|
||||||
"testAssembly": "eve-tui-shell.test.mjs",
|
|
||||||
"testPlatform": "Node terminal-grid contract skeleton",
|
|
||||||
"resultsArtifact": "artifacts/evetui-provider-shell/{stamp}/provider-shell-results.json",
|
|
||||||
"logArtifact": "artifacts/evetui-provider-shell/{stamp}/provider-shell.log",
|
|
||||||
"manifestMutation": "None; the TUI consumes provider advertisements and must not mutate provider product manifests."
|
|
||||||
},
|
|
||||||
"evidencePaths": [
|
|
||||||
"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/evetui-split-handoff.json",
|
|
||||||
"scripts/run-evetui-split-handoff-smoke.ps1",
|
|
||||||
"scripts/run-evetui-provider-shell-smoke.ps1"
|
|
||||||
],
|
|
||||||
"pendingProofs": [
|
|
||||||
"TUI provider-advertisement smoke runs from EveTui with durable terminal transcript or cell-grid artifacts."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"capture": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"status": "cell-grid-json-capture",
|
|
||||||
"captureContract": {
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"runtimeId": "tui",
|
|
||||||
"targetId": "tui",
|
|
||||||
"requestSchema": "gamecult.eve.runtime_capture_request.v1",
|
|
||||||
"requestBuilder": "tools/evetui/evetui-capture-contract.mjs",
|
|
||||||
"advertisementPath": "../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
|
|
||||||
"captureKind": "terminal-cell-grid",
|
|
||||||
"artifactKind": "json-grid",
|
|
||||||
"artifactPattern": "artifacts/evetui-capture/{stamp}/tui-grid.json",
|
|
||||||
"conformanceAttachment": "runtime.captureArtifacts[]",
|
|
||||||
"requiredProvider": "aetheria",
|
|
||||||
"requiredSurface": "aetheria.daemon.game",
|
|
||||||
"additionalProviderSurfaces": [
|
|
||||||
{
|
|
||||||
"providerId": "eve.world-smoke",
|
|
||||||
"surfaceId": "eve.world-smoke.surface",
|
|
||||||
"purpose": "Generic non-product interactive-world capture control."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"providerId": "aetheria",
|
|
||||||
"surfaceId": "aetheria.daemon.editor",
|
|
||||||
"purpose": "Aetheria interactive-world-editor capture control."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"authority": "Capture observes terminal projection only; provider state, command acceptance, and receipts stay provider-owned.",
|
|
||||||
"publishProof": "Terminal cell-grid JSON capture artifact is attached to the runtime conformance report from EveTui."
|
|
||||||
},
|
|
||||||
"evidencePaths": [
|
|
||||||
"tools/evetui/evetui-capture-contract.mjs",
|
|
||||||
"tools/evetui/evetui-capture-contract.test.mjs",
|
|
||||||
"tools/evetui/evetui-capture-artifact.mjs",
|
|
||||||
"tools/evetui/evetui-capture-artifact.test.mjs",
|
|
||||||
"scripts/run-evetui-capture-contract-smoke.ps1",
|
|
||||||
"scripts/run-evetui-capture-smoke.ps1"
|
|
||||||
],
|
|
||||||
"pendingProofs": [
|
|
||||||
"TUI capture artifact is produced from the EveTui owner repo rather than Eve incubation."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"capture": {
|
|
||||||
"status": "json-grid",
|
|
||||||
"note": "TUI attaches a provider-advertisement-backed gamecult.eve.tui_grid.v1 JSON capture artifact; packaged runtime ownership remains an EveTui split blocker.",
|
|
||||||
"artifacts": [
|
|
||||||
{
|
|
||||||
"kind": "json-grid",
|
|
||||||
"schema": "gamecult.eve.tui_grid.v1",
|
|
||||||
"path": "artifacts/evetui-capture/latest/tui-grid.json",
|
|
||||||
"providerId": "aetheria",
|
|
||||||
"surfaceId": "aetheria.daemon.game",
|
|
||||||
"sourceSurfacePath": "../Aetheria/conformance/eve/aetheria-world-surface.json",
|
|
||||||
"requestPath": "artifacts/evetui-capture/latest/capture-request.json"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
{
|
|
||||||
"schema": "gamecult.eve.runtime_split_handoff.v1",
|
|
||||||
"splitTarget": "EveTui",
|
|
||||||
"ownerRepo": "EveTui",
|
|
||||||
"currentHostRepo": "Eve",
|
|
||||||
"runtimeId": "tui",
|
|
||||||
"packageName": "eve-tui",
|
|
||||||
"sourcePackageRoot": "runtimes/incubating/eve-tui",
|
|
||||||
"destinationPackageRoot": "packages/eve-tui",
|
|
||||||
"authorityBoundary": "EveTui owns compact terminal/grid projection, keyboard command input, dense state inspection, terminal capability manifests, capture transcripts, and TUI-specific style/token lowering. Providers such as Aetheria own live state, authored TUI surfaces, command authorization, receipts, and product-specific flows. Eve keeps the GUI/TUI surface contracts, command descriptors, provider advertisements, plugin advertisements, and conformance export contracts.",
|
|
||||||
"moveSets": [
|
|
||||||
{
|
|
||||||
"id": "tui-runtime-body",
|
|
||||||
"stage": "runtime-body",
|
|
||||||
"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",
|
|
||||||
"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."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "tui-world-surface-lowering",
|
|
||||||
"stage": "world-surface-lowering",
|
|
||||||
"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"
|
|
||||||
],
|
|
||||||
"destinationOwner": "EveTui",
|
|
||||||
"replacementProof": "EveTui claims the provider-advertised tui world-surface lowering target with explicit support level, lossiness, and command boundary evidence; transcript capture remains a separate lifecycle proof."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "tui-command-transport",
|
|
||||||
"stage": "command",
|
|
||||||
"currentPaths": [
|
|
||||||
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
|
|
||||||
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs"
|
|
||||||
],
|
|
||||||
"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",
|
|
||||||
"currentPaths": [
|
|
||||||
"tools/evetui/evetui-capture-contract.mjs",
|
|
||||||
"tools/evetui/evetui-capture-contract.test.mjs",
|
|
||||||
"tools/evetui/evetui-capture-artifact.mjs",
|
|
||||||
"tools/evetui/evetui-capture-artifact.test.mjs",
|
|
||||||
"scripts/run-evetui-capture-contract-smoke.ps1",
|
|
||||||
"scripts/run-evetui-capture-smoke.ps1"
|
|
||||||
],
|
|
||||||
"destinationOwner": "EveTui",
|
|
||||||
"replacementProof": "EveTui builds a provider-advertisement-backed terminal capture request and deterministic gamecult.eve.tui_grid.v1 JSON capture artifact now; the split is not complete until EveTui owns that capture path outside Eve incubation."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contractInputs": [
|
|
||||||
"gamecult.eve.surface.v1",
|
|
||||||
"gamecult.eve.command.v1",
|
|
||||||
"gamecult.eve.provider_advertisement.v1",
|
|
||||||
"gamecult.eve.plugin_advertisement.v1",
|
|
||||||
"gamecult.eve.plugin.v1",
|
|
||||||
"gamecult.eve.conformance_export.v1"
|
|
||||||
],
|
|
||||||
"forbiddenImports": [
|
|
||||||
"provider internals as TUI runtime authority",
|
|
||||||
"renderer-local product state as provider truth",
|
|
||||||
"terminal-only command routes that bypass gamecult.eve.command.v1",
|
|
||||||
"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.",
|
|
||||||
"EveTui emits gamecult.eve.command.v1 intents and renders provider receipts.",
|
|
||||||
"EveTui attaches terminal transcript or cell-grid capture artifacts to conformance output from its owner repo."
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
{
|
|
||||||
"name": "eve-tui",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"type": "module",
|
|
||||||
"description": "Provider-agnostic compact TUI shell skeleton for Eve surfaces.",
|
|
||||||
"main": "src/eve-tui-shell.mjs",
|
|
||||||
"scripts": {
|
|
||||||
"test": "C:\\Progra~1\\nodejs\\node.exe --test test/*.test.mjs"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"Eve",
|
|
||||||
"TUI",
|
|
||||||
"CultUI",
|
|
||||||
"provider-advertisement"
|
|
||||||
],
|
|
||||||
"license": "MPL-2.0"
|
|
||||||
}
|
|
||||||
|
|
@ -1,297 +0,0 @@
|
||||||
export class EveTuiShell {
|
|
||||||
constructor({ clientId = "tui", width = 80 } = {}) {
|
|
||||||
this.clientId = clientId;
|
|
||||||
this.width = Math.max(20, Number(width) || 80);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectSurface(providerAdvertisement, requestedSurfaceId = "") {
|
|
||||||
const provider = normalizeProviderAdvertisement(providerAdvertisement);
|
|
||||||
const surface = requestedSurfaceId
|
|
||||||
? provider.surfaces.find(candidate => candidate.surfaceId === requestedSurfaceId)
|
|
||||||
: provider.surfaces[0];
|
|
||||||
if (!surface) {
|
|
||||||
throw new Error(`Provider ${provider.providerId} does not advertise surface ${requestedSurfaceId || "(first)"}`);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
providerId: provider.providerId,
|
|
||||||
surfaceId: surface.surfaceId,
|
|
||||||
title: surface.title || provider.title || surface.surfaceId,
|
|
||||||
transport: surface.transport || "",
|
|
||||||
url: surface.url || surface.key || "",
|
|
||||||
worldInteraction: normalizeWorldInteraction(surface.worldInteraction),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
createCommandIntent(providerAdvertisement, requestedSurfaceId, command, payload = {}) {
|
|
||||||
if (!command) throw new Error("Command is required.");
|
|
||||||
const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId);
|
|
||||||
const action = objectValue(payload.action);
|
|
||||||
const commandBoundary = firstString(
|
|
||||||
selected.worldInteraction.commandBoundary,
|
|
||||||
payload.commandBoundary,
|
|
||||||
action.commandBoundary,
|
|
||||||
action.target,
|
|
||||||
);
|
|
||||||
const receiptSchema = firstString(
|
|
||||||
selected.worldInteraction.receiptSchema,
|
|
||||||
payload.receiptSchema,
|
|
||||||
action.receiptSchema,
|
|
||||||
);
|
|
||||||
const intent = {
|
|
||||||
type: "surface-command",
|
|
||||||
schema: "gamecult.eve.command_invocation.v1",
|
|
||||||
providerId: selected.providerId,
|
|
||||||
surfaceId: selected.surfaceId,
|
|
||||||
command,
|
|
||||||
payload: {
|
|
||||||
...payload,
|
|
||||||
transport: payload.transport ?? null,
|
|
||||||
},
|
|
||||||
issuedAt: new Date().toISOString(),
|
|
||||||
clientId: this.clientId,
|
|
||||||
};
|
|
||||||
if (commandBoundary) intent.commandBoundary = commandBoundary;
|
|
||||||
if (receiptSchema) intent.receiptSchema = receiptSchema;
|
|
||||||
return intent;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 TUI surface ${selected.surfaceId}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = [
|
|
||||||
fitLine(`EveTui ${selected.providerId}`, this.width),
|
|
||||||
fitLine(`surface ${selected.surfaceId}`, this.width),
|
|
||||||
fitLine(`command ${selected.worldInteraction.commandBoundary || "unadvertised"}`, this.width),
|
|
||||||
fitLine(`receipt ${selected.worldInteraction.receiptSchema || "unadvertised"}`, this.width),
|
|
||||||
...buildComponentLines(document.root, this.width),
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
schema: "gamecult.eve.tui_grid.v1",
|
|
||||||
runtimeId: "tui",
|
|
||||||
providerId: selected.providerId,
|
|
||||||
surfaceId: selected.surfaceId,
|
|
||||||
projectionKind: selected.worldInteraction.projectionKind,
|
|
||||||
commandBoundary: selected.worldInteraction.commandBoundary,
|
|
||||||
receiptSchema: selected.worldInteraction.receiptSchema,
|
|
||||||
ownership: selected.worldInteraction.ownership,
|
|
||||||
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",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSummary(providerAdvertisement, requestedSurfaceId = "") {
|
|
||||||
const selected = this.selectSurface(providerAdvertisement, requestedSurfaceId);
|
|
||||||
const lines = [
|
|
||||||
fitLine(`EveTui ${selected.providerId}`, this.width),
|
|
||||||
fitLine(`surface ${selected.surfaceId}`, this.width),
|
|
||||||
fitLine(`transport ${selected.transport || "unknown"}`, this.width),
|
|
||||||
fitLine(`command ${selected.worldInteraction.commandBoundary || "unadvertised"}`, this.width),
|
|
||||||
fitLine(`receipt ${selected.worldInteraction.receiptSchema || "unadvertised"}`, this.width),
|
|
||||||
];
|
|
||||||
return {
|
|
||||||
schema: "gamecult.eve.tui_grid.v1",
|
|
||||||
runtimeId: "tui",
|
|
||||||
providerId: selected.providerId,
|
|
||||||
surfaceId: selected.surfaceId,
|
|
||||||
width: this.width,
|
|
||||||
lines,
|
|
||||||
lossiness: "provider-shell-summary-only; use lowerSurface for TUI world-surface lowering",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function normalizeProviderAdvertisement(providerAdvertisement) {
|
|
||||||
if (!providerAdvertisement || typeof providerAdvertisement !== "object") {
|
|
||||||
throw new Error("Provider advertisement is required.");
|
|
||||||
}
|
|
||||||
const providerId = String(providerAdvertisement.providerId || "");
|
|
||||||
if (!providerId) throw new Error("Provider advertisement missing providerId.");
|
|
||||||
const surfaces = Array.isArray(providerAdvertisement.surfaces)
|
|
||||||
? providerAdvertisement.surfaces
|
|
||||||
.filter(surface => surface && typeof surface === "object" && surface.surfaceId)
|
|
||||||
.map(surface => ({ ...surface, surfaceId: String(surface.surfaceId) }))
|
|
||||||
: [];
|
|
||||||
if (!surfaces.length) throw new Error(`Provider ${providerId} advertises no surfaces.`);
|
|
||||||
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 normalizeWorldInteraction(value) {
|
|
||||||
const source = objectValue(value);
|
|
||||||
return {
|
|
||||||
projectionKind: firstString(source.projectionKind),
|
|
||||||
commandBoundary: firstString(source.commandBoundary),
|
|
||||||
receiptSchema: firstString(source.receiptSchema),
|
|
||||||
ownership: firstString(source.ownership),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildComponentLines(component, width, depth = 0) {
|
|
||||||
const source = objectValue(component);
|
|
||||||
const kind = firstString(source.kind) || "component";
|
|
||||||
const id = firstString(source.id) || "(anonymous)";
|
|
||||||
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 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 [
|
|
||||||
ownLine,
|
|
||||||
...embeddedDocuments.map(document => fitLine(`${indent} slot ${document.slotId || "(slot)"} ${document.documentId || "(document)"}`, width)),
|
|
||||||
...children.flatMap(child => buildComponentLines(child, width, depth + 1)),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectEmbeddedDocumentSlots(component, slots = []) {
|
|
||||||
const source = objectValue(component);
|
|
||||||
const ownerId = firstString(source.id);
|
|
||||||
for (const document of normalizeEmbeddedDocuments(source.embeddedDocuments)) {
|
|
||||||
slots.push({ ownerId, ...document });
|
|
||||||
}
|
|
||||||
for (const child of Array.isArray(source.children) ? source.children : []) {
|
|
||||||
collectEmbeddedDocumentSlots(child, 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
|
|
||||||
.filter(document => document && typeof document === "object")
|
|
||||||
.map(document => ({
|
|
||||||
slotId: firstString(document.slotId, document.id),
|
|
||||||
documentId: firstString(document.documentId, document.href, document.url),
|
|
||||||
schemaId: firstString(document.schemaId, document.schema),
|
|
||||||
presentationKind: firstString(document.presentationKind, document.kind),
|
|
||||||
}))
|
|
||||||
.filter(document => document.slotId || document.documentId)
|
|
||||||
: [];
|
|
||||||
}
|
|
||||||
|
|
||||||
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";
|
|
||||||
if (componentKind.startsWith("field.") || componentKind.startsWith("world.")) return "world";
|
|
||||||
if (componentKind.startsWith("text.") || componentKind === "text" || componentKind === "label") return "text";
|
|
||||||
if (componentKind === "metric") return "metric";
|
|
||||||
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",
|
|
||||||
availability: "required",
|
|
||||||
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",
|
|
||||||
availability: "optional-nested",
|
|
||||||
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",
|
|
||||||
availability: "optional-nested",
|
|
||||||
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;
|
|
||||||
return `${text.slice(0, Math.max(0, width - 3))}...`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function objectValue(value) {
|
|
||||||
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function firstString(...values) {
|
|
||||||
for (const value of values) {
|
|
||||||
if (typeof value === "string" && value.trim()) return value;
|
|
||||||
if (typeof value === "number" && Number.isFinite(value)) return String(value);
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
@ -1,256 +0,0 @@
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import test from "node:test";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import { validateSchemaSubset } from "../../test-support/schema-subset.mjs";
|
|
||||||
import { EveTuiShell } from "../src/eve-tui-shell.mjs";
|
|
||||||
|
|
||||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
|
|
||||||
const tuiGridSchema = JSON.parse(readFileSync(
|
|
||||||
path.join(repoRoot, "schemas/gamecult.eve.tui_grid.v1.schema.json"),
|
|
||||||
"utf8",
|
|
||||||
));
|
|
||||||
|
|
||||||
test("selects advertised provider surfaces for compact projection", () => {
|
|
||||||
const shell = new EveTuiShell({ width: 48 });
|
|
||||||
const selected = shell.selectSurface(advertisement(), "aetheria.daemon.editor");
|
|
||||||
|
|
||||||
assert.equal(selected.providerId, "aetheria");
|
|
||||||
assert.equal(selected.surfaceId, "aetheria.daemon.editor");
|
|
||||||
assert.equal(selected.worldInteraction.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(selected.worldInteraction.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("command intents carry provider-advertised boundaries", () => {
|
|
||||||
const shell = new EveTuiShell();
|
|
||||||
const intent = shell.createCommandIntent(
|
|
||||||
advertisement(),
|
|
||||||
"aetheria.daemon.game",
|
|
||||||
"aetheria.daemon.commands",
|
|
||||||
{
|
|
||||||
action: {
|
|
||||||
commandBoundary: "component.fallback.boundary",
|
|
||||||
receiptSchema: "component.fallback.receipt.v1",
|
|
||||||
target: "component.fallback.target",
|
|
||||||
},
|
|
||||||
commandId: "aetheria.daemon.focus",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
assert.equal(intent.schema, "gamecult.eve.command_invocation.v1");
|
|
||||||
assert.equal(intent.providerId, "aetheria");
|
|
||||||
assert.equal(intent.surfaceId, "aetheria.daemon.game");
|
|
||||||
assert.equal(intent.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(intent.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
assert.equal(intent.clientId, "tui");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("summary grid is explicitly lossy provider-shell evidence", () => {
|
|
||||||
const shell = new EveTuiShell({ width: 36 });
|
|
||||||
const grid = shell.renderSummary(advertisement(), "aetheria.daemon.game");
|
|
||||||
|
|
||||||
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
|
|
||||||
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
|
|
||||||
assert.equal(grid.runtimeId, "tui");
|
|
||||||
assert.equal(grid.surfaceId, "aetheria.daemon.game");
|
|
||||||
assert.match(grid.lossiness, /use lowerSurface/);
|
|
||||||
assert.ok(grid.lines.every(line => line.length <= 36));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("lowers provider surface trees into a terminal grid", () => {
|
|
||||||
const shell = new EveTuiShell({ width: 44 });
|
|
||||||
const grid = shell.lowerSurface(surfaceDocument(), advertisement(), "aetheria.daemon.game");
|
|
||||||
|
|
||||||
assert.deepEqual(validateSchemaSubset(tuiGridSchema, grid), []);
|
|
||||||
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
|
|
||||||
assert.equal(grid.runtimeId, "tui");
|
|
||||||
assert.equal(grid.providerId, "aetheria");
|
|
||||||
assert.equal(grid.surfaceId, "aetheria.daemon.game");
|
|
||||||
assert.equal(grid.projectionKind, "provider-authored-world-surface");
|
|
||||||
assert.equal(grid.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(grid.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
assert.match(grid.lossiness, /terminal-grid-command-surface/);
|
|
||||||
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("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",
|
|
||||||
availability: "optional-nested",
|
|
||||||
fallbackKind: "terminal-outline",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
assert.deepEqual(grid.embeddedDocumentSlots, [
|
|
||||||
{
|
|
||||||
ownerId: "aetheria.daemon.game.norn",
|
|
||||||
slotId: "norn.map",
|
|
||||||
documentId: "cultmesh://aetheria/norn/map",
|
|
||||||
schemaId: "gamecult.eve.surface.v1",
|
|
||||||
presentationKind: "terminal-grid-slot",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("rejects surface documents that do not match the advertised TUI target", () => {
|
|
||||||
const shell = new EveTuiShell();
|
|
||||||
assert.throws(
|
|
||||||
() => shell.lowerSurface(surfaceDocument("aetheria.daemon.game"), advertisement(), "aetheria.daemon.editor"),
|
|
||||||
/does not match advertised TUI surface/,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
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").availability, "required");
|
|
||||||
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").availability, "optional-nested");
|
|
||||||
assert.equal(byPlugin.get("norn.graph").fallbackKind, "terminal-outline");
|
|
||||||
assert.equal(byPlugin.get("tex.math").semanticOwner, "EvePlugins");
|
|
||||||
assert.equal(byPlugin.get("tex.math").availability, "optional-nested");
|
|
||||||
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",
|
|
||||||
providerId: "aetheria",
|
|
||||||
title: "Aetheria",
|
|
||||||
surfaces: [
|
|
||||||
{
|
|
||||||
surfaceId: "aetheria.daemon.game",
|
|
||||||
transport: "local-json",
|
|
||||||
url: "conformance/eve/aetheria-world-surface.json",
|
|
||||||
worldInteraction: {
|
|
||||||
projectionKind: "provider-authored-world-surface",
|
|
||||||
commandBoundary: "aetheria.daemon.commands",
|
|
||||||
receiptSchema: "aetheria.eve_command_acceptance_status.v1",
|
|
||||||
ownership: "provider-owns-world-state-assets-command-acceptance-and-receipts",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
surfaceId: "aetheria.daemon.editor",
|
|
||||||
transport: "local-json",
|
|
||||||
url: "conformance/eve/aetheria-world-surface.json",
|
|
||||||
worldInteraction: {
|
|
||||||
projectionKind: "provider-authored-world-editor-surface",
|
|
||||||
commandBoundary: "aetheria.daemon.commands",
|
|
||||||
receiptSchema: "aetheria.eve_command_acceptance_status.v1",
|
|
||||||
ownership: "provider-owns-editor-state-assets-command-acceptance-and-receipts",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
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}.gravity`,
|
|
||||||
kind: "field.surface2d",
|
|
||||||
props: {
|
|
||||||
label: "Current Zone Gravity",
|
|
||||||
bind: "aetheria.daemon.soaView.gravityField",
|
|
||||||
fieldId: "aetheria.zone.gravity",
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${surfaceId}.focus`,
|
|
||||||
kind: "control.button",
|
|
||||||
props: {
|
|
||||||
label: "Focus Relay",
|
|
||||||
command: "aetheria.daemon.commands",
|
|
||||||
commandId: "aetheria.daemon.focus",
|
|
||||||
},
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: `${surfaceId}.norn`,
|
|
||||||
kind: "embed.norn",
|
|
||||||
props: {
|
|
||||||
label: "Norn tactical map",
|
|
||||||
},
|
|
||||||
embeddedDocuments: [
|
|
||||||
{
|
|
||||||
slotId: "norn.map",
|
|
||||||
documentId: "cultmesh://aetheria/norn/map",
|
|
||||||
schemaId: "gamecult.eve.surface.v1",
|
|
||||||
presentationKind: "terminal-grid-slot",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
children: [],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
styles: [],
|
|
||||||
},
|
|
||||||
commands: [],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -86,12 +86,7 @@ try {
|
||||||
--expect-runtime-witness electron-shell:aetheria:aetheria.daemon.game:cold:pass `
|
--expect-runtime-witness electron-shell:aetheria:aetheria.daemon.game:cold:pass `
|
||||||
--expect-runtime-witness unity-scene:aetheria.daemon:aetheria.game:cold:pass `
|
--expect-runtime-witness unity-scene:aetheria.daemon:aetheria.game:cold:pass `
|
||||||
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:aetheria:aetheria.daemon.game `
|
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:aetheria:aetheria.daemon.game `
|
||||||
--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-target-proof "EveElectron:Aetheria consumes EveElectron for generic window and preload authority" `
|
--expect-split-target-proof "EveElectron:Aetheria consumes EveElectron for generic window and preload authority" `
|
||||||
--expect-split-target-blocker "EveTui:TUI package release is cut from EveTui rather than Eve incubation" `
|
|
||||||
--expect-split-target-blocker "EveTui:TUI capture path is owned by EveTui rather than Eve incubation" `
|
|
||||||
--expect-provider-command aetheria:aetheria.daemon.commands `
|
--expect-provider-command aetheria:aetheria.daemon.commands `
|
||||||
--expect-provider-receipt-state aetheria:accepted `
|
--expect-provider-receipt-state aetheria:accepted `
|
||||||
--expect-provider-receipt-state aetheria:pending `
|
--expect-provider-receipt-state aetheria:pending `
|
||||||
|
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $CapabilityManifestPath = "runtimes\incubating\eve-tui\eve-runtime-capability.json",
|
|
||||||
[string] $AdvertisementPath = "..\Aetheria\conformance\eve\aetheria.provider-advertisement.json",
|
|
||||||
[string] $OutputPath = "artifacts\evetui-capture-contract\latest\capture-request.json",
|
|
||||||
[string] $Stamp = "smoke"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$absoluteCapabilityPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
|
|
||||||
$CapabilityManifestPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $CapabilityManifestPath
|
|
||||||
}
|
|
||||||
$absoluteAdvertisementPath = if ([System.IO.Path]::IsPathRooted($AdvertisementPath)) {
|
|
||||||
$AdvertisementPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $AdvertisementPath
|
|
||||||
}
|
|
||||||
$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) {
|
|
||||||
$OutputPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $OutputPath
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($path in @($absoluteCapabilityPath, $absoluteAdvertisementPath)) {
|
|
||||||
if (-not (Test-Path -LiteralPath $path)) {
|
|
||||||
throw "EveTui capture contract input not found: $path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$manifest = Get-Content -LiteralPath $absoluteCapabilityPath -Raw | ConvertFrom-Json
|
|
||||||
$captureContract = $manifest.lifecycle.capture.captureContract
|
|
||||||
if ($null -eq $captureContract) {
|
|
||||||
throw "EveTui capture contract missing lifecycle.capture.captureContract"
|
|
||||||
}
|
|
||||||
foreach ($field in @("ownerRepo", "runtimeId", "targetId", "requestSchema", "requestBuilder", "advertisementPath", "captureKind", "artifactKind", "artifactPattern", "conformanceAttachment", "requiredProvider", "requiredSurface", "authority", "publishProof")) {
|
|
||||||
if (-not $captureContract.$field) {
|
|
||||||
throw "EveTui capture contract missing $field"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($captureContract.ownerRepo -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui capture owner: $($captureContract.ownerRepo)"
|
|
||||||
}
|
|
||||||
if ($captureContract.runtimeId -ne "tui") {
|
|
||||||
throw "Unexpected EveTui capture runtime: $($captureContract.runtimeId)"
|
|
||||||
}
|
|
||||||
if ($captureContract.targetId -ne "tui") {
|
|
||||||
throw "Unexpected EveTui capture target: $($captureContract.targetId)"
|
|
||||||
}
|
|
||||||
if ($captureContract.requestSchema -ne "gamecult.eve.runtime_capture_request.v1") {
|
|
||||||
throw "Unexpected EveTui capture request schema: $($captureContract.requestSchema)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$builderPath = Join-Path $projectRoot $captureContract.requestBuilder
|
|
||||||
if (-not (Test-Path -LiteralPath $builderPath)) {
|
|
||||||
throw "EveTui capture request builder not found: $($captureContract.requestBuilder)"
|
|
||||||
}
|
|
||||||
if ((Join-Path $projectRoot $captureContract.advertisementPath) -ne $absoluteAdvertisementPath) {
|
|
||||||
throw "EveTui capture advertisement path mismatch: $($captureContract.advertisementPath) vs $AdvertisementPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
node --test (Join-Path $projectRoot "tools\evetui\evetui-capture-contract.test.mjs")
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui capture contract tests failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
node $builderPath --capability $absoluteCapabilityPath --advertisement $absoluteAdvertisementPath --output $absoluteOutputPath --stamp $Stamp
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui capture request builder failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteOutputPath)) {
|
|
||||||
throw "EveTui capture request was not written: $absoluteOutputPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
|
|
||||||
foreach ($expectation in @(
|
|
||||||
@{ field = "schema"; value = "gamecult.eve.runtime_capture_request.v1" },
|
|
||||||
@{ field = "runtimeId"; value = "tui" },
|
|
||||||
@{ field = "ownerRepo"; value = "EveTui" },
|
|
||||||
@{ field = "providerId"; value = "aetheria" },
|
|
||||||
@{ field = "surfaceId"; value = "aetheria.daemon.game" },
|
|
||||||
@{ field = "targetId"; value = "tui" },
|
|
||||||
@{ field = "commandBoundary"; value = "aetheria.daemon.commands" },
|
|
||||||
@{ field = "receiptSchema"; value = "aetheria.eve_command_acceptance_status.v1" },
|
|
||||||
@{ field = "captureKind"; value = "terminal-cell-grid" },
|
|
||||||
@{ field = "artifactKind"; value = "json-grid" },
|
|
||||||
@{ field = "conformanceAttachment"; value = "runtime.captureArtifacts[]" }
|
|
||||||
)) {
|
|
||||||
if ($request.($expectation.field) -ne $expectation.value) {
|
|
||||||
throw "EveTui capture request $($expectation.field) expected $($expectation.value) got $($request.($expectation.field))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($request.artifactPath -ne "artifacts/evetui-capture/$Stamp/tui-grid.json") {
|
|
||||||
throw "Unexpected EveTui capture artifact path: $($request.artifactPath)"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "EveTui capture contract smoke passed: $absoluteOutputPath"
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $CapabilityManifestPath = "runtimes\incubating\eve-tui\eve-runtime-capability.json",
|
|
||||||
[string] $AdvertisementPath = "..\Aetheria\conformance\eve\aetheria.provider-advertisement.json",
|
|
||||||
[string] $SurfacePath = "..\Aetheria\conformance\eve\aetheria-world-surface.json",
|
|
||||||
[string] $OutputPath = "artifacts\evetui-capture\latest\tui-grid.json",
|
|
||||||
[string] $RequestOutputPath = "artifacts\evetui-capture\latest\capture-request.json",
|
|
||||||
[string] $Stamp = "latest",
|
|
||||||
[string] $ExpectedProviderId = "aetheria",
|
|
||||||
[string] $ExpectedSurfaceId = "aetheria.daemon.game",
|
|
||||||
[string] $ExpectedCommandBoundary = "aetheria.daemon.commands",
|
|
||||||
[string] $ExpectedReceiptSchema = "aetheria.eve_command_acceptance_status.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
function Resolve-RepoPath([string] $candidate) {
|
|
||||||
if ([System.IO.Path]::IsPathRooted($candidate)) {
|
|
||||||
return $candidate
|
|
||||||
}
|
|
||||||
return Join-Path $projectRoot $candidate
|
|
||||||
}
|
|
||||||
|
|
||||||
$absoluteCapabilityPath = Resolve-RepoPath $CapabilityManifestPath
|
|
||||||
$absoluteAdvertisementPath = Resolve-RepoPath $AdvertisementPath
|
|
||||||
$absoluteSurfacePath = Resolve-RepoPath $SurfacePath
|
|
||||||
$absoluteOutputPath = Resolve-RepoPath $OutputPath
|
|
||||||
$absoluteRequestOutputPath = Resolve-RepoPath $RequestOutputPath
|
|
||||||
|
|
||||||
foreach ($path in @($absoluteCapabilityPath, $absoluteAdvertisementPath, $absoluteSurfacePath)) {
|
|
||||||
if (-not (Test-Path -LiteralPath $path)) {
|
|
||||||
throw "EveTui capture input not found: $path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node --test (Join-Path $projectRoot "tools\evetui\evetui-capture-artifact.test.mjs")
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui capture artifact tests failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
node (Join-Path $projectRoot "tools\evetui\evetui-capture-artifact.mjs") `
|
|
||||||
--capability $absoluteCapabilityPath `
|
|
||||||
--advertisement $absoluteAdvertisementPath `
|
|
||||||
--surface $absoluteSurfacePath `
|
|
||||||
--output $absoluteOutputPath `
|
|
||||||
--request-output $absoluteRequestOutputPath `
|
|
||||||
--stamp $Stamp
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui capture artifact builder failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($path in @($absoluteOutputPath, $absoluteRequestOutputPath)) {
|
|
||||||
if (-not (Test-Path -LiteralPath $path)) {
|
|
||||||
throw "EveTui capture artifact output missing: $path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$grid = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
|
|
||||||
foreach ($expectation in @(
|
|
||||||
@{ field = "schema"; value = "gamecult.eve.tui_grid.v1" },
|
|
||||||
@{ field = "runtimeId"; value = "tui" },
|
|
||||||
@{ field = "providerId"; value = $ExpectedProviderId },
|
|
||||||
@{ field = "surfaceId"; value = $ExpectedSurfaceId },
|
|
||||||
@{ field = "commandBoundary"; value = $ExpectedCommandBoundary },
|
|
||||||
@{ field = "receiptSchema"; value = $ExpectedReceiptSchema },
|
|
||||||
@{ field = "artifactKind"; value = "json-grid" },
|
|
||||||
@{ field = "captureKind"; value = "terminal-cell-grid" }
|
|
||||||
)) {
|
|
||||||
if ($grid.($expectation.field) -ne $expectation.value) {
|
|
||||||
throw "EveTui grid $($expectation.field) expected $($expectation.value) got $($grid.($expectation.field))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (@($grid.lines).Count -lt 1) {
|
|
||||||
throw "EveTui grid capture must contain terminal lines"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "EveTui capture smoke passed: $absoluteOutputPath"
|
|
||||||
|
|
@ -1,196 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $CapabilityManifestPath = "runtimes\incubating\eve-tui\eve-runtime-capability.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$absoluteManifestPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
|
|
||||||
$CapabilityManifestPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $CapabilityManifestPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteManifestPath)) {
|
|
||||||
throw "EveTui runtime capability manifest not found: $absoluteManifestPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$manifest = Get-Content -LiteralPath $absoluteManifestPath -Raw | ConvertFrom-Json
|
|
||||||
|
|
||||||
if ($manifest.schema -ne "gamecult.eve.runtime_capability.v1") {
|
|
||||||
throw "Unexpected EveTui manifest schema: $($manifest.schema)"
|
|
||||||
}
|
|
||||||
if ($manifest.runtimeId -ne "tui") {
|
|
||||||
throw "Unexpected EveTui runtime id: $($manifest.runtimeId)"
|
|
||||||
}
|
|
||||||
if ($manifest.owner -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui owner: $($manifest.owner)"
|
|
||||||
}
|
|
||||||
if ($manifest.incubation.ownerRepo -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui incubation owner: $($manifest.incubation.ownerRepo)"
|
|
||||||
}
|
|
||||||
if ($manifest.incubation.currentHostRepo -ne "Eve") {
|
|
||||||
throw "Unexpected EveTui current host repo: $($manifest.incubation.currentHostRepo)"
|
|
||||||
}
|
|
||||||
if ($manifest.incubation.splitTarget -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui split target: $($manifest.incubation.splitTarget)"
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($feature in @("providerAdvertisements", "commandTransport", "terminalGridSummary", "terminalGridLowering", "embeddedDocuments", "pluginProjection")) {
|
|
||||||
if (-not (@($manifest.supportedFeatures) -contains $feature)) {
|
|
||||||
throw "EveTui manifest missing provider-shell feature: $feature"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$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 = @()
|
|
||||||
if ($null -ne $manifest.worldSurfaceLowering) {
|
|
||||||
$worldSurfaceLoweringClaims = @($manifest.worldSurfaceLowering)
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaims.Count -ne 1) {
|
|
||||||
throw "EveTui must claim exactly one world-surface lowering target"
|
|
||||||
}
|
|
||||||
$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "tui" } | Select-Object -First 1
|
|
||||||
if (-not $worldSurfaceLoweringClaim) {
|
|
||||||
throw "EveTui manifest missing tui world-surface lowering claim"
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaim.supportLevel -ne "terminal-grid-command-surface") {
|
|
||||||
throw "Unexpected EveTui world-surface support level: $($worldSurfaceLoweringClaim.supportLevel)"
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") {
|
|
||||||
throw "Unexpected EveTui world-surface ownership: $($worldSurfaceLoweringClaim.ownership)"
|
|
||||||
}
|
|
||||||
foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) {
|
|
||||||
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
|
|
||||||
throw "EveTui world-surface claim missing evidence path: $evidencePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($pluginId in $expectedPlugins.Keys) {
|
|
||||||
$unsupported = @($manifest.unsupportedPlugins) | Where-Object { $_.pluginId -eq $pluginId } | Select-Object -First 1
|
|
||||||
if ($unsupported) {
|
|
||||||
throw "EveTui manifest must not report $pluginId unsupported while terminal fallback projection is declared"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($manifest.commandTransport.schema -ne "gamecult.eve.command_invocation.v1") {
|
|
||||||
throw "Unexpected EveTui command schema: $($manifest.commandTransport.schema)"
|
|
||||||
}
|
|
||||||
if ($manifest.commandTransport.status -ne "provider-shell-contract-skeleton") {
|
|
||||||
throw "Unexpected EveTui command transport status: $($manifest.commandTransport.status)"
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($stage in @("release", "test", "capture")) {
|
|
||||||
$stageDocument = $manifest.lifecycle.$stage
|
|
||||||
if ($null -eq $stageDocument) {
|
|
||||||
throw "EveTui lifecycle missing stage: $stage"
|
|
||||||
}
|
|
||||||
if ($stageDocument.ownerRepo -ne "EveTui") {
|
|
||||||
throw "EveTui lifecycle stage $stage has unexpected owner: $($stageDocument.ownerRepo)"
|
|
||||||
}
|
|
||||||
if (-not $stageDocument.status) {
|
|
||||||
throw "EveTui lifecycle stage $stage missing status"
|
|
||||||
}
|
|
||||||
foreach ($evidencePath in @($stageDocument.evidencePaths)) {
|
|
||||||
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
|
|
||||||
throw "EveTui lifecycle stage $stage missing evidence path: $evidencePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$releaseContract = $manifest.lifecycle.release.releaseContract
|
|
||||||
foreach ($field in @("ownerRepo", "packageName", "packageRoot", "versionSource", "tagPattern", "artifactKind", "requestSchema", "requestBuilder", "artifactPattern", "publishProof")) {
|
|
||||||
if (-not $releaseContract.$field) {
|
|
||||||
throw "EveTui release contract missing $field"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($releaseContract.requestSchema -ne "gamecult.eve.runtime_release_request.v1") {
|
|
||||||
throw "EveTui release contract has unexpected request schema: $($releaseContract.requestSchema)"
|
|
||||||
}
|
|
||||||
foreach ($pathProperty in @("packageRoot", "versionSource")) {
|
|
||||||
$relativePath = $releaseContract.$pathProperty
|
|
||||||
$absolutePath = Join-Path $projectRoot $relativePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui release contract $pathProperty does not exist: $relativePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($pathProperty in @("requestBuilder")) {
|
|
||||||
$relativePath = $releaseContract.$pathProperty
|
|
||||||
$absolutePath = Join-Path $projectRoot $relativePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui release contract $pathProperty does not exist: $relativePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$testContract = $manifest.lifecycle.test.testContract
|
|
||||||
foreach ($field in @("ownerRepo", "runnerKind", "runnerScript", "consumerProject", "packageName", "testAssembly", "testPlatform", "resultsArtifact", "logArtifact", "manifestMutation")) {
|
|
||||||
if (-not $testContract.$field) {
|
|
||||||
throw "EveTui test contract missing $field"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
foreach ($pathProperty in @("runnerScript", "consumerProject")) {
|
|
||||||
$candidate = $testContract.$pathProperty
|
|
||||||
$absolutePath = if ([System.IO.Path]::IsPathRooted($candidate)) {
|
|
||||||
$candidate
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $candidate
|
|
||||||
}
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui test contract $pathProperty does not exist: $candidate"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$captureContract = $manifest.lifecycle.capture.captureContract
|
|
||||||
foreach ($field in @("ownerRepo", "runtimeId", "targetId", "requestSchema", "requestBuilder", "advertisementPath", "captureKind", "artifactKind", "artifactPattern", "conformanceAttachment", "requiredProvider", "requiredSurface", "authority", "publishProof")) {
|
|
||||||
if (-not $captureContract.$field) {
|
|
||||||
throw "EveTui capture contract missing $field"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($captureContract.runtimeId -ne "tui") {
|
|
||||||
throw "EveTui capture contract has unexpected runtime: $($captureContract.runtimeId)"
|
|
||||||
}
|
|
||||||
if ($captureContract.targetId -ne "tui") {
|
|
||||||
throw "EveTui capture contract has unexpected target: $($captureContract.targetId)"
|
|
||||||
}
|
|
||||||
if ($captureContract.requestSchema -ne "gamecult.eve.runtime_capture_request.v1") {
|
|
||||||
throw "EveTui capture contract has unexpected request schema: $($captureContract.requestSchema)"
|
|
||||||
}
|
|
||||||
if ($captureContract.captureKind -ne "terminal-cell-grid") {
|
|
||||||
throw "EveTui capture contract has unexpected capture kind: $($captureContract.captureKind)"
|
|
||||||
}
|
|
||||||
foreach ($pathProperty in @("requestBuilder", "advertisementPath")) {
|
|
||||||
$relativePath = $captureContract.$pathProperty
|
|
||||||
$absolutePath = Join-Path $projectRoot $relativePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui capture contract $pathProperty does not exist: $relativePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& (Join-Path $projectRoot "scripts\run-evetui-split-handoff-smoke.ps1")
|
|
||||||
& (Join-Path $projectRoot "scripts\run-evetui-release-contract-smoke.ps1")
|
|
||||||
& (Join-Path $projectRoot "scripts\run-evetui-provider-shell-smoke.ps1")
|
|
||||||
& (Join-Path $projectRoot "scripts\run-evetui-capture-contract-smoke.ps1")
|
|
||||||
& (Join-Path $projectRoot "scripts\run-evetui-capture-smoke.ps1")
|
|
||||||
|
|
||||||
Write-Host "EveTui lifecycle smoke passed: $absoluteManifestPath"
|
|
||||||
|
|
@ -1,111 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $CapabilityManifestPath = "runtimes\incubating\eve-tui\eve-runtime-capability.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$absoluteManifestPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
|
|
||||||
$CapabilityManifestPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $CapabilityManifestPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteManifestPath)) {
|
|
||||||
throw "EveTui runtime capability manifest not found: $absoluteManifestPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$manifest = Get-Content -LiteralPath $absoluteManifestPath -Raw | ConvertFrom-Json
|
|
||||||
|
|
||||||
if ($manifest.runtimeId -ne "tui") {
|
|
||||||
throw "Unexpected EveTui runtime id: $($manifest.runtimeId)"
|
|
||||||
}
|
|
||||||
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", "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)
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaims.Count -ne 1) {
|
|
||||||
throw "EveTui must claim exactly one provider world-surface lowering target"
|
|
||||||
}
|
|
||||||
$worldSurfaceLoweringClaim = $worldSurfaceLoweringClaims | Where-Object { $_.targetId -eq "tui" } | Select-Object -First 1
|
|
||||||
if (-not $worldSurfaceLoweringClaim) {
|
|
||||||
throw "EveTui missing tui world-surface lowering claim"
|
|
||||||
}
|
|
||||||
foreach ($surfaceKind in @("interactive-world", "interactive-world-editor")) {
|
|
||||||
if (-not (@($worldSurfaceLoweringClaim.surfaceKinds) -contains $surfaceKind)) {
|
|
||||||
throw "EveTui 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 "EveTui world-surface claim missing projection kind: $projectionKind"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaim.supportLevel -ne "terminal-grid-command-surface") {
|
|
||||||
throw "Unexpected EveTui support level: $($worldSurfaceLoweringClaim.supportLevel)"
|
|
||||||
}
|
|
||||||
if ($worldSurfaceLoweringClaim.ownership -ne "runtime-lowers-provider-world-surface-without-owning-world-state") {
|
|
||||||
throw "Unexpected EveTui ownership: $($worldSurfaceLoweringClaim.ownership)"
|
|
||||||
}
|
|
||||||
foreach ($evidencePath in @($worldSurfaceLoweringClaim.evidencePaths)) {
|
|
||||||
$absoluteEvidencePath = Join-Path $projectRoot $evidencePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteEvidencePath)) {
|
|
||||||
throw "EveTui world-surface claim missing evidence path: $evidencePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$expectedFiles = @(
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
foreach ($relativePath in $expectedFiles) {
|
|
||||||
$absolutePath = Join-Path $projectRoot $relativePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui provider shell missing file: $relativePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$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", "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", "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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Push-Location (Join-Path $projectRoot "runtimes\incubating\eve-tui")
|
|
||||||
try {
|
|
||||||
npm test
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui provider shell tests failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
Pop-Location
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "EveTui provider shell smoke passed: $absoluteManifestPath"
|
|
||||||
|
|
@ -1,98 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $CapabilityManifestPath = "runtimes\incubating\eve-tui\eve-runtime-capability.json",
|
|
||||||
[string] $OutputPath = "artifacts\evetui-release-contract\latest\release-request.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$absoluteCapabilityPath = if ([System.IO.Path]::IsPathRooted($CapabilityManifestPath)) {
|
|
||||||
$CapabilityManifestPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $CapabilityManifestPath
|
|
||||||
}
|
|
||||||
$absoluteOutputPath = if ([System.IO.Path]::IsPathRooted($OutputPath)) {
|
|
||||||
$OutputPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $OutputPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteCapabilityPath)) {
|
|
||||||
throw "EveTui release contract input not found: $absoluteCapabilityPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$manifest = Get-Content -LiteralPath $absoluteCapabilityPath -Raw | ConvertFrom-Json
|
|
||||||
$releaseContract = $manifest.lifecycle.release.releaseContract
|
|
||||||
if ($null -eq $releaseContract) {
|
|
||||||
throw "EveTui release contract missing lifecycle.release.releaseContract"
|
|
||||||
}
|
|
||||||
foreach ($field in @("ownerRepo", "packageName", "packageRoot", "versionSource", "tagPattern", "artifactKind", "requestSchema", "requestBuilder", "artifactPattern", "publishProof")) {
|
|
||||||
if (-not $releaseContract.$field) {
|
|
||||||
throw "EveTui release contract missing $field"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($releaseContract.ownerRepo -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui release owner: $($releaseContract.ownerRepo)"
|
|
||||||
}
|
|
||||||
if ($releaseContract.packageName -ne "eve-tui") {
|
|
||||||
throw "Unexpected EveTui package name: $($releaseContract.packageName)"
|
|
||||||
}
|
|
||||||
if ($releaseContract.requestSchema -ne "gamecult.eve.runtime_release_request.v1") {
|
|
||||||
throw "Unexpected EveTui release request schema: $($releaseContract.requestSchema)"
|
|
||||||
}
|
|
||||||
if ($releaseContract.artifactKind -ne "terminal-runtime") {
|
|
||||||
throw "Unexpected EveTui release artifact kind: $($releaseContract.artifactKind)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$builderPath = Join-Path $projectRoot $releaseContract.requestBuilder
|
|
||||||
$packagePath = Join-Path $projectRoot $releaseContract.versionSource
|
|
||||||
$packageRoot = Join-Path $projectRoot $releaseContract.packageRoot
|
|
||||||
foreach ($path in @($builderPath, $packagePath, $packageRoot)) {
|
|
||||||
if (-not (Test-Path -LiteralPath $path)) {
|
|
||||||
throw "EveTui release contract path not found: $path"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node --test (Join-Path $projectRoot "tools\evetui\evetui-release-contract.test.mjs")
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui release contract tests failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
|
|
||||||
node $builderPath --capability $absoluteCapabilityPath --package $packagePath --output $absoluteOutputPath
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
|
||||||
throw "EveTui release request builder failed with exit code $LASTEXITCODE"
|
|
||||||
}
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteOutputPath)) {
|
|
||||||
throw "EveTui release request was not written: $absoluteOutputPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$request = Get-Content -LiteralPath $absoluteOutputPath -Raw | ConvertFrom-Json
|
|
||||||
$packageManifest = Get-Content -LiteralPath $packagePath -Raw | ConvertFrom-Json
|
|
||||||
foreach ($expectation in @(
|
|
||||||
@{ field = "schema"; value = "gamecult.eve.runtime_release_request.v1" },
|
|
||||||
@{ field = "ownerRepo"; value = "EveTui" },
|
|
||||||
@{ field = "repository"; value = "GameCult/EveTui" },
|
|
||||||
@{ field = "packageName"; value = "eve-tui" },
|
|
||||||
@{ field = "version"; value = $packageManifest.version },
|
|
||||||
@{ field = "packageRoot"; value = "runtimes/incubating/eve-tui" },
|
|
||||||
@{ field = "versionSource"; value = "runtimes/incubating/eve-tui/package.json" },
|
|
||||||
@{ field = "artifactKind"; value = "terminal-runtime" }
|
|
||||||
)) {
|
|
||||||
if ($request.($expectation.field) -ne $expectation.value) {
|
|
||||||
throw "EveTui release request $($expectation.field) expected $($expectation.value) got $($request.($expectation.field))"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$expectedTagName = $releaseContract.tagPattern.Replace("{version}", $packageManifest.version)
|
|
||||||
if ($request.tagName -ne $expectedTagName) {
|
|
||||||
throw "Unexpected EveTui release tag: $($request.tagName)"
|
|
||||||
}
|
|
||||||
$expectedArtifactPath = $releaseContract.artifactPattern.Replace("{version}", $packageManifest.version)
|
|
||||||
if ($request.artifactPath -ne $expectedArtifactPath) {
|
|
||||||
throw "Unexpected EveTui release artifact path: $($request.artifactPath)"
|
|
||||||
}
|
|
||||||
if ($request.main -ne $packageManifest.main) {
|
|
||||||
throw "EveTui release request lost package main entry"
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "EveTui release contract smoke passed: $absoluteOutputPath"
|
|
||||||
|
|
@ -1,125 +0,0 @@
|
||||||
param(
|
|
||||||
[string] $HandoffPath = "runtimes\incubating\eve-tui\evetui-split-handoff.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
$ErrorActionPreference = "Stop"
|
|
||||||
|
|
||||||
$projectRoot = Split-Path -Parent $PSScriptRoot
|
|
||||||
$absoluteHandoffPath = if ([System.IO.Path]::IsPathRooted($HandoffPath)) {
|
|
||||||
$HandoffPath
|
|
||||||
} else {
|
|
||||||
Join-Path $projectRoot $HandoffPath
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-not (Test-Path -LiteralPath $absoluteHandoffPath)) {
|
|
||||||
throw "EveTui split handoff manifest not found: $absoluteHandoffPath"
|
|
||||||
}
|
|
||||||
|
|
||||||
$handoff = Get-Content -LiteralPath $absoluteHandoffPath -Raw | ConvertFrom-Json
|
|
||||||
|
|
||||||
if ($handoff.schema -ne "gamecult.eve.runtime_split_handoff.v1") {
|
|
||||||
throw "Unexpected EveTui split handoff schema: $($handoff.schema)"
|
|
||||||
}
|
|
||||||
if ($handoff.splitTarget -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui split handoff target: $($handoff.splitTarget)"
|
|
||||||
}
|
|
||||||
if ($handoff.ownerRepo -ne "EveTui") {
|
|
||||||
throw "Unexpected EveTui split handoff owner: $($handoff.ownerRepo)"
|
|
||||||
}
|
|
||||||
if ($handoff.currentHostRepo -ne "Eve") {
|
|
||||||
throw "Unexpected EveTui split handoff host: $($handoff.currentHostRepo)"
|
|
||||||
}
|
|
||||||
if ($handoff.runtimeId -ne "tui") {
|
|
||||||
throw "Unexpected EveTui split handoff runtime: $($handoff.runtimeId)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$sourcePackageRoot = Join-Path $projectRoot $handoff.sourcePackageRoot
|
|
||||||
if (-not (Test-Path -LiteralPath $sourcePackageRoot)) {
|
|
||||||
throw "EveTui source package root missing: $($handoff.sourcePackageRoot)"
|
|
||||||
}
|
|
||||||
|
|
||||||
$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)
|
|
||||||
foreach ($id in $expectedMoveSets.Keys) {
|
|
||||||
$moveSet = $moveSets | Where-Object { $_.id -eq $id } | Select-Object -First 1
|
|
||||||
if (-not $moveSet) {
|
|
||||||
throw "EveTui split handoff missing move set: $id"
|
|
||||||
}
|
|
||||||
if ($moveSet.stage -ne $expectedMoveSets[$id]) {
|
|
||||||
throw "EveTui split handoff move set $id has unexpected stage: $($moveSet.stage)"
|
|
||||||
}
|
|
||||||
if ($moveSet.destinationOwner -ne "EveTui") {
|
|
||||||
throw "EveTui split handoff move set $id has unexpected destination owner: $($moveSet.destinationOwner)"
|
|
||||||
}
|
|
||||||
if (-not $moveSet.replacementProof) {
|
|
||||||
throw "EveTui split handoff move set $id missing replacement proof"
|
|
||||||
}
|
|
||||||
$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-plugin-projection", "tui-capture-lifecycle")) {
|
|
||||||
if ($currentPaths.Count -eq 0) {
|
|
||||||
throw "EveTui split handoff move set $id must name provider-shell skeleton paths"
|
|
||||||
}
|
|
||||||
foreach ($relativePath in $currentPaths) {
|
|
||||||
$absolutePath = Join-Path $projectRoot $relativePath
|
|
||||||
if (-not (Test-Path -LiteralPath $absolutePath)) {
|
|
||||||
throw "EveTui split handoff move set $id references missing current path: $relativePath"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($observedProviderPaths.Count -ne 0) {
|
|
||||||
throw "EveTui split handoff move set $id must not treat provider product paths as generic TUI runtime source"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($contract in @(
|
|
||||||
"gamecult.eve.surface.v1",
|
|
||||||
"gamecult.eve.command.v1",
|
|
||||||
"gamecult.eve.provider_advertisement.v1",
|
|
||||||
"gamecult.eve.plugin_advertisement.v1",
|
|
||||||
"gamecult.eve.plugin.v1",
|
|
||||||
"gamecult.eve.conformance_export.v1"
|
|
||||||
)) {
|
|
||||||
if (-not (@($handoff.contractInputs) -contains $contract)) {
|
|
||||||
throw "EveTui split handoff missing contract input: $contract"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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",
|
|
||||||
"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)) {
|
|
||||||
throw "EveTui split handoff missing forbidden import: $forbiddenImport"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Write-Host "EveTui split handoff smoke passed: $absoluteHandoffPath"
|
|
||||||
|
|
@ -37,16 +37,6 @@ try {
|
||||||
node "E:\Projects\Norn\scripts\run-eve-cultnet-witness.mjs"
|
node "E:\Projects\Norn\scripts\run-eve-cultnet-witness.mjs"
|
||||||
if ($LASTEXITCODE -ne 0) { throw "Norn plugin owner witness failed with exit code $LASTEXITCODE" }
|
if ($LASTEXITCODE -ne 0) { throw "Norn plugin owner witness failed with exit code $LASTEXITCODE" }
|
||||||
& (Join-Path $PSScriptRoot "run-eveflutter-owner-smoke.ps1")
|
& (Join-Path $PSScriptRoot "run-eveflutter-owner-smoke.ps1")
|
||||||
& (Join-Path $PSScriptRoot "run-evetui-capture-smoke.ps1")
|
|
||||||
& (Join-Path $PSScriptRoot "run-evetui-capture-smoke.ps1") `
|
|
||||||
-AdvertisementPath "web\fixtures\eve-world-smoke.provider-advertisement.json" `
|
|
||||||
-SurfacePath "web\fixtures\eve-world-smoke-surface.json" `
|
|
||||||
-OutputPath "artifacts\evetui-capture\latest\eve-world-smoke-tui-grid.json" `
|
|
||||||
-RequestOutputPath "artifacts\evetui-capture\latest\eve-world-smoke-capture-request.json" `
|
|
||||||
-ExpectedProviderId "eve.world-smoke" `
|
|
||||||
-ExpectedSurfaceId "eve.world-smoke.surface" `
|
|
||||||
-ExpectedCommandBoundary "eve.world-smoke.commands" `
|
|
||||||
-ExpectedReceiptSchema "eve.world_smoke.command_receipt.v1"
|
|
||||||
$env:EVE_KERNEL_ROOT = $projectRoot
|
$env:EVE_KERNEL_ROOT = $projectRoot
|
||||||
$env:EVE_CONFORMANCE_OUTPUT = Join-Path $projectRoot "artifacts\conformance"
|
$env:EVE_CONFORMANCE_OUTPUT = Join-Path $projectRoot "artifacts\conformance"
|
||||||
node (Join-Path $conformanceRoot "tools\parity\run-parity.mjs")
|
node (Join-Path $conformanceRoot "tools\parity\run-parity.mjs")
|
||||||
|
|
@ -72,8 +62,6 @@ try {
|
||||||
"E:\Projects\Norn\artifacts\eve-plugin\runtime-witness.json" `
|
"E:\Projects\Norn\artifacts\eve-plugin\runtime-witness.json" `
|
||||||
".\artifacts\conformance\latest"
|
".\artifacts\conformance\latest"
|
||||||
& (Join-Path $PSScriptRoot "run-eveelectron-owner-smoke.ps1")
|
& (Join-Path $PSScriptRoot "run-eveelectron-owner-smoke.ps1")
|
||||||
& (Join-Path $PSScriptRoot "run-evetui-split-handoff-smoke.ps1")
|
|
||||||
& (Join-Path $PSScriptRoot "run-evetui-lifecycle-smoke.ps1")
|
|
||||||
& (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1")
|
& (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1")
|
||||||
& (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1")
|
& (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1")
|
||||||
& (Join-Path $PSScriptRoot "run-runtime-owner-conformance-consumer-smoke.ps1")
|
& (Join-Path $PSScriptRoot "run-runtime-owner-conformance-consumer-smoke.ps1")
|
||||||
|
|
|
||||||
|
|
@ -277,14 +277,11 @@ node $consumerScript $consumerExport `
|
||||||
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.requiredSurface:aetheria.daemon.game" `
|
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.requiredSurface:aetheria.daemon.game" `
|
||||||
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" `
|
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.providerId:eve.world-smoke" `
|
||||||
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" `
|
--expect-runtime-lifecycle-field "electron-shell:capture:captureContract.additionalProviderSurfaces.0.surfaceId:eve.world-smoke.surface" `
|
||||||
--expect-runtime-lifecycle-status tui:release:pending-tui-package `
|
--expect-runtime-lifecycle-status tui:release:owner-npm-package `
|
||||||
--expect-runtime-lifecycle-status tui:test:provider-shell-contract-skeleton `
|
--expect-runtime-lifecycle-status tui:test:provider-shell-contract-skeleton `
|
||||||
--expect-runtime-lifecycle-status tui:capture:cell-grid-json-capture `
|
--expect-runtime-lifecycle-status tui:capture:cell-grid-json-capture `
|
||||||
--expect-runtime-lifecycle-pending "tui:release:TUI package release" `
|
|
||||||
--expect-runtime-lifecycle-pending "tui:test:TUI provider-advertisement smoke runs from EveTui with durable terminal transcript or cell-grid artifacts" `
|
|
||||||
--expect-runtime-lifecycle-pending "tui:capture:TUI capture artifact is produced from the EveTui owner repo" `
|
|
||||||
--expect-runtime-lifecycle-field "tui:release:releaseContract.artifactKind:terminal-runtime" `
|
--expect-runtime-lifecycle-field "tui:release:releaseContract.artifactKind:terminal-runtime" `
|
||||||
--expect-runtime-lifecycle-field "tui:release:releaseContract.versionSource:runtimes/incubating/eve-tui/package.json" `
|
--expect-runtime-lifecycle-field "tui:release:releaseContract.versionSource:package.json" `
|
||||||
--expect-runtime-lifecycle-field "tui:release:releaseContract.requestSchema:gamecult.eve.runtime_release_request.v1" `
|
--expect-runtime-lifecycle-field "tui:release:releaseContract.requestSchema:gamecult.eve.runtime_release_request.v1" `
|
||||||
--expect-runtime-lifecycle-field "tui:release:releaseContract.requestBuilder:tools/evetui/evetui-release-contract.mjs" `
|
--expect-runtime-lifecycle-field "tui:release:releaseContract.requestBuilder:tools/evetui/evetui-release-contract.mjs" `
|
||||||
--expect-runtime-lifecycle-field "tui:release:releaseContract.artifactPattern:artifacts/evetui-release/{version}/eve-tui-{version}.tgz" `
|
--expect-runtime-lifecycle-field "tui:release:releaseContract.artifactPattern:artifacts/evetui-release/{version}/eve-tui-{version}.tgz" `
|
||||||
|
|
|
||||||
|
|
@ -51,18 +51,11 @@ try {
|
||||||
--expect-split-target-proof "EveElectron:EveElectron owner repository publishes the generic runtime package" `
|
--expect-split-target-proof "EveElectron:EveElectron owner repository publishes the generic runtime package" `
|
||||||
--expect-split-target-proof "EveElectron:EveElectron owns secure window, preload, and generic CultMesh asset lifecycle" `
|
--expect-split-target-proof "EveElectron:EveElectron owns secure window, preload, and generic CultMesh asset lifecycle" `
|
||||||
--expect-split-target EveTui `
|
--expect-split-target EveTui `
|
||||||
--expect-split-target-status EveTui:incubating `
|
--expect-split-target-status EveTui:graduated `
|
||||||
--expect-split-target-proof "EveTui:EveTui split handoff is machine-readable" `
|
--expect-split-target-proof "EveTui:EveTui owns a tagged package and runtime lifecycle" `
|
||||||
--expect-split-target-proof "EveTui:EveTui lifecycle contract is machine-readable" `
|
--expect-split-target-proof "EveTui:EveTui lifecycle contract is machine-readable" `
|
||||||
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:aetheria:aetheria.daemon.game `
|
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:aetheria:aetheria.daemon.game `
|
||||||
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:eve.world-smoke:eve.world-smoke.surface `
|
--expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:eve.world-smoke:eve.world-smoke.surface
|
||||||
--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 package release is cut from EveTui rather than Eve incubation" `
|
|
||||||
--expect-split-target-blocker "EveTui:TUI capture path is owned by EveTui rather than Eve incubation"
|
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
throw "Split target conformance consumer smoke failed with exit code $LASTEXITCODE"
|
throw "Split target conformance consumer smoke failed with exit code $LASTEXITCODE"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
import { mkdirSync, writeFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import { EveTuiShell } from "../../runtimes/incubating/eve-tui/src/eve-tui-shell.mjs";
|
|
||||||
import { buildTuiCaptureRequest, loadJsonFile, writeCaptureRequest } from "./evetui-capture-contract.mjs";
|
|
||||||
|
|
||||||
export function buildTuiCaptureArtifact({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
surfaceDocument,
|
|
||||||
advertisementPath = "",
|
|
||||||
capabilityManifestPath = "",
|
|
||||||
captureSurfaceId = "",
|
|
||||||
stamp = "latest",
|
|
||||||
width = 80,
|
|
||||||
} = {}) {
|
|
||||||
const request = buildTuiCaptureRequest({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
advertisementPath,
|
|
||||||
capabilityManifestPath,
|
|
||||||
captureSurfaceId,
|
|
||||||
stamp,
|
|
||||||
});
|
|
||||||
const shell = new EveTuiShell({ width });
|
|
||||||
const grid = shell.lowerSurface(surfaceDocument, advertisement, request.surfaceId);
|
|
||||||
validateGridArtifact(grid, request);
|
|
||||||
|
|
||||||
return {
|
|
||||||
request,
|
|
||||||
grid: {
|
|
||||||
...grid,
|
|
||||||
artifactKind: request.artifactKind,
|
|
||||||
captureKind: request.captureKind,
|
|
||||||
sourceAdvertisementPath: request.sourceAdvertisementPath,
|
|
||||||
sourceCapabilityManifestPath: request.sourceCapabilityManifestPath,
|
|
||||||
issuedAtUtc: request.issuedAtUtc,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeTuiCaptureArtifact({ request, grid }, { outputPath, requestOutputPath = "" } = {}) {
|
|
||||||
if (!outputPath) throw new Error("outputPath is required");
|
|
||||||
mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
||||||
writeFileSync(outputPath, `${JSON.stringify(grid, null, 2)}\n`, "utf8");
|
|
||||||
if (requestOutputPath) {
|
|
||||||
writeCaptureRequest(request, requestOutputPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateGridArtifact(grid, request) {
|
|
||||||
if (grid.schema !== "gamecult.eve.tui_grid.v1") {
|
|
||||||
throw new Error(`Unexpected TUI grid schema: ${grid.schema || ""}`);
|
|
||||||
}
|
|
||||||
for (const [field, expected] of [
|
|
||||||
["runtimeId", request.runtimeId],
|
|
||||||
["providerId", request.providerId],
|
|
||||||
["surfaceId", request.surfaceId],
|
|
||||||
["projectionKind", request.projectionKind],
|
|
||||||
["commandBoundary", request.commandBoundary],
|
|
||||||
["receiptSchema", request.receiptSchema],
|
|
||||||
]) {
|
|
||||||
if ((grid[field] || "") !== (expected || "")) {
|
|
||||||
throw new Error(`TUI grid ${field} expected ${expected || ""} got ${grid[field] || ""}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Array.isArray(grid.lines) || !grid.lines.length) {
|
|
||||||
throw new Error("TUI grid artifact must contain at least one line");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArgs(argv) {
|
|
||||||
const args = {};
|
|
||||||
for (let index = 0; index < argv.length; index += 1) {
|
|
||||||
const value = argv[index];
|
|
||||||
if (!value.startsWith("--")) continue;
|
|
||||||
const key = value.slice(2);
|
|
||||||
const next = argv[index + 1];
|
|
||||||
if (!next || next.startsWith("--")) {
|
|
||||||
args[key] = true;
|
|
||||||
} else {
|
|
||||||
args[key] = next;
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runCli() {
|
|
||||||
const args = parseArgs(process.argv.slice(2));
|
|
||||||
for (const required of ["advertisement", "capability", "surface", "output"]) {
|
|
||||||
if (!args[required]) {
|
|
||||||
throw new Error(`missing required --${required}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const stamp = args.stamp || "latest";
|
|
||||||
const artifact = buildTuiCaptureArtifact({
|
|
||||||
advertisement: loadJsonFile(args.advertisement),
|
|
||||||
capabilityManifest: loadJsonFile(args.capability),
|
|
||||||
surfaceDocument: loadJsonFile(args.surface),
|
|
||||||
advertisementPath: args.advertisement,
|
|
||||||
capabilityManifestPath: args.capability,
|
|
||||||
captureSurfaceId: args["surface-id"] || "",
|
|
||||||
stamp,
|
|
||||||
width: Number(args.width || 80),
|
|
||||||
});
|
|
||||||
writeTuiCaptureArtifact(artifact, {
|
|
||||||
outputPath: args.output,
|
|
||||||
requestOutputPath: args["request-output"] || "",
|
|
||||||
});
|
|
||||||
process.stdout.write(`${args.output}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
||||||
const modulePath = fileURLToPath(import.meta.url);
|
|
||||||
if (invokedPath === modulePath) {
|
|
||||||
runCli();
|
|
||||||
}
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import test from "node:test";
|
|
||||||
import { buildTuiCaptureArtifact } from "./evetui-capture-artifact.mjs";
|
|
||||||
|
|
||||||
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
|
||||||
const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8"));
|
|
||||||
const surfaceDocument = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria-world-surface.json", import.meta.url), "utf8"));
|
|
||||||
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
|
|
||||||
const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8"));
|
|
||||||
|
|
||||||
test("builds TUI JSON grid capture artifact from provider advertisement and surface", () => {
|
|
||||||
const { request, grid } = buildTuiCaptureArtifact({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
surfaceDocument,
|
|
||||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
stamp: "smoke",
|
|
||||||
width: 72,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(request.artifactKind, "json-grid");
|
|
||||||
assert.equal(request.artifactPath, "artifacts/evetui-capture/smoke/tui-grid.json");
|
|
||||||
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
|
|
||||||
assert.equal(grid.runtimeId, "tui");
|
|
||||||
assert.equal(grid.providerId, "aetheria");
|
|
||||||
assert.equal(grid.surfaceId, "aetheria.daemon.game");
|
|
||||||
assert.equal(grid.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(grid.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
assert.equal(grid.artifactKind, "json-grid");
|
|
||||||
assert.equal(grid.captureKind, "terminal-cell-grid");
|
|
||||||
assert.equal(grid.width, 72);
|
|
||||||
assert.ok(grid.lines.some(line => line.includes("aetheria.daemon.game")));
|
|
||||||
});
|
|
||||||
|
|
||||||
test("builds TUI JSON grid capture artifact from a generic world provider", () => {
|
|
||||||
const { grid } = buildTuiCaptureArtifact({
|
|
||||||
advertisement: worldSmokeAdvertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
surfaceDocument: worldSmokeSurfaceDocument,
|
|
||||||
advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json",
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
stamp: "smoke",
|
|
||||||
width: 72,
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(grid.schema, "gamecult.eve.tui_grid.v1");
|
|
||||||
assert.equal(grid.runtimeId, "tui");
|
|
||||||
assert.equal(grid.providerId, "eve.world-smoke");
|
|
||||||
assert.equal(grid.surfaceId, "eve.world-smoke.surface");
|
|
||||||
assert.equal(grid.commandBoundary, "eve.world-smoke.commands");
|
|
||||||
assert.equal(grid.receiptSchema, "eve.world_smoke.command_receipt.v1");
|
|
||||||
assert.equal(grid.artifactKind, "json-grid");
|
|
||||||
assert.equal(grid.captureKind, "terminal-cell-grid");
|
|
||||||
assert.ok(grid.lines.some(line => line.includes("eve.world-smoke.surface")));
|
|
||||||
});
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
||||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
export function buildTuiCaptureRequest({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
advertisementPath = "",
|
|
||||||
capabilityManifestPath = "",
|
|
||||||
captureSurfaceId = "",
|
|
||||||
stamp = new Date().toISOString().replace(/[:.]/g, "-"),
|
|
||||||
} = {}) {
|
|
||||||
if (!advertisement || typeof advertisement !== "object") {
|
|
||||||
throw new Error("advertisement is required");
|
|
||||||
}
|
|
||||||
if (!capabilityManifest || typeof capabilityManifest !== "object") {
|
|
||||||
throw new Error("capabilityManifest is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
const captureContract = capabilityManifest.lifecycle?.capture?.captureContract;
|
|
||||||
if (!captureContract) {
|
|
||||||
throw new Error("capability manifest is missing lifecycle.capture.captureContract");
|
|
||||||
}
|
|
||||||
|
|
||||||
const captureSurface = selectCaptureSurface(captureContract, advertisement.providerId, captureSurfaceId);
|
|
||||||
if (!captureSurface) {
|
|
||||||
if (captureSurfaceId) {
|
|
||||||
throw new Error(
|
|
||||||
`capture surface mismatch: expected ${captureSurfaceList(captureContract)} got ${advertisement.providerId || "missing"}:${captureSurfaceId}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`capture provider mismatch: expected ${captureProviderList(captureContract)} got ${advertisement.providerId || "missing"}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetId = captureContract.targetId || captureContract.runtimeId;
|
|
||||||
const surface = (advertisement.surfaces || []).find(candidate => candidate.surfaceId === captureSurface.surfaceId);
|
|
||||||
if (!surface) {
|
|
||||||
throw new Error(`capture surface not advertised: ${captureSurface.surfaceId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const loweringTargets = surface.worldInteraction?.loweringTargets || [];
|
|
||||||
if (!loweringTargets.includes(targetId)) {
|
|
||||||
throw new Error(`surface ${surface.surfaceId} does not advertise lowering target ${targetId}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const artifactPath = replaceStamp(captureContract.artifactPattern, stamp);
|
|
||||||
|
|
||||||
return {
|
|
||||||
schema: captureContract.requestSchema || "gamecult.eve.runtime_capture_request.v1",
|
|
||||||
runtimeId: capabilityManifest.runtimeId,
|
|
||||||
ownerRepo: captureContract.ownerRepo,
|
|
||||||
providerId: advertisement.providerId,
|
|
||||||
surfaceId: surface.surfaceId,
|
|
||||||
surfaceKind: surface.surfaceKind || "",
|
|
||||||
targetId,
|
|
||||||
projectionKind: surface.worldInteraction?.projectionKind || "",
|
|
||||||
commandBoundary: surface.worldInteraction?.commandBoundary || "",
|
|
||||||
receiptSchema: surface.worldInteraction?.receiptSchema || "",
|
|
||||||
captureKind: captureContract.captureKind,
|
|
||||||
artifactKind: captureContract.artifactKind,
|
|
||||||
artifactPath,
|
|
||||||
conformanceAttachment: captureContract.conformanceAttachment,
|
|
||||||
authority: captureContract.authority,
|
|
||||||
sourceAdvertisementPath: advertisementPath,
|
|
||||||
sourceCapabilityManifestPath: capabilityManifestPath,
|
|
||||||
issuedAtUtc: stamp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadJsonFile(filePath) {
|
|
||||||
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeCaptureRequest(request, outputPath) {
|
|
||||||
mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
||||||
writeFileSync(outputPath, `${JSON.stringify(request, null, 2)}\n`, "utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceStamp(pattern, stamp) {
|
|
||||||
if (!pattern || !pattern.includes("{stamp}")) {
|
|
||||||
throw new Error(`capture artifact pattern must include {stamp}: ${pattern || "missing"}`);
|
|
||||||
}
|
|
||||||
return pattern.replace("{stamp}", stamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectCaptureSurface(captureContract, providerId, surfaceId = "") {
|
|
||||||
const claims = captureSurfaceClaims(captureContract).filter(claim => claim.providerId === providerId);
|
|
||||||
if (surfaceId) return claims.find(claim => claim.surfaceId === surfaceId) || null;
|
|
||||||
return claims[0] || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function captureProviderList(captureContract) {
|
|
||||||
return captureSurfaceClaims(captureContract).map(claim => claim.providerId).filter(Boolean).join(", ") || "missing";
|
|
||||||
}
|
|
||||||
|
|
||||||
function captureSurfaceList(captureContract) {
|
|
||||||
return captureSurfaceClaims(captureContract).map(claim => `${claim.providerId}:${claim.surfaceId}`).join(", ") || "missing";
|
|
||||||
}
|
|
||||||
|
|
||||||
function captureSurfaceClaims(captureContract) {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
providerId: captureContract.requiredProvider || "",
|
|
||||||
surfaceId: captureContract.requiredSurface || "",
|
|
||||||
},
|
|
||||||
...(Array.isArray(captureContract.additionalProviderSurfaces) ? captureContract.additionalProviderSurfaces : []),
|
|
||||||
]
|
|
||||||
.filter(claim => claim && typeof claim === "object")
|
|
||||||
.map(claim => ({
|
|
||||||
providerId: String(claim.providerId || ""),
|
|
||||||
surfaceId: String(claim.surfaceId || ""),
|
|
||||||
}))
|
|
||||||
.filter(claim => claim.providerId && claim.surfaceId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArgs(argv) {
|
|
||||||
const args = {};
|
|
||||||
for (let index = 0; index < argv.length; index += 1) {
|
|
||||||
const value = argv[index];
|
|
||||||
if (!value.startsWith("--")) continue;
|
|
||||||
const key = value.slice(2);
|
|
||||||
const next = argv[index + 1];
|
|
||||||
if (!next || next.startsWith("--")) {
|
|
||||||
args[key] = true;
|
|
||||||
} else {
|
|
||||||
args[key] = next;
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runCli() {
|
|
||||||
const args = parseArgs(process.argv.slice(2));
|
|
||||||
for (const required of ["advertisement", "capability", "output"]) {
|
|
||||||
if (!args[required]) {
|
|
||||||
throw new Error(`missing required --${required}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const stamp = args.stamp || new Date().toISOString().replace(/[:.]/g, "-");
|
|
||||||
const request = buildTuiCaptureRequest({
|
|
||||||
advertisement: loadJsonFile(args.advertisement),
|
|
||||||
capabilityManifest: loadJsonFile(args.capability),
|
|
||||||
advertisementPath: args.advertisement,
|
|
||||||
capabilityManifestPath: args.capability,
|
|
||||||
captureSurfaceId: args["surface-id"] || "",
|
|
||||||
stamp,
|
|
||||||
});
|
|
||||||
writeCaptureRequest(request, args.output);
|
|
||||||
process.stdout.write(`${args.output}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
||||||
const modulePath = fileURLToPath(import.meta.url);
|
|
||||||
if (invokedPath === modulePath) {
|
|
||||||
runCli();
|
|
||||||
}
|
|
||||||
|
|
@ -1,78 +0,0 @@
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import test from "node:test";
|
|
||||||
import { buildTuiCaptureRequest } from "./evetui-capture-contract.mjs";
|
|
||||||
|
|
||||||
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
|
||||||
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
|
|
||||||
const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8"));
|
|
||||||
|
|
||||||
test("builds TUI capture request from provider advertisement", () => {
|
|
||||||
const request = buildTuiCaptureRequest({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
stamp: "smoke",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(request.schema, "gamecult.eve.runtime_capture_request.v1");
|
|
||||||
assert.equal(request.runtimeId, "tui");
|
|
||||||
assert.equal(request.ownerRepo, "EveTui");
|
|
||||||
assert.equal(request.providerId, "aetheria");
|
|
||||||
assert.equal(request.surfaceId, "aetheria.daemon.game");
|
|
||||||
assert.equal(request.targetId, "tui");
|
|
||||||
assert.equal(request.projectionKind, "provider-authored-world-surface");
|
|
||||||
assert.equal(request.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(request.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
assert.equal(request.captureKind, "terminal-cell-grid");
|
|
||||||
assert.equal(request.artifactKind, "json-grid");
|
|
||||||
assert.equal(request.artifactPath, "artifacts/evetui-capture/smoke/tui-grid.json");
|
|
||||||
assert.match(request.authority, /provider state/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("builds TUI capture request for Aetheria editor surface", () => {
|
|
||||||
const request = buildTuiCaptureRequest({
|
|
||||||
advertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
captureSurfaceId: "aetheria.daemon.editor",
|
|
||||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
stamp: "smoke",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(request.runtimeId, "tui");
|
|
||||||
assert.equal(request.providerId, "aetheria");
|
|
||||||
assert.equal(request.surfaceId, "aetheria.daemon.editor");
|
|
||||||
assert.equal(request.surfaceKind, "interactive-world-editor");
|
|
||||||
assert.equal(request.projectionKind, "provider-authored-world-editor-surface");
|
|
||||||
assert.equal(request.commandBoundary, "aetheria.daemon.commands");
|
|
||||||
assert.equal(request.receiptSchema, "aetheria.eve_command_acceptance_status.v1");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("builds TUI capture request from a declared generic world provider", () => {
|
|
||||||
const request = buildTuiCaptureRequest({
|
|
||||||
advertisement: worldSmokeAdvertisement,
|
|
||||||
capabilityManifest,
|
|
||||||
advertisementPath: "web/fixtures/eve-world-smoke.provider-advertisement.json",
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
stamp: "smoke",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(request.runtimeId, "tui");
|
|
||||||
assert.equal(request.providerId, "eve.world-smoke");
|
|
||||||
assert.equal(request.surfaceId, "eve.world-smoke.surface");
|
|
||||||
assert.equal(request.commandBoundary, "eve.world-smoke.commands");
|
|
||||||
assert.equal(request.receiptSchema, "eve.world_smoke.command_receipt.v1");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("rejects capture request when the surface does not advertise TUI", () => {
|
|
||||||
const withoutTarget = structuredClone(advertisement);
|
|
||||||
const surface = withoutTarget.surfaces.find(candidate => candidate.surfaceId === "aetheria.daemon.game");
|
|
||||||
surface.worldInteraction.loweringTargets = surface.worldInteraction.loweringTargets.filter(target => target !== "tui");
|
|
||||||
|
|
||||||
assert.throws(
|
|
||||||
() => buildTuiCaptureRequest({ advertisement: withoutTarget, capabilityManifest, stamp: "smoke" }),
|
|
||||||
/does not advertise lowering target tui/,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
|
|
||||||
export function buildTuiReleaseRequest({
|
|
||||||
capabilityManifest,
|
|
||||||
packageManifest,
|
|
||||||
capabilityManifestPath = "",
|
|
||||||
packageManifestPath = "",
|
|
||||||
repository = "GameCult/EveTui",
|
|
||||||
} = {}) {
|
|
||||||
if (!capabilityManifest || typeof capabilityManifest !== "object") {
|
|
||||||
throw new Error("capabilityManifest is required");
|
|
||||||
}
|
|
||||||
if (!packageManifest || typeof packageManifest !== "object") {
|
|
||||||
throw new Error("packageManifest is required");
|
|
||||||
}
|
|
||||||
|
|
||||||
const releaseContract = capabilityManifest.lifecycle?.release?.releaseContract;
|
|
||||||
if (!releaseContract) {
|
|
||||||
throw new Error("capability manifest is missing lifecycle.release.releaseContract");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (packageManifest.name !== releaseContract.packageName) {
|
|
||||||
throw new Error(`package name mismatch: expected ${releaseContract.packageName} got ${packageManifest.name || "missing"}`);
|
|
||||||
}
|
|
||||||
if (!packageManifest.version || !/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(packageManifest.version)) {
|
|
||||||
throw new Error(`package version is not a SemVer-compatible release version: ${packageManifest.version || "missing"}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tagName = replaceVersion(releaseContract.tagPattern, packageManifest.version);
|
|
||||||
const artifactPath = replaceVersion(
|
|
||||||
releaseContract.artifactPattern || `artifacts/evetui-release/{version}/${releaseContract.packageName}-{version}.tgz`,
|
|
||||||
packageManifest.version,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
schema: releaseContract.requestSchema || "gamecult.eve.runtime_release_request.v1",
|
|
||||||
ownerRepo: releaseContract.ownerRepo,
|
|
||||||
repository,
|
|
||||||
packageName: releaseContract.packageName,
|
|
||||||
version: packageManifest.version,
|
|
||||||
packageRoot: releaseContract.packageRoot,
|
|
||||||
versionSource: releaseContract.versionSource,
|
|
||||||
tagName,
|
|
||||||
artifactKind: releaseContract.artifactKind,
|
|
||||||
artifactPath,
|
|
||||||
publishProof: releaseContract.publishProof,
|
|
||||||
sourceCapabilityManifestPath: capabilityManifestPath,
|
|
||||||
sourcePackageManifestPath: packageManifestPath,
|
|
||||||
main: packageManifest.main || "",
|
|
||||||
scripts: packageManifest.scripts || {},
|
|
||||||
dependencies: packageManifest.dependencies || {},
|
|
||||||
devDependencies: packageManifest.devDependencies || {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadJsonFile(filePath) {
|
|
||||||
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeReleaseRequest(request, outputPath) {
|
|
||||||
mkdirSync(path.dirname(outputPath), { recursive: true });
|
|
||||||
writeFileSync(outputPath, `${JSON.stringify(request, null, 2)}\n`, "utf8");
|
|
||||||
}
|
|
||||||
|
|
||||||
function replaceVersion(pattern, version) {
|
|
||||||
if (!pattern || !pattern.includes("{version}")) {
|
|
||||||
throw new Error(`release pattern must include {version}: ${pattern || "missing"}`);
|
|
||||||
}
|
|
||||||
return pattern.replaceAll("{version}", version);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseArgs(argv) {
|
|
||||||
const args = {};
|
|
||||||
for (let index = 0; index < argv.length; index += 1) {
|
|
||||||
const value = argv[index];
|
|
||||||
if (!value.startsWith("--")) continue;
|
|
||||||
const key = value.slice(2);
|
|
||||||
const next = argv[index + 1];
|
|
||||||
if (!next || next.startsWith("--")) {
|
|
||||||
args[key] = true;
|
|
||||||
} else {
|
|
||||||
args[key] = next;
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
function runCli() {
|
|
||||||
const args = parseArgs(process.argv.slice(2));
|
|
||||||
for (const required of ["capability", "package", "output"]) {
|
|
||||||
if (!args[required]) {
|
|
||||||
throw new Error(`missing required --${required}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = buildTuiReleaseRequest({
|
|
||||||
capabilityManifest: loadJsonFile(args.capability),
|
|
||||||
packageManifest: loadJsonFile(args.package),
|
|
||||||
capabilityManifestPath: args.capability,
|
|
||||||
packageManifestPath: args.package,
|
|
||||||
repository: args.repository || "GameCult/EveTui",
|
|
||||||
});
|
|
||||||
writeReleaseRequest(request, args.output);
|
|
||||||
process.stdout.write(`${args.output}\n`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : "";
|
|
||||||
const modulePath = fileURLToPath(import.meta.url);
|
|
||||||
if (invokedPath === modulePath) {
|
|
||||||
runCli();
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
import assert from "node:assert/strict";
|
|
||||||
import { readFileSync } from "node:fs";
|
|
||||||
import test from "node:test";
|
|
||||||
import { buildTuiReleaseRequest } from "./evetui-release-contract.mjs";
|
|
||||||
|
|
||||||
const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8"));
|
|
||||||
const packageManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/package.json", import.meta.url), "utf8"));
|
|
||||||
|
|
||||||
test("builds EveTui release request from package manifest", () => {
|
|
||||||
const request = buildTuiReleaseRequest({
|
|
||||||
capabilityManifest,
|
|
||||||
packageManifest,
|
|
||||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
|
||||||
packageManifestPath: "runtimes/incubating/eve-tui/package.json",
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(request.schema, "gamecult.eve.runtime_release_request.v1");
|
|
||||||
assert.equal(request.ownerRepo, "EveTui");
|
|
||||||
assert.equal(request.repository, "GameCult/EveTui");
|
|
||||||
assert.equal(request.packageName, "eve-tui");
|
|
||||||
assert.equal(request.version, "0.1.0");
|
|
||||||
assert.equal(request.packageRoot, "runtimes/incubating/eve-tui");
|
|
||||||
assert.equal(request.versionSource, "runtimes/incubating/eve-tui/package.json");
|
|
||||||
assert.equal(request.tagName, "evetui-v0.1.0");
|
|
||||||
assert.equal(request.artifactKind, "terminal-runtime");
|
|
||||||
assert.equal(request.artifactPath, "artifacts/evetui-release/0.1.0/eve-tui-0.1.0.tgz");
|
|
||||||
assert.equal(request.main, "src/eve-tui-shell.mjs");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("rejects package manifests that do not match the TUI release contract", () => {
|
|
||||||
const wrongPackage = { ...packageManifest, name: "eve-wrong" };
|
|
||||||
|
|
||||||
assert.throws(
|
|
||||||
() => buildTuiReleaseRequest({ capabilityManifest, packageManifest: wrongPackage }),
|
|
||||||
/package name mismatch/,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
Loading…
Reference in a new issue