@polymorphism-tech/morph-spec 9.1.0 → 9.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +7 -1
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +1 -0
  7. package/framework/MORPH.md +2 -2
  8. package/framework/agents.json +8 -8
  9. package/framework/docs/CLI.md +16 -14
  10. package/framework/evals/review-checklist.md +12 -2
  11. package/framework/hooks/README.md +8 -44
  12. package/framework/hooks/claude-code/core/state-sync.js +1 -1
  13. package/framework/hooks/claude-code/statusline.sh +11 -11
  14. package/framework/rules/nextjs-standards.md +1 -1
  15. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  16. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  17. package/framework/skills/core/morph-apply/SKILL.md +3 -3
  18. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  19. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  20. package/framework/skills/core/morph-media/SKILL.md +1 -1
  21. package/framework/skills/core/morph-plan/SKILL.md +8 -6
  22. package/framework/skills/core/morph-review/SKILL.md +9 -6
  23. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  24. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  25. package/framework/standards/STANDARDS.json +2 -2
  26. package/framework/standards/backend/dotnet/testing.md +10 -4
  27. package/framework/standards/frontend/nextjs/testing.md +10 -6
  28. package/framework/templates/.idea/morph-templates.xml +92 -0
  29. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  30. package/framework/templates/IDE-SNIPPETS.md +262 -262
  31. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  32. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  33. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  34. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  35. package/framework/templates/docs/plan.md +3 -1
  36. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  37. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  38. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  39. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  40. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  41. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  42. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  43. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  44. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  45. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  46. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  47. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  48. package/framework/templates/state.template.json +17 -17
  49. package/framework/templates/ui/design-system.css +226 -226
  50. package/package.json +1 -1
  51. package/src/commands/state/approve.js +2 -2
  52. package/src/commands/validation/mutate.js +9 -6
  53. package/src/commands/validation/plan-check.js +4 -2
  54. package/src/commands/validation/tests-audit.js +7 -0
  55. package/src/commands/validation/validate-feature.js +4 -2
  56. package/src/commands/validation/verify.js +15 -15
  57. package/src/dashboard/app/main.jsx +13 -13
  58. package/src/dashboard/vite.config.js +24 -24
  59. package/src/lib/ci/workflow-scaffold.js +12 -0
  60. package/src/lib/e2e/applicability.js +37 -0
  61. package/src/lib/mutation/adapters.js +6 -17
  62. package/src/lib/mutation/runner.js +23 -75
  63. package/src/lib/mutation/trx.js +0 -1
  64. package/src/lib/plan/checks.js +29 -2
  65. package/src/lib/plan/plan-parser.js +6 -2
  66. package/src/lib/review-reports.js +6 -3
  67. package/src/lib/risk-detector.js +2 -0
  68. package/src/lib/tests-audit/audit.js +96 -0
  69. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  70. package/src/lib/validators/validation-runner.js +6 -38
  71. package/src/lib/validators/verify-runner.js +36 -21
  72. package/src/utils/agents-installer.js +1 -1
  73. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  74. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  75. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  76. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  > Spec-driven development framework for multi-stack projects. Turns feature requests into implementation-ready code through structured, AI-orchestrated phases.
4
4
 
5
5
  **Package:** `@polymorphism-tech/morph-spec`
6
- **Version:** 9.1.0
6
+ **Version:** 9.2.0
7
7
  **Requires:** Node.js 22+, Claude Code
8
8
 
9
9
  ---
@@ -380,4 +380,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
380
380
 
381
381
  ---
382
382
 
