Tynnaretrato de 22/09, 21:52
voltar à Biblioteca

ADR-0082 — Escopos e partições: isolamento de falha dentro do monorepo

20/09/2026 · aceito

docs/adr/ADR-0082-escopos-e-particoes.md

ADR-0082 — Escopos e partições: isolamento de falha dentro do monorepo

Status: aceito Data: 2026-09-20 Decisor: fundador (brainstorm da extensão 15, 19/09) Gates: tests/standards/escopos.test.ts (cobertura e existência), scripts/ci/escopos.test.ts (loader e gramática), tests/standards/early-exit.test.ts e tests/standards/mergify-gerado.test.ts (consumidores — Tasks 6 e 8), mergify config validate (schema vivo da Mergify) — rodado no PR; virar check é card

Decisão

O monorepo é dividido em 4 escopos — plataforma (catch-all), jep, infra, docs — declarados em config/escopos.yml. O contrato tem duas noções distintas: dono de um job pesado (escopo: runner e partição) e relevância (relevancia: os paths cujo diff torna o resultado do job obsoleto). Três consumidores leem o contrato: early-exit dos jobs pesados (decide POR JOB, pela relevância — nunca pelo dono), escopos da fila Mergify (pelos paths dos escopos) e label de runner (Plano 2). Repo separado por unidade não é adotado agora.

  • Fila em in-place checks (max_parallel_checks: 1, batch_size: 1, merge_conditions == queue_conditions): a Mergify recusou o 1º enfileiramento porque strict é incompatível com PRs de lote. Mantemos strict (lição do legado) — serial com isolamento de falha; scopes declarados para o dia em que houver volume. Descoberto que a fila nunca operou antes (label queue inexistente; 21 merges diretos).

Por quê

  • O objetivo é isolamento de falha (decisão do fundador), não capacidade nem repo próprio.
  • Os cruzamentos vividos (host, portas, /tmp, smoke vermelho em todo PR) vêm de recurso compartilhado, não da estrutura de repos; 71% da fricção medida é CI/ambiente (lição d5907231) — polyrepo multiplica isso.
  • Mergify scopes agrupa a fila por área quando merge_queue.mode é parallel/isolated (PRs do mesmo escopo testados em lote, áreas distintas em paralelo). Hoje o modo é serial in-place (ver bullet abaixo); scopes fica declarado e inerte até o dia em que ligar parallel. partition_rules foi removido da Mergify em 2025-10-31.
  • Dono ≠ relevância porque 2 dos 3 jobs pesados têm predicado mais fino ou mais amplo que o escopo dono: promptfoo pertence ao jep, mas seu golden set vive em promptfoo/** (plataforma) — decidir pelo dono pularia o gate de eval no PR que edita o próprio golden set.
  • mobile-compile fica fora do contrato (runner macOS pago; mantém o early-exit interno).

Não-negociáveis

  • Nunca paths: no gatilho do workflow (lição a7a403ba, G12): o job sempre roda e é sempre required; só o 1º step decide, fail-closed.
  • Diff com --no-renames: mover um arquivo para fora de um escopo conta como tocar o escopo.
  • Gramática de glob só ** e *; qualquer outro metacaractere, ./ inicial ou / final é recusado no carregamento — typo vira vermelho, não escopo furado.

Critério de permanência / reversão

4 semanas após X3 (extensão 15 §10): colisões por partição, duração de PR só-plataforma, falso-positivo dos gates novos. Vazamento de credencial entre escopos ⇒ Abordagem B (infra em repo próprio). Reverter = apagar o contrato e os consumidores; os workflows voltam a rodar tudo.

  • Se o volume justificar paralelismo, a troca é strict OFF + max_parallel_checks > 1 — decisão de board (afrouxa proteção).

  • Custo do promptfoo: hoje o provider é echo (eval grátis); quando virar modelo real, revisar roda_tudo_se_tocar × custo — todo PR em scripts/ci/** ou lockfile roda a eval completa.

Só leitura. Toda mudança nestes documentos é pull request.