@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,30 +1,10 @@
1
- /**
2
- * O checklist de tela do `1-design/mockups.md` — as decisões que uma tela de
3
- * dados precisa (volume, paginação, ordenação, filtro, os três estados,
4
- * mobile, abas) escritas por tela, na fase uiux.
5
- *
6
- * Existe porque o que o design não pergunta não vira cenário, e o que
7
- * não é cenário ninguém entrega: telas de admin saíam listando tudo, sem
8
- * paginar nem ordenar, com o design aprovado. Os leitores do MESMO parser:
9
- *
10
- * - `gate-check uiux` pausa com os problemas nomeados;
11
- * - `approve`/`advance` com assinatura `auto` recusam a mesma pausa sem uma
12
- * decisão gravada (`uiuxAutoApprovalBlock`) — a regra é a do risk-detector;
13
- * - `gate-check review` exige que o verificador registre a conferência de cada
14
- * item decidido contra a tela entregue (`checklistCoverage`).
15
- *
16
- * Mockup no formato ANTERIOR ao checklist (telas `## S001: …`, sem nenhuma
17
- * `## Tela:`) é legado e isento, com a isenção gravada no sinal.
18
- *
19
- * O formato é o que a `morph-uiux` ensina: cada tela é um `## Tela: <nome>`, e
20
- * dentro dela um `### Checklist de tela` com uma linha por item — tabela
21
- * (`| Paginação | servidor, 25/página |`) ou lista (`- **Paginação:** …`).
22
- * "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são.
23
- */
1
+ // Cada tela é `## Tela: <nome>` com um `### Checklist de tela`, uma linha por item (tabela ou lista).
2
+ // "Não se aplica" é resposta válida; vazio, "?" ou "a definir" não são. Telas `## S001: …` sem
3
+ // nenhuma `## Tela:` são o formato legado, isento.
24
4
 
25
5
  import { createHash } from 'crypto';
26
6
  import { existsSync, readFileSync } from 'fs';
27
- import { join } from 'path';
7
+ import { dirname, join } from 'path';
28
8
  import { fileURLToPath } from 'url';
29
9
  import { getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
30
10
  import { verifierScreenChecklist } from '../review-reports.js';
@@ -32,12 +12,7 @@ import { LEGACY_MOCKUP_EXEMPTION, uiuxChecklistPause } from '../risk-detector.js
32
12
 
33
13
  const PACKAGE_AGENTS = fileURLToPath(new URL('../../../framework/agents.json', import.meta.url));
34
14
 
35
- /**
36
- * Os itens, na ordem do template. `match` roda sobre o rótulo NORMALIZADO
37
- * (minúsculas, sem acento): "Estado de erro", "Erro" e "**Erro:**" são o mesmo
38
- * item. Um rótulo que casa com vários ("Estados (vazio/carregando/erro)")
39
- * responde a todos eles.
40
- */
15
+ /** `match` roda sobre o rótulo normalizado; um rótulo que casa com vários itens responde a todos. */
41
16
  export const SCREEN_CHECKLIST_ITEMS = [
42
17
  { id: 'superficie', label: 'Superfície', match: /\bsuperficie\b/ },
43
18
  { id: 'volume', label: 'Volume esperado', match: /\bvolume\b/ },
@@ -51,26 +26,24 @@ export const SCREEN_CHECKLIST_ITEMS = [
51
26
  { id: 'abas', label: 'Abas', match: /\babas\b/ },
52
27
  ];
53
28
 
54
- /**
55
- * Itens que o verificador não confere contra a tela: a superfície decide QUAIS
56
- * standards a tela lê, não um comportamento que a tela entregue exiba.
57
- */
29
+ // A superfície decide quais standards a tela lê, não um comportamento que a tela entregue exiba.
58
30
  const NOT_VERIFIED_AT_REVIEW = new Set(['superficie']);
59
31
 
60
- /**
61
- * Respostas que ocupam a célula sem decidir nada: as exatas, e as que COMEÇAM
62
- * adiando a decisão ("a definir depois", "TBD.", "? talvez").
63
- */
32
+ // As exatas, e as que começam adiando a decisão ("a definir depois", "TBD.").
64
33
  const PLACEHOLDERS = new Set(['', '-', '--', '—', '–', '...', 'x', 'todo', 'definir', 'pendente']);
65
34
  const PLACEHOLDER_PREFIX_RE = /^(a definir|tbd|\?|…)/;
66
35
 
67
- /** O formato de mockup anterior ao checklist: telas `## S001: <nome>`. */
68
36
  const LEGACY_SCREEN_HEADING_RE = /^##\s+S\d+\s*:/i;
69
37
 
70
38
  const NOT_APPLICABLE_RE = /^(nao se aplica|n\/a)\b/;
71
39
 
72
40
  const SCREEN_HEADING_RE = /^##\s+tela\s*[:—–-]\s*(.+?)\s*#*\s*$/i;
73
41
  const CHECKLIST_HEADING_RE = /^(#{3,6})\s+checklist\b/i;
42
+ const PROTOTYPE_LINE_RE = /prot[oó]tipo/i;
43
+ const PROTOTYPE_PATH_RE = /(?:1-design\/)?(prototipos\/[^\s`|)\]]+\.html)/i;
44
+
45
+ /** As larguras dos prints que o laço de autocrítica deixa ao lado do protótipo. */
46
+ export const PROTOTYPE_WIDTHS = [1280, 390];
74
47
 
75
48
  /** Minúsculas, sem acento, sem marcação inline, espaços colapsados. */
76
49
  export function normalizeLabel(text) {
@@ -83,21 +56,15 @@ export function normalizeLabel(text) {
83
56
  .trim();
84
57
  }
85
58
 
86
- /** Nome de tela comparável entre o mockup e o relatório do avaliador. */
87
59
  function screenKey(name) {
88
60
  return normalizeLabel(name).replace(/^tela\s*[:—–-]\s*/, '').replace(/[.:;,]+$/, '');
89
61
  }
90
62
 
91
- /** Os itens que um rótulo responde. */
92
63
  function itemsOf(label) {
93
64
  const norm = normalizeLabel(label);
94
65
  return SCREEN_CHECKLIST_ITEMS.filter((item) => item.match.test(norm)).map((item) => item.id);
95
66
  }
96
67
 
97
- /**
98
- * Uma linha de checklist como `[rótulo, resposta]`, ou null: linha de tabela
99
- * (separador `|---|` fora) ou item de lista `- Rótulo: resposta`.
100
- */
101
68
  function checklistRow(line) {
102
69
  const trimmed = line.trim();
103
70
  if (trimmed.startsWith('|')) {
@@ -112,13 +79,12 @@ function checklistRow(line) {
112
79
  return [bullet[1].slice(0, colon), bullet[1].slice(colon + 1).trim()];
113
80
  }
114
81
 
115
- /** As células de uma linha de tabela markdown, sem as bordas. */
116
82
  export function tableCells(line) {
117
83
  const inner = line.trim().replace(/^\|/, '').replace(/\|$/, '');
118
84
  return inner.split(/(?<!\\)\|/).map((cell) => cell.trim());
119
85
  }
120
86
 
121
- /** Linhas fora de blocos cercados: um exemplo dentro de ``` não é uma tela. */
87
+ // Um exemplo dentro de ``` não é uma tela.
122
88
  function proseLines(markdown) {
123
89
  const out = [];
124
90
  let fenced = false;
@@ -129,12 +95,6 @@ function proseLines(markdown) {
129
95
  return out;
130
96
  }
131
97
 
132
- /**
133
- * As telas do mockup e o que o checklist de cada uma responde.
134
- *
135
- * @param {string} markdown - conteúdo do `1-design/mockups.md`
136
- * @returns {Array<{ name: string, hasChecklist: boolean, answers: Map<string, string> }>}
137
- */
138
98
  export function parseScreens(markdown) {
139
99
  const screens = [];
140
100
  let screen = null;
@@ -146,7 +106,7 @@ export function parseScreens(markdown) {
146
106
  const level = heading[1].length;
147
107
  const named = SCREEN_HEADING_RE.exec(line);
148
108
  if (level <= 2) {
149
- screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map() } : null;
109
+ screen = named && named[1].trim() ? { name: named[1].trim(), hasChecklist: false, answers: new Map(), prototype: null } : null;
150
110
  if (screen) screens.push(screen);
151
111
  checklistLevel = 0;
152
112
  continue;
@@ -160,6 +120,9 @@ export function parseScreens(markdown) {
160
120
  }
161
121
  continue;
162
122
  }
123
+ if (screen && !screen.prototype && PROTOTYPE_LINE_RE.test(line)) {
124
+ screen.prototype = PROTOTYPE_PATH_RE.exec(line)?.[1] ?? null;
125
+ }
163
126
  if (!screen || !checklistLevel) continue;
164
127
  const row = checklistRow(line);
165
128
  if (!row) continue;
@@ -178,16 +141,8 @@ const isNotApplicable = (answer) => NOT_APPLICABLE_RE.test(normalizeLabel(answer
178
141
 
179
142
  const labelOf = (id) => SCREEN_CHECKLIST_ITEMS.find((item) => item.id === id)?.label ?? id;
180
143
 
181
- /**
182
- * As superfícies que a persona `ui-designer` declara no `agents.json` — o
183
- * vocabulário válido do item `Superfície`, que o `morph-plan` usa para
184
- * somar os standards da tela no `standards:` da fatia. Lê a cópia instalada do
185
- * projeto; sem ela (este repositório), a do pacote. Persona sem `surfaces`
186
- * (instalação anterior ao campo) devolve null: o valor não é validado.
187
- *
188
- * @param {string} cwd
189
- * @returns {string[]|null}
190
- */
144
+ // O vocabulário válido de `Superfície`: as `surfaces` da `ui-designer`, da cópia instalada ou do pacote.
145
+ // null quando a persona não declara o campo, e então o valor não é validado.
191
146
  export function knownSurfaces(cwd) {
192
147
  const surfaces = readAgentsJson(cwd)?.agents?.['ui-designer']?.surfaces;
193
148
  return surfaces && typeof surfaces === 'object' && !Array.isArray(surfaces) ? Object.keys(surfaces) : null;
@@ -202,24 +157,13 @@ export function readAgentsJson(cwd) {
202
157
  return null;
203
158
  }
204
159
 
205
- /** A resposta de `Superfície` nomeia uma das superfícies conhecidas? */
206
160
  function namesSurface(answer, surfaces) {
207
161
  const norm = normalizeLabel(answer);
208
162
  return surfaces.some((key) => new RegExp(`(^|[^a-z0-9-])${key}([^a-z0-9-]|$)`).test(norm));
209
163
  }
210
164
 
211
- /**
212
- * Audita o checklist do mockup: toda tela tem o bloco, e todo item tem uma
213
- * resposta escrita — e, quando `surfaces` é dado, a `Superfície` é uma delas.
214
- *
215
- * Sem nenhuma `## Tela:`, o mockup é `legacy` (isento) só quando declara telas
216
- * no formato antigo; qualquer outro formato é o problema "nenhuma tela
217
- * declarada" — senão trocar o heading seria o atalho para pular o checklist.
218
- *
219
- * @param {string} markdown
220
- * @returns {{ legacy: boolean, screens: Array<{ name: string, hasChecklist: boolean, missing: string[], unanswered: string[] }>,
221
- * problems: string[] }} `problems` vazio ⇔ checklist completo (ou isento)
222
- */
165
+ // Sem nenhuma `## Tela:`, só o formato antigo é legado; qualquer outro heading é "nenhuma tela
166
+ // declarada", senão trocar o heading pularia o checklist.
223
167
  export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
224
168
  const parsed = parseScreens(markdown);
225
169
  const problems = [];
@@ -247,31 +191,38 @@ export function auditScreenChecklist(markdown, { surfaces = null } = {}) {
247
191
  problems.push(`tela "${screen.name}": Superfície "${surface}" não é uma das superfícies da ui-designer (${surfaces.join(', ')})`);
248
192
  }
249
193
  }
250
- return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered };
194
+ return { name: screen.name, hasChecklist: screen.hasChecklist, missing, unanswered, prototype: screen.prototype };
251
195
  });
252
196
  return { legacy: false, screens, problems };
253
197
  }
254
198
 
255
- /**
256
- * O sinal `screenChecklist` de um gate, lido do disco. COLETA, nunca decide:
257
- * quem pausa é o risk-detector.
258
- *
259
- * `mockups: false` é "a feature não tem mockup" — nada a cobrar. No review, o
260
- * que se cobra é a conferência (`uncovered`: itens decididos sem linha na
261
- * seção do verificador), não o design, que já passou pelo Gate uiux.
262
- *
263
- * @param {string} cwd
264
- * @param {string} featureName
265
- * @param {string} gateName
266
- * @returns {{ mockups: boolean, legacy: boolean, screens: string[], problems: string[],
267
- * exempt?: string, section?: boolean, uncovered?: string[] }}
268
- */
199
+ // Linha **Protótipo:** da tela + o HTML + os prints do laço de autocrítica ao lado dele.
200
+ export function auditPrototypes(screens, designDir) {
201
+ const problems = [];
202
+ for (const screen of screens) {
203
+ if (!screen.prototype) {
204
+ problems.push(`tela "${screen.name}" sem a linha \`**Protótipo:** 1-design/prototipos/<tela>.html\``);
205
+ continue;
206
+ }
207
+ const base = screen.prototype.replace(/\.html$/i, '');
208
+ const expected = [screen.prototype, ...PROTOTYPE_WIDTHS.map((width) => `${base}-${width}.png`)];
209
+ const missing = expected.filter((rel) => !existsSync(join(designDir, rel)));
210
+ if (missing.length) problems.push(`tela "${screen.name}": falta ${missing.map((rel) => `1-design/${rel}`).join(', ')}`);
211
+ }
212
+ return problems;
213
+ }
214
+
215
+ // Coleta, nunca decide: quem pausa é o risk-detector. No review cobra-se a conferência (`uncovered`), não o design.
269
216
  export function collectScreenChecklist(cwd, featureName, gateName) {
270
217
  const mockups = readMockups(cwd, featureName);
271
218
  if (mockups === null) return { mockups: false, legacy: false, screens: [], problems: [] };
272
219
  const audit = auditScreenChecklist(mockups, { surfaces: knownSurfaces(cwd) });
273
220
  const signal = { mockups: true, legacy: audit.legacy, screens: audit.screens.map((s) => s.name), problems: audit.problems };
274
221
  if (audit.legacy) signal.exempt = LEGACY_MOCKUP_EXEMPTION;
222
+ if (gateName === 'uiux' && !audit.legacy) {
223
+ const designDir = dirname(getAbsoluteOutputPath(cwd, featureName, 'uiMockups'));
224
+ signal.prototypes = auditPrototypes(audit.screens, designDir);
225
+ }
275
226
  if (gateName === 'review') {
276
227
  const report = verifierScreenChecklist(cwd, featureName);
277
228
  signal.section = report.section;
@@ -280,36 +231,15 @@ export function collectScreenChecklist(cwd, featureName, gateName) {
280
231
  return signal;
281
232
  }
282
233
 
283
- /**
284
- * Por que o Gate uiux NÃO pode ser aprovado com assinatura `auto` — a pausa
285
- * que o `gate-check uiux` daria pelo checklist —, ou null. Lido por `approve`
286
- * e `advance`, para que uma aprovação automática nunca passe onde o gate-check
287
- * pausaria. Uma decisão gravada com `pause: false` (o `gate-decision` manual,
288
- * ou um gate-check que não pausou) é a resposta a essa pausa e destrava — só
289
- * para o mockup que ela viu: a decisão guarda `mockupsHash`, e um mockup
290
- * escrito ou mudado depois dela volta a ser conferido.
291
- *
292
- * @param {string} cwd
293
- * @param {string} featureName
294
- * @param {object} feature - o feature.json (lê `gateDecisions.uiux`)
295
- * @returns {string|null}
296
- */
234
+ // Lido por approve e advance para que o `auto` nunca passe onde o gate-check pausaria. Uma decisão com
235
+ // `pause: false` destrava só o design que ela viu: guarda `mockupsHash`, e mudar o arquivo volta a conferir.
297
236
  export function uiuxAutoApprovalBlock(cwd, featureName, feature) {
298
237
  const decision = feature?.gateDecisions?.uiux;
299
238
  if (decision?.pause === false && decision.mockupsHash === mockupsHash(cwd, featureName)) return null;
300
239
  return uiuxChecklistPause(collectScreenChecklist(cwd, featureName, 'uiux'));
301
240
  }
302
241
 
303
- /**
304
- * O que o verificador deixou de conferir: por tela do mockup, os itens com
305
- * decisão (nem "não se aplica", nem a superfície) sem linha na seção
306
- * `## Checklist de tela` do relatório. Uma linha que o avaliador marca como
307
- * não verificável conta como conferida — o que pausa é a AUSÊNCIA da linha.
308
- *
309
- * @param {string} designMarkdown - o `1-design/mockups.md`
310
- * @param {string} reportSection - o corpo da seção do `verifier.md`
311
- * @returns {string[]} `"<tela>: <item>, <item>"` por tela com lacuna
312
- */
242
+ // Linha marcada como não verificável conta como conferida: o que pausa é a ausência da linha.
313
243
  export function checklistCoverage(designMarkdown, reportSection) {
314
244
  const covered = new Map();
315
245
  for (const line of proseLines(reportSection)) {
@@ -333,14 +263,7 @@ export function checklistCoverage(designMarkdown, reportSection) {
333
263
  return gaps;
334
264
  }
335
265
 
336
- /**
337
- * O `1-design/mockups.md` por conteúdo: BOM e CRLF não contam, para que o
338
- * checkout de outra máquina seja o mesmo mockup.
339
- *
340
- * @param {string} cwd
341
- * @param {string} featureName
342
- * @returns {string|null} null sem mockup
343
- */
266
+ // BOM e CRLF não contam: o checkout de outra máquina é o mesmo arquivo.
344
267
  export function mockupsHash(cwd, featureName) {
345
268
  const text = readMockups(cwd, featureName);
346
269
  if (text === null) return null;
@@ -27,17 +27,13 @@ function planHasDotnetSlice(tree, featureName) {
27
27
  return (checks?.slices || []).some((slice) => DOTNET_IMPLEMENTERS.includes(String(slice.persona || '').replace(/^morph-/, '')));
28
28
  }
29
29
 
30
- /**
31
- * "Is this a .NET project?" from what is known before the first code commit:
32
- * the declared `project.stack`, a .NET implementer in the plan's slices, or a
33
- * .csproj/.sln/.slnx already in the tree. Greenfield has only the second.
34
- */
30
+ // The plan's slices are checked because a greenfield tree has no stack or .csproj yet.
35
31
  export function isDotnetProject(tree, { featureName, configRoot = tree } = {}) {
36
32
  if (getProjectTags(readMorphConfig(configRoot)).includes('dotnet')) return true;
37
33
  return planHasDotnetSlice(tree, featureName) || hasDotnetProjectFile(tree);
38
34
  }
39
35
 
40
- /** @returns {string[]} the `.gitignore` entries added to `tree`; `[]` when not .NET or on any error */
36
+ /** Returns the `.gitignore` entries added; `[]` when not .NET or on any error. */
41
37
  export function seedDotnetBuildIgnores(tree, options = {}) {
42
38
  try {
43
39
  return isDotnetProject(tree, options) ? ensureDotnetBuildIgnores(tree) : [];
@@ -1,17 +1,6 @@
1
1
  import { readFileSync, existsSync } from 'fs';
2
2
  import { join } from 'path';
3
3
 
4
- /** @typedef {'nextjs'|'dotnet'|'nodejs-cli'|'unknown'} StackName */
5
-
6
- /**
7
- * @typedef {Object} StackProfile
8
- * @property {StackName} stack
9
- * @property {boolean} isDotnet
10
- * @property {boolean} isNextjs
11
- * @property {string[]} defaultValidators
12
- * @property {string|null} buildCommand
13
- */
14
-
15
4
  const DOTNET_STACKS = new Set(['dotnet', 'aspnet']);
16
5
  const NEXTJS_STACKS = new Set(['nextjs', 'next', 'next.js']);
17
6
  const NO_VALIDATOR_STACKS = new Set(['nodejs-cli', 'node-cli', 'cli']);
@@ -34,9 +23,6 @@ const PROFILES = {
34
23
  },
35
24
  };
36
25
 
37
- /**
38
- * Read project stack from .morph/config/config.json.
39
- */
40
26
  function readStack(cwd) {
41
27
  const configPath = join(cwd, '.morph', 'config', 'config.json');
42
28
  if (!existsSync(configPath)) return null;
@@ -47,7 +33,6 @@ function readStack(cwd) {
47
33
  }
48
34
  }
49
35
 
50
- /** Merge two or more profiles' validator lists (deduped) and boolean flags. */
51
36
  function mergeProfiles(profiles) {
52
37
  return {
53
38
  isDotnet: profiles.some((p) => p.isDotnet),
@@ -57,16 +42,7 @@ function mergeProfiles(profiles) {
57
42
  };
58
43
  }
59
44
 
60
- /**
61
- * Get the StackProfile for the current project.
62
- *
63
- * `stack` may be a compound value (e.g. "dotnet-nextjs", "dotnet+nextjs") —
64
- * the framework's own convention for monorepos (see stack-filter.js's
65
- * getProjectTags, which splits project.stack the same way). Each segment is
66
- * resolved independently and matching profiles are merged, instead of the
67
- * whole compound string failing every exact-match check and silently
68
- * collapsing to the empty profile (0 default validators).
69
- */
45
+ /** A compound stack ("dotnet-nextjs", "dotnet+nextjs") merges the profile of each segment. */
70
46
  export function getStackProfile(cwd, stackOverride) {
71
47
  const root = cwd ?? process.cwd();
72
48
  const raw = stackOverride ?? readStack(root);
@@ -74,8 +50,6 @@ export function getStackProfile(cwd, stackOverride) {
74
50
 
75
51
  const segments = stack.split(/[-+]/).filter(Boolean);
76
52
 
77
- // Single-segment stack (the overwhelming common case): resolve exactly as
78
- // before, byte-for-byte, including the 'unknown' → dotnet fallback.
79
53
  if (segments.length <= 1) {
80
54
  let profile;
81
55
  if (NEXTJS_STACKS.has(stack)) {
@@ -90,7 +64,6 @@ export function getStackProfile(cwd, stackOverride) {
90
64
  return { stack, ...profile };
91
65
  }
92
66
 
93
- // Compound stack: resolve each recognized segment and merge.
94
67
  const matched = [];
95
68
  if (segments.some((s) => DOTNET_STACKS.has(s))) matched.push(PROFILES.dotnet);
96
69
  if (segments.some((s) => NEXTJS_STACKS.has(s))) matched.push(PROFILES.nextjs);
@@ -175,13 +175,7 @@ function auditNextjs(root, read) {
175
175
 
176
176
  const AUDITS = { dotnet: auditDotnet, nextjs: auditNextjs };
177
177
 
178
- /**
179
- * Deterministic stack audit of the tree at `root`, driven by `config.project.stack`:
180
- * only the configured stacks are judged; no configured stack means no rule runs.
181
- *
182
- * @param {string} root
183
- * @param {object|null} config the parsed `.morph/config/config.json`
184
- */
178
+ /** Only the stacks in `config.project.stack` are judged; none configured means no rule runs. */
185
179
  export function scanStack(root, config) {
186
180
  const stack = typeof config?.project?.stack === 'string' ? config.project.stack : null;
187
181
  const tags = getProjectTags(config);
@@ -230,7 +224,6 @@ export function notAuditedReason(result) {
230
224
  : 'não auditado: project.stack ausente em .morph/config/config.json';
231
225
  }
232
226
 
233
- /** The scan as the markdown report the morph-plan skill reads. */
234
227
  export function renderStackScan(result, title) {
235
228
  const out = [`# Stack Scan — ${title}`, ''];
236
229
  out.push(`**Stack configurado:** ${result.stack ? `\`${result.stack}\`` : 'ausente em `project.stack` — nenhuma regra aplicada'}`);
@@ -1,14 +1,4 @@
1
- /**
2
- * Parse `stacks:` from YAML frontmatter content.
3
- * Returns ['*'] if no stacks field found (backwards compatible — always install).
4
- *
5
- * Supports:
6
- * stacks:\n - dotnet\n - blazor (YAML list)
7
- * stacks: [dotnet, blazor] (inline array)
8
- *
9
- * @param {string} content - Raw file content with YAML frontmatter
10
- * @returns {string[]}
11
- */
1
+ /** `['*']` without a `stacks:` field (always install); accepts a YAML list or an inline array. */
12
2
  export function parseStacks(content) {
13
3
  const normalized = content.replace(/\r\n/g, '\n');
14
4
  const fmMatch = normalized.match(/^---\n([\s\S]*?)\n---/);
@@ -16,9 +6,7 @@ export function parseStacks(content) {
16
6
 
17
7
  const fm = fmMatch[1];
18
8
 
19
- // Try inline format: stacks: [dotnet, blazor] — quotes stripped like the
20
- // list branch below; `["umbraco"]` sem isso parseava como a string literal
21
- // `"umbraco"` (com aspas) e nunca casava tag nenhuma.
9
+ // Quotes stripped like the list branch: `["umbraco"]` would otherwise never match a tag.
22
10
  const inlineMatch = fm.match(/^stacks:\s*\[([^\]]*)\]/m);
23
11
  if (inlineMatch) {
24
12
  const items = inlineMatch[1]
@@ -28,7 +16,6 @@ export function parseStacks(content) {
28
16
  return items.length > 0 ? items : ['*'];
29
17
  }
30
18
 
31
- // Try YAML list format: stacks:\n - dotnet\n - blazor
32
19
  const listMatch = fm.match(/^stacks:\s*\n((?:\s+-\s+.+\n?)*)/m);
33
20
  if (listMatch) {
34
21
  const items = listMatch[1]
@@ -41,12 +28,7 @@ export function parseStacks(content) {
41
28
  return ['*'];
42
29
  }
43
30
 
44
- /**
45
- * Extract project tags from config object.
46
- * Combines stack tags (from `project.stack` split by '-' or '+')
47
- * with integration tags (from `project.integrations` array).
48
- * Deduplicates the result.
49
- */
31
+ /** Stack tags (`project.stack` split by '-' or '+') plus `project.integrations`, deduplicated. */
50
32
  export function getProjectTags(config) {
51
33
  const stack = config?.project?.stack;
52
34
  const stackTags = (stack && typeof stack === 'string')
@@ -58,12 +40,7 @@ export function getProjectTags(config) {
58
40
  return [...new Set([...stackTags, ...integrations])];
59
41
  }
60
42
 
61
- /**
62
- * Determine if an asset should be installed for the given project tags.
63
- * - stacks includes '*' → always install
64
- * - stacks is empty → treat as '*' (backwards compatible)
65
- * - Otherwise → install if intersection with projectTags is non-empty
66
- */
43
+ /** Empty or '*' stacks always install; otherwise any intersection with the project tags. */
67
44
  export function shouldInstall(assetStacks, projectTags) {
68
45
  if (assetStacks.length === 0 || assetStacks.includes('*')) return true;
69
46
  return assetStacks.some(s => projectTags.includes(s));
@@ -22,13 +22,7 @@ function readMeta(standardsJsonPath) {
22
22
  }
23
23
  }
24
24
 
25
- /**
26
- * The registry as every reader sees it: runtime entries with their sidecar
27
- * fields merged back by id. A missing or unreadable sidecar degrades to "no
28
- * aliases"; a missing or unreadable STANDARDS.json throws.
29
- * @param {string} standardsJsonPath
30
- * @returns {{ version: string|null, standards: object[], sharedAliases: Record<string, string[]> }}
31
- */
25
+ /** Sidecar merged by id. Missing/unreadable sidecar = no aliases; missing STANDARDS.json throws. */
32
26
  export function loadStandardsRegistry(standardsJsonPath) {
33
27
  const runtime = JSON.parse(readFileSync(standardsJsonPath, 'utf8'));
34
28
  const meta = readMeta(standardsJsonPath);
@@ -5,12 +5,7 @@ export function ghSlug(heading) {
5
5
  return heading.trim().toLowerCase().replace(SLUG_STRIP, '').replace(/ /g, '-');
6
6
  }
7
7
 
8
- /**
9
- * Every heading outside code fences, with the slug GitHub gives it (`-1`, `-2`
10
- * for repeats) and the line it sits on.
11
- * @param {string} text
12
- * @returns {Array<{ level: number, title: string, slug: string, line: number }>}
13
- */
8
+ /** Headings outside code fences, repeated slugs suffixed `-1`, `-2` as GitHub does. */
14
9
  export function markdownHeadings(text) {
15
10
  const out = [];
16
11
  const seen = new Map();
@@ -39,13 +34,7 @@ export function headingSlugs(text) {
39
34
  return new Set(markdownHeadings(text).map((h) => h.slug));
40
35
  }
41
36
 
42
- /**
43
- * The section a slug opens: its heading through the line before the next
44
- * heading of the same or a higher level. `null` when no heading has the slug.
45
- * @param {string} text
46
- * @param {string} slug - with or without the leading `#`
47
- * @returns {string|null}
48
- */
37
+ /** Heading through the line before the next heading of same or higher level; slug may carry `#`. */
49
38
  export function sectionBySlug(text, slug) {
50
39
  const want = String(slug).replace(/^#/, '');
51
40
  const all = markdownHeadings(text);