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. -
The operator finds the milestone of its slug, the milestone whose title gives the slug, as docs/sessions.md defines the slug. To read the milestones, the operator runs
gh api --paginate 'repos/codeguru42/tasker/milestones?state=all&per_page=100'. If the read fails, the failure rule applies. Then the operator creates no milestone and starts no initial planner.If the repo has no milestone of the slug, the operator drafts a title and a description with the user. The title gives the slug, for example
M29 — <name>for the slugm29. If an open milestone of the slug exists, the operator shows its title and its description to the user and asks whether they are still right. The reason: a decision after the first draft of the description can change it. Then the operator drafts each change of the title or the description that the user asks for. If the milestone of the slug is closed, the operator tells the user and starts no initial planner.The operator writes the description to
/tmp/tasker/<slug>/drafts/milestone.md, and the file holds nothing else. Before it shows the draft, it runs the self-check of the simple-english skill over the file, as Before a post says for a draft. The description holds enough detail for the initial planner to start the drafts of the issues. This judgment is open on purpose, and the user makes it with the approval. The operator posts the title and the description only after the approval of the user.After the approval, the operator runs one of these commands. To create the milestone, it runs
gh api -X POST repos/codeguru42/tasker/milestones -f title='<title>' -F description=@<file>. To change a milestone, it runsgh api -X PATCH repos/codeguru42/tasker/milestones/<number>with-f title='<title>',-F description=@<file>or both.<file>is the path of the draft file. If the command fails, the failure rule applies, and the operator starts no initial planner.For a denied create or change command, the operator follows A denied command of the operator. If the command ran, the operator goes on as after its own command. If the command did not run, the operator starts no initial planner and waits for a new word of the user. On a new word of the user to go on, the operator does step 3 again from its first paragraph.
After the post, the operator starts the initial planner, as "Start a session" says. If the user approves the existing title and description with no change, the operator also starts the initial planner. The approval is the decision of the user that the milestone starts, and the operator needs no other request of the user. If the user rejects the draft or says that the milestone waits, the operator posts nothing and starts no initial planner. Then it waits for a new word of the user. On a new word of the user to go on, the operator does step 3 again from its first paragraph.
-
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. It does not wait in a loop, as the answer-wait rule says for a task subagent. If the send fails, the operator tells the user, as the failure rule says. Then the operator asks the user to tell the initial planner in its tab that the initial planning ends. 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 task subagent 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 task subagent 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 task subagent 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. After the start reads, read the title and the description of the milestone. Then start the drafts of its issues.
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.
Before the operator closes the pane of a session, the session runs the rounds of Session-review rounds and sends the report of the rounds. The operator does steps 2 and 3 only after this report arrives. The wait of step 2 does not replace the report.
After step 1, the operator tells the user one time that it waits for the report of the session. While it waits, it continues its other work and does not wait in a loop. If the user asks before the report arrives, the operator reads the state of the session and tells the user, as A blocked session says. The word of the user then closes the pane with no report, keeps the wait, or ends the stop. After an end of the stop, the entry stays in the tab list.
In steps 1 and 2 of the steps of the old operator, the old operator waits for each such report before its hold messages. That report is the report again, because the old sender ran its rounds before its handoff file. If no report arrives, the old operator tells the user, and the word of the user decides. After the hold messages, the old operator starts no stop of a sender of the slug. The hold of the items defines the hold messages and a sender of the slug.
- The operator sends this message to the session with the tool
SendMessage: "Operator: stop. Run the rounds of docs/dev/session-review-rounds.md, then report." The message is a word of the operator, not a word of the user, so it does not start with "From 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. The operator does
step 2 only after the report of the rounds, so the session already read "stop". For a session at its input prompt, the
wait of step 2 then ends at once, and the operator does step 3. For such a session, a wait right after the send of
"stop" ends before the session reads it. The measures in the next paragraphs show this, and it is the reason for the
wait for the report.
On 2026-10-25, with herdr 0.9.3 and Claude Code 2.1.310, the task subagent 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 close, the order of the end of a milestone in steps 1 to 4. Step 5, the close of the milestone on GitHub, comes after the close. A round and the end of the rounds work as Session-review rounds says. The close starts with no word of the user. It starts when the last ticket of the milestone closes and no task of the milestone is in flight. During the close, no session of the slug starts a handoff, as The suggestion of a handoff says.
The close ends with step 4, at the stop of the orchestrator. For a long-running orchestrator, the close starts only when it moves to the next milestone. It ends at a report of step 3 that names no issue of the milestone. If no orchestrator runs, steps 1 and 4 do not apply, and the operator starts step 2 on the word of the user. Then the close ends with the report of step 3.
-
Before its first rounds of the close, the orchestrator tells the operator in a report that the close started. The orchestrator runs its rounds before its report that the milestone is done, as Pick the tickets says.
-
When the report that the close started arrives, the operator tells the follow-up planner that the close started. With no orchestrator, the operator tells it at the word of the user that starts step 2. This word also takes the place of the report that the milestone is done.
After the report that the milestone is done, the operator runs its own rounds or gives the report again. It does this as Session-review rounds says. It sends each finding 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.
-
After this word of the operator, the follow-up planner waits for the report of the ticket subagent of each draft. Then it handles each finding: it posts the finding, adds it to an open issue or drops it. Then it runs its rounds, or gives the report again. It handles the findings of its own rounds in the same way. The operator runs no new round for them.
When each ticket subagent of these findings reported, the follow-up planner sends the report of the rounds to the operator. The report names each issue that the follow-up planner posted or moved into the milestone in this step. If no operator runs, the user tells the follow-up planner that the close started and when to start step 3. Then the follow-up planner gives the report to the user.
-
Steps 1 to 3 are one pass. If the report of step 3 names an issue of the milestone, the orchestrator runs it, as Pick the tickets says. The next report that the milestone is done starts the next pass. In the next pass, a session that did no work after its rounds runs no new round, as Session-review rounds says.
If the report of step 3 names no issue of the milestone, the operator stops the orchestrator, as "Stop a session" says. If no follow-up planner runs, the end of the rounds of step 2 replaces the report of step 3. This step is the request of the user for this stop. The operator needs no new word of the user.
A long-running orchestrator that moves to the next milestone does not stop. A draft that still waits for a decision of the user does not hold this stop. The milestone of an issue gives the milestone of its issue.
-
The user says when to close the milestone on GitHub. After the end of the close, the operator relays this word to the follow-up planner. If the word comes earlier, the operator tells the user that it waits for the end of the close. 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.
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 follows A denied command of the operator, and after the
command ran, it starts the orchestrator.
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, steps 1 and 2 of the steps of the old operator say 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 | It runs the rounds or gives the report again, as Session-review rounds says. It sends the report of the rounds, and its pane closes | Waits for the report, then for idle or done, then closes the pane |
The operator tells the old session "stop". The old session tells the operator, with the report of the rounds | |
| 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 |
This section covers a tool block of two commands that the operator runs itself. They are the create or change command of step 3 of The order of a milestone and the removal of the label "on hold" in The close of a milestone. A denied command of an item of another session stays with the tool-block rule, apart from the item of the last paragraph of this section.
If the user declined the call at a permission prompt, the command did not run. Then the operator gives no choices and waits for a new word of the user.
After a check failure, the operator first runs the same call again one time, in the same form, as the tool-block rule says for the orchestrator. If the try runs, the operator goes on. If the try also gets a check failure, the next paragraph applies. The operator then also gives the user the link https://status.claude.com/.
For each other denial, the operator tells the user the exact command and the folder of the call. It also gives the exact text of the denial, the purpose of the command and its step. Then it gives the user these choices, in this order:
- The user tells the operator to run the denied command again, in the same form or in another form that the user names.
- The user tells another session directly, in that session, to run the command. The user picks that session, for example a session with no role.
- The user runs the exact command in the folder of the call, with the
!prefix of Claude Code.
Only the user picks a choice. The recommendation mode of
A decision of the user does not cover this choice, as that section
says for a tool block. The recommendation of the operator is the first or the second choice, never the ! run. The
reason: the user prefers to run no command with the ! prefix. Which of these two choices the operator recommends is
open on purpose, and the operator judges it. Until the word of the user, the operator runs the denied command in no
other form, and no part of it.
For the first choice, the operator runs the command one time, in the form that the word of the user gives. A word such as "try again" gives no form, so the operator runs the same command in the same form. A new denial of that run starts this section again. If that run fails in another way, the failure case of the step applies.
The operator never asks another session to run the denied command, or a part of it. It also relays no word of the user to another session to run it. Permission laundering is the run of a denied command by another session on a relayed request. That run uses the permissions of the other session in place of the denial. As a result, the operator starts no permission laundering. A word of the user that a session relays answers no permission prompt.
For the second choice, the operator tells the user to give the word in the other session itself. It gives the user the exact command and the folder of the call for that session, and it sends that session nothing. As a result, the other session runs the command only on the direct word of the user, under its own permission rules. This paragraph and the paragraph that starts "The operator never asks another session" win over The word of the user. They also win over its sentence that starts "The operator does not ask the user to relay". The other session runs the command only when its own rules permit it.
A denial in the other session is a tool block of that session, and the step of the operator stays open. For the second and the third choice, the user tells the operator whether the command ran. Until then, the step stays open, and the operator continues its other work. It does not wait in a loop, as the answer-wait rule says for a task subagent.
The other session can send this operator an item about its denial, for example a follow-up planner of the slug. Then the result of the second choice is "did not run", and the operator tells the user that result. For such an item, this paragraph wins over the item steps of the tool-block rule. After the result "did not run", the operator waits for a new word of the user. A word that picks a choice of this section starts that choice. In step 3 of "The order of a milestone", such a word is not a new word of the user to go on.
- 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.