Skip to content

feat(github): Add an opt-in, policy-gated pull request merge tool - #2402

Open
sentry-junior[bot] wants to merge 2 commits into
mainfrom
feat/github-merge-pull-request
Open

sentry-junior[bot] wants to merge 2 commits into
mainfrom
feat/github-merge-pull-request

Conversation

@sentry-junior

@sentry-junior sentry-junior Bot commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Add github_mergePullRequest so Junior can merge in a few narrow cases, such as the fair.io listing automation. Merging is off by default. It turns on per repository in app config.

githubPlugin({ pullRequestMerge: { repositories: ["fairsource/fair.io"] } });

When the list is empty, the tool is not registered. The egress policy then denies every merge.

Merge policy (src/pull-request-merge-policy.ts, deterministic, checked on every call)

The rule: two parties, at least one of them a person. Junior never merges code that only Junior wrote and checked.

  • The requester must have a linked GitHub account with write or admin on the repo. For automations, the requester is the automation creator. This is the code-checkable version of "you've been told you can merge."
  • The PR is open, not a draft, and head.sha equals the headSha the caller checked. The same SHA goes to GitHub's merge API, so a push that lands mid-flight fails the merge.
  • mergeable_state is clean or has_hooks. This requires passing checks, no conflicts, and met branch protection. unknown, unstable, and blocked are refused.
  • No reviewer has an open CHANGES_REQUESTED.
  • Junior-authored PR: a person (not a bot) approved the current head commit. Stale approvals don't count.
  • Person-authored PR: Junior reviewed the current head commit, and Junior wrote none of the PR's commits. Commits are matched by GitHub id or bot email. Without this, Junior could push its own code into someone's PR and then merge it.
  • PRs authored by other bots (Dependabot, etc.) are refused.

Enforcement layers

  • Egress allows PUT /repos/{o}/{r}/pulls/{n}/merge only with the tool's github.pull.merge operation and only on listed repos. Raw gh api -X PUT .../merge from the sandbox stays denied. gh pr merge and auto-merge use GraphQL mutations, which are already denied.
  • The merge uses installation-write, so the App installation needs Contents: write on the repo.

Open questions for review

  • Should a person-authored PR also need the requester to be someone other than the PR author? Today, "author asks Junior to merge their own PR" counts as two parties (author plus Junior's review).
  • Is the default squash OK, or should the method come from repo settings?
  • Should we keep the egress-layer allowlist? It duplicates the tool check on purpose, as defense in depth.

Follow-up (junior-prod, not in this PR): list fairsource/fair.io in pullRequestMerge. Add the stricter fair-source-review checks (valid companies.json, logo exists, hex color, no changes outside the entry). Then update the automation to merge only on a pass.

Checks

  • pnpm lint passed.
  • tsc --noEmit passed for junior-github.
  • vitest passed for tests/merge-pull-request.test.ts and tests/github-plugin.test.ts.
  • The full junior-github suite had one webhook-outcomes timeout. That test passes alone, and this PR does not change it.
  • Not run: evals and live GitHub.

via David Cramer.

--

View Session [Sentry]

Add github_mergePullRequest. The app turns it on per repository with
githubPlugin({ pullRequestMerge: { repositories } }). With no list, the
tool is not registered and egress denies every merge.

Each merge must pass a deterministic policy:
- the requester has a linked GitHub account with write access
- the PR is open, not a draft, and its head is the checked SHA
- GitHub reports mergeable_state clean (or has_hooks)
- no reviewer has an open change request
- a bot-authored PR has a human approval on the current head
- a human-authored PR has a bot review on the current head and no
  bot-written commits

Egress allows PUT /pulls/{n}/merge only with the tool's operation and
only on listed repositories, so raw gh/api merges stay denied.

Co-Authored-By: David Cramer <david@sentry.io>
@vercel

vercel Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
junior-docs Ready Ready Preview Oct 30, 2026 2:41am UTC

Request Review

@sentry-evals

sentry-evals Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Sentry Evals

junior-guardian

Run Status Passed Failed Errored Cost Tokens Duration
Head Passed 94 0 0 $0.03 0 24.56s

Base commit 6e32d62 has no eval run to compare against.

@github-actions github-actions Bot added the risk: high PR risk score: high label Oct 30, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 95206fc. Configure here.

GitHub lists at most 250 commits for a pull request. Compare the
listed commits with the PR commit count, so an unlisted bot commit
cannot pass the person-authored merge rule.

This branch was successfully deployed

1 active deployment
Preview – junior-docs — 3815ee5b Deployed Oct 30, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk: high PR risk score: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants