# Plano 05 — Qualidade Máxima (PHPStan/Larastan + Testes)

> **Milestone:** M1 · **Depende de:** 03 (paralelo a 04) · **Habilita:** 06–15
> **Objetivo:** tornar o rigor técnico automático e barato, de modo que o padrão
> correto seja o caminho de menor resistência.

---

## 1. Contexto

O `cms/` fixou PHPStan L7 (specs pediam L9) — ambiguidade que gera erosão. Aqui a
regra é explícita: **nível máximo**, com *baseline* que só encolhe. O
`sucesu-cms` mostrou que sem gate de fronteira e sem testes estruturais nasce código
órfão. O `prefeituras_v5` mostrou o custo de não ter nada disso.

---

## 2. Ferramentas e metas

| Ferramenta | Meta |
|---|---|
| **PHPStan + Larastan** | `level: max`, `treatPhpDocTypesAsCertain`, baseline commitado |
| **phpstan-strict-rules** | ativo |
| **phpstan-deprecation-rules** | ativo |
| **Laravel Pint** | `pint --test` no CI |
| **Prettier + ESLint** | `prettier --check` + `eslint` para JS/TS/Vite |
| **PHPUnit** | `failOnWarning/Notice/Risky/Deprecation/Empty` |
| **Cobertura** | core ≥90%, services ≥85%, layout ≥80%, modules ≥75% |
| **Gate de fronteiras** | script próprio (AST) que quebra o build |
| **Gate de nomenclatura (grep-friendly)** | `tools/name-check` + `make check` (ADR-004) |
| **Gate de migrations** | `tools/migration-check` (ADR-017) |
| **Gate de otimização/segurança (ADR-008)** | `declare(strict_types=1)`, zero `env()` em runtime, `optimize` verde |
| **Infection (observabilidade)** | MSI em `src/core` + `src/services` via `tools/mutation.sh`; piso (gate) após estabilizar |
| **CI** | GitLab: lint → análise → testes → e2e → build |

## 3. O gate de fronteiras arquiteturais

Script `tools/arch-check` (PHP + `nikic/php-parser`, já transitivo do PHPStan) que:

1. Varre todos os `use`/`new`/referências de classe por camada.
2. Aplica a matriz permitida:

```
core     → core
services → core, services
layout   → core, services, layout
modules  → core, services, layout, o próprio módulo
```

3. Reporta violação com arquivo:linha e motivo, com código de saída ≠ 0.
4. **Tem testes próprios** (fixtures válidas e inválidas) — o gate também é testado.

Complementa (não substitui) o PHPStan: uma regra custom pode ser adicionada depois,
mas o script é simples, rápido e legível.

## 3.1. O gate de nomenclatura grep-friendly (ADR-004)

`tools/name-check` complementa o `arch-check`:

1. Para cada diretório em `src/modules/{Modulo}`, deriva o token canônico
   (ex.: `DiarioOficial` → `diario-oficial`/`diario_oficial`/`diarioOficial`).
2. Verifica que **todo** arquivo de código/Blade/migration/teste no módulo contém o token
   em algum dos estilos (nome de arquivo **e** identificadores internos).
3. `make exec cmd='bash tools/name-check.sh --grep <token>'` confirma que a busca única encontra ≥1 arquivo por camada
   (Model/Action/View/Migration/Test).
4. Exceções (integração com legado) exigem comentário explícito e são listadas no relatório.

## 3.2. O gate de migrations (ADR-017)

`tools/migration-check` varre `src/**/Database/Migrations` e falha quando:

1. O `down()` contém `drop`/`dropIfExists`/`dropColumn` — deve renomear para `backup_*`.
2. Existe coluna `*_html`, `*_css`, `*_style`, `*_class` ou `style` (UI no banco).
3. Conteúdo rico é declarado como `text`/`string` em vez de `jsonb` (heurística por nome:
   `corpo`, `content`, `body`, `conteudo`, `blocks`, `schema`).
4. `guardar apresentação` — flags como `is_highlighted`/`is_super_highlighted` devem
   viver em `apresentacao` JSONB / `SlotConfig`, não como colunas.
5. Usa **sintaxe inválida no PostgreSQL 18** ou algo destrutivo indevido; e alerta se
   `CREATE INDEX CONCURRENTLY` aparece dentro do helper transacional `safely()` (não pode
   rodar em transação). Recursos modernos (`GENERATED ... STORED`, `INCLUDE`, `MERGE`) são
   permitidos, mas exigem justificativa explícita.

Cada exceção exige justificativa explícita e entra no relatório do gate.

## 3.3. O gate de vendorização (zero CDN)

`tools/vendor-check` falha quando houver referência a CDN em views/assets (`cdnjs`,
`unpkg`, `jsdelivr`, `googleapis`, `fonts.googleapis`) ou `<script src>`/`<link href>`
apontando para domínio externo — garantindo que todo JS/CSS/fonte é build local (Vite) ou
vendorizado. Complementa `composer audit`/`npm audit`.

Além disso, valida as **licenças** das dependências contra uma allowlist permissiva
(**MIT, ISC, Apache-2.0, BSD, OFL**) e mantém `THIRD_PARTY_LICENSES`; **copyleft (GPL/AGPL)
ou licença não-permissiva falha o gate**.

## 4. Atividades

