aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/docs/lanes.md
blob: 2fc6833ae303e80174b6f95d1e3ec1ca646d3ec8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# 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 like `Ref:`; `- Model:` also parsed), in the
  body tail before `After:` / `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:
```toml
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 of `EFFORTS`, ≤ `slice_above`; lanes' efforts are disjoint; every effort ≤ `slice_above`
  belongs to some lane. Tasks without effort → the lane holding `<1h`.
- `order`: `unblock` | `priority`. `fallback`: another lane name, optional, no cycles.
- `slices = true` on 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"]` with `slice_above = "5h"`). Several workers in
  one lane = existing `<lane>-2`, `-3` worktrees.

## 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_above` and no open slices = slice job. It is not pickable as an
  implementation task in any lane; it is pickable in the `slices` lane, ranked by that lane's order.
- Shown with `slice:` prefix in `wf next`, `wf list --runner`, `wf lanes` counts (`2 pickable (1 slice)`).
- `wf next` prints for it: `Slice job: wf add "<title>" -e <1h|1h> --parent <id> --model M` per slice, each
  with Steps/Done/Ref; no code; `wf note <id> "sliced into …"`; `wf status <id> clear`. Not `wf 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`; otherwise `wf done` of the last slice prints `parent <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, `priority` order, slice jobs included.
- Registry `.wf/sessions/<lane>.json` (`{"lane", "model", "socket", "pid", "session", "at"}`, from env
  `CLAUDE_CODE_MESSAGING_SOCKET`, `CLAUDE_PID`, `CLAUDE_CODE_SESSION_ID`; skipped without them); no `--lane`
  → `all.json` (`all` is reserved). `.wf/.gitignore` = `*`. Newest wins per lane unless another live session
  holds it: then kept, and `wf next` first prints `another live <lane> session holds this lane: uds:…`.
  Alive = pid and socket file exist. `wf lanes --unregister` drops this session's records.
- Claims: `wf status ID progress …` writes `.wf/claims/ID.json` (`{"id", "lane", "model", "socket", "pid",
  "session", "at"}`); `status clear|blocked` and `wf done` remove it. `wf next` skips a task whose claim is
  alive, not mine and still `in 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 from `wf lanes`; per lane
  `wf list --runner --lane <lane>` → first id; spawn `wf-worker` with `model: <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 line `Lane: <lane>  Model: <model>`; slice job → slice only, no code.
- `wf usage --log … --lane L`: cost line per worker; `--report` rows by lane × model × effort.

## 6. Area maps
- Notes file: config `areas = "<path>"`, default project `CLAUDE.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 by `wf 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`: write `Checked: <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 → warning `area <a>: anchor <tok> not found`. Unknown Checked sha → warning.
- `wf done`: after bookkeeping, for each stale area with no open task whose id is `t-map-<slug>` → add
  `t-map-<slug>` P2 `<1h` Model: sonnet, title `Refresh <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. Printed `added t-map-<slug> (area map stale)`.
  Id taken by an archived task → `t-map-<slug>-2`, … (ids never reused).
- No `## Areas` section → feature silent. Template `templates/CLAUDE.md` area block has `Paths:`/`Checked:`.