Skip to content

fix(cursor-review): give the judge the same timeout as the panel it adjudicates - #219

Merged
mattmillerai merged 2 commits into
mainfrom
matt/cursor-review-judge-timeout
Aug 24, 2026
Merged

fix(cursor-review): give the judge the same timeout as the panel it adjudicates#219
mattmillerai merged 2 commits into
mainfrom
matt/cursor-review-judge-timeout

Conversation

@mattmillerai

@mattmillerai mattmillerai commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

ELI5

The Cursor review runs 8 model calls in parallel (the panel), then one more call (the judge) reads all 8 and writes the single review that lands on your PR. The panel was allowed 30 minutes; the judge only 15 — even though the judge has to read everything the panel produced.

So on a big PR the panel would finish fine after ~25 minutes, and then the judge would get killed right at the end. All that work, no review posted.

What was happening

consolidate had timeout-minutes: 15; review (one panel cell) has 30. The judge calls cursor-agent once more with prompt-judge.md against all 8 cell artifacts, using the same top reasoning tier a single cell uses — the strictly harder call, on half the budget.

A diff slow enough to stretch the panel stretches the judge with it, so the two go long together and only the judge has a tight cap.

It also hid itself. GitHub reports a timeout-minutes kill as cancelled, not failed, so the run page reads as though someone stopped it by hand rather than a cap firing.

Evidence

Two occurrences on one consumer repo on 2026-08-24, both landing exactly on the cap:

Judge started Killed Elapsed
19:49:57 20:05:10 15m13s
17:55:11 18:10:16 15m05s

In the first, all 8 panel cells succeeded between 19:24 and 19:49 — the slowest (adversarial (claude-opus-5-thinking-max)) took 25 minutes of review's 30. Then the judge burned its full 15 and was killed. Nothing posted.

Across the last 60 Cursor Review runs on that repo, 5 were cancelled: 2 were this judge timeout, the other 3 were ordinary concurrency cancels (every job killed within seconds of start when a new push superseded the run). So ~3% of reviews were running a full panel and discarding the result.

notify-complete already branches on needs.consolidate.result, so the DM correctly degraded to a warning — the loss was the review itself, not a false success signal.

The change

Raising the consolidate job cap alone would only move the cliff. A job-level timeout-minutes kill takes Build consolidated findings file and Post review with it, and that is the one failure mode that can never reach the degraded panel-union fallback the workflow already has — so a hung judge would still burn the budget and post nothing, just 30 minutes later.

So the cap goes on the step, in the shape the ledger job already uses:

  • Run judge gets timeout-minutes: 30 — the review panel cap, so the judge is never given less model time than one of the cells it reads — plus continue-on-error: true, which is what makes the timeout absorbable. On a hung judge the steps below then read the --init seed (status=error), take the existing panel-union path and post what the 8 cells found. A hung judge now costs the adjudication, not the panel.
  • The consolidate job cap goes 1540, deliberately above the step so the step trips first. The 10m of slack covers this job's own overhead — fetch-depth: 0 checkout, CLI install, three artifact downloads, the post — which a panel cell does not all pay, so the judge's effective model budget is the step cap, not the job cap.
  • A cross-reference comment at the review cap records the coupling, so a future bump there does not silently recreate the same skew.

continue-on-error hides no real signal: cursor-agent's exit code is already captured in-script, and a judge that did not submit is reported to the PR as judge_status by Post review.

