Release Blog Post Generator
Produce one combined blog post for an ai-platform-engineering release:
docs/releases/YYYY-MM-DD-release-X-Y-Z.md — release notes narrative
(highlights, what's new, bug fixes, breaking changes) followed by the full
upgrade guide (Helm values diff, step-by-step runbook, personal impact
analysis) as an embedded section.
The file is picked up by the Docusaurus releases blog plugin and published at
/blog/releases/release-X.Y.Z.
Execution context
Runs in two modes:
- Coding agent (Claude Code, Cursor) with shell access — run
gitandhelmcommands directly and write the file to disk. - Chat-only (CAIPE chat, Slack, web UI) — render output as a fenced markdown block the user can copy; note which commands to run manually.
Detect by whether a Bash/shell tool is available.
Step 1 — Gather inputs
Ask the user for:
| Input | Example | Required |
|---|---|---|
| To version | 0.4.9 |
Yes |
| From version | 0.4.8 |
Yes (defaults to previous tag) |
User's values.yaml |
paste or path | No — enables personal impact analysis |
| Environment | dev / preview / prod / vm |
No — enables env-specific notes |
If from/to are not provided, detect from the repo:
git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | head -2
Step 2 — Collect raw data
Run all of the following in parallel where possible.
2a — Git log between versions
git log <from>..<to> --oneline --no-merges
Fetch full PR bodies and authors for every merged PR in the range:
# for each PR number in the log, e.g. (#1324):
gh pr view <number> --json title,body,author,labels,files,mergedAt
List the human commit authors as a cross-check. Merge commits and co-authored work can otherwise hide a contributor:
git log <from>..<to> --format='%aN <%aE>%x09%s'
2b — Helm values diff
CHART=oci://ghcr.io/caipe-io/charts/ai-platform-engineering
helm show values "$CHART" --version <from> > /tmp/values-from.yaml
helm show values "$CHART" --version <to> > /tmp/values-to.yaml
diff -u /tmp/values-from.yaml /tmp/values-to.yaml
2c — Chart metadata
helm show chart "$CHART" --version <to>
2d — CHANGELOG.md
sed -n '/^## <to>/,/^## <from>/p' CHANGELOG.md
Step 3 — Classify changes
Classify git commits
| Type | Criteria |
|---|---|
| Feature | feat(*) commits |
| Fix | fix(*) commits |
| Security | commits touching Dockerfile, securityContext, PSS/PSA |
| Breaking | commit body contains "BREAKING CHANGE" or PR label breaking-change |
| Chore / Internal | chore, refactor, ci, test — omit from user-facing notes |
Classify Helm values diff lines
| Category | Criteria |
|---|---|
| Breaking | Key renamed, removed, or type changed |
| New required | New key with no default |
| New optional | New key with working default |
| Deprecated | Key still works but will be removed |
| Default changed | Same key, different default value |
Account for contributors
Before drafting, make a scratch checklist of every merged PR and human contributor in the release range. For each PR, record one of two outcomes:
- included in the release post, with its PR link and distinct user or operator impact;
- omitted because it is release automation, internal cleanup, tests, or another change with no user-visible or upgrade impact.
Do not publish the scratch checklist. Use it to catch missing work, then add every human
whose included work shipped in the release to the frontmatter authors list. Verify each
author key exists in docs/releases/authors.yml and keep bots out of the byline.
Related PRs can share a section, but do not compress separate outcomes so far that a meaningful contribution disappears.
Step 4 — Write docs/releases/YYYY-MM-DD-release-X-Y-Z.md
Use the date the tag was pushed (or today's date if cutting now).
---
slug: release-<to>
title: "Release <to>: <short subtitle capturing the biggest change>"
date: <YYYY-MM-DD>
authors: [<verified human contributor keys>]
tags: [release]
---
## Highlights
<1-3 sentence narrative of the most significant changes in plain English.
Focus on operator/user impact, not internal implementation details.>
<!-- truncate -->
## What's New
### <Feature area, when grouping helps>
- **<Title>** — <one-line description linking to PR #NNNN>
## Bug Fixes
- **<scope>**: <description> ([#NNNN](https://github.com/caipe-io/ai-platform-engineering/pull/NNNN))
## Security
<Any security-context, PSS, or CVE-related changes. Omit section if none.>
## Breaking Changes
<If none:>
No breaking changes. Drop-in upgrade from <from>.
## Known Issues
<If none:>
None known at this time.
---
## Upgrade Guide: <from> → <to>
### Overview
<One paragraph: overall theme, e.g. "Drop-in upgrade — no values.yaml edits required.">
### Helm Values Changes
<If no diff:>
No Helm values changes between <from> and <to>. Drop-in upgrade.
<If diff exists, use these subsections:>
#### Breaking Changes
**Affected key**: `global.foo.bar`
**Before (<from>)**:
```yaml
global:
foo:
bar: "old-value"
```
**After (<to>)**:
```yaml
global:
foo:
bar: "new-value"
```
**Action**: Update your `values.yaml`. If left unchanged, <consequence>.
#### New Optional Fields
| Env Var / Key | Default | Description |
| ----------------- | -------------- | ---------------------------- |
| `TOOL_CALL_LIMIT` | `0` (disabled) | Max tool invocations per run |
#### Deprecated / Removed Keys
| Key | Removed in | Replacement |
| -------------- | ---------- | --------------- |
| `tags.old-key` | <to> | `global.newKey` |
### Upgrade Runbook
#### 1. Update chart version
```bash
helm upgrade ai-platform-engineering \
oci://ghcr.io/caipe-io/charts/ai-platform-engineering \
--version <to> \
-f your-values.yaml
```
#### 2. Apply values.yaml changes
<Paste exact diffs for each breaking change.>
#### 3. Verify
```bash
kubectl get pods -n <namespace>
```
### Personal Impact Analysis
<If user provided values.yaml: cross-reference against breaking-changes list.>
<If not: prompt them to provide it for a personalised checklist.>
### Full Values Diff
<details>
<summary>Raw diff (<from> → <to>)</summary>
```diff
<paste diff output>
```
</details>
Step 5 — Write file to disk (coding agent)
mkdir -p docs/releases
cat > docs/releases/<YYYY-MM-DD>-release-<X-Y-Z>.md << 'EOF'
<generated content>
EOF
Step 6 — Snapshot and prune Docusaurus versions (coding agent)
After writing the blog post, snapshot the current docs/ tree as the new version
and prune old snapshots to stay within the retention policy.
Retention policy:
- Latest 5 releases from the current minor series (e.g.
0.4.7–0.4.11) - Highest release from each previous minor series (e.g.
0.3.11,0.2.x)
Run from repo root:
NEW_VERSION=<to> node docs/scripts/snapshot-and-prune-versions.js
This script:
- Runs
docusaurus docs:version <to>— snapshotsdocs/intoversioned_docs/version-<to>/ - Prunes
versioned_docs/,versioned_sidebars/, andversions.jsonto the retention policy - Updates
docs/versions-config.json— setslastVersion, marks<to>as(Latest), removes pruned entries
Commit all release artifacts together:
git add docs/releases/<YYYY-MM-DD>-release-<X-Y-Z>.md
git add docs/versioned_docs/ docs/versioned_sidebars/
git add docs/versions.json docs/versions-config.json
git commit -s -m "docs: release <to> — blog post, docs snapshot, version prune"
Guidelines
Voice and audience
- Write for end users, not operators or engineers. The release notes section (Highlights, What's New, Bug Fixes) is read by people deciding whether to upgrade and by users discovering new capabilities. The Upgrade Guide section is the only place technical operator details belong.
- Describe what users can do or what changed for them — not how it was implemented. "Team members can now connect their own Slack channels" is good. "Non-admin users can self-serve
integration_panel_modes" is not. - Describe bug fixes by the symptom users experienced, not the root cause. "Users were randomly logged out in multi-replica deployments" is good. "Per-pod in-memory token Map replaced with MongoDB-backed L1+L2 store" is not.
- No internal identifiers in release notes — no API route paths, env var names, function names, internal module names, Helm key paths, or code-level details. Those belong only in the Upgrade Guide if they are operator-actionable.
- Keep feature bullets short and outcome-focused. One sentence: what you can do now that you couldn't before, or what problem went away.
- Treat corrected install and upgrade documentation as user-facing. Include it when the old instructions would lead users to the wrong registry, values, credentials, or deployment result.
Natural writing
- Write the smallest accurate release post. One meaningful change is enough; do not add weak bullets or extra sections to make the post look fuller.
- Open with what changed and who it affects. Skip throat-clearing, broad claims about the industry, and a closing paragraph that repeats the highlights.
- Use direct sentences with a clear actor. Prefer "The chart keeps custom resources stable across upgrades" over "Improved stability is provided for custom resources."
- Prefer concrete verbs and plain connecting words. Avoid filler such as "leverage," "robust," "seamless," "holistic," "comprehensive," and "transformative" when a specific description is available.
- Do not force groups of three, symmetrical paragraphs, a heading for every paragraph, or a redundant key-takeaways section.
- Read the draft once as if explaining the release to a teammate. Remove any sentence that does not change an upgrade decision or explain a user-visible result.
Structure and formatting
<!-- truncate -->goes immediately after the Highlights section so the blog list shows just the intro paragraph- Show concrete before/after YAML for every breaking change in the Upgrade Guide — never prose-only
- For breaking changes, state the consequence of not updating
- Keep the upgrade runbook linear — steps should be self-contained
- If
helm show valuesis unavailable, ask the user to paste output — never guess - Highlight environment-specific notes (VM kind clusters use
standardstorageClass; EKS usesgp2/gp3) - For
ExternalSecretadditions, list the exact Vault path and key the chart now expects - Omit
chore,ci,test, andrefactorcommits from user-facing notes - If there are zero helm value changes, say so explicitly and reassure it is a drop-in upgrade
- Do NOT create separate migration guide files — the upgrade guide lives inside the release post