Manual patch releases generation
ValidMind release notes guides
Locally generate customer-managed ValidMind (CMVM) hotfix or patch release notes (cmvm/YY.MM.NN where NN ≥ 02).
Prerequisites
Batch vs individual patch release generation
- Use batch when you need the full automation workflow (PRs, Shortcut, Slack).
- Use individual when you only need the generator to write
.qmdfiles on your local branch or worktree. - Scope — How many patch tag versions are generated during a run — comes from discovery for batch paths (
targets[]indiscovery.json) or from the single--versionyou pass for individual paths.
| Path | What it does | Scope | When to use | Section |
|---|---|---|---|---|
| Batch — generate | Discovery plus generate_patch_batch.py: drafts release notes, opens automation PRs (release-notes and docs preview), creates a Shortcut Story, sends Slack |
Every entry in targets[] — Refer to Discovery scope |
Net-new patch not yet on main; you want the full PR workflow locally |
Generate batch |
| Batch — regenerate | Same as batch generate with --refresh-in-place discovery and --preserve-human-edits |
Same as batch generate (discovered delta drives scope) | Open automation PR needs a refresh; human edits on branch/worktree must be kept | Regenerate batch |
| Individual — generate | generate_patch_release_notes.py only — writes .qmd with explicit --edit; no PRs or Shortcut Stories automatically created or Slack notifications sent |
Exactly one version from --version / --start-tag / --end-tag |
Single edge case patch version tag | Generate individual |
| Individual — regenerate | Same as individual generate with --preserve-human-edits on an existing file |
Same single version | Refresh one release patch tag in place without opening or updating automation PRs | Regenerate individual |
discover_undocumented_cmvm_patches.pybuildstargets[];generate_patch_batch.pyruns the full workflow once per target.- Per-target
start_tagandgap_start_tagare the incremental previous patch tag in the month (or the month’s.01for the first patch). Fetch, generate, and cross-check all use this window — same rules as Patch release automation.VM_RELEASE_DELTAis discovery-only and does not widen per-target fetch windows.
| Flag | Scope | When to use |
|---|---|---|
--tag cmvm/26.06.07 |
Ceiling/trigger tag — matches Patch release automation (GitHub workflow always passes --tag). Discovers undocumented patches in that month with NN ≤ 07. Usually one target when the triggered tag is the newest and earlier patches are on main; can return several if gaps exist below the ceiling. |
Normal release — net-new patch from a stable tag trigger. |
--month 26.06 |
Local/manual only — whole-month scope of all undocumented patches in 26.06. Not available through Patch release automation. |
Backfill — document every missing patch in a month when catching up locally. |
--refresh-in-place 26.06.06 |
Force a refresh target when an open automation PR exists and the file is not on main. |
Regenerate — refresh an open automation draft in place. |
Generating batch patches locally
Generate batch patch releases
1. Release delta discovery
Discover the scope of undocumented patch releases within the delta. See Discovery scope for --tag, --month, and other flag options.
# Local backfill alternative: --month YY.MM (no --tag); see Discovery scope above
poetry run python scripts/discover_undocumented_cmvm_patches.py \
--tag cmvm/26.06.06 --json | tee discovery.json # Example — replace with your stable patch tag
jq '.targets[] | {version, start_tag, end_tag}' discovery.json2. Generate patch batches
Run the patch batch generation script with initial LLM cleanup (with --edit):
poetry run python scripts/generate_patch_batch.py --discovery discovery.json --edit3. Human follow-up
Review the automatically generated draft release-notes and documentation PRs (resolve What needs special review?, such as Zendesk cross-check gaps), polish customer-facing prose, then merge PRs in sequence.
Regenerate batch patch releases
1. Release delta discovery
Rediscover delta scope when an open automation PR on automated/patch-{version} needs a fresh fetch/generate pass and you must keep existing human edits on your branch/worktree. See Discovery scope for --tag, --month, and --refresh-in-place; add --refresh-in-place when discovery would otherwise skip an in-flight version:
- If
releases/cmvm/{version}/release-notes.qmdis not onmainyet and an automation PR is already open onautomated/patch-{version}, normal discovery treats that version as in-flight and skips it — correct for net-new work, wrong when you want to update the existing draft. - Pass
--refresh-in-place {version}to force a refresh target (discovery emitsrefresh_in_place: trueinstead of skipping). - Batch generation checks out the existing
automated/patch-{version}branch, re-fetches Shortcut/Zendesk/GitHub data, regeneratesrelease-notes.qmd, commits, and pushes — updating the open PR, not opening a new one. It reuses the existing Shortcut story and PR bodies, and sets up the human-edit protection baseline.
# Local backfill alternative: --month YY.MM (no --tag); add --refresh-in-place {version} for in-flight targets — see Discovery scope above
poetry run python scripts/discover_undocumented_cmvm_patches.py \
--tag cmvm/26.06.06 --refresh-in-place 26.06.06 --json | tee discovery.json # Example — replace with your stable patch tag and version
jq '.targets[] | {version, start_tag, end_tag}' discovery.json2. Regenerate patch batches
- Run the patch batch generation script with LLM cleanup (with
--edit). - Pass
--preserve-human-editsso git-diff protection skips blocks you changed since the automation baseline commit, keeping your edits intact.
poetry run python scripts/generate_patch_batch.py \
--discovery discovery.json --edit --preserve-human-edits3. Human follow-up
Review the automatically refreshed draft release-notes and documentation PRs (resolve What needs special review?, such as Zendesk cross-check gaps), polish customer-facing prose, then merge PRs in sequence.
Generating individual patch releases locally
The individual path is draft generation only — it writes .qmd files on your current branch with no PR automation, no Shortcut Story created, and no Slack notifications, and no automated/patch-{version} branch is created for you.
Generate individual patch releases
1. Create branch or worktree
- Create a branch from up-to-date
mainbefore running the generator. - If you use
git worktree add, copy the repo-root.envfrom your main checkout into the worktree — worktrees do not share.envautomatically.
2. Release delta discovery
- Discovery resolves
version,start_tag, andend_tagfor one provided patch. generate_patch_release_notes.pydoes not readdiscovery.json— copy those values into your generation step, or skip discover if you already know the tag range.- Tag range: Set only one
--start-tagat a time (immediate previous patch tag in the month). To document multiple patches, use batch discovery with--month— do not widen--start-tagon individual runs.
poetry run python scripts/discover_undocumented_cmvm_patches.py \
--tag cmvm/26.06.06 --json | tee discovery.json # Example — replace with your stable patch tag
jq '.targets[] | {version, start_tag, end_tag}' discovery.json
# Copy jq output into step 3, for example:
# ONLY SET ONE TAG AT A TIME — widening --start-tag skips patches that batch discovery would document in separate per-target windows.
# To generate several patches at once, use the batch generation instructions instead.
# --start-tag sets the GitHub compare baseline for PR fetch, note generation, and Zendesk cross-check merge window.
# --start-tag sets discovery metadata; fetch/cross-check use --gap-start-tag (defaults to --start-tag).
# Optional --gap-start-tag when stale discovery.json or fetch baseline must differ from metadata start_tag.
# --start-tag is the immediate previous patch tag in the month (incremental).
# poetry run python scripts/generate_patch_release_notes.py \
# --version 26.06.06 \
# --start-tag cmvm/26.06.05 \ # compare baseline (PRs, notes, Zendesk)
# --end-tag cmvm/26.06.06 \ # target release tag being generated
# --edit3. Generate patch release notes
Run the individual patch release notes generator for the patch you want with initial LLM cleanup (with --edit):
# ONLY SET ONE TAG AT A TIME — widening --start-tag skips patches that batch discovery would document in separate per-target windows.
# To generate several patches at once, use the batch generation instructions instead.
# --start-tag sets the GitHub compare baseline for PR fetch, note generation, and Zendesk cross-check merge window.
# --start-tag sets discovery metadata; fetch/cross-check use --gap-start-tag (defaults to --start-tag).
# Optional --gap-start-tag when stale discovery.json or fetch baseline must differ from metadata start_tag.
# --start-tag is the immediate previous patch tag in the month (incremental).
poetry run python scripts/generate_patch_release_notes.py \
--version 26.06.06 \
--start-tag cmvm/26.06.05 \ # compare baseline (PRs, notes, Zendesk)
--end-tag cmvm/26.06.06 \ # target release tag being generated
--edit # Example — replace with your patch version and tag range4. Human follow-up
Review and edit releases/cmvm/{version}/release-notes.qmd on your branch/worktree (complete manual human tasks such as Zendesk cross-check gaps — refer to releases-data/{version}/cross_check_{version}.json (dots in {version} become underscores in the filename, e.g. 26.06.06 → cross_check_26_06_06.json) or Zendesk cross-check review, polish customer-facing prose, then open PRs manually if you are shipping through the normal workflow.
Regenerate individual patch releases
1. Switch to release branch or worktree
- Check out the branch or worktree that already holds the release notes file when you need a fresh fetch/generate pass and must keep existing human edits on your branch/worktree.
- Use your manual branch/worktree or an open automation branch — the individual path writes
.qmdfiles on your current branch, unlike batch runs that check outautomated/patch-{version}.
2. Release delta discovery
- Discovery resolves
version,start_tag, andend_tagfor one provided patch. generate_patch_release_notes.pydoes not readdiscovery.json— copy those values into your generation step, or skip discover if you already know the tag range.- Tag range: Set only one
--start-tagat a time (immediate previous patch tag in the month). To document multiple patches, use batch discovery with--month— do not widen--start-tagon individual runs.
poetry run python scripts/discover_undocumented_cmvm_patches.py \
--tag cmvm/26.06.06 --json | tee discovery.json # Example — replace with your stable patch tag
jq '.targets[] | {version, start_tag, end_tag}' discovery.json
# Copy jq output into step 3, for example:
# ONLY SET ONE TAG AT A TIME — widening --start-tag skips patches that batch discovery would document in separate per-target windows.
# To generate several patches at once, use the batch generation instructions instead.
# --start-tag sets the GitHub compare baseline for PR fetch, note generation, and Zendesk cross-check merge window.
# --start-tag sets discovery metadata; fetch/cross-check use --gap-start-tag (defaults to --start-tag).
# Optional --gap-start-tag when stale discovery.json or fetch baseline must differ from metadata start_tag.
# --start-tag is the immediate previous patch tag in the month (incremental).
# poetry run python scripts/generate_patch_release_notes.py \
# --version 26.06.06 \
# --start-tag cmvm/26.06.05 \ # compare baseline (PRs, notes, Zendesk)
# --end-tag cmvm/26.06.06 \ # target release tag being generated
# --editWhen an open automation PR exists and you need to refresh that version in place:
- If
releases/cmvm/{version}/release-notes.qmdis not onmainyet and an automation PR is already open onautomated/patch-{version}, normal discovery treats that version as in-flight and skips it — correct for net-new work, wrong when you want to update the existing draft. - Pass
--refresh-in-place {version}to force a refresh target (discovery emitsrefresh_in_place: trueinstead of skipping). - Individual regeneration writes
.qmdfiles on your current branch or worktree; you do not check outautomated/patch-{version}, but discover still needs the flag when that open PR exists.
poetry run python scripts/discover_undocumented_cmvm_patches.py \
--tag cmvm/26.06.06 --refresh-in-place 26.06.06 --json | tee discovery.json # Example — replace with your stable patch tag and version
jq '.targets[] | {version, start_tag, end_tag}' discovery.json3. Regenerate patch release notes
- Run the individual patch release notes generator with LLM cleanup (with
--edit). - If you skipped discover, pass explicit
--version,--start-tag, and--end-tag. - Pass
--preserve-human-editsso git-diff protection skips blocks you changed since the automation baseline commit, keeping your edits intact. - When refreshing on
automated/patch-{version}, also pass--refresh-in-placeso human-edit protection requires the automation baseline commit.
# ONLY SET ONE TAG AT A TIME — widening --start-tag skips patches that batch discovery would document in separate per-target windows.
# To generate several patches at once, use the batch generation instructions instead.
# --start-tag sets the GitHub compare baseline for PR fetch, note generation, and Zendesk cross-check merge window.
# --start-tag sets discovery metadata; fetch/cross-check use --gap-start-tag (defaults to --start-tag).
# Optional --gap-start-tag when stale discovery.json or fetch baseline must differ from metadata start_tag.
# --start-tag is the immediate previous patch tag in the month (incremental).
# Example — replace with your patch versions and tag ranges
poetry run python scripts/generate_patch_release_notes.py \
--version 26.06.06 \
--start-tag cmvm/26.06.05 \ # compare baseline (PRs, notes, Zendesk)
--end-tag cmvm/26.06.06 \ # target release tag being generated
--edit \
--preserve-human-edits
# Add --refresh-in-place when checked out on automated/patch-{version} (requires automation baseline commit)4. Human follow-up
Review the refreshed .qmd with human edits preserved (complete manual human tasks such as Zendesk cross-check gaps — refer to releases-data/{version}/cross_check_{version}.json (dots in {version} become underscores in the filename, e.g. 26.06.06 → cross_check_26_06_06.json) or Zendesk cross-check review, polish customer-facing prose, then open PRs manually if you are shipping through the normal workflow.