@polymorphism-tech/morph-spec 4.8.7 → 4.8.8

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 (33) hide show
  1. package/README.md +2 -2
  2. package/bin/morph-spec.js +22 -1
  3. package/bin/task-manager.cjs +120 -16
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/agents.json +1854 -1815
  8. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +141 -23
  9. package/framework/hooks/claude-code/statusline.py +0 -12
  10. package/framework/hooks/claude-code/statusline.sh +6 -2
  11. package/framework/hooks/claude-code/stop/validate-completion.js +70 -23
  12. package/framework/hooks/dev/guard-version-numbers.js +1 -1
  13. package/framework/skills/level-0-meta/morph-init/SKILL.md +44 -6
  14. package/framework/skills/level-0-meta/tool-usage-guide/SKILL.md +67 -16
  15. package/framework/skills/level-1-workflows/phase-clarify/SKILL.md +1 -1
  16. package/framework/skills/level-1-workflows/phase-codebase-analysis/SKILL.md +77 -7
  17. package/framework/skills/level-1-workflows/phase-design/SKILL.md +114 -50
  18. package/framework/skills/level-1-workflows/phase-implement/SKILL.md +139 -1
  19. package/framework/skills/level-1-workflows/phase-setup/SKILL.md +29 -6
  20. package/framework/skills/level-1-workflows/phase-tasks/SKILL.md +4 -3
  21. package/framework/skills/level-1-workflows/phase-uiux/SKILL.md +1 -1
  22. package/framework/standards/STANDARDS.json +944 -933
  23. package/framework/standards/architecture/vertical-slice/vertical-slice.md +429 -0
  24. package/framework/templates/REGISTRY.json +1909 -1888
  25. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +282 -0
  26. package/package.json +1 -1
  27. package/src/commands/agents/dispatch-agents.js +430 -0
  28. package/src/commands/agents/index.js +2 -1
  29. package/src/commands/project/doctor.js +137 -2
  30. package/src/commands/state/state.js +20 -4
  31. package/src/commands/templates/generate-contracts.js +445 -0
  32. package/src/commands/templates/index.js +1 -0
  33. package/src/lib/validators/validation-runner.js +19 -7
@@ -1,20 +1,47 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * PreCompact Hook: Save Context Before Compaction
4
+ * PreCompact Hook: Inject MORPH Context into Compact Summary
5
5
  *
6
6
  * Event: PreCompact
7
7
  *
8
- * Snapshots current morph-spec state to .morph/memory/ before context is lost
9
- * to compaction. The SessionStart hook can use this to restore awareness.
8
+ * Injects a rich morph-spec state snapshot as additionalContext so that
9
+ * Claude's compacted summary includes feature state, task progress, outputs,
10
+ * and next steps. Ensures the session resumes with full morph awareness
11
+ * without having to re-read all the spec files.
12
+ *
13
+ * Also saves snapshot to .morph/memory/ for SessionStart to reference.
10
14
  *
11
15
  * Fail-open: exits 0 on any error.
12
16
  */
13
17
 
14
- import { writeFileSync, mkdirSync, existsSync } from 'fs';
18
+ import { writeFileSync, mkdirSync, existsSync, readFileSync } from 'fs';
15
19
  import { join } from 'path';
16
- import { stateExists, loadState, getActiveFeature } from '../../shared/state-reader.js';
17
- import { pass } from '../../shared/hook-response.js';
20
+ import {
21
+ stateExists, loadState, getActiveFeature,
22
+ derivePhaseForFeature, getPendingGates, getOutputs,
23
+ } from '../../shared/state-reader.js';
24
+ import { injectContext, pass } from '../../shared/hook-response.js';
25
+
26
+ const DECISIONS_MAX_CHARS = 1500;
27
+ const MAX_PENDING_TASKS = 8;
28
+
29
+ const PHASE_POSITIONS = {
30
+ proposal: 1, setup: 1,
31
+ uiux: 2, design: 2,
32
+ clarify: 3,
33
+ tasks: 4,
34
+ implement: 5, sync: 5,
35
+ };
36
+ const PIPELINE_TOTAL = 5;
37
+
38
+ const NEXT_STEP_HINT = {
39
+ proposal: (n) => `Continue spec pipeline: /morph-proposal ${n}`,
40
+ design: (n) => `Approve design gate: morph-spec approve ${n} design`,
41
+ uiux: (n) => `Approve UI gate: morph-spec approve ${n} uiux`,
42
+ tasks: (n) => `Approve tasks gate: morph-spec approve ${n} tasks`,
43
+ implement: (n) => `Continue implementation: morph-spec task next ${n}`,
44
+ };
18
45
 
