How the orchestrator runs tasks end-to-end.
Full Development Cycle
Issue → Branch + Worktree → Agent works → Push → PR → Review Agent → Merge → Cleanup- Issue — created via
orch task addor a scheduled job (orch job tick) - Branch + Worktree — engine creates via
gh issue develop+git worktree add - Agent works — runs inside worktree, edits files, commits changes
- Push — engine pushes the branch after agent finishes
- PR — engine creates PR and links it to the issue
- Review — different agent reviews the PR (approve / request changes)
- Fix + Reply — fix review findings, commit fixes (engine pushes)
- Merge — engine merges PR after review approval and CI passes
- Release — CI auto-tags, generates changelog, creates GitHub release, updates Homebrew
- Cleanup — engine detects merged PR, removes worktree + local branch
Mention-Driven Tasks
When someone comments @orchestrator ... on a GitHub issue/PR, the GitHub mentions listener can create a task like:
Respond to @orchestrator mention in #<N>Expected outcome:
- Read the mention body + any referenced issues/PRs
- Reply back on the target issue with a concise status update and clear next steps
- Avoid including
@orchestratorin automated replies or agent summaries (useorchestratorwithout the@) to prevent mention-task feedback loops - If no code/docs changes are required, the task can be completed without opening a PR
Task Lifecycle
new → routed → in_progress → needs_review → in_review → done (merged)
→ done (no PR)
→ blocked- new: task created (via
orch task addor a scheduled job) - routed: LLM router assigned agent, model, profile, skills
- in_progress: agent is running
- needs_review: PR exists and is queued for review, or max attempts exceeded
- in_review: review agent is actively running on the PR
- done: PR merged (or agent completed with no code changes)
- blocked: waiting on child tasks to complete
Engine Tick
The Rust engine ticks every engine.tick_interval seconds (default 10s). Each tick runs sequential phases:
- Poll sessions — checks tmux for finished agent sessions, collects results
- Recover stuck tasks — detects
in_progresstasks with no tmux session and age >10 min, resets tonew - Route — assigns agent, model, and profile to
newtasks via LLM router - Dispatch — launches routed tasks in tmux sessions inside worktrees; breaks the loop early if work is already merged
- Unblock — if all children of a blocked parent are
done, resets parent tonew - Jobs — runs due scheduled jobs (cron, per-project)
A separate sync tick (~45s) handles less-frequent operations:
- Cleanup — removes worktrees for done tasks (runs in background), deletes orphaned remote branches, pulls main once per cycle
- Merged PR detection — marks tasks done when their PRs are merged
- Mention scanning — detects
@orchestratormentions in issue comments - PR review processing — re-routes tasks when reviews request changes (not child task creation), resets
ci_merge_failurescounter on re-route - Review agent — spawns review agent for
needs_reviewtasks - Stale review detection — resets
in_reviewtasks with no active tmux session (>5 min) - Owner commands — scans for
/slashcommands in issue comments - Skills sync — clones/pulls skill repositories
Channels & Live Sessions
The engine supports bidirectional channels (Telegram, Discord, Slack, GitHub, tmux). Incoming messages are routed to tmux sessions or turned into internal tasks. The capture service polls tmux panes and broadcasts diffs to all connected channel threads with per-channel rate limiting and message-splitting to satisfy platform limits.
Worktrees
The engine creates worktrees before launching agents. Agents do NOT create worktrees themselves.
Worktree path: ~/.orch/worktrees/<project>/<branch>/
Steps:
gh issue develop <issue> --base main --name <branch>— registers branch with GitHubgit branch <branch> main— creates branch from maingit worktree add ~/.orch/worktrees/<project>/<branch> <branch>— creates worktree- Agent runs inside the worktree directory (
PROJECT_DIRis set to worktree)
After agent finishes:
- Engine pushes the branch (
git push -u origin <branch>) if there are unpushed commits - Engine creates the PR and links it to the issue
- Engine checks CI status and triggers the review agent
- Agents should NOT push, create PRs, or call GitHub write APIs — the engine handles all git operations beyond committing
Agent Invocation
The Rust runner spawns the agent inside a tmux session:
claude -p \
--model <model> \
--permission-mode acceptEdits \
--allowedTools "Write" \
--disallowedTools "Bash(rm *)" \
--output-format json \
--append-system-prompt <system_prompt> \
<agent_message>Agent Output
{
"status": "done|in_progress|blocked|needs_review",
"summary": "what was done",
"reason": "why blocked/needs_review (empty if done)",
"accomplished": ["list of completed items"],
"remaining": ["list of remaining items"],
"blockers": ["list of blockers"],
"files_changed": ["list of modified files"],
"needs_help": false,
"delegations": [{"title": "...", "body": "...", "labels": [], "suggested_agent": "codex"}]
}Review Agent
After agent completion, if a PR is open and enable_review_agent is true:
- Status transitions to
needs_review, thenin_review(this transition is the atomic guard) - Opposite agent selected (codex wrote → claude reviews)
- PR diff fetched via
gh pr diff - Review agent evaluates and posts a comment with
## Automated Review — Approve/Changes Requestedheader - CI workflow reads the review comment to determine approval
See the Review Agent page for full details.
Stuck Task Recovery
The engine detects stuck tasks:
- No tmux session found — task
in_progresswith no live session and age >10 min → reset tonew - Max attempts exceeded — task goes to
needs_review(notblocked) and the forcedagent:*label is removed so an owner can reassign or inspect the task
Note: stuck_timeout is separate from the task execution timeout. Task execution is limited by workflow.timeout_seconds (or workflow.timeout_by_complexity), which controls how long an agent run is allowed to execute before being killed (exit 124 / TIMEOUT).
Max Attempts
Default: 10 attempts per task (configurable via config.yml). After max attempts, task goes to needs_review (not blocked) and the forced agent:* label is removed so an owner can reassign or inspect the task. Retry loop detection: if the same error repeats 3 times, task also goes to needs_review instead of retrying.