@polymorphism-tech/morph-spec 8.46.0 → 9.0.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 (304) hide show
  1. package/CLAUDE.md +53 -44
  2. package/README.md +3 -4
  3. package/bin/morph-spec.js +38 -55
  4. package/bin/validate.js +5 -134
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  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 +75 -18
  11. package/framework/commands/create-umbraco-lp.md +2 -0
  12. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  13. package/framework/commands/morph-apply.md +5 -28
  14. package/framework/commands/morph-archive.md +2 -2
  15. package/framework/commands/morph-hotfix.md +8 -8
  16. package/framework/commands/morph-infra.md +17 -203
  17. package/framework/commands/morph-media.md +3 -0
  18. package/framework/commands/morph-preflight.md +7 -31
  19. package/framework/commands/morph-proposal.md +2 -50
  20. package/framework/commands/morph-status.md +3 -3
  21. package/framework/commands/morph-troubleshoot.md +2 -14
  22. package/framework/docs/CLI.md +585 -286
  23. package/framework/evals/review-checklist.md +82 -41
  24. package/framework/hooks/README.md +10 -11
  25. package/framework/hooks/claude-code/core/comment-guard.js +1 -1
  26. package/framework/hooks/claude-code/core/denylist.json +33 -3
  27. package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
  28. package/framework/hooks/claude-code/core/gate-guard.js +2 -11
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
  30. package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
  31. package/framework/hooks/claude-code/core/state-sync.js +17 -19
  32. package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
  33. package/framework/hooks/claude-code/core/validate-completion.js +33 -97
  34. package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
  35. package/framework/hooks/claude-code/core/worktree-context.js +5 -4
  36. package/framework/hooks/claude-code/statusline.py +55 -41
  37. package/framework/hooks/package.json +3 -0
  38. package/framework/hooks/shared/activity-logger.js +1 -1
  39. package/framework/hooks/shared/build-check.js +10 -6
  40. package/framework/hooks/shared/command-tokens.js +169 -0
  41. package/framework/hooks/shared/comment-rules.js +1 -1
  42. package/framework/hooks/shared/comment-scanner.js +10 -23
  43. package/framework/hooks/shared/comment-stripper.js +1 -1
  44. package/framework/hooks/shared/e2e-check.js +67 -31
  45. package/framework/hooks/shared/errata.js +72 -0
  46. package/framework/hooks/shared/feature-locator.js +1 -1
  47. package/framework/hooks/shared/frontend-locations.js +6 -0
  48. package/framework/hooks/shared/gate-question.js +18 -0
  49. package/framework/hooks/shared/phase-utils.js +15 -32
  50. package/framework/hooks/shared/slice-progress.js +118 -0
  51. package/framework/hooks/shared/spawn-command.js +23 -0
  52. package/framework/hooks/shared/state-reader.js +61 -87
  53. package/framework/hooks/shared/state-writer.js +3 -3
  54. package/framework/mcp/registry.json +3 -4
  55. package/framework/phases.json +23 -39
  56. package/framework/rules/ai-agents-standards.md +18 -16
  57. package/framework/rules/asaas-standards.md +3 -1
  58. package/framework/rules/csharp-standards.md +10 -8
  59. package/framework/rules/data-standards.md +5 -5
  60. package/framework/rules/dotnet-templates.md +1 -1
  61. package/framework/rules/frontend-standards.md +4 -2
  62. package/framework/rules/gsap-standards.md +10 -5
  63. package/framework/rules/hangfire-standards.md +3 -1
  64. package/framework/rules/infrastructure-standards.md +4 -2
  65. package/framework/rules/neon-auth-standards.md +3 -1
  66. package/framework/rules/neon-standards.md +6 -4
  67. package/framework/rules/nextjs-standards.md +13 -11
  68. package/framework/rules/nextjs-templates.md +1 -1
  69. package/framework/rules/resend-standards.md +3 -1
  70. package/framework/schemas/config.schema.json +14 -9
  71. package/framework/schemas/feature.schema.json +15 -52
  72. package/framework/skills/README.md +4 -5
  73. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  74. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  75. package/framework/skills/core/morph-apply/SKILL.md +150 -223
  76. package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
  77. package/framework/skills/core/morph-machine/SKILL.md +4 -4
  78. package/framework/skills/core/morph-media/SKILL.md +3 -3
  79. package/framework/skills/core/morph-plan/SKILL.md +138 -300
  80. package/framework/skills/core/morph-review/SKILL.md +112 -137
  81. package/framework/skills/core/morph-standards/SKILL.md +29 -41
  82. package/framework/skills/core/morph-uiux/SKILL.md +78 -153
  83. package/framework/standards/STANDARDS.json +90 -1219
  84. package/framework/standards/STANDARDS.meta.json +1305 -0
  85. package/framework/standards/ai-agents/evals-with-cache.md +2 -3
  86. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  87. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  88. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  89. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  90. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  91. package/framework/standards/ai-agents/setup.md +2 -2
  92. package/framework/standards/ai-agents/structured-output.md +1 -1
  93. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  94. package/framework/standards/architecture/code-comments.md +8 -8
  95. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  96. package/framework/standards/backend/api/validation.md +135 -47
  97. package/framework/standards/backend/database/postgresql/database.md +11 -7
  98. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  99. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  100. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  101. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  102. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  103. package/framework/standards/backend/dotnet/testing.md +1 -1
  104. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  105. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  106. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  107. package/framework/standards/data/nosql/cache/redis.md +120 -44
  108. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  109. package/framework/standards/frontend/design-system/animations.md +1 -1
  110. package/framework/standards/frontend/design-system/naming.md +1 -1
  111. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  112. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  113. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  114. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  115. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  116. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  117. package/framework/templates/README.md +2 -3
  118. package/framework/templates/REGISTRY.json +6 -170
  119. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  120. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  121. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  122. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  123. package/framework/templates/docs/plan.md +72 -0
  124. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  127. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  128. package/framework/templates/infrastructure/github/README.md +2 -1
  129. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  130. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  131. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  132. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  133. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  134. package/package.json +1 -1
  135. package/src/commands/e2e/e2e.js +2 -2
  136. package/src/commands/observability/cost.js +18 -60
  137. package/src/commands/observability/telemetry.js +3 -5
  138. package/src/commands/project/doctor-procs.js +1 -1
  139. package/src/commands/project/doctor.js +9 -9
  140. package/src/commands/project/fleet.js +4 -4
  141. package/src/commands/project/heavy.js +3 -3
  142. package/src/commands/project/init.js +6 -1
  143. package/src/commands/project/setup-infra-cmd.js +2 -1
  144. package/src/commands/project/stack-scan.js +58 -0
  145. package/src/commands/project/standards.js +79 -25
  146. package/src/commands/project/status.js +29 -69
  147. package/src/commands/project/update.js +33 -19
  148. package/src/commands/project/worktree.js +1 -1
  149. package/src/commands/state/advance.js +62 -46
  150. package/src/commands/state/approve.js +95 -138
  151. package/src/commands/state/archive.js +20 -84
  152. package/src/commands/state/auto-approval.js +68 -0
  153. package/src/commands/state/create.js +28 -1
  154. package/src/commands/state/delete.js +1 -1
  155. package/src/commands/state/finish.js +11 -23
  156. package/src/commands/state/gate-check.js +86 -95
  157. package/src/commands/state/gate-decision.js +6 -4
  158. package/src/commands/state/handoff.js +1 -1
  159. package/src/commands/state/list.js +9 -9
  160. package/src/commands/state/report-check.js +157 -311
  161. package/src/commands/state/retype.js +9 -3
  162. package/src/commands/validation/mutate.js +77 -14
  163. package/src/commands/validation/plan-check.js +81 -0
  164. package/src/commands/validation/validate-feature.js +60 -135
  165. package/src/commands/validation/verify.js +41 -82
  166. package/src/core/paths/output-schema.js +20 -67
  167. package/src/core/state/gate-order.js +5 -6
  168. package/src/core/state/lifecycle-audit.js +2 -2
  169. package/src/core/state/state-manager.js +17 -98
  170. package/src/core/templates/template-registry.js +2 -2
  171. package/src/core/templates/template-seed.js +22 -0
  172. package/src/core/work-types.js +10 -10
  173. package/src/dashboard/app/App.jsx +0 -2
  174. package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
  175. package/src/dashboard/app/components/CommandPalette.jsx +7 -8
  176. package/src/dashboard/app/components/EntityLink.jsx +1 -2
  177. package/src/dashboard/app/components/Markdown.jsx +1 -1
  178. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  179. package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
  180. package/src/dashboard/app/lib/phases.js +2 -4
  181. package/src/dashboard/app/pages/Activity.jsx +1 -1
  182. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
  183. package/src/dashboard/app/pages/Features.jsx +9 -9
  184. package/src/dashboard/app/pages/Guide.jsx +2 -2
  185. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
  186. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  187. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  188. package/src/dashboard/app/pages/Standards.jsx +1 -1
  189. package/src/dashboard/scanner/activity-scanner.js +6 -16
  190. package/src/dashboard/scanner/agents-scanner.js +1 -1
  191. package/src/dashboard/scanner/docs-scanner.js +5 -23
  192. package/src/dashboard/scanner/features-scanner.js +95 -60
  193. package/src/dashboard/scanner/index.js +0 -2
  194. package/src/dashboard/scanner/project-api.js +1 -1
  195. package/src/dashboard/scanner/rules-scanner.js +15 -10
  196. package/src/dashboard/scanner/skills-scanner.js +2 -3
  197. package/src/dashboard/scanner/standards-scanner.js +2 -1
  198. package/src/lib/ci/ci-verify.js +17 -1
  199. package/src/lib/ci/github-ci.js +1 -1
  200. package/src/lib/detectors/index.js +3 -8
  201. package/src/lib/generators/handoff-generator.js +34 -65
  202. package/src/lib/generators/settings-generator.js +11 -89
  203. package/src/lib/git/evidence-staleness.js +1 -1
  204. package/src/lib/git/tree-fingerprint.js +19 -7
  205. package/src/lib/git/tree-state.js +2 -2
  206. package/src/lib/heavy/cleanup.js +1 -5
  207. package/src/lib/installers/memory-installer.js +6 -8
  208. package/src/lib/mutation/commands.js +3 -2
  209. package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
  210. package/src/lib/mutation/plan.js +14 -6
  211. package/src/lib/mutation/runner.js +1 -1
  212. package/src/lib/mutation/subset.js +46 -9
  213. package/src/lib/plan/artifact-vocabulary.js +60 -0
  214. package/src/lib/plan/checks.js +234 -0
  215. package/src/lib/plan/plan-parser.js +209 -0
  216. package/src/lib/plan/plan-template.js +18 -0
  217. package/src/lib/review-reports.js +222 -0
  218. package/src/lib/risk-detector.js +134 -135
  219. package/src/lib/specs/adr-index.js +50 -69
  220. package/src/lib/specs/design-seed.js +18 -0
  221. package/src/lib/specs/frontmatter.js +44 -0
  222. package/src/lib/specs/gfm-table.js +17 -0
  223. package/src/lib/specs/old-format.js +68 -0
  224. package/src/lib/specs/revocations.js +37 -95
  225. package/src/lib/specs/screen-checklist.js +18 -17
  226. package/src/lib/stack/dotnet-build-ignores.js +47 -0
  227. package/src/lib/stack/stack-profile.js +1 -1
  228. package/src/lib/stack/stack-scan.js +267 -0
  229. package/src/lib/standards/registry.js +43 -0
  230. package/src/lib/standards/sections.js +58 -0
  231. package/src/lib/tasks/eval-node.js +4 -12
  232. package/src/lib/tasks/test-nodes.js +1 -74
  233. package/src/lib/tasks/test-runner.js +75 -117
  234. package/src/lib/telemetry/metrics.js +8 -69
  235. package/src/lib/telemetry/telemetry-reader.js +5 -118
  236. package/src/lib/telemetry/verify-event.js +5 -7
  237. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  238. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
  239. package/src/lib/validators/reuse/reuse-validator.js +24 -48
  240. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  241. package/src/lib/validators/traceability-check.js +85 -129
  242. package/src/lib/validators/validation-runner.js +8 -88
  243. package/src/lib/validators/verify-runner.js +134 -301
  244. package/src/lib/worktree/docker-audit.js +1 -2
  245. package/src/lib/worktree/fleet-scanner.js +25 -38
  246. package/src/scripts/setup-infra.js +42 -22
  247. package/src/utils/agents-installer.js +92 -13
  248. package/src/utils/claude-settings-manager.js +0 -35
  249. package/src/utils/file-copier.js +34 -6
  250. package/src/utils/hooks-installer.js +16 -9
  251. package/src/utils/skills-installer.js +1 -2
  252. package/framework/commands/commit.md +0 -166
  253. package/framework/evals/trace-schema.md +0 -67
  254. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  255. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  256. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  257. package/framework/hooks/shared/tasks-validator.js +0 -111
  258. package/framework/rubrics/backend.json +0 -41
  259. package/framework/rubrics/default.json +0 -36
  260. package/framework/rubrics/frontend.json +0 -36
  261. package/framework/rules/morph-task-tracking.md +0 -54
  262. package/framework/rules/morph-workflow.md +0 -244
  263. package/framework/schemas/tasks.schema.json +0 -79
  264. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  265. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  266. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  267. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  268. package/framework/templates/docs/proposal.md +0 -192
  269. package/framework/templates/docs/spec.md +0 -57
  270. package/framework/templates/docs/ui-components.md +0 -52
  271. package/framework/templates/docs/ui-design-system.md +0 -76
  272. package/framework/templates/docs/ui-flows.md +0 -167
  273. package/framework/templates/examples/spec-examples.md +0 -91
  274. package/framework/templates/feature/decisions.md +0 -124
  275. package/framework/templates/feature/recap.md +0 -145
  276. package/framework/templates/project/validate.js +0 -97
  277. package/framework/templates/tasks.template.json +0 -17
  278. package/src/commands/state/usecase.js +0 -143
  279. package/src/commands/tasks/dag.js +0 -181
  280. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  281. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  282. package/src/lib/agents/output-collector.js +0 -76
  283. package/src/lib/evaluator-report.js +0 -91
  284. package/src/lib/specs/acervo-format.js +0 -70
  285. package/src/lib/specs/acervo-lifecycle.js +0 -264
  286. package/src/lib/specs/done-criteria-check.js +0 -200
  287. package/src/lib/specs/usecase-check.js +0 -301
  288. package/src/lib/specs/usecase-promoter.js +0 -301
  289. package/src/lib/specs/usecase-registry.js +0 -237
  290. package/src/lib/tasks/dispatch-planner.js +0 -319
  291. package/src/lib/tasks/mermaid-renderer.js +0 -132
  292. package/src/lib/tasks/task-classifier.js +0 -30
  293. package/src/lib/tasks/task-parser.js +0 -209
  294. package/src/lib/tasks/tasks-json-parser.js +0 -296
  295. package/src/lib/tasks/test-scope.js +0 -597
  296. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  297. package/src/lib/validators/architecture/index.js +0 -7
  298. package/src/lib/validators/css/css-validator.js +0 -340
  299. package/src/lib/validators/css/index.js +0 -7
  300. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  301. package/src/lib/validators/design-system/index.js +0 -7
  302. package/src/lib/validators/score-aggregator.js +0 -244
  303. package/src/lib/validators/ui/index.js +0 -7
  304. package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
