Files
stack/scripts/foundation/resolve.test.mjs
T
jason.woltje 8ebddd6f93 feat(foundation): offline synthetic scope/permission inspector (FI-FILBERT-8 APPROVED r6)
Rocko-authored, Filbert-reviewed inspector (r6 manifest
a4a44930...) with full review/build/verdict evidence under
docs/plans/reviews. 43/0 selftests, oracle zero-disagreement,
foundation checker PASS. Owner A9 acceptance recorded separately.
2026-09-07 14:06:35 -05:00

813 lines
54 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Unit tests for the pure evaluation core (charter §4, §7, §8, §10.4).
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync, readdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
import {
findCycle, deepEqual, isValidTime, isValidRelativePath,
intersectGrants, pathPermitted, layerFromRestrictions, UNRESTRICTED_LAYER, intersectLayers,
buildResult, exitFor, Refusal, RULES, REASONS, RESULTS, PROPOSAL_STEPS, SUPPORTED_OPERATIONS, OPERATION_CATALOG,
validateRecordShape, evaluate, inspect, DISCLAIMER, PREVIEW, RESULT_KEYS as IMPL_RESULT_KEYS,
PINNED_PATTERNS, pinnedPatternMatches, strictPatternMatches, stableOrder, orderingForm, ORDER_KEYS,
} from "./resolve.mjs";
import { parseStrict } from "./strict-json.mjs";
import { canonicalize, CanonicalError } from "./canonical.mjs";
import { createHash } from "node:crypto";
const HERE = dirname(fileURLToPath(import.meta.url));
const FIXTURES = join(HERE, "fixtures");
const CHARTER = join(HERE, "..", "..", "docs", "plans", "2026-09-06_foundation-inspector-charter.md");
const CHARTER_SHA256 = "19b6721128a627a2032ffdb95ece2d50abe69a8f6d521e9eff8bbdaff22798b6";
const INDEX = JSON.parse(readFileSync(join(FIXTURES, "index.json"), "utf8"));
function loadBundle(name) {
return parseStrict(new Uint8Array(readFileSync(join(FIXTURES, "bundles", `${name}.json`))));
}
const CLOSED_KEYS = new Set([
"disclaimer", "preview", "bundleVersion", "authentication", "declarations", "result", "reason", "rule",
"selection", "operation", "proposal", "diagnostic",
"agentId", "projectId", "workspaceId", "assignmentRef", "kind", "id", "scope", "revision",
"name", "target", "root", "path", "selectedAssignmentRef", "byteOffset", "inputPath",
]);
/** Every key at any depth that is not part of the closed result vocabulary (§7). */
function unknownKeys(value, out = []) {
if (Array.isArray(value)) { for (const v of value) unknownKeys(v, out); return out; }
if (value && typeof value === "object") {
for (const [k, v] of Object.entries(value)) { if (!CLOSED_KEYS.has(k)) out.push(k); unknownKeys(v, out); }
}
return out;
}
// Independent copy of the charter §7 closed field list (not the implementation export).
const RESULT_KEYS = [
"disclaimer", "preview", "bundleVersion", "authentication", "declarations", "result", "reason", "rule",
"selection", "operation", "proposal", "diagnostic",
];
/** The §7 "Closed result fields" bullet names parsed from the pinned charter text. */
function charterResultFields() {
const bytes = readFileSync(CHARTER);
const digest = createHash("sha256").update(bytes).digest("hex");
assert.equal(digest, CHARTER_SHA256, "charter candidate 3 must be the pinned text; rebind this test on amendment");
const text = bytes.toString("utf8");
const start = text.indexOf("Closed result fields, emitted on every outcome:");
assert.ok(start > 0, "charter §7 field list not found");
const block = text.slice(start, text.indexOf("\n\n", start));
return block.split("\n").map((l) => /^- ([a-zA-Z]+):/.exec(l)).filter(Boolean).map((m) => m[1]);
}
// --- graph -------------------------------------------------------------------------
test("findCycle: self-loop, pair, diamond, long chain without recursion", () => {
assert.equal(findCycle(new Map([["a", ["a"]]])), "a");
assert.equal(findCycle(new Map([["a", ["b"]], ["b", ["a"]]])), "a");
assert.equal(findCycle(new Map([["a", ["b", "c"]], ["b", ["d"]], ["c", ["d"]], ["d", []]])), null);
assert.equal(findCycle(new Map()), null);
assert.equal(findCycle(new Map([["a", ["missing"]]])), null);
const n = 20000;
const chain = new Map();
for (let i = 0; i < n; i += 1) chain.set(`n${String(i).padStart(6, "0")}`, [`n${String(i + 1).padStart(6, "0")}`]);
assert.equal(findCycle(chain), null);
chain.set(`n${String(n).padStart(6, "0")}`, ["n000000"]);
assert.equal(findCycle(chain), "n000000");
});
test("deepEqual: structural, key-order insensitive, type-strict", () => {
assert.ok(deepEqual({ a: [1, { b: null }] }, { a: [1, { b: null }] }));
assert.ok(deepEqual({ a: 1, b: 2 }, { b: 2, a: 1 }));
assert.ok(!deepEqual({ a: 1 }, { a: "1" }));
assert.ok(!deepEqual([1, 2], [2, 1]));
assert.ok(!deepEqual({ a: 1 }, { a: 1, b: undefined }));
assert.ok(!deepEqual(null, {}));
assert.ok(!deepEqual([], {}));
});
// --- formats (pinned checker semantics) -----------------------------------------------------
test("isValidTime follows the pinned checker's strptime round-trip", () => {
const cases = [
["2026-09-06T03:00:00.000Z", true], ["2026-02-30T00:00:00.000Z", false], ["2024-02-29T00:00:00.000Z", true],
["2023-02-29T00:00:00.000Z", false], ["2026-09-06T24:00:00.000Z", false], ["2026-09-06T03:00:60.000Z", false],
["2026-09-06T03:00:00Z", false], ["2026-09-06T03:00:00.0000Z", false], ["0000-01-01T00:00:00.000Z", false],
["9999-12-31T23:59:59.999Z", true], ["2026-09-06T03:00:00.000Z\n", false], ["2026-9-06T03:00:00.000Z", false],
// pinned checker (check.py strftime round-trip, glibc %Y unpadded below 1000): 0001..0999 refused
["0001-01-01T00:00:00.000Z", false], ["0999-12-31T00:00:00.000Z", false], ["1000-01-01T00:00:00.000Z", true],
["2026-09-06T03:00:00.000+00:00", false], ["2026-13-01T00:00:00.000Z", false], ["2100-02-29T00:00:00.000Z", false],
["2000-02-29T00:00:00.000Z", true], [null, false], [20260906, false],
];
for (const [value, expected] of cases) assert.equal(isValidTime(value), expected, JSON.stringify(value));
});
test("isValidRelativePath: pinned checker categories and byte bounds", () => {
const ok = ["src/main.mjs", "a", "a/b/c", "x".repeat(4096), "é".repeat(2048), "\u2028", "spa ce", "a.b", "...", "dot./x"];
const bad = ["", "/abs", "a//b", "./a", "a/../b", "a/.", "..", "a\\b", "a\u0001", "a\u200b", "\ud800", "a\u0085", "\u007f",
"x".repeat(4097), "é".repeat(2049), "\ufeffx", "a/", null, 5];
for (const v of ok) assert.ok(isValidRelativePath(v), JSON.stringify(v));
for (const v of bad) assert.ok(!isValidRelativePath(v), JSON.stringify(v));
});
// --- restrictions calculus ---------------------------------------------------------------
const ROOT = { root: "workspace", path: null };
const SRC = { root: "workspace", path: "src" };
const SRC_LIB = { root: "workspace", path: "src/lib" };
const DOCS = { root: "workspace", path: "docs" };
test("grant intersection keeps the narrower prefix and never widens", () => {
assert.deepEqual(intersectGrants([ROOT], [SRC]), [SRC]);
assert.deepEqual(intersectGrants([SRC], [ROOT]), [SRC]);
assert.deepEqual(intersectGrants([SRC], [SRC_LIB]), [SRC_LIB]);
assert.deepEqual(intersectGrants([SRC], [DOCS]), []);
assert.deepEqual(intersectGrants([SRC, DOCS], [ROOT]), [SRC, DOCS]);
assert.deepEqual(intersectGrants([], [ROOT]), []);
assert.deepEqual(intersectGrants([ROOT], [ROOT]), [ROOT]);
// segment prefixes, not string prefixes
assert.deepEqual(intersectGrants([SRC], [{ root: "workspace", path: "srcx" }]), []);
});
test("pathPermitted uses segment prefixes", () => {
assert.ok(pathPermitted("src/a.mjs", [SRC]));
assert.ok(pathPermitted("src", [SRC]));
assert.ok(!pathPermitted("srcx/a.mjs", [SRC]));
assert.ok(!pathPermitted("docs/a.md", [SRC]));
assert.ok(pathPermitted("anything/at/all", [ROOT]));
assert.ok(!pathPermitted("anything", []));
});
test("layer intersection narrows only; network none dominates; endpoints intersect structurally", () => {
const full = layerFromRestrictions({
operations: [...OPERATION_CATALOG], readPaths: [ROOT], writePaths: [ROOT], network: "none", endpointRefs: [],
});
assert.deepEqual(intersectLayers(UNRESTRICTED_LAYER, full), full);
assert.deepEqual(intersectLayers(full, UNRESTRICTED_LAYER), full);
const reader = layerFromRestrictions({ operations: ["work.read", "file.read"], readPaths: [SRC], writePaths: [], network: "none", endpointRefs: [] });
const both = intersectLayers(full, reader);
assert.deepEqual(both.operations, ["work.read", "file.read"]);
assert.deepEqual(both.readPaths, [SRC]);
assert.deepEqual(both.writePaths, []);
assert.equal(both.network, "none");
const ep1 = { registry: "endpoint", id: "e1", revision: 1, digest: `sha256:${"0".repeat(64)}` };
const ep2 = { ...ep1, id: "e2" };
const netA = { ...UNRESTRICTED_LAYER, network: "approved-endpoints", endpointRefs: [ep1, ep2] };
const netB = { ...UNRESTRICTED_LAYER, network: "approved-endpoints", endpointRefs: [{ ...ep2 }] };
const netC = { ...UNRESTRICTED_LAYER, network: "approved-endpoints", endpointRefs: [{ ...ep1, revision: 2 }] };
assert.deepEqual(intersectLayers(netA, netB), { ...UNRESTRICTED_LAYER, network: "approved-endpoints", endpointRefs: [ep2] });
assert.deepEqual(intersectLayers(netA, netC), { ...UNRESTRICTED_LAYER, network: "none", endpointRefs: [] });
assert.deepEqual(intersectLayers(netA, { ...UNRESTRICTED_LAYER, network: "none", endpointRefs: [] }), { ...UNRESTRICTED_LAYER, network: "none", endpointRefs: [] });
// delegated operation set narrows without touching paths
const delegated = { ...UNRESTRICTED_LAYER, operations: ["assignment.change"] };
const narrowed = intersectLayers(full, delegated);
assert.deepEqual(narrowed.operations, ["assignment.change"]);
assert.deepEqual(narrowed.readPaths, [ROOT]);
});
// --- closed vocabulary ---------------------------------------------------------------------
test("vocabularies are closed and consistent with the fixture index", () => {
assert.deepEqual([...RESULTS].sort(), ["allowed", "invalid", "refused", "unresolved"]);
assert.deepEqual(SUPPORTED_OPERATIONS, ["work.read", "file.read", "file.change", "assignment.change"]);
assert.equal(new Set(RULES).size, RULES.length);
for (const [step, outcome] of Object.entries(PROPOSAL_STEPS)) {
assert.ok(RULES.includes(step), `proposal step ${step} is a rule`);
assert.ok(["refused", "unresolved"].includes(outcome));
}
assert.equal(PROPOSAL_STEPS["runtime-reconciliation-required"], "unresolved");
const used = new Set();
for (const c of INDEX.cases) {
assert.ok(RESULTS.includes(c.expect.result), c.name);
assert.ok(REASONS.includes(c.expect.reason), c.name);
if (c.expect.rule !== null) { assert.ok(RULES.includes(c.expect.rule), `${c.name} rule ${c.expect.rule}`); used.add(c.expect.rule); }
if (c.expect.proposalRule !== null) assert.ok(RULES.includes(c.expect.proposalRule), c.name);
}
// Every stage-2..5 rule of the closed vocabulary is exercised by at least one fixture,
// except CLI/I-O rules, which cli.test.mjs covers with a synthetic sandbox.
const cliOnly = new Set(["usage-invalid", "open-flags-unavailable", "input-open-failed", "input-not-regular", "input-size-changed"]);
const unexercised = RULES.filter((r) => !used.has(r) && !cliOnly.has(r) && !(r in PROPOSAL_STEPS));
assert.deepEqual(unexercised, [], "rules without a fixture");
});
test("closed result fields equal the pinned charter §7 list; exit is process metadata, not a field", () => {
const fromCharter = charterResultFields();
assert.deepEqual(fromCharter, RESULT_KEYS, "test copy must equal the charter text");
assert.deepEqual([...IMPL_RESULT_KEYS], fromCharter, "implementation export must equal the charter text");
assert.ok(!fromCharter.includes("exit"));
const r = buildResult({ exit: 0, reason: "allowed", rule: null, selection: null, operation: null, proposal: null, diagnostic: null });
assert.deepEqual(Object.keys(r), fromCharter);
assert.ok(!("exit" in r));
assert.equal(exitFor(r), 0);
assert.equal(exitFor({ result: "refused", reason: "not-authorized" }), 3);
assert.equal(exitFor({ result: "unresolved", reason: "unknown-effects" }), 3);
assert.equal(exitFor({ result: "invalid", reason: "invalid-request" }), 2);
assert.equal(exitFor({ result: "invalid", reason: "missing-state" }), 2);
assert.equal(exitFor({ result: "invalid", reason: "io-failure" }), 4);
for (const [exit, reason] of [[3, "allowed"], [0, "not-authorized"], [2, "io-failure"], [4, "invalid-request"]]) {
assert.throws(() => buildResult({ exit, reason, rule: null, selection: null, operation: null, proposal: null, diagnostic: null }), /inconsistent/, `${exit}/${reason}`);
}
});
test("buildResult: fixed key order, derived result, closed shape", () => {
const r = buildResult({ exit: 0, reason: "allowed", rule: null, selection: null, operation: null, proposal: null, diagnostic: null });
assert.deepEqual(Object.keys(r), RESULT_KEYS);
assert.equal(r.disclaimer, DISCLAIMER);
assert.equal(r.preview, PREVIEW);
assert.equal(r.bundleVersion, 1);
assert.equal(r.authentication, "not-modelled");
assert.equal(r.declarations, "unverified-simulation");
assert.equal(r.result, "allowed");
assert.equal(buildResult({ exit: 3, reason: "not-authorized", rule: "operation-not-permitted", selection: null, operation: null, proposal: null, diagnostic: null }).result, "refused");
assert.equal(buildResult({ exit: 3, reason: "unknown-effects", rule: "runtime-reconciliation-required", selection: null, operation: null, proposal: null, diagnostic: null }).result, "unresolved");
assert.equal(buildResult({ exit: 2, reason: "invalid-request", rule: "usage-invalid", selection: null, operation: null, proposal: null, diagnostic: null }).result, "invalid");
assert.equal(buildResult({ exit: 4, reason: "io-failure", rule: "input-open-failed", selection: null, operation: null, proposal: null, diagnostic: { byteOffset: null, inputPath: "x" } }).result, "invalid");
const d = buildResult({ exit: 2, reason: "invalid-request", rule: "input-parse-failed", selection: null, operation: null, proposal: null, diagnostic: { byteOffset: 7 } });
assert.deepEqual(d.diagnostic, { byteOffset: 7, inputPath: null });
assert.throws(() => buildResult({ exit: 3, reason: "not-authorized", rule: "made-up", selection: null, operation: null, proposal: null, diagnostic: null }), /unknown rule/);
assert.throws(() => buildResult({ exit: 3, reason: "made-up", rule: null, selection: null, operation: null, proposal: null, diagnostic: null }), /unknown reason/);
});
test("Refusal rejects unknown rules and reasons at construction", () => {
assert.throws(() => new Refusal(3, "not-authorized", "no-such-rule", null), /unknown rule/);
assert.throws(() => new Refusal(3, "no-such-reason", "operation-not-permitted", null), /unknown reason/);
const r = new Refusal(3, "not-authorized", "operation-not-permitted", "bundle.operation");
assert.equal(r.detail, "bundle.operation");
assert.equal(r.step, null);
});
// --- record shape ----------------------------------------------------------------------------
test("validateRecordShape refuses unsupported kinds at the kind gate without judging payloads", () => {
const base = loadBundle("demo-read-w1");
const project = base.records.find((r) => r.kind === "project");
assert.equal(validateRecordShape(project).verdict, "valid");
const alien = { ...project, kind: "session", payload: 42 };
const v = validateRecordShape(alien);
assert.equal(v.verdict, "unsupported-kind");
assert.equal(v.rule, "record-kind-unsupported");
assert.equal(validateRecordShape({ ...project, kind: "nonsense" }).verdict, "invalid");
assert.equal(validateRecordShape(null).verdict, "invalid");
});
// --- evaluate over fixtures ---------------------------------------------------------------------
test("§10.4 precedence: cycle rules fire before parent-scope and predecessor-continuity refusals", () => {
assert.equal(evaluate(loadBundle("cycle-mission-self-parent")).result.rule, "mission-parent-cycle");
assert.equal(evaluate(loadBundle("cycle-supersedes-self-rev2")).result.rule, "supersedes-cycle");
});
test("evaluate over every bundle fixture: closed result, no locator leakage, deterministic", () => {
const names = readdirSync(join(FIXTURES, "bundles")).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -5));
assert.equal(names.length, INDEX.cases.filter((c) => c.raw === false).length);
for (const name of names) {
const expect = INDEX.cases.find((c) => c.name === name).expect;
const bundle = loadBundle(name);
const { result, exit, detail } = evaluate(bundle);
assert.deepEqual(Object.keys(result), RESULT_KEYS, name);
assert.ok(!("exit" in result), name);
assert.equal(exit, expect.exit, name);
assert.equal(exitFor(result), expect.exit, name);
assert.equal(result.result, expect.result, name);
assert.equal(result.reason, expect.reason, name);
assert.equal(result.rule, expect.rule, name);
assert.equal(result.proposal === null ? null : result.proposal.rule, expect.proposalRule, name);
assert.equal(result.diagnostic, null, name);
assert.ok(!("detail" in result), name);
if (detail !== null) assert.equal(typeof detail, "string", name);
assert.deepEqual(unknownKeys(result), [], `${name}: keys outside the closed result vocabulary`);
assert.deepEqual(inspect(loadBundle(name)), result, `${name}: nondeterministic`);
if (result.proposal !== null) {
assert.equal(result.proposal.result, PROPOSAL_STEPS[result.proposal.rule], name);
assert.equal(result.result, result.proposal.result, name);
}
if (exit === 0) {
assert.equal(result.rule, null, name);
assert.notEqual(result.selection, null, name);
assert.notEqual(result.operation, null, name);
}
}
});
test("evaluate never mutates its input", () => {
const bundle = loadBundle("demo-change-w1");
const before = JSON.stringify(bundle);
evaluate(bundle);
assert.equal(JSON.stringify(bundle), before);
});
// --- static import boundary (bounded evidence, not a sandbox) ------------------------------------
function importsOf(source) {
return [...source.matchAll(/^import\s[^;]*?from\s+"([^"]+)";?$/gm)].map((m) => m[1]);
}
function codeOnly(source) {
// strip block and line comments before token scanning
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/(^|[^:])\/\/.*$/gm, "$1");
}
const FORBIDDEN_PURE = /\b(process|Date|fetch|require|globalThis|setTimeout|setInterval|queueMicrotask|Intl|performance|Worker|Atomics|SharedArrayBuffer|WebAssembly)\b|Math\.random|import\s*\(/;
test("pure modules import only node:crypto and each other, and contain no ambient-capability tokens", () => {
const allowed = new Set(["node:crypto", "./canonical.mjs", "./strict-json.mjs"]);
for (const file of ["strict-json.mjs", "canonical.mjs", "resolve.mjs"]) {
const source = readFileSync(join(HERE, file), "utf8");
for (const spec of importsOf(source)) assert.ok(allowed.has(spec), `${file} imports ${spec}`);
const code = codeOnly(source);
const hit = FORBIDDEN_PURE.exec(code);
assert.equal(hit, null, `${file} contains ${hit && hit[0]}`);
assert.ok(!/\bnode:(fs|child_process|net|http|https|os|dns|tls|worker_threads|vm|readline|cluster|dgram)\b/.test(code), file);
}
});
test("CLI imports only node:fs/url/path and local modules; no process spawning or network modules", () => {
const source = readFileSync(join(HERE, "..", "foundation-inspect.mjs"), "utf8");
const allowed = new Set(["node:fs", "node:url", "node:path", "./foundation/strict-json.mjs", "./foundation/resolve.mjs"]);
const specs = importsOf(source);
assert.ok(specs.length >= 4);
for (const spec of specs) assert.ok(allowed.has(spec), `CLI imports ${spec}`);
const code = codeOnly(source);
assert.ok(!/\b(child_process|node:net|node:http|node:https|node:os|node:dns|node:tls|worker_threads|node:vm|fetch|require|Date|globalThis|setTimeout|readdirSync|readFileSync|writeFileSync|homedir|tmpdir)\b/.test(code));
assert.ok(!/process\.env|process\.cwd|import\s*\(|process\.chdir|process\.stdin/.test(code));
// Only stdout write, exitCode assignment and argv read are ambient uses of `process`.
const uses = [...code.matchAll(/process\.(\w+)/g)].map((m) => m[1]);
assert.deepEqual([...new Set(uses)].sort(), ["argv", "exitCode", "stdout"]);
});
// --- schema column vs strict profile (addendum FI-C2-1; verdict FI-FILBERT-5) -------------
const REVIEWS = join(HERE, "..", "..", "docs", "plans", "reviews");
const ADDENDUM = join(REVIEWS, "2026-09-06_foundation-inspector-pattern-profile-addendum.md");
const ADDENDUM_SHA256 = "afe2980be2f91e701dae5af3018831ac5c300474f52bcc06e740ce5b5cc68ca5";
const VERDICT = join(REVIEWS, "2026-09-06_foundation-inspector-pattern-profile-verdict.md");
const VERDICT_SHA256 = "03c979b77cc6b03b6685ba51ed1ce24c3f1d7b274ce8f772263a103670050da7";
test("the unchanged charter, the exact addendum and its verdict are bound; the profile rule is in the closed vocabulary", () => {
const sha = (file) => createHash("sha256").update(readFileSync(file)).digest("hex");
assert.equal(sha(CHARTER), CHARTER_SHA256, "charter candidate 3 unchanged");
assert.equal(sha(ADDENDUM), ADDENDUM_SHA256, "addendum FI-C2-1 must be the exact reviewed text; rebind on amendment");
assert.equal(sha(VERDICT), VERDICT_SHA256, "verdict FI-FILBERT-5 must be the exact text; rebind on amendment");
assert.ok(readFileSync(ADDENDUM, "utf8").includes("profile-pattern-mismatch"), "addendum names the fixed profile rule");
assert.ok(RULES.includes("profile-pattern-mismatch"));
assert.ok(RULES.indexOf("profile-pattern-mismatch") < RULES.indexOf("duplicate-record-identity"), "stage-2 rule, before identity");
});
const SAMPLES = { id: "task-w1", runtimeId: "00000000-0000-4000-8000-000000000001", digest: `sha256:${"0".repeat(64)}` };
const NEWLINE_MATRIX = [ // label, mutation, pinned-schema verdict, strict-profile verdict (FI-FILBERT-5 table)
["valid", (v) => v, true, true],
["one-final-lf", (v) => `${v}\n`, true, false],
["two-final-lf", (v) => `${v}\n\n`, false, false],
["crlf", (v) => `${v}\r\n`, false, false],
["cr", (v) => `${v}\r`, false, false],
["u2028", (v) => `${v}\u2028`, false, false],
["u2029", (v) => `${v}\u2029`, false, false],
["interior-lf", (v) => `${v.slice(0, 3)}\n${v.slice(3)}`, false, false],
];
test("schema column reproduces the pinned checker's end-anchor semantics; the profile column is strict", () => {
assert.deepEqual(Object.keys(PINNED_PATTERNS), ["id", "runtimeId", "digest"]);
for (const [family, sample] of Object.entries(SAMPLES)) {
const re = PINNED_PATTERNS[family];
assert.ok(re.source.endsWith("$") && !re.flags.includes("m"), `${family} is end-anchored without multiline`);
for (const [label, mutate, schema, profile] of NEWLINE_MATRIX) {
const v = mutate(sample);
assert.equal(pinnedPatternMatches(re, v), schema, `${family}/${label} schema column`);
assert.equal(strictPatternMatches(re, v), profile, `${family}/${label} profile column`);
assert.ok(schema || !profile, "profile-valid implies schema-valid");
}
}
});
test("validateRecordShape reports schema and profile verdicts independently, in validation order, without altering the value", () => {
const base = loadBundle("demo-read-w1").records.find((r) => r.kind === "task" && r.id === "task-w1");
const clone = () => JSON.parse(JSON.stringify(base));
assert.deepEqual(validateRecordShape(clone()), { verdict: "valid", profileViolations: [] });
const one = clone(); one.id = `${one.id}\n`;
const v1 = validateRecordShape(one);
assert.deepEqual(v1, { verdict: "valid", profileViolations: ["record.id"] });
assert.equal(one.id, `${base.id}\n`, "no trimming or normalization");
const two = clone(); two.id = `${two.id}\n\n`;
assert.deepEqual(validateRecordShape(two), { verdict: "invalid", rule: "shape-pattern-mismatch", path: "record.id" });
const many = clone();
many.id = `${many.id}\n`; many.scope.projectId = `${many.scope.projectId}\n`; many.authorizationRef = `${many.authorizationRef}\n`;
many.payload.dependencies[0].id = `${many.payload.dependencies[0].id}\n`;
assert.deepEqual(validateRecordShape(many).profileViolations,
["record.id", "record.scope.projectId", "record.authorizationRef", "record.payload.dependencies[0].id"]);
const text = clone(); text.payload.purpose = "line one\nline two\r\n";
assert.deepEqual(validateRecordShape(text), { verdict: "valid", profileViolations: [] }, "free-form text is not a typed grammar");
const time = clone(); time.createdAt = `${time.createdAt}\n`;
assert.deepEqual(validateRecordShape(time), { verdict: "invalid", rule: "shape-pattern-mismatch", path: "record.createdAt" });
});
test("profile refusal happens before identity lookup and is distinguishable from a missing reference", () => {
const missing = evaluate(loadBundle("ref-missing-dependency"));
assert.equal(missing.result.rule, "record-reference-missing");
const lf = loadBundle("profile-record-dependency-ref-id-dangling-one-final-lf");
const r = evaluate(lf);
assert.equal(r.exit, 2);
assert.equal(r.result.result, "invalid");
assert.equal(r.result.reason, "invalid-request");
assert.equal(r.result.rule, "profile-pattern-mismatch");
assert.equal(r.result.selection, null, "safe pre-admission output");
assert.equal(r.result.operation, null);
assert.equal(r.result.proposal, null);
assert.equal(r.result.diagnostic, null);
assert.deepEqual(Object.keys(r.result), [...IMPL_RESULT_KEYS], "closed fields unchanged");
assert.ok(/^bundle\.records\[\d+\]\.payload\.dependencies\[0\]\.id$/.test(r.detail), "internal locator is the path, not the value");
assert.ok(!JSON.stringify(r.result).includes("task-nope"), "the value never enters the result");
// Every profile fixture: selection/operation withheld, no typed value with a newline echoed.
for (const c of INDEX.cases.filter((x) => x.expect.rule === "profile-pattern-mismatch")) {
const out = evaluate(loadBundle(c.name));
assert.equal(out.exit, 2, c.name);
assert.equal(out.result.selection, null, c.name);
assert.equal(out.result.operation, null, c.name);
assert.ok(!JSON.stringify(out.result).includes("\\n"), `${c.name}: newline-bearing value echoed`);
}
assert.ok(INDEX.cases.filter((x) => x.expect.rule === "profile-pattern-mismatch").length >= 30);
});
// --- FI-FILBERT-6 corrections (F1F5): witness recipes as regression tests -------------------
function firstFailure(bundle) {
const { result, exit } = evaluate(bundle);
return { exit, result: result.result, reason: result.reason, rule: result.rule, step: result.proposal === null ? null : result.proposal.rule };
}
test("F1: consulted registry references resolve by exact four-field declaration at the admission stage", () => {
assert.ok(RULES.includes("registry-declaration-missing"));
const missing = { exit: 3, result: "refused", reason: "missing-state", rule: "registry-declaration-missing", step: null };
for (const name of [
"adm-registry-declaration-missing-agent-refs", "adm-registry-declaration-missing-settings", "adm-registry-declaration-missing-soul",
"adm-registry-declaration-digest-mismatch", "adm-registry-declaration-revision-mismatch",
"adm-registry-declaration-missing-instruction", "adm-registry-declaration-missing-skill",
"adm-registry-declaration-missing-endpoint-execution", "adm-registry-declaration-missing-endpoint-task",
]) assert.deepEqual(firstFailure(loadBundle(name)), missing, name);
// never the ordinary-record structural rule, never an allowed preview
const b = loadBundle("demo-read-w1");
b.registries = b.registries.filter((e) => e.registry !== "harness");
const r = firstFailure(b);
assert.notEqual(r.rule, "record-reference-missing");
assert.equal(r.exit, 3);
// positive controls: declared endpoint, unconsulted record's dangling reference
assert.equal(firstFailure(loadBundle("adm-registry-declaration-endpoint-declared")).exit, 0);
assert.equal(firstFailure(loadBundle("adm-registry-declaration-unselected-agent-ignored")).exit, 0);
});
test("F1: consulted dependency and mission authorizations must be declared", () => {
const undeclared = { exit: 3, result: "refused", reason: "missing-state", rule: "authorization-undeclared", step: null };
assert.deepEqual(firstFailure(loadBundle("adm-dependency-authorization-undeclared")), undeclared);
assert.deepEqual(firstFailure(loadBundle("adm-mission-authorization-undeclared")), undeclared);
assert.equal(firstFailure(loadBundle("adm-unconsulted-authorization-ignored")).exit, 0);
assert.deepEqual(firstFailure(loadBundle("prop-subject-task-authorization-undeclared")), { ...undeclared, step: "intent-not-current" });
});
test("F2: a bound task is consulted work; work.read is required even without mission/dependency references", () => {
const notReadable = { exit: 3, result: "refused", reason: "not-authorized", rule: "consulted-work-not-readable", step: null };
assert.deepEqual(firstFailure(loadBundle("adm-assigned-task-without-work-read")), notReadable);
assert.deepEqual(firstFailure(loadBundle("adm-assigned-task-without-work-read-read-op")), notReadable);
assert.equal(firstFailure(loadBundle("assigned-task-only-with-work-read")).exit, 0);
// genuinely taskless reads keep skipping the assignment layer
assert.equal(firstFailure(loadBundle("taskless-work-read")).exit, 0);
assert.equal(firstFailure(loadBundle("taskless-file-read")).exit, 0);
assert.deepEqual(firstFailure(loadBundle("prop-task-only-contexts-without-work-read")), { ...notReadable, step: "requester-lacks-original-scope-authority" });
assert.deepEqual(firstFailure(loadBundle("prop-task-only-target-context-without-work-read")), { ...notReadable, step: "requester-lacks-target-scope-authority" });
assert.equal(firstFailure(loadBundle("prop-task-only-contexts-with-work-read")).rule, "runtime-reconciliation-required");
});
test("F3: the issuer ceiling narrows both requester calculations and work access is rechecked", () => {
const expect = { exit: 3, result: "refused", reason: "not-authorized", rule: "consulted-work-not-readable", step: "requester-lacks-original-scope-authority" };
assert.deepEqual(firstFailure(loadBundle("prop-issuer-denies-work-read")), expect);
assert.deepEqual(firstFailure(loadBundle("prop-issuer-denies-work-read-task-only")), expect);
assert.equal(firstFailure(loadBundle("prop-issuer-grants-work-read")).rule, "runtime-reconciliation-required");
// the narrowing is the same in both scopes, so the original scope is always the first failure;
// the layer algebra itself is scope-independent
const issuer = layerFromRestrictions({ operations: ["assignment.change"], readPaths: [], writePaths: [], network: "none", endpointRefs: [] });
const ctx = layerFromRestrictions({ operations: ["work.read", "assignment.change"], readPaths: [], writePaths: [], network: "none", endpointRefs: [] });
assert.deepEqual(intersectLayers(ctx, issuer).operations, ["assignment.change"]);
});
test("F4: cycle graphs are revision-exact; contiguous history with reversed edges is not a cycle", () => {
assert.equal(firstFailure(loadBundle("cycle-acyclic-versioned-history")).exit, 0);
assert.equal(firstFailure(loadBundle("cycle-acyclic-versioned-history-file-change")).exit, 0);
assert.equal(firstFailure(loadBundle("cycle-dependency-cross-revision")).rule, "dependency-cycle");
assert.equal(firstFailure(loadBundle("cycle-mission-parent-old-revision-only")).rule, "mission-parent-cycle");
assert.equal(firstFailure(loadBundle("cycle-mission-parent-previous-revision-not-a-cycle")).rule, "mission-parent-scope-mismatch");
// detector-level witness: the exact graph of the history is acyclic, the identity-merged one is not
const exact = new Map([["T#1", ["D#1"]], ["D#1", []], ["T#2", []], ["D#2", ["T#2"]]]);
assert.equal(findCycle(exact), null);
const merged = new Map([["T", ["D"]], ["D", ["T"]]]);
assert.equal(findCycle(merged), "D");
});
test("F5: the first shape failure is independent of record input order, including malformed records", () => {
const forward = loadBundle("shape-order-forward");
const reference = firstFailure(forward);
assert.equal(reference.rule, "shape-enum-mismatch");
const permutations = [
(r) => [...r].reverse(),
(r) => [...r.slice(7), ...r.slice(0, 7)],
(r) => [...r].sort((a, b) => (a.id < b.id ? 1 : -1)),
(r) => r.filter((_, i) => i % 2 === 1).concat(r.filter((_, i) => i % 2 === 0)),
];
for (const [i, perm] of permutations.entries()) {
const b = loadBundle("shape-order-forward");
b.records = perm(b.records);
assert.deepEqual(firstFailure(b), reference, `permutation ${i}`);
}
assert.deepEqual(firstFailure(loadBundle("shape-order-reversed")), reference);
// malformed records (no readable identity) are validated after every identified record, wherever they sit
const withMalformed = loadBundle("shape-order-malformed-record-sorts-last");
assert.equal(firstFailure(withMalformed).rule, "shape-enum-mismatch");
const moved = loadBundle("shape-order-malformed-record-sorts-last");
moved.records = [...moved.records.slice(1), moved.records[0]];
assert.deepEqual(firstFailure(moved), firstFailure(withMalformed));
assert.equal(firstFailure(loadBundle("shape-order-only-malformed-record")).rule, "shape-missing-field");
// two unidentified records: content decides, not position
const twoBad = loadBundle("demo-read-w1");
const a = { schemaVersion: 1 };
const c = { schemaVersion: "1" };
twoBad.records = [a, c, ...twoBad.records];
const first = firstFailure(twoBad);
twoBad.records = [c, ...twoBad.records.slice(2), a];
assert.deepEqual(firstFailure(twoBad), first);
});
// --- FI-ROCKO-7: stable traversal of the declared inventories (§10.4) -------------------------
/** First failure plus its internal locator with the input index replaced by the entry's typed key. */
function firstLocated(bundle, family) {
const { result, exit, detail } = evaluate(bundle);
const m = typeof detail === "string" ? detail.match(new RegExp(`^bundle\\.${family}\\[(\\d+)\\]`)) : null;
const entry = m === null ? null : bundle[family][Number(m[1])];
return { exit, result: result.result, reason: result.reason, rule: result.rule, step: result.proposal === null ? null : result.proposal.rule,
tail: m === null ? detail : detail.slice(m[0].length), key: entry === null ? null : ORDER_KEYS[family](entry) };
}
/**
* Permutations of index array `arr`: every permutation when n ≤ 4, otherwise a fixed
* deterministic family (identity, reverse, every rotation, odd/even interleave, a
* descending sort and twelve seeded FisherYates shuffles). Includes the identity.
*/
function permutations(arr) {
if (arr.length <= 4) {
if (arr.length <= 1) return [arr];
return arr.flatMap((x, i) => permutations([...arr.slice(0, i), ...arr.slice(i + 1)]).map((rest) => [x, ...rest]));
}
const out = [arr, [...arr].reverse(), [...arr].sort((x, y) => y - x), arr.filter((_, i) => i % 2 === 1).concat(arr.filter((_, i) => i % 2 === 0))];
for (let k = 1; k < arr.length; k += 1) out.push([...arr.slice(k), ...arr.slice(0, k)]);
let seed = 0x9e3779b9;
const next = () => { seed = (Math.imul(seed ^ (seed >>> 15), 0x2c1b3c6d) >>> 0); seed = (seed ^ (seed >>> 12)) >>> 0; return seed / 0x100000000; };
for (let s = 0; s < 12; s += 1) {
const p = [...arr];
for (let i = p.length - 1; i > 0; i -= 1) { const j = Math.floor(next() * (i + 1)); [p[i], p[j]] = [p[j], p[i]]; }
out.push(p);
}
return out;
}
/** Assert that every permutation of bundle[family] yields the same located first failure; returns it. */
function assertPermutationStable(name, family, expectRule, expectKey) {
const reference = firstLocated(loadBundle(name), family);
assert.equal(reference.rule, expectRule, name);
if (expectKey !== undefined) assert.deepEqual(reference.key, expectKey, `${name}: first failure locator`);
const base = loadBundle(name);
const perms = permutations(base[family].map((_, i) => i));
assert.ok(perms.length >= 2, `${name}: ${family} has a permutable inventory`);
for (const perm of perms) {
const b = loadBundle(name);
b[family] = perm.map((i) => base[family][i]);
assert.deepEqual(firstLocated(b, family), reference, `${name}: permutation ${JSON.stringify(perm)}`);
}
return reference;
}
test("stableOrder: typed keys, null components after values, unkeyed items last, ordering-form tie-break, then index", () => {
const items = [
{ registry: "scope-role", id: "b", revision: 2, digest: "x" }, // 0
{ registry: "scope-role", id: "b", revision: 1, digest: "x" }, // 1
{ registry: "agent-policy", id: "z", revision: 1, digest: "x" }, // 2
"not-an-entry", // 3 (unkeyed)
{ registry: "scope-role", id: "b", revision: null, digest: "x" }, // 4 (null revision after integers)
{ registry: 7 }, // 5 (unkeyed)
{ registry: "scope-role", id: "b", revision: 1, digest: "x" }, // 6 (duplicate of 1: canonical tie, index decides)
{ registry: "scope-role", id: "b", revision: 1, digest: "a" }, // 7 (digest "a" before "x")
];
assert.deepEqual(stableOrder(items, ORDER_KEYS.registries), [2, 7, 1, 6, 0, 4, 3, 5]);
// unkeyed items order by their ordering form: "not-an-entry" (string) vs {"registry":7} (object); reversing the input does not change it
const rev = [...items].reverse();
const order = stableOrder(rev, ORDER_KEYS.registries).map((i) => rev[i]);
assert.deepEqual(order, stableOrder(items, ORDER_KEYS.registries).map((i) => items[i]));
// records: kind/id/scope/revision, unreadable identity last
const recs = [{ kind: "task", id: "t", scope: { kind: "system" }, revision: 2 }, { schemaVersion: 1 }, { kind: "task", id: "t", scope: { kind: "system" }, revision: 1 }, { kind: "agent-definition", id: "a", scope: { kind: "project", projectId: "p" }, revision: 1 }];
assert.deepEqual(stableOrder(recs, ORDER_KEYS.records), [3, 2, 0, 1]);
assert.deepEqual(stableOrder(["b", "a", 7, "a"], ORDER_KEYS.authorizations), [1, 3, 0, 2]);
assert.deepEqual(stableOrder([{ runId: "r", artifactId: "b", digest: "d" }, { runId: "r", artifactId: "a" }, { runId: 1 }], ORDER_KEYS.artifacts), [1, 0, 2]);
assert.deepEqual(stableOrder([{ decisionRef: { kind: "decision", id: "b", scope: { kind: "system" }, revision: 1 } }, { decisionRef: null }, { decisionRef: { kind: "decision", id: "a", scope: { kind: "system" }, revision: 1 } }], ORDER_KEYS.delegationInputs), [2, 0, 1]);
// semantic sequence arrays are never reordered: the helper is only applied to inventories
const b = loadBundle("demo-read-w1");
const before = JSON.stringify(b);
evaluate(b);
assert.equal(JSON.stringify(b), before, "evaluation does not mutate or reorder the input bundle");
});
test("ordering: registries — differing shape errors, permutation-stable first rule follows the typed key", () => {
const apFull = ["agent-policy", "ap-full", 1, null];
assertPermutationStable("shape-order-registries-forward", "registries", "shape-missing-field", apFull);
assertPermutationStable("shape-order-registries-reversed", "registries", "shape-missing-field", apFull);
// swapping which entry carries which error changes the first rule, not the winning key
const swapped = assertPermutationStable("shape-order-registries-swapped-forward", "registries", "shape-unknown-field");
assert.deepEqual(swapped.key.slice(0, 2), ["agent-policy", "ap-full"]);
assertPermutationStable("shape-order-registries-swapped-reversed", "registries", "shape-unknown-field");
// an entry without a readable registry/id is validated after every keyed entry
const malformed = assertPermutationStable("shape-order-registries-malformed-sorts-last", "registries", "shape-enum-mismatch");
assert.deepEqual(malformed.key.slice(0, 2), ["no-such-registry", "role-writer"]);
assert.equal(malformed.tail, ".registry");
const only = assertPermutationStable("shape-order-registries-only-malformed", "registries", "shape-type-mismatch");
assert.equal(only.key, null);
});
test("ordering: registries — profile then digest stages follow the same key; stage precedence is fixed", () => {
const apReader = ["agent-policy", "ap-reader\n", 1];
const p = assertPermutationStable("profile-order-registries-forward", "registries", "profile-pattern-mismatch");
assert.deepEqual(p.key.slice(0, 3), apReader);
assert.equal(p.tail, ".id");
assertPermutationStable("profile-order-registries-reversed", "registries", "profile-pattern-mismatch");
// shape (later key) precedes profile (earlier key)
const sp = assertPermutationStable("profile-order-shape-precedes-profile-registries", "registries", "shape-unknown-field");
assert.deepEqual(sp.key.slice(0, 2), ["project-policy", "pp-p2"]);
// digest stage: unsupported content on the lower key precedes a mismatch on the higher key, and vice versa
const d = assertPermutationStable("shape-order-registry-digest-forward", "registries", "mock-content-unsupported");
assert.deepEqual(d.key.slice(0, 2), ["agent-policy", "ap-full"]);
assert.equal(d.reason, "unsupported-capability");
assertPermutationStable("shape-order-registry-digest-reversed", "registries", "mock-content-unsupported");
const ds = assertPermutationStable("shape-order-registry-digest-swapped-forward", "registries", "registry-digest-mismatch");
assert.deepEqual(ds.key.slice(0, 2), ["agent-policy", "ap-full"]);
assert.equal(ds.tail, ".digest");
assertPermutationStable("shape-order-registry-digest-swapped-reversed", "registries", "registry-digest-mismatch");
// profile (later key) precedes digest (earlier key)
const pd = assertPermutationStable("shape-order-profile-precedes-digest-registries", "registries", "profile-pattern-mismatch");
assert.deepEqual(pd.key.slice(0, 2), ["project-policy", "pp-p2\n"]);
// two competing digest mismatches: the lower key is reported in every order
const two = assertPermutationStable("shape-order-two-digest-mismatches-reversed", "registries", "registry-digest-mismatch");
assert.deepEqual(two.key.slice(0, 2), ["scope-role", "role-reader"]);
});
test("ordering: artifacts — differing shape errors and profile violations are permutation-stable", () => {
const art1 = assertPermutationStable("shape-order-artifacts-forward", "artifacts", "shape-pattern-mismatch");
assert.deepEqual(art1.key.slice(0, 2), ["run-1", "art-1"]);
assert.equal(art1.tail, ".digest");
assertPermutationStable("shape-order-artifacts-reversed", "artifacts", "shape-pattern-mismatch");
const sw = assertPermutationStable("shape-order-artifacts-swapped-forward", "artifacts", "shape-unknown-field");
assert.deepEqual(sw.key.slice(0, 2), ["run-1", "art-1"]);
assertPermutationStable("shape-order-artifacts-swapped-reversed", "artifacts", "shape-unknown-field");
const mal = assertPermutationStable("shape-order-artifacts-malformed-sorts-last", "artifacts", "shape-pattern-mismatch");
assert.deepEqual(mal.key.slice(0, 2), ["run-1", "art-2"]);
const prof = assertPermutationStable("profile-order-artifacts-reversed", "artifacts", "profile-pattern-mismatch");
assert.deepEqual(prof.key.slice(0, 2), ["run-1", "art-2\n"]);
assert.equal(prof.tail, ".artifactId");
});
test("ordering: authorizations — the lower runtime id is the locator in every order; shape precedes profile", () => {
const low = assertPermutationStable("shape-order-authorizations-forward", "authorizations", "shape-pattern-mismatch");
assert.deepEqual(low.key, ["00000000-0000-1000-8000-000000000002"]);
assertPermutationStable("shape-order-authorizations-reversed", "authorizations", "shape-pattern-mismatch");
const mal = assertPermutationStable("shape-order-authorizations-malformed-sorts-last", "authorizations", "shape-pattern-mismatch");
assert.deepEqual(mal.key, ["00000000-0000-4000-8000-000000000001-x"]);
const prof = assertPermutationStable("profile-order-authorizations-reversed", "authorizations", "profile-pattern-mismatch");
assert.deepEqual(prof.key, ["00000000-0000-4000-8000-000000000001\n"]);
const sp = assertPermutationStable("profile-order-shape-precedes-profile-authorizations", "authorizations", "shape-pattern-mismatch");
assert.deepEqual(sp.key, ["00000000-0000-4000-8000-000000000002-x"]);
});
test("ordering: delegationInputs — shape, identity and structural stages traverse by decisionRef key", () => {
const delegB = ["decision", "d-deleg-b", "workspace:p1/w1", 1];
const f = assertPermutationStable("shape-order-delegation-inputs-forward", "delegationInputs", "shape-enum-mismatch", delegB);
assert.equal(f.tail, ".mode");
assertPermutationStable("shape-order-delegation-inputs-reversed", "delegationInputs", "shape-enum-mismatch", delegB);
assertPermutationStable("shape-order-delegation-inputs-swapped-forward", "delegationInputs", "shape-missing-field", delegB);
assertPermutationStable("shape-order-delegation-inputs-swapped-reversed", "delegationInputs", "shape-missing-field", delegB);
assertPermutationStable("shape-order-delegation-inputs-malformed-sorts-last", "delegationInputs", "shape-enum-mismatch", delegB);
// structural: both inputs reference a missing revision; the lower key is reported wherever it sits
const s = assertPermutationStable("struct-order-delegation-inputs-forward", "delegationInputs", "record-reference-missing", [...delegB.slice(0, 3), 9]);
assert.equal(s.reason, "missing-state");
assert.equal(s.tail, ".decisionRef");
assertPermutationStable("struct-order-delegation-inputs-reversed", "delegationInputs", "record-reference-missing", [...delegB.slice(0, 3), 9]);
// identity: a duplicated input is found after a valid lower-key input in every order
const dup = assertPermutationStable("dup-order-delegation-inputs-reversed", "delegationInputs", "duplicate-delegation-input");
assert.deepEqual(dup.key.slice(0, 2), ["decision", "d-deleg-reg"]);
});
test("ordering: family precedence and identity-stage order are fixed regardless of input positions", () => {
assert.equal(firstFailure(loadBundle("shape-order-family-precedence-registries-before-artifacts")).rule, "shape-unknown-field");
assert.equal(firstFailure(loadBundle("shape-order-family-precedence-artifacts-before-authorizations")).rule, "shape-pattern-mismatch");
assert.equal(firstFailure(loadBundle("shape-order-family-precedence-authorizations-before-delegation-inputs")).rule, "shape-type-mismatch");
assert.equal(firstFailure(loadBundle("dup-order-registries-precede-artifacts")).rule, "duplicate-registry-identity");
// duplicate plain-registry identity with differing digests: the higher digest is the reported entry in every order
const base = loadBundle("demo-read-w1");
const harness = base.registries.find((e) => e.registry === "harness");
base.registries.push({ ...harness, digest: "sha256:" + "f".repeat(64) });
const ref = firstLocated(base, "registries");
assert.equal(ref.rule, "duplicate-registry-identity");
assert.equal(ref.key[3], "sha256:" + "f".repeat(64));
const moved = { ...base, registries: [base.registries[base.registries.length - 1], ...base.registries.slice(0, -1)] };
assert.deepEqual(firstLocated(moved, "registries"), ref);
const lower = { ...base, registries: base.registries.map((e) => (e.digest === "sha256:" + "f".repeat(64) ? { ...e, digest: "sha256:" + "0".repeat(64) } : e)) };
assert.equal(firstLocated(lower, "registries").key[3], harness.digest, "the original (now higher) digest is reported");
// artifacts: duplicate run/artifact with differing digests, permutation-stable locator
const art = loadBundle("dup-artifact-identity");
const a = firstLocated(art, "artifacts");
assert.equal(a.rule, "duplicate-artifact-identity");
const artRev = { ...art, artifacts: [...art.artifacts].reverse() };
assert.deepEqual(firstLocated(artRev, "artifacts"), a);
});
test("R5-1: the ordering form is total and injective over the strict-JSON domain, distinct from the mock digest", () => {
// every parser-producible value has a form; canonicalize() refuses most of these
const values = [null, true, false, 0, -1, 9007199254740991, "", "a", "é", "É", "é", "\u{1F600}", "café", "cafe",
" ", " ", "\\", "\"", [], [null], [1, 2], [2, 1], [[1], 2], [1, [2]], {}, { a: 1 }, { a: 2 }, { b: 1 }, { a: 1, b: 2 },
{ a: { b: 1 } }, { a: [1] }, { "é": 1 }, { "é": "é" }, { extra: "é" }, { x: "é", y: null }, [{ extra: "é" }], ["é"]];
const forms = values.map(orderingForm);
for (const f of forms) assert.equal(typeof f, "string");
assert.equal(new Set(forms).size, values.length, "distinct values have distinct forms");
let refused = 0;
for (const v of values) { try { canonicalize(v); } catch (e) { assert.ok(e instanceof CanonicalError); refused += 1; } }
assert.ok(refused >= 12, "the mock digest domain is narrower than the ordering-form domain");
// key order is by UTF-16 code unit and does not depend on insertion order; array order is semantic
assert.equal(orderingForm({ b: 1, a: 2 }), orderingForm({ a: 2, b: 1 }));
assert.equal(orderingForm({ b: 1, a: 2 }), '{"a":2,"b":1}');
assert.equal(orderingForm({ "é": 1, e: 1, z: 1 }), '{"e":1,"z":1,"é":1}');
assert.notEqual(orderingForm([1, 2]), orderingForm([2, 1]));
assert.equal(orderingForm("é"), JSON.stringify("é"));
assert.equal(orderingForm([{ extra: "é" }, "é"]), '[{"extra":"é"},"é"]');
// no normalization or case folding; the form never throws for non-ASCII and is not a validity judgement
assert.notEqual(orderingForm("é"), orderingForm("é"));
assert.equal(orderingForm("é"), orderingForm("é"));
assert.throws(() => orderingForm(undefined), /non-JSON/);
assert.throws(() => orderingForm(() => 1), /non-JSON/);
// stableOrder on two distinct unkeyed Unicode entries: string form before object form, in both input orders
for (const family of ["records", "registries", "artifacts", "delegationInputs"]) {
assert.deepEqual(stableOrder([{ extra: "é" }, "é"], ORDER_KEYS[family]), [1, 0], family);
assert.deepEqual(stableOrder(["é", { extra: "é" }], ORDER_KEYS[family]), [0, 1], family);
}
// equal typed keys with distinct Unicode bodies: the form decides, never the index
const a = { registry: "agent-policy", id: "ap", revision: 1, digest: "d", content: { name: "café", z: 1 } };
const b = { registry: "agent-policy", id: "ap", revision: 1, digest: "d", content: { name: "café", a: 1 } };
assert.deepEqual(stableOrder([a, b], ORDER_KEYS.registries), [1, 0]);
assert.deepEqual(stableOrder([b, a], ORDER_KEYS.registries), [0, 1]);
// only byte-identical entries fall back to the input index
assert.deepEqual(stableOrder([a, structuredClone(a)], ORDER_KEYS.registries), [0, 1]);
});
test("R5-1: unkeyed Unicode entries — the located first failure is permutation-stable in every family", () => {
for (const [family, slug] of [["records", "records"], ["registries", "registries"], ["artifacts", "artifacts"], ["delegationInputs", "delegation-inputs"]]) {
const f = assertPermutationStable(`shape-order-unkeyed-unicode-${slug}-forward`, family, "shape-type-mismatch");
assert.equal(f.key, null, family);
const r = assertPermutationStable(`shape-order-unkeyed-unicode-${slug}-reversed`, family, "shape-type-mismatch");
assert.equal(r.key, null, family);
// the two fixtures differ only in input order; the string entry is the located one in both
const fb = loadBundle(`shape-order-unkeyed-unicode-${slug}-forward`);
const rb = loadBundle(`shape-order-unkeyed-unicode-${slug}-reversed`);
assert.deepEqual([...fb[family]].reverse(), rb[family], family);
for (const bundle of [fb, rb]) {
const m = new RegExp(`^bundle\\.${family}\\[(\\d+)\\]`).exec(evaluate(structuredClone(bundle)).detail);
assert.ok(m, family);
assert.equal(typeof bundle[family][Number(m[1])], "string", `${family}: the located entry is the string`);
}
}
});
test("R5-1: equal-key Unicode entries — distinct bodies never collapse; first failure is permutation-stable", () => {
// records: two agent-a copies (same kind/id/scope/revision, non-ASCII displayName); the copy with the unknown
// field sorts first by form ("extra" < "harnessRef"), so shape-unknown-field is reported in every order
const agentA = ["agent-definition", "agent-a", "system", 1];
const rf = assertPermutationStable("shape-order-equal-key-unicode-records-forward", "records", "shape-unknown-field", agentA);
assert.equal(rf.tail, ".payload.extra");
assertPermutationStable("shape-order-equal-key-unicode-records-reversed", "records", "shape-unknown-field", agentA);
// registries: two ap-full copies (same registry/id/revision/digest, non-ASCII path); same rule in every order
const gf = assertPermutationStable("shape-order-equal-key-unicode-registries-forward", "registries", "shape-unknown-field");
assert.deepEqual(gf.key.slice(0, 3), ["agent-policy", "ap-full", 1]);
assert.equal(gf.tail, ".content.restrictions.extra");
assertPermutationStable("shape-order-equal-key-unicode-registries-reversed", "registries", "shape-unknown-field");
// the sibling copy's failure (enum) is what remains once the unknown field is removed: the pair really carries two errors
const b = loadBundle("shape-order-equal-key-unicode-records-forward");
const extra = b.records.find((r) => r.id === "agent-a" && "extra" in r.payload);
delete extra.payload.extra;
assert.equal(firstFailure(b).rule, "shape-enum-mismatch");
const g = loadBundle("shape-order-equal-key-unicode-registries-forward");
delete g.registries.find((e) => e.id === "ap-full" && "extra" in e.content.restrictions).content.restrictions.extra;
assert.equal(firstFailure(g).rule, "shape-enum-mismatch");
// Unicode positives remain allowed
assert.equal(firstFailure(loadBundle("positive-unicode-display-name-allowed")).result, "allowed");
});
test("F5: message-is-not-authority follows structural validation and precedes requester admission", () => {
const message = { exit: 3, result: "refused", reason: "not-authorized", rule: "message-is-not-authority", step: "message-is-not-authority" };
assert.deepEqual(firstFailure(loadBundle("prop-message-precedes-requester-admission")), message);
assert.deepEqual(firstFailure(loadBundle("prop-message-is-not-authority")), message);
assert.deepEqual(firstFailure(loadBundle("prop-message-precedes-stale-subject")), message);
assert.equal(firstFailure(loadBundle("prop-message-after-structural-failure")).rule, "requester-context-mismatch");
// without a message the missing delegation keeps its step-3 position
assert.deepEqual(firstFailure(loadBundle("prop-delegation-missing-after-requester-admission")),
{ exit: 3, result: "refused", reason: "not-authorized", rule: "operation-not-permitted", step: "requester-lacks-original-scope-authority" });
assert.equal(firstFailure(loadBundle("prop-delegation-missing")).rule, "delegation-missing");
});