Strengthen provider conformance consumption

This commit is contained in:
Meta Vens 2026-07-09 01:34:27 +01:00
commit 3450e68e98
4 changed files with 74 additions and 1 deletions

View file

@ -280,6 +280,13 @@ Exit criteria:
Recently cut:
- Provider owner conformance consumption now verifies the interactive-world
contract instead of only provider presence. The generic conformance consumer
supports `--expect-provider-surface`, `--expect-provider-command`, and
`--expect-provider-receipt-state`; the export now carries provider receipt
states; and Aetheria's consumer smoke asserts the daemon game/editor surfaces,
daemon command boundary, and accepted/reconciled receipts from the exported
provider pack.
- Plugin owner conformance consumption now has a smoke runner at
`scripts/run-plugin-owner-conformance-consumer-smoke.ps1`. The generic
conformance consumer can assert plugin ABI operations and capabilities with

View file

@ -40,7 +40,15 @@ try {
--expect-pack provider `
--expect-fixture aetheria-world `
--expect-provider aetheria `
--expect-scenario aetheria-world-command-replay
--expect-scenario aetheria-world-command-replay `
--expect-provider-surface aetheria:aetheria.daemon.game `
--expect-provider-surface aetheria:aetheria.daemon.editor `
--expect-provider-command aetheria:aetheria.daemon.commands `
--expect-provider-receipt-state aetheria:accepted `
--expect-provider-receipt-state aetheria:reconciled
if ($LASTEXITCODE -ne 0) {
throw "Aetheria conformance consumer smoke failed with exit code $LASTEXITCODE"
}
} finally {
Pop-Location
}

View file

@ -18,6 +18,9 @@ if (!exportDirectory) {
" --expect-split-target <id>",
" --expect-plugin-operation <pluginId:operation>",
" --expect-plugin-capability <pluginId:capability>",
" --expect-provider-surface <providerId:surfaceId>",
" --expect-provider-command <providerId:command>",
" --expect-provider-receipt-state <providerId:state>",
].join("\n"));
process.exit(2);
}
@ -120,6 +123,36 @@ function validateIndex(index, directory, expectations, errors) {
for (const expectedProvider of expectations.providers) {
if (!providers.some(provider => provider.providerId === expectedProvider)) errors.push(`providers:${expectedProvider}:missing`);
}
for (const expectation of expectations.providerSurfaces) {
const provider = providers.find(candidate => candidate.providerId === expectation.providerId);
if (!provider) {
errors.push(`providers:${expectation.providerId}:missing`);
continue;
}
if (!Array.isArray(provider.surfaces) || !provider.surfaces.includes(expectation.surfaceId)) {
errors.push(`providers:${expectation.providerId}:surface:${expectation.surfaceId}:missing`);
}
}
for (const expectation of expectations.providerCommands) {
const provider = providers.find(candidate => candidate.providerId === expectation.providerId);
if (!provider) {
errors.push(`providers:${expectation.providerId}:missing`);
continue;
}
if (!Array.isArray(provider.commands) || !provider.commands.includes(expectation.command)) {
errors.push(`providers:${expectation.providerId}:command:${expectation.command}:missing`);
}
}
for (const expectation of expectations.providerReceiptStates) {
const provider = providers.find(candidate => candidate.providerId === expectation.providerId);
if (!provider) {
errors.push(`providers:${expectation.providerId}:missing`);
continue;
}
if (!Array.isArray(provider.receiptStates) || !provider.receiptStates.includes(expectation.state)) {
errors.push(`providers:${expectation.providerId}:receiptState:${expectation.state}:missing`);
}
}
for (const expectedRuntime of expectations.runtimes) {
if (!runtimes.some(runtime => runtime.runtimeId === expectedRuntime)) errors.push(`runtimes:${expectedRuntime}:missing`);
}
@ -157,6 +190,9 @@ function parseArguments(args) {
pluginOperations: [],
pluginCapabilities: [],
providers: [],
providerSurfaces: [],
providerCommands: [],
providerReceiptStates: [],
runtimes: [],
scenarios: [],
splitTargets: [],
@ -171,6 +207,9 @@ function parseArguments(args) {
["--expect-split-target", expectations.splitTargets],
["--expect-plugin-operation", expectations.pluginOperations],
["--expect-plugin-capability", expectations.pluginCapabilities],
["--expect-provider-surface", expectations.providerSurfaces],
["--expect-provider-command", expectations.providerCommands],
["--expect-provider-receipt-state", expectations.providerReceiptStates],
]);
for (let index = 1; index < args.length; index += 1) {
@ -189,6 +228,12 @@ function parseArguments(args) {
target.push(parsePluginExpectation(value, "operation"));
} else if (option === "--expect-plugin-capability") {
target.push(parsePluginExpectation(value, "capability"));
} else if (option === "--expect-provider-surface") {
target.push(parseProviderExpectation(value, "surfaceId"));
} else if (option === "--expect-provider-command") {
target.push(parseProviderExpectation(value, "command"));
} else if (option === "--expect-provider-receipt-state") {
target.push(parseProviderExpectation(value, "state"));
} else {
target.push(value);
}
@ -209,3 +254,15 @@ function parsePluginExpectation(value, field) {
[field]: value.slice(separator + 1),
};
}
function parseProviderExpectation(value, field) {
const separator = value.indexOf(":");
if (separator <= 0 || separator === value.length - 1) {
console.error(`Expected provider ${field} in <providerId:${field}> form, got: ${value}`);
process.exit(2);
}
return {
providerId: value.slice(0, separator),
[field]: value.slice(separator + 1),
};
}

View file

@ -1357,6 +1357,7 @@ function buildConformanceExport(report) {
advertisementPath: provider.advertisementPath,
scenarioPath: provider.scenarioPath,
scenarioId: provider.scenarioId,
receiptStates: provider.scenarioReceiptStates || [],
surfaces: provider.surfaceIds,
commands: provider.commandIds,
pluginRequirements: provider.pluginRequirements,