@polymorphism-tech/morph-spec 8.38.0 → 8.41.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 (242) hide show
  1. package/CLAUDE.md +20 -9
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +60 -43
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +1 -1
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +4 -3
  8. package/framework/MORPH.md +10 -9
  9. package/framework/agents.json +43 -26
  10. package/framework/docs/CLI.md +7 -5
  11. package/framework/evals/rubrics/code-quality.md +4 -4
  12. package/framework/evals/rubrics/condensed.md +2 -2
  13. package/framework/hooks/README.md +1 -1
  14. package/framework/hooks/claude-code/core/telemetry-log.js +39 -8
  15. package/framework/hooks/claude-code/core/worktree-create.js +22 -6
  16. package/framework/hooks/shared/git-paths.js +38 -5
  17. package/framework/mcp/registry.json +58 -66
  18. package/framework/rules/infrastructure-standards.md +0 -1
  19. package/framework/rules/morph-workflow.md +9 -4
  20. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  21. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  22. package/framework/skills/core/morph-apply/SKILL.md +59 -25
  23. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  24. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  25. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  26. package/framework/skills/core/morph-implement/SKILL.md +48 -24
  27. package/framework/skills/core/morph-media/SKILL.md +2 -2
  28. package/framework/skills/core/morph-plan/SKILL.md +10 -3
  29. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  30. package/framework/skills/core/morph-review/SKILL.md +1 -1
  31. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  32. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  33. package/framework/skills/core/morph-uiux/SKILL.md +7 -6
  34. package/framework/standards/PROVENANCE.json +4 -4
  35. package/framework/standards/STANDARDS.json +9 -9
  36. package/framework/standards/ai-agents/context-providers.md +16 -26
  37. package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
  38. package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
  39. package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
  40. package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
  41. package/framework/standards/ai-agents/modalities-vision.md +1 -18
  42. package/framework/standards/ai-agents/prompt-sources.md +0 -4
  43. package/framework/standards/architecture/code-comments.md +52 -16
  44. package/framework/standards/backend/dotnet/testing.md +4 -2
  45. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
  46. package/framework/standards/frontend/design-system/ai-image-generation.md +42 -28
  47. package/framework/standards/frontend/design-system/ai-video-generation.md +24 -9
  48. package/framework/standards/frontend/nextjs/testing.md +4 -2
  49. package/framework/standards/integration/mcp/mcp-tools.md +64 -126
  50. package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
  51. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
  52. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
  53. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
  54. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
  55. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
  56. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
  57. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
  58. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
  59. package/framework/templates/code/dotnet/database/migration.cs +49 -83
  60. package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
  61. package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
  62. package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
  63. package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
  64. package/framework/templates/code/dotnet/jobs/job.cs +2 -97
  65. package/framework/templates/code/dotnet/test.cs +2 -87
  66. package/framework/templates/code/typescript/contracts.ts +140 -167
  67. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
  68. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
  69. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
  70. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
  71. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
  72. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
  73. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
  74. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
  75. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
  76. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
  77. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
  78. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
  79. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
  80. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
  81. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
  82. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
  83. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
  84. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
  85. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
  86. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
  87. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
  88. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
  89. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
  90. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
  91. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
  92. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
  93. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
  94. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
  95. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
  96. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
  97. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
  98. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
  99. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
  100. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
  101. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
  102. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
  103. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
  104. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
  105. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
  106. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
  107. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
  108. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
  109. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
  110. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
  111. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
  112. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
  113. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
  114. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
  115. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
  116. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
  117. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
  118. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
  119. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
  120. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
  121. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
  122. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
  123. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
  124. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
  127. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
  128. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
  129. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
  130. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
  131. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
  132. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
  134. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
  135. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
  136. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
  137. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
  138. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
  139. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
  140. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
  141. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
  142. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
  143. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
  144. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
  145. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
  146. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
  147. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
  148. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
  149. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
  150. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
  151. package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
  152. package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
  153. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
  154. package/framework/templates/integrations/asaas-client.cs +332 -387
  155. package/framework/templates/integrations/asaas-webhook.cs +290 -351
  156. package/framework/templates/integrations/neon-auth-config.cs +9 -105
  157. package/framework/templates/project/validate.js +97 -120
  158. package/package.json +2 -2
  159. package/src/commands/dashboard/dashboard.js +80 -53
  160. package/src/commands/mcp/mcp-setup.js +37 -9
  161. package/src/commands/project/doctor.js +12 -6
  162. package/src/commands/project/init.js +6 -3
  163. package/src/commands/project/install-plugin-cmd.js +1 -1
  164. package/src/commands/project/update.js +5 -0
  165. package/src/commands/project/worktree.js +60 -4
  166. package/src/commands/state/gate-check.js +5 -0
  167. package/src/commands/tasks/dag.js +27 -4
  168. package/src/commands/validation/verify.js +22 -38
  169. package/src/dashboard/app/App.jsx +80 -16
  170. package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
  171. package/src/dashboard/app/components/CommandPalette.jsx +155 -0
  172. package/src/dashboard/app/components/DetailPanel.jsx +47 -11
  173. package/src/dashboard/app/components/EntityLink.jsx +55 -0
  174. package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
  175. package/src/dashboard/app/components/FlowGraph.jsx +19 -17
  176. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
  177. package/src/dashboard/app/components/Markdown.jsx +47 -0
  178. package/src/dashboard/app/components/PhaseRail.jsx +111 -0
  179. package/src/dashboard/app/components/SearchFilter.jsx +40 -22
  180. package/src/dashboard/app/components/Sidebar.jsx +146 -37
  181. package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
  182. package/src/dashboard/app/hooks/usePref.js +38 -0
  183. package/src/dashboard/app/lib/format.js +69 -0
  184. package/src/dashboard/app/lib/phases.js +56 -0
  185. package/src/dashboard/app/lib/project.js +50 -0
  186. package/src/dashboard/app/pages/Activity.jsx +24 -0
  187. package/src/dashboard/app/pages/AgentMap.jsx +371 -370
  188. package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
  189. package/src/dashboard/app/pages/Features.jsx +400 -0
  190. package/src/dashboard/app/pages/Guide.jsx +146 -0
  191. package/src/dashboard/app/pages/HookInspector.jsx +357 -322
  192. package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
  193. package/src/dashboard/app/pages/Portfolio.jsx +156 -0
  194. package/src/dashboard/app/pages/Rubrics.jsx +101 -0
  195. package/src/dashboard/app/pages/RulesMap.jsx +300 -273
  196. package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
  197. package/src/dashboard/app/pages/Standards.jsx +407 -332
  198. package/src/dashboard/app/styles/index.css +187 -47
  199. package/src/dashboard/index.html +14 -12
  200. package/src/dashboard/package.json +32 -24
  201. package/src/dashboard/scanner/activity-scanner.js +351 -0
  202. package/src/dashboard/scanner/agents-scanner.js +59 -1
  203. package/src/dashboard/scanner/docs-scanner.js +59 -0
  204. package/src/dashboard/scanner/feature-status.js +10 -0
  205. package/src/dashboard/scanner/features-scanner.js +360 -0
  206. package/src/dashboard/scanner/index.js +14 -7
  207. package/src/dashboard/scanner/phases-scanner.js +14 -15
  208. package/src/dashboard/scanner/portfolio.js +153 -0
  209. package/src/dashboard/scanner/project-api.js +195 -0
  210. package/src/dashboard/scanner/rules-scanner.js +42 -37
  211. package/src/dashboard/scanner/skills-scanner.js +105 -43
  212. package/src/dashboard/scanner/standards-scanner.js +2 -0
  213. package/src/dashboard/vite-plugin-morph-data.js +134 -17
  214. package/src/lib/detectors/claude-config-detector.js +2 -7
  215. package/src/lib/detectors/index.js +5 -1
  216. package/src/lib/evaluator-report.js +22 -0
  217. package/src/lib/generators/settings-generator.js +208 -209
  218. package/src/lib/installers/mcp-installer.js +724 -719
  219. package/src/lib/installers/memory-installer.js +1 -2
  220. package/src/lib/projects/registry.js +154 -0
  221. package/src/lib/risk-detector.js +13 -0
  222. package/src/lib/tasks/tasks-json-parser.js +21 -0
  223. package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
  224. package/src/lib/worktree/env-allocator.js +9 -3
  225. package/src/scripts/install-plugin.js +1 -1
  226. package/src/scripts/setup-infra.js +2 -4
  227. package/src/utils/agents-installer.js +108 -13
  228. package/src/utils/banner.js +1 -1
  229. package/src/commands/index.js +0 -13
  230. package/src/commands/project/checkpoint.js +0 -209
  231. package/src/core/state/phase-state-machine.js +0 -197
  232. package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
  233. package/src/dashboard/app/pages/LiveState.jsx +0 -206
  234. package/src/dashboard/scanner/squads-scanner.js +0 -13
  235. package/src/dashboard/scanner/state-scanner.js +0 -85
  236. package/src/lib/generators/index.js +0 -10
  237. package/src/lib/hooks/hook-executor.js +0 -167
  238. package/src/lib/validators/checkpoint-persistence.js +0 -39
  239. package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
  240. package/src/lib/validators/shared/index.js +0 -12
  241. package/src/lib/validators/shared/issue-counter.js +0 -14
  242. package/src/lib/validators/shared/result-formatter.js +0 -124
