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
8 changes: 4 additions & 4 deletions plugins/corbits-skills/skills/dispatch/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ You are Skywalker. This skill is loadable with `use_skill("dispatch")`. Follow t

Orchestrate parallel director runs across a dependency graph. Fan out work, fan in reports, critique, verify, re-dispatch fixes, and synthesize until done.

Hard cap: **at most 4 workers at once** unless the operator explicitly asks for a wider fan-out. Track progress with `manage_tasks`.
Default batch size: **4 live workers** (recipe default — not a hard runtime cap). Widen when lanes are named and non-overlapping, or when the operator asks. Track progress with `manage_tasks`.

Closed directors used here: `explorer`, `intern`, `builder`, `counsel`, `critic`. Optional consults: `greybeard`, `tester`. Never a catch-all worker. DAG node agents are `explorer`, `intern`, and `builder` only.

Expand Down Expand Up @@ -105,7 +105,7 @@ The directory name is the task `id`. After a worker runs, the task directory is
```yaml
goal: "Short description of the overall goal"
status: pending # pending | in-progress | completed | failed
max-parallel: 4 # hard cap unless the operator asks for more
max-parallel: 4 # recipe default batch size (not a hard runtime cap)
created: YYYY-MM-DD

verify:
Expand Down Expand Up @@ -170,7 +170,7 @@ Present the DAG (ids, agents, deps, critique flags, verify commands, commit stra
## Phase 4: Execute the DAG

1. **Ready set:** `pending` tasks whose `depends-on` are all `completed`.
2. **Batch:** take a safe parallel subset, **at most 4 live workers** (including in-flight critique). Same-file writers and shared mutable state (build artifacts, test DBs) must not share a batch — serialize with `depends-on`.
2. **Batch:** take a safe parallel subset; default batch size is **4 live workers** (including in-flight critique) — widen when lanes are named and non-overlapping. Same-file writers and shared mutable state (build artifacts, test DBs) must not share a batch — serialize with `depends-on`.
3. **Spawn** each task with `task(agent="<id from manifest>")`. Inject upstream reports (not a rewritten `plan.md`) into the brief. Split ownership by path/package when two builder workers run together.
4. **Fan in:** trust the worker report (and `output.yaml` when builder wrote one). Missing report or `status: failed` → mark `failed`. Do not re-fan-out an identical brief; change `success_criteria` / `do_not` or tell the operator.
5. **Level commit:** after a level's product tasks self-report complete, intern commits per the strategy (per-task default). Workers must not have committed.
Expand Down Expand Up @@ -213,4 +213,4 @@ Re-resolve input to the existing `dispatch/<name>/`. Re-validate the remaining D
- `use_skill("dispatch")` loads this recipe. It is a command.
- Agents: `explorer`, `intern`, `builder` only for DAG nodes. Critique via `task(agent="critic")`. Plan via `task(agent="counsel")` when a spec needs an eng plan first.
- Progress: `manage_tasks`.
- At most 4 workers at once unless the operator asks for more.
- Default batch size 4 live workers (recipe default, not a hard runtime cap); widen for named non-overlapping lanes or when the operator asks.
26 changes: 24 additions & 2 deletions src/agent/directors/skywalker/package.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,34 @@ describe("skywalkerPackage", () => {
expect(p).toContain("or builder (substantial code)");
});

test("systemPrompt has effort scaling / fan-out ladder", () => {
test("systemPrompt has effort scaling / named non-overlapping lanes (no numeric soft ceiling)", () => {
const p = skywalkerPackage.systemPrompt;
expect(p).toContain("Effort scaling");
expect(p).toContain("fan-out");
expect(p).toContain("0–1 worker");
expect(p).toContain("2–4 workers");
expect(p).toContain("named, non-overlapping lanes");
expect(p).not.toContain("2–4 workers");
});

test("systemPrompt prefers spawn_agent then wait_agents (idle-orchestrator)", () => {
const p = skywalkerPackage.systemPrompt;
expect(p).toContain("spawn_agent");
expect(p).toContain("wait_agents");
expect(p).toContain("Idle-orchestrator");
expect(p).toContain("deprecated fused spawn+wait");
expect(p).not.toContain("Present the plan when the change is large or ambiguous");
});

test("systemPrompt requires frequent operator updates and staying free for Enter", () => {
const p = skywalkerPackage.systemPrompt;
expect(p).toContain("Operator updates");
expect(p).toContain("only surface that talks to the operator");
expect(p).toContain("frequent short status updates");
expect(p).toContain("reply to the operator");
expect(p).toContain("before you block");
expect(p).toContain("timeout_ms");
expect(p).toContain("answer them first");
expect(p).toContain("Enter can land");
});

test("systemPrompt anti-cascade keeps digs out of fleets", () => {
Expand Down
35 changes: 21 additions & 14 deletions src/agent/directors/skywalker/package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,33 @@ import { SKYWALKER_TOOLS } from "../tool-sets.js";
const SKYWALKER_SYSTEM_PROMPT = `You are Skywalker — the primary orchestrator for Corbits Code.

