Release Checklist
Last updated: 2026-05-13 — v3.8.0 Streamlined release flow that leverages Claude Code skills for automation.
TL;DR
# 1. Bump version + generate CHANGELOG (skill)
/version-bump-cc patch # or minor/major
# 2. Run quality gate locally
npm run check # lint + tests
npm run test:coverage # full coverage gate (75/75/75/70)
# 3. Build & smoke
npm run build
npm run test:e2e # optional but recommended
# 4. Generate release (skill)
/generate-release-cc
# 5. Deploy (skill)
/deploy-vps-both-cc # or akamai-cc / local-cc
# 6. Capture release evidences (skill)
/capture-release-evidences-ccDetailed Checklist
Pre-release
- All PRs targeted to this release are merged to
release/vX.Y.0 - All open Linear/issue items for this version are closed or pushed to next milestone
- CI green on
release/vX.Y.0branch - No
TODO(release)markers in code:grep -r "TODO(release)" src/ open-sse/ - Docker base image up to date (currently
node:24.15.0-trixie-slim)
Version & Changelog
- Run
/version-bump-cc <patch|minor|major>(Claude Code skill)- Bumps
package.json,electron/package.json - Regenerates
CHANGELOG.mdfrom git commits since last tag - Updates README.md badges
- Bumps
- Manually review CHANGELOG.md and clean up commit messages if needed
- Ensure the latest semver section in
CHANGELOG.mdequalspackage.jsonversion - Keep
## [Unreleased]as the first changelog section for upcoming work - Update
docs/reference/openapi.yaml→info.versionmust equalpackage.jsonversion
Code Quality
-
npm run lint— 0 errors (warnings are pre-existing) -
npm run typecheck:core— clean -
npm run typecheck:noimplicit:core— clean (strict) -
npm run check:cycles— no circular deps -
npm run check:any-budget:t11— within budget -
npm run check:route-validation:t06— clean -
npm run check:node-runtime— supported floor met (>=20.20.2 <21,>=22.22.2 <23,>=24.0.0 <25)
Testing
-
npm run test:unit— pass -
npm run test:vitest— pass (MCP server, autoCombo, cache) -
npm run test:coverage— gate 75/75/75/70 satisfied (statements/lines/functions/branches) -
npm run test:integration— pass (if changes touch DB / handlers) -
npm run test:e2e— pass (UI changes) -
npm run test:protocols:e2e— pass (MCP/A2A changes) -
npm run test:ecosystem— pass
Hooks (Husky validated)
Husky hooks live in .husky/ and run automatically on git operations.
- pre-commit:
npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11 - pre-push: currently disabled (commented out). When re-enabled, runs
npm run test:unit.- Run
npm run test:unitmanually before pushing release branches.
- Run
If a hook fails: fix the underlying issue, don't bypass with --no-verify.
Conventional Commits
All release-bound commits must follow type(scope): subject format.
Valid types: feat, fix, refactor, docs, test, chore, perf, style, ci
Valid scopes: db, sse, oauth, dashboard, api, cli, docker, ci, mcp, a2a, memory, skills, cloud-agent, guardrails, compression, auto-combo, resilience, providers, executors, translator, domain, authz
Breaking changes: add BREAKING CHANGE: footer or ! after the scope (e.g. feat(api)!: drop /v0).
Documentation
-
npm run check:docs-syncpasses (auto-run by pre-commit) -
npm run check:docs-allpasses (umbrella: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links) -
npm run check:env-doc-syncexits 0 — code ↔.env.example↔docs/reference/ENVIRONMENT.mdenv contract is intact -
npm run check:doc-linksexits 0 — no broken internal markdown references after restructuring -
docs/architecture/ARCHITECTURE.mdreviewed for storage/runtime drift -
docs/guides/TROUBLESHOOTING.mdreviewed for env var and operational drift - If
.env.examplechanged:docs/reference/ENVIRONMENT.mdupdated - If new feature has a UI:
docs/guides/USER_GUIDE.mdmentions it - If new feature has API:
docs/reference/API_REFERENCE.md+docs/reference/openapi.yamlupdated - If new feature is a module: dedicated
docs/<MODULE>.mdexists - If breaking change:
docs/guides/TROUBLESHOOTING.mdhas migration note
i18n
-
npm run i18n:checkexits 0 — translation state (.i18n-state.json) in sync with source docs (no drifted sources in strict mode; warn-mode advisory is acceptable for last-minute doc touch-ups, but should be 0 before tagging) -
npm run i18n:check-ui-coverageexits 0 — every UI locale at or above the 80% coverage floor -
npm run i18n:sync-ui:dryreports 0 missing keys across all 40 locales - If source English docs changed, run
npm run i18n:run(requiresOMNIROUTE_TRANSLATION_API_KEYin.env) before tagging - Translation contributions can be deferred to next release if minor (track in CHANGELOG)
Database Migrations
- If
src/lib/db/migrations/has new files:- Each migration is idempotent (
CREATE TABLE IF NOT EXISTS, etc.) - Migrations wrapped in transactions
- Numbered correctly (no gaps in sequence)
- Each migration is idempotent (
- Test on fresh install: delete
~/.omniroute/omniroute.dband runnpm run dev - Test on existing install: backup DB, run migration, verify schema
- WAL files (
-wal,-shm) handled correctly if migration rewrites tables
Provider Catalog (Zod-validated)
-
src/shared/constants/providers.tsZod schema valid at load time- All providers have required fields (
id,label,kind, etc.) -
freeNoteprovided for new free providers - OAuth providers have
oauthConfigregistered insrc/lib/oauth/constants/oauth.ts
- All providers have required fields (
- If new provider added: corresponding executor in
open-sse/executors/ - If non-OpenAI format: translator in
open-sse/translator/ - Models registered in
open-sse/config/providerRegistry.ts - Unit tests in
tests/unit/cover provider classification and routing
Desktop (Electron)
If electron/ changed:
-
npm run electron:smoke:packagedpasses - Builds tested for at least one of
:win,:mac,:linux - Code signing certs not expired (if signing)
-
electron/package.jsonversion matches rootpackage.json - Auto-update channel pointer updated if releasing to
stable
Build Layout
The repository uses three distinct output directories — never mix them up:
| Directory | Purpose | Tracked? |
|---|---|---|
src/ | Application source (TypeScript / TSX) | Yes |
.build/ | Build intermediates — next build output (distDir) | No (gitignored) |
dist/ | Shippable npm bundle — assembled by assembleStandalone | No (gitignored) |
Operator note: the remote VPS image directory remains
/usr/lib/node_modules/omniroute/app/. Only the in-repo build output moved (app/→dist/). The deploy skills rsyncdist/contents into the remoteapp/dir — no VPS path changes required.
Single-build flow:
npm run build:release
└─ rm -rf .build dist (clean)
└─ next build → .build/next/ (intermediates)
└─ assembleStandalone (copies standalone + static + public + natives → dist/)
└─ writes dist/BUILD_SHA (HEAD sentinel)Do NOT run npm run build followed by a separate npm run build:cli for deploy — use
npm run build:release which does a clean rebuild + sentinel in one command.
Artifact Validation
-
npm run build:releasesucceeds anddist/BUILD_SHA==git rev-parse --short HEAD -
npm run check:pack-artifactclean — noapp.__qa_backup,scripts/scratch,package-lock.json, or other local residue -
dist/server.jsexists after build
Tagging & Release
- Run
/generate-release-cc(Claude Code skill):- Creates tag
vX.Y.Z - Pushes tag and branch
- Opens GitHub Release with changelog body
- Attaches Electron installers (if built)
- Creates tag
- Or manually:
git tag -a vX.Y.Z -m "Release vX.Y.Z" git push origin vX.Y.Z gh release create vX.Y.Z --notes-from-tag
Deploy
Deploy skills use the light rsync flow — no npm pack, no npm i -g:
- Use deploy skill that matches target:
/deploy-vps-local-cc— local VPS (192.168.0.15)/deploy-vps-akamai-cc— Akamai VPS (69.164.221.35)/deploy-vps-both-cc— both
- Before deploying, confirm
dist/BUILD_SHA==git rev-parse --short HEAD - Build must run where
node_modulesis real (main checkout ornpm ci'd worktree — NOT a symlinked worktree) - Smoke test deployed instance:
- Open
/dashboard/health→ check version string matches release - Run a
/v1/chat/completionsrequest against a known provider - Verify
/api/monitoring/healthreturnsCLOSEDcircuit breakers - Confirm MCP transports respond (
/mcpHTTP,/mcp-sseSSE)
- Open
Post-release
- Run
/capture-release-evidences-cc(Claude Code skill)- Captures WebP screenshots/recordings of new features
- Attaches to release notes / blog post
- Update GitHub Discussions / Discord with release announcement
- Open milestone for next version
- If critical: pin discussion or post in
news.jsonfor in-app banner
Embedded Services smoke (v3.8.4+)
Before shipping any release that includes embedded services changes, verify:
Fresh-DB boot (catches migration collisions — added after v3.8.4 hotfix)
-
DATA_DIR=$(mktemp -d) npm start &— wait 10 s for boot -
curl -s http://127.0.0.1:20128/api/services/9router/status | jq '.tool'returns"9router"(NOT 404, NOT 500). Confirms migration071_services.sqlapplied + row seeded. -
sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(version_manager);" | grep -E "provider_expose|logs_buffer_path|last_sync_at"returns 3 rows. -
sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(webhooks);" | grep -E "kind|metadata_encrypted"returns 2 rows (validates070_webhooks_kind_metadata.sqlapplied). -
node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.tspasses — guards against future collisions.
9Router
-
POST /api/services/9router/installreturns 200 withinstalledVersionin under 2 min -
POST /api/services/9router/startreturns 200 andstate: "running"in under 30 s -
GET /api/services/9router/statusreportshealth: "healthy" -
POST /v1/chat/completionswith"model": "9router/auto/..."returns 200 (end-to-end routing through 9Router) -
GET /dashboard/providers/services/9router/embed/dashboardrenders the 9Router native UI inside the proxy (no direct127.0.0.1:portiframe) -
POST /api/services/9router/rotate-keyreturns{ keyRotated: true }and service restarts cleanly -
POST /api/services/9router/stopreturns 200 andstate: "stopped" -
GET /api/services/9router/logs?tail=50returns SSE stream withsnapshotevent containing recent lines - Install in environment without
npmin PATH returns 500 with a friendly (non-stack-trace) error message
CLIProxyAPI
-
POST /api/services/cliproxy/installreturns 200 in under 2 min -
POST /api/services/cliproxy/startreturns 200 andstate: "running"in under 30 s -
GET /api/services/cliproxy/statusreportshealth: "healthy" -
POST /api/services/cliproxy/stopreturns 200 andstate: "stopped" -
GET /api/services/cliproxy/logs?tail=50returns SSE stream
Security regression
-
curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/9router/startreturns403 LOCAL_ONLY -
curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/cliproxy/startreturns403 LOCAL_ONLY - Error responses from
/api/services/*do not containerr.stackor absolute file paths
v3.8.0+ checks
Before shipping any v3.8.x release, verify these additional items:
-
omniroute --trayboots on macOS (systray2 installed into~/.omniroute/runtime/) -
omniroute --trayboots on Linux (requires DISPLAY; graceful error if not set) -
omniroute --trayboots on Windows (PowerShell NotifyIcon, no extra binaries) -
omniroute config tray enablecreates autostart entry; disable removes it -
npm install -g omniroute@<this-version>runs postinstall without fatal exit -
omniroute statusworks with no.env(CLI token path, loopback only) -
curl http://localhost:20128/api/shutdownreturns 401 (always-protected route) -
curl -H "host: evil.com" http://localhost:20128/api/mcp/ssereturns 401 (loopback guard) - SQLite runtime resolves to
bundledon first run (bundled binary valid for platform) - SQLite runtime falls back to
runtimewhennode_modules/better-sqlite3is deleted - Smart MCP filter compresses real
playwright-mcp browser_snapshotoutput (≥50% reduction) - All 10
skills/omniroute*/SKILL.mdfiles are publicly fetchable via raw GitHub URL - Onboarding wizard shows "How It Works" tier tour step on fresh setup
- Home dashboard tier coverage widget shows configured/active counts
Rollback
If release has critical issue:
gh release edit vX.Y.Z --prerelease(marks as not latest)git tag -d vX.Y.Z && git push --delete origin vX.Y.Z(only if not yet adopted by users)- Or: hotfix on
release/vX.Y.0→ patch releasevX.Y.(Z+1) - Communicate in GitHub Discussions and Discord immediately
Hard Rules
- Never commit directly to
main - Never use
git push --forcetomainorrelease/*branches - Never skip Husky hooks (
--no-verify) - Never commit secrets, credentials, or
.envfiles - Coverage must stay ≥75/75/75/70 (statements/lines/functions/branches)
- Always include or update tests when changing production code in
src/,open-sse/,electron/, orbin/
Automated Sync Check
Run the docs sync guard locally before opening a PR:
npm run check:docs-syncCI also runs this check in .github/workflows/ci.yml (lint job).