@@ -1,8 +1,3 @@
1
- // ============================================================
2
- // TEST TEMPLATE (VSA Pattern — Handler + Validator)
3
- // Generated by MORPH Framework
4
- // ============================================================
5
-
6
1
  using FluentAssertions;
7
2
  using FluentValidation;
8
3
  using FluentValidation.Results;
@@ -14,15 +9,6 @@ using Xunit;
14
9
 
15
10
  namespace {{pascalCase PROJECT_NAME}}.Tests.Unit.Features.{{pascalCase FEATURE_NAME}};
16
11
 
17
- // ============================================================
18
- // Handler Tests
19
- // ============================================================
20
-
21
- /// <summary>
22
- /// Unit tests for {{pascalCase FEATURE_NAME}} handlers.
23
- /// Tests handlers directly against a real ApplicationDbContext backed by
24
- /// in-memory SQLite (no mocks — DbSet is the repository, DbContext is the unit of work).
25
- /// </summary>
26
12
  public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
27
13
  {
28
14
  private readonly SqliteConnection _connection;
@@ -30,6 +16,7 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
30
16
 
31
17
  public {{pascalCase FEATURE_NAME}}HandlerTests()
32
18
  {
19
+ // o banco :memory: some quando a conexão fecha, então ela vive o teste inteiro
33
20
  _connection = new SqliteConnection("DataSource=:memory:");
34
21
  _connection.Open();
35
22
 
@@ -47,19 +34,14 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
47
34
  _connection.Dispose();
48
35
  }
49
36
 
50
- #region Create Handler Tests
51
-
52
37
  [Fact]
53
38
  public async Task Create_HandleAsync_WithValidCommand_ReturnsSuccessResult()
54
39
  {
55
- // Arrange
56
40
  var command = new Create{{pascalCase FEATURE_NAME}}Request("Test {{pascalCase FEATURE_NAME}}");
57
41
  var handler = new Create{{pascalCase FEATURE_NAME}}Handler(_db);
58
42
 
59
- // Act
60
43
  var result = await handler.HandleAsync(command, CancellationToken.None);
61
44
 
62
- // Assert
63
45
  result.IsSuccess.Should().BeTrue();
64
46
  result.Value.Should().NotBeNull();
65
47
 
@@ -67,14 +49,9 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
67
49
  persisted.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
68
50
  }
69
51
 
70
- #endregion
71
-
72
- #region GetById Handler Tests
73
-
74
52
  [Fact]
75
53
  public async Task GetById_HandleAsync_WithValidId_ReturnsSuccessResult()
76
54
  {
77
- // Arrange
78
55
  var entity = CreateTestEntity("Test {{pascalCase FEATURE_NAME}}");
79
56
  _db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
80
57
  await _db.SaveChangesAsync();
@@ -82,10 +59,8 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
82
59
  var query = new Get{{pascalCase FEATURE_NAME}}ByIdRequest(entity.Id);
83
60
  var handler = new Get{{pascalCase FEATURE_NAME}}ByIdHandler(_db);
84
61
 
85
- // Act
86
62
  var result = await handler.HandleAsync(query, CancellationToken.None);
87
63
 
88
- // Assert
89
64
  result.IsSuccess.Should().BeTrue();
90
65
  result.Value.Should().NotBeNull();
91
66
  result.Value!.Name.Should().Be("Test {{pascalCase FEATURE_NAME}}");
@@ -94,25 +69,17 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
94
69
  [Fact]
95
70
  public async Task GetById_HandleAsync_WithInvalidId_ReturnsFailureResult()
96
71
  {
97
- // Arrange
98
72
  var query = new Get{{pascalCase FEATURE_NAME}}ByIdRequest(Guid.CreateVersion7());
99
73
  var handler = new Get{{pascalCase FEATURE_NAME}}ByIdHandler(_db);
100
74
 
101
- // Act
102
75
  var result = await handler.HandleAsync(query, CancellationToken.None);
103
76
 
104
- // Assert
105
77
  result.IsSuccess.Should().BeFalse();
106
78
  }
107
79
 
108
- #endregion
109
-
110
- #region Update Handler Tests
111
-
112
80
  [Fact]
113
81
  public async Task Update_HandleAsync_WithValidCommand_ReturnsSuccessResult()
114
82
  {
115
- // Arrange
116
83
  var entity = CreateTestEntity("Original Name");
117
84
  _db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
118
85
  await _db.SaveChangesAsync();
@@ -120,10 +87,8 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
120
87
  var command = new Update{{pascalCase FEATURE_NAME}}Request(entity.Id, "Updated Name");
121
88
  var handler = new Update{{pascalCase FEATURE_NAME}}Handler(_db);
122
89
 
123
- // Act
124
90
  var result = await handler.HandleAsync(command, CancellationToken.None);
125
91
 
126
- // Assert
127
92
  result.IsSuccess.Should().BeTrue();
128
93
 
129
94
  var persisted = await _db.Set<{{pascalCase FEATURE_NAME}}>().SingleAsync(x => x.Id == entity.Id);
@@ -133,25 +98,17 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
133
98
  [Fact]
134
99
  public async Task Update_HandleAsync_WithNonExistentId_ReturnsFailureResult()
135
100
  {
136
- // Arrange
137
101
  var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7(), "Updated");
138
102
  var handler = new Update{{pascalCase FEATURE_NAME}}Handler(_db);
139
103
 
140
- // Act
141
104
  var result = await handler.HandleAsync(command, CancellationToken.None);
142
105
 
143
- // Assert
144
106
  result.IsSuccess.Should().BeFalse();
145
107
  }
146
108
 
147
- #endregion
148
-
149
- #region Delete Handler Tests
150
-
151
109
  [Fact]
152
110
  public async Task Delete_HandleAsync_WithValidId_ReturnsSuccessResult()
153
111
  {
154
- // Arrange
155
112
  var entity = CreateTestEntity("To Delete");
156
113
  _db.Set<{{pascalCase FEATURE_NAME}}>().Add(entity);
157
114
  await _db.SaveChangesAsync();
@@ -159,10 +116,8 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
159
116
  var command = new Delete{{pascalCase FEATURE_NAME}}Request(entity.Id);
160
117
  var handler = new Delete{{pascalCase FEATURE_NAME}}Handler(_db);
161
118
 
162
- // Act
163
119
  var result = await handler.HandleAsync(command, CancellationToken.None);
164
120
 
165
- // Assert
166
121
  result.IsSuccess.Should().BeTrue();
167
122
 
168
123
  (await _db.Set<{{pascalCase FEATURE_NAME}}>().AnyAsync(x => x.Id == entity.Id)).Should().BeFalse();
@@ -171,57 +126,35 @@ public class {{pascalCase FEATURE_NAME}}HandlerTests : IDisposable
171
126
  [Fact]
172
127
  public async Task Delete_HandleAsync_WithNonExistentId_ReturnsFailureResult()
173
128
  {
174
- // Arrange
175
129
  var command = new Delete{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7());
176
130
  var handler = new Delete{{pascalCase FEATURE_NAME}}Handler(_db);
177
131
 
178
- // Act
179
132
  var result = await handler.HandleAsync(command, CancellationToken.None);
180
133
 
181
- // Assert
182
134
  result.IsSuccess.Should().BeFalse();
183
135
  }
184
136
 
185
- #endregion
186
-
187
- #region Helper Methods
188
-
189
137
  private static {{pascalCase FEATURE_NAME}} CreateTestEntity(string name)
190
138
  {
191
139
  return new {{pascalCase FEATURE_NAME}}
192
140
  {
193
141
  Id = Guid.CreateVersion7(),
194
142
  Name = name,
195
- // TODO: preencher demais campos obrigatórios da entidade
143
+ // TODO: demais campos obrigatórios da entidade
196
144
  };
197
145
  }
198
-
199
- #endregion
200
146
  }
201
147
 
202
- // ============================================================
203
- // Validator Tests
204
- // ============================================================
205
-
206
- /// <summary>
207
- /// Unit tests for {{pascalCase FEATURE_NAME}} validators.
208
- /// Tests FluentValidation rules using ValidateAsync.
209
- /// </summary>
210
148
  public class {{pascalCase FEATURE_NAME}}ValidatorTests
211
149
  {
212
- #region Create Validator Tests
213
-
214
150
  [Fact]
215
151
  public async Task CreateValidator_WithValidCommand_PassesValidation()
216
152
  {
217
- // Arrange
218
153
  var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
219
154
  var command = new Create{{pascalCase FEATURE_NAME}}Request("Valid Name");
220
155
 
221
- // Act
222
156
  ValidationResult result = await validator.ValidateAsync(command);
223
157
 
224
- // Assert
225
158
  result.IsValid.Should().BeTrue();
226
159
  result.Errors.Should().BeEmpty();
227
160
  }
@@ -232,14 +165,11 @@ public class {{pascalCase FEATURE_NAME}}ValidatorTests
232
165
  [InlineData(null)]
233
166
  public async Task CreateValidator_WithInvalidName_FailsValidation(string? name)
234
167
  {
235
- // Arrange
236
168
  var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
237
169
  var command = new Create{{pascalCase FEATURE_NAME}}Request(name!);
238
170
 
239
- // Act
240
171
  ValidationResult result = await validator.ValidateAsync(command);
241
172
 
242
- // Assert
243
173
  result.IsValid.Should().BeFalse();
244
174
  result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}Request.Name));
245
175
  }