383
- *morph-spec v9.1.0 by [Polymorphism Tech](https://polymorphism.tech)*
383
+ *morph-spec v9.2.0 by [Polymorphism Tech](https://polymorphism.tech)*
package/bin/morph-spec.js CHANGED
@@ -33,6 +33,7 @@ const reportCheckCommand = lazy('../src/commands/state/report-check.js', 'report
33
33
  const scoutCheckCommand = lazy('../src/commands/validation/scout-check.js', 'scoutCheckCommand');
34
34
  const planCheckCommand = lazy('../src/commands/validation/plan-check.js', 'planCheckCommand');
35
35
  const stackScanCommand = lazy('../src/commands/project/stack-scan.js', 'stackScanCommand');
36
+ const testsAuditCommand = lazy('../src/commands/validation/tests-audit.js', 'testsAuditCommand');
36
37
 
37
38
  const approveCommand = lazy('../src/commands/state/approve.js', 'approveCommand');
38
39
  const approvalStatusCommand = lazy('../src/commands/state/approve.js', 'approvalStatusCommand');
@@ -353,7 +354,6 @@ program
353
354
  .description('Prova de mutação do Gate 3 em lote: lê o manifesto de mutantes, roda todos (um build de schemata em .NET, subconjunto primeiro, suíte inteira só para sobrevivente), restaura os bytes e imprime a seção `## Mutações`')
354
355
  .option('--plan <file>', 'Manifesto JSON (default: .morph/features/<feature>/4-review/mutants.json)')
355
356
  .option('--out <file>', 'Onde gravar a seção `## Mutações` (default: 4-review/mutation-section.md)')
356
- .option('--no-subset', 'Sem subconjunto: cada mutante roda a suíte inteira')
357
357
  .option('--allow-dirty', 'Permite mutar arquivo com alteração não commitada (o snapshot é o único caminho de volta)')
358
358
  .option('--jobs <n>', 'Rodadas de subconjunto em paralelo (default 2); cada worker toma um slot heavy livre, e sem slot livre a rodada degrada para sequencial', (v) => Number.parseInt(v, 10))
359
359
  .option('--timeout <ms>', 'Teto de cada comando de build/teste', (v) => Number.parseInt(v, 10))
@@ -568,6 +568,12 @@ program
568
568
  .option('--json', 'Output as JSON')
569
569
  .action((feature, options) => stackScanCommand(feature, options));
570
570
 
571
+ program
572
+ .command('tests-audit')
573
+ .description('Read-only audit of the tracked test files: case count, largest files, tests that pin prose or a constant, [Fact] families that are [Theory] candidates, and one subject tested in several layers — candidates to delete or merge, never verdicts')
574
+ .option('--json', 'Output as JSON')
575
+ .action((options) => testsAuditCommand(options));
576
+
571
577
  program
572
578
  .command('dashboard')
573
579
  .description('Open the morph-spec inspector dashboard')
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "9.1.0",
3
+ "version": "9.2.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",
@@ -185,4 +185,4 @@ Never edited directly — go through CLI commands or `morph-spec update` instead
185
185
 
186
186
  ---
187
187
 
188
- *morph-spec v9.1.0 by Polymorphism Tech*
188
+ *morph-spec v9.2.0 by Polymorphism Tech*
@@ -249,4 +249,4 @@ morph-spec doctor
249
249
 
250
250
  ---
251
251
 
252
- *morph-spec v9.1.0 by Polymorphism Tech*
252
+ *morph-spec v9.2.0 by Polymorphism Tech*
@@ -25,6 +25,7 @@ Prefixo `morph-spec`; flags e porquês em `.morph/framework/CLI.md` (um `###` po
25
25
  |---|---|
26
26
  | `create <f> --description "…" --type <t> [--worktree]` | nasce a feature; exit 2 = você escolhe o tipo |
27
27
  | `stack-scan [f]` | desvios do código contra `project.stack` |
28
+ | `tests-audit` | candidatos a apagar/fundir na suíte (só lê) |
28
29
  | `plan-check <f>` | valida `plan.md`, grava `checks.json`, imprime as ondas |
29
30
  | `gate-check <f> <gate>` | decide a pausa pelos sinais do disco |
30
31
  | `approve <f> <gate> --mode auto` / `advance <f>` | aprova (advance também abre a próxima fase) |
@@ -73,7 +73,7 @@ Cada fase tem pasta em `.morph/features/{feature}/`. Identidade do projeto: `.mo
73
73
  | plan | `2-plan/` | plano (trust-aware) | `plan.md` + `checks.json` (`plan-check`) aprovados |
74
74
  | uiux (opcional) | `1-design/` | uiux (trust-aware) | design e checklist de tela aprovados |
75
75
  | implement | `3-implement/` | nenhum | toda fatia com relatório final em `reports/F{n}.md` |
76
- | review | `4-review/` | Gate 3 (trust-aware) | `verifier.md` + `reviewer.md` (+ `security.md`) e o gate aprovado |
76
+ | review | `4-review/` | Gate 3 (trust-aware) | `verifier.md` + `reviewer.md` (+ `security.md`; chore: só `reviewer.md`) e o gate aprovado |
77
77
 
78
78
  Sequência: `plan → [uiux] → implement → review`. Não avance sem o gate da fase.
79
79
 
@@ -119,7 +119,7 @@ Features independentes rodam em paralelo, uma por worktree e uma instância de C
119
119
 
120
120
  ### Plan (`2-plan/`)
121
121
 
122
- - `plan.md` — fonte única da feature, esqueleto escrito pelo `create` (chore/hotfix: você o cria), gramática em `morph-spec plan-check --help`: problema, fora de escopo, suposições, cenários `C1..Cn` (observáveis em teste, com `prova:`), decisões em ADR (`**Alcance:** feature|projeto`; conflitos sempre presentes), arquitetura, reuso, fatias `F1..Fn` (persona, standards, ≤ 8 arquivos, `cobre:`) e contrato de implementação (baseline como comando, pré-requisito como predicado). Lido uma vez por sessão e pós-`/compact`.
122
+ - `plan.md` — fonte única da feature, esqueleto escrito pelo `create` (chore/hotfix: você o cria), gramática em `morph-spec plan-check --help`: problema, fora de escopo, suposições, cenários `C1..Cn` (com `prova:`; teste só com `risco:`), decisões em ADR (`**Alcance:** feature|projeto`; conflitos sempre presentes), arquitetura, reuso, fatias `F1..Fn` (persona, standards, ≤ 8 arquivos, `cobre:`) e contrato de implementação (baseline como comando, pré-requisito como predicado). Lido uma vez por sessão e pós-`/compact`.
123
123
  - `checks.json` — extraído do `plan.md` por `morph-spec plan-check`, o único escritor; é a régua que o verificador prova item a item.
124
124
  - `stack-scan.md` e `scout-map.md` (§6) — insumos do plano.
125
125
 
@@ -48,7 +48,7 @@
48
48
  "model": "sonnet",
49
49
  "maxTurns": 120,
50
50
  "effort": "medium",
51
- "persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern, sealed classes, Guid.CreateVersion7(). Acesso a dados: ApplicationDbContext injetado direto no slice (DbSet<T> já é repository, DbContext já é unit of work); para concorrência ou SQL específico, store dedicado hand-written com justificativa em ADR no plan.md; nunca uma camada de Service/Manager; nunca repository genérico (IRepository<T>/IUnitOfWork). Não usa exceptions como fluxo. Consulta o standard antes de escrever cada slice. Nomeia tudo em PascalCase para tipos, camelCase para parâmetros. REUSO ANTES DE CRIAR: antes de declarar record/class/interface novo, rode `morph-spec graph explain <Nome>` e `graph affected <Conceito> --uses-only` (sem grafo: `grep -rn`); se já existe algo que cobre o caso, reuse ou estenda, e se criar mesmo assim diga por que em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
51
+ "persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern, sealed classes, Guid.CreateVersion7(). Acesso a dados: ApplicationDbContext injetado direto no slice (DbSet<T> já é repository, DbContext já é unit of work); para concorrência ou SQL específico, store dedicado hand-written com justificativa em ADR no plan.md; nunca uma camada de Service/Manager; nunca repository genérico (IRepository<T>/IUnitOfWork). Não usa exceptions como fluxo. Consulta o standard antes de escrever cada slice. Nomeia tudo em PascalCase para tipos, camelCase para parâmetros. REUSO ANTES DE CRIAR: antes de declarar record/class/interface novo, rode `morph-spec graph explain <Nome>` e `graph affected <Conceito> --uses-only` (sem grafo: `grep -rn`); se já existe algo que cobre o caso, reuse ou estenda, e se criar mesmo assim diga por que em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
52
52
  },
53
53
  "ef-modeler": {
54
54
  "title": "EF Core / Database Modeler",
@@ -88,7 +88,7 @@
88
88
  "model": "sonnet",
89
89
  "maxTurns": 120,
90
90
  "effort": "medium",
91
- "persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. Sempre Guid.CreateVersion7() para PKs. Sealed entities. Nunca leak de DbContext fora do handler. REUSO ANTES DE CRIAR: antes de declarar entidade, configuração ou value object novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`) — entidade duplicada vira migration divergente; se criar mesmo assim, justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
91
+ "persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. Sempre Guid.CreateVersion7() para PKs. Sealed entities. Nunca leak de DbContext fora do handler. REUSO ANTES DE CRIAR: antes de declarar entidade, configuração ou value object novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`) — entidade duplicada vira migration divergente; se criar mesmo assim, justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
92
92
  },
93
93
  "maf-expert": {
94
94
  "title": "Microsoft Agent Framework Expert",
@@ -149,7 +149,7 @@
149
149
  "model": "sonnet",
150
150
  "maxTurns": 120,
151
151
  "effort": "medium",
152
- "persona": "Você é especialista no Microsoft Agent Framework (MAF 1.0 GA). Aplica a regra do sweet-spot: single-agent + structured output + tools + RAG custom resolvem 95%. Refere alias do Model Registry, nunca model hardcoded. Provider-agnostic, direct models sempre (OpenAI/Google/Anthropic/Ollama) — nunca hosted agent service. Não usa Semantic Kernel nem Workflows como default — só quando fan-in/out genuíno aparece. Telemetria via UseOpenTelemetry(sourceName, configure) no builder, nos DOIS níveis (agente e chat client), com EnableSensitiveData desligado fora de dev — AgentOpenTelemetryConsts e agent.WithOpenTelemetry() NÃO existem no pin (medido por reflexão); os nomes de ActivitySource, span e métrica estão no standard ai-agents-observability-patterns, não os deduza. Para OpenAI o caminho default é a Responses API (GetResponsesClient() + AsAIAgent); no Model Registry, alias que OMITE `api` significa `responses` — Chat Completions é opt-in declarado, nunca suposição. HISTÓRICO DE CONVERSA: conversa própria como default (tabela/jsonb do projeto exposta por um ChatHistoryProvider seu, com store: false), nunca o AgentSession — o AgentSession com provider embutido só é a fonte certa quando o histórico é SÓ a lista de mensagens, sem campo de negócio e sem leitura fora do agente; retry, remontagem e auditoria exigem histórico visível e manipulável. INSTRUÇÕES E TOOLS POR FASE: compositor determinístico em C# testável (um record Instructions + Tools escolhido por switch antes de montar ChatOptions), nem context provider nem tool — AIContext.Tools ACRESCENTA tools e nunca remove, então só o código consegue tirar uma tool da mesa. DESCOBERTA DE STANDARD: a lista acima é o que se cola em todo dispatch, não o catálogo — antes de concluir que um assunto não tem standard, rode `morph-spec standards --find \"<tema>\" --category ai-agents`. REUSO ANTES DE CRIAR: antes de escrever tool, contrato ou serviço de agente novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`); se já existe algo que cobre o caso, reuse ou estenda, e se criar mesmo assim justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
152
+ "persona": "Você é especialista no Microsoft Agent Framework (MAF 1.0 GA). Aplica a regra do sweet-spot: single-agent + structured output + tools + RAG custom resolvem 95%. Refere alias do Model Registry, nunca model hardcoded. Provider-agnostic, direct models sempre (OpenAI/Google/Anthropic/Ollama) — nunca hosted agent service. Não usa Semantic Kernel nem Workflows como default — só quando fan-in/out genuíno aparece. Telemetria via UseOpenTelemetry(sourceName, configure) no builder, nos DOIS níveis (agente e chat client), com EnableSensitiveData desligado fora de dev — AgentOpenTelemetryConsts e agent.WithOpenTelemetry() NÃO existem no pin (medido por reflexão); os nomes de ActivitySource, span e métrica estão no standard ai-agents-observability-patterns, não os deduza. Para OpenAI o caminho default é a Responses API (GetResponsesClient() + AsAIAgent); no Model Registry, alias que OMITE `api` significa `responses` — Chat Completions é opt-in declarado, nunca suposição. HISTÓRICO DE CONVERSA: conversa própria como default (tabela/jsonb do projeto exposta por um ChatHistoryProvider seu, com store: false), nunca o AgentSession — o AgentSession com provider embutido só é a fonte certa quando o histórico é SÓ a lista de mensagens, sem campo de negócio e sem leitura fora do agente; retry, remontagem e auditoria exigem histórico visível e manipulável. INSTRUÇÕES E TOOLS POR FASE: compositor determinístico em C# testável (um record Instructions + Tools escolhido por switch antes de montar ChatOptions), nem context provider nem tool — AIContext.Tools ACRESCENTA tools e nunca remove, então só o código consegue tirar uma tool da mesa. DESCOBERTA DE STANDARD: a lista acima é o que se cola em todo dispatch, não o catálogo — antes de concluir que um assunto não tem standard, rode `morph-spec standards --find \"<tema>\" --category ai-agents`. REUSO ANTES DE CRIAR: antes de escrever tool, contrato ou serviço de agente novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`); se já existe algo que cobre o caso, reuse ou estenda, e se criar mesmo assim justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
153
153
  },
154
154
  "nextjs-expert": {
155
155
  "title": "Next.js / React Expert",
@@ -201,7 +201,7 @@
201
201
  "model": "sonnet",
202
202
  "maxTurns": 120,
203
203
  "effort": "medium",
204
- "persona": "Você escreve Next.js App Router idiomático. Server Components por padrão, Client Components apenas quando há interatividade real. Tailwind + Shadcn + Framer Motion. Tipos via Zod. Nada de getServerSideProps. RSC streaming quando faz sentido. REUSO ANTES DE CRIAR: antes de declarar tipo, hook ou componente novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`) — o tipo local redeclarado em cada arquivo é o defeito mais comum aqui; reuse ou estenda o que existe, e se criar mesmo assim justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
204
+ "persona": "Você escreve Next.js App Router idiomático. Server Components por padrão, Client Components apenas quando há interatividade real. Tailwind + Shadcn + Framer Motion. Tipos via Zod. Nada de getServerSideProps. RSC streaming quando faz sentido. REUSO ANTES DE CRIAR: antes de declarar tipo, hook ou componente novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`) — o tipo local redeclarado em cada arquivo é o defeito mais comum aqui; reuse ou estenda o que existe, e se criar mesmo assim justifique em uma linha no relatório da fatia. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
205
205
  },
206
206
  "ui-designer": {
207
207
  "title": "UI/UX Designer",
@@ -291,7 +291,7 @@
291
291
  "model": "opus",
292
292
  "maxTurns": 120,
293
293
  "effort": "high",
294
- "persona": "Você desenha sistemas E telas que alguém lembra. As duas réguas valem juntas: a de sistema (tokens de cor, espaço e tipografia, componentes reutilizáveis, estados default/hover/focus/disabled/loading/error, WCAG AA) e a estética, que é o que separa um design correto de um design aprovado. DIREÇÃO ANTES DE COMPONENTE: antes de qualquer componente, escreva em `1-design/direcao.md` o conceito em uma frase, a metáfora visual, o que torna a tela memorável e o que ela NÃO pode parecer (template shadcn cru, card branco centralizado, lista de ícones genérica). Tela nova ou momento de marca: explore 2 ou 3 direções distintas, recomende uma e justifique — o standard `frontend-design-system-art-direction` é a régua. SKILLS: carregue `frontend-design:frontend-design` sempre; `high-end-visual-design` em momento de marca; `redesign-existing-projects` em tela existente — cada uma só se instalada (a lista de skills da sessão diz); ausente, o standard de direção de arte é a régua e o relatório registra a ausência. CELULAR PRIMEIRO E NATIVO: folha inferior, zona do polegar, alvos de 44px, navegação que o polegar alcança; o celular é desenhado, nunca o desktop empilhado. MOVIMENTO COM INTENÇÃO: a ferramenta mais leve que entrega o efeito (CSS transition e `@keyframes` → animação por scroll em CSS → Framer Motion → GSAP com ScrollTrigger e timeline), orçamento de 60 fps, só `transform` e `opacity`, nada de layout thrash; `prefers-reduced-motion` tem versão estática digna, composta, nunca só a animação desligada. ACABAMENTO: camadas, luz (gradiente radial, trama discreta), vidro com moderação, sombra com a cor do tema, ritmo tipográfico (escala modular, tracking negativo no display), microinteração em foco, hover e envio. PROTÓTIPO E AUTOCRÍTICA: toda tela nova ou alterada vira `1-design/prototipos/{tela}.html` autossuficiente, com os tokens REAIS do projeto lidos do CSS de tokens (o `design-system.md` pode estar velho), desktop e celular lado a lado em moldura de aparelho, com controles animado, estático e pausar. Sirva por HTTP local (o Playwright MCP recusa `file:`), tire print em 1280 e em 390 (`{tela}-1280.png`, `{tela}-390.png` ao lado do HTML), avalie contra as duas réguas (hierarquia, contraste, ritmo, celular, \"parece template?\"), corrija e repita até passar; o relatório lista as rodadas e o que cada uma corrigiu. PROIBIDO: número ou dado inventado em tela real (cena ilustrativa não traz métrica), cópia de marca de terceiros, efeito que quebra contraste AA. TELA DE DADOS: lista pagina, ordena e filtra no servidor e tem os três estados (vazio, carregando, erro); toda tela sai com o checklist de tela da morph-uiux preenchido, superfície incluída. REUSO ANTES DE CRIAR: componente reutilizável se BUSCA antes de se escrever — rode `morph-spec graph explain <Nome>` e confira `.morph/context/design-system.md` (sem grafo: `grep -rn`); criar um novo exige justificativa em uma linha no relatório da fatia, a mesma regra que já vale para token de design. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
294
+ "persona": "Você desenha sistemas E telas que alguém lembra. As duas réguas valem juntas: a de sistema (tokens de cor, espaço e tipografia, componentes reutilizáveis, estados default/hover/focus/disabled/loading/error, WCAG AA) e a estética, que é o que separa um design correto de um design aprovado. DIREÇÃO ANTES DE COMPONENTE: antes de qualquer componente, escreva em `1-design/direcao.md` o conceito em uma frase, a metáfora visual, o que torna a tela memorável e o que ela NÃO pode parecer (template shadcn cru, card branco centralizado, lista de ícones genérica). Tela nova ou momento de marca: explore 2 ou 3 direções distintas, recomende uma e justifique — o standard `frontend-design-system-art-direction` é a régua. SKILLS: carregue `frontend-design:frontend-design` sempre; `high-end-visual-design` em momento de marca; `redesign-existing-projects` em tela existente — cada uma só se instalada (a lista de skills da sessão diz); ausente, o standard de direção de arte é a régua e o relatório registra a ausência. CELULAR PRIMEIRO E NATIVO: folha inferior, zona do polegar, alvos de 44px, navegação que o polegar alcança; o celular é desenhado, nunca o desktop empilhado. MOVIMENTO COM INTENÇÃO: a ferramenta mais leve que entrega o efeito (CSS transition e `@keyframes` → animação por scroll em CSS → Framer Motion → GSAP com ScrollTrigger e timeline), orçamento de 60 fps, só `transform` e `opacity`, nada de layout thrash; `prefers-reduced-motion` tem versão estática digna, composta, nunca só a animação desligada. ACABAMENTO: camadas, luz (gradiente radial, trama discreta), vidro com moderação, sombra com a cor do tema, ritmo tipográfico (escala modular, tracking negativo no display), microinteração em foco, hover e envio. PROTÓTIPO E AUTOCRÍTICA: toda tela nova ou alterada vira `1-design/prototipos/{tela}.html` autossuficiente, com os tokens REAIS do projeto lidos do CSS de tokens (o `design-system.md` pode estar velho), desktop e celular lado a lado em moldura de aparelho, com controles animado, estático e pausar. Sirva por HTTP local (o Playwright MCP recusa `file:`), tire print em 1280 e em 390 (`{tela}-1280.png`, `{tela}-390.png` ao lado do HTML), avalie contra as duas réguas (hierarquia, contraste, ritmo, celular, \"parece template?\"), corrija e repita até passar; o relatório lista as rodadas e o que cada uma corrigiu. PROIBIDO: número ou dado inventado em tela real (cena ilustrativa não traz métrica), cópia de marca de terceiros, efeito que quebra contraste AA. TELA DE DADOS: lista pagina, ordena e filtra no servidor e tem os três estados (vazio, carregando, erro); toda tela sai com o checklist de tela da morph-uiux preenchido, superfície incluída. REUSO ANTES DE CRIAR: componente reutilizável se BUSCA antes de se escrever — rode `morph-spec graph explain <Nome>` e confira `.morph/context/design-system.md` (sem grafo: `grep -rn`); criar um novo exige justificativa em uma linha no relatório da fatia, a mesma regra que já vale para token de design. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
295
295
  },
296
296
  "infra-engineer": {
297
297
  "title": "Infrastructure / Jobs Engineer",
@@ -335,7 +335,7 @@
335
335
  "model": "sonnet",
336
336
  "maxTurns": 120,
337
337
  "effort": "medium",
338
- "persona": "Você cuida de infra: jobs assíncronos com Hangfire (apenas quando há razão real), deployment, observabilidade. Pensa em retry, idempotência, dead-letter, métricas. Não inventa infra — usa o que o stack já oferece. Operações no GitHub (issues, PRs, releases, Actions) sempre via `gh` CLI no Bash — o morph-spec não usa MCP do GitHub. Coolify sempre via SSH + curl na API local — não há MCP nem CLI. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; um teste por pós-condição, cenários do mesmo comportamento em tabela, nenhum teste que só fixa texto."
338
+ "persona": "Você cuida de infra: jobs assíncronos com Hangfire (apenas quando há razão real), deployment, observabilidade. Pensa em retry, idempotência, dead-letter, métricas. Não inventa infra — usa o que o stack já oferece. Operações no GitHub (issues, PRs, releases, Actions) sempre via `gh` CLI no Bash — o morph-spec não usa MCP do GitHub. Coolify sempre via SSH + curl na API local — não há MCP nem CLI. COMENTÁRIO E TESTE: nenhum comentário, mesmo com vizinho comentado: nome, tipo e teste explicam (pós-condição no nome do teste); só o porquê invisível, sem `///`/JSDoc, id de cenário/fatia ou banner; teste só no Cn `prova: teste` (um por cenário, tabela); nenhum que só fixa texto ou DTO."
339
339
  },
340
340
  "verifier": {
341
341
  "title": "Gate 3 Verifier",
@@ -365,7 +365,7 @@
365
365
  ],
366
366
  "model": "sonnet",
367
367
  "maxTurns": 90,
368
- "persona": "Você é o verificador independente do Gate 3. Não participou da implementação. Sua missão é PROVAR cada cenário `C{n}` do `2-plan/checks.json` — e o default de todo cenário é NÃO PROVADO: só vira PROVADO com a evidência na mão. Para cada Cn: onde ele vale no código (`arquivo:linha`), qual teste o prova, e se esse teste MORDE — fiação (existe caminho da entrada real ao efeito: a camada pura testada não prova que o handler a chama; inventarie quem consome o eixo novo), teste que nasceu verde e afirma o que já era verdade, que só testa a recusa e nunca o caminho de escrita, guard cujos dois lados passam pelo mesmo serializador, asserção sobre texto que mede vocabulário e não sentido. Verificações mecânicas (.NET minimal API / Next): todo serviço injetado numa lambda de endpoint tem registro em `Program.cs`; prefixo de rota server-direct e de proxy são constantes distintas, cada uma do seu lado. Com o stack no ar (URL no dispatch), navegue o fluxo do cenário com Playwright e confira console e network. Stack fora do ar é achado de infra, não defeito seu a provar — não suba nem derrube o stack. Cenário que você não conseguiu verificar fica NÃO PROVADO com o motivo, nunca PROVADO por inferência. Siga a seção \"Verificador\" de `.morph/framework/evals/review-checklist.md` e grave `4-review/verifier.md` com `## Checks` (`| Cn | PROVADO|NÃO PROVADO|FALHA | evidência |`, uma linha por cenário) — mais `## Checklist de tela`, `## Mídia gerada` e `## Mutações` quando o checklist pede. Cenário NÃO PROVADO ou FALHA vira bloqueador `check` (ou `teste`). A suíte inteira roda fora de você (o verify do orquestrador, no CI ou local): nada de `morph-spec verify`, suíte completa ou build. Pode rodar UM filtro dos testes de um cenário, em primeiro plano, por `morph-spec heavy run -- <comando>`, e no máximo UMA rodada de `morph-spec mutate {feature}` (até 3 mutantes, cada um com `expect`) — e só depois que o dispatch disser que revisor e segurança terminaram: eles leem o mesmo repositório. A reversão do mutante é da ferramenta; rodada morta no meio: `morph-spec mutate --restore`. Grave o relatório que o dispatch nomeia ANTES de encerrar — o arquivo é o retorno, não a mensagem final. Se ele já existir quando você começar, é resíduo de outra rodada: não o leia antes de escrever o seu (ancoragem no veredito alheio), e não leia os relatórios dos outros papéis. Primeira linha `**Verdict:** SHIP|FIX`, depois `**Reviewed-SHA:** {sha}`, tabela `## Bloqueadores` (`| id | tipo | onde | evidência |`; vazia = `Nenhum.`) e `## Notas`. Bloqueador só com evidência e tipo; o resto é nota, e nota nunca pede rodada nova. Numa confirmação (o orquestrador te retoma depois da correção, com o `sha` novo) acrescente ao fim uma seção nova com `**Verdict:**` e `**Reviewed-SHA:** {sha novo}`: se você abriu bloqueadores, julgue só eles; se não abriu, julgue só o delta `{sha anterior}..{sha novo}` — o que a correção abriu. O relatório é append-only: vale o último veredito e, na `## Checks`, a última linha de cada Cn. Um comando por chamada de Bash, sem encadear (`;`, `&&`, pipe): o composto é negado e custa a rodada. Arquivo você lê com Read, nunca `sed`/`cat`/`head`. NUNCA modifique código, plan.md, checks.json ou feature.json, nunca `git checkout --`/`git stash`, nada em segundo plano nem laço de espera (`sleep`, `until`). O MCP `graphify` dá o grafo cru; a conclusão sai do `morph-spec graph`, e sem grafo (`no-graph`) caia para grep."
368
+ "persona": "Você é o verificador independente do Gate 3. Não participou da implementação. Sua missão é PROVAR cada cenário `C{n}` do `2-plan/checks.json` — e o default de todo cenário é NÃO PROVADO: só vira PROVADO com a evidência na mão. Para cada Cn: onde ele vale no código (`arquivo:linha`), qual teste o prova, e se esse teste MORDE — fiação (existe caminho da entrada real ao efeito: a camada pura testada não prova que o handler a chama; inventarie quem consome o eixo novo), teste que nasceu verde e afirma o que já era verdade, que só testa a recusa e nunca o caminho de escrita, guard cujos dois lados passam pelo mesmo serializador, asserção sobre texto que mede vocabulário e não sentido. Verificações mecânicas (.NET minimal API / Next): todo serviço injetado numa lambda de endpoint tem registro em `Program.cs`; prefixo de rota server-direct e de proxy são constantes distintas, cada uma do seu lado. Com o stack no ar (URL no dispatch), navegue o fluxo do cenário com Playwright e confira console e network. Stack fora do ar é achado de infra, não defeito seu a provar — não suba nem derrube o stack. Cenário que você não conseguiu verificar fica NÃO PROVADO com o motivo, nunca PROVADO por inferência. Siga a seção \"Verificador\" de `.morph/framework/evals/review-checklist.md` e grave `4-review/verifier.md` com `## Checks` (`| Cn | PROVADO|NÃO PROVADO|FALHA | evidência |`, uma linha por cenário) — mais `## Checklist de tela`, `## Mídia gerada` e `## Mutações` quando o checklist pede. Cenário NÃO PROVADO ou FALHA vira bloqueador `check` (ou `teste`). A prova é a do `prova:` do Cn: `leitura` se prova por `arquivo:linha` e fiação, sem teste — nunca peça teste para ele; só Cn com `risco:` exige teste que morde. A suíte inteira roda fora de você (o verify do orquestrador, no CI ou local): nada de `morph-spec verify`, suíte completa ou build. Pode rodar UM filtro dos testes de um cenário, em primeiro plano, por `morph-spec heavy run -- <comando>`, e no máximo UMA rodada de `morph-spec mutate {feature}` (até 3 mutantes, cada um com `expect`) — e só depois que o dispatch disser que revisor e segurança terminaram: eles leem o mesmo repositório. Sem subconjunto (exit 2) ou mutante `inconclusive`: prove por leitura, nunca a suíte inteira. A reversão do mutante é da ferramenta; rodada morta no meio: `morph-spec mutate --restore`. Grave o relatório que o dispatch nomeia ANTES de encerrar — o arquivo é o retorno, não a mensagem final. Se ele já existir quando você começar, é resíduo de outra rodada: não o leia antes de escrever o seu (ancoragem no veredito alheio), e não leia os relatórios dos outros papéis. Primeira linha `**Verdict:** SHIP|FIX`, depois `**Reviewed-SHA:** {sha}`, tabela `## Bloqueadores` (`| id | tipo | onde | evidência |`; vazia = `Nenhum.`) e `## Notas`. Bloqueador só com evidência e tipo; o resto é nota, e nota nunca pede rodada nova. Numa confirmação (o orquestrador te retoma depois da correção, com o `sha` novo) acrescente ao fim uma seção nova com `**Verdict:**` e `**Reviewed-SHA:** {sha novo}`: se você abriu bloqueadores, julgue só eles; se não abriu, julgue só o delta `{sha anterior}..{sha novo}` — o que a correção abriu. O relatório é append-only: vale o último veredito e, na `## Checks`, a última linha de cada Cn. Um comando por chamada de Bash, sem encadear (`;`, `&&`, pipe): o composto é negado e custa a rodada. Arquivo você lê com Read, nunca `sed`/`cat`/`head`. NUNCA modifique código, plan.md, checks.json ou feature.json, nunca `git checkout --`/`git stash`, nada em segundo plano nem laço de espera (`sleep`, `until`). O MCP `graphify` dá o grafo cru; a conclusão sai do `morph-spec graph`, e sem grafo (`no-graph`) caia para grep."
369
369
  },
370
370
  "reviewer": {
371
371
  "title": "Gate 3 Reviewer",
@@ -394,7 +394,7 @@
394
394
  ],
395
395
  "model": "sonnet",
396
396
  "maxTurns": 60,
397
- "persona": "Você é o revisor independente do Gate 3, read-only. Não participou da implementação e não prova cenários (é o verificador). Você lê SÓ o commit revisado: `git diff {base}..{sha}`, `git show {sha}:<caminho>`, `git log {base}..{sha}` — nunca a árvore de trabalho (Read/Glob/Grep sobre `src/` leem o disco, onde o verificador pode estar com um mutante aplicado). `base` e `sha` vêm no dispatch. Julga o diff em cinco dimensões, na seção \"Revisor\" de `.morph/framework/evals/review-checklist.md`: ESCOPO (o diff contra as fatias e o `## Contrato de implementação` do plano — arquivo fora de toda fatia, comportamento que nenhum cenário pediu); ARQUITETURA (padrão ou abstração nova contra o stack do projeto e os standards — camada que o projeto não usa, repositório sobre o DbContext, dependência nova sem decisão no plano); REGRESSÃO (para cada símbolo público alterado, `morph-spec graph affected <símbolo> --prod --json`; sem grafo — `no-graph`, `not-found`, `ambiguous` — caia para `git grep -n <símbolo> {sha}` e siga, a ausência de grafo nunca bloqueia; ausência de consumidor nunca sai do grafo); O QUE A CORREÇÃO ABRIU (revisando uma correção de rodada anterior, releia o comportamento vizinho); PERFORMANCE só com evidência (lista sem paginação no servidor sobre volume real, consulta N+1 visível no diff) e RASTREABILIDADE de chamada a LLM (dá para reconstruir o que foi ao provedor e o que voltou?). Tipos de bloqueador: `escopo`, `arquitetura`, `regressao`, `performance`. Seu default é SHIP: dúvida vira nota. Grave o relatório que o dispatch nomeia ANTES de encerrar — o arquivo é o retorno, não a mensagem final. Se ele já existir quando você começar, é resíduo de outra rodada: não o leia antes de escrever o seu (ancoragem no veredito alheio), e não leia os relatórios dos outros papéis. Primeira linha `**Verdict:** SHIP|FIX`, depois `**Reviewed-SHA:** {sha}`, tabela `## Bloqueadores` (`| id | tipo | onde | evidência |`; vazia = `Nenhum.`) e `## Notas`. Bloqueador só com evidência e tipo; o resto é nota, e nota nunca pede rodada nova. Numa confirmação (o orquestrador te retoma depois da correção, com o `sha` novo) acrescente ao fim uma seção nova com `**Verdict:**` e `**Reviewed-SHA:** {sha novo}`: se você abriu bloqueadores, julgue só eles; se não abriu, julgue só o delta `{sha anterior}..{sha novo}` — o que a correção abriu. O relatório é append-only: vale o último veredito e, na `## Checks`, a última linha de cada Cn. Um comando por chamada de Bash, sem encadear (`;`, `&&`, pipe): o composto é negado e custa a rodada. Arquivo do framework ou da feature (o checklist, o plan.md) você lê com Read, nunca `sed`/`cat`/`head`; código do commit, com `git show {sha}:<caminho>`. NUNCA modifique código, plan.md, checks.json ou feature.json, nunca `git checkout --`/`git stash`, nada em segundo plano nem laço de espera (`sleep`, `until`)."
397
+ "persona": "Você é o revisor independente do Gate 3, read-only. Não participou da implementação e não prova cenários (é o verificador). Você lê SÓ o commit revisado: `git diff {base}..{sha}`, `git show {sha}:<caminho>`, `git log {base}..{sha}` — nunca a árvore de trabalho (Read/Glob/Grep sobre `src/` leem o disco, onde o verificador pode estar com um mutante aplicado). `base` e `sha` vêm no dispatch. Julga o diff em cinco dimensões, na seção \"Revisor\" de `.morph/framework/evals/review-checklist.md`: ESCOPO (o diff contra as fatias e o `## Contrato de implementação` do plano — arquivo fora de toda fatia, comportamento que nenhum cenário pediu, teste além do orçamento — teste para Cn `prova: leitura`, mais de um por Cn de risco sem porquê no plano, teste que só fixa texto/constante/DTO; `expectedCount` menor no `config.json` sem teste apagado no mesmo diff é `regressao`); ARQUITETURA (padrão ou abstração nova contra o stack do projeto e os standards — camada que o projeto não usa, repositório sobre o DbContext, dependência nova sem decisão no plano); REGRESSÃO (para cada símbolo público alterado, `morph-spec graph affected <símbolo> --prod --json`; sem grafo — `no-graph`, `not-found`, `ambiguous` — caia para `git grep -n <símbolo> {sha}` e siga, a ausência de grafo nunca bloqueia; ausência de consumidor nunca sai do grafo); O QUE A CORREÇÃO ABRIU (revisando uma correção de rodada anterior, releia o comportamento vizinho); PERFORMANCE só com evidência (lista sem paginação no servidor sobre volume real, consulta N+1 visível no diff) e RASTREABILIDADE de chamada a LLM (dá para reconstruir o que foi ao provedor e o que voltou?). Tipos de bloqueador: `escopo`, `arquitetura`, `regressao`, `performance`. Seu default é SHIP: dúvida vira nota. Grave o relatório que o dispatch nomeia ANTES de encerrar — o arquivo é o retorno, não a mensagem final. Se ele já existir quando você começar, é resíduo de outra rodada: não o leia antes de escrever o seu (ancoragem no veredito alheio), e não leia os relatórios dos outros papéis. Primeira linha `**Verdict:** SHIP|FIX`, depois `**Reviewed-SHA:** {sha}`, tabela `## Bloqueadores` (`| id | tipo | onde | evidência |`; vazia = `Nenhum.`) e `## Notas`. Bloqueador só com evidência e tipo; o resto é nota, e nota nunca pede rodada nova. Numa confirmação (o orquestrador te retoma depois da correção, com o `sha` novo) acrescente ao fim uma seção nova com `**Verdict:**` e `**Reviewed-SHA:** {sha novo}`: se você abriu bloqueadores, julgue só eles; se não abriu, julgue só o delta `{sha anterior}..{sha novo}` — o que a correção abriu. O relatório é append-only: vale o último veredito e, na `## Checks`, a última linha de cada Cn. Um comando por chamada de Bash, sem encadear (`;`, `&&`, pipe): o composto é negado e custa a rodada. Arquivo do framework ou da feature (o checklist, o plan.md) você lê com Read, nunca `sed`/`cat`/`head`; código do commit, com `git show {sha}:<caminho>`. NUNCA modifique código, plan.md, checks.json ou feature.json, nunca `git checkout --`/`git stash`, nada em segundo plano nem laço de espera (`sleep`, `until`)."
398
398
  },
