@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 (345) hide show
  1. package/CLAUDE.md +8 -9
  2. package/README.md +38 -36
  3. package/bin/morph-spec.js +11 -63
  4. package/bin/validate.js +1 -39
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +1 -1
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/CLAUDE.md +2 -1
  9. package/framework/MORPH.md +3 -3
  10. package/framework/agents.json +19 -11
  11. package/framework/commands/morph-apply.md +1 -1
  12. package/framework/commands/morph-status.md +1 -1
  13. package/framework/docs/CLI.md +22 -31
  14. package/framework/evals/review-checklist.md +12 -2
  15. package/framework/hooks/README.md +8 -44
  16. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
  17. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
  18. package/framework/hooks/claude-code/core/chain.js +4 -65
  19. package/framework/hooks/claude-code/core/comment-guard.js +2 -16
  20. package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
  21. package/framework/hooks/claude-code/core/gate-guard.js +7 -67
  22. package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
  23. package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
  24. package/framework/hooks/claude-code/core/heavy-release.js +2 -13
  25. package/framework/hooks/claude-code/core/loop-detect.js +2 -31
  26. package/framework/hooks/claude-code/core/pre-bash-denylist.js +0 -4
  27. package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
  28. package/framework/hooks/claude-code/core/state-sync.js +12 -68
  29. package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
  30. package/framework/hooks/claude-code/core/validate-completion.js +11 -57
  31. package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
  32. package/framework/hooks/claude-code/core/worktree-context.js +10 -110
  33. package/framework/hooks/claude-code/core/worktree-create.js +6 -70
  34. package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
  35. package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
  36. package/framework/hooks/claude-code/statusline.sh +11 -11
  37. package/framework/hooks/shared/activity-logger.js +9 -89
  38. package/framework/hooks/shared/agent-reap.js +2 -17
  39. package/framework/hooks/shared/build-check.js +6 -75
  40. package/framework/hooks/shared/claim-registry.js +9 -119
  41. package/framework/hooks/shared/command-tokens.js +6 -51
  42. package/framework/hooks/shared/comment-rules.js +5 -20
  43. package/framework/hooks/shared/comment-scanner.js +6 -25
  44. package/framework/hooks/shared/comment-stripper.js +11 -85
  45. package/framework/hooks/shared/e2e-check.js +0 -0
  46. package/framework/hooks/shared/errata.js +2 -15
  47. package/framework/hooks/shared/feature-locator.js +11 -95
  48. package/framework/hooks/shared/frontend-locations.js +1 -3
  49. package/framework/hooks/shared/gate-question.js +1 -7
  50. package/framework/hooks/shared/git-exclude.js +3 -20
  51. package/framework/hooks/shared/git-paths.js +8 -104
  52. package/framework/hooks/shared/graph-freshness.js +22 -166
  53. package/framework/hooks/shared/graph-refresh.js +21 -149
  54. package/framework/hooks/shared/heavy-command.js +4 -12
  55. package/framework/hooks/shared/hook-response.js +8 -83
  56. package/framework/hooks/shared/morph-cli.js +8 -52
  57. package/framework/hooks/shared/payload-utils.js +10 -132
  58. package/framework/hooks/shared/phase-utils.js +1 -42
  59. package/framework/hooks/shared/redact-secrets.js +4 -15
  60. package/framework/hooks/shared/reparse-scan.js +6 -66
  61. package/framework/hooks/shared/slice-progress.js +4 -23
  62. package/framework/hooks/shared/source-files.js +4 -17
  63. package/framework/hooks/shared/state-reader.js +13 -183
  64. package/framework/hooks/shared/state-writer.js +8 -52
  65. package/framework/hooks/shared/stdin-reader.js +1 -11
  66. package/framework/hooks/shared/telemetry-logger.js +7 -67
  67. package/framework/hooks/shared/transcript-usage.js +7 -29
  68. package/framework/hooks/shared/worktree-root.js +7 -29
  69. package/framework/rules/csharp-standards.md +1 -1
  70. package/framework/rules/frontend-standards.md +1 -0
  71. package/framework/rules/hangfire-standards.md +1 -1
  72. package/framework/rules/neon-standards.md +1 -1
  73. package/framework/rules/nextjs-standards.md +1 -1
  74. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  75. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  76. package/framework/skills/core/morph-apply/SKILL.md +4 -4
  77. package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
  78. package/framework/skills/core/morph-machine/SKILL.md +1 -1
  79. package/framework/skills/core/morph-media/SKILL.md +1 -1
  80. package/framework/skills/core/morph-plan/SKILL.md +9 -7
  81. package/framework/skills/core/morph-review/SKILL.md +9 -6
  82. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  83. package/framework/skills/core/morph-uiux/SKILL.md +29 -12
  84. package/framework/standards/STANDARDS.json +36 -14
  85. package/framework/standards/STANDARDS.meta.json +16 -2
  86. package/framework/standards/ai-agents/agent-archetypes.md +2 -2
  87. package/framework/standards/ai-agents/agent-session.md +1 -1
  88. package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
  89. package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
  90. package/framework/standards/ai-agents/guardrails.md +1 -1
  91. package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
  92. package/framework/standards/ai-agents/observability-patterns.md +1 -1
  93. package/framework/standards/ai-agents/production.md +1 -1
  94. package/framework/standards/ai-agents/prompt-sources.md +2 -2
  95. package/framework/standards/ai-agents/providers/model-registry.md +1 -1
  96. package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
  97. package/framework/standards/ai-agents/setup.md +2 -2
  98. package/framework/standards/ai-agents/sweet-spot.md +2 -2
  99. package/framework/standards/ai-agents/tool-adapters.md +0 -2
  100. package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
  101. package/framework/standards/ai-agents/workflows.md +1 -1
  102. package/framework/standards/architecture/code-comments.md +1 -1
  103. package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
  104. package/framework/standards/backend/api/validation.md +1 -1
  105. package/framework/standards/backend/dotnet/testing.md +10 -4
  106. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
  107. package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
  108. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
  109. package/framework/standards/data/nosql/cache/redis.md +1 -1
  110. package/framework/standards/data/nosql/cosmos-db.md +3 -3
  111. package/framework/standards/frontend/admin-app/forms.md +1 -1
  112. package/framework/standards/frontend/design-system/art-direction.md +88 -0
  113. package/framework/standards/frontend/design-system/premium-finish.md +77 -2
  114. package/framework/standards/frontend/nextjs/testing.md +10 -6
  115. package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
  116. package/framework/templates/.idea/morph-templates.xml +92 -0
  117. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  118. package/framework/templates/CLAUDE.md.core.template +3 -3
  119. package/framework/templates/IDE-SNIPPETS.md +262 -262
  120. package/framework/templates/REGISTRY.json +9 -9
  121. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  122. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  123. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  124. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  125. package/framework/templates/docs/plan.md +3 -1
  126. package/framework/templates/docs/ui-mockups.md +4 -0
  127. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  128. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  129. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  130. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  131. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  132. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  133. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  134. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  135. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  136. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  137. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  138. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  139. package/framework/templates/state.template.json +17 -17
  140. package/framework/templates/ui/design-system.css +226 -226
  141. package/package.json +1 -1
  142. package/src/commands/context/context-refresh.js +0 -16
  143. package/src/commands/dashboard/dashboard.js +2 -8
  144. package/src/commands/e2e/e2e.js +17 -104
  145. package/src/commands/graph/affected.js +0 -14
  146. package/src/commands/graph/build.js +11 -50
  147. package/src/commands/graph/explain.js +0 -16
  148. package/src/commands/graph/status.js +1 -12
  149. package/src/commands/mcp/mcp-setup.js +4 -62
  150. package/src/commands/observability/cost.js +5 -57
  151. package/src/commands/observability/telemetry.js +1 -14
  152. package/src/commands/project/branch.js +4 -45
  153. package/src/commands/project/claim.js +4 -48
  154. package/src/commands/project/doctor.js +48 -302
  155. package/src/commands/project/env.js +1 -27
  156. package/src/commands/project/fleet.js +3 -32
  157. package/src/commands/project/init.js +4 -68
  158. package/src/commands/project/install-plugin-cmd.js +0 -1
  159. package/src/commands/project/standards.js +1 -62
  160. package/src/commands/project/status.js +0 -53
  161. package/src/commands/project/update.js +31 -239
  162. package/src/commands/project/worktree.js +108 -882
  163. package/src/commands/scaffold/scaffold.js +0 -25
  164. package/src/commands/state/advance.js +14 -85
  165. package/src/commands/state/approve.js +15 -97
  166. package/src/commands/state/archive.js +12 -64
  167. package/src/commands/state/auto-approval.js +2 -5
  168. package/src/commands/state/create.js +22 -109
  169. package/src/commands/state/delete.js +11 -63
  170. package/src/commands/state/finish.js +98 -475
  171. package/src/commands/state/gate-check.js +22 -132
  172. package/src/commands/state/gate-decision.js +3 -39
  173. package/src/commands/state/handoff.js +3 -30
  174. package/src/commands/state/list.js +14 -84
  175. package/src/commands/state/report-check.js +8 -58
  176. package/src/commands/state/retype.js +4 -26
  177. package/src/commands/validation/index.js +0 -3
  178. package/src/commands/validation/mutate.js +11 -11
  179. package/src/commands/validation/plan-check.js +4 -2
  180. package/src/commands/validation/tests-audit.js +7 -0
  181. package/src/commands/validation/validate-feature.js +10 -97
  182. package/src/commands/validation/verify.js +30 -113
  183. package/src/core/paths/output-schema.js +2 -101
  184. package/src/core/state/feature-identity.js +1 -25
  185. package/src/core/state/gate-order.js +0 -27
  186. package/src/core/state/lifecycle-audit.js +8 -100
  187. package/src/core/state/state-manager.js +22 -304
  188. package/src/core/templates/template-registry.js +2 -97
  189. package/src/core/templates/template-renderer.js +13 -93
  190. package/src/core/templates/template-seed.js +0 -1
  191. package/src/core/work-types.js +1 -36
  192. package/src/dashboard/app/App.jsx +1 -8
  193. package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
  194. package/src/dashboard/app/components/CommandPalette.jsx +0 -7
  195. package/src/dashboard/app/components/DetailPanel.jsx +1 -5
  196. package/src/dashboard/app/components/EntityLink.jsx +0 -12
  197. package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
  198. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
  199. package/src/dashboard/app/components/Markdown.jsx +0 -9
  200. package/src/dashboard/app/components/PhaseRail.jsx +0 -11
  201. package/src/dashboard/app/components/SearchFilter.jsx +0 -4
  202. package/src/dashboard/app/components/Sidebar.jsx +0 -5
  203. package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
  204. package/src/dashboard/app/hooks/usePref.js +1 -5
  205. package/src/dashboard/app/lib/format.js +0 -2
  206. package/src/dashboard/app/lib/phases.js +1 -5
  207. package/src/dashboard/app/lib/project.js +0 -12
  208. package/src/dashboard/app/main.jsx +13 -13
  209. package/src/dashboard/app/pages/AgentMap.jsx +1 -8
  210. package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
  211. package/src/dashboard/app/pages/Features.jsx +1 -8
  212. package/src/dashboard/app/pages/Guide.jsx +0 -1
  213. package/src/dashboard/app/pages/HookInspector.jsx +2 -23
  214. package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
  215. package/src/dashboard/app/pages/Portfolio.jsx +0 -1
  216. package/src/dashboard/app/pages/RulesMap.jsx +1 -16
  217. package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
  218. package/src/dashboard/app/pages/Standards.jsx +2 -17
  219. package/src/dashboard/scanner/activity-scanner.js +10 -69
  220. package/src/dashboard/scanner/agents-scanner.js +1 -23
  221. package/src/dashboard/scanner/docs-scanner.js +0 -6
  222. package/src/dashboard/scanner/feature-status.js +2 -5
  223. package/src/dashboard/scanner/features-scanner.js +10 -66
  224. package/src/dashboard/scanner/hooks-scanner.js +5 -28
  225. package/src/dashboard/scanner/index.js +0 -4
  226. package/src/dashboard/scanner/portfolio.js +5 -29
  227. package/src/dashboard/scanner/project-api.js +8 -54
  228. package/src/dashboard/scanner/rules-scanner.js +1 -2
  229. package/src/dashboard/scanner/skills-scanner.js +2 -12
  230. package/src/dashboard/scanner/standards-scanner.js +2 -6
  231. package/src/dashboard/vite-plugin-morph-data.js +5 -19
  232. package/src/dashboard/vite.config.js +24 -24
  233. package/src/lib/ai/ai-checks.js +7 -52
  234. package/src/lib/ai/ai-project-scan.js +14 -108
  235. package/src/lib/ai/code-audit.js +7 -63
  236. package/src/lib/ai/pin-drift.js +8 -51
  237. package/src/lib/ai/registry-audit.js +11 -90
  238. package/src/lib/ci/ci-verify.js +4 -19
  239. package/src/lib/ci/github-ci.js +9 -37
  240. package/src/lib/ci/workflow-scaffold.js +14 -10
  241. package/src/lib/detectors/claude-config-detector.js +13 -74
  242. package/src/lib/detectors/index.js +4 -50
  243. package/src/lib/e2e/applicability.js +37 -0
  244. package/src/lib/e2e/compose-env.js +5 -66
  245. package/src/lib/e2e/stack-teardown.js +6 -40
  246. package/src/lib/generators/architecture-scanner.js +18 -156
  247. package/src/lib/generators/handoff-generator.js +9 -53
  248. package/src/lib/generators/scaffold-generator.js +5 -101
  249. package/src/lib/generators/settings-generator.js +4 -37
  250. package/src/lib/git/branch-classifier.js +11 -84
  251. package/src/lib/git/default-branch.js +14 -169
  252. package/src/lib/git/evidence-staleness.js +1 -0
  253. package/src/lib/git/gh.js +2 -17
  254. package/src/lib/git/pending-prs.js +6 -37
  255. package/src/lib/git/pr-files.js +3 -28
  256. package/src/lib/git/pr-index.js +3 -34
  257. package/src/lib/git/tree-fingerprint.js +3 -15
  258. package/src/lib/git/tree-state.js +6 -33
  259. package/src/lib/graph/god-nodes.js +2 -22
  260. package/src/lib/graph/graph-health.js +3 -24
  261. package/src/lib/graph/graph-loader.js +5 -56
  262. package/src/lib/graph/graphify-runtime.js +7 -37
  263. package/src/lib/graph/graphifyignore.js +8 -74
  264. package/src/lib/graph/sanitize.js +16 -136
  265. package/src/lib/heavy/cleanup.js +1 -1
  266. package/src/lib/installers/effects.js +8 -93
  267. package/src/lib/installers/mcp-installer.js +9 -169
  268. package/src/lib/installers/memory-installer.js +4 -27
  269. package/src/lib/installers/templates-reconcile.js +5 -60
  270. package/src/lib/mutation/adapters.js +6 -17
  271. package/src/lib/mutation/dotnet-introspection.js +20 -106
  272. package/src/lib/mutation/runner.js +23 -75
  273. package/src/lib/mutation/subset.js +1 -4
  274. package/src/lib/mutation/trx.js +0 -1
  275. package/src/lib/plan/artifact-vocabulary.js +5 -25
  276. package/src/lib/plan/checks.js +34 -33
  277. package/src/lib/plan/plan-parser.js +7 -8
  278. package/src/lib/projects/registry.js +7 -44
  279. package/src/lib/review-reports.js +11 -26
  280. package/src/lib/risk-detector.js +41 -173
  281. package/src/lib/specs/adr-index.js +6 -34
  282. package/src/lib/specs/frontmatter.js +2 -15
  283. package/src/lib/specs/gfm-table.js +1 -7
  284. package/src/lib/specs/revocations.js +1 -14
  285. package/src/lib/specs/screen-checklist.js +47 -124
  286. package/src/lib/stack/dotnet-build-ignores.js +2 -6
  287. package/src/lib/stack/stack-profile.js +1 -28
  288. package/src/lib/stack/stack-scan.js +1 -8
  289. package/src/lib/stack-filter.js +4 -27
  290. package/src/lib/standards/registry.js +1 -7
  291. package/src/lib/standards/sections.js +2 -13
  292. package/src/lib/standards/verified-against.js +19 -191
  293. package/src/lib/tasks/eval-node.js +21 -169
  294. package/src/lib/tasks/linux-runner.js +18 -142
  295. package/src/lib/tasks/test-nodes.js +43 -240
  296. package/src/lib/tasks/test-runner.js +43 -341
  297. package/src/lib/telemetry/metrics.js +7 -36
  298. package/src/lib/telemetry/telemetry-reader.js +13 -125
  299. package/src/lib/telemetry/verify-event.js +2 -30
  300. package/src/lib/tests-audit/audit.js +96 -0
  301. package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
  302. package/src/lib/validators/code-budget/index.js +0 -7
  303. package/src/lib/validators/contracts/contract-compliance-validator.js +2 -24
  304. package/src/lib/validators/contracts/index.js +0 -6
  305. package/src/lib/validators/nextjs/index.js +0 -5
  306. package/src/lib/validators/nextjs/next-component-validator.js +4 -81
  307. package/src/lib/validators/packages/index.js +0 -6
  308. package/src/lib/validators/packages/package-validator.js +6 -56
  309. package/src/lib/validators/reuse/index.js +0 -8
  310. package/src/lib/validators/reuse/reuse-validator.js +11 -101
  311. package/src/lib/validators/reuse/symbol-scanner.js +6 -62
  312. package/src/lib/validators/traceability-check.js +5 -63
  313. package/src/lib/validators/validation-runner.js +21 -158
  314. package/src/lib/validators/verify-runner.js +85 -376
  315. package/src/lib/work-type-detector.js +5 -67
  316. package/src/lib/worktree/docker-audit.js +17 -136
  317. package/src/lib/worktree/env-allocator.js +10 -98
  318. package/src/lib/worktree/feature-truth.js +12 -115
  319. package/src/lib/worktree/finish-backup.js +32 -242
  320. package/src/lib/worktree/fleet-scanner.js +13 -95
  321. package/src/lib/worktree/ignored-triage.js +15 -91
  322. package/src/lib/worktree/vscode-settings.js +2 -3
  323. package/src/lib/worktree/worktree-audit.js +21 -213
  324. package/src/lib/worktree/worktree-include.js +3 -5
  325. package/src/scripts/global-install.js +3 -9
  326. package/src/scripts/install-plugin.js +1 -13
  327. package/src/scripts/setup-infra.js +17 -147
  328. package/src/utils/agents-installer.js +29 -276
  329. package/src/utils/banner.js +4 -25
  330. package/src/utils/claude-md-injector.js +1 -37
  331. package/src/utils/claude-settings-manager.js +1 -52
  332. package/src/utils/file-copier.js +11 -95
  333. package/src/utils/hooks-installer.js +21 -298
  334. package/src/utils/skills-installer.js +4 -101
  335. package/src/utils/version-checker.js +4 -83
  336. package/framework/commands/morph-proposal.md +0 -9
  337. package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
  338. package/framework/hooks/git/pre-commit/agents.sh +0 -25
  339. package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
  340. package/framework/hooks/git/pre-push/run-tests.sh +0 -44
  341. package/src/commands/utils/index.js +0 -3
  342. package/src/lib/detectors/design-system-detector.js +0 -188
  343. package/src/lib/generators/design-system-generator.js +0 -508
  344. package/src/lib/specs/old-format.js +0 -68
  345. package/src/utils/agent-id-mapper.js +0 -11
@@ -115,8 +115,12 @@ respectively and the test suite stays green.
115
115
 
116
116
  ## Test budget
117
117
 
118
- The plan (`morph-plan`) gives each task a budget: **one test per postcondition, plus one
119
- regression test per bug fixed**. Going above it takes a one-line justification in the task.
118
+ Tests are paid for by **risk, not by new code**. The plan (`morph-plan`) marks each scenario
119
+ with `prova:` and, for a test, a `risco:` (business rule, money, external contract, concurrency,
120
+ data integrity, regression). The budget is **one test per risky scenario, plus the regression test of
121
+ the bug being fixed**; going above it takes a one-line justification on the scenario. A scenario
122
+ without risk — CRUD, DTO, mapping, wiring, constants, layout — is `prova: leitura`: the Gate 3
123
+ verifier proves it by reading the code, and it gets no test.
120
124
 
121
125
  - **Table before copy.** Scenarios of the same behavior — including its edge cases (null, empty,
122
126
  boundary) — are rows of one `[Theory]` (`[InlineData]`/`[MemberData]`), never N `[Fact]` methods
@@ -125,11 +129,13 @@ regression test per bug fixed**. Going above it takes a one-line justification i
125
129
  UI copy, tenant config or prose proves the file's version, not behavior. Enforce it with an
126
130
  analyzer, a lint rule or a validation at publish time, outside the suite count. The versioned
127
131
  prompt case (at most one structural test) is `ai-agents-testing-ai`.
132
+ - **One layer per behavior.** A rule tested in the handler is not tested again through the
133
+ endpoint or the SQL integration: pick the cheapest layer that bites (wiring is the verifier's).
128
134
  - **Shared fixture by default.** Reuse the project's `WebApplicationFactory` fixture, fakes and
129
135
  builders. Before writing a new helper, look for the existing one (`morph-spec graph explain
130
136
  <Name>`; fallback without a graph: `grep -rn`).
131
- - **Order is free, proof is not the implementer's:** a bug gets a regression test, and pure logic
132
- gets its tests, in whatever order the code comes out. The implementer does not run red→green
137
+ - **Order is free, proof is not the implementer's:** a bug gets a regression test, and a risky
138
+ scenario gets its test, in whatever order the code comes out. The implementer does not run red→green
133
139
  cycles nor mutate production code to watch a test fail; it runs its own filter once before the
134
140
  report, and the Gate 3 mutation proof is what shows the test bites.
135
141
  - **A surviving mutant** of the Gate 3 mutation proof is killed by tightening an existing assertion
@@ -225,13 +225,13 @@ public sealed class SearchBooksHandler(
225
225
  When a slice needs concurrency control (`SELECT ... FOR UPDATE`, lease/outbox semantics,
226
226
  `DbUpdateConcurrencyException` retry loops) or SQL that LINQ cannot express, write a
227
227
  **hand-written, purpose-built store** with intention-named methods (e.g. `OutboxStore.ClaimBatchAsync`,
228
- `LeaseStore.TryAcquireAsync`) — and document the justification in `decisions.md`.
228
+ `LeaseStore.TryAcquireAsync`) — and document the justification in `plan.md` (`## Decisões`).
229
229
  Never a generic-per-entity abstraction.
230
230
 
231
231
  **Rule of thumb:** everything → `ApplicationDbContext` in the slice. Concurrency or
232
- specific SQL → dedicated hand-written store + `decisions.md` entry. Never reach for raw SQL
232
+ specific SQL → dedicated hand-written store + `plan.md` (`## Decisões`) entry. Never reach for raw SQL
233
233
  (`FromSqlRaw`) unless a query genuinely cannot be expressed in LINQ — and document that
234
- in `decisions.md`.
234
+ in `plan.md` (`## Decisões`).
235
235
 
236
236
  ---
237
237
 
@@ -307,8 +307,8 @@ builder.Services.AddExceptionHandler<CustomExceptionHandler>()
307
307
  | Share handlers across features | Each feature has its own handler, even if similar |
308
308
  | Use constructor + field for DI | Use primary constructors: `class Handler(IDep dep)` |
309
309
  | Group by technical concern (Controllers/, Services/) | Group by feature: `Features/BookFeature/CreateBook/` |
310
- | Route data access through any indirection layer | Inject `DbContext` and write LINQ in the slice; dedicated store only with `decisions.md` justification |
311
- | Use raw SQL (`FromSqlRaw` / `ExecuteQuery(string)`) by default | Use LINQ; raw SQL only with a `decisions.md` justification |
310
+ | Route data access through any indirection layer | Inject `DbContext` and write LINQ in the slice; dedicated store only with `plan.md` (`## Decisões`) justification |
311
+ | Use raw SQL (`FromSqlRaw` / `ExecuteQuery(string)`) by default | Use LINQ; raw SQL only with a `plan.md` (`## Decisões`) justification |
312
312
 
313
313
  ---
314
314
 
@@ -15,7 +15,7 @@ Background job processing for .NET 10 VSA applications with Hangfire on Neon Pos
15
15
 
16
16
  ## When to use Hangfire
17
17
 
18
- Hangfire is in the stack **only with a real reason**: work that must survive a process restart, run on a schedule, or be retried later (daily reindex, delayed reminders, retrying a flaky third party). Work that fits in the request (send one email after a signup, index one uploaded document) runs in the slice handler. Record the reason in `decisions.md`.
18
+ Hangfire is in the stack **only with a real reason**: work that must survive a process restart, run on a schedule, or be retried later (daily reindex, delayed reminders, retrying a flaky third party). Work that fits in the request (send one email after a signup, index one uploaded document) runs in the slice handler. Record the reason in `plan.md` (`## Decisões`).
19
19
 
20
20
  Hangfire provides fire-and-forget, delayed and recurring (cron) jobs, continuations, a dashboard and automatic retries.
21
21
 
@@ -64,7 +64,7 @@ place to audit.
64
64
  **The exception process:** a feature that genuinely needs the frontend to reach Neon directly for
65
65
  business data (for example, via the Neon MCP `provision_neon_data_api` tool) is an exception, not a
66
66
  default — treat it exactly like vertical-slice.md's "Stores dedicados (exceção justificada)" clause:
67
- document the justification in the feature's `decisions.md` before writing the code. A tool being
67
+ document the justification in the feature's `plan.md` (`## Decisões`) before writing the code. A tool being
68
68
  available is not, by itself, justification.
69
69
 
70
70
  See [Row-Level Security Integration](#row-level-security-integration) for how to constrain a
@@ -727,7 +727,7 @@ Each Neon branch gets its own isolated auth data. This means:
727
727
  fetch and cache the JWKS yourself and verify EdDSA signatures manually (BouncyCastle)
728
728
  11. **Route business data through the .NET backend by default** — the frontend's only inherent
729
729
  reason to talk to Neon directly is its own auth/session SDK; a direct Data API call needs a
730
- `decisions.md` ADR (see [Data Access Boundary](#data-access-boundary--backend-first-by-default))
730
+ `plan.md` (`## Decisões`) ADR (see [Data Access Boundary](#data-access-boundary--backend-first-by-default))
731
731
 
732
732
  ---
733
733
 
@@ -179,4 +179,4 @@ The minute comes from `TimeProvider` (testable with `FakeTimeProvider`), never `
179
179
  - Never omit TTL -- unbounded keys fill memory
180
180
  - Never wrap the cache in a `XService`/repository class -- cache-aside is three lines in the handler
181
181
  - Never return `null` for "not found" -- the handler returns `Result` with `NotFound`
182
- - Never swallow `RedisConnectionException` in an empty `catch`: a cache outage must be visible (health check + logs). Falling back to the database while Redis is down is a product decision: record it in `decisions.md` and implement it once, at the cache boundary, not per call site
182
+ - Never swallow `RedisConnectionException` in an empty `catch`: a cache outage must be visible (health check + logs). Falling back to the database while Redis is down is a product decision: record it in `plan.md` (`## Decisões`) and implement it once, at the cache boundary, not per call site
@@ -3,7 +3,7 @@
3
3
  > **Scope:** data-nosql
4
4
  > **Layer:** 3 (opcional — nenhuma rule nem persona o carrega; só sob demanda via `morph-spec standards --find`, depois de a decisão existir)
5
5
  > **Keywords:** cosmos-db, nosql, document-database, event-store, partition-key, azure-cosmos
6
- > **Load When:** `decisions.md` already records the choice of Cosmos DB over Neon for a specific store
6
+ > **Load When:** `plan.md` (`## Decisões`) already records the choice of Cosmos DB over Neon for a specific store
7
7
 
8
8
  **Verified against:** .NET 10 (SDK 10.0.400-preview) + Microsoft.Azure.Cosmos 3.63.2 + Newtonsoft.Json 13.0.4 — every `csharp` block below compiles with `TreatWarningsAsErrors` against the `Result`/`Error` types of `backend-dotnet-result-pattern`. Last-verified: 2026-10-03.
9
9
 
@@ -11,7 +11,7 @@
11
11
 
12
12
  ## Overview
13
13
 
14
- **Cosmos DB is not part of the stack.** The default store is Neon (Postgres) through EF Core, with the `ApplicationDbContext` used directly in the slice. Reach for Cosmos only when a requirement Postgres cannot meet is written down in `decisions.md` (the "why not Neon" included). Everything below applies the stack conventions to that exception: the client is injected straight into the slice handler, ids are `Guid.CreateVersion7()`, expected failures are `Result` values, and classes are `sealed`.
14
+ **Cosmos DB is not part of the stack.** The default store is Neon (Postgres) through EF Core, with the `ApplicationDbContext` used directly in the slice. Reach for Cosmos only when a requirement Postgres cannot meet is written down in `plan.md` (`## Decisões`) (the "why not Neon" included). Everything below applies the stack conventions to that exception: the client is injected straight into the slice handler, ids are `Guid.CreateVersion7()`, expected failures are `Result` values, and classes are `sealed`.
15
15
 
16
16
  ## When It Can Be Justified
17
17
 
@@ -155,7 +155,7 @@ using var feed = container.GetItemQueryIterator<OrderEvent>(
155
155
 
156
156
  | Do NOT | Do Instead |
157
157
  |--------|------------|
158
- | Pick Cosmos without a `decisions.md` entry | Neon (Postgres) via EF Core is the default store |
158
+ | Pick Cosmos without a `plan.md` (`## Decisões`) entry | Neon (Postgres) via EF Core is the default store |
159
159
  | Wrap `Container` in a generic `CosmosRepository<T>` | Inject the keyed `Container` into the slice handler |
160
160
  | `try/catch (CosmosException)` for 404/409 | Stream APIs + `StatusCode` → `Result` |
161
161
  | `Guid.NewGuid()` for document ids | `Guid.CreateVersion7()` |
@@ -16,7 +16,7 @@ An admin app has **one** form pattern. It is the base pattern of `nextjs/forms.m
16
16
  - **The Zod schema is shared.** The same schema types the form, validates it, and parses the API response (`admin-app/data-table.md` parses the list query with Zod too). One source of truth per entity.
17
17
  - **react-hook-form keeps inputs uncontrolled** — a 30-field form does not re-render on every keystroke, which is what `useState` per field does.
18
18
  - **shadcn's current form docs are written around it** (`Field`, `FieldLabel`, `FieldError` + `Controller`).
19
- - **Coexisting styles are the defect this rule exists for.** Measured in a real admin app: three form styles side by side (react-hook-form in some screens, hand-rolled `useState` in others) — three validation behaviours for one product, and a redesign that changed none of them. A screen that needs something the pattern cannot do registers the exception in `decisions.md`; it does not start a fourth style.
19
+ - **Coexisting styles are the defect this rule exists for.** Measured in a real admin app: three form styles side by side (react-hook-form in some screens, hand-rolled `useState` in others) — three validation behaviours for one product, and a redesign that changed none of them. A screen that needs something the pattern cannot do registers the exception in `plan.md` (`## Decisões`); it does not start a fourth style.
20
20
 
21
21
  ## Core Rules
22
22
 
@@ -0,0 +1,88 @@
1
+ # Art Direction — Decide What the Screen Is Before Drawing It
2
+
3
+ > **Scope:** frontend-design-system
4
+ > **Layer:** 3
5
+ > **Keywords:** art direction, concept, visual metaphor, design direction, template look, generic ui, memorable, mobile-first, brand moment
6
+ > **Load When:** ui-designer active, before any component of a new or changed screen
7
+
8
+ **Verified against:** the `login-ciclo-do-produto` design round (2026-10-06): a correct, plain design was rejected; a second round with a written direction, an HTML prototype and a screenshot self-review was approved on first sight. Last-verified: 2026-10-06.
9
+
10
+ ---
11
+
12
+ A design that follows the tokens, has every state and passes WCAG can still be forgettable. What made the difference in practice was not talent but order: a direction written before any component, a real prototype, and a critical look at the rendered result. This standard is the first of the three.
13
+
14
+ ---
15
+
16
+ ## Section 1: Write the Direction First
17
+
18
+ Before the first component, answer four questions in `1-design/direcao.md`:
19
+
20
+ 1. **Concept in one sentence.** What the screen says, not what it contains. *"The login tells the product cycle in scenes while you sign in."*
21
+ 2. **Visual metaphor.** The image that organizes the layout. *Stories; a control room; a ticket being stamped; a garden that grows.*
22
+ 3. **What makes it memorable.** The one thing someone would describe to a colleague. If nothing, the direction is not done.
23
+ 4. **What it must NOT look like.** Name the generic version you are avoiding (Section 4).
24
+
25
+ Only then pick components. A component chosen before the direction is chosen by habit.
26
+
27
+ ---
28
+
29
+ ## Section 2: Explore Two or Three Directions
30
+
31
+ For a new screen or a brand moment (login, onboarding, first screen, empty state, success, showcase), write two or three directions that are **distinct**, not three color variations of one layout. For each: concept, metaphor, the mobile idea, the motion idea, and the risk. Recommend one and say why, in terms of the product and the user, not taste.
32
+
33
+ A changed screen inside an established system may need only one direction, written the same way, with what changes and why.
34
+
35
+ ---
36
+
37
+ ## Section 3: Good and Bad Directions
38
+
39
+ | Bad direction | Why it fails | Good direction |
40
+ |---|---|---|
41
+ | "Clean, modern login with the logo and a form." | Describes every login. Nothing to remember. | "The panel tells the product cycle in three scenes, stories-style; the form is a sheet the thumb reaches." |
42
+ | "Dashboard with cards for each metric." | Content, not concept. Layout by habit. | "A control room: one hero number the operator acts on, the rest as quiet instruments around it." |
43
+ | "Empty state with an icon and a button." | The default of every kit. | "The empty list is the first page of a notebook: an illustrated first entry shows what a good one looks like." |
44
+ | "Mobile: same layout, stacked." | Mobile derived, not designed. | "Mobile is an app: bottom sheet, thumb-zone actions, the scene compressed to a strip above." |
45
+
46
+ ---
47
+
48
+ ## Section 4: Template Tells to Avoid
49
+
50
+ Each one is fine in isolation; together they read as "generated". If the screen has three or more, raise it.
51
+
52
+ - Raw shadcn look: default radius, default gray border, no brand in any surface.
53
+ - A white card centered on a gray background, logo on top, form inside.
54
+ - A generic icon list (three features, three Lucide icons, three lines of copy).
55
+ - Stack-default or AI-tell fonts with no tracking or scale decision (see `premium-finish` Section 1).
56
+ - Flat solid backgrounds with no light, layer or texture.
57
+ - Mobile as the desktop stacked: centered card, tiny targets, actions out of thumb reach.
58
+ - Motion as decoration: everything fades in, nothing tells a sequence.
59
+ - Made-up numbers on illustrative cards (`R$ 12.430`, `98% satisfaction`).
60
+
61
+ ---
62
+
63
+ ## Section 5: Mobile Is Designed, Not Derived
64
+
65
+ Draw the phone at 390 px as its own composition: bottom sheet for the primary task, actions in the thumb zone, targets of at least 44 px, the brand scene compressed rather than removed. Then check the desktop is not the phone blown up either.
66
+
67
+ ---
68
+
69
+ ## Section 6: Motion With Intent
70
+
71
+ Motion should tell something (a sequence, a cause, a state change). Pick the lightest tool that delivers it: CSS transition and `@keyframes`, then CSS scroll-driven animation, then Framer Motion, then GSAP with ScrollTrigger and timelines. Budget: 60 fps, only `transform` and `opacity`, no layout thrash, lazy below the fold. `prefers-reduced-motion` gets a composed static version, designed as carefully as the animated one.
72
+
73
+ ---
74
+
75
+ ## Section 7: Prototype and Self-Review
76
+
77
+ The direction is proved in a rendered prototype, not in prose: `1-design/prototipos/{screen}.html`, self-contained, using the project's REAL tokens read from its token CSS (the design-system doc can be stale), desktop and phone side by side in a device frame, with animated / static / pause controls. Serve it over local HTTP, screenshot at 1280 and 390, judge against this standard and the system bar (hierarchy, contrast, rhythm, mobile, "does it look like a template?"), fix, repeat. The final screenshots sit next to the HTML.
78
+
79
+ ---
80
+
81
+ ## Checklist
82
+
83
+ - [ ] `direcao.md` answers concept, metaphor, memorable, must-not-look-like
84
+ - [ ] Two or three distinct directions for a new screen or brand moment, one recommended with a reason
85
+ - [ ] Fewer than three template tells on the final screen
86
+ - [ ] Mobile composed at 390 px on its own terms
87
+ - [ ] Motion tells something, with a composed reduced-motion version
88
+ - [ ] Prototype rendered, screenshotted at 1280 and 390, and revised until it passes
@@ -3,9 +3,9 @@
3
3
  > **Scope:** frontend-design-system
4
4
  > **Layer:** 3
5
5
  > **Keywords:** premium, polish, typography, film grain, atmosphere, load-in, scrim, legibility over video, accent
6
- > **Load When:** ui-designer active, or the brief asks for a premium or high-end feel
6
+ > **Load When:** ui-designer active (every surface), or the brief asks for a premium or high-end feel
7
7
 
8
- **Verified against:** Production landing pages, CSS-only techniques, no runtime dependency. Last-verified: 2026-08-21.
8
+ **Verified against:** Production landing pages, CSS-only techniques, no runtime dependency; app patterns from an approved login prototype (2026-10-06). Last-verified: 2026-10-06.
9
9
 
10
10
  ---
11
11
 
@@ -148,6 +148,78 @@ Finish with fresh eyes: open the page as if you were the client. Whatever looks
148
148
 
149
149
  ---
150
150
 
151
+ ## Section 8: Patterns For App Screens
152
+
153
+ The finish is not only for marketing pages. Login, onboarding, the first screen after sign-up, an empty state and a success moment are brand moments inside the product, and a data screen also has to look designed: elegant density, illustrated empty states, skeletons with shimmer, transitions between states. These patterns come from an app login that was approved on first sight after a plain one was rejected.
154
+
155
+ ### Bottom sheet on mobile
156
+
157
+ On a phone the form lives in a sheet anchored to the bottom, inside the thumb zone, with the brand scene above it. Never the desktop card centered and shrunk.
158
+
159
+ ```css
160
+ .sheet {
161
+ position: fixed; inset: auto 0 0 0;
162
+ padding: 12px 20px calc(20px + env(safe-area-inset-bottom));
163
+ border-radius: 24px 24px 0 0;
164
+ background: var(--surface);
165
+ box-shadow: 0 -12px 40px color-mix(in oklab, var(--brand) 18%, transparent);
166
+ }
167
+ .sheet::before {
168
+ content: ''; display: block; width: 36px; height: 4px; margin: 0 auto 16px;
169
+ border-radius: 2px; background: var(--border);
170
+ }
171
+ .sheet button, .sheet input { min-height: 44px; }
172
+ ```
173
+
174
+ ### Stories-style progress
175
+
176
+ A sequence of scenes reads like stories: thin segmented bars at the top, each filling while its scene is on. One keyframe, the delay per segment, and the scene swap keyed to the same delays.
177
+
178
+ ```css
179
+ .stories { display: flex; gap: 4px; }
180
+ .stories span { flex: 1; height: 3px; border-radius: 2px; background: color-mix(in oklab, var(--fg) 20%, transparent); overflow: hidden; }
181
+ .stories span::after {
182
+ content: ''; display: block; height: 100%; background: var(--fg);
183
+ transform: scaleX(0); transform-origin: left;
184
+ animation: fill var(--beat) linear forwards;
185
+ animation-delay: calc(var(--i) * var(--beat));
186
+ }
187
+ @keyframes fill { to { transform: scaleX(1); } }
188
+ ```
189
+
190
+ ### Synchronized highlight by `animation-delay`
191
+
192
+ When a scene card lights up while its story bar runs, both share `--i` and `--beat`. No JavaScript timer: the browser keeps them in step, and pausing is one `animation-play-state: paused` on the parent.
193
+
194
+ ### Scene cards without real data
195
+
196
+ A card that illustrates the product cycle shows shapes, icons and labels, never a metric. A made-up number on a real screen reads as a real number ("R$ 12.430 recovered") and becomes a claim nobody can back. Use bars without values, avatars without names, a check without a count.
197
+
198
+ ### Dot grid faded with `mask-image`
199
+
200
+ Texture for a hero or a sheet backdrop, cheaper than an image and on brand:
201
+
202
+ ```css
203
+ .dots {
204
+ background-image: radial-gradient(color-mix(in oklab, var(--fg) 22%, transparent) 1px, transparent 1px);
205
+ background-size: 16px 16px;
206
+ mask-image: radial-gradient(ellipse at 50% 30%, #000 30%, transparent 75%);
207
+ }
208
+ ```
209
+
210
+ ### Static version for reduced motion
211
+
212
+ `prefers-reduced-motion: reduce` gets a composed still frame (the best scene, every bar full, the highlight on), not the first frame of a stopped animation.
213
+
214
+ ```css
215
+ @media (prefers-reduced-motion: reduce) {
216
+ .stories span::after { animation: none; transform: scaleX(1); }
217
+ .scene { animation: none; opacity: 1; }
218
+ }
219
+ ```
220
+
221
+ ---
222
+
151
223
  ## Checklist
152
224
 
153
225
  - [ ] Display font chosen from the brand, not from stack defaults or the AI-tell list
@@ -157,4 +229,7 @@ Finish with fresh eyes: open the page as if you were the client. Whatever looks
157
229
  - [ ] Directional scrim plus text-shadow on every text-over-media block
158
230
  - [ ] Accent used in three places or fewer
159
231
  - [ ] Details swept: scrollbar, selection, hairlines, hover micro-transitions
232
+ - [ ] App screen: mobile designed on its own (bottom sheet, thumb zone, 44px targets), never the desktop stacked
233
+ - [ ] No invented number or data on a real screen
234
+ - [ ] Reduced motion gets a composed static frame
160
235
  - [ ] Fresh-eyes pass done, and it does not read as a template
@@ -17,7 +17,7 @@ Vitest + React Testing Library for component tests. MSW for API mocking. Co-loca
17
17
  - ALWAYS use `@testing-library/user-event` for interactions — not `fireEvent`
18
18
  - ALWAYS mock the API layer with MSW — never mock `fetch` directly
19
19
  - NEVER test implementation details — test what the user sees
20
- - ALWAYS test the happy path + one error path per component
20
+ - ONLY test a component when a plan scenario with `risco:` lives in it — a component is not a unit of the budget
21
21
 
22
22
  ## Test File Co-location
23
23
 
@@ -103,9 +103,13 @@ export function QueryClientWrapper({ children }: { children: React.ReactNode })
103
103
 
104
104
  ## Test Budget
105
105
 
106
- The plan (`morph-plan`) gives each task a budget: **one test per postcondition, plus one
107
- regression test per bug fixed**. Going above it takes a one-line justification in the task. The
108
- happy path + one error path per component above is that budget applied to a component.
106
+ Tests are paid for by **risk, not by new code**. The plan (`morph-plan`) marks each scenario
107
+ with `prova:` and, for a test, a `risco:` (business rule, money, external contract, concurrency,
108
+ data integrity, regression). The budget is **one test per risky scenario, plus the regression test of
109
+ the bug being fixed**; going above it takes a one-line justification on the scenario. A scenario
110
+ without risk — CRUD, DTO, mapping, wiring, constants, layout — is `prova: leitura`: the Gate 3
111
+ verifier proves it by reading the code, and it gets no test. Rendering, layout and copy are
112
+ `leitura` or `e2e`, never a unit test per component.
109
113
 
110
114
  - **Table before copy.** Scenarios of the same behavior — edge cases included — are rows of one
111
115
  `it.each`/`test.each`, never N `it()` blocks that differ by one value.
@@ -116,8 +120,8 @@ happy path + one error path per component above is that budget applied to a comp
116
120
  - **Shared helpers by default.** Reuse the existing wrappers (`test/helpers.tsx`), MSW handlers and
117
121
  factories. Before writing a new one, look for it (`morph-spec graph explain <Name>`; fallback
118
122
  without a graph: `grep -rn`).
119
- - **Order is free, proof is not the implementer's:** a bug gets a regression test, and pure logic
120
- (formatters, reducers, schema) gets its tests, in whatever order the code comes out. The
123
+ - **Order is free, proof is not the implementer's:** a bug gets a regression test, and a risky
124
+ scenario (a formatter of money, a reducer with rules, a schema) gets its test, in whatever order the code comes out. The
121
125
  implementer does not run red→green cycles nor mutate production code to watch a test fail; it
122
126
  runs its own filter once before the report, and the Gate 3 mutation proof shows the test bites.
123
127
 
@@ -19,7 +19,7 @@ Stack: .NET VSA + Neon Serverless Postgres. For the end-to-end RAG pipeline (ing
19
19
  - ALWAYS call `UseVector()` on the `UseNpgsql(...)` options builder (EF Core); `NpgsqlDataSourceBuilder.UseVector()` is only for raw Npgsql
20
20
  - ALWAYS register the `vector` extension in `OnModelCreating` (`HasPostgresExtension("vector")`)
21
21
  - Map vector properties as `Pgvector.Vector` -- EF Core does not map `float[]` or `ReadOnlyMemory<float>` to the `vector` column
22
- - Query through the `ApplicationDbContext` in the slice handler with the `CosineDistance`/`L2Distance`/`MaxInnerProduct` LINQ translations -- no repository class, no raw SQL from C# without a `decisions.md` justification
22
+ - Query through the `ApplicationDbContext` in the slice handler with the `CosineDistance`/`L2Distance`/`MaxInnerProduct` LINQ translations -- no repository class, no raw SQL from C# without a `plan.md` (`## Decisões`) justification
23
23
  - IDs come from the app (`Guid.CreateVersion7()`), never from a `gen_random_uuid()` column default
24
24
 
25
25
  ## Enable pgvector
@@ -39,7 +39,7 @@ CREATE EXTENSION IF NOT EXISTS vector;
39
39
  <PackageReference Include="Pgvector.EntityFrameworkCore" Version="0.3.0" />
40
40
  ```
41
41
 
42
- `Pgvector.EntityFrameworkCore` 0.3.0 requires `Npgsql.EntityFrameworkCore.PostgreSQL` >= 9.0.1 and brings `Pgvector` (the `Vector` type and the raw-Npgsql plugin) transitively. The configuration below maps each column to the snake_case names of `backend/database/postgresql/database.md` (`embedding`, `created_at`) with `HasColumnName`; without it EF creates `"Embedding"`, `"CreatedAt"` and every SQL snippet below would have to quote PascalCase names. (A project that wants this for every entity can add `EFCore.NamingConventions` and `UseSnakeCaseNamingConvention()` once, instead of per column -- a project-wide decision for `decisions.md`.)
42
+ `Pgvector.EntityFrameworkCore` 0.3.0 requires `Npgsql.EntityFrameworkCore.PostgreSQL` >= 9.0.1 and brings `Pgvector` (the `Vector` type and the raw-Npgsql plugin) transitively. The configuration below maps each column to the snake_case names of `backend/database/postgresql/database.md` (`embedding`, `created_at`) with `HasColumnName`; without it EF creates `"Embedding"`, `"CreatedAt"` and every SQL snippet below would have to quote PascalCase names. (A project that wants this for every entity can add `EFCore.NamingConventions` and `UseSnakeCaseNamingConvention()` once, instead of per column -- a project-wide decision for `plan.md` (`## Decisões`).)
43
43
 
44
44
  ### Entity Definition
45
45
 
@@ -196,9 +196,9 @@ public sealed class SearchDocumentsHandler(
196
196
 
197
197
  Storing an embedding is an ordinary EF write in the handler that owns the document: `document.Embedding = new Vector(await embeddings.GenerateVectorAsync(text, cancellationToken: ct));` followed by `SaveChangesAsync`. No `UPDATE ... SET embedding` from C#.
198
198
 
199
- ### SQL Functions (only with a `decisions.md` justification)
199
+ ### SQL Functions (only with a `plan.md` (`## Decisões`) justification)
200
200
 
201
- When the query must live in the database (called from several apps, or a hybrid score the LINQ translation cannot express), a SQL function is acceptable -- record why in `decisions.md`, call it through EF Core with interpolated parameters, and keep the same distance rules:
201
+ When the query must live in the database (called from several apps, or a hybrid score the LINQ translation cannot express), a SQL function is acceptable -- record why in `plan.md` (`## Decisões`), call it through EF Core with interpolated parameters, and keep the same distance rules:
202
202
 
203
203
  ```sql
204
204
  CREATE OR REPLACE FUNCTION match_documents(
@@ -218,7 +218,7 @@ $$;
218
218
 
219
219
  ## Hybrid Search (Vector + Full-Text)
220
220
 
221
- Blends the vector score with `ts_rank`. Same rule as above: a SQL function, justified in `decisions.md`.
221
+ Blends the vector score with `ts_rank`. Same rule as above: a SQL function, justified in `plan.md` (`## Decisões`).
222
222
 
223
223
  ```sql
224
224
  CREATE OR REPLACE FUNCTION hybrid_search(
@@ -0,0 +1,92 @@
1
+ <templateSet group="MORPH Templates">
2
+ <template name="morph-pascal" value="{{pascalCase $FEATURE_NAME$}}" description="Convert to PascalCase (UserAuth)" toReformat="false" toShortenFQNames="true">
3
+ <variable name="FEATURE_NAME" expression="" defaultValue="&quot;FEATURE_NAME&quot;" alwaysStopAt="true" />
4
+ <context>
5
+ <option name="OTHER" value="true" />
6
+ </context>
7
+ </template>
8
+
9
+ <template name="morph-camel" value="{{camelCase $FEATURE_NAME$}}" description="Convert to camelCase (userAuth)" toReformat="false" toShortenFQNames="true">
10
+ <variable name="FEATURE_NAME" expression="" defaultValue="&quot;FEATURE_NAME&quot;" alwaysStopAt="true" />
11
+ <context>
12
+ <option name="OTHER" value="true" />
13
+ </context>
14
+ </template>
15
+
16
+ <template name="morph-snake" value="{{snakeCase $FEATURE_NAME$}}" description="Convert to snake_case (user_auth)" toReformat="false" toShortenFQNames="true">
17
+ <variable name="FEATURE_NAME" expression="" defaultValue="&quot;FEATURE_NAME&quot;" alwaysStopAt="true" />
18
+ <context>
19
+ <option name="OTHER" value="true" />
20
+ </context>
21
+ </template>
22
+
23
+ <template name="morph-title" value="{{titleCase $FEATURE_NAME$}}" description="Convert to Title Case (User Auth)" toReformat="false" toShortenFQNames="true">
24
+ <variable name="FEATURE_NAME" expression="" defaultValue="&quot;FEATURE_NAME&quot;" alwaysStopAt="true" />
25
+ <context>
26
+ <option name="OTHER" value="true" />
27
+ </context>
28
+ </template>
29
+
30
+ <template name="morph-kebab" value="{{kebabCase $FEATURE_NAME$}}" description="Convert to kebab-case (user-auth)" toReformat="false" toShortenFQNames="true">
31
+ <variable name="FEATURE_NAME" expression="" defaultValue="&quot;FEATURE_NAME&quot;" alwaysStopAt="true" />
32
+ <context>
33
+ <option name="OTHER" value="true" />
34
+ </context>
35
+ </template>
36
+
37
+ <template name="morph-if" value="{{#if $CONDITION$}}&#10; $END$&#10;{{/if}}" description="Handlebars if block" toReformat="false" toShortenFQNames="true">
38
+ <variable name="CONDITION" expression="" defaultValue="&quot;condition&quot;" alwaysStopAt="true" />
39
+ <context>
40
+ <option name="OTHER" value="true" />
41
+ </context>
42
+ </template>
43
+
44
+ <template name="morph-if-else" value="{{#if $CONDITION$}}&#10; $TRUE_BRANCH$&#10;{{else}}&#10; $FALSE_BRANCH$&#10;{{/if}}" description="Handlebars if-else block" toReformat="false" toShortenFQNames="true">
45
+ <variable name="CONDITION" expression="" defaultValue="&quot;condition&quot;" alwaysStopAt="true" />
46
+ <variable name="TRUE_BRANCH" expression="" defaultValue="&quot;&quot;" alwaysStopAt="true" />
47
+ <variable name="FALSE_BRANCH" expression="" defaultValue="&quot;&quot;" alwaysStopAt="true" />
48
+ <context>
49
+ <option name="OTHER" value="true" />
50
+ </context>
51
+ </template>
52
+
53
+ <template name="morph-each" value="{{#each $ITEMS$}}&#10; {{this.$PROPERTY$}}&#10;{{/each}}" description="Handlebars each loop" toReformat="false" toShortenFQNames="true">
54
+ <variable name="ITEMS" expression="" defaultValue="&quot;items&quot;" alwaysStopAt="true" />
55
+ <variable name="PROPERTY" expression="" defaultValue="&quot;name&quot;" alwaysStopAt="true" />
56
+ <context>
57
+ <option name="OTHER" value="true" />
58
+ </context>
59
+ </template>
60
+
61
+ <template name="morph-service" value="namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;&#10;&#10;public class {{pascalCase FEATURE_NAME}}Service(&#10; I{{pascalCase FEATURE_NAME}}Repository repository,&#10; ILogger&lt;{{pascalCase FEATURE_NAME}}Service&gt; logger) : I{{pascalCase FEATURE_NAME}}Service&#10;{&#10; public async Task&lt;{{pascalCase FEATURE_NAME}}Dto?&gt; GetByIdAsync(int id, CancellationToken cancellationToken = default)&#10; {&#10; $END$&#10; }&#10;}" description="MORPH .NET Service class template" toReformat="false" toShortenFQNames="true">
62
+ <context>
63
+ <option name="C_SHARP" value="true" />
64
+ </context>
65
+ </template>
66
+
67
+ <template name="morph-repository" value="public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository&#10;{&#10; private readonly DbSet&lt;{{pascalCase FEATURE_NAME}}&gt; _dbSet = context.Set&lt;{{pascalCase FEATURE_NAME}}&gt;();&#10;&#10; public async Task&lt;{{pascalCase FEATURE_NAME}}?&gt; GetByIdAsync(int id, CancellationToken cancellationToken = default)&#10; {&#10; $END$&#10; }&#10;}" description="MORPH .NET Repository class template" toReformat="false" toShortenFQNames="true">
68
+ <context>
69
+ <option name="C_SHARP" value="true" />
70
+ </context>
71
+ </template>
72
+
73
+ <template name="morph-component" value="@page &quot;/{{kebabCase FEATURE_NAME}}&quot;&#10;@inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service&#10;@inject ILogger&lt;{{pascalCase FEATURE_NAME}}Page&gt; Logger&#10;&#10;&lt;PageTitle&gt;{{pascalCase FEATURE_NAME}}&lt;/PageTitle&gt;&#10;&#10;&lt;div class=&quot;container-fluid&quot;&gt;&#10; $END$&#10;&lt;/div&gt;&#10;&#10;@code {&#10;&#10;}" description="MORPH Blazor component template" toReformat="false" toShortenFQNames="true">
74
+ <context>
75
+ <option name="HTML" value="true" />
76
+ </context>
77
+ </template>
78
+
79
+ <template name="morph-migration" value="public partial class Add{{pascalCase FEATURE_NAME}} : Migration&#10;{&#10; protected override void Up(MigrationBuilder migrationBuilder)&#10; {&#10; migrationBuilder.CreateTable(&#10; name: &quot;{{pascalCase FEATURE_NAME}}s&quot;,&#10; columns: table =&gt; new&#10; {&#10; Id = table.Column&lt;int&gt;(type: &quot;int&quot;, nullable: false)&#10; .Annotation(&quot;SqlServer:Identity&quot;, &quot;1, 1&quot;),&#10; $END$&#10; },&#10; constraints: table =&gt;&#10; {&#10; table.PrimaryKey(&quot;PK_{{pascalCase FEATURE_NAME}}s&quot;, x =&gt; x.Id);&#10; });&#10; }&#10;&#10; protected override void Down(MigrationBuilder migrationBuilder)&#10; {&#10; migrationBuilder.DropTable(name: &quot;{{pascalCase FEATURE_NAME}}s&quot;);&#10; }&#10;}" description="MORPH EF Core migration template" toReformat="false" toShortenFQNames="true">
80
+ <context>
81
+ <option name="C_SHARP" value="true" />
82
+ </context>
83
+ </template>
84
+
85
+ <template name="morph-ts-contract" value="export interface {{pascalCase FEATURE_NAME}} {&#10; id: number;&#10; $FIELD1$&#10;}&#10;&#10;export interface Create{{pascalCase FEATURE_NAME}}Request {&#10; $FIELD2$&#10;}&#10;&#10;export interface Update{{pascalCase FEATURE_NAME}}Request {&#10; $END$&#10;}" description="MORPH TypeScript contract template" toReformat="false" toShortenFQNames="true">
86
+ <variable name="FIELD1" expression="" defaultValue="&quot;&quot;" alwaysStopAt="true" />
87
+ <variable name="FIELD2" expression="" defaultValue="&quot;&quot;" alwaysStopAt="true" />
88
+ <context>
89
+ <option name="TypeScript" value="true" />
90
+ </context>
91
+ </template>
92
+ </templateSet>