# Release Checklist (Українська) 🌐 **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) · 🇲🇾 [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) · 🇵🇰 [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) --- > **Останнє оновлення:** 2026-08-28 — v3.8.51 > Оптимізований процес випуску, що використовує навички Claude Code для автоматизації. > > **Підтримуйте чергу/гілку в зеленому стані між випусками:** див. [RELEASE_GREEN.md](./RELEASE_GREEN.md) > (сімейство `/green-prs` + `npm run check:release-green` + `/babysit` + нічний запуск). Періодичний запуск > цього процесу — і особливо **перед** виконанням цього контрольного списку — дає змогу почати PR випуску із зеленим статусом. ## Коротко ```bash # 1. Підвищте версію та згенеруйте CHANGELOG (навичка) /version-bump-cc patch # або minor/major # 2. Локально виконайте перевірку якості npm run check # лінтинг + тести npm run test:coverage # повна перевірка покриття (60/60/60/60) # 3. Зберіть і виконайте димове тестування npm run build npm run test:e2e # необов’язково, але рекомендовано # 4. Згенеруйте випуск (навичка) /generate-release-cc # 5. Розгорніть (навичка) /deploy-vps-both-cc # або akamai-cc / local-cc # 6. Зберіть підтвердження випуску (навичка) /capture-release-evidences-cc ``` ## Довірена публікація npm (типово з v3.8.51) — поетапна за запитом, пряма як резервний варіант `npm-publish.yml` типово публікує через **довірену публікацію npm (OIDC)**: завдання `stage-npm` (на сервері GitHub) обмінює id-токен GitHub на короткочасні облікові дані npm для цього запуску — без довготривалого токена npm у секретах репозиторію, без запиту 2FA, із доданим підтвердженням походження. Саме цей обхідний механізм тепер схвалює npm, оскільки токени, що пропускають 2FA, виводяться з ужитку; він відновлює повністю автоматичний процес, який проєкт мав до v3.8.48, водночас зберігаючи гарантію WS1.3 (витік токена сам по собі не дає змоги виконати публікацію — токена немає). **Одноразове налаштування (власник):** npmjs.com → пакет `omniroute` → Settings → _Trusted Publisher_ → GitHub: власник `diegosouzapw`, репозиторій `OmniRoute`, робочий процес `npm-publish.yml` (середовище: немає). Поки це не налаштовано, автоматичний крок завершується помилкою `ENEEDAUTH`: повторно запустіть із `publish_mode=staged` (нижче) або `direct`. ### Поетапна публікація (за запитом — `publish_mode=staged`) Робочий процес npm-publish більше не публікує безпосередньо: він запускає упакований tarball (`check:pack-boot`), а потім виконує `npm stage publish` — точні байти розміщуються в реєстрі, але **недоступні для встановлення**, доки власник їх не схвалить. Людський шлюз 2FA переміщено на етап ПІСЛЯ перевірки, а не перед нею. **Дії власника після успішного завершення робочого процесу:** 1. `npm stage list omniroute` — знайдіть ідентифікатор етапу (також зазначений у підсумку робочого процесу). 2. Перевірте розміщені байти (рекомендовано): `npm stage download `, потім установіть завантажений tarball у тимчасовий префікс і запустіть його (`npm run check:pack-boot` автоматизує таку саму перевірку «пакування → встановлення → запуск» у CI). 3. `npm stage approve ` — запит 2FA І Є публікацією. `npm stage reject ` відхиляє розміщення. 4. Захист після публікації: засіб перевірки після публікації (WS1.4 плану v3.8.49) установлює опубліковану версію з публічного реєстру в чистому контейнері та запускає її. **Аварійний резервний варіант:** `workflow_dispatch` із `publish_mode=direct` відновлює застарілий негайний `npm publish` (використовуйте лише в разі некоректної роботи самого поетапного розміщення; зафіксуйте причину). **Одноразове посилення захисту (власник, npmjs.com):** налаштуйте Trusted Publisher для `omniroute` у режимі лише поетапного розміщення, щоб витік довготривалого токена не давав змоги виконати `npm publish` безпосередньо звідки завгодно — CI може лише виконати поетапне розміщення; випуск здійснюється лише через 2FA власника. **Інструкція для пошкодженого артефакту (без змін):** `npm deprecate omniroute@ " — use "` як типова перша реакція (кілька хвилин, можна скасувати); `npm unpublish` — лише в межах вікна 72 годин/за відсутності залежних пакетів і ніколи не як перша дія. Docker: ніколи не перепризначайте тег версії — відкочування полягає в перепризначенні `latest` останньому справному дайджесту. **Docker Hub `latest` (обов’язково для кожної публікації стабільної версії SemVer):** робочий процес `docker-publish` повинен призначити теги **і** `X.Y.Z`, **і**, якщо `should-promote-latest.sh` підтверджує, що це найвища стабільна версія SemVer, `:latest` з **однаковим дайджестом**. Після виконання завдання: дайджест `latest` у Hub дорівнює дайджесту нової версії SemVer, а `last_updated` оновлено. Не залишайте `:latest` на старішій збірці, якщо в примітках до випуску йдеться про виправлення, наявні лише в git. Швидкі приклади запуску Compose використовують `:latest`; у GitOps слід і надалі фіксувати `X.Y.Z`. Див. [Канали випуску Docker](../guides/DOCKER_GUIDE.md#release-channels) і #10317. ## Швидкий шлях для термінових виправлень (мітка `hotfix`) PR із міткою `hotfix` пропускає важку матрицю CI (9-сегментний E2E, контрольне підвищення покриття, quality-gate, quality-extended) і зберігає швидкі та високоінформативні перевірки: збирання, сегменти модульних тестів, інтеграційні тести, vitest, lint/typecheck, docs-sync, `check:pack-artifact` і перевірку запуску з tarball (`check:pack-boot`). Мета: зелений статус за ≤15 хв замість ~33 хв. **Політика допуску — усі чотири умови обов’язкові (за зразком екстрених шляхів Chromium/VS Code/Node):** 1. **Критичність**: продакшн зламаний — опублікований артефакт аварійно завершується під час запуску / виправлення безпеки / проблема зачіпає кожного користувача релізу. «Важливо» не означає «зламано». 2. **Повноваження**: лише власник репозиторію застосовує мітку `hotfix`. Мітка Є схваленням — її ніколи не можна самостійно застосовувати до PR кампанії. 3. **Докази**: тіло PR містить посилання на попередній повністю зелений важкий запуск (набір, який повторно перевірили б пропущені завдання), а також на власний тест виправлення, що спочатку завершується невдало, а потім успішно. 4. **Обсяг**: лише cherry-pick — мінімальне виправлення, без рефакторингу та супутніх змін. Пропущена поверхня перевірки покриття/контрольного підвищення повторно перевіряється наступним повним запуском у гілці релізу (безперервно зелений реліз) — цей шлях пропускає ОЧІКУВАННЯ, але ніколи не перевірку. Зміни лише в тестах (усі файли в `tests/`, жодного в `tests/e2e/`) автоматично пропускають матрицю E2E без будь-якої мітки. ## Детальний контрольний список ### Перед випуском - [ ] Усі PR, призначені для цього випуску, об’єднано з `release/vX.Y.0` - [ ] Усі відкриті елементи Linear/завдання для цієї версії закрито або перенесено до наступного етапу - [ ] CI успішно проходить у гілці `release/vX.Y.0` - [ ] У коді немає маркерів `TODO(release)`: `grep -r "TODO(release)" src/ open-sse/` - [ ] Базовий образ Docker актуальний (наразі `node:24.15.0-trixie-slim`) ### Версія та журнал змін - [ ] Запустити `/version-bump-cc ` (навичка Claude Code) - Оновлює версії в `package.json`, `electron/package.json` - Повторно генерує `CHANGELOG.md` із комітів git після останнього тегу - Оновлює значки в README.md - [ ] Вручну перевірити CHANGELOG.md і за потреби відредагувати повідомлення комітів - [ ] Переконатися, що останній розділ semver у `CHANGELOG.md` відповідає версії в `package.json` - [ ] Залишити `## [Unreleased]` першим розділом журналу змін для майбутніх робіт - [ ] Оновити `docs/openapi.yaml` → `info.version` має відповідати версії в `package.json` ### Якість коду - [ ] `npm run lint` — 0 помилок (попередження існували раніше) - [ ] `npm run typecheck:core` — без помилок - [ ] `npm run typecheck:noimplicit:core` — без помилок (суворий режим) - [ ] `npm run check:cycles:ratchet` — у межах зафіксованого ліміту `metrics.cycles` (звичайний `check:cycles` завершується з кодом 1 за наявності будь-якого циклу, #15281) - [ ] `npm run check:any-budget:t11` — у межах бюджету - [ ] `npm run check:route-validation:t06` — без помилок - [ ] `npm run check:node-runtime` — дотримано мінімально підтримувану версію середовища виконання (`>=22.22.2 <23`, `>=24.0.0 <27`, відповідно до `SUPPORTED_NODE_RANGE` у `src/shared/utils/nodeRuntimeSupport.ts`; узгоджено з `engines` у `package.json`) ### Тестування - [ ] `npm run test:unit` — успішно - [ ] `npm run test:vitest` — успішно (сервер MCP, autoCombo, кеш) - [ ] `npm run test:coverage` — виконано поріг 60/60/60/60 (вирази/рядки/функції/гілки) - [ ] `npm run test:integration` — успішно (якщо зміни стосуються БД / обробників) - [ ] `npm run test:combo:matrix` — успішно (матриця комбінованих стратегій: детерміновано підтверджує рішення щодо вибору для всіх 19 загальнодоступних стратегій маршрутизації; запускати в разі змін комбінованої маршрутизації, визначення стратегій або резервної логіки) - [ ] `RUN_COMBO_LIVE=1 npm run test:combo:live` — **необов’язково/вручну** (ізольована перевірка реального вищого рівня; отримує знімок БД лише для читання з VPS `root@192.168.0.15`; звертається до реальних провайдерів, витрачає кредити; ніколи не запускається в CI; коректно пропускається без прапорця) - [ ] `npm run test:combo:live:vps` — **необов’язково/вручну** (оперативна перевірка VPS етапу 3: 7 HTTP-сценаріїв для активного сервера `.15` через звичайний Node ESM; потребує `ssh root@192.168.0.15`; створює/видаляє лише комбінації `__live_test__*`; звертається до реальних провайдерів; ніколи не запускається в CI) - [ ] `npm run test:e2e` — успішно (зміни інтерфейсу) - [ ] `npm run test:protocols:e2e` — успішно (зміни MCP/A2A) - [ ] `npm run test:ecosystem` — успішно ### Хуки (перевірені Husky) Хуки Husky розташовані в `.husky/` і запускаються автоматично під час операцій git. - **pre-commit:** `npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11` - **pre-push:** швидкі детерміновані перевірки — `npm run check:any-budget:t11 && npm run check:tracked-artifacts` (активовано 2026-06-13). Навмисно не включає `test:unit` (повільний; охоплюється завданням CI `test-unit`). - Перед надсиланням гілок випуску запустити `npm run test:unit` вручну. Якщо хук завершується помилкою: виправити першопричину, не обходити перевірку за допомогою `--no-verify`. ### Conventional Commits Усі коміти, призначені для випуску, мають відповідати формату `type(scope): subject`. **Допустимі типи:** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `style`, `ci` **Допустимі області:** `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 CHANGE:` або `!` після області (наприклад, `feat(api)!: drop /v0`). ### Документація - [ ] `npm run check:docs-sync` виконується успішно (автоматично запускається перед комітом) - [ ] `npm run check:docs-all` виконується успішно (зведена перевірка: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links) - [ ] `npm run check:env-doc-sync` завершується з кодом 0 — контракт змінних середовища між кодом ↔ `.env.example` ↔ `docs/reference/ENVIRONMENT.md` не порушено - [ ] `npm run check:doc-links` завершується з кодом 0 — після реструктуризації немає пошкоджених внутрішніх посилань markdown - [ ] `docs/architecture/ARCHITECTURE.md` перевірено на розбіжності щодо сховища/середовища виконання - [ ] `docs/guides/TROUBLESHOOTING.md` перевірено на розбіжності щодо змінних середовища та операцій - [ ] Якщо `.env.example` змінено: `docs/reference/ENVIRONMENT.md` оновлено - [ ] Якщо нова функція має інтерфейс: її згадано в `docs/guides/USER_GUIDE.md` - [ ] Якщо нова функція має API: оновлено `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` - [ ] Якщо нова функція є модулем: існує окремий файл `docs/.md` - [ ] Якщо це критична зміна: `docs/guides/TROUBLESHOOTING.md` містить примітку щодо міграції ### i18n - [ ] `npm run i18n:check` завершується з кодом 0 — стан перекладу (`.i18n-state.json`) синхронізовано з початковою документацією (у суворому режимі немає неузгоджених джерел; рекомендаційне попередження допустиме для останніх правок документації, але перед створенням тегу код має бути 0) - [ ] `npm run i18n:check-ui-coverage` завершується з кодом 0 — кожна локаль інтерфейсу має покриття не нижче порогового значення 80% - [ ] `npm run i18n:sync-ui:dry` повідомляє про 0 відсутніх ключів у всіх 66 цільових локалях (у кожній локалі, окрім початкової `en`) - [ ] Якщо початкову документацію англійською змінено, перед створенням тегу запустити `npm run i18n:run` (потребує `OMNIROUTE_TRANSLATION_API_KEY` у `.env`) - [ ] Внески до перекладів можна відкласти до наступного випуску, якщо вони незначні (відстежувати в CHANGELOG) ### Міграції бази даних - [ ] Якщо в `src/lib/db/migrations/` є нові файли: - [ ] Кожна міграція є ідемпотентною (`CREATE TABLE IF NOT EXISTS` тощо) - [ ] Міграції обгорнуто в транзакції - [ ] Нумерація правильна (без пропусків у послідовності) - [ ] Протестувати на чистому встановленні: видалити `~/.omniroute/omniroute.db` і запустити `npm run dev` - [ ] Протестувати на наявному встановленні: створити резервну копію БД, виконати міграцію, перевірити схему - [ ] Файли WAL (`-wal`, `-shm`) обробляються правильно, якщо міграція перезаписує таблиці ### Каталог провайдерів (перевірений за допомогою Zod) - [ ] Схема Zod у `src/shared/constants/providers.ts` є валідною під час завантаження - [ ] Усі провайдери мають обов’язкові поля (`id`, `label`, `kind` тощо) - [ ] Для нових безкоштовних провайдерів указано `freeNote` - [ ] Для OAuth-провайдерів зареєстровано `oauthConfig` у `src/lib/oauth/constants/oauth.ts` - [ ] Якщо додано нового провайдера: відповідний виконавець у `open-sse/executors/` - [ ] Якщо формат відрізняється від OpenAI: транслятор у `open-sse/translator/` - [ ] Моделі зареєстровано в `open-sse/config/providerRegistry.ts` - [ ] Модульні тести в `tests/unit/` охоплюють класифікацію та маршрутизацію провайдерів ### Десктопний застосунок (Electron) Якщо `electron/` змінено: - [ ] `npm run electron:smoke:packaged` виконується успішно - [ ] Збірки протестовано принаймні для однієї з платформ: `:win`, `:mac`, `:linux` - [ ] Сертифікати підписування коду не прострочені (якщо використовується підписування) - [ ] Версія в `electron/package.json` відповідає кореневому `package.json` - [ ] Вказівник каналу автоматичного оновлення оновлено, якщо випуск призначено для `stable` ### Структура збірки Репозиторій використовує три окремі вихідні каталоги — ніколи не плутайте їх: | Каталог | Призначення | Відстежується? | | --------- | -------------------------------------------------------------------------------- | ---------------- | | `src/` | Вихідний код застосунку (TypeScript / TSX) | Так | | `.build/` | Проміжні файли збірки — результат `next build` (`distDir`) | Ні (у gitignore) | | `dist/` | Готовий до розповсюдження npm-пакет — зібраний за допомогою `assembleStandalone` | Ні (у gitignore) | > **Примітка для оператора:** каталог образу на віддаленому VPS залишається `/usr/lib/node_modules/omniroute/app/`. > Переміщено лише результат збірки **в репозиторії** (`app/` → `dist/`). Навички розгортання синхронізують через rsync > вміст `dist/` із віддаленим каталогом `app/` — змінювати шляхи на VPS не потрібно. **Процес єдиної збірки:** ``` npm run build:release └─ rm -rf .build dist (очищення) └─ next build → .build/next/ (проміжні файли) └─ assembleStandalone (копіює автономну збірку + статичні файли + публічні файли + нативні модулі → dist/) └─ записує dist/BUILD_SHA (контрольний маркер HEAD) ``` НЕ запускайте `npm run build`, а потім окремо `npm run build:cli` для розгортання — використовуйте `npm run build:release`, що виконує чисту повторну збірку та створює контрольний маркер однією командою. ### Перевірка артефактів - [ ] `npm run build:release` виконується успішно, а `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] `npm run check:pack-artifact` виконується без помилок — немає `app.__qa_backup`, `scripts/scratch`, `package-lock.json` чи інших локальних залишкових файлів - [ ] Після збірки існує `dist/server.js` - [ ] Необов’язкова локальна димова перевірка запакованого середовища виконання: `npm run dev:candidate -- validate` після `npm run dev:candidate -- build` запускає запакований tarball в ізольованому `DATA_DIR` і перевіряє `/api/health` + `/v1/models` (див. [Основний шлях для внесків](CONTRIBUTION_GOLDEN_PATH.md#local-candidate-loop)) ### Тегування та випуск - [ ] Запустіть `/generate-release-cc` (навичка Claude Code): - Створює тег `vX.Y.Z` - Надсилає тег і гілку - Створює GitHub Release з текстом журналу змін - Додає інсталятори Electron (якщо їх зібрано) - [ ] Або вручну: ```bash git tag -a vX.Y.Z -m "Випуск vX.Y.Z" git push origin vX.Y.Z gh release create vX.Y.Z --notes-from-tag ``` ### Розгортання Навички розгортання використовують полегшений процес rsync — без `npm pack` і без `npm i -g`: - [ ] Використовуйте навичку розгортання, що відповідає цільовому середовищу: - `/deploy-vps-local-cc` — локальний VPS (192.168.0.15) - `/deploy-vps-akamai-cc` — VPS Akamai (69.164.221.35) - `/deploy-vps-both-cc` — обидва - [ ] Перед розгортанням підтвердьте, що `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] Збірка має виконуватися там, де `node_modules` є справжнім каталогом (основна робоча копія або worktree після `npm ci` — НЕ worktree із символічним посиланням) - [ ] Виконайте димову перевірку розгорнутого екземпляра: - Відкрийте `/dashboard/health` → перевірте, що рядок версії відповідає випуску - Виконайте запит `/v1/chat/completions` до відомого провайдера - Переконайтеся, що `/api/monitoring/health` повертає автоматичні вимикачі зі станом `CLOSED` - Підтвердьте, що транспорти MCP відповідають (`/mcp` HTTP, `/mcp-sse` SSE) ### Після випуску - [ ] Запустіть `/capture-release-evidences-cc` (навичка Claude Code) - Створює знімки екрана/записи нових функцій у форматі WebP - Додає їх до приміток до випуску / допису в блозі - [ ] Оновіть GitHub Discussions / Discord оголошенням про випуск - [ ] Створіть етап для наступної версії - [ ] Якщо випуск критичний: закріпіть обговорення або опублікуйте запис у `news.json` для банера в застосунку ### Умови публічного запуску Radar Оголошення Radar навмисно закомічено з `active: false`. Активація є окремою зміною після надання підтверджень для кожного пункту нижче: - [ ] Усі послідовні PR Radar об’єднано, а CI для вершини випуску успішно пройдено - [ ] Розгорніть і виконайте димову перевірку маршрутів OSS Radar, залишивши `RADAR_ENABLED` вимкненим за замовчуванням - [ ] Виконайте димову перевірку `GET /planos`, `/termos`, `/privacidade` та `/reembolso` на визначеному хості Radar - [ ] Запишіть ідентифікаційні дані/контактну інформацію/адресу оператора та схвалений власником результат юридичної перевірки в приватному сервісі - [ ] Перевірте Stripe Checkout і підписаний webhook лише в тестовому режимі - [ ] Перевірте одну доставку зашифрованого транзакційного електронного листа зі схваленого відправника/домену - [ ] Підтвердьте відновлення з резервної копії та один контрольований дослідницький запуск з обмеженим бюджетом - [ ] Схваліть політику перевірки BRL/PIX до приймання підтверджень пожертв - [ ] Увімкніть публічний Checkout лише після виконання попередніх умов, а потім активуйте новий `news.json` ID - [ ] Переконайтеся, що банер на головній сторінці використовує локалізований текст, а новий ID знову з’являється після відхилення старішого ID ## Smoke-тест вбудованих сервісів (v3.8.4+) Перед випуском будь-якого релізу, що містить зміни у вбудованих сервісах, перевірте: ### Запуск із новою БД (виявляє конфлікти міграцій — додано після термінового виправлення v3.8.4) - [ ] `DATA_DIR=$(mktemp -d) npm start &` — зачекайте 10 с на запуск - [ ] `curl -s http://127.0.0.1:20128/api/services/9router/status | jq '.tool'` повертає `"9router"` (НЕ 404, НЕ 500). Це підтверджує, що міграцію `071_services.sql` застосовано, а рядок додано. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(version_manager);" | grep -E "provider_expose|logs_buffer_path|last_sync_at"` повертає 3 рядки. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(webhooks);" | grep -E "kind|metadata_encrypted"` повертає 2 рядки (підтверджує застосування `070_webhooks_kind_metadata.sql`). - [ ] `node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.ts` виконується успішно — захищає від майбутніх конфліктів. ### 9Router - [ ] `POST /api/services/9router/install` повертає 200 з `installedVersion` менш ніж за 2 хв - [ ] `POST /api/services/9router/start` повертає 200 і `state: "running"` менш ніж за 30 с - [ ] `GET /api/services/9router/status` повідомляє `health: "healthy"` - [ ] `POST /v1/chat/completions` із `"model": "9router/auto/..."` повертає 200 (наскрізна маршрутизація через 9Router) - [ ] `GET /dashboard/providers/services/9router/embed/dashboard` відображає нативний інтерфейс 9Router усередині проксі (без прямого iframe на `127.0.0.1:port`) - [ ] `POST /api/services/9router/rotate-key` повертає `{ keyRotated: true }`, а сервіс коректно перезапускається - [ ] `POST /api/services/9router/stop` повертає 200 і `state: "stopped"` - [ ] `GET /api/services/9router/logs?tail=50` повертає потік SSE з подією `snapshot`, що містить останні рядки - [ ] Встановлення в середовищі без `npm` у PATH повертає 500 зі зрозумілим повідомленням про помилку (без трасування стека) ### CLIProxyAPI - [ ] `POST /api/services/cliproxy/install` повертає 200 менш ніж за 2 хв - [ ] `POST /api/services/cliproxy/start` повертає 200 і `state: "running"` менш ніж за 30 с - [ ] `GET /api/services/cliproxy/status` повідомляє `health: "healthy"` - [ ] `POST /api/services/cliproxy/stop` повертає 200 і `state: "stopped"` - [ ] `GET /api/services/cliproxy/logs?tail=50` повертає потік SSE ### Регресія безпеки - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/9router/start` повертає `403 LOCAL_ONLY` - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/cliproxy/start` повертає `403 LOCAL_ONLY` - [ ] Відповіді з помилками від `/api/services/*` не містять `err.stack` або абсолютних шляхів до файлів ## Перевірки для v3.8.0+ Перед випуском будь-якого релізу v3.8.x перевірте також такі пункти: - [ ] `omniroute --tray` запускається на macOS (systray2 встановлено в `~/.omniroute/runtime/`) - [ ] `omniroute --tray` запускається на Linux (потрібна DISPLAY; коректна помилка, якщо її не задано) - [ ] `omniroute --tray` запускається на Windows (PowerShell NotifyIcon, без додаткових бінарних файлів) - [ ] `omniroute config tray enable` створює запис автозапуску; вимкнення видаляє його - [ ] `npm install -g omniroute@` виконує postinstall без аварійного завершення - [ ] Шлях оновлення зберігає необов’язкові залежності: `omniroute update --apply` і засіб автоматичного оновлення виконують `npm install -g … --include=optional`, щоб `optionalDependencies` (better-sqlite3, keytar, tls-client і стек SLM llmlingua: `@atjsh/llmlingua-2@2.0.5`, `js-tiktoken`) зберігалися після оновлення. Для рівня SLM ultra з `modelPath` також потрібна модель tinybert, яка автоматично завантажується до `${DATA_DIR}/models/llmlingua` під час першого використання. Потім postinstall (`scripts/build/colocateOptionals.mjs`) розміщує необов’язкове замикання SLM поруч у `dist/node_modules`, щоб воркер використовував ОДИН екземпляр `@huggingface/transformers` ^4.2.0 — автономне трасування включає лише transformers, але не динамічно імпортовані необов’язкові залежності, тож без цього воркер завантажував би llmlingua-2 із transformers кореневого рівня, а рівень SLM непомітно переходив би в режим fail-open. - [ ] `omniroute status` працює без `.env` (шлях токена CLI, лише loopback) - [ ] `curl http://localhost:20128/api/shutdown` повертає 401 (маршрут завжди захищений) - [ ] `curl -H "host: evil.com" http://localhost:20128/api/mcp/sse` повертає 401 (захист loopback) - [ ] Середовище виконання SQLite під час першого запуску визначається як `bundled` (вбудований бінарний файл дійсний для платформи) - [ ] Середовище виконання SQLite переходить на `runtime`, коли `node_modules/better-sqlite3` видалено - [ ] Розумний фільтр MCP стискає реальний вивід `playwright-mcp browser_snapshot` (скорочення ≥50%) - [ ] Усі 10 файлів `skills/omniroute*/SKILL.md` загальнодоступні через необроблену URL-адресу GitHub - [ ] Майстер початкового налаштування показує крок огляду рівнів «Як це працює» під час нового налаштування - [ ] Віджет покриття рівнів на головній панелі показує кількість налаштованих/активних рівнів --- ## Відгалуження 3.9.0 LTS (репетиція у 3.8.58) Після v3.8.59 наступною версією буде 3.9.0, а її вершина стане основою двох довготривалих гілок: `stable/v3` (лінійка v3 LTS, npm `latest`) і `develop` (v4, версію підвищено до 4.0.0, npm `nightly`). Модель гілок/каналів, пряме перенесення змін і мітки описані в [RELEASE_STRATEGY.md](./RELEASE_STRATEGY.md); план наведено в [ROADMAP](../../ROADMAP.md) (Фаза 3). Відгалуження виконується один раз; у 3.8.58 воно репетирується від початку до кінця у форку, а 3.8.59 завершується [контрольним списком GO/NO-GO](./LTS_GO_NO_GO.md). ### Пробний запуск (лише читання, безпечний у будь-який час) ```bash npm run release:dry-run-lts-cut # справжнє відгалуження: 3.9.0 з HEAD, попередній тег v3.8.59 npm run release:dry-run-lts-cut -- --from <3.9.0-tip> # зафіксувати вихідний коміт ``` `scripts/release/dry-run-lts-cut.mjs` нічого не виконує: він читає дані git і `gh` та виводить повну послідовність — передумови (вихідне посилання розпізнається, вихідний репозиторій має повну історію — неглибокий клон не може відправити `stable/v3`, сторона-одержувач відхиляє пакет на першому прищепленому батьківському коміті, тому спочатку виконуйте відгалуження з повного клону або `git fetch --unshallow origin` — попередній тег існує, `package.json` містить цільову версію, відкрито задачу `release-freeze`, немає відкритої задачі `Release branch not green` для наявної гілки випуску — для гілки, якої не існує, повідомляється `?` (невідомо), але ніколи не зелений статус — чергу Mergify `release` налаштовано (G11: `queue_rules`, `checks_timeout`, мітка `queue`), набір правил `release/*` усе ще блокує видалення та примусове відправлення, а `stable/v3` і `develop` ще не існують), два кроки створення гілок, які тригери неактивних робочих процесів і умови `if:` стають істинними (а які залишаються заблокованими змінною репозиторію або закріпленими за канонічним репозиторієм), очікувані dist-теги (`latest` → 3.9.0, `next` і `nightly` порожні) та відкочування. Код виходу `0` = `RESULT: READY`, `1` = не виконано блокувальну передумову (`✗`), `2` = помилка використання. `--advisory ` понижує перевірку до попередження (`!`), не приховуючи її. Виконайте пробний запуск справжнього відгалуження, поки заморожування випуску 3.9.0 ще діє — гілки створюються після тега й до того, як Фаза 12c зніме заморожування. ### Репетиція 3.8.58 (лише у форку) ```bash # 1. Пробний запуск на поточній вершині з параметрами репетиції npm run release:dry-run-lts-cut -- --target-version 3.8.58 --previous-tag v3.8.57 \ --advisory freeze,base-green # 2. Виконання для віддаленого ФОРКУ (origin або будь-який віддалений репозиторій, URL якого # вказує на канонічний репозиторій, відхиляється; кожен крок запитує підтвердження в терміналі) 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. Випробування неактивних робочих процесів у форку (workflow_dispatch там, де пробний запуск # повідомляє про прив’язку до канонічного репозиторію), після чого — відкочування 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 ``` Коміт підвищення версії develop створюється за допомогою низькорівневих засобів git (робоче дерево не змінюється) і підвищує версію в тих самих п’яти файлах, що й коміт відкриття циклу: `package.json`, `open-sse/package.json`, `electron/package.json`, `package-lock.json` і `docs/openapi.yaml`. Розділ `[4.0.0]` CHANGELOG та його локалізовані копії відкриваються в `develop` після цього, до першого PR у цій гілці. Скрипт ніколи не змінює dist-теги npm — репетируйте це з тестовим пакетом. ### Артефакт попереднього перегляду PR (зібрати один раз, просувати ті самі байти) `.github/workflows/preview-artifact.yml` збирає один робочий tarball із вершини PR і перевіряє саме цю збірку (частина (a) задачі #8084). Лише PR з того самого репозиторію; нічого не публікується. ```bash gh workflow run preview-artifact.yml -f pr_number= # або додайте мітку `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 # встановлення попередньої версії ``` Запуск виконує `npm ci`, `npm run build:release`, `npm run check:pack-artifact`, пакує tarball, запускає `npm run check:pack-boot` (несправжні секрети, тимчасовий каталог даних), повторно пакує та завершується помилкою, якщо дайджест не ідентичний, після чого записує `artifact-identity.json` (SHA вершини, SHA бази, хеш lock-файлу, платформа, архітектура, ABI Node, пакувальник, політика збірки — `scripts/release/artifact-identity.mjs`) і засвідчує tarball в окремому завданні. Просування попередньої версії означає встановлення цього tarball: ніколи не перебудовуйте його з вихідного коду. ### Відгалуження (3.9.0, після GO) 1. Рішення GO зафіксовано в [LTS_GO_NO_GO.md](./LTS_GO_NO_GO.md). 2. `npm run release:dry-run-lts-cut -- --from v3.9.0` виводить `RESULT: READY`. 3. Створіть гілки в `origin` вручну за допомогою команд, які виводить пробний запуск — скрипт відмовляється виконувати відправлення до `origin`. Щоб повторно використати перевірений коміт develop, спочатку виконайте репетицію з `--execute` на вершині 3.9.0 для свого форку; вона виведе обидва SHA, після чого ті самі коміти можна відправити: ```bash git push origin :refs/heads/stable/v3 :refs/heads/develop ``` 4. Захистіть `stable/v3` і `develop` (набори правил + черга злиття) до потрапляння першого PR. 5. Неактивні робочі процеси вмикаються після створення відповідних гілок: `forward-port.yml` (push до `stable/v3`), `validate-stable-pr.yml` (PR до `stable/v3`) і `nightly-v4-build.yml` (збирає `develop`). Перед запуском налаштуйте секрет репозиторію `secrets.FORWARD_PORT_TOKEN` (щоб CI запускався для PR із перенесенням змін уперед); нічна публікація залишатиметься вимкненою, доки власник не встановить для змінної репозиторію `vars.NIGHTLY_PUBLISH` значення `true` і npm Trusted Publishing не дозволить `nightly-v4-build.yml`. Канал визначається за допомогою `scripts/release/dist-tag.mjs` — того самого засобу визначення, який використовує `npm-publish.yml`. 6. Перевірте канали: `npm view omniroute dist-tags --json` має показувати `latest` = 3.9.0 і відсутність `next` / `nightly`, доки не буде опубліковано v4. 7. За потреби виконайте відкат: `git push origin --delete refs/heads/stable/v3 refs/heads/develop` і `npm dist-tag add omniroute@3.8.59 latest`. --- ## Відкочування Якщо випуск має критичну проблему: 1. `gh release edit vX.Y.Z --prerelease` (позначає як не найновіший) 2. `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` (лише якщо користувачі ще не почали його використовувати) 3. Або: термінове виправлення в `release/vX.Y.0` → випуск виправлення `vX.Y.(Z+1)` 4. Негайно повідомте про це в GitHub Discussions і Discord ## Суворі правила - Ніколи не робіть коміти безпосередньо в `main` - Ніколи не використовуйте `git push --force` для гілок `main` або `release/*` - Ніколи не пропускайте хуки Husky (`--no-verify`) - Ніколи не додавайте до комітів секрети, облікові дані або файли `.env` - Покриття має залишатися ≥60/60/60/60 (оператори/рядки/функції/гілки) - Завжди додавайте або оновлюйте тести під час зміни робочого коду в `src/`, `open-sse/`, `electron/` або `bin/` ## Автоматизована перевірка синхронізації Перед відкриттям PR запустіть локально перевірку синхронізації документації: ```bash npm run check:docs-sync ``` CI також виконує цю перевірку в `.github/workflows/ci.yml` (завдання lint).