cujo
Skip to content
Contents

Blocking and the unlock

A critical finding blocks the merge at once, through a check run nobody can dismiss. Who may lift it, and how.

The verdict comes from the tool, not from the model

The agent chooses which of two tools to call. Which tool it called is what makes a review advisory or blocking — so a model cannot talk its way to a softer verdict after the fact, and nothing waits for a person before it posts.

SituationWhat posts
No critical findingA comment review, with the findings and the inline comments.
Any critical findingREQUEST_CHANGES, at once. A broken test, a probe that contradicts the diff, a decoy secret read, an install that called an unknown host: all of them block the same way.

Cujo never posts APPROVE. It cannot satisfy branch protection, so it can never wave a bad merge through by staying quiet.

The lock is a check run

A review can be dismissed by anyone with write access — a coding agent with write access included. A check run cannot be dismissed at all: only the App that owns it can complete it. So beside every review, Cujo writes a check run named cujo/guard on the commit it reviewed, and moves it as the run moves: in progress from the moment the commit is claimed, success on a clean run, failure on a block.

To make the block hold the merge, require the check in branch protection: Require status checks to pass, and pick cujo/guard. The check appears in the picker once the App has written one. Without that setting a block is still a REQUEST_CHANGES review and a red mark on the commit, and nothing more.

The check needs the Checks: write permission on the App, which every installation has to approve once. Until it has, the review and the reaction post as before and the check is not written.

The unlock, on the pull request

A block is lifted by a person, and only by a person. Two verbs exist, each alone on its own line in a comment. They are matched as exact strings by the service and never by a model, and a line inside a code fence, a blockquote or an HTML comment does not count — if a reader cannot see it, it is not a command.

CommandDoesWho may
/cujo dismissDismisses Cujo’s blocking review on the current commit and turns the check neutral, naming who dismissed it. The findings and their evidence stay on the pull request; only the block is lifted.Anyone with write access except the pull request’s author, and never a bot account.
/cujo reviewReviews the current head again. Its main use is a pull request Cujo never saw; any earlier run for that commit is replaced.Anyone with write access, the author included.

The author may not dismiss, because that is the direction that lifts a block on their own change. A bot account may not dismiss whatever access it holds, because the unlock exists to be the one thing a coding agent cannot do to the block it earned; GitHub’s own word for the account is what decides it, before any permission is read. A new commit gets its own run, its own review and its own check, so a block is never carried forward — and never dismissed forward either.

Write access is read from GitHub on every command, and every outcome speaks on the pull request — a refusal nobody can see is indistinguishable from a delivery that never arrived. The command comment gets a thumbs up when it was applied and a confused face when it was refused.

The seven run states

StatusMeansReactionCheck
runningReview running: tests, probes, a smoke boot, and dependency detonation.eyesin progress
cleanNo critical finding. The advisory review posted.hooraysuccess
blockedBlocking review posted as REQUEST_CHANGES; the cujo/guard check fails.thumbs downfailure
dismissedThe block was lifted by a maintainer. The observation stands.thumbs upneutral, naming who dismissed it
errorThe run ended in error.confusedneutral
unprovenThe review posted with no evidence: not one check returned a report.confusedneutral
supersededReplaced by a newer commit on this PR.nothingskipped, unless a newer run owns the commit
  • The reactions describe what happened to the pull request, not what Cujo concluded — which is why a dismissed block leaves a thumbs up even though the findings stand.
  • Red is reserved for a pull request that is dangerous, never for Cujo falling over, so a run that errored is drawn in the same blue as a clean one at reduced strength.
  • A superseded run writes no reaction at all. The run that replaced it is about to say what the pull request should show. Its check is per commit, so it is marked skipped — unless the replacement reviews the same commit, which then owns the check.
  • A diff review reaches three of these: running, clean and error. Its clean is an advisory review with findings of at most warn, and never unproven, since it never had evidence to post; the run page and the record mark it diff review so the two kinds of clean are not confused.

What each state looks like on the board is on reading the board.