@polymorphism-tech/morph-spec 9.3.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.
- package/CLAUDE.md +1 -1
- package/README.md +2 -2
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/MORPH.md +23 -33
- package/framework/agents.json +10 -9
- package/framework/docs/CLI.md +3 -3
- package/framework/evals/review-checklist.md +1 -1
- package/framework/hooks/README.md +3 -2
- package/framework/hooks/claude-code/__pycache__/statusline.cpython-313.pyc +0 -0
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -15
- package/framework/hooks/claude-code/core/comment-guard.js +1 -0
- package/framework/hooks/claude-code/core/conventional-commit.js +57 -0
- package/framework/hooks/claude-code/core/denylist.json +63 -1
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +0 -8
- package/framework/hooks/claude-code/core/gate-guard.js +0 -8
- package/framework/hooks/claude-code/core/loop-detect.js +0 -3
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +2 -1
- package/framework/hooks/claude-code/core/protect-spec-files.js +0 -7
- package/framework/hooks/claude-code/core/validate-completion.js +0 -4
- package/framework/hooks/claude-code/core/worktree-context.js +0 -2
- package/framework/hooks/claude-code/statusline.py +3 -44
- package/framework/hooks/shared/build-check.js +12 -19
- package/framework/hooks/shared/command-tokens.js +17 -0
- package/framework/hooks/shared/comment-rules.js +11 -1
- package/framework/hooks/shared/hook-response.js +0 -4
- package/framework/hooks/shared/payload-utils.js +1 -35
- package/framework/hooks/shared/phase-utils.js +0 -12
- package/framework/hooks/shared/slice-progress.js +0 -2
- package/framework/hooks/shared/state-reader.js +0 -19
- package/framework/hooks/shared/telemetry-logger.js +1 -1
- package/framework/rules/csharp-standards.md +5 -3
- package/framework/rules/dotnet-templates.md +2 -2
- package/framework/rules/frontend-standards.md +1 -2
- package/framework/rules/nextjs-standards.md +10 -8
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +3 -3
- package/framework/skills/core/morph-review/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +87 -37
- package/framework/standards/STANDARDS.meta.json +41 -15
- package/framework/standards/architecture/code-comments.md +1 -1
- package/framework/standards/architecture/engineering-lens.md +100 -0
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +7 -9
- package/framework/standards/backend/dotnet/analyzers.md +59 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -12
- package/framework/standards/frontend/admin-app/data-table.md +1 -3
- package/framework/standards/frontend/admin-app/page-structure.md +8 -4
- package/framework/standards/frontend/nextjs/app-router.md +3 -4
- package/framework/standards/frontend/nextjs/components.md +2 -15
- package/framework/standards/frontend/nextjs/data-fetching.md +2 -3
- package/framework/standards/frontend/nextjs/lint.md +141 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +2 -7
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -2
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +5 -7
- package/framework/standards/frontend/nextjs/project-structure.md +3 -2
- package/framework/standards/frontend/nextjs/state-management.md +6 -7
- package/framework/templates/README.md +4 -5
- package/framework/templates/REGISTRY.json +21 -516
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +54 -56
- package/package.json +3 -2
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/archive.js +1 -1
- package/src/commands/state/finish.js +1 -1
- package/src/core/paths/output-schema.js +0 -9
- package/src/core/state/feature-identity.js +0 -2
- package/src/core/state/state-manager.js +1 -169
- package/src/core/templates/template-registry.js +1 -43
- package/src/core/templates/template-renderer.js +0 -23
- package/src/dashboard/app/lib/format.js +0 -7
- package/src/dashboard/app/lib/phases.js +0 -7
- package/src/dashboard/scanner/activity-scanner.js +0 -4
- package/src/lib/ai/ai-project-scan.js +1 -6
- package/src/lib/graph/graphify-runtime.js +0 -1
- package/src/lib/mutation/snapshot.js +1 -5
- package/src/lib/risk-detector.js +0 -5
- package/src/lib/stack/stack-profile.js +0 -4
- package/src/lib/stack/stack-scan.js +18 -9
- package/src/lib/tasks/eval-node.js +1 -7
- package/src/lib/validators/validation-runner.js +0 -43
- package/src/lib/validators/verify-runner.js +18 -2
- package/src/utils/agents-installer.js +12 -19
- package/src/utils/claude-settings-manager.js +0 -28
- package/src/utils/file-copier.js +0 -53
- package/src/utils/hooks-installer.js +1 -1
- package/framework/hooks/shared/activity-logger.js +0 -131
- package/framework/standards/frontend/design-system/naming.md +0 -68
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +0 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +0 -100
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +0 -90
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +0 -84
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +0 -44
- package/framework/templates/meta-prompts/validators/checkpoint-validator.md +0 -107
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +0 -94
- package/src/commands/validation/index.js +0 -2
- package/src/lib/validators/code-budget/index.js +0 -2
- package/src/lib/validators/contracts/index.js +0 -1
- package/src/lib/validators/nextjs/index.js +0 -1
- package/src/lib/validators/packages/index.js +0 -1
- 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, ~
|
|
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.
|
|
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.
|
|
383
|
+
*morph-spec v9.5.0 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED
package/framework/MORPH.md
CHANGED
|
@@ -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:**
|
|
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`
|
|
20
|
-
| `verify` exit 3
|
|
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 (
|
|
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
|
-
**
|
|
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.**
|
|
103
|
-
- `feature.
|
|
104
|
-
- **Só o morph
|
|
105
|
-
- **
|
|
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`.
|
|
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
|
|
120
|
-
- `checks.json` —
|
|
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.
|
|
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);
|
|
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) —
|
|
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>`.
|
|
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:**
|
|
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
|
|
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/`.
|
|
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
|
|
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.
|
|
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:**
|
|
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.
|
|
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
|
|
package/framework/agents.json
CHANGED
|
@@ -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
|
|
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.
|
|
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`.
|
|
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 +
|
|
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 →
|
|
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
|
|
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",
|
|
@@ -390,14 +389,16 @@
|
|
|
390
389
|
"stack": [
|
|
391
390
|
"*"
|
|
392
391
|
],
|
|
393
|
-
"standards": [
|
|
392
|
+
"standards": [
|
|
393
|
+
"architecture-engineering-lens"
|
|
394
|
+
],
|
|
394
395
|
"tools": "Read, Glob, Grep, Bash",
|
|
395
396
|
"mcp": [
|
|
396
397
|
"graphify"
|
|
397
398
|
],
|
|
398
399
|
"model": "sonnet",
|
|
399
400
|
"maxTurns": 60,
|
|
400
|
-
"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. 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`)."
|
|
401
|
+
"persona": "Você é o revisor independente do Gate 3, read-only. Leia como o arquiteto que herda este repo amanhã: simples, legível, fácil de estender. 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 — e o que o DIFF introduziu da lista fechada `architecture-engineering-lens#review-blockers`; o que já existia no arquivo é nota); 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. 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`)."
|
|
401
402
|
},
|
|
402
403
|
"security": {
|
|
403
404
|
"title": "Gate 3 Security Reviewer",
|
package/framework/docs/CLI.md
CHANGED
|
@@ -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
|
|
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()
|
|
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
|
|
|
@@ -34,7 +34,7 @@ diferente") é nota; nota não bloqueia e não pede rodada nova. Na dúvida, sem
|
|
|
34
34
|
| `teste` | verificador | teste vermelho; teste que não morde a regra que diz proteger | saída do verify, mutante sobrevivente, assert que passa pelo motivo errado |
|
|
35
35
|
| `regressao` | revisor | comportamento que funcionava e quebrou | consumidor afetado + o caminho que quebra |
|
|
36
36
|
| `escopo` | revisor | o diff faz o que nenhuma fatia pediu, ou deixa de fazer o que o contrato pede | arquivo/trecho fora das fatias, ou o item do contrato ausente |
|
|
37
|
-
| `arquitetura` | revisor | padrão ou abstração nova contra o stack e os standards | o trecho e o standard que ele contraria |
|
|
37
|
+
| `arquitetura` | revisor | padrão ou abstração nova contra o stack e os standards; item da lista fechada de `architecture-engineering-lens#review-blockers` que o DIFF introduziu (o que já existia é nota) | o trecho e o standard que ele contraria |
|
|
38
38
|
| `performance` | revisor | custo que o volume real torna visível | a consulta/lista e o volume que a derruba |
|
|
39
39
|
| `seguranca` | segurança | authz, segredo, injeção, PII, custo sem teto, migration destrutiva | o caminho de exploração |
|
|
40
40
|
|
|
@@ -25,6 +25,7 @@ typechecks or runs tests: that is `morph-spec verify`.
|
|
|
25
25
|
| **PreToolUse** (Write\|Edit) | protect-spec-files.js | Block | Blocks edits to `plan.md` (and the uiux artifacts) after their gate is approved; an append-only `## Errata` block at the end of `plan.md` passes |
|
|
26
26
|
| **PreToolUse** (Write\|Edit) | enforce-phase-writes.js | Block | Ensures writes go to current phase directory |
|
|
27
27
|
| **PreToolUse** (Bash) | pre-bash-denylist.js | Block | Blocks `rm -rf .morph/` and direct state edits |
|
|
28
|
+
| **PreToolUse** (Bash) | conventional-commit.js | Block | Blocks a `git commit -m` whose subject is not Conventional Commits (`<type>(<scope>): <subject>`); a `$(…)`/heredoc message, `-F` and git-generated `Merge`/`Revert` pass |
|
|
28
29
|
| **PreToolUse** (Bash) | block-destructive-worktree-removal.js | Block | Blocks `git worktree remove` / `rm -rf` / `Remove-Item -Recurse` on a directory holding reparse points (the infra junctions a raw removal would FOLLOW out, emptying the root's `.claude/`); names `morph-spec worktree remove` |
|
|
29
30
|
| **PreToolUse** (Bash) | block-raw-worktree-add.js | Block | Blocks raw `git worktree add` (a tree with no junctions → no hooks, no port block, no identity, outside `worktrees/`); names `morph-spec worktree setup <feature>` / `worktree provision <name> --base <ref>`. `list`/`prune`/`remove`/`lock`/`move`/`repair` pass |
|
|
30
31
|
| **PreToolUse** (Bash) | warn-bash-source-write.js | Warn | Heredoc/redirect/`tee` into project source (`isProjectSource`): context naming the file, the command still runs — Write/Edit is what phase, protected-file and comment guards see |
|
|
@@ -32,7 +33,7 @@ typechecks or runs tests: that is `morph-spec verify`.
|
|
|
32
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 |
|
|
33
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 |
|
|
34
35
|
| **PostToolUse** (Write\|Edit\|AskUserQuestion) | state-sync.js | State sync | Autonomous state updates (feature register, output tracking, gate approval) |
|
|
35
|
-
| **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions
|
|
36
|
+
| **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions |
|
|
36
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` |
|
|
37
38
|
| **PostToolUse** (all tools) | loop-detect.js | Advisory | Warns when the last 8 actions collapse to ≤2 unique signatures |
|
|
38
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 |
|
|
@@ -85,7 +86,7 @@ All Claude Code hooks import from `framework/hooks/shared/`:
|
|
|
85
86
|
|
|
86
87
|
| Module | Purpose |
|
|
87
88
|
|--------|---------|
|
|
88
|
-
| `state-reader.js` | `loadState()`, `getActiveFeature()`, `getFeaturePhase()`, `isGateApproved()`, `getPendingGates()
|
|
89
|
+
| `state-reader.js` | `loadState()`, `getActiveFeature()`, `getFeaturePhase()`, `isGateApproved()`, `getPendingGates()` |
|
|
89
90
|
| `phase-utils.js` | Phase constants, path extraction, file classification |
|
|
90
91
|
| `hook-response.js` | `block(reason)`, `approve(context)`, `injectContext(text)`, `pass()` |
|
|
91
92
|
| `stdin-reader.js` | `readStdin()` — Promise-based stdin JSON reader |
|
|
Binary file
|
|
@@ -3,23 +3,10 @@ import { blockResult, runStandalone } from '../../shared/hook-response.js';
|
|
|
3
3
|
import { stateExists } from '../../shared/state-reader.js';
|
|
4
4
|
// From shared/, never from the sibling hook: importing a hook module fires its
|
|
5
5
|
// standalone tail (stdin read + exit) inside THIS process.
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
const GIT_GLOBAL_WITH_VALUE = new Set(['-c', '-C', '--git-dir', '--work-tree', '--namespace', '--exec-path', '--config-env']);
|
|
6
|
+
import { gitInvocations } from '../../shared/command-tokens.js';
|
|
9
7
|
|
|
10
8
|
export function isRawWorktreeAdd(command) {
|
|
11
|
-
|
|
12
|
-
const lower = tokens.map((t) => t.toLowerCase());
|
|
13
|
-
for (let i = 0; i < lower.length; i++) {
|
|
14
|
-
if (lower[i] !== 'git' && !/(^|[\\/])git(\.exe)?$/.test(lower[i])) continue;
|
|
15
|
-
let j = i + 1;
|
|
16
|
-
while (j < lower.length && lower[j].startsWith('-')) {
|
|
17
|
-
if (GIT_GLOBAL_WITH_VALUE.has(tokens[j])) j += 2;
|
|
18
|
-
else j += 1;
|
|
19
|
-
}
|
|
20
|
-
if (lower[j] === 'worktree' && lower[j + 1] === 'add') return true;
|
|
21
|
-
}
|
|
22
|
-
return false;
|
|
9
|
+
return gitInvocations(command).some(({ sub, args }) => sub === 'worktree' && args[0]?.toLowerCase() === 'add');
|
|
23
10
|
}
|
|
24
11
|
|
|
25
12
|
export async function run(payload) {
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { blockResult, runStandalone } from '../../shared/hook-response.js';
|
|
3
|
+
import { stateExists } from '../../shared/state-reader.js';
|
|
4
|
+
import { gitInvocations } from '../../shared/command-tokens.js';
|
|
5
|
+
|
|
6
|
+
const CONVENTIONAL = /^(feat|fix|refactor|test|chore|docs|perf|ci|build|style|revert)(\([^)]+\))?!?: \S/;
|
|
7
|
+
const GIT_GENERATED = /^(Merge|Revert) /;
|
|
8
|
+
const MESSAGE_FLAG = /^-[a-zA-Z]*m$/;
|
|
9
|
+
|
|
10
|
+
function subjectOf(args) {
|
|
11
|
+
for (let i = 0; i < args.length; i++) {
|
|
12
|
+
const arg = args[i];
|
|
13
|
+
if (arg === '--message' || MESSAGE_FLAG.test(arg)) return args[i + 1] ?? null;
|
|
14
|
+
if (arg.startsWith('--message=')) return arg.slice('--message='.length);
|
|
15
|
+
if (/^-m./.test(arg)) return arg.slice(2);
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// `$(…)` and heredocs are only known after the shell expands them.
|
|
21
|
+
function isJudgeable(subject) {
|
|
22
|
+
return subject !== null && !subject.includes('$(') && !subject.includes('<<') && !subject.includes('`');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function nonConventionalSubject(command) {
|
|
26
|
+
for (const { sub, args } of gitInvocations(command)) {
|
|
27
|
+
if (sub !== 'commit') continue;
|
|
28
|
+
const subject = subjectOf(args);
|
|
29
|
+
if (!isJudgeable(subject)) continue;
|
|
30
|
+
const firstLine = subject.split('\n')[0].trim();
|
|
31
|
+
if (!CONVENTIONAL.test(firstLine) && !GIT_GENERATED.test(firstLine)) return firstLine;
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function run(payload) {
|
|
37
|
+
try {
|
|
38
|
+
if (!stateExists()) return null;
|
|
39
|
+
|
|
40
|
+
const command = payload?.tool_input?.command || '';
|
|
41
|
+
if (!command.includes('commit')) return null;
|
|
42
|
+
const subject = nonConventionalSubject(command);
|
|
43
|
+
if (subject === null) return null;
|
|
44
|
+
|
|
45
|
+
return blockResult(
|
|
46
|
+
`Bloqueado: o assunto do commit não segue Conventional Commits: "${subject}".\n\n` +
|
|
47
|
+
'Formato: `<tipo>(<escopo>): <assunto>` — tipo em feat, fix, refactor, test, chore ' +
|
|
48
|
+
'(também docs, perf, ci, build, style, revert); `!` antes de `:` marca breaking change.\n' +
|
|
49
|
+
'Exemplos: `feat(orders): aceita cupom no checkout`, `fix: trata pedido sem itens`, ' +
|
|
50
|
+
'`chore(F2): wip handler de pagamento`.'
|
|
51
|
+
);
|
|
52
|
+
} catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await runStandalone(run);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.
|
|
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
|
}
|