Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e7912e1
feat(review): read-only tool profile for every review pass, role policy
constantin-jais Oct 24, 2026
65216fb
test(review): vector 2 of ADR-0045, every pass holds read-only tools …
constantin-jais Oct 24, 2026
081eb75
test(review): vector 1 of ADR-0045, a trapped verdict obtains no effe…
constantin-jais Oct 24, 2026
a0d4f65
test(review): vector 3 of ADR-0045, free text never decides (INV-c)
constantin-jais Oct 24, 2026
28dfe9c
feat(review): INV-a to INV-c met in the activation table, refusal sta…
constantin-jais Oct 24, 2026
73903fa
fix(review): plan option values and outputDir under policy, no write …
constantin-jais Oct 24, 2026
23e9e98
fix(review): refuse a reply with more than one verdict block
constantin-jais Oct 24, 2026
1c45731
Merge origin/migrate/recover-code into work/wp3-inv-vectors
constantin-jais Oct 26, 2026
5554cfd
fix(review): untrusted text in pass errors decides nothing and reache…
constantin-jais Oct 26, 2026
3b5790a
fix(review): a proof is a regular vector test file; the activation ta…
constantin-jais Oct 26, 2026
6c7d674
feat(review): sixth activation condition, pass-read-confined, refused…
constantin-jais Oct 26, 2026
cb112ff
fix(review): a reply that could show a second verdict is refused, sha…
constantin-jais Oct 26, 2026
6e8ed94
fix(review): open the journal once per campaign and append through th…
constantin-jais Oct 26, 2026
90e57ff
fix(review): format characters refused in option values, neutralised …
constantin-jais Oct 26, 2026
4949625
fix(review): one exit-code policy for the command line, pinned withou…
constantin-jais Oct 26, 2026
a75da2d
Merge origin/migrate/recover-code into work/wp3-inv-vectors
constantin-jais Oct 26, 2026
512e6c0
fix(review): adaptive-attacks belongs to WP-7 after the WP-4 split
constantin-jais Oct 26, 2026
993692c
fix(review): one synchronous write per journal line, never two writes…
constantin-jais Oct 26, 2026
374143e
fix(review): tilde fences and indented JSON blocks also refuse the ve…
constantin-jais Oct 26, 2026
8c5709f
fix(review): a journal line is written whole, a stalled write is an e…
constantin-jais Oct 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions tools/review/authority-boundary.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ describe("the default path does not load the package", () => {
}
});

// CA-32: the default driver applies the activation table itself, and the
// command line hands neither driver a table of its own.
test("the default driver takes its refusal from fanout-activation.ts, and fanout.ts passes no table", async () => {
const run = await read(join(REVIEW_DIR, "fanout-run.ts"));
expect(grepCount(run, /from "\.\/fanout-activation"/)).toBe(1);
expect(grepCount(run, /defaultDriverRefusal\(conditions\)/)).toBe(1);
expect(
grepCount(
run,
/conditions: readonly ActivationCondition\[\] = LANGGRAPH_ACTIVATION_CONDITIONS/,
),
).toBe(1);
const cli = await read(join(REVIEW_DIR, "fanout.ts"));
expect(grepCount(cli, /runCampaignDirect\(input\)/)).toBe(1);
expect(grepCount(cli, /runCampaignDirect\([^)]*,/)).toBe(0);
expect(grepCount(cli, /satisfied|ALL_CONDITIONS_MET_FOR_TESTS/)).toBe(0);
});

