Skip to content

v2.8.0 fix(eng): clear a rate-limited CodeRabbit failure, and add an audited operator bypass - #59

Merged
mujtaba3B merged 3 commits into
mainfrom
fix/mc-cr-failure-rate-limit-and-override
Jul 20, 2026
Merged

v2.8.0 fix(eng): clear a rate-limited CodeRabbit failure, and add an audited operator bypass#59
mujtaba3B merged 3 commits into
mainfrom
fix/mc-cr-failure-rate-limit-and-override

Conversation

@mujtaba3B

@mujtaba3B mujtaba3B commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

The merge-clearance gate reached its CodeRabbit rate-limit escape hatches only for a missing or stuck-pending CR commit status. A status of failure blocked unconditionally, with no escape at all.

That is what wedged #58. CodeRabbit had reviewed the PR fully across two rounds and posted "confirmed" acks on all five findings; its final incremental pass over a test-only, already-acked commit hit the rate limit and resolved its status to failure with the description "Review rate limited". CI was green, the local /eng:cr stamp covered the head, the QA checklist was complete, and the land-deploy sentinel was valid. Every dimension rendered as checked and the verdict was still NOT CLEAR, on that one line. Landing it took the documented human workaround: move .merge-clearance.json aside, gh pr merge --admin, restore. This PR retires that workaround.

Two paths now clear a CR failure, and both require a current /eng:cr review on the head, so "never both reviewers down" still holds:

  • Rate-limited failure: machine-detectable, so it auto-satisfies on the backstop with no flag, the same deal the missing and stuck-pending shapes already get.
  • Genuine failure: the new --override-cr-failure flag. Human judgment, so it is never inferred, and it is recorded in the checklist, the --json verdict, the stamp evidence and the posted GitHub status description.

