Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ Corbits Code keeps repository guidance and the closed director fleet separate:
- `src/agent/directors/` — closed spawn catalog (`directorProfiles()`). Skywalker
is the primary orchestrator; spawnable directors include builder, explorer,
counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper,
emil, rand, shakespeare, testsmith, tester, gauntlet, prober, and migrator. Closed ids cannot be
emil, rand, shakespeare, testsmith, tester, gauntlet, prober, migrator, and warden. Closed ids cannot be
overridden by plugins or local files.
- `.agents/agents/` — optional local profile additions; this directory is not
required and may be absent
Expand Down
3 changes: 2 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ Enforcement is runtime code at the existing tool-mount point, not prompt wording

#### Closed director fleet (`src/agent/directors/`)

Every shipped specialist is a **director package** — a prompt-first `DirectorPackage` (system prompt, tool envelope, spawn rights, nudge budget, report contract, `modelRole`, fleet authority `tier`) registered in a **closed** set of 19 ids. There is no catch-all worker: `spawn_agent` without `agent` or non-general `intent`, and `spawn_agent(intent="general")`, fail closed so the primary reclassifies. Nested directors with a spawn allowlist reject off-list children at `spawn_agent` dispatch time (not prompt-only). Skywalker is the primary session identity: `spawn_agent(agent="skywalker")` is refused, and `directorProfiles()` omits it from the spawn catalog.
Every shipped specialist is a **director package** — a prompt-first `DirectorPackage` (system prompt, tool envelope, spawn rights, nudge budget, report contract, `modelRole`, fleet authority `tier`) registered in a **closed** set of 20 ids. There is no catch-all worker: `spawn_agent` without `agent` or non-general `intent`, and `spawn_agent(intent="general")`, fail closed so the primary reclassifies. Nested directors with a spawn allowlist reject off-list children at `spawn_agent` dispatch time (not prompt-only). Skywalker is the primary session identity: `spawn_agent(agent="skywalker")` is refused, and `directorProfiles()` omits it from the spawn catalog.

**Primary**

