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 a8c9eab triage: history-based @-mention routing replaces
scope-default (#150)
a8c9eab is described below
commit a8c9eab9a322fe30d016d34f3b44ece1905317e9
Author: Jarek Potiuk <[email protected]>
AuthorDate: Thu May 14 13:05:42 2026 +0200
triage: history-based @-mention routing replaces scope-default (#150)
Replaces the existing scope-based @-mention routing in
security-issue-triage with a history-based priority order:
1. PR-author of the analogous prior fix (via gh search prs)
2. Recent reviewer of the area (via gh pr list reviewed-by)
3. Scope-default fallback (existing behavior, narrowed to 1 pick)
The routing decision is cached per unique code area within a
bulk-mode run so a 5-tracker sweep through one directory issues
one set of gh search prs queries, not five.
Adds an explicit routing-failure fallback: when no PR-history
match exists for a code area, the skill stops and surfaces a
"confirm @-mentions before posting" prompt rather than silently
defaulting to a generic roster ping.
Motivation: scope-default routing produces generic 3-person pings
that the team trains itself to ignore. For airflow-s#406 (missing
ti:self enforcement on /execution/task-reschedules) the right
pick was unambiguous — @amoghrajesh wrote apache/airflow#66071
introducing the mechanism — but the existing scope-default rule
produced a generic providers ping. This PR codifies the priority
order so the classifier reaches the same answer the human triager
reaches by hand.
Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
---
.claude/skills/security-issue-triage/SKILL.md | 91 +++++++++++++++++++++------
1 file changed, 72 insertions(+), 19 deletions(-)
diff --git a/.claude/skills/security-issue-triage/SKILL.md
b/.claude/skills/security-issue-triage/SKILL.md
index b99a616..1f35ba0 100644
--- a/.claude/skills/security-issue-triage/SKILL.md
+++ b/.claude/skills/security-issue-triage/SKILL.md
@@ -524,25 +524,78 @@ people are best placed to answer>?
### `@`-mention routing
-The skill picks **2-3 security-team handles** per comment from
-the roster cached in Step 0. The picking heuristic:
-
-1. **Scope-based** — scope `airflow` (core) routes to the core
- security-team subset; scope `providers` routes to the
- providers maintainers on the security team; scope `chart`
- routes to the chart maintainers. The project's
- `release-trains.md` lists these subsets.
-2. **Topic-specific override** — if a tracker is a variant of
- a recently-closed CVE, also tag the `@`-handle of whoever
- owned that CVE's fix PR (the topic-specific person has the
- richest context).
-3. **Never tag the triager themselves** — the skill is invoked
- by a security-team member; tagging them in their own comment
- is noise. Drop their handle from the routing set before
- composition.
-4. **Never tag the entire roster** — 12+ handles on every
- triage comment trains the team to ignore the pings. Cap at
- 3 per comment, pick by relevance.
+The skill picks **1-3 security-team handles** per comment from
+the roster cached in Step 0. Priority order, applied
+mechanically:
+
+1. **PR-author of the analogous prior fix.** For each tracker,
+ extract the code pointers (file paths, function names) from
+ Step 2. For each pointer, run:
+
+ ```bash
+ gh search prs --repo <upstream> --json author,title,mergedAt,url \
+ -- <pointer> security
+ gh search prs --repo <upstream> --json author,title,mergedAt,url \
+ -- <pointer> fix
+ ```
+
+ The most-recent matching PR's author is the **#1 pick** — they
+ have the deepest current context. Cross-check against the
+ security-team roster cached in Step 0; drop if not on the
+ roster. If multiple PRs are recent, pick the one with the
+ tightest title-match on the tracker's vulnerability class
+ (e.g. "auth" / "deserialize" / "path traversal").
+
+2. **Recent reviewer of the area.** For the same code pointer,
+ find roster members who have reviewed recent PRs:
+
+ ```bash
+ gh pr list --repo <upstream> --search 'reviewed-by:<handle>' \
+ --json files,reviews,mergedAt -- <pointer>
+ ```
+
+ Iterate the roster (cached in Step 0); the roster member with
+ the most-recent review in the area is the **#2 pick**.
+
+3. **Scope-default fallback.** Only if 1 and 2 yield nothing,
+ fall back to the scope-based subset from
+
[`<project-config>/release-trains.md`](../../../<project-config>/release-trains.md).
+ Pick **1 person**, not 3 — a single targeted ping outperforms
+ a roster sweep. The project may declare per-scope expertise
+ hints under a "Security team area-of-expertise hints"
+ subsection of `release-trains.md`; honour those when present
+ but do not require them.
+
+**Cache the routing decision per code area** within the run —
+if 5 trackers all touch `airflow/api_fastapi/execution_api/`,
+the @-mention set is identical for all 5 (computed once,
+re-used).
+
+**Cap at 3 handles per comment**, prefer 2. The triager (cached
+in Step 0 as `viewer_login`) is automatically excluded.
+
+**Topic-specific override** (still applies on top of the above):
+if a tracker is a variant of a recently-closed CVE, also tag
+the `@`-handle of whoever owned that CVE's fix PR (the
+topic-specific person has the richest context). Cap still
+applies; if topic-specific puts you at the cap, drop the
+scope-default fallback.
+
+**Never tag the entire roster.** 12+ handles on every triage
+comment trains the team to ignore the pings. If routing returns
+more than 3 candidates, the cap forces a prioritised pick.
+
+**Routing-failure fallback.** If the `gh search prs` queries
+return nothing (new code area, no prior PRs), the skill stops
+and surfaces *"no PR-history match for `<pointer>`; falling
+back to scope-default — please confirm @-mentions before
+posting"*. The user types the corrected handle(s) into the
+proposal; the skill caches the decision for the rest of the
+run so the same fallback doesn't recur on every tracker.
+
+**Budget**: ≤ 2 `gh search prs` calls per unique code area
+(across all trackers in a bulk run, post-caching). The cache
+makes this trivially cheap even on large sweeps.
The roster source-of-truth is
[`<project-config>/release-trains.md`](../../../<project-config>/release-trains.md);