@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,81 +1,24 @@
1
- /**
2
- * Risk Detector — decides whether a gate should pause execution.
3
- *
4
- * In `trust: "auto"` mode (default), gates auto-pass UNLESS one of these
5
- * risk signals triggers a forced pause. In `trust: "manual"` mode, gates
6
- * always pause via AskUserQuestion.
7
- *
8
- * Signals checked (in order):
9
- * - designSystemDivergence: boolean — conflicting design sources detected
10
- * - standardsViolations: string[] — violations of the plan's forbidden patterns
11
- * - verificationStatus: string — último `morph-spec verify` ('fail' pausa, qualquer gate)
12
- * - e2eStatus + e2eReason + gate — nó e2e do último verify. Um `skip`
13
- * cuja razão NÃO seja dispensa legítima pausa SÓ no ship (gate 'review')
14
- * - verificationStale + gate: bool/str — stamp de um commit cujo código já mudou
15
- * (fora de .morph/) pausa SÓ no ship (gate 'review')
16
- * - reviewReports + gate: object — `reviewSignal` (review-reports.js): os
17
- * relatórios exigidos e a cobertura dos Cn. No ship, só SHIP sem bloqueador
18
- * em todo exigido, com todo Cn PROVADO, libera
19
- * - mediaClaims + gate: object — { section, featureGeneratesMedia }
20
- * - screenChecklist + gate: object — { mockups, legacy, screens, problems, exempt?,
21
- * section?, uncovered? } derivado do 1-design/mockups.md. No uiux, checklist
22
- * de tela incompleto pausa; no ship, item decidido sem a conferência do
23
- * verificador pausa. Mockup legado é isento nos dois, com a razão listada
24
- * - planChecks + gate: string — `readPlanChecks(...).status`: os checks
25
- * extraídos contra o plano; no gate do plano, tudo que não é `fresh` pausa
26
- * - sliceCount: number — fatias extraídas (pausa acima de MAX_SLICES)
27
- */
28
-
29
1
  import { MAX_SLICES } from './plan/checks.js';
30
2
  import { BLOCKER_TYPES, reviewFileName } from './review-reports.js';
31
3
 
32
- // Caminho alterado que pede a revisão de segurança (persona `security`, opus).
33
4
  const SECURITY_PATH_RE = /(^|[/._-])(auth|login|senha|password|token|secret|crypto|permiss|rbac|acl|lgpd|pii|privacy|payment|pagamento|billing|cobranca|invoice|asaas|stripe|migrations?)([/._-]|$)/i;
34
5
 
35
- // Work types whose ship gate (review) always pauses for a human, regardless
36
- // of trust mode — a type policy, not a per-feature risk signal. Mirrors
37
- // `forcedPauseGates` in src/core/work-types.js (hotfix.review); kept here too
38
- // because risk-detector is the deterministic layer consumed independent of
39
- // the work-type config object.
6
+ // Espelha `forcedPauseGates` (hotfix.review) de src/core/work-types.js: os dois mudam juntos.
40
7
  const FORCED_PAUSE_WORK_TYPES = ['hotfix'];
41
8
 
42
- // As razões literais que `framework/hooks/shared/e2e-check.js` emite quando o
43
- // nó e2e pula. Casadas por PADRÃO e não por igualdade porque duas delas
44
- // carregam dado variável (o nome do grupo da task). Ordem irrelevante: os
45
- // padrões são disjuntos.
9
+ // Razões literais que `e2e-check.js` emite ao pular; casadas por padrão porque algumas carregam dado variável.
46
10
  const E2E_SKIP_PATTERNS = [
47
- // "docker is not available in this environment — e2e could not run"
48
- // (e2e-check.js). A app não foi exercida porque a MÁQUINA não podia.
49
11
  [/docker is not available/i, 'environment'],
50
- // `--skip-e2e` — o operador pediu para pular.
51
12
  [/^requested$/i, 'requested'],
52
- // "project does not containerize (no compose file)" — não há app para subir.
53
13
  [/does not containerize/i, 'project'],
14
+ [/^not applicable:/i, 'not-applicable'],
54
15
  ];
55
16
 
56
- // Classes de skip que NÃO são dispensa: o e2e não rodou e nada no repositório
57
- // diz que ele não precisava rodar. `unknown` está aqui de propósito — inclui a
58
- // razão vazia/nula, e uma dispensa sem motivo é o mesmo no-op silencioso que
59
- // esta regra existe para eliminar.
17
+ // `unknown` (razão vazia) pausa de propósito: dispensa sem motivo é o no-op silencioso que a regra elimina.
60
18
  const E2E_SKIP_CLASSES_THAT_PAUSE = ['environment', 'requested', 'unknown'];
61
19
 
62
- /**
63
- * Classifica a razão literal de um `skip` do nó e2e — de QUEM é a ausência de
64
- * prova. Existe porque `verificationStatus` é o rollup do `verify` e um nó que
65
- * pula NÃO o reprova: sem classificar a razão, "o e2e rodou e passou" e "o e2e
66
- * nunca rodou" chegam ao gate como o mesmo `pass`.
67
- *
68
- * A assimetria é o ponto: `project` descreve o REPOSITÓRIO dizendo que
69
- * não há runtime a exercer (dispensa legítima, registrada com motivo);
70
- * `environment`/`requested` descrevem a MÁQUINA ou o operador, e nenhum dos
71
- * dois é evidência sobre o código. `unknown` cai no lado seguro pelo mesmo
72
- * motivo: recusar é barato, aprovar sem prova não é.
73
- *
74
- * @param {string|null|undefined} reason - `verification.e2eReason` (a razão
75
- * literal que o `morph-spec verify` carimbou). Vazia/ausente ⇒ 'unknown' —
76
- * DESCONHECIDO, jamais lido como dispensa.
77
- * @returns {'environment'|'requested'|'project'|'unknown'}
78
- */
20
+ // `project` diz que o repositório não tem runtime (dispensa legítima); `environment`/`requested` falam
21
+ // da máquina ou do operador, nunca do código. Razão vazia é `unknown`, jamais dispensa.
79
22
  export function classifyE2eSkip(reason) {
80
23
  const text = typeof reason === 'string' ? reason.trim() : '';
81
24
  if (text === '') return 'unknown';
@@ -85,7 +28,6 @@ export function classifyE2eSkip(reason) {
85
28
  return 'unknown';
86
29
  }
87
30
 
88
- /** Mensagem de pausa por classe — nomeia SEMPRE a razão literal do verify. */
89
31
  function e2ePauseReason(klass, reason) {
90
32
  const literal = typeof reason === 'string' && reason.trim() !== '' ? reason.trim() : null;
91
33
  if (klass === 'environment') {
@@ -102,38 +44,32 @@ function e2ePauseReason(klass, reason) {
102
44
  return `e2e pulado por razão não classificada: "${literal}" — sem classificação, não há dispensa`;
103
45
  }
104
46
 
105
- /** A razão gravada no sinal quando o checklist de tela isenta um mockup legado. */
106
47
  export const LEGACY_MOCKUP_EXEMPTION = 'checklist dispensado: mockup no formato antigo';
107
48
 
108
- /** Os dois primeiros itens de uma lista, e quantos ficaram de fora. */
109
49
  function firstOf(items) {
110
50
  const shown = items.slice(0, 2).join('; ');
111
51
  return items.length > 2 ? `${shown} (+${items.length - 2})` : shown;
112
52
  }
113
53
 
114
- /**
115
- * Por que o checklist de tela pausa o Gate uiux, ou null. Exportada porque o
116
- * `approve`/`advance` com assinatura `auto` recusam pela MESMA regra: uma
117
- * aprovação automática que passasse onde o gate-check pausa seria o atalho.
118
- *
119
- * Mockup ausente (`mockups: false`) não conta — nem toda feature de UI tem
120
- * tela —, e o legado chega sem problemas: a auditoria o isenta.
121
- *
122
- * @param {object|undefined} screenChecklist - o sinal de `collectScreenChecklist`
123
- * @returns {string|null}
124
- */
54
+ // Exportada porque approve/advance `auto` recusam pela mesma regra. Sem design de tela não conta,
55
+ // e o legado chega sem problemas: a auditoria o isenta. Checklist
56
+ // completo ainda deve o protótipo e os prints 1280/390 (`prototypes`).
125
57
  export function uiuxChecklistPause(screenChecklist) {
126
58
  if (screenChecklist?.mockups !== true) return null;
127
59
  const problems = screenChecklist.problems ?? [];
128
- if (problems.length === 0) return null;
129
- return `checklist de tela incompleto em 1-design/mockups.md: ${firstOf(problems)}`
130
- + ' — volume, paginação, ordenação, filtro, os três estados, mobile e abas por tela'
131
- + ' ("não se aplica" vale, se escrito)';
60
+ if (problems.length > 0) {
61
+ return `checklist de tela incompleto em 1-design/mockups.md: ${firstOf(problems)}`
62
+ + ' — volume, paginação, ordenação, filtro, os três estados, mobile e abas por tela'
63
+ + ' ("não se aplica" vale, se escrito)';
64
+ }
65
+ const prototypes = screenChecklist.prototypes ?? [];
66
+ if (prototypes.length === 0) return null;
67
+ return `protótipo incompleto: ${firstOf(prototypes)}`
68
+ + ' — cada tela aponta o HTML renderizado e deixa os prints de 1280 e 390 ao lado dele';
132
69
  }
133
70
 
134
71
  const ROLE_PERSONA = { verifier: 'morph-verifier', reviewer: 'morph-reviewer', security: 'morph-security' };
135
72
 
136
- /** Por que os relatórios de review seguram o ship, ou null. */
137
73
  export function reviewPause(signal) {
138
74
  const required = signal?.required ?? ['verifier'];
139
75
  const reports = signal?.reports ?? {};
@@ -164,6 +100,7 @@ export function reviewPause(signal) {
164
100
  if (r.verdict !== 'SHIP') return `veredito do ${role} é ${r.verdict}`;
165
101
  }
166
102
  const checks = signal?.checks;
103
+ if (checks?.status === 'not-required') return null;
167
104
  if (!checks || checks.status !== 'ok') {
168
105
  return 'sem `2-plan/checks.json` legível — a cobertura dos cenários pelo verificador não é conferível';
169
106
  }
@@ -190,14 +127,7 @@ function reviewSummary(signal) {
190
127
  return `review: ${verdicts}; ${blockers} bloqueador(es)${checks}`;
191
128
  }
192
129
 
193
- /**
194
- * Modelo do verificador: `opus` quando a feature mexe em prompt/modelo ou
195
- * gera mídia; `sonnet` no resto. Caminho sensível não muda o verificador:
196
- * despacha a persona `security` ({@link securityReviewFor}).
197
- *
198
- * @param {{ promptModelTouched?: boolean, generatesMedia?: boolean }} input
199
- * @returns {{ model: 'opus'|'sonnet', reasons: string[] }}
200
- */
130
+ // Caminho sensível não muda o verificador: despacha a persona `security` (`securityReviewFor`).
201
131
  export function verifierModelFor({ promptModelTouched = false, generatesMedia = false } = {}) {
202
132
  const reasons = [];
203
133
  if (promptModelTouched) reasons.push('prompt/modelo alterado');
@@ -205,13 +135,6 @@ export function verifierModelFor({ promptModelTouched = false, generatesMedia =
205
135
  return { model: reasons.length ? 'opus' : 'sonnet', reasons };
206
136
  }
207
137
 
208
- /**
209
- * A revisão de segurança é exigida quando o diff toca auth, segredo,
210
- * permissão, dado pessoal, pagamento ou migration.
211
- *
212
- * @param {string[]} changedFiles
213
- * @returns {{ required: boolean, reasons: string[] }}
214
- */
215
138
  export function securityReviewFor(changedFiles = []) {
216
139
  const risky = changedFiles.filter((f) => SECURITY_PATH_RE.test(f.replaceAll('\\', '/')));
217
140
  return { required: risky.length > 0, reasons: risky.length ? [`caminho sensível: ${firstOf(risky)}`] : [] };
@@ -224,27 +147,13 @@ const PLAN_CHECKS_REASONS = {
224
147
  stale: '2-plan/checks.json não corresponde ao plan.md atual',
225
148
  };
226
149
 
227
- /**
228
- * Por que o gate do plano não fecha sem pessoa: os checks extraídos são o que
229
- * o apply despacha, o report-check confere e a review cobra — um plano sem
230
- * eles, ou com os de outra versão do plano, aprovaria critérios que ninguém
231
- * extraiu.
232
- * Regra única do gate-check (pausa) e do approve/advance automáticos (recusa).
233
- *
234
- * @param {string|undefined} status - `readPlanChecks(...).status`
235
- * @returns {string|null}
236
- */
150
+ // Regra única do gate-check (pausa) e do approve/advance automáticos (recusa).
237
151
  export function planChecksPause(status) {
238
152
  if (status === undefined || status === 'fresh') return null;
239
153
  const what = PLAN_CHECKS_REASONS[status] ?? `checks.json em estado "${status}"`;
240
154
  return `${what} — rode \`morph-spec plan-check {feature}\` até zero erro antes do gate do plano`;
241
155
  }
242
156
 
243
- /**
244
- * @param {object} config - .morph/config/config.json contents (uses `trust`)
245
- * @param {object} signals
246
- * @returns {{ pause: boolean, reason: string }}
247
- */
248
157
  export function shouldPauseGate(config, signals) {
249
158
  const trust = config?.trust ?? 'auto';
250
159
 
@@ -270,10 +179,7 @@ export function shouldPauseGate(config, signals) {
270
179
  };
271
180
  }
272
181
 
273
- // Verify vermelho no momento do gate = a barreira determinística (build/
274
- // testes/validadores) está falhando — código antes do juiz vale também
275
- // para a decisão de pausa. Só 'fail' pausa: ausência de stamp não é sinal
276
- // (features antigas não têm o campo).
182
+ // Só 'fail' pausa aqui: features antigas não têm o stamp.
277
183
  if (signals.verificationStatus === 'fail') {
278
184
  return {
279
185
  pause: true,
@@ -281,7 +187,7 @@ export function shouldPauseGate(config, signals) {
281
187
  };
282
188
  }
283
189
 
284
- // No ship, ausência de stamp não é "verde": uma feature foi aprovada em campo sem verify nenhum.
190
+ // No ship, ausência de stamp não é verde.
285
191
  if (signals.gate === 'review' && !signals.verificationStatus) {
286
192
  return {
287
193
  pause: true,
@@ -289,16 +195,8 @@ export function shouldPauseGate(config, signals) {
289
195
  };
290
196
  }
291
197
 
292
- // No gate de SHIP apenas: o nó e2e do último verify PULOU. Um skip não
293
- // reprova o rollup, então `verificationStatus` sai 'pass' e o gate ficava
294
- // cego — auto-passando um Gate 3 sobre uma app que ninguém subiu. A razão
295
- // literal decide: dispensa legítima (o projeto não containeriza) passa e
296
- // fica REGISTRADA nos signals; ausência de
297
- // prova (docker indisponível, `--skip-e2e`, razão nenhuma) pausa. É a prosa
298
- // do morph-review virando regra — "docker indisponível não é dispensa" — e
299
- // é o que dispensa o `gate-decision --pause false` manual que ela mandava
300
- // rodar. Só no review: pausar o Gate 1/2 numa máquina sem docker travaria
301
- // trabalho legítimo de planejamento.
198
+ // Skip não reprova o rollup, então só a razão literal separa dispensa de ausência de prova.
199
+ // Só no ship: pausar plan/uiux numa máquina sem docker travaria planejamento legítimo.
302
200
  if (signals.gate === 'review' && signals.e2eStatus === 'skip') {
303
201
  const klass = classifyE2eSkip(signals.e2eReason);
304
202
  if (E2E_SKIP_CLASSES_THAT_PAUSE.includes(klass)) {
@@ -306,13 +204,7 @@ export function shouldPauseGate(config, signals) {
306
204
  }
307
205
  }
308
206
 
309
- // Furo 2: no gate de SHIP (review) apenas, um stamp de verification cujo
310
- // sha diverge do HEAD atual significa que código mudou depois do último
311
- // `morph-spec verify` — o resultado carimbado não cobre mais o que está
312
- // prestes a ser aprovado. Pausa só aqui, nunca em plan/uiux: essa
313
- // é uma regra de ship, não uma regra geral de gate. Stamp AUSENTE não entra
314
- // aqui (verificationStale é fail-open para `false` em collectSignals
315
- // quando não há stamp) — features legadas sem stamp continuam liberadas.
207
+ // Regra de ship, não de gate em geral; stamp ausente é fail-open (`false`) em collectSignals.
316
208
  if (signals.gate === 'review' && signals.verificationStale === true) {
317
209
  return {
318
210
  pause: true,
@@ -325,18 +217,9 @@ export function shouldPauseGate(config, signals) {
325
217
  if (reason) return { pause: true, reason };
326
218
  }
327
219
 
328
- // No gate de SHIP apenas: a feature mexeu num caminho que o PROJETO declarou
329
- // como prompt ou modelo, e não há eval verde carimbado. Prompt e modelo são a
330
- // única parte do produto cujo comportamento não é lido por build nem por teste
331
- // unitário: os dois continuam verdes com o prompt trocado.
332
- //
333
- // DEPOIS dos relatórios de review, de propósito: relatório ausente é o
334
- // defeito mais grave dos dois e é a razão reportada.
335
- //
336
- // PAUSA, jamais bloqueia: `approve review` não ganha precondição nova. Uma
337
- // segunda precondição dura ali tornaria inaprovável, inclusive por decisão
338
- // humana, toda feature legada. Pausa é a severidade certa — o humano lê a
339
- // razão e decide.
220
+ // Prompt e modelo não são lidos por build nem por teste. Depois dos relatórios de review de propósito:
221
+ // relatório ausente é o defeito mais grave. Pausa, nunca bloqueia: bloquear tornaria inaprovável
222
+ // toda feature legada.
340
223
  if (signals.gate === 'review' && (signals.promptModel?.touched?.length ?? 0) > 0) {
341
224
  const touched = signals.promptModel.touched;
342
225
  const where = `${touched.slice(0, 3).join(', ')}${touched.length > 3 ? ` (+${touched.length - 3})` : ''}`;
@@ -358,9 +241,7 @@ export function shouldPauseGate(config, signals) {
358
241
  + ' — rode `morph-spec verify {feature}` (escopo de feature) antes do ship',
359
242
  };
360
243
  }
361
- // 'fail' cai fora: um eval vermelho já avermelha o rollup e a regra de
362
- // `verificationStatus` acima pausou antes daqui. 'pass' com stamp velho
363
- // também já pausou, por `verificationStale`.
244
+ // 'fail' já pausou por `verificationStatus`; 'pass' com stamp velho, por `verificationStale`.
364
245
  }
365
246
 
366
247
  // Feature que gera mídia: custo, revisão humana, marcação e hot path só valem provados na seção.
@@ -375,19 +256,13 @@ export function shouldPauseGate(config, signals) {
375
256
  };
376
257
  }
377
258
 
378
- // No fecho do DESIGN: o mockup declara telas sem o checklist de tela
379
- // completo. É ele que faz paginação, ordenação, filtro e os três estados
380
- // virarem cenário do plano — sem ele, uma lista de 2 000 linhas é aprovada
381
- // listando tudo, e o implementador entrega exatamente o cenário.
259
+ // Sem o checklist de tela completo, paginação, ordenação, filtro e os três estados não viram cenário.
382
260
  if (signals.gate === 'uiux') {
383
261
  const reason = uiuxChecklistPause(signals.screenChecklist);
384
262
  if (reason) return { pause: true, reason };
385
263
  }
386
264
 
387
- // No SHIP, depois da cláusula de mídia e pela mesma disciplina: o design
388
- // decidiu itens de tela e o verificador não registrou a conferência deles
389
- // contra a tela entregue. Cobra-se o item DEVIDO (`uncovered`): tela toda
390
- // "não se aplica", mockup legado ou ausente não devem nada.
265
+ // Cobra-se só o item devido (`uncovered`): tela toda "não se aplica", design legado ou ausente não devem nada.
391
266
  if (signals.gate === 'review' && (signals.screenChecklist?.uncovered?.length ?? 0) > 0) {
392
267
  const { section, uncovered } = signals.screenChecklist;
393
268
  if (section !== true) {
@@ -420,10 +295,6 @@ export function shouldPauseGate(config, signals) {
420
295
  return { pause: false, reason: '' };
421
296
  }
422
297
 
423
- /**
424
- * Human-readable list of risk signals present (for logging/Gate summary).
425
- * @returns {string[]}
426
- */
427
298
  export function getRiskSignals(signals) {
428
299
  const out = [];
429
300
  if (FORCED_PAUSE_WORK_TYPES.includes(signals.workType)) out.push('hotfix (gate humano obrigatório)');
@@ -432,8 +303,7 @@ export function getRiskSignals(signals) {
432
303
  out.push(`standards violations: ${signals.standardsViolations.join(', ')}`);
433
304
  }
434
305
  if (signals.verificationStatus === 'fail') out.push('verify vermelho');
435
- // Listado mesmo quando NÃO pausa: uma dispensa que passa silenciosamente é
436
- // indistinguível de uma omissão. A classe diz de quem é a ausência de prova.
306
+ // Listado mesmo quando não pausa: dispensa silenciosa é indistinguível de omissão.
437
307
  if (signals.gate === 'review' && signals.e2eStatus === 'skip') {
438
308
  const klass = classifyE2eSkip(signals.e2eReason);
439
309
  const literal = typeof signals.e2eReason === 'string' && signals.e2eReason.trim() !== ''
@@ -445,9 +315,7 @@ export function getRiskSignals(signals) {
445
315
  out.push('verification stamp desatualizado (stale) no ship');
446
316
  }
447
317
  if (signals.gate === 'review') out.push(reviewSummary(signals.reviewReports));
448
- // Listado mesmo quando NÃO pausa, pela mesma razão do e2e: um projeto que não
449
- // declara caminho de prompt/modelo é indistinguível, no relatório, de um que
450
- // declara e não tocou em nada — e as duas coisas significam coisas diferentes.
318
+ // Listado mesmo quando não pausa, pela mesma razão do e2e.
451
319
  if (signals.gate === 'review' && signals.promptModel) {
452
320
  const { declared, touched } = signals.promptModel;
453
321
  if (!declared) {
@@ -462,17 +330,12 @@ export function getRiskSignals(signals) {
462
330
  if (typeof signals.sliceCount === 'number' && signals.sliceCount > MAX_SLICES) {
463
331
  out.push(`${signals.sliceCount} fatias (acima de ${MAX_SLICES})`);
464
332
  }
465
- // Listado mesmo quando NÃO pausa, pela mesma razão do sinal irmão: a isenção
466
- // de uma feature que não gera mídia tem de ser VISÍVEL, senão é
467
- // indistinguível de uma omissão.
468
333
  if (signals.gate === 'review' && signals.mediaClaims?.featureGeneratesMedia === true) {
469
334
  out.push(signals.mediaClaims.section === true
470
335
  ? 'prova de mídia registrada'
471
336
  : 'verifier.md sem a seção `## Mídia gerada` (prova de mídia ausente)');
472
337
  }
473
338
 
474
- // Listado mesmo quando NÃO pausa: a isenção (sem mockup, legado, nada
475
- // devido) e o checklist conferido precisam ser distinguíveis de uma omissão.
476
339
  if ((signals.gate === 'uiux' || signals.gate === 'review') && signals.screenChecklist) {
477
340
  const { mockups, legacy, exempt, screens = [], problems = [], section, uncovered = [] } = signals.screenChecklist;
478
341
  if (!mockups) {
@@ -483,13 +346,18 @@ export function getRiskSignals(signals) {
483
346
  out.push(problems.length > 0
484
347
  ? `checklist de tela incompleto (${problems.length} problema(s))`
485
348
  : `checklist de tela completo (${screens.length} tela(s))`);
349
+ const prototypes = signals.screenChecklist.prototypes ?? [];
350
+ if (screens.length > 0) {
351
+ out.push(prototypes.length > 0
352
+ ? `protótipo incompleto em ${prototypes.length} tela(s)`
353
+ : `protótipo e prints 1280/390 presentes (${screens.length} tela(s))`);
354
+ }
486
355
  } else if (uncovered.length > 0) {
487
356
  out.push(section !== true
488
357
  ? 'verifier.md sem a seção `## Checklist de tela`'
489
358
  : `checklist de tela sem conferência em ${uncovered.length} tela(s)`);
490
359
  } else if (screens.length === 0) {
491
- // O design passou pela uiux sem tela nenhuma: nada a cobrar no ship, mas
492
- // não pode ler igual a uma tela toda "não se aplica".
360
+ // Sem tela nenhuma: nada a cobrar no ship, mas não pode ler igual a uma tela toda "não se aplica".
493
361
  out.push(`checklist de tela: ${firstOf(problems)}`);
494
362
  } else if (section === true) {
495
363
  out.push(`checklist de tela conferido (${screens.length} tela(s))`);
@@ -1,12 +1,4 @@
1
- /**
2
- * The index of the ADRs that still hold across the project:
3
- * `.morph/specs/adrs/`, one file per ADR, maintained by finish/archive alone,
4
- * inside the archive commit, from the closing feature's `2-plan/plan.md`:
5
- * - enters: an ADR of `## Decisões` with `**Alcance:** projeto`;
6
- * - leaves: an ADR its `### Conflitos com o acervo` revokes in full (file deleted);
7
- * - narrows: one it revokes in part keeps its file, with `scope:` where it still holds.
8
- * One file per ADR so parallel features that index different ADRs merge cleanly.
9
- */
1
+ // One file per ADR under `.morph/specs/adrs/`, so parallel features that index different ADRs merge cleanly.
10
2
 
11
3
  import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
12
4
  import { basename, join } from 'path';
@@ -48,11 +40,7 @@ function renderAdrFile({ ref, title, body }, feature) {
48
40
  ].join('\n');
49
41
  }
50
42
 
51
- /**
52
- * The ADRs the index holds; `[]` when there is none or it cannot be read.
53
- *
54
- * @returns {Array<{ ref: string, file: string, title: string|null, scope: string|null }>}
55
- */
43
+ /** `[]` when there is no index or it cannot be read. */
56
44
  export function readAdrIndex(cwd) {
57
45
  const dir = join(cwd, ADR_INDEX_PATHSPEC);
58
46
  try {
@@ -61,7 +49,7 @@ export function readAdrIndex(cwd) {
61
49
  const ref = refOf(file);
62
50
  if (!ref) return [];
63
51
  let content = '';
64
- try { content = readFileSync(join(dir, file), 'utf8'); } catch { /* listed, unreadable */ }
52
+ try { content = readFileSync(join(dir, file), 'utf8'); } catch {}
65
53
  return [{ ref, file, title: readFrontmatterField(content, 'title'), scope: readFrontmatterField(content, 'scope') }];
66
54
  });
67
55
  } catch {
@@ -69,16 +57,7 @@ export function readAdrIndex(cwd) {
69
57
  }
70
58
  }
71
59
 
72
- /**
73
- * Apply a closing feature's plan to the index: its project-wide ADRs enter, and
74
- * the ADRs its conflicts table revokes are deleted (total) or narrowed (parcial).
75
- * An unreadable or absent plan touches nothing.
76
- *
77
- * @param {string} cwd
78
- * @param {string} featureDir - the feature folder (already archived, at close time)
79
- * @returns {{ changed: boolean, added: string[], revoked: string[], narrowed: string[],
80
- * problems: Array<{ id: string|null, reason: string, message: string }> }}
81
- */
60
+ /** An unreadable or absent plan touches nothing. */
82
61
  export function updateAdrIndex(cwd, featureDir) {
83
62
  const result = { changed: false, added: [], revoked: [], narrowed: [], problems: [] };
84
63
  const feature = basename(featureDir);
@@ -88,7 +67,7 @@ export function updateAdrIndex(cwd, featureDir) {
88
67
  try {
89
68
  const planPath = join(featureDir, PLAN.phaseDir, PLAN.filename);
90
69
  if (existsSync(planPath)) plan = parsePlan(readFileSync(planPath, 'utf8'));
91
- } catch { /* unreadable plan.md — nothing enters or leaves */ }
70
+ } catch {}
92
71
  if (!plan) return result;
93
72
 
94
73
  for (const { id, title, body } of plan.adrs.filter((a) => a.scope === 'projeto')) {
@@ -128,14 +107,7 @@ export function updateAdrIndex(cwd, featureDir) {
128
107
  return result;
129
108
  }
130
109
 
131
- /**
132
- * What the finish would fail to apply from a plan's revocation table, reported
133
- * while the plan can still change.
134
- *
135
- * @param {string} cwd
136
- * @param {ReturnType<typeof parsePlan>} plan
137
- * @returns {string[]}
138
- */
110
+ /** What the finish would fail to apply from the revocation table, reported while the plan can still change. */
139
111
  export function revocationWarnings(cwd, plan) {
140
112
  const { entries, problems } = parseRevocations(plan.conflicts?.text);
141
113
  const warnings = problems.map((p) => `### Conflitos com o acervo: ${p.message}`);
@@ -1,15 +1,6 @@
1
1
  const FRONTMATTER_RE = /^(?---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))/;
2
2
 
3
- /**
4
- * Set (or replace) a single top-level frontmatter field, preserving every
5
- * other line, the original EOL style, and anything after the closing `---`.
6
- * A document with no frontmatter block is returned unchanged.
7
- *
8
- * @param {string} content
9
- * @param {string} key
10
- * @param {string} value - written as-is; quote it first when it is free text
11
- * @returns {string}
12
- */
3
+ /** Preserves every other line, the EOL style and the body; no frontmatter block → unchanged. `value` is written as-is. */
13
4
  export function setFrontmatterField(content, key, value) {
14
5
  const m = FRONTMATTER_RE.exec(content);
15
6
  if (!m || m.index !== 0) return content;
@@ -25,11 +16,7 @@ export function setFrontmatterField(content, key, value) {
25
16
  return open + next + close + content.slice(full.length);
26
17
  }
27
18
 
28
- /**
29
- * Read a single top-level frontmatter field; `null` when absent or there is no
30
- * frontmatter block. A double-quoted value is read as the JSON string it is
31
- * written as; a single-quoted one as YAML reads it (`''` is a quote).
32
- */
19
+ /** `null` when absent; double-quoted values read as JSON strings, single-quoted as YAML (`''` is a quote). */
33
20
  export function readFrontmatterField(content, key) {
34
21
  const m = FRONTMATTER_RE.exec(content);
35
22
  if (!m || m.index !== 0) return null;
@@ -4,13 +4,7 @@ export const SEPARATOR_ROW_RE = /^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)*\|?\s
4
4
  /** A line that belongs to a table: it carries an unescaped pipe (GFM does not require the leading one). */
5
5
  export const isTableLine = (line) => /(?<!\\)\|/.test(line);
6
6
 
7
- /**
8
- * Split a table row on unescaped pipes, outer pipes optional; `\|` inside a
9
- * cell is a literal pipe.
10
- *
11
- * @param {string} row
12
- * @returns {string[]} trimmed cells
13
- */
7
+ /** Splits on unescaped pipes, outer pipes optional; `\|` inside a cell is a literal pipe. */
14
8
  export function tableCells(row) {
15
9
  const inner = row.trim().replace(/^\|/, '').replace(/(?<!\\)\|$/, '');
16
10
  return inner.split(/(?<!\\)\|/).map((c) => c.replace(/\\\|/g, '|').trim());
@@ -1,10 +1,4 @@
1
- /**
2
- * The ADRs of the project index that a plan revokes: the table under
3
- * `### Conflitos com o acervo` whose first header cell is `ADR`, with columns
4
- * `{feature}/ADR-{nnn}`, `total|parcial` and the scope where the old one still holds.
5
- * plan-check reports the problems while the plan can still change (it freezes
6
- * at the plan gate); nothing ambiguous reaches the finish.
7
- */
1
+ // plan-check reports the problems while the plan can still change, so nothing ambiguous reaches the finish.
8
2
 
9
3
  import { SEPARATOR_ROW_RE, isTableLine, tableCells } from './gfm-table.js';
10
4
 
@@ -46,13 +40,6 @@ function readRow(line) {
46
40
  return { entry: problems.length ? { id } : { id, change, scope }, problems };
47
41
  }
48
42
 
49
- /**
50
- * @param {string|null} conflicts - the text of `### Conflitos com o acervo` (`parsePlan(...).conflicts.text`)
51
- * @returns {{
52
- * entries: Array<{ id: string, change: 'total'|'parcial', scope: string|null }>,
53
- * problems: Array<{ id: string|null, reason: string, message: string }>
54
- * }}
55
- */
56
43
  export function parseRevocations(conflicts) {
57
44
  const problems = [];
58
45
  const rows = [];