how it works
From one goal to a verified result.
Brio is a loop with six steps (plan, route, coordinate, verify, learn, govern), and every step is an event you can watch, replay and audit. This page walks through each one with the real names the code uses.
01 · A mission, start to end
Nine hand-offs between you, the brain and the runners.
The brain decides, runners execute, governance can veto anything, and events record everything. Postgres is the only infrastructure: the data, the queue and the run state all live there.
- 1Yougive a goal, with a project, files or a GitHub issue, a priority and a budget
- 2Plannerproposes a task graph with contracts and estimates
- 3Youedit and approve the plan (small, cheap plans can start on their own)
- 4Routerpicks the agents and the execution mode for each ready task
- 5Governancechecks budgets, data policies and approvals before anything is dispatched
- 6Runnerruns each attempt in a sandbox with a fresh worktree, brief, contract and handoffs
- 7Verifierruns the contract's checks; failures go back with the reason
- 8Scoreboardlearns from the verdict; leases are released; a handoff packet is written
- 9Mergeintegrates verified work in dependency order and reports to you
1Surfaces
- web app
- brio CLI
- MCP server
- A2A server
- REST API + SDK
2Orchestrator
- planner
- router
- scheduler
- leases + contracts
- mailbox
- verifier
- scoreboard
- memory
3Governance
- budgets
- policies + approvals
- audit ledger
- BYOK vault
4Execution
- pg-boss queue
- runners
- a sandbox per attempt
- local runners
5Connectors
- model APIs
- coding CLIs
- hosted agents
- A2A agents
- MCP tools
- browser
- media
- the brain decides, runners execute, governance can veto anything, events record everything · one Postgres
02 · Plan
A goal becomes a graph of tasks, each with a contract.
The planner is itself a task (plan.decompose) given to the agent that scores best at planning. Its output is validated against a schema and retried with the errors until it is right.
Five passes over every plan
- 1Decompose: tasks with a type, a contract, dependencies, a suggested mode and an estimate
- 2Contracts first: shared interfaces (an API between front and back end, a schema, file formats) become their own design tasks that every dependant waits for
- 3Leases: each code task's footprint (paths, symbols) is predicted; two parallel tasks that overlap get a dependency or a split
- 4Estimate: cost and time per task, from the scoreboard's averages for that type and agent
- 5Lint: no cycles, every task has acceptance checks, every leaf serves the goal, the budget fits
You see the plan before anything runs: its tasks, contracts, the agent each would go to and the estimated cost. Reorder, split, merge, pin an agent, add a check, then approve. At most three questions come back with it, and only ones that block the work. Small, cheap plans can start on their own.
{
objective: "GET /health returns 200 and the build's version",
inputs: [{ kind: "contract", ref: "health-api@v1" }],
outputs: [{ kind: "diff", description: "the route and its test" }],
acceptance: [
{ kind: "tests", hard: true, command: "pnpm test -- health" },
{ kind: "typecheck", hard: true },
{ kind: "diff_scope", hard: true },
{ kind: "rubric_review", hard: false, criteria: ["matches the contract"] }
],
constraints: ["no new dependencies"],
expectedFootprint: { paths: ["apps/api/src/routes/health.ts", "apps/api/test/**"] },
sensitivity: "internal",
budget: { usd: 0.8, minutes: 20 }
}38 task types, fixed and versioned
Every task gets exactly one type, and scores are kept per type, so the list changes only with a migration.
| Code | code.featurecode.bugfixcode.refactorcode.testscode.reviewcode.migrationcode.frontend_uicode.infra_devopscode.data_pipelinecode.merge_resolve |
|---|---|
| Design & contracts | design.api_contractdesign.schemadesign.architecturedesign.ui_mockup |
| Research | research.webresearch.deep_reportresearch.market_chatterresearch.academicresearch.fact_check |
| Writing | write.docwrite.marketing_copywrite.emailwrite.summarywrite.translation_indic |
| Data | data.analysisdata.extractiondata.sql |
| Media | media.imagemedia.videomedia.voicemedia.music |
| Browser | browser.taskbrowser.form_fillbrowser.scrape_public |
| Meta | plan.decomposejudge.pairwisereview.rubricmerge.integrate |
03 · Route
Each task goes to the agent most likely to win it.
Candidates are filtered first, then scored. A pin always wins: you can send any task to any agent yourself.
1 · Filter
Only agents that can do the task's type, that the data policy allows for its sensitivity, that are healthy, have a known price and fit the budget. A confidential task with no policy goes only to local models.
2 · Score
Thompson sampling with a cost-aware utility: draw a pass rate from each agent's Beta posterior, add its head-to-head strength, subtract its normalised cost and latency.
sample q ~ Beta(α, β) utility = q + λ_elo·elo − λ_cost·cost − λ_time·latency
3 · Explain
Every decision is logged with its rationale, the mode and the estimate, so you can see why an agent was picked and overrule it next time.
task 3 · code.tests · priority: balanced
- codex← picked0.82 pass · $0.31 · 2m14s · n=41
- claude-code0.79 pass · $0.42 · 3m02s · n=37
- gemini0.64 pass · $0.09 · 1m40s · n=12
| priority | what it favours |
|---|---|
| quality | the best pass rate; cost barely counts (λ_cost 0.05) |
| balanced | the default: quality, with cost in view (λ_cost 0.2) |
| cheap | the lowest cost that still passes (λ_cost 0.5) |
| fast | the shortest time to a verified result (λ_time 0.5) |
04 · Execution modes
Seven ways to put agents on one task.
Default rules choose a mode per task; the planner or you can override it. In every multi-agent mode Brio prefers agents from different vendors, because their errors correlate less.
| mode | how it works | chosen by default for |
|---|---|---|
| single | One agent does the task; a rubric review from another vendor checks it. | Writing and media by default |
| best_of_n | Two or three agents from different vendors try; the tests decide, a judge breaks ties. | Code with tests, when the budget allows |
| maker_checker | One vendor makes, another reviews and sends it back until it passes. | Code without tests |
| ensemble | Several agents answer; a merger dedupes and reconciles them, keeping citations. | Deep research reports |
| debate | Two vendors propose and argue; a judge picks and merges. | Design, planning and high-stakes contracts |
| cascade | A cheap model first; it escalates to a stronger one only when a check fails. | Summaries, extraction and other utility work |
| pipeline | A fixed sequence of agents you saved as a template. | Your own templates |
With fewer agents than a mode needs, it runs with what there is and says so on the board: a best-of-two with one agent runs once, and a review by the same vendor is labelled as one.
05 · Coordinate
Agents in parallel, without trampling each other.
Four mechanisms keep parallel work coherent: leases on what each task may touch, versioned contracts, a mailbox between running agents, and handoff packets when work moves on.
Leases
Contracts
The mailbox
Project memory
Handoff packets
When a task passes, Brio writes a packet for the tasks that depend on it: what was done, what was decided and must not be reopened, open questions with defaults, contracts, evidence, risks and next steps. Deterministic fields come from the database, not a model.
- rendered into the receiver's prompt as quoted, untrusted context
- written to .brio/handoff.json in its worktree
- never carries a secret; checked like any other output
{
"from": { "taskKey": "t1", "agentKey": "claude-code", "vendor": "anthropic" },
"to": { "taskKey": "t2" },
"objective": "Implement GET /health against contract health-api@v1",
"done": ["Wrote the contract: status, version, uptime"],
"decisions": [{ "id": "d1", "statement": "Version comes from package.json, not git" }],
"openQuestions": [{ "question": "Expose uptime publicly?", "defaultIfUnanswered": "yes", "askWho": "user" }],
"contracts": [{ "name": "health-api", "version": "1", "ref": ".brio/contract.json" }],
"evidence": [{ "kind": "review", "ref": "review/42", "summary": "gemini: pass" }],
"risks": ["Load balancers may cache /health"],
"nextSteps": ["Add the route", "Test 200 and the version field"],
"trust": "untrusted-content"
}06 · Run
One sandbox and one git worktree per attempt.
An attempt is one agent's run at one task. Runners pull attempts from a queue in Postgres and run each in its own container, on its own branch, with its own limits.
runner host · Docker
container · one per attempt
- non-root user, no capabilities
- read-only base image
- writable: the worktree and /tmp only
- no Docker socket, no host mounts
- CPU, memory, disk and PID limits
- a wall-clock limit, then killed
.brio/brief.md · .brio/contract.json · .brio/handoff.json
egress: default deny, through a proxy
- 1fetch the attempt, its agent and the key, decrypted in memory only
- 2create the sandbox from its profile: image, CPU, memory, network allowlist, timeout
- 3check the project out into a fresh worktree on brio/<mission>/<task>/<attempt>
- 4write the brief, the contract and incoming handoffs into .brio/
- 5give CLI agents Brio's MCP mailbox
- 6run the adapter; turn what it does into events
- 7enforce the limits on tokens, cost and time; kill on a breach
- 8collect the diff, new files and outputs, with their sha256
- 9tear the sandbox down; keep the branch for the merge
| profile | for | limits |
|---|---|---|
| code-standard | coding CLIs | 2 vCPU · 4 GB · 45 min |
| code-heavy | builds, end-to-end tests | 4 vCPU · 8 GB · 90 min |
| api-only | model API calls | 0.5 vCPU · 512 MB · 15 min |
| browser | browser agents | 2 vCPU · 4 GB · 30 min |
| media | media APIs | 0.5 vCPU · 1 GB · 30 min |
Every vendor is reached through one interface, AgentAdapter: model APIs, coding CLIs run headless as documented, hosted agent APIs, A2A agents, MCP tools, browser and media models. The adapter spec.
07 · Verify
Checks decide, not the agent's word.
After every attempt the verifier runs the contract's checks. Hard checks gate the verdict; soft ones inform it. Every result also gets a hard injection scan, so text aimed at the next agent reaches a person first.
| check | how it runs |
|---|---|
| tests | the project's own tests, in the sandbox; pass is exit 0, counts are parsed |
| typecheck | the compiler, not the agent's word |
| lint | the project's linter |
| build | the build has to succeed |
| contract_conformance | the output matches the contract: OpenAPI diff, TypeScript types, JSON Schema |
| diff_scope | changed files stay inside the task's leased footprint |
| rubric_review | a model from a different vendor scores it against the objective, 1–5 per criterion |
| fact_check | claims are pulled out and checked against the cited sources |
| citation_present | every factual paragraph has a source |
| media_spec | dimensions, duration and format; optionally a vision check against the brief |
| link_check | every link in a web output resolves |
| a11y | accessibility checks on web outputs |
| lighthouse | performance and best-practice scores on web outputs |
| human | a person approves |
pass
revise
fail
Judges compare candidates pairwise in both orders to cancel position bias; tests outrank a judge's opinion, and the judge is from another vendor wherever possible.
08 · Learn
A scoreboard you own, from real verdicts.
Every verdict updates the scores of that agent at that task type. Each connector and model pair is scored on its own.
Pass rate
Strength
Cost
Speed
Your ratings count
Thumbs up adds half a pass; thumbs down, a fail. Rate chat replies and mission results alike.
New models can climb
Scores decay with a 60-day half-life, and a new model starts from its predecessor's record at low weight.
Global priors, opt-in
Workspaces may pool anonymised outcomes. They are used only with 20 outcomes from 3 workspaces, and your own scores always come first.
09 · Merge and deliver
Verified work is merged in order, and pushed only with your yes.
Verified attempts are merged in dependency order into brio/<mission>/integration. A conflict becomes a merge task of its own, given to the best merger with both diffs, the contracts and the handoffs, and verified like any task.
- 01
integrate
merge verified branches in DAG order
- 02
report
what was done, by whom, which checks passed, what is open
- 03
propose
a branch, or a pull request on GitHub, waits in your inbox
- 04
deliver
approved, the runner pushes and opens the pull request with the project's token
A person decides the delivery; an API token never can. Both the push and the pull request are entries in the audit ledger.
10 · Events
Every action is an event; the board is just a view of them.
Events are appended to one table and never changed. The board, the scoreboard, the ledger and your notifications all read from them, and the web app streams them over server-sent events.
A reconnect resumes exactly where it left off (Last-Event-ID is the event's sequence number), so a dropped connection never loses a line.
The scheduler is idempotent: it can crash and resume from the database. Leases and attempts carry heartbeats, and a reaper releases what was abandoned.
Secrets are redacted before anything is written, at the adapter boundary.
The architecture →- planner → plan v1 · 4 tasks · est $1.12
- you → approved plan v1
- claude-code → t1 · contract written · $0.08
- claude-code → codex · handoff · api contract v1
- codex → t3 · tests passed · $0.31 · 2m14s
- gemini → t4 · review t3 · pass
- brio → mission done · $0.46 of $5.00
11 · Chat, underneath
A chat is a mission that stays open.
Chat reuses everything above. Each message is one task in a standing plan, carrying the conversation so far as quoted, untrusted context, so every reply gets routing, modes, budgets, policies, checks, the ledger and the scoreboard.
| in chat | runs as | what happens |
|---|---|---|
| Auto | single | The agent best at that kind of question answers, chosen by the scoreboard. |
| Together | ensemble | Each agent you picked answers, then one merges them into a single reply. |
| Review | maker_checker | One answers; another checks it and asks for fixes until it holds up. |
| Debate | debate | They argue it out, and a judge decides and writes the answer. |
One message at a time. A failed reply is revised or rerouted like any task and the chat stays open; a stopped reply stays stopped. When a question turns into real work, the chat becomes a mission with the conversation as its context.
See it on your own agents.
Run Brio in about fifteen minutes, connect one agent, and watch a mission go through every step on this page.