@@ -247,50 +177,35 @@ public class {{pascalCase FEATURE_NAME}}ValidatorTests
247
177
  [Fact]
248
178
  public async Task CreateValidator_WithNameExceedingMaxLength_FailsValidation()
249
179
  {
250
- // Arrange
251
180
  var validator = new Create{{pascalCase FEATURE_NAME}}Validator();
252
181
  var command = new Create{{pascalCase FEATURE_NAME}}Request(new string('A', 201));
253
182
 
254
- // Act
255
183
  ValidationResult result = await validator.ValidateAsync(command);
256
184
 
257
- // Assert
258
185
  result.IsValid.Should().BeFalse();
259
186
  result.Errors.Should().Contain(e => e.PropertyName == nameof(Create{{pascalCase FEATURE_NAME}}Request.Name));
260
187
  }
261
188
 
262
- #endregion
263
-
264
- #region Update Validator Tests
265
-
266
189
  [Fact]
267
190
  public async Task UpdateValidator_WithValidCommand_PassesValidation()
268
191
  {
269
- // Arrange
270
192
  var validator = new Update{{pascalCase FEATURE_NAME}}Validator();
271
193
  var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.CreateVersion7(), "Updated Name");
272
194
 
273
- // Act
274
195
  ValidationResult result = await validator.ValidateAsync(command);
