fix(dev-lead): stop false "rate-limited" claims; dedupe superseded check runs - #462
Conversation
Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 57 minutes and 26 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR refines CI blocker detection and retry messaging in the dev-lead automation script. Check-run deduplication now groups by check name and GitHub App id to avoid treating cancelled-then-replaced runs as persistent blockers. The rate-limited marker function is extended with a ChangesBlocker deduplication and signaling
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
Dev-Lead — review-changes (no-changes)No changes were needed for this PR. |
There was a problem hiding this comment.
Code Review
This pull request addresses issue #461 by implementing name-level deduplication for GitHub check runs, preventing superseded concurrency-cancelled runs from acting as permanent blockers. It also refactors the rate-limiting logic to distinguish between genuine rate limits and PR blockers, providing more accurate user-facing messages and a 30-minute backoff retry. A critical issue was identified in the jq filter used for deduplication, where composite sorting keys were separated by a comma instead of being wrapped in an array, which leads to incorrect sorting behavior.
There was a problem hiding this comment.
Pull request overview
This PR fixes dev-lead’s incorrect “all AI engines are currently rate-limited” messaging when the real cause is PR hard blockers, and improves CI blocker detection by deduplicating superseded check runs (by name) so stale concurrency-cancelled runs don’t cause endless retry loops.
Changes:
- Deduplicate commit check-runs by check name (keeping the newest) when building CI context for blocker detection.
- Extend
post_reviews_rate_limitedto accept areason(rate-limitvsblocked) so user-facing messaging is accurate while preserving thestatus=rate-limitedmarker token for retry automation. - Expand unit coverage in
test_fix_reviews.batsfor both messaging correctness and the new check-run dedupe behavior.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
scripts/dev-lead-fix-reviews.sh |
Adds check-run dedupe logic and improves rate-limit/backoff marker posting to distinguish genuine rate limits from PR blockers. |
tests/dev-lead/unit/test_fix_reviews.bats |
Adds/extends tests to validate correct marker tagging/wording and dedupe behavior for superseded vs lone cancelled check runs. |
donpetry-bot
left a comment
There was a problem hiding this comment.
Automated review — APPROVED ✓
Risk: LOW
Reviewed commit: 9460e2a34108acb702282d73210ae28ac7c2b165
Review mode: triage-approved (single reviewer)
Summary
Targeted, well-scoped fix for the two defects described in issue #461:
- Check-run dedup by name in
fetch_pr_context—[.check_runs[]?] | group_by(.name) | map(sort_by(.started_at // "", .id // 0) | last | …)collapses across check suites so a concurrency-cancelled run is no longer left sitting next to its successful same-named replacement. Mirrors the legacy-statuses dedup already present in the same function. A lone cancelled run (genuinely the latest of its name) still blocks — covered by a dedicated regression test. - Honest blocked-retry messaging —
post_reviews_rate_limitedgains areasonparam (rate-limitdefault |blocked). The blocked path posts## Dev-Lead — waiting on PR blockersand a candid review-changes ack instead of falsely claiming the engines are rate-limited. The machine-readablestatus=rate-limitedmarker token is preserved (the retry cron keys on it); a new informationalreason=field is appended. The+30minbackoff write is hoisted from the two duplicated call sites into the function.
Linked issue analysis
Fixes #461 — addresses both root causes the issue identified (the false "all AI engines are currently rate-limited" ack on the hard-blocker backoff path, and the superseded-cancelled check-run accumulation). The PR description also acknowledges the known limitation around a lone cancelled check still cycling — left out of scope as pre-existing (#426).
Findings
No blocking issues.
One prior comment investigated and dismissed. @gemini-code-assist flagged the jq composite sort as critical, claiming sort_by(.started_at // "", .id // 0) should be wrapped in an array (sort_by([…])) to sort correctly. This is wrong: jq evaluates the comma-separated stream inside sort_by as a multi-key tuple, and the two forms produce identical orderings. Verified directly against jq 1.7 with the test fixture (three same-named runs, two cancelled, one newer success): the PR's filter correctly returns only the newest successful run. The bats test superseded cancelled check run is not a hard blocker pins this end-to-end.
Minor observations (non-blocking):
- Marker compatibility claims in the PR description (dev-lead-retry.sh substring match on
status=rate-limited, e2e scenario 07 loose greps, dev-lead-fix-ci.sh markers untouched) match the change shape — thereason=field is appended afterstatus=rate-limited, so existing scanners that key on the status token are unaffected. - The
30 minutesbackoff value moves intopost_reviews_rate_limitedbut stays a hard-coded literal; centralizing it is the right call, parameterizing further is out of scope here.
CI status
All required checks passing — bats (295/295 per PR description), shellcheck, ShellCheck, Lint, unit-tests, validate-agent-profiles, gh-aw-compile, Compile agentic workflows, CodeQL, Agent Security Scan, Secret scan (gitleaks), AgentShield, SonarCloud, dependency-audit. SonarQube Cloud quality gate passed (0 new issues, 0 security hotspots). Mergeable; BLOCKED only on the org-leads review requirement.
Reviewed automatically by the PR-review agent (single-reviewer mode: opus 4.7). Reply if you need a human review.
Dev-Lead — fix-reviews (applied)Changes committed and pushed. |
c8e8774
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9460e2a341
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Dev-Lead — review-changes (applied)Changes committed and pushed. |
Dev-Lead — fix-bot-comment (no-changes)Agent reasoning |
Dev-Lead — review-changes (no-changes)No changes were needed for this PR. |
Dev-Lead — waiting on PR blockers (intent: review-changes)PR: #462 |
|
Note @don-petry I reviewed this PR and no code changes were needed, but it still has blocking checks or reviews (failing or cancelled checks, or changes-requested reviews), so I cannot mark it done yet. I'll re-check automatically. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7290be6c7a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Dev-Lead — fix-reviews (applied)Changes committed and pushed. |
Dev-Lead — review-changes (no-changes)No changes were needed for this PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 559d868d74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@don-petry assigned me as reviewer — starting a fresh review now. Results will appear in a few minutes. |
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
…eck runs (#462) * fix(dev-lead): honest blocked-retry messaging and check-run dedup Fixes #461 — two defects that combined to falsely tell users "all AI engines are currently rate-limited" (PR #453 incident): 1. fetch_pr_context now dedupes check runs by name, keeping the newest (started_at, then id). Each triggering event creates its own check suite, so the API's filter=latest never collapses runs across suites: a concurrency-cancelled run sat forever next to its successful same-named replacement and registered as a permanent Tier-1 blocker. Mirrors the legacy-statuses dedup a few lines below. A lone cancelled run (latest of its name) still blocks. 2. post_reviews_rate_limited gains a reason param (rate-limit|blocked). The hard-blocker backoff path now posts "waiting on PR blockers" wording and an honest review-changes ack instead of claiming the engines are rate-limited. The machine-readable status=rate-limited marker token is unchanged (dev-lead-retry.sh keys on it); a new reason= field records the real cause. The +30min backoff write moves from the two call sites into the function. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(reviews): address review comments [skip ci-relay] * chore: apply manual instructions [skip ci-relay] * fix(reviews): address review comments [skip ci-relay] * docs(dev-lead): document deliberate cross-suite Stage-2 dedup + pin with test Addresses the cycle-2 review finding (Codex P2, scripts/dev-lead-fix-reviews.sh Stage 2 key wider than Stage 1): the asymmetry is intentional and load-bearing. A concurrency-cancelled run is always superseded from a different check suite (the PR #453 incident shape), so requiring suite equality in Stage 2 would never drop anything and would reintroduce issue #461's endless retry loop. The check-runs API exposes no workflow identity, so a cancelled check from a sibling workflow sharing name+app with a newer success is also dropped — accepted trade-off matching GitHub's own required-check gate, which keys on the latest same-named run (PR #453 merged with stale cancelled runs still on its head SHA). Failures remain exempt from Stage 2. Adds the missing test the review called out: cancelled across distinct suites of the same app with a newer same-named success → dropped, no blocker. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: donpetry-bot <{}+donpetry-bot@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: donpetry-bot <281750570+donpetry-bot@users.noreply.github.com>
Summary
Fixes #461 — dev-lead told users "all AI engines are currently rate-limited" when no engine was rate-limited (PR #453 incident, comment 4642769821). Two defects combined:
1. Superseded cancelled check runs counted as permanent Tier-1 blockers
fetch_pr_contextbuiltCI_STATUS_JSONfromGET /commits/{sha}/check-runswith no dedup by name. Each triggering event creates its own check suite, so the API's defaultfilter=latestnever collapses runs across suites — on PR #453, two concurrency-cancelledreviewruns sat forever next to their successful same-named replacement, andhas_hard_blockerscountedcancelled→ endless 30-minute retry cycling.Fix: dedupe check runs by name, keeping the newest (
started_at, thenid). This mirrors the legacy-statuses dedup already present a few lines below (same rationale, same function), and matches GitHub's own merge semantics — branch protection keys required checks by name. A lone cancelled run (genuinely the latest of its name) still blocks.2. The hard-blocker backoff path reused the rate-limit messaging verbatim
The "engine ran fine, no changes, but hard blockers remain" path called
post_reviews_rate_limitedand posted the literal "all AI engines are currently rate-limited" ack with a fabricatednow+30min"reset" time.Fix:
post_reviews_rate_limitedgains areasonparam (rate-limitdefault |blocked). The blocked path now posts "waiting on PR blockers" marker wording and an honest review-changes ack ("no code changes were needed, but it still has blocking checks or reviews…"). The machine-readablestatus=rate-limitedmarker token is unchanged —dev-lead-retry.shkeys its re-dispatch scan on it — and a new informationalreason=field records the real cause. The+30minbackoff write moves from the two duplicated call sites into the function.Compatibility verified
dev-lead-retry.shmarker patterns are substringtest()matches ending atstatus=rate-limited; thereset=capture is position-independent — both parse the newreason=-tagged format (verified directly).dev-lead-fix-ci.shmarkers untouched (it never scans/classifies the commit's check-run list).Tests
reason=blocked, the "waiting on PR blockers" wording, the keptstatus=rate-limitedtoken, and assert the false "all AI engines…" claim is gone; genuine rate-limit test pinsreason=rate-limitand unchanged wording.bats tests/dev-lead/unit/— 295/295 pass;shellcheck --severity=warningclean; both CI integration tests pass.Known limitation (pre-existing, out of scope)
A lone cancelled check (nothing newer of the same name) still cycles 30-minute retries without anything re-triggering the cancelled workflow — the retry re-runs the engine, not the check. Pre-dates this change (#426); this PR shrinks its blast radius to genuinely-latest cancellations only.
🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes