The board
In harness-demo every stage is a column on a kanban board, and a task's status is the column it rests in. One
status, one column, one owner. A card shows a "working" badge while a unit is working on it.
Column = status | Owner | What happens there | Moves on to |
|---|---|---|---|
draft | You write a title and a few lines. Submit or a drag moves it on. | spec | |
spec | writer | The spec unit writes the spec. | plan · needs-input |
needs-input | You answer the open questions on the card. | back to the stage that asked | |
plan | explorerplanner | The explorer maps the code; the planner writes phases, scope, gate and criteria. | planned · needs-input |
planned | You approve the plan, or send it back to the planner with a comment. | prototype for ui tasks, else ready; plan with a comment | |
prototype | designer, then you | The designer builds a prototype; you accept it or comment. | ready · another round |
ready | approved, waiting | Nothing runs yet. | implementing when the implement unit starts |
implementing | testerimplementer | The tester writes the tests, the implementer the code; the harness commits after a green gate. | qa · parked |
qa | qa | Runs every [closes] command and the gate; with an app, walks each [found] criterion in the browser. | judge · implementing · parked |
judge | judge | Re-runs every closing check and tries to reject. | done · implementing |
done | runner | With deliver.pr on, the runner pushes the branch and opens the PR, with no agent and no tokens. Without it, done is the end. | delivered with deliver.pr, otherwise none |
delivered | — | The branch is pushed and the PR is open. | end: a human merges |
parked | Stopped with a reason. Reopen sends it back. | the status it was parked from |
Pick a status to see who owns it and every way out of it. The switches add the moves that exist only with full autonomy or merged review.
- a human move (the dashboard and the CLI offer only these)
- a move the harness makes after a unit, in the colour of the stage's role
- park: a hard stop, from any unfinished status
qaqaThe qa unit runs every [closes] command and the gate, and checks the criteria it can. Read-only.qa-pass→by the harness · without merged reviewqa-fail→by the harness · while bounces + 1 < limits.bouncesqa-fail→by the harness · when this rejection reaches limits.bouncespark→parkedon a hard stop: scope, task file, trace, gate, an agent error or a timeout
Who moves the card
Only two kinds of move exist, and they never mix.
You move a card when a human decision is needed: submit a draft, answer questions, approve a plan or send it back, accept or comment on a prototype, reopen a parked task. In the dashboard these are drags or buttons. A drag is allowed only along a human move, and it is refused while a unit is working on that task.
Every human move also exists as a command, for when you work from a terminal:
harness new --title "Dark mode toggle" --draft "Users want a dark theme." --kind ui
harness submit T-007 # draft → spec
harness answer T-007 Q1 a # answer an open question, with an optional note
harness approve T-007 # planned → prototype (ui) or ready
harness revise T-007 "split criterion 2 into two checks" # planned → plan, with what the plan should change
harness comment T-007 "move the toggle left of the avatar"
harness accept T-007 # prototype → ready
harness reopen T-007 # parked → the status it was parked from
Add --project <path> when you are not inside the project folder.
The harness moves a card after a unit ends and its contracts pass. No agent writes status directly; the runner
does, from the unit's outcome.
The interview
Agents ask instead of guessing. The writer asks product questions at draft time; the planner asks questions that only appear once it has read the code. Both use the same protocol:
- The agent appends a question to
## Questionswith two to four lettered options, the recommended one first. - It sets
status: needs-inputandresume:to its own stage, and ends the unit green. - The dashboard shows the open questions as a picker on the card.
- Your answer is written under the question, and the question flips to
answered. - When no question is open, the harness moves the task back to the stage in
resume.
The writer sees the other tasks on the board, so a recommended option never duplicates another task's work; the spec names such tasks in its out-of-scope list instead.
### Q1 · Where should the toggle live? · open
- (a) Header, right of the nav — recommended: visible on every page
- (b) Settings page only
- (c) Both
> **Answer:**
The prototype step
Tasks with kind: ui get one more stage before code. The designer builds a prototype as a route in the app and leaves a
picture of it in the task's artifacts folder. With an app (the project sets app and it started), the designer opens the route in the browser and
takes a 1440×900 prototype.png, plus prototype-2.png and on for more states. Without one, it draws
prototype.svg: a 1440 px wide wireframe with the page's real copy, layout and colours, one column per state,
nothing overlapping. The designer never starts or stops a server: the runner owns the app. The dashboard shows the picture on the card and in the drawer; a round that
leaves none gets a note in ## Log, and you can still review the route. The card stops in prototype and waits for
you:
- Accept moves the task to
ready. - Comment sends it back to the designer for another round, with your comment in
### Review.
Rounds are capped by limits.prototypeRounds, 3 by default. The prototype route is dropped at delivery unless the
plan says to keep it.
How much autonomy
By default, with autonomy.mode: gated, the harness stops for you twice per task: to approve the plan and to review a
prototype.
At the plan gate you can also send the plan back. Request changes in the Plan tab of the drawer, or
harness revise, writes your comment into a ### Review block at the end of ## Plan, and the task returns to plan.
The next plan round reads the newest comment and must leave the block unchanged. While the plan is back with the planner,
or while a unit works on the task, a second request is refused.
autonomy.mode: full turns both gates off. A planned task moves on by itself, and a finished prototype goes straight to
ready. Two things still need a human even in full mode: a draft needs Submit, and an open question needs an answer.
Branches and delivery
- The first time a task enters
implementing, the harness creates the branchharness/T-NNN-<slug>from the project's base branch. That branch is the only one the scope contract allows. - After every implement unit with a green gate, the harness commits on that branch.
- By default delivery is the commit: with
deliver: { pr: false }, a task ends indone. - The base branch must be clean before a batch. The supervisor refuses a dirty tree it did not make.
Delivery by pull request
Set deliver.pr: true in config.json, and deliver.remote if it is not origin. A done task then gets one more
step. The runner does it itself: no agent, no tokens
(harness/deliver.ts).
- It checks the branch contract: the task branch exists, it is ahead of the base branch, and every commit subject
starts with
T-NNN:. - It writes the pull request body to
.harness/artifacts/T-NNN/pr-body.md: the goal from the spec, the plan, each acceptance criterion with a ✓ and its command for[closes], a ✓ and its screenshot for a[found]criterion qa walked in the browser (the others stay unchecked, for a human), a line that says how many qa walked, the qa and judge verdicts with their unit numbers, and the commits. - It pushes the branch with
git push -u <remote> <branch>. - It opens the pull request with
gh pr create. It asksgh pr view <branch>first, so a retry never opens a second one. - It writes
pr: <url>into the task file and moves the task fromdonetodelivered.
Nothing ever merges. The only gh commands the harness runs are gh auth status, gh pr view and gh pr create;
merging is yours. If the branch contract, the push or gh fails, the task is parked with the reason
(parked: deliver: …), and Reopen retries the step.
With deliver.pr on, the supervisor checks before it starts: a remote, gh installed and logged in, and the base branch
on the remote. If one is missing, it exits with code 2 and prints the fix. HARNESS_GH replaces the gh command the
way AGENT_CMD replaces the agent; the self-tests use it.
What one task costs
A task from draft to pull request is five agent units, each with a fresh context: spec, plan, implement, qa and judge. A UI task adds a prototype round, and an interview adds one more spec unit. Delivery is a runner step that spends no tokens. That is more than a single long session would use. The trade is visibility, since every card rests in the column that owns it, and a clean context for every role.
harness-demo measured it on a pilot: four real tasks on the bookmarks example, run by claude -p on a subscription
login with the default models, gated autonomy and delivery on. Claude passed the human gates on the recommended
options. All four tasks ended in an open pull request, and nothing was merged. T-005 ran after qa and
the designer got a browser.
| Task | Kind | What happened | Units | Agent time | Tokens | API-equivalent | Pull request |
|---|---|---|---|---|---|---|---|
| T-004 Show when a bookmark was added | logic | the plan sent back once; three harness bugs parked it on the way, each fixed | 13 | 6:51 | 25.0k | $2.30 | #2 |
| T-001 Search box that filters the list | ui | a 2-question interview, the prototype accepted on round 1, no bounce | 7 | 3:21 | 11.1k | $1.68 | #3 |
| T-003 Make the bookmarks better | vague | a 4-question interview, the plan sent back once; the judge rejected round 1, round 2 fixed it | 14 | 7:48 | 18.7k | $2.86 | #4 |
| T-005 Undo after removing a bookmark | ui | a 1-question interview; the plan sent back once (it leaned on T-001 and T-004, not in main yet); the designer screenshotted the real toast; qa walked all 7 [found] criteria in the browser | 8 | 6:46 | 17.4k | $2.30 | #5 |
Across the pilot, harness stats counted 47 units, 5 of them the stand-in's, and 5 parked units: 24:49 of agent time,
72.1k tokens and $9.13 API-equivalent. Tokens are input plus output. Prompt-cache reads come on top and cost much less.
On a subscription login the API-equivalent is what the same tokens would cost on the API, not a bill. Every failure
became a contract or a prompt line with a test; the lessons page lists the general ones.