| name | issue-controller |
| description | Issue Agent OS controller loop. Pulls issues from a GitHub issue queue (priority + dependency resolved), fans out triage to parallel triager agents, dispatches coder / reviewer / splitter / planner / debugger workers based on verdicts, drives review loops, merges approved PRs, and repeats until the queue is empty. Use when the user asks to "process issues", "run the issue controller", "work through the queue", or similar. Do NOT use for single-issue ad-hoc work — this is the full automated loop.
|
| invocation_posture | manual |
| version | 0.1.0 |
Issue Controller
Automated controller loop for the Issue Agent OS. You (the main session) become the thin dispatcher. You never read issue content yourself — all judgment lives in the sub-agents you spawn.
Invocation
The user provides a repository and optionally a max_workers count:
- "Process the issue queue for owner/repo"
- "Run the issue controller for owner/repo, max_workers=3"
Default max_workers is 6. Default max_review_rounds is 3.
Prerequisites
Before starting the loop, ensure agent labels exist on the repo. Run once:
gh label create "agent:queued" --color "0e8a16" --description "Queued for Issue Agent OS" --repo <repo> 2>/dev/null || true
gh label create "agent:triaging" --color "1d76db" --description "Being triaged" --repo <repo> 2>/dev/null || true
gh label create "agent:in-progress" --color "fbca04" --description "Worker actively implementing" --repo <repo> 2>/dev/null || true
gh label create "agent:splitting" --color "5319e7" --description "Being decomposed" --repo <repo> 2>/dev/null || true
gh label create "agent:planning" --color "c5def5" --description "Design in progress" --repo <repo> 2>/dev/null || true
gh label create "agent:review" --color "fbca04" --description "PR waiting for merge" --repo <repo> 2>/dev/null || true
gh label create "agent:done" --color "0e8a16" --description "Completed" --repo <repo> 2>/dev/null || true
gh label create "agent:blocked" --color "d73a4a" --description "Blocked, needs intervention" --repo <repo> 2>/dev/null || true
gh label create "agent:deferred" --color "e4e669" --description "Deferred, needs clarification" --repo <repo> 2>/dev/null || true
gh label create "agent:rejected" --color "cfd3d7" --description "Rejected by triage" --repo <repo> 2>/dev/null || true
gh label create "agent:needs-human" --color "d876e3" --description "Escalated to human" --repo <repo> 2>/dev/null || true
Main Loop
loop:
TRIAGE PHASE → DISPATCH PHASE → COMPLETION PHASE → repeat
Triage Phase
- Count available slots:
max_workers - active_workers.
- If slots > 0, pull eligible issues:
node scripts/lib/issue-driven-os-queue.js next --repo <repo> --limit <slots>
- For each returned issue, label it
agent:triaging and spawn a triager agent in the background:
Agent(type="triager", prompt="Triage issue #<N> in repo <repo>", run_in_background=true)
On Codex: spawn_agent("triager", "Triage issue #<N> in repo <repo>")
- Spawn all triagers in parallel (one message, multiple Agent calls).
Dispatch Phase
As triager agents complete, read their verdict and dispatch:
- execute: Create worktree, label
agent:in-progress, spawn coder in background with the triager's brief and worktree path.
- split: Label
agent:splitting, spawn splitter in background.
- plan_then_execute: Label
agent:planning, spawn planner in background.
- investigate: Create worktree, label
agent:in-progress, spawn debugger in background with the triager's brief and worktree path.
- defer / reject / escalate: Triager already posted a GitHub comment and (for reject) closed the issue. Update labels. No worker needed. Slot stays free.
Completion Phase
As worker agents complete, handle their results:
- Coder done: The coder already created a Draft PR and pushed. Spawn reviewer in background with the PR number and issue brief. The reviewer posts its review directly on the PR via
gh pr review.
- Reviewer approved: Merge the PR and close the issue:
- Mark PR ready:
gh pr ready <number>
- Wait for CI to pass: poll
gh pr checks <number> --json name,state,conclusion until all checks show conclusion: "SUCCESS". Do NOT use --watch.
- Merge:
gh pr merge <number> --squash --delete-branch. If GitHub reports "still checking mergeable status", retry after 5 seconds.
- The issue auto-closes via "Closes #N" in the PR body.
- Label issue
agent:done, release lease, clean up worktree, free slot.
- Reviewer needs changes: The reviewer already posted REQUEST_CHANGES on the PR. If review rounds < max, re-spawn coder with the PR number (coder reads review comments, fixes, pushes). Otherwise, label
agent:blocked, post comment on both issue and PR, free slot.
- Splitter done: Label parent
agent:blocked (waiting for children). Children are already agent:queued. Free slot.
- Planner done: Create worktree, spawn coder with the plan as brief.
- Debugger done with fix: Spawn reviewer with the diff.
- Debugger done without fix: Post findings as comment on issue, label
agent:blocked, free slot.
Worktree Management
Create worktrees before spawning coders or debuggers:
node -e "
const ws = require('./scripts/lib/issue-driven-os-workspace');
const paths = require('./scripts/lib/issue-driven-os-state-store').buildRuntimePaths({repo: '<repo>'});
ws.createIssueWorktree({issueNumber: <N>, runtimePaths: paths}).then(r => console.log(JSON.stringify(r)));
"
Lease Management
Acquire leases before dispatching, release after completion or blocking:
node -e "
const ss = require('./scripts/lib/issue-driven-os-state-store');
const paths = ss.buildRuntimePaths({repo: '<repo>'});
ss.acquireIssueLease({issueNumber: <N>, runtimePaths: paths}).then(r => console.log(JSON.stringify(r)));
"
Label Transitions
Use gh issue edit <N> --add-label <label> --remove-label <old-label> for state transitions. Always remove the previous state label when adding the new one.
Observability
Every state transition must be visible on GitHub:
- Label changes on issues for all state transitions.
- When blocking an issue, always comment on the issue with the reason.
- When review loop is exhausted, comment on both the issue and the PR.
- The triager comments its verdict on the issue. The reviewer posts reviews on the PR. The coder creates the Draft PR. You tie it all together with labels and transitions.
What You Track (in your context)
For each active issue, one line:
#<N>: <state> | worker: <agent_type> | reviews: <count> | pr: #<M>
~100 tokens per issue. No issue bodies, no diffs, no code.
Core Rules
- NEVER read issue content. The triager does that.
- NEVER reason about code or architecture. Workers do that.
- NEVER write briefs. The triager does that.
- NEVER modify files. The coder does that.
- NEVER review code. The reviewer does that.
- Your ONLY job is: pull → triage → dispatch → handle completions → repeat.
- Respect max_workers — never have more active background agents than allowed.
- Never force-push, delete branches, or take destructive Git actions.
Error Handling
- Worker fails or times out → label issue
agent:blocked, comment with error detail, free slot.
- Queue empty → wait 30 seconds and check again. If still empty after 3 checks, report "Queue empty, stopping" and exit.
- All slots occupied → wait for any completion before pulling more issues.
- Lease acquisition fails → skip that issue (another controller has it).
Exit Conditions
Stop the loop when:
- Queue is empty for 3 consecutive checks (no more work).
- All issues are blocked/deferred/done (nothing actionable).
- User interrupts.
Report final status: how many issues processed, how many PRs merged, how many blocked.