# F4.I2.T3 — MODULE.md + token canônico (ADR-004)

| Campo | Valor |
|---|---|
| **Fase** | F4 — Produto & CMS |
| **Issue** | F4.I2 — Módulo piloto + blueprint (plano 13) |
| **kd task** | `task_01pos1wx` (`closed`) |
| **Âncora** | `wiki/implementation/04-produto-cms.md` |
| **Status da revisão** | 🔧 com correções |

## O que foi implementado

- `MODULE.md.stub` gera o contrato de leitura em todo `make:module`; o piloto tem `MODULE.md` preenchido.
- `make:module --register`/`core:module:activate` corrigidos (`ModuleDiscoveryService::refresh()`); regressão em `ModuleDiscoveryTest`.

## Arquivos para revisar

- `src/core/Stub/Module/MODULE.md.stub`
- `src/modules/Publicacoes/Noticias/MODULE.md`
- `src/core/Internal/ModuleDiscovery/ModuleDiscoveryService.php`
- `src/core/Internal/ModuleDiscovery/Console/ActivateModuleCommand.php`
- `src/core/Internal/ModuleDiscovery/Test/ModuleDiscoveryTest.php`
- `tests/Feature/Adr004GrepFriendlyTest.php`
- `tools/name-check.sh`

## Pontos de revisão

### PHP 8.4
- [ ] `declare(strict_types=1)`; tipos nativos (union/enum/`readonly`); sem `mixed` desnecessário.
- [ ] Dependências **injetadas** (nenhum `new`/`app()`/`resolve()` no corpo); sem traits/abstract/generics.
- [ ] `match` em vez de cadeias de `if`; `?->`/`??`; sem código morto; PHPStan `max` limpo.

### Laravel 12
- [ ] Nativo primeiro (Eloquent casts/scopes, `FormRequest`, `Gate`, scheduler, events) — sem reimplementar.
- [ ] Sem closure em rota; resources/rotas nomeadas; `config()` só lê, `env()` só em `config/`.
- [ ] Migração **idempotente**; `down()` → `backup_*` (nunca `DROP`); sem `orderBy` cru quando `latest/oldest` serve.

### HTML semântico + a11y (eMAG/WCAG 2.2 AA)
- [ ] Um `<main>`, headings em nível correto, landmarks; só ARIA quando necessário.
- [ ] Alvos ≥ 24 px (≥ 44 px em ponteiro grosso); foco visível; rótulos em todos os campos; contraste (axe).
- [ ] Funciona **sem JS**; `sniffCSS` sem `fail`/`warn` fora do ledger.

### CSS moderno
- [ ] `oklch`, `clamp`, `rem`/`fr`/`dvh`/`%`; `@layer` (a11y por último); `@property`; subgrid com fallback.
- [ ] Sem `!important`, sem `style="…"` inline, sem hex/rgb (exceto justificado); sem CDN.

### Decisões do projeto (ADRs)
- [ ] Respeita a ADR do escopo (ver cabeçalho) e não escapa das demais (001, 002, 004, 013, 015…).
- [ ] Determinismo: relógio/id injetados, ordem explícita, dados idempotentes (ADR-001).

### i18n (ADR-012)
- [ ] Nenhuma string visível fixa; 4 locales com paridade de chaves; `route()`/`localized_url()` (nunca path literal).

### Observabilidade/segurança/testes
- [ ] Span + `log_event('{module}.{action}')` com atributos `scalar|null`, **sem PII** (ADR-024).
- [ ] Teste que falha sem a mudança (vermelho→verde) + evidência no `kd`; `make quality` 2×.

### Específicos desta task

- [ ] Token único alcança todas as camadas (`make check-grep term=<Token>`).
- [ ] `MODULE.md` sincronizado com o código (rotas/permissões/eventos).

## Comandos úteis

```bash
make quality            # lint + PHPStan max + gates + testes
make e2e                # Playwright + axe (se a task toca UI)
make check-boundaries   # fronteiras core/services/layout/modules
```

---

> Revisar contra `AGENTS.md`, `wiki/adr/` e o plano citado. Registrar achados no `kd` (`kd write --anchor`) e, se houver correção, teste de regressão.

## Notas da revisão

| Data | Revisor | Resultado | Observações |
|---|---|---|---|
| 2026-10-18 | agente | 🔧 | **A** (médio) `tools/name-check.sh --grep <token>` — a verificação que o **próprio `MODULE.md` gerado prescreve** — **falhava** para um módulo sem persistência: `--grep home` acusava `camada 'Model'/'Action'/'Migration' sem 'home'` (o `Home` só tem `Lang/Layout/Routes/Seo`), enquanto o **modo árvore** (o gate de verdade, no `make check`) passava — alarme falso no comando prescrito → a camada só é **exigida** quando a feature do token **tem** aquela pasta (senão `AVISO`) e um token que não aparece em artefato nenhum vira `ERRO` → `home` → exit 0, token inexistente → exit 1, os 5 módulos de persistência inalterados, `gates-selftest` **OK no container** — `fact_00iqbhsw`; **B** (médio) nada garantia que o `MODULE.md` continuasse **sincronizado com o código** (o ponto específico da ficha) — e essa classe já quebrou duas vezes (nome de rota no stub e alvos `make`, ambos em T1) → **dois gates novos** em `DocsReferencesTest`: toda rota da tabela **Rotas** precisa existir (wildcard por prefixo) e todo `log_event('{token}.{ação}')` do módulo precisa ser citado no `MODULE.md` — ambos **provados vermelhos** (rota renomeada → `noticias.embutir`; evento renomeado → `lgpd.update_dsar`) — `fact_00hekyfq`; **C** (baixo) `Demo`, `Blueprint` e `Home` **não têm** `MODULE.md`, que a ADR-014 e o gerador exigem (só Noticias/DadosAbertos/Lgpd têm) → `task_00v7tsxs`; **D/E** (obs) `makeProviderFor` instancia o provider com `new $class` (fábrica por convenção — o “sem `new`” da ADR-001 mira DI de domínio) e a coluna **URI** do `MODULE.md` embute o domínio da área (`/cms/...`, `/api/...`), esclarecido pela nota logo abaixo da tabela — `risk_01angu5b`; **F** (médio) o e2e de Notícias asseria o aviso de sucesso — **flash de uma requisição só**: qualquer requisição intercalada (favicon) o consome e o teste falhava de forma intermitente (o log do servidor mostra POST 302 em 114 ms e o GET da tela em 73 ms — **não** era lentidão) → o flash passou a ser provado no PHPUnit (`assertSessionHas` no redirect + teste do **render** da tela, **vermelho** ao remover o bloco) e o e2e ficou com o resultado — `fact_01lcm8je`. **Verificado OK:** o token alcança todas as camadas nos 5 módulos de persistência (`--grep`) e o modo árvore passa nos 6; `ActivateModuleCommand` (refresh no cache miss, `setActive` = dado + invalidação instantânea, `core:cache-modules` só quando o módulo foi descoberto agora, erro claro no inexistente); `ModuleDiscoveryService` (manifesto **sem banco** — provado por `test_loading_the_manifest_does_not_query_the_database`; `refresh()` descarta arquivo + memória com teste de regressão real; ativação como **dado** com o manifesto de default); as tabelas de rotas/permissões/eventos dos 3 `MODULE.md` estão **corretas** (Noticias: 11 rotas e permissão por ação; DadosAbertos: 5 rotas + `dados_abertos.*`; Lgpd). |

> Resultado: `✅ revisado` · `🔧 com correções` · `⏭️ delegado` (com justificativa).
