@polymorphism-tech/morph-spec 8.21.0 → 8.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +43 -1
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +2 -1
  7. package/framework/MORPH.md +7 -3
  8. package/framework/agents.json +3 -1
  9. package/framework/docs/CLI.md +5 -3
  10. package/framework/hooks/shared/e2e-check.js +0 -0
  11. package/framework/schemas/feature.schema.json +5 -3
  12. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  13. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  14. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  15. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  16. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  17. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  18. package/framework/skills/core/morph-implement/SKILL.md +14 -2
  19. package/framework/skills/core/morph-plan/SKILL.md +27 -4
  20. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  21. package/framework/skills/core/morph-review/SKILL.md +60 -8
  22. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  23. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  24. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  25. package/framework/standards/STANDARDS.json +28 -0
  26. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +5 -2
  27. package/framework/standards/backend/dotnet/testing.md +127 -0
  28. package/framework/templates/infrastructure/docker/docker-compose.template.yml +29 -5
  29. package/package.json +1 -1
  30. package/src/commands/e2e/e2e.js +318 -0
  31. package/src/commands/state/approve.js +11 -0
  32. package/src/commands/state/archive.js +12 -3
  33. package/src/commands/state/finish.js +47 -4
  34. package/src/commands/state/handoff.js +3 -2
  35. package/src/commands/state/usecase.js +77 -0
  36. package/src/commands/validation/validate-feature.js +7 -1
  37. package/src/commands/validation/verify.js +13 -0
  38. package/src/lib/e2e/compose-env.js +74 -0
  39. package/src/lib/generators/handoff-generator.js +2 -2
  40. package/src/lib/specs/usecase-promoter.js +135 -19
  41. package/src/lib/specs/usecase-registry.js +237 -0
  42. package/src/lib/validators/traceability-check.js +198 -0
  43. package/src/lib/validators/verify-runner.js +135 -2
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.21.0
6
+ **Version:** 8.23.0
7
7
  **Requires:** Node.js 22+, Claude Code
8
8
 
9
9
  ---
@@ -368,4 +368,4 @@ Code generated by morph-spec (contracts, templates, implementation output) belon
368
368
 
369
369
  ---
370
370
 
371
- *morph-spec v8.21.0 by [Polymorphism Tech](https://polymorphism.tech)*
371
+ *morph-spec v8.23.0 by [Polymorphism Tech](https://polymorphism.tech)*
package/bin/morph-spec.js CHANGED
@@ -42,6 +42,7 @@ import { retypeCommand } from '../src/commands/state/retype.js';
42
42
  import { handoffCommand } from '../src/commands/state/handoff.js';
43
43
  import { listCommand } from '../src/commands/state/list.js';
44
44
  import { deleteCommand } from '../src/commands/state/delete.js';
45
+ import { usecaseReserveCommand } from '../src/commands/state/usecase.js';
45
46
 
46
47
  // === Tasks ===
47
48
  import { dagCommand } from '../src/commands/tasks/dag.js';
@@ -50,6 +51,7 @@ import { dagCommand } from '../src/commands/tasks/dag.js';
50
51
  import { validateCommand } from './validate.js';
51
52
  import { validateFeatureCommand } from '../src/commands/validation/validate-feature.js';
52
53
  import { verifyCommand } from '../src/commands/validation/verify.js';
54
+ import { e2eUpCommand, e2eStatusCommand, e2eDownCommand } from '../src/commands/e2e/e2e.js';
53
55
 
54
56
  // === Scaffolding ===
55
57
  import { scaffoldDotnetCommand, scaffoldNextjsCommand } from '../src/commands/scaffold/scaffold.js';
@@ -257,6 +259,19 @@ program
257
259
  .option('--json', 'Output as JSON')
258
260
  .action((feature, options) => handoffCommand(feature, options));
259
261
 
262
+ // ─── Use cases ────────────────────────────────────────────────────────────
263
+ const usecaseCommand = program
264
+ .command('usecase')
265
+ .description('Project-wide UC-{nn} numbering (acervo + every worktree of this repo)');
266
+
267
+ usecaseCommand
268
+ .command('reserve')
269
+ .description('Atomically reserve the next N UC-{nn} numbers, shared across every worktree of this repo — replaces the old max+1 disk scan, which collides across worktrees')
270
+ .option('--count <n>', 'How many numbers to reserve', '1')
271
+ .option('--feature <name>', 'Feature to record as the reservation owner (defaults to the sole active feature, if unambiguous) — also what the promotion collision guard compares against')
272
+ .option('--json', 'Output as JSON')
273
+ .action((options) => usecaseReserveCommand(options));
274
+
260
275
  // ─── Tasks ────────────────────────────────────────────────────────────────
261
276
  program
262
277
  .command('dag <feature>')
@@ -293,12 +308,14 @@ program
293
308
 
294
309
  program
295
310
  .command('verify <feature> [task]')
296
- .description('Run the deterministic verification chain (build + tests + validators) — code before the LLM judge')
311
+ .description('Run the deterministic verification chain (build + tests + validators + e2e) — code before the LLM judge')
297
312
  .option('--json', 'Output as JSON')
298
313
  .option('--rubric <id>', 'Force a rubric (default: by task group → config → default)')
299
314
  .option('--timeout <ms>', 'Build timeout in ms (default 120000)', (v) => parseInt(v, 10))
300
315
  .option('--skip-tests', 'Skip the test node')
301
316
  .option('--skip-build', 'Skip the build node')
317
+ .option('--skip-e2e', 'Skip the e2e node (Docker bring-up + Playwright)')
318
+ .option('--fresh-e2e', 'Rebuild the e2e stack instead of reusing a warm one')
302
319
  .option('--no-persist', 'Do not persist the result to feature.json')
303
320
  .action((feature, task, options) =>
304
321
  verifyCommand(feature, task, { ...options, noPersist: options.persist === false })
@@ -351,6 +368,31 @@ contextCommand
351
368
  .option('--json', 'Output scan result as JSON')
352
369
  .action((options) => contextRefreshCommand(options));
353
370
 
371
+ // ─── E2E stack (Docker bring-up for runtime verification) ─────────────────
372
+ const e2eCommand = program
373
+ .command('e2e')
374
+ .description("Lifecycle of the feature's verification stack (docker compose)");
375
+
376
+ e2eCommand
377
+ .command('up <feature>')
378
+ .description('Bring the stack up (reusing a warm one when it still matches the working tree)')
379
+ .option('--fresh', 'Rebuild instead of reusing a warm stack')
380
+ .option('--json', 'Output as JSON')
381
+ .action((feature, options) => e2eUpCommand(feature, options));
382
+
383
+ e2eCommand
384
+ .command('status <feature>')
385
+ .description('Is the stack up, healthy, and built from the current source? Exit 0 only if all three')
386
+ .option('--json', 'Output as JSON')
387
+ .action((feature, options) => e2eStatusCommand(feature, options));
388
+
389
+ e2eCommand
390
+ .command('down <feature>')
391
+ .description('Tear the stack down (best-effort — never blocks)')
392
+ .option('--volumes', 'Also remove named volumes')
393
+ .option('--json', 'Output as JSON')
394
+ .action((feature, options) => e2eDownCommand(feature, options));
395
+
354
396
  // ─── Worktree (optional, isolation) ───────────────────────────────────────
355
397
  const worktreeCommand = program
356
398
  .command('worktree')
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "morph-spec",
3
- "version": "8.21.0",
3
+ "version": "8.23.0",
4
4
  "displayName": "MORPH-SPEC Framework",
5
5
  "description": "Spec-driven development with 38 agents and 8-phase workflow for .NET/Blazor/Next.js/Azure",
6
6
  "publisher": "polymorphism-tech",
@@ -185,4 +185,4 @@ Never edited directly — go through CLI commands or `morph-spec update` instead
185
185
 
186
186
  ---
187
187
 
188
- *morph-spec v8.21.0 by Polymorphism Tech*
188
+ *morph-spec v8.23.0 by Polymorphism Tech*
@@ -249,4 +249,4 @@ morph-spec doctor
249
249
 
250
250
  ---
251
251
 
252
- *morph-spec v8.21.0 by Polymorphism Tech*
252
+ *morph-spec v8.23.0 by Polymorphism Tech*
@@ -65,7 +65,8 @@ Se não houver feature ativa, pergunte ao usuário qual feature trabalhar.
65
65
  | `fleet` | Visão agregada de toda feature ativa (raiz + worktrees) |
66
66
  | `claim [f] [--release] [--list]` | Posse advisory — confirme antes de `finish`/`archive`/`delete` se outra sessão tiver claim vivo |
67
67
  | `dag <f>` | DAG de `tasks.json`: sequencial/paralelo, recomenda isolamento |
68
- | `verify <f> [task] [--json]` | Build+testes+validadores, antes do juiz LLM |
68
+ | `verify <f> [task] [--json]` | Build+testes+validadores+e2e, antes do juiz LLM |
69
+ | `e2e up\|status\|down <f>` | Stack de verificação (docker compose) |
69
70
  | `doctor --worktrees [--prune]` | Reconcilia git × disco × features; só `--prune` remove algo |
70
71
  | `report-check <f> [task] [--json]` | Audita relatório de sub-agent vs. trabalho entregue |
71
72
  | `telemetry [--feature] [--limit]` | Caminho de execução da sessão a partir de `events.jsonl` |
@@ -122,7 +122,7 @@ Features independentes podem ser desenvolvidas **em paralelo, cada uma no seu gi
122
122
  - **Ambiente separado (portas):** o setup aloca um **bloco de portas determinístico** por feature em `.morph/worktree.env` (`PORT`, `MORPH_PORT_RANGE`; `ASPNETCORE_URLS` se dotnet). **Suba dev servers SEMPRE nas portas do bloco** — as portas padrão (3000/5000) pertencem à raiz. Arquivo local, git-invisível; SessionStart injeta as portas. Overrides em `config.json → fleet.basePort/blockSize`.
123
123
  - **Banco de dados:** compartilhado por padrão. Isolamento real (Neon MCP): branch de DB `morph/{f}` + connection string no `.morph/worktree.env`; sem Neon, prefixe schema/DB local pelo nome da feature. Migration destrutiva compartilhando banco → integre antes de abrir worktrees irmãos.
124
124
  - **Limitação conhecida:** `node_modules` compartilhado (junction) — mudança de dependência afeta os irmãos. Mudou dependência: conclua e integre (`finish`) antes de seguir em paralelo.
125
- - **Handoff entre sessões:** `.morph/features/{f}/handoff.md` — documento de retomada comprimido, regenerável (`morph-spec handoff <f>`), **aponta** para os artefatos em vez de copiá-los. Regenerado em `worktree setup`/`create --worktree`/`advance`. **Leia-o antes de re-derivar contexto à mão.** Nunca edite manualmente.
125
+ - **Handoff entre sessões:** `.morph/features/{f}/handoff.md` — documento de retomada comprimido, regenerável (`morph-spec handoff <f>`), **aponta** para os artefatos em vez de copiá-los. Regenerado em `worktree setup`/`create --worktree`/`advance`/`approve`. **Leia-o antes de re-derivar contexto à mão.** Nunca edite manualmente.
126
126
  - **Visão da frota:** `morph-spec fleet` agrega raiz + worktrees (fase, gates, tasks, portas, handoff, último commit, sujeira). `morph-spec worktree list` é a visão enxuta por worktree.
127
127
  - **Ownership de arquivos:** sem lock em runtime — colisão se resolve **em plan-time** (`morph-spec 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
128
  - **Fechamento:** aprove o Gate 3, depois **`morph-spec finish <f> --pr|--merge`** — encerramento único e simétrico. **Arquiva** commitando `features/{f} → archive/{f}` **dentro de `morph/{f}`** e integra: `--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). Worktree desmontado (PROVE-or-DECLINE), portas liberadas; no `--pr` a branch é **preservada**. `morph-spec archive` fica para arquivar-sem-integrar (features legadas, faxina).
@@ -148,7 +148,7 @@ Não invente caminhos alternativos.
148
148
 
149
149
  - `spec.md` — especificação técnica: arquitetura, componentes, integrações, fluxos de dados, edge cases **técnicos**. Responde **como** os use cases serão realizados — não repete o comportamento. Deve bastar para implementar sem perguntas básicas: tecnologia listada sem explicar como os componentes se conectam é um spec.md fraco.
150
150
 
151
- - `mandate.md` — contrato de implementação: stack exata, standards obrigatórios (com links), patterns proibidos, checklist de done verificável. Lido no início de cada task — específico o bastante para resolver ambiguidades sem consultar o Orchestrator.
151
+ - `mandate.md` — contrato de implementação: stack exata, standards obrigatórios (com links), patterns proibidos, **verificação de runtime** (como o stack sobe, que fluxos são exercitados, quais viram spec Playwright, setup de auth) e checklist de done verificável. Lido no início de cada task — específico o bastante para resolver ambiguidades sem consultar o Orchestrator.
152
152
 
153
153
  - `tasks.json` — validada contra `framework/schemas/tasks.schema.json`. Cada task: `id` (T1, T2...), `title`, `description`, `dependencies` (IDs), `effort` (S/M/L/XL), `doneCriteria`, `status` (pending/in_progress/done/skipped/blocked), `outputs` (paths esperados), `usecase` (opcional — **ID** do use case, `UC-07`, nunca o nome do arquivo). `doneCriteria` de task com `usecase` carrega a **pós-condição literal**, com rótulo (`"P2 — cada veterinário traz..."`). Toda entrada do recap.md referencia um ID do tasks.json.
154
154
 
@@ -218,6 +218,8 @@ Gates são pontos de **pausa potencial**. Em `trust: "auto"` (default), auto-pas
218
218
  | `designSystemDivergence` | true | Brainstorming detecta fontes conflitantes (spec antigo vs bundle local) |
219
219
  | `standardsViolations` | array.length > 0 | Mandate ou implementation viola padrões proibidos |
220
220
  | `evalScore` | < 9 | score composto da feature (via morph-review) abaixo do mínimo |
221
+ | `verificationStatus` | === 'fail' | `morph-spec verify` vermelho — build, testes, validadores **ou e2e** (a app não sobe / as specs não passam). Vale em **qualquer** gate |
222
+ | `verificationStale` | true | Stamp do `verify` com sha != HEAD: o verde não cobre mais o código. Só no **ship** (Gate 3) |
221
223
  | `taskCount` | > 20 | Escopo grande — provavelmente vale revisar antes de implementar |
222
224
  | `workType` | === 'hotfix' | Gate 3 pausa SEMPRE (detalhe abaixo, "Gates por tipo de trabalho") |
223
225
 
@@ -225,7 +227,9 @@ Gates são pontos de **pausa potencial**. Em `trust: "auto"` (default), auto-pas
225
227
  | ------ | -------- | -------------------- |
226
228
  | 1 — Proposta | `designSystemDivergence`, `standardsViolations` (improvável) | `morph-brainstorming` |
227
229
  | 2 — Plano | Gate 1 + `taskCount`, `standardsViolations` em mandate | `morph-plan` |
228
- | 3 — Review | `evalScore` composite, `standardsViolations` em implementation | `morph-review` |
230
+ | 3 — Review | `evalScore` composite, `standardsViolations` em implementation, `verificationStale` | `morph-review` |
231
+
232
+ `verificationStatus` não está numa linha só porque vale em todas — verify vermelho pausa qualquer gate.
229
233
 
230
234
  **Gates por tipo de trabalho:** `chore`/`hotfix` pulam Gates 1 e 2 — carimbados por política no nascimento (`approvedBy: "policy:{tipo}"`, nunca `"auto"`, para não colidir com o gate-guard); a barra do morph-eval e o Gate 3 valem para todos os tipos. `hotfix`: Gate 3 **sempre humano** (mesmo em `trust: auto` — `morph-hotfix` pausa via `AskUserQuestion`, o `risk-detector` força, o backstop server-side recusa `approve ... review --mode auto`) e exige `4-review/review-report.md` em vez de `evaluator-report.md`. Demais tipos exigem `evaluator-report.md`, **enforced server-side** (`morph-spec approve {feature} review` recusa exit 1 sem ele; `--force` não dispensa) — ou `--self-assessed` registrado (`approvalGates.review.selfAssessed`, marcado no `morph-spec list`).
231
235
 
@@ -20,6 +20,7 @@
20
20
  "backend-dotnet-result-pattern",
21
21
  "backend-dotnet-vsa-handler-patterns",
22
22
  "backend-dotnet-fluent-validation-vsa",
23
+ "backend-dotnet-testing",
23
24
  "backend-integrations-neon-auth-neon-auth"
24
25
  ],
25
26
  "mcp": ["context7"],
@@ -144,8 +145,9 @@
144
145
  "stack": ["*"],
145
146
  "standards": [],
146
147
  "tools": "Read, Glob, Grep, Bash",
148
+ "mcp": ["playwright"],
147
149
  "model": "opus",
148
- "persona": "Você aplica as rubricas em .morph/framework/evals/rubrics/. Você não participou da implementação — avalia o mandate, o diff e a saída real dos testes, com contexto fresco. O orquestrador anexa o resultado do `morph-spec verify`, mas você tem Bash para conferir por conta própria: rode `morph-spec verify {feature} [task]`, testes/build do stack, `git log/diff/show` — nunca confie cegamente no que foi reportado. Bash é só para verificação: NUNCA para modificar código-fonte, tasks.json, feature.json ou o board de sessão — quem faz isso é o loop de correção, não você. Não reescreve código — produz scores 0-10 por dimensão (architecture, contracts, code-quality, test-coverage), feedback priorizado acionável, e ação clara (aprovado em >=9 / feedback para o loop de correção em <9). Silêncio em scores ≥ 9 é o sinal correto."
150
+ "persona": "Você aplica as rubricas em .morph/framework/evals/rubrics/. Você não participou da implementação — avalia o mandate, o diff e a saída real dos testes, com contexto fresco. O orquestrador anexa o resultado do `morph-spec verify`, mas você tem Bash para conferir por conta própria: rode `morph-spec verify {feature} [task]`, testes/build do stack, `git log/diff/show` — nunca confie cegamente no que foi reportado. Bash é só para verificação: NUNCA para modificar código-fonte, tasks.json, feature.json ou o board de sessão — quem faz isso é o loop de correção, não você. Você também tem Playwright: quando o nó `e2e` do verify subiu o stack, NAVEGUE a aplicação como um usuário faria e percorra os fluxos da feature — ler o diff prova que o código existe, usar a aplicação prova que ela funciona. Confira console e network atrás de erro que nenhum teste cobre. Quem sobe e derruba o stack é o `morph-spec verify`/`morph-spec e2e`, não você: se ele não subiu, isso é um achado de infra a reportar, não um defeito de implementação a pontuar. Se um fluxo que você exercitou merece virar regressão, recomende a spec Playwright no relatório — recomendar é seu papel, escrever não. Não reescreve código — produz scores 0-10 por dimensão (architecture, contracts, code-quality, test-coverage), feedback priorizado acionável, e ação clara (aprovado em >=9 / feedback para o loop de correção em <9). Silêncio em scores ≥ 9 é o sinal correto."
149
151
  },
150
152
  "scout": {
151
153
  "title": "Codebase Scout",
@@ -21,17 +21,19 @@
21
21
 
22
22
  ## Worktree / Fleet
23
23
 
24
- - **`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` 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, `--force` dispensa o Gate 3, `--discard-ignored` consente descartar gitignored no teardown. Substitui o antigo `worktree finish` + `archive` separados.
24
+ - **`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, `--force` dispensa o Gate 3, `--discard-ignored` consente descartar gitignored no teardown. Substitui o antigo `worktree finish` + `archive` separados.
25
+ - **`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.
25
26
  - **`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.
26
27
  - **`morph-spec worktree setup|link|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). O fechamento saiu daqui — use `morph-spec finish` (acima).
27
- - **`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`.
28
+ - **`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`/`approve`.
28
29
  - **`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.
29
30
  - **`morph-spec claim [feature] [--release] [--list] [--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`.
30
31
 
31
32
  ## Validação
32
33
 
33
34
  - **`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.
34
- - **`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.
35
+ - **`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.
36
+ - **`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.
35
37
  - **`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 o único reparo seguro (`git worktree prune` — registros de pastas que já sumiram); apagar pasta que ainda existe é reportado com o comando exato, nunca executado.
36
38
  - **`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. O comando responde a pergunta certa (`work-landed-report-missing` → aceite degradado, NÃO re-despache; `no-work-no-report` → re-despache uma vez). Protocolo completo em MORPH.md §6.
37
39
 
@@ -106,7 +106,8 @@
106
106
  "properties": {
107
107
  "build": { "type": "string", "enum": ["pass", "fail", "skip"] },
108
108
  "tests": { "type": "string", "enum": ["pass", "fail", "skip"] },
109
- "validators": { "type": "string", "enum": ["pass", "fail", "skip"] }
109
+ "validators": { "type": "string", "enum": ["pass", "fail", "skip"] },
110
+ "e2e": { "type": "string", "enum": ["pass", "fail", "skip"], "description": "Docker bring-up + Playwright specs against the running stack. Absent on entries written before the node existed — which is why it is NOT in `required`." }
110
111
  },
111
112
  "additionalProperties": false
112
113
  },
@@ -132,7 +133,8 @@
132
133
  "properties": {
133
134
  "build": { "type": "string", "enum": ["pass", "fail", "skip"] },
134
135
  "tests": { "type": "string", "enum": ["pass", "fail", "skip"] },
135
- "validators": { "type": "string", "enum": ["pass", "fail", "skip"] }
136
+ "validators": { "type": "string", "enum": ["pass", "fail", "skip"] },
137
+ "e2e": { "type": "string", "enum": ["pass", "fail", "skip"], "description": "Docker bring-up + Playwright specs against the running stack. Absent on entries written before the node existed — which is why it is NOT in `required`." }
136
138
  },
137
139
  "additionalProperties": false
138
140
  },
@@ -147,7 +149,7 @@
147
149
  },
148
150
  "verification": {
149
151
  "type": "object",
150
- "description": "The single latest deterministic verify stamp (any scope), written ONLY by `morph-spec verify`. Distinct from `verifications`/`taskVerifications` above (per-scope history with attempts/nodes): this is overwritten by every verify call regardless of scope, carrying just enough to correlate a pass/fail with the exact commit it covers. Primary consumer: `morph-spec gate-check`, which surfaces it as a signal (informational — no shouldPauseGate rule keys on it directly).",
152
+ "description": "The single latest deterministic verify stamp (any scope), written ONLY by `morph-spec verify`. Distinct from `verifications`/`taskVerifications` above (per-scope history with attempts/nodes): this is overwritten by every verify call regardless of scope, carrying just enough to correlate a pass/fail with the exact commit it covers. Primary consumer: `morph-spec gate-check`, which feeds `status` to shouldPauseGate as `verificationStatus` — a red verify pauses the gate (risk-detector.js). The remaining fields (sha/ts/scope, and the derived `verificationStale`) are audit context, not pause rules.",
151
153
  "required": ["status", "sha", "ts", "scope"],
152
154
  "properties": {
153
155
  "status": { "type": "string", "enum": ["pass", "fail"] },
@@ -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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -4,7 +4,7 @@ description: Migra uma landing page existente (React/Vite, Next.js estático ou
4
4
  version: core
5
5
  when-to-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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  stacks:
9
9
  - umbraco
10
10
  ---
@@ -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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -246,7 +246,7 @@ re-apresente o gate**: siga direto para o encerramento (§6) conforme a decisão
246
246
  - `framework/skills/core/morph-review/SKILL.md`
247
247
  - `framework/skills/core/morph-plan/SKILL.md` §5a — resolve e carimba `task.agent`/`task.standards` no plano; fonte primária deste §3b
248
248
  - `morph-spec dag {feature}` — análise de DAG e decisão sequencial vs paralelo (ponderada por effort), incluindo recomendação de isolamento (`isolation.recommended: "worktree"`)
249
- - `morph-spec verify {feature} [task] --json` — cadeia determinística build+testes+validadores (código antes do juiz)
249
+ - `morph-spec verify {feature} [task] --json` — cadeia determinística build+testes+validadores+e2e (código antes do juiz)
250
250
  - `morph-spec score {feature} {task} {score} [--dims a,c,q,t]` — grava taskScores no feature.json com timestamp server-side
251
251
  - `morph-spec gate-check {feature} {gate} [--json]` — default: coleta sinais do disco, decide via `shouldPauseGate`, persiste `gateDecisions`
252
252
  - `morph-spec gate-decision {feature} {gate} --pause {bool} [--reason ...] [--signals ...]` — escape manual: risco que o `gate-check` não vê no disco (shape em `.morph/framework/schemas/feature.schema.json`)
@@ -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.21.0"
7
+ cliVersion: "8.23.0"
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.21.0"
7
+ cliVersion: "8.23.0"
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.21.0"
7
+ cliVersion: "8.23.0"
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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-implement
@@ -129,9 +129,21 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
129
129
  - **`outputs` contém apenas paths realmente produzidos.** Deleções, remoções
130
130
  e anotações (ex.: `"pasta/ (removido: a, b)"`) se registram em `notes`,
131
131
  nunca em `outputs`. E `notes` é **uma string única** — não um array.
132
+ - **`outputs` DEVE listar TODO arquivo criado ou modificado pela task — sem exceção.**
133
+ Isso inclui artefato auxiliar que não estava no plano original (ex.: uma
134
+ factory de design-time, um seed, um arquivo de config gerado a reboque de
135
+ outro output) — se a task o produziu, ele entra em `outputs` junto com o
136
+ resto. Arquivo shipped sem dono em `outputs` de task alguma é falha de
137
+ protocolo: o Gate 3 (avaliador independente da `morph-review`) aponta como
138
+ achado de rastreabilidade, mesmo que o arquivo em si esteja correto — quem
139
+ lê `tasks.json`/`recap.md` depois precisa achar todo arquivo em produção
140
+ citado em algum lugar, e é essa a lacuna que este parágrafo fecha.
132
141
  - **Se os `outputs` reais divergirem dos planejados**, registre o porquê em
133
142
  `notes` na mesma edição — o hook exige a note quando há divergência (e a
134
- mensagem de violação aponta o campo/valor exato do problema).
143
+ mensagem de violação aponta o campo/valor exato do problema). Isto é o
144
+ caso mais comum em que a regra acima se aplica: todo arquivo extra na lista
145
+ de "realmente produzidos" acima do planejado é, por definição, uma
146
+ divergência real vs. planejado — a note explica o porquê.
135
147
  - `tasks.json` é protegido após o Gate 2: o hook `protect-spec-files` faz diff
136
148
  semântico e libera Write ou Edit desde que nenhum campo de contrato do plano
137
149
  (`title`, `description`, `dependencies`, `effort`, `doneCriteria`, `group`)
@@ -4,7 +4,7 @@ description: Guia a produção dos artefatos de planejamento de uma feature (use
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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-plan
@@ -56,7 +56,7 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
56
56
 
57
57
  **A regra que sustenta a camada inteira: pós-condição precisa ser observável em teste.** "O sistema fica consistente" não passa. "A resposta contém todos os veterinários não arquivados" passa. Antes de escrever cada pós-condição, pergunte: *que asserção eu escreveria para verificar isto?* Se não há resposta, a pós-condição está mal formulada — reescreva ou descarte. Sem esse filtro a camada vira prosa e não chega ao `doneCriteria`.
58
58
 
59
- **Identificador sequencial no projeto, não na feature.** Antes de numerar, leia o acervo `.morph/specs/usecases/` (se existir) mais os use cases das features ativas em `.morph/features/*/2-plan/usecases/`; tome o maior `UC-{nn}` e some um. Numerar por feature garante colisão no momento da promoção.
59
+ **Identificador sequencial, reservado — nunca leia o disco à mão.** Rode `morph-spec usecase reserve --count {n} --feature {feature}` (`n` = quantos UCs esta feature vai criar; pode reservar de novo se descobrir mais no meio do plano). Um scan manual do maior `UC-{nn}` só enxerga a ÁRVORE CORRENTE — cada worktree tem a própria cópia de `.morph/features/`, então dois worktrees planejando ao mesmo tempo calculam o mesmo maior número e mintam o MESMO `UC-{nn}` sem saber um do outro; na promoção do `finish`, o segundo a fechar sobrescreve por ID e **apaga o use case do primeiro** — já causou perda de dados em produção. `usecase reserve` aloca atomicamente a partir de um contador compartilhado em `<git-common-dir>/morph-spec/uc-registry.json` (visível a todo worktree do repo, mesmo mecanismo do `claim`/fleet), semeado do maior número já visto no acervo + nas features da árvore corrente + no próprio contador — nunca regride. Fora de um repositório git, cai para o scan local antigo e avisa que o paralelismo entre worktrees não está protegido.
60
60
 
61
61
  **Seções obrigatórias:** `Pré-condições` (o que já é verdade antes), `Cenário principal` (o caminho feliz, numerado, **curto** — não é o lugar para exaustão de casos) e `Pós-condições` (**P1**, **P2**… — o que passa a ser verdade, cada uma testável; é a seção que viaja **literal** ao `doneCriteria`, ver abaixo).
62
62
 
@@ -67,12 +67,14 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
67
67
  | Tipo | Use case |
68
68
  |---|---|
69
69
  | `feature` | Obrigatório quando há comportamento observável. Feature puramente técnica (refactor, infra, migração) dispensa — **registre a dispensa em `decisions.md`**. |
70
- | `bug` | Não cria novo: **copie** o `UC-{nn}-*.md` do acervo para `2-plan/usecases/` e **corrija ou adicione a pós-condição** lá — o bug é a pós-condição que faltava. |
70
+ | `bug` | Não cria novo, não reserva número novo: **copie** o `UC-{nn}-*.md` do acervo para `2-plan/usecases/` **mantendo o nome do arquivo publicado** (mesmo slug) e **corrija ou adicione a pós-condição** lá — o bug é a pós-condição que faltava, não a identidade do UC. É esse mesmo nome, inalterado, que faz o `finish` reconhecer a correção como o MESMO UC (abaixo) em vez de uma colisão. |
71
71
  | `chore` | Nenhum (já pula a fase plan por política). |
72
72
  | `hotfix` | Não bloqueia. Referencia se existir; o pós-mortem pode virar pós-condição numa feature posterior. |
73
73
 
74
74
  **Nunca edite `.morph/specs/usecases/` à mão.** A única via de escrita no acervo é a promoção do `finish`, que lê **só** de `2-plan/usecases/`: é ela que carimba `status: implemented`, sobrescreve por ID (mesmo com slug novo) e leva o arquivo no commit de arquivamento. Editar o acervo direto pula os três e cria uma segunda verdade fora do mecanismo. Mudar um UC existente = copiar para o `2-plan/usecases/` da feature, corrigir lá, e deixar o `finish` sobrescrever.
75
75
 
76
+ **Backstop de colisão na promoção.** Se, apesar da reserva, duas features chegarem à promoção com o MESMO `UC-{nn}` e slugs diferentes — registro de reserva aponta para uma feature diferente da que já está no acervo —, o `finish`/`archive` **recusa promover esse UC específico** (nunca recusa fechar a feature: fail-open, só aquele use case fica de fora) e imprime onde o arquivo ficou e como recuperar. Se acontecer: renumere com `usecase reserve` e promova manualmente, ou — se for mesmo o caso `bug` — confira se o slug bate com o publicado.
77
+
76
78
  O use case fica **editável depois do Gate 2**, por design — é o que permite mudar o comportamento e re-rodar `implement`. A trilha de auditoria fica no git.
77
79
 
78
80
  3. Produza `spec.md` em `.morph/features/{feature}/2-plan/`:
@@ -88,6 +90,27 @@ Ao iniciar a fase plan — após o `proposal.md` ser aprovado e antes de qualque
88
90
  - Patterns proibidos — **copie por extenso no mandate.md** a lista canônica de `MORPH.md` § "Stack baked-in" (`Guid.NewGuid()`→`Guid.CreateVersion7()`, classes não-sealed sem justificativa, exceptions como fluxo de controle, camada de Service/Manager entre handler e dados, repository genérico `IRepository<T>`/`IUnitOfWork`, SQL cru sem justificativa) mais qualquer proibição específica da feature. Copie o texto completo — o mandate.md é lido por implementadores em contexto fresco, sem `MORPH.md` carregado, então um ponteiro sozinho não basta aqui.
89
91
  - Política de acesso a dados (registre no mandate): `ApplicationDbContext` injetado direto no slice — para CRUD e para queries específicas (joins, projeção, paginação). Para concorrência ou SQL específico, store dedicado hand-written com justificativa em `decisions.md`.
90
92
  - **Features com agentes de IA (MAF):** se a feature envolve um agente (Microsoft Agent Framework), **use a tool Read** nos standards relevantes de `framework/standards/ai-agents/*.md` (ex.: `setup`, `agent-session`, `middleware-patterns`, `mcp-server`, `mcp-tools`, `multi-agent-patterns`, `production`) — filtre pelo que a feature exige via `morph-spec standards --find "<tema>" --category ai-agents`. Esses standards carregam as **convenções de projeto** (single-agent por padrão — Workflows só para fan-in/out real; provider, caching, observability, A2A) que uma fonte externa de docs (ex.: context7) não conhece. Padrão de divisão: **superfície da API** pode vir de docs ao vivo/context7 (mais frescas para um SDK em evolução), mas **as convenções vêm dos standards** — registre ambas no mandate. Não pule os `ai-agents/*.md` só porque o STANDARDS.json index lista que eles existem.
93
+ - **Verificação de runtime** — como se PROVA que a feature funciona rodando, não só compilando.
94
+ Compilar não é subir: um serviço injetado sem registro em `Program.cs` passa no build e derruba
95
+ a aplicação no startup. Registre no mandate:
96
+ - **Como o stack sobe:** o `docker compose` do projeto (`morph-spec e2e up {feature}`). Se o
97
+ projeto containeriza mas não tem compose, **isso é uma task** — sem ele o nó `e2e` do verify
98
+ reprova (não dispensa).
99
+ - **O que precisa ser exercitado:** os fluxos desta feature que o avaliador vai navegar, em
100
+ linguagem de comportamento — os mesmos verbos das pós-condições dos use cases.
101
+ - **Quais viram spec Playwright versionada:** caminho crítico e bug corrigido merecem regressão;
102
+ exploração pontual não. O critério vai aqui; escrever a spec é tarefa da implementação.
103
+ - **Base URL vinda do ambiente:** o harness dá a cada feature um bloco de portas próprio (para
104
+ duas worktrees não brigarem pela porta 3000), então o `playwright.config.ts` **não pode**
105
+ cravar a URL — leia `process.env.PLAYWRIGHT_BASE_URL ?? \`http://localhost:${process.env.PORT ?? 3000}\``
106
+ e faça o compose publicar `"${PORT:-3000}:3000"`. Config cravada = suíte batendo em porta
107
+ vazia e `ECONNREFUSED` com cara de app quebrada. Se estiver cravado, **isso é uma task**.
108
+ - **Setup de autenticação**, se a aplicação tem login: `e2e.auth` em `config.json`
109
+ (`mode: seed|storageState|bypass|none`, `seedCommand`, `storageStatePath`, `envFile`), com as
110
+ credenciais num `.env.e2e` **gitignored** — nunca no config commitado. **Se a app tem login e
111
+ isso não existe, crie a task de setup**: sem ela a navegação para na tela de login e a
112
+ verificação de runtime vira teatro. É gap de Gate 2, não dispensa de Gate 3.
113
+ - **Quando não se aplica** (biblioteca pura, chore sem runtime), registre a dispensa e o porquê.
91
114
  - Checklist de done com critérios verificáveis por tarefa.
92
115
 
93
116
  5. Produza `tasks.json` em `.morph/features/{feature}/2-plan/` seguindo o schema em `.morph/framework/schemas/tasks.schema.json`.
@@ -202,7 +225,7 @@ Ao registrar a aprovação do Gate 2, atualize o frontmatter de cada use case de
202
225
  - `morph-spec standards --find "<query>"` — para identificar standards relevantes ao mandate (o índice inteiro só como navegação exploratória; ver `morph-standards`)
203
226
  - `.morph/framework/agents.json` — personas por domínio; lido UMA VEZ no passo 5a para carimbar `agent`/`standards` por task
204
227
  - `.morph/framework/schemas/tasks.schema.json` — schema obrigatório de tasks.json
205
- - `.morph/specs/usecases/` — acervo permanente de use cases do projeto (leia antes de numerar; em `bug`, é onde mora o UC a corrigir)
228
+ - `.morph/specs/usecases/` — acervo permanente de use cases do projeto (`usecase reserve` já lê antes de numerar; em `bug`, é onde mora o UC a corrigir)
206
229
  - `.morph/features/{feature}/0-proposal/proposal.md` — escopo da feature
207
230
  - `.morph/features/{feature}/0-proposal/stack-scan.md` — contexto técnico detectado
208
231
  - `.morph/framework/schemas/tasks.schema.json` — schema e enums de tasks.json
@@ -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.21.0"
7
+ cliVersion: "8.23.0"
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.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-review
@@ -42,20 +42,66 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
42
42
  confirme que usam duas constantes de prefixo nomeadas distintas, nenhuma reusada para o
43
43
  outro propósito. Falha aqui é bloqueante — quebra silenciosa em runtime, não em build.
44
44
 
45
- 4. **Despache o avaliador independente** — o score do Gate 3 nunca é auto-atribuído por quem implementou:
46
- - Rode `morph-spec verify {feature} --json` (escopo feature) e capture o JSON — é a saída real de build+testes+validadores que você anexa ao prompt do avaliador independente (`morph-evaluator`).
45
+ 4. **Antes de despachar, verifique se já existe um relatório íntegro** — inclusive em sessão
46
+ retomada. **Esta verificação NÃO é pulada porque a sessão foi retomada** — é exatamente esse o
47
+ cenário que já mordeu em produção: 4 sessões retomadas re-despacharam o avaliador do Gate 3 sem
48
+ nunca ler o `evaluator-report.md` que já existia, íntegro, desde o 1º dispatch (`APPROVED`, score
49
+ 9.3) — e a 4ª sessão chegou a afirmar, falsamente, que "o avaliador nunca rodou". `SessionStart:
50
+ resume`/`init` não reseta este passo: ele roda sempre, a cada retomada de controle, antes de
51
+ cogitar um dispatch.
52
+ - Rode `morph-spec report-check {feature} --json` (sem `task`, modo feature inteira) e leia o
53
+ campo `evaluator` da resposta — ou, equivalente, leia `4-review/evaluator-report.md` direto e
54
+ confira: scores por dimensão presentes + a linha `**Composite-Score:**` presente = **íntegro**.
55
+ - **Se íntegro** (`evaluator.exists && evaluator.complete`): **NÃO despache de novo.** Pule direto
56
+ para "**Leia primeiro**" logo abaixo — leia o arquivo e siga para o passo 5 (síntese do
57
+ review-report a partir dele). Re-despachar aqui joga fora um veredito de Gate 3 já válido.
58
+ - **Se ausente, oco, ou sem scores por dimensão:** este é exatamente o caso de falha de protocolo
59
+ que este passo trata — prossiga com o dispatch abaixo.
60
+
61
+ **Despache o avaliador independente** — o score do Gate 3 nunca é auto-atribuído por quem implementou:
62
+ - Rode `morph-spec verify {feature} --json` (escopo feature) e capture o JSON — é a saída real de build+testes+validadores+e2e que você anexa ao prompt do avaliador independente (`morph-evaluator`).
63
+ - **Confira o nó `e2e` do verify antes de despachar.** Ele carrega o estado do stack:
64
+ - `pass` → a aplicação subiu e está saudável; o avaliador pode navegá-la. **Passe a URL no prompt**
65
+ — cada feature roda no seu bloco de portas, não em `localhost:3000`; pegue de
66
+ `morph-spec e2e status {feature} --json` (`baseUrl`) e não deixe o avaliador adivinhar.
67
+ - `fail` → **não abra o Gate 3.** Volte ao loop de correção: a app não sobe, ou as specs quebraram.
68
+ - `skip` → leia a razão e decida se a dispensa é legítima. "não containeriza" num projeto sem UI é
69
+ legítimo; "docker indisponível no ambiente" numa feature de UI **não** é — registre você mesmo
70
+ `morph-spec gate-decision {feature} review --pause true --reason "e2e não verificado: {razão}"`.
71
+ O disco não distingue dispensa legítima de verificação que faltou; esse julgamento é seu.
72
+ - **Confira também `traceability.warnings`** (advisory, nunca faz `verify` falhar): cada entrada é
73
+ um arquivo citado no `recap.md` sem task dona em `outputs` — repasse ao avaliador como sinal de
74
+ rastreabilidade para a dimensão de qualidade, não como bloqueio automático do gate.
47
75
  - Gere o **diff da implementação** (`git diff` do range da feature, ou a lista de arquivos do recap.md com seus conteúdos atuais).
48
- - Despache o subagent registrado `morph-evaluator` (gerado de `agents.json` — a persona do avaliador é o system prompt dele, com Read/Glob/Grep + Bash somente para verificação, sem Write/Edit), com contexto fresco:
76
+ - Despache o subagent registrado `morph-evaluator` (gerado de `agents.json` — a persona do avaliador é o system prompt dele, com Read/Glob/Grep + Bash somente para verificação + Playwright para navegar, sem Write/Edit), com contexto fresco:
49
77
  ```
50
78
  dispatch Agent (subagent_type: "morph-evaluator") with prompt:
51
79
  contexto: mandate.md + spec.md + diff da implementação + saída real dos testes
80
+ + nó e2e do verify (stack no ar? em qual URL?)
52
81
  instrução: |
53
82
  Você NÃO participou da implementação — avalie o que está no diff,
54
- contra o mandate. Você tem Bash: use `morph-spec verify`, testes/build
83
+ contra o mandate. NÃO leia `4-review/evaluator-report.md` neste
84
+ caminho antes de escrever o seu: se esse arquivo já existir quando
85
+ você começar, é resíduo de uma tentativa anterior — NÃO é insumo,
86
+ é o SEU caminho de escrita, não de leitura. Sua nota nasce do
87
+ mandate + spec + diff + testes, nunca do veredito de outro
88
+ avaliador; ler o relatório de um antecessor antes de pontuar é
89
+ ancoragem — exatamente o que "contexto fresco" existe para evitar.
90
+ Você tem Bash: use `morph-spec verify`, testes/build
55
91
  do stack e `git log/diff/show` para conferir por conta própria o que
56
92
  foi reportado, em vez de confiar cegamente. Aplique as rubricas em
57
93
  .morph/framework/evals/rubrics/ (architecture 30%, contracts 25%,
58
94
  code-quality 25%, test-coverage 20%) seguindo as bandas 0-10 de cada uma.
95
+ Se o nó e2e subiu o stack, NAVEGUE a aplicação com Playwright e
96
+ percorra os fluxos desta feature como um usuário faria — ler o diff
97
+ prova que o código existe, usar a aplicação prova que ela funciona.
98
+ Confira console e network atrás de erro que teste nenhum cobriu.
99
+ Registre no relatório o que você navegou e o que viu. Se um fluxo que
100
+ você exercitou merece virar regressão, RECOMENDE a spec Playwright —
101
+ recomendar é seu papel, escrever a spec não é.
102
+ NÃO suba nem derrube o stack por conta própria (`morph-spec e2e` e o
103
+ nó de verify fazem isso): stack que não sobe é achado de INFRA a
104
+ reportar, não defeito de implementação a pontuar.
59
105
  NÃO reescreva código. NÃO toque em tasks.json, feature.json nem no board
60
106
  de sessão — Bash é só para verificação, nunca para mudar estado.
61
107
  Contrato do relatório final: GRAVE seu relatório em
@@ -71,8 +117,9 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
71
117
  contracts×0.25 + quality×0.25 + tests×0.20) gravado TAMBÉM como a
72
118
  linha literal `**Composite-Score:** {n.n}` (machine-readable — o
73
119
  `morph-spec gate-check` lê o score real daí em vez do proxy de
74
- taskScores), e feedback priorizado (bloqueantes primeiro) para
75
- qualquer dimensão < 9. Relatório ausente, oco, ou sem esses itens,
120
+ taskScores), feedback priorizado (bloqueantes primeiro) para
121
+ qualquer dimensão < 9, e — quando o stack estava no ar — o que você
122
+ navegou e o que observou. Relatório ausente, oco, ou sem esses itens,
76
123
  é falha de protocolo.
77
124
  ```
78
125
  - **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).
@@ -87,6 +134,10 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
87
134
  uma regra de negócio, e ainda dizendo `976/976` quando eram 982).
88
135
  - Scores por dimensão (arquitetura, contratos, qualidade, cobertura)
89
136
  - Score composto final
137
+ - **Verificação de runtime** — o resultado do nó `e2e` (`pass`/`fail`/`skip` + razão), e o que o
138
+ avaliador navegou. Dispensa (`skip`) entra aqui **por extenso**: o valor de um gate está tanto
139
+ no que ele provou quanto no que ele deixou de provar, e uma dispensa silenciosa se lê como
140
+ aprovação. Se houver spec Playwright recomendada pelo avaliador, liste-a como melhoria.
90
141
  - Lista de itens bloqueantes (se houver) — com descrição e critério de resolução
91
142
  - Lista de melhorias recomendadas (não bloqueantes)
92
143
  - Recomendação final: Aprovado / Rejeitar com condições / Reprovar
@@ -130,6 +181,7 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
130
181
 
131
182
  **Composite score:** {score}/10
132
183
  **Dimensões:** arquitetura={X} contratos={Y} qualidade={Z} cobertura={W}
184
+ **Runtime:** e2e={pass|skip} {razão, quando skip}
133
185
  **Bloqueantes:** {N (zero esperado em auto-aprovação)}
134
186
  **Recomendação:** Aprovado
135
187
  ```
@@ -147,7 +199,7 @@ e o `recap.md` estar atualizado. Normalmente invocada pela skill orquestradora `
147
199
  - 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
148
200
 
149
201
  ## Referências
150
- - `morph-spec verify {feature} [task] --json` — cadeia determinística build+testes+validadores anexada ao prompt do avaliador (passo 4)
202
+ - `morph-spec verify {feature} [task] --json` — cadeia determinística build+testes+validadores+e2e anexada ao prompt do avaliador (passo 4)
151
203
  - `.morph/framework/MORPH.md` — seção 5 (Gate 3 + regras de pausa), seção 1 (papel Evaluator), Trust Mode
152
204
  - `.claude/agents/morph-evaluator.md` — subagent do avaliador (gerado de `.morph/framework/agents.json`), despachado no passo 4
153
205
  - `framework/skills/core/morph-eval/SKILL.md` — rubricas e bandas que o avaliador aplica
@@ -4,7 +4,7 @@ description: Varre o codebase existente, detecta stack atual, compara com o stac
4
4
  version: core
5
5
  when-to-invoke: ao iniciar uma feature em projeto brownfield (codebase já existente)
6
6
  user-invokable: true
7
- cliVersion: "8.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-stack-scan
@@ -4,7 +4,7 @@ description: Localiza um standard específico via busca server-side (morph-spec
4
4
  version: core
5
5
  when-to-invoke: quando precisar consultar um standard específico durante a implementação
6
6
  user-invokable: true
7
- cliVersion: "8.21.0"
7
+ cliVersion: "8.23.0"
8
8
  ---
9
9
 
10
10
  # morph-standards