aboutsummaryrefslogtreecommitdiffziptar.gz
path: root/docs/lanes.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/lanes.md')
-rw-r--r--docs/lanes.md115
1 files changed, 115 insertions, 0 deletions
diff --git a/docs/lanes.md b/docs/lanes.md
new file mode 100644
index 0000000..2fc6833
--- /dev/null
+++ b/docs/lanes.md
@@ -0,0 +1,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:`.