Skip to main content
Hand a ticket to a team of coding agents and get back a change that passes the tests. A model from another family checks it against every requirement in the ticket. Three reviewers from three model families read it, and their notes arrive as one list. A judge decides which notes are worth another round, so a matter of taste does not cost one. A person approves the exact bytes before the change lands. Run it unattended, and the change lands without asking. Each ticket runs in its own Git worktree. The change lands on your current branch only when the whole run passes. The same team can work through a backlog, one ticket after another.

The seven steps

Run it

Set the project up as Installation describes. Sign in to Claude Code and Codex, and install OpenCode. Run the file from a Git repository. Put the ticket in briefs/ticket.md. List the files it changes and its test files in its front matter, and commit the tests. Put judge.json beside the file for the offline judge. JEV=live asks Jev instead, with TYPESAFE_ENDPOINT and TYPESAFE_API_KEY in the environment.
Terminal
When the run reaches the approval, it prints a page to answer on and waits. Keep the process running until the person answers: only the running process can take the answer. With approve.json beside the file, the answer comes from it instead. To leave the approval out, run it unattended:
Terminal
briefs/ticket.md
Run the file in a repository you’re happy for a model to change. The Claude seat runs with permission prompts off, so it can write anywhere the process can.

The seats

One seat builds. A seat from another model family checks the goal. The three reviewers come from three model families:
examples/teams/feature-delivery.ts (excerpt)

1. Build and 2. Checks

The builder reads the ticket and changes the files it names. Then the ticket’s own tests run. A red test goes back to the builder with the command’s output, and no model reads it first:
examples/teams/feature-delivery.ts (excerpt)
When the tests are still red after three tries, the run fails and nothing lands.

3. Goal check

Green tests do not prove the ticket was met. A test can miss a requirement. The goal seat reads the ticket and the work, and marks each requirement met or unmet, with evidence:
examples/teams/feature-delivery.ts (excerpt)
An unmet requirement goes back to the builder with its evidence. The reviewers do not run that round, and the judge is not asked. Check the brief was met has the details.

4. Review battery

Each reviewer reads the change against the ticket and changes nothing. It replies with a verdict and its findings, each tagged block, should-fix or nice-to-have:
examples/teams/feature-delivery.ts (excerpt)

5. Synthesis

The three reviewers run at the same time. synthesise: true makes their reviews one list. The first reviewer’s seat merges the findings that name the same problem. Then each reviewer votes once on the findings it did not raise:
examples/teams/feature-delivery.ts (excerpt)
A finding is dropped when the reviewers who reject it outnumber those who raised or backed it, unless it is a block. A finding the vote splits on stays, marked disputed. Ask a panel shows the vote in full.

6. Judge

When a reviewer asks for changes, Jev decides each finding on the list, a block included: act on it or skip it. The builder gets only the findings Jev acts on. With no cap, the rounds end when Jev stops them or every reviewer passes. Offline, judge.json replays Jev’s answers:
examples/teams/feature-delivery.ts (excerpt)
To bound the rounds as well, pass a cap: judge(judgeSeat, { cap: 3 }). Know when to stop shows the questions Jev answers.

7. Approval

Attended, a person approves the exact bytes. Everything the run changes in its worktree lands, so the question names every file the run added, changed or deleted, whether the ticket names it or not. start is the commit the worktree began at, so a file the builder committed itself is named too. Each file shows the first 12 characters of its sha256, and the request carries each full sha256. A yes is a yes to those bytes: after the yes the files are read again, and if any changed while the question waited, the run fails and nothing lands. A no fails the run, and the change does not land:
examples/teams/feature-delivery.ts (excerpt)
One flag switches between the two ways to run. --unattended leaves the approval step out of the team, and the change lands without asking:
examples/teams/feature-delivery.ts (excerpt)
examples/teams/feature-delivery.ts (excerpt)

Work from a backlog

The runtime keeps no ticket type. Your application chooses where its work lives. The backlog example points the same team at a folder of markdown tickets, backlog/. It takes the next ticket and delivers it in its own worktree. It keeps a record of each run in records/, moves the ticket to backlog/done/, and takes the next one. It stops at the first ticket that does not pass, and leaves that ticket in the backlog. To take the work from Linear or GitHub issues instead, change two functions. Make next ask your tracker for the next ready issue, and make finish close it. Nothing else changes:
examples/teams/feature-team-backlog.ts (excerpt)
The loop delivers one ticket at a time with deliver from the team’s file:
examples/teams/feature-team-backlog.ts (excerpt)
Terminal

Copy the files

The team is examples/teams/feature-delivery.ts. The backlog is examples/teams/feature-team-backlog.ts, and it imports the team’s file.
examples/teams/feature-delivery.ts
examples/teams/feature-team-backlog.ts

Next steps