@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 (345) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +11 -63
  4. package/bin/validate.js +1 -39
  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 +2 -1
  9. package/framework/MORPH.md +3 -3
  10. package/framework/agents.json +19 -11
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +22 -31
  14. package/framework/evals/review-checklist.md +12 -2
  15. package/framework/hooks/README.md +8 -44
  16. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  17. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  18. package/framework/hooks/claude-code/core/chain.js +4 -65
  19. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  20. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  21. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  22. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  23. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  24. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  25. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  26. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  27. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  28. package/framework/hooks/claude-code/core/state-sync.js +12 -68
  29. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  30. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  31. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  32. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  33. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  34. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  35. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  36. package/framework/hooks/claude-code/statusline.sh +11 -11
  37. package/framework/hooks/shared/activity-logger.js +9 -89
  38. package/framework/hooks/shared/agent-reap.js +2 -17
  39. package/framework/hooks/shared/build-check.js +6 -75
  40. package/framework/hooks/shared/claim-registry.js +9 -119
  41. package/framework/hooks/shared/command-tokens.js +6 -51
  42. package/framework/hooks/shared/comment-rules.js +5 -20
  43. package/framework/hooks/shared/comment-scanner.js +6 -25
  44. package/framework/hooks/shared/comment-stripper.js +11 -85
  45. package/framework/hooks/shared/e2e-check.js +0 -0
  46. package/framework/hooks/shared/errata.js +2 -15
  47. package/framework/hooks/shared/feature-locator.js +11 -95
  48. package/framework/hooks/shared/frontend-locations.js +1 -3
  49. package/framework/hooks/shared/gate-question.js +1 -7
  50. package/framework/hooks/shared/git-exclude.js +3 -20
  51. package/framework/hooks/shared/git-paths.js +8 -104
  52. package/framework/hooks/shared/graph-freshness.js +22 -166
  53. package/framework/hooks/shared/graph-refresh.js +21 -149
  54. package/framework/hooks/shared/heavy-command.js +4 -12
  55. package/framework/hooks/shared/hook-response.js +8 -83
  56. package/framework/hooks/shared/morph-cli.js +8 -52
  57. package/framework/hooks/shared/payload-utils.js +10 -132
  58. package/framework/hooks/shared/phase-utils.js +1 -42
  59. package/framework/hooks/shared/redact-secrets.js +4 -15
  60. package/framework/hooks/shared/reparse-scan.js +6 -66
  61. package/framework/hooks/shared/slice-progress.js +4 -23
  62. package/framework/hooks/shared/source-files.js +4 -17
  63. package/framework/hooks/shared/state-reader.js +13 -183
  64. package/framework/hooks/shared/state-writer.js +8 -52
  65. package/framework/hooks/shared/stdin-reader.js +1 -11
  66. package/framework/hooks/shared/telemetry-logger.js +7 -67
  67. package/framework/hooks/shared/transcript-usage.js +7 -29
  68. package/framework/hooks/shared/worktree-root.js +7 -29
  69. package/framework/rules/csharp-standards.md +1 -1
  70. package/framework/rules/frontend-standards.md +1 -0
  71. package/framework/rules/hangfire-standards.md +1 -1
  72. package/framework/rules/neon-standards.md +1 -1
  73. package/framework/rules/nextjs-standards.md +1 -1
  74. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  75. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  76. package/framework/skills/core/morph-apply/SKILL.md +4 -4
  77. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  78. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  79. package/framework/skills/core/morph-media/SKILL.md +1 -1
  80. package/framework/skills/core/morph-plan/SKILL.md +9 -7
  81. package/framework/skills/core/morph-review/SKILL.md +9 -6
  82. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  83. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  84. package/framework/standards/STANDARDS.json +36 -14
  85. package/framework/standards/STANDARDS.meta.json +16 -2
  86. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  87. package/framework/standards/ai-agents/agent-session.md +1 -1
  88. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  89. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  90. package/framework/standards/ai-agents/guardrails.md +1 -1
  91. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  92. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  93. package/framework/standards/ai-agents/production.md +1 -1
  94. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  95. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  96. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  97. package/framework/standards/ai-agents/setup.md +2 -2
  98. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  99. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  100. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  101. package/framework/standards/ai-agents/workflows.md +1 -1
  102. package/framework/standards/architecture/code-comments.md +1 -1
  103. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  104. package/framework/standards/backend/api/validation.md +1 -1
  105. package/framework/standards/backend/dotnet/testing.md +10 -4
  106. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  107. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  108. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  109. package/framework/standards/data/nosql/cache/redis.md +1 -1
  110. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  111. package/framework/standards/frontend/admin-app/forms.md +1 -1
  112. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  113. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  114. package/framework/standards/frontend/nextjs/testing.md +10 -6
  115. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  116. package/framework/templates/.idea/morph-templates.xml +92 -0
  117. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  118. package/framework/templates/CLAUDE.md.core.template +3 -3
  119. package/framework/templates/IDE-SNIPPETS.md +262 -262
  120. package/framework/templates/REGISTRY.json +9 -9
  121. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  122. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  123. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  124. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  125. package/framework/templates/docs/plan.md +3 -1
  126. package/framework/templates/docs/ui-mockups.md +4 -0
  127. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  128. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  129. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  130. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  131. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  132. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  133. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  134. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  135. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  136. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  137. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  138. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  139. package/framework/templates/state.template.json +17 -17
  140. package/framework/templates/ui/design-system.css +226 -226
  141. package/package.json +1 -1
  142. package/src/commands/context/context-refresh.js +0 -16
  143. package/src/commands/dashboard/dashboard.js +2 -8
  144. package/src/commands/e2e/e2e.js +17 -104
  145. package/src/commands/graph/affected.js +0 -14
  146. package/src/commands/graph/build.js +11 -50
  147. package/src/commands/graph/explain.js +0 -16
  148. package/src/commands/graph/status.js +1 -12
  149. package/src/commands/mcp/mcp-setup.js +4 -62
  150. package/src/commands/observability/cost.js +5 -57
  151. package/src/commands/observability/telemetry.js +1 -14
  152. package/src/commands/project/branch.js +4 -45
  153. package/src/commands/project/claim.js +4 -48
  154. package/src/commands/project/doctor.js +48 -302
  155. package/src/commands/project/env.js +1 -27
  156. package/src/commands/project/fleet.js +3 -32
  157. package/src/commands/project/init.js +4 -68
  158. package/src/commands/project/install-plugin-cmd.js +0 -1
  159. package/src/commands/project/standards.js +1 -62
  160. package/src/commands/project/status.js +0 -53
  161. package/src/commands/project/update.js +31 -239
  162. package/src/commands/project/worktree.js +108 -882
  163. package/src/commands/scaffold/scaffold.js +0 -25
  164. package/src/commands/state/advance.js +14 -85
  165. package/src/commands/state/approve.js +15 -97
  166. package/src/commands/state/archive.js +12 -64
  167. package/src/commands/state/auto-approval.js +2 -5
  168. package/src/commands/state/create.js +22 -109
  169. package/src/commands/state/delete.js +11 -63
  170. package/src/commands/state/finish.js +98 -475
  171. package/src/commands/state/gate-check.js +22 -132
  172. package/src/commands/state/gate-decision.js +3 -39
  173. package/src/commands/state/handoff.js +3 -30
  174. package/src/commands/state/list.js +14 -84
  175. package/src/commands/state/report-check.js +8 -58
  176. package/src/commands/state/retype.js +4 -26
  177. package/src/commands/validation/index.js +0 -3
  178. package/src/commands/validation/mutate.js +11 -11
  179. package/src/commands/validation/plan-check.js +4 -2
  180. package/src/commands/validation/tests-audit.js +7 -0
  181. package/src/commands/validation/validate-feature.js +10 -97
  182. package/src/commands/validation/verify.js +30 -113
  183. package/src/core/paths/output-schema.js +2 -101
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +0 -27
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +22 -304
  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 +0 -1
  191. package/src/core/work-types.js +1 -36
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  194. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  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 +1 -5
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/main.jsx +13 -13
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  211. package/src/dashboard/app/pages/Features.jsx +1 -8
  212. package/src/dashboard/app/pages/Guide.jsx +0 -1
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  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 +10 -69
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +0 -6
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +10 -66
  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 +2 -6
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/dashboard/vite.config.js +24 -24
  233. package/src/lib/ai/ai-checks.js +7 -52
  234. package/src/lib/ai/ai-project-scan.js +14 -108
  235. package/src/lib/ai/code-audit.js +7 -63
  236. package/src/lib/ai/pin-drift.js +8 -51
  237. package/src/lib/ai/registry-audit.js +11 -90
  238. package/src/lib/ci/ci-verify.js +4 -19
  239. package/src/lib/ci/github-ci.js +9 -37
  240. package/src/lib/ci/workflow-scaffold.js +14 -10
  241. package/src/lib/detectors/claude-config-detector.js +13 -74
  242. package/src/lib/detectors/index.js +4 -50
  243. package/src/lib/e2e/applicability.js +37 -0
  244. package/src/lib/e2e/compose-env.js +5 -66
  245. package/src/lib/e2e/stack-teardown.js +6 -40
  246. package/src/lib/generators/architecture-scanner.js +18 -156
  247. package/src/lib/generators/handoff-generator.js +9 -53
  248. package/src/lib/generators/scaffold-generator.js +5 -101
  249. package/src/lib/generators/settings-generator.js +4 -37
  250. package/src/lib/git/branch-classifier.js +11 -84
  251. package/src/lib/git/default-branch.js +14 -169
  252. package/src/lib/git/evidence-staleness.js +1 -0
  253. package/src/lib/git/gh.js +2 -17
  254. package/src/lib/git/pending-prs.js +6 -37
  255. package/src/lib/git/pr-files.js +3 -28
  256. package/src/lib/git/pr-index.js +3 -34
  257. package/src/lib/git/tree-fingerprint.js +3 -15
  258. package/src/lib/git/tree-state.js +6 -33
  259. package/src/lib/graph/god-nodes.js +2 -22
  260. package/src/lib/graph/graph-health.js +3 -24
  261. package/src/lib/graph/graph-loader.js +5 -56
  262. package/src/lib/graph/graphify-runtime.js +7 -37
  263. package/src/lib/graph/graphifyignore.js +8 -74
  264. package/src/lib/graph/sanitize.js +16 -136
  265. package/src/lib/heavy/cleanup.js +1 -1
  266. package/src/lib/installers/effects.js +8 -93
  267. package/src/lib/installers/mcp-installer.js +9 -169
  268. package/src/lib/installers/memory-installer.js +4 -27
  269. package/src/lib/installers/templates-reconcile.js +5 -60
  270. package/src/lib/mutation/adapters.js +6 -17
  271. package/src/lib/mutation/dotnet-introspection.js +20 -106
  272. package/src/lib/mutation/runner.js +23 -75
  273. package/src/lib/mutation/subset.js +1 -4
  274. package/src/lib/mutation/trx.js +0 -1
  275. package/src/lib/plan/artifact-vocabulary.js +5 -25
  276. package/src/lib/plan/checks.js +34 -33
  277. package/src/lib/plan/plan-parser.js +7 -8
  278. package/src/lib/projects/registry.js +7 -44
  279. package/src/lib/review-reports.js +11 -26
  280. package/src/lib/risk-detector.js +41 -173
  281. package/src/lib/specs/adr-index.js +6 -34
  282. package/src/lib/specs/frontmatter.js +2 -15
  283. package/src/lib/specs/gfm-table.js +1 -7
  284. package/src/lib/specs/revocations.js +1 -14
  285. package/src/lib/specs/screen-checklist.js +47 -124
  286. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +1 -8
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +1 -7
  291. package/src/lib/standards/sections.js +2 -13
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +21 -169
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -240
  296. package/src/lib/tasks/test-runner.js +43 -341
  297. package/src/lib/telemetry/metrics.js +7 -36
  298. package/src/lib/telemetry/telemetry-reader.js +13 -125
  299. package/src/lib/telemetry/verify-event.js +2 -30
  300. package/src/lib/tests-audit/audit.js +96 -0
  301. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  302. package/src/lib/validators/code-budget/index.js +0 -7
  303. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  304. package/src/lib/validators/contracts/index.js +0 -6
  305. package/src/lib/validators/nextjs/index.js +0 -5
  306. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  307. package/src/lib/validators/packages/index.js +0 -6
  308. package/src/lib/validators/packages/package-validator.js +6 -56
  309. package/src/lib/validators/reuse/index.js +0 -8
  310. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  311. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  312. package/src/lib/validators/traceability-check.js +5 -63
  313. package/src/lib/validators/validation-runner.js +21 -158
  314. package/src/lib/validators/verify-runner.js +85 -376
  315. package/src/lib/work-type-detector.js +5 -67
  316. package/src/lib/worktree/docker-audit.js +17 -136
  317. package/src/lib/worktree/env-allocator.js +10 -98
  318. package/src/lib/worktree/feature-truth.js +12 -115
  319. package/src/lib/worktree/finish-backup.js +32 -242
  320. package/src/lib/worktree/fleet-scanner.js +13 -95
  321. package/src/lib/worktree/ignored-triage.js +15 -91
  322. package/src/lib/worktree/vscode-settings.js +2 -3
  323. package/src/lib/worktree/worktree-audit.js +21 -213
  324. package/src/lib/worktree/worktree-include.js +3 -5
  325. package/src/scripts/global-install.js +3 -9
  326. package/src/scripts/install-plugin.js +1 -13
  327. package/src/scripts/setup-infra.js +17 -147
  328. package/src/utils/agents-installer.js +29 -276
  329. package/src/utils/banner.js +4 -25
  330. package/src/utils/claude-md-injector.js +1 -37
  331. package/src/utils/claude-settings-manager.js +1 -52
  332. package/src/utils/file-copier.js +11 -95
  333. package/src/utils/hooks-installer.js +21 -298
  334. package/src/utils/skills-installer.js +4 -101
  335. package/src/utils/version-checker.js +4 -83
  336. package/framework/commands/morph-proposal.md +0 -9
  337. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  338. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  339. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  340. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
  341. package/src/commands/utils/index.js +0 -3
  342. package/src/lib/detectors/design-system-detector.js +0 -188
  343. package/src/lib/generators/design-system-generator.js +0 -508
  344. package/src/lib/specs/old-format.js +0 -68
  345. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,55 +1,17 @@
1
- /**
2
- * traceability-check.js — advisory, feature-scope-only check: do the files
3
- * cited under `**Arquivos:**` in the slice reports (`3-implement/reports/F{n}.md`)
4
- * belong to some slice of the plan (its `arquivos:`, as extracted by plan-check)?
5
- *
6
- * It catches the reports and the plan DISAGREEING: a file the implementation
7
- * says it touched that no slice declared. A file absent from both documents
8
- * is invisible to it by construction — that drift is the reuse/contract
9
- * reviewer's job.
10
- *
11
- * Advisory, never blocking: `status` is only ever 'pass' | 'warn' | 'skip'.
12
- * `runVerify()` keeps this result OUTSIDE `nodes` so it can never flip the
13
- * overall build/tests/validators/e2e status.
14
- *
15
- * Fail-open throughout: no slice report, no extracted plan or a parse error
16
- * all degrade to `{ status: 'skip' }`.
17
- *
18
- * @module traceability-check
19
- */
1
+ // Advisory: status is only 'pass' | 'warn' | 'skip', and runVerify() keeps it outside
2
+ // `nodes` so it never flips the verify rollup.
20
3
 
21
4
  import { existsSync, readFileSync } from 'fs';
22
5
  import { join } from 'path';
23
6
  import { readPlanChecks } from '../plan/checks.js';
24
7
  import { sliceProgress, sliceReportPath } from '../../../framework/hooks/shared/slice-progress.js';
25
8
 
26
- /**
27
- * An `**Arquivos:**` marker and what follows it: the rest of its own line
28
- * (inline list) plus any `- ` bullet lines right below.
29
- */
30
9
  const FILES_BLOCK_RE = /\*\*Arquivos:\*\*([^\r\n]*)\r?\n?((?:^[ \t]*-[ \t].*\r?\n?)*)/gm;
31
10
 
32
- /** Backtick-quoted spans within a line. */
33
11
  const BACKTICK_RE = /`([^`]+)`/g;
34
12
 
35
- /**
36
- * Extract the set of file paths cited under every `**Arquivos:**` marker of a
37
- * slice report, applying a conservative decomposition of the shorthand real
38
- * reports use:
39
- *
40
- * - A line with exactly one backtick span: that span is a candidate,
41
- * whether or not it contains a `/`.
42
- * - A line with several comma-separated spans: only the FIRST span and any
43
- * LATER span that itself contains `/` are candidates. A later span with
44
- * no `/` is ambiguous shorthand and is skipped rather than guessed —
45
- * false negatives are acceptable for advisory tooling, false positives
46
- * are not.
47
- * - Any span containing `{`/`}` (brace expansion) is skipped.
48
- * - Any span ending in `/` is a directory reference, not a file — skipped.
49
- *
50
- * @param {string} reportText
51
- * @returns {string[]} de-duplicated, normalized (forward-slash) candidate paths
52
- */
13
+ // A later span without `/` is ambiguous shorthand and is skipped, not guessed: false
14
+ // negatives are acceptable for advisory tooling, false positives are not.
53
15
  export function extractCitedFiles(reportText) {
54
16
  const cited = new Set();
55
17
  if (!reportText) return [];
@@ -72,16 +34,11 @@ export function extractCitedFiles(reportText) {
72
34
  return [...cited];
73
35
  }
74
36
 
75
- /** Backslashes to forward slashes, strip a leading `./`. */
76
37
  export function normalizePath(p) {
77
38
  return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
78
39
  }
79
40
 
80
- /**
81
- * Union of the `arquivos:` of every slice of the plan, normalized.
82
- *
83
- * @returns {string[]|null} null when the plan has no readable extraction
84
- */
41
+ /** null when the plan has no readable extraction. */
85
42
  export function collectSliceFiles(projectPath, featureName) {
86
43
  const checks = readPlanChecks(projectPath, featureName).checks;
87
44
  if (!checks) return null;
@@ -90,14 +47,6 @@ export function collectSliceFiles(projectPath, featureName) {
90
47
  .map(normalizePath));
91
48
  }
92
49
 
93
- /**
94
- * A cited file is covered if some slice names it exactly, or names a
95
- * directory (an entry ending in `/`) that the cited file falls under.
96
- *
97
- * @param {string} file
98
- * @param {string[]} declared
99
- * @returns {boolean}
100
- */
101
50
  function isCovered(file, declared) {
102
51
  for (const entry of declared) {
103
52
  if (entry === file) return true;
@@ -114,13 +63,6 @@ function readOrNull(path) {
114
63
  }
115
64
  }
116
65
 
117
- /**
118
- * Run the reports-vs-slices traceability check for a feature.
119
- *
120
- * @param {string} projectPath
121
- * @param {string} featureName
122
- * @returns {{ status: 'pass'|'warn'|'skip', warnings: string[], reason: string|null, citedCount: number }}
123
- */
124
66
  export function checkOutputsTraceability(projectPath, featureName) {
125
67
  const featureDir = join(projectPath, '.morph', 'features', featureName);
126
68
  const progress = sliceProgress(featureDir);
@@ -1,31 +1,11 @@
1
- /**
2
- * MORPH-SPEC Validation Runner
3
- *
4
- * Unified orchestrator that runs the right validators based on the project's
5
- * stack profile. Called by task-done before marking tasks complete.
6
- *
7
- * MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
8
- */
9
-
10
1
  import { existsSync } from 'fs';
11
2
  import chalk from 'chalk';
12
3
  import { getAbsoluteOutputPath, resolveContractsOutputType } from '../../core/paths/output-schema.js';
13
4
  import { getStackProfile } from '../stack/stack-profile.js';
14
5
 
15
- // Skipped in 'light' mode (checkpoint-only); light keeps `nextjs-component`.
16
- const HEAVY_VALIDATORS = new Set([
17
- 'packages', 'contract-compliance', 'reuse', 'code-budget'
18
- ]);
19
-
20
6
  /**
21
- * Run all relevant validators for a feature
22
- *
23
- * @param {string} projectPath - Project root path
24
- * @param {string} featureName - Feature name from state.json
25
- * @param {Object} options - Options
26
- * @param {boolean} options.verbose - Show detailed output
27
- * @param {string[]} options.validators - Specific validators to run (overrides stack-based detection)
28
- * @returns {Object} { passed, errors, warnings, issues, summary }
7
+ * @param {string[]} [options.validators] - overrides stack-based detection
8
+ * @returns {Object} { passed, errors, warnings, issues, summary, results, skipped, _skipReason? }
29
9
  */
30
10
  export async function runValidation(projectPath, featureName, options = {}) {
31
11
  const result = {
@@ -34,114 +14,38 @@ export async function runValidation(projectPath, featureName, options = {}) {
34
14
  warnings: 0,
35
15
  issues: [],
36
16
  summary: '',
37
- results: {} // Per-validator results for hooks integration
38
- };
39
-
40
- // Decision constraints support (stub - decision-constraint-loader removed as orphaned code)
41
- const constraints = {
42
- enabledValidators: [], // No constraint-based validators
43
- validators: {} // No validator-specific constraints
17
+ results: {}
44
18
  };
45
19
 
46
- // Determine which validators to run.
47
- // COPY: the mode filter below used to splice in place, and when the caller
48
- // passed `options.validators` that array WAS this array — so a light run
49
- // emptied the caller's own list, and running the same list twice validated
50
- // less the second time.
20
+ // A copy: filtering in place would empty the caller's own `options.validators`.
51
21
  const detected = options.validators || detectValidators(projectPath);
52
- let validatorIds = [...detected];
22
+ const validatorIds = [...detected];
53
23
 
54
- // Filter by mode: 'light' skips heavy validators, 'full' (default) runs all
55
- const mode = options.mode || 'full';
56
- const droppedByMode = [];
57
- if (mode === 'light') {
58
- validatorIds = validatorIds.filter(id => {
59
- if (HEAVY_VALIDATORS.has(id)) { droppedByMode.push(id); return false; }
60
- return true;
61
- });
62
- }
63
-
64
- // Merge constraint-enabled validators
65
- validatorIds.push(...constraints.enabledValidators);
66
-
67
- // Reuse is stack-agnostic: "isto já existe?" é a mesma pergunta em C# e em
68
- // TypeScript, e o validador se cala sozinho quando não há grafo (ADR-002).
69
- // Por isso ele não entra no `defaultValidators` de stack profile nenhum —
70
- // entra aqui, ANTES do early-return de "nenhum validador": num projeto de
71
- // stack `empty`/`nodejs-cli` não há outro validador para carregá-lo, e é
72
- // exatamente lá que ele seria a única verificação a rodar.
73
- // Em `light` (checkpoint) ele fica de fora, como todo validador pesado — é
74
- // uma leitura de graph.json (11-24 MB nos repos medidos) — e por isso está
75
- // em HEAVY_VALIDATORS, que é onde essa política se declara. Não entra em
76
- // `droppedByMode`: essa lista responde "o que o modo tirou da SUA lista", e
77
- // é ela que distingue "light pulou os pesados" de "este stack não tem
78
- // validador nenhum" na mensagem final.
79
- if (!options.skipReuse && mode !== 'light') {
80
- validatorIds.push('reuse');
81
- }
82
- // Mesma política, pelo mesmo motivo: comentário e orçamento de teste valem
83
- // em qualquer stack, e a medida é da feature, não de um checkpoint.
84
- if (mode !== 'light') {
85
- validatorIds.push('code-budget');
86
- }
24
+ // Stack-agnostic: on an `empty`/`nodejs-cli` stack they are the only check.
25
+ if (!options.skipReuse) validatorIds.push('reuse');
26
+ validatorIds.push('code-budget');
87
27
 
88
- if (validatorIds.length === 0) {
89
- // Say which of the two it is. On a pure `dotnet` project every default
90
- // validator is heavy, so a light run legitimately runs none — but the old
91
- // message blamed the stack ("no validators configured"), which reads as
92
- // "your stack has nothing to check" when the full run has two.
93
- result.summary = droppedByMode.length > 0
94
- ? `No validators ran — light mode skipped ${droppedByMode.length} heavy validator(s): ${droppedByMode.join(', ')}`
95
- : 'No validators ran — no validators configured for this stack';
96
- console.log(chalk.gray(`\n ℹ️ ${result.summary.replace('No validators ran — ', '')}`));
97
- result._skipReason = result.summary;
98
- result._droppedByMode = droppedByMode;
99
- return result;
100
- }
101
-
102
- // Always show what's running (even non-verbose) — helps confirm validators are active
103
28
  console.log(chalk.gray(`\n Running ${validatorIds.length} validator(s): ${validatorIds.join(', ')}`));
104
- if (constraints.enabledValidators.length > 0) {
105
- console.log(chalk.gray(` (+ ${constraints.enabledValidators.length} from decisions)`));
106
- }
107
29
 
108
- // Always run contract compliance if the contracts file exists (stack-aware)
109
30
  if (!options.skipContracts) {
110
31
  const _profileForContracts = getStackProfile(projectPath);
111
32
  const contractsType = resolveContractsOutputType(_profileForContracts.stack);
112
33
  const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
113
- // Positive check on isDotnet (contract-compliance is a VSA/.NET concern) —
114
- // NOT `!isNextjs`, which used to wrongly suppress it for a compound
115
- // "dotnet-nextjs" stack that has a .NET backend but is ALSO isNextjs.
116
- // `mode: 'light'` has to hold here too. This push sits AFTER the mode
117
- // filter, so contract-compliance — which is in HEAVY_VALIDATORS — used to
118
- // slip into every light run that had a contracts file, making light mode
119
- // heavier than declared on exactly the compound stacks it matters for.
120
- const contractsIsHeavyAndSkipped = mode === 'light' && HEAVY_VALIDATORS.has('contract-compliance');
121
- if (existsSync(contractsPath) && _profileForContracts.isDotnet && !contractsIsHeavyAndSkipped) {
34
+ // Positive `isDotnet`, not `!isNextjs`: a "dotnet-nextjs" stack is both.
35
+ if (existsSync(contractsPath) && _profileForContracts.isDotnet) {
122
36
  validatorIds.push('contract-compliance');
123
37
  }
124
38
  }
125
39
 
126
- // Deduplicate
127
40
  const uniqueValidators = [...new Set(validatorIds)];
128
41
 
129
- // Validadores que rodaram mas se declararam inaplicáveis (ex.: `reuse` num
130
- // projeto sem grafo). Um validador assim não acha nada — e "não achou nada"
131
- // é indistinguível de "verificou e está limpo" se ninguém contar.
42
+ // A validator that declared itself inapplicable found nothing, which is not "clean".
132
43
  const selfSkipped = [];
133
44
  result.skipped = selfSkipped;
134
45
 
135
- // Run each validator
136
46
  for (const validatorId of uniqueValidators) {
137
47
  try {
138
- // Pass validator-specific constraints from decisions.md
139
- const validatorOptions = {
140
- ...options,
141
- constraints: constraints.validators[validatorId] || {}
142
- };
143
-
144
- const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, validatorOptions);
48
+ const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, options);
145
49
 
146
50
  if (validatorResult) {
147
51
  if (validatorResult._skipReason) {
@@ -156,8 +60,6 @@ export async function runValidation(projectPath, featureName, options = {}) {
156
60
  result.warnings += warnings;
157
61
  result.issues.push(...issues);
158
62
 
159
- // A validator that skipped itself checked nothing: in `results` it would
160
- // read as a validator that ran clean.
161
63
  if (!validatorResult._skipReason) result.results[validatorId] = { passed: errors === 0, issues };
162
64
  }
163
65
  } catch (err) {
@@ -178,10 +80,7 @@ export async function runValidation(projectPath, featureName, options = {}) {
178
80
  }
179
81
  }
180
82
 
181
- // Todos os validadores se declararam inaplicáveis: o resultado é SKIP, não um
182
- // verde. Sem isto, um projeto de stack sem validadores e sem grafo receberia
183
- // `validators: pass` do `morph-spec verify` tendo verificado exatamente nada
184
- // — o modo de falha que este repositório já pagou caro (guardrail inerte).
83
+ // Every validator self-skipped: SKIP, never a green over nothing checked.
185
84
  if (selfSkipped.length > 0 && selfSkipped.length === uniqueValidators.length) {
186
85
  result._skipReason = `No validators ran — ${selfSkipped.map((s) => `${s.validator}: ${s.reason}`).join('; ')}`;
187
86
  result.summary = result._skipReason;
@@ -196,44 +95,20 @@ export async function runValidation(projectPath, featureName, options = {}) {
196
95
  return result;
197
96
  }
198
97
 
199
- /**
200
- * Detect which validators to run, driven by the project's stack profile.
201
- * @param {string} projectPath - Project root path (default: current directory)
202
- * @returns {string[]} Array of validator IDs to run
203
- */
204
98
  function detectValidators(projectPath = '.') {
205
99
  const profile = getStackProfile(projectPath);
206
100
  return [...profile.defaultValidators];
207
101
  }
208
102
 
209
103
  /**
210
- * Reduce any validator's return value to the runner's contract:
211
- * `{ errors, warnings, issues }`.
212
- *
213
- * Validators were written at different times and return different shapes. The
214
- * runner used to read `errors`/`warnings`/`issues` off the raw object, so a
215
- * validator that reports through any other shape contributed ZERO — and since
216
- * `passed = errors === 0`, its findings were recorded as a pass. Measured: of
217
- * seven validators only two returned all three keys. `packages` — one of the
218
- * two defaults for the `dotnet` stack — returned
219
- * `{status, totalFiles, filesWithIssues, results}` and had every NuGet
220
- * incompatibility it found thrown away, while `verify` reported green.
221
- *
222
- * Reading the nested `results` shape here keeps that contract in ONE place
223
- * instead of asking every validator to remember it.
224
- *
225
- * @param {string} validatorId
226
- * @param {Object} raw - whatever the validator returned
227
- * @returns {{errors: number, warnings: number, issues: Array}}
104
+ * Reduce any validator's return shape to `{ errors, warnings, issues }`, so a
105
+ * validator reporting through nested `results` never contributes zero and reads as a pass.
228
106
  */
229
107
  export function normalizeValidatorResult(validatorId, raw) {
230
108
  if (!raw || typeof raw !== 'object') return { errors: 0, warnings: 0, issues: [] };
231
109
 
232
- // Issues can be top-level, or nested one level under `results` — either as
233
- // an array of `{issues}` (packages: one entry per .csproj) or an object of
234
- // them keyed by check name. The nested read is a FALLBACK,
235
- // never additive: validators that expose both would otherwise report every
236
- // finding twice.
110
+ // The nested read is a FALLBACK, never additive: validators exposing both
111
+ // shapes would otherwise report every finding twice.
237
112
  const topLevel = Array.isArray(raw.issues) ? raw.issues : [];
238
113
  const nested = raw.results && typeof raw.results === 'object'
239
114
  ? (Array.isArray(raw.results) ? raw.results : Object.values(raw.results))
@@ -242,8 +117,7 @@ export function normalizeValidatorResult(validatorId, raw) {
242
117
  const issues = (topLevel.length ? topLevel : nested)
243
118
  .map(i => ({ validator: validatorId, ...i }));
244
119
 
245
- // Prefer the validator's own counts when it reports them — it may weigh
246
- // issues differently than one-each. Otherwise derive from the issue levels.
120
+ // The validator's own counts win: it may weigh issues differently than one-each.
247
121
  const counted = {
248
122
  errors: issues.filter(i => i.level === 'error').length,
249
123
  warnings: issues.filter(i => i.level === 'warning').length,
@@ -251,8 +125,7 @@ export function normalizeValidatorResult(validatorId, raw) {
251
125
  const errors = Number.isFinite(raw.errors) ? raw.errors : counted.errors;
252
126
  const warnings = Number.isFinite(raw.warnings) ? raw.warnings : counted.warnings;
253
127
 
254
- // Last resort: a validator that says `status: 'error'` but exposes no
255
- // countable detail still must not read as a pass.
128
+ // `status: 'error'` with no countable detail still must not read as a pass.
256
129
  if (errors === 0 && raw.status === 'error') {
257
130
  return {
258
131
  errors: 1,
@@ -268,9 +141,6 @@ export function normalizeValidatorResult(validatorId, raw) {
268
141
  return { errors, warnings, issues };
269
142
  }
270
143
 
271
- /**
272
- * Run a single validator by ID
273
- */
274
144
  async function runSingleValidator(validatorId, projectPath, featureName, options) {
275
145
  switch (validatorId) {
276
146
  case 'packages': {
@@ -299,11 +169,8 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
299
169
  }
300
170
 
301
171
  default:
302
- // A warning, not silence: an id with no case runs nothing, and returning
303
- // a bare null made that indistinguishable from a validator that ran and
304
- // found nothing. A typo in a validator list would have read as a clean
305
- // pass. Not an error — an unknown id is a configuration mistake, not a
306
- // defect in the code under validation.
172
+ // A warning, not silence (a typo would read as a clean pass) and not an
173
+ // error (a config mistake, not a defect in the code under validation).
307
174
  if (options.verbose) {
308
175
  console.log(chalk.gray(` Unknown validator: ${validatorId}`));
309
176
  }
@@ -320,9 +187,6 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
320
187
  }
321
188
  }
322
189
 
323
- /**
324
- * Format validation results for console output
325
- */
326
190
  export function formatValidationResults(result) {
327
191
  if (result.passed && result.warnings === 0) {
328
192
  const ranCount = Object.keys(result.results || {}).length;
@@ -337,7 +201,6 @@ export function formatValidationResults(result) {
337
201
  console.log(chalk.red(`\n ❌ Validation FAILED: ${result.errors} error(s), ${result.warnings} warning(s)\n`));
338
202
  }
339
203
 
340
- // Group issues by level
341
204
  const errors = result.issues.filter(i => i.level === 'error');
342
205
  const warnings = result.issues.filter(i => i.level === 'warning');
343
206