# Release Checklist (Bahasa Indonesia) 🌐 **Languages:** 🇺🇸 [English](../../../../ops/RELEASE_CHECKLIST.md) · 🇪🇹 [am](../../../am/docs/ops/RELEASE_CHECKLIST.md) · 🇸🇦 [ar](../../../ar/docs/ops/RELEASE_CHECKLIST.md) · 🇦🇿 [az](../../../az/docs/ops/RELEASE_CHECKLIST.md) · 🇧🇬 [bg](../../../bg/docs/ops/RELEASE_CHECKLIST.md) · 🇧🇩 [bn](../../../bn/docs/ops/RELEASE_CHECKLIST.md) · 🇧🇦 [bs](../../../bs/docs/ops/RELEASE_CHECKLIST.md) · 🇨🇿 [cs](../../../cs/docs/ops/RELEASE_CHECKLIST.md) · 🇩🇰 [da](../../../da/docs/ops/RELEASE_CHECKLIST.md) · 🇩🇪 [de](../../../de/docs/ops/RELEASE_CHECKLIST.md) · 🇬🇷 [el](../../../el/docs/ops/RELEASE_CHECKLIST.md) · 🇪🇸 [es](../../../es/docs/ops/RELEASE_CHECKLIST.md) · 🇪🇪 [et](../../../et/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇷 [fa](../../../fa/docs/ops/RELEASE_CHECKLIST.md) · 🇫🇮 [fi](../../../fi/docs/ops/RELEASE_CHECKLIST.md) · 🇫🇷 [fr](../../../fr/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇪 [ga](../../../ga/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [gu](../../../gu/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇬 [ha](../../../ha/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇱 [he](../../../he/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [hi](../../../hi/docs/ops/RELEASE_CHECKLIST.md) · 🇭🇷 [hr](../../../hr/docs/ops/RELEASE_CHECKLIST.md) · 🇭🇺 [hu](../../../hu/docs/ops/RELEASE_CHECKLIST.md) · 🇦🇲 [hy](../../../hy/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇬 [ig](../../../ig/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇹 [it](../../../it/docs/ops/RELEASE_CHECKLIST.md) · 🇯🇵 [ja](../../../ja/docs/ops/RELEASE_CHECKLIST.md) · 🇬🇪 [ka](../../../ka/docs/ops/RELEASE_CHECKLIST.md) · 🇰🇭 [km](../../../km/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [kn](../../../kn/docs/ops/RELEASE_CHECKLIST.md) · 🇰🇷 [ko](../../../ko/docs/ops/RELEASE_CHECKLIST.md) · 🇱🇹 [lt](../../../lt/docs/ops/RELEASE_CHECKLIST.md) · 🇱🇻 [lv](../../../lv/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [ml](../../../ml/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [mr](../../../mr/docs/ops/RELEASE_CHECKLIST.md) · 🇲🇾 [ms](../../../ms/docs/ops/RELEASE_CHECKLIST.md) · 🇲🇹 [mt](../../../mt/docs/ops/RELEASE_CHECKLIST.md) · 🇲🇲 [my](../../../my/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇵 [ne](../../../ne/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇱 [nl](../../../nl/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇴 [no](../../../no/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [or](../../../or/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [pa](../../../pa/docs/ops/RELEASE_CHECKLIST.md) · 🇵🇭 [phi](../../../phi/docs/ops/RELEASE_CHECKLIST.md) · 🇵🇱 [pl](../../../pl/docs/ops/RELEASE_CHECKLIST.md) · 🇵🇹 [pt](../../../pt/docs/ops/RELEASE_CHECKLIST.md) · 🇧🇷 [pt-BR](../../../pt-BR/docs/ops/RELEASE_CHECKLIST.md) · 🇷🇴 [ro](../../../ro/docs/ops/RELEASE_CHECKLIST.md) · 🇷🇺 [ru](../../../ru/docs/ops/RELEASE_CHECKLIST.md) · 🇱🇰 [si](../../../si/docs/ops/RELEASE_CHECKLIST.md) · 🇸🇰 [sk](../../../sk/docs/ops/RELEASE_CHECKLIST.md) · 🇸🇮 [sl](../../../sl/docs/ops/RELEASE_CHECKLIST.md) · 🇷🇸 [sr](../../../sr/docs/ops/RELEASE_CHECKLIST.md) · 🇸🇪 [sv](../../../sv/docs/ops/RELEASE_CHECKLIST.md) · 🇰🇪 [sw](../../../sw/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [ta](../../../ta/docs/ops/RELEASE_CHECKLIST.md) · 🇮🇳 [te](../../../te/docs/ops/RELEASE_CHECKLIST.md) · 🇹🇭 [th](../../../th/docs/ops/RELEASE_CHECKLIST.md) · 🇹🇷 [tr](../../../tr/docs/ops/RELEASE_CHECKLIST.md) · 🇺🇦 [uk-UA](../../../uk-UA/docs/ops/RELEASE_CHECKLIST.md) · 🇵🇰 [ur](../../../ur/docs/ops/RELEASE_CHECKLIST.md) · 🇺🇿 [uz](../../../uz/docs/ops/RELEASE_CHECKLIST.md) · 🇻🇳 [vi](../../../vi/docs/ops/RELEASE_CHECKLIST.md) · 🇳🇬 [yo](../../../yo/docs/ops/RELEASE_CHECKLIST.md) · 🇨🇳 [zh-CN](../../../zh-CN/docs/ops/RELEASE_CHECKLIST.md) · 🇹🇼 [zh-TW](../../../zh-TW/docs/ops/RELEASE_CHECKLIST.md) --- > **Terakhir diperbarui:** 2026-08-28 — v3.8.51 > Alur rilis yang disederhanakan dengan memanfaatkan skill Claude Code untuk otomatisasi. > > **Jaga agar antrean/cabang tetap hijau di antara rilis:** lihat [RELEASE_GREEN.md](./RELEASE_GREEN.md) > (keluarga `/green-prs` + `npm run check:release-green` + `/babysit` + proses setiap malam). Menjalankan > ini secara berkala — dan terutama **sebelum** daftar periksa ini — membuat PR rilis dimulai dalam kondisi hijau. ## Ringkasan ```bash # 1. Naikkan versi + buat CHANGELOG (skill) /version-bump-cc patch # atau minor/major # 2. Jalankan gerbang kualitas secara lokal npm run check # lint + pengujian npm run test:coverage # gerbang cakupan penuh (60/60/60/60) # 3. Build & uji cepat npm run build npm run test:e2e # opsional tetapi disarankan # 4. Buat rilis (skill) /generate-release-cc # 5. Deploy (skill) /deploy-vps-both-cc # atau akamai-cc / local-cc # 6. Ambil bukti rilis (skill) /capture-release-evidences-cc ``` ## npm Trusted Publishing (default sejak v3.8.51) — bertahap berdasarkan permintaan, langsung sebagai fallback `npm-publish.yml` melakukan publikasi melalui **npm Trusted Publishing (OIDC)** secara default: job `stage-npm` (yang di-host GitHub) menukarkan id-token GitHub dengan kredensial npm berumur pendek untuk proses tersebut — tanpa token npm berumur panjang dalam secret repositori, tanpa permintaan 2FA, serta dengan provenance terlampir. Itulah mekanisme bypass yang kini diizinkan npm karena token yang melewati 2FA sedang dihentikan; mekanisme ini memulihkan alur yang sepenuhnya otomatis seperti yang dimiliki proyek hingga v3.8.48 sekaligus mempertahankan jaminan WS1.3 (token yang bocor tidak dapat melakukan publikasi sendirian — karena memang tidak ada token). **Penyiapan satu kali (pemilik):** npmjs.com → paket `omniroute` → Pengaturan → _Trusted Publisher_ → GitHub: pemilik `diegosouzapw`, repo `OmniRoute`, workflow `npm-publish.yml` (environment: none). Hingga konfigurasi tersebut tersedia, langkah otomatis akan gagal dengan `ENEEDAUTH`: jalankan ulang dengan `publish_mode=staged` (di bawah) atau `direct`. ### Publikasi bertahap (berdasarkan permintaan — `publish_mode=staged`) Workflow npm-publish tidak lagi melakukan publikasi secara langsung: workflow tersebut menjalankan tarball yang telah dikemas (`check:pack-boot`), lalu menjalankan `npm stage publish` — byte yang sama persis ditempatkan di registry, tetapi **tidak dapat diinstal** hingga pemilik menyetujuinya. Gerbang 2FA manusia dipindahkan ke SETELAH pembuktian, bukan sebelumnya. **Alur pemilik setelah workflow berstatus hijau:** 1. `npm stage list omniroute` — temukan ID tahap (juga dicetak dalam ringkasan workflow). 2. Verifikasi byte yang ditahapkan (disarankan): `npm stage download `, lalu instal tarball yang diunduh ke prefix sementara dan jalankan (`npm run check:pack-boot` mengotomatiskan keputusan pack→install→boot yang sama di CI). 3. `npm stage approve ` — permintaan 2FA ADALAH proses publikasi. `npm stage reject ` akan membuangnya. 4. Pengaman pascapublikasi: pemverifikasi pascapublikasi (WS1.4 dari rencana v3.8.49) menginstal versi yang dipublikasikan dari registry publik dalam container bersih, lalu menjalankannya. **Fallback darurat:** `workflow_dispatch` dengan `publish_mode=direct` memulihkan `npm publish` langsung versi lama (gunakan hanya jika proses bertahap itu sendiri bermasalah; catat alasannya). **Penguatan satu kali (pemilik, npmjs.com):** konfigurasikan Trusted Publisher untuk `omniroute` dalam mode khusus tahap agar token berumur panjang yang bocor tidak dapat menjalankan `npm publish` secara langsung dari mana pun — CI hanya dapat menahapkan; hanya 2FA pemilik yang dapat merilis. **Panduan artefak rusak (tidak berubah):** `npm deprecate omniroute@ " — use "` sebagai respons default (beberapa menit, dapat dibatalkan); gunakan `npm unpublish` hanya dalam jendela 72 jam/tanpa dependensi dan jangan pernah sebagai langkah pertama. Docker: jangan pernah menimpa tag versi — rollback dilakukan dengan mengarahkan ulang `latest` ke digest terakhir yang berfungsi dengan baik. **Docker Hub `latest` (wajib pada setiap publikasi SemVer stabil):** workflow `docker-publish` harus memberi tag **keduanya**, yaitu `X.Y.Z` dan, ketika `should-promote-latest.sh` menyatakan bahwa versi tersebut adalah SemVer stabil tertinggi, `:latest` dengan **digest yang sama**. Setelah job selesai: digest `latest` di Hub harus sama dengan digest SemVer baru dan `last_updated` harus berubah. Jangan biarkan `:latest` tetap mengarah ke build lama sementara catatan rilis membahas perbaikan yang hanya tersedia di git. Quickstart Compose menggunakan `:latest`; GitOps harus tetap menyematkan `X.Y.Z`. Lihat [Saluran rilis Docker](../guides/DOCKER_GUIDE.md#release-channels) dan #10317. ## Jalur Cepat Hotfix (label `hotfix`) PR berlabel `hotfix` melewati matriks CI yang berat (E2E 9 shard, peningkatan batas coverage, quality-gate, quality-extended) dan mempertahankan pemeriksaan cepat dengan sinyal tinggi: build, shard unit, integrasi, vitest, lint/typecheck, docs-sync, `check:pack-artifact`, serta pengujian awal boot untuk tarball (`check:pack-boot`). Target: berhasil dalam ≤15 menit, bukan ~33 menit. **Kebijakan masuk — keempatnya wajib dipenuhi (dimodelkan berdasarkan jalur darurat Chromium/VS Code/Node):** 1. **Tingkat keparahan**: produksi rusak — artefak yang dipublikasikan mengalami crash saat boot / perbaikan keamanan / setiap pengguna rilis terdampak. "Penting" bukan berarti "rusak". 2. **Wewenang**: hanya pemilik repositori yang menerapkan label `hotfix`. Label tersebut ADALAH persetujuannya — jangan pernah menerapkannya sendiri pada PR kampanye. 3. **Bukti**: isi PR menautkan proses berat sebelumnya yang sepenuhnya berhasil (suite yang akan divalidasi ulang oleh job yang dilewati), ditambah pengujian milik perbaikan tersebut yang awalnya gagal lalu berhasil. 4. **Cakupan**: hanya cherry-pick — perbaikan minimal, tanpa refactor, tanpa perubahan tambahan yang menumpang. Cakupan coverage/ratchet yang dilewati divalidasi ulang oleh proses penuh berikutnya pada branch rilis (rilis hijau berkelanjutan) — jalur ini hanya melewati WAKTU TUNGGU, bukan validasi. Diff khusus pengujian (semua file di bawah `tests/`, tidak ada yang di bawah `tests/e2e/`) melewati matriks E2E secara otomatis, tanpa label apa pun. ## Daftar Periksa Terperinci ### Pra-rilis - [ ] Semua PR yang ditargetkan untuk rilis ini telah digabungkan ke `release/vX.Y.0` - [ ] Semua item Linear/isu yang masih terbuka untuk versi ini telah ditutup atau dipindahkan ke milestone berikutnya - [ ] CI berhasil pada cabang `release/vX.Y.0` - [ ] Tidak ada penanda `TODO(release)` dalam kode: `grep -r "TODO(release)" src/ open-sse/` - [ ] Image dasar Docker sudah terbaru (saat ini `node:24.15.0-trixie-slim`) ### Versi & Log Perubahan - [ ] Jalankan `/version-bump-cc ` (skill Claude Code) - Menaikkan versi `package.json`, `electron/package.json` - Membuat ulang `CHANGELOG.md` dari commit git sejak tag terakhir - Memperbarui badge README.md - [ ] Tinjau CHANGELOG.md secara manual dan rapikan pesan commit jika diperlukan - [ ] Pastikan bagian semver terbaru di `CHANGELOG.md` sama dengan versi `package.json` - [ ] Pertahankan `## [Unreleased]` sebagai bagian log perubahan pertama untuk pekerjaan mendatang - [ ] Perbarui `docs/openapi.yaml` → `info.version` harus sama dengan versi `package.json` ### Kualitas Kode - [ ] `npm run lint` — 0 galat (peringatan sudah ada sebelumnya) - [ ] `npm run typecheck:core` — bersih - [ ] `npm run typecheck:noimplicit:core` — bersih (ketat) - [ ] `npm run check:cycles:ratchet` — berada dalam batas maksimum `metrics.cycles` yang dibekukan (`check:cycles` biasa keluar dengan kode 1 jika ada siklus apa pun, #15281) - [ ] `npm run check:any-budget:t11` — berada dalam anggaran - [ ] `npm run check:route-validation:t06` — bersih - [ ] `npm run check:node-runtime` — batas minimum runtime yang didukung terpenuhi (`>=22.22.2 <23`, `>=24.0.0 <27`, sesuai `SUPPORTED_NODE_RANGE` di `src/shared/utils/nodeRuntimeSupport.ts`; selaras dengan `engines` di `package.json`) ### Pengujian - [ ] `npm run test:unit` — lulus - [ ] `npm run test:vitest` — lulus (server MCP, autoCombo, cache) - [ ] `npm run test:coverage` — ambang 60/60/60/60 terpenuhi (pernyataan/baris/fungsi/cabang) - [ ] `npm run test:integration` — lulus (jika perubahan menyentuh DB / handler) - [ ] `npm run test:combo:matrix` — lulus (matriks strategi combo: membuktikan keputusan pemilihan seluruh 19 strategi perutean publik secara deterministik; jalankan saat menyentuh perutean combo, resolusi strategi, atau logika fallback) - [ ] `RUN_COMBO_LIVE=1 npm run test:combo:live` — **opsional/manual** (smoke test real-upstream yang dilindungi gate; mengambil snapshot DB hanya-baca dari VPS `root@192.168.0.15`; mengakses penyedia nyata dan menggunakan kredit; tidak pernah berjalan di CI; dilewati secara bersih tanpa gate) - [ ] `npm run test:combo:live:vps` — **opsional/manual** (smoke test langsung VPS Fase-3: 7 skenario HTTP terhadap server `.15` yang aktif melalui Node ESM biasa; memerlukan `ssh root@192.168.0.15`; hanya membuat/menghapus combo `__live_test__*`; mengakses penyedia nyata; tidak pernah berjalan di CI) - [ ] `npm run test:e2e` — lulus (perubahan UI) - [ ] `npm run test:protocols:e2e` — lulus (perubahan MCP/A2A) - [ ] `npm run test:ecosystem` — lulus ### Hook (Divalidasi Husky) Hook Husky berada di `.husky/` dan berjalan secara otomatis pada operasi git. - **pre-commit:** `npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11` - **pre-push:** gate deterministik yang cepat — `npm run check:any-budget:t11 && npm run check:tracked-artifacts` (diaktifkan 2026-06-13). Sengaja tidak menyertakan `test:unit` (lambat; dicakup oleh job CI `test-unit`). - Jalankan `npm run test:unit` secara manual sebelum mendorong cabang rilis. Jika hook gagal: perbaiki masalah yang mendasarinya, jangan lewati dengan `--no-verify`. ### Conventional Commits Semua commit yang ditujukan untuk rilis harus mengikuti format `type(scope): subject`. **Tipe yang valid:** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `style`, `ci` **Cakupan yang valid:** `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`, `a2a`, `memory`, `skills`, `cloud-agent`, `guardrails`, `compression`, `auto-combo`, `resilience`, `providers`, `executors`, `translator`, `domain`, `authz` Perubahan yang merusak kompatibilitas: tambahkan footer `BREAKING CHANGE:` atau `!` setelah cakupan (misalnya `feat(api)!: drop /v0`). ### Dokumentasi - [ ] `npm run check:docs-sync` lulus (dijalankan otomatis oleh pre-commit) - [ ] `npm run check:docs-all` lulus (payung: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links) - [ ] `npm run check:env-doc-sync` keluar dengan kode 0 — kontrak env antara kode ↔ `.env.example` ↔ `docs/reference/ENVIRONMENT.md` tetap konsisten - [ ] `npm run check:doc-links` keluar dengan kode 0 — tidak ada referensi markdown internal yang rusak setelah restrukturisasi - [ ] `docs/architecture/ARCHITECTURE.md` telah ditinjau untuk penyimpangan penyimpanan/runtime - [ ] `docs/guides/TROUBLESHOOTING.md` telah ditinjau untuk penyimpangan variabel env dan operasional - [ ] Jika `.env.example` berubah: `docs/reference/ENVIRONMENT.md` diperbarui - [ ] Jika fitur baru memiliki UI: `docs/guides/USER_GUIDE.md` menyebutkannya - [ ] Jika fitur baru memiliki API: `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` diperbarui - [ ] Jika fitur baru merupakan modul: tersedia `docs/.md` khusus - [ ] Jika terdapat perubahan yang merusak kompatibilitas: `docs/guides/TROUBLESHOOTING.md` memiliki catatan migrasi ### i18n - [ ] `npm run i18n:check` keluar dengan kode 0 — status terjemahan (`.i18n-state.json`) sinkron dengan dokumen sumber (tidak ada sumber yang menyimpang dalam mode ketat; pemberitahuan dalam mode peringatan dapat diterima untuk perbaikan dokumen pada saat-saat terakhir, tetapi harus bernilai 0 sebelum membuat tag) - [ ] `npm run i18n:check-ui-coverage` keluar dengan kode 0 — setiap locale UI berada pada atau di atas batas minimum cakupan 80% - [ ] `npm run i18n:sync-ui:dry` melaporkan 0 kunci yang hilang di seluruh 66 locale target (setiap locale kecuali sumber `en`) - [ ] Jika dokumen sumber berbahasa Inggris berubah, jalankan `npm run i18n:run` (memerlukan `OMNIROUTE_TRANSLATION_API_KEY` di `.env`) sebelum membuat tag - [ ] Kontribusi terjemahan dapat ditunda ke rilis berikutnya jika bersifat minor (catat di CHANGELOG) ### Migrasi Database - [ ] Jika `src/lib/db/migrations/` memiliki file baru: - [ ] Setiap migrasi bersifat idempoten (`CREATE TABLE IF NOT EXISTS`, dll.) - [ ] Migrasi dibungkus dalam transaksi - [ ] Dinomori dengan benar (tidak ada celah dalam urutan) - [ ] Uji pada instalasi baru: hapus `~/.omniroute/omniroute.db` dan jalankan `npm run dev` - [ ] Uji pada instalasi yang sudah ada: cadangkan DB, jalankan migrasi, verifikasi skema - [ ] File WAL (`-wal`, `-shm`) ditangani dengan benar jika migrasi menulis ulang tabel ### Katalog Penyedia (divalidasi Zod) - [ ] Skema Zod `src/shared/constants/providers.ts` valid saat dimuat - [ ] Semua penyedia memiliki kolom wajib (`id`, `label`, `kind`, dll.) - [ ] `freeNote` disediakan untuk penyedia gratis baru - [ ] Penyedia OAuth memiliki `oauthConfig` yang terdaftar di `src/lib/oauth/constants/oauth.ts` - [ ] Jika penyedia baru ditambahkan: tersedia eksekutor yang sesuai di `open-sse/executors/` - [ ] Jika formatnya bukan OpenAI: tersedia penerjemah di `open-sse/translator/` - [ ] Model terdaftar di `open-sse/config/providerRegistry.ts` - [ ] Pengujian unit di `tests/unit/` mencakup klasifikasi dan perutean penyedia ### Desktop (Electron) Jika `electron/` berubah: - [ ] `npm run electron:smoke:packaged` berhasil - [ ] Build diuji untuk setidaknya salah satu dari `:win`, `:mac`, `:linux` - [ ] Sertifikat penandatanganan kode belum kedaluwarsa (jika melakukan penandatanganan) - [ ] Versi `electron/package.json` sama dengan `package.json` root - [ ] Pointer kanal pembaruan otomatis diperbarui jika merilis ke `stable` ### Tata Letak Build Repositori menggunakan tiga direktori output yang berbeda — jangan pernah tertukar: | Direktori | Tujuan | Dilacak? | | --------- | ------------------------------------------------------------------- | --------------------- | | `src/` | Sumber aplikasi (TypeScript / TSX) | Ya | | `.build/` | Perantara build — output `next build` (`distDir`) | Tidak (diabaikan git) | | `dist/` | Bundel npm siap didistribusikan — dirakit oleh `assembleStandalone` | Tidak (diabaikan git) | > **Catatan operator:** direktori image VPS jarak jauh tetap `/usr/lib/node_modules/omniroute/app/`. > Hanya output build **dalam repositori** yang dipindahkan (`app/` → `dist/`). Skill deployment melakukan rsync > terhadap isi `dist/` ke direktori `app/` jarak jauh — tidak diperlukan perubahan jalur VPS. **Alur build tunggal:** ``` npm run build:release └─ rm -rf .build dist (pembersihan) └─ next build → .build/next/ (perantara) └─ assembleStandalone (menyalin standalone + static + public + native → dist/) └─ writes dist/BUILD_SHA (sentinel HEAD) ``` JANGAN jalankan `npm run build` yang diikuti dengan `npm run build:cli` terpisah untuk deployment — gunakan `npm run build:release` yang melakukan build ulang bersih + sentinel dalam satu perintah. ### Validasi Artefak - [ ] `npm run build:release` berhasil dan `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] `npm run check:pack-artifact` bersih — tidak ada `app.__qa_backup`, `scripts/scratch`, `package-lock.json`, atau residu lokal lainnya - [ ] `dist/server.js` tersedia setelah build - [ ] Smoke test runtime terpaket lokal opsional: `npm run dev:candidate -- validate` setelah `npm run dev:candidate -- build` menjalankan tarball terpaket pada `DATA_DIR` terisolasi dan memeriksa `/api/health` + `/v1/models` (lihat [Alur Utama Kontribusi](CONTRIBUTION_GOLDEN_PATH.md#local-candidate-loop)) ### Pemberian Tag & Rilis - [ ] Jalankan `/generate-release-cc` (skill Claude Code): - Membuat tag `vX.Y.Z` - Melakukan push tag dan branch - Membuka GitHub Release dengan isi changelog - Melampirkan installer Electron (jika dibuat) - [ ] Atau secara manual: ```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 ``` ### Deployment Skill deployment menggunakan alur rsync ringan — tanpa `npm pack`, tanpa `npm i -g`: - [ ] Gunakan skill deployment yang sesuai dengan target: - `/deploy-vps-local-cc` — VPS lokal (192.168.0.15) - `/deploy-vps-akamai-cc` — VPS Akamai (69.164.221.35) - `/deploy-vps-both-cc` — keduanya - [ ] Sebelum melakukan deployment, pastikan `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] Build harus dijalankan di tempat `node_modules` benar-benar tersedia (checkout utama atau worktree yang telah menjalankan `npm ci` — BUKAN worktree dengan symlink) - [ ] Lakukan smoke test terhadap instance yang telah di-deploy: - Buka `/dashboard/health` → periksa apakah string versi sesuai dengan rilis - Jalankan permintaan `/v1/chat/completions` terhadap penyedia yang sudah diketahui - Verifikasi bahwa `/api/monitoring/health` mengembalikan circuit breaker `CLOSED` - Pastikan transport MCP merespons (`/mcp` HTTP, `/mcp-sse` SSE) ### Setelah Rilis - [ ] Jalankan `/capture-release-evidences-cc` (skill Claude Code) - Mengambil tangkapan layar/rekaman WebP untuk fitur baru - Melampirkannya ke catatan rilis / postingan blog - [ ] Perbarui GitHub Discussions / Discord dengan pengumuman rilis - [ ] Buka milestone untuk versi berikutnya - [ ] Jika kritis: sematkan diskusi atau posting di `news.json` untuk banner dalam aplikasi ### Gerbang peluncuran publik Radar Pengumuman Radar sengaja di-commit dengan `active: false`. Aktivasi merupakan perubahan terpisah setelah bukti untuk setiap item di bawah ini tersedia: - [ ] Semua PR Radar bertumpuk telah di-merge dan CI pada ujung rilis berstatus hijau - [ ] Deploy dan lakukan smoke test terhadap route Radar OSS dengan `RADAR_ENABLED` tetap nonaktif secara default - [ ] Lakukan smoke test terhadap `GET /planos`, `/termos`, `/privacidade`, dan `/reembolso` pada host Radar yang telah ditentukan - [ ] Catat identitas/kontak/alamat operator serta tinjauan hukum yang disetujui pemilik di layanan privat - [ ] Uji Stripe Checkout dan webhook bertanda tangan hanya dalam mode pengujian - [ ] Uji satu pengiriman email transaksional terenkripsi menggunakan pengirim/domain yang disetujui - [ ] Buktikan pemulihan cadangan dan satu proses riset yang diawasi serta dibatasi anggaran - [ ] Setujui kebijakan peninjauan BRL/PIX sebelum menerima bukti donasi - [ ] Aktifkan Checkout publik hanya setelah gerbang sebelumnya terpenuhi, lalu aktifkan ID `news.json` baru - [ ] Verifikasi bahwa banner Home menggunakan salinan yang dilokalkan dan ID baru muncul kembali setelah ID lama ditutup ## Uji asap Embedded Services (v3.8.4+) Sebelum merilis versi apa pun yang mencakup perubahan pada embedded services, verifikasi: ### Boot dengan DB baru (mendeteksi benturan migrasi — ditambahkan setelah hotfix v3.8.4) - [ ] `DATA_DIR=$(mktemp -d) npm start &` — tunggu 10 detik hingga boot selesai - [ ] `curl -s http://127.0.0.1:20128/api/services/9router/status | jq '.tool'` mengembalikan `"9router"` (BUKAN 404, BUKAN 500). Mengonfirmasi bahwa migrasi `071_services.sql` diterapkan + baris awal dimasukkan. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(version_manager);" | grep -E "provider_expose|logs_buffer_path|last_sync_at"` mengembalikan 3 baris. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(webhooks);" | grep -E "kind|metadata_encrypted"` mengembalikan 2 baris (memvalidasi bahwa `070_webhooks_kind_metadata.sql` diterapkan). - [ ] `node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.ts` berhasil — mencegah benturan pada masa mendatang. ### 9Router - [ ] `POST /api/services/9router/install` mengembalikan 200 dengan `installedVersion` dalam waktu kurang dari 2 menit - [ ] `POST /api/services/9router/start` mengembalikan 200 dan `state: "running"` dalam waktu kurang dari 30 detik - [ ] `GET /api/services/9router/status` melaporkan `health: "healthy"` - [ ] `POST /v1/chat/completions` dengan `"model": "9router/auto/..."` mengembalikan 200 (perutean menyeluruh melalui 9Router) - [ ] `GET /dashboard/providers/services/9router/embed/dashboard` merender UI native 9Router di dalam proksi (tanpa iframe langsung ke `127.0.0.1:port`) - [ ] `POST /api/services/9router/rotate-key` mengembalikan `{ keyRotated: true }` dan layanan dimulai ulang dengan bersih - [ ] `POST /api/services/9router/stop` mengembalikan 200 dan `state: "stopped"` - [ ] `GET /api/services/9router/logs?tail=50` mengembalikan aliran SSE dengan peristiwa `snapshot` yang berisi baris-baris terbaru - [ ] Instalasi di lingkungan tanpa `npm` dalam PATH mengembalikan 500 dengan pesan kesalahan yang ramah (bukan stack trace) ### CLIProxyAPI - [ ] `POST /api/services/cliproxy/install` mengembalikan 200 dalam waktu kurang dari 2 menit - [ ] `POST /api/services/cliproxy/start` mengembalikan 200 dan `state: "running"` dalam waktu kurang dari 30 detik - [ ] `GET /api/services/cliproxy/status` melaporkan `health: "healthy"` - [ ] `POST /api/services/cliproxy/stop` mengembalikan 200 dan `state: "stopped"` - [ ] `GET /api/services/cliproxy/logs?tail=50` mengembalikan aliran SSE ### Regresi keamanan - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/9router/start` mengembalikan `403 LOCAL_ONLY` - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/cliproxy/start` mengembalikan `403 LOCAL_ONLY` - [ ] Respons kesalahan dari `/api/services/*` tidak memuat `err.stack` atau path file absolut ## Pemeriksaan v3.8.0+ Sebelum merilis versi v3.8.x apa pun, verifikasi item-item tambahan berikut: - [ ] `omniroute --tray` berhasil boot di macOS (systray2 diinstal ke `~/.omniroute/runtime/`) - [ ] `omniroute --tray` berhasil boot di Linux (memerlukan DISPLAY; menampilkan kesalahan yang baik jika tidak disetel) - [ ] `omniroute --tray` berhasil boot di Windows (PowerShell NotifyIcon, tanpa biner tambahan) - [ ] `omniroute config tray enable` membuat entri autostart; penonaktifan menghapusnya - [ ] `npm install -g omniroute@` menjalankan postinstall tanpa keluar secara fatal - [ ] Jalur pembaruan mempertahankan dependensi opsional: `omniroute update --apply` dan auto-updater menjalankan `npm install -g … --include=optional` agar `optionalDependencies` (better-sqlite3, keytar, tls-client, dan stack SLM llmlingua: `@atjsh/llmlingua-2@2.0.5`, `js-tiktoken`) tetap tersedia setelah pembaruan. Tingkat SLM ultra `modelPath` juga memerlukan model tinybert, yang diunduh otomatis ke `${DATA_DIR}/models/llmlingua` saat pertama kali digunakan. Postinstall (`scripts/build/colocateOptionals.mjs`) kemudian menempatkan closure opsional SLM bersama-sama di `dist/node_modules` sehingga worker me-resolve SATU instans `@huggingface/transformers` ^4.2.0 — trace mandiri hanya membundel transformers, bukan dependensi opsional yang diimpor secara dinamis, sehingga tanpa ini worker akan memuat llmlingua-2 menggunakan transformers milik root dan tingkat SLM akan diam-diam beralih ke mode fail-open. - [ ] `omniroute status` berfungsi tanpa `.env` (jalur token CLI, hanya loopback) - [ ] `curl http://localhost:20128/api/shutdown` mengembalikan 401 (rute yang selalu dilindungi) - [ ] `curl -H "host: evil.com" http://localhost:20128/api/mcp/sse` mengembalikan 401 (perlindungan loopback) - [ ] Runtime SQLite me-resolve ke `bundled` saat pertama kali dijalankan (biner yang dibundel valid untuk platform) - [ ] Runtime SQLite beralih ke `runtime` saat `node_modules/better-sqlite3` dihapus - [ ] Filter Smart MCP mengompresi output nyata `playwright-mcp browser_snapshot` (pengurangan ≥50%) - [ ] Semua 10 file `skills/omniroute*/SKILL.md` dapat diakses publik melalui URL raw GitHub - [ ] Wizard onboarding menampilkan langkah tur tingkat "How It Works" pada penyiapan baru - [ ] Widget cakupan tingkat di dasbor beranda menampilkan jumlah yang dikonfigurasi/aktif --- ## Pemotongan 3.9.0 LTS (direhearsalkan di 3.8.58) Setelah v3.8.59, versi berikutnya adalah 3.9.0, dan tip-nya menjadi dua cabang berumur panjang: `stable/v3` (jalur v3 LTS, npm `latest`) dan `develop` (v4, dinaikkan ke 4.0.0, npm `nightly`). Model cabang/channel, forward-port, dan label tersedia di [RELEASE_STRATEGY.md](./RELEASE_STRATEGY.md); rencananya tersedia di [ROADMAP](../../ROADMAP.md) (Fase 3). Pemotongan dijalankan satu kali; 3.8.58 merehearsalkannya dari awal hingga akhir pada sebuah fork, dan 3.8.59 ditutup dengan [daftar periksa GO/NO-GO](./LTS_GO_NO_GO.md). ### Dry-run (hanya baca, aman kapan saja) ```bash npm run release:dry-run-lts-cut # pemotongan sebenarnya: 3.9.0 dari HEAD, tag sebelumnya v3.8.59 npm run release:dry-run-lts-cut -- --from <3.9.0-tip> # sematkan commit sumber ``` `scripts/release/dry-run-lts-cut.mjs` tidak mengeksekusi apa pun: skrip ini membaca git dan `gh`, lalu mencetak seluruh urutan — prasyarat (sumber dapat di-resolve, repositori sumber memiliki riwayat lengkap — shallow clone tidak dapat melakukan push ke `stable/v3`, sisi penerima menolak pack pada parent hasil graft pertama, jadi lakukan pemotongan dari clone lengkap atau jalankan `git fetch --unshallow origin` terlebih dahulu — tag sebelumnya tersedia, `package.json` berisi versi target, issue `release-freeze` sedang terbuka, tidak ada issue `Release branch not green` yang terbuka pada cabang rilis yang ada — cabang yang tidak ada melaporkan `?` unknown, tidak pernah green — antrean `release` Mergify telah dikonfigurasi (G11: `queue_rules`, `checks_timeout`, label `queue`), ruleset `release/*` masih memblokir penghapusan dan force-push, serta `stable/v3` dan `develop` belum tersedia), kedua langkah cabang, trigger workflow dorman dan kondisi `if:` mana yang menjadi true (serta mana yang tetap dibatasi oleh variabel repositori atau disematkan ke repositori kanonis), dist-tag yang diharapkan (`latest` → 3.9.0, `next` dan `nightly` kosong), serta rollback. Exit `0` = `RESULT: READY`, `1` = prasyarat pemblokir gagal (`✗`), `2` = kesalahan penggunaan. `--advisory ` menurunkan pemeriksaan menjadi peringatan (`!`) tanpa menyembunyikannya. Jalankan dry-run pemotongan sebenarnya selagi release freeze 3.9.0 masih terbuka — cabang dibuat setelah tag dan sebelum Fase 12c mencabut freeze. ### Rehearsal 3.8.58 (khusus fork) ```bash # 1. Jalankan dry-run pada tip saat ini dengan parameter rehearsal npm run release:dry-run-lts-cut -- --target-version 3.8.58 --previous-tag v3.8.57 \ --advisory freeze,base-green # 2. Eksekusi terhadap remote FORK (origin, atau remote apa pun yang URL-nya merupakan # repositori kanonis, akan ditolak; setiap langkah meminta konfirmasi di terminal) git remote add rehearsal https://github.com//OmniRoute.git node scripts/release/dry-run-lts-cut.mjs --execute --remote rehearsal \ --target-version 3.8.58 --previous-tag v3.8.57 --advisory freeze,base-green # 3. Uji workflow dorman di fork (workflow_dispatch jika dry-run # melaporkan penyematan repositori kanonis), lalu lakukan rollback node scripts/release/dry-run-lts-cut.mjs --execute --rollback --remote rehearsal \ --target-version 3.8.58 --previous-tag v3.8.57 --advisory freeze,base-green ``` Commit bump develop dibuat dengan git plumbing (working tree tidak disentuh) dan menaikkan lima file yang sama seperti commit cycle-open: `package.json`, `open-sse/package.json`, `electron/package.json`, `package-lock.json`, dan `docs/openapi.yaml`. Bagian `[4.0.0]` CHANGELOG beserta mirror i18n-nya kemudian dibuka di `develop`, sebelum PR pertamanya. Skrip tidak pernah mengubah npm dist-tag — lakukan rehearsal terhadapnya pada package sementara. ### Artefak pratinjau PR (build sekali, promosikan byte yang sama) `.github/workflows/preview-artifact.yml` membuat satu tarball produksi dari head PR dan memvalidasi build yang sama persis (#8084 bagian (a)). Hanya untuk PR dari repositori yang sama; tidak ada yang dipublikasikan. ```bash gh workflow run preview-artifact.yml -f pr_number= # atau tambahkan label `preview-artifact` gh run download --name preview-artifact-pr- --dir preview cd preview && sha256sum -c SHA256SUMS gh attestation verify omniroute-*.tgz --repo diegosouzapw/OmniRoute npm install -g ./omniroute-*.tgz # instalasi pratinjau ``` Proses tersebut menjalankan `npm ci`, `npm run build:release`, `npm run check:pack-artifact`, mengemas tarball, menjalankan `npm run check:pack-boot` (secret palsu, direktori data sementara), mengemas ulang, dan gagal kecuali digest-nya identik, kemudian mencatat `artifact-identity.json` (head SHA, base SHA, hash lockfile, platform, arch, node ABI, bundler, kebijakan build — `scripts/release/artifact-identity.mjs`) serta mengatestasi tarball dalam job terpisah. Mempromosikan pratinjau berarti menginstal tarball tersebut: jangan pernah melakukan build ulang dari sumber. ### Pemotongan (3.9.0, setelah GO) 1. GO dicatat di [LTS_GO_NO_GO.md](./LTS_GO_NO_GO.md). 2. `npm run release:dry-run-lts-cut -- --from v3.9.0` mencetak `RESULT: READY`. 3. Buat cabang di `origin` secara manual menggunakan perintah yang dicetak oleh dry-run — skrip menolak melakukan push ke `origin`. Untuk menggunakan kembali commit develop yang telah ditinjau, jalankan rehearsal `--execute` pada tip 3.9.0 terhadap fork Anda terlebih dahulu; perintah tersebut mencetak kedua SHA, dan commit yang sama dapat di-push: ```bash git push origin :refs/heads/stable/v3 :refs/heads/develop ``` 4. Lindungi `stable/v3` dan `develop` (ruleset + antrean penggabungan) sebelum PR pertama masuk. 5. Workflow yang tidak aktif akan diaktifkan berdasarkan keberadaan cabang: `forward-port.yml` (push ke `stable/v3`), `validate-stable-pr.yml` (PR ke `stable/v3`), dan `nightly-v4-build.yml` (membangun `develop`). Sebelum peluncuran, atur secret repositori `secrets.FORWARD_PORT_TOKEN` (agar CI berjalan pada PR forward-port); publikasi nightly tetap dinonaktifkan hingga pemilik mengatur variabel repositori `vars.NIGHTLY_PUBLISH` menjadi `true` dan npm Trusted Publishing menerima `nightly-v4-build.yml`. Resolusi kanal dilakukan oleh `scripts/release/dist-tag.mjs`, resolver yang sama dengan yang digunakan `npm-publish.yml`. 6. Verifikasi kanal: `npm view omniroute dist-tags --json` menampilkan `latest` = 3.9.0 dan tidak ada `next` / `nightly` hingga v4 dipublikasikan. 7. Rollback, jika diperlukan: `git push origin --delete refs/heads/stable/v3 refs/heads/develop` dan `npm dist-tag add omniroute@3.8.59 latest`. --- ## Rollback Jika rilis memiliki masalah kritis: 1. `gh release edit vX.Y.Z --prerelease` (menandainya sebagai bukan yang terbaru) 2. `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` (hanya jika belum digunakan oleh pengguna) 3. Atau: lakukan hotfix pada `release/vX.Y.0` → rilis patch `vX.Y.(Z+1)` 4. Segera komunikasikan melalui GitHub Discussions dan Discord ## Aturan Ketat - Jangan pernah melakukan commit langsung ke `main` - Jangan pernah menggunakan `git push --force` ke cabang `main` atau `release/*` - Jangan pernah melewati hook Husky (`--no-verify`) - Jangan pernah melakukan commit terhadap rahasia, kredensial, atau file `.env` - Cakupan harus tetap ≥60/60/60/60 (pernyataan/baris/fungsi/cabang) - Selalu sertakan atau perbarui pengujian ketika mengubah kode produksi di `src/`, `open-sse/`, `electron/`, atau `bin/` ## Pemeriksaan Sinkronisasi Otomatis Jalankan pemeriksaan sinkronisasi dokumentasi secara lokal sebelum membuka PR: ```bash npm run check:docs-sync ``` CI juga menjalankan pemeriksaan ini di `.github/workflows/ci.yml` (pekerjaan lint).