mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-07 15:52:52 +03:00
* chore(release): open v3.8.35 development cycle
* fix db vacuum scheduler settings (#4726)
Scheduled VACUUM now follows Storage page settings (scheduledVacuum/vacuumHour) as single source of truth; env-flag control path removed. 11/11 vacuum-scheduler tests pass against release/v3.8.35 tip; no orphaned env refs. Integrated into release/v3.8.35.
* fix(tier): noAuth providers count as free; free filter returns empty … (#4753)
noAuth providers now classified free (union of legacy list + NOAUTH_PROVIDERS chat-tier derivation), -free arena_elo alias, and auto/<cat>:free returns an empty pool when no free candidate matches (opt-in legacy fallback via OMNIROUTE_AUTO_FREE_FALLBACK_TO_FULL_POOL). New env var documented in .env.example + ENVIRONMENT.md; CHANGELOG bullet added (maintainer co-author). 46/46 node + 56/56 vitest tests pass on release tip; env-doc-sync, docs-sync, typecheck:core, lint, file-size all green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai 11 helpers de nível superior para 6 leaves puros (#3501) (#4571)
chatCore god-file decomposition (#3501): extract 6 pure leaves (cacheUsageMeta, executorClientHeaders, nonStreamingResponseBody, skillsFormat, streamErrorResult, streamFinalize) from chatCore.ts. Rebased onto release/v3.8.35 tip (resolved single chatCore.ts conflict — removed now-extracted inline buildExecutorClientHeaders). 265/265 chatcore tests, 26/26 new leaf tests, typecheck:core, cycles, file-size all green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai resolveExecutorWithProxy + getExecutionCredentials para leaves (#3501) (#4646)
chatCore #3501: extract resolveExecutorWithProxy + getExecutionCredentials to leaves (executorProxy.ts, executionCredentials.ts). Clean cherry-pick onto release tip post-#4571. 12/12 new leaf tests, typecheck:core, cycles, file-size green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai transforms de mensagens Claude p/ leaf (#3501) (#4708)
chatCore #3501: extract Claude upstream-message transforms to leaf (claudeUpstreamMessages.ts + claudeMessageTypes.ts). Clean cherry-pick post-#4646. 8/8 new leaf tests, typecheck/cycles/file-size green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai persistAttemptLogs para leaf (#3501) (#4717)
chatCore #3501: extract persistAttemptLogs to leaf (attemptLogging.ts). Rebased onto release tip post-#4708 (resolved imports conflict: kept tip's resolveCompressionHeader from compression Phase 3, dropped now-unused logTruncation import moved into the leaf). 288/288 chatcore tests, typecheck/cycles/file-size green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai stageTrace + compressionUsageReceipt para leaves (#3501) (#4721)
chatCore #3501: extract stageTrace + compressionUsageReceipt to leaves. Clean cherry-pick post-#4717. 6/6 new leaf tests, typecheck/cycles/file-size green. Integrated into release/v3.8.35.
* refactor(chatCore): extrai prepareUpstreamBody (1ª sub-fatia do executeProviderRequest, #3501) (#4730)
chatCore #3501: extract prepareUpstreamBody (first sub-slice of executeProviderRequest) to leaf (upstreamBody.ts). Clean cherry-pick post-#4721. 7/7 new leaf tests, full 301/301 chatcore suite, typecheck/cycles/file-size green. Completes the 6-PR chatCore decomposition stack into release/v3.8.35.
* fix(db): make db-backup import size cap configurable (#4719) (#4757)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* chore(quality): expand check:release-green to the FULL release-PR gate set (#4758)
The release-green pre-flight (Solution C) previously covered only a subset of the
gates that run exclusively on the release PR (PR→main), so reds still accrued
silently on release/** and surfaced in ~40-min layers at release time (v3.8.34:
3 CI rounds — CodeQL sanitization, then the fail-fast Quality Ratchet revealing
openapi then cyclomatic-complexity one push at a time, plus zizmor/integration).
Now check:release-green reproduces the COMPLETE release-PR gate set and reports
EVERY red in one pass (collected, not fail-fast):
- New DRIFT ratchets (report-only, rebaselined at release, never block):
cyclomatic complexity, dead-code, type-coverage, compression-budget,
openapi-coverage, workflow-lint (zizmor), codeql-ratchet.
- New HARD gates (real defects): docs-all (fabricated-docs strict + i18n mirror
sync) and the integration test suite (gated behind !--quick).
The only release-PR gates it still cannot reproduce locally are GitHub-side CodeQL
semantic analysis and SonarQube/SonarCloud (external services).
The nightly-release-green workflow and /green-prs inherit the expanded coverage
automatically (they invoke this script), so cycle drift is now surfaced
continuously and the release PR is green on its first CI run.
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* fix(dashboard): add missing onboarding.tiers step title (#4698) (#4755)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* feat(compression): Output Styles registry + D0 telemetry (Phase 4A) (#4694)
Phase 4A: Output Styles registry + D0 telemetry. Integrated into release/v3.8.35.
* feat(compression): SLM tier for ultra (Phase 4B) [stacked on #4694] (#4707)
Phase 4B: SLM tier for ultra. Integrated into release/v3.8.35.
* feat(compression): context-budget adaptive compression (Phase 4C) [stacked on #4707] (#4716)
Phase 4C: adaptive context-budget compression. Integrated into release/v3.8.35.
* feat(compression): offline evaluation harness (Phase 4 D1) [stacked on #4716] (#4720)
Phase 4 D1: offline evaluation harness. Integrated into release/v3.8.35.
* fix(sse): deepseek-web folds role:tool results into prompt transcript (#4712) (#4756)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* fix(dashboard): remove dead unconditional useLiveRequests call in HomePageClient (#4759, #4745, #4596) (#4761)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* fix(dashboard): dedupe provider nodes by id on compatible-provider add (#4746) (#4768)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* chore(db): re-export compressionRunTelemetry from localDb to satisfy db-rules (#4775)
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
* docs(security): add canonical STRIDE-based threat model (#4783)
Canonical STRIDE threat model. Integrated into release/v3.8.35.
* test(dashboard): add smoke test for home client dashboard (#4793)
Smoke test guarding the dashboard home client render (regression #4745/#4759). Code fix already landed via #4761; this PR's jsdom smoke test is the net-new regression guard. Integrated into release/v3.8.35.
* fix(combos): auto-promote zeroLatencyOptimizationsEnabled so legacy configs (pre-3.8.33 fallbackCompressionMode="lite") round-trip on the first GUI edit (#4774)
Auto-promote zeroLatencyOptimizationsEnabled + strip v3.8.31-era removed keys so legacy combo configs round-trip through PUT /api/combos/{id} on first GUI edit (closes #4382 followup). Pre-merge: rewrote the now-stale reject test to assert auto-promotion + added passthrough/round-trip regression guards; reconciled combos/page.tsx file-size baseline. Integrated into release/v3.8.35.
* refactor(chatCore): extrai parse + usage-stats não-streaming do executeProviderRequest (#3501) (#4762)
chatCore #3501: extract parseNonStreamingResponseBody + recordNonStreamingUsageStats. Integrated into release/v3.8.35.
* refactor(chatCore): extrai recordContextEditingTelemetryHook (#3501) (#4779)
chatCore #3501: extract recordContextEditingTelemetryHook. Integrated into release/v3.8.35.
* refactor(chatCore): extrai recordCompressionCacheStats (#3501) (#4792)
chatCore #3501: extract recordCompressionCacheStats. Integrated into release/v3.8.35.
* refactor(chatCore): extrai writeCavemanOutputAnalytics (#3501) (#4794)
chatCore #3501: extract writeCavemanOutputAnalytics. Integrated into release/v3.8.35.
* refactor(chatCore): extrai scheduleQuotaShareConsumption (POST-hook não-streaming, #3501) (#4780)
chatCore #3501: extract scheduleQuotaShareConsumption (non-streaming POST-hook). Integrated into release/v3.8.35.
* refactor(chatCore): extrai emitRequestGamificationEvent (helper compartilhado DRY, #3501) (#4776)
chatCore #3501: extract emitRequestGamificationEvent (DRY streaming/non-streaming). Integrated into release/v3.8.35.
* refactor(chatCore): extrai runPluginOnResponseHook (#3501) (#4782)
chatCore #3501: extract runPluginOnResponseHook. Integrated into release/v3.8.35.
* refactor(chatCore): extrai scheduleStreamingQuotaShareConsumption (POST-hook streaming, #3501) (#4784)
chatCore #3501: extract scheduleStreamingQuotaShareConsumption (streaming POST-hook). Integrated into release/v3.8.35.
* refactor(chatCore): extrai recordStreamingUsageStats (analytics de usage streaming, #3501) (#4791)
chatCore #3501: extract recordStreamingUsageStats. Integrated into release/v3.8.35.
* refactor(chatCore): extrai recordStreamingCost (custo por-request streaming, #3501) (#4790)
chatCore #3501: extract recordStreamingCost (per-request streaming cost). Integrated into release/v3.8.35.
* docs(readme): credit ponytail + OmniCompress; restore env-doc-sync release-green (#4799)
README compression credits (ponytail/OmniCompress) + env-doc-sync ignore for eval-only OMNIROUTE_EVAL_CREDENTIALS (restores release-green after #4720). Integrated into release/v3.8.35.
* chore(quality): trim combo-config.test.ts comments under file-size cap (#4774 follow-up) (#4800)
Restore file-size release-green. Integrated into release/v3.8.35.
* feat(api-docs): Redoc-rendered /api/docs + consolidate OpenAPI spec to docs/openapi.yaml (#4781)
Redoc /api/docs + OpenAPI spec consolidated to docs/openapi.yaml (canonical 201-path complete spec; old path → legacy fallback). All refs/gates/tests/CI updated. Integrated into release/v3.8.35.
* docs(compression): declare Phase 4 layers — Output Styles, adaptive dial, per-request control (#4801)
The README compression section listed the 9 input engines but not the Phase 4
layers now in production:
- Output Styles (output-axis steering: terse-prose / less-code / terse-cjk, lite/full/ultra)
- adaptive context-budget dial (reserve-output|percentage|absolute · floor|replace-autotrigger|off)
- per-request x-omniroute-compression precedence + the offline eval harness
Also bumped the highlights range to v3.8.35, expanded the compression feature bullet,
and marked the GUIDE's Phase 4 row Shipped (was 'Planned' — it's merged on v3.8.35).
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* chore(release): finalize v3.8.35 CHANGELOG + docs reconciliation
- CHANGELOG: complete 3.8.35 section (all 35 commits since v3.8.34,
contributor attribution: @rdself @megamen32 @KooshaPari @JxnLexn)
- docs(security): align THREAT_MODEL.md refs with real code
(routeGuard.ts, tokenLimits.ts, /api/monitoring/health) — fabricated-docs gate
- check:fabricated-docs: skip docs/superpowers/specs (dated research reports)
- i18n: sync 3.8.35 section into 41 CHANGELOG mirrors (docs-sync size gate)
- ratchet rebaseline: cyclomatic 1916->1920, eslintWarnings 3907->3912
(inherited cycle drift; release-finalize diff is docs-only)
* fix(release): resolve inherited base-reds surfaced by v3.8.35 release CI
Cycle base-reds that only run on PR→main (not the PR→release fast-path):
- test(autoCombo): suffixComposition-4517 used node:test in a vitest-only dir
(#4753) → vitest found no suite. Switch to the vitest API. (Vitest job)
- test(agentSkills): openapiParser fixture wrote docs/reference/openapi.yaml;
parser reads docs/openapi.yaml since #4781 → point fixture at the new path.
(Unit/Coverage/Node24/Node26 shard 4)
- test(integration): proxy-pipeline source-scan expected inline streaming-cost
code that #4790/#3501 extracted to the recordStreamingCost leaf → assert the
delegation instead. (Integration 1/2)
- fix(chatCore): derive the log trace id from crypto, not Math.random
(CodeQL js/insecure-randomness — log-correlation id, not a secret).
- test(resilience): circuit-breaker invalid-cooldown fallback asserted t>29000,
flaking on slow CI where ~1.6s elapsed gave t=28401 → tolerate wall-clock
drift (t>25000). (Unit 6/8)
* fix(usage): derive pending-request id from crypto, not Math.random
CodeQL js/insecure-randomness (#669): the pending-request id generated in
trackPendingRequest (usageHistory.ts) flows into attempt logging and was flagged
as insecure randomness in a security context. It's a log-correlation id, not a
secret — switch to crypto RNG to clear the alert. Pairs with the chatCore traceId
fix in 37c49781a (same sink).
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <souzamiriamrodrigues790@gmail.com>
Co-authored-by: Randi <55005611+rdself@users.noreply.github.com>
Co-authored-by: Demiurge The Single <megamen932@gmail.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: Jan Leon <Jan.gaschler@gmail.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
319 lines
15 KiB
Markdown
319 lines
15 KiB
Markdown
---
|
|
title: "Release Checklist"
|
|
version: 3.8.2
|
|
lastUpdated: 2026-05-13
|
|
---
|
|
|
|
# Release Checklist
|
|
|
|
> **Last updated:** 2026-05-13 — v3.8.0
|
|
> Streamlined release flow that leverages Claude Code skills for automation.
|
|
|
|
## TL;DR
|
|
|
|
```bash
|
|
# 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-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.yaml` → `info.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 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: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.example` ↔ `docs/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:
|
|
```bash
|
|
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`,
|
|
`@tensorflow/tfjs`, `js-tiktoken`) survive an update. The ultra `modelPath` SLM tier
|
|
additionally needs `@huggingface/transformers@3.5.2` (pinned — llmlingua-2 uses the 3.x
|
|
tokenizer API) and 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 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 ≥75/75/75/70 (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:
|
|
|
|
```bash
|
|
npm run check:docs-sync
|
|
```
|
|
|
|
CI also runs this check in `.github/workflows/ci.yml` (lint job).
|