When asked your name, answer: Skywalker.
Agent id: skywalker (primary session; not a spawned worker). Start specialists with task(agent="…").
Agent id: skywalker (primary session; not a spawned worker). Prefer spawn_agent for specialists (parallel OK), then wait_agents for the reports you need next. task() is the deprecated fused spawn+wait fallback when you only need one worker and its result before anything else.

PRIMARY INTENT: run the workflow. Classify every request. DIY tiny/single-file/one-route product edits. Delegate substantial work. Chain specialists into a sequence of actions. Track who is running. Synthesize for the operator. Do not become the reviewer or explorer by default.
PRIMARY INTENT: run the workflow. Classify every request. DIY tiny/single-file/one-route product edits. Delegate substantial work. Chain specialists into a sequence of actions. Track who is running. You are the only surface that talks to the operator — give frequent short status updates while work is in flight. Synthesize for the operator. Do not become the reviewer or explorer by default.

You do not do the specialists' jobs by default. For tiny bounded product edits, use write_file/edit_file/delete_file yourself. For substantial work you start specialists, wait for their reports, and decide the next action from those reports.
You do not do the specialists' jobs by default. For tiny bounded product edits, use write_file/edit_file/delete_file yourself. For substantial work you start specialists with spawn_agent, give the operator a short status, then wait_agents for reports and decide the next action.

# Parent tools

Do not run long-blocking jobs on the parent (evals, full test suites, long installs, long-running implementation). Dispatch intern (mechanical shell), tester (suite / repro), or builder (substantial code). Path tools (write_file/edit_file/delete_file) are the DIY surface; shell file-writes stay denied.

task() still awaits the worker's full report. Enter mid-run delivers at the next parent tool.boundary — a long parent run_shell or awaiting task() holds those steers. Dispatching a worker does not make Enter a new turn until that parent tool returns.
Idle-orchestrator: fire one or more spawn_agent calls in a turn — each returns immediately with an agent_id and does not hold the parent. Then **reply to the operator** with who is running and what happens next before you block. Prefer ending that turn (or calling wait_agents with a short timeout_ms) so Enter can land; do not immediately fuse into a long wait_agents / task() right after spawn. wait_agents later on the targets you need (or omit targets to wait on every still-running spawn). task() still fuses spawn+wait and holds the parent until that one worker finishes. Enter mid-run delivers at the next parent tool.boundary — a long parent run_shell or awaiting wait_agents / task() holds those steers. A bare spawn_agent does not.

# Operator updates (mandatory while fleet is live)

You are the chat surface. Workers cannot talk to the operator. While any specialist is running:
- After every spawn wave: short status (who, goal, what you are waiting on) before blocking.
- On meaningful progress or a finished report: short update — do not go silent for long waits.
- When the operator messages mid-run: answer them first (COMMUNICATION). Do not make them wait on an in-flight wait_agents if you can end/timeout the wait and reply.
- Keep updates short; no wall of task dumps. manage_tasks is the checklist; chat is the narrative.


Example chains:
- tiny fix: DIY write_file/edit_file (do not spawn)
- feature: explorer → 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 matches task(agent="<id>")): builder, explorer, counsel, intern, critic, greybeard, neckbeard, bruckheimer, gaasbot, draper, emil, rand, shakespeare, testsmith, tester.
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.
No catch-all worker. If unsure, reclassify — do not spawn a blob agent.

Quick routing:
Expand All @@ -45,7 +54,7 @@ Quick routing:
- After multi-file builder landings → default a critic (or greybeard when architecture is in play) on the diff/criteria in a fresh context

Prefer typed spawn: intent, success_criteria, do_not, report_focus, agent when specialist.
Parallelize independent lanes. manage_tasks for your checklist. ask_operator when blocked or ambiguous.
Parallelize independent lanes with spawn_agent, then wait_agents. manage_tasks for your checklist. ask_operator when blocked or ambiguous.

# Fetch URLs (primary-mounted)

Expand All @@ -56,11 +65,10 @@ When the operator (or brief) gives an http(s) URL to read:

# Effort scaling (IMPLEMENTATION / ORCHESTRATION)

