Portia

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.

>/portiaPROJ-123

Epic in, one reviewed PR out. A Claude Code skill plus a small CLI. Jira or GitHub Issues. Reference · Source

P O R T I A›ACME-500Rocket delivery to remote mesas⋮ in session · 14:02
epic PR #412epic/ACME-5004 of 8 landed2 running
wave 0 spine1/1
✓ACME-501Delivery order and crate manifestlanded#415opusattempt 1
wave 12/2
✓ACME-502Price a delivery from the mesa zone tablelanded#418sonnetattempt 1
✓ACME-503Canyon surcharge per countylanded#421opusattempt 2
wave 22 running1/3
✓ACME-504Order history for Super Genius accountslanded#427sonnetattempt 1
⋮ACME-505Customers download a signed receipt as PDFexit gatesonnetattempt 2
⋮ACME-506Telegram the customer when the rocket launchesworkingopusattempt 1
wave 3pending0/1
·ACME-507Refund an order that missed its targetpending
wave 4pending0/1
·ACME-508Live tracking from rocket telemetryneeds ACME-506
epic gatenot run yet · runs once every child has landedACME-505 task gate ✓ 3/3
One run, replayed from its run log: the spine lands, waves run, a gate fails and retries, a change outside a child's paths is approved. Hover to pause.

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

WaveChildStatePRProvesModel
0ACME-501 Delivery order and crate manifestlanded#415R1 ✓opus
1ACME-502 Price a delivery from the mesa zone tablelanded#418R2 ✓sonnet
1ACME-503 Canyon surcharge per countylanded#421R3 ✓opus
2ACME-504 Order history for Super Genius accountslanded#427R4 ✓sonnet
2ACME-505 Customers download a signed receipt as PDFrunningR5 ✗sonnet
2ACME-506 Telegram the customer when the rocket launchesrunningR6 ✗opus
3ACME-507 Refund an order that missed its targetpendingR7 ✗
4ACME-508 Live tracking from rocket telemetrypendingR8 ✗

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.

portia view's gate matrix: one row per child with a tick or cross for each gate step, how often integration and unit failed across exit gates, and a clean baseline

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.

P O R T I A›ACME-410›ACME-418! parked · 14:02

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, then the spine, then waves of children, then one epic PRplanyou say yesspine · wave 0shared code landsevery acceptance spec, failingchildchildwave 1childchildchildwave 2✓✓each child: rebased · re-gated · squash-mergedepic PR · you mergeepic gate: every acceptance spec passes
  1. 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.

  2. 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.

  3. 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.

  4. 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 ticketsWorkspaces and multiplexersHosted agentsPortia
Decisions a person makesN starts, N reviews, N merges, and the integrationN assignments, N reviews, N mergesOne plan, one sign-off, the parked questions, one review
Where integration breaks surfaceWhen branches meet, after the workWhen PRs meet on your branch, after the workAt each child's gate on the rebased head, before it lands
When the whole feature is testedWhen you write and run those testsPer task, by the agent, after writingBefore 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
  • main conflicts 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 permissions only 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.

portia view home: the Portia banner, the build machine's CPU, memory and disk, and runs grouped as in session, awaiting judgement, at rest and delivered

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
gthe gate matrix
o Ochild PR, epic PR
?every key
portia view of a worker: the live transcript of one child's Claude Code session with its reads, writes, a failing then passing test run, and the task gate it starts next

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.

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 interrupted

Every 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.