medlab / case

medlab

what got hurt, and what now fails loudly

↑ sublayer

2026-11-02 · fixed: yes

A review on your own pull request is assumed to be invalid

InvariantsHuman ComprehensionIndependent ReviewContract Tests

now fails loudly The refusal covers verdicts only: an author may post a comment review on their own pull request, and a test requires that path to exist, so the handback has a channel.

What broke

Agents work on pull requests together. One writes a change, another reviews it, and when the reviewer asks for changes the author is expected to fix them and say so. The saying-so is part of the protocol: without it, a request-changes review has no reply and the change sits until somebody notices.

The command-line tool agents use to post reviews refused every review on the author's own pull request. That rule was written to stop a person approving their own work, which would be a verdict with no second reader behind it.

It also removed the reply. The tool has three review kinds: approve, request changes, and comment. Only the first two are verdicts. A comment is a note attached to the review record, carrying no decision, and it is how an author says the requested changes are in. Refusing it left the handback with no path through the tool at all.

The belief was that "a review on your own pull request" names one thing. It names a verdict, which should be refused, and a comment, which should not.

How it showed

The refusal was a sentence rather than a silent failure, so the failure appeared as an author who could not reply rather than as a review that went missing. The sentence the tool printed was about reviews:

#7: mercer opened this pull request — a review by its author is not a review, so forge will not post one

Nothing in it named which of the three kinds had been refused, so it read as a rule about reviews rather than a rule about verdicts. An author who tried a comment and was refused had no reason to try anything else, and the flag that would have worked did not exist either.

What was watching

Review is the sensor that decides whether a change is good, and here it was the thing that had just been widened: the guard was added to protect the branch, and it was reviewed as a protection.

No test exercised a comment on an author's own request. The guard's tests covered the verdicts it refused, which is the case it was written for, and the path it broke had no test asserting it existed.

The detecting sensor was a person using the tool to hand a fix back and finding the door closed, one day after it was closed.

Where it sits

This is an invariants case where the rule and the mechanism were confused.

The family is invariants because the guard's intent is "no verdict without a second reader", and the rule implemented was "no review record at all", which is wider than its invariant. It is comprehension because the three review kinds had been treated as one thing by the code that refused them.

Independent review was the mechanism being protected and also the sensor that broke: an author cannot be their own independent reviewer, so refusing the verdict is right, and the same reasoning does not extend to a comment. Contract tests would have separated the three kinds: the tool's own vocabulary is approve, request changes, comment, and a refusal that names "review" without naming the kind is not asserting anything about that vocabulary.

A guard that is wider than the rule it implements is a recurring shape here, and it is the failure mode that looks like safety while it removes a path.

What we did

The refusal now covers verdicts only. An author can post a comment review on their own pull request, which emits the comment event the station routes to the reviewer, so the handback works again.

The refusal's own sentence now carries the exception, in the same line:

#7: mercer opened this pull request — a verdict by its author is not a review, so forge will not post one (a --comment is allowed: that is how a fix is handed back)

so the next author who hits it is told which door is open.

What now fails loudly

TestReviewCommentOnYourOwnPullRequestIsAllowed requires the comment path to work, so a future widening of the guard fails a test rather than closing the handback again.

The verdicts are still refused, and that is the part worth keeping: the test sits beside the refusals rather than replacing them, so both halves of the rule are asserted in one place.

What is not guarded is the general shape: nothing compares a guard's rule against the invariant it claims to protect. Every guard on this station is written by someone who can read the invariant, and review is what catches one that is too wide, which is what happened here one day later.