Monthly release automation
ValidMind release notes guides
Automated monthly (full, feature) release notes (YY.MM, stable tag cmvm/YY.MM.01).
When are monthly release drafts automatically generated?
| Scenario | Trigger | installation RELEASE_AUTOMATION repo variable |
Notes |
|---|---|---|---|
| A (tag push) | When validmind/installation receives a new stable cmvm/YY.MM.01 tag |
vars.RELEASE_AUTOMATION == 'true' |
A notifier on installation dispatches monthly-release-notes to the release-notes repo |
B (workflow_dispatch) — Fallback |
A human manually runs monthly_release_notes.yml with a tag |
Ungated | Example: gh workflow run monthly_release_notes.yml --repo validmind/release-notes -f tag=cmvm/26.06.01 |
- Monthly release automation is triggered by stable
cmvm/YY.MM.01tags only — release candidate-rc#tags and patch tags (NN ≥ 02) are rejected by discovery; patch tags dispatch Patch release automation instead. - Discovery skips a monthly release when
releases/cmvm/{version}/release-notes.qmdis already onvalidmind/release-notesmain, or when an open automation PR (release-notes draft and/or documentation preview) is already in flight for that version. - Re-dispatching the same stable tag (for example, running the workflow again) while a draft PR on
automated/monthly-{version}is still open—and the file is not yet onmain— setsrefresh_in_placeand regenerates on that branch instead of skipping or opening a new PR.
What happens when monthly drafts are automatically generated?
1. Discovery
discover_undocumented_cmvm_monthlies.pybuildstargets[];generate_monthly_batch.pyruns the full workflow once per target (each monthly release tag identified in the delta).- Per-target
start_tagandgap_start_tagare the previous monthly stable tag (cmvm/YY.MM.01), not the last patch in the prior month. Fetch, generate, and Zendesk cross-check all use this window — each monthly target documents PRs from the prior monthly stable through the current monthly stable (e.g.cmvm/26.06.01 → cmvm/26.07.01, notcmvm/26.06.04 → cmvm/26.07.01). Refer to the Zendesk cross-check review guide. VM_RELEASE_DELTAis discovery-only — used as a fallback when discover cannot resolve a previous monthly stable tag (first monthly at the automation floor). It does not widen fetch or generate windows across batch targets.- Stale
discovery.json: targets created before the dedupe fix may have a widestart_tagwithoutgap_start_tag. Batch warns and usesgap_start_tagwhen present; re-dispatch openautomated/*branches with fresh discover output after merging dedupe fixes — do not reuse pre-fixdiscovery.json. - Shortcut release-label preflight — before building targets, discover validates the
Release {YY.MM}label in Shortcut. Mode is controlled byvalidate_release_label:
| Mode | Behavior |
|---|---|
nonempty (default) |
Label must exist in the workspace and have at least one tagged epic or story |
exists |
Label must exist in the workspace (no content check) |
none |
Skip preflight entirely |
- Manual
workflow_dispatch: the Actions form exposes Shortcut preflight as a dropdown (none/exists/nonempty; defaultnonempty). - Tag-push
repository_dispatch: notifiers do not pass this input; the workflow hardcodesnonemptyfor discover regardless of the dropdown default.
2. Automation branch checked out, release notes drafted, and pushed
Before generating, generate_monthly_batch.py runs _ensure_branch (ensure_batch_branch in batch_git.py) so the working tree is on automated/monthly-{version} (creating or checking out from base when needed). The draft lands on that automation branch — not on main.
generate_monthly_batch.pyrunsgenerate_monthly_release_notes.pywith no--editby default (skip_edit: truein the workflow — structural/raw output only). LLM polish belongs at author-stagematerialize_monthly_highlights.py --edit/--edit-highlightsin Monthly highlights.Written on
release-notesbranch (automated/monthly-{version}) —releases/cmvm/{version}/release-notes.qmdplus_pr-{n}.qmdandpr-{n}.qmdfor all in-scope PRs, expanded CMVM shell, andfile-manifest.json. No curated*-highlights.qmdfiles automatically generated in the initial draft — human authors run the Monthly highlights process.validmind-libraryis not auto-generated here — use Library release automation; authors may manually embed library highlight partials in the CMVM shell when relevant (see monthly PR template).Committed and pushed to
validmind/release-notesonautomated/monthly-{version}. No branch is created invalidmind/documentationat this step.
3. Shortcut Story created or reused
Batch automation resolves a Shortcut Story using one of three paths:
| Path | When | How the story is resolved |
|---|---|---|
| create | First run for a version (no automation branch yet, or new branch from base) | Title search (Edit {version} release notes) → reuse existing story if found, otherwise POST a new chore story |
| refresh | Discover sets refresh_in_place: true (same tag re-dispatch, release-notes.qmd not on main, open release-notes PR on automated/monthly-{version}) |
Parse the Shortcut blockquote from the open release-notes PR body (no new POST) |
| orphan | Remote automation branch exists but there is no open PR (partial/failed prior run) | Same as create — title search then POST or reuse; batch then opens the release-notes PR |
release-notes and documentation PRs link to the Shortcut Story via the Story blockquote in the PR template. On refresh, if the blockquote is missing or malformed, batch falls back to the create path (title search) instead of failing.
| Field | Value |
|---|---|
| Story type | chore |
| Title | Edit {version} release notes |
| State | In progress |
| Epic | Automated Release Notes |
| Group | Docs |
| Estimate | 2 |
| Iteration | Current iteration when started |
| Technical Area | Product Documentation |
4. Release-notes PR opened
Draft PR on validmind/release-notes from automated/monthly-{version}. Follows the template: monthly_pr_body.md.
On refresh (refresh_in_place), an open PR already exists — batch updates the branch in place and reuses the existing PR (no new gh pr create). On orphan resume, batch opens the release-notes PR after Shortcut story resolution.
5. Documentation preview deployed
The Documentation preview check on the release-notes PR dispatches the reusable preview workflow on validmind/documentation. It renders the exact release-notes PR commit and deploys it to pr_previews/release-notes/pr-{pr_number}. Every new commit redeploys the same preview URL; no documentation branch or PR is created.
6. Slack notification sent
- Posts success or failure to
#release-notes. Follows templates:monthly-success.md,monthly-failure.md. - Slack messages use mrkdwn (not GitHub Markdown): line 1 is fully bold (
*…*), links use<url|label>, to-do items use•bullets, and failure error text is shown in a monospace code block so subprocess output cannot break link formatting. generate_monthly_batch.pycallsrender_slack_batch_notify.pyper target on success or failure when run locally. In GitHub Actions, batch scripts skip in-process Slack; workflow steps post frombatch_results.json(--batch-successeson success/partial,--batch-failures/--batch-crashedon failure). Handled per-target failures do not trigger duplicate Slack messages.- When Initial draft
--editis opted in viaworkflow_dispatchand soft-fails, the Slack message includes an edit-skipped line.
| Scenario | Channel | When | Example message |
|---|---|---|---|
| Success | #release-notes |
Target completes in generate_monthly_batch.py |
Monthly release notes draft ready — June 2026 Triggered by cmvm/26.06.01 <shortcut URL|sc-12345> | <PR URL|release-notes #42> | <docs PR URL|documentation #99> Tag window: cmvm/25.05.01 → cmvm/26.06.01 | Cross-check gaps: 1 | Bugs: 2 | Manifest pending: 12 … To-do: • Complete What needs special review? on release-notes PR — including generating the Monthly highlights; • Confirm docs preview CI green |
| Success (refresh) | #release-notes |
Same tag re-dispatch while an automation PR is open (refresh_in_place) |
Same as success; title includes (refresh): Monthly release notes draft ready — June 2026 (refresh) |
| Success (edit skipped) | #release-notes |
Initial draft --edit soft-fails when opted in (edit_skipped: true) |
Same as success, with an extra line after the tag-range summary: ⚠️ LLM –edit skipped — review draft wording manually. |
| Failure (target step) | #release-notes |
BatchStepError or unhandled exception in process_target |
⚠️ Monthly release notes automation FAILED — June 2026 Triggered by cmvm/26.06.01 Error (step: release_notes_pr):<br>{subprocess error text}<br>Workflow run: <run URL|View workflow run> Branches: • <release-notes tree URL|automated/monthly-26.06> — pushed • documentation preview: none created (failed at step: release_notes_pr)Artifacts: • Shortcut story: created — <shortcut URL|sc-12345> • Release-notes PR: not created • Documentation PR: not created … To-do: • Inspect logs; • Delete orphan branches; • Re-run workflow_dispatch |
| Failure (discovery) | #release-notes |
discover step fails in monthly_release_notes.yml |
⚠️ Monthly release notes automation FAILED — June 2026 Error (step: discover):<br>Discovery failed<br>Branches: none created (failed at step: discover)Artifacts: • Shortcut story: not created • Release-notes PR: not created • Documentation PR: not created |
| Failure (batch crashed) | #release-notes |
Batch step fails before batch_results.json is written (true crash only) |
Error (step: generate):<br>Batch crashed before writing batch_results.json<br>— one failure message per target in discovery.json |
What does a human have to do after the monthly release automation runs?
1. Zendesk cross-check
Monthly draft generation runs an initial Zendesk cross-check automatically — generate_monthly_release_notes.py fetches Shortcut zendesk_pr_mapping and GitHub tag-delta, evaluates PR merge windows against CMVM tag timestamps, and injects qualifying customer-linked fixes into the draft.
- Cross-check gaps flagged by automation under What needs special review? on the
release-notesautomation PR; gap count also appears in the#release-notesSlack success message (Cross-check gaps: N). - Confirm every qualifying fix is in
releases/cmvm/{version}/release-notes.qmdwith Zendesk ticket HTML comments; resolve each gap row; spot-check 1–2 tickets in Zendesk for wording and monthly scope.
2. Monthly feature highlights
- Prepare release memory to make theme naming, bullet grouping, and PR matching faster and more accurate.
- Generate feature highlights automation PR branch: provide & generate themes → match PRs to themes → orphan (unhighlighted PRs) disposition → materialize highlights (
extract_release_themes.py→match_theme_bullets_to_prs.py→report_orphan_prs.py→materialize_monthly_highlights.py). - Materialize highlights with LLM polish — Initial monthly draft does not run
--editand provides raw prose only, so LLM-cleanup happens here when we’ve pared down files enough not to waste tokens. After themes, match, and orphan disposition are committed, runmaterialize_monthly_highlights.pywith--edit(highlight partial prose in_pr-*-highlights.qmd) and/or--edit-highlights(wrapper title, subtitle, and.hiddendescription).
3. Editing & proofreading
a. LLM polish non-highlight partials
Initial draft generation skips --edit to save on tokens for PRs we intend to hide or reorganize into highlights, and materialize --edit / --edit-highlights edit highlight artifacts only:
- This means that “orphan” (not highlighted, support/bug, or hidden) are not edited by the LLM.
- After orphan review is completed and assignments are committed (preferable before materialize, when promoted/hidden PRs are already decided): Run
edit_non_highlight_partials.pywith--require-orphan-review --editto edit all other relevant orphan files. - This edit will skip theme-linked partials (
release-themes.json/theme-pr-matches.jsonprs[]), hidden wrappers, and*-highlights.qmdpaths.
b. Human editing & review
- Proofread customer-facing wording in all files that will be visible in the CMVM or public releases portals — refer to the Editing generated release notes guide.
- Confirm that
releases/validmind-platform-releases.qmdhas been updated with the rightsubtitle: "Latest:{version}"anddatefrom your release tag
5. Archive release memory
- Initial monthly release draft automation seeded retrieval artifacts and a draft brief to accelerate theme work, and then your interactive human-lead editing finalized customer-facing prose.
- Archiving now persists those investigation conclusions, final theme narratives, PR functional deltas, and any human corrections into the release memory store so future monthly passes do not re-derive the same context from tag-delta alone.
- Run on archival when you are satisfied with your release notes and ready to mark the associated PRs ready-for-review.
- Invoke the
release-memory-archivistskill for{version}, then validate and regenerate the memory site:
poetry run python scripts/validate_release_memory.py
poetry run python scripts/generate_release_memory_site.py --release {version} --render- Commit and push all
memory/changes on this branch:
git add memory/
git commit -m "Archive release memory for {version}"
git push6. Merge release notes
After the Documentation preview check passes, merge release-notes > main. There is no paired documentation PR to clean up or merge. The merge automatically rebuilds the documentation staging site from the updated release-notes source.
7. Publish documentation site
After Publish release notes to documentation staging passes, push documentation to production: staging > prod.
Do monthly drafts automatically get regenerated?
| Scenario | What happens | Notes |
|---|---|---|
| Stable refresh | Re-dispatch with the same tag while an open automation PR exists (and release-notes.qmd is not on main) sets refresh_in_place on the discover target; batch regenerates automation-owned paths and preserves human edits automatically. Never regenerates themes via batch — protects release-themes.json, materialized highlights, and hidden wrappers. |
Same installation tag; workflow re-dispatched — not a new tag. |
| New tag | A new monthly stable tag opens a new automation PR. |
Appendix
| Script | Role |
|---|---|
discover_undocumented_cmvm_monthlies.py |
Find undocumented monthly targets |
generate_monthly_release_notes.py |
Initial automated drafts |
generate_monthly_batch.py |
Batch orchestration (PR, Shortcut, preview) |
extract_release_themes.py |
Extract themes during interactive highlight curation |
match_theme_bullets_to_prs.py |
Match PR themes to bullets f during interactive highlight curation |
report_orphan_prs.py |
Review orphan PRs during interactive highlight curation |
materialize_monthly_highlights.py |
Materialize curated highlights |
validate_release_memory.py |
Validate memory store before merge |
generate_release_memory_site.py |
Regenerate memory site pages |
| Workflow | Trigger |
|---|---|
monthly_release_notes.yml |
repository_dispatch / workflow_dispatch |
documentation_preview.yml |
Commits to release-notes PRs that change releases/** |
installation notifier |
Tag push on validmind/installation when RELEASE_AUTOMATION=true (monthly stable tags cmvm/YY.MM.01, no -rc) |
release_memory_ingest.yml |
Optional single-PR memory ingest |
| Skill | When |
|---|---|
generate-release-notes |
Use in the final human proofread only, NOT to generate release notes as that is now taken care of by the automations/updated scripts — apply the Editing generated release notes process content rules |
nosy-neighbour |
Optional pre-theme investigation |
release-memory-archivist |
Post-edit archive — persist final narratives to memory layer before merge |
generate-release-notifications |
Post-merge customer emails |
| Item | Notes |
|---|---|
| PR body template | monthly_pr_body.md |
| Discovery floor | VM_RELEASE_DELTA — discovery-only; does not widen per-target fetch windows |
| Highlight depth | Monthly feature highlights — not duplicated here |