@polymorphism-tech/morph-spec 8.46.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (304) hide show
  1. package/CLAUDE.md +53 -44
  2. package/README.md +3 -4
  3. package/bin/morph-spec.js +38 -55
  4. package/bin/validate.js +5 -134
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +2 -2
  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 +75 -18
  11. package/framework/commands/create-umbraco-lp.md +2 -0
  12. package/framework/commands/migrate-lp-to-umbraco.md +2 -0
  13. package/framework/commands/morph-apply.md +5 -28
  14. package/framework/commands/morph-archive.md +2 -2
  15. package/framework/commands/morph-hotfix.md +8 -8
  16. package/framework/commands/morph-infra.md +17 -203
  17. package/framework/commands/morph-media.md +3 -0
  18. package/framework/commands/morph-preflight.md +7 -31
  19. package/framework/commands/morph-proposal.md +2 -50
  20. package/framework/commands/morph-status.md +3 -3
  21. package/framework/commands/morph-troubleshoot.md +2 -14
  22. package/framework/docs/CLI.md +585 -286
  23. package/framework/evals/review-checklist.md +82 -41
  24. package/framework/hooks/README.md +10 -11
  25. package/framework/hooks/claude-code/core/comment-guard.js +1 -1
  26. package/framework/hooks/claude-code/core/denylist.json +33 -3
  27. package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
  28. package/framework/hooks/claude-code/core/gate-guard.js +2 -11
  29. package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
  30. package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
  31. package/framework/hooks/claude-code/core/state-sync.js +17 -19
  32. package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
  33. package/framework/hooks/claude-code/core/validate-completion.js +33 -97
  34. package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
  35. package/framework/hooks/claude-code/core/worktree-context.js +5 -4
  36. package/framework/hooks/claude-code/statusline.py +55 -41
  37. package/framework/hooks/package.json +3 -0
  38. package/framework/hooks/shared/activity-logger.js +1 -1
  39. package/framework/hooks/shared/build-check.js +10 -6
  40. package/framework/hooks/shared/command-tokens.js +169 -0
  41. package/framework/hooks/shared/comment-rules.js +1 -1
  42. package/framework/hooks/shared/comment-scanner.js +10 -23
  43. package/framework/hooks/shared/comment-stripper.js +1 -1
  44. package/framework/hooks/shared/e2e-check.js +67 -31
  45. package/framework/hooks/shared/errata.js +72 -0
  46. package/framework/hooks/shared/feature-locator.js +1 -1
  47. package/framework/hooks/shared/frontend-locations.js +6 -0
  48. package/framework/hooks/shared/gate-question.js +18 -0
  49. package/framework/hooks/shared/phase-utils.js +15 -32
  50. package/framework/hooks/shared/slice-progress.js +118 -0
  51. package/framework/hooks/shared/spawn-command.js +23 -0
  52. package/framework/hooks/shared/state-reader.js +61 -87
  53. package/framework/hooks/shared/state-writer.js +3 -3
  54. package/framework/mcp/registry.json +3 -4
  55. package/framework/phases.json +23 -39
  56. package/framework/rules/ai-agents-standards.md +18 -16
  57. package/framework/rules/asaas-standards.md +3 -1
  58. package/framework/rules/csharp-standards.md +10 -8
  59. package/framework/rules/data-standards.md +5 -5
  60. package/framework/rules/dotnet-templates.md +1 -1
  61. package/framework/rules/frontend-standards.md +4 -2
  62. package/framework/rules/gsap-standards.md +10 -5
  63. package/framework/rules/hangfire-standards.md +3 -1
  64. package/framework/rules/infrastructure-standards.md +4 -2
  65. package/framework/rules/neon-auth-standards.md +3 -1
  66. package/framework/rules/neon-standards.md +6 -4
  67. package/framework/rules/nextjs-standards.md +13 -11
  68. package/framework/rules/nextjs-templates.md +1 -1
  69. package/framework/rules/resend-standards.md +3 -1
  70. package/framework/schemas/config.schema.json +14 -9
  71. package/framework/schemas/feature.schema.json +15 -52
  72. package/framework/skills/README.md +4 -5
  73. package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
  74. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
  75. package/framework/skills/core/morph-apply/SKILL.md +150 -223
  76. package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
  77. package/framework/skills/core/morph-machine/SKILL.md +4 -4
  78. package/framework/skills/core/morph-media/SKILL.md +3 -3
  79. package/framework/skills/core/morph-plan/SKILL.md +138 -300
  80. package/framework/skills/core/morph-review/SKILL.md +112 -137
  81. package/framework/skills/core/morph-standards/SKILL.md +29 -41
  82. package/framework/skills/core/morph-uiux/SKILL.md +78 -153
  83. package/framework/standards/STANDARDS.json +90 -1219
  84. package/framework/standards/STANDARDS.meta.json +1305 -0
  85. package/framework/standards/ai-agents/evals-with-cache.md +2 -3
  86. package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
  87. package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
  88. package/framework/standards/ai-agents/prompt-sources.md +6 -4
  89. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  90. package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
  91. package/framework/standards/ai-agents/setup.md +2 -2
  92. package/framework/standards/ai-agents/structured-output.md +1 -1
  93. package/framework/standards/ai-agents/sweet-spot.md +1 -1
  94. package/framework/standards/architecture/code-comments.md +8 -8
  95. package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
  96. package/framework/standards/backend/api/validation.md +135 -47
  97. package/framework/standards/backend/database/postgresql/database.md +11 -7
  98. package/framework/standards/backend/database/vector-search-rag.md +207 -324
  99. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
  100. package/framework/standards/backend/dotnet/result-pattern.md +40 -6
  101. package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
  102. package/framework/standards/backend/dotnet/scrutor.md +1 -1
  103. package/framework/standards/backend/dotnet/testing.md +1 -1
  104. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
  105. package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
  106. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
  107. package/framework/standards/data/nosql/cache/redis.md +120 -44
  108. package/framework/standards/data/nosql/cosmos-db.md +114 -71
  109. package/framework/standards/frontend/design-system/animations.md +1 -1
  110. package/framework/standards/frontend/design-system/naming.md +1 -1
  111. package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
  112. package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
  113. package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
  114. package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
  115. package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
  116. package/framework/standards/integration/mcp/mcp-tools.md +7 -7
  117. package/framework/templates/README.md +2 -3
  118. package/framework/templates/REGISTRY.json +6 -170
  119. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
  120. package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
  121. package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
  122. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
  123. package/framework/templates/docs/plan.md +72 -0
  124. package/framework/templates/dotnet/ai-kit/README.md +1 -9
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
  127. package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
  128. package/framework/templates/infrastructure/github/README.md +2 -1
  129. package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
  130. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
  131. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
  132. package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
  133. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
  134. package/package.json +1 -1
  135. package/src/commands/e2e/e2e.js +2 -2
  136. package/src/commands/observability/cost.js +18 -60
  137. package/src/commands/observability/telemetry.js +3 -5
  138. package/src/commands/project/doctor-procs.js +1 -1
  139. package/src/commands/project/doctor.js +9 -9
  140. package/src/commands/project/fleet.js +4 -4
  141. package/src/commands/project/heavy.js +3 -3
  142. package/src/commands/project/init.js +6 -1
  143. package/src/commands/project/setup-infra-cmd.js +2 -1
  144. package/src/commands/project/stack-scan.js +58 -0
  145. package/src/commands/project/standards.js +79 -25
  146. package/src/commands/project/status.js +29 -69
  147. package/src/commands/project/update.js +33 -19
  148. package/src/commands/project/worktree.js +1 -1
  149. package/src/commands/state/advance.js +62 -46
  150. package/src/commands/state/approve.js +95 -138
  151. package/src/commands/state/archive.js +20 -84
  152. package/src/commands/state/auto-approval.js +68 -0
  153. package/src/commands/state/create.js +28 -1
  154. package/src/commands/state/delete.js +1 -1
  155. package/src/commands/state/finish.js +11 -23
  156. package/src/commands/state/gate-check.js +86 -95
  157. package/src/commands/state/gate-decision.js +6 -4
  158. package/src/commands/state/handoff.js +1 -1
  159. package/src/commands/state/list.js +9 -9
  160. package/src/commands/state/report-check.js +157 -311
  161. package/src/commands/state/retype.js +9 -3
  162. package/src/commands/validation/mutate.js +77 -14
  163. package/src/commands/validation/plan-check.js +81 -0
  164. package/src/commands/validation/validate-feature.js +60 -135
  165. package/src/commands/validation/verify.js +41 -82
  166. package/src/core/paths/output-schema.js +20 -67
  167. package/src/core/state/gate-order.js +5 -6
  168. package/src/core/state/lifecycle-audit.js +2 -2
  169. package/src/core/state/state-manager.js +17 -98
  170. package/src/core/templates/template-registry.js +2 -2
  171. package/src/core/templates/template-seed.js +22 -0
  172. package/src/core/work-types.js +10 -10
  173. package/src/dashboard/app/App.jsx +0 -2
  174. package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
  175. package/src/dashboard/app/components/CommandPalette.jsx +7 -8
  176. package/src/dashboard/app/components/EntityLink.jsx +1 -2
  177. package/src/dashboard/app/components/Markdown.jsx +1 -1
  178. package/src/dashboard/app/components/Sidebar.jsx +1 -2
  179. package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
  180. package/src/dashboard/app/lib/phases.js +2 -4
  181. package/src/dashboard/app/pages/Activity.jsx +1 -1
  182. package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
  183. package/src/dashboard/app/pages/Features.jsx +9 -9
  184. package/src/dashboard/app/pages/Guide.jsx +2 -2
  185. package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
  186. package/src/dashboard/app/pages/RulesMap.jsx +3 -3
  187. package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
  188. package/src/dashboard/app/pages/Standards.jsx +1 -1
  189. package/src/dashboard/scanner/activity-scanner.js +6 -16
  190. package/src/dashboard/scanner/agents-scanner.js +1 -1
  191. package/src/dashboard/scanner/docs-scanner.js +5 -23
  192. package/src/dashboard/scanner/features-scanner.js +95 -60
  193. package/src/dashboard/scanner/index.js +0 -2
  194. package/src/dashboard/scanner/project-api.js +1 -1
  195. package/src/dashboard/scanner/rules-scanner.js +15 -10
  196. package/src/dashboard/scanner/skills-scanner.js +2 -3
  197. package/src/dashboard/scanner/standards-scanner.js +2 -1
  198. package/src/lib/ci/ci-verify.js +17 -1
  199. package/src/lib/ci/github-ci.js +1 -1
  200. package/src/lib/detectors/index.js +3 -8
  201. package/src/lib/generators/handoff-generator.js +34 -65
  202. package/src/lib/generators/settings-generator.js +11 -89
  203. package/src/lib/git/evidence-staleness.js +1 -1
  204. package/src/lib/git/tree-fingerprint.js +19 -7
  205. package/src/lib/git/tree-state.js +2 -2
  206. package/src/lib/heavy/cleanup.js +1 -5
  207. package/src/lib/installers/memory-installer.js +6 -8
  208. package/src/lib/mutation/commands.js +3 -2
  209. package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
  210. package/src/lib/mutation/plan.js +14 -6
  211. package/src/lib/mutation/runner.js +1 -1
  212. package/src/lib/mutation/subset.js +46 -9
  213. package/src/lib/plan/artifact-vocabulary.js +60 -0
  214. package/src/lib/plan/checks.js +234 -0
  215. package/src/lib/plan/plan-parser.js +209 -0
  216. package/src/lib/plan/plan-template.js +18 -0
  217. package/src/lib/review-reports.js +222 -0
  218. package/src/lib/risk-detector.js +134 -135
  219. package/src/lib/specs/adr-index.js +50 -69
  220. package/src/lib/specs/design-seed.js +18 -0
  221. package/src/lib/specs/frontmatter.js +44 -0
  222. package/src/lib/specs/gfm-table.js +17 -0
  223. package/src/lib/specs/old-format.js +68 -0
  224. package/src/lib/specs/revocations.js +37 -95
  225. package/src/lib/specs/screen-checklist.js +18 -17
  226. package/src/lib/stack/dotnet-build-ignores.js +47 -0
  227. package/src/lib/stack/stack-profile.js +1 -1
  228. package/src/lib/stack/stack-scan.js +267 -0
  229. package/src/lib/standards/registry.js +43 -0
  230. package/src/lib/standards/sections.js +58 -0
  231. package/src/lib/tasks/eval-node.js +4 -12
  232. package/src/lib/tasks/test-nodes.js +1 -74
  233. package/src/lib/tasks/test-runner.js +75 -117
  234. package/src/lib/telemetry/metrics.js +8 -69
  235. package/src/lib/telemetry/telemetry-reader.js +5 -118
  236. package/src/lib/telemetry/verify-event.js +5 -7
  237. package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
  238. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
  239. package/src/lib/validators/reuse/reuse-validator.js +24 -48
  240. package/src/lib/validators/reuse/symbol-scanner.js +2 -4
  241. package/src/lib/validators/traceability-check.js +85 -129
  242. package/src/lib/validators/validation-runner.js +8 -88
  243. package/src/lib/validators/verify-runner.js +134 -301
  244. package/src/lib/worktree/docker-audit.js +1 -2
  245. package/src/lib/worktree/fleet-scanner.js +25 -38
  246. package/src/scripts/setup-infra.js +42 -22
  247. package/src/utils/agents-installer.js +92 -13
  248. package/src/utils/claude-settings-manager.js +0 -35
  249. package/src/utils/file-copier.js +34 -6
  250. package/src/utils/hooks-installer.js +16 -9
  251. package/src/utils/skills-installer.js +1 -2
  252. package/framework/commands/commit.md +0 -166
  253. package/framework/evals/trace-schema.md +0 -67
  254. package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
  255. package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
  256. package/framework/hooks/git/pre-commit/specs.sh +0 -50
  257. package/framework/hooks/shared/tasks-validator.js +0 -111
  258. package/framework/rubrics/backend.json +0 -41
  259. package/framework/rubrics/default.json +0 -36
  260. package/framework/rubrics/frontend.json +0 -36
  261. package/framework/rules/morph-task-tracking.md +0 -54
  262. package/framework/rules/morph-workflow.md +0 -244
  263. package/framework/schemas/tasks.schema.json +0 -79
  264. package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
  265. package/framework/skills/core/morph-implement/SKILL.md +0 -194
  266. package/framework/skills/core/morph-proposal/SKILL.md +0 -152
  267. package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
  268. package/framework/templates/docs/proposal.md +0 -192
  269. package/framework/templates/docs/spec.md +0 -57
  270. package/framework/templates/docs/ui-components.md +0 -52
  271. package/framework/templates/docs/ui-design-system.md +0 -76
  272. package/framework/templates/docs/ui-flows.md +0 -167
  273. package/framework/templates/examples/spec-examples.md +0 -91
  274. package/framework/templates/feature/decisions.md +0 -124
  275. package/framework/templates/feature/recap.md +0 -145
  276. package/framework/templates/project/validate.js +0 -97
  277. package/framework/templates/tasks.template.json +0 -17
  278. package/src/commands/state/usecase.js +0 -143
  279. package/src/commands/tasks/dag.js +0 -181
  280. package/src/dashboard/app/pages/Rubrics.jsx +0 -97
  281. package/src/dashboard/scanner/rubrics-scanner.js +0 -12
  282. package/src/lib/agents/output-collector.js +0 -76
  283. package/src/lib/evaluator-report.js +0 -91
  284. package/src/lib/specs/acervo-format.js +0 -70
  285. package/src/lib/specs/acervo-lifecycle.js +0 -264
  286. package/src/lib/specs/done-criteria-check.js +0 -200
  287. package/src/lib/specs/usecase-check.js +0 -301
  288. package/src/lib/specs/usecase-promoter.js +0 -301
  289. package/src/lib/specs/usecase-registry.js +0 -237
  290. package/src/lib/tasks/dispatch-planner.js +0 -319
  291. package/src/lib/tasks/mermaid-renderer.js +0 -132
  292. package/src/lib/tasks/task-classifier.js +0 -30
  293. package/src/lib/tasks/task-parser.js +0 -209
  294. package/src/lib/tasks/tasks-json-parser.js +0 -296
  295. package/src/lib/tasks/test-scope.js +0 -597
  296. package/src/lib/validators/architecture/architecture-validator.js +0 -270
  297. package/src/lib/validators/architecture/index.js +0 -7
  298. package/src/lib/validators/css/css-validator.js +0 -340
  299. package/src/lib/validators/css/index.js +0 -7
  300. package/src/lib/validators/design-system/design-system-validator.js +0 -237
  301. package/src/lib/validators/design-system/index.js +0 -7
  302. package/src/lib/validators/score-aggregator.js +0 -244
  303. package/src/lib/validators/ui/index.js +0 -7
  304. package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
@@ -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
  ↓
@@ -23,7 +23,7 @@ import { isGenerated, isProjectSource } from '../../shared/source-files.js';
23
23
  const MAX_LISTED = 10;
24
24
 
25
25
  const RULE_LABEL = {
26
- process: 'história de processo (UC/ADR/task/gate)',
26
+ process: 'história de processo (ADR, cenário, fatia, gate, plan.md)',
27
27
  banner: 'banner ou #region',
28
28
  'doc-comment': '///·JSDoc em código que não é pacote publicado',
29
29
  'long-block': `bloco com mais de ${MAX_BLOCK_LINES} linhas`,
@@ -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
  }
@@ -6,9 +6,9 @@
6
6
  * Event: PreToolUse | Matcher: Write|Edit
7
7
  *
8
8
  * Ensures writes to .morph/features/{feature}/ go to the current phase's subfolder:
9
- * proposal → only 0-proposal/
9
+ * plan → only 2-plan/ (and 2-plan/ is writable in every phase: the errata
10
+ * and plan-check live there)
10
11
  * uiux → only 1-design/
11
- * plan → only 2-plan/
12
12
  * implement → 3-implement/ + any source code (unrestricted)
13
13
  * review → only 4-review/
14
14
  *
@@ -30,15 +30,16 @@ import {
30
30
  * The gate that authorizes writing a phase folder's FIRST artifact. Once that
31
31
  * gate is approved, the next phase's canonical file may be created even though
32
32
  * the phase only advances (via derivation) once that file exists — this is the
33
- * fix for the forward-transition chicken-and-egg. proposal-approval unlocks both
34
- * the optional `1-design/` and `2-plan/` (the uiux fork).
33
+ * fix for the forward-transition chicken-and-egg. The plan gate unlocks both
34
+ * the optional `1-design/` and `3-implement/` (the uiux fork).
35
35
  */
36
36
  const PHASE_DIR_ENTRY_GATE = {
37
- '1-design': 'proposal',
38
- '2-plan': 'proposal',
37
+ '1-design': 'plan',
39
38
  '3-implement': 'plan',
40
39
  '4-review': 'plan',
41
40
  };
41
+
42
+ const ALWAYS_WRITABLE_DIR = '2-plan';
42
43
  import { blockResult, runStandalone } from '../../shared/hook-response.js';
43
44
  import { logHookActivity } from '../../shared/activity-logger.js';
44
45
  import { getFilePath, resolveMorphRoot, resolveTargetPath } from '../../shared/payload-utils.js';
@@ -67,9 +68,8 @@ export async function run(payload) {
67
68
  // Resolve the tree that OWNS this file, NOT the tree the session sits in.
68
69
  // A session rooted in the primary repo may legitimately write to a feature
69
70
  // living in a secondary worktree; reading the session's `.morph/` there
70
- // reported the ROOT's phase for the WORKTREE's file, and blocked a legal
71
- // `2-plan/` write with "not allowed during PROPOSAL phase". `stateExists`
72
- // must be asked of that same tree — a worktree carries its own state.
71
+ // reported the ROOT's phase for the WORKTREE's file and blocked a legal write.
72
+ // `stateExists` must be asked of that same tree — a worktree carries its own state.
73
73
  const root = resolveMorphRoot(filePath, process.cwd());
74
74
  if (!stateExists(root)) return null;
75
75
 
@@ -84,6 +84,7 @@ export async function run(payload) {
84
84
 
85
85
  const targetDir = extractPhaseDir(filePath);
86
86
  if (!targetDir) return null; // File directly in feature root — allow
87
+ if (targetDir === ALWAYS_WRITABLE_DIR) return null;
87
88
 
88
89
  const allowedDir = PHASE_DIRS[phase];
89
90
  if (!allowedDir) return null; // Unknown phase — allow
@@ -102,14 +103,9 @@ export async function run(payload) {
102
103
  // advances once that file exists) — the deadlock this hook used to create.
103
104
  const entryGate = PHASE_DIR_ENTRY_GATE[targetDir];
104
105
  if (entryGate && isGateApproved(featureName, entryGate, root)) {
105
- // uiux is OPTIONAL — proposal approval alone is enough to enter
106
- // 2-plan/ for a feature that never took the design branch (no
107
- // 1-design/ dir), exactly as before this check. But once a feature
108
- // HAS opted into uiux (1-design/ exists), that gate is not
109
- // decorative: 2-plan/ must wait for it too, or a feature can skip
110
- // straight from an approved proposal into planning with its design
111
- // gate silently left pending.
112
- const tookDesignBranch = targetDir === '2-plan'
106
+ // uiux is optional, but once 1-design/ exists its gate is not
107
+ // decorative: implementation waits for it too.
108
+ const tookDesignBranch = targetDir !== '1-design'
113
109
  && existsSync(resolveTargetPath(`.morph/features/${featureName}/1-design`, root));
114
110
 
115
111
  if (!tookDesignBranch || isGateApproved(featureName, 'uiux', root)) {
@@ -119,9 +115,9 @@ export async function run(payload) {
119
115
 
120
116
  logHookActivity('enforce-phase-writes', 'PreToolUse', 'blocked');
121
117
  return blockResult(
122
- `MORPH-SPEC: Writing to '2-plan/' is not allowed yet.\n` +
118
+ `MORPH-SPEC: Writing to '${targetDir}/' is not allowed yet.\n` +
123
119
  `This feature entered the uiux branch (1-design/ exists) and the 'uiux' gate is still pending.\n\n` +
124
- `Approve the design gate first, then re-enter plan:\n` +
120
+ `Approve the design gate first, then move on to implementation:\n` +
125
121
  ` morph-spec approve ${featureName} uiux\n` +
126
122
  ` morph-spec advance ${featureName}`
127
123
  );
@@ -129,7 +125,7 @@ export async function run(payload) {
129
125
 
130
126
  const phaseLabel = phase.toUpperCase();
131
127
  // Name the tree the verdict came from whenever it is NOT the session's own.
132
- // The phase read is per-tree, so "which .morph/ said proposal?" is the first
128
+ // The phase read is per-tree, so "which .morph/ said plan?" is the first
133
129
  // question anyone debugging this asks.
134
130
  const treeNote = root !== process.cwd().replace(/\\/g, '/')
135
131
  ? `\n(phase read from the tree that owns the file: ${root})`
@@ -26,6 +26,7 @@
26
26
 
27
27
  import { loadState, stateExists, getActiveFeature } from '../../shared/state-reader.js';
28
28
  import { contextResult, runStandalone } from '../../shared/hook-response.js';
29
+ import { gateFromQuestion } from '../../shared/gate-question.js';
29
30
  import { logHookActivity } from '../../shared/activity-logger.js';
30
31
  import { resolveMorphRoot, resolveTargetPath } from '../../shared/payload-utils.js';
31
32
  import { locateFeatureForQuestion } from '../../shared/feature-locator.js';
@@ -68,16 +69,6 @@ function askedTexts(input) {
68
69
  return texts;
69
70
  }
70
71
 
71
- /** Map a gate question's text to a gate id. Mirrors state-sync.js. */
72
- function gateFromQuestion(question) {
73
- if (typeof question !== 'string') return null;
74
- const lower = question.toLowerCase();
75
- if (lower.includes('gate 1') || lower.includes('proposta')) return 'proposal';
76
- if (lower.includes('gate 2') || lower.includes('plano') || lower.includes('plan')) return 'plan';
77
- if (lower.includes('gate 3') || lower.includes('review')) return 'review';
78
- return null;
79
- }
80
-
81
72
  function getDecision(state, feature, gate) {
82
73
  const d = state.features?.[feature]?.gateDecisions?.[gate];
83
74
  if (!d || typeof d.pause !== 'boolean') return null;
@@ -154,7 +145,7 @@ function checkStateEdit(input, sessionRoot) {
154
145
 
155
146
  let checked = false;
156
147
  const gates = state.features?.[feature]?.approvalGates ?? {};
157
- for (const gate of ['proposal', 'plan', 'review']) {
148
+ for (const gate of ['plan', 'uiux', 'review']) {
158
149
  const g = gates[gate];
159
150
  const autoApproved = g?.approved === true && g?.approvedBy === 'auto';
160
151
  if (!autoApproved) continue;
@@ -4,8 +4,39 @@ import { join, dirname } from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import { blockResult, runStandalone } from '../../shared/hook-response.js';
6
6
  import { stateExists } from '../../shared/state-reader.js';
7
+ import { tokenize } from '../../shared/command-tokens.js';
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url));
10
+ const SEPARATOR = /^[;&|]+$/;
11
+ const executableName = (token) => token.replace(/\\/g, '/').split('/').pop().toLowerCase().replace(/\.exe$/, '');
12
+
13
+ const hasFlag = (token, flag) => (flag.startsWith('--') || token.startsWith('--')
14
+ ? token === flag
15
+ : /^-[A-Za-z]+$/.test(token) && token.includes(flag.slice(1)));
16
+
17
+ /**
18
+ * A quoted argument is one token, never the executable: mentioning the command in a message is not calling it.
19
+ * `args` match a whole token; each group in `flags` (case-sensitive, clusters like `-rf` count) must appear in the same command.
20
+ */
21
+ export function matchesExecutableRule(command, rule) {
22
+ const exe = rule.executable.toLowerCase();
23
+ const args = new Set(rule.args.map((a) => a.toLowerCase()));
24
+ const flagGroups = rule.flags || [];
25
+ const tokens = tokenize(command);
26
+ for (let i = 0; i < tokens.length; i++) {
27
+ if (executableName(tokens[i].replace(/^[;&|(]+/, '')) !== exe) continue;
28
+ const seen = [];
29
+ let argHit = false;
30
+ for (let j = i + 1; j < tokens.length && !SEPARATOR.test(tokens[j]); j++) {
31
+ const raw = tokens[j].replace(/[;&|)]+$/, '');
32
+ if (args.has(raw.toLowerCase())) argHit = true;
33
+ seen.push(raw);
34
+ if (tokens[j] !== raw) break;
35
+ }
36
+ if (argHit && flagGroups.every((group) => seen.some((t) => group.some((flag) => hasFlag(t, flag))))) return true;
37
+ }
38
+ return false;
39
+ }
9
40
 
10
41
  /**
11
42
  * @param {object} payload - parsed PreToolUse stdin payload (Bash matcher)
@@ -26,6 +57,13 @@ export async function run(payload) {
26
57
  }
27
58
  }
28
59
 
60
+ for (const rule of denylist.executableArgs || []) {
61
+ if (matchesExecutableRule(command, rule)) {
62
+ const shown = [rule.executable, ...(rule.flags || []).map((group) => group[0]), rule.args[0]].join(' ');
63
+ return blockResult(`Comando bloqueado pela denylist: \`${shown}\` ${rule.reason}.`);
64
+ }
65
+ }
66
+
29
67
  return null;
30
68
  } catch {
31
69
  return null;