Tasks are stored directly in GitHub Issues — no local database, no sync layer. The orchestrator reads and writes GitHub via the gh CLI.
Setup
- Install and authenticate:
gh auth login- Configure in
config.yml:
gh:
enabled: true
repo: "owner/repo"- Optionally set up a GitHub Project v2:
orch project info --fix # auto-fills project field/option IDs into configHow It Works
Task ID = Issue Number. When you run orch task add "Fix the login bug", it creates a GitHub issue and returns the issue number.
Field → GitHub Mapping
| Task Field | GitHub Storage |
|---|---|
| title | Issue title |
| body | Issue body |
| status | Label status:new, status:routed, etc. |
| agent | Label agent:claude, agent:codex, etc. |
| complexity | Label complexity:low/med/high |
| labels | Issue labels (non-prefixed) |
| parent_id | Sub-issue relationship |
| summary, response | Structured comment with <!-- orch:agent-response --> marker |
| branch, worktree | Local sidecar file ($STATE_DIR/tasks/{id}.json) |
Agent Response Comments
When an agent completes a task, the orchestrator posts a structured comment:
- Agent badges: 🟣 Claude, 🟢 Codex, 🔵 OpenCode
- Metadata table: status, agent, model, attempt, duration, tokens, prompt hash
- Sections: errors & blockers, accomplished, remaining, files changed
- Collapsed sections: stderr output and full prompt (with hash)
- Content-hash dedup: identical comments not re-posted
Labels
status:<status>— task status (new,routed,in_progress,done,needs_review,blocked)agent:<agent>— assigned agentcomplexity:<level>— routing complexityblocked(red) — applied when task is blocked, removed when unblockedscheduledandjob:{id}— for job-created tasks- When task is
done,auto_closecontrols whether to close the issue
Local Sidecar
Machine-specific fields (branch, worktree path, attempt count) are stored in $STATE_DIR/tasks/{id}.json. These are ephemeral and not synced.
Backoff
When GitHub rate limits or abuse detection triggers, the orchestrator sleeps and retries:
gh:
backoff:
mode: "wait" # "wait" (default) or "skip"
base_seconds: 30 # initial backoff duration
max_seconds: 900 # maximum backoff durationProjects (Optional)
Link tasks to a GitHub Project v2 board:
gh:
project_id: "PVT_..."
project_status_field_id: "PVTSSF_..."
# Optional: set these if your Project "Status" options aren't
# exactly: Backlog, In Progress, Review, Done
project_status_names:
backlog: "Todo"
in_progress: "Doing"
review: ["In Review", "Needs Review"]
done: "Done"
project_status_map:
backlog: "option-id-1"
in_progress: "option-id-2"
review: "option-id-3"
done: "option-id-4"Discover IDs automatically:
orch project info # show current project field/option IDs
orch project info --fix # auto-fill into configOwner Feedback
When the repo owner comments on a GitHub issue linked to a completed task, the orchestrator detects the feedback and re-activates the task:
- Tasks with status
done,in_review, orneeds_revieware checked for new owner comments - If new comments are found, the task is reset to
routed(keeping its agent assignment) - The feedback is appended to the task context so the agent sees it on re-run
Slash Commands
Owner comments can also start with a slash command on the first line (case-insensitive):
| Command | Action |
|---|---|
/retry | Reset task to status:new (clears agent + attempts) |
/assign <agent> | Set agent (claude, codex, opencode) and move to status:routed |
/unblock | Clear blocked/error state and reset to status:new |
/close | Mark status:done and close the issue |
/context <text> | Append text to task context and move to status:routed |
| `/priority <low | medium |
/help | Show supported commands |
Notes
- The repo is resolved from
config.ymlorgh repo view - Tasks with
no_ghorlocal-onlylabels are skipped - Agents never call GitHub directly; the orchestrator handles all API calls so it can back off safely