@polymorphism-tech/morph-spec 4.12.0 → 4.14.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 (316) hide show
  1. package/CLAUDE.md +7 -7
  2. package/README.md +28 -34
  3. package/bin/morph-spec.js +41 -24
  4. package/bin/task-manager.js +962 -1341
  5. package/bin/validate.js +1 -1
  6. package/claude-plugin.json +1 -1
  7. package/docs/CHEATSHEET.md +14 -16
  8. package/docs/QUICKSTART.md +15 -15
  9. package/framework/CLAUDE.md +5 -2
  10. package/framework/agents/README.md +3 -4
  11. package/framework/agents/ai-agents/ai-system-architect.md +6 -10
  12. package/framework/agents/architecture/seo-growth-hacker.md +166 -326
  13. package/framework/agents/architecture/standards-architect.md +21 -31
  14. package/framework/agents/backend/dotnet-senior.md +160 -86
  15. package/framework/agents/backend/ef-modeler.md +2 -6
  16. package/framework/agents/backend/hangfire-orchestrator.md +8 -9
  17. package/framework/agents/backend/ms-agent-expert.md +1 -1
  18. package/framework/agents/frontend/nextjs-expert.md +198 -133
  19. package/framework/agents/frontend/ui-ux-designer.md +226 -267
  20. package/framework/agents/infrastructure/infra-architect.md +191 -27
  21. package/framework/agents/integrations/clerk-auth.md +19 -26
  22. package/framework/agents/integrations/hangfire-integration.md +1 -4
  23. package/framework/agents.json +1084 -2278
  24. package/framework/commands/morph-apply.md +4 -6
  25. package/framework/commands/morph-preflight.md +192 -269
  26. package/framework/commands/morph-proposal.md +210 -238
  27. package/framework/commands/morph-status.md +72 -92
  28. package/framework/commands/morph-troubleshoot.md +6 -13
  29. package/framework/hooks/README.md +1 -6
  30. package/framework/hooks/claude-code/post-tool-use/validator-feedback.js +4 -6
  31. package/framework/hooks/claude-code/pre-compact/save-morph-context.js +12 -7
  32. package/framework/hooks/claude-code/pre-tool-use/enforce-phase-writes.js +2 -2
  33. package/framework/hooks/claude-code/pre-tool-use/protect-spec-files.js +2 -2
  34. package/framework/hooks/claude-code/pre-tool-use/task-tracking-guard.js +5 -10
  35. package/framework/hooks/claude-code/session-start/inject-morph-context.js +63 -30
  36. package/framework/hooks/claude-code/session-start/resume-context.js +77 -0
  37. package/framework/hooks/claude-code/statusline.py +4 -5
  38. package/framework/hooks/claude-code/stop/validate-completion.js +4 -4
  39. package/framework/hooks/claude-code/user-prompt/enrich-prompt.js +8 -8
  40. package/framework/hooks/shared/compact-restore.js +28 -5
  41. package/framework/hooks/shared/phase-utils.js +4 -8
  42. package/framework/hooks/shared/state-reader.js +34 -3
  43. package/framework/phases.json +162 -200
  44. package/framework/rules/ai-agents-standards.md +15 -0
  45. package/framework/rules/asaas-standards.md +13 -0
  46. package/framework/rules/clerk-standards.md +13 -0
  47. package/framework/rules/csharp-standards.md +8 -4
  48. package/framework/rules/data-standards.md +18 -0
  49. package/framework/rules/dotnet-templates.md +87 -0
  50. package/framework/rules/frontend-standards.md +13 -13
  51. package/framework/rules/hangfire-standards.md +13 -0
  52. package/framework/rules/infrastructure-standards.md +27 -16
  53. package/framework/rules/morph-workflow.md +73 -13
  54. package/framework/rules/nextjs-standards.md +1 -0
  55. package/framework/rules/nextjs-templates.md +40 -0
  56. package/framework/rules/resend-standards.md +12 -0
  57. package/framework/rules/supabase-standards.md +29 -0
  58. package/framework/skills/README.md +6 -8
  59. package/framework/skills/level-0-meta/mcp-registry.json +21 -99
  60. package/framework/{standards/ai-agents/team-orchestration.md → skills/level-0-meta/morph-agent-teams/SKILL.md} +472 -479
  61. package/framework/skills/level-0-meta/morph-brainstorming/SKILL.md +28 -1
  62. package/framework/skills/level-0-meta/morph-checklist/SKILL.md +2 -2
  63. package/framework/skills/level-0-meta/morph-code-review/SKILL.md +1 -5
  64. package/framework/skills/level-0-meta/morph-code-review/scripts/scan-csharp.mjs +1 -1
  65. package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +23 -41
  66. package/framework/skills/level-0-meta/morph-init/SKILL.md +18 -14
  67. package/framework/skills/level-0-meta/morph-parallel-dispatch/SKILL.md +225 -0
  68. package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +20 -0
  69. package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +4 -8
  70. package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +15 -59
  71. package/framework/skills/level-0-meta/morph-verification-before-completion/SKILL.md +7 -19
  72. package/framework/skills/level-0-meta/morph-verification-before-completion/scripts/check-phase-outputs.mjs +8 -8
  73. package/framework/skills/level-1-workflows/README.md +1 -1
  74. package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +49 -50
  75. package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +57 -123
  76. package/framework/skills/level-1-workflows/morph-phase-design/references/architecture-analysis-guide.md +5 -50
  77. package/framework/skills/level-1-workflows/morph-phase-design/references/spec-example.md +1 -1
  78. package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +127 -159
  79. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/code-quality-reviewer-prompt.md +1 -1
  80. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/implementer-prompt.md +4 -3
  81. package/framework/skills/level-1-workflows/morph-phase-implement/prompts/spec-reviewer-prompt.md +2 -2
  82. package/framework/skills/level-1-workflows/morph-phase-implement/references/recap-example.md +1 -1
  83. package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +17 -13
  84. package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +22 -40
  85. package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -172
  86. package/framework/skills/level-1-workflows/morph-phase-tasks/references/tasks-example.md +5 -5
  87. package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +57 -77
  88. package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
  89. package/framework/squad-templates/backend-only.json +6 -6
  90. package/framework/squad-templates/frontend-only.json +17 -8
  91. package/framework/squad-templates/full-stack.json +29 -24
  92. package/framework/standards/STANDARDS.json +1021 -2369
  93. package/framework/standards/backend/dotnet/fluent-validation-vsa.md +200 -0
  94. package/framework/standards/backend/dotnet/result-pattern.md +213 -0
  95. package/framework/standards/backend/dotnet/scalar-ui.md +172 -0
  96. package/framework/standards/backend/dotnet/scrutor.md +158 -0
  97. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -0
  98. package/framework/standards/frontend/nextjs/motion-patterns.md +1 -1
  99. package/framework/standards/infrastructure/supabase/mcp-setup.md +83 -193
  100. package/framework/standards/integration/mcp/mcp-tools.md +91 -77
  101. package/framework/templates/IDE-SNIPPETS.md +2 -6
  102. package/framework/templates/README.md +709 -814
  103. package/framework/templates/REGISTRY.json +223 -762
  104. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -0
  105. package/framework/templates/code/dotnet/abstractions/error.cs +60 -0
  106. package/framework/templates/code/dotnet/abstractions/handler.cs +12 -0
  107. package/framework/templates/code/dotnet/abstractions/result.cs +74 -0
  108. package/framework/templates/code/dotnet/backend/repository.cs +148 -141
  109. package/framework/templates/code/dotnet/constants/api-tags.cs +6 -0
  110. package/framework/templates/code/dotnet/exceptions/exception-handler.cs +32 -0
  111. package/framework/templates/code/dotnet/extensions/database-extensions.cs +20 -0
  112. package/framework/templates/code/dotnet/extensions/handler-registration.cs +38 -0
  113. package/framework/templates/code/dotnet/extensions/health-checks.cs +24 -0
  114. package/framework/templates/code/dotnet/extensions/map-endpoints.cs +35 -0
  115. package/framework/templates/code/dotnet/extensions/result-extensions.cs +29 -0
  116. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -0
  117. package/framework/templates/code/dotnet/feature/entity.cs.hbs +8 -0
  118. package/framework/templates/code/dotnet/feature/errors.cs.hbs +8 -0
  119. package/framework/templates/code/dotnet/feature/handler.cs.hbs +24 -0
  120. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -0
  121. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -0
  122. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -0
  123. package/framework/templates/code/dotnet/jobs/job.cs +21 -17
  124. package/framework/templates/code/dotnet/pipelines/logging-decorator.cs +31 -0
  125. package/framework/templates/code/dotnet/pipelines/validation-decorator.cs +97 -0
  126. package/framework/templates/code/dotnet/test.cs +192 -106
  127. package/framework/templates/context/ARCHITECTURE.md +89 -0
  128. package/framework/templates/context/CONTEXT-FEATURE.md +2 -2
  129. package/framework/templates/context/CONTEXT.md +180 -181
  130. package/framework/templates/context/design-system.md +1 -5
  131. package/framework/templates/docs/onboarding.md +2 -3
  132. package/framework/templates/docs/proposal.md +171 -182
  133. package/framework/templates/docs/spec.md +1 -3
  134. package/framework/templates/docs/ui-components.md +0 -72
  135. package/framework/templates/examples/design-system-examples.md +4 -4
  136. package/framework/templates/feature/decisions.md +0 -66
  137. package/framework/templates/feature/recap.md +1 -2
  138. package/framework/templates/infrastructure/github/README.md +56 -593
  139. package/framework/templates/integrations/clerk-config.cs +0 -41
  140. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +2 -2
  141. package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -2
  142. package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +83 -126
  143. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +2 -2
  144. package/framework/templates/project/validate.js +2 -4
  145. package/framework/templates/ui/design-system.css +1 -1
  146. package/framework/workflows/README.md +8 -8
  147. package/framework/workflows/configs/fast-track.json +51 -0
  148. package/framework/workflows/configs/full.json +68 -0
  149. package/framework/workflows/configs/standard.json +7 -3
  150. package/framework/workflows/docs/STORY-DRIVEN-DEVELOPMENT.md +1 -1
  151. package/framework/workflows/docs/enforcement-pipeline.md +18 -19
  152. package/framework/workflows/docs/full-morph.md +69 -76
  153. package/framework/workflows/docs/standard.md +49 -44
  154. package/framework/workflows/docs/ui-refresh.md +1 -1
  155. package/package.json +3 -3
  156. package/src/commands/agents/dispatch-agents.js +55 -116
  157. package/src/commands/context/context-refresh.js +79 -0
  158. package/src/commands/phase/phase-reset.js +1 -1
  159. package/src/commands/project/doctor.js +10 -32
  160. package/src/commands/project/init.js +12 -0
  161. package/src/commands/project/status.js +6 -12
  162. package/src/commands/scaffold/scaffold.js +131 -0
  163. package/src/commands/scope/escalate.js +1 -1
  164. package/src/commands/state/advance-phase.js +15 -29
  165. package/src/commands/state/state.js +20 -5
  166. package/src/commands/tasks/task.js +0 -30
  167. package/src/commands/validation/validate-feature.js +3 -10
  168. package/src/core/paths/output-schema.js +12 -13
  169. package/src/core/state/phase-state-machine.js +211 -217
  170. package/src/core/state/state-manager.js +9 -9
  171. package/src/lib/agents/agents-manager.js +2 -2
  172. package/src/lib/agents/output-collector.js +1 -2
  173. package/src/lib/detectors/design-system-detector.js +4 -4
  174. package/src/lib/detectors/index.js +1 -11
  175. package/src/lib/generators/architecture-scanner.js +640 -0
  176. package/src/lib/generators/context-generator.js +3 -3
  177. package/src/lib/generators/design-system-generator.js +508 -587
  178. package/src/lib/generators/index.js +12 -10
  179. package/src/lib/generators/metadata-extractor.js +386 -387
  180. package/src/lib/generators/recap-generator.js +4 -4
  181. package/src/lib/generators/scaffold-generator.js +317 -0
  182. package/src/lib/generators/settings-generator.js +0 -1
  183. package/src/lib/phase-chain/phase-validator.js +64 -42
  184. package/src/lib/stack/stack-profile.js +2 -2
  185. package/src/lib/standards/digest-builder.js +72 -285
  186. package/src/lib/tasks/task-parser.js +11 -5
  187. package/src/lib/validators/architecture/architecture-validator.js +259 -387
  188. package/src/lib/validators/content/content-validator.js +287 -287
  189. package/src/lib/validators/css/css-validator.js +340 -352
  190. package/src/lib/validators/packages/package-validator.js +347 -360
  191. package/src/lib/validators/shared/issue-counter.js +14 -18
  192. package/src/lib/validators/validation-runner.js +3 -19
  193. package/src/scripts/setup-infra.js +1 -1
  194. package/src/utils/agents-installer.js +61 -10
  195. package/src/utils/file-copier.js +1 -0
  196. package/src/utils/hooks-installer.js +11 -19
  197. package/src/utils/skills-installer.js +178 -178
  198. package/framework/agents/architecture/prompt-engineer.md +0 -195
  199. package/framework/agents/backend/api-designer.md +0 -106
  200. package/framework/agents/frontend/blazor-builder.md +0 -229
  201. package/framework/agents/frontend/nextjs-ui-specialist.md +0 -218
  202. package/framework/agents/infrastructure/azure-architect.md +0 -150
  203. package/framework/agents/infrastructure/azure-deploy-specialist.md +0 -707
  204. package/framework/agents/infrastructure/bicep-architect.md +0 -132
  205. package/framework/agents/infrastructure/container-specialist.md +0 -137
  206. package/framework/agents/infrastructure/devops-engineer.md +0 -126
  207. package/framework/agents/integrations/azure-identity.md +0 -148
  208. package/framework/agents/quality/code-analyzer.md +0 -239
  209. package/framework/agents/quality/testing-specialist.md +0 -130
  210. package/framework/hooks/claude-code/notification/approval-reminder.js +0 -57
  211. package/framework/hooks/claude-code/post-tool-use/context-refresh.js +0 -109
  212. package/framework/hooks/claude-code/post-tool-use/skill-reminder.js +0 -78
  213. package/framework/index/troubleshooting-index.json +0 -184
  214. package/framework/rules/testing-standards.md +0 -14
  215. package/framework/skills/level-0-meta/morph-replicate/SKILL.md +0 -230
  216. package/framework/skills/level-0-meta/morph-replicate/references/blazor-html-mapping.md +0 -70
  217. package/framework/skills/level-1-workflows/morph-phase-clarify/SKILL.md +0 -256
  218. package/framework/skills/level-1-workflows/morph-phase-clarify/references/clarifications-example.md +0 -117
  219. package/framework/skills/level-1-workflows/morph-phase-setup/SKILL.md +0 -269
  220. package/framework/standards/ai-agents/blazor-ui.md +0 -364
  221. package/framework/standards/architecture/ddd/aggregates.md +0 -120
  222. package/framework/standards/architecture/ddd/bounded-contexts.md +0 -105
  223. package/framework/standards/architecture/ddd/complexity-levels.md +0 -108
  224. package/framework/standards/architecture/ddd/entities.md +0 -99
  225. package/framework/standards/architecture/ddd/ubiquitous-language.md +0 -58
  226. package/framework/standards/architecture/ddd/value-objects.md +0 -124
  227. package/framework/standards/backend/api/minimal-api.md +0 -494
  228. package/framework/standards/backend/api/rest.md +0 -492
  229. package/framework/standards/backend/authentication/passkeys.md +0 -428
  230. package/framework/standards/backend/database/ef-core.md +0 -199
  231. package/framework/standards/backend/database/migrations.md +0 -393
  232. package/framework/standards/backend/database/repository-patterns.md +0 -528
  233. package/framework/standards/backend/dotnet/async.md +0 -366
  234. package/framework/standards/backend/dotnet/core.md +0 -117
  235. package/framework/standards/backend/dotnet/di.md +0 -439
  236. package/framework/standards/backend/dotnet/program-cs-checklist.md +0 -92
  237. package/framework/standards/context/analytics.md +0 -96
  238. package/framework/standards/context/bundles.md +0 -110
  239. package/framework/standards/context/priming.md +0 -78
  240. package/framework/standards/core/architecture.md +0 -185
  241. package/framework/standards/core/coding.md +0 -214
  242. package/framework/standards/core/git-branching-strategy.md +0 -403
  243. package/framework/standards/core/git.md +0 -185
  244. package/framework/standards/core/testing.md +0 -295
  245. package/framework/standards/data/nosql/blob-storage.md +0 -102
  246. package/framework/standards/data/vector-search/azure-ai-search.md +0 -121
  247. package/framework/standards/frontend/blazor/design-checklist.md +0 -222
  248. package/framework/standards/frontend/blazor/fluent-ui-setup.md +0 -595
  249. package/framework/standards/frontend/blazor/fluent-ui.md +0 -137
  250. package/framework/standards/frontend/blazor/html-conversion.md +0 -184
  251. package/framework/standards/frontend/blazor/lifecycle.md +0 -195
  252. package/framework/standards/frontend/blazor/pitfalls.md +0 -198
  253. package/framework/standards/frontend/blazor/state.md +0 -191
  254. package/framework/standards/frontend/design-system/aesthetic-direction.md +0 -266
  255. package/framework/standards/frontend/design-system/design-trends.md +0 -171
  256. package/framework/standards/infrastructure/azure/azure.md +0 -624
  257. package/framework/standards/infrastructure/azure/bicep/bicep-patterns.md +0 -422
  258. package/framework/standards/infrastructure/azure/devops/azure-devops-setup.md +0 -516
  259. package/framework/standards/infrastructure/azure/devops/local-development.md +0 -520
  260. package/framework/standards/infrastructure/azure/services/functions.md +0 -486
  261. package/framework/standards/infrastructure/azure/services/service-bus.md +0 -459
  262. package/framework/standards/infrastructure/azure/services/storage.md +0 -407
  263. package/framework/standards/infrastructure/vercel/vercel-database.md +0 -106
  264. package/framework/standards/integration/api/graphql.md +0 -91
  265. package/framework/standards/integration/api/grpc.md +0 -114
  266. package/framework/standards/integration/api/rest-design.md +0 -95
  267. package/framework/standards/integration/event-driven/cqrs.md +0 -101
  268. package/framework/standards/integration/event-driven/event-sourcing.md +0 -124
  269. package/framework/standards/integration/event-driven/service-bus.md +0 -95
  270. package/framework/standards/observability/logging.md +0 -131
  271. package/framework/standards/observability/metrics.md +0 -121
  272. package/framework/standards/observability/monitoring.md +0 -114
  273. package/framework/standards/observability/tracing.md +0 -132
  274. package/framework/standards/workflows/parallel-execution.md +0 -112
  275. package/framework/standards/workflows/thread-management.md +0 -113
  276. package/framework/templates/code/dotnet/backend/service.cs +0 -139
  277. package/framework/templates/code/dotnet/contracts/Commands.cs +0 -74
  278. package/framework/templates/code/dotnet/contracts/Entities.cs +0 -25
  279. package/framework/templates/code/dotnet/contracts/Queries.cs +0 -74
  280. package/framework/templates/code/dotnet/contracts/api-contracts.cs +0 -173
  281. package/framework/templates/code/dotnet/contracts/contracts-level1.cs +0 -69
  282. package/framework/templates/code/dotnet/contracts/contracts-level2.cs +0 -86
  283. package/framework/templates/code/dotnet/contracts/contracts-level3.cs +0 -41
  284. package/framework/templates/code/dotnet/frontend/component.razor +0 -239
  285. package/framework/templates/infrastructure/azure/Dockerfile.example +0 -82
  286. package/framework/templates/infrastructure/azure/README.md +0 -286
  287. package/framework/templates/infrastructure/azure/app-insights.bicep +0 -63
  288. package/framework/templates/infrastructure/azure/app-service.bicep +0 -164
  289. package/framework/templates/infrastructure/azure/container-app-env.bicep +0 -49
  290. package/framework/templates/infrastructure/azure/container-app.bicep +0 -156
  291. package/framework/templates/infrastructure/azure/deploy-checklist.md +0 -426
  292. package/framework/templates/infrastructure/azure/deploy.ps1 +0 -229
  293. package/framework/templates/infrastructure/azure/deploy.sh +0 -208
  294. package/framework/templates/infrastructure/azure/key-vault.bicep +0 -91
  295. package/framework/templates/infrastructure/azure/main.bicep +0 -189
  296. package/framework/templates/infrastructure/azure/parameters.dev.json +0 -29
  297. package/framework/templates/infrastructure/azure/parameters.prod.json +0 -29
  298. package/framework/templates/infrastructure/azure/parameters.staging.json +0 -29
  299. package/framework/templates/infrastructure/azure/sql-database.bicep +0 -103
  300. package/framework/templates/infrastructure/azure/storage.bicep +0 -106
  301. package/framework/templates/infrastructure/github/actions/azure-auth/action.yml.hbs +0 -22
  302. package/framework/templates/infrastructure/github/workflows/deploy-azure-app-service.yml.hbs +0 -61
  303. package/framework/templates/integrations/azure-identity-config.cs +0 -288
  304. package/framework/templates/project-structure/dotnet-ddd.md +0 -70
  305. package/framework/templates/saas/subscription.cs +0 -347
  306. package/framework/templates/saas/tenant.cs +0 -338
  307. package/framework/templates/ui/FluentDesignTheme.cs +0 -149
  308. package/framework/templates/ui/MudTheme.cs +0 -281
  309. package/framework/workflows/configs/nodejs-cli.json +0 -40
  310. package/src/commands/standards/context-budget.js +0 -89
  311. package/src/lib/standards/index.js +0 -7
  312. package/src/lib/standards/standards-context-injector.js +0 -302
  313. package/src/lib/validators/blazor/blazor-concurrency-analyzer.js +0 -316
  314. package/src/lib/validators/blazor/blazor-state-validator.js +0 -279
  315. package/src/lib/validators/blazor/blazor-validator.js +0 -369
  316. package/src/lib/validators/blazor/index.js +0 -9
@@ -0,0 +1,200 @@
1
+ # FluentValidation in VSA Pipelines
2
+
3
+ > **Scope:** dotnet-vsa
4
+ > **Layer:** 0 (always load)
5
+ > **Keywords:** fluent-validation, validation, decorator, pipeline, vsa
6
+ > **Load When:** dotnet VSA projects
7
+
8
+ ## Overview
9
+
10
+ FluentValidation integrates into VSA via the decorator pattern. Validators are auto-discovered per assembly, and the `ValidationDecorator<TRequest, TResponse>` runs all matching validators before the handler executes. Failed validation short-circuits to `Result.Failure` without touching the handler.
11
+
12
+ ---
13
+
14
+ ## Validator Structure
15
+
16
+ One validator per request DTO. Lives in the same feature folder as the handler:
17
+
18
+ ```csharp
19
+ // Features/BookFeature/CreateBook/CreateBookValidator.cs
20
+ public class CreateBookValidator : AbstractValidator<CreateBookRequest>
21
+ {
22
+ public CreateBookValidator()
23
+ {
24
+ RuleFor(c => c.Title)
25
+ .NotEmpty().WithMessage("Title is required")
26
+ .MaximumLength(200).WithMessage("Title must not exceed 200 characters");
27
+
28
+ RuleFor(c => c.Author)
29
+ .NotEmpty().WithMessage("Author is required")
30
+ .MaximumLength(100).WithMessage("Author must not exceed 100 characters");
31
+
32
+ RuleFor(c => c.ISBN)
33
+ .NotEmpty().WithMessage("ISBN is required");
34
+
35
+ RuleFor(c => c.Price)
36
+ .GreaterThan(0).WithMessage("Price must be greater than 0");
37
+
38
+ RuleFor(c => c.PublishedYear)
39
+ .GreaterThan(1000).WithMessage("Published year must be a valid year")
40
+ .LessThanOrEqualTo(DateTime.UtcNow.Year)
41
+ .WithMessage("Published year cannot be in the future");
42
+ }
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Auto-Discovery
49
+
50
+ Register all validators from the assembly in `Program.cs`. This must happen **before** handler registration so the `ValidationDecorator` can resolve them:
51
+
52
+ ```csharp
53
+ // Program.cs
54
+ builder.Services.AddValidatorsFromAssembly(typeof(CreateBookValidator).Assembly);
55
+ builder.Services.AddHandlersFromAssembly(typeof(Program).Assembly);
56
+ ```
57
+
58
+ ---
59
+
60
+ ## ValidationDecorator Pattern
61
+
62
+ The decorator wraps every `IHandler<TRequest, TResponse>` via Scrutor. It collects all `IValidator<TRequest>` from DI, runs them in parallel, and short-circuits on failure:
63
+
64
+ ```csharp
65
+ public sealed class ValidationDecorator<TRequest, TResponse>(
66
+ IEnumerable<IValidator<TRequest>> validators,
67
+ IHandler<TRequest, TResponse> innerHandler) : IHandler<TRequest, TResponse>
68
+ {
69
+ public async Task<TResponse> HandleAsync(
70
+ TRequest command, CancellationToken cancellationToken)
71
+ {
72
+ // No validators registered for this request — pass through
73
+ if (!validators.Any())
74
+ {
75
+ return await innerHandler.HandleAsync(command, cancellationToken);
76
+ }
77
+
78
+ var context = new ValidationContext<TRequest>(command);
79
+
80
+ // Run all validators in parallel
81
+ ValidationFailure[] failures = (await Task.WhenAll(
82
+ validators.Select(v => v.ValidateAsync(context, cancellationToken))))
83
+ .SelectMany(r => r.Errors)
84
+ .Where(f => f is not null)
85
+ .ToArray();
86
+
87
+ if (failures.Length == 0)
88
+ {
89
+ return await innerHandler.HandleAsync(command, cancellationToken);
90
+ }
91
+
92
+ // Short-circuit: convert to Result failure
93
+ return CreateFailureResponse(failures);
94
+ }
95
+ }
96
+ ```
97
+
98
+ ---
99
+
100
+ ## FluentValidation to ValidationError Conversion
101
+
102
+ `ValidationFailure[]` from FluentValidation is converted to the Result pattern's `ValidationError`:
103
+
104
+ ```csharp
105
+ private static ValidationError CreateValidationError(ValidationFailure[] validationFailures) =>
106
+ new(validationFailures
107
+ .Select(f => Error.Validation(f.ErrorCode, f.ErrorMessage))
108
+ .ToArray());
109
+ ```
110
+
111
+ This produces a `ValidationError` containing an array of typed `Error` records, each with `ErrorType.Validation`.
112
+
113
+ ---
114
+
115
+ ## Reflection for Typed Failure Responses
116
+
117
+ The decorator supports both `Result` and `Result<T>` via reflection. This is necessary because the generic `TResponse` type is not known at compile time:
118
+
119
+ ```csharp
120
+ private static TResponse CreateFailureResponse(ValidationFailure[] failures)
121
+ {
122
+ // Non-generic Result
123
+ if (typeof(TResponse) == typeof(Result))
124
+ {
125
+ return (TResponse)(object)Result.Failure(CreateValidationError(failures));
126
+ }
127
+
128
+ // Generic Result<T> — use reflection to call Result.Failure<T>()
129
+ if (typeof(TResponse).IsGenericType &&
130
+ typeof(TResponse).GetGenericTypeDefinition() == typeof(Result<>))
131
+ {
132
+ var valueType = typeof(TResponse).GetGenericArguments()[0];
133
+ var failureMethod = typeof(Result)
134
+ .GetMethods()
135
+ .First(m =>
136
+ m.Name == nameof(Result.Failure) &&
137
+ m.IsGenericMethodDefinition &&
138
+ m.GetParameters().Length == 1);
139
+
140
+ var typedFailure = failureMethod
141
+ .MakeGenericMethod(valueType)
142
+ .Invoke(null, [CreateValidationError(failures)]);
143
+
144
+ return (TResponse)typedFailure!;
145
+ }
146
+
147
+ throw new InvalidOperationException(
148
+ $"ValidationDecorator supports only Result and Result<T> responses.");
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ## RuleFor Chain Reference
155
+
156
+ Common FluentValidation rules used in VSA:
157
+
158
+ | Rule | Purpose |
159
+ |------|---------|
160
+ | `.NotEmpty()` | Non-null and non-whitespace for strings |
161
+ | `.MaximumLength(n)` | String max length |
162
+ | `.GreaterThan(n)` | Numeric minimum (exclusive) |
163
+ | `.LessThanOrEqualTo(n)` | Numeric maximum (inclusive) |
164
+ | `.Must(predicate)` | Custom inline validation |
165
+ | `.WithMessage("...")` | Human-readable error message |
166
+ | `.WithErrorCode("...")` | Machine-readable error code |
167
+ | `.When(condition)` | Conditional rule application |
168
+
169
+ ---
170
+
171
+ ## File Placement
172
+
173
+ ```
174
+ Features/
175
+ BookFeature/
176
+ CreateBook/
177
+ CreateBookRequest.cs (or inline in handler)
178
+ CreateBookResponse.cs (or inline in handler)
179
+ CreateBookHandler.cs
180
+ CreateBookValidator.cs <-- one validator per request
181
+ CreateBookEndpoint.cs
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Anti-Patterns
187
+
188
+ | Do NOT | Do Instead |
189
+ |--------|------------|
190
+ | Validate inside the handler | Use `AbstractValidator<T>` + decorator |
191
+ | Throw `ValidationException` | Return `Result.Failure(ValidationError)` |
192
+ | Register validators after handlers | Register validators first: `AddValidatorsFromAssembly()` before `AddHandlersFromAssembly()` |
193
+ | Put validation logic in endpoints | Endpoints only do `Match()` — validation is automatic |
194
+ | Use `IValidateOptions<T>` | Use `AbstractValidator<T>` (FluentValidation, not Microsoft) |
195
+ | Skip `.WithMessage()` | Always provide user-facing messages |
196
+ | Use `RuleSet` grouping | One validator per request DTO — no rulesets needed in VSA |
197
+
198
+ ---
199
+
200
+ *MORPH-SPEC by Polymorphism Tech*
@@ -0,0 +1,213 @@
1
+ # Result Pattern — Railway-Oriented Programming
2
+
3
+ > **Scope:** dotnet-vsa
4
+ > **Layer:** 0 (always load)
5
+ > **Keywords:** result, error-handling, railway, match, monadic
6
+ > **Load When:** dotnet VSA projects
7
+
8
+ ## Overview
9
+
10
+ The Result pattern replaces exceptions for expected failures. Every handler returns `Result` or `Result<T>`, and endpoints use `Match()` to map success/failure to HTTP responses. No try-catch in business logic.
11
+
12
+ ---
13
+
14
+ ## Result Anatomy
15
+
16
+ ```csharp
17
+ public class Result
18
+ {
19
+ protected internal Result(bool isSuccess, Error error) { ... }
20
+
21
+ public bool IsSuccess { get; }
22
+ public bool IsFailure => !IsSuccess;
23
+ public Error Error { get; }
24
+
25
+ // Factory methods
26
+ public static Result Success() => new(true, Error.None);
27
+ public static Result<TValue> Success<TValue>(TValue value) => new(value, true, Error.None);
28
+ public static Result Failure(Error error) => new(false, error);
29
+ public static Result<TValue> Failure<TValue>(Error error) => new(default, false, error);
30
+ public static Result<TValue> Create<TValue>(TValue? value) =>
31
+ value is not null ? Success(value) : Failure<TValue>(Error.Null);
32
+ }
33
+ ```
34
+
35
+ ---
36
+
37
+ ## Result\<T\> with Implicit Conversions
38
+
39
+ ```csharp
40
+ public class Result<TValue> : Result
41
+ {
42
+ public TValue Value => IsSuccess
43
+ ? _value!
44
+ : throw new InvalidOperationException(
45
+ "The value of a failure result can not be accessed.");
46
+
47
+ // Implicit: value -> Result<T> (success)
48
+ public static implicit operator Result<TValue>(TValue? value) => Create(value);
49
+
50
+ // Implicit: Error -> Result<T> (failure)
51
+ public static implicit operator Result<TValue>(Error error) => Failure<TValue>(error);
52
+ }
53
+ ```
54
+
55
+ This allows handlers to return values or errors directly:
56
+
57
+ ```csharp
58
+ // Both are valid return statements in a handler returning Result<BookResponse>
59
+ return new BookResponse(book.Id, book.Title); // implicit success
60
+ return BookErrors.NotFound(bookId); // implicit failure
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Error Types (7 + Custom)
66
+
67
+ ```csharp
68
+ public record Error(string Code, string? Description = default, ErrorType Type = ErrorType.Failure)
69
+ {
70
+ public static readonly Error None = new(string.Empty);
71
+ public static readonly Error Null = new("Error.NullValue", "The specified result value is null.");
72
+
73
+ // Factory methods
74
+ public static Error Failure(string code, string description) =>
75
+ new(code, description, ErrorType.Failure);
76
+ public static Error Unexpected(string code, string description) =>
77
+ new(code, description, ErrorType.Unexpected);
78
+ public static Error Validation(string code, string description) =>
79
+ new(code, description, ErrorType.Validation);
80
+ public static Error Conflict(string code, string description) =>
81
+ new(code, description, ErrorType.Conflict);
82
+ public static Error NotFound(string code, string description) =>
83
+ new(code, description, ErrorType.NotFound);
84
+ public static Error Unauthorized(string code, string description) =>
85
+ new(code, description, ErrorType.Unauthorized);
86
+ public static Error Forbidden(string code, string description) =>
87
+ new(code, description, ErrorType.Forbidden);
88
+
89
+ // Implicit: Error -> Result (failure)
90
+ public static implicit operator Result(Error error) => Result.Failure(error);
91
+ }
92
+
93
+ public enum ErrorType
94
+ {
95
+ Failure, // General business rule violation
96
+ Unexpected, // Unrecoverable system error
97
+ Validation, // Input validation failure (often aggregated)
98
+ Conflict, // Duplicate / concurrency conflict
99
+ NotFound, // Entity does not exist
100
+ Unauthorized, // Missing credentials
101
+ Forbidden, // Insufficient permissions
102
+ Custom // Domain-specific extensions
103
+ }
104
+ ```
105
+
106
+ ### When to Use Which Error Type
107
+
108
+ | Error Type | HTTP Status | Use Case |
109
+ |------------|-------------|----------|
110
+ | `Failure` | 400 | Business rule violation (e.g., insufficient balance) |
111
+ | `Unexpected` | 500 | System error (e.g., external service down) |
112
+ | `Validation` | 400 | Input validation (FluentValidation aggregated) |
113
+ | `Conflict` | 409 | Duplicate key, concurrency conflict |
114
+ | `NotFound` | 404 | Entity lookup returned null |
115
+ | `Unauthorized` | 401 | Missing or invalid auth token |
116
+ | `Forbidden` | 403 | Authenticated but lacks permission |
117
+
118
+ ---
119
+
120
+ ## ValidationError (Aggregated)
121
+
122
+ Wraps multiple `Error` instances from FluentValidation:
123
+
124
+ ```csharp
125
+ public sealed record ValidationError : Error
126
+ {
127
+ public ValidationError(Error[] errors)
128
+ : base("Validation.General", "One or more validation errors occurred", ErrorType.Validation)
129
+ {
130
+ Errors = errors;
131
+ }
132
+
133
+ public Error[] Errors { get; }
134
+
135
+ public static ValidationError FromResults(IEnumerable<Result> results) =>
136
+ new(results.Where(r => r.IsFailure).Select(r => r.Error).ToArray());
137
+ }
138
+ ```
139
+
140
+ ---
141
+
142
+ ## Match() Pattern
143
+
144
+ Endpoints use `Match()` to convert `Result<T>` into HTTP responses — never access `.Value` directly:
145
+
146
+ ```csharp
147
+ public static class ResultExtensions
148
+ {
149
+ // For Result (no value)
150
+ public static TOut Match<TOut>(
151
+ this Result result,
152
+ Func<TOut> onSuccess,
153
+ Func<Error, TOut> onFailure)
154
+ {
155
+ return result.IsSuccess ? onSuccess() : onFailure(result.Error);
156
+ }
157
+
158
+ // For Result<T> (with value)
159
+ public static TOut Match<TIn, TOut>(
160
+ this Result<TIn> result,
161
+ Func<TIn, TOut> onSuccess,
162
+ Func<Error, TOut> onFailure)
163
+ {
164
+ return result.IsSuccess ? onSuccess(result.Value) : onFailure(result.Error);
165
+ }
166
+ }
167
+ ```
168
+
169
+ Usage in an endpoint:
170
+
171
+ ```csharp
172
+ var result = await handler.HandleAsync(command, cancellationToken);
173
+ return result.Match(
174
+ onSuccess: () => Results.Ok(result.Value),
175
+ onFailure: error => Results.BadRequest(error));
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Defining Feature Errors
181
+
182
+ Each feature defines its own static error factory class:
183
+
184
+ ```csharp
185
+ public static class BookErrors
186
+ {
187
+ public static Error NotFound(Guid bookId) =>
188
+ Error.NotFound("Book.NotFound", $"Book with ID '{bookId}' was not found.");
189
+
190
+ public static Error DuplicateIsbn(string isbn) =>
191
+ Error.Conflict("Book.DuplicateIsbn", $"A book with ISBN '{isbn}' already exists.");
192
+
193
+ public static Error InvalidPrice =>
194
+ Error.Failure("Book.InvalidPrice", "Book price must be greater than zero.");
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Anti-Patterns
201
+
202
+ | Do NOT | Do Instead |
203
+ |--------|------------|
204
+ | Throw exceptions for expected failures | Return `Result.Failure(error)` |
205
+ | Access `.Value` without checking `IsSuccess` | Use `Match()` |
206
+ | Use string error messages without codes | Use `Error.NotFound("Code", "Description")` |
207
+ | Catch exceptions in handlers | Let unexpected exceptions bubble to `CustomExceptionHandler` |
208
+ | Return `null` from handlers | Return `Result.Failure<T>(error)` |
209
+ | Use generic `Error.Failure` for everything | Choose the correct `ErrorType` for proper HTTP mapping |
210
+
211
+ ---
212
+
213
+ *MORPH-SPEC by Polymorphism Tech*
@@ -0,0 +1,172 @@
1
+ # Scalar.AspNetCore — API Documentation UI
2
+
3
+ > **Scope:** dotnet-vsa
4
+ > **Layer:** 0 (always load)
5
+ > **Keywords:** scalar, openapi, swagger, api-docs, documentation
6
+ > **Load When:** dotnet VSA projects
7
+
8
+ ## Overview
9
+
10
+ Scalar replaces Swagger UI as the API documentation frontend. It provides a modern UI with built-in request testing, client code generation, and dark themes. Works with ASP.NET Core's native OpenAPI support (`Microsoft.AspNetCore.OpenApi`).
11
+
12
+ ---
13
+
14
+ ## Package Setup
15
+
16
+ ```xml
17
+ <!-- .csproj -->
18
+ <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
19
+ <PackageReference Include="Scalar.AspNetCore" Version="2.12.40" />
20
+ ```
21
+
22
+ No Swashbuckle needed. ASP.NET Core 9+ has built-in OpenAPI document generation.
23
+
24
+ ---
25
+
26
+ ## Program.cs Configuration
27
+
28
+ ```csharp
29
+ using Scalar.AspNetCore;
30
+
31
+ var builder = WebApplication.CreateBuilder(args);
32
+
33
+ // Enable OpenAPI document generation
34
+ builder.Services.AddOpenApi();
35
+
36
+ var app = builder.Build();
37
+
38
+ // Map the OpenAPI JSON endpoint (dev only)
39
+ if (app.Environment.IsDevelopment())
40
+ {
41
+ app.MapOpenApi();
42
+ }
43
+
44
+ // Map Scalar UI at /scalar/v1
45
+ app.MapScalarApiReference(options =>
46
+ {
47
+ options.WithTheme(ScalarTheme.DeepSpace);
48
+
49
+ // Enable Developer Tools (request + client code panel)
50
+ options.WithDefaultHttpClient(
51
+ ScalarTarget.CSharp,
52
+ ScalarClient.HttpClient);
53
+ });
54
+ ```
55
+
56
+ **Access URL:** `https://localhost:{port}/scalar/v1`
57
+
58
+ ---
59
+
60
+ ## Available Themes
61
+
62
+ | Theme | Description |
63
+ |-------|-------------|
64
+ | `ScalarTheme.Default` | Light theme |
65
+ | `ScalarTheme.DeepSpace` | Dark blue (recommended) |
66
+ | `ScalarTheme.Purple` | Purple accent |
67
+ | `ScalarTheme.Moon` | Dark grey |
68
+ | `ScalarTheme.Saturn` | Warm dark |
69
+ | `ScalarTheme.Mars` | Red accent |
70
+
71
+ ---
72
+
73
+ ## ApiTags Organization
74
+
75
+ Group endpoints by feature using a constants class and `.WithTags()`:
76
+
77
+ ```csharp
78
+ // Constants/ApiTags.cs
79
+ public static class ApiTags
80
+ {
81
+ public const string Books = "books";
82
+ public const string Authors = "authors";
83
+ public const string Orders = "orders";
84
+ }
85
+ ```
86
+
87
+ Apply tags in endpoints:
88
+
89
+ ```csharp
90
+ internal sealed class CreateBookEndpoint : IApiEndpoint
91
+ {
92
+ public void MapEndpoint(WebApplication app)
93
+ {
94
+ app.MapPost("books", async (
95
+ IHandler<CreateBookRequest, Result<CreateBookResponse>> handler,
96
+ CreateBookRequest command,
97
+ CancellationToken cancellationToken) =>
98
+ {
99
+ var result = await handler.HandleAsync(command, cancellationToken);
100
+ return result.Match(
101
+ onSuccess: () => Results.Ok(result.Value),
102
+ onFailure: error => Results.BadRequest(error));
103
+ })
104
+ .WithTags(ApiTags.Books)
105
+ .Produces<CreateBookResponse>(StatusCodes.Status200OK)
106
+ .Produces(StatusCodes.Status400BadRequest);
107
+ }
108
+ }
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Endpoint Metadata
114
+
115
+ Use `.Produces<T>()` to document response types for OpenAPI:
116
+
117
+ ```csharp
118
+ app.MapGet("books/{id:guid}", async (...) => { ... })
119
+ .WithTags(ApiTags.Books)
120
+ .WithName("GetBookById")
121
+ .WithSummary("Get a book by its unique identifier")
122
+ .Produces<GetBookResponse>(StatusCodes.Status200OK)
123
+ .Produces(StatusCodes.Status404NotFound);
124
+
125
+ app.MapDelete("books/{id:guid}", async (...) => { ... })
126
+ .WithTags(ApiTags.Books)
127
+ .Produces(StatusCodes.Status204NoContent)
128
+ .Produces(StatusCodes.Status404NotFound);
129
+ ```
130
+
131
+ ### Common Metadata Methods
132
+
133
+ | Method | Purpose |
134
+ |--------|---------|
135
+ | `.WithTags("tag")` | Group in Scalar sidebar |
136
+ | `.WithName("OperationId")` | Unique operation identifier |
137
+ | `.WithSummary("...")` | Short description in endpoint list |
138
+ | `.WithDescription("...")` | Long description in detail panel |
139
+ | `.Produces<T>(statusCode)` | Document success response type |
140
+ | `.Produces(statusCode)` | Document error response (no body) |
141
+ | `.ProducesProblem(statusCode)` | Document ProblemDetails response |
142
+ | `.RequireAuthorization()` | Marks as requiring auth in docs |
143
+
144
+ ---
145
+
146
+ ## Client Code Generation
147
+
148
+ Scalar auto-generates client code for API calls. The `WithDefaultHttpClient` option selects the default language:
149
+
150
+ | Target | Client | Description |
151
+ |--------|--------|-------------|
152
+ | `ScalarTarget.CSharp` | `ScalarClient.HttpClient` | .NET HttpClient |
153
+ | `ScalarTarget.JavaScript` | `ScalarClient.Fetch` | Browser fetch() |
154
+ | `ScalarTarget.Python` | `ScalarClient.Requests` | Python requests |
155
+ | `ScalarTarget.Shell` | `ScalarClient.Curl` | cURL command |
156
+
157
+ ---
158
+
159
+ ## Anti-Patterns
160
+
161
+ | Do NOT | Do Instead |
162
+ |--------|------------|
163
+ | Install Swashbuckle | Use `Microsoft.AspNetCore.OpenApi` + `Scalar.AspNetCore` |
164
+ | Expose OpenAPI in production | Guard with `IsDevelopment()` check |
165
+ | Skip `.WithTags()` on endpoints | Every endpoint needs a tag for organized docs |
166
+ | Skip `.Produces<T>()` | Always document response types for accurate API docs |
167
+ | Use magic strings for tags | Define tags in `Constants/ApiTags.cs` |
168
+ | Map Scalar before `MapOpenApi()` | `MapOpenApi()` first, then `MapScalarApiReference()` |
169
+
170
+ ---
171
+
172
+ *MORPH-SPEC by Polymorphism Tech*