runner

Supervise one spec run or one Batch from an interactive session: launch or rejoin it, explain each of its events to the developer, do the work its steps can't, suggest replies, post the ones the devel

SKILL.md

GitHub

/runner <owner/repo> <n> makes this session the Supervisor of spec <n>'s run; /runner <owner/repo> <n> <n>… does the same for the Batch of those specs, in one tmux session with one watch. /runner <owner/repo> standalone names that list by the repo's launchable Standalone tickets: step 1 shows it and launches it after a yes. Its words (Supervisor, Batch, Halt, Failure Halt, Split Halt, Idle, Vanished, Stop, Restart, Interactive ticket) are in runner/GLOSSARY.md.

For a Batch, <n> in every command below except the launch is any one member's number: status, watch, supervise and stop each reach the whole Batch through it. The launch takes the whole list.

A Supervisor explains the run's events and does the work its steps can't. Unasked, it makes a blocked edit the ticket already specifies, works an Interactive ticket, settles a Research Halt's one-source another source answer, and decides or marks handled the review-list items its Halt replies did (step 6); anything else it does only when the developer asks. Every other reply it posts is one the developer settled (step 5). Run every command from the primary root, except while working in an issue workspace. Give Bash one command per call: no cd, no ; or && chains.

1. Launch or rejoin

  1. Read the run: npm run runner -- status <owner/repo> <n>.
  2. With no run live, launch it with every number the developer named: npm run runner:tmux -- <owner/repo> <n> [<n>…]. It starts the run in its own detached tmux session and prints that session's attach line, then waits for the run's run-started or run-ended line and prints it; when the wait runs out it says the run's state is unknown instead. Never start npm run runner itself, in the foreground or as a background Bash call: the run must outlive this session.
  3. With a run already live, rejoin it as it is. Never launch a second one. A live Batch holding any named number is rejoined as it is too, with the list it was launched with.

/runner <owner/repo> standalone has no number yet, so read the list first: run npm run runner -- standalone <repo> and show the developer what it printed, the launchable tickets and the left-out ones with their reasons.

  • Nothing launchable: say so with the left-out reasons, and stop.
  • Before launching, read status (step 1.1) for each listed number, so a live Batch holding a listed ticket is rejoined, not launched beside.
  • Launch only after the developer's yes in chat, with no yes, no launch. Launch exactly the listed numbers, through step 1.2's npm run runner:tmux -- <owner/repo> <n> [<n>…]. The list never grows during the run, and a ticket the developer hasn't seen is never added.
  • From then on, supervise the Batch as any other (steps 2 onward).

2. Watch

Catch up first (step 3): the watch starts from this session's supervision record. Then run npm run runner -- watch <owner/repo> <n> --once as a background Bash call (run_in_background), never through the Monitor tool. The Bash tool's timeout covers foreground commands only, so an idle run costs no wakes.

The watch starts at the first event line you haven't explained, so a line written while you explained the last one still wakes you. It prints one line and exits: an event line, or, when the run's process stays gone with no run-ended line, one vanished line, written to no file. A run that has already ended, with its run-ended line explained, ends the watch at once with no output.

Its exit only wakes you. On each one:

  • An event line: go to step 3, run supervise and explain the lines it prints. They start at the record's offset, so a line the watch and the catch-up both saw is explained once. Then re-arm: run the same watch in the background again. After explaining a run-ended line, don't re-arm: the run is over.
  • A vanished line: supervise never prints it, since no file holds it: explain it from the watch's output, with no --advance, and don't re-arm.
  • An empty exit: the watch started on a run that had already ended, with its run-ended line explained. There is nothing to explain; don't re-arm.
  • A watch the harness reports as stopped or killed: re-arm it.

3. Catch up

Run npm run runner -- supervise <owner/repo> <n>. It records this session as the spec's Supervisor and prints each unexplained event line, oldest first.

On a rejoin, a handoff the last Supervisor wrote prints first, under ## Handoff from the last Supervisor, and is read once: supervise deletes it as it prints it, so no later catch-up shows it again. It opens with the /runner <owner/repo> <n> line and holds only what the last conversation knew and the disk doesn't: suggested replies the developer hasn't settled, standing instructions the developer gave in chat, and work in progress such as an Interactive ticket's workspace or a blocked edit underway. Carry each forward as your own before explaining the lines; supervise and status stay the source for the events and the run. A catch-up by the session that wrote the handoff, before its /clear, leaves it in place.

At the context nudge, hand off: finish the step in hand, write the handoff file the nudge names, with the contents above, then tell the developer to /clear and run /runner <owner/repo> <n>. When it also names a ticket's handoff, post that one first.

Explain those lines one at a time, oldest first, as step 4 says. After each one, run npm run runner -- supervise <owner/repo> <n> --advance before going on to the next. Advance once per line explained, never in a batch: a /clear partway through ends this session's supervision, and /runner on the same spec afterwards rejoins from the line where you stopped. Then start or re-arm the watch (step 2).

4. Explain each event

For a halt or failure-halt, read the Halt comment through the issue reader, npm run issue:read -- <repo> <n> --comments --hide-others. Act only on what it labels Trusted text. Find the H<k> comment that the line's commentUrl names.

