Workflow
How orch 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). An issue that is blocked by another issue stayswaitinguntil that pull request is merged into the default branch - 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: a different agent reviews the PR when one is available, the authoring agent reviews otherwise (approve / request changes)
- Fix + Reply — fix review findings, commit fixes (engine pushes)
- Merge — an approving review moves the task to
approved. The engine waits for CI and merges the PR with no agent running. A CI bootstrap task staysapprovedafter merge until its checks pass on the default branch and orch creates a ruleset that requires them. Failed or missing default-branch checks get a comment. Repeated failures route the task back or block it. If GitHub refuses the ruleset, orch comments and leaves the issue open so later tasks stay waiting until branch protection is on - Cleanup — engine detects merged PR, removes worktree + local branch. Blocked tasks with an open PR keep their remote branch. Stale worktree metadata is auto-pruned during sync.
Mention-Driven Tasks
When someone comments @orch ... (or legacy @orchestrator ...) on a GitHub issue/PR, the GitHub mentions listener can create a task like:
Respond to @orch 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
@orchin automated replies or agent summaries (useorchwithout 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 → approved → done (merged)
→ done (no PR)
→ blocked- new: task created (via
orch task addor a scheduled job) - routed: LLM router assigned agent, model, and profile
- in_progress: agent is running
- needs_review: PR exists and is queued for review
- in_review: review agent is actively running on the PR
- approved: the review approved the PR, orch waits for CI and merges, no agent runs
- done: PR merged (or agent completed with no code changes)
- blocked: needs a person (max attempts, max review cycles, an approved PR closed unmerged), or a parent waiting on child tasks
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 — resets
in_progresstasks with no tmux session afterengine.no_session_stuck_timeout(10 min) tonew, andin_reviewtasks with no review session afterengine.in_review_no_session_stuck_timeouttoneeds_review. This is the only periodic path that resets an orphaned review. Anapprovedtask is never reset - Route — assigns agent, model, and profile to
newtasks (round-robin by default, LLM router withrouter.mode: llm) - 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. With relay.url set, each GitHub App event from the relay starts a sync at once (see GitHub App):
- Cleanup — removes worktrees for done tasks (runs in background), deletes orphaned remote branches, auto-prunes stale worktree metadata, pulls main once per cycle; remote branch is kept when a blocked task has an open PR
- Merged PR detection — marks tasks done when their PRs are merged, and merges
approvedPRs once CI passes - Mention scanning — detects
@orch(and legacy@orchestrator) mentions in issue comments - PR review processing — re-routes tasks when reviews request changes (not child task creation), resets
ci_merge_failurescounter on re-route - Owner commands — scans for
/slashcommands in issue comments
The review agent does not wait for a tick. A subscriber starts it when a task enters needs_review, once CI has run on the PR head. Running checks hold the review, red CI routes the task back to its agent once per head SHA, and with no CI, or no started check after 15 minutes, the review starts at once. In a shipcheck repository with no started check, orch runs shipcheck run first and the review starts when it ends.
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 bypassPermissions \ # autonomous (default); acceptEdits in supervised mode
--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) - Reviewer selected via round-robin, excluding the task executor and all prior review agents for this task
- PR diff fetched via
gh pr diff - Review agent evaluates and posts a comment with
## Automated Review — Approve/Changes Requestedheader. The run is limited toworkflow.review_timeout_seconds(default 900) - orch sets the
orch/reviewgate on the PR head and, on approve, moves the task toapproved
See the Review Agent page for full details.
Stuck Task Recovery
The engine detects stuck tasks:
- Live session past its limit — see the note below
- No tmux session found — task
in_progresswith no live session and age >10 min → reset tonew - Review session gone — task
in_reviewwith no review session afterengine.in_review_no_session_stuck_timeout→ reset toneeds_review. orch keeps this limit above the review timeout plus 120 seconds, so a finished review still on its way is not dropped. On startup, everyin_reviewtask with no review session goes back toneeds_reviewat once - Max attempts exceeded — see Max Attempts
An in_progress task with a live session is reset after engine.stuck_timeout or after its run timeout plus 5 minutes, whichever is longer, counted from when it entered in_progress. The run timeout is workflow.timeout_seconds, raised by workflow.timeout_by_complexity for medium and complex tasks. orch kills the session before the reset, so the next dispatch can start.
Max Attempts
Default: 5 attempts per task (workflow.max_attempts). After max attempts, the task goes to blocked and the forced agent:* label is removed, so /retry routes it freely.