@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,7 +1 @@
1
- /**
2
- * Contract Validators
3
- *
4
- * API contract compliance validation.
5
- */
6
-
7
1
  export * from './contract-compliance-validator.js';
@@ -1,6 +1 @@
1
- /**
2
- * Next.js Validators
3
- *
4
- * Validators specific to Next.js App Router patterns.
5
- */
6
1
  export { validateNextComponent, validateNextComponentFiles } from './next-component-validator.js';
@@ -2,25 +2,8 @@ import { glob } from 'glob';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
4
 
5
- /**
6
- * Next.js Component Validator
7
- *
8
- * Validates .tsx files for:
9
- * 1. Unnecessary 'use client' directives (no interactivity)
10
- * 2. Missing 'use client' when React hooks are used
11
- * 3. File naming convention (kebab-case)
12
- *
13
- * @module next-component-validator
14
- */
15
-
16
- // ============================================
17
- // CONSTANTS
18
- // ============================================
19
-
20
- /** Hooks/patterns that require 'use client' */
21
5
  const CLIENT_HOOKS = ['useState', 'useEffect', 'useRef', 'useReducer', 'useCallback', 'useMemo', 'useContext'];
22
6
 
23
- /** Event handler patterns that indicate interactivity (hooks are checked via CLIENT_HOOKS) */
24
7
  const EVENT_HANDLER_PATTERNS = [
25
8
  /\bonClick\b/,
26
9
  /\bonChange\b/,
@@ -28,66 +11,29 @@ const EVENT_HANDLER_PATTERNS = [
28
11
  /\bonFocus\b/,
29
12
  /\bonBlur\b/,
30
13
  /\bonKeyDown\b/,
31
- // Qualquer outro handler de evento em JSX (onMouseEnter, onKeyUp, onScroll, onDrop…).
32
- // Sem isto, a lista fechada acima transforma interatividade legítima em falso-positivo.
33
14
  /\bon[A-Z]\w*\s*=\s*\{/,
34
15
  ];
35
16
 
36
- /**
37
- * Chamada de QUALQUER hook, não só os sete nativos: `useRouter()`, `useQuery()`, `useForm<T>()`.
38
- * O `[<(]` é o que faz `useState<Record<string, string>>(` contar — um hook com argumento de
39
- * tipo não casa com `useState\s*\(` e passava despercebido.
40
- */
17
+ // `[<(]` faz hook com argumento de tipo (`useState<Record<string, string>>(`) contar.
41
18
  const HOOK_CALL_PATTERN = /\buse[A-Z]\w*\s*[<(]/;
42
19
 
43
- /**
44
- * APIs de React que só existem no cliente. `forwardRef` e `createContext` lançam em Server
45
- * Component, então um módulo que os usa PRECISA da diretiva, mesmo sem hook nem handler.
46
- */
20
+ // Lançam em Server Component: o módulo precisa da diretiva mesmo sem hook nem handler.
47
21
  const CLIENT_ONLY_APIS = [/\bforwardRef\b/, /\bcreateContext\b/];
48
22
 
49
- /** Globais de browser: quem os toca não roda no servidor. */
50
23
  const BROWSER_GLOBALS = /\b(window|document|localStorage|sessionStorage|navigator)\s*[.[]/;
51
24
 
52
- /**
53
- * Pacotes cujos componentes são client-only. O caso que mais gerava ruído é o wrapper de
54
- * shadcn/Radix: ele só reexporta primitivas, então não tem hook nem handler no próprio
55
- * arquivo — e era acusado para sempre, apesar de estar correto.
56
- */
25
+ // Wrapper shadcn/Radix só reexporta primitivas client-only, sem hook nem handler no arquivo.
57
26
  const CLIENT_ONLY_IMPORTS = /from\s+['"](@radix-ui\/|sonner|@react-pdf\/|file-saver|framer-motion|motion\/|recharts|next\/navigation|react-hook-form|@tanstack\/react-query|embla-carousel|cmdk|vaul|react-day-picker|input-otp|@dnd-kit\/)/;
58
27
 
59
- /** Next.js special files exempt from naming checks */
60
28
  const NEXTJS_SPECIAL_FILES = ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx', 'not-found.tsx', 'route.ts', 'middleware.ts'];
61
29
 
62
- // ============================================
63
- // MAIN VALIDATION FUNCTION
64
- // ============================================
65
-
66
- /**
67
- * @typedef {Object} ValidationIssue
68
- * @property {'error' | 'warning' | 'info'} type
69
- * @property {string} message
70
- * @property {string} [suggestion]
71
- * @property {string} file
72
- * @property {number} [line]
73
- */
74
-
75
- /**
76
- * Validates a Next.js component file.
77
- *
78
- * @param {string} content - File content
79
- * @param {string} filePath - Relative file path
80
- * @returns {ValidationIssue[]}
81
- */
82
30
  export function validateNextComponent(content, filePath) {
83
31
  const issues = [];
84
32
 
85
- // Only validate .tsx and .ts files
86
33
  if (!filePath.endsWith('.tsx') && !filePath.endsWith('.ts')) {
87
34
  return issues;
88
35
  }
89
36
 
90
- // Skip non-component utility files (no JSX, no hooks)
91
37
  const hasJsx = /(?<![a-zA-Z0-9])<[A-Z][a-zA-Z]*[\s/>]|(?<![a-zA-Z0-9])<[a-z][a-z]+[\s>/]/.test(content);
92
38
  const hasHooks = CLIENT_HOOKS.some(hook => content.includes(hook));
93
39
 
@@ -97,13 +43,10 @@ export function validateNextComponent(content, filePath) {
97
43
 
98
44
  const hasUseClient = /^'use client';?|^"use client";?/.test(content.trimStart());
99
45
 
100
- // Check 1: use client present but no interactivity
101
46
  if (hasUseClient) {
102
47
  const hasEventHandlers = EVENT_HANDLER_PATTERNS.some(pattern => pattern.test(content));
103
48
  const hasClientHooks = CLIENT_HOOKS.some(hook => new RegExp(`\\b${hook}\\b`).test(content));
104
- // Só acusa quando NENHUM sinal de necessidade de cliente aparece. Um falso-positivo aqui
105
- // pede a remoção de uma diretiva que o build precisa, então a checagem erra para o lado
106
- // de ficar calada.
49
+ // Falso-positivo pediria remover uma diretiva que o build precisa: erra para o lado calado.
107
50
  const needsClient =
108
51
  hasEventHandlers ||
109
52
  hasClientHooks ||
@@ -121,7 +64,6 @@ export function validateNextComponent(content, filePath) {
121
64
  }
122
65
  }
123
66
 
124
- // Check 2: hooks used without use client
125
67
  // .ts files (custom hooks, utilities) never render JSX — skip this check
126
68
  if (!hasUseClient) {
127
69
  if (!filePath.endsWith('.tsx')) return issues;
@@ -137,7 +79,6 @@ export function validateNextComponent(content, filePath) {
137
79
  }
138
80
  }
139
81
 
140
- // Check 3: file naming convention (kebab-case)
141
82
  const fileName = filePath.split('/').pop() ?? '';
142
83
  const isSpecialFile = NEXTJS_SPECIAL_FILES.includes(fileName);
143
84
 
@@ -157,10 +98,6 @@ export function validateNextComponent(content, filePath) {
157
98
  return issues;
158
99
  }
159
100
 
160
- // ============================================
161
- // HELPERS
162
- // ============================================
163
-
164
101
  function toKebabCase(str) {
165
102
  return str
166
103
  .replace(/([A-Z])/g, '-$1')
@@ -168,22 +105,9 @@ function toKebabCase(str) {
168
105
  .replace(/^-/, '');
169
106
  }
170
107
 
171
- // ============================================
172
- // PROJECT-LEVEL SCANNER
173
- // ============================================
174
-
175
- /**
176
- * Validates all Next.js component files in a project directory.
177
- * Wraps validateNextComponent for use in validation-runner.js.
178
- *
179
- * @param {string} projectPath - Root path of the project
180
- * @param {Object} _options - Validator options (unused, for interface compatibility)
181
- * @returns {Promise<{ errors: number, warnings: number, issues: Array }>}
182
- */
183
108
  export async function validateNextComponentFiles(projectPath, _options = {}) {
184
109
  const result = { errors: 0, warnings: 0, issues: [] };
185
110
 
186
- // Find all .tsx and .ts files under src/
187
111
  const srcDir = join(projectPath, 'src');
188
112
  const normalizedSrc = srcDir.replace(/\\/g, '/');
189
113
  const pattern = normalizedSrc + '/**/*.{tsx,ts}';
@@ -197,7 +121,6 @@ export async function validateNextComponentFiles(projectPath, _options = {}) {
197
121
  continue;
198
122
  }
199
123
 
200
- // Make path relative for cleaner output
201
124
  const normFilePath = filePath.replace(/\\/g, '/');
202
125
  const relPath = normFilePath.replace(projectPath.replace(/\\/g, '/') + '/', '');
203
126
  const fileIssues = validateNextComponent(content, relPath);
@@ -1,7 +1 @@
1
- /**
2
- * Package Validators
3
- *
4
- * Package and dependency validation.
5
- */
6
-
7
1
  export * from './package-validator.js';
@@ -1,21 +1,9 @@
1
- /**
2
- * Package Compatibility Validator
3
- *
4
- * Validates NuGet package versions against .NET compatibility matrix.
5
- * Critical for .NET 10 migration - prevents runtime errors.
6
- *
7
- * MORPH-SPEC 3.0 - Sprint 4
8
- */
9
-
10
1
  import { readFileSync } from 'fs';
11
2
  import { join } from 'path';
12
3
  import { glob } from 'glob';
13
4
  import chalk from 'chalk';
14
5
 
15
- /**
16
- * Compatibility matrix for .NET versions
17
- * Source: framework/standards/dotnet10-compatibility.md
18
- */
6
+ // Source: framework/standards/dotnet10-compatibility.md
19
7
  const COMPATIBILITY_MATRIX = {
20
8
  'Hangfire.AspNetCore': {
21
9
  9: '1.8.0',
@@ -50,7 +38,7 @@ const COMPATIBILITY_MATRIX = {
50
38
  breaking: false
51
39
  },
52
40
  'Microsoft.SemanticKernel': {
53
- 9: null, // Not compatible with .NET 10
41
+ 9: null,
54
42
  10: null,
55
43
  deprecated: true,
56
44
  replacement: 'Microsoft.Extensions.AI.Abstractions',
@@ -58,17 +46,11 @@ const COMPATIBILITY_MATRIX = {
58
46
  }
59
47
  };
60
48
 
61
- /**
62
- * Package Validator Class
63
- */
64
49
  export class PackageValidator {
65
50
  constructor(projectPath = '.') {
66
51
  this.projectPath = projectPath;
67
52
  }
68
53
 
69
- /**
70
- * Validate all .csproj files in project
71
- */
72
54
  async validateAll() {
73
55
  const csprojFiles = await glob('**/*.csproj', {
74
56
  cwd: this.projectPath,
@@ -86,10 +68,7 @@ export class PackageValidator {
86
68
  const allIssues = [];
87
69
 
88
70
  for (const file of csprojFiles) {
89
- // glob returns paths relative to `cwd: this.projectPath`, so they only
90
- // resolve against projectPath — reading them relative to process.cwd()
91
- // raised ENOENT whenever the two differed (worktree, fleet, a verify
92
- // launched from the primary root), and the throw read as a clean pass.
71
+ // glob paths are relative to `cwd: this.projectPath`, never to process.cwd().
93
72
  const result = await this.validateFile(join(this.projectPath, file));
94
73
  if (result.issues.length > 0) {
95
74
  results.push({ file, ...result });
@@ -101,9 +80,7 @@ export class PackageValidator {
101
80
  status: results.length === 0 ? 'ok' : 'error',
102
81
  totalFiles: csprojFiles.length,
103
82
  filesWithIssues: results.length,
104
- // The runner's contract. Without these three the findings below were
105
- // read as `errors: 0` and the run reported "✅ All validations passed"
106
- // while holding a list of NuGet packages that do not build on .NET 10.
83
+ // The runner's contract: without these counts the findings read as `errors: 0`.
107
84
  errors: allIssues.filter(i => i.level === 'error').length,
108
85
  warnings: allIssues.filter(i => i.level === 'warning').length,
109
86
  issues: allIssues,
@@ -111,13 +88,9 @@ export class PackageValidator {
111
88
  };
112
89
  }
113
90
 
114
- /**
115
- * Validate single .csproj file
116
- */
117
91
  async validateFile(filePath) {
118
92
  const content = readFileSync(filePath, 'utf-8');
119
93
 
120
- // Extract .NET version
121
94
  const tfmMatch = content.match(/<TargetFramework>(.*?)<\/TargetFramework>/);
122
95
  if (!tfmMatch) {
123
96
  return {
@@ -142,10 +115,8 @@ export class PackageValidator {
142
115
  };
143
116
  }
144
117
 
145
- // Extract package references
146
118
  const packages = this.extractPackages(content);
147
119
 
148
- // Validate each package
149
120
  const issues = [];
150
121
 
151
122
  for (const pkg of packages) {
@@ -162,9 +133,6 @@ export class PackageValidator {
162
133
  };
163
134
  }
164
135
 
165
- /**
166
- * Extract package references from .csproj content
167
- */
168
136
  extractPackages(csprojContent) {
169
137
  const packages = [];
170
138
  const packageRegex = /<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
@@ -180,18 +148,13 @@ export class PackageValidator {
180
148
  return packages;
181
149
  }
182
150
 
183
- /**
184
- * Validate single package against compatibility matrix
185
- */
186
151
  validatePackage(pkg, dotnetVersion) {
187
152
  const rule = COMPATIBILITY_MATRIX[pkg.name];
188
153
 
189
154
  if (!rule) {
190
- // Package not in matrix - no validation
191
155
  return null;
192
156
  }
193
157
 
194
- // Check if deprecated
195
158
  if (rule.deprecated) {
196
159
  return {
197
160
  level: 'error',
@@ -204,7 +167,6 @@ export class PackageValidator {
204
167
  };
205
168
  }
206
169
 
207
- // Check if version is sufficient
208
170
  const requiredVersion = rule[dotnetVersion];
209
171
 
210
172
  if (!requiredVersion) {
@@ -235,13 +197,10 @@ export class PackageValidator {
235
197
  };
236
198
  }
237
199
 
238
- return null; // Package is compatible
200
+ return null;
239
201
  }
240
202
 
241
- /**
242
- * Compare semantic versions
243
- * Returns: -1 (v1 < v2), 0 (equal), 1 (v1 > v2)
244
- */
203
+ // -1 (v1 < v2), 0 (equal), 1 (v1 > v2)
245
204
  compareVersions(v1, v2) {
246
205
  const parts1 = v1.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);
247
206
  const parts2 = v2.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);
@@ -259,9 +218,6 @@ export class PackageValidator {
259
218
  return 0;
260
219
  }
261
220
 
262
- /**
263
- * Format validation results for console output
264
- */
265
221
  formatResults(results) {
266
222
  if (results.status === 'ok') {
267
223
  console.log(chalk.green('✅ All packages are compatible'));
@@ -314,9 +270,6 @@ export class PackageValidator {
314
270
  }
315
271
  }
316
272
 
317
- /**
318
- * Auto-fix compatible issues
319
- */
320
273
  async autoFix(results) {
321
274
  const { execSync } = await import('child_process');
322
275
  let fixedCount = 0;
@@ -340,9 +293,6 @@ export class PackageValidator {
340
293
  }
341
294
  }
342
295
 
343
- /**
344
- * Quick validation function (for imports)
345
- */
346
296
  export async function validatePackages(projectPath = '.', options = {}) {
347
297
  const validator = new PackageValidator(projectPath);
348
298
  const results = await validator.validateAll();
@@ -1,10 +1,2 @@
1
- /**
2
- * Reuse Validators
3
- *
4
- * Cobrança determinística de reuso de código: símbolo novo cujo nome já existe
5
- * no grafo do codebase, na mesma linguagem. É a terceira camada do ciclo
6
- * PROSA → ARTEFATO → VERIFICAÇÃO (ver reuse-validator.js).
7
- */
8
-
9
1
  export * from './reuse-validator.js';
10
2
  export * from './symbol-scanner.js';
@@ -1,32 +1,6 @@
1
- /**
2
- * Reuse Validator — símbolo novo cujo nome já existe no codebase.
3
- *
4
- * A terceira camada do ciclo de reuso:
5
- *
6
- * PROSA (morph-plan, personas) ──► ARTEFATO (seção de reuso do plano)
7
- * ──► COBRANÇA (este arquivo)
8
- *
9
- * Prosa sozinha não segura — a skill de planejamento já mandava olhar o que
10
- * existe, e o ProspectPRO redefiniu `SimpleBuilding` em cinco arquivos da
11
- * MESMA pasta assim mesmo: só a verificação em código segura o ciclo.
12
- *
13
- * Regras, na ordem em que reduzem ruído:
14
- *
15
- * 1. Só tipo (class/record/interface/enum/struct/type) — não função nem
16
- * variável. É a categoria que o grafo casa com confiança.
17
- * 2. Só MESMA LINGUAGEM. Contrato espelhado entre C# e TS (110 casos no
18
- * ProspectPRO) é o escopo F da proposta: exige gerar tipos do contrato,
19
- * não se resolve por aviso, e fica FORA daqui de propósito.
20
- * 3. Mesma feature = achado (`error`); feature diferente = aviso
21
- * (`warning`), porque VSA prescreve DTO homônimo entre slices.
22
- * 4. Veredito `CRIAR` declarado na tabela de reuso do plano rebaixa o
23
- * achado a aviso: a decisão foi tomada no gate do plano, com justificativa
24
- * em `why`. O validador cobra a decisão AUSENTE, não a decisão tomada.
25
- *
26
- * Sem grafo o validador não roda (ADR-002: ausência de grafo nunca bloqueia).
27
- *
28
- * @module reuse-validator
29
- */
1
+ // Só tipos, só mesma linguagem (contrato espelhado C#↔TS não se resolve por aviso).
2
+ // Mesma feature = error; outra feature = warning (VSA admite DTO homônimo entre slices).
3
+ // Veredito CRIAR no plano rebaixa a warning: cobra-se a decisão AUSENTE, não a tomada.
30
4
 
31
5
  import { existsSync, readFileSync } from 'fs';
32
6
  import { execFileSync } from 'child_process';
@@ -42,26 +16,15 @@ import {
42
16
  } from './symbol-scanner.js';
43
17
  import { readPlanChecks } from '../../plan/checks.js';
44
18
 
45
- /** Quantos locais citar por achado antes de resumir com "(+N)". */
46
19
  const MAX_CITED = 4;
47
20
 
48
- /**
49
- * Índice "o que já existe": nome em minúsculas -> ocorrências reais do grafo.
50
- *
51
- * Só nós com `source_file` preenchido entram — nó com `source_file: ""` é stub
52
- * não resolvido (BCL, NuGet, pacote externo), e apontar `Task` do .NET como
53
- * duplicação seria ruído garantido. É o mesmo critério do `buildIndex`.
54
- *
55
- * @param {{nodes: object[]}} graph
56
- * @returns {Map<string, Array<{name: string, file: string, language: string, feature: string}>>}
57
- */
58
21
  export function buildExistingIndex(graph) {
59
22
  const index = new Map();
60
23
 
61
24
  for (const node of graph?.nodes ?? []) {
25
+ // `source_file` vazio é stub não resolvido (BCL, NuGet): `Task` do .NET não é duplicação.
62
26
  if (!node.source_file) continue;
63
- // `_callable_class` é a marca de tipo do graphify; nós de ARQUIVO também a
64
- // carregam, e se distinguem pelo label com extensão.
27
+ // Nós de ARQUIVO também têm `_callable_class`; distinguem-se pelo label com extensão.
65
28
  if (node._callable_class !== true) continue;
66
29
  if (/\.[a-z0-9]+$/i.test(node.label)) continue;
67
30
  if (!isScannableFile(node.source_file)) continue;
@@ -81,23 +44,10 @@ export function buildExistingIndex(graph) {
81
44
  return index;
82
45
  }
83
46
 
84
- /**
85
- * Casa as declarações desta feature contra o que já existe.
86
- *
87
- * Puro e sem I/O — é aqui que a regra mora, e é isto que o teste roda contra o
88
- * grafo real do ProspectPRO.
89
- *
90
- * @param {Array<{name: string, kind: string, line: number, file: string}>} declarations
91
- * @param {Map<string, Array<{name: string, file: string, language: string, feature: string}>>} existing
92
- * @param {{verdicts?: Map<string, {verdict: string, target?: string}>}} [opts]
93
- * @returns {Array<object>} issues, uma por símbolo duplicado
94
- */
95
47
  export function findDuplicateSymbols(declarations, existing, opts = {}) {
96
48
  const verdicts = opts.verdicts ?? new Map();
97
49
  const issues = [];
98
50
 
99
- // Uma declaração por (nome, arquivo); o mesmo nome em N arquivos da feature
100
- // vira UM achado com N locais, não N achados.
101
51
  const byName = new Map();
102
52
  for (const decl of declarations) {
103
53
  const key = decl.name.toLowerCase();
@@ -112,17 +62,10 @@ export function findDuplicateSymbols(declarations, existing, opts = {}) {
112
62
  const declaredFiles = new Set(decls.map((d) => d.file));
113
63
  const language = languageOf(decls[0].file);
114
64
 
115
- // Onde mais esse nome vive: no grafo (código que já existia) e nos OUTROS
116
- // arquivos desta mesma leva (código novo que o grafo ainda não viu — ele
117
- // só é refeito depois do commit, e é exatamente o caso SimpleBuilding).
65
+ // A leva também conta: o grafo só é refeito depois do commit e ainda não viu o código novo.
118
66
  const fromGraph = (existing.get(key) ?? []).filter(
119
67
  (e) => e.language === language && !declaredFiles.has(e.file),
120
68
  );
121
- // O filtro de linguagem vale AQUI TAMBÉM, não só contra o grafo: uma leva
122
- // que toca `CatalogContracts.cs` e `catalog.types.ts` declara `CatalogItem`
123
- // nos dois, na mesma feature `Catalog/` — e sem este filtro isso vira erro,
124
- // que é precisamente o contrato espelhado C#↔TS que a proposta separa no
125
- // escopo F por não se resolver com aviso. Medido no MORPH_GHLBrain.
126
69
  const fromBatch = decls
127
70
  .slice(1)
128
71
  .filter((d) => languageOf(d.file) === language)
@@ -166,13 +109,6 @@ export function findDuplicateSymbols(declarations, existing, opts = {}) {
166
109
  });
167
110
  }
168
111
 
169
- /**
170
- * Vereditos de reuso do plano: a tabela `## Reuso` que o plan-check extraiu.
171
- *
172
- * @param {string} projectPath
173
- * @param {string} featureName
174
- * @returns {Map<string, {verdict: string, target?: string}>}
175
- */
176
112
  export function readReuseVerdicts(projectPath, featureName) {
177
113
  const verdicts = new Map();
178
114
  if (!featureName) return verdicts;
@@ -187,17 +123,8 @@ export function readReuseVerdicts(projectPath, featureName) {
187
123
  return verdicts;
188
124
  }
189
125
 
190
- /**
191
- * Arquivos que esta feature criou ou alterou.
192
- *
193
- * Duas fontes, unidas, porque cada uma é cega no momento em que a outra
194
- * enxerga: os `arquivos:` das fatias são o contrato do plano e sobrevivem ao
195
- * commit; `git status` pega o que o plano não previu. Sem git, sem drama.
196
- *
197
- * @param {string} projectPath
198
- * @param {string} featureName
199
- * @returns {string[]} paths relativos ao projeto, com barra normal
200
- */
126
+ // União de duas fontes cegas em momentos diferentes: `arquivos:` das fatias sobrevivem
127
+ // ao commit; `git status` pega o que o plano não previu.
201
128
  export function collectChangedFiles(projectPath, featureName) {
202
129
  const files = new Set();
203
130
  for (const slice of readPlanChecks(projectPath, featureName).checks?.slices ?? []) {
@@ -215,11 +142,7 @@ export function collectChangedFiles(projectPath, featureName) {
215
142
  });
216
143
  for (const line of out.split(/\r?\n/)) {
217
144
  if (!line.trim()) continue;
218
- // "XY path" e, em rename, "XY antigo -> novo": só o destino interessa.
219
- // Os paths vêm relativos à RAIZ do repositório — que no morph-spec é a
220
- // própria árvore da feature (cada worktree é a sua raiz). Se algum dia
221
- // não coincidir com `projectPath`, o `existsSync` adiante descarta o
222
- // path: perde-se cobertura, nunca se acusa um arquivo que não existe.
145
+ // Rename vem como "XY antigo -> novo": só o destino interessa.
223
146
  const path = line.slice(3).split(' -> ').pop().replace(/^"|"$/g, '');
224
147
  if (path) files.add(path.replace(/\\/g, '/'));
225
148
  }
@@ -230,17 +153,6 @@ export function collectChangedFiles(projectPath, featureName) {
230
153
  return [...files];
231
154
  }
232
155
 
233
- /**
234
- * Validador plugado no `validation-runner` (e por ele no nó `validators` do
235
- * `morph-spec verify`).
236
- *
237
- * @param {string} projectPath
238
- * @param {string} featureName
239
- * @param {object} [options]
240
- * @param {string[]} [options.changedFiles] - override da coleta (testes)
241
- * @param {object} [options._graph] - grafo já carregado (testes)
242
- * @returns {Promise<{errors: number, warnings: number, issues: object[]}>}
243
- */
244
156
  export async function validateReuse(projectPath, featureName = null, options = {}) {
245
157
  const result = { errors: 0, warnings: 0, issues: [] };
246
158
 
@@ -250,11 +162,9 @@ export async function validateReuse(projectPath, featureName = null, options = {
250
162
  })();
251
163
 
252
164
  if (!graph) {
253
- // ADR-002: projeto sem grafo (sem Python, ou grafo ainda não construído)
254
- // segue em frente. Um validador que reprovasse aqui transformaria uma
255
- // dependência opcional em pré-requisito.
165
+ // Sem grafo segue em frente: reprovar faria de uma dependência opcional um pré-requisito.
256
166
  result._skipReason = 'sem grafo de codebase — `morph-spec graph build` habilita esta verificação';
257
- // "Could not check", unlike "nothing to check": verify reports it aloud.
167
+ // "Não pude checar", diferente de "nada a checar": o verify relata em voz alta.
258
168
  result._skipUnavailable = true;
259
169
  return result;
260
170
  }
@@ -1,74 +1,32 @@
1
- /**
2
- * Extração determinística de declarações de tipo a partir do TEXTO do arquivo.
3
- *
4
- * Por que não ler as declarações do grafo: o grafo só é reconstruído depois de
5
- * um commit (o refresh em background de framework/hooks/shared/graph-refresh.js),
6
- * então o código que acabou de ser escrito — o caso que este validador existe
7
- * para pegar — ainda NÃO está nele. O grafo responde "o que já existia"; o
8
- * disco responde "o que esta feature acabou de declarar". Inverter os dois
9
- * papéis produziria um validador cego justamente na janela em que ele precisa
10
- * morder — ver graph-loader.js `getFreshness`.
11
- *
12
- * A varredura é por linha, com regex ancorada no início da declaração: uma
13
- * declaração citada dentro de uma string ou de um comentário de bloco pode
14
- * passar. O custo de errar é um aviso a mais, nunca um bloqueio silencioso.
15
- *
16
- * @module reuse-symbol-scanner
17
- */
1
+ // Lê declarações do disco, não do grafo: o grafo só é refeito depois de um commit,
2
+ // então o código recém-escrito (o que este validador existe para pegar) ainda não está nele.
18
3
 
19
4
  import { isTestPath, languageOf } from '../../graph/sanitize.js';
20
5
  import { isProjectSource } from '../../../../framework/hooks/shared/source-files.js';
21
6
 
22
- /**
23
- * Nomes que a convenção usa como tipo LOCAL de arquivo, não como conceito do
24
- * domínio. Todo componente React declara o seu `Props`; dois arquivos com
25
- * `interface Props` não são o mesmo conceito escrito duas vezes — são dois
26
- * conceitos diferentes com o nome que a convenção manda usar.
27
- *
28
- * A lista é curta de propósito e só admite nome genérico de UMA palavra: assim
29
- * que há qualificador de domínio (`BuildingFiltersProps`, `FilterOption`) o
30
- * nome volta a ser um conceito e a regra vale.
31
- */
7
+ // Tipo LOCAL de arquivo por convenção (todo componente tem seu `Props`), não conceito do
8
+ // domínio. Só nome genérico de UMA palavra: com qualificador (`FilterOption`) a regra vale.
32
9
  const IDIOMATIC_LOCAL_NAMES = new Set([
33
10
  'props', 'state', 'row', 'item', 'column', 'context', 'params', 'options',
34
11
  ]);
35
12
 
36
- /** Declaração de tipo em C#. `partial` é capturado para ser excluído depois. */
37
13
  const CSHARP_DECL =
38
14
  /^\s*(?:\[[^\]]*\]\s*)*(?:(?:public|internal|private|protected|sealed|abstract|static|readonly|new|file|partial)\s+)*(class|record|interface|enum|struct)\s+([A-Za-z_]\w*)/;
39
15
 
40
- /** Declaração de tipo em TS/JS. */
41
16
  const WEB_DECL =
42
17
  /^\s*(?:export\s+(?:default\s+)?)?(?:declare\s+)?(?:abstract\s+)?(interface|type|class|enum)\s+([A-Za-z_$][\w$]*)/;
43
18
 
44
- /** Linha que é claramente comentário — não vale a pena parsear. */
45
19
  const COMMENT_LINE = /^\s*(?:\/\/|\/\*|\*|<!--)/;
46
20
 
47
- /**
48
- * @param {string} sourceFile
49
- * @returns {boolean} arquivo de teste, pelo mesmo predicado do grafo
50
- */
51
21
  export function isTestFile(sourceFile) {
52
22
  return isTestPath(String(sourceFile || '').replace(/\\/g, '/'));
53
23
  }
54
24
 
55
- /**
56
- * @param {string} sourceFile
57
- * @returns {boolean} o arquivo entra na varredura de reuso
58
- */
59
25
  export function isScannableFile(sourceFile) {
60
26
  return isProjectSource(sourceFile) && !isTestFile(sourceFile);
61
27
  }
62
28
 
63
- /**
64
- * "Feature" de um path, com a mesma heurística do baseline
65
- * (docs/specs/code-reuse-gap/measure-duplication.mjs): o segmento após
66
- * `features/`/`Features/` quando existe, senão a pasta do arquivo. É o que
67
- * separa o achado indefensável (mesma feature) do aviso tolerado por VSA.
68
- *
69
- * @param {string} sourceFile
70
- * @returns {string}
71
- */
29
+ /** Segmento após `features/`/`Features/`, senão a pasta do arquivo. */
72
30
  export function featureOf(sourceFile) {
73
31
  const f = String(sourceFile || '').replace(/\\/g, '/');
74
32
  const m = /(?:^|\/)(?:features|Features)\/([^/]+)/.exec(f);
@@ -76,25 +34,11 @@ export function featureOf(sourceFile) {
76
34
  return f.split('/').slice(0, -1).join('/').toLowerCase();
77
35
  }
78
36
 
79
- /**
80
- * @param {string} name
81
- * @returns {boolean} nome genérico que a convenção usa como tipo local
82
- */
83
37
  export function isIdiomaticLocalName(name) {
84
38
  return IDIOMATIC_LOCAL_NAMES.has(String(name).toLowerCase());
85
39
  }
86
40
 
87
- /**
88
- * Declarações de tipo de um arquivo.
89
- *
90
- * `partial` (C#) é reconhecido e DESCARTADO: declarar a mesma classe em dois
91
- * arquivos é o propósito da palavra-chave, e sinalizá-la seria um falso
92
- * positivo garantido em qualquer projeto .NET com código gerado.
93
- *
94
- * @param {string} content - conteúdo do arquivo
95
- * @param {string} sourceFile - path, usado só para decidir a linguagem
96
- * @returns {Array<{name: string, kind: string, line: number}>}
97
- */
41
+ // `partial` (C#) é descartado: declarar a mesma classe em dois arquivos é o propósito da palavra-chave.
98
42
  export function scanDeclarations(content, sourceFile) {
99
43
  const language = languageOf(sourceFile);
100
44
  const re = language === 'dotnet' ? CSHARP_DECL : WEB_DECL;