Skip to content

ReviewGate: a previous round's pass timer escalates the current round, because attempt resets to 0 each round and timers are never cancelled - #1997

Merged
ryanrborn merged 4 commits into
mainfrom
bugfix/1963-reviewgate-previous-round-s-pass-timer
Sep 23, 2026
Merged

ryanrborn merged 4 commits into
mainfrom
bugfix/1963-reviewgate-previous-round-s-pass-timer

Conversation

@ryanrborn

Copy link
Copy Markdown
Owner

Summary

ReviewGate's per-pass timeout (Process.send_after(self(), {:timeout, attempt}, timeout_ms)) tagged its timer only with attempt. attempt resets to 0 at the start of every revise round (bd-bgeo6i, so reprompt budgets start fresh), so round N's implementer and round N+1's implementer are both launched as the same attempt number (typically 2). The timer armed for round N's pass was never cancelled, so it could fire mid-flight through round N+1's pass at the same attempt number and get accepted as belonging to it — escalating a revising pass as timed out well before it had used its own full timeout_ms budget. This repeatedly killed live vstim revisions mid-edit.

Fixed by tagging the timer (and matching handle_info clauses) with {round, attempt} instead of attempt alone. round never repeats within a gate's lifetime, so the pair is unique for as long as the gate runs — no timer ref bookkeeping or Process.cancel_timer/1 needed. Per-round reprompt-budget reset (bd-bgeo6i) is untouched.

Test plan

  • New test in review_gate_test.exs (revise-and-rediscuss loop describe block): a round-1 implementer sleeps for most of its per-pass timeout before committing; a round-2 implementer (same attempt number) is still running when round-1's stale timer would fire. Reproduces the bug on main (fails with "revising pass timed out ... round 2"), passes with the fix. New fixtures: review_reject_twice.sh, revise_slow_then_fast.sh.
  • mix test test/arbiter/worker/review_gate_test.exs plus every other review_gate_*_test.exs file (235 tests, 0 failures).
  • mix precommit — full arbiter app suite (1677 tests, 0 failures; the sandbox-connection-killed / Exqlite teardown noise is a documented baseline flake, not caused by this change). One arbiter_cli failure (ReleaseDeployTest "active workspace resolves") is caused by this worker session's own ARB_* environment variables, not by this diff — confirmed by re-running with those vars unset (38/38 pass).

References

bd-28u8v4
Closes #1963

…n't escalate a later round (bd-28u8v4)

attempt resets to 0 at the start of every revise round (bd-bgeo6i), so
round N's implementer and round N+1's implementer are both launched as
the same attempt number. The timer armed for round N's pass was never
cancelled and was matched on attempt alone, so it could fire mid-flight
through round N+1's pass at the same attempt and escalate it as timed
out — even though that pass was still well within its own fresh
budget. round never repeats within a gate's lifetime, so tagging the
timer (and matching handle_info) on {round, attempt} instead makes
timer identity unique for as long as the gate runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ryanrborn and others added 3 commits September 23, 2026 06:48
… reviewer (bd-28u8v4)

Round 1's reviewer timer collision with round 2's reviewer (both attempt 1,
since attempt resets per round) went through a separate :reviewing /
timeout_retries_left handler that the earlier implementer-collision test
never exercised. Add a sibling fixture and test covering that path.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-28u8v4)

No behavior change. `mix format` on the test file's Path.expand indentation,
and re-anchor 7 docs/review-coverage-and-guard-policy.md citations that
review_gate.ex line-number edits from this branch staled (reviewer_commit_check,
empty_diff_guard, escalate_pre_review, escalate_timeout, maybe_reprompt,
escalate_no_changes, stamp_reviewed_head).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ryanrborn
ryanrborn merged commit a99a373 into main Sep 23, 2026
5 of 6 checks passed
@ryanrborn
ryanrborn deleted the bugfix/1963-reviewgate-previous-round-s-pass-timer branch September 23, 2026 15:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ReviewGate: a previous round's pass timer escalates the current round, because attempt resets to 0 each round and timers are never cancelled

1 participant