@polymorphism-tech/morph-spec 8.34.2 → 8.35.1

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 (328) hide show
  1. package/CLAUDE.md +22 -5
  2. package/README.md +2 -2
  3. package/bin/morph-spec.js +18 -4
  4. package/bin/validate.js +379 -379
  5. package/claude-plugin.json +1 -1
  6. package/docs/CHEATSHEET.md +5 -4
  7. package/docs/QUICKSTART.md +1 -1
  8. package/framework/MORPH.md +16 -17
  9. package/framework/agents.json +197 -44
  10. package/framework/ai-pin.json +22 -0
  11. package/framework/commands/morph-media.md +45 -0
  12. package/framework/docs/CLI.md +236 -11
  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/claude-code/core/protect-spec-files.js +41 -4
  25. package/framework/hooks/claude-code/core/worktree-env-inject.js +43 -0
  26. package/framework/hooks/claude-code/statusline.sh +11 -11
  27. package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
  28. package/framework/hooks/git/pre-commit/agents.sh +25 -25
  29. package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
  30. package/framework/hooks/git/pre-commit/specs.sh +50 -50
  31. package/framework/hooks/git/pre-push/run-tests.sh +44 -44
  32. package/framework/hooks/shared/e2e-check.js +135 -22
  33. package/framework/hooks/shared/payload-utils.js +15 -2
  34. package/framework/hooks/shared/stdin-reader.js +26 -26
  35. package/framework/rules/ai-agents-standards.md +14 -2
  36. package/framework/schemas/ai-pin.schema.json +97 -0
  37. package/framework/schemas/config.schema.json +107 -0
  38. package/framework/schemas/feature.schema.json +23 -4
  39. package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
  40. package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
  41. package/framework/skills/core/morph-apply/SKILL.md +26 -5
  42. package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
  43. package/framework/skills/core/morph-eval/SKILL.md +1 -1
  44. package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
  45. package/framework/skills/core/morph-implement/SKILL.md +18 -2
  46. package/framework/skills/core/morph-media/SKILL.md +215 -0
  47. package/framework/skills/core/morph-plan/SKILL.md +33 -2
  48. package/framework/skills/core/morph-proposal/SKILL.md +1 -1
  49. package/framework/skills/core/morph-review/SKILL.md +166 -9
  50. package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
  51. package/framework/skills/core/morph-standards/SKILL.md +1 -1
  52. package/framework/skills/core/morph-uiux/SKILL.md +1 -1
  53. package/framework/standards/PROVENANCE.json +81 -3
  54. package/framework/standards/STANDARDS.json +1483 -839
  55. package/framework/standards/ai-agents/agent-archetypes.md +160 -0
  56. package/framework/standards/ai-agents/agent-session.md +164 -68
  57. package/framework/standards/ai-agents/agent-spec.md +196 -0
  58. package/framework/standards/ai-agents/batching.md +61 -37
  59. package/framework/standards/ai-agents/context-providers.md +97 -24
  60. package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
  61. package/framework/standards/ai-agents/cost-and-budget.md +256 -0
  62. package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
  63. package/framework/standards/ai-agents/evals-with-cache.md +289 -0
  64. package/framework/standards/ai-agents/guardrails.md +222 -0
  65. package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
  66. package/framework/standards/ai-agents/mcp-server.md +119 -18
  67. package/framework/standards/ai-agents/mcp-tools.md +85 -32
  68. package/framework/standards/ai-agents/media-pipeline.md +352 -0
  69. package/framework/standards/ai-agents/media-video.md +267 -0
  70. package/framework/standards/ai-agents/middleware-patterns.md +39 -5
  71. package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
  72. package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
  73. package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
  74. package/framework/standards/ai-agents/modalities-vision.md +6 -2
  75. package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
  76. package/framework/standards/ai-agents/observability-patterns.md +227 -17
  77. package/framework/standards/ai-agents/production.md +38 -9
  78. package/framework/standards/ai-agents/prompt-sources.md +389 -0
  79. package/framework/standards/ai-agents/providers/model-registry.md +174 -39
  80. package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
  81. package/framework/standards/ai-agents/service-tier-flex.md +27 -19
  82. package/framework/standards/ai-agents/setup.md +166 -50
  83. package/framework/standards/ai-agents/structured-output.md +34 -3
  84. package/framework/standards/ai-agents/sweet-spot.md +23 -3
  85. package/framework/standards/ai-agents/testing-ai.md +236 -0
  86. package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
  87. package/framework/standards/ai-agents/workflows.md +27 -7
  88. package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
  89. package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
  90. package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
  91. package/framework/templates/.idea/morph-templates.xml +92 -0
  92. package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
  93. package/framework/templates/IDE-SNIPPETS.md +262 -262
  94. package/framework/templates/REGISTRY.json +13 -0
  95. package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
  96. package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
  97. package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
  98. package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
  99. package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
  100. package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
  101. package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
  102. package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
  103. package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
  104. package/framework/templates/code/dotnet/database/migration.cs +83 -83
  105. package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
  106. package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
  107. package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
  108. package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
  109. package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
  110. package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
  111. package/framework/templates/code/typescript/contracts.ts +167 -167
  112. package/framework/templates/docs/clarifications.md +253 -253
  113. package/framework/templates/docs/schema-analysis.md +119 -119
  114. package/framework/templates/docs/ui-components.md +52 -52
  115. package/framework/templates/docs/ui-design-system.md +76 -76
  116. package/framework/templates/docs/ui-flows.md +167 -167
  117. package/framework/templates/docs/ui-mockups.md +98 -98
  118. package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
  119. package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
  120. package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
  121. package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
  122. package/framework/templates/dotnet/ai-kit/README.md +128 -0
  123. package/framework/templates/dotnet/ai-kit/global.json +9 -0
  124. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
  125. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
  126. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
  127. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
  128. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
  129. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
  130. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
  131. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
  132. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
  133. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
  134. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
  135. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
  136. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
  137. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
  138. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
  139. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
  140. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
  141. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
  142. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
  143. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
  144. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
  145. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
  146. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
  147. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
  148. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
  149. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
  150. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
  151. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
  152. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
  153. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
  154. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
  155. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
  156. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
  157. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
  158. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
  159. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
  160. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
  161. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
  162. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
  163. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
  164. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
  165. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
  166. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
  167. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
  168. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
  169. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
  170. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
  171. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
  172. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
  173. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
  174. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
  175. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
  176. package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
  177. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
  178. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
  179. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
  180. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
  181. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
  182. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
  183. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
  184. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
  185. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
  186. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
  187. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
  188. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
  189. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
  190. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
  191. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
  192. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
  193. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
  194. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
  195. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
  196. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
  197. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
  198. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
  199. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
  200. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
  201. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
  202. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
  203. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
  204. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
  205. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
  206. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
  207. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
  208. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
  209. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
  210. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
  211. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
  212. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
  213. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
  214. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
  215. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
  216. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
  217. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
  218. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
  219. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
  220. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
  221. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
  222. package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
  223. package/framework/templates/feature/decisions.md +122 -122
  224. package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
  225. package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
  226. package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
  227. package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
  228. package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
  229. package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
  230. package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
  231. package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
  232. package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
  233. package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
  234. package/framework/templates/infrastructure/github/README.md +56 -56
  235. package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
  236. package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
  237. package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
  238. package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
  239. package/framework/templates/integrations/asaas-client.cs +387 -387
  240. package/framework/templates/integrations/asaas-webhook.cs +351 -351
  241. package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
  242. package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
  243. package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
  244. package/framework/templates/project/validate.js +120 -120
  245. package/framework/templates/state.template.json +17 -17
  246. package/framework/templates/ui/design-system.css +226 -226
  247. package/package.json +3 -3
  248. package/src/commands/dashboard/dashboard.js +53 -53
  249. package/src/commands/e2e/e2e.js +15 -2
  250. package/src/commands/index.js +13 -13
  251. package/src/commands/project/checkpoint.js +209 -209
  252. package/src/commands/project/doctor.js +148 -6
  253. package/src/commands/project/update.js +469 -153
  254. package/src/commands/project/worktree.js +76 -4
  255. package/src/commands/state/advance.js +24 -2
  256. package/src/commands/state/approve.js +33 -1
  257. package/src/commands/state/create.js +72 -11
  258. package/src/commands/state/finish.js +379 -24
  259. package/src/commands/state/gate-check.js +190 -6
  260. package/src/commands/state/score.js +36 -1
  261. package/src/commands/validation/verify.js +134 -25
  262. package/src/core/state/phase-state-machine.js +197 -197
  263. package/src/core/state/state-manager.js +9 -1
  264. package/src/core/templates/template-renderer.js +477 -477
  265. package/src/dashboard/app/components/DetailPanel.jsx +11 -11
  266. package/src/dashboard/app/components/FlowGraph.jsx +17 -17
  267. package/src/dashboard/app/components/SearchFilter.jsx +22 -22
  268. package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
  269. package/src/dashboard/app/main.jsx +13 -13
  270. package/src/dashboard/app/pages/AgentMap.jsx +370 -370
  271. package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
  272. package/src/dashboard/app/pages/RulesMap.jsx +273 -273
  273. package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
  274. package/src/dashboard/app/pages/Standards.jsx +332 -332
  275. package/src/dashboard/app/styles/index.css +47 -47
  276. package/src/dashboard/index.html +12 -12
  277. package/src/dashboard/package.json +24 -24
  278. package/src/dashboard/scanner/phases-scanner.js +15 -15
  279. package/src/dashboard/scanner/rubrics-scanner.js +12 -12
  280. package/src/dashboard/scanner/rules-scanner.js +37 -37
  281. package/src/dashboard/scanner/skills-scanner.js +43 -43
  282. package/src/dashboard/scanner/squads-scanner.js +13 -13
  283. package/src/dashboard/vite-plugin-morph-data.js +17 -17
  284. package/src/dashboard/vite.config.js +24 -24
  285. package/src/lib/ai/ai-checks.js +182 -0
  286. package/src/lib/ai/ai-project-scan.js +347 -0
  287. package/src/lib/ai/code-audit.js +181 -0
  288. package/src/lib/ai/comment-stripper.js +184 -0
  289. package/src/lib/ai/pin-drift.js +138 -0
  290. package/src/lib/ai/registry-audit.js +285 -0
  291. package/src/lib/detectors/design-system-detector.js +188 -188
  292. package/src/lib/detectors/index.js +217 -217
  293. package/src/lib/e2e/compose-env.js +50 -4
  294. package/src/lib/evaluator-report.js +269 -15
  295. package/src/lib/generators/design-system-generator.js +508 -508
  296. package/src/lib/generators/settings-generator.js +209 -209
  297. package/src/lib/git/default-branch.js +92 -4
  298. package/src/lib/git/pr-files.js +56 -0
  299. package/src/lib/hooks/hook-executor.js +167 -167
  300. package/src/lib/installers/effects.js +193 -0
  301. package/src/lib/installers/mcp-installer.js +719 -719
  302. package/src/lib/installers/templates-reconcile.js +3 -3
  303. package/src/lib/risk-detector.js +284 -0
  304. package/src/lib/standards/verified-against.js +276 -0
  305. package/src/lib/tasks/eval-node.js +485 -0
  306. package/src/lib/tasks/linux-runner.js +297 -0
  307. package/src/lib/tasks/task-classifier.js +30 -30
  308. package/src/lib/tasks/task-parser.js +209 -209
  309. package/src/lib/tasks/test-nodes.js +486 -0
  310. package/src/lib/tasks/test-runner.js +410 -33
  311. package/src/lib/validators/checkpoint-persistence.js +39 -39
  312. package/src/lib/validators/css/css-validator.js +340 -340
  313. package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
  314. package/src/lib/validators/shared/issue-counter.js +14 -14
  315. package/src/lib/validators/verify-runner.js +583 -28
  316. package/src/lib/worktree/docker-audit.js +287 -0
  317. package/src/lib/worktree/env-allocator.js +24 -0
  318. package/src/lib/worktree/worktree-audit.js +30 -1
  319. package/src/scripts/setup-infra.js +24 -11
  320. package/src/utils/agent-id-mapper.js +11 -11
  321. package/src/utils/agents-installer.js +18 -8
  322. package/src/utils/claude-md-injector.js +10 -5
  323. package/src/utils/claude-settings-manager.js +300 -300
  324. package/src/utils/file-copier.js +11 -4
  325. package/src/utils/hooks-installer.js +29 -26
  326. package/src/utils/skills-installer.js +36 -29
  327. package/src/utils/version-checker.js +68 -3
  328. package/framework/standards/ai-agents/prompts-in-db.md +0 -214
@@ -0,0 +1,289 @@
1
+ # Evals com cache — regressão de prompt e de modelo sem chave no CI
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 1 (on-keyword)
5
+ > **Keywords:** eval, evals, response cache, cache de respostas, dataset de eval, regressão de prompt, troca de modelo, model swap, prompt regression, EvalFixture, OfflineChatClient, DiskBasedResponseCacheProvider, nó evals, --refresh-cache
6
+ > **Read by Claude in:** implement (quando a feature muda prompt, troca modelo, ou o projeto vai declarar o nó `evals` no `verify`)
7
+
8
+ **Verified against:** Microsoft.Extensions.AI.Evaluation 10.9.0 + `.Quality` + `.Reporting` (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs`, que compila e roda no CI contra exatamente essas versões. A superfície de `DiskBasedReportingConfiguration`, `DiskBasedResponseCacheProvider`, `ReportingConfiguration` e dos avaliadores de `.Quality` foi **medida por reflexão sobre as assemblies `Microsoft.Extensions.AI.Evaluation*` 10.9.0** em 2026-09-08. Last-verified: 2026-09-08.
9
+
10
+ ---
11
+
12
+ ## Por que eval não é teste unitário
13
+
14
+ Um teste unitário responde "esta função ainda faz o que fazia?". Um eval responde
15
+ "este **produto** ainda responde o que respondia?" — e a diferença não é de grau.
16
+
17
+ Trocar o prompt de sistema de um agente, ou trocar `gpt-4.1` por `gpt-5`, deixa **build
18
+ verde e suíte unitária verde**. Nenhuma das duas coisas lê o comportamento do modelo; as
19
+ duas leem o código em volta dele. O prompt e o modelo são a única parte do produto cujo
20
+ comportamento não é coberto por nada do que já existe no gate.
21
+
22
+ O precedente é de campo, e é caro: um repositório real manteve o script de eval **fora do
23
+ CI** por decisão escrita (`ci.yml`, decisão registrada D-F5-2) porque *"variância de LLM
24
+ real não pertence a pipeline determinístico"*. A decisão estava certa **para aquele
25
+ desenho**. Um eval que chama o modelo a cada PR é lento, caro e flaky — e um eval flaky é
26
+ um eval desligado, que verifica exatamente nada.
27
+
28
+ Este standard descreve o desenho em que a decisão muda: **o eval lê de uma cache de
29
+ respostas commitada**, e por isso é rápido, gratuito e determinístico.
30
+
31
+ ## O que se ganha, em uma frase
32
+
33
+ Com a cache quente, a suíte de eval roda **sem credencial e sem rede**: a biblioteca
34
+ embrulha o cliente interno num cliente de cache, e no *hit* o cliente interno nunca é
35
+ invocado. Trocando o cliente interno por um que **recusa** (`OfflineChatClient`), o único
36
+ caminho para a rede fica fechado — ou tudo resolve pela cache, ou a suíte fica vermelha
37
+ nomeando o cenário que faltou.
38
+
39
+ Isso é o que permite o nó de eval existir no CI de um repositório que **não tem uma única
40
+ chave de LLM nos seus workflows**. Um mecanismo que exigisse uma chave seria adotado por
41
+ ninguém.
42
+
43
+ ## Dataset por agente e por tenant
44
+
45
+ ```
46
+ eval-datasets/
47
+ ├── triagem/acme/a-saudacao.json
48
+ ├── triagem/acme/b-escalonamento.json
49
+ ├── triagem/globex/a-saudacao.json
50
+ └── orcamento/acme/a-resposta-estruturada.json
51
+ ```
52
+
53
+ Os dois primeiros segmentos existem porque a pergunta útil quase nunca é "o produto
54
+ regrediu?", e sim **"o agente X regrediu para o tenant Y?"**. Um dataset plano não responde
55
+ isso sem convenção de nome, e convenção de nome é a forma mais frágil de estrutura.
56
+
57
+ O caminho **é** a identidade do cenário: ele vira a chave do cenário na cache. Dois tenants
58
+ têm cenários com o mesmo nome de propósito (`saudacao`, `handoff`), e uma chave que os
59
+ confundisse devolveria a resposta de um tenant para o outro.
60
+
61
+ > **Dado real de tenant é proibido no dataset.** A cache guarda **mensagens inteiras** e é
62
+ > commitada: um segredo que entre no dataset entra no git, para sempre. O fixture só lê
63
+ > cenário do diretório versionado — nunca de banco, nunca de variável de ambiente.
64
+
65
+ ## O que entra na chave de cache
66
+
67
+ Medido no pacote 10.9.0, e o achado importa:
68
+
69
+ | Entra na chave | Não entra |
70
+ |---|---|
71
+ | As mensagens e as `ChatOptions` | **O endpoint (`ProviderUri`)** |
72
+ | `ProviderName` do `ChatClientMetadata` | |
73
+ | `DefaultModelId` do `ChatClientMetadata` | |
74
+ | A versão interna de formato de cache da biblioteca | |
75
+
76
+ **A documentação afirma que o endpoint entra. Ele não entra.** A divergência entre o
77
+ comentário XML da API e o comportamento medido é o que torna possível um cliente offline
78
+ sem endpoint algum: ele não tem para onde ligar, e não precisa ter.
79
+
80
+ A consequência prática: o cliente que **lê** a cache tem de replicar `ProviderName` e
81
+ `DefaultModelId` do run que a **gravou**. Se divergirem, a chave computada na leitura é
82
+ outra e **todo** cenário dá miss — uma cache quente indistinguível de uma cache vazia.
83
+
84
+ ## TTL explícito, sempre — e por quê
85
+
86
+ `Defaults.DefaultTimeToLiveForCacheEntries` são **14 dias**, e a expiração é **destrutiva na
87
+ leitura**: ao encontrar uma entrada vencida, o provedor **apaga o diretório da chave** antes
88
+ de devolver `null`.
89
+
90
+ Uma cache commitada com o default **se apaga sozinha ao ser lida**, duas semanas depois de
91
+ gravada. O sintoma chega como CI verde por duas semanas e então miss em massa num dia em
92
+ que ninguém mexeu em nada — com a árvore apagada no working tree, sujando o `git status`.
93
+
94
+ Um TTL implícito aqui não é preguiça de configuração; é uma bomba-relógio. Declare-o, e
95
+ declare-o **longo** (o kit recusa qualquer valor abaixo de um ano, com a razão na mensagem).
96
+
97
+ ## O carimbo, e o que ele responde de graça
98
+
99
+ A função de hash que produz a chave **não promete ser estável entre versões da biblioteca**
100
+ — a própria biblioteca carrega um número de versão de cache interno, justamente para
101
+ invalidar tudo quando o formato muda. Ou seja: **subir o pin pode invalidar a árvore
102
+ inteira**.
103
+
104
+ Sem carimbo, isso chega como N misses inexplicáveis. Com carimbo, chega como uma frase:
105
+
106
+ ```
107
+ eval-cache-stale-pin: Microsoft.Extensions.AI.Evaluation: gravada 10.9.0 vs pin 10.11.0
108
+ ```
109
+
110
+ `.morph-eval-cache.json` fica na raiz da árvore e guarda o `pinnedAt` e as versões da
111
+ família `Microsoft.Extensions.AI` sob as quais a árvore foi gravada. **Carimbo ausente tem
112
+ motivo próprio**, distinto de carimbo divergente: as duas situações pedem ações diferentes
113
+ — a primeira é "regrave", a segunda é "regrave **porque o pacote mudou**".
114
+
115
+ ## O que se commita, e o que não
116
+
117
+ ```
118
+ <cachePath>/ COMMITADO
119
+ ├── .morph-eval-cache.json o carimbo
120
+ └── cache/<cenário>/<iteração>/<chave-curta>/{entry.json, contents.data}
121
+
122
+ <cachePath>/../eval-results/ NUNCA COMMITADO
123
+ └── results/<execução>/<cenário>/<iteração>.json
124
+ ```
125
+
126
+ Os resultados de execução carregam **latência medida por cronômetro, uso de tokens e o flag
127
+ de cache hit**: são não-determinísticos por natureza, e commitá-los é diff garantido em toda
128
+ rodada, sem sinal nenhum.
129
+
130
+ Eles ficam fora da árvore commitada **por construção** — outra raiz de armazenamento — e
131
+ não por um `.gitignore` dentro de um diretório commitado. Uma regra de ignore ali é uma
132
+ regra que a próxima pessoa remove sem entender.
133
+
134
+ ### Por que a chave é CURTA, e por que isso não é detalhe
135
+
136
+ O provedor de cache do kit (`ShortPathResponseCacheProvider`) nomeia o diretório da entrada
137
+ com os **16 primeiros caracteres** do hash, e não com o hash inteiro. A chave completa é
138
+ verificada dentro do `entry.json`: se dois cenários colidirem no prefixo, a leitura vira
139
+ **miss** — nunca a resposta errada.
140
+
141
+ O motivo é medido, e custou uma rodada inteira. Com o hash inteiro (96 caracteres em hex), o
142
+ pior caminho relativo desta árvore dava **225**. Somado à raiz do repositório nesta máquina
143
+ (73), o absoluto dava **298** — contra os 260 do `MAX_PATH` do Windows. O git **não recusou
144
+ barulhento**: avisou `Filename too long` no stderr, commitou **1 de 9 arquivos**, e saiu com
145
+ código 0.
146
+
147
+ E o dano real não foi o arquivo faltando. Foi que o canário de CI — um `git diff --exit-code`
148
+ sobre a árvore — passou a **reportar limpo**, não porque a árvore estivesse limpa, mas porque
149
+ o git nunca enxergou aqueles arquivos. **Um guarda que não consegue observar o objeto reporta
150
+ verde por construção.**
151
+
152
+ Com a chave curta o pior caminho é **145**, e o teto que o cobra é 160 — a diferença é
153
+ orçamento explícito, não sobra por acaso:
154
+
155
+ | Parcela | Chars |
156
+ |---|---|
157
+ | `MAX_PATH` utilizável do Windows | 259 |
158
+ | − `node_modules\@polymorphism-tech\morph-spec\` (estes arquivos viajam no tarball npm) | 43 |
159
+ | − teto do caminho relativo | 160 |
160
+ | = folga para a raiz do projeto de quem instala | **56** |
161
+
162
+ Quem cobra os dois fatos — que cada arquivo é **endereçável pelo git** e que o pior caminho
163
+ cabe no teto — é `test/framework/eval-cache-portability.test.js`, e ele recusa passar sobre
164
+ um conjunto vazio: um canário que roda sobre zero arquivos é exatamente o canário cego que
165
+ o defeito original tinha.
166
+
167
+ ### O payload é opaco: `-text` no `.gitattributes`
168
+
169
+ A árvore de cache precisa de uma linha própria em `.gitattributes`:
170
+
171
+ ```
172
+ <cachePath>/** -text
173
+ ```
174
+
175
+ Sem ela, o `text=auto eol=lf` do repositório **normaliza** os `contents.data` — medido: avisos
176
+ de CRLF→LF nos quatro payloads, e bytes que não sobreviveriam a um clone. `-text` (e não
177
+ `binary`) porque ele preserva a fidelidade de byte **sem** perder o diff legível: quando o
178
+ canário acusa, ele mostra o quê mudou, e não apenas que mudou.
179
+
180
+ E o invariante que sustenta a commitabilidade é barato de cobrar: **um run offline não
181
+ escreve um byte na árvore de cache.** Um canário que compara a árvore antes e depois (por
182
+ conteúdo, não por data de modificação) morde no dia em que a biblioteca passar a tocar disco
183
+ no caminho de hit — e não meses depois, quando alguém estranhar um diff que aparece sozinho.
184
+
185
+ ## Gate duro só em propriedade determinística
186
+
187
+ | Papel | Quem | Reprova sozinho? |
188
+ |---|---|---|
189
+ | Asserção determinística | contém / não contém, chave de JSON, ferramenta escolhida, argumento de ferramenta, ausência de PII | **SIM** — é o gate |
190
+ | Juízo de LLM | `RelevanceEvaluator`, `CompletenessEvaluator` | **NÃO** — número reportado |
191
+
192
+ Um juízo de LLM que reprovasse sozinho reintroduziria exatamente o `FLAKY` que fez a decisão
193
+ D-F5-2 manter o eval fora do CI. A métrica de juízo continua útil (dá tendência entre
194
+ versões de prompt) sem precisar ser autoritativa — e, com a cache quente, ela é
195
+ determinística também, porque a resposta do juiz está na cache.
196
+
197
+ > **Dois dos quatro avaliadores de qualidade são `[Experimental("AIEVAL001")]` na 10.9.0**
198
+ > (`TaskAdherenceEvaluator` e `ToolCallAccuracyEvaluator`). Sob `TreatWarningsAsErrors`,
199
+ > usá-los exige suprimir um diagnóstico — uma decisão consciente, nunca um efeito colateral.
200
+ > `.Safety` e `.NLP` ficam fora por outro motivo: ambos são preview, e `.Safety` exige
201
+ > `TokenCredential` + Azure AI Foundry **na construção** — o oposto de "roda em CI sem
202
+ > credencial".
203
+
204
+ Um cenário **sem** asserção determinística não passa: ele não cobra nada. Deixá-lo verde é o
205
+ mesmo defeito de um teste vazio, e aqui seria pior, porque o cenário apareceria na contagem
206
+ como se tivesse verificado algo.
207
+
208
+ ## Quando o eval roda
209
+
210
+ Declare-o como um nó a mais em `project.tests[]` — não como uma estrutura paralela:
211
+
212
+ ```json
213
+ {
214
+ "project": {
215
+ "tests": [
216
+ { "id": "unit", "command": "dotnet test tests/App.Tests/App.Tests.csproj" },
217
+ {
218
+ "id": "evals",
219
+ "runner": "evals",
220
+ "command": "dotnet test tests/App.Tests/App.Tests.csproj --filter-trait Category=Eval",
221
+ "evals": { "cachePath": "tests/App.Tests/eval-cache", "envFile": ".env.evals" }
222
+ }
223
+ ],
224
+ "evals": { "watch": ["tenants/**", "product/**", "config/model-registry.json"] }
225
+ }
226
+ }
227
+ ```
228
+
229
+ | Quando | O que acontece |
230
+ |---|---|
231
+ | `verify {feature} {task}` (loop de task) | **pulado**, com motivo registrado — o relógio de uma task não paga uma suíte de eval |
232
+ | `verify {feature}` (escopo de feature) | **roda**, offline, contra a cache commitada |
233
+ | miss de cache sem credencial | **reprova** nomeando o cenário, o caminho, o comando de regravação e o arquivo de credencial |
234
+ | `--refresh-cache` | regrava a árvore contra o provider real. **Nunca implícito**: é uma chamada paga |
235
+
236
+ **Miss jamais vira `skip`.** A tentação é real ("não dá para avaliar, então não avalio") e
237
+ recria o buraco que este harness já fechou duas vezes: gate verde sobre verificação que não
238
+ aconteceu. E há razão positiva: em CI, com cache commitada, um miss significa **uma coisa
239
+ só** — o prompt ou o modelo mudou e ninguém regravou. O miss é o veredito, não o obstáculo.
240
+
241
+ ## `evals.watch[]`: o que conta como "mudei o prompt"
242
+
243
+ Os globs declaram, **para o projeto**, quais caminhos são prompt ou modelo. Eles são casados
244
+ contra o que a feature mudou (diff contra a branch default **mais** arquivos novos ainda não
245
+ rastreados) e alimentam a regra de Gate 3: tocou caminho declarado e não há eval verde
246
+ carimbado no mesmo commit ⇒ o gate **pausa** e nomeia o caminho.
247
+
248
+ Exclua `<cachePath>/**` e `<datasetPath>/**` dos globs. Sem isso, regravar a cache dispara a
249
+ própria regra.
250
+
251
+ O detector é por caminho, e as três formas conhecidas de escapar dele estão ditas em voz
252
+ alta, porque um detector que se apresenta como completo e não é vale menos que nenhum:
253
+
254
+ 1. **eval trivialmente verde** — um cenário só, asserção vazia. É a mesma classe de contorno
255
+ de um teste vazio, e a resposta é a rubrica de prova, não uma segunda regra determinística
256
+ que também seria contornável;
257
+ 2. **prompt publicado direto no banco**, sem tocar arquivo;
258
+ 3. **modelo trocado por variável de ambiente** no provedor de deploy — invisível ao
259
+ repositório por construção.
260
+
261
+ ## Semeando a cache exemplar sem gastar dinheiro
262
+
263
+ A cache commitada de um kit ou de um projeto novo é gravada rodando o fixture em modo
264
+ `Refresh` contra um **fake roteirizado**, cujo `ChatClientMetadata` é configurado com o
265
+ mesmo `providerName`/`defaultModelId` que o cliente offline vai replicar.
266
+
267
+ Isto não é um atalho: o caminho de **escrita** exercitado é o da biblioteca, e a chave é
268
+ computada pela mesma função que a leitura vai usar. A leitura offline lê exatamente o que
269
+ aquela escrita produziu.
270
+
271
+ **A lacuna residual, declarada:** isso não prova que uma cache gravada contra um provider
272
+ real é legível offline. O que a mitiga é o fato medido acima — a chave depende de
273
+ `ProviderName` e `DefaultModelId`, e não do endpoint, e ambos estão sob nosso controle.
274
+
275
+ ## Fronteira com `testing-ai`
276
+
277
+ | Standard | Cobre |
278
+ |---|---|
279
+ | `testing-ai` | Fake roteirizado rodando o loop real do agente; prova ferramenta escolhida, argumentos e guard. **Sem rede, sem modelo** |
280
+ | `evals-with-cache` (este) | Modelo **real** com cache em disco; dataset por tenant/agente; regressão de prompt e de modelo; o nó `evals` no `verify` |
281
+
282
+ Em uma frase: `testing-ai` prova que **o código em volta do modelo** se comporta;
283
+ `evals-with-cache` prova que **o modelo** ainda responde o que respondia.
284
+
285
+ ## Se precisar mapear o blast radius de um prompt
286
+
287
+ `morph-spec graph explain <símbolo>` ajuda a achar quem lê um prompt ou um alias de modelo.
288
+ **Sem grafo construído, caia para `grep -rn` e siga em frente** — a ausência do grafo nunca
289
+ bloqueia este ciclo, e o fallback é a resposta correta, não um plano B envergonhado.
@@ -0,0 +1,222 @@
1
+ # Guardrails — o que o código garante quando o prompt já falhou
2
+
3
+ > **Scope:** stacks=["dotnet"]
4
+ > **Layer:** 1 (on-keyword)
5
+ > **Keywords:** guardrail, guard de tool, regra proibida, validacao de pertinencia, fail-open guard, argumento nao confiavel, choke-point, veredito ternario
6
+ > **Read by Claude in:** implement (ao escrever regra de negócio que o modelo poderia violar) e review (ao avaliar o que garante o comportamento)
7
+
8
+ **Verified against:** Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não traz um guard de exemplo; o padrão é destilado de dois repositórios em produção, com o local de cada evidência citado na seção correspondente. Last-verified: 2026-09-08.
9
+
10
+ ---
11
+
12
+ ## Regra proibida vira código
13
+
14
+ **Instrução que já falhou ao vivo não se reforça escrevendo-a de novo em maiúsculas.**
15
+
16
+ Quando uma regra tem consequência real — não falar de um assunto, não prometer um prazo, não citar
17
+ um valor — ela sai do prompt e vira uma condição em C#. O prompt continua existindo para *orientar*;
18
+ o código existe para *garantir*. São papéis diferentes, e confundi-los é como se descobre em
19
+ produção que "o modelo geralmente obedece".
20
+
21
+ Três formas medidas em campo, em ordem crescente de força:
22
+
23
+ | Forma | Onde acontece | O que garante |
24
+ |---|---|---|
25
+ | **Gatilho literal avaliado antes do turno** | No compositor, sobre o texto de entrada | O assunto proibido nunca chega ao modelo com as tools que o executariam |
26
+ | **Veredito calculado em .NET antes do turno e reescrito depois sobre a resposta** | Antes e depois da chamada | O número que o produto publica é o número que o backend calculou, não o que o modelo escreveu |
27
+ | **Dado resolvido e pontuado em .NET, com o agente apenas copiando** | Fora do modelo | O modelo não tem oportunidade de errar a conta |
28
+
29
+ **O motivo empírico está escrito no próprio código de campo**, e é a frase mais útil desta seção:
30
+ *"o modelo obedece bem a 'uma entrada por elemento desta lista', mas erra feio quando precisa julgar
31
+ sozinho quais blocos têm dado"* — com a medição ao lado: uma saída esperada de dez itens saía com
32
+ dois.
33
+
34
+ A leitura correta disso não é "o modelo é ruim". É: **o modelo é bom em transformação enumerada e
35
+ ruim em julgamento de suficiência.** Dê-lhe a lista; faça o julgamento você.
36
+
37
+ ---
38
+
39
+ ## Onde fica o choke-point
40
+
41
+ Três lugares legítimos, e cada um pega uma classe diferente de problema. Escolher o errado é o que
42
+ faz um guard parecer instalado sem estar.
43
+
44
+ **(a) Antes do turno, no compositor.** Pega o que não deve nem ser oferecido: a fase errada, a tool
45
+ que este tenant não tem, o assunto proibido. É o mais barato — o modelo nem é chamado — e o único
46
+ que evita gastar dinheiro com uma chamada que já nasceu errada.
47
+
48
+ **(b) No middleware de invocação de função.** Pega o que o modelo pediu e não deveria ter pedido, e
49
+ decide **o que volta ao modelo**. Duas exigências de forma:
50
+
51
+ - **Vocabulário fechado de desfecho.** `ok` · `error` · `invalid_args` · `limit` · `unavailable`.
52
+ Fechado porque a alternativa — devolver a mensagem da exceção — dá ao modelo texto livre para
53
+ interpretar, e o modelo interpreta.
54
+ - **Argumento obrigatório ausente é barrado sem executar a tool**, lendo `required` do próprio
55
+ schema JSON da função. Não se escreve a lista de obrigatórios num segundo lugar: ela já existe, e
56
+ a segunda cópia é a que diverge.
57
+
58
+ **(c) Depois do run, como backstop.** É o único caminho em que o **código** invoca a implementação
59
+ da tool diretamente, quando a resposta indica que algo obrigatório não foi feito. Serve como rede,
60
+ nunca como mecanismo principal: um guard que só existe em (c) deixa o modelo decidir e depois
61
+ conserta, pagando as duas coisas.
62
+
63
+ O **mecanismo** de middleware — os três níveis, o registro no builder, a ordem — é de
64
+ `ai-agents-middleware-patterns`. Este standard usa esse mecanismo e não o repete.
65
+
66
+ ---
67
+
68
+ ## Argumento de tool é input não confiável
69
+
70
+ **Argumento vindo do modelo é entrada de usuário, com a agravante de ser gerada por algo que não
71
+ tem intenção.**
72
+
73
+ A regra: **identificadores e valores com efeito vêm da closure do turno, nunca do modelo.** A tool é
74
+ construída já sabendo em qual conversa, em qual tenant e para qual registro ela opera; o modelo só
75
+ fornece o que é genuinamente decisão dele.
76
+
77
+ Os dois lados existem no acervo, e vale ver os dois:
78
+
79
+ - **Padrão:** as tools de um produto são construídas sobre closures, com a regra escrita no código —
80
+ *"o modelo nunca fornece o identificador da conta, o identificador do contato nem valores
81
+ monetários"*.
82
+ - **Padrão, no outro projeto:** o filtro de tenant da busca vetorial *"sempre vem do caller, nunca
83
+ do LLM"*.
84
+
85
+ O caso que essa regra impede é direto: uma tool que aceita o identificador do registro como
86
+ argumento é uma tool que, com o prompt certo na conversa errada, opera sobre o registro de outra
87
+ pessoa. Não é um risco hipotético de segurança — é um bug de correção que aparece como suporte.
88
+
89
+ Corolário: **valide o que sobra.** O que o modelo legitimamente decide (um texto, uma categoria, um
90
+ valor dentro de uma faixa) ainda passa pelo schema e pelas regras de domínio, como qualquer entrada.
91
+
92
+ ---
93
+
94
+ ## Pertinência, não presença
95
+
96
+ Esta é a seção difícil, e a que separa um guard de um `!= null`.
97
+
98
+ O guard fraco pergunta: *o campo veio preenchido?*
99
+ O guard forte pergunta: **o que veio se sustenta?**
100
+
101
+ Três formas medidas, em ordem crescente de custo e de força:
102
+
103
+ **(1) Rubrica determinística que soma 100.** As bandas espelham o que o prompt pediu, há uma
104
+ whitelist explícita de "sem dados" (para que ausência legítima não seja punida como erro), e — o
105
+ detalhe que faz a diferença — **os pontos que o backend já calculou são conferidos por igualdade**,
106
+ não por plausibilidade. O validador **acumula todos os erros** em vez de parar no primeiro, e
107
+ classifica o resultado em **incompleto × inválido**, que são coisas diferentes: o primeiro pede mais
108
+ dado, o segundo pede rejeição. Referência de campo: o maior arquivo da camada de agentes daquele
109
+ projeto, com 343 linhas, é justamente esse validador.
110
+
111
+ **(2) Trava anti-alucinação por proveniência.** A afirmação do modelo só sobrevive se houver
112
+ **rastro de origem**: um "encontrei" só permanece verdadeiro se o identificador correspondente
113
+ estiver no sink de captura — isto é, se alguma tool de fato o raspou. Sem rastro, o resultado é
114
+ **rebaixado** para "não encontrado", **preservando o texto de raciocínio original para auditoria**.
115
+ Preservar é parte do padrão: apagar o raciocínio destrói a única pista de por que o modelo
116
+ alucinou.
117
+
118
+ **(3) Corte de confiança, com o caso real que o justifica.** O modelo casou um perfil apenas pelo
119
+ nome e devolveu confiança 85, usando **a ausência de concorrentes como evidência a favor** — um
120
+ raciocínio que soa razoável e é inválido. O remédio foi um mínimo de confiança **por rede**, não um
121
+ mínimo global: a mesma pontuação significa coisas diferentes em fontes diferentes.
122
+
123
+ O que as três têm em comum: **elas comparam com algo que não veio do modelo** — uma conta do
124
+ backend, um rastro de execução, um limiar calibrado por fonte. Um guard que só olha para a resposta
125
+ do modelo está pedindo ao modelo que se avalie.
126
+
127
+ ---
128
+
129
+ ## Quando o guard não consegue decidir
130
+
131
+ A seção que o campo obriga, porque é o caso que mais acontece e o menos escrito.
132
+
133
+ **O acervo responde fail-open, sempre.** Configuração ausente resolve para "pode"; configuração
134
+ ilegível resolve para "pode"; os caminhos auxiliares (persona, sumarização, análise de memória, base
135
+ de conhecimento, multimodal) devolvem vazio ou nulo em qualquer falha, e o turno segue.
136
+
137
+ **Este standard ratifica o fail-open — com uma condição dura: o escape é registrado, nunca
138
+ silencioso.**
139
+
140
+ Ratifica porque a alternativa é pior no caso concreto: um guard que fecha quando não sabe derruba a
141
+ conversa por causa de uma configuração ausente, e o custo de um falso bloqueio numa conversa ao vivo
142
+ é maior que o de uma permissão a mais que alguém vai auditar.
143
+
144
+ A condição é dura porque sem ela o fail-open é indistinguível de um bug.
145
+
146
+ **Precedente que mostra a forma certa:** um guard **deliberadamente permeável** — rejeita a primeira
147
+ tentativa, a segunda passa — **com o escape instrumentado**, num contador dedicado. A permeabilidade
148
+ foi uma decisão de produto; a instrumentação é o que a torna revisável. *Guard permeável e medido é
149
+ engenharia; guard permeável e mudo é bug.*
150
+
151
+ **O anti-padrão, com número do campo:** **81 de 83 blocos `catch (Exception ex)` sem leitura do
152
+ `ex`**, no mesmo projeto. Isso é fail-open sem instrumentação em escala — e está registrado como
153
+ dívida pelo próprio time, o que significa que nem eles conseguem responder o que está sendo
154
+ engolido.
155
+
156
+ ### Veredito ternário
157
+
158
+ O resultado de um guard é `pass` / `reject` / `not_evaluated`, com **`reject` vencendo `pass` na
159
+ mesma execução**.
160
+
161
+ Um booleano perde exatamente a informação que o fail-open produz: *não deu para avaliar*. Com
162
+ booleano, "não avaliado" vira "aprovado", e a métrica de aprovação passa a incluir tudo o que o
163
+ guard não conseguiu olhar — que é a métrica errada com a aparência da certa.
164
+
165
+ `not_evaluated` também é o que permite responder "quantas vezes este guard sequer rodou?", que é a
166
+ primeira pergunta a fazer quando um guard "nunca reprova nada".
167
+
168
+ ---
169
+
170
+ ## Anti-patterns
171
+
172
+ | Anti-pattern | Por quê é problema | Solução |
173
+ |---|---|---|
174
+ | Regra crítica reforçada em maiúsculas no prompt | O modelo geralmente obedece; "geralmente" não é garantia | Vira condição em código |
175
+ | Pedir ao modelo que julgue quais blocos têm dado | Medido: dez itens esperados, dois entregues | Enumere você; peça transformação, não julgamento |
176
+ | Guard só depois do run | Paga a chamada errada e depois conserta | Choke-point antes do turno, quando possível |
177
+ | Devolver a mensagem da exceção ao modelo | Texto livre que o modelo interpreta | Vocabulário fechado de desfecho |
178
+ | Lista de argumentos obrigatórios escrita num segundo lugar | A segunda cópia diverge do schema | Leia `required` do próprio schema da função |
179
+ | Identificador ou valor vindo do modelo | Com o prompt certo, opera no registro de outra pessoa | Closure do turno |
180
+ | Guard que checa presença (`!= null`) | Aprova qualquer coisa preenchida, inclusive alucinação | Compare com algo que não veio do modelo |
181
+ | Validador que para no primeiro erro | Uma correção por rodada; o custo vira o número de rodadas | Acumule todos, e separe incompleto de inválido |
182
+ | Rebaixar uma alucinação apagando o raciocínio | Destrói a única pista de por que aconteceu | Rebaixe preservando o raciocínio para auditoria |
183
+ | Limiar de confiança global | A mesma pontuação significa coisas diferentes por fonte | Limiar por fonte |
184
+ | Fail-open silencioso | Indistinguível de bug; 81 de 83 `catch` sem leitura | Fail-open **registrado** |
185
+ | Veredito booleano | "Não avaliado" vira "aprovado", e a métrica mente | `pass` / `reject` / `not_evaluated` |
186
+
187
+ ---
188
+
189
+ ## Checklist (verifiable by morph-eval)
190
+
191
+ - [ ] Toda regra com consequência real existe como condição em código, não apenas no prompt
192
+ - [ ] O choke-point escolhido está nomeado no `decisions.md`, com o que ele pega
193
+ - [ ] O middleware de tool devolve **vocabulário fechado** de desfecho
194
+ - [ ] Argumento obrigatório ausente é barrado **sem** executar a tool, lendo o schema da própria função
195
+ - [ ] Nenhum identificador com efeito vem do modelo — todos vêm da closure do turno
196
+ - [ ] Ao menos um guard compara a saída com algo que **não** veio do modelo
197
+ - [ ] Validador acumula erros e distingue incompleto de inválido
198
+ - [ ] Todo caminho fail-open **registra** o escape num contador ou span
199
+ - [ ] O veredito é ternário, e `reject` vence `pass` na mesma execução
200
+ - [ ] Existe forma de responder "quantas vezes este guard rodou, e quantas ele não conseguiu avaliar"
201
+
202
+ ---
203
+
204
+ ## References
205
+
206
+ - `ai-agents-middleware-patterns` — **dono do mecanismo** de middleware de invocação de função
207
+ (os três níveis, o registro, a ordem). Este standard o usa e não o repete
208
+ - `ai-agents-conversational-agent-with-phases` — o choke-point (a) na prática: compositor,
209
+ allow-list por fase e teto de tool calls
210
+ - `ai-agents-agent-spec` — a spec do agente, onde as tools do turno são declaradas
211
+ - `ai-agents-structured-output` — o schema cuja seção `required` o guard lê
212
+ - `ai-agents-observability-patterns` — onde o escape registrado vira span e métrica
213
+ - `ai-agents-production` — o panorama que hoje cita guardrails como um item de checklist
214
+
215
+ > **Lacuna declarada.** O par compilável deste standard seria um guard de exemplo em
216
+ > `templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/`, com o teste que o vê barrar um argumento
217
+ > obrigatório ausente **sem** executar a tool. **Ele ainda não existe** no ai-kit. Por isso o
218
+ > cabeçalho declara verificação documental e não carimba `provado por`.
219
+
220
+ ---
221
+
222
+ *MORPH-SPEC by Polymorphism Tech — ai-agents/guardrails.md v1.0 (2026-09-08)*