--record-review will bind a fallback receipt to the current head even when the configured review bot has not finished — and decide_done then reports done: true, allowing a merge.
Observed on #16. The receipt was recorded while CodeRabbit's status check read PENDING — Review queued. done flipped true, the PR merged, and CodeRabbit's review — four valid findings, two of them real logic bugs in the /upgrade skill that PR added — landed minutes later, post-merge. They needed a follow-up PR (#17).
Why the gate couldn't catch it. Every input was correct at the moment of the poll: CI green, no unacked comments, a receipt bound to the exact head. decide_done has no way to distinguish "no review is coming" from "a review is queued and will arrive shortly". The operator judgment that failed — treating a slow bot as an unavailable one — is exactly the judgment the doctrine says to make, and it is currently unmechanized.
Note the confounder: CodeRabbit's check reports state: SUCCESS even when rate-limited, with the real story only in its description ("Review rate limited") and comment body. So check state is not a usable signal on its own; the existing unavailable_markers body-matching is what actually works today.
Candidate fix, and the reason it needs care. Treat a configured bot's own status check sitting PENDING as a merge blocker while no receipt exists for the current head. That deliberately inverts _INFORMATIONAL_CHECK_NAMES in one narrow case — and that exclusion is load-bearing: it is what stops the watch loop wedging forever on a review bot that never reports after a trivial follow-up commit. So the condition has to be "pending AND no receipt yet", never "pending" alone, and it probably wants a bound (accept the fallback after N minutes of pending) so a genuinely dead bot can't block indefinitely.
Cheaper interim mitigation: have --record-review refuse, or at least warn loudly, when a check whose name is in review.informational_checks is currently PENDING — pushing the operator to re-read the state rather than silently accepting the receipt.
--record-reviewwill bind a fallback receipt to the current head even when the configured review bot has not finished — anddecide_donethen reportsdone: true, allowing a merge.Observed on #16. The receipt was recorded while CodeRabbit's status check read
PENDING — Review queued.doneflipped true, the PR merged, and CodeRabbit's review — four valid findings, two of them real logic bugs in the/upgradeskill that PR added — landed minutes later, post-merge. They needed a follow-up PR (#17).Why the gate couldn't catch it. Every input was correct at the moment of the poll: CI green, no unacked comments, a receipt bound to the exact head.
decide_donehas no way to distinguish "no review is coming" from "a review is queued and will arrive shortly". The operator judgment that failed — treating a slow bot as an unavailable one — is exactly the judgment the doctrine says to make, and it is currently unmechanized.Note the confounder: CodeRabbit's check reports
state: SUCCESSeven when rate-limited, with the real story only in itsdescription("Review rate limited") and comment body. So check state is not a usable signal on its own; the existingunavailable_markersbody-matching is what actually works today.Candidate fix, and the reason it needs care. Treat a configured bot's own status check sitting
PENDINGas a merge blocker while no receipt exists for the current head. That deliberately inverts_INFORMATIONAL_CHECK_NAMESin one narrow case — and that exclusion is load-bearing: it is what stops the watch loop wedging forever on a review bot that never reports after a trivial follow-up commit. So the condition has to be "pending AND no receipt yet", never "pending" alone, and it probably wants a bound (accept the fallback after N minutes of pending) so a genuinely dead bot can't block indefinitely.Cheaper interim mitigation: have
--record-reviewrefuse, or at least warn loudly, when a check whose name is inreview.informational_checksis currentlyPENDING— pushing the operator to re-read the state rather than silently accepting the receipt.