@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,22 +1,5 @@
1
1
  namespace Morph.AiKit.Rag;
2
2
 
3
- /// <summary>
4
- /// Erro de <b>entrada</b> do <see cref="DeltaIngest"/>: a mesma lookup-key apareceu duas
5
- /// vezes na mesma remessa.
6
- /// </summary>
7
- /// <remarks>
8
- /// <para>
9
- /// Nao e fluxo de controle e nao e recuperavel no lugar: com a chave repetida, "o que esta
10
- /// no acervo" deixa de ter resposta unica e o plano vira nao-deterministico — qual dos dois
11
- /// itens venceu passa a depender da ordem de enumeracao. Silenciar (ficar com o ultimo)
12
- /// gravaria um dos dois ao acaso; deduplicar por conta propria esconderia um defeito da
13
- /// origem, que e onde ele tem conserto.
14
- /// </para>
15
- /// <para>
16
- /// Os dois indices vao na excecao porque "chave X duplicada" manda procurar num corpus de
17
- /// milhares; "posicoes 12 e 4.871" manda olhar duas linhas.
18
- /// </para>
19
- /// </remarks>
20
3
  public sealed class DuplicateLookupKeyException : ArgumentException
21
4
  {
22
5
  internal DuplicateLookupKeyException(string lookupKey, int firstIndex, int duplicateIndex, string paramName)
@@ -32,58 +15,16 @@ public sealed class DuplicateLookupKeyException : ArgumentException
32
15
  DuplicateIndex = duplicateIndex;
33
16
  }
34
17
 
35
- /// <summary>A chave repetida.</summary>
36
18
  public string LookupKey { get; }
37
19
 
38
- /// <summary>Onde ela apareceu pela primeira vez.</summary>
39
20
  public int FirstIndex { get; }
40
21
 
41
- /// <summary>Onde ela reapareceu.</summary>
42
22
  public int DuplicateIndex { get; }
43
23
  }
44
24
 
45
- /// <summary>
46
- /// Planejamento de ingestao <b>idempotente por lookup-key</b>: dada uma remessa e o que o
47
- /// acervo ja tem, diz o que entrou, o que mudou, o que sumiu e o que ficou igual.
48
- /// </summary>
49
- /// <remarks>
50
- /// <para>
51
- /// <b>Funcao pura, sem I/O.</b> Nao abre conexao, nao chama embedding, nao mede tempo — o
52
- /// que a torna testavel sem banco e, mais importante, torna a idempotencia <b>demonstravel</b>
53
- /// em vez de prometida: aplicar o plano e replanejar a mesma entrada tem de devolver
54
- /// <see cref="DeltaPlan{T}.IsNoOp"/> verdadeiro.
55
- /// </para>
56
- /// <para>
57
- /// <b>A chave e a identidade; o hash e a versao.</b> Sao duas perguntas diferentes e por isso
58
- /// dois delegados: <c>lookupKey</c> responde "e o mesmo documento?" e <c>contentHash</c>
59
- /// responde "mudou?". Colapsar as duas num campo so — usar o hash como chave — faz cada
60
- /// edicao parecer um documento novo e o original parecer removido, que e a ingestao
61
- /// apaga-tudo com outro nome.
62
- /// </para>
63
- /// </remarks>
64
25
  public static class DeltaIngest
65
26
  {
66
- /// <summary>
67
- /// Compara a remessa com o acervo e devolve o plano.
68
- /// </summary>
69
- /// <typeparam name="T">O item ingerido.</typeparam>
70
- /// <param name="incoming">A remessa. Enumerada uma unica vez.</param>
71
- /// <param name="lookupKey">
72
- /// A identidade estavel do item — o que faz duas versoes do mesmo documento serem o
73
- /// mesmo documento. Nula ou em branco e recusada: uma chave vazia colapsaria itens
74
- /// distintos num so.
75
- /// </param>
76
- /// <param name="contentHash">
77
- /// A versao do conteudo. Igual significa "nao mudou"; diferente significa "mudou". Nulo e
78
- /// recusado — sem hash nao ha como distinguir <c>Unchanged</c> de <c>Updated</c>, e
79
- /// tratar a ausencia como "mudou" reintroduziria o re-embedding de tudo.
80
- /// </param>
81
- /// <param name="existingHashes">
82
- /// O que o acervo tem hoje: lookup-key para hash de conteudo. As chaves daqui que nao
83
- /// aparecerem na remessa saem em <see cref="DeltaPlan{T}.Removed"/>.
84
- /// </param>
85
- /// <exception cref="DuplicateLookupKeyException">A mesma chave veio duas vezes.</exception>
86
- /// <exception cref="ArgumentException">Um item produziu chave ou hash nulo/em branco.</exception>
27
+ // A chave e a identidade e o hash e a versao: usar o hash como chave faz cada edicao virar remocao + insercao.
87
28
  public static DeltaPlan<T> Plan<T>(
88
29
  IEnumerable<T> incoming,
89
30
  Func<T, string> lookupKey,
@@ -99,9 +40,6 @@ public static class DeltaIngest
99
40
  List<T> updated = [];
100
41
  List<T> unchanged = [];
101
42
 
102
- // Comparacao ordinal, explicita: a chave e um identificador, nao texto de usuario.
103
- // Deixar a cultura decidir faria o mesmo corpus produzir planos diferentes conforme a
104
- // maquina que roda a ingestao.
105
43
  var seen = new Dictionary<string, int>(StringComparer.Ordinal);
106
44
  var index = 0;
107
45
 
@@ -151,9 +89,6 @@ public static class DeltaIngest
151
89
  index++;
152
90
  }
153
91
 
154
- // O que o acervo tem e a remessa nao trouxe. Note que 'seen' e consultado pela MESMA
155
- // chave e com o MESMO comparador usados acima — uma segunda nocao de igualdade aqui
156
- // faria itens presentes serem reportados como removidos.
157
92
  List<string> removed = [];
158
93
 
159
94
  foreach (var key in existingHashes.Keys)
@@ -1,55 +1,14 @@
1
1
  namespace Morph.AiKit.Rag;
2
2
 
3
- /// <summary>
4
- /// O que precisa acontecer no acervo para que ele passe a refletir a entrada — e nada mais.
5
- /// </summary>
6
- /// <remarks>
7
- /// <para>
8
- /// <b>O defeito que este tipo existe para matar</b> e o apaga-e-regrava: a base de
9
- /// conhecimento do GHLBrain <b>apaga e re-embeda o documento inteiro</b> a cada ingestao,
10
- /// mudou ou nao. O custo e pago em embedding de trecho que nao mudou e em latencia (uma
11
- /// chamada de embedding por item no reindex em massa). Um plano em quatro baldes torna
12
- /// visivel o que a ingestao inteira escondia: quase sempre <see cref="Unchanged"/> e a lista
13
- /// grande e as outras tres sao curtas.
14
- /// </para>
15
- /// <para>
16
- /// Errata (2026-09-15, Morph.GHLBrain#96): a versao anterior deste comentario citava tambem
17
- /// uma janela entre o apagar e o gravar em que a busca nao acha nada. No GHLBrain o apagar e
18
- /// o gravar ja rodam numa transacao so, com o embedding antes dela, entao essa janela nao
19
- /// existe la. Ela existe em quem apaga fora da transacao que grava.
20
- /// </para>
21
- /// <para>
22
- /// <b>E um plano, nao uma execucao.</b> Nada aqui toca banco, vetor ou arquivo: quem aplica
23
- /// e o projeto, que sabe qual e a sua transacao. E por isso que a idempotencia da ingestao
24
- /// pode ser provada sem subir infraestrutura nenhuma.
25
- /// </para>
26
- /// </remarks>
27
- /// <typeparam name="T">O item ingerido — documento, trecho, linha.</typeparam>
28
- /// <param name="Added">Itens cuja chave nao existia no acervo.</param>
29
- /// <param name="Updated">Itens cuja chave existia com <b>outro</b> conteudo.</param>
30
- /// <param name="Removed">
31
- /// Chaves que estavam no acervo e <b>nao vieram</b> na entrada. Sao chaves, e nao itens:
32
- /// o que sumiu da origem nao tem corpo para carregar.
33
- /// </param>
34
- /// <param name="Unchanged">
35
- /// Itens cuja chave existia com o <b>mesmo</b> conteudo. Nao re-embedar estes e a economia
36
- /// inteira desta feature.
37
- /// </param>
38
3
  public sealed record DeltaPlan<T>(
39
4
  IReadOnlyList<T> Added,
40
5
  IReadOnlyList<T> Updated,
41
6
  IReadOnlyList<string> Removed,
42
7
  IReadOnlyList<T> Unchanged)
43
8
  {
44
- /// <summary>Plano vazio — nem entrada, nem acervo.</summary>
45
9
  public static DeltaPlan<T> Empty { get; } = new([], [], [], []);
46
10
 
47
- /// <summary>
48
- /// Se o acervo ja reflete a entrada. <b>E a idempotencia expressa como predicado:</b>
49
- /// re-planejar a mesma entrada sobre o acervo que ela produziu tem de dar verdadeiro.
50
- /// </summary>
51
11
  public bool IsNoOp => Added.Count == 0 && Updated.Count == 0 && Removed.Count == 0;
52
12
 
53
- /// <summary>Quantos itens da entrada tocam o acervo. Zero num plano idempotente.</summary>
54
13
  public int WriteCount => Added.Count + Updated.Count + Removed.Count;
55
14
  }
@@ -8,23 +8,8 @@ using Xunit;
8
8
 
9
9
  namespace Morph.AiKit.Tests;
10
10
 
11
- /// <summary>
12
- /// Canarios da <see cref="AgentFactory"/> (T10). Duas afirmacoes sao o coracao do arquivo:
13
- /// <b>a precedencia do protocolo</b> (spec vence o alias quando presente, alias vence quando
14
- /// a spec cala) e <b>a ordem do pipeline</b>, observada na sequencia real de chamadas, nao
15
- /// na leitura do codigo.
16
- /// </summary>
17
- /// <remarks>
18
- /// Todo <c>await</c> passa <c>TestContext.Current.CancellationToken</c> — exigencia do
19
- /// analisador xUnit1051, que sob <c>TreatWarningsAsErrors</c> e build vermelho.
20
- /// </remarks>
21
11
  public sealed class AgentFactoryTests
22
12
  {
23
- // ---------------------------------------------------------------------------------
24
- // Documentos de registry usados pelos casos
25
- // ---------------------------------------------------------------------------------
26
-
27
- /// <summary>Um alias Responses e um alias Chat COM o limite do GPT-5.4 declarado.</summary>
28
13
  private const string TwoProtocolsDocument = """
29
14
  {
30
15
  "version": "2.0",
@@ -53,14 +38,6 @@ public sealed class AgentFactoryTests
53
38
  private static AgentSpec Spec(string alias = "responses-alias", AgentApi? api = null)
54
39
  => new() { Alias = alias, Instructions = "voce e um agente de teste", Api = api };
55
40
 
56
- // ---------------------------------------------------------------------------------
57
- // doneCriteria 1 — a precedencia do api:
58
- // ---------------------------------------------------------------------------------
59
-
60
- /// <summary>
61
- /// Spec calada: vale o <c>api:</c> do alias. O seletor de cliente e o observatorio —
62
- /// ele recebe o protocolo <b>efetivo</b>, ja resolvido.
63
- /// </summary>
64
41
  [Fact]
65
42
  public void TheAliasApiWinsWhenTheSpecDeclaresNone()
66
43
  {
@@ -77,11 +54,6 @@ public sealed class AgentFactoryTests
77
54
  Assert.Equal([AgentApi.Responses, AgentApi.Chat], seen);
78
55
  }
79
56
 
80
- /// <summary>
81
- /// Spec preenchida: ela vence, nos dois sentidos. A regra nao e reimplementada aqui —
82
- /// a fabrica chama <see cref="AgentSpec.ResolveApi"/> (T7), e este teste morre se
83
- /// alguem inverter a precedencia la ou copia-la para ca.
84
- /// </summary>
85
57
  [Fact]
86
58
  public void TheSpecApiWinsOverTheAliasInBothDirections()
87
59
  {
@@ -98,11 +70,6 @@ public sealed class AgentFactoryTests
98
70
  Assert.Equal([AgentApi.Chat, AgentApi.Responses], seen);
99
71
  }
100
72
 
101
- /// <summary>
102
- /// Sem seletor, "a spec vence" nao pode ser cumprido: o registry memoiza UM cliente por
103
- /// alias, construido para o protocolo do alias. A fabrica recusa nomeando os dois
104
- /// protocolos, em vez de devolver um agente que diz falar um e fala outro.
105
- /// </summary>
106
73
  [Fact]
107
74
  public void WithoutASelectorADivergentSpecApiIsRefusedNamingBothProtocols()
108
75
  {
@@ -118,10 +85,6 @@ public sealed class AgentFactoryTests
118
85
  Assert.Contains("responses-alias", error.Message, StringComparison.Ordinal);
119
86
  }
120
87
 
121
- /// <summary>
122
- /// Sem divergencia e sem seletor, o caminho normal: o cliente memoizado do alias, uma
123
- /// unica construcao por alias, por mais agentes que se criem.
124
- /// </summary>
125
88
  [Fact]
126
89
  public void WithoutASelectorEveryAgentOfAnAliasSharesTheMemoizedClient()
127
90
  {
@@ -135,20 +98,6 @@ public sealed class AgentFactoryTests
135
98
  Assert.Equal(1, providerFactory.CreateCount);
136
99
  }
137
100
 
138
- // ---------------------------------------------------------------------------------
139
- // doneCriteria 2 — a ordem do pipeline, observada nas chamadas
140
- // ---------------------------------------------------------------------------------
141
-
142
- /// <summary>
143
- /// A ordem completa, medida numa invocacao com <b>duas</b> voltas de tool calling.
144
- /// </summary>
145
- /// <remarks>
146
- /// O que a sequencia prova, e que nenhuma leitura de codigo prova: o encaixe de custo
147
- /// entra e sai <b>uma</b> vez envolvendo as duas voltas (esta ACIMA do
148
- /// <c>FunctionInvokingChatClient</c>), e o encaixe de telemetria entra e sai <b>duas</b>
149
- /// vezes, uma por chamada ao modelo (esta ABAIXO). Trocar os dois de lugar muda esta
150
- /// lista, e o teste morre.
151
- /// </remarks>
152
101
  [Fact]
153
102
  public async Task ThePipelineOrderIsUsageCostAboveFunctionInvocationAndTelemetryBelowIt()
154
103
  {
@@ -180,12 +129,7 @@ public sealed class AgentFactoryTests
180
129
  log);
181
130
  }
182
131
 
183
- /// <summary>
184
- /// A razao de o custo ficar acima do FICC, em numeros: ele ve UMA resposta com o uso
185
- /// somado das duas voltas (10+30 de entrada, 4+6 de saida). Abaixo do FICC veria duas
186
- /// respostas parciais — e leria um <c>UsageDetails</c> que o proprio FICC muta para
187
- /// acumular, o que faria a mesma conta sair diferente conforme a hora de ler.
188
- /// </summary>
132
+ // Abaixo do FunctionInvokingChatClient o custo leria o UsageDetails que ele muta para acumular entre voltas.
189
133
  [Fact]
190
134
  public async Task UsageCostAboveFunctionInvocationSeesTheUsageOfEveryToolRoundSummedOnce()
191
135
  {
@@ -209,15 +153,6 @@ public sealed class AgentFactoryTests
209
153
  Assert.Equal(10, usage.OutputTokenCount);
210
154
  }
211
155
 
212
- // ---------------------------------------------------------------------------------
213
- // As opcoes de nivel de agente (T7) chegam ao lugar que a T7 declarou
214
- // ---------------------------------------------------------------------------------
215
-
216
- /// <summary>
217
- /// <c>MaxToolIterations</c> e <c>AllowConcurrentToolInvocation</c> tem destino nomeado
218
- /// no contrato da T7. Aqui eles sao lidos <b>do objeto real</b> do pipeline, via
219
- /// <c>GetService</c> — nao de um campo que a fabrica tenha guardado.
220
- /// </summary>
221
156
  [Fact]
222
157
  public void TheAgentLevelKnobsReachTheFunctionInvokingChatClient()
223
158
  {
@@ -236,10 +171,6 @@ public sealed class AgentFactoryTests
236
171
  Assert.True(functionInvoking.AllowConcurrentInvocation);
237
172
  }
238
173
 
239
- /// <summary>
240
- /// <c>TimeoutSeconds</c> vira <see cref="CancellationToken"/> de verdade: um cliente que
241
- /// nunca responde e cancelado, em vez de pendurar a invocacao.
242
- /// </summary>
243
174
  [Fact]
244
175
  public async Task TimeoutSecondsBecomesACancellationTokenThatActuallyCancels()
245
176
  {
@@ -260,15 +191,6 @@ public sealed class AgentFactoryTests
260
191
  () => agent.RunAsync("oi", session, cancellationToken: TestContext.Current.CancellationToken));
261
192
  }
262
193
 
263
- // ---------------------------------------------------------------------------------
264
- // A spec inteira chega as ChatOptions do agente
265
- // ---------------------------------------------------------------------------------
266
-
267
- /// <summary>
268
- /// O modelo vem do alias (a spec nunca cita modelo), as instrucoes e o schema vem da
269
- /// spec, e <see cref="AgentSpecOptions.ApplyTo"/> nao atropela nem as tools nem o
270
- /// formato de resposta ja definidos.
271
- /// </summary>
272
194
  [Fact]
273
195
  public async Task TheSpecReachesTheChatOptionsWithoutClobberingToolsOrSchema()
274
196
  {
@@ -304,14 +226,6 @@ public sealed class AgentFactoryTests
304
226
  Assert.Equal("agente-de-teste", agent.Name);
305
227
  }
306
228
 
307
- // ---------------------------------------------------------------------------------
308
- // Recusas
309
- // ---------------------------------------------------------------------------------
310
-
311
- /// <summary>
312
- /// Limite de alias violado nao vira agente. A recusa carrega a procedencia escrita no
313
- /// registry — sem ela, o limite nao teria sequer carregado (T8).
314
- /// </summary>
315
229
  [Fact]
316
230
  public void AViolatedAliasConstraintRefusesTheAgentAndCarriesItsProvenance()
317
231
  {
@@ -328,11 +242,6 @@ public sealed class AgentFactoryTests
328
242
  Assert.Contains("issue #93", error.Message, StringComparison.Ordinal);
329
243
  }
330
244
 
331
- /// <summary>
332
- /// O MESMO alias, sem tools, nao viola nada: o limite tem quatro fatos, e tres nao
333
- /// bastam. O canario existe para que a recusa acima nao seja confundida com "modelo de
334
- /// raciocinio rejeita tool".
335
- /// </summary>
336
245
  [Fact]
337
246
  public void TheSameAliasWithoutToolsCreatesTheAgentNormally()
338
247
  {
@@ -347,7 +256,6 @@ public sealed class AgentFactoryTests
347
256
  Assert.NotNull(agent);
348
257
  }
349
258
 
350
- /// <summary>Alias desconhecido falha na fabrica com o mesmo erro do registry.</summary>
351
259
  [Fact]
352
260
  public void AnUnknownAliasFailsAtCreateTime()
353
261
  {
@@ -359,7 +267,6 @@ public sealed class AgentFactoryTests
359
267
  Assert.Equal(ModelRegistryError.UnknownAlias, error.Reason);
360
268
  }
361
269
 
362
- /// <summary>Seletor que devolve nulo e recusa explicita, nunca agente sem cliente.</summary>
363
270
  [Fact]
364
271
  public void ASelectorThatReturnsNullIsRefusedInsteadOfProducingAClientlessAgent()
365
272
  {
@@ -374,10 +281,6 @@ public sealed class AgentFactoryTests
374
281
  Assert.Equal(AgentFactoryError.NoChatClient, error.Reason);
375
282
  }
376
283
 
377
- /// <summary>
378
- /// Sem nenhum encaixe preenchido, o agente e valido e responde. Um agente sem custo e
379
- /// sem telemetria nao e um agente pela metade.
380
- /// </summary>
381
284
  [Fact]
382
285
  public async Task AnAgentWithNoHooksIsStillAValidAgentThatAnswers()
383
286
  {
@@ -395,15 +298,7 @@ public sealed class AgentFactoryTests
395
298
  Assert.Equal("pronto", response.Text);
396
299
  }
397
300
 
398
- // ---------------------------------------------------------------------------------
399
- // Apoio dos testes
400
- // ---------------------------------------------------------------------------------
401
-
402
- /// <summary>
403
- /// Roteiro de duas voltas: o modelo pede a tool, a tool responde, o modelo conclui. Os
404
- /// numeros de uso sao escolhidos para que a soma (40/10) seja distinguivel de qualquer
405
- /// uma das parcelas.
406
- /// </summary>
301
+ // Os usos por volta (10/4 e 30/6) diferem da soma (40/10), para a soma nao se confundir com uma parcela.
407
302
  private static FakeChatClient ScriptedToolCallThenAnswer()
408
303
  => new FakeChatClient()
409
304
  .Enqueue(new ChatResponse(new ChatMessage(
@@ -433,10 +328,6 @@ public sealed class AgentFactoryTests
433
328
  return factory.Create(spec);
434
329
  }
435
330
 
436
- /// <summary>
437
- /// Marca a entrada e a saida de uma posicao do pipeline. E o unico jeito de observar a
438
- /// ORDEM: ela nao esta em nenhuma propriedade, so na sequencia das chamadas.
439
- /// </summary>
440
331
  private sealed class ProbeChatClient(
441
332
  IChatClient inner,
442
333
  string tag,
@@ -465,7 +356,6 @@ public sealed class AgentFactoryTests
465
356
  }
466
357
  }
467
358
 
468
- /// <summary>Cliente que nunca responde — existe so para o timeout ter o que cancelar.</summary>
469
359
  private sealed class NeverAnsweringChatClient : IChatClient
470
360
  {
471
361
  public async Task<ChatResponse> GetResponseAsync(
@@ -7,24 +7,8 @@ using Xunit;
7
7
 
8
8
  namespace Morph.AiKit.Tests;
9
9
 
10
- /// <summary>
11
- /// Canarios do agente-como-dado (T7). O que estes testes protegem nao e a existencia dos
12
- /// tipos — o compilador ja faz isso — e sim a UNICA regra estrutural que
13
- /// <see cref="AgentSpecOptions"/> tem: <b>nulo nao envia nada ao provider</b>.
14
- /// </summary>
15
- /// <remarks>
16
- /// A comparacao e feita por reflexao contra um <see cref="ChatOptions"/> virgem, e nao
17
- /// contra uma lista de propriedades escrita a mao, de proposito: uma propriedade nova no
18
- /// <c>Microsoft.Extensions.AI</c> entra na comparacao sozinha. Uma lista escrita a mao
19
- /// envelheceria em silencio no proximo bump do pin — que e exatamente o defeito que a
20
- /// feature inteira existe para matar.
21
- /// </remarks>
22
10
  public sealed class AgentSpecTests
23
11
  {
24
- // ---------------------------------------------------------------------------------
25
- // A regra estrutural: nulo = nao envia
26
- // ---------------------------------------------------------------------------------
27
-
28
12
  [Fact]
29
13
  public void FreshOptionsProduceChatOptionsIndistinguishableFromPristine()
30
14
  {
@@ -68,9 +52,7 @@ public sealed class AgentSpecTests
68
52
  [Fact]
69
53
  public void AgentLevelKnobsNeverLeakIntoChatOptions()
70
54
  {
71
- // As quatro opcoes de nivel de agente. Nenhuma tem casa em ChatOptions: elas sao
72
- // lidas pela AgentFactory (T10) e pela telemetria (T12). Se alguem "resolver" isso
73
- // despejando-as em AdditionalProperties, este teste morre — que e o ponto.
55
+ // As quatro opcoes de nivel de agente nao tem casa em ChatOptions: a AgentFactory e a telemetria as leem.
74
56
  var produced = new AgentSpecOptions
75
57
  {
76
58
  MaxToolIterations = 7,
@@ -86,10 +68,6 @@ public sealed class AgentSpecTests
86
68
  $"Opcao de nivel de agente vazou para ChatOptions: {string.Join(", ", differing)}");
87
69
  }
88
70
 
89
- // ---------------------------------------------------------------------------------
90
- // Mapeamento de cada opcao de nivel de requisicao para a sua casa
91
- // ---------------------------------------------------------------------------------
92
-
93
71
  [Fact]
94
72
  public void EachRequestLevelOptionLandsInItsOwnChatOptionsHome()
95
73
  {
@@ -115,8 +93,7 @@ public sealed class AgentSpecTests
115
93
  Assert.Same(ChatToolMode.RequireAny, produced.ToolMode);
116
94
  Assert.Equal(ReasoningEffort.Medium, produced.Reasoning?.Effort);
117
95
 
118
- // service tier NAO existe em ChatOptions no Microsoft.Extensions.AI 10.9.0 (apurado
119
- // por reflexao sobre o assembly do pin) — viaja em AdditionalProperties.
96
+ // ServiceTier nao existe em ChatOptions no Microsoft.Extensions.AI 10.9.0: viaja em AdditionalProperties.
120
97
  Assert.NotNull(produced.AdditionalProperties);
121
98
  Assert.True(produced.AdditionalProperties.TryGetValue(
122
99
  AgentSpecOptions.ServiceTierPropertyName,
@@ -127,8 +104,6 @@ public sealed class AgentSpecTests
127
104
  [Fact]
128
105
  public void AnExplicitlyEmptyStopSequenceListIsSentAsWritten()
129
106
  {
130
- // "Nulo nao envia; nao-nulo envia o que voce escreveu." Tratar lista vazia como
131
- // nula seria uma excecao escondida na unica regra do tipo.
132
107
  var produced = new AgentSpecOptions { StopSequences = [] }.ToChatOptions();
133
108
 
134
109
  Assert.Equal([nameof(ChatOptions.StopSequences)], DiffAgainstPristine(produced));
@@ -139,7 +114,6 @@ public sealed class AgentSpecTests
139
114
  [Fact]
140
115
  public void ApplyToNeverClobbersWhatTheCallerAlreadySet()
141
116
  {
142
- // A AgentFactory (T10) preenche Tools e ResponseFormat; ApplyTo nao pode apaga-los.
143
117
  var destination = new ChatOptions
144
118
  {
145
119
  ResponseFormat = ChatResponseFormat.Json,
@@ -158,10 +132,6 @@ public sealed class AgentSpecTests
158
132
  public void ApplyToRejectsANullDestination()
159
133
  => Assert.Throws<ArgumentNullException>(() => new AgentSpecOptions().ApplyTo(null!));
160
134
 
161
- // ---------------------------------------------------------------------------------
162
- // AgentSpec
163
- // ---------------------------------------------------------------------------------
164
-
165
135
  [Fact]
166
136
  public void AFreshSpecCarriesNoToolsNoSchemaAndSendsNoOptions()
167
137
  {
@@ -194,11 +164,9 @@ public sealed class AgentSpecTests
194
164
  var withoutOverride = new AgentSpec { Alias = "a", Instructions = "i" };
195
165
  var withOverride = withoutOverride with { Api = AgentApi.Chat };
196
166
 
197
- // alias manda quando a spec nao opinou
198
167
  Assert.Equal(AgentApi.Responses, withoutOverride.ResolveApi(AgentApi.Responses));
199
168
  Assert.Equal(AgentApi.Chat, withoutOverride.ResolveApi(AgentApi.Chat));
200
169
 
201
- // spec manda quando opinou — inclusive contra o alias
202
170
  Assert.Equal(AgentApi.Chat, withOverride.ResolveApi(AgentApi.Responses));
203
171
  }
204
172
 
@@ -222,15 +190,6 @@ public sealed class AgentSpecTests
222
190
  Assert.Equal("object", spec.OutputSchema.Value.GetProperty("type").GetString());
223
191
  }
224
192
 
225
- // ---------------------------------------------------------------------------------
226
- // Helpers
227
- // ---------------------------------------------------------------------------------
228
-
229
- /// <summary>
230
- /// Nomes das propriedades de <see cref="ChatOptions"/> em que <paramref name="actual"/>
231
- /// difere de um <see cref="ChatOptions"/> virgem — ou seja, o conjunto exato do que
232
- /// seria enviado ao provider.
233
- /// </summary>
234
193
  private static IReadOnlyList<string> DiffAgainstPristine(ChatOptions actual)
235
194
  {
236
195
  var pristine = new ChatOptions();