Skip to content

feat: implement issue #1994 — Rate-limited PR reviews wait up to ~7h after the limit resets — the retry relies on GitHub's best-effort cron, which fires far less often than every 15 min - #1999

Merged
don-petry merged 10 commits into
mainfrom
dev-lead/issue-1994-20261001-1846
Oct 1, 2026
Merged

don-petry merged 10 commits into
mainfrom
dev-lead/issue-1994-20261001-1846

Conversation

@don-petry

@don-petry don-petry commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Rate-limited PR reviews wait up to ~7h after the limit resets — the retry relies on GitHub's best-effort cron, which fires far less often than every 15 min

From the issue: When pr-review defers a PR because an advisory reviewer is rate-limited, it writes: <!-- pr-review-agent rate-limited v1 sha=<HEAD> status=rate-limited reset=<ISO> --> from scripts/lib/advisory-review-gate.sh (~L340). The only thing that re-dispatches the review after reset is pr-review-sweep.yml / scripts/sweep-stuck-reviews.sh (~L300).

Design choice and runner-minute cost

Choice: this PR uses a dispatched, sleeping one-shot workflow (pr-review-delayed-retry.yml). It does not shorten or duplicate the cron. When the sweep defers a PR whose reset falls within DELAYED_RETRY_HORIZON_SEC (default 3600s), it arms one retry for that PR and head SHA. The retry sleeps until reset plus DELAYED_RETRY_BUFFER_SEC (default 120s). It then re-checks that the PR is open, still at the same head, and still carries the marker, and hands the PR to the normal sweep path with ARM_DELAYED_RETRY=false.

Runner-minute cost: each armed retry uses at most ~62 runner-minutes of sleep (horizon + buffer = 3720s), plus about a minute of setup and the one-PR sweep. The job is capped at timeout-minutes: 70.

  • Retries per PR: at most one per (PR, head SHA). A dedup check skips re-arming while one is queued or running, and the per-(PR, head) concurrency group cancels any duplicate.
  • Total across PRs: bounded by the sweep's shared MAX_DISPATCH budget, since arms count against it.
  • Resets beyond the horizon: no retry is armed; the cron handles them.

Risk

Medium — changes GitHub Actions workflow behavior, which is exercised only post-merge; verify via the affected workflow runs.

Test plan

Tests added/updated: tests/test_delayed_rate_limit_retry.bats,tests/test_sweep_stuck_reviews.bats. Verification: bash scripts/dev-lead-lint.sh (shellcheck --severity=warning) ran pre-commit; the bats suite runs in CI.

Rollback

Revert this PR. No non-revertible side effects (no tags, migrations, or external state).

Monitoring

Watch the affected workflow run(s) in the Actions tab and this PR's Lint check for regressions.

Closes #1994

&lt;source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"&gt;&lt;source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"&gt;&lt;img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"&gt;

…after the limit resets — the retry relies on GitHub's best-effort cron, which fires far less often than every 15 min
@chatgpt-codex-connector

This comment has been minimized.

@qodo-code-review

This comment has been minimized.

@coderabbitai

This comment has been minimized.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — waiting on PR blockers (intent: review-changes)

PR: #1999
No changes were committed, but the PR still can't be marked done: required check SonarCloud is still pending. The retry cron will re-attempt automatically. Next attempt after: 2026-10-01T19:43:10Z

@don-petry

Copy link
Copy Markdown
Collaborator Author

Note

@don-petry I reviewed this PR and no code changes were needed, but I can't mark it done yet: required check SonarCloud is still pending. I'll re-check automatically.
Next attempt after: 2026-10-01T19:43:10Z

@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@don-petry
don-petry enabled auto-merge (squash) October 1, 2026 19:13
Comment thread scripts/sweep-stuck-reviews.sh

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces a deterministic delayed rate-limit retry mechanism via a new workflow and script to handle deferred PR reviews near their rate-limit reset times, reducing reliance on the best-effort cron schedule. It also updates the sweep script, documentation, and adds comprehensive unit tests. The review feedback suggests improving the robustness of the shell script by adding error handling to the mktemp command and utilizing optional chaining with fallback values in jq to prevent potential crashes under set -e.

Comment thread scripts/delayed-rate-limit-retry.sh Outdated
Comment thread scripts/delayed-rate-limit-retry.sh Outdated
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T20:14:09Z.

@don-petry
don-petry disabled auto-merge October 1, 2026 19:14
@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — review-changes (partial)

