ADR-057 — Axis-driven work-list sweep (whole-improve-loop's real mechanism)
Status: accepted (2026-07-03). Replaces the chunked-review mechanism of whole_improve_loop (ADR-011 chunking + ADR-055 per-unit convergence over chunks) with an axis-driven work-list sweep. ADR-055's landing/convergence machinery (per-item verify gate, incremental commit, bounded loops) is kept; what changes is the unit of work and how it is discovered.
Superseded for the improve loops by ADR-058: the encoded reviewer/fixer sweep graph is gone — one campaign agent carries the axis and the work-list inside its contract. This ADR remains the reference for the sweep mechanics (axis definition, work-list discovery, done-oracle re-enumeration).
Context
Two dogfood facts and one observed human workflow forced this.
Chunked review can't converge or go global.
whole_improve_loopdeterministically byte/package-chunks the repo and hands each reviewer one slice to find whatever is wrong. On a whole repo it never converges (run019f2247: 9h, 48 iterations, 0 commits — there is always another local issue in the next chunk), and it structurally cannot produce a global, cross-cutting change because no agent ever holds the whole system: a reviewer handed a slice can only find slice-local issues. ADR-055 made the loop converge + land per unit, but the unit was still a chunk, so the output stayed local and incremental-but-small (proof runs019f2750/019f275e: one dead-code fix each).The operator's proven manual pattern is a sweep, not a review. Inspecting the operator's own Claude Code sessions against this repo shows the shape of every successful whole-codebase improvement: a to-do work-list + frequent incremental commits — e.g. one session with 178
TodoWrites, 1053 edits, 112 commits; another 75 / 280 / 94. The git log is the output side of the same pattern:split the six largest source files into cohesive smaller files,converge hand-rolled <kbd> chips onto the ui/Kbd primitive,extract store-agnostic streaming package,make generated spec types the source of truth. Each is one determined axis applied to every matching site across the codebase, committed site-by-site — never "review each chunk for unknown issues".Chunking is the wrong mechanism for that. Chunking slices the repo to review everything for the unknown; the operator's pattern searches for the sites of a known axis and transforms each. Different verb (transform vs review), different unit (a matching site vs a byte slice), different discovery (search vs partition), different done-condition (the axis is applied everywhere vs a clean review streak).
Decision
whole_improve_loop's mechanism becomes an axis-driven work-list sweep. improvement_prompt is the axis (e.g. "split every file > 600 lines into cohesive units", "converge duplicated X onto a shared helper", "make error handling use pattern Y"). The graph:
enumerate— a whole-repo, adaptive agent (claude_code, full tools, whole-repo context like native Claude Code) reads the codebase by its real structure (grep / glob / read — NOT chunks) and emits an ordered work-list:[{id, title, targets (files/symbols/sites), change_spec}]+total_items. This is the operator's "write the todos" step. Persisted to a crash-safe state file (resumable, like the old cursor state).- Sweep loop over the work-list, one item at a time:
transform— an adaptive fixer (whole-repo context) applies the axis change for the current item.verify— the deterministic build/test gate (reuseverify_build/verify_run): the change must be green; red → bounded verify-fix retry; still red → skip the item uncommitted (never land broken code) or pushback.review(multi-model, the iterion edge over a manual loop) — one cross-family reviewer confirms the transform correctly and safely applies the axis at this site (consistency + no regression), not an open-ended re-audit.commit_item— an incremental commit for this item (reusecommit_unit'sgit add -Aincl. untracked, minus scratch, empty-guard); message = axis + item title. One commit per item, matching the operator's ~1-commit-per-work-item cadence.- advance to the next item.
- Converge — when the work-list is exhausted, a
re_enumeratepass re-scans for any remaining sites matching the axis (the done-oracle: the axis is fully applied iff a fresh scan finds nothing). New sites → appended, sweep continues; none → done. Bounded bymax_items/ a loop cap so a pathological axis can't run forever.
Kept from ADR-055: the per-item verify gate, incremental commit, bounded verify-fix loop, crash-safe resumable state, stack-agnostic behaviour (the axis
- the repo define the sites; no language literals in the DSL). Retired: the
snapshot_chunkbyte/package chunker, the per-chunk streak, the partial-view guard (2b) — all chunk-specific. The old chunked-review mode MAY be preserved behind a flag as a rarely-used "find-unknown-issues" fallback, but the axis sweep is the default and the primary mechanism.
Why this is strictly better
- Goes global. The
enumerate/transformagents hold whole-repo context, so a cross-cutting change (introduce a shared abstraction, converge N call sites onto a primitive) is a first-class work-item — the exact thing chunked review cannot express. - Converges by construction. Done = "the axis's sites are exhausted and a re-scan finds none", a finite monotone condition, instead of an unreachable clean-sweep streak over a whole repo.
- Lands continuously. One verified commit per item = the operator's proven cadence; an interrupted/capped run has banked every completed item.
- Amplifies the manual loop. It is the operator's manual Claude Code workflow (todo-list + incremental commits) plus what a human can't cheaply do: parallel/persistent execution, a deterministic per-item verify gate, and cross-family review of each transform — the ADR-055 north star ("orchestrate a capable adaptive agent; don't fragment it") realized.
Alternatives considered
- Keep chunked review, add a global architect phase in front. The prior sketch. Rejected: it bolts a whole-context phase onto a mechanism (chunking) the operator says is wrong, and keeps the review verb where the workflow needs the transform verb.
- A separate new "axis-sweep" bot, leave whole-improve-loop as chunked review. Rejected by the operator: they want whole_improve_loop to be this. One bot, their pattern encoded; chunked review demoted to an optional fallback.
- Deterministic enumeration (pure grep/AST, no LLM). Insufficient alone — "sites matching the axis" needs judgement for most axes. The re-enumeration done-oracle is the deterministic backstop; the enumeration itself is adaptive (mirrors the operator).
Consequences
- A large rewrite of
bots/whole-improve-loop/main.bot(graph, schemas, prompts) —snapshot_chunk→enumerate/next_item/re_enumerate; the chunk state tuple → a work-list + cursor. e2e rewritten around the sweep. - The bot's identity sharpens: "apply a determined improvement axis across the whole codebase, site by site, verified and committed" — a campaign engine, not a scanner.
- Risk: a vague axis yields a vague work-list. Mitigation:
enumeratemust emit concretetargetsper item (a site it can point to), andverify+ per-item review keep each landed change honest; a fuzzy item that can't name a target is dropped, not guessed. - Validation needs a live dogfood on a concrete axis (e.g. "split files > N lines") — stub e2e proves the graph/flow, the live run proves the sweep actually enumerates + transforms + lands across the codebase.
Family rollout (follow-on, after the whole_improve_loop pilot is proven)
The axis-sweep mechanism generalizes to the loop-bot family — but only where the job is "systematically apply a determined change across a body of code", NOT everywhere. Align by fit, not blanket:
branch_improve_loop(Billy) — YES, the natural sibling: an axis sweep whose scope is the branch's touched files (git diff <base>..HEADname set) instead of the whole workspace.enumerateruns the axis over that file set; everything else (transform/verify/review/commit_item/re_enumerate) is identical. It shares whole_improve_loop's convergence machinery today, so it inherits the rewrite most directly.- The shared review machinery — the
alt→reviewer_*→streak_check+ ADR-052 mono/dual topology is currently common to both improve loops; factor the reusable sweep pieces (verify gate,commit_item, re-enumeration done-oracle, work-list state) so both bots and any future sweep share one implementation rather than diverging copies. - Does NOT fit (leave as-is):
feature_dev(build ONE feature — a goal-directed task, not a codebase-wide axis), and any pure diff/PR review bot (judge a given change, not enumerate-and-transform).docs-refreshis a borderline case — "align docs with code" is axis-like and MAY adopt the sweep (enumerate stale-doc sites → transform), evaluate after Billy.
Rollout order: (1) whole_improve_loop (this ADR), prove live; (2) branch_improve_loop as the branch-scoped sweep, factoring the shared pieces; (3) evaluate docs-refresh. Each step keeps the ADR-052 topology + universality + right-artifact invariants and its own dogfood bilan.
