@polymorphism-tech/morph-spec 7.7.3 → 7.8.1

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 (187) hide show
  1. package/README.md +2 -2
  2. package/bin/validate.js +379 -379
  3. package/claude-plugin.json +1 -1
  4. package/docs/CHEATSHEET.md +1 -1
  5. package/docs/QUICKSTART.md +1 -1
  6. package/framework/CLAUDE.md +1 -1
  7. package/framework/MORPH.md +3 -2
  8. package/framework/agents.json +1 -1
  9. package/framework/commands/commit.md +166 -166
  10. package/framework/commands/morph-infra.md +215 -215
  11. package/framework/commands/morph-preflight.md +205 -205
  12. package/framework/commands/morph-troubleshoot.md +124 -124
  13. package/framework/evals/scenarios/dotnet-crud.yaml +1 -1
  14. package/framework/hooks/claude-code/statusline.py +679 -679
  15. package/framework/hooks/claude-code/statusline.sh +11 -11
  16. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  17. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  18. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  19. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  20. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  21. package/framework/hooks/shared/activity-logger.js +160 -160
  22. package/framework/hooks/shared/hook-response.js +45 -45
  23. package/framework/hooks/shared/payload-utils.js +78 -78
  24. package/framework/hooks/shared/stdin-reader.js +26 -26
  25. package/framework/rubrics/backend.json +41 -41
  26. package/framework/rules/ai-agents-standards.md +45 -45
  27. package/framework/rules/dotnet-templates.md +86 -87
  28. package/framework/rules/frontend-standards.md +13 -13
  29. package/framework/rules/infrastructure-standards.md +27 -27
  30. package/framework/rules/neon-standards.md +25 -25
  31. package/framework/rules/nextjs-standards.md +20 -20
  32. package/framework/rules/nextjs-templates.md +40 -40
  33. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  34. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  35. package/framework/skills/core/morph-apply/SKILL.md +1 -1
  36. package/framework/skills/core/morph-brainstorming/SKILL.md +9 -5
  37. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  38. package/framework/skills/core/morph-implement/SKILL.md +1 -1
  39. package/framework/skills/core/morph-plan/SKILL.md +11 -6
  40. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  41. package/framework/skills/core/morph-review/SKILL.md +13 -7
  42. package/framework/skills/core/morph-stack-scan/SKILL.md +5 -5
  43. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  44. package/framework/standards/STANDARDS.json +1 -1
  45. package/framework/standards/ai-agents/agent-session.md +160 -160
  46. package/framework/standards/ai-agents/batching.md +193 -193
  47. package/framework/standards/ai-agents/context-providers.md +188 -188
  48. package/framework/standards/ai-agents/mcp-server.md +250 -250
  49. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  50. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  51. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  52. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  53. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  54. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  55. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  56. package/framework/standards/ai-agents/production.md +275 -275
  57. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  58. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  59. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  60. package/framework/standards/ai-agents/structured-output.md +249 -249
  61. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  62. package/framework/standards/architecture/vertical-slice/vertical-slice.md +34 -35
  63. package/framework/standards/backend/api/validation.md +2 -2
  64. package/framework/standards/backend/dotnet/scrutor.md +1 -3
  65. package/framework/standards/backend/dotnet/vsa-handler-patterns.md +42 -37
  66. package/framework/standards/data/nosql/cache/redis.md +2 -2
  67. package/framework/templates/CLAUDE.md.core.template +1 -1
  68. package/framework/templates/IDE-SNIPPETS.md +262 -262
  69. package/framework/templates/README.md +0 -1
  70. package/framework/templates/REGISTRY.json +0 -21
  71. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  72. package/framework/templates/code/dotnet/abstractions/handler.cs +0 -3
  73. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  74. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  75. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  76. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  77. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  78. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  79. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  80. package/framework/templates/code/dotnet/constants/api-tags.cs +1 -1
  81. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -282
  82. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  83. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  84. package/framework/templates/code/dotnet/feature/handler.cs.hbs +23 -24
  85. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  86. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  87. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  88. package/framework/templates/code/dotnet/jobs/job.cs +15 -15
  89. package/framework/templates/code/dotnet/test.cs +72 -101
  90. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  91. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  92. package/framework/templates/code/typescript/contracts.ts +167 -167
  93. package/framework/templates/docs/clarifications.md +253 -253
  94. package/framework/templates/docs/schema-analysis.md +119 -119
  95. package/framework/templates/docs/ui-components.md +52 -52
  96. package/framework/templates/docs/ui-design-system.md +76 -76
  97. package/framework/templates/docs/ui-flows.md +167 -167
  98. package/framework/templates/docs/ui-mockups.md +98 -98
  99. package/framework/templates/feature/decisions.md +122 -122
  100. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  101. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  102. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  103. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  104. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  105. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  106. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  107. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  108. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  109. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  110. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  111. package/framework/templates/infrastructure/github/README.md +56 -56
  112. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  113. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  114. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  115. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  116. package/framework/templates/integrations/asaas-client.cs +387 -387
  117. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  118. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  119. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  120. package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +113 -113
  121. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  122. package/framework/templates/project/validate.js +120 -120
  123. package/framework/templates/state.template.json +17 -17
  124. package/framework/templates/ui/design-system.css +226 -226
  125. package/package.json +1 -1
  126. package/src/commands/dashboard/dashboard.js +53 -53
  127. package/src/commands/index.js +13 -13
  128. package/src/commands/mcp/mcp-setup.js +519 -519
  129. package/src/commands/project/checkpoint.js +209 -209
  130. package/src/commands/project/update.js +7 -4
  131. package/src/core/state/phase-state-machine.js +197 -197
  132. package/src/core/templates/template-renderer.js +477 -477
  133. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  134. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  135. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  136. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  137. package/src/dashboard/app/main.jsx +13 -13
  138. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  139. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  140. package/src/dashboard/app/pages/LiveState.jsx +239 -239
  141. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  142. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  143. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  144. package/src/dashboard/app/pages/Standards.jsx +332 -332
  145. package/src/dashboard/app/styles/index.css +47 -47
  146. package/src/dashboard/index.html +12 -12
  147. package/src/dashboard/package.json +24 -24
  148. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  149. package/src/dashboard/scanner/phases-scanner.js +15 -15
  150. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  151. package/src/dashboard/scanner/rules-scanner.js +37 -37
  152. package/src/dashboard/scanner/skills-scanner.js +43 -43
  153. package/src/dashboard/scanner/squads-scanner.js +13 -13
  154. package/src/dashboard/scanner/standards-scanner.js +26 -26
  155. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  156. package/src/dashboard/vite.config.js +24 -24
  157. package/src/lib/agents/metrics-aggregator.js +56 -56
  158. package/src/lib/agents/metrics-calculator.js +125 -125
  159. package/src/lib/agents/output-collector.js +73 -73
  160. package/src/lib/detectors/claude-config-detector.js +152 -152
  161. package/src/lib/detectors/design-system-detector.js +231 -231
  162. package/src/lib/detectors/index.js +217 -217
  163. package/src/lib/generators/architecture-scanner.js +639 -639
  164. package/src/lib/generators/design-system-generator.js +508 -508
  165. package/src/lib/generators/index.js +13 -13
  166. package/src/lib/generators/settings-generator.js +209 -209
  167. package/src/lib/hooks/hook-executor.js +167 -167
  168. package/src/lib/installers/mcp-installer.js +719 -719
  169. package/src/lib/tasks/task-classifier.js +30 -30
  170. package/src/lib/tasks/task-parser.js +209 -209
  171. package/src/lib/tasks/test-runner.js +81 -81
  172. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  173. package/src/lib/validators/checkpoint-persistence.js +39 -39
  174. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  175. package/src/lib/validators/css/css-validator.js +340 -340
  176. package/src/lib/validators/packages/package-validator.js +347 -347
  177. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  178. package/src/lib/validators/shared/issue-counter.js +14 -14
  179. package/src/lib/validators/validation-runner.js +349 -349
  180. package/src/scripts/setup-infra.js +383 -383
  181. package/src/utils/agent-id-mapper.js +11 -11
  182. package/src/utils/banner.js +52 -43
  183. package/src/utils/claude-settings-manager.js +300 -300
  184. package/src/utils/skills-installer.js +172 -172
  185. package/framework/templates/.idea/morph-templates.xml +0 -92
  186. package/framework/templates/.vscode/morph-templates.code-snippets +0 -186
  187. package/framework/templates/code/dotnet/backend/repository.cs +0 -141
@@ -1,109 +1,109 @@
1
- # Sweet Spot — When to use what in MAF
2
-
3
- > **Scope:** stacks=["dotnet"]
4
- > **Layer:** 0 (always-load via persona maf-expert)
5
- > **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent
6
- > **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
7
-
8
- **Verified against:** MAF Tier List research (2026-05-18) + Microsoft Learn docs. Last-verified: 2026-05-19.
9
-
10
- ---
11
-
12
- ## The 4-feature rule
13
-
14
- ```
15
- 95% of production AI agents need ONLY:
16
-
17
- 1. Chat (the LLM call)
18
- 2. Structured Output (typed records, not free text)
19
- 3. Tools (typed functions the agent can call)
20
- 4. RAG Custom (pgvector tool, not hosted RAG)
21
-
22
- If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
23
- ```
24
-
25
- Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
26
-
27
- ---
28
-
29
- ## Decision tree (Claude reads this in plan phase)
30
-
31
- ```
32
- Step 1 — Does the task even need an LLM?
33
- ├─ "Can a deterministic function solve this?" → YES → write the function, no agent
34
- └─ NO → continue
35
-
36
- Step 2 — Single agent vs multi-agent?
37
- ├─ One topic + one decision → single agent (DEFAULT)
38
- ├─ Multiple specialized topics with clear handoff → multi-agent
39
- │ └─ Prefer Agent-as-Tool pattern (one agent uses another as a tool)
40
- │ └─ See ai-agents-multi-agent-patterns
41
- └─ "I'm not sure" → start with single agent, escalate if needed
42
-
43
- Step 3 — Need a Workflow?
44
- ├─ The feature has fan-in/out (many inputs aggregated) → maybe workflow
45
- │ └─ Confirm: can you write it as a foreach + LINQ aggregate?
46
- │ └─ YES → write the foreach, NO workflow
47
- │ └─ NO → workflow may help
48
- ├─ Long-running with human-in-the-loop checkpoints → maybe workflow
49
- │ └─ See ai-agents-workflows (Onda 2)
50
- └─ Otherwise → NO workflow
51
-
52
- Step 4 — Which provider?
53
- ├─ ALWAYS direct models — never a hosted agent service
54
- ├─ Provider choice (OpenAI / Google / Anthropic / Ollama) lives in the
55
- │ project's Model Registry, not hardcoded → see ai-agents-providers-model-registry
56
- └─ Hosted services (Foundry Agent Service, Vertex AI hosted) → NOT used
57
-
58
- Step 5 — RAG approach?
59
- ├─ Need control over chunking/embedding/storage? → RAG custom (pgvector)
60
- │ └─ See ai-agents-rag-custom-pgvector (Onda 2)
61
- └─ Don't reach for hosted RAG — loses control
62
- ```
63
-
64
- ---
65
-
66
- ## Anti-patterns (what Claude should NOT do)
67
-
68
- | Anti-pattern | Why it's wrong | What to do instead |
69
- |--------------|----------------|---------------------|
70
- | Reach for Workflow when a function does the job | "Tudo que Workflows fazem, código C# normal faz com menos complexidade" — pesquisa | Write the function. Wrap in a tool if an agent needs to call it. |
71
- | Use a hosted agent service | Hosted is "always 6-9 months behind" on models + removes control | Direct models always (OpenAI / Google / Anthropic / Ollama) |
72
- | Upload PDFs to a hosted RAG | Loses control of chunking + embedding + storage | Custom RAG with pgvector. See ai-agents-rag-custom-pgvector |
73
- | Add Memory/Session because "agents need state" | If not building a chatbot, don't add chat state | Skip. Add when a real chat UI exists. |
74
- | Use Semantic Kernel for new features | SK is superseded by MAF | Use Microsoft.Agents.AI. See ai-agents-setup |
75
- | Return free text and parse it downstream | LLM output is non-deterministic | Structured Output. See ai-agents-structured-output (Onda 2) |
76
-
77
- ---
78
-
79
- ## When the 4-feature rule does NOT apply
80
-
81
- You may need to step outside the sweet spot when:
82
- - The feature genuinely fans-out (e.g., monthly report aggregating N items) → workflow with fan-in
83
- - The feature needs vision/audio/image-gen → modality-specific standards (see ai-agents-modalities-* — Onda 3)
84
-
85
- **Even in these cases:** the core of the agent still follows the 4-feature rule. Only the orchestration layer changes.
86
-
87
- ---
88
-
89
- ## Checklist (verifiable by morph-eval)
90
-
91
- - [ ] Was each agent designed as single-agent BEFORE reaching for multi-agent? (Check `decisions.md`.)
92
- - [ ] If a Workflow is used, is fan-in/out or HITL checkpointing genuinely present? Or could `foreach + LINQ` replace it?
93
- - [ ] Are the 4 features (Chat, Structured Output, Tools, RAG Custom) all present where applicable?
94
- - [ ] Are hosted agent services avoided?
95
-
96
- ---
97
-
98
- ## References
99
-
100
- - `ai-agents-setup` — packages and DI
101
- - `ai-agents-providers-model-registry` — provider-agnostic config
102
- - `ai-agents-structured-output` (Onda 2)
103
- - `ai-agents-multi-agent-patterns` (Onda 2)
104
- - `ai-agents-workflows` (Onda 2, reframed)
105
- - `ai-agents-rag-custom-pgvector` (Onda 2)
106
-
107
- ---
108
-
109
- *MORPH-SPEC by Polymorphism Tech — ai-agents/sweet-spot.md v1.0 (2026-05-19)*
1
+ # Sweet Spot — When to use what in MAF
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 0 (always-load via persona maf-expert)
5
+ > **Keywords:** sweet spot, when to use agent, agent vs workflow, when not to use workflow, MAF decision, simple agent
6
+ > **Read by Claude in:** plan (decide arquitetura da feature antes de quebrar em tasks) + implement (validar que a arquitetura ainda é a mais simples possível)
7
+
8
+ **Verified against:** MAF Tier List research (2026-05-18) + Microsoft Learn docs. Last-verified: 2026-05-19.
9
+
10
+ ---
11
+
12
+ ## The 4-feature rule
13
+
14
+ ```
15
+ 95% of production AI agents need ONLY:
16
+
17
+ 1. Chat (the LLM call)
18
+ 2. Structured Output (typed records, not free text)
19
+ 3. Tools (typed functions the agent can call)
20
+ 4. RAG Custom (pgvector tool, not hosted RAG)
21
+
22
+ If your feature needs ONLY these 4 → use a single agent. Don't reach for workflows.
23
+ ```
24
+
25
+ Apply this rule before deciding multi-agent or workflow. Most of the time the answer is "single agent".
26
+
27
+ ---
28
+
29
+ ## Decision tree (Claude reads this in plan phase)
30
+
31
+ ```
32
+ Step 1 — Does the task even need an LLM?
33
+ ├─ "Can a deterministic function solve this?" → YES → write the function, no agent
34
+ └─ NO → continue
35
+
36
+ Step 2 — Single agent vs multi-agent?
37
+ ├─ One topic + one decision → single agent (DEFAULT)
38
+ ├─ Multiple specialized topics with clear handoff → multi-agent
39
+ │ └─ Prefer Agent-as-Tool pattern (one agent uses another as a tool)
40
+ │ └─ See ai-agents-multi-agent-patterns
41
+ └─ "I'm not sure" → start with single agent, escalate if needed
42
+
43
+ Step 3 — Need a Workflow?
44
+ ├─ The feature has fan-in/out (many inputs aggregated) → maybe workflow
45
+ │ └─ Confirm: can you write it as a foreach + LINQ aggregate?
46
+ │ └─ YES → write the foreach, NO workflow
47
+ │ └─ NO → workflow may help
48
+ ├─ Long-running with human-in-the-loop checkpoints → maybe workflow
49
+ │ └─ See ai-agents-workflows (Onda 2)
50
+ └─ Otherwise → NO workflow
51
+
52
+ Step 4 — Which provider?
53
+ ├─ ALWAYS direct models — never a hosted agent service
54
+ ├─ Provider choice (OpenAI / Google / Anthropic / Ollama) lives in the
55
+ │ project's Model Registry, not hardcoded → see ai-agents-providers-model-registry
56
+ └─ Hosted services (Foundry Agent Service, Vertex AI hosted) → NOT used
57
+
58
+ Step 5 — RAG approach?
59
+ ├─ Need control over chunking/embedding/storage? → RAG custom (pgvector)
60
+ │ └─ See ai-agents-rag-custom-pgvector (Onda 2)
61
+ └─ Don't reach for hosted RAG — loses control
62
+ ```
63
+
64
+ ---
65
+
66
+ ## Anti-patterns (what Claude should NOT do)
67
+
68
+ | Anti-pattern | Why it's wrong | What to do instead |
69
+ |--------------|----------------|---------------------|
70
+ | Reach for Workflow when a function does the job | "Tudo que Workflows fazem, código C# normal faz com menos complexidade" — pesquisa | Write the function. Wrap in a tool if an agent needs to call it. |
71
+ | Use a hosted agent service | Hosted is "always 6-9 months behind" on models + removes control | Direct models always (OpenAI / Google / Anthropic / Ollama) |
72
+ | Upload PDFs to a hosted RAG | Loses control of chunking + embedding + storage | Custom RAG with pgvector. See ai-agents-rag-custom-pgvector |
73
+ | Add Memory/Session because "agents need state" | If not building a chatbot, don't add chat state | Skip. Add when a real chat UI exists. |
74
+ | Use Semantic Kernel for new features | SK is superseded by MAF | Use Microsoft.Agents.AI. See ai-agents-setup |
75
+ | Return free text and parse it downstream | LLM output is non-deterministic | Structured Output. See ai-agents-structured-output (Onda 2) |
76
+
77
+ ---
78
+
79
+ ## When the 4-feature rule does NOT apply
80
+
81
+ You may need to step outside the sweet spot when:
82
+ - The feature genuinely fans-out (e.g., monthly report aggregating N items) → workflow with fan-in
83
+ - The feature needs vision/audio/image-gen → modality-specific standards (see ai-agents-modalities-* — Onda 3)
84
+
85
+ **Even in these cases:** the core of the agent still follows the 4-feature rule. Only the orchestration layer changes.
86
+
87
+ ---
88
+
89
+ ## Checklist (verifiable by morph-eval)
90
+
91
+ - [ ] Was each agent designed as single-agent BEFORE reaching for multi-agent? (Check `decisions.md`.)
92
+ - [ ] If a Workflow is used, is fan-in/out or HITL checkpointing genuinely present? Or could `foreach + LINQ` replace it?
93
+ - [ ] Are the 4 features (Chat, Structured Output, Tools, RAG Custom) all present where applicable?
94
+ - [ ] Are hosted agent services avoided?
95
+
96
+ ---
97
+
98
+ ## References
99
+
100
+ - `ai-agents-setup` — packages and DI
101
+ - `ai-agents-providers-model-registry` — provider-agnostic config
102
+ - `ai-agents-structured-output` (Onda 2)
103
+ - `ai-agents-multi-agent-patterns` (Onda 2)
104
+ - `ai-agents-workflows` (Onda 2, reframed)
105
+ - `ai-agents-rag-custom-pgvector` (Onda 2)
106
+
107
+ ---
108
+
109
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/sweet-spot.md v1.0 (2026-05-19)*
@@ -10,7 +10,8 @@
10
10
 
11
11
  Padrão arquitetural baseado em **KanaiyaKatarmal/VerticalSliceArchitectureTemplate** (.NET 10, Minimal APIs, EF Core 10).
12
12
  Cada feature é uma fatia vertical completa: endpoint → handler → validator → entity → DB.
13
- Sem MediatR, sem AggregateRoot, sem Domain Events, sem CQRS formal.
13
+ Sem MediatR, sem AggregateRoot, sem Domain Events, sem CQRS formal, sem repository genérico
14
+ (divergência intencional do template base: o handler usa `ApplicationDbContext` direto).
14
15
 
15
16
  ---
16
17
 
@@ -54,11 +55,6 @@ Sem MediatR, sem AggregateRoot, sem Domain Events, sem CQRS formal.
54
55
  ├── Pipelines/
55
56
  │ ├── ValidationDecorator.cs # Decorator que executa validators
56
57
  │ └── LoggingDecorator.cs # Decorator que loga request/failure
57
- ├── Repository/
58
- │ ├── IRepository.cs # IRepository<T> genérico
59
- │ ├── Repository.cs # Repository<T> : IRepository<T>
60
- │ ├── IUnitOfWork.cs # IUnitOfWork
61
- │ └── UnitOfWork.cs # UnitOfWork
62
58
  └── Program.cs
