brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
app-store-changelogWrites the App Store "What's New in This Version" text for an app update from the repository's commit history: finds the range since the last shipped version, separates changes users can notice from internal work, drafts plain-text notes within Apple's 4,000-character limit and review guidelines, and returns a trace from every sentence back to its commits. …
Independent GitHub snapshot · Automated classification · Original documentation governs
This skill turns the commits between two releases into the text shown under "What's New" on an app's product page and in the Updates list. It delivers one plain-text note per locale with its character count, a table tracing each sentence to commits, and the list of changes left out with the reason.
Facts from Apple's documentation that shape the work:
Look in the repository before asking:
fastlane/metadata/*/release_notes.txt shows the locales in use and the voice and length of earlier notes.CHANGELOG.md, release tags, and the marketing version (MARKETING_VERSION in the Xcode project or an xcconfig file).Then confirm with the user: the commit being shipped; the last version that actually reached the store (a tag may belong to a build that was rejected or only went to TestFlight); the locales needed; any feature that is merged but switched off.
git tag --sort=-creatordate | head -5 # recent tags, newest first
git describe --tags --abbrev=0 # nearest tag reachable from HEAD
git describe --tags --abbrev=0 HEAD^ # previous tag when HEAD itself is already tagged
Without tags, find the commits that changed the version number and use the previous one as the start:
git log --date=short --pretty='format:%h %ad %s' -G'MARKETING_VERSION' -- '*.pbxproj' '*.xcconfig'
Prefer a commit or tag over a date: --since=2026-09-01 counts from the current time of day on that date and silently drops commits made earlier that morning.
Save this as release-range.sh outside the repository (or paste the commands one by one) and run it from the repository root. It only reads.
#!/usr/bin/env bash
# Usage: release-range.sh [FROM_REF] [TO_REF]
set -euo pipefail
to="${2:-HEAD}"
from="${1:-}"
if [ -z "$from" ]; then
from="$(git describe --tags --abbrev=0 "$to" 2>/dev/null || true)"
# the release commit may already carry the new tag: step back one tag
if [ -n "$from" ] && [ "$(git rev-parse "$from^{commit}")" = "$(git rev-parse "$to^{commit}")" ]; then
from="$(git describe --tags --abbrev=0 "$to^" 2>/dev/null || true)"
fi
fi
range="${from:+$from..}$to"
echo "## Range: $range ($(git rev-list --count --no-merges "$range") commits, no merges)"
echo; echo "## Commits (oldest first)"
git log --reverse --no-merges --date=short --pretty='format:%h %ad %s' "$range"
echo; echo; echo "## Reverts in range"
git log --no-merges --grep='^Revert' --pretty='format:%h %s' "$range"
echo; echo; echo "## Files changed per area"
if [ -n "$from" ]; then
git diff --name-only "$from" "$to" | awk -F/ '{print (NF>1 ? $1"/"$2 : $1)}' | sort | uniq -c | sort -rn
echo; echo "## User-facing strings touched"
git diff --stat "$from" "$to" -- '*.xcstrings' '*.strings' '*.stringsdict' '*/strings.xml' | cat
fi
For any commit whose subject does not explain itself, read it: git show --stat --format='%h %s%n%b' 21b6016, then the diff of the files that matter. In repositories that merge pull requests, git log --merges --first-parent --pretty='format:%h %s: %b' v2.3.0..HEAD lists them; GitHub's default merge commit keeps the pull-request title in the body, which is why %b is there.
| Evidence | Treatment |
|---|---|
| Feature, fix or speed-up in screens, widgets, notifications, sync | Candidate for the notes |
| CI, tests, refactors, build settings, tooling | Leave out |
| Dependency update | Leave out, unless it fixes something users saw |
| Commit and its revert both inside the range | Leave out both |
| Code behind a flag that ships switched off | Leave out; describing a feature the build does not show is misleading (2.3.1) |
| Fix for a bug introduced inside the same range | Leave out; users never saw the bug |
| Vague subject ("wip", "fix crash", "update") | Read the diff; ask the user if it is still unclear |
| Changed strings files | Use them to learn what the interface calls the feature |
Check flags with git grep -n 'exportEnabled' v2.4.0 -- '*.swift' (flag name and ref from the project). A flag controlled from a server cannot be read from the repository: ask.
for f in fastlane/metadata/*/release_notes.txt; do
printf '%s %s chars\n' "$f" "$(python3 -c 'import sys; print(len(open(sys.argv[1], encoding="utf-8").read().strip()))' "$f")"
done
grep -n -i -E 'android|google play|\$[0-9]|€ ?[0-9]|coming soon' fastlane/metadata/*/release_notes.txt || echo "no flagged terms"
Then confirm that every sentence maps to at least one commit in the range, that no left-out item is described, and that the wording matches the interface. Count with the Python line rather than wc -m, whose result depends on the shell locale.
Reply with the note per locale, the count, the trace table and the left-out list. Write files only where the project already keeps them:
| Destination | Where the text goes |
|---|---|
| fastlane deliver | fastlane/metadata/en-US/release_notes.txt, one folder per locale (de-DE, fr-FR, ja, zh-Hans) |
| App Store Connect API | attribute whatsNew in PATCH /v1/appStoreVersionLocalizations/{id} |
| App Store Connect website | the What's New field of the new version, per language |
| Google Play via fastlane supply | fastlane/metadata/android/en-US/changelogs/156.txt (file named after the version code), at most 500 characters per language |
Do not upload or submit unless asked. When the release adds features, offer a draft for the separate Notes for Review field, where guideline 2.3.1 requires new functionality to be described specifically.
Tidepool 2.4.0 (a tide-times app) is ready; the last store version is tagged v2.3.0. Output of release-range.sh, shortened:
## Range: v2.3.0..HEAD (13 commits, no merges)
e9bebca 2026-08-11 feat(alerts): tide alerts for saved spots (#412)
1bc978c 2026-08-13 fix(map): pin jumps to wrong harbour after rotating device (#415)
728aa0c 2026-08-14 chore(deps): bump swift-collections to 1.2.1
5249aa8 2026-08-19 ci: cache derived data on main
54d3444 2026-08-21 feat(widget): lock screen widget with next high tide (#421)
3c1afb3 2026-08-25 refactor(store): split SpotStore into reader and writer
9a72212 2026-08-27 perf(chart): draw tide curve off the main thread (#426)
21b6016 2026-09-01 feat(export): CSV export of tide log (#430)
29c84c9 2026-09-02 fix(chart): tide times shown one hour early after daylight saving change (#433)
3a0ddbb 2026-09-03 fix(alerts): alert sound missing on first launch (#434)
2d72cc9 2026-09-04 feat(onboarding): new welcome carousel (#436)
e4871f6 2026-09-05 Revert "feat(onboarding): new welcome carousel (#436)"
e3a5568 2026-09-08 chore: release 2.4.0
git show 21b6016 shows that the export is hidden behind Flags.exportEnabled, and git grep finds static let exportEnabled = false, so it stays out. The note for en-US (408 characters):
Tide alerts are here. Pick any saved spot and Tidepool will notify you ahead of the next high or low tide.
Also new:
• A Lock Screen widget that shows the next high tide at a glance.
• The tide chart scrolls more smoothly on long date ranges.
Fixed:
• The map pin no longer jumps to another harbour when you rotate your device.
• Tide times are no longer shown an hour early after a daylight saving change.
| Line in the note | Commits |
|---|---|
| Tide alerts | e9bebca |
| Lock Screen widget | 54d3444 |
| Smoother chart | 9a72212 |
| Map pin fix | 1bc978c |
| Tide times fix | 29c84c9 |
Left out: 21b6016 (flag off); 2d72cc9 and e4871f6 (reverted); 3a0ddbb (repairs the alerts added in this same range, so no user met the bug); 728aa0c, 5249aa8, 3c1afb3, e3a5568 (internal). The German note, translated from this one, came to 470 characters.
Larder (a pantry inventory app) has no tags. The version-bump search returns:
a469e17 2026-08-03 Bump version to 1.6.1
d3581b6 2026-07-02 Bump version to 1.6.0
release-range.sh d3581b6 HEAD lists six commits: wip, fix crash, update pods, expiry sort, typo, and the bump. Reading the diffs: wip and fix crash both touch Larder/Scanner/BarcodeScanner.swift and together stop a crash when camera access was denied; expiry sort changes the list screen and adds the string "Sort by expiry date"; typo only edits the README.
Note for the App Store (166 characters):
You can now sort your pantry by expiry date, so what needs using up is at the top.
Fixed: the barcode scanner no longer crashes if camera access hasn't been allowed.
At 166 characters it is also under Google Play's 500 and names no platform, so the Android release can reuse it unchanged. Question sent back to the user: did the scanner crash ship in 1.6.0? If it was introduced and repaired between the two bumps, the "Fixed" line is dropped.
%b in the log format when subjects are thin. Monorepos need a path filter (-- apps/ios) on every command.CHANGELOG.md with its own conventions) and not for the first version of an app, which has no What's New field.