@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
@@ -1541,13 +1541,17 @@
1541
1541
  "fake clock",
1542
1542
  "integration test",
1543
1543
  "ProblemDetails",
1544
- "error body assertion"
1544
+ "error body assertion",
1545
+ "test budget",
1546
+ "orçamento de teste",
1547
+ "[Theory] table"
1545
1548
  ],
1546
1549
  "anchors": {
1547
1550
  "clock": "#rule-1--inject-timeprovider-fake-it-with-faketimeprovider-in-tests",
1548
- "errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code"
1551
+ "errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code",
1552
+ "test-budget": "#test-budget"
1549
1553
  },
1550
- "digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches.",
1554
+ "digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior (edge cases included) are rows of one [Theory], never copied [Fact]s; no test whose only assertion is a copy, config or prose string (that is an analyzer or a publish-time validation, outside the suite count); shared fixtures before new helpers; TDD for bugs (regression first) and pure logic; a surviving mutant is killed by tightening an assertion or adding a row, not by a new test.",
1551
1555
  "verifiedAgainst": ".NET 10 + xUnit 2 + `Microsoft.Extensions.TimeProvider.Testing`.",
1552
1556
  "provedBy": []
1553
1557
  },
@@ -2136,8 +2140,8 @@
2136
2140
  "patterns": "#three-tier-hierarchy",
2137
2141
  "shadcn": "#tier-1--shadcnui-primitives"
2138
2142
  },
2139
- "digest": "Server Components for data fetching and static content. Client Components for interactivity. shadcn/ui components are copy-paste — customize in src/components/ui/. Never use index.tsx for named exports. One component per file. Avoid prop drilling — use composition or Context API.",
2140
- "verifiedAgainst": "Next.js 15 + shadcn/ui CLI + TanStack Table 8.",
2143
+ "digest": "Three tiers: components/ui is shadcn CLI output (never edit, wrap instead); components/ composes primitives with no domain knowledge; features/*/components knows the domain. 'use client' only for hooks, events or browser APIs; never pass raw API data as props — type and validate it first. The shared data table is specified once, in admin-app/data-table.md.",
2144
+ "verifiedAgainst": "Next.js 16 + shadcn 4 CLI (the code blocks typecheck under `tsc --strict`).",
2141
2145
  "provedBy": []
2142
2146
  },
2143
2147
  {
@@ -2185,15 +2189,15 @@
2185
2189
  "react-hook-form",
2186
2190
  "Zod",
2187
2191
  "form validation",
2188
- "Server Actions forms",
2189
- "useFormState"
2192
+ "shadcn Field",
2193
+ "Controller"
2190
2194
  ],
2191
2195
  "anchors": {
2192
2196
  "validation": "#schema-composition",
2193
2197
  "submission": "#complete-form-pattern"
2194
2198
  },
2195
- "digest": "react-hook-form with Zod resolver for client forms. Define Zod schema before component. Server Actions for mutations: 'use server' directive. Use useFormState/useActionState for Server Action feedback. Never submit to API routes — use Server Actions or React Query mutations. Display field-level errors from Zod.",
2196
- "verifiedAgainst": "Next.js 15 + react-hook-form 7 + Zod 4 + @hookform/resolvers 3.",
2199
+ "digest": "react-hook-form + Zod + shadcn Field components wired through Controller. Define the Zod schema first and derive the type with z.infer (z.input/z.output when it coerces); zodResolver connects it; Field/FieldLabel/FieldError render each field and its error; never useState per field; submit through TanStack Query useMutation and put a failed submit on root. Edit forms reuse the create schema (.partial()). Zod 4: z.email(), a single `error` param. A legacy shadcn Form/FormField screen is migrated when touched, never extended. Admin CRUD forms add the deltas of admin-app/forms.md.",
2200
+ "verifiedAgainst": "Next.js 16 + react-hook-form 7 + Zod 4 + @hookform/resolvers 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
2197
2201
  "provedBy": []
2198
2202
  },
2199
2203
  {
@@ -2357,13 +2361,17 @@
2357
2361
  "testing-library",
2358
2362
  "Playwright",
2359
2363
  "Next.js testing",
2360
- "component tests"
2364
+ "component tests",
2365
+ "test budget",
2366
+ "orçamento de teste",
2367
+ "it.each table"
2361
2368
  ],
2362
2369
  "anchors": {
2363
2370
  "unit": "#component-test-pattern",
2364
- "e2e": "#test-file-co-location"
2371
+ "e2e": "#test-file-co-location",
2372
+ "test-budget": "#test-budget"
2365
2373
  },
2366
- "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output.",
2374
+ "digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior are rows of one it.each, never copied it() blocks; no test whose only assertion is a copy string (lint or publish-time validation instead); shared wrappers, MSW handlers and factories before new helpers; TDD for bugs and pure logic, component tests may come after the code.",
2367
2375
  "verifiedAgainst": "Vitest 3 + React Testing Library 16 + MSW 2.",
2368
2376
  "provedBy": []
2369
2377
  },
@@ -2681,8 +2689,167 @@
2681
2689
  "tools": "#mcp-providers-by-phase",
2682
2690
  "setup": "#mcp-availability-detection"
2683
2691
  },
2684
- "digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Test tools with MCP Inspector before integrating.",
2685
- "verifiedAgainst": "Claude Code MCP tooling — context7, Playwright; GitHub via `gh` CLI; Neon via `neonctl`.",
2692
+ "digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Tool names follow where the server is configured: mcp__context7__query-docs from .mcp.json, mcp__plugin_context7_context7__query-docs from the plugin; a deferred tool loads via ToolSearch. Test tools with MCP Inspector before integrating.",
2693
+ "verifiedAgainst": "Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`.",
2694
+ "provedBy": []
2695
+ },
2696
+ {
2697
+ "id": "frontend-admin-app-data-table",
2698
+ "name": "data table",
2699
+ "path": "frontend/admin-app/data-table.md",
2700
+ "category": "frontend",
2701
+ "subcategory": "admin-app",
2702
+ "tags": [
2703
+ "frontend",
2704
+ "admin-app"
2705
+ ],
2706
+ "stacks": [
2707
+ "nextjs"
2708
+ ],
2709
+ "aliases": [
2710
+ "data table",
2711
+ "admin list",
2712
+ "server-side pagination",
2713
+ "paginação",
2714
+ "tabela de dados",
2715
+ "ordenação no servidor",
2716
+ "empty state",
2717
+ "row selection",
2718
+ "bulk actions",
2719
+ "manualPagination"
2720
+ ],
2721
+ "anchors": {
2722
+ "contract": "#the-list-contract-url-and-api",
2723
+ "table": "#the-shared-datatable-tier-2",
2724
+ "states": "#the-three-states",
2725
+ "mobile": "#mobile"
2726
+ },
2727
+ "digest": "Every admin list is paged, sorted and filtered on the server, with page/pageSize/sort/dir/filters in the URL (searchParams parsed by a Zod schema with .catch defaults) and the API answering { items, totalCount } over an OrderBy plus a unique id tiebreaker. The Server Component page reads searchParams and wraps the results in a Suspense keyed by the query; one shared components/data-table.tsx on TanStack Table 9 sets manualPagination + rowCount, manualSorting and selection by a stable getRowId, and only writes the URL (router.replace with scroll:false, page reset on any filter/sort change). Three non-happy states always: loading skeleton, empty (no records vs no match for the filters), error as a value with retry via router.refresh(). Compact density, right-aligned tabular numbers, one inline row action plus a menu, a bulk bar over the selected page, a card list below md. Never 'first N + see all' instead of paging.",
2728
+ "verifiedAgainst": "Next.js 16 App Router + @tanstack/react-table 9 + Zod 4 + shadcn 4 (the code blocks typecheck under `tsc --strict`).",
2729
+ "provedBy": []
2730
+ },
2731
+ {
2732
+ "id": "frontend-admin-app-feedback",
2733
+ "name": "feedback",
2734
+ "path": "frontend/admin-app/feedback.md",
2735
+ "category": "frontend",
2736
+ "subcategory": "admin-app",
2737
+ "tags": [
2738
+ "frontend",
2739
+ "admin-app"
2740
+ ],
2741
+ "stacks": [
2742
+ "nextjs"
2743
+ ],
2744
+ "aliases": [
2745
+ "confirmation dialog",
2746
+ "confirm dialog",
2747
+ "delete confirmation",
2748
+ "toast",
2749
+ "sonner",
2750
+ "toast messages",
2751
+ "undo toast",
2752
+ "diálogo de confirmação"
2753
+ ],
2754
+ "anchors": {
2755
+ "when": "#when-to-confirm",
2756
+ "dialog": "#the-confirmation-dialog-tier-2",
2757
+ "catalog": "#the-toast-catalog"
2758
+ },
2759
+ "digest": "One ConfirmDialog (controlled shadcn AlertDialog, a plain Button that awaits the action, stays open showing the error on failure, optional type-to-confirm) used only for destructive-irreversible, bulk or discard-changes actions; a reversible action gets a 'Desfazer' toast instead. The title names the object and the confirm label repeats the verb. Every toast string comes from a catalog (lib/toast-messages.ts for generic outcomes + per-entity EntityMessages, because Portuguese agrees in gender and number); sonner toast.success/toast.error with an id per action, a longer duration for errors, errors mapped by status (403, 409, network), never the server's raw text, and field validation never in a toast.",
2760
+ "verifiedAgainst": "shadcn 4 `AlertDialog` (Radix) + sonner 2 + TanStack Query 5 (the code blocks typecheck under `tsc --strict`).",
2761
+ "provedBy": []
2762
+ },
2763
+ {
2764
+ "id": "frontend-admin-app-forms",
2765
+ "name": "forms",
2766
+ "path": "frontend/admin-app/forms.md",
2767
+ "category": "frontend",
2768
+ "subcategory": "admin-app",
2769
+ "tags": [
2770
+ "frontend",
2771
+ "admin-app"
2772
+ ],
2773
+ "stacks": [
2774
+ "nextjs"
2775
+ ],
2776
+ "aliases": [
2777
+ "admin form",
2778
+ "crud form",
2779
+ "server validation errors",
2780
+ "ValidationProblemDetails",
2781
+ "unsaved changes",
2782
+ "formulário"
2783
+ ],
2784
+ "anchors": {
2785
+ "server-errors": "#server-errors-on-the-field",
2786
+ "placement": "#where-the-form-lives",
2787
+ "dirty": "#unsaved-changes"
2788
+ },
2789
+ "digest": "Admin forms use the one pattern of nextjs/forms.md (Zod schema, zodResolver, react-hook-form Controller + shadcn Field/FieldError, useMutation) plus the CRUD deltas: map .NET ValidationProblemDetails errors onto their fields with setError (unmatched ones go to root.serverError), mode 'onTouched', submit disabled only while saving, form.reset(values) after a successful save, a guard for unsaved changes, the three useForm generics (z.input / unknown / z.output) when the schema coerces, and placement by size: dialog up to 4 fields, sheet or page up to 12, its own page beyond that, a wizard when steps depend on each other.",
2790
+ "verifiedAgainst": "react-hook-form 7 + @hookform/resolvers 5 + Zod 4 + TanStack Query 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
2791
+ "provedBy": []
2792
+ },
2793
+ {
2794
+ "id": "frontend-admin-app-page-structure",
2795
+ "name": "page structure",
2796
+ "path": "frontend/admin-app/page-structure.md",
2797
+ "category": "frontend",
2798
+ "subcategory": "admin-app",
2799
+ "tags": [
2800
+ "frontend",
2801
+ "admin-app"
2802
+ ],
2803
+ "stacks": [
2804
+ "nextjs"
2805
+ ],
2806
+ "aliases": [
2807
+ "tabs",
2808
+ "abas",
2809
+ "long page",
2810
+ "página longa",
2811
+ "wizard",
2812
+ "multi-step form",
2813
+ "stepper",
2814
+ "detail page"
2815
+ ],
2816
+ "anchors": {
2817
+ "decision": "#tabs-long-page-or-wizard",
2818
+ "tabs": "#tabs-in-the-url",
2819
+ "wizard": "#wizard-for-long-flows"
2820
+ },
2821
+ "digest": "Decide per screen and write it in the mockup checklist: tabs for 3+ independent sections, a long page with sticky section nav when sections are read together or share one save, a plain page for 2 short blocks, a wizard when later fields depend on earlier answers. The active tab lives in ?tab= and only its content is rendered and fetched on the server; switching tab drops the list params of the tab being left. Unbounded related lists get their own paged tab, never a capped preview. A wizard keeps one react-hook-form across steps, validates each step with trigger(stepFields), never loses data going back, and commits on a final review step.",
2822
+ "verifiedAgainst": "Next.js 16 App Router + shadcn 4 `Tabs` (Radix) + react-hook-form 7 + Zod 4 (the code blocks typecheck under `tsc --strict`).",
2823
+ "provedBy": []
2824
+ },
2825
+ {
2826
+ "id": "architecture-code-comments",
2827
+ "name": "code comments",
2828
+ "path": "architecture/code-comments.md",
2829
+ "category": "architecture",
2830
+ "subcategory": null,
2831
+ "tags": [
2832
+ "architecture",
2833
+ "comments"
2834
+ ],
2835
+ "stacks": [
2836
+ "*"
2837
+ ],
2838
+ "aliases": [
2839
+ "code comments",
2840
+ "comentário de código",
2841
+ "comment policy",
2842
+ "política de comentário",
2843
+ "comentário de processo",
2844
+ "self-documenting code"
2845
+ ],
2846
+ "anchors": {
2847
+ "comment-policy": "#o-que-merece-comentário",
2848
+ "not-worth": "#o-que-não-merece",
2849
+ "size": "#tamanho"
2850
+ },
2851
+ "digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and decisions.md), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. The verify validators node (code-budget) warns on added comments that cite process and on added comment density.",
2852
+ "verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo).",
2686
2853
  "provedBy": []
2687
2854
  }
2688
2855
  ],
@@ -2767,6 +2934,14 @@
2767
2934
  "seedance": [
2768
2935
  "ai-agents-media-video",
2769
2936
  "frontend-design-system-ai-video-generation"
2937
+ ],
2938
+ "test budget": [
2939
+ "backend-dotnet-testing",
2940
+ "frontend-nextjs-testing"
2941
+ ],
2942
+ "orçamento de teste": [
2943
+ "backend-dotnet-testing",
2944
+ "frontend-nextjs-testing"
2770
2945
  ]
2771
2946
  }
2772
2947
  }
@@ -30,8 +30,7 @@ Preciso que o agente saiba X.
30
30
  Plano por fase (instruções + **allow-list de tools** conforme o estado do funil) **não** é caso de provider. É C# puro, montado antes de criar a requisição:
31
31
 
32
32
  ```csharp
33
- // O CÓDIGO decide o modo; o LLM só classifica. A allow-list de tools é a parte
34
- // que um provider não consegue dar: AIContext.Tools ACRESCENTA tools, não remove.
33
+ // A allow-list não cabe num provider: AIContext.Tools ACRESCENTA tools, não remove.
35
34
  public sealed record OperatorPlan(string Instructions, IReadOnlyList<AITool> Tools);
36
35
 
37
36
  public static OperatorPlan PlanFor(FunnelStage stage) => stage switch
@@ -92,10 +91,8 @@ Use um `AIContextProvider` quando o agente precisa de **contexto dinâmico compu
92
91
 
93
92
  **Método a sobrescrever:**
94
93
 
95
- ```csharp
96
- // ProvideAIContextAsync → chamado ANTES do LLM (before-run hook) — injeta contexto
97
- // StoreAIContextAsync → chamado APÓS o LLM (after-run hook) — persiste estado se necessário
98
- ```
94
+ - `ProvideAIContextAsync` → chamado ANTES do LLM (before-run hook) — injeta contexto
95
+ - `StoreAIContextAsync` → chamado APÓS o LLM (after-run hook) — persiste estado se necessário (opcional)
99
96
 
100
97
  ### A invariante de instância — a mesma de `ai-agents-agent-session`
101
98
 
@@ -122,32 +119,33 @@ Consequência prática: **compaction só faz sentido com histórico local**. Com
122
119
 
123
120
  ## Exemplo C# — context provider que injeta data atual e perfil do tenant
124
121
 
122
+ O bloco tem três partes: o provider, o registro do agente com o provider no DI (`Program.cs`) e o uso
123
+ normal. `ITenantContext` é implementado via `IHttpContextAccessor` ou `AsyncLocal` para resolver o
124
+ tenant em runtime, nunca registrado como instância estática. O contexto volta como instrução
125
+ adicional, não como mensagem do usuário, e `StoreAIContextAsync` fica vazio porque o provider é
126
+ stateless. No uso, o provider é invocado a cada `RunAsync`: o agente já sabe a data, o nome do tenant
127
+ e o idioma, sem código extra no caller.
128
+
125
129
  ```csharp
126
130
  using Microsoft.Agents.AI;
127
131
  using Microsoft.Extensions.AI;
128
132
 
129
- // ── 1. Definir o provider ──
130
133
  public sealed class TenantContextProvider : AIContextProvider
131
134
  {
132
135
  private readonly ITenantService _tenantService;
133
136
  private readonly ITenantContext _tenantContext;
134
137
 
135
- // ITenantContext deve ser implementado via IHttpContextAccessor ou AsyncLocal
136
- // para resolver o tenant atual em runtime — não registre como instância estática.
137
- // O AIAgent é singleton, então o provider também é singleton; o tenant corrente
138
- // é lido de forma ambient (IHttpContextAccessor/AsyncLocal) a cada chamada.
138
+ // Singleton como o AIAgent: o tenant corrente é lido ambient a cada chamada, nunca guardado.
139
139
  public TenantContextProvider(ITenantService tenantService, ITenantContext tenantContext)
140
140
  {
141
141
  _tenantService = tenantService;
142
142
  _tenantContext = tenantContext;
143
143
  }
144
144
 
145
- // Sobrescreva: injetado ANTES de cada chamada ao LLM
146
145
  protected override async ValueTask<AIContext> ProvideAIContextAsync(
147
146
  InvokingContext context,
148
147
  CancellationToken cancellationToken = default)
149
148
  {
150
- // Tenant resolvido via serviço injetado (ambient context, ex: IHttpContextAccessor)
151
149
  var tenantId = _tenantContext.TenantId ?? "unknown";
152
150
  var tenant = await _tenantService.GetTenantAsync(tenantId, cancellationToken);
153
151
 
@@ -159,20 +157,16 @@ public sealed class TenantContextProvider : AIContextProvider
159
157
 
160
158
  return new AIContext
161
159
  {
162
- // Injeta como instrução adicional — não como mensagem do usuário
163
160
  Instructions = instructions,
164
161
  };
165
162
  }
166
163
 
167
- // Sobrescreva (opcional): chamado APÓS o LLM — use para persistir estado derivado da resposta
168
164
  protected override ValueTask StoreAIContextAsync(
169
165
  InvokedContext context,
170
166
  CancellationToken cancellationToken = default)
171
- => ValueTask.CompletedTask; // stateless provider — nada a persistir
167
+ => ValueTask.CompletedTask;
172
168
  }
173
169
 
174
- // ── 2. Registrar o agente com o provider no DI ──
175
- // Program.cs
176
170
  builder.Services.AddSingleton<AIAgent>(sp =>
177
171
  {
178
172
  var registry = sp.GetRequiredService<ModelRegistry>();
@@ -187,19 +181,15 @@ builder.Services.AddSingleton<AIAgent>(sp =>
187
181
  });
188
182
  });
189
183
 
190
- // ── Alternativa: pipeline de chat (compacta/injeta SÓ o request em voo) ──
191
- // using Microsoft.Extensions.AI; // a extensão vive na assembly Microsoft.Agents.AI
192
- // IChatClient enriched = chatClient.AsBuilder()
193
- // .UseAIContextProviders(new TenantContextProvider(tenantSvc, tenantContext))
194
- // .Build();
195
-
196
- // ── 3. Uso normal — provider é invocado automaticamente a cada RunAsync ──
197
184
  AgentSession session = await agent.CreateSessionAsync();
198
185
  AgentResponse response = await agent.RunAsync("Quais são meus benefícios do plano?", session);
199
186
  Console.WriteLine(response.Text);
200
- // O agente já sabe a data, o nome do tenant e o idioma — sem código extra no caller
201
187
  ```
202
188
 
189
+ **Alternativa: pipeline de chat**, que compacta/injeta **só** o request em voo: registre o provider
190
+ com `.UseAIContextProviders(...)` no `ChatClientBuilder` (`chatClient.AsBuilder()`). O `using` é
191
+ `Microsoft.Extensions.AI`, mas a extensão vive na assembly `Microsoft.Agents.AI`.
192
+
203
193
  ---
204
194
 
205
195
  ## Context Provider vs Tools vs RAG
@@ -22,12 +22,11 @@ If neither applies, a plain workflow (or no workflow at all — see `ai-agents-s
22
22
 
23
23
  ## Human-in-the-Loop via `RequestPort`
24
24
 
25
- HITL is modeled with `RequestPort` — a special node in the workflow graph that pauses execution and waits for an external (human) response instead of processing automatically.
25
+ HITL is modeled with `RequestPort` — a special node in the workflow graph that pauses execution and waits for an external (human) response instead of processing automatically. Its two type arguments are the request type the port sends out and the response type it expects back.
26
26
 
27
27
  ```csharp
28
28
  using Microsoft.Agents.AI.Workflows;
29
29
 
30
- // Request type the port sends out, response type it expects back.
31
30
  RequestPort<ApprovalRequest, bool> approvalPort =
32
31
  RequestPort.Create<ApprovalRequest, bool>("RefundApproval");
33
32
 
@@ -47,8 +46,7 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
47
46
  switch (evt)
48
47
  {
49
48
  case RequestInfoEvent requestEvt:
50
- // Surface requestEvt.Request to a human (dashboard, email, Slack, ...).
51
- // This is where execution pauses until an external answer arrives.
49
+ // Execution pauses here until the human's answer arrives (dashboard, email, Slack, ...).
52
50
  bool approved = await GetHumanDecisionAsync(requestEvt.Request);
53
51
  await run.SendResponseAsync(requestEvt.Request.CreateResponse(approved));
54
52
  break;
@@ -77,8 +75,14 @@ O caso do agente único é o que a casa realmente tem (MORPH_OS: billing, envio
77
75
 
78
76
  ### O ciclo, medido em `Microsoft.Extensions.AI` 10.9.0
79
77
 
78
+ Quatro passos: (1) embrulhe a tool — `ApprovalRequiredAIFunction : DelegatingAIFunction`; (2) o run
79
+ **termina** pedindo aprovação — não bloqueia, não espera —, e `ToolApprovalRequestContent.ToolCall` é
80
+ um `ToolCallContent` (medido; **não** é `FunctionCallContent`, que aparece em
81
+ `ToolAutoApprovalRuleContext`); (3) responda **na mesma sessão**, com
82
+ `CreateResponse(bool approved, string? reason)`; (4) **repita** o passo 2 até não restar pendência —
83
+ uma volta pode pedir **outra** tool.
84
+
80
85
  ```csharp
81
- // 1. Embrulhe a tool. ApprovalRequiredAIFunction : DelegatingAIFunction.
82
86
  AIFunction charge = AIFunctionFactory.Create(BillingTools.ChargeCustomer);
83
87
  AITool guarded = new ApprovalRequiredAIFunction(charge);
84
88
 
@@ -88,22 +92,16 @@ AIAgent agent = registry.GetChatClient("text-default")
88
92
  AgentSession session = await agent.CreateSessionAsync();
89
93
  AgentResponse response = await agent.RunAsync("Cobre o cliente 42.", session);
90
94
 
91
- // 2. O run TERMINA pedindo aprovação — não bloqueia, não espera.
92
- // ToolApprovalRequestContent.ToolCall é um ToolCallContent (medido; NÃO é
93
- // FunctionCallContent — este último aparece em ToolAutoApprovalRuleContext).
94
95
  var pending = response.Messages
95
96
  .SelectMany(m => m.Contents)
96
97
  .OfType<ToolApprovalRequestContent>()
97
98
  .ToList();
98
99
 
99
- // 3. Responda NA MESMA SESSÃO. CreateResponse(bool approved, string? reason).
100
100
  if (pending.Count > 0)
101
101
  {
102
102
  var approvals = pending.Select(r => (AIContent)r.CreateResponse(approved: true, reason: "aprovado por lucas@…"));
103
103
  response = await agent.RunAsync(new ChatMessage(ChatRole.User, [.. approvals]), session);
104
104
  }
105
-
106
- // 4. REPITA o passo 2 até não restar pendência. Uma volta pode pedir OUTRA tool.
107
105
  ```
108
106
 
109
107
  **Os quatro pontos que erram na primeira tentativa:**
@@ -157,10 +155,11 @@ Checkpointing is part of the **base, GA** `Microsoft.Agents.AI.Workflows` namesp
157
155
  | `CheckpointManager.CreateJson(new FileSystemJsonCheckpointStore(dir))` | Local disk, survives process restart | Single-instance VPS deployments — no Azure, no preview package |
158
156
  | Custom `ICheckpointStore` implementation | Whatever backend you implement it against | **Recommended for production** — implement against the project's own Neon Postgres table, matching the ownership model already used by `ai-agents-rag-custom-pgvector` |
159
157
 
158
+ Local-disk durability, with no Azure dependency:
159
+
160
160
  ```csharp
161
161
  using Microsoft.Agents.AI.Workflows;
162
162
 
163
- // Local-disk durability — no Azure dependency:
164
163
  var checkpointFolder = Directory.CreateDirectory("./checkpoints");
165
164
  CheckpointManager checkpointManager = CheckpointManager.CreateJson(
166
165
  new FileSystemJsonCheckpointStore(checkpointFolder));
@@ -171,13 +170,12 @@ await foreach (WorkflowEvent evt in run.WatchStreamAsync())
171
170
  if (evt is SuperStepCompletedEvent stepEvt)
172
171
  {
173
172
  CheckpointInfo? checkpoint = stepEvt.CompletionInfo?.Checkpoint;
174
- // Persist checkpoint.CheckpointId alongside the business record
175
- // (e.g. the refund request row) so it can be resumed after a restart.
173
+ // Persist checkpoint.CheckpointId with the business record (e.g. the refund row) to resume after a restart.
176
174
  }
177
175
  }
178
176
  ```
179
177
 
180
- Resuming after a restart — rehydrate into a **new** run instance from the last known checkpoint:
178
+ Resuming after a restart — rehydrate into a **new** run instance from the last known checkpoint. A pending `RequestPort` request is re-emitted as a `RequestInfoEvent`, so the paused approval survives the restart:
181
179
 
182
180
  ```csharp
183
181
  StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
@@ -185,8 +183,6 @@ StreamingRun resumedRun = await InProcessExecution.ResumeStreamingAsync(
185
183
 
186
184
  await foreach (WorkflowEvent evt in resumedRun.WatchStreamAsync())
187
185
  {
188
- // A pending RequestPort request is re-emitted here as a RequestInfoEvent —
189
- // the paused approval survives the restart.
190
186
  if (evt is RequestInfoEvent requestEvt) { /* re-surface to the human */ }
191
187
  }
192
188
  ```
@@ -211,14 +207,12 @@ internal sealed class RefundAnalyzerExecutor() : Executor("RefundAnalyzer")
211
207
 
212
208
  ### `ICheckpointStore` — Neon/Postgres-backed (recommended production pattern)
213
209
 
214
- Implementing `ICheckpointStore` against the project's own Postgres database keeps checkpoint durability inside the same infrastructure the rest of the project already trusts (see `infrastructure-neon-setup`), instead of introducing a new storage dependency:
210
+ Implementing `ICheckpointStore` against the project's own Postgres database keeps checkpoint durability inside the same infrastructure the rest of the project already trusts (see `infrastructure-neon-setup`), instead of introducing a new storage dependency. The store persists and retrieves checkpoint blobs in a `workflow_checkpoints` table (`workflow_id`, `checkpoint_id`, `payload` jsonb/bytea, `created_at`), implemented per the `ICheckpointStore` contract in the `Microsoft.Agents.AI.Workflows` docs:
215
211
 
216
212
  ```csharp
217
213
  public sealed class PostgresCheckpointStore(NpgsqlDataSource dataSource) : ICheckpointStore
218
214
  {
219
- // Persist/retrieve checkpoint blobs in a workflow_checkpoints table
220
- // (workflow_id, checkpoint_id, payload jsonb/bytea, created_at).
221
- // Implement per the ICheckpointStore contract — see Microsoft.Agents.AI.Workflows docs.
215
+ // ICheckpointStore members over workflow_checkpoints — elided.
222
216
  }
223
217
  ```
224
218
 
@@ -50,8 +50,7 @@ using Microsoft.Extensions.AI;
50
50
  using OpenAI.Images;
51
51
 
52
52
  #pragma warning disable MEAI001 // IImageGenerator e experimental: a superficie pode mudar entre menores
53
- // AsIImageGenerator() vem de Microsoft.Extensions.AI.OpenAI — um pacote A MAIS.
54
- // Sem ele, escreva o adaptador (e o kit escreve: ver a nota abaixo).
53
+ // AsIImageGenerator() exige o pacote Microsoft.Extensions.AI.OpenAI, a mais (ver a nota abaixo).
55
54
  IImageGenerator generator = new ImageClient("gpt-image-2", apiKey).AsIImageGenerator();
56
55
 
57
56
  var response = await generator.GenerateAsync(
@@ -197,35 +196,33 @@ trocar a foto de referência **é** outro pedido, mesmo com o prompt idêntico.
197
196
 
198
197
  ## Exemplo C# completo
199
198
 
199
+ O adaptador é de **provider**, não um serviço público que um handler possa chamar: quem produz bytes
200
+ vive atrás do enfileiramento de `ai-agents/media-pipeline.md`, e no kit esse tipo é `internal` de
201
+ propósito. Quatro escolhas do bloco abaixo foram impostas pelo compilador:
202
+
203
+ - **Sem `using OpenAI.Images;`.** O gerador chega injetado e nenhum tipo do SDK da OpenAI aparece no
204
+ arquivo; deixar o `using` torna `ImageGenerationOptions` ambíguo (CS0104) entre
205
+ `Microsoft.Extensions.AI` e `OpenAI.Images`. Onde os dois convivem de verdade, como no adaptador do
206
+ kit, o tipo vai qualificado.
207
+ - **O pragma abre acima da classe**, não dentro do método: o parâmetro do construtor primário também
208
+ nomeia `IImageGenerator`, e `MEAI001` é erro por padrão, não aviso. Pragma dentro do corpo deixa a
209
+ assinatura de fora e o arquivo não compila.
210
+ - **O `IImageGenerator` chega injetado**, não construído aqui, e é isso que faz o exemplo compilar com
211
+ ou sem `Microsoft.Extensions.AI.OpenAI` no pin (ver a ressalva de §A API): com o pacote,
212
+ `imageClient.AsIImageGenerator()`; sem ele, um `IImageGenerator` escrito à mão sobre o `ImageClient`,
213
+ como em `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs`.
214
+ - **`ReadOnlyMemory<byte>` dos dois lados**, porque é o tipo real de `DataContent.Data`; declarar
215
+ `BinaryData` não converte (CS0029).
216
+
217
+ `OriginalImages` é o gancho de edição: com referências, o produto real é preservado em vez de recriado.
218
+
200
219
  ```csharp
201
- // SEM `using OpenAI.Images;`. Ele nao e necessario aqui — o gerador chega injetado e nenhum
202
- // tipo do SDK da OpenAI aparece neste arquivo — e deixa-lo torna `ImageGenerationOptions`
203
- // AMBIGUO (CS0104) entre Microsoft.Extensions.AI e OpenAI.Images. Onde os dois convivem de
204
- // verdade, como no adaptador do kit, o tipo vai QUALIFICADO.
205
220
  using Microsoft.Extensions.AI;
206
221
 
207
- // O pragma abre ACIMA da classe, e nao dentro do metodo: o parametro do construtor primario
208
- // tambem nomeia IImageGenerator, e MEAI001 e ERRO por padrao — nao aviso. Pragma dentro do
209
- // corpo deixa a assinatura de fora e o arquivo nao compila.
210
222
  #pragma warning disable MEAI001 // IImageGenerator/ImageGenerationRequest sao experimentais: a superficie pode mudar entre menores.
211
223
 
212
- /// <summary>
213
- /// Adaptador de PROVIDER. Note o que ele NAO e: um servico publico que um handler
214
- /// possa chamar. Quem produz bytes vive atras do enfileiramento descrito em
215
- /// ai-agents/media-pipeline.md, e no kit esse tipo e `internal` de proposito.
216
- /// </summary>
217
- /// <remarks>
218
- /// O IImageGenerator chega INJETADO, e nao construido aqui. As duas formas de obte-lo
219
- /// sao legitimas e a escolha e do seu pin (ver a ressalva da secao "A API"):
220
- /// - com Microsoft.Extensions.AI.OpenAI no pin: imageClient.AsIImageGenerator()
221
- /// - sem ele: um IImageGenerator escrito a mao sobre o ImageClient, como em
222
- /// templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs
223
- /// Injetar em vez de construir e o que faz ESTE exemplo compilar nos dois casos.
224
- /// </remarks>
225
224
  internal sealed class OpenAiImageAdapter(IImageGenerator generator)
226
225
  {
227
- // ReadOnlyMemory<byte> dos dois lados porque e o tipo REAL de DataContent.Data.
228
- // Declarar BinaryData aqui nao converte (CS0029) — e o compilador que decide isso.
229
226
  public async Task<ReadOnlyMemory<byte>> ProduceAsync(
230
227
  string prompt,
231
228
  IReadOnlyList<ReadOnlyMemory<byte>> references,
@@ -234,7 +231,6 @@ internal sealed class OpenAiImageAdapter(IImageGenerator generator)
234
231
  var request = new ImageGenerationRequest { Prompt = prompt };
235
232
  if (references.Count > 0)
236
233
  {
237
- // OriginalImages e o gancho de EDICAO: preserva o produto real em vez de recria-lo.
238
234
  request.OriginalImages = [.. references.Select(r => new DataContent(r, "image/png"))];
239
235
  }
240
236
 
@@ -49,6 +49,8 @@ O projeto injeta `AudioClient` via DI. Para transcrição com timestamps (ex: si
49
49
 
50
50
  ## Exemplo C# completo
51
51
 
52
+ Três entradas: arquivo, bytes (ex: upload do usuário) — as duas devolvem texto simples — e a verbose, com timestamps por palavra e segmento, útil para legendas ou busca por trecho.
53
+
52
54
  ```csharp
53
55
  using OpenAI.Audio;
54
56
  using Microsoft.Extensions.Configuration;
@@ -64,19 +66,12 @@ public sealed class TranscriptionService
64
66
  _client = new AudioClient(model, apiKey);
65
67
  }
66
68
 
67
- /// <summary>
68
- /// Transcreve áudio a partir de um arquivo e retorna texto simples.
69
- /// O transcript retornado pode ser passado diretamente como input para um AIAgent downstream.
70
- /// </summary>
71
69
  public async Task<string> TranscribeAsync(string audioFilePath, CancellationToken ct = default)
72
70
  {
73
71
  AudioTranscription transcription = await _client.TranscribeAudioAsync(audioFilePath, cancellationToken: ct);
74
72
  return transcription.Text;
75
73
  }
76
74
 
77
- /// <summary>
78
- /// Transcreve áudio a partir de bytes (ex: upload do usuário) e retorna texto simples.
79
- /// </summary>
80
75
  public async Task<string> TranscribeBytesAsync(
81
76
  Stream audioStream,
82
77
  string fileName,
@@ -93,9 +88,6 @@ public sealed class TranscriptionService
93
88
  return transcription.Text;
94
89
  }
95
90
 
96
- /// <summary>
97
- /// Transcreve com timestamps por palavra e segmento (útil para legendas ou busca por trecho).
98
- /// </summary>
99
91
  public async Task<AudioTranscription> TranscribeVerboseAsync(string audioFilePath, CancellationToken ct = default)
100
92
  {
101
93
  var options = new AudioTranscriptionOptions
@@ -109,21 +101,18 @@ public sealed class TranscriptionService
109
101
  }
110
102
  ```
111
103
 
112
- **Alimentando um agente downstream com o transcript:**
104
+ **Alimentando um agente downstream com o transcript** — ele é texto puro, então basta passá-lo como input para qualquer `AIAgent`; aqui, um Status Agent que extrai as ações da reunião:
113
105
 
114
106
  ```csharp
115
- // O transcript é texto puro — basta passar como input para qualquer AIAgent
116
107
  string transcript = await _transcription.TranscribeAsync("meeting-2024-05-19.mp3");
117
108
 
118
- // Ex: Status Agent que extrai ações da reunião
119
109
  AgentResponse<MeetingActions> actions = await _statusAgent.RunAsync<MeetingActions>(
120
110
  $"Extraia ações, decisões e responsáveis desta transcrição:\n\n{transcript}");
121
111
  ```
122
112
 
123
- **Registro DI:**
113
+ **Registro DI** (`Program.cs`):
124
114
 
125
115
  ```csharp
126
- // Program.cs
127
116
  builder.Services.AddSingleton<TranscriptionService>();
128
117
  ```
129
118