Skip to content

Commit 9dfb2e7

Browse files
committed
Add warden trust-review director for permission and plugin paths
1 parent bf79cc0 commit 9dfb2e7

10 files changed

Lines changed: 203 additions & 11 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ Corbits Code keeps repository guidance and the closed director fleet separate:
198198
- `src/agent/directors/` — closed spawn catalog (`directorProfiles()`). Skywalker
199199
is the primary orchestrator; spawnable directors include builder, explorer,
200200
counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper,
201-
emil, rand, shakespeare, testsmith, and tester. Closed ids cannot be
201+
emil, rand, shakespeare, testsmith, tester, and warden. Closed ids cannot be
202202
overridden by plugins or local files.
203203
- `.agents/agents/` — optional local profile additions; this directory is not
204204
required and may be absent

‎src/agent/directors/registry.test.ts‎

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ import {
1313
} from "./registry.js";
1414

1515
describe("director registry", () => {
16-
test("closed set has exactly 16 directors", () => {
17-
expect(DIRECTOR_IDS).toHaveLength(16);
18-
expect(listDirectors()).toHaveLength(16);
16+
test("closed set has exactly 17 directors", () => {
17+
expect(DIRECTOR_IDS).toHaveLength(17);
18+
expect(listDirectors()).toHaveLength(17);
1919
for (const id of DIRECTOR_IDS) {
2020
expect(DIRECTOR_REGISTRY[id].id).toBe(id);
2121
}
@@ -116,8 +116,8 @@ describe("director registry", () => {
116116

117117
test("directorProfiles is the spawn catalog (closed set minus skywalker)", () => {
118118
const profiles = directorProfiles();
119-
expect(profiles).toHaveLength(15);
120-
expect(new Set(profiles.map((p) => p.id)).size).toBe(15);
119+
expect(profiles).toHaveLength(16);
120+
expect(new Set(profiles.map((p) => p.id)).size).toBe(16);
121121
expect(profiles.map((p) => p.id)).not.toContain("skywalker");
122122
});
123123

@@ -151,6 +151,7 @@ describe("director registry", () => {
151151
"bruckheimer",
152152
"rand",
153153
"skywalker",
154+
"warden",
154155
] as const) {
155156
const allow = DIRECTOR_REGISTRY[id].tools?.allow ?? [];
156157
expect(allow).toContain("write_file");
@@ -194,11 +195,11 @@ describe("director registry", () => {
194195
expect(s.tools?.allow).toContain("write_file");
195196
expect(s.tools?.allow).toContain("edit_file");
196197
expect(s.tools?.allow).toContain("delete_file");
197-
expect(s.spawn.allowlist).toHaveLength(15);
198+
expect(s.spawn.allowlist).toHaveLength(16);
198199
});
199200

200201
// CL-6941: tier and spawn.maySpawn independently encode "may this package
201-
// spawn", hand-set across 16 files. This pins their agreement so drift
202+
// spawn", hand-set across 17 files. This pins their agreement so drift
202203
// (adding maySpawn: true without bumping tier, or vice versa) fails a test
203204
// instead of surfacing as an unexplained FleetAuthorityError at dispatch.
204205
test("tier agrees with spawn.maySpawn for every director", () => {

‎src/agent/directors/registry.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { shakespearePackage } from "./shakespeare/index.js";
1515
import { skywalkerPackage } from "./skywalker/index.js";
1616
import { testerPackage } from "./tester/index.js";
1717
import { testsmithPackage } from "./testsmith/index.js";
18+
import { wardenPackage } from "./warden/index.js";
1819
import { formatDirectorSystemPrompt } from "./identity.js";
1920
import {
2021
DIRECTOR_IDS,
@@ -58,6 +59,7 @@ export const DIRECTOR_REGISTRY: Readonly<Record<DirectorId, DirectorPackage>> =
5859
shakespeare: shakespearePackage,
5960
testsmith: testsmithPackage,
6061
tester: testerPackage,
62+
warden: wardenPackage,
6163
};
6264

6365
export function isDirectorId(value: unknown): value is DirectorId {

‎src/agent/directors/skywalker/package.test.ts‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe("skywalkerPackage", () => {
2828

2929
test("maySpawn true with full closed allowlist", () => {
3030
expect(skywalkerPackage.spawn.maySpawn).toBe(true);
31-
expect(skywalkerPackage.spawn.allowlist).toHaveLength(15);
31+
expect(skywalkerPackage.spawn.allowlist).toHaveLength(16);
3232
expect(skywalkerPackage.spawn.allowlist).toEqual([
3333
"builder",
3434
"explorer",
@@ -45,6 +45,7 @@ describe("skywalkerPackage", () => {
4545
"shakespeare",
4646
"testsmith",
4747
"tester",
48+
"warden",
4849
]);
4950
});
5051

@@ -316,4 +317,14 @@ describe("skywalkerPackage", () => {
316317
expect(p).not.toContain("gh pr create");
317318
expect(p).not.toContain("gh pr review");
318319
});
320+
321+
test("systemPrompt routes trust-path diffs to warden (tiny verify-after-ship mention)", () => {
322+
const p = skywalkerPackage.systemPrompt;
323+
expect(p).toContain("warden = permission / provider-auth / plugin-loader");
324+
expect(p).toContain("only when the diff touches those paths");
325+
expect(p).toContain(
326+
"When the diff touches permission, provider-auth, or plugin-loader paths, add a warden trust review alongside critic",
327+
);
328+
expect(p).toContain("never ships fixes");
329+
});
319330
});

‎src/agent/directors/skywalker/package.ts‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@ Example chains:
3131
- feature: explorer → plan → implement → critic
3232
- "why / how / is this stalled": answer yourself; at most one explorer if a single unknown blocks you
3333
34-
Closed directors (use search_agents / registry; each id is a spawn agent= target): builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester.
34+
Closed directors (use search_agents / registry; each id is a spawn agent= target): builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester, warden.
3535
No catch-all worker. If unsure, reclassify — do not spawn a blob agent.
3636
3737
Quick routing:
3838
- explorer = map/read codebase
3939
- counsel = ordered eng plan (no ship)
4040
- builder = ship product code + tests
4141
- critic = defects with evidence including hygiene the diff introduced (no fix)
42+
- warden = permission / provider-auth / plugin-loader trust review (no fix; only when the diff touches those paths)
4243
- greybeard = architecture judgment
4344
- neckbeard = hygiene / pedantry with receipts
4445
- tester = run the suite / repro
@@ -51,6 +52,7 @@ Quick routing:
5152
- bruckheimer = product discovery docs
5253
- intern = exact shell / mechanical ops
5354
- After every delegated builder landing → run a critic on the diff/criteria in a fresh context; when architecture is in play, add greybeard for architecture judgment
55+
- When the diff touches permission, provider-auth, or plugin-loader paths, add a warden trust review alongside critic; warden reports trust findings and never ships fixes.
5456
5557
success_criteria is required for implement/review and their default directors; recommended otherwise. Pass intent, do_not, report_focus, and agent when specialist.
5658
Parallelize independent lanes with spawn_agent, then idle. manage_tasks for your checklist. ask_operator when blocked or ambiguous — put long rationale in a normal transcript reply first, then call ask_operator with a short question and short option labels only.
@@ -144,7 +146,7 @@ Do not reclassify COMMUNICATION as ORCHESTRATION just to justify parallel spawn
144146
# Spawn graph
145147
146148
Skywalker = full closed set. Greybeard = limited spawn only (intern/explorer/critic) — not a second primary.
147-
You may spawn: builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester.
149+
You may spawn: builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester, warden.
148150
149151
When spawning, pass a typed brief. success_criteria is required for implement/review and their default directors; recommended otherwise:
150152
- intent — explore | implement | plan | review
@@ -204,6 +206,7 @@ export const skywalkerPackage: DirectorPackage = {
204206
"shakespeare",
205207
"testsmith",
206208
"tester",
209+
"warden",
207210
],
208211
},
209212
modelRole: "orchestrator",

‎src/agent/directors/types.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const DIRECTOR_IDS = [
2020
"shakespeare",
2121
"testsmith",
2222
"tester",
23+
"warden",
2324
] as const;
2425

2526
export type DirectorId = (typeof DIRECTOR_IDS)[number];
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { wardenPackage } from "./package.js";
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { describe, expect, test } from "bun:test";
2+
import { wardenPackage } from "./package.js";
3+
4+
describe("wardenPackage", () => {
5+
test("id matches directory", () => {
6+
expect(wardenPackage.id).toBe("warden");
7+
});
8+
9+
test("systemPrompt is real, not a placeholder", () => {
10+
expect(wardenPackage.systemPrompt.length).toBeGreaterThan(0);
11+
expect(wardenPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
12+
});
13+
14+
test("systemPrompt states PRIMARY INTENT", () => {
15+
expect(wardenPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
16+
});
17+
18+
test("systemPrompt identity is Warden / WardenDirector", () => {
19+
const p = wardenPackage.systemPrompt;
20+
expect(p).toMatch(/WardenDirector \(Warden\)/);
21+
expect(p).toMatch(/trust lane only/i);
22+
});
23+
24+
test("systemPrompt trigger is written trust paths only", () => {
25+
const p = wardenPackage.systemPrompt;
26+
expect(p).toMatch(/TRIGGER/i);
27+
expect(p).toMatch(/permission/);
28+
expect(p).toMatch(/provider-auth/);
29+
expect(p).toMatch(/plugin-loader/);
30+
expect(p).toMatch(/Anything else is out of lane/);
31+
expect(p).toMatch(/Do not expand into general code review/);
32+
});
33+
34+
test("systemPrompt findings lens covers the trust surface", () => {
35+
const p = wardenPackage.systemPrompt;
36+
expect(p).toMatch(/Findings lens/i);
37+
expect(p).toMatch(/grant-matching holes/i);
38+
expect(p).toMatch(/secret-guard bypass/i);
39+
expect(p).toMatch(/arktype boundary skips/i);
40+
expect(p).toMatch(/shell-policy peel gaps/i);
41+
expect(p).toMatch(/plugin trust/i);
42+
expect(p).toMatch(/blocking, should-fix, or file-for-later/i);
43+
});
44+
45+
test("systemPrompt is evidence-based findings, never-fix", () => {
46+
const p = wardenPackage.systemPrompt;
47+
expect(p).toMatch(/evidence/);
48+
expect(p).toMatch(/never fix/i);
49+
expect(p).toMatch(/Do not ship fixes/);
50+
expect(p).toMatch(/permanent tests/i);
51+
expect(p).toContain("testsmith/builder");
52+
expect(p).toContain("route to builder");
53+
});
54+
55+
test("systemPrompt is not a second critic or greybeard", () => {
56+
const p = wardenPackage.systemPrompt;
57+
expect(p).toMatch(/Do not become critic/);
58+
expect(p).toMatch(/greybeard/);
59+
});
60+
61+
test("systemPrompt has no tool-schema restatement or fake caps", () => {
62+
const p = wardenPackage.systemPrompt;
63+
expect(p).not.toMatch(/parameters?:/i);
64+
expect(p).not.toMatch(/fan-out/i);
65+
expect(p).not.toMatch(/at most \d+/i);
66+
expect(p).not.toMatch(/turn budget/i);
67+
expect(p).not.toMatch(/scheduler/i);
68+
expect(p).not.toMatch(/Prefer grep\/search_files/i);
69+
expect(p).not.toMatch(/Shell find\/rg/i);
70+
expect(p).not.toMatch(/Write tools are not mounted/i);
71+
expect(p).not.toMatch(/via run_shell/i);
72+
});
73+
74+
test("spawn.maySpawn is false", () => {
75+
expect(wardenPackage.spawn.maySpawn).toBe(false);
76+
});
77+
78+
test("tier is leaf", () => {
79+
expect(wardenPackage.tier).toBe("leaf");
80+
});
81+
82+
test("tools.allow is review surface with product writes", () => {
83+
const allow = wardenPackage.tools?.allow ?? [];
84+
expect(allow).toContain("read_file");
85+
expect(allow).not.toContain("use_skill");
86+
expect(allow).toContain("write_file");
87+
expect(allow).toContain("edit_file");
88+
expect(allow).toContain("delete_file");
89+
});
90+
91+
test("modelRole is review", () => {
92+
expect(wardenPackage.modelRole).toBe("review");
93+
});
94+
95+
test("optionalSkills order is style, philosophy, native-integration, idiot-proof", () => {
96+
expect(wardenPackage.optionalSkills).toEqual([
97+
"style",
98+
"philosophy",
99+
"native-integration",
100+
"idiot-proof",
101+
]);
102+
});
103+
104+
test("primaryIntent and outOfLane match warden lane", () => {
105+
expect(wardenPackage.primaryIntent).toBe(
106+
"Trust review of permission, provider-auth, and plugin-loader diffs; never fix product code",
107+
);
108+
expect(wardenPackage.outOfLane).toContain("implementing fixes");
109+
expect(wardenPackage.outOfLane).toContain(
110+
"general code review outside trust paths",
111+
);
112+
expect(wardenPackage.outOfLane).toContain(
113+
"architecture judgment without trust evidence",
114+
);
115+
expect(wardenPackage.outOfLane).toContain("feature design");
116+
});
117+
});
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import type { DirectorPackage } from "../types.js";
2+
import { REVIEW_TOOLS } from "../tool-sets.js";
3+
4+
/**
5+
* Warden trust-review worker (CL-7657).
6+
* Permission / provider-auth / plugin-loader diffs only; findings, never fixes.
7+
*/
8+
export const wardenPackage: DirectorPackage = {
9+
id: "warden",
10+
primaryIntent:
11+
"Trust review of permission, provider-auth, and plugin-loader diffs; never fix product code",
12+
outOfLane: [
13+
"implementing fixes",
14+
"general code review outside trust paths",
15+
"architecture judgment without trust evidence",
16+
"feature design",
17+
],
18+
description: "Permission and trust review worker",
19+
optionalSkills: ["style", "philosophy", "native-integration", "idiot-proof"],
20+
tools: { allow: REVIEW_TOOLS },
21+
spawn: { maySpawn: false },
22+
tier: "leaf",
23+
modelRole: "review",
24+
systemPrompt: `You are WardenDirector (Warden), a specialist in Corbits Code.
25+
26+
PRIMARY INTENT: trust review of permission, provider-auth, and plugin-loader diffs. Find trust defects with evidence; never fix product code. Cite path, line or symbol, what breaks, and the concrete input or sequence that triggers it.
27+
28+
TRIGGER — written paths only. Review only when the diff touches permission, provider-auth, or plugin-loader code. Anything else is out of lane: say so under Blockers and stop. Do not expand into general code review.
29+
30+
You are the trust lane only — not an implementer, not an explorer, not an orchestrator. Do not ship fixes. Do not become critic (general defects) or greybeard (architecture judgment) as your primary job.
31+
32+
Findings lens — rank each as blocking, should-fix, or file-for-later:
33+
- grant-matching holes (permission grants that over- or under-match the request)
34+
- secret-guard bypass (secrets or credentials reachable past the guard)
35+
- arktype boundary skips (unvalidated input crossing a trust boundary)
36+
- shell-policy peel gaps (shell policy peeled or bypassed by a wrapper layer)
37+
- plugin trust (untrusted plugin code gaining capability it was not granted)
38+
39+
Evidence rules:
40+
- Every claim needs path + line/symbol + reproduction shape (input, sequence, missing branch).
41+
- "This is genuinely fine" is a valid finding when true.
42+
- Call out gaps: what you did not cover so the parent does not assume closed.
43+
- Recommend permanent tests the suite should keep (name the scenario; do not implement them here — route to testsmith/builder).
44+
45+
Before substantial review work: follow style, philosophy, native-integration, and idiot-proof (baked; use_skill is not mounted on workers). Read the code under review.
46+
47+
OUT OF LANE → refuse or reclassify under Blockers:
48+
- implementing fixes (route to builder)
49+
- general code review outside trust paths (route to critic)
50+
- architecture judgment without trust evidence (route to greybeard)
51+
- feature design (route to counsel)`,
52+
};

‎src/agent/prompt-sizes.test.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ const CHAR_BUDGET: Record<DirectorId, number> = {
3333
shakespeare: 54700,
3434
testsmith: 16200,
3535
tester: 13900,
36+
// CL-7657: measured 51410 default / 51926 grok chars; +2000 allowance, ceiling to 100.
37+
warden: 53500,
3638
};
3739

3840
const BYTE_BUDGET: Record<DirectorId, number> = {
@@ -54,6 +56,8 @@ const BYTE_BUDGET: Record<DirectorId, number> = {
5456
shakespeare: 55900,
5557
testsmith: 17200,
5658
tester: 15000,
59+
// CL-7657: measured 51584 default / 52102 grok bytes; +3000 allowance, ceiling to 100.
60+
warden: 54600,
5761
};
5862

5963
function budgetMessage(

0 commit comments

Comments
 (0)