fix(review-swarm): give the lens retry budget a delay that can span a 60s backoff - #259
Conversation
… 60s backoff Setting no errorHandling does not mean "no retries". The runner's applyReliabilityDefaults() injects strategy:retry / maxRetries:2 / retryDelayMs:1000 whenever errorHandling is absent, so every lens has always had three attempts one second apart. That budget cannot survive a server asking for a longer wait. flows#256: [lens-maintainability] mcp-args --register failed: registration for 'maintainability' was rate-limited; retry after 60s: Workspace write capacity is busy (code: workspace_busy; attempts: 1) Three attempts across two seconds against an advertised 60s. The retries are gone before the requested backoff begins, and the step reports "failed after 2 retries" as though it had waited. Sets only retryDelayMs. maxRetries stays at the injected default of 2, so this changes how long the swarm waits, never how many times it tries. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Review swarm: maintainabilityNo fresh transcript was produced for run |
Review swarm: historyNo fresh transcript was produced for run |
Review swarm: structureNo fresh transcript was produced for run |
|
🎯 review-swarm: FAILED (M:pass H:pass S:missing) Lens transcripts posted as sibling comments above. |
Review swarm: FAILED
Cloud run: |
…on claim #258 and #259 failed back-to-back on the same relayfile mount fault, third occurrence. My "no mode repeats consecutively, therefore no single fix" reading was true for the sampled window and is false now -- fixing relayfile#492 plausibly would unblock the gate. Named the deadlock: both gate-repair PRs are blocked by the gate they repair. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
…inutes No review run had occurred since the promote, so the chain was unverified. Re-ran #259's check. Still running -- and correcting my own comparison: this branch's prior failure took ~19 min, not the 1-5 min I had generalised from a different failure mode, so it has not yet outlived its own window. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
This PR cannot be validated by its own gate, by designWorth stating explicitly for whoever reviews it, because the failing The gate checks the swarm definition out of main, not out of the PR head: - name: Check out immutable gate from main
uses: actions/checkout@v4
with:
ref: main
path: gate-files
sparse-checkout: |
workflows/review-swarm.yaml
...and launches that copy:
That immutability is correct and I am not proposing to change it. It exists so a PR cannot weaken the gate that judges it, which is exactly the property you want. The consequence is narrower: a change to the gate definition itself is unfalsifiable by the gate, and can only be exercised by merging. So the decision here is a human one on the diff's merits. The check will stay red until merge regardless of whether the change is right or wrong, and re-running it produces no new information. For what it is worth on the merits: the last review run (34440534265, 08:16Z) failed with which is the mode this PR addresses — the injected default gives each lens three attempts one second apart against a server explicitly asking for backoff. The companion cloud-side fix is AgentWorkforce/cloud#3516, which classifies that exact string; neither is sufficient alone. The same reasoning applies to #258, which touches |
The gate sparse-checks workflows/review-swarm.yaml out of main, which is the only file #259 touches -- so its own run always uses the old definition and the red check carries no information about the PR. #258 differs: its file comes from the PR head, so it is self-exercising. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
…step New snapshot (sdk 11.11.0) promoted 09:13. Re-ran #259's check: zero workspace_busy, zero cursor_expired, and the swarm ran a lens for the first time tonight -- which exited 1. Not claiming the SDK fixed contention; one observation is not a trend. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
…not set
The maintainability lens reviewing this PR flagged that "maxRetries stays at
the injected default of 2" is a claim about code in another repository. If the
runner default changes, or someone adds maxRetries to this block, the comment
becomes quietly wrong while the YAML stays correct.
Reworded to state the shape rather than the figure: maxRetries is deliberately
not set, the count lives in the runner, and what this change guarantees is that
it alters how long the swarm waits, never how many attempts it makes. The
current default is still mentioned, but as an illustration of the worst-case
cost rather than as a guarantee.
No behaviour change: errorHandling is still {strategy: retry, retryDelayMs:
60000} and swarm.timeoutMs is unchanged, both asserted after editing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Two of three lenses reviewed this PR and both passedThe gate reports red, but that is the structure lens never producing a verdict, not a verdict against the change. I recovered the run's artifacts with All three end in So the swarm is largely working now: two lenses reviewed this change in substance and passed it. That is not a full gate pass and I am not presenting it as one — the aggregate legitimately requires all three. Acting on the maintainability reviewIt raised a fair point I have fixed in 39dd213:
That is exactly right, and it is the failure mode this repo already names — a comment asserting something the code does not enforce. My wording implied the file guaranteed a count it never sets. Reworded to state the shape rather than the figure: Asserted after editing that The lens also independently derived the budget arithmetic — 3 attempts x 60s = 180s per lens against a 1800s lens timeout — and reached the same conclusion I did about the ordering invariant holding. Worth noting, since it checked the number rather than taking the comment's word for it. The other maintainability points (the |
cloud sync works on terminal runs and returned three REVIEW_PASSED transcripts the GHA log never showed. Acted on the maintainability lens's fair catch about my own comment (39dd213). Also filed relay#1734: sync strips +x from every file it applies, which disarmed drain.sh and the gate scripts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
|
One caveat on the two REVIEW_PASSED verdicts I reported above, which I found while reading the transcripts more carefully: neither lens actually had the PR diff. The history lens says so outright — "The /tmp diff was absent... Static history review only" — and the maintainability lens gives it away through its citations, which are all line numbers in For this particular PR that happens to be nearly equivalent, since the whole change is a comment block in that one file and reading the file shows it. But I do not want the earlier comment standing unqualified: two lenses passed a change they were not looking at through the input they were told to use. Filed as #260, with the mechanism — It does not change my read of the verdicts' substance — the maintainability review engaged closely with the actual comment text and caught a real flaw in it — but the signal is weaker than "two independent lenses reviewed the diff and passed", and I said that first without checking. |
The lens tasks read /tmp/pr-<n>.diff, which only the fetch step writes, and steps do not share a filesystem. Both passing lenses reviewed the working tree instead -- and passed anyway, which is the real defect. Qualified my earlier claim on #259 accordingly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
|
Correcting my previous comment on this PR, which was based on the same mistake I have just corrected on #260. I said neither lens had the diff. The history lens did — I truncated its sentence. It reads "The /tmp diff was absent, so the supplied For the maintainability lens I still cannot tell, and this time I will say that rather than infer it: the transcript does not name its source. So my original report stands closer to as written than my correction did: at least one lens reviewed this diff in substance and passed it, and the maintainability lens engaged closely enough with the change to catch a real flaw in my comment. I over-corrected in the direction of doubt, which is its own kind of inaccuracy. |
The retries were never the problem. The delay was.
workflows/review-swarm.yamlsets noerrorHandling, which looks like "no retry policy configured". It is not.applyReliabilityDefaults()in the runner injects one whenever the key is absent:So every lens has always had three attempts, one second apart — and the retry loop applies it literally (
await this.delay(retryDelay)).Why that fails against this platform
flows#256's swarm died with:
The server asked for 60 seconds. The step spent its entire budget in two, then reported
failed after 2 retries— which reads like patience, and was not. flows#245 hit the sibling shape (database_overloaded, "Retry after the interval in the Retry-After header") with the same 1-second budget.The change
Only the delay.
maxRetriesis deliberately left to the injected default of 2, so this changes how long the swarm waits, never how many times it tries.Two things worth flagging for the reviewer:
strategy: retryis load-bearing.applyReliabilityDefaultsearly-returns onfail-fastandcontinue, which would strip the injected defaults and drop every lens to zero retries. Writing the seemingly-conservative value here would make the gate strictly worse.swarm 60m < GHA poll 65m < GHA job 75mordering invariant is untouched, and I assertedswarm.timeoutMsis unchanged.What this does not fix
Honestly: this addresses at most 2 of the 9 failure modes currently blocking this repo (#255 has the full table).
workspace_busyanddatabase_overloadedare retryable with a real backoff. The launch-queue deadline (#238) and sandbox-provisioning (#244) failures happen before or outside the step retry loop and are untouched by this. The recurring mountcursor_expiredfault is filed separately as AgentWorkforce/relayfile#492.I am not claiming this unblocks the gate. It removes one specific way the gate lies about having tried.
Incidental
applyReliabilityDefaultscontains(existing ? DEFAULT_WORKFLOW_MAX_RETRIES : DEFAULT_WORKFLOW_MAX_RETRIES)— both branches identical. Harmless, but it reads like an intended distinction that got lost. Cloud-side, not changed here.🤖 Generated with Claude Code
https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
Note
Low Risk
Workflow reliability tuning only; no auth, data, or application logic changes.
Overview
Configures explicit retry backoff for the review swarm so lens steps can honor platform rate limits that ask for ~60s waits.
Adds an
errorHandlingblock toworkflows/review-swarm.yamlwithstrategy: retryandretryDelayMs: 60000, overriding the runner’s implicit 1s inter-attempt delay while leavingmaxRetrieson the runner default. Inline comments document why omittingerrorHandlingstill meant three quick retries, whystrategy: retryis required (other strategies disable injected retries), and that worst-case extra wait (~120s per lens) stays within existing swarm/GHA timeouts.Reviewed by Cursor Bugbot for commit 39dd213. Bugbot is set up for automated code reviews on this repo. Configure here.