@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,59 +1,78 @@
1
- # Checklist do avaliador — Gate 3
1
+ # Checklist do Gate 3 — verificador, revisor, segurança
2
2
 
3
- Você não dá nota. Você responde: **esta entrega faz o que os use cases dizem, sem quebrar o que já
4
- funcionava?** E prova cada "não" com evidência.
5
-
6
- ## Saída (`4-review/evaluator-report.md`)
3
+ Três agentes em contexto fresco, cada um com o seu relatório em `4-review/`. Ninguém dá nota. Cada
4
+ relatório começa igual e é append-only (vale o último `**Verdict:**`):
7
5
 
8
6
  ```markdown
9
7
  **Verdict:** SHIP ← ou FIX
8
+ **Reviewed-SHA:** <sha do dispatch>
10
9
 
11
10
  ## Bloqueadores
12
11
 
13
- | # | Tipo | Onde | Evidência |
14
- |---|------|------|-----------|
15
- | B1 | uc | src/Orders/Create.cs:42 | UC-03 P2: pedido sem itens é aceito — `POST /orders {items:[]}` → 201 |
12
+ | id | tipo | onde | evidência |
13
+ |----|------|------|-----------|
14
+ | B1 | check | src/Orders/Create.cs:42 | C3: pedido sem itens é aceito — `POST /orders {items:[]}` → 201 |
16
15
 
17
16
  ## Notas
18
17
 
19
18
  - (o que melhoraria, sem bloquear)
20
-
21
- ## Navegado
22
-
23
- - (fluxos exercitados na app, quando o stack estava no ar; o que viu no console/network)
24
19
  ```
25
20
 
26
21
  Sem bloqueador, escreva `Nenhum.` na seção. `SHIP` com bloqueador aberto é contradição e pausa o gate.
22
+ Confirmação depois de uma correção: seção nova ao fim com `**Verdict:**` e o `**Reviewed-SHA:**` novo —
23
+ julgando só os seus bloqueadores, ou, sem eles, só o delta `{sha anterior}..{sha novo}`. Bloqueador
24
+ que a correção fechou sai de `## Bloqueadores` (escreva `Nenhum.`) e o fechamento vai em `## Notas`;
25
+ a `## Checklist de tela` que vale é a da última seção, então repita-a inteira.
26
+ Parcial (limite de turnos): `PARCIAL` na 1ª linha, o feito e o que falta, SEM `**Verdict:**`.
27
+
28
+ **O que bloqueia — só com evidência de um destes tipos.** Todo o resto (estilo, nome, "eu faria
29
+ diferente") é nota; nota não bloqueia e não pede rodada nova. Na dúvida, sem evidência tipada, é nota.
30
+
31
+ | Tipo | Quem | Quando | Evidência mínima |
32
+ |------|------|--------|------------------|
33
+ | `check` | verificador | cenário `Cn` NÃO PROVADO ou FALHA | o que falta provar, ou o repro que falha |
34
+ | `teste` | verificador | teste vermelho; teste que não morde a regra que diz proteger | saída do verify, mutante sobrevivente, assert que passa pelo motivo errado |
35
+ | `regressao` | revisor | comportamento que funcionava e quebrou | consumidor afetado + o caminho que quebra |
36
+ | `escopo` | revisor | o diff faz o que nenhuma fatia pediu, ou deixa de fazer o que o contrato pede | arquivo/trecho fora das fatias, ou o item do contrato ausente |
37
+ | `arquitetura` | revisor | padrão ou abstração nova contra o stack e os standards | o trecho e o standard que ele contraria |
38
+ | `performance` | revisor | custo que o volume real torna visível | a consulta/lista e o volume que a derruba |
39
+ | `seguranca` | segurança | authz, segredo, injeção, PII, custo sem teto, migration destrutiva | o caminho de exploração |
40
+
41
+ ## Verificador (`4-review/verifier.md`)
42
+
43
+ Missão: **provar cada cenário** do `2-plan/checks.json`. O default de todo `Cn` é NÃO PROVADO;
44
+ critério que você não verificou nunca vira PROVADO por inferência.
27
45
 
28
- ## O que bloqueia — só com evidência de um destes tipos
29
-
30
- | Tipo | Quando | Evidência mínima |
31
- |------|--------|------------------|
32
- | `uc` | pós-condição de UC ou `doneCriteria` não atendida | repro (comando, request, teste) ou o trecho que prova |
33
- | `teste` | teste vermelho; teste novo que não morde a regra que diz proteger | saída do verify, mutante sobrevivente, ou o assert que passa pelo motivo errado |
34
- | `regressao` | comportamento que funcionava e quebrou | teste ou repro contra o comportamento anterior |
35
- | `seguranca` | segredo exposto, authz faltando, injeção, dado pessoal vazando, custo sem teto | o caminho de exploração |
46
+ ```markdown
47
+ ## Checks
36
48
 
37
- Todo o resto — estilo, nome, estrutura, "eu faria diferente" — é **nota**. Nota não bloqueia e não
38
- pede rodada nova. Na dúvida entre bloqueador e nota, sem evidência tipada, é nota.
49
+ | Cn | status | evidência |
50
+ |----|--------|-----------|
51
+ | C1 | PROVADO | `src/Orders/Create.cs:42` + `OrdersTests.RecusaSemItens` (morde: M1 morto) |
52
+ | C2 | NÃO PROVADO | nenhum teste exercita o caminho de escrita |
53
+ ```
39
54
 
40
- ## O que procurar, em ordem de dano
55
+ Uma linha por cenário, status exato `PROVADO`, `NÃO PROVADO` ou `FALHA`. O gate pausa com cenário
56
+ sem linha ou não PROVADO. Numa confirmação, uma `## Checks` nova só com os Cn que mudaram: vale a
57
+ última linha de cada Cn. Para cada `Cn`, em ordem de dano:
41
58
 
42
- 1. **Cada pós-condição de UC / `doneCriteria`**: onde no código ela vale, e qual teste a prova.
59
+ 1. **Onde vale**: `arquivo:linha` do código que entrega o cenário.
43
60
  2. **Fiação**: existe caminho da entrada real ao efeito observável? A camada pura testada não prova
44
61
  que o handler a chama. Inventarie quem consome o eixo novo.
45
- 3. **Teste que não morde**: nasceu verde e afirma o que já era verdade antes da mudança; só testa
46
- recusa e nunca o caminho de escrita; guard cujos dois lados passam pelo mesmo serializador;
47
- asserção sobre texto que mede vocabulário, não sentido.
62
+ 3. **O teste morde?** Nasceu verde e afirma o que já era verdade; só testa a recusa e nunca o caminho
63
+ de escrita; guard cujos dois lados passam pelo mesmo serializador; asserção sobre texto que mede
64
+ vocabulário, não sentido.
48
65
  4. **Verificações mecânicas** (.NET minimal API / Next): todo serviço injetado numa lambda de endpoint
49
66
  tem registro em `Program.cs` (senão a app não sobe); prefixo de rota server-direct e de proxy são
50
67
  constantes distintas, cada uma usada só do seu lado.
51
- 5. **Correção de rodada anterior**: o que ela abriu? Releia o comportamento vizinho.
52
- 6. **Fora do diff**: o blast radius que o orquestrador anexou (`graph affected`).
68
+ 5. **Runtime**: com o stack no ar (URL no dispatch), navegue o fluxo do cenário e confira console e
69
+ network. Stack fora do ar é achado de infra, não defeito seu a provar.
53
70
 
54
- ## Prova por mutação — opcional, no máximo 3
71
+ A suíte inteira é do orquestrador. Você roda no máximo um filtro de testes de um cenário, em primeiro
72
+ plano, por `morph-spec heavy run -- <comando>`.
55
73
 
56
- Só quando o diff tem uma linha de guarda ou regra que um UC exige e você não achou o teste que a
74
+ **Prova por mutação — opcional, no máximo 3, só depois que revisor e segurança terminaram.** Só
75
+ quando o diff tem uma linha de guarda ou regra que um cenário exige e você não achou o teste que a
57
76
  morde. Escreva `4-review/mutants.json` e rode `morph-spec mutate {feature}` **uma vez**:
58
77
 
59
78
  ```json
@@ -63,20 +82,15 @@ morde. Escreva `4-review/mutants.json` e rode `morph-spec mutate {feature}` **um
63
82
 
64
83
  `expect` é obrigatório: diga qual teste deve falhar e por quê **antes** de rodar — mutante cujo
65
84
  efeito você não sabe prever tende a ser equivalente. Barra invertida do código vira `{{BS}}`.
66
- Sobreviveu numa linha que um UC exige → bloqueador `teste`. Sobreviveu em outra → nota. Cole a seção
67
- `## Mutações` que a ferramenta imprime. A reversão é da ferramenta; rodada morta no meio:
85
+ Sobreviveu numa linha que um cenário exige → bloqueador `teste`. Sobreviveu em outra → nota. Cole a
86
+ seção `## Mutações` que a ferramenta imprime. A reversão é da ferramenta; rodada morta no meio:
68
87
  `morph-spec mutate --restore`.
69
88
 
70
- ## Condicionais
71
-
72
- **Feature que gera mídia** (alguma task cita em `standards[]` `standards/ai-agents/modalities-image-gen.md`,
89
+ **Feature que gera mídia** (alguma fatia cita em `standards:` `standards/ai-agents/modalities-image-gen.md`,
73
90
  `media-video.md` ou `media-pipeline.md`): grave `## Mídia gerada`, uma linha por afirmação — custo
74
91
  controlado, revisão humana, mídia marcada, hot path não bloqueado —, cada uma com o código que a prende
75
92
  e o teste que a prova. Afirmação falsa é bloqueador. Sem a seção, o gate pausa.
76
93
 
77
- **Feature que chama LLM**: dá para reconstruir o que foi ao provedor e o que ele respondeu neste turno?
78
- Não → nota, ou bloqueador quando um UC exige rastreabilidade.
79
-
80
94
  **Prompt ou modelo alterado**: o nó `evals` do verify é a prova; sem ele, o gate pausa sozinho.
81
95
 
82
96
  **Contrato de tela** (`1-design/mockups.md` com `## Tela:`): grave `## Checklist de tela`, uma linha por
@@ -86,5 +100,32 @@ item decidido de cada tela:
86
100
 
87
101
  Veredito `cumpre` / `diverge` / `não verificável` (com o motivo). Paginação, ordenação ou filtro que o
88
102
  checklist põe no servidor e a tela faz em memória, ou estado vazio/carregando/erro decidido e
89
- ausente, é bloqueador `uc`. Divergência registrada em `decisions.md` é nota. Sem a seção, havendo
90
- item decidido, o gate pausa.
103
+ ausente, é bloqueador `check`. Divergência registrada em `plan.md ## Decisões` é nota. Sem a seção,
104
+ havendo item decidido, o gate pausa.
105
+
106
+ ## Revisor (`4-review/reviewer.md`)
107
+
108
+ Read-only, sobre o commit do dispatch: `git diff {base}..{sha}`, `git show {sha}:<caminho>`. Nunca a
109
+ árvore de trabalho — o verificador pode estar com um mutante aplicado nela. Default SHIP.
110
+
111
+ 1. **Escopo**: o diff contra as fatias (`arquivos:`) e o `## Contrato de implementação` do plano.
112
+ 2. **Arquitetura**: padrão ou abstração nova contra o stack do projeto e os standards (camada que o
113
+ projeto não usa, repositório sobre o DbContext, dependência nova sem decisão no plano).
114
+ 3. **Regressão**: para cada símbolo público alterado, `morph-spec graph affected <símbolo> --prod --json`.
115
+ Sem grafo (`no-graph`, `not-found`, `ambiguous`), caia para `git grep -n <símbolo> {sha}` e siga — a
116
+ ausência de grafo nunca bloqueia, e ausência de consumidor nunca sai do grafo.
117
+ 4. **O que a correção abriu**: revisando a correção de uma rodada anterior, releia o comportamento
118
+ vizinho ao que ela mudou.
119
+ 5. **Performance só com evidência**: lista sem paginação no servidor sobre volume real, N+1 visível.
120
+ 6. **Chamada a LLM**: dá para reconstruir o que foi ao provedor e o que ele respondeu neste turno?
121
+ Não → nota.
122
+
123
+ ## Segurança (`4-review/security.md`)
124
+
125
+ Despachada só quando o diff toca auth, segredo, permissão, dado pessoal, pagamento ou migration.
126
+ Mesmo contrato de leitura do revisor (só o SHA). Procure: authz faltando ou checada do lado errado;
127
+ segredo no código, no log ou no cliente; injeção (SQL, comando, prompt); dado pessoal vazando para log,
128
+ resposta ou provedor; custo sem teto (chamada paga em laço, sem limite por usuário); mídia publicada
129
+ sem revisão humana; migration destrutiva (DROP/rename de coluna com dado, branch Neon de produção).
130
+ Bloqueador `seguranca` exige o caminho de exploração — quem, por qual entrada, chega a quê. Sem
131
+ caminho, é nota.
@@ -12,7 +12,8 @@ framework/hooks/
12
12
  │ │ ├── worktree-create.js # WorktreeCreate: governed tree via `morph-spec worktree setup|provision`
13
13
  │ │ ├── worktree-remove.js # WorktreeRemove: safe teardown via `worktree remove --if-clean`
14
14
  │ │ ├── block-destructive-worktree-removal.js # PreToolUse(Bash): raw removal follows junctions out
15
- │ │ └── block-raw-worktree-add.js # PreToolUse(Bash): raw `git worktree add` is ungoverned
15
+ │ │ ├── block-raw-worktree-add.js # PreToolUse(Bash): raw `git worktree add` is ungoverned
16
+ │ │ └── warn-bash-source-write.js # PreToolUse(Bash): source written by redirect skips the Write/Edit guards
16
17
  │ ├── user-prompt/
17
18
  │ │ └── enrich-prompt.js # Context-aware prompt enrichment
18
19
  │ ├── pre-tool-use/
@@ -32,8 +33,7 @@ framework/hooks/
32
33
  ├── git/ # Git hooks (Bash)
33
34
  │ ├── pre-commit/
34
35
  │ │ ├── orchestrator.sh # Master hook dispatcher
35
- │ │ ├── agents.sh # Validates agents.json schema
36
- │ │ └── specs.sh # Validates spec.md sections
36
+ │ │ └── agents.sh # Validates agents.json schema
37
37
  │ ├── commit-msg/
38
38
  │ │ └── conventional-commits.sh # Enforces conventional commits
39
39
  │ └── pre-push/
@@ -45,18 +45,17 @@ framework/hooks/
45
45
 
46
46
  | Event | Hook | Type | Purpose |
47
47
  |-------|------|------|---------|
48
- | **SessionStart** | worktree-context.js | Context inject | Secondary worktree → feature identity (name/branch/phase) + missing-junction warning; primary root → active-feature summary (phase, task stats, pending gates), silent if none. Also seeds a worktree that has no codebase graph from the root's, and launches the background refresh of the ROOT's graph (`shared/graph-refresh.js`) |
48
+ | **SessionStart** | worktree-context.js | Context inject | Secondary worktree → feature identity (name/branch/phase) + missing-junction warning; primary root → active-feature summary (phase, slices done/total, pending gates), silent if none. Also seeds a worktree that has no codebase graph from the root's, and launches the background refresh of the ROOT's graph (`shared/graph-refresh.js`) |
49
49
  | **PreToolUse** (Write\|Edit) | _(native permissions.deny)_ | Block | Blocks edits to state.json and .morph/framework/ |
50
- | **PreToolUse** (Write\|Edit) | protect-spec-files.js | Block | Blocks edits to spec files after approval; for tasks.json validates the mutable-field diff, the status enum, and requires a `notes` when `outputs` diverge from the plan |
50
+ | **PreToolUse** (Write\|Edit) | protect-spec-files.js | Block | Blocks edits to `plan.md` (and the uiux artifacts) after their gate is approved; an append-only `## Errata` block at the end of `plan.md` passes |
51
51
  | **PreToolUse** (Write\|Edit) | enforce-phase-writes.js | Block | Ensures writes go to current phase directory |
52
52
  | **PreToolUse** (Bash) | pre-bash-denylist.js | Block | Blocks `rm -rf .morph/` and direct state edits |
53
53
  | **PreToolUse** (Bash) | block-destructive-worktree-removal.js | Block | Blocks `git worktree remove` / `rm -rf` / `Remove-Item -Recurse` on a directory holding reparse points (the infra junctions a raw removal would FOLLOW out, emptying the root's `.claude/`); names `morph-spec worktree remove` |
54
54
  | **PreToolUse** (Bash) | block-raw-worktree-add.js | Block | Blocks raw `git worktree add` (a tree with no junctions → no hooks, no port block, no identity, outside `worktrees/`); names `morph-spec worktree setup <feature>` / `worktree provision <name> --base <ref>`. `list`/`prune`/`remove`/`lock`/`move`/`repair` pass |
55
+ | **PreToolUse** (Bash) | warn-bash-source-write.js | Warn | Heredoc/redirect/`tee` into project source (`isProjectSource`): context naming the file, the command still runs — Write/Edit is what phase, protected-file and comment guards see |
55
56
  | **WorktreeCreate** | worktree-create.js | Replaces native | Fires for `claude -w`, EnterWorktree and `Agent isolation:"worktree"`; replaces Claude Code's bare `git worktree add`. Runs `morph-spec worktree setup <n>` (known feature) or `worktree provision <n>` (task tree, branch `morph-task/<n>`), bases a task tree on the dispatcher's `morph/<f>` branch when `worktree.baseRef` is unset (`"head"` = current HEAD, `"fresh"` = default branch), prints the path as its last stdout line. CLI unreachable → plain git, still under `<root>/worktrees/` |
56
57
  | **WorktreeRemove** | worktree-remove.js | Fail-open | Routes Claude Code's own cleanup through `morph-spec worktree remove --target --if-clean` (junctions dropped first; a dirty tree is left alone). Always exit 0. Secondary net — the event was observed NOT to fire headless; the Bash guard above stays the primary defense |
57
- | **PostToolUse** (Write\|Edit) | tasks-json-guard.js | Advisory | Validates the written tasks.json schema (status/effort enums); warns so the LLM fixes it immediately |
58
- | **PostToolUse** (Write\|Edit) | trace-autogen.js | State sync | Auto-generates `.morph/traces/YYYY-MM-DD-{feature}-implement.json` from tasks.json + recap.md on every tasks.json write — the skills never write traces by hand |
59
- | **PostToolUse** (Write\|Edit) | comment-guard.js | Blocking | Blocks when the edit ADDED a comment the `architecture-code-comments` standard never allows: process history (UC/ADR/task/gate), banner/`#region`, a block over 3 lines, or a doc-comment outside a published package. Old comments are never reported; last link of the chain |
58
+ | **PostToolUse** (Write\|Edit) | comment-guard.js | Blocking | Blocks when the edit ADDED a comment the `architecture-code-comments` standard never allows: process history (ADR, scenario/slice id, gate, plan.md), banner/`#region`, a block over 3 lines, or a doc-comment outside a published package. Old comments are never reported; last link of the chain |
60
59
  | **PostToolUse** (Write\|Edit\|AskUserQuestion) | state-sync.js | State sync | Autonomous state updates (feature register, output tracking, gate approval) |
61
60
  | **PostToolUse** (Write\|Edit\|AskUserQuestion) | gate-guard.js | Advisory | Warns (never blocks) when a gate action disagrees with the persisted gateDecisions; logs one `coherent`/`divergent` line per check in `.morph/logs/activity.jsonl` (issue #63) |
62
61
  | **PostToolUse** (all tools) | telemetry-log.js | Telemetry | Appends every tool call (skill, sub-agent dispatch + prompt, knowledge-base read, bash/edit) to `.morph/logs/events.jsonl`; silent, fail-open — the NLH execution-path capture read back by `morph-spec telemetry` |
@@ -66,7 +65,7 @@ framework/hooks/
66
65
  | **PostToolBatch** | telemetry-log.js | Telemetry | One `ToolBatch` event per model turn that used tools (the turn counter of `cost --metrics`) |
67
66
  | **SubagentStop** | telemetry-log.js | Telemetry | `AgentUsage`: the sub-agent's token usage read from its own transcript, as a delta per agent; never blocks the stop |
68
67
  | **UserPromptSubmit** | telemetry-log.js | Telemetry | Appends the user's prompt to the same `events.jsonl` stream; silent, fail-open |
69
- | **Stop** | validate-completion.js | Advisory | Warns about incomplete tasks/missing outputs/recap drift (hash-deduped, only re-injects when the warning set changes) |
68
+ | **Stop** | validate-completion.js | Advisory | Warns about incomplete slice reports (hash-deduped, only re-injects when the warning set changes) |
70
69
 
71
70
  ## Design Principles
72
71
 
@@ -150,8 +149,8 @@ Claude Code sends JSON to stdin: { tool_input: { file_path: "..." } }
150
149
  ↓
151
150
  protect-spec-files.js
152
151
  ├── Is in .morph/features/{feature}/?
153
- │ ├── Is spec.md and design gate approved? → BLOCK
154
- │ ├── Is tasks.json and tasks gate approved? → BLOCK
152
+ │ ├── Is plan.md and plan gate approved? → BLOCK (unless append-only ## Errata)
153
+ │ ├── Is a 1-design/ artifact and uiux gate approved? → BLOCK
155
154
  │ └── Gate not approved → pass
156
155
  └── Not a feature file → pass
157
156
  ↓
@@ -6,70 +6,31 @@ import { stateExists } from '../../shared/state-reader.js';
6
6
  import { scanForBlockers } from '../../shared/reparse-scan.js';
7
7
  import { tokenize } from '../../shared/command-tokens.js';
8
8
 
9
- /**
10
- * block-destructive-worktree-removal — the only defense that does not depend on
11
- * someone reading documentation.
12
- *
13
- * A morph worktree carries JUNCTIONS to the primary root's shared infra
14
- * (`node_modules`, `.claude`, `.morph/framework`). Every generic recursive
15
- * removal — `git worktree remove` (plain AND `--force`), `rm -rf`,
16
- * `Remove-Item -Recurse`, `rmdir /s` — FOLLOWS those links out of the worktree
17
- * and deletes what they point at, leaving the primary root's `.claude/` and
18
- * `.morph/framework/` as empty directories. That is the incident this hook
19
- * exists for, and it is not hypothetical: it happened, and the command was
20
- * copied from a message the CLI itself printed.
21
- *
22
- * `morph-spec worktree remove` is safe because it drops the junctions BEFORE
23
- * letting git near the directory. This hook is what stops the unsafe path from
24
- * being reachable by accident.
25
- *
26
- * DELIBERATELY NARROW — an inert or over-eager guardrail is worse than none:
27
- * - only recursive-removal shapes, only when a target path can be extracted;
28
- * - only when that path exists AND the scan finds reparse points under it
29
- * (an already-unlinked worktree, or one that never had junctions, passes);
30
- * - anything unparseable passes.
31
- * The scan is the authority, not the path shape: a worktree kept outside
32
- * `worktrees/` is protected just the same, and a `worktrees/` directory with
33
- * nothing to traverse is not blocked for looking dangerous.
34
- */
9
+ // Generic recursive removals FOLLOW a worktree's junctions and empty the root's `.claude/` and `.morph/framework/`.
10
+ // The reparse scan is the authority, not the path shape; anything unparseable passes.
35
11
 
36
- // The tokenizer lives in shared/command-tokens.js — block-raw-worktree-add.js
37
- // reads `git worktree add` off the same tokens, and a hook must never import a
38
- // sibling hook (its standalone tail would fire in the importer's process).
12
+ // A hook never imports a sibling hook (its standalone tail would fire), so the tokenizer lives in shared/.
39
13
  export { tokenize };
40
14
 
41
- /**
42
- * The path a recursive-removal command would destroy, or null when this is not
43
- * such a command (or the target cannot be read off it).
44
- *
45
- * @param {string} command
46
- * @returns {string|null}
47
- */
48
15
  export function extractRemovalTarget(command) {
49
16
  const tokens = tokenize(command);
50
17
  if (!tokens.length) return null;
51
18
  const lower = tokens.map((t) => t.toLowerCase());
52
19
 
53
- // `git worktree remove [--force] <path>` — the path is always the last
54
- // non-flag token; git has no form of this command without one.
20
+ // The path is always the last non-flag token.
55
21
  const gi = lower.indexOf('git');
56
22
  if (gi !== -1 && lower[gi + 1] === 'worktree' && lower[gi + 2] === 'remove') {
57
23
  const rest = tokens.slice(gi + 3).filter((t) => !t.startsWith('-'));
58
24
  return rest.length ? rest[rest.length - 1] : null;
59
25
  }
60
26
 
61
- // Generic recursive deletes. Each needs BOTH its command word and a recursive
62
- // flag — a plain `rm file` or `Remove-Item file` cannot traverse a junction's
63
- // contents and is none of this hook's business.
27
+ // Needs both the command word and a recursive flag: a plain `rm file` cannot traverse a junction.
64
28
  const recursive = tokens.some((t) => /^(-[a-z]*r[a-z]*|--recursive|\/s)$/i.test(t));
65
29
  const removalCmd = lower.find((t) => /^(rm|remove-item|ri|rd|rmdir|del)$/.test(t));
66
30
  if (!removalCmd || !recursive) return null;
67
31
 
68
32
  const idx = lower.indexOf(removalCmd);
69
- // A cmd.exe switch is a slash plus a letter or two (`rmdir /s /q`). Anything
70
- // longer that starts with `/` is a POSIX ABSOLUTE PATH — filtering on the
71
- // bare `/` made every Linux target look like a flag, so `rm -rf /tmp/wt`
72
- // extracted nothing and the hook passed exactly where it had to block.
33
+ // A cmd.exe switch is a slash plus one or two letters; anything longer is a POSIX absolute path.
73
34
  const isCmdSwitch = (t) => /^\/[a-z]{1,2}$/i.test(t);
74
35
  const operands = tokens.slice(idx + 1).filter((t) => !t.startsWith('-') && !isCmdSwitch(t));
75
36
  // Named-parameter form: `Remove-Item -Path <p> -Recurse`.
@@ -78,10 +39,6 @@ export function extractRemovalTarget(command) {
78
39
  return operands.length ? operands[0] : null;
79
40
  }
80
41
 
81
- /**
82
- * @param {object} payload - parsed PreToolUse stdin payload (Bash matcher)
83
- * @returns {Promise<object|null>} `{decision:'block',reason}` or null (pass).
84
- */
85
42
  export async function run(payload) {
86
43
  try {
87
44
  if (!stateExists()) return null;
@@ -5,56 +5,23 @@ import { stateExists } from '../../shared/state-reader.js';
5
5
  // standalone tail (stdin read + exit) inside THIS process.
6
6
  import { tokenize } from '../../shared/command-tokens.js';
7
7
 
8
- /**
9
- * block-raw-worktree-add — PreToolUse (Bash) guard against `git worktree add`.
10
- *
11
- * A worktree created by raw git is a worktree morph cannot see: no infra
12
- * junctions (`.claude`, `node_modules`, `.morph/framework` — so NO hooks run
13
- * in there), no port block (its dev server collides with the root's), no
14
- * feature/task identity (invisible to `fleet`, `doctor --worktrees`,
15
- * `worktree list`), and a location git chose, not `<root>/worktrees/`. The
16
- * sibling guard (block-destructive-worktree-removal) protects the way OUT;
17
- * this one protects the way IN, by naming the two commands that build a
18
- * governed tree:
19
- *
20
- * morph-spec worktree setup <feature> — a feature's tree
21
- * morph-spec worktree provision <name> [--base <ref>] — a task-isolation tree
22
- *
23
- * DELIBERATELY NARROW: only the `add` subcommand. `list`, `prune`, `remove`
24
- * (the sibling's business), `lock`, `unlock`, `move`, `repair` all pass. The
25
- * `git` token may be followed by global options (`-C <dir>`, `--git-dir=<d>`,
26
- * `-c k=v`) before `worktree`, and may sit anywhere in a chained command
27
- * (`cd a && git worktree add b`) — every `git` occurrence is checked.
28
- */
29
-
30
- /** Global `git` options that take a separate value: `git -C <dir> worktree add`. */
31
8
  const GIT_GLOBAL_WITH_VALUE = new Set(['-c', '-C', '--git-dir', '--work-tree', '--namespace', '--exec-path', '--config-env']);
32
9
 
33
- /**
34
- * Does this command line invoke `git worktree add` anywhere?
35
- * @param {string} command
36
- * @returns {boolean}
37
- */
38
10
  export function isRawWorktreeAdd(command) {
39
11
  const tokens = tokenize(command);
40
12
  const lower = tokens.map((t) => t.toLowerCase());
41
13
  for (let i = 0; i < lower.length; i++) {
42
14
  if (lower[i] !== 'git' && !/(^|[\\/])git(\.exe)?$/.test(lower[i])) continue;
43
15
  let j = i + 1;
44
- // Skip global options between `git` and the subcommand.
45
16
  while (j < lower.length && lower[j].startsWith('-')) {
46
- if (GIT_GLOBAL_WITH_VALUE.has(tokens[j])) j += 2; // `-C dir` consumes its value
47
- else j += 1; // `--git-dir=x`, `--no-pager`, …
17
+ if (GIT_GLOBAL_WITH_VALUE.has(tokens[j])) j += 2;
18
+ else j += 1;
48
19
  }
49
20
  if (lower[j] === 'worktree' && lower[j + 1] === 'add') return true;
50
21
  }
51
22
  return false;
52
23
  }
53
24
 
54
- /**
55
- * @param {object} payload - parsed PreToolUse stdin payload (Bash matcher)
56
- * @returns {Promise<object|null>} `{decision:'block',reason}` or null (pass).
57
- */
58
25
  export async function run(payload) {
59
26
  try {
60
27
  if (!stateExists()) return null;
@@ -1,54 +1,6 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * chain.js — roda vários hooks core num único processo node.
4
- *
5
- * Motivação (onda de performance R2): cada hook registrado no Claude Code é
6
- * hoje um processo node próprio (~60ms de cold start). Um único Write/Edit
7
- * disparava até 9 desses processos — 2 PreToolUse + 3+2+2 PostToolUse. Este
8
- * script agrupa os elos de um mesmo event+matcher (a mesma entrada do
9
- * registry em src/utils/hooks-installer.js) num só spawn: lê stdin UMA vez,
10
- * importa e chama `run(payload)` de cada hook em sequência, e combina as
11
- * respostas — reduzindo esses 9 spawns para 4 por Write/Edit (1 PreToolUse +
12
- * 3 PostToolUse: Write|Edit / Write|Edit|AskUserQuestion / matcher-`*`).
13
- *
14
- * Uso: `node chain.js <hook1> <hook2> ...` — nomes de arquivo SEM `.js`,
15
- * resolvidos no mesmo diretório deste script (core/), na ORDEM em que devem
16
- * rodar. A ordem é a mesma que o registry antigo (duas entries separadas)
17
- * já implicava — ver o comentário de cada grupo em hooks-installer.js.
18
- *
19
- * Cada hook core exporta `run(payload)` (ver framework/hooks/shared/hook-response.js
20
- * `runStandalone`) — o arquivo continua executável standalone (spawnSync
21
- * direto, como os testes já fazem) porque a própria cauda do arquivo só lê
22
- * stdin/imprime/sai quando NÃO está sendo importado por esta cadeia.
23
- *
24
- * Regras de combinação:
25
- * - PreToolUse (decision:'block'): o PRIMEIRO elo que retornar um bloqueio
26
- * VENCE e encerra a cadeia — os elos seguintes nem chegam a rodar. Isto é
27
- * uma otimização NOVA desta consolidação (antes, Claude Code podia
28
- * disparar os dois processos de qualquer forma); o resultado observável
29
- * pro Claude Code é idêntico (o tool call é bloqueado do mesmo jeito).
30
- * - PostToolUse / outros eventos de contexto: os textos de TODOS os elos que
31
- * retornarem `hookSpecificOutput.additionalContext` (ou `decision:'approve'`
32
- * com additionalContext) são concatenados, na ORDEM dos argv, separados por
33
- * uma linha em branco — nenhum elo é pulado por um vizinho ter respondido.
34
- * - Nome do evento (hookSpecificOutput.hookEventName) no resultado final:
35
- * o valor que o PRIMEIRO elo a declarar um venceu (mesma regra de
36
- * "primeiro vence" do bloqueio acima — na prática todos os elos de um
37
- * mesmo grupo observam o MESMO evento real do Claude Code, então isto só
38
- * importa como desempate determinístico); na ausência de qualquer elo
39
- * declarando, cai para `payload.hook_event_name`; por último, `'PostToolUse'`.
40
- * - updatedInput (PreToolUse): um elo pode devolver
41
- * `hookSpecificOutput.updatedInput` (ver `updatedInputResult` em
42
- * hook-response.js) para reescrever o tool_input. O ÚLTIMO elo a devolver um
43
- * vence (só um elo do grupo reescreve, por construção). `permissionDecision:
44
- * 'allow'` + `updatedInput` só entram no resultado final quando algum elo
45
- * reescreveu — um `allow` incondicional pularia o prompt de permissão de
46
- * TODO Bash, que é exatamente o contrário de um guardrail.
47
- * - Fail-open POR ELO: uma exceção não tratada por um hook individual (import
48
- * falhou, run() não existe, etc.) não derruba a cadeia — os elos seguintes
49
- * ainda rodam. Cada hook já fail-opens sozinho dentro do seu próprio
50
- * try/catch; isto é a segunda rede de segurança, para o import em si.
51
- */
2
+ // Usage: node chain.js <hook> ... (names without .js, run in argv order). The first block wins and ends the chain,
3
+ // the contexts of every link concatenate, and an exception in one link never stops the next.
52
4
 
53
5
  import { dirname, join } from 'path';
54
6
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -56,20 +8,12 @@ import { readStdin } from '../../shared/stdin-reader.js';
56
8
 
57
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
58
10
 
59
- // Marca "já estou dentro de uma cadeia" para os hooks importados abaixo: a
60
- // cauda de execução standalone de cada hook (runStandalone() em
61
- // hook-response.js) só dispara quando esta flag NÃO está setada. Setada
62
- // ANTES do primeiro import dinâmico — cada hook importado herda o mesmo
63
- // process.env deste processo.
11
+ // Set before the first import: each hook's standalone tail runs only when this is unset.
64
12
  process.env.MORPH_HOOK_CHAINED = '1';
65
13
 
66
- // Override só para testes da mecânica genérica desta cadeia (fixtures fora
67
- // de core/, sem precisar ser hooks reais nem satisfazer as convenções que
68
- // hook-wiring.test.js exige de todo arquivo em core/). Produção nunca seta
69
- // isto — resolve sempre no próprio diretório deste script.
14
+ // Test-only override; production always resolves next to this script.
70
15
  const coreDir = process.env.MORPH_HOOK_CORE_DIR || __dirname;
71
16
 
72
- /** Extrai o texto de contexto de um resultado de hook, se houver. */
73
17
  function contextOf(result) {
74
18
  if (!result) return null;
75
19
  if (result.decision === 'approve' && typeof result.additionalContext === 'string') {
@@ -93,13 +37,11 @@ async function main() {
93
37
  const mod = await import(pathToFileURL(join(coreDir, `${name}.js`)).href);
94
38
  result = await mod.run(payload);
95
39
  } catch {
96
- // Fail-open POR ELO — ver doc do módulo acima.
97
40
  result = null;
98
41
  }
99
42
 
100
43
  if (!result) continue;
101
44
 
102
- // PreToolUse: o primeiro bloqueio vence e encerra a cadeia.
103
45
  if (result.decision === 'block') {
104
46
  // A later link blocking must not swallow what earlier links reported (a tsc error on the same edit).
105
47
  if (contexts.length) result = { ...result, reason: [...contexts, result.reason].join('\n\n') };
@@ -116,9 +58,6 @@ async function main() {
116
58
  const ctx = contextOf(result);
117
59
  if (ctx) {
118
60
  contexts.push(ctx);
119
- // First link to declare a name wins (mirrors block-first-wins above) —
120
- // in practice every link in a group observes the SAME real Claude Code
121
- // event, so this only matters as a deterministic tie-break.
122
61
  eventName = eventName || result?.hookSpecificOutput?.hookEventName;
123
62
  }
124
63
  }
@@ -1,17 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- /**
4
- * PostToolUse Hook: comments the edit added in a shape the
5
- * `architecture-code-comments` standard never allows.
6
- *
7
- * Event: PostToolUse | Matcher: Write|Edit
8
- *
9
- * `decision: block` makes the agent fix it in the same turn; the edit itself
10
- * already landed. Only comments absent from `tool_response.originalFile`
11
- * count, so editing a legacy file never reports its old comments.
12
- *
13
- * Fail-open: any error passes silently.
14
- */
3
+ // `decision: block` makes the agent fix it in the same turn (the edit already landed). Only comments absent from
4
+ // `tool_response.originalFile` count, so editing a legacy file never reports its old comments.
15
5
 
16
6
  import { readFileSync } from 'fs';
17
7
  import { isAbsolute, relative, resolve } from 'path';
@@ -23,7 +13,7 @@ import { isGenerated, isProjectSource } from '../../shared/source-files.js';
23
13
  const MAX_LISTED = 10;
24
14
 
25
15
  const RULE_LABEL = {
26
- process: 'história de processo (UC/ADR/task/gate)',
16
+ process: 'história de processo (ADR, cenário, fatia, gate, plan.md)',
27
17
  banner: 'banner ou #region',
28
18
  'doc-comment': '///·JSDoc em código que não é pacote publicado',
29
19
  'long-block': `bloco com mais de ${MAX_BLOCK_LINES} linhas`,
@@ -78,10 +68,6 @@ function describe(file, findings) {
78
68
  ].join('\n');
79
69
  }
80
70
 
81
- /**
82
- * @param {object} payload - PostToolUse stdin payload
83
- * @returns {Promise<object|null>}
84
- */
85
71
  export async function run(payload) {
86
72
  try {
87
73
  if (!['Write', 'Edit'].includes(payload?.tool_name)) return null;
@@ -1,8 +1,6 @@
1
1
  {
2
- "version": "1.0.0",
2
+ "version": "1.2.0",
3
3
  "patterns": [
4
- "rm -rf /",
5
- "rm -rf ~",
6
4
  "DROP TABLE",
7
5
  "DROP DATABASE",
8
6
  "format c:",
@@ -10,5 +8,37 @@
10
8
  "> /dev/sda",
11
9
  "dd if=/dev/zero",
12
10
  ":(){:|:&};:"
11
+ ],
12
+ "executableArgs": [
13
+ {
14
+ "executable": "taskkill",
15
+ "args": [
16
+ "/im",
17
+ "-im"
18
+ ],
19
+ "reason": "mata por nome de imagem e derruba os MCPs de todas as sessões; encerre pelo PID que você subiu (`/PID <n>`)"
20
+ },
21
+ {
22
+ "executable": "rm",
23
+ "flags": [
24
+ [
25
+ "-r",
26
+ "-R",
27
+ "--recursive"
28
+ ],
29
+ [
30
+ "-f",
31
+ "--force"
32
+ ]
33
+ ],
34
+ "args": [
35
+ "/",
36
+ "/*",
37
+ "~",
38
+ "~/",
39
+ "~/*"
40
+ ],
41
+ "reason": "apaga a raiz ou o home inteiro; remova o caminho específico"
42
+ }
13
43
  ]
14
44
  }