Track generic Unity capture truth

This commit is contained in:
Meta Vens 2026-07-10 03:35:00 +01:00
commit 79e5121120
6 changed files with 52 additions and 22 deletions

View file

@ -1003,7 +1003,7 @@
"ownerRepo": "EveUnity",
"repoRole": "runtime-target",
"splitTarget": "EveUnity",
"graduationTrigger": "Graduated: EveUnity owns the provider-agnostic Unity scene package, release lifecycle, and runtime tests. Real Unity frame capture and live CultMesh transport remain active lifecycle work.",
"graduationTrigger": "Graduated: EveUnity owns the provider-agnostic Unity scene package, release lifecycle, runtime tests, and generic PlayMode capture. The Aetheria daemon product witness remains active lifecycle work.",
"capabilityManifest": {
"schemaPath": "schemas/gamecult.eve.runtime_capability.v1.schema.json",
"manifestPath": "packages/org.gamecult.eve.unity-scene/eve-runtime-capability.json"
@ -1111,9 +1111,17 @@
}
],
"capture": {
"status": "pending-unity-frame-capture",
"note": "A real Unity PNG/frame capture driven by a provider surface remains active lifecycle work.",
"artifacts": []
"status": "partial-world-smoke-playmode-capture",
"note": "The owner-controlled generic world-smoke PlayMode PNG passes; the separately running Aetheria daemon capture remains active lifecycle work.",
"artifacts": [
{
"kind": "png",
"schema": "gamecult.eve.runtime_frame_capture.v1",
"path": "artifacts/playmode/world-smoke.png",
"providerId": "eve.world-smoke",
"surfaceId": "eve.world-smoke.surface"
}
]
},
"sourceRoot": "../EveUnity",
"currentHostRepo": "EveUnity",
@ -1166,7 +1174,7 @@
},
"capture": {
"ownerRepo": "EveUnity",
"status": "pending-unity-frame-capture",
"status": "partial-world-smoke-playmode-capture",
"captureContract": {
"ownerRepo": "EveUnity",
"runtimeId": "unity-scene",
@ -1190,8 +1198,8 @@
"authority": "Capture observes Unity scene projection only; provider state, command acceptance, and receipts stay provider-owned.",
"publishProof": "Unity scene screenshot or frame-capture PNG artifact is attached to the runtime conformance report from EveUnity."
},
"evidencePaths": ["scripts/build-capture-request.ps1"],
"pendingProofs": ["Unity scene screenshot or frame-capture PNG artifact is attached to the runtime conformance report."]
"evidencePaths": ["scripts/build-capture-request.ps1", "scripts/run-playmode-capture.ps1", "TestProject/Assets/Tests/PlayMode/GenericWorldCaptureTests.cs", "artifacts/playmode/world-smoke.png"],
"pendingProofs": ["Attach the Aetheria daemon PlayMode PNG; the generic world-smoke control is captured by EveUnity."]
}
}
},

View file

@ -320,7 +320,7 @@ async function evaluateRuntime(runtime, fixtureResults) {
const lifecycleManifestDocument = await readRuntimeLifecycleManifestDocument(runtime);
const lifecycle = runtime.lifecycle || capabilityManifestDocument?.lifecycle || readLifecycleStages(lifecycleManifestDocument);
const capture = runtime.capture || capabilityManifestDocument?.capture || null;
const captureArtifactErrors = validateRuntimeCaptureArtifacts(capture);
const captureArtifactErrors = validateRuntimeCaptureArtifacts(runtime, capture);
const lifecycleErrors = await validateRuntimeLifecycle(runtime, lifecycle, lifecycleManifestDocument);
const splitHandoffPath = await readRuntimeSplitHandoffPath(runtime);
const splitHandoffErrors = await validateRuntimeSplitHandoff(runtime, splitHandoffPath);
@ -2673,7 +2673,11 @@ function buildRuntimeCaptureProbeRecord(runtime) {
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 providerMatches = !contract.requiredProvider || artifact.providerId === contract.requiredProvider;
const surfaceMatches = !contract.requiredSurface || artifact.surfaceId === contract.requiredSurface;
const contractTargetMatches = providerMatches && surfaceMatches;
const contractArtifactPresent = hasContract && artifactPresent && contractTargetMatches && currentArtifactKind === contractArtifactKind && !errors.length;
const controlArtifactPresent = hasContract && artifactPresent && !contractTargetMatches && currentArtifactKind === contractArtifactKind && !errors.length;
const semanticArtifactOnly = hasContract && artifactPresent && currentArtifactKind !== contractArtifactKind && !errors.length;
let status = "missing-capture-contract";
@ -2683,6 +2687,10 @@ function buildRuntimeCaptureProbeRecord(runtime) {
status = "contract-artifact-present";
severity = "info";
detail = "Runtime capture artifact satisfies the lifecycle capture contract.";
} else if (controlArtifactPresent) {
status = "control-artifact-present-product-capture-pending";
severity = "blocker";
detail = `Runtime publishes a ${currentArtifactKind} control artifact for ${artifact.providerId || "another provider"}; ${contract.requiredProvider || "the required provider"} product capture remains owner-repo work.`;
} else if (semanticArtifactOnly) {
status = "semantic-artifact-present-capture-pending";
severity = contractArtifactKind === "png" ? "blocker" : "gap";
@ -2944,7 +2952,7 @@ function readRuntimeLayoutProbe(runtime, fixtureId) {
return { config, document, errors };
}
function validateRuntimeCaptureArtifacts(capture) {
function validateRuntimeCaptureArtifacts(runtime, capture) {
const artifacts = Array.isArray(capture?.artifacts) ? capture.artifacts : [];
const errors = [];
for (const artifact of artifacts) {
@ -2953,11 +2961,14 @@ function validateRuntimeCaptureArtifacts(capture) {
errors.push("artifact.path:missing");
continue;
}
const absoluteArtifactPath = path.join(repoRoot, artifactPath);
const absoluteArtifactPath = resolveRuntimeOwnedPath(runtime, artifactPath);
if (!existsSync(absoluteArtifactPath)) {
errors.push(`${artifactPath}:missing`);
continue;
}
if (artifact.kind === "png") {
continue;
}
let document;
try {
document = JSON.parse(readFileSync(absoluteArtifactPath, "utf8"));
@ -2984,7 +2995,7 @@ function validateRuntimeCaptureArtifacts(capture) {
}
}
for (const linkedPath of [artifact.sourceSurfacePath, artifact.requestPath].filter(Boolean)) {
if (!existsSync(path.join(repoRoot, linkedPath))) {
if (!existsSync(resolveRuntimeOwnedPath(runtime, linkedPath))) {
errors.push(`${artifactPath}:linked-path:${linkedPath}:missing`);
}
}