@@ -9,13 +9,13 @@ export async function validateContracts(projectPath, featureName, options = {})
9
9
 
10
10
  const skipped = (message) => ({ status: 'ok', errors: 0, warnings: 0, issues: [], coverage: null, message });
11
11
 
12
- if (!existsSync(contractsPath)) return skipped('No spec.md found — skipping');
12
+ if (!existsSync(contractsPath)) return skipped('No plan.md found — skipping');
13
13
 
14
14
  const contractsContent = readFileSync(contractsPath, 'utf-8');
15
15
  const contracts = parseContracts(contractsContent);
16
16
 
17
17
  if (contracts.interfaces.length === 0 && contracts.records.length === 0 && contracts.enums.length === 0) {
18
- return skipped('spec.md declares no interfaces/records/enums — skipping');
18
+ return skipped('plan.md declares no interfaces/records/enums — skipping');
19
19
  }
20
20
 
21
21
  // Find all .cs files in the project (excluding .morph outputs, bin, obj)
@@ -1,15 +1,14 @@
1
1
  /**
2
2
  * Reuse Validator — símbolo novo cujo nome já existe no codebase.
3
3
  *
4
- * A terceira camada do ciclo de reuso, no molde do `design-system-validator`:
4
+ * A terceira camada do ciclo de reuso:
5
5
  *
6
- * PROSA (morph-plan, personas) ──► ARTEFATO (`reuse` em tasks.json)
6
+ * PROSA (morph-plan, personas) ──► ARTEFATO (seção de reuso do plano)
7
7
  * ──► COBRANÇA (este arquivo)
8
8
  *
9
- * Prosa sozinha não segura — `morph-brainstorming/SKILL.md:39` já manda olhar
10
- * o que existe, e o ProspectPRO redefiniu `SimpleBuilding` em cinco arquivos da
11
- * MESMA pasta assim mesmo. O único ciclo que funciona no framework (design
12
- * tokens) tem verificação em código; este é o equivalente para código.
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.
13
12
  *
14
13
  * Regras, na ordem em que reduzem ruído:
15
14
  *
@@ -20,9 +19,9 @@
20
19
  * não se resolve por aviso, e fica FORA daqui de propósito.
21
20
  * 3. Mesma feature = achado (`error`); feature diferente = aviso
22
21
  * (`warning`), porque VSA prescreve DTO homônimo entre slices.
23
- * 4. Veredito `CRIAR` declarado no plano (tasks.json `reuse`) rebaixa o
24
- * achado a aviso: a decisão foi tomada no Gate 2, com justificativa em
25
- * `why`. O validador cobra a decisão AUSENTE, não a decisão tomada.
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.
26
25
  *
27
26
  * Sem grafo o validador não roda (ADR-002: ausência de grafo nunca bloqueia).
28
27
  *
@@ -35,13 +34,13 @@ import { join, isAbsolute } from 'path';
35
34
 
36
35
  import { loadGraph } from '../../graph/graph-loader.js';
37
36
  import { languageOf } from '../../graph/sanitize.js';
38
- import { getAbsoluteOutputPath } from '../../../core/paths/output-schema.js';
39
37
  import {
40
38
  featureOf,
41
39
  isIdiomaticLocalName,
42
40
  isScannableFile,
43
41
  scanDeclarations,
44
42
  } from './symbol-scanner.js';
43
+ import { readPlanChecks } from '../../plan/checks.js';
45
44
 
46
45
  /** Quantos locais citar por achado antes de resumir com "(+N)". */
