contributing-docs/25_maintainer_pr_triage.md
This document describes how Apache Airflow maintainers triage incoming Pull Requests
using agentic skills that run inside Claude Code.
The triage workflow that used to live in the breeze pr auto-triage command has been
replaced by the pr-management-triage skill, which is
maintained as plain Markdown alongside the codebase and is invoked from a maintainer's
local Claude Code session.
Apache Airflow receives a high volume of Pull Requests from contributors around the world. Maintainers need to assess each PR for basic quality criteria, CI status, merge conflicts, and code correctness before it can be merged. The skill-based workflow streamlines this in a way that:
gh and the GitHub GraphQL API directly — no bespoke long-running
tool, no per-developer setup beyond gh auth login.This anchor is referenced by the AI-attribution footer that the skill appends to every contributor-facing comment. Contributors who follow the link from one of those comments land here.
Apache Airflow gets dozens of new Pull Requests every day from contributors all over the world. A maintainer's most valuable resource is the time they spend in actual conversation with a contributor — explaining a design decision, helping a first-time contributor land their change, weighing a tradeoff that only a human reviewer can weigh. That time is finite and irreplaceable.
To protect it, the project splits PR handling into two stages:
pr-management-triage skill, which sweeps the open
PR queue, runs purely deterministic checks (CI status, merge conflicts, unresolved
review threads, workflow-approval state, draft staleness), and proposes a
disposition for each PR. The maintainer confirms in batches — accepting a group of
"rebase" PRs in one keystroke, pulling individual PRs out for case-by-case
handling, skipping anything that doesn't fit. Comments posted by the skill are
drafted by an AI-assisted tool, attribute themselves clearly to the contributor,
and link back here so contributors understand why their first piece of feedback
came from a tool rather than a person.ready for maintainer review label, a maintainer reads the diff,
leaves substantive comments, asks questions, suggests improvements, and ultimately
approves or requests changes. This is the part of the work that has to be done by
a human and benefits from a human's full attention.Automating the first pass is what makes the second pass possible. Without it,
maintainers would spend most of their week mechanically checking whether each PR has
green CI, is rebased on main, and has no unresolved review threads — instead of
actually reviewing code. There is no rush — the skill grants generous grace
windows and posts comments that explicitly tell contributors to take their time. The
goal is not to push contributors faster; it is to make sure that when a maintainer
does engage, it is on a PR that is ready for that engagement.
flowchart TD
A[Contributor opens PR] --> B["Open PR (no label)"]
B -->|"Maintainer invokes
pr-management-triage skill in Claude Code"| C["Stage 1: TRIAGE
Deterministic checks"]
C --> D{Outcome}
D -->|Issues found| E[Convert to Draft
with comment + AI footer]
D -->|Looks good| F["Add 'ready for
maintainer review' label"]
D -->|Suspicious| G[Close all PRs
by author]
E --> H[Contributor fixes
and marks Ready]
H --> I[Stage 2: REVIEW]
F --> I
I["Stage 2: REVIEW
Maintainer reads the diff
and leaves human review"] --> J{Outcome}
J -->|Comments posted| K[Author addresses feedback]
J -->|Approve| L[Merge]
J -->|Request changes| K
K --> I
Stage 1 is the focus of this document. Stage 2 is plain human review and intentionally
has no automation behind it — the dedicated review skill that may eventually live
alongside pr-management-triage is out of scope here.
pr-management-triage skillThe skill is provided by the apache/airflow-steward framework,
adopted via its snapshot mechanism (see README.md → "Agent-assisted contribution" for first-time setup). After running /setup-steward the
framework lives gitignored at .apache-steward/ and a .claude/skills/pr-management-triage symlink picks it up
so Claude Code finds it without any per-developer config. Its entry point is
SKILL.md; the rest of the directory breaks
the logic out by topic (classification, fetch-and-batch, stale sweeps, comment
templates, etc.).
To run a triage pass, open Claude Code in your local clone of the repository and ask for a triage. Common phrasings the skill recognises:
triage the PR queue — default sweep over open non-collaborator PRstriage PR 12345 — re-triage a single PR (e.g. after a contributor pushed)triage PRs with label area:core — restrict to a labeltriage PRs from <login> — restrict to one authortriage review-for-me — only PRs with review requested from yourun the stale sweep — close stale drafts / convert inactive PRs to draftmorning triage — same as the default sweepThe skill's when_to_use block (in SKILL.md)
lists the full set of recognised invocation patterns.
The first-pass classification is purely deterministic — see
classify-and-act.md for the full decision matrix
— and runs against data fetched in a single aliased GraphQL call per page of PRs:
mergeable: CONFLICTING. Routed straight to draft (GitHub's
update-branch endpoint cannot resolve conflicts, so a rebase suggestion would
waste a round-trip).GET /repos/.../actions/runs?status=action_required endpoint as the primary
signal, then cross-referenced against statusCheckRollup. The REST call is
needed because the rollup can return SUCCESS while real CI is still pending —
see Golden rule 1b in SKILL.md.copilot*[bot] (Copilot threads are evaluated separately, with a 7-day grace
window).stale-sweeps.md.For each classified PR the skill picks a default action and groups the PRs by action so the maintainer can confirm a whole group at once. Per-PR override is always available. The full set of actions:
| Action | Description |
|---|---|
draft | Convert to draft and post a comment listing the violations. |
comment | Post the violations comment without converting to draft (used while the contributor is actively working). |
close | Close with a comment — used for repeated quality violations from the same author. Never accepts batch-confirm without a per-PR review. |
rebase | Trigger GitHub's update-branch — only for PRs that are not in conflict (conflicting PRs are routed to draft instead). |
rerun | Rerun failed CI checks — used for transient failures or for PRs whose failures match recent main-branch flakes. |
mark-ready | Add the ready for maintainer review label, signalling that Stage 1 is done. Never applied while workflow approval is pending; the skill MUST verify zero action_required workflow runs before adding the label. |
ping | Ping author or stale reviewer about an unresolved thread. |
approve-workflow | Approve a first-time-contributor's pending CI workflows after a diff inspection. |
flag-suspicious | Close all PRs from the author and apply the suspicious changes detected label. Per-PR confirm always required. |
skip | Leave the PR alone this run. |
The full action recipes (the gh / GraphQL calls each action issues) are in
actions.md; the comment bodies posted by
the draft / comment / close / ping actions are in
comment-templates.md.
Every comment the skill posts to a contributor ends with the same AI-attribution
footer (see comment-templates.md).
The footer:
This is non-negotiable per Golden rule 8 in
SKILL.md — only the
intentionally-terse suspicious-changes template is exempt.
PRs that survive Stage 1 (carry the ready for maintainer review label) are read by
a maintainer. There is no automation behind this stage by design — the read,
critique, suggest, and approve loop is exactly the part of the work that needs full
human attention.
A separate pr-review skill that handles structured walk-throughs of long diffs may
appear alongside pr-management-triage in the future, but it is out of scope here. Anything
about line-level review comments, approve / request-changes submissions, or merge
decisions belongs in that future skill, not in pr-management-triage.
pr-management-stats skillThe pr-management-stats skill is the read-only,
no-mutations counterpart of pr-management-triage. It is the successor to the now-removed
breeze pr stats command and produces two summary tables grouped by area:* label:
<1d, 1-7d, 1-4w, >4w) on both the time-since-drafting and
time-since-author-response axes.Invoke it the same way as pr-management-triage — open Claude Code in your local clone and ask
for stats. Phrasings the skill recognises include run PR stats, show the area breakdown, how is the PR queue doing, or any variation on "give me numbers about
the open PR backlog". A typical workflow is pr-management-stats → spot the area with the worst
triage coverage → pr-management-triage label:area:<that-area> to act on it.
The skill detects triaged PRs by scanning comment bodies for the canonical
Pull Request quality criteria marker that pr-management-triage posts on every triage comment.
That same detector also catches the legacy HTML-comment markers left by the removed
breeze command, so historical triaged PRs are still counted correctly.
The triage workflow uses the following labels and states:
| Label / State | Meaning |
|---|---|
ready for maintainer review | PR has passed Stage 1 and is queued for human review. Applied only after the skill has verified that no workflow runs are awaiting approval. |
closed because of multiple quality violations | PR was closed because the author has multiple open PRs with quality issues. |
suspicious changes detected | PR (and all open PRs by the same author) was closed because the diff contained suspicious patterns (secret exfiltration, malicious CI modifications, etc.). |
| Draft status | PR was converted to draft because it does not meet quality criteria. The contributor is asked to fix the listed issues and mark the PR as "Ready for review" once done. |
You don't need to use the triage skill — it is a maintainer-facing workflow. But understanding how it works helps you get your PRs reviewed faster.
There is no rush. The skill grants generous grace windows: 24 hours by default before a PR with failing CI gets flagged, 96 hours once a maintainer has already engaged with you, 7 days before a draft is closed for inactivity, 4 weeks before an inactive non-draft PR is converted to draft. Comments posted by the skill explicitly remind you that you can take your time.
Tips for getting through Stage 1 quickly: