.agents/skills/version-release/references/release-notes-style.md
Use this guide for GitHub Release notes — the body of a release PR that becomes the GitHub Release after merge. Do not use it for docs/changelog/*.mdx website pages (load ../../docs-changelog/SKILL.md instead).
This release-note style is:
Collect these inputs first:
<prev_tag>...<current_tag>)If metrics cannot be reliably computed, omit unknown numbers instead of guessing.
Hallucinated PR numbers and wrong "Since v..." bases are the #1 failure mode of this skill. Every number and every
(#XXXX)must come fromgit, never from memory or inference.
mainDo not eyeball the tag list or pick the "last weekly" PR. Compute it:
git fetch origin main canary --tags
PREV_TAG=$(git describe --tags --abbrev=0 origin/main --match 'v*.*.*' --exclude '*-canary*' --exclude '*-nightly*')
echo "$PREV_TAG"
Sanity check that the tag is reachable from the release branch:
git merge-base --is-ancestor "$PREV_TAG" origin/release/weekly-{YYYYMMDD} && echo OK
If the check fails, stop and ask the user — the release branch is based on the wrong source.
Why not "the last weekly release PR"? Hotfixes (
v2.1.54,v2.1.55, …) merge directly into main between weeklies. They get back-merged viasync-main-to-canary, so the latest semver tag on main is the correct previous release for both weekly and minor flows. Picking the previous weekly's tag will silently undercount and put a stale version in "Since v…".
Compute the canonical set:
git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" \
--pretty=format:'%s' --no-merges \
| grep -oE '\(#[0-9]+\)$' \
| sort -u > /tmp/release_prs.txt
Hard rules:
(#XXXX) you write in the body must appear in /tmp/release_prs.txt. No exceptions.rtk), bypass it. With rtk use rtk proxy git log …. Verify with wc -l /tmp/release_prs.txt — the count must match git log $PREV_TAG..HEAD --no-merges --pretty=format:'%h' | wc -l minus the few commits without a PR ref. A mismatch of >5% means subjects are being silently truncated.PR_COUNT=$(wc -l < /tmp/release_prs.txt | tr -d ' ')
COMMIT_COUNT=$(git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" --no-merges --pretty=format:'%h' | wc -l | tr -d ' ')
CONTRIBUTOR_COUNT=$(git log "$PREV_TAG..origin/release/weekly-{YYYYMMDD}" --no-merges --pretty=format:'%an' \
| sort -u \
| grep -viE '^(lobehubbot|LobeHub Bot|renovate\[bot\])$' \
| wc -l | tr -d ' ')
If a number cannot be confidently derived, omit it — never guess.
Git %an is the commit author display name, not the GitHub handle. For each author you mention, confirm the handle:
gh pr view "$PR_NUMBER" --repo lobehub/lobe-chat --json author --jq '.author.login'
Use the result for @handle. Then classify each author per the LobeHub team roster below; community first, team after.
Before gh pr create / gh pr edit --body-file, diff body PR refs against the canonical set:
grep -oE '#[0-9]+' release_body.md | sort -u > /tmp/body_prs.txt
sed 's/[()]//g' /tmp/release_prs.txt > /tmp/release_prs_clean.txt
echo "=== In body but NOT in actual range (must be EMPTY) ==="
comm -23 /tmp/body_prs.txt /tmp/release_prs_clean.txt
Empty diff = OK. Any output = the body cites a PR that wasn't merged in this range. Stop and fix before publishing.
Also verify the metrics line in the body matches the computed values (PR_COUNT, CONTRIBUTOR_COUNT) and that **Full Changelog** uses $PREV_TAG, not some older tag.
Follow this section order for Minor and Weekly releases unless the user asks otherwise. For Hotfix and DB Migration, see § Variants for Shorter Releases below — the canonical structure does not apply.
# 🚀 LobeHub Release (<YYYYMMDD>)Release DateSince <Previous Version> metrics## ✨ Highlights (6-12 bullets for major releases; 3-8 for weekly)### subsections:
## 🏗️ Core Agent & Architecture (or equivalent product core)## 📱 Platforms / Integrations## 🖥️ CLI & User Experience## 🔧 Tooling## 🔒 Security & Reliability## 📚 Documentation (optional if meaningful)## 👥 Contributors**Full Changelog**: <prev>...<current>Use --- separators between major blocks for long releases.
The Canonical Structure above is for long-form (Minor / Weekly). Two short-form variants override it.
A hotfix targets one regression and ships fast. The body is short and operator-focused — no Highlights, no domain blocks, no Contributors line.
Required sections, in order:
# 🚀 LobeHub Release (<YYYYMMDD>)**Hotfix Scope:** — one line summarizing the regression scope (e.g. Agent topic-switching regression — stale chat state on agent change). Replaces the long-form Release Date / Since vX.Y.Z metrics.## 🐛 What's Fixed — 1-3 bullets, each **<symptom>** — <fix in one sentence>. (#PR). No root-cause prose; that lives in the commit message.## ⚙️ Upgrade — short notes for self-hosted (pull image / restart, schema or env changes) and cloud (usually "applied automatically").## 👥 Owner — single @handle for the PR author, resolved via gh pr view "$PR" --json author --jq '.author.login'. Never hardcoded.Hard rules specific to hotfix:
Since vX.Y.Z doesn't apply; the body cites the single PR (or 1-3 PRs) directly.changelog-example/hotfix.md for the canonical template.Database schema changes that need to be released independently. Operator impact is the headline.
Required sections, in order:
# 🚀 LobeHub Release (<YYYYMMDD>) + scope line## 👥 Owner — single PR author, resolved via gh pr viewSee changelog-example/db-migration.md for the canonical template.
(#1234) when IDs are available.**Fast Mode (/fast)** — Priority routing for OpenAI and Anthropic, reducing latency on supported models. (#6875, #6960)Highlights usually 8-12 bullets.Highlights usually 4-8 bullets.Migration overview, operator impact, and rollback/backup note.Render contributors as a single flat list (no separate "Community" / "Core Team" subsections). Order: community contributors first, team members after. Within each group, sort by PR count desc. Bots (@lobehubbot, renovate[bot]) go on a separate "maintenance" line.
LobeHub team roster — anyone in this list is a team member; anyone not in this list is a community contributor:
Resolving handles — git author names (e.g.
YuTengjing) are not always the GitHub handle. Verify viagh pr view "$PR" --json authororgh api search/users -f q='<email>'before listing.
If a new contributor appears who is not on this list, treat them as community by default and ask the user whether to add them to the roster.
# 🚀 LobeHub Release (<YYYYMMDD>)
**Release Date:** <Month DD, YYYY>
**Since <Previous Version>:** <N merged PRs> · <N resolved issues> · <N contributors>
> <One release thesis sentence: what this release unlocks in practice.>
---
## ✨ Highlights
- **<Capability A>** — <What changed and why it matters>. (#1234)
- **<Capability B>** — <What changed and why it matters>. (#2345)
- **<Capability C>** — <What changed and why it matters>. (#3456)
---
## 🏗️ Core Product & Architecture
### <Subdomain>
- <Concrete change + impact>. (#...)
- <Concrete change + impact>. (#...)
---
## 📱 Platforms / Integrations
- <Platform update + impact>. (#...)
- <Compatibility/reliability fix + impact>. (#...)
---
## 🖥️ CLI & User Experience
- <User-facing workflow improvement>. (#...)
- <Quality-of-life fix>. (#...)
---
## 🔧 Tooling
- <Tool/runtime improvement>. (#...)
---
## 🔒 Security & Reliability
- **Security:** <hardening or vulnerability fix>. (#...)
- **Reliability:** <stability/performance behavior improvement>. (#...)
---
## 👥 Contributors
Huge thanks to **<N contributors>** who shipped **<N merged PRs>** this cycle.
@<community-handle> · @<community-handle> · @<team-handle> · @<team-handle>
Plus @lobehubbot and renovate[bot] for maintenance.
---
**Full Changelog**: <previous_tag>...<current_tag>
PREV_TAG is git describe --tags --abbrev=0 origin/main (latest semver), not the last weekly's tag(#XXXX) in the body appears in /tmp/release_prs.txt (verified via comm -23)Since v… line uses $PREV_TAG; PR / contributor counts match wc -l on the computed sets**Full Changelog** uses $PREV_TAG...release/weekly-<YYYYMMDD> (or …v{x.y.z} for minor)gh pr view --json author, not assumed from %anHighlights plus domain-grouped sections**Hotfix Scope:** line replaces metrics line## 🐛 What's Fixed has 1-3 bullets, each **<symptom>** — <fix>. (#PR) with PR ref verified to exist and be merged## ⚙️ Upgrade notes self-hosted action and cloud auto-apply## 👥 Owner is a single @handle resolved via gh pr view "$PR" --json author