diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 60dfb3d..060962e 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -285,14 +285,22 @@ Exit criteria: ## Active Work Queue -1. Add a real Unity PlayMode PNG/frame capture in EveUnity for Aetheria's - daemon-published game surface and the generic world-smoke provider. -2. Replace Aetheria's local state-file polling adapter with a generic live - CultMesh/CultNet provider transport when that transport contract is ready; - Aetheria should then supply only provider advertisement and credentials. +1. Run the same EveUnity-owned PlayMode client against Aetheria's separately + running daemon and attach the Aetheria world PNG plus command/receipt proof. +2. Graduate the generic Unity transport and capture contracts only after the + product witness proves that no Aetheria authority leaked into EveUnity. Recently cut: +- EveUnity now owns a minimal clean `TestProject` that installs CultLib, Eve + surface contracts, and the EveUnity scene package without Aetheria code. Its + generic `eve.world-smoke` PlayMode test lowers a world, routes a command + intent, and emits a real 640x360 PNG. Conformance records this as a valid + control artifact while keeping the Aetheria daemon product capture blocked. +- CultLib now owns generation of `org.gamecult.cultlib`, the explicit managed + assembly closure consumed by clean Unity clients. Eve declares CultLib as a + surface-package dependency; provider repos no longer need to vendor Brokkr's + private DLL layout to compile the generic client. - Eve no longer contains `runtimes/incubating/eve-unity-scene`. Runtime capability, source-symbol, lifecycle, and package evidence now resolve from `../EveUnity` through the parity runtime's `sourceRoot`. Eve-local scene diff --git a/packages/org.gamecult.eve.surface/package.json b/packages/org.gamecult.eve.surface/package.json index bea317d..8d57c63 100644 --- a/packages/org.gamecult.eve.surface/package.json +++ b/packages/org.gamecult.eve.surface/package.json @@ -4,6 +4,9 @@ "version": "0.1.0", "unity": "2021.3", "description": "Unity-compatible DTOs for gamecult.eve.surface.v1 documents.", + "dependencies": { + "org.gamecult.cultlib": "1.0.3" + }, "keywords": [ "Eve", "CultUI", diff --git a/scripts/run-conformance-consumer-smoke.ps1 b/scripts/run-conformance-consumer-smoke.ps1 index 7a9b125..02bd9d2 100644 --- a/scripts/run-conformance-consumer-smoke.ps1 +++ b/scripts/run-conformance-consumer-smoke.ps1 @@ -101,7 +101,7 @@ try { --expect-screenshot-metric web:eve-world-smoke:bounding-boxes:pass ` --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:contract-artifact-missing:png:EveUnity ` + --expect-runtime-capture-probe unity-scene:control-artifact-present-product-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" ` diff --git a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 index c928377..0e96e03 100644 --- a/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 +++ b/scripts/run-runtime-owner-conformance-consumer-smoke.ps1 @@ -164,7 +164,7 @@ node $consumerScript $consumerExport ` --expect-runtime-capture-status unity-uitoolkit:json-projection ` --expect-runtime-capture-artifact unity-uitoolkit:json-projection:gamecult.eve.unity_uitoolkit_projection.v1:aetheria:aetheria.daemon.game ` --expect-runtime-capture-artifact unity-uitoolkit:json-projection:gamecult.eve.unity_uitoolkit_projection.v1:eve.world-smoke:eve.world-smoke.surface ` - --expect-runtime-capture-status unity-scene:pending-unity-frame-capture ` + --expect-runtime-capture-status unity-scene:partial-world-smoke-playmode-capture ` --expect-runtime-capture-status electron-shell:json-projection ` --expect-runtime-capture-artifact electron-shell:json-projection:gamecult.eve.electron_shell_projection.v1:aetheria:aetheria.daemon.game ` --expect-runtime-capture-artifact electron-shell:json-projection:gamecult.eve.electron_shell_projection.v1:eve.world-smoke:eve.world-smoke.surface ` @@ -173,7 +173,7 @@ node $consumerScript $consumerExport ` --expect-runtime-capture-artifact tui:json-grid:gamecult.eve.tui_grid.v1:eve.world-smoke:eve.world-smoke.surface ` --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:contract-artifact-missing:png:EveUnity ` + --expect-runtime-capture-probe unity-scene:control-artifact-present-product-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 ` @@ -236,10 +236,10 @@ node $consumerScript $consumerExport ` --expect-runtime-lifecycle-field "unity-uitoolkit:capture:captureContract.additionalProviderSurfaces.1.surfaceId:aetheria.daemon.editor" ` --expect-runtime-lifecycle-status unity-scene:release:owner-repo-upm-artifact ` --expect-runtime-lifecycle-status unity-scene:test:unity-scene-editmode-batchmode ` - --expect-runtime-lifecycle-status unity-scene:capture:pending-unity-frame-capture ` + --expect-runtime-lifecycle-status unity-scene:capture:partial-world-smoke-playmode-capture ` --expect-runtime-lifecycle-pending "unity-scene:release:Cut and publish the first tagged EveUnity package release" ` --expect-runtime-lifecycle-pending "unity-scene:test:Move the Aetheria consumer scenario into a provider-owned conformance pack" ` - --expect-runtime-lifecycle-pending "unity-scene:capture:Unity scene screenshot or frame-capture PNG artifact" ` + --expect-runtime-lifecycle-pending "unity-scene:capture:Attach the Aetheria daemon PlayMode PNG" ` --expect-runtime-lifecycle-field "unity-scene:release:releaseContract.packageName:org.gamecult.eve.unity-scene" ` --expect-runtime-lifecycle-field "unity-scene:release:releaseContract.artifactKind:upm-package" ` --expect-runtime-lifecycle-field "unity-scene:release:releaseContract.tagPattern:eveunity-scene-v{version}" ` diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index e86523d..20e7769 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -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."] } } }, diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index c251183..20660e3 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -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`); } }