From 73005d797b31a565f4246aa8860f080ebe1bfc3f Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 8 Jul 2026 23:58:32 +0100 Subject: [PATCH] Record Fensalir Direct2D adapter status --- docs/eve-migration-roadmap.md | 8 +++++--- docs/renderer-parity.md | 2 +- tools/parity/parity-manifest.json | 25 ++++++++++++++++++++++- tools/parity/run-parity.mjs | 33 +++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) diff --git a/docs/eve-migration-roadmap.md b/docs/eve-migration-roadmap.md index 59c4be8..588db2b 100644 --- a/docs/eve-migration-roadmap.md +++ b/docs/eve-migration-roadmap.md @@ -274,12 +274,14 @@ Exit criteria: ## Active Work Queue -1. Add a Direct2D adapter spike or explicitly demote Direct2D until Fensalir owns - the body. -2. Add split-readiness reports for EveFlutter and EveUnity. +1. Add split-readiness reports for EveFlutter and EveUnity. Recently cut: +- Direct2D is now recorded as an external Fensalir adapter spike, not an active + Eve runtime. Activation requires recorded `gamecult.eve.surface.v1` fixture + replay, command emission through the advertised boundary, and Direct2D frame + capture evidence from Fensalir. - Fixture conformance metadata now lives beside each fixture and is validated by the parity harness, so the corpus can move toward `EveConformance` without relying only on Eve's local manifest layout. diff --git a/docs/renderer-parity.md b/docs/renderer-parity.md index c7d32bc..be477cd 100644 --- a/docs/renderer-parity.md +++ b/docs/renderer-parity.md @@ -38,7 +38,7 @@ proves the same contract at the CultMesh typed document sync layer. | iOS / UIKit | Active native screenshot target | SSH capture stages selected fixture JSON, EveCanvas renders it, and emits a PNG from `/var/mobile/Library/EveCanvas/latest-screenshot.png`; `embedded-surface` is part of the required fixture matrix | Needs real scene composition for `vn.stage`, Repixelizer token fidelity, and responsive/simulator capture | | Android / Kotlin device edge | Active lightweight renderer | Native APK consumes CultMesh dashboard/sensor documents and preserves `surface.slot` / `embeddedDocuments` identity in the dashboard renderer | Needs automated native dashboard capture | | Rust / CultMesh | Document-sync contract runtime | CultLib replicates the `gamecult.eve.surface.v1` embedded slot contract through typed document sync | Not a pixel renderer | -| Fensalir Direct2D | Specialized native/game-runtime landing zone | Existing `AquariumUiDocument` and `DirectWriteOverlay` path | Needs adapter from Eve surface document to `AquariumUiDocument`, DirectWrite/Direct2D token lowering, and a clear reason to stay separate from Flutter for desktop UI | +| Fensalir Direct2D | External adapter spike, not active parity runtime | Fensalir has `src/Aquarium.EveSurface/EveSurfaceRuntime.cs`, reads an Eve broker, and lowers into `AquariumUiDocument` / `DirectWriteOverlay` | Needs recorded `gamecult.eve.surface.v1` fixture replay, command emission through the advertised boundary, DirectWrite/Direct2D token lowering, frame capture, and a clear reason to stay separate from Flutter for desktop UI | ## Spawned Surfaces diff --git a/tools/parity/parity-manifest.json b/tools/parity/parity-manifest.json index adfdb99..3990771 100644 --- a/tools/parity/parity-manifest.json +++ b/tools/parity/parity-manifest.json @@ -593,13 +593,36 @@ "repoRole": "runtime-external-target", "splitTarget": "Fensalir or EveDirect2D if it earns a separate body", "graduationTrigger": "Do not graduate until the Eve-to-AquariumUiDocument adapter and Direct2D frame capture exist.", + "adapterSpike": "external", + "demotionReason": "Fensalir has an external Aquarium.EveSurface adapter spike, but Eve has no Direct2D frame capture and the local fensalir fixture is still legacy dashboard-state instead of gamecult.eve.surface.v1 conformance input.", + "activationCriteria": [ + "Fensalir runs recorded gamecult.eve.surface.v1 fixtures without provider-specific renderer knowledge.", + "Fensalir emits gamecult.eve.command.v1 intents through the advertised provider command boundary.", + "Direct2D capture artifacts are produced and attached to the parity report." + ], "requiredFixtures": ["embedded-surface"], "pluginFixtures": ["sai-vn"], "supportedFeatures": [], "expectedPaths": ["web/fixtures/fensalir-client-surface.json"], + "expectedExternalPaths": [ + "E:\\Projects\\Fensalir\\src\\Aquarium.EveSurface\\EveSurfaceRuntime.cs", + "E:\\Projects\\Fensalir\\docs\\eve-direct2d-client-surface.md" + ], + "expectedExternalSourceSymbols": [ + { + "path": "E:\\Projects\\Fensalir\\src\\Aquarium.EveSurface\\EveSurfaceRuntime.cs", + "contains": [ + "FENSALIR_EVE_BROKER", + "AquariumUiDocument Ui => ComposeUi()", + "ReadSurface(root)", + "EveCompositionElement", + "DirectWriteOverlay" + ] + } + ], "capture": { "status": "missing", - "note": "Needs Eve surface to AquariumUiDocument adapter and Direct2D frame capture." + "note": "External Fensalir adapter spike exists; Eve still needs recorded surface replay and Direct2D frame capture before this runtime can become active." } } ] diff --git a/tools/parity/run-parity.mjs b/tools/parity/run-parity.mjs index 1fcedb3..16439cc 100644 --- a/tools/parity/run-parity.mjs +++ b/tools/parity/run-parity.mjs @@ -245,6 +245,8 @@ async function loadSurface(surface) { async function evaluateRuntime(runtime, fixtureResults) { const expectedPaths = runtime.expectedPaths || []; const missingPaths = expectedPaths.filter(candidate => !existsSync(path.join(repoRoot, candidate))); + const expectedExternalPaths = runtime.expectedExternalPaths || []; + const missingExternalPaths = expectedExternalPaths.filter(candidate => !existsSync(candidate)); const expectedSourceSymbols = runtime.expectedSourceSymbols || []; const missingSourceSymbols = []; for (const expectation of expectedSourceSymbols) { @@ -260,6 +262,20 @@ async function evaluateRuntime(runtime, fixtureResults) { if (!source.includes(symbol)) missingSourceSymbols.push(`${sourcePath}:${symbol}`); } } + const expectedExternalSourceSymbols = runtime.expectedExternalSourceSymbols || []; + const missingExternalSourceSymbols = []; + for (const expectation of expectedExternalSourceSymbols) { + const sourcePath = expectation.path || ""; + if (!existsSync(sourcePath)) { + missingExternalSourceSymbols.push(`${sourcePath}:missing`); + continue; + } + + const source = await readFile(sourcePath, "utf8"); + for (const symbol of expectation.contains || []) { + if (!source.includes(symbol)) missingExternalSourceSymbols.push(`${sourcePath}:${symbol}`); + } + } const requiredFixtures = runtime.requiredFixtures || []; const missingRequiredFixtures = requiredFixtures.filter(id => !fixtureResults.some(fixture => fixture.id === id && fixture.status === "pass")); const supportedFeatures = runtime.supportedFeatures || []; @@ -277,6 +293,9 @@ async function evaluateRuntime(runtime, fixtureResults) { if (runtime.kind === "active" && missingRequiredFeatures.length) status = "missing-required-feature"; if (runtime.kind === "active" && missingIncubationFields.length) status = "missing-incubation-metadata"; if (runtime.kind === "active" && pluginCapabilityGaps.length && status === "active") status = "active-with-capability-gaps"; + if (runtime.kind === "pending" && runtime.adapterSpike === "external" && !missingExternalPaths.length && !missingExternalSourceSymbols.length) { + status = "external-adapter-spike"; + } if (["ssh-png", "adb-png", "golden"].includes(runtime.capture?.status) && status === "pending") { status = "capture-ready"; } @@ -288,8 +307,12 @@ async function evaluateRuntime(runtime, fixtureResults) { semanticHarness: Boolean(runtime.semanticHarness), expectedPaths, missingPaths, + expectedExternalPaths, + missingExternalPaths, expectedSourceSymbols, missingSourceSymbols, + expectedExternalSourceSymbols, + missingExternalSourceSymbols, requiredFixtures, pluginFixtures: runtime.pluginFixtures || [], missingRequiredFixtures, @@ -300,6 +323,9 @@ async function evaluateRuntime(runtime, fixtureResults) { repoRole: runtime.repoRole || "", graduationTrigger: runtime.graduationTrigger || "", splitTarget: runtime.splitTarget || "", + adapterSpike: runtime.adapterSpike || "", + demotionReason: runtime.demotionReason || "", + activationCriteria: runtime.activationCriteria || [], missingIncubationFields, pluginCapabilityGaps, capture: runtime.capture, @@ -788,7 +814,9 @@ function renderMarkdown(report) { for (const runtime of report.runtimes) { const missing = [ ...runtime.missingPaths, + ...runtime.missingExternalPaths.map(id => `external:${id}`), ...runtime.missingSourceSymbols.map(id => `source:${id}`), + ...runtime.missingExternalSourceSymbols.map(id => `external-source:${id}`), ...runtime.missingRequiredFixtures.map(id => `fixture:${id}`), ...runtime.missingRequiredFeatures.map(id => `feature:${id}`), ...runtime.missingIncubationFields.map(id => `metadata:${id}`), @@ -799,6 +827,11 @@ function renderMarkdown(report) { lines.push("", "## Runtime Notes", ""); for (const runtime of report.runtimes) { if (runtime.capture?.note) lines.push(`- ${runtime.title}: ${runtime.capture.note}`); + if (runtime.adapterSpike) lines.push(`- ${runtime.title} adapter: ${runtime.adapterSpike}`); + if (runtime.demotionReason) lines.push(`- ${runtime.title} demotion: ${runtime.demotionReason}`); + for (const criterion of runtime.activationCriteria || []) { + lines.push(`- ${runtime.title} activation: ${criterion}`); + } } return `${lines.join("\n")}\n`;