The operator runs each herdr command in its own shell, in a Bash call. See Operator and
Sessions for the terms.
- The user picks the slug.
- The user runs
tasker show-config --repo <key>and readssessions.models.operator.<key>is the repo key of the repo of the local clone, for examplecodeguru42/tasker. If the command fails, the user does not open the operator. Then the user opens the operator inclaudeand names itoperator-<slug>. The user passes the model and the effort ofsessions.models.operatorwith the flags of the seam of an agent kind, for exampleclaude -n operator-m9 --model opus --effort high. Ifsessions.models.operatorhas no entry, the user passes neither flag. - If the user tells the operator that the milestone has no initial planning, the operator does not start the initial planner. The operator skips steps 4 to 6 and does step 7 at once. Otherwise, when the user asks, the operator starts the initial planner, as "Start a session" says. An example of a milestone with no initial planning is a milestone that stays open and gets its issues from the follow-ups of other tickets.
- The user works with the initial planner in its tab. The initial planner and the follow-up planner of planner.md gives the rules of the initial planner.
- When the initial planning ends, the user tells the initial planner that the initial planning ends. If the user tells
the operator first, the operator tells
planner-<slug>withSendMessage, as The word of the user says. The message of the operator starts with "From the user:" and says that the initial planning ends. Then the operator waits for the message of the initial planner that the initial planning is done. This message ends the wait. While it waits, the operator continues its other work and does not wait in a loop, as the answer-wait rule says for an agent. If the send fails, the operator tells the user, as the failure rule says. The tab of the initial planner stays open, and the operator still waits for the message of the initial planner. - The message of the initial planner is the one signal that ends the initial planning. The operator does this step only after this message. The word of the user to the initial planner in step 5 is the request of the user for this stop. Then the operator closes the tab of the initial planner, as "Stop a session" says.
- The operator starts the follow-up planner and the orchestrator, as "Start a session" says.
- At the end of the milestone, The close of a milestone applies.
If a stop or a start fails, the failure cases of "Stop a session" and "Start a session" apply.
Each time that the operator does this section, it first reads docs/dev/operator-start.md again, as
the doc-read rule says. Then it does the steps of the text that it read. The reason: a
notice can be lost, and a start or a stop from an old text acts on a real session. A section keeps the text that it read
until the section ends. A new text that lands in the middle of the section applies from the next read. A section keeps
this text also when a notice arrives in the middle of it, and the notice applies to the next section.
-
The operator runs
tasker show-config --repo <key>in its shell.<key>is the repo key of the repo of the local clone, for examplecodeguru42/tasker.tasker show-config --repo <key>
The operator keeps
.sessions.kindand the entry of.sessions.models.<role>of the output.<role>is the role of the new session:planner,orchestratoror, in a handoff of the operator,operator. As a result, the new operator starts with the model and the effort ofsessions.models.operator. If the command fails, the operator tells the user the command and the error, as the failure rule says. The operator does not do steps 2 and 3. No tab exists, so no entry goes into the tab list. -
The operator runs
herdr tab createin its shell. The folder of--cwdis the local clone.herdr tab create --workspace "$HERDR_WORKSPACE_ID" --cwd <local clone> --label <name> --no-focus
In a handoff of the operator, the operator leaves out
--no-focus, as The start of the new operator says.The operator keeps
.result.tab.tab_idand.result.root_pane.pane_idof the output. If the command works, the operator puts the tab id and the session name into the tab list. If the command fails, the operator tells the user the command and the error, as the failure rule says. The operator does not do step 3. No tab exists, so no entry goes into the tab list. -
Before the command, the operator joins the lines of the prompt into one line, with one space between two lines. herdr 0.9.3 refuses a prompt with a newline, with the error "agent arguments cannot be encoded safely for the target shell". operator-docs measured this on 2026-10-24, in the handoff of planner-docs. Then the operator runs
herdr agent startin its shell. The command starts the agent of the kind in the new pane.herdr agent start <name> --kind <kind> --pane <pane id> -- -n <name> <model flags> "<prompt>"
In a handoff of the operator, the herdr agent name and the session name differ. The herdr agent name is
operator-<slug>-<time>, and-ntakes the session nameoperator-<slug>. "The start of the new operator" gives the command.<kind>issessions.kind.<model flags>is the model flag and the effort flag of the kind in the seam of an agent kind, with the model and the effort of the entry of the role. Ifsessions.modelshas no entry for the role, the operator leaves out<model flags>.herdr agent startcan fail while the new session still starts. The known errors are "timed out waiting for agent startup", and "named agent<name>no longer owns the target terminal" with the codeagent_name_not_found.<name>is the name of the session.After one of these errors, the operator first looks for the new session in
ListAgents, or it runsherdr pane read <pane id>in its shell. The pane id is the one that step 2 kept. If the session runs, the start worked. IfListAgentsdoes not list the new session, orherdr pane readfails or shows no running session, the failure case that follows applies. In a handoff of the operator, step 3 of "The start of the new operator" wins over the first sentence of this paragraph.operator-docs saw the timeout on 2026-10-24, in the handoff of planner-docs, and the session ran. operator-docs saw the error "named agent orchestrator-docs no longer owns the target terminal" (
agent_name_not_found) on 2026-10-24, at the start of the new orchestrator-docs, and the session ran.If the command fails in another way, or the new session does not run, the operator tells the user the command and the error. The new tab stays open, and its entry stays in the tab list. The operator does not close the tab without the user. If the user closes that tab, or tells the operator to close it, the entry leaves the tab list.
The facts that follow were measured on 2026-10-24 with herdr 0.9.3 and Claude Code 2.1.310:
- Herdr sets
HERDR_WORKSPACE_IDin each pane that it manages. As a result, the new tab opens in the workspace of the operator. - The flag
-nsets the session name, and the last argument is the first prompt of the session. The seam of an agent kind is the home of these flags. - In the fact tests of #503 and #516, the row of a test session had the field
name, with the herdr agent name. Each agent read the row soon after aherdr agent startthat exited with 0. The row for a pane thatherdr pane report-agentreported, with noherdr agent startin that pane, has nonamefield. The agent of #503 measured this with herdr 0.9.3 on 2026-10-25. - On 2026-10-25 at about 15:40 MDT, none of the 17 rows of
herdr agent listhad anamefield. This includes the rows of planner-m14 and orchestrator-m14, whichherdr agent startstarted. The cause of the missing field is not known. The start of planner-m14 and the start of orchestrator-m14 each exited with 1 and the error codetimeout. For the other rows, the result of the start is not known. operator-m14 and planner-m14 measured this with herdr 0.9.3 on 2026-10-25. herdr agent startrefuses a herdr agent name that already runs. It exits with code 1 and prints the error codeagent_name_taken. The agent of #503 measured this with herdr 0.9.3 on 2026-10-25.- The new session is a top-level session, not a subagent of the operator.
- The tool
SendMessageworks in both directions, andListAgentslists the session. - The model flag and the effort flag of the seam of an agent kind reach the new session. The new session runs with the model and the effort of these flags.
The operator puts the milestone title in place of <milestone>. Each prompt is the last argument of
herdr agent start.
The initial planner:
You are the planner of <milestone>. Run git fetch origin. Then run git show origin/main:AGENTS.md and git show origin/main:docs/planner.md. This is the initial planning: the user works with you in this tab.
The follow-up planner:
You are the planner of <milestone>. This is not the initial planning. Run git fetch origin. Then run git show origin/main:AGENTS.md and git show origin/main:docs/planner.md.
The orchestrator:
You are the orchestrator of <milestone>. Run git fetch origin. Then run git show origin/main:AGENTS.md and git show origin/main:docs/orchestrator.md. Then start.
Each time that the operator does this section, it first reads docs/dev/operator-start.md again, as
the doc-read rule says. Then it does the steps of the text that it read.
Start a session gives the reason and the rule of the new text.
The operator stops a session only on a request of the user. In a handoff of an orchestrator or a follow-up planner, the rule of the user of 2026-10-25 in The end of a handoff is that request, for step 4 of A handoff. The operator closes only a tab that it opened in its own session, or a tab that an earlier operator of its slug opened. In a handoff, the operator also stops a session in a tab that it did not open, for example a tab that the user opened.
In a handoff of the operator, the old operator closes its own tab with no request of the user. This sentence wins over two rules for the own tab of the old operator. The first rule is "The operator closes only a tab that it opened in its own session, or a tab that an earlier operator of its slug opened." The second rule is the sentence of the intro of the operator index that says "The tab list is the list of the tabs that an operator can close." That index is docs/operator.md. The start of the new operator gives the steps.
Outside a handoff, the operator takes the tab id of each session from the tab list, also of its own sessions. If the tab list gives no tab id for the session, the operator tells the user and closes nothing. In a handoff, the operator takes the tab id from the row of the session.
A missing tab id has two reports: the tab list gives none, or the row gives a tab id outside the tab list. After either report, the user can tell the operator to close the session. On that word, the operator takes the tab id from the row of the session, as in a handoff. That word covers a tab that the operator did not open. It wins over each sentence here that limits a stop to a tab of the tab list, or to a tab that the operator opened. It also wins over the sentence on the tab list in docs/operator.md.
On 2026-10-30, the user opened the initial planner, so its tab was not in the tab list. The user said: "You close the initial planner".
The operator finds the pane id and the tab id in the row of the session. The row of a session is the object in
.result.agents of the output of herdr agent list whose terminal_title_stripped is the name of the session. The row
gives pane_id and tab_id. Before step 1, the operator runs herdr agent list in its shell. If the command fails,
the operator tells the user, as the failure rule says, and closes nothing. If no row has
the name of the session, the operator tells the user and closes nothing.
Each time that the operator reads the row of a session, in a handoff or in each other stop, it counts the rows with the name of the session. If more than one row has the name, the operator does not do steps 1 to 3, sends no "stop" and closes no pane and no tab. It tells the user one time. The message names the session, and the pane id and the tab id of each row with that name, and it gives no other items. This stop did not close the pane of the session, and the operator sends no second message for the failed close. A close of the wrong pane stops the other session.
Outside a handoff, if the tab id of the row is not a tab id of the tab list, the operator tells the user and closes nothing. Steps 2 and 3 use the pane id and the tab id of the row.
- The operator sends "stop" to the session with the tool
SendMessage, as a word of the user (see The word of the user). IfSendMessagefails, the operator tells the user and does not do steps 2 and 3. The tab stays open. - The operator runs
herdr agent wait <pane id> --timeout 600000in its shell.herdr agent wait <session name>exits with 1 and the codeagent_not_foundfor each listed session (herdr 0.9.3, measured on 2026-10-24). - If the wait ends in
idleordone, the operator runsherdr tab get <tab id>in its shell. If.result.tab.pane_countis 1, the operator runsherdr tab close <tab id>. If.result.tab.pane_countis more than 1, the operator runsherdr pane close <pane id>. A tab that the user opened can hold other panes of the user. Ifherdr tab get,herdr tab closeorherdr pane closefails, the operator tells the user, and the pane stays open. If the close works and the tab has an entry in the tab list, the operator takes that entry out. Afterherdr pane close, the tab stays open.
A session at its input prompt runs no turn and is ready for the next input at its prompt, in the state idle or done.
A session in blocked is not such a session, and A blocked session covers it. For a session at
its input prompt, the wait of step 2 ends at once, before the session reads "stop". Then the session can read "stop" in
a new turn right after the wait, or it reads nothing before the close. In both cases, the operator still does the close
of step 3 at once. It does not wait for a read of "stop", and it sends no message to the user for this case. The reason:
the turn that "stop" starts only answers the stop, so a close that cuts it loses no work. On 2026-10-25, with herdr
0.9.3 and Claude Code 2.1.310, the agent of #519 sent "stop" to a test session of its own in the state done.
herdr agent wait <pane id> --timeout 600000 returned done at once. About 0.5 seconds later, the message started a
new turn, and the session answered the stop. On 2026-10-25, with herdr 0.9.3 and Claude Code 2.1.310, operator-docs sent
"From the user: stop" to planner-messages, which waited for the input of the user.
herdr agent wait wB:p9 --timeout 600000 returned idle at once. herdr pane read showed "1 new message (ctrl+End)",
so the stop was still unread. Then herdr tab close closed the session before it read the stop.
If the wait fails or times out, the operator tells the user and closes nothing. If the wait ends in blocked, the
operator follows "A blocked session" and closes nothing. Without --until, the wait also ends in blocked.
After each other end of this section, the entry stays in the tab list. Examples are a failed SendMessage, a failed
wait, a blocked session and a failed herdr tab close.
This section gives the order of the end of a milestone. A round and the end of the rounds work as the bullet that starts "When the Todo column of the milestone is empty" of Pick the tickets says. If no orchestrator runs, steps 1 and 2 do not apply.
- The orchestrator runs its rounds, as that bullet says.
- When the orchestrator reports that the milestone is done, the operator stops the orchestrator, as "Stop a session" says. The decision of the user of 2026-10-31 is the request of the user for this stop. A long-running orchestrator that moves to the next milestone does not stop.
- The operator runs its own rounds. It can start them at the same time as step 1. It sends each finding that proposes a change of the docs to the follow-up planner. If no follow-up planner runs, it gives the finding to the user. When its rounds end, it tells the follow-up planner.
- The follow-up planner finishes each draft in its review loops. Then it handles each finding: it posts the finding, drops it or writes it to memory. Then it runs its rounds. It handles the findings of its own rounds in the same way. The operator runs no new round for them. After its last round, the follow-up planner sends a report to the operator.
- When the user says to close the milestone, the operator relays this word to the follow-up planner. The follow-up planner closes the milestone, as Milestones says. If no follow-up planner runs, the operator tells the user and closes nothing.
- When the user says to stop the follow-up planner, the operator stops it, as "Stop a session" says. If step 5 did not run, or the follow-up planner has an open item, the operator first tells the user. It then stops the session only on a new word of the user.
- The operator ends last, and only on the word of the user. The user closes the tab of the operator.
If a round or a send of steps 3 and 4 fails, the failure rule applies to that session. Its rounds end. The operator does not wait in a loop for a report. If a report does not come, the operator tells the user, and the word of the user decides.
The user can end the hold of a ticket on hold while its milestone is open and ListAgents shows no session
orchestrator-<slug> of its slug. Then the operator of that slug removes the label with
gh issue edit <number> --remove-label "on hold", and it starts an orchestrator of that milestone, as "Start a session"
says. Then it sends the orchestrator the word of the user, as
The word of the user says. If the read of ListAgents or the removal
fails, the failure rule applies, and the operator starts no orchestrator. For a denied
command, the operator gives the user the command with the ! prefix, and it starts the orchestrator after the user says
that the command ran. In each other case, the operator tells the user and does nothing more.
Each time that the operator does this section, it first reads docs/dev/operator-start.md again, as
the doc-read rule says. Then it does the steps of the text that it read.
Start a session gives the reason and the rule of the new text. In steps 4 and 5, "Stop a session"
and "Start a session" each read the file again, because these steps can run hours after step 1.
The operator does these steps for each handoff of an orchestrator or a follow-up planner. This also covers a session that the user started in a tab of the user. The old session is the orchestrator or the follow-up planner that hands off its work. The new session takes the work, with the same name as the old session.
If a handoff of an orchestrator or a follow-up planner is in progress at the start of a handoff of the operator, step 1 of the steps of the old operator says what each operator does.
- A recommendation "hand off now" of the old session arrives. This recommendation is a suggestion of a handoff, as
The suggestion of a handoff says. The operator sends the go to the session
at once with
SendMessage, with no question to the user. The go is a word of the operator, not a word of the user, so it does not start with "From the user:". The session finishes its current tasks and then does the steps of its handoff. IfSendMessagefails, the operator tells the user, as the failure rule says. - The old session sends the operator the path of its handoff file as an item. The item can also give the prompt for the new session.
- The operator reads the handoff file. It takes the prompt for the new session from the code block of the file. If the read fails, the operator tells the user, as the failure rule says, and does not do steps 4 and 5.
- The operator stops the old session, as Stop a session says.
- The operator starts the new session, as Start a session says. The prompt is the prompt of step 3.
If step 4 does not close the pane of the old session, the operator does not do step 5. It tells the user, as
the failure rule says. In the case of more than one row, the message of
Stop a session is the one message. Two sessions with one name break each SendMessage to that name.
In a handoff, these rules stay with the user:
- Only the user answers a prompt of a session in
blocked, as A blocked session says. - After a failed
herdr agent start, the operator does not close the new tab without the user, as Start a session says.
The table gives each change of a handoff. An empty cell means that the page gives no state for that actor. For the case of more than one row, the row "More than one row has the name" wins over the row "A close or a start fails".
| Change | Old session | Operator | New session | Who tells whom |
|---|---|---|---|---|
| The recommendation arrives | Waits for the go | Sends the go | The old session tells the operator | |
| The go arrives | Finishes its tasks, writes the handoff file | The operator tells the old session | ||
| The path arrives | Waits | Reads the handoff file | The old session tells the operator, with an item | |
| The stop | If it runs a turn, it reads "stop" and ends the turn. At its input prompt, it can read "stop" in a new turn or read nothing. Its pane closes | Waits for idle or done, then closes the pane |
The operator tells the old session "stop" | |
| The start | Starts the new session | Runs, from the prompt | The operator starts it with herdr agent start |
|
| A close or a start fails | Tells the user | The operator tells the user | ||
| More than one row has the name | Runs | Closes nothing, tells the user | Not started | The operator tells the user |
- When an item arrives, and when the user asks, the operator runs
herdr agent listin its shell. If the command fails, the failure rule applies. - A session in
blockedwaits at a permission prompt or a question prompt in its own tab. The operator tells the user the name of the session and the label of its tab. - The operator does not answer the prompt. Only the user answers it, in that tab.