47
46
  const MAX_CITED = 4;
@@ -155,7 +154,7 @@ export function findDuplicateSymbols(declarations, existing, opts = {}) {
155
154
  ? `Reuse ou estenda o tipo existente em vez de redeclarar. Confirme com ` +
156
155
  `\`morph-spec graph explain ${decls[0].name}\` (sem grafo: ` +
157
156
  `\`grep -rn "${decls[0].name}"\`) e, se a criação for mesmo necessária, ` +
158
- 'registre o veredito CRIAR com a justificativa em "why" no tasks[].reuse da task.'
157
+ 'registre o veredito CRIAR com a justificativa em "why" na tabela ## Reuso do plan.md.'
159
158
  : `VSA tolera DTO homônimo entre slices — confirme que são conceitos ` +
160
159
  `diferentes e não a mesma coisa escrita duas vezes.`,
161
160
  });
@@ -168,9 +167,7 @@ export function findDuplicateSymbols(declarations, existing, opts = {}) {
168
167
  }
169
168
 
170
169
  /**
171
- * Vereditos de reuso carimbados no plano: `tasks[].reuse[]`.
172
- *
173
- * O veredito mora só no `tasks.json`: dado estruturado, lido aqui e no Gate 2.
170
+ * Vereditos de reuso do plano: a tabela `## Reuso` que o plan-check extraiu.
174
171
  *
175
172
  * @param {string} projectPath
176
173
  * @param {string} featureName
@@ -179,24 +176,13 @@ export function findDuplicateSymbols(declarations, existing, opts = {}) {
179
176
  export function readReuseVerdicts(projectPath, featureName) {
180
177
  const verdicts = new Map();
181
178
  if (!featureName) return verdicts;
182
-
183
- try {
184
- const tasksPath = getAbsoluteOutputPath(projectPath, featureName, 'tasks');
185
- if (!existsSync(tasksPath)) return verdicts;
186
- const doc = JSON.parse(readFileSync(tasksPath, 'utf8').replace(/^/, ''));
187
- for (const task of doc.tasks ?? []) {
188
- for (const entry of task.reuse ?? []) {
189
- if (!entry?.concept) continue;
190
- verdicts.set(String(entry.concept).toLowerCase(), {
191
- verdict: String(entry.verdict || '').toUpperCase(),
192
- target: entry.target,
193
- });
194
- }
195
- }
196
- } catch {
197
- // Plano ilegível não é problema deste validador — o tasks-json-guard já
198
- // cobra o schema, e aqui a ausência de veredito só torna a cobrança mais
199
- // rígida, nunca mais frouxa.
179
+ // Sem extração, nenhum veredito: a cobrança só fica mais rígida, nunca mais frouxa.
180
+ for (const entry of readPlanChecks(projectPath, featureName).checks?.reuse ?? []) {
181
+ if (!entry?.concept) continue;
182
+ verdicts.set(String(entry.concept).toLowerCase(), {
183
+ verdict: String(entry.verdict || '').toUpperCase(),
184
+ target: entry.target,
185
+ });
200
186
  }
201
187
  return verdicts;
202
188
  }
@@ -205,9 +191,8 @@ export function readReuseVerdicts(projectPath, featureName) {
205
191
  * Arquivos que esta feature criou ou alterou.
206
192
  *
207
193
  * Duas fontes, unidas, porque cada uma é cega no momento em que a outra
208
- * enxerga: `outputs` do `tasks.json` é o contrato do framework (morph-implement
209
- * §8 exige listar TODO arquivo produzido) e sobrevive ao commit; `git status`
210
- * pega o que ainda não foi registrado numa task. Sem git, sem drama.
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.
211
196
  *
212
197
  * @param {string} projectPath
213
198
  * @param {string} featureName
@@ -215,19 +200,10 @@ export function readReuseVerdicts(projectPath, featureName) {
215
200
  */
216
201
  export function collectChangedFiles(projectPath, featureName) {
217
202
  const files = new Set();
218
-
219
- try {
220
- const tasksPath = getAbsoluteOutputPath(projectPath, featureName, 'tasks');
221
- if (existsSync(tasksPath)) {
222
- const doc = JSON.parse(readFileSync(tasksPath, 'utf8').replace(/^/, ''));
223
- for (const task of doc.tasks ?? []) {
224
- for (const out of task.outputs ?? []) {
225
- if (typeof out === 'string') files.add(out.replace(/\\/g, '/'));
226
- }
227
- }
203
+ for (const slice of readPlanChecks(projectPath, featureName).checks?.slices ?? []) {
204
+ for (const file of slice.files ?? []) {
205
+ if (typeof file === 'string') files.add(file.replace(/\\/g, '/'));
228
206
  }
229
- } catch {
230
- // tasks.json ausente/ilegível: sobra o git.
231
207
  }
232
208
 
233
209
  try {
@@ -248,7 +224,7 @@ export function collectChangedFiles(projectPath, featureName) {
248
224
  if (path) files.add(path.replace(/\\/g, '/'));
249
225
  }
250
226
  } catch {
251
- // Fora de um repo git, ou git indisponível: o tasks.json já respondeu.
227
+ // Fora de um repo git, ou git indisponível: o plano já respondeu.
252
228
  }
253
229
 
254
230
  return [...files];
@@ -9,8 +9,7 @@
9
9
  * papéis produziria um validador cego justamente na janela em que ele precisa
10
10
  * morder — ver graph-loader.js `getFreshness`.
11
11
  *
12
- * A varredura é por linha, com regex ancorada no início da declaração. Mesma
13
- * limitação (e mesmo grau de tolerância) do design-system-validator: uma
12
+ * A varredura é por linha, com regex ancorada no início da declaração: uma
14
13
  * declaração citada dentro de uma string ou de um comentário de bloco pode
15
14
  * passar. O custo de errar é um aviso a mais, nunca um bloqueio silencioso.
16
15
  *
@@ -28,8 +27,7 @@ import { isProjectSource } from '../../../../framework/hooks/shared/source-files
28
27
  *
29
28
  * A lista é curta de propósito e só admite nome genérico de UMA palavra: assim
30
29
  * que há qualificador de domínio (`BuildingFiltersProps`, `FilterOption`) o
31
- * nome volta a ser um conceito e a regra vale. Mesmo papel da allowlist de
32
- * cores de sistema no design-system-validator.
30
+ * nome volta a ser um conceito e a regra vale.
33
31
  */
34
32
  const IDIOMATIC_LOCAL_NAMES = new Set([
35
33
  'props', 'state', 'row', 'item', 'column', 'context', 'params', 'options',
@@ -1,89 +1,69 @@
1
1
  /**
2
2
  * traceability-check.js — advisory, feature-scope-only check: do the files
3
- * cited in recap.md's "**Files:**" bullet lists have an owning task in
4
- * tasks.json's `outputs`?
3
+ * cited under `**Arquivos:**` in the slice reports (`3-implement/reports/F{n}.md`)
4
+ * belong to some slice of the plan (its `arquivos:`, as extracted by plan-check)?
5
5
  *
6
- * This is the DETERMINISTIC half of a two-layer fix. The NLH half lives in
7
- * `framework/skills/core/morph-implement/SKILL.md` §7 ("outputs DEVE listar
8
- * TODO arquivo criado ou modificado pela task") and targets the root cause: a
9
- * file shipped without EVER being declared anywhere (not in outputs, not in
10
- * recap.md — the real-world case that motivated this module, see the finding
11
- * below). This module cannot catch THAT class of drift — a file absent from
12
- * both documents is invisible to a check that only compares the two documents
13
- * against each other. What it DOES catch is a narrower, complementary class:
14
- * recap.md and tasks.json DISAGREEING — an author cited a file in the recap's
15
- * prose but forgot (or a later task re-touched it without) declaring it in any
16
- * task's `outputs`. That disagreement is real signal even though it is not the
17
- * same defect the SKILL.md rule targets.
6
+ * It catches the reports and the plan DISAGREEING: a file the implementation
7
+ * says it touched that no slice declared. A file absent from both documents
8
+ * is invisible to it by construction — that drift is the reuse/contract
9
+ * reviewer's job.
18
10
  *
19
- * Motivating evidence: run 2026-08-12T133732Z-vet-visit-followup-reminder —
20
- * `ApplicationDbContextFactory.cs` shipped in production with no owning task
21
- * in `outputs` AND no mention in recap.md (out of this module's reach by
22
- * construction). Separately, `src/VetClinic.Api/appsettings.json` and
23
- * `Properties/launchSettings.json` WERE cited in T1's recap `**Files:**` list
24
- * but never appear in any task's `outputs` — exactly the disagreement this
25
- * module flags.
11
+ * Advisory, never blocking: `status` is only ever 'pass' | 'warn' | 'skip'.
12
+ * `runVerify()` keeps this result OUTSIDE `nodes` so it can never flip the
13
+ * overall build/tests/validators/e2e status.
26
14
  *
27
- * Advisory, never blocking: the returned `status` is only ever 'pass' | 'warn'
28
- * | 'skip' — never 'fail'. `runVerify()` keeps this result OUTSIDE `nodes` on
29
- * purpose so it can never flip the overall build/tests/validators/e2e status
30
- * computed from `nodes` alone (see verify-runner.js).
31
- *
32
- * Fail-open throughout: no recap.md, no tasks.json, or a parse error all
33
- * degrade to `{ status: 'skip' }` rather than throwing or warning about the
34
- * absence itself — this is rastreabilidade tooling, not a completeness gate.
15
+ * Fail-open throughout: no slice report, no extracted plan or a parse error
16
+ * all degrade to `{ status: 'skip' }`.
35
17
  *
36
18
  * @module traceability-check
37
19
  */
38
20
 
39
21
  import { existsSync, readFileSync } from 'fs';
40
- import { getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
22
+ import { join } from 'path';
23
+ import { readPlanChecks } from '../plan/checks.js';
24
+ import { sliceProgress, sliceReportPath } from '../../../framework/hooks/shared/slice-progress.js';
41
25
 
42
- /** Matches a `**Files:**` marker followed by one or more `- ` bullet lines. */
43
- const FILES_BLOCK_RE = /\*\*Files:\*\*[ \t]*\r?\n((?:^[ \t]*-[ \t].*\r?\n?)+)/gm;
26
+ /**
27
+ * An `**Arquivos:**` marker and what follows it: the rest of its own line
28
+ * (inline list) plus any `- ` bullet lines right below.
29
+ */
30
+ const FILES_BLOCK_RE = /\*\*Arquivos:\*\*([^\r\n]*)\r?\n?((?:^[ \t]*-[ \t].*\r?\n?)*)/gm;
44
31
 
45
- /** Backtick-quoted spans within a bullet line. */
32
+ /** Backtick-quoted spans within a line. */
46
33
  const BACKTICK_RE = /`([^`]+)`/g;
47
34
 
48
35
  /**
49
- * Extract the set of file paths cited under every "**Files:**" block in a
50
- * recap.md's text, applying a conservative decomposition of the natural
51
- * language shorthand real recaps use:
36
+ * Extract the set of file paths cited under every `**Arquivos:**` marker of a
37
+ * slice report, applying a conservative decomposition of the shorthand real
38
+ * reports use:
52
39
  *
53
40
  * - A line with exactly one backtick span: that span is a candidate,
54
- * whether or not it contains a `/` (a legitimate top-level file, e.g.
55
- * `` `VetClinic.sln` ``, has no slash).
56
- * - A line with multiple comma-separated backtick spans (e.g.
57
- * `` `a/b.json`, `c.json`, `d/e.json` ``): only the FIRST span and any
41
+ * whether or not it contains a `/`.
42
+ * - A line with several comma-separated spans: only the FIRST span and any
58
43
  * LATER span that itself contains `/` are candidates. A later span with
59
- * no `/` is ambiguous shorthand (a bare filename implicitly relative to
60
- * the first span's directory, or a suffix like `` `.Designer.cs` ``) and
61
- * is skipped rather than guessed — false negatives are the acceptable
62
- * failure mode for advisory tooling, false positives are not.
63
- * - Any span containing `{`/`}` (brace-expansion shorthand, e.g.
64
- * `` `Foo/{A,B,C}.cs` ``) is skipped entirely — decomposing it correctly
65
- * would require guessing at delimiters this format doesn't standardize.
44
+ * no `/` is ambiguous shorthand and is skipped rather than guessed —
45
+ * false negatives are acceptable for advisory tooling, false positives
46
+ * are not.
47
+ * - Any span containing `{`/`}` (brace expansion) is skipped.
66
48
  * - Any span ending in `/` is a directory reference, not a file — skipped.
67
49
  *
68
- * @param {string} recapText
50
+ * @param {string} reportText
69
51
  * @returns {string[]} de-duplicated, normalized (forward-slash) candidate paths
70
52
  */
71
- export function extractCitedFiles(recapText) {
53
+ export function extractCitedFiles(reportText) {
72
54
  const cited = new Set();
73
- if (!recapText) return [];
55
+ if (!reportText) return [];
74
56
 
75
57
  FILES_BLOCK_RE.lastIndex = 0;
76
58
  let blockMatch;
77
- while ((blockMatch = FILES_BLOCK_RE.exec(recapText)) !== null) {
78
- const block = blockMatch[1];
79
- const lines = block.split(/\r?\n/).filter((l) => /^[ \t]*-[ \t]/.test(l));
80
-
81
- for (const line of lines) {
59
+ while ((blockMatch = FILES_BLOCK_RE.exec(reportText)) !== null) {
60
+ const bullets = blockMatch[2].split(/\r?\n/).filter((l) => /^[ \t]*-[ \t]/.test(l));
61
+ for (const line of [blockMatch[1], ...bullets]) {
82
62
  const spans = [...line.matchAll(BACKTICK_RE)].map((m) => m[1].trim()).filter(Boolean);
83
63
  spans.forEach((span, idx) => {
84
- if (idx > 0 && !span.includes('/')) return; // ambiguous shorthand continuation
85
- if (span.includes('{') || span.includes('}')) return; // brace-expansion, too ambiguous to decompose
86
- if (span.endsWith('/')) return; // directory reference, not a file
64
+ if (idx > 0 && !span.includes('/')) return;
65
+ if (span.includes('{') || span.includes('}')) return;
66
+ if (span.endsWith('/')) return;
87
67
  cited.add(normalizePath(span));
88
68
  });
89
69
  }
@@ -92,114 +72,90 @@ export function extractCitedFiles(recapText) {
92
72
  return [...cited];
93
73
  }
94
74
 
95
- /**
96
- * Backslashes to forward slashes, strip a leading `./`.
97
- *
98
- * Exported because `src/lib/tasks/test-scope.js` normalises the SAME thing —
99
- * a task's `outputs` entry — before deciding which tests a task-scoped verify
100
- * runs. One normaliser for `outputs`, so the traceability check and the test
101
- * recorte never disagree about which file a task delivered.
102
- */
75
+ /** Backslashes to forward slashes, strip a leading `./`. */
103
76
  export function normalizePath(p) {
104
77
  return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
105
78
  }
106
79
 
107
80
  /**
108
- * Union of `outputs` across every `done` task in tasks.json, normalized.
81
+ * Union of the `arquivos:` of every slice of the plan, normalized.
109
82
  *
110
- * @param {string} projectPath
111
- * @param {string} featureName
112
- * @returns {string[]|null} null when tasks.json is missing/unreadable/invalid
113
- * (the caller treats null as "cannot judge coverage" and skips, not warns)
83
+ * @returns {string[]|null} null when the plan has no readable extraction
114
84
  */
115
- export function collectDoneOutputs(projectPath, featureName) {
116
- let tasksPath;
117
- try {
118
- tasksPath = getAbsoluteOutputPath(projectPath, featureName, 'tasks');
119
- } catch {
120
- return null;
121
- }
122
- if (!existsSync(tasksPath)) return null;
123
-
124
- let data;
125
- try {
126
- data = JSON.parse(readFileSync(tasksPath, 'utf8'));
127
- } catch {
128
- return null;
129
- }
130
-
131
- const tasks = Array.isArray(data?.tasks) ? data.tasks : [];
132
- const outputs = [];
133
- for (const task of tasks) {
134
- if (task?.status !== 'done') continue;
135
- for (const output of task.outputs || []) {
136
- if (typeof output === 'string' && output.trim()) outputs.push(normalizePath(output));
137
- }
138
- }
139
- return outputs;
85
+ export function collectSliceFiles(projectPath, featureName) {
86
+ const checks = readPlanChecks(projectPath, featureName).checks;
87
+ if (!checks) return null;
88
+ return checks.slices.flatMap((slice) => (slice.files ?? [])
89
+ .filter((f) => typeof f === 'string' && f.trim())
90
+ .map(normalizePath));
140
91
  }
141
92
 
142
93
  /**
143
- * A cited file is covered if some done task's `outputs` names it exactly, or
144
- * names a directory (an entry ending in `/`) that the cited file falls under.
94
+ * A cited file is covered if some slice names it exactly, or names a
95
+ * directory (an entry ending in `/`) that the cited file falls under.
145
96
  *
146
97
  * @param {string} file
147
- * @param {string[]} outputs
98
+ * @param {string[]} declared
148
99
  * @returns {boolean}
149
100
  */
150
- function isCovered(file, outputs) {
151
- for (const output of outputs) {
152
- if (output === file) return true;
153
- if (output.endsWith('/') && file.startsWith(output)) return true;
101
+ function isCovered(file, declared) {
102
+ for (const entry of declared) {
103
+ if (entry === file) return true;
104
+ if (entry.endsWith('/') && file.startsWith(entry)) return true;
154
105
  }
155
106
  return false;
156
107
  }
157
108
 
109
+ function readOrNull(path) {
110
+ try {
111
+ return existsSync(path) ? readFileSync(path, 'utf8') : null;
112
+ } catch {
113
+ return null;
114
+ }
115
+ }
116
+
158
117
  /**
159
- * Run the recap-vs-outputs traceability check for a feature.
118
+ * Run the reports-vs-slices traceability check for a feature.
160
119
  *
161
120
  * @param {string} projectPath
162
121
  * @param {string} featureName
163
122
  * @returns {{ status: 'pass'|'warn'|'skip', warnings: string[], reason: string|null, citedCount: number }}
164
123
  */
165
124
  export function checkOutputsTraceability(projectPath, featureName) {
166
- let recapPath;
167
- try {
168
- recapPath = getAbsoluteOutputPath(projectPath, featureName, 'recap');
169
- } catch {
170
- return { status: 'skip', warnings: [], reason: 'unknown feature', citedCount: 0 };
171
- }
172
-
173
- if (!existsSync(recapPath)) {
174
- return { status: 'skip', warnings: [], reason: 'no recap.md', citedCount: 0 };
125
+ const featureDir = join(projectPath, '.morph', 'features', featureName);
126
+ const progress = sliceProgress(featureDir);
127
+ const reports = (progress?.slices ?? [])
128
+ .map((slice) => ({ id: slice.id, text: readOrNull(sliceReportPath(featureDir, slice.id)) }))
129
+ .filter((r) => r.text !== null);
130
+ if (reports.length === 0) {
131
+ return { status: 'skip', warnings: [], reason: 'no slice reports', citedCount: 0 };
175
132
  }
176
133
 
177
- let recapText;
178
- try {
179
- recapText = readFileSync(recapPath, 'utf8');
180
- } catch {
181
- return { status: 'skip', warnings: [], reason: 'recap.md unreadable', citedCount: 0 };
134
+ const citedBy = new Map();
135
+ for (const { id, text } of reports) {
136
+ for (const file of extractCitedFiles(text)) {
137
+ if (!citedBy.has(file)) citedBy.set(file, []);
138
+ citedBy.get(file).push(id);
139
+ }
182
140
  }
183
-
184
- const cited = extractCitedFiles(recapText);
185
- if (cited.length === 0) {
186
- return { status: 'skip', warnings: [], reason: 'no Files: entries found in recap.md', citedCount: 0 };
141
+ if (citedBy.size === 0) {
142
+ return { status: 'skip', warnings: [], reason: 'no Arquivos: entries found in the slice reports', citedCount: 0 };
187
143
  }
188
144
 
189
- const outputs = collectDoneOutputs(projectPath, featureName);
190
- if (outputs === null) {
191
- return { status: 'skip', warnings: [], reason: 'no tasks.json', citedCount: cited.length };
145
+ const declared = collectSliceFiles(projectPath, featureName);
146
+ if (declared === null) {
147
+ return { status: 'skip', warnings: [], reason: 'no checks.json', citedCount: citedBy.size };
192
148
  }
193
149
 
194
- const warnings = cited
195
- .filter((file) => !isCovered(file, outputs))
150
+ const warnings = [...citedBy.keys()]
151
+ .filter((file) => !isCovered(file, declared))
196
152
  .sort()
197
- .map((file) => `${file} — cited in recap.md but not listed in any done task's outputs`);
153
+ .map((file) => `${file} — cited in reports/${citedBy.get(file).join(', ')} but declared in no slice's arquivos:`);
198
154
 
199
155
  return {
200
156
  status: warnings.length > 0 ? 'warn' : 'pass',
201
157
  warnings,
202
158
  reason: null,
203
- citedCount: cited.length,
159
+ citedCount: citedBy.size,
204
160
  };
205
161
  }
@@ -7,24 +7,14 @@
7
7
  * MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
8
8
  */
9
9
 
10
- import { existsSync, readFileSync } from 'fs';
11
- import { join } from 'path';
10
+ import { existsSync } from 'fs';
12
11
  import chalk from 'chalk';
13
12
  import { getAbsoluteOutputPath, resolveContractsOutputType } from '../../core/paths/output-schema.js';
14
13
  import { getStackProfile } from '../stack/stack-profile.js';
15
- import { aggregateScores, loadRubric } from './score-aggregator.js';
16
14
 
17
- /**
18
- * Heavy validators skipped in 'light' mode (checkpoint-only).
19
- *
20
- * What light mode actually leaves behind is whatever the stack contributes that
21
- * is NOT in this set — in practice `contrast` and `nextjs-component`. There are
22
- * no `spec` or `content` validators; the switch in runSingleValidator has no
23
- * case for either, and the old comment here promised both.
24
- */
15
+ // Skipped in 'light' mode (checkpoint-only); light keeps `nextjs-component`.
25
16
  const HEAVY_VALIDATORS = new Set([
26
- 'architecture', 'packages', 'contract-compliance',
27
- 'css', 'design-system', 'reuse', 'code-budget'
17
+ 'packages', 'contract-compliance', 'reuse', 'code-budget'
28
18
  ]);
29
19
 
30
20
  /**
@@ -167,17 +157,12 @@ export async function runValidation(projectPath, featureName, options = {}) {
167
157
  result.issues.push(...issues);
168
158
 
169
159
  // A validator that skipped itself checked nothing: in `results` it would
170
- // be scored as a clean 10 and dilute the ones that ran.
160
+ // read as a validator that ran clean.
171
161
  if (!validatorResult._skipReason) result.results[validatorId] = { passed: errors === 0, issues };
172
162
  }
173
163
  } catch (err) {
174
- // A validator that throws is a FAILED validation, not a silent skip.
175
- // This used to record `passed: false` in `results` and stop there —
176
- // `result.errors` never moved, so `result.passed = errors === 0` stayed
177
- // true and the run reported "✅ All validations passed". The only trace
178
- // was a console line, and `verify` silences stdout. That is how a broken
179
- // import (`css`) and a path bug (`packages` under a different cwd) went
180
- // unnoticed: both threw, both read as clean.
164
+ // A validator that throws is a FAILED validation, not a silent skip:
165
+ // `verify` silences stdout, so a console line alone would read as clean.
181
166
  if (options.verbose) {
182
167
  console.log(chalk.yellow(` ⚠ Validator '${validatorId}' failed: ${err.message}`));
183
168
  }
@@ -208,17 +193,6 @@ export async function runValidation(projectPath, featureName, options = {}) {
208
193
  ? `✅ All validations passed (${result.warnings} warnings)`
209
194
  : `❌ ${result.errors} error(s), ${result.warnings} warning(s)`;
210
195
 
211
- // Score aggregation — layered on top of pass/fail
212
- try {
213
- const rubricId = readRubricConfig(projectPath);
214
- const rubric = loadRubric(projectPath, rubricId);
215
- const thresholdOverrides = readThresholdConfig(projectPath);
216
- result.scores = aggregateScores(result, rubric, thresholdOverrides);
217
- } catch {
218
- // Score aggregation is non-blocking — if it fails, pass/fail still works
219
- result.scores = null;
220
- }
221
-
222
196
  return result;
223
197
  }
224
198
 
@@ -243,7 +217,7 @@ function detectValidators(projectPath = '.') {
243
217
  * seven validators only two returned all three keys. `packages` — one of the
244
218
  * two defaults for the `dotnet` stack — returned
245
219
  * `{status, totalFiles, filesWithIssues, results}` and had every NuGet
246
- * incompatibility it found thrown away, while `verify` reported 10/10.
220
+ * incompatibility it found thrown away, while `verify` reported green.
247
221
  *
248
222
  * Reading the nested `results` shape here keeps that contract in ONE place
249
223
  * instead of asking every validator to remember it.
@@ -257,7 +231,7 @@ export function normalizeValidatorResult(validatorId, raw) {
257
231
 
258
232
  // Issues can be top-level, or nested one level under `results` — either as
259
233
  // an array of `{issues}` (packages: one entry per .csproj) or an object of
260
- // them (architecture: keyed by check name). The nested read is a FALLBACK,
234
+ // them keyed by check name. The nested read is a FALLBACK,
261
235
  // never additive: validators that expose both would otherwise report every
262
236
  // finding twice.
263
237
  const topLevel = Array.isArray(raw.issues) ? raw.issues : [];
@@ -299,38 +273,16 @@ export function normalizeValidatorResult(validatorId, raw) {
299
273
  */
300
274
  async function runSingleValidator(validatorId, projectPath, featureName, options) {
301
275
  switch (validatorId) {
302
- case 'architecture': {
303
- const { validateArchitecture } = await import('./architecture/architecture-validator.js');
304
- return await validateArchitecture(projectPath, options);
305
- }
306
-
307
276
  case 'packages': {
308
277
  const { validatePackages } = await import('./packages/package-validator.js');
309
278
  return await validatePackages(projectPath, options);
310
279
  }
311
280
 
312
- case 'contrast': {
313
- const { validateContrast } = await import('./ui/ui-contrast-validator.js');
314
- return await validateContrast(projectPath, options);
315
- }
316
-
317
- case 'css': {
318
- // The module exports `validateCssClasses`; this asked for `validateCss`,
319
- // which is undefined — the call threw on every invocation.
320
- const { validateCssClasses } = await import('./css/css-validator.js');
321
- return await validateCssClasses(projectPath, options);
322
- }
323
-
324
281
  case 'contract-compliance': {
325
282
  const { validateContracts } = await import('./contracts/contract-compliance-validator.js');
326
283
  return await validateContracts(projectPath, featureName, options);
327
284
  }
328
285
 
329
- case 'design-system': {
330
- const { validateDesignSystem } = await import('./design-system/design-system-validator.js');
331
- return await validateDesignSystem(projectPath, featureName, options);
332
- }
333
-
334
286
  case 'reuse': {
335
287
  const { validateReuse } = await import('./reuse/reuse-validator.js');
336
288
  return await validateReuse(projectPath, featureName, options);
@@ -414,35 +366,3 @@ export function formatValidationResults(result) {
414
366
 
415
367
  console.log('');
416
368
  }
417
-
418
- /**
419
- * Read rubric ID from project config.
420
- * @param {string} projectPath
421
- * @returns {string}
422
- */
423
- function readRubricConfig(projectPath) {
424
- try {
425
- const configPath = join(projectPath, '.morph', 'config', 'config.json');
426
- if (!existsSync(configPath)) return 'default';
427
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
428
- return config.validation?.rubric ?? 'default';
429
- } catch {
430
- return 'default';
431
- }
432
- }
433
-
434
- /**
435
- * Read threshold overrides from project config.
436
- * @param {string} projectPath
437
- * @returns {Object} { approve?: number, retry?: number }
438
- */
439
- function readThresholdConfig(projectPath) {
440
- try {
441
- const configPath = join(projectPath, '.morph', 'config', 'config.json');
442
- if (!existsSync(configPath)) return {};
443
- const config = JSON.parse(readFileSync(configPath, 'utf8'));
444
- return config.validation?.thresholds ?? {};
445
- } catch {
446
- return {};
447
- }
448
- }