Expand All @@ -266,6 +266,7 @@ Every shipped specialist is a **director package** — a prompt-first `DirectorP
| bruckheimer | Product discovery → PRODUCT/ARCHITECTURE/IMPLEMENTATION-oriented briefs | Eng plan, code |
| gaasbot | Quick CTO opinion voice | Formal review gate, implement |
| migrator | Reversible settings/config/session-state migrations | Live-state execution, new features |
| warden | Trust review of permission/provider-auth/plugin-loader diffs | Fixes, general review |

**Design trio (dev perspective)**

Expand Down
16 changes: 8 additions & 8 deletions docs/PRODUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,14 +155,14 @@ Capabilities beyond the core toolset are opt-in plugins, enabled per workspace t

## Multi-agent (fleet agents)

The primary session is always **orchestrator** (single-agent mode is gone). Its identity is **Skywalker** (product name remains Corbits Code; when asked its name, answer Skywalker): classify work, DIY tiny/single-file/one-route product edits, dispatch a **closed fleet of 19 directors** for substantial work, track the fleet, and synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary (CORE / `SKYWALKER_TOOLS`) — path tools are the DIY surface; spawn remains the default for substantial, multi-file, parallel, or specialist work. Shell file-writes stay denied. MCP tools are not re-filtered by a product-write deny list (that list is gone). There is no static per-package write-path declaration (CL-6952 removed it — no shipped director ever set one). A concurrent dispatch landing on the same working directory as another still-running lane is recorded as a `conflict` intervention, not blocked. Operator slash recipes (`/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`, `/lexicon`) tell Skywalker which directors to spawn for substantial work (`/lexicon` runs on the primary and spawns nothing); tiny/bounded edits may run on the primary.

| Lane | Directors |
| --------- | ------------------------------------------------------------------------------------------------ |
| Primary | skywalker |
| Eng | builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, migrator |
| Design | draper, emil, rand |
| Docs / QA | shakespeare, testsmith, tester, gauntlet, prober |
The primary session is always **orchestrator** (single-agent mode is gone). Its identity is **Skywalker** (product name remains Corbits Code; when asked its name, answer Skywalker): classify work, DIY tiny/single-file/one-route product edits, dispatch a **closed fleet of 20 directors** for substantial work, track the fleet, and synthesize. Product mutation tools (`write_file` / `edit_file` / `delete_file`) are mounted on the primary (CORE / `SKYWALKER_TOOLS`) — path tools are the DIY surface; spawn remains the default for substantial, multi-file, parallel, or specialist work. Shell file-writes stay denied. MCP tools are not re-filtered by a product-write deny list (that list is gone). There is no static per-package write-path declaration (CL-6952 removed it — no shipped director ever set one). A concurrent dispatch landing on the same working directory as another still-running lane is recorded as a `conflict` intervention, not blocked. Operator slash recipes (`/implement`, `/plan`, `/refactor`, `/review`, `/pull-request-review`, `/create-issue`, `/scribe`, `/interview`, `/ast-grep`, `/lexicon`) tell Skywalker which directors to spawn for substantial work (`/lexicon` runs on the primary and spawns nothing); tiny/bounded edits may run on the primary.

| Lane | Directors |
| --------- | -------------------------------------------------------------------------------------------------------- |
| Primary | skywalker |
| Eng | builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, migrator, warden |
| Design | draper, emil, rand |
| Docs / QA | shakespeare, testsmith, tester, gauntlet, prober |

There is **no catch-all worker**. `spawn_agent` requires `agent=…` or a non-general `intent` (implement/explore/plan/review→critic); bare dispatch and `intent=general` are refused. Named `spawn_agent(agent=…)` selects a director package without requiring a plugin profile, except `skywalker` which is the primary session identity and is refused as a spawned worker. Nested spawn is runtime-enforced: only skywalker (full fleet allowlist) and greybeard (intern/explorer/critic) may spawn; other workers have no fleet tools. Primary omits an allowlist so plugin profiles remain reachable from the main session.

Expand Down
15 changes: 8 additions & 7 deletions src/agent/directors/registry.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
} from "./registry.js";

describe("director registry", () => {
test("closed set has exactly 19 directors", () => {
expect(DIRECTOR_IDS).toHaveLength(19);
expect(listDirectors()).toHaveLength(19);
test("closed set has exactly 20 directors", () => {
expect(DIRECTOR_IDS).toHaveLength(20);
expect(listDirectors()).toHaveLength(20);
for (const id of DIRECTOR_IDS) {
expect(DIRECTOR_REGISTRY[id].id).toBe(id);
}
Expand Down Expand Up @@ -116,8 +116,8 @@ describe("director registry", () => {

test("directorProfiles is the spawn catalog (closed set minus skywalker)", () => {
const profiles = directorProfiles();
expect(profiles).toHaveLength(18);
expect(new Set(profiles.map((p) => p.id)).size).toBe(18);
expect(profiles).toHaveLength(19);
expect(new Set(profiles.map((p) => p.id)).size).toBe(19);
expect(profiles.map((p) => p.id)).not.toContain("skywalker");
});

Expand Down Expand Up @@ -168,6 +168,7 @@ describe("director registry", () => {
"prober",
"skywalker",
"gauntlet",
"warden",
] as const) {
const allow = DIRECTOR_REGISTRY[id].tools?.allow ?? [];
expect(allow).toContain("write_file");
Expand Down Expand Up @@ -211,11 +212,11 @@ describe("director registry", () => {
expect(s.tools?.allow).toContain("write_file");
expect(s.tools?.allow).toContain("edit_file");
expect(s.tools?.allow).toContain("delete_file");
expect(s.spawn.allowlist).toHaveLength(18);
expect(s.spawn.allowlist).toHaveLength(19);
});

// CL-6941: tier and spawn.maySpawn independently encode "may this package
// spawn", hand-set across 19 files. This pins their agreement so drift
// spawn", hand-set across 20 files. This pins their agreement so drift
// (adding maySpawn: true without bumping tier, or vice versa) fails a test
// instead of surfacing as an unexplained FleetAuthorityError at dispatch.
test("tier agrees with spawn.maySpawn for every director", () => {
Expand Down
2 changes: 2 additions & 0 deletions src/agent/directors/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { testerPackage } from "./tester/index.js";
import { testsmithPackage } from "./testsmith/index.js";
import { gauntletPackage } from "./gauntlet/index.js";
import { proberPackage } from "./prober/index.js";
import { wardenPackage } from "./warden/index.js";
import { formatDirectorSystemPrompt } from "./identity.js";
import {
DIRECTOR_IDS,
Expand Down Expand Up @@ -64,6 +65,7 @@ export const DIRECTOR_REGISTRY: Readonly<Record<DirectorId, DirectorPackage>> =
gauntlet: gauntletPackage,
prober: proberPackage,
migrator: migratorPackage,
warden: wardenPackage,
};

export function isDirectorId(value: unknown): value is DirectorId {
Expand Down
13 changes: 12 additions & 1 deletion src/agent/directors/skywalker/package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe("skywalkerPackage", () => {

test("maySpawn true with full closed allowlist", () => {
expect(skywalkerPackage.spawn.maySpawn).toBe(true);
expect(skywalkerPackage.spawn.allowlist).toHaveLength(18);
expect(skywalkerPackage.spawn.allowlist).toHaveLength(19);
expect(skywalkerPackage.spawn.allowlist).toEqual([
"builder",
"explorer",
Expand All @@ -48,6 +48,7 @@ describe("skywalkerPackage", () => {
"gauntlet",
"prober",
"migrator",
"warden",
]);
});

Expand Down Expand Up @@ -338,4 +339,14 @@ describe("skywalkerPackage", () => {
"gauntlet = mutation-check that tests can actually fail (tree clean)",
);
});

test("systemPrompt routes trust-path diffs to warden (tiny verify-after-ship mention)", () => {
const p = skywalkerPackage.systemPrompt;
expect(p).toContain("warden = permission / provider-auth / plugin-loader");
expect(p).toContain("only when the diff touches those paths");
expect(p).toContain(
"When the diff touches permission, provider-auth, or plugin-loader paths, add a warden trust review alongside critic",
);
expect(p).toContain("never ships fixes");
});
});
7 changes: 5 additions & 2 deletions src/agent/directors/skywalker/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ Example chains:
- feature: explorer → plan → implement → critic
- "why / how / is this stalled": answer yourself; at most one explorer if a single unknown blocks you

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, gauntlet, prober, migrator.
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, gauntlet, prober, migrator, warden.
No catch-all worker. If unsure, reclassify — do not spawn a blob agent.

Quick routing:
- explorer = map/read codebase
- counsel = ordered eng plan (no ship)
- builder = ship product code + tests
- critic = defects with evidence including hygiene the diff introduced (no fix)
- warden = permission / provider-auth / plugin-loader trust review (no fix; only when the diff touches those paths)
- greybeard = architecture judgment
- neckbeard = hygiene / pedantry with receipts
- tester = run the suite / repro
Expand All @@ -54,6 +55,7 @@ Quick routing:
- bruckheimer = product discovery docs
- intern = exact shell / mechanical ops
- 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
- 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.

Pass intent, do_not, report_focus, and agent when specialist.
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.
Expand Down Expand Up @@ -146,7 +148,7 @@ Do not reclassify COMMUNICATION as ORCHESTRATION just to justify parallel spawn
# Spawn graph

Skywalker = full closed set. Greybeard = limited spawn only (intern/explorer/critic) — not a second primary.
You may spawn: builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester, gauntlet, prober, migrator.
You may spawn: builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester, gauntlet, prober, migrator, warden.

When spawning, pass a typed brief. success_criteria is required for implement/review and their default directors; recommended otherwise:
- intent — explore | implement | plan | review
Expand Down Expand Up @@ -209,6 +211,7 @@ export const skywalkerPackage: DirectorPackage = {
"gauntlet",
"prober",
"migrator",
"warden",
],
},
modelRole: "orchestrator",
Expand Down
1 change: 1 addition & 0 deletions src/agent/directors/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const DIRECTOR_IDS = [
"gauntlet",
"prober",
"migrator",
"warden",
] as const;

export type DirectorId = (typeof DIRECTOR_IDS)[number];
Expand Down
1 change: 1 addition & 0 deletions src/agent/directors/warden/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { wardenPackage } from "./package.js";
117 changes: 117 additions & 0 deletions src/agent/directors/warden/package.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { describe, expect, test } from "bun:test";
import { wardenPackage } from "./package.js";

describe("wardenPackage", () => {
test("id matches directory", () => {
expect(wardenPackage.id).toBe("warden");
});

test("systemPrompt is real, not a placeholder", () => {
expect(wardenPackage.systemPrompt.length).toBeGreaterThan(0);
expect(wardenPackage.systemPrompt.startsWith("Placeholder")).toBe(false);
});

test("systemPrompt states PRIMARY INTENT", () => {
expect(wardenPackage.systemPrompt).toMatch(/PRIMARY INTENT/i);
});

test("systemPrompt identity is Warden / WardenDirector", () => {
const p = wardenPackage.systemPrompt;
expect(p).toMatch(/WardenDirector \(Warden\)/);
expect(p).toMatch(/trust lane only/i);
});

test("systemPrompt trigger is written trust paths only", () => {
const p = wardenPackage.systemPrompt;
expect(p).toMatch(/TRIGGER/i);
expect(p).toMatch(/permission/);
expect(p).toMatch(/provider-auth/);
expect(p).toMatch(/plugin-loader/);
expect(p).toMatch(/Anything else is out of lane/);
expect(p).toMatch(/Do not expand into general code review/);
});

test("systemPrompt findings lens covers the trust surface", () => {
const p = wardenPackage.systemPrompt;
expect(p).toMatch(/Findings lens/i);
expect(p).toMatch(/grant-matching holes/i);
expect(p).toMatch(/secret-guard bypass/i);
expect(p).toMatch(/arktype boundary skips/i);
expect(p).toMatch(/shell-policy peel gaps/i);
expect(p).toMatch(/plugin trust/i);
expect(p).toMatch(/blocking, should-fix, or file-for-later/i);
});

test("systemPrompt is evidence-based findings, never-fix", () => {
const p = wardenPackage.systemPrompt;
expect(p).toMatch(/evidence/);
expect(p).toMatch(/never fix/i);
expect(p).toMatch(/Do not ship fixes/);
expect(p).toMatch(/permanent tests/i);
expect(p).toContain("testsmith/builder");
expect(p).toContain("route to builder");
});

test("systemPrompt is not a second critic or greybeard", () => {
const p = wardenPackage.systemPrompt;
expect(p).toMatch(/Do not become critic/);
expect(p).toMatch(/greybeard/);
});

test("systemPrompt has no tool-schema restatement or fake caps", () => {
const p = wardenPackage.systemPrompt;
expect(p).not.toMatch(/parameters?:/i);
expect(p).not.toMatch(/fan-out/i);
expect(p).not.toMatch(/at most \d+/i);
expect(p).not.toMatch(/turn budget/i);
expect(p).not.toMatch(/scheduler/i);
expect(p).not.toMatch(/Prefer grep\/search_files/i);
expect(p).not.toMatch(/Shell find\/rg/i);
expect(p).not.toMatch(/Write tools are not mounted/i);
expect(p).not.toMatch(/via run_shell/i);
});

test("spawn.maySpawn is false", () => {
expect(wardenPackage.spawn.maySpawn).toBe(false);
});

test("tier is leaf", () => {
expect(wardenPackage.tier).toBe("leaf");
});

test("tools.allow is review surface with product writes", () => {
const allow = wardenPackage.tools?.allow ?? [];
expect(allow).toContain("read_file");
expect(allow).not.toContain("use_skill");
expect(allow).toContain("write_file");
expect(allow).toContain("edit_file");
expect(allow).toContain("delete_file");
});

test("modelRole is review", () => {
expect(wardenPackage.modelRole).toBe("review");
});

test("optionalSkills order is style, philosophy, native-integration, idiot-proof", () => {
expect(wardenPackage.optionalSkills).toEqual([
"style",
"philosophy",
"native-integration",
"idiot-proof",
]);
});

test("primaryIntent and outOfLane match warden lane", () => {
expect(wardenPackage.primaryIntent).toBe(
"Trust review of permission, provider-auth, and plugin-loader diffs; never fix product code",
);
expect(wardenPackage.outOfLane).toContain("implementing fixes");
expect(wardenPackage.outOfLane).toContain(
"general code review outside trust paths",
);
expect(wardenPackage.outOfLane).toContain(
"architecture judgment without trust evidence",
);
expect(wardenPackage.outOfLane).toContain("feature design");
});
});
Loading
Loading