Export split handoff move coverage

This commit is contained in:
Meta Vens 2026-07-09 03:16:59 +01:00
commit b9f7592edf
6 changed files with 229 additions and 7 deletions

View file

@ -310,6 +310,12 @@ Recently cut:
and Unity UI Toolkit are `covered` because they advertise
`gamecult.eve.command.v1`; Unity scene and TUI stay visible as
`missing-runtime-claim` until their generic lowerers exist.
- The conformance export carries `splitHandoffMoveCoverage[]`, joining runtime
split handoff move sets to source path status. EveElectron now exposes the
existing Aetheria `Aetheria.Rts.Web/Electron` and command transport paths as
`observed-provider` sources that must be replaced by a provider-agnostic
shell; Unity scene exposes `replacement-required` move sets because no
generic scene body or command transport source exists yet.
- The conformance export schema and consumer smoke now require minimum typed
item shapes for plugins, providers, runtimes, and split targets. Owner repos
can rely on exported ids, owners, statuses, split targets, manifest or

View file

@ -183,6 +183,15 @@ typed `blockerRecords[]` / root `splitTargetBlockers[]` entries. Split-target
consumers can assert blocker kind and subject with
`--expect-split-target-blocker-record <targetId:kind:subject>` instead of
scraping prose from handoff notes.
Runtime split handoff move/source coverage is exported as
`splitHandoffMoveCoverage[]`. Each record joins a split target, runtime id,
handoff move set, destination owner, replacement proof, and source path status.
`current` paths are Eve-incubated code that still needs to leave,
`observed-provider` paths are provider-owned pressure sources such as
Aetheria's current Electron client, and `replacement-required` records mark
move sets where no generic runtime source exists yet. Consumer smokes can
assert them with
`--expect-split-handoff-move <splitTarget:runtimeId:moveSetId:pathKind:status:path-substring>`.
After generating the parity report, `run-parity-harness.ps1` copies
`artifacts/conformance/latest` into
@ -266,12 +275,16 @@ target being present is not enough; future owner repos must be able to consume
why it is or is not ready to leave Eve incubation. EveElectron is intentionally
pending: its handoff names the generic Electron shell boundary, while the
Starbridge RTS client remains Aetheria-owned product code until a
provider-agnostic shell exists. EveTui is also intentionally pending: Aetheria
advertises `tui` as an agent-facing lowering target, but Eve should not claim
that target until a real terminal/grid lowerer and transcript capture exist.
EveUnity includes both the active `unity-uitoolkit` package proof and the
pending `unity-scene` runtime boundary; UI Toolkit support is not treated as
full Unity scene/world lowering.
provider-agnostic shell exists. The same smoke asserts that the Aetheria
Electron source paths named by the handoff currently exist as
`observed-provider` sources, so the extraction pressure is visible without
turning those paths into EveElectron authority. EveTui is also intentionally
pending: Aetheria advertises `tui` as an agent-facing lowering target, but Eve
should not claim that target until a real terminal/grid lowerer and transcript
capture exist. EveUnity includes both the active `unity-uitoolkit` package proof
and the pending `unity-scene` runtime boundary; UI Toolkit support is not
treated as full Unity scene/world lowering, and `unity-scene` move sets remain
`replacement-required` until a generic scene body exists.
The Flutter widget smoke also exercises `EveProviderCatalog` and
`EveProviderPicker` against a conformance-export-shaped provider list. That is

View file