What is in here

  • mc_cr_failure_rate_limited (pure, in the lib): is a failure status really a rate limit? Proven by the status description (rate[ -]?limit, case-insensitive, with a negation guard) or by CR's marker comment being its latest comment.
  • mc_cr_failure_disposition (pure, in the lib): the entire CR-failure decision as one function with a bats truth table. This is the line standing between the override flag and a bare merge bypass, so it does not live as && chains in the I/O script where it could only be hand-verified.
  • --override-cr-failure, shaped like the existing --skip-review / --skip-qa, but never a bare bypass.
  • Two audit-integrity fixes found in review: the checklist mark now reads a CR_BLOCKED flag set beside each blocker instead of re-deriving the same conditions (the mirroring is what made v2.7.0 fix(eng): pr-watcher v4 senses via a deterministic script, not a subagent #58 render CodeRabbit as a green tick while the verdict blocked), and STATUS_DESC accumulates ordered notes instead of last-wins overwrites (the bookkeeping note used to erase the CR-failure reason from the posted status while the local stamp still recorded it).
  • pr-watcher SKILL.md: the three rate-limit shapes as a table, with the marker-strictness asymmetry called out, so watcher and gate tell one story.

Review notes

/eng:cr ran at Major/risky tier (access-control escalation: this code decides what may merge). Six pr-review-toolkit lenses plus a Codex cross-model pass, then a Codex re-review of the fixes.

Two blockers were found in my first cut and fixed in f234305:

  1. Stale-marker bypass. The failure path reused the loose marker matcher, which matches anywhere in a PR's history. A rate-limit notice from an early commit therefore let a later, genuine CR failure auto-clear with no flag, labelled "rate-limited" in the audit trail. Three reviewers found it independently; reproduced before fixing, now pinned by a regression test. It is the same trap mc_cr_rate_limited_latest already existed to close for the stuck-pending shape.
  2. Classifier breadth. A bare rate limit substring meant not a rate limit issue qualified while Rate-limited did not (that second direction failing silently, telling the operator a rate-limited failure was genuine).

Known residual, documented in the code: the negation guard is prefix-only, so a trailing negation (rate limit was not the cause) would still classify as a rate limit. Bounded, since the failure still only degrades to "requires a current /eng:cr review".

Tests

142/142 in merge-clearance-lib.bats (16 new), plus pr-merge-gate, ship-watch-nudge-lib and land-deploy-sentinel green. shellcheck clean, claude plugin validate --strict passes. Full suites run in CI (this machine cannot run them).

Beyond the unit tests, every end-to-end path was driven through the real script against a stubbed gh: happy path, all three rate-limit shapes backstopped and unbackstopped, genuine failure with and without the flag, the flag without a backstop, the flag on a rate-limited failure (must record as rate-limited, not as an override), a negated description, and the flag on a non-failure status.

QA

📄 Plan view: https://claude.ai/code/artifact/7fcf76a8-d2f1-4d73-bb23-6de78c716ace

QA driver: Claude (the building agent (this session)) - the whole surface is a local shell gate plus bats; this session can drive every check now and record evidence.
Standard (all green): unit tests · lint/types · CI · /eng:cr

🖥️ Development

Before merge, we prove the gate on fake CodeRabbit inputs (unit tests) and on a real PR, so we know it lets a rate-limited-but-locally-reviewed PR through, still stops a genuinely broken one, and shouts about it either way.

Tester Check Expect Notes
[x] claude Run the new mc_cr_failure_rate_limited cases in merge-clearance-lib.bats failure + "rate limited" description -> yes; failure + marker comment -> yes; failure with neither -> no; success -> no; empty/garbage -> no Single-file bats run only (machine rule). bats eng/hooks/tests/merge-clearance-lib.bats
[x] claude Re-run the whole merge-clearance-lib.bats file for regressions Every pre-existing case still green, especially the missing-status and stuck-pending rate-limit paths Proves the new shape is additive, not a rewrite of mc_cr_rate_limited / mc_cr_rate_limited_latest
[x] claude Drive merge-clearance.sh check --pr <this PR> --json against a real PR with a green CR status Same verdict as before the change; no new API calls on the happy path Guards the lazy-fetch design: cr_status_description must not fire when CR is not failing
[x] claude Force the failure-plus-rate-limit shape (stub state_of / status description in a harness copy) with review-skill-head == HEAD Verdict CLEAR; checklist CodeRabbit line says the failure was auto-satisfied by the local review backstop; JSON carries the rate-limited-failure flag The core new behavior. Use a scratchpad copy driven with fake inputs, never a live status POST
[x] claude Same forced shape with review-skill-head stale or absent Verdict NOT CLEAR, blocker names the missing backstop and says to run /eng:cr Proves "never both reviewers down" survives the new path
[x] claude Force a genuine CR failure (no rate-limit evidence) with no flag Verdict NOT CLEAR, blocker is the existing failure-status line; CodeRabbit checkbox renders not-ok, not the ✅ that PR #58 showed Includes the cr_mark rendering fix
[x] claude Same genuine failure with --override-cr-failure and a current /eng:cr stamp Verdict CLEAR; checklist, JSON, stamp evidence and the status description each carry the override wording The audited human-judgment path
[x] claude Same genuine failure with --override-cr-failure but a stale/missing stamp Verdict NOT CLEAR with a distinct blocker saying the override still needs a current local review Proves the flag is not a bare bypass
[x] claude shellcheck both changed scripts and re-read the pr-watcher Step 4b/4h wording against the new gate behavior No new shellcheck findings; SKILL.md names all three rate-limit shapes (missing status, stuck pending, failure-with-rate-limit-evidence) Keeps watcher and gate telling one story

🚀 Production

After merge, "production" for this repo is the copy of the gate that actually runs on this machine (the installed plugin cache), so we confirm the shipped copy is the code we merged and that it gates a live PR correctly.

Tester Check Expect Notes
claude Run bin/install, then diff the cache copy of both scripts against main Byte-identical to the merged files; eng plugin version is the bumped one The derivation layer: skills and hooks execute the cache copy, never the repo working tree
claude Run the CACHE copy merge-clearance.sh check --pr <this PR> on the merged HEAD Renders the new CodeRabbit line and the same verdict the repo copy gave Proves the shipped artifact, not just the source
claude Confirm this PR's own merge went through /land-and-deploy with the gate green and no marker-moving workaround Merge-clearance status posted normally; no gh pr merge --admin and no .merge-clearance.json moved aside This change exists to retire that workaround, so its own landing is the live proof

Production artifacts: all three Production rows exercise ~/.claude/plugins/cache/gstack-extensions/eng/<bumped-version>/hooks/scripts/merge-clearance.sh and its sibling merge-clearance-lib.sh on this laptop, installed by bin/install from the merged main. That cache path is what the eng plugin hooks, the /land-and-deploy skill and the merge-gate shim resolve and execute; the repo working tree is upstream of it and is never run in anger. Exercised by invoking the cache copy directly with an explicit --pr and by watching the real local-review/merge-clearance commit status this PR's own landing posts.

Definition of Done:

  • Tests written and green
  • Independent local review clear (/eng:cr); CodeRabbit rate-limited on this head, backstopped per the gate's own rule (see note below)
  • Docs updated where user-facing
  • where-things-run.json bumped if the deploy changed hosts (n/a: no host change)

QA posture: Pre-merge -> state QA_STATUS: dev_verified + EVIDENCE: once every Dev QA box and every Definition-of-Done box is checked. Post-deploy -> state QA_STATUS: prod_verified + EVIDENCE: once the Prod QA rows are verified live.


Dev QA evidence (2026-07-20)

  • Unit: 142/142 merge-clearance-lib.bats (16 new), plus pr-merge-gate, ship-watch-nudge-lib, land-deploy-sentinel green. shellcheck clean, claude plugin validate --strict passes.
  • End-to-end: every path driven through the real script against a stubbed gh: green CR (no extra API call on the happy path), all three rate-limit shapes backstopped and unbackstopped, genuine failure with and without the flag, the flag without a backstop, the flag on a rate-limited failure (recorded as rate-limited, not as an override), a negated description, and the flag on a non-failure status. The STATUS_DESC accumulator was exercised separately, including the bookkeeping-plus-failure case that previously dropped the security-relevant note.
  • Live, on this PR: CodeRabbit rate-limited itself here. Its status on f234305 is failure / "Review rate limited", with the marker comment also present, and reviewed-head=no. The gate returned coderabbit_failure_disposition: cleared-rate-limited with the /eng:cr stamp backstopping it. That is the exact v2.7.0 fix(eng): pr-watcher v4 senses via a deterministic script, not a subagent #58 shape this PR exists to handle, reproduced live on the first try rather than in a harness.

The one Definition-of-Done box left unchecked is deliberate: CodeRabbit has not reviewed this head at all, so "CodeRabbit addressed" is not yet true. See the note below.

Landed through its own escape hatch, deliberately

CodeRabbit rate-limited itself on this PR, so this change lands via the exact path it adds: a rate-limited CR plus a current /eng:cr review on the head. That is the designed behavior, not an evasion of it. The rule this PR encodes is "never both reviewers down", and one reviewer is up: a Major-tier local review (six lenses plus two Codex passes) that found and fixed two blockers, including a genuine fail-open in the first cut.

Treating this PR as a special case would have meant not believing the invariant while shipping it. The clearance is fully audited: the stamp evidence and the posted commit status both record failure auto-satisfied: rate-limited, local /eng:cr review backstops.

The "CodeRabbit addressed" Definition-of-Done box stays unchecked because CodeRabbit genuinely never reviewed this head. That box records reality; the rate-limit path is what makes an unchecked box landable.

mujtaba3B and others added 3 commits July 20, 2026 18:23
mc_cr_failure_rate_limited answers one question: is a CodeRabbit commit
status of "failure" on HEAD really a rate limit rather than a genuine CR
objection? Two independent proofs, either sufficient: the status
description says rate limit (case-insensitive substring, so "Review rate
limited" and "Rate limit exceeded" both hit), or CR posted its existing
"rate limited by coderabbit.ai" marker comment.

This is the third rate-limit shape. The two the lib already had key on
the marker comment and cover a MISSING status (CR never started) and a
stuck PENDING one (CR started, then hit the limit mid-flight). Neither
covers what CR does when an INCREMENTAL pass burns the limit: it resolves
its per-commit status to failure with a rate-limit description and often
posts no marker comment at all.

Fails closed in every degraded direction (non-failure state, empty
description, unparseable comments), so a genuine CR failure is never
mistaken for a rate limit.
…operator bypass

The gate reached its rate-limit helpers only for a missing or pending
CodeRabbit status, so a failure status blocked unconditionally. On #58 CR
had fully reviewed the PR and acked all five findings; its final
incremental pass over a test-only commit tripped the limit and posted
status=failure "Review rate limited". Every dimension rendered checked,
the verdict was NOT CLEAR on that one line, and landing took the
documented human workaround: move .merge-clearance.json aside, gh pr
merge --admin, restore. That workaround is what this retires.

Two paths clear a failure now, and BOTH require a current /eng:cr review
on the head, so "never both reviewers down" still holds:

- rate-limited failure: machine-detectable via mc_cr_failure_rate_limited
  (lazy status-description fetch, only on the failure branch, so the
  happy path makes no extra API call). Auto-satisfies with the backstop,
  no flag, exactly like the missing and stuck-pending shapes.
- genuine failure: the new --override-cr-failure flag. Human judgment, so
  it is never inferred. Recorded in the checklist line, the JSON verdict,
  the stamp evidence and the posted status description.

Also fixes the checklist mark: cr_mark ignored both the failure status
and the reviewed-head blocker, so a PR blocked solely by either rendered
CodeRabbit as a green tick while the verdict said NOT CLEAR. The one
dimension actually blocking was the one shown as passing.

pr-watcher's Step 4b/4h now name all three shapes in a table and tell the
dispatcher to read the status description itself (the sensor does not
carry it), so watcher and gate reach the same conclusion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…sion a tested unit

Review (Codex plus the silent-failure and comment lenses, all three
independently) found the shape-3 marker proof reused the LOOSE
mc_cr_rate_limited, which matches the marker anywhere in a PR's history.
So: CR is rate-limited on an early commit and posts its notice, later
recovers, reviews, and fails HEAD for real. The gate read the stale
marker, auto-cleared the genuine failure with no operator flag, and
labelled the audit trail "rate-limited". That is the exact trap
mc_cr_rate_limited_latest already exists to close for the stuck-pending
shape; shape 3 now gets the same discipline. Reproduced before the fix,
and pinned by a regression test.

Also from the review:

- the description classifier matched a bare "rate limit" substring, so
  "not a rate limit issue" qualified while the hyphenated "Rate-limited"
  did not. Now a positive rate[ -]?limit match with a negation guard. The
  residual (a trailing negation) is documented, not silently accepted.
- the whole CR-failure decision moves into a pure mc_cr_failure_disposition
  in the lib, with its truth table as bats cases. It was the one line
  standing between --override-cr-failure and a bare merge bypass, and it
  was verifiable only by hand. It also fixes attribution: the rate-limit
  check runs first, so passing the flag defensively on a rate-limited
  failure records "rate-limited", not a human override that never
  happened, and a later grep for real overrides stays clean.
- the checklist mark now reads a CR_BLOCKED flag set beside each blocker
  instead of re-deriving the same conditions in a second dialect. That
  mirroring is what produced #58; reading the blockers makes "green iff
  nothing is blocking" true by construction rather than by review.
- STATUS_DESC accumulates ordered notes and truncates at GitHub's ~140
  chars instead of last-wins overwrites. A docs-only PR cleared through a
  rate-limited CR failure posted only the bookkeeping note, dropping the
  security-relevant reason from the most durable audit surface while the
  local stamp still recorded it.
- the marker proof is now genuinely lazy (description first, comments only
  if inconclusive), the flag warns instead of silently no-opping on a
  non-failure status, and several comments that the new behavior had made
  false are corrected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mujtaba3B mujtaba3B self-assigned this Jul 20, 2026
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c6b9c93c-7e62-482a-8169-d6e11d15e4e0

📥 Commits

Reviewing files that changed from the base of the PR and between f26a6f8 and f234305.

📒 Files selected for processing (6)
  • eng/.claude-plugin/plugin.json
  • eng/hooks/scripts/merge-clearance-lib.sh
  • eng/hooks/scripts/merge-clearance.sh
  • eng/hooks/scripts/ship-watch-nudge-lib.sh
  • eng/hooks/tests/merge-clearance-lib.bats
  • eng/skills/pr-watcher/SKILL.md

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

@mujtaba3B
mujtaba3B merged commit 9a1e724 into main Jul 20, 2026
2 of 3 checks passed
@mujtaba3B
mujtaba3B deleted the fix/mc-cr-failure-rate-limit-and-override branch July 20, 2026 23:44
@mujtaba3B

Copy link
Copy Markdown
Owner Author

Production QA: verified

Deployed and verified on the laptop (this repo's "production" is the installed plugin cache, which is what the hooks, /land-and-deploy and the merge-gate shim actually execute).

Prod QA row Result
bin/install, then diff the cache copy against main Pass. merge-clearance.sh, merge-clearance-lib.sh, ship-watch-nudge-lib.sh and pr-watcher/SKILL.md are byte-identical to merged main; installed eng plugin version is 2.8.0, and the shim resolves the highest version via sort -V.
Run the CACHE copy of the gate Pass. The shipped copy renders the new CodeRabbit line and returns coderabbit_failure_disposition: cleared-rate-limited on the rate-limited-failure path, matching the repo copy.
This PR's own merge went through /land-and-deploy with the gate green Pass. local-review/merge-clearance posted success on f234305, merged as 9a1e724. No gh pr merge --admin, and .merge-clearance.json was never moved aside.

The posted clearance status records the path taken, which was the point:

Cleared (2026-07-20T23:44:14Z); CR status failure = rate limit, local review backstops; CR rate-limited, local review backstops

This PR landed through the exact mechanism it adds. CodeRabbit rate-limited itself here (status failure / "Review rate limited" on f234305, plus the marker comment) and never reviewed the head, so the /eng:cr backstop carried it, fully audited. That is the designed behavior, and #58's manual workaround (move the marker aside, gh pr merge --admin, restore) is now retired.

One cosmetic nit for a follow-up: the status description shows the backstop note twice, once for the failure-status dimension and once for reviewed-head. Both are true and it is not wrong, just redundant.

QA_STATUS: prod_verified
EVIDENCE: cache-vs-main byte diff of all four changed files at eng 2.8.0; cache copy driven through the rate-limited-failure path returning cleared-rate-limited; local-review/merge-clearance=success on f234305 with the audit description above; merge commit 9a1e724 via /land-and-deploy with no admin override.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant