@polymorphism-tech/morph-spec 9.4.0 → 9.5.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 (85) hide show
  1. package/CLAUDE.md +1 -1
  2. package/README.md +2 -2
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/MORPH.md +23 -33
  7. package/framework/agents.json +6 -7
  8. package/framework/docs/CLI.md +3 -3
  9. package/framework/hooks/README.md +2 -2
  10. package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
  11. package/framework/hooks/claude-code/core/comment-guard.js +1 -0
  12. package/framework/hooks/claude-code/core/denylist.json +63 -1
  13. package/framework/hooks/claude-code/core/enforce-phase-writes.js +0 -8
  14. package/framework/hooks/claude-code/core/gate-guard.js +0 -8
  15. package/framework/hooks/claude-code/core/loop-detect.js +0 -3
  16. package/framework/hooks/claude-code/core/pre-bash-denylist.js +2 -1
  17. package/framework/hooks/claude-code/core/protect-spec-files.js +0 -7
  18. package/framework/hooks/claude-code/core/validate-completion.js +0 -4
  19. package/framework/hooks/claude-code/core/worktree-context.js +0 -2
  20. package/framework/hooks/claude-code/statusline.py +3 -44
  21. package/framework/hooks/shared/build-check.js +12 -19
  22. package/framework/hooks/shared/comment-rules.js +11 -1
  23. package/framework/hooks/shared/hook-response.js +0 -4
  24. package/framework/hooks/shared/payload-utils.js +1 -35
  25. package/framework/hooks/shared/phase-utils.js +0 -12
  26. package/framework/hooks/shared/slice-progress.js +0 -2
  27. package/framework/hooks/shared/state-reader.js +0 -19
  28. package/framework/hooks/shared/telemetry-logger.js +1 -1
  29. package/framework/rules/frontend-standards.md +1 -2
  30. package/framework/rules/nextjs-standards.md +9 -8
  31. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  32. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  33. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  34. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  35. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  36. package/framework/skills/core/morph-media/SKILL.md +1 -1
  37. package/framework/skills/core/morph-plan/SKILL.md +1 -1
  38. package/framework/skills/core/morph-review/SKILL.md +1 -1
  39. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  40. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  41. package/framework/standards/STANDARDS.json +32 -29
  42. package/framework/standards/STANDARDS.meta.json +13 -15
  43. package/framework/standards/architecture/code-comments.md +1 -1
  44. package/framework/standards/frontend/admin-app/data-table.md +1 -3
  45. package/framework/standards/frontend/admin-app/page-structure.md +8 -4
  46. package/framework/standards/frontend/nextjs/app-router.md +3 -4
  47. package/framework/standards/frontend/nextjs/components.md +2 -15
  48. package/framework/standards/frontend/nextjs/data-fetching.md +2 -3
  49. package/framework/standards/frontend/nextjs/lint.md +141 -0
  50. package/framework/standards/frontend/nextjs/motion-patterns.md +2 -7
  51. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -2
  52. package/framework/standards/frontend/nextjs/nextjs-patterns.md +5 -7
  53. package/framework/standards/frontend/nextjs/project-structure.md +3 -2
  54. package/framework/standards/frontend/nextjs/state-management.md +6 -7
  55. package/framework/templates/README.md +4 -4
  56. package/framework/templates/REGISTRY.json +21 -309
  57. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +54 -56
  58. package/package.json +3 -2
  59. package/src/core/paths/output-schema.js +0 -9
  60. package/src/core/state/feature-identity.js +0 -2
  61. package/src/core/state/state-manager.js +1 -169
  62. package/src/core/templates/template-renderer.js +0 -23
  63. package/src/dashboard/app/lib/format.js +0 -7
  64. package/src/dashboard/app/lib/phases.js +0 -7
  65. package/src/dashboard/scanner/activity-scanner.js +0 -4
  66. package/src/lib/ai/ai-project-scan.js +1 -6
  67. package/src/lib/graph/graphify-runtime.js +0 -1
  68. package/src/lib/mutation/snapshot.js +1 -5
  69. package/src/lib/risk-detector.js +0 -5
  70. package/src/lib/stack/stack-profile.js +0 -4
  71. package/src/lib/stack/stack-scan.js +15 -4
  72. package/src/lib/tasks/eval-node.js +1 -7
  73. package/src/lib/validators/validation-runner.js +0 -43
  74. package/src/lib/validators/verify-runner.js +18 -2
  75. package/src/utils/agents-installer.js +12 -19
  76. package/src/utils/claude-settings-manager.js +0 -28
  77. package/src/utils/file-copier.js +0 -53
  78. package/framework/hooks/shared/activity-logger.js +0 -131
  79. package/framework/standards/frontend/design-system/naming.md +0 -68
  80. package/src/commands/validation/index.js +0 -2
  81. package/src/lib/validators/code-budget/index.js +0 -2
  82. package/src/lib/validators/contracts/index.js +0 -1
  83. package/src/lib/validators/nextjs/index.js +0 -1
  84. package/src/lib/validators/packages/index.js +0 -1
  85. package/src/lib/validators/reuse/index.js +0 -2
package/CLAUDE.md CHANGED
@@ -18,7 +18,7 @@ npm install --prefix src/dashboard
18
18
  # Dev loop: only the test files the current change can reach (vs the merge-base with main)
19
19
  npm run test:changed # add -- --list to see the selection and why
20
20
 
21
- # Run all tests (files in parallel, ~2.5 min; the repo-mutating files run serially after)
21
+ # Run all tests (files in parallel, cpus-1 at a time; ~6 min on a 16-core Windows box, bound by process/git contention)
22
22
  npm test
23
23
 