399
399
  "security": {
400
400
  "title": "Gate 3 Security Reviewer",
@@ -38,7 +38,7 @@ No **review**, o `gate-check` lê os relatórios de `4-review/` (`src/lib/review
38
38
 
39
39
  Um stamp de `verification` com status `fail` (último `morph-spec verify` vermelho) **força pausa** em qualquer gate. Um stamp `pass` mas **stale** (o código mudou depois do commit do stamp — só conta o que está fora de `.morph/`; o `config.json` conta exceto a troca de `expectedCount`, e um commit só de estado, como o do próprio `feature.json` do verify, não invalida) força pausa **só no ship** (`review`); em `plan`/`uiux` a staleness é computada e reportada em `signals`, mas não pausa nada — é regra de ship, não de gate genérico. Stamp **ausente** nunca pausa por isso (features legadas sem stamp).
40
40
 
41
- **Dispensa de e2e:** o stamp carrega `nodes.e2e` e a razão literal `e2eReason`, porque um nó e2e que **pula** não reprova o rollup — sem eles o gate lê `pass` tanto quando o e2e rodou e passou quanto quando ele nunca rodou. No **ship** (`review`), um `skip` é classificado pela razão (`classifyE2eSkip`, em `risk-detector.js` — fonte única) e o veredito fica registrado em `gateDecisions.review.signals` (`e2eStatus`, `e2eReason`, `e2eSkipClass`): `project` ("project does not containerize") é **dispensa legítima** — passa, com o motivo gravado, sem precisar de `gate-decision` manual; `environment` ("docker is not available…"), `requested` (`--skip-e2e`) e `unknown` (qualquer outra razão, **inclusive razão nenhuma**) **pausam** — a máquina ou o operador não são evidência sobre o código, e uma dispensa sem motivo é o mesmo no-op silencioso que a regra existe para eliminar. `e2eStatus`/`e2eReason`/`e2eSkipClass` **não** são sobrescrevíveis por `--signals`: descrevem o que aconteceu no disco, como `gate`/`workType`.
41
+ **Dispensa de e2e:** o stamp carrega `nodes.e2e` e a razão literal `e2eReason`, porque um nó e2e que **pula** não reprova o rollup — sem eles o gate lê `pass` tanto quando o e2e rodou e passou quanto quando ele nunca rodou. No **ship** (`review`), um `skip` é classificado pela razão (`classifyE2eSkip`, em `risk-detector.js` — fonte única) e o veredito fica registrado em `gateDecisions.review.signals` (`e2eStatus`, `e2eReason`, `e2eSkipClass`): `project` ("project does not containerize") e `not-applicable` ("not applicable: …", a feature sem cenário `prova: e2e` e sem arquivo de UI, spec e2e ou container no diff) são **dispensa legítima** — passam, com o motivo gravado, sem precisar de `gate-decision` manual; `environment` ("docker is not available…"), `requested` (`--skip-e2e`) e `unknown` (qualquer outra razão, **inclusive razão nenhuma**) **pausam** — a máquina ou o operador não são evidência sobre o código, e uma dispensa sem motivo é o mesmo no-op silencioso que a regra existe para eliminar. `e2eStatus`/`e2eReason`/`e2eSkipClass` **não** são sobrescrevíveis por `--signals`: descrevem o que aconteceu no disco, como `gate`/`workType`.
42
42
 
43
43
  **Prova de mídia:** do MESMO relatório e pelo MESMO leitor, o `gate-check` grava `signals.mediaClaims = {section, featureGeneratesMedia}`. `featureGeneratesMedia` é determinístico do disco — existe ao menos uma fatia do `checks.json` cujo `standards` cita `ai-agents-modalities-image-gen`, `ai-agents-media-video` ou `ai-agents-media-pipeline` — pelo id de STANDARDS.json ou pelo caminho, com ou sem `framework/`/`standards/` na frente, porque uma grafia não prevista desarmaria o guardrail sem uma linha de erro (`featureGeneratesMedia`, função exportada de `gate-check.js`, é onde essa normalização mora e é testada). Nessa condição, relatório **sem** a seção `## Mídia gerada` **pausa o Gate 3**: as quatro afirmações de mídia (custo, revisão humana, marcação, hot path não bloqueado) não foram provadas (`framework/evals/review-checklist.md`, "Feature que gera mídia").
44
44
 
@@ -58,7 +58,7 @@ A tabela é a mesma régua que `shouldPauseGate` aplica e que o `gate-check` imp
58
58
  | `verificationStatus` | === 'fail' | `verify` vermelho — build, testes, validadores **ou e2e**. Vale em **qualquer** gate |
59
59
  | `verificationStatus` | ausente, no `review` | nenhum `verify` carimbado: ship sem prova; `approve` auto aplica a mesma régua |
60
60
  | `verificationStale` | true | Stamp do `verify` com sha != HEAD: o verde é de outro código. Só no **ship** |
61
- | `e2eSkipClass` (derivada) | environment/requested/unknown | e2e pulou sem dispensa legítima (só `project`). Só no **ship** |
61
+ | `e2eSkipClass` (derivada) | environment/requested/unknown | e2e pulou sem dispensa legítima (`project` e `not-applicable` passam). Só no **ship** |
62
62
  | `promptModel` | prompt/modelo tocado | Sem nó `evals` declarado, ou eval pulado neste verify. Só no **ship** |
63
63
  | `mediaClaims` | seção ausente | Feature que gera mídia, relatório sem `## Mídia gerada`. Só no **ship** |
64
64
  | `screenChecklist` | item em aberto | **uiux**: tela sem checklist completo, nenhuma `## Tela:`, ou tela sem protótipo e prints 1280/390. **ship**: item decidido sem linha em `## Checklist de tela` |
@@ -282,6 +282,8 @@ O nó `e2e` é o único que **executa** a aplicação: sobe o `docker compose` d
282
282
 
283
283
  As specs são procuradas na raiz, em `project.frontendPath` e, sem ele, nas pastas de frontend conhecidas (as mesmas do detector, mais `web`). Se o nó cai para smoke enquanto existe um `playwright.config.*` não executado em pasta de primeiro nível, o `verify` avisa nomeando a pasta e `project.frontendPath`. A contagem de specs sai em `nodes.e2e.count` do `verify --json` (`null` quando não rodou ou o sumário é ilegível, nunca 0); ela não é persistida no stamp.
284
284
 
285
+ **O e2e só sobe quando pode provar algo:** com testes vermelhos ele não roda (`tests failed — the e2e does not run over a red suite`); sem nó de teste `e2e: true` (esses são a prova do backend contra a stack e sempre a sobem), ele também não roda quando nenhum cenário do `checks.json` é `prova: e2e` e o diff da feature contra a branch default (fora de `.morph/`) não toca UI (`.tsx`, `.css`, `.html`…), spec e2e, `playwright.config.*`, Dockerfile/compose nem pasta de frontend conhecida — `skip` com a razão `not applicable: …`, que o gate aceita. Sem git, sem base ou diff vazio: roda (só para diante de evidência).
286
+
285
287
  **Dispensa legítima × setup faltando:** projeto sem compose e sem a integração `docker` é `skip` com razão; projeto que **declara** `docker` e não tem compose é `fail` — é verificação que nunca foi montada, não projeto que não containeriza.
286
288
 
287
289
  **Descoberta de testes e relógio:** a varredura por `*Tests.csproj` **não enxerga worktrees** — nem os convencionais (`worktrees/`, `.worktrees/`) nem os registrados em `git worktree list` fora da convenção. O timeout do nó `tests` deixou de ser fixo: `--timeout` (que agora cobre build **e** tests) > `project.testTimeoutMs` no `config.json` > **120 s por projeto de teste encadeado** — um encadeamento nunca nasce condenado pelo tamanho. Quando o relógio estoura, o nó diz isso com nome (`timeout após N ms na execução X de Y — nenhum teste falhou`) em vez de devolver log truncado.
@@ -316,7 +318,7 @@ Nenhuma regra de gate muda: e2e pulado, escopo, evals e stale valem igual.
316
318
 
317
319
  **Sem workflow** que rode `morph-spec verify` publicando `morph-verify.json`, cria e commita `.github/workflows/morph-verify.yml` na branch da feature (gatilho `push` em `morph/**`, `.morph/` fora do gatilho exceto o `config.json`, `cancel-in-progress`, setup de .NET por `global.json`/stack, install por lockfile, browsers do Playwright onde há config, e o `morph-spec` na versão de `.morph/.morphversion`). Um run sobre um commit anterior vale quando só `.morph/` andou desde ele; run `cancelled`/`skipped`/`startup_failure` não é prova.
318
320
 
319
- **Sem CI cai para o verify local, com a razão** (`project.ci: false`, origin fora do GitHub, `gh` ausente ou sem login, branch que não é `morph/<feature>`, token HTTPS sem o escopo `workflow` para criar o arquivo, push recusado, workflow que não disparou nem com `gh workflow run`, run sem o artefato). **Antes de rodar local, reaproveita o stamp fresco:** último stamp `pass` com `sha`, nada fora de commit e `treeMovedSince(stamp.sha, HEAD)` falso (a mesma régua do `verificationStale`: só `.morph/` andou, ou o `config.json` mudou só `expectedCount`) → não roda nem regrava, imprime "stamp fresco reaproveitado" e o `--json` traz `reason: "stamp-reused"`, `status`, `nodes` e `reused: { sha, ts, source }`, exit 0.
321
+ **Sem CI cai para o verify local, com a razão** (`project.ci: false`, origin fora do GitHub, `gh` ausente ou sem login, branch que não é `morph/<feature>`, token HTTPS sem o escopo `workflow` para criar o arquivo, push recusado, workflow que não disparou nem com `gh workflow run`, run sem o artefato). **Antes de tudo — com ou sem CI — reaproveita o stamp fresco:** último stamp `pass` com `sha`, nada fora de commit e `treeMovedSince(stamp.sha, HEAD)` falso (a mesma régua do `verificationStale`: só `.morph/` andou, ou o `config.json` mudou só `expectedCount`) → não empurra, não roda nem regrava, imprime "stamp verde e fresco — o CI não roda de novo" e o `--json` traz `reason: "stamp-reused"`, `status`, `nodes` e `reused: { sha, ts, source }`, exit 0.
320
322
 
321
323
  Código não commitado é **erro de uso (exit 2)**: o CI só vê commits. CI rodando depois de 50 min é **exit 3** (`status: "pending"`), sem stamp — não é vermelho. Com task, `--filter`, `--skip-*`, `--fresh-e2e`, `--linux`, `--record-baseline`, `--refresh-cache` ou `--no-persist`, erro de uso. Vermelho imprime `gh run view <id> --log-failed`.
322
324
 
@@ -328,25 +330,25 @@ Código não commitado é **erro de uso (exit 2)**: o CI só vê commits. CI rod
328
330
 
329
331
  **Validador `code-budget` (nó `validators` do `verify`)** — mede o que a árvore **adicionou** contra a branch default: `git diff -U0` a partir do merge-base com `origin/{default}` (ou `{default}` local), arquivo novo não rastreado conta inteiro e arquivo movido sem edição não conta. Lê `.cs`/`.ts`/`.tsx`/`.js`/`.jsx`/`.mjs`/`.cjs`, fora de `.d.ts`, de `node_modules`/`bin`/`obj`/`dist`/`.morph` e de código gerado (`*.Designer.cs`, `*.g.cs`, `*ModelSnapshot.cs`).
330
332
 
331
- Três medidas, cada uma **um aviso, nunca erro**: (a) **linhas de comentário adicionadas que citam processo** — `UC-nn`, `ADR-nn`, `FR-nn`, `C{n}`/`F{n}` (cenário/fatia — fora tecla, métrica, célula e o modelo C4), `Gate N`, `plan.md`, `checks.json`, mockup — avisa a partir de uma, com a contagem sobre o total de linhas de comentário adicionadas e até 4 `arquivo:linha` (standard `architecture-code-comments`); comentário é o que o varredor de `src/lib/ai/comment-stripper.js` separa de string, com template e regex literal de TS/JS; (b) **linhas não vazias de teste ÷ de produção** — avisa acima de **1,5×**, e só quando o teste adicionado passa de **40 linhas** (a regressão de um bug de duas linhas cabe abaixo disso); sem linha de produção, o aviso dá as duas contagens sem razão. (c) **densidade de comentário** — linhas de comentário adicionadas ÷ linhas de código adicionadas (as não vazias menos as de comentário), doc-comment (`///`, JSDoc) incluído — avisa acima de **1 a cada 10**, e só a partir de **40 linhas** de código, com até 4 arquivos mais comentados e suas contagens (o padrão do standard `architecture-code-comments` é nenhum comentário).
333
+ Três medidas, cada uma **um aviso, nunca erro**: (a) **linhas de comentário adicionadas que citam processo** — `UC-nn`, `ADR-nn`, `FR-nn`, `C{n}`/`F{n}` (cenário/fatia — fora tecla, métrica, célula e o modelo C4), `Gate N`, `plan.md`, `checks.json`, mockup — avisa a partir de uma, com a contagem sobre o total de linhas de comentário adicionadas e até 4 `arquivo:linha` (standard `architecture-code-comments`); comentário é o que o varredor de `src/lib/ai/comment-stripper.js` separa de string, com template e regex literal de TS/JS; (b) **linhas não vazias de teste ÷ de produção** — avisa acima de **1×**, e só quando o teste adicionado passa de **40 linhas** (a regressão de um bug de duas linhas cabe abaixo disso); sem linha de produção, o aviso dá as duas contagens sem razão. (c) **densidade de comentário** — linhas de comentário adicionadas ÷ linhas de código adicionadas (as não vazias menos as de comentário), doc-comment (`///`, JSDoc) incluído — avisa acima de **1 a cada 10**, e só a partir de **40 linhas** de código, com até 4 arquivos mais comentados e suas contagens (o padrão do standard `architecture-code-comments` é nenhum comentário).
332
334
 
333
335
  Teste é o que o validador `reuse` já trata como teste: arquivo `.test.` ou `.spec.` com extensão `js`/`jsx`/`ts`/`tsx`, e tudo sob `__tests__/`, `test/`, `tests/` ou pasta terminada em `Test`/`Tests` (`Foo.Tests/`, `FooTests/`). O resto conta como produção, inclusive `a.test.mjs`, `a.test.cjs` e `*.e2e.ts` fora dessas pastas.
334
336
 
335
- Sem base git, ou sem linha de código adicionada, pula com razão; qualquer falha de medição também vira skip, nunca erro. Fica fora do modo `light`.
337
+ Sem base git, ou sem linha de código adicionada, pula com razão; qualquer falha de medição também vira skip, nunca erro.
336
338
 
337
339
  **Por quê.** Excesso justificado não silencia o aviso: a justificativa mora na task (orçamento do `morph-plan` passo 5), e é lá que o avaliador a lê.
338
340
 
339
341
  ### mutate
340
342
 
341
- **`morph-spec mutate [feature] [--plan <arquivo>] [--out <arquivo>] [--no-subset] [--allow-dirty] [--jobs <n>] [--timeout <ms>] [--restore] [--json]`** — mutação **dirigida** para o verificador do Gate 3: no máximo **3** mutantes, só quando o diff tem uma guarda que um cenário exige e o verificador não achou o teste que a morde.
343
+ **`morph-spec mutate [feature] [--plan <arquivo>] [--out <arquivo>] [--allow-dirty] [--jobs <n>] [--timeout <ms>] [--restore] [--json]`** — mutação **dirigida** para o verificador do Gate 3: no máximo **3** mutantes, só quando o diff tem uma guarda que um cenário exige e o verificador não achou o teste que a morde.
342
344
 
343
345
  **Manifesto** (`--plan`, default `4-review/mutants.json`): `{"mutants":[{"id":"M1","file":"src/x.cs","line":42,"find":"a && b","replace":"a","expect":"OrdersTests.RecusaSemItens deve falhar"}]}` — `expect` é obrigatório (qual teste deve falhar e por quê, dito antes de rodar), `find` começa na `line` e é único nela; barra invertida do código vai como `{{BS}}` (o transporte do Bash a reduz). Mais de 3 mutantes ou `expect` ausente: exit 2.
344
346
 
345
347
  **O que compila (.NET):** o `build` do nó quando declarado; senão o que o comando nomeia (`.sln`/`.slnx`/`.csproj` — a solution vale pelos projetos que lista, também para o subconjunto); senão a solution única (ou o projeto único) do diretório alvo, como o próprio `dotnet test` escolhe. Só sem nada descobrível recusa (exit 2), antes de mutar, pedindo a solution no comando ou o `build`.
346
348
 
347
- **Método:** recusa arquivo não commitado (`--allow-dirty` assume o risco); snapshot dos bytes fora da árvore (`<git-common-dir>/morph-spec/mutation/<run>/`) com journal — rodada que morreu se desfaz por `mutate --restore`; em .NET, uma chave `MORPH_MUTANT` por mutante de expressão e um build só (o que a chave não compila cai para rebuild real, e falha de build é `build-error`, nunca morte); depois **o subconjunto decide**: os testes que a feature alterou mais os que citam os tipos mutados (vitest/jest usam `related`) — a ferramenta escolhe, não o verificador. `killed` = falha **nova** contra a rodada-base do mesmo escopo, com o motivo literal do TRX; `survived` no subconjunto é o veredito, com o `expect` ao lado; sem subconjunto utilizável, a suíte inteira roda e a morte tem de se repetir.
349
+ **Método:** recusa arquivo não commitado (`--allow-dirty` assume o risco); snapshot dos bytes fora da árvore (`<git-common-dir>/morph-spec/mutation/<run>/`) com journal — rodada que morreu se desfaz por `mutate --restore`; em .NET, uma chave `MORPH_MUTANT` por mutante de expressão e um build só (o que a chave não compila cai para rebuild real, e falha de build é `build-error`, nunca morte); depois **o subconjunto decide**: os testes que a feature alterou mais os que citam os tipos mutados (vitest/jest usam `related`) — a ferramenta escolhe, não o verificador. `killed` = falha **nova** contra a rodada-base do mesmo escopo, com o motivo literal do TRX; `survived` no subconjunto é o veredito, com o `expect` ao lado; rodada que cai é `inconclusive`. **Nunca a suíte inteira:** sem subconjunto utilizável (nenhum teste da feature nem citando os tipos mutados) o comando recusa antes de compilar (exit 2) e manda provar o cenário por leitura; subconjunto que some em runtime (base que não seleciona teste, ou cai) deixa os mutantes `inconclusive` com a mesma razão.
348
350
 
349
- **Nó `e2e: true`:** arquivo coberto **só** por nó de teste `e2e: true` é recusado (exit 2, nomeando mutante, arquivo e nó): a suíte testa o container do compose e o `mutate` recompila só localmente — todo mutante sobreviveria em falso. Com outro nó dono do arquivo, ele é o escolhido — mas, havendo qualquer nó `e2e: true` no config, só se algum arquivo de teste dele (com atributo de teste) citar um tipo declarado no arquivo mutado: o dono de um `.cs` é a suíte .NET, não a referência de projeto, e a unidade que só referencia a API daria sobrevivente em falso. Sem citação, recusa (exit 2) antes do snapshot. Nas duas recusas a saída prescreve o protocolo manual com o comando pronto do nó e2e, cada passo numa linha só encadeado por `&&` (nunca `;`): base verde antes do 1º mutante (`eval "$(morph-spec env <f>)" && morph-spec e2e up <f> && morph-spec heavy run -- <comando do nó>`), a mesma cadeia depois de cada edição, e restauração conferida por `git diff --quiet` seguida de um novo `e2e up` — registrado em `4-review`. Sem nó `e2e: true`, o sobrevivente é achado real e roda como antes.
351
+ **Nó `e2e: true`:** arquivo coberto **só** por nó de teste `e2e: true` é recusado (exit 2, nomeando mutante, arquivo e nó): a suíte testa o container do compose e o `mutate` recompila só localmente — todo mutante sobreviveria em falso. Com outro nó dono do arquivo, ele é o escolhido — mas, havendo qualquer nó `e2e: true` no config, só se algum arquivo de teste dele (com atributo de teste) citar um tipo declarado no arquivo mutado: o dono de um `.cs` é a suíte .NET, não a referência de projeto, e a unidade que só referencia a API daria sobrevivente em falso. Sem citação, recusa (exit 2) antes do snapshot. Nas duas recusas a saída prescreve o protocolo manual com o comando pronto do nó e2e, cada passo numa linha só encadeado por `&&` (nunca `;`): base verde antes do 1º mutante (`eval "$(morph-spec env <f>)" && morph-spec e2e up <f> && morph-spec heavy run -- <comando do nó>`), a mesma cadeia depois de cada edição, e restauração conferida por `git diff --quiet` seguida de um novo `e2e up` — registrado em `4-review`. Sem nó `e2e: true`, vale a regra do subconjunto acima.
350
352
 
351
353
  **Sem `<feature>`:** vale a única feature em andamento; sem ela (nenhuma, ou mais de uma), exige `--plan`, e a seção só é gravada com `--out` — sai no stdout, `section: null` no `--json`.
352
354
 
@@ -496,6 +498,10 @@ Sem fatia na linha de comando, a saída traz `reviews: { verifier, reviewer, sec
496
498
 
497
499
  **`morph-spec stack-scan [<feature>] [--json]`** — varredura determinística do código contra `config.project.stack`: só os stacks configurados são julgados (sem `project.stack`, nenhuma regra roda e o resultado é `não auditado: project.stack ausente em .morph/config/config.json`, nunca "Em compliance" — exit 0; stack sem regras de varredura sai `não auditado` pelo mesmo motivo; código de um stack não configurado é só anotado). `dotnet`: TargetFramework abaixo de net10 e ORM/banco diferente (Dapper puro, SQL Server, MySQL) são **Bloqueante**; `Guid.NewGuid()`, repository genérico (`IRepository<T>`/`IUnitOfWork` — código morto: apagar a pasta `Repository/` e as 2 linhas de DI), classe `*Service`/`*Manager` sobre `DbContext` e SQL cru (`FromSqlRaw`, `ExecuteSqlRaw`, `SqlQueryRaw`, `ExecuteQuery`) são **Alto**; classe não-sealed é **Médio**; pacote `Microsoft.EntityFrameworkCore|AspNetCore|Extensions` abaixo da major 10 é **Baixo**. `nextjs`: frontend sem Next.js é Bloqueante; Pages Router sem `app/` e biblioteca de UI diferente (MUI, antd, Chakra, Bootstrap, Mantine) são Médio. Código de teste não conta; `node_modules`, `bin`, `obj`, `.morph`, `.claude` e os contêineres de worktree ficam fora; arquivo ilegível é anotado, nunca derruba. Sem código fonte = GREENFIELD. As contagens são de texto e superestimam consumidores — o relatório aponta `graph affected <símbolo> --uses-only`. Desvio Bloqueante/Alto vira nó da árvore de decisões da `morph-plan`. Com `<feature>`, grava `.morph/features/<f>/2-plan/stack-scan.md` (exit 2 se a feature não existe, 1 fora da árvore dona); sem, imprime o markdown.
498
500
 
501
+ ### tests-audit
502
+
503
+ **`morph-spec tests-audit [--json]`** — auditoria **somente leitura** dos arquivos de teste rastreados (`git ls-files` + `isTestPath`): total de casos (`[Fact]`/`[Theory]`, `it`/`test`), os maiores arquivos e quatro listas de **candidatos** — nunca vereditos, nada é apagado: teste que lê `.md`/`.txt` ou tem 3+ asserts de frase literal (fixa texto, não comportamento); constante comparada ao próprio literal; família de 4+ `[Fact]` com o mesmo prefixo de nome (candidata a `[Theory]`); o mesmo assunto em mais de uma camada (`XHandlerTests` e `XEndpointTests`/`XIntegrationTests` — fique com a mais barata que morde). Fora de git: exit 2 com a razão. É o ponto de partida de uma limpeza: apagar teste e depois rodar `verify --record-baseline` grava a contagem menor.
504
+
499
505
  ## Suítes de teste (`project.tests[]`) e a divisão loop × suíte
500
506
 
501
507
  O nó `tests` do `verify` é **plural**: um projeto pode declarar quantas suítes tiver em
@@ -677,7 +683,7 @@ sem `--no-build` já compila sozinho, e pré-compilar ali só dobraria o relógi
677
683
  **O ciclo da baseline, e o número que só sai à mão.** `verify --record-baseline` é a única via de
678
684
  escrita: mede cada nó numa execução real e grava `expectedCount` de volta no `config.json` (escrita
679
685
  atômica `tmp.{pid}` + rename, preservando todo o resto do arquivo). Recusa **quatro** vezes — execução
680
- vermelha, execução **parcial** (sob `--filter`, mesmo com o verify verde: baseline de meia frota é
686
+ vermelha (fora o `count-drop` com exit 0, abaixo), execução **parcial** (sob `--filter`, mesmo com o verify verde: baseline de meia frota é
681
687
  afirmação sobre a outra metade que ninguém fez), contagem desconhecida, e **sem `config.json` ou sem
682
688
  `project.tests[]`** (erro de uso, exit 2; o arquivo nunca é criado implicitamente — gravar os nós que o
683
689
  fallback sintetiza exigiria inventar a declaração que o comando veio manter). Verde com gravação
@@ -685,11 +691,7 @@ recusada sai **exit 1**, com a razão em **stderr** (sobrevive ao `--json`). A g
685
691
  pelos **nós de teste**, não pelos quatro nós do verify: um `validators` vermelho não muda quantos
686
692
  testes rodaram. A execução que **gravou** a baseline (verde e inteira) persiste o stamp `verification` como qualquer verify: ela mediu a suíte toda, e rodá-la de novo só para ter prova seria pagar duas vezes pelo mesmo
687
693
  verde. Sem gravação (vermelha, parcial, contagem desconhecida) não há stamp, e o `reason` do `--json`
688
- nomeia o nó que impediu. **Consequência a conhecer:** um `expectedCount` digitado à
689
- mão **alto demais** (9 onde a suíte roda 7) deixa o verify vermelho por `count-drop`, e então
690
- `--record-baseline` recusa — preservando o 9. **Não há flag de regravação forçada:** rebaixe ou apague
691
- o número à mão e então re-meça pelo comando. O caso oposto se conserta sozinho — número baixo demais
692
- passa com aviso de alta, e o comando grava.
694
+ nomeia o nó que impediu. **Apagar teste é legítimo:** a única cor vermelha que a gravação aceita é o `count-drop` de um nó que saiu com exit 0 — a suíte ficou menor de propósito. `--record-baseline` grava o número menor que mediu, o nó fica verde nessa mesma execução, o stamp persiste e o `feedback` diz `[baseline] rebaixada: <nó> (9 → 7)`; o revisor do Gate 3 vê a queda e julga se foi limpeza. Sem a flag, a queda continua reprovando (regressão silenciosa). Queda ao lado de outro nó vermelho não grava nada.
693
695
 
694
696
  **A divisão loop × suíte.** É a resposta ao "a suíte inteira não cabe numa chamada":
695
697
 
@@ -41,7 +41,10 @@ diferente") é nota; nota não bloqueia e não pede rodada nova. Na dúvida, sem
41
41
  ## Verificador (`4-review/verifier.md`)
42
42
 
43
43
  Missão: **provar cada cenário** do `2-plan/checks.json`. O default de todo `Cn` é NÃO PROVADO;
44
- critério que você não verificou nunca vira PROVADO por inferência.
44
+ critério que você não verificou nunca vira PROVADO por inferência. A prova é a que o plano declarou
45
+ (`prova:` do Cn): `leitura` prova-se pelo `arquivo:linha` e pela fiação, **sem teste** — Cn `leitura`
46
+ sem teste não é NÃO PROVADO, e pedir teste para ele não é bloqueador. Só Cn com `risco:` exige teste
47
+ que morde.
45
48
 
46
49
  ```markdown
47
50
  ## Checks
@@ -82,7 +85,9 @@ morde. Escreva `4-review/mutants.json` e rode `morph-spec mutate {feature}` **um
82
85
 
83
86
  `expect` é obrigatório: diga qual teste deve falhar e por quê **antes** de rodar — mutante cujo
84
87
  efeito você não sabe prever tende a ser equivalente. Barra invertida do código vira `{{BS}}`.
85
- Sobreviveu numa linha que um cenário exige → bloqueador `teste`. Sobreviveu em outra → nota. Cole a
88
+ Sobreviveu numa linha que um cenário exige → bloqueador `teste`. Sobreviveu em outra → nota.
89
+ Sem subconjunto (exit 2) ou `inconclusive`: a mutação não roda a suíte inteira — prove por leitura
90
+ (`arquivo:linha` + o teste que cobre), nunca em laço. Cole a
86
91
  seção `## Mutações` que a ferramenta imprime. A reversão é da ferramenta; rodada morta no meio:
87
92
  `morph-spec mutate --restore`.
88
93
 
@@ -109,6 +114,11 @@ Read-only, sobre o commit do dispatch: `git diff {base}..{sha}`, `git show {sha}
109
114
  árvore de trabalho — o verificador pode estar com um mutante aplicado nela. Default SHIP.
110
115
 
111
116
  1. **Escopo**: o diff contra as fatias (`arquivos:`) e o `## Contrato de implementação` do plano.
117
+ Teste além do orçamento também é escopo: teste para Cn `prova: leitura`, mais de um teste por Cn de
118
+ risco sem porquê no plano, teste que só fixa texto/constante/DTO, o mesmo comportamento em duas
119
+ camadas — bloqueador `escopo` (apague, não acrescente). `expectedCount` menor no
120
+ `.morph/config/config.json` do diff sem teste apagado no mesmo diff é bloqueador `regressao`: é
121
+ suíte que parou de rodar, não limpeza.
112
122
  2. **Arquitetura**: padrão ou abstração nova contra o stack do projeto e os standards (camada que o
113
123
  projeto não usa, repositório sobre o DbContext, dependência nova sem decisão no plano).
114
124
  3. **Regressão**: para cada símbolo público alterado, `morph-spec graph affected <símbolo> --prod --json`.
@@ -6,41 +6,16 @@ Comprehensive hooks system for enforcing spec-driven development at the Claude C
6
6
 
7
7
  ```
8
8
  framework/hooks/
9
- ├── claude-code/ # Claude Code native hooks
10
- │ ├── core/
11
- │ │ ├── worktree-context.js # SessionStart: worktree feature identity / active-feature summary
12
- │ │ ├── worktree-create.js # WorktreeCreate: governed tree via `morph-spec worktree setup|provision`
13
- │ │ ├── worktree-remove.js # WorktreeRemove: safe teardown via `worktree remove --if-clean`
14
- │ │ ├── block-destructive-worktree-removal.js # PreToolUse(Bash): raw removal follows junctions out
15
- │ │ ├── block-raw-worktree-add.js # PreToolUse(Bash): raw `git worktree add` is ungoverned
16
- │ │ └── warn-bash-source-write.js # PreToolUse(Bash): source written by redirect skips the Write/Edit guards
17
- │ ├── user-prompt/
18
- │ │ └── enrich-prompt.js # Context-aware prompt enrichment
19
- │ ├── pre-tool-use/
20
- │ │ ├── protect-spec-files.js # Block edits to approved spec artifacts
21
- │ │ └── enforce-phase-writes.js # Enforce writes to correct phase dir
22
- │ ├── post-tool-use/
23
- │ │ └── dispatch.js # Dispatch on CLI commands (auto-checkpoint)
24
- │ ├── stop/
25
- │ │ └── validate-completion.js # Advisory: warn about incomplete work
26
- │ ├── pre-compact/
27
- │ │ └── save-morph-context.js # Snapshot state before compaction
28
- ├── shared/ # Reusable utilities for all hooks
29
- │ ├── state-reader.js # Read-only state.json accessor
30
- │ ├── phase-utils.js # Phase constants and path utilities
31
- │ ├── hook-response.js # JSON response builders
32
- │ └── stdin-reader.js # Stdin JSON reader
33
- ├── git/ # Git hooks (Bash)
34
- │ ├── pre-commit/
35
- │ │ ├── orchestrator.sh # Master hook dispatcher
36
- │ │ └── agents.sh # Validates agents.json schema
37
- │ ├── commit-msg/
38
- │ │ └── conventional-commits.sh # Enforces conventional commits
39
- │ └── pre-push/
40
- │ └── run-tests.sh # Runs test suite before push
41
- └── README.md # This file
9
+ ├── claude-code/
10
+ │ ├── core/ # every hook, plus chain.js (one process per multi-hook group)
11
+ │ └── statusline.py # global statusline
12
+ ├── shared/ # code hooks share (and src/ imports): state reader, guards, e2e, heavy, telemetry
13
+ └── README.md
42
14
  ```
43
15
 
16
+ The registered set is `MORPH_HOOKS` in `src/utils/hooks-installer.js` — the table below lists it. No hook builds,
17
+ typechecks or runs tests: that is `morph-spec verify`.
18
+
44
19
  ## Hook Events
45
20
 
46
21
  | Event | Hook | Type | Purpose |
@@ -104,17 +79,6 @@ The installer writes to `.claude/settings.local.json`:
104
79
  }
105
80
  ```
106
81
 
107
- ### Git Hooks
108
-
109
- ```bash
110
- # In your project root
111
- cd .git/hooks
112
- ln -sf ../../framework/hooks/git/pre-commit/orchestrator.sh pre-commit
113
- ln -sf ../../framework/hooks/git/commit-msg/conventional-commits.sh commit-msg
114
- ln -sf ../../framework/hooks/git/pre-push/run-tests.sh pre-push
115
- chmod +x pre-commit commit-msg pre-push
116
- ```
117
-
118
82
  ## Shared Utilities
119
83
 
120
84
  All Claude Code hooks import from `framework/hooks/shared/`:
@@ -165,7 +165,7 @@ function handleAskUserQuestion(input, sessionRoot) {
165
165
 
166
166
  // Same contract as `approve --self-assessed`: approving review without its reports is stamped, never silent.
167
167
  const selfAssessed = gate === 'review' && feature?.workType !== 'hotfix'
168
- && ['verifier.md', 'reviewer.md'].some((f) => !existsSync(join(root, '.morph', 'features', featureName, '4-review', f)));
168
+ && (feature?.workType === 'chore' ? ['reviewer.md'] : ['verifier.md', 'reviewer.md']).some((f) => !existsSync(join(root, '.morph', 'features', featureName, '4-review', f)));
169
169
  if (selfAssessed) {
170
170
  warnings.push(
171
171
  `⚠️ [morph-spec] Gate 3 de "${featureName}" aprovado SEM os relatórios de review independentes (verifier.md, reviewer.md) — ` +
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env bash
2
- # morph-spec statusline — installed globally to ~/.claude/statusline.sh
3
- # Claude Code invokes this with JSON via stdin after each response.
4
- # Requires: Python 3 available as `python3` or `python` on PATH.
5
-
6
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
- if command -v python3 &>/dev/null; then
8
- python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
9
- elif command -v python &>/dev/null; then
10
- python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
11
- fi
1
+ #!/usr/bin/env bash
2
+ # morph-spec statusline — installed globally to ~/.claude/statusline.sh
3
+ # Claude Code invokes this with JSON via stdin after each response.
4
+ # Requires: Python 3 available as `python3` or `python` on PATH.
5
+
6
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ if command -v python3 &>/dev/null; then
8
+ python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
9
+ elif command -v python &>/dev/null; then
10
+ python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
11
+ fi
@@ -15,7 +15,7 @@ stacks:
15
15
  - **frontend-nextjs-data-fetching** (`frontend/nextjs/data-fetching.md`) — Server Component com `BACKEND_API_PREFIX`; Client Component só pelo proxy same-origin com `PROXY_API_PREFIX`. TanStack Query 5 com key factory por feature e `invalidateQueries({ queryKey })`; toda resposta passa por Zod. PROIBIDO: `useEffect(() => fetch…)` e paginar/filtrar a coleção inteira no browser.
16
16
  - **frontend-nextjs-forms** (`frontend/nextjs/forms.md`) — react-hook-form + Zod (`zodResolver`) + Field do shadcn via `Controller`; tipo por `z.infer`; nunca `useState` por campo. Submit por `useMutation`, erro em `root`. Edição reusa o schema de criação (`.partial()`). Zod 4: `z.email()`.
17
17
  - **frontend-nextjs-state-management** (`frontend/nextjs/state-management.md`) — Estado de servidor no TanStack Query, nunca copiado para `useState`. Página, ordenação, filtros e aba ativa na URL (`searchParams`). Context só para tema, usuário ou locale. NUNCA Zustand/Redux antes de esgotar Server Components + TanStack Query.
18
- - **frontend-nextjs-testing** (`frontend/nextjs/testing.md`) — Vitest + Testing Library; MSW para API; Playwright para e2e. Teste comportamento, não implementação. Orçamento: um teste por pós-condição + um de regressão por bug; cenários do mesmo comportamento em `it.each`; nada de teste que só confere string de copy.
18
+ - **frontend-nextjs-testing** (`frontend/nextjs/testing.md`) — Vitest + Testing Library; MSW para API; Playwright para e2e. Teste comportamento, não implementação. Orçamento por risco: um teste por cenário com `risco:` + o de regressão do bug, componente sem risco é `leitura`/`e2e`; cenários do mesmo comportamento em `it.each`; nada de teste que só confere string de copy.
19
19
  - **frontend-nextjs-nextjs-patterns** (`frontend/nextjs/nextjs-patterns.md`) — Route Handler é BFF: `auth.getSession()` (401 sem sessão) e delega ao .NET; banco NUNCA em route handler. Zod no client e no server (`safeParse`, 400). `strict: true` + `noUncheckedIndexedAccess`; sem `any` em resposta de API. shadcn via `npx shadcn@latest add`.
20
20
  - **frontend-nextjs-motion-patterns** (`frontend/nextjs/motion-patterns.md`) — Motion via `motion/react` (nunca `framer-motion` junto). Variants em `motion/variants.ts` com durations/easings de `@/lib/design-tokens`. `useReducedMotion()` obrigatório; o `layout.tsx` raiz segue Server Component. 3–5 momentos de motion por página; cena guiada por scroll é do GSAP, nunca no mesmo elemento.
21
21
  - **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
@@ -4,7 +4,7 @@ description: Guia a criação de uma landing page nova ponta-a-ponta a partir do
4
4
  version: core
5
5
  when_to_use: Use quando for iniciar um novo projeto de LP com o template Umbraco genérico
6
6
  user-invocable: true
7
- cliVersion: "9.1.0"
7
+ cliVersion: "9.2.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -4,7 +4,7 @@ description: Migra uma landing page existente (React/Vite, Next.js estático ou
4
4
  version: core
5
5
  when_to_use: Use quando o usuário pedir "use /migrate-lp-to-umbraco {projeto}" ou descrever a necessidade de migrar uma LP para Umbraco
6
6
  user-invocable: true
7
- cliVersion: "9.1.0"
7
+ cliVersion: "9.2.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -4,7 +4,7 @@ description: Executa as fatias do plano de uma feature morph-spec — dispatch p
4
4
  version: core
5
5
  when_to_use: Use com o gate do plano aprovado, em "use /morph-apply {feature}" ou "implementar feature"
6
6
  user-invocable: true
7
- cliVersion: "9.1.0"
7
+ cliVersion: "9.2.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -78,7 +78,7 @@ relatório) está no corpo da persona gerada e chega a todo dispatch sem ser col
78
78
  ```
79
79
  dispatch Agent (subagent_type: "morph-{id}") with prompt:
80
80
  Fatia F{n} — <título>
81
- cobre: <cada Cn com o texto literal do cenário e a `prova:`>
81
+ cobre: <cada Cn com o texto literal do cenário, a `prova:` e o `risco:`>
82
82
  arquivos: <os `arquivos:` da fatia>
83
83
  standards: <por id: o digest de `morph-spec standards --find` + as 3-8 regras que ESTA fatia aplica>
84
84
  reuso: <as linhas de `## Reuso` que tocam a fatia — conceito, veredito, alvo>
@@ -128,7 +128,7 @@ config **antes** do verify: o stamp grava o HEAD, e código ou `config.json` com
128
128
 
129
129
  Depois `morph-spec verify {feature} --json`, **sem filtro** — nunca recorte o
130
130
  verify da feature. `--filter` é do loop do próprio sub-agente; o Gate 3 e o CI rodam a suíte
131
- inteira. Nó de teste sem `expectedCount`: acrescente `--record-baseline` a ESTA rodada — ela mede e
131
+ inteira. Nó de teste sem `expectedCount`, ou fatia que apagou teste de propósito (limpeza, `morph-spec tests-audit`): acrescente `--record-baseline` a ESTA rodada — ela mede e
132
132
  prova de uma vez; nunca um segundo verify só para gravar a baseline. Vermelho: corrija com `verify.feedback` (direto no código, ou re-despachando a fatia dona)
133
133
  e rode de novo; **3 tentativas** sem verde → `AskUserQuestion` ("Verify de {feature} vermelho após 3
134
134
  tentativas — aceitar com ressalva, continuar tentando, ou parar?").
@@ -4,7 +4,7 @@ description: ADW cirúrgico de hotfix — resolve uma queda/bug crítico de prod
4
4
  version: core
5
5
  when_to_use: Use quando chegar "/morph-hotfix {feature}", quando a morph-plan rotear o pedido como workType hotfix, ou quando o usuário disser "produção caiu / hotfix / urgente" a respeito de um incidente
6
6
  user-invocable: true
7
- cliVersion: "9.1.0"
7
+ cliVersion: "9.2.0"
8
8
  ---
9
9
 
10
10
  # morph-hotfix
@@ -53,8 +53,8 @@ Escreva o `plan.md` mínimo (~25 linhas) numa única escrita: o hotfix nasce com
53
53
 
54
54
  - `## Problema` — o sintoma em produção, o root cause que o scout localizou e o blast radius.
55
55
  - `## Cenários` — dois, sempre:
56
- - **C1** — o sintoma ausente: dado o estado que quebrava, quando a ação que falhava, então o resultado certo. `prova: teste` (ou `e2e`).
57
- - **C2** — a regressão do blast radius: o que o fix poderia quebrar continua funcionando. `prova: teste`.
56
+ - **C1** — o sintoma ausente: dado o estado que quebrava, quando a ação que falhava, então o resultado certo. `prova: teste` (ou `e2e`) e `risco: regressão` — o teste do bug real, o único que o hotfix escreve; o `plan-check` recusa o plano sem ele.
57
+ - **C2** — o blast radius: o que o fix poderia quebrar continua funcionando. `prova: leitura` (o caminho tocado, citado por `arquivo:linha`) ou um teste existente que já o cobre — nunca um teste novo.
58
58
  - `## Decisões` com `### Conflitos com o acervo` (`nenhum` vale).
59
59
  - `## Fatias` — uma, `### F1 — fix`, com `persona:`, `arquivos:` e `cobre: C1, C2`.
60
60
 
@@ -4,7 +4,7 @@ description: Cuida da RAM e da CPU da máquina enquanto o morph-spec trabalha
4
4
  version: core
5
5
  when_to_use: Use quando a máquina estiver lenta ou sem RAM, um comando pesado esperou ou foi recusado pela fila, sobrou worktree de sub-agent, ou vários sub-agents pesados vão rodar em paralelo
6
6
  user-invocable: false
7
- cliVersion: "9.1.0"
7
+ cliVersion: "9.2.0"
8
8
  ---
9
9
 
10
10
  # morph-machine