Export command boundary coverage
This commit is contained in:
parent
a4fdc2f6df
commit
79db997e28
6 changed files with 191 additions and 0 deletions
|
|
@ -304,6 +304,12 @@ Recently cut:
|
|||
as `claimed`, `missing-claim`, or `missing-runtime`. The generic conformance
|
||||
consumer now asserts Aetheria's claimed web/UI Toolkit targets and missing
|
||||
Unity scene/TUI targets from the same typed surface.
|
||||
- The conformance export carries `commandBoundaryCoverage[]`, joining each
|
||||
provider-owned interactive world surface target to its runtime owner,
|
||||
provider command boundary, receipt schema, and runtime command envelope. Web
|
||||
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 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
|
||||
|
|
|
|||
|
|
@ -126,6 +126,15 @@ receipt schema, lowering targets, and ownership rule so runtime lowerers can
|
|||
discover provider-authored world surfaces without importing provider source
|
||||
layout. Consumer smokes can assert advertised targets with
|
||||
`--expect-interactive-world-surface <providerId:surfaceId:targetId:ownerRepo>`.
|
||||
The command path for those targets is exported as
|
||||
`commandBoundaryCoverage[]`. Each record joins a provider-owned surface,
|
||||
advertised lowering target, runtime owner, provider command boundary, receipt
|
||||
schema, and runtime command envelope schema. Status is `covered` only when the
|
||||
runtime has claimed the target and advertises `gamecult.eve.command.v1`;
|
||||
otherwise it reports `missing-runtime`, `missing-runtime-claim`,
|
||||
`missing-provider-boundary`, or `missing-command-transport`. Consumer smokes can
|
||||
assert it with
|
||||
`--expect-command-boundary-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>`.
|
||||
Declared runtime/plugin projection gaps are exported as
|
||||
`runtimePluginProjectionGaps[]`. These records carry runtime id, runtime owner,
|
||||
split target, plugin id, reason, severity, and fixture lists so EveUnity,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"providerPluginRequirementCoverage",
|
||||
"interactiveWorldSurfaces",
|
||||
"worldSurfaceLoweringCoverage",
|
||||
"commandBoundaryCoverage",
|
||||
"splitTargetBlockers",
|
||||
"splitTargets"
|
||||
],
|
||||
|
|
@ -414,6 +415,79 @@
|
|||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"commandBoundaryCoverage": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"providerId",
|
||||
"providerOwnerRepo",
|
||||
"surfaceId",
|
||||
"surfaceKind",
|
||||
"targetId",
|
||||
"runtimeId",
|
||||
"runtimeOwnerRepo",
|
||||
"status",
|
||||
"severity",
|
||||
"commandBoundary",
|
||||
"receiptSchema",
|
||||
"runtimeCommandSchema"
|
||||
],
|
||||
"properties": {
|
||||
"providerId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"providerOwnerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"surfaceId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"surfaceKind": {
|
||||
"type": "string"
|
||||
},
|
||||
"targetId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtimeId": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"runtimeOwnerRepo": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"splitTarget": {
|
||||
"type": "string"
|
||||
},
|
||||
"runtimeStatus": {
|
||||
"type": "string"
|
||||
},
|
||||
"status": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"severity": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"commandBoundary": {
|
||||
"type": "string"
|
||||
},
|
||||
"receiptSchema": {
|
||||
"type": "string"
|
||||
},
|
||||
"runtimeCommandSchema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": true
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,10 @@ try {
|
|||
--expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:claimed:EveUnity:unity-uitoolkit `
|
||||
--expect-world-lowering-coverage aetheria:aetheria.daemon.game:unity-scene:missing-claim:EveUnity:unity-scene `
|
||||
--expect-world-lowering-coverage aetheria:aetheria.daemon.editor:tui:missing-claim:EveTui:tui `
|
||||
--expect-command-boundary-coverage aetheria:aetheria.daemon.game:web-reference:covered:Eve:web `
|
||||
--expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-uitoolkit:covered:EveUnity:unity-uitoolkit `
|
||||
--expect-command-boundary-coverage aetheria:aetheria.daemon.game:unity-scene:missing-runtime-claim:EveUnity:unity-scene `
|
||||
--expect-command-boundary-coverage aetheria:aetheria.daemon.editor:tui:missing-runtime-claim:EveTui:tui `
|
||||
--expect-world-lowering-gap aetheria:aetheria.daemon.game:unity-scene:EveUnity:unity-scene `
|
||||
--expect-world-lowering-gap aetheria:aetheria.daemon.game:tui:EveTui:tui `
|
||||
--expect-world-lowering-gap aetheria:aetheria.daemon.editor:tui:EveTui:tui `
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ if (!exportDirectory) {
|
|||
" --expect-capability-gap <substring>",
|
||||
" --expect-runtime-plugin-gap <runtimeId:pluginId:ownerRepo>",
|
||||
" --expect-world-lowering-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
" --expect-command-boundary-coverage <providerId:surfaceId:targetId:status:ownerRepo:runtimeId>",
|
||||
" --expect-world-lowering-gap <providerId:surfaceId:targetId:ownerRepo:runtimeId>",
|
||||
" --expect-conformance-handoff",
|
||||
" --expect-plugin-operation <pluginId:operation>",
|
||||
|
|
@ -133,6 +134,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
const runtimePluginProjectionGaps = Array.isArray(index.runtimePluginProjectionGaps) ? index.runtimePluginProjectionGaps : [];
|
||||
const interactiveWorldSurfaces = Array.isArray(index.interactiveWorldSurfaces) ? index.interactiveWorldSurfaces : [];
|
||||
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 worldSurfaceLoweringGaps = Array.isArray(index.worldSurfaceLoweringGaps) ? index.worldSurfaceLoweringGaps : [];
|
||||
|
||||
|
|
@ -157,6 +159,7 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
validateRuntimePluginProjectionGaps(index.runtimePluginProjectionGaps, errors);
|
||||
validateSplitTargetBlockers(index.splitTargetBlockers, errors);
|
||||
validateWorldSurfaceLoweringCoverage(index.worldSurfaceLoweringCoverage, errors);
|
||||
validateCommandBoundaryCoverage(index.commandBoundaryCoverage, errors);
|
||||
validateWorldSurfaceLoweringGaps(index.worldSurfaceLoweringGaps, errors);
|
||||
for (const expectedGap of expectations.capabilityGaps) {
|
||||
if (!capabilityGaps.some(gap => capabilityGapText(gap).includes(expectedGap))) {
|
||||
|
|
@ -197,6 +200,25 @@ function validateIndex(index, directory, expectations, errors) {
|
|||
errors.push(`worldSurfaceLoweringCoverage:${expectation.providerId}:${expectation.surfaceId}:${expectation.targetId}:runtimeId:expected ${expectation.runtimeId} got ${coverage.runtimeId || ""}`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.commandBoundaryCoverage) {
|
||||
const coverage = commandBoundaryCoverage.find(candidate =>
|
||||
candidate.providerId === expectation.providerId &&
|
||||
candidate.surfaceId === expectation.surfaceId &&
|
||||
candidate.targetId === expectation.targetId);
|
||||
if (!coverage) {
|
||||
errors.push(`commandBoundaryCoverage:${expectation.providerId}:${expectation.surfaceId}:${expectation.targetId}:missing`);
|
||||
continue;
|
||||
}
|
||||
if (coverage.status !== expectation.status) {
|
||||
errors.push(`commandBoundaryCoverage:${expectation.providerId}:${expectation.surfaceId}:${expectation.targetId}:status:expected ${expectation.status} got ${coverage.status || ""}`);
|
||||
}
|
||||
if (coverage.runtimeOwnerRepo !== expectation.ownerRepo) {
|
||||
errors.push(`commandBoundaryCoverage:${expectation.providerId}:${expectation.surfaceId}:${expectation.targetId}:runtimeOwnerRepo:expected ${expectation.ownerRepo} got ${coverage.runtimeOwnerRepo || ""}`);
|
||||
}
|
||||
if (coverage.runtimeId !== expectation.runtimeId) {
|
||||
errors.push(`commandBoundaryCoverage:${expectation.providerId}:${expectation.surfaceId}:${expectation.targetId}:runtimeId:expected ${expectation.runtimeId} got ${coverage.runtimeId || ""}`);
|
||||
}
|
||||
}
|
||||
for (const expectation of expectations.worldLoweringGaps) {
|
||||
const gap = worldSurfaceLoweringGaps.find(candidate =>
|
||||
candidate.providerId === expectation.providerId &&
|
||||
|
|
@ -726,6 +748,21 @@ function validateWorldSurfaceLoweringCoverage(records, errors) {
|
|||
}
|
||||
}
|
||||
|
||||
function validateCommandBoundaryCoverage(records, errors) {
|
||||
if (!Array.isArray(records)) {
|
||||
errors.push("commandBoundaryCoverage:missing");
|
||||
return;
|
||||
}
|
||||
for (const [index, record] of records.entries()) {
|
||||
for (const field of ["providerId", "providerOwnerRepo", "surfaceId", "targetId", "runtimeId", "runtimeOwnerRepo", "status", "severity", "commandBoundary", "receiptSchema"]) {
|
||||
if (!record?.[field]) errors.push(`commandBoundaryCoverage:${index}:${field}:missing`);
|
||||
}
|
||||
if (record?.status === "covered" && record.runtimeCommandSchema !== "gamecult.eve.command.v1") {
|
||||
errors.push(`commandBoundaryCoverage:${index}:runtimeCommandSchema:expected gamecult.eve.command.v1 got ${record.runtimeCommandSchema || ""}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function validateWorldSurfaceLoweringGaps(gaps, errors) {
|
||||
if (!Array.isArray(gaps)) {
|
||||
errors.push("worldSurfaceLoweringGaps:missing");
|
||||
|
|
@ -899,6 +936,7 @@ function parseArguments(args) {
|
|||
capabilityGaps: [],
|
||||
runtimePluginGaps: [],
|
||||
worldLoweringCoverage: [],
|
||||
commandBoundaryCoverage: [],
|
||||
worldLoweringGaps: [],
|
||||
conformanceHandoff: false,
|
||||
};
|
||||
|
|
@ -944,6 +982,7 @@ function parseArguments(args) {
|
|||
["--expect-capability-gap", expectations.capabilityGaps],
|
||||
["--expect-runtime-plugin-gap", expectations.runtimePluginGaps],
|
||||
["--expect-world-lowering-coverage", expectations.worldLoweringCoverage],
|
||||
["--expect-command-boundary-coverage", expectations.commandBoundaryCoverage],
|
||||
["--expect-world-lowering-gap", expectations.worldLoweringGaps],
|
||||
]);
|
||||
|
||||
|
|
@ -1033,6 +1072,8 @@ function parseArguments(args) {
|
|||
target.push(parseWorldLoweringGapExpectation(value));
|
||||
} else if (option === "--expect-world-lowering-coverage") {
|
||||
target.push(parseWorldLoweringCoverageExpectation(value));
|
||||
} else if (option === "--expect-command-boundary-coverage") {
|
||||
target.push(parseCommandBoundaryCoverageExpectation(value));
|
||||
} else if (option === "--expect-runtime-plugin-gap") {
|
||||
target.push(parseRuntimePluginGapExpectation(value));
|
||||
} else {
|
||||
|
|
@ -1075,6 +1116,22 @@ function parseWorldLoweringCoverageExpectation(value) {
|
|||
};
|
||||
}
|
||||
|
||||
function parseCommandBoundaryCoverageExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 6 || parts.some(part => !part)) {
|
||||
console.error(`Expected command boundary coverage in <providerId:surfaceId:targetId:status:ownerRepo:runtimeId> form, got: ${value}`);
|
||||
process.exit(2);
|
||||
}
|
||||
return {
|
||||
providerId: parts[0],
|
||||
surfaceId: parts[1],
|
||||
targetId: parts[2],
|
||||
status: parts[3],
|
||||
ownerRepo: parts[4],
|
||||
runtimeId: parts[5],
|
||||
};
|
||||
}
|
||||
|
||||
function parseRuntimePluginGapExpectation(value) {
|
||||
const parts = value.split(":");
|
||||
if (parts.length !== 3 || parts.some(part => !part)) {
|
||||
|
|
|
|||
|
|
@ -1672,6 +1672,7 @@ function buildConformanceExport(report) {
|
|||
runtimePluginProjectionGaps: collectRuntimePluginProjectionGaps(report),
|
||||
interactiveWorldSurfaces: collectInteractiveWorldSurfaces(report),
|
||||
worldSurfaceLoweringCoverage: collectWorldSurfaceLoweringCoverage(report),
|
||||
commandBoundaryCoverage: collectCommandBoundaryCoverage(report),
|
||||
worldSurfaceLoweringGaps: collectWorldSurfaceLoweringGaps(report),
|
||||
splitTargetBlockers: collectSplitTargetBlockers(report),
|
||||
pluginAbiOperationCoverage: collectPluginAbiOperationCoverage(report),
|
||||
|
|
@ -2069,6 +2070,46 @@ function collectWorldSurfaceLoweringCoverage(report) {
|
|||
return coverage;
|
||||
}
|
||||
|
||||
function collectCommandBoundaryCoverage(report) {
|
||||
const { claimByTarget, runtimeByTarget } = buildWorldSurfaceLoweringTargetIndex(report);
|
||||
const coverage = [];
|
||||
for (const surface of collectInteractiveWorldSurfaces(report)) {
|
||||
for (const targetId of surface.loweringTargets || []) {
|
||||
const claim = claimByTarget.get(targetId);
|
||||
const runtime = runtimeByTarget.get(targetId);
|
||||
const runtimeCommandSchema = runtime?.commandTransportSmoke?.schema || "";
|
||||
const missingProviderBoundary = !surface.commandBoundary || !surface.receiptSchema;
|
||||
let status = "covered";
|
||||
if (!runtime) {
|
||||
status = "missing-runtime";
|
||||
} else if (!claim) {
|
||||
status = "missing-runtime-claim";
|
||||
} else if (missingProviderBoundary) {
|
||||
status = "missing-provider-boundary";
|
||||
} else if (runtimeCommandSchema !== "gamecult.eve.command.v1") {
|
||||
status = "missing-command-transport";
|
||||
}
|
||||
coverage.push({
|
||||
providerId: surface.providerId,
|
||||
providerOwnerRepo: surface.ownerRepo || "",
|
||||
surfaceId: surface.surfaceId,
|
||||
surfaceKind: surface.surfaceKind || "",
|
||||
targetId,
|
||||
runtimeId: runtime?.id || "world-surface-lowering",
|
||||
runtimeOwnerRepo: runtime ? resolveRuntimeProjectionOwnerRepo(runtime) : "Eve",
|
||||
splitTarget: runtime?.splitTarget || "",
|
||||
runtimeStatus: runtime?.status || "",
|
||||
status,
|
||||
severity: status === "covered" ? "ok" : "blocker",
|
||||
commandBoundary: surface.commandBoundary || "",
|
||||
receiptSchema: surface.receiptSchema || "",
|
||||
runtimeCommandSchema,
|
||||
});
|
||||
}
|
||||
}
|
||||
return coverage;
|
||||
}
|
||||
|
||||
function resolveRuntimeProjectionOwnerRepo(runtime) {
|
||||
return runtime.lifecycle?.release?.ownerRepo
|
||||
|| runtime.lifecycle?.test?.ownerRepo
|
||||
|
|
|
|||
Loading…
Reference in a new issue