275
196
 
276
- // Assert
277
197
  result.IsValid.Should().BeTrue();
278
198
  }
279
199
 
280
200
  [Fact]
281
201
  public async Task UpdateValidator_WithInvalidId_FailsValidation()
282
202
  {
283
- // Arrange
284
203
  var validator = new Update{{pascalCase FEATURE_NAME}}Validator();
285
204
  var command = new Update{{pascalCase FEATURE_NAME}}Request(Guid.Empty, "Updated Name");
286
205
 
287
- // Act
288
206
  ValidationResult result = await validator.ValidateAsync(command);
289
207
 
290
- // Assert
291
208
  result.IsValid.Should().BeFalse();
292
209
  result.Errors.Should().Contain(e => e.PropertyName == nameof(Update{{pascalCase FEATURE_NAME}}Request.Id));
293
210
  }
294
-
295
- #endregion
296
211
  }
@@ -1,167 +1,140 @@
1
- // ============================================================
2
- // CONTRACTS: {{titleCase FEATURE_NAME}}
3
- // Stack: Next.js + Neon
4
- // Generated by MORPH Framework
5
- // Date: {{DATE}}
6
- // ============================================================
7
-
8
- // ----- Neon Config -----
9
-
10
- export interface NeonConfig {
11
- connectionString: string;
12
- }
13
-
14
- // ----- API Client Types -----
15
-
16
- export interface ApiClientConfig {
17
- baseUrl: string;
18
- getAccessToken: () => Promise<string | null>;
19
- }
20
-
21
- export interface ApiError {
22
- message: string;
23
- code: string;
24
- statusCode: number;
25
- details?: Record<string, string[]>;
26
- }
27
-
28
- export type ApiResult<T> =
29
- | { success: true; data: T }
30
- | { success: false; error: ApiError };
31
-
32
- // ----- Pagination -----
33
-
34
- export interface PaginationQuery {
35
- page?: number;
36
- pageSize?: number;
37
- }
38
-
39
- export interface PagedResult<T> {
40
- items: T[];
41
- totalCount: number;
42
- page: number;
43
- pageSize: number;
44
- totalPages: number;
45
- hasNext: boolean;
46
- hasPrevious: boolean;
47
- }
48
-
49
- // ----- Database Client Type Aliases -----
50
- // Generated by your ORM (Drizzle or Prisma) from your Neon PostgreSQL schema
51
-
52
- export type Tables<T extends keyof Database["public"]["Tables"]> =
53
- Database["public"]["Tables"][T]["Row"];
54
-
55
- export type Insertable<T extends keyof Database["public"]["Tables"]> =
56
- Database["public"]["Tables"][T]["Insert"];
57
-
58
- export type Updatable<T extends keyof Database["public"]["Tables"]> =
59
- Database["public"]["Tables"][T]["Update"];
60
-
61
- // ----- Database Schema (placeholder - replace with generated types) -----
62
-
63
- export interface Database {
64
- public: {
65
- Tables: {
66
- {{FEATURE_NAME}}: {
67
- Row: {
68
- id: string;
69
- name: string;
70
- user_id: string;
71
- status: {{pascalCase FEATURE_NAME}}Status;
72
- created_at: string;
73
- updated_at: string | null;
74
- };
75
- Insert: {
76
- id?: string;
77
- name: string;
78
- user_id?: string;
79
- status?: {{pascalCase FEATURE_NAME}}Status;
80
- created_at?: string;
81
- updated_at?: string | null;
82
- };
83
- Update: {
84
- id?: string;
85
- name?: string;
86
- user_id?: string;
87
- status?: {{pascalCase FEATURE_NAME}}Status;
88
- created_at?: string;
89
- updated_at?: string | null;
90
- };
91
- };
92
- };
93
- Views: Record<string, never>;
94
- Functions: Record<string, never>;
95
- Enums: {
96
- {{FEATURE_NAME}}_status: {{pascalCase FEATURE_NAME}}Status;
97
- };
98
- };
99
- }
100
-
101
- // ----- Feature Types -----
102
-
103
- export type {{pascalCase FEATURE_NAME}}Status =
104
- | "draft"
105
- | "active"
106
- | "completed"
107
- | "archived"
108
- | "failed"
109
- | "cancelled";
110
-
111
- export interface {{pascalCase FEATURE_NAME}} {
112
- id: string;
113
- name: string;
114
- userId: string;
115
- status: {{pascalCase FEATURE_NAME}}Status;
116
- createdAt: string;
117
- updatedAt: string | null;
118
- }
119
-
120
- export interface Create{{pascalCase FEATURE_NAME}}Input {
121
- name: string;
122
- // Add other required fields
123
- }
124
-
125
- export interface Update{{pascalCase FEATURE_NAME}}Input {
126
- name?: string;
127
- // Add other updatable fields
128
- }
129
-
130
- // ----- Storage Types -----
131
-
132
- export interface StorageUpload {
133
- bucket: string;
134
- path: string;
135
- file: File;
136
- contentType?: string;
137
- }
138
-
139
- export interface StorageFile {
140
- id: string;
141
- name: string;
142
- bucket: string;
143
- path: string;
144
- publicUrl: string;
145
- size: number;
146
- contentType: string;
147
- createdAt: string;
148
- }
149
-
150
- // ----- Auth Types -----
151
-
152
- export interface AuthUser {
153
- id: string;
154
- email: string;
155
- displayName: string | null;
156
- avatarUrl: string | null;
157
- }
158
-
159
- export interface AuthSession {
160
- user: AuthUser;
161
- accessToken: string;
162
- expiresAt: number;
163
- }
164
-
165
- // ----- Form Schemas (use with zod) -----
166
- // Define zod schemas in component files, not here.
167
- // This file is for type definitions only.
1
+ // {{titleCase FEATURE_NAME}} contracts, generated {{DATE}}
2
+
3
+ export interface NeonConfig {
4
+ connectionString: string;
5
+ }
6
+
7
+ export interface ApiClientConfig {
8
+ baseUrl: string;
9
+ getAccessToken: () => Promise<string | null>;
10
+ }
11
+
12
+ export interface ApiError {
13
+ message: string;
14
+ code: string;
15
+ statusCode: number;
16
+ details?: Record<string, string[]>;
17
+ }
18
+
19
+ export type ApiResult<T> =
20
+ | { success: true; data: T }
21
+ | { success: false; error: ApiError };
22
+
23
+ export interface PaginationQuery {
24
+ page?: number;
25
+ pageSize?: number;
26
+ }
27
+
28
+ export interface PagedResult<T> {
29
+ items: T[];
30
+ totalCount: number;
31
+ page: number;
32
+ pageSize: number;
33
+ totalPages: number;
34
+ hasNext: boolean;
35
+ hasPrevious: boolean;
36
+ }
37
+
38
+ export type Tables<T extends keyof Database["public"]["Tables"]> =
39
+ Database["public"]["Tables"][T]["Row"];
40
+
41
+ export type Insertable<T extends keyof Database["public"]["Tables"]> =
42
+ Database["public"]["Tables"][T]["Insert"];
43
+
44
+ export type Updatable<T extends keyof Database["public"]["Tables"]> =
45
+ Database["public"]["Tables"][T]["Update"];
46
+
47
+ // Placeholder: replace with the types your ORM (Drizzle or Prisma) generates from the Neon schema.
48
+ export interface Database {
49
+ public: {
50
+ Tables: {
51
+ {{FEATURE_NAME}}: {
52
+ Row: {
53
+ id: string;
54
+ name: string;
55
+ user_id: string;
56
+ status: {{pascalCase FEATURE_NAME}}Status;
57
+ created_at: string;
58
+ updated_at: string | null;
59
+ };
60
+ Insert: {
61
+ id?: string;
62
+ name: string;
63
+ user_id?: string;
64
+ status?: {{pascalCase FEATURE_NAME}}Status;
65
+ created_at?: string;
66
+ updated_at?: string | null;
67
+ };
68
+ Update: {
69
+ id?: string;
70
+ name?: string;
71
+ user_id?: string;
72
+ status?: {{pascalCase FEATURE_NAME}}Status;
73
+ created_at?: string;
74
+ updated_at?: string | null;
75
+ };
76
+ };
77
+ };
78
+ Views: Record<string, never>;
79
+ Functions: Record<string, never>;
80
+ Enums: {
81
+ {{FEATURE_NAME}}_status: {{pascalCase FEATURE_NAME}}Status;
82
+ };
83
+ };
84
+ }
85
+
86
+ export type {{pascalCase FEATURE_NAME}}Status =
87
+ | "draft"
88
+ | "active"
89
+ | "completed"
90
+ | "archived"
91
+ | "failed"
92
+ | "cancelled";
93
+
94
+ export interface {{pascalCase FEATURE_NAME}} {
95
+ id: string;
96
+ name: string;
97
+ userId: string;
98
+ status: {{pascalCase FEATURE_NAME}}Status;
99
+ createdAt: string;
100
+ updatedAt: string | null;
101
+ }
102
+
103
+ export interface Create{{pascalCase FEATURE_NAME}}Input {
104
+ name: string;
105
+ }
106
+
107
+ export interface Update{{pascalCase FEATURE_NAME}}Input {
108
+ name?: string;
109
+ }
110
+
111
+ export interface StorageUpload {
112
+ bucket: string;
113
+ path: string;
114
+ file: File;
115
+ contentType?: string;
116
+ }
117
+
118
+ export interface StorageFile {
119
+ id: string;
120
+ name: string;
121
+ bucket: string;
122
+ path: string;
123
+ publicUrl: string;
124
+ size: number;
125
+ contentType: string;
126
+ createdAt: string;
127
+ }
128
+
129
+ export interface AuthUser {
130
+ id: string;
131
+ email: string;
132
+ displayName: string | null;
133
+ avatarUrl: string | null;
134
+ }
135
+
136
+ export interface AuthSession {
137
+ user: AuthUser;
138
+ accessToken: string;
139
+ expiresAt: number;
140
+ }
@@ -1,65 +1,18 @@
1
1
  namespace Morph.AiKit.Agents;
