@polymorphism-tech/morph-spec 8.33.0 → 8.35.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 (253) hide show
  1. package/CLAUDE.md +22 -2
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +43 -5
  4. package/claude-plugin.json +1 -1
  5. package/docs/CHEATSHEET.md +5 -4
  6. package/docs/QUICKSTART.md +1 -1
  7. package/framework/CLAUDE.md +2 -2
  8. package/framework/MORPH.md +20 -22
  9. package/framework/agents.json +198 -44
  10. package/framework/ai-pin.json +22 -0
  11. package/framework/commands/morph-media.md +45 -0
  12. package/framework/docs/CLI.md +238 -10
  13. package/framework/evals/proof-rubrics/README.md +76 -0
  14. package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
  15. package/framework/evals/proof-rubrics/fiacao.md +54 -0
  16. package/framework/evals/proof-rubrics/guard-cego.md +52 -0
  17. package/framework/evals/proof-rubrics/media-claims.md +109 -0
  18. package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
  19. package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
  20. package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
  21. package/framework/evals/rubrics/architecture.md +30 -0
  22. package/framework/evals/rubrics/code-quality.md +26 -0
  23. package/framework/evals/rubrics/test-coverage.md +20 -0
  24. package/framework/hooks/README.md +11 -1
  25. package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
  26. package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
  27. package/framework/hooks/claude-code/core/chain.js +27 -7
  28. package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
  29. package/framework/hooks/claude-code/core/worktree-context.js +83 -14
  30. package/framework/hooks/claude-code/core/worktree-create.js +206 -0
  31. package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
  32. package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
  33. package/framework/hooks/shared/command-tokens.js +23 -0
  34. package/framework/hooks/shared/e2e-check.js +0 -0
  35. package/framework/hooks/shared/git-paths.js +25 -0
  36. package/framework/hooks/shared/hook-response.js +17 -0
  37. package/framework/hooks/shared/morph-cli.js +134 -0
  38. package/framework/rules/ai-agents-standards.md +14 -2
  39. package/framework/rules/infrastructure-standards.md +3 -1
  40. package/framework/schemas/ai-pin.schema.json +97 -0
  41. package/framework/schemas/config.schema.json +107 -0
  42. package/framework/schemas/feature.schema.json +23 -4
  43. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  44. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  45. package/framework/skills/core/morph-apply/SKILL.md +28 -7
  46. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  47. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  48. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  49. package/framework/skills/core/morph-implement/SKILL.md +20 -2
  50. package/framework/skills/core/morph-media/SKILL.md +215 -0
  51. package/framework/skills/core/morph-plan/SKILL.md +38 -4
  52. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  53. package/framework/skills/core/morph-review/SKILL.md +166 -9
  54. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  55. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  56. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  57. package/framework/standards/PROVENANCE.json +81 -3
  58. package/framework/standards/STANDARDS.json +1504 -830
  59. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  60. package/framework/standards/ai-agents/agent-session.md +256 -160
  61. package/framework/standards/ai-agents/agent-spec.md +196 -0
  62. package/framework/standards/ai-agents/batching.md +217 -193
  63. package/framework/standards/ai-agents/context-providers.md +261 -188
  64. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  65. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  66. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  67. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  68. package/framework/standards/ai-agents/guardrails.md +222 -0
  69. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  70. package/framework/standards/ai-agents/mcp-server.md +351 -250
  71. package/framework/standards/ai-agents/mcp-tools.md +250 -197
  72. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  73. package/framework/standards/ai-agents/media-video.md +267 -0
  74. package/framework/standards/ai-agents/middleware-patterns.md +241 -207
  75. package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
  76. package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
  77. package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
  78. package/framework/standards/ai-agents/modalities-vision.md +205 -201
  79. package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
  80. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  81. package/framework/standards/ai-agents/production.md +260 -231
  82. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  83. package/framework/standards/ai-agents/providers/model-registry.md +293 -158
  84. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  85. package/framework/standards/ai-agents/service-tier-flex.md +143 -135
  86. package/framework/standards/ai-agents/setup.md +166 -50
  87. package/framework/standards/ai-agents/structured-output.md +280 -249
  88. package/framework/standards/ai-agents/sweet-spot.md +129 -109
  89. package/framework/standards/ai-agents/testing-ai.md +236 -0
  90. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  91. package/framework/standards/ai-agents/workflows.md +27 -7
  92. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  93. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  94. package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
  95. package/framework/templates/REGISTRY.json +14 -1
  96. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
  97. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
  98. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  99. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  100. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  101. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  102. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  103. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  104. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  105. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  106. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  107. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  108. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  109. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  110. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  111. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  112. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  113. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  114. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  115. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  116. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  117. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  118. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  119. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  120. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  121. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  122. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  123. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  157. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  158. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  159. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  160. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  161. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  162. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  163. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  164. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  165. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  166. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  167. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  168. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  169. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  170. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  171. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  172. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  173. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  174. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  175. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  176. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  203. package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
  204. package/package.json +3 -3
  205. package/src/commands/e2e/e2e.js +91 -40
  206. package/src/commands/project/doctor.js +143 -6
  207. package/src/commands/project/env.js +95 -0
  208. package/src/commands/project/update.js +469 -153
  209. package/src/commands/project/worktree.js +274 -53
  210. package/src/commands/state/advance.js +24 -2
  211. package/src/commands/state/approve.js +33 -1
  212. package/src/commands/state/create.js +72 -11
  213. package/src/commands/state/delete.js +8 -0
  214. package/src/commands/state/finish.js +360 -53
  215. package/src/commands/state/gate-check.js +190 -6
  216. package/src/commands/state/score.js +36 -1
  217. package/src/commands/validation/verify.js +134 -25
  218. package/src/core/state/state-manager.js +9 -1
  219. package/src/dashboard/app/pages/HookInspector.jsx +430 -418
  220. package/src/dashboard/scanner/hooks-scanner.js +5 -1
  221. package/src/lib/ai/ai-checks.js +182 -0
  222. package/src/lib/ai/ai-project-scan.js +347 -0
  223. package/src/lib/ai/code-audit.js +181 -0
  224. package/src/lib/ai/comment-stripper.js +184 -0
  225. package/src/lib/ai/pin-drift.js +138 -0
  226. package/src/lib/ai/registry-audit.js +285 -0
  227. package/src/lib/e2e/compose-env.js +55 -14
  228. package/src/lib/e2e/stack-teardown.js +115 -0
  229. package/src/lib/evaluator-report.js +269 -15
  230. package/src/lib/generators/architecture-scanner.js +2 -1
  231. package/src/lib/git/default-branch.js +92 -4
  232. package/src/lib/graph/graphifyignore.js +2 -1
  233. package/src/lib/installers/effects.js +193 -0
  234. package/src/lib/installers/templates-reconcile.js +3 -3
  235. package/src/lib/risk-detector.js +284 -0
  236. package/src/lib/standards/verified-against.js +276 -0
  237. package/src/lib/tasks/eval-node.js +485 -0
  238. package/src/lib/tasks/linux-runner.js +297 -0
  239. package/src/lib/tasks/test-nodes.js +486 -0
  240. package/src/lib/tasks/test-runner.js +431 -37
  241. package/src/lib/validators/verify-runner.js +590 -28
  242. package/src/lib/worktree/dirs.js +32 -0
  243. package/src/lib/worktree/docker-audit.js +287 -0
  244. package/src/lib/worktree/env-allocator.js +69 -11
  245. package/src/lib/worktree/worktree-audit.js +70 -3
  246. package/src/scripts/setup-infra.js +24 -11
  247. package/src/utils/agents-installer.js +18 -8
  248. package/src/utils/claude-md-injector.js +10 -5
  249. package/src/utils/file-copier.js +28 -16
  250. package/src/utils/hooks-installer.js +75 -29
  251. package/src/utils/skills-installer.js +194 -187
  252. package/src/utils/version-checker.js +68 -3
  253. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -0,0 +1,389 @@
1
+ # Prompt Sources — onde vive o prompt de um agente
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 1 (on-keyword)
5
+ > **Keywords:** prompt sources, prompts in db, agent prompts, prompt versioning, prompt hot-reload, editable instructions, prompt management, prompt por tenant, spec publicado, reconciliação de prompt
6
+ > **Read by Claude in:** implement (quando as instruções de um agente precisam viver fora do código) e troubleshoot (quando a versão que rodou não é a que se esperava)
7
+
8
+ **Verified against:** Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não traz loader de prompt, e os dois mecanismos descritos aqui são destilados de três produtos em produção. As **quatro** superfícies de API que este standard cita foram medidas por reflexão, e vivem em **três assemblies diferentes** — o carimbo diz onde remedir, então o nome importa: `IChatClient.AsAIAgent(instructions:, name:)` é `Microsoft.Extensions.AI.ChatClientExtensions`, em **`Microsoft.Agents.AI` 1.20.0** (medida em 2026-09-08); `AIAgent.RunAsync` (as oito sobrecargas) e `AIAgent.CreateSessionAsync` são de `Microsoft.Agents.AI.AIAgent`, na assembly **`Microsoft.Agents.AI.Abstractions` 1.20.0** — **remedidas** em 2026-09-09, porque a leitura de 2026-09-08 estava errada no *quê*, e a de 2026-09-09 estava errada no *onde* (as duas retificações estão no callout abaixo); e `builder.AddAIAgent` (§Keyed singleton) é `Microsoft.Agents.AI.Hosting.HostApplicationBuilderAgentExtensions` — cinco sobrecargas, na assembly **`Microsoft.Agents.AI.Hosting` 1.20.0.0**, que vem do pacote **`Microsoft.Agents.AI.Hosting` `1.20.0-preview.260831.1`**: ele é **prerelease** e está **FORA do `ai-pin.json`** (apurado em `scripts/api-probe/companions.json`, 2026-09-08) — quem adotar esta seção registra a dependência prerelease em `decisions.md`, como manda `ai-agents-production`. **Duas ressalvas de escopo**, porque um carimbo que não as diz promete mais do que mediu: `Microsoft.Agents.AI.Abstractions` e `Microsoft.Extensions.AI.Abstractions` são nomes de **assembly** (elas viajam dentro dos pacotes `Microsoft.Agents.AI` e `Microsoft.Extensions.AI`), não ids de pacote; e `IMemoryCache`/`DbContext`, usados no padrão de cache e na §Keyed singleton, são superfícies de ASP.NET Core e EF Core — **fora do pin, não medidas aqui, verificação documental**. Last-verified: 2026-09-08.
9
+
10
+ ---
11
+
12
+ ## As duas fontes legítimas
13
+
14
+ Este standard nasceu conhecendo **um** mecanismo (a tabela versionada) e hoje conhece **dois**. A
15
+ troca de nome deste arquivo foi a consequência disso, não a causa: o assunto nunca foi "banco", foi
16
+ **de onde vem o texto de `Instructions`** (ver a nota "Estado do rename" adiante).
17
+
18
+ | Fonte | Forma | Quando cabe |
19
+ |---|---|---|
20
+ | **Banco versionado** | Tabela `agent_prompts` com `agent_key` + `version` + `is_active`; publica-se por INSERT + flip, nunca UPDATE. É o que as seções seguintes descrevem em detalhe | **Single-tenant**: um produto, um prompt por agente, editável sem deploy |
21
+ | **Repo (YAML) → composição → spec publicado** | Uma moldura de produto com buracos de política + um YAML por tenant que os preenche → um compositor determinístico → um blob renderizado publicado numa coluna `jsonb` → lido em runtime | **Produto × tenant**: a mesma moldura serve N clientes, cada um com política própria |
22
+
23
+ **A regra de escolha, em uma frase:**
24
+
25
+ > Se o texto varia por cliente, o prompt **não é um registro** — é a **saída de uma composição**, e o
26
+ > que se versiona é a **fonte**, não o resultado.
27
+
28
+ Lastro: o primeiro mecanismo tem **dois exemplares em produção** (num deles, 36 migrations de prompt,
29
+ a maior com 47.283 bytes); o segundo tem **um**, com blobs renderizados de 8.727 e 12.526 caracteres
30
+ para dois tenants do mesmo produto.
31
+
32
+ **O erro que a fronteira evita** é tentar espremer o segundo caso no primeiro: uma linha de
33
+ `agent_prompts` por tenant. Funciona com dois tenants e falha no décimo, porque a regra comum passa a
34
+ existir em N cópias e a próxima edição corrige N-1 delas.
35
+
36
+ ---
37
+
38
+ ## Por que prompts no DB
39
+
40
+ Hardcoded instructions in source code mean every prompt tweak requires a full build and deploy cycle. With prompts in the database the team can tune agent behavior without touching code, pushing a commit, or triggering CI.
41
+
42
+ Key benefits:
43
+
44
+ | Benefit | Explanation |
45
+ |---------|-------------|
46
+ | **No deploy to tune** | Product/ML team edits prompts via a backoffice or SQL — the agent picks up the change on next load |
47
+ | **Versioning and rollback** | Every version is kept as an immutable row; rollback = flip `is_active` |
48
+ | **A/B testing** | Two rows for the same `agent_key`, activate one; compare metrics; flip back if needed |
49
+ | **Audit trail** | `created_at` per version gives a full timeline of what prompt was active when |
50
+
51
+ The agent receives its `instructions:` value from the repository at construction time — not from a string literal in code.
52
+
53
+ ---
54
+
55
+ ## O pattern
56
+
57
+ ```
58
+ ┌─────────────────┐ ┌──────────────────┐ ┌──────────────────────┐
59
+ │ Agent Handler │──────▶│ PromptRepository │──────▶│ agent_prompts table │
60
+ │ (constructs │ │ (IMemoryCache │ │ (PostgreSQL) │
61
+ │ AIAgent) │◀──────│ TTL: 5 min) │◀──────│ │
62
+ └─────────────────┘ └──────────────────┘ └──────────────────────┘
63
+ ```
64
+
65
+ 1. The handler (or DI factory) calls `IPromptRepository.GetActivePromptAsync(agentKey, ct)`.
66
+ 2. `PromptRepository` checks `IMemoryCache`. On a hit, returns the cached string. On a miss, queries the DB for the single row where `agent_key = @key AND is_active = true`, caches it with a short TTL (e.g. 5 minutes), and returns it.
67
+ 3. The handler constructs the agent: `chatClient.AsAIAgent(instructions: prompt, name: "MyAgent")`.
68
+ 4. **Agents are cheap to construct** — recreate on every request (or per scoped lifetime). Do NOT attempt to mutate a running agent's instructions.
69
+
70
+ ---
71
+
72
+ ## Schema da tabela
73
+
74
+ The `agent_prompts` table stores one row per prompt version. See the full migration in:
75
+
76
+ ```
77
+ templates/code/sql/ai-agents/agent-prompts-migration.sql.template
78
+ ```
79
+
80
+ Column summary:
81
+
82
+ | Column | Type | Notes |
83
+ |--------|------|-------|
84
+ | `id` | `uuid` | PK, `gen_random_uuid()` |
85
+ | `agent_key` | `text NOT NULL` | Logical identifier, e.g. `"proposal-agent"` |
86
+ | `version` | `int NOT NULL` | Monotonically increasing per `agent_key` |
87
+ | `content` | `text NOT NULL` | The full system-prompt text |
88
+ | `is_active` | `boolean NOT NULL DEFAULT false` | Only one row active per `agent_key` |
89
+ | `created_at` | `timestamptz NOT NULL DEFAULT now()` | Immutable audit timestamp |
90
+
91
+ ### Unique-partial-index trick
92
+
93
+ ```sql
94
+ CREATE UNIQUE INDEX ix_agent_prompts_active
95
+ ON {{SCHEMA}}.agent_prompts (agent_key)
96
+ WHERE is_active;
97
+ ```
98
+
99
+ This index enforces at the database level that only one row per `agent_key` can have `is_active = true`. An attempt to activate a second row for the same key will fail with a unique-constraint violation — no application logic needed to guard this invariant.
100
+
101
+ ---
102
+
103
+ ## O repository
104
+
105
+ Reference implementation:
106
+
107
+ ```
108
+ templates/code/dotnet/ai-agents/PromptRepository.cs.template
109
+ ```
110
+
111
+ Interface contract:
112
+
113
+ ```csharp
114
+ public interface IPromptRepository
115
+ {
116
+ /// <summary>
117
+ /// Returns the content of the currently active prompt for the given agent key.
118
+ /// Throws <see cref="InvalidOperationException"/> if no active prompt exists.
119
+ /// </summary>
120
+ Task<string> GetActivePromptAsync(string agentKey, CancellationToken ct = default);
121
+ }
122
+ ```
123
+
124
+ The concrete `PromptRepository`:
125
+
126
+ - Receives `DbContext` and `IMemoryCache` via primary constructor.
127
+ - Uses a cache key of `$"agent_prompt:{agentKey}"` with a sliding expiration of 5 minutes.
128
+ - On a cache miss, queries `AgentPrompts.Where(p => p.AgentKey == agentKey && p.IsActive)` via EF Core.
129
+ - Throws `InvalidOperationException($"No active prompt found for agent '{agentKey}'.")` when no row matches — fail fast so misconfiguration is immediately visible.
130
+ - Does NOT catch DB exceptions — let the caller decide how to handle transient failures.
131
+
132
+ DI registration (in `Program.cs` or a bootstrap extension):
133
+
134
+ ```csharp
135
+ builder.Services.AddMemoryCache();
136
+ builder.Services.AddScoped<IPromptRepository, PromptRepository>();
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Integração com o agente
142
+
143
+ ### Scoped construction (recommended)
144
+
145
+ ```csharp
146
+ public sealed class ProposalAgentHandler(
147
+ IPromptRepository promptRepo,
148
+ ModelRegistry modelRegistry)
149
+ {
150
+ public async Task<string> RunAsync(string userMessage, CancellationToken ct)
151
+ {
152
+ var prompt = await promptRepo.GetActivePromptAsync("proposal-agent", ct);
153
+
154
+ var agent = modelRegistry
155
+ .GetChatClient("text-default")
156
+ .AsAIAgent(instructions: prompt, name: "ProposalAgent");
157
+
158
+ AgentSession session = await agent.CreateSessionAsync(ct);
159
+ var response = await agent.RunAsync(userMessage, session, cancellationToken: ct);
160
+ return response.Text;
161
+ }
162
+ }
163
+ ```
164
+
165
+ ### Hot-reload semantics
166
+
167
+ When a new prompt is saved with `is_active = true`:
168
+
169
+ 1. The old active row is set to `is_active = false` (see §Versionamento).
170
+ 2. The cache entry expires within the configured TTL (5 min by default).
171
+ 3. The next agent construction after expiry picks up the new prompt automatically.
172
+
173
+ **Do NOT mutate a running agent.** Agents are lightweight value-like objects in MAF — recreate on each scoped lifetime instead of attempting to patch `instructions` on a live instance.
174
+
175
+ > **Conferência de 2026-09-08, reconferida em 2026-09-09.** A extensão usada por este padrão —
176
+ > `AsAIAgent(this IChatClient chatClient, string? instructions, string? name, string? description,
177
+ > IList<AITool>? tools, …)` — está intacta, assim como a sobrecarga que recebe
178
+ > `ChatClientAgentOptions`. Ela é `Microsoft.Extensions.AI.ChatClientExtensions`, e a assembly é
179
+ > **`Microsoft.Agents.AI` 1.20.0** — namespace e assembly divergem aqui, e é o **segundo** nome que
180
+ > diz onde remedir. **E o nome do tipo, sozinho, é ambíguo:** existe um
181
+ > `Microsoft.Extensions.AI.ChatClientExtensions` em **duas** assemblies — esta e
182
+ > `Microsoft.Extensions.AI.Abstractions` —, e o `dump` do probe resolve **uma** delas e nunca mostra
183
+ > a colisão. `AsAIAgent` está na de `Microsoft.Agents.AI`; citar o tipo sem a assembly manda o
184
+ > próximo re-verificador ao arquivo errado com o nome certo. Medido: sete parâmetros, nesta ordem. **O argumento genérico `AITool` NÃO é
185
+ > coberto por este carimbo:** o `dump` do `api-probe` imprime `ParameterType.Name`, que apaga
186
+ > argumentos genéricos (ele mede ``IList`1 tools``); a aridade e a ordem são medidas, o `AITool` é
187
+ > documental. **A mesma ressalva vale para o `ChatMessage`** de `IEnumerable<ChatMessage>` no
188
+ > parágrafo seguinte: o dump emite `` IEnumerable`1 ``, e os **16** call sites de `AIAgent.RunAsync`
189
+ > no ai-kit passam **todos** uma `string` — nada nesta árvore mede aquele token.
190
+ >
191
+ > E há um **segundo** limite deste aparelho, que esta ressalva não cobre e por isso está dito aqui:
192
+ > o dump **não mede opcionalidade de parâmetro**. A afirmação *"`AgentSession` não é obrigatória"*,
193
+ > abaixo, não se apoia no dump — apoia-se no ai-kit **compilando e rodando** a chamada sem sessão,
194
+ > que é outro instrumento.
195
+ >
196
+ > **Correção de 2026-09-09, medida por reflexão sobre `Microsoft.Agents.AI.Abstractions` 1.20.0** —
197
+ > que é onde `AIAgent` vive, e **não** `Microsoft.Agents.AI`, como esta linha dizia até a
198
+ > reconferência. O exemplo acima
199
+ > chamava `agent.RunAsync(userMessage, ct)`, que **não compila**. A razão, medida: das oito
200
+ > sobrecargas de `AIAgent.RunAsync`, **nenhuma aceita um `CancellationToken` como segundo parâmetro
201
+ > posicional** — a posição 2 é sempre `AgentSession`, `JsonSerializerOptions` ou `AgentRunOptions`.
202
+ > Nas **seis** que recebem a mensagem primeiro — três tipos de mensagem (`string`, `ChatMessage` e
203
+ > `IEnumerable<ChatMessage>`, este último **documental**, pela ressalva acima), cada um com e sem
204
+ > `JsonSerializerOptions` — a posição 2 é a `AgentSession`, logo o token só chega **nomeado**.
205
+ >
206
+ > **`AgentSession` NÃO é obrigatória** — e este é o ponto em que uma leitura apressada erra: duas das
207
+ > oito sobrecargas a recebem na **primeira** posição, e o próprio ai-kit compila e roda
208
+ > `await agent.RunAsync("sem sessao", cancellationToken: Ct)`
209
+ > (`tests/Morph.AiKit.Tests/ConversationStoreTests.cs:338`, teste
210
+ > `ARoundWithoutAnExplicitSessionStillHasAConversationOfItsOwn`), caso em que o agente cria uma
211
+ > sessão **efêmera** para a volta. Passar a sessão explicitamente é escolha de **desenho**, não
212
+ > exigência do SDK: sem ela a conversa nasce com id próprio a cada turno, que é o oposto do que este
213
+ > standard quer. As duas afirmações comportamentais desta linha são as que o teste **assere**
214
+ > (`Assert.Single(store.ConversationIds)` na linha 343, para a primeira volta; e
215
+ > `Assert.Equal(2, store.ConversationIds.Count)` na **351**, que é quem prova que a volta seguinte
216
+ > **não herda** a conversa) — não leitura de assinatura. O `Assert.DoesNotContain` da 352 assere
217
+ > outra coisa: que a mensagem anterior não foi **reenviada** ao provedor.
218
+ >
219
+ > O carimbo anterior *"nada a corrigir de API neste standard"* estava errado quanto a esta linha;
220
+ > ele é anterior a esta feature, e está retificado aqui em vez de repetido.
221
+ >
222
+ > **Estado do rename:** feito. Este arquivo **é** o antigo `prompts-in-db.md`, renomeado para
223
+ > `prompt-sources.md` e generalizado — "prompt vem de uma fonte, e banco é uma delas". O id do
224
+ > registry mudou junto, de `ai-agents-prompts-in-db` para `ai-agents-prompt-sources`, e os seis
225
+ > referenciadores do framework foram atualizados no mesmo movimento.
226
+
227
+ ### Keyed singleton (long-lived, explicit refresh)
228
+
229
+ For agents registered as keyed singletons (e.g. via `builder.AddAIAgent`), hot-reload requires explicit invalidation. In that pattern, call `IMemoryCache.Remove($"agent_prompt:{key}")` from an admin endpoint or background job after saving a new version, then recreate the agent in the factory. This is more complex; prefer scoped construction unless the agent carries expensive state (e.g. an embedded tool pipeline that is costly to rebuild).
230
+
231
+ ---
232
+
233
+ ## Versionamento
234
+
235
+ **Rule: never UPDATE the `content` column.** The content of a prompt row is immutable once written.
236
+
237
+ To release a new prompt version:
238
+
239
+ ```sql
240
+ -- 1. Deactivate the current active version
241
+ UPDATE {{SCHEMA}}.agent_prompts
242
+ SET is_active = false
243
+ WHERE agent_key = 'proposal-agent' AND is_active = true;
244
+
245
+ -- 2. Insert the new version (version = previous max + 1)
246
+ INSERT INTO {{SCHEMA}}.agent_prompts (agent_key, version, content, is_active)
247
+ VALUES (
248
+ 'proposal-agent',
249
+ (SELECT COALESCE(MAX(version), 0) + 1 FROM {{SCHEMA}}.agent_prompts WHERE agent_key = 'proposal-agent'),
250
+ 'Your new system prompt text here...',
251
+ true
252
+ );
253
+ ```
254
+
255
+ This keeps a complete, auditable history. Rollback = deactivate the current row and reactivate any previous row.
256
+
257
+ ---
258
+
259
+ ## Fonte 2: YAML por tenant
260
+
261
+ Quando o mesmo produto atende clientes com política própria, o prompt deixa de ser um registro
262
+ editável e passa a ser **o resultado de uma composição**. A cadeia, em quatro etapas, cada uma com
263
+ um dono claro:
264
+
265
+ ```
266
+ moldura de produto (repo, versionada) ← a regra que vale para todos, com buracos nomeados
267
+ +
268
+ YAML por tenant (repo, versionado) ← só a política daquele cliente preenche os buracos
269
+ ↓
270
+ compositor determinístico ← puro, sem I/O; entrada igual, saída igual
271
+ ↓
272
+ spec publicado (coluna jsonb) ← o blob renderizado, lido em runtime
273
+ ```
274
+
275
+ **As quatro regras que fazem isso funcionar:**
276
+
277
+ 1. **O que se versiona é a fonte.** A moldura e o YAML vivem no repositório, com histórico, revisão e
278
+ `blame`. O blob publicado é **derivado**; editá-lo à mão é editar um artefato de build.
279
+ 2. **A moldura tem buracos nomeados, não texto opcional.** Um buraco que o tenant não preenche é um
280
+ erro de publicação, não um parágrafo que some silenciosamente.
281
+ 3. **O compositor é determinístico e puro.** Mesma moldura + mesmo YAML = mesmo byte. Sem isso, a
282
+ seção `## Reconciliação` abaixo é impossível.
283
+ 4. **A publicação é um passo explícito**, com quem publicou e quando. Publicar não é salvar.
284
+
285
+ **Quando NÃO usar este mecanismo:** produto single-tenant. A cadeia de quatro etapas paga por si
286
+ quando existe variação real por cliente; num produto só, ela é uma indireção que transforma "editar o
287
+ prompt" em "editar, compor e publicar".
288
+
289
+ **Onde este mecanismo é fraco, e é honesto dizer:** editar prompt passa a exigir um ciclo de repo
290
+ (commit, revisão, publicação). O ganho de "tunar sem deploy" da fonte 1 **desaparece** — e trocá-lo
291
+ por consistência entre tenants é uma decisão de produto, não uma melhoria automática.
292
+
293
+ ---
294
+
295
+ ## Reconciliação
296
+
297
+ **Versionar e publicar não prova que a versão ativa foi a que rodou.**
298
+
299
+ É a dor que este standard não cobria e que custou caro: num produto do acervo, versões v7 e v8 de
300
+ prompt foram **ignoradas em 6 de 9 execuções** — o registro dizia que existiam, a execução usava
301
+ outra coisa, e ninguém tinha como saber sem reproduzir manualmente.
302
+
303
+ O buraco é estrutural, e vale para as duas fontes: entre "qual versão está ativa" e "qual texto foi
304
+ enviado" há um cache, um deploy, uma cópia de configuração ou uma etapa de publicação — e cada um
305
+ deles pode estar servindo algo antigo.
306
+
307
+ **A exigência, em duas linhas:**
308
+
309
+ - **O registro de execução carrega a versão efetiva.** Não a versão ativa no momento da consulta: a
310
+ que foi usada naquele turno. Um campo, gravado junto com o resultado.
311
+ - **No caminho composto, ele carrega também o hash do blob renderizado.** Versão sozinha não basta
312
+ quando o blob é derivado de duas fontes: o hash é o que amarra o texto enviado à composição que o
313
+ gerou.
314
+
315
+ **As duas técnicas de prova que o campo já usa**, e nenhuma delas exige infraestrutura nova:
316
+
317
+ | Técnica | O que prova | Onde vive |
318
+ |---|---|---|
319
+ | **Equivalência por byte-identidade** entre a composição da fonte e o blob publicado | Que a publicação não divergiu da fonte versionada | Teste, sobre o compositor puro |
320
+ | **Prompt como texto**: ler a fonte versionada e assertar presença de regra, vocabulário de chaves e **ausência de instrução revogada** | Que a regra que se acredita ter está lá, e que a que foi revogada saiu | Teste, sobre a migration ou o YAML |
321
+
322
+ A segunda asserção — **ausência** — é a que ninguém escreve e a que mais pega regressão: uma
323
+ instrução removida do prompt em produção continua viva no registro antigo, e o rollback a traz de
324
+ volta sem que ninguém note.
325
+
326
+ O detalhamento dessas duas técnicas está em `ai-agents-testing-ai`; a metade de produção da
327
+ rastreabilidade (hash em span, prompt cru só em harness) está em `ai-agents-observability-patterns`.
328
+
329
+ ---
330
+
331
+ ## Anti-patterns
332
+
333
+ | Anti-pattern | Por quê é problema | Solução |
334
+ |---|---|---|
335
+ | Hardcoded prompt string in production code | Every tune requires a build + deploy | Move to `agent_prompts` table |
336
+ | `UPDATE agent_prompts SET content = '...'` without a version | Destroys history; no rollback | INSERT new row with version+1, flip `is_active` |
337
+ | No cache — DB query on every agent invocation | High latency + DB load under traffic | Use `IMemoryCache` with a short TTL |
338
+ | `appsettings.json` prompts | Still requires a deploy to change; no versioning | Move to DB or environment-variable-agnostic store |
339
+ | Single global `IMemoryCache` key collisions | Two agents with similar keys overwrite each other | Always prefix: `$"agent_prompt:{agentKey}"` |
340
+ | Activating a new row without deactivating the old one | Two active rows for the same key — undefined behavior | The partial unique index prevents this at DB level; always deactivate first in app code |
341
+ | Uma linha de `agent_prompts` por tenant | A regra comum passa a existir em N cópias; a próxima edição corrige N-1 | Fonte 2: moldura + YAML por tenant, composto |
342
+ | Editar à mão o blob publicado | É artefato derivado; a próxima composição o sobrescreve | Edite a fonte e republique |
343
+ | Compositor com I/O ou relógio | Perde o determinismo, e a prova por byte-identidade deixa de ser possível | Compositor puro |
344
+ | Buraco de política que some em silêncio quando o tenant não o preenche | O prompt publicado fica com um pedaço a menos e ninguém sabe | Buraco não preenchido é erro de publicação |
345
+ | Registrar a execução sem a versão efetiva do prompt | "v7 e v8 ignoradas em 6 de 9 execuções", e sem como descobrir | Grave a versão usada naquele turno |
346
+ | No caminho composto, gravar só a versão e não o hash do blob | Versão não identifica um texto derivado de duas fontes | Grave versão **e** hash |
347
+ | Testar só a presença de uma regra no prompt | A instrução revogada continua viva e volta no rollback | Asserte também a **ausência** |
348
+
349
+ ---
350
+
351
+ ## Checklist (verifiable by morph-eval)
352
+
353
+ - [ ] `agent_prompts` table exists with all required columns (`id`, `agent_key`, `version`, `content`, `is_active`, `created_at`)
354
+ - [ ] Unique partial index `ix_agent_prompts_active` on `(agent_key) WHERE is_active` is present
355
+ - [ ] `IPromptRepository` registered in DI (`AddScoped`)
356
+ - [ ] `IMemoryCache` registered (`builder.Services.AddMemoryCache()`)
357
+ - [ ] No prompt string literals in production agent construction code
358
+ - [ ] `GetActivePromptAsync` throws `InvalidOperationException` (not returns null) when no active prompt found
359
+ - [ ] Versionamento pattern followed: INSERT + flip, never UPDATE content
360
+ - [ ] At least one seed row per `agent_key` in migrations or seed script
361
+ - [ ] A fonte escolhida (banco versionado × composição por tenant) está nomeada no `decisions.md`,
362
+ com a pergunta "o texto varia por cliente?" respondida
363
+ - [ ] No caminho composto: a moldura e o YAML estão versionados no repo, e o blob publicado **não** é
364
+ editado à mão
365
+ - [ ] O compositor é puro e determinístico (mesma entrada, mesmo byte)
366
+ - [ ] Buraco de política não preenchido falha a publicação, em vez de sumir do texto
367
+ - [ ] O registro de execução carrega a **versão efetiva** do prompt daquele turno
368
+ - [ ] No caminho composto, carrega também o **hash do blob renderizado**
369
+ - [ ] Existe teste de equivalência por byte-identidade entre a fonte e o blob publicado
370
+ - [ ] Existe teste de prompt como texto, incluindo asserção de **ausência** de instrução revogada
371
+
372
+ ---
373
+
374
+ ## References
375
+
376
+ - `ai-agents-setup` — how to construct an `AIAgent` via `AsAIAgent(instructions:)`
377
+ - `ai-agents-providers-model-registry` — how to obtain the `IChatClient` from the Model Registry
378
+ - `ai-agents-agent-spec` — o campo `Instructions` cuja origem este standard governa
379
+ - `ai-agents-testing-ai` — as duas técnicas de prova da seção de reconciliação, em detalhe
380
+ - `ai-agents-observability-patterns` — a metade de produção da rastreabilidade (hash em span)
381
+ - `backend-database-postgresql-database` — naming conventions, timestamptz, uuid PKs, partial indexes
382
+
383
+ > **Lacuna declarada.** O par compilável deste standard seria um loader de prompt com hash em
384
+ > `templates/dotnet/ai-kit/src/Morph.AiKit/`. **Ele ainda não existe** no ai-kit. Por isso o
385
+ > cabeçalho declara verificação documental e não carimba `provado por`.
386
+
387
+ ---
388
+
389
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/prompt-sources.md v2.0 (2026-09-08)*