The records

Part of team up — every line a run prints, and what each line means.

A seat already ready

A seat already ready is left as it is, and the words it was left with are the detail under its record. A ready seat the state records without its process identity, or with a start outside the machine lobby, is left as it is too — only a relaunch repairs either — and its detail says so: already ready; left as it is; a relaunch records its process: team remove <seat> --keep, then team add <seat> (or team down, then team up, for the whole team), or the same words saying a relaunch moves it into the lobby. For a seat the file names as orchestrator or operator, the detail offers only team down, then team up (to restart the whole team).

The file it reads, and the watch

A command run in a folder that is not a git repository reads .agents/team.yaml in that folder, and nowhere above it. A link at .agents or at team.yaml is not followed. When --file names a file a watch started in the project folder would not read, up starts no watch and prints this instead of watch: started, and exits 1 even when every seat is ready:

watch: not started: a watch started there could not read this file, or would read another one under this session's name. Move the file to .agents/team.yaml in the folder the watch starts in. A fuller repair is planned.

A stale pane

A seat is its pane only while the process team launched is still in it. A seat the state records whose pane no longer holds that process is not "already ready": its workspace is closed without a key and without input — its pane runs no CLI, or one team did not launch, and nothing in it is the seat — and the seat is launched fresh with its rules. Its record is <seat>: ready, and the detail under it says both: its pane held no CLI; closed without input and launched again when the pane was back at its shell, its pane held a process team did not launch; closed without input and launched again when another process held it.

The close, and its proof

The reading that decides the repair is taken while the plan is built, and the close can be minutes later, after every seat before this one was created, waited for and delivered to. up reads herdr again immediately before it, with nothing between those reads and the close, and closes only a pane that is provably still the seat's stale one: the agent list must name this seat on the recorded pane, that pane's workspace as herdr reports it now must be the recorded workspace, that workspace must hold no other panes (named or not), the process reading must still say the pane runs no CLI or another process, and a pane held by another process must not read working or unsent — a process that is working or holds unsent text is never closed by up, whoever started it. When any of that does not hold, nothing is closed, nothing is launched for that seat, its state is left as it is, the seat is out of the run, and up exits 1 with one record:

RecordWhen
<seat>: left out: its pane is the seat's again; left as it isthe pane now holds the recorded process — its owner restarted the CLI between the plan and the close. Not an error: the seat is skipped as ready and the exit code is unaffected
<seat>: left out: herdr no longer shows this seat on its recorded pane; nothing closed; run team statusthe agent list no longer names the seat on the recorded pane, or that pane's workspace is no longer the recorded one
<seat>: left out: its workspace holds other panes; nothing closed (close its pane there, then run team up)herdr's pane listing for the recorded workspace holds more than the seat's recorded pane
<seat>: left out: the process in its pane is working; nothing closed (stop it there, or run team remove <seat>)a process team did not launch holds the pane and its screen reads working
<seat>: left out: the process in its pane holds unsent text; nothing closed (send or clear it there, or run team remove <seat>)a process team did not launch holds the pane and its screen reads unsent
<seat>: left out: its pane could not be read; nothing closedherdr can't give the workspace's panes, the pane's processes, or its screen reads as nothing this version knows

Wrong in the safe direction

This comparison is wrong in the safe direction, but it is wrong: a CLI that replaces its own process — an updater that re-executes, a wrapper that hands over — changes the foreground pids, and the seat then reads restored, not launched by team although nothing was restored. up closes that pane without input and launches the seat fresh, and the conversation in it is lost. The record holds every pid the launch left in front, and accepts the seat while any recorded pid is still there; no reading of a CLI after its first message was taken when this record was made, though a review's runs of a CLI with a helper beside it and of one behind a pipeline kept every pid. Before running up, the owner looks: team status names such a seat, and the pane itself says whether the CLI there is the one team launched.

A workspace that does not close

