Lanes, Model line, slice jobs, area maps
Reference (released 2026-10-05). Lanes split work by task size and pick policy: a fast lane clears
small, unblocking work and slices big tasks; a slow lane takes 1h slices; more lanes by config. The model is
a per-task attribute (Model: line): interactive sessions take tasks with Model ≤ their own, orchestrated
workers run on the task's Model. Area maps (code map + test recipe per area) are checked and refreshed by
auto-added tasks.
0. The Model line
- Task body line
Model: haiku|sonnet|opus(2-space indent likeRef:;- Model:also parsed), in the body tail beforeAfter:/Ref:. No line →opus. Order haiku < sonnet < opus. - Set by the task's writer at
wf add --model M;wf set ID --model M;--model ""removes it (→ opus). haiku: mechanical edit, exact instructions, no judgement.sonnet: exact Done criteria + an existing pattern to copy + a real-data pass/fail test.opus: design forks, debugging, reverse engineering / oracles, cross-source analysis, anything unclear.- Wrong level found mid-task:
wf set ID --model <higher>+wf note ID "<why>"+wf status ID clear. wf check: unknown value or two Model lines → error; extra words after the value → warning; missing → fine.
1. Lane config
workflow.toml, optional; absent → built-in default equal to:
slice_above = "1h" # effort above this → slice job (§3); one of EFFORTS
[lanes.fast]
efforts = ["<1h"] # efforts this lane implements
order = "unblock" # pick policy (§2)
slices = true # this lane also takes slice jobs (exactly one lane may set it)
[lanes.slow]
efforts = ["1h"]
order = "priority"
fallback = "fast" # nothing pickable here → pick from that lane
- Lane name:
[a-z][a-z0-9-]*; used in worktree.worktrees/<lane>, branch<lane>/<id>, registry file. efforts: subset ofEFFORTS, ≤slice_above; lanes' efforts are disjoint; every effort ≤slice_abovebelongs to some lane. Tasks without effort → the lane holding<1h.order:unblock|priority.fallback: another lane name, optional, no cycles.slices = trueon exactly one lane; default: the lane holding<1h.- Validation errors in
config.load(unknown keys are errors). Defining[lanes.*]replaces the whole default set (no merge). - Extra lanes: add tables (e.g.
[lanes.long] efforts = ["5h"]withslice_above = "5h"). Several workers in one lane = existing<lane>-2,-3worktrees.
2. Pick order
Pickable set (Pending, not blocked, no open slices, all After: archived, no live foreign claim), filtered
to the lane's efforts (plus slice jobs on the slices lane) and to Model ≤ session model (interactive only).
Effective priority = best (lowest P) of the task's own and that of every open task waiting on it,
transitively ("waits on" = After: [[x]], or a parent waiting on its open slices). Keys:
- unblock: (1) waited-on count > 0 first, (2) more open tasks waiting on it (transitively) first,
(3) effective priority, (4) file order.
- priority: (1) effective priority, (2) waited on by another lane first, (3) waited-on count,
(4) file order (section order, then position).
3. Slice jobs
- A Pending task with effort >
slice_aboveand no open slices = slice job. It is not pickable as an implementation task in any lane; it is pickable in thesliceslane, ranked by that lane's order. - Shown with
slice:prefix inwf next,wf list --runner,wf lanescounts (2 pickable (1 slice)). wf nextprints for it:Slice job: wf add "<title>" -e <1h|1h> --parent <id> --model Mper slice, each with Steps/Done/Ref; no code;wf note <id> "sliced into …";wf status <id> clear. Notwf done(parent stays open until its slices are done).- Model of a slice job = the task's Model (default opus; slicing is judgement).
- A task with open slices is never a slice job (already sliced). All slices done → parent pickable
only if its effort ≤
slice_above; otherwisewf doneof the last slice printsparent <id>: all slices done → wf done <id> or add slices(it is not offered as a slice job again while it has done slices: rule = no open slices AND no archived slices).
4. Sessions, registry, model
wf next [--lane L] [--as M]:--as= the session's model (haiku|sonnet|opus); none → haiku, with a hint line.--lane= lane name; no--lane→ all lanes merged,priorityorder, slice jobs included.- Registry
.wf/sessions/<lane>.json({"lane", "model", "socket", "pid", "session", "at"}, from envCLAUDE_CODE_MESSAGING_SOCKET,CLAUDE_PID,CLAUDE_CODE_SESSION_ID; skipped without them); no--lane→all.json(allis reserved)..wf/.gitignore=*. Newest wins per lane unless another live session holds it: then kept, andwf nextfirst printsanother live <lane> session holds this lane: uds:…. Alive = pid and socket file exist.wf lanes --unregisterdrops this session's records. - Claims:
wf status ID progress …writes.wf/claims/ID.json({"id", "lane", "model", "socket", "pid", "session", "at"});status clear|blockedandwf doneremove it.wf nextskips a task whose claim is alive, not mine and stillin progress. wf lanes [--lane L]: one line per configured lane:fast: 3 pickable (1 slice) · 1 waiting · session uds:… (alive)/… · no session.- Waiting block:
t-y waits on t-x (slow lane) → message uds:…: "t-x blocks my t-y, please take it"/… → no slow session: tell the owner— lane of a task = lane by effort (slice lane for slice jobs). wf done: tasks of other lanes that became pickable →notify <lane> uds:…: now pickable t-y/<lane> work now pickable: t-y (no session: tell the owner).- Messages go with the agents' SendMessage tool,
to=uds:<socket>. wf list: model and lane columns; filters--model M,--lane L;--runner --lane L= ids in pick order for that lane (no session model filter; the orchestrator spawns per task model).
5. Orchestrator, batch, worker
docs/orchestrator.md,shared/skills/wf-orchestrate: lanes fromwf lanes; per lanewf list --runner --lane <lane>→ first id; spawnwf-workerwithmodel: <task's Model>; worktree.worktrees/<lane>, branch<lane>/<id>. At most one worker per lane worktree; lanes in parallel.wf batch N [--lanes fast,slow](default: all configured lanes).shared/agents/wf-worker.md: prompt lineLane: <lane> Model: <model>; slice job → slice only, no code.wf usage --log … --lane L: cost line per worker;--reportrows by lane × model × effort.
6. Area maps
- Notes file: config
areas = "<path>", default projectCLAUDE.md. Section## Areas, one### <area>each with lines (all optional): - Code map: …— anchors = backticked tokens (`parse_item`).- Test recipe: …- Paths: <globs, space-separated>— default: whole repo.- Checked: <short sha>— last refresh; set bywf areas --mark.wf areas [AREA]: per area: missing anchors (git grep -F -q <token> -- <paths>fails), commits touching Paths since Checked (git rev-list --count <sha>..HEAD -- <paths>), stale yes/no. Exit 0.wf areas --mark AREA: writeChecked: <HEAD short sha>into the section (add the line if missing).- Stale = any missing anchor, or Checked set and commits ≥
area_stale_commits(config, default 20). No Checked → only the anchor test. wf check: missing anchor → warningarea <a>: anchor <tok> not found. Unknown Checked sha → warning.wf done: after bookkeeping, for each stale area with no open task whose id ist-map-<slug>→ addt-map-<slug>P2<1hModel: sonnet, titleRefresh <area> area map, Steps:wf areas <area>; fix missing anchors and test recipe from changed files (git log --stat <Checked>..HEAD -- <paths>);wf areas --mark <area>. Done:wf areas <area>shows not stale. Printedadded t-map-<slug> (area map stale). Id taken by an archived task →t-map-<slug>-2, … (ids never reused).- No
## Areassection → feature silent. Templatetemplates/CLAUDE.mdarea block hasPaths:/Checked:.