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 4a2a22d  feat(security-issue-sync): RM handoff — no shell commands; 
sync drives full post-advisory close-out (#222)
4a2a22d is described below

commit 4a2a22dfe2f47ef467c9c0ffebf6a19267b311a1
Author: Jarek Potiuk <[email protected]>
AuthorDate: Tue May 19 00:32:38 2026 +0200

    feat(security-issue-sync): RM handoff — no shell commands; sync drives full 
post-advisory close-out (#222)
    
    Reshape the release-manager hand-off contract so the RM's surface is
    **Vulnogram-UI clicks, reviewer-thread responses, and the advisory
    send** — nothing else. Three coupled changes:
    
    1) **Drop `uv run` invocations from RM-facing instructions** in both
       hand-off templates. The CVE-record API push (and any re-push
       triggered by a body change) is run by the security team during
       sync via `vulnogram-api-record-update`, not by the RM. Even in the
       manual-paste fallback variant, the RM only pastes JSON in the
       `#source` UI; the API tooling is not exposed.
    
    2) **Sync drives the entire post-advisory lifecycle close-out.** On
       the next sync run after the advisory lands in the users-list
       archive, the skill — in a single combined apply triggered by the
       archive-URL signal — captures the URL into the *Public advisory
       URL* body field, **extracts the public-facing short summary from
       the advisory email body** and writes it back to the *Short public
       summary for publish* body field, flips the tracker labels
       (`fix released → announced - emails sent + announced`),
       regenerates and re-pushes the CVE JSON, **moves the Vulnogram
       record `REVIEW → PUBLIC` via the OAuth API** (formerly a manual
       Step-15 click; now driven by sync since the archive URL is the
       real-world signal that the advisory has actually shipped),
       moves the project board to the `Announced` column, and closes the
       tracker.
    
    3) **Sync posts a conditional wrap-up comment** tagging the RM with
       the residual manual steps: archive the now-closed tracker from
       the `Announced` column, and — **only if every sibling on the
       tracker's milestone is also closed at that moment** — close the
       milestone via the URL the comment carries. The conditional
       close-milestone line means the RM never has to check sibling
       state by hand; the milestone close happens when the *last*
       sibling tracker reaches this step.
    
    The previous framing of `REVIEW → PUBLIC` as "intentionally
    human-only" is reversed. The gate is now "published archive URL
    captured", which collapses RM workflow to a small handful of clicks
    and one reviewer-thread response.
    
    Documentation changes in this commit:
    
      - tools/vulnogram/release-manager-handoff-comment-oauth-pushed.md
        fully rewritten: 7 RM-facing steps, no `uv run` blocks, Step 6
        documents the auto-publish flow, Step 7 follows the wrap-up
        comment.
      - tools/vulnogram/release-manager-handoff-comment.md (manual-paste
        variant) reworked to match the same 7-step RM-facing shape with
        paste-into-#source-UI as the fallback when OAuth is unavailable.
        Still no `uv run` invocations RM-facing.
      - .claude/skills/security-issue-sync/SKILL.md — Step 2b's
        advisory-archive row rewritten as the combined-apply trigger;
        lifecycle-states table updated to reflect the collapsed
        14 → 15 transition.
    
    Implementation TBD as a follow-up: a `vulnogram-api-publish` tool
    (REVIEW → PUBLIC via OAuth API), sync code to extract short summary
    from the archived email body, label-flip + tracker-close + wrap-up-
    comment composition. The convention documented here is the target;
    the implementation will follow in a separate PR.
    
    Worked examples landed today on airflow-s/airflow-s as the adopter-
    side dry-run of the convention:
      - airflow-s/airflow-s#295 (CVE-2026-27173)
      - airflow-s/airflow-s#355 (CVE-2026-42526)
    
    The adopter override codifying the same convention is at
    airflow-s/airflow-s .apache-steward-overrides/security-issue-sync.md
    (landed via airflow-s PRs #427, #428, #429 today). When this
    upstream PR + the implementation follow-up land, the override
    becomes redundant and can be removed via /setup-override-upstream.
---
 .claude/skills/security-issue-sync/SKILL.md        |   9 +-
 ...release-manager-handoff-comment-oauth-pushed.md | 171 +++++++++++----------
 tools/vulnogram/release-manager-handoff-comment.md | 170 ++++++++++++++------
 3 files changed, 221 insertions(+), 129 deletions(-)

diff --git a/.claude/skills/security-issue-sync/SKILL.md 
b/.claude/skills/security-issue-sync/SKILL.md
index 393f16d..35eff1b 100644
--- a/.claude/skills/security-issue-sync/SKILL.md
+++ b/.claude/skills/security-issue-sync/SKILL.md
@@ -696,8 +696,8 @@ update, label change, or next-step recommendation in Step 2:
 | Reporter reply with a confirmed credit line (*"please credit me as …"*, 
*"use handle X"*, *"anonymous is fine"*) | Replace the `Reporter credited as` 
placeholder with the confirmed form; mark the credit question as resolved so 
the next status-update draft does not re-ask it. |
 | Reporter explicit opt-out of credit (*"do not credit me"*, *"anonymous"*) | 
Set the field to `anonymous` and flag the advisory to use that form. |
 | Release manager's `[RESULT][VOTE] Release Airflow <version>` on `<dev-list>` 
for a version that carries the fix | Record the release manager in the "Known 
release managers" subsection of [`AGENTS.md`](../../../AGENTS.md) if not 
already there; flag Step 13 (advisory) as assigned to that person. |
-| Advisory message sent to `[email protected]` / `<users-list>` for the CVE 
on the tracker | Propose adding the `announced - emails sent` label and 
removing `fix released`. **Do not propose closing the issue here** — closing is 
gated on the archived public advisory URL being captured (see the next row). |
-| Advisory archived on `<users-list>` (the announcement message is now visible 
in `lists.apache.org/list.html?<users-list>` — scan the archive with the CVE ID 
when `announced - emails sent` is set and the *"Public advisory URL"* body 
field is empty) | Propose populating the *"Public advisory URL"* body field 
with the archive URL, regenerating the CVE JSON attachment (the generator picks 
the URL up automatically and tags it `vendor-advisory`), adding the `announced` 
label, **and moving th [...]
+| Advisory archived on `<users-list>` (the announcement message is now visible 
in `lists.apache.org/list.html?<users-list>` — scan the archive with the CVE ID 
when `fix released` is set and the *"Public advisory URL"* body field is empty) 
| This is the **post-advisory lifecycle close-out trigger**. Propose, in a 
single combined apply: (1) populate the *"Public advisory URL"* body field with 
the archive URL; (2) **extract the public-facing short summary from the 
advisory email body** (the [...]
+| Advisory message sent to `[email protected]` / `<users-list>` but archive 
URL not yet visible | No-op transition; **do not** flip the `fix released → 
announced` labels here. The label flip is part of the combined "archive URL 
captured" apply above and only fires when the archive URL is confirmed live on 
`lists.apache.org` (this is the load-bearing real-world signal that the 
advisory actually shipped — a `[VOTE]/[ANNOUNCE]` mail thread in flight without 
an archived URL is ambiguous). |
 | Project-board column drifted from the issue's label-derived state (e.g. a 
tracker carries `pr merged` but is still in the `PR created` column on [Project 
2](<project-board-url>), or `announced` + *Public advisory URL* body field 
populated but the column is still `Fix released`) | Propose moving the project 
item to the correct column per the mapping table in Step 2b. The board is the 
primary security-team overview surface; a stale column hides ownership handoffs 
from the team at a glance. |
 | `announced` label set and CVE record on `cveprocess.apache.org` now reports 
state PUBLISHED (checked via `curl -s 
https://cveprocess.apache.org/cve5/<CVE-ID>.json` / the ASF CVE tool API, or an 
explicit release-manager comment on the issue stating the Vulnogram push is 
done) | Propose closing the issue. Do not update any labels. This is the 
terminal transition. |
 | CVE record has open **review comments / reviewer proposals** (detected via 
the Gmail-search path in Step 1e — reviewer-comment notifications from 
Vulnogram land on `<security-list>` with the CVE ID in the subject line; the 
`cveprocess.apache.org/cve5/<CVE-ID>.json` endpoint is behind ASF OAuth and is 
not readable from this skill's context, so Gmail is the load-bearing signal 
source). | Surface each open review comment in Step 2a with **clickable links** 
to the Gmail thread and to the C [...]
@@ -917,9 +917,8 @@ process the issue is currently at:
 | Fix PR merged, no release with the fix has shipped yet (swap `pr created` → 
`pr merged`) | 11 |
 | Release with the fix has shipped, advisory not sent yet (swap `pr merged` → 
`fix released`) | 12 |
 | `fix released` set, advisory not yet sent — release manager owns the 
advisory | 13 |
-| Advisory sent, `announced - emails sent` set, *Public advisory URL* body 
field still empty (issue stays open) | 13 → 14 |
-| *Public advisory URL* populated, `announced` label set (issue stays open — 
awaiting RM's Vulnogram push) | 14 |
-| `announced` set and CVE state is PUBLISHED on `cveprocess.apache.org` → 
close the issue (do not update labels) | 15 |
+| Advisory sent, no archive URL yet (no labels flipped; the `fix released → 
announced` label flip is deferred to the combined "archive URL captured" apply) 
| 13 → 14 |
+| **Archive URL captured** — sync's combined apply fires at this moment: 
writes the URL into the body, extracts the public short summary from the 
advisory and writes it into the body, flips `fix released → announced - emails 
sent + announced`, regenerates + re-pushes the JSON, moves the Vulnogram record 
`REVIEW → PUBLIC` via API, moves the board to `Announced`, closes the tracker, 
and posts the conditional wrap-up comment with the milestone URL when 
last-sibling on the milestone. See the [...]
 | **Closed**, `announced` set, cve.org check **not yet run** for this tracker 
since close | post-15 (cve.org publication check — see 
[1g](#1g-recently-closed-trackers--check-cveorg-publication-state)) |
 | Closed, credits missing | 16 |
 
diff --git a/tools/vulnogram/release-manager-handoff-comment-oauth-pushed.md 
b/tools/vulnogram/release-manager-handoff-comment-oauth-pushed.md
index 4ccf7ba..44e2399 100644
--- a/tools/vulnogram/release-manager-handoff-comment-oauth-pushed.md
+++ b/tools/vulnogram/release-manager-handoff-comment-oauth-pushed.md
@@ -25,7 +25,7 @@
      `.claude/skills/security-issue-sync/SKILL.md` for the decision
      flow.
 
-     Idempotency: the marker on line below is the **same** as the
+     Idempotency: the marker on the line below is the **same** as the
      manual-paste variant's. The skill's idempotency grep keys on the
      marker only; the variant choice is detected by re-reading the
      comment body and checking which template's signature it carries.
@@ -33,6 +33,13 @@
      variant but the current push succeeded, the skill PATCH-edits the
      comment body in place to the OAuth-pushed body (no second comment).
 
+     The OAuth-pushed variant intentionally carries **no `uv run`
+     invocations as RM-facing instructions** — the API push (and any
+     re-push triggered by a body change) is run by `security-issue-sync`
+     during sync, not by the release manager. The RM's surface is
+     restricted to Vulnogram UI clicks, reviewer-thread responses, and
+     the advisory send.
+
      Placeholders the skill substitutes:
 
        CVE_ID                    e.g. CVE-2026-40690
@@ -47,13 +54,12 @@
                                  CVE JSON section
        ARCHIVE_SCAN_URL          The PonyMail / archive search URL for
                                  USERS_LIST (parameterised on CVE_ID)
+       MILESTONE_URL             Tracker-side URL of the milestone this
+                                 tracker belongs to (used in the
+                                 conditional close-milestone line of the
+                                 wrap-up comment in Step 6)
        FRAMEWORK_RECORD_MD_URL   Link to tools/vulnogram/record.md on
                                  the framework's GitHub
-       FRAMEWORK_OAUTH_API_README_URL  Link to tools/vulnogram/oauth-
-                                 api/README.md on the framework's
-                                 GitHub
-       FRAMEWORK_PROJECT_PATH    Substitution for `<framework>` in the
-                                 `uv run --project ...` invocations
        FRAMEWORK_SYNC_SKILL_URL  Link to .claude/skills/security-issue-sync/
                                  SKILL.md on the framework's GitHub
        FRAMEWORK_README_URL      Link to README.md on the framework's
@@ -72,89 +78,98 @@
 RM_HANDLE, the release containing the fix has shipped — this tracker
 now belongs to you. **The CVE JSON has already been pushed to
 [`#source`](SOURCE_TAB_URL) by the security team via the OAuth API at
-`PUSH_TIMESTAMP`**, so the only remaining writes for you are the
-Vulnogram-UI state-transition clicks, the advisory send, and the
-close.
-
-The paste-ready CVE JSON is embedded in this tracker's
-[issue body](JSON_ANCHOR_URL) and is regenerated automatically on
-every body change by the [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL)
-skill — *and re-pushed via the OAuth API on the same sync run*, so the
-Vulnogram record at [`#source`](SOURCE_TAB_URL) stays in lock-step with
-the tracker body. No paste step is required from you in the normal
-case.
+`PUSH_TIMESTAMP`**, and `security-issue-sync` keeps the record in
+lock-step with the tracker body on every subsequent sync run. Your
+remaining work is a small handful of Vulnogram-UI clicks plus the
+advisory send — no shell commands required.
 
 ### Step-by-step
 
-1. **Verify the record content.** Open [`#source`](SOURCE_TAB_URL) and
-   confirm it matches the [tracker body's embedded JSON](JSON_ANCHOR_URL).
-   The two should be byte-identical; if they diverge, the OAuth push
-   failed silently and the sync's recap will have flagged it — fall
-   back to the manual-paste instructions further down.
-
-2. **Move `DRAFT` → `REVIEW`** via the Vulnogram UI button.
-
-3. **Wait for CNA review.** Reviewer comments arrive by email on
-   `SECURITY_LIST` with the CVE ID in the subject line.
-   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) detects them and
-   proposes matching body-field updates on this tracker; the security
-   team confirms, the embedded JSON regenerates, and the API re-push
-   lands automatically on the same sync run. Each push lands a fresh
-   entry on this tracker's rollup comment so you can see what changed
-   without opening Vulnogram. **If no reviewer comments arrive (common
-   case), skip to step 5.**
-
-4. **Re-verify** the record at [`#source`](SOURCE_TAB_URL) when the
-   rollup shows a subsequent push entry (the body has changed since
-   `PUSH_TIMESTAMP`).
-
-5. **Set `READY`.** Vulnogram UI button.
-
-6. **Preview the advisory email** on the [email tab](EMAIL_TAB_URL).
-   If anything needs to change, edit the corresponding tracker body
-   field; the JSON regenerates + auto-pushes; then re-preview.
-
-7. **Send advisory emails** from Vulnogram. The form sends to
-   `USERS_LIST` and `ANNOUNCE_LIST`. Then on this tracker, add the
-   `announced - emails sent` label and remove `fix released`.
-
-8. **(automatic) Archive URL captured.**
-   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) scans the
-   [users-list archive](ARCHIVE_SCAN_URL) for the CVE ID on every run.
-   Once it finds the advisory, it populates the *Public advisory URL*
-   body field, regenerates the JSON to include the archive URL as a
-   `vendor-advisory` reference, **re-pushes via the OAuth API**, adds
-   the `announced` label — **and posts a follow-up comment on this
-   tracker** giving you the explicit go-ahead for the final state
-   move below.
-
-9. **Move `REVIEW` → `PUBLIC`** via the Vulnogram UI button. *Only
-   after the follow-up comment in step 8 fires.* The data write is
-   already done; the state transition is intentionally human-only
-   because it triggers the CNA-feed dispatch to `cve.org`.
-
-10. **Close the tracker** — close as completed; do not update any
-    labels. [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) archives
-    the project-board item so the closed tracker leaves the active
-    board.
+1. **Confirm the record is in `REVIEW`.** Open
+   [`#source`](SOURCE_TAB_URL). If the record is still in `DRAFT`,
+   click the Vulnogram UI button to move `DRAFT` → `REVIEW`. (The
+   data is already in place — `PUSH_TIMESTAMP`.)
+
+2. **Respond to any pending reviewer asks** in the
+   [`#email` tab](EMAIL_TAB_URL). Reviewer comments arrive by email on
+   `SECURITY_LIST` with the CVE ID in the subject line —
+   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) detects them
+   automatically and updates the tracker body if a field needs to
+   change. If the body changes, the JSON regenerates and re-pushes as
+   part of the next sync; you do not push manually.
+
+3. **Set `READY`** via the Vulnogram UI button when the reviewer
+   thread closes. The record is now ready for the advisory-send step.
+
+4. **Preview the advisory email** on the
+   [`#email` tab](EMAIL_TAB_URL). Inspect how the email will render:
+   subject, body, recipient list. The preview surfaces formatting
+   issues (truncation, broken markdown, missing patch links) that the
+   JSON view does not. If anything needs to change, edit the
+   corresponding tracker body field — the JSON regenerates and
+   re-pushes; re-preview before sending.
+
+5. **Send the advisory** from the Vulnogram form. The form sends to
+   `USERS_LIST` and `ANNOUNCE_LIST`. **Do not touch the tracker
+   labels** — sync handles the label flips automatically when it sees
+   the advisory on the users-list (see Step 6).
+
+6. **(fully automatic — sync skill drives the lifecycle close-out.)**
+   On the next sync run after the advisory lands in the
+   [users-list archive](ARCHIVE_SCAN_URL),
+   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL):
+   - Captures the published advisory URL into the *Public advisory
+     URL* body field.
+   - **Extracts the public-facing short summary** from the advisory
+     email body and writes it back to the *Short summary for the
+     publish* body field, so the tracker matches what actually
+     shipped.
+   - **Flips the tracker labels**: adds `announced - emails sent` and
+     `announced`; removes `fix released`. The `announced` label
+     triggers the project-board automation to move the item from the
+     `Fix released` column to the `Announced` column.
+   - Regenerates the embedded CVE JSON (now picking up the updated
+     short summary as `descriptions[].value` and the archive URL as a
+     `vendor-advisory` reference) and **re-pushes the JSON to the
+     Vulnogram record** over the OAuth API.
+   - **Moves the record `REVIEW → PUBLIC`** via the OAuth API. This
+     triggers the CNA-feed dispatch to `cve.org`. (Previously a
+     manual UI click; sync now drives it on the same trigger as the
+     archive-URL capture, since the archive URL is the real-world
+     signal that the advisory has actually shipped.)
+   - **Closes the tracker** as `completed`.
+   - **Posts a follow-up comment** tagging the RM with the wrap-up
+     checklist: archive the now-closed tracker from the project
+     board's `Announced` column, and — **if every sibling on the
+     tracker's milestone is also closed** at that moment — close the
+     milestone via the link in the comment ([`MILESTONE_URL`](MILESTONE_URL)).
+     If other siblings on the milestone are still open, the wrap-up
+     comment omits the close-milestone line; the close happens when
+     the *last* sibling tracker reaches the same Step 6.
+
+7. **Follow the wrap-up comment** posted by sync in Step 6. Archive
+   the closed tracker from the project board's `Announced` column
+   (it stays accessible via the *Archived items* filter). If the
+   comment also linked the milestone (last-sibling case), click
+   through and close it — that's the explicit *"everything destined
+   for this release has shipped and been announced"* signal.
 
 ---
 
 **If the OAuth push fails on a future sync** (session expired, schema
-rejection, transient HTTP error), the sync's recap will flag it and
-this comment will be edited to the manual-paste variant — re-open
-[`#source`](SOURCE_TAB_URL), paste the JSON from the
-[tracker body](JSON_ANCHOR_URL), click **Save**, then continue with the
-state transitions above. The most common cause is a stale session
-cookie — re-run `uv run --project 
FRAMEWORK_PROJECT_PATH/tools/vulnogram/oauth-api vulnogram-api-setup`
-(see [`oauth-api/README.md`](FRAMEWORK_OAUTH_API_README_URL)) and the
-next sync will resume auto-pushing.
+rejection, transient HTTP error), the sync's recap surfaces the
+failure and PATCH-edits this comment back to the manual-paste variant
+([`release-manager-handoff-comment.md`](FRAMEWORK_RECORD_MD_URL)). The
+most common cause is a stale OAuth session cookie on the operator
+machine — the security team re-runs `vulnogram-api-setup`, the next
+sync resumes auto-pushing, and the comment flips back to this
+variant. **You as the RM are never asked to run shell commands** in
+this fallback path.
 
 ---
 
 **References:**
 