test("no other orchestrator source imports the graph driver", async () => {
for (const name of orchestratorSources()) {
if (name === "fanout.ts" || name === "fanout-graph.ts") continue;
Expand Down
135 changes: 121 additions & 14 deletions tools/review/fanout-activation.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { describe, expect, test } from "bun:test";
import { mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { mkdirSync, mkdtempSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join, relative } from "node:path";
import {
type ActivationCondition,
ActivationRefusedError,
activationRefusal,
assertNoPublisherTelemetry,
defaultDriverRefusal,
isProofInRepository,
LANGGRAPH_ACTIVATION_CONDITIONS,
publisherTelemetryVariables,
Expand All @@ -27,15 +29,80 @@ const condition = (overrides: Partial<ActivationCondition>): ActivationCondition
});

describe("activation conditions", () => {
test("today every condition of ADR-0045 and ADR-0058 is unmet, each owned by a work package", () => {
test("the conditions of WP-4 and WP-7 are still unmet, so the refusal still stands", () => {
// WP-4 was split on 2026-10-26: CA-31 (adaptive attacks) moved to WP-7.
expect(unmetConditions().map((c) => `${c.id}:${c.workPackage}`)).toEqual([
"inv-a:WP-3",
"inv-b:WP-3",
"inv-c:WP-3",
"telemetry-egress-refused:WP-4",
"adaptive-attacks:WP-4",
"adaptive-attacks:WP-7",
"pass-read-confined:WP-4",
]);
expect(LANGGRAPH_ACTIVATION_CONDITIONS).toHaveLength(5);
expect(LANGGRAPH_ACTIVATION_CONDITIONS).toHaveLength(6);
expect(activationRefusal()).not.toBeNull();
});

test("the sixth condition is the owner's decision of 2026-10-24, citing CA-32, unmet", () => {
const sixth = LANGGRAPH_ACTIVATION_CONDITIONS[5];
expect(sixth).toEqual({
id: "pass-read-confined",
requirement: "a review pass cannot read outside its detached worktree, enforced outside pi",
source:
"owner decision of 2026-10-24 (sixth activation condition, applied to both drivers), CA-32",
workPackage: "WP-4",
satisfied: false,
evidence: null,
});
});

test("the default path is refused by the sixth condition alone, and says so", () => {
const message = defaultDriverRefusal() ?? "";
expect(message).toContain("the default runtime does not run real review passes yet");
expect(message).toContain("1 of 1 activation condition(s) that bind it unmet");
expect(message).toContain("- pass-read-confined:");
expect(message).not.toContain("telemetry-egress-refused");
expect(message).toContain("--dry-run is available on this runtime");
const confined = LANGGRAPH_ACTIVATION_CONDITIONS.map((c) =>
c.id === "pass-read-confined"
? { ...c, satisfied: true, evidence: "tools/review/vectors/inv-a-read-only-passes.test.ts" }
: c,
);
expect(defaultDriverRefusal(confined)).toBeNull();
expect(activationRefusal(confined)).not.toBeNull();
});

test("the default path refuses a table other than the canonical one", () => {
expect(() => defaultDriverRefusal([])).toThrow(ActivationRefusedError);
expect(() => defaultDriverRefusal(LANGGRAPH_ACTIVATION_CONDITIONS.slice(0, 5))).toThrow(
"activation table",
);
});

test("INV-a to INV-c are met, each by its vector in tools/review/vectors", () => {
const met = LANGGRAPH_ACTIVATION_CONDITIONS.filter((c) => !unmetConditions().includes(c));
expect(met.map((c) => [c.id, c.workPackage, c.evidence])).toEqual([
["inv-a", "WP-3", "tools/review/vectors/inv-a-read-only-passes.test.ts"],
["inv-b", "WP-3", "tools/review/vectors/inv-b-effect-arguments.test.ts"],
["inv-c", "WP-3", "tools/review/vectors/inv-c-closed-vocabulary.test.ts"],
]);
for (const c of met) expect(isProofInRepository(c.evidence ?? "")).toBe(true);
});

// F8: nothing that imports the module can mark a condition met at run time.
test("the table and each of its conditions are frozen", () => {
expect(Object.isFrozen(LANGGRAPH_ACTIVATION_CONDITIONS)).toBe(true);
for (const c of LANGGRAPH_ACTIVATION_CONDITIONS)
expect([c.id, Object.isFrozen(c)]).toEqual([c.id, true]);
const table = LANGGRAPH_ACTIVATION_CONDITIONS as ActivationCondition[];
const first = LANGGRAPH_ACTIVATION_CONDITIONS[0] as { satisfied: boolean };
expect(() => table.push(condition({ satisfied: true }))).toThrow(TypeError);
expect(() => table.splice(3)).toThrow(TypeError);
const unmetBefore = unmetConditions().length;
expect(() => {
for (const c of LANGGRAPH_ACTIVATION_CONDITIONS)
(c as { satisfied: boolean }).satisfied = true;
}).toThrow(TypeError);
expect(first.satisfied).toBe(true);
expect(unmetConditions()).toHaveLength(unmetBefore);
expect(activationRefusal()).not.toBeNull();
});

test("a condition that claims to be met without naming its proof stays unmet", () => {
Expand All @@ -52,7 +119,10 @@ describe("activation conditions", () => {

test("when every condition is met and proven there is nothing to refuse", () => {
const met = [
condition({ satisfied: true, evidence: "tools/review/fanout-activation.test.ts" }),
condition({
satisfied: true,
evidence: "tools/review/vectors/inv-b-effect-arguments.test.ts",
}),
];
expect(unmetConditions(met)).toEqual([]);
expect(activationRefusal(met)).toBeNull();
Expand All @@ -71,10 +141,45 @@ describe("activation conditions", () => {
test("a link inside the root to a file outside it is not a proof", () => {
const root = mkdtempSync(join(tmpdir(), "activation-root-"));
try {
writeFileSync(join(root, "inside.test.ts"), "");
symlinkSync("/etc/hosts", join(root, "outside-link.test.ts"));
expect(isProofInRepository("inside.test.ts", root)).toBe(true);
expect(isProofInRepository("outside-link.test.ts", root)).toBe(false);
const vectors = join(root, "tools", "review", "vectors");
mkdirSync(vectors, { recursive: true });
writeFileSync(join(vectors, "inside.test.ts"), "");
symlinkSync("/etc/hosts", join(vectors, "outside-link.test.ts"));
expect(isProofInRepository("tools/review/vectors/inside.test.ts", root)).toBe(true);
expect(isProofInRepository("tools/review/vectors/outside-link.test.ts", root)).toBe(false);
} finally {
rmSync(root, { recursive: true, force: true });
}
});

// F7: a proof is a regular test file under tools/review/vectors/, nothing else.
test.each([
["the vectors directory itself", "tools/review/vectors"],
["a directory named like a test", "tools/review/vectors/dir.test.ts"],
["an existing file of the root", "package.json"],
["a test outside the vectors directory", "tools/review/fanout-activation.test.ts"],
["a file of the vectors directory that is not a test", "tools/review/vectors/notes.ts"],
[
"a link in the vectors directory to a file inside the root",
"tools/review/vectors/alias.test.ts",
],
[
"a traversal back into the vectors directory",
"tools/review/../review/vectors/inside.test.ts",
],
])("%s is not a proof", (_name, path) => {
const root = mkdtempSync(join(tmpdir(), "activation-root-"));
try {
const vectors = join(root, "tools", "review", "vectors");
mkdirSync(join(vectors, "dir.test.ts"), { recursive: true });
writeFileSync(join(vectors, "inside.test.ts"), "");
writeFileSync(join(vectors, "notes.ts"), "");
writeFileSync(join(root, "package.json"), "{}");
writeFileSync(join(root, "tools", "review", "fanout-activation.test.ts"), "");
symlinkSync(join(root, "package.json"), join(vectors, "alias.test.ts"));
// The control: the same root accepts a real vector.
expect(isProofInRepository("tools/review/vectors/inside.test.ts", root)).toBe(true);
expect(isProofInRepository(path, root)).toBe(false);
} finally {
rmSync(root, { recursive: true, force: true });
}
Expand All @@ -89,9 +194,11 @@ describe("activation conditions", () => {

test("the refusal names each unmet condition with its source and its work package", () => {
const message = activationRefusal() ?? "";
expect(message).toContain("5 of 5 activation condition(s) unmet");
expect(message).toContain("ADR-0045 decision 1 (INV-a), CA-26; delivered by WP-3");
expect(message).toContain("3 of 6 activation condition(s) unmet");
expect(message).not.toContain("INV-a");
expect(message).toContain("ADR-0058 section 3 condition 4, CA-22; delivered by WP-4");
expect(message).toContain("ADR-0045 consequences (vector 4), CA-31; delivered by WP-7");
expect(message).toContain("- pass-read-confined:");
expect(message).toContain("--dry-run is available on this runtime");
});
});
Expand Down
121 changes: 102 additions & 19 deletions tools/review/fanout-activation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { existsSync, realpathSync } from "node:fs";
import { isAbsolute, resolve, sep } from "node:path";
import { lstatSync, realpathSync } from "node:fs";
import { join, resolve } from "node:path";

// Conditions under which the LangGraph runtime may run REAL review passes.
//
Expand All @@ -9,8 +9,15 @@ import { isAbsolute, resolve, sep } from "node:path";
// their red vectors pass (ADR-0045 decision 4), and ADR-0058 section 3
// condition 4 adds the egress vector: "a harness that does not produce this
// refusal does not activate the worker". Work package WP-2 ships the runtime;
// WP-3 and WP-4 ship the proofs. Until they do, the LangGraph path answers
// `--dry-run` and refuses everything else.
// WP-3 ships the proofs of INV-a to INV-c (tools/review/vectors). The
// conditions still unmet each name their own package: WP-4 the egress proof
// (telemetry-egress-refused) and the confinement proof (pass-read-confined),
// WP-7 the adaptive-attack proof (adaptive-attacks), since the owner split WP-4
// on 2026-10-26. Until every condition holds, the LangGraph path answers
// `--dry-run` and refuses everything else. The sixth
// condition (owner decision of 2026-10-24, CA-32) binds the default path too:
// it refuses real passes until a pass cannot read outside its worktree
// (`defaultDriverRefusal`, applied by `runCampaignDirect` itself).
//
// The table is code, not configuration: nothing in the environment or on the
// command line can mark a condition met. A condition flips to `satisfied: true`
Expand All @@ -20,16 +27,23 @@ import { isAbsolute, resolve, sep } from "node:path";

const REPO_ROOT = resolve(import.meta.dir, "..", "..");

// A proof is a test file of the vectors directory, named by its plain
// repository-relative path: not a directory, not any file that happens to
// exist (package.json), not a test elsewhere in the tree.
const PROOF_PATH_PATTERN = /^tools\/review\/vectors\/[A-Za-z0-9][A-Za-z0-9._-]*\.test\.ts$/;

/**
* True when `path` is repository-relative, exists, and stays under the root
* once links are followed: a link inside the root to a file outside it is not
* a proof. @internal `root` is a parameter for the tests only.
* True when `path` names a regular test file under tools/review/vectors/ of
* the repository: neither a link (a link inside the root to a file elsewhere
* is not a proof) nor a directory, and the path resolves to itself.
* @internal `root` is a parameter for the tests only.
*/
export function isProofInRepository(path: string, root: string = REPO_ROOT): boolean {
if (isAbsolute(path)) return false;
if (!PROOF_PATH_PATTERN.test(path)) return false;
const resolved = resolve(root, path);
if (!existsSync(resolved)) return false;
return realpathSync(resolved).startsWith(`${realpathSync(root)}${sep}`);
const entry = lstatSync(resolved, { throwIfNoEntry: false });
if (entry === undefined || !entry.isFile()) return false;
return realpathSync(resolved) === join(realpathSync(root), path);
}

export interface ActivationCondition {
Expand All @@ -43,31 +57,41 @@ export interface ActivationCondition {
readonly evidence: string | null;
}

export const LANGGRAPH_ACTIVATION_CONDITIONS: readonly ActivationCondition[] = [
/**
* The table, and each condition in it, frozen: no code that imports the
* module can mark a condition met or swap a proof at run time; changing the
* table is a change of this file, visible in review.
*/
function frozenTable(table: ActivationCondition[]): readonly ActivationCondition[] {
for (const condition of table) Object.freeze(condition);
return Object.freeze(table);
}

export const LANGGRAPH_ACTIVATION_CONDITIONS: readonly ActivationCondition[] = frozenTable([
{
id: "inv-a",
requirement:
"no untrusted string in front of a model that holds a write, execute or network tool",
source: "ADR-0045 decision 1 (INV-a), CA-26",
workPackage: "WP-3",
satisfied: false,
evidence: null,
satisfied: true,
evidence: "tools/review/vectors/inv-a-read-only-passes.test.ts",
},
{
id: "inv-b",
requirement: "provenance by value, policy on every effect argument",
source: "ADR-0045 decision 1 (INV-b), CA-27",
workPackage: "WP-3",
satisfied: false,
evidence: null,
satisfied: true,
evidence: "tools/review/vectors/inv-b-effect-arguments.test.ts",
},
{
id: "inv-c",
requirement: "quarantine output with a closed vocabulary",
source: "ADR-0045 decision 1 (INV-c), CA-28",
workPackage: "WP-3",
satisfied: false,
evidence: null,
satisfied: true,
evidence: "tools/review/vectors/inv-c-closed-vocabulary.test.ts",
},
{
id: "telemetry-egress-refused",
Expand All @@ -81,11 +105,47 @@ export const LANGGRAPH_ACTIVATION_CONDITIONS: readonly ActivationCondition[] = [
id: "adaptive-attacks",
requirement: "adaptive attack suite, utility and attack success rates reported together",
source: "ADR-0045 consequences (vector 4), CA-31",
workPackage: "WP-7",
satisfied: false,
evidence: null,
},
{
id: "pass-read-confined",
requirement: "a review pass cannot read outside its detached worktree, enforced outside pi",
source:
"owner decision of 2026-10-24 (sixth activation condition, applied to both drivers), CA-32",
workPackage: "WP-4",
satisfied: false,
evidence: null,
},
];
]);

/** Thrown by a driver that refuses real passes; the command line prints it and exits 2. */
export class ActivationRefusedError extends Error {
override readonly name = "ActivationRefusedError";
}

const CANONICAL_CONDITION_IDS = LANGGRAPH_ACTIVATION_CONDITIONS.map((condition) => condition.id);

/**
* Throws unless `conditions` lists exactly the canonical conditions, in order:
* an empty or partial table would otherwise have nothing left to refuse.
*/
export function assertCanonicalTable(conditions: readonly ActivationCondition[]): void {
const ids = conditions.map((condition) => condition.id);
if (
ids.length !== CANONICAL_CONDITION_IDS.length ||
ids.some((id, index) => id !== CANONICAL_CONDITION_IDS[index])
) {
throw new ActivationRefusedError(
`the activation table must list exactly the conditions of fanout-activation.ts (${CANONICAL_CONDITION_IDS.join(", ")}); got: ${ids.join(", ") || "none"}`,
);
}
}

// The default driver loads no package, so the conditions on the package do not
// bind it; the confinement of a pass does, whichever driver runs it (CA-32).
const DEFAULT_DRIVER_CONDITION_IDS: readonly string[] = ["pass-read-confined"];

/**
* A condition counts as met only when it says so AND names a proof that exists
Expand Down Expand Up @@ -117,7 +177,30 @@ export function activationRefusal(
(condition) =>
`- ${condition.id}: ${condition.requirement} (${condition.source}; delivered by ${condition.workPackage})`,
),
"--dry-run is available on this runtime; omit --runtime to run the default path",
"--dry-run is available on this runtime",
].join("\n");
}

/**
* The refusal message for real passes on the default path, or null when the
* conditions that bind it hold. The table must be the canonical one.
*/
export function defaultDriverRefusal(
conditions: readonly ActivationCondition[] = LANGGRAPH_ACTIVATION_CONDITIONS,
): string | null {
assertCanonicalTable(conditions);
const binding = conditions.filter((condition) =>
DEFAULT_DRIVER_CONDITION_IDS.includes(condition.id),
);
const unmet = unmetConditions(binding);
if (unmet.length === 0) return null;
return [
`the default runtime does not run real review passes yet: ${unmet.length} of ${binding.length} activation condition(s) that bind it unmet`,
...unmet.map(
(condition) =>
`- ${condition.id}: ${condition.requirement} (${condition.source}; delivered by ${condition.workPackage})`,
),
"--dry-run is available on this runtime",
].join("\n");
}

Expand Down
Loading