From 81d4e80fd5aabe4e80f58e960affa795cf7d34ec Mon Sep 17 00:00:00 2001 From: godosa Date: Wed, 7 Oct 2026 07:27:17 +0200 Subject: workflow: initial public history --- docs/lanes.md | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 docs/lanes.md (limited to 'docs/lanes.md') 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 ` + `wf note ID ""` + `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/`, branch `/`, 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 `-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 "" -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:`. -- cgit