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.
1365 lines
87 KiB
JavaScript
1365 lines
87 KiB
JavaScript
/**
|
||
* Deterministic fixture generator for the foundation synthetic inspector.
|
||
*
|
||
* node scripts/foundation/fixtures/build-fixtures.mjs <output-dir>
|
||
*
|
||
* Writes bundles/<name>.json (one per case), raw/<name>.json (byte-exact lexical
|
||
* cases), index.json (expected verdict per case) and demo/*.json (owner demo).
|
||
* The checked-in copies under scripts/foundation/fixtures/ must equal a fresh run;
|
||
* scripts/test-foundation.sh regenerates into a scratch directory and diffs.
|
||
*
|
||
* Everything here is synthetic. No live identities, credentials or paths.
|
||
*/
|
||
|
||
import { writeFileSync, mkdirSync } from "node:fs";
|
||
import { join } from "node:path";
|
||
import { createHash } from "node:crypto";
|
||
import { digestOf } from "../canonical.mjs";
|
||
import { OPERATION_CATALOG } from "../resolve.mjs";
|
||
|
||
const AUTH = "00000000-0000-4000-8000-000000000001";
|
||
const AUTH_2 = "00000000-0000-4000-8000-000000000002";
|
||
const AUTH_UNDECLARED = "00000000-0000-4000-8000-000000000099"; // well-formed, never declared (FI-FILBERT-6 F1)
|
||
const T = "2026-09-06T03:00:00.000Z";
|
||
const OWNER = { kind: "human", principalId: "owner", executionId: null };
|
||
|
||
function fakeDigest(label) {
|
||
return `sha256:${createHash("sha256").update(`synthetic:${label}`).digest("hex")}`;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Restrictions and registries
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const ROOT = { root: "workspace", path: null };
|
||
const NO_NET = { network: "none", endpointRefs: [] };
|
||
|
||
function restrictions(operations, readPaths, writePaths) {
|
||
return { operations, readPaths, writePaths, ...NO_NET };
|
||
}
|
||
|
||
const R_FULL = restrictions([...OPERATION_CATALOG], [ROOT], [ROOT]);
|
||
const R_READER = restrictions(["work.read", "file.read"], [ROOT], []);
|
||
const R_WRITER = restrictions(["work.read", "file.read", "file.change"], [ROOT], [{ root: "workspace", path: "src" }]);
|
||
const R_MANAGER = restrictions(["work.read", "file.read", "file.change", "assignment.change"], [ROOT], [ROOT]);
|
||
const R_NO_WORK = restrictions(["file.read", "file.change"], [ROOT], [ROOT]);
|
||
const R_NO_DELEGATE = restrictions(["work.read", "file.read", "file.change"], [ROOT], [ROOT]);
|
||
const R_NO_FILE_READ = restrictions(["work.read", "file.change"], [ROOT], [ROOT]);
|
||
|
||
function contentEntry(registry, id, revision, r) {
|
||
const content = { restrictions: r };
|
||
return { registry, id, revision, digest: digestOf(content), content };
|
||
}
|
||
|
||
function plainEntry(registry, id, revision) {
|
||
return { registry, id, revision, digest: fakeDigest(`${registry}/${id}/${revision}`) };
|
||
}
|
||
|
||
function regRef(entry) {
|
||
return { registry: entry.registry, id: entry.id, revision: entry.revision, digest: entry.digest };
|
||
}
|
||
|
||
const REG = {
|
||
harness: plainEntry("harness", "pi", 1),
|
||
settings: plainEntry("settings", "default", 1),
|
||
soul: plainEntry("context-content", "soul", 1),
|
||
apFull: contentEntry("agent-policy", "ap-full", 1, R_FULL),
|
||
apReader: contentEntry("agent-policy", "ap-reader", 1, R_READER),
|
||
roleReader: contentEntry("scope-role", "role-reader", 1, R_READER),
|
||
roleWriter: contentEntry("scope-role", "role-writer", 1, R_WRITER),
|
||
roleManager: contentEntry("scope-role", "role-manager", 1, R_MANAGER),
|
||
ppP1: contentEntry("project-policy", "pp-p1", 1, R_FULL),
|
||
ppP2: contentEntry("project-policy", "pp-p2", 1, R_FULL),
|
||
};
|
||
|
||
const ART_1 = { runId: "run-1", artifactId: "art-1", digest: fakeDigest("art-1") };
|
||
const ART_2 = { runId: "run-1", artifactId: "art-2", digest: fakeDigest("art-2") };
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Records
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const SYSTEM = { kind: "system" };
|
||
const P1 = { kind: "project", projectId: "p1" };
|
||
const P2 = { kind: "project", projectId: "p2" };
|
||
const W1 = { kind: "workspace", projectId: "p1", workspaceId: "w1" };
|
||
const W2 = { kind: "workspace", projectId: "p1", workspaceId: "w2" };
|
||
const W3 = { kind: "workspace", projectId: "p2", workspaceId: "w3" };
|
||
|
||
function rec(kind, id, scope, payload, extra = {}) {
|
||
return {
|
||
schemaVersion: 1,
|
||
kind,
|
||
id,
|
||
scope,
|
||
revision: 1,
|
||
createdAt: T,
|
||
createdBy: OWNER,
|
||
supersedes: null,
|
||
authorizationRef: AUTH,
|
||
payload,
|
||
...extra,
|
||
};
|
||
}
|
||
|
||
function ref(r) {
|
||
return { kind: r.kind, id: r.id, scope: r.scope, revision: r.revision };
|
||
}
|
||
|
||
function agent(id, policy) {
|
||
return rec("agent-definition", id, SYSTEM, {
|
||
displayName: `Agent ${id}`,
|
||
agentType: "worker",
|
||
harnessRef: regRef(REG.harness),
|
||
settingsRef: regRef(REG.settings),
|
||
soulRef: regRef(REG.soul),
|
||
instructionRefs: [],
|
||
skillRefs: [],
|
||
policyRef: regRef(policy),
|
||
status: "enabled",
|
||
});
|
||
}
|
||
|
||
function project(scope, policy) {
|
||
return rec("project", scope.projectId, scope, { displayName: `Project ${scope.projectId}`, policyRef: regRef(policy), status: "active" });
|
||
}
|
||
|
||
function workspace(scope, policy) {
|
||
return rec("workspace", scope.workspaceId, scope, {
|
||
displayName: `Workspace ${scope.workspaceId}`, policyRef: regRef(policy), status: "active", fileRootId: `root-${scope.workspaceId}`,
|
||
});
|
||
}
|
||
|
||
function registration(id, scope, agentId, role, parent) {
|
||
return rec("registration", id, scope, {
|
||
agentId,
|
||
scopeRoleRef: regRef(role),
|
||
restrictions: null,
|
||
projectRegistrationRef: parent ? ref(parent) : null,
|
||
status: "active",
|
||
delegationRef: null,
|
||
});
|
||
}
|
||
|
||
function decision(id, scope, decisionKind, subjectRefs, extra = {}) {
|
||
return rec("decision", id, scope, {
|
||
decisionKind,
|
||
subjectRefs,
|
||
outcome: "approved",
|
||
basisRef: ART_1,
|
||
evidenceRefs: [ART_2],
|
||
delegatedOperations: [],
|
||
...extra,
|
||
});
|
||
}
|
||
|
||
function mission(id, scope, parent) {
|
||
return rec("mission", id, scope, {
|
||
objective: `Mission ${id}`,
|
||
criteria: [{ id: "c1", text: "done" }],
|
||
parentMissionRef: parent ? ref(parent) : null,
|
||
restrictions: null,
|
||
status: "active",
|
||
});
|
||
}
|
||
|
||
function task(id, scope, missionRec, intentId, status, deps = []) {
|
||
return rec("task", id, scope, {
|
||
purpose: `Task ${id}`,
|
||
criteria: [{ id: "c1", text: "done" }],
|
||
missionRef: missionRec ? ref(missionRec) : null,
|
||
intentRef: { kind: "decision", id: intentId, scope, revision: 1 },
|
||
dependencies: deps.map(ref),
|
||
restrictions: null,
|
||
status,
|
||
});
|
||
}
|
||
|
||
function assignment(id, scope, taskRec, agentId, intentId) {
|
||
return rec("assignment", id, scope, {
|
||
taskRef: ref(taskRec),
|
||
agentId,
|
||
intentRef: { kind: "decision", id: intentId, scope, revision: 1 },
|
||
status: "selected",
|
||
endReason: null,
|
||
});
|
||
}
|
||
|
||
function buildBase() {
|
||
const agentA = agent("agent-a", REG.apFull);
|
||
const agentB = agent("agent-b", REG.apFull);
|
||
const p1 = project(P1, REG.ppP1);
|
||
const p2 = project(P2, REG.ppP2);
|
||
const w1 = workspace(W1, REG.ppP1);
|
||
const w2 = workspace(W2, REG.ppP1);
|
||
const w3 = workspace(W3, REG.ppP2);
|
||
|
||
const regAP1 = registration("reg-a-p1", P1, "agent-a", REG.roleWriter, null);
|
||
const regAW1 = registration("reg-a-w1", W1, "agent-a", REG.roleWriter, regAP1);
|
||
const regBP1 = registration("reg-b-p1", P1, "agent-b", REG.roleManager, null);
|
||
const regBW1 = registration("reg-b-w1", W1, "agent-b", REG.roleManager, regBP1);
|
||
const regBW2 = registration("reg-b-w2", W2, "agent-b", REG.roleManager, regBP1);
|
||
const regBP2 = registration("reg-b-p2", P2, "agent-b", REG.roleManager, null);
|
||
const regBW3 = registration("reg-b-w3", W3, "agent-b", REG.roleManager, regBP2);
|
||
|
||
const mP1 = mission("m-p1", P1, null);
|
||
const mW1 = mission("m-w1", W1, mP1);
|
||
const mW2 = mission("m-w2", W2, mP1);
|
||
const mP2 = mission("m-p2", P2, null);
|
||
|
||
const taskW1Dep = task("task-w1-dep", W1, null, "d-plan-dep", "accepted");
|
||
const taskW1 = task("task-w1", W1, mW1, "d-plan-1", "active", [taskW1Dep]);
|
||
const taskW2 = task("task-w2", W2, mW2, "d-plan-w2", "ready");
|
||
const taskW1B = task("task-w1-b", W1, null, "d-plan-w1b", "active");
|
||
const taskW2B = task("task-w2-b", W2, null, "d-plan-w2b", "active");
|
||
const taskW3 = task("task-w3", W3, null, "d-plan-w3", "accepted");
|
||
|
||
const asgAW1 = assignment("asg-a-w1", W1, taskW1, "agent-a", "d-plan-1");
|
||
const asgBW1 = assignment("asg-b-w1", W1, taskW1B, "agent-b", "d-plan-w1b");
|
||
const asgBW2 = assignment("asg-b-w2", W2, taskW2B, "agent-b", "d-plan-w2b");
|
||
|
||
const dPlan1 = decision("d-plan-1", W1, "plan-approval", [ref(taskW1), ref(asgAW1)]);
|
||
const dPlanDep = decision("d-plan-dep", W1, "plan-approval", [ref(taskW1Dep)]);
|
||
const dPlanW2 = decision("d-plan-w2", W2, "plan-approval", [ref(taskW2)]);
|
||
const dPlanW1B = decision("d-plan-w1b", W1, "plan-approval", [ref(taskW1B), ref(asgBW1)]);
|
||
const dPlanW2B = decision("d-plan-w2b", W2, "plan-approval", [ref(taskW2B), ref(asgBW2)]);
|
||
const dPlanW3 = decision("d-plan-w3", W3, "plan-approval", [ref(taskW3)]);
|
||
const dDelegB = decision("d-deleg-b", W1, "delegation", [ref(agentB), ref(asgAW1)], { delegatedOperations: ["assignment.change"] });
|
||
const dChange1 = decision("d-change-1", W1, "assignment-change", [ref(asgAW1), ref(taskW2)]);
|
||
|
||
// Deep-cloned so per-case mutations never leak through shared module-level objects.
|
||
return clone({
|
||
bundleVersion: 1,
|
||
kind: "foundation-inspector-bundle",
|
||
simulation: true,
|
||
records: [
|
||
agentA, agentB, p1, p2, w1, w2, w3,
|
||
regAP1, regAW1, regBP1, regBW1, regBW2, regBP2, regBW3,
|
||
mP1, mW1, mW2, mP2,
|
||
taskW1Dep, taskW1, taskW2, taskW1B, taskW2B, taskW3,
|
||
asgAW1, asgBW1, asgBW2,
|
||
dPlan1, dPlanDep, dPlanW2, dPlanW1B, dPlanW2B, dPlanW3, dDelegB, dChange1,
|
||
],
|
||
registries: Object.values(REG),
|
||
artifacts: [ART_1, ART_2],
|
||
authorizations: [AUTH],
|
||
selection: {
|
||
agentId: "agent-a",
|
||
projectId: "p1",
|
||
workspaceId: "w1",
|
||
assignmentRef: ref(asgAW1),
|
||
execution: { kind: "unrestricted-simulation" },
|
||
},
|
||
operation: { name: "work.read", target: null },
|
||
proposal: null,
|
||
delegationInputs: [],
|
||
});
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Mutation helpers
|
||
// ---------------------------------------------------------------------------
|
||
|
||
function clone(v) {
|
||
return JSON.parse(JSON.stringify(v));
|
||
}
|
||
|
||
function find(b, kind, id) {
|
||
const r = b.records.find((x) => x.kind === kind && x.id === id);
|
||
if (!r) throw new Error(`fixture: no ${kind} ${id}`);
|
||
return r;
|
||
}
|
||
|
||
function findReg(b, id) {
|
||
const e = b.registries.find((x) => x.id === id);
|
||
if (!e) throw new Error(`fixture: no registry ${id}`);
|
||
return e;
|
||
}
|
||
|
||
/** Append revision 2 of a record (supersedes rev 1) with a payload patch; returns it. */
|
||
function reviseRecord(b, kind, id, patch) {
|
||
const head = find(b, kind, id);
|
||
const next = clone(head);
|
||
next.revision = 2;
|
||
next.supersedes = ref(head);
|
||
Object.assign(next.payload, patch);
|
||
b.records.push(next);
|
||
return next;
|
||
}
|
||
|
||
function fileOp(name, path) {
|
||
return { name, target: { root: "workspace", path } };
|
||
}
|
||
|
||
function restricted(r) {
|
||
return { kind: "restrictions", restrictions: r };
|
||
}
|
||
|
||
function withRegistrationDelegation(b, ops = ["work.read", "file.read"], ceiling = R_FULL) {
|
||
const agentA = find(b, "agent-definition", "agent-a");
|
||
const regAW1 = find(b, "registration", "reg-a-w1");
|
||
const d = decision("d-deleg-reg", W1, "delegation", [ref(agentA), ref(regAW1)], { delegatedOperations: ops });
|
||
b.records.push(d);
|
||
regAW1.payload.delegationRef = ref(d);
|
||
b.delegationInputs.push({ decisionRef: ref(d), mode: "direct-declared", issuerCeiling: ceiling });
|
||
return d;
|
||
}
|
||
|
||
/** Clear every mission/dependency reference so each task is consulted work on its own (F2). */
|
||
function clearWorkReferences(b) {
|
||
for (const r of b.records) {
|
||
if (r.kind === "task") { r.payload.missionRef = null; r.payload.dependencies = []; }
|
||
}
|
||
}
|
||
|
||
/**
|
||
* F4 witness: a contiguous revision-2 history where task-w1 rev 2 has no dependencies
|
||
* and task-w1-dep rev 2 depends on task-w1 rev 2. Exact-reference graphs are acyclic;
|
||
* an identity-merged graph would see task-w1 -> task-w1-dep -> task-w1.
|
||
*/
|
||
function versionedHistory(b) {
|
||
const t2 = reviseRecord(b, "task", "task-w1", { dependencies: [] });
|
||
const d2 = reviseRecord(b, "task", "task-w1-dep", { dependencies: [ref(t2)] });
|
||
const a2 = reviseRecord(b, "assignment", "asg-a-w1", { taskRef: ref(t2) });
|
||
const p2 = reviseRecord(b, "decision", "d-plan-1", { subjectRefs: [ref(t2), ref(a2)] });
|
||
const pd2 = reviseRecord(b, "decision", "d-plan-dep", { subjectRefs: [ref(d2)] });
|
||
t2.payload.intentRef = ref(p2);
|
||
a2.payload.intentRef = ref(p2);
|
||
d2.payload.intentRef = ref(pd2);
|
||
b.selection.assignmentRef = ref(a2);
|
||
return { t2, d2, a2, p2, pd2 };
|
||
}
|
||
|
||
function makeProposal(b) {
|
||
const asgAW1 = find(b, "assignment", "asg-a-w1");
|
||
const taskW2 = find(b, "task", "task-w2");
|
||
const dDelegB = find(b, "decision", "d-deleg-b");
|
||
const dChange1 = find(b, "decision", "d-change-1");
|
||
const asgBW1 = find(b, "assignment", "asg-b-w1");
|
||
const asgBW2 = find(b, "assignment", "asg-b-w2");
|
||
b.operation = { name: "assignment.change", target: null };
|
||
b.proposal = {
|
||
kind: "assignment-change",
|
||
requesterAgentId: "agent-b",
|
||
subjectAssignmentRef: ref(asgAW1),
|
||
targetTaskRef: ref(taskW2),
|
||
delegationRef: ref(dDelegB),
|
||
changeDecisionRef: ref(dChange1),
|
||
requesterContexts: {
|
||
original: { assignmentRef: ref(asgBW1), execution: { kind: "unrestricted-simulation" } },
|
||
target: { assignmentRef: ref(asgBW2), execution: { kind: "unrestricted-simulation" } },
|
||
},
|
||
};
|
||
b.delegationInputs = [{ decisionRef: ref(dDelegB), mode: "direct-declared", issuerCeiling: R_FULL }];
|
||
return b;
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Case table
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const ALLOWED = { exit: 0, result: "allowed", reason: "allowed", rule: null, proposalRule: null };
|
||
const invalid = (rule, reason = "invalid-request") => ({ exit: 2, result: "invalid", reason, rule, proposalRule: null });
|
||
const refused = (rule, reason = "not-authorized") => ({ exit: 3, result: "refused", reason, rule, proposalRule: null });
|
||
const proposalRefused = (step, rule, reason = "not-authorized") => ({ exit: 3, result: "refused", reason, rule, proposalRule: step });
|
||
const UNRESOLVED = { exit: 3, result: "unresolved", reason: "unknown-effects", rule: "runtime-reconciliation-required", proposalRule: "runtime-reconciliation-required" };
|
||
|
||
const cases = [];
|
||
function add(name, group, expect, mutate) {
|
||
const b = buildBase();
|
||
mutate(b);
|
||
cases.push({ name, group, expect, bundle: b });
|
||
}
|
||
|
||
// --- A1/A2 positives -------------------------------------------------------
|
||
add("demo-read-w1", "positive", ALLOWED, () => {});
|
||
add("demo-file-read-src", "positive", ALLOWED, (b) => { b.operation = fileOp("file.read", "src/main.mjs"); });
|
||
add("demo-file-change-src", "positive", ALLOWED, (b) => { b.operation = fileOp("file.change", "src/main.mjs"); });
|
||
add("taskless-work-read", "positive", ALLOWED, (b) => { b.selection.assignmentRef = null; });
|
||
add("taskless-file-read", "positive", ALLOWED, (b) => { b.selection.assignmentRef = null; b.operation = fileOp("file.read", "docs/readme.md"); });
|
||
add("deleg-approved-file-read", "positive", ALLOWED, (b) => { withRegistrationDelegation(b); b.operation = fileOp("file.read", "src/x.mjs"); });
|
||
add("work-parent-project-mission", "positive", ALLOWED, (b) => {
|
||
find(b, "task", "task-w1").payload.missionRef = ref(find(b, "mission", "m-p1"));
|
||
});
|
||
add("p2-inventory-unselected", "positive", ALLOWED, (b) => {
|
||
// p2/w3 inventory present but unselected; p2 retired must not matter for a w1 read
|
||
find(b, "project", "p2").payload.status = "retired";
|
||
});
|
||
add("file-read-with-unaccepted-dependency", "positive", ALLOWED, (b) => {
|
||
find(b, "task", "task-w1-dep").payload.status = "active";
|
||
b.operation = fileOp("file.read", "src/main.mjs");
|
||
});
|
||
add("execution-restrictions-narrow-ok", "positive", ALLOWED, (b) => {
|
||
b.selection.execution = restricted(restrictions(["work.read", "file.read"], [{ root: "workspace", path: "src" }], []));
|
||
b.operation = fileOp("file.read", "src/deep/file.txt");
|
||
});
|
||
add("mission-restrictions-narrow-ok", "positive", ALLOWED, (b) => {
|
||
find(b, "mission", "m-w1").payload.restrictions = restrictions(["work.read", "file.read", "file.change"], [ROOT], [{ root: "workspace", path: "src" }]);
|
||
b.operation = fileOp("file.change", "src/a.txt");
|
||
});
|
||
add("registration-restrictions-narrow-ok", "positive", ALLOWED, (b) => {
|
||
find(b, "registration", "reg-a-w1").payload.restrictions = restrictions(["work.read", "file.read", "file.change"], [{ root: "workspace", path: "src" }], [ROOT]);
|
||
b.operation = fileOp("file.read", "src/b.txt");
|
||
});
|
||
add("demo-change-w1", "proposal", UNRESOLVED, (b) => { makeProposal(b); });
|
||
|
||
// --- stage 2: shapes / profile --------------------------------------------
|
||
add("shape-unknown-top-field", "shape", invalid("shape-unknown-field"), (b) => { b.extra = 1; });
|
||
add("shape-proto-key", "shape", invalid("shape-unknown-field"), (b) => { b.__proto__x = 1; });
|
||
add("shape-missing-execution", "shape", invalid("shape-missing-field"), (b) => { delete b.selection.execution; });
|
||
add("shape-missing-operation", "shape", invalid("shape-missing-field"), (b) => { delete b.operation; });
|
||
add("shape-simulation-false", "shape", invalid("shape-enum-mismatch"), (b) => { b.simulation = false; });
|
||
add("shape-bundle-version-2", "shape", invalid("shape-enum-mismatch"), (b) => { b.bundleVersion = 2; });
|
||
add("shape-record-unknown-field", "shape", invalid("shape-unknown-field"), (b) => { find(b, "task", "task-w1").payload.note = "x"; });
|
||
add("shape-record-missing-field", "shape", invalid("shape-missing-field"), (b) => { delete find(b, "task", "task-w1").payload.criteria; });
|
||
add("shape-record-bad-id", "shape", invalid("shape-pattern-mismatch"), (b) => { find(b, "task", "task-w1").id = "Task-W1"; });
|
||
add("shape-record-bad-time", "shape", invalid("shape-pattern-mismatch"), (b) => { find(b, "task", "task-w1").createdAt = "2026-09-06T03:00:00Z"; });
|
||
add("shape-record-calendar-invalid", "shape", invalid("calendar-invalid"), (b) => { find(b, "task", "task-w1").createdAt = "2026-02-30T03:00:00.000Z"; });
|
||
add("shape-record-leap-second", "shape", invalid("calendar-invalid"), (b) => { find(b, "task", "task-w1").createdAt = "2026-06-30T23:59:60.000Z"; });
|
||
add("shape-record-status-enum", "shape", invalid("shape-enum-mismatch"), (b) => { find(b, "task", "task-w1").payload.status = "done"; });
|
||
add("shape-record-criteria-empty", "shape", invalid("shape-bound-exceeded"), (b) => { find(b, "task", "task-w1").payload.criteria = []; });
|
||
add("shape-record-criteria-duplicate", "shape", invalid("shape-unique-violation"), (b) => {
|
||
const t = find(b, "task", "task-w1"); t.payload.criteria = [{ id: "c1", text: "done" }, { text: "done", id: "c1" }];
|
||
});
|
||
add("shape-record-displayname-too-long", "shape", invalid("shape-bound-exceeded"), (b) => { find(b, "project", "p1").payload.displayName = "x".repeat(129); });
|
||
add("shape-record-agent-actor-null-execution", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "task", "task-w1").createdBy = { kind: "agent", principalId: "agent-a", executionId: null };
|
||
});
|
||
add("shape-record-rev1-with-supersedes", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
const t = find(b, "task", "task-w1"); t.supersedes = ref(t);
|
||
});
|
||
add("shape-project-registration-with-parent", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "registration", "reg-a-p1").payload.projectRegistrationRef = ref(find(b, "registration", "reg-b-p1"));
|
||
});
|
||
add("shape-project-mission-with-parent", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "mission", "m-p1").payload.parentMissionRef = ref(find(b, "mission", "m-p2"));
|
||
});
|
||
add("shape-assignment-ended-without-reason", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "assignment", "asg-b-w2").payload.status = "ended";
|
||
});
|
||
add("shape-delegation-without-operations", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "decision", "d-deleg-b").payload.delegatedOperations = [];
|
||
});
|
||
add("shape-plan-with-delegated-operations", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "decision", "d-plan-1").payload.delegatedOperations = ["file.read"];
|
||
});
|
||
add("shape-restrictions-none-with-endpoints", "shape", invalid("shape-conditional-mismatch"), (b) => {
|
||
find(b, "task", "task-w1").payload.restrictions = { operations: ["work.read"], readPaths: [], writePaths: [], network: "none", endpointRefs: [plainEntry("endpoint", "ep", 1)] };
|
||
});
|
||
add("shape-restrictions-unknown-operation", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
find(b, "task", "task-w1").payload.restrictions = restrictions(["file.delete"], [], []);
|
||
});
|
||
add("shape-path-grant-absolute", "shape", invalid("path-invalid"), (b) => {
|
||
find(b, "task", "task-w1").payload.restrictions = restrictions(["work.read"], [{ root: "workspace", path: "/etc" }], []);
|
||
});
|
||
add("shape-target-dotdot", "shape", invalid("path-invalid"), (b) => { b.operation = fileOp("file.read", "src/../etc/passwd"); });
|
||
add("shape-target-backslash", "shape", invalid("path-invalid"), (b) => { b.operation = fileOp("file.read", "src\\x"); });
|
||
add("shape-target-empty-segment", "shape", invalid("path-invalid"), (b) => { b.operation = fileOp("file.read", "src//x"); });
|
||
add("shape-target-control-char", "shape", invalid("path-invalid"), (b) => { b.operation = fileOp("file.read", "src/x\u001b[31m"); });
|
||
// U+2028 is not Cc/Cf/Cs: the pinned checker accepts it as a path character; the text renderer must escape it on echo.
|
||
add("shape-target-line-separator-echo-ok", "positive", ALLOWED, (b) => { b.operation = fileOp("file.read", "src/x\u2028y"); });
|
||
add("shape-target-unicode-ok", "positive", ALLOWED, (b) => { b.operation = fileOp("file.read", "src/café/\u{1F600}.txt"); });
|
||
add("shape-target-wrong-root", "shape", invalid("shape-enum-mismatch"), (b) => { b.operation = { name: "file.read", target: { root: "home", path: "x" } }; });
|
||
add("shape-registry-digest-mismatch", "profile", invalid("registry-digest-mismatch"), (b) => {
|
||
findReg(b, "role-writer").content.restrictions.operations.push("command.execute");
|
||
});
|
||
add("shape-registry-content-non-ascii", "profile", invalid("mock-content-unsupported", "unsupported-capability"), (b) => {
|
||
const e = findReg(b, "role-writer"); e.content.restrictions.readPaths = [{ root: "workspace", path: "café" }]; e.digest = fakeDigest("whatever");
|
||
});
|
||
add("shape-registry-missing-content", "shape", invalid("shape-missing-field"), (b) => { delete findReg(b, "role-writer").content; });
|
||
add("shape-registry-plain-with-content", "shape", invalid("shape-unknown-field"), (b) => { findReg(b, "pi").content = { restrictions: R_FULL }; });
|
||
add("shape-unsupported-kind-session", "profile", invalid("record-kind-unsupported", "unsupported-kind"), (b) => {
|
||
b.records.push({ schemaVersion: 1, kind: "session", id: "s1", scope: W1, revision: 1, createdAt: T, createdBy: OWNER, supersedes: null, authorizationRef: AUTH, payload: {} });
|
||
});
|
||
add("shape-unknown-kind", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
b.records.push({ schemaVersion: 1, kind: "widget", id: "s1", scope: W1, revision: 1, createdAt: T, createdBy: OWNER, supersedes: null, authorizationRef: AUTH, payload: {} });
|
||
});
|
||
|
||
// --- stage 2 profile: strict typed-string grammar (addendum FI-C2-1) ----------------
|
||
// The pinned checker's "$" tolerates exactly one final LF on id/runtimeId/digest values;
|
||
// the inspector's schema column reproduces that verdict and the strict profile refuses
|
||
// the value before any identity lookup (exit 2, profile-pattern-mismatch). Every other
|
||
// line-ending form fails the schema pattern in both implementations.
|
||
const LF1 = "\n";
|
||
const NEWLINE_VARIANTS = [
|
||
["two-final-lf", (v) => `${v}\n\n`], ["crlf", (v) => `${v}\r\n`], ["cr", (v) => `${v}\r`],
|
||
["interior-lf", (v) => `${v.slice(0, 3)}\n${v.slice(3)}`],
|
||
["u2028", (v) => `${v}\u2028`], ["u2029", (v) => `${v}\u2029`],
|
||
];
|
||
const PROFILE_MISMATCH = invalid("profile-pattern-mismatch");
|
||
const PATTERN_MISMATCH = invalid("shape-pattern-mismatch");
|
||
// One site per type family, run through the whole line-ending matrix.
|
||
const FAMILY_SITES = [
|
||
["id-record-id", (b, f) => { const t = find(b, "task", "task-w1"); t.id = f(t.id); }],
|
||
["runtime-id-authorization-ref", (b, f) => { const t = find(b, "task", "task-w1"); t.authorizationRef = f(t.authorizationRef); }],
|
||
["digest-registry-ref", (b, f) => { const r = find(b, "project", "p1").payload.policyRef; r.digest = f(r.digest); }],
|
||
];
|
||
for (const [site, mutate] of FAMILY_SITES) {
|
||
add(`profile-${site}-one-final-lf`, "profile", PROFILE_MISMATCH, (b) => mutate(b, (v) => `${v}${LF1}`));
|
||
for (const [variant, f] of NEWLINE_VARIANTS) add(`profile-${site}-${variant}`, "shape", PATTERN_MISMATCH, (b) => mutate(b, f));
|
||
}
|
||
// One final LF at every other typed occurrence: record envelope/payload references,
|
||
// unselected inventory, wrapper inventories, selection, proposal and declared inputs.
|
||
const ONE_FINAL_LF_SITES = {
|
||
"record-scope-project-id": (b) => { find(b, "task", "task-w1").scope.projectId += LF1; },
|
||
"record-scope-workspace-id": (b) => { find(b, "task", "task-w1").scope.workspaceId += LF1; },
|
||
"record-actor-principal-id": (b) => { find(b, "task", "task-w1").createdBy.principalId += LF1; },
|
||
"record-actor-execution-id": (b) => { find(b, "task", "task-w1").createdBy = { kind: "agent", principalId: "agent-a", executionId: `${AUTH}${LF1}` }; },
|
||
"record-supersedes-id": (b) => { reviseRecord(b, "task", "task-w1-b", {}).supersedes.id += LF1; },
|
||
// Without the LF this is the existing ref-missing-dependency case (record-reference-missing):
|
||
// the profile refuses before identity lookup, so the two outcomes stay distinguishable.
|
||
"record-dependency-ref-id-dangling": (b) => { find(b, "task", "task-w1").payload.dependencies[0].id = `task-nope${LF1}`; },
|
||
"record-registry-ref-id": (b) => { find(b, "project", "p1").payload.policyRef.id += LF1; },
|
||
"record-agent-type": (b) => { find(b, "agent-definition", "agent-a").payload.agentType += LF1; },
|
||
"record-file-root-id": (b) => { find(b, "workspace", "w1").payload.fileRootId += LF1; },
|
||
"record-registration-agent-id": (b) => { find(b, "registration", "reg-a-w1").payload.agentId += LF1; },
|
||
"record-assignment-agent-id": (b) => { find(b, "assignment", "asg-a-w1").payload.agentId += LF1; },
|
||
"record-criterion-id": (b) => { find(b, "mission", "m-w1").payload.criteria[0].id += LF1; },
|
||
"record-decision-subject-ref-id": (b) => { find(b, "decision", "d-plan-1").payload.subjectRefs[0].id += LF1; },
|
||
"unselected-record-id": (b) => { find(b, "project", "p2").id += LF1; },
|
||
"unselected-record-scope-project-id": (b) => { find(b, "task", "task-w3").scope.projectId += LF1; },
|
||
"registry-id": (b) => { findReg(b, "pi").id += LF1; },
|
||
"registry-digest-plain": (b) => { findReg(b, "pi").digest += LF1; },
|
||
// Content-bearing entry: the pattern profile precedes the content-digest profile.
|
||
"registry-digest-content-bearing": (b) => { findReg(b, "role-writer").digest += LF1; },
|
||
"artifact-run-id": (b) => { b.artifacts[0].runId += LF1; },
|
||
"artifact-id": (b) => { b.artifacts[0].artifactId += LF1; },
|
||
"artifact-digest": (b) => { b.artifacts[0].digest += LF1; },
|
||
"authorization": (b) => { b.authorizations[0] += LF1; },
|
||
"selection-agent-id": (b) => { b.selection.agentId += LF1; },
|
||
"selection-project-id": (b) => { b.selection.projectId += LF1; },
|
||
"selection-workspace-id": (b) => { b.selection.workspaceId += LF1; },
|
||
"selection-assignment-ref-id": (b) => { b.selection.assignmentRef.id += LF1; },
|
||
"selection-assignment-ref-scope-workspace-id": (b) => { b.selection.assignmentRef.scope.workspaceId += LF1; },
|
||
"proposal-requester-agent-id": (b) => { makeProposal(b); b.proposal.requesterAgentId += LF1; },
|
||
"proposal-target-task-ref-id": (b) => { makeProposal(b); b.proposal.targetTaskRef.id += LF1; },
|
||
"proposal-requester-context-assignment-ref-id": (b) => { makeProposal(b); b.proposal.requesterContexts.original.assignmentRef.id += LF1; },
|
||
"delegation-input-decision-ref-id": (b) => { withRegistrationDelegation(b); b.delegationInputs[0].decisionRef.id += LF1; },
|
||
};
|
||
for (const [site, mutate] of Object.entries(ONE_FINAL_LF_SITES)) add(`profile-${site}-one-final-lf`, "profile", PROFILE_MISMATCH, mutate);
|
||
// Negative control: free-form text with (escaped) newlines is not a typed grammar and stays admitted.
|
||
add("profile-escaped-newline-free-text-allowed", "positive", ALLOWED, (b) => {
|
||
find(b, "mission", "m-w1").payload.objective = "line one\nline two\n";
|
||
find(b, "task", "task-w1").payload.purpose = "multi\r\nline";
|
||
});
|
||
// time/path keep their existing semantics: a final LF fails the format in both implementations.
|
||
add("profile-time-one-final-lf", "shape", PATTERN_MISMATCH, (b) => { find(b, "task", "task-w1").createdAt += LF1; });
|
||
add("profile-path-one-final-lf", "shape", invalid("path-invalid"), (b) => { b.operation = fileOp("file.read", `src/main.mjs${LF1}`); });
|
||
add("shape-too-many-records", "shape", invalid("shape-bound-exceeded"), (b) => {
|
||
for (let i = 0; i < 260; i += 1) b.records.push(decision(`d-bulk-${i}`, W1, "plan-approval", [ref(find(b, "task", "task-w1"))]));
|
||
});
|
||
add("shape-delegation-input-other-mode", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
b.delegationInputs.push({ decisionRef: ref(find(b, "decision", "d-deleg-b")), mode: "parent-delegation", issuerCeiling: R_FULL });
|
||
});
|
||
add("shape-delegation-input-parent-ref", "shape", invalid("shape-unknown-field"), (b) => {
|
||
b.delegationInputs.push({ decisionRef: ref(find(b, "decision", "d-deleg-b")), mode: "direct-declared", issuerCeiling: R_FULL, parentDelegationRef: null });
|
||
});
|
||
add("shape-proposal-on-read", "operation", invalid("proposal-not-applicable"), (b) => { makeProposal(b); b.operation = { name: "work.read", target: null }; });
|
||
|
||
// --- stage 3: duplicates ---------------------------------------------------
|
||
add("dup-record-identity", "identity", invalid("duplicate-record-identity"), (b) => { b.records.push(clone(find(b, "task", "task-w1"))); });
|
||
add("dup-registry-identity", "identity", invalid("duplicate-registry-identity"), (b) => { b.registries.push(clone(findReg(b, "role-writer"))); });
|
||
add("dup-artifact-identity", "identity", invalid("duplicate-artifact-identity"), (b) => { b.artifacts.push({ ...ART_1, digest: fakeDigest("other") }); });
|
||
add("dup-authorization", "identity", invalid("duplicate-authorization-id"), (b) => { b.authorizations.push(AUTH); });
|
||
add("dup-delegation-input", "identity", invalid("duplicate-delegation-input"), (b) => {
|
||
makeProposal(b); b.delegationInputs.push(clone(b.delegationInputs[0]));
|
||
});
|
||
|
||
// --- stage 4: structural refs ---------------------------------------------
|
||
add("ref-missing-task-intent", "structure", invalid("record-reference-missing", "missing-state"), (b) => { find(b, "task", "task-w1").payload.intentRef.id = "d-nope"; });
|
||
add("ref-missing-selection-assignment", "structure", invalid("record-reference-missing", "missing-state"), (b) => { b.selection.assignmentRef.id = "asg-nope"; });
|
||
add("ref-missing-supersedes", "structure", invalid("record-reference-missing", "missing-state"), (b) => {
|
||
const t = find(b, "task", "task-w1"); t.revision = 2; t.supersedes = { kind: "task", id: "task-w1", scope: W1, revision: 1 };
|
||
});
|
||
add("ref-missing-dependency", "structure", invalid("record-reference-missing", "missing-state"), (b) => { find(b, "task", "task-w1").payload.dependencies[0].id = "task-nope"; });
|
||
add("ref-missing-decision-subject", "structure", invalid("record-reference-missing", "missing-state"), (b) => { find(b, "decision", "d-plan-1").payload.subjectRefs[0].revision = 7; });
|
||
|
||
// --- stage 5: cycles -------------------------------------------------------
|
||
add("cycle-dependency-self", "cycle", invalid("dependency-cycle"), (b) => { const t = find(b, "task", "task-w1"); t.payload.dependencies = [ref(t)]; });
|
||
add("cycle-dependency-pair", "cycle", invalid("dependency-cycle"), (b) => {
|
||
const t = find(b, "task", "task-w1"); const d = find(b, "task", "task-w1-dep"); d.payload.dependencies = [ref(t)];
|
||
});
|
||
add("cycle-mission-self-parent", "cycle", invalid("mission-parent-cycle"), (b) => { const m = find(b, "mission", "m-w1"); m.payload.parentMissionRef = ref(m); });
|
||
add("cycle-supersedes-self-rev2", "cycle", invalid("supersedes-cycle"), (b) => {
|
||
const t = find(b, "task", "task-w1"); t.revision = 2; t.supersedes = ref(t);
|
||
find(b, "assignment", "asg-a-w1").payload.taskRef.revision = 2;
|
||
find(b, "decision", "d-plan-1").payload.subjectRefs[0].revision = 2;
|
||
});
|
||
|
||
// --- stage 6: continuity ---------------------------------------------------
|
||
add("continuity-revision-gap", "continuity", invalid("revision-chain-gap"), (b) => {
|
||
const head = find(b, "task", "task-w1-b");
|
||
const r3 = clone(head); r3.revision = 3; r3.supersedes = { kind: "task", id: "task-w1-b", scope: W1, revision: 1 };
|
||
b.records.push(r3);
|
||
});
|
||
add("continuity-supersedes-other-identity", "continuity", invalid("supersedes-mismatch"), (b) => {
|
||
const head = find(b, "task", "task-w1-b");
|
||
const r2 = clone(head); r2.revision = 2; r2.supersedes = ref(find(b, "task", "task-w2-b"));
|
||
b.records.push(r2);
|
||
});
|
||
|
||
// --- stage 7: ownership ----------------------------------------------------
|
||
add("own-project-id-scope", "ownership", invalid("project-id-scope-mismatch"), (b) => { find(b, "project", "p2").id = "p9"; });
|
||
add("own-workspace-id-scope", "ownership", invalid("workspace-id-scope-mismatch"), (b) => { find(b, "workspace", "w3").id = "w9"; });
|
||
add("own-workspace-project-missing", "ownership", invalid("workspace-project-missing", "missing-state"), (b) => {
|
||
b.records = b.records.filter((r) => !(r.kind === "project" && r.id === "p2"));
|
||
b.records = b.records.filter((r) => !(r.kind === "mission" && r.id === "m-p2"));
|
||
});
|
||
add("own-registration-parent-other-agent", "ownership", invalid("registration-parent-mismatch"), (b) => {
|
||
find(b, "registration", "reg-a-w1").payload.projectRegistrationRef = ref(find(b, "registration", "reg-b-p1"));
|
||
});
|
||
add("own-registration-parent-other-project", "ownership", invalid("registration-parent-mismatch"), (b) => {
|
||
find(b, "registration", "reg-b-w3").payload.projectRegistrationRef = ref(find(b, "registration", "reg-b-p1"));
|
||
});
|
||
add("own-mission-parent-other-project", "ownership", invalid("mission-parent-scope-mismatch"), (b) => {
|
||
find(b, "mission", "m-w1").payload.parentMissionRef = ref(find(b, "mission", "m-p2"));
|
||
});
|
||
add("own-mission-parent-workspace", "ownership", invalid("mission-parent-scope-mismatch"), (b) => {
|
||
find(b, "mission", "m-w1").payload.parentMissionRef = ref(find(b, "mission", "m-w2"));
|
||
});
|
||
add("own-mission-owning-project-mismatch", "ownership", invalid("mission-owning-project-mismatch"), (b) => {
|
||
find(b, "task", "task-w1").payload.missionRef = ref(find(b, "mission", "m-p2"));
|
||
});
|
||
add("own-assignment-task-other-workspace", "ownership", invalid("assignment-task-scope-mismatch"), (b) => {
|
||
find(b, "assignment", "asg-a-w1").payload.taskRef = ref(find(b, "task", "task-w2"));
|
||
});
|
||
|
||
// --- stage 8: operation ----------------------------------------------------
|
||
add("op-unknown-name", "operation", invalid("operation-unknown"), (b) => { b.operation = { name: "file.delete", target: null }; });
|
||
add("op-unsupported-catalog", "operation", invalid("operation-unsupported", "unsupported-capability"), (b) => { b.operation = { name: "command.execute", target: null }; });
|
||
add("op-unsupported-workspace-retire", "operation", invalid("operation-unsupported", "unsupported-capability"), (b) => { b.operation = { name: "workspace.retire", target: null }; });
|
||
add("op-work-read-with-target", "operation", invalid("operation-target-mismatch"), (b) => { b.operation = fileOp("work.read", "x"); });
|
||
add("op-file-read-without-target", "operation", invalid("operation-target-mismatch"), (b) => { b.operation = { name: "file.read", target: null }; });
|
||
add("op-change-without-proposal", "operation", invalid("proposal-required"), (b) => { b.operation = { name: "assignment.change", target: null }; });
|
||
|
||
// --- stage 9: admission ----------------------------------------------------
|
||
add("adm-selected-agent-missing", "admission", invalid("selected-agent-missing", "missing-state"), (b) => { b.selection.agentId = "agent-z"; b.selection.assignmentRef = null; });
|
||
add("adm-selected-project-missing", "admission", invalid("selected-project-missing", "missing-state"), (b) => {
|
||
b.selection.projectId = "p9"; b.selection.workspaceId = "w9"; b.selection.assignmentRef = null;
|
||
});
|
||
add("adm-selected-workspace-missing", "admission", invalid("selected-workspace-missing", "missing-state"), (b) => { b.selection.workspaceId = "w9"; b.selection.assignmentRef = null; });
|
||
add("adm-authorization-undeclared", "admission", refused("authorization-undeclared", "missing-state"), (b) => { find(b, "agent-definition", "agent-a").authorizationRef = AUTH_2; });
|
||
add("adm-agent-disabled", "admission", refused("agent-disabled"), (b) => { find(b, "agent-definition", "agent-a").payload.status = "disabled"; });
|
||
add("adm-agent-policy-missing", "admission", refused("agent-policy-missing", "missing-state"), (b) => { b.registries = b.registries.filter((e) => e.id !== "ap-full"); });
|
||
add("adm-policy-ceiling-stale", "admission", refused("policy-ceiling-stale", "stale-revision"), (b) => { b.registries.push(contentEntry("agent-policy", "ap-full", 2, R_READER)); });
|
||
add("adm-project-retired", "admission", refused("project-not-active"), (b) => { find(b, "project", "p1").payload.status = "retired"; });
|
||
add("adm-project-policy-missing", "admission", refused("project-policy-missing", "missing-state"), (b) => {
|
||
find(b, "project", "p1").payload.policyRef = regRef(contentEntry("project-policy", "pp-gone", 1, R_FULL));
|
||
});
|
||
add("adm-workspace-retiring", "admission", refused("workspace-not-active"), (b) => { find(b, "workspace", "w1").payload.status = "retiring"; });
|
||
add("adm-workspace-policy-missing", "admission", refused("workspace-policy-missing", "missing-state"), (b) => {
|
||
find(b, "workspace", "w1").payload.policyRef = regRef(contentEntry("project-policy", "pp-gone", 1, R_FULL));
|
||
});
|
||
add("adm-project-registration-missing", "admission", refused("project-registration-missing"), (b) => {
|
||
b.records = b.records.filter((r) => !(r.kind === "registration" && r.id === "reg-a-p1"));
|
||
find(b, "registration", "reg-a-w1").payload.projectRegistrationRef = ref(find(b, "registration", "reg-b-p1"));
|
||
find(b, "registration", "reg-a-w1").payload.agentId = "agent-b";
|
||
b.selection.assignmentRef = null;
|
||
});
|
||
add("adm-workspace-registration-missing", "admission", refused("workspace-registration-missing"), (b) => {
|
||
b.records = b.records.filter((r) => !(r.kind === "registration" && r.id === "reg-a-w1"));
|
||
b.selection.assignmentRef = null;
|
||
});
|
||
add("adm-registration-revoked", "admission", refused("registration-revoked"), (b) => { reviseRecord(b, "registration", "reg-a-w1", { status: "revoked" }); });
|
||
add("adm-registration-ambiguous", "admission", invalid("registration-ambiguous"), (b) => {
|
||
const r = clone(find(b, "registration", "reg-a-w1")); r.id = "reg-a-w1-dup"; b.records.push(r);
|
||
});
|
||
add("adm-registration-parent-stale", "admission", refused("registration-parent-stale", "stale-revision"), (b) => { reviseRecord(b, "registration", "reg-a-p1", {}); });
|
||
add("adm-registration-parent-not-current", "admission", refused("registration-parent-not-current"), (b) => {
|
||
// A second, revoked project registration of the same agent at p1; the workspace
|
||
// registration names it as parent. Same agent and scope (ownership passes), but
|
||
// not the active project registration and not a stale revision of it.
|
||
const old = clone(find(b, "registration", "reg-a-p1")); old.id = "reg-a-p1-old"; old.payload.status = "revoked"; b.records.push(old);
|
||
find(b, "registration", "reg-a-w1").payload.projectRegistrationRef = ref(old);
|
||
});
|
||
add("adm-scope-role-missing", "admission", refused("scope-role-missing", "missing-state"), (b) => { b.registries = b.registries.filter((e) => e.id !== "role-writer"); });
|
||
add("adm-assignment-required-for-change", "admission", refused("assignment-required"), (b) => { b.selection.assignmentRef = null; b.operation = fileOp("file.change", "src/x"); });
|
||
add("adm-assignment-stale", "admission", refused("assignment-stale", "stale-revision"), (b) => { reviseRecord(b, "assignment", "asg-a-w1", {}); });
|
||
add("adm-assignment-paused", "admission", refused("assignment-not-selected"), (b) => { find(b, "assignment", "asg-a-w1").payload.status = "paused"; });
|
||
add("adm-assignment-ended", "admission", refused("assignment-not-selected"), (b) => {
|
||
const a = find(b, "assignment", "asg-a-w1"); a.payload.status = "ended"; a.payload.endReason = "completed";
|
||
});
|
||
add("adm-assignment-other-agent", "admission", refused("assignment-agent-mismatch"), (b) => {
|
||
b.selection.assignmentRef = ref(find(b, "assignment", "asg-b-w1"));
|
||
});
|
||
add("adm-assignment-other-workspace", "admission", refused("assignment-workspace-mismatch"), (b) => {
|
||
const a = find(b, "assignment", "asg-b-w2"); a.payload.agentId = "agent-a";
|
||
b.selection.assignmentRef = ref(a);
|
||
});
|
||
add("adm-task-ref-stale", "admission", refused("task-ref-stale", "stale-revision"), (b) => {
|
||
reviseRecord(b, "task", "task-w1", {});
|
||
});
|
||
add("adm-assignment-intent-stale", "admission", refused("assignment-intent-stale", "stale-revision"), (b) => {
|
||
const r2 = reviseRecord(b, "decision", "d-plan-1", {});
|
||
find(b, "task", "task-w1").payload.intentRef = ref(r2);
|
||
});
|
||
add("adm-assignment-intent-rejected", "admission", refused("assignment-intent-not-applicable"), (b) => { find(b, "decision", "d-plan-1").payload.outcome = "rejected"; });
|
||
add("adm-assignment-intent-other-subject", "admission", refused("assignment-intent-not-applicable"), (b) => {
|
||
find(b, "assignment", "asg-a-w1").payload.intentRef = { kind: "decision", id: "d-plan-w1b", scope: W1, revision: 1 };
|
||
});
|
||
add("adm-task-proposed", "admission", refused("task-not-ready"), (b) => { find(b, "task", "task-w1").payload.status = "proposed"; });
|
||
add("adm-task-intent-stale", "admission", refused("task-intent-stale", "stale-revision"), (b) => {
|
||
const r2 = reviseRecord(b, "decision", "d-plan-1", {});
|
||
find(b, "assignment", "asg-a-w1").payload.intentRef = ref(r2);
|
||
});
|
||
add("adm-task-intent-not-plan", "admission", refused("task-intent-not-approved"), (b) => {
|
||
find(b, "task", "task-w1").payload.intentRef = { kind: "decision", id: "d-change-1", scope: W1, revision: 1 };
|
||
find(b, "assignment", "asg-a-w1").payload.intentRef = { kind: "decision", id: "d-change-1", scope: W1, revision: 1 };
|
||
});
|
||
add("adm-artifact-undeclared", "admission", refused("artifact-undeclared", "missing-state"), (b) => { b.artifacts = [ART_1]; });
|
||
add("adm-mission-stale", "admission", refused("mission-stale", "stale-revision"), (b) => { reviseRecord(b, "mission", "m-w1", {}); });
|
||
add("adm-parent-mission-stale", "admission", refused("mission-stale", "stale-revision"), (b) => { reviseRecord(b, "mission", "m-p1", {}); });
|
||
add("adm-mission-blocked", "admission", refused("mission-not-active"), (b) => { find(b, "mission", "m-w1").payload.status = "blocked"; });
|
||
add("adm-parent-mission-accepted", "admission", refused("mission-not-active"), (b) => { find(b, "mission", "m-p1").payload.status = "accepted"; });
|
||
add("adm-dependency-stale", "admission", refused("dependency-stale", "stale-revision"), (b) => { reviseRecord(b, "task", "task-w1-dep", {}); });
|
||
add("adm-dependency-not-accepted-change", "admission", refused("dependency-not-accepted"), (b) => {
|
||
find(b, "task", "task-w1-dep").payload.status = "active"; b.operation = fileOp("file.change", "src/x");
|
||
});
|
||
add("adm-cross-workspace-mission", "admission", refused("cross-workspace-work-reference-not-modelled", "unsupported-capability"), (b) => {
|
||
find(b, "task", "task-w1").payload.missionRef = ref(find(b, "mission", "m-w2"));
|
||
});
|
||
add("adm-cross-project-dependency", "admission", refused("cross-project-work-reference-not-modelled", "unsupported-capability"), (b) => {
|
||
find(b, "task", "task-w1").payload.dependencies.push(ref(find(b, "task", "task-w3")));
|
||
});
|
||
add("adm-consulted-work-not-readable", "admission", refused("consulted-work-not-readable"), (b) => {
|
||
b.selection.execution = restricted(R_NO_WORK); b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("adm-operation-not-permitted-role", "admission", refused("operation-not-permitted"), (b) => {
|
||
find(b, "registration", "reg-a-w1").payload.scopeRoleRef = regRef(REG.roleReader); b.operation = fileOp("file.change", "src/x");
|
||
});
|
||
add("adm-operation-not-permitted-agent-policy", "admission", refused("operation-not-permitted"), (b) => {
|
||
find(b, "agent-definition", "agent-a").payload.policyRef = regRef(REG.apReader); b.operation = fileOp("file.change", "src/x");
|
||
});
|
||
add("adm-operation-not-permitted-execution", "admission", refused("operation-not-permitted"), (b) => {
|
||
b.selection.execution = restricted(R_NO_FILE_READ); b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("adm-path-not-permitted-write-outside", "admission", refused("path-not-permitted"), (b) => { b.operation = fileOp("file.change", "docs/x.md"); });
|
||
add("adm-path-not-permitted-prefix-string", "admission", refused("path-not-permitted"), (b) => { b.operation = fileOp("file.change", "srcx/x.md"); });
|
||
add("adm-path-not-permitted-task-narrows", "admission", refused("path-not-permitted"), (b) => {
|
||
find(b, "task", "task-w1").payload.restrictions = restrictions(["work.read", "file.read", "file.change"], [ROOT], [{ root: "workspace", path: "src/only" }]);
|
||
b.operation = fileOp("file.change", "src/other/x");
|
||
});
|
||
add("adm-path-not-permitted-read-execution", "admission", refused("path-not-permitted"), (b) => {
|
||
b.selection.execution = restricted(restrictions(["work.read", "file.read"], [{ root: "workspace", path: "docs" }], []));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("adm-dependency-grants-nothing", "admission", refused("path-not-permitted"), (b) => {
|
||
// an accepted dependency with wide restrictions must not widen the selected task
|
||
find(b, "task", "task-w1-dep").payload.restrictions = R_FULL;
|
||
find(b, "task", "task-w1").payload.restrictions = restrictions(["work.read", "file.read", "file.change"], [ROOT], [{ root: "workspace", path: "src/only" }]);
|
||
b.operation = fileOp("file.change", "src/x");
|
||
});
|
||
|
||
// --- §10.1 registration delegation ----------------------------------------
|
||
add("rdeleg-stale", "registration-delegation", refused("registration-delegation-stale", "stale-revision"), (b) => {
|
||
withRegistrationDelegation(b); reviseRecord(b, "decision", "d-deleg-reg", {}); b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-not-approved", "registration-delegation", refused("registration-delegation-not-approved"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
const d = find(b, "decision", "d-deleg-reg"); d.payload.outcome = "rejected"; d.payload.delegatedOperations = [];
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-wrong-kind", "registration-delegation", refused("registration-delegation-not-approved"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
const d = find(b, "decision", "d-deleg-reg"); d.payload.decisionKind = "plan-approval"; d.payload.delegatedOperations = [];
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-scope-not-modelled", "registration-delegation", refused("registration-delegation-scope-not-modelled", "unsupported-capability"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
const d = find(b, "decision", "d-deleg-reg"); d.scope = P1;
|
||
find(b, "registration", "reg-a-w1").payload.delegationRef = ref(d);
|
||
b.delegationInputs[0].decisionRef = ref(d);
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-subject-form-three", "registration-delegation", refused("registration-delegation-subject-form", "unsupported-capability"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
find(b, "decision", "d-deleg-reg").payload.subjectRefs.push(ref(find(b, "task", "task-w1")));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-subject-form-task", "registration-delegation", refused("registration-delegation-subject-form", "unsupported-capability"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
find(b, "decision", "d-deleg-reg").payload.subjectRefs[1] = ref(find(b, "task", "task-w1"));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-bounds-other-agent", "registration-delegation", refused("registration-delegation-bounds"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
find(b, "decision", "d-deleg-reg").payload.subjectRefs[0] = ref(find(b, "agent-definition", "agent-b"));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-bounds-other-registration", "registration-delegation", refused("registration-delegation-bounds"), (b) => {
|
||
withRegistrationDelegation(b);
|
||
find(b, "decision", "d-deleg-reg").payload.subjectRefs[1] = ref(find(b, "registration", "reg-b-w1"));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-input-missing", "registration-delegation", refused("delegation-input-missing", "missing-state"), (b) => {
|
||
withRegistrationDelegation(b); b.delegationInputs = []; b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-exceeds-issuer-ceiling", "registration-delegation", refused("delegation-exceeds-issuer-ceiling"), (b) => {
|
||
withRegistrationDelegation(b, ["work.read", "file.read"], R_NO_FILE_READ); b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-narrows-to-delegated-ops", "registration-delegation", refused("operation-not-permitted"), (b) => {
|
||
withRegistrationDelegation(b, ["work.read", "file.read"]); b.operation = fileOp("file.change", "src/x");
|
||
});
|
||
add("rdeleg-issuer-ceiling-narrows-path", "registration-delegation", refused("path-not-permitted"), (b) => {
|
||
withRegistrationDelegation(b, ["work.read", "file.read"], restrictions(["work.read", "file.read"], [{ root: "workspace", path: "docs" }], []));
|
||
b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
add("rdeleg-policy-ceiling-stale", "registration-delegation", refused("policy-ceiling-stale", "stale-revision"), (b) => {
|
||
withRegistrationDelegation(b); b.registries.push(contentEntry("scope-role", "role-writer", 2, R_READER)); b.operation = fileOp("file.read", "src/x");
|
||
});
|
||
|
||
// --- §5 proposal (assignment.change) ---------------------------------------
|
||
add("prop-subject-not-selected-assignment", "proposal", invalid("proposal-subject-mismatch"), (b) => {
|
||
makeProposal(b); b.proposal.subjectAssignmentRef = ref(find(b, "assignment", "asg-b-w1"));
|
||
});
|
||
add("prop-selection-null-assignment", "proposal", invalid("proposal-subject-mismatch"), (b) => { makeProposal(b); b.selection.assignmentRef = null; });
|
||
add("prop-selection-agent-mismatch", "proposal", invalid("proposal-selection-mismatch"), (b) => { makeProposal(b); b.selection.agentId = "agent-b"; });
|
||
add("prop-requester-agent-missing", "proposal", invalid("requester-agent-missing", "missing-state"), (b) => { makeProposal(b); b.proposal.requesterAgentId = "agent-z"; });
|
||
add("prop-requester-context-wrong-agent", "proposal", invalid("requester-context-mismatch"), (b) => {
|
||
makeProposal(b); b.proposal.requesterContexts.original.assignmentRef = ref(find(b, "assignment", "asg-a-w1"));
|
||
});
|
||
add("prop-requester-context-wrong-scope", "proposal", invalid("requester-context-mismatch"), (b) => {
|
||
makeProposal(b); b.proposal.requesterContexts.target.assignmentRef = ref(find(b, "assignment", "asg-b-w1"));
|
||
});
|
||
add("prop-subject-assignment-stale", "proposal", refused("assignment-stale", "stale-revision"), (b) => {
|
||
makeProposal(b); reviseRecord(b, "assignment", "asg-a-w1", {});
|
||
});
|
||
add("prop-target-task-stale", "proposal", refused("task-ref-stale", "stale-revision"), (b) => {
|
||
makeProposal(b); reviseRecord(b, "task", "task-w2", {});
|
||
});
|
||
add("prop-original-requester-lacks-change", "proposal", proposalRefused("requester-lacks-original-scope-authority", "operation-not-permitted"), (b) => {
|
||
makeProposal(b); find(b, "registration", "reg-b-w1").payload.scopeRoleRef = regRef(REG.roleWriter);
|
||
});
|
||
add("prop-original-requester-registration-revoked", "proposal", proposalRefused("requester-lacks-original-scope-authority", "registration-revoked"), (b) => {
|
||
makeProposal(b); reviseRecord(b, "registration", "reg-b-w1", { status: "revoked" });
|
||
});
|
||
add("prop-original-requester-assignment-paused", "proposal", proposalRefused("requester-lacks-original-scope-authority", "assignment-not-selected"), (b) => {
|
||
makeProposal(b); find(b, "assignment", "asg-b-w1").payload.status = "paused";
|
||
});
|
||
add("prop-original-context-no-work-read", "proposal", proposalRefused("requester-lacks-original-scope-authority", "consulted-work-not-readable"), (b) => {
|
||
makeProposal(b); b.proposal.requesterContexts.original.execution = restricted(restrictions(["file.read", "assignment.change"], [ROOT], [ROOT]));
|
||
});
|
||
add("prop-target-requester-lacks-change", "proposal", proposalRefused("requester-lacks-target-scope-authority", "operation-not-permitted"), (b) => {
|
||
makeProposal(b); find(b, "registration", "reg-b-w2").payload.scopeRoleRef = regRef(REG.roleWriter);
|
||
});
|
||
add("prop-target-workspace-retired", "proposal", proposalRefused("requester-lacks-target-scope-authority", "workspace-not-active"), (b) => {
|
||
makeProposal(b); find(b, "workspace", "w2").payload.status = "retired";
|
||
});
|
||
add("prop-target-cross-project-task", "proposal", invalid("requester-context-mismatch"), (b) => {
|
||
makeProposal(b); b.proposal.targetTaskRef = ref(find(b, "task", "task-w3"));
|
||
});
|
||
add("prop-target-cross-project-context", "proposal", proposalRefused("requester-lacks-target-scope-authority", "task-not-ready"), (b) => {
|
||
// requester holds a valid w3 context, but task-w3 is accepted (not ready/active) -> refused at step 2 via context task? no: context task is the requester's own.
|
||
makeProposal(b);
|
||
const taskW3 = find(b, "task", "task-w3"); taskW3.payload.status = "ready";
|
||
const asgBW3 = assignment("asg-b-w3", W3, taskW3, "agent-b", "d-plan-w3");
|
||
b.records.push(asgBW3);
|
||
find(b, "decision", "d-plan-w3").payload.subjectRefs.push(ref(asgBW3));
|
||
b.proposal.targetTaskRef = ref(taskW3);
|
||
b.proposal.requesterContexts.target.assignmentRef = ref(asgBW3);
|
||
find(b, "decision", "d-change-1").payload.subjectRefs[1] = ref(taskW3);
|
||
taskW3.payload.status = "proposed";
|
||
});
|
||
add("prop-message-is-not-authority", "proposal", proposalRefused("message-is-not-authority", "message-is-not-authority"), (b) => {
|
||
makeProposal(b); b.proposal.delegationRef = null; b.proposal.message = "owner said it is fine";
|
||
});
|
||
add("prop-delegation-missing", "proposal", proposalRefused("delegation-not-applicable", "delegation-missing"), (b) => {
|
||
makeProposal(b); b.proposal.delegationRef = null;
|
||
});
|
||
add("prop-delegation-stale", "proposal", proposalRefused("delegation-not-applicable", "delegation-stale", "stale-revision"), (b) => {
|
||
makeProposal(b); reviseRecord(b, "decision", "d-deleg-b", {});
|
||
});
|
||
add("prop-delegation-rejected", "proposal", proposalRefused("delegation-not-applicable", "delegation-not-approved"), (b) => {
|
||
makeProposal(b); const d = find(b, "decision", "d-deleg-b"); d.payload.outcome = "rejected"; d.payload.delegatedOperations = [];
|
||
});
|
||
add("prop-delegation-wrong-operation", "proposal", proposalRefused("delegation-not-applicable", "delegation-operation-not-covered"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-deleg-b").payload.delegatedOperations = ["file.read"];
|
||
});
|
||
add("prop-delegation-other-recipient", "proposal", proposalRefused("delegation-not-applicable", "delegation-bounds"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-deleg-b").payload.subjectRefs[0] = ref(find(b, "agent-definition", "agent-a"));
|
||
});
|
||
add("prop-delegation-other-subject", "proposal", proposalRefused("delegation-not-applicable", "delegation-bounds"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-deleg-b").payload.subjectRefs[1] = ref(find(b, "assignment", "asg-b-w2"));
|
||
});
|
||
add("prop-delegation-input-missing", "proposal", proposalRefused("delegation-not-applicable", "delegation-input-missing", "missing-state"), (b) => {
|
||
makeProposal(b); b.delegationInputs = [];
|
||
});
|
||
add("prop-delegation-exceeds-issuer-ceiling", "proposal", proposalRefused("delegation-not-applicable", "delegation-exceeds-issuer-ceiling"), (b) => {
|
||
makeProposal(b); b.delegationInputs[0].issuerCeiling = R_NO_DELEGATE;
|
||
});
|
||
add("prop-delegation-artifact-undeclared", "proposal", proposalRefused("delegation-not-applicable", "artifact-undeclared", "missing-state"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-deleg-b").payload.basisRef = { runId: "run-1", artifactId: "art-9", digest: fakeDigest("art-9") };
|
||
});
|
||
add("prop-change-not-recorded", "proposal", proposalRefused("change-not-recorded", "change-not-recorded"), (b) => {
|
||
makeProposal(b); b.proposal.changeDecisionRef = null;
|
||
});
|
||
add("prop-change-stale", "proposal", proposalRefused("change-not-recorded", "change-decision-stale", "stale-revision"), (b) => {
|
||
makeProposal(b); reviseRecord(b, "decision", "d-change-1", {});
|
||
});
|
||
add("prop-change-rejected", "proposal", proposalRefused("change-not-recorded", "change-not-approved"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-change-1").payload.outcome = "rejected";
|
||
});
|
||
add("prop-change-wrong-kind", "proposal", proposalRefused("change-not-recorded", "change-not-approved"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-change-1").payload.decisionKind = "owner-checkpoint";
|
||
});
|
||
add("prop-change-subjects-mismatch", "proposal", proposalRefused("change-not-recorded", "change-subjects-mismatch"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-change-1").payload.subjectRefs = [ref(find(b, "assignment", "asg-a-w1"))];
|
||
});
|
||
add("prop-intent-subject-paused", "proposal", proposalRefused("intent-not-current", "assignment-not-selected"), (b) => {
|
||
makeProposal(b);
|
||
find(b, "assignment", "asg-a-w1").payload.status = "paused";
|
||
});
|
||
add("prop-intent-subject-intent-stale", "proposal", proposalRefused("intent-not-current", "assignment-intent-stale", "stale-revision"), (b) => {
|
||
makeProposal(b);
|
||
const r2 = reviseRecord(b, "decision", "d-plan-1", {});
|
||
find(b, "task", "task-w1").payload.intentRef = ref(r2);
|
||
});
|
||
add("prop-intent-target-task-blocked", "proposal", proposalRefused("intent-not-current", "task-not-ready"), (b) => {
|
||
makeProposal(b); find(b, "task", "task-w2").payload.status = "blocked";
|
||
});
|
||
add("prop-intent-target-task-intent-rejected", "proposal", proposalRefused("intent-not-current", "task-intent-not-approved"), (b) => {
|
||
makeProposal(b); find(b, "decision", "d-plan-w2").payload.outcome = "rejected";
|
||
});
|
||
add("prop-all-pass-restricted-contexts", "proposal", UNRESOLVED, (b) => {
|
||
makeProposal(b);
|
||
b.proposal.requesterContexts.original.execution = restricted(R_MANAGER);
|
||
b.proposal.requesterContexts.target.execution = restricted(R_MANAGER);
|
||
});
|
||
|
||
// --- FI-FILBERT-6 corrections (F1–F5): witness recipes and positive controls ----
|
||
// F1: exact registry declarations for every consulted registry reference
|
||
add("adm-registry-declaration-missing-agent-refs", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
// verdict witness: harness/settings/context-content declarations removed, references intact
|
||
b.registries = b.registries.filter((e) => !["harness", "settings", "context-content"].includes(e.registry));
|
||
});
|
||
add("adm-registry-declaration-missing-settings", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
b.registries = b.registries.filter((e) => e.registry !== "settings");
|
||
});
|
||
add("adm-registry-declaration-missing-soul", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
b.registries = b.registries.filter((e) => e.registry !== "context-content");
|
||
});
|
||
add("adm-registry-declaration-digest-mismatch", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
// declared entry exists, reference names another digest: not an exact four-field match
|
||
find(b, "agent-definition", "agent-a").payload.harnessRef.digest = fakeDigest("harness/pi/1/other");
|
||
});
|
||
add("adm-registry-declaration-revision-mismatch", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
// harness is not a ceiling registry: a newer declared revision is not "stale", the exact ref is simply absent
|
||
const r = find(b, "agent-definition", "agent-a").payload.settingsRef; r.revision = 2; r.digest = fakeDigest("settings/default/2");
|
||
});
|
||
add("adm-registry-declaration-missing-instruction", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
find(b, "agent-definition", "agent-a").payload.instructionRefs = [regRef(plainEntry("context-content", "instr-1", 1))];
|
||
});
|
||
add("adm-registry-declaration-missing-skill", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
find(b, "agent-definition", "agent-a").payload.skillRefs = [regRef(plainEntry("context-content", "skill-1", 1))];
|
||
});
|
||
add("adm-registry-declaration-missing-endpoint-execution", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
const r = restrictions(["work.read", "file.read"], [ROOT], []); r.network = "approved-endpoints"; r.endpointRefs = [regRef(plainEntry("endpoint", "ep-1", 1))];
|
||
b.selection.execution = restricted(r);
|
||
});
|
||
add("adm-registry-declaration-missing-endpoint-task", "admission", refused("registry-declaration-missing", "missing-state"), (b) => {
|
||
const r = restrictions(["work.read", "file.read"], [ROOT], []); r.network = "approved-endpoints"; r.endpointRefs = [regRef(plainEntry("endpoint", "ep-1", 1))];
|
||
find(b, "task", "task-w1").payload.restrictions = r;
|
||
});
|
||
add("adm-registry-declaration-unselected-agent-ignored", "positive", ALLOWED, (b) => {
|
||
// an unconsulted record's dangling declaration is not consulted: agent-b is not selected
|
||
find(b, "agent-definition", "agent-b").payload.harnessRef.digest = fakeDigest("harness/pi/1/other");
|
||
});
|
||
add("adm-registry-declaration-endpoint-declared", "positive", ALLOWED, (b) => {
|
||
// positive control: the endpoint reference is declared exactly; network still intersects to none
|
||
const ep = plainEntry("endpoint", "ep-1", 1); b.registries.push(ep);
|
||
const r = restrictions(["work.read", "file.read"], [ROOT], []); r.network = "approved-endpoints"; r.endpointRefs = [regRef(ep)];
|
||
b.selection.execution = restricted(r);
|
||
});
|
||
add("adm-dependency-authorization-undeclared", "admission", refused("authorization-undeclared", "missing-state"), (b) => {
|
||
// verdict witness: the consulted dependency names a well-formed, undeclared authorization
|
||
find(b, "task", "task-w1-dep").authorizationRef = AUTH_UNDECLARED;
|
||
});
|
||
add("adm-mission-authorization-undeclared", "admission", refused("authorization-undeclared", "missing-state"), (b) => {
|
||
find(b, "mission", "m-p1").authorizationRef = AUTH_UNDECLARED;
|
||
});
|
||
add("adm-unconsulted-authorization-ignored", "positive", ALLOWED, (b) => {
|
||
// task-w3 is never consulted by a w1 read; its undeclared authorization is not an admission input
|
||
find(b, "task", "task-w3").authorizationRef = AUTH_UNDECLARED;
|
||
});
|
||
// F2: the assigned task is consulted work even without mission/dependency references
|
||
add("adm-assigned-task-without-work-read", "admission", refused("consulted-work-not-readable"), (b) => {
|
||
// verdict witness: task-only selected task, execution grants file.change only
|
||
const t = find(b, "task", "task-w1"); t.payload.missionRef = null; t.payload.dependencies = [];
|
||
b.selection.execution = restricted(restrictions(["file.change"], [], [ROOT]));
|
||
b.operation = fileOp("file.change", "src/main.mjs");
|
||
});
|
||
add("adm-assigned-task-without-work-read-read-op", "admission", refused("consulted-work-not-readable"), (b) => {
|
||
const t = find(b, "task", "task-w1"); t.payload.missionRef = null; t.payload.dependencies = [];
|
||
b.selection.execution = restricted(restrictions(["file.read"], [ROOT], []));
|
||
b.operation = fileOp("file.read", "src/main.mjs");
|
||
});
|
||
add("assigned-task-only-with-work-read", "positive", ALLOWED, (b) => {
|
||
// positive control: same task-only context with work.read granted
|
||
const t = find(b, "task", "task-w1"); t.payload.missionRef = null; t.payload.dependencies = [];
|
||
b.selection.execution = restricted(restrictions(["work.read", "file.change"], [], [ROOT]));
|
||
b.operation = fileOp("file.change", "src/main.mjs");
|
||
});
|
||
add("prop-task-only-contexts-without-work-read", "proposal", proposalRefused("requester-lacks-original-scope-authority", "consulted-work-not-readable"), (b) => {
|
||
// verdict witness: every task is reference-free; both requester executions grant assignment.change only
|
||
makeProposal(b); clearWorkReferences(b);
|
||
b.proposal.requesterContexts.original.execution = restricted(restrictions(["assignment.change"], [], []));
|
||
b.proposal.requesterContexts.target.execution = restricted(restrictions(["assignment.change"], [], []));
|
||
});
|
||
add("prop-task-only-target-context-without-work-read", "proposal", proposalRefused("requester-lacks-target-scope-authority", "consulted-work-not-readable"), (b) => {
|
||
makeProposal(b); clearWorkReferences(b);
|
||
b.proposal.requesterContexts.original.execution = restricted(restrictions(["work.read", "assignment.change"], [], []));
|
||
b.proposal.requesterContexts.target.execution = restricted(restrictions(["assignment.change"], [], []));
|
||
});
|
||
add("prop-task-only-contexts-with-work-read", "proposal", UNRESOLVED, (b) => {
|
||
// positive control: task-only contexts with work.read reach the runtime-reconciliation step
|
||
makeProposal(b); clearWorkReferences(b);
|
||
b.proposal.requesterContexts.original.execution = restricted(restrictions(["work.read", "assignment.change"], [], []));
|
||
b.proposal.requesterContexts.target.execution = restricted(restrictions(["work.read", "assignment.change"], [], []));
|
||
});
|
||
// F3: the issuer ceiling narrows both requester calculations; work access is rechecked
|
||
add("prop-issuer-denies-work-read", "proposal", proposalRefused("requester-lacks-original-scope-authority", "consulted-work-not-readable"), (b) => {
|
||
// verdict witness: delegated operation within the ceiling, but the narrowed intersection lacks work.read
|
||
makeProposal(b); b.delegationInputs[0].issuerCeiling = restrictions(["assignment.change"], [ROOT], [ROOT]);
|
||
});
|
||
add("prop-issuer-denies-work-read-task-only", "proposal", proposalRefused("requester-lacks-original-scope-authority", "consulted-work-not-readable"), (b) => {
|
||
makeProposal(b); clearWorkReferences(b); b.delegationInputs[0].issuerCeiling = restrictions(["assignment.change"], [ROOT], [ROOT]);
|
||
});
|
||
add("prop-issuer-grants-work-read", "proposal", UNRESOLVED, (b) => {
|
||
// positive control: the minimal issuer ceiling that keeps work.read passes the recheck
|
||
makeProposal(b); b.delegationInputs[0].issuerCeiling = restrictions(["work.read", "assignment.change"], [ROOT], [ROOT]);
|
||
});
|
||
add("prop-subject-task-authorization-undeclared", "proposal", proposalRefused("intent-not-current", "authorization-undeclared", "missing-state"), (b) => {
|
||
makeProposal(b); find(b, "task", "task-w1").authorizationRef = AUTH_UNDECLARED;
|
||
});
|
||
// F4: revision-exact cycle graphs
|
||
add("cycle-acyclic-versioned-history", "positive", ALLOWED, (b) => { versionedHistory(b); });
|
||
add("cycle-acyclic-versioned-history-file-change", "positive", ALLOWED, (b) => {
|
||
versionedHistory(b); b.operation = fileOp("file.change", "src/main.mjs");
|
||
});
|
||
add("cycle-dependency-cross-revision", "cycle", invalid("dependency-cycle"), (b) => {
|
||
// a real cycle at exact revisions: task-w1 rev 2 <-> task-w1-dep rev 2
|
||
const { t2, d2 } = versionedHistory(b); t2.payload.dependencies = [ref(d2)];
|
||
});
|
||
add("cycle-mission-parent-previous-revision-not-a-cycle", "ownership", invalid("mission-parent-scope-mismatch"), (b) => {
|
||
// m-w1 rev 2 names m-w1 rev 1 as parent: an identity-merged graph would see a self-loop,
|
||
// the exact graph is acyclic, so the later ownership rule (parent must be a project mission) fires
|
||
const mw1 = find(b, "mission", "m-w1");
|
||
const mw1r2 = reviseRecord(b, "mission", "m-w1", { parentMissionRef: ref(mw1) });
|
||
find(b, "task", "task-w1").payload.missionRef = ref(mw1r2);
|
||
});
|
||
add("cycle-mission-parent-old-revision-only", "cycle", invalid("mission-parent-cycle"), (b) => {
|
||
// the self-parent cycle lives entirely in revision 1; a clean revision 2 does not repair it
|
||
const mw1 = find(b, "mission", "m-w1");
|
||
mw1.payload.parentMissionRef = ref(mw1);
|
||
const mw1r2 = reviseRecord(b, "mission", "m-w1", { parentMissionRef: ref(find(b, "mission", "m-p1")) });
|
||
find(b, "task", "task-w1").payload.missionRef = ref(mw1r2);
|
||
});
|
||
// F5: deterministic first failure — record order and message precedence
|
||
add("shape-order-forward", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
// verdict witness: agent-a invalid status enum, agent-b unknown payload field; input order forward
|
||
find(b, "agent-definition", "agent-a").payload.status = "sleeping";
|
||
find(b, "agent-definition", "agent-b").payload.extra = true;
|
||
});
|
||
add("shape-order-reversed", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
find(b, "agent-definition", "agent-a").payload.status = "sleeping";
|
||
find(b, "agent-definition", "agent-b").payload.extra = true;
|
||
b.records.reverse();
|
||
});
|
||
add("shape-order-malformed-record-sorts-last", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
// a record without a readable identity is validated after every identified record
|
||
find(b, "agent-definition", "agent-b").payload.status = "sleeping";
|
||
b.records.unshift({ schemaVersion: 1 });
|
||
});
|
||
add("shape-order-only-malformed-record", "shape", invalid("shape-missing-field"), (b) => {
|
||
b.records.unshift({ schemaVersion: 1 });
|
||
});
|
||
// FI-ROCKO-7: stable traversal of the declared inventories (registries, artifacts,
|
||
// authorizations, delegationInputs) at the shape, profile, digest, identity and structural
|
||
// stages. Each pair carries two different failures; the typed key decides which is first,
|
||
// never input position. Forward/reversed pairs share one expectation.
|
||
function orderRegistries(b) {
|
||
// ap-full (key agent-policy/ap-full) sorts before pp-p1 (project-policy/pp-p1): a missing
|
||
// field on ap-full precedes an unknown field on pp-p1 wherever the two sit
|
||
delete findReg(b, "ap-full").digest;
|
||
findReg(b, "pp-p1").extra = true;
|
||
}
|
||
add("shape-order-registries-forward", "shape", invalid("shape-missing-field"), (b) => { orderRegistries(b); });
|
||
add("shape-order-registries-reversed", "shape", invalid("shape-missing-field"), (b) => { orderRegistries(b); b.registries.reverse(); });
|
||
function orderRegistriesSwapped(b) {
|
||
// the same two failures with the roles swapped: the first key now carries the unknown field
|
||
findReg(b, "ap-full").extra = true;
|
||
delete findReg(b, "pp-p1").digest;
|
||
}
|
||
add("shape-order-registries-swapped-forward", "shape", invalid("shape-unknown-field"), (b) => { orderRegistriesSwapped(b); });
|
||
add("shape-order-registries-swapped-reversed", "shape", invalid("shape-unknown-field"), (b) => { orderRegistriesSwapped(b); b.registries.reverse(); });
|
||
add("shape-order-registries-malformed-sorts-last", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
// an entry without a readable registry/id sorts after every keyed entry; the keyed failure is first
|
||
findReg(b, "role-writer").registry = "no-such-registry";
|
||
b.registries.unshift({ registry: 7 });
|
||
});
|
||
add("shape-order-registries-only-malformed", "shape", invalid("shape-type-mismatch"), (b) => { b.registries.unshift("not-an-entry"); });
|
||
function orderArtifacts(b) {
|
||
// run-1/art-1 sorts before run-1/art-2: a digest pattern failure on art-1 precedes an unknown field on art-2
|
||
b.artifacts[0].digest = "sha256:short";
|
||
b.artifacts[1].extra = true;
|
||
}
|
||
add("shape-order-artifacts-forward", "shape", invalid("shape-pattern-mismatch"), (b) => { orderArtifacts(b); });
|
||
add("shape-order-artifacts-reversed", "shape", invalid("shape-pattern-mismatch"), (b) => { orderArtifacts(b); b.artifacts.reverse(); });
|
||
function orderArtifactsSwapped(b) {
|
||
b.artifacts[0].extra = true;
|
||
b.artifacts[1].digest = "sha256:short";
|
||
}
|
||
add("shape-order-artifacts-swapped-forward", "shape", invalid("shape-unknown-field"), (b) => { orderArtifactsSwapped(b); });
|
||
add("shape-order-artifacts-swapped-reversed", "shape", invalid("shape-unknown-field"), (b) => { orderArtifactsSwapped(b); b.artifacts.reverse(); });
|
||
add("shape-order-artifacts-malformed-sorts-last", "shape", invalid("shape-pattern-mismatch"), (b) => {
|
||
b.artifacts[1].digest = "sha256:short";
|
||
b.artifacts.unshift({ runId: 1 });
|
||
});
|
||
function orderAuthorizations(b) {
|
||
// two pattern failures: the locator is the lower value either way (asserted in ../resolve.test.mjs)
|
||
b.authorizations = [AUTH_2.replace("-4000-", "-1000-"), `${AUTH}-x`];
|
||
}
|
||
add("shape-order-authorizations-forward", "shape", invalid("shape-pattern-mismatch"), (b) => { orderAuthorizations(b); });
|
||
add("shape-order-authorizations-reversed", "shape", invalid("shape-pattern-mismatch"), (b) => { orderAuthorizations(b); b.authorizations.reverse(); });
|
||
add("shape-order-authorizations-malformed-sorts-last", "shape", invalid("shape-pattern-mismatch"), (b) => {
|
||
// a non-string entry has no readable key and is validated after every string entry
|
||
b.authorizations = [7, `${AUTH}-x`];
|
||
});
|
||
function orderDelegationInputs(b) {
|
||
// decision d-deleg-b (W1) sorts before d-deleg-reg (W1) by id: a missing field on the
|
||
// d-deleg-b input precedes an enum failure on the d-deleg-reg input
|
||
makeProposal(b);
|
||
const d = withRegistrationDelegation(b);
|
||
b.delegationInputs.find((x) => x.decisionRef.id === "d-deleg-b").mode = "parent-delegation";
|
||
delete b.delegationInputs.find((x) => x.decisionRef.id === "d-deleg-reg").issuerCeiling;
|
||
return d;
|
||
}
|
||
add("shape-order-delegation-inputs-forward", "shape", invalid("shape-enum-mismatch"), (b) => { orderDelegationInputs(b); });
|
||
add("shape-order-delegation-inputs-reversed", "shape", invalid("shape-enum-mismatch"), (b) => { orderDelegationInputs(b); b.delegationInputs.reverse(); });
|
||
function orderDelegationInputsSwapped(b) {
|
||
makeProposal(b);
|
||
withRegistrationDelegation(b);
|
||
delete b.delegationInputs.find((x) => x.decisionRef.id === "d-deleg-b").issuerCeiling;
|
||
b.delegationInputs.find((x) => x.decisionRef.id === "d-deleg-reg").mode = "parent-delegation";
|
||
}
|
||
add("shape-order-delegation-inputs-swapped-forward", "shape", invalid("shape-missing-field"), (b) => { orderDelegationInputsSwapped(b); });
|
||
add("shape-order-delegation-inputs-swapped-reversed", "shape", invalid("shape-missing-field"), (b) => { orderDelegationInputsSwapped(b); b.delegationInputs.reverse(); });
|
||
add("shape-order-delegation-inputs-malformed-sorts-last", "shape", invalid("shape-enum-mismatch"), (b) => {
|
||
makeProposal(b);
|
||
b.delegationInputs[0].mode = "parent-delegation";
|
||
b.delegationInputs.unshift({ decisionRef: null });
|
||
});
|
||
// stage precedence across families is fixed: records, registries, artifacts, authorizations, delegationInputs
|
||
add("shape-order-family-precedence-registries-before-artifacts", "shape", invalid("shape-unknown-field"), (b) => {
|
||
b.artifacts[0].digest = "sha256:short";
|
||
findReg(b, "pp-p2").extra = true;
|
||
});
|
||
add("shape-order-family-precedence-artifacts-before-authorizations", "shape", invalid("shape-pattern-mismatch"), (b) => {
|
||
b.authorizations = [7];
|
||
b.artifacts[1].digest = "sha256:short";
|
||
});
|
||
add("shape-order-family-precedence-authorizations-before-delegation-inputs", "shape", invalid("shape-type-mismatch"), (b) => {
|
||
makeProposal(b);
|
||
b.delegationInputs[0].mode = "parent-delegation";
|
||
b.authorizations = [7];
|
||
});
|
||
// profile stage: the first strict-profile violation in stable order, after every shape failure
|
||
function orderProfileRegistries(b) {
|
||
// ap-reader sorts before role-reader: two one-final-LF ids, the lower key is the locator either way
|
||
findReg(b, "role-reader").id += LF1;
|
||
findReg(b, "ap-reader").id += LF1;
|
||
}
|
||
add("profile-order-registries-forward", "profile", PROFILE_MISMATCH, (b) => { orderProfileRegistries(b); });
|
||
add("profile-order-registries-reversed", "profile", PROFILE_MISMATCH, (b) => { orderProfileRegistries(b); b.registries.reverse(); });
|
||
add("profile-order-shape-precedes-profile-registries", "shape", invalid("shape-unknown-field"), (b) => {
|
||
// a shape failure on a later key still precedes a profile violation on an earlier key
|
||
findReg(b, "ap-reader").id += LF1;
|
||
findReg(b, "pp-p2").extra = true;
|
||
});
|
||
add("profile-order-artifacts-reversed", "profile", PROFILE_MISMATCH, (b) => {
|
||
b.artifacts[0].runId += LF1; b.artifacts[1].artifactId += LF1; b.artifacts.reverse();
|
||
});
|
||
add("profile-order-authorizations-reversed", "profile", PROFILE_MISMATCH, (b) => {
|
||
b.authorizations = [`${AUTH_2}${LF1}`, `${AUTH}${LF1}`];
|
||
});
|
||
add("profile-order-shape-precedes-profile-authorizations", "shape", invalid("shape-pattern-mismatch"), (b) => {
|
||
// a shape failure on the higher value still precedes a profile violation on the lower one
|
||
b.authorizations = [`${AUTH}${LF1}`, `${AUTH_2}-x`];
|
||
});
|
||
// digest stage: content digests are checked in stable order after the profile stage
|
||
function orderDigests(b) {
|
||
// ap-full (unsupported content) sorts before role-writer (digest mismatch)
|
||
const apFull = findReg(b, "ap-full"); apFull.content.restrictions.readPaths = [{ root: "workspace", path: "caf\u00e9" }];
|
||
findReg(b, "role-writer").content.restrictions.operations.push("command.execute");
|
||
}
|
||
add("shape-order-registry-digest-forward", "profile", invalid("mock-content-unsupported", "unsupported-capability"), (b) => { orderDigests(b); });
|
||
add("shape-order-registry-digest-reversed", "profile", invalid("mock-content-unsupported", "unsupported-capability"), (b) => { orderDigests(b); b.registries.reverse(); });
|
||
function orderDigestsSwapped(b) {
|
||
findReg(b, "ap-full").content.restrictions.operations.pop();
|
||
const rw = findReg(b, "role-writer"); rw.content.restrictions.readPaths = [{ root: "workspace", path: "caf\u00e9" }];
|
||
}
|
||
add("shape-order-registry-digest-swapped-forward", "profile", invalid("registry-digest-mismatch"), (b) => { orderDigestsSwapped(b); });
|
||
add("shape-order-registry-digest-swapped-reversed", "profile", invalid("registry-digest-mismatch"), (b) => { orderDigestsSwapped(b); b.registries.reverse(); });
|
||
add("shape-order-profile-precedes-digest-registries", "profile", PROFILE_MISMATCH, (b) => {
|
||
// a profile violation on a later key precedes a digest failure on an earlier key
|
||
findReg(b, "ap-full").content.restrictions.operations.pop();
|
||
findReg(b, "pp-p2").id += LF1;
|
||
});
|
||
add("shape-order-two-digest-mismatches-reversed", "profile", invalid("registry-digest-mismatch"), (b) => {
|
||
findReg(b, "role-writer").content.restrictions.operations.push("command.execute");
|
||
findReg(b, "role-reader").content.restrictions.operations.push("command.execute");
|
||
b.registries.reverse();
|
||
});
|
||
// identity stage: duplicates are detected in stable order across every family
|
||
add("dup-order-registries-precede-artifacts", "identity", invalid("duplicate-registry-identity"), (b) => {
|
||
// registries precede artifacts at the identity stage wherever the duplicates sit
|
||
b.artifacts.unshift({ ...ART_2, digest: fakeDigest("other") });
|
||
b.registries.unshift(clone(findReg(b, "role-writer")));
|
||
});
|
||
add("dup-order-delegation-inputs-reversed", "identity", invalid("duplicate-delegation-input"), (b) => {
|
||
// the duplicate pair is listed before a valid lower-key input; the duplicate is still found
|
||
makeProposal(b); withRegistrationDelegation(b); b.delegationInputs.reverse(); b.delegationInputs.unshift(clone(b.delegationInputs[0]));
|
||
});
|
||
// structural stage: the first missing delegation decision is the lowest key, not the first listed
|
||
function orderStructuralDelegationInputs(b) {
|
||
makeProposal(b);
|
||
withRegistrationDelegation(b);
|
||
for (const d of b.delegationInputs) d.decisionRef.revision = 9;
|
||
}
|
||
add("struct-order-delegation-inputs-forward", "structure", invalid("record-reference-missing", "missing-state"), (b) => { orderStructuralDelegationInputs(b); });
|
||
add("struct-order-delegation-inputs-reversed", "structure", invalid("record-reference-missing", "missing-state"), (b) => { orderStructuralDelegationInputs(b); b.delegationInputs.reverse(); });
|
||
// FI-ROCKO-8 (FI-FILBERT-7 R5-1): the equal-key / unkeyed tie-break is a total ordering
|
||
// form over the strict-JSON domain, not the ASCII-only mock-digest canonicalizer, so
|
||
// distinct non-ASCII entries never collapse to one form. Recipes are the verdict's
|
||
// witnesses rebuilt from the base bundle, forward and reversed.
|
||
for (const family of ["records", "registries", "artifacts", "delegationInputs"]) {
|
||
const slug = family === "delegationInputs" ? "delegation-inputs" : family;
|
||
// two unkeyed entries, both non-ASCII: the string form ("\"…") sorts before the object form ("{…")
|
||
add(`shape-order-unkeyed-unicode-${slug}-forward`, "shape", invalid("shape-type-mismatch"), (b) => { b[family] = [{ extra: "\u00e9" }, "\u00e9"]; });
|
||
add(`shape-order-unkeyed-unicode-${slug}-reversed`, "shape", invalid("shape-type-mismatch"), (b) => { b[family] = ["\u00e9", { extra: "\u00e9" }]; });
|
||
}
|
||
function equalKeyUnicodeRecords(b) {
|
||
// two agent-a copies with the same kind/id/scope/revision and a legitimate non-ASCII displayName;
|
||
// sorted-key form: the copy with the extra payload key ("extra" < "harnessRef") sorts first
|
||
const a = find(b, "agent-definition", "agent-a");
|
||
a.payload.displayName = "Caf\u00e9";
|
||
const c = clone(a);
|
||
a.payload.status = "invalid-status";
|
||
c.payload.extra = true;
|
||
b.records.splice(b.records.indexOf(a) + 1, 0, c);
|
||
}
|
||
add("shape-order-equal-key-unicode-records-forward", "shape", invalid("shape-unknown-field"), (b) => { equalKeyUnicodeRecords(b); });
|
||
add("shape-order-equal-key-unicode-records-reversed", "shape", invalid("shape-unknown-field"), (b) => { equalKeyUnicodeRecords(b); b.records.reverse(); });
|
||
function equalKeyUnicodeRegistries(b) {
|
||
// two ap-full copies with identical registry/id/revision/digest and a non-ASCII logical path;
|
||
// sorted-key form: the copy with the extra restrictions key ("extra" < "network") sorts first
|
||
const e = findReg(b, "ap-full");
|
||
e.content.restrictions.readPaths = [{ root: "workspace", path: "caf\u00e9" }];
|
||
const c = clone(e);
|
||
e.content.restrictions.network = "everything";
|
||
c.content.restrictions.extra = true;
|
||
b.registries.splice(b.registries.indexOf(e) + 1, 0, c);
|
||
}
|
||
add("shape-order-equal-key-unicode-registries-forward", "shape", invalid("shape-unknown-field"), (b) => { equalKeyUnicodeRegistries(b); });
|
||
add("shape-order-equal-key-unicode-registries-reversed", "shape", invalid("shape-unknown-field"), (b) => { equalKeyUnicodeRegistries(b); b.registries.reverse(); });
|
||
// Unicode positives stay allowed: the ordering form is not a validity judgement
|
||
add("positive-unicode-display-name-allowed", "positive", ALLOWED, (b) => { find(b, "agent-definition", "agent-a").payload.displayName = "Caf\u00e9 \u{1F600}"; });
|
||
add("prop-message-precedes-requester-admission", "proposal", proposalRefused("message-is-not-authority", "message-is-not-authority"), (b) => {
|
||
// verdict witness: no delegation plus a message; the original requester context lacks assignment.change
|
||
makeProposal(b); b.proposal.delegationRef = null; b.proposal.message = "owner said it is fine";
|
||
b.proposal.requesterContexts.original.execution = restricted(restrictions(["work.read"], [ROOT], []));
|
||
});
|
||
add("prop-delegation-missing-after-requester-admission", "proposal", proposalRefused("requester-lacks-original-scope-authority", "operation-not-permitted"), (b) => {
|
||
// without a message the missing delegation stays at step 3, after the requester's original-scope admission
|
||
makeProposal(b); b.proposal.delegationRef = null;
|
||
b.proposal.requesterContexts.original.execution = restricted(restrictions(["work.read"], [ROOT], []));
|
||
});
|
||
add("prop-message-after-structural-failure", "proposal", invalid("requester-context-mismatch"), (b) => {
|
||
// a structural proposal failure still precedes the message rule
|
||
makeProposal(b); b.proposal.delegationRef = null; b.proposal.message = "owner said it is fine";
|
||
b.proposal.requesterContexts.original.assignmentRef = ref(find(b, "assignment", "asg-a-w1"));
|
||
});
|
||
add("prop-message-precedes-stale-subject", "proposal", proposalRefused("message-is-not-authority", "message-is-not-authority"), (b) => {
|
||
// current-head admission (stale subject assignment) is not structural validation: the message rule comes first
|
||
makeProposal(b); b.proposal.delegationRef = null; b.proposal.message = "owner said it is fine";
|
||
reviseRecord(b, "assignment", "asg-a-w1", {});
|
||
});
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Raw lexical cases (byte-exact)
|
||
// ---------------------------------------------------------------------------
|
||
|
||
const rawCases = [];
|
||
function addRaw(name, bytes, expect, byteOffset = null) {
|
||
rawCases.push({ name, bytes: bytes instanceof Uint8Array ? bytes : new TextEncoder().encode(bytes), expect, byteOffset });
|
||
}
|
||
const PARSE = { exit: 2, result: "invalid", reason: "invalid-request", rule: "input-parse-failed", proposalRule: null };
|
||
const baseText = JSON.stringify(buildBase(), null, 2);
|
||
addRaw("raw-duplicate-key", '{"bundleVersion": 1, "bundleVersion": 1}', PARSE, 21);
|
||
addRaw("raw-bom-prefix", `\ufeff${baseText}`, PARSE, 0);
|
||
addRaw("raw-trailing-content", `${baseText}\n{}`, PARSE, baseText.length + 1);
|
||
addRaw("raw-fraction-number", baseText.replace('"bundleVersion": 1', '"bundleVersion": 1.0'), PARSE, 21);
|
||
addRaw("raw-negative-zero", baseText.replace('"bundleVersion": 1', '"bundleVersion": -0'), PARSE, 21);
|
||
addRaw("raw-exponent", baseText.replace('"bundleVersion": 1', '"bundleVersion": 1e0'), PARSE, 21);
|
||
addRaw("raw-unsafe-integer", baseText.replace('"bundleVersion": 1', '"bundleVersion": 9007199254740992'), PARSE, 21);
|
||
addRaw("raw-depth-33", `${"[".repeat(33)}${"]".repeat(33)}`, PARSE, 32);
|
||
addRaw("raw-invalid-utf8", new Uint8Array([0x7b, 0x22, 0x61, 0xc0, 0x80, 0x22, 0x3a, 0x31, 0x7d]), PARSE, 3);
|
||
addRaw("raw-utf8-surrogate", new Uint8Array([0x7b, 0x22, 0x61, 0xed, 0xa0, 0x80, 0x22, 0x3a, 0x31, 0x7d]), PARSE, 3);
|
||
addRaw("raw-lone-escape-surrogate", '{"a": "\\ud800"}', PARSE, 7);
|
||
addRaw("raw-raw-control-in-string", '{"a": "x\u0001y"}', PARSE, 8);
|
||
addRaw("raw-empty-file", "", PARSE, 0);
|
||
addRaw("raw-not-object", "[]", invalid("shape-type-mismatch"));
|
||
addRaw("raw-proto-key", '{"__proto__": {"polluted": true}}', invalid("shape-unknown-field"));
|
||
addRaw("raw-constructor-key", '{"constructor": {"prototype": {"polluted": true}}}', invalid("shape-unknown-field"));
|
||
addRaw("raw-string-too-long", `{"a": "${"x".repeat(4097)}"}`, PARSE, 6);
|
||
addRaw("raw-array-too-long", `[${new Array(1025).fill("1").join(",")}]`, PARSE, 0);
|
||
{
|
||
// exactly 1 MiB + 1 bytes: refused by the CLI size gate (exit 2 input-too-large) before parsing
|
||
const big = new Uint8Array(1024 * 1024 + 1);
|
||
big.fill(0x20);
|
||
big[0] = 0x5b; big[big.length - 1] = 0x5d; // "[ ... ]"
|
||
addRaw("raw-oversize-file", big, { exit: 2, result: "invalid", reason: "invalid-request", rule: "input-too-large", proposalRule: null });
|
||
}
|
||
|
||
// ---------------------------------------------------------------------------
|
||
// Output
|
||
// ---------------------------------------------------------------------------
|
||
|
||
function writeJson(path, value) {
|
||
writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
|
||
}
|
||
|
||
function main() {
|
||
const out = process.argv[2];
|
||
if (!out) {
|
||
process.stderr.write("usage: build-fixtures.mjs <output-dir>\n");
|
||
process.exit(2);
|
||
}
|
||
mkdirSync(join(out, "bundles"), { recursive: true });
|
||
mkdirSync(join(out, "raw"), { recursive: true });
|
||
mkdirSync(join(out, "demo"), { recursive: true });
|
||
const names = new Set();
|
||
const index = [];
|
||
for (const c of cases) {
|
||
if (names.has(c.name)) throw new Error(`duplicate case ${c.name}`);
|
||
names.add(c.name);
|
||
writeJson(join(out, "bundles", `${c.name}.json`), c.bundle);
|
||
index.push({ name: c.name, group: c.group, file: `bundles/${c.name}.json`, raw: false, expect: c.expect });
|
||
}
|
||
for (const c of rawCases) {
|
||
if (names.has(c.name)) throw new Error(`duplicate case ${c.name}`);
|
||
names.add(c.name);
|
||
writeFileSync(join(out, "raw", `${c.name}.json`), c.bytes);
|
||
index.push({ name: c.name, group: "lexical", file: `raw/${c.name}.json`, raw: true, expect: { ...c.expect, byteOffset: c.byteOffset } });
|
||
}
|
||
index.sort((a, b) => (a.name < b.name ? -1 : 1));
|
||
writeJson(join(out, "index.json"), { generator: "build-fixtures.mjs", count: index.length, cases: index });
|
||
// Owner demo copies
|
||
for (const name of ["demo-read-w1", "demo-file-change-src", "demo-change-w1", "adm-registration-revoked", "prop-message-is-not-authority"]) {
|
||
writeJson(join(out, "demo", `${name}.json`), cases.find((c) => c.name === name).bundle);
|
||
}
|
||
process.stdout.write(`${index.length} cases written to ${out}\n`);
|
||
}
|
||
|
||
main();
|