@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.
- package/CLAUDE.md +22 -2
- package/README.md +2 -2
- package/bin/morph-spec.js +43 -5
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -2
- package/framework/MORPH.md +20 -22
- package/framework/agents.json +198 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +238 -10
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/README.md +11 -1
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +5 -8
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +79 -0
- package/framework/hooks/claude-code/core/chain.js +27 -7
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/core/worktree-context.js +83 -14
- package/framework/hooks/claude-code/core/worktree-create.js +206 -0
- package/framework/hooks/claude-code/core/worktree-env-inject.js +87 -0
- package/framework/hooks/claude-code/core/worktree-remove.js +81 -0
- package/framework/hooks/shared/command-tokens.js +23 -0
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/git-paths.js +25 -0
- package/framework/hooks/shared/hook-response.js +17 -0
- package/framework/hooks/shared/morph-cli.js +134 -0
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/rules/infrastructure-standards.md +3 -1
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +28 -7
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +20 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +38 -4
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1504 -830
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +256 -160
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +217 -193
- package/framework/standards/ai-agents/context-providers.md +261 -188
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +351 -250
- package/framework/standards/ai-agents/mcp-tools.md +250 -197
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +241 -207
- package/framework/standards/ai-agents/modalities-image-gen.md +422 -173
- package/framework/standards/ai-agents/modalities-speech-to-text.md +177 -171
- package/framework/standards/ai-agents/modalities-text-to-speech.md +189 -181
- package/framework/standards/ai-agents/modalities-vision.md +205 -201
- package/framework/standards/ai-agents/multi-agent-patterns.md +211 -155
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +260 -231
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +293 -158
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +143 -135
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +280 -249
- package/framework/standards/ai-agents/sweet-spot.md +129 -109
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +90 -0
- package/framework/templates/REGISTRY.json +14 -1
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -95
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -58
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +24 -14
- package/package.json +3 -3
- package/src/commands/e2e/e2e.js +91 -40
- package/src/commands/project/doctor.js +143 -6
- package/src/commands/project/env.js +95 -0
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +274 -53
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/delete.js +8 -0
- package/src/commands/state/finish.js +360 -53
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/state-manager.js +9 -1
- package/src/dashboard/app/pages/HookInspector.jsx +430 -418
- package/src/dashboard/scanner/hooks-scanner.js +5 -1
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/e2e/compose-env.js +55 -14
- package/src/lib/e2e/stack-teardown.js +115 -0
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/architecture-scanner.js +2 -1
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/graph/graphifyignore.js +2 -1
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +431 -37
- package/src/lib/validators/verify-runner.js +590 -28
- package/src/lib/worktree/dirs.js +32 -0
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +69 -11
- package/src/lib/worktree/worktree-audit.js +70 -3
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/file-copier.js +28 -16
- package/src/utils/hooks-installer.js +75 -29
- package/src/utils/skills-installer.js +194 -187
- package/src/utils/version-checker.js +68 -3
- 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)*
|