63
59
  ```
64
60
 
@@ -190,8 +186,7 @@ public sealed record CreateBookRequest(string Title, string Author, string ISBN,
190
186
  public sealed record CreateBookResponse(Guid Id, string Title, string Author, string ISBN, decimal Price, int PublishedYear);
191
187
 
192
188
  public sealed class CreateBookHandler(
193
- IRepository<Book> _bookRepo,
194
- IUnitOfWork _unitOfWork) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
189
+ ApplicationDbContext _db) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
195
190
  {
196
191
  public async Task<Result<CreateBookResponse>> HandleAsync(
197
192
  CreateBookRequest command, CancellationToken cancellationToken)
@@ -205,8 +200,8 @@ public sealed class CreateBookHandler(
205
200
  Price = command.Price,
206
201
  PublishedYear = command.PublishedYear
207
202
  };
208
- await _bookRepo.AddAsync(book, cancellationToken);
209
- await _unitOfWork.CommitAsync(cancellationToken);
203
+ _db.Books.Add(book);
204
+ await _db.SaveChangesAsync(cancellationToken);
210
205
  return Result.Success(new CreateBookResponse(
211
206
  book.Id, book.Title, book.Author, book.ISBN, book.Price, book.PublishedYear));
212
207
  }
@@ -306,38 +301,41 @@ Ordem de execução (innermost-first): `LoggingDecorator → ValidationDecorator
306
301
 
307
302
  ---
308
303
 
309
- ## Repository Pattern
304
+ ## Acesso a Dados — DbContext direto no slice
305
+
306
+ O handler injeta `ApplicationDbContext` direto. Sobre EF Core um repository genérico é
307
+ redundante: `DbSet<T>` já é repository, `DbContext` já é unit of work. **Nunca crie
308
+ `IRepository<T>`/`IUnitOfWork`** — a auditoria de projetos reais mostrou que o par nasce
309
+ registrado no DI e morre órfão, sem consumidores.
310
310
 
311
311
  ```csharp
312
- // Repository/IRepository.cs
313
- public interface IRepository<T> where T : class
314
- {
315
- Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
316
- Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
317
- Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
318
- Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
319
- Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
320
- IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams);
321
- Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
322
- void RemoveRange(IEnumerable<T> entities);
323
- }
312
+ // CRUD por chave — direto no DbSet
313
+ var book = await _db.Books.FindAsync([command.Id], cancellationToken);
314
+ _db.Books.Add(book);
315
+ await _db.SaveChangesAsync(cancellationToken);
316
+
317
+ // Queries específicas — LINQ no slice (joins, projeção, paginação, filtros compostos)
318
+ var page = await _db.Books
319
+ .Where(b => b.Author == command.Author)
320
+ .OrderBy(b => b.Title)
321
+ .Skip(command.Skip).Take(command.Take)
322
+ .Select(b => new BookDto(b.Id, b.Title))
323
+ .ToListAsync(cancellationToken);
324
324
  ```
325
325
 
326
- **Quando NÃO usar o repository:** o `IRepository<T>` genérico cobre CRUD por chave.
327
- Para queries com join, projeção, paginação ou filtros compostos, injete
328
- `ApplicationDbContext` direto no handler e escreva o LINQ no slice — não polua a
329
- interface genérica com métodos de um único slice, e não use `ExecuteQuery(string sql)`.
330
- Veja `backend/dotnet/vsa-handler-patterns.md` → "When to Use DbContext Directly".
326
+ **Stores dedicados (exceção justificada):** quando um slice exige controle de concorrência
327
+ (ex.: `SELECT ... FOR UPDATE`, retry de `DbUpdateConcurrencyException`) ou SQL específico
328
+ que o LINQ não expressa, escreva um store hand-written e purpose-built (ex.: `OutboxStore`,
329
+ `LeaseStore`) com métodos nomeados pela intenção — e registre a justificativa em
330
+ `decisions.md`. Nunca um genérico por entidade.
331
+
332
+ Veja `backend/dotnet/vsa-handler-patterns.md` → "Data Access: DbContext in the Slice".
331
333
 
332
334
  ---
333
335
 
334
336
  ## DI Registration (Program.cs)
335
337
 
336
338
  ```csharp
337
- // Repositories + Unit of Work
338
- builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
339
- builder.Services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
340
-
341
339
  // EF Core (SQLite/SqlServer/PostgreSQL)
342
340
  builder.Services.AddSQLDatabaseConfiguration(builder.Configuration);
343
341
 
@@ -385,8 +383,8 @@ app.MapApiEndpoints();
385
383
  4. **`result.Match()`** nos endpoints — nunca acessar `result.Value` sem checar `IsSuccess`.
386
384
  5. **IApiEndpoint `internal sealed`** — endpoints não são públicos.
387
385
  6. **`{Entity}Errors` compartilhado** por todas as operações da feature (1 arquivo por feature, não por slice).
388
- 7. **Acesso a dados híbrido**: `IRepository<T>` injetado no handler para CRUD por chave; `ApplicationDbContext` injetado direto no slice para queries específicas (joins, projeção, paginação). Nunca uma Application Service layer entre handler e dados.
389
- 8. **`IUnitOfWork.CommitAsync()`** em operações de escrita (Create, Update, Delete).
386
+ 7. **`ApplicationDbContext` direto no slice**: CRUD por chave via `DbSet<T>`; queries específicas (joins, projeção, paginação) via LINQ no handler. Nunca uma Application Service layer entre handler e dados; nunca repository genérico (`IRepository<T>`/`IUnitOfWork`).
387
+ 8. **`SaveChangesAsync()`** explícito em operações de escrita (Create, Update, Delete).
390
388
  9. **Sem validator em GetAll** — GetAllBooksHandler não tem validator (sem parâmetros para validar).
391
389
 
392
390
  ---
@@ -399,7 +397,8 @@ app.MapApiEndpoints();
399
397
  | `DomainEvent` | VSA não tem Domain Events — reações são handlers separados |
400
398
  | `MediatR` / `IRequest` / `IRequestHandler` | Substituído por `IHandler<TRequest, TResponse>` próprio |
401
399
  | DTOs compartilhados entre features | Cada slice define seus próprios records |
402
- | Application Service layer | Handler acessa Repository diretamente |
400
+ | Application Service layer | Handler acessa o `ApplicationDbContext` diretamente |
401
+ | Repository genérico (`IRepository<T>`/`IUnitOfWork`) | `DbSet<T>` já é repository, `DbContext` já é unit of work — vira código morto |
403
402
  | CQRS formal (Commands/Queries) | Handler simples, sem distinção Command/Query |
404
403
  | Shared Validators em Application layer | Validator fica no mesmo namespace do slice |
405
404
 
@@ -62,7 +62,7 @@ public enum OrderStatus
62
62
  ```csharp
63
63
  public async Task<Result> ProcessOrderAsync(Guid orderId, CancellationToken ct)
64
64
  {
65
- var order = await _repository.GetByIdAsync(orderId, ct);
65
+ var order = await _db.Orders.FindAsync([orderId], ct);
66
66
  if (order == null) return Result.Failure("Order not found");
67
67
 
68
68
  // State validation (invalid states)
@@ -74,7 +74,7 @@ public async Task<Result> ProcessOrderAsync(Guid orderId, CancellationToken ct)
74
74
  if (order.RequiresPayment && !order.IsPaid) return Result.Failure("Payment required");
75
75
 
76
76
  order.MarkAsProcessing();
77
- await _repository.UpdateAsync(order, ct);
77
+ await _db.SaveChangesAsync(ct);
78
78
  return Result.Success();
79
79
  }
80
80
  ```
@@ -126,9 +126,7 @@ builder.Services.AddHandlersFromAssembly(typeof(Program).Assembly);
126
126
  The full DI registration sequence in VSA:
127
127
 
128
128
  ```csharp
129
- // 1. Infrastructure
130
- builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
131
- builder.Services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
129
+ // 1. Infrastructure (DbContext)
132
130
  builder.Services.AddSQLDatabaseConfiguration(builder.Configuration);
133
131
 
134
132
  // 2. Endpoints (auto-discovery)
@@ -11,7 +11,7 @@
11
11
 
12
12
  ## Overview
13
13
 
14
- Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR). Dependencies use primary constructors. The data path is `Handler -> Repository -> UnitOfWork` with no service layer in between.
14
+ Each feature in VSA is a self-contained slice with 5 files in a feature folder. Handlers implement `IHandler<TRequest, TResponse>` (not MediatR). Dependencies use primary constructors. The data path is `Handler -> ApplicationDbContext` — no service layer, no generic repository in between.
15
15
 
16
16
  ---
17
17
 