2
2
 
3
- /// <summary>
4
- /// Protocolo pelo qual um modelo e alcancado. E o campo <c>api:</c> por alias que a
5
- /// issue #92 exige: a MESMA familia de modelos e atendida por dois protocolos
6
- /// diferentes, e qual deles vale e decisao de configuracao — nunca folclore espalhado
7
- /// por condicional no meio do codigo.
8
- /// </summary>
9
- /// <remarks>
10
- /// <para>
11
- /// <see cref="Responses"/> e <b>0 de proposito</b>: <c>default(AgentApi)</c> e a Responses
12
- /// API porque essa e a decisao da casa (Responses por padrao, Chat Completions como
13
- /// opt-in explicito por alias). Um alias que omite <c>api:</c> no
14
- /// <c>model-registry.v2.json</c> cai em Responses; usar Chat Completions exige escrever
15
- /// <c>"api": "chat"</c> — ou seja, exige alguem assumir a escolha por escrito.
16
- /// </para>
17
- /// <para>
18
- /// O enum descreve a escolha ENTRE OS DOIS PROTOCOLOS DA OpenAI. Provider que expoe um
19
- /// unico protocolo (Google, Ollama) ignora o campo, e por isso declara — ou omite,
20
- /// caindo em — <see cref="Responses"/>: escrever <c>chat</c> ali sugeriria uma escolha
21
- /// que naquele provider nao existe.
22
- /// </para>
23
- /// </remarks>
24
3
  public enum AgentApi
