Skip to content

Gate external PRs on an assigned, linked issue - #3291

Open
maxisbey wants to merge 2 commits into
mainfrom
pr-intake-gate
Open

Gate external PRs on an assigned, linked issue#3291
maxisbey wants to merge 2 commits into
mainfrom
pr-intake-gate

Conversation

@maxisbey

@maxisbey maxisbey commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Adds an intake gate for pull requests from outside the maintainer team: a PR stays open only if it links an open issue its author is assigned to (or one labeled help wanted). Everything else is closed by a bot with an explanation and reopens automatically once a maintainer assigns the author. CONTRIBUTING.md is rewritten around that policy.

Motivation and Context

Over the last six months this repo received ~640 pull requests from outside the maintainer team — 2.7× the previous six months — of which 24 were merged. 41% of newly opened issues now attract an external PR within 48 hours (median under 11 hours), the open-PR backlog is ~80% external, and CONTRIBUTING.md's existing "issue first, no drive-by agents" rules have no mechanical backing. Reviewing a PR properly costs the same as it always did; producing one no longer does. With the maintainer time we actually have, issues are the contribution we can use, and this makes the repo say so and behave accordingly.

The workflow is adapted from PrefectHQ/fastmcp's require-issue-link.yml (which came from langchain's); pydantic and pydantic-ai run similar gates. Within this org, inspector has already gone issues-only and typescript-sdk restricted PR creation for a month in June for the same reason.