@@ -71,8 +71,8 @@ public sealed record CreateBookResponse(
71
71
  namespace ProjectName.Features.BookFeature.CreateBook;
72
72
 
73
73
  using ProjectName.Abstractions;
74
+ using ProjectName.Database;
74
75
  using ProjectName.Entities;
75
- using ProjectName.Repository;
76
76
 
77
77
  public sealed record CreateBookRequest(
78
78
  string Title, string Author, string ISBN, decimal Price, int PublishedYear);
@@ -82,8 +82,7 @@ public sealed record CreateBookResponse(
82
82
 
83
83
  // Primary constructor for DI — no field declarations needed
84
84
  public sealed class CreateBookHandler(
85
- IRepository<Book> bookRepo,
86
- IUnitOfWork unitOfWork) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
85
+ ApplicationDbContext db) : IHandler<CreateBookRequest, Result<CreateBookResponse>>
87
86
  {
88
87
  public async Task<Result<CreateBookResponse>> HandleAsync(
89
88
  CreateBookRequest command, CancellationToken cancellationToken)
@@ -98,8 +97,8 @@ public sealed class CreateBookHandler(
98
97
  PublishedYear = command.PublishedYear
99
98
  };
100
99
 
101
- await bookRepo.AddAsync(book, cancellationToken);
102
- await unitOfWork.CommitAsync(cancellationToken);
100
+ db.Books.Add(book);
101
+ await db.SaveChangesAsync(cancellationToken);
103
102
 
104
103
  return Result.Success(new CreateBookResponse(
105
104
  book.Id, book.Title, book.Author, book.ISBN, book.Price, book.PublishedYear));
@@ -111,7 +110,7 @@ public sealed class CreateBookHandler(
111
110
 
112
111
  - **`sealed class`** — handlers are never inherited
113
112
  - **`sealed record`** — DTOs are immutable value types
114
- - **Primary constructors** — `(IRepository<Book> bookRepo, IUnitOfWork unitOfWork)` replaces field + constructor boilerplate
113
+ - **Primary constructors** — `(ApplicationDbContext db)` replaces field + constructor boilerplate
115
114
  - **`Guid.CreateVersion7()`** — time-sortable, index-friendly UUID (not `Guid.NewGuid()`)
116
115
  - **`CancellationToken`** — passed through every async call
117
116
  - **`Result<T>` return** — never throw for expected failures
@@ -172,36 +171,35 @@ All endpoint classes implementing this interface are auto-discovered and mapped
172
171
 
173
172
  ---
174
173
 
175
- ## Repository + UnitOfWork (No Service Layer)
174
+ ## Data Access: DbContext in the Slice (No Service Layer, No Generic Repository)
176
175
 
177
- Handlers call repositories directly. There is no service/manager layer:
176
+ Handlers inject `ApplicationDbContext` directly. Over EF Core a generic repository is
177
+ redundant — `DbSet<T>` **is** the repository, `DbContext` **is** the unit of work. A
178
+ generic `IRepository<T>`/`IUnitOfWork` pair adds indirection with zero value and, in
179
+ audited real projects, ends up registered in DI with zero consumers (dead code).
180
+
181
+ CRUD by key goes straight to the `DbSet`:
178
182
 
179
183
  ```csharp
180
- public interface IRepository<T> where T : class
184
+ public sealed class DeleteBookHandler(
185
+ ApplicationDbContext db) : IHandler<DeleteBookRequest, Result>
181
186
  {
182
- Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
183
- Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
184
- Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
185
- Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
186
- Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
187
- Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
188
- void RemoveRange(IEnumerable<T> entities);
189
- }
187
+ public async Task<Result> HandleAsync(
188
+ DeleteBookRequest command, CancellationToken cancellationToken)
189
+ {
190
+ var book = await db.Books.FindAsync([command.Id], cancellationToken);
191
+ if (book is null)
192
+ return BookErrors.NotFound(command.Id);
190
193
 
191
- public interface IUnitOfWork
192
- {
193
- Task<int> CommitAsync(CancellationToken cancellationToken);
194
- Task RollbackAsync(CancellationToken cancellationToken = default);
194
+ db.Books.Remove(book);
195
+ await db.SaveChangesAsync(cancellationToken);
196
+ return Result.Success();
197
+ }
195
198
  }
196
199
  ```
197
200
 
198
- **Pattern:** `Repository.AddAsync()` -> `UnitOfWork.CommitAsync()` — always commit explicitly.
199
-
200
- ### When to Use DbContext Directly Instead
201
-
202
- `IRepository<T>` is the right tool for CRUD by key. It is **not** the right tool for
203
- slice-specific queries — joins, projections, pagination, composite filters. For those,
204
- inject `ApplicationDbContext` directly into the handler and write the LINQ in the slice:
201
+ Slice-specific queries — joins, projections, pagination, composite filters — are LINQ
202
+ written in the slice:
205
203
 
206
204
  ```csharp
207
205
  public sealed class SearchBooksHandler(
@@ -222,8 +220,16 @@ public sealed class SearchBooksHandler(
222
220
  }
223
221
  ```
224
222
 
225
- **Rule of thumb:** CRUD by key → `IRepository<T>` + `IUnitOfWork`. Anything that needs a
226
- custom query shape → `ApplicationDbContext` in the slice. Never reach for raw SQL
223
+ ### Dedicated Stores (the justified exception)
224
+
225
+ When a slice needs concurrency control (`SELECT ... FOR UPDATE`, lease/outbox semantics,
226
+ `DbUpdateConcurrencyException` retry loops) or SQL that LINQ cannot express, write a
227
+ **hand-written, purpose-built store** with intention-named methods (e.g. `OutboxStore.ClaimBatchAsync`,
228
+ `LeaseStore.TryAcquireAsync`) — and document the justification in `decisions.md`.
229
+ Never a generic-per-entity abstraction.
230
+
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
227
233
  (`FromSqlRaw`) unless a query genuinely cannot be expressed in LINQ — and document that
228
234
  in `decisions.md`.
229
235
 
@@ -251,7 +257,7 @@ public static class BookErrors
251
257
  Usage in handler:
252
258
 
253
259
  ```csharp
254
- var existing = await bookRepo.GetByIdAsync(command.BookId, cancellationToken);
260
+ var existing = await db.Books.FindAsync([command.BookId], cancellationToken);
255
261
  if (existing is null)
256
262
  return BookErrors.NotFound(command.BookId); // implicit conversion to Result<T>
257
263
  ```
@@ -264,9 +270,7 @@ if (existing is null)
264
270
  // 1. OpenAPI
265
271
  builder.Services.AddOpenApi();
266
272
 
267
- // 2. Infrastructure (Repository, UnitOfWork, DbContext)
268
- builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
269
- builder.Services.AddScoped(typeof(IUnitOfWork), typeof(UnitOfWork));
273
+ // 2. Infrastructure (DbContext)
270
274
  builder.Services.AddSQLDatabaseConfiguration(builder.Configuration);
271
275
 
272
276
  // 3. Endpoint auto-discovery
@@ -293,7 +297,8 @@ builder.Services.AddExceptionHandler<CustomExceptionHandler>()
293
297
  | Do NOT | Do Instead |
294
298
  |--------|------------|
295
299
  | Use MediatR `IRequest`/`Send()` | Use `IHandler<TReq, TRes>` resolved directly from DI |
296
- | Create a service/manager layer | Handler calls Repository + UnitOfWork directly |
300
+ | Create a service/manager layer | Handler uses `ApplicationDbContext` directly |
301
+ | Add a generic repository (`IRepository<T>`/`IUnitOfWork`) | `DbSet<T>` is the repository; `DbContext` is the unit of work |
297
302
  | Use `Guid.NewGuid()` | Use `Guid.CreateVersion7()` for time-sortable IDs |
298
303
  | Make handler classes non-sealed | Always `sealed class` |
299
304
  | Use mutable DTOs (class with setters) | Always `sealed record` with positional parameters |
@@ -302,7 +307,7 @@ builder.Services.AddExceptionHandler<CustomExceptionHandler>()
302
307
  | Share handlers across features | Each feature has its own handler, even if similar |
303
308
  | Use constructor + field for DI | Use primary constructors: `class Handler(IDep dep)` |
304
309
  | Group by technical concern (Controllers/, Services/) | Group by feature: `Features/BookFeature/CreateBook/` |
305
- | Force a slice-specific query through `IRepository<T>` | Inject `DbContext` and write LINQ in the slice |
310
+ | Route data access through any indirection layer | Inject `DbContext` and write LINQ in the slice; dedicated store only with `decisions.md` justification |
306
311
  | Use raw SQL (`FromSqlRaw` / `ExecuteQuery(string)`) by default | Use LINQ; raw SQL only with a `decisions.md` justification |
307
312
 
308
313
  ---
@@ -32,7 +32,7 @@ builder.Services.AddStackExchangeRedisCache(opts =>
32
32
  public class CachedProductService : IProductService
33
33
  {
34
34
  private readonly IDistributedCache _cache;
35
- private readonly IProductRepository _repository;
35
+ private readonly ApplicationDbContext _db;
36
36
 
37
37
  public async Task<Product?> GetAsync(Guid id, CancellationToken ct = default)
38
38
  {
@@ -44,7 +44,7 @@ public class CachedProductService : IProductService
44
44
  return JsonSerializer.Deserialize<Product>(cached);
45
45
 
46
46
  // Cache miss → read from DB
47
- var product = await _repository.GetAsync(id, ct);
47
+ var product = await _db.Products.FindAsync([id], ct);
48
48
  if (product == null) return null;
49
49
 
50
50
  // Cache with TTL
@@ -6,7 +6,7 @@
6
6
 
7
7
  VSA + .NET 10 + EF Core + Neon (Postgres) + Microsoft Agent Framework + Next.js (App Router) + Tailwind + Framer Motion + Shadcn + Hangfire (quando necessário).
8
8
 
9
- Padrões proibidos por padrão: `Guid.NewGuid()` (use `Guid.CreateVersion7()`), classes não-sealed sem justificativa, exceptions como fluxo de controle, camada de Service/Manager entre handler e dados, SQL cru sem justificativa. Acesso a dados é híbrido: `IRepository<T>` + `IUnitOfWork` para CRUD; `DbContext` direto no slice para queries específicas.
9
+ Padrões proibidos por padrão: `Guid.NewGuid()` (use `Guid.CreateVersion7()`), classes não-sealed sem justificativa, exceptions como fluxo de controle, camada de Service/Manager entre handler e dados, repository genérico (`IRepository<T>`/`IUnitOfWork`), SQL cru sem justificativa. Acesso a dados: `ApplicationDbContext` direto no slice (`DbSet<T>` já é repository, `DbContext` já é unit of work); para concorrência ou SQL específico, store dedicado hand-written com justificativa em `decisions.md`.
10
10
 
11
11
  ## Harness
12
12