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 b6f13607 feat(pr-management-triage): author-only folded triage notes
(#511)
b6f13607 is described below
commit b6f13607aa9b1722829a272b3ecd5b6fcc3109ef
Author: Jarek Potiuk <[email protected]>
AuthorDate: Fri Jun 12 12:08:39 2026 +0200
feat(pr-management-triage): author-only folded triage notes (#511)
Make the folded PR-body note the single contributor channel for all triage
feedback (draft/comment/close/ping/request-author-confirmation/stale),
replacing
the per-action comment bodies. One compact [!IMPORTANT] callout, replaced in
place each sweep, that:
- @-mentions and assigns ONLY the PR author (the one notification);
- credits the operator and names reviewers backtick-quoted only — no
maintainer
is ever @-mentioned, assigned, or pinged (reviewer-ping /
reviewer-re-review
variants removed);
- carries a one-line disclaimer (replacing the multi-sentence footer);
- flips to a "Ready for review" note and un-assigns the author when the PR
becomes ready.
The agent-guard mention guard is inverted to match: in a gh pr edit --body
it now
permits the author's @-mention and blocks any maintainer mention
(previously it
blocked all). Legacy triage_feedback_channel: comment mode keeps the old
bodies.
Promotes a local override from the Apache Airflow adopter
(.apache-magpie-overrides/pr-management-triage.md).
Generated-by: Claude Code (Opus 4.8)
---
skills/pr-management-triage/SKILL.md | 65 +++++++----
skills/pr-management-triage/actions.md | 15 +++
skills/pr-management-triage/comment-templates.md | 137 +++++++++++++++++++++--
skills/pr-management-triage/guards/mention.py | 43 ++++---
4 files changed, 204 insertions(+), 56 deletions(-)
diff --git a/skills/pr-management-triage/SKILL.md
b/skills/pr-management-triage/SKILL.md
index b18cb168..d368fddb 100644
--- a/skills/pr-management-triage/SKILL.md
+++ b/skills/pr-management-triage/SKILL.md
@@ -262,7 +262,11 @@ owed to a contributor who will otherwise be left guessing.
See
bodies.
**Golden rule 8 — every contributor-facing comment ends with
-the AI-attribution footer.** The triage comments this skill
+the AI-attribution footer.** (Under the default folded-note model
+the multi-sentence footer is replaced by the single `<sub>`
+disclaimer line in the note — see Golden rule 12; the long footer
+below applies to the legacy `triage_feedback_channel: comment`
+mode.) The triage comments this skill
posts are AI-drafted on the maintainer's behalf, and
contributors deserve to know that up front. Every template in
[`comment-templates.md`](comment-templates.md) (with one
@@ -365,30 +369,45 @@ comment. Editing a PR body does not notify subscribers,
so the
default keeps maintainer mailboxes quiet — the denoise change from
the dev@ thread with Elad (see
[`rationale.md#why-fold-feedback-into-the-pr-body-denoise`](rationale.md#why-fold-feedback-into-the-pr-body-denoise)).
-Two consequences the implementation MUST honour:
-
-- **The folded block carries no `@`-mention.** A body edit that
- introduces a fresh `@`-mention can itself notify; reference the
- author as a backtick-quoted login instead. The no-`@`-mention
- rule is what makes the fold silent. When the framework's secure
- setup is installed, this is **enforced deterministically** by the
- agent-guard `PreToolUse` hook (the `mention` guard): any
- `@`-mention in a `gh pr edit --body` is blocked, and an
- author-directed `gh pr comment` may only `@`-mention the PR
- author — see [`tools/agent-guard`](../../tools/agent-guard/README.md).
-- **Pings still notify.** `review-nudge`, `reviewer-ping`,
- `request-author-confirmation`, `security-language`,
- `suspicious-changes`, and stale-sweep notices always post a
- comment regardless of the setting — their purpose is to reach a
- human. Only the three violation-feedback actions honour the
- channel switch.
-
-The maintainer-facing proposal MUST state which channel a given
-action will use, so the maintainer knows whether a notification
-will fire. See
-[`comment-templates.md#body-fold-rendering`](comment-templates.md#body-fold-rendering)
+Under the default `pr-body` channel **every** contributor-facing
+action — not just the three violation actions, but `ping`,
+`request-author-confirmation`, and the stale-sweep notices too —
+folds into the one managed block (Golden rule 12), so a PR never
+carries more than a single triage note. The legacy
+`triage_feedback_channel: comment` mode keeps the per-template
+comment bodies for adopters who opt into it. See
+[`comment-templates.md#the-folded-maintainer-triage-note--the-single-contributor-channel`](comment-templates.md#the-folded-maintainer-triage-note--the-single-contributor-channel)
and [`actions.md`](actions.md).
+**Golden rule 12 — the folded note notifies the author, and only
+the author.** Under the default `pr-body` channel the folded
+maintainer-triage note is **not** silent — it deliberately
+`@`-mentions the PR author and **assigns** them, because the note
+is a "your move" signal. But the author is the *only* person ever
+notified:
+
+- Only the author is `@`-mentioned; only the author is assigned
+ (`gh pr edit --add-assignee <author>`). On the ready-for-review
+ flip the author is **un-assigned** (the ball returns to the
+ maintainers).
+- **No maintainer is ever `@`-mentioned, assigned, or pinged** —
+ not the operator, not a reviewer, not a CODEOWNER, not a team.
+ Maintainer handles appear backtick-quoted (`` `@login` ``) only.
+- The framework's reviewer-re-review / reviewer-ping variants are
+ removed; the author-primary nudge (folded, reviewer named with a
+ backtick handle) is the only nudge. The author pings the reviewer
+ themselves, from their own account, when ready.
+- Enforced deterministically by the agent-guard `mention` guard:
+ in a `gh pr edit --body` it permits the author's `@`-mention and
+ blocks every other. See
+ [`tools/agent-guard`](../../tools/agent-guard/README.md) and
+
[`comment-templates.md`](comment-templates.md#the-folded-maintainer-triage-note--the-single-contributor-channel).
+
+This supersedes Golden rule 9's "pings still notify a maintainer"
+expectation for the operator/reviewer side: F5a/F5b still make the
+skill *step back* from an active maintainer conversation, but the
+skill itself never generates a maintainer notification.
+
---
## Inputs
diff --git a/skills/pr-management-triage/actions.md
b/skills/pr-management-triage/actions.md
index e74f45b4..ca113910 100644
--- a/skills/pr-management-triage/actions.md
+++ b/skills/pr-management-triage/actions.md
@@ -3,6 +3,21 @@
# Actions
+> **Author-only folded-note model (Golden rule 12).** Under the default
+> `triage_feedback_channel: pr-body`, **every** contributor-facing action below
+> delivers its feedback as the one replace-in-place
+> [folded maintainer-triage
note](comment-templates.md#the-folded-maintainer-triage-note--the-single-contributor-channel),
+> not a comment — including `ping`, `request-author-confirmation`, and the
+> stale-sweep notices, which the legacy comment mode posts separately. In
+> addition, every such action **assigns the PR author**
+> (`gh pr edit <N> --repo <repo> --add-assignee <author>`) to signal the ball
is
+> in their court, and `@`-mentions **only** the author (all maintainer handles
+> backtick-quoted — no operator/reviewer ping; the reviewer-ping mutation is
+> removed). On the **ready-for-review flip** the note is replaced with the `✅`
+> variant and the author is **un-assigned** (`--remove-assignee <author>`). The
+> per-action recipes below describe the body content; this banner is the
+> cross-cutting behaviour they all share.
+
Exact recipes for every mutation the skill can execute. Every
action in this file assumes:
diff --git a/skills/pr-management-triage/comment-templates.md
b/skills/pr-management-triage/comment-templates.md
index 6c7d8b80..ee3638a9 100644
--- a/skills/pr-management-triage/comment-templates.md
+++ b/skills/pr-management-triage/comment-templates.md
@@ -50,6 +50,103 @@ anchor text breaks the re-triage skip logic.
---
+## The folded maintainer-triage note — the single contributor channel
+
+> **This section is normative and supersedes the per-template bodies and the
+> reviewer-re-review variants further down this file.** All contributor-facing
+> triage feedback is delivered as **one** managed, replace-in-place block
folded
+> into the PR description — never as a standalone comment.
+
+### Author-only notification (the hard rule)
+
+The PR **author** is the only person this skill ever notifies:
+
+- Only the author is **`@`-mentioned**, and only the author is **assigned**
+ (`gh pr edit <N> --add-assignee <author>`). The author's `@`-mention in the
+ note is the one notification each refresh produces.
+- **No maintainer is ever `@`-mentioned, assigned, or pinged** — not the
operator
+ running the triage, not a reviewer, not a CODEOWNER, not a committers team.
+ When a maintainer handle must appear (operator credit, the reviewer who left
+ feedback), render it **backtick-quoted** — `` `@login` `` — which shows the
+ handle without notifying.
+- The framework's *reviewer-re-review* and *reviewer-ping* variants (which
+ `@`-mention `<reviewers>` to summon a maintainer back) are **removed** — see
+ the note on [Review nudge](#review-nudge) / [Reviewer ping](#reviewer-ping).
+ The only nudge that goes out is the author-directed note below. Where the
body
+ tells the author to "ping the reviewer when ready", the *author* does that
from
+ their own account; our note names the reviewer with a backtick handle only.
+
+### The note format
+
+Every action (`draft`, `comment`, `close`, `ping`,
+`request-author-confirmation`, and the stale-sweep notices) renders this one
+compact callout, folded into the body via the
+[`pr-triage-fold` marker](#body-fold-rendering) so the newest note always
+replaces the previous one:
+
+```markdown
+---
+
+> [!IMPORTANT]
+> **🛠️ Maintainer triage note for @<author>** · by `@<operator>` · <YYYY-MM-DD
HH:MM UTC>
+>
+> <one-line framing for this action — see the per-action table>
+> <violation bullets, or the action-specific ask, as `>`-quoted lines>
+>
+> **The ball is in your court** — you've been assigned to this PR. <one-line
next step>.
+>
+> <sub>_Automated triage — may be imperfect; a maintainer takes the next
look._</sub>
+```
+
+- `@<author>` — the one `@`-mention. `<operator>` (the authenticated `<viewer>`
+ login) is credited backtick-quoted so the contributor knows which maintainer
+ stands behind the note, without a notification.
+- A UTC `YYYY-MM-DD HH:MM UTC` stamp always appears in the header (matches the
+ fold marker's `triaged=`).
+- The literal `Pull Request quality criteria` marker link (and, for
+ `request-author-confirmation`, the literal `ready for maintainer review
+ confirmation` string) must still appear verbatim so the already-triaged /
+ confirmation detectors keep working.
+- One `<sub>` disclaimer line replaces the multi-sentence
+ [AI-attribution footer](#ai-attribution-footer) — do not also append the long
+ footer.
+
+#### Per-action framing + next step
+
+| Action | Framing line | Next-step line |
+|---|---|---|
+| `draft` / `comment` | Helpful heads-up from the maintainers — please address
before this PR can be reviewed: | Fix the above, then mark it **Ready for
review**. |
+| `ping` (threads / stale review) | Some review feedback from `@<reviewer>` is
waiting on you: | Reply or push a fix in each thread, then mark them resolved. |
+| `request-author-confirmation` | Your review threads look addressed — please
confirm this PR is **ready for maintainer review confirmation**: | Reply `yes /
ready` and a maintainer will pick it up from the queue. |
+| `stale-*-close` | This PR is being closed to keep the queue clean: | Reopen
or open a fresh PR once addressed — no rush. |
+| `inactive-to-draft` / `stale-workflow-approval` | Paused pending your next
update: | Rebase, address new failures, and mark **Ready for review** again. |
+
+### The ✅ ready-for-review flip
+
+When a PR carrying a `pr-triage-fold` block is detected as ready on a later
sweep
+(the author un-drafted it / marked it Ready for review, the items resolved so
it
+classifies `passing`, or the skill applies the `ready for maintainer review`
+label), **replace** the note with the ready confirmation, **unassign the
author**
+(`gh pr edit <N> --remove-assignee <author>` — the ball is back with the
+maintainers), and apply the ready label as usual:
+
+```markdown
+---
+
+> [!NOTE]
+> **✅ Ready for review** · @<author> → `@<operator>` · <YYYY-MM-DD HH:MM UTC>
+>
+> Thanks @<author> — the earlier triage items look addressed and this PR is
now ready for review. The ball is back with the maintainers; a maintainer will
take the next look.
+>
+> <sub>_Automated triage — may be imperfect._</sub>
+```
+
+(Use `action=ready` in the opening marker.) Instant flipping on the author's
+click would need an event-driven hook on `pull_request.ready_for_review`; the
+sweep replacement above is the baseline.
+
+---
+
## Reviewer-mention policy
When a comment's only addressee is the PR author (the
@@ -82,6 +179,12 @@ than by the bot.
## AI-attribution footer
+> **Superseded by the folded-note model.** Under
+> [the folded maintainer-triage
note](#the-folded-maintainer-triage-note--the-single-contributor-channel)
+> the multi-sentence footer below is replaced by the single `<sub>` disclaimer
+> line in the note template. The long footer is retained here only for adopters
+> who pin `triage_feedback_channel: comment` and want the legacy comment
bodies.
+
**Every contributor-facing template below ends with this
footer.** It calibrates the contributor's trust in the comment
(AI-drafted, may be wrong), reassures them that a human
@@ -195,18 +298,18 @@ description is preserved above it, untouched):
already-triaged marker search (which scans the PR body as well
as comments) keeps working.
-### No `@`-mention in the folded block
+### Author-only `@`-mention in the folded block
-The block **must not** contain an `@`-mention of anyone. The
-opening `@<author>` that the comment templates use is dropped in
-fold mode; reference the author as a backtick-quoted login
-(`` `<author>` ``) instead, the same convention the
-[Reviewer-mention policy](#reviewer-mention-policy) uses for
-`<reviewer_logins>`. A body edit that introduces a fresh
-`@`-mention can generate the very notification this change exists
-to avoid, so the no-`@`-mention rule is what makes the fold truly
-silent. The author owns the PR and sees its description — they do
-not need pinging to read it.
+The block `@`-mentions **only the PR author** (once, in the header) — that
single
+mention is the intended notification, the "your move" signal. **Every other
+handle is backtick-quoted** (`` `@login` ``) and therefore silent: the operator
+credit, any reviewer named in a `ping` / `request-author-confirmation` note,
and
+any team. A body edit that introduces a *maintainer* `@`-mention would notify a
+maintainer, which this model forbids — see
+[Author-only notification](#author-only-notification-the-hard-rule). The
+deterministic enforcement of this lives in the agent-guard `mention` guard
+([`tools/agent-guard`](../../tools/agent-guard/README.md)): in a
+`gh pr edit --body` it permits the author's `@`-mention and blocks any other.
### Idempotent replace, never append
@@ -393,6 +496,13 @@ notification.
*(`review-nudge` — stale `CHANGES_REQUESTED` ping)*
+> **Reviewer-re-review variant removed.** Per
+> [Author-only notification](#author-only-notification-the-hard-rule) the skill
+> never `@`-pings a reviewer. Always use the author-primary nudge, folded into
+> the note (action `ping`), with the reviewer named as a backtick handle. The
+> reviewer-re-review variant below is retained only for the legacy
+> `triage_feedback_channel: comment` mode and must not `@`-mention the
reviewer.
+
Used when the action is `ping` on a `stale_review`
classification.
@@ -461,6 +571,11 @@ coherent to-do list.
*(`reviewer-ping` — unresolved-review-thread ping)*
+> **Reviewer-re-review variant removed.** Per
+> [Author-only notification](#author-only-notification-the-hard-rule) the skill
+> never `@`-pings a reviewer. Always use the author-primary nudge, folded into
+> the note (action `ping`), with the reviewer named as a backtick handle.
+
Used when the action is `ping` on a `deterministic_flag`
classification triggered by unresolved review threads (i.e.
the reviewer commented but the thread stayed unresolved and the
diff --git a/skills/pr-management-triage/guards/mention.py
b/skills/pr-management-triage/guards/mention.py
index d389d3c6..d29878f1 100644
--- a/skills/pr-management-triage/guards/mention.py
+++ b/skills/pr-management-triage/guards/mention.py
@@ -17,11 +17,16 @@
"""pr-management-triage mention guard (skill-contributed).
-Deterministically enforces the denoise rule (Golden rule 11): author-directed
-feedback never @-pings a maintainer, and the silent PR-body "fold" channel
never
-@-mentions anyone. Discovered by the agent-guard PreToolUse dispatcher from a
-guards.d directory — see tools/agent-guard for the engine and the GuardContext
-API. Import-free: everything comes from ``ctx``.
+Deterministically enforces author-only notification (Golden rule 12): the PR
+author is the only login the skill may @-mention. This applies identically to
+the folded maintainer-triage note (`gh pr edit --body` / `--body-file`) and to
+author-directed comments (`gh pr/issue comment`) — in both, the author's
+@-mention is permitted (it is the intended "your move" signal) and any other
+@-mention (a maintainer: operator, reviewer, CODEOWNER, team) is blocked.
+Maintainer handles must be backtick-quoted so they never notify. Discovered by
+the agent-guard PreToolUse dispatcher from a guards.d directory — see
+tools/agent-guard for the engine and the GuardContext API. Import-free:
+everything comes from ``ctx``.
"""
TRIGGERS = ["gh"]
@@ -47,36 +52,30 @@ def guard(ctx):
if ctx.override("STEWARD_ALLOW_MENTIONS"):
return None
- if is_pr_body_edit:
- return (
- "agent-guard[mention]: a `gh pr edit --body` (the silent
PR-description "
- f"'fold' channel) must not @-mention anyone — found
{sorted(set(mentions))}. "
- "Editing a PR body should never ping; reference logins as
backticked "
- "`login`, not @login. Override (rare): prefix
STEWARD_ALLOW_MENTIONS=1."
- )
-
- # Comment channel: only the PR/issue author may be @-mentioned.
- target = ctx.positional_after(name)
+ # Both channels share one rule: only the PR/issue author may be
@-mentioned.
+ # Resolve the author from the target PR/issue number.
+ target = ctx.positional_after("edit" if is_pr_body_edit else name)
view = "pr" if group == "pr" else "issue"
author = None
if target:
author = ctx.run(
["gh", view, "view", target, *ctx.repo_flag(), "--json", "author",
"--jq", ".author.login"]
)
+ surface = "folded triage note" if is_pr_body_edit else "author-directed
comment"
if not author:
return (
- "agent-guard[mention]: this author-directed comment @-mentions "
+ f"agent-guard[mention]: this {surface} @-mentions "
f"{sorted(set(mentions))} but the PR/issue author could not be
verified, "
- "so the guard cannot confirm none of them are maintainers. Re-run
once the "
+ "so the guard cannot confirm they are not a maintainer. Re-run
once the "
"author is known, drop the @-mentions (use backticked `login`), or
override "
- "with STEWARD_ALLOW_MENTIONS=1 if the ping is intentional."
+ "with STEWARD_ALLOW_MENTIONS=1 if the mention is intentional."
)
offenders = sorted({m for m in mentions if m != author.lower()})
if offenders:
return (
- "agent-guard[mention]: an author-directed comment may only
@-mention the "
- f"author (`{author}`); refusing to ping {offenders}. Reference
other people "
- "as backticked `login` (no @) so they are not notified, or
override with "
- "STEWARD_ALLOW_MENTIONS=1 for a deliberate ping."
+ f"agent-guard[mention]: a {surface} may only @-mention the PR
author "
+ f"(`{author}`); refusing to notify maintainer(s) {offenders}.
Reference "
+ "them as backticked `login` (no @) so they are not pinged, or
override with "
+ "STEWARD_ALLOW_MENTIONS=1 for a deliberate exception."
)
return None