@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 (377) hide show
  1. package/CLAUDE.md +51 -43
  2. package/README.md +39 -38
  3. package/bin/morph-spec.js +41 -113
  4. package/bin/validate.js +1 -147
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +86 -21
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-status.md +4 -4
  17. package/framework/docs/CLI.md +574 -286
  18. package/framework/evals/review-checklist.md +82 -41
  19. package/framework/hooks/README.md +10 -11
  20. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  21. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  22. package/framework/hooks/claude-code/core/chain.js +4 -65
  23. package/framework/hooks/claude-code/core/comment-guard.js +3 -17
  24. package/framework/hooks/claude-code/core/denylist.json +33 -3
  25. package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
  26. package/framework/hooks/claude-code/core/gate-guard.js +9 -78
  27. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  28. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  29. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  30. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  31. package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
  32. package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
  33. package/framework/hooks/claude-code/core/state-sync.js +15 -85
  34. package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
  35. package/framework/hooks/claude-code/core/validate-completion.js +37 -147
  36. package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
  37. package/framework/hooks/claude-code/core/worktree-context.js +15 -114
  38. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  39. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  40. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  41. package/framework/hooks/claude-code/statusline.py +55 -41
  42. package/framework/hooks/package.json +3 -0
  43. package/framework/hooks/shared/activity-logger.js +9 -89
  44. package/framework/hooks/shared/agent-reap.js +2 -17
  45. package/framework/hooks/shared/build-check.js +11 -76
  46. package/framework/hooks/shared/claim-registry.js +9 -119
  47. package/framework/hooks/shared/command-tokens.js +168 -44
  48. package/framework/hooks/shared/comment-rules.js +5 -20
  49. package/framework/hooks/shared/comment-scanner.js +11 -43
  50. package/framework/hooks/shared/comment-stripper.js +11 -85
  51. package/framework/hooks/shared/e2e-check.js +0 -0
  52. package/framework/hooks/shared/errata.js +59 -0
  53. package/framework/hooks/shared/feature-locator.js +11 -95
  54. package/framework/hooks/shared/frontend-locations.js +4 -0
  55. package/framework/hooks/shared/gate-question.js +12 -0
  56. package/framework/hooks/shared/git-exclude.js +3 -20
  57. package/framework/hooks/shared/git-paths.js +8 -104
  58. package/framework/hooks/shared/graph-freshness.js +22 -166
  59. package/framework/hooks/shared/graph-refresh.js +21 -149
  60. package/framework/hooks/shared/heavy-command.js +4 -12
  61. package/framework/hooks/shared/hook-response.js +8 -83
  62. package/framework/hooks/shared/morph-cli.js +8 -52
  63. package/framework/hooks/shared/payload-utils.js +10 -132
  64. package/framework/hooks/shared/phase-utils.js +14 -72
  65. package/framework/hooks/shared/redact-secrets.js +4 -15
  66. package/framework/hooks/shared/reparse-scan.js +6 -66
  67. package/framework/hooks/shared/slice-progress.js +99 -0
  68. package/framework/hooks/shared/source-files.js +4 -17
  69. package/framework/hooks/shared/spawn-command.js +23 -0
  70. package/framework/hooks/shared/state-reader.js +51 -247
  71. package/framework/hooks/shared/state-writer.js +9 -54
  72. package/framework/hooks/shared/stdin-reader.js +1 -11
  73. package/framework/hooks/shared/telemetry-logger.js +7 -67
  74. package/framework/hooks/shared/transcript-usage.js +7 -29
  75. package/framework/hooks/shared/worktree-root.js +7 -29
  76. package/framework/mcp/registry.json +3 -4
  77. package/framework/phases.json +23 -39
  78. package/framework/rules/csharp-standards.md +1 -1
  79. package/framework/rules/frontend-standards.md +1 -0
  80. package/framework/rules/hangfire-standards.md +1 -1
  81. package/framework/rules/neon-standards.md +1 -1
  82. package/framework/schemas/config.schema.json +14 -9
  83. package/framework/schemas/feature.schema.json +15 -50
  84. package/framework/skills/README.md +3 -4
  85. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  86. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  87. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  88. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  89. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  90. package/framework/skills/core/morph-media/SKILL.md +1 -1
  91. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  92. package/framework/skills/core/morph-review/SKILL.md +111 -136
  93. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  94. package/framework/skills/core/morph-uiux/SKILL.md +94 -152
  95. package/framework/standards/STANDARDS.json +109 -1219
  96. package/framework/standards/STANDARDS.meta.json +1319 -0
  97. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  98. package/framework/standards/ai-agents/agent-session.md +1 -1
  99. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  100. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  101. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  102. package/framework/standards/ai-agents/guardrails.md +1 -1
  103. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  104. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  105. package/framework/standards/ai-agents/production.md +1 -1
  106. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  107. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  108. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  109. package/framework/standards/ai-agents/setup.md +3 -3
  110. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  111. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  112. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  113. package/framework/standards/ai-agents/workflows.md +1 -1
  114. package/framework/standards/architecture/code-comments.md +7 -7
  115. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  116. package/framework/standards/backend/api/validation.md +1 -1
  117. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  118. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  119. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  120. package/framework/standards/data/nosql/cache/redis.md +1 -1
  121. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  122. package/framework/standards/frontend/admin-app/forms.md +1 -1
  123. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  124. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  125. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  126. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  127. package/framework/templates/CLAUDE.md.core.template +3 -3
  128. package/framework/templates/README.md +2 -3
  129. package/framework/templates/REGISTRY.json +15 -179
  130. package/framework/templates/docs/plan.md +72 -0
  131. package/framework/templates/docs/ui-mockups.md +4 -0
  132. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  134. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  135. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  136. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  137. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  138. package/package.json +1 -1
  139. package/src/commands/context/context-refresh.js +0 -16
  140. package/src/commands/dashboard/dashboard.js +2 -8
  141. package/src/commands/e2e/e2e.js +18 -105
  142. package/src/commands/graph/affected.js +0 -14
  143. package/src/commands/graph/build.js +11 -50
  144. package/src/commands/graph/explain.js +0 -16
  145. package/src/commands/graph/status.js +1 -12
  146. package/src/commands/mcp/mcp-setup.js +4 -62
  147. package/src/commands/observability/cost.js +17 -111
  148. package/src/commands/observability/telemetry.js +1 -14
  149. package/src/commands/project/branch.js +4 -45
  150. package/src/commands/project/claim.js +4 -48
  151. package/src/commands/project/doctor-procs.js +1 -1
  152. package/src/commands/project/doctor.js +53 -307
  153. package/src/commands/project/env.js +1 -27
  154. package/src/commands/project/fleet.js +6 -35
  155. package/src/commands/project/heavy.js +3 -3
  156. package/src/commands/project/init.js +8 -68
  157. package/src/commands/project/install-plugin-cmd.js +0 -1
  158. package/src/commands/project/setup-infra-cmd.js +2 -1
  159. package/src/commands/project/stack-scan.js +58 -0
  160. package/src/commands/project/standards.js +65 -72
  161. package/src/commands/project/status.js +28 -121
  162. package/src/commands/project/update.js +31 -223
  163. package/src/commands/project/worktree.js +109 -883
  164. package/src/commands/scaffold/scaffold.js +0 -25
  165. package/src/commands/state/advance.js +65 -120
  166. package/src/commands/state/approve.js +87 -229
  167. package/src/commands/state/archive.js +23 -139
  168. package/src/commands/state/auto-approval.js +65 -0
  169. package/src/commands/state/create.js +49 -109
  170. package/src/commands/state/delete.js +11 -63
  171. package/src/commands/state/finish.js +105 -494
  172. package/src/commands/state/gate-check.js +74 -183
  173. package/src/commands/state/gate-decision.js +8 -42
  174. package/src/commands/state/handoff.js +4 -31
  175. package/src/commands/state/list.js +21 -91
  176. package/src/commands/state/report-check.js +134 -338
  177. package/src/commands/state/retype.js +12 -28
  178. package/src/commands/validation/index.js +0 -3
  179. package/src/commands/validation/mutate.js +79 -19
  180. package/src/commands/validation/plan-check.js +81 -0
  181. package/src/commands/validation/validate-feature.js +52 -216
  182. package/src/commands/validation/verify.js +45 -160
  183. package/src/core/paths/output-schema.js +9 -155
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +2 -30
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +32 -395
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +21 -0
  191. package/src/core/work-types.js +10 -45
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
  194. package/src/dashboard/app/components/CommandPalette.jsx +4 -11
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +3 -9
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/pages/Activity.jsx +1 -1
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
  211. package/src/dashboard/app/pages/Features.jsx +9 -16
  212. package/src/dashboard/app/pages/Guide.jsx +2 -3
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +14 -83
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +1 -7
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +92 -113
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +4 -7
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/lib/ai/ai-checks.js +7 -52
  233. package/src/lib/ai/ai-project-scan.js +14 -108
  234. package/src/lib/ai/code-audit.js +7 -63
  235. package/src/lib/ai/pin-drift.js +8 -51
  236. package/src/lib/ai/registry-audit.js +11 -90
  237. package/src/lib/ci/ci-verify.js +14 -13
  238. package/src/lib/ci/github-ci.js +9 -37
  239. package/src/lib/ci/workflow-scaffold.js +2 -10
  240. package/src/lib/detectors/claude-config-detector.js +13 -74
  241. package/src/lib/detectors/index.js +8 -59
  242. package/src/lib/e2e/compose-env.js +5 -66
  243. package/src/lib/e2e/stack-teardown.js +6 -40
  244. package/src/lib/generators/architecture-scanner.js +18 -156
  245. package/src/lib/generators/handoff-generator.js +37 -112
  246. package/src/lib/generators/scaffold-generator.js +5 -101
  247. package/src/lib/generators/settings-generator.js +11 -122
  248. package/src/lib/git/branch-classifier.js +11 -84
  249. package/src/lib/git/default-branch.js +14 -169
  250. package/src/lib/git/evidence-staleness.js +2 -1
  251. package/src/lib/git/gh.js +2 -17
  252. package/src/lib/git/pending-prs.js +6 -37
  253. package/src/lib/git/pr-files.js +3 -28
  254. package/src/lib/git/pr-index.js +3 -34
  255. package/src/lib/git/tree-fingerprint.js +17 -17
  256. package/src/lib/git/tree-state.js +6 -33
  257. package/src/lib/graph/god-nodes.js +2 -22
  258. package/src/lib/graph/graph-health.js +3 -24
  259. package/src/lib/graph/graph-loader.js +5 -56
  260. package/src/lib/graph/graphify-runtime.js +7 -37
  261. package/src/lib/graph/graphifyignore.js +8 -74
  262. package/src/lib/graph/sanitize.js +16 -136
  263. package/src/lib/heavy/cleanup.js +2 -6
  264. package/src/lib/installers/effects.js +8 -93
  265. package/src/lib/installers/mcp-installer.js +9 -169
  266. package/src/lib/installers/memory-installer.js +8 -33
  267. package/src/lib/installers/templates-reconcile.js +5 -60
  268. package/src/lib/mutation/commands.js +3 -2
  269. package/src/lib/mutation/dotnet-introspection.js +281 -0
  270. package/src/lib/mutation/plan.js +14 -6
  271. package/src/lib/mutation/runner.js +1 -1
  272. package/src/lib/mutation/subset.js +47 -13
  273. package/src/lib/plan/artifact-vocabulary.js +40 -0
  274. package/src/lib/plan/checks.js +208 -0
  275. package/src/lib/plan/plan-parser.js +204 -0
  276. package/src/lib/plan/plan-template.js +18 -0
  277. package/src/lib/projects/registry.js +7 -44
  278. package/src/lib/review-reports.js +204 -0
  279. package/src/lib/risk-detector.js +131 -274
  280. package/src/lib/specs/adr-index.js +38 -85
  281. package/src/lib/specs/design-seed.js +18 -0
  282. package/src/lib/specs/frontmatter.js +31 -0
  283. package/src/lib/specs/gfm-table.js +11 -0
  284. package/src/lib/specs/revocations.js +31 -102
  285. package/src/lib/specs/screen-checklist.js +56 -132
  286. package/src/lib/stack/dotnet-build-ignores.js +43 -0
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +260 -0
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +37 -0
  291. package/src/lib/standards/sections.js +47 -0
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +23 -179
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -313
  296. package/src/lib/tasks/test-runner.js +107 -447
  297. package/src/lib/telemetry/metrics.js +13 -103
  298. package/src/lib/telemetry/telemetry-reader.js +13 -238
  299. package/src/lib/telemetry/verify-event.js +4 -34
  300. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  301. package/src/lib/validators/code-budget/index.js +0 -7
  302. package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
  303. package/src/lib/validators/contracts/index.js +0 -6
  304. package/src/lib/validators/nextjs/index.js +0 -5
  305. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  306. package/src/lib/validators/packages/index.js +0 -6
  307. package/src/lib/validators/packages/package-validator.js +6 -56
  308. package/src/lib/validators/reuse/index.js +0 -8
  309. package/src/lib/validators/reuse/reuse-validator.js +24 -137
  310. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  311. package/src/lib/validators/traceability-check.js +54 -156
  312. package/src/lib/validators/validation-runner.js +20 -125
  313. package/src/lib/validators/verify-runner.js +152 -575
  314. package/src/lib/work-type-detector.js +5 -67
  315. package/src/lib/worktree/docker-audit.js +17 -137
  316. package/src/lib/worktree/env-allocator.js +10 -98
  317. package/src/lib/worktree/feature-truth.js +12 -115
  318. package/src/lib/worktree/finish-backup.js +32 -242
  319. package/src/lib/worktree/fleet-scanner.js +31 -126
  320. package/src/lib/worktree/ignored-triage.js +15 -91
  321. package/src/lib/worktree/vscode-settings.js +2 -3
  322. package/src/lib/worktree/worktree-audit.js +21 -213
  323. package/src/lib/worktree/worktree-include.js +3 -5
  324. package/src/scripts/global-install.js +3 -9
  325. package/src/scripts/install-plugin.js +1 -13
  326. package/src/scripts/setup-infra.js +17 -138
  327. package/src/utils/agents-installer.js +92 -272
  328. package/src/utils/banner.js +4 -25
  329. package/src/utils/claude-md-injector.js +1 -37
  330. package/src/utils/claude-settings-manager.js +1 -87
  331. package/src/utils/file-copier.js +32 -90
  332. package/src/utils/hooks-installer.js +26 -296
  333. package/src/utils/skills-installer.js +4 -102
  334. package/src/utils/version-checker.js +4 -83
  335. package/framework/commands/morph-proposal.md +0 -57
  336. package/framework/evals/trace-schema.md +0 -67
  337. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  338. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  339. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  340. package/framework/hooks/shared/tasks-validator.js +0 -111
  341. package/framework/rules/morph-task-tracking.md +0 -54
  342. package/framework/rules/morph-workflow.md +0 -244
  343. package/framework/schemas/tasks.schema.json +0 -79
  344. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  345. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  346. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  347. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  348. package/framework/templates/docs/proposal.md +0 -192
  349. package/framework/templates/docs/spec.md +0 -57
  350. package/framework/templates/docs/ui-components.md +0 -52
  351. package/framework/templates/docs/ui-design-system.md +0 -76
  352. package/framework/templates/docs/ui-flows.md +0 -167
  353. package/framework/templates/examples/spec-examples.md +0 -91
  354. package/framework/templates/feature/decisions.md +0 -124
  355. package/framework/templates/feature/recap.md +0 -145
  356. package/framework/templates/tasks.template.json +0 -17
  357. package/src/commands/state/usecase.js +0 -143
  358. package/src/commands/tasks/dag.js +0 -181
  359. package/src/commands/utils/index.js +0 -3
  360. package/src/lib/agents/output-collector.js +0 -76
  361. package/src/lib/detectors/design-system-detector.js +0 -188
  362. package/src/lib/evaluator-report.js +0 -104
  363. package/src/lib/generators/design-system-generator.js +0 -508
  364. package/src/lib/specs/acervo-format.js +0 -70
  365. package/src/lib/specs/acervo-lifecycle.js +0 -264
  366. package/src/lib/specs/done-criteria-check.js +0 -200
  367. package/src/lib/specs/usecase-check.js +0 -301
  368. package/src/lib/specs/usecase-promoter.js +0 -301
  369. package/src/lib/specs/usecase-registry.js +0 -237
  370. package/src/lib/tasks/dispatch-planner.js +0 -319
  371. package/src/lib/tasks/dotnet-test-introspection.js +0 -632
  372. package/src/lib/tasks/mermaid-renderer.js +0 -132
  373. package/src/lib/tasks/task-classifier.js +0 -30
  374. package/src/lib/tasks/task-parser.js +0 -209
  375. package/src/lib/tasks/tasks-json-parser.js +0 -296
  376. package/src/lib/tasks/test-scope.js +0 -597
  377. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,277 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * trace-autogen.js — PostToolUse hook (Write|Edit)
