# ADR-039 — Mutation testing obrigatório (Infection)

| Campo | Valor |
|---|---|
| **Status** | Aceito (2026-10-17) — etapa 1 (observabilidade) implementada (2026-10-20); etapa 2 (gate) pendente |
| **Data** | 2026-10-17 |
| **Plano de origem** | decisão do dono (revisão F3, 2026-10-17) |
| **Relacionadas** | ADR-001, ADR-023, ADR-038 |

## Contexto

Cobertura de linhas (ADR-038) mede **execução**, não **verificação**: um teste pode passar pela
linha sem afirmar nada (asserção fraca, `assertTrue(true)`, mock que não valida). Nada no projeto
prova hoje que a suíte **mata mutantes** — ou seja, que ela **detecta** uma regressão real. Para o
objetivo de “não-regressão” (ADR-001), isso é o furo central.

## Decisão

- **Mutation testing com Infection** (MIT) passa a ser **parte obrigatória** do rigor do projeto.
- **Prioridade pós-revisão**: entra **depois** da fila de revisão `_to_revision/` (registrado no
  épico — `wiki/implementation/README.md`). Não bloqueia a revisão em curso.
- Adoção em **duas etapas** (evita travar o fluxo):
  1. **Observabilidade**: roda no CI, publica o MSI (Mutation Score Indicator), **não bloqueia**.
  2. **Gate**: após estabilizar, o MSI entra no fluxo (`gate de mutation (B9)`) com piso definido (a fixar
     com o dono na implementação, alvo inicial ≥ MSI que a suíte atingir de forma estável).
- Escopo inicial: `src/core` e `src/services` (maior densidade de regra de domínio); módulos por
  último. Foco em **regras de negócio**, não em views/config.
- Config commitada (`tools/infection.json5`), determinística, sem rede.

## Justificativa (alternativas descartadas)

- **Só cobertura** — insuficiente: mede execução, não verificação (motivação desta ADR).
- **Mutation em tudo, bloqueando de imediato** — descartada: cara e ruidosa no início; travaria o
  fluxo. Adota-se observabilidade → gate.
- **Ferramentas alternativas** (ex.: Mago analyzer, ver ADR-040 “alternativas”) — não cobrem
  mutação; Infection é a referência para PHP.

## Consequências

**Positivas**
- Prova objetiva de que os testes pegam bugs; guia onde faltam asserções de valor.

**Custos / negativas**
- Execução cara (minutos); rodar em escopo/subconjunto e no CI, não a cada `make check` local.
- Exige excluir código não-testável (DTOs triviais, config) para não distorcer o MSI.

## Implementação (etapa 1 — 2026-10-20)

- `tools/infection.json5` cobre `src/core` + `src/services`, exclui `Test`, `Migrations`, `Lang` e `Stub`;
  logs em `build/mutation/`; `minMsi`/`minCoveredMsi` = 0 (observabilidade, sem bloqueio).
- `tools/mutation.sh` recria o banco base e roda `vendor/bin/infection` com
  `LOG_JSON_STREAM=/dev/null` — o `InitialTestsRunner` interrompe no primeiro byte de stderr e o log
  estruturado usa stderr; o canal diário continua ativo.
- Job `mutation` no `.gitlab-ci.yml` é **manual** e `allow_failure: true`; publica o artefato.
- O `make check` **não** chama Infection. A etapa 2 define o piso com o dono e move o job para gate.
