Release Process
How we cut releases. Follow exactly — the inter-package version pins are easy to get wrong.
Cadence
We don't run on a fixed cadence. Releases happen when:
- A bug is severe enough to warrant patching production within hours (most of the 0.9.x cycle).
- A feature is ready and tested across all affected packages.
- A research finding produces a new integration plan worth shipping.
There is no release planning meeting. Each release is a self-contained git commit + tag + npm publish + GitHub release.
Versioning
The monorepo doesn't use a single version. Each package versions independently:
| Package | Series | Notes |
|---|---|---|
@agenticmail/core |
0.9.x | Stable SDK surface; bumps only on schema or behavior changes |
@agenticmail/mcp |
0.9.x | MCP tool catalogue; bumps when tool schemas change |
@agenticmail/api |
0.9.x | HTTP API server; bumps frequently (user-visible) |
@agenticmail/claudecode |
0.2.x | Claude Code integration; separate series because Claude Code's SDK was on its own minor track |
@agenticmail/cli |
0.9.x | Top-level CLI; bumps to pin newer api + claudecode |
Plugin manifest (plugin.json) |
0.9.x | Mirrors the cli version |
When in doubt: bump the cli for every release, even if only api or claudecode changed underneath. The cli is the user-facing version number, and the version bump forces npm install -g @agenticmail/cli@latest to pull the new transitive deps.
Checklist
For each release, in order:
1. Make the change
Edit code, add tests, ensure all packages build and all tests pass.
cd ~/Desktop/projects/agenticmail/agentic-mail
npm run build
# per-package:
cd packages/api && npm test
cd packages/claudecode && npm test
cd packages/core && npm test
2. Bump versions
Edit package.json files in the packages that changed. Update:
packages/api/package.jsonif api changedpackages/claudecode/package.jsonif claudecode changedagenticmail/package.jsonalways — bump version AND update the deps pin (e.g."@agenticmail/api": "^0.9.11"if api just shipped)plugin/.claude-plugin/plugin.json— mirror the cli version
The cli's deps pin is the most-frequently-missed step. Without it, npm install -g @agenticmail/cli@latest won't pull the new api/claudecode you just published.
3. Update the changelog
Add a section to CHANGELOG.md at the top:
## [0.9.X] - 2026-MM-DD
### Fixed — Concise one-line summary of the user-visible symptom
User report (if applicable): *"quoted user message"*
### Root cause
Plain-English diagnosis. What was wrong. WHY it was wrong.
### Fix
Code-level diff intent. Not literal diff — the explanation.
### Tests
If tests were added, name them.
### Published
| Package | Old | New |
|---|---|---|
| `@agenticmail/api` | 0.9.X | 0.9.Y |
| `@agenticmail/cli` | 0.9.X | 0.9.Y |
Format faithfully — these get pasted verbatim into the GitHub release notes.
4. Rebuild
cd ~/Desktop/projects/agenticmail/agentic-mail
npm run build:api && npm run build:claudecode && npm run build:cli
build:cli copies packages/api/public/ into agenticmail/dist/public/ — without this, web UI changes ship without their assets.
5. Commit + tag + push
git add -A
git commit -m "$(cat <<'EOF'
Release 0.9.X: short summary
Multi-line description.
Published:
@agenticmail/api 0.9.X -> 0.9.Y
@agenticmail/cli 0.9.X -> 0.9.Y
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"
git tag v0.9.X
git push origin main
git push origin v0.9.X
6. Publish to npm
Order matters — publish leaf packages BEFORE packages that depend on them, so the dep resolution works for fresh installs:
# If core changed:
cd packages/core && npm publish
# If mcp changed:
cd packages/mcp && npm publish
# If api changed:
cd packages/api && npm publish
# If claudecode changed:
cd packages/claudecode && npm publish
# Always last:
cd agenticmail && npm publish
The cli always publishes last because it pins the newest versions of everything else.
7. GitHub release
gh release create v0.9.X \
--title "v0.9.X — One-line title" \
--notes "$(cat <<'EOF'
[paste the changelog entry here, lightly reformatted for GH markdown]
EOF
)"
8. Operator instructions
In the release notes' "Operator upgrade" section, always include:
npm install -g @agenticmail/cli@latest
pm2 restart agenticmail-claudecode-dispatcher
If the change affected the web UI, add:
# Then hard-reload the browser to clear cached JS / CSS
If the change affected the hook installer:
agenticmail-claudecode install # re-runs hook installer
Mistakes that have actually happened
Historical from the 0.9.x cycle, with what to do next time:
- 0.9.6 was crash-looping in production because the bundle missed
@agenticmail/corefrom deps. Always rebuild + manually inspectdist/for unexpected chunk sizes before publishing. A claudecode bundle should be ~70 KB, not 500 KB. - Forgetting to bump the cli's deps pin. The most common mistake. Symptom:
npm install -g @agenticmail/cli@latestdoesn't pull the new api/claudecode because the cli'spackage.jsonstill pins the old version. Verify by checkingagenticmail/package.jsonreflects the version you just bumped. - Forgetting to republish the cli. Even if you only changed api code, you must republish the cli with the bumped api pin. Otherwise users still install the OLD api.
- Publishing claudecode before bumping its deps. If claudecode imports from
@agenticmail/coreyou must bump its dep before publishing — otherwise the published bundle is built against an old core. - Pushing to the wiki repo before initializing it. GitHub doesn't create
.wiki.gituntil at least one page is created in the web UI. First commit must come from the UI.
When NOT to release
- Build passes but tests fail. Fix the tests first. Do not
--no-verifypush or commit through it. - You can't reproduce the bug locally. Especially for "the dispatcher crashed in production" reports — get the actual
pm2 logsoutput from the user before releasing a guess. - The fix changes behavior across packages but you only updated one package's tests. Bump and test all affected packages, even if only one of them publishes.