Initialize Eve conformance owner
This commit is contained in:
commit
4cdfa9c6eb
10 changed files with 9073 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
artifacts/
|
||||||
|
node_modules/
|
||||||
|
|
||||||
20
README.md
Normal file
20
README.md
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
# EveConformance
|
||||||
|
|
||||||
|
EveConformance owns the cross-repository evidence that an Eve provider, plugin,
|
||||||
|
or runtime obeys the contracts published by the Eve kernel.
|
||||||
|
|
||||||
|
Eve owns schemas and the browser reference lowerer. Provider repos own live
|
||||||
|
state and scenarios. Plugin repos own semantic sidecars. Runtime repos own
|
||||||
|
native projection and captures. This repo reads those public surfaces and emits
|
||||||
|
portable parity reports and conformance packs; it does not import their private
|
||||||
|
implementation bodies.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
powershell -ExecutionPolicy Bypass -File .\scripts\run-conformance.ps1
|
||||||
|
```
|
||||||
|
|
||||||
|
The default workspace assumes sibling repositories under `E:\Projects`. Set
|
||||||
|
`EVE_KERNEL_ROOT` when the Eve checkout lives elsewhere.
|
||||||
|
|
||||||
9
package.json
Normal file
9
package.json
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"name": "@gamecult/eve-conformance",
|
||||||
|
"private": true,
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "powershell -ExecutionPolicy Bypass -File ./scripts/run-conformance.ps1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
22
scripts/run-conformance.ps1
Normal file
22
scripts/run-conformance.ps1
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
param(
|
||||||
|
[string] $KernelRoot = $(if ($env:EVE_KERNEL_ROOT) { $env:EVE_KERNEL_ROOT } else { "E:\Projects\Eve" }),
|
||||||
|
[string] $OutputDirectory = "artifacts\parity"
|
||||||
|
)
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||||
|
$env:EVE_KERNEL_ROOT = (Resolve-Path $KernelRoot).Path
|
||||||
|
$env:EVE_PARITY_OUTPUT = if ([IO.Path]::IsPathRooted($OutputDirectory)) {
|
||||||
|
$OutputDirectory
|
||||||
|
} else {
|
||||||
|
Join-Path $repoRoot $OutputDirectory
|
||||||
|
}
|
||||||
|
$env:EVE_CONFORMANCE_OUTPUT = Join-Path $repoRoot "artifacts\conformance"
|
||||||
|
|
||||||
|
Push-Location $repoRoot
|
||||||
|
try {
|
||||||
|
node .\tools\parity\run-parity.mjs
|
||||||
|
node .\tools\conformance\consume-export.mjs .\artifacts\conformance\latest
|
||||||
|
} finally {
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
44
tools/conformance/attach-plugin-witness.mjs
Normal file
44
tools/conformance/attach-plugin-witness.mjs
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { validateSchemaSubset } from "../../runtimes/incubating/test-support/schema-subset.mjs";
|
||||||
|
|
||||||
|
const [witnessArgument, exportArgument] = process.argv.slice(2);
|
||||||
|
if (!witnessArgument || !exportArgument) {
|
||||||
|
console.error("Usage: node tools/conformance/attach-plugin-witness.mjs <plugin-witness.json> <conformance-export-directory>");
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
const witnessPath = path.resolve(witnessArgument);
|
||||||
|
const exportDirectory = path.resolve(exportArgument);
|
||||||
|
const indexPath = path.join(exportDirectory, "index.json");
|
||||||
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
|
const witness = parseJson(await readFile(witnessPath, "utf8"));
|
||||||
|
const schema = parseJson(await readFile(path.join(repoRoot, "schemas/gamecult.eve.plugin_witness.v1.schema.json"), "utf8"));
|
||||||
|
const schemaErrors = validateSchemaSubset(schema, witness);
|
||||||
|
if (schemaErrors.length) throw new Error(`Plugin witness schema errors: ${schemaErrors.join("; ")}`);
|
||||||
|
|
||||||
|
const attachmentRoot = path.join(exportDirectory, "attachments", "plugin", safeSegment(witness.witnessId));
|
||||||
|
await mkdir(attachmentRoot, { recursive: true });
|
||||||
|
const exportedWitnessPath = path.join(attachmentRoot, "witness.json");
|
||||||
|
await writeFile(exportedWitnessPath, `${JSON.stringify(witness, null, 2)}\n`);
|
||||||
|
|
||||||
|
const index = parseJson(await readFile(indexPath, "utf8"));
|
||||||
|
const record = {
|
||||||
|
witnessId: witness.witnessId,
|
||||||
|
pluginId: witness.pluginId,
|
||||||
|
ownerRepo: witness.ownerRepo,
|
||||||
|
status: witness.status,
|
||||||
|
transport: witness.transport,
|
||||||
|
operationCount: witness.operations.length,
|
||||||
|
witnessExportPath: slash(path.relative(exportDirectory, exportedWitnessPath)),
|
||||||
|
};
|
||||||
|
index.pluginWitnesses = [
|
||||||
|
...(Array.isArray(index.pluginWitnesses) ? index.pluginWitnesses : []).filter(candidate => candidate.witnessId !== record.witnessId),
|
||||||
|
record,
|
||||||
|
].sort((left, right) => left.witnessId.localeCompare(right.witnessId));
|
||||||
|
await writeFile(indexPath, `${JSON.stringify(index, null, 2)}\n`);
|
||||||
|
console.log(exportedWitnessPath);
|
||||||
|
|
||||||
|
function safeSegment(value) { return String(value).replace(/[^a-zA-Z0-9._-]+/g, "-"); }
|
||||||
|
function slash(value) { return value.replaceAll("\\", "/"); }
|
||||||
|
function parseJson(value) { return JSON.parse(value.replace(/^\uFEFF/, "")); }
|
||||||
78
tools/conformance/attach-runtime-witness.mjs
Normal file
78
tools/conformance/attach-runtime-witness.mjs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { validateSchemaSubset } from "../../runtimes/incubating/test-support/schema-subset.mjs";
|
||||||
|
|
||||||
|
const [witnessArgument, exportArgument] = process.argv.slice(2);
|
||||||
|
if (!witnessArgument || !exportArgument) {
|
||||||
|
console.error("Usage: node tools/conformance/attach-runtime-witness.mjs <runtime-witness.json> <conformance-export-directory>");
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const witnessPath = path.resolve(witnessArgument);
|
||||||
|
const exportDirectory = path.resolve(exportArgument);
|
||||||
|
const indexPath = path.join(exportDirectory, "index.json");
|
||||||
|
const witness = parseJson(await readFile(witnessPath, "utf8"));
|
||||||
|
validateWitness(witness);
|
||||||
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
|
const witnessSchema = parseJson(await readFile(path.join(repoRoot, "schemas/gamecult.eve.runtime_witness.v1.schema.json"), "utf8"));
|
||||||
|
const schemaErrors = validateSchemaSubset(witnessSchema, witness);
|
||||||
|
if (schemaErrors.length) throw new Error(`Runtime witness schema errors: ${schemaErrors.join("; ")}`);
|
||||||
|
|
||||||
|
const attachmentRoot = path.join(exportDirectory, "attachments", "runtime", safeSegment(witness.witnessId));
|
||||||
|
await mkdir(attachmentRoot, { recursive: true });
|
||||||
|
const artifacts = [];
|
||||||
|
for (const artifact of witness.artifacts) {
|
||||||
|
const sourcePath = path.resolve(path.dirname(witnessPath), artifact.path);
|
||||||
|
const fileName = path.basename(sourcePath);
|
||||||
|
const destinationPath = path.join(attachmentRoot, fileName);
|
||||||
|
await copyFile(sourcePath, destinationPath);
|
||||||
|
artifacts.push({
|
||||||
|
...artifact,
|
||||||
|
exportPath: slash(path.relative(exportDirectory, destinationPath)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const exportedWitness = { ...witness, artifacts };
|
||||||
|
const exportedWitnessPath = path.join(attachmentRoot, "witness.json");
|
||||||
|
await writeFile(exportedWitnessPath, `${JSON.stringify(exportedWitness, null, 2)}\n`);
|
||||||
|
|
||||||
|
const index = parseJson(await readFile(indexPath, "utf8"));
|
||||||
|
const record = {
|
||||||
|
witnessId: witness.witnessId,
|
||||||
|
runtimeId: witness.runtimeId,
|
||||||
|
runtimeOwnerRepo: witness.runtimeOwnerRepo,
|
||||||
|
providerId: witness.providerId,
|
||||||
|
surfaceId: witness.surfaceId,
|
||||||
|
projectionKind: witness.projectionKind,
|
||||||
|
status: witness.status,
|
||||||
|
cacheState: witness.execution.cacheState,
|
||||||
|
witnessExportPath: slash(path.relative(exportDirectory, exportedWitnessPath)),
|
||||||
|
artifactCount: artifacts.length,
|
||||||
|
};
|
||||||
|
index.runtimeWitnesses = [
|
||||||
|
...(Array.isArray(index.runtimeWitnesses) ? index.runtimeWitnesses : []).filter(candidate => candidate.witnessId !== record.witnessId),
|
||||||
|
record,
|
||||||
|
].sort((left, right) => left.witnessId.localeCompare(right.witnessId));
|
||||||
|
await writeFile(indexPath, `${JSON.stringify(index, null, 2)}\n`);
|
||||||
|
console.log(exportedWitnessPath);
|
||||||
|
|
||||||
|
function validateWitness(value) {
|
||||||
|
for (const field of ["witnessId", "runtimeId", "runtimeOwnerRepo", "providerId", "surfaceId", "projectionKind", "status", "generatedAtUtc", "authority"]) {
|
||||||
|
if (!value?.[field]) throw new Error(`Runtime witness missing ${field}.`);
|
||||||
|
}
|
||||||
|
if (value.schema !== "gamecult.eve.runtime_witness.v1") throw new Error(`Unexpected runtime witness schema: ${value.schema || ""}`);
|
||||||
|
if (!value.execution?.cacheState || !Number.isFinite(value.execution?.durationMs)) throw new Error("Runtime witness execution is incomplete.");
|
||||||
|
if (!value.assertions || typeof value.assertions !== "object") throw new Error("Runtime witness assertions are missing.");
|
||||||
|
if (value.receipts !== undefined && !Array.isArray(value.receipts)) throw new Error("Runtime witness receipts must be an array when present.");
|
||||||
|
if (!Array.isArray(value.artifacts) || !value.artifacts.length) throw new Error("Runtime witness artifacts are missing.");
|
||||||
|
for (const artifact of value.artifacts) {
|
||||||
|
if (!artifact.path || !/^[a-f0-9]{64}$/i.test(artifact.sha256 || "") || !(artifact.sizeBytes > 0)) {
|
||||||
|
throw new Error(`Runtime witness artifact is invalid: ${artifact.path || "unknown"}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeSegment(value) { return String(value).replace(/[^a-zA-Z0-9._-]+/g, "-"); }
|
||||||
|
function slash(value) { return value.replaceAll("\\", "/"); }
|
||||||
|
function parseJson(value) { return JSON.parse(value.replace(/^\uFEFF/, "")); }
|
||||||
2100
tools/conformance/consume-export.mjs
Normal file
2100
tools/conformance/consume-export.mjs
Normal file
File diff suppressed because it is too large
Load diff
36
tools/parity/export-fixture.mjs
Normal file
36
tools/parity/export-fixture.mjs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
||||||
|
import path from "node:path";
|
||||||
|
import { fileURLToPath } from "node:url";
|
||||||
|
import { compileEveDsl } from "../../web/eve-dsl.js";
|
||||||
|
|
||||||
|
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||||
|
const manifest = JSON.parse(await readFile(path.join(repoRoot, "tools/parity/parity-manifest.json"), "utf8"));
|
||||||
|
|
||||||
|
const fixtureId = process.argv[2] || "cultui-inspector";
|
||||||
|
const outputPath = process.argv[3]
|
||||||
|
? path.resolve(process.argv[3])
|
||||||
|
: path.join(repoRoot, "artifacts/parity/fixtures", `${fixtureId}.surface.json`);
|
||||||
|
|
||||||
|
const fixture = manifest.fixtures.find(candidate =>
|
||||||
|
candidate.id === fixtureId || candidate.expect?.providerId === fixtureId);
|
||||||
|
|
||||||
|
if (!fixture) {
|
||||||
|
throw new Error(`Unknown parity fixture: ${fixtureId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const surface = await loadSurface(fixture.surface);
|
||||||
|
surface.fixtureId = fixture.id;
|
||||||
|
surface.values ||= surface.mesh?.snapshot?.() || {};
|
||||||
|
delete surface.mesh;
|
||||||
|
|
||||||
|
await mkdir(path.dirname(outputPath), { recursive: true });
|
||||||
|
await writeFile(outputPath, `${JSON.stringify(surface, null, 2)}\n`);
|
||||||
|
console.log(outputPath);
|
||||||
|
|
||||||
|
async function loadSurface(surface) {
|
||||||
|
const absolutePath = path.join(repoRoot, 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);
|
||||||
|
throw new Error(`Unsupported parity surface transport: ${surface.transport}`);
|
||||||
|
}
|
||||||
3104
tools/parity/parity-manifest.json
Normal file
3104
tools/parity/parity-manifest.json
Normal file
File diff suppressed because it is too large
Load diff
3657
tools/parity/run-parity.mjs
Normal file
3657
tools/parity/run-parity.mjs
Normal file
File diff suppressed because it is too large
Load diff
Reference in a new issue