Pular para o conteúdo
OmniRoute source

Quality-Gate System — Critical Assessment, Catalog and Replication Playbook (Português (Brasil))

Parte 1 — Veredito e Classificação de Maturidade

Seção intitulada “Parte 1 — Veredito e Classificação de Maturidade”

Nota geral: A− / “Avançado”. Entre os ~5–10% melhores projetos. O sistema implementa de forma independente vários padrões que o setor nomeia explicitamente — o que constitui o sinal mais forte de alinhamento (não copiamos uma checklist; convergimos para as práticas corretas).

Framework de referência Nossa situação Nota
OWASP DSOMM (5 níveis, 5 dimensões) Nível 3 sólido, alcançando o 4 em Intensidade de Testes e Profundidade Estática. A maioria das organizações está nos níveis 1–2. L3→L4
OpenSSF Scorecard (18 verificações) Passamos em CI-Tests, Code-Review, Dependency-Update-Tool, Fuzzing, SAST, Signed-Releases (proveniência), Token-Permissions, Vulnerabilities e Dangerous-Workflow. Lacunas: Branch-Protection em main DESATIVADA; algumas actions não estão fixadas em versões específicas. ~7–8/10
SLSA (4 níveis) npm publish --provenance + id-token: write + build hospedado pelo GitHub = L2, aproximando-se de L3. Falta um builder reforçado/hermético para L3+. L2→L3
SonarQube “Clean as You Code” Filosofia idêntica: o ratchet impede regressões (código novo não piora a métrica). Divergência: o Sonar recomenda poucas condições; temos ~46 gates (risco de fadiga). Alinhado, com ressalva
Padrão Quality-Ratchet Implementação de referência: ratchet + dedicatedGate + tightenSlack + --require-tighten + graceful-skip. Mais sofisticada que a maioria dos exemplos públicos. Exemplar
DORA 2024 Muito forte no eixo de estabilidade. Risco: gates pesados podem aumentar o lead time — mitigado pela separação de fast-gates, mas com uma lacuna de cobertura (consulte a Parte 2). Forte (estabilidade)
OWASP LLM Top 10 (2025) Cobrimos o risco nº 1 (injeção de prompt) com proteção em runtime + promptfoo (avaliação) + garak (red team). Ferramentas padrão do setor. Coberto
Testes de mutação Stryker executado todas as noites, limites 70/50, 8 módulos críticos. Consenso do setor (60% para código existente / 80% para código novo, execução noturna) — nós o superamos. Lacuna: a pontuação ainda não é um ratchet. Quase lá

Parte 2 — Avaliação crítica (pontos fortes + fraquezas sinceras)

