Repository navigation
💡 Per-PR Attempt Ledger with No-Progress Detection to Stop Bot Ping-Pong and Repeated Refused Passes #2350
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Summary
Give dev-lead's stateless passes a small, durable record of each pass on a PR: (head SHA, intent, thread or finding id, outcome, guard refusal detail, reply hash). A pure no-progress check reads it before a pass starts. When a pass would repeat the same action against the same head with the same result, the check stops it and escalates once with the reason. This is the in-flight, per-event counterpart to the daily, after-the-fact pr-runaway-detect.sh.
Market Signal
OpenHands ships a StuckDetector on by default in its agent SDK. It halts on repeated action/observation (4+ times), repeated action/error (3+), monologue (3+) and alternating A/B ping-pong (6+ cycles), and compares events by meaning rather than by ID or timestamp. OpenRouter's Agent SDK documents 'doom-loop detection' as a first-class feature. A 2026 source-code study of eleven coding-agent harnesses (arXiv 2609.00006) covers loop control as a core harness concern. Loop breaking is becoming a standard harness feature.
User Signal
#2309: dev-lead and CodeRabbit traded 114 comments (57 each) on one declined thread on PR #1796, stopped only by a hold label and then by API exhaustion. #2181: five passes on PR #2157 re-implemented the same refused change, 38 minutes per pass, 54 replies retracted, because the refusal reason lived only in the run log. #2247 (the retry cap misses passes that die before posting a marker) and #2200 (marker-only comments) show that the current state lives in PR comments and is fragile.
Technical Opportunity
The pieces exist but are scattered: pr-runaway-detect.sh (daily thresholds), review-cycle.sh, bot-thread-retry.sh, test-regression-guard.sh and test-tamper-guard.sh (refusals), and rate-limit-markers.sh (structured markers). The ledger can be one hidden JSON marker per PR, or an artifact keyed by PR. It follows the repo's pure-logic-plus-bats pattern (ADR-0004): a pure function
attempt_is_repeat <ledger> <candidate>that is bats-tested on the #2309 and #2181 transcripts as golden fixtures. Two patterns cover both bugs: (1) the same thread, the same head and a bot reply whose meaning is unchanged → do not reply again; (2) the same head, the same intent and the last outcome was a guard refusal → put the refusal detail into the prompt once, then hold.Assessment
Adversarial Review
Strongest objection: Point fixes are already landing (#2161 no-change path, #2142 hold label), and the factory MVP (#2226) will replace the run model with one run contract, so a ledger is throwaway work.
Rebuttal: Each point fix has covered one loop shape after it happened, and #2309 showed up while #2161 was already deployed. A general no-progress check catches the next shape without knowing it in advance, which is why OpenHands built a detector instead of patches. The ledger schema is exactly the run-outcome record #2226's run contract needs, so it moves into the greenfield build rather than being thrown away. It is also a cheap way to measure loop frequency before the MVP sets its targets.
Suggested Next Step
Write the pure attempt_is_repeat function plus bats tests that replay PR #1796 (thread r4235466918) and PR #2157 as fixtures. Ship it in report-only mode, logging 'would have stopped' for one week, then enable stopping.
Sources
Proposed by Mary (BMAD analyst), weekly feature ideation, 2026-10-16.
All reactions