Add plugin owner conformance consumer proof

This commit is contained in:
Meta Vens 2026-07-09 01:32:53 +01:00
commit 923a099d2f
5 changed files with 118 additions and 1 deletions

View file

@ -280,6 +280,14 @@ Exit criteria:
Recently cut:
- 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
`--expect-plugin-operation` and `--expect-plugin-capability`, and the export
now carries plugin manifest paths, advertisement paths, split targets,
graduation triggers, and optional plugin dependencies. Sai and Norn can prove
the plugin contract from the conformance export without reading Eve's parity
manifest.
- EveUnity now has a split lifecycle smoke at
`scripts/run-eveunity-lifecycle-smoke.ps1`. It validates the existing
`packages/org.gamecult.eve.unity-uitoolkit/eve-runtime-capability.json`

View file

@ -15,6 +15,7 @@ Push-Location $projectRoot
try {
node .\tools\parity\run-parity.mjs
& (Join-Path $PSScriptRoot "run-conformance-consumer-smoke.ps1")
& (Join-Path $PSScriptRoot "run-plugin-owner-conformance-consumer-smoke.ps1")
} finally {
Pop-Location
}

View file

@ -0,0 +1,59 @@
param(
[string] $ExportDirectory = "artifacts\conformance\latest",
[string] $ConsumerDirectory = "artifacts\plugin-owner-conformance-consumer-smoke"
)
$ErrorActionPreference = "Stop"
$projectRoot = Split-Path -Parent $PSScriptRoot
$sourceExport = if ([System.IO.Path]::IsPathRooted($ExportDirectory)) {
$ExportDirectory
} else {
Join-Path $projectRoot $ExportDirectory
}
$consumerRoot = if ([System.IO.Path]::IsPathRooted($ConsumerDirectory)) {
$ConsumerDirectory
} else {
Join-Path $projectRoot $ConsumerDirectory
}
$consumerExport = Join-Path $consumerRoot "export"
if (-not (Test-Path $sourceExport)) {
throw "Conformance export not found: $sourceExport"
}
if (Test-Path $consumerExport) {
Remove-Item -LiteralPath $consumerExport -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $consumerRoot | Out-Null
Copy-Item -LiteralPath $sourceExport -Destination $consumerExport -Recurse
$consumerScript = Join-Path $projectRoot "tools\conformance\consume-export.mjs"
node $consumerScript $consumerExport `
--expect-pack plugin `
--expect-fixture sai-vn `
--expect-plugin sai.vn `
--expect-plugin norn.graph `
--expect-plugin-operation sai.vn:describe `
--expect-plugin-operation sai.vn:validate `
--expect-plugin-operation sai.vn:project `
--expect-plugin-operation sai.vn:lower `
--expect-plugin-operation sai.vn:measure `
--expect-plugin-operation sai.vn:apply `
--expect-plugin-operation norn.graph:describe `
--expect-plugin-operation norn.graph:validate `
--expect-plugin-operation norn.graph:project `
--expect-plugin-operation norn.graph:lower `
--expect-plugin-operation norn.graph:measure `
--expect-plugin-operation norn.graph:apply `
--expect-plugin-capability sai.vn:vn.stage `
--expect-plugin-capability sai.vn:story.choose `
--expect-plugin-capability sai.vn:story.continue `
--expect-plugin-capability sai.vn:story.jump `
--expect-plugin-capability norn.graph:embed.norn `
--expect-plugin-capability norn.graph:graph.node.activate
if ($LASTEXITCODE -ne 0) {
throw "Plugin owner conformance consumer smoke failed with exit code $LASTEXITCODE"
}

View file

@ -16,6 +16,8 @@ if (!exportDirectory) {
" --expect-runtime <id>",
" --expect-scenario <id>",
" --expect-split-target <id>",
" --expect-plugin-operation <pluginId:operation>",
" --expect-plugin-capability <pluginId:capability>",
].join("\n"));
process.exit(2);
}
@ -95,6 +97,26 @@ function validateIndex(index, directory, expectations, errors) {
for (const expectedPlugin of expectations.plugins) {
if (!plugins.some(plugin => plugin.pluginId === expectedPlugin)) errors.push(`plugins:${expectedPlugin}:missing`);
}
for (const expectation of expectations.pluginOperations) {
const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId);
if (!plugin) {
errors.push(`plugins:${expectation.pluginId}:missing`);
continue;
}
if (!Array.isArray(plugin.abiOperations) || !plugin.abiOperations.includes(expectation.operation)) {
errors.push(`plugins:${expectation.pluginId}:operation:${expectation.operation}:missing`);
}
}
for (const expectation of expectations.pluginCapabilities) {
const plugin = plugins.find(candidate => candidate.pluginId === expectation.pluginId);
if (!plugin) {
errors.push(`plugins:${expectation.pluginId}:missing`);
continue;
}
if (!Array.isArray(plugin.capabilities) || !plugin.capabilities.includes(expectation.capability)) {
errors.push(`plugins:${expectation.pluginId}:capability:${expectation.capability}:missing`);
}
}
for (const expectedProvider of expectations.providers) {
if (!providers.some(provider => provider.providerId === expectedProvider)) errors.push(`providers:${expectedProvider}:missing`);
}
@ -132,6 +154,8 @@ function parseArguments(args) {
packs: [],
fixtures: [],
plugins: [],
pluginOperations: [],
pluginCapabilities: [],
providers: [],
runtimes: [],
scenarios: [],
@ -145,6 +169,8 @@ function parseArguments(args) {
["--expect-runtime", expectations.runtimes],
["--expect-scenario", expectations.scenarios],
["--expect-split-target", expectations.splitTargets],
["--expect-plugin-operation", expectations.pluginOperations],
["--expect-plugin-capability", expectations.pluginCapabilities],
]);
for (let index = 1; index < args.length; index += 1) {
@ -159,9 +185,27 @@ function parseArguments(args) {
console.error(`Missing value for option: ${option}`);
process.exit(2);
}
target.push(value);
if (option === "--expect-plugin-operation") {
target.push(parsePluginExpectation(value, "operation"));
} else if (option === "--expect-plugin-capability") {
target.push(parsePluginExpectation(value, "capability"));
} else {
target.push(value);
}
index += 1;
}
return { exportDirectory: exportPath, expectations };
}
function parsePluginExpectation(value, field) {
const separator = value.indexOf(":");
if (separator <= 0 || separator === value.length - 1) {
console.error(`Expected plugin ${field} in <pluginId:${field}> form, got: ${value}`);
process.exit(2);
}
return {
pluginId: value.slice(0, separator),
[field]: value.slice(separator + 1),
};
}

View file

@ -1341,8 +1341,13 @@ function buildConformanceExport(report) {
pluginId: plugin.pluginId,
status: plugin.status,
ownerRepo: plugin.ownerRepo,
splitTarget: plugin.splitTarget,
graduationTrigger: plugin.graduationTrigger,
manifestPath: plugin.manifestPath,
advertisementPath: plugin.advertisementPath,
abiFixturePath: plugin.abiFixturePath,
abiOperations: plugin.abiOperations || [],
optionalPlugins: plugin.optionalPlugins || [],
capabilities: plugin.capabilities,
})),
providers: (report.providers || []).map(provider => ({