Skip to main content

Manual patch releases generation

ValidMind release notes guides

Content Architecture

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 .qmd files on your local branch or worktree.
  • Scope — How many patch tag versions are generated during a run — comes from discovery for batch paths (targets[] in discovery.json) or from the single --version you 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.py builds targets[]; generate_patch_batch.py runs the full workflow once per target.
  • Per-target start_tag and gap_start_tag are the incremental previous patch tag in the month (or the month’s .01 for the first patch). Fetch, generate, and cross-check all use this window — same rules as Patch release automation. VM_RELEASE_DELTA is 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.json

2. 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 --edit

3. 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.qmd is not on main yet and an automation PR is already open on automated/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 emits refresh_in_place: true instead of skipping).
  • Batch generation checks out the existing automated/patch-{version} branch, re-fetches Shortcut/Zendesk/GitHub data, regenerates release-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.json

2. Regenerate patch batches

  • Run the patch batch generation script with LLM cleanup (with --edit).
  • Pass --preserve-human-edits so 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-edits

3. 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 main before running the generator.
  • If you use git worktree add, copy the repo-root .env from your main checkout into the worktree — worktrees do not share .env automatically.

2. Release delta discovery

  • Discovery resolves version, start_tag, and end_tag for one provided patch.
  • generate_patch_release_notes.py does not read discovery.json — copy those values into your generation step, or skip discover if you already know the tag range.
  • Tag range: Set only one --start-tag at a time (immediate previous patch tag in the month). To document multiple patches, use batch discovery with --month — do not widen --start-tag on 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
#   --edit

3. 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 range

4. 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 .qmd files on your current branch, unlike batch runs that check out automated/patch-{version}.

2. Release delta discovery

  • Discovery resolves version, start_tag, and end_tag for one provided patch.
  • generate_patch_release_notes.py does not read discovery.json — copy those values into your generation step, or skip discover if you already know the tag range.
  • Tag range: Set only one --start-tag at a time (immediate previous patch tag in the month). To document multiple patches, use batch discovery with --month — do not widen --start-tag on 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
#   --edit

When an open automation PR exists and you need to refresh that version in place:

  • If releases/cmvm/{version}/release-notes.qmd is not on main yet and an automation PR is already open on automated/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 emits refresh_in_place: true instead of skipping).
  • Individual regeneration writes .qmd files on your current branch or worktree; you do not check out automated/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.json

3. 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-edits so 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-place so 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.