-- Vulnogram state machine + paste flow: 
[`tools/vulnogram/record.md`](FRAMEWORK_RECORD_MD_URL).
-- OAuth API setup + record-update one-liner: 
[`oauth-api/README.md`](FRAMEWORK_OAUTH_API_README_URL).
+- Vulnogram state machine: 
[`tools/vulnogram/record.md`](FRAMEWORK_RECORD_MD_URL).
 - Reusable email wording (if you draft anything by hand): 
[`canned-responses.md`](CANNED_RESPONSES_URL).
 - Full lifecycle (Steps 12-15): 
[`README.md`](FRAMEWORK_README_URL#for-release-managers--steps-1215).
diff --git a/tools/vulnogram/release-manager-handoff-comment.md 
b/tools/vulnogram/release-manager-handoff-comment.md
index 813e2e5..aaac446 100644
--- a/tools/vulnogram/release-manager-handoff-comment.md
+++ b/tools/vulnogram/release-manager-handoff-comment.md
@@ -11,9 +11,27 @@
      https://www.apache.org/licenses/LICENSE-2.0 -->
 
 <!--
-     Comment-template body for the release-manager hand-off comment
+     Manual-paste variant of the release-manager hand-off comment
      posted by `security-issue-sync` at the `pr merged` → `fix released`
-     transition (Step 12 of the lifecycle).
+     transition (Step 12 of the lifecycle), when the OAuth API push
+     did not succeed (no OAuth credentials configured on the operator
+     machine, session expired, transient HTTP error, schema rejection).
+
+     This template is the counterpart to
+     `release-manager-handoff-comment-oauth-pushed.md`. The sync skill
+     picks between the two based on the outcome of `vulnogram-api-check`
+     + `vulnogram-api-record-update` — see Step 5b of
+     `.claude/skills/security-issue-sync/SKILL.md` for the decision
+     flow.
+
+     **No `uv run` invocations are RM-facing in this template either.**
+     When the OAuth push fails, the security team's next sync resolves
+     the issue (re-run `vulnogram-api-setup`, retry the push, etc.).
+     The RM's surface stays the same as in the OAuth-pushed variant
+     plus an explicit "you may need to paste the JSON into #source"
+     fallback if the security team cannot resolve the OAuth path. The
+     paste itself is a UI action — click Edit → paste → Save — not a
+     shell command.
 
      Placeholders the skill substitutes:
 
@@ -31,29 +49,24 @@
                                  
https://github.com/<tracker>/issues/<N>#cve-json--paste-ready-for-cve-yyyy-nnnn)
        ARCHIVE_SCAN_URL          The PonyMail / archive search URL for
                                  USERS_LIST (parameterised on CVE_ID)
+       MILESTONE_URL             Tracker-side URL of the milestone this
+                                 tracker belongs to (used in the
+                                 conditional close-milestone line of the
+                                 wrap-up comment in Step 6)
        FRAMEWORK_RECORD_MD_URL   Link to tools/vulnogram/record.md on
                                  the framework's GitHub
-       FRAMEWORK_OAUTH_API_README_URL  Link to tools/vulnogram/oauth-
-                                 api/README.md on the framework's
-                                 GitHub (the cookie-capture walkthrough
-                                 the API one-liner depends on)
-       FRAMEWORK_PROJECT_PATH    Substitution for `<framework>` in the
-                                 `uv run --project ...` invocations —
-                                 typically `.apache-steward/apache-
-                                 steward` for an adopter and `.` for
-                                 the framework standalone.
-       FRAMEWORK_SYNC_SKILL_URL  Link to .claude/skills/sync-security-
-                                 issue/SKILL.md on the framework's GitHub
+       FRAMEWORK_SYNC_SKILL_URL  Link to .claude/skills/security-issue-sync/
+                                 SKILL.md on the framework's GitHub
        FRAMEWORK_README_URL      Link to README.md on the framework's
                                  GitHub (Steps 12-15 anchor)
        CANNED_RESPONSES_URL      Link to <project-config>/canned-
                                  responses.md on the tracker's GitHub
 
-     The HTML marker on line 1 is load-bearing: the skill detects an
+     The HTML marker on line 60 is load-bearing: the skill detects an
      already-posted hand-off comment by grepping for this exact string
      and skips the post on subsequent sync runs (idempotency).
 
-     Do not paraphrase the marker. Do not move it off line 1. Do not
+     Do not paraphrase the marker. Do not move it off line 60. Do not
      add a `<!-- v2 -->` until the schema actually changes — the
      skill's grep is anchored on `v1`.
 -->
@@ -62,45 +75,110 @@
 ## 🚀 Release-manager hand-off — `CVE_ID`
 
 RM_HANDLE, the release containing the fix has shipped — this tracker
-now belongs to you. The Vulnogram-specific recipe lives in
+now belongs to you. The OAuth-API push of the CVE JSON did not
+succeed during the last sync (the security team will retry on the
+next pass); until that lands, the paste-ready CVE JSON in this
+tracker's [issue body](JSON_ANCHOR_URL) is the canonical version. If
+the OAuth path remains blocked when you start working through the
+checklist below, the manual-paste fallback at the bottom tells you
+what to copy where — **all via the Vulnogram UI, no shell commands
+required from you**.
+
+The Vulnogram-specific recipe lives in
 [`tools/vulnogram/record.md` — *Release-manager 
checklist*](FRAMEWORK_RECORD_MD_URL);
 the high-level numbered checklist below is here for at-a-glance
 reference without leaving this issue.
 
-The paste-ready CVE JSON is embedded in this tracker's
-[issue body](JSON_ANCHOR_URL) and is regenerated automatically on
-every body change by the [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) 
skill.
-
 ### Step-by-step
 
-Each *write* below is a `vulnogram-api-record-update` call by
-default (the API path bypasses the manual paste). The fallback —
-[`#source` tab](SOURCE_TAB_URL) → paste → **Save** — works
-identically for any operator who has not run `vulnogram-api-setup`
-or whose session has expired.
-
-1. **First write — `DRAFT` → `REVIEW`.** Run the API one-liner below (or fall 
back to opening [`#source`](SOURCE_TAB_URL) and pasting the JSON from the 
[tracker body](JSON_ANCHOR_URL)). Then move the record `DRAFT` → `REVIEW` via 
the Vulnogram UI button.
-
-   ```bash
-   uv run --project FRAMEWORK_PROJECT_PATH/tools/vulnogram/oauth-api 
vulnogram-api-record-update \
-     --cve-id CVE_ID --json-file <path-to-the-embedded-JSON>
-   ```
+1. **Confirm the record content matches the tracker body.** Open
+   [`#source`](SOURCE_TAB_URL) and compare to the
+   [tracker body's embedded JSON](JSON_ANCHOR_URL). If they diverge
+   (the OAuth push remained blocked), follow the manual-paste
+   fallback at the bottom of this comment — open
+   [`#source`](SOURCE_TAB_URL), paste the embedded JSON, click
+   **Save**. Then click `DRAFT → REVIEW` via the Vulnogram UI button.
+
+2. **Respond to any pending reviewer asks** in the
+   [`#email` tab](EMAIL_TAB_URL). Reviewer comments arrive by email on
+   `SECURITY_LIST` with the CVE ID in the subject line —
+   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL) detects them
+   automatically and updates the tracker body if a field needs to
+   change. If the body changes and the OAuth push is healthy by then,
+   the JSON re-push runs in the next sync; if not, follow the
+   manual-paste fallback again to land the regenerated JSON.
+
+3. **Set `READY`** via the Vulnogram UI button when the reviewer
+   thread closes. The record is now ready for the advisory-send step.
+
+4. **Preview the advisory email** on the
+   [`#email` tab](EMAIL_TAB_URL). If anything needs to change, edit
+   the corresponding tracker body field; the JSON regenerates and (if
+   the OAuth push is healthy) re-pushes; re-preview before sending.
+
+5. **Send the advisory** from the Vulnogram form. The form sends to
+   `USERS_LIST` and `ANNOUNCE_LIST`. **Do not touch the tracker
+   labels** — sync handles the label flips automatically when it sees
+   the advisory on the users-list (see Step 6).
+
+6. **(fully automatic — sync skill drives the lifecycle close-out.)**
+   On the next sync run after the advisory lands in the
+   [users-list archive](ARCHIVE_SCAN_URL),
+   [`security-issue-sync`](FRAMEWORK_SYNC_SKILL_URL):
+   - Captures the published advisory URL into the *Public advisory
+     URL* body field.
+   - **Extracts the public-facing short summary** from the advisory
+     email body and writes it back to the *Short summary for the
+     publish* body field, so the tracker matches what actually
+     shipped.
+   - **Flips the tracker labels**: adds `announced - emails sent` and
+     `announced`; removes `fix released`. The `announced` label
+     triggers the project-board automation to move the item from the
+     `Fix released` column to the `Announced` column.
+   - Regenerates the embedded CVE JSON (now picking up the updated
+     short summary as `descriptions[].value` and the archive URL as a
+     `vendor-advisory` reference).
+   - **If the OAuth push is healthy**: re-pushes the regenerated JSON
+     and **moves the record `REVIEW → PUBLIC`** via the OAuth API.
+   - **If the OAuth push is still blocked**: the wrap-up comment from
+     this step names the `#source`-tab paste-and-Save + UI-click
+     `REVIEW → PUBLIC` as your manual fallback (single UI session).
+   - **Closes the tracker** as `completed`.
+   - **Posts a follow-up comment** tagging the RM with the wrap-up
+     checklist: archive the now-closed tracker from the project
+     board's `Announced` column, and — **if every sibling on the
+     tracker's milestone is also closed** at that moment — close the
+     milestone via the link in the comment ([`MILESTONE_URL`](MILESTONE_URL)).
+     If other siblings on the milestone are still open, the wrap-up
+     comment omits the close-milestone line; the close happens when
+     the *last* sibling tracker reaches the same Step 6.
+
+7. **Follow the wrap-up comment** posted by sync in Step 6. Archive
+   the closed tracker from the project board's `Announced` column
+   (it stays accessible via the *Archived items* filter). If the
+   comment also linked the milestone (last-sibling case), click
+   through and close it — that's the explicit *"everything destined
+   for this release has shipped and been announced"* signal.
 
-   First-time setup: `uv run --project 
FRAMEWORK_PROJECT_PATH/tools/vulnogram/oauth-api vulnogram-api-setup` and 
follow the cookie-capture walkthrough in 
[`oauth-api/README.md`](FRAMEWORK_OAUTH_API_README_URL). When the session ages 
out, the call exits with `SessionExpired` and the same setup re-runs in ~30 s.
-
-2. **Wait for CNA review.** Reviewer comments arrive by email on 
`SECURITY_LIST` with the CVE ID in the subject line. The `security-issue-sync` 
skill detects them automatically and proposes matching body-field updates on 
this tracker; the security team confirms and the embedded JSON regenerates. 
**If no reviewer comments arrive (common case), skip to step 4.** Otherwise, 
once the body has settled, re-run the same `vulnogram-api-record-update` 
one-liner with the regenerated JSON (or, fall [...]
-3. **Set `READY`.** Vulnogram UI action — the record is now ready for the 
advisory-send step.
-4. **Preview the advisory email** on the [email tab](EMAIL_TAB_URL) of the CVE 
record. Inspect how the email will render: subject, body, recipient list. The 
preview surfaces formatting issues (truncation, broken markdown, missing patch 
links) that the JSON view does not. If anything needs to change, edit the 
corresponding body field on this tracker, wait for the JSON to regenerate, 
re-run the API one-liner (or re-paste in [`#source`](SOURCE_TAB_URL)), and 
re-preview before sending.
-5. **Send advisory emails** from Vulnogram. The form sends to `USERS_LIST` and 
`ANNOUNCE_LIST`. Then on this tracker, add the `announced - emails sent` label 
and remove `fix released`.
-6. **(automatic) Archive URL captured.** The `security-issue-sync` skill scans 
the [users-list archive](ARCHIVE_SCAN_URL) for the CVE ID on every run. Once it 
finds the advisory, it populates the *Public advisory URL* body field, 
regenerates the CVE JSON to carry the archive URL as a `vendor-advisory` 
reference, and adds the `announced` label — **and posts a follow-up comment on 
this tracker** giving you the explicit go-ahead for the final write below.
-7. **Second write — `REVIEW` → `PUBLIC`.** *Only after the follow-up comment 
in step 6 fires.* Re-run the API one-liner with the now-final JSON (carrying 
the archive URL in `references[]`), or, fallback, re-open 
[`#source`](SOURCE_TAB_URL) and paste. Then move `REVIEW` → `PUBLIC` via the 
Vulnogram UI button (this transition is intentionally human-only because it 
triggers the CNA-feed dispatch to `cve.org`).
-
-   ```bash
-   uv run --project FRAMEWORK_PROJECT_PATH/tools/vulnogram/oauth-api 
vulnogram-api-record-update \
-     --cve-id CVE_ID --json-file <path-to-the-final-JSON>
-   ```
+---
 
-8. **Close the tracker** — close as completed; do not update any labels. The 
`security-issue-sync` skill archives the project-board item so the closed 
tracker leaves the active board.
+**Manual-paste fallback** — only when the OAuth push remained blocked
+through to the advisory-send pass:
+
+- Open [`#source`](SOURCE_TAB_URL) for the CVE record.
+- Open the [tracker body's embedded JSON](JSON_ANCHOR_URL) section.
+- Copy the JSON block; paste it into Vulnogram's `#source` editor;
+  click **Save**.
+- Repeat after any subsequent body-field change on the tracker (the
+  embedded JSON regenerates automatically on each change; the paste
+  is the only step that does not happen automatically when the OAuth
+  push is blocked).
+
+The security team's next sync run resolves the underlying OAuth
+issue (re-run `vulnogram-api-setup`, retry the push) and the comment
+PATCH-edits back to the OAuth-pushed variant. **You as the RM are
+never asked to run `vulnogram-api-*` shell commands** in this
+fallback path.
 
 ---
 


Reply via email to