Behaviour

  • External, non-draft PR must Fixes / Closes / Resolves an open issue in this repo where the author is an assignee, or the issue carries help wanted. Otherwise: missing-issue-link label, one comment, closed.
  • Exempt: anyone with triage or better on the repo (resolved from the collaborator-permission capability flags, so private org membership and custom roles don't matter), bot accounts, drafts (re-checked on ready-for-review).
  • Ways back in: a maintainer assigns the author on the linked issue → the PR reopens itself; the author fixes the description → reopens on the edited event; anyone triage+ reopens the PR, removes the label, or adds bypass-issue-check → sticky override.
  • If GitHub refuses to reopen (branch force-pushed or deleted while closed, or a sibling PR from the same branch is already open) the gate keeps the control label so the PR stays findable and replaces its comment with instructions covering those cases.
  • PRs numbered below 3200 predate the gate and are left alone unless a maintainer evaluates one via workflow_dispatch; from 3200 up a PR is evaluated on its next event. Once labeled, a PR is managed normally regardless of number.
  • Live from merge. Setting the repository variable PR_GATE_ENFORCE to false is a kill switch: runs then log PASS/FAIL and [dry-run] would … lines and mutate nothing.

Differences from the fastmcp version (also listed in the file header): the rules live in .github/scripts/pr_intake_gate.js (with scenario tests beside it) and the workflow is just triggers and routing; one script serves all entry points (PR events, issue assignment, manual dispatch) so admission and reopening can't drift; PR state is always read live rather than from the event payload; linked issues must be open and still in this repo; gated PRs are found with the list API rather than Search; reopen happens before the label is removed and a refused reopen is explained rather than guessed at; trust from capability flags rather than role-name strings; the waiver label is our existing help wanted.

Docs

  • CONTRIBUTING.md: new "Why issues, not pull requests", "How pull requests get in", "Who we actively want to hear from" sections; the assignment rules (a bare "please assign me" is noise; engaging with the issue is the conversation we assign on; reporters have first claim); label table corrected (ready for work means queued for a maintainer, not an invitation).
  • AGENTS.md: an agent-facing statement of the policy at the top, since that's the file coding agents load.
  • .github/pull_request_template.md: a short repo-level template that leads with the Fixes # line the gate looks for, replacing the inherited org template here.

How Has This Been Tested?

  • .github/scripts/pr_intake_gate.test.js: 26 named scenarios (no link; link to help wanted; maintainer reopening their own vs a gated PR; triage-role and bot label removal; the bypass label; Dependabot; hand-closed and merged PRs; closed/transferred/cross-repo/PR-number references; refused reopen and the retry after it; issue assignment incl. past the reference cap; dispatch backfill; drafts; a planted marker comment) plus kill-switch and fail-safe cases, run in CI on Node 24.
  • actionlint and zizmor --pedantic clean (the pull_request_target trigger carries an inline justification; the workflow never checks out or executes PR code and interpolates nothing from the PR into the script).
  • The endpoint shapes it depends on (collaborators/{user}/permission capability flags for admins, outsiders and app logins; issues.listForRepo with creator+labels+state=closed; minimizeComment/unminimizeComment) were checked against the live API, and the same permission set is what fastmcp's copy has been running with since May.
  • Real traffic gets exercised in dry-run after merge before enforcement is switched on.

Breaking Changes

None for SDK users. For contributors: PRs opened without an assigned, linked issue will be closed automatically once enforcement is on; CONTRIBUTING.md describes the path.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Repository automation

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Rollout after merge:

  1. It is live from merge: new PRs are evaluated as they open, already-open PRs ≥ feat(mcp): async retry middleware for client transport with backoff+jitter #3200 on their next edit. Watch Actions → Require Linked Issue for the first day; PR_GATE_ENFORCE=false pauses it.
  2. Label hygiene: update the help wanted / ready for work label descriptions to match CONTRIBUTING.md.
  3. Backfill any already-open PRs we want evaluated: gh workflow run require-linked-issue.yml -f pr_number=<n>.
  4. Optionally stand up a triage-permission collaborators team for regular outside contributors; until then a maintainer reopening a PR is the per-PR equivalent.

AI Disclaimer

Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread AGENTS.md Outdated
Comment thread .github/pull_request_template.md
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/workflows/require-linked-issue.yml Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
Comment thread CONTRIBUTING.md Outdated
@maxisbey
maxisbey marked this pull request as ready for review August 16, 2026 13:32

@cubic-dev-ai cubic-dev-ai 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.

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread CONTRIBUTING.md
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/workflows/require-linked-issue.yml Outdated
Comment thread .github/scripts/pr_intake_gate.js Outdated
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/scripts/pr_intake_gate.js Outdated
Comment thread .pre-commit-config.yaml Outdated
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/scripts/pr_intake_gate.js Outdated
Unsolicited pull requests now outnumber issues four to one and almost none
are reviewable in the time we have. This adds a workflow that closes an
external PR unless its description links an open issue the author is
assigned to (or one labeled "help wanted"), and reopens it automatically
once a maintainer assigns them. Anyone with triage or better, bots and
drafts are exempt; reopening a PR, removing the control label, or adding
the bypass label is a sticky maintainer override. PRs numbered below 3200
predate the gate and are only evaluated on manual dispatch. It is live from
merge; setting the PR_GATE_ENFORCE repository variable to "false" turns it
log-only.

The rules live in .github/scripts/pr_intake_gate.js with scenario tests
beside it (run in the checks job); the workflow file is triggers, routing
and a checkout + require. Adapted from PrefectHQ/fastmcp's
require-issue-link.yml (itself from langchain).

CONTRIBUTING.md is rewritten around the policy (issues are the
contribution; how PRs get in; who we'd love to hear from), AGENTS.md points
agents at it, and the repo-level PR template is the org template plus a
short note about the gate.

No-Verification-Needed: workflow, its script and tests, and docs only
Signed-off-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/scripts/pr_intake_gate.js
Comment thread .github/scripts/pr_intake_gate.test.js Outdated
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/workflows/require-linked-issue.yml
Comment thread .github/scripts/pr_intake_gate.test.js
Comment thread .github/scripts/pr_intake_gate.test.js
…ns per assignee

Also reword the refused-reopen advice for PRs that passed via a maintainer override, and drop an unused test fixture.

No-Verification-Needed: workflow script, its tests, and config only
Signed-off-by: Max Isbey <224885523+maxisbey@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant