Phase 3 (Q12): no-progress give-up via a monotonic failing-check floor#3
Merged
Merged
Conversation
petemoore
added a commit
that referenced
this pull request
Jun 15, 2026
… plan Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the exact-set no-progress guard with a progress metric (T10/Q12). The old guard reset its counter on ANY change to the blocking-check set, so a worker that thrashed — fix A breaks B, fix B re-breaks A — oscillated the set forever and only the global MaxImplIterations (30) / MaxImplTime cap stopped it. decideNoProgress now tracks the lowest blocking-check count seen so far (a monotonic floor) and the number of consecutive settled attempts since that floor last improved; it gives up once that stall reaches MaxNoProgressIterations. A floor (not a sliding window over raw counts) cannot be gamed by up-down oscillation: 5→4→5→4 never lowers the floor below 4, so the stall keeps climbing. This subsumes both the stationary-stuck-set and the oscillating-thrash cases. - Default raised 3 → 8 and now exposed as `--max-no-progress-iterations` (was effectively hardcoded), with a >0 validation check and a WithX option. - Removed the now-unused exact-set helper `sameSet`. - The metric counts whatever AcceptableGiven deems blocking, so it automatically scopes to required checks once Q7 lands — no coupling to that change. Tests: rewrote TestDecideNoProgress for the floor transitions and added TestDecideNoProgressSequences driving whole sequences (stationary → attempt 9, oscillation → attempt 10 [the old guard never fired], strict progress → never, progress-then-stall → attempt 12). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
d35996f to
7a5d226
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 sub-item Q12 (
docs/WORKPLAN.md; resolves T10). Self-contained and independent of the analyser-removal rework — see the within-Phase-3 sequencing note indocs/questions.md.Problem (T10)
The old no-progress guard gave up only when the exact same blocking-check set recurred across N consecutive turns. Any change reset the counter, so a worker that thrashes — fix A breaks B, fix B re-breaks A — kept changing the set and never tripped it, running to the global
MaxImplIterations(30) /MaxImplTimecap. Oscillation is precisely the expensive-flailing mode the guard was meant to catch.Fix (Q12 → a, K=8, monotonic floor)
decideNoProgressnow tracks:floor— the lowest blocking-check count seen so far, andstall— consecutive settled attempts sincefloorlast improved.It gives up once
stallreachesMaxNoProgressIterations. A monotonic floor (not a sliding window over raw counts) can't be gamed by up-down oscillation:5→4→5→4never lowers the floor below 4, so the stall keeps climbing and the loop terminates. Subsumes both the stationary-stuck-set and oscillating-thrash cases.--max-no-progress-iterations(was effectively hardcoded), with>0validation and aWithMaxNoProgressIterationsoption.sameSethelper.AcceptableGivendeems blocking, so it automatically scopes to required checks once Q7 lands — no coupling.Tests
TestDecideNoProgress— the floor/stall transitions (first call sets floor, equal/worse stalls, new low resets, reaches maxStall).TestDecideNoProgressSequences— whole sequences: stationary → give up attempt 9; oscillation 5,4,5,4 → attempt 10 (the old guard never fired); strict progress → never; progress-then-stall → attempt 12.Local:
go build/vet/test ./...,gofmt,staticcheckall clean.🤖 Generated with Claude Code