When the workspace does not close, nothing is launched in its place and the seat is left as it is: <seat>: left out: its workspace did not close; left as it is, with up exiting 1. A seat with no launched record in state (one launched before this version recorded process identity, or one whose process herdr could not read when it stopped: the reading is taken once, after the idle prompt and after the launch model check) keeps today's behaviour: nothing checks its pane.

A screen that names no model

When the idle screen names no model this version can read, up writes <seat>: its screen doesn't show a model this version knows; not checked to stderr, after the seat's record, and continues. It is detail, not a record: the log keeps the seat's one line, the record ready. Nothing is assumed about which model is running.

Rules as a first message

A seat whose rules travel as a first message (codex, cursor, antigravity) gets them from a file: up writes the approved rules text to <project state folder>/rules/<seat>.md — owner-only, mode 0600, beside the approval store, never inside a worktree, the lobby or the project — and types one line into the pane at an empty idle prompt:

Read /home/owner/.config/team/demo-3f9c2a8e1d7b/rules/implementer.md (sha256 5e1d0a9c4b2f): your standing rules for this session; reply ready and wait for your brief.

The state folder is resolved from the approved copy's project name, through the one resolver the writer, the line, the hash check before Enter, status, doctor and remove all use: a project rename is not approval drift, and the line a seat is told to obey must name the file the checks look at, not one resolved from the live file's new name.

The line carries the file's absolute path and the first 12 hex digits of its SHA-256, so the seat — and the read-back — can tell exactly which text is meant. A path that holds whitespace or a character outside letters, digits and . _ / @ + - is never typed: nothing is quoted or escaped, and the report says the path can't be typed safely. The line is read back row by row — every visible character in order, a row break allowed to stand for at most one space or nothing — and only then is Enter pressed, once. A box that already holds exactly today's line (a run that stopped after typing it) is verified and sent, never typed onto again. up never clears a box it could not verify: a stop leaves the text where it is.

A seat that does not get there

A seat that doesn't get there prints its one record with what stopped it, and up exits 1. For a seat whose wait ended without a prompt, the record is <seat>: left out: timeout (or the ended-at-its-shell reason below), and the detail under the record, on stderr, carries the last non-empty lines the pane showed — the launch line's own echo first when it is within reach, six at most, every escape sequence and every control character but the line breaks removed, each line cut to 200 characters with … — each on |; run \team up` again to resume itnames how the seat is finished. A wait that runs out with the owner at a terminal is not left there: it meets the pause astimeoutand is the owner's to open, skip or stop (above); the recorded timeout and the detail above are what a run without a terminal keeps. Those lines go to the terminal only: the log file gets the record, never the screen's text. The same holds for every folder the run resolves: a refusal's record holds the finding in words — the start folder a launch line was looked for in, the lobby a failed confirmation names — and the sentence with the folder is written under the record, on stderr, for this terminal alone. One line per final record, in file order, plus the watch's own line —watch: started, or watch:` and its reason in words when it did not — is the whole of what the log gets: a defect in the seat's file, or a note like the unread-model one below, is stderr detail and never a log line.

The pause that paragraph's above names is on the waiting page.

Every line, and its meaning

Rows that name the pause, above, the same proof as every close above, or an agent team cannot name (above) mean the waiting page's own: the pause, the close it proves, and the owner's by-hand answer.

The records are what stdout gets; a line below that is not a record is written to stderr, after the record it belongs to, as its meaning says.

