Files
OmniRoute/docs/ops/RELEASE_CHECKLIST.md
Diego Rodrigues de Sa e Souza 5ac4637816 docs: reorganize docs/ + accuracy audit + drop Node 20 (rebased onto current release tip) (#5262)
* docs: move superpowers/research artifacts to isolated _tasks repo + docs tree cleanup

- Move docs/superpowers/{plans,specs} and docs/research/* into the gitignored,
  separately-versioned _tasks/ repo; untrack the two tracked research design docs.
- Add CLAUDE.md "Planning & Research Artifacts" section overriding the superpowers
  default save paths (docs/... -> _tasks/...); align REPOSITORY_MAP and
  DOCUMENTATION_OVERHAUL_PLAN with the new convention.
- Drop 4 now-obsolete /api/discovery/* entries from check-docs-symbols allowlist
  (stale-enforcement) and refresh code/spec path comments to _tasks/...
- Sweeps in concurrent docs-tree restructuring (root-level provider/guide docs,
  compression spec cleanup, .mcp.json.example removal).

* docs: reorganize docs/ tree + fix stale facts across ~26 docs

Phase A — reorganization:
- Move 7 orphan root docs into subfolders (providers/ created; TIERS+USAGE_QUOTA→guides/;
  plugins+PLUGIN_SDK→frameworks/); delete 8 obsolete/redundant docs (SUBMIT_PR superseded
  by CONTRIBUTING; DOCUMENTATION_OVERHAUL_PLAN; INCIDENT_RESPONSE/PERF_BUDGETS/THREAT_MODEL;
  3 ops snapshots). Rebuild README index (was missing ~40 files) + per-folder meta.json nav.
- Clean 14 dangling doc-path references in bin/ ops scripts, scripts/, workflow, tests;
  fix the dockerignore-docs-coverage required-docs path (PROVIDERS→providers/CLAUDE_WEB).

Phase B — content accuracy (verified against code, not the audit summary):
- Functional: ENVIRONMENT flag defaults (INPUT_SANITIZER/MCP_ENFORCE_SCOPES=true,
  COMPRESS_DESCRIPTIONS=false, dynamic heap); MCP-SERVER notion tool names (omniroute_*→
  notion_*) + counts 87→94; coverage gate 75/70→60/60/60/60 (RELEASE_CHECKLIST, COVERAGE_PLAN,
  ERROR_SANITIZATION, CONTRIBUTING); pre-push hook description; regenerate PROVIDER_REFERENCE (237).
- Count drift: providers 237, executors 70, migrations 106, db modules 94, oauth 19,
  strategies 17, MCP 94, flags 38, TS 6.0, open-sse ~900/services 294 across architecture/
  frameworks/ops docs; AUTO-COMBO 9→12 factors w/ correct DEFAULT_WEIGHTS; REASONING +2
  patterns; STEALTH UA defaults; AGENT_PROTOCOLS +cursor-cloud/list-capabilities;
  LANGUAGE_PACKS +id pack.
- Kept Node 20 (runtime guard accepts 20.20.2+; only engines is stricter) and MCP scopes=13
  (mcpScopes.ts) — both were correct in the docs; corrected only the attribution.

* docs: finish content refresh — compression engines, CLI_TOKEN merge, metadata sweep

- Compression: document the additional built-in engines (CCR, headroom, ionizer,
  session-dedup) in COMPRESSION_ENGINES; clarify LLMLingua-2 is the ultra-mode SLM
  backend + cross-ref the extra engines in EXTENDING_COMPRESSION; add the id
  (Indonesian) language pack to LANGUAGE_PACKS.
- AUTO-COMBO: replace the orphan 'How tiers fit' weight table (stale weights) with a
  pointer to the canonical 12-factor DEFAULT_WEIGHTS table.
- Security: merge CLI_TOKEN_AUTH.md (legacy 32-char SHA-256 format) into CLI_TOKEN.md
  as a 'Legacy format — still accepted' section (server accepts both HMAC + legacy),
  delete CLI_TOKEN_AUTH.md, drop it from the index + security nav.
- Metadata: bump stale frontmatter (version/lastUpdated) to 3.8.40/2026-06-28 across the
  doc set audited this pass, and normalize the in-body 'Last updated' header lines to match.

* fix(runtime): drop Node 20 from supported range + align all docs/diagrams/counts

- Node minimum is now 22 (aligned with package.json engines). SUPPORTED_NODE_RANGE in
  src/shared/utils/nodeRuntimeSupport.ts (and the bin/ mirror) drops the 20.x line →
  '>=22.22.2 <23 || >=24.0.0 <27'; getNodeRuntimeSupport now rejects Node 20 as
  unsupported-major. Test updated (TDD): node-runtime-support.test.ts asserts Node 20
  rejected. Docs aligned (TROUBLESHOOTING ×2, TERMUX, RELEASE_CHECKLIST, CODEBASE,
  CLI-TOOLS, README, llm.txt + 42 i18n llm.txt mirrors, skills/cli-serve).
- Diagrams regenerated: mcp-tools-87 -> mcp-tools-94 (34 base + pool 6 = 94) and
  auto-combo-9factor -> auto-combo-12factor (correct DEFAULT_WEIGHTS); SVGs re-rendered
  via mermaid-cli; doc refs + diagrams/README updated; fixed a pre-existing broken
  resilience-3layers image path.
- CLAUDE.md + AGENTS.md aligned to real counts (237 providers, 94 MCP tools / 34 base,
  106 migrations, 94 db modules, 12-factor auto-combo, 17 strategies); README provider
  count 231 -> 237; executor count corrected to 68 (provider executors, excl base/index)
  and OAuth to 18 across architecture docs. check:docs-all now passes (0 strict drift,
  0 broken links); removed dead .mcp.json.example doc link.

* fix(services): update installer Node hint to >=22.22.2 (aligned with dropped Node 20)

* docs: realign counts to current release tip after rebase

The release tip advanced while this work was in flight (Gemini CLI provider/executor
removed by #5246, plus other PRs). Re-counted against the current code and updated:
providers 237->236, executors 68->67, OAuth modules 18->17, open-sse services 294->298;
regenerated PROVIDER_REFERENCE.md (236). check:docs-all passes (0 strict drift).

* docs(changelog) + i18n: record Node 20 drop + fix nodeIncompatibleHint

- CHANGELOG: add [3.8.40] entries for the Node 20.x removal (runtime) and the docs
  reorganization/accuracy audit.
- i18n: nodeIncompatibleHint across all 42 locales no longer lists Node 20.x as
  supported (ASCII + CJK full-width variants), aligned with the dropped Node 20.

* fix(docs): repair CI breakages from the doc moves

- test: cli-plugin-system asserted docs/dev/plugins.md exists; the file moved to
  docs/frameworks/PLUGINS.md — point the test at the new path (Unit fast-path 2/2 fix).
- frontmatter: PLUGINS.md and PLUGIN_SDK.md moved into the fumadocs-indexed
  docs/frameworks/ which requires a 'title' frontmatter; the missing frontmatter
  failed the Next.js MDX build (dast-smoke 'invalid frontmatter'). Added frontmatter
  to both, plus the providers/ docs (consistency; that folder is not indexed).
2026-06-28 22:07:18 -03:00

16 KiB

title, version, lastUpdated
title version lastUpdated
Release Checklist 3.8.40 2026-06-28

Release Checklist

Last updated: 2026-06-28 — v3.8.40 Streamlined release flow that leverages Claude Code skills for automation.

Manter a fila/branch verdes entre releases: veja RELEASE_GREEN.md (família /green-prs + npm run check:release-green + /babysit + nightly). Rodar periodicamente — e principalmente antes deste checklist — faz a release PR nascer verde.

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 (60/60/60/60)

# 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-cc

Detailed 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.0 branch
  • 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.md from git commits since last tag
    • Updates README.md badges
  • Manually review CHANGELOG.md and clean up commit messages if needed
  • Ensure the latest semver section in CHANGELOG.md equals package.json version
  • Keep ## [Unreleased] as the first changelog section for upcoming work
  • Update docs/openapi.yamlinfo.version must equal package.json version

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 runtime floor met (>=22.22.2 <23, >=24.0.0 <27, per SUPPORTED_NODE_RANGE in src/shared/utils/nodeRuntimeSupport.ts; aligned with package.json engines)

Testing

  • npm run test:unit — pass
  • npm run test:vitest — pass (MCP server, autoCombo, cache)
  • npm run test:coverage — gate 60/60/60/60 satisfied (statements/lines/functions/branches)
  • npm run test:integration — pass (if changes touch DB / handlers)
  • npm run test:combo:matrix — pass (combo strategy matrix: proves all 17 routing strategies' selection decisions deterministically; run when touching combo routing, strategy resolution, or fallback logic)
  • RUN_COMBO_LIVE=1 npm run test:combo:liveoptional/manual (gated real-upstream smoke; sources a read-only DB snapshot from VPS root@192.168.0.15; hits real providers, costs credits; never runs in CI; skips cleanly without the gate)
  • npm run test:combo:live:vpsoptional/manual (Phase-3 VPS live smoke: 7 HTTP scenarios against the live .15 server via plain Node ESM; requires ssh root@192.168.0.15; creates/deletes only __live_test__* combos; hits real providers; never runs in CI)
  • 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: fast deterministic gates — npm run check:any-budget:t11 && npm run check:tracked-artifacts (activated 2026-06-13). Intentionally excludes test:unit (slow; covered by the CI test-unit job).
    • Run npm run test:unit manually before pushing release branches.

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-sync passes (auto-run by pre-commit)
  • npm run check:docs-all passes (umbrella: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links)
  • npm run check:env-doc-sync exits 0 — code ↔ .env.exampledocs/reference/ENVIRONMENT.md env contract is intact
  • npm run check:doc-links exits 0 — no broken internal markdown references after restructuring
  • docs/architecture/ARCHITECTURE.md reviewed for storage/runtime drift
  • docs/guides/TROUBLESHOOTING.md reviewed for env var and operational drift
  • If .env.example changed: docs/reference/ENVIRONMENT.md updated
  • If new feature has a UI: docs/guides/USER_GUIDE.md mentions it
  • If new feature has API: docs/reference/API_REFERENCE.md + docs/openapi.yaml updated
  • If new feature is a module: dedicated docs/<MODULE>.md exists
  • If breaking change: docs/guides/TROUBLESHOOTING.md has migration note

i18n

  • npm run i18n:check exits 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-coverage exits 0 — every UI locale at or above the 80% coverage floor
  • npm run i18n:sync-ui:dry reports 0 missing keys across all 42 locales
  • If source English docs changed, run npm run i18n:run (requires OMNIROUTE_TRANSLATION_API_KEY in .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)
  • Test on fresh install: delete ~/.omniroute/omniroute.db and run npm 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.ts Zod schema valid at load time
    • All providers have required fields (id, label, kind, etc.)
    • freeNote provided for new free providers
    • OAuth providers have oauthConfig registered in src/lib/oauth/constants/oauth.ts
  • 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:packaged passes
  • Builds tested for at least one of :win, :mac, :linux
  • Code signing certs not expired (if signing)
  • electron/package.json version matches root package.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 rsync dist/ contents into the remote app/ 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:release succeeds and dist/BUILD_SHA == git rev-parse --short HEAD
  • npm run check:pack-artifact clean — no app.__qa_backup, scripts/scratch, package-lock.json, or other local residue
  • dist/server.js exists 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)
  • 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_modules is real (main checkout or npm ci'd worktree — NOT a symlinked worktree)
  • Smoke test deployed instance:
    • Open /dashboard/health → check version string matches release
    • Run a /v1/chat/completions request against a known provider
    • Verify /api/monitoring/health returns CLOSED circuit breakers
    • Confirm MCP transports respond (/mcp HTTP, /mcp-sse SSE)

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.json for 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 migration 071_services.sql applied + 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 (validates 070_webhooks_kind_metadata.sql applied).
  • node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.ts passes — guards against future collisions.

9Router

  • POST /api/services/9router/install returns 200 with installedVersion in under 2 min
  • POST /api/services/9router/start returns 200 and state: "running" in under 30 s
  • GET /api/services/9router/status reports health: "healthy"
  • POST /v1/chat/completions with "model": "9router/auto/..." returns 200 (end-to-end routing through 9Router)
  • GET /dashboard/providers/services/9router/embed/dashboard renders the 9Router native UI inside the proxy (no direct 127.0.0.1:port iframe)
  • POST /api/services/9router/rotate-key returns { keyRotated: true } and service restarts cleanly
  • POST /api/services/9router/stop returns 200 and state: "stopped"
  • GET /api/services/9router/logs?tail=50 returns SSE stream with snapshot event containing recent lines
  • Install in environment without npm in PATH returns 500 with a friendly (non-stack-trace) error message

CLIProxyAPI

  • POST /api/services/cliproxy/install returns 200 in under 2 min
  • POST /api/services/cliproxy/start returns 200 and state: "running" in under 30 s
  • GET /api/services/cliproxy/status reports health: "healthy"
  • POST /api/services/cliproxy/stop returns 200 and state: "stopped"
  • GET /api/services/cliproxy/logs?tail=50 returns SSE stream

Security regression

  • curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/9router/start returns 403 LOCAL_ONLY
  • curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/cliproxy/start returns 403 LOCAL_ONLY
  • Error responses from /api/services/* do not contain err.stack or absolute file paths

v3.8.0+ checks

Before shipping any v3.8.x release, verify these additional items:

  • omniroute --tray boots on macOS (systray2 installed into ~/.omniroute/runtime/)
  • omniroute --tray boots on Linux (requires DISPLAY; graceful error if not set)
  • omniroute --tray boots on Windows (PowerShell NotifyIcon, no extra binaries)
  • omniroute config tray enable creates autostart entry; disable removes it
  • npm install -g omniroute@<this-version> runs postinstall without fatal exit
  • Update path keeps optional deps: omniroute update --apply and the auto-updater run npm install -g … --include=optional so optionalDependencies (better-sqlite3, keytar, tls-client, and the llmlingua SLM stack: @atjsh/llmlingua-2, @huggingface/transformers@3.5.2, @tensorflow/tfjs, js-tiktoken) survive an update. @huggingface/transformers stays optional so its onnxruntime-node CUDA provider postinstall cannot abort installation on CUDA 11 hosts. The ultra modelPath SLM tier also needs the tinybert model, auto-downloaded to ${DATA_DIR}/models/llmlingua on first use. Postinstall (scripts/build/colocateOptionals.mjs) then co-locates the SLM optional closure into dist/node_modules so the worker resolves a SINGLE @huggingface/transformers 3.5.2 optional instance — the standalone trace bundles only transformers, not the dynamically-imported optionals, so without this the worker would load llmlingua-2 against the root's transformers and the SLM tier would silently fail-open.
  • omniroute status works with no .env (CLI token path, loopback only)
  • curl http://localhost:20128/api/shutdown returns 401 (always-protected route)
  • curl -H "host: evil.com" http://localhost:20128/api/mcp/sse returns 401 (loopback guard)
  • SQLite runtime resolves to bundled on first run (bundled binary valid for platform)
  • SQLite runtime falls back to runtime when node_modules/better-sqlite3 is deleted
  • Smart MCP filter compresses real playwright-mcp browser_snapshot output (≥50% reduction)
  • All 10 skills/omniroute*/SKILL.md files 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:

  1. gh release edit vX.Y.Z --prerelease (marks as not latest)
  2. git tag -d vX.Y.Z && git push --delete origin vX.Y.Z (only if not yet adopted by users)
  3. Or: hotfix on release/vX.Y.0 → patch release vX.Y.(Z+1)
  4. Communicate in GitHub Discussions and Discord immediately

Hard Rules

  • Never commit directly to main
  • Never use git push --force to main or release/* branches
  • Never skip Husky hooks (--no-verify)
  • Never commit secrets, credentials, or .env files
  • Coverage must stay ≥60/60/60/60 (statements/lines/functions/branches)
  • Always include or update tests when changing production code in src/, open-sse/, electron/, or bin/

Automated Sync Check

Run the docs sync guard locally before opening a PR:

npm run check:docs-sync

CI also runs this check in .github/workflows/ci.yml (lint job).