Summary
A review pass (compose N anchored comments on a doc, leave it resolvable thread-by-thread) is one cognitive unit for the reviewer, but the propose_change tool decomposes it into N transactions each with its own freshness math. The cost of that decomposition is paid in re-reads, coordinate bookkeeping, and retries. The tool is correct — every op did what its contract says — but the grain of the tool and the grain of the work disagree.
Surfaced during a 14-comment review pass on docs/superpowers/specs/2026-05-27-word-body-led-actionability-design.md. Edits landed cleanly but took 3 batches + re-reads to get there when it should have been ~1.
Full reviewer's-eye writeup: docs/findings/2026-05-27-multi-batch-criticmarkup-edit-friction.md
Friction points
- In-batch coordinate shift can't be precomputed. "Offsets auto-adjusted between ops" is the right contract, but op 2's coordinate isn't the one I read — it's post-op-1. I can't precompute all coordinates from one fresh read. Op index 4 in a batch failed even though re-reading showed the content at the cited line.
- All-or-nothing rollback inverts the risk math at scale. For a 14-op batch, even a 5% per-op fail rate → ~51% batch fail rate, and every retry re-pays the cost of ops that already succeeded. Rational risk management pushes toward 1-op batches, which destroys the atomicity benefit.
- Cross-batch coordinate freshness loss from my own edits. Header expansion + appended footnote definitions shifted numbering; required a re-read between every batch.
- Content-anchor vs LINE:HASH split. The content anchor "survives slight shifts" but the schema requires
at: LINE:HASH. Most review comments don't conceptually need exact line addressing — they need "find this phrase, attach a comment."
- Anchor copy precision (backticks, smart quotes, em-dashes, footnote markers). Self-inflicted but systematic: the rendering layer and the matcher disagree by design.
- Working-memory load ~5x the actual review complexity; the gradient pulled toward quantity-over-quality.
What works well (keep)
- Response shape (
affected_lines.lines w/ current hashes, created_regions, document_state) — excellent for re-orientation
- Specific, honest error messages
- Multi-form normalization matching (NFKC, whitespace-collapsed, view-surface, decided-text, current-text)
- Auto-supersession on re-propose
Proposals (reviewer perspective; impl cost not assessed)
The real design question
What should "a review pass" be as a first-class object in the tool, rather than an emergent property of many propose calls? That's the conversation worth having alongside design refinement.
Summary
A review pass (compose N anchored comments on a doc, leave it resolvable thread-by-thread) is one cognitive unit for the reviewer, but the
propose_changetool decomposes it into N transactions each with its own freshness math. The cost of that decomposition is paid in re-reads, coordinate bookkeeping, and retries. The tool is correct — every op did what its contract says — but the grain of the tool and the grain of the work disagree.Surfaced during a 14-comment review pass on
docs/superpowers/specs/2026-05-27-word-body-led-actionability-design.md. Edits landed cleanly but took 3 batches + re-reads to get there when it should have been ~1.Full reviewer's-eye writeup:
docs/findings/2026-05-27-multi-batch-criticmarkup-edit-friction.mdFriction points
at: LINE:HASH. Most review comments don't conceptually need exact line addressing — they need "find this phrase, attach a comment."What works well (keep)
affected_lines.linesw/ current hashes,created_regions,document_state) — excellent for re-orientationProposals (reviewer perspective; impl cost not assessed)
partial: truemode forpropose_changereturningapplied[]+failed[]without rollback. Atomicity stays default; opt out to salvage partial success.LINE:?/anchor:syntax that searches by content with line as hint.review_group:tying multiplepropose_changecalls into one author-visible review pass (vs. emergentcn-1/cn-2/cn-3).The real design question
What should "a review pass" be as a first-class object in the tool, rather than an emergent property of many propose calls? That's the conversation worth having alongside design refinement.