# Release Checklist (Português (Portugal)) 🌐 **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-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) --- > **Última atualização:** 2026-08-28 — v3.8.51 > Fluxo de lançamento simplificado que tira partido das competências do Claude Code para a automatização. > > **Mantenha a fila/branch verde entre lançamentos:** consulte [RELEASE_GREEN.md](./RELEASE_GREEN.md) > (família `/green-prs` + `npm run check:release-green` + `/babysit` + execução noturna). Executar > isto periodicamente — e especialmente **antes** desta lista de verificação — faz com que o PR de lançamento comece verde. ## Resumo ```bash # 1. Incrementar a versão + gerar o CHANGELOG (competência) /version-bump-cc patch # ou minor/major # 2. Executar localmente a validação de qualidade npm run check # lint + testes npm run test:coverage # validação completa da cobertura (60/60/60/60) # 3. Compilar e executar testes de verificação npm run build npm run test:e2e # opcional, mas recomendado # 4. Gerar o lançamento (competência) /generate-release-cc # 5. Implementar (competência) /deploy-vps-both-cc # ou akamai-cc / local-cc # 6. Capturar evidências do lançamento (competência) /capture-release-evidences-cc ``` ## Publicação fidedigna no npm (predefinição desde a v3.8.51) — faseada a pedido, direta como alternativa Por predefinição, `npm-publish.yml` publica através da **Publicação fidedigna no npm (OIDC)**: a tarefa `stage-npm` (alojada no GitHub) troca o id-token do GitHub por uma credencial npm de curta duração para essa execução — sem qualquer token npm de longa duração nos segredos do repositório, sem pedido de 2FA e com proveniência anexada. Esta é a alternativa aprovada pelo npm, agora que os tokens que ignoram a 2FA estão a ser descontinuados; restaura o fluxo totalmente automático que o projeto tinha até à v3.8.48, mantendo simultaneamente a garantia WS1.3 (um token comprometido não pode publicar por si só — não existe qualquer token). **Configuração única (proprietário):** npmjs.com → pacote `omniroute` → Settings → _Trusted Publisher_ → GitHub: proprietário `diegosouzapw`, repositório `OmniRoute`, workflow `npm-publish.yml` (ambiente: nenhum). Enquanto esta configuração não existir, o passo automático falha com `ENEEDAUTH`: volte a acionar com `publish_mode=staged` (abaixo) ou `direct`. ### Publicação faseada (a pedido — `publish_mode=staged`) O workflow npm-publish deixa de publicar diretamente: inicia o tarball empacotado (`check:pack-boot`) e, em seguida, executa `npm stage publish` — os bytes exatos ficam reservados no registo, **não sendo instaláveis** até que o proprietário dê a sua aprovação. A validação humana por 2FA passou para DEPOIS da prova, e não antes. **Fluxo do proprietário depois de o workflow ficar verde:** 1. `npm stage list omniroute` — encontre o id da fase (também apresentado no resumo do workflow). 2. Verifique os bytes faseados (recomendado): `npm stage download ` e, em seguida, instale o tarball transferido num prefixo temporário e inicie-o (`npm run check:pack-boot` automatiza em CI a mesma validação de empacotar→instalar→iniciar). 3. `npm stage approve ` — o pedido de 2FA É a publicação. `npm stage reject ` descarta-a. 4. Rede de segurança pós-publicação: o verificador pós-publicação (WS1.4 do plano da v3.8.49) instala a versão publicada a partir do registo público num contentor limpo e inicia-a. **Alternativa de emergência:** `workflow_dispatch` com `publish_mode=direct` restaura a execução imediata legada de `npm publish` (utilize apenas se o próprio faseamento não funcionar corretamente; registe o motivo). **Reforço único (proprietário, npmjs.com):** configure o Trusted Publisher para `omniroute` no modo exclusivo de faseamento, para que um token de longa duração comprometido não possa executar `npm publish` diretamente a partir de lado algum — a CI só pode efetuar o faseamento; apenas a 2FA do proprietário permite o lançamento. **Procedimento para artefactos com problemas (inalterado):** `npm deprecate omniroute@ " — use "` como reação predefinida (demora minutos e é reversível); utilize `npm unpublish` apenas dentro da janela de 72 horas/sem dependentes e nunca como primeira medida. Docker: nunca reescreva uma etiqueta de versão — a reversão consiste em voltar a apontar `latest` para o último digest válido. **Docker Hub `latest` (obrigatório em cada publicação SemVer estável):** o workflow `docker-publish` tem de atribuir etiquetas a **ambos** `X.Y.Z` e, quando `should-promote-latest.sh` confirmar que esta é a SemVer estável mais elevada, `:latest` com o **mesmo digest**. Após a tarefa: o digest `latest` no Hub é igual ao novo digest SemVer e `last_updated` foi atualizado. Não deixe `:latest` numa compilação mais antiga enquanto as notas de lançamento mencionam correções que apenas existem no git. Os inícios rápidos do Compose utilizam `:latest`; o GitOps deve continuar a fixar `X.Y.Z`. Consulte [Canais de lançamento do Docker](../guides/DOCKER_GUIDE.md#release-channels) e #10317. ## Via Rápida de Hotfix (etiqueta `hotfix`) Um PR com a etiqueta `hotfix` ignora a matriz de CI pesada (E2E com 9 shards, controlo progressivo da cobertura, quality-gate, quality-extended) e mantém as verificações rápidas e de sinal elevado: compilação, shards unitários, integração, vitest, lint/verificação de tipos, sincronização da documentação, `check:pack-artifact` e o teste básico de arranque do tarball (`check:pack-boot`). Objetivo: ficar verde em ≤15 min, em vez de ~33 min. **Política de entrada — os quatro requisitos são obrigatórios (baseada nas vias de emergência do Chromium/VS Code/Node):** 1. **Gravidade**: a produção está inoperacional — um artefacto publicado falha no arranque / uma correção de segurança / todos os utilizadores da versão são afetados. «Importante» não significa «inoperacional». 2. **Autoridade**: apenas o proprietário do repositório aplica a etiqueta `hotfix`. A etiqueta É a aprovação — nunca deve ser aplicada pelo próprio num PR de campanha. 3. **Evidências**: o corpo do PR contém uma ligação para a execução pesada anterior totalmente verde (a suite que as tarefas ignoradas voltariam a validar), bem como para o teste da própria correção, primeiro a falhar e depois a passar. 4. **Âmbito**: apenas cherry-pick — a correção mínima, sem refatorações nem alterações adicionais. A superfície de cobertura/controlo progressivo ignorada é novamente validada pela próxima execução completa no ramo de lançamento (estado verde contínuo do lançamento) — a via ignora a ESPERA, nunca a validação. As diferenças apenas em testes (todos os ficheiros em `tests/`, nenhum em `tests/e2e/`) ignoram automaticamente a matriz E2E, sem qualquer etiqueta. ## Lista de Verificação Detalhada ### Pré-lançamento - [ ] Todos os PRs destinados a este lançamento estão integrados em `release/vX.Y.0` - [ ] Todos os itens em aberto no Linear/problemas relativos a esta versão estão fechados ou foram movidos para o marco seguinte - [ ] CI sem erros no ramo `release/vX.Y.0` - [ ] Nenhum marcador `TODO(release)` no código: `grep -r "TODO(release)" src/ open-sse/` - [ ] Imagem base do Docker atualizada (atualmente `node:24.15.0-trixie-slim`) ### Versão e Registo de Alterações - [ ] Executar `/version-bump-cc ` (competência do Claude Code) - Atualiza as versões em `package.json`, `electron/package.json` - Regenera `CHANGELOG.md` a partir dos commits do git desde a última etiqueta - Atualiza os emblemas de README.md - [ ] Rever manualmente CHANGELOG.md e corrigir as mensagens de commit, se necessário - [ ] Garantir que a secção semver mais recente em `CHANGELOG.md` corresponde à versão em `package.json` - [ ] Manter `## [Unreleased]` como a primeira secção do registo de alterações para trabalhos futuros - [ ] Atualizar `docs/openapi.yaml` → `info.version` tem de corresponder à versão em `package.json` ### Qualidade do Código - [ ] `npm run lint` — 0 erros (os avisos já existiam) - [ ] `npm run typecheck:core` — sem erros - [ ] `npm run typecheck:noimplicit:core` — sem erros (estrito) - [ ] `npm run check:cycles:ratchet` — dentro do limite máximo fixo de `metrics.cycles` (o comando simples `check:cycles` termina com o código 1 ao encontrar qualquer ciclo, #15281) - [ ] `npm run check:any-budget:t11` — dentro do orçamento - [ ] `npm run check:route-validation:t06` — sem erros - [ ] `npm run check:node-runtime` — limite mínimo de execução suportado cumprido (`>=22.22.2 <23`, `>=24.0.0 <27`, de acordo com `SUPPORTED_NODE_RANGE` em `src/shared/utils/nodeRuntimeSupport.ts`; alinhado com `engines` em `package.json`) ### Testes - [ ] `npm run test:unit` — aprovado - [ ] `npm run test:vitest` — aprovado (servidor MCP, autoCombo, cache) - [ ] `npm run test:coverage` — limite de 60/60/60/60 cumprido (instruções/linhas/funções/ramos) - [ ] `npm run test:integration` — aprovado (se as alterações afetarem a BD/os processadores) - [ ] `npm run test:combo:matrix` — aprovado (matriz de estratégias combinadas: comprova, de forma determinística, as decisões de seleção das 19 estratégias públicas de encaminhamento; executar ao alterar o encaminhamento combinado, a resolução de estratégias ou a lógica de contingência) - [ ] `RUN_COMBO_LIVE=1 npm run test:combo:live` — **opcional/manual** (teste rápido controlado com serviços externos reais; obtém um instantâneo da BD em modo só de leitura do VPS `root@192.168.0.15`; acede a fornecedores reais e consome créditos; nunca é executado em CI; é ignorado corretamente sem a ativação) - [ ] `npm run test:combo:live:vps` — **opcional/manual** (teste rápido em ambiente real no VPS da Fase 3: 7 cenários HTTP no servidor `.15` ativo através de Node ESM simples; requer `ssh root@192.168.0.15`; cria/elimina apenas combinações `__live_test__*`; acede a fornecedores reais; nunca é executado em CI) - [ ] `npm run test:e2e` — aprovado (alterações à IU) - [ ] `npm run test:protocols:e2e` — aprovado (alterações a MCP/A2A) - [ ] `npm run test:ecosystem` — aprovado ### Hooks (Validados pelo Husky) Os hooks do Husky encontram-se em `.husky/` e são executados automaticamente durante as operações do git. - **pre-commit:** `npx lint-staged + node scripts/check/check-docs-sync.mjs + npm run check:any-budget:t11` - **pre-push:** verificações rápidas e determinísticas — `npm run check:any-budget:t11 && npm run check:tracked-artifacts` (ativado em 2026-06-13). Exclui intencionalmente `test:unit` (lento; abrangido pela tarefa `test-unit` da CI). - Executar manualmente `npm run test:unit` antes de enviar ramos de lançamento. Se um hook falhar: corrigir o problema subjacente; não o contornar com `--no-verify`. ### Commits Convencionais Todos os commits destinados ao lançamento têm de seguir o formato `type(scope): subject`. **Tipos válidos:** `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `style`, `ci` **Âmbitos válidos:** `db`, `sse`, `oauth`, `dashboard`, `api`, `cli`, `docker`, `ci`, `mcp`, `a2a`, `memory`, `skills`, `cloud-agent`, `guardrails`, `compression`, `auto-combo`, `resilience`, `providers`, `executors`, `translator`, `domain`, `authz` Alterações incompatíveis: adicionar o rodapé `BREAKING CHANGE:` ou `!` após o âmbito (por exemplo, `feat(api)!: drop /v0`). ### Documentação - [ ] `npm run check:docs-sync` é aprovado (executado automaticamente pelo pre-commit) - [ ] `npm run check:docs-all` é aprovado (agregador: docs-sync + docs-counts + env-doc-sync + deprecated-versions + doc-links) - [ ] `npm run check:env-doc-sync` termina com o código 0 — o contrato de ambiente entre o código ↔ `.env.example` ↔ `docs/reference/ENVIRONMENT.md` está intacto - [ ] `npm run check:doc-links` termina com o código 0 — não existem referências markdown internas quebradas após a reestruturação - [ ] `docs/architecture/ARCHITECTURE.md` revisto para detetar divergências no armazenamento/ambiente de execução - [ ] `docs/guides/TROUBLESHOOTING.md` revisto para detetar divergências nas variáveis de ambiente e na operação - [ ] Se `.env.example` tiver sido alterado: atualizar `docs/reference/ENVIRONMENT.md` - [ ] Se uma nova funcionalidade tiver uma IU: `docs/guides/USER_GUIDE.md` menciona-a - [ ] Se uma nova funcionalidade tiver uma API: atualizar `docs/reference/API_REFERENCE.md` + `docs/openapi.yaml` - [ ] Se uma nova funcionalidade for um módulo: existe um ficheiro dedicado `docs/.md` - [ ] Se houver uma alteração incompatível: `docs/guides/TROUBLESHOOTING.md` contém uma nota de migração ### i18n - [ ] `npm run i18n:check` termina com o código 0 — o estado das traduções (`.i18n-state.json`) está sincronizado com os documentos de origem (sem fontes divergentes no modo estrito; os avisos no modo de aviso são aceitáveis para retoques de última hora na documentação, mas o resultado deve ser 0 antes da criação da etiqueta) - [ ] `npm run i18n:check-ui-coverage` termina com o código 0 — todas as regiões da IU atingem ou excedem o limite mínimo de cobertura de 80% - [ ] `npm run i18n:sync-ui:dry` comunica 0 chaves em falta nas 66 regiões de destino (todas as regiões exceto a origem `en`) - [ ] Se os documentos de origem em inglês tiverem sido alterados, executar `npm run i18n:run` (requer `OMNIROUTE_TRANSLATION_API_KEY` em `.env`) antes da criação da etiqueta - [ ] As contribuições de tradução podem ser adiadas para o lançamento seguinte se forem menores (registar no CHANGELOG) ### Migrações da Base de Dados - [ ] Se `src/lib/db/migrations/` tiver novos ficheiros: - [ ] Cada migração é idempotente (`CREATE TABLE IF NOT EXISTS`, etc.) - [ ] As migrações estão encapsuladas em transações - [ ] Estão numeradas corretamente (sem lacunas na sequência) - [ ] Testar numa instalação nova: eliminar `~/.omniroute/omniroute.db` e executar `npm run dev` - [ ] Testar numa instalação existente: criar uma cópia de segurança da BD, executar a migração e verificar o esquema - [ ] Ficheiros WAL (`-wal`, `-shm`) tratados corretamente se a migração reescrever tabelas ### Catálogo de fornecedores (validado pelo Zod) - [ ] O esquema Zod em `src/shared/constants/providers.ts` é válido no momento do carregamento - [ ] Todos os fornecedores têm os campos obrigatórios (`id`, `label`, `kind`, etc.) - [ ] `freeNote` fornecido para novos fornecedores gratuitos - [ ] Os fornecedores OAuth têm `oauthConfig` registado em `src/lib/oauth/constants/oauth.ts` - [ ] Se for adicionado um novo fornecedor: executor correspondente em `open-sse/executors/` - [ ] Se o formato não for OpenAI: tradutor em `open-sse/translator/` - [ ] Modelos registados em `open-sse/config/providerRegistry.ts` - [ ] Os testes unitários em `tests/unit/` abrangem a classificação e o encaminhamento dos fornecedores ### Ambiente de trabalho (Electron) Se `electron/` tiver sido alterado: - [ ] `npm run electron:smoke:packaged` é executado com êxito - [ ] Compilações testadas para, pelo menos, um de `:win`, `:mac`, `:linux` - [ ] Os certificados de assinatura de código não expiraram (se aplicável) - [ ] A versão em `electron/package.json` corresponde à de `package.json` na raiz - [ ] O ponteiro do canal de atualização automática foi atualizado se a versão for lançada para `stable` ### Estrutura da compilação O repositório utiliza três diretórios de saída distintos — nunca os confunda: | Diretório | Finalidade | Controlado? | | --------- | ------------------------------------------------------------- | ----------------------- | | `src/` | Código-fonte da aplicação (TypeScript / TSX) | Sim | | `.build/` | Intermédios da compilação — saída de `next build` (`distDir`) | Não (ignorado pelo git) | | `dist/` | Pacote npm distribuível — montado por `assembleStandalone` | Não (ignorado pelo git) | > **Nota para o operador:** o diretório de imagens no VPS remoto continua a ser `/usr/lib/node_modules/omniroute/app/`. > Apenas a saída de compilação **dentro do repositório** foi movida (`app/` → `dist/`). As competências de implementação sincronizam > o conteúdo de `dist/` por rsync para o diretório remoto `app/` — não são necessárias alterações aos caminhos do VPS. **Fluxo de compilação única:** ``` npm run build:release └─ rm -rf .build dist (limpeza) └─ next build → .build/next/ (intermédios) └─ assembleStandalone (copia standalone + static + public + componentes nativos → dist/) └─ escreve dist/BUILD_SHA (sentinela HEAD) ``` NÃO execute `npm run build` seguido de um `npm run build:cli` separado para a implementação — utilize `npm run build:release`, que efetua uma recompilação limpa e cria a sentinela num único comando. ### Validação de artefactos - [ ] `npm run build:release` é concluído com êxito e `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] `npm run check:pack-artifact` sem problemas — sem `app.__qa_backup`, `scripts/scratch`, `package-lock.json` ou outros resíduos locais - [ ] `dist/server.js` existe após a compilação - [ ] Teste rápido local opcional do ambiente empacotado: `npm run dev:candidate -- validate`, após `npm run dev:candidate -- build`, inicia o tarball empacotado num `DATA_DIR` isolado e verifica `/api/health` + `/v1/models` (consulte o [Percurso recomendado para contribuições](CONTRIBUTION_GOLDEN_PATH.md#local-candidate-loop)) ### Etiquetagem e lançamento - [ ] Execute `/generate-release-cc` (competência do Claude Code): - Cria a etiqueta `vX.Y.Z` - Envia a etiqueta e o ramo - Abre uma versão no GitHub com o registo de alterações no corpo - Anexa os instaladores do Electron (se tiverem sido compilados) - [ ] Ou manualmente: ```bash git tag -a vX.Y.Z -m "Lançamento vX.Y.Z" git push origin vX.Y.Z gh release create vX.Y.Z --notes-from-tag ``` ### Implementação As competências de implementação utilizam o fluxo rsync simplificado — sem `npm pack` nem `npm i -g`: - [ ] Utilize a competência de implementação que corresponde ao destino: - `/deploy-vps-local-cc` — VPS local (192.168.0.15) - `/deploy-vps-akamai-cc` — VPS da Akamai (69.164.221.35) - `/deploy-vps-both-cc` — ambos - [ ] Antes da implementação, confirme que `dist/BUILD_SHA` == `git rev-parse --short HEAD` - [ ] A compilação tem de ser executada onde `node_modules` seja real (checkout principal ou worktree com `npm ci` executado — NÃO uma worktree com ligações simbólicas) - [ ] Efetue um teste rápido à instância implementada: - Abra `/dashboard/health` → verifique se a cadeia da versão corresponde ao lançamento - Execute um pedido a `/v1/chat/completions` utilizando um fornecedor conhecido - Verifique se `/api/monitoring/health` devolve disjuntores `CLOSED` - Confirme que os transportes MCP respondem (`/mcp` HTTP, `/mcp-sse` SSE) ### Pós-lançamento - [ ] Execute `/capture-release-evidences-cc` (competência do Claude Code) - Captura imagens/gravações WebP das novas funcionalidades - Anexa-as às notas de lançamento/publicação do blogue - [ ] Atualize as Discussões do GitHub/Discord com o anúncio do lançamento - [ ] Abra um marco para a próxima versão - [ ] Se for crítico: fixe a discussão ou publique em `news.json` para apresentar uma faixa na aplicação ### Critérios para o lançamento público do Radar O anúncio do Radar é intencionalmente submetido com `active: false`. A ativação é uma alteração separada depois de existirem evidências de todos os itens abaixo: - [ ] Todos os PRs empilhados do Radar foram integrados e a CI da ponta da versão está verde - [ ] Implemente e teste rapidamente as rotas OSS do Radar com `RADAR_ENABLED` ainda desativado por predefinição - [ ] Teste rapidamente `GET /planos`, `/termos`, `/privacidade` e `/reembolso` no anfitrião designado do Radar - [ ] Registe a identidade/contacto/morada do operador e a revisão jurídica aprovada pelo proprietário no serviço privado - [ ] Teste o Stripe Checkout e o webhook assinado apenas no modo de teste - [ ] Teste uma entrega de correio eletrónico transacional encriptado com o remetente/domínio aprovado - [ ] Comprove o restauro da cópia de segurança e uma execução de investigação supervisionada e com orçamento limitado - [ ] Aprove a política de revisão de BRL/PIX antes de aceitar comprovativos de donativos - [ ] Ative o Checkout público apenas após os critérios anteriores e, em seguida, ative o novo ID de `news.json` - [ ] Verifique se a faixa da página inicial utiliza texto localizado e se um novo ID reaparece depois de um ID anterior ser dispensado ## Teste rápido dos Serviços Incorporados (v3.8.4+) Antes de disponibilizar qualquer versão que inclua alterações aos serviços incorporados, verifique: ### Arranque com BD nova (deteta colisões de migrações — adicionado após a correção urgente da v3.8.4) - [ ] `DATA_DIR=$(mktemp -d) npm start &` — aguarde 10 s pelo arranque - [ ] `curl -s http://127.0.0.1:20128/api/services/9router/status | jq '.tool'` devolve `"9router"` (NÃO 404, NÃO 500). Confirma que a migração `071_services.sql` foi aplicada e que a linha foi inserida. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(version_manager);" | grep -E "provider_expose|logs_buffer_path|last_sync_at"` devolve 3 linhas. - [ ] `sqlite3 $DATA_DIR/storage.sqlite "PRAGMA table_info(webhooks);" | grep -E "kind|metadata_encrypted"` devolve 2 linhas (valida que `070_webhooks_kind_metadata.sql` foi aplicada). - [ ] `node --import tsx/esm --test tests/unit/db/no-migration-collisions.test.ts` é executado com êxito — protege contra colisões futuras. ### 9Router - [ ] `POST /api/services/9router/install` devolve 200 com `installedVersion` em menos de 2 min - [ ] `POST /api/services/9router/start` devolve 200 e `state: "running"` em menos de 30 s - [ ] `GET /api/services/9router/status` comunica `health: "healthy"` - [ ] `POST /v1/chat/completions` com `"model": "9router/auto/..."` devolve 200 (encaminhamento de extremo a extremo através do 9Router) - [ ] `GET /dashboard/providers/services/9router/embed/dashboard` apresenta a interface nativa do 9Router dentro do proxy (sem iframe direto para `127.0.0.1:port`) - [ ] `POST /api/services/9router/rotate-key` devolve `{ keyRotated: true }` e o serviço reinicia corretamente - [ ] `POST /api/services/9router/stop` devolve 200 e `state: "stopped"` - [ ] `GET /api/services/9router/logs?tail=50` devolve um fluxo SSE com um evento `snapshot` que contém linhas recentes - [ ] A instalação num ambiente sem `npm` em PATH devolve 500 com uma mensagem de erro clara (sem rastreio da pilha) ### CLIProxyAPI - [ ] `POST /api/services/cliproxy/install` devolve 200 em menos de 2 min - [ ] `POST /api/services/cliproxy/start` devolve 200 e `state: "running"` em menos de 30 s - [ ] `GET /api/services/cliproxy/status` comunica `health: "healthy"` - [ ] `POST /api/services/cliproxy/stop` devolve 200 e `state: "stopped"` - [ ] `GET /api/services/cliproxy/logs?tail=50` devolve um fluxo SSE ### Regressão de segurança - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/9router/start` devolve `403 LOCAL_ONLY` - [ ] `curl -H "X-Forwarded-For: 1.2.3.4" http://localhost:20128/api/services/cliproxy/start` devolve `403 LOCAL_ONLY` - [ ] As respostas de erro de `/api/services/*` não contêm `err.stack` nem caminhos absolutos de ficheiros ## Verificações da v3.8.0+ Antes de disponibilizar qualquer versão v3.8.x, verifique estes itens adicionais: - [ ] `omniroute --tray` arranca no macOS (systray2 instalado em `~/.omniroute/runtime/`) - [ ] `omniroute --tray` arranca no Linux (requer DISPLAY; erro claro caso não esteja definido) - [ ] `omniroute --tray` arranca no Windows (PowerShell NotifyIcon, sem binários adicionais) - [ ] `omniroute config tray enable` cria uma entrada de arranque automático; a desativação remove-a - [ ] `npm install -g omniroute@` executa o pós-instalação sem terminar com um erro fatal - [ ] O processo de atualização mantém as dependências opcionais: `omniroute update --apply` e o atualizador automático executam `npm install -g … --include=optional` para que as `optionalDependencies` (better-sqlite3, keytar, tls-client e a pilha SLM do llmlingua: `@atjsh/llmlingua-2@2.0.5`, `js-tiktoken`) sejam preservadas após uma atualização. O nível SLM ultra `modelPath` também requer o modelo tinybert, transferido automaticamente para `${DATA_DIR}/models/llmlingua` na primeira utilização. O pós-instalação (`scripts/build/colocateOptionals.mjs`) coloca depois o fecho opcional do SLM em `dist/node_modules`, para que o worker resolva uma ÚNICA instância de `@huggingface/transformers` ^4.2.0 — o rastreio autónomo inclui apenas transformers, não as dependências opcionais importadas dinamicamente, pelo que, sem isto, o worker carregaria llmlingua-2 com a versão de transformers da raiz e o nível SLM passaria silenciosamente para o modo aberto em caso de falha. - [ ] `omniroute status` funciona sem `.env` (caminho do token da CLI, apenas loopback) - [ ] `curl http://localhost:20128/api/shutdown` devolve 401 (rota sempre protegida) - [ ] `curl -H "host: evil.com" http://localhost:20128/api/mcp/sse` devolve 401 (proteção de loopback) - [ ] O runtime do SQLite é resolvido como `bundled` na primeira execução (binário incluído válido para a plataforma) - [ ] O runtime do SQLite passa para `runtime` quando `node_modules/better-sqlite3` é eliminado - [ ] O filtro MCP inteligente comprime a saída real de `playwright-mcp browser_snapshot` (redução ≥50%) - [ ] Todos os 10 ficheiros `skills/omniroute*/SKILL.md` estão acessíveis publicamente através de um URL raw do GitHub - [ ] O assistente de configuração inicial apresenta o passo de visita guiada aos níveis "Como funciona" numa configuração nova - [ ] O widget de cobertura dos níveis do painel inicial apresenta as contagens de níveis configurados/ativos --- ## Corte da versão 3.9.0 LTS (ensaiado na 3.8.58) Após a v3.8.59, a versão seguinte é a 3.9.0, e a respetiva ponta dá origem a dois ramos de longa duração: `stable/v3` (a linha LTS v3, npm `latest`) e `develop` (v4, atualizada para 4.0.0, npm `nightly`). O modelo de ramos/canais, o forward-port e as etiquetas encontram-se em [RELEASE_STRATEGY.md](./RELEASE_STRATEGY.md); o plano encontra-se no [ROADMAP](../../ROADMAP.md) (Fase 3). O corte é executado uma única vez; a versão 3.8.58 ensaia-o de ponta a ponta num fork, e a 3.8.59 termina com a [lista de verificação GO/NO-GO](./LTS_GO_NO_GO.md). ### Simulação (só de leitura, segura em qualquer altura) ```bash npm run release:dry-run-lts-cut # o corte real: 3.9.0 a partir de HEAD, etiqueta anterior v3.8.59 npm run release:dry-run-lts-cut -- --from <3.9.0-tip> # fixa o commit de origem ``` `scripts/release/dry-run-lts-cut.mjs` não executa nada: lê o git e o `gh` e apresenta a sequência completa — pré-condições (a origem é resolvida, o repositório de origem tem o histórico completo — um clone superficial não consegue enviar `stable/v3`, o lado recetor rejeita o pacote no primeiro progenitor enxertado, pelo que deve efetuar o corte a partir de um clone completo ou executar primeiro `git fetch --unshallow origin` — a etiqueta anterior existe, `package.json` tem a versão de destino, existe um problema `release-freeze` aberto, não existe nenhum problema `Release branch not green` aberto num ramo de lançamento existente — um ramo que não existe apresenta `?` desconhecido, nunca verde — a fila `release` do Mergify está configurada (G11: `queue_rules`, `checks_timeout`, etiqueta `queue`), o conjunto de regras `release/*` continua a bloquear a eliminação e o push forçado, e `stable/v3` e `develop` ainda não existem), os dois passos relativos aos ramos, os acionadores dos fluxos de trabalho inativos e as condições `if:` que se tornam verdadeiras (e quais permanecem bloqueadas por uma variável do repositório ou fixadas ao repositório canónico), as dist-tags esperadas (`latest` → 3.9.0, `next` e `nightly` vazias) e a reversão. Código de saída `0` = `RESULT: READY`, `1` = falha de uma pré-condição bloqueante (`✗`), `2` = erro de utilização. `--advisory ` rebaixa uma verificação para aviso (`!`) sem a ocultar. Execute a simulação do corte real enquanto o congelamento do lançamento da versão 3.9.0 ainda estiver ativo — os ramos são criados após a etiqueta e antes de a Fase 12c levantar o congelamento. ### Ensaio da versão 3.8.58 (apenas num fork) ```bash # 1. Simulação na ponta atual com parâmetros de ensaio npm run release:dry-run-lts-cut -- --target-version 3.8.58 --previous-tag v3.8.57 \ --advisory freeze,base-green # 2. Executar num remoto de um FORK (origin, ou qualquer remoto cujo URL seja o do repositório # canónico, é recusado; cada passo solicita confirmação no 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. Exercitar os fluxos de trabalho inativos no fork (workflow_dispatch quando a simulação # indicar uma fixação ao repositório canónico) e, em seguida, reverter 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 ``` O commit de atualização de develop é criado com os mecanismos internos do git (sem tocar na árvore de trabalho) e atualiza os mesmos cinco ficheiros que um commit de abertura de ciclo: `package.json`, `open-sse/package.json`, `electron/package.json`, `package-lock.json` e `docs/openapi.yaml`. A secção `[4.0.0]` do CHANGELOG e os respetivos equivalentes i18n são abertos posteriormente em `develop`, antes do seu primeiro PR. O script nunca altera as dist-tags do npm — ensaie-as num pacote descartável. ### Artefacto de pré-visualização do PR (compilar uma vez, promover exatamente os mesmos bytes) `.github/workflows/preview-artifact.yml` compila um único tarball de produção a partir da ponta de um PR e valida exatamente essa compilação (parte (a) do #8084). Apenas PRs do mesmo repositório; nada é publicado. ```bash gh workflow run preview-artifact.yml -f pr_number= # ou adicionar a etiqueta `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 # instalação de pré-visualização ``` A execução efetua `npm ci`, `npm run build:release`, `npm run check:pack-artifact`, empacota o tarball, executa `npm run check:pack-boot` (segredos falsos, diretório de dados efémero), volta a empacotar e falha se o resumo não for idêntico; em seguida, regista `artifact-identity.json` (SHA da ponta, SHA da base, hash do lockfile, plataforma, arquitetura, ABI do node, bundler, política de compilação — `scripts/release/artifact-identity.mjs`) e atesta o tarball numa tarefa separada. Promover uma pré-visualização significa instalar esse tarball: nunca voltar a compilar a partir do código-fonte. ### O corte (3.9.0, após GO) 1. GO registado em [LTS_GO_NO_GO.md](./LTS_GO_NO_GO.md). 2. `npm run release:dry-run-lts-cut -- --from v3.9.0` apresenta `RESULT: READY`. 3. Crie manualmente os ramos em `origin` com os comandos apresentados pela simulação — o script recusa-se a fazer push para `origin`. Para reutilizar um commit de develop revisto, execute primeiro o ensaio com `--execute` na ponta da versão 3.9.0 no seu fork; este apresenta ambos os SHAs, e os mesmos commits podem ser enviados: ```bash git push origin :refs/heads/stable/v3 :refs/heads/develop ``` 4. Proteja `stable/v3` e `develop` (conjuntos de regras + fila de integração) antes de o primeiro PR ser integrado. 5. Os workflows inativos são ativados pela existência dos branches: `forward-port.yml` (push para `stable/v3`), `validate-stable-pr.yml` (PRs para `stable/v3`) e `nightly-v4-build.yml` (compila `develop`). Antes da entrada em produção, defina o segredo de repositório `secrets.FORWARD_PORT_TOKEN` (para que o CI seja executado nos PRs de forward-port); a publicação nightly permanece desativada até o responsável definir a variável de repositório `vars.NIGHTLY_PUBLISH` como `true` e o npm Trusted Publishing aceitar `nightly-v4-build.yml`. A resolução do canal é efetuada por `scripts/release/dist-tag.mjs`, o mesmo resolvedor utilizado por `npm-publish.yml`. 6. Verifique os canais: `npm view omniroute dist-tags --json` mostra `latest` = 3.9.0 e nenhum `next` / `nightly` até à publicação da v4. 7. Reversão, se necessário: `git push origin --delete refs/heads/stable/v3 refs/heads/develop` e `npm dist-tag add omniroute@3.8.59 latest`. --- ## Reversão Se a versão tiver um problema crítico: 1. `gh release edit vX.Y.Z --prerelease` (assinala-a como não sendo a mais recente) 2. `git tag -d vX.Y.Z && git push --delete origin vX.Y.Z` (apenas se ainda não tiver sido adotada pelos utilizadores) 3. Ou: correção urgente em `release/vX.Y.0` → versão de correção `vX.Y.(Z+1)` 4. Comunicar imediatamente nas Discussões do GitHub e no Discord ## Regras Rígidas - Nunca efetuar commits diretamente em `main` - Nunca utilizar `git push --force` nos branches `main` ou `release/*` - Nunca ignorar os hooks do Husky (`--no-verify`) - Nunca incluir segredos, credenciais ou ficheiros `.env` em commits - A cobertura deve manter-se ≥60/60/60/60 (instruções/linhas/funções/branches) - Incluir ou atualizar sempre os testes ao alterar código de produção em `src/`, `open-sse/`, `electron/` ou `bin/` ## Verificação Automatizada de Sincronização Executar localmente a verificação de sincronização da documentação antes de abrir um PR: ```bash npm run check:docs-sync ``` O CI também executa esta verificação em `.github/workflows/ci.yml` (tarefa de lint).