This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow-steward.git
The following commit(s) were added to refs/heads/main by this push:
new 7b8ef76 feat(pr-management-triage): add Step 0.5 to promote
bot-authored draft PRs (#221)
7b8ef76 is described below
commit 7b8ef7641e50a103da380b9f034914ee7ec5f247
Author: Jarek Potiuk <[email protected]>
AuthorDate: Mon May 18 22:21:37 2026 +0200
feat(pr-management-triage): add Step 0.5 to promote bot-authored draft PRs
(#221)
Adds a once-per-session pre-pass that runs after pre-flight and before
the main classify loop. Sweeps open draft PRs authored by the F2 bot
logins (`dependabot`, `dependabot[bot]`, `renovate[bot]`,
`github-actions`, `github-actions[bot]`, `*[bot]`), bundles
`gh pr ready` + `ready for maintainer review` label-add as the new
`promote-bot-draft` action, and presents the matches as a single
interaction-loop group with `[A]ll` as the default keystroke.
Inherits Golden rule 1b's workflow-approval guard — a bot draft with
`action_required` workflow runs at its head SHA refuses promotion and
re-routes to `pending_workflow_approval`. F2 still excludes the same
logins from Steps 1–5, so the pre-pass is the only place bot drafts
surface; a maintainer who skips the group leaves the PR untouched.
Generated-by: Claude Code (Opus 4.7)
---
.claude/skills/pr-management-triage/SKILL.md | 56 ++++++++++++++++++++--
.claude/skills/pr-management-triage/actions.md | 48 +++++++++++++++++++
.../pr-management-triage/classify-and-act.md | 2 +-
3 files changed, 102 insertions(+), 4 deletions(-)
diff --git a/.claude/skills/pr-management-triage/SKILL.md
b/.claude/skills/pr-management-triage/SKILL.md
index 499a183..0c85ee8 100644
--- a/.claude/skills/pr-management-triage/SKILL.md
+++ b/.claude/skills/pr-management-triage/SKILL.md
@@ -9,8 +9,8 @@ description: |
options per PR: draft / comment / close / rebase / CI-rerun
/ workflow-approve / ping-stale-reviewer / request author
confirmation of readiness / mark `ready for maintainer
- review`. Does **not** perform code review — that lives in
- `pr-management-code-review`.
+ review` / promote bot-authored draft. Does **not** perform
+ code review — that lives in `pr-management-code-review`.
when_to_use: |
Invoke when a maintainer says "triage the PR queue", "go
through new contributor PRs", "run the morning triage",
@@ -334,6 +334,55 @@ gracefully with warnings.
---
+## Step 0.5 — Promote bot-authored draft PRs
+
+Before the main triage loop, sweep for open *draft* PRs authored
+by the bot logins enumerated in
+[`classify-and-act.md#pre-filters`](classify-and-act.md), row F2
+(`dependabot`, `dependabot[bot]`, `renovate[bot]`,
+`github-actions`, `github-actions[bot]`, anything matching
+`*[bot]`). For each match the skill proposes two mutations —
+convert draft → non-draft (`gh pr ready`) **and** add the
+`ready for maintainer review` label — bundled as the single
+[`promote-bot-draft`](actions.md#promote-bot-draft--convert-a-bot-authored-draft-and-label-it-ready)
+action.
+
+This is a once-per-session pre-pass, not a per-page sweep — bot
+drafts are author-deterministic, low volume, and don't benefit
+from pagination. F2 still excludes the same logins from
+Steps 1–5, so a bot draft the maintainer skips here stays a
+draft and does not surface again in the main loop.
+
+Fetch query (one GraphQL call, no overlap with Step 1's page-1
+fetch):
+
+```text
+is:pr is:open draft:true repo:<repo>
+```
+
+then client-filter the returned authors to the F2 login pattern.
+If the result set is empty, log a one-line "no bot drafts open"
+and proceed to Step 1.
+
+Otherwise present every match as a single group via the
+[interaction loop](interaction-loop.md). Default keystroke is
+`[A]ll` — the action is deterministic and the bot authorship
+removes the contributor-conversation concern that motivates
+per-PR review elsewhere. The maintainer may still pick
+`[E]ach` / `[P]ick NN` / `[S]kip group` for individual review.
+
+**Golden rule 1b still applies.** The `promote-bot-draft` action
+adds the `ready for maintainer review` label, so its
+implementation MUST run the same `action_required` workflow-run
+check that
[`mark-ready`](actions.md#mark-ready--add-ready-for-maintainer-review-label)
+does. A bot draft with workflow runs awaiting approval refuses
+promotion and is re-routed to `pending_workflow_approval` —
+unusual for trusted bots in practice, but defensive against the
+case where the head SHA picks up a first-time-contributor commit
+via a merge or a misconfigured bot account.
+
+---
+
## Step 1 — Resolve the selector and fetch page 1
Translate the selector into the GraphQL PR-list query from
@@ -505,7 +554,8 @@ On exit, print a one-screen summary:
- counts of PRs handled per action (drafted, commented, closed,
rebased, reruns triggered, author-confirm requests posted,
- marked ready, pinged, workflow approvals, suspicious flags)
+ marked ready, bot drafts promoted, pinged, workflow approvals,
+ suspicious flags)
- counts of PRs skipped and per-reason breakdown (already
triaged, inside grace window, bot, collaborator)
- counts of PRs left pending (reached quit, didn't finish the
diff --git a/.claude/skills/pr-management-triage/actions.md
b/.claude/skills/pr-management-triage/actions.md
index 864d2a7..9db69a9 100644
--- a/.claude/skills/pr-management-triage/actions.md
+++ b/.claude/skills/pr-management-triage/actions.md
@@ -220,6 +220,54 @@ error; this is the only action of the skill whose sole
purpose
---
+## `promote-bot-draft` — convert a bot-authored draft and label it ready
+
+The action behind [Step 0.5 of
`SKILL.md`](SKILL.md#step-05--promote-bot-authored-draft-prs).
+Two mutations bundled per PR: convert draft → non-draft
+(`gh pr ready`) and add the `ready for maintainer review`
+label.
+
+Inherits the workflow-approval guard from
+[`mark-ready`](#mark-ready--add-ready-for-maintainer-review-label)
+verbatim — Golden rule 1b in [`SKILL.md`](SKILL.md) applies
+to every code path that adds the label, including this one.
+
+```bash
+# Pre-check: same action_required index lookup as mark-ready.
+head_sha=$(gh api "repos/<owner>/<repo>/pulls/<N>" --jq '.head.sha')
+pending=$(gh api
"repos/<owner>/<repo>/actions/runs?head_sha=${head_sha}&per_page=20" \
+ --jq '[.workflow_runs[] | select(.conclusion == "action_required")] |
length')
+if [ "$pending" -gt 0 ]; then
+ echo "refuse promote-bot-draft: <N> has ${pending} workflow run(s) awaiting
approval at ${head_sha}" >&2
+ # Reclassify the PR as pending_workflow_approval; the maintainer
+ # handles it via the approve-workflow flow rather than promoting blind.
+ exit 2
+fi
+
+# Mutation 1 — flip draft to ready-for-review.
+gh pr ready <N> --repo <repo>
+
+# Mutation 2 — add the ready-for-maintainer-review label.
+gh pr edit <N> --repo <repo> --add-label "ready for maintainer review"
+```
+
+Order matters: `gh pr ready` first, then the label add. If
+`gh pr ready` fails (PR is no longer a draft, was closed, the
+bot pushed a new commit and the head SHA moved) the action stops
+before labelling — the PR is no longer in the bot-draft
+category and should be re-classified by the normal flow on the
+next run. If the label step fails after a successful ready
+toggle, do **not** roll back: the ready toggle is still a
+maintainer-visible improvement; log the label-add failure for
+the session summary so the maintainer can retry next sweep.
+
+No comment is posted. The bot's own commit message plus the
+`ready for maintainer review` label are sufficient signal — a
+contributor-facing footer would be misdirected for a bot author
+that won't read it.
+
+---
+
## `request-author-confirmation` — ask the PR author whether feedback is
addressed
Single mutation. Used when the only `deterministic_flag` signal
diff --git a/.claude/skills/pr-management-triage/classify-and-act.md
b/.claude/skills/pr-management-triage/classify-and-act.md
index a346bea..ca06f9b 100644
--- a/.claude/skills/pr-management-triage/classify-and-act.md
+++ b/.claude/skills/pr-management-triage/classify-and-act.md
@@ -42,7 +42,7 @@ filter is skipped silently from the main triage flow.
| # | Filter | Match condition |
|---|---|---|
| F1 | Author is collaborator/member/owner | `authorAssociation ∈ {OWNER,
MEMBER, COLLABORATOR}` (override: `authors:all` or `authors:collaborators`) |
-| F2 | Author is a known bot | login is `dependabot`, `dependabot[bot]`,
`renovate[bot]`, `github-actions`, `github-actions[bot]`, or matches `*[bot]` |
+| F2 | Author is a known bot | login is `dependabot`, `dependabot[bot]`,
`renovate[bot]`, `github-actions`, `github-actions[bot]`, or matches `*[bot]`.
Bot-authored **draft** PRs are handled separately by [`SKILL.md` Step
0.5](SKILL.md#step-05--promote-bot-authored-draft-prs) *before* this filter
runs; F2 then drops the same logins from the main triage flow regardless of
whether Step 0.5 promoted them. |
| F3 | Draft and not stale | `isDraft == true` and any activity within the
last 14 days. Stale-sweep classifications in
[`stale-sweeps.md`](stale-sweeps.md) may still pull the PR back in. |
| F4 | Already marked ready, no regression | `labels` contains `ready for
maintainer review` AND CI green AND `mergeable != CONFLICTING` AND no
unresolved threads. **Regression bypasses this filter** — any of: CI red, new
conflict, or new unresolved thread whose triggering event (failing check
`startedAt`, conflict detection, thread `createdAt`) is *after* the label-add
timestamp. The typical case is a contributor pushing a rebase or fixup commit
to a ready-for-review PR that re-introduc [...]
| F5a | Recent collaborator comment (author cooldown) | Most recent comment
from the **union of** general-issue comments (`comments(last:10)`) and
**review-thread comments** (`reviewThreads.nodes.comments`) is by a
`COLLABORATOR`/`MEMBER`/`OWNER`, `createdAt < 72h` ago, AND posted after
`commits(last:1).committedDate`. The review-thread leg is essential — a
maintainer asking a clarifying question in-thread is just as much an active
conversation as a top-level comment, and treating only t [...]