Seção intitulada “Parte 2 — Avaliação crítica (pontos fortes + fraquezas sinceras)”
  1. Mecanismo de catraca com múltiplas métricas. O coração do sistema. 24 métricas em quality-baseline.json
    • 4 baselines dedicadas, cada uma com direção (up/down), tolerância (eps), margem (tightenSlack) e sinalizador dedicatedGate. O que é corrigido permanece corrigido — é o antídoto contra a entropia da base de código.
  2. Defesa em profundidade para a cadeia de suprimentos. SAST (CodeQL/Sonar) + segredos (gitleaks com useDefault) + SCA (osv/npm-audit/Trivy/Dependabot) + licenças + lockfile + SBOM + proveniência SLSA + Scorecard + reforço de segurança dos workflows (zizmor). Poucas bases de código têm uma pilha tão completa.
  3. Antídotos contra a Lei de Goodhart. Usar cobertura como meta é um antipadrão clássico (“quando a medida se torna a meta, ela deixa de ser uma boa medida”). Temos os contrapesos: testes de mutação (medem se o teste detecta o bug, não apenas se executa a linha), check-test-masking (impede o enfraquecimento de asserções para passar), limites mínimos de cobertura por módulo (forçam o teste de código de ALTO risco, não apenas das partes fáceis) e check-pr-evidence (Regra Rígida nº 18).
  4. Gates antialucinação/de consistência. Uma categoria rara e valiosa: check-known-symbols, check-fetch-targets, check-openapi-routes, check-docs-symbols garantem que a documentação, as especificações e os despachos por string apontem para símbolos existentes. Detectam a “deterioração” que lint/testes não detectam.
  5. Ciclo de vida de consultivo→bloqueante. Novos gates entram como consultivos (não bloqueiam merges enquanto amadurecem) e depois se tornam bloqueantes ao final do ciclo. Reduz o atrito sem diminuir o nível máximo.
  6. Ignorar de forma segura quando falta infraestrutura. Os scanners (--ratchet) encerram com exit 0 se o binário/rede falhar — a ausência de infraestrutura nunca bloqueia um PR legítimo. Engenharia madura.
  7. Cultura codificada. Regras Rígidas + trust-but-verify + lista de permissões obsoleta + gate de evidências transformam disciplina em verificação automatizada.
  1. 🔴 A divisão dos gates rápidos ainda deixa uma lacuna estrutural. quality.yml (PR→release/**) agora executa verificação de tipos, testes determinísticos rápidos e um build de produção consultivo para PRs de código, mas ainda não executa toda a superfície de PRs de release de ci.yml (catracas de cobertura, artefato de pacote, integração, E2E, SonarQube). A motivação (velocidade) é válida, mas o gate deve estar onde o merge acontece (shift-left). Maior correção estrutural pendente.
  2. 🟠 Risco de proliferação/fadiga de gates. ~46 gates + 25 jobs é MUITA COISA. O próprio Sonar alerta: condições demais causam “fadiga de gates” e debates sobre prioridades, com o risco de um gate ser ignorado. O DORA alerta que gates pesados aumentam o lead time. Mitigamos isso com níveis consultivos e catracas não absolutas, mas falta uma revisão periódica do ROI por gate (alguns microgates de sincronização de documentação podem ser consolidados).
  3. 🟠 A pontuação de mutação ainda não é uma catraca. O antídoto mais forte contra a manipulação da cobertura é consultivo. É o item pendente de maior valor (e já está 90% implementado).
  4. 🟡 Itens consultivos que deveriam bloquear (com o escopo correto). osv (vulnCount) e oasdiff são consultivos apesar das baselines congeladas. osv consultivo faz sentido (uma nova CVE em uma dependência antiga bloquearia um PR não relacionado) — mas há um meio-termo (bloquear apenas vulnerabilidades CRITICAL+corrigíveis, como fizemos com o Trivy). O fato de oasdiff ser consultivo significa que uma alteração que quebre o contrato pode passar.
  5. 🟡 A segurança em runtime ocorre apenas à noite. schemathesis/garak/promptfoo/chaos/k6 são executados à noite. Decisão correta (são lentos e precisam de um servidor ativo), mas um PR pode introduzir uma regressão na proteção contra injeção que só será detectada na noite seguinte.
  6. 🟡 A proteção de branch em main está DESATIVADA. BRANCH_LOCK_TOKEN protege as branches de release, mas a própria main está desprotegida. Scorecard/DSOMM penalizam isso. É necessária uma ação do responsável.
  7. 🟡 Configuração padrão do CodeQL; semgrep não codificado. A configuração padrão funciona (0 alertas), mas um codeql.yml versionado oferece mais controle; o semgrep é executado por meio de uma plataforma de nuvem externa, não versionada no repositório.

Parte 3 — Catálogo completo de pontos de controle de qualidade (portátil)

Seção intitulada “Parte 3 — Catálogo completo de pontos de controle de qualidade (portátil)”

As 12 categorias abaixo constituem o “sistema de qualidade” em formato reutilizável. Cada uma lista o objetivo (o que proteger), as ferramentas que usamos e o equivalente independente de ferramenta para replicação em qualquer stack.

1. Estilo e formatação (determinísticos, rápidos)

Seção intitulada “1. Estilo e formatação (determinísticos, rápidos)”
  • OmniRoute: Prettier + ESLint via lint-staged (pré-commit), 2 espaços/aspas duplas/100 colunas.
  • Genérico: um formatador com correção automática + um linter, executados no pré-commit nos arquivos preparados.
  • OmniRoute: typecheck:core (bloqueante) + typecheck:noimplicit:core (consultivo) + catraca de type-coverage em 92.17% + limite de any por arquivo.
  • Genérico: verificação estrita de tipos no CI + métrica de cobertura de tipos com catraca + limite de any/mecanismos de escape por arquivo.
  • OmniRoute: 2 executores sem sobreposição (nativo do Node + vitest), 8 shards, cobertura global de 60/60/60/60 + catraca de ~76% + 8 limites mínimos por módulo para módulos críticos + testes de propriedades noturnos + testes de mutação noturnos.
  • Genérico: executor(es) de testes + limite mínimo absoluto de cobertura (antizero) + catraca de cobertura (antirregressão) + limites mínimos por módulo para código de alto risco (anti-Goodhart) + testes baseados em propriedades para lógica pura + testes de mutação noturnos como medida real da qualidade dos testes.
  • OmniRoute: pr-test-policy (código de produção exige um teste), check-test-masking (bloqueia asserções enfraquecidas), pr-evidence (alegação de sucesso exige bloco de evidências), test-discovery (todo teste é coletado por um executor).
  • Genérico: barreira de “código novo ⇒ teste novo” + detector de asserções removidas/tautologias + exigência de evidências (TDD ou teste vivo) + garantia de que nenhum teste fique órfão fora dos globs.
  • OmniRoute: avisos do ESLint (3769↓), duplicação do jscpd (5.72%↓), complexidade ciclomática + máximo de linhas (1800↓), complexidade cognitiva do sonarjs (753↓), código morto/exports não utilizados do knip (339↓), tamanho de arquivo por arquivo (congelado, apenas redução), dependências circulares (Tarjan personalizado, bloqueante).
  • Genérico: aplique uma catraca a cada métrica de saúde (avisos, duplicação, complexidade ciclomática e cognitiva, código morto, tamanho de arquivo, ciclos de importação). A direção é sempre “não regredir”.
  • OmniRoute: CodeQL (catraca de alertas = 0), gitleaks ([extend] useDefault=true — crucial!), SonarQube, regras de segurança personalizadas (credenciais públicas, auxiliar de erros, associação de guardas de rota, validação de rotas).
  • Genérico: SAST (CodeQL/Sonar/semgrep) com catraca de alertas + scanner de segredos com conjunto de regras padrão herdado (configuração personalizada que substitui o padrão = ponto cego) + barreiras de segurança específicas do projeto baseadas em Regras Rígidas.
  • OmniRoute: osv-scanner + npm-audit + Trivy + Dependabot (SCA), license-checker (lista de permissões SPDX), lockfile-lint (HTTPS+sha512+registro), check-deps contra slopsquatting (lista de permissões + idade ≥72h).
  • Genérico: SCA com múltiplas fontes + lista de permissões de licenças + verificação de integridade do lockfile + lista de permissões de dependências com verificação de idade/typosquatting + bot de atualizações agrupadas.
  • OmniRoute: SBOM (CycloneDX + syft), proveniência SLSA (--provenance), OpenSSF Scorecard (semanal), proteção de workflows (zizmor: artipacked→persist-credentials:false, envenenamento de cache, permissões de tokens).
  • Genérico: gere um SBOM na publicação + proveniência assinada (SLSA L2+) + Scorecard agendado + proteja todos os workflows (tokens com privilégios mínimos, nenhuma credencial persistida em checkouts que não fazem push, actions fixadas por SHA).
  • OmniRoute: oasdiff (alterações incompatíveis no OpenAPI), schemathesis (fuzzing de contratos noturno), openapi-coverage (% de rotas documentadas, catraca de 38.3%), openapi-security-tiers (especificação vs. guarda de rota).
  • Genérico: diff de contrato para alterações incompatíveis (oasdiff/buf) + fuzzing baseado em propriedades contra a especificação (schemathesis) + cobertura de documentação com catraca + consistência entre especificação e código.
  • OmniRoute: docs-sync (versões espelhadas), docs-counts-sync (números na documentação vs. código), env-doc-sync, doc-links, fabricated-docs, cli-i18n, i18n-ui-coverage (--threshold=65 + catraca de 80.1%).
  • Genérico: sincronize versões/contagens/variáveis de ambiente entre documentação e código (barreira, não confiança) + valide links internos + cobertura de i18n com catraca.

11. Antialucinação/consistência (a categoria rara)

Seção intitulada “11. Antialucinação/consistência (a categoria rara)”
  • OmniRoute: known-symbols (despacho por string ⇒ símbolo existente), provider-consistency, fetch-targets (fetch do cliente ⇒ rota real), docs-symbols, db-rules (Regras Rígidas nº 2/nº 5), migration-numbering.
  • Genérico: para cada “fonte da verdade duplicada” (registro, despacho por string, referências entre camadas), uma barreira que comprove que os dois lados correspondem. Detecta a deterioração que a verificação de tipos e os testes não detectam.

12. Resiliência e domínio (específicos do produto)

Seção intitulada “12. Resiliência e domínio (específicos do produto)”
  • OmniRoute: caos (injeção de falhas), crescimento de heap (vazamento), k6 (teste de carga prolongado), promptfoo+garak (red team de LLM conforme o OWASP LLM Top 10), as 3 leis de resiliência (circuit breaker/cooldown/lockout).
  • Genérico: identifique os modos de falha do seu domínio e tenha uma barreira (mesmo que noturna) para cada um. Para aplicações de IA: red team contra injeção. Para sistemas distribuídos: caos + vazamento + carga prolongada.

Parte 4 — Plano de replicação para qualquer projeto

Seção intitulada “Parte 4 — Plano de replicação para qualquer projeto”

Implemente em fases, cada uma entregando valor por si só. Não tente adotar todas as 12 categorias de uma só vez — isso causa exatamente a fadiga de gates sobre a qual a Parte 2 alerta. Todo novo gate começa como consultivo e torna-se bloqueante quando estiver estável.

O elemento central reutilizável: a “anatomia de um gate de catraca”

Seção intitulada “O elemento central reutilizável: a “anatomia de um gate de catraca””

Todo o sistema gira em torno deste padrão de 3 arquivos. Copie-o primeiro:

  1. baseline.json — o valor congelado da métrica + direction (up/down) + eps (antiflake) + tightenSlack + dedicatedGate.
  2. collect-metrics.<ext> — executa a ferramenta, extrai o número e grava metrics.json.
  3. check-ratchet.<ext> — compara metrics.json com baseline.json; executa exit 1 somente se houver regressão além de eps; executa exit 0 (ignora de forma segura) se a ferramenta/infraestrutura estiver ausente; com --require-tighten, executa exit 1 se houver melhoria sem atualização da linha de base (consolida o ganho).

Com isso implementado, toda nova métrica (cobertura, complexidade, avisos, alertas de SAST, tamanho do bundle, pontuação de mutação…) é apenas mais uma linha na linha de base.

A CI está implementada; formatador + linter + verificação de tipos + 1 executor de testes + limite mínimo absoluto de cobertura (por exemplo, 60%). O pre-commit executa verificações rápidas com correção automática. Resultado: nenhum PR compromete os fundamentos.

Fase 1 — O mecanismo de catraca (semana 2) — a fundação de tudo

Seção intitulada “Fase 1 — O mecanismo de catraca (semana 2) — a fundação de tudo”

Implemente os 3 arquivos acima. Congele as linhas de base para: avisos, cobertura, complexidade, duplicação, código morto e tamanho de arquivo. Resultado: a partir daqui, a base de código só pode melhorar.

SAST (CodeQL/Sonar/semgrep) com catraca de alertas; scanner de segredos (herde o conjunto de regras padrão); SCA (osv/Dependabot) + lista de licenças permitidas + lockfile-lint. Resultado: vulnerabilidades conhecidas e segredos vazados não passam.

Fase 3 — Cadeia de suprimentos da build (semana 4)

Seção intitulada “Fase 3 — Cadeia de suprimentos da build (semana 4)”

SBOM na publicação + proveniência assinada (SLSA L2) + Scorecard agendado + reforço de segurança do workflow (zizmor: tokens mínimos, nenhuma credencial persistida, actions fixadas em versões específicas). Resultado: releases rastreáveis e à prova de adulteração.

2º executor, se útil; limites mínimos de cobertura por módulo para módulos críticos (anti-Goodhart); testes baseados em propriedades para lógica pura; testes de mutação todas as noites → quando a 1ª pontuação chegar, transforme mutationScore em uma catraca. Resultado: a cobertura deixa de ser uma métrica de vaidade; os testes comprovadamente detectam bugs.

Fase 5 — Contrato e análise dinâmica (semana 7)

Seção intitulada “Fase 5 — Contrato e análise dinâmica (semana 7)”

Se houver uma API pública: oasdiff (alteração incompatível, bloqueante) + schemathesis (fuzzing noturno). DAST/red team todas as noites, conforme apropriado para o domínio. Resultado: contratos não são quebrados silenciosamente.

Um gate de consistência para cada “verdade duplicada” no projeto. Gates de modos de falha específicos do domínio (para IA: red team contra injeção). Resultado: a deterioração estrutural e as falhas do domínio contam com uma rede de proteção.

  • Ciclo consultivo→bloqueante para cada novo gate.
  • stale-allowlist: toda supressão tem uma justificativa + issue; supressões obsoletas são detectadas.
  • evidence-gate: uma alegação de sucesso em um PR exige comprovação (teste ou teste vivo).
  • Revisão trimestral do ROI por gate (elimine ou retire recursos daqueles que não geram retorno — combate a fadiga).
  • Transforme as Regras Rígidas do seu projeto em gates executáveis.
  • Catraca, não valor absoluto. Bloqueie a regressão, não com base em um número fixo (exceto limites mínimos anti-zero).
  • Limite absoluto + catraca em conjunto. O limite impede o colapso; a catraca impede a erosão gradual.
  • Anti-Goodhart desde a concepção. Toda métrica-alvo precisa de um contrapeso (cobertura ⇒ mutação + antimascaramento; limites mínimos por módulo para forçar o teste do código difícil).
  • Ignorar de forma segura. Infraestrutura ausente nunca bloqueia; somente uma regressão real bloqueia.
  • dedicatedGate para métricas custosas. Métricas que precisam de um binário externo têm seu próprio script (com opção de ignorar), fora da catraca central síncrona.
  • Posicione o gate onde o merge acontece. Não deixe uma lacuna entre o gate rápido e o merge efetivo (a lição da separação dos gates rápidos).
  • Poucos gates bloqueantes, bem escolhidos. Sonar/DORA: condições demais = fadiga. Prefira o modo consultivo + catraca em vez de uma barreira de gates bloqueantes.

Parte 5 — Melhorias recomendadas (priorizadas, compatíveis)

Seção intitulada “Parte 5 — Melhorias recomendadas (priorizadas, compatíveis)”

P0 — maior ROI, quase pronto

  1. Aumento progressivo do mutation score (depois que a 1ª execução noturna do Stryker produzir valores). Principal antídoto contra o Goodhart de cobertura; ~90% concluído.
  2. Fechar a lacuna restante nos fast-gates — promover o build de produção de quality.yml após sua semana de avaliação e continuar movendo verificações determinísticas exclusivas do PR de release para o fluxo PR→release.
  3. Proteção de branch em main (configuração do proprietário) — melhora o Scorecard e fecha a lacuna do DSOMM.

P1 — valioso 4. osv/oasdiff → bloqueantes com o escopo correto — osv somente para CRITICAL+corrigível (em duas etapas, como o Trivy); oasdiff bloqueia breaking changes. 5. require-tighten → bloqueante (fim do ciclo) — consolida os ganhos nas métricas. 6. Revisão de ROI/tempo por gate em ci-summary — encontrar e remover gates lentos/de baixo valor.

P2 — retornos decrescentes 7. SLSA L3 — builder hermético/reprodutível (gerador SLSA do GitHub), caso queira avançar a partir do L2. 8. Configuração do CodeQL versionada no repositório + semgrep versionado — mais controle/reprodutibilidade. 9. Smoke test de DAST por PR — subconjunto rápido de schemathesis/promptfoo nos endpoints de maior risco (não apenas à noite). 10. Dashboard de flakiness + métricas DORA — garantir que os gates não estejam reduzindo a velocidade.


Parte 6 — Lições concretas de release (gates a adicionar na Fase 9)

Seção intitulada “Parte 6 — Lições concretas de release (gates a adicionar na Fase 9)”

Esta seção registra incidentes reais ocorridos durante fechamentos de release nos quais um gate estava ausente, com evidências concretas e o gate proposto. Cada item é um candidato para a Parte 5.

Lição da v3.8.27 (2026-06-17) — a “lacuna dos fast-gates” permite que regressões determinísticas cheguem ao dia da release

Seção intitulada “Lição da v3.8.27 (2026-06-17) — a “lacuna dos fast-gates” permite que regressões determinísticas cheguem ao dia da release”

O que aconteceu. Durante o /generate-release da v3.8.27, o PR de release (release/v3.8.27 → main) foi a primeira execução da matriz completa de ci.yml no ciclo integrado. Resultado: 12 falhas de uma só vez — 3 testes determinísticos + ~9 flakes/ambiente. Nenhuma era uma regressão do produto em produção, mas todas passaram despercebidas porque os PRs do ciclo entram em release/** por meio do Fast QG (quality.yml), que NÃO executa a suíte completa de testes unitários, nem pr-test-policy (mascaramento de testes), nem a suíte completa de integração, nem a verificação de paridade de schemas. As 3 determinísticas:

  1. Teste desatualizado por mudança na UI — permissions modal switch buttons declare button type: a #4034 adicionou um 4º switch (type="button" de a11y mantido); a contagem === 3 do teste ficou desatualizada. A análise estática deveria ter detectado isso no PR #4034.
  2. Teste desatualizado por mudança no empacotamento — findMissingArtifactPaths ... root runtime files: dist/http-method-guard.cjs tornou-se um required-path legítimo; a lista esperada pelo teste ficou desatualizada.
  3. Divergência por modularização com perda de dados (a mais grave) — settings schemas accept ... unprefixed toggle: o updateSettingsSchema modularizado (schemas/settings.ts, criado pela #3988) divergiu do canônico (settingsSchemas.ts): 45 campos contra 85 — 40 removidos + 6 divergentes (qdrant*). Era código morto (o runtime usa o canônico), portanto não houve impacto em produção, mas somente um teste de paridade escrito manualmente detectou o problema. A #4030 restaurou 16 remoções análogas da #3988/#3993, mas esta passou despercebida.

Gates propostos (Fase 9):

  • G1 — Fechar de fato a lacuna dos fast-gates (amplia P0 #2). Em quality.yml (PR→release/**), além de typecheck + testes impactados, executar pr-test-policy (mascaramento de testes) + a suíte unitária determinística completa (ou pelo menos os arquivos estáticos/de paridade, que são rápidos e não apresentam flakiness). Dessa forma, testes desatualizados e a remoção de asserts são detectados no PR que os introduz — não no dia da release. Manter integration/e2e fora (lentos/instáveis), mas a camada determinística NÃO PODE permanecer apenas em PR→main.
  • G2 — Gate de paridade de modularização (NOVO, não coberto atualmente). Uma verificação que, para cada símbolo reexportado por um barrel modularizado (src/shared/validation/schemas/*, módulos de providerRegistry etc.), compare o formato (chaves de z.object, entradas de registry) com a fonte canônica e falhe em caso de divergência (campo removido/adicional). Teria detectado a remoção dos 40 campos da #3988 naquele mesmo PR. Generaliza os testes de paridade escritos manualmente (que só existem onde alguém se lembrou de escrevê-los). Baixo custo: importa ambos e compara Object.keys(shape).
  • G3 — Triagem determinística de flakes (suporte). Os testes de inicialização do LiveWS e de integration-combo/breaker falham devido a timeout do servidor/falha em cascata no CI (ambiente), não por lógica. Marcá-los como known-flaky (em quarentena, com issue) para que o vermelho no PR de release contenha somente sinais reais, e não ruído mascarando regressões determinísticas em meio às demais falhas.

Princípio: o gate precisa ser executado onde ocorre o merge (já consta em “Princípios transversais”). O incidente da v3.8.27 mostra que isso também se aplica à camada de testes determinísticos, não apenas a lint/typecheck — caso contrário, a dívida de testes desatualizados + modularização com perda de dados só aparece em PR→main, em lote, no pior momento.



Código-fonte do OmniRoute (a58000c7685f)

HagiCode

HagiCode é um ambiente de programação com agentes, fluxos estruturados, execução multiagente e visualizações Hero Dungeon.

Transforme ideias em software útil com um fluxo de trabalho com agentes mais inteligente, rápido e agradável.

Interface principal do HagiCode no tema claro
  • SmartFluxos estruturados transformam intenções em um caminho executável da ideia à entrega.
  • EfficientFluxos multiagente mantêm pesquisa, implementação e revisão em andamento simultaneamente.
  • FunO Hero Dungeon torna longas sessões de programação mais visuais e colaborativas.
Acessar HagiCode