@ -17,6 +17,7 @@
"worldSurfaceLoweringCoverage",
"commandBoundaryCoverage",
"splitTargetBlockers",
"splitHandoffMoveCoverage",
"splitTargets"
],
"properties": {
@ -838,6 +839,85 @@
"additionalProperties": true
}
},
"splitHandoffMoveCoverage": {
"type": "array",
"items": {
"type": "object",
"required": [
"splitTarget",
"runtimeId",
"runtimeOwnerRepo",
"handoffPath",
"handoffExportPath",
"moveSetId",
"stage",
"destinationOwner",
"replacementProof",
"pathKind",
"sourcePath",
"status",
"severity"
],
"properties": {
"splitTarget": {
"type": "string",
"minLength": 1
},
"runtimeId": {
"type": "string",
"minLength": 1
},
"runtimeOwnerRepo": {
"type": "string",
"minLength": 1
},
"handoffPath": {
"type": "string",
"minLength": 1
},
"handoffExportPath": {
"type": "string",
"minLength": 1
},
"moveSetId": {
"type": "string",
"minLength": 1
},
"stage": {
"type": "string",
"minLength": 1
},
"destinationOwner": {
"type": "string",
"minLength": 1
},
"replacementProof": {
"type": "string",
"minLength": 1
},
"pathKind": {
"type": "string",
"minLength": 1
},
"sourcePath": {
"type": "string",
"minLength": 1
},
"pathExists": {
"type": "boolean"
},
"status": {
"type": "string",
"minLength": 1
},
"severity": {
"type": "string",
"minLength": 1
}
},
"additionalProperties": true
}
},
"splitTargets": {
"type": "array",
"items": {

View file

@ -45,6 +45,8 @@ try {
--expect-split-target-blocker-record EveUnity:runtime-status:unity-scene `
--expect-split-target-blocker-record EveUnity:runtime-plugin-projection:unity-scene:sai.vn `
--expect-split-target-blocker-record "EveUnity:pending-proof:Unity scene runtime body exists outside Aetheria product code." `
--expect-split-handoff-move EveUnity:unity-scene:unity-scene-runtime-body:replacement-required:no-source-paths:none `
--expect-split-handoff-move EveUnity:unity-scene:unity-scene-command-transport:replacement-required:no-source-paths:none `
--expect-split-target-blocker "EveUnity:Tagged UPM release" `
--expect-split-target-blocker "EveUnity:Unity batchmode EditMode runner" `
--expect-split-target-blocker "EveUnity:Unity batchmode or editor capture artifact" `
@ -55,6 +57,8 @@ try {
--expect-split-target-blocker "EveElectron:runtime:electron-shell:status:pending" `
--expect-split-target-blocker-record EveElectron:runtime-status:electron-shell `
--expect-split-target-blocker-record EveElectron:runtime-feature:electron-shell:providerAdvertisements `
--expect-split-handoff-move EveElectron:electron-shell:electron-shell-runtime:observed-provider:exists:Aetheria.Rts.Web/Electron `
--expect-split-handoff-move EveElectron:electron-shell:electron-command-transport:observed-provider:exists:Aetheria.Rts.Web/Client/app.ts `
--expect-split-target-blocker "EveElectron:Electron shell runtime body exists outside Aetheria product code" `
--expect-split-target EveTui `
--expect-split-target-status EveTui:incubating `

View file

@ -53,6 +53,7 @@ if (!exportDirectory) {
" --expect-split-target-status <targetId:status>",
" --expect-split-target-blocker <targetId:blocker-substring>",
" --expect-split-target-blocker-record <targetId:kind:subject>",
" --expect-split-handoff-move <splitTarget:runtimeId:moveSetId:pathKind:status:path-substring>",
" --expect-split-target-proof <targetId:proof-substring>",
].join("\n"));
process.exit(2);
@ -136,6 +137,7 @@ function validateIndex(index, directory, expectations, errors) {
const worldSurfaceLoweringCoverage = Array.isArray(index.worldSurfaceLoweringCoverage) ? index.worldSurfaceLoweringCoverage : [];
const commandBoundaryCoverage = Array.isArray(index.commandBoundaryCoverage) ? index.commandBoundaryCoverage : [];
const splitTargetBlockers = Array.isArray(index.splitTargetBlockers) ? index.splitTargetBlockers : [];
const splitHandoffMoveCoverage = Array.isArray(index.splitHandoffMoveCoverage) ? index.splitHandoffMoveCoverage : [];
const worldSurfaceLoweringGaps = Array.isArray(index.worldSurfaceLoweringGaps) ? index.worldSurfaceLoweringGaps : [];
validatePluginRecords(plugins, errors);
@ -158,6 +160,7 @@ function validateIndex(index, directory, expectations, errors) {
validateCapabilityGaps(index.capabilityGaps, errors);
validateRuntimePluginProjectionGaps(index.runtimePluginProjectionGaps, errors);
validateSplitTargetBlockers(index.splitTargetBlockers, errors);
validateSplitHandoffMoveCoverage(index.splitHandoffMoveCoverage, errors);
validateWorldSurfaceLoweringCoverage(index.worldSurfaceLoweringCoverage, errors);
validateCommandBoundaryCoverage(index.commandBoundaryCoverage, errors);
validateWorldSurfaceLoweringGaps(index.worldSurfaceLoweringGaps, errors);
@ -609,6 +612,18 @@ function validateIndex(index, directory, expectations, errors) {
errors.push(`splitTargetBlockers:${expectation.targetId}:${expectation.kind}:${expectation.subject}:missing`);
}
}
for (const expectation of expectations.splitHandoffMoves) {
const record = splitHandoffMoveCoverage.find(candidate =>
candidate.splitTarget === expectation.splitTarget &&
candidate.runtimeId === expectation.runtimeId &&
candidate.moveSetId === expectation.moveSetId &&
candidate.pathKind === expectation.pathKind &&
candidate.status === expectation.status &&
candidate.sourcePath?.includes(expectation.pathSubstring));
if (!record) {
errors.push(`splitHandoffMoveCoverage:${expectation.splitTarget}:${expectation.runtimeId}:${expectation.moveSetId}:${expectation.pathKind}:${expectation.status}:${expectation.pathSubstring}:missing`);
}
}
for (const expectation of expectations.splitTargetProofs) {
const target = splitTargets.find(candidate => candidate.id === expectation.targetId);
if (!target) {
@ -736,6 +751,21 @@ function validateSplitTargetBlockers(blockers, errors) {
}
}
function validateSplitHandoffMoveCoverage(records, errors) {
if (!Array.isArray(records)) {
errors.push("splitHandoffMoveCoverage:missing");
return;
}
for (const [index, record] of records.entries()) {
for (const field of ["splitTarget", "runtimeId", "runtimeOwnerRepo", "handoffPath", "handoffExportPath", "moveSetId", "stage", "destinationOwner", "replacementProof", "pathKind", "sourcePath", "status", "severity"]) {
if (!record?.[field]) errors.push(`splitHandoffMoveCoverage:${index}:${field}:missing`);
}
if (typeof record?.pathExists !== "boolean") {
errors.push(`splitHandoffMoveCoverage:${index}:pathExists:expected boolean`);
}
}
}
function validateWorldSurfaceLoweringCoverage(records, errors) {
if (!Array.isArray(records)) {
errors.push("worldSurfaceLoweringCoverage:missing");
@ -931,6 +961,7 @@ function parseArguments(args) {
splitTargetStatuses: [],
splitTargetBlockers: [],
splitTargetBlockerRecords: [],
splitHandoffMoves: [],
splitTargetProofs: [],
capabilityMatrix: false,
capabilityGaps: [],
@ -978,6 +1009,7 @@ function parseArguments(args) {
["--expect-split-target-status", expectations.splitTargetStatuses],
["--expect-split-target-blocker", expectations.splitTargetBlockers],
["--expect-split-target-blocker-record", expectations.splitTargetBlockerRecords],
["--expect-split-handoff-move", expectations.splitHandoffMoves],
["--expect-split-target-proof", expectations.splitTargetProofs],
["--expect-capability-gap", expectations.capabilityGaps],
["--expect-runtime-plugin-gap", expectations.runtimePluginGaps],
@ -1066,6 +1098,8 @@ function parseArguments(args) {
target.push(parseSplitTargetExpectation(value, "blocker"));
} else if (option === "--expect-split-target-blocker-record") {
target.push(parseSplitTargetBlockerRecordExpectation(value));
} else if (option === "--expect-split-handoff-move") {
target.push(parseSplitHandoffMoveExpectation(value));
} else if (option === "--expect-split-target-proof") {
target.push(parseSplitTargetExpectation(value, "proof"));
} else if (option === "--expect-world-lowering-gap") {
@ -1158,6 +1192,22 @@ function parseSplitTargetBlockerRecordExpectation(value) {
};
}
function parseSplitHandoffMoveExpectation(value) {
const parts = value.split(":");
if (parts.length < 6 || parts.slice(0, 5).some(part => !part) || !parts.slice(5).join(":")) {
console.error(`Expected split handoff move in <splitTarget:runtimeId:moveSetId:pathKind:status:path-substring> form, got: ${value}`);
process.exit(2);
}
return {
splitTarget: parts[0],
runtimeId: parts[1],
moveSetId: parts[2],
pathKind: parts[3],
status: parts[4],
pathSubstring: parts.slice(5).join(":"),
};
}
function parsePluginExpectation(value, field) {
const separator = value.indexOf(":");
if (separator <= 0 || separator === value.length - 1) {

View file

@ -1,5 +1,5 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { existsSync } from "node:fs";
import { existsSync, readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { compileEveDsl } from "../../web/eve-dsl.js";
@ -1675,6 +1675,7 @@ function buildConformanceExport(report) {
commandBoundaryCoverage: collectCommandBoundaryCoverage(report),
worldSurfaceLoweringGaps: collectWorldSurfaceLoweringGaps(report),
splitTargetBlockers: collectSplitTargetBlockers(report),
splitHandoffMoveCoverage: collectSplitHandoffMoveCoverage(report),
pluginAbiOperationCoverage: collectPluginAbiOperationCoverage(report),
providerPluginRequirementCoverage: collectProviderPluginRequirementCoverage(report),
capabilityGaps: collectCapabilityGaps(report),
@ -2017,6 +2018,74 @@ function collectSplitTargetBlockers(report) {
return (report.splitTargets || []).flatMap(target => target.blockerRecords || []);
}
function collectSplitHandoffMoveCoverage(report) {
const records = [];
for (const runtime of report.runtimes || []) {
if (!runtime.splitHandoffPath) continue;
const absoluteHandoffPath = path.join(repoRoot, runtime.splitHandoffPath);
if (!existsSync(absoluteHandoffPath)) continue;
let handoff;
try {
handoff = JSON.parse(readFileSync(absoluteHandoffPath, "utf8"));
} catch {
continue;
}
for (const moveSet of handoff.moveSets || []) {
const currentPaths = Array.isArray(moveSet.currentPaths) ? moveSet.currentPaths : [];
const observedProviderPaths = Array.isArray(moveSet.observedProviderPaths) ? moveSet.observedProviderPaths : [];
for (const sourcePath of currentPaths) {
const absoluteSourcePath = path.join(repoRoot, sourcePath);
records.push(buildSplitHandoffMoveRecord(runtime, handoff, moveSet, {
pathKind: "current",
sourcePath,
pathExists: existsSync(absoluteSourcePath),
}));
}
for (const sourcePath of observedProviderPaths) {
records.push(buildSplitHandoffMoveRecord(runtime, handoff, moveSet, {
pathKind: "observed-provider",
sourcePath,
pathExists: existsSync(sourcePath),
}));
}
if (!currentPaths.length && !observedProviderPaths.length) {
records.push(buildSplitHandoffMoveRecord(runtime, handoff, moveSet, {
pathKind: "replacement-required",
sourcePath: "(none)",
pathExists: false,
}));
}
}
}
return records.sort((left, right) =>
`${left.splitTarget}:${left.runtimeId}:${left.moveSetId}:${left.pathKind}:${left.sourcePath}`
.localeCompare(`${right.splitTarget}:${right.runtimeId}:${right.moveSetId}:${right.pathKind}:${right.sourcePath}`));
}
function buildSplitHandoffMoveRecord(runtime, handoff, moveSet, source) {
const status = source.pathKind === "replacement-required"
? "no-source-paths"
: source.pathExists ? "exists" : "missing";
return {
splitTarget: handoff.splitTarget || runtime.splitTarget || "",
runtimeId: handoff.runtimeId || runtime.id || "",
runtimeOwnerRepo: handoff.ownerRepo || resolveRuntimeProjectionOwnerRepo(runtime),
handoffPath: runtime.splitHandoffPath || "",
handoffExportPath: makeHandoffExportPath("runtime", runtime.id, runtime.splitHandoffPath || ""),
moveSetId: moveSet.id || "",
stage: moveSet.stage || "",
destinationOwner: moveSet.destinationOwner || "",
replacementProof: moveSet.replacementProof || "",
pathKind: source.pathKind,
sourcePath: normalizePath(source.sourcePath),
pathExists: source.pathExists,
status,
severity: "blocker",
};
}
function collectInteractiveWorldSurfaces(report) {
const surfaces = [];
for (const provider of report.providers || []) {