claude-do-it 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +137 -13
- package/bin/daemon.js +14 -2211
- package/bin/do-it.js +13 -1206
- package/package.json +2 -2
- package/skill/SKILL.md +327 -60
- package/web/assets/index-CYgjGBYX.js +169 -0
- package/web/assets/index-DwsKU-BX.css +2 -0
- package/web/index.html +23 -10
- package/web/assets/index-6QftMBqY.css +0 -2
- package/web/assets/index-Bey8I5te.js +0 -130
package/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: do-it
|
|
3
|
-
description: "Run a Markdown todo as the user's only interface to a team of agents. `/do-it [file]` opens the todo (default TODO.md, created when missing) in the browser; `/do-it <request>` first runs the request read only and writes its result into the todo as one task per item, which wait until the person writes under them. Then this session becomes the conductor: it watches the file, sizes every task line, plans it
|
|
3
|
+
description: "Run a Markdown todo as the user's only interface to a team of agents. `/do-it [file]` opens the todo (default TODO.md, created when missing) in the browser; `/do-it <request>` first runs the request read only and writes its result into the todo as one task per item, which wait until the person writes under them. Then this session becomes the conductor: it watches the file, sizes every task line, plans it on a model tier that fits its size, splits large tasks into subtasks the person reviews first, delivers it through worker agents in their own worktrees and one shared tester, and writes status, questions, and results back under each task. Works anywhere Claude Code runs: inside Herdr, Orca, or T3 Code each worker gets its own worktree and session in that app; in any other terminal the workers are Claude Code subagents in worktrees. Also the reference for reading and changing any do-it document with versioned `do-it` commands. Trigger on '/do-it', 'do-it', 'do it from my todo', 'work through TODO.md', 'conduct this todo'."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# do-it
|
|
@@ -9,6 +9,8 @@ Arguments: **$ARGUMENTS**. When the first line is empty, or names an existing fi
|
|
|
9
9
|
|
|
10
10
|
You are the **conductor**. The person only looks at the todo in their browser. Everything they want done arrives as lines in it, and everything they need to know (status, plans, questions, results) goes back into it, under the task it belongs to. Never ask them to relay anything between agents, and do not expect them to read this chat.
|
|
11
11
|
|
|
12
|
+
**You delegate and monitor; you do not do the work.** You size tasks, dispatch agents, watch the channels, check the facts a delivery claims, and set the small `@started` and `@done` labels. Everything else goes to an agent: searching or reading code, docs, and diffs (a read only `Explore` agent, keep only its conclusion), answering questions and research (Q tasks), planning (a planner), building (workers), testing (the tester), reviewing (a reviewer), and, once a messenger runs, writing the person's status lines, questions, and results (Step 4). Reading what your own agents hand back (a verdict, an answer file, an inbox line) is fine; reading the code yourself is not. Keep your context for the whole todo, not for one task.
|
|
13
|
+
|
|
12
14
|
## The do-it commands
|
|
13
15
|
|
|
14
16
|
Every open file is a CRDT document held by the do-it daemon, shared by the person's browser tab and every agent. Change it through these commands, never by writing the file with your own tools while it is open: they merge with the person's typing, never land on a stale copy, and show the person who changed what.
|
|
@@ -21,11 +23,12 @@ $D sections TODO.md
|
|
|
21
23
|
$D todo list TODO.md # "3. [x] Ship" per task
|
|
22
24
|
$D todo add TODO.md --version 7 --section tasks "Write tests"
|
|
23
25
|
$D todo start TODO.md --version 8 --section tasks -- "Write tests" # add the PlainTasks start time
|
|
24
|
-
$D todo check TODO.md --version 9 -- "Write tests" # by text; also uncheck, cancel, uncancel
|
|
26
|
+
$D todo check TODO.md --version 9 -- "Write tests" # by text or by the number from todo list; also uncheck, cancel, uncancel
|
|
25
27
|
$D append TODO.md --version 10 --section notes -- "- found: flaky CI"
|
|
26
28
|
$D edit TODO.md --version 10 --old "ship friday" --new "ship monday"
|
|
27
29
|
$D diff TODO.md --since 9 --wait 100 # waits for the next change, exit 4 when none came (keep it under the Bash tool's 120 s default)
|
|
28
30
|
$D watch TODO.md --todos # every change, as task lines
|
|
31
|
+
$D doctor # checks Bun, PATH, the daemon build, and this skill; one fix line per problem, exit 1 on any
|
|
29
32
|
```
|
|
30
33
|
|
|
31
34
|
- Every reply starts with the document's version; every change by anyone takes the next number. Always pass the version you last saw: the one in the reply to your last change, or `do-it read FILE --json | jq .version`. Two changes in a row to the same section need two versions (the second names the first one's reply).
|
|
@@ -34,8 +37,68 @@ $D watch TODO.md --todos # every change, as task lines
|
|
|
34
37
|
- Text starting with `-` goes after `--`. `-` alone reads standard input (multi line blocks).
|
|
35
38
|
- `edit --old` must match exactly one place; add context until it does.
|
|
36
39
|
- Name yourself: `--as NAME` or `export DO_IT_AGENT=NAME`. The person sees the name next to your changes, and `watch --as NAME` skips your own changes.
|
|
40
|
+
- The do-it Claude Code plugin puts `do-it` on the Bash tool's PATH, so the next bullet applies only without it. When a Monitor command reports `do-it` not found, spell out the `bunx claude-do-it` call there as the next bullet says.
|
|
37
41
|
- When `do-it` is not on PATH, define it as a shell function at the top of every Bash call (functions do not survive between calls, and `D="bun ..."` breaks in zsh because `$D` is not word split): `do-it() { bunx claude-do-it "$@"; }` (from a repo checkout: `do-it() { bun "<repo>/packages/cli/src/bin.ts" "$@"; }`). Monitor commands spell out the `bunx claude-do-it` (or `bun .../bin.ts`) call in full.
|
|
38
|
-
-
|
|
42
|
+
- When a do-it command fails in a way the replies above do not explain (exit 3, an unknown command), run `do-it doctor` once. Run its `do-it stop` and plain `do-it skill install` fixes yourself (`do-it stop` restarts the daemon for every agent, so tell the person to reload the editor tab). Show the person every other `fix:` line (`--force`, `ln`, `upgrade`, `add -g`, `npm install`, `export PATH`) and let them run it: those change their machine, and an `export` in your Bash call does not outlast it.
|
|
43
|
+
- Exit codes: 0 done; 1 refused, version mismatch, or a doctor problem; 2 invalid arguments; 3 daemon not running and could not start; 4 nothing yet; 9 the document was deleted.
|
|
44
|
+
|
|
45
|
+
## Tags
|
|
46
|
+
|
|
47
|
+
The editor understands PlainTasks `@` tags on task lines and colours them. There are two kinds:
|
|
48
|
+
|
|
49
|
+
| Tag | Who sets it | How |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `@started(26-11-05 14:02)` | the conductor | `do-it todo start`, the moment it takes a task (Step 3). The editor also completes `s` and `@s` to it; a stamp the person typed is replaced by `todo start` |
|
|
52
|
+
| `@done (26-11-05 15:40)` | the conductor, or the person when they check off `Review this task` in the editor | `do-it todo check`, once the task is delivered (Step 5) |
|
|
53
|
+
| `@cancelled (26-11-05 15:40)` | the person (`[-]` or Alt-click in the editor), or the conductor only when the person asks in words to cancel or drop the task | `do-it todo cancel` |
|
|
54
|
+
| `@today`, `@high`, `@low`, `@critical` | the person | typed (Tab after `t`, `h`, `l`, `c`) |
|
|
55
|
+
| `@due(date)`, `@created(date)` | the person | typed (Tab after `d`, `cr`) |
|
|
56
|
+
| `@project(Section)` | the editor's Archive | added when a resolved task moves under `## Archive` |
|
|
57
|
+
| any other `@word` | the person | typed; their own label |
|
|
58
|
+
|
|
59
|
+
- **Stamps come only from commands.** `@started`, `@done`, and `@cancelled` are written by `todo start`, `todo check`, and `todo cancel`, never typed with `edit`. `todo uncheck` and `todo uncancel` remove the resolved stamp and keep `@started`.
|
|
60
|
+
- **`@started` once per task.** Running `todo start` again replaces the time. A follow up on a delivered task the person unchecked keeps its first `@started`.
|
|
61
|
+
- **The person's tags are theirs.** No agent adds, removes, or edits `@today`, `@high`, `@low`, `@critical`, `@due`, `@created`, `@project`, or any other tag. Never put `@` words in your own status lines either: they render as tags.
|
|
62
|
+
- **Only the conductor sets stamps.** Workers, the tester, the messenger, and every other agent never touch tags (workers never edit the todo at all).
|
|
63
|
+
- **Tags steer dispatch.** When tasks wait for a worker, take `@critical` first, then `@high` and `@today`, then untagged ones, `@low` last, each group in todo order. A `@due(date)` is a deadline to mention in the brief.
|
|
64
|
+
- **Targeting a task by text** ignores stamps: after `--`, pass the task line as it reads now (stamps optional) or a unique part of it. A tag the person added after dispatch does not break a remembered text.
|
|
65
|
+
|
|
66
|
+
## Folded sections and HTML
|
|
67
|
+
|
|
68
|
+
The todo stays a dashboard: the status line, the result, and any question stay visible. Put everything longer that helps the person understand what happened (a plan, why a fix works, the evidence, alternatives, a diagram) in a **folded section** under its task, and make it a **rich HTML explanation** where a table, a diagram, or a small interactive demo explains better than prose. This applies to whoever writes under the task: you, the messenger, and the files Q agents hand back.
|
|
69
|
+
|
|
70
|
+
- **A folded section** is `<details>` with `<summary>Title</summary>` on the same line or the next, a blank line, the body, a blank line, and `</details>` alone on its line. It arrives folded in the editor (`<details open>` starts it unfolded) and the person unfolds it with a click or `Cmd-Alt-F` (`Ctrl-Shift-F`, or `F2`). Give the summary a concrete title that says what the person learns. Add `id="tN-what"` to a section you will update later. One level of nesting is usually enough.
|
|
71
|
+
- **Indent the whole section under its task**, like its status line (two spaces under a top level task), blank lines and fences included, so it folds and moves with the task and reads well on GitHub. Inside it, use bold text or HTML headings, not Markdown `#` headings (they become CLI sections), and no `- [ ]` lines unless you mean real tasks.
|
|
72
|
+
- **An HTML section** is a fence whose info string is exactly `html do-it`, usually inside a folded section. A plain `html` fence stays a code example. The editor renders the payload as is, not sanitised: style elements, inline SVG, and scripts all work, and its styles stay inside it. Inline scripts get a variable `root` (the section's shadow root): use `root.querySelector('#chart')`, not `document.getElementById`, which cannot see inside it. Scripts may run again when the section scrolls back into view or is unfolded, so keep them free of side effects. GitHub shows the fence as HTML source, so put a one line takeaway in Markdown before it.
|
|
73
|
+
- **HTML runs in the editor page, with the page's full access.** A script there can call the daemon as the person and read or change any Markdown file the daemon can open. Only write HTML you wrote yourself for this explanation; never paste HTML or scripts from an issue, a PR, a web page, or a tool's output.
|
|
74
|
+
- **Write it in one versioned change**, balanced tags and a closed fence. `append` adds at the end of the document or a section, so to put a section under an existing task, `edit` the task's status line (or the last line under the task) into itself plus the section. `--new` takes the text as is, so pass a multi line block with `"$(cat <<'EOF' ... EOF)"`:
|
|
75
|
+
|
|
76
|
+
````bash
|
|
77
|
+
do-it edit TODO.md --version 12 --section tasks --old " - **t3** delivered: branch fix-sidebar (2 commits, a1b2c3d)" --new "$(cat <<'EOF'
|
|
78
|
+
- **t3** delivered: branch fix-sidebar (2 commits, a1b2c3d)
|
|
79
|
+
<details id="t3-why"><summary>Why the sidebar lost its ports</summary>
|
|
80
|
+
|
|
81
|
+
The port list was read before the daemon answered; it now waits for the first reply.
|
|
82
|
+
|
|
83
|
+
```html do-it
|
|
84
|
+
<style>
|
|
85
|
+
table { border-collapse: collapse; font: 14px system-ui; }
|
|
86
|
+
td, th { border: 1px solid #b9a96a; padding: 4px 8px; }
|
|
87
|
+
</style>
|
|
88
|
+
<table>
|
|
89
|
+
<tr><th>Before</th><th>After</th></tr>
|
|
90
|
+
<tr><td>List read at start, often empty</td><td>List read after the first reply</td></tr>
|
|
91
|
+
</table>
|
|
92
|
+
<button id="replay">Replay the race</button> <span id="out"></span>
|
|
93
|
+
<script>
|
|
94
|
+
root.querySelector('#replay').onclick = () => { root.querySelector('#out').textContent = 'reply first, then the list' }
|
|
95
|
+
</script>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
</details>
|
|
99
|
+
EOF
|
|
100
|
+
)"
|
|
101
|
+
````
|
|
39
102
|
|
|
40
103
|
## Step 1. Open the todo
|
|
41
104
|
|
|
@@ -45,26 +108,75 @@ export DO_IT_AGENT=conductor
|
|
|
45
108
|
ARG='<the todo file from the arguments, empty for a seed request>'
|
|
46
109
|
TODO=$(cd "$(dirname "${ARG:-TODO.md}")" && pwd)/$(basename "${ARG:-TODO.md}")
|
|
47
110
|
SLUG=$(printf '%s' "$TODO" | shasum | cut -c1-10)
|
|
48
|
-
STATE=${DO_IT_HOME:-$HOME/.do-it}/runs/$SLUG # inbox.md, tests.md, plans/, logs/, state.json
|
|
49
|
-
mkdir -p "$STATE/plans" "$STATE/logs"
|
|
111
|
+
STATE=${DO_IT_HOME:-$HOME/.do-it}/runs/$SLUG # inbox.md, tests.md, plans/, answers/, reviews/, logs/, state.json, models.json
|
|
112
|
+
mkdir -p "$STATE/plans" "$STATE/answers" "$STATE/reviews" "$STATE/logs"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Find out where this session runs, once. It decides how workers start (Step 4) and needs no app to be installed:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
detect_harness() {
|
|
119
|
+
if [ "${HERDR_ENV:-}" = 1 ] && command -v herdr >/dev/null; then echo herdr; return; fi
|
|
120
|
+
if [ -n "${ORCA_TERMINAL_HANDLE:-}${ORCA_WORKTREE_ID:-}" ] && command -v orca >/dev/null; then echo orca; return; fi
|
|
121
|
+
if command -v t3 >/dev/null; then
|
|
122
|
+
if [ -n "${T3CODE_PROJECT_ROOT:-}${T3CODE_WORKTREE_PATH:-}" ]; then echo t3code; return; fi
|
|
123
|
+
pid=$PPID # T3 Code does not mark its agent sessions yet, so look for it among the parent processes
|
|
124
|
+
for _ in 1 2 3 4 5 6 7 8; do
|
|
125
|
+
case "$pid" in ''|0|1) break ;; esac
|
|
126
|
+
case "$(ps -o comm= -p "$pid" | tr '[:upper:]' '[:lower:]')" in *t3code*|*"t3 code"*) echo t3code; return ;; esac
|
|
127
|
+
pid=$(ps -o ppid= -p "$pid" | tr -d ' ')
|
|
128
|
+
done
|
|
129
|
+
fi
|
|
130
|
+
echo plain
|
|
131
|
+
}
|
|
132
|
+
detect_harness > "$STATE/harness"; cat "$STATE/harness" # later Bash calls: HARNESS=$(cat "$STATE/harness")
|
|
50
133
|
```
|
|
51
134
|
|
|
52
|
-
|
|
135
|
+
`herdr`, `orca`, and `t3code` start workers as sessions in that app; `plain` (any other terminal) uses Claude Code subagents, which need nothing beyond Claude Code.
|
|
136
|
+
|
|
137
|
+
Find out once which models you can reach beyond Claude Code's own `opus` and `sonnet`. Only L tasks use them (planning, review, and the hard subtasks). Look in two places: the Agent tool's list of agent types and their descriptions, for an agent type whose description names a model stronger than Opus; and PATH, for a CLI that runs another frontier model non interactively. Run `<cli> --help` once for any CLI you find, and store the full non interactive command (read only for planning and review), so later steps and the worker brief's HARD PARTS can paste it. Write what you found to `$STATE/models.json`, for example `{"planner": "agent <type>", "reviewer": null, "hard_parts": "cli <command>"}`, with `null` for anything you did not find. Use only what is there; never assume an agent type or CLI exists. Everything not found falls back to Opus.
|
|
138
|
+
|
|
139
|
+
Open it for the person. It starts or reuses the daemon, creates the file from a template when missing, opens the browser, and returns at once. Inside Sheprd (Herdr GPUI) the todo opens in a split beside this conversation, so the person sees both:
|
|
53
140
|
|
|
54
141
|
```bash
|
|
55
|
-
|
|
56
|
-
|
|
142
|
+
SHEPRD=
|
|
143
|
+
for app in /Applications/Sheprd.app/Contents/MacOS/sheprd /Applications/Herdr.app/Contents/MacOS/Herdr; do
|
|
144
|
+
[ -x "$app" ] && { SHEPRD=$app; break; }
|
|
145
|
+
done
|
|
146
|
+
if [ "${HERDR_ENV:-}" = 1 ] && [ -n "$SHEPRD" ]; then
|
|
147
|
+
DO_IT_BROWSER="$SHEPRD browser open --beside" do-it open "$TODO" # a split next to this pane
|
|
57
148
|
else
|
|
58
149
|
do-it open "$TODO"
|
|
59
150
|
fi
|
|
60
151
|
do-it read "$TODO"
|
|
61
152
|
```
|
|
62
153
|
|
|
63
|
-
|
|
154
|
+
Opening the todo again from the same pane reuses and reloads that split.
|
|
155
|
+
|
|
156
|
+
Tell the person in one line that the todo is open and that they can add tasks there. Create the two agent channels with your Write tool if missing (nobody has them open yet): `$STATE/inbox.md` (`# Inbox`, `## Messages`) and `$STATE/tests.md` (`# Tests`, `## Queue`). If `$STATE/state.json` exists, this is a resume: do Step 1a, which ends with the Step 2 watches armed, then go on with Step 5 events.
|
|
157
|
+
|
|
158
|
+
When `DO_IT_HOME` or `DO_IT_PORT` is set in your environment, every agent you start must use the same daemon. Add to every worker, tester, messenger, and Q brief the line `ENV: run export DO_IT_HOME=<value> DO_IT_PORT=<value> in the same Bash call before every do-it command` (shell variables do not survive between Bash calls), and pass both as `--env KEY=VALUE` to `herdr tab create`. A worker that misses them talks to another daemon about the same files.
|
|
159
|
+
|
|
160
|
+
## Step 1a. Resume
|
|
161
|
+
|
|
162
|
+
A conductor that stopped (a crash, a closed tab, a new session) leaves its workers, tester, and messenger running, and events kept arriving while nobody watched. Resuming is catching up, then conducting as usual: it is never done by one pass.
|
|
163
|
+
|
|
164
|
+
1. **Make sure no conductor still runs.** Only one conductor runs per todo. List the watches with `pgrep -fl "do-it watch"` and keep those naming this run's todo or `$STATE` with `--as conductor`. For each, walk up its parents (`ps -o ppid= -p PID`, then `ps -o comm= -p PPID`): when one is a live `claude` process other than your own session, another conductor still runs, so do not resume: tell the person in chat and stop. Otherwise these watches are orphans of the stopped conductor: kill them. Never touch the tester's or messenger's watches, or another run's.
|
|
165
|
+
2. **Find what still runs.** Match each task by the worktree path and branch saved in `state.json`, and use the agent name only as a cross check (another run in the same repo can have its own `do-tN`). Herdr: `herdr agent list` (cwd and name), `herdr worktree list --json`; Orca: `orca worktree list --json`, `orca terminal list --json`; T3 Code: `t3 thread list`; plain: `git worktree list` (subagents do not survive their conductor, so every plain worker is gone). Reuse the tester and messenger saved in `state.json` while they run; start new ones only when they are gone.
|
|
166
|
+
3. **Read what you missed.** `do-it read` the todo, the inbox, and `tests.md` whole (not a watch: a watch only shows what comes next). Compare with `state.json`: inbox lines after `versions.inbox` (all of them when it is missing), answers in `tests.md`, and the person's edits in the todo since `versions.todo` (new tasks, notes under tasks, answers to questions, a checked `Review this task`).
|
|
167
|
+
4. **Act on each task**, by its `state.json` status and what you found:
|
|
168
|
+
- `delivered`: nothing. Never re-dispatch it, re-check it, or rewrite its status line.
|
|
169
|
+
- a worker whose `done` line is in the inbox but who is not `delivered`: verify and deliver it now (Step 5).
|
|
170
|
+
- a live worker without `done`: keep it, send it any note the person wrote while you were away (`From the TODO: ...`), and never start a second worker on that task.
|
|
171
|
+
- a worker that is gone without `done`: its worktree and branch still hold its work, so start a new worker in that same worktree with its brief plus `RESUMED: continue from the commits on your branch`, and never create a second worktree for it (when the saved worktree is missing or was never saved, dispatch it fresh, Step 4). Herdr: `herdr worktree open --no-focus`, then focus your own workspace again, then start `do-tN` in its root pane; Orca: a new terminal in the saved worktree id; T3 Code: `t3 thread send` to the saved thread; plain: `Agent` without `isolation`, with `CWD: <saved worktree>` in the brief and `cd` there in every Bash call.
|
|
172
|
+
- `queued`: dispatch it in tag order as workers free up, with any note the person wrote under it in the brief.
|
|
173
|
+
- a task line with `@started` and a `**tN**` status line but no `state.json` entry: look for its worker before treating it as new: a worktree whose branch or label names the task, or an agent whose brief or cwd points at this `$STATE`; when nothing matches for sure, ask the person in chat before dispatching it; with `@started` and no status line, triage it (Step 3) and keep the stamp.
|
|
174
|
+
- any other status (`review`, `listed`, `waiting for you`, `stopped`): apply the Step 5 row for what changed in the todo since `versions.todo`, or nothing when nothing did.
|
|
175
|
+
5. **Arm the watches** that apply (Step 2) with `--since` the versions you just read, save those versions in `state.json`, and keep conducting. Never end the turn after the catch up without the watches running.
|
|
64
176
|
|
|
65
177
|
## Step 1b. Seed from a request
|
|
66
178
|
|
|
67
|
-
Only when the arguments were a seed request.
|
|
179
|
+
Only when the arguments were a seed request. Hand the request to one `general-purpose` agent with a read only brief (it may write only its result file): it fetches, searches, and lists, changes nothing, messages nobody, writes the finished block below to `$STATE/seed.md`, and returns only `SEED: <path>`. Start no worker. Then append that file into `## Tasks` yourself (`do-it append "$TODO" --version $V --section tasks - < "$STATE/seed.md"`; the messenger does not exist yet), as one task per item, so the person can act on each one:
|
|
68
180
|
|
|
69
181
|
```markdown
|
|
70
182
|
- [x] <the request, as they wrote it>
|
|
@@ -86,53 +198,76 @@ Only when the arguments were a seed request. Do the request yourself, read only
|
|
|
86
198
|
Start each watch with the Monitor tool; every printed line is an event:
|
|
87
199
|
|
|
88
200
|
```bash
|
|
89
|
-
do-it watch "$TODO" --as conductor --settle 25 # one line
|
|
201
|
+
do-it watch "$TODO" --as conductor --settle 25 # one line 25 s after the person's last change, 2 s after Enter adds a line
|
|
90
202
|
do-it watch "$STATE/inbox.md" --as conductor --section messages
|
|
91
|
-
do-it watch "$STATE/tests.md" --as conductor --todos #
|
|
203
|
+
do-it watch "$STATE/tests.md" --as conductor --todos # plain and t3code: when to start or wake the tester
|
|
92
204
|
```
|
|
93
205
|
|
|
94
|
-
|
|
206
|
+
Run every watch that applies (the `tests.md` one only in plain and t3code) for the whole run, and never end a turn without them: an event that lands while a watch is down is seen only by the next full read. A Monitor expires after its `timeout_ms` (at most 30 minutes): set the maximum, and on expiry re-arm the same watch with `--since` the last version you read. Stop a watch (TaskStop) before you start its replacement, so one file never has two conductor watches. Keep the watch unfiltered or filter for every event you act on (`done`, `question`, `blocked`, `failed`, `progress`, `subtask`), not only for one task.
|
|
207
|
+
|
|
208
|
+
Use `--quiet` to skip the initial `Watching PATH at version N` line instead of filtering it with grep. A filter must be line buffered, or lines arrive late. `Settled at version N by the user` means the person changed the todo and paused, or pressed Enter for a new line (`--settle-line`, 2 s by default; edits inside existing lines wait the full 25 s). Then `do-it read "$TODO"` once and compare it with your picture of the file. Never act on a half typed line. `Missed changes, now at version N: run do-it read` means the watch history was forgotten, so read the current todo before acting. `Daemon restarted, now version N` needs nothing but a fresh read. When re-arming a watch, pass `--since` the last version you have read, not the original one. Exit 9 means the todo was deleted or moved: stop and report in chat.
|
|
95
209
|
|
|
96
210
|
## Step 3. Triage each new task
|
|
97
211
|
|
|
98
|
-
A task is any `- [ ]` line (or plain non empty line under `## Tasks`) that has no `**tN**` status line under it and is not a `listed` item from Step 1b. A `- **Name**` bullet without a checkbox is a group label, never a task. An empty `- [ ]` line is the person's typing spot (the new file template has one): leave it alone.
|
|
212
|
+
A task is any `- [ ]` line (or plain non empty line under `## Tasks`) that has no `**tN**` status line under it, is not already in `state.json`, is not an indented `- [ ]` line under a task whose `state.json` entry is L (those are its subtasks, the person's own additions included), and is not a `listed` item from Step 1b. A `- **Name**` bullet without a checkbox is a group label, never a task. An empty `- [ ]` line is the person's typing spot (the new file template has one): leave it alone. The template's `## Example` samples fall out of these rules (open ones carry a `**t0**` status line, the rest are resolved); real ids start at t1.
|
|
213
|
+
|
|
214
|
+
**Start it first.** The moment you take a task, before sizing it, run `todo start` on it, queued tasks included, so the person sees at once that it was picked up:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
do-it todo start "$TODO" --version $V --section tasks -- "<the task text>"
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Then give it the next id `tN` and size it from its text. Do not read the code it names yourself: when the text alone cannot size it, ask one read only `Explore` agent and keep only its conclusion.
|
|
99
221
|
|
|
100
222
|
| Size | Looks like | Planner | Worker |
|
|
101
223
|
| --- | --- | --- | --- |
|
|
102
|
-
| S | one file or setting, obvious change, no design choice |
|
|
103
|
-
| M | a feature or fix in one area, a few files, needs tests |
|
|
104
|
-
| L | crosses packages or services, protocol or data migration, concurrency, security, unclear root cause, or the person calls it big |
|
|
105
|
-
| Q | a question, research, or a decision, no code | none |
|
|
224
|
+
| S | one file or setting, obvious change, no design choice | Opus, short plan | one Sonnet worker |
|
|
225
|
+
| M | a feature or fix in one area, a few files, needs tests | Opus | one Opus worker |
|
|
226
|
+
| L | crosses packages or services, protocol or data migration, concurrency, security, unclear root cause, or the person calls it big | the stronger planner from `models.json`, else Opus; its plan ends in subtasks (below) | one Opus worker, with the stronger model from `models.json` for the hard subtasks when there is one |
|
|
227
|
+
| Q | a question, research, or a decision, no code | none | one `general-purpose` agent in the background with a read only brief (it may write only its answer file) that writes its answer to `$STATE/answers/tN.md` and returns only `ANSWER: <path>`; never answer it yourself. The answer file goes under the task (Step 5) |
|
|
228
|
+
|
|
229
|
+
Reviews (Step 5) use Opus; an L review uses the stronger reviewer from `models.json` when there is one. The tester and the messenger run on Sonnet.
|
|
106
230
|
|
|
107
231
|
Repo: the one containing the todo, unless the task names another. If you cannot tell, ask under the task instead of guessing.
|
|
108
232
|
|
|
109
|
-
|
|
233
|
+
Then write the status line, and dispatch while fewer than 3 workers are active (otherwise status `queued`, dispatched in the order of the Tags section as workers finish). An L task is planned at once but dispatched (or queued) only after its review (below). Without a messenger you write it:
|
|
110
234
|
|
|
111
235
|
```bash
|
|
112
236
|
do-it edit "$TODO" --version $V --section tasks \
|
|
113
237
|
--old "<the exact task line>" \
|
|
114
238
|
--new "<the exact task line>
|
|
115
|
-
- **t3** M, planning with
|
|
239
|
+
- **t3** M, planning with Opus"
|
|
116
240
|
```
|
|
117
241
|
|
|
118
|
-
|
|
242
|
+
With a messenger (Step 4) you only append `- conductor t3 status: M, planning with Opus` to the inbox and it writes the line. Everywhere below, **write under the task** means exactly this: yourself without a messenger, through a `conductor` inbox line with one.
|
|
119
243
|
|
|
120
|
-
|
|
121
|
-
|
|
244
|
+
**L tasks become subtasks, and the person reviews them first.** No worker starts on an L task until the person agrees with its split. In every transport, plan it right away yourself with a planner agent, as Step 4 plain item 1 does (session workers get the plan in their brief), and ask the planner to end the plan with its subtasks, in order, one line each. Save the plan to `$STATE/plans/tN.md` and the subtask lines to `$STATE/plans/tN-subtasks.md`, the first one always `Review this task`. Record the subtasks in `state.json` under the task with status `review` first, then write them under the task as indented `- [ ]` lines, after its status line (with a messenger: `- conductor tN subtasks: <path>`), and set the status:
|
|
245
|
+
|
|
246
|
+
```markdown
|
|
247
|
+
- [ ] Move the sync protocol to CRDT updates @started(26-11-05 14:02)
|
|
248
|
+
- **t7** L, waiting for your review: check off Review this task to start
|
|
249
|
+
- [ ] Review this task
|
|
250
|
+
- [ ] Add an update message to the protocol schema
|
|
251
|
+
- [ ] Send updates from the daemon instead of snapshots
|
|
252
|
+
- [ ] Apply updates in the web client
|
|
122
253
|
```
|
|
123
254
|
|
|
124
|
-
|
|
255
|
+
Until the person checks off `Review this task`, the task waits and nothing is dispatched. They may edit, add, or delete subtasks in the editor first: the subtask lines as they read when the review is checked are the plan the worker gets. A note under the task asking for a different split goes to the planner with its plan; its new list replaces the unchecked subtasks the same way. Once the review is checked, dispatch one worker for the whole task, with the subtasks in order (Step 5). Subtask texts can repeat across tasks: target a subtask by its text only when it is unique, otherwise by the number a fresh `do-it todo list` shows.
|
|
256
|
+
|
|
257
|
+
Never rewrite the person's own task text. Your lines are indented sub bullets starting with `**tN**`; there is exactly one status line per task, which you replace as the task moves, plus any `**tN question:**` lines. Record every task in `$STATE/state.json` (id, text, size, repo, branch, worktree path, agent id or name and pane or handle, status, and for L its subtasks) before its first status line and after every transition; it is how you resume (Step 1a), and how the messenger finds the task for a `tN`. Keep there too the tester's and messenger's names and panes (`"agents": {"tester": {"name": ..., "pane": ...}, "messenger": {...}}`), and `"versions": {"todo": N, "inbox": N, "tests": N}`, the last version of each you read and acted on, updated after every event you handle. Write the file whole with your Write tool (it is yours alone), not with `sed`.
|
|
125
258
|
|
|
126
259
|
## Step 4. Deliver
|
|
127
260
|
|
|
128
|
-
The roles are the same in
|
|
261
|
+
The roles are the same in every transport; `HARNESS` from Step 1 picks the transport below:
|
|
129
262
|
|
|
130
263
|
| Role | Who | Count | Writes code | Runs builds or tests |
|
|
131
264
|
| --- | --- | --- | --- | --- |
|
|
132
|
-
| Conductor | this session | 1 | no | no |
|
|
133
|
-
| Planner |
|
|
134
|
-
|
|
|
135
|
-
|
|
|
265
|
+
| Conductor | this session: sizes, dispatches, monitors, sets stamps | 1 | no | no |
|
|
266
|
+
| Planner | a read only agent: Opus (S, M), the stronger planner from `models.json` or Opus (L) | per task | no | no |
|
|
267
|
+
| Reviewer | a read only agent: Opus (S, M), the stronger reviewer from `models.json` or Opus (L) | per task | no | no |
|
|
268
|
+
| Worker | one agent per task, in its own worktree, named `tN`: Sonnet (S), Opus (M, L) | at most 3 at once | yes | **no** |
|
|
269
|
+
| Tester | one Sonnet agent | **exactly 1** | no | **yes, the only one** |
|
|
270
|
+
| Messenger | one Sonnet agent, named `messenger` (optional, see below) | 0 or 1 | no | no |
|
|
136
271
|
|
|
137
272
|
The single tester is the compute budget. Nobody else runs a compiler, test suite, linter, type checker, dev server, or browser. Formatters are the exception. Workers queue a test request and wait:
|
|
138
273
|
|
|
@@ -142,24 +277,41 @@ do-it todo add "$STATE/tests.md" --version <v> --section queue --as tN \
|
|
|
142
277
|
do-it diff "$STATE/tests.md" --since <v> --wait 100 # loop on exit 4; read the answer under your request
|
|
143
278
|
```
|
|
144
279
|
|
|
145
|
-
|
|
280
|
+
**The messenger** is optional: one agent that owns every `**tN**` line in the todo (status, `**tN question:**` lines, results, Q answers) and the subtask lines of L tasks, so you only run `todo start`, `todo check`, and `todo cancel` there. Turn it on in the session transports from the first task (it costs one cheap tab), and in `plain` once a second task is active or a Q task appears. Once on it stays on: set `"messenger": true` in `state.json`, write the current inbox version to `$STATE/messenger.version`, and from then on never write a `**tN**` line yourself, even between its runs. Nothing written before needs replaying.
|
|
146
281
|
|
|
147
|
-
|
|
282
|
+
Its only feed is the inbox, in order. Workers already append their `progress`, `question`, `blocked`, and `done` lines there; you append your decisions as `conductor` lines:
|
|
148
283
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
284
|
+
```
|
|
285
|
+
- conductor t3 status: planned by Opus: one parser for both formats
|
|
286
|
+
- conductor t7 subtasks: <STATE>/plans/t7-subtasks.md
|
|
287
|
+
- conductor t3 answered (the person answered: remove the question line)
|
|
288
|
+
- conductor t5 answer: <STATE>/answers/t5.md
|
|
289
|
+
- conductor t3 delivered: branch `fix-sidebar-ports` (2 commits, a1b2c3d), tests t3r2 passed, review: ship
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Run exactly one messenger. It writes worker progress and questions into the todo directly, but `delivered` only from your `conductor` line, never from a worker's `done`. Your `todo check` may land a few seconds before its delivered line; that is fine.
|
|
293
|
+
|
|
294
|
+
### Any other terminal (`HARNESS=plain`): Claude Code subagents
|
|
295
|
+
|
|
296
|
+
This needs nothing but Claude Code: `isolation: "worktree"` makes each worker's worktree. Subagents cannot start subagents and cannot be prompted while they run, so you call every agent yourself, always with `run_in_background: true` so watch events keep arriving.
|
|
297
|
+
|
|
298
|
+
1. **Plan.** S and M: `Agent(subagent_type: "Plan", model: "opus", run_in_background: true)` with the task, the repo, and what triage found; ask for a plan in under 300 words (S: under 100). L: the stronger planner from `models.json` with a brief that forbids edits and asks only for a plan ending in subtasks: an agent type is called the same way; a CLI is run by a `general-purpose` agent that runs it read only with that brief and returns its plan unchanged. With neither, plan L with Opus and say so in the status line. Save the plan it returns to `$STATE/plans/tN.md` as it is (do not rework it) and write under the task `**tN** planned by <Opus or the planner's model>: <summary in a few words>` (L: the subtasks and the review wait, Step 3).
|
|
299
|
+
2. **Build.** `Agent(subagent_type: "general-purpose", model: "sonnet" for S or "opus" for M and L, isolation: "worktree", run_in_background: true)` with the worker brief below. Keep the agent id in `state.json`. A plain worker cannot start agents, so for the hard subtasks of an L task it can use only a stronger model's CLI from `models.json`, named in its brief.
|
|
300
|
+
3. **Test.** When the `tests.md` watch shows an open request and no tester runs, start one: `Agent(subagent_type: "general-purpose", model: "sonnet", run_in_background: true)` with the tester brief. It drains the queue one request at a time, oldest first, and exits; start it again for the next requests.
|
|
301
|
+
4. **Questions.** A worker that needs the person appends a `question` to the inbox and ends its run with `QUESTION: ...`. Write it under the task (` - **tN question:** ...`, status `waiting for you`; the messenger does both from the worker's inbox line). When the person answers under it, resume that same worker with `SendMessage` to its agent id: `From the TODO: <their answer>`.
|
|
302
|
+
5. **Review.** When a worker ends with `DONE`, verify (below, which also saves the diff to `$STATE/reviews/tN.diff`), then run the reviewer (`Agent(subagent_type: "general-purpose", model: "opus")` with a read only brief; for L the stronger reviewer from `models.json` when there is one) with the task, `$STATE/plans/tN.md`, that diff file, and `$STATE/tests.md`. Pass paths, not pasted text, and ask for `ship` or `fix-first` with findings. `ship`: delivered. `fix-first`: resume the worker with the findings.
|
|
303
|
+
6. **Messenger.** When it is on and new inbox lines arrive while no messenger runs (the inbox watch skips your own lines, so also right after you append a `conductor` line), start one: `Agent(subagent_type: "general-purpose", model: "sonnet", run_in_background: true)` with the messenger brief. It writes everything since `$STATE/messenger.version` and ends with `INBOX DRAINED`. Lines that arrive while it runs wait for one restart after it ends; never run two.
|
|
154
304
|
|
|
155
305
|
Worker brief:
|
|
156
306
|
|
|
157
307
|
```
|
|
158
308
|
TASK tN (size S|M|L): <the person's task line, verbatim, plus their sub notes>
|
|
159
309
|
REPO: <repo root>. You run in your own git worktree on your own branch; commit there.
|
|
160
|
-
PLAN: <the plan from $STATE/plans/tN.md>
|
|
310
|
+
PLAN: <the plan from $STATE/plans/tN.md; for S and M in a session transport: none, plan it yourself>
|
|
311
|
+
SUBTASKS (L only): <the subtask lines as the person left them, without Review this task>. Work them
|
|
312
|
+
in order. When you begin one, append "- tN subtask started: <its text>" to the inbox; when its
|
|
313
|
+
commits are in, "- tN subtask done: <its text>". HARD PARTS: <the hard_parts command from models.json, or none>.
|
|
161
314
|
NAME: export DO_IT_AGENT=tN before any do-it command.
|
|
162
|
-
TASK TARGET: keep the person's task text verbatim. For todo start and todo check, target that exact text after --, never a remembered task number. If the text is unavailable, run do-it todo list immediately before choosing a target.
|
|
163
315
|
COMPUTE RULE: never run builds, tests, linters, type checkers, dev servers, or browsers yourself.
|
|
164
316
|
Formatters are fine. To test, queue one request and wait for the answer:
|
|
165
317
|
do-it todo add <STATE>/tests.md --version <v> --section queue "tNrK tN: <command> in <your worktree>"
|
|
@@ -169,6 +321,9 @@ PROGRESS: at real milestones only, append to <STATE>/inbox.md section messages:
|
|
|
169
321
|
do-it append <STATE>/inbox.md --version <v> --section messages -- "- tN progress: <one line>"
|
|
170
322
|
QUESTIONS: when the person must decide, append "- tN question: <question>" there, then end your
|
|
171
323
|
run with "QUESTION: <question>". You will be resumed with the answer.
|
|
324
|
+
EXPLAIN: before DONE, write <STATE>/explain/tN.md for the person: one folded section in the skill's
|
|
325
|
+
"Folded sections and HTML" format (<details id="tN-explain">, indented two spaces) saying what
|
|
326
|
+
changed and why, with an html do-it fence where a table or diagram explains it better.
|
|
172
327
|
DONE: commits on your branch, the final test request PASSED. End with
|
|
173
328
|
"DONE: branch <name>, worktree <path>, commits <shas>, tests <request ids>".
|
|
174
329
|
Do not push, open a PR, or merge unless the task line asks for it in words. Never edit the todo file.
|
|
@@ -189,26 +344,118 @@ If a command would run longer than 30 minutes or needs a desktop you do not have
|
|
|
189
344
|
with the reason. When no open request is left, end your run with "QUEUE EMPTY".
|
|
190
345
|
```
|
|
191
346
|
|
|
192
|
-
|
|
347
|
+
Messenger brief:
|
|
348
|
+
|
|
349
|
+
```
|
|
350
|
+
You write the person's todo for the conductor. export DO_IT_AGENT=messenger. TODO=<todo path>,
|
|
351
|
+
INBOX=<STATE>/inbox.md (section messages), CURSOR=<STATE>/messenger.version, and
|
|
352
|
+
STATE=<STATE>/state.json, where each task's entry gives the task text for its tN. Your input is every
|
|
353
|
+
inbox line added after the version in CURSOR, in order (do-it diff <INBOX> --since <cursor>; on
|
|
354
|
+
"no longer kept", do-it read it and take the lines you have not written yet):
|
|
355
|
+
"tN progress: x" status line of tN becomes "**tN** x" (a few words)
|
|
356
|
+
"tN question: q" add " - **tN question:** q" under tN, status "waiting for you"
|
|
357
|
+
"tN blocked: x", "tN failed: x", "tN done", "tN subtask started: x", "tN subtask done: x"
|
|
358
|
+
nothing: the conductor decides
|
|
359
|
+
"conductor tN status: x" status line of tN becomes "**tN** x"
|
|
360
|
+
"conductor tN answered" remove the "**tN question:**" line
|
|
361
|
+
"conductor tN answer: path" status "**tN** answered", the file's lines as sub bullets under it
|
|
362
|
+
"conductor tN delivered: x" status line of tN becomes "**tN** delivered: x"
|
|
363
|
+
"conductor tN explain: path" the file's folded section goes right after tN's status line (and its
|
|
364
|
+
question lines); one with the same id already under tN is replaced whole,
|
|
365
|
+
from its "<details" line through its "</details>" line
|
|
366
|
+
"conductor tN subtasks: path" only while tN has no "Review this task" line or it is unchecked
|
|
367
|
+
(otherwise skip the line): replace tN's unchecked subtasks with the file's lines, as indented
|
|
368
|
+
"- [ ] " lines after its status line (the only task lines you write)
|
|
369
|
+
Each task has exactly one "**tN**" status line, an indented sub bullet right under the task (the
|
|
370
|
+
line whose text is the state.json entry for tN), which you add when missing and otherwise replace
|
|
371
|
+
in place; the last line wins. Read the todo before each write, skip a question or
|
|
372
|
+
answer that is already there, and change only your "**tN**" lines and the folded sections you add, with do-it edit --section tasks
|
|
373
|
+
naming the version. Never touch the person's text, their task lines, checkboxes, or @ tags, never put @
|
|
374
|
+
words in a line, and never use dash separators. Once every line up to the inbox version you read
|
|
375
|
+
is written, save that version to CURSOR, then look again. When no line is left, end your run with
|
|
376
|
+
"INBOX DRAINED".
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Session workers (Herdr, Orca, T3 Code)
|
|
380
|
+
|
|
381
|
+
In these apps every worker is a full Claude session in its own worktree, not a subagent, so the person sees each task in the app and it can call its own subagents. Every worker, tester, and messenger starts in the background: the person stays on the todo and the conversation, and opens a task's space only when they choose to. Never activate, focus, or reveal what you create. Its brief is the worker brief above with `CWD: <worktree> BRANCH: <branch> BASE: <base branch> REPO RULES: read CLAUDE.md/AGENTS.md` in place of the REPO line (`CWD: the worktree you start in` when the app makes the worktree), plus:
|
|
382
|
+
|
|
383
|
+
```
|
|
384
|
+
PLAN AND REVIEW: S and M: plan with an Opus planner before you edit. L: the plan and the subtasks are
|
|
385
|
+
given; use the stronger model named in HARD PARTS for the hard subtasks when there is one. Get a
|
|
386
|
+
review of your branch (Opus; for L the stronger reviewer from models.json when there is one) before DONE.
|
|
387
|
+
CONDUCTOR: talk to the conductor only through the inbox. Answers arrive as prompts starting "From the TODO".
|
|
388
|
+
After a question, wait for that prompt instead of ending your session.
|
|
389
|
+
DONE (in place of the worker brief's DONE): commits on your branch, the final test request PASSED. Append
|
|
390
|
+
"- tN done: branch <name>, worktree <path>, commits <shas>, tests <request ids>" to the inbox.
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
A session worker's last message stays in its own session, which the conductor does not read, so its `done` line in the inbox is the only delivery signal, and the one a resumed conductor finds (Step 1a).
|
|
193
394
|
|
|
194
|
-
|
|
395
|
+
### Inside Herdr (`HARNESS=herdr`): worktrees and tabs
|
|
396
|
+
|
|
397
|
+
Run `herdr --help` once for the CLI; never split your own pane.
|
|
195
398
|
|
|
196
399
|
- **Tester**: one tab in your workspace, started once, watching the queue itself:
|
|
197
400
|
```bash
|
|
198
401
|
TAB=$(herdr tab create --workspace "$HERDR_WORKSPACE_ID" --cwd "$STATE" --label tester --no-focus)
|
|
199
402
|
PANE=$(jq -r .result.root_pane.pane_id <<<"$TAB")
|
|
200
|
-
herdr agent start do-tester --kind claude --pane "$PANE" -- --model
|
|
403
|
+
herdr agent start do-tester --kind claude --pane "$PANE" -- --model sonnet --effort high --dangerously-skip-permissions
|
|
201
404
|
herdr agent prompt do-tester "$TESTER_BRIEF"
|
|
202
405
|
```
|
|
203
406
|
Use the tester brief above, but instead of ending on an empty queue it keeps watching with `do-it watch <TESTS> --todos --as tester` (Monitor tool). Check `herdr agent list` first: reuse a live `do-tester` only when its cwd is this `$STATE`; on `agent_name_taken` pick `do-<SLUG first 4>-tester`.
|
|
204
|
-
- **
|
|
407
|
+
- **Messenger**: the same way, a second tab labelled `messenger` with an agent `do-messenger` (`--model sonnet --effort medium`) and the messenger brief, except that instead of ending on a drained inbox it keeps watching with `do-it watch <INBOX> --section messages --as messenger` (Monitor tool). The same reuse and name rules apply.
|
|
408
|
+
- **Worker per task**: a Herdr worktree and space in the background (a branch named after the task, based on the repo's integration branch, under `.claude/worktrees/`), then Claude in its root pane named `do-tN` (`$MODEL`: `sonnet` for S, `opus` for M and L; `$EFFORT`: `high`, `xhigh` for L) with the session worker brief above. Create, start, and brief it without ever showing it:
|
|
205
409
|
```bash
|
|
206
|
-
|
|
410
|
+
SRC_WS=$HERDR_WORKSPACE_ID; BRANCH=<branch>; BASE=<base>; REPO_ROOT=<repo root>
|
|
411
|
+
WT=$(herdr worktree create --workspace "$SRC_WS" --branch "$BRANCH" --base "$BASE" \
|
|
412
|
+
--path "$REPO_ROOT/.claude/worktrees/$BRANCH" --label "$BRANCH" --no-focus --json)
|
|
413
|
+
herdr workspace focus "$HERDR_WORKSPACE_ID" # always, at once: create can move focus even with --no-focus
|
|
414
|
+
ROOT_PANE=$(jq -r .result.root_pane.pane_id <<<"$WT")
|
|
415
|
+
herdr agent start do-tN --kind claude --pane "$ROOT_PANE" -- --model "$MODEL" --effort "$EFFORT" --dangerously-skip-permissions
|
|
207
416
|
herdr agent prompt do-tN "$BRIEF" --wait --until working --timeout 20000
|
|
208
|
-
herdr workspace focus "$HERDR_WORKSPACE_ID" # if focus moved
|
|
209
417
|
```
|
|
210
|
-
|
|
211
|
-
- **
|
|
418
|
+
Never pass `--focus`, never focus, select, or reveal the new space, its tabs, or its panes, and never use `herdr worktree open` for a new worker. When you must reopen an existing worktree with `worktree open` (a resume), pass `--no-focus` and run `herdr workspace focus "$HERDR_WORKSPACE_ID"` right after it too, since `open` focuses its target anyway.
|
|
419
|
+
- **Answers** go to the worker as `herdr agent prompt do-tN "From the TODO: <their text>"`; it keeps working while it waits.
|
|
420
|
+
- **Parked workers**: when no inbox message came from a task for 30 minutes, read its screen (`herdr agent read do-tN --source recent-unwrapped --lines 80`); if it waits on something that already finished, prompt it to continue.
|
|
421
|
+
|
|
422
|
+
### Inside Orca (`HARNESS=orca`): worktrees and terminals
|
|
423
|
+
|
|
424
|
+
Each task becomes an Orca workspace with Claude in its first terminal. Read Orca's guide for this CLI version once (`orca skills get orca-cli`), pass `--json`, and read the fields named here from the results.
|
|
425
|
+
|
|
426
|
+
- **Worker per task**: an independent worktree from the repo's default base, then Claude on the task's tier (`$MODEL` and `$EFFORT` as in Herdr) in a terminal of its own, briefed the same way as the tester below. Use only flags Orca's guide lists for this version:
|
|
427
|
+
```bash
|
|
428
|
+
W=$(orca worktree create --name do-tN-<short-slug> --no-parent --comment "tN planning" --json)
|
|
429
|
+
WT=$(jq -r '.result.worktree.id // .worktree.id' <<<"$W")
|
|
430
|
+
T=$(orca terminal create --worktree "id:$WT" --title do-tN --command "claude --model $MODEL --effort $EFFORT --dangerously-skip-permissions" --json)
|
|
431
|
+
orca terminal wait --terminal "$H" --for tui-idle --timeout-ms 60000 --json # $H: the terminal handle in $T
|
|
432
|
+
orca terminal send --terminal "$H" --text "$BRIEF" --enter --wait-submit 10 --json
|
|
433
|
+
```
|
|
434
|
+
Keep `worktree.id` (the whole `<repoId>::<path>`), `worktree.path`, and the terminal handle in `state.json`. This creates the worktree in the background, without switching Orca's view: never add `--activate` or `--run-hooks` (both reveal the new worktree), and never pass `--focus` to `orca terminal create`.
|
|
435
|
+
- **Tester**: one terminal in your own worktree, started once and watching the queue itself, as in Herdr. Reuse a live `do-tester` from `orca terminal list --json` first:
|
|
436
|
+
```bash
|
|
437
|
+
T=$(orca terminal create --worktree active --title do-tester --command "claude --model sonnet --effort high --dangerously-skip-permissions" --json)
|
|
438
|
+
orca terminal wait --terminal "$H" --for tui-idle --timeout-ms 60000 --json # $H: the handle in $T; send only when wait.satisfied is true
|
|
439
|
+
orca terminal send --terminal "$H" --text "$TESTER_BRIEF" --enter --wait-submit 10 --json
|
|
440
|
+
```
|
|
441
|
+
- **Messenger**: a second terminal titled `do-messenger` made the same way (`claude --model sonnet --effort medium ...`) with the messenger brief, watching the inbox itself as in Herdr. Reuse a live one first.
|
|
442
|
+
- **The card mirrors the status line**: `orca worktree set --worktree "id:$WT" --comment "<status in a few words>" --workspace-status in-progress --json` (`in-review` while the review runs, `completed` once delivered), and `--unread` while a question waits for the person.
|
|
443
|
+
- **Answers**: `orca terminal send --terminal "$H" --text "From the TODO: <their text>" --enter --wait-submit 10 --json`. `accepted: true` is enough; never resend on silence.
|
|
444
|
+
- **Parked workers**: after 30 quiet minutes, `orca terminal read --terminal "$H" --json`, and prompt it to continue if it waits. After an Orca restart or a `terminal_handle_stale` error, take the new handle from `orca terminal list --worktree "id:$WT" --json` and use only that one.
|
|
445
|
+
|
|
446
|
+
### Inside T3 Code (`HARNESS=t3code`): threads in worktrees
|
|
447
|
+
|
|
448
|
+
Each task becomes a T3 Code thread in its own worktree. Flags differ between T3 Code versions: run `t3 thread start --help` once and use only flags it lists.
|
|
449
|
+
|
|
450
|
+
- **Worker per task**:
|
|
451
|
+
```bash
|
|
452
|
+
ID=$(t3 thread start . "$BRIEF" --worktree "$BASE" --title "do-tN <task in a few words>" --json | jq -r '.threadId // .thread.id // .id')
|
|
453
|
+
```
|
|
454
|
+
`$BASE` is the repo's integration branch (`origin/main` unless the repo says otherwise). Add the task's tier (`--provider claudeAgent --model sonnet` for S, `--model opus` for M and L, and `--effort high`) only when `--help` lists those flags; otherwise the thread uses the project's default model. Keep the thread id in `state.json`. Start threads in the background: never pass a flag that `--help` describes as opening, focusing, or revealing the thread in the app.
|
|
455
|
+
- **Tester**: one thread in the project folder (no `--worktree`), started when the first request is queued: `TID=$(t3 thread start . "$TESTER_BRIEF" --title do-tester --json | jq -r '.threadId // .thread.id // .id')`. It drains the queue and ends its turn with `QUEUE EMPTY`. For later requests, `t3 thread send "$TID" "New test requests are queued: drain the queue"`; never start a second tester.
|
|
456
|
+
- **Messenger**: one thread in the project folder titled `do-messenger` with the messenger brief, started with the first task. It drains the inbox and ends its turn with `INBOX DRAINED`; on new inbox lines, and right after you append a `conductor` line (your watch skips those), `t3 thread send "$MID" "New inbox lines: drain them"`. Never start a second one.
|
|
457
|
+
- **Answers**: `t3 thread send "$ID" "From the TODO: <their text>"`. A send never interrupts; a busy thread takes it as its next turn.
|
|
458
|
+
- **Parked workers**: after 30 quiet minutes, `t3 thread show "$ID"`, and nudge it when it waits. A thread that waits for an answer inside the app (exit code 2 from a `--wait` send) gets a question line under its task.
|
|
212
459
|
|
|
213
460
|
## Step 5. React to events
|
|
214
461
|
|
|
@@ -217,35 +464,41 @@ The same roles as herdrdocwatch, with `do-it` commands in place of Herdr's `doc`
|
|
|
217
464
|
| New task line | Step 3 |
|
|
218
465
|
| The person writes a note under a `listed` item, or edits its text | triage it now (Step 3) with their note as the instruction |
|
|
219
466
|
| The person adds a section whose text is an instruction ("all PRs in this section: ...") | a standing rule: apply it to every item moved or added into that section, as that item's instruction; an empty section waits |
|
|
220
|
-
| The person edits or adds notes under an active task | forward them
|
|
221
|
-
| The person answers a question (an indented line under `**tN question:**`) | forward it the same way, then remove
|
|
222
|
-
| The person deletes a task, or writes "stop" under it | tell its worker to stop and commit its work in progress; status `stopped`, keep the worktree |
|
|
223
|
-
| The person unchecks a delivered task | their new note under it is a follow up for the same worktree and worker |
|
|
224
|
-
|
|
|
225
|
-
|
|
|
226
|
-
|
|
|
227
|
-
|
|
|
467
|
+
| The person edits or adds notes under an active task | forward them as `From the TODO: ...` through the worker's transport: `SendMessage` (plain, once it has stopped), `herdr agent prompt do-tN`, `orca terminal send`, or `t3 thread send` |
|
|
468
|
+
| The person answers a question (an indented line under `**tN question:**`) | forward it the same way, then remove the question line (with a messenger: append `- conductor tN answered`) |
|
|
469
|
+
| The person deletes a task, or writes "stop" under it | tell its worker to stop and commit its work in progress; status `stopped`, keep the worktree. Only "cancel" or "drop it" in words also gets `todo cancel` |
|
|
470
|
+
| The person unchecks a delivered task | their new note under it is a follow up for the same worktree and worker; keep its `@started` |
|
|
471
|
+
| The person checks off `Review this task` under an L task | if a `conductor tN subtasks:` line of yours is not written yet, wait for the messenger to drain it first; then read the task's subtask lines as they are now, save them in `state.json`, and dispatch one worker with them (status `L, building subtask 1 of N`), or queue it while 3 workers are active (status `L, reviewed, queued`) |
|
|
472
|
+
| The person adds or edits a `- [ ]` line under an L task still in review | a subtask edit: nothing to do until the review is checked |
|
|
473
|
+
| The person writes a plain note under an L task still in review | a request for a different split: send it to the planner with its plan and write the new subtasks (Step 3) |
|
|
474
|
+
| Inbox `subtask started` or `subtask done` | `todo start` or `todo check` on that subtask line; the status reads `L, building subtask K of N` |
|
|
475
|
+
| A Q agent returns `ANSWER: <path>` | write under the task: without a messenger, the file's lines as sub bullets under `**tN** answered`; with one, `- conductor tN answer: <path>`. Then `todo check` |
|
|
476
|
+
| Inbox `progress` | update the status line only, in a few words (the messenger does this by itself) |
|
|
477
|
+
| Inbox `question` | add ` - **tN question:** <question>` under the task; status `waiting for you` (the messenger does this by itself) |
|
|
478
|
+
| Inbox `blocked` or `failed`, or a worker ended without DONE | read what it said; fix the brief and resume it, or after a second failure: an S task moves to an Opus worker, an M task gets a new Opus plan and a fresh Opus worker (or a question under the task), an L task may use the stronger model from `models.json`; write under the task only what the person must decide |
|
|
479
|
+
| Worker DONE | verify (below), review, write under the task `delivered` and its explanation from `$STATE/explain/tN.md` when the worker wrote one (with a messenger: `- conductor tN explain: <path>`; without one, the `edit` in Folded sections and HTML), then run `todo check` for the task |
|
|
228
480
|
| Tester BLOCKED or a long queue | one line under the affected task; never start a second tester |
|
|
229
481
|
|
|
230
|
-
**Verify before `delivered`.** A done message is not proof:
|
|
482
|
+
**Verify before `delivered`.** A done message is not proof. Check only the facts, and save the diff for the reviewer instead of reading it:
|
|
231
483
|
|
|
232
484
|
```bash
|
|
233
485
|
git -C "<worktree>" log --oneline "<base>..HEAD"
|
|
234
486
|
git -C "<worktree>" diff --stat "<base>...HEAD"
|
|
487
|
+
git -C "<worktree>" diff "<base>...HEAD" > "$STATE/reviews/tN.diff"
|
|
235
488
|
```
|
|
236
489
|
|
|
237
|
-
and the final request in `tests.md` says PASS for that commit. The delivered line reads:
|
|
490
|
+
and the final request in `tests.md` says PASS for that commit. The review of the change itself is the reviewer's, on that file. The delivered line reads:
|
|
238
491
|
|
|
239
492
|
```
|
|
240
|
-
- **t3** delivered: branch `fix-sidebar-ports` (2 commits, a1b2c3d), tests t3r2 passed,
|
|
493
|
+
- **t3** delivered: branch `fix-sidebar-ports` (2 commits, a1b2c3d), tests t3r2 passed, review: ship. Worktree `.claude/worktrees/fix-sidebar-ports`
|
|
241
494
|
```
|
|
242
495
|
|
|
243
496
|
Keep your lines short: the todo is a dashboard, not a log. Full detail lives in `plans/`, `logs/`, and the worktrees.
|
|
244
497
|
|
|
245
|
-
|
|
498
|
+
Then mark the task done, which adds `@done`:
|
|
246
499
|
|
|
247
500
|
```bash
|
|
248
|
-
|
|
501
|
+
do-it todo check "$TODO" --version $V --section tasks -- "<the task text>"
|
|
249
502
|
```
|
|
250
503
|
|
|
251
504
|
## Ending
|
|
@@ -255,7 +508,21 @@ Run until the person says in chat to stop, or the todo goes away. On stop: stop
|
|
|
255
508
|
## Rules
|
|
256
509
|
|
|
257
510
|
- **The todo is the interface.** Every event the person should know about lands in it.
|
|
258
|
-
- **
|
|
511
|
+
- **Delegate and monitor.** You never read code, docs, or diffs, never research, and never answer a question yourself; an agent does, and you keep its conclusion. Your own todo labels are `@started` at once and `@done` on delivery.
|
|
512
|
+
- **One writer per channel role.** Always through `do-it` commands naming the version:
|
|
513
|
+
|
|
514
|
+
| Channel | Who writes | What |
|
|
515
|
+
| --- | --- | --- |
|
|
516
|
+
| the todo | the person | anything |
|
|
517
|
+
| the todo | you | stamps only, through `todo start`, `todo check`, `todo cancel` (on subtasks too); plus the `**tN**` lines and L subtask lines while no messenger is on, and the seed block |
|
|
518
|
+
| the todo | the messenger | every `**tN**` line and the L subtask lines once it is on |
|
|
519
|
+
| `inbox.md` | workers; you | their `progress`, `question`, `blocked`, `done`, `subtask` lines; your `conductor` lines |
|
|
520
|
+
| `tests.md` | workers; the tester | requests; the answers under them |
|
|
521
|
+
| `answers/`, `seed.md`, `explain/` | Q, seed, and worker agents | one file each |
|
|
522
|
+
| `state.json`, `models.json`, `plans/`, `reviews/` | you | run state, the models you found, saved plans and subtasks, diffs for review |
|
|
523
|
+
| `messenger.version` | the messenger | its cursor (you write it once, when you turn it on) |
|
|
524
|
+
|
|
525
|
+
Session workers keep their own plans in their worktree; `plans/` holds the ones you saved.
|
|
259
526
|
- **One tester, one test at a time.** A worker caught running a build or test is stopped and reminded of the compute rule.
|
|
260
527
|
- **Three workers at most** unless the person writes otherwise in the todo. Each owns its worktree; no two writers share a checkout.
|
|
261
528
|
- **No outward actions by default.** Pushing, PRs, merges, deploys, and messages to other people happen only when the task line asks in words.
|