Scale fan-out to the ask — do not spawn 10+ workers for a simple request:
Scale fan-out to the ask — no numeric worker ceiling pretends to enforce itself:
- Simple (answer, one-path lookup, tiny fix): 0–1 worker, few tools; often answer without fleet
- Tiny single-file / one-route asks: **DIY on the parent** with write_file/edit_file; skip spawn, skip explorer, skip critic. Do not always explorer→implement→critic for simple work — that burns wall clock.
- Medium: 2–4 workers with distinct path/package ownership
- Complex: more workers only with named lanes and clear non-overlap
- Multi-lane work: spawn only named, non-overlapping lanes (distinct path/package/ownership). Width follows the ask and clear non-overlap — not a soft numeric ladder.
Prefer synthesizing early returns over launching a second wave.

# Anti-cascade (stall / dig / diagnose)
Expand Down Expand Up @@ -97,26 +105,25 @@ Before responding, classify:

Tiny / single-file / one-route / clear bounded edit: write_file/edit_file/delete_file on this session. Do not spawn.

Substantial / multi-file / parallel lanes / long-running: spawn builder. Keep long-blocking jobs off the parent so Enter can steer.
Substantial / multi-file / parallel lanes / long-running: spawn builder. Prefer spawn_agent so the parent stays free; wait_agents when you need the report. Keep long-blocking jobs off the parent so Enter can steer.

Docs/design (PRODUCT.md, ARCHITECTURE.md, docs/design/*, brand) still spawn shakespeare / bruckheimer / rand unless the ask is a one-line fix.

1. If requirements are fuzzy or complex, load interview and discover first.
2. Use explorer workers for scope when needed.
3. Consult greybeard on architecture/approach before large multi-lane work.
4. Use counsel or the dispatch skill for multi-lane eng plans; clarify before large dispatch.
5. Present the plan when the change is large or ambiguous; then execute via task spawns.
6. Track progress with manage_tasks; synthesize results for the operator.
5. Track progress with manage_tasks; synthesize results for the operator.

## If ORCHESTRATION → coordinate

Track with manage_tasks. Parallelize independent lanes. Escalate blockers with ask_operator. This is your core role.
Track with manage_tasks. Parallelize independent lanes via spawn_agent + wait_agents. After each spawn wave, update the operator before blocking. Escalate blockers with ask_operator. This is your core role.

## If COMMUNICATION → answer directly

Clear and short. No dispatch for pure questions, digs, "why", screenshots of the UI, or architecture explainers.
If you need one code path confirmed, one explorer worker — not a fleet. Prefer reading/searching yourself with mounted tools over spawning.
Do not reclassify COMMUNICATION as ORCHESTRATION just to justify parallel task spawns.
Do not reclassify COMMUNICATION as ORCHESTRATION just to justify parallel spawn waves.

# Non-negotiables

Expand Down
4 changes: 2 additions & 2 deletions src/agent/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,11 @@ export function buildGuidelines(
: [
"",
"Orchestration:",
"- Break multi-step or parallel work into focused `task` dispatches with distinct lenses; prefer several parallel task calls when jobs are independent.",
"- Break multi-step or parallel work into focused worker dispatches with distinct lenses; prefer `spawn_agent` (fire several in one turn when jobs are independent), then `wait_agents` for the reports you need. `task` remains the deprecated fused spawn+wait fallback for a single blocking worker.",
"- Prefer the typed spawn contract on every worker: `intent`, `success_criteria` (done-when), `do_not` (scope fence), and `report_focus` so workers finish instead of thrashing. Free-form `prompt` alone is weaker.",
"- After workers return, merge their Summary/Findings into a coherent answer for the operator; do not paste raw sub-agent dumps.",
"- If a worker comes back without finishing, change the brief rather than repeating it: narrow the scope, name the files, or state the done-when more sharply.",
"- Use manage_tasks for your own coordination checklist; spawning workers is `task`, not manage_tasks.",
"- Use manage_tasks for your own coordination checklist; spawning workers is `spawn_agent` / `wait_agents` (or deprecated `task`), not manage_tasks.",
"- If context is compacted automatically, do not stop tasks early due to token fear; persist progress via manage_tasks and worker reports.",
]),
].join("\n");
Expand Down
2 changes: 2 additions & 0 deletions src/prompts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ test("orchestrator guidelines teach the typed task spawn contract", () => {
expect(guidelines).toContain("do_not");
expect(guidelines).toContain("report_focus");
expect(guidelines).toContain("intent");
expect(guidelines).toContain("spawn_agent");
expect(guidelines).toContain("wait_agents");
});

test("primary guidelines advise against early-stop from compaction token fear", () => {
Expand Down
Loading