045 — whole-improve-loop: progressive context disclosure for the reviewer
Status: Accepted (2026-06-23) — implemented behind context_mode, default explore, validated by run (see docs/bot-runs/whole-improve-loop.md).
Context
whole-improve-loop (Willy) reviews a large repo in per-package chunks. Until v0.4.0 the deterministic snapshot_chunk tool rendered the chunk's full source inline into the reviewer prompt (capped at max_review_chunk_tokens), and the reviewer prompt explicitly forbade exploration (NEVER bulk-read, glob **/*) to keep context bounded.
Two problems with the inline model:
- Context overflow. A 30000-token chunk + system prompt + accumulated feedback overflowed gpt-5.5's (ChatGPT-forfait) effective window at review_loop 11 (run 019ef550,
context_length_exceeded). Mitigated in v0.4.0 by A (lower default 16000) + B (model-adaptivereviewer_context_tokens) and, in parallel, by thetail()accumulator cap (356053e8b). But these only bound the inline prompt. - A single package/file larger than the reviewer window can never be inlined, no matter how the budget is tuned — the inline model has a hard ceiling at the model's context window.
Native agentic coding (Claude Code) does not inline everything: it lists what exists and the agent pulls what it needs on demand, managing its own context across many tool calls (with compaction). iterion's own doctrine ("skill-guided adaptive agent + deterministic gate verifies the right work happened") points the same way.
Decision
Add a context_mode (inline | explore, default explore) to Willy.
snapshot_chunkalways writes a per-pass index markdown to a gitignored workspace path (.whole_improve_loop.chunks/chunk-<cursor>.md) listing the chunk's files (workspace-relative path + token estimate + the chunk label), and emitschunk_index_path+chunk_file_list(array). Inexploremode it does not inline the source (chunk_contentbecomes a short pointer); ininlinemode behaviour is unchanged (the validated v0.4.0 path remains available).- The reviewer is told the index path and reads the listed files itself via its
read_file/greptools (bothclaude_codeandclawreviewers already carry them), processing incrementally — so a chunk far larger than a single inline prompt is reviewable. It outputsfiles_reviewed: []using the exact paths from the index. - Anti-Goodhart guard (deterministic, no new graph node). The disclosure model cannot guarantee the model read a file (inline could). The guard, folded into the existing
streak_checkcompute node using only expr builtins (length/unique/concat), requires inexploremode thatfiles_reviewedis non-empty and a subset ofchunk_file_list(|A∪B| == |A|⟺B ⊆ A): the reviewer must have engaged with real files from the index and may not approve a chunk having read nothing or having cited hallucinated paths. A pass that fails the guard does not incrementclean_streakand cannot triggerstop, so the loop re-reviews until it engages (loop_max is the backstop). Full per-file coverage is intentionally not hard-required — the operator asked to let the reviewer triage "what interests it"; cumulative coverage comes from the streak gate + cross-family alternation across passes. A stricter full-coverage knob is left as future work.
Everything stays in the .bot (pure DSL + Python tool); no engine change.
Consequences
- Removes the context-window ceiling on chunk size; scales to packages/files bigger than any single prompt.
- More tool calls per review (read-on-demand) → higher latency and some tool-result tokens, but the reviewer reads selectively and manages its own context (compaction), which is the whole point.
- The coverage guarantee weakens from "every line was in the prompt" to "the reviewer engaged with real files and the streak/cross-family sweep provides cumulative coverage". This is an explicit trade documented here; the deterministic engagement guard is the floor.
inlinemode is retained (--var context_mode=inline) as the conservative fallback and for small repos where inlining is cheapest.- Convergence machinery (streak, cursor rotation, cross-family alternation) is unchanged except for the added engagement term in the clean/stop conditions.
