feat(pr-review): announce the round early and fix the turn budget - #97
Conversation
The sticky status comment was only written by the publisher, so a PR stayed silent for the several minutes a round takes and then showed the review and the status comment at once. Preparation now posts or updates the comment to "Review in progress" before analysis, so the status is there first and the review lands into an already-visible comment. Any path that ends without publishing retires the phase itself — a failure diagnostic, or output discarded because the head moved — so the comment never sits at "in progress" after the job ends. Skip-mode rounds publish nothing and are not announced. Announcing is best effort: a GitHub failure while posting it leaves the round unaffected.
Strong-tier reviews ran out of turns at 24. The driver is not diff size: the prompt mandates six pipeline files plus repo guidance before the diff is read, roughly ten turns, and a small diff inside a large file spends many more paging through surrounding code. A 226-line change across four files exhausted the budget while an 81-line new file finished comfortably. Raise strong to 36 and deep to 56 so the ordering between them stays meaningful. Also stop replaying the failure: the retry reused the first attempt's budget, so an exhausted-turns failure burned a second full model run that could not succeed. It now gets half again as many turns, and its prompt says to favour returning a well-supported result over exhaustive exploration.
|
Added Why 24 was too low. Not diff size — this PR is +226/−15 across 4 files, while an 81-line single-file PR reviewed comfortably. The driver is turns spent understanding context: the prompt mandates six pipeline files plus repo guidance (~10 turns) before the diff is read, and a small diff inside a 2700-line file costs many more paging through it. Classifying every recent Strong 24 → 36. Deep 40 → 56, so it stays meaningfully above strong. Separate bug fixed here too. The retry reused the first attempt's budget. Exhausting turns is deterministic, so the retry replayed the identical failure — visible twice in the failing log — burning a second full Opus run with no chance of success. The retry now gets 1.5× the turns, and its prompt says to favour returning a well-supported result over exhaustive exploration. This cannot fix its own PR. The |
Claude review status
✅ Review clean Last reviewed: head New this round: 0 finding(s), 0 question(s) · Resolved this round: 0 · Open questions: 0 |
Summary
The sticky status comment was only ever written by the publisher. A round takes several minutes, so a PR stayed completely silent for that whole window and then showed the detailed review and the status comment at the same moment — the status never actually preceded the review it is meant to frame.
Preparation now posts (or updates) the comment to
🔄 Review in progressbefore analysis begins. The status is on the PR from the start, and the review lands into an already-visible comment.Three phases:
🔄 Review in progress— set by preparation, with the scope being reviewed and a start timestamp. Items listed underneath are explicitly labelled as carried over from earlier rounds, so they are not misread as this round's output.🛠️ Review did not finish— for a round that ends without publishing, naming the cause where one is known.Two holes I made sure to close, since announcing early means something must always retire the phase:
reportstep, which runsif: always().Without those, a comment reading "Review in progress" would outlive the job that wrote it — worse than the current silence.
Announcing is best effort:
set_sticky_phaseswallows a GitHub failure and returns the previous comment id, so failing to announce never fails a review. Thereportstep gainsGH_TOKENbecause it can now write.Test plan
python3 -m unittest discover -s .github/actions/claude-pr-review -p 'test_*.py'— 72 pass, 6 new: in-progress rendering, prior open items staying visible and labelled during a round, failed-phase rendering, best-effort behaviour on a GitHub failure, and bothclose_out_stickypaths.mise run lint— clean.