@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,39 +1,31 @@
1
- /**
2
- * A hash of what a verify run reads: the commit, the uncommitted changes and
3
- * the untracked files, plus the project's `config.json` (it names the test
4
- * commands). Everything else under `.morph/` stays out — verify itself writes
5
- * feature.json and the logs on every run, and counting that would make two
6
- * runs over the same code look different.
7
- */
1
+ // `.morph/` stays out except `config.json` minus `expectedCount`, as in `treeMovedSince`:
2
+ // committing verify's own bookkeeping must not make the same code look different.
8
3
 
9
4
  import { execFileSync } from 'child_process';
10
5
  import { createHash } from 'crypto';
11
6
  import { existsSync, readFileSync, statSync } from 'fs';
12
7
  import { join } from 'path';
8
+ import { withoutBaseline } from './evidence-staleness.js';
13
9
 
14
- /** Pathspec for "the project minus its `.morph/` bookkeeping". */
15
10
  export const OUTSIDE_MORPH = ['--', '.', ':(exclude).morph'];
16
11
  const MAX_OUTPUT = 64 * 1024 * 1024;
17
12
 
18
- /** git output as a Buffer; stderr dropped, throws on a non-zero exit. */
19
13
  export function git(projectPath, args, input) {
20
14
  return execFileSync('git', args, {
21
15
  cwd: projectPath, stdio: [input === undefined ? 'ignore' : 'pipe', 'pipe', 'ignore'], maxBuffer: MAX_OUTPUT, input,
22
16
  });
23
17
  }
24
18
 
25
- /**
26
- * @param {string} projectPath
27
- * @returns {string|null} sha256 hex, or null outside a git repo / on any git error
28
- */
19
+ /** @returns {string|null} sha256 hex, or null outside a git repo / on any git error */
29
20
  export function treeFingerprint(projectPath) {
30
21
  try {
31
22
  const hash = createHash('sha256');
32
23
  let head = null;
33
24
  try { head = git(projectPath, ['rev-parse', 'HEAD']).toString().trim(); } catch { /* no commit yet */ }
34
- hash.update(`head\0${head ?? 'no-commit'}\0`);
35
- // Before the first commit there is no HEAD to diff against: every file is
36
- // untracked, and the listing below covers them.
25
+ const tree = head
26
+ ? git(projectPath, ['ls-tree', '-z', 'HEAD']).toString().split('\0').filter((e) => e && e.split('\t')[1] !== '.morph')
27
+ : ['no-commit'];
28
+ hash.update(`tree\0${tree.join('\0')}\0`);
37
29
  if (head) hash.update(git(projectPath, ['diff', 'HEAD', '--binary', ...OUTSIDE_MORPH]));
38
30
  hash.update('\0untracked\0');
39
31
  const untracked = git(projectPath, ['ls-files', '--others', '--exclude-standard', '-z', ...OUTSIDE_MORPH])
@@ -50,9 +42,17 @@ export function treeFingerprint(projectPath) {
50
42
  }
51
43
  const config = join(projectPath, '.morph', 'config', 'config.json');
52
44
  hash.update('\0config\0');
53
- if (existsSync(config)) hash.update(readFileSync(config));
45
+ if (existsSync(config)) hash.update(normalizedConfig(readFileSync(config, 'utf8')));
54
46
  return hash.digest('hex');
55
47
  } catch {
56
48
  return null;
57
49
  }
58
50
  }
51
+
52
+ function normalizedConfig(text) {
53
+ try {
54
+ return JSON.stringify(withoutBaseline(text));
55
+ } catch {
56
+ return text;
57
+ }
58
+ }
@@ -1,17 +1,9 @@
1
- /**
2
- * The working tree as the orchestrator needs it when it takes control back
3
- * from a dispatch, in one call instead of `git status` + `git diff --stat` +
4
- * `git log` + a marker grep. Everything is read outside `.morph/`, the same
5
- * cut the tree fingerprint makes.
6
- */
7
-
8
1
  import { existsSync } from 'fs';
9
2
  import { join } from 'path';
10
3
  import { OUTSIDE_MORPH, treeFingerprint, git as gitBuffer } from './tree-fingerprint.js';
11
4
  import { detectDefaultBranch } from './default-branch.js';
12
5
  import { resolveBaseRef } from './branch-classifier.js';
13
6
 
14
- /** What an evaluator writes next to a mutation it has not reverted yet. */
15
7
  export const MUTATION_MARKER = 'MUTACAO';
16
8
  const MAX_LISTED = 50;
17
9
  const MAX_COMMITS = 10;
@@ -24,13 +16,11 @@ function headSha(cwd) {
24
16
  try { return git(cwd, ['rev-parse', 'HEAD']).trim(); } catch { return null; }
25
17
  }
26
18
 
27
- /** Every file that differs from HEAD outside `.morph/`, untracked ones one by one. */
28
19
  function changedFiles(cwd) {
29
20
  return parsePorcelain(git(cwd, ['status', '--porcelain=v1', '-z', '--untracked-files=all', ...OUTSIDE_MORPH]))
30
21
  .map((e) => e.path);
31
22
  }
32
23
 
33
- /** Blob id of each file as it is on disk; null for a file that is gone. */
34
24
  function diskBlobs(cwd, paths) {
35
25
  const present = paths.filter((p) => existsSync(join(cwd, p)));
36
26
  const ids = present.length ? git(cwd, ['hash-object', '--stdin-paths'], present.join('\n')).split('\n') : [];
@@ -39,7 +29,6 @@ function diskBlobs(cwd, paths) {
39
29
  return out;
40
30
  }
41
31
 
42
- /** Blob id of each path in a commit; null where the commit does not have it. */
43
32
  function commitBlobs(cwd, commit, paths) {
44
33
  const out = Object.fromEntries(paths.map((p) => [p, null]));
45
34
  if (!commit || !paths.length) return out;
@@ -49,10 +38,8 @@ function commitBlobs(cwd, commit, paths) {
49
38
  }
50
39
 
51
40
  /**
52
- * What the tree is before a dispatch whose work must leave it unchanged (the
53
- * evaluator mutates and reverts): HEAD plus the blob of every file that
54
- * differs from it. Kept on disk, so a session that resumes after the agent
55
- * died compares against the tree before the dispatch, not its own start.
41
+ * HEAD plus the blob of every file that differs from it, kept on disk so a resumed
42
+ * session compares against the tree before the dispatch, not its own start.
56
43
  * @returns {{ at: string, head: string|null, files: Object<string, string|null> }|null}
57
44
  */
58
45
  export function snapshotTree(cwd) {
@@ -64,9 +51,8 @@ export function snapshotTree(cwd) {
64
51
  }
65
52
 
66
53
  /**
67
- * Files whose content on disk is not what it was in the snapshot. Content,
68
- * not status: a mutation committed since shows up, and a file committed
69
- * unchanged does not.
54
+ * Compares content, not status: a mutation committed since shows up, and a file
55
+ * committed unchanged does not.
70
56
  * @returns {{ at: string, headChanged: boolean, changed: string[], changedCount: number }}
71
57
  */
72
58
  export function compareToSnapshot(cwd, snapshot) {
@@ -85,7 +71,7 @@ export function compareToSnapshot(cwd, snapshot) {
85
71
  return { at: snapshot.at, headChanged: snapshot.head !== head, changed: changed.slice(0, MAX_LISTED), changedCount: changed.length };
86
72
  }
87
73
 
88
- /** `git status --porcelain -z`: a rename carries its old path as an extra entry. */
74
+ // With `-z`, a rename or copy carries its old path as an extra entry.
89
75
  function parsePorcelain(out) {
90
76
  const entries = [];
91
77
  const parts = out.split('\0');
@@ -118,7 +104,6 @@ function commitsOnTop(cwd) {
118
104
  .split('\n').filter(Boolean);
119
105
  }
120
106
 
121
- /** Untracked files count too; gitignored ones (build output) do not. */
122
107
  function markers(cwd) {
123
108
  try {
124
109
  const out = git(cwd, ['grep', '-n', '-I', '--untracked', '-F', '-e', MUTATION_MARKER, ...OUTSIDE_MORPH]);
@@ -132,19 +117,7 @@ function markers(cwd) {
132
117
  }
133
118
  }
134
119
 
135
- /**
136
- * @param {string} cwd
137
- * @param {{ snapshot?: object|null }} [opts] - a `snapshotTree()` to compare with
138
- * @returns {{ head: string|null, branch: string|null, fingerprint: string|null,
139
- * dirty: {status: string, path: string}[], dirtyCount: number,
140
- * diffstat: {files: number, insertions: number, deletions: number}|null,
141
- * commits: string[]|null, markers: {file: string, line: number}[],
142
- * sinceBeforeEval: ReturnType<typeof compareToSnapshot>|null }|null}
143
- * null when git cannot answer (outside a repo, or an error): unknown, never
144
- * a clean tree. `commits` is null when there is no default branch to count
145
- * from; `diffstat` is null before the first commit; `sinceBeforeEval` is
146
- * null without a snapshot.
147
- */
120
+ /** @returns {object|null} null when git cannot answer: unknown, never a clean tree. */
148
121
  export function treeState(cwd, { snapshot = null } = {}) {
149
122
  try {
150
123
  const dirty = parsePorcelain(git(cwd, ['status', '--porcelain=v1', '-z', ...OUTSIDE_MORPH]));
@@ -1,31 +1,11 @@
1
- /**
2
- * God nodes e comunidades para o contexto de arquitetura.
3
- *
4
- * O inventário do architecture-scanner diz o que EXISTE; os god nodes dizem o
5
- * que é CENTRAL. No ProspectPRO, seis linhas (`Result` 223, `Abstractions` 217,
6
- * `Repository` 156, `cn()` 141) orientam melhor que 665 de inventário — sem
7
- * substituí-lo, porque exaustividade é um valor que o grafo não tem.
8
- *
9
- * Só entram nós COM source_file: um god node sem arquivo não orienta ninguém.
10
- * E nada de teste: dublês e fixtures estão no grafo para serem achados, não
11
- * para disputar centralidade — sem o filtro o `FakeGhlToolbelt` entra no top
12
- * 10 do MORPH_GHLBrain e as arestas vindas de teste inflam o grau de `Result`
13
- * de 380 para 443. Aresta com uma ponta em teste não conta para ninguém, e
14
- * comunidade só de teste some da lista.
15
- *
16
- * @module god-nodes
17
- */
1
+ // Só nós com source_file, e nada de teste: dublês e fixtures estão no grafo para serem achados, não para
2
+ // disputar centralidade. Aresta com uma ponta em teste não conta, e comunidade só de teste some.
18
3
  import { loadGraph } from './graph-loader.js';
19
4
  import { buildIndex, resolveEdgeTarget, linksOf, isTestPath } from './sanitize.js';
20
5
 
21
6
  /** Sem `graphify label`, as comunidades se chamam Community_N — ruído puro. */
22
7
  const PLACEHOLDER_LABEL = /^Community[_ ]?\d+$/i;
23
8
 
24
- /**
25
- * @param {string} projectPath
26
- * @param {{top?: number, topCommunities?: number}} [opts]
27
- * @returns {Promise<{available: boolean, godNodes: Array<{label: string, source_file: string, degree: number}>, communities: Array<{id: number, label: string, size: number, labeled: boolean}>}>}
28
- */
29
9
  export async function scanGraph(projectPath, opts = {}) {
30
10
  const top = opts.top ?? 10;
31
11
  const topCommunities = opts.topCommunities ?? 10;
@@ -1,11 +1,4 @@
1
- /**
2
- * Auditoria do grafo para o `morph-spec doctor`.
3
- *
4
- * Nunca devolve estado de erro: o grafo é opcional (ADR-002) e não pode pintar
5
- * de vermelho o doctor de um projeto que não o usa. O pior veredito é `warn`.
6
- *
7
- * @module graph-health
8
- */
1
+ // Nunca devolve erro: o grafo é opcional e não pode pintar de vermelho o doctor de quem não o usa.
9
2
  import { execFileSync } from 'node:child_process';
10
3
  import { readFileSync } from 'node:fs';
11
4
  import { join, resolve } from 'node:path';
@@ -16,17 +9,8 @@ import { GRAPHIFY_VERSION, detectGraphifyVersion } from './graphify-runtime.js';
16
9
 
17
10
  const GRAPHIFY_HOOK_MARKER = /# graphify-(?:checkout-)?hook-start/;
18
11
 
19
- /**
20
- * O hook git do próprio graphify (`graphify hook install`, que o setup-infra
21
- * instalava até a 8.37) está no repositório? Ele é danoso aqui: num commit
22
- * depois de um fast-forward carimba o HEAD num grafo SEM os arquivos que o
23
- * merge trouxe — "fresh" falso — e disputa o graph.json com o refresh do
24
- * morph-spec. Lê o diretório de hooks que o git usa (`core.hooksPath`
25
- * incluído).
26
- *
27
- * @param {string} projectPath
28
- * @returns {boolean}
29
- */
12
+ // O hook git do próprio graphify é danoso: depois de um fast-forward carimba o HEAD num grafo sem os
13
+ // arquivos do merge ("fresh" falso) e disputa o graph.json com o refresh. Respeita `core.hooksPath`.
30
14
  export function graphifyGitHookInstalled(projectPath) {
31
15
  let dir;
32
16
  try {
@@ -46,11 +30,6 @@ export function graphifyGitHookInstalled(projectPath) {
46
30
  });
47
31
  }
48
32
 
49
- /**
50
- * @param {string} projectPath
51
- * @param {{_version?: () => string|null}} [deps]
52
- * @returns {{status: 'ok'|'warn'|'absent', msg: string, details: string[]}}
53
- */
54
33
  export function auditGraph(projectPath, deps = {}) {
55
34
  const installed = (deps._version ?? detectGraphifyVersion)();
56
35
  const loaded = loadGraph(projectPath);
@@ -1,15 +1,4 @@
1
- /**
2
- * Leitura fail-open do graph.json produzido pelo graphify.
3
- *
4
- * Medido em 2026-08-27 sobre o MORPH_GHLBrain (11,46 MB, 6.829 nós, 15.668
5
- * arestas): JSON.parse leva 52 ms e 28 MB de heap. Streaming e cache são
6
- * desnecessários nesse porte — se um dia deixarem de ser, a troca acontece só
7
- * aqui, porque este módulo é a única porta de leitura do grafo.
8
- *
9
- * Nada aqui lança: ausência de grafo é um estado válido do projeto (ADR-002).
10
- *
11
- * @module graph-loader
12
- */
1
+ // A única porta de leitura do grafo, e nada aqui lança: ausência de grafo é um estado válido do projeto.
13
2
  import { readFileSync, existsSync } from 'node:fs';
14
3
  import { join } from 'node:path';
15
4
  import {
@@ -21,18 +10,10 @@ import {
21
10
  import { isSecondaryWorktree, workTreeTop } from '../../../framework/hooks/shared/git-paths.js';
22
11
  import { GRAPHIFY_PACKAGE, GRAPHIFY_VERSION } from './graphify-runtime.js';
23
12
 
24
- /**
25
- * @param {string} projectPath
26
- * @returns {string} caminho canônico do graph.json do projeto
27
- */
28
13
  export function resolveGraphPath(projectPath) {
29
14
  return join(projectPath, 'graphify-out', 'graph.json');
30
15
  }
31
16
 
32
- /**
33
- * @param {string} projectPath
34
- * @returns {{ok: boolean, reason?: 'missing'|'unreadable'|'malformed', graph?: object, path: string}}
35
- */
36
17
  export function loadGraph(projectPath) {
37
18
  const path = resolveGraphPath(projectPath);
38
19
  if (!existsSync(path)) return { ok: false, reason: 'missing', path };
@@ -57,18 +38,7 @@ export function loadGraph(projectPath) {
57
38
  }
58
39
  }
59
40
 
60
- /**
61
- * O frescor do grafo, pela regra única (framework/hooks/shared/graph-freshness.js):
62
- * o carimbo e o registro da última rodada contra o HEAD, e as regras do
63
- * `.graphifyignore` por conteúdo. Um grafo velho diz "não existe" sobre o que
64
- * o último commit trouxe — quem consome precisa saber, ou conclui "ninguém usa
65
- * isso" a partir dele.
66
- *
67
- * @param {string} projectPath
68
- * @param {object} graph - o graph.json já parseado (o carimbo sai dele)
69
- * @param {{_head?: (projectPath: string) => string|null}} [deps]
70
- * @returns {{builtAtCommit: string|null, headCommit: string|null, state: 'fresh'|'stale'|'unknown', reason?: 'ignore-changed'|'rules-unknown'}}
71
- */
41
+ // Um grafo velho diz "não existe" sobre o que o último commit trouxe: quem consome precisa saber.
72
42
  export function getFreshness(projectPath, graph, deps = {}) {
73
43
  const f = graphFreshness(projectPath, {
74
44
  graph,
@@ -84,12 +54,6 @@ export function getFreshness(projectPath, graph, deps = {}) {
84
54
 
85
55
  const short = (sha) => (sha ? String(sha).slice(0, 8) : '?');
86
56
 
87
- /**
88
- * O porquê de um grafo stale, em uma linha — carimbo antigo ou regras.
89
- *
90
- * @param {{builtAtCommit: string|null, headCommit: string|null, reason?: string}} freshness
91
- * @returns {string}
92
- */
93
57
  export function staleCause(freshness) {
94
58
  if (freshness.reason === 'ignore-changed') {
95
59
  return 'as regras do .graphifyignore mudaram depois da extração';
@@ -100,15 +64,8 @@ export function staleCause(freshness) {
100
64
  return `construído em ${short(freshness.builtAtCommit)}, HEAD é ${short(freshness.headCommit)}`;
101
65
  }
102
66
 
103
- /**
104
- * O que o refresh automático faz por esta árvore. Ele cobre só a árvore
105
- * principal (`auto`); numa worktree, `running` é sempre false — um lock vivo
106
- * ali seria o refresh da raiz. `last` é a última rodada registrada para esta
107
- * árvore: em background, manual ou a semeadura.
108
- *
109
- * @param {string} projectPath
110
- * @returns {{auto: boolean, running: boolean, since: string|null, pid: number|null, last: {ok: boolean, exit: number|null, head: string|null, ts: string, unavailable?: boolean, seeded?: boolean}|null}}
111
- */
67
+ // O refresh automático cobre só a árvore principal; numa worktree `running` é sempre false, porque um
68
+ // lock vivo ali seria o refresh da raiz.
112
69
  export function refreshState(projectPath) {
113
70
  const auto = isPrimaryTree(projectPath);
114
71
  const lock = auto ? readRefreshLock(projectPath) : null;
@@ -129,15 +86,7 @@ export function refreshState(projectPath) {
129
86
  };
130
87
  }
131
88
 
132
- /**
133
- * A frase de grafo stale — a mesma no status, no explain, no affected e no
134
- * doctor, e verdadeira sobre o mecanismo: o refresh em background só cobre a
135
- * raiz, disparado pelos hooks do morph-spec; a worktree só anda à mão.
136
- *
137
- * @param {string} projectPath
138
- * @param {{builtAtCommit?: string|null, headCommit?: string|null}} [freshness]
139
- * @returns {string}
140
- */
89
+ /** A frase única de grafo stale do status, explain, affected e doctor. */
141
90
  export function describeStale(projectPath, freshness = {}) {
142
91
  const tree = workTreeTop(projectPath) ?? projectPath;
143
92
  const state = refreshState(projectPath);
@@ -1,23 +1,9 @@
1
- /**
2
- * Descoberta do runtime Python e argv de instalação do graphify.
3
- *
4
- * O binário instalado chama-se `graphify` (e `graphify-mcp` para o servidor
5
- * MCP). NUNCA use `python -m graphify.serve`, que é o que o README oficial
6
- * manda: com `uv tool install` o pacote vive num venv isolado e o Python do
7
- * sistema não o enxerga (ModuleNotFoundError) — medido no eval, Eixo 2.
8
- *
9
- * Toda descoberta é injetável (`_which`, `_run`) para que o teste cubra Windows
10
- * e POSIX sem depender do PATH da máquina que roda o CI.
11
- *
12
- * @module graphify-runtime
13
- */
1
+ // O binário é `graphify` (e `graphify-mcp`). NUNCA `python -m graphify.serve`, como manda o README oficial:
2
+ // com `uv tool install` o pacote vive num venv isolado que o Python do sistema não enxerga.
14
3
  import { execFileSync } from 'node:child_process';
15
4
 
16
- /**
17
- * Pin exato: 0.9.50 é a única versão medida no eval e a base do fixture de
18
- * regressão. O graphify é pré-1.0 com release quase diária, então semver não
19
- * garante nada — bump é decisão consciente, com re-run do fixture.
20
- */
5
+ // Pin exato: o graphify é pré-1.0 com release quase diária e semver não garante nada; subir é decisão
6
+ // consciente, com o fixture de regressão regerado.
21
7
  export const GRAPHIFY_VERSION = '0.9.50';
22
8
  export const GRAPHIFY_PACKAGE = 'graphifyy';
23
9
  export const GRAPHIFY_BIN = 'graphify';
@@ -38,14 +24,7 @@ function whichSync(bin) {
38
24
  }
39
25
  }
40
26
 
41
- /**
42
- * uv > pipx > python3 > python. `kind: null` significa "sem Python": o step do
43
- * setup-infra registra e segue, e o framework continua funcionando por grep
44
- * (ADR-002).
45
- *
46
- * @param {{_which?: (bin: string) => string|null}} [deps]
47
- * @returns {{kind: 'uv'|'pipx'|'python3'|'python'|null, bin: string|null}}
48
- */
27
+ // `kind: null` é "sem Python": o setup registra e segue, e o framework continua funcionando por grep.
49
28
  export function detectInstaller(deps = {}) {
50
29
  const which = deps._which ?? whichSync;
51
30
  for (const kind of PREFERENCE) {
@@ -60,13 +39,7 @@ export function detectInstaller(deps = {}) {
60
39
  return { kind: null, bin: null };
61
40
  }
62
41
 
63
- /**
64
- * @param {'uv'|'pipx'|'python3'|'python'} kind
65
- * @param {{withSql: boolean}} opts sem o extra `[sql]` os arquivos .sql são
66
- * ignorados em silêncio (92 no ProspectPRO) — só um aviso no meio do output
67
- * denuncia
68
- * @returns {{cmd: string, args: string[]}}
69
- */
42
+ // Sem o extra `[sql]` os arquivos .sql são ignorados em silêncio.
70
43
  export function installArgs(kind, { withSql }) {
71
44
  const spec = `${GRAPHIFY_PACKAGE}${withSql ? '[sql]' : ''}==${GRAPHIFY_VERSION}`;
72
45
  switch (kind) {
@@ -82,10 +55,7 @@ export function installArgs(kind, { withSql }) {
82
55
  }
83
56
  }
84
57
 
85
- /**
86
- * @param {{_run?: () => string}} [deps]
87
- * @returns {string|null} versão instalada, ou null se o binário não responde
88
- */
58
+ /** Versão instalada, ou null se o binário não responde. */
89
59
  export function detectGraphifyVersion(deps = {}) {
90
60
  const run = deps._run ?? (() => execFileSync(GRAPHIFY_BIN, ['--version'], {
91
61
  encoding: 'utf-8',
@@ -1,32 +1,6 @@
1
- /**
2
- * Geração stack-aware do .graphifyignore (ADR-004).
3
- *
4
- * Filtrar teste não é cosmético: sem isso o god node #1 do MORPH_GHLBrain é
5
- * `FakeGhlToolbelt`, um dublê. E o padrão genérico `tests/` NÃO serve em .NET:
6
- * no ProspectPRO os testes vivem em `src/backend/ProspectPRO.Tests/`. Com
7
- * `**\/*.Tests/` o grafo caiu de 10.450 para 7.384 nós — 30% era ruído de teste.
8
- *
9
- * Mas dublê, fixture e helper de teste precisam ser ACHÁVEIS: são o que um
10
- * teste novo deveria reusar, e fora do grafo `graph explain FakeX` dá
11
- * not-found. Por isso o conteúdo dos projetos de teste sai e esses arquivos
12
- * voltam. A forma é ditada pela regra do gitignore que o graphify 0.9.50 segue
13
- * (detect.py `_is_ignored`): um `!` não reinclui nada sob um diretório já
14
- * excluído. Medido num fixture: `**\/*.Tests/` + `!**\/Fakes/` não traz nada de
15
- * volta; excluir o CONTEÚDO (`**\/*.Tests/**`), reincluir os diretórios
16
- * (`!**\/*.Tests/**\/`) e então os helpers traz. `bin/`/`obj/` vêm DEPOIS das
17
- * reinclusões — antes, a reinclusão de diretório os devolveria (`obj/` tem
18
- * `.cs` gerado). No MORPH_GHLBrain: +9,6% de nós (11.591 → 12.706), 94
19
- * arquivos de teste, nenhum de bin/obj.
20
- *
21
- * Os dublês quebram o guard de "implementador único" do `dispatches_to` do
22
- * graphify (de 122 para 5 arestas no GHLBrain); o saneamento as recupera
23
- * (sanitize.js `recoverDispatch`) e tira o teste dos god nodes.
24
- *
25
- * Em Next.js o problema é outro: metade dos nós vinha de JSON de configuração,
26
- * e os god nodes viravam `dependencies` e `compilerOptions`.
27
- *
28
- * @module graphifyignore
29
- */
1
+ // Teste sai do grafo, mas dublês e helpers voltam: são o que um teste novo reusa. Pela regra do gitignore
2
+ // um `!` não reinclui nada sob diretório excluído, então exclui-se o conteúdo, reincluem-se os diretórios e
3
+ // então os helpers; `bin/`/`obj/` vêm depois, senão a reinclusão os devolveria.
30
4
  import { writeFileSync, existsSync, readFileSync } from 'node:fs';
31
5
  import { execFileSync } from 'node:child_process';
32
6
  import { join } from 'node:path';
@@ -60,10 +34,7 @@ const WEB_IGNORED = [
60
34
 
61
35
  const HEADER = '# Gerado por morph-spec (ADR-004). Editável: um arquivo editado nunca é sobrescrito.';
62
36
 
63
- /**
64
- * O que a versão anterior gerava — os testes .NET inteiros fora. Só existe
65
- * para reconhecer um arquivo que ninguém editou e trocá-lo pelo atual.
66
- */
37
+ // O que a geração anterior escrevia: só reconhece um arquivo que ninguém editou, para trocá-lo.
67
38
  const PREVIOUS = {
68
39
  header: '# Gerado por morph-spec (ADR-004). Editável: o CLI não sobrescreve.',
69
40
  dotnetTitle: '# .NET — testes em qualquer nível (tests/ genérico não serve)',
@@ -99,11 +70,6 @@ function render(projectTags, { header, dotnetTitle, dotnet }) {
99
70
  return `${lines.join('\n')}\n`;
100
71
  }
101
72
 
102
- /**
103
- * @param {string[]} [projectTags] saída de `getProjectTags(config)` — stack
104
- * splitado por `-`/`+` mais as integrations
105
- * @returns {string} conteúdo completo do .graphifyignore
106
- */
107
73
  export function buildGraphifyignore(projectTags = []) {
108
74
  return render(projectTags, {
109
75
  header: HEADER,
@@ -114,28 +80,8 @@ export function buildGraphifyignore(projectTags = []) {
114
80
 
115
81
  const normalize = (text) => String(text).replace(/^/, '').replace(/\r\n/g, '\n');
116
82
 
117
- /**
118
- * Escreve ou migra o .graphifyignore.
119
- *
120
- * ausente → escreve o atual
121
- * idêntico ao que a versão anterior
122
- * gerava para as mesmas tags → troca pelo atual (`migrated`) —
123
- * só se as REGRAS mudam (em
124
- * projeto sem .NET a diferença é
125
- * só o cabeçalho, e reescrever
126
- * seria um diff à toa num arquivo
127
- * versionado)
128
- * qualquer outra coisa → é do usuário: não toca, e em
129
- * projeto .NET que ainda exclui os
130
- * testes inteiros devolve em
131
- * `outdated` a troca a fazer
132
- *
133
- * @param {string} projectPath
134
- * @param {string[]} [projectTags]
135
- * @param {{fx?: {writeFile: (path: string, content: string) => void}}} [opts]
136
- * coletor de efeitos do `update` — com ele, o `--dry-run` prevê a escrita
137
- * @returns {{written: boolean, migrated?: boolean, outdated?: {remove: string[], add: string[]}, path: string}}
138
- */
83
+ // Ausente → escreve. Idêntico ao que a geração anterior escrevia → migra, só se as regras mudam.
84
+ // Qualquer outra coisa é do usuário: não toca, e em .NET devolve em `outdated` a troca a fazer.
139
85
  export function writeGraphifyignore(projectPath, projectTags = [], { fx } = {}) {
140
86
  const path = join(projectPath, '.graphifyignore');
141
87
  const current = buildGraphifyignore(projectTags);
@@ -172,26 +118,14 @@ export function writeGraphifyignore(projectPath, projectTags = [], { fx } = {})
172
118
  return { written: false, path };
173
119
  }
174
120
 
175
- /**
176
- * A frase que todo chamador imprime para um `outdated` — uma só.
177
- *
178
- * @param {{remove: string[], add: string[]}} outdated
179
- * @returns {string}
180
- */
121
+ /** A frase única que todo chamador imprime para um `outdated`. */
181
122
  export function describeOutdatedIgnore(outdated) {
182
123
  return `.graphifyignore editado mantém os testes .NET inteiros fora do grafo ` +
183
124
  `(dublês e fixtures dão not-found). Troque ${outdated.remove.join(' ')} por, nesta ordem e ` +
184
125
  `antes de bin/ e obj/: ${outdated.add.join(' ')} — depois, morph-spec graph refresh`;
185
126
  }
186
127
 
187
- /**
188
- * O `.graphifyignore` é versionado? Então uma migração só chega às worktrees
189
- * e ao resto do time depois de commitada — cada checkout lê o seu. Fail-open:
190
- * sem git, false.
191
- *
192
- * @param {string} projectPath
193
- * @returns {boolean}
194
- */
128
+ // Versionado, uma migração só chega às worktrees depois de commitada. Fail-open: sem git, false.
195
129
  export function graphifyignoreTracked(projectPath) {
196
130
  try {
197
131
  execFileSync('git', ['ls-files', '--error-unmatch', '--', '.graphifyignore'], {