Prove the feature first.
Then let the agents build it.
Portia turns an epic into failing acceptance tests you sign off, runs Claude Code workers in parallel until every one passes on the rebased head, and hands you one pull request with the evidence for each scenario. It never merges to main. You do.
Why it is different
Agents don't mark their own homework.
Every other tool answers "how do you know the agent's work is right?" with "you review the PR". Portia answers with a process: the charge is written before the work, the evidence is collected as it lands, the trial is run on what will actually merge, and a person gives the verdict.
I · The charge
The tests come first, and a person signs them off.
The spine turns every acceptance scenario into a failing spec before any child exists. You sign off that each spec means what its scenario means. The sign-off is recorded in the run log. No wave starts without it.
specs/delivery/receipt.itest.tsfailing · by design
describe("R5: a customer downloads a signed receipt as PDF", () => {
it("R5: the receipt carries the manifest and a signature", async () => {
// Written by the spine, before ACME-505 exists. Fails until it lands.
const pdf = await download(order.id, { as: customer });
expect(pdf.pages[0]).toContain(order.manifest.crates);
expect(pdf.signature).toVerifyAgainst(acme.publicKey);
});
});✓ signed off · R1–R8 encode their scenarios · a person, 07:48 · recorded in the run log
II · The evidence
Every scenario has a receipt in the PR.
A child finishes by naming the scenario it proved and the test that proves it. The ledger in the epic PR's description lists every child, its PR, and the evidence line, rebuilt from the tracker, GitHub and the run log after every landing. Never edited by hand.
epic PR #412 · description · <!-- epic-ledger:start -->
Ledger: ACME-500
| Wave | Child | State | PR | Proves | Model |
|---|---|---|---|---|---|
| 0 | ACME-501 Delivery order and crate manifest | landed | #415 | R1 ✓ | opus |
| 1 | ACME-502 Price a delivery from the mesa zone table | landed | #418 | R2 ✓ | sonnet |
| 1 | ACME-503 Canyon surcharge per county | landed | #421 | R3 ✓ | opus |
| 2 | ACME-504 Order history for Super Genius accounts | landed | #427 | R4 ✓ | sonnet |
| 2 | ACME-505 Customers download a signed receipt as PDF | running | R5 ✗ | sonnet | |
| 2 | ACME-506 Telegram the customer when the rocket launches | running | R6 ✗ | opus | |
| 3 | ACME-507 Refund an order that missed its target | pending | R7 ✗ | ||
| 4 | ACME-508 Live tracking from rocket telemetry | pending | R8 ✗ |
Evidence
- ACME-501 R1: bun test delivery/ → 31 pass, 0 fail; acceptance manifest.itest.ts 4 passed
- ACME-502 R2: zone pricing acceptance 3 passed; a mesa with no zone refuses to ship
- ACME-503 R3: surcharge.itest.ts 6 passed; a county with no canyon pays no surcharge
- ACME-504 R4: order-history.spec.tsx 9 passed; a coyote with two accounts sees both histories
Rendered from the tracker, GitHub and the run log. Edits between the markers are overwritten.
III · The trial
Nothing lands on the word of the agent that wrote it.
The exit gate runs again on the rebased head, the squash merge is pinned to that exact commit, and the epic gate is your signed-off specs passing on the integration branch. The gate matrix shows which step failed, how often, and whether the base was already red.