4
- *
5
- * Auto-generates the execution trace
6
- * `.morph/traces/YYYY-MM-DD-{feature}-implement.json` from tasks.json +
7
- * recap.md whenever tasks.json is written during
8
- * the implement phase.
9
- *
10
- * This removes the manual trace-writing step from the skills (friction item
11
- * observed in MORPH_GHLBrain 2026-07-11: 5 writes per task). The tasks.json
12
- * status flip — the last write of every task's ceremony — now produces the
13
- * trace for free: deterministic, idempotent, always in sync with the real
14
- * on-disk state. The skills must NOT write traces by hand anymore.
15
- *
16
- * Sources:
17
- * - tasks.json → task set, status (done→completed, skipped→skipped,
18
- * blocked→failed; pending/in_progress stay out), outputs
19
- * - recap.md → per-task `**Standard:**` id and `**Persona:**` line
20
- *
21
- * The most recent existing `*-{feature}-implement.json` is updated in place
22
- * (session_id/started_at preserved); a new dated file is created otherwise.
23
- * `completed_at` + phase_summary are stamped when no task is left
24
- * pending/in_progress. Fail-open: any error exits 0 silently.
25
- *
26
- * This is also the vantage point for TaskStart/TaskEnd telemetry: a
27
- * pending→in_progress flip and a flip to done/skipped are the only two task
28
- * transitions the harness can observe (tasks.json is the single place task
29
- * status is written), so this hook diffs each task's previous status against
30
- * `.morph/logs/task-times.json` and appends the matching event via the shared
31
- * telemetry logger (see `updateTaskTimes` below). The same start/end pair
32
- * feeds `duration_seconds` in the trace entry.
33
- */
34
-
35
- import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
36
- import { join } from 'path';
37
- import { runStandalone } from '../../shared/hook-response.js';
38
- import { stateExists } from '../../shared/state-reader.js';
39
- import { getFilePath, resolveMorphRoot } from '../../shared/payload-utils.js';
40
- import { appendTelemetryEvent } from '../../shared/telemetry-logger.js';
41
-
42
- function projectDir() {
43
- return process.env.CLAUDE_PROJECT_DIR || process.cwd();
44
- }
45
-
46
- function extractFeature(filePath) {
47
- const normalized = String(filePath).replace(/\\/g, '/');
48
- const match = normalized.match(/\.morph\/features\/([^/]+)\/2-plan\/tasks\.json$/);
49
- return match ? match[1] : null;
50
- }
51
-
52
- /**
53
- * Parse recap.md task blocks (`## T{N} — ...`) into
54
- * { [id]: { standard, persona } }. Best-effort: missing fields stay null.
55
- */
56
- function parseRecap(raw) {
57
- const map = {};
58
- const blocks = String(raw).split(/^## (?=T\d+)/m).slice(1);
59
- for (const block of blocks) {
60
- const id = (block.match(/^(T\d+)/) ?? [])[1];
61
- if (!id) continue;
62
- const standard = (block.match(/\*\*Standard:\*\*\s*([^\s(]+)/) ?? [])[1] ?? null;
63
- const persona = (block.match(/\*\*Persona:\*\*\s*(.+)$/m) ?? [])[1]?.trim() ?? null;
64
- map[id] = { standard, persona };
65
- }
66
- return map;
67
- }
68
-
69
- /** session_id in the trace-schema format YYYY-MM-DD-HH-MM-SS. */
70
- function sessionId(d) {
71
- const p = (n) => String(n).padStart(2, '0');
72
- return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}-${p(d.getHours())}-${p(d.getMinutes())}-${p(d.getSeconds())}`;
73
- }
74
-
75
- /** tasks.json status → trace-schema status. Unsettled statuses are omitted. */
76
- const STATUS_MAP = { done: 'completed', skipped: 'skipped', blocked: 'failed' };
77
-
78
- const TASK_TIMES_PATH = join('.morph', 'logs', 'task-times.json');
79
-
80
- /**
81
- * Read `.morph/logs/task-times.json`: `{ [feature]: { [taskId]: { status,
82
- * start?, end? } } }`. Nested by feature — not flat by task id, as a literal
83
- * reading of "keyed by taskId" might suggest — because task ids (T1, T2…)
84
- * are only unique WITHIN a feature; a flat map would let two sibling
85
- * features in the same tree (see worktree-state-resolution.test.js for that
86
- * shape) clobber each other's start/end timestamps.
87
- * Fail-open: a missing/corrupt file reads as {}.
88
- */
89
- function readTaskTimes(root) {
90
- const p = join(root, TASK_TIMES_PATH);
91
- if (!existsSync(p)) return {};
92
- try {
93
- return JSON.parse(readFileSync(p, 'utf8'));
94
- } catch {
95
- return {};
96
- }
97
- }
98
-
99
- function writeTaskTimes(root, allTimes) {
100
- mkdirSync(join(root, '.morph', 'logs'), { recursive: true });
101
- writeFileSync(join(root, TASK_TIMES_PATH), JSON.stringify(allTimes, null, 2), 'utf8');
102
- }
103
-
104
- /**
105
- * Diff each task's PREVIOUS status (from `times`, this feature's slice of
106
- * task-times.json) against its status in the tasks.json that was just
107
- * written, and append the matching telemetry event via the shared logger —
108
- * same shape/mechanism as every other event in events.jsonl, just a
109
- * different `event` value.
110
- *
111
- * - pending → in_progress ⇒ TaskStart, stamp `start`
112
- * - anything → done/skipped ⇒ TaskEnd (carries the new status), stamp `end`
113
- * - first sight of a task id (no prior entry) only seeds the baseline,
114
- * silently — there is no prior status to diff, and emitting an event here
115
- * would fabricate a transition that never happened (e.g. a task whose
116
- * first-ever tasks.json write already shows it `done`, from historical
117
- * data predating this file).
118
- *
119
- * Mutates `times` in place and returns whether anything changed, so the
120
- * caller only rewrites task-times.json when needed and the entries built
121
- * right after this call see the freshly-stamped start/end for
122
- * `duration_seconds`.
123
- */
124
- function updateTaskTimes(times, tasks, feature, root, nowIso) {
125
- let changed = false;
126
- for (const t of tasks) {
127
- const prev = times[t.id];
128
- if (!prev) {
129
- times[t.id] = { status: t.status };
130
- changed = true;
131
- continue;
132
- }
133
- if (prev.status === t.status) continue;
134
-
135
- if (prev.status === 'pending' && t.status === 'in_progress') {
136
- appendTelemetryEvent({ event: 'TaskStart', feature, task: t.id, phase: 'implement' }, root);
137
- times[t.id] = { ...prev, status: t.status, start: nowIso };
138
- } else if (t.status === 'done' || t.status === 'skipped') {
139
- appendTelemetryEvent({ event: 'TaskEnd', feature, task: t.id, status: t.status, phase: 'implement' }, root);
140
- times[t.id] = { ...prev, status: t.status, end: nowIso };
141
- } else {
142
- times[t.id] = { ...prev, status: t.status };
143
- }
144
- changed = true;
145
- }
146
- return changed;
147
- }
148
-
149
- /** Seconds between recorded start/end for a task, or null when either is missing. */
150
- function taskDuration(times, taskId) {
151
- const rec = times[taskId];
152
- if (!rec?.start || !rec?.end) return null;
153
- const secs = (new Date(rec.end).getTime() - new Date(rec.start).getTime()) / 1000;
154
- return Number.isFinite(secs) ? Math.round(secs * 10) / 10 : null;
155
- }
156
-
157
- /**
158
- * @param {object} payload - parsed PostToolUse stdin payload (Write|Edit)
159
- * @returns {Promise<null>} always null — this hook is pure side-effect (trace
160
- * file + task-times.json), never prints/injects context.
161
- */
162
- export async function run(payload) {
163
- try {
164
- await generateTrace(payload);
165
- } catch {
166
- // Fail-open
167
- }
168
- return null;
169
- }
170
-
171
- async function generateTrace(payload) {
172
- if (!payload) return;
173
-
174
- const filePath = getFilePath(payload);
175
- const feature = filePath ? extractFeature(filePath) : null;
176
- if (!feature) return;
177
-
178
- // Read the sources — and write the trace — in the tree that owns the written
179
- // tasks.json. Resolving from the session instead made this hook INERT for
180
- // every worktree feature: the root's copy of the feature has no
181
- // `2-plan/tasks.json`, so the `existsSync` guard below returned silently and
182
- // no trace was ever produced for work done in a worktree.
183
- const cwd = resolveMorphRoot(filePath, projectDir());
184
- if (!stateExists(cwd)) return;
185
-
186
- const featureDir = join(cwd, '.morph', 'features', feature);
187
- const tasksPath = join(featureDir, '2-plan', 'tasks.json');
188
- if (!existsSync(tasksPath)) return;
189
- const tasksDoc = JSON.parse(readFileSync(tasksPath, 'utf8'));
190
- const tasks = Array.isArray(tasksDoc.tasks) ? tasksDoc.tasks : [];
191
-
192
- const now = new Date();
193
-
194
- // TaskStart/TaskEnd telemetry + duration_seconds source: diff each task's
195
- // previous status (persisted below) against the status just written.
196
- const allTimes = readTaskTimes(cwd);
197
- const times = allTimes[feature] ?? (allTimes[feature] = {});
198
- if (updateTaskTimes(times, tasks, feature, cwd, now.toISOString())) {
199
- writeTaskTimes(cwd, allTimes);
200
- }
201
-
202
- let recap = {};
203
- const recapPath = join(featureDir, '3-implement', 'recap.md');
204
- if (existsSync(recapPath)) {
205
- try { recap = parseRecap(readFileSync(recapPath, 'utf8')); } catch { recap = {}; }
206
- }
207
-
208
- const tracesDir = join(cwd, '.morph', 'traces');
209
- mkdirSync(tracesDir, { recursive: true });
210
-
211
- // Multi-session phases update the same file incrementally (trace-schema):
212
- // reuse the most recent trace for this feature+phase if one exists.
213
- const suffix = `-${feature}-implement.json`;
214
- const existing = readdirSync(tracesDir)
215
- .filter(f => /^\d{4}-\d{2}-\d{2}-/.test(f) && f.endsWith(suffix))
216
- .sort()
217
- .pop();
218
-
219
- let previous = null;
220
- if (existing) {
221
- try { previous = JSON.parse(readFileSync(join(tracesDir, existing), 'utf8')); } catch { previous = null; }
222
- }
223
-
224
- const fileName = existing ?? `${now.toISOString().slice(0, 10)}${suffix}`;
225
-
226
- const entries = tasks
227
- .filter(t => STATUS_MAP[t.status])
228
- .map(t => {
229
- const recapEntry = recap[t.id] ?? {};
230
- return {
231
- id: t.id,
232
- title: t.title ?? null,
233
- status: STATUS_MAP[t.status],
234
- standards_consulted: recapEntry.standard ? [recapEntry.standard] : [],
235
- agent_persona: recapEntry.persona ?? null,
236
- sub_agent_dispatched: recapEntry.persona
237
- ? /^morph-|general-purpose/.test(recapEntry.persona)
238
- : false,
239
- files_modified: Array.isArray(t.outputs) ? t.outputs.filter(o => typeof o === 'string') : [],
240
- duration_seconds: taskDuration(times, t.id),
241
- };
242
- });
243
-
244
- const allSettled = tasks.length > 0 &&
245
- tasks.every(t => t.status !== 'pending' && t.status !== 'in_progress');
246
-
247
- const standardCounts = {};
248
- for (const entry of entries) {
249
- for (const std of entry.standards_consulted) {
250
- standardCounts[std] = (standardCounts[std] ?? 0) + 1;
251
- }
252
- }
253
- const mostUsed = Object.entries(standardCounts)
254
- .sort((a, b) => b[1] - a[1])
255
- .slice(0, 3)
256
- .map(([std]) => std);
257
-
258
- const trace = {
259
- schema_version: '1.0',
260
- session_id: previous?.session_id ?? sessionId(now),
261
- feature,
262
- phase: 'implement',
263
- started_at: previous?.started_at ?? now.toISOString(),
264
- completed_at: allSettled ? (previous?.completed_at ?? now.toISOString()) : null,
265
- tasks: entries,
266
- phase_summary: {
267
- tasks_completed: entries.filter(e => e.status === 'completed').length,
268
- tasks_failed: entries.filter(e => e.status === 'failed').length,
269
- standards_most_used: mostUsed,
270
- sub_agents_dispatched: entries.filter(e => e.sub_agent_dispatched).length,
271
- },
272
- };
273
-
274
- writeFileSync(join(tracesDir, fileName), JSON.stringify(trace, null, 2), 'utf8');
275
- }
276
-
277
- await runStandalone(run);
@@ -1,50 +0,0 @@
1
- #!/bin/bash
2
- # Scope: universal | Validates spec.md required sections
3
- # MORPH-SPEC Pre-Commit Hook: Spec Validation
4
- # Validates that spec.md files have required sections
5
-
6
- echo "🔍 Validating spec files..."
7
-
8
- # Find modified spec.md files
9
- SPEC_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep 'spec\.md$')
10
-
11
- if [ -z "$SPEC_FILES" ]; then
12
- echo "✓ No spec files modified"
13
- exit 0
14
- fi
15
-
16
- HAS_ERRORS=false
17
-
18
- for spec_file in $SPEC_FILES; do
19
- echo "Checking: $spec_file"
20
-
21
- # Required sections
22
- REQUIRED_SECTIONS=(
23
- "## 📋 Metadata"
24
- "## 🎯 Overview"
25
- "## 🏗️ Technical Design"
26
- "## ✅ Acceptance Criteria"
27
- )
28
-
29
- for section in "${REQUIRED_SECTIONS[@]}"; do
30
- if ! grep -q "$section" "$spec_file"; then
31
- echo " ❌ Missing section: $section"
32
- HAS_ERRORS=true
33
- fi
34
- done
35
-
36
- # Check if has at least one user story or requirement
37
- if ! grep -qi "user story\|requirement\|acceptance criteria" "$spec_file"; then
38
- echo " ⚠️ Warning: No user stories or requirements found"
39
- fi
40
- done
41
-
42
- if [ "$HAS_ERRORS" = true ]; then
43
- echo ""
44
- echo "❌ COMMIT BLOCKED: spec.md files are incomplete"
45
- echo " Add missing sections before committing"
46
- exit 1
47
- fi
48
-
49
- echo "✓ All spec files are valid"
50
- exit 0
@@ -1,111 +0,0 @@
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
- /** Human-readable type name for error messages ('array', not 'object'). */
23
- function typeName(value) {
24
- return Array.isArray(value) ? 'array' : typeof value;
25
- }
26
-
27
- export function validateTasksDoc(doc) {
28
- if (!doc || typeof doc !== 'object' || !Array.isArray(doc.tasks)) {
29
- return { valid: false, errors: ['tasks.json must be an object with a tasks[] array'] };
30
- }
31
-
32
- const errors = [];
33
- for (const task of doc.tasks) {
34
- const id = task?.id ?? '?';
35
- if (!VALID_TASK_STATUS.includes(task?.status)) {
36
- errors.push(`task '${id}': invalid status '${task?.status}' (valid: ${VALID_TASK_STATUS.join(', ')})`);
37
- }
38
- if (task?.effort !== undefined && !VALID_TASK_EFFORT.includes(task.effort)) {
39
- errors.push(`task '${id}': invalid effort '${task.effort}' (valid: ${VALID_TASK_EFFORT.join(', ')})`);
40
- }
41
- // Mutable-field type checks with the exact field/value in the message —
42
- // a malformed notes/outputs used to surface as the misleading generic
43
- // "notes is empty" downstream (MORPH_GHLBrain 2026-07-11).
44
- if (task?.notes !== undefined && typeof task.notes !== 'string') {
45
- errors.push(`task '${id}': 'notes' must be a string (got ${typeName(task.notes)})`);
46
- }
47
- if (task?.outputs !== undefined) {
48
- if (!Array.isArray(task.outputs)) {
49
- errors.push(`task '${id}': 'outputs' must be an array of path strings (got ${typeName(task.outputs)})`);
50
- } else {
51
- task.outputs.forEach((entry, i) => {
52
- if (typeof entry !== 'string') {
53
- errors.push(`task '${id}': outputs[${i}] must be a string path (got ${typeName(entry)})`);
54
- }
55
- });
56
- }
57
- }
58
- }
59
- return { valid: errors.length === 0, errors };
60
- }
61
-
62
- /**
63
- * Planned-vs-real outputs divergence: tasks whose `outputs` changed relative
64
- * to the previous document but whose `notes` is still empty. The divergence
65
- * itself is legitimate (outputs is a mutable progress field) — shipping it
66
- * without an explanatory note is what gets flagged.
67
- *
68
- * @param {object} oldDoc - parsed previous tasks.json
69
- * @param {object} newDoc - parsed proposed tasks.json
70
- * @returns {string[]} human-readable violation messages (empty = OK)
71
- */
72
- /** Compact outputs rendering for messages, capped so huge arrays stay readable. */
73
- function fmtOutputs(outputs) {
74
- const s = JSON.stringify(outputs ?? []);
75
- return s.length > 120 ? s.slice(0, 117) + '...' : s;
76
- }
77
-
78
- export function outputsDivergenceViolations(oldDoc, newDoc) {
79
- const violations = [];
80
- const oldById = new Map((oldDoc?.tasks ?? []).map(t => [t.id, t]));
81
-
82
- for (const task of newDoc?.tasks ?? []) {
83
- const old = oldById.get(task.id);
84
- if (!old) continue;
85
- const outputsChanged =
86
- JSON.stringify(old.outputs ?? []) !== JSON.stringify(task.outputs ?? []);
87
- if (!outputsChanged) continue;
88
-
89
- // A filled string note explains the divergence — done.
90
- if (typeof task.notes === 'string' && task.notes.trim().length > 0) continue;
91
-
92
- // Root-cause specific messages: a malformed notes value is a DIFFERENT
93
- // failure than a missing one — claiming "notes is empty" when the agent
94
- // just wrote an array reads as a false positive and burns a retry cycle.
95
- if (task.notes !== undefined && typeof task.notes !== 'string') {
96
- violations.push(
97
- `task '${task.id}': outputs diverged from the plan and 'notes' is not a string ` +
98
- `(got ${typeName(task.notes)}) — write the explanation as a single string in 'notes'`
99
- );
100
- continue;
101
- }
102
-
103
- violations.push(
104
- `task '${task.id}': outputs diverged from the plan ` +
105
- `(${fmtOutputs(old.outputs)} → ${fmtOutputs(task.outputs)}) but 'notes' is empty — ` +
106
- `record why in 'notes' in the same edit. ` +
107
- `Deletions and annotations belong in 'notes', not in 'outputs' (outputs = produced paths only).`
108
- );
109
- }
110
- return violations;
111
- }
@@ -1,54 +0,0 @@
1
- ---
2
- stacks:
3
- - "*"
4
- ---
5
-
6
- # MORPH-SPEC Task Tracking
7
-
8
- > Progress lives in `tasks.json` — nowhere else. There is no CLI command to mark a task done and
9
- > no session-level checklist to keep in sync: this file IS the record of what happened.
10
-
11
- ## Persisting Progress to `tasks.json`
12
-
13
- To record task progress for the feature, write `.morph/features/{feature}/2-plan/tasks.json`,
14
- touching ONLY the mutable fields:
15
-
16
- - `status` — `pending` → `in_progress` → `done` (também: `skipped`, `blocked`)
17
- - `outputs` — the files the task produced
18
- - `notes` — free-text record (deviations, context, resolved blockers)
19
-
20
- > **O status terminal é `done`.** `completed` **não existe** no enum de
21
- > `.morph/framework/schemas/tasks.schema.json` — é vocabulário de outro sistema, e o hook
22
- > `protect-spec-files` rejeita a gravação.
23
-
24
- The plan contract — `title`, `description`, `dependencies`, `effort`, `doneCriteria`,
25
- `group`, `agent`, `standards` — is frozen after Gate 2. Any attempt to change it is
26
- blocked by the `protect-spec-files` hook. `agent` (persona id, no `morph-` prefix)
27
- and `standards` (paths pinned by morph-plan) are optional — legacy plans without
28
- them remain valid.
29
-
30
- Use the `Write` tool (whole-file rewrite) OR the `Edit` tool — `Edit` is allowed
31
- as long as it touches only `status`, `outputs`, and `notes`. State is then synced
32
- automatically by the `state-sync` PostToolUse hook; you do not call a CLI to mark
33
- output.
34
-
35
- Update each task as you go — `in_progress` when you pick it up, `done` when it is finished —
36
- rather than batching every write to the end of the phase. `morph-spec status`, `dag`, `verify`
37
- and the dashboard all read this file, so a stale `tasks.json` is a blind orchestrator.
38
-
39
- ## Deviation Tracking
40
-
41
- When completing a task differently than originally planned, record the reason in the
42
- task's `notes` field. `notes` is **uma única string** (o schema declara
43
- `"notes": { "type": "string" }`) — para registrar mais de um desvio, separe-os com
44
- `;` ou `\n` dentro da mesma string. Um array é **rejeitado** pelo validador, com a
45
- mensagem `'notes' must be a string`.
46
-
47
- ```json
48
- {
49
- "id": "T3",
50
- "status": "done",
51
- "outputs": ["Features/User/Update/Handler.cs"],
52
- "notes": "deviation: split validation into a separate validator for testability; blocker-resolved: EF migration conflict fixed by regenerating the snapshot"
53
- }
54
- ```