@polymorphism-tech/morph-spec 8.1.2 → 8.2.1
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/README.md +2 -2
- package/bin/morph-spec.js +16 -1
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +6 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +8 -5
- package/framework/MORPH.md +28 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -1
- package/framework/hooks/claude-code/core/worktree-context.js +43 -0
- package/framework/hooks/shared/build-check.js +8 -1
- package/framework/hooks/shared/payload-utils.js +16 -2
- package/framework/hooks/shared/phase-utils.js +2 -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 +13 -8
- 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 +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +22 -10
- package/framework/skills/core/morph-plan/SKILL.md +1 -1
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +19 -12
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/package.json +1 -1
- package/src/commands/project/fleet.js +54 -0
- package/src/commands/project/worktree.js +206 -43
- package/src/commands/state/advance.js +8 -0
- package/src/commands/state/create.js +6 -0
- package/src/commands/state/handoff.js +49 -0
- package/src/commands/state/score.js +13 -2
- package/src/commands/tasks/dag.js +11 -3
- package/src/core/paths/output-schema.js +28 -0
- package/src/lib/generators/handoff-generator.js +267 -0
- package/src/lib/generators/index.js +1 -3
- package/src/lib/stack/stack-profile.js +40 -9
- package/src/lib/tasks/tasks-json-parser.js +21 -0
- package/src/lib/tasks/test-runner.js +26 -4
- package/src/lib/validators/validation-runner.js +4 -1
- package/src/lib/worktree/env-allocator.js +149 -0
- package/src/lib/worktree/fleet-scanner.js +156 -0
- package/src/lib/context/context-builder.js +0 -289
- package/src/lib/generators/context-generator.js +0 -519
- package/src/lib/generators/mandate-generator.js +0 -468
- package/src/lib/generators/recap-generator.js +0 -285
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:** 8.1
|
|
6
|
+
**Version:** 8.2.1
|
|
7
7
|
**Requires:** Node.js 22+, Claude Code
|
|
8
8
|
|
|
9
9
|
---
|
|
@@ -394,4 +394,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
|
|
|
394
394
|
|
|
395
395
|
---
|
|
396
396
|
|
|
397
|
-
*morph-spec v8.1
|
|
397
|
+
*morph-spec v8.2.1 by [Polymorphism Tech](https://polymorphism.tech)*
|
package/bin/morph-spec.js
CHANGED
|
@@ -24,6 +24,7 @@ import { updateCommand } from '../src/commands/project/update.js';
|
|
|
24
24
|
import { doctorCommand } from '../src/commands/project/doctor.js';
|
|
25
25
|
import { statusCommand } from '../src/commands/project/status.js';
|
|
26
26
|
import { worktreeSetupCommand, worktreeLinkCommand, worktreeFinishCommand, worktreeListCommand } from '../src/commands/project/worktree.js';
|
|
27
|
+
import { fleetCommand } from '../src/commands/project/fleet.js';
|
|
27
28
|
|
|
28
29
|
// === Approval gates (human-only) ===
|
|
29
30
|
import { approveCommand, approvalStatusCommand, unapproveCommand } from '../src/commands/state/approve.js';
|
|
@@ -33,6 +34,7 @@ import { scoreCommand } from '../src/commands/state/score.js';
|
|
|
33
34
|
import { gateDecisionCommand } from '../src/commands/state/gate-decision.js';
|
|
34
35
|
import { createCommand } from '../src/commands/state/create.js';
|
|
35
36
|
import { retypeCommand } from '../src/commands/state/retype.js';
|
|
37
|
+
import { handoffCommand } from '../src/commands/state/handoff.js';
|
|
36
38
|
|
|
37
39
|
// === Tasks ===
|
|
38
40
|
import { dagCommand } from '../src/commands/tasks/dag.js';
|
|
@@ -184,7 +186,7 @@ program
|
|
|
184
186
|
program
|
|
185
187
|
.command('score <feature> <task> <score>')
|
|
186
188
|
.description('Record a task eval score in feature.json (taskScores, server-side timestamp)')
|
|
187
|
-
.option('--dims <dims
|
|
189
|
+
.option('--dims <dims...>', 'Four dimension scores in order a,c,q,t (architecture,contracts,code-quality,test-coverage) — comma- or space-separated; quote under PowerShell: --dims "9.5,9,9,9"')
|
|
188
190
|
.option('--json', 'Output as JSON')
|
|
189
191
|
.action((feature, task, score, options) => scoreCommand(feature, task, score, options));
|
|
190
192
|
|
|
@@ -195,6 +197,12 @@ program
|
|
|
195
197
|
.option('--json', 'Output as JSON')
|
|
196
198
|
.action((feature, newType, options) => retypeCommand(feature, newType, options));
|
|
197
199
|
|
|
200
|
+
program
|
|
201
|
+
.command('handoff <feature>')
|
|
202
|
+
.description('Generate the compressed session-handoff document (.morph/features/{f}/handoff.md) — pointers, not copies')
|
|
203
|
+
.option('--json', 'Output as JSON')
|
|
204
|
+
.action((feature, options) => handoffCommand(feature, options));
|
|
205
|
+
|
|
198
206
|
// ─── Tasks ────────────────────────────────────────────────────────────────
|
|
199
207
|
program
|
|
200
208
|
.command('dag <feature>')
|
|
@@ -321,6 +329,13 @@ worktreeCommand
|
|
|
321
329
|
.option('--json', 'Output as JSON')
|
|
322
330
|
.action((options) => worktreeListCommand(options));
|
|
323
331
|
|
|
332
|
+
// ─── Fleet ────────────────────────────────────────────────────────────────
|
|
333
|
+
program
|
|
334
|
+
.command('fleet')
|
|
335
|
+
.description('Fleet view — every active feature across primary root and worktrees (phase, gates, tasks, ports, handoff, activity)')
|
|
336
|
+
.option('--json', 'Output as JSON')
|
|
337
|
+
.action((options) => fleetCommand(options));
|
|
338
|
+
|
|
324
339
|
// ─── Dashboard ────────────────────────────────────────────────────────────
|
|
325
340
|
program
|
|
326
341
|
.command('dashboard')
|
package/claude-plugin.json
CHANGED
package/docs/CHEATSHEET.md
CHANGED
|
@@ -99,16 +99,18 @@ morph-spec retype {feature} {newType} # change type mid-fligh
|
|
|
99
99
|
|
|
100
100
|
---
|
|
101
101
|
|
|
102
|
-
## CLI — worktrees (parallel development)
|
|
102
|
+
## CLI — worktrees (parallel development / fleet)
|
|
103
103
|
|
|
104
|
-
One feature = one git worktree = one Claude Code instance. Independent features run side by side without stepping on each other's state; a SessionStart hook injects each worktree's feature identity into context.
|
|
104
|
+
One feature = one git worktree = one Claude Code instance. Independent features run side by side without stepping on each other's state; a SessionStart hook injects each worktree's feature identity, allocated ports and handoff pointer into context. Each worktree gets a deterministic port block (`.morph/worktree.env`) and a regenerable handoff document.
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
107
|
morph-spec create {feature} --worktree # birth the feature already isolated in its own worktree
|
|
108
108
|
morph-spec worktree setup {feature} # create .worktrees/{feature}/ on branch morph/{feature}
|
|
109
109
|
morph-spec worktree link [--all] # relink shared infra (node_modules, .claude, .morph/framework)
|
|
110
|
-
morph-spec worktree finish {feature} # merge --no-ff back to the primary root, clean up (requires Gate 3)
|
|
110
|
+
morph-spec worktree finish {feature} # merge --no-ff back to the primary root, clean up + release ports (requires Gate 3)
|
|
111
111
|
morph-spec worktree list [--json] # active feature worktrees with phase/gate status
|
|
112
|
+
morph-spec handoff {feature} [--json] # regenerate the compressed session-handoff doc (pointers, not copies)
|
|
113
|
+
morph-spec fleet [--json] # fleet view: every active feature (root + worktrees) with phase/gates/ports/handoff/activity
|
|
112
114
|
```
|
|
113
115
|
|
|
114
116
|
---
|
|
@@ -237,4 +239,4 @@ These files are never edited directly. Use CLI commands or `morph-spec update` i
|
|
|
237
239
|
|
|
238
240
|
---
|
|
239
241
|
|
|
240
|
-
*morph-spec v8.1
|
|
242
|
+
*morph-spec v8.2.1 by Polymorphism Tech*
|
package/docs/QUICKSTART.md
CHANGED
package/framework/CLAUDE.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
1. Leia `.morph/framework/MORPH.md` — o charter NLH com papéis, fases, gates e skills.
|
|
8
8
|
2. Leia `.morph/state.json` — índice fino e gitignored (só status + updatedAt por feature) — para identificar a feature `in_progress` e a fase atual. **Não o edite à mão:** é reconstruído a partir dos `feature.json` a cada load. O estado autoritativo e commitado de cada feature vive em `.morph/features/{feature}/feature.json`.
|
|
9
|
-
3.
|
|
9
|
+
3. Se existir `.morph/features/{feature}/handoff.md` para a feature ativa, **leia-o antes de re-derivar contexto à mão** — é o documento de retomada (comprimido, aponta para os artefatos; regenere com `morph-spec handoff <f>` se parecer velho).
|
|
10
|
+
4. Assuma o papel correspondente (Orchestrator/Specialist/Evaluator) e continue o trabalho. Não aguarde instrução do usuário se há trabalho pendente entre gates.
|
|
10
11
|
|
|
11
12
|
Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
12
13
|
|
|
@@ -25,7 +26,7 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
|
25
26
|
- Apresentar gates com o template fixo definido em MORPH.md / morph-plan.
|
|
26
27
|
- Registrar decisões não triviais em `decisions.md`.
|
|
27
28
|
- Aplicar `morph-eval` após cada tarefa; silêncio em score ≥ 9 é o sinal correto. Score < 9 dispara o loop de correção autônomo (re-implementa, sem pausa), não escala ao humano entre tarefas. No Gate 3, o score composto da feature vem do **avaliador independente** despachado pela `morph-review` (persona `evaluator`, contexto fresco) — nunca de auto-avaliação de quem implementou.
|
|
28
|
-
- Incluir as 3 cláusulas do contrato do relatório final em todo dispatch de sub-agent (**
|
|
29
|
+
- Incluir as 3 cláusulas do contrato do relatório final em todo dispatch de sub-agent (**o sub-agent GRAVA o relatório em arquivo canônico ANTES de encerrar — `.morph/features/{f}/3-implement/reports/{taskId}.md`, ou `4-review/evaluator-report.md` para o avaliador — com os 4 itens obrigatórios (o quê / arquivos / problemas / auto-score 0-10); esse arquivo é o valor de retorno autoritativo, a última mensagem de texto é cortesia best-effort e pode não chegar inteira; ainda assim não chamar `SendMessage`, que gera um evento a mais e custa um turno inútil**; não tocar tasks.json/feature.json/board; sem build/test da suíte compartilhada em batch paralelo) — templates em `morph-implement` §4 e `morph-apply` §3b. **O orquestrador lê o arquivo ao retomar controle** — nunca confia só na mensagem de retorno da tool.
|
|
29
30
|
|
|
30
31
|
## Slash commands
|
|
31
32
|
|
|
@@ -44,12 +45,14 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
|
44
45
|
- `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).
|
|
45
46
|
- `morph-spec archive <feature>` — arquiva a feature (atômico, git-aware); é o que `/morph-archive` chama.
|
|
46
47
|
- `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).
|
|
47
|
-
- `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**.
|
|
48
|
+
- `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.
|
|
48
49
|
- `morph-spec gate-decision <feature> <gate> --pause <bool> [--reason <txt>] [--signals <json>]` — 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.
|
|
49
50
|
- `morph-spec create <feature> --type <feature|bug|chore|hotfix> [--request "<txt>"] [--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). `--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`); `--worktree` já nasce a feature no seu worktree próprio.
|
|
50
51
|
- `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.
|
|
51
52
|
- `morph-spec verify <feature> [task] [--json]` — cadeia determinística build+testes+validadores (código antes do juiz LLM); escopo de task quando informado, senão a feature inteira.
|
|
52
|
-
- `morph-spec worktree setup|link|finish|list` — gerencia o ciclo do worktree por feature (`setup` cria `.worktrees/{f}/` na branch `morph/{f}`; `link` religa infra compartilhada; `finish` faz merge `--no-ff
|
|
53
|
+
- `morph-spec worktree setup|link|finish|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; `finish` faz merge `--no-ff`, limpa e libera o bloco; `list` mostra fase/gate de cada worktree ativo).
|
|
54
|
+
- `morph-spec handoff <feature>` — regenera o documento de retomada (`.morph/features/{f}/handoff.md`): comprimido, derivado, aponta para os artefatos em vez de copiá-los. Gerado automaticamente em `setup`/`create --worktree`/`advance`.
|
|
55
|
+
- `morph-spec fleet` — visão da frota: toda feature ativa (raiz + worktrees) com fase, gates pendentes, tasks, portas, handoff e atividade. Use entre sessões para decidir onde atuar.
|
|
53
56
|
|
|
54
57
|
## Skills core (em `.claude/skills/`)
|
|
55
58
|
|
|
@@ -68,7 +71,7 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
|
|
|
68
71
|
|
|
69
72
|
Toda feature nasce com um `workType` (`morph-spec create <f> --type <tipo>` ou classificado de `--request "<pedido>"`) que dimensiona o pipeline: `feature` roda o fluxo completo (proposal → [uiux] → plan → implement → review); `bug` roda uma versão enxuta (proposal/plan lean); `chore` pula direto para implement (Gates 1/2 carimbados por política no nascimento); `hotfix` roda scout → fix cirúrgico → test loop → ship. O Gate 3 do `hotfix` **pausa sempre para um humano**, mesmo em `trust: auto` — o CLI recusa `morph-spec approve <f> review --mode auto` nesse tipo. Detalhes em `.morph/framework/MORPH.md` §2.1 e §5.
|
|
70
73
|
|
|
71
|
-
Features independentes podem rodar em paralelo, cada uma isolada num git worktree (`morph-spec create <f> --worktree` ou `morph-spec worktree setup <f>`) — uma feature por worktree, uma instância de Claude Code por worktree. `feature.json` viaja commitado na branch `morph/{f}`; ao fechar, aprove o Gate 3 dentro do worktree e rode `morph-spec worktree finish <f>` seguido de `morph-spec archive <f>` na raiz. Detalhes em `.morph/framework/MORPH.md` §2.2.
|
|
74
|
+
Features independentes podem rodar em paralelo, cada uma isolada num git worktree (`morph-spec create <f> --worktree` ou `morph-spec worktree setup <f>`) — uma feature por worktree, uma instância de Claude Code por worktree. Cada worktree tem seu **bloco de portas** próprio (`.morph/worktree.env` — suba dev servers SEMPRE nelas, nunca nas portas padrão) e seu **handoff.md** de retomada. `feature.json` viaja commitado na branch `morph/{f}`; ao fechar, aprove o Gate 3 dentro do worktree e rode `morph-spec worktree finish <f>` seguido de `morph-spec archive <f>` na raiz. Visão agregada: `morph-spec fleet`. Detalhes em `.morph/framework/MORPH.md` §2.2.
|
|
72
75
|
|
|
73
76
|
## Personas (em `.claude/agents/`)
|
|
74
77
|
|
package/framework/MORPH.md
CHANGED
|
@@ -112,14 +112,20 @@ Nem todo trabalho merece o pipeline completo. Cada feature nasce com um **workTy
|
|
|
112
112
|
|
|
113
113
|
**Compat:** uma feature sem `workType` (criada antes do v8) é tratada como `feature`. Mudança de tipo mid-flight: `morph-spec retype <f> <tipo>` (desfaz só carimbos de política, preserva aprovações humanas; bloqueado após o Gate 3).
|
|
114
114
|
|
|
115
|
-
### 2.2 Desenvolvimento paralelo por worktree
|
|
115
|
+
### 2.2 Desenvolvimento paralelo por worktree (frota)
|
|
116
116
|
|
|
117
117
|
Features independentes podem ser desenvolvidas **em paralelo, cada uma no seu git worktree**, com uma instância de Claude Code por worktree. `morph-spec create <f> --worktree` (ou `morph-spec worktree setup <f>`) cria `.worktrees/{f}/` na branch `morph/{f}`. Contrato:
|
|
118
118
|
|
|
119
119
|
- **Uma feature = um worktree = uma instância.** Nunca abra duas instâncias na mesma feature (o setup recusa com exit 3).
|
|
120
120
|
- O estado autoritativo (`feature.json`) viaja **commitado na branch** `morph/{f}`. O `state.json` local do worktree é um índice derivável — materializado por self-heal, nunca sincronizado à mão.
|
|
121
121
|
- Infra compartilhada (`node_modules`, `.claude`, `.morph/framework`) é linkada por junction; `memory/` e `logs/` são locais de cada worktree (isolamento correto). Se faltar infra num worktree (aberto com `git worktree add` puro), rode `morph-spec worktree link --all`.
|
|
122
|
-
- **
|
|
122
|
+
- **Ambiente separado (portas):** o setup aloca um **bloco de portas determinístico** por feature e escreve `.morph/worktree.env` no worktree (`PORT`, `MORPH_PORT_RANGE`; `ASPNETCORE_URLS` quando o stack tem dotnet). **Suba dev servers SEMPRE nas portas do bloco** — as portas padrão (3000/5000) pertencem à raiz; duas instâncias no default se matam. O arquivo é local e git-invisível; o hook de SessionStart injeta as portas. Overrides em `config.json → fleet.basePort/blockSize`.
|
|
123
|
+
- **Banco de dados:** worktrees compartilham o mesmo banco por padrão. Para isolamento real com o MCP do Neon disponível, crie um branch de DB `morph/{f}` e anote a connection string no `.morph/worktree.env` (a CLI nunca toca em credenciais); sem Neon, prefixe schema/DB local com o nome da feature. Migration destrutiva num worktree compartilhando banco → integre antes de abrir worktrees irmãos.
|
|
124
|
+
- **Limitação conhecida:** `node_modules` é compartilhado (junction) — mudança de dependência feita num worktree afeta os irmãos. Mudou dependência: conclua e integre (`finish`) antes de seguir com worktrees paralelos.
|
|
125
|
+
- **Handoff entre sessões:** `.morph/features/{f}/handoff.md` é o documento de retomada — comprimido, derivado e regenerável (`morph-spec handoff <f>`), ele **aponta** para os artefatos em vez de copiá-los. É regenerado automaticamente no `worktree setup`, `create --worktree` e `advance`; o hook de SessionStart aponta para ele. **Ao abrir uma sessão numa feature em andamento, leia o handoff antes de re-derivar contexto à mão.** Nunca o edite manualmente (é git-invisível e sobrescrito na próxima regeneração).
|
|
126
|
+
- **Visão da frota:** `morph-spec fleet` agrega raiz + worktrees (fase, gates pendentes, tasks, portas, handoff, último commit, sujeira) — use entre sessões para decidir onde atuar. `morph-spec worktree list` continua sendo a visão enxuta por worktree.
|
|
127
|
+
- **Ownership de arquivos:** não há lock em runtime, por design — a colisão de escrita se resolve **em plan-time** (`morph-spec dag` detecta overlap de `outputs` e recomenda `isolation: "worktree"`) e **por construção** (uma feature = um worktree = um escritor). Entre features, arquivos compartilhados convergem no merge do `finish` — conflito ali é o sinal honesto de escopo sobreposto, não um bug.
|
|
128
|
+
- **Fechamento:** aprove o Gate 3 dentro do worktree, depois `morph-spec worktree finish <f>` (merge `--no-ff` da branch na raiz primária limpa; recusa se worktree ou raiz estiverem sujos; aborta em conflito; libera o bloco de portas) e por fim `morph-spec archive <f>` na raiz.
|
|
123
129
|
|
|
124
130
|
---
|
|
125
131
|
|
|
@@ -263,14 +269,32 @@ Inline (sem dispatch) é aceitável para tasks curtas (effort S/M) **dentro do d
|
|
|
263
269
|
- Contexto de outras tarefas não relacionadas
|
|
264
270
|
- Outputs de ferramentas não pertinentes à tarefa
|
|
265
271
|
|
|
266
|
-
**O
|
|
272
|
+
**O ARQUIVO é o retorno, a última mensagem é cortesia.** Em uso real (feature `config-tenant-split`,
|
|
273
|
+
MORPH_GHLBrain, 2026-07-22 — overnight com dispatch de sub-agents), o evento recebido de volta do
|
|
274
|
+
sub-agent foi só um `idle_notification`, sem o texto — o orquestrador teve que reconstruir o
|
|
275
|
+
relatório do disco e pedir via `SendMessage` que o avaliador o persistisse. A última mensagem de
|
|
276
|
+
texto **pode não chegar** inteira ao orquestrador; por isso ela deixou de ser o canal
|
|
277
|
+
autoritativo. O sub-agent **sempre** grava o relatório também em um arquivo canônico:
|
|
278
|
+
|
|
279
|
+
- Task de implementação: `.morph/features/{feature}/3-implement/reports/{taskId}.md` (um arquivo
|
|
280
|
+
por task concluída, mesmo quando uma única sessão de sub-agent cobre várias tasks de um fluxo).
|
|
281
|
+
- Avaliador do Gate 3 (`morph-evaluator`): `.morph/features/{feature}/4-review/evaluator-report.md`
|
|
282
|
+
— o relatório BRUTO do avaliador; a `morph-review` o lê e sintetiza o `review-report.md`
|
|
283
|
+
apresentado no Gate 3 (os dois nunca colidem: um é saída crua do sub-agent, o outro é a curadoria
|
|
284
|
+
do orquestrador).
|
|
285
|
+
|
|
286
|
+
O orquestrador **lê o arquivo** ao retomar controle após o dispatch — nunca confia apenas na
|
|
287
|
+
mensagem de retorno da tool. Se a mensagem de texto final chegou inteira, ótimo (economiza uma
|
|
288
|
+
leitura); se não chegou, o arquivo já está lá.
|
|
289
|
+
|
|
290
|
+
**O que receber de volta (no arquivo, e na mensagem final quando ela chega):**
|
|
267
291
|
- O que foi feito (2-3 frases)
|
|
268
292
|
- Arquivos modificados (lista)
|
|
269
293
|
- Problemas encontrados e como foram resolvidos
|
|
270
294
|
- Auto-score 0-10 com justificativa em uma linha
|
|
271
295
|
|
|
272
296
|
**Contrato do relatório final** (cláusulas que todo prompt de dispatch inclui — ver templates em `morph-implement` §4 e `morph-apply` §3b):
|
|
273
|
-
1. **
|
|
297
|
+
1. **GRAVE o relatório no arquivo canônico do seu dispatch ANTES de encerrar** — é o valor de retorno autoritativo desta sessão, não a sua última mensagem de texto (que é cortesia best-effort e pode não chegar inteira ao orquestrador). Conteúdo obrigatório, idêntico no arquivo: (1) o que foi feito em 2-3 frases, (2) arquivos modificados, (3) problemas encontrados e como foram resolvidos, (4) auto-score 0-10 com justificativa em uma linha. Relatório ausente, oco, ou sem os 4 itens é **falha de protocolo** — registrada no recap. Ainda assim **não chame `SendMessage`**: ele gera um evento a mais (o teammate-message) além da notificação de término, custando um turno inútil ao orquestrador — a mensagem de texto final (cortesia) já é entregue automaticamente.
|
|
274
298
|
2. Não tocar em `tasks.json`, `feature.json`, `state.json` nem no board de sessão (TaskCreate/TaskUpdate) — todo estado é gravado exclusivamente pelo orquestrador.
|
|
275
299
|
3. **Verificação em batch paralelo — condicional ao isolamento:** em batch **sem isolamento** (agentes na mesma working tree), não rode build/test da suíte compartilhada — a verificação (`morph-spec verify`) é do orquestrador, uma vez, pós-batch. Em batch **com `isolation: "worktree"`** (quando o `morph-spec dag` recomenda, por colisão de `outputs` entre fluxos), a regra se inverte: rode build/test **dentro do seu worktree**, commite o trabalho na branch do worktree como último ato antes do relatório, reporte a branch + status de build, e **nunca** faça merge nem toque na árvore principal — a integração é do orquestrador.
|
|
276
300
|
|
|
@@ -52,7 +52,13 @@ function diffPlanContract(oldDoc, newDoc, mutableFields) {
|
|
|
52
52
|
const violations = [];
|
|
53
53
|
const mutable = new Set(mutableFields);
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// Backfilling a previously-absent `version` (undefined → any value) is a
|
|
56
|
+
// benign schema-tooling update, not a plan-contract change — it's what
|
|
57
|
+
// lets legacy tasks.json adopt the field so `morph-spec dag` can analyze
|
|
58
|
+
// it without a lenient fallback. An actual change between two SET
|
|
59
|
+
// versions is still a real contract change and stays blocked.
|
|
60
|
+
const versionBackfilled = oldDoc.version === undefined && newDoc.version !== undefined;
|
|
61
|
+
if (!versionBackfilled && oldDoc.version !== newDoc.version) {
|
|
56
62
|
violations.push(`'version' changed (${oldDoc.version} → ${newDoc.version})`);
|
|
57
63
|
}
|
|
58
64
|
if (oldDoc.feature !== newDoc.feature) {
|
|
@@ -97,11 +97,54 @@ function featureFromSingleDir(dir) {
|
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Fleet env line: the worktree's allocated port block, read from the manifest
|
|
102
|
+
* `worktree setup` writes. Fail-open — a missing or malformed manifest just
|
|
103
|
+
* omits the line (the session then uses project defaults, which is exactly
|
|
104
|
+
* the pre-fleet behavior).
|
|
105
|
+
*/
|
|
106
|
+
function buildEnvLine(dir) {
|
|
107
|
+
try {
|
|
108
|
+
const envPath = join(dir, '.morph', 'worktree.env');
|
|
109
|
+
if (!existsSync(envPath)) return null;
|
|
110
|
+
const content = readFileSync(envPath, 'utf8');
|
|
111
|
+
const port = content.match(/^PORT=(\d+)$/m)?.[1];
|
|
112
|
+
if (!port) return null;
|
|
113
|
+
const range = content.match(/^MORPH_PORT_RANGE=([\d-]+)$/m)?.[1];
|
|
114
|
+
return `🔌 Ambiente desta worktree: PORT=${port}${range ? ` (bloco ${range})` : ''}. ` +
|
|
115
|
+
`Suba dev servers SEMPRE nessas portas (fonte: .morph/worktree.env) — as portas padrão pertencem à raiz.`;
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fleet handoff line: point the fresh session at the compressed handoff doc
|
|
123
|
+
* before it starts re-deriving context by hand. Fail-open.
|
|
124
|
+
*/
|
|
125
|
+
function buildHandoffLine(dir, feature) {
|
|
126
|
+
try {
|
|
127
|
+
const handoffPath = join(dir, '.morph', 'features', feature, 'handoff.md');
|
|
128
|
+
if (!existsSync(handoffPath)) return null;
|
|
129
|
+
const generatedAt = readFileSync(handoffPath, 'utf8')
|
|
130
|
+
.match(/gerado em (\d{4}-\d{2}-\d{2}T[\d:.]+Z)/)?.[1];
|
|
131
|
+
return `📄 Handoff: .morph/features/${feature}/handoff.md${generatedAt ? ` (gerado ${generatedAt})` : ''} — leia antes de continuar.`;
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
100
137
|
function buildWorktreeContext(dir, feature) {
|
|
101
138
|
const phase = getFeaturePhase(feature, dir);
|
|
102
139
|
let text = `🌳 Worktree desta feature: ${feature} (branch morph/${feature}, fase ${phase}). ` +
|
|
103
140
|
`Trabalhe apenas nesta feature nesta sessão; o estado vive no .morph/ deste worktree.`;
|
|
104
141
|
|
|
142
|
+
const envLine = buildEnvLine(dir);
|
|
143
|
+
if (envLine) text += `\n${envLine}`;
|
|
144
|
+
|
|
145
|
+
const handoffLine = buildHandoffLine(dir, feature);
|
|
146
|
+
if (handoffLine) text += `\n${handoffLine}`;
|
|
147
|
+
|
|
105
148
|
const missingJunctions = !existsSync(join(dir, 'node_modules')) || !existsSync(join(dir, '.claude'));
|
|
106
149
|
if (missingJunctions) {
|
|
107
150
|
text += `\n⚠ Infra compartilhada ausente (node_modules/.claude). Rode: morph-spec worktree link --all`;
|
|
@@ -84,7 +84,14 @@ export function getBuildSteps(stack, { cwd, frontendPath, backendPath } = {}) {
|
|
|
84
84
|
|
|
85
85
|
if (isFrontendStack(s)) {
|
|
86
86
|
const frontendDir = frontendPath ? join(cwd, frontendPath) : cwd;
|
|
87
|
-
|
|
87
|
+
// node_modules guard (same fail-open as post-edit-typecheck.js): a fresh worktree before
|
|
88
|
+
// `morph-spec worktree link` has tsconfig.json (versioned) but no toolchain — running
|
|
89
|
+
// `npx tsc` there fetches an unrelated "tsc" package and fails noisily. No toolchain →
|
|
90
|
+
// no tsc step; the real typecheck runs wherever node_modules exists (root/CI).
|
|
91
|
+
if (
|
|
92
|
+
existsSync(join(frontendDir, 'tsconfig.json')) &&
|
|
93
|
+
existsSync(join(frontendDir, 'node_modules'))
|
|
94
|
+
) {
|
|
88
95
|
steps.push({ command: TSC_BUILD_CMD, cwd: frontendDir });
|
|
89
96
|
}
|
|
90
97
|
}
|
|
@@ -93,7 +93,16 @@ export function getToolName(payload) {
|
|
|
93
93
|
* Build a normalized signature for loop detection.
|
|
94
94
|
* - Edit/Write: "Edit:normalized/file/path"
|
|
95
95
|
* - Bash: "Bash:first two words" (e.g., "Bash:npm test")
|
|
96
|
-
* -
|
|
96
|
+
* - Every other tool WITH input: "Tool:<truncated JSON of tool_input>"
|
|
97
|
+
* - No tool_input at all: just the tool name
|
|
98
|
+
*
|
|
99
|
+
* The general (non-Edit/Write/Bash) case matters: a burst of `Read`/`Skill`/
|
|
100
|
+
* `Grep`/`Agent` calls against DIFFERENT targets (phase-context loading,
|
|
101
|
+
* post-task evaluation reads, parallel subagent dispatch) is normal work, not
|
|
102
|
+
* a loop. Collapsing every one of them to the bare tool name is what made
|
|
103
|
+
* loop-detect fire on exactly that legitimate traffic — a real, reported
|
|
104
|
+
* false positive. Folding in the actual input (rather than hand-listing a
|
|
105
|
+
* target field per tool) fixes it uniformly for every tool at once.
|
|
97
106
|
*
|
|
98
107
|
* @param {Object} payload - Hook payload from stdin
|
|
99
108
|
* @returns {string} Signature string
|
|
@@ -116,5 +125,10 @@ export function getCommandSignature(payload) {
|
|
|
116
125
|
return words ? `Bash:${words}` : 'Bash';
|
|
117
126
|
}
|
|
118
127
|
|
|
119
|
-
|
|
128
|
+
try {
|
|
129
|
+
const json = JSON.stringify(input).replace(/\\/g, '/');
|
|
130
|
+
return json ? `${tool}:${json.slice(0, 200)}` : tool;
|
|
131
|
+
} catch {
|
|
132
|
+
return tool;
|
|
133
|
+
}
|
|
120
134
|
}
|
|
@@ -35,6 +35,7 @@ export const OUTPUT_PHASE_MAP = {
|
|
|
35
35
|
decisions: 'plan',
|
|
36
36
|
recap: 'implement',
|
|
37
37
|
reviewReport: 'review',
|
|
38
|
+
evaluatorReport: 'review',
|
|
38
39
|
};
|
|
39
40
|
|
|
40
41
|
/** Map filename → output type (camelCase) */
|
|
@@ -52,6 +53,7 @@ export const FILENAME_TO_OUTPUT = {
|
|
|
52
53
|
'decisions.md': 'decisions',
|
|
53
54
|
'recap.md': 'recap',
|
|
54
55
|
'review-report.md': 'reviewReport',
|
|
56
|
+
'evaluator-report.md': 'evaluatorReport',
|
|
55
57
|
};
|
|
56
58
|
|
|
57
59
|
/** Protected spec files and the approval gate that locks them */
|
|
@@ -4,7 +4,7 @@ description: Guia a criação de uma landing page nova ponta-a-ponta a partir do
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar um novo projeto de LP com o template Umbraco genérico
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# create-umbraco-lp
|
|
@@ -4,7 +4,7 @@ description: Migra uma landing page existente (React/Vite, Next.js estático ou
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber instrução "use /migrate-lp-to-umbraco {projeto}" do usuário, ou quando o usuário descrever necessidade de migrar uma LP para Umbraco
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# migrate-lp-to-umbraco
|
|
@@ -4,7 +4,7 @@ description: Orquestra a execução autônoma de tasks de uma feature morph-spec
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: após Gate 2 aprovado, ao receber instrução "use /morph-apply" ou "implementar feature"
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-apply
|
|
@@ -135,11 +135,16 @@ dispatch Agent (subagent_type: "morph-{id}") with prompt:
|
|
|
135
135
|
Não modifique spec.md, mandate.md.
|
|
136
136
|
|
|
137
137
|
Contrato do relatório final:
|
|
138
|
-
1.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
138
|
+
1. GRAVE um relatório por task concluída em
|
|
139
|
+
`.morph/features/{feature}/3-implement/reports/{taskId}.md` ANTES de
|
|
140
|
+
encerrar — mesmo quando esta sessão cobre várias tasks do fluxo, um
|
|
141
|
+
arquivo por task. Esse arquivo é o valor de retorno autoritativo; a sua
|
|
142
|
+
última mensagem de texto é cortesia best-effort e pode não chegar
|
|
143
|
+
inteira ao orquestrador (o evento recebido às vezes é só um
|
|
144
|
+
idle_notification, sem texto). Ainda assim NÃO chame SendMessage: ele
|
|
145
|
+
gera um evento a mais além da notificação de término, custando um turno
|
|
146
|
+
inútil ao orquestrador. Relatório ausente, oco, ou sem os itens abaixo,
|
|
147
|
+
é falha de protocolo.
|
|
143
148
|
2. NÃO toque em tasks.json, feature.json, state.json nem no board de sessão
|
|
144
149
|
(TaskCreate/TaskUpdate) — todo estado (status de task, scores, gates) é
|
|
145
150
|
gravado exclusivamente pelo orquestrador.
|
|
@@ -163,7 +168,7 @@ dispatch Agent (subagent_type: "morph-{id}") with prompt:
|
|
|
163
168
|
|
|
164
169
|
> **Alternativa quando os fluxos tocam os mesmos projetos/arquivos** (ex.: o mesmo csproj de testes compartilhado): siga a §3b-w abaixo em vez desta seção — despache com `isolation: "worktree"`, cada agente trabalha numa working tree própria e o orquestrador integra ao final.
|
|
165
170
|
|
|
166
|
-
Aguardar todos os agentes terminarem
|
|
171
|
+
Aguardar todos os agentes terminarem, depois **ler primeiro** `.morph/features/{feature}/3-implement/reports/{taskId}.md` de cada task — é o valor de retorno autoritativo; a mensagem de texto do dispatch, se chegou inteira, é só um atalho que economiza essa leitura. Se um arquivo vier ausente, oco, ou sem os itens da cláusula 1, registre a falha de protocolo no recap. Depois, sequencialmente:
|
|
167
172
|
- **Rodar `morph-spec verify {feature} --json` uma única vez, pós-batch** (escopo feature) — os agentes não a rodaram (cláusula 3). Vermelho: use `verify.feedback` para corrigir/redespachar ANTES de qualquer morph-eval. (Com isolamento por worktree, cada agente já rodou o verify dentro do seu worktree; o orquestrador roda o verify compartilhado UMA vez após o merge — ver §3b-w.)
|
|
168
173
|
- Para cada relatório retornado:
|
|
169
174
|
- Aplicar `Skill(morph-eval)`.
|
|
@@ -177,7 +182,7 @@ Depois do batch paralelo, executar o restante em modo sequencial (passo 3a).
|
|
|
177
182
|
|
|
178
183
|
Quando o `morph-spec dag {feature}` reporta `isolation.recommended: "worktree"` (dois fluxos paralelos planejam escrever no mesmo path — colisão de `outputs`), despache cada fluxo com o parâmetro `isolation: "worktree"` do tool Agent. Cada agente trabalha numa working tree própria; build/test rodam lá dentro sem colidir.
|
|
179
184
|
|
|
180
|
-
1. **Dispatch:** mesmo `subagent_type: "morph-{id}"` e standards de §3b, mais `isolation: "worktree"`. Se a stack for Node e faltar `node_modules` no worktree, o agente roda `morph-spec worktree link` (fail-open) antes de testar; se ainda impossível, reporta "verificação: não executada" (o verify pós-merge cobre). O agente commita na branch do worktree e reporta a branch.
|
|
185
|
+
1. **Dispatch:** mesmo `subagent_type: "morph-{id}"` e standards de §3b, mais `isolation: "worktree"`. Se a stack for Node e faltar `node_modules` no worktree, o agente roda `morph-spec worktree link` (fail-open) antes de testar; se ainda impossível, reporta "verificação: não executada" (o verify pós-merge cobre). Se o agente precisar subir um dev server e existir `.morph/worktree.env` no seu cwd, usa as portas de lá (nunca as padrão — MORPH.md §2.2). O agente commita na branch do worktree e reporta a branch.
|
|
181
186
|
2. **Integração (orquestrador, após todos os relatórios):** faça o merge de cada branch reportada, uma por vez, na ordem de chegada — `git merge --no-ff <branch>`. Conflito → `git merge --abort`, marque a task de volta a `pending` com nota "merge conflict — reexecutar sequencial", siga para a próxima branch. Os paths em conflito são exatamente os de `isolation.collisions` do dag.
|
|
182
187
|
3. **Pós-merge:** rode `morph-spec verify {feature} --json` UMA vez (o merge é o novo ponto de integração — verificação compartilhada obrigatória), depois `morph-eval` + `morph-spec score` por relatório, como em §3b.
|
|
183
188
|
4. **Cleanup:** `git worktree remove` + `git branch -d` das branches integradas + `git worktree prune` (fail-open). Branches não integradas ficam e são registradas no recap com o motivo.
|
|
@@ -4,7 +4,7 @@ description: Entende o negócio, os usuários e a arquitetura antes de qualquer
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber um novo request de feature, antes de morph-plan
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-brainstorming
|
|
@@ -4,7 +4,7 @@ description: Avalia a qualidade de uma tarefa ou fase com rubricas graduadas 0-1
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao completar uma tarefa ou ao finalizar uma fase de implementação
|
|
6
6
|
user-invokable: false
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-eval
|
|
@@ -4,7 +4,7 @@ description: ADW cirúrgico de hotfix — resolve uma queda/bug crítico de prod
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber "/morph-hotfix {feature}", quando a morph-proposal classifica o pedido como workType hotfix (Passo 0), ou o usuário diz "produção caiu / hotfix / urgente"
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-hotfix
|
|
@@ -4,7 +4,7 @@ description: Guia a execução de cada tarefa de implementação com standard e
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar cada task de implementação listada em tasks.json
|
|
6
6
|
user-invokable: false
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-implement
|
|
@@ -59,11 +59,14 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
|
|
|
59
59
|
Done quando: [doneCriteria do tasks.json]
|
|
60
60
|
|
|
61
61
|
Contrato do relatório final:
|
|
62
|
-
1.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
62
|
+
1. GRAVE seu relatório em `.morph/features/{feature}/3-implement/reports/[ID].md`
|
|
63
|
+
ANTES de encerrar — esse arquivo é o valor de retorno autoritativo desta
|
|
64
|
+
sessão. A sua última mensagem de texto é cortesia best-effort: pode não
|
|
65
|
+
chegar inteira ao orquestrador (o evento recebido às vezes é só um
|
|
66
|
+
idle_notification, sem texto). Ainda assim NÃO chame SendMessage: ele
|
|
67
|
+
gera um evento a mais além da notificação de término, custando um turno
|
|
68
|
+
inútil ao orquestrador. Relatório ausente, oco, ou sem os 4 itens abaixo,
|
|
69
|
+
é falha de protocolo.
|
|
67
70
|
2. NÃO toque em tasks.json, feature.json, state.json nem no board de sessão
|
|
68
71
|
(TaskCreate/TaskUpdate) — todo estado é gravado exclusivamente pelo
|
|
69
72
|
orquestrador.
|
|
@@ -74,11 +77,18 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
|
|
|
74
77
|
commite na branch do worktree como último ato antes do relatório
|
|
75
78
|
e reporte a branch — nunca faça merge.
|
|
76
79
|
|
|
77
|
-
Seu relatório
|
|
78
|
-
(
|
|
79
|
-
(4) auto-score 0-10.
|
|
80
|
+
Seu relatório (no arquivo, e na mensagem final quando ela chega) deve conter
|
|
81
|
+
APENAS: (1) o que foi feito em 2-3 frases, (2) arquivos modificados,
|
|
82
|
+
(3) problemas encontrados e como foram resolvidos, (4) auto-score 0-10.
|
|
80
83
|
```
|
|
81
|
-
Após
|
|
84
|
+
Após o retorno do dispatch: **leia primeiro** `.morph/features/{feature}/3-implement/reports/[ID].md`
|
|
85
|
+
— é o valor de retorno autoritativo. Trate a mensagem de texto do sub-agent, se chegou inteira,
|
|
86
|
+
como um atalho que economiza essa leitura — nunca como a única fonte. Arquivo ausente é falha de
|
|
87
|
+
protocolo (registre no recap e re-despache/complete a partir do que o sub-agent já fez, se
|
|
88
|
+
recuperável). O auto-score é **triagem, não aprovação** — se < 7, re-despache com o feedback
|
|
89
|
+
específico (ou corrija inline) antes de prosseguir. Em qualquer caso, a task só avança quando
|
|
90
|
+
`morph-eval` der score ≥ 9 (barra única do MORPH.md §0). Integre o relatório ao recap.md e vá
|
|
91
|
+
para o passo 7.
|
|
82
92
|
|
|
83
93
|
**Se não — siga para o passo 5.**
|
|
84
94
|
|
|
@@ -136,6 +146,8 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
|
|
|
136
146
|
|
|
137
147
|
## Output esperado
|
|
138
148
|
- Código implementado conforme o standard consultado (direto ou via sub-agent)
|
|
149
|
+
- Quando despachada: `.morph/features/{feature}/3-implement/reports/{taskId}.md` — o relatório
|
|
150
|
+
autoritativo do sub-agent, canal durável (passo 4)
|
|
139
151
|
- `.morph/features/{feature}/3-implement/recap.md` atualizado com entrada da task concluída
|
|
140
152
|
- `.morph/features/{feature}/2-plan/tasks.json` com status da task atualizado para `done`
|
|
141
153
|
- `.morph/features/{feature}/2-plan/decisions.md` atualizado se houver decisão não trivial
|
|
@@ -4,7 +4,7 @@ description: Guia a produção dos três artefatos de planejamento de uma featur
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar a fase plan de uma feature, após o Gate 1 (proposal aprovado)
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-plan
|
|
@@ -4,7 +4,7 @@ description: Orquestra a fase de proposta+plano de uma feature morph-spec. Encad
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao receber instrução "use /morph-proposal" do usuário, ou ao iniciar uma nova feature
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-proposal
|
|
@@ -4,7 +4,7 @@ description: Conduz a fase de revisão e produz o review-report.md para o Gate 3
|
|
|
4
4
|
version: core
|
|
5
5
|
when-to-invoke: ao iniciar a fase review, após todas as tasks de tasks.json estarem com status `done`
|
|
6
6
|
user-invokable: true
|
|
7
|
-
cliVersion: "8.1
|
|
7
|
+
cliVersion: "8.2.1"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# morph-review
|
|
@@ -56,18 +56,22 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
|
|
|
56
56
|
seguindo as bandas 0-10 de cada uma.
|
|
57
57
|
NÃO reescreva código. NÃO toque em tasks.json, feature.json nem no board
|
|
58
58
|
de sessão.
|
|
59
|
-
Contrato do relatório final
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
Contrato do relatório final: GRAVE seu relatório em
|
|
60
|
+
`.morph/features/{feature}/4-review/evaluator-report.md` ANTES de
|
|
61
|
+
encerrar — esse arquivo é o valor de retorno autoritativo desta
|
|
62
|
+
sessão. A sua última mensagem de texto é cortesia best-effort: pode
|
|
63
|
+
não chegar inteira ao orquestrador (o evento recebido às vezes é só
|
|
64
|
+
um idle_notification, sem texto). Ainda assim NÃO chame SendMessage:
|
|
65
|
+
ele gera um evento a mais além da notificação de término, custando
|
|
66
|
+
um turno inútil ao orquestrador.
|
|
67
|
+
Seu relatório (no arquivo, e na mensagem final quando ela chega)
|
|
68
|
+
deve conter: score 0-10 por dimensão, score composto (arch×0.30 +
|
|
69
|
+
contracts×0.25 + quality×0.25 + tests×0.20), e feedback priorizado
|
|
70
|
+
(bloqueantes primeiro) para qualquer dimensão < 9. Relatório
|
|
71
|
+
ausente, oco, ou sem esses itens, é falha de protocolo.
|
|
68
72
|
```
|
|
69
73
|
- **Fallback (tipo não registrado):** se `morph-evaluator` não estiver na sua lista de subagent types, despache `general-purpose` colando o campo `persona` do agente `evaluator` (Read em `.morph/framework/agents.json`) no topo do prompt — e **registre o fallback no review-report** (seção de método).
|
|
70
|
-
- Os scores por dimensão e o composto do review-report vêm **desse
|
|
74
|
+
- **Leia primeiro** `.morph/features/{feature}/4-review/evaluator-report.md` — é o valor de retorno autoritativo, não a mensagem de texto do dispatch (que é cortesia, útil só quando chega inteira). Os scores por dimensão e o composto do review-report vêm **desse arquivo**, não da sua auto-avaliação. Se o arquivo vier ausente, oco, ou sem os scores por dimensão, registre a falha de protocolo na seção de método do review-report e re-despache.
|
|
71
75
|
|
|
72
76
|
5. Produza `review-report.md` em `.morph/features/{feature}/4-review/` com:
|
|
73
77
|
- Scores por dimensão (arquitetura, contratos, qualidade, cobertura)
|
|
@@ -117,7 +121,10 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
|
|
|
117
121
|
No caminho manual, o hook state-sync detecta a aprovação em answers de AskUserQuestion e grava `approvalGates.review = { approved: true, timestamp, approvedBy: "manual" }` no `feature.json` autoritativo da feature. Em auto sem AskUserQuestion, aprove via `morph-spec approve {feature} review --mode auto` — nunca via Edit à mão de state.json.
|
|
118
122
|
|
|
119
123
|
## Output esperado
|
|
120
|
-
- `.morph/features/{feature}/4-review/
|
|
124
|
+
- `.morph/features/{feature}/4-review/evaluator-report.md` — relatório bruto e autoritativo do
|
|
125
|
+
`morph-evaluator` despachado no passo 4 (canal durável, ver MORPH.md §6)
|
|
126
|
+
- `.morph/features/{feature}/4-review/review-report.md` — síntese do orquestrador a partir do
|
|
127
|
+
evaluator-report, com scores e recomendação
|
|
121
128
|
- Trace da fase com `phase_summary` preenchido
|
|
122
129
|
- Gate 3 registrado aprovado em `.morph/features/{feature}/feature.json` (autoritativo), na forma canônica `{ approved, timestamp, approvedBy }` — via `morph-spec approve` em auto, ou pelo hook state-sync no caminho manual
|
|
123
130
|
|