LineMeaning
<seat>: left out: trust (no terminal for owner)the CLI asked whether to trust the folder, and up never answers one; the run had no terminal to ask on, and the detail under the record says its workspace was closed without an answer and the seat left out. A run at a terminal asks its owner instead — the pause, above. A delegated run closes without input like this one, whatever its stdin
<seat>: left out: permission (no terminal for owner) / <seat>: left out: question (no terminal for owner)a permission dialog, or a question, was left for its owner to answer; the run had no terminal to ask on, and the detail under the record says its workspace was closed without input and the seat left out. A run at a terminal asks its owner instead; a delegated run closes without input like this one, whatever its stdin
<seat>: left out: vendor notice (no terminal for owner)the CLI shows a vendor notice its owner has to act on: a screen its profile captured as one — today, Codex 0.157.0's update screen. team never answers one, and treats it at least as strictly as a question: nothing is typed, the watch reports it, and delivery stops on it. The detail under the record says its workspace was closed without input and the seat left out, and adds untested on <version> when the CLI installed here is outside the range the notice was captured on — the reading itself is never made less cautious by a version. A run at a terminal asks its owner instead; a delegated run closes without input like this one, whatever its stdin
<seat>: waiting for owner (<classification>)the provisional record drawn while the seat's owner is asked, at a terminal (the pause, above); it is rewritten in place when the classification changes, and is not the seat's final record
<seat>: left out: skipped by ownerthe owner pressed [s]: the seat's workspace was closed without input and its launch state cleared, the waiting record with it
<seat>: left out: stopped cleanlythe owner pressed [q] (or Ctrl-C) at this or an earlier seat, and this seat had no final record: a workspace this invocation created for it — not already ready or closed — was closed without input and its state cleared
<seat>: left out: its waiting pane is gone / <seat>: left out: its waiting pane holds another processa later up found the seat recorded waiting and its recorded pane no longer exists, or no longer holds the recorded process: nothing was created, nothing closed, the record kept; the repair under the record on stderr is the team file's own for this seat — team down then team up (to restart the whole team) for an orchestrator or operator, team remove <seat> --keep then team add <seat> (or the whole team) for any other — and the owner answers or closes by hand first when the seat sits at a dialog, or its pane holds an agent team cannot name (above)
<seat>: left out: <reading>; its workspace did not close; left as it isthe close of that workspace failed, with <reading> one of trust, permission, question or vendor notice — a run without a terminal closing it, or the pause's own close at the prompt: nothing claims it was closed, and the seat is left exactly as it was, its state kept — a later up resumes it
<seat>: left out: runs <model> <version>; the file says <model> <version>; left at launched, not named. Add <flag> <id> to its launch, or correct the file's model and version and run team approvethe idle screen shows a different model than the file. The seat is not renamed and gets no rules; its pane stays open. The flag is that CLI's model flag, and the id is the one the profile maps to the file's model. When the profile knows no id, the line says <id>
<seat>: left out: its pane has been back at its shell for <n> s and shows no CLI prompt; left at launchedherdr's process info says the pane's foreground program is back at its shell through three full polls on end, four readings, the screen matches no CLI shape, and the launch line's own echo is visible on the screen. A pane read before the line arrived, one whose echo scrolled away, one whose program is slow to draw, or a herdr that can't say (no shell process info), is waited out to the deadline — the end is never inferred from the screen's text, and a single reading can never reach the three polls. The workspace is kept, and the pane's last lines follow on stderr, under the record
<seat>: left out: timeoutthe prompt never came within the profile's own time limit; the detail under the record, on stderr, says after how long it waited, the screen it last read, the pane's last lines, and that team up again resumes it
<seat>: left out: timeout (no terminal for owner)a delegated run's idle wait ran out: for it the timeout is a dialog like any other stop, so the workspace is closed without input — the same proof as every close above — and the seat left out. The owner at a terminal is asked instead (the pause, above), and the owner without one keeps the plain timeout record above
<seat>: left out: was not in the agent list in time; left at launchedherdr listed no agent in the pane to name
<seat>: left out: rules not typed: the folder that would hold its rules file is <what>; the owner removes or repairs it, then runs up againa folder from team's per-user state root down to rules/ is not a real directory of this user's — a symbolic link, not a directory, another user's, or (for rules/ and the project folder) a mode wider than 0700, never chmod'd closer; <what> says which. Nothing was written, nothing typed
<seat>: left out: rules not typed: its rules file's place holds <what>; the owner removes it, then runs up againthe final name holds anything other than this user's 0600 regular file — a symbolic link, a FIFO, a directory, a wider mode, another owner — and is never replaced; <what> says what is there
<seat>: left out: rules not typed: its rules file did not read back as written; check the project state folder, then run up againthe write landed but did not read back (no-follow) as the hash the line carries; nothing was typed
<seat>: left out: rules not typed: its rules file could not be written; check the project state folder, then run up againthe write failed; nothing was typed
<seat>: left out: rules not typed: its rules file's path can't be typed safely: the read-back can't prove a path outside letters, digits and . _ / @ + -; rename the seat or move the project, then run up againthe path holds a character the read-back cannot prove; nothing was typed, nothing quoted
<seat>: left out: rules not typed: the CLI never appeared as its pane's foreground process (the screen read <kind>); check the seat's launch line — the wrapper it starts through, or the command itself — then run up againwithin the profile's own time limit the pane's foreground process was never the CLI — a wrapper's shell still in front of it, or a launch line that exited. A CLI that starts through a wrapper is waited out, its first frame included; nothing is typed into the wrapper's shell
<seat>: left out: rules not confirmed: the seat is working; run up again when it is idlethe seat is mid-turn; nothing was typed
<seat>: left out: rules not typed: <what stopped it>; <what to do>nothing was typed: the screen was not an empty idle prompt, or the box already held text that is not the rules line. A box that already holds exactly this CLI's exit text — an earlier down typed it and never confirmed it — is its own line: the text is named and the owner is told the one key that empties it (press Ctrl+U in its pane to clear it, then run up again), or, on a CLI with no such key, to clear the box in its pane
<seat>: left out: rules typed, not sent: the read-back didn't match; the line sits in its box, unsent: <what to do>the line was typed and its box did not read back as the line; up does not clear it. When the box drew a row that is not the line's own, the first such row is written under the record on stderr, stripped of control characters and cut to 200 characters
<seat>: left out: rules typed, not sent: the rules file changed after it was writtenthe line read back, but the file — read again without following a link, directly before Enter — no longer held the text whose hash the line names. Enter was not pressed; the line sits in the box, unsent, and the owner checks the project state folder before running up again
<seat>: left out: rules typed, not sent: its box still holds the line after Enter; <what to do>Enter was pressed and the box still shows the line: the key did not take, and nothing was sent
<seat>: left out: the seat did not come back to its idle prompt (<reading>); <what to do>the line was submitted and the seat never came back to its idle prompt — a running turn, or a dialog to answer
<seat>: left out: its rules were not delivered; left at namedthe pane could not be read at all; nothing was typed
<seat>: left out: its workspace did not close; left as it isits pane no longer held the process team launched, and closing that workspace failed, so nothing was launched in its place
<seat>: left out: its workspace was not created; left at launchedherdr made no workspace for it
<seat>: left out: its launch command did not run; left at launchedthe pane took no command
<seat>: left out: the approval allows 3 seats; 4 would be runningthe approval's ceiling, from the record, not the file
<seat>: left out: refused: <account> <window> left <n>%, inside its <reserve>% reserve, changed <age> ago; accounts with room: <accounts>a counted reading is inside that account's reserve and this run would launch the seat; this seat is not started, and the others still are. accounts with room: none when no other account has room
<seat>: left out: refused: <account> spend <amount> <CUR>, at or below its <floor> <CUR> floor, read <age> ago; accounts with room: <accounts>the money its check counted is at or below the account's floor, and this run would launch the seat; this seat is not started, and the others still are
<seat>: left out: refused: its launch line starts , which is not on the PATH / …, which does not exist / …, which is not executable / …, not found from ~`` / …, not found from its start folderthe program the line starts is missing where the seat starts — the lobby for a seat that works in worktrees. When the check looked in a folder it resolved, the record's reason stops at …, not found from its start folder and the sentence naming the folder is written under the record, on stderr. A first word that is one fully quoted literal ("claude", 'zcash') is checked with the quotes removed, the way a shell would run it, and <word> is the word as written, quotes and all — a quoted "~/x" is a pathname with a literal ~ folder, resolved from the start folder, never the home. A path written as the program is read as main's launcher check read it: it must be there and executable, not merely there. This seat is not started, and the others still are
<seat>: left out: refused: its launch line runs , not found from its start folderthe line runs a shell — sh, bash or zsh, by name or by path — whose first argument is a relative script path, not an option, and that script resolves from the project root but not from the folder the line will run in: the shell exits 127 without starting anything. The record's reason stops at …, not found from its start folder; the sentence naming the folder it looked in and the absolute path to write is written under the record, on stderr. This seat is not started, and the others still are
<seat>: <account> <window> left <n>%, inside its <reserve>% reserve, changed <age> ago; accounts with room: <accounts>the same reading, and the seat is already running; setup continues and the line is only a notice — on a real run it is written to stderr, under the seat's record
<seat>: <account> spend <amount> <CUR>, at or below its <floor> <CUR> floor, read <age> ago; accounts with room: <accounts>the same money reading, and the seat is already running; setup continues and the line is only a notice — on a real run it is written to stderr, under the seat's record
<seat>: <account> is unknownthe account is in the file and its figure is unknown — a subscription with no counted reading, or a spend account whose money reading is missing, older than budgets.stale_after, or in another currency than the floor's. A subscription figure with no known reset is unknown while it could still matter — inside its reserve, or within the reserve again outside it; further out — more than the reserve again — it counts, the room the figure last held, and the launch decision is clear: no unknown line. The seat still starts, and the line is a notice written to stderr, under the seat's record, on a real run
<seat>: <account>: first sight only, not yet countedthe account's only readings are unconfirmed; the seat still starts, and the line is a notice written to stderr, under the seat's record, on a real run

