@polymorphism-tech/morph-spec 7.9.3 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.md +31 -2
  3. package/bin/morph-spec.js +50 -2
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +38 -1
  7. package/docs/QUICKSTART.md +60 -1
  8. package/framework/CLAUDE.md +13 -2
  9. package/framework/MORPH.md +38 -2
  10. package/framework/agents.json +22 -3
  11. package/framework/commands/commit.md +166 -166
  12. package/framework/commands/morph-hotfix.md +39 -0
  13. package/framework/commands/morph-infra.md +215 -215
  14. package/framework/commands/morph-preflight.md +205 -205
  15. package/framework/commands/morph-troubleshoot.md +124 -124
  16. package/framework/hooks/README.md +25 -10
  17. package/framework/hooks/claude-code/core/gate-guard.js +4 -2
  18. package/framework/hooks/claude-code/core/loop-detect.js +2 -1
  19. package/framework/hooks/claude-code/core/post-edit-typecheck.js +14 -5
  20. package/framework/hooks/claude-code/core/tasks-json-guard.js +4 -2
  21. package/framework/hooks/claude-code/core/validate-completion.js +1 -1
  22. package/framework/hooks/claude-code/core/worktree-context.js +160 -0
  23. package/framework/hooks/claude-code/statusline.py +679 -679
  24. package/framework/hooks/claude-code/statusline.sh +11 -11
  25. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  26. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  27. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  28. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  29. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  30. package/framework/hooks/shared/activity-logger.js +160 -160
  31. package/framework/hooks/shared/build-check.js +2 -2
  32. package/framework/hooks/shared/hook-response.js +17 -5
  33. package/framework/hooks/shared/payload-utils.js +42 -0
  34. package/framework/hooks/shared/state-reader.js +22 -2
  35. package/framework/hooks/shared/stdin-reader.js +26 -26
  36. package/framework/rubrics/backend.json +41 -41
  37. package/framework/rules/ai-agents-standards.md +45 -45
  38. package/framework/rules/dotnet-templates.md +86 -86
  39. package/framework/rules/frontend-standards.md +13 -13
  40. package/framework/rules/infrastructure-standards.md +27 -27
  41. package/framework/rules/neon-standards.md +25 -25
  42. package/framework/rules/nextjs-standards.md +20 -20
  43. package/framework/rules/nextjs-templates.md +40 -40
  44. package/framework/schemas/feature.schema.json +95 -1
  45. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  46. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  47. package/framework/skills/core/morph-apply/SKILL.md +39 -8
  48. package/framework/skills/core/morph-brainstorming/SKILL.md +9 -1
  49. package/framework/skills/core/morph-eval/SKILL.md +6 -1
  50. package/framework/skills/core/morph-hotfix/SKILL.md +136 -0
  51. package/framework/skills/core/morph-implement/SKILL.md +8 -4
  52. package/framework/skills/core/morph-plan/SKILL.md +5 -1
  53. package/framework/skills/core/morph-proposal/SKILL.md +24 -1
  54. package/framework/skills/core/morph-review/SKILL.md +3 -2
  55. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  56. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  57. package/framework/standards/STANDARDS.json +7 -2
  58. package/framework/standards/ai-agents/agent-session.md +160 -160
  59. package/framework/standards/ai-agents/batching.md +193 -193
  60. package/framework/standards/ai-agents/context-providers.md +188 -188
  61. package/framework/standards/ai-agents/mcp-server.md +250 -250
  62. package/framework/standards/ai-agents/mcp-tools.md +197 -197
  63. package/framework/standards/ai-agents/middleware-patterns.md +207 -207
  64. package/framework/standards/ai-agents/modalities-image-gen.md +173 -173
  65. package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -171
  66. package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -181
  67. package/framework/standards/ai-agents/modalities-vision.md +201 -201
  68. package/framework/standards/ai-agents/multi-agent-patterns.md +155 -155
  69. package/framework/standards/ai-agents/production.md +275 -275
  70. package/framework/standards/ai-agents/prompts-in-db.md +214 -214
  71. package/framework/standards/ai-agents/providers/model-registry.md +158 -158
  72. package/framework/standards/ai-agents/service-tier-flex.md +135 -135
  73. package/framework/standards/ai-agents/structured-output.md +249 -249
  74. package/framework/standards/ai-agents/sweet-spot.md +109 -109
  75. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +45 -3
  76. package/framework/templates/.idea/morph-templates.xml +92 -0
  77. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  78. package/framework/templates/IDE-SNIPPETS.md +262 -262
  79. package/framework/templates/REGISTRY.json +0 -62
  80. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  81. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  82. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  83. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  84. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  85. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  86. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  87. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  88. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  89. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  90. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  91. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  92. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  93. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  94. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  95. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  96. package/framework/templates/code/typescript/contracts.ts +167 -167
  97. package/framework/templates/docs/clarifications.md +253 -253
  98. package/framework/templates/docs/schema-analysis.md +119 -119
  99. package/framework/templates/docs/ui-components.md +52 -52
  100. package/framework/templates/docs/ui-design-system.md +76 -76
  101. package/framework/templates/docs/ui-flows.md +167 -167
  102. package/framework/templates/docs/ui-mockups.md +98 -98
  103. package/framework/templates/feature/decisions.md +122 -122
  104. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  105. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  106. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  107. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  108. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  109. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  110. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  111. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  112. package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -50
  113. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  114. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  115. package/framework/templates/infrastructure/github/README.md +56 -56
  116. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  117. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  118. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  119. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  120. package/framework/templates/integrations/asaas-client.cs +387 -387
  121. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  122. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  123. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  124. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  125. package/framework/templates/project/validate.js +120 -120
  126. package/framework/templates/state.template.json +17 -17
  127. package/framework/templates/ui/design-system.css +226 -226
  128. package/package.json +1 -1
  129. package/src/commands/dashboard/dashboard.js +53 -53
  130. package/src/commands/index.js +13 -13
  131. package/src/commands/mcp/mcp-setup.js +519 -519
  132. package/src/commands/project/checkpoint.js +209 -209
  133. package/src/commands/project/doctor.js +4 -2
  134. package/src/commands/project/init.js +46 -2
  135. package/src/commands/project/update.js +5 -3
  136. package/src/commands/project/worktree.js +1005 -46
  137. package/src/commands/state/advance.js +17 -2
  138. package/src/commands/state/approve.js +18 -1
  139. package/src/commands/state/create.js +158 -14
  140. package/src/commands/state/retype.js +126 -0
  141. package/src/commands/state/score.js +4 -0
  142. package/src/commands/tasks/dag.js +32 -0
  143. package/src/commands/validation/index.js +1 -0
  144. package/src/commands/validation/validate-feature.js +33 -5
  145. package/src/commands/validation/verify.js +190 -0
  146. package/src/core/state/phase-state-machine.js +197 -197
  147. package/src/core/state/state-manager.js +35 -10
  148. package/src/core/templates/template-renderer.js +477 -477
  149. package/src/core/work-types.js +79 -0
  150. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  151. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  152. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  153. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  154. package/src/dashboard/app/main.jsx +13 -13
  155. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  156. package/src/dashboard/app/pages/HookInspector.jsx +418 -418
  157. package/src/dashboard/app/pages/LiveState.jsx +11 -1
  158. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  159. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  160. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  161. package/src/dashboard/app/pages/Standards.jsx +332 -332
  162. package/src/dashboard/app/styles/index.css +47 -47
  163. package/src/dashboard/index.html +12 -12
  164. package/src/dashboard/package.json +24 -24
  165. package/src/dashboard/scanner/hooks-scanner.js +41 -41
  166. package/src/dashboard/scanner/phases-scanner.js +15 -15
  167. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  168. package/src/dashboard/scanner/rules-scanner.js +37 -37
  169. package/src/dashboard/scanner/skills-scanner.js +43 -43
  170. package/src/dashboard/scanner/squads-scanner.js +13 -13
  171. package/src/dashboard/scanner/standards-scanner.js +26 -26
  172. package/src/dashboard/scanner/state-scanner.js +42 -0
  173. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  174. package/src/dashboard/vite.config.js +24 -24
  175. package/src/lib/agents/metrics-aggregator.js +56 -56
  176. package/src/lib/agents/metrics-calculator.js +125 -125
  177. package/src/lib/agents/output-collector.js +73 -73
  178. package/src/lib/detectors/claude-config-detector.js +152 -152
  179. package/src/lib/detectors/design-system-detector.js +231 -231
  180. package/src/lib/detectors/index.js +217 -217
  181. package/src/lib/generators/architecture-scanner.js +639 -639
  182. package/src/lib/generators/design-system-generator.js +508 -508
  183. package/src/lib/generators/index.js +13 -13
  184. package/src/lib/generators/settings-generator.js +209 -209
  185. package/src/lib/hooks/hook-executor.js +167 -167
  186. package/src/lib/installers/mcp-installer.js +719 -719
  187. package/src/lib/installers/memory-installer.js +1 -1
  188. package/src/lib/risk-detector.js +16 -0
  189. package/src/lib/tasks/dispatch-planner.js +122 -4
  190. package/src/lib/tasks/mermaid-renderer.js +132 -0
  191. package/src/lib/tasks/task-classifier.js +30 -30
  192. package/src/lib/tasks/task-parser.js +209 -209
  193. package/src/lib/tasks/test-runner.js +27 -6
  194. package/src/lib/validators/architecture/architecture-validator.js +259 -259
  195. package/src/lib/validators/checkpoint-persistence.js +39 -39
  196. package/src/lib/validators/contracts/contract-compliance-validator.js +274 -274
  197. package/src/lib/validators/css/css-validator.js +340 -340
  198. package/src/lib/validators/packages/package-validator.js +347 -347
  199. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  200. package/src/lib/validators/shared/issue-counter.js +14 -14
  201. package/src/lib/validators/validation-runner.js +349 -349
  202. package/src/lib/validators/verify-runner.js +322 -0
  203. package/src/lib/work-type-detector.js +143 -0
  204. package/src/scripts/setup-infra.js +32 -5
  205. package/src/utils/agent-id-mapper.js +11 -11
  206. package/src/utils/agents-installer.js +10 -6
  207. package/src/utils/claude-settings-manager.js +300 -300
  208. package/src/utils/file-copier.js +43 -0
  209. package/src/utils/hooks-installer.js +41 -11
  210. package/src/utils/skills-installer.js +174 -174
  211. package/framework/templates/meta-prompts/parallel-workers/parallel-coordinator.md +0 -113
  212. package/framework/templates/meta-prompts/parallel-workers/parallel-worker.md +0 -80
  213. package/src/lib/tasks/wave-resolver.js +0 -353