19
46
  try {
20
47
  if (!stateExists()) pass();
@@ -23,39 +50,130 @@ try {
23
50
  if (!state) pass();
24
51
 
25
52
  const active = getActiveFeature();
53
+ const cwd = process.cwd();
26
54
 
55
+ // ── Disk snapshot (for SessionStart restore) ──────────────────────────────
27
56
  const snapshot = {
28
- timestamp: new Date().toISOString(),
29
- event: 'pre-compact',
57
+ timestamp: new Date().toISOString(),
58
+ event: 'pre-compact',
30
59
  activeFeature: active ? active.name : null,
31
- features: {}
60
+ features: {},
32
61
  };
33
62
 
34
- // Save minimal feature summaries
35
63
  for (const [name, feature] of Object.entries(state.features || {})) {
36
64
  snapshot.features[name] = {
37
- phase: feature.phase,
38
- status: feature.status,
65
+ phase: feature.phase || derivePhaseForFeature(name),
66
+ status: feature.status,
39
67
  workflow: feature.workflow,
40
- tasks: feature.tasks,
68
+ tasks: feature.tasks,
41
69
  approvalGates: Object.fromEntries(
42
70
  Object.entries(feature.approvalGates || {}).map(([gate, data]) => [gate, data.approved])
43
- )
71
+ ),
44
72
  };
45
73
  }
46
74
 
47
- // Save to .morph/memory/
48
- const memoryDir = join(process.cwd(), '.morph', 'memory');
49
- if (!existsSync(memoryDir)) {
50
- mkdirSync(memoryDir, { recursive: true });
75
+ const memoryDir = join(cwd, '.morph', 'memory');
76
+ if (!existsSync(memoryDir)) mkdirSync(memoryDir, { recursive: true });
77
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
78
+ writeFileSync(join(memoryDir, `pre-compact-${ts}.json`), JSON.stringify(snapshot, null, 2), 'utf-8');
79
+
80
+ // ── Nothing more to inject if no active feature ───────────────────────────
81
+ if (!active) pass();
82
+
83
+ const { name, feature } = active;
84
+ const phase = derivePhaseForFeature(name);
85
+ const pos = PHASE_POSITIONS[phase];
86
+ const phaseLabel = pos ? `${phase} (${pos}/${PIPELINE_TOTAL})` : phase;
87
+
88
+ const lines = [
89
+ '=== MORPH-SPEC COMPACT CONTEXT ===',
90
+ `Preserve this section verbatim in the compact summary.`,
91
+ '',
92
+ `Feature : ${name}`,
93
+ `Phase : ${phaseLabel}`,
94
+ `Status : ${feature.status} | Workflow: ${feature.workflow || 'auto'}`,
95
+ ];
96
+
97
+ // Task progress
98
+ const tasks = feature.tasks || {};
99
+ const done = tasks.completed || 0;
100
+ const total = tasks.total || 0;
101
+ if (total > 0) {
102
+ const pct = Math.round(done / total * 100);
103
+ lines.push(`Tasks : ${done}/${total} completed (${pct}%)`);
104
+ }
105
+
106
+ // In-progress + next pending tasks from taskList
107
+ const taskList = Array.isArray(feature.taskList) ? feature.taskList : [];
108
+ const inProg = taskList.filter(t => t.status === 'in_progress');
109
+ const pending = taskList.filter(t => t.status === 'pending').slice(0, MAX_PENDING_TASKS);
110
+
111
+ if (inProg.length > 0) {
112
+ lines.push('');
113
+ lines.push('In progress:');
114
+ for (const t of inProg) lines.push(` [${t.id}] ${t.title}`);
115
+ }
116
+ if (pending.length > 0) {
117
+ lines.push('');
118
+ lines.push('Next pending tasks:');
119
+ for (const t of pending) lines.push(` [${t.id}] ${t.title}`);
120
+ if (taskList.filter(t => t.status === 'pending').length > MAX_PENDING_TASKS) {
121
+ lines.push(` ... (use morph-spec task next ${name} for full list)`);
122
+ }
123
+ }
124
+
125
+ // Created outputs
126
+ const outputs = getOutputs(name) || {};
127
+ const created = Object.entries(outputs).filter(([, o]) => o.created);
128
+ const missing = Object.entries(outputs).filter(([, o]) => !o.created);
129
+ if (created.length > 0) {
130
+ lines.push('');
131
+ lines.push('Created outputs:');
132
+ for (const [type, o] of created) lines.push(` ✓ ${type}: ${o.path}`);
133
+ }
134
+ if (missing.length > 0) {
135
+ lines.push('Pending outputs:');
136
+ for (const [type, o] of missing) lines.push(` ✗ ${type}: ${o.path}`);
137
+ }
138
+
139
+ // Active agents
140
+ const agents = feature.activeAgents || [];
141
+ if (agents.length > 0) {
142
+ lines.push('');
143
+ lines.push(`Active agents: ${agents.join(', ')}`);
144
+ }
145
+
146
+ // Pending approval gates
147
+ const pendingGates = getPendingGates(name);
148
+ if (pendingGates.length > 0) {
149
+ lines.push(`Pending approvals: ${pendingGates.join(', ')}`);
150
+ }
151
+
152
+ // Key decisions (truncated to stay within context budget)
153
+ const decisionsPath = join(cwd, `.morph/features/${name}/1-design/decisions.md`);
154
+ if (existsSync(decisionsPath)) {
155
+ try {
156
+ const raw = readFileSync(decisionsPath, 'utf-8');
157
+ const content = raw.length > DECISIONS_MAX_CHARS
158
+ ? raw.slice(0, DECISIONS_MAX_CHARS) + '\n[... truncated — full file at decisions.md]'
159
+ : raw;
160
+ lines.push('');
161
+ lines.push('Key decisions (decisions.md):');
162
+ lines.push(content);
163
+ } catch { /* non-blocking */ }
164
+ }
165
+
166
+ // Next step
167
+ const hintFn = NEXT_STEP_HINT[phase];
168
+ if (hintFn) {
169
+ lines.push('');
170
+ lines.push(`Next step: ${hintFn(name)}`);
51
171
  }
52
172
 
53
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
54
- const snapshotPath = join(memoryDir, `pre-compact-${timestamp}.json`);
55
- writeFileSync(snapshotPath, JSON.stringify(snapshot, null, 2), 'utf-8');
173
+ lines.push('');
174
+ lines.push('=== END MORPH CONTEXT ===');
56
175
 
57
- pass();
176
+ injectContext(lines.join('\n'));
58
177
  } catch {
59
- // Fail-open
60
178
  process.exit(0);
61
179
  }
@@ -544,18 +544,6 @@ def main():
544
544
  line += f" ({toks})"
545
545
  parts2.append(line + suffix)
546
546
 
547
- # Token breakdown from JSONL (session totals: input / output / cached)
548
- if entries:
549
- tok = get_token_metrics(entries)
550
- tok_parts = []
551
- if tok['input']:
552
- tok_parts.append(f"in:{format_tokens(tok['input'])}")
553
- if tok['output']:
554
- tok_parts.append(f"out:{format_tokens(tok['output'])}")
555
- if tok['cached']:
556
- tok_parts.append(f"↩{format_tokens(tok['cached'])}")
557
- if tok_parts:
558
- parts2.append(f"{GRAY}{' '.join(tok_parts)}{R}")
559
547
 
560
548
  # Git info (branch + diff stats)
561
549
  git = get_git_info(cwd)
@@ -1,7 +1,11 @@
1
1
  #!/usr/bin/env bash
2
2
  # morph-spec statusline — installed globally to ~/.claude/statusline.sh
3
3
  # Claude Code invokes this with JSON via stdin after each response.
4
- # Requires: Python 3 available as `python3` on PATH.
4
+ # Requires: Python 3 available as `python3` or `python` on PATH.
5
5
 
6
6
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
- python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
7
+ if command -v python3 &>/dev/null; then
8
+ python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
9
+ elif command -v python &>/dev/null; then
10
+ python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
11
+ fi
@@ -1,23 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * Stop Hook: Validate Completion (Advisory)
4
+ * Stop Hook: Validate Completion + Phase Transition Alerts
5
5
  *
6
6
  * Event: Stop
7
7
  *
8
- * When Claude stops, checks for incomplete work:
9
- * - In implement phase: checks uncompleted tasks
10
- * - In spec phases: checks required outputs still created: false
11
- * - Returns additionalContext with what remains (advisory, not blocking)
8
+ * 1. Detects transitions into the `implement` phase and recommends /compact
9
+ * before starting ensures Claude has a clean context for writing code.
10
+ * 2. Checks for incomplete work (tasks, missing outputs, pending gates).
11
+ *
12
+ * Phase transition tracking: stores last-seen phase per feature in
13
+ * .morph/memory/phase-watch.json so the alert fires exactly once,
14
+ * the first Stop after the transition occurs.
12
15
  *
13
16
  * Uses stop_hook_active env check to prevent infinite loops.
14
17
  *
15
18
  * Fail-open: exits 0 on any error.
16
19
  */
17
20
 
18
- import { stateExists, getActiveFeature, getMissingOutputs } from '../../shared/state-reader.js';
21
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
22
+ import { join } from 'path';
23
+ import {
24
+ stateExists, getActiveFeature, getMissingOutputs, derivePhaseForFeature,
25
+ } from '../../shared/state-reader.js';
19
26
  import { injectContext, pass } from '../../shared/hook-response.js';
20
27
 
28
+ // Phases where compaction is strongly recommended before starting
29
+ const COMPACT_TRIGGER_PHASES = new Set(['implement']);
30
+
31
+ // Phases where compaction is suggested when arriving from a heavy spec phase
32
+ const HEAVY_SOURCE_PHASES = new Set(['tasks', 'design', 'clarify', 'uiux']);
33
+
21
34
  try {
22
35
  // Prevent infinite loop
23
36
  if (process.env.MORPH_STOP_HOOK_ACTIVE === '1') pass();
@@ -28,10 +41,48 @@ try {
28
41
  if (!active) pass();
29
42
 
30
43
  const { name, feature } = active;
44
+ const cwd = process.cwd();
31
45
  const warnings = [];
32
46
 
33
- // Check for incomplete tasks during implement phase
34
- if (feature.phase === 'implement' && feature.tasks) {
47
+ // ── Phase transition detection ────────────────────────────────────────────
48
+ const currentPhase = feature.phase || derivePhaseForFeature(name, cwd);
49
+
50
+ const watchPath = join(cwd, '.morph', 'memory', 'phase-watch.json');
51
+ let phaseWatch = {};
52
+ try {
53
+ if (existsSync(watchPath)) phaseWatch = JSON.parse(readFileSync(watchPath, 'utf-8'));
54
+ } catch { /* corrupt file — start fresh */ }
55
+
56
+ const lastPhase = phaseWatch[name];
57
+
58
+ // Update stored phase (always, so the alert only fires once per transition)
59
+ try {
60
+ phaseWatch[name] = currentPhase;
61
+ writeFileSync(watchPath, JSON.stringify(phaseWatch, null, 2), 'utf-8');
62
+ } catch { /* non-blocking */ }
63
+
64
+ // Alert when entering implement from a heavy spec phase
65
+ const isPhaseTransition = lastPhase && lastPhase !== currentPhase;
66
+ const enteringImplement = COMPACT_TRIGGER_PHASES.has(currentPhase);
67
+ const comingFromHeavyPhase = HEAVY_SOURCE_PHASES.has(lastPhase);
68
+
69
+ if (isPhaseTransition && enteringImplement && comingFromHeavyPhase) {
70
+ warnings.push(
71
+ `⚡ Phase advanced: ${lastPhase} → implement`,
72
+ ``,
73
+ `💡 Recommended before starting implementation:`,
74
+ ` Run /compact to free up context — the spec, design and task files`,
75
+ ` loaded during planning take significant space. A compact now gives`,
76
+ ` Claude a clean slate focused on writing code.`,
77
+ ``,
78
+ ` After /compact, resume with: morph-spec task next ${name}`,
79
+ );
80
+ }
81
+
82
+ // ── Incomplete work checks ────────────────────────────────────────────────
83
+
84
+ // Incomplete tasks during implement phase
85
+ if (currentPhase === 'implement' && feature.tasks) {
35
86
  const remaining = (feature.tasks.total || 0) - (feature.tasks.completed || 0);
36
87
  if (remaining > 0) {
37
88
  warnings.push(`${remaining} task(s) remaining for feature '${name}'`);
@@ -41,11 +92,11 @@ try {
41
92
  }
42
93
  }
43
94
 
44
- // Check for missing outputs in spec phases
45
- if (['proposal', 'design', 'clarify', 'tasks', 'uiux'].includes(feature.phase)) {
95
+ // Missing outputs in spec phases
96
+ if (['proposal', 'design', 'clarify', 'tasks', 'uiux'].includes(currentPhase)) {
46
97
  const missing = getMissingOutputs(name);
47
98
  if (missing.length > 0) {
48
- warnings.push(`Missing outputs for '${name}' (${feature.phase} phase):`);
99
+ warnings.push(`Missing outputs for '${name}' (${currentPhase} phase):`);
49
100
  for (const output of missing.slice(0, 5)) {
50
101
  warnings.push(` - ${output.type}: ${output.path}`);
51
102
  }
@@ -55,34 +106,30 @@ try {
55
106
  }
56
107
  }
57
108
 
58
- // Check pending approval gates
109
+ // Pending approval gates
59
110
  if (feature.approvalGates) {
60
111
  const pendingGates = Object.entries(feature.approvalGates)
61
112
  .filter(([, gate]) => !gate.approved && !gate.timestamp)
62
- .map(([name]) => name);
113
+ .map(([gateName]) => gateName);
63
114
 
64
- // Only warn about gates relevant to current/past phases
65
- const relevantGates = pendingGates.filter(gate => {
66
- const gatePhaseMap = { proposal: 'proposal', uiux: 'uiux', design: 'design', tasks: 'tasks' };
67
- return gatePhaseMap[gate] !== undefined;
68
- });
115
+ const gatePhaseMap = { proposal: 'proposal', uiux: 'uiux', design: 'design', tasks: 'tasks' };
116
+ const relevant = pendingGates.filter(gate => gatePhaseMap[gate] !== undefined);
69
117
 
70
- if (relevantGates.length > 0) {
71
- warnings.push(`Pending approvals: ${relevantGates.join(', ')}`);
118
+ if (relevant.length > 0) {
119
+ warnings.push(`Pending approvals: ${relevant.join(', ')}`);
72
120
  }
73
121
  }
74
122
 
75
123
  if (warnings.length === 0) pass();
76
124
 
77
125
  const message = [
78
- 'MORPH-SPEC: Incomplete work detected:',
126
+ 'MORPH-SPEC:',
79
127
  ...warnings.map(w => ` ${w}`),
80
128
  '',
81
- 'Resume with: morph-spec status ' + name
129
+ `Status: morph-spec status ${name}`,
82
130
  ].join('\n');
83
131
 
84
132
  injectContext(message);
85
133
  } catch {
86
- // Fail-open
87
134
  process.exit(0);
88
135
  }
@@ -7,7 +7,7 @@
7
7
  * Scope: Framework codebase only (dev hook)
8
8
  *
9
9
  * Blocks Write/Edit to files that contain hardcoded version patterns
10
- * like "MORPH-SPEC v4.8.7". Version should only be in package.json.
10
+ * like "MORPH-SPEC v4.8.8". Version should only be in package.json.
11
11
  *
12
12
  * Checked extensions: .md, .cs, .css, .js (covers templates + source)
13
13
  * Exceptions: CHANGELOG.md, node_modules/, test/, package.json, package-lock.json
@@ -111,6 +111,9 @@ Gather evidence to build a stack map. Run these in parallel:
111
111
  | `Glob` | `database/migrations/**` | Supabase local dev |
112
112
  | `Glob` | `supabase/**` | Supabase project config |
113
113
  | `Glob` | `**/*.razor` | Blazor |
114
+ | `Glob` | `**/Abstractions/IHandler.cs` | VSA pattern (KanaiyaKatarmal) |
115
+ | `Glob` | `**/Pipelines/ValidationDecorator.cs` | VSA pipeline |
116
+ | `Glob` | `**/Features/**/*Feature` | VSA feature folder suffix |
114
117
  | `Read` | `.env.example` | Env vars reveal integrations |
115
118
  | `Read` | `README.md` | Existing project description |
116
119
  | `Grep` | `@supabase/supabase-js` | Supabase dep in any package.json |
@@ -120,14 +123,20 @@ Gather evidence to build a stack map. Run these in parallel:
120
123
  Read each found `package.json` for dependency names. Build an **evidence map**:
121
124
 
122
125
  ```
123
- stack: [nextjs, dotnet, ...]
124
- integrations: [supabase, docker, clerk, ...]
125
- uiLibrary: shadcn | fluent-ui | mudblazor | null
126
- monorepo: true | false
127
- frontendPath: src/frontend (if monorepo)
128
- backendPath: src/backend (if monorepo)
126
+ stack: [nextjs, dotnet, ...]
127
+ integrations: [supabase, docker, clerk, ...]
128
+ uiLibrary: shadcn | fluent-ui | mudblazor | null
129
+ monorepo: true | false
130
+ frontendPath: src/frontend (if monorepo)
131
+ backendPath: src/backend (if monorepo)
132
+ architectureStyle: vertical-slice | null ← novo
129
133
  ```
130
134
 
135
+ **VSA detection:** `architectureStyle: "vertical-slice"` se qualquer um destes existir:
136
+ - `Abstractions/IHandler.cs` encontrado pelo Glob
137
+ - `Pipelines/ValidationDecorator.cs` encontrado pelo Glob
138
+ - 2+ pastas com sufixo `Feature` em `Features/` (ex: `BookFeature`, `OrderFeature`)
139
+
131
140
  ---
132
141
 
133
142
  ## Step 4 — Ask Targeted Questions
@@ -195,6 +204,35 @@ Read `.morph/config/config.json` and merge into `project`:
195
204
  }
196
205
  ```
197
206
 
207
+ **Se `architectureStyle: "vertical-slice"` foi detectado no Step 3**, adicione também:
208
+
209
+ ```json
210
+ {
211
+ "architecture": {
212
+ "style": "vertical-slice"
213
+ }
214
+ }
215
+ ```
216
+
217
+ E informe o usuário:
218
+
219
+ ```
220
+ ✓ Detected: Vertical Slice Architecture (IHandler.cs / *Feature folders)
221
+ → config.json: architecture.style = "vertical-slice"
222
+ → morph-spec will use vsa-architect + contracts-vsa.cs for features
223
+ ```
224
+
225
+ > Esta key é o que ativa o `vsa-architect` em vez do `domain-architect` durante `/morph-proposal`.
226
+
227
+ **Se o projeto ainda não existe (novo projeto VSA do zero)**, informe também:
228
+
229
+ ```
230
+ Para criar um novo projeto VSA:
231
+ dotnet new install https://github.com/polymorphism-tech/Morph_Template_VerticalSliceArchitecture
232
+ dotnet new vsa -n MyProject
233
+ cd MyProject && npx morph-spec init
234
+ ```
235
+
198
236
  ---
199
237
 
200
238
  ## Step 7 — Configure MCPs
@@ -34,18 +34,52 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
34
34
  └──┬──────────────┬────────┘
35
35
  │ YES │ NO
36
36
  ▼ ▼
37
- Use MCP tool ┌──────────────────┐
38
- (+ fallback) │ Does it require
39
- MULTI-STEP
40
- exploration of
41
- many files?
42
- └──┬──────────┬─────┘
43
- │ YES │ NO
44
-
45
- Use Task Do it
46
- (subagent) directly
37
+ Use MCP tool ┌──────────────────────────────┐
38
+ (+ fallback) │ Does it involve MULTIPLE
39
+ independent specialist tasks
40
+ (domain analysis + schema,
41
+ backend + frontend tasks)?
42
+ └──┬───────────────┬───────────┘
43
+ │ YES │ NO
44
+
45
+ Dispatch parallel ┌──────────────────┐
46
+ Task subagents │ Does it require
47
+ (see below) │ MULTI-STEP │
48
+ │ exploration of │
49
+ │ many files? │
50
+ └──┬──────────┬─────┘
51
+ │ YES │ NO
52
+ ▼ ▼
53
+ Use Task Do it
54
+ (subagent) directly
47
55
  ```
48
56
 
57
+ ### When to use Task subagents for agent dispatch
58
+
59
+ Dispatch specialist agents as **Task subagents** when ALL conditions are met:
60
+ - Phase is `design`, `tasks`, or `implement`
61
+ - Feature has **2+ active specialist agents** (check `activeAgents` in state.json)
62
+ - Work can be divided into **independent parallel tasks** with clear outputs
63
+
64
+ ```
65
+ Feature with multiple active agents?
66
+
67
+ ├─ design phase → Dispatch domain-architect (complexity analysis)
68
+ │ + tech leads (spec validation) — in PARALLEL
69
+
70
+ ├─ tasks phase → Dispatch per-domain task planners (if spec has 50+ reqs
71
+ │ OR 3+ independent domains detected)
72
+
73
+ └─ implement phase → Dispatch per-domain implementers when tasks.total ≥ 6
74
+ AND feature spans multiple domains (backend + frontend,
75
+ or backend + infra, etc.)
76
+ ```
77
+
78
+ **Key principle (from real-world usage):** Define subagents by **TASK**, not by **ROLE**.
79
+ - ✅ "Analyze this proposal for DDD complexity and return level + blueprint" (task)
80
+ - ✅ "Implement backend tasks T001-T005 for feature X" (task)
81
+ - ❌ "Be a domain architect" (vague role assignment — poor results)
82
+
49
83
  ---
50
84
 
51
85
  ## Tools Per Phase
@@ -80,18 +114,19 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
80
114
  | Action | Tool | Why |
81
115
  |--------|------|-----|
82
116
  | Verify feature state | **Bash** `npx morph-spec state get {feature}` | CLI command |
83
- | Detect agents + standards | **Read** `.morph/framework/agents.json` → match keywords → **Bash** `npx morph-spec state add-agent {f} {id}` per match | No CLI command read agents.json directly |
117
+ | Detect agents + standards | **Read** `.morph/framework/agents.json` → match keywords → **Bash** `npx morph-spec state add-agent {f} {id}` per match | Keyword matching is fast inline |
84
118
  | Read project context | **Read** `.morph/context/README.md` | Single file |
85
119
  | Read config | **Read** `.morph/config.json` | Single file |
86
120
  | Scan project structure | **Glob** `src/**/*.{ts,tsx,cs}` | Understand codebase layout |
87
121
  | Get repo metadata | **GitHub MCP** `get_repo()` | Structured repo info (if MCP available) |
122
+ | Get dispatch config for next phases | **Bash** `npx morph-spec dispatch-agents {feature} design` | Shows which agents will be dispatched in design phase |
88
123
  | Update state | **Bash** `npx morph-spec state set ...` | CLI command |
89
124
 
90
125
  **MCPs used:** GitHub (optional — for repo metadata).
91
126
 
92
127
  **Anti-patterns:**
93
128
  - ❌ Calling `detect-agents` CLI (it doesn't exist — read `.morph/framework/agents.json` directly)
94
- - ❌ Task agent to detect project stack (read agents.json and match keywords inline)
129
+ - ❌ Task agent to detect project stack inline (keyword matching is fast enough directly)
95
130
  - ❌ WebSearch for project info (it's local, use Read/Glob)
96
131
 
97
132
  ---
@@ -132,6 +167,9 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
132
167
  | Action | Tool | Why |
133
168
  |--------|------|-----|
134
169
  | Read proposal + UI specs | **Read** output files | Single known files |
170
+ | **Get dispatch config** | **Bash** `npx morph-spec dispatch-agents {feature} design` | Which agents to spawn + their task prompts |
171
+ | **Dispatch domain-architect** (parallel) | **Task** subagent with prompt from dispatch config | Analyze DDD complexity independently |
172
+ | **Dispatch tech leads** (parallel) | **Task** subagents (dotnet-senior, nextjs-expert, etc.) | Validate architecture independently |
135
173
  | Get database schema | **Supabase MCP** `list_tables()`, `get_table_schema()` | **PREFERRED** — real schema data |
136
174
  | Get table relationships | **Supabase MCP** `get_relationships()` | Real FK/constraint data |
137
175
  | Get RLS policies | **Supabase MCP** `query()` with pg_policies | Security analysis |
@@ -149,11 +187,14 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
149
187
 
150
188
  **MCPs used:** Supabase (schema analysis), Context7 (library research), GitHub (code search).
151
189
 
190
+ **Parallel dispatch opportunity:** Domain complexity analysis (domain-architect) and schema analysis (phase-codebase-analysis) are **independent** — run them as parallel Task subagents to cut design phase time.
191
+
152
192
  **Anti-patterns:**
153
193
  - ❌ Guessing field names without checking schema (use MCP or Grep first!)
154
- - ❌ Task agent to read a single spec file (just use Read)
194
+ - ❌ Task agent to read a single spec file (use Read directly)
155
195
  - ❌ WebSearch for database schema (use Supabase MCP or code analysis)
156
196
  - ❌ Manually writing contracts-level{N}.cs from scratch (use template render)
197
+ - ❌ Running domain analysis and schema analysis sequentially (they're independent — run in parallel)
157
198
 
158
199
  ---
159
200
 
@@ -189,8 +230,10 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
189
230
  | Action | Tool | Why |
190
231
  |--------|------|-----|
191
232
  | Read spec + contracts + decisions | **Read** all output files | Full context needed |
233
+ | **Get dispatch config** | **Bash** `npx morph-spec dispatch-agents {feature} tasks` | Which domain experts to consult |
192
234
  | Analyze implementation complexity | **Grep** patterns in existing code | Estimate effort from codebase size |
193
235
  | Count existing similar patterns | **Glob** `**/Services/**/*.cs` | Understand scope |
236
+ | **Dispatch domain experts** (when spec is complex) | **Task** subagents per domain | Parallel per-domain task planning |
194
237
  | Look up implementation patterns | **Context7 MCP** `query_docs()` | Estimate task complexity accurately |
195
238
  | Create GitHub issues from tasks | **GitHub MCP** `create_issue()` | Sync tasks to project management |
196
239
  | **Fallback:** Create issues via CLI | **Bash** `gh issue create ...` | When no GitHub MCP |
@@ -200,8 +243,9 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
200
243
  **MCPs used:** Context7 (complexity estimation), GitHub (issue creation).
201
244
 
202
245
  **Anti-patterns:**
203
- - ❌ Task agent to break down a simple spec (do it directly, specs are already structured)
204
- - ✅ Task agent for complex specs with 50+ requirements (legitimate multi-step work)
246
+ - ❌ Task agent to break down a simple 1-domain spec (do it directly)
247
+ - ✅ Task agent for complex multi-domain specs (backend + frontend + infra = 3 independent planners)
248
+ - ✅ Task agent when spec has 20+ requirements across multiple bounded contexts
205
249
  - ❌ Manually writing tasks.json without reading all outputs first
206
250
 
207
251
  ---
@@ -213,6 +257,8 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
213
257
  | Action | Tool | Why |
214
258
  |--------|------|-----|
215
259
  | Read task details | **Read** tasks.json, spec.md, contracts.cs | Implementation reference |
260
+ | **Check parallelization viability** | **Bash** `npx morph-spec dispatch-agents {feature} implement` | Whether to dispatch parallel implementers |
261
+ | **Dispatch per-domain implementers** (when viable) | **Task** subagents — one per domain group | Backend tasks + frontend tasks run in parallel |
216
262
  | Create new files | **Write** new source files | New entities, services, pages |
217
263
  | Modify existing files | **Edit** existing source files | Add features to existing code |
218
264
  | Look up API during coding | **Context7 MCP** `query_docs()` | Accurate library usage |
@@ -228,17 +274,22 @@ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
228
274
  | Smoke test feature no browser | **Playwright MCP** `browser_navigate()` + `browser_snapshot()` | Manual testing |
229
275
  | Verificar erros de console | **Playwright MCP** `browser_console_messages()` | Browser DevTools |
230
276
  | Screenshot para recap.md | **Playwright MCP** `browser_take_screenshot()` | Manual screenshot |
231
- | Multi-file refactoring | **Task** (subagent) | Complex parallel changes |
232
277
  | Update state | **Bash** `npx morph-spec state set ...` | CLI command |
233
278
 
234
279
  **MCPs used:** Supabase (migrations, RLS), Context7 (API lookup), GitHub (PRs), Playwright (smoke test, verification).
235
280
 
281
+ **Parallel dispatch threshold:**
282
+ - `tasks.total ≥ 6` AND feature spans 2+ domains (e.g. `dotnet-senior` + `nextjs-expert`) → dispatch parallel implementers
283
+ - Each subagent gets its own task group (backend T001-T005, frontend T006-T009) with isolated file scope
284
+
236
285
  **Anti-patterns:**
237
286
  - ❌ Task agent for a single file edit (use Edit directly)
238
287
  - ✅ Task agent for implementing a full service layer across 5+ files
288
+ - ✅ Task agent for each domain group when feature has 6+ tasks across multiple domains
239
289
  - ❌ Bash `cat` to create files (use Write tool)
240
290
  - ❌ Bash `sed` to modify code (use Edit tool)
241
291
  - ❌ Implementing without reading contracts.cs first (contracts are the source of truth)
292
+ - ❌ Implementing backend and frontend tasks sequentially when they have no cross-dependencies
242
293
 
243
294
  ---
244
295
 
@@ -4,7 +4,7 @@ description: MORPH-SPEC Phase 3 (Clarify). Reviews spec.md for ambiguities, gene
4
4
  argument-hint: "[feature-name]"
5
5
  user-invocable: false
6
6
  allowed-tools: Read, Write, Edit, Bash, Glob, Grep
7
- cliVersion: "4.8.7"
7
+ cliVersion: "4.8.8"
8
8
  ---
9
9
 
10
10
  # MORPH Clarify - FASE 3