Files
wehub-resource-sync e7738de6d2
CI / Deep Native Runtime Cases (1/6) (push) Has been skipped
CI / Native Preflight (push) Failing after 1s
CI / Native Runtime Cases (1/2) (push) Failing after 0s
CI / Native Runtime Cases (2/2) (push) Failing after 1s
CI / Native Metadata Reports (push) Failing after 0s
CI / Native Direct Backend Artifacts (push) Failing after 0s
CI / Native Sanitizer Smoke (push) Failing after 1s
CI / Command Contract Snapshots (push) Failing after 1s
CI / Deep Conformance Suite (push) Has been skipped
CI / Graph Build Perf (push) Failing after 1s
CI / Deep Native Preflight (push) Has been skipped
CI / Deep Native Runtime Cases (2/6) (push) Has been skipped
CI / Deep Native Runtime Cases (3/6) (push) Has been skipped
CI / Conformance Suite (push) Failing after 1s
CI / Workspace Checks (push) Failing after 0s
CI / Deep Native Runtime Cases (5/6) (push) Has been skipped
CI / Deep Native Runtime Cases (6/6) (push) Has been skipped
CI / Deep Native Runtime Cases (4/6) (push) Has been skipped
CI / Deep Graph Build Perf (push) Has been skipped
chore: import upstream snapshot with attribution
2026-07-13 12:29:30 +08:00

1665 lines
100 KiB
TypeScript