@@ -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)*
@@ -2,7 +2,7 @@
2
2
 
3
3
  > **Scope:** universal
4
4
  > **Layer:** 2 (on keyword)
5
- > **Keywords:** neon auth, better auth, authentication, nextjs, user management, session, JWT, trusted origins, EdDSA, Ed25519, BouncyCastle, signature validator, jwks, middleware runtime, api prefix
5
+ > **Keywords:** neon auth, better auth, authentication, nextjs, user management, session, JWT, trusted origins, EdDSA, Ed25519, BouncyCastle, signature validator, jwks, middleware runtime, api prefix, data api, postgrest, bypass backend, direct database access
6
6
  > **Load When:** neon auth or authentication keywords detected
7
7
 
8
8
  **Verified against:** Neon Auth — @neondatabase/auth + @neondatabase/auth-ui (Better Auth). Last-verified: 2026-07-13.
@@ -37,13 +37,42 @@ Neon Auth provides:
37
37
  1. **Database-First**: Auth data lives in `neon_auth` schema — no external service to sync
38
38
  2. **Branch Isolation**: Each Neon branch gets isolated users, sessions, and auth config
39
39
  3. **Server-First**: Use server-side auth checks with `auth.getSession()`
40
- 4. **JWT for RLS**: Tokens contain `sub` (user ID), `email`, `role: "authenticated"` for database policies
40
+ 4. **JWT for RLS**: Tokens contain `sub` (user ID), `email`, `role: "authenticated"` for database
41
+ policies — used only inside the justified Data API exception path, not by default (see
42
+ [Data Access Boundary](#data-access-boundary--backend-first-by-default))
41
43
  5. **No OIDC discovery**: Neon Auth does **not** expose `/.well-known/openid-configuration` — only the
42
44
  raw `/.well-known/jwks.json`. Never assume `options.Authority` alone gives you working discovery on
43
45
  the .NET side (see [.NET Backend Integration](#net-backend-integration)).
44
46
 
45
47
  ---
46
48
 
49
+ ## Data Access Boundary — Backend-First by Default
50
+
51
+ The Next.js frontend has exactly one inherent reason to talk to Neon directly: Neon Auth's own
52
+ client SDK (`authClient`, `useSession`, `auth.getSession()`) — that's Better Auth's session/JWT
53
+ mechanism, not a business-data call. Every read or write of application/business data goes through
54
+ the .NET backend instead, either as a server-direct fetch or a client-via-proxy call (see
55
+ [API Prefix Convention](#api-prefix-convention--server-direct-vs-client-proxy) for the mechanics).
56
+
57
+ **Rule:** the frontend does not query Neon directly for business data — not via a Neon Data API
58
+ (REST-over-Postgres) endpoint, not via a browser-held connection string, not via any other bypass
59
+ of the .NET backend. The .NET backend is the sole gateway to business data, for the same reason
60
+ `architecture/vertical-slice/vertical-slice.md` makes the backend the sole gateway to the database
61
+ in general: handler-level authorization, no business rules duplicated on two sides of the wire, one
62
+ place to audit.
63
+
64
+ **The exception process:** a feature that genuinely needs the frontend to reach Neon directly for
65
+ business data (for example, via the Neon MCP `provision_neon_data_api` tool) is an exception, not a
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
68
+ available is not, by itself, justification.
69
+
70
+ See [Row-Level Security Integration](#row-level-security-integration) for how to constrain a
71
+ Data-API-exception session once one exists — those RLS policies are not a signal that the frontend
72
+ should have direct access by default.
73
+
74
+ ---
75
+
47
76
  ## Installation & Setup
48
77
 
49
78
  ### Install Packages
@@ -299,6 +328,10 @@ export async function GET() {
299
328
 
300
329
  ## API Prefix Convention — Server-Direct vs Client-Proxy
301
330
 
331
+ > Assumes the [Data Access Boundary](#data-access-boundary--backend-first-by-default) rule: all
332
+ > business data routes through the backend by default. This section covers only the *mechanics* of
333
+ > doing so from each render context.
334
+
302
335
  A Next.js app talking to a Neon-Auth-protected .NET backend fetches from **two different places**
303
336
  depending on where the code runs:
304
337
 
@@ -616,7 +649,13 @@ app.Run();
616
649
 
617
650
  ## Row-Level Security Integration
618
651
 
619
- Neon Auth JWT tokens are automatically validated by the Data API. The `auth.user_id()` function (via `pg_session_jwt` extension) exposes the authenticated user ID from the JWT `sub` claim.
652
+ > Defense-in-depth on a JWT-scoped Postgres session — the Data API exception above, not a directive
653
+ > to call Neon from the browser for business data by default. See
654
+ > [Data Access Boundary](#data-access-boundary--backend-first-by-default).
655
+
656
+ When a Data API endpoint is provisioned (the justified exception above), Neon Auth JWT tokens are
657
+ automatically validated by it. The `auth.user_id()` function (via `pg_session_jwt` extension)
658
+ exposes the authenticated user ID from the JWT `sub` claim to any RLS policy on that session.
620
659
 
621
660
  ```sql
622
661
  -- Enable RLS
@@ -673,6 +712,9 @@ Each Neon branch gets its own isolated auth data. This means:
673
712
  service silently becomes a phantom `[FromBody]` parameter and crashes the app at startup
674
713
  10. **On .NET, never rely on `options.Authority` alone** — Neon Auth has no OIDC discovery document;
675
714
  fetch and cache the JWKS yourself and verify EdDSA signatures manually (BouncyCastle)
715
+ 11. **Route business data through the .NET backend by default** — the frontend's only inherent
716
+ reason to talk to Neon directly is its own auth/session SDK; a direct Data API call needs a
717
+ `decisions.md` ADR (see [Data Access Boundary](#data-access-boundary--backend-first-by-default))
676
718
 
677
719
  ---
678
720
 
@@ -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>
@@ -0,0 +1,186 @@
1
+ {
2
+ "MORPH Template - PascalCase": {
3
+ "prefix": "morph-pascal",
4
+ "body": ["{{pascalCase ${1:FEATURE_NAME}}}"],
5
+ "description": "Convert to PascalCase (UserAuth)"
6
+ },
7
+ "MORPH Template - camelCase": {
8
+ "prefix": "morph-camel",
9
+ "body": ["{{camelCase ${1:FEATURE_NAME}}}"],
10
+ "description": "Convert to camelCase (userAuth)"
11
+ },
12
+ "MORPH Template - snake_case": {
13
+ "prefix": "morph-snake",
14
+ "body": ["{{snakeCase ${1:FEATURE_NAME}}}"],
15
+ "description": "Convert to snake_case (user_auth)"
16
+ },
17
+ "MORPH Template - Title Case": {
18
+ "prefix": "morph-title",
19
+ "body": ["{{titleCase ${1:FEATURE_NAME}}}"],
20
+ "description": "Convert to Title Case (User Auth)"
21
+ },
22
+ "MORPH Template - kebab-case": {
23
+ "prefix": "morph-kebab",
24
+ "body": ["{{kebabCase ${1:FEATURE_NAME}}}"],
25
+ "description": "Convert to kebab-case (user-auth)"
26
+ },
27
+ "MORPH Template - UPPER_SNAKE_CASE": {
28
+ "prefix": "morph-upper-snake",
29
+ "body": ["{{upperSnakeCase ${1:FEATURE_NAME}}}"],
30
+ "description": "Convert to UPPER_SNAKE_CASE (USER_AUTH)"
31
+ },
32
+ "MORPH Template - Conditional If": {
33
+ "prefix": "morph-if",
34
+ "body": [
35
+ "{{#if ${1:condition}}}",
36
+ " $2",
37
+ "{{/if}}"
38
+ ],
39
+ "description": "Handlebars if block"
40
+ },
41
+ "MORPH Template - Conditional If-Else": {
42
+ "prefix": "morph-if-else",
43
+ "body": [
44
+ "{{#if ${1:condition}}}",
45
+ " $2",
46
+ "{{else}}",
47
+ " $3",
48
+ "{{/if}}"
49
+ ],
50
+ "description": "Handlebars if-else block"
51
+ },
52
+ "MORPH Template - Each Loop": {
53
+ "prefix": "morph-each",
54
+ "body": [
55
+ "{{#each ${1:items}}}",
56
+ " {{this.$2}}",
57
+ "{{/each}}"
58
+ ],
59
+ "description": "Handlebars each loop"
60
+ },
61
+ "MORPH Template - Unless": {
62
+ "prefix": "morph-unless",
63
+ "body": [
64
+ "{{#unless ${1:condition}}}",
65
+ " $2",
66
+ "{{/unless}}"
67
+ ],
68
+ "description": "Handlebars unless block"
69
+ },
70
+ "MORPH Template - .NET Service Class": {
71
+ "prefix": "morph-service",
72
+ "body": [
73
+ "namespace {{NAMESPACE}}.Application.Features.{{pascalCase FEATURE_NAME}}.Services;",
74
+ "",
75
+ "public class {{pascalCase FEATURE_NAME}}Service(",
76
+ " I{{pascalCase FEATURE_NAME}}Repository repository,",
77
+ " ILogger<{{pascalCase FEATURE_NAME}}Service> logger) : I{{pascalCase FEATURE_NAME}}Service",
78
+ "{",
79
+ " public async Task<{{pascalCase FEATURE_NAME}}Dto?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
80
+ " {",
81
+ " $1",
82
+ " }",
83
+ "}"
84
+ ],
85
+ "description": "MORPH .NET Service class template"
86
+ },
87
+ "MORPH Template - .NET Repository Class": {
88
+ "prefix": "morph-repository",
89
+ "body": [
90
+ "public class {{pascalCase FEATURE_NAME}}Repository(AppDbContext context) : I{{pascalCase FEATURE_NAME}}Repository",
91
+ "{",
92
+ " private readonly DbSet<{{pascalCase FEATURE_NAME}}> _dbSet = context.Set<{{pascalCase FEATURE_NAME}}>();",
93
+ "",
94
+ " public async Task<{{pascalCase FEATURE_NAME}}?> GetByIdAsync(int id, CancellationToken cancellationToken = default)",
95
+ " {",
96
+ " $1",
97
+ " }",
98
+ "}"
99
+ ],
100
+ "description": "MORPH .NET Repository class template"
101
+ },
102
+ "MORPH Template - Blazor Component": {
103
+ "prefix": "morph-component",
104
+ "body": [
105
+ "@page \"/{{kebabCase FEATURE_NAME}}\"",
106
+ "@inject I{{pascalCase FEATURE_NAME}}Service {{pascalCase FEATURE_NAME}}Service",
107
+ "@inject ILogger<{{pascalCase FEATURE_NAME}}Page> Logger",
108
+ "",
109
+ "<PageTitle>{{pascalCase FEATURE_NAME}}</PageTitle>",
110
+ "",
111
+ "<div class=\"container-fluid\">",
112
+ " $1",
113
+ "</div>",
114
+ "",
115
+ "@code {",
116
+ " $2",
117
+ "}"
118
+ ],
119
+ "description": "MORPH Blazor component template"
120
+ },
121
+ "MORPH Template - EF Core Migration": {
122
+ "prefix": "morph-migration",
123
+ "body": [
124
+ "public partial class Add{{pascalCase FEATURE_NAME}} : Migration",
125
+ "{",
126
+ " protected override void Up(MigrationBuilder migrationBuilder)",
127
+ " {",
128
+ " migrationBuilder.CreateTable(",
129
+ " name: \"{{pascalCase FEATURE_NAME}}s\",",
130
+ " columns: table => new",
131
+ " {",
132
+ " Id = table.Column<int>(type: \"int\", nullable: false)",
133
+ " .Annotation(\"SqlServer:Identity\", \"1, 1\"),",
134
+ " $1",
135
+ " },",
136
+ " constraints: table =>",
137
+ " {",
138
+ " table.PrimaryKey(\"PK_{{pascalCase FEATURE_NAME}}s\", x => x.Id);",
139
+ " });",
140
+ " }",
141
+ "",
142
+ " protected override void Down(MigrationBuilder migrationBuilder)",
143
+ " {",
144
+ " migrationBuilder.DropTable(name: \"{{pascalCase FEATURE_NAME}}s\");",
145
+ " }",
146
+ "}"
147
+ ],
148
+ "description": "MORPH EF Core migration template"
149
+ },
150
+ "MORPH Template - Hangfire Job": {
151
+ "prefix": "morph-job",
152
+ "body": [
153
+ "public class {{pascalCase FEATURE_NAME}}ProcessorJob(",
154
+ " I{{pascalCase FEATURE_NAME}}Service service,",
155
+ " ILogger<{{pascalCase FEATURE_NAME}}ProcessorJob> logger) : I{{pascalCase FEATURE_NAME}}ProcessorJob",
156
+ "{",
157
+ " [AutomaticRetry(Attempts = 3, DelaysInSeconds = new[] { 60, 300, 900 })]",
158
+ " [Queue(\"default\")]",
159
+ " [JobDisplayName(\"{{pascalCase FEATURE_NAME}} Processing - ID: {0}\")]",
160
+ " public async Task ExecuteAsync(int id, CancellationToken cancellationToken)",
161
+ " {",
162
+ " $1",
163
+ " }",
164
+ "}"
165
+ ],
166
+ "description": "MORPH Hangfire job template"
167
+ },
168
+ "MORPH Template - TypeScript Contract": {
169
+ "prefix": "morph-ts-contract",
170
+ "body": [
171
+ "export interface {{pascalCase FEATURE_NAME}} {",
172
+ " id: number;",
173
+ " $1",
174
+ "}",
175
+ "",
176
+ "export interface Create{{pascalCase FEATURE_NAME}}Request {",
177
+ " $2",
178
+ "}",
179
+ "",
180
+ "export interface Update{{pascalCase FEATURE_NAME}}Request {",
181
+ " $3",
182
+ "}"
183
+ ],
184
+ "description": "MORPH TypeScript contract template"
185
+ }
186
+ }