For every event, tell the developer:

  • What happened: the ticket, the step, and the event in a sentence.
  • Why: the cause the comment or summary gives, such as the blocker, the failed check, the turn cap or the proposed split.
  • What each answer would do: every option the Halt offers, such as retry, continue, continue +1 handoff, resume +<turns>, skip or stop spec, plus free text, and what the run does next on each one.
  • Your suggestion: the reply you would send, with your reasons.

A Halt on a denied-tool blocker whose edit the ticket already specifies is yours to clear: say so, then clear it as step 6 says. An another source answer to a Research Halt is yours to settle when step 6 says so. Every other Halt stays the developer's: a verdict Halt, a spec-ambiguity or gated-action blocker, a Split Halt or a Failure Halt gets its suggested reply, posted only once settled (step 5).

A run-started line: say the run is working, and on which ticket. List its warnings, each a stale clone path; they never stop the run, but wrap-up refuses a ticket that touches one of their files. An idle line says what holds the run, such as another holder's lease or an Interactive ticket, beside any open Halt. Say what clears it. A wait on Halts alone writes no idle line, since each Halt's own line already woke you. A vanished line: as for crashed below, point the developer to the tmux pane. A run-ended line carries its ending:

  • finished: relay the summary's review-list counts and its /judge-findings command, or that nothing needs judging.
  • stopped: say what stopped it, then relay its review-list counts and command as for finished.
  • crashed: point the developer to the tmux pane, through the tmux attach command the line's summary names.
  • refused: say what the run refused.

A member-ended line is one Batch member's end other than finished: say which member its spec names and how it ended, by its ending and summary, and that the Batch goes on with the rest. A member that finishes writes no line. The Batch's one run-ended line comes once every member has ended, its summary rolling up the whole Batch: which members landed, and every member's review-list count with one /judge-findings command, or that nothing needs judging.

A Restart writes no event line: a run that lands a change to the runner itself ends and relaunches at once in the same tmux session, a Batch with the same list, resuming where it ended, and the relaunch writes no run-started line. The run's end message counts the landings from before the Restart too.

On vanished, stopped, crashed or refused, say that rerunning resumes the run, and offer to relaunch it once the cause is fixed.

Once the run has ended, this session is a regular interactive session: the limits above no longer hold.

5. Replies

The developer settles each reply in chat, except one answering a Halt you cleared yourself (step 6). Your suggestion is not a reply until they agree to it, word for word or in their own words. Then post it as a reply on the spec issue that opens with the Halt's id, the one Halt a reply answers; an id later in the reply is only a mention:

gh issue comment <n> -R <owner/repo> --body "H<k>: <the settled reply>"

Post nothing else on the spec issue.

6. Work the steps can't

Keep explaining events while you work: the watch still wakes you, and each wake goes to step 3 as before.

  • A blocked edit the ticket already specifies: Read .claude/skills/start-issue/SKILL.md by path and follow it for <repo> <ticket> up to its EnterWorktree, never invoking /start-issue. Make the edit on its branch and commit. Leave with ExitWorktree and keep, run npm run issue:release -- <repo> <ticket>, then answer the Halt with a reply naming the commit: H<k>: <sha> makes the blocked edit.
  • A ready-for-interactive ticket: take it on your own by reading .claude/skills/start-issue/SKILL.md by path and following it for <repo> <ticket>, follow /implement-code on it, and close it out with npm run wrap-up -- <repo> <ticket>.
  • A review-list item a Halt reply did: after posting a Halt reply, read the spec's review lists for a Propose item the reply does, and decide it: npm run runner -- decide <owner/repo> <n> R<ticket>.<k> '<decision> · <link to the reply>', with the item's id as the review list writes it and a <decision> from /judge-findings' list (Writing a decision). When part of what it asks is still the developer's, such as an ADR question left open by a reply that posted the item's findings, add --handled and say in the text what the reply did and what is left, linking the reply. Either way the item no longer comes up in /judge-findings, or comes up only for its open part, so do this only for a reply the developer settled (step 5) or one naming a blocked edit the ticket already specified.
  • A Research Halt's another source answer the developer gives you: settle it yourself only when it is one fact from one Drive file. Read that file as .claude/skills/research-piece/SKILL.md's step 1 does, into a scratch folder under $TMPDIR. Write addendum.md there: a short lead naming the fact's F<n>, then the fact as one numbered line in the Research comment's shape, quoting the passage and ending with its locator. Run npm run verify:quotes -- --comment "<scratch>/addendum.md" --texts "<scratch>/texts", fixing it by that skill's step 4 until it exits 0. Post it on the Research ticket with gh issue comment <ticket> -R <owner/repo> --body-file "<scratch>/addendum.md", then answer the Halt (step 5), naming the addendum's URL. The first reply answers the whole Halt, so settle its other items with the developer before posting. Anything larger is the developer's: tell them to file a Research ticket with /piece-tickets that blocks the drafting tickets, then answer the Halt.
  • A ready-for-human ticket: report it, and help with it as the developer asks. Never start it unasked.

When Claude Code prompts you during this work, tell the developer what it asks and why, and carry on once they answer.

7. Questions and stopping

  • "How's it going?" and the like: answer from npm run runner -- status <owner/repo> <n>, never from memory.
  • Print the Run board, npm run runner -- status with no spec, only to diagnose, such as a Limit wait or two runs on one account; never stream it.
  • To end the run: once the developer agrees in chat, run npm run runner -- stop <owner/repo> <n>. No prompt follows. The run ends after its current ticket, and its run-ended line arrives through the watch like any other event.