# F4.I2.T1 — make:module a partir do Blueprint

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

## O que foi implementado

- `make:module Grupo/Modulo` gera a árvore ADR-014 (provider, migrations, model, action, DTO, controllers, routes, views, Lang, tests).
- Alinhado aos ADRs (sem `new` no corpo, `strict_types`, shell, i18n, `MODULE.md`).

## Arquivos para revisar

- `src/core/Internal/Generators/Console/MakeModuleCommand.php`
- `src/core/Internal/Generators/Console/README.md`
- `src/core/Stub/Module/**`
- `src/core/Stub/Module/ModuleProvider.stub`
- `src/core/Stub/Module/Logic/Controllers/WebController.stub`
- `src/core/Stub/Module/Database/Models/Model.stub`

## 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

- [ ] Stub gera código que passa `make quality` e `make check-boundaries` sem edição.
- [ ] `make stub MODULE=Grupo/Modulo` (gerar + ativar + migrar).

## 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** (alto) o módulo gerado **falhava na verificação que o próprio `MODULE.md` prescreve**: `tools/name-check.sh --grep <token>` acusava `camada 'Test' sem …` — os stubs de Test nasciam só em Pascal (`RevisorProbeContractTest`) e o `Migration` dependia por acidente do índice `{{ table }}_ativos_idx`; os 5 módulos reais passam nessa checagem, o gerado não → `{{ module_lower }}` explícito nos 3 stubs (Test/Test/Migration) + gate `a_generated_module_satisfies_the_grep_friendly_token_in_every_layer` (**vermelho**: `Camada Test sem o token no conteúdo`) — `fact_00hv5u2r`; **B** (médio) o `MODULE.md` gerado mandava rodar **4 alvos `make` inexistentes** (`quality`, `check-boundaries`, `check-migrations`, `e2e` — o caminho dourado `make check` os aposentou), o mesmo defeito em 5 docs de `src/` (README do gerador, `core/README.md`, os 3 `MODULE.md` reais) → comandos reais (`make exec cmd='…'`, `make check`, `make check E2E=1`) + gate `every_make_target_referenced_in_src_docs_exists` em `DocsReferencesTest` (só comando — `make sure` em prosa é isento), **provado vermelho** — `fact_01jxxyrw`; **C** (médio) o e2e de legenda/alt era **flaky**: `expect` de 7s num round-trip ao S3 (falhou na rodada completa, passou isolado) — viola ADR-001 → espera por **evento** (resposta do upload + do stream da mídia), provado com atraso artificial de 9s: **verde com a correção, vermelho sem** (`toBeVisible` timeout 7000ms) — `fact_01rrz8ds`; **D** (baixo) README do gerador com exemplos errados (`MODULE=Noticias` cai em `Outros` e **falha** porque já existe; “sem MODULE pergunta interativamente” — ele **erra**), sugerindo “trait padrão” (proibido pela ADR-001) e citando o botão “Salvar e Sincronizar” **inexistente**; **E** (baixo) `{{ view }}`/`{{ resource_lower }}` eram placeholders **mortos** (nenhum stub usa) → removidos do mapa + README, com gate `a_generated_module_has_no_unresolved_placeholder` (a rede de segurança para o sentido inverso, **vermelho** com `{{ orfao }}`); **F** (baixo) a rota do portal nascia `{{ module_lower }}.portal.index` enquanto o `MODULE.md` documentava `{{ module_lower }}.index` e os 5 módulos reais usam `{token}.index` → alinhada + gate `a_generated_module_names_the_portal_route_and_documents_it` (**vermelho**) — `fact_01gk8ain`; **G–K** (obs) `ActionTest` nasce `markTestIncomplete` (política vermelho→verde; `failOnIncomplete` desligado), `Request.stub` sem `authorize()` explícito, `Lang/cms.stub` com chave `example` sem tela, `up()` sem reset de `search_path` em exceção, e a ambiguidade de **estilo de token** que é a `task_00h25ff0`. **Verificado ao vivo** (módulo descartável, depois removido para `.pi/trash/`): `make stub` gera + **ativa** + **migra**; `GET /stub_probe` → **200** e `api.localhost/stub_probe/status` → **200**; `migrate:rollback` renomeia para `backup_stub_probes_<ts>` (nunca dropa — ADR-017); com o módulo no disco: PHPStan `max` limpo, Pint PASS, `arch-check` OK, `migration-check` OK e ADR-004/i18n/ícones/layout/tokens verdes. Risco aberto: a **UI de ativação** que a ADR-034 previa para o F4 não existe (só o comando) — `risk_01gkj3e9`. |

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