IV · The verdict
A person merges. Portia never does.
Children squash-merge into an integration branch, never main. When every child has landed and every spec passes, the epic PR is marked ready, and that is where Portia stops. You review one PR with the evidence beside it, and you merge it or you don't.
ACME-418 · wave 2
Backfill the index for discontinued products
! parked attempt 1 · opus · 24m 43s · 95k tokens
A T T E M P T S
10:52:57 ▸ dispatched opus
11:02:09 ✓ task gate typecheck ✓ lint ✓ related-unit ✓
11:17:40 ! parked May I add the search index migration to my paths?
The backfill needs db/migrations/0042_catalogue_index.sql, which is outside my declared paths. Without it the earthquake pills and the dehydrated boulders stay unfindable.
How a run works
Shared code first. Tests before features. Parallel waves. One PR.
Plan
Portia reads the epic, its children and the repo, and shows you a wave table: which tickets run together and which wait for others. Nothing is written until you say yes. The plan is stored in the tickets themselves, with each dependency as a blocker link.
Spine
One worker lands the shared schema, types and API shapes, and writes every acceptance scenario as a failing spec. You sign off that each spec says what its scenario means. No wave starts until it has landed.
Waves
Ready tickets run in parallel, each in its own worktree, branch, port and database. Children in a wave never touch the same file. Each one is rebased, re-gated on the rebased head, PR'd into the integration branch and squash-merged once CI is green.
Epic PR
When every child has landed and every acceptance spec passes on the integration branch, the epic PR is marked ready. You review it and merge it.
Fast where it counts
Parallel is easy. Not having to do it twice is the hard part.
Any tool can start eight agents. The time goes afterwards: into review rounds, into branches that meet badly, into a feature that passes its parts and fails as a whole. Portia spends its effort on the order of work, so the rework happens early, per child, at the gate, and the person's attention goes to a handful of decisions instead of every diff.
Shared code lands first
Children build against schema, types and contracts that already exist, instead of each inventing a version and reconciling later.
Done is defined before the work starts
A worker's brief names the scenario and the failing test. It knows what passing looks like, so it wanders less and finishes sooner.
Siblings never share a file
No two children in a wave touch the same path, by plan. There is no merge conflict between them to resolve at the end.
Failures are found at the gate, not at the end
The exit gate re-runs on the rebased head before a child lands. A failure retries that child with the failure in its brief; the feature isn't discovered broken after everything merged.
Light children skip heavy gates
A small child outside the sensitive paths runs the light lane and a cheaper model. The expensive checks go where the risk is.
Questions are asked once
Open questions about the plan are gathered before the first wave and asked in one round. A worker that would guess parks instead, so a wrong guess never has to be unpicked.
| For a feature of N tickets | Workspaces and multiplexers | Hosted agents | Portia |
|---|---|---|---|
| Decisions a person makes | N starts, N reviews, N merges, and the integration | N assignments, N reviews, N merges | One plan, one sign-off, the parked questions, one review |
| Where integration breaks surface | When branches meet, after the work | When PRs meet on your branch, after the work | At each child's gate on the rebased head, before it lands |
| When the whole feature is tested | When you write and run those tests | Per task, by the agent, after writing | Before the work (failing) and after every landing (the epic gate) |
These are the structural differences, not measurements. Portia's run log records every attempt, gate result and token count, so a run's first-pass land rate and rework cost can be read off it.
Instead of guessing
It stops and asks.
A worker that needs something outside its brief parks with one clear question. The run waits for a person when:
- the plan needs your approval
- the acceptance specs need sign-off
- a worker parks with a question
mainconflicts with the integration branch- a spend cap is reached
And it never
- Merges to main. The epic PR is left for a human.
- Widens permissions.
portia permissionsonly adds deny rules. - Guesses. Workers and the orchestrator park with one question.
- Lets a worker push, merge or rebase. They change only the paths they declared, in their own worktree.
- Lands a commit its gate didn't pass. The squash merge is pinned to that sha.
- Edits the ledger or the run log by hand.
Watch it
A read-only terminal view of every run.
What is running, what waits on you, and what landed, with the machine's load, memory, disk and worker slots in the header. One stack of pages, Home › Run › Child › Worker. Piped, it prints plain tables. Portia usually runs on a machine of its own, opened over SSH.

The worker's own transcript
A detached worker's Claude Code session, as it runs: what it read, what it wrote, which tests it ran and what they said. Follow the end, search it, or open the log in your editor.
| ↑ ↓ | move, or scroll |
| ⏎ ← | open, back |
| / | filter: state:parked wave:2 model:opus |
| g | the gate matrix |
| o O | child PR, epic PR |
| ? | every key |

Is it the right tool
Built for one feature that needs shared code and several workers.
Good fit
An epic, or a story large enough to split: a shared contract, then several children that can be built side by side once it exists, with acceptance scenarios a person can sign off.
Declined at planning time
A single small change. If one worker could finish the parent, the spine, waves and integration branch only cost time, and portia check says so.
Not a fit
A batch of unrelated tickets. Portia lands everything as one feature in one PR, so unrelated work would be held back by each other's gates and review.
Not built yet
Replanning after each wave; a cloud session per child; raising a child's model tier on retry. The reference keeps the honest list.
Compared
Most tools in this space run agents. Portia tries them.
Workspaces give each task a worktree and leave the proof and the merge to you. Hosted agents test their own work and return a PR from their cloud. Portia writes the tests first, gates each child on what will merge, and returns one PR with the evidence. See where each tool sits, or pick a comparison: the same sixteen rows each, starting with who proves the work and where the rework shows up.
- Portia vs Claude Code on its ownthe agent Portia drives
- Portia vs ConductorMac app, one workspace per task
- Portia vs Supersetdesktop workspace for any CLI agent
- Portia vs Claude Squadterminal multiplexer for agent sessions
- Portia vs Vibe Kanbankanban board for coding agents
- Portia vs Bernsteindeterministic orchestrator
- Portia vs GitHub Copilot coding agenthosted, one issue to one PR
- Portia vs Devinhosted AI software engineer
- Portia vs OpenAI Codexhosted cloud agent, many tasks in parallel
- Portia vs Cursor cloud agentshosted agents from the Cursor editor
Install
Three steps.
You need Bun, git, the GitHub CLI logged in, and Claude Code.
# 1. Install, or update. --uninstall removes it; --design adds Chromium for design checks.
gh api repos/mousedown/portia/contents/scripts/install.sh -H "Accept: application/vnd.github.raw" | bash
# 2. Set up a repository: detects the tracker, checks and machine, then asks.
portia init # or /portia-setup inside Claude Code
# 3. Run an epic from the root of the repository, in Claude Code.
/portia PROJ-123 # a Jira epic or story
/portia GH-42 # a GitHub issue
portia resume PROJ-123 # pick up a run that was interruptedEvery profile field, the plan block format, the gates, the CLI and the safety rules are in the reference. A full example profile shows every option, including a database, Docker services and design checks.