24
24
  # A subtree or file (still parallel); --test-concurrency=1 to debug serially
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.4.0
6
+ **Version:** 9.5.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.4.0 by [Polymorphism Tech](https://polymorphism.tech)*
383
+ *morph-spec v9.5.0 by [Polymorphism Tech](https://polymorphism.tech)*
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "9.4.0",
3
+ "version": "9.5.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.4.0 by Polymorphism Tech*
188
+ *morph-spec v9.5.0 by Polymorphism Tech*
@@ -249,4 +249,4 @@ morph-spec doctor
249
249
 
250
250
  ---
251
251
 
252
- *morph-spec v9.4.0 by Polymorphism Tech*
252
+ *morph-spec v9.5.0 by Polymorphism Tech*
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Leia uma vez por sessão (e de novo após `/compact`). Os comandos imprimem a própria mecânica (sinais, vereditos, próximo passo); referência completa em `.morph/framework/CLI.md`.
4
4
  >
5
- > **Depois de ler:** o SessionStart já injeta a feature ativa e a linha `▶ próximo:`; `morph-spec next <f>` decide o passo do disco (fase, gates, relatórios, verify). Estado autoritativo: `.morph/features/{f}/feature.json`; `.morph/state.json` é índice gitignored — nunca o edite. Sem feature ativa → pergunte qual trabalhar.
5
+ > **Depois de ler:** `morph-spec next <f>` decide o passo do disco (fase, gates, relatórios, verify).
6
6
 
7
7
  ---
8
8
 
@@ -16,8 +16,8 @@ Entre gates, você opera sozinho: o dev valida, você executa.
16
16
  | Ambiguidade de escopo coberta pelo plano | Resolve pelo lado conservador e registra no relatório |
17
17
  | Ambiguidade bloqueante (requisito ausente ou contraditório) | Uma pergunta via `AskUserQuestion` |
18
18
  | Cenário `Cn` sem evidência | Loop de correção com o feedback, como o verify vermelho, antes da review |
19
- | `next` devolve `escalate` (3 verifies vermelhos seguidos) | `AskUserQuestion` (aceitar/continuar/parar) — o único ponto de pausa do loop. Nunca uma 4ª tentativa por conta própria nem re-dispatch: sem informação nova, ela repete o erro |
20
- | `verify` exit 3 (fila pesada cheia) | A fila já esperou: no máximo uma chamada nova, depois avise. Nunca laço de espera |
19
+ | `next` devolve `escalate` | `AskUserQuestion` (aceitar/continuar/parar), o único ponto de pausa do loop; nunca uma 4ª tentativa por conta própria |
20
+ | `verify` exit 3 | fila cheia, não vermelho: no máximo uma chamada nova, nunca laço de espera |
21
21
 
22
22
  **Regra de ouro:** dúvida que não bloqueia → resolva e registre; dúvida que bloqueia → uma pergunta, no máximo. Vale **depois** do gate do plano: na abertura, o **modo entrevista** (`morph-plan` §3) pergunta até fechar o entendimento, e esse é o comportamento desejado.
23
23
 
@@ -38,14 +38,7 @@ O harness conhece o stack. Só pergunte sobre tecnologia com ambiguidade real.
38
38
  | Frontend | Next.js App Router + Tailwind + Framer Motion + Shadcn |
39
39
  | Jobs async | Hangfire, só com razão real |
40
40
 
41
- Padrões proibidos (valem em todo código; os standards detalham):
42
- - `Guid.NewGuid()` → `Guid.CreateVersion7()`: o v7 é ordenado no tempo e não fragmenta o índice do Postgres.
43
- - Classe não-`sealed` sem justificativa: herança aberta por acidente vira contrato que ninguém desenhou.
44
- - Exception como fluxo de controle: o Result deixa o erro esperado no tipo, e a exception fica para o inesperado.
45
- - Camada de Service/Manager entre handler e dados: no VSA o handler fala com o `ApplicationDbContext` direto; a camada só repassa chamada.
46
- - Repository genérico (`IRepository<T>`/`IUnitOfWork`): `DbSet<T>` já é repository e `DbContext` já é unit of work.
47
- - SQL cru (`FromSqlRaw`, `ExecuteQuery(string)`) sem ADR no `plan.md`: o LINQ é tipado e parametrizado; SQL específico ou concorrência vão para um store dedicado com o porquê no ADR.
48
- - Redeclarar tipo/componente que já existe: duas definições divergem em silêncio. Antes de criar, `morph-spec graph explain <nome>` (sem grafo: `grep -rn`); criar exige `CRIAR` com `why` em `## Reuso` do `plan.md`.
41
+ Padrões proibidos (os standards detalham; o mecânico, o build e o lint cobram): exception como fluxo de controle (o Result põe o erro esperado no tipo); Service/Manager entre handler e dados; `IRepository<T>`/`IUnitOfWork` (`DbSet<T>` já é repository); SQL cru sem ADR; redeclarar tipo que já existe (antes, `morph-spec graph explain <nome>`, sem grafo `grep -rn`; criar exige `CRIAR` com `why` em `## Reuso`).
49
42
 
50
43
  ---
51
44
 
@@ -89,9 +82,7 @@ Toda feature nasce com `description` obrigatória e um `workType`: `morph-spec c
89
82
 
90
83
  **Ramo uiux (critério objetivo):** feature/bug que cria ou altera superfície de UI (componente, página, tela, estilo, navegação) toma o ramo `1-design/` depois do gate do plano (`morph-spec advance {feature} --design`, também com o gate já aprovado pelo humano; nunca `1-design/` à mão); sem superfície de UI, direto para implement. Skill: `morph-uiux`. A `morph-plan` §6 decide uma vez; nenhum outro ponto redecide.
91
84
 
92
- **Router:** `create --request` classifica por palavra-chave. Exit 2 (empate ou nenhum sinal) imprime os candidatos: você decide como roteador e re-roda com `--type`. `AskUserQuestion` só quando o pedido é genuinamente indeterminado.
93
-
94
- **Skip:** `chore`/`hotfix` nascem com o gate `plan` carimbado por política e começam em `implement`; escrevem um `2-plan/plan.md` curto (uma fatia; no hotfix, C1 = sintoma ausente, C2 = regressão) e rodam `plan-check`. Mudança: `morph-spec retype <f> <tipo>` (só antes do Gate 3; o esqueleto intocado do plano sai junto).
85
+ **Skip:** `chore`/`hotfix` começam em `implement` com um `plan.md` curto de uma fatia (hotfix: C1 = sintoma ausente, C2 = regressão) e `plan-check`; trocar o tipo: `morph-spec retype`.
95
86
 
96
87
  **Branch:** toda feature nasce em `morph/{f}`, ramificada da default, com ou sem `--worktree`, e fecha igual nos dois com `morph-spec finish <f> --pr|--merge` (§2.2).
97
88
 
@@ -99,16 +90,15 @@ Toda feature nasce com `description` obrigatória e um `workType`: `morph-spec c
99
90
 
100
91
  Features independentes rodam em paralelo, uma por worktree e uma instância de Claude Code por worktree: `create <f> --worktree` (ou `worktree setup <f>`) na branch `morph/{f}`.
101
92
 
102
- - **Uma feature = um worktree = uma instância.** Duas instâncias na mesma feature: o setup recusa (exit 3).
103
- - `feature.json` viaja commitado em `morph/{f}`; a cópia da raiz é esqueleto. Mutadores recusam gravar fora da árvore dona.
104
- - **Só o morph cria worktree:** `worktree setup <f>` (feature) ou `worktree provision <n>` (isolamento de fatia, branch `morph-task/<n>`). `git worktree add` cru é bloqueado; `EnterWorktree`/`-w`/`isolation:"worktree"` passam pelo hook `WorktreeCreate` e caem no contêiner (`worktrees/` ou `worktree.root`); fora dele = criado sem o morph (`doctor --worktrees`). Já existe: abra o `claude` DENTRO dela; `EnterWorktree(path)` sempre pede aprovação. Infra (`node_modules`, `.claude`, `.morph/framework`) entra por junction; faltou → `worktree link --all`.
105
- - **Só o morph remove worktree.** `git worktree remove` (com ou sem `--force`), `rm -rf`, `Remove-Item -Recurse` e o explorador **atravessam a junction** e apagam o conteúdo real da raiz (`.claude`, `node_modules`, `.morph/framework`), saindo com sucesso. Só o morph desmonta a junction antes de remover: fechar a feature = `morph-spec finish <f> --pr|--merge`; só desmontar = `morph-spec worktree remove <f>` (ou `--target <path>`). Sobra de sub-agent (`agent-<hex>`): `worktree reap` remove só a que parou limpa e sem commit exclusivo.
106
- - **Ambiente dedicado:** o setup escreve `.morph/worktree.env` (`PORT`, `API_PORT`, `POSTGRES_PORT`, `COMPOSE_PROJECT_NAME=morph-{f}`) e cada worktree sobe o seu compose; o hook de Bash injeta as variáveis (fora do Claude, `eval "$(morph-spec env)"`). Porta fixa ou `container_name` no compose reprova o nó e2e. `finish` derruba o stack com volumes (`--keep-volumes` preserva). Build/testes/e2e disputam 2 slots por usuário (`heavy status`): exit 3 é fila cheia, não vermelho; fora do `verify`, `heavy run -- <cmd>`.
93
+ - **Uma feature = um worktree = uma instância.** `feature.json` viaja commitado em `morph/{f}`; a cópia da raiz é esqueleto.
94
+ - **Só o morph cria worktree:** `worktree setup <f>` (feature) ou `worktree provision <n>` (isolamento de fatia, branch `morph-task/<n>`). `EnterWorktree`/`-w`/`isolation:"worktree"` passam pelo hook `WorktreeCreate` e caem no contêiner (`worktrees/` ou `worktree.root`); fora dele = criado sem o morph (`doctor --worktrees`). Já existe: abra o `claude` DENTRO dela; `EnterWorktree(path)` sempre pede aprovação. Infra (`node_modules`, `.claude`, `.morph/framework`) entra por junction; faltou → `worktree link --all`.
95
+ - **Só o morph remove worktree.** Qualquer outra remoção atravessa a junction e apaga o conteúdo real da raiz (os hooks barram as formas conhecidas; o explorador, não). Fechar = `morph-spec finish <f> --pr|--merge`; só desmontar = `morph-spec worktree remove <f>`. Sobra de sub-agent (`agent-<hex>`): `worktree reap` remove só a que parou limpa e sem commit exclusivo.
96
+ - **Ambiente dedicado:** o setup escreve `.morph/worktree.env` (`PORT`, `API_PORT`, `POSTGRES_PORT`, `COMPOSE_PROJECT_NAME=morph-{f}`) e cada worktree sobe o seu compose; o hook de Bash injeta as variáveis (fora do Claude, `eval "$(morph-spec env)"`). `finish` derruba o stack com volumes (`--keep-volumes` preserva). Fora do `verify`, build e teste pesados vão por `heavy run -- <cmd>`.
107
97
  - **Migração destrutiva → integre antes de abrir irmãos:** os irmãos ramificam do schema velho e quebram no merge.
108
98
  - **`node_modules` é junction compartilhada:** dependência mudada afeta todos os irmãos; integre antes de seguir em paralelo.
109
99
  - **Handoff:** `.morph/features/{f}/handoff.md` (regenerável por `morph-spec handoff <f>`): leia antes de re-derivar contexto; nunca edite.
110
100
  - **Ownership:** sem lock; colisão se resolve no plano (ondas do `plan-check`) e por construção (uma feature = um escritor). Visão agregada: `fleet`.
111
- - **Fechamento:** aprove o Gate 3 e rode `morph-spec finish <f> --pr|--merge`. `--merge` integra na hora (`--no-ff` local, sem push). **`--pr` não integra:** só abre o PR, e a feature fica `aguardando-merge` até alguém mergear — nunca "entregue" só por `finished: true`. Nos dois, o worktree é desmontado e as portas liberadas.
101
+ - **Fechamento:** aprove o Gate 3 e rode `morph-spec finish <f> --pr|--merge`. **`--pr` não integra:** a feature fica `aguardando-merge` até alguém mergear, nunca "entregue" só por `finished: true`.
112
102
 
113
103
  ---
114
104
 
@@ -116,20 +106,20 @@ Features independentes rodam em paralelo, uma por worktree e uma instância de C
116
106
 
117
107
  ### Plan (`2-plan/`)
118
108
 
119
- - `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`.
120
- - `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.
109
+ - `plan.md` — fonte única da feature (esqueleto do `create`; chore/hotfix: você o cria), gramática em `morph-spec plan-check --help`. Lido uma vez por sessão e pós-`/compact`.
110
+ - `checks.json` — escrito só pelo `plan-check`; é a régua que o verificador prova item a item.
121
111
  - `stack-scan.md` e `scout-map.md` (§6) — insumos do plano.
122
112
 
123
- **Congelamento:** aprovado o gate do plano, o corpo do `plan.md` trava: quem implementa não afrouxa o critério que prova o trabalho. Congelar o contrato não é congelar um erro de fato: o que o código desmente vai num bloco `## Errata` no fim do arquivo, append-only e fora do `planHash`. Mudança de escopo exige `morph-spec unapprove {f} plan`, `plan-check` e re-aprovar.
113
+ **Congelamento:** aprovado o gate do plano, o corpo do `plan.md` trava: quem implementa não afrouxa o critério que prova o trabalho. Fato que o código desmente vai em `## Errata` no fim (append-only); escopo mudou → `morph-spec unapprove {f} plan`.
124
114
 
125
115
  ### Implement (`3-implement/`)
126
116
 
127
- - `3-implement/reports/F{n}.md` — um por fatia (sub-agent, ou você se inline); ausente = pendente, 1ª linha `PARCIAL` = parcial, `BLOQUEADO` = decisão sua. Registro da implementação (`**Arquivos:**`, `**Standards:**`; sem recap); formato em `morph-apply`.
117
+ - `3-implement/reports/F{n}.md` — um por fatia (sub-agent, ou você se inline); formato em `morph-apply`.
128
118
  - `3-implement/evidencias/{Cn}.md` — no cenário `prova: eval` (entrada, modelo, saída, veredito) ou `prova: manual` (passo e resultado).
129
119
 
130
120
  ### Review (`4-review/`)
131
121
 
132
- - `verifier.md`, `reviewer.md`, `security.md` (só com caminho sensível) — `**Verdict:** SHIP|FIX`, `**Reviewed-SHA:**`, `## Bloqueadores` (tipo + evidência), notas. O do verificador traz `## Checks` (`| Cn | PROVADO|NÃO PROVADO|FALHA | evidência |`). Append-only: vale o último veredito.
122
+ - `verifier.md`, `reviewer.md`, `security.md` (só com caminho sensível) — formato em `.morph/framework/evals/review-checklist.md`; vale o último veredito.
133
123
 
134
124
  ---
135
125
 
@@ -157,11 +147,11 @@ Em `trust: "auto"` (default, `.morph/config/config.json → trust`), um gate sem
157
147
  | `designSystemDivergence` | fontes de design conflitantes (tokens, versão, fonte aprovada) |
158
148
  | `standardsViolations` | plano ou código viola um padrão proibido (lista não vazia) |
159
149
 
160
- Risco que nenhum sinal representa: `morph-spec gate-decision <feature> <gate> --pause <bool> --reason <txt>`. Nunca edite `gateDecisions` à mão — colide com o hook `state-sync`. A tabela completa de sinais está em CLI.md (`gate-check`).
150
+ Risco que nenhum sinal representa: `morph-spec gate-decision <feature> <gate> --pause <bool> --reason <txt>`. Sinais completos: CLI.md (`gate-check`).
161
151
 
162
- **Gates por tipo:** `chore`/`hotfix` pulam o gate do plano (carimbado por política no nascimento); o Gate 3 vale para todos. Em `hotfix` o Gate 3 é sempre humano, mesmo em `trust: auto` (`approve … review --mode auto` recusa; use `--approver`), e exige só `4-review/verifier.md`, escrito pelo orquestrador. Os demais exigem `verifier.md` e `reviewer.md` (+ `security.md` com caminho sensível), cobrados pelo `approve` — ou `--self-assessed`, que fica registrado e marcado no `list`.
152
+ **Gates por tipo:** o Gate 3 vale para todos; em `hotfix` é sempre humano (o `approve` recusa `--mode auto`) e exige só `4-review/verifier.md`, escrito pelo orquestrador.
163
153
 
164
- **Aprovar é sempre pela CLI, nunca editando o estado:** em `trust: auto`, `morph-spec approve <feature> <gate> --mode auto`, ou `morph-spec advance <feature>` (aprova e cria a pasta da próxima fase; `--design` toma o ramo uiux). No caminho humano, o hook `state-sync` grava a resposta do `AskUserQuestion` que começa com "aprovado" ou contém "pode avançar", "pode implementar" ou "pode prosseguir"; outra frase não grava, e aí se aprova por `morph-spec approve`. **A pergunta nomeia a feature** ("Gate do plano — Plano de {feature} aprovado?"): é por esse nome que o hook acha o `feature.json` certo, e sem nome numa árvore com mais de uma feature ele recusa gravar.
154
+ **Aprovar é sempre pela CLI, nunca editando o estado:** em `trust: auto`, `morph-spec approve <feature> <gate> --mode auto`, ou `morph-spec advance <feature>` (aprova e cria a pasta da próxima fase; `--design` toma o ramo uiux). No caminho humano, o hook `state-sync` grava a resposta "aprovado, prosseguir" da pergunta que nomeia a feature; outra resposta → `morph-spec approve`.
165
155
 
166
156
  ---
167
157
 
@@ -174,21 +164,21 @@ Trabalho pesado ou de outro domínio vai para um sub-agent com persona e standar
174
164
 
175
165
  Inline vale para fatia pequena no domínio já carregado; inline numa fatia que dispara um gatilho exige uma linha de porquê no relatório. Não despache quando o overhead passa da economia ou a fatia muda arquitetura que você precisa validar antes.
176
166
 
177
- **Mecanismo:** `subagent_type: "morph-{id}"` (ex.: `morph-dotnet-senior`); a persona é o system prompt, gerada de `agents.json` em `.claude/agents/`. `morph-verifier`, `morph-reviewer`, `morph-security` e `morph-scout` são read-only. Tipo não registrado: `general-purpose` com o `persona` colado no prompt, registrado no relatório da fatia.
167
+ **Mecanismo:** `subagent_type: "morph-{id}"` (ex.: `morph-dotnet-senior`); a persona é o system prompt, gerada de `agents.json` em `.claude/agents/`. Tipo não registrado: `general-purpose` com o `persona` colado no prompt, registrado no relatório da fatia.
178
168
 
179
169
  **Scout:** `morph-scout` localiza com `morph-spec graph affected|explain` e cai para grep sem grafo. Obrigatório no início de um hotfix, default no plano de feature grande ou brownfield (`morph-plan` §4). Devolve um mapa (Observado/Inferido) em `2-plan/scout-map.md`; rode `morph-spec scout-check <mapa> --json` antes de usá-lo. Reprovado → re-despache uma vez com as falhas; segunda reprovação → grep você mesmo e escale.
180
170
 
181
171
  **O prompt leva só o que muda por fatia** (`morph-apply` §3): cenários que ela cobre, arquivos, standards, caminho do relatório. As cláusulas fixas de todo dispatch de implementador vivem no corpo gerado da persona (`## Contrato de dispatch`), o único texto que todo dispatch carrega. Não passe histórico da conversa nem saída de outras tarefas.
182
172
 
183
- **O arquivo é o retorno; a última mensagem é cortesia.** O evento de volta às vezes é só um `idle_notification` sem texto, então a mensagem pode não chegar. O sub-agent sempre grava o relatório no caminho canônico — `3-implement/reports/F{n}.md` por fatia; `4-review/{verifier,reviewer,security}.md` no Gate 3 — e o orquestrador lê o arquivo ao retomar o controle.
173
+ **O arquivo é o retorno; a última mensagem é cortesia** (pode não chegar): leia o relatório no caminho canônico.
184
174
 
185
175
  ### Protocolo de auditoria do relatório
186
176
 
187
- Relatório ausente não prova que o agente falhou, e relatório presente não prova que ele está certo (um gravado cedo fica completo e velho). A pergunta é "o trabalho entrou, e é isto que ele diz?", respondida por evidência: `morph-spec report-check <feature> [F1 F2 …] --json` dá o veredito e a ação (`hint`) de cada fatia, e a tabela de ação está em `morph-apply` §5. `report-stale` e `work-landed-report-missing` nunca se resolvem re-despachando: o trabalho já entrou. Caminho degradado fica no relatório (DEGRADADO); segunda ausência sem trabalho é falha real, escale.
177
+ Relatório ausente não prova que o agente falhou, e relatório presente não prova que ele está certo (um gravado cedo fica completo e velho). A pergunta é "o trabalho entrou, e é isto que ele diz?", respondida por evidência: `morph-spec report-check <feature> [F1 F2 …] --json` dá o veredito e a ação (`hint`) de cada fatia, e a tabela de ação está em `morph-apply` §5. `report-stale` e `work-landed-report-missing` nunca se resolvem re-despachando: o trabalho já entrou.
188
178
 
189
- **Teto de turnos:** `maxTurns` por persona no `agents.json` (implementadores 120), anunciado no corpo da persona. O corte é cego: `wip` por marco e, a ~70% do teto, relatório `PARCIAL` — o `report-check` o lê como `report-incomplete` com ação `resume`: retome o mesmo agente, não carimbe DEGRADADO.
179
+ **Teto de turnos:** o corte é cego; relatório `PARCIAL` vira `report-incomplete` com ação `resume`: retome o mesmo agente, não carimbe DEGRADADO.
190
180
 
191
- **Paralelo:** fluxos genuinamente independentes (backend ‖ frontend) vão ao mesmo tempo, um agente por fatia, até 4. `morph-spec plan-check {feature}` imprime as ondas (fatias sem `depende:` comum e sem arquivo em comum). Colisão que escapou do plano → a segunda fatia vai num worktree próprio, em rolling: a cada relatório o orquestrador integra por merge e despacha a próxima pronta (`morph-apply` §4). A feature só segue para a review com `verify` verde e cada cenário com evidência (§0).
181
+ **Paralelo:** fluxos genuinamente independentes (backend ‖ frontend) vão ao mesmo tempo, um agente por fatia, até 4. As ondas saem do `plan-check`. Colisão que escapou do plano → a segunda fatia vai num worktree próprio, em rolling: a cada relatório o orquestrador integra por merge e despacha a próxima pronta (`morph-apply` §4). A feature só segue para a review com `verify` verde e cada cenário com evidência (§0).
192
182
 
193
183
  ---
194
184
 
@@ -49,7 +49,7 @@
49
49
  "model": "sonnet",
50
50
  "maxTurns": 120,
51
51
  "effort": "medium",
52
- "persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern. 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
+ "persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern. 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. COMENTÁRIO: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
53
53
  },
54
54
  "ef-modeler": {
55
55
  "title": "EF Core / Database Modeler",
@@ -90,7 +90,7 @@
90
90
  "model": "sonnet",
91
91
  "maxTurns": 120,
92
92
  "effort": "medium",
93
- "persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. 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."
93
+ "persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. Nunca leak de DbContext fora do handler. Entidade duplicada vira migration divergente: o REUSO do contrato vale dobrado aqui. COMENTÁRIO: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
94
94
  },
95
95
  "maf-expert": {
96
96
  "title": "Microsoft Agent Framework Expert",
@@ -152,7 +152,7 @@
152
152
  "model": "sonnet",
153
153
  "maxTurns": 120,
154
154
  "effort": "medium",
155
- "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."
155
+ "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`. COMENTÁRIO: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
156
156
  },
157
157
  "nextjs-expert": {
158
158
  "title": "Next.js / React Expert",
@@ -204,7 +204,7 @@
204
204
  "model": "sonnet",
205
205
  "maxTurns": 120,
206
206
  "effort": "medium",
207
- "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."
207
+ "persona": "Você escreve Next.js App Router idiomático. Server Components por padrão, Client Components apenas quando há interatividade real. Tailwind + shadcn + Motion. Tipos via Zod. RSC streaming quando faz sentido. Tipo local redeclarado em cada arquivo é o defeito mais comum aqui (REUSO do contrato). COMENTÁRIO: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
208
208
  },
209
209
  "ui-designer": {
210
210
  "title": "UI/UX Designer",
@@ -244,7 +244,6 @@
244
244
  "*"
245
245
  ],
246
246
  "standards": [
247
- "frontend-design-system-naming",
248
247
  "frontend-design-system-art-direction",
249
248
  "frontend-design-system-premium-finish",
250
249
  "frontend-design-system-animations",
@@ -294,7 +293,7 @@
294
293
  "model": "opus",
295
294
  "maxTurns": 120,
296
295
  "effort": "high",
297
- "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."
296
+ "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 → 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. Componente reutilizável se busca antes de se escrever, também em `.morph/context/design-system.md` (REUSO do contrato). COMENTÁRIO: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
298
297
  },
299
298
  "infra-engineer": {
300
299
  "title": "Infrastructure / Jobs Engineer",
@@ -338,7 +337,7 @@
338
337
  "model": "sonnet",
339
338
  "maxTurns": 120,
340
339
  "effort": "medium",
341
- "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."
340
+ "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: só o porquê que o código não mostra, mesmo com vizinho comentado; a pós-condição vai no nome do teste."
342
341
  },
343
342
  "verifier": {
344
343
  "title": "Gate 3 Verifier",
@@ -282,7 +282,7 @@ Fail-open: uma poda que falha nunca derruba o comando. PROVE-or-DECLINE como o r
282
282
 
283
283
  O nó `e2e` é o único que **executa** a aplicação: sobe o `docker compose` do projeto, espera todo serviço ficar saudável e roda as specs Playwright quando existem (sem specs, ainda vale como smoke — "a app sobe" já pega o serviço não registrado que derruba o startup e passa batido no build).
284
284
 
285
- **Onde o nó `build` compila** (chaves de `project` em `.morph/config/config.json`, forma em `config.schema.json`): `project.stack` (tags unidas por `+`, ex. `dotnet+nextjs`) escolhe os passos; `project.backendPath` é a pasta da solução .NET e `project.frontendPath` a do frontend (`tsc`), ambas relativas à raiz. Sem `backendPath` e sem `.sln`/`.slnx`/`.csproj` na raiz, o build compila a **única** `.sln`/`.slnx` do repositório (senão o único `.csproj`), fora de `worktrees/`, `node_modules/`, `bin/` e `obj/` (`discoverDotnetSolution`, `test-runner.js`); nenhuma ou várias → o build falha e o `feedback[]` lista as candidatas e manda declarar `project.backendPath`.
285
+ **Onde o nó `build` compila** (chaves de `project` em `.morph/config/config.json`, forma em `config.schema.json`): `project.stack` (tags unidas por `+`, ex. `dotnet+nextjs`) escolhe os passos; `project.backendPath` é a pasta da solução .NET e `project.frontendPath` a do frontend (`tsc --noEmit` e, com `eslint.config.*` e o pacote `eslint` instalados, `eslint --quiet` só nos arquivos que a feature mudou contra a base), ambas relativas à raiz. Sem `backendPath` e sem `.sln`/`.slnx`/`.csproj` na raiz, o build compila a **única** `.sln`/`.slnx` do repositório (senão o único `.csproj`), fora de `worktrees/`, `node_modules/`, `bin/` e `obj/` (`discoverDotnetSolution`, `test-runner.js`); nenhuma ou várias → o build falha e o `feedback[]` lista as candidatas e manda declarar `project.backendPath`.
286
286
 
287
287
  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.
288
288
 
@@ -404,7 +404,7 @@ Env: `MORPH_HEAVY=off` desliga o semáforo (a suíte do próprio framework roda
404
404
 
405
405
  **O que a vaga não cobre:** ela não tem processo vivo para vigiar (o hook sai na hora), então vence por **TTL** — o `timeout` da chamada, no máximo o da plataforma, mais 30 s — e uma chamada recusada por regra de permissão ou interrompida pelo usuário, que não dispara `Post*`, segura o slot até lá. O TTL conta desde o `PreToolUse`, que roda **antes** do prompt de permissão: se a aprovação demora mais que o `timeout` + 30 s, a vaga vence com o comando ainda por rodar e outra chamada pode tomá-la (sobreassinatura, do lado seguro). Chamadas de Bash de um mesmo lote paralelo rodam **em série** no Claude Code (medido na 2.1.285: o `Pre` da 2ª só rodou depois do `Post` da 1ª), então um agente não fica esperando uma vaga que só a própria chamada anterior liberaria.
406
406
 
407
- `run_in_background`, PowerShell e comando fora da lista não são cobertos, e a cláusula 6 dos templates de dispatch (`heavy run`) segue valendo para eles.
407
+ `run_in_background`, PowerShell e comando fora da lista não são cobertos, e a cláusula PROCESSOS do contrato de dispatch (`heavy run`) segue valendo para eles.
408
408
 
409
409
  **Medido** com `claude -p` real e a chain instalada: fila cheia recusa após a espera e não mexe na vaga alheia; a vaga está presa durante a chamada e volta vazia depois, com exit 0 e com exit 1. Custo num Bash **leve**: ~1,5 ms na chain Pre (mediana 61,4 → 62,9 ms, medido intercalado) e ~0 na Post; os módulos do semáforo só carregam quando o comando é pesado.
410
410
 
@@ -500,7 +500,7 @@ Sem fatia na linha de comando, a saída traz `reviews: { verifier, reviewer, sec
500
500
 
501
501
  ### stack-scan
502
502
 
503
- **`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`), Semantic Kernel (pacote `Microsoft.SemanticKernel*` ou `[KernelFunction]` no código) e serviço de agente hospedado (`Azure.AI.Projects`, `Azure.AI.Agents.Persistent`) são **Alto** — os dois últimos são linhas da tabela de anti-padrões do `ai-agents-setup` §7, para a qual a migração aponta; projeto .NET sem `Microsoft.CodeAnalysis.BannedApiAnalyzers` (migração: `backend-dotnet-analyzers`) e SDK de IA (`Microsoft.Agents.AI*`, `Microsoft.Extensions.AI*`, `OpenAI`, `Google.GenAI`, `ModelContextProtocol`) com versão flutuante (`*` ou faixa) são **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.
503
+ **`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()` (só sem os analyzers: com eles o build já barra), 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`), Semantic Kernel (pacote `Microsoft.SemanticKernel*` ou `[KernelFunction]` no código) e serviço de agente hospedado (`Azure.AI.Projects`, `Azure.AI.Agents.Persistent`) são **Alto** — os dois últimos são linhas da tabela de anti-padrões do `ai-agents-setup` §7, para a qual a migração aponta; projeto .NET sem `Microsoft.CodeAnalysis.BannedApiAnalyzers` (migração: `backend-dotnet-analyzers`) e SDK de IA (`Microsoft.Agents.AI*`, `Microsoft.Extensions.AI*`, `OpenAI`, `Google.GenAI`, `ModelContextProtocol`) com versão flutuante (`*` ou faixa) são **Médio**; pacote `Microsoft.EntityFrameworkCore|AspNetCore|Extensions` abaixo da major 10 é **Baixo**. `nextjs`: frontend sem Next.js é Bloqueante; Pages Router sem `app/`, biblioteca de UI diferente (MUI, antd, Chakra, Bootstrap, Mantine) e app sem `eslint.config.{mjs,js,cjs}` com `no-restricted-imports` na raiz do app (migração: `frontend-nextjs-lint`) são Médio; `motion` e `framer-motion` instalados juntos são Baixo. 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.
504
504
 
505
505
  ### tests-audit
506
506
 
@@ -33,7 +33,7 @@ typechecks or runs tests: that is `morph-spec verify`.
33
33
  | **WorktreeRemove** | worktree-remove.js | Fail-open | Routes Claude Code's own cleanup through `morph-spec worktree remove --target --if-clean` (junctions dropped first; a dirty tree is left alone). Always exit 0. Secondary net — the event was observed NOT to fire headless; the Bash guard above stays the primary defense |
34
34
  | **PostToolUse** (Write\|Edit) | comment-guard.js | Blocking | Blocks when the edit ADDED a comment the `architecture-code-comments` standard never allows: process history (ADR, scenario/slice id, gate, plan.md), banner/`#region`, a block over 3 lines, or a doc-comment outside a published package. Old comments are never reported; last link of the chain |
35
35
  | **PostToolUse** (Write\|Edit\|AskUserQuestion) | state-sync.js | State sync | Autonomous state updates (feature register, output tracking, gate approval) |
36
- | **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions; logs one `coherent`/`divergent` line per check in `.morph/logs/activity.jsonl` (issue #63) |
36
+ | **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions |
37
37
  | **PostToolUse** (all tools) | telemetry-log.js | Telemetry | Appends every tool call (skill, sub-agent dispatch + prompt, knowledge-base read, bash/edit) to `.morph/logs/events.jsonl`; silent, fail-open — the NLH execution-path capture read back by `morph-spec telemetry` |
38
38
  | **PostToolUse** (all tools) | loop-detect.js | Advisory | Warns when the last 8 actions collapse to ≤2 unique signatures |
39
39
  | **PostToolUse** (all tools) | graph-refresh-kick.js | Background | After a Bash or PowerShell command that runs git/gh/morph-spec, launches the DETACHED refresh of the ROOT's codebase graph (`shared/graph-refresh.js`: cwd in the git common dir, never a worktree; one lock per repository; staleness by `shared/graph-freshness.js` — stamp plus the per-tree run record in `<git-common-dir>/morph-spec/graph-refresh-state.json`); never waits, never injects |
@@ -86,7 +86,7 @@ All Claude Code hooks import from `framework/hooks/shared/`:
86
86
 
87
87
  | Module | Purpose |
88
88
  |--------|---------|
89
- | `state-reader.js` | `loadState()`, `getActiveFeature()`, `getFeaturePhase()`, `isGateApproved()`, `getPendingGates()`, `getMissingOutputs()` |
89
+ | `state-reader.js` | `loadState()`, `getActiveFeature()`, `getFeaturePhase()`, `isGateApproved()`, `getPendingGates()` |
90
90
  | `phase-utils.js` | Phase constants, path extraction, file classification |
91
91
  | `hook-response.js` | `block(reason)`, `approve(context)`, `injectContext(text)`, `pass()` |
92
92
  | `stdin-reader.js` | `readStdin()` — Promise-based stdin JSON reader |
@@ -15,6 +15,7 @@ const MAX_LISTED = 10;
15
15
  const RULE_LABEL = {
16
16
  process: 'história de processo (ADR, cenário, fatia, gate, plan.md)',
17
17
  banner: 'banner ou #region',
18
+ 'commented-code': 'código comentado (o git guarda o antigo: apague)',
18
19
  'doc-comment': '///·JSDoc em código que não é pacote publicado',
19
20
  'long-block': `bloco com mais de ${MAX_BLOCK_LINES} linhas`,
20
21
  };
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0",
2
+ "version": "1.3.0",
3
3
  "patterns": [
4
4
  "DROP TABLE",
5
5
  "DROP DATABASE",
@@ -39,6 +39,68 @@
39
39
  "~/*"
40
40
  ],
41
41
  "reason": "apaga a raiz ou o home inteiro; remova o caminho específico"
42
+ },
43
+ {
44
+ "executable": "pkill",
45
+ "args": [
46
+ "node",
47
+ "node.exe"
48
+ ],
49
+ "reason": "mata por nome e derruba os MCPs de todas as sessões; encerre pelo PID que você subiu"
50
+ },
51
+ {
52
+ "executable": "killall",
53
+ "args": [
54
+ "node",
55
+ "node.exe"
56
+ ],
57
+ "reason": "mata por nome e derruba os MCPs de todas as sessões; encerre pelo PID que você subiu"
58
+ },
59
+ {
60
+ "executable": "stop-process",
61
+ "args": [
62
+ "-name"
63
+ ],
64
+ "reason": "mata por nome e derruba os MCPs de todas as sessões; encerre pelo PID que você subiu (`-Id <n>`)"
65
+ },
66
+ {
67
+ "executable": "git",
68
+ "args": [
69
+ "add"
70
+ ],
71
+ "flags": [
72
+ [
73
+ "-A",
74
+ "--all"
75
+ ]
76
+ ],
77
+ "subagentOnly": true,
78
+ "display": "git add -A",
79
+ "reason": "leva junto o arquivo de outra fatia na mesma árvore; `git add <paths>` só dos seus"
80
+ },
81
+ {
82
+ "executable": "git",
83
+ "args": [
84
+ "stash"
85
+ ],
86
+ "subagentOnly": true,
87
+ "display": "git stash",
88
+ "reason": "mexe na árvore que outras fatias e o verificador estão usando"
89
+ },
90
+ {
91
+ "executable": "git",
92
+ "args": [
93
+ "checkout",
94
+ "restore"
95
+ ],
96
+ "flags": [
97
+ [
98
+ "--"
99
+ ]
100
+ ],
101
+ "subagentOnly": true,
102
+ "display": "git checkout --",
103
+ "reason": "reverte código de produção; a prova de que o teste morde é do verificador"
42
104
  }
43
105
  ]
44
106
  }
