@polymorphism-tech/morph-spec 9.0.0 → 9.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +4 -62
  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 +1 -1
  9. package/framework/MORPH.md +1 -1
  10. package/framework/agents.json +12 -4
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +6 -17
  14. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  15. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  16. package/framework/hooks/claude-code/core/chain.js +4 -65
  17. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  18. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  19. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  20. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  21. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  22. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  23. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  24. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  25. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  26. package/framework/hooks/claude-code/core/state-sync.js +11 -67
  27. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  28. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  29. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  30. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  31. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  32. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  33. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  34. package/framework/hooks/shared/activity-logger.js +9 -89
  35. package/framework/hooks/shared/agent-reap.js +2 -17
  36. package/framework/hooks/shared/build-check.js +6 -75
  37. package/framework/hooks/shared/claim-registry.js +9 -119
  38. package/framework/hooks/shared/command-tokens.js +6 -51
  39. package/framework/hooks/shared/comment-rules.js +5 -20
  40. package/framework/hooks/shared/comment-scanner.js +6 -25
  41. package/framework/hooks/shared/comment-stripper.js +11 -85
  42. package/framework/hooks/shared/e2e-check.js +0 -0
  43. package/framework/hooks/shared/errata.js +2 -15
  44. package/framework/hooks/shared/feature-locator.js +11 -95
  45. package/framework/hooks/shared/frontend-locations.js +1 -3
  46. package/framework/hooks/shared/gate-question.js +1 -7
  47. package/framework/hooks/shared/git-exclude.js +3 -20
  48. package/framework/hooks/shared/git-paths.js +8 -104
  49. package/framework/hooks/shared/graph-freshness.js +22 -166
  50. package/framework/hooks/shared/graph-refresh.js +21 -149
  51. package/framework/hooks/shared/heavy-command.js +4 -12
  52. package/framework/hooks/shared/hook-response.js +8 -83
  53. package/framework/hooks/shared/morph-cli.js +8 -52
  54. package/framework/hooks/shared/payload-utils.js +10 -132
  55. package/framework/hooks/shared/phase-utils.js +1 -42
  56. package/framework/hooks/shared/redact-secrets.js +4 -15
  57. package/framework/hooks/shared/reparse-scan.js +6 -66
  58. package/framework/hooks/shared/slice-progress.js +4 -23
  59. package/framework/hooks/shared/source-files.js +4 -17
  60. package/framework/hooks/shared/state-reader.js +13 -183
  61. package/framework/hooks/shared/state-writer.js +8 -52
  62. package/framework/hooks/shared/stdin-reader.js +1 -11
  63. package/framework/hooks/shared/telemetry-logger.js +7 -67
  64. package/framework/hooks/shared/transcript-usage.js +7 -29
  65. package/framework/hooks/shared/worktree-root.js +7 -29
  66. package/framework/rules/csharp-standards.md +1 -1
  67. package/framework/rules/frontend-standards.md +1 -0
  68. package/framework/rules/hangfire-standards.md +1 -1
  69. package/framework/rules/neon-standards.md +1 -1
  70. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  71. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  72. package/framework/skills/core/morph-apply/SKILL.md +2 -2
  73. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  74. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  75. package/framework/skills/core/morph-media/SKILL.md +1 -1
  76. package/framework/skills/core/morph-plan/SKILL.md +2 -2
  77. package/framework/skills/core/morph-review/SKILL.md +1 -1
  78. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  79. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  80. package/framework/standards/STANDARDS.json +34 -12
  81. package/framework/standards/STANDARDS.meta.json +16 -2
  82. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  83. package/framework/standards/ai-agents/agent-session.md +1 -1
  84. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  85. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  86. package/framework/standards/ai-agents/guardrails.md +1 -1
  87. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  88. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  89. package/framework/standards/ai-agents/production.md +1 -1
  90. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  91. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  92. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  93. package/framework/standards/ai-agents/setup.md +2 -2
  94. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  95. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  96. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  97. package/framework/standards/ai-agents/workflows.md +1 -1
  98. package/framework/standards/architecture/code-comments.md +1 -1
  99. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  100. package/framework/standards/backend/api/validation.md +1 -1
  101. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  102. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  103. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  104. package/framework/standards/data/nosql/cache/redis.md +1 -1
  105. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  106. package/framework/standards/frontend/admin-app/forms.md +1 -1
  107. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  108. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  109. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  110. package/framework/templates/CLAUDE.md.core.template +3 -3
  111. package/framework/templates/REGISTRY.json +9 -9
  112. package/framework/templates/docs/ui-mockups.md +4 -0
  113. package/package.json +1 -1
  114. package/src/commands/context/context-refresh.js +0 -16
  115. package/src/commands/dashboard/dashboard.js +2 -8
  116. package/src/commands/e2e/e2e.js +17 -104
  117. package/src/commands/graph/affected.js +0 -14
  118. package/src/commands/graph/build.js +11 -50
  119. package/src/commands/graph/explain.js +0 -16
  120. package/src/commands/graph/status.js +1 -12
  121. package/src/commands/mcp/mcp-setup.js +4 -62
  122. package/src/commands/observability/cost.js +5 -57
  123. package/src/commands/observability/telemetry.js +1 -14
  124. package/src/commands/project/branch.js +4 -45
  125. package/src/commands/project/claim.js +4 -48
  126. package/src/commands/project/doctor.js +48 -302
  127. package/src/commands/project/env.js +1 -27
  128. package/src/commands/project/fleet.js +3 -32
  129. package/src/commands/project/init.js +4 -68
  130. package/src/commands/project/install-plugin-cmd.js +0 -1
  131. package/src/commands/project/standards.js +1 -62
  132. package/src/commands/project/status.js +0 -53
  133. package/src/commands/project/update.js +31 -239
  134. package/src/commands/project/worktree.js +108 -882
  135. package/src/commands/scaffold/scaffold.js +0 -25
  136. package/src/commands/state/advance.js +14 -85
  137. package/src/commands/state/approve.js +14 -96
  138. package/src/commands/state/archive.js +12 -64
  139. package/src/commands/state/auto-approval.js +2 -5
  140. package/src/commands/state/create.js +22 -109
  141. package/src/commands/state/delete.js +11 -63
  142. package/src/commands/state/finish.js +98 -475
  143. package/src/commands/state/gate-check.js +22 -132
  144. package/src/commands/state/gate-decision.js +3 -39
  145. package/src/commands/state/handoff.js +3 -30
  146. package/src/commands/state/list.js +14 -84
  147. package/src/commands/state/report-check.js +8 -58
  148. package/src/commands/state/retype.js +4 -26
  149. package/src/commands/validation/index.js +0 -3
  150. package/src/commands/validation/mutate.js +2 -5
  151. package/src/commands/validation/validate-feature.js +5 -94
  152. package/src/commands/validation/verify.js +15 -98
  153. package/src/core/paths/output-schema.js +2 -101
  154. package/src/core/state/feature-identity.js +1 -25
  155. package/src/core/state/gate-order.js +0 -27
  156. package/src/core/state/lifecycle-audit.js +8 -100
  157. package/src/core/state/state-manager.js +22 -304
  158. package/src/core/templates/template-registry.js +2 -97
  159. package/src/core/templates/template-renderer.js +13 -93
  160. package/src/core/templates/template-seed.js +0 -1
  161. package/src/core/work-types.js +1 -36
  162. package/src/dashboard/app/App.jsx +1 -8
  163. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  164. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  165. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  166. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  167. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  168. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  169. package/src/dashboard/app/components/Markdown.jsx +0 -9
  170. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  171. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  172. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  173. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  174. package/src/dashboard/app/hooks/usePref.js +1 -5
  175. package/src/dashboard/app/lib/format.js +0 -2
  176. package/src/dashboard/app/lib/phases.js +1 -5
  177. package/src/dashboard/app/lib/project.js +0 -12
  178. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  179. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  180. package/src/dashboard/app/pages/Features.jsx +1 -8
  181. package/src/dashboard/app/pages/Guide.jsx +0 -1
  182. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  183. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  184. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  185. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  186. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  187. package/src/dashboard/app/pages/Standards.jsx +2 -17
  188. package/src/dashboard/scanner/activity-scanner.js +10 -69
  189. package/src/dashboard/scanner/agents-scanner.js +1 -23
  190. package/src/dashboard/scanner/docs-scanner.js +0 -6
  191. package/src/dashboard/scanner/feature-status.js +2 -5
  192. package/src/dashboard/scanner/features-scanner.js +10 -66
  193. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  194. package/src/dashboard/scanner/index.js +0 -4
  195. package/src/dashboard/scanner/portfolio.js +5 -29
  196. package/src/dashboard/scanner/project-api.js +8 -54
  197. package/src/dashboard/scanner/rules-scanner.js +1 -2
  198. package/src/dashboard/scanner/skills-scanner.js +2 -12
  199. package/src/dashboard/scanner/standards-scanner.js +2 -6
  200. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  201. package/src/lib/ai/ai-checks.js +7 -52
  202. package/src/lib/ai/ai-project-scan.js +14 -108
  203. package/src/lib/ai/code-audit.js +7 -63
  204. package/src/lib/ai/pin-drift.js +8 -51
  205. package/src/lib/ai/registry-audit.js +11 -90
  206. package/src/lib/ci/ci-verify.js +4 -19
  207. package/src/lib/ci/github-ci.js +9 -37
  208. package/src/lib/ci/workflow-scaffold.js +2 -10
  209. package/src/lib/detectors/claude-config-detector.js +13 -74
  210. package/src/lib/detectors/index.js +4 -50
  211. package/src/lib/e2e/compose-env.js +5 -66
  212. package/src/lib/e2e/stack-teardown.js +6 -40
  213. package/src/lib/generators/architecture-scanner.js +18 -156
  214. package/src/lib/generators/handoff-generator.js +9 -53
  215. package/src/lib/generators/scaffold-generator.js +5 -101
  216. package/src/lib/generators/settings-generator.js +4 -37
  217. package/src/lib/git/branch-classifier.js +11 -84
  218. package/src/lib/git/default-branch.js +14 -169
  219. package/src/lib/git/evidence-staleness.js +1 -0
  220. package/src/lib/git/gh.js +2 -17
  221. package/src/lib/git/pending-prs.js +6 -37
  222. package/src/lib/git/pr-files.js +3 -28
  223. package/src/lib/git/pr-index.js +3 -34
  224. package/src/lib/git/tree-fingerprint.js +3 -15
  225. package/src/lib/git/tree-state.js +6 -33
  226. package/src/lib/graph/god-nodes.js +2 -22
  227. package/src/lib/graph/graph-health.js +3 -24
  228. package/src/lib/graph/graph-loader.js +5 -56
  229. package/src/lib/graph/graphify-runtime.js +7 -37
  230. package/src/lib/graph/graphifyignore.js +8 -74
  231. package/src/lib/graph/sanitize.js +16 -136
  232. package/src/lib/heavy/cleanup.js +1 -1
  233. package/src/lib/installers/effects.js +8 -93
  234. package/src/lib/installers/mcp-installer.js +9 -169
  235. package/src/lib/installers/memory-installer.js +4 -27
  236. package/src/lib/installers/templates-reconcile.js +5 -60
  237. package/src/lib/mutation/dotnet-introspection.js +20 -106
  238. package/src/lib/mutation/subset.js +1 -4
  239. package/src/lib/plan/artifact-vocabulary.js +5 -25
  240. package/src/lib/plan/checks.js +5 -31
  241. package/src/lib/plan/plan-parser.js +1 -6
  242. package/src/lib/projects/registry.js +7 -44
  243. package/src/lib/review-reports.js +5 -23
  244. package/src/lib/risk-detector.js +39 -173
  245. package/src/lib/specs/adr-index.js +6 -34
  246. package/src/lib/specs/frontmatter.js +2 -15
  247. package/src/lib/specs/gfm-table.js +1 -7
  248. package/src/lib/specs/revocations.js +1 -14
  249. package/src/lib/specs/screen-checklist.js +47 -124
  250. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  251. package/src/lib/stack/stack-profile.js +1 -28
  252. package/src/lib/stack/stack-scan.js +1 -8
  253. package/src/lib/stack-filter.js +4 -27
  254. package/src/lib/standards/registry.js +1 -7
  255. package/src/lib/standards/sections.js +2 -13
  256. package/src/lib/standards/verified-against.js +19 -191
  257. package/src/lib/tasks/eval-node.js +21 -169
  258. package/src/lib/tasks/linux-runner.js +18 -142
  259. package/src/lib/tasks/test-nodes.js +43 -240
  260. package/src/lib/tasks/test-runner.js +43 -341
  261. package/src/lib/telemetry/metrics.js +7 -36
  262. package/src/lib/telemetry/telemetry-reader.js +13 -125
  263. package/src/lib/telemetry/verify-event.js +2 -30
  264. package/src/lib/validators/code-budget/code-budget-validator.js +3 -43
  265. package/src/lib/validators/code-budget/index.js +0 -7
  266. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  267. package/src/lib/validators/contracts/index.js +0 -6
  268. package/src/lib/validators/nextjs/index.js +0 -5
  269. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  270. package/src/lib/validators/packages/index.js +0 -6
  271. package/src/lib/validators/packages/package-validator.js +6 -56
  272. package/src/lib/validators/reuse/index.js +0 -8
  273. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  274. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  275. package/src/lib/validators/traceability-check.js +5 -63
  276. package/src/lib/validators/validation-runner.js +20 -125
  277. package/src/lib/validators/verify-runner.js +52 -358
  278. package/src/lib/work-type-detector.js +5 -67
  279. package/src/lib/worktree/docker-audit.js +17 -136
  280. package/src/lib/worktree/env-allocator.js +10 -98
  281. package/src/lib/worktree/feature-truth.js +12 -115
  282. package/src/lib/worktree/finish-backup.js +32 -242
  283. package/src/lib/worktree/fleet-scanner.js +13 -95
  284. package/src/lib/worktree/ignored-triage.js +15 -91
  285. package/src/lib/worktree/vscode-settings.js +2 -3
  286. package/src/lib/worktree/worktree-audit.js +21 -213
  287. package/src/lib/worktree/worktree-include.js +3 -5
  288. package/src/scripts/global-install.js +3 -9
  289. package/src/scripts/install-plugin.js +1 -13
  290. package/src/scripts/setup-infra.js +17 -147
  291. package/src/utils/agents-installer.js +28 -275
  292. package/src/utils/banner.js +4 -25
  293. package/src/utils/claude-md-injector.js +1 -37
  294. package/src/utils/claude-settings-manager.js +1 -52
  295. package/src/utils/file-copier.js +11 -95
  296. package/src/utils/hooks-installer.js +21 -298
  297. package/src/utils/skills-installer.js +4 -101
  298. package/src/utils/version-checker.js +4 -83
  299. package/framework/commands/morph-proposal.md +0 -9
  300. package/src/commands/utils/index.js +0 -3
  301. package/src/lib/detectors/design-system-detector.js +0 -188
  302. package/src/lib/generators/design-system-generator.js +0 -508
  303. package/src/lib/specs/old-format.js +0 -68
  304. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,81 +1,23 @@
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'],
54
14
  ];
55
15
 
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.
16
+ // `unknown` (razão vazia) pausa de propósito: dispensa sem motivo é o no-op silencioso que a regra elimina.
60
17
  const E2E_SKIP_CLASSES_THAT_PAUSE = ['environment', 'requested', 'unknown'];
61
18
 
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
- */
19
+ // `project` diz que o repositório não tem runtime (dispensa legítima); `environment`/`requested` falam
20
+ // da máquina ou do operador, nunca do código. Razão vazia é `unknown`, jamais dispensa.
79
21
  export function classifyE2eSkip(reason) {
80
22
  const text = typeof reason === 'string' ? reason.trim() : '';
81
23
  if (text === '') return 'unknown';
@@ -85,7 +27,6 @@ export function classifyE2eSkip(reason) {
85
27
  return 'unknown';
86
28
  }
87
29
 
88
- /** Mensagem de pausa por classe — nomeia SEMPRE a razão literal do verify. */
89
30
  function e2ePauseReason(klass, reason) {
90
31
  const literal = typeof reason === 'string' && reason.trim() !== '' ? reason.trim() : null;
91
32
  if (klass === 'environment') {
@@ -102,38 +43,32 @@ function e2ePauseReason(klass, reason) {
102
43
  return `e2e pulado por razão não classificada: "${literal}" — sem classificação, não há dispensa`;
103
44
  }
104
45
 
105
- /** A razão gravada no sinal quando o checklist de tela isenta um mockup legado. */
106
46
  export const LEGACY_MOCKUP_EXEMPTION = 'checklist dispensado: mockup no formato antigo';
107
47
 
108
- /** Os dois primeiros itens de uma lista, e quantos ficaram de fora. */
109
48
  function firstOf(items) {
110
49
  const shown = items.slice(0, 2).join('; ');
111
50
  return items.length > 2 ? `${shown} (+${items.length - 2})` : shown;
112
51
  }
113
52
 
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
- */
53
+ // Exportada porque approve/advance `auto` recusam pela mesma regra. Sem design de tela não conta,
54
+ // e o legado chega sem problemas: a auditoria o isenta. Checklist
55
+ // completo ainda deve o protótipo e os prints 1280/390 (`prototypes`).
125
56
  export function uiuxChecklistPause(screenChecklist) {
126
57
  if (screenChecklist?.mockups !== true) return null;
127
58
  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)';
59
+ if (problems.length > 0) {
60
+ return `checklist de tela incompleto em 1-design/mockups.md: ${firstOf(problems)}`
61
+ + ' — volume, paginação, ordenação, filtro, os três estados, mobile e abas por tela'
62
+ + ' ("não se aplica" vale, se escrito)';
63
+ }
64
+ const prototypes = screenChecklist.prototypes ?? [];
65
+ if (prototypes.length === 0) return null;
66
+ return `protótipo incompleto: ${firstOf(prototypes)}`
67
+ + ' — cada tela aponta o HTML renderizado e deixa os prints de 1280 e 390 ao lado dele';
132
68
  }
133
69
 
134
70
  const ROLE_PERSONA = { verifier: 'morph-verifier', reviewer: 'morph-reviewer', security: 'morph-security' };
135
71
 
136
- /** Por que os relatórios de review seguram o ship, ou null. */
137
72
  export function reviewPause(signal) {
138
73
  const required = signal?.required ?? ['verifier'];
139
74
  const reports = signal?.reports ?? {};
@@ -190,14 +125,7 @@ function reviewSummary(signal) {
190
125
  return `review: ${verdicts}; ${blockers} bloqueador(es)${checks}`;
191
126
  }
192
127
 
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
- */
128
+ // Caminho sensível não muda o verificador: despacha a persona `security` (`securityReviewFor`).
201
129
  export function verifierModelFor({ promptModelTouched = false, generatesMedia = false } = {}) {
202
130
  const reasons = [];
203
131
  if (promptModelTouched) reasons.push('prompt/modelo alterado');
@@ -205,13 +133,6 @@ export function verifierModelFor({ promptModelTouched = false, generatesMedia =
205
133
  return { model: reasons.length ? 'opus' : 'sonnet', reasons };
206
134
  }
207
135
 
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
136
  export function securityReviewFor(changedFiles = []) {
216
137
  const risky = changedFiles.filter((f) => SECURITY_PATH_RE.test(f.replaceAll('\\', '/')));
217
138
  return { required: risky.length > 0, reasons: risky.length ? [`caminho sensível: ${firstOf(risky)}`] : [] };
@@ -224,27 +145,13 @@ const PLAN_CHECKS_REASONS = {
224
145
  stale: '2-plan/checks.json não corresponde ao plan.md atual',
225
146
  };
226
147
 
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
- */
148
+ // Regra única do gate-check (pausa) e do approve/advance automáticos (recusa).
237
149
  export function planChecksPause(status) {
238
150
  if (status === undefined || status === 'fresh') return null;
239
151
  const what = PLAN_CHECKS_REASONS[status] ?? `checks.json em estado "${status}"`;
240
152
  return `${what} — rode \`morph-spec plan-check {feature}\` até zero erro antes do gate do plano`;
241
153
  }
242
154
 
243
- /**
244
- * @param {object} config - .morph/config/config.json contents (uses `trust`)
245
- * @param {object} signals
246
- * @returns {{ pause: boolean, reason: string }}
247
- */
248
155
  export function shouldPauseGate(config, signals) {
249
156
  const trust = config?.trust ?? 'auto';
250
157
 
@@ -270,10 +177,7 @@ export function shouldPauseGate(config, signals) {
270
177
  };
271
178
  }
272
179
 
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).
180
+ // Só 'fail' pausa aqui: features antigas não têm o stamp.
277
181
  if (signals.verificationStatus === 'fail') {
278
182
  return {
279
183
  pause: true,
@@ -281,7 +185,7 @@ export function shouldPauseGate(config, signals) {
281
185
  };
282
186
  }
283
187
 
284
- // No ship, ausência de stamp não é "verde": uma feature foi aprovada em campo sem verify nenhum.
188
+ // No ship, ausência de stamp não é verde.
285
189
  if (signals.gate === 'review' && !signals.verificationStatus) {
286
190
  return {
287
191
  pause: true,
@@ -289,16 +193,8 @@ export function shouldPauseGate(config, signals) {
289
193
  };
290
194
  }
291
195
 
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.
196
+ // Skip não reprova o rollup, então só a razão literal separa dispensa de ausência de prova.
197
+ // Só no ship: pausar plan/uiux numa máquina sem docker travaria planejamento legítimo.
302
198
  if (signals.gate === 'review' && signals.e2eStatus === 'skip') {
303
199
  const klass = classifyE2eSkip(signals.e2eReason);
304
200
  if (E2E_SKIP_CLASSES_THAT_PAUSE.includes(klass)) {
@@ -306,13 +202,7 @@ export function shouldPauseGate(config, signals) {
306
202
  }
307
203
  }
308
204
 
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.
205
+ // Regra de ship, não de gate em geral; stamp ausente é fail-open (`false`) em collectSignals.
316
206
  if (signals.gate === 'review' && signals.verificationStale === true) {
317
207
  return {
318
208
  pause: true,
@@ -325,18 +215,9 @@ export function shouldPauseGate(config, signals) {
325
215
  if (reason) return { pause: true, reason };
326
216
  }
327
217
 
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.
218
+ // Prompt e modelo não são lidos por build nem por teste. Depois dos relatórios de review de propósito:
219
+ // relatório ausente é o defeito mais grave. Pausa, nunca bloqueia: bloquear tornaria inaprovável
220
+ // toda feature legada.
340
221
  if (signals.gate === 'review' && (signals.promptModel?.touched?.length ?? 0) > 0) {
341
222
  const touched = signals.promptModel.touched;
342
223
  const where = `${touched.slice(0, 3).join(', ')}${touched.length > 3 ? ` (+${touched.length - 3})` : ''}`;
@@ -358,9 +239,7 @@ export function shouldPauseGate(config, signals) {
358
239
  + ' — rode `morph-spec verify {feature}` (escopo de feature) antes do ship',
359
240
  };
360
241
  }
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`.
242
+ // 'fail' já pausou por `verificationStatus`; 'pass' com stamp velho, por `verificationStale`.
364
243
  }
365
244
 
366
245
  // Feature que gera mídia: custo, revisão humana, marcação e hot path só valem provados na seção.
@@ -375,19 +254,13 @@ export function shouldPauseGate(config, signals) {
375
254
  };
376
255
  }
377
256
 
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.
257
+ // Sem o checklist de tela completo, paginação, ordenação, filtro e os três estados não viram cenário.
382
258
  if (signals.gate === 'uiux') {
383
259
  const reason = uiuxChecklistPause(signals.screenChecklist);
384
260
  if (reason) return { pause: true, reason };
385
261
  }
386
262
 
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.
263
+ // Cobra-se só o item devido (`uncovered`): tela toda "não se aplica", design legado ou ausente não devem nada.
391
264
  if (signals.gate === 'review' && (signals.screenChecklist?.uncovered?.length ?? 0) > 0) {
392
265
  const { section, uncovered } = signals.screenChecklist;
393
266
  if (section !== true) {
@@ -420,10 +293,6 @@ export function shouldPauseGate(config, signals) {
420
293
  return { pause: false, reason: '' };
421
294
  }
422
295
 
423
- /**
424
- * Human-readable list of risk signals present (for logging/Gate summary).
425
- * @returns {string[]}
426
- */
427
296
  export function getRiskSignals(signals) {
428
297
  const out = [];
429
298
  if (FORCED_PAUSE_WORK_TYPES.includes(signals.workType)) out.push('hotfix (gate humano obrigatório)');
@@ -432,8 +301,7 @@ export function getRiskSignals(signals) {
432
301
  out.push(`standards violations: ${signals.standardsViolations.join(', ')}`);
433
302
  }
434
303
  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.
304
+ // Listado mesmo quando não pausa: dispensa silenciosa é indistinguível de omissão.
437
305
  if (signals.gate === 'review' && signals.e2eStatus === 'skip') {
438
306
  const klass = classifyE2eSkip(signals.e2eReason);
439
307
  const literal = typeof signals.e2eReason === 'string' && signals.e2eReason.trim() !== ''
@@ -445,9 +313,7 @@ export function getRiskSignals(signals) {
445
313
  out.push('verification stamp desatualizado (stale) no ship');
446
314
  }
447
315
  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.
316
+ // Listado mesmo quando não pausa, pela mesma razão do e2e.
451
317
  if (signals.gate === 'review' && signals.promptModel) {
452
318
  const { declared, touched } = signals.promptModel;
453
319
  if (!declared) {
@@ -462,17 +328,12 @@ export function getRiskSignals(signals) {
462
328
  if (typeof signals.sliceCount === 'number' && signals.sliceCount > MAX_SLICES) {
463
329
  out.push(`${signals.sliceCount} fatias (acima de ${MAX_SLICES})`);
464
330
  }
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
331
  if (signals.gate === 'review' && signals.mediaClaims?.featureGeneratesMedia === true) {
469
332
  out.push(signals.mediaClaims.section === true
470
333
  ? 'prova de mídia registrada'
471
334
  : 'verifier.md sem a seção `## Mídia gerada` (prova de mídia ausente)');
472
335
  }
473
336
 
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
337
  if ((signals.gate === 'uiux' || signals.gate === 'review') && signals.screenChecklist) {
477
338
  const { mockups, legacy, exempt, screens = [], problems = [], section, uncovered = [] } = signals.screenChecklist;
478
339
  if (!mockups) {
@@ -483,13 +344,18 @@ export function getRiskSignals(signals) {
483
344
  out.push(problems.length > 0
484
345
  ? `checklist de tela incompleto (${problems.length} problema(s))`
485
346
  : `checklist de tela completo (${screens.length} tela(s))`);
347
+ const prototypes = signals.screenChecklist.prototypes ?? [];
348
+ if (screens.length > 0) {
349
+ out.push(prototypes.length > 0
350
+ ? `protótipo incompleto em ${prototypes.length} tela(s)`
351
+ : `protótipo e prints 1280/390 presentes (${screens.length} tela(s))`);
352
+ }
486
353
  } else if (uncovered.length > 0) {
487
354
  out.push(section !== true
488
355
  ? 'verifier.md sem a seção `## Checklist de tela`'
489
356
  : `checklist de tela sem conferência em ${uncovered.length} tela(s)`);
490
357
  } 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".
358
+ // Sem tela nenhuma: nada a cobrar no ship, mas não pode ler igual a uma tela toda "não se aplica".
493
359
  out.push(`checklist de tela: ${firstOf(problems)}`);
494
360
  } else if (section === true) {
495
361
  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 = [];