QUORUM.md format
Define an entire organization — name, budget, mission, and every agent — in a single Markdown file. The complete reference.
Last updated
A QUORUM.md in the org root is the org definition — its name, budget,
mission, and every agent, all in one file. Nothing else is scaffolded, and edits
take effect on the next boot. (The mission, as always, only seeds the board on
the first boot.)
This file is yours. The running org treats QUORUM.md as read-only and
won’t rewrite its own charter — change the team, budget, or mission by editing it
yourself and rebooting. The org shapes what it does through its goals, tasks,
and hires; it never edits the definition it runs on.
The whole thing
# Acme Labs
A two-person studio shipping weekend projects.
- budget: 25
- defaults: { "provider": { "type": "claude", "model": "claude-opus-5" } }
## Mission
Launch the landing page and collect 100 signups.
## Agents
### CEO
You are the CEO. Set direction, break the mission into tasks, delegate.
### Engineer
- reports to: CEO
- capabilities: { "crm": "allow" }
You are the engineer. Build what's asked and verify it works.
One rule throughout
A key: value line (optionally - bulleted) whose key is recognized for its
scope is metadata; every other line is prose. Values are single-line and
parse as JSON when possible, exactly like agent-file frontmatter. Keys match
loosely — reports to, reports-to, and reportsTo are the same key.
That means:
- The
#title is the org name; prose under it is the description. ## Missionis the mission — everything in that section, headings and all, verbatim.- Each
###under## Agentsis one agent: the heading is its role (and id), the prose is its system prompt. - Each
###under## Triggersis one event trigger — a schedule, an inbound webhook, a polled inbox, or a process watcher. See Triggers & scheduling. - Unrecognized
##sections are ignored, so the file can hold notes for humans too. - Anything inside a code fence is verbatim prose — headings and
key:lines there are never parsed as structure, so a mission can safely contain examples.
Two caveats worth knowing while you iterate: the only hard parse error is a
file with no agents — everything else degrades quietly (an unrecognized
key becomes prose, a malformed value stays a string). And the parser doesn’t
validate inside JSON objects like provider or capabilities, so a
misspelled field passes through silently rather than erroring; if a setting
seems ignored, check its spelling first.
Org keys
Placed as key: value lines under the # title.
| key | meaning |
|---|---|
name |
org name (default: the # title) |
description |
one-liner (default: the prose under the title) |
budget |
spend caps in USD — a bare number means { "totalUsd": n }; the full object takes totalUsd (lifetime — the org halts for good), dailyUsd, monthlyUsd, perCycleUsd, and a warnAt fraction (default 0.8) at which a budget warning is audited; omitted fields mean no cap. See Cost & budget |
defaults |
org-wide agent defaults: { "provider": {...}, "capabilities": {...} } |
provider |
shorthand for defaults.provider |
channels |
named outbound routes the org sends through — types slack, resend (transactional email), webhook — e.g. { "slack": { "type": "slack", "channel": "#general", "secret": "slack-bot-token" } }; see Acting in the world |
payments |
the payment provider the org charges customers through — { "provider": "stripe", "secret": "stripe-secret-key", "mode": "test", "currency": "usd" } (provider also accepts the merchant-of-record paddle / lemonsqueezy); see Acting in the world |
telemetry |
the observability backend the org reads its own servers’ logs & metrics from — { "provider": "cloudflare", "account": "<account-id>", "secret": "cloudflare-analytics-token", "window": "1h" }; see Acting in the world |
errors |
the error tracker (Sentry) the org reads its own unresolved issues from — { "provider": "sentry", "org": "<org-slug>", "project": "api", "secret": "sentry-token", "window": "24h" }; a host field points at a self-hosted Sentry (default https://sentry.io); see Acting in the world |
Agent keys
Placed as key: value lines under a ### agent heading.
| key | meaning |
|---|---|
id |
bus address and directory name (default: slug of the heading); what other agents’ reports to must reference. Repeat a heading and the id is auto-numbered (engineer, engineer-2) |
role |
the agent’s label / job title (default: the heading) |
goal |
the agent’s own standing goal, injected alongside the org mission |
reports to |
the manager’s id; omit it to make the agent a root (the org lead) |
provider |
this agent’s brain — overrides defaults.provider |
capabilities |
per-agent allow / ask / deny overrides |
skills |
skill packs from the org’s skills/ folder injected into this agent’s task prompts — a JSON array or comma-separated names; see Skills |
budget |
this agent’s own spend caps in USD — a bare number means { "totalUsd": n }; the object takes dailyUsd, monthlyUsd, totalUsd, warnAt. Past a cap the agent is benched — its open work moves to an in-budget colleague — while the rest of the org keeps running; see Cost & budget |
The reporting tree’s root (an agent with no reports to) is the lead — it
coordinates, plans, and reviews. There is normally exactly one.
The provider object
Used as provider: on an org default or a single agent. See
The brain for the full story.
| field | meaning |
|---|---|
type |
the brain: "claude", "codex", an agent-CLI family member ("opencode" / "cline" / "copilot" / "cursor" / "grok" / "qwen" / "amp" / "pi" / "goose" / "hermes" / "kimi" / "droid" / "antigravity"), the lightweight "llm" text brain, "openai" (any OpenAI-compatible server over direct HTTP — LM Studio, vLLM, OpenRouter, remote OpenAI), or "fake" (deterministic, offline) |
model |
model id, e.g. claude-opus-5, gpt-5-codex, gpt-oss:20b. Unset, the tool runs the model it’s configured with (required for openai) |
baseUrl |
API root of an OpenAI-compatible server, e.g. http://localhost:1234/v1 for LM Studio (openai; default https://api.openai.com/v1) |
apiKeyEnv |
name of the env var holding the bearer token; unset means no auth header (openai; default OPENAI_API_KEY) |
effort |
reasoning effort: low | medium | high | xhigh | max (claude) |
maxBudgetUsd |
hard per-session spend ceiling enforced by the binary (claude) |
bin |
path to the binary (default $CLAUDE_BIN / $CODEX_BIN / the binary name on PATH) |
timeoutMs |
per-call timeout (default 10 minutes) |
retries |
retry count on failure (default 3) |
args |
extra raw CLI args appended to every call |
mcp |
path(s) to MCP server config JSON the org owns; null disables |
strictMcp |
isolate from the operator’s own MCP servers (default true) |
settingSources |
--setting-sources value (e.g. "project,local") limiting which settings the binary loads |
browser |
true gives the brain the org-owned browser as MCP tools (claude) |
bare |
true opts out of session persistence — every turn starts fresh (claude) |
fallbacks |
failover chain — each entry inherits this config and overrides what it sets |
pricing |
override the cost-per-token table used by the cost ledger |
Capabilities
Each capability is "allow", "ask" (routes an approval request to you), or
"deny". Set org-wide via defaults.capabilities, per agent via capabilities.
Built-in defaults are allow for everything except crm and charge (both
ask). Capabilities
that gate a declared backend stay inert until you declare it: charge / revenue
(payments), logs (pull the org’s own server telemetry — read-only, needs a
telemetry backend), and errors (pull the org’s own unresolved issues —
read-only, needs an errors backend). Three
namespaces add capabilities of their own: secret:<name> (stored credentials —
deny unless granted), channel:<name> (outbound channels — ask holds
each message for your approval), and each MCP server’s name. Full details in
Tools & permissions.
- defaults: { "capabilities": { "crm": "allow", "hire": "ask", "channel:email": "ask" } }
### Builder
- capabilities: { "secret:github-token": "allow", "secret:cloudflare-api-token": "allow" }
Skills (reusable know-how)
A skill pack is one markdown file in a skills/ folder beside QUORUM.md —
reusable, versioned know-how below the persona: a coding standard, an outreach
playbook, a deploy recipe. An agent’s skills: key names the packs injected
into its task prompts, the same way the runtime’s own hints are:
### Engineer
- skills: ["shipping-standards", "code-review-checklist"]
Names match the pack’s filename case-insensitively, with or without .md, and
packs are injected in the order declared. The folder is read fresh on every
activation, so editing a pack takes effect on the agent’s next task — no
restart. A name with no matching pack is flagged once in the log and audit
trail, never fatal.
When the know-how needs more than prose, a pack can be a folder —
skills/<name>/ with a SKILL.md entry and its resources (templates,
scripts, reference docs) beside it. Only the SKILL.md is injected; the
prompt names the folder’s path, and agents that can use tools read the
resources on demand instead of paying for them in every prompt. A folder pack
shadows a same-named flat file, so upgrading foo.md to foo/SKILL.md wins
even if the old file lingers:
skills/
shipping-standards.md ← a flat pack: injected verbatim
deploy-recipe/ ← a folder pack
SKILL.md ← injected; the rest is read on demand
wrangler.toml.tmpl
checklist.md
Personas stay business prose — the persona describes the job; a skill holds the mechanics and playbooks, where several roles can share one pack and your version control tracks every revision. There is no store and no marketplace behind this: the folder is the registry, and installing a shared pack is copying it in — deliberately no bundle or archive format, so packs stay plain files you can diff and review.
A fuller example
This example uses nearly every option — custom ids, per-agent brains, standing goals, capability overrides, and outbound channels.
# Cedar Loft
A four-person product studio that ships small SaaS products end to end.
- budget: { "totalUsd": 50, "dailyUsd": 10, "monthlyUsd": 40, "perCycleUsd": 2 }
- defaults: { "provider": { "type": "claude", "model": "claude-opus-5", "effort": "medium" }, "capabilities": { "crm": "allow", "hire": "ask", "channel:email": "ask" } }
- channels: { "slack": { "type": "slack", "channel": "#studio", "secret": "slack-bot-token" }, "email": { "type": "webhook", "url": "https://mail.cedarloft.dev/send", "secret": "mail-hook-secret", "from": "hello@cedarloft.dev" } }
## Mission
Launch a paid landing page for the scheduling product and collect the first
100 signups. Validate pricing with at least ten real customer conversations.
## Agents
### CEO
You are the CEO. Set direction, break the mission into tasks, and delegate to your
leads.
### CTO
- id: ren
- goal: Keep the product shippable at all times.
- reports to: CEO
- provider: { "type": "claude", "model": "claude-opus-5", "effort": "high" }
- capabilities: { "hire": "allow" }
You are the CTO. Own technical strategy and direct the engineer.
### Engineer
- reports to: ren
- provider: { "type": "claude", "model": "claude-sonnet-5", "effort": "medium", "maxBudgetUsd": 2 }
- capabilities: { "hire": "deny", "delegate": "deny", "secret:github-token": "allow", "secret:cloudflare-api-token": "allow" }
- skills: ["shipping-standards"]
You are the engineer. Keep every project in a GitHub repository, publish to
Cloudflare, and register what you ship.
### Dry-Run Analyst
- reports to: CEO
- provider: { "type": "fake" }
You are the analyst. Sanity-check plans and numbers before the org commits
to them.
A few things this file shows: the CEO omits reports to, which is what makes
it the root — the lead. The engineer’s reports to: ren points at the CTO’s
custom id, not the heading. And the analyst’s "fake" provider is the
no-API brain — deterministic echoes, no key needed; give one agent (or
defaults.provider) that type to dry-run a QUORUM.md for free.
The channels line gives every agent a voice (SEND[slack]: … posts to
#studio; email waits for your approval), and the two secret: grants give
only the engineer the tokens it ships with — seeded once via
quorum secret set. The full flow is in
Acting in the world. The engineer’s skills
line injects the org’s skills/shipping-standards.md pack into its every
build task — see Skills.
Notice what the personas don’t say: nothing about quorum’s own mechanics.
Personas describe the job — what the role owns, does, and delegates. Every
agent is taught how to use the org’s systems (registering services, sending
on channels, recording contacts, asking you for credentials) automatically;
you never write that into QUORUM.md.
Drafting one from a brief
Don’t want to write it by hand? Describe the org in plain words and let Claude draft the file — one call, validated against the real parser, which you review before spending anything more:
quorum init --prompt "a newsletter studio: an editor leading a writer and a researcher"
Booting the drafted file never calls the LLM again. See the CLI reference.