The version placeholder

A version: "0" — exactly that literal, the placeholder a fresh init writes before the owner fills the release number — is read as no version declared: the seat's first launch is not stopped by it, and the model family is still compared, so a file whose model names a family the CLI does not run stops as above. No other spelling is read that way: version: "0.0" stops the launch as any mismatch would. team doctor says the placeholder as a note — what the seat really runs, and the one edit that pins it.

The account in a line

<account> in these lines is the seat's own account: its account: when the file names one, its vendor when it doesn't.

Notes on a launch line

A launch line the check can't be sure of is never refused: it is printed as note <seat>: <why> and the seat starts. A note says the line was not checked — a word of it quotes or substitutes text this version does not read, or its first word names no command — or that a relative path is not there yet, not checked: the command may create it; that is every relative argument but the shell's script path above — option text, an output path, a path the command creates, one a launcher changes directory for — and a ~/… that is not there now is said the same way. A seat resumed into an existing pane is checked where that pane runs when the state records the folder it was started in; without that record its launch line is not checked at all and the note says so — the file's folder is not where that pane is. The notes are said once: on stderr on a real run, on stdout with the plan on a dry one, as team doctor says them. A miss is not a note: on a dry run it leaves the seat out as skip <seat>: would refuse: ….

The dry-run plan

The plan a --dry-run prints is also what team down --dry-run prints: + <command> for a command that would run, a (<note>) line under one that carries a note, wait <text> for a wait, skip <text> for a seat left out, and dry run: nothing was run at the end. A seat a stored reading would refuse, and that this run would launch, is skip <seat>: would refuse: … and is not in the commands — the same line a seat whose launch line can't run where it starts is left out with, before its workspace is made. The words after would refuse: are the same as the words after refused:. A seat already running keeps its setup, with that reading in a note. A seat whose account is unknown carries (<account> is unknown; would launch) under its first command. A first sight carries (<account>: first sight only, not yet counted; would launch). A launch carries the seat's rules when they travel as a launch option (claude-code), so its line is long; a first-message seat's launch is the plain CLI command, and the plan shows the one line it types at the seat's idle prompt, with the note that the rules go to a per-seat file in the project state folder first.