This is an automated email from the ASF dual-hosted git repository.

lukaszlenart pushed a commit to branch vote-mail-skill
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 3a40b458845dd6624e51baa6cc5f1a21b47630bd
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sat Aug 8 07:50:17 2026 +0200

    docs: add creating-release-vote-mail skill
    
    Drafts the [VOTE] Apache Struts X.Y.Z mail as a rendering of the
    published Version Notes page. Scoped to the five failures three baseline
    agents actually made without it: security detail routed through a
    private companion mail, a body grown beyond the page, user@ on Cc,
    sending rather than drafting, and a paragraph inserted into the frozen
    vote boilerplate.
    
    Everything the baselines already got right - rendering from the page,
    precondition checks, security truncation, checkboxes, subject, Bcc - is
    cross-referenced to creating-version-notes and creating-security-bulletins
    rather than restated.
    
    Co-Authored-By: Claude Opus 5 <[email protected]>
---
 .claude/skills/creating-release-vote-mail/SKILL.md | 131 +++++++++++++++++++
 .../vote-mail-template.md                          | 139 +++++++++++++++++++++
 2 files changed, 270 insertions(+)

diff --git a/.claude/skills/creating-release-vote-mail/SKILL.md 
b/.claude/skills/creating-release-vote-mail/SKILL.md
new file mode 100644
index 000000000..6429f8716
--- /dev/null
+++ b/.claude/skills/creating-release-vote-mail/SKILL.md
@@ -0,0 +1,131 @@
+---
+name: creating-release-vote-mail
+description: Use when opening the formal release vote for a Struts release 
candidate on any maintenance line (6.x, 7.x) - composing and drafting the 
[VOTE] Apache Struts X.Y.Z mail to dev@ once the Version Notes page, GitHub 
release and staged artifacts are published.
+---
+
+# Creating a Release Vote Mail
+
+## Overview
+
+The `[VOTE]` mail opens the formal release vote. It is four links wrapped in 
frozen ASF
+boilerplate, around a plain-text rendering of the release's Version Notes page.
+
+**Core principle:** the mail is a *rendering* of the page, not a second 
account of the release.
+
+**This is the step after `creating-version-notes`.** That skill produces the 
page, the GitHub
+release and the `[TEST]` announcement; this one consumes all three. If they do 
not exist yet,
+you are in the wrong skill.
+
+[`vote-mail-template.md`](vote-mail-template.md) is the source of truth for 
the artifact.
+
+## The mail is exactly these parts, in this order
+
+1. The two-sentence opener
+2. The page's `Breaking changes`, `Deprecations` and `Rejected requests`, 
where present
+3. The page's issue-type sections, in page order
+4. The four link lines
+5. The vote boilerplate
+6. The sign-off
+
+**A part not on this list is not in the mail, and one mail is produced, not 
two.** Every
+section is the page's content; the opener is the only prose you write.
+
+The pull here is toward helpfulness — an upgrade-notes section derived from 
the fix commits, a
+summary of what changed for integrators, a companion note to a subset of 
recipients. All of it
+is real work that belongs somewhere else. A vote is a judgement on the staged 
artifacts, and
+the page is what describes them.
+
+## The vote carries no security information
+
+No severity, no CVE, no S2-XXX, no bulletin link, no attack description, no 
reporter or
+coordination detail. **That disclosure happens after the vote passes and the 
version is
+released.**
+
+**The restriction is on the vote, not on the audience.** Routing advisory 
detail through
+`private@`, a Cc, an attachment or a companion mail is the same violation as 
putting it in the
+body — a second mail sent to open the vote is part of the vote. "The 
recipients already hold
+this information" is not an exemption; the vote is simply not the vehicle.
+
+Neutral ticket summaries carried from the page are not security information. 
Keep them exactly
+as the page has them, including where the page truncates one at a clause 
boundary.
+
+**REQUIRED BACKGROUND:** `creating-security-bulletins` governs what may be 
said, and when.
+
+## Recipients
+
+```
+To:  [email protected]
+Bcc: [email protected]
+```
+
+**`[email protected]` must not appear in any header — not To, not Cc, not 
Bcc.** The
+`[TEST]` mail one step earlier goes to `dev@` and `user@`, which is right for 
it: it asks
+people to test. This mail asks people to *vote*, and a vote invitation on the 
user list
+solicits votes that are not binding and scatters the tally across two lists.
+
+Cc is not a compromise. If a release manager asks you to include the user 
list, the answer is
+that the `[TEST]` mail already did.
+
+`private@` goes on **Bcc, not Cc**: on the 7.1.1 and 6.8.0 votes it was on Cc, 
and reply-all
+`+1`s landed on the private PMC list.
+
+Subject is exactly `[VOTE] Apache Struts X.Y.Z`.
+
+## Draft it, do not send it
+
+Create a Gmail draft with To, Bcc, Subject and body set. **Never send.**
+
+| Rationalization | Reality |
+|---|---|
+| "The release manager authorised whatever I produce" | Authorisation to 
compose is not authorisation to transmit. |
+| "Every fact is verified; review would catch nothing" | Sending is not a 
quality gate, it is a commitment. Verification does not confer it. |
+| "A draft doesn't open the vote, which defeats the request" | Correct, and 
that is the right outcome when the release manager is not there to send it. |
+| "The 72-hour clock is the reason for the hurry" | A vote opened on the wrong 
artifacts costs far more than the hours saved. |
+
+Sending opens a binding vote on a permanently archived public list and commits 
the PMC to the
+artifacts as staged.
+
+## The boilerplate is frozen
+
+Everything from `Once you have had a chance to review the test build` to the 
sign-off is
+byte-identical to the template. **Inserting a paragraph between existing ones 
is an edit** —
+that is how it actually gets broken, not by rewording.
+
+If something about this release needs explaining to voters, it belongs in the 
opener, above
+the vote call. The vote call itself says the same thing every release, which 
is what lets a
+voter skim to the checkboxes.
+
+## What this skill does not restate
+
+Cross-references, not copies:
+
+- `creating-version-notes` — the page, the GitHub release, the `[TEST]` mail, 
and what belongs
+  on them. The issue list, Breaking changes wording and ticket reconciliation 
are settled
+  there; render what the page says.
+- `creating-security-bulletins` — what may be said about an unpublished 
advisory.
+
+Before drafting, confirm all four links resolve and the GitHub release is 
still a prerelease.
+A vote opened on a 404 burns the window before anyone can test.
+
+## Red Flags — STOP
+
+- Any part in the mail that is not on the six-item list
+- A second mail produced alongside the vote
+- Severity, CVE, S2-XXX, bulletin link or reporter detail anywhere, on any 
channel
+- `[email protected]` in any header, including Cc
+- Sending rather than drafting
+- A new paragraph inserted into the vote boilerplate
+- A quality checkbox arriving pre-ticked
+- An opening sentence carried over from the previous release
+
+## Common Mistakes
+
+| Mistake | Reality |
+|---|---|
+| "Voters can't judge fixes they can't see" | They can open the restricted 
bulletins themselves. The vote is not the disclosure channel. |
+| "It's only going to private@, so nothing leaks" | The rule is about the 
vote, not the audience. A companion mail is part of the vote. |
+| "Cc'ing user@ keeps the vote on dev@ and still informs them" | The `[TEST]` 
mail informed them. Cc splits the tally. |
+| "I verified everything, so I can send" | Verification earns a draft. Sending 
is the release manager's keystroke. |
+| "I'm adding to the boilerplate, not changing it" | Insertion is editing. The 
vote call is byte-frozen. |
+| "The release notes leave out what integrators need" | Then the page needs 
fixing. The mail renders the page. |
+| "Last release's opening sentence fits" | It described last release. Write 
the one this list supports. |
diff --git a/.claude/skills/creating-release-vote-mail/vote-mail-template.md 
b/.claude/skills/creating-release-vote-mail/vote-mail-template.md
new file mode 100644
index 000000000..f6a5b8439
--- /dev/null
+++ b/.claude/skills/creating-release-vote-mail/vote-mail-template.md
@@ -0,0 +1,139 @@
+# Release Vote Mail Template
+
+The canonical skeleton for the `[VOTE] Apache Struts X.Y.Z` mail that opens a 
release vote.
+Companion to [`SKILL.md`](SKILL.md), which covers *how* to fill the slots; 
this file covers
+*what the mail contains*.
+
+**This file is the source of truth.** Start every vote mail from the skeleton 
below.
+
+## Slots
+
+| Slot | What goes in it |
+|---|---|
+| `<X.Y.Z>` | The release being voted on, dotted — subject, opening sentence, 
Version Notes URL, dist path |
+| `<X_Y_Z>` | The same version underscored, for the `STRUTS_` git tag only |
+| `<SHAPE SENTENCE>` | See below — authored per release |
+| Page sections | Breaking changes, Deprecations, Rejected requests, and the 
issue-type sections, copied from the Version Notes page. Omit any the page 
omits. |
+
+## The shape sentence is authored per release
+
+The opener is two sentences. The first is fixed. The second describes the 
*shape* of the issue
+list — how the release is composed — and is written from the list in front of 
you.
+
+| The page has | Second sentence |
+|---|---|
+| No Breaking changes | `With this release the following issues were 
addressed:` |
+| Breaking changes | `This release contains <what>:` |
+
+**Do not reuse a previous release's wording.** 7.2.1's *"a few minor breaking 
changes plus some
+bug fixes. Also a lot of dependencies have been updated"* describes 7.2.1. 
Applied to 7.3.0 —
+seven breaking changes, one dependency bump — both halves are false.
+
+Name no individual ticket here. The list below is the detail.
+
+## Skeleton
+
+```
+Subject: [VOTE] Apache Struts <X.Y.Z>
+To:      [email protected]
+Bcc:     [email protected]
+
+The Apache Struts <X.Y.Z> test build is available. <SHAPE SENTENCE>
+
+Breaking changes
+
+- <page item, verbatim, ending [WW-XXXX].>
+
+Deprecations
+
+- <page item, verbatim, ending [WW-XXXX].>
+
+Rejected requests
+
+[WW-XXXX] - <page item, verbatim>
+
+Bug
+[WW-XXXX] - <summary>
+
+New Feature
+[WW-XXXX] - <summary>
+
+Improvement
+[WW-XXXX] - <summary>
+
+Task
+[WW-XXXX] - <summary>
+
+Dependency
+[WW-XXXX] - <summary>
+
+Release notes:
+* https://cwiki.apache.org/confluence/display/WW/Version+Notes+<X.Y.Z>
+
+Github release
+* https://github.com/apache/struts/releases/tag/STRUTS_<X_Y_Z>
+
+Distribution:
+* https://dist.apache.org/repos/dist/dev/struts/<X.Y.Z>/
+
+Maven 2 staging repository:
+* https://repository.apache.org/content/groups/staging/
+
+Once you have had a chance to review the test build, please respond
+with a vote on its quality:
+
+[ ] Leave at test build
+[ ] Alpha
+[ ] Beta
+[ ] General Availability (GA)
+
+Everyone who has tested the build is invited to vote. Votes by PMC
+members are considered binding. A vote passes if there are at least
+three binding +1s and more +1s than -1s.
+
+The vote will remain open for at least 72 hours, longer upon request.
+A vote can be amended at any time to upgrade or downgrade the quality
+of the release based on future experience. If an initial vote
+designates the build as "Beta", the release will be submitted for
+mirroring and announced to the user list. Once released as a public
+beta, subsequent quality votes on a build may be held on the user
+list.
+
+As always, the act of voting carries certain obligations. A binding
+vote not only states an opinion, but means that the voter is agreeing
+to help do the work.
+
+On behalf of the Apache Struts project
+Łukasz
+```
+
+Hard-wrap the body at 72 columns, continuation lines unindented, so the list 
stays legible in
+the ASF archives and in quoted replies.
+
+## Frozen text
+
+Everything from `Once you have had a chance to review the test build` to the 
sign-off is
+byte-frozen. It is the vote call itself: the options voters tick, the binding 
threshold, the
+72-hour minimum, and what a binding vote commits the voter to.
+
+Details that look like defects and are kept:
+
+| Detail | Why |
+|---|---|
+| `Github release` has no trailing colon | Every archived Struts vote mail 
reads this way |
+| All four checkboxes empty | The release manager's `+1 (binding)` is a 
separate reply |
+
+The staging URL is `content/groups/staging/`, matching the Version Notes page 
and the `[TEST]`
+mail. The group repo also resolves released transitive dependencies, which the 
bare staging
+repository does not.
+
+## Pre-draft checklist
+
+- [ ] All four links resolve; the GitHub release is still flagged pre-release
+- [ ] Ticket ids in the mail match the page's exactly, both directions
+- [ ] Boilerplate byte-identical to the frozen text above
+- [ ] `To: dev@` only; `user@` absent from every header; `Bcc: private@` 
present
+- [ ] Subject is exactly `[VOTE] Apache Struts X.Y.Z`
+- [ ] All four checkboxes empty
+- [ ] No severity, CVE, S2-XXX, bulletin link or reporter detail anywhere
+- [ ] Exactly one mail

Reply via email to