A commit was pushed, but not every requested change was applied. Per requested item:

  • scripts/sweep-stuck-reviews.sh:232 — not applied
  • scripts/delayed-rate-limit-retry.sh:117 — applied
  • scripts/delayed-rate-limit-retry.sh:100 — applied

The unaddressed items above still need work.

@don-petry
don-petry enabled auto-merge (squash) October 1, 2026 19:18
@don-petry
don-petry disabled auto-merge October 1, 2026 19:19
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T20:19:51Z.

@donpetry-bot

donpetry-bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at ad82ff675ba4e6f600329f8edbf6a8b256234910 — click to expand prior review.

Review — fix requested (cycle 1/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: dd20521d36764ec762d9b578e7b4f52388c23527
Cascade: triage → deep+duck (triage: haiku 4.5 [sonnet 5.5, sonnet 5] → deep: opus 5.5 [opus 4.8, sonnet 5.5] + duck: gemini-3.8-flash [sonnet 5.5] → audit: opus 5.5 [opus 4.8, opus 4.7])

Summary

Both reviewers rate risk MEDIUM and escalate, so they agree fully. Both found the design sound at dd20521, but neither can approve: the PR head has moved to ad82ff6 and the required bats check is still running. Both also raised the separate arm/dispatch budget in sweep-stuck-reviews.sh, which the newer head appears to fix with a shared MAX_DISPATCH, and the cost of sleeping runners. The rubber duck alone raised the cron re-arm and cancel-in-progress churn and the thin 65-minute timeout margin. The deep review alone caught the vacuous dry-run test assertion.

Cross-engine agreement

full

Findings

  • major: Reviewed SHA dd20521 is no longer the PR head. ad82ff6 changes sweep-stuck-reviews.sh (arms and dispatches share one MAX_DISPATCH budget) and delayed-rate-limit-retry.sh. Those changes need their own review, and the required checks on that head are still queued or running.
  • major: The bats check in the Lint workflow was IN_PROGRESS at review time. It is the only validation of the new tests, so the CI gate isn't satisfied. (tests/test_delayed_rate_limit_retry.bats)
  • minor: The test 'near-reset arming honours DRY_RUN' asserts output contains "arm". The sweep summary always prints 'delayed retries armed', so it passes even when nothing is armed. Assert on 'would arm delayed retry' instead. (tests/test_sweep_stuck_reviews.bats:724)
  • minor: Each armed retry holds an ubuntu-latest runner asleep for up to about 62 minutes, and a burst of rate limits can arm up to MAX_DISPATCH of them. This costs billed runner minutes and uses concurrency slots. Issue Rate-limited PR reviews wait up to ~7h after the limit resets — the retry relies on GitHub's best-effort cron, which fires far less often than every 15 min #1994 asks the PR to state the cost, but the description doesn't. Consider a lower arm cap or one sleeper per earliest reset. (.github/workflows/pr-review-delayed-retry.yml:89)
  • minor: At dd20521, arms were capped separately from dispatches, so one sweep could send up to 2×MAX_DISPATCH workflow_dispatch requests. ad82ff6 fixes this with a shared budget. Confirm it is intended that armed retries now use up review-dispatch slots. (Both reviewers flagged this, at different lines and with different categories, so it was not merged.) (scripts/sweep-stuck-reviews.sh:430)
  • minor: armed is capped by MAX_DISPATCH separately from dispatched, and armed retries bypass the dispatched -ge MAX_DISPATCH early exit. A shared budget or a lower arm cap would be safer, given that each arm holds a sleeping runner. (scripts/sweep-stuck-reviews.sh:225)
  • minor: The 15-minute cron re-arms the same (PR, head) every tick while the reset is within the horizon. With cancel-in-progress, each tick cancels and restarts the sleeping run, and could kill a retry that is already mid-delegation. Consider cancel-in-progress: false with a no-op on duplicates, or skip arming when one is already in flight. (.github/workflows/pr-review-delayed-retry.yml:62)
  • minor: timeout-minutes of 65 leaves about 180s of margin over the 3720s sleep cap. That margin must cover checkout, the gh calls and the delegated sweep. On timeout the run is cancelled and falls back to the cron, so it isn't fatal. (.github/workflows/pr-review-delayed-retry.yml:76)
  • info: Arming only happens when a sweep runs while the reset is still in the future and within the 3600s horizon, so it depends on a cron tick landing in that window. Arming from the marker writer (advisory-review-gate.sh) would remove the dependency. Consider it as a follow-up. (scripts/sweep-stuck-reviews.sh:455)
  • info: With an empty NOT_BEFORE, GNU date -u -d "" returns today's midnight rather than an error. The script then logs 'reset already elapsed' instead of 'NOT_BEFORE unparseable'. Behaviour is the same, but the log is misleading. (scripts/delayed-rate-limit-retry.sh:315)
  • info: DELAYED_RETRY_HORIZON_SEC (3600), DELAYED_RETRY_MAX_SLEEP_SEC (3720) and timeout-minutes: 65 depend on each other but are tuned separately. A comment cross-referencing them would help. (scripts/delayed-rate-limit-retry.sh:296)
  • info: mktemp under set -e is unguarded, and the NOT_BEFORE parse fails open to an immediate retry. The sweep re-validates the reset gate, so this is acceptable. The BSD date -j fallback silently ignores timezone offsets other than Z. (scripts/delayed-rate-limit-retry.sh:117)

Reviewed by the PR-review cascade (triage: haiku 4.5 [sonnet 5.5, sonnet 5] → deep: opus 5.5 [opus 4.8, sonnet 5.5] + duck: gemini-3.8-flash [sonnet 5.5] → audit: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Acknowledged — this is a Codex usage-limit notice from the external code-review bot, not a finding against this PR's diff. No code action is warranted; advisory-bot availability is an org billing/quota concern tracked outside this PR, and the pr-review-agent already posted a rate-limit note. Recording as informational.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Acknowledged — this is a Qodo trial-ended / billing notice, not a finding against the diff. No code action applies; resuming Qodo reviews is an org billing decision outside this PR. Recording as informational.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Acknowledged — this is CodeRabbit's auto-generated "review in progress" placeholder (no findings posted yet); it self-updates once the bot finishes. Nothing actionable in the diff to address here. Recording as informational.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Acknowledged — SonarCloud reports Quality Gate passed with 0 new issues for this PR. This is a passing-status notice, not a finding; no code action required. Recording as informational.

@don-petry

Copy link
Copy Markdown
Collaborator Author

Dev-Lead — waiting on PR blockers (intent: fix-reviews)

PR: #1999
No changes were committed, but the PR still can't be marked done: required check SonarCloud is still pending. The retry cron will re-attempt automatically. Next attempt after: 2026-10-01T19:51:40Z

@don-petry
don-petry enabled auto-merge (squash) October 1, 2026 19:21

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread .github/workflows/pr-review-delayed-retry.yml
Comment thread tests/test_delayed_rate_limit_retry.bats
Comment thread tests/test_sweep_stuck_reviews.bats Outdated
Comment thread scripts/sweep-stuck-reviews.sh
Comment thread interaction-contracts/pr-review.yml
Comment thread .github/workflows/pr-review-delayed-retry.yml
Comment thread .github/workflows/pr-review-delayed-retry.yml Outdated
@don-petry

Copy link
Copy Markdown
Collaborator Author

No description provided.

@donpetry-bot

donpetry-bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
Superseded by automated re-review at ad82ff675ba4e6f600329f8edbf6a8b256234910 — click to expand prior review.

Review — fix requested (cycle 2/3)

The automated review identified the following issues. Please address each one:

Findings to fix

Automated review — NEEDS HUMAN REVIEW

Risk: MEDIUM
Reviewed commit: ad82ff675ba4e6f600329f8edbf6a8b256234910
Review mode: triage-approved (single reviewer)

Summary

This PR fixes #1994. When the sweep defers a rate-limited PR whose reset is within 3600s, it now arms a new workflow_dispatch-only workflow, pr-review-delayed-retry.yml. That workflow sleeps until reset+120s, checks the head SHA hasn't changed, then hands back to the sweep for that one PR with ARM_DELAYED_RETRY=false, so it can't arm itself again. The design is sound. Dispatches and arms now share one MAX_DISPATCH budget, the PAT handling is the same as pr-review-sweep.yml, and workflow inputs are passed through env rather than interpolated into run:. I can't approve it yet: the required bats and unit checks are still running, and bats is the only check that runs the new tests. A small test-assertion fix from the prior review is also still open.

Linked issue analysis

Issue #1994 is addressed. A near-reset defer now arms a retry that fires within minutes of the reset, without waiting on the cron. Each (PR, head) gets one armed retry through its concurrency group. Retries for different PRs never cancel each other or the sweep. A newer push makes the old retry a no-op. The docs (pr-review-sweep.yml header, AGENTS.md exception, interaction model §4 table, pr-review interaction contract) now call the cron the eventual backstop rather than a guaranteed ≤15-min one. Still missing: the issue asks for the runner cost of a sleeping retry to be stated, and the PR description doesn't state it.

Findings

Prior findings (cascade review at dd20521), checked against ad82ff6:

  • ✅ Resolved: the reviewed SHA was stale. This review covers head ad82ff6.
  • ✅ Resolved: arms and dispatches now share one MAX_DISPATCH budget, both in the main-loop break and in arm_delayed_retry. Two new tests cover this. The codeant thread is resolved.
  • ✅ Resolved: mktemp now has a guard, and the head-SHA jq uses .headRefOid? // "". Both gemini threads are resolved.
  • ❌ Carried forward (blocking, CI): the required bats and unit checks are still IN_PROGRESS on ad82ff6. bats is the only check that runs tests/test_delayed_rate_limit_retry.bats and the new sweep tests. Re-review once they are green. (bats isn't installed in this reviewer's environment, so I couldn't run the tests here. shellcheck --severity=warning passes on both scripts.)
  • ❌ Carried forward (minor): tests/test_sweep_stuck_reviews.bats:732. The test 'near-reset arming honours DRY_RUN' still asserts *"arm"*. The sweep summary always prints "delayed retries armed", so the test passes even when nothing is armed. Dev-lead reported this item as "not applied". Assert on *"would arm delayed retry"* instead.
  • ❌ Carried forward (minor): issue Rate-limited PR reviews wait up to ~7h after the limit resets — the retry relies on GitHub's best-effort cron, which fires far less often than every 15 min #1994 asks the PR to state the runner cost. Each armed retry holds an ubuntu-latest runner asleep for up to about 62 minutes, and the PR description doesn't mention this. Since arms now use MAX_DISPATCH slots, a cron tick that re-arms near-reset PRs also takes slots from real review dispatches. That's acceptable, but worth noting.
  • ℹ️ Downgraded: the concern that cancel-in-progress could kill a retry mid-delegation doesn't hold. arm_delayed_retry only arms while reset - now > 0, and the retry wakes at reset+120s, so no later tick can re-arm and cancel it after it wakes. Before the reset, each tick still cancels and restarts the sleeping run, which only wastes runner minutes. Optional: use cancel-in-progress: false and skip arming when one is already queued.
  • ℹ️ Unchanged, informational: timeout-minutes: 65 against a 3720s sleep cap leaves about 3 minutes of margin. An empty NOT_BEFORE makes GNU date -d "" return today's midnight, so the log says 'reset already elapsed' instead of 'unparseable', but the behaviour is the same. Arming still depends on a sweep tick landing inside the 1h horizon. Arming from advisory-review-gate.sh itself would be a good follow-up.

New issues in ad82ff6: none.

Security: no secret is piped into run:. The PAT expression secrets.GH_PAT_DON_PETRY || secrets.GH_PAT_WORKFLOWS is the same one pr-review-sweep.yml already uses. Workflow-level permissions: contents: read is set. actions/checkout is SHA-pinned. Dispatch inputs reach the script only through env:. Gitleaks, CodeQL, AgentShield and SonarCloud (0 new issues) all pass. The MCP secret-scanning tool isn't available in this environment.

CI status

Not yet green. Still IN_PROGRESS: bats, unit, and the advisory cubic. Passing: Lint, ShellCheck, shellcheck, actionlint, CodeQL / Analyze, SonarCloud, gitleaks, AgentShield, check-model-pins, validate-interaction-contracts, validate-interaction-model, caller-permissions, toplevel-permissions, and the rest. The CANCELLED entries (dev-lead dispatch/ci-relay/resume, Dismiss, review) each have a later SUCCESS or SKIPPED run, so they were superseded and aren't failures.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

Additional tasks

  1. Resolve all unresolved review thread comments from other reviewers
  2. Ensure all CI checks pass after your changes
  3. Rebase on the target branch if behind
  4. Do NOT modify files unrelated to the findings above

The review cascade will automatically re-review after new commits are pushed.

@don-petry
don-petry disabled auto-merge October 1, 2026 19:23
@don-petry
don-petry enabled auto-merge (squash) October 1, 2026 19:34
@donpetry-bot donpetry-bot added the needs-human-review Flagged by automated PR review agent label Oct 1, 2026
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T20:36:01Z.

@don-petry

Copy link
Copy Markdown
Collaborator Author

dev-lead is withholding action on this item.

It is labeled needs-human-review (flagged for human review — this label is applied by automation as well as by people, so an item can become held without anyone noticing), so dev-lead will not pick it up while that label is present. This notice is posted once so the withhold is visible rather than looking like a stalled run.

To re-enable automated pickup: remove the needs-human-review label.

Comment thread tests/test_delayed_rate_limit_retry.bats
@don-petry don-petry removed the needs-human-review Flagged by automated PR review agent label Oct 1, 2026 — with Claude
…erive the sleep ceiling

Addresses the open review threads on #1999:

- Marker at wake time (cubic P2). still_applicable now also requires the
  armed head's rate-limit marker. Without it, a retry whose marker was
  removed fell through the delegated sweep's REVIEW_REQUIRED + green-CI path
  and launched an ordinary review.
- Failed arms spend the budget (cubic P2). A failed `gh workflow run` in
  arm_delayed_retry now counts against MAX_DISPATCH, so a transient dispatch
  error cannot fan out across every remaining candidate.
- Sleep ceiling derived (cubic P3). DELAYED_RETRY_MAX_SLEEP_SEC now defaults
  to the sweep's DELAYED_RETRY_HORIZON_SEC + DELAYED_RETRY_BUFFER_SEC (same
  variables and defaults). An explicit lower value is raised to that floor
  with a warning, so the retry never wakes before reset+buffer.
- Docs (cubic P3). docs/agentic-interaction-model.md and the sweep comment
  no longer call the cron a guaranteed <=15-min backstop.
- Tests (graphite). write_pr fixtures carry `state`; new cases cover a
  merged PR, a closed PR, a missing marker, a marker for another head, the
  raised ceiling, and a failed arm. The marker, ceiling and failed-arm cases
  fail without the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
@donpetry-bot donpetry-bot added the needs-human-review Flagged by automated PR review agent label Oct 1, 2026
don-petry and others added 2 commits October 1, 2026 15:33
1efea90 wrapped the Bridge B sentence in docs/agentic-interaction-model.md
so a line began with "#1952),". markdownlint reads that as an ATX heading
without a space and fails the Lint check. Reworded so the issue reference
sits mid-line. markdownlint-cli2 is clean over the repo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T21:39:46Z.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 5 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread scripts/delayed-rate-limit-retry.sh
Comment thread scripts/delayed-rate-limit-retry.sh
@donpetry-bot

Copy link
Copy Markdown
Contributor

CI checks on this PR are still running. The PR-review sweep re-reviews this PR automatically once the checks complete — no action is needed.

Posted by the donpetry-bot PR-review cascade.

cubic on #1999:
- P2: the digit-only checks admit a leading zero, which bash arithmetic reads
  as octal (0600 -> 384). DELAYED_RETRY_HORIZON_SEC, DELAYED_RETRY_BUFFER_SEC
  and DELAYED_RETRY_MAX_SLEEP_SEC are now normalized with 10#, so the
  ceiling matches the sweep's horizon.
- P3: the sweep reads rate-limit markers from reviews AND comments, but the
  wake-time check only read comments, so a marker in a review body would
  silently strand the retry. still_applicable now fetches `reviews` and
  searches both.

Both new tests fail without the change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread tests/test_delayed_rate_limit_retry.bats
cubic P3: the test name says the retry re-dispatches but it only checked the
no-marker message was absent. It now asserts the pr-review-trigger.yml
dispatch for that PR, like the other happy-path cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015fdDZDb4mhT65MU1izeA4R
@donpetry-bot

Copy link
Copy Markdown
Contributor

Advisory bots were rate-limited; auto-approval is withheld until they recover. pr-review-sweep will re-review this PR after 2026-10-01T22:14:06Z.

@donpetry-bot

Copy link
Copy Markdown
Contributor

Automated review — human attention needed

This PR has been through 3 automated review cycles since the last approval or escalation (cap: 3) without converging. Further automated review has been paused to avoid infinite loops.

Please take a look manually, or close this PR if it's no longer needed. To re-engage the automated cascade with a fresh cycle budget, a human should remove the needs-human-review label after reviewing. Re-engagement is intentionally human-gated (#926): mentioning the bot will not reset the cap.

Posted by the donpetry-bot PR-review cascade.

@don-petry don-petry removed the needs-human-review Flagged by automated PR review agent label Oct 1, 2026 — with Claude
@donpetry-bot

Copy link
Copy Markdown
Contributor

CI checks on this PR are still running. The PR-review sweep re-reviews this PR automatically once the checks complete — no action is needed.

Posted by the donpetry-bot PR-review cascade.

@sonarqubecloud

sonarqubecloud Bot commented Oct 1, 2026

Copy link
Copy Markdown

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 481142d140a34fa5b7c4ffef37afdd2b1938f8c0
Review mode: triage-approved (single reviewer)

Summary

Fixes #1994. When the sweep defers a rate-limited PR whose reset is within a configurable horizon (default 3600s), it now dispatches a new workflow_dispatch-only workflow, pr-review-delayed-retry.yml. That workflow checks the PR is open, still at the armed head, and still carries the marker. It sleeps until reset+120s (capped at horizon+buffer), checks the same things again, then hands the PR back to the sweep with ARM_DELAYED_RETRY=false. The sweep re-checks the marker, any standing verdict, holds and CI before it re-dispatches through the normal trigger, so the retry never forces a review. Overstated "guaranteed ≤15-min backstop" wording is corrected throughout. Risk is MEDIUM: this is new workflow and orchestration logic. It uses the same PAT pattern as pr-review-sweep.yml: the don-petry identity guard, workflow_dispatch inputs passed only through env: (never interpolated into run:), contents: read, and a pinned checkout. It adds no new secret handling or third-party surface.

Linked issue analysis

Every acceptance criterion in #1994 is met:

  • Near-reset arming, configurable horizon, wait to reset+buffer, same re-dispatch path: done (arm_delayed_retry, DELAYED_RETRY_HORIZON_SEC, delegation to the sweep through SWEEP_PRS_FILE).
  • Concurrency: each (PR, head SHA) gets its own group, so retries for different PRs never cancel each other or the full sweep.
  • Idempotent and bounded: a dedup check matches the run-name (gh run list displayTitle) and skips re-arming when a retry is already queued or in progress. A newer push makes the retry exit without acting (checked before and after the sleep). Hold, escalation and standing-verdict exclusions are re-applied by the delegated sweep. Arms share the single MAX_DISPATCH budget, including failed attempts. The retry cannot re-arm itself.
  • Comment corrections: done in the workflow, script, AGENTS.md, the interaction-model doc and the interaction contract.
  • Tests: near reset arms one retry; far reset arms none; SHA change, standing verdict, merged, closed and missing marker all exit without acting; per-PR concurrency group; DRY_RUN; MAX_DISPATCH sharing; dedup; sleep floor; base-10 parsing.

One small gap (not blocking): the AC asks to "state the choice and the runner-minute cost". The design is documented in code comments and AGENTS.md, but the PR body does not state the cost explicitly (up to about 62 minutes of runner sleep per armed retry, bounded by timeout-minutes: 70).

Findings

Prior cascade findings (cycle 3/3 at ad82ff67), all resolved by later commits:

  • Vacuous DRY_RUN arming assertion → the test now asserts "would arm delayed retry".
  • Sleep, buffer and cap paths untested → a near-future reset test exercises the sleep; tests cover the max-sleep floor and leading-zero/base-10 parsing.
  • Lane missing from the concurrency_lane registry → added to interaction-contracts/pr-review.yml.
  • Re-arm churn on every sweep tick → delayed_retry_in_flight dedup via run-name/displayTitle, which fails open.
  • Arms starving MAX_DISPATCH → arms and dispatches now share one budget, and a failed arm still uses a slot.
  • Retry waking before the sweep's reset check → the sleep floor is forced to horizon+buffer, so an armed retry always wakes after reset.
  • 65-minute timeout too tight → now 70 minutes.
  • No pre-sleep supersession check → still_applicable pre-sleep added; it also requires the marker and an OPEN state.
  • Marker only searched in comments → now searches reviews and comments.
  • Unresolved bot threads → none remain unresolved (GraphQL check: 0).

Non-blocking notes:

  • info: Dedup keys on (PR, head), not (PR, head, reset). If a second marker with a later reset appears at the same head while a retry sleeps, the first retry wakes, the sweep defers again without re-arming, and the next sweep tick arms the new reset. The cron backstop covers this; it is harmless.
  • info: delayed_retry_in_flight matches only queued/in_progress. A run in waiting/pending would not count, but with cancel-in-progress: true the worst outcome is one redundant arm.
  • info: shellcheck --severity=warning is clean on both scripts (checked locally).

CI status

All required checks pass: SonarCloud, CodeQL, agent-shield / AgentShield, dependency-audit / Detect ecosystems, duplicate-decl-gate. The other completed checks pass too, including Lint, bats, shellcheck, unit-tests, unit, actionlint, validate-interaction-contracts, validate-interaction-model, gh-aw-compile and Secret scan (gitleaks).

Two non-required guard checks (Test-Deletion Guard, Sync Scope Guard) have been queued for about 25 minutes because of a runner backlog; their first runs at this SHA were cancelled when the merge commit replaced them. Neither applies to this diff: it deletes no test lines, and it is not a standards-sync PR. The rest of the pending rollup entries (add-to-project, dev-lead dispatch, pr-auto-review) are automation triggers, not CI.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

@don-petry
don-petry merged commit 8693ab4 into main Oct 1, 2026
74 of 76 checks passed
@don-petry
don-petry deleted the dev-lead/issue-1994-20261001-1846 branch October 1, 2026 21:46

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 481142d140a34fa5b7c4ffef37afdd2b1938f8c0
Review mode: triage-approved (single reviewer)

Summary

Fixes #1994. When the sweep defers a PR on a rate-limit marker whose reset is within DELAYED_RETRY_HORIZON_SEC (default 3600s), it now arms a new dispatch-only workflow, pr-review-delayed-retry.yml. The armed run checks that the PR is open, still at the armed head and still carries that head's marker. It then sleeps until reset+buffer (never less than horizon+buffer), runs the same checks again, and hands the single PR back to sweep-stuck-reviews.sh with ARM_DELAYED_RETRY=false. The sweep applies its usual hold, standing-verdict, reset and CI gates before it dispatches through the normal trigger, so the retry never forces a review and cannot re-arm itself. Comments that called the cron a "guaranteed ≤15-min backstop" are corrected across the workflow, script, AGENTS.md, the interaction-model doc and the interaction contract.

Risk is MEDIUM: this adds new orchestration logic and a new workflow. The workflow uses the same don-petry PAT as pr-review-sweep.yml and in the same way: the identical PAT-presence and gh api user identity guard, contents: read, and a SHA-pinned checkout. workflow_dispatch inputs reach the script only through env: and are never interpolated into run:. No new secret, third-party action or reusable workflow is introduced. I confirmed this triage-approved assessment independently.

Linked issue analysis

Every #1994 acceptance criterion is substantively met:

  • Near-reset arming, configurable horizon, wait until reset+buffer, same re-dispatch path: arm_delayed_retry in the defer branch, DELAYED_RETRY_HORIZON_SEC / DELAYED_RETRY_BUFFER_SEC, and delegation to the sweep through SWEEP_PRS_FILE.
  • Design choice and runner-minute cost: now stated in the PR body (≤~62 runner-minutes of sleep per armed retry, capped by timeout-minutes: 70). This closes the one gap the earlier review at this SHA noted.
  • Concurrency: a per-(PR, head SHA) group, so retries never cancel each other or the sweep's group.
  • Idempotent and bounded: a run-name/displayTitle dedup that fails open; supersession checks before and after the sleep; exclusions (holds per #946/#1550, standing verdict) re-applied by the sweep. I checked in sweep-stuck-reviews.sh at head that they are evaluated before the defer/arm branch. Arms and dispatches share one MAX_DISPATCH budget, failed attempts included, and there is no force-review.
  • Tests: near reset arms exactly one retry; far reset arms none; SHA change, standing verdict, merged, closed and missing marker all no-op; concurrency group; DRY_RUN; shared budget; dedup; sleep floor; base-10 parsing.

Note: #1994 was closed manually as completed (2026-10-01T21:46Z) before this PR merged. The PR's Closes #1994 is harmless, but the issue state does not yet reflect a merged fix.

Findings

No blocking findings. Unresolved review threads: 0. No unanswered human questions; all later don-petry comments are automation dispositions.

Non-blocking notes:

  • info: horizon/ceiling coupling. The sweep reads DELAYED_RETRY_HORIZON_SEC from its own env. The retry script derives its sleep floor from its own copy of the variable (default 3600), which is not passed as a workflow input. If someone later raises the horizon only on the sweep side, the retry could wake before reset. It would also hit timeout-minutes: 70 once horizon+buffer gets above ~68 min. Either way the delegated sweep would re-defer without re-arming, which falls back to the cron. Not reachable with today's defaults (neither workflow overrides the variable). Consider passing the horizon through as an input, or adding a comment that both sides must change together.
  • info: Dedup keys on (PR, head), not (PR, head, reset), and only counts queued/in_progress runs (not waiting/pending). The worst case is one redundant arm, which cancel-in-progress collapses, or a cron pickup.
  • info: run-name and concurrency.group interpolate inputs.pr_url/inputs.head_sha. These are not shell sinks, and only users with write access can dispatch, so there is no injection surface.

CI status

All substantive checks pass at 481142d1: Lint, bats, shellcheck/ShellCheck, unit-tests, unit, actionlint, CodeQL (actions, python), SonarCloud (Quality Gate passed, 0 new issues), Secret scan (gitleaks), AgentShield / Agent Security Scan, validate-interaction-contracts, validate-interaction-model, template-drift, reusable-pin-compliance, toplevel-permissions, concurrency-config and gh-aw-compile.

The guard / sync-scope-guard entries show one cancelled run, but a later run of each succeeded. Still-queued rollup entries (dev-lead dispatch/resume, Dismiss, pr-auto-review, review / review) are automation triggers with completed-success siblings, not CI gates.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

@donpetry-bot donpetry-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Automated review — APPROVED ✓

Risk: MEDIUM
Reviewed commit: 481142d140a34fa5b7c4ffef37afdd2b1938f8c0
Review mode: triage-approved (single reviewer)

Summary

Fixes #1994. When the sweep defers a PR whose rate-limit reset is within DELAYED_RETRY_HORIZON_SEC (default 3600s), it now dispatches a new workflow_dispatch-only workflow, pr-review-delayed-retry.yml. That workflow sleeps until reset + buffer, checks that the PR is still open, at the same head and still carrying the rate-limit marker, then hands the PR back to the sweep with ARM_DELAYED_RETRY=false. It also corrects the docs and contract wording that called the cron a 'guaranteed ≤15-min backstop'. Confirms the triage-approved assessment: MEDIUM, approvable.

Linked issue analysis

#1994's acceptance criteria are addressed:

  • Near reset arms a retry; far reset does not: arming happens only when 0 < delta ≤ horizon, and a reset past the horizon is left to the cron.
  • Not schedule-based, with stated cost: dispatch-only workflow; the PR body gives the design choice and the runner-minute cost (≤ ~62 min sleep, capped by timeout-minutes: 70).
  • Concurrency: the group is per-(pr_url, head_sha), so retries for different PRs never cancel each other or the full sweep. A gh run list displayTitle check skips re-arming while one is queued or running.
  • Idempotent and bounded:
    • SHA supersession guard runs both before and after the sleep.
    • The marker re-check stops a plain review from being forced.
    • Delegating to the sweep keeps its existing exclusions and standing-verdict checks.
    • Arms count against the shared MAX_DISPATCH budget.
    • No force flag is passed.
  • Comment correction: the 'guaranteed ≤15 min' wording is fixed in pr-review-sweep.yml, AGENTS.md, docs/agentic-interaction-model.md and interaction-contracts/pr-review.yml.
  • Tests: new tests/test_delayed_rate_limit_retry.bats plus additions to tests/test_sweep_stuck_reviews.bats, wired into lint.yml.

Note: #1994 already shows CLOSED although this PR is not merged. The PR's Closes #1994 is still correct.

Findings

No blocking findings.

  • Security: The PAT handling in the new workflow is the same as the existing pr-review-sweep.yml: GH_PAT_DON_PETRY || GH_PAT_WORKFLOWS passed through env, with a fail-closed check that the PAT is bound to don-petry. Permissions are contents: read, and checkout is pinned to the same v7.0.1 SHA used elsewhere in the repo. Workflow inputs reach the script only through env (run-name and concurrency use them only as expressions), so there is no shell-injection path.
  • Correctness:
    • The sleep ceiling has a floor of horizon + buffer, so the retry always wakes after reset.
    • The base-10 coercion avoids octal misreads.
    • An unparseable NOT_BEFORE proceeds immediately, so the PR is not stranded.
    • The in-flight dedup query fails open.
    • A failed arm still counts against MAX_DISPATCH, which prevents a retry storm.
  • Review threads: none unresolved. Earlier codeant, gemini, cubic and graphite comments were addressed in prior cycles, and cubic reports 'All reported issues were addressed'.
  • Docs: AGENTS.md now lists the new repo-specific workflow as an exception, and the §4 classification table and interaction contract are updated.
  • Secret scan: the MCP run_secret_scanning tool is not available in this session. The gitleaks CI check passed.

CI status

All substantive checks pass: Lint, bats, unit-tests, ShellCheck, actionlint, CodeQL, SonarCloud (Quality Gate passed, 0 new issues), gitleaks, Agent Security Scan, and the stub/permissions/contract validators. Earlier cancelled runs of guard and sync-scope-guard have later successful runs. The only queued or pending entries are the review automation itself (pr-auto-review, review/review, dev-lead dispatch/resume), not CI gates.


Reviewed automatically by the PR-review agent (single-reviewer mode: opus 5.5 [opus 4.8, opus 4.7]). Reply if you need a human review.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants