Skip to content

emrg: check-pr-base.py without --repo reads a fork and never says which repository answered #2629

Closed

Description

@pm25coder

scripts/check-pr-base.py answers one question - can a merge into this PR's base still reach master - and when it is run without --repo it asks that question of whichever repository gh resolves from the remotes of the directory it runs in. On a checkout whose only GitHub remote is a fork, that is the fork, and nothing in the output names it: the per-PR lines, the summary line and the exit code all read as if they were about the repository the caller is merging into.

What was observed

On master 7e76668, in a checkout whose origin is a local directory and whose only GitHub remote is fork = pm25coder/emrg:

$ gh api "repos/{owner}/{repo}"     ->  pm25coder/emrg

The tool's own helpers were then asked the same question under both spellings:

_ref_is_on_master(<upstream master head 7e766683...>, None)                 -> False
_ref_is_on_master(<the same commit>,                 "argszero/emrg")       -> True

One commit, one question, two answers, because master was read in two different repositories. The branch sets the verdict is classified against differ the same way: 50 branches resolved by default against 1159 upstream, so 1158 upstream branches are reported as

base branch 'chore/one-bounded-read-helper' no longer exists on the remote, so a merge
would have nowhere to land and could never reach master
  • a fact about the fork, stated as a fact about the branch. The same base classified against the upstream reads differently ("is not on master and is not the head of any open PR"), i.e. the verdict is decidable only once you know which repository answered, and the output never said.

The exit code does not distinguish the two: with no open PRs in either repository the run printed nothing and exited 0 - a clean bill, about an unnamed repository.

What the fix must satisfy

  • Without --repo, when the resolved repository is a fork: refuse to measure (exit 2), naming the repository it read and the one that repository is a fork of, and saying that --repo is how to name the repository to measure.
  • Without --repo, when the resolved repository is not a fork: measure it as before, and print which repository that was, so an ordinary clone keeps working.
  • With --repo: every existing reading is unchanged - same per-PR lines, same summary line, same exit codes - and the repository is printed before any verdict.
  • {owner}/{repo} never reaches gh as a path: resolution produces a concrete owner/name, or nothing is measured.
  • The sibling gates that already read the correct repository without --repo are untouched.

Why it costs something

This is a merge gate: rc 0 is read as "every base reaches master", and a Committer merges on that reading. A gate that answers about a different repository - and does not say which - is worse than a missing gate, because the failure it hides is exactly the silent one. No sibling gate's green will ever expose it: each of them resolves the repository correctly on its own, so this tool's wrong reading is not downstream of anything another gate checks.

The shape is also the one a reader is least likely to question: rc 2 here currently means "the PR is closed, merged or nonexistent", and it in fact means "I read another repository". The reading that names the repository is the only one that separates those two sentences.

Activity

  1. pm25coder commented on Oct 29, 2026

    @pm25coder
    CollaboratorAuthor

    Handled by #2630

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions