@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
@@ -2,30 +2,6 @@ using Morph.AiKit.Media;
2
2
 
3
3
  namespace Morph.AiKit.Tests.Fakes;
4
4
 
5
- /// <summary>
6
- /// <see cref="IMediaProvider"/> roteirizado: a costura que dispensa rede, chave de API e
7
- /// <b>fatura</b> na suite inteira de midia.
8
- /// </summary>
9
- /// <remarks>
10
- /// <para>
11
- /// <b>Por que ele existe:</b> o unico ponto do subsistema que gasta dinheiro e um
12
- /// <c>IMediaProvider</c>. Trocando-o por uma fila de respostas escritas a mao, todo o resto
13
- /// — <c>MediaGenerator</c>, <c>MediaWorker</c>, <c>MediaJob</c>, <c>MediaStore</c> — passa a
14
- /// ser testavel sem socket, sem segredo e sem cobranca. Mesmo desenho do
15
- /// <see cref="FakeChatClient"/>, pela mesma razao.
16
- /// </para>
17
- /// <para>
18
- /// Ele so e construivel porque <c>InternalsVisibleTo("Morph.AiKit.Tests")</c> existe no
19
- /// <c>.csproj</c> da biblioteca. Um projeto consumidor <b>nao</b> consegue escrever esta
20
- /// classe — e essa impossibilidade e a regra dura de <c>ai-agents/media-pipeline.md</c>,
21
- /// asserida em <c>MediaAccessibilityTests</c>.
22
- /// </para>
23
- /// <para>
24
- /// <b>Chamada nao roteirizada e defeito de teste.</b> Esgotada a fila, o fake devolve uma
25
- /// falha nomeada com a contagem de chamadas, em vez de inventar bytes — um artefato
26
- /// fabricado pelo proprio fake faria um teste passar por engano.
27
- /// </para>
28
- /// </remarks>
29
5
  internal sealed class FakeMediaProvider : IMediaProvider
30
6
  {
31
7
  private readonly Queue<MediaResult<MediaArtifact>> _scripted = new();
@@ -37,19 +13,14 @@ internal sealed class FakeMediaProvider : IMediaProvider
37
13
  Supported = supports;
38
14
  }
39
15
 
40
- /// <inheritdoc />
41
16
  public string Provider { get; }
42
17
 
43
- /// <summary>Que tipo de midia este fake diz atender.</summary>
44
18
  public MediaKind Supported { get; }
45
19
 
46
- /// <summary>Quantas vezes foi chamado. E como um teste prova que NAO houve segunda chamada paga.</summary>
47
20
  public int Calls { get; private set; }
48
21
 
49
- /// <summary>Os pedidos vistos, na ordem.</summary>
50
22
  public List<MediaRequest> Seen { get; } = [];
51
23
 
52
- /// <summary>Enfileira bytes de sucesso.</summary>
53
24
  public FakeMediaProvider Returns(byte[] bytes, string mediaType = "image/png", string? revisedPrompt = null)
54
25
  {
55
26
  _scripted.Enqueue(MediaResult<MediaArtifact>.Ok(new MediaArtifact(
@@ -57,17 +28,14 @@ internal sealed class FakeMediaProvider : IMediaProvider
57
28
  return this;
58
29
  }
59
30
 
60
- /// <summary>Enfileira uma recusa com o codigo dado — inclusive <c>PolicyBlocked</c>.</summary>
61
31
  public FakeMediaProvider Fails(MediaErrorCode code, string message = "roteirizado")
62
32
  {
63
33
  _scripted.Enqueue(MediaResult<MediaArtifact>.Fail(code, message));
64
34
  return this;
65
35
  }
66
36
 
67
- /// <inheritdoc />
68
37
  public bool Supports(MediaKind kind) => kind == Supported;
69
38
 
70
- /// <inheritdoc />
71
39
  public Task<MediaResult<MediaArtifact>> ProduceAsync(
72
40
  MediaRequest request,
73
41
  MediaAliasBinding binding,
@@ -3,40 +3,26 @@ using Morph.AiKit.Providers;
3
3
 
4
4
  namespace Morph.AiKit.Tests.Fakes;
5
5
 
6
- /// <summary>
7
- /// <see cref="IProviderClientFactory"/> de teste: devolve <see cref="FakeChatClient"/> e
8
- /// <b>conta quantas vezes foi chamada</b>. E o contador que torna a memoizacao do
9
- /// <c>ModelRegistry</c> uma afirmacao verificavel em vez de uma promessa.
10
- /// </summary>
11
6
  public sealed class FakeProviderClientFactory : IProviderClientFactory
12
7
  {
13
8
  private readonly Func<ModelAlias, IChatClient?> _create;
14
9
  private readonly Dictionary<string, IChatClient> _created = new(StringComparer.Ordinal);
15
10
  private readonly List<ModelAlias> _requested = [];
16
11
 
17
- /// <summary>Fabrica que devolve um <see cref="FakeChatClient"/> novo por alias.</summary>
18
12
  public FakeProviderClientFactory()
19
13
  : this(_ => new FakeChatClient())
20
14
  {
21
15
  }
22
16
 
23
- /// <summary>
24
- /// Fabrica com construcao sob medida — inclusive devolvendo nulo, para exercitar a
25
- /// recusa do registry a um cliente inexistente.
26
- /// </summary>
27
17
  public FakeProviderClientFactory(Func<ModelAlias, IChatClient?> create)
28
18
  => _create = create;
29
19
 
30
- /// <summary>Quantas vezes <see cref="CreateChatClient"/> foi realmente executada.</summary>
31
20
  public int CreateCount { get; private set; }
32
21
 
33
- /// <summary>Os aliases para os quais um cliente foi criado, na ordem.</summary>
34
22
  public IReadOnlyList<ModelAlias> Requested => _requested;
35
23
 
36
- /// <summary>Os clientes criados, por nome de alias.</summary>
37
24
  public IReadOnlyDictionary<string, IChatClient> Created => _created;
38
25
 
39
- /// <inheritdoc />
40
26
  public IChatClient CreateChatClient(ModelAlias alias)
41
27
  {
42
28
  ArgumentNullException.ThrowIfNull(alias);
@@ -2,55 +2,31 @@ using Morph.AiKit.Middleware;
2
2
 
3
3
  namespace Morph.AiKit.Tests.Fakes;
4
4
 
5
- /// <summary>
6
- /// Sink de teste: acumula os registros de custo publicados pelo
7
- /// <see cref="UsageCostMiddleware"/>.
8
- /// </summary>
9
- /// <remarks>
10
- /// <para>
11
- /// Estava nos <c>outputs</c> da T9, mas depende de <see cref="IUsageCostSink"/> e
12
- /// <see cref="UsageCostRecord"/>, que sao <c>outputs</c> da T11. A T9 recusou-se a criar os
13
- /// dois tipos por fora (seriam arquivos de outra task, e duplica-los criaria a segunda
14
- /// verdade do registro de custo) e deixou este codigo pronto no seu relatorio; a errata E8
15
- /// do <c>spec.md</c> moveu formalmente o arquivo para ca. Nasce junto dos tipos que
16
- /// implementa.
17
- /// </para>
18
- /// <para>
19
- /// Seguro sob concorrencia pelo mesmo motivo do <see cref="FakeChatClient"/>: um mesmo
20
- /// pipeline atende varias conversas, e um sink que perdesse linha sob concorrencia faria o
21
- /// teste medir o sink em vez do middleware.
22
- /// </para>
23
- /// </remarks>
24
5
  public sealed class FakeUsageCostSink : IUsageCostSink
25
6
  {
26
7
  private readonly Lock _gate = new();
27
8
  private readonly List<UsageCostRecord> _records = [];
28
9
 
29
- /// <summary>Tudo o que foi publicado, na ordem. Snapshot — iterar e seguro.</summary>
30
10
  public IReadOnlyList<UsageCostRecord> Records
31
11
  {
32
12
  get { lock (_gate) { return [.. _records]; } }
33
13
  }
34
14
 
35
- /// <summary>O ultimo registro publicado, ou nulo se nenhum foi.</summary>
36
15
  public UsageCostRecord? Last
37
16
  {
38
17
  get { lock (_gate) { return _records.Count == 0 ? null : _records[^1]; } }
39
18
  }
40
19
 
41
- /// <summary>Quantas linhas de custo foram publicadas.</summary>
42
20
  public int Count
43
21
  {
44
22
  get { lock (_gate) { return _records.Count; } }
45
23
  }
46
24
 
47
- /// <summary>A soma dos custos <b>medidos</b> — os demais status nao entram na conta.</summary>
48
25
  public decimal MeasuredTotalUsd
49
26
  {
50
27
  get { lock (_gate) { return _records.Where(r => r.IsMeasuredCost).Sum(r => r.CostUsd); } }
51
28
  }
52
29
 
53
- /// <inheritdoc />
54
30
  public void Record(UsageCostRecord record)
55
31
  {
56
32
  ArgumentNullException.ThrowIfNull(record);
@@ -3,29 +3,17 @@ using Morph.AiKit.Media;
3
3
 
4
4
  namespace Morph.AiKit.Tests.Fakes;
5
5
 
6
- /// <summary>
7
- /// Blob em memoria. <b>Conta as gravacoes</b> — e a contagem e o que prova que o
8
- /// <see cref="MediaStore"/> deduplicou em vez de regravar.
9
- /// </summary>
10
- /// <remarks>
11
- /// Um teste que so confira "o hash e o mesmo" nao distingue dedupe de regravacao: os dois
12
- /// produzem o mesmo hash. Por isso <see cref="Writes"/> existe.
13
- /// </remarks>
14
6
  public sealed class InMemoryMediaBlobs : IMediaBlobs
15
7
  {
16
8
  private readonly ConcurrentDictionary<string, byte[]> _files = new(StringComparer.Ordinal);
17
9
 
18
- /// <summary>Quantas gravacoes de verdade aconteceram.</summary>
19
10
  public int Writes { get; private set; }
20
11
 
21
- /// <summary>Os caminhos gravados.</summary>
22
12
  public IReadOnlyCollection<string> Paths => _files.Keys.ToArray();
23
13
 
24
- /// <inheritdoc />
25
14
  public ValueTask<bool> ExistsAsync(string relativePath, CancellationToken cancellationToken)
26
15
  => ValueTask.FromResult(_files.ContainsKey(relativePath));
27
16
 
28
- /// <inheritdoc />
29
17
  public ValueTask WriteAsync(string relativePath, ReadOnlyMemory<byte> bytes, CancellationToken cancellationToken)
30
18
  {
31
19
  Writes++;
@@ -34,25 +22,18 @@ public sealed class InMemoryMediaBlobs : IMediaBlobs
34
22
  }
35
23
  }
36
24
 
37
- /// <summary>
38
- /// <see cref="IMediaStore"/> em memoria, para os testes que precisam de UM storage e nao
39
- /// estao medindo o storage.
40
- /// </summary>
41
25
  public sealed class InMemoryMediaStore : IMediaStore
42
26
  {
43
27
  private readonly MediaStore _inner;
44
28
 
45
- /// <summary>Cria o store sobre um blob em memoria proprio.</summary>
46
29
  public InMemoryMediaStore()
47
30
  {
48
31
  Blobs = new InMemoryMediaBlobs();
49
32
  _inner = new MediaStore(Blobs);
50
33
  }
51
34
 
52
- /// <summary>O blob por baixo, para conferir gravacoes.</summary>
53
35
  public InMemoryMediaBlobs Blobs { get; }
54
36
 
55
- /// <inheritdoc />
56
37
  public ValueTask<MediaStoreEntry> PutAsync(
57
38
  ReadOnlyMemory<byte> bytes,
58
39
  string mediaType,
@@ -60,25 +41,20 @@ public sealed class InMemoryMediaStore : IMediaStore
60
41
  => _inner.PutAsync(bytes, mediaType, cancellationToken);
61
42
  }
62
43
 
63
- /// <summary>Acervo de jobs em memoria — a costura com o banco do projeto, em teste.</summary>
64
44
  public sealed class InMemoryMediaJobs : IMediaJobRepository
65
45
  {
66
46
  private readonly ConcurrentDictionary<Guid, MediaJob> _byId = new();
67
47
 
68
- /// <summary>Quantas gravacoes aconteceram. Prova que o worker persistiu cada passo.</summary>
69
48
  public int Saves { get; private set; }
70
49
 
71
- /// <inheritdoc />
72
50
  public ValueTask<MediaJob?> FindByIdempotencyKeyAsync(
73
51
  MediaIdempotencyKey key,
74
52
  CancellationToken cancellationToken)
75
53
  => ValueTask.FromResult(_byId.Values.FirstOrDefault(j => j.IdempotencyKey == key));
76
54
 
77
- /// <inheritdoc />
78
55
  public ValueTask<MediaJob?> FindAsync(MediaJobId id, CancellationToken cancellationToken)
79
56
  => ValueTask.FromResult(_byId.TryGetValue(id.Value, out var job) ? job : null);
80
57
 
81
- /// <inheritdoc />
82
58
  public ValueTask SaveAsync(MediaJob job, CancellationToken cancellationToken)
83
59
  {
84
60
  Saves++;
@@ -87,32 +63,26 @@ public sealed class InMemoryMediaJobs : IMediaJobRepository
87
63
  }
88
64
  }
89
65
 
90
- /// <summary>Resolvedor de alias de teste: um alias, um binding.</summary>
91
66
  public sealed class StubAliasResolver : IMediaAliasResolver
92
67
  {
93
68
  private readonly Dictionary<string, MediaAliasBinding> _bindings = new(StringComparer.Ordinal);
94
69
 
95
- /// <summary>Declara um alias.</summary>
96
70
  public StubAliasResolver With(MediaAliasBinding binding)
97
71
  {
98
72
  _bindings[binding.Alias] = binding;
99
73
  return this;
100
74
  }
101
75
 
102
- /// <inheritdoc />
103
76
  public MediaResult<MediaAliasBinding> Resolve(string alias)
104
77
  => _bindings.TryGetValue(alias, out var binding)
105
78
  ? MediaResult<MediaAliasBinding>.Ok(binding)
106
79
  : MediaResult<MediaAliasBinding>.Fail(MediaErrorCode.InvalidRequest, $"alias '{alias}' nao existe.");
107
80
  }
108
81
 
109
- /// <summary>Teto de custo de teste: aceita ate um limite, e registra o que foi pedido.</summary>
110
82
  public sealed class CappedBudget(decimal cap) : IMediaBudget
111
83
  {
112
- /// <summary>Os custos estimados que chegaram, na ordem.</summary>
113
84
  public List<decimal> Seen { get; } = [];
114
85
 
115
- /// <inheritdoc />
116
86
  public ValueTask<bool> TryReserveAsync(
117
87
  MediaRequest request,
118
88
  decimal estimatedCostUsd,
@@ -123,9 +93,7 @@ public sealed class CappedBudget(decimal cap) : IMediaBudget
123
93
  }
124
94
  }
125
95
 
126
- /// <summary>Relogio parado, para que o carimbo de tempo seja um fato do teste.</summary>
127
96
  public sealed class FixedMediaClock(DateTimeOffset now) : IMediaClock
128
97
  {
129
- /// <inheritdoc />
130
98
  public DateTimeOffset UtcNow { get; } = now;
131
99
  }
@@ -4,33 +4,10 @@ using Xunit;
4
4
 
5
5
  namespace Morph.AiKit.Tests;
6
6
 
7
- /// <summary>
8
- /// O canario da REGRA DURA de <c>ai-agents/media-pipeline.md</c>:
9
- /// <b>nunca gere midia dentro de um turno de conversa</b>.
10
- /// </summary>
11
- /// <remarks>
12
- /// <para>
13
- /// A regra nao e presa por prosa — e presa por ACESSIBILIDADE. O tipo que produz bytes e
14
- /// <c>internal</c> ao assembly; um handler de mensagem no projeto consumidor nao consegue
15
- /// nomea-lo, e o codigo nao compila. Este arquivo e o que mantem essa afirmacao verdadeira
16
- /// depois de qualquer refatoracao.
17
- /// </para>
18
- /// <para>
19
- /// <b>A mutacao foi rodada</b> (T10): tornar <c>IMediaProvider</c> publico derruba
20
- /// <see cref="TheTypeThatProducesBytesIsNotPublic"/> nomeando o defeito. Um canario que
21
- /// ninguem viu morder pode ser o canario errado.
22
- /// </para>
23
- /// <para>
24
- /// A leitura e por <b>reflexao sobre a assembly</b>, e nao por <c>grep</c> no fonte: o
25
- /// modificador que importa e o que sobreviveu a compilacao. Um <c>grep</c> por
26
- /// <c>"internal interface"</c> mediria o texto, e o texto e uma copia do objeto.
27
- /// </para>
28
- /// </remarks>
29
7
  public sealed class MediaAccessibilityTests
30
8
  {
31
9
  private static readonly Assembly Kit = typeof(MediaJob).Assembly;
32
10
 
33
- /// <summary>Os nomes que produzem, ou orquestram a producao de, bytes pagos.</summary>
34
11
  public static TheoryData<string> ByteProducingTypes() =>
35
12
  [
36
13
  "Morph.AiKit.Media.IMediaProvider",
@@ -44,10 +21,6 @@ public sealed class MediaAccessibilityTests
44
21
  "Morph.AiKit.Media.MediaArtifact",
45
22
  ];
46
23
 
47
- /// <summary>
48
- /// A asserção literal do plano:
49
- /// <c>typeof(MediaJob).Assembly.GetType("Morph.AiKit.Media.IMediaProvider")!.IsPublic == false</c>.
50
- /// </summary>
51
24
  [Fact]
52
25
  public void TheTypeThatProducesBytesIsNotPublic()
53
26
  {
@@ -61,7 +34,6 @@ public sealed class MediaAccessibilityTests
61
34
  "compilador. E prosa outra vez.");
62
35
  }
63
36
 
64
- /// <summary>Uma rodada por tipo: nenhum deles pode vazar para fora do assembly.</summary>
65
37
  [Theory]
66
38
  [MemberData(nameof(ByteProducingTypes))]
67
39
  public void NoByteProducingTypeIsVisibleOutsideTheAssembly(string fullName)
@@ -73,11 +45,6 @@ public sealed class MediaAccessibilityTests
73
45
  Assert.False(type.IsNestedPublic, $"{fullName} nao pode ser aninhado publico.");
74
46
  }
75
47
 
76
- /// <summary>
77
- /// O outro lado da mesma afirmacao: a superficie publica EXISTE, e ela nunca devolve
78
- /// bytes. Sem esta metade, tornar tudo internal passaria — e um subsistema inalcancavel
79
- /// tambem esta errado.
80
- /// </summary>
81
48
  [Fact]
82
49
  public void ThePublicEntryPointExistsAndNeverReturnsBytes()
83
50
  {
@@ -101,10 +68,6 @@ public sealed class MediaAccessibilityTests
101
68
  }
102
69
  }
103
70
 
104
- /// <summary>
105
- /// <see cref="MediaArtifact"/> — o registro que CARREGA os bytes — nao pode aparecer em
106
- /// nenhuma assinatura publica do subsistema. Ele existe entre dois tipos internal.
107
- /// </summary>
108
71
  [Fact]
109
72
  public void TheArtifactNeverAppearsInAPublicSignature()
110
73
  {
@@ -5,21 +5,6 @@ using Xunit;
5
5
 
6
6
  namespace Morph.AiKit.Tests;
7
7
 
8
- /// <summary>
9
- /// A companheira obrigatoria de <c>AgentApi.Images</c>/<c>AgentApi.Videos</c> existirem:
10
- /// <b>um alias de midia nao vira agente</b>.
11
- /// </summary>
12
- /// <remarks>
13
- /// <para>
14
- /// Um registry so e um vocabulario so de alias e a decisao (<c>decisions.md</c> D3) — e
15
- /// justamente por isso a recusa precisa existir. Sem ela, um alias de imagem passado por
16
- /// engano viraria um agente de chat apontando para um modelo que nao fala chat: a falha
17
- /// apareceria no primeiro turno, com uma mensagem do provider que nao diz o que aconteceu.
18
- /// </para>
19
- /// <para>
20
- /// Enum novo sem recusa e uma armadilha nova. Este arquivo e o que impede que ela volte.
21
- /// </para>
22
- /// </remarks>
23
8
  public sealed class MediaAliasIsNotAnAgentTests
24
9
  {
25
10
  private const string MediaAliasDocument = """
@@ -52,7 +37,6 @@ public sealed class MediaAliasIsNotAnAgentTests
52
37
  private static AgentSpec Spec(string alias, AgentApi? api = null)
53
38
  => new() { Alias = alias, Instructions = "voce e um agente de teste", Api = api };
54
39
 
55
- /// <summary>O registry ACEITA <c>images</c> e <c>videos</c> — um vocabulario so de alias.</summary>
56
40
  [Theory]
57
41
  [InlineData("imagem", AgentApi.Images)]
58
42
  [InlineData("video", AgentApi.Videos)]
@@ -63,7 +47,6 @@ public sealed class MediaAliasIsNotAnAgentTests
63
47
  Assert.Equal(expected, registry.Resolve(alias).Api);
64
48
  }
65
49
 
66
- /// <summary>E a fabrica RECUSA, na construcao, com erro nomeado.</summary>
67
50
  [Theory]
68
51
  [InlineData("imagem")]
69
52
  [InlineData("video")]
@@ -78,10 +61,6 @@ public sealed class MediaAliasIsNotAnAgentTests
78
61
  Assert.Contains("EnqueueAsync", ex.Message, StringComparison.Ordinal);
79
62
  }
80
63
 
81
- /// <summary>
82
- /// A recusa tambem cobre o caminho pela SPEC: um alias de conversa com
83
- /// <c>Api = Images</c> na spec e a mesma confusao, pela outra porta.
84
- /// </summary>
85
64
  [Fact]
86
65
  public void TheFactoryRefusesAMediaProtocolDeclaredOnTheSpec()
87
66
  {
@@ -93,7 +72,6 @@ public sealed class MediaAliasIsNotAnAgentTests
93
72
  Assert.Equal(AgentFactoryError.MediaAliasIsNotAnAgent, ex.Reason);
94
73
  }
95
74
 
96
- /// <summary>Alias de conversa continua passando — a recusa nova nao pode ser um bloqueio geral.</summary>
97
75
  [Fact]
98
76
  public void AChatAliasStillBuildsAnAgent()
99
77
  {
@@ -103,7 +81,6 @@ public sealed class MediaAliasIsNotAnAgentTests
103
81
  Assert.NotNull(factory.Create(Spec("texto")));
104
82
  }
105
83
 
106
- /// <summary>O predicado mora num lugar so — duas copias e como as duas discordam.</summary>
107
84
  [Theory]
108
85
  [InlineData(AgentApi.Responses, false)]
109
86
  [InlineData(AgentApi.Chat, false)]
@@ -112,7 +89,6 @@ public sealed class MediaAliasIsNotAnAgentTests
112
89
  public void IsMediaAnswersForEveryProtocol(AgentApi api, bool expected)
113
90
  => Assert.Equal(expected, api.IsMedia());
114
91
 
115
- /// <summary>Um <c>api</c> inventado continua sendo recusado na carga do documento.</summary>
116
92
  [Fact]
117
93
  public void AnUnknownApiIsStillRefusedWithTheFullListOfValidValues()
118
94
  {
@@ -4,37 +4,12 @@ using Xunit;
4
4
 
5
5
  namespace Morph.AiKit.Tests;
6
6
 
7
- /// <summary>
8
- /// Canários da MARCAÇÃO — a quarta afirmação da rubrica `media-claims`, e a última que este
9
- /// kit ainda prendia só por prosa.
10
- /// </summary>
11
- /// <remarks>
12
- /// <para>
13
- /// <b>A assimetria que estes testes fecham, medida:</b> a regra do turno de conversa estava
14
- /// presa por TIPO (`internal` + id de job em vez de bytes) e provada por mutação; custo e
15
- /// revisão humana, por ESTADO. A marcação — que é a obrigação legal, o art. 50 do AI Act que
16
- /// motivou a issue — estava escrita em `MediaJobState.cs` ("publicado, com a marcacao
17
- /// aplicada") e em `MediaRequest.cs` ("marcacao visivel obrigatoria"), e um `grep` por label,
18
- /// watermark ou disclosure no kit inteiro voltava <b>vazio</b>. Zero mecanismo.
19
- /// </para>
20
- /// <para>
21
- /// Uma rubrica que cobra de terceiros uma prova que o próprio exemplar não dá é o defeito que
22
- /// esta epic catalogou. Estes testes são o inverso literal dele.
23
- /// </para>
24
- /// <para>
25
- /// <b>Fronteira honesta:</b> nada aqui prova que o rótulo chegou aos olhos de alguém — a
26
- /// renderização é do aplicativo consumidor, e nenhum tipo em C# alcança um pixel. O que se
27
- /// prova é que a publicação <b>não acontece</b> sem a decisão registrada, com texto e
28
- /// superfície nomeados.
29
- /// </para>
30
- /// </remarks>
31
7
  public sealed class MediaDisclosureTests
32
8
  {
33
9
  private static readonly DateTimeOffset T0 = new(2026, 9, 8, 12, 0, 0, TimeSpan.Zero);
34
10
 
35
11
  private static MediaDisclosure Label => MediaDisclosure.Create("Imagem gerada por IA", "legenda", T0);
36
12
 
37
- /// <summary>Um job aprovado, na finalidade pedida.</summary>
38
13
  private static MediaJob Approved(MediaPurpose purpose)
39
14
  {
40
15
  var job = MediaJob.Create(
@@ -50,11 +25,6 @@ public sealed class MediaDisclosureTests
50
25
  return job.Approve(MediaReview.ApprovedBy("lucas", T0)).Value;
51
26
  }
52
27
 
53
- // ---------------------------------------------------------------------------------
54
- // A política, por finalidade — dado, não comentário
55
- // ---------------------------------------------------------------------------------
56
-
57
- /// <summary>Catálogo e marketing exigem marcação; rascunho não chega a publicar.</summary>
58
28
  [Theory]
59
29
  [InlineData(MediaPurpose.CatalogPhoto, true)]
60
30
  [InlineData(MediaPurpose.Marketing, true)]
@@ -62,7 +32,6 @@ public sealed class MediaDisclosureTests
62
32
  public void RequiresDisclosureIsDeclaredPerPurpose(MediaPurpose purpose, bool expected)
63
33
  => Assert.Equal(expected, purpose.RequiresDisclosure());
64
34
 
65
- /// <summary>Rascunho é a única finalidade não publicável.</summary>
66
35
  [Theory]
67
36
  [InlineData(MediaPurpose.CatalogPhoto, true)]
68
37
  [InlineData(MediaPurpose.Marketing, true)]
@@ -70,14 +39,6 @@ public sealed class MediaDisclosureTests
70
39
  public void OnlyDraftIsUnpublishable(MediaPurpose purpose, bool expected)
71
40
  => Assert.Equal(expected, purpose.IsPublishable());
72
41
 
73
- // ---------------------------------------------------------------------------------
74
- // A porta: publicar sem marcação NÃO é um caminho
75
- // ---------------------------------------------------------------------------------
76
-
77
- /// <summary>
78
- /// O caso central. Um job aprovado, de catálogo, sem marcação: <b>não publica</b>, e a
79
- /// razão nomeia o que falta.
80
- /// </summary>
81
42
  [Theory]
82
43
  [InlineData(MediaPurpose.CatalogPhoto)]
83
44
  [InlineData(MediaPurpose.Marketing)]
@@ -93,7 +54,6 @@ public sealed class MediaDisclosureTests
93
54
  Assert.Contains("marcacao", result.Error.Message, StringComparison.OrdinalIgnoreCase);
94
55
  }
95
56
 
96
- /// <summary>Com a marcação, publica — e ela fica GRAVADA no job, não descartada.</summary>
97
57
  [Fact]
98
58
  public void PublishSucceedsWithADisclosureAndRecordsIt()
99
59
  {
@@ -106,10 +66,6 @@ public sealed class MediaDisclosureTests
106
66
  Assert.Equal(T0, published.Value.Disclosure.AppliedAt);
107
67
  }
108
68
 
109
- /// <summary>
110
- /// Rascunho não publica, <b>nem com marcação e nem estando aprovado</b>. Era uma frase
111
- /// num comentário XML; agora é uma recusa.
112
- /// </summary>
113
69
  [Fact]
114
70
  public void ADraftNeverPublishesEvenApprovedAndLabelled()
115
71
  {
@@ -119,11 +75,6 @@ public sealed class MediaDisclosureTests
119
75
  Assert.Contains("nao publica", result.Error!.Message, StringComparison.OrdinalIgnoreCase);
120
76
  }
121
77
 
122
- /// <summary>
123
- /// A ordem das portas importa: o estado vem antes da marcação. Um job NÃO aprovado, sem
124
- /// marcação, tem de reclamar da revisão humana — mandar consertar a marcação de algo que
125
- /// nem foi revisado manda consertar a coisa errada.
126
- /// </summary>
127
78
  [Fact]
128
79
  public void TheStateGateIsReportedBeforeTheDisclosureGate()
129
80
  {
@@ -142,29 +93,6 @@ public sealed class MediaDisclosureTests
142
93
  Assert.DoesNotContain("marcacao", result.Error.Message, StringComparison.OrdinalIgnoreCase);
143
94
  }
144
95
 
145
- // ---------------------------------------------------------------------------------
146
- // A marcação vazia é o campo preenchido "para passar"
147
- // ---------------------------------------------------------------------------------
148
-
149
- /// <summary>
150
- /// <b>A construção não tem outra porta.</b> Este é o teste que o nome do anterior prometia
151
- /// e não entregava.
152
- /// </summary>
153
- /// <remarks>
154
- /// <para>
155
- /// A primeira versão de <see cref="MediaDisclosure"/> era um record POSICIONAL, e record
156
- /// posicional gera construtor <b>público</b>. A validação vivia só em
157
- /// <see cref="MediaDisclosure.Create"/>, então um consumidor externo fazia
158
- /// <c>new MediaDisclosure("", "", at)</c> e publicava com rótulo vazio — medido:
159
- /// <c>IsSuccess=True State=Published</c>. O canário de então exercitava a FÁBRICA e passava
160
- /// verde o tempo todo: media a copia, nao o objeto.
161
- /// </para>
162
- /// <para>
163
- /// Por isso a asserção é por REFLEXÃO sobre o tipo. Ela não pode ser escrita como
164
- /// <c>new MediaDisclosure(...)</c> — hoje isso nem compila, e é esse o ponto: uma asserção
165
- /// que só existe enquanto o defeito existe não é canário.
166
- /// </para>
167
- /// </remarks>
168
96
  [Fact]
169
97
  public void TheTypeExposesNoPublicConstructor()
170
98
  {
@@ -179,7 +107,6 @@ public sealed class MediaDisclosureTests
179
107
  "MediaReview. Encontrados: " + string.Join(" | ", publicCtors.Select(c => c.ToString())));
180
108
  }
181
109
 
182
- /// <summary>Texto ou superfície em branco é recusado pela única porta que existe.</summary>
183
110
  [Theory]
184
111
  [InlineData("", "legenda")]
185
112
  [InlineData(" ", "legenda")]
@@ -188,10 +115,6 @@ public sealed class MediaDisclosureTests
188
115
  public void ABlankDisclosureIsRefusedByTheOnlyFactory(string text, string surface)
189
116
  => Assert.Throws<ArgumentException>(() => MediaDisclosure.Create(text, surface, T0));
190
117
 
191
- /// <summary>
192
- /// A superfície é obrigatória junto do texto: "marcado" sem dizer ONDE não responde se
193
- /// alguém consegue ver.
194
- /// </summary>
195
118
  [Fact]
196
119
  public void TheDisclosureCarriesBothTheTextAndWhereItAppears()
197
120
  {
@@ -201,7 +124,6 @@ public sealed class MediaDisclosureTests
201
124
  Assert.Equal("sobreposicao", disclosure.Surface);
202
125
  }
203
126
 
204
- /// <summary>Uma publicação recusada não muda o job — o registro continua imutável.</summary>
205
127
  [Fact]
206
128
  public void ARefusedPublishLeavesTheJobUntouched()
207
129
  {