@polymorphism-tech/morph-spec 8.47.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 (377) hide show
  1. package/CLAUDE.md +51 -43
  2. package/README.md +39 -38
  3. package/bin/morph-spec.js +41 -113
  4. package/bin/validate.js +1 -147
  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 +41 -101
  9. package/framework/MORPH.md +95 -252
  10. package/framework/agents.json +86 -21
  11. package/framework/commands/morph-apply.md +5 -28
  12. package/framework/commands/morph-archive.md +1 -1
  13. package/framework/commands/morph-hotfix.md +8 -8
  14. package/framework/commands/morph-infra.md +1 -1
  15. package/framework/commands/morph-preflight.md +2 -3
  16. package/framework/commands/morph-status.md +4 -4
  17. package/framework/docs/CLI.md +574 -286
  18. package/framework/evals/review-checklist.md +82 -41
  19. package/framework/hooks/README.md +10 -11
  20. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  21. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  22. package/framework/hooks/claude-code/core/chain.js +4 -65
  23. package/framework/hooks/claude-code/core/comment-guard.js +3 -17
  24. package/framework/hooks/claude-code/core/denylist.json +33 -3
  25. package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
  26. package/framework/hooks/claude-code/core/gate-guard.js +9 -78
  27. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  28. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  29. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  30. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  31. package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
  32. package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
  33. package/framework/hooks/claude-code/core/state-sync.js +15 -85
  34. package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
  35. package/framework/hooks/claude-code/core/validate-completion.js +37 -147
  36. package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
  37. package/framework/hooks/claude-code/core/worktree-context.js +15 -114
  38. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  39. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  40. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  41. package/framework/hooks/claude-code/statusline.py +55 -41
  42. package/framework/hooks/package.json +3 -0
  43. package/framework/hooks/shared/activity-logger.js +9 -89
  44. package/framework/hooks/shared/agent-reap.js +2 -17
  45. package/framework/hooks/shared/build-check.js +11 -76
  46. package/framework/hooks/shared/claim-registry.js +9 -119
  47. package/framework/hooks/shared/command-tokens.js +168 -44
  48. package/framework/hooks/shared/comment-rules.js +5 -20
  49. package/framework/hooks/shared/comment-scanner.js +11 -43
  50. package/framework/hooks/shared/comment-stripper.js +11 -85
  51. package/framework/hooks/shared/e2e-check.js +0 -0
  52. package/framework/hooks/shared/errata.js +59 -0
  53. package/framework/hooks/shared/feature-locator.js +11 -95
  54. package/framework/hooks/shared/frontend-locations.js +4 -0
  55. package/framework/hooks/shared/gate-question.js +12 -0
  56. package/framework/hooks/shared/git-exclude.js +3 -20
  57. package/framework/hooks/shared/git-paths.js +8 -104
  58. package/framework/hooks/shared/graph-freshness.js +22 -166
  59. package/framework/hooks/shared/graph-refresh.js +21 -149
  60. package/framework/hooks/shared/heavy-command.js +4 -12
  61. package/framework/hooks/shared/hook-response.js +8 -83
  62. package/framework/hooks/shared/morph-cli.js +8 -52
  63. package/framework/hooks/shared/payload-utils.js +10 -132
  64. package/framework/hooks/shared/phase-utils.js +14 -72
  65. package/framework/hooks/shared/redact-secrets.js +4 -15
  66. package/framework/hooks/shared/reparse-scan.js +6 -66
  67. package/framework/hooks/shared/slice-progress.js +99 -0
  68. package/framework/hooks/shared/source-files.js +4 -17
  69. package/framework/hooks/shared/spawn-command.js +23 -0
  70. package/framework/hooks/shared/state-reader.js +51 -247
  71. package/framework/hooks/shared/state-writer.js +9 -54
  72. package/framework/hooks/shared/stdin-reader.js +1 -11
  73. package/framework/hooks/shared/telemetry-logger.js +7 -67
  74. package/framework/hooks/shared/transcript-usage.js +7 -29
  75. package/framework/hooks/shared/worktree-root.js +7 -29
  76. package/framework/mcp/registry.json +3 -4
  77. package/framework/phases.json +23 -39
  78. package/framework/rules/csharp-standards.md +1 -1
  79. package/framework/rules/frontend-standards.md +1 -0
  80. package/framework/rules/hangfire-standards.md +1 -1
  81. package/framework/rules/neon-standards.md +1 -1
  82. package/framework/schemas/config.schema.json +14 -9
  83. package/framework/schemas/feature.schema.json +15 -50
  84. package/framework/skills/README.md +3 -4
  85. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  86. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  87. package/framework/skills/core/morph-apply/SKILL.md +149 -222
  88. package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
  89. package/framework/skills/core/morph-machine/SKILL.md +2 -2
  90. package/framework/skills/core/morph-media/SKILL.md +1 -1
  91. package/framework/skills/core/morph-plan/SKILL.md +137 -299
  92. package/framework/skills/core/morph-review/SKILL.md +111 -136
  93. package/framework/skills/core/morph-standards/SKILL.md +27 -39
  94. package/framework/skills/core/morph-uiux/SKILL.md +94 -152
  95. package/framework/standards/STANDARDS.json +109 -1219
  96. package/framework/standards/STANDARDS.meta.json +1319 -0
  97. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  98. package/framework/standards/ai-agents/agent-session.md +1 -1
  99. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  100. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  101. package/framework/standards/ai-agents/evals-with-cache.md +1 -2
  102. package/framework/standards/ai-agents/guardrails.md +1 -1
  103. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  104. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  105. package/framework/standards/ai-agents/production.md +1 -1
  106. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  107. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  108. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  109. package/framework/standards/ai-agents/setup.md +3 -3
  110. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  111. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  112. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  113. package/framework/standards/ai-agents/workflows.md +1 -1
  114. package/framework/standards/architecture/code-comments.md +7 -7
  115. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  116. package/framework/standards/backend/api/validation.md +1 -1
  117. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  118. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  119. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  120. package/framework/standards/data/nosql/cache/redis.md +1 -1
  121. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  122. package/framework/standards/frontend/admin-app/forms.md +1 -1
  123. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  124. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  125. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  126. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  127. package/framework/templates/CLAUDE.md.core.template +3 -3
  128. package/framework/templates/README.md +2 -3
  129. package/framework/templates/REGISTRY.json +15 -179
  130. package/framework/templates/docs/plan.md +72 -0
  131. package/framework/templates/docs/ui-mockups.md +4 -0
  132. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  134. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  135. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  136. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  137. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  138. package/package.json +1 -1
  139. package/src/commands/context/context-refresh.js +0 -16
  140. package/src/commands/dashboard/dashboard.js +2 -8
  141. package/src/commands/e2e/e2e.js +18 -105
  142. package/src/commands/graph/affected.js +0 -14
  143. package/src/commands/graph/build.js +11 -50
  144. package/src/commands/graph/explain.js +0 -16
  145. package/src/commands/graph/status.js +1 -12
  146. package/src/commands/mcp/mcp-setup.js +4 -62
  147. package/src/commands/observability/cost.js +17 -111
  148. package/src/commands/observability/telemetry.js +1 -14
  149. package/src/commands/project/branch.js +4 -45
  150. package/src/commands/project/claim.js +4 -48
  151. package/src/commands/project/doctor-procs.js +1 -1
  152. package/src/commands/project/doctor.js +53 -307
  153. package/src/commands/project/env.js +1 -27
  154. package/src/commands/project/fleet.js +6 -35
  155. package/src/commands/project/heavy.js +3 -3
  156. package/src/commands/project/init.js +8 -68
  157. package/src/commands/project/install-plugin-cmd.js +0 -1
  158. package/src/commands/project/setup-infra-cmd.js +2 -1
  159. package/src/commands/project/stack-scan.js +58 -0
  160. package/src/commands/project/standards.js +65 -72
  161. package/src/commands/project/status.js +28 -121
  162. package/src/commands/project/update.js +31 -223
  163. package/src/commands/project/worktree.js +109 -883
  164. package/src/commands/scaffold/scaffold.js +0 -25
  165. package/src/commands/state/advance.js +65 -120
  166. package/src/commands/state/approve.js +87 -229
  167. package/src/commands/state/archive.js +23 -139
  168. package/src/commands/state/auto-approval.js +65 -0
  169. package/src/commands/state/create.js +49 -109
  170. package/src/commands/state/delete.js +11 -63
  171. package/src/commands/state/finish.js +105 -494
  172. package/src/commands/state/gate-check.js +74 -183
  173. package/src/commands/state/gate-decision.js +8 -42
  174. package/src/commands/state/handoff.js +4 -31
  175. package/src/commands/state/list.js +21 -91
  176. package/src/commands/state/report-check.js +134 -338
  177. package/src/commands/state/retype.js +12 -28
  178. package/src/commands/validation/index.js +0 -3
  179. package/src/commands/validation/mutate.js +79 -19
  180. package/src/commands/validation/plan-check.js +81 -0
  181. package/src/commands/validation/validate-feature.js +52 -216
  182. package/src/commands/validation/verify.js +45 -160
  183. package/src/core/paths/output-schema.js +9 -155
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +2 -30
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +32 -395
  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 +21 -0
  191. package/src/core/work-types.js +10 -45
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
  194. package/src/dashboard/app/components/CommandPalette.jsx +4 -11
  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 +3 -9
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/pages/Activity.jsx +1 -1
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
  211. package/src/dashboard/app/pages/Features.jsx +9 -16
  212. package/src/dashboard/app/pages/Guide.jsx +2 -3
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
  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 +14 -83
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +1 -7
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +92 -113
  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 +4 -7
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/lib/ai/ai-checks.js +7 -52
  233. package/src/lib/ai/ai-project-scan.js +14 -108
  234. package/src/lib/ai/code-audit.js +7 -63
  235. package/src/lib/ai/pin-drift.js +8 -51
  236. package/src/lib/ai/registry-audit.js +11 -90
  237. package/src/lib/ci/ci-verify.js +14 -13
  238. package/src/lib/ci/github-ci.js +9 -37
  239. package/src/lib/ci/workflow-scaffold.js +2 -10
  240. package/src/lib/detectors/claude-config-detector.js +13 -74
  241. package/src/lib/detectors/index.js +8 -59
  242. package/src/lib/e2e/compose-env.js +5 -66
  243. package/src/lib/e2e/stack-teardown.js +6 -40
  244. package/src/lib/generators/architecture-scanner.js +18 -156
  245. package/src/lib/generators/handoff-generator.js +37 -112
  246. package/src/lib/generators/scaffold-generator.js +5 -101
  247. package/src/lib/generators/settings-generator.js +11 -122
  248. package/src/lib/git/branch-classifier.js +11 -84
  249. package/src/lib/git/default-branch.js +14 -169
  250. package/src/lib/git/evidence-staleness.js +2 -1
  251. package/src/lib/git/gh.js +2 -17
  252. package/src/lib/git/pending-prs.js +6 -37
  253. package/src/lib/git/pr-files.js +3 -28
  254. package/src/lib/git/pr-index.js +3 -34
  255. package/src/lib/git/tree-fingerprint.js +17 -17
  256. package/src/lib/git/tree-state.js +6 -33
  257. package/src/lib/graph/god-nodes.js +2 -22
  258. package/src/lib/graph/graph-health.js +3 -24
  259. package/src/lib/graph/graph-loader.js +5 -56
  260. package/src/lib/graph/graphify-runtime.js +7 -37
  261. package/src/lib/graph/graphifyignore.js +8 -74
  262. package/src/lib/graph/sanitize.js +16 -136
  263. package/src/lib/heavy/cleanup.js +2 -6
  264. package/src/lib/installers/effects.js +8 -93
  265. package/src/lib/installers/mcp-installer.js +9 -169
  266. package/src/lib/installers/memory-installer.js +8 -33
  267. package/src/lib/installers/templates-reconcile.js +5 -60
  268. package/src/lib/mutation/commands.js +3 -2
  269. package/src/lib/mutation/dotnet-introspection.js +281 -0
  270. package/src/lib/mutation/plan.js +14 -6
  271. package/src/lib/mutation/runner.js +1 -1
  272. package/src/lib/mutation/subset.js +47 -13
  273. package/src/lib/plan/artifact-vocabulary.js +40 -0
  274. package/src/lib/plan/checks.js +208 -0
  275. package/src/lib/plan/plan-parser.js +204 -0
  276. package/src/lib/plan/plan-template.js +18 -0
  277. package/src/lib/projects/registry.js +7 -44
  278. package/src/lib/review-reports.js +204 -0
  279. package/src/lib/risk-detector.js +131 -274
  280. package/src/lib/specs/adr-index.js +38 -85
  281. package/src/lib/specs/design-seed.js +18 -0
  282. package/src/lib/specs/frontmatter.js +31 -0
  283. package/src/lib/specs/gfm-table.js +11 -0
  284. package/src/lib/specs/revocations.js +31 -102
  285. package/src/lib/specs/screen-checklist.js +56 -132
  286. package/src/lib/stack/dotnet-build-ignores.js +43 -0
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +260 -0
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +37 -0
  291. package/src/lib/standards/sections.js +47 -0
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +23 -179
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -313
  296. package/src/lib/tasks/test-runner.js +107 -447
  297. package/src/lib/telemetry/metrics.js +13 -103
  298. package/src/lib/telemetry/telemetry-reader.js +13 -238
  299. package/src/lib/telemetry/verify-event.js +4 -34
  300. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  301. package/src/lib/validators/code-budget/index.js +0 -7
  302. package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
  303. package/src/lib/validators/contracts/index.js +0 -6
  304. package/src/lib/validators/nextjs/index.js +0 -5
  305. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  306. package/src/lib/validators/packages/index.js +0 -6
  307. package/src/lib/validators/packages/package-validator.js +6 -56
  308. package/src/lib/validators/reuse/index.js +0 -8
  309. package/src/lib/validators/reuse/reuse-validator.js +24 -137
  310. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  311. package/src/lib/validators/traceability-check.js +54 -156
  312. package/src/lib/validators/validation-runner.js +20 -125
  313. package/src/lib/validators/verify-runner.js +152 -575
  314. package/src/lib/work-type-detector.js +5 -67
  315. package/src/lib/worktree/docker-audit.js +17 -137
  316. package/src/lib/worktree/env-allocator.js +10 -98
  317. package/src/lib/worktree/feature-truth.js +12 -115
  318. package/src/lib/worktree/finish-backup.js +32 -242
  319. package/src/lib/worktree/fleet-scanner.js +31 -126
  320. package/src/lib/worktree/ignored-triage.js +15 -91
  321. package/src/lib/worktree/vscode-settings.js +2 -3
  322. package/src/lib/worktree/worktree-audit.js +21 -213
  323. package/src/lib/worktree/worktree-include.js +3 -5
  324. package/src/scripts/global-install.js +3 -9
  325. package/src/scripts/install-plugin.js +1 -13
  326. package/src/scripts/setup-infra.js +17 -138
  327. package/src/utils/agents-installer.js +92 -272
  328. package/src/utils/banner.js +4 -25
  329. package/src/utils/claude-md-injector.js +1 -37
  330. package/src/utils/claude-settings-manager.js +1 -87
  331. package/src/utils/file-copier.js +32 -90
  332. package/src/utils/hooks-installer.js +26 -296
  333. package/src/utils/skills-installer.js +4 -102
  334. package/src/utils/version-checker.js +4 -83
  335. package/framework/commands/morph-proposal.md +0 -57
  336. package/framework/evals/trace-schema.md +0 -67
  337. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  338. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  339. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  340. package/framework/hooks/shared/tasks-validator.js +0 -111
  341. package/framework/rules/morph-task-tracking.md +0 -54
  342. package/framework/rules/morph-workflow.md +0 -244
  343. package/framework/schemas/tasks.schema.json +0 -79
  344. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  345. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  346. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  347. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  348. package/framework/templates/docs/proposal.md +0 -192
  349. package/framework/templates/docs/spec.md +0 -57
  350. package/framework/templates/docs/ui-components.md +0 -52
  351. package/framework/templates/docs/ui-design-system.md +0 -76
  352. package/framework/templates/docs/ui-flows.md +0 -167
  353. package/framework/templates/examples/spec-examples.md +0 -91
  354. package/framework/templates/feature/decisions.md +0 -124
  355. package/framework/templates/feature/recap.md +0 -145
  356. package/framework/templates/tasks.template.json +0 -17
  357. package/src/commands/state/usecase.js +0 -143
  358. package/src/commands/tasks/dag.js +0 -181
  359. package/src/commands/utils/index.js +0 -3
  360. package/src/lib/agents/output-collector.js +0 -76
  361. package/src/lib/detectors/design-system-detector.js +0 -188
  362. package/src/lib/evaluator-report.js +0 -104
  363. package/src/lib/generators/design-system-generator.js +0 -508
  364. package/src/lib/specs/acervo-format.js +0 -70
  365. package/src/lib/specs/acervo-lifecycle.js +0 -264
  366. package/src/lib/specs/done-criteria-check.js +0 -200
  367. package/src/lib/specs/usecase-check.js +0 -301
  368. package/src/lib/specs/usecase-promoter.js +0 -301
  369. package/src/lib/specs/usecase-registry.js +0 -237
  370. package/src/lib/tasks/dispatch-planner.js +0 -319
  371. package/src/lib/tasks/dotnet-test-introspection.js +0 -632
  372. package/src/lib/tasks/mermaid-renderer.js +0 -132
  373. package/src/lib/tasks/task-classifier.js +0 -30
  374. package/src/lib/tasks/task-parser.js +0 -209
  375. package/src/lib/tasks/tasks-json-parser.js +0 -296
  376. package/src/lib/tasks/test-scope.js +0 -597
  377. package/src/utils/agent-id-mapper.js +0 -11
@@ -1,117 +1,45 @@
1
1
  import chalk from 'chalk';
2
- import { existsSync, readFileSync } from 'fs';
3
2
  import { getFeature } from '../../core/state/state-manager.js';
4
- import { getAbsoluteOutputPath } from '../../core/paths/output-schema.js';
5
- import { shouldPauseGate, getRiskSignals, classifyE2eSkip } from '../../lib/risk-detector.js';
6
- import { evaluatorReportState, evaluatorMediaSection, verdictSignal } from '../../lib/evaluator-report.js';
3
+ import { shouldPauseGate, getRiskSignals, classifyE2eSkip, planChecksPause, securityReviewFor } from '../../lib/risk-detector.js';
4
+ import { reviewReports, reviewSignal, requiredReviewRoles, verifierMediaSection } from '../../lib/review-reports.js';
7
5
  import { collectScreenChecklist } from '../../lib/specs/screen-checklist.js';
8
6
  import { persistGateDecision, VALID_GATES, readTrustFromConfig } from './gate-decision.js';
9
7
  import { minimatch } from 'minimatch';
10
8
  import { detectDefaultBranch, getHeadSha, getDiffStat, changedPathsAgainst } from '../../lib/git/default-branch.js';
11
9
  import { treeMovedSince } from '../../lib/git/evidence-staleness.js';
12
10
  import { readMorphConfig } from '../../lib/tasks/test-runner.js';
13
- import { oversizedTasks } from '../../lib/tasks/tasks-json-parser.js';
11
+ import { readPlanChecks } from '../../lib/plan/checks.js';
14
12
  import { assertOwnerTree, ownerTreeRefusalLines } from '../../lib/worktree/feature-truth.js';
15
13
 
16
- /**
17
- * `--signals` on `gate-check` is NOT a general override like `gate-decision`'s
18
- * — everything else here is read straight off disk (workType, taskCount, sha,
19
- * diffstat, verification), and letting a caller override those would let a
20
- * careless/hostile `--signals` silently defeat the policy this command exists
21
- * to make deterministic (e.g. overriding `workType` away from "hotfix" would
22
- * suppress risk-detector's forced Gate-3 pause). Only the signals that are
23
- * genuinely NOT derivable from disk today are accepted here:
24
- * - standardsViolations / designSystemDivergence: still an LLM's
25
- * natural-language read (see the R4 report — no deterministic detector
26
- * exists in JS yet).
27
- */
14
+ // Only signals NOT derivable from disk may be overridden: overriding a disk fact (e.g. `workType`
15
+ // away from hotfix) would silently defeat the policy this command makes deterministic.
28
16
  const OVERRIDABLE_SIGNAL_KEYS = ['standardsViolations', 'designSystemDivergence'];
29
17
 
30
- /**
31
- * Os paths de standard que fazem uma feature ser "de midia".
32
- *
33
- * Escritos aqui, uma vez, e lidos do campo `standards[]` das tasks — que o Gate
34
- * 2 congelou. Nao ha julgamento envolvido: ou a task pinou o standard, ou nao
35
- * pinou. Um sinal derivado de opiniao seria sobrescrivivel na pratica ainda que
36
- * nao estivesse em OVERRIDABLE_SIGNAL_KEYS.
37
- *
38
- * A forma dos valores e a de `tasks.json` (`standards/...`), nao a do disco
39
- * (`framework/standards/...`): e o vocabulario que o plano usa.
40
- */
41
- export const MEDIA_STANDARD_PATHS = [
42
- 'standards/ai-agents/modalities-image-gen.md',
43
- 'standards/ai-agents/media-video.md',
44
- 'standards/ai-agents/media-pipeline.md',
18
+ export const MEDIA_STANDARD_IDS = [
19
+ 'ai-agents-modalities-image-gen',
20
+ 'ai-agents-media-video',
21
+ 'ai-agents-media-pipeline',
45
22
  ];
46
23
 
47
- /**
48
- * Uma feature "gera midia" quando ALGUMA task pinou um dos tres standards de
49
- * midia no seu array `standards[]`.
50
- *
51
- * Isto e uma FUNCAO EXPORTADA, e nao uma expressao dentro de `collectSignals`,
52
- * porque um predicado que decide se um guardrail arma tem de ser testavel
53
- * sozinho — o irmao `productionCode` tem quatro casos, e este nao tinha nenhum.
54
- * Um predicado que sempre devolve `false` desarma a clausula em silencio, e
55
- * silencio e o defeito que esta epic catalogou sete vezes.
56
- *
57
- * **O prefixo `framework/` e normalizado, e essa e a razao de existir da
58
- * funcao.** O vocabulario do plano e `standards/...` (o exemplo do
59
- * `tasks.schema.json`, e a unica forma medida hoje nos `standards[]` desta
60
- * arvore), mas o disco e `framework/standards/...` — e a mesma prosa das skills
61
- * cita as duas formas. Um plano que escrevesse a forma do disco desarmaria o
62
- * guardrail sem erro nenhum: `standards --find`, `validate` e o proprio Gate 3
63
- * continuariam verdes. Aceitar as duas grafias custa uma linha; recusar uma
64
- * delas em silencio custa o gate.
65
- *
66
- * Fail-open por AUSENCIA (nao por opiniao): lista vazia, `standards` ausente ou
67
- * de tipo errado ⇒ `false` ⇒ nao pausa. Sem plano legivel nao ha como AFIRMAR
68
- * que a feature gera midia, e uma regra nova nao trava feature legada.
69
- *
70
- * @param {Array<object>} tasks - `tasks.json` → `tasks[]` (qualquer coisa nao-array vira []).
71
- * @returns {boolean} true se ao menos uma task pinou um standard de midia.
72
- */
73
- export function featureGeneratesMedia(tasks) {
74
- if (!Array.isArray(tasks)) return false;
75
-
76
- return tasks.some((t) =>
77
- Array.isArray(t?.standards) &&
78
- t.standards.some((s) =>
79
- typeof s === 'string' &&
80
- MEDIA_STANDARD_PATHS.includes(s.replace(/^framework\//, ''))));
24
+ // O plano escreve o standard pelo id ou pelo caminho (com ou sem `framework/`/`standards/`).
25
+ export function standardIdOf(value) {
26
+ return String(value ?? '').trim().replace(/\\/g, '/')
27
+ .replace(/^(\.morph\/)?(framework\/)?(standards\/)?/, '')
28
+ .replace(/\.md$/, '')
29
+ .replace(/\//g, '-');
81
30
  }
82
31
 
83
- /** Read tasks.json's task list for a feature; [] if missing/corrupt. Mirrors verify.js's readTasks(). */
84
- function readTasks(featureName) {
85
- try {
86
- const tasksPath = getAbsoluteOutputPath(process.cwd(), featureName, 'tasks');
87
- if (!existsSync(tasksPath)) return [];
88
- return JSON.parse(readFileSync(tasksPath, 'utf8')).tasks || [];
89
- } catch {
90
- return [];
91
- }
32
+ // Decide se um guardrail arma: um predicado que sempre devolve `false` desarma em silêncio a
33
+ // pausa de mídia da review e a rota opus.
34
+ export function featureGeneratesMedia(slices) {
35
+ if (!Array.isArray(slices)) return false;
36
+ return slices.some((s) => Array.isArray(s?.standards)
37
+ && s.standards.some((id) => MEDIA_STANDARD_IDS.includes(standardIdOf(id))));
92
38
  }
93
39
 
94
- /**
95
- * "Esta feature mexeu em prompt ou em modelo?" — em duas metades, porque uma só
96
- * não distingue as duas situações que pedem ações diferentes.
97
- *
98
- * `declared: false` é "o projeto não declara caminho algum de prompt/modelo" —
99
- * a regra é inerte por construção e a razão registrada diz isso. `declared:
100
- * true` com `touched: []` é "declara, e nada foi tocado" — a regra rodou e não
101
- * viu nada. Colapsar os dois num array vazio faria um projeto que nunca
102
- * configurou `watch[]` parecer um projeto que foi verificado.
103
- *
104
- * O predicado NÃO é invenção deste framework: o repositório de referência já
105
- * roda exatamente ele em produção, disparando publicação por `paths:` sobre os
106
- * mesmos globs e decidindo o escopo com `git diff --name-only`.
107
- *
108
- * COLETA, NUNCA DECIDE. Nada aqui julga se o que foi tocado deveria pausar um
109
- * gate — política escrita no coletor cria duas fontes para o mesmo fato.
110
- *
111
- * @param {string} cwd
112
- * @param {string|null} defaultBranch
113
- * @returns {{declared: boolean, watch: string[], touched: string[]}}
114
- */
40
+ // `declared: false` (nenhum caminho declarado: regra inerte) e `touched: []` (declarou, nada
41
+ // tocado) não podem colapsar num array vazio. COLETA, NUNCA DECIDE: política no coletor cria
42
+ // duas fontes para o mesmo fato.
115
43
  function collectPromptModel(cwd, defaultBranch) {
116
44
  const watch = readMorphConfig(cwd)?.project?.evals?.watch;
117
45
  const globs = Array.isArray(watch) ? watch.filter((g) => typeof g === 'string' && g.trim()) : [];
@@ -126,103 +54,73 @@ function collectPromptModel(cwd, defaultBranch) {
126
54
  };
127
55
  }
128
56
 
129
- /**
130
- * Collect every signal `shouldPauseGate()` (risk-detector.js) is documented
131
- * to consume, straight off disk — the deterministic call site risk-detector
132
- * never had (it shipped fully tested with zero callers; see the R4 report).
133
- *
134
- * `verificationStatus` feeds a real pause rule (verify vermelho pausa —
135
- * política adicionada em risk-detector.js no fecho do R4, qualquer gate).
136
- * `verificationStale` também alimenta uma regra própria, mas só no ship
137
- * (`gate === 'review'`) — sha do stamp divergindo do HEAD atual. `gate` em si
138
- * é o que permite risk-detector.js diferenciar as duas. `e2eStatus`/`e2eReason`
139
- * alimentam a regra da dispensa de e2e (também só no ship): um nó e2e que
140
- * PULOU não reprova o rollup, logo `verificationStatus` sai 'pass' e a razão
141
- * literal é o único fato que distingue dispensa legítima de ausência de prova.
142
- * `verificationScope` é o quarto sinal da mesma regra: um `verify {f} {task}`
143
- * carimba `scope: 'task:{id}'` e o e2e dele pula com a razão do GRUPO daquela
144
- * task, que risk-detector.js recusa como dispensa da feature.
145
- * `verification` completo, `e2eSkipClass`, diffstat e sha seguem como contexto
146
- * de auditoria persistido, sem regra própria.
147
- *
148
- * @param {string} featureName
149
- * @param {object} feature
150
- * @param {string} cwd
151
- * @param {string} gateName
152
- * @returns {object}
153
- */
57
+ /** A regra única de quais relatórios o ship exige, lida por gate-check, approve e report-check. */
58
+ export function collectReviewRequirement(cwd, feature) {
59
+ let changed = [];
60
+ try { changed = changedPathsAgainst(detectDefaultBranch(cwd), cwd); } catch { changed = []; }
61
+ const security = securityReviewFor(changed);
62
+ return { required: requiredReviewRoles({ workType: feature?.workType, securityRequired: security.required }), security };
63
+ }
64
+
65
+ export function collectReviewSignal(cwd, featureName, feature, planChecks = readPlanChecks(cwd, featureName)) {
66
+ const { required } = collectReviewRequirement(cwd, feature);
67
+ const checkIds = Array.isArray(planChecks.checks?.checks) ? planChecks.checks.checks.map((c) => c.id) : null;
68
+ return reviewSignal(reviewReports(cwd, featureName), { required, checkIds });
69
+ }
70
+
71
+ /** Why an automatic signature may not close the plan gate: the rule that makes gate-check pause it. */
72
+ export function planAutoApprovalBlock(cwd, featureName) {
73
+ const reason = planChecksPause(readPlanChecks(cwd, featureName).status);
74
+ return reason ? reason.replace('{feature}', featureName) : null;
75
+ }
76
+
77
+ // Collects only; risk-detector.js decides. `e2eStatus`/`e2eReason` exist because a skipped e2e
78
+ // node does not fail the rollup: the literal reason is the only fact separating a legitimate
79
+ // waiver from missing proof.
154
80
  export function collectSignals(featureName, feature, cwd, gateName) {
155
- const tasks = readTasks(featureName);
81
+ const planChecks = readPlanChecks(cwd, featureName);
82
+ const slices = planChecks.checks?.slices ?? null;
156
83
  const defaultBranch = detectDefaultBranch(cwd);
157
84
  const sha = getHeadSha(cwd);
158
85
  const verification = feature.verification || null;
159
86
 
160
87
  const signals = {
161
- // Furo 2: disk-derived, never in OVERRIDABLE_SIGNAL_KEYS — a caller
162
- // cannot spoof which gate risk-detector.js thinks it is deciding, same
163
- // anti-spoof rationale as workType (hotfix's forced pause).
88
+ // Disk-derived, never overridable: a caller cannot spoof which gate is being decided.
164
89
  gate: gateName,
165
90
  workType: feature.workType || 'feature',
166
- taskCount: tasks.length,
91
+ sliceCount: slices ? slices.length : null,
167
92
  standardsViolations: [],
168
93
  designSystemDivergence: false,
169
94
  };
170
95
 
171
- // Fato de disco, fora de OVERRIDABLE_SIGNAL_KEYS: task que não cabe num
172
- // dispatch se fatia no plano, não depois de um sub-agent bater no teto.
173
- if (gateName === 'plan') signals.oversizedTasks = oversizedTasks({ tasks });
96
+ if (gateName === 'plan') signals.planChecks = planChecks.status;
174
97
 
175
- // Só onde o checklist decide algo: no fecho do design e no ship. Fato de
176
- // disco, fora de OVERRIDABLE_SIGNAL_KEYS.
98
+ // Só onde o checklist decide algo: no fecho do design e no ship.
177
99
  if (gateName === 'uiux' || gateName === 'review') {
178
100
  signals.screenChecklist = collectScreenChecklist(cwd, featureName, gateName);
179
101
  }
180
102
 
181
103
  if (gateName === 'review') {
182
- signals.evaluatorVerdict = verdictSignal(evaluatorReportState(cwd, featureName));
104
+ signals.reviewReports = collectReviewSignal(cwd, featureName, feature, planChecks);
183
105
  signals.mediaClaims = {
184
- section: evaluatorMediaSection(cwd, featureName),
185
- featureGeneratesMedia: featureGeneratesMedia(tasks),
106
+ section: verifierMediaSection(cwd, featureName),
107
+ featureGeneratesMedia: featureGeneratesMedia(slices),
186
108
  };
187
109
  }
188
110
 
189
- // verificationStatus alimenta a regra "verify vermelho pausa"; o resto do
190
- // stamp é auditoria. verificationStale é fail-open na direção "not stale":
191
- // stamp com sha null (checkout sem git) ou stamp ausente lê como NOT stale.
111
+ // verificationStale é fail-open: stamp sem sha (checkout sem git) ou ausente lê como NOT stale.
192
112
  signals.verification = verification;
193
113
  if (verification?.status) signals.verificationStatus = verification.status;
194
114
  signals.verificationStale = treeMovedSince(cwd, verification?.sha, sha);
195
115
 
196
- // O nó e2e, separado do rollup de propósito: um `skip` NÃO reprova
197
- // `verification.status`, então sem estas duas chaves o gate lê 'pass' tanto
198
- // quando o e2e rodou e passou quanto quando ele nunca rodou. `null` aqui é
199
- // DESCONHECIDO (stamp ausente, ou anterior ao nó e2e), nunca 'skip'.
200
- // `e2eSkipClass` é DERIVADO pela fonte única (classifyE2eSkip, em
201
- // risk-detector.js) e persistido só como auditoria — quem decide continua
202
- // sendo shouldPauseGate, aqui não há política.
203
- //
204
- // Nenhuma das três entra em OVERRIDABLE_SIGNAL_KEYS: descrevem O QUE
205
- // ACONTECEU no disco, como `gate`/`workType`, não uma opinião que o chamador
206
- // possa afirmar. Um `--signals '{"e2eStatus":"pass"}'` compraria o Gate 3.
116
+ // Separado do rollup: um `skip` NÃO reprova `verification.status`. `null` é DESCONHECIDO, nunca
117
+ // 'skip'. Fora de OVERRIDABLE_SIGNAL_KEYS: um `--signals '{"e2eStatus":"pass"}'` compraria o ship.
207
118
  signals.e2eStatus = verification?.nodes?.e2e ?? null;
208
119
  signals.e2eReason = verification?.e2eReason ?? null;
209
120
  signals.e2eSkipClass = signals.e2eStatus === 'skip' ? classifyE2eSkip(signals.e2eReason) : null;
210
- // De QUEM é o carimbo. `verify {f} {task}` grava 'task:{id}'; só 'feature'
211
- // responde pela app inteira, e é por isso que este sinal existe: a razão
212
- // 'task group "docs" does not touch runtime' é dispensa legítima do que
213
- // AQUELA task verificou, e sem o escopo ela dispensaria o Gate 3 da feature.
214
- // Fato de disco como os outros três — jamais sobrescrevível por --signals.
215
- signals.verificationScope = verification?.scope ?? null;
216
121
 
217
- // O nó de eval, separado do rollup pela mesma razão que o e2e: um eval que
218
- // PULOU (escopo de task) não avermelha `verification.status`, então o gate
219
- // leria 'pass' tanto para "o eval rodou e passou" quanto para "o eval nunca
220
- // rodou". `null` aqui é "este projeto não declara nó de eval" — DISTINTO de
221
- // 'skip', que é "declara, e esta corrida não o executou".
122
+ // Mesma razão do e2e. `null` = o projeto não declara nó de eval, DISTINTO de 'skip'.
222
123
  signals.evalsStatus = verification?.nodes?.evals ?? null;
223
- // O que a feature TOCOU, contra os globs que o projeto declarou como
224
- // prompt/modelo. Fato de disco: `declared` vem do config commitado, `touched`
225
- // do git. Nenhuma política mora aqui — quem decide é shouldPauseGate.
226
124
  signals.promptModel = collectPromptModel(cwd, defaultBranch);
227
125
 
228
126
  signals.diffstat = getDiffStat(defaultBranch, cwd);
@@ -231,20 +129,8 @@ export function collectSignals(featureName, feature, cwd, gateName) {
231
129
  return signals;
232
130
  }
233
131
 
234
- /**
235
- * `morph-spec gate-check <feature> <gate> [--signals <json>] [--trust <mode>]
236
- * [--json]` — the deterministic, disk-derived counterpart to `gate-decision`:
237
- * collects the signals `shouldPauseGate()` (risk-detector.js) already knows
238
- * how to score, calls it, and persists the result through the SAME write path
239
- * as `gate-decision` ({@link persistGateDecision}) so the two commands can
240
- * never produce a differently-shaped `gateDecisions.{gate}` entry.
241
- *
242
- * Golden rule: this command does NOT change gate policy. It is a faithful
243
- * caller of risk-detector.js's existing, already-tested rules — nothing here
244
- * adds a new pause condition. Where the skills' natural-language risk tables
245
- * (morph-brainstorming/morph-plan/morph-review/morph-apply) diverge from
246
- * risk-detector.js, risk-detector.js wins (see the R4 report for the full list).
247
- */
132
+ // Never changes gate policy: a faithful caller of risk-detector.js, which wins wherever the
133
+ // skills' risk tables diverge. Persists through the same path as `gate-decision`.
248
134
  export async function gateCheckCommand(featureName, gateName, options = {}) {
249
135
  try {
250
136
  if (!VALID_GATES.includes(gateName)) {
@@ -259,10 +145,7 @@ export async function gateCheckCommand(featureName, gateName, options = {}) {
259
145
  process.exit(1);
260
146
  }
261
147
 
262
- // Owner-tree guard: gate-check reads workType/tasks/verdict/git off cwd
263
- // AND writes the resulting decision through persistGateDecision — both
264
- // halves are wrong from a tree that isn't this feature's own, so refuse
265
- // rather than compute a decision from stale reads and then fail to save it.
148
+ // Both the reads and the persisted decision are wrong from a tree that doesn't own the feature.
266
149
  const owner = assertOwnerTree(featureName, process.cwd());
267
150
  if (owner.blocked) {
268
151
  for (const { tone, text } of ownerTreeRefusalLines(featureName, owner.tree, owner.branch, `morph-spec gate-check ${featureName} ${gateName}`)) {
@@ -289,7 +172,7 @@ export async function gateCheckCommand(featureName, gateName, options = {}) {
289
172
  if (rejected.length > 0) {
290
173
  console.error(chalk.yellow(
291
174
  `\n⚠️ --signals ignored for keys not derived from disk-facts you may safely override: ${rejected.join(', ')}\n` +
292
- ` Only ${OVERRIDABLE_SIGNAL_KEYS.join(', ')} may be overridden here — everything else is read from feature.json/tasks.json/git.\n` +
175
+ ` Only ${OVERRIDABLE_SIGNAL_KEYS.join(', ')} may be overridden here — everything else is read from feature.json/checks.json/git.\n` +
293
176
  ` Use \`morph-spec gate-decision\` directly for a fully manual decision.\n`
294
177
  ));
295
178
  }
@@ -303,9 +186,14 @@ export async function gateCheckCommand(featureName, gateName, options = {}) {
303
186
  const signals = { ...collectSignals(featureName, feature, cwd, gateName), ...overrides };
304
187
 
305
188
  const trust = options.trust || readTrustFromConfig();
306
- const { pause, reason } = shouldPauseGate({ trust }, signals);
189
+ const computed = shouldPauseGate({ trust }, signals);
190
+ const manual = feature.gateDecisions?.[gateName];
191
+ const manualPause = manual?.source === 'manual' && manual.pause === true;
192
+ const { pause, reason } = manualPause ? { pause: true, reason: manual.reason } : computed;
307
193
 
308
- const entry = await persistGateDecision(featureName, gateName, { pause, reason, signals, trust, cwd });
194
+ const entry = await persistGateDecision(featureName, gateName, {
195
+ pause, reason, signals, trust, cwd, source: manualPause ? 'manual' : 'gate-check',
196
+ });
309
197
 
310
198
  if (options.json) {
311
199
  console.log(JSON.stringify({ feature: featureName, gate: gateName, ...entry }, null, 2));
@@ -317,6 +205,9 @@ export async function gateCheckCommand(featureName, gateName, options = {}) {
317
205
  ? chalk.yellow(`\n⏸️ Gate "${gateName}" — PAUSE (${featureName})`)
318
206
  : chalk.green(`\n▶️ Gate "${gateName}" — auto-pass (${featureName})`));
319
207
  console.log(chalk.gray(` reason: ${entry.reason} | trust: ${entry.trust} | decidedAt: ${entry.decidedAt}`));
208
+ if (manualPause) {
209
+ console.log(chalk.gray(` pausa manual mantida (sinais calculados: ${computed.reason}); só \`morph-spec gate-decision ${featureName} ${gateName} --pause false\` a libera`));
210
+ }
320
211
  if (allSignals.length > 0) {
321
212
  console.log(chalk.gray(` risk signals present: ${allSignals.join('; ')}`));
322
213
  }
@@ -5,7 +5,7 @@ import { getFeature, updateFeature } from '../../core/state/state-manager.js';
5
5
  import { assertOwnerTree, ownerTreeRefusalLines } from '../../lib/worktree/feature-truth.js';
6
6
  import { mockupsHash } from '../../lib/specs/screen-checklist.js';
7
7
 
8
- export const VALID_GATES = ['proposal', 'uiux', 'plan', 'review'];
8
+ export const VALID_GATES = ['plan', 'uiux', 'review'];
9
9
 
10
10
  function parsePauseValue(raw) {
11
11
  const v = String(raw).toLowerCase();
@@ -15,11 +15,7 @@ function parsePauseValue(raw) {
15
15
  process.exit(1);
16
16
  }
17
17
 
18
- /**
19
- * trust from the committed config (BOM-tolerant), defaulting to "auto".
20
- * Shared by `gate-decision` (recorded as metadata) and `gate-check`
21
- * (drives shouldPauseGate's decision directly).
22
- */
18
+ /** `trust` from the committed config (BOM-tolerant), defaulting to "auto". */
23
19
  export function readTrustFromConfig() {
24
20
  try {
25
21
  const path = join(process.cwd(), '.morph', 'config', 'config.json');
@@ -32,24 +28,10 @@ export function readTrustFromConfig() {
32
28
  }
33
29
 
34
30
  /**
35
- * Write the canonical `gateDecisions.{gate}` entry to a feature's
36
- * authoritative feature.json — the ONE write path both `gate-decision` (a
37
- * caller-supplied pause/reason/signals, e.g. an LLM's natural-language risk
38
- * read, or a manual override) and `gate-check` (a `shouldPauseGate()`-derived
39
- * pause/reason/signals) feed into, so the two commands can never drift into
40
- * two different persisted shapes for the same field. Does NOT validate
41
- * `gateName` — callers that need a friendly CLI error check `VALID_GATES`
42
- * themselves before calling this.
43
- *
44
- * @param {string} featureName
45
- * @param {string} gateName
46
- * @param {{ pause: boolean, reason?: string, signals?: object, trust?: string, cwd?: string }} decision
47
- * @returns {Promise<{ pause: boolean, reason: string, signals: object, trust: string, decidedAt: string, mockupsHash?: string|null }>}
48
- * the exact entry written (decidedAt is server-side, generated here). A uiux
49
- * decision also carries the hash of the mockup it saw: it answers that
50
- * mockup's pause, not the next one's.
31
+ * The ONE write path of `gateDecisions.{gate}`, shared by `gate-decision` and `gate-check`.
32
+ * Does not validate `gateName`. A uiux decision records the `mockupsHash` it saw.
51
33
  */
52
- export async function persistGateDecision(featureName, gateName, { pause, reason, signals = {}, trust, cwd = process.cwd() } = {}) {
34
+ export async function persistGateDecision(featureName, gateName, { pause, reason, signals = {}, trust, cwd = process.cwd(), source } = {}) {
53
35
  const entry = {
54
36
  pause,
55
37
  reason: reason || (pause ? 'risco detectado' : 'nenhum sinal de risco'),
@@ -57,30 +39,13 @@ export async function persistGateDecision(featureName, gateName, { pause, reason
57
39
  trust: trust || readTrustFromConfig(),
58
40
  decidedAt: new Date().toISOString(),
59
41
  ...(gateName === 'uiux' ? { mockupsHash: mockupsHash(cwd, featureName) } : {}),
42
+ source,
60
43
  };
61
44
 
62
45
  await updateFeature(featureName, `gateDecisions.${gateName}`, entry);
63
46
  return entry;
64
47
  }
65
48
 
66
- /**
67
- * `morph-spec gate-decision <feature> <gate> --pause <bool> [--reason <txt>]
68
- * [--signals <json>] [--trust <mode>]` — record the trust decision for a gate
69
- * under `gateDecisions` in the authoritative feature.json.
70
- *
71
- * The timestamp (`decidedAt`) is
72
- * stamped server-side and the write is atomic (state-manager). This replaces
73
- * the manual Edit of feature.json from the skills — which collided with the
74
- * state-sync hook rewriting the file between the agent's Read and Edit
75
- * ("file modified since read"). The canonical shape is documented in
76
- * framework/schemas/feature.schema.json and consumed by gate-guard.js.
77
- *
78
- * The manual escape hatch: `gate-check` (src/commands/state/gate-check.js) is
79
- * the default, disk-derived path via `shouldPauseGate()`; this command stays
80
- * for a caller that already knows the decision (an LLM's natural-language
81
- * risk read a script can't see, or a deliberate override) — both write
82
- * through {@link persistGateDecision}, so the persisted shape never diverges.
83
- */
84
49
  export async function gateDecisionCommand(featureName, gateName, options = {}) {
85
50
  try {
86
51
  if (!VALID_GATES.includes(gateName)) {
@@ -118,7 +83,7 @@ export async function gateDecisionCommand(featureName, gateName, options = {}) {
118
83
  process.exit(1);
119
84
  }
120
85
  if (!signals || typeof signals !== 'object' || Array.isArray(signals)) {
121
- console.error(chalk.red('\n❌ Invalid --signals: expected a JSON object, e.g. \'{"taskCount":12}\'\n'));
86
+ console.error(chalk.red('\n❌ Invalid --signals: expected a JSON object, e.g. \'{"standardsViolations":[]}\'\n'));
122
87
  process.exit(1);
123
88
  }
124
89
  }
@@ -128,6 +93,7 @@ export async function gateDecisionCommand(featureName, gateName, options = {}) {
128
93
  reason: options.reason,
129
94
  signals,
130
95
  trust: options.trust,
96
+ source: 'manual',
131
97
  });
132
98
 
133
99
  if (options.json) {
@@ -1,34 +1,9 @@
1
- /**
2
- * `morph-spec handoff <feature>` — generate the compressed session-handoff
3
- * document (`.morph/features/{f}/handoff.md`).
4
- *
5
- * The fleet workflow hands a feature between Claude Code instances (a new
6
- * worktree session, a resumed session after a phase transition). The handoff
7
- * is the sanctioned way to carry context across: derived, regenerable, and
8
- * pointer-based (see handoff-generator.js). This command is also called
9
- * in-process by `worktree setup`, `create --worktree`, `advance` and
10
- * `approve` — the CLI surface exists for the LLM/user to refresh the
11
- * document on demand.
12
- *
13
- * WORKTREE-AWARE: `create --worktree` writes a COMPLETE feature.json into the
14
- * primary root and the branch-committed copy (`morph/{f}`) becomes the truth
15
- * from then on. Building the handoff from `process.cwd()` unconditionally
16
- * used to mean: run this from the root while the worktree is alive, and you
17
- * either got "Feature not found" (root's copy was already sheded) or a
18
- * document silently built from the stale birth skeleton, written INTO the
19
- * root (a second, always-defasada copy of a document that's supposed to be
20
- * regenerated, not duplicated). This resolves the owning tree first
21
- * (`resolveFeatureTree`) and writes THERE, unless `--here` forces the cwd.
22
- */
23
-
24
1
  import chalk from 'chalk';
25
2
  import { buildHandoff, writeHandoff } from '../../lib/generators/handoff-generator.js';
26
3
  import { resolveFeatureTree } from '../../lib/worktree/feature-truth.js';
27
4
 
28
- /**
29
- * @param {string} featureName
30
- * @param {{ json?: boolean, here?: boolean }} [options]
31
- */
5
+ // With a live worktree the root copy is a stale birth skeleton: build and write in the
6
+ // owning tree (`resolveFeatureTree`) unless `--here` forces the cwd.
32
7
  export function handoffCommand(featureName, options = {}) {
33
8
  const cwd = process.cwd();
34
9
 
@@ -44,9 +19,7 @@ export function handoffCommand(featureName, options = {}) {
44
19
  }
45
20
 
46
21
  if (owner.source === 'branch-only') {
47
- // The truth exists, committed on morph/{feature}, but is not materialized
48
- // into ANY tree right now. A handoff points at files on disk — pointing
49
- // at files that exist nowhere is worse than recusing outright.
22
+ // The handoff points at files on disk; a truth materialized in no tree is refused.
50
23
  const hint = `morph-spec worktree setup ${featureName} (ou: git checkout ${owner.branch})`;
51
24
  if (options.json) {
52
25
  console.log(JSON.stringify({ error: 'feature-not-in-tree', feature: featureName, branch: owner.branch, hint }));
@@ -87,6 +60,6 @@ export function handoffCommand(featureName, options = {}) {
87
60
  if (tree !== cwd) {
88
61
  console.log(chalk.yellow(` resolvido para ${tree} — a cópia da raiz é esqueleto de nascimento, não a verdade.`));
89
62
  }
90
- console.log(chalk.gray(` fase ${result.meta.phase} · tasks ${result.meta.tasksDone}/${result.meta.tasksTotal} · ${result.meta.generatedAt}`));
63
+ console.log(chalk.gray(` fase ${result.meta.phase} · fatias ${result.meta.slicesDone}/${result.meta.slicesTotal} · ${result.meta.generatedAt}`));
91
64
  console.log(chalk.gray(' A próxima sessão lê este documento antes de continuar (ponteiros, não cópias).\n'));
92
65
  }