# Release Checklist (Bahasa Melayu) 🌐 **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) · 🇮🇩 [id](../../../id/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) · 🇲🇹 [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) --- > **Kemas kini terakhir:** 2026-08-28 — v3.8.51 > Aliran keluaran diperkemas yang memanfaatkan kemahiran Claude Code untuk automasi. > > **Pastikan baris gilir/cabang kekal hijau antara keluaran:** lihat [RELEASE_GREEN.md](./RELEASE_GREEN.md) > (keluarga `/green-prs` + `npm run check:release-green` + `/babysit` + tugasan setiap malam). Menjalankan > ini secara berkala — dan terutamanya **sebelum** senarai semak ini — memastikan PR keluaran bermula dalam keadaan hijau. ## Ringkasan ```bash # 1. Naikkan versi + jana CHANGELOG (kemahiran) /version-bump-cc patch # atau minor/major # 2. Jalankan gerbang kualiti secara setempat npm run check # lint + ujian npm run test:coverage # gerbang liputan penuh (60/60/60/60) # 3. Bina & uji ringkas npm run build npm run test:e2e # pilihan tetapi disyorkan # 4. Jana keluaran (kemahiran) /generate-release-cc # 5. Laksanakan penggunaan (kemahiran) /deploy-vps-both-cc # atau akamai-cc / local-cc # 6. Tangkap bukti keluaran (kemahiran) /capture-release-evidences-cc ``` ## Penerbitan Dipercayai npm (lalai sejak v3.8.51) — berperingkat atas permintaan, langsung sebagai pilihan sandaran `npm-publish.yml` menerbitkan melalui **Penerbitan Dipercayai npm (OIDC)** secara lalai: tugasan `stage-npm` (dihoskan GitHub) menukar id-token GitHub dengan kelayakan npm jangka pendek untuk pelaksanaan tersebut — tiada token npm jangka panjang dalam rahsia repositori, tiada gesaan 2FA, asal-usul disertakan. Itulah mekanisme pintasan yang dibenarkan oleh npm kini setelah token yang melangkau 2FA sedang ditamatkan; ia memulihkan aliran automatik sepenuhnya yang dimiliki projek sehingga v3.8.48 sambil mengekalkan jaminan WS1.3 (token yang bocor tidak boleh menerbitkan dengan sendirinya — tiada token). **Persediaan sekali sahaja (pemilik):** npmjs.com → pakej `omniroute` → Settings → _Trusted Publisher_ → GitHub: pemilik `diegosouzapw`, repositori `OmniRoute`, aliran kerja `npm-publish.yml` (persekitaran: tiada). Sehingga ini diwujudkan, langkah automatik akan gagal dengan `ENEEDAUTH`: cetuskan semula dengan `publish_mode=staged` (di bawah) atau `direct`. ### Penerbitan berperingkat (atas permintaan — `publish_mode=staged`) Aliran kerja npm-publish tidak lagi menerbitkan secara langsung: ia memulakan tarball yang telah dibungkus (`check:pack-boot`) dan kemudian menjalankan `npm stage publish` — bait yang tepat disimpan sementara dalam daftar, **tidak boleh dipasang** sehingga pemilik meluluskannya. Gerbang 2FA manusia telah dialihkan ke SELEPAS pembuktian, bukan sebelumnya. **Aliran pemilik selepas aliran kerja menjadi hijau:** 1. `npm stage list omniroute` — cari id peringkat (turut dicetak dalam ringkasan aliran kerja). 2. Sahkan bait berperingkat (disyorkan): `npm stage download `, kemudian pasang tarball yang dimuat turun ke dalam awalan sementara dan mulakannya (`npm run check:pack-boot` mengautomatikkan keputusan pack→install→boot yang sama dalam CI). 3. `npm stage approve ` — gesaan 2FA ITULAH proses penerbitan. `npm stage reject ` akan membuangnya. 4. Jaring keselamatan selepas penerbitan: pengesah selepas penerbitan (WS1.4 daripada pelan v3.8.49) memasang versi yang diterbitkan daripada daftar awam dalam bekas yang bersih dan memulakannya. **Pilihan sandaran kecemasan:** `workflow_dispatch` dengan `publish_mode=direct` memulihkan `npm publish` segera yang terdahulu (gunakan hanya jika pementasan itu sendiri bermasalah; rekodkan sebabnya). **Pengukuhan sekali sahaja (pemilik, npmjs.com):** konfigurasikan Trusted Publisher untuk `omniroute` dalam mod peringkat sahaja supaya token jangka panjang yang bocor tidak boleh menjalankan `npm publish` secara langsung dari mana-mana sahaja — CI hanya boleh membuat pementasan; hanya 2FA pemilik boleh membuat keluaran. **Panduan artifak rosak (tidak berubah):** `npm deprecate omniroute@ " — use "` sebagai tindak balas lalai (beberapa minit, boleh diterbalikkan); `npm unpublish` hanya dalam tempoh 72j/tiada-kebergantungan dan jangan sekali-kali sebagai tindakan pertama. Docker: jangan sesekali menulis semula tag versi — pengunduran dilakukan dengan menghalakan semula `latest` kepada digest baik yang terakhir. **Docker Hub `latest` (diperlukan pada setiap penerbitan SemVer stabil):** aliran kerja `docker-publish` mesti menandakan **kedua-dua** `X.Y.Z` dan, apabila `should-promote-latest.sh` bersetuju bahawa ini ialah SemVer stabil tertinggi, `:latest` dengan **digest yang sama**. Selepas tugasan: digest `latest` Hub menyamai digest SemVer baharu dan `last_updated` telah berubah. Jangan biarkan `:latest` kekal pada binaan yang lebih lama sedangkan nota keluaran membincangkan pembaikan yang hanya wujud pada git. Permulaan pantas Compose menggunakan `:latest`; GitOps harus terus menyematkan `X.Y.Z`. Lihat [Saluran keluaran Docker](../guides/DOCKER_GUIDE.md#release-channels) dan #10317. ## Laluan Pantas Hotfix (label `hotfix`) PR berlabel `hotfix` melangkau matriks CI yang berat (E2E 9 serpihan, peningkatan ambang liputan, quality-gate, quality-extended) dan mengekalkan gerbang pantas dengan isyarat tinggi: binaan, serpihan unit, integrasi, vitest, lint/semakan jenis, docs-sync, `check:pack-artifact` dan ujian asap permulaan tarball (`check:pack-boot`). Sasaran: hijau dalam ≤15 minit berbanding ~33 minit. **Dasar kemasukan — keempat-empatnya diperlukan (dimodelkan berdasarkan laluan kecemasan Chromium/VS Code/Node):** 1. **Keterukan**: pengeluaran rosak — artifak yang diterbitkan ranap semasa permulaan / pembaikan keselamatan / setiap pengguna keluaran terjejas. "Penting" bukan bermaksud "rosak". 2. **Kuasa**: hanya pemilik repositori menggunakan label `hotfix`. Label tersebut IALAH kelulusan — jangan sekali-kali gunakannya sendiri pada PR kempen. 3. **Bukti**: badan PR memautkan pelaksanaan berat terdahulu yang hijau sepenuhnya (suit yang akan disahkan semula oleh tugas yang dilangkau) serta ujian pembaikan itu sendiri yang pada mulanya gagal, kemudian lulus. 4. **Skop**: cherry-pick sahaja — pembaikan minimum, tanpa pemfaktoran semula, tanpa perubahan sampingan. Permukaan liputan/peningkatan ambang yang dilangkau disahkan semula oleh pelaksanaan penuh seterusnya pada cabang keluaran (hijau keluaran berterusan) — laluan ini melangkau PENUNGGUAN, bukan pengesahan. Perbezaan ujian sahaja (semua fail di bawah `tests/`, tiada di bawah `tests/e2e/`) melangkau matriks E2E secara automatik, tanpa sebarang label. ## Senarai Semak Terperinci ### Pra-keluaran - [ ] Semua PR yang disasarkan untuk keluaran ini telah digabungkan ke dalam `release/vX.Y.0` - [ ] Semua item Linear/isu yang masih terbuka untuk versi ini telah ditutup atau ditunda ke pencapaian seterusnya - [ ] CI berjaya pada cabang `release/vX.Y.0` - [ ] Tiada penanda `TODO(release)` dalam kod: `grep -r "TODO(release)" src/ open-sse/` - [ ] Imej asas Docker adalah terkini (kini `node:24.15.0-trixie-slim`) ### Versi & Log Perubahan - [ ] Jalankan `/version-bump-cc ` (kemahiran Claude Code) - Menaikkan versi `package.json`, `electron/package.json` - Menjana semula `CHANGELOG.md` daripada komit git sejak tag terakhir - Mengemas kini lencana README.md - [ ] Semak CHANGELOG.md secara manual dan kemaskan mesej komit jika perlu - [ ] Pastikan bahagian semver terkini dalam `CHANGELOG.md` sepadan dengan versi `package.json` - [ ] Kekalkan `## [Unreleased]` sebagai bahagian pertama log perubahan untuk kerja akan datang - [ ] Kemas kini `docs/openapi.yaml` → `info.version` mesti sepadan dengan versi `package.json` ### Kualiti Kod - [ ] `npm run lint` — 0 ralat (amaran memang telah wujud) - [ ] `npm run typecheck:core` — bersih - [ ] `npm run typecheck:noimplicit:core` — bersih (ketat) - [ ] `npm run check:cycles:ratchet` — dalam had maksimum `metrics.cycles` yang dibekukan (`check:cycles` biasa keluar dengan kod 1 jika terdapat sebarang kitaran, #15281) - [ ] `npm run check:any-budget:t11` — dalam peruntukan - [ ] `npm run check:route-validation:t06` — bersih - [ ] `npm run check:node-runtime` — had minimum masa jalan yang disokong dipenuhi (`>=22.22.2 <23`, `>=24.0.0 <27`, mengikut `SUPPORTED_NODE_RANGE` dalam `src/shared/utils/nodeRuntimeSupport.ts`; sejajar dengan `engines` dalam `package.json`) ### Pengujian - [ ] `npm run test:unit` — lulus - [ ] `npm run test:vitest` — lulus (pelayan MCP, autoCombo, cache) - [ ] `npm run test:coverage` — ambang 60/60/60/60 dipenuhi (pernyataan/baris/fungsi/cabang) - [ ] `npm run test:integration` — lulus (jika perubahan melibatkan DB / pengendali) - [ ] `npm run test:combo:matrix` — lulus (matriks strategi gabungan: membuktikan secara deterministik keputusan pemilihan bagi kesemua 19 strategi penghalaan awam; jalankan apabila mengubah penghalaan gabungan, resolusi strategi atau logik sandaran) - [ ] `RUN_COMBO_LIVE=1 npm run test:combo:live` — **pilihan/manual** (ujian asap huluan sebenar yang dikawal; mendapatkan petikan DB baca sahaja daripada VPS `root@192.168.0.15`; mengakses penyedia sebenar dan menggunakan kredit; tidak pernah dijalankan dalam CI; dilangkau dengan bersih tanpa kawalan tersebut) - [ ] `npm run test:combo:live:vps` — **pilihan/manual** (ujian asap langsung VPS Fasa 3: 7 senario HTTP terhadap pelayan `.15` langsung melalui Node ESM biasa; memerlukan `ssh root@192.168.0.15`; hanya mencipta/memadam gabungan `__live_test__*`; mengakses penyedia sebenar; tidak pernah dijalankan dalam CI) - [ ] `npm run test:e2e` — lulus (perubahan UI) - [ ] `npm run test:protocols:e2e` — lulus (perubahan MCP/A2A) - [ ] `npm run test:ecosystem` — lulus ### Cangkuk (Disahkan oleh Husky) Cangkuk Husky terletak dalam `.husky/` dan dijalankan secara automatik semasa operasi git. - **pra-komit:** `npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11` - **pra-tolak:** kawalan pantas dan deterministik — `npm run check:any-budget:t11 && npm run check:tracked-artifacts` (diaktifkan pada 2026-06-13). Sengaja mengecualikan `test:unit` (perlahan; diliputi oleh tugas CI `test-unit`). - Jalankan `npm run test:unit` secara manual sebelum menolak cabang keluaran. Jika cangkuk gagal: betulkan isu asas, jangan pintas dengan `--no-verify`. ### Komit Konvensional Semua komit yang akan dimasukkan ke dalam keluaran mesti mengikut format `type(scope): subject`. **Jenis yang sah:** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `style`, `ci` **Skop yang sah:** `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 pecah serasi: tambahkan pengaki `BREAKING CHANGE:` atau `!` selepas skop (cth. `feat(api)!: drop /v0`). ### Dokumentasi - [ ] `npm run check:docs-sync` lulus (dijalankan secara automatik oleh pra-komit) - [ ] `npm run check:docs-all` lulus (menyeluruh: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links) - [ ] `npm run check:env-doc-sync` keluar dengan kod 0 — kontrak persekitaran antara kod ↔ `.env.example` ↔ `docs/reference/ENVIRONMENT.md` kekal utuh - [ ] `npm run check:doc-links` keluar dengan kod 0 — tiada rujukan markdown dalaman yang rosak selepas penstrukturan semula - [ ] `docs/architecture/ARCHITECTURE.md` telah disemak untuk penyimpangan storan/masa jalan - [ ] `docs/guides/TROUBLESHOOTING.md` telah disemak untuk penyimpangan pemboleh ubah persekitaran dan operasi - [ ] Jika `.env.example` berubah: `docs/reference/ENVIRONMENT.md` dikemas kini - [ ] Jika ciri baharu mempunyai UI: `docs/guides/USER_GUIDE.md` menyebutnya - [ ] Jika ciri baharu mempunyai API: `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` dikemas kini - [ ] Jika ciri baharu ialah modul: `docs/.md` khusus wujud - [ ] Jika terdapat perubahan pecah serasi: `docs/guides/TROUBLESHOOTING.md` mempunyai nota migrasi ### i18n - [ ] `npm run i18n:check` keluar dengan kod 0 — keadaan terjemahan (`.i18n-state.json`) disegerakkan dengan dokumen sumber (tiada sumber yang menyimpang dalam mod ketat; nasihat mod amaran boleh diterima untuk kemasan dokumentasi saat akhir, tetapi seharusnya 0 sebelum pengetagan) - [ ] `npm run i18n:check-ui-coverage` keluar dengan kod 0 — setiap tempatan UI berada pada atau melebihi had minimum liputan 80% - [ ] `npm run i18n:sync-ui:dry` melaporkan 0 kunci yang tiada merentas kesemua 66 tempatan sasaran (setiap tempatan kecuali sumber `en`) - [ ] Jika dokumen sumber bahasa Inggeris berubah, jalankan `npm run i18n:run` (memerlukan `OMNIROUTE_TRANSLATION_API_KEY` dalam `.env`) sebelum pengetagan - [ ] Sumbangan terjemahan boleh ditunda ke keluaran seterusnya jika kecil (jejaki dalam CHANGELOG) ### Migrasi Pangkalan Data - [ ] Jika `src/lib/db/migrations/` mempunyai fail baharu: - [ ] Setiap migrasi adalah idempoten (`CREATE TABLE IF NOT EXISTS`, dsb.) - [ ] Migrasi dibalut dalam transaksi - [ ] Dinomborkan dengan betul (tiada jurang dalam jujukan) - [ ] Uji pada pemasangan baharu: padam `~/.omniroute/omniroute.db` dan jalankan `npm run dev` - [ ] Uji pada pemasangan sedia ada: sandarkan DB, jalankan migrasi, sahkan skema - [ ] Fail WAL (`-wal`, `-shm`) dikendalikan dengan betul jika migrasi menulis semula jadual ### Katalog Penyedia (disahkan oleh Zod) - [ ] Skema Zod `src/shared/constants/providers.ts` sah semasa dimuatkan - [ ] Semua penyedia mempunyai medan yang diperlukan (`id`, `label`, `kind`, dll.) - [ ] `freeNote` disediakan untuk penyedia percuma baharu - [ ] Penyedia OAuth mempunyai `oauthConfig` yang didaftarkan dalam `src/lib/oauth/constants/oauth.ts` - [ ] Jika penyedia baharu ditambahkan: pelaksana yang sepadan dalam `open-sse/executors/` - [ ] Jika format bukan OpenAI: penterjemah dalam `open-sse/translator/` - [ ] Model didaftarkan dalam `open-sse/config/providerRegistry.ts` - [ ] Ujian unit dalam `tests/unit/` meliputi pengelasan dan penghalaan penyedia ### Desktop (Electron) Jika `electron/` berubah: - [ ] `npm run electron:smoke:packaged` berjaya - [ ] Binaan diuji untuk sekurang-kurangnya satu daripada `:win`, `:mac`, `:linux` - [ ] Sijil tandatangan kod belum tamat tempoh (jika menandatangani) - [ ] Versi `electron/package.json` sepadan dengan `package.json` akar - [ ] Penuding saluran kemas kini automatik dikemas kini jika mengeluarkan ke `stable` ### Susun Atur Binaan Repositori menggunakan tiga direktori output yang berbeza — jangan sesekali mencampuradukkannya: | Direktori | Tujuan | Dijejaki? | | --------- | -------------------------------------------------------------------- | ------------------ | | `src/` | Sumber aplikasi (TypeScript / TSX) | Ya | | `.build/` | Perantaraan binaan — output `next build` (`distDir`) | Tidak (gitignored) | | `dist/` | Himpunan npm boleh diedarkan — dihimpunkan oleh `assembleStandalone` | Tidak (gitignored) | > **Nota pengendali:** direktori imej VPS jauh kekal sebagai `/usr/lib/node_modules/omniroute/app/`. > Hanya output binaan **dalam repositori** yang dipindahkan (`app/` → `dist/`). Kemahiran pengerahan melakukan rsync > kandungan `dist/` ke dalam direktori `app/` jauh — tiada perubahan laluan VPS diperlukan. **Aliran binaan tunggal:** ``` npm run build:release └─ rm -rf .build dist (bersihkan) └─ next build → .build/next/ (perantaraan) └─ assembleStandalone (menyalin standalone + static + public + natives → dist/) └─ menulis dist/BUILD_SHA (sentinel HEAD) ``` JANGAN jalankan `npm run build` diikuti oleh `npm run build:cli` yang berasingan untuk pengerahan — gunakan `npm run build:release` yang menjalankan binaan semula bersih + sentinel dalam satu perintah. ### Pengesahan Artifak - [ ] `npm run build:release` berjaya dan `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] `npm run check:pack-artifact` bersih — tiada `app.__qa_backup`, `scripts/scratch`, `package-lock.json`, atau sisa setempat lain - [ ] `dist/server.js` wujud selepas binaan - [ ] Ujian asap masa jalan berpakej setempat pilihan: `npm run dev:candidate -- validate` selepas `npm run dev:candidate -- build` memulakan tarball berpakej pada `DATA_DIR` yang diasingkan dan memeriksa `/api/health` + `/v1/models` (lihat [Laluan Emas Sumbangan](CONTRIBUTION_GOLDEN_PATH.md#local-candidate-loop)) ### Pengetagan & Keluaran - [ ] Jalankan `/generate-release-cc` (kemahiran Claude Code): - Mencipta tag `vX.Y.Z` - Menolak tag dan cabang - Membuka Keluaran GitHub dengan kandungan log perubahan - Melampirkan pemasang Electron (jika dibina) - [ ] Atau secara manual: ```bash git tag -a vX.Y.Z -m "Keluaran vX.Y.Z" git push origin vX.Y.Z gh release create vX.Y.Z --notes-from-tag ``` ### Pengerahan Kemahiran pengerahan menggunakan aliran rsync ringan — tanpa `npm pack`, tanpa `npm i -g`: - [ ] Gunakan kemahiran pengerahan yang sepadan dengan sasaran: - `/deploy-vps-local-cc` — VPS setempat (192.168.0.15) - `/deploy-vps-akamai-cc` — VPS Akamai (69.164.221.35) - `/deploy-vps-both-cc` — kedua-duanya - [ ] Sebelum mengerah, sahkan `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] Binaan mesti dijalankan di tempat `node_modules` adalah sebenar (checkout utama atau worktree yang telah menjalankan `npm ci` — BUKAN worktree dengan pautan simbolik) - [ ] Jalankan ujian asap pada tika yang dikerahkan: - Buka `/dashboard/health` → semak rentetan versi sepadan dengan keluaran - Jalankan permintaan `/v1/chat/completions` terhadap penyedia yang diketahui - Sahkan `/api/monitoring/health` mengembalikan pemutus litar `CLOSED` - Sahkan pengangkutan MCP memberikan respons (`/mcp` HTTP, `/mcp-sse` SSE) ### Selepas keluaran - [ ] Jalankan `/capture-release-evidences-cc` (kemahiran Claude Code) - Menangkap tangkap layar/rakaman WebP bagi ciri baharu - Melampirkannya pada nota keluaran / catatan blog - [ ] Kemas kini GitHub Discussions / Discord dengan pengumuman keluaran - [ ] Buka pencapaian untuk versi seterusnya - [ ] Jika kritikal: sematkan perbincangan atau siarkan dalam `news.json` untuk sepanduk dalam aplikasi ### Gerbang pelancaran awam Radar Pengumuman Radar sengaja dikomitkan dengan `active: false`. Pengaktifan ialah perubahan berasingan selepas bukti bagi setiap perkara di bawah tersedia: - [ ] Semua PR Radar bertindan telah digabungkan dan CI pada hujung keluaran berstatus hijau - [ ] Kerah dan uji asap laluan Radar sumber terbuka dengan `RADAR_ENABLED` masih dimatikan secara lalai - [ ] Uji asap `GET /planos`, `/termos`, `/privacidade`, dan `/reembolso` pada hos Radar yang ditetapkan - [ ] Rekod identiti/maklumat hubungan/alamat pengendali dan semakan undang-undang yang diluluskan pemilik dalam perkhidmatan peribadi - [ ] Uji Stripe Checkout dan webhook bertandatangan dalam mod ujian sahaja - [ ] Uji satu penghantaran e-mel transaksi tersulit dengan pengirim/domain yang diluluskan - [ ] Buktikan pemulihan sandaran dan satu pelaksanaan penyelidikan terselia yang dihadkan bajet - [ ] Luluskan dasar semakan BRL/PIX sebelum menerima bukti sumbangan - [ ] Dayakan Checkout awam hanya selepas gerbang terdahulu dilepasi, kemudian aktifkan ID `news.json` baharu - [ ] Sahkan sepanduk Laman Utama menggunakan salinan yang disetempatkan dan ID baharu muncul semula selepas ID lama ditutup ## Ujian asap Perkhidmatan Terbenam (v3.8.4+) Sebelum menerbitkan sebarang keluaran yang merangkumi perubahan pada perkhidmatan terbenam, sahkan: ### But DB baharu (mengesan pertembungan migrasi — ditambahkan selepas pembaikan segera v3.8.4) - [ ] `DATA_DIR=$(mktemp -d) npm start &` — tunggu 10 s untuk but - [ ] `curl -s http://127.0.0.1:20128/api/services/9router/status | jq '.tool'` mengembalikan `"9router"` (BUKAN 404, BUKAN 500). Mengesahkan migrasi `071_services.sql` telah digunakan + baris telah disemai. - [ ] `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 (mengesahkan `070_webhooks_kind_metadata.sql` telah digunakan). - [ ] `node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.ts` lulus — melindungi daripada pertembungan pada masa hadapan. ### 9Router - [ ] `POST /api/services/9router/install` mengembalikan 200 dengan `installedVersion` dalam masa kurang daripada 2 min - [ ] `POST /api/services/9router/start` mengembalikan 200 dan `state: "running"` dalam masa kurang daripada 30 s - [ ] `GET /api/services/9router/status` melaporkan `health: "healthy"` - [ ] `POST /v1/chat/completions` dengan `"model": "9router/auto/..."` mengembalikan 200 (penghalaan hujung ke hujung melalui 9Router) - [ ] `GET /dashboard/providers/services/9router/embed/dashboard` memaparkan UI asli 9Router di dalam proksi (tiada iframe `127.0.0.1:port` langsung) - [ ] `POST /api/services/9router/rotate-key` mengembalikan `{ keyRotated: true }` dan perkhidmatan dimulakan semula dengan lancar - [ ] `POST /api/services/9router/stop` mengembalikan 200 dan `state: "stopped"` - [ ] `GET /api/services/9router/logs?tail=50` mengembalikan strim SSE dengan peristiwa `snapshot` yang mengandungi baris terkini - [ ] Pemasangan dalam persekitaran tanpa `npm` dalam PATH mengembalikan 500 dengan mesej ralat yang mesra (tanpa surihan tindanan) ### CLIProxyAPI - [ ] `POST /api/services/cliproxy/install` mengembalikan 200 dalam masa kurang daripada 2 min - [ ] `POST /api/services/cliproxy/start` mengembalikan 200 dan `state: "running"` dalam masa kurang daripada 30 s - [ ] `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 strim SSE ### Regresi keselamatan - [ ] `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 ralat daripada `/api/services/*` tidak mengandungi `err.stack` atau laluan fail mutlak ## Semakan v3.8.0+ Sebelum menerbitkan sebarang keluaran v3.8.x, sahkan perkara tambahan berikut: - [ ] `omniroute --tray` berjaya dibut pada macOS (systray2 dipasang ke dalam `~/.omniroute/runtime/`) - [ ] `omniroute --tray` berjaya dibut pada Linux (memerlukan DISPLAY; ralat terkawal jika tidak ditetapkan) - [ ] `omniroute --tray` berjaya dibut pada Windows (PowerShell NotifyIcon, tiada binari tambahan) - [ ] `omniroute config tray enable` mencipta entri automula; nyahdayakan akan mengalih keluarnya - [ ] `npm install -g omniroute@` menjalankan postinstall tanpa keluar secara fatal - [ ] Laluan kemas kini mengekalkan kebergantungan pilihan: `omniroute update --apply` dan pengemas kini automatik menjalankan `npm install -g … --include=optional` supaya `optionalDependencies` (better-sqlite3, keytar, tls-client, dan tindanan SLM llmlingua: `@atjsh/llmlingua-2@2.0.5`, `js-tiktoken`) kekal selepas kemas kini. Tahap SLM ultra `modelPath` juga memerlukan model tinybert, yang dimuat turun secara automatik ke `${DATA_DIR}/models/llmlingua` pada penggunaan pertama. Postinstall (`scripts/build/colocateOptionals.mjs`) kemudian menempatkan bersama penutupan pilihan SLM ke dalam `dist/node_modules` supaya worker menyelesaikan SATU tika `@huggingface/transformers` ^4.2.0 — surihan kendiri hanya membungkus transformers, bukan pilihan yang diimport secara dinamik, jadi tanpanya worker akan memuatkan llmlingua-2 menggunakan transformers pada akar dan tahap SLM akan gagal secara senyap dalam keadaan terbuka. - [ ] `omniroute status` berfungsi tanpa `.env` (laluan token CLI, gelung balik sahaja) - [ ] `curl http://localhost:20128/api/shutdown` mengembalikan 401 (laluan yang sentiasa dilindungi) - [ ] `curl -H "host: evil.com" http://localhost:20128/api/mcp/sse` mengembalikan 401 (pengawal gelung balik) - [ ] Masa jalan SQLite diselesaikan kepada `bundled` pada pelaksanaan pertama (binari terbundel sah untuk platform) - [ ] Masa jalan SQLite beralih kepada `runtime` apabila `node_modules/better-sqlite3` dipadamkan - [ ] Penapis MCP pintar memampatkan output sebenar `playwright-mcp browser_snapshot` (pengurangan ≥50%) - [ ] Kesemua 10 fail `skills/omniroute*/SKILL.md` boleh diambil secara awam melalui URL mentah GitHub - [ ] Bestari penyediaan awal menunjukkan langkah jelajah tahap "Cara Ia Berfungsi" pada persediaan baharu - [ ] Widget liputan tahap papan pemuka utama menunjukkan bilangan yang dikonfigurasikan/aktif --- ## Pemotongan LTS 3.9.0 (diraptai dalam 3.8.58) Selepas v3.8.59, versi seterusnya ialah 3.9.0 dan tipnya menjadi dua cabang berjangka panjang: `stable/v3` (barisan LTS v3, npm `latest`) dan `develop` (v4, dinaikkan kepada 4.0.0, npm `nightly`). Model cabang/saluran, port ke hadapan dan label terdapat dalam [RELEASE_STRATEGY.md](./RELEASE_STRATEGY.md); pelannya terdapat dalam [ROADMAP](../../ROADMAP.md) (Fasa 3). Pemotongan dilaksanakan sekali; 3.8.58 meraptainya dari awal hingga akhir pada fork, manakala 3.8.59 ditutup dengan [senarai semak GO/NO-GO](./LTS_GO_NO_GO.md). ### Percubaan kering (baca sahaja, selamat pada bila-bila masa) ```bash npm run release:dry-run-lts-cut # pemotongan sebenar: 3.9.0 daripada HEAD, tag sebelumnya v3.8.59 npm run release:dry-run-lts-cut -- --from <3.9.0-tip> # tetapkan commit sumber ``` `scripts/release/dry-run-lts-cut.mjs` tidak melaksanakan apa-apa: ia membaca git dan `gh` serta mencetak keseluruhan urutan — prasyarat (sumber dapat diselesaikan, repositori sumber mempunyai sejarah penuh — klon cetek tidak boleh menolak `stable/v3`, pihak penerima menolak pek pada induk tercantum yang pertama, jadi lakukan pemotongan daripada klon penuh atau jalankan `git fetch --unshallow origin` terlebih dahulu — tag sebelumnya wujud, `package.json` ialah versi sasaran, isu `release-freeze` dibuka, tiada isu `Release branch not green` yang terbuka pada cabang keluaran sedia ada — cabang yang tidak wujud melaporkan `?` tidak diketahui, tidak sekali-kali hijau — baris gilir `release` Mergify dikonfigurasikan (G11: `queue_rules`, `checks_timeout`, label `queue`), set peraturan `release/*` masih menyekat pemadaman dan tolakan paksa, serta `stable/v3` dan `develop` belum wujud), dua langkah cabang, pencetus aliran kerja dorman dan syarat `if:` yang menjadi benar (serta yang kekal disekat oleh pemboleh ubah repositori atau ditetapkan kepada repositori kanonik), dist-tag yang dijangkakan (`latest` → 3.9.0, `next` dan `nightly` kosong) dan pengunduran. Kod keluar `0` = `RESULT: READY`, `1` = prasyarat penyekat gagal (`✗`), `2` = ralat penggunaan. `--advisory ` menurunkan taraf semakan kepada amaran (`!`) tanpa menyembunyikannya. Jalankan percubaan kering bagi pemotongan sebenar sementara pembekuan keluaran 3.9.0 masih dibuka — cabang dicipta selepas tag dan sebelum Fasa 12c menarik balik pembekuan. ### Raptai 3.8.58 (fork sahaja) ```bash # 1. Percubaan kering pada tip semasa dengan parameter raptai npm run release:dry-run-lts-cut -- --target-version 3.8.58 --previous-tag v3.8.57 \ --advisory freeze,base-green # 2. Laksanakan terhadap remote FORK (origin, atau mana-mana remote yang URL-nya ialah repositori # kanonik, akan ditolak; setiap langkah meminta pengesahan pada 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. Jalankan aliran kerja dorman dalam fork (workflow_dispatch apabila percubaan kering # melaporkan penetapan repositori kanonik), kemudian undur semula 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 peningkatan develop dibina dengan paip dalaman git (tiada pepohon kerja disentuh) dan menaikkan lima fail yang sama seperti commit pembukaan kitaran: `package.json`, `open-sse/package.json`, `electron/package.json`, `package-lock.json` dan `docs/openapi.yaml`. Bahagian CHANGELOG `[4.0.0]` dan cerminan i18n-nya dibuka pada `develop` selepas itu, sebelum PR pertamanya. Skrip tidak pernah mengubah dist-tag npm — raptai perubahan tersebut pada pakej sementara. ### Artifak pratonton PR (bina sekali, promosikan bait yang sama) `.github/workflows/preview-artifact.yml` membina satu tarball pengeluaran daripada kepala PR dan mengesahkan binaan tepat tersebut (bahagian (a) #8084). PR daripada repositori yang sama sahaja; tiada apa-apa diterbitkan. ```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 # pemasangan pratonton ``` Proses tersebut menjalankan `npm ci`, `npm run build:release`, `npm run check:pack-artifact`, membungkus tarball, menjalankan `npm run check:pack-boot` (rahsia palsu, direktori data sementara), membungkus semula dan gagal melainkan cernanya sepadan, kemudian merekodkan `artifact-identity.json` (SHA kepala, SHA asas, hash fail kunci, platform, seni bina, ABI node, pembundel, dasar binaan — `scripts/release/artifact-identity.mjs`) serta mengesahkan tarball dalam tugas berasingan. Mempromosikan pratonton bermaksud memasang tarball tersebut: jangan sekali-kali membina semula daripada sumber. ### Pemotongan (3.9.0, selepas GO) 1. GO direkodkan dalam [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. Cipta cabang pada `origin` secara manual menggunakan perintah yang dicetak oleh percubaan kering — skrip enggan menolak ke `origin`. Untuk menggunakan semula commit develop yang telah disemak, jalankan raptai `--execute` pada tip 3.9.0 terhadap fork anda terlebih dahulu; ia mencetak kedua-dua SHA dan commit yang sama boleh ditolak: ```bash git push origin :refs/heads/stable/v3 :refs/heads/develop ``` 4. Lindungi `stable/v3` dan `develop` (set peraturan + baris gilir penggabungan) sebelum PR pertama diterima. 5. Aliran kerja dorman diaktifkan berdasarkan kewujudan cabang: `forward-port.yml` (tolakan ke `stable/v3`), `validate-stable-pr.yml` (PR ke `stable/v3`) dan `nightly-v4-build.yml` (membina `develop`). Sebelum pelancaran, tetapkan rahsia repositori `secrets.FORWARD_PORT_TOKEN` (supaya CI berjalan pada PR port ke hadapan); penerbitan setiap malam kekal dimatikan sehingga pemilik menetapkan pemboleh ubah repositori `vars.NIGHTLY_PUBLISH` kepada `true` dan npm Trusted Publishing menerima `nightly-v4-build.yml`. Penentuan saluran dilakukan oleh `scripts/release/dist-tag.mjs`, penentu yang sama digunakan oleh `npm-publish.yml`. 6. Sahkan saluran: `npm view omniroute dist-tags --json` menunjukkan `latest` = 3.9.0 dan tiada `next` / `nightly` sehingga v4 diterbitkan. 7. Undur semula, jika perlu: `git push origin --delete refs/heads/stable/v3 refs/heads/develop` dan `npm dist-tag add omniroute@3.8.59 latest`. --- ## Pengembalian Semula Jika keluaran mempunyai isu kritikal: 1. `gh release edit vX.Y.Z --prerelease` (menandakan keluaran sebagai bukan yang terkini) 2. `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` (hanya jika belum digunakan oleh pengguna) 3. Atau: lakukan pembaikan segera pada `release/vX.Y.0` → keluaran tampalan `vX.Y.(Z+1)` 4. Maklumkan dengan segera dalam GitHub Discussions dan Discord ## Peraturan Tegas - Jangan sekali-kali membuat commit terus ke `main` - Jangan sekali-kali menggunakan `git push --force` ke `main` atau cabang `release/*` - Jangan sekali-kali melangkau hook Husky (`--no-verify`) - Jangan sekali-kali membuat commit yang mengandungi rahsia, kelayakan, atau fail `.env` - Liputan mesti kekal ≥60/60/60/60 (pernyataan/baris/fungsi/cabang) - Sentiasa sertakan atau kemas kini ujian apabila mengubah kod produksi dalam `src/`, `open-sse/`, `electron/`, atau `bin/` ## Semakan Penyegerakan Automatik Jalankan pengawal penyegerakan dokumentasi secara setempat sebelum membuka PR: ```bash npm run check:docs-sync ``` CI turut menjalankan semakan ini dalam `.github/workflows/ci.yml` (tugas lint).