Record Fensalir Direct2D adapter status

This commit is contained in:
Meta Vens 2026-07-08 23:58:32 +01:00
commit 73005d797b
4 changed files with 63 additions and 5 deletions

View file

@ -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.

View file

@ -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

View file

@ -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."
}
}
]

View file

@ -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`;