25
4
  {
26
- /// <summary>Responses API — o padrao da casa. Valor 0, logo <c>default(AgentApi)</c>.</summary>
5
+ // 0 de proposito: default(AgentApi) e alias sem 'api:' caem em Responses; Chat e opt-in escrito.
27
6
  Responses = 0,
28
7
 
29
- /// <summary>
30
- /// Chat Completions — opt-in explicito por alias. Carrega limites que a Responses API
31
- /// nao tem; ver <c>Morph.AiKit.Providers.ModelAliasConstraints</c>.
32
- /// </summary>
33
8
  Chat = 1,
34
9
 
35
- /// <summary>
36
- /// Images — geracao/edicao de imagem. <b>Nao e um protocolo de conversa</b>: um alias
37
- /// com este <c>api</c> e recusado pela <c>AgentFactory</c>, com erro nomeado.
38
- /// </summary>
39
- /// <remarks>
40
- /// Um registry so, um vocabulario so de alias: quem procura "qual modelo este projeto
41
- /// usa" encontra o de imagem no mesmo lugar que o de chat. A alternativa era um
42
- /// <c>media-registry.json</c> separado — duas verdades sobre o mesmo fato.
43
- /// </remarks>
44
10
  Images = 2,
45
11
 
46
- /// <summary>
47
- /// Videos — geracao de video. Mesma regra de <see cref="Images"/>: alias de midia nao
48
- /// vira agente.
49
- /// </summary>
50
12
  Videos = 3,
51
13
  }
52
14
 
53
- /// <summary>Predicados sobre <see cref="AgentApi"/>.</summary>
54
15
  public static class AgentApiExtensions
55
16
  {
56
- /// <summary>
57
- /// Se o protocolo e de MIDIA (imagem ou video) e portanto nao fala conversa.
58
- /// </summary>
59
- /// <remarks>
60
- /// Escrito uma vez, aqui, porque duas copias deste predicado sao como a
61
- /// <c>AgentFactory</c> e o <c>ModelRegistry</c> passariam a discordar sobre o mesmo
62
- /// alias.
63
- /// </remarks>
64
17
  public static bool IsMedia(this AgentApi api) => api is AgentApi.Images or AgentApi.Videos;
65
18
  }