org.yaml reference
Every field in the org manifest, and what reads it.
The org manifest. Lives at the root of the ops repo, and is read at the top of every composed
prompt, by runner-plan.mjs, by agents.mjs, and by every CLI command.
Parsed by parseYaml in compose.mjs, which reads a deliberately small, strict subset of
YAML: scalars, nested maps, block lists, and inline maps in a list. It does not do anchors,
multi-line scalars, or flow maps at the top level. A manifest that needs more than that has
outgrown being a manifest, and the parser refuses to guess rather than misparsing.
A complete example
org: acme
name: Acme Robotics
ops_dir: roster-ops
human:
name: Will
github: you
marker: will
role: founder
experiment_private: true
agent:
id: claude-code-action
permissions: full
defaults:
model: claude-opus-5-5
timeout_minutes: 90
mention_timeout_minutes: 90
budget: 300
review_gate: true
staff:
- { handle: cto, dir: technology, name: Chief Technology Officer, schedule: "0 7 * * 1-5" }
- { handle: cmo, dir: marketing, name: Chief Marketing Officer, schedule: "40 7 * * 1-5" }
repos:
- { name: acme-web, visibility: public, role: product }
- { name: technology, visibility: private, role: brain }
- { name: roster-ops, visibility: private, role: ops }Fields
Top level
| Field | Required | Means |
|---|---|---|
org |
yes | The GitHub organisation. Every repo name is resolved against it. |
name |
yes | What the business is called, in prose. Appears in prompts. |
ops_dir |
no | Directory name of the ops repo in the runner checkout. Defaults to roster-ops. |
experiment_private |
no | Whether the fact that this org is agent-run is itself private. Read by the guardrails fragment. |
budget |
no | USD over any trailing 30 days, for the whole org. See below. |
review_gate |
no | true to have GitHub require a reviewed pull request before anything merges into a product repo. Off by default, because private repos need a paid plan for it. See security. |
human and humans
Who the staff answer to. One person is a human map; more than one is a humans list:
humans:
- { name: Will, github: will-lamerton, marker: will, role: founder }
- { name: Sam, github: sam-x, marker: sam, role: operations }| Field | Required | Means |
|---|---|---|
github |
yes | Login. The mention callers gate on these, so without one nothing can wake an agent. |
name |
no | What to call them in prose. Defaults to the login. |
marker |
no | Provenance tag on a fact they ruled on, as in [will]. Also used as a label. Defaults to the first part of the name, lowercased. |
role |
no | Prose only. |
Both keys are read, and the singular is not deprecated: an org with one human should keep
writing human. When both are present, anyone in human who is not already in the list is
appended rather than dropped.
The first entry is the primary. Prompts are prose addressed to somebody ("Will is not
here"), and a list of two cannot go in that sentence, so the first one goes there, their
marker is what %%HUMAN_MARKER%% renders, and the rest are named by
{{humans_extra}} in the identity fragment. Everything that gates on identity reads all of
them: the mention caller's if: is contains(fromJSON('["will-lamerton","sam-x"]'), …).
Adding a human changes every generated caller workflow, so it takes a roster upgrade to reach
the brain repos. Until that lands, the new person can open issues and read everything, and
mentioning a staff member does nothing.
agent
Which coding agent runs a session. Either a string, or a map. See choosing a coding agent.
| Field | Required | Means |
|---|---|---|
id |
no | A preset name, or your own label. Defaults to claude-code-action. |
install |
if id is unknown |
Shell command that installs the agent on the runner. |
run |
if id is unknown |
Shell command that runs it, reading $AGENT_PROMPT_FILE. |
token_env |
if id is unknown |
Environment variable its credential goes in. |
model |
no | Default model for this agent. A staff member's own model wins. |
permissions |
no | full, workspace or read-only. Defaults to full. One word, translated into each agent's own vocabulary: a tool list for Claude, a sandbox and an approval policy for Codex, a development mode for nanocoder. A staff member can set their own, and be trusted less than the org. |
options |
no | A map, in that agent's own vocabulary, spelled onto its command line untranslated. The escape hatch for anything roster does not model. |
Any field given overrides the preset's, so a preset that is right except for one flag needs one line.
An agent that needs a config file of its own gets one written when roster init chooses it,
with the parts only a person can supply left as FILL IN blanks. roster doctor fails while
any of them are still there.
defaults
Fallbacks for staff members who do not set their own.
| Field | Means |
|---|---|
model |
Model id passed to the agent. |
timeout_minutes |
Ceiling on a daily session. 90 if unset. |
mention_timeout_minutes |
Ceiling on a mention run. Falls back to timeout_minutes, then 90. |
allowed_tools |
Claude's own spelling of a permission level, kept because it predates agent.permissions and still wins for the agents that take a tool list. Nothing translates it for the others: a list written for one agent is not a permission level for another. Prefer agent.permissions, which every agent understands. |
memory
Budgets roster lint holds every staff member's memory to. All optional; a staff member's own
memory: overrides these.
memory:
max_fact_chars: 400 # one fact's line in memory/INDEX.md
max_index_kb: 24 # the whole index, read in full at every boot
max_decisions_kb: 24 # log/decisions.mdOver budget is a warning naming what to cut, never an error. See memory.
staff
The registry. One inline map per staff member. This is the org's view of them; the rest
lives in their own staff.yaml.
| Field | Required | Means |
|---|---|---|
handle |
yes | Short identifier. Must match the handle in their manifest. |
dir |
no | Directory and repo name. Defaults to the handle. |
name |
no | Role name in prose. |
schedule |
no | Cron. Informational here; the caller workflow is what actually schedules. |
budget |
no | USD over any trailing 30 days, for this staff member alone. |
roster hire appends to this list. An empty list (staff: []) is valid and is what a fresh
org has.
budget
A number of dollars, on the org, on a staff entry, or both:
budget: 300
staff:
- { handle: cto, dir: technology, name: Chief Technology Officer, budget: 200 }Spend over the trailing 30 days is added up from each run's record. Past a budget, roster doctor warns and the portal's Runs screen marks the total. Nothing stops a run. A cap that
ends a session fails it after the work is done and committed, which is a false red rather than a
saved penny; timeout_minutes is the real bound. See cost.
repos
Every repository the org owns, and what it is for.
| Field | Means |
|---|---|
name |
Repo name, resolved against org. |
visibility |
public or private. roster doctor warns when this disagrees with reality. |
role |
brain, product or ops. |
role: product is load-bearing: roster hire uses it to fill a new staff member's
works_in, and the prompts refer to the product repo by name.
What reads what
| Reader | Uses |
|---|---|
compose.mjs |
org, name, human, humans, ops_dir, staff |
runner-plan.mjs |
org, staff, and each manifest's works_in and peers |
agents.mjs |
agent, staff |
roster hire |
all of it, plus every existing manifest |
roster doctor |
all of it, including budget |
roster lint |
staff, memory |
roster portal |
all of it; the Runs screen reads budget |
Editing it
It is yours. roster upgrade never touches it: it has no template, because a tenant's registry
is not something a framework can have an opinion about.
The exception is roster hire --apply, which appends a staff entry and a repos entry.
Those are inserted textually under the existing keys rather than by re-serialising the file,
so your comments and formatting survive.