@@ -18,7 +18,6 @@ const PHASE_DIR_ENTRY_GATE = {
18
18
 
19
19
  const ALWAYS_WRITABLE_DIR = '2-plan';
20
20
  import { blockResult, runStandalone } from '../../shared/hook-response.js';
21
- import { logHookActivity } from '../../shared/activity-logger.js';
22
21
  import { getFilePath, resolveMorphRoot, resolveTargetPath } from '../../shared/payload-utils.js';
23
22
 
24
23
  export async function run(payload) {
@@ -57,7 +56,6 @@ export async function run(payload) {
57
56
  if (targetDir !== allowedDir) {
58
57
  const absolutePath = resolveTargetPath(filePath, root);
59
58
  if (existsSync(absolutePath)) {
60
- logHookActivity('enforce-phase-writes', 'PreToolUse', 'ok');
61
59
  return null;
62
60
  }
63
61
 
@@ -69,11 +67,8 @@ export async function run(payload) {
69
67
  && existsSync(resolveTargetPath(`.morph/features/${featureName}/1-design`, root));
70
68
 
71
69
  if (!tookDesignBranch || isGateApproved(featureName, 'uiux', root)) {
72
- logHookActivity('enforce-phase-writes', 'PreToolUse', 'ok');
73
70
  return null;
74
71
  }
75
-
76
- logHookActivity('enforce-phase-writes', 'PreToolUse', 'blocked');
77
72
  return blockResult(
78
73
  `MORPH-SPEC: Writing to '${targetDir}/' is not allowed yet.\n` +
79
74
  `This feature entered the uiux branch (1-design/ exists) and the 'uiux' gate is still pending.\n\n` +
@@ -87,7 +82,6 @@ export async function run(payload) {
87
82
  const treeNote = root !== process.cwd().replace(/\\/g, '/')
88
83
  ? `\n(phase read from the tree that owns the file: ${root})`
89
84
  : '';
90
- logHookActivity('enforce-phase-writes', 'PreToolUse', 'blocked');
91
85
  return blockResult(
92
86
  `MORPH-SPEC: Writing to '${targetDir}/' is not allowed during ${phaseLabel} phase.\n` +
93
87
  `Current phase '${phase}' only permits writes to '${allowedDir}/'.${treeNote}\n\n` +
@@ -96,8 +90,6 @@ export async function run(payload) {
96
90
  `(run it in that tree — the gate is recorded in its own feature.json).`
97
91
  );
98
92
  }
99
-
100
- logHookActivity('enforce-phase-writes', 'PreToolUse', 'ok');
101
93
  return null;
102
94
  } catch {
103
95
  return null;
@@ -4,7 +4,6 @@
4
4
  import { loadState, stateExists, getActiveFeature } from '../../shared/state-reader.js';
5
5
  import { contextResult, runStandalone } from '../../shared/hook-response.js';
6
6
  import { gateFromQuestion } from '../../shared/gate-question.js';
7
- import { logHookActivity } from '../../shared/activity-logger.js';
8
7
  import { resolveMorphRoot, resolveTargetPath } from '../../shared/payload-utils.js';
9
8
  import { locateFeatureForQuestion } from '../../shared/feature-locator.js';
10
9
 
@@ -61,7 +60,6 @@ function checkAskUserQuestion(input, sessionRoot) {
61
60
  const decision = getDecision(state, feature, gate);
62
61
  if (!decision) continue;
63
62
  if (decision.pause === false) {
64
- logHookActivity('gate-guard', 'PostToolUse', 'divergent', cwd);
65
63
  return contextResult(
66
64
  `⚠️ [morph-spec] GATE OVER-PAUSE — você apresentou o Gate "${gate}" via AskUserQuestion, ` +
67
65
  `mas a decisão registrada em state.json (gateDecisions.${gate}) era pause:false ` +
@@ -72,7 +70,6 @@ function checkAskUserQuestion(input, sessionRoot) {
72
70
  'PostToolUse'
73
71
  );
74
72
  }
75
- logHookActivity('gate-guard', 'PostToolUse', 'coherent', cwd);
76
73
  return null;
77
74
  }
78
75
  return null;
@@ -94,8 +91,6 @@ function checkStateEdit(input, sessionRoot) {
94
91
  if (!state) return null;
95
92
  const feature = featureFromStatePath(filePath) || getActiveFeature(cwd)?.name;
96
93
  if (!feature) return null;
97
-
98
- let checked = false;
99
94
  const gates = state.features?.[feature]?.approvalGates ?? {};
100
95
  for (const gate of ['plan', 'uiux', 'review']) {
101
96
  const g = gates[gate];
@@ -104,7 +99,6 @@ function checkStateEdit(input, sessionRoot) {
104
99
  const decision = getDecision(state, feature, gate);
105
100
  if (!decision) continue;
106
101
  if (decision.pause === true) {
107
- logHookActivity('gate-guard', 'PostToolUse', 'divergent', cwd);
108
102
  return contextResult(
109
103
  `⚠️ [morph-spec] GATE SKIPPED PAUSE — o Gate "${gate}" foi marcado approvedBy:"auto", ` +
110
104
  `mas a decisão registrada (gateDecisions.${gate}) era pause:true ` +
@@ -114,9 +108,7 @@ function checkStateEdit(input, sessionRoot) {
114
108
  'PostToolUse'
115
109
  );
116
110
  }
117
- checked = true;
118
111
  }
119
- if (checked) logHookActivity('gate-guard', 'PostToolUse', 'coherent', cwd);
120
112
  return null;
121
113
  }
122
114
 
@@ -5,7 +5,6 @@ import { join } from 'path';
5
5
  import { stateExists } from '../../shared/state-reader.js';
6
6
  import { contextResult, runStandalone } from '../../shared/hook-response.js';
7
7
  import { getCommandSignature } from '../../shared/payload-utils.js';
8
- import { logEvent } from '../../shared/activity-logger.js';
9
8
 
10
9
  const MAX_BUFFER = 15;
11
10
  const LOOP_WINDOW = 8;
@@ -60,8 +59,6 @@ export async function run(payload) {
60
59
 
61
60
  writeFileSync(cooldownPath, JSON.stringify({ lastDetected: new Date().toISOString() }), 'utf-8');
62
61
 
63
- logEvent('loop_detected', { signatures: uniqueSigs.size }, process.cwd());
64
-
65
62
  const sigList = [...uniqueSigs].join(', ');
66
63
  return contextResult(
67
64
  `⚠️ [morph-spec] LOOP DETECTED — the last ${LOOP_WINDOW} actions repeat only ${uniqueSigs.size} pattern(s): ${sigList}\n\n` +
@@ -54,8 +54,9 @@ export async function run(payload) {
54
54
  }
55
55
 
56
56
  for (const rule of denylist.executableArgs || []) {
57
+ if (rule.subagentOnly && !payload.agent_id) continue;
57
58
  if (matchesExecutableRule(command, rule)) {
58
- const shown = [rule.executable, ...(rule.flags || []).map((group) => group[0]), rule.args[0]].join(' ');
59
+ const shown = rule.display ?? [rule.executable, ...(rule.flags || []).map((group) => group[0]), rule.args[0]].join(' ');
59
60
  return blockResult(`Comando bloqueado pela denylist: \`${shown}\` ${rule.reason}.`);
60
61
  }
61
62
  }