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
timeout_minutes: 90
mention_timeout_minutes: 90
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. |
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. |
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. |
roster hire appends to this list. An empty list (staff: []) is valid and is what a fresh
org has.
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 |
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.