@polymorphism-tech/morph-spec 8.37.1 → 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 (317) hide show
  1. package/CLAUDE.md +37 -13
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +75 -49
  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 +6 -5
  8. package/framework/MORPH.md +25 -22
  9. package/framework/agents.json +96 -36
  10. package/framework/docs/CLI.md +54 -17
  11. package/framework/evals/rubrics/code-quality.md +5 -0
  12. package/framework/evals/rubrics/condensed.md +6 -2
  13. package/framework/evals/rubrics/contracts.md +20 -0
  14. package/framework/evals/rubrics/test-coverage.md +17 -6
  15. package/framework/hooks/README.md +9 -3
  16. package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
  17. package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
  18. package/framework/hooks/claude-code/core/validate-completion.js +16 -90
  19. package/framework/hooks/claude-code/core/worktree-context.js +10 -0
  20. package/framework/hooks/shared/command-tokens.js +96 -0
  21. package/framework/hooks/shared/git-exclude.js +44 -0
  22. package/framework/hooks/shared/git-paths.js +52 -0
  23. package/framework/hooks/shared/graph-freshness.js +380 -0
  24. package/framework/hooks/shared/graph-refresh.js +416 -0
  25. package/framework/hooks/shared/morph-cli.js +17 -5
  26. package/framework/hooks/shared/redact-secrets.js +55 -0
  27. package/framework/hooks/shared/telemetry-logger.js +25 -2
  28. package/framework/hooks/shared/transcript-usage.js +80 -0
  29. package/framework/mcp/registry.json +33 -2
  30. package/framework/rubrics/backend.json +2 -2
  31. package/framework/rubrics/default.json +2 -2
  32. package/framework/rubrics/frontend.json +2 -2
  33. package/framework/rules/csharp-standards.md +1 -0
  34. package/framework/rules/infrastructure-standards.md +0 -1
  35. package/framework/rules/morph-workflow.md +10 -4
  36. package/framework/rules/nextjs-standards.md +1 -0
  37. package/framework/schemas/config.schema.json +41 -1
  38. package/framework/schemas/feature.schema.json +5 -1
  39. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  40. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  41. package/framework/skills/core/morph-apply/SKILL.md +40 -11
  42. package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
  43. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  44. package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
  45. package/framework/skills/core/morph-implement/SKILL.md +44 -10
  46. package/framework/skills/core/morph-media/SKILL.md +1 -1
  47. package/framework/skills/core/morph-plan/SKILL.md +73 -15
  48. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  49. package/framework/skills/core/morph-review/SKILL.md +51 -13
  50. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  51. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  52. package/framework/skills/core/morph-uiux/SKILL.md +39 -13
  53. package/framework/standards/STANDARDS.json +189 -14
  54. package/framework/standards/ai-agents/context-providers.md +16 -26
  55. package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
  56. package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
  57. package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
  58. package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
  59. package/framework/standards/ai-agents/modalities-vision.md +1 -18
  60. package/framework/standards/ai-agents/prompt-sources.md +0 -4
  61. package/framework/standards/ai-agents/testing-ai.md +2 -1
  62. package/framework/standards/architecture/code-comments.md +100 -0
  63. package/framework/standards/backend/dotnet/testing.md +22 -0
  64. package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
  65. package/framework/standards/frontend/admin-app/data-table.md +562 -0
  66. package/framework/standards/frontend/admin-app/feedback.md +236 -0
  67. package/framework/standards/frontend/admin-app/forms.md +232 -0
  68. package/framework/standards/frontend/admin-app/page-structure.md +203 -0
  69. package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
  70. package/framework/standards/frontend/nextjs/components.md +37 -58
  71. package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
  72. package/framework/standards/frontend/nextjs/forms.md +31 -30
  73. package/framework/standards/frontend/nextjs/state-management.md +2 -1
  74. package/framework/standards/frontend/nextjs/testing.md +18 -0
  75. package/framework/standards/integration/mcp/mcp-tools.md +48 -117
  76. package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
  77. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
  78. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
  79. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
  80. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
  81. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
  82. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
  83. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
  84. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
  85. package/framework/templates/code/dotnet/database/migration.cs +49 -83
  86. package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
  87. package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
  88. package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
  89. package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
  90. package/framework/templates/code/dotnet/jobs/job.cs +2 -97
  91. package/framework/templates/code/dotnet/test.cs +2 -87
  92. package/framework/templates/code/typescript/contracts.ts +140 -167
  93. package/framework/templates/context/design-system.md +3 -3
  94. package/framework/templates/docs/proposal.md +11 -0
  95. package/framework/templates/docs/spec.md +15 -0
  96. package/framework/templates/docs/ui-mockups.md +115 -98
  97. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
  98. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
  99. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
  100. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
  101. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
  102. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
  103. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
  104. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
  105. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
  106. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
  107. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
  108. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
  109. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
  110. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
  111. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
  112. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
  113. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
  114. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
  115. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
  116. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
  117. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
  118. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
  119. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
  120. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
  121. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
  122. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
  123. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
  148. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
  149. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
  150. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
  151. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
  152. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
  153. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
  154. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
  155. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
  156. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
  157. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
  158. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
  159. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
  160. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
  161. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
  162. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
  163. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
  164. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
  165. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
  166. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
  167. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
  168. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
  169. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
  170. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
  171. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
  172. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
  173. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
  174. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
  175. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
  176. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
  180. package/framework/templates/feature/decisions.md +124 -122
  181. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
  182. package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
  183. package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
  184. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
  185. package/framework/templates/integrations/asaas-client.cs +332 -387
  186. package/framework/templates/integrations/asaas-webhook.cs +290 -351
  187. package/framework/templates/integrations/neon-auth-config.cs +9 -105
  188. package/framework/templates/project/validate.js +97 -120
  189. package/package.json +2 -2
  190. package/src/commands/dashboard/dashboard.js +80 -53
  191. package/src/commands/graph/affected.js +4 -3
  192. package/src/commands/graph/build.js +109 -18
  193. package/src/commands/graph/explain.js +8 -5
  194. package/src/commands/graph/status.js +14 -8
  195. package/src/commands/mcp/mcp-setup.js +37 -9
  196. package/src/commands/observability/cost.js +148 -18
  197. package/src/commands/project/doctor.js +85 -15
  198. package/src/commands/project/init.js +6 -3
  199. package/src/commands/project/install-plugin-cmd.js +1 -1
  200. package/src/commands/project/update.js +30 -0
  201. package/src/commands/project/worktree.js +73 -90
  202. package/src/commands/state/advance.js +18 -0
  203. package/src/commands/state/approve.js +15 -0
  204. package/src/commands/state/archive.js +62 -9
  205. package/src/commands/state/finish.js +62 -14
  206. package/src/commands/state/gate-check.js +8 -1
  207. package/src/commands/state/gate-decision.js +8 -4
  208. package/src/commands/state/report-check.js +90 -9
  209. package/src/commands/state/usecase.js +66 -0
  210. package/src/commands/tasks/dag.js +17 -2
  211. package/src/commands/validation/validate-feature.js +6 -1
  212. package/src/commands/validation/verify.js +20 -1
  213. package/src/dashboard/app/App.jsx +80 -16
  214. package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
  215. package/src/dashboard/app/components/CommandPalette.jsx +155 -0
  216. package/src/dashboard/app/components/DetailPanel.jsx +47 -11
  217. package/src/dashboard/app/components/EntityLink.jsx +55 -0
  218. package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
  219. package/src/dashboard/app/components/FlowGraph.jsx +19 -17
  220. package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
  221. package/src/dashboard/app/components/Markdown.jsx +47 -0
  222. package/src/dashboard/app/components/PhaseRail.jsx +111 -0
  223. package/src/dashboard/app/components/SearchFilter.jsx +40 -22
  224. package/src/dashboard/app/components/Sidebar.jsx +146 -37
  225. package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
  226. package/src/dashboard/app/hooks/usePref.js +38 -0
  227. package/src/dashboard/app/lib/format.js +69 -0
  228. package/src/dashboard/app/lib/phases.js +56 -0
  229. package/src/dashboard/app/lib/project.js +50 -0
  230. package/src/dashboard/app/pages/Activity.jsx +24 -0
  231. package/src/dashboard/app/pages/AgentMap.jsx +371 -370
  232. package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
  233. package/src/dashboard/app/pages/Features.jsx +400 -0
  234. package/src/dashboard/app/pages/Guide.jsx +146 -0
  235. package/src/dashboard/app/pages/HookInspector.jsx +357 -322
  236. package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
  237. package/src/dashboard/app/pages/Portfolio.jsx +156 -0
  238. package/src/dashboard/app/pages/Rubrics.jsx +101 -0
  239. package/src/dashboard/app/pages/RulesMap.jsx +300 -273
  240. package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
  241. package/src/dashboard/app/pages/Standards.jsx +407 -332
  242. package/src/dashboard/app/styles/index.css +187 -47
  243. package/src/dashboard/index.html +14 -12
  244. package/src/dashboard/package.json +32 -24
  245. package/src/dashboard/scanner/activity-scanner.js +351 -0
  246. package/src/dashboard/scanner/agents-scanner.js +63 -1
  247. package/src/dashboard/scanner/docs-scanner.js +59 -0
  248. package/src/dashboard/scanner/feature-status.js +10 -0
  249. package/src/dashboard/scanner/features-scanner.js +360 -0
  250. package/src/dashboard/scanner/hooks-scanner.js +8 -1
  251. package/src/dashboard/scanner/index.js +14 -7
  252. package/src/dashboard/scanner/phases-scanner.js +14 -15
  253. package/src/dashboard/scanner/portfolio.js +153 -0
  254. package/src/dashboard/scanner/project-api.js +195 -0
  255. package/src/dashboard/scanner/rules-scanner.js +42 -37
  256. package/src/dashboard/scanner/skills-scanner.js +105 -43
  257. package/src/dashboard/scanner/standards-scanner.js +4 -1
  258. package/src/dashboard/vite-plugin-morph-data.js +134 -17
  259. package/src/lib/ai/comment-stripper.js +101 -4
  260. package/src/lib/detectors/claude-config-detector.js +2 -7
  261. package/src/lib/detectors/index.js +5 -1
  262. package/src/lib/evaluator-report.js +47 -0
  263. package/src/lib/generators/handoff-generator.js +4 -18
  264. package/src/lib/generators/settings-generator.js +208 -209
  265. package/src/lib/git/default-branch.js +52 -0
  266. package/src/lib/git/tree-fingerprint.js +58 -0
  267. package/src/lib/git/tree-state.js +167 -0
  268. package/src/lib/graph/god-nodes.js +12 -3
  269. package/src/lib/graph/graph-health.js +48 -2
  270. package/src/lib/graph/graph-loader.js +99 -25
  271. package/src/lib/graph/graphifyignore.js +145 -28
  272. package/src/lib/graph/sanitize.js +117 -11
  273. package/src/lib/installers/mcp-installer.js +724 -719
  274. package/src/lib/installers/memory-installer.js +1 -2
  275. package/src/lib/projects/registry.js +154 -0
  276. package/src/lib/risk-detector.js +90 -0
  277. package/src/lib/specs/acervo-format.js +70 -0
  278. package/src/lib/specs/acervo-lifecycle.js +264 -0
  279. package/src/lib/specs/adr-index.js +169 -0
  280. package/src/lib/specs/revocations.js +156 -0
  281. package/src/lib/specs/screen-checklist.js +357 -0
  282. package/src/lib/specs/usecase-check.js +20 -16
  283. package/src/lib/specs/usecase-promoter.js +103 -38
  284. package/src/lib/specs/usecase-registry.js +7 -7
  285. package/src/lib/tasks/tasks-json-parser.js +2 -3
  286. package/src/lib/telemetry/metrics.js +386 -0
  287. package/src/lib/telemetry/telemetry-reader.js +161 -51
  288. package/src/lib/telemetry/verify-event.js +79 -0
  289. package/src/lib/validators/code-budget/code-budget-validator.js +200 -0
  290. package/src/lib/validators/code-budget/comment-scanner.js +75 -0
  291. package/src/lib/validators/code-budget/index.js +9 -0
  292. package/src/lib/validators/reuse/reuse-validator.js +4 -2
  293. package/src/lib/validators/reuse/symbol-scanner.js +27 -22
  294. package/src/lib/validators/validation-runner.js +21 -5
  295. package/src/lib/validators/verify-runner.js +11 -0
  296. package/src/lib/worktree/finish-backup.js +739 -0
  297. package/src/lib/worktree/ignored-triage.js +15 -8
  298. package/src/lib/worktree/worktree-audit.js +5 -0
  299. package/src/scripts/install-plugin.js +1 -1
  300. package/src/scripts/setup-infra.js +31 -22
  301. package/src/utils/agents-installer.js +338 -24
  302. package/src/utils/banner.js +1 -1
  303. package/src/utils/hooks-installer.js +75 -15
  304. package/src/commands/index.js +0 -13
  305. package/src/commands/project/checkpoint.js +0 -209
  306. package/src/core/state/phase-state-machine.js +0 -197
  307. package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
  308. package/src/dashboard/app/pages/LiveState.jsx +0 -206
  309. package/src/dashboard/scanner/squads-scanner.js +0 -13
  310. package/src/dashboard/scanner/state-scanner.js +0 -85
  311. package/src/lib/generators/index.js +0 -10
  312. package/src/lib/hooks/hook-executor.js +0 -167
  313. package/src/lib/validators/checkpoint-persistence.js +0 -39
  314. package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
  315. package/src/lib/validators/shared/index.js +0 -12
  316. package/src/lib/validators/shared/issue-counter.js +0 -14
  317. package/src/lib/validators/shared/result-formatter.js +0 -124
@@ -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
  {
@@ -5,56 +5,14 @@ using Microsoft.Extensions.AI.Evaluation.Reporting.Storage;
5
5
 
6
6
  namespace Morph.AiKit.Evals;
7
7
 
8
- /// <summary>O que uma execucao de cenario produziu.</summary>
9
- /// <param name="Scenario">A chave do cenario na cache (<c>{agente}/{tenant}/{nome}</c>).</param>
10
- /// <param name="Passed">Se TODAS as assercoes deterministicas se sustentaram.</param>
11
- /// <param name="Outcomes">Um veredito por assercao, na ordem do dataset.</param>
12
- /// <param name="Result">O resultado completo da biblioteca, com as metricas de juizo junto.</param>
13
8
  public sealed record EvalScenarioResult(
14
9
  string Scenario,
15
10
  bool Passed,
16
11
  IReadOnlyList<EvalAssertionOutcome> Outcomes,
17
12
  EvaluationResult Result);
18
13
 
19
- /// <summary>
20
- /// Monta a configuracao de eval com cache em disco de TTL explicito, roda os cenarios do
21
- /// dataset versionado e escreve o sumario que o CLI le.
22
- /// </summary>
23
- /// <remarks>
24
- /// <para>
25
- /// <b>A montagem inteira existe para uma frase: com a cache quente, o eval roda sem
26
- /// credencial e sem rede.</b> O caminho e este — a biblioteca embrulha o cliente interno num
27
- /// cliente de cache; no HIT o interno nunca e invocado; em modo offline o interno e o
28
- /// <see cref="OfflineChatClient"/>, que recusa. Logo, ou tudo resolve pela cache, ou a suite
29
- /// fica vermelha nomeando o cenario que faltou. Nunca <c>skip</c>: gate verde sobre
30
- /// verificacao que nao aconteceu e o modo de falha que este harness mais paga para evitar.
31
- /// </para>
32
- /// <para>
33
- /// <b>Cache e resultados vivem em raizes DIFERENTES</b>, e isso e uma decisao, nao um
34
- /// detalhe: os resultados carregam latencia, uso de tokens e o flag de cache hit, todos
35
- /// nao-deterministicos. Numa raiz so, commitar a cache arrastaria os resultados junto e
36
- /// produziria diff em toda rodada, sem sinal nenhum. Separar por CONSTRUCAO (duas raizes) e
37
- /// mais forte que separar por <c>.gitignore</c>: uma regra de ignore dentro de um diretorio
38
- /// commitado e uma regra que a proxima pessoa remove sem entender.
39
- /// </para>
40
- /// <para>
41
- /// <b>Uma <c>ReportingConfiguration</c> por cenario</b>, e nao uma para a suite, porque as
42
- /// assercoes deterministicas SAO os avaliadores e elas mudam a cada cenario. A alternativa
43
- /// seria um avaliador com estado mutavel entre cenarios — barato de escrever e caro de
44
- /// depurar no dia em que dois cenarios rodarem fora de ordem.
45
- /// </para>
46
- /// </remarks>
47
14
  public sealed class EvalFixture : IAsyncDisposable
48
15
  {
49
- /// <summary>
50
- /// O nome do cenario em execucao, por indirecao.
51
- /// </summary>
52
- /// <remarks>
53
- /// O <see cref="OfflineChatClient"/> e construido ANTES do fixture (ele e o cliente
54
- /// interno dele) e precisa nomear, na mensagem de miss, o cenario que esta rodando
55
- /// AGORA. Um nome capturado na construcao nomearia sempre o primeiro cenario, e a
56
- /// mensagem mandaria o leitor investigar o cenario errado.
57
- /// </remarks>
58
16
  private sealed class ScenarioCursor
59
17
  {
60
18
  public string Name { get; set; } = "(nenhum)";
@@ -87,33 +45,18 @@ public sealed class EvalFixture : IAsyncDisposable
87
45
  _resultStore = new DiskBasedResultStore(options.ResultsRoot);
88
46
  }
89
47
 
90
- /// <summary>Nome da execucao no store de resultados. Estavel, porque os resultados nao se commitam.</summary>
91
48
  public const string ExecutionName = "morph-eval";
92
49
 
93
- /// <summary>A configuracao efetiva desta execucao.</summary>
94
50
  public EvalCacheOptions Options { get; }
95
51
 
96
- /// <summary>
97
- /// O veredito do carimbo, apurado UMA vez na construcao — antes de avaliar qualquer
98
- /// cenario. Nao faz sentido pagar o relogio de uma suite para descobrir no fim que a
99
- /// cache nao servia.
100
- /// </summary>
101
52
  public EvalCacheStampCheck StampCheck { get; }
102
53
 
103
- /// <summary>
104
- /// O cliente offline, quando o modo e <see cref="EvalMode.Offline"/>. E por ele que se
105
- /// mede "zero invocacoes do cliente interno" — a prova de que nenhuma chamada paga foi
106
- /// feita.
107
- /// </summary>
108
54
  public OfflineChatClient? Offline => _offline;
109
55
 
110
- /// <summary>Quantas vezes a cache falhou e a chamada desceu ao cliente interno.</summary>
111
56
  public int Misses => _offline?.Invocations ?? _counting?.Calls ?? 0;
112
57
 
113
- /// <summary>Os resultados acumulados, na ordem em que os cenarios rodaram.</summary>
114
58
  public IReadOnlyList<EvalScenarioResult> Results => _results;
115
59
 
116
- /// <summary>O sumario do que rodou ate agora.</summary>
117
60
  public EvalRunSummary Summary => new()
118
61
  {
119
62
  Scenarios = _results.Count,
@@ -128,15 +71,6 @@ public sealed class EvalFixture : IAsyncDisposable
128
71
  Failures = CollectFailures(),
129
72
  };
130
73
 
131
- /// <summary>
132
- /// Uma entrada por assercao REPROVADA, com o cenario que a carregava.
133
- /// </summary>
134
- /// <remarks>
135
- /// Percorre as assercoes reprovadas, e nao os cenarios reprovados: o cenario diz ONDE, a
136
- /// assercao diz O QUE. Um sumario que so contasse cenarios obrigaria quem le a reabrir o
137
- /// dataset para descobrir qual regua quebrou — que e exatamente a investigacao que a
138
- /// mensagem existe para dispensar.
139
- /// </remarks>
140
74
  private IReadOnlyList<EvalFailureDetail> CollectFailures()
141
75
  {
142
76
  var failures = new List<EvalFailureDetail>();
@@ -145,10 +79,6 @@ public sealed class EvalFixture : IAsyncDisposable
145
79
  {
146
80
  var reproved = result.Outcomes.Where(o => !o.Passed).ToList();
147
81
 
148
- // O cenario SEM assercao nenhuma reprova por vacuidade (ele nao cobra nada), e
149
- // nesse caso nao existe outcome reprovado para nomear. Sem esta linha ele seria
150
- // contado em `Failed` e invisivel em `Failures` — um numero sem frase, que e a
151
- // metade exata do defeito que este tipo existe para fechar.
152
82
  if (reproved.Count == 0)
153
83
  {
154
84
  failures.Add(new EvalFailureDetail(
@@ -169,20 +99,6 @@ public sealed class EvalFixture : IAsyncDisposable
169
99
  return failures;
170
100
  }
171
101
 
172
- /// <summary>
173
- /// Constroi o fixture a partir das variaveis que o CLI DITA.
174
- /// </summary>
175
- /// <remarks>
176
- /// Fora do CLI um projeto pode montar <see cref="EvalCacheOptions"/> a mao e chamar
177
- /// <see cref="Create"/>; as variaveis existem para que a MESMA suite seja executavel pelo
178
- /// <c>verify</c> sem um segundo caminho de configuracao.
179
- /// </remarks>
180
- /// <param name="liveClient">
181
- /// O cliente real. Obrigatorio em <see cref="EvalMode.Refresh"/> e
182
- /// <see cref="EvalMode.Online"/>; ignorado em <see cref="EvalMode.Offline"/>.
183
- /// </param>
184
- /// <param name="qualityEvaluators">Avaliadores de juizo. Reportados, nunca decisivos.</param>
185
- /// <param name="timeProvider">Relogio, para a checagem de pin no futuro.</param>
186
102
  public static EvalFixture FromEnvironment(
187
103
  IChatClient? liveClient = null,
188
104
  IEnumerable<IEvaluator>? qualityEvaluators = null,
@@ -198,8 +114,6 @@ public sealed class EvalFixture : IAsyncDisposable
198
114
  DatasetRoot = Environment.GetEnvironmentVariable("MORPH_EVAL_DATASET") ?? Path.Combine(beside, "eval-datasets"),
199
115
  ResultsRoot = Environment.GetEnvironmentVariable("MORPH_EVAL_RESULTS") ?? Path.Combine(beside, "eval-results"),
200
116
  Mode = ParseMode(Environment.GetEnvironmentVariable("MORPH_EVAL_MODE")),
201
- // Explicito e longo: o default da biblioteca (14 dias) apagaria a arvore
202
- // commitada durante uma leitura. Ver EvalCacheOptions.
203
117
  TimeToLive = TimeSpan.FromDays(3650),
204
118
  ProviderName = Environment.GetEnvironmentVariable("MORPH_EVAL_PROVIDER") ?? "morph-eval",
205
119
  DefaultModelId = Environment.GetEnvironmentVariable("MORPH_EVAL_MODEL"),
@@ -211,9 +125,6 @@ public sealed class EvalFixture : IAsyncDisposable
211
125
  return Create(options, liveClient, qualityEvaluators, timeProvider);
212
126
  }
213
127
 
214
- /// <summary>Constroi o fixture a partir de opcoes explicitas.</summary>
215
- /// <exception cref="ArgumentOutOfRangeException">TTL abaixo do piso — ver <see cref="EvalCacheOptions.MinimumTimeToLive"/>.</exception>
216
- /// <exception cref="InvalidOperationException">Carimbo ausente ou divergente em modo de leitura; ou modo de escrita sem cliente real.</exception>
217
128
  public static EvalFixture Create(
218
129
  EvalCacheOptions options,
219
130
  IChatClient? liveClient = null,
@@ -237,8 +148,7 @@ public sealed class EvalFixture : IAsyncDisposable
237
148
  var clock = timeProvider ?? TimeProvider.System;
238
149
  IReadOnlyList<IEvaluator> quality = qualityEvaluators is null ? [] : [.. qualityEvaluators];
239
150
 
240
- // O carimbo so e COBRADO nos modos que LEEM a cache. Em Refresh a arvore esta sendo
241
- // (re)escrita — cobrar carimbo ali tornaria impossivel criar a primeira cache.
151
+ // Em Refresh a arvore esta sendo reescrita: cobrar carimbo impediria gravar a primeira cache.
242
152
  var stampCheck = options.Mode == EvalMode.Refresh || options.Pin is null
243
153
  ? new EvalCacheStampCheck(
244
154
  EvalStampVerdict.Ok,
@@ -278,10 +188,6 @@ public sealed class EvalFixture : IAsyncDisposable
278
188
  return new EvalFixture(options, stampCheck, cursor, inner, offline, counting, quality);
279
189
  }
280
190
 
281
- /// <summary>
282
- /// Roda TODOS os cenarios do dataset e escreve o sumario, quando ha caminho para ele.
283
- /// </summary>
284
- /// <exception cref="InvalidOperationException">Dataset sem nenhum cenario — uma suite de eval que avalia zero cenarios e sai verde e um guardrail inerte.</exception>
285
191
  public async Task<EvalRunSummary> RunAllAsync(CancellationToken cancellationToken = default)
286
192
  {
287
193
  var scenarios = EvalScenario.LoadAll(Options.DatasetRoot);
@@ -293,9 +199,7 @@ public sealed class EvalFixture : IAsyncDisposable
293
199
  + "que este kit existe para nao produzir.");
294
200
  }
295
201
 
296
- // Em Refresh a arvore inteira e zerada antes de regravar: uma regravacao parcial
297
- // deixaria entradas de uma versao antiga convivendo com entradas novas, e a proxima
298
- // leitura offline nao teria como saber quais sao quais.
202
+ // Zera antes de regravar: entradas de versoes diferentes nao podem conviver na arvore.
299
203
  if (Options.Mode == EvalMode.Refresh)
300
204
  {
301
205
  try
@@ -305,12 +209,7 @@ public sealed class EvalFixture : IAsyncDisposable
305
209
  }
306
210
  catch (DirectoryNotFoundException)
307
211
  {
308
- // MEDIDO na v10.9.0: ResetAsync chama Directory.Delete no subdiretorio de
309
- // cache e LANCA quando ele ainda nao existe — o caso da PRIMEIRA gravacao.
310
- // "Nao ha o que zerar" e sucesso, nao falha. Capturado aqui, e nao evitado
311
- // com um Directory.Exists sobre o layout interno da biblioteca: repetir o
312
- // nome do subdiretorio dela aqui seria uma segunda verdade sobre onde a
313
- // cache mora.
212
+ // Raiz ausente na primeira gravacao: nada a zerar.
314
213
  }
315
214
  }
316
215
 
@@ -339,7 +238,6 @@ public sealed class EvalFixture : IAsyncDisposable
339
238
  return summary;
340
239
  }
341
240
 
342
- /// <summary>Roda UM cenario: resolve a resposta (cache ou cliente interno) e aplica os avaliadores.</summary>
343
241
  public async Task<EvalScenarioResult> RunAsync(EvalScenario scenario, CancellationToken cancellationToken = default)
344
242
  {
345
243
  ArgumentNullException.ThrowIfNull(scenario);
@@ -378,11 +276,9 @@ public sealed class EvalFixture : IAsyncDisposable
378
276
  outcomes.Add(assertion.Evaluate(response));
379
277
  }
380
278
 
381
- // Um cenario SEM assercao deterministica nao passa: ele nao cobra nada. Deixa-lo
382
- // verde por vacuidade e o mesmo defeito de um teste vazio — e aqui ele seria pior,
383
- // porque o cenario apareceria na contagem como se tivesse verificado algo.
384
279
  var scenarioResult = new EvalScenarioResult(
385
280
  scenario.CacheKey,
281
+ // Sem assercao o cenario reprova: verde por vacuidade contaria como verificado.
386
282
  outcomes.Count > 0 && outcomes.All(o => o.Passed),
387
283
  outcomes,
388
284
  result);
@@ -391,7 +287,6 @@ public sealed class EvalFixture : IAsyncDisposable
391
287
  return scenarioResult;
392
288
  }
393
289
 
394
- /// <inheritdoc />
395
290
  public ValueTask DisposeAsync()
396
291
  {
397
292
  _inner.Dispose();
@@ -1,32 +1,10 @@
1
1
  namespace Morph.AiKit.Evals;
2
2
 
3
- /// <summary>
4
- /// De onde a resposta do modelo pode vir nesta execucao de eval.
5
- /// </summary>
6
- /// <remarks>
7
- /// A distincao existe porque as tres situacoes tem custos diferentes em DINHEIRO e em
8
- /// confianca, e um enum de dois valores ("usa cache" / "nao usa") esconderia a do meio.
9
- /// </remarks>
10
3
  public enum EvalMode
11
4
  {
12
- /// <summary>
13
- /// So a cache. Um miss e falha nomeada, nunca uma chamada de rede — e nunca um
14
- /// <c>skip</c>: gate verde sobre verificacao que nao aconteceu e o modo de falha que
15
- /// este harness mais paga para evitar. E o modo do CI.
16
- /// </summary>
17
5
  Offline = 0,
18
6
 
19
- /// <summary>
20
- /// Reexecuta TODOS os cenarios contra o provider e regrava a arvore de cache. Exige
21
- /// credencial e nunca acontece implicitamente: uma regravacao automatica e uma chamada
22
- /// paga que ninguem pediu.
23
- /// </summary>
24
7
  Refresh = 1,
25
8
 
26
- /// <summary>
27
- /// Cache primeiro, provider no miss. E o modo de quem esta escrevendo cenario novo na
28
- /// propria maquina: paga so pelo que falta, e o resultado sai marcado como
29
- /// <c>cold</c> com aviso de que a arvore mudou e precisa ser commitada.
30
- /// </summary>
31
9
  Online = 2,
32
10
  }
@@ -3,44 +3,9 @@ using Microsoft.Extensions.AI.Evaluation.Quality;
3
3
 
4
4
  namespace Morph.AiKit.Evals;
5
5
 
6
- /// <summary>
7
- /// Os avaliadores de JUIZO que o kit considera utilizaveis, e a fronteira que os separa dos
8
- /// experimentais.
9
- /// </summary>
10
- /// <remarks>
11
- /// <para>
12
- /// <b>Eles nunca decidem.</b> O veredito de um cenario sai das <see cref="EvalAssertion"/>
13
- /// deterministicas; o que sai daqui e METRICA REPORTADA. Um juizo de LLM que reprovasse
14
- /// sozinho reintroduziria a variancia que faz suites de eval serem desligadas do CI — e um
15
- /// no de eval desligado nao verifica nada.
16
- /// </para>
17
- /// <para>
18
- /// <b>Por que a lista e curta.</b> Dois dos quatro avaliadores que o plano desta feature
19
- /// listava como estaveis carregam <c>[Experimental("AIEVAL001")]</c> na versao pinada:
20
- /// <c>TaskAdherenceEvaluator</c> e <c>ToolCallAccuracyEvaluator</c>. Sob
21
- /// <c>TreatWarningsAsErrors</c>, usa-los exige suprimir um diagnostico — uma decisao
22
- /// consciente de quem adota o kit, jamais um efeito colateral de chamar um metodo chamado
23
- /// "Stable". Medido por reflexao sobre <c>Microsoft.Extensions.AI.Evaluation.Quality</c>
24
- /// 10.9.0, e pinado pelo teste <c>WhichQualityEvaluatorsAreExperimentalIsMEASURED</c>.
25
- /// </para>
26
- /// <para>
27
- /// <b>E por que este tipo existe.</b> Sem uma unica referencia em <c>src/</c>, o pacote
28
- /// <c>.Quality</c> teria apenas contrato de VERSAO (o restore, NU1102) e um rename de API
29
- /// nele nao reprovaria PR nenhuma. Com o <c>using</c> real acima, ele passa a ter cobertura
30
- /// de compilacao — que e o nivel de garantia que o kit promete para a familia de Evaluation.
31
- /// </para>
32
- /// </remarks>
33
6
  public static class EvalQualityEvaluators
34
7
  {
35
- /// <summary>
36
- /// Os avaliadores de qualidade NAO experimentais da versao pinada, prontos para
37
- /// <see cref="EvalFixture.Create"/>.
38
- /// </summary>
39
- /// <remarks>
40
- /// Uma lista nova a cada chamada: os avaliadores sao passados adiante e guardados por uma
41
- /// <c>ReportingConfiguration</c>, e um array compartilhado seria estado global disfarcado
42
- /// de conveniencia.
43
- /// </remarks>
8
+ // TaskAdherence e ToolCallAccuracy ficam fora: sao [Experimental("AIEVAL001")] no Quality 10.9.0.
44
9
  public static IReadOnlyList<IEvaluator> Stable() =>
45
10
  [
46
11
  new RelevanceEvaluator(),