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 3afe02d  refactor(README): slim to overview + skill index + condensed 
lifecycle (#32)
3afe02d is described below

commit 3afe02dd9428fee6c4467938b7962bfecc90fd63
Author: Jarek Potiuk <[email protected]>
AuthorDate: Sat May 2 17:21:31 2026 +0200

    refactor(README): slim to overview + skill index + condensed lifecycle (#32)
    
    * refactor(README): slim README to overview + categorised skill index + 
condensed lifecycle
    
    The top-level README was 1139 lines, mixing a framework overview with
    a 478-line "Process reference: the 16 steps" deep dive that duplicated
    content already authoritative in each skill's `SKILL.md`. After this
    PR it is 996 lines (-12%, -422/+279 diff) and shaped as:
    
    - **NEW: `## Skills`** — categorised index grouping the 14 skills by
      function (Setup / Security workflow / PR triage and review). PR
      triage section is a placeholder — the `pr-*` skill family is being
      lifted from `apache/airflow` in a follow-up PR.
    
    - **REPLACED: `## Process reference: the 16 steps`** — the deep dive
      was condensed to a Mermaid lifecycle diagram + brief 1-3 sentence
      summary per step that links to the responsible skill's `SKILL.md`
      (the authoritative process doc). Step anchors are preserved so
      existing links still resolve.
    
    - Role sections, Shared conventions, Prerequisites, Label lifecycle,
      and Adopting the framework are unchanged.
    
    The Mermaid diagram colour-codes the lifecycle by role (yellow =
    triager Steps 1-5, blue = remediation developer Steps 6-11, green =
    release manager Steps 12-16, red = invalid-close terminal state) so
    the role hand-offs are visible at a glance.
    
    Per-skill flow diagrams in skill folders (the user-stated "eventually
    in mermaid diagram docs in the skill folders" goal) are intentionally
    deferred to a follow-up PR — keeping this one focused on the README
    restructure.
    
    Generated-by: Claude Code (Claude Opus 4.7)
    
    * docs(README): regenerate TOC for new Skills section
    
    Generated-by: Claude Code (Claude Opus 4.7)
---
 README.md | 705 +++++++++++++++++++++++++-------------------------------------
 1 file changed, 283 insertions(+), 422 deletions(-)

diff --git a/README.md b/README.md
index 13ec691..477dd2f 100644
--- a/README.md
+++ b/README.md
@@ -16,6 +16,10 @@
   - [Keeping the reporter informed](#keeping-the-reporter-informed)
   - [Recording status transitions on the 
tracker](#recording-status-transitions-on-the-tracker)
   - [Confidentiality](#confidentiality)
+- [Skills](#skills)
+  - [Setup](#setup)
+  - [Security workflow](#security-workflow)
+  - [PR triage and review](#pr-triage-and-review)
 - [For issue triagers — Steps 1–6](#for-issue-triagers--steps-16)
   - [Daily triage loop](#daily-triage-loop)
   - [Assessing a report](#assessing-a-report)
@@ -310,6 +314,55 @@ lives in
 [`AGENTS.md` — Confidentiality of the tracker 
repository](AGENTS.md#confidentiality-of-the-tracker-repository).
 Read it before editing anything that might be seen outside the team.
 
+## Skills
+
+The framework's skills are flat folders under
+[`.claude/skills/`](.claude/skills/) — each skill's `SKILL.md` is the
+authoritative process documentation. Skills are named with a category
+prefix so the function is visible at a glance:
+
+### Setup
+
+First-time install of the secure-agent setup, ongoing verification,
+and framework-lifecycle housekeeping.
+
+| Skill | Purpose |
+|---|---|
+| [`setup-secure-config`](.claude/skills/setup-secure-config/SKILL.md) | 
First-time install of the secure agent setup. |
+| 
[`setup-verify-secure-config`](.claude/skills/setup-verify-secure-config/SKILL.md)
 | Verify the secure setup landed correctly. |
+| 
[`setup-update-secure-config`](.claude/skills/setup-update-secure-config/SKILL.md)
 | Surface drift between the installed setup and the framework's latest. |
+| [`setup-upgrade-steward`](.claude/skills/setup-upgrade-steward/SKILL.md) | 
Pull the framework checkout to latest `origin/main`. |
+| [`setup-verify-steward`](.claude/skills/setup-verify-steward/SKILL.md) | 
Verify the framework is integrated correctly into an adopter tracker. |
+| 
[`setup-sync-shared-config`](.claude/skills/setup-sync-shared-config/SKILL.md) 
| Commit + push the user's shared Claude config to its sync repo. |
+
+### Security workflow
+
+The 16-step lifecycle for security-issue handling — from
+`<security-list>` import through CVE publication. See
+[Process reference](#process-reference-the-16-steps) below for the
+cross-skill flow as a diagram.
+
+| Skill | Purpose |
+|---|---|
+| [`security-import-issues`](.claude/skills/security-import-issues/SKILL.md) | 
Import new reports from `<security-list>` into `<tracker>`. |
+| 
[`security-import-issue-from-pr`](.claude/skills/security-import-issue-from-pr/SKILL.md)
 | Open a tracker for a security-relevant fix opened as a public PR. |
+| 
[`security-import-issues-from-md`](.claude/skills/security-import-issues-from-md/SKILL.md)
 | Bulk-import findings from a markdown report. |
+| [`security-sync-issues`](.claude/skills/security-sync-issues/SKILL.md) | 
Reconcile a tracker against its mail thread, fix PR, release train, and 
archives. |
+| [`security-allocate-cve`](.claude/skills/security-allocate-cve/SKILL.md) | 
Allocate a CVE for a tracker (Vulnogram URL + paste-ready JSON). |
+| [`security-fix-issue`](.claude/skills/security-fix-issue/SKILL.md) | 
Implement the fix as a public PR in `<upstream>`. |
+| 
[`security-deduplicate-issues`](.claude/skills/security-deduplicate-issues/SKILL.md)
 | Merge two trackers describing the same root-cause vulnerability. |
+| 
[`security-invalidate-issue`](.claude/skills/security-invalidate-issue/SKILL.md)
 | Close a tracker as invalid with a polite-but-firm reporter reply. |
+
+### PR triage and review
+
+*Coming in a follow-up PR.* The `pr-*` skill family (`pr-triage`,
+`pr-stats`, `pr-maintainer-review`) is being lifted from
+`apache/airflow` into this framework — see
+[Adopting the framework](#adopting-the-framework) for the
+abstraction model that keeps project-specific knobs
+(maintainer roster, CI-check → doc URL map, label conventions) in
+the adopter's `<project-config>/`.
+
 ## For issue triagers — Steps 1–6
 
 You own the tracker from an inbound report on `<security-list>`
@@ -558,481 +611,289 @@ security team to push the information to `cve.org`. See
 
 ## Process reference: the 16 steps
 
-This is the authoritative step-by-step description of the lifecycle. Each
-role section above points into specific steps. If something in the role
-sections conflicts with what is here, the reference wins.
+This is the authoritative outline of the 16-step lifecycle. Each step
+links to the skill or document that owns the deep mechanics — the
+brief descriptions below are an overview, not a substitute for the
+linked skill's `SKILL.md`. If the role sections above conflict with
+what is here, this reference wins.
+
+```mermaid
+flowchart TD
+    S1[1. Report arrives on security-list]
+    S2[2. Import the report]
+    ALT2([alt: import from public PR])
+    S3[3. Discuss CVE-worthiness]
+    S4[4. Escalate stalled discussions]
+    S5{5. Valid or invalid?}
+    INV([Invalid: close + reporter reply])
+    S6[6. Allocate CVE]
+    S7[7. Self-assign + implement]
+    S8[8. Open public PR]
+    S9[9. Open private PR exceptional]
+    S10[10. Link PR + apply pr created]
+    S11[11. PR merged]
+    S12[12. Fix released]
+    S13[13. Send advisory]
+    S14[14. Capture public archive URL]
+    S15[15. Publish CVE + close issue]
+    S16[16. Credit corrections]
+
+    S1 --> S2
+    ALT2 --> S5
+    S2 --> S3
+    S3 --> S4
+    S3 --> S5
+    S4 --> S5
+    S5 -->|valid| S6
+    S5 -->|invalid| INV
+    S6 --> S7
+    S7 --> S8
+    S7 --> S9
+    S8 --> S10
+    S9 --> S10
+    S10 --> S11
+    S11 --> S12
+    S12 --> S13
+    S13 --> S14
+    S14 --> S15
+    S15 --> S16
+
+    classDef triager fill:#fff3cd,stroke:#664d03,color:#000
+    classDef remed fill:#cfe2ff,stroke:#055160,color:#000
+    classDef releaseMgr fill:#d4edda,stroke:#0f5132,color:#000
+    classDef terminal fill:#f8d7da,stroke:#842029,color:#000
+
+    class S1,S2,ALT2,S3,S4,S5 triager
+    class S6,S7,S8,S9,S10,S11 remed
+    class S12,S13,S14,S15,S16 releaseMgr
+    class INV terminal
+```
+
+Colour key: yellow = triager (Steps 1–5), blue = remediation
+developer (Steps 6–11), green = release manager (Steps 12–16),
+red = terminal close.
 
 ### Step 1 — Report arrives on security@
 
-The reporter reports the issue to the adopting project's
-`<security-list>` or to `[email protected]` (in the latter case,
-the security team of the Apache Software Foundation will forward the
-issue to the project's security mailing list).
+The reporter sends the issue to the adopting project's
+`<security-list>` (or to `[email protected]`, which forwards to the
+project list).
 
 ### Step 2 — Import the report
 
-**Import the report into `<tracker>` as a tracking issue.** The
 [`security-import-issues`](.claude/skills/security-import-issues/SKILL.md)
-skill is the on-ramp of the process: it scans `<security-list>`
-for threads that have not yet been imported, classifies each candidate
-(real report vs. automated-scan / consolidated / media / spam), extracts
-the issue-template fields from the root message, and proposes one tracker
-per valid report plus a receipt-of-confirmation Gmail draft for each.
-Nothing is applied without explicit user confirmation. A security team
-member runs the skill (*"import new reports"*) as the first action of a
-triage sweep; the newly-created issue lands with the `needs triage` label
-set automatically by the issue template, and the draft reply is ready in
-Gmail for the triager to review and send.
-
-If the report is "obviously invalid" (we've seen such issues before and
-triaged or responded to them) — for example an automated-scanner dump or a
-consolidated multi-issue report — the skill proposes the matching canned
-response from 
[`<project-config>/canned-responses.md`](<project-config>/canned-responses.md) 
as a Gmail draft
-and does **not** create a tracker, so the invalid class never enters the
+scans `<security-list>` for un-imported threads, classifies each
+candidate (real / automated-scan / consolidated / spam), extracts the
+issue-template fields from the root message, and proposes one tracker
+per valid report plus a Gmail receipt-of-confirmation draft. Nothing
+is applied without explicit confirmation. The newly-created tracker
+lands with `needs triage`.
+
+If the report matches a known-invalid pattern, the skill drafts the
+matching canned reply from
+[`<project-config>/canned-responses.md`](<project-config>/canned-responses.md)
+and does **not** create a tracker — invalid noise never enters the
 board.
 
-The tracker still has no scope label at this point — that is applied at
-Step 5 when validity is confirmed.
-
-**Alternate entry point — found in a public PR.** Occasionally a
-security-relevant fix lands as a public PR on `<upstream>` without ever
-going through `<security-list>` (a contributor opens a routine-looking
-fix that the security team later realises warrants a CVE). For that
-case, use
-[`security-import-issue-from-pr`](.claude/skills/security-import-issue-from-pr/SKILL.md)
-instead of `security-import-issues`. The skill takes a PR URL/number,
-detects scope from the changed file paths, and creates the tracker
-directly in the **`Assessed`** column with the scope label applied —
-the deliberate import implies the security team has already
-informally concluded the report is a security issue, so the
-validity-discussion gate at Step 5 is skipped and the tracker is
-ready for `security-allocate-cve` immediately. There is no reporter to ack on
-this path, so no Gmail draft is created. Only use this skill when the
-security relevance is already agreed; for genuinely-uncertain reports,
-discuss in security team chat first and then either route through
-`security@` if a reporter is involved, or open a `Needs triage`
-tracker manually.
+**Alternate entry — fix already opened as a public PR.** Use
+[`security-import-issue-from-pr`](.claude/skills/security-import-issue-from-pr/SKILL.md).
+The tracker lands directly in the `Assessed` column with the scope
+label applied (validity already decided informally), so Step 5 is
+skipped and the tracker is ready for `security-allocate-cve`
+immediately.
+
+**Alternate entry — bulk import from markdown.** Use
+[`security-import-issues-from-md`](.claude/skills/security-import-issues-from-md/SKILL.md)
+when triaging the output of an AI security review or third-party
+scanner. Each finding becomes one tracker.
 
 ### Step 3 — Discuss CVE-worthiness
 
-In the issue, we discuss and agree on whether it is worth having a CVE for
-it.
+Drive the validity assessment in tracker comments. Pull at least
+one other security-team member into the discussion. Use canned
+responses from
+[`<project-config>/canned-responses.md`](<project-config>/canned-responses.md)
+for negative assessments so the tone stays polite-but-firm.
 
 ### Step 4 — Escalate stalled discussions
 
-If the discussion stalls and we cannot make a decision in about 30 days,
-escalate to a broader audience. Escalation runs in **two phases** —
-phase 1 is a short call for ideas; phase 2, if phase 1 stays silent for
-another ~7 days, is an AI-generated design-space analysis intended to
-unstick the discussion by surfacing concrete options. **Both phases are
-proposals that the triager driving the escalation reviews before
-posting** — the agent never posts to the tracker, the
-`<private-list>` thread, the `<security-list>` thread, or the reporter
-without an explicit "go" from the triager.
-
-Audiences (same for both phases):
-
-* `<private-list>`
-* `[email protected]`
-* the reporter(s) who raised the issue, asking them for their opinion and
-  additional context
-
-#### Phase 1 — short call for ideas (no AI analysis)
-
-The first escalation message is **deliberately short**. It states the
-report exists, that internal discussion has stalled for ~30 days, asks
-the wider audience for input, and links the tracker URL as a stable
-identifier — fine on either private or public audiences per the
-[three-layer confidentiality 
rule](AGENTS.md#confidentiality-of-the-tracker-repository)
-(URLs are public-safe; tracker contents and security framing stay
-embargoed). It does **not** include a design-space analysis or
-proposed fixes — that is phase 2's job. The point of phase 1 is to give domain 
experts who haven't seen
-the report a chance to weigh in with their own ideas, without being
-anchored to a pre-baked solution.
-
-Phase-1 message shape (3-4 short paragraphs):
-
-1. One sentence: tracker `<tracker>#NNN`, CVE `CVE-…`, one-line
-   description.
-2. One sentence: discussion has been stalled for `<N>` days, last
-   substantive comment was `<date>` by `<who>`.
-3. One sentence: we are asking for input on the design / fix-direction;
-   any thoughts welcome.
-4. The relevant links (tracker URL on private audiences only; GHSA URL
-   if applicable; reporter thread ID).
-
-The triager drafts this with the agent assistance, the agent presents
-the draft as a proposal for review (Gmail draft + status-rollup
-preview), and only after the triager confirms does the agent send /
-post.
-
-#### Phase 2 — AI-generated design-space analysis
-
-When phase 1 has been out for **another ~7 days** with no substantive
-response (silence, "+1 looks bad" without a fix proposal, or
-acknowledgement-without-engagement), the agent prepares a deeper
-analysis to unstick the design question. The shape:
-
-1. **Lead with an AI-generated disclaimer** — the analysis was drafted
-   by an AI agent, the triager reviewed it before posting, and errors
-   are possible. Invite reviewers to push back on anything that smells
-   wrong.
-2. **Methodology for tagging** — explain how the people pinged were
-   selected. Standard methodology: cross-reference of (a) top
-   committers since `<date>` on the affected files (commit-frequency
-   via `git log`), (b) the `<tracker>` collaborator list
-   (`gh api repos/<tracker>/collaborators`), and (c)
-   participants already engaged on the thread.
-3. **TL;DR** — one paragraph with the recommended short-term + long-term
-   fix combo.
-4. **Why the obvious fix is insufficient** — if the discussion already
-   surfaced a "just do X" suggestion that the agent thinks is partial,
-   explain the gap concretely (with a code example showing the
-   bypass). This is the most load-bearing part of the analysis — it
-   converts a stalled "should we do X?" into a concrete "X alone
-   doesn't close case Y, here is the alternative".
-5. **Design-space options** — labelled A, B, C, etc. Each option has:
-   *what it does* (concrete code sketch where useful), *what it
-   closes*, *what it does not close*, *risk* (deployment-compat
-   concerns, migration cost, etc.).
-6. **Open design questions** — the 3-5 blockers that need a human
-   decision before any of the options can ship. These are the things
-   the agent cannot decide on its own (which option to take, how to
-   handle migration, whether to deviate from a project convention).
-7. **Tag people inline + at the bottom** with `@`-mentions per the
-   methodology above.
-
-The agent presents the full draft as a proposal — the entire comment
-body, plus the list of `@`-handles it intends to mention with the
-selection rationale. **The triager reviews and approves the draft
-before the agent posts it** to the tracker. The same applies for any
-parallel `<private-list>` mail draft that mirrors the analysis to
-the PMC audience.
-
-If the triager edits the draft, the agent applies the edits and
-re-presents for confirmation. The agent does not "iterate to
-quality" on its own — every visible change to the analysis goes
-through the triager.
-
-#### Recording the escalation on the tracker
-
-Both phases land as rollup entries on the tracker (per
-[`tools/github/status-rollup.md`](tools/github/status-rollup.md)),
-tagged with the action label `Sync (Step 4 escalation)` so the audit
-trail shows where in the timeline each phase was sent. The phase-1
-entry records the message that went out and the silence-period clock
-start; the phase-2 entry records the AI-analysis comment + the list
-of people pinged + the 7-day post-phase-1 silence that triggered
-phase 2.
-
-#### Why phases
-
-Posting an AI-generated analysis as the *first* escalation step
-short-circuits the two best outcomes phase 1 is designed to produce:
-
-- **A domain expert proposing a fix the agent didn't think of.** Phase
-  1 leaves space for ideas the agent's literature is unaware of —
-  particularly for cross-cutting architectural patterns that are
-  obvious to someone with deep area knowledge but not visible from
-  the file diff. The AI analysis tends to converge on
-  reporter-suggested options + standard hardening patterns; it
-  *will* miss novel approaches.
-- **An expert flagging that the report is invalid / out of scope.**
-  Sometimes the right answer is "this is documented behaviour" or
-  "this duplicates `<tracker>#XYZ` from 2 years ago"; an AI analysis
-  that already proposes a fix anchors the discussion towards
-  *implementing* that fix rather than evaluating whether one is
-  needed.
-
-Phase 2 is for when phase 1 has demonstrably failed to produce
-either outcome. Skipping straight to phase 2 turns the escalation
-into a "rubber-stamp this design" ask, which is the opposite of what
-the wider-audience step is for.
+If discussion stalls for ~30 days, escalate in **two phases**:
+
+* **Phase 1 — short call for ideas.** A 3-4-paragraph message that
+  states the report exists and asks the wider audience for input.
+  No AI analysis, no proposed fixes — phase 1 is deliberately bare so
+  domain experts can weigh in with novel ideas without being anchored
+  to a pre-baked solution.
+* **Phase 2 — AI-generated design-space analysis.** Triggered if
+  phase 1 stays silent for ~7 more days. The agent drafts a
+  structured analysis (TL;DR, why-the-obvious-fix-is-insufficient,
+  options A/B/C with trade-offs, open design questions, tagged
+  reviewers per a documented selection methodology). The triager
+  reviews and approves before posting.
+
+Audiences are the same for both phases: `<private-list>`,
+`[email protected]`, the original reporter. Both phases land as
+rollup entries on the tracker (per
+[`tools/github/status-rollup.md`](tools/github/status-rollup.md))
+with the action label `Sync (Step 4 escalation)`.
 
 ### Step 5 — Land the valid/invalid consensus
 
-Finally, if we cannot reach consensus we follow
-[voting](https://www.apache.org/foundation/voting.html#apache-voting-process).
-A vote on code modification is used, which means that committers have
-binding votes, whereas everyone else has advisory votes — and all are
-encouraged to vote and express their opinion. If there is no major
-disagreement during the discussion, there is no need to formally vote via
-a mailing list thread — the voting is done in the PR. However, if there
-are differing opinions, voting is done on the `<security-list>`
-list. The `needs triage` label should then be removed.
+If valid, apply exactly one scope label from
+[`<project-config>/scope-labels.md`](<project-config>/scope-labels.md);
+remove `needs triage`. If invalid,
+[`security-invalidate-issue`](.claude/skills/security-invalidate-issue/SKILL.md)
+labels `invalid`, posts a closing comment, archives the board item,
+and (for `<security-list>`-imported trackers) drafts a polite-but-firm
+reporter reply. If consensus cannot be reached, follow
+[ASF 
voting](https://www.apache.org/foundation/voting.html#apache-voting-process)
+on `<security-list>`.
+
+If a candidate duplicate is detected,
+[`security-deduplicate-issues`](.claude/skills/security-deduplicate-issues/SKILL.md)
+merges two trackers in place — preserving every reporter's credit,
+every mailing-list thread reference, and every independent
+attack-vector description. The kept issue's body is updated, the
+duplicate is closed with the `duplicate` label, and the CVE JSON
+attachment is regenerated so both finders land in `credits[]`.
 
 ### Step 6 — Allocate the CVE
 
-If we agree the issue is invalid, a team member closes the issue and
-responds to the reporter with that information. The
-[`security-invalidate-issue`](.claude/skills/security-invalidate-issue/SKILL.md)
-skill is the apply mechanism: it labels the tracker `invalid`, posts
-a short closing comment, archives the project-board item, and — when
-the tracker has an inbound `<security-list>` thread — drafts a
-polite-but-firm reply to the reporter on the original Gmail thread,
-with reasoning extracted from the tracker's discussion and a fitting
-canned response from
-[`<project-config>/canned-responses.md`](<project-config>/canned-responses.md)
-as the spine. The draft is never sent — the triager reviews in Gmail
-before sending. The skill hard-stops if a CVE is already allocated
-(a Vulnogram REJECT is required first) or if the advisory has
-already shipped (a public retraction needs explicit team
-escalation).
-
-If the issue is valid,
-**a PMC member of the adopting project** allocates a CVE via the
-project's CVE tool (see
-[`<project-config>/project.md → CVE 
tooling`](<project-config>/project.md#cve-tooling)).
-The allocation action is PMC-gated on the server side, so a triager
-who is not on the PMC cannot complete the allocation themselves —
-they prepare the request (using the
-[`security-allocate-cve`](.claude/skills/security-allocate-cve/SKILL.md) 
skill, which
-strips any redundant project prefix from the title per
-`<project-config>/title-normalization.md` and builds a relay
-message) and forward it to a PMC member via an `@`-mention on the
-tracker or on the `<security-list>` thread. Once the PMC member has
-allocated and reported the `CVE-YYYY-NNNNN` back, the skill can be
-re-invoked with the ID as an override to wire the allocated CVE into
-the tracker: the *CVE tool link* body field, the `cve allocated`
-label, a status-change comment, and a refreshed CVE-JSON body embed.
-The skill then hands off to `security-sync-issues` to reconcile the
-rest of the tracker (milestone, assignee, fix-PR state,
-reporter-thread drafts) in the same flow.
-
-The team member (triager or PMC, whoever has the reporter's thread
-loaded) then responds in the email thread to confirm creation of the CVE
-to the reporter, including the CVE ID, asks the reporter how they would
-like to be credited, and updates the reporter name in the issue
-description when the reporter answers.
+[`security-allocate-cve`](.claude/skills/security-allocate-cve/SKILL.md)
+opens the project's CVE allocation tool (for Airflow, ASF Vulnogram
+at <https://cveprocess.apache.org/allocatecve>; in general see
+[`<project-config>/project.md → CVE 
tooling`](<project-config>/project.md#cve-tooling)),
+normalises the title per
+[`<project-config>/title-normalization.md`](<project-config>/title-normalization.md),
+and — if the triager isn't on the PMC — builds an `@`-mention relay
+message for a PMC member. Once the allocated `CVE-YYYY-NNNNN` is
+pasted back, the skill wires it into the tracker (CVE tool link
+body field, `cve allocated` label, status-change comment, refreshed
+CVE-JSON attachment) and hands off to `security-sync-issues` to
+reconcile the rest.
 
 ### Step 7 — Self-assign and implement the fix
 
-One of the team members self-assigns the issue (not necessarily the
-person who originally started the discussion) and implements the fix.
-
-NOTE: In some cases it is possible to delegate the fix to a trusted
-third-party individual. For example, if the security team member assigned
-to the issue has access to developers willing or otherwise dedicated to
-development on the adopting project, they may delegate to one such
-individual, provided that:
-
-1) The individual is trusted.
-2) The individual only receives the information required to implement a
-   fix (no wholesale sharing of security team emails, GitHub issues,
-   etc.).
-3) A LAZY CONSENSUS vote is conducted in either the email thread or the
-   GitHub issue associated with the security issue (GitHub communications
-   are synced to the email group for posterity).
+A security team member self-assigns and implements the fix.
+Optional automation:
+[`security-fix-issue`](.claude/skills/security-fix-issue/SKILL.md)
+proposes an implementation plan, writes the change in your local
+`<upstream>` clone, runs local tests, and opens a public PR via
+`gh pr create --web` with a scrubbed title + body. Every public
+surface (commit message, branch name, PR title, PR body, newsfragment)
+is grep-checked for `CVE-`, the `<tracker>` slug, *"vulnerability"*,
+*"security fix"*, and similar leakage before being written or pushed.
+
+The skill refuses to proceed for reports still being assessed,
+reports not yet classified as valid, and changes that require the
+private-PR fallback (Step 9). Even when it succeeds end-to-end, you
+remain the PR's author and reviewer-facing contact — stay on the PR
+through review and merge.
+
+Delegation to a trusted third-party individual is permitted under
+LAZY CONSENSUS, sharing only the information required to implement
+the fix.
 
 ### Step 8 — Open a public PR (straightforward cases)
 
-If the issue is straightforward, it may be followed by a direct PR
-in the `<upstream>` repository. The description in the PR should not
-reveal the CVE or the security nature of it.
+The PR description **must not** reveal the CVE, the security nature
+of the change, or link back to `<tracker>`. See
+[`AGENTS.md → 
Confidentiality`](AGENTS.md#confidentiality-of-the-tracker-repository).
+Request the appropriate `backport-to-vN-N-test` label on the public
+PR when the fix should ship on a patch train.
 
 ### Step 9 — Open a private PR (exceptional cases)
 
-In exceptional cases — when the issue is highly critical, or when code
-discussion is needed and the PR requires input and review before it gets
-merged — the person solving it can create a PR in the `<tracker>`
-repository with "Closes: #issue". The PR should be raised against the
-`main` branch of the `<tracker>` repository (not the default branch
-declared in `<project-config>/project.md → tracker_default_branch`).
-This allows for detailed code-change discussion in private. For now,
-CI is not run for PRs in the `<tracker>` repository, so static checks
-and tests should be run manually by the person creating the PR. We
-may improve this in the future. Once the PR has been reviewed,
-approved, and is ready to merge, the branch with the fix should be
-pushed to the `<upstream>` repository and the PR should be re-opened
-there by pushing the branch to public `<upstream>` and merging it.
+For highly critical fixes or code that needs private review, open
+the PR against `<tracker>`'s `main` branch first (not the
+`tracker_default_branch` set in `<project-config>/project.md`). CI
+does not run there — run static checks + tests manually. Once
+approved, push the branch to `<upstream>` and re-open the PR there.
 
 ### Step 10 — Link the PR and apply `pr created`
 
-Once the PR is created in the `<upstream>` repository, the team
-member who creates it should link to the PR in the description of the
-issue and mark the issue with the `pr created` label on `<tracker>`.
+The remediation developer links the PR in the tracker description
+and applies `pr created` on `<tracker>`.
 
 ### Step 11 — PR merged
 
-**PR merged.** When the `<upstream>` PR merges, the security team
-member merging it should move the issue from `pr created` to `pr merged`.
-If there is a private variant of the PR in the `<tracker>`
-repository, it should be closed. The milestone of the issue should be set
-to the milestone of the release it is planned to ship in.
-
-The **milestone naming conventions** are project-specific; for the
-currently adopting project see
-[`<project-config>/milestones.md`](<project-config>/milestones.md), which
-also records the policy for creating missing milestones via `gh api` and
-for bumping a fix to the next minor release instead of the next patch
-when triage warrants it.
-
-**The issue stays at `pr merged` until the release containing the fix
-actually ships.** That may be hours (for core patch releases cut on a
-fast cadence) or weeks (for providers waves on a fixed monthly schedule).
-During that window the issue is waiting on the release train, not on any
-action from the security team — the next transition fires automatically
-when the release hits PyPI / the Helm registry (Step 12).
+When the `<upstream>` PR merges, swap `pr created` → `pr merged`
+and set the milestone of the release the fix will ship in (per
+[`<project-config>/milestones.md`](<project-config>/milestones.md)).
+Close any private variant in `<tracker>`. The tracker waits at
+`pr merged` until the release ships — this can be hours (fast core
+patches) or weeks (provider waves on a fixed cadence).
 
 ### Step 12 — Fix released
 
-**Fix released.** When the release carrying the fix actually ships
-to users — the final release artefact (per the adopting project's
-release-train conventions; for Airflow see
-[`<project-config>/milestones.md`](<project-config>/milestones.md))
-is live on the project's package index — the issue moves from
-`pr merged` to `fix released`. The `security-sync-issues` skill
-detects the release (by checking the project's package index for
-the milestone version) and proposes the label swap on the next run,
-so in practice this transition is automatic; a security team member
-only needs to confirm the sync proposal.
-
-**Why this is its own step.** The `pr merged` → `fix released` swap is
-the cue that ownership of the issue has transferred from the fix author /
-triager to the **release manager** for that release. Before
-`fix released`, the issue is a code-change artifact; after `fix released`,
-it is an advisory-coordination artifact and the release manager is
-responsible for steps 13–15 below. Combining the two into one step made
-this ownership hand-off implicit; splitting them makes it explicit and
-surfaces a `fix released` backlog the release manager can drive from the
-board.
-
-**Hand-off comment.** The same `security-sync-issues` pass that proposes
-the `pr merged` → `fix released` swap also proposes posting an explicit
-**release-manager hand-off comment** on the tracker — a self-contained,
-numbered checklist (steps 13–15 from the RM's perspective) that
-@`-mentions the release manager and links to the paste-ready CVE JSON,
-the Vulnogram `#source` and `#email` tabs, and the canned-response
-templates. The comment is a one-shot, posted exactly once per tracker;
-subsequent sync runs detect it via an HTML marker and skip the post.
-This is a separate first-class comment, not a status-rollup entry —
-the rollup is for the security team's audit trail, the hand-off is the
-RM's call-to-action surface.
+When the release containing the fix ships to users (PyPI / Helm
+registry / equivalent),
+[`security-sync-issues`](.claude/skills/security-sync-issues/SKILL.md)
+detects the release version on the next run and proposes the
+`pr merged` → `fix released` swap, which is the hand-off cue from
+remediation developer to release manager. The same pass proposes
+posting a one-shot **release-manager hand-off comment** with a
+numbered checklist (Steps 13–15 from the RM's perspective) and
+links to the paste-ready CVE JSON, the Vulnogram `#source` and
+`#email` tabs, and canned-response templates.
 
 ### Step 13 — Send the advisory
 
-During releases, the release manager looks through `fix released`
-issues on `<tracker>`, updates the project's CVE tool (for Airflow,
-[the ASF CVE tool](https://cveprocess.apache.org); in general see
-`<project-config>/project.md → CVE tooling`), and updates the
-following fields, taking them from the issue:
-
-* CWE (Common Weakness Enumeration) — possible CWEs are available
-  [here](https://cwe.mitre.org/data/index.html)
-* Product name — per the adopting project's scope-label → product
-  mapping (for Airflow, see
-  [`<project-config>/scope-labels.md`](<project-config>/scope-labels.md))
-* Version affected (`0, < Version released`)
-* Short public summary
-* Severity score — based on the
-  [Severity Rating blog post](https://security.apache.org/blog/severityrating).
-  The issue owner should, during discussion on the issue, propose the
-  score and update the ticket. In obvious cases with no objections, this
-  should work in lazy-consensus mode. If there are differing opinions,
-  driving the discussion to achieve consensus is the preferred outcome.
-  Voting may be cast if needed. If the severity has not been decided or
-  consensus reached during earlier discussion, the Release Manager has
-  the final say on the severity score (but should take into account the
-  opinions of the security team). This is to prioritize getting the issue
-  announcement out in a timely manner.
-* References:
-    * `patch` — PR to the fix in the `<upstream>` repository
-* Credits:
-    * `reporter` — reporter(s) of the issue
-    * `remediation developer` — PR author(s)
-
-The release manager also generates the CVE description, sets the CVE to
-REVIEW if feedback is needed and then to READY, and eventually sends the
-announcement emails from the CVE tool. The release manager then adds
-the `announced - emails sent` label and removes the `fix released` label.
-**The issue stays open** at this point — it is closed only at Step 15
-below, after the public archive URL has been captured (Step 14) and the
-CVE record has been moved to PUBLIC in Vulnogram (Step 15). This
-gives the `security-sync-issues` skill one more handoff where it can
-notice a missing archive URL and prompt for it before the issue is
-forgotten.
+The release manager fills the remaining CVE fields:
+
+* CWE — see [cwe.mitre.org](https://cwe.mitre.org/data/index.html);
+* affected versions (`0, < <version released>`);
+* short public summary;
+* severity score per the
+  [ASF severity rating](https://security.apache.org/blog/severityrating)
+  (lazy consensus during discussion; voting if there's disagreement;
+  RM has the final say to keep the announcement on schedule);
+* references — `patch` PR URL on `<upstream>`;
+* credits — `reporter`, `remediation developer`.
+
+The RM generates the description, sets the CVE to REVIEW (then
+READY), and sends the announcement emails from the project's CVE
+tool. Apply `announced - emails sent`, remove `fix released`. **The
+issue stays open** at this point — it closes only at Step 15.
 
 ### Step 14 — Capture the public advisory URL
 
-**Capture the public advisory URL and move the tracker to
-`announced`.** Once the announcement email has been delivered
-and archived, this is done by the next `security-sync-issues` run (or the
-release manager, if they want to drive it by hand):
-
-* retrieves the archive URL from the
-  [users@ list archive](https://lists.apache.org/list.html?<users-list>) —
-  the `security-sync-issues` skill scans the archive for the CVE ID on
-  every run and proposes the URL automatically once it finds a match;
-* pastes the URL into the tracking issue's **Public advisory URL** body
-  field (the field was added to the issue template specifically for this
-  handoff — never reuse the *"Security mailing list thread"* field, which
-  holds the private `security@` thread);
-* regenerates the CVE JSON attachment — `generate-cve-json` now picks up
-  the URL from the body automatically and tags it as `vendor-advisory` in
-  `references[]`, so the attached CVE record carries a resolvable
-  `vendor-advisory` link ready to paste into the ASF CVE tool;
-* **adds the `announced` label** to the tracking issue. The
-  issue **stays open** at this point — closing is the release manager's
-  job in Step 15 below, after they have moved the CVE record to PUBLIC
-  in Vulnogram.
-
-Until the *Public advisory URL* field is populated, the
-`security-sync-issues` skill will not propose moving the issue to
-`announced` — this is deliberate: the field is what the CVE
-record's public `vendor-advisory` reference will point at, and publishing
-a CVE with an empty reference leaks a broken record into `cve.org`.
-
-**Publication-ready notification comment.** The same sync pass that
-populates the *Public advisory URL* body field also proposes posting a
-**publication-ready notification comment** on the tracker — a separate
-first-class comment that `@`-mentions the release manager, summarises
-the deterministic updates that just landed (URL captured, JSON
-regenerated, `announced` label added), and gives the explicit go-ahead
-for the final paste + `READY` → `PUBLIC` move and tracker close. Like
-the Step 12 hand-off comment, it is a one-shot posted exactly once
-per tracker (idempotent on subsequent runs via an HTML marker).
-Together the two comments form a two-part narrative the release
-manager can drive from the tracker page without consulting the rollup
-or external docs.
+Once the announcement is archived on the users@ list, the next
+`security-sync-issues` run finds the URL, populates the *Public
+advisory URL* body field (a dedicated field on the issue template —
+never reuse the *"Security mailing list thread"* field), regenerates
+the CVE JSON attachment (now carrying a `vendor-advisory` reference),
+and adds the `announced` label. The same pass proposes a one-shot
+**publication-ready notification comment** for the release manager.
+
+Until *Public advisory URL* is populated, the sync skill will not
+propose `announced` — publishing a CVE with an empty
+`vendor-advisory` reference would leak a broken record into
+`cve.org`.
 
 ### Step 15 — Publish the CVE record and close the issue
 
-**Push the final CVE record and close the issue.** For every issue
-carrying the `announced` label, the release manager (the
-same person who sent the advisory in Step 13):
-
-* opens the Vulnogram `#source` tab at
-  `https://cveprocess.apache.org/cve5/<CVE-ID>#source`;
-* copies the latest CVE JSON attachment from the tracking issue (the one
-  regenerated in Step 14, now carrying the `vendor-advisory` URL) and
-  pastes it into the `#source` form;
-* saves and moves the record from `READY` to `PUBLIC` in the ASF CVE
-  tool — **this is the final action** that propagates the record to
-  [`cve.org`](https://cve.org);
-* **closes the issue** — do not update any labels. That closes
-  the lifecycle. The `security-sync-issues` skill follows the close
-  with an explicit `archiveProjectV2Item` mutation so the closed
-  tracker leaves the active board permanently
-  (see [`tools/github/project-board.md` — *Archive a board 
item*](tools/github/project-board.md#archive-a-board-item--terminal-state-cleanup)).
-
-This two-step hand-off (sync captures the URL → RM publishes the record)
-means nobody has to remember both halves: the sync skill's responsibility
-ends when the label is `announced`, and the RM's
-responsibility is scoped to taking an `announced` issue to the
-closed state. An issue that sits on `announced` for more than
-a day or two is a signal to ping the RM.
+The release manager opens the project's CVE tool's `#source` view at
+`https://cveprocess.apache.org/cve5/<CVE-ID>#source`, copies the
+latest CVE JSON attachment from the tracker (the one regenerated in
+Step 14), pastes it into the form, saves, and moves the record from
+READY to PUBLIC — propagating to [`cve.org`](https://cve.org). Then
+closes the tracker (no label updates). `security-sync-issues`
+follows the close with an `archiveProjectV2Item` mutation so the
+closed tracker leaves the active board (see
+[`tools/github/project-board.md` — *Archive a board 
item*](tools/github/project-board.md#archive-a-board-item--terminal-state-cleanup)).
+
+A tracker that sits on `announced` for more than a day or two is a
+signal to ping the RM.
 
 ### Step 16 — Credit corrections
 
-If we need to add missing credits (which sometimes happens due to
-copy-and-paste errors and the brittleness of the process), the release
-manager:
+If credits need correction post-announcement, the release manager:
 
-* responds to the announcement emails and mentions the missing credits
-* updates the [ASF CVE tool](https://cveprocess.apache.org) with the
-  missing credits
+* responds to the announcement emails with the missing credits;
+* updates the project's CVE tool with the missing credits;
 * asks the ASF security team to push the information to
-  [cve.org](https://cve.org)
+  [`cve.org`](https://cve.org).
 
 ## Label lifecycle
 


Reply via email to