--- name: generate-changelog description: Generate a user-facing changelog entry for a new Android release from the unreleased changelog fragments, audited against git commits since the last shipped tag. Updates changelog.json and consumes the fragments. user_invocable: true --- # Generate Changelog Generate a changelog entry for a new release from the fragments in `android/changelog.d/`, maintained at commit time (rules in `.claude/rules/changelog.md`), audited against git commits since the last shipped tag. **This skill is invoked automatically by `/deploy-android`.** It can also be run standalone. ## Inputs You need two values — either passed as arguments or calculated the same way `/deploy-android` does: - **VERSION_NAME**: date-based version, `YYYY.MM.DD` (e.g., `2026.09.23`) - **TAG**: `vYYMMDDNN` (e.g., `v26092301`) ## Steps ### 1. Collect the unreleased changes ```bash git fetch origin main --quiet ``` Run `support/scripts/changelog-gather.sh`, which merges the fragments (one `-.json` per change, plus `SINCE`) into one `{since, features, improvements, fixes}` document. This is the **primary source**: whoever made each change recorded it when committing it. - **`since`** (from `android/changelog.d/SINCE`) — the last tag that actually shipped to users. This is the release baseline, **not** `git describe`: a tag can exist on a build that was withdrawn before users saw it, and `SINCE` is the only record of what really shipped. - **`features` / `improvements` / `fixes`** — the draft entry lines, each with the commits that contributed. The git log since `since` is an **audit** for anything missed, not the source. Run it with full commit messages and file stats so you can understand the actual changes rather than guessing from subject lines: ```bash git log ..origin/main --no-merges --stat -- "android/" ``` If the audit turns up a user-facing change that is missing from the fragment, add it to the entry, and tell the user the fragment discipline slipped so it can be corrected going forward. **Anti-pattern — do NOT skim with subject lines only.** `--oneline` strips the body, which is where user-facing impact actually lives. A `perf(playback)` commit might be a fail-fast change, not a speed-up; a `fix(app)` commit might be internal hardening for a bug no user ever saw. Read the body of every commit you're considering including or dismissing. If `SINCE` is empty (bootstrap before the first tagged release under this scheme), fall back to recent commits: ```bash git log origin/main --no-merges --stat -20 -- "android/" ``` If the fragment and the audit are both empty, tell the user and stop. #### Understanding ambiguous commits When a commit subject is unclear about the user-facing impact, read the actual diff: ```bash git show -- "android/" ``` Only do this for commits where the subject + body + file stats aren't enough to confidently describe the user impact. #### The before-state hard rule Any line implying a before-state ("no longer", "now", "instead of", "faster", "fixed") must be checked against the **source code at the last shipped tag**, never against commit messages or the previous changelog entry: ```bash git show : git diff ..HEAD -- ``` If the before-state never shipped — the feature was itself added since `since` and then polished — the line is not a fix: fold it into the feature's own description, or drop it. #### When in doubt, ask The user reviews each release. If you're unsure whether to include a particular item, ask before generating the JSON. A brief question is much cheaper than a wrong release note. #### A short changelog is better than a wrong one If after filtering you only have one user-facing change, write a one-bullet changelog. Don't pad with marginal items under "improvements". Single-fix releases are normal — say so plainly. ### 2. Generate the changelog entry Read the commits and write plain, friendly lines a user would understand — not developer jargon. Look at existing entries in `app/src/main/assets/changelog.json` for tone. **Style:** - Plain language, no em dashes, no filler editorialising ("for a cleaner look", "for a better experience") — state the change. - No audience labels ("Alpha testers:"). - One line per change, most exciting/impactful first within each category. - Omit empty categories from the entry (don't write `"features": []` if there are none). ### 3. Output format Match the existing schema in `android/app/src/main/assets/changelog.json`: ```json { "versionName": "2026.09.23", "releaseDate": "23/09/2026", "features": ["feature 1", "feature 2"], "fixes": ["fix 1"], "improvements": ["improvement 1"], "notes": [] } ``` - `versionName` is the date-based `YYYY.MM.DD` form (matches `TAG` without the `v` and with dots). - `releaseDate` is `DD/MM/YYYY`, matching the format of every existing entry. - `notes` is an optional freeform blurb array (existing entries use it for a short aside from the developer) — leave it as `[]` unless the user supplies one; don't invent a "voice" for them. ### 4. Update changelog.json and consume the fragments Read `android/app/src/main/assets/changelog.json` (a JSON array of release entries, newest first). - If an entry with the same `versionName` already exists, **replace** it. - Otherwise, **prepend** the new entry to the array. Write the updated file. Ensure valid JSON with consistent formatting (2-space indent). Then delete every consumed fragment and write the new baseline: ```bash git rm -q android/changelog.d/*.json echo "" > android/changelog.d/SINCE ``` **Commit them together** with the release: a changelog entry committed without its fragments removed makes the next release describe a delta against fragments that still claim the old baseline. ```bash git add -A android/app/src/main/assets/changelog.json android/changelog.d git commit -m "docs(app): update changelog for $VERSION_NAME" ``` This repo has no Play Store listing or release-notes automation (no fastlane) — the in-app changelog is the only user-facing release note. Nothing further to write.