auto-run
Autonomous dispatch-reconcile loop for batch task processing. Use with /auto-run --through <id> to execute tasks unattended. Requires Linear MCP or tasks to exist. Supports --resume for checkpoint recovery and --skip-milestone-review.
Autonomous Orchestrator Loop: $ARGUMENTS
You are an autonomous orchestrator. You dispatch workers, handle their completions, reconcile results, and dispatch newly unblocked tasks — repeating until all work is done or limits are reached.
Workers run in isolated git worktrees via the Agent tool. Each worker gets its own filesystem — no conflicts possible.
Section 1: Argument Parsing
Arguments: $ARGUMENTS
Parse the following flags:
--max-batches N— Stop after N dispatch rounds (default: unlimited)--max-hours H— Stop after H hours (default: unlimited)--max-concurrent N— Max parallel workers per batch (default: 3)--sequential— Execute tasks one at a time directly on the current branch (no worktrees, no PRs per task). Use for dependent/sequential tasks. Passes--sequentialto/dispatch.--dry-run— Show what would be dispatched without acting--resume— Resume from checkpoint (skip orient)--skip-milestone-review— Skip the milestone review phase after tasks complete--milestone-review-iterations N— Max milestone review iterations (default: 5)--through <task-id>— Complete everything needed to finish this task, then stop--epic <epic-id>— Complete all tasks within this epic, then stop--only <id1> <id2> ...— Only dispatch these specific tasks (and their blockers)
Section 2: Initial Setup
Check for Checkpoint
Read checkpoint at docs/auto-run-checkpoint.json (resolve from main repo root via git worktree list | head -1 | awk '{print $1}').
If --resume AND checkpoint exists with status "running":
- Read checkpoint state (including scope config)
- Log resumption context: batch number, completed list, in-progress list, scope
- Check if any previously in-progress tasks have since completed — look for session summaries:
ls docs/session_summaries/<task-id>*.txt 2>/dev/null - If summaries found → reconcile them first:
Call
/reconcile-summary --yesvia Skill tool - Skip orient, proceed to dispatch loop (Section 3)
If fresh start (no checkpoint or no --resume):
- Run
/orientvia Skill tool - Resolve scope (see Section 2.1)
- Write initial checkpoint
Write checkpoint JSON to docs/auto-run-checkpoint.json with:
version: 2status: "running"start_time: current ISO8601 timestampconfig: parsed flags (max_batches, max_hours, max_concurrent)scope: resolved scope (see 2.1)batch_number: 0tasks: { completed: [], failed: [], in_progress: [] }stats: { total_dispatched: 0, total_completed: 0, total_failed: 0, total_batches: 0 }
2.1 Resolve Scope
Determine which tasks are in-scope for this auto-run:
If --through <task-id>:
Walk the dependency graph backward from <task-id> to find all transitive blockers. Call get_issue(id=<task-id>, includeRelations=true) and recursively resolve each blockedBy entry's own blockers.
Build a set of all task IDs that must complete for <task-id> to be unblocked and completable. Include <task-id> itself. Store as scope.task_ids in checkpoint. Set scope.mode to "through" and scope.target to <task-id>.
If --epic <project-name>:
List all tasks in this project by calling list_issues(project=<project-name>).
Store all issue IDs as scope.task_ids. Set scope.mode to "epic" and scope.target to <project-name>.
If --only <id1> <id2> ...:
Use exactly those task IDs. Also resolve their transitive blockers (tasks that must complete first) and include those in scope. Store as scope.task_ids. Set scope.mode to "only".
If no scope flags:
scope.task_ids = null (all ready tasks are in scope). Set scope.mode to "all".
Log the resolved scope:
Auto-run scope: N tasks [list IDs]. Target: <task-id or epic-id or "all">
First Dispatch
- Query ready tasks: call
list_issues(state=Todo), then for each callget_issue(includeRelations=true)and filter to those with emptyblockedByarrays - Filter ready tasks to only those in
scope.task_ids(if set; if null, use all) - If no in-scope ready tasks:
- Check if target task is already closed → exit with completion report
- Otherwise report "No ready tasks in scope" and exit
- If ready tasks exist:
- Calculate count = min(ready_count, max_concurrent)
- If
--sequential: Call/dispatch --sequential <specific-task-ids> --no-plan --yesvia Skill tool. This dispatches tasks one at a time in foreground (no worktrees). The orchestrator blocks until each task completes. - If parallel (default): Call
/dispatch <specific-task-ids> --count <count> --no-plan --yesvia Skill tool, passing only in-scope task IDs
- Update checkpoint: add dispatched tasks to
in_progress, setbatch_number: 1
Section 3: Main Loop
If --sequential: The loop is synchronous. Each /dispatch --sequential call blocks until all tasks in that batch complete. After completion, proceed directly to Step B (Reconcile) → Step B.5 (Pull) → Step D (Dispatch Next). No background notifications needed.
If parallel (default): The loop is driven by background agent completion notifications. When a worker finishes (you are notified of its completion), process the result:
Step A — Identify Completion
When notified of a worker's completion, extract the task ID from the worker's name or result. Check for session summary:
ls docs/session_summaries/<task-id>*.txt 2>/dev/null
Step B — Reconcile
If summary exists:
Call /reconcile-summary <task-id> --yes via Skill tool.
After reconciliation, check if the completed task touched frontend files:
# Check the session summary for frontend file extensions
grep -E '\.(tsx|jsx|vue|svelte|html|css|scss)' docs/session_summaries/<task-id>*.txt 2>/dev/null
If frontend files were modified, note for milestone review:
Frontend changes detected in <task-id> — Playwright browser verification will run during milestone review.
If no summary (worker may have failed):
- Check the worker's return value for error information
- Mark as failed
- If Linear MCP is available, create investigation task with quality gate:
After creation, run post-write validation:save_issue( title="Investigate: <task-id> failed", team=<team>, priority=1, project=<project>, labels=["Bug"], description="## Problem\n<what the worker was attempting and how it failed>\n\n## Approach\nInvestigate root cause: check worker logs, git state, and error output.\n\n## Acceptance Criteria\n- [ ] Root cause identified\n- [ ] Fix applied or follow-up task created\n\n## Target Files\n- <files the failed task was working on>" )get_issue(id=<created-id>)— check for formatting artifacts and rewrite if needed.
Update checkpoint: move task from in_progress to completed (or failed).
Circuit breaker: If the same task ID appears in the checkpoint's failed list with attempts >= 2, skip it and log: "Task <id> failed twice — flagged for human attention."
Step B.5 — Pull and Clean Up
After reconciliation, pull changes so subsequent dispatches see the latest code:
If --sequential: No worktree prune needed (no worktrees created). Workers already committed directly to this branch, so git pull may not be necessary — but run it to sync with any remote changes:
git pull origin $(git branch --show-current) --rebase 2>/dev/null || true
If parallel (default): Prune completed worktrees and pull the worker's merged changes:
git worktree prune
git pull origin $(git branch --show-current)
Step C — Check Limits
- If
--max-batchesreached → write checkpoint withstatus: "paused", report "Auto-run paused. N tasks remain.", exit. - If
--max-hourselapsed (compare current time tostart_timein checkpoint) → same.
Step D — Dispatch Next Batch
- Query ready tasks (same pattern as First Dispatch: list_issues + get_issue filter)
- Filter to in-scope tasks only (if
scope.task_idsis set in checkpoint) - Calculate
available_slots = max_concurrent - current_in_progress_count
If in-scope ready tasks AND available_slots > 0:
- If
--sequential: Call/dispatch --sequential <specific-task-ids> --no-plan --yesvia Skill tool. This blocks until all tasks complete. - If parallel (default): Call
/dispatch <specific-task-ids> --no-plan --yesvia Skill tool (pass only in-scope IDs, limited to available_slots) - Increment
batch_numberin checkpoint - Add dispatched tasks to
in_progressin checkpoint
Completion checks:
- If
--throughtarget task is now closed → all done → go to Section 4 (Completion) - If
--epicand all epic children closed → all done → go to Section 4 - If no in-scope ready AND no in-progress → all done → go to Section 4
- If no in-scope ready BUT tasks still in-progress → wait for more completions (background agents will notify you when they finish; in
--sequentialmode this does not apply since dispatch blocks)
Step E — Context Self-Monitoring
After every 3 reconciliation cycles, assess context health. If you notice degradation (losing track of state, responses feeling truncated, difficulty recalling earlier context):
- Write checkpoint with
status: "running"(preserving all current state) - Log: "Context limit approaching. Exiting for wrapper restart."
- Exit gracefully — the wrapper script (if running) will restart with
--resume
Section 4: Completion
When no ready tasks AND no in-progress tasks (all work done):
- Final reconciliation: Call
/reconcile-summary --yesvia Skill tool
Step 1.5: Milestone Review Phase
After reconciliation, run an iterative review-fix pass on accumulated branch changes:
-
If
--skip-milestone-reviewwas passed: skip, log "Milestone review skipped by flag" -
Detect milestone branch:
git branch -r --list 'origin/milestone/*' | sort -V | tail -1 -
If no milestone branch found: skip, log "No milestone branch found — skipping milestone review"
-
Update checkpoint:
milestone_review.status: "in_progress" -
Dispatch a review worker using Agent tool:
isolation: "worktree"mode: "bypassPermissions"- Prompt: Check out the milestone branch, run
/milestone-review --max-iterations <N> --base-branch main(use--milestone-review-iterationsvalue or default 5) - The worker pushes fixes directly to the milestone branch (no separate PR — the milestone-to-main PR is the human review checkpoint)
-
Wait for worker completion
-
Read the worker's report/session summary
-
Update checkpoint:
milestone_review.status: "completed"with stats from the worker's report -
Write checkpoint with
status: "completed"and final stats -
Final report:
═══════════════════════════════════════════
AUTO-RUN COMPLETE
═══════════════════════════════════════════
Duration: <elapsed time>
Batches: <count>
Completed: <count> tasks
Failed: <count> tasks
MILESTONE REVIEW:
- Status: <completed|skipped|N/A>
- Iterations: <count>
- Findings fixed: <count>
- Findings deferred: <count> (needs human decision)
COMPLETED:
- <task-id>: <title>
- ...
FAILED:
- <task-id>: <title> — <reason>
- ...
REMAINING (if any):
- <task-id>: <title>
- ...
═══════════════════════════════════════════
- If Linear MCP is available, call
list_issuesto show final board state.
Checkpoint Schema
File: docs/auto-run-checkpoint.json
{
"version": 2,
"status": "running|completed|paused|errored",
"start_time": "ISO8601",
"last_updated": "ISO8601",
"config": {
"max_batches": null,
"max_hours": null,
"max_concurrent": 3,
"execution_mode": "parallel|sequential"
},
"scope": {
"mode": "all|through|epic|only",
"target": "INT-14",
"task_ids": ["INT-12", "INT-13", "INT-14"]
},
"batch_number": 2,
"session_count": 1,
"tasks": {
"completed": [{ "id": "INT-12", "title": "...", "completed_at": "...", "batch": 1 }],
"failed": [{ "id": "INT-14", "title": "...", "reason": "...", "attempts": 1 }],
"in_progress": [{ "id": "INT-13", "title": "...", "dispatched_at": "...", "batch": 2 }]
},
"stats": {
"total_dispatched": 4,
"total_completed": 1,
"total_failed": 0,
"total_batches": 2
},
"milestone_review": {
"status": "pending|in_progress|completed|skipped",
"iterations": 0,
"findings_fixed": 0,
"findings_deferred": 0
}
}
Error Handling
- Failed tasks: If Linear MCP available, create investigation issues; otherwise log and continue
- Circuit breaker: Same task failing twice → skip and flag for human attention
- No task tracker: If Linear MCP is not available, exit with "No task tracker configured. Connect Linear MCP or use /dispatch manually."
- Checkpoint corruption: If checkpoint can't be parsed, start fresh (warn user)