deepwork
Multi-agent workflows on iii with the kanban board as the task list: one lead per ticket with its own sub-agents, parallel waves, independent verify and review, and bounded rounds. Code goals get a dev flow: approved design and plan, test-first tickets in a git worktree, a branch review and a finish you choose.
exact versions are immutable; every profile and skill is stored by its sha-256, so updates show exactly what changed.
readme
open as markdowndeepwork
Give Deepwork a goal and a team of agents works it, with the kanban board as the task list:
- Deepwork breaks the goal into tickets.
- Each ticket gets a lead agent that takes it through its phases with its own sub-agents.
- Leads run in parallel, in waves.
- Rounds repeat until the goal is done, nothing is left, progress stalls, rounds run out or the budget is spent.
A goal that changes code in a git repository runs the dev flow. You approve the design and the plan. Each code ticket is written test-first in a git worktree, with one commit. Every commit is verified and reviewed, then the whole branch is reviewed, and you choose how it finishes. Other goals (research, reading, analysis) run the plain flow: work, then verify.
Deepwork needs only the harness, state, kanban and ide workers.
Profiles
| Profile | What it does | Model to pin |
|---|---|---|
deepwork |
the orchestrator you talk to | none: uses the session's |
deepwork-lead |
runs one ticket through its phases | a fast, cheap one |
deepwork-worker |
research, reading, analysis | optional |
deepwork-impl |
writes the failing test and the code, and commits | a strong one |
deepwork-adversary |
verifies and reviews each ticket (the default checker) | a mid-tier one |
deepwork-verifier |
base of the checkers, used for judges | none: each judge sets one |
Every profile except deepwork is hidden.
Install
iii trigger directory::download-kit kit=iii/deepwork apply=true --timeout-ms 1200000Sometimes the reply is a plan instead of an install. That happens when there are warnings or decisions, for example a profile with the same id already exists. Review the plan and apply it in the ADE under Directory → Kits, or run:
iii trigger directory::kits::apply plan_id=<plan_id> --timeout-ms 1200000Applying can take minutes. If the command times out, the apply keeps running, so check Directory → Kits instead of running it again.
Pin the models
The kit ships without models because it can't know your providers. Add a model: line to these files:
agents/deepwork-lead.mdagents/deepwork-impl.mdagents/deepwork-adversary.mdagents/deepwork-worker.md(optional)
Use the provider::model form so reasoning_effort reaches the provider. Kit updates keep your edits.
A tier without its own model uses the one set on Default (agents/default.md). If Default has none, it uses the orchestrator's model. An unpinned lead spends one turn per phase on that model. With judges=, deepwork-verifier must resolve no model at all, Default included, or the run won't start.
Run it
In the console, pick Deepwork and type the goal. To change the defaults, add an args: line:
Add per-user rate limits to the login route.
args: max_rounds=2 concurrency=4 budget_usd=5| Arg | Default | What it does |
|---|---|---|
flow |
auto |
dev for code goals in a git repository, plain for the rest. Set it to force one |
approve |
on | in the dev flow, waits for you on the design, the plan and the finish. off runs straight through and keeps the branch |
max_rounds |
3 | rounds over the queue |
attempts |
2 | tries per ticket per round. A rejected try is retried with the reasons |
concurrency |
8 | leads per wave, at most 8. A ticket waits for its Depends on: tickets. In the dev flow, tickets that share a file never share a wave |
verify |
on | checks each ticket |
verifier |
deepwork-adversary |
the checker profile: deepwork-adversary* or deepwork-verifier* |
judges |
– | comma-separated model ids. One deepwork-verifier per model checks each ticket and the majority wins. At most 7 in the dev flow, 8 in the plain flow |
deadline_min |
20 | minutes each phase gets |
critic |
on | one last check for anything missing once the queue is empty. In the dev flow, this is the branch review |
max_items |
5 × concurrency |
the most tickets a run creates |
budget_usd |
– | soft cost cap. It is checked before each wave, and no new wave starts once it is reached. A model without catalog pricing, or a call without usage, isn't counted, so the cap can be overshot |
notify |
– | a function id that gets the final report, such as a Slack or email send |
project |
– | an existing kanban project (id or name) for the run. Without it, each run creates its own project, deepwork: |
release |
– | an existing kanban release (id or name). Agents can't create releases |
Hard cost cap
The console only has the soft budget_usd. For a hard cap, start the run from the CLI:
iii trigger harness::send --json '{"message":"<goal + args>","model":"<provider::model>","options":{"agent":"deepwork","max_cost_usd":5,"max_total_tokens":2000000}}'You must pass model, because the profile sets none. Every model the run uses needs catalog pricing, judges included. The design, plan and finish questions show up in that session in the console. Pass approve=off to skip them.
The dev flow
Design. Deepwork reads the repository and asks about anything the code can't answer. It asks one question at a time and offers two or three options, with its own pick first.
Plan. The design goes on the run ticket. Each task becomes a subtask with
Files:,Test first:,Implement:,Verify:andCommit:lines. You answer one card: Start, Stop, or type your changes. If you type changes, Deepwork rewrites the plan and asks again.Worktree. The work happens on a new branch,
deepwork/, in.worktrees/. That folder is excluded locally, so your checkout is untouched. Run Deepwork from the repository's top folder. The test suite runs once to get a baseline.Code tickets. Each code ticket follows three steps:
- Write a failing test.
- Write the least code that makes it pass, and commit once.
- Verify and review the commit, side by side. Verify re-runs the
Verify:lines. Review checks that the tests are unchanged and nothing outsideFiles:moved.
A rejection goes back to the same code session with the reasons. Research tickets in the same run are worked and verified without a commit.
Branch review. Once the queue is empty, one reviewer runs the full test suite and the linter and reviews the whole diff. Its findings become tickets for the next round.
Finish. You pick one: merge into your branch, push and open a PR, keep the branch, or discard it.
Sub-agents work only inside the worktree. They never push, merge, rebase, reset, switch, stash or clean. Deepwork pushes or merges only when you pick that at the finish. Loose ends the goal doesn't need become Backlog subtasks labelled pending.
The board
Run ticket. It is titled
deepwork:and labelleddw:run. Every ticket of the run is its subtask, so its page shows the run's progress. Each round adds a comment with the cost.Queue. The subtasks in To do are the queue, in board order. In progress means a lead is on the ticket. The session tree shows each lead as
, with its phases under it.· Route a ticket. Set its assignee to
deepwork-worker*ordeepwork-impl*. With any other assignee, a ticket that changes code or files runs asdeepwork-impl, and any other ticket runs asdeepwork-worker. To add your own tier, createagents/deepwork-impl-with.md extends: deepwork-impl(ordeepwork-worker) and amodel.Change the plan. Tell Deepwork in the chat. It edits the queued subtasks from the next round. Running tickets finish first, then each one gets a follow-up.
Steer between rounds. You can:
- add a subtask on the run ticket and move it to To do;
- reorder To do;
- comment on a ticket to steer its retry;
- move a Backlog card to To do to run it again.
Skip a ticket. Move it from To do to Backlog. Don't drag In progress cards, because Deepwork's next write overrides your move. Deleting a card removes it from the run.
Outcomes.
- Done tickets carry
verifiedoraccepted. - Backlog tickets carry
blocked,failed,unverified,unfinished,stuckorpending. - Tickets dropped from the plan are
canceledand go to the board's canceled column. Older boards may lack one: add it in the Kanban settings. Otherwise those tickets go to Backlog with thecanceledlabel.
Deepwork only changes these outcome labels, so your own labels stay.
- Done tickets carry
Prerequisites
kanban with the default columns: ids
backlog,todo,in_progressanddone, anddone_status: done.state with the defaults: the kv adapter (redis won't work),
store_method: file_basedandtriggers_enabled: true.harness with
max_depthof at least 3 andmax_childrenof at leastconcurrency. The defaults, 3 and 8, are enough.Approval rules. These functions must run without a prompt:
Who Functions the harness state::barrierorchestrator harness::spawn,harness::metrics,harness::stop,state::compare-and-set,kanban::ticket::list,kanban::project::create,shell::exec, plusdirectory::agents::getwithjudges=leads harness::spawn,harness::status,harness::stop,engine::register_trigger,state::list,state::compare-and-set,kanban::comment::createphase sub-agents state::compare-and-set,kanban::ticket::get,kanban::comment::create,shell::exec,coder::read-file, plus whatever the work needs.deepwork-implalso needscoder::create-fileandcoder::update-file. Checkers also needstate::getA phase call that waits for approval parks its ticket as
blocked. If every lead in a wave waits for approval, the run ends asblocked.harness::stop. The repository rules (iii-permissions.yaml) deny it with a'!harness::stop'line. To allow it, remove that line and add- harness::stop. An allow above the deny line is not enough, because the console makes every deny rule session-wide. If you keep the deny, leads that time out are parked asstuck.Project creation. The repository rules allow a
kanban::project::createwhoseleadisdeepwork(ruledeepwork-run-project). With older rules, add that rule or passproject=. If the create is refused, the run goes on without a project.Trust. Any agent that can create or update kanban tickets can add a subtask to the run ticket. That subtask then runs with the orchestrator's permissions.
How it works
Each round takes the run ticket's To do subtasks and runs them in waves of
concurrencyleads.For each wave, the orchestrator moves the tickets to In progress and spawns one
deepwork-leadper ticket. It then waits on onestate::barrierwake with a deadline.Each lead does the same for its phases: one sub-agent per phase and one barrier to wait on. It retries rejected attempts, then writes one result to state.
The orchestrator wakes once, when every lead has written or the deadline passes. A lead with no valid result is stopped, and its ticket is retried next round. If the lead can't be stopped, the ticket is parked as
stuck. Backgroundshell::exec_bgjobs keep running after a stop.Verified and accepted tickets go to Done. Failed ones go back to To do while rounds remain, and the rest go to Backlog.
The run ends when one of these happens:
- rounds run out;
- nothing is left;
- progress stalls;
- the budget is spent;
- approvals block it;
- you stop it.
The report goes on the run ticket, into the chat and to
notify.
Results go through state rather than kanban events. State has compare-and-set, and a kanban outage won't lose results.
Not in 0.2: a hard cost cap from the console, and one worktree per ticket. Tickets that share a file wait for a later wave instead.