@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
@@ -46,11 +46,10 @@ If `.morph/framework/agents.json` is in staged files:
46
46
  - No duplicate agentIds
47
47
  - Valid tier values (1-4)
48
48
 
49
- ### 4. Spec Format Validation (if spec.md staged)
50
- If any `spec.md` is staged:
51
- - Has required sections: Overview, Requirements, Technical Design, Data Model, API Contracts
49
+ ### 4. Plan Format Validation (if plan.md staged)
50
+ If any `.morph/features/*/2-plan/plan.md` is staged:
51
+ - `morph-spec plan-check <feature>` exits 0 (sections, scenarios with `prova:`, slices that cover them)
52
52
  - No placeholder text (`{{TODO}}`, `TBD`, `[PLACEHOLDER]`)
53
- - Word count > 200 (meaningful spec)
54
53
 
55
54
  ### 5. Stack-Specific Validation
56
55
  {{#if IS_CSHARP}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polymorphism-tech/morph-spec",
3
- "version": "8.47.0",
3
+ "version": "9.1.0",
4
4
  "description": "MORPH-SPEC: NLH (Natural Language Harness) for spec-driven development with Claude Code",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -1,22 +1,6 @@
1
- /**
2
- * CLI handler for `morph-spec context refresh`
3
- *
4
- * Scans the project filesystem for architectural patterns and generates
5
- * a rich .morph/context/README.md that Claude reads at session start.
6
- *
7
- * @module context-refresh
8
- */
9
-
10
1
  import { scanArchitecture, renderArchitectureContext } from '../../lib/generators/architecture-scanner.js';
11
2
  import { logger } from '../../utils/logger.js';
12
3
 
13
- /**
14
- * @param {Object} options - Commander options
15
- * @param {string} [options.path] - Target project path
16
- * @param {boolean} [options.verbose] - Show detailed output
17
- * @param {boolean} [options.json] - Output raw JSON
18
- * @param {boolean} [options.quiet] - Suppress output (for programmatic use)
19
- */
20
4
  export async function contextRefreshCommand(options = {}) {
21
5
  const projectPath = options.path || process.cwd();
22
6
 
@@ -8,19 +8,14 @@ import { registerProject } from '../../lib/projects/registry.js';
8
8
 
9
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
10
 
11
- /** Where the hash of the package.json that produced node_modules is kept. */
12
11
  export const DEPS_STAMP = '.morph-deps-stamp';
13
12
 
14
- /** Hash of the dashboard's package.json — the dependency set it declares. */
15
13
  export function depsHash(dashboardDir) {
16
14
  return createHash('sha256').update(readFileSync(join(dashboardDir, 'package.json'))).digest('hex');
17
15
  }
18
16
 
19
- /**
20
- * Does the dashboard need an `npm install`? Not only when node_modules is
21
- * missing: an install made before a dependency was added fails at import time,
22
- * so node_modules must have been produced by THIS package.json.
23
- */
17
+ // An install made before a dependency was added fails at import time, so
18
+ // node_modules must have been produced by THIS package.json, not merely exist.
24
19
  export function needsInstall(dashboardDir) {
25
20
  const stamp = join(dashboardDir, 'node_modules', DEPS_STAMP);
26
21
  if (!existsSync(stamp)) return true;
@@ -48,7 +43,6 @@ export async function dashboardCommand(options = {}) {
48
43
  writeFileSync(join(dashboardDir, 'node_modules', DEPS_STAMP), depsHash(dashboardDir));
49
44
  }
50
45
 
51
- // Opening the dashboard in a project puts it in the portfolio.
52
46
  registerProject(process.cwd(), 'dashboard');
53
47
 
54
48
  console.log(chalk.cyan(`\n Starting morph-spec dashboard at http://localhost:${port}\n`));
@@ -1,22 +1,6 @@
1
- /**
2
- * `morph-spec e2e up|status|down` — lifecycle of the verification stack.
3
- *
4
- * The stack is deliberately NOT torn down between verifies: cold-starting a
5
- * .NET container per task is the difference between a loop people run and one
6
- * they disable. `up` brings it (or reuses it), `status` says whether what is
7
- * running still matches the working tree, and `down` releases it — which
8
- * `morph-spec finish` also does as part of teardown.
9
- *
10
- * Every subcommand acts in the TREE THAT OWNS THE FEATURE: run from the
11
- * primary root for a feature that lives in a worktree, the compose file, the
12
- * bind mount and the stamp are all the worktree's — the root's copy is a
13
- * different checkout of different code.
14
- *
15
- * All the real logic lives in framework/hooks/shared/e2e-check.js, shared with
16
- * the `verify` e2e node so the two can never drift.
17
- *
18
- * @module commands/e2e
19
- */
1
+ // The stack is deliberately NOT torn down between verifies: a cold .NET start per task is the
2
+ // difference between a loop people run and one they disable. Every subcommand acts in the tree
3
+ // that owns the feature.
20
4
 
21
5
  import chalk from 'chalk';
22
6
  import { execFileSync } from 'child_process';
@@ -46,22 +30,10 @@ import { createHeavyGate, HeavyBusyError } from '../../lib/heavy/gate.js';
46
30
  import { afterHeavyRun } from '../../lib/heavy/cleanup.js';
47
31
  import { readHeavyConfig } from '../../../framework/hooks/shared/heavy-config.js';
48
32
 
49
- /** Exit code for a usage error, matching `verify`'s convention. */
50
33
  const EXIT_USAGE = 2;
51
34
  const EXIT_BUSY = 3;
52
35
 
53
- // ============================================================================
54
- // Shared context
55
- // ============================================================================
56
-
57
- /**
58
- * Everything the three subcommands need, resolved once — against the tree
59
- * that owns the feature (its live worktree when it has one, the root
60
- * otherwise).
61
- *
62
- * Returns null (after printing) when that tree has no compose file — a usage
63
- * error here, unlike in the verify node where it is a graded skip.
64
- */
36
+ // No compose file is a usage error here, unlike in the verify node where it is a graded skip.
65
37
  function resolveContext(featureName, { json = false } = {}) {
66
38
  const root = resolvePrimaryRoot(process.cwd());
67
39
  const found = findWorktreeForFeature(featureName, root);
@@ -95,49 +67,24 @@ function resolveContext(featureName, { json = false } = {}) {
95
67
  };
96
68
  }
97
69
 
98
- /**
99
- * Compose environment: the port block this feature owns, plus any credentials
100
- * from the gitignored auth env file.
101
- *
102
- * The port block comes from `buildComposeEnv` rather than being assembled here
103
- * so this command and the `e2e` node inside `verify` agree by construction —
104
- * isolation that exists only in the command nobody runs is no isolation at all.
105
- */
70
+ // The port block comes from `buildComposeEnv`, so this command and the verify node agree by
71
+ // construction.
106
72
  function buildEnv({ cwd, config, featureName, projectName, gitCommonDir }) {
107
- // COMPOSE_PROJECT_NAME is part of the port layout now; the explicit key stays
108
- // as the fail-open floor for a repo with no git common dir (no block → no
109
- // layout), where the project name is still what isolates the containers.
73
+ // The explicit key is the floor for a repo with no git common dir (no port layout).
110
74
  return {
111
75
  COMPOSE_PROJECT_NAME: projectName,
112
76
  ...buildComposeEnv({ cwd, config, featureName, gitCommonDir }),
113
77
  };
114
78
  }
115
79
 
116
- // ============================================================================
117
- // up
118
- // ============================================================================
119
-
120
- /**
121
- * `morph-spec e2e up <feature> [--fresh]`
122
- *
123
- * `--fresh` is destructive on purpose: it tears the previous stack down WITH
124
- * its named volumes and forces the auth seed to run again. A rebuild that
125
- * preserved the database would hand the next suite the previous run's rows,
126
- * and a storageState captured against that database would keep authenticating
127
- * a user that no longer exists.
128
- *
129
- * Exit codes: 0 = stack healthy · 1 = failed to come up (or auth broke, or the
130
- * compose cannot be isolated inside a worktree) · 2 = usage (no compose file /
131
- * no docker).
132
- */
80
+ // `--fresh` drops the named volumes and reruns the auth seed: a preserved database would hand the
81
+ // next suite stale rows and a storageState for a user that no longer exists.
133
82
  export async function e2eUpCommand(featureName, options = {}) {
134
83
  const ctx = resolveContext(featureName, options);
135
84
  if (!ctx) process.exit(EXIT_USAGE);
136
85
 
137
- // Isolation BEFORE the docker check: `COMPOSE_PROJECT_NAME` isolates
138
- // container names but not host ports, and a compose that hardcodes them (or
139
- // names its containers) collides with the sibling worktree no matter what we
140
- // inject. Same rule as the verify node — see decideIsolation().
86
+ // Isolation BEFORE the docker check: a compose that hardcodes host ports or container names
87
+ // collides with a sibling worktree whatever we inject.
141
88
  const lint = lintComposeIsolation(readComposeText(ctx.cwd, ctx.composeFile));
142
89
  const isolation = decideIsolation({ findings: lint.findings, insideWorktree: ctx.insideWorktree });
143
90
  if (isolation.status === 'fail') {
@@ -189,8 +136,7 @@ export async function e2eUpCommand(featureName, options = {}) {
189
136
  ? prepareAuth(ctx.cwd, { config: ctx.config, env: ctx.env, forceSeed: Boolean(options.fresh) })
190
137
  : { status: 'skip', reason: null };
191
138
 
192
- // Same warnings[] contract as the verify e2e node: a seedCommand that never
193
- // runs because `mode` switches it off is reported, never swallowed.
139
+ // A seedCommand that `mode` switches off is reported, never swallowed.
194
140
  const warnings = [];
195
141
  if (isolation.status === 'warn') warnings.push(isolation.reason);
196
142
  if (auth.warning) warnings.push(auth.warning);
@@ -225,32 +171,13 @@ export async function e2eUpCommand(featureName, options = {}) {
225
171
  process.exit(failed ? 1 : 0);
226
172
  }
227
173
 
228
- // ============================================================================
229
- // status
230
- // ============================================================================
231
-
232
- /**
233
- * Where the app actually landed.
234
- *
235
- * The reviewer hands this to the evaluator instead of letting it guess
236
- * `localhost:3000` — each feature owns a port block, so the default port is the
237
- * one address most likely to be wrong. Mirrors what `playwright.config.ts` is
238
- * asked to do, deliberately: one rule, two readers.
239
- *
240
- * @param {Record<string,string>} env
241
- * @returns {string}
242
- */
174
+ // Handed to the verifier so it never guesses `localhost:3000`: each feature owns a port block.
175
+ // Mirrors what `playwright.config.ts` does: one rule, two readers.
243
176
  export function resolveBaseUrl(env = {}) {
244
177
  return env.PLAYWRIGHT_BASE_URL ?? `http://localhost:${env.PORT ?? 3000}`;
245
178
  }
246
179
 
247
- /**
248
- * `morph-spec e2e status <feature> [--json]`
249
- *
250
- * Answers the question that matters before trusting a green run: is what is
251
- * running still built from the code on disk? Exit 0 when up AND fresh, 1
252
- * otherwise — so it can gate a script.
253
- */
180
+ // Exit 0 only when up AND built from the code on disk, so it can gate a script.
254
181
  export async function e2eStatusCommand(featureName, options = {}) {
255
182
  const ctx = resolveContext(featureName, options);
256
183
  if (!ctx) process.exit(EXIT_USAGE);
@@ -309,17 +236,8 @@ export async function e2eStatusCommand(featureName, options = {}) {
309
236
  process.exit(status.up && status.healthy && fresh ? 0 : 1);
310
237
  }
311
238
 
312
- // ============================================================================
313
- // down
314
- // ============================================================================
315
-
316
- /**
317
- * `morph-spec e2e down <feature> [--volumes]`
318
- *
319
- * Teardown is best-effort by design — a stack that will not come down must
320
- * never block `finish`. The freshness stamp goes regardless: a stamp that
321
- * outlives its containers would let the next `up` reuse a stack that is gone.
322
- */
239
+ // Best-effort: a stack that will not come down must never block `finish`. The freshness stamp
240
+ // goes regardless, or the next `up` would reuse a stack that is gone.
323
241
  export async function e2eDownCommand(featureName, options = {}) {
324
242
  const ctx = resolveContext(featureName, options);
325
243
  if (!ctx) process.exit(EXIT_USAGE);
@@ -331,7 +249,7 @@ export async function e2eDownCommand(featureName, options = {}) {
331
249
  volumes: Boolean(options.volumes),
332
250
  });
333
251
  const stampDeleted = deleteStamp(ctx.gitCommonDir, ctx.projectName);
334
- const cleanup = afterHeavyRun({ projectPath: ctx.cwd, config: readHeavyConfig(ctx.cwd), scope: 'e2e-down' });
252
+ const cleanup = afterHeavyRun({ projectPath: ctx.cwd, config: readHeavyConfig(ctx.cwd) });
335
253
 
336
254
  if (options.json) {
337
255
  console.log(JSON.stringify(
@@ -348,10 +266,6 @@ export async function e2eDownCommand(featureName, options = {}) {
348
266
  process.exit(0);
349
267
  }
350
268
 
351
- // ============================================================================
352
- // Shared helpers
353
- // ============================================================================
354
-
355
269
  function readConfig(cwd) {
356
270
  try {
357
271
  const path = join(cwd, '.morph', 'config', 'config.json');
@@ -370,7 +284,6 @@ function readComposeText(cwd, composeFile) {
370
284
  }
371
285
  }
372
286
 
373
- /** Same resolution (and same fail-open) as verify-runner's. */
374
287
  function resolveGitCommonDir(cwd) {
375
288
  try {
376
289
  const common = execFileSync('git', ['rev-parse', '--git-common-dir'], {
@@ -1,23 +1,9 @@
1
- /**
2
- * `morph-spec graph affected <símbolo>` — blast radius saneado.
3
- *
4
- * O valor medido não é recall: contra o grep, o grafo achou ZERO arquivos a
5
- * mais. É precisão — 58% menos ruído em 12 símbolos do ProspectPRO (1.092
6
- * arquivos no grep, 266 usos reais) e a classificação da relação. `IHandler`
7
- * aparece em 193 arquivos no grep e tem 2 usos reais: é implementada por 98.
8
- *
9
- * @module graph-affected
10
- */
11
1
  import chalk from 'chalk';
12
2
  import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
13
3
  import { buildIndex, resolveLabel, affected } from '../../lib/graph/sanitize.js';
14
4
 
15
5
  const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob — o grafo é um subconjunto estrito do grep, então cair para grep não perde recall.';
16
6
 
17
- /**
18
- * @param {string} symbol
19
- * @param {{path?: string, json?: boolean, prod?: boolean, usesOnly?: boolean}} [options]
20
- */
21
7
  export async function graphAffectedCommand(symbol, options = {}) {
22
8
  const projectPath = options.path || process.cwd();
23
9
  const loaded = loadGraph(projectPath);
@@ -1,11 +1,3 @@
1
- /**
2
- * `morph-spec graph build` (extração completa) e `graph refresh` (incremental).
3
- *
4
- * Fail-open: sem graphify no PATH, explica o que fazer e sai 1 — sem stack
5
- * trace. Um projeto sem grafo continua funcionando por grep.
6
- *
7
- * @module graph-build
8
- */
9
1
  import chalk from 'chalk';
10
2
  import { readFileSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
11
3
  import { join } from 'node:path';
@@ -33,26 +25,9 @@ function readProjectTags(projectPath) {
33
25
  }
34
26
  }
35
27
 
36
- /**
37
- * O `.graphifyignore` com que o graphify vai rodar.
38
- *
39
- * Na raiz: escrito ou migrado (writeGraphifyignore). Numa worktree o arquivo é
40
- * do checkout e nunca é reescrito — seria uma mudança na branch da feature.
41
- * Ausente ali (a raiz ainda não o versionou), as regras — as da raiz
42
- * (`ignoreTextOf`), ou as geradas — entram só durante a rodada e saem depois
43
- * (`cleanup`): um `?? .graphifyignore` deixado na worktree faz o `finish` e
44
- * o `worktree remove --if-clean` recusarem com worktree-dirty (medido), e pôr
45
- * o arquivo no exclude do common dir esconderia também o da raiz. O arquivo leva
46
- * `TEMP_IGNORE_MARKER` na 1ª linha: se este processo morrer antes do
47
- * `cleanup`, a sobra é reconhecida pelo conteúdo — a próxima rodada a reusa e o
48
- * `finish` a apaga (`dropLeftoverIgnore`). O graphify não herda o da raiz: o
49
- * ignore dele para na raiz de VCS mais próxima, que numa worktree é ela mesma
50
- * (detect.py `_load_graphifyignore`).
51
- *
52
- * @param {string} projectPath
53
- * @param {string[]} tags
54
- * @returns {{written: boolean, migrated?: boolean, outdated?: object, temporary?: boolean, path: string, cleanup: () => void}}
55
- */
28
+ // Numa worktree o arquivo do checkout nunca é reescrito; ausente, as regras entram só durante
29
+ // a rodada (uma sobra suja a worktree e o `finish` recusa). `TEMP_IGNORE_MARKER` na 1ª linha
30
+ // deixa a próxima rodada e o `finish` reconhecerem a sobra de um processo morto.
56
31
  function prepareIgnore(projectPath, tags) {
57
32
  const none = () => {};
58
33
  if (!isSecondaryWorktree(projectPath)) return { ...writeGraphifyignore(projectPath, tags), cleanup: none };
@@ -71,9 +46,6 @@ function prepareIgnore(projectPath, tags) {
71
46
  };
72
47
  }
73
48
 
74
- /**
75
- * @param {{path?: string, json?: boolean, refresh?: boolean}} [options]
76
- */
77
49
  export async function graphBuildCommand(options = {}) {
78
50
  const projectPath = options.path || process.cwd();
79
51
  const refresh = options.refresh === true;
@@ -93,19 +65,14 @@ export async function graphBuildCommand(options = {}) {
93
65
  process.exit(1);
94
66
  }
95
67
 
96
- // O ignore precisa existir ANTES da extração, senão o grafo nasce com o
97
- // ruído que a ADR-004 existe para tirar.
68
+ // O ignore precisa existir ANTES da extração, senão o grafo nasce com o ruído.
98
69
  const ignore = prepareIgnore(projectPath, readProjectTags(projectPath));
99
70
  if (ignore.outdated && !options.json) {
100
71
  console.error(chalk.yellow(`\n⚠ ${describeOutdatedIgnore(ignore.outdated)}`));
101
72
  }
102
73
 
103
- // Na árvore principal o refresh em background (framework/hooks/shared/
104
- // graph-refresh.js) escreve o mesmo graph.json: dois graphify ao mesmo tempo
105
- // disputam o arquivo e todos os núcleos. Numa worktree ninguém mais escreve
106
- // o graph.json dela — o background só toca a raiz —, e esperar o lock da
107
- // raiz ali prometeria "o grafo chega ao HEAD" sobre um refresh que nunca a
108
- // alcança. Sem git não há lock, e o comando segue sozinho.
74
+ // Na raiz o refresh em background escreve o mesmo graph.json: dois graphify disputam o arquivo
75
+ // e os núcleos. Numa worktree só este comando escreve o grafo dela, e sem git não há lock.
109
76
  const primary = isPrimaryTree(projectPath);
110
77
  const lock = primary ? acquireRefreshLock(projectPath) : null;
111
78
  const running = primary && !lock ? readRefreshLock(projectPath) : null;
@@ -127,24 +94,18 @@ export async function graphBuildCommand(options = {}) {
127
94
  ? ['update', projectPath]
128
95
  : ['extract', projectPath, '--code-only'];
129
96
 
130
- // Capturamos o output em vez de herdar o terminal porque precisamos
131
- // inspecioná-lo: o graphify sai 1 tanto quando quebra quanto quando não há
132
- // código para indexar, e essas duas coisas não podem virar a mesma mensagem.
133
- // HEAD e regras lidos ANTES: são o que o graphify leu.
97
+ // Output capturado: o graphify sai 1 tanto ao quebrar quanto sem código para indexar, e as duas
98
+ // coisas não podem virar a mesma mensagem. HEAD e regras lidos ANTES: são o que o graphify leu.
134
99
  const head = readHead(projectPath);
135
100
  const ignoreHash = ignoreRulesHash(projectPath);
136
101
  let run;
137
102
  let empty = false;
138
103
  try {
139
104
  run = runGraphify(projectPath, args, { timeout: null });
140
- // Projeto sem código é o estado normal logo depois de um `init`, não uma
141
- // falha: o graphify diz "found 0 code" / "graph is empty", sai 1 e não
142
- // escreve graph.json. Tratar isso como erro faria todo init greenfield
143
- // terminar em vermelho.
105
+ // Projeto sem código (logo depois de um `init`) não é falha: o graphify sai 1 sem graph.json.
144
106
  empty = run.status !== 0 && (/found 0 code/i.test(run.output) || /graph is empty/i.test(run.output));
145
- // O registro é o que torna "untouched" em dia: sem ele, um refresh que não
146
- // mudou a topologia deixa o carimbo no commit antigo e o grafo parece
147
- // velho para sempre (framework/hooks/shared/graph-freshness.js).
107
+ // Sem o registro, um refresh que não mudou a topologia deixa o carimbo no commit antigo
108
+ // e o grafo parece velho para sempre.
148
109
  if (!empty) {
149
110
  recordRefreshRun(projectPath, {
150
111
  head, exit: run.status ?? null, ignoreHash, unavailable: Boolean(run.unavailable),
@@ -1,25 +1,9 @@
1
- /**
2
- * `morph-spec graph explain <símbolo>` — vizinhança com citação arquivo:linha.
3
- *
4
- * É o comando preferido nas personas: no eval, `explain` foi consistentemente
5
- * mais preciso que `query`, que trunca por budget e traz ruído.
6
- *
7
- * Cada aresta carrega a procedência (`via`), então dá para ver o que o
8
- * saneamento recuperou — `coalesced` são exatamente as arestas que o graphify
9
- * cru perde.
10
- *
11
- * @module graph-explain
12
- */
13
1
  import chalk from 'chalk';
14
2
  import { loadGraph, getFreshness, describeStale } from '../../lib/graph/graph-loader.js';
15
3
  import { buildIndex, resolveLabel, neighborhood, isTestPath } from '../../lib/graph/sanitize.js';
16
4
 
17
5
  const FALLBACK_HINT = 'Sem resposta do grafo, caia para grep/Glob.';
18
6
 
19
- /**
20
- * @param {string} symbol
21
- * @param {{path?: string, json?: boolean}} [options]
22
- */
23
7
  export async function graphExplainCommand(symbol, options = {}) {
24
8
  const projectPath = options.path || process.cwd();
25
9
  const loaded = loadGraph(projectPath);
@@ -1,12 +1,3 @@
1
- /**
2
- * `morph-spec graph status` — existência, tamanho, frescor e saúde do
3
- * saneamento.
4
- *
5
- * Ausência de grafo NÃO é erro (ADR-002): exit 0 com `available: false`. Um
6
- * projeto sem grafo é um estado válido, e o framework segue por grep.
7
- *
8
- * @module graph-status
9
- */
10
1
  import chalk from 'chalk';
11
2
  import {
12
3
  loadGraph, getFreshness, describeStale, refreshState, staleCause,
@@ -24,9 +15,7 @@ function summarizeSanitize(index) {
24
15
  return counts;
25
16
  }
26
17
 
27
- /**
28
- * @param {{path?: string, json?: boolean}} [options]
29
- */
18
+ // Ausência de grafo não é erro: exit 0 com `available: false`, e o framework segue por grep.
30
19
  export async function graphStatusCommand(options = {}) {
31
20
  const projectPath = options.path || process.cwd();
32
21
  const installed = detectGraphifyVersion();