Subagent-Execute-Plan (Coordinator)
Relies on BDK foundation (STARTUP_INSTRUCTIONS.md) for project context and MCP tool preference.
This skill is a coordinator only. It holds plan state, builds an execution schedule, and dispatches subagents. It never edits files, never runs tests, never reads source code. Subagents do all work.
Why coordinator-only: isolated context per subagent → no pollution. The coordinator's context stays small (plan, TaskList, last subagent return) so it can run a long plan without hitting its own context limit. Subagents do the heavy lifting and discard their context when they return.
Core loop:
plan → explorer (group disjoint tasks) → for each group:
parallel implementers (background, in worktrees if needed) →
decide: tests/lints worth running now? →
if yes → test-runner / static-analyse subagents →
if findings → SendMessage original implementer OR spawn bdk:fixer →
commit group → next group
Subagent fleet
| Agent | Purpose | Model | Spawn timing |
|---|---|---|---|
bdk:explorer | Analyze plan tasks for file-disjoint groups | haiku | once, upfront |
bdk:implementer | Implement one task end-to-end (TDD only — no final lint/test) | sonnet | per task, parallel where safe, background |
bdk:test-runner | Run tests | haiku | per group, orchestrator's judgment |
bdk:static-analyse | Lint changed files | haiku | per group, orchestrator's judgment |
bdk:fixer | Apply specific findings | sonnet | on failures, when SendMessage to original is wrong fit |
bdk:code-reviewer | Review final branch diff | sonnet | once at end |
bdk:architecture-reviewer | Review architectural surface | opus | end, conditional |
The coordinator may spawn multiple implementers in parallel for a single group when bdk:explorer reports the tasks touch disjoint file sets. Same group → same worktree (disjoint files = no conflict).
Subagent return contract (REQUIRED)
Every implementer / fixer dispatch must return the YAML envelope defined in references/return-contract.md as its final message. Four statuses: DONE, DONE_WITH_CONCERNS, NEEDS_CONTEXT, BLOCKED. Anything else is treated as BLOCKED with reason "malformed return."
The dispatch prompt (Step 3b) MUST include the schema from references/return-contract.md verbatim and the line: "Final message must be exactly this YAML block — no prose before or after."
Reviewer / verification subagents have their own return formats — see references/dispatch-templates.md.
Step 0 — Prepare
-
Validate input.
$ARGUMENTSmust point to a plan in.bdk/plans/. If empty: list available plans, stop. -
Read plan once. For each task extract:
- Task number and title
- Full task text
Test cases:block (drives implementer's TDD)- File paths the task touches
-
Branch check. If on
main/master, stop with error. Coordinator never auto-switches branches. -
Working tree check.
git status --porcelain— if dirty, stop with error and list dirty paths. Refuse to commingle uncommitted user work with plan execution. -
Record
BASE_SHA.git rev-parse HEAD→ store as a coordinator-local variable. Used by Step 4a (BASE_SHA..HEAD) and Step 3g declared-vs-actual reconciliation. -
Resume detection. If a TaskList already exists for this plan slug (entries titled
[Group N] Task X.Y …), load it and skip to the firstpendinggroup. Else rebuild from plan in Step 2. -
Spawn
bdk:explorerto compute parallel groups (Step 1) — only if not resuming. -
Build TaskList (Step 2) — only if not resuming.
-
Print summary:
[subagent-execute-plan] Plan loaded: {path} Resume: {yes|no} Tasks: {N} Parallel groups: {G} (e.g. [1.1,1.2] [1.3] [2.1,2.2,2.3] [3.1]) Base SHA: {short-sha} Worktree mode: same-worktree (disjoint files within group) Test/lint cadence: orchestrator judgment per group (max 2 consecutive skips)
Step 1 — Explorer-driven group planning
Spawn bdk:explorer (one foreground call). Pass:
- The list of tasks (number, title, file paths declared in plan).
- The repo root.
- The schema and grouping rules from
references/explorer-contract.md(verbatim).
The explorer returns the JSON envelope defined in references/explorer-contract.md: confidence (float), groups[] (ordered, each with tasks + rationale), warnings[].
Fallback triggers (any → full serial mode, every task its own group): malformed JSON, confidence < 0.6, or any group referencing an undefined task id. See references/explorer-contract.md for full grouping rules and confidence calibration hints.
Step 2 — Build TaskList
One TaskCreate per task, all pending. Track group membership in the task content (e.g. [Group 1] Task 1.1 — Add lineChild group spec test). The coordinator may add dynamic tasks during execution: verify-batch, fix-lint, fix-test. Add them as new TaskList entries when spawned, mark completed when their subagent returns success.
Step 3 — Per-group loop
For each group in order:
3a. Pick implementer model per task
| Task profile | Model |
|---|---|
| 1–2 files, mechanical, full spec | haiku |
| Cross-file, integration, refactor | sonnet (default) |
| Architectural decision, broad surface, ambiguous spec | opus |
Re-dispatch after BLOCKED due to reasoning gap | escalate one tier |
See references/model-selection.md.
3b. Dispatch implementers (parallel if group has >1 task)
For a multi-task group, send one message with multiple Agent calls using run_in_background: true. Each call passes:
- Full task text inline (do not make subagent re-read the plan)
- Test cases block
- File paths the task touches
- One-paragraph architectural context
- Branch name and
BASE_SHA - The return-contract YAML schema (verbatim from the section above)
- Explicit instruction: "Do not run final lint or test verification. The coordinator schedules those separately."
For a single-task group: foreground or background — both fine. Background is cheaper if the orchestrator has nothing to do meanwhile.
You will be notified when each background agent completes. Do not poll.
3c. Wait for group completion, handle each implementer's status
Each implementer returns one of:
| Status | Coordinator action |
|---|---|
DONE | Record files_changed. Task is ready for verification. |
DONE_WITH_CONCERNS | Read concerns. Correctness/scope concern → queue a fixer. Observation only → log, proceed. |
NEEDS_CONTEXT | SendMessage(to: agent_id, …) with the missing context (cache likely warm). |
BLOCKED | Diagnose: bad context → SendMessage; reasoning gap → spawn fresh implementer one model tier up; task too large → split task in TaskList, re-dispatch first slice; plan wrong → log and stop with explicit error. |
| (malformed return) | Treat as BLOCKED with reason "malformed return." Re-dispatch fresh, same tier. |
Max 3 re-dispatch cycles per task before stopping the whole skill with an error report.
Re-dispatch ≠ fresh spawn. For
NEEDS_CONTEXTand small clarifications, preferSendMessage(to: "<agent_id>", ...)— the implementer keeps its prior reasoning. Spawn fresh only when escalating model tier. See STARTUP "Continuing a Spawned Agent".
3d. Decide: run tests/lints now? (pure judgment)
After all implementers in the group return DONE, decide whether to verify. No fixed cadence. Heuristics, not rules:
- Schema / API / public-contract change → likely yes.
- Trivial rename, comment-only edit, single-line tweak → likely no, batch with next group.
- Group included a "verify" task whose
Test cases:block IS the verification → yes, that's the whole point. - About to commit a group of >5 changed files → yes.
- Small group, low-risk, more tasks queued behind → defer to next checkpoint.
Hard cap: at most 2 consecutive groups may skip verifi