Verification

  • python3 -m unittest discover -s .github/workflow-pins/tests — 216 passed
  • python3 .github/workflow-pins/check_workflow_pins.py — OK, 11 workflows, none with a default ref
  • python3 -m unittest discover -s .github/cursor-review/tests — 194 passed
  • YAML parses; review = 30, consolidate job = 40, Run judge step = 30 with continue-on-error: true
  • check_public_repo_hygiene.py over the edited workflow: 13 findings, all pre-existing (BE-####) commit-convention refs, zero repo-name refs

Note for the merger

This touches .github/workflows/cursor-review.yml, so merging trips bump-cursor-review-callers.yml's path filter and rolls the cursor-review fleet. That is expected for any change to this file.

Provenance

Authored by: agent-work loop

Verified: workflow-pins (216) and cursor-review (194) unittest suites pass; check_workflow_pins.py clean; the workflow YAML parses and the three caps resolve as stated; check_public_repo_hygiene.py over the edited file reports no repo-name reference. Not verified end-to-end: the step-timeout → degraded-fallback path has not been exercised against a genuinely hung judge in CI — it is reasoned from the --init seed's status=error and the existing degraded branch in Build consolidated findings file.

Deviations: The job cap is 40, not the 30 the first commit used. The review panel asked for headroom above the panel cap, and the step-under-job idiom requires the job to outlive the step; 40 satisfies both. The panel also suggested asserting the review/consolidate coupling mechanically in check_workflow_pins.py — not done: that checker is scoped to workflows_ref pinning, and a timeout-coupling rule is unrelated machinery. A comment at the review cap records the coupling instead.

🤖 Generated with Claude Code

…djudicates

The `consolidate` job was capped at 15 minutes while `review` — one panel
cell — gets 30. The judge is the strictly harder call: it reads all 8 cell
artifacts with the same top reasoning tier a single cell uses.

That asymmetry made the failure mode the expensive one. A diff slow enough to
stretch the panel stretches the judge with it, so all 8 cells would run green
for ~25 minutes and then the judge would be killed at the last step, discarding
every cell's work with nothing posted to the PR.

It also hid itself: GitHub reports a `timeout-minutes` kill as `cancelled`
rather than `failed`, so the run page reads as though a human stopped it.

Observed twice in one day on the cloud repo, both at 15m0Xs — about 3% of
recent runs burning a full panel and throwing the result away.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 949bfc2c-7c9c-4393-a916-d39742ac4c86

📥 Commits

Reviewing files that changed from the base of the PR and between 3b2c8ca and 598cc56.

📒 Files selected for processing (1)
  • .github/workflows/cursor-review.yml

Included review availability: 0 reviews are currently available. Your included PR review attempts over the past 7 days set your current allowance at 1 review per hour.


📝 Walkthrough

Walkthrough

The workflow documents the relationship between review and consolidation timeouts. It increases the consolidate job timeout to 40 minutes and gives the judge step a 30-minute timeout with error continuation so fallback findings can still be posted.

Changes

Review resilience

Layer / File(s) Summary
Consolidation timeout and judge fallback
.github/workflows/cursor-review.yml
The consolidate job timeout increases from 15 to 40 minutes. The judge step now times out after 30 minutes and continues on error, allowing fallback findings to be posted. Timeout relationships are documented.

Suggested reviewers: huntcsg

Merge Risk: ⚪ Minimal · up to 598cc

The change gives the judge a matching step timeout and preserves the existing degraded review fallback; no actionable merge-blocking risk remains after normal checks and review.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch matt/cursor-review-judge-timeout
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch matt/cursor-review-judge-timeout

Comment @coderabbitai help to get the list of available commands.

@mattmillerai mattmillerai added the cursor-review Multi-model cursor review label Aug 24, 2026

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Cursor Review — Consolidated panel

Triggered by @mattmillerai.

Found 4 finding(s).

Severity Count
🟠 High 1
🟡 Medium 1
🟢 Low 1
⚪ Nit 1

Panel: 8/8 reviewers contributed findings.

Comment thread .github/workflows/cursor-review.yml Outdated
Comment thread .github/workflows/cursor-review.yml Outdated
Comment thread .github/workflows/cursor-review.yml Outdated
Comment thread .github/workflows/cursor-review.yml Outdated
…er repo

Review follow-ups on the judge timeout fix.

Raising the `consolidate` JOB cap alone only moved the cliff: a
`timeout-minutes` kill at the job level takes `Build consolidated findings
file` and `Post review` with it, so a hung judge still burns the whole budget
and posts nothing — the one failure mode that can never reach the degraded
panel-union fallback the workflow already has. Time-box `Run judge` itself
instead, `continue-on-error` so the timeout is absorbed, and leave the job cap
above it — the same step-under-job idiom the `ledger` job uses. A hung judge
now costs the adjudication, not the panel's 8 cells.

The step cap is the `review` panel cap (30m), and the job cap sits 10m over it
to cover this job's own overhead (fetch-depth: 0 checkout, CLI install, three
artifact downloads, the post), so the judge's effective MODEL budget is at
least a single cell's rather than smaller than one.

Also drops the consumer repo name from the job comment. This repo is public
and deliberately not self-enrolled in public-repo-hygiene, so nothing would
have caught it; the observation is kept, anonymized. And cross-references the
coupling at the `review` cap, so a future bump there does not silently
recreate the skew.
@coderabbitai
coderabbitai Bot requested a review from huntcsg August 24, 2026 20:32
@mattmillerai
mattmillerai merged commit 676fbb6 into main Aug 24, 2026
4 of 5 checks passed
@mattmillerai
mattmillerai deleted the matt/cursor-review-judge-timeout branch August 24, 2026 20:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cursor-review Multi-model cursor review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants