@polymorphism-tech/morph-spec 8.38.0 → 8.40.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 (227) hide show
  1. package/CLAUDE.md +19 -8
  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 +3 -2
  8. package/framework/MORPH.md +1 -1
  9. package/framework/agents.json +36 -25
  10. package/framework/docs/CLI.md +2 -2
  11. package/framework/evals/rubrics/code-quality.md +4 -4
  12. package/framework/evals/rubrics/condensed.md +2 -2
  13. package/framework/mcp/registry.json +33 -2
  14. package/framework/rules/infrastructure-standards.md +0 -1
  15. package/framework/rules/morph-workflow.md +9 -4
  16. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  17. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  18. package/framework/skills/core/morph-apply/SKILL.md +5 -1
  19. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  20. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  21. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  22. package/framework/skills/core/morph-implement/SKILL.md +6 -1
  23. package/framework/skills/core/morph-media/SKILL.md +1 -1
  24. package/framework/skills/core/morph-plan/SKILL.md +4 -3
  25. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  26. package/framework/skills/core/morph-review/SKILL.md +1 -1
  27. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  28. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  29. package/framework/skills/core/morph-uiux/SKILL.md +6 -5
  30. package/framework/standards/STANDARDS.json +3 -3
  31. package/framework/standards/ai-agents/context-providers.md +16 -26
  32. package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
  33. package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
  34. package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
  35. package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
  36. package/framework/standards/ai-agents/modalities-vision.md +1 -18
  37. package/framework/standards/ai-agents/prompt-sources.md +0 -4
  38. package/framework/standards/architecture/code-comments.md +52 -16
  39. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
  40. package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
  41. package/framework/standards/integration/mcp/mcp-tools.md +48 -117
  42. package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
  43. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
  44. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
  45. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
  46. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
  47. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
  48. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
  49. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
  50. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
  51. package/framework/templates/code/dotnet/database/migration.cs +49 -83
  52. package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
  53. package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
  54. package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
  55. package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
  56. package/framework/templates/code/dotnet/jobs/job.cs +2 -97
  57. package/framework/templates/code/dotnet/test.cs +2 -87
  58. package/framework/templates/code/typescript/contracts.ts +140 -167
  59. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
  60. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
  61. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
  62. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
  63. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
  64. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
  65. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
  66. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
  67. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
  68. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
  69. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
  70. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
  71. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
  72. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
  73. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
  74. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
  75. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
  76. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
  77. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
  78. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
  79. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
  80. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
  81. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
  82. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
  83. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
  84. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
  85. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
  86. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
  87. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
  88. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
  89. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
  90. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
  91. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
  92. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
  93. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
  94. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
  95. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
  96. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
  97. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
  98. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
  99. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
  100. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
  101. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
  102. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
  103. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
  104. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
  105. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
  106. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
  107. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
  108. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
  109. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
  110. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
  111. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
  112. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
  113. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
  114. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
  115. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
  116. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
  117. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
  118. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
  119. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
  120. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
  121. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
  122. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
  123. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
  124. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
  125. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
  126. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
  127. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
  128. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
  129. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
  130. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
  131. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
  132. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
  133. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
  134. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
  135. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
  136. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
  137. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
  138. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
  139. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
  140. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
  141. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
  142. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
  143. package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
  144. package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
  145. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
  146. package/framework/templates/integrations/asaas-client.cs +332 -387
  147. package/framework/templates/integrations/asaas-webhook.cs +290 -351
  148. package/framework/templates/integrations/neon-auth-config.cs +9 -105
  149. package/framework/templates/project/validate.js +97 -120
  150. package/package.json +2 -2
  151. package/src/commands/dashboard/dashboard.js +80 -53
  152. package/src/commands/mcp/mcp-setup.js +37 -9
  153. package/src/commands/project/doctor.js +3 -5
  154. package/src/commands/project/init.js +6 -3
  155. package/src/commands/project/install-plugin-cmd.js +1 -1
  156. package/src/commands/project/update.js +5 -0
  157. package/src/dashboard/app/App.jsx +80 -16
  158. package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
  159. package/src/dashboard/app/components/CommandPalette.jsx +155 -0
  160. package/src/dashboard/app/components/DetailPanel.jsx +47 -11
  161. package/src/dashboard/app/components/EntityLink.jsx +55 -0
  162. package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
  163. package/src/dashboard/app/components/FlowGraph.jsx +19 -17
  164. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
  165. package/src/dashboard/app/components/Markdown.jsx +47 -0
  166. package/src/dashboard/app/components/PhaseRail.jsx +111 -0
  167. package/src/dashboard/app/components/SearchFilter.jsx +40 -22
  168. package/src/dashboard/app/components/Sidebar.jsx +146 -37
  169. package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
  170. package/src/dashboard/app/hooks/usePref.js +38 -0
  171. package/src/dashboard/app/lib/format.js +69 -0
  172. package/src/dashboard/app/lib/phases.js +56 -0
  173. package/src/dashboard/app/lib/project.js +50 -0
  174. package/src/dashboard/app/pages/Activity.jsx +24 -0
  175. package/src/dashboard/app/pages/AgentMap.jsx +371 -370
  176. package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
  177. package/src/dashboard/app/pages/Features.jsx +400 -0
  178. package/src/dashboard/app/pages/Guide.jsx +146 -0
  179. package/src/dashboard/app/pages/HookInspector.jsx +357 -322
  180. package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
  181. package/src/dashboard/app/pages/Portfolio.jsx +156 -0
  182. package/src/dashboard/app/pages/Rubrics.jsx +101 -0
  183. package/src/dashboard/app/pages/RulesMap.jsx +300 -273
  184. package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
  185. package/src/dashboard/app/pages/Standards.jsx +407 -332
  186. package/src/dashboard/app/styles/index.css +187 -47
  187. package/src/dashboard/index.html +14 -12
  188. package/src/dashboard/package.json +32 -24
  189. package/src/dashboard/scanner/activity-scanner.js +351 -0
  190. package/src/dashboard/scanner/agents-scanner.js +59 -1
  191. package/src/dashboard/scanner/docs-scanner.js +59 -0
  192. package/src/dashboard/scanner/feature-status.js +10 -0
  193. package/src/dashboard/scanner/features-scanner.js +360 -0
  194. package/src/dashboard/scanner/index.js +14 -7
  195. package/src/dashboard/scanner/phases-scanner.js +14 -15
  196. package/src/dashboard/scanner/portfolio.js +153 -0
  197. package/src/dashboard/scanner/project-api.js +195 -0
  198. package/src/dashboard/scanner/rules-scanner.js +42 -37
  199. package/src/dashboard/scanner/skills-scanner.js +105 -43
  200. package/src/dashboard/scanner/standards-scanner.js +2 -0
  201. package/src/dashboard/vite-plugin-morph-data.js +134 -17
  202. package/src/lib/detectors/claude-config-detector.js +2 -7
  203. package/src/lib/detectors/index.js +5 -1
  204. package/src/lib/evaluator-report.js +22 -0
  205. package/src/lib/generators/settings-generator.js +208 -209
  206. package/src/lib/installers/mcp-installer.js +724 -719
  207. package/src/lib/installers/memory-installer.js +1 -2
  208. package/src/lib/projects/registry.js +154 -0
  209. package/src/lib/validators/code-budget/code-budget-validator.js +32 -4
  210. package/src/scripts/install-plugin.js +1 -1
  211. package/src/scripts/setup-infra.js +2 -4
  212. package/src/utils/agents-installer.js +57 -12
  213. package/src/utils/banner.js +1 -1
  214. package/src/commands/index.js +0 -13
  215. package/src/commands/project/checkpoint.js +0 -209
  216. package/src/core/state/phase-state-machine.js +0 -197
  217. package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
  218. package/src/dashboard/app/pages/LiveState.jsx +0 -206
  219. package/src/dashboard/scanner/squads-scanner.js +0 -13
  220. package/src/dashboard/scanner/state-scanner.js +0 -85
  221. package/src/lib/generators/index.js +0 -10
  222. package/src/lib/hooks/hook-executor.js +0 -167
  223. package/src/lib/validators/checkpoint-persistence.js +0 -39
  224. package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
  225. package/src/lib/validators/shared/index.js +0 -12
  226. package/src/lib/validators/shared/issue-counter.js +0 -14
  227. package/src/lib/validators/shared/result-formatter.js +0 -124
@@ -50,8 +50,7 @@ using Microsoft.Extensions.AI;
50
50
  using OpenAI.Images;
51
51
 
52
52
  #pragma warning disable MEAI001 // IImageGenerator e experimental: a superficie pode mudar entre menores
53
- // AsIImageGenerator() vem de Microsoft.Extensions.AI.OpenAI — um pacote A MAIS.
54
- // Sem ele, escreva o adaptador (e o kit escreve: ver a nota abaixo).
53
+ // AsIImageGenerator() exige o pacote Microsoft.Extensions.AI.OpenAI, a mais (ver a nota abaixo).
55
54
  IImageGenerator generator = new ImageClient("gpt-image-2", apiKey).AsIImageGenerator();
56
55
 
57
56
  var response = await generator.GenerateAsync(
@@ -197,35 +196,33 @@ trocar a foto de referência **é** outro pedido, mesmo com o prompt idêntico.
197
196
 
198
197
  ## Exemplo C# completo
199
198
 
199
+ O adaptador é de **provider**, não um serviço público que um handler possa chamar: quem produz bytes
200
+ vive atrás do enfileiramento de `ai-agents/media-pipeline.md`, e no kit esse tipo é `internal` de
201
+ propósito. Quatro escolhas do bloco abaixo foram impostas pelo compilador:
202
+
203
+ - **Sem `using OpenAI.Images;`.** O gerador chega injetado e nenhum tipo do SDK da OpenAI aparece no
204
+ arquivo; deixar o `using` torna `ImageGenerationOptions` ambíguo (CS0104) entre
205
+ `Microsoft.Extensions.AI` e `OpenAI.Images`. Onde os dois convivem de verdade, como no adaptador do
206
+ kit, o tipo vai qualificado.
207
+ - **O pragma abre acima da classe**, não dentro do método: o parâmetro do construtor primário também
208
+ nomeia `IImageGenerator`, e `MEAI001` é erro por padrão, não aviso. Pragma dentro do corpo deixa a
209
+ assinatura de fora e o arquivo não compila.
210
+ - **O `IImageGenerator` chega injetado**, não construído aqui, e é isso que faz o exemplo compilar com
211
+ ou sem `Microsoft.Extensions.AI.OpenAI` no pin (ver a ressalva de §A API): com o pacote,
212
+ `imageClient.AsIImageGenerator()`; sem ele, um `IImageGenerator` escrito à mão sobre o `ImageClient`,
213
+ como em `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs`.
214
+ - **`ReadOnlyMemory<byte>` dos dois lados**, porque é o tipo real de `DataContent.Data`; declarar
215
+ `BinaryData` não converte (CS0029).
216
+
217
+ `OriginalImages` é o gancho de edição: com referências, o produto real é preservado em vez de recriado.
218
+
200
219
  ```csharp
201
- // SEM `using OpenAI.Images;`. Ele nao e necessario aqui — o gerador chega injetado e nenhum
202
- // tipo do SDK da OpenAI aparece neste arquivo — e deixa-lo torna `ImageGenerationOptions`
203
- // AMBIGUO (CS0104) entre Microsoft.Extensions.AI e OpenAI.Images. Onde os dois convivem de
204
- // verdade, como no adaptador do kit, o tipo vai QUALIFICADO.
205
220
  using Microsoft.Extensions.AI;
206
221
 
207
- // O pragma abre ACIMA da classe, e nao dentro do metodo: o parametro do construtor primario
208
- // tambem nomeia IImageGenerator, e MEAI001 e ERRO por padrao — nao aviso. Pragma dentro do
209
- // corpo deixa a assinatura de fora e o arquivo nao compila.
210
222
  #pragma warning disable MEAI001 // IImageGenerator/ImageGenerationRequest sao experimentais: a superficie pode mudar entre menores.
211
223
 
212
- /// <summary>
213
- /// Adaptador de PROVIDER. Note o que ele NAO e: um servico publico que um handler
214
- /// possa chamar. Quem produz bytes vive atras do enfileiramento descrito em
215
- /// ai-agents/media-pipeline.md, e no kit esse tipo e `internal` de proposito.
216
- /// </summary>
217
- /// <remarks>
218
- /// O IImageGenerator chega INJETADO, e nao construido aqui. As duas formas de obte-lo
219
- /// sao legitimas e a escolha e do seu pin (ver a ressalva da secao "A API"):
220
- /// - com Microsoft.Extensions.AI.OpenAI no pin: imageClient.AsIImageGenerator()
221
- /// - sem ele: um IImageGenerator escrito a mao sobre o ImageClient, como em
222
- /// templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs
223
- /// Injetar em vez de construir e o que faz ESTE exemplo compilar nos dois casos.
224
- /// </remarks>
225
224
  internal sealed class OpenAiImageAdapter(IImageGenerator generator)
226
225
  {
227
- // ReadOnlyMemory<byte> dos dois lados porque e o tipo REAL de DataContent.Data.
228
- // Declarar BinaryData aqui nao converte (CS0029) — e o compilador que decide isso.
229
226
  public async Task<ReadOnlyMemory<byte>> ProduceAsync(
230
227
  string prompt,
231
228
  IReadOnlyList<ReadOnlyMemory<byte>> references,
@@ -234,7 +231,6 @@ internal sealed class OpenAiImageAdapter(IImageGenerator generator)
234
231
  var request = new ImageGenerationRequest { Prompt = prompt };
235
232
  if (references.Count > 0)
236
233
  {
237
- // OriginalImages e o gancho de EDICAO: preserva o produto real em vez de recria-lo.
238
234
  request.OriginalImages = [.. references.Select(r => new DataContent(r, "image/png"))];
239
235
  }
240
236
 
@@ -49,6 +49,8 @@ O projeto injeta `AudioClient` via DI. Para transcrição com timestamps (ex: si
49
49
 
50
50
  ## Exemplo C# completo
51
51
 
52
+ Três entradas: arquivo, bytes (ex: upload do usuário) — as duas devolvem texto simples — e a verbose, com timestamps por palavra e segmento, útil para legendas ou busca por trecho.
53
+
52
54
  ```csharp
53
55
  using OpenAI.Audio;
54
56
  using Microsoft.Extensions.Configuration;
@@ -64,19 +66,12 @@ public sealed class TranscriptionService
64
66
  _client = new AudioClient(model, apiKey);
65
67
  }
66
68
 
67
- /// <summary>
68
- /// Transcreve áudio a partir de um arquivo e retorna texto simples.
69
- /// O transcript retornado pode ser passado diretamente como input para um AIAgent downstream.
70
- /// </summary>
71
69
  public async Task<string> TranscribeAsync(string audioFilePath, CancellationToken ct = default)
72
70
  {
73
71
  AudioTranscription transcription = await _client.TranscribeAudioAsync(audioFilePath, cancellationToken: ct);
74
72
  return transcription.Text;
75
73
  }
76
74
 
77
- /// <summary>
78
- /// Transcreve áudio a partir de bytes (ex: upload do usuário) e retorna texto simples.
79
- /// </summary>
80
75
  public async Task<string> TranscribeBytesAsync(
81
76
  Stream audioStream,
82
77
  string fileName,
@@ -93,9 +88,6 @@ public sealed class TranscriptionService
93
88
  return transcription.Text;
94
89
  }
95
90
 
96
- /// <summary>
97
- /// Transcreve com timestamps por palavra e segmento (útil para legendas ou busca por trecho).
98
- /// </summary>
99
91
  public async Task<AudioTranscription> TranscribeVerboseAsync(string audioFilePath, CancellationToken ct = default)
100
92
  {
101
93
  var options = new AudioTranscriptionOptions
@@ -109,21 +101,18 @@ public sealed class TranscriptionService
109
101
  }
110
102
  ```
111
103
 
112
- **Alimentando um agente downstream com o transcript:**
104
+ **Alimentando um agente downstream com o transcript** — ele é texto puro, então basta passá-lo como input para qualquer `AIAgent`; aqui, um Status Agent que extrai as ações da reunião:
113
105
 
114
106
  ```csharp
115
- // O transcript é texto puro — basta passar como input para qualquer AIAgent
116
107
  string transcript = await _transcription.TranscribeAsync("meeting-2024-05-19.mp3");
117
108
 
118
- // Ex: Status Agent que extrai ações da reunião
119
109
  AgentResponse<MeetingActions> actions = await _statusAgent.RunAsync<MeetingActions>(
120
110
  $"Extraia ações, decisões e responsáveis desta transcrição:\n\n{transcript}");
121
111
  ```
122
112
 
123
- **Registro DI:**
113
+ **Registro DI** (`Program.cs`):
124
114
 
125
115
  ```csharp
126
- // Program.cs
127
116
  builder.Services.AddSingleton<TranscriptionService>();
128
117
  ```
129
118
 
@@ -58,17 +58,11 @@ public sealed class TextToSpeechService
58
58
 
59
59
  public TextToSpeechService(IConfiguration config)
60
60
  {
61
- // tts-1: mais rápido, menor qualidade. tts-1-hd: mais lento, maior qualidade.
62
61
  var model = config["TTS:Model"] ?? "tts-1";
63
62
  var apiKey = config["OpenAI:ApiKey"]!;
64
63
  _client = new AudioClient(model, apiKey);
65
64
  }
66
65
 
67
- /// <summary>
68
- /// Converte texto em áudio e retorna bytes MP3.
69
- /// Cachear o resultado é altamente recomendado para textos repetitivos — gerar o mesmo texto
70
- /// duas vezes é desperdício de custo e latência.
71
- /// </summary>
72
66
  public async Task<BinaryData> SynthesizeAsync(
73
67
  string text,
74
68
  GeneratedSpeechVoice? voice = null,
@@ -91,14 +85,13 @@ public sealed class TextToSpeechService
91
85
  }
92
86
  ```
93
87
 
94
- **Registro DI:**
88
+ **Registro DI** (`Program.cs`):
95
89
 
96
90
  ```csharp
97
- // Program.cs
98
91
  builder.Services.AddSingleton<TextToSpeechService>();
99
92
  ```
100
93
 
101
- **Servindo como download ou stream:**
94
+ **Servindo como download ou stream** — verifique o cache antes de gerar (ver Anti-patterns):
102
95
 
103
96
  ```csharp
104
97
  public class AudioController(TextToSpeechService tts) : ControllerBase
@@ -106,14 +99,12 @@ public class AudioController(TextToSpeechService tts) : ControllerBase
106
99
  [HttpPost("synthesize")]
107
100
  public async Task<IActionResult> Synthesize([FromBody] SynthesizeRequest req)
108
101
  {
109
- // Verificar cache antes de gerar (ver Anti-patterns)
110
102
  BinaryData audio = await tts.SynthesizeAsync(
111
103
  text: req.Text,
112
104
  voice: GeneratedSpeechVoice.Nova,
113
105
  format: GeneratedSpeechFormat.Mp3,
114
106
  speedRatio: 1.1f);
115
107
 
116
- // the storage layer may assign the final name; use a timestamp-based name here
117
108
  var fileName = $"tts-{DateTime.UtcNow:yyyyMMddHHmmss}.mp3";
118
109
  return File(audio.ToStream(), "audio/mpeg", fileName);
119
110
  }
@@ -31,7 +31,6 @@ Vision em MAF usa `ChatMessage` multimodal — a mesma abstração `IChatClient`
31
31
  ```csharp
32
32
  using Microsoft.Extensions.AI;
33
33
 
34
- // Constrói um ChatMessage com texto + imagem
35
34
  ChatMessage message = new(ChatRole.User, [
36
35
  new TextContent("O que aparece nesta imagem?"),
37
36
  new UriContent(new Uri("https://example.com/foto.jpg"), "image/jpeg")
@@ -59,7 +58,7 @@ O `IChatClient` (e portanto `AIAgent`) aceita `IEnumerable<ChatMessage>` — voc
59
58
 
60
59
  ## Exemplo C# completo
61
60
 
62
- Agente de visão que recebe URL de imagem e retorna descrição. Para extração de dados tipados de um documento, combine com `RunAsync<T>` (ver comentário inline).
61
+ Agente de visão que recebe URL de imagem e retorna descrição. Três entradas: URL pública, arquivo local via `DataContent.LoadFromAsync` (o padrão canônico MAF, preferido sempre que houver o caminho do arquivo) e bytes já em memória (ex: upload de usuário já lido). Para extração de dados tipados de um documento, combine com `RunAsync<T>` em vez de `RunAsync` — `AgentResponse<InvoiceData> r = await _agent.RunAsync<InvoiceData>(message);` e `return r.Result;` —, com o padrão completo em `ai-agents-structured-output`.
63
62
 
64
63
  ```csharp
65
64
  using Microsoft.Agents.AI;
@@ -84,7 +83,6 @@ public sealed class VisionAgent
84
83
  });
85
84
  }
86
85
 
87
- /// <summary>Analisa uma imagem por URL pública e retorna descrição em texto.</summary>
88
86
  public async Task<string> AnalyzeImageUrlAsync(Uri imageUri, string question, CancellationToken ct = default)
89
87
  {
90
88
  ChatMessage message = new(ChatRole.User, [
@@ -96,14 +94,6 @@ public sealed class VisionAgent
96
94
  return response.Text;
97
95
  }
98
96
 
99
- /// <summary>
100
- /// Analisa imagem a partir de um arquivo local ou caminho de upload.
101
- /// Usa DataContent.LoadFromAsync — padrão canônico MAF para carregar imagem de arquivo.
102
- /// Para extrair dados tipados, use RunAsync&lt;T&gt; em vez de RunAsync:
103
- /// AgentResponse&lt;InvoiceData&gt; r = await _agent.RunAsync&lt;InvoiceData&gt;(message);
104
- /// return r.Result;
105
- /// Veja ai-agents-structured-output para o padrão completo.
106
- /// </summary>
107
97
  public async Task<string> AnalyzeImageFileAsync(string filePath, string question, CancellationToken ct = default)
108
98
  {
109
99
  DataContent imageContent = await DataContent.LoadFromAsync(filePath);
@@ -116,10 +106,6 @@ public sealed class VisionAgent
116
106
  return response.Text;
117
107
  }
118
108
 
119
- /// <summary>
120
- /// Analisa imagem a partir de bytes já em memória (ex: upload de usuário já lido).
121
- /// Use AnalyzeImageFileAsync quando tiver o caminho do arquivo — é o padrão preferido.
122
- /// </summary>
123
109
  public async Task<string> AnalyzeImageBytesAsync(BinaryData imageData, string mediaType, string question, CancellationToken ct = default)
124
110
  {
125
111
  ChatMessage message = new(ChatRole.User, [
@@ -138,17 +124,14 @@ public sealed class VisionAgent
138
124
  ```csharp
139
125
  var agent = new VisionAgent(modelRegistry);
140
126
 
141
- // Por URL pública
142
127
  string desc = await agent.AnalyzeImageUrlAsync(
143
128
  new Uri("https://cdn.example.com/invoice-2024.jpg"),
144
129
  "Extraia os campos: número da nota, valor total e data de emissão.");
145
130
 
146
- // Por arquivo local — padrão canônico com DataContent.LoadFromAsync
147
131
  string desc2 = await agent.AnalyzeImageFileAsync(
148
132
  "nota-fiscal.png",
149
133
  "Liste todos os itens e valores desta nota fiscal.");
150
134
 
151
- // Por bytes em memória (ex: upload já lido de IFormFile)
152
135
  BinaryData data = await BinaryData.FromStreamAsync(formFile.OpenReadStream());
153
136
  string desc3 = await agent.AnalyzeImageBytesAsync(data, "image/png",
154
137
  "Descreva o conteúdo da imagem enviada.");
@@ -113,10 +113,6 @@ Interface contract:
113
113
  ```csharp
114
114
  public interface IPromptRepository
115
115
  {
116
- /// <summary>
117
- /// Returns the content of the currently active prompt for the given agent key.
118
- /// Throws <see cref="InvalidOperationException"/> if no active prompt exists.
119
- /// </summary>
120
116
  Task<string> GetActivePromptAsync(string agentKey, CancellationToken ct = default);
121
117
  }
122
118
  ```
@@ -9,8 +9,24 @@
9
9
 
10
10
  ---
11
11
 
12
- Comentário é relido a cada leitura do arquivo, por quem mantém e pelo agente que abre o arquivo na
13
- próxima task. Ele só se paga quando diz o que o código **não consegue** dizer.
12
+ **O padrão é nenhum comentário.** O código se explica pelos nomes, pelos tipos e pelos testes. Um
13
+ comentário é relido a cada leitura do arquivo, por quem mantém e pelo agente que abre o arquivo na
14
+ próxima task, e ninguém o atualiza quando o código muda. Ele só entra quando diz algo que o código
15
+ **não consegue** dizer, e isso é raro: a maioria dos arquivos não tem nenhum.
16
+
17
+ ## Antes de escrever um comentário
18
+
19
+ Tente, nesta ordem, e pare no primeiro que resolve:
20
+
21
+ | Em vez de comentar... | Faça |
22
+ |---|---|
23
+ | o que uma variável ou função é | renomeie: `retryDelayMs`, `isEligibleForRefund()` |
24
+ | o que um trecho faz | extraia uma função com esse nome |
25
+ | um número ou string mágica | extraia uma constante nomeada |
26
+ | um estado que não pode acontecer | expresse no tipo (enum, união, record, `required`) |
27
+ | o que o código garante | escreva um teste que falha quando deixar de valer |
28
+
29
+ Só sobrando algo que nenhum desses expressa, o comentário entra.
14
30
 
15
31
  ## O que merece comentário
16
32
 
@@ -21,15 +37,22 @@ Só o **porquê que o código não mostra**:
21
37
  | Invariante que o tipo não expressa | "a chave é a identidade e o hash é a versão" |
22
38
  | Ordem que importa | "invalidar o cache ANTES de publicar: o consumidor relê na hora" |
23
39
  | Peculiaridade externa | API de terceiro que devolve 200 com erro no corpo; bug de biblioteca, com o link |
24
- | Contrato de API pública | `///` ou JSDoc no membro público: o que recebe, devolve e lança |
40
+ | Contrato de pacote publicado | `///` ou JSDoc só em API de pacote consumido **fora** deste repositório (NuGet, npm) |
25
41
 
26
42
  ## O que não merece
27
43
 
28
44
  - **História de processo.** Task, UC, ADR, FR, Gate, avaliador, `spec.md`, mockup, "confirmado ao
29
45
  implementar a T11", "o Gate 3 pediu". Isso vai na mensagem de commit e no `decisions.md`, onde tem
30
46
  data e autor e não envelhece dentro do arquivo.
31
- - **Repetição do código.** `// incrementa o contador` sobre `count++`. Nome bom substitui
32
- comentário: extraia `isEligibleForRefund()` em vez de comentar a condição.
47
+ - **Repetição do código.** `// incrementa o contador` sobre `count++`, `// valida o request` sobre
48
+ `validator.Validate(request)`. Nome bom substitui comentário.
49
+ - **Doc-comment que repete a assinatura.** `/// <summary>Obtém o pedido.</summary>` sobre
50
+ `GetOrder()`, JSDoc com `@param id o id`. Membro de aplicação (handler, endpoint, componente,
51
+ serviço interno) não leva `///` nem JSDoc, mesmo sendo `public`.
52
+ - **Estrutura e decoração.** Cabeçalho de arquivo, banner de seção (`// ---- Handlers ----`,
53
+ `#region`), comentário que só rotula o bloco seguinte.
54
+ - **Código comentado e TODO solto.** O git guarda o código antigo; pendência vira issue, e o
55
+ comentário, se ficar, cita o número dela.
33
56
  - **Estado do mundo fora do arquivo.** "Public Sans é a fonte da interface" passa a mentir na
34
57
  primeira troca de fonte, e nada avisa.
35
58
  - **Afirmação sobre comportamento.** "nunca retorna vazio", "ordenado por sobrenome": isso é
@@ -37,27 +60,40 @@ Só o **porquê que o código não mostra**:
37
60
 
38
61
  ## Tamanho
39
62
 
40
- Teto de ~3 linhas. Acima disso o comentário justifica o volume (algoritmo não óbvio, armadilha de
41
- terceiro com a reprodução). Bloco longo narrando decisões é `decisions.md` com outro nome.
63
+ Uma linha é o normal; teto de ~3. Acima disso o comentário justifica o volume (algoritmo não
64
+ óbvio, armadilha de terceiro com a reprodução). Bloco longo narrando decisões é `decisions.md` com
65
+ outro nome. Em volume, código de aplicação fica perto de zero: mais de uma linha de comentário a
66
+ cada dez de código é sinal de que os nomes não estão fazendo o trabalho.
42
67
 
43
68
  ## Exemplo
44
69
 
45
70
  ```ts
46
- // Ruim: conta a task, repete o código e afirma comportamento que nenhum teste cobra.
47
- // T8 + T11: mutação obrigatória do relatório, item c. Confirmado ao implementar a T11.
48
- // Faz upsert do documento com chave e hash e nunca duplica.
49
- await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
50
-
51
- // Bom: o porquê que o código não mostra.
71
+ // Ruim: rotula, conta a task, repete o código e afirma comportamento que nenhum teste cobra.
72
+ /**
73
+ * Salva o documento.
74
+ * @param doc o documento
75
+ */
76
+ // T8 + T11: mutação obrigatória do relatório, item c. Faz upsert e nunca duplica.
77
+ async function save(doc: Doc) {
78
+ await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
79
+ }
80
+
81
+ // Bom: sem comentário nenhum, porque o nome e a chamada já dizem tudo...
82
+ async function save(doc: Doc) {
83
+ await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
84
+ }
85
+
86
+ // ...e um comentário só quando há um porquê invisível.
52
87
  // A chave é a identidade e o hash é a versão: hash novo na mesma chave substitui.
53
88
  await upsert({ key: doc.sourceId, hash: sha256(doc.body) });
54
89
  ```
55
90
 
56
91
  ## Na revisão
57
92
 
58
- Comentário de processo é achado da rubrica `code-quality` no Gate 3. O nó `validators` do
59
- `morph-spec verify` mede, nas linhas que a feature adicionou, quantas linhas de comentário citam
60
- processo (validador `code-budget`) e avisa com o número.
93
+ Comentário que o código dispensava é achado da rubrica `code-quality` no Gate 3. O nó `validators`
94
+ do `morph-spec verify` mede, nas linhas que a feature adicionou (validador `code-budget`), quantas
95
+ linhas de comentário citam processo e a densidade de comentário sobre o código adicionado, e avisa
96
+ com os números e os arquivos mais comentados.
61
97
 
62
98
  ---
63
99