team add

Starts one seat of a running team: a seat the file declares but nothing runs for — team up started the others — or a temporary seat beside the team, which the file does not declare at all. A declared seat the file marks stopped: true, or that team remove took out, is put back from the approved copy first. It never touches the seats that are already running.

The one seat starts where up would start it: every seat starts in the machine lobby ~/.config/team/lobby outside repositories, verified by the lobby gate, unless --worktree names the worktree it works in. add makes the lobby when it needs it, and refuses the same folders up refuses, before it writes anything.

Synopsis

team add <name> [--dry-run] [--session <name>] [--file <path>]
team add --temporary --like <seat> --until <result:path|merged:branch> [--worktree <task>]
         [--dry-run] [--session <name>] [--file <path>]

What it reads and writes

Reads the team file (or the one --file names), this machine's approval store (the record, and the approved copy the seat is put back from), the session's state (.agents/team.state.json, including the project's stored budget readings), herdr (the session's state, its agents and their workspaces), and, like up: the doctor's findings and the machine's load, free memory, free disk and free swap.

Writes the team file when the seat has to be put back into it, .agents/team.state.json (the seat's stage, pane and workspace; a seat stopped at a dialog's waiting-owner record, with the process identity read at that moment; a temporary seat's entry), .agents/team.log, the store's ledger of seats the team has had, and, through herdr: the workspace, the launch, and the name.

Who may run it

The owner, the coordinator's seat and the operator's seat. The seat is that name's, in a session this project's state records — the file's session, or one the state records the caller's pane in — on the pane the state records for that name in that session: a seat of another session, or a pane merely renamed to the coordinator's or the operator's name, is refused — and so is a seat the state records no pane for, or records on another pane than this call is on; those two refusals name the seat and the repair. What that proves is placement, and no more: the state file is in the project, and a process of the same user that writes its own pane there under the coordinator's name, and renames its pane, passes. The check guards a mistaken agent, not a hostile process running as the same user. --file and --session are the owner's alone, from a terminal outside herdr: a non-owner aiming either is refused before the flagged file or session is read at all. Everything up refuses on — a file that is not the approved one, a MISS finding from team doctor, the machine past its limits, the approval's ceilings — refuses here too, for the one seat being started.

Flags

FlagMeaning
--temporarystart a seat the file does not declare, beside the team
--like <seat>the approved seat a temporary seat is a copy of: its CLI, model and launch
--until <end>what the temporary seat works for: result:<path> (a file it writes, relative to the project) or merged:<branch> (a branch merged into the base)
--worktree <task>the worktree the temporary seat is started in, instead of the seat's own cwd
--session <name>the herdr session, instead of team.session; the owner's alone
--dry-runprint whether this seat would launch or be refused, and exit 0; nothing is written
--file <path>the team file, instead of .agents/team.yaml; the owner's alone
--help, -hthe usage, and exit 0

The temporary seat's name is the --like seat's, with -tmp-<n>: the first n that is free.

What it prints

claude-keeper: ready

A seat that reaches its idle prompt with its rules delivered prints <seat>: ready; the temporary seat prints <name>: ready under the name it was given. A seat the state records whose pane no longer holds the process team launched is not already running: its workspace is closed without input and the seat is launched fresh, with the line up prints for it. Everything else a launch can print is the same as up's, with the seat's name in front: one record per seat — <name>: ready, or <name>: left out: <what stopped it> — and the detail that explains it written to stderr under the record, exactly as up's table reads: left out: its workspace was not created; left at launched, left out: timeout, left out: its pane has been back at its shell for <n> s and shows no CLI prompt; left at launched, left out: permission, left out: vendor notice, and the rest of the table on the team up page. A dialog is never answered and never asked about: add keeps the seat's workspace, records waiting-owner with the classification and the process identity read at that moment, sends no input, and reports <name>: left out: <classification> — trust under either policy, permission, question or vendor notice. The seat is the owner's to finish then: a later team up reuses the recorded pane and enters the pause on the team up page. A seat whose idle wait runs out keeps the ordinary timeout record and the detail under it. A wait that ended without a prompt writes the pane's last lines under the record, on stderr, as up does. The log file gets each record's line alone, once per final record: its reason in words, never a folder the run resolved, a pane's text or a file's content — the detail under the record is stderr's alone. First-message rules go to the seat's file in the project state folder and arrive as the one line up types, read back and entered, exactly as that page describes. A seat add leaves out takes its rules file with it; a temporary seat removed with team remove or stopped by team down loses its file with the seat, while a declared seat's file stays for the next up.

The seat's own launch line is checked where it will start, before the file is edited and before any workspace is made. A note — a word of the line quotes or substitutes text, or a relative argument is not there yet — is said once as note <name>: <why>, as up says it. A line that can't run is refused in the same paragraph as any other MISS finding: team add: <name>: its launch line starts \`, which is not on the PATH, or team add: : its launch line runs ``, not found from its start folder ; the same file is at `` from the project root — write that path— and the file is left alone. A seat thisaddadopts into a pane that is already there — its state names the workspace, and an unnamed pane is in it — runs nothing now: its line is checked where that pane runs when the state records the folder it was started in, and otherwise is not checked at all, the note saying so, and a miss found there is never refused. A--dry-runprints a refusal in the plan instead — skip : would refuse: …abovedry run: nothing was run` — makes nothing and exits 0.

Refusals

MessageExit
team add: unknown option --x / team add: a seat name is required / team add: --like needs a value (each with the usage)2
team add: --like, --until and --worktree are for --temporary2
team add: line <n>: <message> / team add: <message>2
team add: only the owner, the coordinator or the operator runs it; this call is <caller>1
team add: --file is the owner's, from a terminal outside herdr; this call is <caller>1
team add: --session is the owner's, from a terminal outside herdr; this call is <caller>1
team add: no pane is recorded for seat <name> in this session: the owner stops that seat and runs `team up` 1
team add: the state records pane <pane> for seat <name> in this session, not the pane this call is on: the owner stops the team and starts it again (`team down`, then `team up`) 1
team add: the file was never approved on this machine: run `team approve` 1
team add: approved before records were signed: run `team approve` once — the record was written by an earlier team; the same line, with the case, for a record that does not verify1
team add: the file is not the approved one (<differences>): run `team approve` 1
team add: the approved copy can't be read: run `team approve` 1
team add: the approved file has no seat "<name>"1
team add: <name> is already running1
team add: no launch profile for \` in this version`1
team add: the approval allows <n> seats; <m> would be running / … <n> temporary seats; … / … <n> <vendor> seats; …1
team add: refused: <account> <window> left <n>%, inside its <reserve>% reserve, changed <age> ago; accounts with room: <accounts>1
team add: refused: <account> spend <amount> <CUR>, at or below its <floor> <CUR> floor, read <age> ago; accounts with room: <accounts>1
team add: session can't be "default", herdr's own session1
team add: herdr doesn't answer1
team add: session <session> is stopped; clear it with `herdr session delete <session>` 1
team add: session <session> runs, and its agents can't be read1
team add: --until is result:<path> or merged:<branch>1
team add: a result path is relative to the project / team add: <path> already exists1
team add: workspace.base is required to read a merged end / team add: branch <branch> doesn't exist1
team add: no worktree named "<task>" is recorded1
team add: worktree <task> has a failed setup; team worktree remove <task>1
team add: the file is legacy: migrate trust to absolute paths including the lobby ~/.config/team/lobby: ...1
team add: trust: must list the lobby ~/.config/team/lobby1
team add: the lobby ~/.config/team/lobby: <check>1
team add: seat beacon-qa would start in live, inside the protected checkout live; a seat that isn't `mode: shared` never starts in one1
team add: the file changed while add was checking; nothing was written1
team add: the load is <n> per core, above <n> / team add: free memory is <n>%, below <n>%1
a MISS finding from team doctor, the seat's own launch line among them1

The ceilings are read from the approval's record, never from the file: a seat that would put the session past limits.seats, past limits.temporary for a temporary seat, or past a limits.vendors entry for its vendor, is refused.

Exit codes

  • 0 — the seat reached its idle prompt and is ready; or the temporary seat is; or --dry-run printed its plan, a would-be refusal included.
  • 1 — the run was refused, or the seat was left behind at some stage of its launch.
  • 2 — the invocation or the team file can't be read.

Examples

.agents/team.yaml
format: 1
project: beacon
coordinator: claude-keeper
operator: claude-keeper

trust:
  - ~/.config/team/lobby
  - ~/Code/beacon

workspace:
  mode: shared

seats:
  - role: coordinator
    name: claude-keeper
    label: coordinator
    cli: claude-code
    vendor: anthropic
    model: Claude Opus
    version: "5.5"
    launch: claude --model claude-opus-5-5

  - role: implementer
    name: claude-beacon
    label: implementer
    cli: claude-code
    vendor: anthropic
    model: Claude Opus
    version: "5.5"
    launch: claude --model claude-opus-5-5

Only the implementer is up. A pane made by hand and renamed claude-keeper is not the coordinator: the state records no pane for the seat, so nothing tells that shell apart from the seat, and every command that changes the team refuses it, naming the seat and the repair only the owner can make — the hand-started pane is closed, and team up starts the seat with a pane of its own:

Terminal
 team add claude-beacon ; echo "exit $?"
team add: no pane is recorded for seat claude-keeper in this session: the owner stops that seat and runs `team up`
exit 1

The coordinator's seat is in the file and nothing runs for it, which is team status's missing and the repair it names:

Terminal
 team add claude-keeper ; echo "exit $?"
claude-keeper: ready
exit 0

The seat that is already running is never touched, and adding it is refused:

Terminal
 team add claude-beacon ; echo "exit $?"
team add: claude-beacon is already running
exit 1

An implementer's seat is neither the coordinator's nor the operator's, so it adds nothing — not even itself:

Terminal
 team add claude-keeper ; echo "exit $?"
team add: only the owner, the coordinator or the operator runs it; this call is claude-beacon
exit 1

team remove --keep leaves a seat in the file marked stopped, and add is what clears the mark and starts it again:

Terminal
 team remove claude-beacon --keep ; echo "exit $?"
claude-beacon: stopped
stopped claude-beacon
exit 0
 team add claude-beacon ; echo "exit $?"
claude-beacon: ready
exit 0

A temporary seat is not in the file at all. It is a copy of an approved seat — here the implementer's — and it works until a result exists or a branch is merged:

Terminal
 team add --temporary --like claude-beacon --until result:notes/result.md ; echo "exit $?"
claude-beacon-tmp-1: ready
exit 0

The seat's name must be one the approved file has, so a name that is not is refused before herdr is asked anything:

Terminal
 team add scratch ; echo "exit $?"
team add: the approved file has no seat "scratch"
exit 1
 team add --temporary --like claude-beacon --until later ; echo "exit $?"
team add: --until is result:<path> or merged:<branch>
exit 1

The flags that describe a temporary seat are refused without --temporary, and a name is required outside it. Both stop at the usage line, before the file is read:

Terminal
 team add claude-keeper --like claude-beacon ; echo "exit $?"
team add: --like, --until and --worktree are for --temporary
exit 2
 team add ; echo "exit $?"
team add: a seat name is required
Usage: team add <name> [--dry-run] [--session <name>] [--file <path>]
       team add --temporary --like <seat> --until <result:path|merged:branch> [--worktree <task>] [--dry-run] [--session <name>] [--file <path>]
exit 2