| name | deft-directive-swarm |
| description | Parallel local agent orchestration. Use when running multiple agents on story-level xBRIEFs simultaneously — to scan active/ for allocatable work, set up isolated worktrees, launch agents with proven prompts, monitor progress, handle stalled review cycles, and close out PRs cleanly. |
Deft Directive Swarm
Structured workflow for a monitor agent to orchestrate N parallel local agents working on story-level xBRIEFs from xbrief/active/.
Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
⚠️ See also: swarm.md | deft-directive-review-cycle
Platform Requirements
! This skill requires GitHub as the SCM platform and the GitHub CLI (gh) to be installed and authenticated. Issue fetching, PR creation, and post-merge verification all depend on gh.
Branch-Protection Policy Guard
! Before any state mutation (creating worktrees, dispatching sub-agents, opening PRs), run the skill-level branch-policy guard (#746 / #747). Halt with the actionable disclosure message when the project's plan.policy.allowDirectCommitsToMaster is unresolvable AND DEFT_ALLOW_DEFAULT_BRANCH_COMMIT is unset:
task verify:branch || exit 1
The swarm skill creates branches per agent so the guard is mostly informational here, but a malformed PROJECT-DEFINITION (missing plan.policy block AND no legacy narrative) is a fail-closed signal worth surfacing before the swarm spawns N agents.
Deterministic Questions Contract
! Every numbered-menu prompt rendered in this skill (Phase 0 Step 0 queue-driven promote prompts (#1142 / N2), Step 0.5 bridge approval gate, Step 5 final-approval gate, Phase 1 Step 3 file-overlap audit gate, Phase 5->6 ready-to-merge gate) MUST follow ../../contracts/deterministic-questions.md: render the canonical numbered menu in chat unless the host UI visibly preserves numeric option labels and returns numeric selections or exact displayed option text. The final two numbered options MUST be Discuss and Back, in that order. The Discuss-pause semantic is documented verbatim there -- on Discuss selection the agent MUST halt the in-progress sequence immediately, prompt What would you like to discuss?, and resume only on an explicit user signal. Implicit resumption is forbidden, and fallback chat replies MUST map only to the displayed number or exact displayed option text.
When to Use
- User says "run agents", "parallel agents", "swarm", or "launch N agents on stories"
- Multiple independent story-level xBRIEFs in
xbrief/active/ need to be worked on simultaneously
- A batch of stories are ready and have no mutual dependencies
Running Swarms in Grok Build / Non-Warp Environments
Minimal runtime contract for the Grok Build dispatch-provider path (one supported backend among several -- see Phase 3 Step 1b for provider-neutral heterogeneous routing):
- One isolated git worktree per agent (identical to the Warp path — see Phase 2)
- Workers launched via
spawn_subagent dispatch (Phase 3 Step 2d)
- Monitor coordination via worktree-state polling (
git status, git log) and get_command_or_subagent_output
- Review-cycle sub-agents spawned via
spawn_subagent (not start_agent)
This path became first-class in #1342 (platform adapter slices 1-3) and is fully documented in Phase 3 Step 2d and Phase 4. Grok Build + Windows users should also see #1353 (§3.5 in templates/agent-prompt-preamble.md) for shell output capture limitations that affect get_command_or_subagent_output in PowerShell 5.1 contexts. Refs #1342, #1331.
~ Windows + Grok Build (#1353): When issuing shell commands via run_terminal_command on this platform, avoid |, >, or 2>&1 in the command string — use Python pathlib/subprocess or plain task targets instead to avoid wrapper leakage. See templates/agent-prompt-preamble.md §3.5 for the full escape hatch list.
! Windows + Cursor Task-tool console windows (#2563): Local Cursor Task swarms on Windows are first-class. Shipped mitigations (do not regress): windowsHide: true (CREATE_NO_WINDOW) on engine/spawn paths, and warm-dist skip via tasks/ts-build-fresh.cjs so engine:_ts-build does not cold-rebuild when packages/cli/dist is current. See templates/agent-prompt-preamble.md §3.8. ! Default to local Task workers on Windows (not cloud-for-Windows). Parallel cohorts are allowed — do not force concurrency=1 for #2563. ⊗ Drop or weaken those mitigations without a replacement that keeps Windows local swarm workable.
Prerequisites
- !
xbrief/active/ contains one or more story-level xBRIEFs with status running
- ! GitHub CLI (
gh) is authenticated
- !
git supports worktrees (git worktree available)
- ~
oz CLI available (for oz agent run-cloud cloud launch — see Phase 3 Step 2c)
Phase 0 — Allocate
! Before assigning work to agents, build the cohort from the triage queue (queue-driven per #1142 / N2; see Step 0 below), then read project state and plan allocation against the activated cohort.
Headless cohort fast-path: low-ceremony launch (C1 / #1387)
! When the operator supplies a pre-approved cohort via the C1 task swarm:launch CLI, Phase 0 runs in headless / low-ceremony mode: the per-phase interactive approval gates (the Step 0c promote-fill prompts, the Step 0.5 lifecycle-bridge approval, and the Step 4/5 allocation approval) collapse into a SINGLE consent -- the ## Allocation context token (#1378) carried in the dispatch envelope. The interactive promote-fill loop (Step 0a -- 0d below) is SKIPPED.
! The C1 signature is task swarm:launch -- --stories <ids|paths> [--group <label>] [--worktree-map <path>] [--base-branch <branch>] [--autonomous]. --stories names the pre-approved story ids or xBRIEF paths; --group is an optional cohort label; --worktree-map points at the pre-created C3 worktree-map JSON consumed in Phase 2; --base-branch overrides the default master; --autonomous runs without the interactive launch confirmation.
! The SINGLE consent is the #1378 ## Allocation context token with dispatch_kind: swarm-cohort and a NON-NULL allocation_plan_id AND batching_rationale (the recognition contract in templates/agent-prompt-preamble.md § 2.5). That token IS the batched approval for the whole cohort -- the deterministic-question gates the interactive path runs (per ../../contracts/deterministic-questions.md) are bypassed wholesale on the headless path, not asked once per phase.
⊗ Re-prompt the operator for per-phase batching approval, or run the interactive promote-fill loop (Step 0a -- 0d), when a pre-approved cohort is supplied via task swarm:launch -- the headless path's single #1378 consent already authorizes the batch, and re-prompting mid-cohort violates the all-or-nothing dispatch-envelope rule (#954).
? The interactive queue-driven path (Step 0 below) remains the DEFAULT when no pre-approved cohort is supplied; the headless fast-path is the opt-in low-ceremony route for a cohort the operator has already curated and approved upstream.
Ordered-plan / cohort exhaustion (#2402)
! When the approved cohort (or an active plan-sequence of kind swarm/cohort/delivery) is exhausted, stop. Do not promote, queue, open, or dispatch adjacent work after the final approved entry unless the operator explicitly authorizes a new cohort or queue-driven selection.
! Continuation language ("next", "proceed") advances only within the approved cohort order — not into triage-queue remainder.
Step 0: Queue-driven cohort selection (#1142 / N2)
! Phase 0 is queue-driven: consult the triage cache (D2 / #1122 + D11 / #1128) for the ranked promotion candidates, then fill the WIP cap. Do NOT pick the cohort by hand from xbrief/pending/ or xbrief/active/ -- the queue is the canonical record of "what's next?" per AGENTS.md ## Cache-as-authoritative work selection (#1149). The four sub-phases below run in canonical order; existing Step 0.5 (lifecycle bridge) and Steps 1-5 (readiness / blockers / allocation / present / approval) proceed unchanged after Phase 0d.
Phase 0a -- State overview via task triage:summary (D2 / #1122)
- ! Run
task triage:summary to emit the current triage-cache one-liner ([triage] N untriaged ... WIP X/Y [⚠]). The monitor uses the result to:
- confirm the cache is fresh enough to act on (the D5 / #1127
task verify:cache-fresh warning is silent on a fresh cache; D2's one-liner is the human-readable parallel for the operator);
- read the current
pending/ + active/ count against the configured wipCap (default 20 per #2319, raised from the original 10 per umbrella #1119 Current Shape v3, exposed via plan.policy.wipCap).
- ! If the summary reports an empty cache (no candidates ever ingested), surface the bootstrap remediation (
task triage:bootstrap or the N3 / #1143 onboarding ritual task triage:welcome) and HALT Phase 0 -- there is no queue to drive cohort selection from.
task triage:summary
# [triage] 12 untriaged · 3 in-flight · WIP 4/10
Phase 0b -- Ranked candidates via task triage:queue (D11 / #1128)
- ! Run
task triage:queue --state=accept --limit=20 to surface the top-20 ranked promotion candidates. The queue is grouped ([RESUME] -> [URGENT] -> untriaged -> other) and ordered by updated_at within group (D11); the --state=accept filter restricts to issues whose latest triage decision is accept (the canonical "promote-ready" subset).
- ! Treat the queue as authoritative. Do NOT supplement the list with agent recall, open-GitHub-issue intuition, or memory of recent commits -- the queue is the rank; swarm does not re-rank.
- ! Present the candidate list to the operator as a numbered table (issue number, title, age in queue, top-line ranking rationale).
task triage:queue --state=accept --limit=20
Phase 0c -- Promote-fill-cap loop
! While pending/ + active/ count < wipCap AND the queue is non-empty, prompt the operator to promote the next ranked candidate to xbrief/pending/.
Loop body, per candidate (top-of-queue first):
-
! Render the next queue candidate with brief context (issue title, labels, top-1 ranking rationale).
-
! Prompt the operator: Promote #<N> to xbrief/pending/? [yes/skip/stop]. The final two numbered options remain Discuss and Back per ../../contracts/deterministic-questions.md.
-
On yes -- promote via the canonical lifecycle verb:
# D18 #1136 fallback: the eventual --from-issue=<N> shape is OPEN but not
# yet implemented. Until #1136 lands, the monitor resolves the candidate's
# xBRIEF file from the issue number (file lives in xbrief/proposed/ from a
# prior triage:accept step, D10 / #1129) and passes the path to
# `task scope:promote`. Same lifecycle command, just routed through the
# file path rather than the issue-number shortcut.
task scope:promote xbrief/proposed/<file>.xbrief.json
# TODO(#1136): when D18 ships, replace the two-step (resolve file from #N,
# then pass to `task scope:promote`) with the deterministic one-step
# `task scope:promote --from-issue=<N>` invocation. The integration point
# is this Phase 0c loop body; the operator-facing prompt collapses from
# "Promote #<N>? [resolved to <path>]" to "Promote #<N>?" with the path
# resolution done inside the task.
Re-run task triage:summary (or read the post-promote count directly) to refresh the pending/ + active/ total before the next loop iteration.
-
On skip -- drop this candidate from the current session's cohort; it stays in the queue for the next session. Advance to the next ranked candidate.
-
On stop -- exit the loop early; the partial cohort proceeds to Phase 0d.
! D18 #1136 integration point: the eventual task scope:promote --from-issue=<N> shape (D18 / #1136) is OPEN but not yet implemented. When it lands, the prompt above will be replaced with a deterministic task scope:promote --from-issue=<N> invocation; the operator no longer needs to resolve the xBRIEF file path manually. Until then, the file-path fallback above is the canonical Phase 0c verb -- it is the same task scope:promote lifecycle command, just routed through the file path rather than the issue-number shortcut. Track via #1136.
! WIP-cap exit-clean prose: When WIP cap is reached, swarm Phase 0 stops adding to the cohort and exits cleanly with a count of what was filled. Operator can demote (D1 / #1121, task scope:demote <existing> or task scope:demote --batch --older-than-days 30) to free slots or --force to override (the override is audit-logged as wip_cap_override in xbrief/.eval/scope-lifecycle.jsonl per D4 / #1124).
! Cohort recovery on cap-fill exit: If the queue surfaces 10 candidates but the cap allows only 4 more slots, the unpicked 6 stay queued for the next session. No state is lost; the queue is the canonical record. The operator can free a slot via task scope:demote <existing> (D1 / #1121) before re-running Phase 0, or accept the smaller cohort for this session.
Phase 0d -- Cohort dispatch
- ! After the promote-fill loop exits (cap reached, queue empty, or operator
stop), xbrief/pending/ now holds the cohort. Phase 0e below is now deprecated (#1891) -- per-role operator model routing (task swarm:routing-set, #1739) supersedes the sub-agent backend enum; task verify:routing -- --advise is the session-start disclosure surface. The existing Step 0.5 (Lifecycle Bridge -- Promote and Activate Proposed Scope xBRIEFs, #1025) moves the cohort pending/ -> active/, and Steps 1-5 (readiness report, blockers, allocation, present, approval) proceed against the activated set. Existing swarm Phase 1+ (Select, Setup, Launch, Monitor, Review, Close) proceeds unchanged.
Phase 0e -- Interactive sub-agent backend selection (DEPRECATED -- #1568 / superseded by #1739)
This phase is superseded. Per-role operator model routing (.deft/routing.local.json, #1739) is the current mechanism for recording which model each worker role uses. Run task verify:routing -- --advise at session start and task swarm:routing-set to configure routing decisions. The plan.policy.swarmSubagentBackend enum and task policy:subagent-backend(s) surface are still present but deprecated (#1891); do not consult them for new work.
~ If plan.policy.swarmSubagentBackend is already set in the project policy and no .deft/routing.local.json is present, surface a one-line nudge asking the operator to run task swarm:routing-set to migrate to the routing surface before dispatch.
⊗ Prompt the operator to select or persist a swarmSubagentBackend enum value for new work -- the routing surface (#1739) supersedes the enum; using the enum steers agents into a dead configuration path.
Phase 0f -- Greenfield swarm-ready bootstrap (#1053)
! Before allocation on a greenfield or just-setup project, run a greenfield swarm-ready bootstrap check that states project infrastructure is separate from machine-tool availability. A host may have task, uv, python, gh, and git installed (the #1187 machine-tool lane) while the project is still not swarm-ready.
! Check the project infrastructure needed by swarm launch: a git repository, GitHub remote visibility for later PR handoff, Taskfile wiring for task swarm:* / lifecycle gates, install layout consistency between source and consumer projections, .gitignore coverage for .deft-scratch/, and scratch/worktree readiness under .deft-scratch/worktrees/.
! When any required project infrastructure is missing, surface the exact remediation path and ask for explicit approval before creating or changing repo, remote, Taskfile, install layout, or gitignore state. Do not silently initialize a repository, add a remote, rewrite task includes, or create ignored scratch paths on the operator's behalf.
! When all in-scope candidates are freshly setup-created candidates from the same setup session, present one explicit batch confirmation before promoting and activating the full set through Step 0.5. The confirmation must name the candidate list and the lifecycle transition (proposed/ or pending/ -> active/) so the setup handoff is swarm-ready without asking once per file.
~ Setup-side handoff language SHOULD point here: after setup creates initial scope xBRIEFs, tell the operator that the swarm skill will verify or offer to create the remaining project infrastructure before allocation. #1187 remains the dependency for missing executable tools; #1053 owns the greenfield project-infrastructure bridge.
⊗ Treat #1187 machine-tool success as proof that a greenfield project is swarm-ready -- repo, remote, Taskfile wiring, install layout, gitignore, and scratch/worktree readiness are separate checks (#1053).
Manual / GitHub-issue escape hatch
? When the operator explicitly opts out of the queue (e.g. a one-off ad-hoc cohort that has not been ingested into the triage cache yet, or a swarm batch driven from a hand-supplied list of issue numbers), the monitor MAY fall back to the legacy GitHub-issue path:
- ! Fetch issue data:
gh api repos/<owner>/<repo>/issues/<N> (REST per templates/agent-prompt-preamble.md § 5; never the GraphQL gh issue view --json surface).
- ! Generate a minimal xBRIEF in
xbrief/proposed/ following the YYYY-MM-DD-descriptive-slug.xbrief.json naming convention (slug rules: ../../conventions/vbrief-filenames.md) and conforming to the canonical v0.6 schema (xbrief/schemas/xbrief-core.schema.json, strict const: "0.6"; see ../../conventions/references.md).
- ! Promote through the canonical lifecycle (
task scope:promote -- <path> then task scope:activate -- <path>), respecting the WIP cap and the same --force audit-logged override semantics as the queue-driven loop.
- ! Surface the opt-out reason in the Step 4 (Present Analysis) summary so a reviewer can see WHY the queue was bypassed.
⊗ Default to the manual escape hatch when the queue is non-empty -- the cache-as-authoritative directive (AGENTS.md ## Cache-as-authoritative work selection (#1149)) requires consulting the queue first.
Step 0.5: Lifecycle Bridge -- Promote and Activate Proposed Scope xBRIEFs (#1025)
! Before running the Step 1 preflight gate, scan xbrief/proposed/ and xbrief/pending/ for candidate scope xBRIEFs and bridge them to xbrief/active/. The deft-directive-setup skill Phase 3 (Output -- Light Path / Output -- Full Path) deposits new scope xBRIEFs in xbrief/proposed/; the deft-directive-refinement skill Phase 4 (Promote/Demote) deposits them in xbrief/pending/. The swarm Phase 0 Step 1 preflight gate (task xbrief:preflight) only accepts xBRIEFs in xbrief/active/ with plan.status == "running", so candidates in proposed/ or pending/ MUST be bridged through the canonical lifecycle (proposed -> pending -> active) before allocation. Without this bridge, the monitor discovers the gap at runtime as a wholesale preflight rejection (Invalid transition: 'activate' requires file in pending/), as in the originating 2026-05-10 first-session consumer swarm.
! Scan: list every *.xbrief.json under xbrief/proposed/ and xbrief/pending/. Cross-reference each candidate against the user's stated swarm scope (the issue numbers / xBRIEF filenames the user asked the monitor to swarm on). Candidates outside the stated scope MUST NOT be promoted or activated by this bridge -- they may be in a deliberate refinement queue owned by skills/deft-directive-refinement/SKILL.md Phase 4.
! Present: render a numbered list of in-scope candidates to the user with their current lifecycle folder (proposed/ vs pending/) and plan.status. Render the canonical numbered menu in chat unless the host UI visibly preserves the same numeric option labels and returns numeric selections or exact displayed option text. The final two numbered options MUST be Discuss and Back per ../../contracts/deterministic-questions.md.
! Approve: wait for explicit user approval (yes, confirmed, approve) before any lifecycle mutation. Broad affirmative continuation phrases (proceed, do it, go ahead) are NOT authorisation -- the bridge MUST be explicitly confirmed because promoting + activating a scope xBRIEF is a lifecycle commitment that flips plan.status to running and clears the #810 implementation-intent gate for downstream agent dispatch.
! Bridge: for each approved candidate, run the canonical lifecycle commands in order:
- For candidates in
xbrief/proposed/: task scope:promote -- <path> (moves to pending/, status pending), THEN task scope:activate -- <path-in-pending> (moves to active/, status running).
- For candidates already in
xbrief/pending/: task scope:activate -- <path> alone (moves to active/, status running).
Both commands are idempotent: a same-folder move with matching status is a no-op. If either command exits non-zero, surface the exit message verbatim, do NOT attempt to allocate against the failed candidate, and ask the user how to route.
! Verify: re-run the scan and confirm each approved candidate now lives in xbrief/active/ with plan.status == "running". Only candidates that pass this verification advance to Step 1 (Read Project State); the rest stay surfaced as preflight rejections.
⊗ Auto-promote + activate every candidate in xbrief/proposed/ or xbrief/pending/ without explicit user approval -- proposed-stage xBRIEFs may be in a deliberate refinement queue (skills/deft-directive-refinement/SKILL.md Phase 4) and silent promotion bypasses the user's lifecycle intent.
⊗ Skip the lifecycle bridge and let the Step 1 preflight gate (task xbrief:preflight) reject the candidates wholesale -- the gate's exit message tells the user WHAT failed but not WHY the source folder was wrong; the bridge is the contract that prevents that confusion before it surfaces.
⊗ Promote candidates outside the user's stated swarm scope. The bridge is scope-bounded by what the user asked the monitor to swarm on; out-of-scope candidates remain in proposed/ / pending/ for the refinement skill to own.
Cross-references:
- Setup-side deposit point:
skills/deft-directive-setup/SKILL.md Phase 3 Output -- Light Path / Output -- Full Path (scope xBRIEFs land in xbrief/proposed/).
- Refinement-side deposit point:
skills/deft-directive-refinement/SKILL.md Phase 4 -- Promote/Demote (lifecycle transitions via the same task scope:promote / task scope:activate surface).
- Underlying CLI:
task scope:promote / task scope:activate (the deterministic state machine; idempotent on same-folder moves; three-state exit 0 / 1 / 2).
- Recurrence record: issue #1025 (2026-05-10 first-session consumer tic-tac-toe swarm; monitor hit
Invalid transition: 'activate' requires file in pending/ on all four candidate xBRIEFs because they were still in proposed/).
Step 1: Read Project State and Readiness Report
- ! Scan
xbrief/active/ for candidate xBRIEFs (files matching *.xbrief.json)
- ! For each candidate xBRIEF, MUST run
task xbrief:preflight -- <path> (the structural intent gate, #810) to validate lifecycle eligibility before allocation work. Skip any xBRIEF that exits non-zero -- the helper's stderr message is the actionable redirect (task xbrief:activate <path>). Surface the exit message in the Phase 0 Step 4 analysis so the user can route the lifecycle move; do NOT attempt to allocate, dispatch, or implement against a xBRIEF that fails the preflight.
- ! Run
task swarm:readiness -- xbrief/active/*.xbrief.json before any agent allocation. This deterministic report is the allocator's source of truth for ready stories, blocked stories, decomposition-needed epics/phases, dependency waves, conflict groups, file overlap matrix, and missing fields.
- ! Treat
plan.metadata.kind = "epic" and plan.metadata.kind = "phase" as needs decomposition, not merely incomplete. Route broad scopes to skills/deft-directive-decompose/SKILL.md instead of assigning them to workers.
- ! Read only readiness-approved story fields for allocation:
plan.title, plan.status, non-empty plan.items, planRef, references, plan.metadata.kind, and plan.metadata.swarm.
- ! Read
xbrief/PROJECT-DEFINITION.xbrief.json for project-wide context (narratives, scope registry)
- ! Determine the base branch: ask the user which branch to target for worktree creation, PR targets, and rebase cascade (default:
master). Record this as the configured base branch for all subsequent phases.
- ⊗ Spawn an implementation agent (via
start_agent, oz agent run, Warp tab dispatch, or any other path) for a xBRIEF that has not passed task xbrief:preflight -- the gate is the only authorization signal; affirmative continuation phrases and workflow-shape vocabulary are NOT (#810).
- ⊗ Allocate concurrent workers unless candidates are swarm-ready
kind=story xBRIEFs with non-empty executable plan.items and task swarm:readiness exits 0.
- ⊗ Use manual file-overlap reasoning as the only safety check; use the readiness report first, then explain any additional human judgment.
Step 2: Surface Blockers
- ! Identify blocked xBRIEFs (status
blocked) and their blocking reasons (check narrative fields)
- ! Identify xBRIEFs with incomplete acceptance criteria (no
plan.items or empty items array)
- ! Identify epic/phase scope xBRIEFs from the readiness report and route them to decomposition
- ! Identify dependency conflicts between candidate xBRIEFs (e.g. story A depends on story B via
planRef or edges, but B is assigned to a different agent or is incomplete)
- ! Flag any candidate xBRIEFs whose prerequisites are unmet
Step 3: Plan Allocation
! The monitor allocates one or more xBRIEFs to each agent based on scope, complexity, and dependencies. There is no fixed per-agent limit.
- ! Small/independent stories can be batched to a single agent only after explicit operator approval or an approved allocation plan -- group related or low-complexity xBRIEFs together and record the batching rationale
- ! Large/complex stories get dedicated agents — a story with broad file scope or high acceptance criteria count should not share an agent
- ! Dependency-aware grouping — xBRIEFs that share
planRef to the same epic or have edges between them should be assigned to the same agent when possible, OR sequenced with clear ordering
- ! The monitor decides allocation dynamically — no hardcoded 1:1 rule
- ! WIP cap awareness (#1124 / D4 of #1119) — the cohort + any bridge-promoted candidates (Step 0.5) MUST fit within
plan.policy.wipCap (default 20 per #2319, raised from the original 10 per umbrella #1119 Current Shape v3). When pending/ + active/ count is at-or-above the cap, task scope:promote refuses with an error message naming task scope:demote <existing> and task scope:demote --batch --older-than-days 30 as the relief valves. The monitor MUST drain the WIP set via task scope:demote (D1 / #1121) before promoting more candidates, OR open a per-promote task scope:promote <file> --force (audit-logged as wip_cap_override in xbrief/.eval/scope-lifecycle.jsonl) for the genuinely time-critical case. task triage:summary (D2 / #1122) surfaces the cap as WIP X/Y with a warning glyph when at-or-above cap.
Step 4: Present Analysis
! Present a summary to the user containing:
- Candidate xBRIEFs: story-level xBRIEFs eligible for assignment (with titles, statuses, and origin references)
- Readiness report: ready stories, blocked stories, decomposition-needed epics/phases, dependency waves, conflict groups, file overlap matrix, and missing fields from
task swarm:readiness.
- Preflight rejections (#810): any xBRIEFs that failed
task xbrief:preflight in Step 1 -- include the file path AND the helper's exit message verbatim so the user can route the appropriate task xbrief:activate <path> move. These xBRIEFs MUST NOT be allocated until they pass the preflight on a re-run.
- Blockers found: blocked xBRIEFs, unresolved dependencies, items requiring design decisions
- Decomposition needed: epic/phase scopes that must go through
skills/deft-directive-decompose/SKILL.md before swarm allocation
- Incomplete xBRIEFs: stories with missing or empty acceptance criteria
- Allocation plan: which agent gets which xBRIEF(s), with reasoning for batching decisions; multi-story batching is allowed only after explicit operator approval or approval of this allocation plan
- Tentative version bump: current version (from CHANGELOG.md or latest git tag) and proposed next version (patch/minor/major) based on the scope and nature of candidate items — this is advisory and will be confirmed before merge cascade
Step 5: Get User Approval
- ! Wait for explicit user approval (
yes, confirmed, approve) before proceeding to Phase 1 (Select)
- ! If the user requests changes to the allocation plan, re-analyze and re-present
- ⊗ Proceed to Phase 1 (Select) without completing the allocate phase and receiving explicit user approval
Phase 1 — Select
! Finalize assignments from the allocation plan. Each agent gets a coherent set of related work.
Step 1: Confirm Candidates
- ! Use the allocation plan and xBRIEF analysis from Phase 0 as the starting point
- ! Re-read
xbrief/active/ only if Phase 0 was skipped (user override) or context was lost
- ! For each candidate xBRIEF, verify its
plan.status is running (not blocked or completed)
- ! Exclude xBRIEFs that are blocked, have unresolved dependencies, or require design decisions
Step 2: File-Overlap Audit
! Before assigning tasks to agents, start from the task swarm:readiness file-overlap matrix and conflict groups, then list every file each xBRIEF's acceptance criteria are expected to touch.
- ! Verify ZERO file overlap between agents — no two agents may modify the same file
- ! Check transitive file touches, not just primary scope — trace each xBRIEF's acceptance criteria to specific files. A task may require changes to files outside its obvious scope (e.g., an enforcement task adding an anti-pattern to a skill file owned by another agent).
- ! Shared files (CHANGELOG.md) are exceptions — each agent adds entries but does not edit existing content
- ! If overlap exists, reassign tasks until overlap is eliminated
⊗ Proceed to Phase 2 while any file overlap exists between agents (excluding shared append-only files).
⊗ Assume a task only touches files in its primary scope — always check acceptance criteria for cross-file requirements.
Step 3: Present Assignment
- ! Show the user: agent number, branch name, assigned xBRIEF(s) (with origin issue numbers), and files each agent will touch
- ~ Wait for user approval unless the user explicitly said to proceed autonomously
Phase 2 — Setup
Step 1: Create Worktrees
! Two modes (C3 / #1387): Phase 2 either CONSUMES a pre-created worktree map (the headless path, when task swarm:launch --worktree-map <path> supplied one) or creates worktrees itself (the interactive path). Mode A is preferred whenever a map is present; Mode B is the default otherwise.
Mode A -- Pre-created worktree map (C3, headless via --worktree-map)
- ! When
task swarm:launch -- ... --worktree-map <path> supplied a pre-created worktree map (C3), Phase 2 CONSUMES it instead of running git worktree add per agent. The C3 map is a JSON array of { "story_id": str, "worktree_path": str, "base_branch": str }.
- ! The launch engine resolves the worktree map via
resolveWorktreeMap (packages/core/src/swarm/worktrees.ts), which validates normalized C3 records and RAISES on same-path collisions or base-branch mismatches. The monitor MUST surface any such raise verbatim and HALT setup -- a same-path collision means two agents would share one worktree (the Duplicate-Agent Failure Mode in Phase 4).
- ! Each resolved record's
worktree_path and base_branch feed straight into Phase 3 dispatch and MUST match the C2 launch-manifest's worktree_path / branch fields for the same story_id.
Mode B -- Monitor-created worktrees (interactive path)
For each agent, create an isolated git worktree:
git worktree add <path> -b <branch-name> <configured-base-branch>
- ! One worktree per agent under deterministic ignored scratch paths by default:
.deft-scratch/worktrees/<story-id>. This matches the headless task swarm:launch default and keeps interactive swarms from cluttering sibling checkout directories in the user's projects folder.
- ! If the C2 launch manifest is present, use the launch manifest's resolved
worktree_path for that story instead of inventing a new path.
- ?
%TEMP% or another OS temp location is an explicit override only for throwaway CI or rehearsal runs. When using OS temp, say that the worktree may disappear with temp cleanup and is not the durable default.
- ! Branch naming:
agent<N>/<type>/<issue-numbers>-<short-description> (e.g. agent1/cleanup/31-50-23-strategy-consolidation) — the agent number prefix aids traceability since GitHub PR numbers won't match agent numbers
- ! All worktrees branch from the same base (the configured base branch from Phase 0)
Step 2: Generate Prompt Files
! Create a launch-agent.ps1 (Windows) or launch-agent.sh (Unix) in each worktree using the Prompt Template below.
~ Also prepare plain-text prompt versions for pasting into Warp agent chat or other terminal interfaces.
Phase 3 — Launch
Step 0: Populate the allocation-context consent token (#1378)
! Before dispatching ANY worker prompt -- swarm cohort OR solo -- the dispatcher MUST populate a ## Allocation context section (the frozen schema defined in templates/agent-prompt-preamble.md, Story A of #1378) in every launched agent's dispatch envelope. Populate all five fields in order: dispatch_kind (solo | swarm-cohort), allocation_plan_id, batching_rationale, cohort_vbriefs, and operator_approval_evidence.
- ! For a swarm cohort, set
dispatch_kind: swarm-cohort with a non-null allocation_plan_id (the Phase 0 allocation-plan snapshot path or the monitor session id) AND a non-null batching_rationale (the one-line rationale from the Phase 0 Step 4 allocation plan), and list the full cohort in cohort_vbriefs. This is the structured consent token the worker's build-skill Step 0 recognizes mechanically (#1378 Story B), so the worker processes its cohort without re-prompting the parent for batching approval mid-cohort.
- ! For a solo dispatch, set
dispatch_kind: solo and list the single assigned xBRIEF in cohort_vbriefs; allocation_plan_id and batching_rationale MAY be null. Populating the section even for solo dispatches keeps the recognition surface uniform across every launch path.
⊗ Dispatch a worker prompt (cohort or solo) without a populated ## Allocation context section -- an absent section forces the worker back onto the #1371 prose carve-out fallback and forfeits the deterministic consent-token recognition the structured section enables (#1378).
Step 0.5: Consume the launch-manifest before dispatch (headless path, C2 / #1387)
! On the headless path, before dispatching ANY worker, the monitor consumes the C2 launch-manifest emitted by task swarm:launch -- a JSON array of { "story_id": str, "vbrief_path": str, "worktree_path": str, "branch": str, "allocation_context": {...} }, where each record's allocation_context is the #1378 token (its five fields dispatch_kind, allocation_plan_id, batching_rationale, cohort_vbriefs, operator_approval_evidence, per templates/agent-prompt-preamble.md § 2.5). Each record carries everything one worker dispatch needs.
! On the headless path the manifest's per-record allocation_context already satisfies Step 0 above -- the consent token is pre-populated, so the monitor READS it from the manifest rather than re-assembling the ## Allocation context section by hand.
! Manifest consumption is PREP ONLY. It supplies the per-agent dispatch parameters (worktree_path, branch, vbrief_path, allocation_context); the spawn itself remains agent-driven via the runtime-detected launch path (Step 2a start_agent / Step 2d spawn_subagent). task swarm:launch emits the manifest and STOPS -- it does NOT spawn agents.
⊗ Treat the C2 launch-manifest as the spawn itself -- it is dispatch-prep / handoff data, not an agent-launch primitive. The actual dispatch still goes through the platform adapter (Step 2a / 2d per the runtime detection below); the manifest replaces the manual per-agent parameter assembly, NOT the spawn primitive.
? On the interactive path (no task swarm:launch, no manifest), the monitor assembles each dispatch's parameters from the Phase 1 assignment plus the Step 0 token by hand, as before.
Step 1: Runtime Capability Detection
! Before selecting a launch method, probe the environment to determine the best available path.
- ! Probe for
start_agent tool — check the available tool set for start_agent (or equivalent agent-orchestration tool). Its presence indicates a Warp environment with native orchestration support.
- ! Probe for Warp environment — if
start_agent is not available, check for WARP_* environment variables (e.g. WARP_TERMINAL_SESSION, WARP_IS_WARP_TERMINAL). Their presence indicates Warp without orchestration.
- ! Probe for the Cursor
Task tool — when neither start_agent nor WARP_* is present, check the tool set for the Cursor Task sub-agent tool (dispatchable in the background via run_in_background: true). Its presence indicates a Cursor environment with a first-class Tier-1 sub-agent primitive (#1877). Classify as cursor-composer for an interactive Composer session and cursor-cloud-agent for a Cursor cloud agent. Cursor is Tier 1 → Approach 1 — do NOT let it fall through to generic-terminal / the Approach-3 blocking poll.
- ! Probe for
spawn_subagent tool — when none of start_agent, WARP_*, or the Cursor Task tool is present, check for spawn_subagent (Grok Build / non-Warp TUI launch adapter, #1342 slice 2). Its presence indicates the grok-build platform.
- ! Select launch path automatically based on detection results — do NOT present static options:
start_agent available → Orchestrated launch (Step 2a) — preferred path, fully automated, no manual tab management
start_agent unavailable, Warp detected → Interactive Warp tabs (Step 2b) — full MCP, global rules, warm index; requires manual tab management
- Cursor
Task tool available (no start_agent, no WARP_*) → Cursor sub-agent launch (Step 2e) via the Task tool with run_in_background: true (Tier 1 / Approach 1) — keeps the monitor pane interactive; descriptor is cursor-composer (interactive) or cursor-cloud-agent (cloud)
grok-build (spawn_subagent available, no start_agent, no WARP_*, no Cursor Task tool) → Grok Build launch (Step 2d) — first-class non-Warp path
- No orchestration primitive detected →
generic-terminal degraded launch. Offer a Serial self-execution downgrade first: with explicit operator consent, the monitor may execute the prepared worker prompts itself one story at a time from the isolated worktrees. This preserves forward progress but is not true concurrent swarm execution.
- ! Return a stable platform descriptor for downstream phases — one of
warp-orchestrated (start_agent available), warp-manual (Warp without start_agent), cursor-composer (Cursor Task tool, interactive Composer), cursor-cloud-agent (Cursor Task tool, cloud agent), grok-build (spawn_subagent available, non-Warp, non-Cursor), or generic-terminal (no orchestration primitives). The detection matrix MUST include explicit absence checks for start_agent, WARP_*, and the Cursor Task tool so the six descriptors are unambiguous. Phase 4 monitoring and Phase 6 sub-agent dispatch read this stable platform descriptor as a single source of truth instead of re-running detection per call.
- ? Cloud escape hatch — use
oz agent run-cloud (Step 2c) ONLY if the user explicitly requests cloud execution. Never default to cloud. (The Cursor cursor-cloud-agent descriptor above is distinct — it is a Cursor-native cloud agent detected via the Task tool, not the oz escape hatch.)
! In generic-terminal mode, if the operator declines serial self-execution, the manual terminal prompt-paste fallback remains available: the user can paste each generated prompt into any terminal or agent interface with access to the matching worktree. Surface the tradeoff clearly: manual paste preserves user control but requires tab/process management and is still not automated orchestration.
⊗ Do not describe this downgrade as a swarm, parallel execution, or concurrent orchestration. It is serial fallback execution: one story at a time, same gates, same isolated worktrees, lower coordination value (#1053).
⊗ Present static launch options (A/B/C) instead of detecting capabilities at runtime.
⊗ Offer Warp-specific launch paths (tabs, start_agent) when not running inside Warp — gate on WARP_* environment variables or start_agent tool presence.
Step 1a: Worker Runtime and GitHub Auth Preflight (#1557)
! Before dispatching workers that will call gh, probe the worker execution envelope (not the parent monitor shell) for runtime mode and GitHub credential readiness. The read-only capability probe (packages/core/src/platform/platform-capabilities.ts, #1557a) and auth validator (packages/core/src/intake/github-auth-modes.ts, #1557b) MUST run from the same environment the worker will use.
- ! Classify runtime mode — run the capability probe from each worker worktree (or dispatch target):
task verify:tools -- --json
The probe returns one of:
local-unsandboxed — interactive local shell without Cursor native sandbox
cursor-native-sandbox — Cursor native sandbox; effective UID 0 inside the worker is a sandbox identity, not host root
cloud-headless — cloud or headless agent runtime without local host context
-
! Interpret Cursor sandbox UID remap — when sandbox_uid_remap is true, effective UID 0 inside the worker is remapped to the host user, not real root. The probe sets identity_kind to sandbox-remapped-local-user. Do NOT present sandbox UID 0 or sandbox-root ownership as proof of host-root access — cwd ownership and /proc/self/uid_map are interpreted as a sandbox view of the host filesystem, not as the host running as root.
-
! Validate GitHub auth from the worker environment — run auth validation from the same envelope:
task verify:gh-auth -- --json
Modes:
host-gh (default for local-unsandboxed and cursor-native-sandbox) — requires gh auth status and a minimal GitHub API reachability check from the worker environment
injected-token (default for cloud-headless) — requires GH_TOKEN, GITHUB_TOKEN, or GH_ENTERPRISE_TOKEN; fails closed with missing_injected_token when absent and never falls back to host gh credential store
-
! Surface remediation when parent host auth works but worker auth fails — a common failure mode is the parent shell passing gh auth status while the worker sandbox cannot authenticate or reach GitHub. When validation reports gh_auth_failed, api_unreachable, or repo_access_denied in cursor-native-sandbox, surface these remediation paths to the operator (token values MUST NOT enter prompts or transcripts):
- Full-access execution — run the GitHub step with full filesystem/network access so the worker shares the host
gh credential store
- Trusted
gh command allowlisting — allowlist the trusted gh command path for the worker sandbox
- Injected-token handoff — bind credentials at the invocation layer (
GH_TOKEN / GITHUB_TOKEN) without pasting token values into dispatch envelopes
-
! Cloud/headless injected-token failure — when runtime mode is cloud-headless and no injected token is available, validation fails with missing_injected_token. Do NOT assume host gh state is visible to cloud workers; re-dispatch with injected-token handoff or switch to a local interactive runtime.
⊗ Assume parent-shell gh auth status proves worker-environment readiness — always validate from the worker envelope (#1557).
⊗ Present sandbox UID 0 or sandbox-root cwd ownership as host-root access — UID remap means sandbox identity is a view of the host user (#1557).
⊗ Paste GH_TOKEN / GITHUB_TOKEN values into worker prompts or dispatch envelopes — use invocation-layer handoff only (#1557).
Cross-references: packages/core/src/platform/platform-capabilities.ts (#1557a), packages/core/src/intake/github-auth-modes.ts (#1557b), docs/subagent-heartbeat.md (runtime/auth troubleshooting). Refs #1557.
Step 1b: Provider-neutral sub-agent routing (#1531)
! Heterogeneous dispatch is provider-neutral. Tiered / heterogeneous swarm topology is an opt-in extension of the platform adapter (#1342 / #1331), not a Grok Build-only path. When routing leaf workers, the monitor separates three concerns that MUST NOT be collapsed:
- Dispatch provider — the runtime primitive or adapter that launches the child worker (e.g.
spawn_subagent, start_agent, Cursor Composer/task agents, cloud agents, or a future adapter).
- Worker role — what the child is permitted to do: leaf implementation, orchestrator/strategist, review-cycle monitor, conflict-resolution rebase, merge, or release gate. Role boundaries are load-bearing regardless of which dispatch provider is active.
- Model or agent selection — the operator or harness policy that maps role plus xBRIEF attributes to a concrete agent/model. deft stays model-agnostic at dispatch time; the harness or provider backend resolves the concrete model.
! Supported backend examples (none mandatory): Composer-class coding agents, Grok Build spawn_subagent workers, Cursor/cloud agents, and future adapters are all first-class examples. No single backend is required — Grok Build is one implementation of provider-neutral routing, not the only target.
! Operator model routing (#1739): the concrete per-role model lives in the gitignored, per-machine .deft/routing.local.json, keyed by (dispatch_provider, worker_role). Record a decision with task swarm:routing-set -- --role <role> (--model <slug> | --harness-default). task swarm:launch resolves the active provider's route and stamps resolved_model + model_source into each C2 manifest record. When resolved_model is non-null, the monitor MUST pass it as the model argument of the actual dispatch primitive (e.g. the Task tool's model field for a Cursor sub-agent) — stamping the manifest is prep; a recorded model that never reaches the spawn call is the bug #1739 closes. Run task verify:routing before dispatching a cohort (pre-dispatch hard gate; fails when a dispatched role is undecided) and task verify:routing -- --advise at session start (non-blocking disclosure). For harness-bound providers (e.g. grok) only --harness-default is recordable and resolved_model stays null.
~ DEPRECATED — Policy surface (#1531a / #1891): plan.policy.swarmSubagentBackend (set via task policy:subagent-backend) was the previous mechanism for recording the operator's preferred coding sub-agent provider. It is superseded by per-role operator model routing above (#1739). The enum and associated task policy:subagent-backend(s) tasks remain functional but deprecated; hard deletion is tracked by #1860. Use task swarm:routing-set instead.
! Role boundaries for cheaper leaf agents: Cheaper, high-context leaf agents are appropriate for leaf implementation work in isolated worktrees when xBRIEF scope is tight and gates (task check, Greptile review cycle) hold. The following roles MUST remain on strong, review-capable agents regardless of backend availability:
- orchestration and cohort monitoring (the monitor / strategist conversation)
- review-cycle decisions (fix-or-defer judgment, P0/P1 triage)
- conflict-resolution rebase during merge cascades
- merge cascade execution and protected-issue gates
- release gates (Phase 5->6 version bump approval,
task release surfaces)
⊗ Treat Grok Build spawn_subagent as the only supported sub-agent backend in swarm guidance — provider-neutral routing explicitly includes Composer-class coding agents, Cursor/cloud agents, and future adapters (#1531).
⊗ Route orchestration, review-cycle decisions, conflict-resolution rebase, merge cascade, or release gates to cheaper leaf agents — irreversible-damage surfaces stay on strong/review-capable agents (#1531).
Cross-references: packages/core/src/swarm/routing.ts (SWARM_WORKER_ROLES), templates/agent-prompt-preamble.md (dispatch envelope metadata), docs/the-harness-is-everything.md (orchestrator -> commodity-coder layering). Refs #1531.
Orchestrator dispatch doctrine (#1880)
! Deliberate model routing before ANY dispatch: Before launching ANY worker in this phase (cohort OR solo), run task verify:routing and resolve each (dispatch_provider, worker_role) via task swarm:routing-set / .deft/routing.local.json. Populate ## Worker metadata per templates/agent-prompt-preamble.md §2.6 and pass resolved_model into the actual dispatch primitive when non-null. Never silently inherit the monitor's model. Deterministic gate enforcement is #1877; this rule is behavioral doctrine (#1880).
! Worker-owns-lifecycle (Gap C): Every implementation-worker dispatch prompt MUST declare the unit-of-work boundary: stop-at: pr-open OR drive-to: merge-ready (default for story xBRIEF work). Workers scoped drive-to: merge-ready own pre-PR, push, PR open, Greptile review-cycle poll/fix, and the #1259 Step 6 fail-closed exit as ONE dispatch — they spawn their own review poller per skills/deft-directive-review-cycle/SKILL.md monitoring tiers. The monitor MUST NOT plan a separate post-PR review leaf for a worker already scoped merge-ready.
! Background / independent dispatch (Gap D): Dispatch implementation, fix, and review-cycle workers independently / in the background when the platform supports it. On Cursor, use the Task tool background path (run_in_background: true) so the monitor conversation stays interactive. Foreground dispatch is for short tasks (<~3 min) only.
⊗ Hand back at PR-open and re-dispatch separate review-monitor or fix leaf agents for a worker whose envelope scoped drive-to: merge-ready (#1880 Gap C).
⊗ Foreground/blocking dispatch for long-running implementation, fix, or review-cycle workers when background dispatch is available (#1880 Gap D).
Step 2a: Orchestrated Launch (start_agent available)
! When start_agent is detected in the tool set, use it directly to launch each agent.
- ! Launch one agent per worktree using
start_agent with the generated prompt and worktree path as the working directory
- ! Agents inherit the current environment's MCP servers, Warp Drive rules, and codebase index — equivalent to interactive Warp tabs but without manual tab management
- ! No user intervention needed — launch is fully automated
- ~ This is the preferred path: richest context with zero manual overhead
Step 2b: Interactive Warp Tabs (start_agent unavailable, Warp detected)
! When start_agent is not available but Warp is detected (via WARP_* environment variables), fall back to manual Warp tab launch — briefly note that orchestrated launch is not available in this session, then proceed with the tab instructions below.
! Warp tabs cannot be opened programmatically. There is no API or CLI command to open a new Warp terminal tab from an agent or script.
Ask the user to open N new Warp terminal tabs. For each tab, the user:
- Navigates to the worktree:
cd <worktree>
- Pastes the prompt directly into the Warp agent chat input (not the terminal)
Context advantages of Warp tabs:
- Global Warp Drive rules (personal rules auto-injected)
- MCP servers via UUID (GitHub, etc. — zero-config)
- Warp Drive notebooks, workflows, and other auto-injected context
- Warm codebase index from the active Warp session (no cold-start delay)
- Agent is interruptible and steerable mid-run
Tradeoff: Requires the user to manually open and manage one Warp tab per agent.
? If not running inside Warp at all (no WARP_* variables, no start_agent), use the same tab approach but with any terminal emulator — the user pastes prompts into their preferred terminal or agent interface.
Step 2c: Cloud Agents (explicit user request only)
! Use oz agent run-cloud ONLY when the user explicitly requests cloud execution. Never default to this path.
oz agent run-cloud --prompt "TASK: You must complete..."
Agents execute on remote VMs without local MCP servers, codebase indexing, or Warp Drive rules. Agents MUST use gh CLI for GitHub operations. AGENTS.md is the only behavioral control surface.
Tradeoff: Fully automated with zero tab management, but context-starved — no MCP, no Warp Drive rules, no codebase indexing. Best for self-contained tasks that don't need rich local context.
⊗ Default to cloud launch — it is an escape hatch, not a default path.
⊗ Use oz agent run-cloud when the user expects local execution — run-cloud routes to remote VMs with no local context.
Step 2d: Grok Build Launch (spawn_subagent available)
! When the platform descriptor is grok-build (spawn_subagent detected, no start_agent, no WARP_*), dispatch each worker via spawn_subagent with:
- The canonical
templates/agent-prompt-preamble.md content as the preamble
- The standard worktree prompt (STEP 1-6 from the Prompt Template below), adapted to use
get_command_or_subagent_output for polling rather than start_agent lifecycle events
- The worktree path set to the agent's isolated git worktree
~ This is the first-class non-Warp path. Workers use worktree state polling (git status, git log) and get_command_or_subagent_output as their coordination channel instead of Warp tab state.
Step 2e: Cursor Launch (Task tool available) — #1877
! When the platform descriptor is cursor-composer or cursor-cloud-agent (Cursor Task tool detected, no start_agent, no WARP_*, no spawn_subagent), dispatch each worker via the Cursor Task tool with:
- The canonical
templates/agent-prompt-preamble.md content as the preamble (AGENTS.md read mandate, #810 xBRIEF gate, #798 PowerShell UTF-8, pre-PR + review-cycle mandates).
- The standard worktree prompt (STEP 1-6 from the Prompt Template below).
- The worktree path set to the agent's isolated git worktree.
- !
run_in_background: true for any worker or poller whose loop runs longer than a short task (~3 min) — implementation, fix, and review-cycle workers — so the monitor conversation pane stays interactive (#1880 Gap D). The parent is notified on completion.
- ! Deliberate model routing (#1739): pass the route's
resolved_model (when non-null) as the Task tool's model argument — stamping the C2 manifest is prep; the recorded model MUST reach the actual spawn call.
~ This is the first-class Cursor path. It is Tier 1 → Approach 1 (a backgroundable sub-agent primitive), equivalent in tier to start_agent / spawn_subagent; it MUST NOT be downgraded to a generic-terminal blocking poll. Cursor pollers whose loop runs > ~3 min MUST honour the sub-agent heartbeat contract (docs/subagent-heartbeat.md, #1166), same as the spawn_subagent path.
Phase 4 — Monitor
Polling Cadence
- ~ Check each agent's worktree every 2–3 minutes:
git status --short and git log --oneline -3
- ~ After 5 minutes with no changes, check if the agent process is still running
Heartbeat liveness check (#1365)
! On the Grok Build hybrid path (spawn_subagent dispatch, no native lifecycle channel back to the monitor), worktree git state alone is INSUFFICIENT to distinguish a healthy mid-poll sub-agent from a stalled one. Long-running review-cycle pollers spend most of their wall-clock waiting on Greptile and emit no commits during that wait -- the #1166 swarm session is the recurrence record (two of three dispatched pollers went silent with zero observable signals; the monitor could not tell).
! The canonical alive-check on the Grok Build hybrid path is the heartbeat contract documented in docs/subagent-heartbeat.md. Every long-running sub-agent (pollers, watchdogs, implementation agents whose tool loop exceeds ~3 min) writes a JSON heartbeat to .deft-scratch/subagent-status/<agent-id>.json per the canonical poller template + agent preamble; the monitor reads those records via task agent:monitor (three-state exit 0 ok / 1 stale-or-malformed / 2 config error). Default threshold is 30 minutes; --threshold-minutes overrides.
# Scan all worktrees in the cohort
task agent:monitor -- \
--scratch-dir <worktree-1>/.deft-scratch/subagent-status \
--scratch-dir <worktree-2>/.deft-scratch/subagent-status
! Run the heartbeat sweep alongside the worktree git checks at every monitor polling iteration (~2-3 min). When a record is reported STALE (mid-flight, terminal_state unpopulated, age > threshold), treat it as a candidate for the Takeover Triggers below; when it is reported MALFORMED, surface the diagnostics to the user and re-dispatch the agent with a fresh prompt that re-establishes the heartbeat contract. A TERMINAL record (terminal_state set) is NEVER stale -- the agent reached its exit on its own terms.
~ The heartbeat is filesystem-only by design; a network partition or rate-limit ceiling cannot mask agent liveness. Pair the on-disk sweep with the worktree git checks (git status --short, git log --oneline -3) and the per-PR readiness gate (task pr:merge-ready) for the full alive + progressing + clean picture.
⊗ Spawn a replacement sub-agent for a worktree where the heartbeat record reports OK or TERMINAL -- the agent is alive (or finished cleanly) and a replacement would re-trigger the Duplicate-Agent Failure Mode below.
⊗ Treat the absence of a .deft-scratch/subagent-status/<agent-id>.json record on the Grok Build hybrid path as "agent is alive but quiet" -- a sub-agent that never wrote a heartbeat is either pre-startup (acceptable for the first ~30s) OR violated the contract (treat as stalled and verify via worktree state before any replacement decision).
Checkpoints
Track each agent through these stages:
- Reading — agent is loading AGENTS.md, xBRIEF files, project files (no file changes yet)
- Implementing — working tree shows modified files
- Validating — agent running
task check
- Committed — new commit(s) in
git log
- Pushed — branch exists on
origin
- PR Created — PR visible via
gh pr list --head <branch>
- Review Cycling — additional commits after PR creation (Greptile fix rounds)
Takeover Triggers
! Pre-spawn verification: Before spawning a replacement agent, verify the original is truly unresponsive by waiting for an idle/blocked lifecycle event — verified via worktree state (git status, git log --oneline -3) and sub-agent lifecycle signals showing no in-flight work (for grok-build / spawn_subagent agents: polling is via worktree state + get_command_or_subagent_output rather than tab observation). Do NOT spawn a replacement based solely on message timing, absence of recent commits, or a perceived delay — original agents (Warp tabs or spawn_subagent processes) can resume after apparent failure, and spawning a new agent creates two concurrent agents on the same worktree (see Duplicate-Tab Failure Mode below).
! Take over an agent's workflow if ANY of these occur:
- Agent process has exited and PR has not been created
- Agent process has exited and Greptile review cycle was not started
- Agent is idle for >5 minutes after PR creation with no review activity
- Agent is stuck in an error loop (same error 3+ times)
When taking over: read the agent's current state (git log, diff, PR comments), complete remaining steps manually following the same deft process.
Duplicate-Agent Failure Mode (a.k.a. Duplicate-Tab Failure Mode)
⚠️ Root cause of #261 and #263 (generalized for #1342 slice 3): This is the Duplicate-Agent Failure Mode -- it fires on every platform descriptor, not just Warp tabs. Original Warp agent tabs may resume after apparent failure (network hiccup, temporary Warp UI freeze, context window pressure); the same failure mode applies to spawn_subagent-launched grok-build sub-agents that appear stalled but later resume. If the monitor spawns a new agent for the same worktree, two concurrent agents execute on the same branch simultaneously. This corrupts the tool_use/tool_result message chain — both agents issue tool calls, but responses are interleaved unpredictably, causing one or both agents to act on stale or incorrect state.
Recovery guidance:
- ! Keep original agents active until their PR is merged — do not terminate agent processes that appear stalled (for Warp tabs: keep the tab open; for grok-build / spawn_subagent agents: verify via
get_command_or_subagent_output before replacing)
- ! If an agent appears stalled, attempt to resume it in its original context (for Warp: go to the original Warp tab and say "continue from where you left off"; for grok-build: re-query via
get_command_or_subagent_output or send a resume message) rather than spawning a replacement
- ! If the original agent is truly unrecoverable (Warp crash, tab closed, or spawn_subagent process terminated), only then create a new agent — and first verify the worktree state (
git status, git log, gh pr list) to avoid conflicting with any in-flight work
Context-Length Warning
! Long monitoring sessions accumulate large conversation history (hundreds of tool_use/tool_result pairs) and are susceptible to conversation corruption — the tool_use/tool_result mismatch observed in #263 occurred at approximately message 158 in a single monitor conversation. To mitigate:
- ! Offload rebase, review-watch, and merge sub-tasks to ephemeral sub-agents using the tiered approach from
skills/deft-directive-review-cycle/SKILL.md (spawn via the platform adapter's dispatch primitive when available (e.g. spawn_subagent for Grok Build), discrete tool calls with yield otherwise) — this keeps the monitor conversation shallow
- ~ Target <100 tool-call round-trips in any single monitor conversation before considering a fresh session handoff
- ! If the monitor detects degraded output (repeated errors, inconsistent state references, tool call failures), stop and hand off to a fresh session with a state summary rather than continuing in a corrupted context
Phase 5 — Review & Complete
Verify Review Cycle Completion
For each agent's PR:
- ! Check that Greptile has reviewed the latest commit (compare "Last reviewed commit" SHA to branch HEAD)
- ! Verify Greptile confidence score > 3
- ! Verify no P0 or P1 issues remain (P2 are non-blocking style suggestions)
- ! Worker-owns-lifecycle fallback (#1880): Prefer workers scoped
drive-to: merge-ready so this step is rare. When a worker exits at PR-open without reaching merge-ready, the monitor MAY run skills/deft-directive-review-cycle/SKILL.md itself or dispatch ONE review-cycle owner — but MUST NOT split review polling and fix batches across separate leaf agents for the same PR (#727 + #1880 Gap C).
Complete xBRIEFs
! The cohort's story xBRIEFs are completed by the deterministic cohort completion sweep in Phase 6 (task swarm:complete-cohort, Phase 6 Step 1.5 below), which runs AFTER the merge cascade. Do NOT move story xBRIEFs out of xbrief/active/ before their PRs merge — a pre-merge move creates premature state if the merge cascade fails. This section is where the monitor records, per story, what the post-merge sweep will finalize:
- ! For each story xBRIEF an agent's PR fully resolves, note that it is ready to complete (
xbrief/active/ -> xbrief/completed/, status completed). The underlying primitive is task scope:complete <file>; the Phase 6 sweep wraps it across the whole cohort so nothing is missed on the headless / multi-worker path.
- ! If a story carries a
planRef to a parent epic, the sweep also completes that epic once ALL its children are settled — you do NOT reconcile epic parents by hand, and you do NOT manually repair parent/child references (the lifecycle helper keeps task xbrief:validate green via the #1485 / #1487 reference maintenance).
⚠️ Both the xBRIEF lifecycle moves AND origin/issue closure happen in Phase 6 (after merge), not here — completing xBRIEFs or closing issues before merge creates premature state if the merge cascade fails.
Exit Condition
All PRs meet ALL of:
- Greptile confidence > 3
- No P0 or P1 issues remain (P2 issues are non-blocking style suggestions)
task check passed (or equivalent validation completed)
- CHANGELOG entries present under
[Unreleased]
! Mandatory cohort verifier (#1364): After every poller (Phase 6 review-cycle sub-agent) reports back, the monitor MUST run task swarm:verify-review-clean -- <pr-numbers...> and confirm exit 0 BEFORE evaluating the rest of the Exit Condition or surfacing the Phase 5 -> 6 gate. The verifier re-uses the Greptile rolling-summary parser from task pr:merge-ready so the per-PR merge gate and the cohort gate stay in lockstep (a parser fix lands in both surfaces at once). Exit codes: 0 (cohort CLEAN -- all PRs simultaneously have SHA match + confidence > 3 + zero P0/P1 + not errored on current HEAD); 1 (one or more PRs unclean with per-PR diagnostics -- re-dispatch the poller for the unclean PR or address findings, then re-run the verifier); 2 (config error -- empty cohort, malformed xBRIEF glob, gh missing). The verifier is the structural answer to the #1166 swarm execution recurrence where multiple pollers exited with clean_gate_holdout=confidence (confidence == 3) and the monitor still raised the Phase 5 -> 6 gate because the trigger keyed on "all pollers have reported back" rather than "every PR in the cohort is objectively CLEAN".
! Deterministic PR-verdict wait (#1056): When a Phase 5 monitor needs to wait on Greptile/SLizard for an in-flight PR (cascade rebase + re-review, late Greptile pass), use task pr:watch -- <N> [--repo <owner>/<repo>] as the canonical wait-until-verdict helper. Blocking-by-default poll to a terminal three-state verdict — exit 0 CLEAN, 1 NEW_P0_P1, 2 ERRORED|STALL|TIMEOUT|config — with --one-shot for a single probe, --json for the structured shape, and --max-wait-minutes / --poll-seconds for the budget (defaults 30m / 90s). SHA-match gates the verdict to the current HEAD. For mergeable+merge cascade automation (not Greptile verdict alone), use task pr:wait-mergeable-and-merge (#1369); for adaptive merge-ready polling with layered via fallbacks, use task pr:merge-ready / task pr:monitor (#1368).
! Fallback-chain discriminator semantics (#1368): task pr:merge-ready -- <N> --json ALWAYS emits a via discriminator on every response. via="primary" and via="fallback1" are authoritative -- a merge_ready: true verdict on either is CLEAN. via="fallback2" is the coarse PR-view + check-run last-resort signal: it surfaces the PR's state / merged / mergeable / flattened check-run summary so a monitor can keep stepping forward through transient gh failures, but it is NEVER CLEAN -- the failure list carries the sentinel "fallback2 is a coarse signal, not a CLEAN verdict ..." and the merge cascade MUST keep waiting for a primary/fallback1 CLEAN. via="error" (every layer failed) is also non-CLEAN; the response carries error (one-line summary) + partial_data (per-layer diagnostics) so the monitor can step forward without blinding. Both task swarm:verify-review-clean and task pr:merge-ready treat fallback2 and error as merge-blocked.
⊗ Surface or discuss the Phase 5 -> 6 merge cascade gate while task swarm:verify-review-clean has not yet exited 0 on the current cohort (#1364). Keying the transition on poller lifecycle completion alone -- i.e. treating "every poller sub-agent returned a terminal message" as sufficient -- is the exact recurrence pattern this rule closes. The verifier is the only authoritative cohort-level CLEAN signal; a poller's clean_gate_holdout=confidence / clean_gate_holdout=has_blocking / clean_gate_holdout=sha_match / clean_gate_holdout=errored exit IS a non-CLEAN report and MUST hold the gate even if every sub-agent has technically returned.
⊗ Treat a via="fallback2" or via="error" response from task pr:merge-ready as CLEAN, regardless of the surrounding merge_ready field (#1368). Fallback2 is structurally never CLEAN -- the Greptile rolling-summary comment was unreachable on both the primary and fallback1 paths, so any merge taken on the basis of the coarse signal alone bypasses the SUCCESS-with-findings blind spot the per-PR gate was designed to close (#796 / #652). The merge cascade MUST keep waiting for a primary/fallback1 CLEAN.
Phase 5→6 Gate: Release Decision Checkpoint
! Before proceeding to Phase 6 (Close), the monitor MUST present the proposed release scope and version bump to the user for confirmation.
⊗ Context-pressure bypass prohibition: Even under long-context or time pressure (large conversation history, many tool calls, approaching context limits), this gate MUST NOT be bypassed. The Phase 5→6 gate is mandatory regardless of conversation length, elapsed time, or perceived urgency. If the monitor's context is degraded, hand off to a fresh session rather than skipping the gate.
-
! Present a summary containing:
- PRs ready to merge: list of PRs with titles, issue numbers, and current review status
- Proposed version bump: the tentative version from Phase 0 (patch/minor/major) with rationale — updated if scope changed during implementation
- Release scope: brief description of what this batch of changes represents
-
! Merge-readiness checklist: Before any gh pr merge call, the monitor MUST emit a structured checklist confirming each PR is merge-ready. For each PR, verify and explicitly confirm:
- Greptile confidence score > 3
- No P0 or P1 issues remaining
task check passed on the branch
- CHANGELOG.md entry present under
[Unreleased]
- Explicit user approval received for this merge cascade
! Cohort gate (#1364): Before the merge-readiness checklist is even emitted, the monitor MUST have already passed task swarm:verify-review-clean -- <pr-numbers...> per the Phase 5 Exit Condition above. The cohort gate is the structural pre-condition for this entire Phase 5 -> 6 sequence -- without exit 0 on the verifier, the checklist below MUST NOT be presented to the user. The per-merge task pr:merge-ready gate below remains the merge-time freshness-window-atomic check; the cohort verifier is the once-after-pollers gate that gates the discussion at all.
! Programmatic gate: Before each gh pr merge call, the monitor MUST run task pr:merge-ready -- <N> and abort the cascade on non-zero exit. The task parses the Greptile rolling-summary comment body (confidence, P0 / P1 badge counts, errored sentinel, HEAD-SHA freshness) -- not the GitHub CheckRun status. The CheckRun goes green when Greptile finishes its review pass, irrespective of findings; relying on it alone is the SUCCESS-with-findings blind spot that started the PR #652 incident merge cascade against Confidence: 3/5 + 1×P1 + 2×P2.
! Atomic gate (freshness window): The monitor MUST invoke task pr:merge-ready -- <N> and gh pr merge <N> in the same shell call (e.g. task pr:merge-ready -- <N> && gh pr merge <N> --squash --delete-branch --admin) so no time elapses between verdict and merge. A readiness check more than ~60 seconds stale is a Mode-1 false-positive risk: in the elapsed window an unrelated commit may land on master, auto-rebase trigger a fresh Greptile pass, and the new pass surface a P1 the cached verdict did not see. Re-invoking the gate is cheap (single gh api call); the shell-&& chain makes the freshness window structurally enforceable rather than prose-trust.
⊗ Merge on the basis of a SUCCESS Greptile CheckRun alone. The CheckRun signals review completion, not review approval. Parse the comment body (confidence + P0/P1 count) via task pr:merge-ready -- <N> before merging.
⊗ Run task pr:merge-ready -- <N> upstream of gh pr merge <N> (e.g. as a separate batched check during cascade prep, then later run gh pr merge after intervening rebase / sub-agent dispatch / user discussion). Stale verdicts risk Mode-1 false positives -- always chain readiness and merge in the same shell call.
-
! Wait for explicit user approval (yes, confirmed, approve) before proceeding to Phase 6 merge cascade
-
! If the user requests changes (e.g. different version bump, defer a PR), adjust and re-present
⊗ Begin merge cascade without presenting the version bump proposal and receiving explicit user approval.
Phase 6 — Close
Sub-Agent Role Separation (#727)
! Post-PR sub-agents are review-cycle agents (#727): Sub-agents addressing review findings, waiting for re-review, and iterating to clean MUST embody skills/deft-directive-review-cycle/SKILL.md end-to-end as a single coherent role. Do NOT split the review-cycle into separate "poll" and "fix" agents -- pollers that spawn separate fix agents create cross-agent state-handoff hazards and double the chance of an agent exiting at the wrong lifecycle boundary.
! Sub-agents MUST emit a heartbeat (#1365): every long-running review-cycle / poller sub-agent dispatched under Phase 6 MUST write a heartbeat record to .deft-scratch/subagent-status/<agent-id>.json per the contract in docs/subagent-heartbeat.md. The canonical poller template (templates/swarm-greptile-poller-prompt.md bounded poll loop) already encodes the per-iteration heartbeat write and the final terminal heartbeat, and the canonical orchestrator preamble (templates/agent-prompt-preamble.md § 10.5) restates the contract for any non-poller long-running sub-agent. The monitor watches via task agent:monitor -- see Phase 4 Heartbeat liveness check. Without the heartbeat, a spawn_subagent-dispatched poller that stalls is indistinguishable from a healthy mid-poll one (the #1166 recurrence).
! Post-PR monitoring runs in a fresh sub-agent (#727): Post-PR monitoring (Greptile, CI checks, downloadCount drift, lifecycle events, etc.) MUST be done by spawning a fresh short-lived sub-agent via the platform adapter's dispatch primitive for the detected runtime (e.g. spawn_subagent when the Grok Build / non-Warp platform is active, start_agent for Warp-orchestrated environments). The parent yields with no tool calls and waits for the sub-agent's messages -- this preserves conversation steerability so the user can interrupt or redirect while the watch is pending. The platform adapter (introduced in slices 1-3 of #1342) supplies the appropriate async callback channel and spawn surface per the runtime capability detection matrix; every Taskfile / shell-sleep / time.sleep / synchronous tool-call alternative blocks the parent's turn for the duration of the watch.
! Canonical poller template (#727): When delegating to a poller / review-cycle sub-agent, MUST use the canonical poller-prompt template at templates/swarm-greptile-poller-prompt.md with placeholders ({pr_number}, {repo}, {poll_interval_seconds}, {poll_cap_minutes}, {parent_agent_id}) filled in. Do NOT hand-author per-watch prompts -- the template encodes parsing fixes (markdown-link Last reviewed commit: regex, badge-based / negation-aware P0/P1 detection) that hand-authored variants have repeatedly missed (Agent D, post-#721 swarm; #727 comment 2).
! Destructive commands run alone (#727): Sub-agent prompts MUST instruct the agent to run destructive commands (rm, Remove-Item, del, git clean, etc.) in their OWN shell call, never chained with non-destructive commands. Chaining poisons Warp's is_risky classification on the entire pipeline and forces manual approval on every otherwise-safe operation -- a multi-commit branch hits the user N times per agent.
! Commit-message temp file is leave-alone (#727): When using the canonical PowerShell UTF-8-safe commit-message pattern (create_file <tmp> -> git commit -F <tmp>), MUST NOT clean up the temp file in the same shell call. Leave it orphaned -- worktree teardown or git clean -fd reclaims it. The two-step value (separate cleanup) is not worth the per-commit approval prompt the chained rm triggers.
⊗ Run a poll loop in the parent's own turn (via task, shell sleep, time.sleep, or any synchronous tool call). The conversation must remain user-steerable while watches are pending.
⊗ Bundle "watch for Greptile" / "monitor CI" instructions into an implementation agent's dispatch prompt (regardless of the platform adapter's spawn primitive) -- implementation agents exit at PR-open via the succeeded lifecycle, so any post-exit monitoring instruction is unreachable.
⊗ Spawn a "pure poller" sub-agent for a PR that has likely findings. Pure pollers are appropriate ONLY when no fixes are expected (CI watch on known-good HEAD, post-merge state checks, lifecycle observers). Default for post-PR work is review-cycle, NOT poller.
⊗ Chain rm (or any destructive command) with git commit / git push / any non-destructive command in a single shell pipeline.
Step 1: Merge
! Per-PR sub-agent identity gate: Before acting on any PR (merge, force-push, status check), query the specific sub-agent responsible for that PR for live status. Do not infer a PR's status from a different agent's tab, from message timing, or from the absence of recent commits. If the responsible agent is unreachable, verify PR state directly via gh pr view <number> and gh pr checks <number> before proceeding.
! Idempotent pre-check pattern: Before each action in the merge cascade, verify the current PR/branch state to ensure the action is still needed and safe to execute. Check: is this PR already merged (gh pr view <number> --json state --jq .state)? Is this branch already rebased onto the latest master? Has this issue already been closed? This makes recovery re-runs safe — a crash mid-cascade can resume from any point without duplicate actions or errors.
! Pre-merge protected-issue link inspection (Layer 3, #701): Before any gh pr merge call where a referenced issue MUST remain OPEN (umbrella, anchor, follow-up tracker), inspect GitHub's persistent linked-issue list:
gh pr view <N> --repo <owner/repo> --json closingIssuesReferences --jq '.closingIssuesReferences[].number'