A few weeks ago, I opened the auth middleware I had written six months earlier and could not, for the life of me, remember why I had switched httpOnly: true back to httpOnly: false. The commit message was a single line: fix: cookie issue. git blame led me to a PR description that no longer existed because I had squashed and force-pushed my own branch (yes, on my own project — old habits). Thirty minutes of digging through Slack DMs to myself eventually surfaced the answer: a specific browser extension was clobbering session cookies with httpOnly: true.
After enough of those archaeology sessions, I finally accepted that solo developers also need a CHANGELOG. The problem was that writing one from scratch every release never lasts. What sticks for me is a half-automated flow: feed the git log to Antigravity, ask it to draft a CHANGELOG section, then edit it by hand. The "half" turns out to matter — it keeps me honest without becoming a chore.
Why a Solo Project Still Needs CHANGELOG.md
The usual argument against a CHANGELOG on a solo project is "I'm the only one reading it, so I'll just remember." I told myself that for years. The shift happened when I started thinking of future-me as a different person.
Three months later, refactors blur. Six months later, SDK upgrades feel arbitrary. A year later, your data model has decisions baked into it that you cannot reconstruct from the code alone. CHANGELOG.md is, more than anything, an index that makes those past selves legible to your present self.
The reason most solo CHANGELOGs die is timing. If you only update it at release time, you face a week of commits to triage, and you put it off. The flip that worked for me was deciding to update CHANGELOG.md every Saturday morning, ten minutes flat, even when there is nothing to release.
Loose Conventional Commits as Raw Material
For Antigravity to draft anything useful, your commit messages need a small amount of structure. Full Conventional Commits (feat(scope): description with footer) is overkill for solo work — at least, it is for me. The point of a convention is to lower friction, and a strict convention can do the opposite.
Here is the loose set I actually use:
feat:user-visible new functionalityfix:user-visible bug fixrefactor:code reshuffling that does not change behaviorchore:dependencies, build config, CI plumbing- Anything else: write it however you want
That last rule matters. Tired Friday-night commits should not be blocked by your tooling. Antigravity normalizes the rough edges later, so the human side stays cheap.
If you want to enforce the prefixes anyway, the commit-msg hook approach in Husky and lint-staged for Solo Project Commit Hygiene plugs in cleanly. Personally I stop short of enforcement because losing a few "wip" commits is worth the long-term motivation.
Designing the Prompt You Hand to Antigravity
Asking Antigravity to "turn this git log into a changelog" tends to produce vague summaries. Specificity comes from naming the input shape, the output shape, and the things you do not want it to do.
Here is the prompt I use, kept in .agentic/prompts/changelog.md:
You are a CHANGELOG editor for a solo developer.
From the git log below, extract only the changes that matter from a USER's
perspective (feat / fix / important refactor) and emit a Markdown block to
append to the Unreleased section of CHANGELOG.md.
Rules:
- Skip internal-only work (chore, internal refactor) unless it changed behavior.
- Collapse multiple commits on the same feature into a single line.
- Phrase each entry as a change the user can FEEL, not as a commit subject.
- Do NOT speculate about user impact. If the log doesn't say "performance",
do not write "improved performance."
Output format:
### Added
- ...
### Fixed
- ...
### Changed
- ...
The "do not speculate" rule earns its keep. Without it, Antigravity sometimes adds polite-sounding lines like "Improved overall performance" when the actual commits never mentioned performance at all. CHANGELOG entries you cannot defend are worse than missing entries.
Implementation: A Shell Script You Can Steal in Five Minutes
Save the script below as scripts/draft-changelog.sh. It collects the last two weeks of commits in a shape Antigravity can ingest cleanly.
#!/usr/bin/env bash
# scripts/draft-changelog.sh
# Collect recent git history into a Markdown file for Antigravity to read.
# Output: a fenced code block with hash, subject, and first body line per commit.
set -euo pipefail
SINCE="${1:-2 weeks ago}" # override window: bash draft-changelog.sh "1 month ago"
OUTPUT_FILE=".changelog-draft-input.md"
echo "## git log since: ${SINCE}" > "$OUTPUT_FILE"
echo '' >> "$OUTPUT_FILE"
echo '```' >> "$OUTPUT_FILE"
# --no-merges drops merge commits, --pretty keeps body content readable
git log --no-merges --since="$SINCE" \
--pretty=format:'%h %s%n%b%n---' >> "$OUTPUT_FILE"
echo '```' >> "$OUTPUT_FILE"
echo "✓ Wrote: $OUTPUT_FILE"
echo " → Open Antigravity, paste this file plus the CHANGELOG prompt into the Side Panel, run it."A trimmed example of what the file looks like:
## git log since: 2 weeks ago
```
a3b8f12 feat: add Stripe webhook handler for subscription events
Verified signatures with Cloudflare Workers SubtleCrypto.
---
e2c0a91 fix: prevent double-charge when user retries Stripe Checkout
Used the session id as the idempotency key.
---
(truncated)
```
✓ Wrote: .changelog-draft-input.md
→ Open Antigravity, paste this file plus the CHANGELOG prompt into the Side Panel, run it.
Drop that file plus the prompt into Antigravity's Side Panel. You will get a draft ### Added / Fixed / Changed block. I read it once, delete the lines that feel padded, rephrase one or two that read awkwardly, and paste the result under ## [Unreleased] in CHANGELOG.md. Ten minutes, every Saturday.
A pleasant side effect: scrolling through your own log forces you to remember work you forgot to mention to users. Most of my "release notes" tweets now come out of this Saturday window, not out of release-eve panic.
What I Deliberately Leave Out
Knowing what not to put in the CHANGELOG is half of the work. Solo developers who try to capture everything either burn out or end up with a log so noisy nobody (including future-them) reads it.
The categories I drop without hesitation:
- Dependency bumps that did not visibly change anything. A patch-level bump of an internal lint rule belongs in
package-lock.json, not in CHANGELOG.md. If the bump fixed a security advisory or introduced a breaking change, that is different — it gets a dedicated entry. - Internal refactors with no user-facing effect. Renaming a private function from
parseRowtoparseCSVRowis invisible to users. CHANGELOG is a user-facing document; engineering hygiene lives in the commit history itself. - Experiments that got reverted in the same week. If you tried a feature, did not like it, and removed it before any release, there is no need to document the round trip. That is what
git reflogis for. - CI and tooling. Switching from GitHub Actions to a different runner is interesting to you, irrelevant to the user. Goes in a separate
INTERNAL_NOTES.mdif you must.
What I always include, even when tempted to skip:
- Anything that changes a default. Even a one-line config change becomes a support ticket if a user notices the new behavior. Documenting the change saves the future reply.
- Anything that affects pricing, billing, or auth. These are the categories where users feel betrayed when the change appears unannounced. Always log them, even if "the user" right now is just you.
- Anything that changes data shape. New fields on stored records, removed fields, renamed enum values — these have a long tail of consequences. CHANGELOG is the cheapest way to leave breadcrumbs for the migration you will eventually need to write.
This filter is also exactly the lens I tell Antigravity to use, which is why the prompt above explicitly asks for "user-visible" changes only.
Three Things That Kept the Habit Alive
After three months on this loop, I can name the patterns that almost killed it. Pre-empting them helps.
Stop trying to capture every commit.
In the early weeks I felt obligated to translate every line of git log into something CHANGELOG-worthy. Internal refactors and CI tweaks do not belong in a user-facing changelog. When Antigravity drops them, let them stay dropped.
Saturday morning, not Friday evening.
Friday afternoon you are tired and sloppy. Saturday morning, with coffee and no Slack pings, the user-visible rephrasing comes out cleaner. Find your own equivalent — the point is to not write CHANGELOG entries while running on fumes.
Make end-of-month "Unreleased → vX.Y.Z" a small ritual.
Each Saturday you grow [Unreleased]. Once a month you cut it into a real version. I do this at a café, with a different coffee than my Saturday usual. It marks the boundary between "operating" and "shipping" in a way that is small but specific. CHANGELOGs without a release ritual quietly fall behind.
If you want CHANGELOG.md to feed back into how you actually work — for example, having Antigravity reference it as required reading before any large refactor — I cover the .agentic/rules setup in Mastering Antigravity Custom Rules and Project Config. It is a natural next step once you have a few months of CHANGELOG history accumulated.
One Concrete Next Step
If you already have a git history, you can try this today. Run git log --no-merges --since="2 weeks ago" --oneline and just look at it. Notice how many lines genuinely matter to a user, and how many do not. From there, the ten lines of shell above are enough to seed the loop. Next Saturday, your CHANGELOG.md starts growing on its own.