- [x] `phpstan.neon`: `level: max` + **Larastan**, paths `src/core src/services src/layout src/modules`,
      `excludePaths` só migrations/factories, `reportUnmatchedIgnoredErrors: true`.
- [x] Baseline inicial gerado (**213 erros**; max + Larastan + strict-rules + deprecation-rules) e **ratchet** em `tools/phpstan-ratchet.sh` +
      baseline **zero** (ADR-040; sem arquivo de baseline). `make check` verde.
- [x] `phpunit.xml`: suites por camada (Unit/Feature/Core/Services/Layout/Modules) + `<env>` de teste (banco `concordia_test`; sem `.env.testing`);
      banco real de teste `concordia_test` (`tools/test-db.sh`); `CoreTestCase` aborta fora de `_test`.
- [x] `pint.json` (PHP) com `declare_strict_types` (aplicado também em `config/` e `public/`).
- [x] `.prettierrc.json`/`eslint.config.js` (JS/CSS/JSON) + hook local `.githooks/pre-commit`
      (`git config core.hooksPath .githooks`) chamando `make check`.
- [x] `.editorconfig` (indentação, EOL, charset; `Makefile` com tab).
- [x] `tools/arch-check` (php-parser) + `tools/name-check` + `tools/migration-check` +
      `tools/vendor-check` (zero-CDN + licenças) + `tools/name-check.sh`/`tools/migration-check`/`tools/gates-selftest.sh` (todos no `make check`)
      com **selftest em fixtures** (falham de propósito). Exceções justificadas (`@*-check-exception`).
- [x] `Makefile`: `quality` = `lint` (Pint + Prettier/ESLint) + `static` + `ratchet` + `check-gates` +
      `coverage` (PCOV → clover/cobertura); `fix` corrige PHP e JS; `quality-2x` anti-flaky.
- [x] `.gitlab-ci.yml`: stages `runtime → lint → static → test → e2e → build → deploy`
      (runner shell `deploy`), cache `vendor/`+`node_modules/`, cobertura Cobertura (artefato)
      e `deploy` manual via `scripts/update_prod.sh` (sem `git reset --hard`).
- [x] Política de **non-regression**: PR de bug precisa do teste vermelho→verde (ver `CONTRIBUTING.md`).
- [x] Guia funcional verificável: dependências injetadas, sem `new`/`resolve` no corpo
      da regra, toda ação emite log estruturado; **sem limite de linhas** (critério é
      funcionalidade/efetividade). Documentado em `CONTRIBUTING.md`.
- [x] Pré-condições do `php artisan optimize` no CI (ADR-008): `env()` só em `config/*.php`,
      zero closure em rotas, todo PHP com `declare(strict_types=1)` (`make check`;
      `tools/optimize-check` via php-parser, permissivo a callbacks de `Route::group`).
- [x] Job de CI roda `php artisan optimize` em ambiente efêmero e falha se algo não cachear
      (`make check`; verifica `bootstrap/cache/{config,routes-v7,events}.php` + views e limpa).
- [x] `composer audit` + `npm audit` no CI (`make check`, falha em vulnerabilidade conhecida) e
      SBOM CycloneDX determinístico no build (`make check` → `sbom.cdx.json`, 359 componentes).
- [x] Eloquent Strict Mode (`Model::shouldBeStrict()` fora de produção) e
      `DB::prohibitDestructiveCommands()` em produção (ver `AppServiceProvider`).
- [x] Testes **determinísticos** (ADR-001): clock/aleatoriedade injetados via fakes, sem `sleep`
      nem dependência de ordem; gate anti-flaky roda `make check` **2× seguidas**.

## 5. Testes e evidências

- [x] `make check` verde no esqueleto + Blueprint (128 arquivos Pint; PHPStan 210; 30 testes,
      2 skipped por deferimento a F2; cobertura clover+cobertura em `build/coverage/`).
- [x] `make check` verde **2× seguidas** sem flaky — prova de determinismo (ADR-001).
- [x] `make check` falha no fixture inválido e passa no válido (core → services).
- [x] `tools/name-check` falha em arquivo de módulo sem o token canônico.
- [x] `tools/migration-check` falha em fixture com `dropColumn` no `down()`, coluna
      `*_html`, conteúdo rico fora de `jsonb` e flag `is_highlighted`.
- [x] Gate de otimização falha em PHP sem `declare(strict_types=1)` e em `env()` fora de `config/` (T4).
- [x] `php artisan optimize` verde no CI (config/route/view/event cacheados) (T4).
- [x] Gate de vendorização falha em fixture com `<script src="https://cdn...">`.
- [ ] Cobertura do `core` ≥90% — **meta de F2** (hoje o `core` importado tem buracos; ver risco registrado).

## 6. Definition of Done

- [x] Nível máximo do PHPStan/Larastan ativo e reproduzível no container.
- [x] Gate de fronteiras no CI com testes próprios.
- [x] Pipeline GitLab com estágios `lint → static → test → e2e → build` (+ `deploy` manual).
- [x] Um dev novo recebe feedback completo com um único `make check`.

## 7. Riscos

| Risco | Mitigação |
|---|---|
| Nível máximo travar o início | Baseline inicial + ratchet; não relaxar o nível |
| Falsos positivos do Larastan | `ignoreErrors` justificado por entrada, nunca global |
| Gate virar gargalo de CI | Script rápido (<2s), em paralelo ao PHPStan |
