Files
ai-workflow-skill/docs/orch-cli.md
T
2026-03-19 14:02:33 +08:00

607 lines
15 KiB
Markdown

# Orch CLI
## Purpose
`orch` is the leader-facing scheduler and control plane. It owns the run, task graph, dependencies, ready queue, dispatch decisions, retries, and reassignment logic.
`orch` does not replace `inbox`. It uses `inbox` as the durable transport and execution record.
In normal operation:
- leaders use `orch`
- `orch` creates and monitors `inbox` threads
- workers continue using `inbox`
## Responsibilities
`orch` is responsible for:
- creating a run for one user request or project
- defining tasks and dependencies
- calculating which tasks are ready
- dispatching ready tasks to workers
- tracking attempts and mapping them to inbox threads
- allocating attempt worktrees for code tasks
- surfacing blocked tasks to the leader
- sending answers back into the active inbox thread
- reconciling thread state into task state
- blocking until actionable events arrive for the leader
- retrying, reassigning, cancelling, or adding follow-up tasks
## Non-Responsibilities
`orch` should not implement:
- worker claiming
- direct worker polling
- raw message append storage
- low-level thread history management
Those belong to `inbox`.
## Core Objects
- `run`: one coordinated execution for a user request
- `task`: one schedulable unit of work
- `dependency`: an edge between tasks
- `attempt`: one execution try for a task
- `dispatch`: the act of materializing a task into an inbox thread
- `workspace`: the branch and worktree assigned to one code-writing attempt
## Workspace Model
For code-writing tasks, `orch` should allocate one Git worktree per attempt.
Strict policy:
- dispatch from a concrete committed `base_ref`
- fail dispatch if strict mode is enabled and the leader is implicitly relying on uncommitted state
- create a fresh worktree for every retry
- do not let workers edit the user's primary checkout
See [worktree-execution.md](/home/kurihada/project/ai-workflow-skill/docs/worktree-execution.md) for the full execution model.
## Task State Model
- `planned`: task exists but is not yet eligible for dispatch
- `ready`: dependencies are satisfied and it can be dispatched
- `dispatched`: an inbox thread exists but the worker has not started yet
- `running`: the task has been claimed and is actively executing
- `blocked`: the active attempt needs clarification or an external dependency
- `done`: task completed and passed its current acceptance gate
- `failed`: task completed unsuccessfully
- `cancelled`: task was cancelled and should not continue
Suggested transitions:
- `planned -> ready`
- `ready -> dispatched`
- `dispatched -> running`
- `running -> blocked`
- `blocked -> running`
- `running -> done`
- `running -> failed`
- `failed -> ready` through explicit retry
- `* -> cancelled` by leader action
## Leader Workflow
The normal leader loop is:
1. create a run
2. add tasks
3. add dependencies
4. inspect `ready`
5. `dispatch` tasks
6. `reconcile` inbox state back into task state
7. inspect `blocked`
8. answer blocked questions
9. if nothing is actionable, call `wait`
10. retry or reassign failures when needed
11. finish when all required tasks are `done`
The leader should block on `orch wait`, not on ad hoc `sleep`.
## CLI Surface
The binary name is `orch`.
### Global Flags
- `--db PATH`
- `--json`
### `orch run init`
Create a new run.
Suggested flags:
- `--run RUN_ID`
- `--goal TEXT`
- `--summary TEXT`
Example:
```bash
orch run init --db .agents/coord.db --run blog_mvp_001 --goal "Build blog MVP" --summary "Public blog plus admin CRUD"
```
### `orch run show`
Show run metadata and current aggregate status.
Suggested flags:
- `--run RUN_ID`
### `orch task add`
Add a task to a run.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--title TEXT`
- `--summary TEXT`
- `--default-to AGENT`
- `--acceptance-json STRING`
- `--priority low|normal|high`
### `orch dep add`
Add a dependency edge.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--depends-on TASK_ID`
### `orch ready`
List tasks ready for dispatch.
Suggested flags:
- `--run RUN_ID`
- `--limit N`
### `orch dispatch`
Dispatch a ready task to a worker by creating an inbox thread and the first task message.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--to AGENT`
- `--repo-path PATH`
- `--base-ref REF`
- `--workspace-root PATH`
- `--strict-worktree`
- `--body TEXT`
- `--body-file PATH`
Behavior:
- creates a new attempt
- resolves the source repository from `--repo-path` or the current working directory
- resolves a committed base revision
- creates a branch and worktree for the attempt when the task writes code
- creates or links an `inbox` thread
- writes workspace metadata into attempt storage and task payload
- moves the task to `dispatched`
Strict-mode recommendation:
- if `--base-ref` is omitted and the repository is clean, default to `HEAD`
- if `--base-ref` is omitted and the repository is dirty, fail dispatch
- if `--base-ref` is provided, resolve it to a commit and use it exactly
- if `--workspace-root` is omitted in worktree mode, default to `.orch/worktrees` under the source repository
### `orch reconcile`
Read inbox state and update run/task state.
Suggested flags:
- `--run RUN_ID`
Behavior:
- maps inbox `claimed` or `in_progress` to `running`
- maps inbox `blocked` to `blocked`
- maps inbox `done` to `done`
- maps inbox `failed` to `failed`
### `orch blocked`
List blocked tasks and their latest question.
Suggested flags:
- `--run RUN_ID`
### `orch wait`
Block until one or more run-scoped events become available.
This is the normal wait primitive for the interactive leader.
Suggested flags:
- `--run RUN_ID`
- `--for task_ready,task_blocked,task_done,task_failed`
- `--after-event EVENT_ID`
- `--timeout-seconds N`
Behavior:
- blocks until a later matching event exists
- reconciles inbox state while polling so worker thread transitions can surface as `task_*` events
- returns a cursor for the next wait
- lets the leader wait for worker activity without manual sleep loops
### `orch answer`
Answer the active blocked question for a task by writing into the mapped inbox thread.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--body TEXT`
- `--body-file PATH`
- `--payload-json STRING`
### `orch retry`
Explicitly retry a failed task.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--to AGENT`
- `--body TEXT`
- `--body-file PATH`
Behavior:
- creates a new attempt
- links the retry to the prior failed attempt
- dispatches a new inbox thread or fresh task message
### `orch reassign`
Move a blocked or failed task to another worker.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--to AGENT`
- `--reason TEXT`
### `orch cancel`
Cancel a task or an entire run.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--reason TEXT`
### `orch cleanup`
Remove completed or abandoned attempt worktrees that are no longer needed.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
- `--attempt N`
- `--all-completed`
- `--force`
### `orch status`
Show task state summary for the run.
Suggested flags:
- `--run RUN_ID`
### `orch show`
Show one task with dependencies, attempts, and inbox mapping.
Suggested flags:
- `--run RUN_ID`
- `--task TASK_ID`
### `orch council start`
Start a three-reviewer council workflow for one target.
Suggested flags:
- `--run RUN_ID`
- `--target TEXT`
- `--target-file PATH`
- `--repo-path PATH`
- `--task-id TASK_ID`
- `--target-type text|repo|mixed`
- `--mode brainstorm|review`
- `--output markdown|json|both`
- `--only-unanimous`
Default behavior:
- fixed reviewer roles: `architecture-reviewer`, `implementation-reviewer`, `risk-reviewer`
- analysis only
- `--target-type mixed`
- `--output both`
- unanimous-only disabled unless requested
- reviewer count fixed at `3` in v1
### `orch council wait`
Block until the council has enough reviewer responses to continue.
Suggested flags:
- `--run RUN_ID`
- `--timeout-seconds N`
### `orch council tally`
Group similar reviewer suggestions and compute support counts.
Suggested flags:
- `--run RUN_ID`
- `--similarity strict|normal`
Behavior:
- groups semantically similar reviewer proposals
- assigns `consensus`, `majority`, or `minority`
- persists grouped recommendations in `orch` storage
Default behavior:
- `--similarity normal`
### `orch council report`
Render the final grouped council output.
Suggested flags:
- `--run RUN_ID`
- `--show consensus|majority|minority|all|consensus,majority`
Default behavior:
- show `consensus,majority`
- preserve `minority` in persisted storage even if omitted from the main report
- support both markdown artifacts and JSON output
## Relationship To Inbox
`orch` should be implemented as a control plane on top of `inbox`.
- `orch dispatch` writes the first `task` message into `inbox`
- `orch dispatch` also writes worktree metadata for code tasks into the attempt record and inbox payload
- workers claim and update status through `inbox`
- `orch reconcile` reads thread state and converts it into task state
- `orch answer` writes an inbox `answer` message to the active thread
The leader should not need to hand-write `inbox send` during normal dispatch.
Higher-level workflows such as council review should also run on top of `orch`, not as a separate infrastructure layer. See [council-review.md](/home/kurihada/project/ai-workflow-skill/docs/council-review.md).
## Waiting Model
The leader does not receive worker output as an in-memory push. Instead:
- workers write updates into `inbox`
- `inbox` appends events
- `orch reconcile` converts thread state into task state
- `orch wait` blocks on the run-scoped event stream
This is still a single leader model. `orch wait` is just the leader's blocking read primitive.
## JSON Contract
Every command should support `--json`.
Suggested success shape:
```json
{
"ok": true,
"command": "dispatch",
"run_id": "blog_mvp_001",
"task": {
"task_id": "T4",
"status": "dispatched",
"assigned_to": "backend-worker"
},
"attempt": {
"attempt_no": 1,
"thread_id": "thr_987",
"base_ref": "main",
"base_commit": "abc1234",
"branch_name": "orch/blog_mvp_001/T4/attempt-1",
"worktree_path": ".orch/worktrees/blog_mvp_001/T4/attempt-1"
}
}
```
Suggested `wait` wake shape:
```json
{
"ok": true,
"command": "wait",
"woke": true,
"next_event_id": 127,
"events": [
{
"event_id": 127,
"type": "task_blocked",
"run_id": "blog_mvp_001",
"task_id": "T5",
"thread_id": "thr_t5_attempt1",
"summary": "Editor choice undecided",
"payload": {
"question": "Should the admin editor use a rich text editor or plain textarea in MVP?"
}
}
]
}
```
## Exit Codes
- `0`: success
- `10`: no ready or matching tasks
- `20`: conflict
- `30`: invalid input or invalid state transition
- `40`: not found
- `50`: storage or internal error
## SQLite Schema Draft
These tables should live in the same SQLite file as the inbox tables.
```sql
CREATE TABLE IF NOT EXISTS runs (
run_id TEXT PRIMARY KEY,
goal TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE IF NOT EXISTS tasks (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
title TEXT NOT NULL,
summary TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL,
default_to TEXT,
priority TEXT NOT NULL DEFAULT 'normal',
acceptance_json TEXT NOT NULL DEFAULT '[]',
latest_attempt_no INTEGER,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (run_id, task_id),
FOREIGN KEY(run_id) REFERENCES runs(run_id)
);
CREATE TABLE IF NOT EXISTS task_dependencies (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
depends_on_task_id TEXT NOT NULL,
PRIMARY KEY (run_id, task_id, depends_on_task_id)
);
CREATE TABLE IF NOT EXISTS task_attempts (
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
attempt_no INTEGER NOT NULL,
assigned_to TEXT NOT NULL,
thread_id TEXT NOT NULL,
base_ref TEXT,
base_commit TEXT,
branch_name TEXT,
worktree_path TEXT,
workspace_status TEXT,
result_commit TEXT,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (run_id, task_id, attempt_no)
);
CREATE INDEX IF NOT EXISTS idx_tasks_run_status
ON tasks(run_id, status, priority, updated_at);
CREATE TABLE IF NOT EXISTS events (
event_id INTEGER PRIMARY KEY AUTOINCREMENT,
run_id TEXT NOT NULL,
task_id TEXT NOT NULL,
thread_id TEXT,
source TEXT NOT NULL,
event_type TEXT NOT NULL,
message_id TEXT,
summary TEXT NOT NULL DEFAULT '',
payload_json TEXT NOT NULL DEFAULT '{}',
created_at TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_events_run_event
ON events(run_id, event_id);
```
## Embedded Skill Draft
The following block is a draft `SKILL.md` for the leader-facing orchestration skill.
````markdown
```markdown
---
name: orchestrator
description: Use this skill when the leader needs to plan and schedule work through the orch CLI. It is for creating runs, adding tasks and dependencies, finding ready work, dispatching tasks to workers, allocating task worktrees, reconciling inbox state, waiting for worker events, reviewing blocked tasks, answering them, retrying failures, reassigning work, and cleaning up attempt worktrees. Do not use this skill for worker-side claim or progress updates; use inbox for that.
---
# Orchestrator
Use this skill when you are the leader and need to control the task graph through the `orch` CLI.
## When To Use
- you need to decompose a goal into tasks
- you need to record dependencies
- you need to know which tasks are ready
- you need to dispatch work to workers
- you need to allocate isolated worktrees for code-writing tasks
- you need to inspect blocked tasks and answer them
- you need to retry or reassign a failed task
## Rules
- Prefer `orch` over hand-written `inbox send` for normal leader operations.
- Reconcile inbox state before making new dispatch decisions.
- If nothing is actionable, use `orch wait` instead of manual sleep loops.
- For code tasks, dispatch from a committed base and allocate a fresh worktree per attempt.
- Keep tasks small enough to be checkable and to minimize clarification loops.
- Use `inbox` directly only for inspection or manual repair.
- Keep user-facing discussion in the leader.
## Typical Commands
```bash
orch run init --run blog_mvp_001 --goal "Build blog MVP" --summary "Public blog plus admin CRUD" --json
orch task add --run blog_mvp_001 --task T1 --title "Project skeleton" --summary "Initialize app structure and database wiring" --default-to foundation-worker --json
orch dep add --run blog_mvp_001 --task T2 --depends-on T1 --json
orch ready --run blog_mvp_001 --json
orch dispatch --run blog_mvp_001 --task T1 --to foundation-worker --base-ref main --workspace-root .orch/worktrees --strict-worktree --body-file tasks/t1.md --json
orch reconcile --run blog_mvp_001 --json
orch wait --run blog_mvp_001 --for task_blocked,task_done,task_failed --after-event 0 --timeout-seconds 900 --json
orch blocked --run blog_mvp_001 --json
orch answer --run blog_mvp_001 --task T2 --body "MVP supports draft and published only." --json
orch retry --run blog_mvp_001 --task T7a --to backend-worker --body "Retry after fixing the contract mismatch." --json
orch cleanup --run blog_mvp_001 --all-completed --json
```
```
````