@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
@@ -227,25 +227,18 @@ export function getFeature(featureName, projectPath) {
227
227
  * Derive phase from filesystem (phase deleted from state).
228
228
  *
229
229
  * Walks the phase map in descending order and returns the first phase whose
230
- * CANONICAL ARTIFACT exists. An empty folder is not enough — this avoids the
231
- * chicken-and-egg problem where `mkdir 2-plan/` (or any pre-created sibling)
232
- * advances the phase before plan content is written, then
233
- * `enforce-phase-writes` blocks writes back to `0-proposal/`.
230
+ * CANONICAL ARTIFACT exists. An empty folder is not enough — a pre-created
231
+ * sibling folder never advances the phase before its content is written.
234
232
  *
235
233
  * Canonical artifacts (one per phase) match MORPH.md §3:
236
- * - 4-review → review-report.md
237
- * - 3-implement → recap.md
238
- * - 2-plan → spec.md (the first plan-phase output produced)
239
- * - 1-design → design-system.md
240
- * - 0-proposal → proposal.md
234
+ * - 4-review → verifier.md
235
+ * - 3-implement → any slice report (`reports/*.md`)
236
+ * - 1-design → design-system.md
237
+ * - 2-plan → plan.md
241
238
  *
242
- * If no canonical file is found, returns `'proposal'` (the starting phase).
243
- *
244
- * @param {string} featureName
245
- * @param {string} [projectPath]
246
- * @returns {string} Phase name
239
+ * If no canonical file is found, returns `'plan'` (the starting phase).
247
240
  */
248
- export const PHASE_INDEX = { proposal: 0, uiux: 1, plan: 2, implement: 3, review: 4 };
241
+ export const PHASE_INDEX = { plan: 0, uiux: 1, implement: 2, review: 3 };
249
242
 
250
243
  /**
251
244
  * The canonical artifact of each phase, descending. Exported so a caller that
@@ -253,32 +246,61 @@ export const PHASE_INDEX = { proposal: 0, uiux: 1, plan: 2, implement: 3, review
253
246
  * tree, say) can answer the same question without re-encoding the map.
254
247
  */
255
248
  export const PHASE_ARTIFACTS = [
256
- ['4-review', 'review', 'review-report.md'],
257
- ['3-implement', 'implement', 'recap.md'],
258
- ['2-plan', 'plan', 'spec.md'],
249
+ ['4-review', 'review', 'verifier.md'],
250
+ ['3-implement', 'implement', 'reports/'],
259
251
  ['1-design', 'uiux', 'design-system.md'],
260
- ['0-proposal', 'proposal', 'proposal.md'],
252
+ ['2-plan', 'plan', 'plan.md'],
261
253
  ];
262
254
 
255
+ const DESIGN_DIR = '1-design';
256
+
263
257
  const gatesOf = (feature) => feature?.approvals || feature?.approvalGates;
264
258
 
265
259
  /**
266
260
  * Gate-implied phase from an ALREADY-LOADED feature object — no disk access.
267
- *
268
- * Split out of `gateImpliedPhase` so callers holding a feature.json that did
269
- * not come from the working tree (e.g. `git show morph/{f}:…`) can reuse the
270
- * rule instead of copying it. The path-based wrapper below is unchanged.
261
+ * An approved gate authorizes the NEXT phase:
262
+ * review approved → review (terminal)
263
+ * plan approved → implement, or uiux while the design branch was taken
264
+ * (`1-design/` exists) and its gate is still open
265
+ * otherwise → plan
271
266
  *
272
267
  * @param {Object|null} feature
268
+ * @param {boolean} [designStarted] - whether `1-design/` exists for the feature
273
269
  * @returns {string} phase name
274
270
  */
275
- export function gateImpliedPhaseFrom(feature) {
271
+ export function gateImpliedPhaseFrom(feature, designStarted = false) {
276
272
  const gates = gatesOf(feature);
277
273
  const ok = (g) => gates?.[g]?.approved === true;
278
274
  if (ok('review')) return 'review';
279
- if (ok('plan')) return 'implement';
280
- if (ok('uiux')) return 'plan';
281
- return 'proposal';
275
+ if (ok('plan')) return designStarted && !ok('uiux') ? 'uiux' : 'implement';
276
+ return 'plan';
277
+ }
278
+
279
+ /**
280
+ * Artifact predicate over a feature dir on disk. A path ending in `/` asks for
281
+ * any `.md` file directly under it: an empty folder is not an artifact.
282
+ */
283
+ export function diskArtifactProbe(featureDir) {
284
+ return (rel) => {
285
+ const abs = join(featureDir, ...rel.split('/').filter(Boolean));
286
+ if (!rel.endsWith('/')) return existsSync(abs);
287
+ try {
288
+ return readdirSync(abs, { withFileTypes: true }).some((e) => e.isFile() && e.name.endsWith('.md'));
289
+ } catch {
290
+ return false;
291
+ }
292
+ };
293
+ }
294
+
295
+ /** The same predicate over a set of feature-relative POSIX paths (a git tree). */
296
+ export function fileSetArtifactProbe(files) {
297
+ return (rel) => {
298
+ if (!rel.endsWith('/')) return files.has(rel);
299
+ for (const f of files) {
300
+ if (f.startsWith(rel) && f.endsWith('.md') && !f.slice(rel.length).includes('/')) return true;
301
+ }
302
+ return false;
303
+ };
282
304
  }
283
305
 
284
306
  /**
@@ -286,44 +308,33 @@ export function gateImpliedPhaseFrom(feature) {
286
308
  * access of its own, so the artifact universe can be a git tree.
287
309
  *
288
310
  * @param {Object|null} feature
289
- * @param {(relPath: string) => boolean} hasArtifact - receives POSIX-style
290
- * paths relative to the feature dir, e.g. `2-plan/spec.md`
311
+ * @param {(relPath: string) => boolean} hasArtifact - {@link diskArtifactProbe}
312
+ * or {@link fileSetArtifactProbe}: POSIX paths relative to the feature dir
291
313
  * @returns {string} phase name
292
314
  */
293
315
  export function derivePhaseFrom(feature, hasArtifact) {
294
- let artifactPhase = 'proposal';
316
+ let artifactPhase = 'plan';
295
317
  for (const [folder, phase, canonical] of PHASE_ARTIFACTS) {
296
318
  if (hasArtifact(`${folder}/${canonical}`)) { artifactPhase = phase; break; }
297
319
  }
298
320
 
299
321
  // Gate-aware: an approved gate advances the phase even before the next
300
- // phase's first canonical artifact exists. This breaks the chicken-and-egg
301
- // where that first artifact could never be written because the phase never
302
- // advanced to permit it. Take whichever of (artifact, gate-implied) is later.
303
- const implied = gateImpliedPhaseFrom(feature);
322
+ // phase's first canonical artifact exists, or that artifact could never be
323
+ // written. Take whichever of (artifact, gate-implied) is later.
324
+ const designStarted = hasArtifact(DESIGN_DIR) || hasArtifact(`${DESIGN_DIR}/design-system.md`);
325
+ const implied = gateImpliedPhaseFrom(feature, designStarted);
304
326
  return PHASE_INDEX[implied] > PHASE_INDEX[artifactPhase] ? implied : artifactPhase;
305
327
  }
306
328
 
307
- /**
308
- * The phase implied purely by approved gates, independent of which artifacts
309
- * exist on disk. An approved gate authorizes the NEXT phase, so:
310
- * review approved → review (terminal)
311
- * plan approved → implement
312
- * uiux approved → plan
313
- * otherwise → proposal (proposal-approval's fork between uiux/plan is
314
- * resolved by enforce-phase-writes, not here)
315
- * @returns {string} phase name
316
- */
329
+ /** The phase implied purely by approved gates — see {@link gateImpliedPhaseFrom}. */
317
330
  export function gateImpliedPhase(featureName, projectPath) {
318
- return gateImpliedPhaseFrom(getFeature(featureName, projectPath));
331
+ const designDir = join(projectPath || process.cwd(), '.morph/features', featureName, DESIGN_DIR);
332
+ return gateImpliedPhaseFrom(getFeature(featureName, projectPath), existsSync(designDir));
319
333
  }
320
334
 
321
335
  export function derivePhaseForFeature(featureName, projectPath) {
322
336
  const basePath = join(projectPath || process.cwd(), '.morph/features', featureName);
323
- return derivePhaseFrom(
324
- getFeature(featureName, projectPath),
325
- (rel) => existsSync(join(basePath, rel)),
326
- );
337
+ return derivePhaseFrom(getFeature(featureName, projectPath), diskArtifactProbe(basePath));
327
338
  }
328
339
 
329
340
  /**
@@ -342,7 +353,7 @@ export function getFeaturePhase(featureName, projectPath) {
342
353
  /**
343
354
  * Get approval status for a gate.
344
355
  * @param {string} featureName
345
- * @param {string} gate - 'proposal'|'uiux'|'plan'|'review'
356
+ * @param {string} gate - 'plan'|'uiux'|'review'
346
357
  * @param {string} [projectPath]
347
358
  * @returns {boolean}
348
359
  */
@@ -384,43 +395,6 @@ export function getOutputs(featureName, projectPath) {
384
395
  return feature?.outputs || null;
385
396
  }
386
397
 
387
- /**
388
- * Get uncompleted required outputs for current phase.
389
- * @param {string} featureName
390
- * @param {string} [projectPath]
391
- * @returns {Array<{type: string, path: string}>}
392
- */
393
- /**
394
- * Get task statistics directly from taskList (preferred over feature.tasks counters).
395
- * Excludes expanded tasks from counts, matching calculateProgress() behavior.
396
- *
397
- * @param {Object} feature - Feature state object
398
- * @returns {{ total: number, completed: number, inProgress: number, pending: number }}
399
- */
400
- export function getTaskStats(feature) {
401
- const list = feature?.taskList || [];
402
- if (list.length > 0) {
403
- const active = list.filter(t => t.status !== 'expanded');
404
- return {
405
- total: active.length,
406
- completed: active.filter(t => t.status === 'completed').length,
407
- inProgress: active.filter(t => t.status === 'in_progress').length,
408
- pending: active.filter(t => t.status === 'pending').length,
409
- };
410
- }
411
- // Fallback: read from legacy counter object
412
- const tasks = feature?.tasks;
413
- if (tasks && typeof tasks === 'object' && !Array.isArray(tasks)) {
414
- return {
415
- total: tasks.total || 0,
416
- completed: tasks.completed || 0,
417
- inProgress: tasks.inProgress || 0,
418
- pending: tasks.pending || 0,
419
- };
420
- }
421
- return { total: 0, completed: 0, inProgress: 0, pending: 0 };
422
- }
423
-
424
398
  export function getMissingOutputs(featureName, projectPath) {
425
399
  const feature = getFeature(featureName, projectPath);
426
400
  if (!feature?.outputs) return [];
@@ -101,7 +101,7 @@ export function persistFeature(featureName, feature, projectPath) {
101
101
  * canonical shape — onto the feature and persists it. Returns true on success.
102
102
  *
103
103
  * @param {string} featureName
104
- * @param {string} gate - 'proposal'|'uiux'|'plan'|'review'
104
+ * @param {string} gate - 'plan'|'uiux'|'review'
105
105
  * @param {{ approvedBy?: string, timestamp?: string }} [meta]
106
106
  * @param {string} [projectPath]
107
107
  */
@@ -114,6 +114,7 @@ export function setGateApproved(featureName, gate, meta = {}, projectPath) {
114
114
  approved: true,
115
115
  timestamp: meta.timestamp || now,
116
116
  approvedBy: meta.approvedBy || 'manual',
117
+ ...(meta.selfAssessed ? { selfAssessed: true } : {}),
117
118
  };
118
119
  feature.updatedAt = now;
119
120
  return persistFeature(featureName, feature, projectPath);
@@ -141,9 +142,8 @@ export function createDefaultFeature(featureName) {
141
142
  createdAt: now,
142
143
  updatedAt: now,
143
144
  approvalGates: {
144
- proposal: { approved: false, timestamp: null, approvedBy: null },
145
- uiux: { approved: false, timestamp: null, approvedBy: null },
146
145
  plan: { approved: false, timestamp: null, approvedBy: null },
146
+ uiux: { approved: false, timestamp: null, approvedBy: null },
147
147
  review: { approved: false, timestamp: null, approvedBy: null },
148
148
  },
149
149
  outputs: {},
@@ -3,7 +3,7 @@
3
3
  "description": "Centralized MCP registry with phase relevance and install configuration for morph-spec workflows",
4
4
  "mcps": {
5
5
  "context7": {
6
- "phases": ["proposal", "plan", "implement"],
6
+ "phases": ["plan", "implement"],
7
7
  "tools": ["resolve-library-id", "query-docs"],
8
8
  "serverNames": ["context7", "plugin_context7_context7"],
9
9
  "detection": "enabledPlugins includes context7 or mcpServers includes context7",
@@ -216,7 +216,7 @@
216
216
  }
217
217
  },
218
218
  "graphify": {
219
- "phases": ["proposal", "plan", "implement", "review"],
219
+ "phases": ["plan", "implement", "review"],
220
220
  "tools": ["query_graph", "get_node", "get_neighbors", "get_community", "god_nodes", "graph_stats", "shortest_path", "list_prs", "get_pr_impact", "triage_prs"],
221
221
  "detection": "graphify-out/graph.json exists in the project root (written by morph-spec graph build, or by the graphify post-commit hook)",
222
222
  "fallback": "Comportamento atual: Grep/Glob/Read. O grafo é um subconjunto estrito do grep — no eval não encontrou um único arquivo que o grep perderia — então cair para grep não perde recall, só devolve mais ruído",
@@ -281,9 +281,8 @@
281
281
  },
282
282
  "_phaseMatrixNote": "Keys must be the v5 phases from framework/phases.json. The v4 keys 'design' and 'tasks' both collapsed into 'plan'; 'review' was absent entirely, so a project in review resolved to undefined. Stack-scoped MCPs (asaas, neon) stay out of this matrix on purpose — only universal ones are listed.",
283
283
  "phaseMatrix": {
284
- "proposal": ["context7", "graphify"],
285
- "uiux": ["playwright", "context7", "stitch", "morph-assets"],
286
284
  "plan": ["context7", "graphify"],
285
+ "uiux": ["playwright", "context7", "stitch", "morph-assets"],
287
286
  "implement": ["context7", "playwright", "vercel", "morph-assets", "graphify"],
288
287
  "review": ["playwright", "context7", "graphify"]
289
288
  }
@@ -1,22 +1,22 @@
1
1
  {
2
2
  "version": "5.0.0",
3
3
  "phases": {
4
- "proposal": {
5
- "id": "proposal",
4
+ "plan": {
5
+ "id": "plan",
6
6
  "index": 0,
7
- "folder": "0-proposal",
8
- "displayName": "Proposal",
7
+ "folder": "2-plan",
8
+ "displayName": "Plan",
9
9
  "goals": [
10
- "Understand the business problem and user flows",
11
- "Detect existing stack (brownfield) or assume default stack (greenfield)",
12
- "Produce proposal.md and reach Gate 1"
10
+ "Understand the business problem and scan the stack (brownfield) or assume the default (greenfield)",
11
+ "Write plan.md — scenarios with proof, decisions, reuse and slices",
12
+ "Run plan-check to produce checks.json and reach the plan gate"
13
13
  ],
14
- "requiredOutputs": ["proposal"],
15
- "optionalOutputs": ["stackScan"],
14
+ "requiredOutputs": ["plan", "checks"],
15
+ "optionalOutputs": [],
16
16
  "pausePoints": [
17
- { "id": "proposal", "label": "Gate 1 — Approve proposal" }
17
+ { "id": "plan", "label": "Gate do plano — Approve plan.md + checks.json" }
18
18
  ],
19
- "skill": "morph-brainstorming"
19
+ "skill": "morph-plan"
20
20
  },
21
21
  "uiux": {
22
22
  "id": "uiux",
@@ -35,50 +35,34 @@
35
35
  ],
36
36
  "skill": "morph-uiux"
37
37
  },
38
- "plan": {
39
- "id": "plan",
40
- "index": 2,
41
- "folder": "2-plan",
42
- "displayName": "Plan",
43
- "goals": [
44
- "Write spec.md (technical spec)",
45
- "Write tasks.json (ordered task breakdown)"
46
- ],
47
- "requiredOutputs": ["spec", "tasks"],
48
- "optionalOutputs": ["decisions"],
49
- "pausePoints": [
50
- { "id": "plan", "label": "Gate 2 — Approve spec + tasks" }
51
- ],
52
- "skill": "morph-plan"
53
- },
54
38
  "implement": {
55
39
  "id": "implement",
56
- "index": 3,
40
+ "index": 2,
57
41
  "folder": "3-implement",
58
42
  "displayName": "Implementation",
59
43
  "goals": [
60
- "Execute tasks in order with auto-advance between tasks",
61
- "Advance a task only on verify green and its doneCriteria met",
62
- "Maintain recap.md and execution trace"
44
+ "Execute the slices of plan.md, in parallel waves when plan-check allows",
45
+ "Close a slice only on verify green and its scenarios proven",
46
+ "One report per slice (3-implement/reports/F{n}.md, with **Arquivos:** and **Standards:**) — the implementation record"
63
47
  ],
64
- "requiredOutputs": ["recap"],
48
+ "requiredOutputs": [],
65
49
  "optionalOutputs": ["evidencias"],
66
50
  "pausePoints": [],
67
- "skill": "morph-implement"
51
+ "skill": "morph-apply"
68
52
  },
69
53
  "review": {
70
54
  "id": "review",
71
- "index": 4,
55
+ "index": 3,
72
56
  "folder": "4-review",
73
57
  "displayName": "Review",
74
58
  "goals": [
75
- "Independent evaluator verdict (SHIP/FIX) with evidence-typed blockers",
76
- "Produce review-report.md and present Gate 3"
59
+ "Verifier proves every scenario; reviewer (and security, when required) judge the diff at a fixed SHA",
60
+ "verifier.md + reviewer.md (+ security.md) with SHIP/FIX and evidence-typed blockers, then Gate 3"
77
61
  ],
78
- "requiredOutputs": ["reviewReport"],
79
- "optionalOutputs": [],
62
+ "requiredOutputs": ["verifierReport", "reviewerReport"],
63
+ "optionalOutputs": ["securityReport"],
80
64
  "pausePoints": [
81
- { "id": "review", "label": "Gate 3 — Approve review-report" }
65
+ { "id": "review", "label": "Gate 3 — Approve the review reports" }
82
66
  ],
83
67
  "skill": "morph-review"
84
68
  }
@@ -9,26 +9,28 @@ stacks:
9
9
 
10
10
  # Microsoft Agent Framework Standards
11
11
 
12
- > **Layer 0/1 — auto-carregados.** São os standards core: aplicam-se a praticamente todo
13
- > agente MAF. Carregue-os sempre que tocar código de agentes.
12
+ > **Layer 0/1 — resumo injetado pela rule.** São os standards core: aplicam-se a praticamente todo
13
+ > agente MAF. O resumo de cada um entra no contexto quando você toca código de agentes.
14
14
 
15
- @.morph/framework/standards/ai-agents/setup.md
16
- @.morph/framework/standards/ai-agents/sweet-spot.md
17
- @.morph/framework/standards/ai-agents/providers/model-registry.md
18
- @.morph/framework/standards/ai-agents/structured-output.md
19
- @.morph/framework/standards/ai-agents/multi-agent-patterns.md
20
- @.morph/framework/standards/ai-agents/prompt-sources.md
21
- @.morph/framework/standards/ai-agents/rag-custom-pgvector.md
22
- @.morph/framework/standards/ai-agents/middleware-patterns.md
23
- @.morph/framework/standards/ai-agents/observability-patterns.md
24
- @.morph/framework/standards/ai-agents/workflows.md
25
- @.morph/framework/standards/ai-agents/production.md
15
+ - **ai-agents-setup** (`ai-agents/setup.md`) — Versões exatas: Microsoft.Agents.AI 1.20.0, Microsoft.Extensions.AI 10.9.0, OpenAI 2.13.0. OpenAI pelo Responses API (`GetResponsesClient().AsAIAgent(...)`); Chat Completions só com `"api": "chat"`; `store: false`. PROIBIDO: orquestração Semantic Kernel, `AgentOpenTelemetryConsts` (não existe).
16
+ - **ai-agents-sweet-spot** (`ai-agents/sweet-spot.md`) — Chat + Structured Output + Tools + RAG custom cobre 95%. Single-agent é o padrão; multi-agent/workflow só com fan-in/out ou handoff real. Teto prático de ~12 tools por agente (podar, depois allow-list por fase, depois agent-as-tool). Leia no plan, antes de decidir a arquitetura.
17
+ - **ai-agents-providers-model-registry** (`ai-agents/providers/model-registry.md`) — `model-registry.json` v2 mapeia alias → { provider, model, api, options, constraints }; agente usa alias, nunca string de modelo. Todo alias declara `api` (`responses` padrão, `chat` opt-in). `temperature` e `reasoningEffort` são mutuamente exclusivos por alias. Sem range flutuante em PackageReference.
18
+ - **ai-agents-structured-output** (`ai-agents/structured-output.md`) — Saída que alimenta outro sistema é record tipado (`RunAsync<T>` ou `ChatResponseFormat.ForJsonSchema<T>()`): `sealed record` com `[Description]` e `[JsonPropertyName]`. Raiz nunca primitivo/array (use wrapper); em streaming, `ToAgentResponseAsync()` antes de desserializar. Regra de negócio se valida depois da chamada.
19
+ - **ai-agents-multi-agent-patterns** (`ai-agents/multi-agent-patterns.md`) — Do mais barato: agent-as-tool (`AsAIFunction()`, aninhamento ≤ 2) → sequencial → workflow; handoff vem depois de agent-as-tool, com Id estável e `WithAutonomousMode(turnLimit:)`; group chat ≤ 3 agentes. PROIBIDO: agente auxiliar DENTRO de uma tool (dois loops de function calling no mesmo turno).
20
+ - **ai-agents-prompt-sources** (`ai-agents/prompt-sources.md`) — Prompt fixo: tabela versionada `agent_prompts` (INSERT + flip de `is_active` numa transação, nunca UPDATE; id `Guid.CreateVersion7()`). Prompt por tenant: YAML no repo → compositor determinístico → spec renderizada em jsonb. O registro de execução grava a versão EFETIVA (e o hash do blob no caminho composto).
21
+ - **ai-agents-rag-custom-pgvector** (`ai-agents/rag-custom-pgvector.md`) — RAG em pgvector via EF Core: `IEmbeddingGenerator` injetado direto (sem EmbeddingService), alias `embeddings` do registry, `CosineDistance` LINQ, HNSW `vector_cosine_ops`, `tenant_id` filtrado em toda query e nunca exposto ao LLM. Ingestão delta por lookup-key: Unchanged nunca re-embeda, Removed só depois das escritas.
22
+ - **ai-agents-middleware-patterns** (`ai-agents/middleware-patterns.md`) — Registro sempre `.AsBuilder().Use(...).Build()`; o PRIMEIRO registrado é o mais externo. Ordem medida: timeout → usage-cost → FunctionInvokingChatClient → chat telemetry → provider. Só o delegate não-streaming rebaixa streaming em silêncio: use `Use(sharedFunc)`.
23
+ - **ai-agents-observability-patterns** (`ai-agents/observability-patterns.md`) — Registre os DOIS ActivitySources (`Experimental.Microsoft.Agents.AI` e `Experimental.Microsoft.Extensions.AI`); a API é `builder.UseOpenTelemetry(...)` (`agent.WithOpenTelemetry()` não existe). Instrumentar os dois níveis é normal; o mesmo nível duas vezes duplica. Prompt cru só em dev com opt-in, hash em produção; turno cancelado não grava nada.
24
+ - **ai-agents-workflows** (`ai-agents/workflows.md`) — Raro: só para fan-in/out real ou HITL longo; `foreach` + LINQ costuma bastar. `AgentWorkflowBuilder`/`WorkflowBuilder`/`InProcessExecution` vivem no pacote SEPARADO `Microsoft.Agents.AI.Workflows` 1.20.0. Agente declarativo (`Microsoft.Agents.AI.Declarative`) ainda pede `--prerelease`.
25
+ - **ai-agents-production** (`ai-agents/production.md`) — A2A (`Microsoft.Agents.AI.A2A`) é PRERELEASE. Isolamento multi-tenant (`AgentIsolationKeyProvider`, `UseClaimsBasedAgentIsolation`) com a chave vinda da credencial, nunca do payload. `AllowConcurrentInvocation` é opt-in e inseguro com DbContext scoped. Cache: Redis semântico + HybridCache.
26
+
27
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
26
28
 
27
29
  ---
28
30
 
29
- > **Layer 2 — sob demanda (NÃO auto-carregados).** São standards de modalidade, otimização e
30
- > MCP: relevantes apenas quando a feature usa especificamente esse recurso. Carregue via
31
- > `morph-standards` quando uma keyword corresponder — manter fora do auto-load preserva o
31
+ > **Layer 2 — sob demanda (sem resumo aqui).** São standards de modalidade, otimização e
32
+ > MCP: relevantes apenas quando a feature usa especificamente esse recurso. Busque via
33
+ > `morph-spec standards --find` (skill `morph-standards`) quando uma keyword corresponder — manter fora do resumo preserva o
32
34
  > context window para o trabalho que importa.
33
35
 
34
36
  | Standard | Carregue quando a feature envolve |
@@ -10,4 +10,6 @@ stacks:
10
10
 
11
11
  # Asaas Payment Integration Standards
12
12
 
13
- @.morph/framework/standards/backend/integrations/asaas/asaas-api.md
13
+ - **backend-integrations-asaas-asaas-api** (`backend/integrations/asaas/asaas-api.md`) — Só o backend .NET chama o Asaas (o Next passa pelo proxy): `AsaasClient` HttpClient tipado, base URL com barra final e paths relativos, headers `access_token` + `User-Agent`; respostas viram `Result`. Sem cobrança duplicada: `externalReference` + GET antes de criar, nunca retry automático em cartão. Webhook: `asaas-access-token` comparado com `FixedTimeEquals`, id de evento deduplicado. Nunca guarde dado de cartão.
14
+
15
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -8,11 +8,13 @@ stacks:
8
8
 
9
9
  # C# and .NET VSA Standards
10
10
 
11
- @.morph/framework/standards/architecture/vertical-slice/vertical-slice.md
12
- @.morph/framework/standards/backend/dotnet/vsa-handler-patterns.md
13
- @.morph/framework/standards/backend/dotnet/result-pattern.md
14
- @.morph/framework/standards/backend/dotnet/fluent-validation-vsa.md
15
- @.morph/framework/standards/backend/dotnet/scrutor.md
16
- @.morph/framework/standards/backend/dotnet/scalar-ui.md
17
- @.morph/framework/standards/backend/api/validation.md
18
- @.morph/framework/standards/architecture/code-comments.md
11
+ - **architecture-vertical-slice-vertical-slice** (`architecture/vertical-slice/vertical-slice.md`) — Uma slice por operação em `Features/{Entity}Feature/{Verb}{Entity}/`: Request/Response `sealed record`, Handler, Validator e Endpoint `IApiEndpoint` `internal sealed`. Decorators Scrutor: Logging → Validation → Handler. PROIBIDO: MediatR/`IRequest`, AggregateRoot, DomainEvent, CQRS formal, camada Application Service, DTO compartilhado entre features, `IRepository<T>`/`IUnitOfWork`.
12
+ - **backend-dotnet-vsa-handler-patterns** (`backend/dotnet/vsa-handler-patterns.md`) — Handler `sealed class` com primary constructor implementando `IHandler<TRequest, TResponse>` (não MediatR): injeta `ApplicationDbContext` direto, usa `Guid.CreateVersion7()` (nunca `Guid.NewGuid()`), passa `CancellationToken` em tudo e devolve `Result<T>`. Erros em `{Feature}Errors.cs`. SQL cru ou store dedicado só com justificativa em `decisions.md`.
13
+ - **backend-dotnet-result-pattern** (`backend/dotnet/result-pattern.md`) — Falha esperada é `Result`/`Result<T>`, nunca exceção. Endpoint: `result.Match(value => Results.Ok(value), error => error.ToHttpResult())`; nunca leia `.Value` fora do `Match`. Status pelo `ErrorType` (Validation 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500), nunca BadRequest para tudo. PROIBIDO: try/catch no handler, retornar null, erro string sem código.
14
+ - **backend-dotnet-fluent-validation-vsa** (`backend/dotnet/fluent-validation-vsa.md`) — Um `sealed AbstractValidator<TRequest>` (FluentValidation 12) por request, na pasta da slice; o `ValidationDecorator` roda antes do handler e devolve `Result.Failure(ValidationError)`. `AddValidatorsFromAssembly()` ANTES de `AddHandlersFromAssembly()`. Sempre `.WithMessage()`; tempo via `TimeProvider` lido DENTRO da regra. Nunca lance `ValidationException`.
15
+ - **backend-dotnet-scrutor** (`backend/dotnet/scrutor.md`) — `AddHandlersFromAssembly` registra cada `IHandler<,>` como Scoped (filtro `!t.ContainsGenericParameters`) e depois `Decorate` Validation e então Logging (o último registrado é o mais externo). Endpoints `IApiEndpoint` via `TryAddEnumerable` Transient + `MapApiEndpoints`. Nunca registre handler um a um.
16
+ - **backend-dotnet-scalar-ui** (`backend/dotnet/scalar-ui.md`) — Scalar no lugar do Swagger: `Microsoft.AspNetCore.OpenApi` 10.0.0 + `Scalar.AspNetCore` 2.12.40, nunca Swashbuckle. `MapOpenApi()` só em `IsDevelopment()` e antes de `MapScalarApiReference`. Todo endpoint com `.WithTags(ApiTags.X)` (constantes), `.Produces<T>` e `.ProducesProblem(status)` por falha possível.
17
+ - **backend-api-validation** (`backend/api/validation.md`) — O validator checa só FORMA (sem DbContext); existência, unicidade e estado ficam no handler com `{Entity}Errors` tipados. Checagem de estado valida o INVÁLIDO (enum ordenado, estados de erro em 100+). PROIBIDO: lançar exceção, validar na lambda do endpoint, misturar DataAnnotations, `if (status != Expected)`, switch exaustivo sobre estados válidos.
18
+ - **architecture-code-comments** (`architecture/code-comments.md`) — Padrão é nenhum comentário: renomeie, extraia, tipe ou teste antes. Só o porquê que o código não mostra (invariante, ordem, quirk externo com link), até ~3 linhas. PROIBIDO: história de processo (task, UC, ADR, Gate), repetir o código, XML doc/JSDoc que repete a assinatura, banner, `#region`, código comentado, TODO solto. O hook `comment-guard` bloqueia.
19
+
20
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -2,7 +2,6 @@
2
2
  paths:
3
3
  - "**/*Cache*.cs"
4
4
  - "**/*Redis*.cs"
5
- - "**/*Cosmos*.cs"
6
5
  - "**/*Vector*.cs"
7
6
  - "**/*Embedding*.cs"
8
7
  - "**/*Rag*.cs"
@@ -12,7 +11,8 @@ stacks:
12
11
 
13
12
  # Data & Cache Standards
14
13
 
15
- @.morph/framework/standards/data/nosql/cache/redis.md
16
- @.morph/framework/standards/data/nosql/cosmos-db.md
17
- @.morph/framework/standards/data/vector-search/rag-chunking.md
18
- @.morph/framework/standards/backend/database/vector-search-rag.md
14
+ - **data-nosql-cache-redis** (`data/nosql/cache/redis.md`) — Cache distribuído, nunca store primário. `Microsoft.Extensions.Caching.StackExchangeRedis` 10.0.12; `IConnectionMultiplexer` singleton LAZY (nunca conectar no registro). Cache-aside no handler com `IDistributedCache` (sem wrapper); invalide a chave exata depois do `SaveChangesAsync`. PROIBIDO: cachear segredo/PII sem criptografia, omitir TTL, engolir `RedisConnectionException`.
15
+ - **data-vector-search-rag-chunking** (`data/vector-search/rag-chunking.md`) — Prosa: sentence-aware, 256 tokens; doc técnico: semântico por headers; Q&A inteiro; código: a função inteira. Overlap 10–20%, mínimo 50 e máximo ~512 tokens. Sempre grave metadados do chunk (DocumentId, ChunkIndex, Section, SourceUrl) e reindexe quando o hash do conteúdo mudar.
16
+ - **backend-database-vector-search-rag** (`backend/database/vector-search-rag.md`) — Slices VSA sobre EF Core 10 + pgvector: o handler injeta `ApplicationDbContext` e `IEmbeddingGenerator` direto (sem EmbeddingService/SearchService). Busca por `d.Embedding!.CosineDistance(query)` com a distância em ordem ASC. Sem documento relevante, `NotFound` antes de chamar o LLM. Indexação em lote: um `GenerateAsync` e um `SaveChanges`.
17
+
18
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -30,7 +30,7 @@ morph-spec scaffold dotnet book get-all-books
30
30
 
31
31
  Generates 7 files: endpoint, handler, request, response, validator + entity, errors (shared at feature level). Entity/errors auto-skipped if they already exist.
32
32
 
33
- **Only use `template render` for individual files when adding to an existing slice.**
33
+ **Adding a single file to an existing slice:** read its `.hbs` template (table below) and write the file following it — `scaffold` generates whole slices only, and there is no single-file render command.
34
34
 
35
35
  ## VSA Feature Templates (Vertical Slice Architecture)
36
36
 
@@ -9,5 +9,7 @@ stacks:
9
9
 
10
10
  # Frontend Standards
11
11
 
12
- @.morph/framework/standards/frontend/design-system/naming.md
13
- @.morph/framework/standards/frontend/design-system/animations.md
12
+ - **frontend-design-system-naming** (`frontend/design-system/naming.md`) — BEM para componente reutilizável (`.card`, `.card__header`, `.card--featured`); prefixo de página para elemento específico (`.home-hero`). Custom properties em `:root` com namespace `--app-`. Estado por sufixo fixo: `--active`, `--disabled`, `--loading`, `--error`.
13
+ - **frontend-design-system-animations** (`frontend/design-system/animations.md`) — Classes `.animate-*`, `.duration-fast/normal/slow`, `.stagger-1..10`. Lista escalonada exige `animation-delay` E `animation-fill-mode: backwards`. Sempre o bloco `prefers-reduced-motion: reduce`. Além do CSS: Motion (`motion/react`) para enter/exit/layout; GSAP só para ScrollTrigger, SVG morph e timelines.
14
+
15
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -1,13 +1,18 @@
1
1
  ---
2
2
  paths:
3
- - "**/*.tsx"
4
- - "**/*.ts"
3
+ - "**/animations/**"
4
+ - "**/*gsap*"
5
+ - "**/*Gsap*"
6
+ - "**/*scroll-trigger*"
7
+ - "**/*ScrollTrigger*"
5
8
  stacks:
6
9
  - nextjs
7
10
  ---
8
11
 
9
12
  # GSAP Animation Standards
10
13
 
11
- @.morph/framework/standards/frontend/gsap/gsap-core.md
12
- @.morph/framework/standards/frontend/gsap/gsap-scrolltrigger.md
13
- @.morph/framework/standards/frontend/gsap/gsap-plugins.md
14
+ - **frontend-gsap-core** (`frontend/gsap/gsap-core.md`) — GSAP 3.13 + `@gsap/react` só para ScrollTrigger, SVG morph/draw, timelines complexas e SplitText; o resto é Motion, e NUNCA os dois no mesmo elemento. Código em `'use client'` dentro de `useGSAP()` com `{ scope: containerRef }` (nunca `useEffect`). Só transform/opacity; `prefers-reduced-motion` via `gsap.matchMedia()` é obrigatório; durations/easings vêm de `@/lib/design-tokens`.
15
+ - **frontend-gsap-scrolltrigger** (`frontend/gsap/gsap-scrolltrigger.md`) — `gsap.registerPlugin(ScrollTrigger)` uma vez no init, nunca em `useEffect`/loop, e nunca `ScrollTrigger.create()` no escopo do módulo. Scrub com ease `'none'` (0.3–1 em produção). `pin: true` + `anticipatePin: 1`; nunca pin aninhado. `ScrollTrigger.batch` em grids de 20+. `ScrollTrigger.refresh()` após conteúdo dinâmico, troca de rota e `onLoad` de imagem.
16
+ - **frontend-gsap-plugins** (`frontend/gsap/gsap-plugins.md`) — Todo plugin é gratuito no pacote público `gsap` (`npm install gsap @gsap/react`); o registry privado `@gsap/shockingly` é obsoleto. Flip: `getState()` → muda o DOM → `Flip.from(...)`. SplitText com `autoSplit: true`, animando em `onSplit`. MorphSVG exige `<path>`; DrawSVG exige stroke visível; Draggable com inércia exige InertiaPlugin.
17
+
18
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -10,4 +10,6 @@ stacks:
10
10
 
11
11
  # Hangfire Background Jobs Standards
12
12
 
13
- @.morph/framework/standards/backend/integrations/hangfire/hangfire-jobs.md
13
+ - **backend-integrations-hangfire-hangfire-jobs** (`backend/integrations/hangfire/hangfire-jobs.md`) — Hangfire 1.8.25 só com razão real em `decisions.md`; o que cabe no request fica no handler. Storage `Hangfire.PostgreSql` 1.21.1 com `UseNpgsqlConnection` no endpoint DIRETO do Neon; referencie `Newtonsoft.Json` 13.0.4. Job é classe fina e idempotente que chama o handler; `[AutomaticRetry]`, sem try/catch nem retry manual. Dashboard exige Admin autenticado.
14
+
15
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -10,8 +10,10 @@ stacks:
10
10
 
11
11
  # Infrastructure Standards
12
12
 
13
- @.morph/framework/standards/infrastructure/docker/coolify-deploy.md
14
- @.morph/framework/standards/infrastructure/docker/local-compose-isolation.md
13
+ - **infrastructure-docker-coolify-deploy** (`infrastructure/docker/coolify-deploy.md`) — Acesso só por SSH na VPS, API do Coolify em `localhost:8000/api/v1` com token server-side (nunca impresso). Env vars no Coolify, nunca no Dockerfile. Endpoint `/health`. Redeploy por `POST /api/v1/deploy?uuid=` (GET não deploya); sem `deployment_uuid` na resposta, nada foi deployado.
14
+ - **infrastructure-docker-local-compose-isolation** (`infrastructure/docker/local-compose-isolation.md`) — Cada worktree roda o próprio stack compose; portas do bloco da worktree (`PORT`, `API_PORT`, `POSTGRES_PORT`, `REDIS_PORT`) publicadas como `${VAR:-default}:container`. PROIBIDO: porta fixa no host, `container_name`. Healthcheck em todo serviço. Compose que lê a porta por outro nome: declare em `e2e.portEnvNames`.
15
+
16
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
15
17
 
16
18
  ## Infrastructure Templates
17
19
 
@@ -10,4 +10,6 @@ stacks:
10
10
 
11
11
  # Neon Auth Standards
12
12
 
13
- @.morph/framework/standards/backend/integrations/neon-auth/neon-auth.md
13
+ - **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
14
+
15
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
@@ -10,10 +10,12 @@ stacks:
10
10
 
11
11
  # Neon PostgreSQL Standards
12
12
 
13
- @.morph/framework/standards/infrastructure/neon/neon-setup.md
14
- @.morph/framework/standards/infrastructure/neon/neon-pgvector.md
15
- @.morph/framework/standards/infrastructure/neon/neon-pgrag.md
16
- @.morph/framework/standards/backend/integrations/neon-auth/neon-auth.md
13
+ - **infrastructure-neon-setup** (`infrastructure/neon/neon-setup.md`) — Duas connection strings: `DefaultConnection` (pooled `-pooler`) para a app e `MigrationConnection` (direta) para migrations e advisory locks; nunca commitadas (user-secrets / env do Coolify). Formato keyword (`Host=...;SSL Mode=Require`), não URI. `EnableRetryOnFailure(5, 10s)` por causa do scale-to-zero. Branch sempre com `--expires-at`.
14
+ - **infrastructure-neon-pgvector** (`infrastructure/neon/neon-pgvector.md`) — `Npgsql.EntityFrameworkCore.PostgreSQL` 10.0.3 + `Pgvector.EntityFrameworkCore` 0.3.0; coluna `Vector` (`vector(1536)`) e `UseVector()` no `UseNpgsql`. SEMPRE índice HNSW com operator class casando a função LINQ (senão seq scan); distância em ordem ASC. PROIBIDO: DocumentRepository, SQL cru do C# sem `decisions.md`, default `gen_random_uuid()`.
15
+ - **infrastructure-neon-pgrag** (`infrastructure/neon/neon-pgrag.md`) — A extensão `rag` é EXPERIMENTAL: protótipo e RAG só-SQL, não produção sem teste. Nunca misture modelos de embedding numa coluna (bge-small 384 × text-embedding-3-small 1536). Índice HNSW depois da carga inicial. Produção .NET com lógica própria: tool MAF tipada sobre pgvector.
16
+ - **backend-integrations-neon-auth-neon-auth** (`backend/integrations/neon-auth/neon-auth.md`) — SDK `@neondatabase/auth`: `createNeonAuth()` no server, `createAuthClient()` no client, `auth.middleware()` com `runtime='nodejs'`. Duas constantes de prefixo (`BACKEND_API_PREFIX`/`PROXY_API_PREFIX`), nunca uma só. O front não consulta o Neon para dado de negócio. .NET: token EdDSA/Ed25519, verificado pelo JWKS com BouncyCastle + `SignatureValidator` devolvendo `JsonWebToken`.
17
+
18
+ > Resumo injetado quando você lê ou edita um arquivo casado por `paths:`. Texto completo: `morph-spec standards --find "<id>"` e leia só a seção necessária de `.morph/framework/standards/<path>`.
17
19
 
18
20
  ## SQL Templates
19
21