From b068cd053ed60263ef9c5818e5bbd8426b07a466 Mon Sep 17 00:00:00 2001 From: Jarek Potiuk Date: Thu, 14 May 2026 11:49:54 +0200 Subject: [PATCH] triage: history-based @-mention routing replaces scope-default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .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 82c8d13c..e9306b29 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 --json author,title,mergedAt,url \ + -- security + gh search prs --repo --json author,title,mergedAt,url \ + -- 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 --search 'reviewed-by:' \ + --json files,reviews,mergedAt -- + ``` + + 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 + [`/release-trains.md`](../../..//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 ``; 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 [`/release-trains.md`](../../..//release-trains.md);