#!/usr/bin/env -S node --experimental-strip-types --disable-warning=ExperimentalWarning
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { existsSync } from "node:fs";
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { promisify } from "node:util";
const execFileAsync = promisify(execFile);
const execMaxBuffer = 128 * 1024 * 1024;
const zero = process.env.ZERO_BIN || (existsSync(".zero/bin/zero") ? ".zero/bin/zero" : "bin/zero");
const outDir = `/tmp/zero-program-graph-parity-${process.pid}`;
const zeroEnv = { ...process.env, ZERO_CACHE_DIR: process.env.ZERO_CACHE_DIR || `${outDir}/cache` };
const requireStableNodeIds = process.argv.includes("--require-stable-node-ids");
const graphHashPrime = 1099511628211n;
const graphHashMask = (1n << 64n) - 1n;
function firstDiagnosticCode(diagnostics) {
return Array.isArray(diagnostics) && diagnostics.length > 0 ? diagnostics[0].code ?? null : null;
}
function targetReadinessSummary(readiness) {
if (!readiness) return null;
return {
languageOk: readiness.languageOk,
buildable: readiness.buildable,
stage: readiness.stage,
code: firstDiagnosticCode(readiness.diagnostics),
};
}
async function zeroJson(args) {
const result = await execFileAsync(zero, args, { env: zeroEnv, maxBuffer: execMaxBuffer });
return JSON.parse(result.stdout);
}
async function zeroJsonFailure(args) {
try {
await execFileAsync(zero, args, { env: zeroEnv, maxBuffer: execMaxBuffer });
} catch (error) {
return JSON.parse(error.stdout);
}
assert.fail(`${zero} ${args.join(" ")} should fail`);
}
async function zeroText(args) {
const result = await execFileAsync(zero, args, { env: zeroEnv, maxBuffer: execMaxBuffer });
return result.stdout;
}
async function dumpGraphArtifact(fixture, name) {
const artifact = `${outDir}/${name}.program-graph`;
if (fixture.endsWith(".0") && !existsSync(projectionSidecarPath(fixture))) {
await importProjectionSidecar(fixture);
}
await zeroText(["dump", "--out", artifact, fixture]);
return artifact;
}
function projectionSidecarPath(sourcePath) {
assert(sourcePath.endsWith(".0"), `${sourcePath}: expected a .0 projection path`);
return `${sourcePath.slice(0, -2)}.graph`;
}
async function importProjectionSidecar(sourcePath) {
const sidecar = projectionSidecarPath(sourcePath);
await zeroText(["import", "--format", "binary", "--out", sidecar, sourcePath]);
return sidecar;
}
async function graphCompilerInputForFixture(fixture) {
if (!fixture.endsWith(".0")) return fixture;
const sidecar = projectionSidecarPath(fixture);
if (!existsSync(sidecar)) await importProjectionSidecar(fixture);
return sidecar;
}
function artifactNameForFixture(prefix, fixture) {
return `${prefix}-${fixture.replace(/[^A-Za-z0-9_.-]+/g, "-")}`;
}
function buildSummary(result) {
return {
emit: result.emit,
target: result.target,
profile: result.profile,
compiler: result.compiler,
generatedCBytes: result.generatedCBytes,
cBridgeFallback: result.cBridgeFallback,
objectEmission: result.objectBackend?.objectEmission?.path ?? null,
linkFormat: result.objectBackend?.linking?.objectFormat ?? null,
directFacts: result.objectBackend?.directFacts ?? null,
};
}
function assertGraphCompilerRoute(result, fixture, lowering = "typed-program-graph-mir") {
const graph = result.graph ?? {
artifact: result.artifact,
canonicalSource: result.canonicalSource,
graphHash: result.graphHash,
lowering: result.check?.lowering,
};
assert(graph.artifact, `${fixture}: graph input should report graph compiler input`);
const sidecar = fixture.endsWith(".0") ? `${fixture.slice(0, -2)}.graph` : fixture;
const expectedArtifact = existsSync(sidecar) ? sidecar : fixture;
assert.equal(graph.artifact, expectedArtifact, `${fixture}: graph artifact should be the active graph input`);
assert.equal(graph.canonicalSource, false, `${fixture}: graph compiler input should not be canonical source`);
assert.match(graph.graphHash, /^graph:[0-9a-f]{16}$/, `${fixture}: graph hash`);
if (result.graph?.lowering) assert.equal(graph.lowering, lowering, `${fixture}: graph lowering`);
}
function compilerCacheKey(result, name) {
const cache = result.compilerCaches?.find((item) => item.name === name);
assert(cache, `missing compiler cache ${name}`);
return cache.key;
}
function compilerCache(result, name) {
const cache = result.compilerCaches?.find((item) => item.name === name);
assert(cache, `missing compiler cache ${name}`);
return cache;
}
const MIR_FUNCTION_COUNT_OFFSET = 16;
const MIR_LOCAL_COUNT_OFFSET = 24;
const MIR_INSTR_COUNT_OFFSET = 32;
const MIR_VALUE_COUNT_OFFSET = 40;
const MIR_VALUE_REF_COUNT_OFFSET = 48;
const MIR_INSTR_REF_COUNT_OFFSET = 56;
const MIR_EXTERNAL_COUNT_OFFSET = 64;
const MIR_EXTERNAL_PARAM_TYPE_COUNT_OFFSET = 72;
const MIR_DATA_SEGMENT_COUNT_OFFSET = 80;
const MIR_DATA_BYTES_OFFSET = 88;
const MIR_STRING_BYTES_OFFSET = 96;
const MIR_GRAPH_HASH_REF_OFFSET = 120;
function corruptMappedMirGraphHashIdentity(bytes) {
const copy = Buffer.from(bytes);
const stringBytes = Number(copy.readBigUInt64LE(MIR_STRING_BYTES_OFFSET));
const graphHashOffset = Number(copy.readBigUInt64LE(MIR_GRAPH_HASH_REF_OFFSET));
const graphHashLen = Number(copy.readBigUInt64LE(MIR_GRAPH_HASH_REF_OFFSET + 8));
assert(stringBytes <= copy.length, "mapped MIR cache has invalid string table size");
assert(graphHashLen > 8, "mapped MIR graph hash identity is too short to corrupt");
const stringsOffset = copy.length - stringBytes;
assert(graphHashOffset + 6 < stringBytes, "mapped MIR graph hash identity offset is out of range");
copy[stringsOffset + graphHashOffset + 6] = 0;
return copy;
}
function corruptMappedMirU64(bytes, offset, value) {
const copy = Buffer.from(bytes);
copy.writeBigUInt64LE(value, offset);
return copy;
}
function corruptMappedMirFunctionCount(bytes) {
return corruptMappedMirU64(bytes, MIR_FUNCTION_COUNT_OFFSET, 100_001n);
}
function corruptMappedMirLocalCount(bytes) {
return corruptMappedMirU64(bytes, MIR_LOCAL_COUNT_OFFSET, 1_000_001n);
}
function corruptMappedMirInstrCount(bytes) {
return corruptMappedMirU64(bytes, MIR_INSTR_COUNT_OFFSET, 4_000_001n);
}
function corruptMappedMirValueCount(bytes) {
return corruptMappedMirU64(bytes, MIR_VALUE_COUNT_OFFSET, 4_000_001n);
}
function corruptMappedMirValueRefCount(bytes) {
return corruptMappedMirU64(bytes, MIR_VALUE_REF_COUNT_OFFSET, 8_000_001n);
}
function corruptMappedMirInstrRefCount(bytes) {
return corruptMappedMirU64(bytes, MIR_INSTR_REF_COUNT_OFFSET, 8_000_001n);
}
function corruptMappedMirExternalCount(bytes) {
return corruptMappedMirU64(bytes, MIR_EXTERNAL_COUNT_OFFSET, 100_001n);
}
function corruptMappedMirExternalParamTypeCount(bytes) {
return corruptMappedMirU64(bytes, MIR_EXTERNAL_PARAM_TYPE_COUNT_OFFSET, 1_000_001n);
}
function corruptMappedMirDataSegmentCount(bytes) {
return corruptMappedMirU64(bytes, MIR_DATA_SEGMENT_COUNT_OFFSET, 1_000_001n);
}
function corruptMappedMirDataBytes(bytes) {
return corruptMappedMirU64(bytes, MIR_DATA_BYTES_OFFSET, 256n * 1024n * 1024n + 1n);
}
function corruptMappedMirStringBytes(bytes) {
return corruptMappedMirU64(bytes, MIR_STRING_BYTES_OFFSET, 256n * 1024n * 1024n + 1n);
}
async function assertMappedMirCacheRegeneratesAfterCorruption(cachePath, corrupt, buildOut, artifact, message) {
const original = await readFile(cachePath);
await writeFile(cachePath, corrupt(original));
const result = await zeroJson(["build", "--json", "--target", "linux-musl-x64", "--out", buildOut, artifact]);
const cache = compilerCache(result, "mappedFinalMir");
assert.equal(cache.hit, false, `${message}: corrupted mapped MIR cache must not be reused`);
assert.equal(cache.written, true, `${message}: corrupted mapped MIR cache should be regenerated`);
assert.equal(cache.codegenImmediate, false, `${message}: regeneration should not claim immediate codegen from stale MIR`);
assert.equal(cache.programReconstructed, false, `${message}: graph build should stay graph-native while regenerating MIR`);
assert.equal(cache.path, cachePath, `${message}: regenerated cache should use the same deterministic path`);
assert(result.artifactBytes > 0, `${message}: build should still produce an artifact`);
}
const mappedMirCorruptionCases = [
["embedded NUL in mapped MIR graph identity", corruptMappedMirGraphHashIdentity],
["oversized mapped MIR function count", corruptMappedMirFunctionCount],
["oversized mapped MIR local count", corruptMappedMirLocalCount],
["oversized mapped MIR instruction count", corruptMappedMirInstrCount],
["oversized mapped MIR value count", corruptMappedMirValueCount],
["oversized mapped MIR value ref count", corruptMappedMirValueRefCount],
["oversized mapped MIR instruction ref count", corruptMappedMirInstrRefCount],
["oversized mapped MIR external count", corruptMappedMirExternalCount],
["oversized mapped MIR external param type count", corruptMappedMirExternalParamTypeCount],
["oversized mapped MIR data segment count", corruptMappedMirDataSegmentCount],
["oversized mapped MIR data bytes", corruptMappedMirDataBytes],
["oversized mapped MIR string bytes", corruptMappedMirStringBytes],
];
function testSummary(result) {
return {
ok: result.ok,
target: result.target,
selectedTests: result.selectedTests,
discoveredTests: result.discoveredTests,
passedTests: result.passedTests,
failedTests: result.failedTests,
expectedFailures: result.expectedFailures,
unexpectedPasses: result.unexpectedPasses,
stdout: result.stdout,
};
}
function findResolutionReference(graph, predicate, message) {
const reference = graph.resolution?.references?.find(predicate);
assert(reference, message);
return reference;
}
function findSemanticCall(graph, predicate, message) {
const call = graph.semantics?.calls?.find(predicate);
assert(call, message);
return call;
}
function assertSemanticCallResolutionMatches(graph, call, message) {
const reference = graph.resolution?.references?.find((item) => item.kind === "call" && item.node === call.node);
assert(reference, `${message}: resolver call reference`);
for (const field of ["qualifiedName", "targetKind", "targetNode", "symbolId"]) {
assert.equal(call.resolution?.[field], reference[field], `${message}: semantic resolution ${field}`);
}
}
function resolutionBindings(graph) {
return graph.resolution?.scopes?.flatMap((scope) => scope.bindings ?? []) ?? [];
}
function hasIncomingGraphEdge(graph, nodeId, kind, order = null) {
return graph.edges.some((edge) => edge.target === "node" && edge.to === nodeId && edge.kind === kind && (order === null || edge.order === order));
}
function graphHashText(hash, text = "") {
for (const byte of Buffer.from(text ?? "")) {
hash ^= BigInt(byte);
hash = (hash * graphHashPrime) & graphHashMask;
}
hash ^= 0xffn;
return (hash * graphHashPrime) & graphHashMask;
}
function graphHashU64(hash, value) {
let item = BigInt(value ?? 0);
for (let i = 0n; i < 8n; i++) {
hash ^= (item >> (i * 8n)) & 0xffn;
hash = (hash * graphHashPrime) & graphHashMask;
}
return hash;
}
function graphDomainId(domain, hash) {
return `${domain}:${hash.toString(16).padStart(16, "0")}`;
}
function graphSymbolComponent(text) {
let out = "";
for (const ch of text ?? "") {
const code = ch.charCodeAt(0);
const allowed = (code >= 48 && code <= 57) ||
(code >= 65 && code <= 90) ||
(code >= 97 && code <= 122) ||
ch === "." || ch === "_" || ch === "-" || ch === "@" || ch === "/";
out += allowed ? ch : "_";
}
return out || "main";
}
function graphIdentityName(identity) {
if (!identity) return "main";
if (identity.startsWith("module:")) return identity.slice("module:".length);
if (identity.startsWith("package:")) return identity.slice("package:".length);
return identity;
}
function graphStorageModuleName(identity) {
if (!identity) return "main";
return identity.startsWith("module:") ? identity.slice("module:".length) : identity;
}
function graphModuleScopeName(graph, module) {
const identity = graph.moduleIdentity ?? "module:main";
const moduleName = module?.name || graphIdentityName(identity);
if (identity.startsWith("package:")) return `${graphSymbolComponent(graphIdentityName(identity))}/${graphSymbolComponent(moduleName)}`;
return graphSymbolComponent(moduleName);
}
const graphSymbolNodeKinds = new Set([
"Module",
"Const",
"CImport",
"TypeAlias",
"Shape",
"Interface",
"Enum",
"Choice",
"Function",
"Param",
"Field",
"EnumCase",
"ChoiceCase",
"Let",
"ErrorVariant",
]);
const graphOwnerEdgeKinds = new Set([
"alias", "arg", "arm", "body", "cImport", "case", "choice", "const", "constraint", "declaredType", "default",
"effect", "else", "enum", "error", "expr", "field", "function", "guard", "import", "interface", "left",
"method", "param", "rangeEnd", "returnType", "right", "shape", "statement", "target", "then", "type",
"typeArg", "typeParam", "value", "variant",
]);
function graphNodeDeclaresSymbol(node) {
return Boolean(node?.name) && graphSymbolNodeKinds.has(node.kind);
}
function graphNodeSymbolNamespace(node, ownerEdge) {
if (node.kind === "Module") return "module";
if (node.kind === "Import") return "import";
if (node.kind === "CImport") return "c-import";
if (node.kind === "Const") return "value";
if (node.kind === "Function") return ownerEdge?.kind === "method" ? "method" : "value";
if (["TypeAlias", "Shape", "Interface", "Enum", "Choice"].includes(node.kind)) return "type";
if (node.kind === "Param") return "param";
if (node.kind === "Field") return "field";
if (["EnumCase", "ChoiceCase", "ErrorVariant"].includes(node.kind)) return "variant";
if (node.kind === "Let") return "local";
return null;
}
function graphSymbolName(node) {
if (!node) return "";
if (node.kind !== "Import") return node.name ?? "";
if (node.value) return node.value;
const lastDot = (node.name ?? "").lastIndexOf(".");
return lastDot >= 0 ? node.name.slice(lastDot + 1) : node.name ?? "";
}
function graphOwnerEdgeByNode(graph) {
const nodeIndex = new Map<string, number>(graph.nodes.map((node, index) => [node.id, index] as [string, number]));
const ownerEdges = Array(graph.nodes.length).fill(null);
for (const edge of graph.edges) {
if ((edge.target ?? "node") !== "node" || !graphOwnerEdgeKinds.has(edge.kind)) continue;
const target = nodeIndex.get(edge.to);
if (target !== undefined && !ownerEdges[target]) ownerEdges[target] = edge;
}
return ownerEdges;
}
function graphSymbolOwnerIndex(graph, ownerEdges, nodeIndex) {
const ids = new Map<string, number>(graph.nodes.map((node, index) => [node.id, index] as [string, number]));
let current = nodeIndex;
for (let depth = 0; depth < graph.nodes.length; depth++) {
const edge = ownerEdges[current];
if (!edge) break;
const ownerIndex = ids.get(edge.from);
if (ownerIndex === undefined) break;
const owner = graph.nodes[ownerIndex];
if (owner.kind === "Module" || graphNodeDeclaresSymbol(owner)) return ownerIndex;
current = ownerIndex;
}
return null;
}
function graphNodeSymbolId(graph, ownerEdges, nodeIndex, memo = new Map(), depth = 0) {
if (memo.has(nodeIndex)) return memo.get(nodeIndex);
if (depth > graph.nodes.length) return "";
const node = graph.nodes[nodeIndex];
if (!graphNodeDeclaresSymbol(node)) return "";
const name = graphSymbolName(node);
if (!name) return "";
if (node.kind === "Module") {
const symbol = `symbol:${graphModuleScopeName(graph, node)}::module`;
memo.set(nodeIndex, symbol);
return symbol;
}
const ownerEdge = ownerEdges[nodeIndex];
const ownerIndex = graphSymbolOwnerIndex(graph, ownerEdges, nodeIndex);
const namespace = graphNodeSymbolNamespace(node, ownerEdge);
if (!namespace) return "";
let prefix = "";
if (ownerIndex !== null && graph.nodes[ownerIndex].kind === "Module") {
prefix = `symbol:${graphModuleScopeName(graph, graph.nodes[ownerIndex])}::`;
} else if (ownerIndex !== null) {
prefix = `${graphNodeSymbolId(graph, ownerEdges, ownerIndex, memo, depth + 1)}/`;
} else {
prefix = `symbol:${graphSymbolComponent(graphIdentityName(graph.moduleIdentity))}::`;
}
let symbol = `${prefix}${graphSymbolComponent(namespace)}.${graphSymbolComponent(name)}`;
if (node.kind === "Let") symbol += `@${graphSymbolComponent(node.id.split(".").at(-1) ?? node.id)}`;
memo.set(nodeIndex, symbol);
return symbol;
}
function graphNodeHash(node) {
let hash = 1469598103934665603n;
for (const item of [node.kind, node.name, node.type, node.value]) hash = graphHashText(hash, item ?? "");
for (const item of [node.public, node.mutable, node.static, node.fallible, node.exportC]) hash = graphHashU64(hash, item ? 1 : 0);
return graphDomainId("nodehash", hash);
}
function graphTypeId(node) {
return node.type ? graphDomainId("type", graphHashText(1469598103934665603n, node.type)) : "";
}
function graphEffectId(node) {
return node.kind === "EffectRef" && node.name ? graphDomainId("effect", graphHashText(1469598103934665603n, node.name)) : "";
}
function finalizeGraphIdentities(graph) {
const ownerEdges = graphOwnerEdgeByNode(graph);
const symbolMemo = new Map();
let graphHash = graphHashU64(1469598103934665603n, graph.schemaVersion ?? 1);
graphHash = graphHashText(graphHash, graph.moduleIdentity ?? "");
for (let index = 0; index < graph.nodes.length; index++) {
const node = graph.nodes[index];
node.symbolId = graphNodeSymbolId(graph, ownerEdges, index, symbolMemo);
node.typeId = graphTypeId(node);
node.effectId = graphEffectId(node);
node.nodeHash = graphNodeHash(node);
for (const item of [node.id, node.nodeHash, node.symbolId, node.typeId, node.effectId]) graphHash = graphHashText(graphHash, item ?? "");
}
for (const edge of graph.edges) {
for (const item of [edge.from, edge.to, edge.kind, edge.target ?? "node"]) graphHash = graphHashText(graphHash, item ?? "");
graphHash = graphHashU64(graphHash, edge.order ?? 0);
}
graph.graphHash = graphDomainId("graph", graphHash);
return graph;
}
function graphDumpQuote(text) {
return JSON.stringify(text ?? "");
}
function graphNodeDumpLine(node) {
const parts = [`node ${node.id} ${node.kind}`];
for (const field of ["name", "type", "value", "path"]) {
if (node[field]) parts.push(`${field}:${graphDumpQuote(node[field])}`);
}
if (node.line > 0) parts.push(`line:${node.line}`);
if (node.column > 0) parts.push(`column:${node.column}`);
for (const [field, label] of [["public", "public"], ["mutable", "mutable"], ["static", "static"], ["fallible", "fallible"], ["exportC", "exportC"]]) {
if (node[field]) parts.push(`${label}:true`);
}
return parts.join(" ");
}
function graphDumpText(graph) {
const lines = [
"zero-graph v1",
"origin source-text",
`module ${graphDumpQuote(graphStorageModuleName(graph.moduleIdentity))}`,
`hash ${graphDumpQuote(graph.graphHash)}`,
"",
...graph.nodes.map(graphNodeDumpLine),
...graph.edges.map((edge) => `edge ${edge.from} ${edge.kind} ${edge.to} target:${edge.target ?? "node"} order:${edge.order ?? 0}`),
"",
];
return lines.join("\n");
}
async function assertCheckParity(fixture) {
const sourceInput = await graphCompilerInputForFixture(fixture);
const source = await zeroJson(["check", "--json", sourceInput]);
const artifact = await dumpGraphArtifact(fixture, artifactNameForFixture("check", fixture));
const graph = await zeroJson(["check", "--json", artifact]);
const sourceGraphArtifact = source.graph?.artifact ?? source.artifact ?? source.sourceFile;
const sourceCanonical = source.graph?.canonicalSource ?? source.canonicalSource;
const graphBackedInput = source.sourceFile?.endsWith("/zero.graph") || sourceGraphArtifact?.endsWith(".graph");
assert.equal(sourceCanonical, !graphBackedInput, `${fixture}: source check should report the expected graph input kind`);
assert.equal(graph.canonicalSource, false, `${fixture}: graph artifact check should report artifact input`);
assert.equal(graph.check.phase, "typecheck", `${fixture}: graph artifact check phase`);
assert.equal(graph.check.lowering, "graph-native-check", `${fixture}: graph artifact lowering`);
assert.equal(graph.graphCompiler?.graphNativeCheckerUsed, true, `${fixture}: graph artifact native checker`);
assert.equal(graph.graphCompiler?.graphHirToMirUsed, true, `${fixture}: graph artifact HIR-to-MIR readiness`);
assert.equal(graph.check.ok, source.ok, `${fixture}: source and graph typecheck should agree`);
assert.equal(graph.check.target, source.targetReadiness?.target ?? graph.check.target, `${fixture}: target should agree`);
assert.deepEqual(targetReadinessSummary(graph.targetReadiness), targetReadinessSummary(source.targetReadiness), `${fixture}: target readiness should agree`);
}
async function assertCheckFailureParity(fixture) {
const sourceInput = await graphCompilerInputForFixture(fixture);
const source = await zeroJsonFailure(["check", "--json", sourceInput]);
const artifact = await dumpGraphArtifact(fixture, artifactNameForFixture("check-fail", fixture));
const graph = await zeroJsonFailure(["check", "--json", artifact]);
assert.equal(graph.canonicalSource, false, `${fixture}: graph artifact check failure should report artifact input`);
assert.equal(graph.check.phase, "typecheck", `${fixture}: graph artifact check failure phase`);
assert.equal(graph.check.lowering, "graph-native-check", `${fixture}: graph artifact check failure lowering`);
assert.equal(graph.graphCompiler?.graphNativeCheckerUsed, true, `${fixture}: graph artifact failure native checker`);
assert.equal(graph.check.ok, false, `${fixture}: graph artifact check should fail`);
const sourceDiag = source.diagnostics?.[0];
const graphDiag = graph.diagnostics?.[0];
for (const field of ["code", "message", "path", "line", "column", "expected", "actual", "help"]) {
assert.equal(graphDiag?.[field], sourceDiag?.[field], `${fixture}: diagnostic ${field} should agree`);
}
}
async function assertRoundtripStable(fixture) {
const roundtrip = await zeroJson(["roundtrip", "--json", fixture]);
assert.equal(roundtrip.ok, true, `${fixture}: graph roundtrip ok`);
assert.equal(roundtrip.semanticStable, true, `${fixture}: graph roundtrip semantic stability`);
assert.equal(roundtrip.lowering, "direct-program-graph", `${fixture}: graph roundtrip lowering`);
assert.equal(roundtrip.roundtripModuleIdentity, roundtrip.moduleIdentity, `${fixture}: module identity`);
assert.equal(roundtrip.comparison.ok, true, `${fixture}: graph semantic comparison`);
assert.deepEqual(roundtrip.semanticCounts.original, roundtrip.semanticCounts.roundtrip, `${fixture}: semantic counts`);
}
async function assertCommandStateContracts() {
const sourceDump = await zeroJson(["dump", "--json", "examples/hello.0"]);
const sidecarDump = await zeroJson(["validate", "--json", "examples/hello.graph"]);
assert.equal(sourceDump.canonicalSource, false, "graph dump from a projection should use its graph sidecar");
assert.equal(sourceDump.graphHash, sidecarDump.graphHash, "graph dump from a projection should read the sidecar graph");
assert.equal(sourceDump.validation.state, "shape-valid", "graph dump should produce a shape-valid graph");
assert.equal(sourceDump.validation.ok, true, "graph dump validation should pass");
assert.equal(sourceDump.resolution.state, "resolved", "graph dump should expose name resolution state");
assert.equal(sourceDump.resolution.ok, true, "graph dump resolution should pass");
const worldRef = findResolutionReference(sourceDump, (item) => item.kind === "identifier" && item.name === "world", "graph dump should resolve parameter identifiers");
assert.equal(worldRef.targetKind, "param", "graph dump should classify parameter references");
const writeRef = findResolutionReference(sourceDump, (item) => item.kind === "call" && item.qualifiedName === "world.out.write", "graph dump should resolve member calls to their base binding");
assert.equal(writeRef.targetKind, "member", "graph dump should classify member calls");
const artifact = await dumpGraphArtifact("examples/hello.0", "state-contracts");
const validate = await zeroJson(["validate", "--json", artifact]);
assert.equal(validate.ok, true, "graph validate should accept the artifact");
assert.equal(validate.canonicalSource, false, "graph validate should report artifact input");
assert.equal(validate.validation.state, "shape-valid", "graph validate should promise shape-valid state");
assert.equal(validate.validation.ok, true, "graph validate state should be ok");
const emptyLiteralArtifact = await dumpGraphArtifact("benchmarks/rosetta/empty-string.0", "empty-string-literal");
const emptyLiteralDump = await readFile(emptyLiteralArtifact, "utf8");
assert.match(emptyLiteralDump, /node #[^ ]+ Literal[^\n]* value:""/, "graph dump should preserve empty literal values");
const emptyLiteralValidate = await zeroJson(["validate", "--json", emptyLiteralArtifact]);
assert.equal(emptyLiteralValidate.ok, true, "graph validate should accept stored empty string literal artifacts");
const view = await zeroJson(["view", "--json", artifact]);
assert.equal(view.ok, true, "graph view should render a valid source projection");
assert.equal(view.canonicalSource, false, "graph view should report artifact input");
assert.match(view.view, /pub fn main/, "graph view should include canonical source text");
const check = await zeroJson(["check", "--json", artifact]);
assert.equal(check.ok, true, "graph check should typecheck the artifact");
assert.equal(check.canonicalSource, false, "graph check should report artifact input");
assert.equal(check.check.phase, "typecheck", "graph check should promise typecheck state");
assert.equal(check.check.lowering, "graph-native-check", "graph check should use graph-native semantic checks");
assert.equal(check.graphCompiler.graphNativeCheckerUsed, true, "graph check should report graph-native checker use");
assert.equal(check.graphCompiler.graphHirToMirUsed, true, "graph check should report graph HIR-to-MIR readiness");
assert.equal(check.targetReadiness.languageOk, true, "graph check should include language readiness");
const size = await zeroJson(["size", "--json", "--target", "linux-musl-x64", artifact]);
assert.equal(size.graph.canonicalSource, false, "graph size should report artifact input");
assert.equal(size.graph.lowering, "mapped-final-mir", "graph size should lower through mapped final MIR");
assert.equal(size.generatedCBytes, 0, "graph size should stay on the direct backend");
assert.equal(size.cBridgeFallback, false, "graph size should not use C bridge fallback");
const sizeMirCache = size.compilerCaches.find((cache) => cache.name === "mappedFinalMir");
assert.equal(sizeMirCache.codegenImmediate, false, "graph size should compute report facts after a cold mapped-MIR write");
assert.equal(sizeMirCache.programReconstructed, false, "graph size should derive report facts from graph/IR metadata instead of reconstructing checked Program facts");
const build = await zeroJson(["build", "--json", "--target", "linux-musl-x64", "--out", `${outDir}/state-contracts-build`, artifact]);
const buildMirCache = build.compilerCaches.find((cache) => cache.name === "mappedFinalMir");
assert.equal(buildMirCache.hit, true, "graph build should reuse the mapped final MIR warmed by size");
assert.equal(buildMirCache.codegenImmediate, true, "graph build should codegen immediately from mapped final MIR");
assert.equal(buildMirCache.programReconstructed, false, "graph build should not reconstruct checked program facts on a mapped MIR hit");
for (const [index, [label, corrupt]] of mappedMirCorruptionCases.entries()) {
await assertMappedMirCacheRegeneratesAfterCorruption(
sizeMirCache.path,
corrupt,
`${outDir}/state-contracts-build-corrupt-${index}`,
artifact,
label,
);
}
const stdArgsArtifact = await dumpGraphArtifact("conformance/native/pass/std-args.0", "std-args-mir");
const stdArgsSize = await zeroJson(["size", "--json", "--target", "linux-musl-x64", stdArgsArtifact]);
assert.equal(stdArgsSize.graph.lowering, "mapped-final-mir", "graph size should lower std args through mapped final MIR");
assert.equal(stdArgsSize.generatedCBytes, 0, "graph MIR std args should stay on the direct backend");
assert.equal(stdArgsSize.objectBackend.directFacts.functionCount, 1, "graph MIR std args should retain the main function");
const stdArgsRun = await zeroText(["run", "--out", `${outDir}/std-args-run`, stdArgsArtifact, "one", "two"]);
assert.equal(stdArgsRun, "one\n", "graph run should execute std args from typed graph MIR");
const roundtrip = await zeroJson(["roundtrip", "--json", artifact]);
assert.equal(roundtrip.ok, true, "graph roundtrip should accept the artifact");
assert.equal(roundtrip.canonicalSource, false, "graph roundtrip should report artifact input");
assert.equal(roundtrip.semanticStable, true, "graph roundtrip should promise semantic stability");
assert.equal(roundtrip.lowering, "direct-program-graph", "graph roundtrip should lower through ProgramGraph");
const sourceMap = await zeroJson(["source-map", "--json", "examples/hello.0"]);
assert.equal(sourceMap.ok, true, "graph source-map should succeed");
assert.equal(sourceMap.artifact, "examples/hello.graph", "graph source-map should read the projection sidecar");
assert.equal(sourceMap.canonicalSource, false, "graph source-map should report graph artifact input for projections");
const mainMapping = sourceMap.mappings.find((mapping) => mapping.kind === "Function" && mapping.name === "main");
assert(mainMapping && mainMapping.sourceRange.path === "hello.0", "graph source-map should map function nodes to stored graph source ranges");
assert.equal(mainMapping.sourceAvailable, false, "graph source-map should not tokenize the projection when the sidecar is active");
assert.deepEqual(mainMapping.sourceRange.start, { line: 1, column: 1 }, "graph source-map should preserve stored graph source locations");
assert.deepEqual(mainMapping.sourceRange.end, { line: 1, column: 2 }, "graph source-map should preserve stored graph source ranges");
assert.equal(await zeroText(["source-map", "examples/hello.0"]), `program graph source map ok: ${sourceMap.counts.mappings} mappings\n`, "graph source-map text output");
const repeatedTypesFixture = `${outDir}/source-map-repeated-types.0`;
await writeFile(repeatedTypesFixture, [
"fn double(value: i32) -> i32 {",
" return value + value",
"}",
"",
"pub fn main(world: World) -> Void raises {",
" check world.out.write(\"source map repeated types\\n\")",
"}",
"",
].join("\n"));
await importProjectionSidecar(repeatedTypesFixture);
const repeatedTypesMap = await zeroJson(["source-map", "--json", repeatedTypesFixture]);
const repeatedTypeRanges = repeatedTypesMap.mappings
.filter((mapping) => mapping.kind === "TypeRef" && mapping.type === "i32" && mapping.sourceRange.start.line === 1)
.map((mapping) => mapping.sourceRange.start);
assert.deepEqual(repeatedTypeRanges, [{ line: 1, column: 11 }, { line: 1, column: 1 }], "graph source-map should use stored graph ranges without reparsing projection text");
assert.equal(repeatedTypesMap.mappings.every((mapping) => mapping.sourceAvailable === false), true, "graph source-map should not tokenize sidecar-backed projection text");
}
async function assertResolutionFacts() {
const stdStr = await zeroJson(["dump", "--json", "examples/std-str.0"]);
assert.equal(stdStr.resolution.ok, true, "std-str graph resolution");
const grepScan = await zeroJson(["dump", "--json", "examples/grep-scan.0"]);
assert.equal(grepScan.resolution.ok, true, "grep-scan graph resolution");
const nextLine = findResolutionReference(grepScan, (item) => item.kind === "call" && item.qualifiedName === "std.io.nextLine", "graph-backed std call should resolve");
assert.equal(nextLine.targetKind, "graphBackedStdlib", "graph-backed std call target kind");
assert.match(nextLine.symbolId, /^symbol:std\.io::value\.__zero_std_io_next_line$/, "graph-backed std call symbol");
const memEql = findResolutionReference(stdStr, (item) => item.kind === "call" && item.qualifiedName === "std.mem.eql", "table std helper should resolve");
assert.equal(memEql.targetKind, "stdlib", "table std helper target kind");
assert.equal(memEql.symbolId, "stdlib:std.mem.eql", "table std helper symbol");
const packageGraph = await zeroJson(["dump", "--json", "examples/direct-package-arrays"]);
assert.equal(packageGraph.resolution.ok, true, "package graph resolution");
const record = findResolutionReference(packageGraph, (item) => item.kind === "call" && item.qualifiedName === "record", "package import call should resolve");
assert.equal(record.targetKind, "function", "package import call target kind");
assert.equal(record.symbolId, "symbol:direct-package-arrays@0.1.0/arrays::value.record", "package import call target symbol");
assert.equal(record.viaImport, "symbol:main::import.arrays", "package import call should record import binding");
const cImport = await zeroJson(["dump", "--json", "conformance/native/pass/c-import-alias-later-local.0"]);
assert.equal(cImport.resolution.ok, true, "C import graph resolution");
const cCall = findResolutionReference(cImport, (item) => item.kind === "call" && item.qualifiedName === "c.zero_c_add", "C import call should resolve");
assert.equal(cCall.targetKind, "cFunction", "C import call target kind");
assert.match(cCall.symbolId, /^symbol:c-import-alias-later-local::c-import\.c$/, "C import call symbol");
const localC = findResolutionReference(cImport, (item) => item.kind === "identifier" && item.name === "c" && item.targetKind === "local", "later local should shadow C import after declaration");
assert.match(localC.symbolId, /local\.c@/, "local shadow symbol");
const cImportTypeShadow = await zeroJson(["dump", "--json", "conformance/native/pass/c-import-type-shadowing.0"]);
assert.equal(cImportTypeShadow.resolution.ok, true, "C import/type shadow graph resolution");
const shadowedCounterCall = findResolutionReference(cImportTypeShadow, (item) => item.kind === "call" && item.qualifiedName === "Counter.zero_c_add", "static method should resolve when a C import alias shares the type name");
assert.equal(shadowedCounterCall.targetKind, "method", "C import/type shadow static method target kind");
assert.equal(shadowedCounterCall.symbolId, "symbol:c-import-type-shadowing::type.Counter/method.zero_c_add", "C import/type shadow static method symbol");
assert.equal(shadowedCounterCall.viaImport, "", "same-module static method should not report an import binding");
const counterIdentifier = findResolutionReference(cImportTypeShadow, (item) => item.kind === "identifier" && item.name === "Counter", "call-chain base should resolve to the type namespace");
assert.equal(counterIdentifier.targetKind, "shape", "call-chain base should prefer the type binding for static method chains");
const staticInterface = await zeroJson(["dump", "--json", "examples/static-interface.0"]);
assert.equal(staticInterface.resolution.ok, true, "static interface graph resolution");
const interfaceCall = findResolutionReference(staticInterface, (item) => item.kind === "call" && item.qualifiedName === "T.read", "constrained interface method call should resolve");
assert.equal(interfaceCall.targetKind, "interfaceMethod", "constrained interface call target kind");
assert.equal(interfaceCall.symbolId, "symbol:static-interface::type.Readable/method.read", "constrained interface call target symbol");
const genericFunction = await zeroJson(["dump", "--json", "conformance/native/pass/generic-function-basic.0"]);
assert.equal(genericFunction.resolution.ok, true, "generic function graph resolution");
const identityReturnType = findResolutionReference(genericFunction, (item) => item.kind === "type" && item.name === "T" && item.symbolId === "symbol:generic-function-basic::value.identity/param.T" && hasIncomingGraphEdge(genericFunction, item.node, "returnType"), "generic return type should resolve to its type parameter");
assert.equal(identityReturnType.targetKind, "type", "generic return type target kind");
const staticValues = await zeroJson(["dump", "--json", "conformance/native/pass/static-value-params.0"]);
assert.equal(staticValues.resolution.ok, true, "static value parameter graph resolution");
const staticParamBinding = resolutionBindings(staticValues).find((item) => item.name === "N" && item.kind === "staticParam" && item.symbolId === "symbol:static-value-params::value.first/param.N");
assert(staticParamBinding, "function static parameter should be classified as a staticParam binding");
const capTypeArg = findResolutionReference(staticValues, (item) => item.kind === "type" && item.name === "cap" && hasIncomingGraphEdge(staticValues, item.node, "typeArg"), "static value type argument should resolve through value lookup");
assert.equal(capTypeArg.targetKind, "const", "static value type argument target kind");
assert.equal(capTypeArg.symbolId, "symbol:static-value-params::value.cap", "static value type argument symbol");
const staticExplicitFixture = `${outDir}/resolution-static-explicit-args.0`;
await writeFile(staticExplicitFixture, [
"const Foo: usize = 3",
"",
"type Foo {",
" value: i32,",
"}",
"",
"type Gate<static enabledFlag: Bool, static selectedMode: Mode> {",
" value: i32,",
"}",
"",
"enum Mode: u8 {",
" fast,",
" tiny,",
"}",
"",
"fn readGate<static enabledFlag: Bool, static selectedMode: Mode>(gate: ref<Gate<enabledFlag, selectedMode>>) -> i32 {",
" if enabledFlag {",
" return gate.value",
" }",
" return 0",
"}",
"",
"fn pick<T: Type, static N: usize>(value: T, items: ref<[N]i32>) -> T {",
" return value",
"}",
"",
"pub fn main() -> Void {",
" let items: [Foo]i32 = [1, 2, 3]",
" let value: Foo = pick<Foo, Foo>(Foo { value: 7 }, &items)",
" let gate: Gate<true, Mode.fast> = Gate { value: 9 }",
" let gated: i32 = readGate<true, Mode.fast>(&gate)",
" expect (value.value == 7 && gated == 9)",
"}",
"",
].join("\n"));
await importProjectionSidecar(staticExplicitFixture);
const staticExplicit = await zeroJson(["dump", "--json", staticExplicitFixture]);
assert.equal(staticExplicit.resolution.ok, true, "explicit static generic argument graph resolution");
const pickTypeArg = findResolutionReference(staticExplicit, (item) => item.kind === "type" && item.name === "Foo" && hasIncomingGraphEdge(staticExplicit, item.node, "typeArg", 0), "type argument in a mixed generic call should resolve as a type");
assert.equal(pickTypeArg.targetKind, "type", "mixed generic type argument target kind");
assert.equal(pickTypeArg.symbolId, "symbol:resolution-static-explicit-args::type.Foo", "mixed generic type argument symbol");
const pickStaticArg = findResolutionReference(staticExplicit, (item) => item.kind === "type" && item.name === "Foo" && hasIncomingGraphEdge(staticExplicit, item.node, "typeArg", 1), "static argument with a type-name collision should resolve as a value");
assert.equal(pickStaticArg.targetKind, "const", "static argument with a type-name collision target kind");
assert.equal(pickStaticArg.symbolId, "symbol:resolution-static-explicit-args::value.Foo", "static argument with a type-name collision symbol");
const boolStaticArg = findResolutionReference(staticExplicit, (item) => item.kind === "type" && item.qualifiedName === "true" && hasIncomingGraphEdge(staticExplicit, item.node, "typeArg", 0), "literal bool static argument should resolve");
assert.equal(boolStaticArg.targetKind, "staticLiteral", "literal bool static argument target kind");
assert.equal(boolStaticArg.symbolId, "literal:true", "literal bool static argument symbol");
const enumStaticArg = findResolutionReference(staticExplicit, (item) => item.kind === "type" && item.qualifiedName === "Mode.fast" && hasIncomingGraphEdge(staticExplicit, item.node, "typeArg", 1), "enum-case static argument should resolve");
assert.equal(enumStaticArg.targetKind, "variant", "enum-case static argument target kind");
assert.equal(enumStaticArg.symbolId, "symbol:resolution-static-explicit-args::type.Mode/variant.fast", "enum-case static argument symbol");
const valueTypeCollisionFixture = `${outDir}/resolution-value-type-collision.0`;
await writeFile(valueTypeCollisionFixture, [
"const Foo: usize = 3",
"",
"type Foo {",
" value: i32,",
"}",
"",
"pub fn main() -> Void {",
" let n: usize = Foo",
" expect n == 3",
"}",
"",
].join("\n"));
await importProjectionSidecar(valueTypeCollisionFixture);
const valueTypeCollision = await zeroJson(["dump", "--json", valueTypeCollisionFixture]);
assert.equal(valueTypeCollision.resolution.ok, true, "value/type collision graph resolution");
const valueFoo = findResolutionReference(valueTypeCollision, (item) => item.kind === "identifier" && item.name === "Foo", "ordinary identifiers should prefer value bindings over same-name type bindings");
assert.equal(valueFoo.targetKind, "const", "value/type collision identifier target kind");
assert.equal(valueFoo.symbolId, "symbol:resolution-value-type-collision::value.Foo", "value/type collision identifier symbol");
const builtinShadowFixture = `${outDir}/resolution-builtin-shadow.0`;
await writeFile(builtinShadowFixture, [
"type World {",
" value: i32,",
"}",
"",
"pub fn main() -> Void {",
" let item: World = World { value: 1 }",
" expect item.value == 1",
"}",
"",
].join("\n"));
await importProjectionSidecar(builtinShadowFixture);
const builtinShadow = await zeroJson(["dump", "--json", builtinShadowFixture]);
assert.equal(builtinShadow.resolution.ok, true, "builtin type shadow graph resolution");
const shadowedWorld = findResolutionReference(builtinShadow, (item) => item.kind === "type" && item.name === "World" && hasIncomingGraphEdge(builtinShadow, item.node, "declaredType"), "declared types should shadow builtin type names in resolution facts");
assert.equal(shadowedWorld.targetKind, "type", "builtin shadow type target kind");
assert.equal(shadowedWorld.symbolId, "symbol:resolution-builtin-shadow::type.World", "builtin shadow type symbol");
const forRange = await zeroJson(["dump", "--json", "conformance/native/pass/for-range.0"]);
assert.equal(forRange.resolution.ok, true, "for range graph resolution");
const forIndexBinding = resolutionBindings(forRange).find((item) => item.name === "index" && item.kind === "local");
assert(forIndexBinding, "for range iterator should create a local binding");
assert.match(forIndexBinding.symbolId, /local\.index@_stmt_/, "for range iterator symbol should include its node id");
const forIndexRef = findResolutionReference(forRange, (item) => item.kind === "identifier" && item.name === "index" && item.targetKind === "local", "for range body should resolve iterator references");
assert.equal(forIndexRef.symbolId, forIndexBinding.symbolId, "for range iterator reference symbol");
const resultChoice = await zeroJson(["dump", "--json", "examples/result-choice.0"]);
assert.equal(resultChoice.resolution.ok, true, "choice constructor graph resolution");
const choiceConstructor = findResolutionReference(resultChoice, (item) => item.kind === "call" && item.qualifiedName === "Result.ok", "choice constructor should resolve to its case binding");
assert.equal(choiceConstructor.targetKind, "variant", "choice constructor target kind");
assert.equal(choiceConstructor.symbolId, "symbol:result-choice::type.Result/variant.ok", "choice constructor target symbol");
assert.equal(choiceConstructor.viaImport, "", "same-module choice constructor should not report an import binding");
const patternBinding = resolutionBindings(resultChoice).find((item) => item.name === "value" && item.kind === "pattern");
assert(patternBinding, "choice match payload should create a pattern binding");
assert.match(patternBinding.symbolId, /pattern\.value@_stmt_/, "choice match payload symbol should include its node id");
const testBlocks = await zeroJson(["dump", "--json", "conformance/native/pass/test-blocks.0"]);
assert.equal(testBlocks.resolution.ok, true, "test block graph resolution");
const expectCall = findResolutionReference(testBlocks, (item) => item.kind === "call" && item.qualifiedName === "expect", "test expect call should resolve as a language builtin");
assert.equal(expectCall.targetKind, "testExpect", "test expect call target kind");
assert.equal(expectCall.symbolId, "builtin:expect", "test expect call symbol");
const compileTime = await zeroJson(["dump", "--json", "conformance/native/pass/compile-time-v1.0"]);
assert.equal(compileTime.resolution.ok, true, "compile-time facts graph resolution");
const targetNamespace = findResolutionReference(compileTime, (item) => item.kind === "identifier" && item.name === "target", "target meta namespace should resolve");
assert.equal(targetNamespace.targetKind, "targetNamespace", "target namespace target kind");
for (const fact of ["hasField", "fieldCount", "fieldType", "enumCaseCount", "hasEnumCase", "choiceCaseCount", "hasChoiceCase"]) {
const metaFact = findResolutionReference(compileTime, (item) => item.kind === "call" && item.qualifiedName === fact, `${fact} meta call should resolve`);
assert.equal(metaFact.targetKind, "metaFact", `${fact} meta call target kind`);
assert.equal(metaFact.symbolId, `meta:${fact}`, `${fact} meta call symbol`);
}
const targetFacts = await zeroJson(["dump", "--json", "conformance/native/pass/meta-typed-target-type.0"]);
assert.equal(targetFacts.resolution.ok, true, "target fact graph resolution");
const hasCapability = findResolutionReference(targetFacts, (item) => item.kind === "call" && item.qualifiedName === "target.hasCapability", "target fact call should resolve");
assert.equal(hasCapability.targetKind, "targetFact", "target fact call target kind");
assert.equal(hasCapability.symbolId, "meta:target.hasCapability", "target fact call symbol");
const fixedVec = await zeroJson(["dump", "--json", "examples/fixed-vec.0"]);
assert.equal(fixedVec.resolution.ok, true, "Self graph resolution");
const selfReturn = findResolutionReference(fixedVec, (item) => item.kind === "type" && item.name === "Self" && hasIncomingGraphEdge(fixedVec, item.node, "returnType"), "Self return type should resolve to the enclosing type");
assert.equal(selfReturn.targetKind, "type", "Self return type target kind");
assert.equal(selfReturn.symbolId, "symbol:fixed-vec::type.FixedVec", "Self return type symbol");
const receiverPush = findResolutionReference(fixedVec, (item) => item.kind === "call" && item.qualifiedName === "vec.push", "receiver method should resolve to the method binding");
assert.equal(receiverPush.targetKind, "method", "receiver method target kind");
assert.equal(receiverPush.symbolId, "symbol:fixed-vec::type.FixedVec/method.push", "receiver method target symbol");
const systemsPackage = await zeroJson(["dump", "--json", "examples/systems-package"]);
assert.equal(systemsPackage.resolution.ok, true, "package-local module graph resolution");
const statusType = findResolutionReference(systemsPackage, (item) => item.kind === "type" && item.name === "Status" && item.symbolId === "symbol:systems-package@0.1.0/types::type.Status", "package-local type should resolve across loaded modules");
assert.equal(statusType.targetKind, "type", "package-local type target kind");
const statusVariant = findResolutionReference(systemsPackage, (item) => item.kind === "identifier" && item.name === "Status" && item.symbolId === "symbol:systems-package@0.1.0/types::type.Status", "package-local enum namespace should resolve across loaded modules");
assert.equal(statusVariant.targetKind, "enum", "package-local enum namespace target kind");
const hostedTypes = await zeroJson(["dump", "--json", "examples/readall-cli"]);
assert.equal(hostedTypes.resolution.ok, true, "hosted std-backed type graph resolution");
const stdShadowFixture = `${outDir}/std-shadow.0`;
await writeFile(stdShadowFixture, [
"pub fn main(world: World) -> Void raises {",
" let std: i32 = 1",
" if std == 1 {",
" check world.out.write(\"std shadow ok\\n\")",
" }",
"}",
"",
].join("\n"));
await importProjectionSidecar(stdShadowFixture);
const stdShadow = await zeroJson(["dump", "--json", stdShadowFixture]);
assert.equal(stdShadow.resolution.ok, true, "local std shadow graph resolution");
const stdRef = findResolutionReference(stdShadow, (item) => item.kind === "identifier" && item.name === "std", "local std identifier should be present");
assert.equal(stdRef.targetKind, "local", "local std should shadow the stdlib namespace");
assert.match(stdRef.symbolId, /local\.std@/, "local std shadow symbol");
}
async function assertStdGraphDependencyMerge() {
const fixture = `${outDir}/stdlib-codec-http-merge.0`;
const artifact = `${outDir}/stdlib-codec-http-merge.graph`;
await writeFile(fixture, [
"export c fn main() -> i32 {",
" var ok: Bool = true",
"",
" let bad_hex: Maybe<usize> = std.codec.hexDecodedLen(\"41z\")",
" var base64_buf: [2]u8 = [0_u8; 2]",
" let bad_base64: Maybe<Span<u8>> = std.codec.base64Decode(base64_buf, \"AAB=\")",
" if bad_hex.has || bad_base64.has {",
" ok = false",
" }",
"",
" var request_buf: [128]u8 = [0_u8; 128]",
" let request: Maybe<Span<u8>> = std.http.writeJsonRequest(request_buf, \"POST https://example.com/api?name=zero\", \"{\\\"ping\\\":1}\")",
" if request.has {",
" let missing: Maybe<Span<u8>> = std.http.requestQueryValue(request.value, \"missing\")",
" if missing.has {",
" ok = false",
" }",
" }",
"",
" if ok {",
" return 0",
" }",
" return 1",
"}",
"",
].join("\n"));
await zeroText(["import", "--format", "text", "--out", artifact, fixture]);
assert.equal(await zeroText(["validate", artifact]), "program graph ok\n", "merged std dependency graph should validate");
assert.equal(await zeroText(["check", artifact]), "ok\n", "merged std dependency graph should check");
const graph = await zeroJson(["dump", "--json", artifact]);
assert.equal(graph.validation.ok, true, "merged std dependency graph dump validation");
const stdUrl = graph.nodes.find((node) => node.kind === "Module" && node.name === "std.url");
assert(stdUrl, "std graph dependency merge should retain the shared std.url module");
const nodeById = new Map<string, any>(graph.nodes.map((node: any) => [node.id, node]));
const functionEdges = graph.edges.filter((edge) => edge.target === "node" && edge.from === stdUrl.id && edge.kind === "function");
const orders = functionEdges.map((edge) => edge.order).sort((a, b) => a - b);
assert.deepEqual(orders, [...orders.keys()], "shared std module function edges should be compact after merging partial std graphs");
const functionNames = new Set(functionEdges.map((edge) => nodeById.get(edge.to)?.name));
assert(!functionNames.has("__zero_std_url_percent_encode"), "unused std.codec URL dependency helpers should be pruned");
assert(functionNames.has("__zero_std_url_query_value"), "std.http dependency should contribute URL query helpers");
}
async function assertSemanticFacts() {
const hello = await zeroJson(["dump", "--json", "examples/hello.0"]);
assert.equal(hello.semantics.state, "typed-facts", "hello graph semantic fact state");
assert.equal(hello.semantics.ok, true, "hello graph semantic facts");
assert.equal(hello.semantics.counts.functions, 1, "hello semantic function count");
assert.equal(hello.semantics.counts.calls, 1, "hello semantic call count");
assert.equal(hello.semantics.counts.fallibleCalls, 1, "hello semantic fallible call count");
assert.equal(hello.semantics.counts.resources, hello.semantics.resources.length, "hello semantic resource count");
assert.equal(hello.semantics.counts.targetRequirements, hello.semantics.targetRequirements.length, "hello semantic target requirement count");
assert.equal(hello.semantics.counts.repairs, hello.semantics.repairs.length, "hello semantic repair count");
const helloMain = hello.semantics.functions.find((item) => item.name === "main");
assert(helloMain, "hello semantic function fact");
assert.equal(helloMain.returnType, "Void", "hello function return type");
assert.equal(helloMain.fallible, true, "hello function fallibility");
assert.deepEqual(helloMain.params.map((item) => [item.name, item.type]), [["world", "World"]], "hello function params");
assert.equal(helloMain.sourceRange.path, "hello.0", "hello function semantic source range should use the stored graph path");
assert.deepEqual(helloMain.sourceRange.start, { line: 1, column: 1 }, "hello function semantic source range should come from stored graph metadata");
assert.deepEqual(helloMain.sourceRange.end, { line: 1, column: 2 }, "hello function semantic source range should come from stored graph metadata");
assert(hello.semantics.ownership.some((item) => item.name === "world" && item.ownership === "resource-handle" && item.resource === true), "world parameter should be represented as a resource handle");
const write = findSemanticCall(hello, (item) => item.qualifiedName === "world.out.write", "world write semantic call fact");
assert.equal(write.returnType, "Void", "world write return type");
assert.equal(write.contract.kind, "worldStreamWrite", "world write contract kind");
assert.equal(write.contract.capability, "io", "world write capability");
assert.equal(write.contract.targetSupport, "world-io", "world write target support");
assert.match(write.contract.targetNode, /^#param_/, "world write contract target node");
assert.equal(write.contract.symbolId, "symbol:hello::value.main/param.world", "world write contract symbol");
assertSemanticCallResolutionMatches(hello, write, "world write semantic resolution");
assert.equal(write.resolution.targetKind, "member", "world write semantic resolution kind");
assert.equal(write.resolution.symbolId, "symbol:hello::value.main/param.world", "world write semantic resolution symbol");
assert.equal(write.fallible, true, "world write fallibility");
assert.equal(write.checked, true, "world write checked state");
assert.equal(write.contract.requiresCheck, false, "checked world write should not require repair");
assert.equal(write.contract.repair.id, "check-fallible-call", "world write repair shape");
assert.equal(write.sourceRange.path, "hello.0", "world write source range should use the stored graph path");
assert.deepEqual(write.sourceRange.start, { line: 2, column: 26 }, "world write semantic source range should come from stored graph metadata");
assert.deepEqual(write.sourceRange.end, { line: 2, column: 27 }, "world write semantic source range should come from stored graph metadata");
assert(hello.semantics.resources.some((item) => item.kind === "capabilityUse" && item.resourceKind === "world-io" && item.qualifiedName === "world.out.write"), "world write resource fact");
assert(hello.semantics.targetRequirements.some((item) => item.qualifiedName === "world.out.write" && item.capability === "io" && item.targetSupport === "world-io"), "world write target requirement fact");
assert(hello.semantics.repairs.some((item) => item.qualifiedName === "world.out.write" && item.requiresCheck === false && item.repair.id === "check-fallible-call"), "world write top-level repair fact");
const grepScan = await zeroJson(["dump", "--json", "examples/grep-scan.0"]);
const nextLine = findSemanticCall(grepScan, (item) => item.qualifiedName === "std.io.nextLine", "std io nextLine semantic call fact");
assert.equal(nextLine.contract.kind, "graphBackedStdlib", "graph-backed std contract kind");
assert.equal(nextLine.contract.sourceModule, "std.io", "graph-backed std module");
assert.equal(nextLine.contract.returnType, "Maybe<Span<u8>>", "graph-backed std return type");
assert.equal(nextLine.contract.capability, "memory", "graph-backed std capability");
assert.equal(nextLine.contract.targetSupport, "target-neutral", "graph-backed std target support");
assert.equal(nextLine.contract.expectedArgCount, 2, "graph-backed std arg count");
assert.deepEqual(nextLine.contract.expectedArgTypes, ["Span<u8>", "usize"], "graph-backed std arg types");
assertSemanticCallResolutionMatches(grepScan, nextLine, "graph-backed std semantic resolution");
assert.equal(nextLine.resolution.targetKind, "graphBackedStdlib", "graph-backed std semantic resolution kind");
assert.equal(nextLine.resolution.symbolId, "symbol:std.io::value.__zero_std_io_next_line", "graph-backed std semantic symbol");
assert.deepEqual(nextLine.args.map((item) => item.type), ["Span<u8>", "usize"], "graph-backed std actual arg types");
const stdFs = await zeroJson(["dump", "--json", "conformance/native/pass/std-fs-fallible.0"]);
const stdFsMain = stdFs.semantics.functions.find((item) => item.name === "main");
assert(stdFsMain, "std fs main semantic function fact");
assert.deepEqual(stdFsMain.errors, ["NotFound", "TooLarge", "Io"], "named error set facts");
const readAll = findSemanticCall(stdFs, (item) => item.qualifiedName === "std.fs.readAllOrRaise", "fallible std helper semantic call fact");
assert.equal(readAll.contract.kind, "stdlib", "fallible std helper contract kind");
assert.equal(readAll.contract.fallible, true, "fallible std helper contract fallibility");
assert.equal(readAll.contract.checked, true, "fallible std helper checked state");
assert.deepEqual(readAll.contract.errors, ["NotFound", "TooLarge", "Io"], "fallible std helper errors");
assert.equal(readAll.contract.capability, "fs", "fallible std helper capability");
assert.equal(readAll.contract.targetSupport, "host", "fallible std helper target support");
assert.equal(readAll.contract.repair.id, "check-fallible-call", "fallible std helper repair shape");
assertSemanticCallResolutionMatches(stdFs, readAll, "fallible std helper semantic resolution");
assert.equal(readAll.resolution.targetKind, "stdlib", "fallible std helper semantic resolution kind");
assert.equal(readAll.resolution.symbolId, "stdlib:std.fs.readAllOrRaise", "fallible std helper semantic symbol");
assert(stdFs.semantics.ownership.some((item) => item.name === "body" && item.type === "owned<ByteBuf>" && item.ownership === "owned"), "owned ByteBuf ownership fact");
assert(stdFs.semantics.borrowing.some((item) => item.type === "ref<ByteBuf>" && item.borrowKind === "borrow" && item.target), "borrowed ByteBuf fact");
assert(stdFs.semantics.resources.some((item) => item.kind === "capabilityUse" && item.qualifiedName === "std.fs.readAllOrRaise" && item.capability === "fs"), "std fs resource-use fact");
assert(stdFs.semantics.targetRequirements.some((item) => item.qualifiedName === "std.fs.readAllOrRaise" && item.capability === "fs" && item.targetSupport === "host"), "std fs target requirement fact");
assert(stdFs.semantics.repairs.some((item) => item.qualifiedName === "std.fs.readAllOrRaise" && item.requiresCheck === false), "std fs top-level repair fact");
const cImport = await zeroJson(["dump", "--json", "conformance/native/pass/c-import-alias-later-local.0"]);
const cCall = findSemanticCall(cImport, (item) => item.qualifiedName === "c.zero_c_add", "C import semantic call fact");
assert.equal(cCall.contract.kind, "cAbi", "C import contract kind");
assert.equal(cCall.contract.capability, "c-abi", "C import capability");
assert.equal(cCall.contract.targetSupport, "host-c-abi", "C import target support");
assert.match(cCall.contract.targetNode, /^#cimp_/, "C import contract target node");
assert.match(cCall.contract.symbolId, /^symbol:c-import-alias-later-local::c-import\.c$/, "C import contract symbol");
assertSemanticCallResolutionMatches(cImport, cCall, "C import semantic resolution");
assert.equal(cCall.resolution.targetKind, "cFunction", "C import semantic resolution kind");
assert.equal(cCall.returnType, "i32", "C import return type");
assert.deepEqual(cCall.args.map((item) => item.type), ["i32", "i32"], "C import actual arg types");
assert(cImport.semantics.targetRequirements.some((item) => item.qualifiedName === "c.zero_c_add" && item.capability === "c-abi" && item.targetSupport === "host-c-abi"), "C import target requirement fact");
assert(cImport.semantics.resources.some((item) => item.kind === "capabilityUse" && item.qualifiedName === "c.zero_c_add" && item.resourceKind === "c-abi"), "C import resource fact");
const borrowGraph = await zeroJson(["dump", "--json", "conformance/native/pass/borrow-return-explicit-ref-field-origin.0"]);
assert(borrowGraph.semantics.ownership.some((item) => item.name === "current" && item.ownership === "borrow"), "borrowed local ownership fact");
assert(borrowGraph.semantics.borrowing.some((item) => item.borrowKind === "mut-borrow" && item.mutable === true && item.target), "mutable borrow target fact");
assert.equal(borrowGraph.semantics.resources.length, 0, "borrow-only fixture should not invent resource facts");
const userResourceNameFixture = `${outDir}/semantic-user-resource-names.0`;
await writeFile(userResourceNameFixture, [
"type File {",
" fd: i32,",
"}",
"",
"type UserFileRecord {",
" value: i32,",
"}",
"",
"pub fn main() -> Void {",
" let record: UserFileRecord = UserFileRecord { value: 1 }",
" let file: File = File { fd: record.value }",
" let maybe_file: Maybe<File> = null",
" expect file.fd == 1",
"}",
"",
].join("\n"));
await importProjectionSidecar(userResourceNameFixture);
const userResourceNames = await zeroJson(["dump", "--json", userResourceNameFixture]);
assert.equal(userResourceNames.semantics.resources.length, 0, "user-defined type names containing resource words should not emit resource facts");
assert(!userResourceNames.semantics.ownership.some((item) => item.type === "File" || item.type === "UserFileRecord" || item.type === "Maybe<File>"), "user-defined type names containing resource words should not emit ownership resource facts");
const shadowedStdResourceFixture = `${outDir}/semantic-shadowed-stdlib-resource.0`;
await writeFile(shadowedStdResourceFixture, [
"type File {",
" fd: i32,",
"}",
"",
"pub fn main() -> Void raises [NotFound, TooLarge, Io] {",
" let fs: Fs = std.fs.host()",
" let owned_file: owned<File> = check std.fs.createOrRaise(fs, \".zero/semantic-shadowed-stdlib-resource.txt\")",
" let user_file: File = File { fd: 1 }",
" expect user_file.fd == 1",
"}",
"",
].join("\n"));
await importProjectionSidecar(shadowedStdResourceFixture);
const shadowedStdResource = await zeroJson(["dump", "--json", shadowedStdResourceFixture]);
assert(shadowedStdResource.semantics.ownership.some((item) => item.name === "owned_file" && item.type === "owned<File>" && item.ownership === "owned" && item.resource === true), "stdlib File resource facts should survive a user-defined File type");
assert(!shadowedStdResource.semantics.ownership.some((item) => item.name === "user_file"), "user-defined File binding should not become an ownership fact");
assert(shadowedStdResource.semantics.resources.some((item) => item.kind === "binding" && item.type === "owned<File>" && item.resourceKind === "file"), "stdlib File resource binding should survive a user-defined File type");
const stdIoLines = await zeroJson(["dump", "--json", "conformance/native/pass/std-io-lines.graph"]);
assert(stdIoLines.semantics.ownership.some((item) => item.name === "line_reader" && item.type === "FixedReader" && item.ownership === "resource-handle" && item.resource === true), "FixedReader bindings should remain resource handles despite stdlib shape declarations");
assert(stdIoLines.semantics.ownership.some((item) => item.name === "writer" && item.type === "FixedWriter" && item.ownership === "resource-handle" && item.resource === true), "FixedWriter bindings should remain resource handles despite stdlib shape declarations");
assert(stdIoLines.semantics.ownership.some((item) => item.type === "mutref<FixedReader>" && item.ownership === "mut-borrow" && item.resource === true), "FixedReader borrows should preserve resource facts");
assert(stdIoLines.semantics.ownership.some((item) => item.type === "mutref<FixedWriter>" && item.ownership === "mut-borrow" && item.resource === true), "FixedWriter borrows should preserve resource facts");
assert(stdIoLines.semantics.resources.some((item) => item.kind === "binding" && item.type === "FixedReader" && item.resourceKind === "resource"), "FixedReader bindings should emit resource facts");
assert(stdIoLines.semantics.resources.some((item) => item.kind === "binding" && item.type === "FixedWriter" && item.resourceKind === "resource"), "FixedWriter bindings should emit resource facts");
const searchSort = await zeroJson(["dump", "--json", "conformance/native/pass/std-search-sort-widths.0"]);
assert(!searchSort.semantics.resources.some((item) => item.qualifiedName === "std.search.binaryU32" || item.qualifiedName === "std.search.binaryUsize"), "no-allocation search helpers should not emit resource facts");
const callResolution = await zeroJson(["dump", "--json", "conformance/check/pass/call-resolution-inspection.0"]);
const resolverCallReferences = callResolution.resolution.references.filter((item) => item.kind === "call");
assert.equal(callResolution.semantics.calls.length, resolverCallReferences.length, "semantic calls should mirror resolver call references and skip operators");
for (const call of callResolution.semantics.calls) assertSemanticCallResolutionMatches(callResolution, call, `call resolution semantic fact ${call.qualifiedName}`);
const receiverCall = findSemanticCall(callResolution, (item) => item.qualifiedName === "counter.checkedRead", "receiver method semantic call fact");
assert.equal(receiverCall.args.length, 0, "receiver method source-visible args");
assert.equal(receiverCall.contract.expectedArgCount, 0, "receiver method expected arg count should exclude implicit self");
assert.equal(receiverCall.receiver?.implicit, true, "receiver method should expose implicit receiver");
assert.equal(receiverCall.receiver?.type, "Counter", "receiver method receiver type");
const staticRead = findSemanticCall(callResolution, (item) => item.qualifiedName === "Counter.read", "static method call semantic fact");
assert.equal(staticRead.receiver, null, "static method namespace calls should not report implicit receivers");
assert.equal(staticRead.args.length, 1, "static method call source-visible args");
assert.equal(staticRead.contract.expectedArgCount, 1, "static method expected arg count should keep explicit self arg");
const interfaceRead = findSemanticCall(callResolution, (item) => item.qualifiedName === "T.read", "interface method semantic call fact");
assert.equal(interfaceRead.resolution.targetKind, "interfaceMethod", "interface method semantic target kind");
assert.equal(interfaceRead.resolution.symbolId, "symbol:call-resolution-inspection::type.Readable/method.read", "interface method semantic symbol");
const eventKey = findSemanticCall(callResolution, (item) => item.qualifiedName === "Event.key", "choice variant semantic call fact");
assert.equal(eventKey.resolution.targetKind, "variant", "choice variant semantic target kind");
assert.equal(eventKey.resolution.symbolId, "symbol:call-resolution-inspection::type.Event/variant.key", "choice variant semantic symbol");
}
async function assertUnconstrainedGenericTypeParams() {
const fixture = `${outDir}/generic-no-constraint.0`;
await writeFile(fixture, [
"type Box<T> {",
" value: T,",
"}",
"",
"fn id<T>(value: T) -> T {",
" return value",
"}",
"",
"pub fn main(world: World) -> Void raises {",
" let box: Box<i32> = Box { value: id<i32>(1) }",
" if box.value == 1 {",
" check world.out.write(\"generic no constraint ok\\n\")",
" }",
"}",
"",
].join("\n"));
const fixtureGraph = await importProjectionSidecar(fixture);
const check = await zeroJson(["check", "--json", fixtureGraph]);
assert.equal(check.ok, true, "source check should accept unconstrained generic parameters");
const dump = await zeroJson(["dump", "--json", fixture]);
assert.equal(dump.validation.ok, true, "graph dump should validate unconstrained generic parameters");
assert(dump.nodes.some((node) => node.kind === "Param" && node.name === "T" && node.type === ""), "graph dump should preserve an unconstrained type parameter");
const artifact = await dumpGraphArtifact(fixture, "generic-no-constraint");
const validate = await zeroJson(["validate", "--json", artifact]);
assert.equal(validate.ok, true, "graph validate should accept unconstrained generic parameter artifacts");
}
async function assertBuildParity(fixture, name) {
const sourceInput = await graphCompilerInputForFixture(fixture);
const artifact = await dumpGraphArtifact(fixture, `${name}-build-input`);
const sourceOut = `${outDir}/${name}.source-build`;
const graphOut = `${outDir}/${name}.graph-build`;
const source = await zeroJson(["build", "--json", "--target", "linux-musl-x64", "--out", sourceOut, sourceInput]);
const graph = await zeroJson(["build", "--json", "--target", "linux-musl-x64", "--out", graphOut, artifact]);
assert.equal(graph.graph.artifact, artifact, `${fixture}: graph build artifact`);
assert.equal(graph.graph.canonicalSource, false, `${fixture}: graph build should use artifact input`);
assert.equal(graph.graph.lowering, "mapped-final-mir", `${fixture}: graph build lowering`);
assert.deepEqual(buildSummary(graph), buildSummary(source), `${fixture}: source and graph build summaries should agree`);
assert(source.artifactBytes > 0, `${fixture}: source build should write an artifact`);
assert(graph.artifactBytes > 0, `${fixture}: graph build should write an artifact`);
}
async function assertRunParity(fixture, name, args = []) {
const sourceInput = await graphCompilerInputForFixture(fixture);
const artifact = await dumpGraphArtifact(fixture, `${name}-run-input`);
const sourceOut = `${outDir}/${name}.source-run`;
const graphOut = `${outDir}/${name}.graph-run`;
const source = await zeroText(["run", "--out", sourceOut, sourceInput, "--", ...args]);
const graph = await zeroText(["run", "--out", graphOut, artifact, "--", ...args]);
assert.equal(graph, source, `${fixture}: source and graph run output should agree`);
}
async function assertTestParity(fixture, name) {
const sourceInput = await graphCompilerInputForFixture(fixture);
const artifact = await dumpGraphArtifact(fixture, `${name}-test-input`);
const source = await zeroJson(["test", "--json", sourceInput]);
const graph = await zeroJson(["test", "--json", artifact]);
assert.equal(graph.graph.artifact, artifact, `${fixture}: graph test artifact`);
assert.equal(graph.graph.canonicalSource, false, `${fixture}: graph test should use artifact input`);
assert.equal(graph.graph.lowering, "direct-program-graph", `${fixture}: graph test lowering`);
assertGraphCompilerRoute(source, sourceInput, "direct-program-graph");
assert.equal(source.testBackend, "direct-program-graph", `${fixture}: source test backend`);
assert.equal(graph.testBackend, "direct-program-graph", `${fixture}: graph test backend`);
assert.deepEqual(testSummary(graph), testSummary(source), `${fixture}: source and graph test summaries should agree`);
}
async function assertGraphCommandCompilerPath() {
const helloCheck = await zeroJson(["check", "--json", "examples/hello.graph"]);
assertGraphCompilerRoute(helloCheck, "examples/hello.graph");
if (helloCheck.compilerCaches) assert.equal(helloCheck.compilerCaches[0].sourceKind, "program-graph", "graph check should use graph cache identity");
assert.deepEqual(targetReadinessSummary(helloCheck.targetReadiness), {
languageOk: true,
buildable: true,
stage: "ready",
code: null,
}, "graph check target readiness");
const stdPathCheck = await zeroJson(["check", "--json", "std/path.graph"]);
assertGraphCompilerRoute(stdPathCheck, "std/path.graph", "typed-program-graph-mir");
assert.equal(stdPathCheck.ok, true, "stdlib graph check should preserve library entrypoint rules");
if (stdPathCheck.compilerCaches) assert.equal(stdPathCheck.compilerCaches[0].sourceKind, "program-graph", "stdlib graph check should use graph cache identity");
const helloBuildOut = `${outDir}/graph-command-build`;
const helloBuild = await zeroJson(["build", "--json", "--target", "linux-musl-x64", "--out", helloBuildOut, "examples/hello.graph"]);
assertGraphCompilerRoute(helloBuild, "examples/hello.graph", "mapped-final-mir");
assert.equal(helloBuild.generatedCBytes, 0, "graph build should stay on direct backend");
assert.equal(helloBuild.compilerCaches[0].sourceKind, "program-graph", "graph build should use graph cache identity");
assert.equal(helloBuild.incrementalInvalidation.sourceKind, "program-graph", "graph build invalidation source kind");
assert.equal(helloBuild.incrementalInvalidation.graphInput.artifact, "examples/hello.graph", "graph build graph input");
assert(helloBuild.artifactBytes > 0, "graph build should write an artifact");
const helloSize = await zeroJson(["size", "--json", "--target", "linux-musl-x64", "examples/hello.graph"]);
assertGraphCompilerRoute(helloSize, "examples/hello.graph", "mapped-final-mir");
assert.equal(helloSize.generatedCBytes, 0, "graph size should stay on direct backend");
assert.equal(helloSize.cBridgeFallback, false, "graph size should not use C bridge fallback");
assert.equal(helloSize.compilerCaches[0].sourceKind, "program-graph", "graph size should use graph cache identity");
const helloArtifact = await dumpGraphArtifact("examples/hello.0", "source-command-cache-key");
const helloGraphSize = await zeroJson(["size", "--json", "--target", "linux-musl-x64", helloArtifact]);
assert.equal(compilerCacheKey(helloSize, "parseTree"), compilerCacheKey(helloGraphSize, "parseTree"), "graph size and graph artifact size should share graph parse cache key");
assert.equal(compilerCacheKey(helloSize, "checkedBody"), compilerCacheKey(helloGraphSize, "checkedBody"), "graph size and graph artifact size should share graph check cache key");
const helloMem = await zeroJson(["mem", "--json", "examples/hello.graph"]);
assertGraphCompilerRoute(helloMem, "examples/hello.graph", "mapped-final-mir");
assert.equal(helloMem.compilerCaches[0].sourceKind, "program-graph", "graph mem should use graph cache identity");
assert.equal(helloMem.incrementalInvalidation.sourceKind, "program-graph", "graph mem invalidation source kind");
const packageCheck = await zeroJson(["check", "--json", "conformance/packages/test-app"]);
assert(packageCheck.graph, "package command should report graph compiler input");
assert.equal(packageCheck.graph.artifact, "conformance/packages/test-app/zero.graph", "package should route through repository graph store");
assert.equal(packageCheck.graph.canonicalSource, false, "package graph should not report canonical source");
assert.match(packageCheck.graph.graphHash, /^graph:[0-9a-f]{16}$/, "package graph hash");
assert.equal(packageCheck.graph.lowering, "graph-native-check", "package graph lowering");
assert.equal(packageCheck.graph.moduleIdentity, "package:test-app@0.1.0", "package graph identity");
assert.equal(packageCheck.package.name, "test-app", "package metadata");
const packageTest = await zeroJson(["test", "--json", "conformance/packages/test-app"]);
assert(packageTest.graph, "package test should report graph compiler input");
assert.equal(packageTest.graph.artifact, "conformance/packages/test-app/zero.graph", "package test should route through repository graph store");
assert.equal(packageTest.graph.canonicalSource, false, "package test graph should not report canonical source");
assert.match(packageTest.graph.graphHash, /^graph:[0-9a-f]{16}$/, "package test graph hash");
assert.equal(packageTest.graph.lowering, "direct-program-graph", "package test graph lowering");
assert.equal(packageTest.testBackend, "direct-program-graph", "graph test backend");
assert.equal(packageTest.testDiscovery.mode, "package-graph", "package tests should report graph discovery");
assert.equal(packageTest.generatedCBytes, 0, "source graph tests should not use generated C");
assert.equal(packageTest.cBridgeFallback, false, "source graph tests should not use C bridge fallback");
}
async function assertPatchRecomputesNestedSymbolOwners() {
const graphPath = `${outDir}/reordered-owner.program-graph`;
const patchPath = `${outDir}/reordered-owner.patch`;
const patchedPath = `${outDir}/reordered-owner.patched.program-graph`;
const baseNode = {
value: "",
path: `${outDir}/reordered-owner.0`,
line: 1,
column: 1,
public: false,
mutable: false,
static: false,
fallible: false,
exportC: false,
};
const graph = finalizeGraphIdentities({
schemaVersion: 1,
moduleIdentity: "module:hello",
nodes: [
{ ...baseNode, id: "#param_p", kind: "Param", name: "world", type: "World", column: 13 },
{ ...baseNode, id: "#type_w", kind: "TypeRef", name: "", type: "World", column: 20 },
{ ...baseNode, id: "#type_v", kind: "TypeRef", name: "", type: "Void", column: 30 },
{ ...baseNode, id: "#block_b", kind: "Block", name: "body", type: "" },
{ ...baseNode, id: "#mod_m", kind: "Module", name: "hello", type: "" },
{ ...baseNode, id: "#decl_f", kind: "Function", name: "main", type: "Void", public: true },
],
edges: [
{ from: "#decl_f", kind: "param", to: "#param_p", target: "node", order: 0 },
{ from: "#param_p", kind: "type", to: "#type_w", target: "node", order: 0 },
{ from: "#decl_f", kind: "returnType", to: "#type_v", target: "node", order: 0 },
{ from: "#decl_f", kind: "body", to: "#block_b", target: "node", order: 0 },
{ from: "#mod_m", kind: "function", to: "#decl_f", target: "node", order: 0 },
],
});
await writeFile(graphPath, graphDumpText(graph));
const before = await zeroJson(["source-map", "--json", graphPath]);
const beforeParam = before.mappings.find((item) => item.nodeId === "#param_p");
assert.equal(beforeParam?.symbolId, "symbol:hello::value.main/param.world", "reordered graph starts with nested owner symbol");
await writeFile(patchPath, [
"zero-program-graph-patch v1",
`expect graphHash "${graph.graphHash}"`,
'set node="#decl_f" field="name" expect="main" value="entry"',
"",
].join("\n"));
const patch = await zeroJson(["patch", "--json", "--out", patchedPath, graphPath, patchPath]);
assert.equal(patch.ok, true, "patch should save a reordered graph after owner rename");
const validate = await zeroJson(["validate", "--json", patchedPath]);
assert.equal(validate.ok, true, "patched reordered graph should read back with matching identities");
const after = await zeroJson(["source-map", "--json", patchedPath]);
const afterParam = after.mappings.find((item) => item.nodeId === "#param_p");
assert.equal(afterParam?.symbolId, "symbol:hello::value.entry/param.world", "nested symbol owner should be recomputed after owner rename");
}
async function assertGraphSidecarPatchParity() {
const fixture = `${outDir}/graph-sidecar-patch.0`;
const original = await readFile("examples/hello.0", "utf8");
await writeFile(fixture, original);
const sidecar = await importProjectionSidecar(fixture);
const beforeGraph = await zeroJson(["dump", "--json", fixture]);
const beforeSidecar = await zeroJson(["validate", "--json", sidecar]);
assert.equal(beforeGraph.canonicalSource, false, "projection handle should read the graph sidecar for graph queries");
assert.equal(beforeGraph.graphHash, beforeSidecar.graphHash, "projection handle should match the graph sidecar");
assert.equal(beforeGraph.validation.state, "shape-valid", "sidecar graph should be shape-valid");
const literal = findStringLiteral(beforeGraph, "hello from zero\n");
const patch = await zeroJson([
"patch",
"--json",
"--out",
sidecar,
sidecar,
"--expect-graph-hash",
beforeGraph.graphHash,
"--op",
`set node="${literal.id}" field="value" expect="hello from zero\\n" value="hello sidecar graph\\n"`,
]);
assert.equal(patch.ok, true, "graph sidecar patch should succeed");
assert.equal(patch.canonicalSource, false, "graph sidecar patch should report graph input");
assert.equal(patch.saved.path, sidecar, "graph sidecar patch should save to the graph sidecar");
assert.equal(patch.originalGraphHash, beforeGraph.graphHash, "graph sidecar patch should check the expected graph hash");
assert.match(patch.patchedGraphHash, /^graph:[0-9a-f]{16}$/, "graph sidecar patch should report a graph hash");
assert.notEqual(patch.patchedGraphHash, beforeGraph.graphHash, "graph sidecar patch should change graph hash");
assert.equal(patch.operationCount, 1, "graph sidecar patch should report one operation");
assert.equal(patch.operations[0].ok, true, "graph sidecar patch operation should pass");
assert.equal(patch.operations[0].node, literal.id, "graph sidecar patch should target the requested node");
await zeroText(["view", "--out", fixture, sidecar]);
const patchedSource = await readFile(fixture, "utf8");
assert.match(patchedSource, /hello sidecar graph\\n/, "graph sidecar patch should export updated source text");
assert.equal(await zeroText(["check", sidecar]), "ok\n", "patched sidecar should check through first-class check command");
const artifact = await dumpGraphArtifact(fixture, "graph-sidecar-patch-run");
const sourceOut = `${outDir}/graph-sidecar-patch.source-run`;
const graphOut = `${outDir}/graph-sidecar-patch.graph-run`;
const source = await zeroText(["run", "--out", sourceOut, sidecar]);
const graph = await zeroText(["run", "--out", graphOut, artifact]);
assert.equal(source, "hello sidecar graph\n", "patched projection handle run output");
assert.equal(graph, source, "patched graph artifact run output should match source");
}
async function assertGraphPatchPreservesNodeIds() {
const artifact = await dumpGraphArtifact("examples/hello.0", "patch-preserves-id");
const patchedArtifact = `${outDir}/patch-preserves-id.patched.program-graph`;
const beforeGraph = await zeroJson(["dump", "--json", "examples/hello.0"]);
const beforeLiteral = findStringLiteral(beforeGraph, "hello from zero\n");
const beforeMain = beforeGraph.nodes.find((node) => node.kind === "Function" && node.name === "main");
assert(beforeMain, "missing main function");
const patch = await zeroJson([
"patch",
"--json",
"--out",
patchedArtifact,
artifact,
"--expect-graph-hash",
beforeGraph.graphHash,
"--op",
`replace node="${beforeLiteral.id}" expect="${beforeLiteral.nodeHash}" kind="Literal" type="String" value="hello preserved\\n"`,
]);
assert.equal(patch.ok, true, "graph artifact patch should succeed");
assert.equal(patch.operations[0].node, beforeLiteral.id, "graph patch should target the inspected literal ID");
assert.notEqual(patch.patchedGraphHash, beforeGraph.graphHash, "graph patch should update graphHash");
const patchedText = await readFile(patchedArtifact, "utf8");
assert.match(patchedText, new RegExp(`node ${beforeLiteral.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} Literal[^\\n]*value:"hello preserved\\\\n"`), "graph patch should preserve literal node ID");
assert.match(patchedText, new RegExp(`node ${beforeMain.id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")} Function[^\\n]*name:"main"`), "graph patch should not churn unrelated function ID");
assert.equal(await zeroText(["check", patchedArtifact]), "ok\n", "patched graph artifact should check");
}
function findStringLiteral(graph, value) {
const literal = graph.nodes.find((node) => node.kind === "Literal" && node.type === "String" && node.value === value);
assert(literal, `missing string literal ${JSON.stringify(value)}`);
return literal;
}
function findNodeById(graph, id) {
const node = graph.nodes.find((item) => item.id === id);
assert(node, `missing graph node ${id}`);
return node;
}
function assertMissingNodeId(graph, id, message) {
assert(!graph.nodes.some((item) => item.id === id), message);
}
function findOwnerNode(graph, nodeId, edgeKind) {
const edge = graph.edges.find((item) => item.target === "node" && item.to === nodeId && item.kind === edgeKind);
assert(edge, `missing ${edgeKind} owner edge for ${nodeId}`);
return findNodeById(graph, edge.from);
}
function findCheckForStringLiteral(graph, value) {
const literal = findStringLiteral(graph, value);
const call = findOwnerNode(graph, literal.id, "arg");
const check = findOwnerNode(graph, call.id, "expr");
assert.equal(check.kind, "Check", `expected check owner for ${JSON.stringify(value)}`);
return { check, literal };
}
function findNodeByKindAndName(graph, kind, name) {
const node = graph.nodes.find((item) => item.kind === kind && item.name === name);
assert(node, `missing ${kind} node ${name}`);
return node;
}
async function assertDeclarationSiblingIdentity() {
const declarationFixture = `${outDir}/identity-declarations.0`;
const declarations = [
"type Point {",
" x: i32,",
"}",
"",
"type Other {",
" y: i32,",
"}",
"",
"pub fn main() -> Void {",
" let point: Point = Point { x: 1 }",
" expect point.x == 1",
"}",
"",
].join("\n");
await writeFile(declarationFixture, declarations);
await importProjectionSidecar(declarationFixture);
const beforeDeclarations = await zeroJson(["dump", "--json", declarationFixture]);
const beforePoint = findNodeByKindAndName(beforeDeclarations, "Shape", "Point");
const beforeOther = findNodeByKindAndName(beforeDeclarations, "Shape", "Other");
await writeFile(declarationFixture, declarations.replace("type Point", "type Added {\n z: i32,\n}\n\ntype Point"));
await importProjectionSidecar(declarationFixture);
const prependedDeclarations = await zeroJson(["dump", "--json", declarationFixture]);
assert.equal(findNodeByKindAndName(prependedDeclarations, "Shape", "Point").id, beforePoint.id, "prepending a declaration should not churn existing shape IDs");
assert.equal(findNodeByKindAndName(prependedDeclarations, "Shape", "Other").id, beforeOther.id, "prepending a declaration should not churn later shape IDs");
const methodFixture = `${outDir}/identity-methods.0`;
const methods = [
"type Counter {",
" value: i32,",
" fn read(self: ref<Self>) -> i32 {",
" return self.value",
" }",
" fn done(self: ref<Self>) -> Bool {",
" return true",
" }",
"}",
"",
"pub fn main() -> Void {",
" let counter: Counter = Counter { value: 42 }",
" expect Counter.read(&counter) == 42",
"}",
"",
].join("\n");
await writeFile(methodFixture, methods);
await importProjectionSidecar(methodFixture);
const beforeMethods = await zeroJson(["dump", "--json", methodFixture]);
const beforeRead = findNodeByKindAndName(beforeMethods, "Function", "read");
const beforeDone = findNodeByKindAndName(beforeMethods, "Function", "done");
await writeFile(methodFixture, methods.replace(" fn read", " fn zero(self: ref<Self>) -> u32 {\n return 0_u32\n }\n fn read"));
await importProjectionSidecar(methodFixture);
const prependedMethods = await zeroJson(["dump", "--json", methodFixture]);
assert.equal(findNodeByKindAndName(prependedMethods, "Function", "read").id, beforeRead.id, "prepending a distinct method should not churn existing method IDs");
assert.equal(findNodeByKindAndName(prependedMethods, "Function", "done").id, beforeDone.id, "prepending a distinct method should not churn later method IDs");
await writeFile(methodFixture, methods.replace(" fn read", " fn count(self: ref<Self>) -> i32 {\n return 1\n }\n fn read"));
await importProjectionSidecar(methodFixture);
const sameShapeMethods = await zeroJson(["dump", "--json", methodFixture]);
const sameShapeRead = findNodeByKindAndName(sameShapeMethods, "Function", "read");
const countMethod = findNodeByKindAndName(sameShapeMethods, "Function", "count");
assert.notEqual(sameShapeRead.id, beforeRead.id, "same-shape method collision should retire the old ambiguous method ID");
assert.notEqual(countMethod.id, beforeRead.id, "prepended same-shape method should not steal the old method ID");
assertMissingNodeId(sameShapeMethods, beforeRead.id, "old ambiguous method ID should not target any same-shape method");
}
async function assertSourceEditIdentityBaseline() {
const fixture = `${outDir}/identity-edit.0`;
const original = [
"fn helper() -> i32 {",
" return 1",
"}",
"",
"pub fn main(world: World) -> Void raises {",
" check world.out.write(\"hello from zero\\n\")",
"}",
"",
].join("\n");
await writeFile(fixture, original);
await importProjectionSidecar(fixture);
const beforeGraph = await zeroJson(["dump", "--json", fixture]);
const before = findStringLiteral(beforeGraph, "hello from zero\n");
const beforeHelper = beforeGraph.nodes.find((node) => node.kind === "Function" && node.name === "helper");
const beforeCheck = beforeGraph.nodes.find((node) => node.kind === "Check");
assert(beforeHelper, "missing helper function before rename");
assert(beforeCheck, "missing check statement before insertion");
await writeFile(fixture, original.replace("hello from zero\\n", "hello from graph\\n"));
await importProjectionSidecar(fixture);
const afterGraph = await zeroJson(["dump", "--json", fixture]);
const after = findStringLiteral(afterGraph, "hello from graph\n");
assert.notEqual(after.nodeHash, before.nodeHash, "editing literal content should change nodeHash");
assert.notEqual(afterGraph.graphHash, beforeGraph.graphHash, "editing literal content should change graphHash");
assert.equal(after.id, before.id, "source-imported node id should survive a local content edit");
await writeFile(fixture, original.replace("fn helper", "fn renamedHelper"));
await importProjectionSidecar(fixture);
const renamedGraph = await zeroJson(["dump", "--json", fixture]);
const renamedHelper = renamedGraph.nodes.find((node) => node.kind === "Function" && node.name === "renamedHelper");
assert(renamedHelper, "missing renamed function");
assert.equal(renamedHelper.id, beforeHelper.id, "source-imported declaration id should survive a rename");
assert.notEqual(renamedHelper.nodeHash, beforeHelper.nodeHash, "renaming a declaration should change nodeHash");
assert.notEqual(renamedHelper.symbolId, beforeHelper.symbolId, "renaming a declaration should change symbolId");
if (requireStableNodeIds) assert.equal(renamedHelper.id, beforeHelper.id, "strict stable node id check");
await writeFile(fixture, original.replace("\npub fn main", "\nfn appendedHelper() -> i32 {\n return 2\n}\n\npub fn main"));
await importProjectionSidecar(fixture);
const sameShapeSiblingGraph = await zeroJson(["dump", "--json", fixture]);
const sameShapeHelper = sameShapeSiblingGraph.nodes.find((node) => node.kind === "Function" && node.name === "helper");
const insertedHelper = sameShapeSiblingGraph.nodes.find((node) => node.kind === "Function" && node.name === "appendedHelper");
assert(sameShapeHelper, "missing helper after same-shape sibling insertion");
assert(insertedHelper, "missing inserted same-shape sibling");
assert.notEqual(sameShapeHelper.id, beforeHelper.id, "same-shape sibling collision should retire the old ambiguous declaration ID");
assert.notEqual(insertedHelper.id, beforeHelper.id, "same-shape sibling should not steal the old declaration ID");
assertMissingNodeId(sameShapeSiblingGraph, beforeHelper.id, "old ambiguous declaration ID should not target any same-shape sibling");
await writeFile(fixture, original.replace("fn helper", "fn prependedHelper() -> i32 {\n return 2\n}\n\nfn helper"));
await importProjectionSidecar(fixture);
const prependedSiblingGraph = await zeroJson(["dump", "--json", fixture]);
const prependedExistingHelper = prependedSiblingGraph.nodes.find((node) => node.kind === "Function" && node.name === "helper");
const prependedHelper = prependedSiblingGraph.nodes.find((node) => node.kind === "Function" && node.name === "prependedHelper");
assert(prependedExistingHelper, "missing helper after prepended same-shape sibling");
assert(prependedHelper, "missing prepended same-shape sibling");
assert.notEqual(prependedExistingHelper.id, beforeHelper.id, "prepending a same-shape sibling should retire the old ambiguous declaration ID");
assert.notEqual(prependedHelper.id, beforeHelper.id, "prepended same-shape sibling should not steal the old declaration ID");
assertMissingNodeId(prependedSiblingGraph, beforeHelper.id, "old ambiguous declaration ID should not target any prepended sibling");
await writeFile(fixture, original.replace(" check world.out.write", " let marker: i32 = 1\n check world.out.write"));
await importProjectionSidecar(fixture);
const insertedGraph = await zeroJson(["dump", "--json", fixture]);
const insertedCheck = insertedGraph.nodes.find((node) => node.kind === "Check");
const insertedLiteral = findStringLiteral(insertedGraph, "hello from zero\n");
assert.equal(insertedCheck?.id, beforeCheck.id, "inserting before a statement should not churn the existing statement ID");
assert.equal(insertedLiteral.id, before.id, "inserting before a statement should not churn nested expression IDs");
await writeFile(fixture, original.replace(" check world.out.write(\"hello from zero\\n\")", " check world.out.write(\"hello from zero\\n\")\n check world.out.write(\"inserted\\n\")"));
await importProjectionSidecar(fixture);
const sameKindStatementGraph = await zeroJson(["dump", "--json", fixture]);
const sameKindExisting = findCheckForStringLiteral(sameKindStatementGraph, "hello from zero\n");
const sameKindInserted = findCheckForStringLiteral(sameKindStatementGraph, "inserted\n");
assert.notEqual(sameKindExisting.check.id, beforeCheck.id, "same-kind statement collision should retire the old ambiguous statement ID");
assert.notEqual(sameKindExisting.literal.id, before.id, "same-kind statement collision should retire nested ambiguous expression IDs");
assert.notEqual(sameKindInserted.check.id, beforeCheck.id, "appended same-kind statement should not steal the old statement ID");
assert.notEqual(sameKindInserted.literal.id, before.id, "appended same-kind expression should not steal the old expression ID");
assertMissingNodeId(sameKindStatementGraph, beforeCheck.id, "old ambiguous statement ID should not target any same-kind statement");
assertMissingNodeId(sameKindStatementGraph, before.id, "old ambiguous expression ID should not target any same-kind expression");
await writeFile(fixture, original.replace(" check world.out.write", " check world.out.write(\"inserted\\n\")\n check world.out.write"));
await importProjectionSidecar(fixture);
const prependedStatementGraph = await zeroJson(["dump", "--json", fixture]);
const prependedExisting = findCheckForStringLiteral(prependedStatementGraph, "hello from zero\n");
const prependedInserted = findCheckForStringLiteral(prependedStatementGraph, "inserted\n");
assert.notEqual(prependedExisting.check.id, beforeCheck.id, "prepending a same-kind statement should retire the old ambiguous statement ID");
assert.notEqual(prependedExisting.literal.id, before.id, "prepending a same-kind statement should retire nested ambiguous expression IDs");
assert.notEqual(prependedInserted.check.id, beforeCheck.id, "prepended same-kind statement should not steal the old statement ID");
assert.notEqual(prependedInserted.literal.id, before.id, "prepended same-kind expression should not steal the old expression ID");
assertMissingNodeId(prependedStatementGraph, beforeCheck.id, "old ambiguous statement ID should not target any prepended statement");
assertMissingNodeId(prependedStatementGraph, before.id, "old ambiguous expression ID should not target any prepended expression");
}
async function assertSourceEditReconcile() {
const fixture = `${outDir}/identity-reconcile.0`;
const original = [
"fn helper() -> i32 {",
" return 1",
"}",
"",
"pub fn main(world: World) -> Void raises {",
" check world.out.write(\"hello from zero\\n\")",
"}",
"",
].join("\n");
await writeFile(fixture, original);
const baseArtifact = await dumpGraphArtifact(fixture, "identity-reconcile-base");
await writeFile(fixture, original.replace("hello from zero\\n", "hello from graph\\n"));
const edited = await zeroJson(["reconcile", "--json", baseArtifact, "--source", fixture]);
assert.equal(edited.ok, true, "reconcile should accept an unambiguous literal edit");
assert.equal(edited.identity.edited > 0, true, "reconcile should report edited nodes");
assert.equal(edited.identity.ambiguous, 0, "literal edit should not be ambiguous");
assert.equal(edited.graphPatch.available, true, "literal edit should produce a graph patch");
assert.match(edited.graphPatch.text, /set node="#expr_[^"]+" field="value"/, "literal edit patch should target a graph node");
const literalDecision = edited.decisions.find((decision) => decision.status === "edited" && decision.kind === "Literal");
assert.deepEqual(literalDecision?.sourceRange.start, { line: 6, column: 27 }, "reconcile should map edited literal decisions to the source token");
assert.deepEqual(literalDecision?.sourceRange.end, { line: 6, column: 47 }, "reconcile should include the full edited literal token");
assert.equal(await zeroText(["reconcile", baseArtifact, "--source", fixture]), "program graph reconcile ok\n", "reconcile text output");
await writeFile(fixture, original.replace("\npub fn main", "\nfn appendedHelper() -> i32 {\n return 2\n}\n\npub fn main"));
const appended = await zeroJson(["reconcile", "--json", baseArtifact, "--source", fixture]);
assert.equal(appended.ok, true, "reconcile should accept unambiguous appended declarations");
assert.equal(appended.identity.ambiguous, 0, "named appended declaration should not be ambiguous");
assert.equal(appended.identity.inserted > 0, true, "reconcile should report inserted declaration nodes");
assert(appended.decisions.some((decision) => decision.status === "unchanged" && decision.kind === "Function" && decision.name === "helper"), "existing helper declaration should keep its identity");
assert(appended.decisions.some((decision) => decision.status === "inserted" && decision.kind === "Function" && decision.name === "appendedHelper"), "appended helper declaration should receive a new identity");
const copiedFixture = `${outDir}/identity-reconcile-copy.0`;
await writeFile(copiedFixture, original);
const moduleMismatch = await zeroJsonFailure(["reconcile", "--json", baseArtifact, "--source", copiedFixture]);
assert.equal(moduleMismatch.ok, false, "reconcile should reject different module identities");
assert.equal(moduleMismatch.identity.moduleIdentityChanged, true, "reconcile should flag module identity changes");
assert.equal(moduleMismatch.diagnostics[0].code, "GRC003", "reconcile should explain module identity mismatch");
}
try {
await mkdir(outDir, { recursive: true });
for (const fixture of [
"examples/hello.0",
"examples/type-alias.0",
"examples/static-interface.0",
"examples/direct-rescue-basic.0",
"examples/std-math.0",
"examples/systems-package",
"examples/readall-cli",
"examples/direct-package-arrays",
"conformance/check/pass/c-header-import.0",
"conformance/native/pass/borrow-field-independent-assignment.0",
"conformance/native/pass/open-ended-slices.0",
"conformance/native/pass/allocator-primitives.0",
"conformance/native/pass/std-fs-fallible.0",
"benchmarks/rosetta/fibonacci-sequence.0",
]) {
await assertCheckParity(fixture);
}
for (const fixture of [
"examples/hello.0",
"examples/type-alias.0",
"examples/static-interface.0",
"examples/direct-rescue-basic.0",
"examples/std-math.0",
"examples/systems-package",
"examples/readall-cli",
"conformance/check/pass/c-header-import.0",
"conformance/native/pass/borrow-field-independent-assignment.0",
"conformance/native/pass/open-ended-slices.0",
"conformance/native/pass/allocator-primitives.0",
"conformance/native/pass/std-fs-fallible.0",
"benchmarks/rosetta/fibonacci-sequence.0",
]) {
await assertRoundtripStable(fixture);
}
await assertCommandStateContracts();
await assertResolutionFacts();
await assertStdGraphDependencyMerge();
await assertSemanticFacts();
await assertUnconstrainedGenericTypeParams();
await assertGraphCommandCompilerPath();
await assertPatchRecomputesNestedSymbolOwners();
await assertBuildParity("examples/hello.0", "hello");
await assertRunParity("examples/hello.0", "hello");
await assertRunParity("conformance/native/pass/std-args.0", "std-args", ["alpha", "beta"]);
await assertTestParity("conformance/native/pass/test-blocks.0", "test-blocks");
await assertGraphSidecarPatchParity();
await assertGraphPatchPreservesNodeIds();
await assertSourceEditIdentityBaseline();
await assertSourceEditReconcile();
await assertDeclarationSiblingIdentity();
console.log("program graph parity ok");
} finally {
await rm(outDir, { force: true, recursive: true });
}