@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
@@ -6,79 +6,31 @@ using Microsoft.Extensions.AI.Evaluation;
6
6
 
7
7
  namespace Morph.AiKit.Evals;
8
8
 
9
- /// <summary>O que uma assercao deterministica pergunta a uma resposta.</summary>
10
- /// <remarks>
11
- /// A lista e curta de proposito. Toda entrada aqui e uma propriedade que se decide sem
12
- /// julgamento: ou o texto contem, ou nao contem; ou a ferramenta foi escolhida, ou nao foi.
13
- /// Uma "assercao" que precisasse de um modelo para decidir seria juizo, e juizo nao reprova
14
- /// sozinho neste kit.
15
- /// </remarks>
16
9
  public enum EvalAssertionKind
17
10
  {
18
- /// <summary>O texto da resposta contem o valor (ignorando caixa).</summary>
19
11
  Contains = 0,
20
12
 
21
- /// <summary>O texto da resposta NAO contem o valor (ignorando caixa).</summary>
22
13
  NotContains = 1,
23
14
 
24
- /// <summary>A resposta e um objeto JSON e tem a chave de topo indicada em <c>Target</c>.</summary>
25
15
  JsonHasKey = 2,
26
16
 
27
- /// <summary>A resposta nao carrega dado pessoal nas formas que o kit sabe reconhecer.</summary>
28
17
  NoPii = 3,
29
18
 
30
- /// <summary>A resposta escolheu a ferramenta nomeada em <c>Target</c>.</summary>
31
19
  ToolCalled = 4,
32
20
 
33
- /// <summary>A ferramenta <c>Target</c> foi chamada com o argumento <c>Value</c> no formato <c>chave=valor</c>.</summary>
34
21
  ToolArgument = 5,
35
22
  }
36
23
 
37
- /// <summary>Veredito de uma assercao: o booleano e a frase que o explica.</summary>
38
- /// <param name="Passed">Se a propriedade se sustentou.</param>
39
- /// <param name="Reason">Por que — nomeando o que foi procurado, para nao mandar o leitor adivinhar.</param>
40
24
  public sealed record EvalAssertionOutcome(bool Passed, string Reason)
41
25
  {
42
- /// <summary>Nome da assercao que produziu este veredito.</summary>
43
- /// <remarks>
44
- /// Carimbado por <see cref="EvalAssertion.Evaluate"/>. Existe porque uma falha que chega a
45
- /// jusante como contador ("Expected 0, Actual 1") nao diz O QUE quebrou: quem le precisa
46
- /// abrir o dataset e adivinhar. Um eval que morde anonimamente obriga a investigacao que
47
- /// ele deveria dispensar.
48
- /// </remarks>
49
26
  public string? Assertion { get; init; }
50
27
 
51
- /// <summary>O tipo da assercao que julgou — a REGUA, nao so o nome dela.</summary>
52
28
  public EvalAssertionKind? Kind { get; init; }
53
29
  }
54
30
 
55
- /// <summary>
56
- /// A assercao deterministica: o unico tipo de avaliador que REPROVA neste kit.
57
- /// </summary>
58
- /// <remarks>
59
- /// <para>
60
- /// <b>Por que so ela decide.</b> Os avaliadores de qualidade da biblioteca sao juizo de LLM,
61
- /// e juizo de LLM varia. Um gate que reprovasse por variancia de juiz reintroduziria o
62
- /// FLAKY que faz suites de eval serem desligadas do CI — e um no de eval desligado nao
63
- /// verifica nada. Entao a divisao e explicita: assercao deterministica decide; qualidade e
64
- /// numero reportado.
65
- /// </para>
66
- /// <para>
67
- /// <b>Ela e um <see cref="IEvaluator"/> de verdade</b>, e nao um predicado aplicado por
68
- /// fora, para que a metrica caia no MESMO relatorio que as de qualidade. Um resultado que
69
- /// vivesse fora do relatorio precisaria de um segundo lugar para ser lido, e o segundo lugar
70
- /// e onde as duas verdades comecam.
71
- /// </para>
72
- /// </remarks>
73
31
  public sealed record EvalAssertion : IEvaluator
74
32
  {
75
- /// <summary>
76
- /// Formas de dado pessoal que o kit reconhece. Deliberadamente poucas e literais: um
77
- /// detector de PII abrangente e um projeto proprio, e prometer mais do que se mede seria
78
- /// pior do que nao prometer. Sem <c>\b</c> em nenhum padrao — a fronteira de palavra do
79
- /// .NET tambem nao trata acento como letra em toda cultura, e um padrao que nao casa e
80
- /// pior que um padrao que nao existe.
81
- /// </summary>
33
+ // Sem \b: a fronteira de palavra do .NET nao trata acento como letra em toda cultura.
82
34
  private static readonly (string Label, Regex Pattern)[] PiiPatterns =
83
35
  [
84
36
  ("e-mail", new Regex("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+[.][A-Za-z]{2,}", RegexOptions.CultureInvariant)),
@@ -86,26 +38,17 @@ public sealed record EvalAssertion : IEvaluator
86
38
  ("telefone", new Regex("[(][0-9]{2}[)] ?[0-9]{4,5}-[0-9]{4}", RegexOptions.CultureInvariant)),
87
39
  ];
88
40
 
89
- /// <summary>
90
- /// Nome da metrica. UNICO dentro de um cenario: a biblioteca indexa metricas por nome, e
91
- /// duas assercoes homonimas fariam a segunda sobrescrever a primeira em silencio — um
92
- /// cenario que parece cobrar duas propriedades e cobra uma.
93
- /// </summary>
41
+ // Unico por cenario: a biblioteca indexa metricas por nome e a homonima sobrescreve em silencio.
94
42
  public required string Name { get; init; }
95
43
 
96
- /// <summary>O que esta assercao pergunta.</summary>
97
44
  public required EvalAssertionKind Kind { get; init; }
98
45
 
99
- /// <summary>Chave JSON ou nome de ferramenta, conforme o <see cref="Kind"/>.</summary>
100
46
  public string? Target { get; init; }
101
47
 
102
- /// <summary>Texto procurado, ou <c>chave=valor</c> em <see cref="EvalAssertionKind.ToolArgument"/>.</summary>
103
48
  public string? Value { get; init; }
104
49
 
105
- /// <inheritdoc />
106
50
  public IReadOnlyCollection<string> EvaluationMetricNames => [Name];
107
51
 
108
- /// <summary>Aplica a assercao a uma resposta. Puro: nao le disco, nao le ambiente, nao chama rede.</summary>
109
52
  public EvalAssertionOutcome Evaluate(ChatResponse response)
110
53
  {
111
54
  ArgumentNullException.ThrowIfNull(response);
@@ -135,12 +78,9 @@ public sealed record EvalAssertion : IEvaluator
135
78
  _ => new EvalAssertionOutcome(false, $"tipo de assercao desconhecido: {Kind}"),
136
79
  };
137
80
 
138
- // Carimba QUEM julgou. O veredito sozinho e um booleano; com o nome e a regua ele vira
139
- // uma frase que nomeia o defeito sem obrigar ninguem a abrir o dataset.
140
81
  return outcome with { Assertion = Name, Kind = this.Kind };
141
82
  }
142
83
 
143
- /// <inheritdoc />
144
84
  public ValueTask<EvaluationResult> EvaluateAsync(
145
85
  IEnumerable<ChatMessage> messages,
146
86
  ChatResponse modelResponse,
@@ -156,7 +96,6 @@ public sealed record EvalAssertion : IEvaluator
156
96
  return ValueTask.FromResult(new EvaluationResult(metric));
157
97
  }
158
98
 
159
- /// <summary>Texto concatenado da resposta — a superficie que as assercoes de texto leem.</summary>
160
99
  internal static string TextOf(ChatResponse response)
161
100
  {
162
101
  var sb = new StringBuilder();
@@ -1,36 +1,9 @@
1
1
  namespace Morph.AiKit.Evals;
2
2
 
3
- /// <summary>
4
- /// Lancada quando um cenario de eval NAO estava na cache e o modo corrente e
5
- /// <see cref="EvalMode.Offline"/> — o unico caminho pelo qual a suite tocaria a rede.
6
- /// </summary>
7
- /// <remarks>
8
- /// <para>
9
- /// <b>Por que uma excecao, e nao um resultado.</b> A regra do kit e "excecao nunca como
10
- /// fluxo de controle"; esta e a excecao de CONTRATO que a propria regra admite. O ponto de
11
- /// deteccao e o <see cref="OfflineChatClient"/>, que implementa <c>IChatClient</c> — uma
12
- /// interface de terceiro cuja assinatura devolve <c>Task&lt;ChatResponse&gt;</c> e nao tem
13
- /// onde carregar um veredito. Devolver uma resposta vazia ali seria pior do que lancar:
14
- /// o eval ficaria VERDE sobre um cenario que ninguem avaliou, que e exatamente o defeito
15
- /// (gate verde sobre verificacao que nao aconteceu) que esta feature existe para fechar.
16
- /// </para>
17
- /// <para>
18
- /// <b>A mensagem e o produto.</b> Em CI, com a cache commitada, um miss significa uma coisa
19
- /// so: o prompt ou o modelo mudou e ninguem regravou. Entao a mensagem nomeia as quatro
20
- /// coisas que o leitor precisa para agir sem abrir o codigo — o cenario, o caminho da
21
- /// cache, o comando de regravacao e o arquivo de credencial esperado. Um "cache miss"
22
- /// seco mandaria o leitor procurar as quatro.
23
- /// </para>
24
- /// </remarks>
25
3
  public sealed class EvalCacheMissException : Exception
26
4
  {
27
- /// <summary>Comando literal que regrava a arvore de cache. Nunca implicito, nunca automatico.</summary>
28
5
  public const string RefreshCommand = "morph-spec verify {feature} --refresh-cache";
29
6
 
30
- /// <summary>Cria a excecao com todos os dados que a mensagem precisa nomear.</summary>
31
- /// <param name="scenarioName">O cenario que faltou na cache.</param>
32
- /// <param name="cachePath">Raiz da arvore de cache consultada.</param>
33
- /// <param name="envFile">Arquivo (gitignored) de onde a credencial viria numa regravacao.</param>
34
7
  public EvalCacheMissException(string scenarioName, string cachePath, string envFile)
35
8
  : base(BuildMessage(scenarioName, cachePath, envFile))
36
9
  {
@@ -39,13 +12,10 @@ public sealed class EvalCacheMissException : Exception
39
12
  EnvFile = envFile;
40
13
  }
41
14
 
42
- /// <summary>O cenario que faltou na cache.</summary>
43
15
  public string ScenarioName { get; }
44
16
 
45
- /// <summary>Raiz da arvore de cache consultada.</summary>
46
17
  public string CachePath { get; }
47
18
 
48
- /// <summary>Arquivo de credencial esperado por uma regravacao.</summary>
49
19
  public string EnvFile { get; }
50
20
 
51
21
  private static string BuildMessage(string scenarioName, string cachePath, string envFile)
@@ -1,78 +1,27 @@
1
1
  namespace Morph.AiKit.Evals;
2
2
 
3
- /// <summary>
4
- /// Tudo o que o <see cref="EvalFixture"/> precisa saber, e nada que ele possa adivinhar.
5
- /// </summary>
6
- /// <remarks>
7
- /// <para>
8
- /// <b><see cref="TimeToLive"/> nao tem valor default, e isso e o item mais importante deste
9
- /// tipo.</b> O default da biblioteca e de 14 dias E A EXPIRACAO E DESTRUTIVA NA LEITURA: ao
10
- /// achar uma entrada vencida, o provedor APAGA o diretorio da chave e devolve nulo. Uma
11
- /// cache commitada com o default se apaga sozinha ao ser lida, duas semanas depois de
12
- /// gravada — e o sintoma chega como miss em massa (e arvore suja no <c>git status</c>) num
13
- /// dia em que ninguem mexeu em nada. Por isso o valor e obrigatorio aqui e o fixture recusa
14
- /// qualquer coisa abaixo de <see cref="MinimumTimeToLive"/>.
15
- /// </para>
16
- /// <para>
17
- /// <b><see cref="ProviderName"/> e <see cref="DefaultModelId"/> entram na chave de cache</b>
18
- /// e por isso viajam como configuracao, nao como constante: eles precisam reproduzir o run
19
- /// que GRAVOU a arvore. O endpoint nao entra na chave (medido na v10.9.0), que e o que
20
- /// permite ao cliente offline nao ter endpoint algum.
21
- /// </para>
22
- /// </remarks>
23
3
  public sealed record EvalCacheOptions
24
4
  {
25
- /// <summary>
26
- /// Piso do tempo de vida das entradas. Um ano nao e um numero bonito: e o menor prazo
27
- /// que garante que uma cache commitada sobreviva a um ciclo de release sem se apagar
28
- /// durante uma leitura.
29
- /// </summary>
30
5
  public static readonly TimeSpan MinimumTimeToLive = TimeSpan.FromDays(365);
31
6
 
32
- /// <summary>Raiz da arvore de cache. COMMITADA.</summary>
33
7
  public required string CacheRoot { get; init; }
34
8
 
35
- /// <summary>
36
- /// Raiz dos resultados de execucao. FORA da arvore commitada — por construcao, e nao por
37
- /// um <c>.gitignore</c> dentro de um diretorio commitado (regra de ignore ali e regra que
38
- /// a proxima pessoa remove sem entender). Os resultados carregam latencia medida por
39
- /// cronometro, uso de tokens e o flag de cache hit: commita-los e diff garantido em toda
40
- /// rodada, sem sinal nenhum.
41
- /// </summary>
42
9
  public required string ResultsRoot { get; init; }
43
10
 
44
- /// <summary>Raiz do dataset versionado, em <c>{agente}/{tenant}/{cenario}.json</c>.</summary>
45
11
  public required string DatasetRoot { get; init; }
46
12
 
47
- /// <summary>Tempo de vida das entradas. Obrigatorio, sempre — ver as observacoes do tipo.</summary>
13
+ // Sem default: o da biblioteca (14 dias) apaga a entrada vencida durante a leitura da arvore commitada.
48
14
  public required TimeSpan TimeToLive { get; init; }
49
15
 
50
- /// <summary>O <c>ProviderName</c> que entra na chave de cache.</summary>
51
16
  public required string ProviderName { get; init; }
52
17
 
53
- /// <summary>O <c>DefaultModelId</c> que entra na chave de cache. Nulo e um valor.</summary>
54
18
  public string? DefaultModelId { get; init; }
55
19
 
56
- /// <summary>De onde a resposta pode vir nesta execucao.</summary>
57
20
  public required EvalMode Mode { get; init; }
58
21
 
59
- /// <summary>
60
- /// Onde o sumario JSON e escrito. O CAMINHO E DITADO PELO CHAMADOR (o CLI escolhe um
61
- /// arquivo temporario) para que dois nos de eval no mesmo <c>verify</c> nao colidam.
62
- /// Nulo quando ninguem quer sumario — rodar sem sumario e legitimo, inventar um caminho
63
- /// nao e.
64
- /// </summary>
65
22
  public string? SummaryPath { get; init; }
66
23
 
67
- /// <summary>
68
- /// Arquivo (gitignored) de onde a credencial viria numa regravacao. So aparece em
69
- /// mensagem de erro; o fixture nunca o le.
70
- /// </summary>
71
24
  public string EnvFile { get; init; } = ".env.evals";
72
25
 
73
- /// <summary>
74
- /// O pin corrente, como o CLI o entregou. Nulo desliga a checagem de carimbo — util para
75
- /// um projeto que constroi o fixture a mao, e explicitamente NAO o caminho do CLI.
76
- /// </summary>
77
26
  public EvalPin? Pin { get; init; }
78
27
  }
@@ -3,84 +3,34 @@ using System.Text.Json.Serialization;
3
3
 
4
4
  namespace Morph.AiKit.Evals;
5
5
 
6
- /// <summary>O veredito de um carimbo de cache — quatro estados, nunca um booleano.</summary>
7
- /// <remarks>
8
- /// <c>Missing</c> e <c>PinMismatch</c> sao SEPARADOS de proposito: as duas situacoes pedem
9
- /// acoes diferentes. A primeira e "regrave"; a segunda e "regrave <b>porque o pacote
10
- /// mudou</b>". Colapsa-las num unico "carimbo invalido" devolve ao leitor a pergunta que o
11
- /// carimbo existia para responder.
12
- /// </remarks>
13
6
  public enum EvalStampVerdict
14
7
  {
15
- /// <summary>Carimbo presente e de acordo com o pin corrente.</summary>
16
8
  Ok = 0,
17
9
 
18
- /// <summary>Nao ha carimbo: cache anterior a esta feature, ou diretorio criado a mao.</summary>
19
10
  Missing = 1,
20
11
 
21
- /// <summary>Ha um arquivo, mas ele nao e JSON legivel no formato do carimbo.</summary>
22
12
  Unreadable = 2,
23
13
 
24
- /// <summary>As versoes gravadas divergem do pin corrente — a chave de cache pode ter mudado.</summary>
25
14
  PinMismatch = 3,
26
15
 
27
- /// <summary>O <c>pinnedAt</c> do pin corrente esta no futuro. Erro de pin, jamais cache fresca.</summary>
28
16
  FuturePin = 4,
29
17
  }
30
18
 
31
- /// <summary>Resultado de <see cref="EvalCacheStamp.Check"/>: veredito mais a frase que o explica.</summary>
32
- /// <param name="Verdict">O estado apurado.</param>
33
- /// <param name="Reason">
34
- /// Frase pronta para o relatorio. Nos dois vereditos que nomeiam versoes, ela cita AS DUAS
35
- /// (a gravada e a do pin) — dizer so uma obriga o leitor a ir buscar a outra.
36
- /// </param>
37
19
  public sealed record EvalCacheStampCheck(EvalStampVerdict Verdict, string Reason)
38
20
  {
39
- /// <summary>Atalho de leitura: o veredito unico que permite avaliar cenario.</summary>
40
21
  public bool IsOk => Verdict == EvalStampVerdict.Ok;
41
22
  }
42
23
 
43
- /// <summary>
44
- /// O pin, do jeito que o fixture o recebe: data de apuracao mais pacote-para-versao.
45
- /// </summary>
46
- /// <remarks>
47
- /// Achatado (nome para versao) e nao a forma completa de <c>framework/ai-pin.json</c>
48
- /// porque quem o produz e o CLI, na variavel <c>MORPH_EVAL_PIN</c>, e o kit nao le o pin do
49
- /// disco: um segundo parser de <c>ai-pin.json</c> e como as duas verdades comecam. Aqui
50
- /// entram apenas os pacotes que decidem o FORMATO da cache — a familia
51
- /// <c>Microsoft.Extensions.AI</c> e as tres de Evaluation. Subir o provider da OpenAI nao
52
- /// invalida uma arvore de cache, e um carimbo que reagisse a isso reprovaria por nada.
53
- /// </remarks>
54
- /// <param name="PinnedAt">Data ISO em que um humano conferiu as versoes.</param>
55
- /// <param name="Packages">Nome do pacote para versao exata.</param>
24
+ // So os pacotes que decidem o formato da cache; o kit nunca le ai-pin.json do disco.
56
25
  public sealed record EvalPin(
57
26
  [property: JsonPropertyName("pinnedAt")] string PinnedAt,
58
27
  [property: JsonPropertyName("packages")] IReadOnlyDictionary<string, string> Packages);
59
28
 
60
- /// <summary>
61
- /// O carimbo que fica na raiz da arvore de cache commitada, e a regra que o julga.
62
- /// </summary>
63
- /// <remarks>
64
- /// <para>
65
- /// <b>Por que existe.</b> A funcao de hash que produz a chave de cache nao promete ser
66
- /// estavel entre versoes da biblioteca — a propria biblioteca carrega um numero de versao
67
- /// de cache interno, para invalidar tudo quando o formato muda. Ou seja: SUBIR O PIN PODE
68
- /// INVALIDAR A ARVORE INTEIRA. Sem carimbo isso chega como N misses inexplicaveis num dia
69
- /// em que ninguem mexeu em nada. Com carimbo, chega como uma frase que nomeia as duas
70
- /// versoes e o comando de regravacao.
71
- /// </para>
72
- /// <para>
73
- /// <b>Ele nao substitui a leitura da cache</b> — a chave continua sendo computada pela
74
- /// biblioteca. Ele responde antes, e de graca, a pergunta cuja resposta cara seria "rodou a
75
- /// suite inteira para descobrir que a cache nao servia".
76
- /// </para>
77
- /// </remarks>
29
+ // A chave de cache da biblioteca nao e estavel entre versoes: subir o pin pode invalidar a arvore inteira.
78
30
  public sealed record EvalCacheStamp
79
31
  {
80
- /// <summary>Nome do arquivo, na raiz de <c>cachePath</c>. Uma unica grafia.</summary>
81
32
  public const string FileName = ".morph-eval-cache.json";
82
33
 
83
- /// <summary>Quem grava. Um carimbo de outra origem e um carimbo que ninguem mantem.</summary>
84
34
  public const string Writer = "morph-eval-fixture";
85
35
 
86
36
  private static readonly JsonSerializerOptions Json = new()
@@ -89,34 +39,27 @@ public sealed record EvalCacheStamp
89
39
  PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
90
40
  };
91
41
 
92
- /// <summary>O <c>pinnedAt</c> vigente quando a arvore foi gravada.</summary>
93
42
  [JsonPropertyName("pinnedAt")]
94
43
  public required string PinnedAt { get; init; }
95
44
 
96
- /// <summary>As versoes de pacote sob as quais a arvore foi gravada.</summary>
97
45
  [JsonPropertyName("packages")]
98
46
  public required IReadOnlyDictionary<string, string> Packages { get; init; }
99
47
 
100
- /// <summary>O <c>ProviderName</c> que entrou na chave.</summary>
101
48
  [JsonPropertyName("provider")]
102
49
  public required string Provider { get; init; }
103
50
 
104
- /// <summary>O <c>DefaultModelId</c> que entrou na chave. Nulo e um valor, nao um erro.</summary>
105
51
  [JsonPropertyName("model")]
106
52
  public string? Model { get; init; }
107
53
 
108
- /// <summary>Quem gravou o carimbo.</summary>
109
54
  [JsonPropertyName("writtenBy")]
110
55
  public string WrittenBy { get; init; } = Writer;
111
56
 
112
- /// <summary>Caminho do arquivo de carimbo dentro de uma raiz de cache.</summary>
113
57
  public static string PathFor(string cacheRoot)
114
58
  {
115
59
  ArgumentException.ThrowIfNullOrWhiteSpace(cacheRoot);
116
60
  return Path.Combine(cacheRoot, FileName);
117
61
  }
118
62
 
119
- /// <summary>Grava o carimbo. Cria a raiz se ela ainda nao existe.</summary>
120
63
  public static void Write(string cacheRoot, EvalCacheStamp stamp)
121
64
  {
122
65
  ArgumentException.ThrowIfNullOrWhiteSpace(cacheRoot);
@@ -124,17 +67,10 @@ public sealed record EvalCacheStamp
124
67
 
125
68
  Directory.CreateDirectory(cacheRoot);
126
69
 
127
- // Quebras de linha normalizadas para LF e uma quebra final: o carimbo e um arquivo
128
- // COMMITADO. Sem isso ele produziria diff que nao e diff — CRLF numa maquina, LF
129
- // noutra, e nenhuma quebra no fim em lugar nenhum.
130
70
  var json = JsonSerializer.Serialize(stamp, Json).ReplaceLineEndings("\n");
131
71
  File.WriteAllText(PathFor(cacheRoot), json + "\n");
132
72
  }
133
73
 
134
- /// <summary>
135
- /// Le o carimbo. Devolve <c>null</c> quando o arquivo nao existe OU nao parseia —
136
- /// <see cref="Check"/> e quem distingue os dois casos, porque so ela tem as palavras.
137
- /// </summary>
138
74
  public static EvalCacheStamp? Read(string cacheRoot)
139
75
  {
140
76
  ArgumentException.ThrowIfNullOrWhiteSpace(cacheRoot);
@@ -155,24 +91,13 @@ public sealed record EvalCacheStamp
155
91
  }
156
92
  }
157
93
 
158
- /// <summary>
159
- /// O carimbo desta arvore contra o pin corrente.
160
- /// </summary>
161
- /// <param name="cacheRoot">Raiz da arvore de cache.</param>
162
- /// <param name="pin">O pin corrente, como o CLI o entregou.</param>
163
- /// <param name="timeProvider">
164
- /// Relogio injetado — a comparacao de <c>pinnedAt</c> com hoje e uma leitura de relogio,
165
- /// e um teste que dependesse do relogio da maquina mudaria de veredito na virada do dia.
166
- /// </param>
167
94
  public static EvalCacheStampCheck Check(string cacheRoot, EvalPin pin, TimeProvider timeProvider)
168
95
  {
169
96
  ArgumentException.ThrowIfNullOrWhiteSpace(cacheRoot);
170
97
  ArgumentNullException.ThrowIfNull(pin);
171
98
  ArgumentNullException.ThrowIfNull(timeProvider);
172
99
 
173
- // O pin vem ANTES do carimbo: um pin com data no futuro renova sozinho qualquer
174
- // janela medida contra ele, e comparar um carimbo contra uma regua adulterada
175
- // produziria um "ok" que nao significa nada.
100
+ // O pin vem antes do carimbo: um pin com data no futuro renovaria qualquer janela medida contra ele.
176
101
  var today = timeProvider.GetUtcNow().UtcDateTime.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
177
102
  if (string.CompareOrdinal(pin.PinnedAt, today) > 0)
178
103
  {