@polymorphism-tech/morph-spec 8.29.0 → 8.32.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 +11 -1
- package/README.md +13 -2
- package/bin/morph-spec.js +67 -1
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +4 -1
- package/framework/MORPH.md +4 -3
- package/framework/agents.json +6 -6
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +68 -45
- package/framework/evals/rubrics/code-quality.md +17 -0
- package/framework/evals/rubrics/condensed.md +3 -1
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/phase-utils.js +2 -0
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/mcp/registry.json +39 -4
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/schemas/tasks.schema.json +25 -0
- 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 +14 -4
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +12 -2
- package/framework/skills/core/morph-plan/SKILL.md +45 -3
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +12 -4
- package/framework/skills/core/morph-stack-scan/SKILL.md +13 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/ai-agents/agent-session.md +160 -160
- package/framework/standards/ai-agents/batching.md +193 -193
- package/framework/standards/ai-agents/context-providers.md +188 -188
- package/framework/standards/ai-agents/mcp-server.md +250 -250
- package/framework/standards/ai-agents/mcp-tools.md +197 -197
- package/framework/standards/ai-agents/middleware-patterns.md +207 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
- package/framework/standards/ai-agents/modalities-vision.md +201 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
- package/framework/standards/ai-agents/production.md +231 -231
- package/framework/standards/ai-agents/prompts-in-db.md +214 -214
- package/framework/standards/ai-agents/providers/model-registry.md +158 -158
- package/framework/standards/ai-agents/service-tier-flex.md +135 -135
- package/framework/standards/ai-agents/structured-output.md +249 -249
- package/framework/standards/ai-agents/sweet-spot.md +109 -109
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/context/ARCHITECTURE.md +20 -0
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +1 -1
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/graph/affected.js +89 -0
- package/src/commands/graph/build.js +94 -0
- package/src/commands/graph/explain.js +96 -0
- package/src/commands/graph/status.js +93 -0
- package/src/commands/index.js +13 -13
- package/src/commands/project/branch.js +170 -0
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +148 -9
- package/src/commands/project/fleet.js +10 -3
- package/src/commands/project/worktree.js +73 -50
- package/src/commands/state/finish.js +41 -26
- package/src/commands/state/list.js +79 -6
- package/src/core/paths/output-schema.js +7 -0
- package/src/core/state/lifecycle-audit.js +127 -0
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/HookInspector.jsx +418 -418
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/generators/architecture-scanner.js +45 -4
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/git/branch-classifier.js +177 -0
- package/src/lib/git/gh.js +30 -0
- package/src/lib/git/pending-prs.js +89 -0
- package/src/lib/git/pr-index.js +70 -0
- package/src/lib/graph/god-nodes.js +75 -0
- package/src/lib/graph/graph-health.js +61 -0
- package/src/lib/graph/graph-loader.js +86 -0
- package/src/lib/graph/graphify-runtime.js +100 -0
- package/src/lib/graph/graphifyignore.js +86 -0
- package/src/lib/graph/sanitize.js +280 -0
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/tasks-json-parser.js +26 -0
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/reuse/index.js +10 -0
- package/src/lib/validators/reuse/reuse-validator.js +324 -0
- package/src/lib/validators/reuse/symbol-scanner.js +144 -0
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/validation-runner.js +40 -1
- package/src/lib/worktree/fleet-scanner.js +51 -21
- package/src/lib/worktree/ignored-triage.js +197 -0
- package/src/lib/worktree/worktree-audit.js +200 -35
- package/src/scripts/setup-infra.js +60 -1
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +40 -0
- package/src/utils/skills-installer.js +187 -187
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
package/docs/QUICKSTART.md
CHANGED
package/framework/CLAUDE.md
CHANGED
|
@@ -58,13 +58,16 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
|
58
58
|
| `list [--all] [--json]` | Quadro de auditoria: feature × workType × status × gates |
|
|
59
59
|
| `delete <f> [--archive] [--force]` | Remove feature; recusa se gate aprovado sem `--force` |
|
|
60
60
|
| `retype <f> <tipo>` | Muda o workType mid-flight; bloqueado após o Gate 3 |
|
|
61
|
-
| `finish <f> --pr\|--merge` | Encerramento único: arquiva, promove use cases,
|
|
61
|
+
| `finish <f> --pr\|--merge` | Encerramento único: arquiva, promove use cases, desmonta worktree. Integra só no `--merge`; `--pr` abre PR e fica `aguardando-merge` até alguém mergear |
|
|
62
62
|
| `archive <f>` | Arquiva sem integrar (legadas/faxina) |
|
|
63
63
|
| `worktree setup\|link\|remove\|list` | Ciclo do worktree; `remove` é a única remoção segura |
|
|
64
|
+
| `branch prune [--dry-run]` | Apaga branch já integrada, inclusive squash-merge; nunca a com PR aberto |
|
|
64
65
|
| `handoff <f>` | Regenera o documento de retomada da feature |
|
|
65
66
|
| `fleet` | Visão agregada de toda feature ativa (raiz + worktrees) |
|
|
66
67
|
| `claim [f] [--release] [--list]` | Posse advisory — confirme antes de `finish`/`archive`/`delete` se outra sessão tiver claim vivo |
|
|
67
68
|
| `dag <f>` | DAG de `tasks.json`: sequencial/paralelo, recomenda isolamento |
|
|
69
|
+
| `graph status\|build\|refresh` | Grafo de codebase. `status` reporta frescor: o rebuild do hook é assíncrono |
|
|
70
|
+
| `graph affected\|explain <sym>` | Blast radius e vizinhança saneados. Sem grafo, caia para grep — nunca é erro |
|
|
68
71
|
| `verify <f> [task] [--json]` | Build+testes+validadores+e2e, antes do juiz LLM |
|
|
69
72
|
| `e2e up\|status\|down <f>` | Stack de verificação (docker compose) |
|
|
70
73
|
| `doctor --worktrees [--prune]` | Reconcilia git × disco × features; só `--prune` remove algo |
|
package/framework/MORPH.md
CHANGED
|
@@ -54,6 +54,7 @@ Padrões proibidos automaticamente injetados no mandate:
|
|
|
54
54
|
- Camada de Service/Manager entre handler e dados — o handler fala com o `ApplicationDbContext` direto, nunca com um service
|
|
55
55
|
- Repository genérico (`IRepository<T>`/`IUnitOfWork`) — redundante sobre EF Core: `DbSet<T>` já é repository, `DbContext` já é unit of work
|
|
56
56
|
- SQL cru (`FromSqlRaw`, `ExecuteQuery(string)`) sem justificativa em `decisions.md` — use LINQ
|
|
57
|
+
- Redeclarar tipo/componente que já existe na mesma feature — `morph-spec graph explain <nome>` antes de criar (sem grafo: `grep -rn`); criar exige veredito `CRIAR` no `reuse-map.md`
|
|
57
58
|
|
|
58
59
|
Acesso a dados em VSA: `ApplicationDbContext` direto no slice (`DbSet<T>` já é repository, `DbContext` já é unit of work). Concorrência ou SQL específico → store dedicado hand-written com justificativa em `decisions.md`.
|
|
59
60
|
|
|
@@ -109,7 +110,7 @@ Nem todo trabalho merece o pipeline completo. Cada feature nasce com **identidad
|
|
|
109
110
|
|
|
110
111
|
**Compat:** feature sem `workType` (pré-v8) é tratada como `feature`. Mudança mid-flight: `morph-spec retype <f> <tipo>` (desfaz só carimbos de política, preserva aprovações humanas; bloqueado após o Gate 3).
|
|
111
112
|
|
|
112
|
-
**Nascimento numa branch:** toda feature — com ou sem `--worktree` — nasce em `morph/{f}` ramificada da default (comum: desenvolve direto na raiz; worktree: isola em `worktrees/{f}`).
|
|
113
|
+
**Nascimento numa branch:** toda feature — com ou sem `--worktree` — nasce em `morph/{f}` ramificada da default (comum: desenvolve direto na raiz; worktree: isola em `worktrees/{f}`). Fecha-se igual nos dois com `morph-spec finish <f> --pr|--merge` (§2.2). Features pré-modelo (sem branch `morph/{f}`) fecham por `morph-spec archive <f>`, não por `finish`.
|
|
113
114
|
|
|
114
115
|
### 2.2 Desenvolvimento paralelo por worktree (frota)
|
|
115
116
|
|
|
@@ -125,7 +126,7 @@ Features independentes podem ser desenvolvidas **em paralelo, cada uma no seu gi
|
|
|
125
126
|
- **Handoff entre sessões:** `.morph/features/{f}/handoff.md` — retomada comprimida, regenerável (`morph-spec handoff <f>`, resolve a árvore dona sozinho), **aponta** para os artefatos em vez de copiá-los. Regenerado em `setup`/`create --worktree`/`advance`/`approve`. **Leia-o antes de re-derivar contexto à mão** — nunca edite manualmente.
|
|
126
127
|
- **Visão da frota:** `fleet` agrega raiz + worktrees (fase, gates, tasks, portas, handoff, commit, sujeira); `worktree list` é a visão enxuta por worktree.
|
|
127
128
|
- **Ownership de arquivos:** sem lock runtime — colisão resolve **em plan-time** (`dag` detecta overlap de `outputs`, recomenda `isolation: "worktree"`) e **por construção** (uma feature = um worktree = um escritor). Arquivos compartilhados convergem no merge do `finish`; conflito ali é escopo sobreposto, não bug.
|
|
128
|
-
- **Fechamento:** aprove o Gate 3, depois **`morph-spec finish <f> --pr|--merge`** — encerramento único
|
|
129
|
+
- **Fechamento:** aprove o Gate 3, depois **`morph-spec finish <f> --pr|--merge`** — encerramento único; `--merge` integra na hora, **`--pr` não** — só abre o PR. **Arquiva** commitando `features/{f} → archive/{f}` **dentro de `morph/{f}`**: `--merge` faz `--no-ff` **local** (sem push; recusa se sujo; aborta em conflito); `--pr` faz `push` + `gh pr create` (exige `origin`+`gh`, checados **antes** de arquivar) — fica `aguardando-merge` até o merge, **nunca "entregue" só por `finished: true`**. Worktree desmontado (PROVE-or-DECLINE), portas liberadas; no `--pr` a branch é **preservada**. `morph-spec archive` fica para arquivar-sem-integrar (features legadas, faxina).
|
|
129
130
|
|
|
130
131
|
---
|
|
131
132
|
|
|
@@ -264,7 +265,7 @@ Inline (sem dispatch) é aceitável para tasks curtas (S/M) **dentro do domínio
|
|
|
264
265
|
|
|
265
266
|
**Mecanismo de dispatch:** agents de domínio são subagents **registrados** (`.claude/agents/morph-{id}.md`, gerados de `agents.json` na instalação). Despache com `subagent_type: "morph-{id}"` (ex.: `morph-dotnet-senior`) — persona é o **system prompt**, tools/model/MCPs por domínio em `agents.json` (ex.: `context7` nos implementadores; Stitch/Gemini/Playwright no `morph-ui-designer`; `morph-evaluator` é read-only). **Fallback** (tipo não registrado): `general-purpose` com `persona` colado no prompt, **registrado nas `notes` da task**. O `**Persona:**` do recap registra sempre o mecanismo real — nunca auto-declaração.
|
|
266
267
|
|
|
267
|
-
**Persona de estágio — scout:** `morph-scout` (read-only: Read/Glob/Grep/Bash somente-leitura). Despache para um mapa do território sem contaminar a thread principal — obrigatório no início de um hotfix (root cause + blast radius), opcional antes do plan de uma feature grande. Devolve um mapa curto como relatório final; não escreve código.
|
|
268
|
+
**Persona de estágio — scout:** `morph-scout` (read-only: Read/Glob/Grep/Bash somente-leitura). Começa por `morph-spec graph affected|explain` e cai para grep sozinho quando não há grafo. Despache para um mapa do território sem contaminar a thread principal — obrigatório no início de um hotfix (root cause + blast radius), opcional antes do plan de uma feature grande (`morph-plan` §1b). Devolve um mapa curto como relatório final; não escreve código.
|
|
268
269
|
|
|
269
270
|
**Passe ao sub-agent:** especificação da tarefa (tasks.json), path do standard relevante (só um), arquivos a ler/modificar, critério de done claro e verificável.
|
|
270
271
|
|
package/framework/agents.json
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"mcp": ["context7"],
|
|
27
27
|
"model": "sonnet",
|
|
28
|
-
"persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern, sealed classes, Guid.CreateVersion7(). Acesso a dados: ApplicationDbContext injetado direto no slice (DbSet<T> já é repository, DbContext já é unit of work); para concorrência ou SQL específico, store dedicado hand-written com justificativa em decisions.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."
|
|
28
|
+
"persona": "Você é um engenheiro .NET sênior. Pensa em VSA com handlers diretos, result pattern, sealed classes, Guid.CreateVersion7(). Acesso a dados: ApplicationDbContext injetado direto no slice (DbSet<T> já é repository, DbContext já é unit of work); para concorrência ou SQL específico, store dedicado hand-written com justificativa em decisions.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 task."
|
|
29
29
|
},
|
|
30
30
|
"ef-modeler": {
|
|
31
31
|
"title": "EF Core / Database Modeler",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
],
|
|
45
45
|
"mcp": ["context7"],
|
|
46
46
|
"model": "sonnet",
|
|
47
|
-
"persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. Sempre Guid.CreateVersion7() para PKs. Sealed entities. Nunca leak de DbContext fora do handler."
|
|
47
|
+
"persona": "Você é um especialista em EF Core sobre Neon (Postgres). Pensa em migrations explícitas, configurações fluentes, navigation properties bem nomeadas, índices intencionais. Sempre Guid.CreateVersion7() para PKs. Sealed entities. Nunca leak de DbContext fora do handler. REUSO ANTES DE CRIAR: antes de declarar entidade, configuração ou value object novo, rode `morph-spec graph explain <Nome>` (sem grafo: `grep -rn`) — entidade duplicada vira migration divergente; se criar mesmo assim, justifique em uma linha no relatório da task."
|
|
48
48
|
},
|
|
49
49
|
"maf-expert": {
|
|
50
50
|
"title": "Microsoft Agent Framework Expert",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
],
|
|
71
71
|
"mcp": ["context7"],
|
|
72
72
|
"model": "sonnet",
|
|
73
|
-
"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 AgentOpenTelemetryConsts.DefaultSourceName."
|
|
73
|
+
"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 AgentOpenTelemetryConsts.DefaultSourceName. 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 task."
|
|
74
74
|
},
|
|
75
75
|
"nextjs-expert": {
|
|
76
76
|
"title": "Next.js / React Expert",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
],
|
|
93
93
|
"mcp": ["context7"],
|
|
94
94
|
"model": "sonnet",
|
|
95
|
-
"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."
|
|
95
|
+
"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 task."
|
|
96
96
|
},
|
|
97
97
|
"ui-designer": {
|
|
98
98
|
"title": "UI/UX Designer",
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
"tools": "Read, Write, Edit, Glob, Grep",
|
|
124
124
|
"mcp": ["context7", "stitch", "gemini-image", "playwright", "seedance"],
|
|
125
125
|
"model": "sonnet",
|
|
126
|
-
"persona": "Você desenha sistemas, não telas isoladas. Pensa em tokens (cor, espaço, tipografia), componentes reutilizáveis, estados (default/hover/focus/disabled/loading/error), acessibilidade WCAG AA. Mockups em Tailwind direto, sem Figma intermediário."
|
|
126
|
+
"persona": "Você desenha sistemas, não telas isoladas. Pensa em tokens (cor, espaço, tipografia), componentes reutilizáveis, estados (default/hover/focus/disabled/loading/error), acessibilidade WCAG AA. Mockups em Tailwind direto, sem Figma intermediário. 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 task, a mesma regra que já vale para token de design."
|
|
127
127
|
},
|
|
128
128
|
"infra-engineer": {
|
|
129
129
|
"title": "Infrastructure / Jobs Engineer",
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
"standards": [],
|
|
171
171
|
"tools": "Read, Glob, Grep, Bash",
|
|
172
172
|
"model": "haiku",
|
|
173
|
-
"persona": "Você é um scout read-only: mapeia o território sem escrever código. Dado um sintoma ou uma feature, localiza os arquivos relevantes, o root cause provável e o blast radius (o que mais é afetado).
|
|
173
|
+
"persona": "Você é um scout read-only: mapeia o território sem escrever código. Dado um sintoma ou uma feature, localiza os arquivos relevantes, o root cause provável e o blast radius (o que mais é afetado).\n\nCOMECE PELO GRAFO, não pelo grep. `morph-spec graph affected <símbolo> --json` dá o blast radius; `morph-spec graph explain <símbolo> --json` dá a vizinhança com arquivo:linha. Contra o grep isso é 58% menos ruído e, principalmente, classifica a relação: 20 tipos podem IMPLEMENTAR uma interface enquanto só 2 a USAM, e é essa diferença que decide se dá para mexer. Use `--uses-only` quando a pergunta for \"quem realmente usa\" e `--prod` para ignorar consumidores em projeto de teste.\n\nFALLBACK OBRIGATÓRIO: se o comando devolver `no-graph`, `not-found` ou `ambiguous`, caia para Read/Glob/Grep e siga normalmente — o grafo é um subconjunto do grep, então nunca perde arquivo que o grep acharia. Diga no seu mapa qual caminho você usou. Nunca trate ausência de grafo como impedimento.\n\nDUAS RESSALVAS que mudam a leitura do resultado: (1) o grafo pode estar STALE, porque o rebuild do hook roda em background — logo depois de um commit ele ainda é o anterior; confirme no código antes de afirmar que algo NÃO tem consumidor, que é justamente a conclusão perigosa antes de um hotfix. (2) Em frontend o grafo é estrutura de módulos, não call graph: ali o blast radius responde QUEM IMPORTA, não quem chama. Em backend responde quem chama.\n\nUsa Read/Glob/Grep e Bash SOMENTE-LEITURA (morph-spec graph, git log/diff/show, ls, cat) — nunca Write/Edit, nunca comandos que mudam estado. Devolve um mapa curto e acionável (arquivos com paths, hipótese de causa, áreas de impacto) como relatório final e encerra. Não implementa, não corrige — ilumina o caminho para quem vai."
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -29,7 +29,7 @@ If the user did not pass a feature name in `$ARGUMENTS`:
|
|
|
29
29
|
## What you display
|
|
30
30
|
|
|
31
31
|
The CLI prints a banner, the phase chain (`proposal → uiux → plan → implement → review`),
|
|
32
|
-
output completion (X/
|
|
32
|
+
output completion (X/N outputs), task progress, and next-step suggestions.
|
|
33
33
|
|
|
34
34
|
After running the CLI, summarize:
|
|
35
35
|
- Current phase and which gate (if any) is open
|
package/framework/docs/CLI.md
CHANGED
|
@@ -1,45 +1,68 @@
|
|
|
1
|
-
# morph-spec CLI — Referência Completa
|
|
2
|
-
|
|
3
|
-
> Referência completa (flags, semântica, exemplos) dos comandos `morph-spec` usados pela LLM em vez de editar o estado à mão. Resumo rápido em `.morph/framework/CLAUDE.md`; racional dos gates em `.morph/framework/MORPH.md` §5, dispatch de sub-agents em §6, worktree/fleet em §2.2.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Gates e estado
|
|
8
|
-
|
|
9
|
-
- **`morph-spec approve <feature> <gate> --mode auto`** — aprova um gate (`proposal`/`uiux`/`plan`/`review`) em `trust: auto`. **`uiux`** é o único gate cujo `AskUserQuestion` o hook `state-sync.js` não reconhece por texto (só casa "Gate 1/2/3") — aprove-o sempre explicitamente por este comando ou por `advance`, nunca contando com o hook. **O Gate 3 exige `4-review/evaluator-report.md`**: sem o relatório do avaliador independente o comando recusa (exit 1). Se o dispatch do avaliador for impossível nesta sessão, aprove com `--self-assessed` — a degradação é gravada em `approvalGates.review.selfAssessed` e aparece marcada no `morph-spec list`. `--force` **não** dispensa esta regra (é só para ordem de gate). **Exceção `hotfix`:** esse tipo nunca despacha o avaliador (`morph-hotfix` §7); o Gate 3 exige `4-review/review-report.md` no lugar do `evaluator-report.md`, e a assinatura humana continua obrigatória via `--approver` (o backstop de `--mode auto` acima permanece recusando).
|
|
10
|
-
- **`morph-spec advance <feature>`** — aprova o gate da fase corrente **e** cria a pasta da próxima fase num passo só (`--design` toma o ramo opcional de uiux a partir da proposta).
|
|
11
|
-
- **`morph-spec score <feature> <task> <score> [--dims a,c,q,t]`** — grava `taskScores` no feature.json com timestamp server-side; **nunca edite taskScores à mão**. Em PowerShell, cite o valor de `--dims` entre aspas (`--dims "9.5,9,9,9"`) — sem aspas, a lista pode chegar como tokens separados; o parser aceita ambas as formas, mas citar evita qualquer ambiguidade.
|
|
12
|
-
- **`morph-spec gate-check <feature> <gate> [--signals <json>] [--trust <mode>] [--json]`** — o caminho **default**: coleta do disco os sinais que `shouldPauseGate` (risk-detector.js) já sabe avaliar (`workType`, `taskCount`, verificação, sha/diffstat), decide a pausa e persiste `gateDecisions.{gate}`. `--signals` aceita só as chaves que o disco genuinamente não expõe sozinho — `designSystemDivergence`, `standardsViolations`, `evalScore` — qualquer outra chave é ignorada com aviso. No **review**, o `evalScore` real vem do token `**Composite-Score:** {n.n}` do `evaluator-report.md` (o avaliador o grava por contrato). **Score que não venha desse token PAUSA o Gate 3**, qualquer que seja o valor: o proxy (média das `taskScores`) e o override de `--signals` medem auto-avaliação de quem implementou, não qualidade da feature — em campo, 7 tasks com auto-score 9.0-9.3 auto-passaram um produto que o avaliador independente pontuou 7.2 e reprovou com 3 P1. `evalScoreSource` registra a procedência (`evaluator-report` / `taskScores-proxy` / `signals-override`). Para forçar a passagem mesmo assim, use `gate-decision --pause false` — explícito e registrado. Fora do review a regra não vale: antes de haver implementação não existe avaliador independente. Um stamp de `verification` com status `fail` (último `morph-spec verify` vermelho) **força pausa** em qualquer gate. Um stamp `pass` mas **stale** (sha diferente do HEAD atual — código mudou depois do último verify) força pausa **só no ship** (`review`); em `plan`/`uiux`/`proposal` a staleness é computada e reportada em `signals`, mas não pausa nada — é regra de ship, não de gate genérico. Stamp **ausente** nunca pausa por isso (features legadas sem stamp).
|
|
13
|
-
- **`morph-spec gate-decision <feature> <gate> --pause <bool> [--reason <txt>] [--signals <json>]`** — o **escape manual**: registra uma decisão já tomada pela sessão (ex.: `discoveryMode: "interview"`, sinal que `shouldPauseGate` nem representa). Mesmo write path do `gate-check` — grava `gateDecisions` no feature.json com timestamp server-side (shape em `framework/schemas/feature.schema.json`); **nunca edite gateDecisions à mão** — o Edit manual colide com o hook state-sync.
|
|
14
|
-
- **Guarda de árvore dona:** `approve`/`unapprove`/`advance`/`score`/`gate-decision`/`gate-check` recusam (exit 1) quando um worktree vivo é dono da feature e a sessão está em outra árvore — sem isso o comando carimbaria o gate/score/decisão na cópia congelada da raiz, criando o drift em vez de só herdá-lo. A recusa nomeia o worktree e o comando exato para rodar lá; `--force` **não** dispensa (é sobre gate order, não sobre árvore). Rodando de dentro do worktree dono, ou numa feature sem worktree algum, nada muda.
|
|
15
|
-
|
|
16
|
-
## Features
|
|
17
|
-
|
|
18
|
-
- **`morph-spec create <feature> --description "<o quê/porquê>" --type <feature|bug|chore|hotfix> [--request "<txt>"] [--name "<nome>"] [--worktree]`** — cria a estrutura da feature + `feature.json` canônico explicitamente (nascimento explícito no início da proposal; o hook state-sync continua cobrindo o caminho implícito como fallback). **Identidade no nascimento:** `name` (default = slug) e `description` são carimbados; a `--description` é **obrigatória** numa feature nova (ou `--request`, que a semeia) — sem nenhuma das duas o `create` recusa (fim dos scaffolds anônimos). `--type` classifica direto; `--request "<pedido>"` roda o detector determinístico (empate/sem sinal sai com exit 2 e candidatos — a LLM decide e re-roda com `--type` + `--request`/`--description`); `--worktree` já nasce a feature no seu worktree próprio. **Toda feature nasce numa branch `morph/{f}`** (ramificada da default) mesmo sem `--worktree` — por isso o fechamento por `morph-spec finish` é idêntico nos dois fluxos.
|
|
19
|
-
- **`morph-spec list [--all] [--json] [--no-branch]`** — o quadro de auditoria: uma linha por feature × workType × status × fase × gates G1/G2/G3 × descrição, com `⚠` para inconsistências (sem descrição, ordem de gate). Uma feature com worktree/branch ativos tem a cópia da RAIZ congelada no instante do nascimento (`create --worktree` grava lá um esqueleto completo e nunca mais atualiza) — por padrão o `list` lê a verdade commitada em `morph/{f}` quando ela diverge do disco desta árvore (mesma leitura do `fleet`), reportando `source: 'branch'|'disk'` e uma coluna **WHERE** (`raiz` / `branch morph/{f}` / `worktree {dir}` / `⚠ worktree ausente`). `--no-branch` volta ao disco puro, sem custo de git. Um ponteiro `worktree.path` que não existe mais aparece com o flag `worktree-missing` (repare com `doctor --worktrees --prune`). Uma feature já arquivada em outra árvore/branch, cujo esqueleto sobrevive aqui, ganha o flag `archived-elsewhere` em vez de sumir da lista (read-only: nunca ressuscita nem poda). `--all` inclui as arquivadas; `--json` para scripting. É o complemento do `delete` (enxergar o que está morto antes de remover).
|
|
20
|
-
- **`morph-spec delete <feature> [--archive] [--force] [--with-worktree]`** — remove uma feature. Hard por padrão (pasta apagada, git-aware, + entrada podada do índice raiz para não ressuscitar); `--archive` move para `.morph/archive` com status `abandoned` em vez de apagar. Recusa se algum gate estiver aprovado, a menos que `--force`. Recusa TAMBÉM se um worktree vivo estiver em outra árvore (`feature-has-worktree` — apagar o registro da raiz órfão o worktree+branch em silêncio) — `--force` **não** dispensa isto (é consentimento sobre gate, não sobre filesystem); feche primeiro com `finish`/`worktree remove`, ou passe `--with-worktree` para desmontar o worktree (PROVE-or-DECLINE) e apagar num passo só — a branch sobrevive por padrão (`--delete-branch` para descartá-la também). Use para faxina de scaffolds mortos e drafts abandonados (`archive` é para trabalho concluído; `delete` é para trabalho morto).
|
|
21
|
-
- **`morph-spec retype <feature> <newType>`** — muda o tipo de trabalho mid-flight; desfaz só os gates carimbados por política, preserva aprovações humanas; bloqueado após o Gate 3.
|
|
22
|
-
|
|
23
|
-
## Worktree / Fleet
|
|
24
|
-
|
|
25
|
-
- **`morph-spec finish <feature> --pr|--merge`** — o **encerramento único** da feature (worktree ou comum): **arquiva** commitando `features/{f} → archive/{f}` dentro de `morph/{f}` — promovendo junto, no mesmo commit, os use cases da feature para o acervo permanente `.morph/specs/usecases/` com `status: implemented` (fail-open: sem `2-plan/usecases/`, no-op silencioso) — depois integra. Um `UC-{nn}` cuja promoção colide com outra feature já publicada sob o mesmo número (`ucCollisions` no output; ver `usecase reserve` abaixo) **não é promovido**, mas isso nunca recusa o `finish` — só aquele use case fica de fora, com a mensagem apontando onde o arquivo ficou e como recuperar. `--merge` faz merge `--no-ff` **local** na default (sem push); `--pr` faz `push` + `gh pr create` (exige `origin` + `gh`, checados **antes** de arquivar — um `--pr` recusado não deixa meio-estado). Desmonta o worktree (PROVE-or-DECLINE) e libera o bloco de portas; no `--pr` **preserva** a branch (o PR precisa dela) e fica em `morph/{f}`. Flags: `--keep` mantém o worktree **e** o registro dele na raiz (válido enquanto o diretório existir) — o output reporta `worktree: {path, branch}` e um `hint` lembrando `worktree remove <f>` para fechar depois; `--force` dispensa o Gate 3; `--discard-ignored` consente descartar gitignored no teardown. Substitui o antigo `worktree finish` + `archive` separados. **Retomável:** conflito de merge, checkout ou push falham *depois* do archive (é o commit do archive que viaja com a branch) — re-executar o `finish` retoma na integração e sai com `resumed: true`, em vez de recusar por feature ausente. Se a integração já tiver acontecido, recusa com `already-finished` — e o que conta como "já integrada" depende do modo: no `--merge`, branch contida na default; no `--pr`, PR aberto para `morph/{f}` (a branch de um `--pr` concluído fica não-mergeada de propósito, então só o teste de ancestralidade deixaria passar um segundo `gh pr create`). Sem conseguir provar nem uma coisa nem outra, recusa com `resume-undecidable` em vez de retomar às cegas.
|
|
26
|
-
- **`morph-spec usecase reserve [--count <n>] [--feature <name>] [--json]`** — reserva atomicamente os próximos N números `UC-{nn}`, compartilhado por **todo worktree** deste repo (`<git-common-dir>/morph-spec/uc-registry.json`, mesmo mecanismo do `claim`/env-registry do fleet). Substitui o scan manual "leia o acervo + as features da árvore e some 1": um scan só enxerga a ÁRVORE CORRENTE, então dois worktrees planejando ao mesmo tempo mintavam o MESMO número — colisão que a promoção do `finish` resolvia sobrescrevendo por ID, apagando o use case do primeiro a chegar (já custou perda de dados em produção). O contador nunca regride (semeado do maior número já visto no acervo + nas features da árvore + no próprio registro). `--feature` é mais que auditoria: é o sinal que o guarda de colisão do `finish`/`archive` usa para distinguir uma correção legítima (`bug`, nunca reserva — reaproveita um número publicado) de uma colisão real (a feature reservou o número e o acervo já tem outra dona nele); sem `--feature`, infere a única feature ativa se for inequívoco. Fora de um repositório git: cai para o scan local (avisa no output, `shared: false`) — ainda evita autocolisão dentro da mesma árvore, mas sem a garantia entre worktrees.
|
|
27
|
-
- **`morph-spec archive <feature>`** — arquiva-**sem-integrar** (atômico, git-aware, commita o move escopado por pathspec): reservado a features legadas (sem branch `morph/{f}`) e faxina. O fechamento normal (arquiva **e** integra num passo) é `morph-spec finish`. É o que `/morph-archive` chama.
|
|
28
|
-
- **`morph-spec worktree setup|link|remove|list`** — gerencia o ciclo do worktree por feature (`setup` cria `worktrees/{f}/` na branch `morph/{f}`, aloca o bloco de portas e escreve `.morph/worktree.env`; `link` religa infra compartilhada; `list` mostra fase/gate de cada worktree ativo). **Fechar a feature** continua sendo `morph-spec finish` (acima).
|
|
29
|
-
- **`morph-spec worktree remove [feature] [--target <path>] [--discard-ignored] [--delete-branch]`** — a remoção **segura** de um worktree, sem fechar a feature. Desmonta as junctions de infra ANTES de deixar o git chegar no diretório e recusa se não conseguir **provar** que é seguro (PROVE-or-DECLINE) — `git worktree remove` cru, com ou sem `--force`, atravessa a junction e apaga o `.claude/`/`.morph/framework/`/`node_modules/` **reais da raiz**, saindo com sucesso. Use `--target` para worktree sem feature registrada (os que o `isolation: "worktree"` cria). A branch **sobrevive** por padrão: remover o worktree não é descartar o trabalho.
|
|
30
|
-
- **`morph-spec
|
|
31
|
-
- **`morph-spec
|
|
32
|
-
- **`morph-spec
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
- **`morph-spec
|
|
38
|
-
- **`morph-spec
|
|
39
|
-
- **`morph-spec
|
|
40
|
-
- **`morph-spec
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
- **`morph-spec
|
|
45
|
-
- **`morph-spec
|
|
1
|
+
# morph-spec CLI — Referência Completa
|
|
2
|
+
|
|
3
|
+
> Referência completa (flags, semântica, exemplos) dos comandos `morph-spec` usados pela LLM em vez de editar o estado à mão. Resumo rápido em `.morph/framework/CLAUDE.md`; racional dos gates em `.morph/framework/MORPH.md` §5, dispatch de sub-agents em §6, worktree/fleet em §2.2.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Gates e estado
|
|
8
|
+
|
|
9
|
+
- **`morph-spec approve <feature> <gate> --mode auto`** — aprova um gate (`proposal`/`uiux`/`plan`/`review`) em `trust: auto`. **`uiux`** é o único gate cujo `AskUserQuestion` o hook `state-sync.js` não reconhece por texto (só casa "Gate 1/2/3") — aprove-o sempre explicitamente por este comando ou por `advance`, nunca contando com o hook. **O Gate 3 exige `4-review/evaluator-report.md`**: sem o relatório do avaliador independente o comando recusa (exit 1). Se o dispatch do avaliador for impossível nesta sessão, aprove com `--self-assessed` — a degradação é gravada em `approvalGates.review.selfAssessed` e aparece marcada no `morph-spec list`. `--force` **não** dispensa esta regra (é só para ordem de gate). **Exceção `hotfix`:** esse tipo nunca despacha o avaliador (`morph-hotfix` §7); o Gate 3 exige `4-review/review-report.md` no lugar do `evaluator-report.md`, e a assinatura humana continua obrigatória via `--approver` (o backstop de `--mode auto` acima permanece recusando).
|
|
10
|
+
- **`morph-spec advance <feature>`** — aprova o gate da fase corrente **e** cria a pasta da próxima fase num passo só (`--design` toma o ramo opcional de uiux a partir da proposta).
|
|
11
|
+
- **`morph-spec score <feature> <task> <score> [--dims a,c,q,t]`** — grava `taskScores` no feature.json com timestamp server-side; **nunca edite taskScores à mão**. Em PowerShell, cite o valor de `--dims` entre aspas (`--dims "9.5,9,9,9"`) — sem aspas, a lista pode chegar como tokens separados; o parser aceita ambas as formas, mas citar evita qualquer ambiguidade.
|
|
12
|
+
- **`morph-spec gate-check <feature> <gate> [--signals <json>] [--trust <mode>] [--json]`** — o caminho **default**: coleta do disco os sinais que `shouldPauseGate` (risk-detector.js) já sabe avaliar (`workType`, `taskCount`, verificação, sha/diffstat), decide a pausa e persiste `gateDecisions.{gate}`. `--signals` aceita só as chaves que o disco genuinamente não expõe sozinho — `designSystemDivergence`, `standardsViolations`, `evalScore` — qualquer outra chave é ignorada com aviso. No **review**, o `evalScore` real vem do token `**Composite-Score:** {n.n}` do `evaluator-report.md` (o avaliador o grava por contrato). **Score que não venha desse token PAUSA o Gate 3**, qualquer que seja o valor: o proxy (média das `taskScores`) e o override de `--signals` medem auto-avaliação de quem implementou, não qualidade da feature — em campo, 7 tasks com auto-score 9.0-9.3 auto-passaram um produto que o avaliador independente pontuou 7.2 e reprovou com 3 P1. `evalScoreSource` registra a procedência (`evaluator-report` / `taskScores-proxy` / `signals-override`). Para forçar a passagem mesmo assim, use `gate-decision --pause false` — explícito e registrado. Fora do review a regra não vale: antes de haver implementação não existe avaliador independente. Um stamp de `verification` com status `fail` (último `morph-spec verify` vermelho) **força pausa** em qualquer gate. Um stamp `pass` mas **stale** (sha diferente do HEAD atual — código mudou depois do último verify) força pausa **só no ship** (`review`); em `plan`/`uiux`/`proposal` a staleness é computada e reportada em `signals`, mas não pausa nada — é regra de ship, não de gate genérico. Stamp **ausente** nunca pausa por isso (features legadas sem stamp).
|
|
13
|
+
- **`morph-spec gate-decision <feature> <gate> --pause <bool> [--reason <txt>] [--signals <json>]`** — o **escape manual**: registra uma decisão já tomada pela sessão (ex.: `discoveryMode: "interview"`, sinal que `shouldPauseGate` nem representa). Mesmo write path do `gate-check` — grava `gateDecisions` no feature.json com timestamp server-side (shape em `framework/schemas/feature.schema.json`); **nunca edite gateDecisions à mão** — o Edit manual colide com o hook state-sync.
|
|
14
|
+
- **Guarda de árvore dona:** `approve`/`unapprove`/`advance`/`score`/`gate-decision`/`gate-check` recusam (exit 1) quando um worktree vivo é dono da feature e a sessão está em outra árvore — sem isso o comando carimbaria o gate/score/decisão na cópia congelada da raiz, criando o drift em vez de só herdá-lo. A recusa nomeia o worktree e o comando exato para rodar lá; `--force` **não** dispensa (é sobre gate order, não sobre árvore). Rodando de dentro do worktree dono, ou numa feature sem worktree algum, nada muda.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **`morph-spec create <feature> --description "<o quê/porquê>" --type <feature|bug|chore|hotfix> [--request "<txt>"] [--name "<nome>"] [--worktree]`** — cria a estrutura da feature + `feature.json` canônico explicitamente (nascimento explícito no início da proposal; o hook state-sync continua cobrindo o caminho implícito como fallback). **Identidade no nascimento:** `name` (default = slug) e `description` são carimbados; a `--description` é **obrigatória** numa feature nova (ou `--request`, que a semeia) — sem nenhuma das duas o `create` recusa (fim dos scaffolds anônimos). `--type` classifica direto; `--request "<pedido>"` roda o detector determinístico (empate/sem sinal sai com exit 2 e candidatos — a LLM decide e re-roda com `--type` + `--request`/`--description`); `--worktree` já nasce a feature no seu worktree próprio. **Toda feature nasce numa branch `morph/{f}`** (ramificada da default) mesmo sem `--worktree` — por isso o fechamento por `morph-spec finish` é idêntico nos dois fluxos.
|
|
19
|
+
- **`morph-spec list [--all] [--json] [--no-branch]`** — o quadro de auditoria: uma linha por feature × workType × status × fase × gates G1/G2/G3 × descrição, com `⚠` para inconsistências (sem descrição, ordem de gate). Uma feature com worktree/branch ativos tem a cópia da RAIZ congelada no instante do nascimento (`create --worktree` grava lá um esqueleto completo e nunca mais atualiza) — por padrão o `list` lê a verdade commitada em `morph/{f}` quando ela diverge do disco desta árvore (mesma leitura do `fleet`), reportando `source: 'branch'|'disk'` e uma coluna **WHERE** (`raiz` / `branch morph/{f}` / `worktree {dir}` / `⚠ worktree ausente`). `--no-branch` volta ao disco puro, sem custo de git — **e também pula a consulta de PR** (nenhuma coluna, nenhuma linha sintetizada, ver abaixo): não é só o modo disk-only de antes, é o modo offline completo. Um ponteiro `worktree.path` que não existe mais aparece com o flag `worktree-missing` (repare com `doctor --worktrees --prune`). Uma feature já arquivada em outra árvore/branch, cujo esqueleto sobrevive aqui, ganha o flag `archived-elsewhere` em vez de sumir da lista (read-only: nunca ressuscita nem poda). Coluna **PR** mostra `#{n} {estado}` (`-` sem PR para a branch da feature). Uma feature cujo `finish --pr` rodou mas cujo PR segue aberto — sem cópia local nesta árvore — aparece como linha **sintetizada**: fase `aguardando-merge`, flag `pr-pending`, WHERE `branch morph/{f}`, descrição "aguardando merge há Nd — sem cópia local" (mesma síntese que `fleet` e `doctor` usam, de `src/lib/git/pending-prs.js` — uma feature visível numa dessas telas e invisível na outra é o bug que esse módulo compartilhado existe para impedir). `--all` inclui as arquivadas; `--json` para scripting. É o complemento do `delete` (enxergar o que está morto antes de remover).
|
|
20
|
+
- **`morph-spec delete <feature> [--archive] [--force] [--with-worktree]`** — remove uma feature. Hard por padrão (pasta apagada, git-aware, + entrada podada do índice raiz para não ressuscitar); `--archive` move para `.morph/archive` com status `abandoned` em vez de apagar. Recusa se algum gate estiver aprovado, a menos que `--force`. Recusa TAMBÉM se um worktree vivo estiver em outra árvore (`feature-has-worktree` — apagar o registro da raiz órfão o worktree+branch em silêncio) — `--force` **não** dispensa isto (é consentimento sobre gate, não sobre filesystem); feche primeiro com `finish`/`worktree remove`, ou passe `--with-worktree` para desmontar o worktree (PROVE-or-DECLINE) e apagar num passo só — a branch sobrevive por padrão (`--delete-branch` para descartá-la também). Use para faxina de scaffolds mortos e drafts abandonados (`archive` é para trabalho concluído; `delete` é para trabalho morto).
|
|
21
|
+
- **`morph-spec retype <feature> <newType>`** — muda o tipo de trabalho mid-flight; desfaz só os gates carimbados por política, preserva aprovações humanas; bloqueado após o Gate 3.
|
|
22
|
+
|
|
23
|
+
## Worktree / Fleet
|
|
24
|
+
|
|
25
|
+
- **`morph-spec finish <feature> --pr|--merge`** — o **encerramento único** da feature (worktree ou comum): **arquiva** commitando `features/{f} → archive/{f}` dentro de `morph/{f}` — promovendo junto, no mesmo commit, os use cases da feature para o acervo permanente `.morph/specs/usecases/` com `status: implemented` (fail-open: sem `2-plan/usecases/`, no-op silencioso) — depois integra: `--merge` na hora; `--pr` só abre o PR, sem integrar. **O payload distingue os dois** — `--merge` sai com `integrated: true`; `--pr` sai com `integrated: false`, `awaiting: "merge"`, `pr: {number, url}` e um `nextStep` apontando para o merge. **`finished: true` sai nos dois casos e marca só o comando ter terminado, nunca a integração** — `--pr` não encerra a feature, quem encerra é o merge do PR; até lá ela aparece em `morph-spec list`/`fleet` como `aguardando-merge`, sem cópia local (ver `list` acima e `doctor` abaixo). Um `UC-{nn}` cuja promoção colide com outra feature já publicada sob o mesmo número (`ucCollisions` no output; ver `usecase reserve` abaixo) **não é promovido**, mas isso nunca recusa o `finish` — só aquele use case fica de fora, com a mensagem apontando onde o arquivo ficou e como recuperar. `--merge` faz merge `--no-ff` **local** na default (sem push); `--pr` faz `push` + `gh pr create` (exige `origin` + `gh`, checados **antes** de arquivar — um `--pr` recusado não deixa meio-estado). Desmonta o worktree (PROVE-or-DECLINE) e libera o bloco de portas; no `--pr` **preserva** a branch (o PR precisa dela) e fica em `morph/{f}`. Flags: `--keep` mantém o worktree **e** o registro dele na raiz (válido enquanto o diretório existir) — o output reporta `worktree: {path, branch}` e um `hint` lembrando `worktree remove <f>` para fechar depois; `--force` dispensa o Gate 3; `--discard-ignored` consente descartar gitignored no teardown. Substitui o antigo `worktree finish` + `archive` separados. **Retomável:** conflito de merge, checkout ou push falham *depois* do archive (é o commit do archive que viaja com a branch) — re-executar o `finish` retoma na integração e sai com `resumed: true`, em vez de recusar por feature ausente. Se a integração já tiver acontecido, recusa com `already-finished` — e o que conta como "já integrada" depende do modo: no `--merge`, branch contida na default; no `--pr`, PR aberto para `morph/{f}` (a branch de um `--pr` concluído fica não-mergeada de propósito, então só o teste de ancestralidade deixaria passar um segundo `gh pr create`). Sem conseguir provar nem uma coisa nem outra, recusa com `resume-undecidable` em vez de retomar às cegas.
|
|
26
|
+
- **`morph-spec usecase reserve [--count <n>] [--feature <name>] [--json]`** — reserva atomicamente os próximos N números `UC-{nn}`, compartilhado por **todo worktree** deste repo (`<git-common-dir>/morph-spec/uc-registry.json`, mesmo mecanismo do `claim`/env-registry do fleet). Substitui o scan manual "leia o acervo + as features da árvore e some 1": um scan só enxerga a ÁRVORE CORRENTE, então dois worktrees planejando ao mesmo tempo mintavam o MESMO número — colisão que a promoção do `finish` resolvia sobrescrevendo por ID, apagando o use case do primeiro a chegar (já custou perda de dados em produção). O contador nunca regride (semeado do maior número já visto no acervo + nas features da árvore + no próprio registro). `--feature` é mais que auditoria: é o sinal que o guarda de colisão do `finish`/`archive` usa para distinguir uma correção legítima (`bug`, nunca reserva — reaproveita um número publicado) de uma colisão real (a feature reservou o número e o acervo já tem outra dona nele); sem `--feature`, infere a única feature ativa se for inequívoco. Fora de um repositório git: cai para o scan local (avisa no output, `shared: false`) — ainda evita autocolisão dentro da mesma árvore, mas sem a garantia entre worktrees.
|
|
27
|
+
- **`morph-spec archive <feature>`** — arquiva-**sem-integrar** (atômico, git-aware, commita o move escopado por pathspec): reservado a features legadas (sem branch `morph/{f}`) e faxina. O fechamento normal (arquiva **e** integra num passo) é `morph-spec finish`. É o que `/morph-archive` chama.
|
|
28
|
+
- **`morph-spec worktree setup|link|remove|list`** — gerencia o ciclo do worktree por feature (`setup` cria `worktrees/{f}/` na branch `morph/{f}`, aloca o bloco de portas e escreve `.morph/worktree.env`; `link` religa infra compartilhada; `list` mostra fase/gate de cada worktree ativo). **Fechar a feature** continua sendo `morph-spec finish` (acima).
|
|
29
|
+
- **`morph-spec worktree remove [feature] [--target <path>] [--discard-ignored] [--delete-branch]`** — a remoção **segura** de um worktree, sem fechar a feature. Desmonta as junctions de infra ANTES de deixar o git chegar no diretório e recusa se não conseguir **provar** que é seguro (PROVE-or-DECLINE) — `git worktree remove` cru, com ou sem `--force`, atravessa a junction e apaga o `.claude/`/`.morph/framework/`/`node_modules/` **reais da raiz**, saindo com sucesso. Use `--target` para worktree sem feature registrada (os que o `isolation: "worktree"` cria). A branch **sobrevive** por padrão: remover o worktree não é descartar o trabalho. **Os gitignored são triados um a um**, nunca com um veredito só para o conjunto: o que existe na raiz no mesmo path com o **mesmo SHA-256** é replicável e some sem cerimônia (`replicatedInRoot`); artefato de build regenerável (`bin/`, `obj/`, `node_modules/`, `__pycache__/`, `test-results/`, `.auth/`, `*.tsbuildinfo`) é descartado (`discardedIgnored`); o que existe **só ali** é copiado para `.git/morph-spec/finish-backup/` e a remoção **prossegue** (`ignoredBackup` + `backedUp`). A única recusa por gitignored que sobrou é o **backup ter falhado** — e aí o `hint` diz o que é exclusivo, o que era replicável e para onde o backup ia, em vez de mandar rodar o comando que acabou de falhar. `--discard-ignored` deixou de significar "apague o exclusivo junto": agora significa **pular o backup**. As junctions desmontadas saem em `unlinked` — a prova de que o `.claude/` da raiz foi desligado, nunca seguido.
|
|
30
|
+
- **`morph-spec branch prune [--remote] [--dry-run] [--json]`** — apaga as branches locais cujo trabalho **já está** na default, **incluindo as squash-merged que o git não enxerga**. O problema que resolve: num repo que faz squash-merge, o commit de merge entra em `origin/master` mas os SHAs da branch não — `git branch --merged` não a lista, `git branch -d` recusa, e sobra o `-D`, indistinguível de descartar trabalho de verdade. Cada branch é classificada com evidência impressa: `merged-ancestral` (o git prova), `merged-squash` (PR `MERGED` pelo `gh`, **ou** árvore idêntica à da default), `open` (PR OPEN — nunca apagada), `in-worktree` (em uso — nunca apagada) e `unmerged` (**não foi possível provar** integração: sem `gh`, sem default resolvível ou trabalho de verdade — todas tratadas igual, de propósito). Só as duas primeiras são apagadas. Branch remota só com `--remote` explícito.
|
|
31
|
+
- **`morph-spec handoff <feature> [--here]`** — regenera o documento de retomada (`.morph/features/{f}/handoff.md`): comprimido, derivado, aponta para os artefatos em vez de copiá-los. **Resolve a árvore dona** antes de gerar (mesma lógica do `list`/`fleet`) e escreve LÁ, não necessariamente no cwd — rodar da raiz com o worktree vivo é seguro: o handoff sai correto e no lugar certo, em vez do esqueleto de nascimento da raiz. Sem árvore nenhuma materializada (só existe commitado em `morph/{f}`) recusa com `feature-not-in-tree`, nomeando a branch, em vez de gerar um documento apontando para arquivos inexistentes. `--here` força construir/escrever no cwd mesmo assim. Gerado automaticamente em `setup`/`create --worktree`/`advance`/`approve`.
|
|
32
|
+
- **`morph-spec fleet`** — visão da frota: toda feature ativa (raiz + worktrees) com fase, gates pendentes, tasks, **remoto** (`local`/`pushed`/`PR #N`), **claim** (que sessão está na feature), portas, handoff e atividade. Use entre sessões para decidir onde atuar.
|
|
33
|
+
- **`morph-spec claim [feature] [--release] [--list] [--all] [--json]`** — posse **advisory** de feature. Cada sessão reclama sua feature ativa automaticamente (hook SessionStart) e renova o heartbeat a cada tool call; o registro vive em `<git-common-dir>/morph-spec/claims.json`, compartilhado por todos os worktrees. **`morph-spec claim <feature>` (sem flags) ADQUIRE** — se esta árvore já tinha claim vivo em outra feature, ele é liberado no mesmo passo (o CLI reporta "movido de X para Y"); é o jeito de trocar de feature no meio da tarefa sem a dança de dois passos `--release` + reclamar. `--list` força a leitura sem adquirir mesmo passando uma feature. **Claims avisam, nunca bloqueiam** — mas se o SessionStart avisar que outra sessão tem claim vivo, **confirme antes de `finish`/`archive`/`delete`**: duas sessões na mesma feature já custaram um recap.md inteiro e um sub-agent (o worktree sumiu debaixo dele). Claim órfão de sessão morta: `morph-spec claim <f> --release`. **O board mostra só claims VIVOS** (dentro do TTL de 30 min): um claim morto não avisa mais nada, e deixá-lo listado transformava o quadro em ruído (features encerradas há 20h e 73h ainda aparecendo). `--all` mostra o registro inteiro; a contagem dos omitidos sai no rodapé, nunca em silêncio.
|
|
34
|
+
|
|
35
|
+
## Validação
|
|
36
|
+
|
|
37
|
+
- **`morph-spec dag <feature>`** — análise do DAG de tasks.json com decisão sequencial vs paralelo **ponderada por effort** (raízes S vão inline, nunca ganham agente próprio). Também cruza `tasks.json` com os use cases: `usecase` apontando para um `UC-{nn}` inexistente é **erro** (exit 1, não despacha); pós-condição órfã e `doneCriteria` que parafraseou em vez de copiar são avisos. `--mermaid` nunca é bloqueado.
|
|
38
|
+
- **`morph-spec verify <feature> [task] [--json] [--skip-e2e] [--fresh-e2e]`** — cadeia determinística build+testes+validadores+**e2e** (código antes do juiz LLM); escopo de task quando informado, senão a feature inteira. 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). Aplicabilidade: escopo de feature (Gate 3) **sempre** roda; escopo de task roda para `group` `frontend`/`backend`/`infra` e dispensa `tests`/`docs`. **Dispensa legítima × setup faltando:** projeto sem compose e sem a integração `docker` é `skip` com razão; projeto que **declara** `docker` e não tem compose é `fail` — é verificação que nunca foi montada, não projeto que não containeriza.
|
|
39
|
+
- **`morph-spec e2e up|status|down <feature>`** — ciclo de vida do stack de verificação, compartilhando toda a lógica com o nó `e2e` do verify. `up [--fresh]` reusa um stack quente **só** quando ele provadamente serve o código atual: o carimbo compara `HEAD` + `git status --porcelain` + `git diff HEAD`, então qualquer edição, commitada ou não, invalida o reuso (a exceção é o serviço com bind mount do código, que serve o disco por construção). Na dúvida, rebuild — o layer cache do Docker faz disso barato. `status [--json]` responde a pergunta que decide se um verde vale alguma coisa (no ar? saudável? construído do código atual?) e sai 0 só com os três; o `baseUrl` do payload é o endereço real da app — é dele que a `morph-review` tira a URL que vai no prompt do avaliador, em vez de deixá-lo adivinhar `localhost:3000`. `down [--volumes]` é best-effort e nunca bloqueia. Isolamento entre worktrees: `COMPOSE_PROJECT_NAME=morph-{feature}` separa os containers e o bloco de portas do fleet (`env-allocator`) separa as portas — um compose com porta fixa não pode ser isolado, e isso é avisado. O bloco vale nos **dois** caminhos (este comando e o nó do `verify`): `PORT`, `MORPH_PORT_BASE`, `ASPNETCORE_URLS` em dotnet, mais `PLAYWRIGHT_BASE_URL` apontando para a porta alocada. Por isso o `playwright.config.ts` precisa ler a base URL do ambiente em vez de cravá-la — com a URL cravada, a suíte bate numa porta vazia e o `ECONNREFUSED` se disfarça de app quebrada.
|
|
40
|
+
- **`morph-spec doctor [--full] [--mcp] [--reset]`** — checagem de saúde padrão (sem `--worktrees`): instalação (versões, `.claude/`, hooks, agents, grafo de codebase, templates, `state.json`) mais duas auditorias.
|
|
41
|
+
**Ciclo de vida das features:** ordem de gates, feature sem identidade, pasta órfã, entrada de índice morta, `branch-drift` (a cópia da raiz atrasada em relação ao `morph/{f}` commitado) e `abandoned-scaffold`. Este último é o esqueleto que o `create --worktree` deixa para trás: `feature.json` sozinho, `in_progress`, **0 artefatos, 0 gates aprovados** e `morph/{f}` **sem commit próprio** (a branch ainda apontando para o commit de merge de outra PR). As quatro condições valem juntas e só depois de **24h** — recém-nascida tem exatamente a mesma forma, e alarme em todo `create` é alarme que ninguém lê. Fora de repo git, ou com a default branch indeterminável, o check cala (não dá para provar). Reparo: `morph-spec delete <f> --archive`, ou retome a feature.
|
|
42
|
+
**Pull requests:** `pr-awaiting-merge` (PR aberto há mais de 7 dias sem cópia local nesta árvore) e `pr-merged-not-landed` (PR já `MERGED` mas `.morph/archive/{f}` ainda não chegou aqui — sinal de `git pull` faltando). Offline-safe: sem `gh`, sem remoto no GitHub, ou qualquer falha, a checagem sai **não verificado** (nunca `ok`) — `[]` (perguntei, nada encontrado) e `null` (não pude perguntar) nunca se confundem, e o "não verificado" nunca deixa o `doctor` mais barulhento nem muda o exit code.
|
|
43
|
+
Ver `--worktrees` abaixo para a reconciliação de worktrees (modo separado).
|
|
44
|
+
- **`morph-spec doctor --worktrees [--prune] [--json]`** — reconcilia o registro do git × as pastas em disco × as features conhecidas. Worktrees órfãos acumulavam **invisíveis**: o `fleet` só reporta worktrees em branch `morph/*`, então um worktree em outra branch (resíduo de finish anterior) não aparecia em lugar nenhum. `--prune` roda os reparos seguros: `git worktree prune` (registros de pastas que já sumiram), **remove os diretórios VAZIOS** em `worktrees/`/`.worktrees/` que o git não registra (`unregistered-dir` com `empty: true` — resíduo de finish; pasta com zero itens não tem o que perder, então some sem perguntar) **e** limpa o registro `worktree` de qualquer feature cujo `.path` aponte para um diretório já sumido (`dangling-registration` — a direção simétrica do `no-feature`: não é o worktree sem feature, é a feature com um ponteiro morto na raiz; a ordem é carregada: a pasta vazia sai primeiro, e o ponteiro que a nomeava é limpo no MESMO run). Apagar pasta que ainda **guarda** alguma coisa continua só reportado com o comando exato, nunca executado. **Nunca sugere remover worktree com trabalho não integrado:** antes de emitir `no-feature` **ou** `non-morph-branch` (os dois diagnósticos que sugeriam remoção), checa commits fora da default e PR aberto — havendo qualquer um, o diagnóstico vira `unintegrated-work` (severidade **info**, não conta como inconsistência e não faz o comando sair 1) e o `fix` diz para integrar, não para apagar. "Sem feature conhecida" é fato de bookkeeping (o `finish` arquivou a feature), não veredito sobre o trabalho — a versão anterior recomendou `worktree remove --target` no worktree que carregava o único exemplar de um PR aberto.
|
|
45
|
+
- **`morph-spec report-check <feature> [task] [--json]`** — audita os relatórios de sub-agent contra o trabalho que realmente entrou. **Relatório ausente NÃO quer dizer que o agente falhou** — ele pode ter feito tudo e perdido o meio de gravar. E relatório PRESENTE não basta: `report-stale` é o relatório completo porém gravado ANTES do fim do trabalho (o agente relatou cedo e continuou escrevendo) — completo e mentindo. O comando responde a pergunta certa (`work-landed-report-missing` → aceite degradado, NÃO re-despache; `no-work-no-report` → re-despache uma vez; `report-stale` → atualize a partir do campo `observed`, NÃO re-despache). A evidência de `observed` vem do `events.jsonl` (todo `Write`/`Edit` observado na janela da task, cruzada com `task-times.json`), não de `outputs`, que é auto-declarado pelo próprio agente — writes do orquestrador sob `.morph/` e o do próprio relatório são excluídos. Sem telemetria no projeto, o comando volta ao comportamento anterior (fail-open). Protocolo completo em MORPH.md §6.
|
|
46
|
+
|
|
47
|
+
## Grafo de codebase
|
|
48
|
+
|
|
49
|
+
Grafo de símbolos extraído localmente pelo [graphify](https://pypi.org/project/graphifyy/) (versão pinada **0.9.50**, AST via tree-sitter, **zero token**), servido atrás de uma camada de saneamento própria. **Tudo é fail-open:** sem Python, sem grafo, com grafo velho ou corrompido, o framework segue por grep — é assim que ele sempre funcionou, e nenhuma skill pode exigir o grafo (ADR-002).
|
|
50
|
+
|
|
51
|
+
O ganho medido **não é recall**: contra o grep, o grafo não achou um único arquivo a mais. É precisão — em 12 símbolos do ProspectPRO, o grep aponta 1.092 arquivos onde existem 266 usos reais (58% de ruído), e o grafo ainda diz *que tipo* de relação é cada um. `IHandler` aparece em 193 arquivos no grep e tem **2** usos reais: é implementada por 98.
|
|
52
|
+
|
|
53
|
+
**Por que não consumir o graphify cru.** O resolvedor C# dele não implementa escopo hierárquico de namespace, então um tipo usado por um namespace filho sem `using` explícito vira um *stub* por arquivo — e o blast radius do símbolo real devolve **zero** consumidores onde existem dois. Falso negativo silencioso é o pior modo de falha possível antes de um hotfix. A camada de saneamento coalesce esses stubs (+91,9% de arestas de uso recuperadas no MORPH_GHLBrain), trata como externo só o que **não tem nenhum** nó com arquivo (BCL/NuGet), e devolve as opções quando o símbolo é genuinamente ambíguo em vez de escolher por conta própria.
|
|
54
|
+
|
|
55
|
+
- **`morph-spec graph status [-p <path>] [--json]`** — existência, tamanho, **frescor** e saúde do saneamento. Grafo ausente **sai 0**, não 1: não ter grafo é um estado válido do projeto. O frescor compara o `built_at_commit` gravado no `graph.json` com o `HEAD` — e importa porque o hook `post-commit` do graphify rebuilda **em background**: logo depois de um commit, o grafo ainda é o do commit anterior. `--json` devolve `{ available, path, nodes, links, freshness: {state}, version: {installed, pinned, matches}, sanitize: {coalescible, external, ambiguous} }`.
|
|
56
|
+
- **`morph-spec graph build [-p <path>] [--json]`** — extração completa (`graphify extract --code-only`). Gera o `.graphifyignore` stack-aware antes de extrair, porque um grafo que nasce com o ruído dentro não se conserta depois. Projeto **sem nenhum arquivo de código** (o caso de um `init` recém-feito) sai **0** com aviso, não erro. Sem graphify no PATH: exit 1 com `reason: graphify-unavailable` e o comando de instalação — nunca stack trace.
|
|
57
|
+
- **`morph-spec graph refresh [-p <path>] [--json]`** — atualização incremental (`graphify update`), 2 a 3× mais rápida que o build graças ao cache por hash de conteúdo.
|
|
58
|
+
- **`morph-spec graph affected <símbolo> [--prod] [--uses-only] [-p <path>] [--json]`** — blast radius saneado. `--uses-only` exclui `inherits`/`implements`/`extends`, que é o que separa *"20 implementam"* de *"2 usam"*; `--prod` descarta consumidores em projeto de teste. Erros são estados, não exceções: `no-graph`, `not-found` e `ambiguous` (este devolvendo as opções com path e id) saem 1 com a dica de fallback para grep.
|
|
59
|
+
- **`morph-spec graph explain <símbolo> [-p <path>] [--json]`** — vizinhança com citação `arquivo:linha`, separando quem depende dele de do que ele depende. **Prefira `explain` a `query`** nas personas: no eval, `explain` foi consistentemente mais preciso, enquanto `query` trunca por budget e traz ruído. Cada aresta traz a procedência em `via` (`direct`, `coalesced`, `language-disambiguated`), então dá para ver exatamente o que o saneamento recuperou.
|
|
60
|
+
|
|
61
|
+
**O que o resultado significa muda com a stack.** Em backend, blast radius é *quem chama*. Em frontend, o grafo é estrutura de módulos e não call graph (`imports: 78` contra `calls: 3` no LP_Polymorphism), então ali ele responde *quem importa*. Ler um como o outro leva a conclusão errada, e é por isso que as personas dizem isso no próprio texto.
|
|
62
|
+
|
|
63
|
+
O servidor MCP (`graphify-mcp`, 10 tools, todas aceitando `project_path`) está registrado em `framework/mcp/registry.json` para exploração interativa e consulta cross-repo — mas ele devolve o grafo **cru**, sem saneamento. Para qualquer decisão, prefira os comandos acima.
|
|
64
|
+
|
|
65
|
+
## Observabilidade
|
|
66
|
+
|
|
67
|
+
- **`morph-spec telemetry [--json] [--feature <f>] [--limit <n>]`** — reconstrói o caminho de execução da sessão a partir de `.morph/logs/events.jsonl` (skills invocadas, personas despachadas + prompts, standards/base de conhecimento lidos, mix de tools, prompts do usuário). Capturado silenciosamente pelo hook `telemetry-log` em todo tool call e todo UserPromptSubmit.
|
|
68
|
+
- **`morph-spec cost [feature] [--by task|phase] [--all-trees] [--json]`** — custo da execução a partir do mesmo `events.jsonl`: duração por task (TaskStart→TaskEnd), bytes de contexto re-ingerido por tipo (`rubric`/`standard`/`skill`/`agents`/`plan-artifact`/`code`), dispatches + tamanho de prompt e mix de tools. Bytes re-ingeridos são **proxy determinística** de custo de token (tokens reais não eram observáveis por hook até o evento `SessionUsage` — quando presente no stream, `inputTokens`/`outputTokens`/`cacheReadTokens`/`cacheCreationTokens` são somados e exibidos à parte como "tokens reais", nunca misturados com a proxy de bytes). `--all-trees` agrega `.morph/logs/events.jsonl` de **toda árvore git** que tiver um (não só a corrente) — reusa a mesma enumeração de worktree do `doctor --worktrees`/`fleet`; mostra um resumo por árvore + o total combinado (`--by` aplica ao agregado combinado). Use antes/depois de mudar skill ou fluxo para medir o efeito.
|
|
@@ -8,6 +8,7 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
8
8
|
- Funções com responsabilidade única: cada função faz uma coisa e tem no máximo 20-25 linhas.
|
|
9
9
|
- Classes com responsabilidade única: coesas, sem mistura de concerns.
|
|
10
10
|
- Zero duplicação óbvia — lógica repetida foi extraída.
|
|
11
|
+
- **Nada do que foi criado aqui já existia no projeto**: os conceitos novos batem com o veredito do `reuse-map.md`, e cada `CRIAR` tem justificativa que se sustenta.
|
|
11
12
|
- Segue os padrões do stack conforme os standards do projeto (ex: Result pattern para .NET, hooks pattern para React).
|
|
12
13
|
- Sem "dead code" — sem variáveis não usadas, sem blocos comentados, sem imports desnecessários.
|
|
13
14
|
|
|
@@ -16,6 +17,7 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
16
17
|
- Nomes claros na maioria dos casos, com raras abreviações não óbvias.
|
|
17
18
|
- Funções e classes bem dimensionadas com poucos casos acima do ideal.
|
|
18
19
|
- Duplicação mínima e pontual.
|
|
20
|
+
- No máximo um conceito recriado que já existia no codebase, com impacto pequeno e localizado.
|
|
19
21
|
- Segue padrões do stack com desvios isolados e justificáveis.
|
|
20
22
|
- Sem dead code significativo.
|
|
21
23
|
|
|
@@ -24,6 +26,7 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
24
26
|
- Alguns nomes genéricos ou pouco descritivos (`data`, `result`, `temp`).
|
|
25
27
|
- Funções longas (>30 linhas) ou com múltiplas responsabilidades.
|
|
26
28
|
- Duplicação perceptível que aumenta custo de manutenção.
|
|
29
|
+
- Um ou dois tipos/componentes recriados que já existiam em outro arquivo do projeto.
|
|
27
30
|
- Desvios dos padrões do stack sem justificativa.
|
|
28
31
|
- Dead code pontual.
|
|
29
32
|
|
|
@@ -32,6 +35,7 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
32
35
|
- Nomes que exigem contexto externo para entender o que representam.
|
|
33
36
|
- Funções com >50 linhas ou classes com >200 linhas sem coesão.
|
|
34
37
|
- Duplicação significativa: mesma lógica em múltiplos lugares.
|
|
38
|
+
- Conceito central do domínio redeclarado na MESMA feature (o caso `SimpleBuilding`: cinco cópias na mesma pasta, uma já divergindo do resto).
|
|
35
39
|
- Não segue os padrões do stack — código parece escrito para outro contexto.
|
|
36
40
|
|
|
37
41
|
## Score 1-2 — Reprovado
|
|
@@ -39,12 +43,25 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
39
43
|
- Nomes arbitrários ou gerados automaticamente sem semântica.
|
|
40
44
|
- Funções/classes sem separação de responsabilidades — "god objects".
|
|
41
45
|
- Duplicação massiva ou copy-paste evidente.
|
|
46
|
+
- Ignora por completo o que o projeto já tem: o `reuse-map.md` mandou REUSAR e o código criou do zero.
|
|
42
47
|
- Completamente fora dos padrões do stack — exige reescrita.
|
|
43
48
|
|
|
49
|
+
> **O escopo desta dimensão é o CODEBASE, não só o diff.** "Duplicação" lê-se
|
|
50
|
+
> naturalmente como repetição *dentro do que foi escrito* — e foi assim que
|
|
51
|
+
> `SimpleBuilding` foi redefinido cinco vezes na mesma pasta do ProspectPRO sem
|
|
52
|
+
> que nenhuma avaliação pegasse: cada diff, sozinho, parecia limpo. Antes de
|
|
53
|
+
> pontuar, pergunte de fora do diff. Ferramentas: o `reuse-map.md` da feature
|
|
54
|
+
> (`2-plan/reuse-map.md`) diz o que o plano decidiu; `morph-spec graph explain
|
|
55
|
+
> <símbolo>` diz o que existe de fato (**sem grafo: `grep -rn "<símbolo>"`**);
|
|
56
|
+
> o nó `validators` do `morph-spec verify` já traz os achados do validador
|
|
57
|
+
> `reuse`. Um símbolo criado contra um veredito `REUSAR` é achado desta
|
|
58
|
+
> dimensão, mesmo que o código esteja impecável por dentro.
|
|
59
|
+
|
|
44
60
|
## Perguntas de avaliação
|
|
45
61
|
|
|
46
62
|
1. É possível entender o que uma função faz apenas pelo seu nome e assinatura?
|
|
47
63
|
2. Alguma função tem mais de 30 linhas ou mais de uma responsabilidade clara?
|
|
48
64
|
3. Há lógica duplicada que poderia ser extraída?
|
|
65
|
+
3b. **O que foi criado aqui já existia no projeto?** Confira os tipos, componentes, hooks e serviços novos contra o `reuse-map.md` e contra o grafo — e diga no relatório o que você conferiu.
|
|
49
66
|
4. O código segue os padrões definidos nos standards do projeto?
|
|
50
67
|
5. Há variáveis não usadas, imports desnecessários ou blocos comentados?
|
|
@@ -30,9 +30,11 @@ Se o diff toca endpoint minimal API ou fetch cross-camada, independente do resto
|
|
|
30
30
|
|---|---|---|---|---|
|
|
31
31
|
| **Arquitetura** | Ignora a arquitetura do spec.md; sem feature folder; deps circulares; ou violação mecânica acima | Pastas corretas, responsabilidades se misturam; deps cross-feature sem justificativa; domain acoplado a infra | Desvios menores justificados; deps cross-feature via abstração; impurezas de infra pontuais | Feature folder rigoroso; zero deps cross-feature diretas; domain isolado; DI onde exige |
|
|
32
32
|
| **Contratos** | Sem tipos ou `any` generalizado; zero validação; sem tratamento de erro; naming arbitrário | `any`/genérico visível; validação parcial; mix exceptions/Result; naming inconsistente | Tipos bem definidos, `any` raro e justificado; validação na maioria; erro consistente | Zero `any`; validação em todos endpoints; Result pattern/discriminated union; naming consistente |
|
|
33
|
-
| **Qualidade** | Nomes arbitrários; god objects; duplicação massiva; fora do padrão do stack | Nomes genéricos (`data`,`temp`); funções >30 linhas; duplicação perceptível | Nomes claros, raras exceções; bem dimensionado; duplicação mínima | Nomes comunicam intenção; funções ≤20-25 linhas, 1 responsabilidade; zero duplicação/dead code |
|
|
33
|
+
| **Qualidade** | Nomes arbitrários; god objects; duplicação massiva; recria o que o `reuse-map` mandou REUSAR; fora do padrão do stack | Nomes genéricos (`data`,`temp`); funções >30 linhas; duplicação perceptível; conceito da MESMA feature redeclarado | Nomes claros, raras exceções; bem dimensionado; duplicação mínima; no máximo um conceito recriado | Nomes comunicam intenção; funções ≤20-25 linhas, 1 responsabilidade; zero duplicação/dead code; nada criado que já existia no projeto |
|
|
34
34
|
| **Testes** | Sem testes ou sem asserts reais; zero edge case; nomes genéricos | Happy path OK, edge cases parciais; erros só óbvios; testes interdependentes | Happy path completo; maioria dos edge cases; erros nos fluxos principais | Toda pós-condição do `usecase` tem teste; A1/A2 cobertos; edge+erro cobertos; isolados |
|
|
35
35
|
|
|
36
|
+
> **Qualidade olha o CODEBASE, não só o diff.** Símbolo novo cujo nome já existe no projeto não aparece no diff — é preciso perguntar de fora dele. O nó `validators` do `morph-spec verify` já traz os achados do validador `reuse`; o `2-plan/reuse-map.md` diz o que o plano decidiu por conceito.
|
|
37
|
+
|
|
36
38
|
> Task com `usecase: UC-nn`: pós-condição sem teste **impede a faixa 9-10** em Testes, mesmo com o resto da suíte completo (herdado de `test-coverage.md`).
|
|
37
39
|
|
|
38
40
|
## Score composto
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
-
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
-
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
-
if command -v python3 &>/dev/null; then
|
|
8
|
-
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
-
elif command -v python &>/dev/null; then
|
|
10
|
-
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
-
fi
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
+
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
+
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
if command -v python3 &>/dev/null; then
|
|
8
|
+
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
+
elif command -v python &>/dev/null; then
|
|
10
|
+
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
+
fi
|
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Scope: universal | Validates commit message format (Conventional Commits)
|
|
3
|
-
# MORPH-SPEC Commit Message Hook
|
|
4
|
-
# Enforces Conventional Commits specification
|
|
5
|
-
|
|
6
|
-
COMMIT_MSG_FILE="$1"
|
|
7
|
-
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
|
|
8
|
-
|
|
9
|
-
# Conventional Commits pattern: type(scope): description
|
|
10
|
-
# Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert
|
|
11
|
-
PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .{1,100}"
|
|
12
|
-
|
|
13
|
-
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
|
|
14
|
-
echo "❌ Invalid commit message format!"
|
|
15
|
-
echo ""
|
|
16
|
-
echo "Required format: <type>(<scope>): <description>"
|
|
17
|
-
echo ""
|
|
18
|
-
echo "Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert"
|
|
19
|
-
echo ""
|
|
20
|
-
echo "Examples:"
|
|
21
|
-
echo " feat(auth): add JWT refresh token rotation"
|
|
22
|
-
echo " fix(api): resolve null reference in UserService"
|
|
23
|
-
echo " docs(readme): update installation instructions"
|
|
24
|
-
echo ""
|
|
25
|
-
echo "Your message:"
|
|
26
|
-
echo " $COMMIT_MSG"
|
|
27
|
-
echo ""
|
|
28
|
-
echo "Override with: git commit --no-verify"
|
|
29
|
-
exit 1
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
echo "✅ Commit message format valid"
|
|
33
|
-
exit 0
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Scope: universal | Validates commit message format (Conventional Commits)
|
|
3
|
+
# MORPH-SPEC Commit Message Hook
|
|
4
|
+
# Enforces Conventional Commits specification
|
|
5
|
+
|
|
6
|
+
COMMIT_MSG_FILE="$1"
|
|
7
|
+
COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
|
|
8
|
+
|
|
9
|
+
# Conventional Commits pattern: type(scope): description
|
|
10
|
+
# Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert
|
|
11
|
+
PATTERN="^(feat|fix|docs|style|refactor|perf|test|chore|build|ci|revert)(\(.+\))?: .{1,100}"
|
|
12
|
+
|
|
13
|
+
if ! echo "$COMMIT_MSG" | grep -qE "$PATTERN"; then
|
|
14
|
+
echo "❌ Invalid commit message format!"
|
|
15
|
+
echo ""
|
|
16
|
+
echo "Required format: <type>(<scope>): <description>"
|
|
17
|
+
echo ""
|
|
18
|
+
echo "Types: feat, fix, docs, style, refactor, perf, test, chore, build, ci, revert"
|
|
19
|
+
echo ""
|
|
20
|
+
echo "Examples:"
|
|
21
|
+
echo " feat(auth): add JWT refresh token rotation"
|
|
22
|
+
echo " fix(api): resolve null reference in UserService"
|
|
23
|
+
echo " docs(readme): update installation instructions"
|
|
24
|
+
echo ""
|
|
25
|
+
echo "Your message:"
|
|
26
|
+
echo " $COMMIT_MSG"
|
|
27
|
+
echo ""
|
|
28
|
+
echo "Override with: git commit --no-verify"
|
|
29
|
+
exit 1
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
echo "✅ Commit message format valid"
|
|
33
|
+
exit 0
|