Export runtime capture probe coverage
This commit is contained in:
parent
e063828c2c
commit
419c747ba6
7 changed files with 290 additions and 1 deletions
|
|
@ -614,6 +614,14 @@ Recently cut:
|
||||||
`artifacts/eveelectron-capture/latest/electron-shell-projection.json`, and
|
`artifacts/eveelectron-capture/latest/electron-shell-projection.json`, and
|
||||||
exports it as `runtime.captureArtifacts[]`. The actual packaged window PNG
|
exports it as `runtime.captureArtifacts[]`. The actual packaged window PNG
|
||||||
remains a split blocker for EveElectron.
|
remains a split blocker for EveElectron.
|
||||||
|
- Runtime capture probe coverage is now exported as
|
||||||
|
`runtimeCaptureProbeCoverage[]`. It records the lifecycle capture contract,
|
||||||
|
capture owner, requested artifact kind, current semantic/cell-grid artifact,
|
||||||
|
request path, pending proofs, and validation errors for each runtime with
|
||||||
|
capture evidence. The consumer smokes assert that Unity UI Toolkit, Unity
|
||||||
|
scene, and Electron currently have `json-projection` semantic artifacts while
|
||||||
|
their contracted `png` captures remain owner-repo work; TUI's current
|
||||||
|
`json-grid` artifact satisfies its contract but still needs EveTui ownership.
|
||||||
- EveTui capture lifecycle now attaches a typed terminal-grid artifact instead
|
- EveTui capture lifecycle now attaches a typed terminal-grid artifact instead
|
||||||
of stopping at a request contract. `tools/evetui/evetui-capture-artifact.mjs`
|
of stopping at a request contract. `tools/evetui/evetui-capture-artifact.mjs`
|
||||||
lowers `web/fixtures/aetheria-world-surface.json` through `EveTuiShell`,
|
lowers `web/fixtures/aetheria-world-surface.json` through `EveTuiShell`,
|
||||||
|
|
|
||||||
|
|
@ -454,6 +454,14 @@ It records scene graph projection, embedded slots, and sidecar plugin projection
|
||||||
metadata. The remaining blocker is Unity screenshot or frame-capture PNG
|
metadata. The remaining blocker is Unity screenshot or frame-capture PNG
|
||||||
production from EveUnity.
|
production from EveUnity.
|
||||||
|
|
||||||
|
Runtime capture probe coverage is exported as `runtimeCaptureProbeCoverage[]`.
|
||||||
|
Each record joins runtime status, capture owner, lifecycle capture contract,
|
||||||
|
current semantic/cell-grid artifact, pending proofs, and artifact errors. This
|
||||||
|
keeps `json-projection` evidence useful without letting it impersonate the
|
||||||
|
contracted `png` artifact for Unity or Electron. Runtime-owner consumers can
|
||||||
|
assert the ledger with
|
||||||
|
`--expect-runtime-capture-probe <runtimeId:status:contractArtifactKind:captureOwnerRepo>`.
|
||||||
|
|
||||||
EveUnity split handoff evidence:
|
EveUnity split handoff evidence:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|
@ -579,7 +587,8 @@ Pending runtimes are allowed. Silent fake parity is not.
|
||||||
## Next Cuts
|
## Next Cuts
|
||||||
|
|
||||||
1. Extend runtime-owned image/layout probes to the Unity/Electron capture
|
1. Extend runtime-owned image/layout probes to the Unity/Electron capture
|
||||||
blockers and move EveTui's JSON grid capture path into the EveTui owner repo.
|
blockers with actual owner-produced image artifacts and move EveTui's JSON
|
||||||
|
grid capture path into the EveTui owner repo.
|
||||||
2. Normalize text scale and font loading across web, Flutter Android, Flutter
|
2. Normalize text scale and font loading across web, Flutter Android, Flutter
|
||||||
desktop, and iOS.
|
desktop, and iOS.
|
||||||
3. Give iOS a real `vn.stage` scene compositor instead of compact stacked
|
3. Give iOS a real `vn.stage` scene compositor instead of compact stacked
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@
|
||||||
"worldSurfaceLoweringCoverage",
|
"worldSurfaceLoweringCoverage",
|
||||||
"commandBoundaryCoverage",
|
"commandBoundaryCoverage",
|
||||||
"screenshotComparisonMetrics",
|
"screenshotComparisonMetrics",
|
||||||
|
"runtimeCaptureProbeCoverage",
|
||||||
"splitTargetBlockers",
|
"splitTargetBlockers",
|
||||||
"splitHandoffMoveCoverage",
|
"splitHandoffMoveCoverage",
|
||||||
"pluginHandoffMoveCoverage",
|
"pluginHandoffMoveCoverage",
|
||||||
|
|
@ -820,6 +821,118 @@
|
||||||
"additionalProperties": true
|
"additionalProperties": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"runtimeCaptureProbeCoverage": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"runtimeId",
|
||||||
|
"runtimeStatus",
|
||||||
|
"runtimeOwnerRepo",
|
||||||
|
"splitTarget",
|
||||||
|
"captureStatus",
|
||||||
|
"captureOwnerRepo",
|
||||||
|
"contractArtifactKind",
|
||||||
|
"currentArtifactKind",
|
||||||
|
"currentArtifactPath",
|
||||||
|
"pendingProofs",
|
||||||
|
"errors",
|
||||||
|
"status",
|
||||||
|
"severity",
|
||||||
|
"detail"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"runtimeId": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"runtimeStatus": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"runtimeOwnerRepo": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"splitTarget": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"captureStatus": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"lifecycleStatus": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"captureOwnerRepo": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"captureKind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"contractArtifactKind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"contractRequestSchema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"requestBuilder": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"advertisementPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"artifactPattern": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"conformanceAttachment": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"requiredProvider": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"requiredSurface": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"authority": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"currentArtifactKind": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"currentArtifactSchema": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"currentArtifactPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"requestPath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sourceSurfacePath": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pendingProofs": {
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"severity": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
},
|
||||||
|
"detail": {
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"plugins": {
|
"plugins": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,10 @@ try {
|
||||||
--expect-screenshot-metric web:sai-vn:color-tokens:pass `
|
--expect-screenshot-metric web:sai-vn:color-tokens:pass `
|
||||||
--expect-screenshot-metric web:sai-vn:bounding-boxes:pass `
|
--expect-screenshot-metric web:sai-vn:bounding-boxes:pass `
|
||||||
--expect-screenshot-metric electron-shell:aetheria-world:bounding-boxes:pending-capture `
|
--expect-screenshot-metric electron-shell:aetheria-world:bounding-boxes:pending-capture `
|
||||||
|
--expect-runtime-capture-probe unity-uitoolkit:semantic-artifact-present-capture-pending:png:EveUnity `
|
||||||
|
--expect-runtime-capture-probe unity-scene:semantic-artifact-present-capture-pending:png:EveUnity `
|
||||||
|
--expect-runtime-capture-probe electron-shell:semantic-artifact-present-capture-pending:png:EveElectron `
|
||||||
|
--expect-runtime-capture-probe tui:contract-artifact-present:json-grid:EveTui `
|
||||||
--expect-capability-gap "split-target:EveUnity:EveUnity:proof:Tagged UPM release" `
|
--expect-capability-gap "split-target:EveUnity:EveUnity:proof:Tagged UPM release" `
|
||||||
--expect-conformance-handoff
|
--expect-conformance-handoff
|
||||||
if ($LASTEXITCODE -ne 0) {
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,10 @@ node $consumerScript $consumerExport `
|
||||||
--expect-runtime-capture-status tui:json-grid `
|
--expect-runtime-capture-status tui:json-grid `
|
||||||
--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-status direct2d:missing `
|
--expect-runtime-capture-status direct2d:missing `
|
||||||
|
--expect-runtime-capture-probe unity-uitoolkit:semantic-artifact-present-capture-pending:png:EveUnity `
|
||||||
|
--expect-runtime-capture-probe unity-scene:semantic-artifact-present-capture-pending:png:EveUnity `
|
||||||
|
--expect-runtime-capture-probe electron-shell:semantic-artifact-present-capture-pending:png:EveElectron `
|
||||||
|
--expect-runtime-capture-probe tui:contract-artifact-present:json-grid:EveTui `
|
||||||
--expect-screenshot-metric web:embedded-surface:structure:pass `
|
--expect-screenshot-metric web:embedded-surface:structure:pass `
|
||||||
--expect-screenshot-metric web:embedded-surface:color-tokens:pass `
|
--expect-screenshot-metric web:embedded-surface:color-tokens:pass `
|
||||||
--expect-screenshot-metric web:embedded-surface:text-presence:pass `
|
--expect-screenshot-metric web:embedded-surface:text-presence:pass `
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ if (!exportDirectory) {
|
||||||
" --expect-command-boundary-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-world-lowering-gap <providerId:surfaceId:targetId:ownerRepo:runtimeId>",
|
||||||
" --expect-screenshot-metric <runtimeId:fixtureId:metricKind:status>",
|
" --expect-screenshot-metric <runtimeId:fixtureId:metricKind:status>",
|
||||||
|
" --expect-runtime-capture-probe <runtimeId:status:contractArtifactKind:captureOwnerRepo>",
|
||||||
" --expect-conformance-handoff",
|
" --expect-conformance-handoff",
|
||||||
" --expect-plugin-operation <pluginId:operation>",
|
" --expect-plugin-operation <pluginId:operation>",
|
||||||
" --expect-plugin-capability <pluginId:capability>",
|
" --expect-plugin-capability <pluginId:capability>",
|
||||||
|
|
@ -151,6 +152,7 @@ function validateIndex(index, directory, expectations, errors) {
|
||||||
const worldSurfaceLoweringCoverage = Array.isArray(index.worldSurfaceLoweringCoverage) ? index.worldSurfaceLoweringCoverage : [];
|
const worldSurfaceLoweringCoverage = Array.isArray(index.worldSurfaceLoweringCoverage) ? index.worldSurfaceLoweringCoverage : [];
|
||||||
const commandBoundaryCoverage = Array.isArray(index.commandBoundaryCoverage) ? index.commandBoundaryCoverage : [];
|
const commandBoundaryCoverage = Array.isArray(index.commandBoundaryCoverage) ? index.commandBoundaryCoverage : [];
|
||||||
const screenshotComparisonMetrics = Array.isArray(index.screenshotComparisonMetrics) ? index.screenshotComparisonMetrics : [];
|
const screenshotComparisonMetrics = Array.isArray(index.screenshotComparisonMetrics) ? index.screenshotComparisonMetrics : [];
|
||||||
|
const runtimeCaptureProbeCoverage = Array.isArray(index.runtimeCaptureProbeCoverage) ? index.runtimeCaptureProbeCoverage : [];
|
||||||
const splitTargetBlockers = Array.isArray(index.splitTargetBlockers) ? index.splitTargetBlockers : [];
|
const splitTargetBlockers = Array.isArray(index.splitTargetBlockers) ? index.splitTargetBlockers : [];
|
||||||
const splitHandoffMoveCoverage = Array.isArray(index.splitHandoffMoveCoverage) ? index.splitHandoffMoveCoverage : [];
|
const splitHandoffMoveCoverage = Array.isArray(index.splitHandoffMoveCoverage) ? index.splitHandoffMoveCoverage : [];
|
||||||
const pluginHandoffMoveCoverage = Array.isArray(index.pluginHandoffMoveCoverage) ? index.pluginHandoffMoveCoverage : [];
|
const pluginHandoffMoveCoverage = Array.isArray(index.pluginHandoffMoveCoverage) ? index.pluginHandoffMoveCoverage : [];
|
||||||
|
|
@ -186,6 +188,7 @@ function validateIndex(index, directory, expectations, errors) {
|
||||||
validateWorldSurfaceLoweringCoverage(index.worldSurfaceLoweringCoverage, errors);
|
validateWorldSurfaceLoweringCoverage(index.worldSurfaceLoweringCoverage, errors);
|
||||||
validateCommandBoundaryCoverage(index.commandBoundaryCoverage, errors);
|
validateCommandBoundaryCoverage(index.commandBoundaryCoverage, errors);
|
||||||
validateScreenshotComparisonMetrics(index.screenshotComparisonMetrics, errors);
|
validateScreenshotComparisonMetrics(index.screenshotComparisonMetrics, errors);
|
||||||
|
validateRuntimeCaptureProbeCoverage(index.runtimeCaptureProbeCoverage, errors);
|
||||||
validateWorldSurfaceLoweringGaps(index.worldSurfaceLoweringGaps, errors);
|
validateWorldSurfaceLoweringGaps(index.worldSurfaceLoweringGaps, errors);
|
||||||
for (const expectedGap of expectations.capabilityGaps) {
|
for (const expectedGap of expectations.capabilityGaps) {
|
||||||
if (!capabilityGaps.some(gap => capabilityGapText(gap).includes(expectedGap))) {
|
if (!capabilityGaps.some(gap => capabilityGapText(gap).includes(expectedGap))) {
|
||||||
|
|
@ -339,6 +342,22 @@ function validateIndex(index, directory, expectations, errors) {
|
||||||
errors.push(`screenshotComparisonMetrics:${expectation.runtimeId}:${expectation.fixtureId}:${expectation.metricKind}:status:expected ${expectation.status} got ${metric.status || ""}`);
|
errors.push(`screenshotComparisonMetrics:${expectation.runtimeId}:${expectation.fixtureId}:${expectation.metricKind}:status:expected ${expectation.status} got ${metric.status || ""}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (const expectation of expectations.runtimeCaptureProbes) {
|
||||||
|
const record = runtimeCaptureProbeCoverage.find(candidate => candidate.runtimeId === expectation.runtimeId);
|
||||||
|
if (!record) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${expectation.runtimeId}:missing`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (record.status !== expectation.status) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${expectation.runtimeId}:status:expected ${expectation.status} got ${record.status || ""}`);
|
||||||
|
}
|
||||||
|
if (record.contractArtifactKind !== expectation.contractArtifactKind) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${expectation.runtimeId}:contractArtifactKind:expected ${expectation.contractArtifactKind} got ${record.contractArtifactKind || ""}`);
|
||||||
|
}
|
||||||
|
if (record.captureOwnerRepo !== expectation.captureOwnerRepo) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${expectation.runtimeId}:captureOwnerRepo:expected ${expectation.captureOwnerRepo} got ${record.captureOwnerRepo || ""}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const expectedFixture of expectations.fixtures) {
|
for (const expectedFixture of expectations.fixtures) {
|
||||||
if (!fixtureIds.has(expectedFixture)) errors.push(`fixture:${expectedFixture}:missing`);
|
if (!fixtureIds.has(expectedFixture)) errors.push(`fixture:${expectedFixture}:missing`);
|
||||||
|
|
@ -1030,6 +1049,31 @@ function validateScreenshotComparisonMetrics(records, errors) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateRuntimeCaptureProbeCoverage(records, errors) {
|
||||||
|
if (!Array.isArray(records)) {
|
||||||
|
errors.push("runtimeCaptureProbeCoverage:missing");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!records.length) {
|
||||||
|
errors.push("runtimeCaptureProbeCoverage:empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const [index, record] of records.entries()) {
|
||||||
|
for (const field of ["runtimeId", "runtimeOwnerRepo", "captureOwnerRepo", "status", "severity", "detail"]) {
|
||||||
|
if (!record?.[field]) errors.push(`runtimeCaptureProbeCoverage:${index}:${field}:missing`);
|
||||||
|
}
|
||||||
|
for (const field of ["pendingProofs", "errors"]) {
|
||||||
|
if (!Array.isArray(record?.[field])) errors.push(`runtimeCaptureProbeCoverage:${index}:${field}:expected array`);
|
||||||
|
}
|
||||||
|
if (record?.currentArtifactPath && !record.currentArtifactKind) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${index}:currentArtifactKind:missing`);
|
||||||
|
}
|
||||||
|
if (record?.contractArtifactKind && !record.contractRequestSchema) {
|
||||||
|
errors.push(`runtimeCaptureProbeCoverage:${index}:contractRequestSchema:missing`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function validateWorldSurfaceLoweringGaps(gaps, errors) {
|
function validateWorldSurfaceLoweringGaps(gaps, errors) {
|
||||||
if (!Array.isArray(gaps)) {
|
if (!Array.isArray(gaps)) {
|
||||||
errors.push("worldSurfaceLoweringGaps:missing");
|
errors.push("worldSurfaceLoweringGaps:missing");
|
||||||
|
|
@ -1261,6 +1305,7 @@ function parseArguments(args) {
|
||||||
commandBoundaryCoverage: [],
|
commandBoundaryCoverage: [],
|
||||||
worldLoweringGaps: [],
|
worldLoweringGaps: [],
|
||||||
screenshotMetrics: [],
|
screenshotMetrics: [],
|
||||||
|
runtimeCaptureProbes: [],
|
||||||
conformanceHandoff: false,
|
conformanceHandoff: false,
|
||||||
};
|
};
|
||||||
const optionTargets = new Map([
|
const optionTargets = new Map([
|
||||||
|
|
@ -1318,6 +1363,7 @@ function parseArguments(args) {
|
||||||
["--expect-command-boundary-coverage", expectations.commandBoundaryCoverage],
|
["--expect-command-boundary-coverage", expectations.commandBoundaryCoverage],
|
||||||
["--expect-world-lowering-gap", expectations.worldLoweringGaps],
|
["--expect-world-lowering-gap", expectations.worldLoweringGaps],
|
||||||
["--expect-screenshot-metric", expectations.screenshotMetrics],
|
["--expect-screenshot-metric", expectations.screenshotMetrics],
|
||||||
|
["--expect-runtime-capture-probe", expectations.runtimeCaptureProbes],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
for (let index = 1; index < args.length; index += 1) {
|
for (let index = 1; index < args.length; index += 1) {
|
||||||
|
|
@ -1418,6 +1464,8 @@ function parseArguments(args) {
|
||||||
target.push(parseCommandBoundaryCoverageExpectation(value));
|
target.push(parseCommandBoundaryCoverageExpectation(value));
|
||||||
} else if (option === "--expect-screenshot-metric") {
|
} else if (option === "--expect-screenshot-metric") {
|
||||||
target.push(parseScreenshotMetricExpectation(value));
|
target.push(parseScreenshotMetricExpectation(value));
|
||||||
|
} else if (option === "--expect-runtime-capture-probe") {
|
||||||
|
target.push(parseRuntimeCaptureProbeExpectation(value));
|
||||||
} else if (option === "--expect-runtime-plugin-gap") {
|
} else if (option === "--expect-runtime-plugin-gap") {
|
||||||
target.push(parseRuntimePluginGapExpectation(value));
|
target.push(parseRuntimePluginGapExpectation(value));
|
||||||
} else if (option === "--expect-runtime-plugin-projection") {
|
} else if (option === "--expect-runtime-plugin-projection") {
|
||||||
|
|
@ -1453,6 +1501,20 @@ function parseScreenshotMetricExpectation(value) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseRuntimeCaptureProbeExpectation(value) {
|
||||||
|
const parts = value.split(":");
|
||||||
|
if (parts.length !== 4 || parts.some(part => !part)) {
|
||||||
|
console.error(`Expected runtime capture probe in <runtimeId:status:contractArtifactKind:captureOwnerRepo> form, got: ${value}`);
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
runtimeId: parts[0],
|
||||||
|
status: parts[1],
|
||||||
|
contractArtifactKind: parts[2],
|
||||||
|
captureOwnerRepo: parts[3],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parseLocalProviderCatalogExpectation(value) {
|
function parseLocalProviderCatalogExpectation(value) {
|
||||||
const parts = value.split(":");
|
const parts = value.split(":");
|
||||||
if (parts.length !== 3 || parts.some(part => !part)) {
|
if (parts.length !== 3 || parts.some(part => !part)) {
|
||||||
|
|
|
||||||
|
|
@ -2003,6 +2003,7 @@ function buildConformanceExport(report) {
|
||||||
worldSurfaceLoweringCoverage: collectWorldSurfaceLoweringCoverage(report),
|
worldSurfaceLoweringCoverage: collectWorldSurfaceLoweringCoverage(report),
|
||||||
commandBoundaryCoverage: collectCommandBoundaryCoverage(report),
|
commandBoundaryCoverage: collectCommandBoundaryCoverage(report),
|
||||||
screenshotComparisonMetrics: collectScreenshotComparisonMetrics(report),
|
screenshotComparisonMetrics: collectScreenshotComparisonMetrics(report),
|
||||||
|
runtimeCaptureProbeCoverage: collectRuntimeCaptureProbeCoverage(report),
|
||||||
worldSurfaceLoweringGaps: collectWorldSurfaceLoweringGaps(report),
|
worldSurfaceLoweringGaps: collectWorldSurfaceLoweringGaps(report),
|
||||||
splitTargetBlockers: collectSplitTargetBlockers(report),
|
splitTargetBlockers: collectSplitTargetBlockers(report),
|
||||||
splitHandoffMoveCoverage: collectSplitHandoffMoveCoverage(report),
|
splitHandoffMoveCoverage: collectSplitHandoffMoveCoverage(report),
|
||||||
|
|
@ -2560,6 +2561,94 @@ function collectScreenshotComparisonMetrics(report) {
|
||||||
`${left.runtimeId}:${left.fixtureId}:${left.metricKind}`.localeCompare(`${right.runtimeId}:${right.fixtureId}:${right.metricKind}`));
|
`${left.runtimeId}:${left.fixtureId}:${left.metricKind}`.localeCompare(`${right.runtimeId}:${right.fixtureId}:${right.metricKind}`));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function collectRuntimeCaptureProbeCoverage(report) {
|
||||||
|
return (report.runtimes || [])
|
||||||
|
.filter(runtime => (runtime.capture?.artifacts || []).length || runtime.lifecycle?.capture?.captureContract)
|
||||||
|
.map(runtime => buildRuntimeCaptureProbeRecord(runtime))
|
||||||
|
.sort((left, right) => left.runtimeId.localeCompare(right.runtimeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRuntimeCaptureProbeRecord(runtime) {
|
||||||
|
const capture = runtime.capture || {};
|
||||||
|
const lifecycleCapture = runtime.lifecycle?.capture || {};
|
||||||
|
const contract = lifecycleCapture.captureContract || {};
|
||||||
|
const artifacts = Array.isArray(capture.artifacts) ? capture.artifacts : [];
|
||||||
|
const artifact = selectCaptureArtifactForContract(artifacts, contract) || artifacts[0] || {};
|
||||||
|
const errors = runtime.captureArtifactErrors || [];
|
||||||
|
const contractArtifactKind = contract.artifactKind || "";
|
||||||
|
const currentArtifactKind = artifact.kind || "";
|
||||||
|
const hasContract = Boolean(contractArtifactKind || contract.captureKind || contract.requestSchema);
|
||||||
|
const artifactPresent = Boolean(currentArtifactKind && artifact.path);
|
||||||
|
const contractArtifactPresent = hasContract && artifactPresent && currentArtifactKind === contractArtifactKind && !errors.length;
|
||||||
|
const semanticArtifactOnly = hasContract && artifactPresent && currentArtifactKind !== contractArtifactKind && !errors.length;
|
||||||
|
|
||||||
|
let status = "missing-capture-contract";
|
||||||
|
let severity = "blocker";
|
||||||
|
let detail = "Runtime has capture evidence but no lifecycle capture contract.";
|
||||||
|
if (contractArtifactPresent) {
|
||||||
|
status = "contract-artifact-present";
|
||||||
|
severity = "info";
|
||||||
|
detail = "Runtime capture artifact satisfies the lifecycle capture contract.";
|
||||||
|
} else if (semanticArtifactOnly) {
|
||||||
|
status = "semantic-artifact-present-capture-pending";
|
||||||
|
severity = contractArtifactKind === "png" ? "blocker" : "gap";
|
||||||
|
detail = `Runtime publishes ${currentArtifactKind} semantic evidence; ${contractArtifactKind || "contract"} capture remains owner-repo work.`;
|
||||||
|
} else if (hasContract && errors.length) {
|
||||||
|
status = "invalid-current-artifact";
|
||||||
|
severity = "blocker";
|
||||||
|
detail = "Runtime capture artifact is declared but invalid or missing.";
|
||||||
|
} else if (hasContract && !artifactPresent) {
|
||||||
|
status = "contract-artifact-missing";
|
||||||
|
severity = "blocker";
|
||||||
|
detail = "Runtime lifecycle capture contract exists, but no capture artifact is attached.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
runtimeId: runtime.id,
|
||||||
|
runtimeStatus: runtime.status,
|
||||||
|
runtimeOwnerRepo: runtime.ownerRepo || "",
|
||||||
|
splitTarget: runtime.splitTarget || "",
|
||||||
|
captureStatus: capture.status || "",
|
||||||
|
lifecycleStatus: lifecycleCapture.status || "",
|
||||||
|
captureOwnerRepo: contract.ownerRepo || lifecycleCapture.ownerRepo || runtime.splitTarget || runtime.ownerRepo || "",
|
||||||
|
captureKind: contract.captureKind || "",
|
||||||
|
contractArtifactKind,
|
||||||
|
contractRequestSchema: contract.requestSchema || "",
|
||||||
|
requestBuilder: contract.requestBuilder || "",
|
||||||
|
advertisementPath: contract.advertisementPath || "",
|
||||||
|
artifactPattern: contract.artifactPattern || "",
|
||||||
|
conformanceAttachment: contract.conformanceAttachment || "",
|
||||||
|
requiredProvider: contract.requiredProvider || "",
|
||||||
|
requiredSurface: contract.requiredSurface || "",
|
||||||
|
authority: contract.authority || "",
|
||||||
|
currentArtifactKind,
|
||||||
|
currentArtifactSchema: artifact.schema || "",
|
||||||
|
currentArtifactPath: artifact.path || "",
|
||||||
|
requestPath: artifact.requestPath || "",
|
||||||
|
sourceSurfacePath: artifact.sourceSurfacePath || "",
|
||||||
|
pendingProofs: lifecycleCapture.pendingProofs || [],
|
||||||
|
errors,
|
||||||
|
status,
|
||||||
|
severity,
|
||||||
|
detail,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectCaptureArtifactForContract(artifacts, contract) {
|
||||||
|
if (!artifacts.length) return null;
|
||||||
|
const requiredProvider = contract.requiredProvider || "";
|
||||||
|
const requiredSurface = contract.requiredSurface || "";
|
||||||
|
const contractArtifactKind = contract.artifactKind || "";
|
||||||
|
return artifacts.find(artifact =>
|
||||||
|
(!requiredProvider || artifact.providerId === requiredProvider)
|
||||||
|
&& (!requiredSurface || artifact.surfaceId === requiredSurface)
|
||||||
|
&& (!contractArtifactKind || artifact.kind === contractArtifactKind))
|
||||||
|
|| artifacts.find(artifact =>
|
||||||
|
(!requiredProvider || artifact.providerId === requiredProvider)
|
||||||
|
&& (!requiredSurface || artifact.surfaceId === requiredSurface))
|
||||||
|
|| null;
|
||||||
|
}
|
||||||
|
|
||||||
function buildStructureMetric(runtime, fixture) {
|
function buildStructureMetric(runtime, fixture) {
|
||||||
const checks = (fixture.checks || []).filter(check =>
|
const checks = (fixture.checks || []).filter(check =>
|
||||||
check.id.startsWith("component:")
|
check.id.startsWith("component:")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue