The team file
A documented subset of YAML, read by the library's own parser: maps, lists, one-line { } and
[ ], plain and quoted values, comments. Anchors, aliases, tags, block scalars, several documents
in one file and duplicate keys are refused, with the line number. The file starts with format: 1.
The package ships the JSON Schema at schema/team.schema.json, and team init writes a # yaml-language-server: $schema=… line at the top of the file so editors validate it.
By example:
format: 1 # the only format this version reads
project: hello
coordinator: coordinator # the seat that dispatches work
operator: coordinator # the seat the watch reports to
identity:
signature:
commits:
position: trailer # last-line | trailer | anywhere
exempt: [merge] # merge commits need no signature
rules: # lines added to every seat's rules at launch. Rules delivered as a launch
# option (claude-code) close with "These are standing rules, not a task.";
# rules typed as a first message (codex, cursor, antigravity) close with
# "These are standing rules, not a task: reply ready and wait for your brief."
- Run the tests your change touches, not the whole suite.
workspace:
mode: shared # shared | worktree: the default for every seat
seats:
- role: coordinator
name: coordinator
cli: claude-code # the launch profile
vendor: anthropic # the model's maker
model: Claude Opus # the model's name, without its version
version: "5.5" # the release alone, quoted
launch: claude --model claude-opus-5-5 # no approval flags: the profile adds them
- role: implementer
name: implementer
cli: codex
vendor: openai
account: openai-hello # the seat's account, when one vendor has two; absent, its vendor
model: GPT Sol
version: "6"
display: GPT-6 Sol # the vendor's spelling, for the signature
launch: codex -m gpt-6-sol -c model_reasoning_effort=high
parked: true # running, and not reported while idle
- role: implementer
name: implementer-deepseek
cli: claude-code # DeepSeek's model, run by Claude Code
vendor: deepseek
model: DeepSeek Flash
version: "V4.1"
display: DeepSeek V4.1 Flash
launch: team-deepseek # a launcher on the PATH, holding the account's key and endpoint
count: 2 # implementer-deepseek and implementer-deepseek-2
- role: reviewer
name: reviewer
cli: grok
vendor: xai
model: Grok
version: "4.7"
launch: grok --model grok-4.7
stopped: true # kept in the file; `up` doesn't start it
budgets: # the owner's: reserve or floor per account, marks, freshness
accounts:
openai-hello: # the account implementer spends
kind: subscription
reserve: 10% # refuse a launch on a figure inside it
sources: [status_line] # the figure comes off Codex's status lineThe head
format: 1coordinatorandoperatorname seats: the coordinator dispatches work, the operator receives the watch's reports and nudges.
sessionnames the herdr session and defaults toproject;--sessionoverrides it.
identity
identity:
signature:
commits:
position: trailer # last-line | trailer | anywhere
exempt: [merge]
pull_requests:
position: last-line
template: "**Agent:** {display} · {role}"identity.signatureis the rulecheckenforces: a template, where it must stand, and which commits are exempt. Commit signatures readAgent: {display} · {role}, pull request bodies**Agent:** {display} · {role}. Withoutdisplay, the signature reads "model version"; with it, the vendor's own spelling.identity.sinceskips an older history,identity.humanslists commit authors who don't sign, andidentity.forbiddenadds to the defaults —^Claude-Session:lines and session links are always refused.
workspace
workspace:
mode: shared # every seat works in the project rootworkspace.modeisshared(every seat in the project) orworktree(each task in its own checkout, withpath,baseandsetup). Underworktree, a seat that isn'tmode: sharedstarts in the lobby — the parent ofworkspace.pathwith.lobbybeside the worktrees, insidetrustand outside every protected checkout — never in the project root;upandaddrefuse a seat whose folder, lobby included, would be protected or untrusted.
machine
machine: # checked before each seat is launched, and by the watch
load_start: 2.0 # 1-minute load per core above which `up` and `add` refuse
load_max: 8.0 # per core, above which the watch reports
memory_start: 30% # free memory below which `up` and `add` refuse
memory_min: 10% # below which the watch reports
disk_min: 20GB # free on the project's volume; both refuse and reportseats
seats:
- role: coordinator
name: claude-keeper
label: coordinator
cli: claude-code
vendor: anthropic
model: Claude Nova
version: "2"
launch: claude
- role: operator
name: claude-signal
label: operator
cli: claude-code
vendor: anthropic
model: Claude Nova
version: "2"
launch: claude
- role: implementer
name: codex-beacon
label: codex
cli: codex
vendor: openai
account: openai-team # the seat's account, when one vendor has two; absent, its vendor
model: GPT Comet
version: "3"
display: GPT-3 Comet # the vendor's spelling, for the signature
launch: codex -m gpt-comet-3
parked: true # running, and not reported while idle
- role: implementer
name: cursor-beacon
label: cursor
cli: cursor
vendor: meridian
model: Meridian
version: "1"
launch: cursor-agent # the model is chosen inside Cursor
# three seats from one entry: nimbus-beacon, nimbus-beacon-2 and -3
- role: implementer
name: nimbus-beacon
label: nimbus
cli: claude-code # the vendor's model, run by Claude Code
vendor: nimbus
model: Nimbus
version: "1"
launch: nimbus-claude # a launcher on the PATH, holding the account's key and endpoint
count: 3seats[*].clipicks the launch profile;claude-code,codex,cursorandantigravityare available, andteam doctorsays what the others still need.vendor,modelandversionspell one seat's model.accountnames the budget account the seat spends when one vendor has two; without it, the seat spends itsvendor, and changing either is an edit the owner re-approves.
launchis the plain command, without approval flags: the profile adds them.count: 2makes the numbered names;parkedkeeps a seat out of idle reports,stoppedkeeps it out ofup.
watch
The watch's own timings are the owner's too: interval, idle_first, idle_repeat, team_idle,
nudge_wait and unsent_after are approved with the file, and an edit to one of them changes
nothing until the owner approves it — the passes keep running with the values of the approved copy,
or with the defaults when nothing was approved, and the difference is reported.
A check the team doesn't want is turned off in watch.checks — a section only the owner changes, so it is approved like the rest of them.
watch:
interval: 120s
checks:
memory: offbudgets
budgets is the owner's: marks (percent used), how long a figure stays fresh, and each
account's reserve or floor. An account's shared key is informational; team does not act
on it. A seat spends its own account: when the file names one, its vendor
when it doesn't, so one vendor's two accounts are two buckets; a pattern names the account it
measures, not the seat's. A check command is resolved to a file and hashed when the
owner approves. A change to that file leaves that account's check unapproved: it is
not run, and the account reads unknown, until the owner approves again. The rest of
the file still runs. watch.quota_marks is still read, with a warning, until you move it to
budgets.marks. A figure first seen on one seat does not count until it changes or a
second seat shows the same number. It goes stale from the moment it last changed, and
the last readings are kept in the state file beside the team file. team status prints
them, one row per account and window, when there is an account or a stored reading.
examples/checks/codex-quota is a check for an openai account. It ships with the package: with a
global install it is at $(npm root -g)/team/examples/checks/codex-quota, and it is
examples/checks/codex-quota
in the repository. It is a Bun script — the check needs Bun on PATH, whatever runs team — and
it uses only built-in file modules, so there is no package to install beside it. Copy it onto
PATH and name that command:
openai:
kind: subscription
reserve: 10%
sources: [check, status_line]
check: codex-quotaMore fields
More fields exist — tools, trust, machine, limits, watch, visibility — and the comments
team init writes name them; validation refuses what it cannot check, and this build acts on what
the commands below read.