From 1d18c34e1ef984fbac5638079d30c1d77fc9e5e5 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 4 Oct 2026 15:14:25 -0700 Subject: [PATCH] feat(dispatch): spawn first with no parallelization cap Fan-out left the live card when guidelines were stripped. Restore spawn-on-first-turn, many small parallel lanes, and no width cap so same-repo is not treated as the same job. --- src/agent/directors/dispatch/package.ts | 12 +++++++----- src/agent/directors/registry.test.ts | 14 ++++++++++++++ src/agent/prompt-sizes.test.ts | 2 +- src/prompts.test.ts | 12 ++++++++++++ 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/agent/directors/dispatch/package.ts b/src/agent/directors/dispatch/package.ts index 00bbe9f74..263a85c13 100644 --- a/src/agent/directors/dispatch/package.ts +++ b/src/agent/directors/dispatch/package.ts @@ -19,9 +19,9 @@ You are Dispatch, the coordinator for Corbits Code. Specialists own substantive # Delegation boundary - Delegate by default. File count does not determine complexity: a single-file bug fix or behavior change still belongs to coder. -- Answer directly when existing context or one or two targeted reads suffice. Delegate broader investigation to explorer. +- Answer directly only for trivia already in context. One or two targeted reads are trivia, not a repo walk. Do not DIY-map explorer work. - Before editing, classify the change. DIY is only for obvious mechanical corrections requiring no diagnosis, design, new behavior, or new tests. If uncertain, delegate. -- Read only enough to route and write a useful brief. Existing exploration is not permission to implement; do not solve the task yourself before spawning. +- Read only enough to route and write a useful brief. Spawn on the first assistant turn for investigation, implementation, or multi-ticket work. Existing exploration is not permission to implement; do not solve the task yourself before spawning. # Rules - Edit files with file tools only, never shell redirection or sed. @@ -31,7 +31,10 @@ You are Dispatch, the coordinator for Corbits Code. Specialists own substantive # Spawn - Brief: description, prompt, relevant context, success_criteria, do_not, report_focus. The worker starts blank. -- Spawn independent lanes together. Do not duplicate a live worker's work. On mailbox surfaces, yield and process incoming reports; where wait_agents is mounted, collect with it instead. +- Latest operator turn is law. Do not re-litigate or ask how they want it solved when they named the outcome. +- Spawn independent lanes in the same turn. There is no parallelization cap. Duplicate = the same live job, not the same repo. +- Split by path, ownership, or lens. One worker = one outcome. Independent tickets/files/reviews go out together. Do not serialize explorer-then-coder unless the coder brief needs the map. +- After spawn, yield. Mailbox (TUI/nested) or wait_agents (exec) delivers. Spawn the next independent wave before the first finishes. Do not poll. - Use reports as the working record. Resolve gaps with the same worker instead of repeating its investigation. Route unfinished implementation back to coder, not yourself. - After coder finishes non-trivial or risky code changes, including single-file changes, run reviewer on the diff. Skip review only for mechanical or docs-only diffs and say so. @@ -49,10 +52,9 @@ export const dispatchPackage: DirectorPackage = { outOfLane: [ "substantive product work without spawning, including single-file behavior changes", "docs/design authorship (PRODUCT.md, ARCHITECTURE.md, DESIGN.md) except one-line fixes", - "deep multi-path repo walks when a single explorer worker or mounted tools suffice", + "walking the repo yourself; spawn explorers (as many as the question needs)", "being the reviewer, planner, or coder by default", "catch-all worker", - "diagnostic fleets for why/how/stall questions", "searching the repo yourself after a worker stops without finishing", ], description: diff --git a/src/agent/directors/registry.test.ts b/src/agent/directors/registry.test.ts index 69a810002..d5dd6a4cf 100644 --- a/src/agent/directors/registry.test.ts +++ b/src/agent/directors/registry.test.ts @@ -182,6 +182,20 @@ describe("director registry", () => { expect(tierForDirectorId("prober")).toBe("leaf"); }); + test("dispatch card has no parallelization cap; duplicate is the same live job", () => { + const card = DIRECTOR_REGISTRY.dispatch.systemPrompt; + expect(card).toContain("no parallelization cap"); + expect(card).toMatch(/same live job/i); + expect(card).not.toMatch(/Do not duplicate a live worker/i); + }); + + test("dispatch outOfLane spawns explorers instead of walking the repo", () => { + const lanes = DIRECTOR_REGISTRY.dispatch.outOfLane.join("\n"); + expect(lanes).not.toMatch(/diagnostic fleet/i); + expect(lanes).not.toMatch(/single explorer worker/i); + expect(lanes).toMatch(/spawn explorer/i); + }); + test("every director profile declares matching agent id in system prompt", () => { for (const id of DIRECTOR_IDS) { const profile = packageToProfile(DIRECTOR_REGISTRY[id]); diff --git a/src/agent/prompt-sizes.test.ts b/src/agent/prompt-sizes.test.ts index c6a9b6f5e..847f2e24b 100644 --- a/src/agent/prompt-sizes.test.ts +++ b/src/agent/prompt-sizes.test.ts @@ -34,7 +34,7 @@ const PROMPT_SIZE_BASELINE: Record< DirectorId, { chars: number; bytes: number } > = { - dispatch: { chars: 3551, bytes: 3555 }, + dispatch: { chars: 5277, bytes: 5281 }, explorer: { chars: 4041, bytes: 4059 }, planner: { chars: 4439, bytes: 4447 }, coder: { chars: 4705, bytes: 4715 }, diff --git a/src/prompts.test.ts b/src/prompts.test.ts index 7db537b48..aa1230a27 100644 --- a/src/prompts.test.ts +++ b/src/prompts.test.ts @@ -19,6 +19,18 @@ test("chat system prompt keeps the routing, spawn-brief, and rules sections", () expect(prompt).toContain("manage_tasks"); }); +test("dispatch card is spawn-first with no parallelization cap", () => { + const prompt = buildChatSystemPrompt(); + expect(prompt).toContain("# Spawn"); + expect(prompt).toContain("no parallelization cap"); + expect(prompt).toMatch(/first assistant turn/i); + expect(prompt).toMatch(/same turn/i); + expect(prompt).toMatch(/same live job/i); + expect(prompt).not.toMatch(/Do not duplicate a live worker/i); + expect(prompt).not.toMatch(/diagnostic fleet/i); + expect(prompt).not.toMatch(/single explorer worker/i); +}); + const envBase: EnvironmentInfo = { cwd: "/tmp/proj", platform: "test",