Consume Aetheria provider evidence from owner pack
This commit is contained in:
parent
3dce24961d
commit
4ab9fa5185
26 changed files with 103 additions and 884 deletions
|
|
@ -67,7 +67,6 @@
|
|||
"currentPaths": [
|
||||
"web/fixtures/cultui-slider-inspector.conformance.json",
|
||||
"web/fixtures/sai-vn-surface.conformance.json",
|
||||
"web/fixtures/aetheria-world-surface.conformance.json",
|
||||
"web/fixtures/fensalir-direct2d-surface.conformance.json",
|
||||
"web/fixtures/cultui-embedded-surface.conformance.json"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ import { readFileSync } from "node:fs";
|
|||
import test from "node:test";
|
||||
import { buildTuiCaptureArtifact } from "./evetui-capture-artifact.mjs";
|
||||
|
||||
const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8"));
|
||||
const surfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria-world-surface.json", import.meta.url), "utf8"));
|
||||
const surfaceDocument = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria-world-surface.json", import.meta.url), "utf8"));
|
||||
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const worldSmokeSurfaceDocument = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke-surface.json", import.meta.url), "utf8"));
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ test("builds TUI JSON grid capture artifact from provider advertisement and surf
|
|||
advertisement,
|
||||
capabilityManifest,
|
||||
surfaceDocument,
|
||||
advertisementPath: "web/fixtures/aetheria.provider-advertisement.json",
|
||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
||||
stamp: "smoke",
|
||||
width: 72,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { readFileSync } from "node:fs";
|
|||
import test from "node:test";
|
||||
import { buildTuiCaptureRequest } from "./evetui-capture-contract.mjs";
|
||||
|
||||
const advertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const advertisement = JSON.parse(readFileSync(new URL("../../../Aetheria/conformance/eve/aetheria.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("../../web/fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
|
||||
const capabilityManifest = JSON.parse(readFileSync(new URL("../../runtimes/incubating/eve-tui/eve-runtime-capability.json", import.meta.url), "utf8"));
|
||||
|
||||
|
|
@ -11,7 +11,7 @@ test("builds TUI capture request from provider advertisement", () => {
|
|||
const request = buildTuiCaptureRequest({
|
||||
advertisement,
|
||||
capabilityManifest,
|
||||
advertisementPath: "web/fixtures/aetheria.provider-advertisement.json",
|
||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
||||
stamp: "smoke",
|
||||
});
|
||||
|
|
@ -36,7 +36,7 @@ test("builds TUI capture request for Aetheria editor surface", () => {
|
|||
advertisement,
|
||||
capabilityManifest,
|
||||
captureSurfaceId: "aetheria.daemon.editor",
|
||||
advertisementPath: "web/fixtures/aetheria.provider-advertisement.json",
|
||||
advertisementPath: "conformance/eve/aetheria.provider-advertisement.json",
|
||||
capabilityManifestPath: "runtimes/incubating/eve-tui/eve-runtime-capability.json",
|
||||
stamp: "smoke",
|
||||
});
|
||||
|
|
|
|||
|
|
@ -211,11 +211,12 @@
|
|||
"title": "Aetheria Interactive World",
|
||||
"pack": "provider",
|
||||
"ownerRepo": "Aetheria",
|
||||
"sourceRoot": "../Aetheria",
|
||||
"surface": {
|
||||
"transport": "local-json",
|
||||
"path": "web/fixtures/aetheria-world-surface.json"
|
||||
"path": "conformance/eve/aetheria-world-surface.json"
|
||||
},
|
||||
"metadataPath": "web/fixtures/aetheria-world-surface.conformance.json",
|
||||
"metadataPath": "conformance/eve/aetheria-world-surface.conformance.json",
|
||||
"expect": {
|
||||
"providerId": "aetheria",
|
||||
"componentKinds": ["surface", "card", "text", "metric", "partition", "field.surface2d", "control.button"],
|
||||
|
|
@ -385,11 +386,11 @@
|
|||
"kind": "game.runtime",
|
||||
"ownerRepo": "Aetheria",
|
||||
"repoRole": "provider-external",
|
||||
"sourceRoot": "../Aetheria",
|
||||
"graduationTrigger": "Aetheria owns RTS/ARPG world surfaces, generated level state, assets, command receipts, and scenario conformance; Eve keeps contract fixtures only.",
|
||||
"schemaPath": "schemas/gamecult.eve.provider_advertisement.v1.schema.json",
|
||||
"advertisementPath": "web/fixtures/aetheria.provider-advertisement.json",
|
||||
"scenarioPath": "web/fixtures/aetheria-world-scenario.json",
|
||||
"handoffPath": "web/fixtures/aetheria-provider-handoff.json",
|
||||
"advertisementPath": "conformance/eve/aetheria.provider-advertisement.json",
|
||||
"scenarioPath": "conformance/eve/aetheria-world-scenario.json",
|
||||
"requiredFixtures": ["aetheria-world"],
|
||||
"expectedSchemas": ["gamecult.eve.surface.v1", "gamecult.eve.command.v1", "gamecult.eve.command_invocation.v1", "gamecult.eve.unity_playable_world_asset_manifest.v1", "aetheria.runtime.daemon.frame.v1"],
|
||||
"expectedSurfaces": ["aetheria.daemon.game", "aetheria.daemon.editor"],
|
||||
|
|
@ -399,9 +400,9 @@
|
|||
},
|
||||
"expectedCommands": ["aetheria.daemon.commands"],
|
||||
"expectedPaths": [
|
||||
"web/fixtures/aetheria-world-scenario.json",
|
||||
"web/fixtures/aetheria-provider-handoff.json",
|
||||
"scripts/run-aetheria-provider-handoff-smoke.ps1"
|
||||
"conformance/eve/provider-pack.json",
|
||||
"conformance/eve/aetheria-world-scenario.json",
|
||||
"scripts/verify-eve-conformance-pack.mjs"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
|
@ -840,7 +841,7 @@
|
|||
"runtimeId": "electron-shell",
|
||||
"providerId": "aetheria",
|
||||
"surfaceId": "aetheria.daemon.game",
|
||||
"sourceSurfacePath": "../Eve/web/fixtures/aetheria-world-surface.json",
|
||||
"sourceSurfacePath": "../Aetheria/conformance/eve/aetheria-world-surface.json",
|
||||
"requestPath": "artifacts/capture/aetheria-world/runtime-witness.json"
|
||||
}
|
||||
]
|
||||
|
|
@ -889,7 +890,7 @@
|
|||
"runtimes/incubating/eve-tui/src/eve-tui-shell.mjs",
|
||||
"runtimes/incubating/eve-tui/test/eve-tui-shell.test.mjs",
|
||||
"web/fixtures/sai-vn-surface.json",
|
||||
"web/fixtures/aetheria.provider-advertisement.json",
|
||||
"../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
|
||||
"web/fixtures/eve-world-smoke.provider-advertisement.json"
|
||||
]
|
||||
}
|
||||
|
|
@ -957,7 +958,7 @@
|
|||
"path": "artifacts/evetui-capture/latest/tui-grid.json",
|
||||
"providerId": "aetheria",
|
||||
"surfaceId": "aetheria.daemon.game",
|
||||
"sourceSurfacePath": "web/fixtures/aetheria-world-surface.json",
|
||||
"sourceSurfacePath": "../Aetheria/conformance/eve/aetheria-world-surface.json",
|
||||
"requestPath": "artifacts/evetui-capture/latest/capture-request.json"
|
||||
},
|
||||
{
|
||||
|
|
@ -1157,7 +1158,7 @@
|
|||
"targetId": "unity-scene",
|
||||
"requestSchema": "gamecult.eve.runtime_capture_request.v1",
|
||||
"requestBuilder": "scripts/build-capture-request.ps1",
|
||||
"advertisementPath": "GameCult/Aetheria:web/fixtures/aetheria.provider-advertisement.json",
|
||||
"advertisementPath": "Aetheria:conformance/eve/aetheria.provider-advertisement.json",
|
||||
"captureKind": "unity-scene-frame-png",
|
||||
"artifactKind": "png",
|
||||
"artifactPattern": "artifacts/eveunity-scene-capture/{stamp}/unity-scene.png",
|
||||
|
|
@ -1197,9 +1198,8 @@
|
|||
"supportedFeatures": ["embeddedDocuments", "providerAdvertisements", "stateBindings", "styleTokens", "authorityWitnesses"],
|
||||
"localProviderCatalog": {
|
||||
"path": "web/local-provider-catalog.json",
|
||||
"requiredProviders": ["aetheria", "eve.world-smoke", "repixelizer", "gamecult.home.vn"],
|
||||
"requiredProviders": ["eve.world-smoke", "repixelizer", "gamecult.home.vn"],
|
||||
"requiredAdvertisements": [
|
||||
"web/fixtures/aetheria.provider-advertisement.json",
|
||||
"web/fixtures/eve-world-smoke.provider-advertisement.json",
|
||||
"web/fixtures/repixelizer.provider-advertisement.json",
|
||||
"web/fixtures/sai-vn.provider-advertisement.json"
|
||||
|
|
@ -1229,7 +1229,6 @@
|
|||
"evidencePaths": [
|
||||
"web/surface.js",
|
||||
"packages/eve-browser-lowering/src/index.ts",
|
||||
"web/fixtures/aetheria.provider-advertisement.json",
|
||||
"web/fixtures/eve-world-smoke.provider-advertisement.json"
|
||||
]
|
||||
}
|
||||
|
|
@ -1245,11 +1244,11 @@
|
|||
},
|
||||
{
|
||||
"path": "web/provider-advertisements.test.mjs",
|
||||
"contains": ["preserves Sai required and optional nested plugin requirements from advertisement", "merges local fixture transport with advertised Aetheria world interaction", "loads Aetheria Unity playable world asset manifest as provider-authored data", "gamecult.eve.unity_playable_world_asset_manifest.v1", "merges generic world fixture transport with advertised world interaction", "requiresPlugins", "worldInteraction", "optional-nested", "aetheria.daemon.commands", "eve.world-smoke.commands"]
|
||||
"contains": ["preserves Sai required and optional nested plugin requirements from advertisement", "merges generic world fixture transport with advertised world interaction", "requiresPlugins", "worldInteraction", "optional-nested", "eve.world-smoke.commands"]
|
||||
},
|
||||
{
|
||||
"path": "web/local-provider-catalog.json",
|
||||
"contains": ["aetheria.provider-advertisement.json", "eve-world-smoke.provider-advertisement.json", "repixelizer.provider-advertisement.json", "sai-vn.provider-advertisement.json"]
|
||||
"contains": ["eve-world-smoke.provider-advertisement.json", "repixelizer.provider-advertisement.json", "sai-vn.provider-advertisement.json"]
|
||||
},
|
||||
{
|
||||
"path": "packages/eve-browser-lowering/src/index.ts",
|
||||
|
|
@ -1604,7 +1603,7 @@
|
|||
"evidencePaths": [
|
||||
"packages/org.gamecult.eve.unity-uitoolkit/Runtime/EveUiToolkitSurfaceLowerer.cs",
|
||||
"packages/org.gamecult.eve.unity-uitoolkit/Tests/Editor/EveUiToolkitSurfaceLowererTests.cs",
|
||||
"../Eve/web/fixtures/aetheria.provider-advertisement.json",
|
||||
"../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
|
||||
"../Eve/web/fixtures/eve-world-smoke.provider-advertisement.json"
|
||||
]
|
||||
}
|
||||
|
|
@ -1784,7 +1783,7 @@
|
|||
"targetId": "unity-uitoolkit",
|
||||
"requestSchema": "gamecult.eve.runtime_capture_request.v1",
|
||||
"requestBuilder": "scripts/build-uitoolkit-capture-request.ps1",
|
||||
"advertisementPath": "web/fixtures/aetheria.provider-advertisement.json",
|
||||
"advertisementPath": "../Aetheria/conformance/eve/aetheria.provider-advertisement.json",
|
||||
"captureKind": "unity-editor-or-batchmode-png",
|
||||
"artifactKind": "png",
|
||||
"artifactPattern": "artifacts/eveunity-uitoolkit-capture/{stamp}/unity-uitoolkit.png",
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ if (conformanceExportErrors.length > 0) process.exitCode = 1;
|
|||
|
||||
async function evaluateFixture(fixture) {
|
||||
const startedAt = Date.now();
|
||||
const state = await loadSurface(fixture.surface);
|
||||
const state = await loadSurface(fixture, fixture.surface);
|
||||
const root = state.surface?.root;
|
||||
const nodes = root ? flattenSurface(root) : [];
|
||||
const kindCounts = countBy(nodes, node => node.kind || "unknown");
|
||||
|
|
@ -264,8 +264,8 @@ async function evaluateFixture(fixture) {
|
|||
};
|
||||
}
|
||||
|
||||
async function loadSurface(surface) {
|
||||
const absolutePath = path.join(repoRoot, surface.path);
|
||||
async function loadSurface(owner, surface) {
|
||||
const absolutePath = resolveOwnedPath(owner, surface.path);
|
||||
const source = await readFile(absolutePath, "utf8");
|
||||
if (surface.transport === "local-eve-dsl") return compileEveDsl(source);
|
||||
if (surface.transport === "local-json") return JSON.parse(source);
|
||||
|
|
@ -795,7 +795,8 @@ async function evaluateFixtureMetadata(fixture) {
|
|||
const errors = [];
|
||||
if (!fixture.metadataPath) return { metadata: null, errors: ["metadataPath:missing"] };
|
||||
|
||||
errors.push(...await validateJsonDocument(
|
||||
errors.push(...await validateOwnedJsonDocument(
|
||||
fixture,
|
||||
manifest.schemas?.["gamecult.eve.conformance_fixture.v1"],
|
||||
fixture.metadataPath,
|
||||
{
|
||||
|
|
@ -805,7 +806,7 @@ async function evaluateFixtureMetadata(fixture) {
|
|||
));
|
||||
if (errors.length) return { metadata: null, errors };
|
||||
|
||||
const metadata = await readJsonDocument(fixture.metadataPath);
|
||||
const metadata = await readOwnedJsonDocument(fixture, fixture.metadataPath);
|
||||
if (metadata.pack !== fixture.pack) errors.push(`pack:expected ${fixture.pack} got ${metadata.pack}`);
|
||||
if (metadata.ownerRepo !== fixture.ownerRepo) errors.push(`ownerRepo:expected ${fixture.ownerRepo} got ${metadata.ownerRepo}`);
|
||||
if (metadata.surface?.transport !== fixture.surface?.transport) {
|
||||
|
|
@ -821,15 +822,15 @@ async function evaluateFixtureMetadata(fixture) {
|
|||
|
||||
async function evaluateProvider(provider, fixtureResults, pluginResults) {
|
||||
const expectedPaths = provider.expectedPaths || [];
|
||||
const missingPaths = expectedPaths.filter(candidate => !existsSync(path.join(repoRoot, candidate)));
|
||||
const missingPaths = expectedPaths.filter(candidate => !existsSync(resolveOwnedPath(provider, candidate)));
|
||||
const requiredFixtures = provider.requiredFixtures || [];
|
||||
const missingRequiredFixtures = requiredFixtures.filter(id => !fixtureResults.some(fixture => fixture.id === id && fixture.status === "pass"));
|
||||
const missingIncubationFields = requiredIncubationFields(provider).filter(field => !provider[field]);
|
||||
const advertisementErrors = await validateJsonDocument(provider.schemaPath, provider.advertisementPath, {
|
||||
const advertisementErrors = await validateOwnedJsonDocument(provider, provider.schemaPath, provider.advertisementPath, {
|
||||
schema: "gamecult.eve.provider_advertisement.v1",
|
||||
providerId: provider.providerId,
|
||||
});
|
||||
const advertisement = advertisementErrors.length ? null : await readJsonDocument(provider.advertisementPath);
|
||||
const advertisement = advertisementErrors.length ? null : await readOwnedJsonDocument(provider, provider.advertisementPath);
|
||||
const schemaIds = advertisement ? extractProviderSchemaIds(advertisement.schemas || []) : [];
|
||||
const advertisedSchemaIds = advertisement ? [...new Set([
|
||||
...schemaIds,
|
||||
|
|
@ -848,7 +849,7 @@ async function evaluateProvider(provider, fixtureResults, pluginResults) {
|
|||
const missingSurfaceKinds = validateProviderSurfaceKinds(provider, surfaceKinds);
|
||||
const missingCommands = (provider.expectedCommands || []).filter(command => !commandIds.includes(command));
|
||||
const scenarioErrors = await validateProviderScenario(provider, advertisement, fixtureResults, advertisedSchemaIds, surfaceIds, commandIds);
|
||||
const scenario = scenarioErrors.length || !provider.scenarioPath ? null : await readJsonDocument(provider.scenarioPath);
|
||||
const scenario = scenarioErrors.length || !provider.scenarioPath ? null : await readOwnedJsonDocument(provider, provider.scenarioPath);
|
||||
const handoffErrors = await validateProviderHandoff(provider);
|
||||
const status = missingPaths.length
|
||||
? "missing-body"
|
||||
|
|
@ -1027,10 +1028,44 @@ async function readJsonDocument(documentPath) {
|
|||
return JSON.parse(await readFile(path.join(repoRoot, documentPath), "utf8"));
|
||||
}
|
||||
|
||||
function resolveOwnedPath(owner, candidate) {
|
||||
return path.resolve(repoRoot, owner?.sourceRoot || ".", candidate);
|
||||
}
|
||||
|
||||
async function readOwnedJsonDocument(owner, documentPath) {
|
||||
return JSON.parse(await readFile(resolveOwnedPath(owner, documentPath), "utf8"));
|
||||
}
|
||||
|
||||
async function validateOwnedJsonDocument(owner, schemaPath, documentPath, expected = {}) {
|
||||
const errors = [];
|
||||
if (!schemaPath) errors.push("schemaPath:missing");
|
||||
if (!documentPath) errors.push("documentPath:missing");
|
||||
if (errors.length) return errors;
|
||||
|
||||
const absoluteSchemaPath = path.join(repoRoot, schemaPath);
|
||||
const absoluteDocumentPath = resolveOwnedPath(owner, documentPath);
|
||||
if (!existsSync(absoluteSchemaPath)) errors.push(`${schemaPath}:missing`);
|
||||
if (!existsSync(absoluteDocumentPath)) errors.push(`${documentPath}:missing`);
|
||||
if (errors.length) return errors;
|
||||
|
||||
try {
|
||||
const schema = JSON.parse(await readFile(absoluteSchemaPath, "utf8"));
|
||||
const document = JSON.parse(await readFile(absoluteDocumentPath, "utf8"));
|
||||
errors.push(...validateSchemaSubset(schema, document));
|
||||
for (const [key, value] of Object.entries(expected)) {
|
||||
if (document[key] !== value) errors.push(`${documentPath}:${key}:expected ${value} got ${document[key]}`);
|
||||
}
|
||||
} catch (error) {
|
||||
errors.push(`${documentPath}:invalid-json:${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
async function validateProviderScenario(provider, advertisement, fixtureResults, advertisedSchemaIds, surfaceIds, commandIds) {
|
||||
if (!provider.scenarioPath) return [];
|
||||
|
||||
const errors = await validateJsonDocument(
|
||||
const errors = await validateOwnedJsonDocument(
|
||||
provider,
|
||||
manifest.schemas?.["gamecult.eve.provider_scenario.v1"],
|
||||
provider.scenarioPath,
|
||||
{
|
||||
|
|
@ -1041,7 +1076,7 @@ async function validateProviderScenario(provider, advertisement, fixtureResults,
|
|||
if (errors.length) return errors;
|
||||
|
||||
try {
|
||||
const scenario = await readJsonDocument(provider.scenarioPath);
|
||||
const scenario = await readOwnedJsonDocument(provider, provider.scenarioPath);
|
||||
if (scenario.ownerRepo !== provider.ownerRepo) {
|
||||
errors.push(`${provider.scenarioPath}:ownerRepo:expected ${provider.ownerRepo} got ${scenario.ownerRepo}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue