@polymorphism-tech/morph-spec 7.6.0 → 7.7.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.
Files changed (43) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +18 -0
  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 +12 -4
  7. package/framework/MORPH.md +18 -10
  8. package/framework/agents.json +12 -4
  9. package/framework/hooks/README.md +8 -10
  10. package/framework/hooks/claude-code/core/gate-guard.js +36 -16
  11. package/framework/hooks/claude-code/core/protect-spec-files.js +20 -5
  12. package/framework/hooks/claude-code/core/tasks-json-guard.js +81 -0
  13. package/framework/hooks/shared/tasks-validator.js +68 -0
  14. package/framework/mcp/registry.json +5 -48
  15. package/framework/rubrics/backend.json +3 -3
  16. package/framework/rubrics/default.json +3 -3
  17. package/framework/rubrics/frontend.json +3 -3
  18. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  19. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  20. package/framework/skills/core/morph-apply/SKILL.md +53 -40
  21. package/framework/skills/core/morph-brainstorming/SKILL.md +7 -12
  22. package/framework/skills/core/morph-eval/SKILL.md +10 -8
  23. package/framework/skills/core/morph-implement/SKILL.md +27 -21
  24. package/framework/skills/core/morph-plan/SKILL.md +12 -21
  25. package/framework/skills/core/morph-proposal/SKILL.md +3 -8
  26. package/framework/skills/core/morph-review/SKILL.md +30 -14
  27. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  28. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  29. package/framework/standards/integration/mcp/mcp-tools.md +38 -43
  30. package/package.json +1 -1
  31. package/src/commands/project/doctor.js +36 -1
  32. package/src/commands/project/update.js +26 -2
  33. package/src/commands/state/approve.js +29 -4
  34. package/src/commands/state/score.js +88 -0
  35. package/src/commands/tasks/dag.js +68 -0
  36. package/src/dashboard/scanner/rubrics-scanner.js +1 -1
  37. package/src/lib/generators/recap-generator.js +1 -1
  38. package/src/lib/tasks/dispatch-planner.js +201 -0
  39. package/src/lib/validators/score-aggregator.js +3 -3
  40. package/src/scripts/setup-infra.js +43 -0
  41. package/src/utils/agents-installer.js +142 -10
  42. package/src/utils/hooks-installer.js +13 -5
  43. package/framework/rubrics/eval-rubric.json +0 -66
@@ -0,0 +1,68 @@
1
+ /**
2
+ * tasks-validator.js — minimal hook-side schema validation for tasks.json.
3
+ *
4
+ * Mirrors VALID_STATUS / VALID_EFFORT from src/lib/tasks/tasks-json-parser.js.
5
+ * Hooks are installed standalone into target projects (.morph/framework/hooks/),
6
+ * so they cannot import from src/ — the enum lives here, duplicated on purpose
7
+ * and covered by tests on both sides.
8
+ */
9
+
10
+ export const VALID_TASK_STATUS = ['pending', 'in_progress', 'done', 'skipped', 'blocked'];
11
+ export const VALID_TASK_EFFORT = ['S', 'M', 'L', 'XL'];
12
+
13
+ /**
14
+ * Validate the parts of a tasks.json document that hooks care about:
15
+ * a tasks[] array whose entries carry a valid status (and effort, when set).
16
+ * The production failure this guards: a leaf agent writing status "completed"
17
+ * (not in the enum) without any validation.
18
+ *
19
+ * @param {object} doc - parsed tasks.json
20
+ * @returns {{ valid: boolean, errors: string[] }}
21
+ */
22
+ export function validateTasksDoc(doc) {
23
+ if (!doc || typeof doc !== 'object' || !Array.isArray(doc.tasks)) {
24
+ return { valid: false, errors: ['tasks.json must be an object with a tasks[] array'] };
25
+ }
26
+
27
+ const errors = [];
28
+ for (const task of doc.tasks) {
29
+ const id = task?.id ?? '?';
30
+ if (!VALID_TASK_STATUS.includes(task?.status)) {
31
+ errors.push(`task '${id}': invalid status '${task?.status}' (valid: ${VALID_TASK_STATUS.join(', ')})`);
32
+ }
33
+ if (task?.effort !== undefined && !VALID_TASK_EFFORT.includes(task.effort)) {
34
+ errors.push(`task '${id}': invalid effort '${task.effort}' (valid: ${VALID_TASK_EFFORT.join(', ')})`);
35
+ }
36
+ }
37
+ return { valid: errors.length === 0, errors };
38
+ }
39
+
40
+ /**
41
+ * Planned-vs-real outputs divergence: tasks whose `outputs` changed relative
42
+ * to the previous document but whose `notes` is still empty. The divergence
43
+ * itself is legitimate (outputs is a mutable progress field) — shipping it
44
+ * without an explanatory note is what gets flagged.
45
+ *
46
+ * @param {object} oldDoc - parsed previous tasks.json
47
+ * @param {object} newDoc - parsed proposed tasks.json
48
+ * @returns {string[]} human-readable violation messages (empty = OK)
49
+ */
50
+ export function outputsDivergenceViolations(oldDoc, newDoc) {
51
+ const violations = [];
52
+ const oldById = new Map((oldDoc?.tasks ?? []).map(t => [t.id, t]));
53
+
54
+ for (const task of newDoc?.tasks ?? []) {
55
+ const old = oldById.get(task.id);
56
+ if (!old) continue;
57
+ const outputsChanged =
58
+ JSON.stringify(old.outputs ?? []) !== JSON.stringify(task.outputs ?? []);
59
+ const hasNote = typeof task.notes === 'string' && task.notes.trim().length > 0;
60
+ if (outputsChanged && !hasNote) {
61
+ violations.push(
62
+ `task '${task.id}': outputs diverged from the plan but 'notes' is empty — ` +
63
+ `record why in 'notes' in the same edit`
64
+ );
65
+ }
66
+ }
67
+ return violations;
68
+ }
@@ -58,50 +58,6 @@
58
58
  "setupGuide": null
59
59
  }
60
60
  },
61
- "github": {
62
- "phases": ["tasks", "implement"],
63
- "tools": ["get_repo", "search_code", "create_issue", "create_pull_request", "search_issues"],
64
- "detection": "mcpServers includes github",
65
- "fallback": "Bash `gh` CLI commands",
66
- "usage": "Repo metadata, code search, issue creation, PR management",
67
- "healthCheck": {
68
- "toolPattern": "github",
69
- "testCall": "Call any read-only GitHub tool (e.g., search_repositories or get_repo) to verify connectivity",
70
- "testParams": {},
71
- "successIndicator": "Returns repository data or search results without auth errors",
72
- "failureHint": "GitHub MCP not responding. If using Docker, ensure Docker Desktop is running. Verify GITHUB_PERSONAL_ACCESS_TOKEN env var is set. Ensure the token has not expired and has the required scopes (repo, read:org)."
73
- },
74
- "install": {
75
- "autoInstall": false,
76
- "transport": "stdio",
77
- "config": {
78
- "type": "stdio",
79
- "command": "docker",
80
- "args": ["run", "-i", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "ghcr.io/github/github-mcp-server"],
81
- "env": {
82
- "GITHUB_PERSONAL_ACCESS_TOKEN": ""
83
- }
84
- },
85
- "credentials": [
86
- {
87
- "name": "GitHub Personal Access Token",
88
- "envVar": "GITHUB_PERSONAL_ACCESS_TOKEN",
89
- "secret": true,
90
- "helpUrl": "https://github.com/settings/tokens"
91
- }
92
- ],
93
- "prerequisites": [
94
- {
95
- "name": "Docker",
96
- "checkCommand": "docker --version",
97
- "installUrl": "https://docs.docker.com/get-docker/"
98
- }
99
- ],
100
- "stackRelevance": ["*"],
101
- "warnings": [],
102
- "setupGuide": "Official GitHub MCP Server (github/github-mcp-server). Requires Docker."
103
- }
104
- },
105
61
  "vercel": {
106
62
  "phases": ["implement"],
107
63
  "tools": ["search_docs", "list_projects", "get_deployments", "get_deployment_logs", "manage_env_vars"],
@@ -234,7 +190,8 @@
234
190
  {
235
191
  "name": "Gemini API Key",
236
192
  "envVar": "GEMINI_API_KEY",
237
- "source": "Google AI Studio (https://aistudio.google.com/apikey)"
193
+ "secret": true,
194
+ "helpUrl": "https://aistudio.google.com/apikey"
238
195
  }
239
196
  ],
240
197
  "prerequisites": ["Google AI Studio API key"],
@@ -247,8 +204,8 @@
247
204
  "phaseMatrix": {
248
205
  "proposal": ["context7"],
249
206
  "uiux": ["playwright", "context7", "stitch", "gemini-image"],
250
- "design": ["context7", "github"],
251
- "tasks": ["context7", "github"],
252
- "implement": ["context7", "playwright", "github", "vercel", "gemini-image"]
207
+ "design": ["context7"],
208
+ "tasks": ["context7"],
209
+ "implement": ["context7", "playwright", "vercel", "gemini-image"]
253
210
  }
254
211
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "backend",
3
- "version": "1.0.0",
4
- "description": "Backend-focused rubric. Higher weight on architecture, security, and package health.",
3
+ "version": "1.1.0",
4
+ "description": "Backend-focused rubric. Higher weight on architecture, security, and package health. Deterministic-validator rubric — approve threshold aligned to the single bar 9 from MORPH.md §0.",
5
5
  "categories": {
6
6
  "functionality": {
7
7
  "weight": 0.25,
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "thresholds": {
38
- "approve": 8.0,
38
+ "approve": 9.0,
39
39
  "retry": 6.0
40
40
  }
41
41
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "default",
3
- "version": "1.0.0",
4
- "description": "Default rubric for general-purpose projects. Balanced weights across all categories.",
3
+ "version": "1.1.0",
4
+ "description": "Default rubric for general-purpose projects. Balanced weights across all categories. Deterministic-validator rubric — approve threshold aligned to the single bar 9 from MORPH.md §0 (the LLM eval rubrics live in framework/evals/rubrics/*.md).",
5
5
  "categories": {
6
6
  "functionality": {
7
7
  "weight": 0.30,
@@ -30,7 +30,7 @@
30
30
  }
31
31
  },
32
32
  "thresholds": {
33
- "approve": 8.0,
33
+ "approve": 9.0,
34
34
  "retry": 6.0
35
35
  }
36
36
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "frontend",
3
- "version": "1.0.0",
4
- "description": "Frontend-focused rubric. Higher weight on design system compliance and UI quality.",
3
+ "version": "1.1.0",
4
+ "description": "Frontend-focused rubric. Higher weight on design system compliance and UI quality. Deterministic-validator rubric — approve threshold aligned to the single bar 9 from MORPH.md §0.",
5
5
  "categories": {
6
6
  "functionality": {
7
7
  "weight": 0.25,
@@ -30,7 +30,7 @@
30
30
  }
31
31
  },
32
32
  "thresholds": {
33
- "approve": 8.0,
33
+ "approve": 9.0,
34
34
  "retry": 6.0
35
35
  }
36
36
  }
@@ -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: "7.6.0"
7
+ cliVersion: "7.7.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: "7.6.0"
7
+ cliVersion: "7.7.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: "7.6.0"
7
+ cliVersion: "7.7.1"
8
8
  ---
9
9
 
10
10
  # morph-apply
@@ -33,34 +33,29 @@ Executar todas as tasks pendentes de uma feature até `done`, aplicando `morph-e
33
33
  ### 1. Carregar contexto
34
34
 
35
35
  - Ler `.morph/config/config.json → trust`.
36
- - Parsear `tasks.json` via `src/lib/tasks/tasks-json-parser.js`:
37
- ```js
38
- import { parseTasksJson, getReadyTasks, getPendingTasks, updateTaskStatus, writeTasksJson, groupTasks } from '../../../src/lib/tasks/tasks-json-parser.js';
39
- const data = await parseTasksJson('.morph/features/{feature}/2-plan/tasks.json');
40
- ```
36
+ - **Use a tool Read** em `.morph/features/{feature}/2-plan/tasks.json` para carregar as tasks (ids, `dependencies`, `effort`, `group`, `status`, `doneCriteria`). Não escreva scripts para isso — o parse e a validação de schema acontecem no comando CLI do passo 2.
41
37
 
42
38
  ### 2. Análise de DAG — decisão sequencial vs paralelo
43
39
 
44
- Construir o grafo de dependências e detectar fluxos de trabalho independentes. Há duas formas de paralelismo a detectar — e a segunda é a que mais aparece na prática:
40
+ Rode o comando CLI (ele parseia o tasks.json, valida o schema e pondera o esforço):
45
41
 
46
- ```js
47
- const groups = groupTasks(data); // { backend: [ids], frontend: [ids], tests: [...], infra: [...], docs: [...] }
48
- const independentRoots = data.tasks.filter(t => t.dependencies.length === 0);
42
+ ```
43
+ morph-spec dag {feature}
49
44
  ```
50
45
 
51
- **Regras de decisão:**
46
+ O comando analisa `dependencies`, `group` e `effort` das tasks pendentes e imprime a decisão pronta com justificativa. As regras que ele aplica (e que você segue):
52
47
 
53
- 1. Se `data.tasks.length <= 3`: **sequencial** (overhead de Agent dispatch não compensa).
54
- 2. **Fluxos de domínio independentes (o caso comum — backend ‖ frontend).** Use `groupTasks(data)` para separar as tasks por `group`. Dois grupos formam fluxos paralelos quando **nenhuma task de um grupo depende de uma task de outro grupo** (as cadeias só convergem, se convergirem, numa task final de integração). Se existem **≥ 2 fluxos de domínio independentes** — ex: a cadeia `backend` `T1→…→T11` e a cadeia `frontend` `T12→T13` não se cruzam até `T14` —, dispache **um sub-agent por fluxo, em paralelo**, cada um com a persona e o standard do seu domínio (ver passo 3b). Esse é exatamente o caso que a regra antiga de "≥3 raízes independentes" deixava passar (duas cadeias contam como 2 raízes, não 3).
55
- 3. **Raízes independentes (fan-out na largura).** Identificar tasks **100% independentes** (`dependencies: []`). Se `independentRoots.length >= 3`: dispachar essas raízes em **paralelo**, depois cair em sequencial para o resto.
48
+ 1. Se `tasks.length <= 3`: **sequencial** (overhead de Agent dispatch não compensa).
49
+ 2. **Fluxos de domínio independentes (o caso comum — backend ‖ frontend).** Grupos cujas tasks não dependem de tasks de outro grupo formam fluxos paralelos (as cadeias só convergem, se convergirem, numa task final de integração). **≥ 2 fluxos independentes com peso de esforço suficiente** → dispache **um sub-agent por fluxo, em paralelo**, cada um com a persona e o standard do seu domínio (ver passo 3b).
50
+ 3. **Raízes independentes (fan-out na largura).** Tasks 100% independentes (`dependencies: []`). **≥ 3 raízes com effort M ou maior** → dispachar essas raízes em **paralelo**, depois cair em sequencial para o resto.
56
51
  4. Caso contrário: **sequencial puro** com auto-advance.
57
52
 
58
- > O cap de **4 agentes simultâneos** (anti-padrão abaixo) vale para ambas as formas. Se um fluxo de domínio é uma cadeia longa, despache o **fluxo inteiro** a um sub-agent (que executa a cadeia em ordem internamente) — um agente por fluxo, não um agente por task.
53
+ > **Peso de esforço:** a decisão é ponderada por `effort` (S=1, M=2, L=4, XL=8). **Raízes e fluxos compostos só de tasks S não ganham dispatch próprio** — overhead > trabalho. O comando os lista como `inline` (você os executa no modo sequencial) ou os agrupa num **único** agente quando são muitos. O cap de **4 agentes simultâneos** (anti-padrão abaixo) vale para ambas as formas. Se um fluxo de domínio é uma cadeia longa, despache o **fluxo inteiro** a um sub-agent (que executa a cadeia em ordem internamente) — um agente por fluxo, não um agente por task.
59
54
 
60
- Logar a decisão (mostre qual forma disparou):
55
+ Registre a linha de decisão que o comando imprimiu, por exemplo:
61
56
  ```
62
- DAG analysis: 14 tasks, groups {backend:9, frontend:2, tests:2, infra:1}; backend ‖ frontend independentes até T14 → 2 fluxos paralelos (1 agente backend, 1 agente frontend) + T14 sequencial no fim
63
- DAG analysis: 12 tasks, 4 raízes independentes → parallel batch (T1, T3, T7, T9) + sequential remainder
57
+ DAG: 14 tasks, fluxos {backend:9, frontend:2} independentes (peso 18‖4) → 2 fluxos paralelos + T14 (integração) sequencial no fim
58
+ DAG: 12 tasks, 4 raízes independentes (3 com peso ≥ M) → parallel batch (T1, T3, T7) + T9 (S) inline + restante sequencial
64
59
  ```
65
60
 
66
61
  ### 3a. Modo Sequencial (auto-advance)
@@ -68,13 +63,12 @@ DAG analysis: 12 tasks, 4 raízes independentes → parallel batch (T1, T3, T7,
68
63
  Loop:
69
64
  ```
70
65
  while (true):
71
- ready = getReadyTasks(data)
66
+ ready = tasks 'pending' cujas dependencies estão todas 'done' (releia tasks.json)
72
67
  if ready.length === 0:
73
68
  break
74
69
 
75
70
  task = ready[0]
76
- updateTaskStatus(data, task.id, 'in_progress')
77
- writeTasksJson(tasksPath, data)
71
+ marcar task.status = 'in_progress' em tasks.json (tool Edit — só campos mutáveis)
78
72
 
79
73
  // Loop de correção autônomo — a barra de qualidade é >= 9 (ver MORPH.md §0).
80
74
  // Entre tasks NÃO há pausa humana: score < 9 dispara re-implementação, não AskUserQuestion.
@@ -86,25 +80,24 @@ while (true):
86
80
  (no attempt > 1: passar o feedback priorizado do morph-eval anterior como input)
87
81
  result = invoke Skill(morph-eval) with implementation output
88
82
  if result.score > best.score: best = result
89
- // Persista o score no feature.json autoritativo da feature, sob taskScores — torna o
90
- // evalScore um signal de disco no MOMENTO da implementação (não no fim). O hook
91
- // gate-guard.js e o Gate 3 leem dali. Edit em .morph/features/** é permitido. Grave:
92
- // taskScores[task.id] = { lastScore: result.score, bestScore: best.score, attempts: attempt, updatedAt: <ISO> }
93
- edit .morph/features/{feature}/feature.json com o taskScores acima
83
+ // Persista o score no feature.json autoritativo — torna o evalScore um signal
84
+ // de disco no MOMENTO da implementação (não no fim). O hook gate-guard.js e o
85
+ // Gate 3 leem dali. O timestamp é carimbado server-side pelo CLI — NUNCA edite
86
+ // feature.json à mão para isso:
87
+ run: morph-spec score {feature} {task.id} {result.score} --dims {arch},{contracts},{quality},{tests}
94
88
 
95
89
  if result.score >= 9:
96
90
  break
97
91
 
98
92
  if best.score >= 9:
99
- updateTaskStatus(data, task.id, 'done')
93
+ marcar task.status = 'done' em tasks.json (tool Edit — atualize também outputs
94
+ reais e, se divergirem do planejado, registre o porquê em notes)
100
95
  else:
101
96
  // 3 tentativas sem atingir 9 — AQUI sim escala ao humano (escape definido).
102
97
  // Esta é a única pausa do loop de implement, e só após esgotar o loop de correção.
103
98
  registrar em recap.md: as 3 tentativas e o melhor score (best.score) com o feedback
104
99
  AskUserQuestion: "Task {id} não atingiu score 9 após 3 tentativas (melhor: {best.score}) — aceitar mesmo assim, continuar tentando, ou pular?"
105
100
  options: [aceitar (marca done com ressalva no recap), continuar tentando (+3 tentativas), pular task (marca blocked)]
106
-
107
- writeTasksJson(tasksPath, data)
108
101
  ```
109
102
 
110
103
  > **Por que loop e não pausa:** MORPH.md §0 manda auto-avançar entre tasks. Score baixo numa task individual é um problema de qualidade que o modelo deve **corrigir sozinho**, não delegar ao humano no meio da implementação. A pausa só existe quando o loop de correção se esgota (3 tentativas) — o "escape" do modelo autônomo. O trust mode não muda esse loop: ele governa os **gates** (proposal/plan/review), não o ciclo de correção intra-task.
@@ -113,18 +106,30 @@ while (true):
113
106
 
114
107
  **Antes de despachar, escope cada agente ao seu domínio.** Um agente genérico sem persona nem standard não "conhece" as convenções daquele domínio — produz código fora do padrão. Para cada fluxo/task a despachar, resolva:
115
108
 
116
- - **Persona:** em `agents.json`, encontre o agente cujo `domains` cobre o `group`/domínio da task (ex: `backend` → `dotnet-senior`, `frontend` → `nextjs-expert`/`ui-designer`, `database` → `ef-modeler`). Pegue o texto do campo `persona`.
109
+ - **Subagent:** em `.morph/framework/agents.json`, encontre o agente cujo `domains` cobre o `group`/domínio da task (ex: `backend` → `dotnet-senior`, `frontend` → `nextjs-expert`/`ui-designer`, `database` → `ef-modeler`). O `subagent_type` do dispatch é **`morph-{id}`** (ex.: `morph-dotnet-senior`) — os subagents são gerados de `agents.json` na instalação e a persona já é o **system prompt** deles; não a cole no prompt.
117
110
  - **Standard(s):** do array `standards` desse mesmo agente em `agents.json` (e/ou do `mandate.md`), selecione o(s) path(s) `.md` relevante(s) à task.
111
+ - **Fallback (tipo não registrado):** se `morph-{id}` não estiver na sua lista de subagent types (stack não instalado neste projeto), use `subagent_type: "general-purpose"` colando o campo `persona` no prompt — e **registre nas `notes` da task**: `dispatch-fallback: general-purpose ({id} não registrado)`. O recap deve refletir o mecanismo real.
118
112
 
119
113
  ```
120
- dispatch Agent (subagent_type: "general-purpose") with prompt:
121
- persona: <texto do campo `persona` do agente de domínio em agents.json>
114
+ dispatch Agent (subagent_type: "morph-{id}") with prompt:
122
115
  contexto: spec.md, mandate.md, task description completa, doneCriteria
123
116
  standard: <path(s) .md do(s) standard(s) do domínio — leia-o(s) antes de implementar>
124
117
  instrução: |
125
- Adote a persona acima. Leia o(s) standard(s) indicado(s) antes de escrever código.
118
+ Leia o(s) standard(s) indicado(s) antes de escrever código.
126
119
  Implemente esta task (ou cadeia de tasks do fluxo) isoladamente.
127
120
  Não modifique spec.md, mandate.md.
121
+
122
+ Protocolo obrigatório:
123
+ 1. Ao concluir, ENTREGUE o relatório final — sua última mensagem é o relatório;
124
+ se a sessão permanecer viva aguardando, envie-o via SendMessage para main.
125
+ Terminar idle sem reportar é falha de protocolo.
126
+ 2. NÃO toque em tasks.json, feature.json, state.json nem no board de sessão
127
+ (TaskCreate/TaskUpdate) — todo estado (status de task, scores, gates) é
128
+ gravado exclusivamente pelo orquestrador.
129
+ 3. Você roda em batch paralelo: NÃO rode build/test da suíte compartilhada
130
+ (dotnet test / npm test do projeto inteiro) — edições concorrentes quebram
131
+ o build umas das outras; a verificação é do orquestrador, pós-batch.
132
+
128
133
  Retorne:
129
134
  - sumário das mudanças
130
135
  - arquivos modificados (paths)
@@ -132,12 +137,16 @@ dispatch Agent (subagent_type: "general-purpose") with prompt:
132
137
  - auto-score 0-10 (rubric: completeness, correctness, mandate compliance)
133
138
  ```
134
139
 
135
- > Este template é deliberadamente paritário com o template sequencial em `morph-implement` §4: ambos passam **persona + standard escopados**. A única diferença é que aqui múltiplos agentes rodam ao mesmo tempo, um por fluxo independente.
140
+ > Este template é deliberadamente paritário com o template sequencial em `morph-implement` §4: ambos despacham o **subagent registrado do domínio (`morph-{id}`)** com standard escopado e as mesmas cláusulas de protocolo. A única diferença é que aqui múltiplos agentes rodam ao mesmo tempo, um por fluxo independente.
141
+
142
+ > **Alternativa quando os fluxos tocam os mesmos projetos/arquivos** (ex.: o mesmo csproj de testes compartilhado): despache com `isolation: "worktree"` — cada agente trabalha numa working tree própria e o orquestrador integra ao final. Nesse caso a cláusula 3 vira "rode build/test apenas dentro do seu worktree".
136
143
 
137
- Aguardar todos os agentes terminarem (Promise.all equivalente). Depois, sequencialmente:
138
- - Para cada output retornado:
144
+ Aguardar todos os agentes terminarem. **Se um agente ficar idle sem entregar o relatório, cobre-o via SendMessage e registre a falha de protocolo no recap.** Depois, sequencialmente:
145
+ - **Rodar a verificação compartilhada uma única vez** (build + suíte de testes do projeto) — os agentes não a rodaram (cláusula 3 do protocolo).
146
+ - Para cada relatório retornado:
139
147
  - Aplicar `Skill(morph-eval)`.
140
- - Se score >= 9: marcar `done`.
148
+ - Gravar o score: `morph-spec score {feature} {taskId} {score} --dims {a},{c},{q},{t}`.
149
+ - Se score >= 9: marcar `done` em tasks.json (tool Edit — só campos mutáveis).
141
150
  - Se score < 9: marcar `pending` novamente com nota — volta ao modo sequencial, onde o loop de correção (até 3 tentativas) busca o score 9.
142
151
 
143
152
  Depois do batch paralelo, executar o restante em modo sequencial (passo 3a).
@@ -157,7 +166,7 @@ Sinais de risco para Gate 3:
157
166
  - Presença de erros não resolvidos.
158
167
  - Mudanças incidentais em standards/.morph/framework/.
159
168
 
160
- Mesma lógica do Gate 1/Gate 2 via `shouldPauseGate(config, signals)`:
169
+ Mesma lógica do Gate 1/Gate 2 (regras da tabela do MORPH.md §5, aplicadas em linguagem natural — sem scripts): `trust: manual` → pausa sempre; `standardsViolations` não-vazio → pausa; `evalScore < 9` → pausa; caso contrário → auto-passa.
161
170
 
162
171
  **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
163
172
  `gateDecisions.review = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
@@ -178,6 +187,9 @@ Mesma lógica do Gate 1/Gate 2 via `shouldPauseGate(config, signals)`:
178
187
  - Não pular `morph-eval` — uma task só avança com score >= 9; abaixo disso, o loop de correção roda (não pausa).
179
188
  - Não reescrever spec.md/mandate.md durante implement (são read-only após Gate 2 — `protect-spec-files.js` hook bloqueia).
180
189
  - Não dispachar mais que 4 agentes em paralelo (sobrecarga de contexto + custo).
190
+ - Não dispachar um agente inteiro para uma raiz de effort S — overhead > trabalho; o `morph-spec dag` já as marca como `inline`.
191
+ - Não deixar sub-agent tocar `tasks.json`, `feature.json` ou o board de sessão — estado é do orquestrador (cláusula 2 do protocolo).
192
+ - Não editar `feature.json` à mão para gravar scores — use `morph-spec score` (timestamp server-side).
181
193
 
182
194
  ## Output esperado
183
195
 
@@ -193,5 +205,6 @@ Mesma lógica do Gate 1/Gate 2 via `shouldPauseGate(config, signals)`:
193
205
  - `framework/skills/core/morph-implement/SKILL.md`
194
206
  - `framework/skills/core/morph-eval/SKILL.md`
195
207
  - `framework/skills/core/morph-review/SKILL.md`
196
- - `src/lib/tasks/tasks-json-parser.js` — DAG queries (`getReadyTasks`, `getPendingTasks`)
197
- - `src/lib/risk-detector.js` — `shouldPauseGate`
208
+ - `morph-spec dag {feature}` — análise de DAG e decisão sequencial vs paralelo (ponderada por effort)
209
+ - `morph-spec score {feature} {task} {score} [--dims a,c,q,t]` — grava taskScores no feature.json com timestamp server-side
210
+ - `.morph/framework/MORPH.md` §5 — regras de pausa de gate (trust mode)
@@ -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: "7.6.0"
7
+ cliVersion: "7.7.1"
8
8
  ---
9
9
 
10
10
  # morph-brainstorming
@@ -75,17 +75,12 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
75
75
 
76
76
  5. Apresente o Gate 1 segundo trust mode.
77
77
 
78
- Ler `.morph/config/config.json → trust` (default `"auto"`). Calcular sinais de risco via `src/lib/risk-detector.js`:
78
+ Ler `.morph/config/config.json → trust` (default `"auto"`). Calcular os sinais de risco em linguagem natural — sem scripts:
79
79
 
80
- ```js
81
- import { shouldPauseGate, getRiskSignals } from '../../../src/lib/risk-detector.js';
82
- const signals = {
83
- designSystemDivergence: /* from passo 3 */,
84
- standardsViolations: /* lista de violações detectadas */,
85
- };
86
- const { pause, reason } = shouldPauseGate(config, signals);
87
- const riskList = getRiskSignals(signals);
88
- ```
80
+ - `designSystemDivergence` = detectado no passo 3 (fontes de design conflitantes).
81
+ - `standardsViolations` = lista de violações detectadas.
82
+
83
+ **Decisão de pausa** (regras da tabela do MORPH.md §5): `trust: manual` → pausa sempre; `designSystemDivergence` → pausa; `standardsViolations` não-vazio → pausa; caso contrário → auto-passa (`pause: false`). Monte `reason` com o sinal que disparou.
89
84
 
90
85
  **Persista a decisão no `feature.json` autoritativo da feature ANTES de apresentar ou auto-aprovar** — grave
91
86
  `gateDecisions.proposal = { pause, reason, signals, trust, decidedAt: <ISO> }` em `.morph/features/{feature}/feature.json`
@@ -127,4 +122,4 @@ O harness conhece o stack por padrão. Não pergunte sobre tecnologia a menos qu
127
122
  - `.morph/framework/MORPH.md` — seções 1 (Papéis), 2 (Fases), 5 (Gates), Trust Mode
128
123
  - `.morph/framework/standards/STANDARDS.json` — para referência de standards relevantes à stack
129
124
  - `framework/skills/core/morph-stack-scan/SKILL.md` — invocada na Parte 2
130
- - `src/lib/risk-detector.js` — `shouldPauseGate(config, signals)` para Gate 1
125
+ - `.morph/framework/MORPH.md` §5 — regras de pausa do Gate 1 (trust mode)
@@ -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: "7.6.0"
7
+ cliVersion: "7.7.1"
8
8
  ---
9
9
 
10
10
  # morph-eval
@@ -17,11 +17,13 @@ O objetivo não é punir — é identificar o que melhorar antes de seguir adian
17
17
  Após concluir uma tarefa ou ao finalizar a fase de implementação.
18
18
  Também invocado ao receber o retorno de um sub-agent com auto-score < 7.
19
19
 
20
+ > **Escopo de fase (Gate 3):** a avaliação da feature inteira NÃO é feita por quem implementou — a `morph-review` despacha a persona `evaluator` de `agents.json` (contexto fresco, sem autoria; recebe mandate + diff + saída real dos testes) usando esta skill como guia. O eval por task pode ser inline; o score composto do Gate 3 vem sempre do avaliador independente.
21
+
20
22
  ## O que fazer
21
23
 
22
24
  1. Identifique o escopo da avaliação: tarefa única ou fase completa.
23
25
 
24
- 2. Para cada dimensão, leia a rubrica correspondente em `framework/evals/rubrics/`:
26
+ 2. Para cada dimensão, leia a rubrica correspondente em `.morph/framework/evals/rubrics/`:
25
27
  - `architecture.md` — peso 30% — vertical slice, separação de responsabilidades, infra
26
28
  - `contracts.md` — peso 25% — tipos, validação, tratamento de erros, naming
27
29
  - `code-quality.md` — peso 25% — naming, ausência de code smells, padrões do stack
@@ -49,9 +51,9 @@ Também invocado ao receber o retorno de um sub-agent com auto-score < 7.
49
51
  - Trace atualizado com `eval_score` e `eval_dimensions`
50
52
 
51
53
  ## Referências
52
- - `framework/evals/rubrics/architecture.md` — rubrica de arquitetura (30%)
53
- - `framework/evals/rubrics/contracts.md` — rubrica de contratos (25%)
54
- - `framework/evals/rubrics/code-quality.md` — rubrica de qualidade (25%)
55
- - `framework/evals/rubrics/test-coverage.md` — rubrica de cobertura (20%)
56
- - `framework/evals/trace-schema.md` — formato canônico do trace
57
- - `framework/MORPH.md` — seção 1 (papel Evaluator)
54
+ - `.morph/framework/evals/rubrics/architecture.md` — rubrica de arquitetura (30%)
55
+ - `.morph/framework/evals/rubrics/contracts.md` — rubrica de contratos (25%)
56
+ - `.morph/framework/evals/rubrics/code-quality.md` — rubrica de qualidade (25%)
57
+ - `.morph/framework/evals/rubrics/test-coverage.md` — rubrica de cobertura (20%)
58
+ - `.morph/framework/evals/trace-schema.md` — formato canônico do trace
59
+ - `.morph/framework/MORPH.md` — seção 1 (papel Evaluator)
@@ -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: "7.6.0"
7
+ cliVersion: "7.7.1"
8
8
  ---
9
9
 
10
10
  # morph-implement
@@ -35,7 +35,7 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
35
35
  3. Identifique a persona de agente adequada para a tarefa:
36
36
  - **Use a tool Read em `.morph/framework/agents.json`** — não recupere a persona de memória. O registro existe para ser consultado; pular o Read significa que a persona vira um rótulo inventado em vez de uma seleção auditável. Encontre o agente cujo `domains` cobre o domínio da tarefa.
37
37
  - Leia o campo `persona` do agente e adote esse ponto de vista durante a implementação. O array `standards` desse agente é o ponto de partida para o passo 2.
38
- - O `**Persona:**` que você grava no recap (passo 6) **deve** ser o `name` exato de um agente que existe em `agents.json` — é assim que o reviewer audita de qual ponto de vista a task foi feita. Uma persona que não casa com nenhum agente do registro é um sinal de que o consult não aconteceu.
38
+ - O `**Persona:**` que você grava no recap (passo 6) **deve** refletir o mecanismo REAL usado: `morph-{id}` (dispatch pelo subagent registrado), `general-purpose (fallback: {id})` (tipo não registrado — registre também nas `notes` da task), ou `inline ({id})` (você executou inline adotando a persona). É assim que o reviewer audita contra o que o runtime realmente registrou — uma auto-declaração que não casa com o dispatch é drift.
39
39
 
40
40
  4. Decida se a task deve ser despachada para um sub-agent. Dispatch é o **default** para o caso pesado/multi-domínio (ver `.morph/framework/MORPH.md` seção 6) — despache quando a task dispara qualquer gatilho:
41
41
  - **Domínio diferente do que você tem carregado agora** (a persona do passo 3 difere da que você vinha usando) — gatilho mais importante: o especialista carrega seus próprios standards sem inchar seu contexto.
@@ -47,7 +47,7 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
47
47
 
48
48
  Inline é aceitável só para tasks S/M **dentro do domínio já carregado**. Se for inline numa task que dispara um gatilho, registre a justificativa em uma linha no `decisions.md` — inline silencioso numa task pesada/multi-domínio é o anti-padrão da seção 6.
49
49
 
50
- **Se for despachar — use este template (passe a persona e o standard escopados, nunca um agente genérico sem contexto de domínio):**
50
+ **Se for despachar — use `subagent_type: "morph-{id}"` do agente identificado no passo 3.** Os subagents são gerados de `agents.json` na instalação (`.claude/agents/morph-{id}.md`) e a persona é o **system prompt** deles — não a cole no prompt. **Fallback:** se `morph-{id}` não estiver na sua lista de subagent types (stack não instalado), use `general-purpose` colando o campo `persona` no prompt e registre nas `notes` da task: `dispatch-fallback: general-purpose ({id} não registrado)`. Template do prompt:
51
51
  ```
52
52
  Implementar [ID]: [title da task]
53
53
 
@@ -56,10 +56,20 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
56
56
  Arquivos relevantes: [lista de paths]
57
57
  Done quando: [doneCriteria do tasks.json]
58
58
 
59
+ Protocolo obrigatório:
60
+ 1. Ao concluir, ENTREGUE o relatório final — sua última mensagem é o relatório;
61
+ se a sessão permanecer viva aguardando, envie-o via SendMessage para main.
62
+ Terminar idle sem reportar é falha de protocolo.
63
+ 2. NÃO toque em tasks.json, feature.json, state.json nem no board de sessão
64
+ (TaskCreate/TaskUpdate) — todo estado é gravado exclusivamente pelo
65
+ orquestrador.
66
+ 3. Se você faz parte de um batch paralelo, NÃO rode build/test da suíte
67
+ compartilhada — a verificação é do orquestrador, pós-batch.
68
+
59
69
  Retorne APENAS: (1) o que foi feito em 2-3 frases, (2) arquivos modificados,
60
70
  (3) problemas encontrados e como foram resolvidos, (4) auto-score 0-10.
61
71
  ```
62
- Após receber o retorno: se auto-score < 7, aplique `morph-eval` antes de aceitar. Se >= 7, integre ao recap.md e vá para o passo 7.
72
+ Após receber o retorno: o auto-score é **triagem, não aprovação** — se < 7, re-despache com o feedback específico (ou corrija inline) antes de prosseguir. Em qualquer caso, a task só avança quando `morph-eval` der score ≥ 9 (barra única do MORPH.md §0). Integre o relatório ao recap.md e vá para o passo 7.
63
73
 
64
74
  **Se não — siga para o passo 5.**
65
75
 
@@ -74,7 +84,7 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
74
84
  ## T{N} — {title}
75
85
 
76
86
  **Standard:** {standard-id} (`.morph/framework/standards/{path}.md`)
77
- **Persona:** {agent-name}
87
+ **Persona:** {mecanismo real — `morph-{id}` | `general-purpose (fallback: {id})` | `inline ({id})`}
78
88
  **Files:**
79
89
  - `{path}`
80
90
  - `{path}`
@@ -93,26 +103,22 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
93
103
  e o reviewer não consegue auditar de onde veio a decisão técnica.
94
104
 
95
105
  7. Registre a entrada no trace de execução em `.morph/traces/YYYY-MM-DD-{feature}-implement.json`:
96
- - Crie o arquivo se não existir (use o schema em `framework/evals/trace-schema.md`)
106
+ - Crie o arquivo se não existir (use o schema em `.morph/framework/evals/trace-schema.md`)
97
107
  - Adicione entrada em `tasks[]` com: `id`, `title`, `status`, `standards_consulted`, `agent_persona`, `files_modified`, `sub_agent_dispatched`
98
108
  - Deixe `eval_score` e `eval_dimensions` como `null` — serão preenchidos pelo morph-eval
99
109
  - Se esta for a última tarefa da fase, preencha `completed_at`
100
110
 
101
- 8. Atualizar `tasks.json` marcando a task como `done` via `updateTaskStatus`:
102
-
103
- ```js
104
- import { parseTasksJson, updateTaskStatus, writeTasksJson, getReadyTasks } from '../../../src/lib/tasks/tasks-json-parser.js';
105
- const data = await parseTasksJson(tasksPath);
106
- updateTaskStatus(data, task.id, 'done');
107
- await writeTasksJson(tasksPath, data);
108
- ```
111
+ 8. Atualizar `tasks.json` marcando a task como `done` — **direto com a tool `Edit` (ou `Write`)**, sem scripts:
109
112
 
110
- `tasks.json` é protegido após o Gate 2, mas os campos de progresso (`status`,
111
- `outputs`, `notes`) continuam graváveis durante a fase implement. `writeTasksJson`
112
- grava o arquivo inteiro (Write) e é o caminho mais simples; **uma `Edit` que mexe
113
- só nesses campos mutáveis também é permitida** — o hook `protect-spec-files` faz
114
- diff semântico e libera Write ou Edit desde que nenhum campo de contrato do plano
115
- (`title`, `description`, `dependencies`, `effort`, `doneCriteria`, `group`) mude.
113
+ - Mude apenas os campos mutáveis da task: `status` (enum válido: `pending`,
114
+ `in_progress`, `done`, `skipped`, `blocked` — nunca invente valores como
115
+ `"completed"`), `outputs` (paths realmente produzidos) e `notes`.
116
+ - **Se os `outputs` reais divergirem dos planejados**, registre o porquê em
117
+ `notes` na mesma edição — o hook exige a note quando há divergência.
118
+ - `tasks.json` é protegido após o Gate 2: o hook `protect-spec-files` faz diff
119
+ semântico e libera Write ou Edit desde que nenhum campo de contrato do plano
120
+ (`title`, `description`, `dependencies`, `effort`, `doneCriteria`, `group`)
121
+ mude e o schema continue válido.
116
122
 
117
123
  9. Quando invocada por `morph-apply`, retornar o controle à skill orquestradora — ela decide qual task vem em seguida via `getReadyTasks(data)`. Quando invocada manualmente, verificar se há próxima task ready e iniciá-la imediatamente aplicando esta skill desde o passo 1 (auto-advance). Pause apenas se encontrar ambiguidade bloqueante não coberta pelo mandate.
118
124
 
@@ -127,4 +133,4 @@ Ao iniciar cada task de `tasks.json` — antes de escrever qualquer código. Nor
127
133
  - `.morph/framework/agents.json` — personas dos agentes por domínio
128
134
  - `.morph/framework/MORPH.md` seção 6 — critérios de dispatch e contrato de retorno do sub-agent
129
135
  - `.morph/features/{feature}/2-plan/mandate.md` — contrato da feature
130
- - `src/lib/tasks/tasks-json-parser.js` — `parseTasksJson`, `updateTaskStatus`, `writeTasksJson`
136
+ - `.morph/framework/schemas/tasks.schema.json` — schema de tasks.json (enum de status e campos)