@polymorphism-tech/morph-spec 8.46.0 → 9.0.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 +53 -44
- package/README.md +3 -4
- package/bin/morph-spec.js +38 -55
- package/bin/validate.js +5 -134
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +2 -2
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +75 -18
- package/framework/commands/create-umbraco-lp.md +2 -0
- package/framework/commands/migrate-lp-to-umbraco.md +2 -0
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +2 -2
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +17 -203
- package/framework/commands/morph-media.md +3 -0
- package/framework/commands/morph-preflight.md +7 -31
- package/framework/commands/morph-proposal.md +2 -50
- package/framework/commands/morph-status.md +3 -3
- package/framework/commands/morph-troubleshoot.md +2 -14
- package/framework/docs/CLI.md +585 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/comment-guard.js +1 -1
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +16 -20
- package/framework/hooks/claude-code/core/gate-guard.js +2 -11
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +38 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +30 -242
- package/framework/hooks/claude-code/core/state-sync.js +17 -19
- package/framework/hooks/claude-code/core/telemetry-log.js +3 -3
- package/framework/hooks/claude-code/core/validate-completion.js +33 -97
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +31 -0
- package/framework/hooks/claude-code/core/worktree-context.js +5 -4
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +1 -1
- package/framework/hooks/shared/build-check.js +10 -6
- package/framework/hooks/shared/command-tokens.js +169 -0
- package/framework/hooks/shared/comment-rules.js +1 -1
- package/framework/hooks/shared/comment-scanner.js +10 -23
- package/framework/hooks/shared/comment-stripper.js +1 -1
- package/framework/hooks/shared/e2e-check.js +67 -31
- package/framework/hooks/shared/errata.js +72 -0
- package/framework/hooks/shared/feature-locator.js +1 -1
- package/framework/hooks/shared/frontend-locations.js +6 -0
- package/framework/hooks/shared/gate-question.js +18 -0
- package/framework/hooks/shared/phase-utils.js +15 -32
- package/framework/hooks/shared/slice-progress.js +118 -0
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +61 -87
- package/framework/hooks/shared/state-writer.js +3 -3
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/ai-agents-standards.md +18 -16
- package/framework/rules/asaas-standards.md +3 -1
- package/framework/rules/csharp-standards.md +10 -8
- package/framework/rules/data-standards.md +5 -5
- package/framework/rules/dotnet-templates.md +1 -1
- package/framework/rules/frontend-standards.md +4 -2
- package/framework/rules/gsap-standards.md +10 -5
- package/framework/rules/hangfire-standards.md +3 -1
- package/framework/rules/infrastructure-standards.md +4 -2
- package/framework/rules/neon-auth-standards.md +3 -1
- package/framework/rules/neon-standards.md +6 -4
- package/framework/rules/nextjs-standards.md +13 -11
- package/framework/rules/nextjs-templates.md +1 -1
- package/framework/rules/resend-standards.md +3 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -52
- package/framework/skills/README.md +4 -5
- package/framework/skills/core/create-umbraco-lp/SKILL.md +3 -3
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +3 -3
- package/framework/skills/core/morph-apply/SKILL.md +150 -223
- package/framework/skills/core/morph-hotfix/SKILL.md +34 -43
- package/framework/skills/core/morph-machine/SKILL.md +4 -4
- package/framework/skills/core/morph-media/SKILL.md +3 -3
- package/framework/skills/core/morph-plan/SKILL.md +138 -300
- package/framework/skills/core/morph-review/SKILL.md +112 -137
- package/framework/skills/core/morph-standards/SKILL.md +29 -41
- package/framework/skills/core/morph-uiux/SKILL.md +78 -153
- package/framework/standards/STANDARDS.json +90 -1219
- package/framework/standards/STANDARDS.meta.json +1305 -0
- package/framework/standards/ai-agents/evals-with-cache.md +2 -3
- package/framework/standards/ai-agents/llm-runtime-defaults.md +1 -1
- package/framework/standards/ai-agents/modalities-speech-to-text.md +36 -26
- package/framework/standards/ai-agents/prompt-sources.md +6 -4
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +67 -39
- package/framework/standards/ai-agents/setup.md +2 -2
- package/framework/standards/ai-agents/structured-output.md +1 -1
- package/framework/standards/ai-agents/sweet-spot.md +1 -1
- package/framework/standards/architecture/code-comments.md +8 -8
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +18 -9
- package/framework/standards/backend/api/validation.md +135 -47
- package/framework/standards/backend/database/postgresql/database.md +11 -7
- package/framework/standards/backend/database/vector-search-rag.md +207 -324
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +14 -8
- package/framework/standards/backend/dotnet/result-pattern.md +40 -6
- package/framework/standards/backend/dotnet/scalar-ui.md +13 -7
- package/framework/standards/backend/dotnet/scrutor.md +1 -1
- package/framework/standards/backend/dotnet/testing.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +7 -7
- package/framework/standards/backend/integrations/asaas/asaas-api.md +309 -165
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +134 -154
- package/framework/standards/data/nosql/cache/redis.md +120 -44
- package/framework/standards/data/nosql/cosmos-db.md +114 -71
- package/framework/standards/frontend/design-system/animations.md +1 -1
- package/framework/standards/frontend/design-system/naming.md +1 -1
- package/framework/standards/frontend/nextjs/motion-patterns.md +45 -29
- package/framework/standards/frontend/nextjs/naming-conventions.md +1 -1
- package/framework/standards/infrastructure/docker/coolify-deploy.md +8 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +120 -75
- package/framework/standards/infrastructure/neon/neon-setup.md +53 -31
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +6 -170
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/extensions/database-extensions.cs +1 -1
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +27 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +2 -2
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +1 -1
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/infrastructure/docker/coolify.template.json +3 -3
- package/framework/templates/infrastructure/github/README.md +2 -1
- package/framework/templates/infrastructure/github/workflows/deploy-coolify.yml.hbs +2 -1
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/e2e/e2e.js +2 -2
- package/src/commands/observability/cost.js +18 -60
- package/src/commands/observability/telemetry.js +3 -5
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +9 -9
- package/src/commands/project/fleet.js +4 -4
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +6 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +79 -25
- package/src/commands/project/status.js +29 -69
- package/src/commands/project/update.js +33 -19
- package/src/commands/project/worktree.js +1 -1
- package/src/commands/state/advance.js +62 -46
- package/src/commands/state/approve.js +95 -138
- package/src/commands/state/archive.js +20 -84
- package/src/commands/state/auto-approval.js +68 -0
- package/src/commands/state/create.js +28 -1
- package/src/commands/state/delete.js +1 -1
- package/src/commands/state/finish.js +11 -23
- package/src/commands/state/gate-check.js +86 -95
- package/src/commands/state/gate-decision.js +6 -4
- package/src/commands/state/handoff.js +1 -1
- package/src/commands/state/list.js +9 -9
- package/src/commands/state/report-check.js +157 -311
- package/src/commands/state/retype.js +9 -3
- package/src/commands/validation/mutate.js +77 -14
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +60 -135
- package/src/commands/validation/verify.js +41 -82
- package/src/core/paths/output-schema.js +20 -67
- package/src/core/state/gate-order.js +5 -6
- package/src/core/state/lifecycle-audit.js +2 -2
- package/src/core/state/state-manager.js +17 -98
- package/src/core/templates/template-registry.js +2 -2
- package/src/core/templates/template-seed.js +22 -0
- package/src/core/work-types.js +10 -10
- package/src/dashboard/app/App.jsx +0 -2
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -3
- package/src/dashboard/app/components/CommandPalette.jsx +7 -8
- package/src/dashboard/app/components/EntityLink.jsx +1 -2
- package/src/dashboard/app/components/Markdown.jsx +1 -1
- package/src/dashboard/app/components/Sidebar.jsx +1 -2
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -1
- package/src/dashboard/app/lib/phases.js +2 -4
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -129
- package/src/dashboard/app/pages/Features.jsx +9 -9
- package/src/dashboard/app/pages/Guide.jsx +2 -2
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -4
- package/src/dashboard/app/pages/RulesMap.jsx +3 -3
- package/src/dashboard/app/pages/SkillsLibrary.jsx +3 -3
- package/src/dashboard/app/pages/Standards.jsx +1 -1
- package/src/dashboard/scanner/activity-scanner.js +6 -16
- package/src/dashboard/scanner/agents-scanner.js +1 -1
- package/src/dashboard/scanner/docs-scanner.js +5 -23
- package/src/dashboard/scanner/features-scanner.js +95 -60
- package/src/dashboard/scanner/index.js +0 -2
- package/src/dashboard/scanner/project-api.js +1 -1
- package/src/dashboard/scanner/rules-scanner.js +15 -10
- package/src/dashboard/scanner/skills-scanner.js +2 -3
- package/src/dashboard/scanner/standards-scanner.js +2 -1
- package/src/lib/ci/ci-verify.js +17 -1
- package/src/lib/ci/github-ci.js +1 -1
- package/src/lib/detectors/index.js +3 -8
- package/src/lib/generators/handoff-generator.js +34 -65
- package/src/lib/generators/settings-generator.js +11 -89
- package/src/lib/git/evidence-staleness.js +1 -1
- package/src/lib/git/tree-fingerprint.js +19 -7
- package/src/lib/git/tree-state.js +2 -2
- package/src/lib/heavy/cleanup.js +1 -5
- package/src/lib/installers/memory-installer.js +6 -8
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/{tasks/dotnet-test-introspection.js → mutation/dotnet-introspection.js} +17 -282
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +46 -9
- package/src/lib/plan/artifact-vocabulary.js +60 -0
- package/src/lib/plan/checks.js +234 -0
- package/src/lib/plan/plan-parser.js +209 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/review-reports.js +222 -0
- package/src/lib/risk-detector.js +134 -135
- package/src/lib/specs/adr-index.js +50 -69
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +44 -0
- package/src/lib/specs/gfm-table.js +17 -0
- package/src/lib/specs/old-format.js +68 -0
- package/src/lib/specs/revocations.js +37 -95
- package/src/lib/specs/screen-checklist.js +18 -17
- package/src/lib/stack/dotnet-build-ignores.js +47 -0
- package/src/lib/stack/stack-profile.js +1 -1
- package/src/lib/stack/stack-scan.js +267 -0
- package/src/lib/standards/registry.js +43 -0
- package/src/lib/standards/sections.js +58 -0
- package/src/lib/tasks/eval-node.js +4 -12
- package/src/lib/tasks/test-nodes.js +1 -74
- package/src/lib/tasks/test-runner.js +75 -117
- package/src/lib/telemetry/metrics.js +8 -69
- package/src/lib/telemetry/telemetry-reader.js +5 -118
- package/src/lib/telemetry/verify-event.js +5 -7
- package/src/lib/validators/code-budget/code-budget-validator.js +1 -1
- package/src/lib/validators/contracts/contract-compliance-validator.js +2 -2
- package/src/lib/validators/reuse/reuse-validator.js +24 -48
- package/src/lib/validators/reuse/symbol-scanner.js +2 -4
- package/src/lib/validators/traceability-check.js +85 -129
- package/src/lib/validators/validation-runner.js +8 -88
- package/src/lib/validators/verify-runner.js +134 -301
- package/src/lib/worktree/docker-audit.js +1 -2
- package/src/lib/worktree/fleet-scanner.js +25 -38
- package/src/scripts/setup-infra.js +42 -22
- package/src/utils/agents-installer.js +92 -13
- package/src/utils/claude-settings-manager.js +0 -35
- package/src/utils/file-copier.js +34 -6
- package/src/utils/hooks-installer.js +16 -9
- package/src/utils/skills-installer.js +1 -2
- package/framework/commands/commit.md +0 -166
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rubrics/backend.json +0 -41
- package/framework/rubrics/default.json +0 -36
- package/framework/rubrics/frontend.json +0 -36
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/project/validate.js +0 -97
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/dashboard/app/pages/Rubrics.jsx +0 -97
- package/src/dashboard/scanner/rubrics-scanner.js +0 -12
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/evaluator-report.js +0 -91
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/lib/validators/architecture/architecture-validator.js +0 -270
- package/src/lib/validators/architecture/index.js +0 -7
- package/src/lib/validators/css/css-validator.js +0 -340
- package/src/lib/validators/css/index.js +0 -7
- package/src/lib/validators/design-system/design-system-validator.js +0 -237
- package/src/lib/validators/design-system/index.js +0 -7
- package/src/lib/validators/score-aggregator.js +0 -244
- package/src/lib/validators/ui/index.js +0 -7
- package/src/lib/validators/ui/ui-contrast-validator.js +0 -422
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "3.0.0",
|
|
3
3
|
"standards": [
|
|
4
4
|
{
|
|
5
5
|
"id": "ai-agents-agent-archetypes",
|
|
@@ -16,16 +16,6 @@
|
|
|
16
16
|
"stacks": [
|
|
17
17
|
"dotnet"
|
|
18
18
|
],
|
|
19
|
-
"aliases": [
|
|
20
|
-
"agent archetype",
|
|
21
|
-
"arquetipo de agente",
|
|
22
|
-
"qual forma de agente",
|
|
23
|
-
"conversational agent",
|
|
24
|
-
"one-shot agent",
|
|
25
|
-
"batch agent",
|
|
26
|
-
"vision agent",
|
|
27
|
-
"chat with history"
|
|
28
|
-
],
|
|
29
19
|
"anchors": {
|
|
30
20
|
"escolha": "#como-escolher",
|
|
31
21
|
"conversacional": "#conversacional-com-fases",
|
|
@@ -35,12 +25,7 @@
|
|
|
35
25
|
"lastro": "#lastro-de-campo-por-arquétipo",
|
|
36
26
|
"checklist": "#checklist-verifiable-in-review"
|
|
37
27
|
},
|
|
38
|
-
"digest": "The four shapes an agent can take, and the plan-phase question that picks one: conversational-with-phases, one-shot batch, vision + structured output, chat with history. Field ballast is UNEVEN and the standard says so: batch has 3 exemplars, conversational has 1, vision+structured has ZERO (it is a projected shape, not a distilled one), and conversational and chat-with-history are two AXES OF THE SAME AGENT (phases x history ownership), not alternatives. Pairs with ai-agents-sweet-spot, which decides agent-vs-workflow before this file decides which shape."
|
|
39
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` e `.../Conversation/ConversationStore.cs` (arquétipos **one-shot em batch** e **chat com histórico** — o conversacional com fases NÃO tem lastro no kit, e `ai-agents-conversational-agent-with-phases` declara isso no próprio cabeçalho). O arquétipo visão + structured output é **verificação documental** — o ai-kit não exercita multimodalidade.",
|
|
40
|
-
"provedBy": [
|
|
41
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs",
|
|
42
|
-
".../Conversation/ConversationStore.cs"
|
|
43
|
-
]
|
|
28
|
+
"digest": "The four shapes an agent can take, and the plan-phase question that picks one: conversational-with-phases, one-shot batch, vision + structured output, chat with history. Field ballast is UNEVEN and the standard says so: batch has 3 exemplars, conversational has 1, vision+structured has ZERO (it is a projected shape, not a distilled one), and conversational and chat-with-history are two AXES OF THE SAME AGENT (phases x history ownership), not alternatives. Pairs with ai-agents-sweet-spot, which decides agent-vs-workflow before this file decides which shape."
|
|
44
29
|
},
|
|
45
30
|
{
|
|
46
31
|
"id": "ai-agents-agent-session",
|
|
@@ -57,30 +42,13 @@
|
|
|
57
42
|
"stacks": [
|
|
58
43
|
"dotnet"
|
|
59
44
|
],
|
|
60
|
-
"aliases": [
|
|
61
|
-
"agent session",
|
|
62
|
-
"chat history",
|
|
63
|
-
"conversation state",
|
|
64
|
-
"chat reducer",
|
|
65
|
-
"history truncation",
|
|
66
|
-
"multi-turn",
|
|
67
|
-
"persistent session",
|
|
68
|
-
"ChatHistoryProvider",
|
|
69
|
-
"own conversation table",
|
|
70
|
-
"conversa propria",
|
|
71
|
-
"ProviderSessionState"
|
|
72
|
-
],
|
|
73
45
|
"anchors": {
|
|
74
46
|
"when": "#o-critério--de-onde-o-histórico-deve-vir",
|
|
75
47
|
"api": "#a-api",
|
|
76
48
|
"example": "#exemplo-c--agente-conversacional-com-session-persistente-entre-turnos",
|
|
77
49
|
"history": "#history-management"
|
|
78
50
|
},
|
|
79
|
-
"digest": "Where conversation history comes from — the CRITERION first, the verdict after. Own conversation storage (project table/jsonb behind a ChatHistoryProvider subclass) is the DEFAULT whenever the conversation carries business fields, must be readable and editable by the application (retry, audit, deletion), or is multi-tenant; AgentSession + a built-in provider only when history is just the message list; InMemoryChatHistoryProvider only for prototypes. Override ProvideChatHistoryAsync / StoreChatHistoryAsync; serialize with SerializeSessionAsync / DeserializeSessionAsync. Key invariant: ONE provider instance shared by every session — per-session state lives in ProviderSessionState<TState>, never in a provider field. ConversationId is scoped to the API key, never an authorization boundary. The pre-GA thread API was renamed and no longer exists in 1.20.0."
|
|
80
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs` (a subclasse de `ChatHistoryProvider`, a invariante de instância única e o teste de duas sessões concorrentes). Nomes de tipo e membros medidos por reflexão sobre `Microsoft.Agents.AI.Abstractions` 1.20.0 em 2026-09-08.",
|
|
81
|
-
"provedBy": [
|
|
82
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs"
|
|
83
|
-
]
|
|
51
|
+
"digest": "Where conversation history comes from — the CRITERION first, the verdict after. Own conversation storage (project table/jsonb behind a ChatHistoryProvider subclass) is the DEFAULT whenever the conversation carries business fields, must be readable and editable by the application (retry, audit, deletion), or is multi-tenant; AgentSession + a built-in provider only when history is just the message list; InMemoryChatHistoryProvider only for prototypes. Override ProvideChatHistoryAsync / StoreChatHistoryAsync; serialize with SerializeSessionAsync / DeserializeSessionAsync. Key invariant: ONE provider instance shared by every session — per-session state lives in ProviderSessionState<TState>, never in a provider field. ConversationId is scoped to the API key, never an authorization boundary. The pre-GA thread API was renamed and no longer exists in 1.20.0."
|
|
84
52
|
},
|
|
85
53
|
{
|
|
86
54
|
"id": "ai-agents-agent-spec",
|
|
@@ -97,15 +65,6 @@
|
|
|
97
65
|
"stacks": [
|
|
98
66
|
"dotnet"
|
|
99
67
|
],
|
|
100
|
-
"aliases": [
|
|
101
|
-
"agent spec",
|
|
102
|
-
"AgentSpec",
|
|
103
|
-
"agent catalog",
|
|
104
|
-
"AgentCatalog",
|
|
105
|
-
"um agente por spec",
|
|
106
|
-
"defaults do agente",
|
|
107
|
-
"quantos agentes"
|
|
108
|
-
],
|
|
109
68
|
"anchors": {
|
|
110
69
|
"dado": "#o-agente-como-dado",
|
|
111
70
|
"spec": "#agentspec",
|
|
@@ -114,13 +73,7 @@
|
|
|
114
73
|
"higiene": "#higiene-de-publicação",
|
|
115
74
|
"checklist": "#checklist-verifiable-in-review"
|
|
116
75
|
},
|
|
117
|
-
"digest": "The agent as DATA: one immutable AgentSpec record per agent (Alias required, Instructions required, Api?, Tools, OutputSchema?, Options, Name?, Description?), a catalog that enumerates them, and defaults outside the code. The doctrine this standard freezes: a NULL property means \"do not send to the provider\", never \"send the framework default\" — and an empty list is NOT treated as null, it is sent as written. Precedence between the spec api and the alias api lives in ONE method on the record, never inside the factory. Blank Alias/Instructions are rejected at construction. A catalog entry with no runtime consumer is dead weight (measured: a seeded agent with a versioned prompt and zero C# callers)."
|
|
118
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs`, `.../Agents/AgentSpecOptions.cs` e `.../Agents/AgentFactory.cs` — o record, o bag de opções e a materialização são exatamente o que o kit compila.",
|
|
119
|
-
"provedBy": [
|
|
120
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs",
|
|
121
|
-
".../Agents/AgentSpecOptions.cs",
|
|
122
|
-
".../Agents/AgentFactory.cs"
|
|
123
|
-
]
|
|
76
|
+
"digest": "The agent as DATA: one immutable AgentSpec record per agent (Alias required, Instructions required, Api?, Tools, OutputSchema?, Options, Name?, Description?), a catalog that enumerates them, and defaults outside the code. The doctrine this standard freezes: a NULL property means \"do not send to the provider\", never \"send the framework default\" — and an empty list is NOT treated as null, it is sent as written. Precedence between the spec api and the alias api lives in ONE method on the record, never inside the factory. Blank Alias/Instructions are rejected at construction. A catalog entry with no runtime consumer is dead weight (measured: a seeded agent with a versioned prompt and zero C# callers)."
|
|
124
77
|
},
|
|
125
78
|
{
|
|
126
79
|
"id": "ai-agents-batching",
|
|
@@ -137,26 +90,13 @@
|
|
|
137
90
|
"stacks": [
|
|
138
91
|
"dotnet"
|
|
139
92
|
],
|
|
140
|
-
"aliases": [
|
|
141
|
-
"batching",
|
|
142
|
-
"batch api",
|
|
143
|
-
"bulk prompts",
|
|
144
|
-
"batch processing",
|
|
145
|
-
"async batch",
|
|
146
|
-
"bulk inference",
|
|
147
|
-
"offline batch",
|
|
148
|
-
"CreateBatchOperation",
|
|
149
|
-
"BatchClient"
|
|
150
|
-
],
|
|
151
93
|
"anchors": {
|
|
152
94
|
"when": "#quando-usar-batch",
|
|
153
95
|
"api": "#a-api",
|
|
154
96
|
"example": "#exemplo-c-completo",
|
|
155
97
|
"when-not": "#quando-não-usar"
|
|
156
98
|
},
|
|
157
|
-
"digest": "OpenAI Batch API for N independent prompts with no user waiting: 50% cheaper, up to 24h, separate rate limits. Flow is upload JSONL -> submit -> poll -> download, and the poll belongs in a background job. Measured against OpenAI 2.13.0: Batch is a PROTOCOL API, not a model one — CreateBatchAsync takes BinaryContent (there is no (fileId, endpoint, completionWindow) overload), GetBatchAsync returns a raw ClientResult, BatchJob.Status is a non-exported type, and OpenAIBatch does not exist; read the status from the JSON. GetBatchClient(), OpenAI.Batch, FileUploadPurpose.Batch, UploadFileAsync and DownloadFileAsync are confirmed. Persist CreateBatchOperation.RehydrationToken if the poller can restart. Always handle error_file_id."
|
|
158
|
-
"verifiedAgainst": "OpenAI 2.13.0 (ai-pin 2026-09-08) + Microsoft.Extensions.AI 10.9.0. **Verificação documental** do fluxo da Batch API (platform.openai.com/docs/guides/batch, lido 2026-09-08), sem cláusula `provado por`: o ai-kit **referencia** o pacote `OpenAI` no `.csproj` — contrato de versão, régua = restore (`NU1102`) — mas **não o exercita**; não existe um único `using OpenAI` em `src/` (medido 2026-09-08), logo um rename de API não reprova a PR. Ver a tabela \"Dois níveis de garantia\" no README do kit. **Todos os nomes de tipo e assinaturas .NET foram medidos por reflexão sobre a assembly `OpenAI` 2.13.0 em 2026-09-08**, e os 9 marcadores `// VERIFY` desta página foram RESOLVIDOS por essa medição.",
|
|
159
|
-
"provedBy": []
|
|
99
|
+
"digest": "OpenAI Batch API for N independent prompts with no user waiting: 50% cheaper, up to 24h, separate rate limits. Flow is upload JSONL -> submit -> poll -> download, and the poll belongs in a background job. Measured against OpenAI 2.13.0: Batch is a PROTOCOL API, not a model one — CreateBatchAsync takes BinaryContent (there is no (fileId, endpoint, completionWindow) overload), GetBatchAsync returns a raw ClientResult, BatchJob.Status is a non-exported type, and OpenAIBatch does not exist; read the status from the JSON. GetBatchClient(), OpenAI.Batch, FileUploadPurpose.Batch, UploadFileAsync and DownloadFileAsync are confirmed. Persist CreateBatchOperation.RehydrationToken if the poller can restart. Always handle error_file_id."
|
|
160
100
|
},
|
|
161
101
|
{
|
|
162
102
|
"id": "ai-agents-context-providers",
|
|
@@ -173,28 +113,13 @@
|
|
|
173
113
|
"stacks": [
|
|
174
114
|
"dotnet"
|
|
175
115
|
],
|
|
176
|
-
"aliases": [
|
|
177
|
-
"context provider",
|
|
178
|
-
"dynamic context",
|
|
179
|
-
"context injection",
|
|
180
|
-
"AIContextProvider",
|
|
181
|
-
"memory provider",
|
|
182
|
-
"inject context",
|
|
183
|
-
"system prompt injection",
|
|
184
|
-
"compaction",
|
|
185
|
-
"MAAI001",
|
|
186
|
-
"tool before provider",
|
|
187
|
-
"ordem de escolha"
|
|
188
|
-
],
|
|
189
116
|
"anchors": {
|
|
190
117
|
"when": "#quando-usar-context-provider",
|
|
191
118
|
"api": "#a-api",
|
|
192
119
|
"example": "#exemplo-c--context-provider-que-injeta-data-atual-e-perfil-do-tenant",
|
|
193
120
|
"comparison": "#context-provider-vs-tools-vs-rag"
|
|
194
121
|
},
|
|
195
|
-
"digest": "Order of choice first: a TOOL comes before an AIContextProvider — the provider is for cross-cutting context (memory, compaction, tenant) the LLM must not be able to skip. Passive RAG as a provider is the concrete anti-pattern it prevents (a vector search paid on every \"hi\"). Phase plans (instructions + a tool allow-list per funnel stage) are a deterministic C# composer, not a provider — AIContext only ADDS tools, it cannot remove them. AIContext has exactly Instructions/Messages/Tools; AgentRequestMessageSourceType prevents re-injection loops. Same instance invariant as agent-session (ProviderSessionState<TState>). Compaction is [Experimental(\"MAAI001\")] and where you register it changes the result: on the ChatClientBuilder it compacts only the in-flight request; on ChatClientAgentOptions it skips the tool loop and leaks the summary into persisted history. UseAIContextProviders ships in Microsoft.Agents.AI, not .Hosting."
|
|
196
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08) + doc oficial de context providers e de compaction (learn.microsoft.com, `ms.date` 2026-07-30). **Verificação documental** para o desenho — o ai-kit não registra `AIContextProvider`; os nomes de tipo, o pacote de cada extensão e o atributo `[Experimental]` da compaction foram **medidos por reflexão sobre `Microsoft.Agents.AI` 1.20.0** em 2026-09-08.",
|
|
197
|
-
"provedBy": []
|
|
122
|
+
"digest": "Order of choice first: a TOOL comes before an AIContextProvider — the provider is for cross-cutting context (memory, compaction, tenant) the LLM must not be able to skip. Passive RAG as a provider is the concrete anti-pattern it prevents (a vector search paid on every \"hi\"). Phase plans (instructions + a tool allow-list per funnel stage) are a deterministic C# composer, not a provider — AIContext only ADDS tools, it cannot remove them. AIContext has exactly Instructions/Messages/Tools; AgentRequestMessageSourceType prevents re-injection loops. Same instance invariant as agent-session (ProviderSessionState<TState>). Compaction is [Experimental(\"MAAI001\")] and where you register it changes the result: on the ChatClientBuilder it compacts only the in-flight request; on ChatClientAgentOptions it skips the tool loop and leaks the summary into persisted history. UseAIContextProviders ships in Microsoft.Agents.AI, not .Hosting."
|
|
198
123
|
},
|
|
199
124
|
{
|
|
200
125
|
"id": "ai-agents-conversational-agent-with-phases",
|
|
@@ -211,16 +136,6 @@
|
|
|
211
136
|
"stacks": [
|
|
212
137
|
"dotnet"
|
|
213
138
|
],
|
|
214
|
-
"aliases": [
|
|
215
|
-
"agente conversacional",
|
|
216
|
-
"fases do agente",
|
|
217
|
-
"allow-list de tools",
|
|
218
|
-
"RequireAny",
|
|
219
|
-
"ChatToolMode",
|
|
220
|
-
"compositor de turno",
|
|
221
|
-
"teto de tool calls",
|
|
222
|
-
"turno de conversa"
|
|
223
|
-
],
|
|
224
139
|
"anchors": {
|
|
225
140
|
"quando": "#quando-este-arquétipo",
|
|
226
141
|
"compositor": "#compositor-de-turno-determinístico",
|
|
@@ -230,9 +145,7 @@
|
|
|
230
145
|
"auxiliar": "#agente-auxiliar-em-sequência",
|
|
231
146
|
"checklist": "#checklist-verifiable-in-review"
|
|
232
147
|
},
|
|
233
|
-
"digest": "ONE agent, phase as turn data — the deep dive on the first archetype, and it DECLARES a single field source in its own body. A deterministic turn composer (pure static class, no I/O) yields the turn plan; the prompt is born decomposed into NAMED BLOCKS and the text sent IS the concatenation, derived without cache ON PURPOSE, \"so telemetry cannot lie\" — the invariant that makes a prompt hash worth anything. Allow-list prunes in two stages (phase, then tenant capability), a tool absent from the map is AVAILABLE, and pruning preserves order. Tool-call ceiling with an exempt tool, at agent level (not ChatOptions). ChatToolMode.RequireAny exists (measured by reflection) and is the default when every action is a tool — but it ships with an EXIT CRITERION, not a blind prescription: the field alternative (a second pass) has 7 tests and a \"recovered\" outcome, so migrate when the recovered rate stops paying for the second paid call. An auxiliary agent runs in sequence, never nested inside a tool call."
|
|
234
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08). `ChatToolMode.RequireAny` foi **medido por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0** em 2026-09-08. O restante é **verificação documental**, sem cláusula `provado por` — o ai-kit ainda não traz um compositor de turno, e o padrão é destilado de um único repositório de campo (ver a nota de fonte única abaixo).",
|
|
235
|
-
"provedBy": []
|
|
148
|
+
"digest": "ONE agent, phase as turn data — the deep dive on the first archetype, and it DECLARES a single field source in its own body. A deterministic turn composer (pure static class, no I/O) yields the turn plan; the prompt is born decomposed into NAMED BLOCKS and the text sent IS the concatenation, derived without cache ON PURPOSE, \"so telemetry cannot lie\" — the invariant that makes a prompt hash worth anything. Allow-list prunes in two stages (phase, then tenant capability), a tool absent from the map is AVAILABLE, and pruning preserves order. Tool-call ceiling with an exempt tool, at agent level (not ChatOptions). ChatToolMode.RequireAny exists (measured by reflection) and is the default when every action is a tool — but it ships with an EXIT CRITERION, not a blind prescription: the field alternative (a second pass) has 7 tests and a \"recovered\" outcome, so migrate when the recovered rate stops paying for the second paid call. An auxiliary agent runs in sequence, never nested inside a tool call."
|
|
236
149
|
},
|
|
237
150
|
{
|
|
238
151
|
"id": "ai-agents-cost-and-budget",
|
|
@@ -249,16 +162,6 @@
|
|
|
249
162
|
"stacks": [
|
|
250
163
|
"dotnet"
|
|
251
164
|
],
|
|
252
|
-
"aliases": [
|
|
253
|
-
"custo de llm",
|
|
254
|
-
"cost middleware",
|
|
255
|
-
"UsageCostMiddleware",
|
|
256
|
-
"teto de custo",
|
|
257
|
-
"budget middleware",
|
|
258
|
-
"tarifa por token",
|
|
259
|
-
"cost_usd",
|
|
260
|
-
"orçamento do agente"
|
|
261
|
-
],
|
|
262
165
|
"anchors": {
|
|
263
166
|
"tarifa": "#tarifa-no-registry",
|
|
264
167
|
"middleware": "#o-middleware-de-custo",
|
|
@@ -269,13 +172,7 @@
|
|
|
269
172
|
"fronteira": "#fronteira-com-observability-patterns",
|
|
270
173
|
"checklist": "#checklist-verifiable-in-review"
|
|
271
174
|
},
|
|
272
|
-
"digest": "The only owner of COST as a subject. The SDK gives tokens and does not give money: UsageDetails (measured by reflection on Microsoft.Extensions.AI.Abstractions 10.9.0) exposes Input/Output/Total/Cached/Reasoning counts, the text-audio pairs, AdditionalCounts and Add() — and NO price property. So the tariff lives per alias in the registry, WITH a verification date, and an alias without a tariff must carry a written waiver. One middleware, above function-invocation in the pipeline (measured: above it, Usage already totals every tool round and one line is published per call). Three formula traps: reasoning tokens billed as output, CACHED input is a SUBSET of input (adding them double-counts), and hosted-operation flat fees never appear in UsageDetails. cost_usd is NEVER null: zero with an explicit state — Computed / Waived / UsageMissing. Budget UNIT is a parameter (lead, tenant, operation, day); the measured pain is one of PLACE — a ceiling living inside a tool does not govern the pipeline. Warn before blocking, and the block returns an actionable sentence to the model, never an exception. Two-way delegation with ai-agents-observability-patterns: span attributes stay there, tariff and ceiling live here."
|
|
273
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs`, `.../Middleware/UsageCostRecord.cs` e `.../Providers/ModelAlias.cs` — o middleware, o record de custo não-nulo e a conta de tarifa são o que o kit compila. A ausência de qualquer propriedade de preço em `UsageDetails` foi **medida por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0** em 2026-09-08. O teto de orçamento é **verificação documental** — o ai-kit não traz um middleware de orçamento.",
|
|
274
|
-
"provedBy": [
|
|
275
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs",
|
|
276
|
-
".../Middleware/UsageCostRecord.cs",
|
|
277
|
-
".../Providers/ModelAlias.cs"
|
|
278
|
-
]
|
|
175
|
+
"digest": "The only owner of COST as a subject. The SDK gives tokens and does not give money: UsageDetails (measured by reflection on Microsoft.Extensions.AI.Abstractions 10.9.0) exposes Input/Output/Total/Cached/Reasoning counts, the text-audio pairs, AdditionalCounts and Add() — and NO price property. So the tariff lives per alias in the registry, WITH a verification date, and an alias without a tariff must carry a written waiver. One middleware, above function-invocation in the pipeline (measured: above it, Usage already totals every tool round and one line is published per call). Three formula traps: reasoning tokens billed as output, CACHED input is a SUBSET of input (adding them double-counts), and hosted-operation flat fees never appear in UsageDetails. cost_usd is NEVER null: zero with an explicit state — Computed / Waived / UsageMissing. Budget UNIT is a parameter (lead, tenant, operation, day); the measured pain is one of PLACE — a ceiling living inside a tool does not govern the pipeline. Warn before blocking, and the block returns an actionable sentence to the model, never an exception. Two-way delegation with ai-agents-observability-patterns: span attributes stay there, tariff and ceiling live here."
|
|
279
176
|
},
|
|
280
177
|
{
|
|
281
178
|
"id": "ai-agents-durable-workflows-hitl",
|
|
@@ -292,31 +189,13 @@
|
|
|
292
189
|
"stacks": [
|
|
293
190
|
"dotnet"
|
|
294
191
|
],
|
|
295
|
-
"aliases": [
|
|
296
|
-
"durable workflow",
|
|
297
|
-
"checkpoint",
|
|
298
|
-
"checkpointing",
|
|
299
|
-
"human in the loop",
|
|
300
|
-
"hitl",
|
|
301
|
-
"requestport",
|
|
302
|
-
"requestinfoevent",
|
|
303
|
-
"resume workflow",
|
|
304
|
-
"long running workflow",
|
|
305
|
-
"approval workflow",
|
|
306
|
-
"icheckpointstore",
|
|
307
|
-
"tool approval",
|
|
308
|
-
"ApprovalRequiredAIFunction",
|
|
309
|
-
"ToolApprovalAgent"
|
|
310
|
-
],
|
|
311
192
|
"anchors": {
|
|
312
193
|
"when": "#quando-usar-este-standard",
|
|
313
194
|
"hitl": "#human-in-the-loop-via-requestport",
|
|
314
195
|
"checkpointing": "#checkpointing--durável-sem-depender-de-azure",
|
|
315
196
|
"durable-task": "#azure-durable-task-extension--escape-hatch-not-default"
|
|
316
197
|
},
|
|
317
|
-
"digest": "Two DIFFERENT human-in-the-loop mechanisms, with the boundary made explicit. Single agent with an irreversible tool: ApprovalRequiredAIFunction — no workflow at all. The run ENDS emitting ToolApprovalRequestContent (whose ToolCall is a ToolCallContent), you answer with CreateResponse(approved, reason) IN THE SAME SESSION, and you loop until no pending approval remains. ToolApprovalAgent (stable since 1.14) centralises AutoApprovalRules. Since 1.14 approval responses are BOUND to the request — code written against 1.0 must be reviewed, not just recompiled. Graph with a human step or a multi-day wait: RequestPort + CheckpointManager over a custom ICheckpointStore (no Azure). Durable Task was extracted to a separate repo in 1.17 and lags the framework. Approval is not the right guard when the person on the other end is the interested party — use a deterministic guard."
|
|
318
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `Microsoft.Agents.AI.Workflows` 1.20.0 e `Microsoft.Agents.AI.DurableTask` `1.16.0-preview.260730.1` **não estão no pin** — versões apuradas no nuget.org em 2026-09-08. **Verificação documental**, sem cláusula `provado por`: o ai-kit não exercita workflows nem aprovação de tool. Os nomes de tipo e membros de aprovação foram **medidos por reflexão sobre as assemblies do pin** em 2026-09-08.",
|
|
319
|
-
"provedBy": []
|
|
198
|
+
"digest": "Two DIFFERENT human-in-the-loop mechanisms, with the boundary made explicit. Single agent with an irreversible tool: ApprovalRequiredAIFunction — no workflow at all. The run ENDS emitting ToolApprovalRequestContent (whose ToolCall is a ToolCallContent), you answer with CreateResponse(approved, reason) IN THE SAME SESSION, and you loop until no pending approval remains. ToolApprovalAgent (stable since 1.14) centralises AutoApprovalRules. Since 1.14 approval responses are BOUND to the request — code written against 1.0 must be reviewed, not just recompiled. Graph with a human step or a multi-day wait: RequestPort + CheckpointManager over a custom ICheckpointStore (no Azure). Durable Task was extracted to a separate repo in 1.17 and lags the framework. Approval is not the right guard when the person on the other end is the interested party — use a deterministic guard."
|
|
320
199
|
},
|
|
321
200
|
{
|
|
322
201
|
"id": "ai-agents-evals-with-cache",
|
|
@@ -330,25 +209,6 @@
|
|
|
330
209
|
"stacks": [
|
|
331
210
|
"dotnet"
|
|
332
211
|
],
|
|
333
|
-
"aliases": [
|
|
334
|
-
"eval",
|
|
335
|
-
"evals",
|
|
336
|
-
"eval de modelo",
|
|
337
|
-
"regressao de prompt",
|
|
338
|
-
"prompt regression",
|
|
339
|
-
"model swap",
|
|
340
|
-
"troca de modelo",
|
|
341
|
-
"cache de respostas",
|
|
342
|
-
"response cache",
|
|
343
|
-
"dataset de eval",
|
|
344
|
-
"EvalFixture",
|
|
345
|
-
"OfflineChatClient",
|
|
346
|
-
"DiskBasedResponseCacheProvider",
|
|
347
|
-
"no evals",
|
|
348
|
-
"runner evals",
|
|
349
|
-
"--refresh-cache",
|
|
350
|
-
"evals.watch"
|
|
351
|
-
],
|
|
352
212
|
"anchors": {
|
|
353
213
|
"porque": "#por-que-eval-não-é-teste-unitário",
|
|
354
214
|
"dataset": "#dataset-por-agente-e-por-tenant",
|
|
@@ -362,11 +222,7 @@
|
|
|
362
222
|
"semeadura": "#semeando-a-cache-exemplar-sem-gastar-dinheiro",
|
|
363
223
|
"fronteira": "#fronteira-com-testing-ai"
|
|
364
224
|
},
|
|
365
|
-
"digest": "Eval de modelo e de prompt lendo de uma cache de respostas COMMITADA: roda no CI sem credencial e sem rede, porque no hit da cache o cliente interno nunca e invocado e o cliente interno e um que RECUSA. Owns: dataset versionado por {agente}/{tenant}; o que entra na chave de cache (ProviderName + DefaultModelId; o endpoint NAO entra, apesar de a doc afirmar que entra - medido na 10.9.0); TTL explicito e longo (o default de 14 dias e DESTRUTIVO NA LEITURA e apaga a arvore commitada); o carimbo .morph-eval-cache.json que distingue cache sem carimbo de cache gravada sob outro pin; o que se commita (cache sim, resultados de execucao nao, por construcao); gate duro so em assercao deterministica, juizo de LLM como metrica reportada (2 dos 4 avaliadores de .Quality sao [Experimental] na 10.9.0); o no runner:evals no project.tests[] e o project.evals.watch[] que alimenta a regra de Gate 3; miss de cache reprova nomeado, JAMAIS skip. Fronteira com testing-ai: la o loop do agente contra fake, aqui o modelo real via cache."
|
|
366
|
-
"verifiedAgainst": "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.",
|
|
367
|
-
"provedBy": [
|
|
368
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs"
|
|
369
|
-
]
|
|
225
|
+
"digest": "Eval de modelo e de prompt lendo de uma cache de respostas COMMITADA: roda no CI sem credencial e sem rede, porque no hit da cache o cliente interno nunca e invocado e o cliente interno e um que RECUSA. Owns: dataset versionado por {agente}/{tenant}; o que entra na chave de cache (ProviderName + DefaultModelId; o endpoint NAO entra, apesar de a doc afirmar que entra - medido na 10.9.0); TTL explicito e longo (o default de 14 dias e DESTRUTIVO NA LEITURA e apaga a arvore commitada); o carimbo .morph-eval-cache.json que distingue cache sem carimbo de cache gravada sob outro pin; o que se commita (cache sim, resultados de execucao nao, por construcao); gate duro so em assercao deterministica, juizo de LLM como metrica reportada (2 dos 4 avaliadores de .Quality sao [Experimental] na 10.9.0); o no runner:evals no project.tests[] e o project.evals.watch[] que alimenta a regra de Gate 3; miss de cache reprova nomeado, JAMAIS skip. Fronteira com testing-ai: la o loop do agente contra fake, aqui o modelo real via cache."
|
|
370
226
|
},
|
|
371
227
|
{
|
|
372
228
|
"id": "ai-agents-guardrails",
|
|
@@ -383,15 +239,6 @@
|
|
|
383
239
|
"stacks": [
|
|
384
240
|
"dotnet"
|
|
385
241
|
],
|
|
386
|
-
"aliases": [
|
|
387
|
-
"guardrail",
|
|
388
|
-
"guard de tool",
|
|
389
|
-
"regra proibida",
|
|
390
|
-
"validacao de pertinencia",
|
|
391
|
-
"fail-open guard",
|
|
392
|
-
"argumento nao confiavel",
|
|
393
|
-
"choke-point"
|
|
394
|
-
],
|
|
395
242
|
"anchors": {
|
|
396
243
|
"codigo": "#regra-proibida-vira-código",
|
|
397
244
|
"chokepoint": "#onde-fica-o-choke-point",
|
|
@@ -400,9 +247,7 @@
|
|
|
400
247
|
"failopen": "#quando-o-guard-não-consegue-decidir",
|
|
401
248
|
"checklist": "#checklist-verifiable-in-review"
|
|
402
249
|
},
|
|
403
|
-
"digest": "What the CODE guarantees once the prompt has already failed. A rule with real consequence leaves the prompt and becomes a C# condition — the measured reason, written in field code: the model obeys \"one entry per element of this list\" but errs badly when it must judge on its own which blocks have data (10 expected, 2 delivered). Three legitimate choke-points (before the turn in the composer; in the function-invocation middleware, with a CLOSED outcome vocabulary ok/error/invalid_args/limit/unavailable and required arguments read from the function JSON schema; after the run as backstop). Tool arguments are UNTRUSTED input: ids and money come from the turn closure, never from the model. Relevance, not presence: compare against something that did NOT come from the model — a deterministic rubric checked BY EQUALITY against the backend numbers, a provenance lock that demotes an unsupported \"found\" while PRESERVING the reasoning for audit, and a per-source confidence floor. When the guard cannot decide the answer is FAIL-OPEN, ratified with one hard condition: the escape is RECORDED (the anti-pattern is 81 of 83 catch blocks that never read the exception). Verdict is ternary — pass / reject / not_evaluated — because a boolean turns \"could not evaluate\" into \"approved\"."
|
|
404
|
-
"verifiedAgainst": "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.",
|
|
405
|
-
"provedBy": []
|
|
250
|
+
"digest": "What the CODE guarantees once the prompt has already failed. A rule with real consequence leaves the prompt and becomes a C# condition — the measured reason, written in field code: the model obeys \"one entry per element of this list\" but errs badly when it must judge on its own which blocks have data (10 expected, 2 delivered). Three legitimate choke-points (before the turn in the composer; in the function-invocation middleware, with a CLOSED outcome vocabulary ok/error/invalid_args/limit/unavailable and required arguments read from the function JSON schema; after the run as backstop). Tool arguments are UNTRUSTED input: ids and money come from the turn closure, never from the model. Relevance, not presence: compare against something that did NOT come from the model — a deterministic rubric checked BY EQUALITY against the backend numbers, a provenance lock that demotes an unsupported \"found\" while PRESERVING the reasoning for audit, and a per-source confidence floor. When the guard cannot decide the answer is FAIL-OPEN, ratified with one hard condition: the escape is RECORDED (the anti-pattern is 81 of 83 catch blocks that never read the exception). Verdict is ternary — pass / reject / not_evaluated — because a boolean turns \"could not evaluate\" into \"approved\"."
|
|
406
251
|
},
|
|
407
252
|
{
|
|
408
253
|
"id": "ai-agents-llm-runtime-defaults",
|
|
@@ -419,16 +264,6 @@
|
|
|
419
264
|
"stacks": [
|
|
420
265
|
"dotnet"
|
|
421
266
|
],
|
|
422
|
-
"aliases": [
|
|
423
|
-
"llm defaults",
|
|
424
|
-
"runtime defaults",
|
|
425
|
-
"timeout llm",
|
|
426
|
-
"retry llm",
|
|
427
|
-
"strict json schema",
|
|
428
|
-
"temperature reasoning",
|
|
429
|
-
"capability por alias",
|
|
430
|
-
"network timeout"
|
|
431
|
-
],
|
|
432
267
|
"anchors": {
|
|
433
268
|
"fronteira": "#fronteira-com-o-model-registry",
|
|
434
269
|
"retry": "#retry-herdado",
|
|
@@ -438,14 +273,7 @@
|
|
|
438
273
|
"pin": "#pin-de-sdk-e-compatibilidade-binária",
|
|
439
274
|
"checklist": "#checklist-verifiable-in-review"
|
|
440
275
|
},
|
|
441
|
-
"digest": "The runtime KNOBS of a model call, with the boundary against ai-agents-providers-model-registry (Layer 0, which owns the SCHEMA) written as the first section. Retry is INHERITED from the System.ClientModel pipeline — do not stack a loop on top (3 x 3 = 9 paid calls). Timeout is NOT inherited and must be created: ChatOptions has no timeout property (measured by reflection) and OpenAIClientOptions declares only Endpoint/OrganizationId/ProjectId/UserAgentApplicationId — the knob comes from the ClientPipelineOptions base. A CancellationToken is not a timeout. StrictJsonSchema DOES NOT EXIST: zero exported types containing \"Strict\" across the 14 pinned assemblies, and ChatResponseFormatJson has no strict field; the real mechanism is a key in ChatOptions.AdditionalProperties, so strict is per alias and NEVER a process-global flag. ReasoningEffort is a first-class enum (None/Low/Medium/High/ExtraHigh). Pin AI packages exactly — a floating 2.* range cost a MissingMethodException after the call was already paid for."
|
|
442
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 + OpenAI 2.13.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs` e `.../Providers/ModelRegistry.cs` (alias, `api:`, e limite por alias com procedência obrigatória). A superfície do SDK citada aqui — `ChatOptions`, `ChatResponseFormatJson`, `ReasoningEffort`, `OpenAIClientOptions` — foi **medida por reflexão sobre as assemblies do pin** em 2026-09-08. O timeout é **provado por** `.../Agents/AgentSpecOptions.cs` e `.../Agents/AgentFactory.cs`, que o kit compila **por spec** (`TimeoutSeconds` vira um cliente de timeout no pipeline) — e **não** por alias: a allowlist de alias do registry não tem chave de timeout (medido). Os valores default do retry do `System.ClientModel` 1.15.0 são **verificação documental** — essa assembly está fora do conjunto que o probe carrega.",
|
|
443
|
-
"provedBy": [
|
|
444
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs",
|
|
445
|
-
".../Providers/ModelRegistry.cs",
|
|
446
|
-
".../Agents/AgentSpecOptions.cs",
|
|
447
|
-
".../Agents/AgentFactory.cs"
|
|
448
|
-
]
|
|
276
|
+
"digest": "The runtime KNOBS of a model call, with the boundary against ai-agents-providers-model-registry (Layer 0, which owns the SCHEMA) written as the first section. Retry is INHERITED from the System.ClientModel pipeline — do not stack a loop on top (3 x 3 = 9 paid calls). Timeout is NOT inherited and must be created: ChatOptions has no timeout property (measured by reflection) and OpenAIClientOptions declares only Endpoint/OrganizationId/ProjectId/UserAgentApplicationId — the knob comes from the ClientPipelineOptions base. A CancellationToken is not a timeout. StrictJsonSchema DOES NOT EXIST: zero exported types containing \"Strict\" across the 14 pinned assemblies, and ChatResponseFormatJson has no strict field; the real mechanism is a key in ChatOptions.AdditionalProperties, so strict is per alias and NEVER a process-global flag. ReasoningEffort is a first-class enum (None/Low/Medium/High/ExtraHigh). Pin AI packages exactly — a floating 2.* range cost a MissingMethodException after the call was already paid for."
|
|
449
277
|
},
|
|
450
278
|
{
|
|
451
279
|
"id": "ai-agents-mcp-server",
|
|
@@ -462,19 +290,6 @@
|
|
|
462
290
|
"stacks": [
|
|
463
291
|
"dotnet"
|
|
464
292
|
],
|
|
465
|
-
"aliases": [
|
|
466
|
-
"mcp server",
|
|
467
|
-
"expose mcp",
|
|
468
|
-
"build mcp server",
|
|
469
|
-
"mcp host",
|
|
470
|
-
"publish tools",
|
|
471
|
-
"create mcp server",
|
|
472
|
-
"ModelContextProtocol server",
|
|
473
|
-
"request filters",
|
|
474
|
-
"AddCallToolFilter",
|
|
475
|
-
"stateless",
|
|
476
|
-
"spec 2026-07-28"
|
|
477
|
-
],
|
|
478
293
|
"anchors": {
|
|
479
294
|
"when": "#quando-criar-um-mcp-server",
|
|
480
295
|
"api": "#a-api",
|
|
@@ -482,9 +297,7 @@
|
|
|
482
297
|
"http": "#exemplo-c--transport-http",
|
|
483
298
|
"security": "#segurança"
|
|
484
299
|
},
|
|
485
|
-
"digest": "Expose project capabilities as an MCP server. There is NO spec revision called \"MCP 2.0\": the spec is dated (current revision 2026-07-28) and 2.x is the C# SDK major (ModelContextProtocol 2.2.0). That revision removed sessions and Mcp-Session-Id (SEP-2567), removed initialize (version/capabilities travel in _meta per request, SEP-2575), deprecated Roots/Sampling/Logging (SEP-2577), moved Tasks to an extension, replaced elicitation/sampling with MRTR, and deprecated HTTP+SSE. The C# server API kept its names (AddMcpServer, WithStdioServerTransport, WithHttpTransport, [McpServerTool]) but Stateless is now the DEFAULT, not a choice. Multi-tenant needs TWO request filters: AddListToolsFilter hides a tool, only AddCallToolFilter stops the call — and a connected client never re-reads the list. Identity per request via context.User; tool arguments are untrusted input and tenant ids come from the credential, never from the model."
|
|
486
|
-
"verifiedAgainst": "ModelContextProtocol 2.2.0 + ModelContextProtocol.AspNetCore 2.2.0 (ai-pin 2026-09-08; o `.AspNetCore` não está no pin — versão apurada no nuget.org em 2026-09-08) + revisão de spec MCP **2026-07-28** (modelcontextprotocol.io/specification/versioning, lido 2026-09-08). **Verificação documental — o ai-kit não exercita MCP**, e por isso este standard NÃO carimba `provado por`. Os nomes de tipo, os defaults e os avisos de obsolescência abaixo foram **medidos por reflexão sobre as assemblies 2.2.0** em 2026-09-08.",
|
|
487
|
-
"provedBy": []
|
|
300
|
+
"digest": "Expose project capabilities as an MCP server. There is NO spec revision called \"MCP 2.0\": the spec is dated (current revision 2026-07-28) and 2.x is the C# SDK major (ModelContextProtocol 2.2.0). That revision removed sessions and Mcp-Session-Id (SEP-2567), removed initialize (version/capabilities travel in _meta per request, SEP-2575), deprecated Roots/Sampling/Logging (SEP-2577), moved Tasks to an extension, replaced elicitation/sampling with MRTR, and deprecated HTTP+SSE. The C# server API kept its names (AddMcpServer, WithStdioServerTransport, WithHttpTransport, [McpServerTool]) but Stateless is now the DEFAULT, not a choice. Multi-tenant needs TWO request filters: AddListToolsFilter hides a tool, only AddCallToolFilter stops the call — and a connected client never re-reads the list. Identity per request via context.User; tool arguments are untrusted input and tenant ids come from the credential, never from the model."
|
|
488
301
|
},
|
|
489
302
|
{
|
|
490
303
|
"id": "ai-agents-mcp-tools",
|
|
@@ -501,27 +314,13 @@
|
|
|
501
314
|
"stacks": [
|
|
502
315
|
"dotnet"
|
|
503
316
|
],
|
|
504
|
-
"aliases": [
|
|
505
|
-
"mcp",
|
|
506
|
-
"model context protocol",
|
|
507
|
-
"mcp client",
|
|
508
|
-
"consume mcp server",
|
|
509
|
-
"mcp tools",
|
|
510
|
-
"external tools",
|
|
511
|
-
"agent mcp client",
|
|
512
|
-
"McpClient.CreateAsync",
|
|
513
|
-
"HttpClientTransport",
|
|
514
|
-
"MRTR"
|
|
515
|
-
],
|
|
516
317
|
"anchors": {
|
|
517
318
|
"what": "#o-que-é-no-contexto-de-um-agente-maf",
|
|
518
319
|
"api": "#a-api",
|
|
519
320
|
"example": "#exemplo-c",
|
|
520
321
|
"comparison": "#mcp-server-vs-tool-nativa"
|
|
521
322
|
},
|
|
522
|
-
"digest": "A MAF agent consuming external MCP servers, against ModelContextProtocol 2.2.0 (out of preview). Renames that break old code: McpClientFactory.CreateAsync became McpClient.CreateAsync, and SseClientTransport became HttpClientTransport with TransportMode = HttpTransportMode.Sse — neither old name exists in 2.2.0. McpClientTool derives from AIFunction, so .Cast<AITool>() is unnecessary. ElicitAsync/SampleAsync/RequestRootsAsync need a persistent session and fail against a stateless server; the cross-configuration path is MRTR (InputRequiredException + InputRequest.ForElicitation). Long-running Tasks moved to ModelContextProtocol.Extensions.Tasks with NO wire compatibility with the 1.3/1.4 implementation. Every discovered tool is re-sent in the definition on each turn — prune."
|
|
523
|
-
"verifiedAgainst": "ModelContextProtocol 2.2.0 + Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08) + revisão de spec MCP **2026-07-28**. **Verificação documental — o ai-kit não exercita MCP**, e por isso este standard NÃO carimba `provado por`. Todos os nomes de tipo abaixo foram **medidos por reflexão sobre `ModelContextProtocol.Core` 2.2.0** em 2026-09-08.",
|
|
524
|
-
"provedBy": []
|
|
323
|
+
"digest": "A MAF agent consuming external MCP servers, against ModelContextProtocol 2.2.0 (out of preview). Renames that break old code: McpClientFactory.CreateAsync became McpClient.CreateAsync, and SseClientTransport became HttpClientTransport with TransportMode = HttpTransportMode.Sse — neither old name exists in 2.2.0. McpClientTool derives from AIFunction, so .Cast<AITool>() is unnecessary. ElicitAsync/SampleAsync/RequestRootsAsync need a persistent session and fail against a stateless server; the cross-configuration path is MRTR (InputRequiredException + InputRequest.ForElicitation). Long-running Tasks moved to ModelContextProtocol.Extensions.Tasks with NO wire compatibility with the 1.3/1.4 implementation. Every discovered tool is re-sent in the definition on each turn — prune."
|
|
525
324
|
},
|
|
526
325
|
{
|
|
527
326
|
"id": "ai-agents-media-pipeline",
|
|
@@ -539,17 +338,6 @@
|
|
|
539
338
|
"stacks": [
|
|
540
339
|
"dotnet"
|
|
541
340
|
],
|
|
542
|
-
"aliases": [
|
|
543
|
-
"media pipeline",
|
|
544
|
-
"media job",
|
|
545
|
-
"media review",
|
|
546
|
-
"C2PA",
|
|
547
|
-
"SynthID",
|
|
548
|
-
"AI Act",
|
|
549
|
-
"content provenance",
|
|
550
|
-
"marcação de IA",
|
|
551
|
-
"idempotência de mídia"
|
|
552
|
-
],
|
|
553
341
|
"anchors": {
|
|
554
342
|
"hard-rule": "#a-regra-dura",
|
|
555
343
|
"states": "#a-máquina-de-estados",
|
|
@@ -563,11 +351,7 @@
|
|
|
563
351
|
"tests": "#testes",
|
|
564
352
|
"reverify": "#como-re-verificar-este-standard"
|
|
565
353
|
},
|
|
566
|
-
"digest": "Onde mora a regra dura: NUNCA gerar midia dentro de um turno de conversa (WhatsApp, chat, webhook sincrono) — latencia documentada de ate 2 min em imagem e de 11 s a 6 min em video, custo por chamada, e zero revisao possivel dentro do turno. A regra e presa por TIPO e nao por prosa: o que produz bytes e internal ao assembly, a superficie publica devolve id de job. O limite do tipo esta declarado: ele fecha \"obter bytes\" e \"nomear o produtor\", NAO fecha \"aguardar inline\" — o gatilho do worker (IMediaJobRunner.RunAsync) e publico por necessidade, e um await dele dentro de um handler de turno compila e paga a latencia; quem fecha essa porta e a revisao de codigo. Maquina de estados pending/generating/generated/approved|rejected|rejected_by_policy/published + failed, onde bloqueio de moderacao e terminal e NAO retryable. DUAS chaves de idempotencia, distintas de proposito: a de PEDIDO (sha256 de provider+model+prompt+refs+opcoes) dedupe a chamada paga; a de CONTEUDO (sha256 dos bytes) dedupe o storage. Bytes no blob proprio porque URL de provedor expira; banco com caminho relativo + sha256. Proveniencia com nove campos e C2PA preservado sem re-encode. C2PA e SynthID vem embutidos e NAO bastam (caem com screenshot/re-encode; not_detected nao significa humano), entao a marcacao visivel e obrigacao separada no ponto de publicacao — EU AI Act art. 50, com a ressalva de fonte declarada. E ela e presa por TIPO, nao por prosa: Publish tem TRES portas (estado Approved; finalidade publicavel; marcacao presente quando a finalidade a exige) e recebe a marcacao como parametro, com texto E superficie obrigatorios na construcao. O limite e declarado: prova que a decisao foi gravada, nao que o rotulo chegou aos olhos de alguem — renderizar e do aplicativo consumidor. Approve exige humano com carimbo; juiz LLM e pre-filtro. Teto de custo por tenant/dia recusa no enqueue. O mecanismo de job NAO e reescrito: cita backend/integrations/hangfire/hangfire-jobs.md, e durable-workflows-hitl e a outra coisa (HITL dentro de um Workflow MAF)."
|
|
567
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + OpenAI 2.13.0 + Google.GenAI 1.21.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Media/` — a máquina de estados (`MediaJob`), a revisão humana (`MediaReview`), a marcação exigida na publicação (`MediaDisclosure` + `MediaJob.Publish`), as duas chaves (`MediaIdempotencyKey` × `MediaHash`), o storage por conteúdo (`MediaStore`) e a acessibilidade `internal` de `IMediaProvider` compilam e são exercitados por teste. Preço, data de desligamento, C2PA/SynthID e o texto legal do AI Act são **verificação documental** contra as páginas de §Como re-verificar este standard.",
|
|
568
|
-
"provedBy": [
|
|
569
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Media/"
|
|
570
|
-
]
|
|
354
|
+
"digest": "Onde mora a regra dura: NUNCA gerar midia dentro de um turno de conversa (WhatsApp, chat, webhook sincrono) — latencia documentada de ate 2 min em imagem e de 11 s a 6 min em video, custo por chamada, e zero revisao possivel dentro do turno. A regra e presa por TIPO e nao por prosa: o que produz bytes e internal ao assembly, a superficie publica devolve id de job. O limite do tipo esta declarado: ele fecha \"obter bytes\" e \"nomear o produtor\", NAO fecha \"aguardar inline\" — o gatilho do worker (IMediaJobRunner.RunAsync) e publico por necessidade, e um await dele dentro de um handler de turno compila e paga a latencia; quem fecha essa porta e a revisao de codigo. Maquina de estados pending/generating/generated/approved|rejected|rejected_by_policy/published + failed, onde bloqueio de moderacao e terminal e NAO retryable. DUAS chaves de idempotencia, distintas de proposito: a de PEDIDO (sha256 de provider+model+prompt+refs+opcoes) dedupe a chamada paga; a de CONTEUDO (sha256 dos bytes) dedupe o storage. Bytes no blob proprio porque URL de provedor expira; banco com caminho relativo + sha256. Proveniencia com nove campos e C2PA preservado sem re-encode. C2PA e SynthID vem embutidos e NAO bastam (caem com screenshot/re-encode; not_detected nao significa humano), entao a marcacao visivel e obrigacao separada no ponto de publicacao — EU AI Act art. 50, com a ressalva de fonte declarada. E ela e presa por TIPO, nao por prosa: Publish tem TRES portas (estado Approved; finalidade publicavel; marcacao presente quando a finalidade a exige) e recebe a marcacao como parametro, com texto E superficie obrigatorios na construcao. O limite e declarado: prova que a decisao foi gravada, nao que o rotulo chegou aos olhos de alguem — renderizar e do aplicativo consumidor. Approve exige humano com carimbo; juiz LLM e pre-filtro. Teto de custo por tenant/dia recusa no enqueue. O mecanismo de job NAO e reescrito: cita backend/integrations/hangfire/hangfire-jobs.md, e durable-workflows-hitl e a outra coisa (HITL dentro de um Workflow MAF)."
|
|
571
355
|
},
|
|
572
356
|
{
|
|
573
357
|
"id": "ai-agents-media-video",
|
|
@@ -585,17 +369,6 @@
|
|
|
585
369
|
"stacks": [
|
|
586
370
|
"dotnet"
|
|
587
371
|
],
|
|
588
|
-
"aliases": [
|
|
589
|
-
"media video",
|
|
590
|
-
"video generation",
|
|
591
|
-
"text to video",
|
|
592
|
-
"image to video",
|
|
593
|
-
"veo",
|
|
594
|
-
"seedance",
|
|
595
|
-
"kling",
|
|
596
|
-
"runway",
|
|
597
|
-
"GenerateVideosAsync"
|
|
598
|
-
],
|
|
599
372
|
"anchors": {
|
|
600
373
|
"when": "#quando-usar",
|
|
601
374
|
"sdk": "#o-caminho-com-sdk",
|
|
@@ -608,11 +381,7 @@
|
|
|
608
381
|
"deprecations": "#deprecações",
|
|
609
382
|
"reverify": "#como-re-verificar-este-standard"
|
|
610
383
|
},
|
|
611
|
-
"digest": "Geracao de VIDEO em runtime a partir de .NET. A fronteira esta na primeira linha: footage de LP scroll-driven por MCP em tempo de autoria e frontend/design-system/ai-video-generation.md; aqui e o worker do backend. Veo 3.1 via Google.GenAI 1.21.0 (GenerateVideosAsync + Operations.GetAsync em polling ~10 s) e o UNICO caminho com SDK .NET oficial depois do desligamento da Sora — sora-2, sora-2-pro e o VideoClient do openai-dotnet saem sem substituto, entao nao se escreve adaptador para eles. Seedance 2.0, Kling e Runway sao HttpClient tipado com DTOs proprios: criar tarefa, PERSISTIR o id antes de qualquer outra coisa, polling com back-off ou webhook, baixar, gravar. O arquivo do Veo expira em 2 dias, o que torna o download obrigatorio e nao otimizacao. Bloqueio de moderacao NAO cobra e e estado terminal (rejected_by_policy), nunca erro retryable. 8 s e obrigatorio no Veo para 1080p, 4K e imagens de referencia. Nenhum alias default e pinado para Seedance/Kling/Runway porque a faixa de preco medida varia 20x entre canais. Preco em #custo-por-segundo, datas em #deprecacoes."
|
|
612
|
-
"verifiedAgainst": "Google.GenAI 1.21.0 + OpenAI 2.13.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs` — `Models.GenerateVideosAsync(model, GenerateVideosSource, GenerateVideosConfig, ct)`, o polling por `Operations.GetAsync` + `operation.Done` e o sinal de bloqueio `RaiMediaFilteredCount` COMPILAM ali. Preço, duração, retenção e data de desligamento são **verificação documental** contra as páginas listadas em §Como re-verificar este standard.",
|
|
613
|
-
"provedBy": [
|
|
614
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs"
|
|
615
|
-
]
|
|
384
|
+
"digest": "Geracao de VIDEO em runtime a partir de .NET. A fronteira esta na primeira linha: footage de LP scroll-driven por MCP em tempo de autoria e frontend/design-system/ai-video-generation.md; aqui e o worker do backend. Veo 3.1 via Google.GenAI 1.21.0 (GenerateVideosAsync + Operations.GetAsync em polling ~10 s) e o UNICO caminho com SDK .NET oficial depois do desligamento da Sora — sora-2, sora-2-pro e o VideoClient do openai-dotnet saem sem substituto, entao nao se escreve adaptador para eles. Seedance 2.0, Kling e Runway sao HttpClient tipado com DTOs proprios: criar tarefa, PERSISTIR o id antes de qualquer outra coisa, polling com back-off ou webhook, baixar, gravar. O arquivo do Veo expira em 2 dias, o que torna o download obrigatorio e nao otimizacao. Bloqueio de moderacao NAO cobra e e estado terminal (rejected_by_policy), nunca erro retryable. 8 s e obrigatorio no Veo para 1080p, 4K e imagens de referencia. Nenhum alias default e pinado para Seedance/Kling/Runway porque a faixa de preco medida varia 20x entre canais. Preco em #custo-por-segundo, datas em #deprecacoes."
|
|
616
385
|
},
|
|
617
386
|
{
|
|
618
387
|
"id": "ai-agents-middleware-patterns",
|
|
@@ -628,28 +397,12 @@
|
|
|
628
397
|
"stacks": [
|
|
629
398
|
"dotnet"
|
|
630
399
|
],
|
|
631
|
-
"aliases": [
|
|
632
|
-
"middleware",
|
|
633
|
-
"agent middleware",
|
|
634
|
-
"function calling middleware",
|
|
635
|
-
"retry",
|
|
636
|
-
"anti-hallucination",
|
|
637
|
-
"resilience",
|
|
638
|
-
"telemetry middleware",
|
|
639
|
-
"agent run middleware",
|
|
640
|
-
"pipeline order",
|
|
641
|
-
"streaming downgrade"
|
|
642
|
-
],
|
|
643
400
|
"anchors": {
|
|
644
401
|
"levels": "#os-3-níveis-de-middleware",
|
|
645
402
|
"resilience": "#pattern-resilência-retry--rate-limit",
|
|
646
403
|
"anti-hallucination": "#pattern-anti-hallucination"
|
|
647
404
|
},
|
|
648
|
-
"digest": "The three middleware levels (Agent Run, Function Calling, IChatClient) and where each one belongs. Registration is always .AsBuilder().Use(...).Build(); in a ChatClientBuilder the FIRST registered is the OUTERMOST. Pipeline ORDER is measured, not folklore: timeout -> usage-cost -> FunctionInvokingChatClient -> chat telemetry -> provider client. Usage-cost sits ABOVE the FICC because below it would read the same UsageDetails the FICC mutates to accumulate; chat telemetry sits BELOW it so the chat span closes before the tools. Supplying only the non-streaming delegate silently downgrades streaming calls — use Use(sharedFunc) when the middleware does not care. Patterns: telemetry, resilience (retry + rate limit), anti-hallucination."
|
|
649
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `provado por templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` para a **ordem do pipeline** — a fábrica do kit é o único lugar que conhece essa ordem, e ela é medida por teste, não herdada de folclore. As assinaturas de `Use(...)` foram **medidas por reflexão sobre `AIAgentBuilder` e `ChatClientBuilder`** em 2026-09-08.",
|
|
650
|
-
"provedBy": [
|
|
651
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs"
|
|
652
|
-
]
|
|
405
|
+
"digest": "The three middleware levels (Agent Run, Function Calling, IChatClient) and where each one belongs. Registration is always .AsBuilder().Use(...).Build(); in a ChatClientBuilder the FIRST registered is the OUTERMOST. Pipeline ORDER is measured, not folklore: timeout -> usage-cost -> FunctionInvokingChatClient -> chat telemetry -> provider client. Usage-cost sits ABOVE the FICC because below it would read the same UsageDetails the FICC mutates to accumulate; chat telemetry sits BELOW it so the chat span closes before the tools. Supplying only the non-streaming delegate silently downgrades streaming calls — use Use(sharedFunc) when the middleware does not care. Patterns: telemetry, resilience (retry + rate limit), anti-hallucination."
|
|
653
406
|
},
|
|
654
407
|
{
|
|
655
408
|
"id": "ai-agents-modalities-image-gen",
|
|
@@ -666,29 +419,13 @@
|
|
|
666
419
|
"stacks": [
|
|
667
420
|
"dotnet"
|
|
668
421
|
],
|
|
669
|
-
"aliases": [
|
|
670
|
-
"image generation",
|
|
671
|
-
"text to image",
|
|
672
|
-
"dall-e",
|
|
673
|
-
"gpt-image",
|
|
674
|
-
"mockup",
|
|
675
|
-
"image synthesis",
|
|
676
|
-
"generate image",
|
|
677
|
-
"image from text",
|
|
678
|
-
"MEAI001",
|
|
679
|
-
"IImageGenerator"
|
|
680
|
-
],
|
|
681
422
|
"anchors": {
|
|
682
423
|
"when": "#quando-usar-geração-de-imagem",
|
|
683
424
|
"api": "#a-api",
|
|
684
425
|
"example": "#exemplo-c-completo",
|
|
685
426
|
"params": "#parâmetros"
|
|
686
427
|
},
|
|
687
|
-
"digest": "Text-to-image with the OpenAI SDK: ImageClient.GenerateImageAsync(prompt, options, ct) — signature confirmed in OpenAI 2.13.0, alongside GenerateImagesAsync, GenerateImageEditAsync and GenerateImageVariationAsync. The abstraction path (IImageGenerator, AsIImageGenerator, ImageGeneratorBuilder) is [Experimental(\"MEAI001\")] and needs the diagnostic suppressed; the concrete ImageClient does not. HostedImageGenerationTool lets the MODEL decide to generate, and is documented as mapping to the Responses API — that mapping is internal and was not confirmable by reflection. There is no GetImageClient(alias) in the standard ModelRegistry; register the ImageClient yourself."
|
|
688
|
-
"verifiedAgainst": "OpenAI 2.13.0 + Microsoft.Extensions.AI 10.9.0 + Google.GenAI 1.21.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs` — `IImageGenerator`, `MEAI001`, `ImageGenerationRequest.OriginalImages` e a ausência de propriedade tipada para `quality` COMPILAM ali, e `OPENAI001` em `GeneratedImageQuality` foi medido pelo compilador. `HostedImageGenerationTool` foi medido por reflexão sobre a assembly do pin (existe, não é experimental); preço, data de desligamento e limites de referência são **verificação documental** contra as páginas de §Como re-verificar este standard.",
|
|
689
|
-
"provedBy": [
|
|
690
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs"
|
|
691
|
-
]
|
|
428
|
+
"digest": "Text-to-image with the OpenAI SDK: ImageClient.GenerateImageAsync(prompt, options, ct) — signature confirmed in OpenAI 2.13.0, alongside GenerateImagesAsync, GenerateImageEditAsync and GenerateImageVariationAsync. The abstraction path (IImageGenerator, AsIImageGenerator, ImageGeneratorBuilder) is [Experimental(\"MEAI001\")] and needs the diagnostic suppressed; the concrete ImageClient does not. HostedImageGenerationTool lets the MODEL decide to generate, and is documented as mapping to the Responses API — that mapping is internal and was not confirmable by reflection. There is no GetImageClient(alias) in the standard ModelRegistry; register the ImageClient yourself."
|
|
692
429
|
},
|
|
693
430
|
{
|
|
694
431
|
"id": "ai-agents-modalities-speech-to-text",
|
|
@@ -706,24 +443,13 @@
|
|
|
706
443
|
"stacks": [
|
|
707
444
|
"dotnet"
|
|
708
445
|
],
|
|
709
|
-
"aliases": [
|
|
710
|
-
"speech to text",
|
|
711
|
-
"transcription",
|
|
712
|
-
"whisper",
|
|
713
|
-
"audio to text",
|
|
714
|
-
"voice transcription",
|
|
715
|
-
"STT",
|
|
716
|
-
"audio transcription"
|
|
717
|
-
],
|
|
718
446
|
"anchors": {
|
|
719
447
|
"when": "#quando-usar-stt",
|
|
720
448
|
"api": "#a-api",
|
|
721
449
|
"example": "#exemplo-c-completo",
|
|
722
450
|
"formats": "#formatos-e-limites"
|
|
723
451
|
},
|
|
724
|
-
"digest": "Use STT
|
|
725
|
-
"verifiedAgainst": "OpenAI 2.13.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 transcreve áudio. `AudioClient.TranscribeAudioAsync` e a ponte `AsISpeechToTextClient` foram **medidas por reflexão sobre as assemblies do pin** em 2026-09-08.",
|
|
726
|
-
"provedBy": []
|
|
452
|
+
"digest": "Use STT só quando o input real é áudio; se já é texto, use-o direto. Transcrição é chamada de serviço, não agente: registre o `AudioClient` (OpenAI 2.13.0) UMA vez no DI com modelo (`whisper-1`) e API key vindos de config e injete-o pelo construtor do `TranscriptionService` (nunca `new AudioClient` no chamador), e passe o texto como `string` ao `AIAgent` downstream; NÃO crie `AIAgent` para transcrever. API: `TranscribeAudioAsync(path, options)` NÃO aceita `CancellationToken` — para cancelar, `File.OpenRead` e a sobrecarga `(Stream, fileName, options, ct)`, que exige o nome do arquivo (dele se deduz o formato); `ResponseFormat = Verbose` com `TimestampGranularities = AudioTimestampGranularities.Word | Segment` (enum de flags, não coleção) dá timestamps. Limites: mp3/mp4/mpeg/mpga/m4a/wav/webm, 25 MB por request (acima, erro 413): divida client-side em chunks de ≤20 MB, com alguns segundos de overlap se o corte for crítico. Defina `Language = \"pt\"` quando o idioma é conhecido. Opcional: `ISpeechToTextClient` via `AsISpeechToTextClient()`."
|
|
727
453
|
},
|
|
728
454
|
{
|
|
729
455
|
"id": "ai-agents-modalities-text-to-speech",
|
|
@@ -741,24 +467,13 @@
|
|
|
741
467
|
"stacks": [
|
|
742
468
|
"dotnet"
|
|
743
469
|
],
|
|
744
|
-
"aliases": [
|
|
745
|
-
"text to speech",
|
|
746
|
-
"TTS",
|
|
747
|
-
"voice synthesis",
|
|
748
|
-
"audio generation",
|
|
749
|
-
"speech synthesis",
|
|
750
|
-
"voice output",
|
|
751
|
-
"speak text"
|
|
752
|
-
],
|
|
753
470
|
"anchors": {
|
|
754
471
|
"when": "#quando-usar-tts",
|
|
755
472
|
"api": "#a-api",
|
|
756
473
|
"example": "#exemplo-c-completo",
|
|
757
474
|
"params": "#parâmetros"
|
|
758
475
|
},
|
|
759
|
-
"digest": "Use TTS when the agent must return audio output — voice assistants, accessibility narration, podcast generation. MAF uses AudioClient for text-to-speech with configurable voice, speed, and output format (mp3, opus, flac). Covers streaming audio response, voice selection, and integration with blob storage for async delivery. Anti-patterns: synthesizing long texts synchronously in a request handler."
|
|
760
|
-
"verifiedAgainst": "OpenAI 2.13.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 gera áudio. `AudioClient.GenerateSpeechAsync`, a sobrecarga de streaming e a ponte `AsITextToSpeechClient` foram **medidas por reflexão sobre as assemblies do pin** em 2026-09-08.",
|
|
761
|
-
"provedBy": []
|
|
476
|
+
"digest": "Use TTS when the agent must return audio output — voice assistants, accessibility narration, podcast generation. MAF uses AudioClient for text-to-speech with configurable voice, speed, and output format (mp3, opus, flac). Covers streaming audio response, voice selection, and integration with blob storage for async delivery. Anti-patterns: synthesizing long texts synchronously in a request handler."
|
|
762
477
|
},
|
|
763
478
|
{
|
|
764
479
|
"id": "ai-agents-modalities-vision",
|
|
@@ -775,25 +490,13 @@
|
|
|
775
490
|
"stacks": [
|
|
776
491
|
"dotnet"
|
|
777
492
|
],
|
|
778
|
-
"aliases": [
|
|
779
|
-
"vision",
|
|
780
|
-
"image to text",
|
|
781
|
-
"OCR",
|
|
782
|
-
"image analysis",
|
|
783
|
-
"multimodal input",
|
|
784
|
-
"image agent",
|
|
785
|
-
"PDF analysis",
|
|
786
|
-
"visual understanding"
|
|
787
|
-
],
|
|
788
493
|
"anchors": {
|
|
789
494
|
"when": "#quando-usar-vision",
|
|
790
495
|
"api": "#a-api",
|
|
791
496
|
"example": "#exemplo-c-completo",
|
|
792
497
|
"content-types": "#datacontent-vs-uricontent"
|
|
793
498
|
},
|
|
794
|
-
"digest": "
|
|
795
|
-
"verifiedAgainst": "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 exercita conteúdo multimodal. Os três tipos de conteúdo citados (`TextContent`, `UriContent`, `DataContent`) foram **conferidos por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0** em 2026-09-08 e continuam existindo com esses nomes.",
|
|
796
|
-
"provedBy": []
|
|
499
|
+
"digest": "Vision em MAF usa ChatMessage multimodal (Microsoft.Extensions.AI): new ChatMessage(ChatRole.User, [new TextContent(pergunta), imagem]) passado a agent.RunAsync. Imagem em URL pública: UriContent(uri, mediaType); arquivo local: DataContent.LoadFromAsync(path), o helper canônico e preferido; bytes já em memória: new DataContent(BinaryData, mediaType). Upload de usuário ou imagem privada usa DataContent, nunca URL temporária (pode expirar); CDN estável usa UriContent. O modelo vem do alias vision no Model Registry, pois nem todo provider suporta visão. Dado tipado: RunAsync<T> com sealed record, nunca regex em response.Text. Proibido: base64 manual, imagem 4K sem resize (tokens cobrados por resolução; ex.: 800px para OCR), vision quando já há texto extraído ou um parser/OCR determinístico basta. PDF: confirme application/pdf no provider."
|
|
797
500
|
},
|
|
798
501
|
{
|
|
799
502
|
"id": "ai-agents-multi-agent-patterns",
|
|
@@ -808,26 +511,12 @@
|
|
|
808
511
|
"stacks": [
|
|
809
512
|
"dotnet"
|
|
810
513
|
],
|
|
811
|
-
"aliases": [
|
|
812
|
-
"multi-agent",
|
|
813
|
-
"agent as tool",
|
|
814
|
-
"agent composition",
|
|
815
|
-
"sequential agents",
|
|
816
|
-
"handoff",
|
|
817
|
-
"sub-agent",
|
|
818
|
-
"agent orchestration",
|
|
819
|
-
"nested agent",
|
|
820
|
-
"agente auxiliar",
|
|
821
|
-
"AsAIFunction"
|
|
822
|
-
],
|
|
823
514
|
"anchors": {
|
|
824
515
|
"agent-as-tool": "#agent-as-tool-padrão-recomendado",
|
|
825
516
|
"sequential": "#sequential-cadeia-fixa",
|
|
826
517
|
"decision-tree": "#decision-tree"
|
|
827
518
|
},
|
|
828
|
-
"digest": "Composing agents, cheapest first: Agent-as-Tool (agent.AsAIFunction(), nesting <= 2) before Sequential before Workflow. Handoff has GRADUATED — HandoffWorkflowBuilder carries no [Experimental] in 1.20.0 — but still comes after agent-as-tool; it needs a stable Id per agent plus WithAutonomousMode(turnLimit:) or a termination condition, and group chat stays at <= 3 agents. Field-measured anti-pattern: a helper agent nested INSIDE a tool — two function-calling loops overlap in the same turn (CallId reconciliation) and the main turn stays open while the helper runs. The correct contract is two visible calls, helper in sequence, orchestrated by code. Structured output between agents; no deep cascades."
|
|
829
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08) + Microsoft.Agents.AI.Workflows 1.20.0 (**fora do pin**; versão apurada no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não compõe agentes. As assinaturas de `AgentWorkflowBuilder`/`HandoffWorkflowBuilder` e a **ausência** do atributo `[Experimental]` no Handoff foram **medidas por reflexão sobre a assembly 1.20.0** em 2026-09-08.",
|
|
830
|
-
"provedBy": []
|
|
519
|
+
"digest": "Composing agents, cheapest first: Agent-as-Tool (agent.AsAIFunction(), nesting <= 2) before Sequential before Workflow. Handoff has GRADUATED — HandoffWorkflowBuilder carries no [Experimental] in 1.20.0 — but still comes after agent-as-tool; it needs a stable Id per agent plus WithAutonomousMode(turnLimit:) or a termination condition, and group chat stays at <= 3 agents. Field-measured anti-pattern: a helper agent nested INSIDE a tool — two function-calling loops overlap in the same turn (CallId reconciliation) and the main turn stays open while the helper runs. The correct contract is two visible calls, helper in sequence, orchestrated by code. Structured output between agents; no deep cascades."
|
|
831
520
|
},
|
|
832
521
|
{
|
|
833
522
|
"id": "ai-agents-observability-patterns",
|
|
@@ -843,22 +532,6 @@
|
|
|
843
532
|
"stacks": [
|
|
844
533
|
"dotnet"
|
|
845
534
|
],
|
|
846
|
-
"aliases": [
|
|
847
|
-
"observability",
|
|
848
|
-
"opentelemetry",
|
|
849
|
-
"telemetry",
|
|
850
|
-
"logging",
|
|
851
|
-
"tracing",
|
|
852
|
-
"log investigation",
|
|
853
|
-
"troubleshooting",
|
|
854
|
-
"aspire dashboard",
|
|
855
|
-
"correlation id",
|
|
856
|
-
"agent observability",
|
|
857
|
-
"agent telemetry",
|
|
858
|
-
"EnableSensitiveData",
|
|
859
|
-
"span duplication",
|
|
860
|
-
"invoke_agent"
|
|
861
|
-
],
|
|
862
535
|
"anchors": {
|
|
863
536
|
"setup": "#setup--opentelemetry",
|
|
864
537
|
"enrichment": "#enrichment--o-que-preencher-em-spans-e-logs",
|
|
@@ -866,11 +539,7 @@
|
|
|
866
539
|
"rastreabilidade": "#rastreabilidade-do-que-foi-ao-provedor",
|
|
867
540
|
"convencao": "#estabilidade-da-convenção-genai--a-ressalva-que-muda-como-ler-a-tabela-acima"
|
|
868
541
|
},
|
|
869
|
-
"digest": "Canonical treatment of OpenTelemetry for MAF agents: setup, enrichment, investigation. Two DIFFERENT ActivitySources must be registered — Experimental.Microsoft.Agents.AI (span invoke_agent) and Experimental.Microsoft.Extensions.AI (spans chat and execute_tool, metrics gen_ai.client.operation.duration and gen_ai.client.token.usage). AgentOpenTelemetryConsts and agent.WithOpenTelemetry() do NOT exist in 1.20.0; the API is builder.UseOpenTelemetry(sourceName, configure) and the sensitive-data switch is EnableSensitiveData (same name on OpenTelemetryAgent and OpenTelemetryChatClient). Duplication rule, measured: instrumenting the two LEVELS is normal and does not duplicate; instrumenting the same level twice does. With a custom pipeline (UseProvidedChatClientAsIs) the MAF guard does not reach it and chat instrumentation becomes mandatory. Issue 3637 (no GenAI ActivityEvents) is a known open limitation. TRACEABILITY (added 2026-09-08): the question the instrumentation exists to answer is whether the turn can be RECONSTRUCTED — raw prompt only in dev/harness with opt-in, HASH in production, and the invariant that makes the hash worth anything is that the text sent is derived from named blocks WITHOUT cache (a cache between assembly and send lets telemetry and request diverge in silence). Two closing assertions: the offered tool list came from the real turn list, and a CANCELLED turn writes nothing. CONVENTION STABILITY: the GenAI semantic conventions LEFT the main OpenTelemetry repo — semconv v1.42.0 (2026-06-12) deprecated and moved all of gen_ai.*, v1.43.0 carries none, the content lives in open-telemetry/semantic-conventions-genai where NOTHING GenAI is Stable (all Development; only error.type/server.address/server.port are stable, and those are common attributes), while OpenTelemetryChatClient declares v1.37 — behind the split. Instrument anyway, but do not build an alert whose only identifier is a gen_ai.* name without a rename plan. Cost delegation is two-way: tariff, cost formula and budget ceiling live in ai-agents-cost-and-budget; span attributes stay here. The Gate 3 ruler for this property is evals/review-checklist.md."
|
|
870
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `provado por templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs` — o guard de span duplicado, a regra de \"um nível, uma instrumentação\" e o default `EnableSensitiveData = false` são exatamente o que o kit compila, com os spans contados em `tests/Morph.AiKit.Tests/TelemetryTests.cs`. Nomes de símbolo, de span, de métrica e das duas `ActivitySource` foram **medidos por reflexão e por leitura dos literais das assemblies do pin** em 2026-09-08, e em 2026-09-09 **todos os treze nomes `gen_ai.*` desta página** foram remedidos, um a um, por **teste de exclusividade**: os quatro `SetTag` da seção *Enrichment* e os nove das seções de span, métrica e atributo de agente. **Os treze existem** — depois de uma correção: `gen_ai.response.finish_reason`, no **singular**, não existia, só aparecia como cabeça de `finish_reason`**s**, e sobreviveu a duas varreduras porque as duas usaram `includes` — e **`includes` de um prefixo não prova o literal autônomo**. Os treze estão tabelados, em **duas** tabelas com propósitos diferentes: a de *Enrichment* registra os **4** `SetTag` daquele bloco (mais os **cinco** nomes rejeitados: o `finish_reason` singular, `PromptTokens`, `CompletionTokens`, `prompt_tokens` e `completion_tokens`), e a de distribuição, na seção *Nomes de span, métricas e atributos — medidos*, registra os **13** por assembly. A conferência é reproduzível por `node scripts/api-probe/probe.mjs tags` sobre este arquivo. A **instabilidade da convenção** — o split do semconv **v1.42.0 (2026-06-12)**, que depreciou e moveu todo `gen_ai.*` para `open-telemetry/semantic-conventions-genai`, onde nada GenAI está `Stable`, contra a v1.37 que o `OpenTelemetryChatClient` declara implementar — é **verificação documental**, apurada em 2026-09-08.",
|
|
871
|
-
"provedBy": [
|
|
872
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs"
|
|
873
|
-
]
|
|
542
|
+
"digest": "Canonical treatment of OpenTelemetry for MAF agents: setup, enrichment, investigation. Two DIFFERENT ActivitySources must be registered — Experimental.Microsoft.Agents.AI (span invoke_agent) and Experimental.Microsoft.Extensions.AI (spans chat and execute_tool, metrics gen_ai.client.operation.duration and gen_ai.client.token.usage). AgentOpenTelemetryConsts and agent.WithOpenTelemetry() do NOT exist in 1.20.0; the API is builder.UseOpenTelemetry(sourceName, configure) and the sensitive-data switch is EnableSensitiveData (same name on OpenTelemetryAgent and OpenTelemetryChatClient). Duplication rule, measured: instrumenting the two LEVELS is normal and does not duplicate; instrumenting the same level twice does. With a custom pipeline (UseProvidedChatClientAsIs) the MAF guard does not reach it and chat instrumentation becomes mandatory. Issue 3637 (no GenAI ActivityEvents) is a known open limitation. TRACEABILITY (added 2026-09-08): the question the instrumentation exists to answer is whether the turn can be RECONSTRUCTED — raw prompt only in dev/harness with opt-in, HASH in production, and the invariant that makes the hash worth anything is that the text sent is derived from named blocks WITHOUT cache (a cache between assembly and send lets telemetry and request diverge in silence). Two closing assertions: the offered tool list came from the real turn list, and a CANCELLED turn writes nothing. CONVENTION STABILITY: the GenAI semantic conventions LEFT the main OpenTelemetry repo — semconv v1.42.0 (2026-06-12) deprecated and moved all of gen_ai.*, v1.43.0 carries none, the content lives in open-telemetry/semantic-conventions-genai where NOTHING GenAI is Stable (all Development; only error.type/server.address/server.port are stable, and those are common attributes), while OpenTelemetryChatClient declares v1.37 — behind the split. Instrument anyway, but do not build an alert whose only identifier is a gen_ai.* name without a rename plan. Cost delegation is two-way: tariff, cost formula and budget ceiling live in ai-agents-cost-and-budget; span attributes stay here. The Gate 3 ruler for this property is evals/review-checklist.md."
|
|
874
543
|
},
|
|
875
544
|
{
|
|
876
545
|
"id": "ai-agents-production",
|
|
@@ -884,28 +553,13 @@
|
|
|
884
553
|
"stacks": [
|
|
885
554
|
"dotnet"
|
|
886
555
|
],
|
|
887
|
-
"aliases": [
|
|
888
|
-
"agent middleware",
|
|
889
|
-
"production agent",
|
|
890
|
-
"a2a protocol",
|
|
891
|
-
"mcp integration",
|
|
892
|
-
"agent observability",
|
|
893
|
-
"agent telemetry",
|
|
894
|
-
"anti-hallucination",
|
|
895
|
-
"semantic caching",
|
|
896
|
-
"multi-tenant isolation",
|
|
897
|
-
"AgentIsolationKeyProvider",
|
|
898
|
-
"concurrent tool invocation"
|
|
899
|
-
],
|
|
900
556
|
"anchors": {
|
|
901
557
|
"middleware": "#middleware-pipeline",
|
|
902
558
|
"a2a": "#a2a-protocol-agent-to-agent",
|
|
903
559
|
"mcp": "#mcp-integration-model-context-protocol",
|
|
904
560
|
"observability": "#observability--opentelemetry"
|
|
905
561
|
},
|
|
906
|
-
"digest": "Production panorama for MAF agents. Middleware, MCP integration and observability are summarized here and treated canonically in ai-agents-middleware-patterns / ai-agents-mcp-tools / ai-agents-mcp-server / ai-agents-observability-patterns. Owns: A2A protocol via Microsoft.Agents.AI.A2A (PRERELEASE, not GA — corrected 2026-09-08); Redis semantic + HybridCache caching; multi-tenant isolation renamed in 1.18 to AgentIsolationKeyProvider / IsolationKeyScopedAgentSessionStore / UseClaimsBasedAgentIsolation, all in the prerelease Microsoft.Agents.AI.Hosting, with the isolation key coming from the credential and never from the payload; concurrent tool invocation is opt-in (AllowConcurrentInvocation defaults to false) and is unsafe with a scoped DbContext. Security and production checklists."
|
|
907
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08) + Microsoft.Agents.AI.Hosting `1.20.0-preview.260831.1` e Microsoft.Agents.AI.A2A `1.20.0-preview.260831.1` (**fora do pin**; versões apuradas no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — este standard é panorama, e cada tema tem dono canônico noutro standard. Os nomes de isolamento multi-tenant foram **medidos por reflexão sobre `Microsoft.Agents.AI.Hosting`** em 2026-09-08.",
|
|
908
|
-
"provedBy": []
|
|
562
|
+
"digest": "Production panorama for MAF agents. Middleware, MCP integration and observability are summarized here and treated canonically in ai-agents-middleware-patterns / ai-agents-mcp-tools / ai-agents-mcp-server / ai-agents-observability-patterns. Owns: A2A protocol via Microsoft.Agents.AI.A2A (PRERELEASE, not GA — corrected 2026-09-08); Redis semantic + HybridCache caching; multi-tenant isolation renamed in 1.18 to AgentIsolationKeyProvider / IsolationKeyScopedAgentSessionStore / UseClaimsBasedAgentIsolation, all in the prerelease Microsoft.Agents.AI.Hosting, with the isolation key coming from the credential and never from the payload; concurrent tool invocation is opt-in (AllowConcurrentInvocation defaults to false) and is unsafe with a scoped DbContext. Security and production checklists."
|
|
909
563
|
},
|
|
910
564
|
{
|
|
911
565
|
"id": "ai-agents-prompt-sources",
|
|
@@ -921,18 +575,6 @@
|
|
|
921
575
|
"stacks": [
|
|
922
576
|
"dotnet"
|
|
923
577
|
],
|
|
924
|
-
"aliases": [
|
|
925
|
-
"prompts in db",
|
|
926
|
-
"agent prompts",
|
|
927
|
-
"prompt versioning",
|
|
928
|
-
"prompt hot-reload",
|
|
929
|
-
"editable instructions",
|
|
930
|
-
"prompt management",
|
|
931
|
-
"prompt sources",
|
|
932
|
-
"prompt por tenant",
|
|
933
|
-
"spec publicado",
|
|
934
|
-
"reconciliação de prompt"
|
|
935
|
-
],
|
|
936
578
|
"anchors": {
|
|
937
579
|
"pattern": "#o-pattern",
|
|
938
580
|
"schema": "#schema-da-tabela",
|
|
@@ -941,9 +583,7 @@
|
|
|
941
583
|
"tenant": "#fonte-2-yaml-por-tenant",
|
|
942
584
|
"reconciliacao": "#reconciliação"
|
|
943
585
|
},
|
|
944
|
-
"digest": "WHERE an agent prompt lives, and how to prove which version actually ran. Two legitimate sources, and the one-sentence rule that picks between them: if the text varies per customer, the prompt is not a record — it is the OUTPUT OF A COMPOSITION, and what you version is the source. (1) Versioned table agent_prompts (agent_key + version + is_active, INSERT + flip, never UPDATE, unique partial index, IMemoryCache with a short TTL) for single-tenant. (2) Repo YAML per tenant + a product frame with named holes -> a deterministic composer -> a rendered spec published to a jsonb column, for product x tenant; editing prompts then costs a repo cycle, which is a product trade-off, not a free upgrade. RECONCILIATION is the section that was missing: versioning and publishing do NOT prove the active version ran (measured: v7/v8 ignored in 6 of 9 executions). The execution record must carry the EFFECTIVE version, plus the hash of the rendered blob on the composed path; the two field-proven techniques are byte-identity equivalence between source and published blob, and a single structural prompt-as-text test asserting the ABSENCE of a revoked instruction."
|
|
945
|
-
"verifiedAgainst": "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**.",
|
|
946
|
-
"provedBy": []
|
|
586
|
+
"digest": "WHERE an agent prompt lives, and how to prove which version actually ran. Two legitimate sources, and the one-sentence rule that picks between them: if the text varies per customer, the prompt is not a record — it is the OUTPUT OF A COMPOSITION, and what you version is the source. (1) Versioned table agent_prompts (id app-assigned with Guid.CreateVersion7(), no gen_random_uuid() default; agent_key + version + is_active, INSERT + flip in one transaction, never UPDATE, unique partial index, IMemoryCache with a short TTL) for single-tenant. (2) Repo YAML per tenant + a product frame with named holes -> a deterministic composer -> a rendered spec published to a jsonb column, for product x tenant; editing prompts then costs a repo cycle, which is a product trade-off, not a free upgrade. RECONCILIATION is the section that was missing: versioning and publishing do NOT prove the active version ran (measured: v7/v8 ignored in 6 of 9 executions). The execution record must carry the EFFECTIVE version, plus the hash of the rendered blob on the composed path; the two field-proven techniques are byte-identity equivalence between source and published blob, and a single structural prompt-as-text test asserting the ABSENCE of a revoked instruction."
|
|
947
587
|
},
|
|
948
588
|
{
|
|
949
589
|
"id": "ai-agents-providers-model-registry",
|
|
@@ -960,36 +600,13 @@
|
|
|
960
600
|
"stacks": [
|
|
961
601
|
"dotnet"
|
|
962
602
|
],
|
|
963
|
-
"aliases": [
|
|
964
|
-
"model registry",
|
|
965
|
-
"provider agnostic",
|
|
966
|
-
"model alias",
|
|
967
|
-
"IChatClient factory",
|
|
968
|
-
"multi-model",
|
|
969
|
-
"model selection",
|
|
970
|
-
"model config",
|
|
971
|
-
"switch provider",
|
|
972
|
-
"openai google anthropic ollama",
|
|
973
|
-
"direct model",
|
|
974
|
-
"api per alias",
|
|
975
|
-
"responses vs chat",
|
|
976
|
-
"reasoning effort",
|
|
977
|
-
"temperature vs reasoning",
|
|
978
|
-
"model pricing"
|
|
979
|
-
],
|
|
980
603
|
"anchors": {
|
|
981
604
|
"schema": "#schema-canonical",
|
|
982
605
|
"providers": "#providers-first-class",
|
|
983
606
|
"implementation": "#implementation-pattern",
|
|
984
607
|
"checklist": "#checklist-verifiable-in-review"
|
|
985
608
|
},
|
|
986
|
-
"digest": "Single JSON file (model-registry.json v2) maps aliases to { provider, model, api, options, constraints }. Agents reference aliases, never model strings. EVERY alias declares `api`: `responses` (default when omitted) or `chat` (explicit opt-in); Ollama/OpenAI-compatible providers ignore the field but never travel the Responses path. `temperature` and `reasoningEffort` are mutually exclusive per alias, with the two origins labelled separately: the documented one (Chat Completions rejects tool calling with reasoning_effort != none from GPT-5.4) and the field-measured one (temperature on reasoning models — no primary source, so it is a per-alias constraint with mandatory provenance). .NET path: ChatOptions.Reasoning = new ReasoningOptions { Effort = ReasoningEffort.* } is CONFIRMED in M.E.AI 10.9.0; RawRepresentationFactory is the escape hatch for values the enum lacks. OPENAI001 still applies to the Responses types. No floating PackageReference ranges."
|
|
987
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 + OpenAI 2.13.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs` e `.../Providers/ModelAlias.cs` e `.../Agents/AgentApi.cs`. Nomes de tipo do `Microsoft.Extensions.AI` e do `OpenAI` medidos por reflexão sobre as assemblies do pin em 2026-09-08.",
|
|
988
|
-
"provedBy": [
|
|
989
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs",
|
|
990
|
-
".../Providers/ModelAlias.cs",
|
|
991
|
-
".../Agents/AgentApi.cs"
|
|
992
|
-
]
|
|
609
|
+
"digest": "Single JSON file (model-registry.json v2) maps aliases to { provider, model, api, options, constraints }. Agents reference aliases, never model strings. EVERY alias declares `api`: `responses` (default when omitted) or `chat` (explicit opt-in); Ollama/OpenAI-compatible providers ignore the field but never travel the Responses path. `temperature` and `reasoningEffort` are mutually exclusive per alias, with the two origins labelled separately: the documented one (Chat Completions rejects tool calling with reasoning_effort != none from GPT-5.4) and the field-measured one (temperature on reasoning models — no primary source, so it is a per-alias constraint with mandatory provenance). .NET path: ChatOptions.Reasoning = new ReasoningOptions { Effort = ReasoningEffort.* } is CONFIRMED in M.E.AI 10.9.0; RawRepresentationFactory is the escape hatch for values the enum lacks. OPENAI001 still applies to the Responses types. No floating PackageReference ranges."
|
|
993
610
|
},
|
|
994
611
|
{
|
|
995
612
|
"id": "ai-agents-rag-custom-pgvector",
|
|
@@ -1006,31 +623,12 @@
|
|
|
1006
623
|
"stacks": [
|
|
1007
624
|
"dotnet"
|
|
1008
625
|
],
|
|
1009
|
-
"aliases": [
|
|
1010
|
-
"rag",
|
|
1011
|
-
"vector search",
|
|
1012
|
-
"pgvector",
|
|
1013
|
-
"embeddings",
|
|
1014
|
-
"custom rag",
|
|
1015
|
-
"retrieval",
|
|
1016
|
-
"semantic search",
|
|
1017
|
-
"knowledge base",
|
|
1018
|
-
"RAG tool",
|
|
1019
|
-
"delta ingestion",
|
|
1020
|
-
"incremental ingestion",
|
|
1021
|
-
"lookup key",
|
|
1022
|
-
"content hash"
|
|
1023
|
-
],
|
|
1024
626
|
"anchors": {
|
|
1025
627
|
"architecture": "#arquitetura",
|
|
1026
628
|
"tool": "#vectorsearchtool-tool-maf",
|
|
1027
629
|
"multi-tenant": "#multi-tenant"
|
|
1028
630
|
},
|
|
1029
|
-
"digest": "Custom RAG on pgvector/Neon you control: chunking, embedding model via the Model Registry `embeddings` alias, HNSW with vector_cosine_ops, tenant_id filtered in every query and never exposed to the LLM. Owns DELTA INGESTION by lookup-key: a pure Plan(incoming, lookupKey, contentHash, existingHashes) returning Added/Updated/Removed/Unchanged, where Unchanged is never re-embedded, Updated is an upsert on a deterministic id, Removed is applied AFTER the writes (no empty window), and a duplicate lookup-key fails the run naming both positions. Microsoft.Extensions.DataIngestion (still preview) solves hygiene, not embedding cost — its IncrementalIngestion deletes all pre-existing chunks of a document before rewriting. Open item: similarity threshold and k per scope."
|
|
1030
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `provado por templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs` **restrito à seção \"Ingestão delta por lookup-key\"** — é a única parte deste standard que o ai-kit compila. Todo o resto (pgvector, SQL, HNSW, embeddings, multi-tenant) é **verificação documental**; a versão e o status de `Microsoft.Extensions.DataIngestion` foram apurados no nuget.org em 2026-09-08.",
|
|
1031
|
-
"provedBy": [
|
|
1032
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs"
|
|
1033
|
-
]
|
|
631
|
+
"digest": "Custom RAG on pgvector/Neon you control: chunking, embedding model via the Model Registry `embeddings` alias with `IEmbeddingGenerator<string, Embedding<float>>` injected directly into the ingestion handler and the VectorSearchTool (no EmbeddingService wrapper; batch GenerateAsync at ingestion, GenerateVectorAsync per query), the tool querying through EF Core (`CosineDistance` LINQ, raw NpgsqlDataSource only with a decisions.md reason), ids app-assigned (deterministic V5 for delta chunks, else Guid.CreateVersion7(); no gen_random_uuid() default), HNSW with vector_cosine_ops, tenant_id filtered in every query and never exposed to the LLM. Owns DELTA INGESTION by lookup-key: a pure Plan(incoming, lookupKey, contentHash, existingHashes) returning Added/Updated/Removed/Unchanged, where Unchanged is never re-embedded, Updated is an upsert on a deterministic id, Removed is applied AFTER the writes (no empty window), and a duplicate lookup-key fails the run naming both positions. Microsoft.Extensions.DataIngestion (still preview) solves hygiene, not embedding cost — its IncrementalIngestion deletes all pre-existing chunks of a document before rewriting. Open item: similarity threshold and k per scope."
|
|
1034
632
|
},
|
|
1035
633
|
{
|
|
1036
634
|
"id": "ai-agents-service-tier-flex",
|
|
@@ -1047,25 +645,13 @@
|
|
|
1047
645
|
"stacks": [
|
|
1048
646
|
"dotnet"
|
|
1049
647
|
],
|
|
1050
|
-
"aliases": [
|
|
1051
|
-
"service tier",
|
|
1052
|
-
"flex tier",
|
|
1053
|
-
"cost optimization",
|
|
1054
|
-
"async tier",
|
|
1055
|
-
"priority tier",
|
|
1056
|
-
"tier flex",
|
|
1057
|
-
"background inference",
|
|
1058
|
-
"ChatServiceTier"
|
|
1059
|
-
],
|
|
1060
648
|
"anchors": {
|
|
1061
649
|
"when": "#quando-usar-tier-flex",
|
|
1062
650
|
"api": "#a-api",
|
|
1063
651
|
"example": "#exemplo-c",
|
|
1064
652
|
"decision": "#decision"
|
|
1065
653
|
},
|
|
1066
|
-
"digest": "Service tier trades latency for cost on async work (workers, Hangfire, nightly reports) — if the result goes to a database or a queue, use flex. OpenAI-specific: Microsoft.Extensions.AI.ChatOptions has no service-tier property (measured), so it goes through the concrete client or AdditionalProperties + RawRepresentationFactory. Measured in OpenAI 2.13.0: the property is ChatCompletionOptions.ServiceTier of type ChatServiceTier? (and CreateResponseOptions.ServiceTier of ResponseServiceTier?), with values Auto/Default/Flex/Scale — it is NOT a string, and there is no `priority` value. Never use flex on an interactive completion."
|
|
1067
|
-
"verifiedAgainst": "OpenAI 2.13.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08). **Verificação documental** da política de tiers (platform.openai.com/docs/guides/service-tiers, lido 2026-09-08), sem cláusula `provado por`: o ai-kit **referencia** o pacote `OpenAI` no `.csproj` — contrato de versão, régua = restore (`NU1102`) — mas **não o exercita**; não existe um único `using OpenAI` em `src/` (medido 2026-09-08), logo um rename de API não reprova a PR. Ver a tabela \"Dois níveis de garantia\" no README do kit. **O nome e o tipo da propriedade C# foram medidos por reflexão sobre a assembly `OpenAI` 2.13.0 em 2026-09-08**, e o marcador `// VERIFY` desta página foi RESOLVIDO por essa medição.",
|
|
1068
|
-
"provedBy": []
|
|
654
|
+
"digest": "Service tier trades latency for cost on async work (workers, Hangfire, nightly reports) — if the result goes to a database or a queue, use flex. OpenAI-specific: Microsoft.Extensions.AI.ChatOptions has no service-tier property (measured), so it goes through the concrete client or AdditionalProperties + RawRepresentationFactory. Measured in OpenAI 2.13.0: the property is ChatCompletionOptions.ServiceTier of type ChatServiceTier? (and CreateResponseOptions.ServiceTier of ResponseServiceTier?), with values Auto/Default/Flex/Scale — it is NOT a string, and there is no `priority` value. Never use flex on an interactive completion."
|
|
1069
655
|
},
|
|
1070
656
|
{
|
|
1071
657
|
"id": "ai-agents-setup",
|
|
@@ -1079,31 +665,13 @@
|
|
|
1079
665
|
"stacks": [
|
|
1080
666
|
"dotnet"
|
|
1081
667
|
],
|
|
1082
|
-
"aliases": [
|
|
1083
|
-
"MAF setup",
|
|
1084
|
-
"Microsoft Agents AI",
|
|
1085
|
-
"ChatClient",
|
|
1086
|
-
"AsAIAgent",
|
|
1087
|
-
"AIAgent dotnet",
|
|
1088
|
-
"agent framework setup",
|
|
1089
|
-
"MAF packages",
|
|
1090
|
-
"MAF nuget",
|
|
1091
|
-
"responses api",
|
|
1092
|
-
"GetResponsesClient",
|
|
1093
|
-
"store false",
|
|
1094
|
-
"chat completions"
|
|
1095
|
-
],
|
|
1096
668
|
"anchors": {
|
|
1097
669
|
"packages": "#1-packages-nuget",
|
|
1098
670
|
"quickstart": "#2-quick-start-canonical-minimal-agent",
|
|
1099
671
|
"providers": "#3-setup-by-provider",
|
|
1100
672
|
"telemetry": "#5-opentelemetry"
|
|
1101
673
|
},
|
|
1102
|
-
"digest": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 + OpenAI 2.13.0, exact versions, no floating ranges. For OpenAI the DEFAULT path is the Responses API: new OpenAIClient(key).GetResponsesClient().AsAIAgent(model:, instructions:) — Chat Completions stays legitimate but is an explicit opt-in (`\"api\": \"chat\"` on the alias). `store: false` is the house rule (AsIChatClientWithStoredOutputDisabled) because the project owns its own conversation history. AsIChatClient comes from Microsoft.Extensions.AI.OpenAI; AsAIAgent from Microsoft.Agents.AI.OpenAI. Telemetry needs BOTH ActivitySources (Experimental.Microsoft.Agents.AI and Experimental.Microsoft.Extensions.AI) — AgentOpenTelemetryConsts does not exist. Do NOT use Semantic Kernel orchestration."
|
|
1103
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 + OpenAI 2.13.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` (pipeline e escolha de protocolo). A superfície do SDK `OpenAI` e das pontes `Microsoft.Agents.AI.OpenAI` / `Microsoft.Extensions.AI.OpenAI` foi **medida por reflexão sobre as assemblies do pin** em 2026-09-08 — o ai-kit **não exercita** o cliente concreto da OpenAI — referencia o pacote no `.csproj`, mas não há `using OpenAI` em `src/` (medido 2026-09-08); ver a tabela \"Dois níveis de garantia\" no README do kit.",
|
|
1104
|
-
"provedBy": [
|
|
1105
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs"
|
|
1106
|
-
]
|
|
674
|
+
"digest": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 + OpenAI 2.13.0, exact versions, no floating ranges. For OpenAI the DEFAULT path is the Responses API: new OpenAIClient(key).GetResponsesClient().AsAIAgent(model:, instructions:) — Chat Completions stays legitimate but is an explicit opt-in (`\"api\": \"chat\"` on the alias). `store: false` is the house rule (AsIChatClientWithStoredOutputDisabled) because the project owns its own conversation history. AsIChatClient comes from Microsoft.Extensions.AI.OpenAI; AsAIAgent from Microsoft.Agents.AI.OpenAI. Telemetry needs BOTH ActivitySources (Experimental.Microsoft.Agents.AI and Experimental.Microsoft.Extensions.AI) — AgentOpenTelemetryConsts does not exist. Do NOT use Semantic Kernel orchestration."
|
|
1107
675
|
},
|
|
1108
676
|
{
|
|
1109
677
|
"id": "ai-agents-structured-output",
|
|
@@ -1119,28 +687,12 @@
|
|
|
1119
687
|
"stacks": [
|
|
1120
688
|
"dotnet"
|
|
1121
689
|
],
|
|
1122
|
-
"aliases": [
|
|
1123
|
-
"structured output",
|
|
1124
|
-
"typed response",
|
|
1125
|
-
"response format",
|
|
1126
|
-
"schema validated output",
|
|
1127
|
-
"record output",
|
|
1128
|
-
"JSON schema agent",
|
|
1129
|
-
"RunAsync generic",
|
|
1130
|
-
"typed agent output",
|
|
1131
|
-
"ToAgentResponseAsync",
|
|
1132
|
-
"response format limits"
|
|
1133
|
-
],
|
|
1134
690
|
"anchors": {
|
|
1135
691
|
"api": "#a-api-maf-10-ga",
|
|
1136
692
|
"type": "#definindo-o-tipo-de-saída",
|
|
1137
693
|
"validation": "#validação-pós-call"
|
|
1138
694
|
},
|
|
1139
|
-
"digest": "Every agent whose output feeds another system returns a typed record, never free text. Form 1 is RunAsync<T>; Form 2 is ResponseFormat = ChatResponseFormat.ForJsonSchema<T>() in ChatOptions. Type is a sealed record with [Description] on the type and [JsonPropertyName] on every property. Documented limits: ResponseFormat does not accept a primitive or an array at the root (use a wrapper), and in streaming you must build the whole response with ToAgentResponseAsync() before deserialising. Schema validates shape, not semantics — always validate business rules after the call. Do not use temperature as the example of a normal ChatOption."
|
|
1140
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08); `provado por templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs` **apenas** para o mapeamento de `AgentSpec.OutputSchema` → `ChatOptions.ResponseFormat = ChatResponseFormat.ForJsonSchema(...)`, que é o que o kit compila. `RunAsync<T>`, `Deserialize<T>` e os limites do `ResponseFormat` são **verificação documental**, com os nomes de tipo medidos por reflexão sobre `Microsoft.Extensions.AI.Abstractions` 10.9.0 em 2026-09-08.",
|
|
1141
|
-
"provedBy": [
|
|
1142
|
-
"templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs"
|
|
1143
|
-
]
|
|
695
|
+
"digest": "Every agent whose output feeds another system returns a typed record, never free text. Form 1 is RunAsync<T>; Form 2 is ResponseFormat = ChatResponseFormat.ForJsonSchema<T>() in ChatOptions. Type is a sealed record with [Description] on the type and [JsonPropertyName] on every property. Documented limits: ResponseFormat does not accept a primitive or an array at the root (use a wrapper), and in streaming you must build the whole response with ToAgentResponseAsync() before deserialising. Schema validates shape, not semantics — always validate business rules after the call. Do not use temperature as the example of a normal ChatOption."
|
|
1144
696
|
},
|
|
1145
697
|
{
|
|
1146
698
|
"id": "ai-agents-sweet-spot",
|
|
@@ -1156,26 +708,12 @@
|
|
|
1156
708
|
"stacks": [
|
|
1157
709
|
"dotnet"
|
|
1158
710
|
],
|
|
1159
|
-
"aliases": [
|
|
1160
|
-
"sweet spot",
|
|
1161
|
-
"when to use agent",
|
|
1162
|
-
"agent vs workflow",
|
|
1163
|
-
"when not to use workflow",
|
|
1164
|
-
"MAF decision",
|
|
1165
|
-
"simple agent default",
|
|
1166
|
-
"4 feature rule",
|
|
1167
|
-
"single agent default",
|
|
1168
|
-
"tool budget",
|
|
1169
|
-
"how many tools"
|
|
1170
|
-
],
|
|
1171
711
|
"anchors": {
|
|
1172
712
|
"rule": "#the-4-feature-rule",
|
|
1173
713
|
"decision-tree": "#decision-tree-claude-reads-this-in-plan-phase",
|
|
1174
714
|
"anti-patterns": "#anti-patterns-what-claude-should-not-do"
|
|
1175
715
|
},
|
|
1176
|
-
"digest": "The 4-feature rule (Chat + Structured Output + Tools + RAG Custom) covers 95% of agents. Single-agent is the default; reach for multi-agent or workflow only when fan-in/out or genuine handoff is needed. Fifth signal: HOW MANY tools the agent carries — every registered tool re-sends its name, description and JSON Schema on every turn, so the practical ceiling is ~12 tools; above it prune, then use a per-phase allow-list, then agent-as-tool. Handoff has graduated out of experimental in 1.20.0, which changes the reason but not the order of preference. Read in plan phase before deciding architecture."
|
|
1177
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 + Microsoft.Agents.AI.Workflows 1.20.0 (ai-pin 2026-09-08 para o primeiro; o segundo está **fora do pin**, versão apurada no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — este standard é decisão de arquitetura, não API. A escada de complexidade foi reconferida e **continua correta**; o que entrou de novo é o custo de definição de tool por turno e o status do Handoff.",
|
|
1178
|
-
"provedBy": []
|
|
716
|
+
"digest": "The 4-feature rule (Chat + Structured Output + Tools + RAG Custom) covers 95% of agents. Single-agent is the default; reach for multi-agent or workflow only when fan-in/out or genuine handoff is needed. Fifth signal: HOW MANY tools the agent carries — every registered tool re-sends its name, description and JSON Schema on every turn, so the practical ceiling is ~12 tools; above it prune, then use a per-phase allow-list, then agent-as-tool. Handoff has graduated out of experimental in 1.20.0, which changes the reason but not the order of preference. Read in plan phase before deciding architecture."
|
|
1179
717
|
},
|
|
1180
718
|
{
|
|
1181
719
|
"id": "ai-agents-testing-ai",
|
|
@@ -1192,15 +730,6 @@
|
|
|
1192
730
|
"stacks": [
|
|
1193
731
|
"dotnet"
|
|
1194
732
|
],
|
|
1195
|
-
"aliases": [
|
|
1196
|
-
"testar agente",
|
|
1197
|
-
"fake IChatClient",
|
|
1198
|
-
"ScriptedChatClient",
|
|
1199
|
-
"harness de turno",
|
|
1200
|
-
"eval de agente",
|
|
1201
|
-
"teste sem rede",
|
|
1202
|
-
"golden de prompt"
|
|
1203
|
-
],
|
|
1204
733
|
"anchors": {
|
|
1205
734
|
"fronteira": "#fronteira-com-o-testing-genérico",
|
|
1206
735
|
"fake": "#fake-de-ichatclient",
|
|
@@ -1210,12 +739,7 @@
|
|
|
1210
739
|
"golden": "#golden-com-parcimônia",
|
|
1211
740
|
"checklist": "#checklist-verifiable-in-review"
|
|
1212
741
|
},
|
|
1213
|
-
"digest": "Only what is agent-specific — backend-dotnet-testing keeps xUnit, FakeTimeProvider and isolation. The single point where code touches a provider is an IChatClient, so everything follows from replacing it: a SCRIPTED and DUMB fake (a queue of hand-written responses, capture of messages/options/call count) that THROWS when the queue runs out — a silent empty response makes a test pass by accident. Two independent products converged on this shape; the kit compiles the third. Run the REAL function-invocation loop on top of the fake: tool choice, arguments, guard and ceiling are only observable through the loop. The seam keeps classes sealed (a one-operation interface, or an internal constructor taking alias -> IChatClient). ZERO real provider calls inside dotnet test, verified by a sweep of tests/ for API keys, environment reads and HTTP clients — and the standard records the price of leaving the real harness outside the suite: 11 scenarios that were never run end to end. Eval means DETERMINISTIC eval over the full real path with the model scripted; a hard gate only over deterministic properties. Raw prompt only in dev/harness with opt-in, hash in production. Golden proves FORM, not content (counter-example: 13.6 KB frozen to prove two lines of concatenation, deliberately deleted). A versioned prompt gets AT MOST ONE structural test (keys the consumer reads, absence of a revoked instruction); what the model answers is proven at runtime by evals-with-cache, never by Contains over the prompt source."
|
|
1214
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); provado por `templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs` e `.../src/Morph.AiKit/Providers/IProviderClientFactory.cs` — o fake roteirizado e a costura que dispensa rede são o que o kit compila e exercita. Harness de turno completo e evals com cache de resposta são **verificação documental** — o ai-kit não os traz.",
|
|
1215
|
-
"provedBy": [
|
|
1216
|
-
"templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs",
|
|
1217
|
-
".../src/Morph.AiKit/Providers/IProviderClientFactory.cs"
|
|
1218
|
-
]
|
|
742
|
+
"digest": "Only what is agent-specific — backend-dotnet-testing keeps xUnit, FakeTimeProvider and isolation. The single point where code touches a provider is an IChatClient, so everything follows from replacing it: a SCRIPTED and DUMB fake (a queue of hand-written responses, capture of messages/options/call count) that THROWS when the queue runs out — a silent empty response makes a test pass by accident. Two independent products converged on this shape; the kit compiles the third. Run the REAL function-invocation loop on top of the fake: tool choice, arguments, guard and ceiling are only observable through the loop. The seam keeps classes sealed (a one-operation interface, or an internal constructor taking alias -> IChatClient). ZERO real provider calls inside dotnet test, verified by a sweep of tests/ for API keys, environment reads and HTTP clients — and the standard records the price of leaving the real harness outside the suite: 11 scenarios that were never run end to end. Eval means DETERMINISTIC eval over the full real path with the model scripted; a hard gate only over deterministic properties. Raw prompt only in dev/harness with opt-in, hash in production. Golden proves FORM, not content (counter-example: 13.6 KB frozen to prove two lines of concatenation, deliberately deleted). A versioned prompt gets AT MOST ONE structural test (keys the consumer reads, absence of a revoked instruction); what the model answers is proven at runtime by evals-with-cache, never by Contains over the prompt source."
|
|
1219
743
|
},
|
|
1220
744
|
{
|
|
1221
745
|
"id": "ai-agents-vector-data-extensions",
|
|
@@ -1232,27 +756,13 @@
|
|
|
1232
756
|
"stacks": [
|
|
1233
757
|
"dotnet"
|
|
1234
758
|
],
|
|
1235
|
-
"aliases": [
|
|
1236
|
-
"microsoft extensions vectordata",
|
|
1237
|
-
"vectorstorecollection",
|
|
1238
|
-
"vector store abstraction",
|
|
1239
|
-
"data ingestion",
|
|
1240
|
-
"ingestion pipeline",
|
|
1241
|
-
"semantic kernel connectors exception",
|
|
1242
|
-
"pgvector connector",
|
|
1243
|
-
"vectorstorewriter",
|
|
1244
|
-
"CommunityToolkit VectorData",
|
|
1245
|
-
"connector rename"
|
|
1246
|
-
],
|
|
1247
759
|
"anchors": {
|
|
1248
760
|
"naming-trap": "#a-pegadinha-de-nomenclatura-crítico--leia-antes-de-tudo",
|
|
1249
761
|
"maturity": "#maturidade-atual-verificado-2026-07-06",
|
|
1250
762
|
"decision-matrix": "#decision-matrix--custom-pgvector-vs-vectordata",
|
|
1251
763
|
"data-ingestion": "#microsoftextensionsdataingestion-preview--pipeline-pattern-para-adoção-futura"
|
|
1252
764
|
},
|
|
1253
|
-
"digest": "Microsoft.Extensions.VectorData.Abstractions (10.9.0, GA) is the provider-agnostic vector-store abstraction. The concrete connectors MOVED: the Microsoft.SemanticKernel.Connectors.* names are a dead end (PgVector stopped at 1.74.0-preview, 2026-03-20, never stable) and the successor is CommunityToolkit.VectorData.* (PgVector 1.0.1, stable, 2026-08-31). The old naming trap — SK-named packages carrying zero SK orchestration — still matters for legacy projects. Microsoft.Extensions.DataIngestion remains preview. Decision matrix: keep ai-agents-rag-custom-pgvector where it already works (the \"it is preview\" argument has expired; \"do not rewrite what works\" has not); reach for VectorData on new projects or genuine multi-backend portability."
|
|
1254
|
-
"verifiedAgainst": "Microsoft.Extensions.VectorData.Abstractions **10.9.0** + CommunityToolkit.VectorData.PgVector **1.0.1** + Microsoft.Extensions.DataIngestion **10.9.0-preview.1.26411.16** — nenhum deles está no `framework/ai-pin.json` (que cobre os 5 pacotes de agente), portanto **todas as versões acima foram apuradas na API do nuget.org em 2026-09-08** e são citadas com essa data, não como pin. **Verificação documental**, sem cláusula `provado por`: o ai-kit **não referencia nem exercita** VectorData — o pacote não está no `Morph.AiKit.csproj`, cujos cinco `PackageReference` são exatamente os do pin (medido 2026-09-08). Aqui nem a régua fraca do restore existe.",
|
|
1255
|
-
"provedBy": []
|
|
765
|
+
"digest": "Microsoft.Extensions.VectorData.Abstractions (10.9.0, GA) is the provider-agnostic vector-store abstraction. The concrete connectors MOVED: the Microsoft.SemanticKernel.Connectors.* names are a dead end (PgVector stopped at 1.74.0-preview, 2026-03-20, never stable) and the successor is CommunityToolkit.VectorData.* (PgVector 1.0.1, stable, 2026-08-31). The old naming trap — SK-named packages carrying zero SK orchestration — still matters for legacy projects. Microsoft.Extensions.DataIngestion remains preview. Decision matrix: keep ai-agents-rag-custom-pgvector where it already works (the \"it is preview\" argument has expired; \"do not rewrite what works\" has not); reach for VectorData on new projects or genuine multi-backend portability."
|
|
1256
766
|
},
|
|
1257
767
|
{
|
|
1258
768
|
"id": "ai-agents-workflows",
|
|
@@ -1266,26 +776,12 @@
|
|
|
1266
776
|
"stacks": [
|
|
1267
777
|
"dotnet"
|
|
1268
778
|
],
|
|
1269
|
-
"aliases": [
|
|
1270
|
-
"workflow",
|
|
1271
|
-
"agent workflow",
|
|
1272
|
-
"fan-in",
|
|
1273
|
-
"fan-out",
|
|
1274
|
-
"when not to use workflow",
|
|
1275
|
-
"orchestration",
|
|
1276
|
-
"AgentWorkflowBuilder",
|
|
1277
|
-
"concurrent workflow",
|
|
1278
|
-
"Microsoft.Agents.AI.Workflows",
|
|
1279
|
-
"declarative workflow"
|
|
1280
|
-
],
|
|
1281
779
|
"anchors": {
|
|
1282
780
|
"when-not": "#quando-não-usar-um-workflow",
|
|
1283
781
|
"when-justified": "#quando-um-workflow-é-justificado",
|
|
1284
782
|
"canonical-example": "#exemplo-canônico--fan-in-concurrent"
|
|
1285
783
|
},
|
|
1286
|
-
"digest": "Workflows are advanced and rare — 95% of agents need only a single agent (see ai-agents-sweet-spot). Reach for a Workflow ONLY for genuine fan-in/out over many inputs or long-running human-in-the-loop; a foreach + LINQ aggregate usually beats one. PACKAGE CORRECTION: AgentWorkflowBuilder, WorkflowBuilder, InProcessExecution and CheckpointManager are NOT in Microsoft.Agents.AI — they live in the SEPARATE package Microsoft.Agents.AI.Workflows (1.20.0, stable). Declarative workflows (Microsoft.Agents.AI.Workflows.Declarative 1.20.0) are stable; declarative AGENTS (Microsoft.Agents.AI.Declarative 1.20.0-rc1) still need --prerelease. Since 1.17 a workflow fails when a participating agent returns an error."
|
|
1287
|
-
"verifiedAgainst": "Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08) + Microsoft.Agents.AI.Workflows 1.20.0, Microsoft.Agents.AI.Workflows.Declarative 1.20.0 e Microsoft.Agents.AI.Declarative `1.20.0-rc1` (**os três fora do pin**; versões apuradas no nuget.org em 2026-09-08). **Verificação documental**, sem cláusula `provado por` — o ai-kit não monta workflows. **A localização dos tipos de workflow foi medida por reflexão em 2026-09-08 e CORRIGE este standard**: eles não estão no pacote base.",
|
|
1288
|
-
"provedBy": []
|
|
784
|
+
"digest": "Workflows are advanced and rare — 95% of agents need only a single agent (see ai-agents-sweet-spot). Reach for a Workflow ONLY for genuine fan-in/out over many inputs or long-running human-in-the-loop; a foreach + LINQ aggregate usually beats one. PACKAGE CORRECTION: AgentWorkflowBuilder, WorkflowBuilder, InProcessExecution and CheckpointManager are NOT in Microsoft.Agents.AI — they live in the SEPARATE package Microsoft.Agents.AI.Workflows (1.20.0, stable). Declarative workflows (Microsoft.Agents.AI.Workflows.Declarative 1.20.0) are stable; declarative AGENTS (Microsoft.Agents.AI.Declarative 1.20.0-rc1) still need --prerelease. Since 1.17 a workflow fails when a participating agent returns an error."
|
|
1289
785
|
},
|
|
1290
786
|
{
|
|
1291
787
|
"id": "architecture-vertical-slice-vertical-slice",
|
|
@@ -1300,21 +796,12 @@
|
|
|
1300
796
|
"stacks": [
|
|
1301
797
|
"dotnet"
|
|
1302
798
|
],
|
|
1303
|
-
"aliases": [
|
|
1304
|
-
"vertical slice",
|
|
1305
|
-
"VSA",
|
|
1306
|
-
"slice architecture",
|
|
1307
|
-
"feature slice",
|
|
1308
|
-
"CQRS slice"
|
|
1309
|
-
],
|
|
1310
799
|
"anchors": {
|
|
1311
800
|
"structure": "#estrutura-de-pastas",
|
|
1312
801
|
"rules": "#padrões-obrigatórios",
|
|
1313
802
|
"cqrs": "#anti-patterns-nunca-fazer-em-vsa"
|
|
1314
803
|
},
|
|
1315
|
-
"digest": "
|
|
1316
|
-
"verifiedAgainst": ".NET 10 + EF Core 10 + FluentValidation 12 + Scrutor 7.",
|
|
1317
|
-
"provedBy": []
|
|
804
|
+
"digest": "Uma slice por operação em Features/{Entity}Feature/{Verb}{Entity}/: Request e Response (sealed records) no mesmo arquivo do Handler, Validator FluentValidation (nenhum em GetAll) e Endpoint `IApiEndpoint` `internal sealed`. O handler implementa o `IHandler<TRequest, TResponse>` próprio, devolve `Result<T>` (erros em `{Entity}Errors`, 1 por feature), injeta `ApplicationDbContext` direto e chama `SaveChangesAsync` nas escritas; IDs com `Guid.CreateVersion7()`, nunca `Guid.NewGuid()`; o endpoint usa `result.Match()`: o sucesso recebe o valor no lambda (nunca `result.Value`) e a falha vira `error.ToHttpResult()` (status pelo `ErrorType`, ProblemDetails), nunca `Results.BadRequest` para todo erro, declarando cada status com `.ProducesProblem`. Decorators Scrutor: Logging -> Validation -> Handler. PROIBIDO: MediatR/IRequest, AggregateRoot, DomainEvent, CQRS formal, Application Service layer, DTOs compartilhados entre features e `IRepository<T>`/`IUnitOfWork` (DbSet já é repository, vira código morto); store dedicado só com justificativa em `decisions.md`. Não misture VSA com DDD."
|
|
1318
805
|
},
|
|
1319
806
|
{
|
|
1320
807
|
"id": "backend-api-validation",
|
|
@@ -1329,20 +816,12 @@
|
|
|
1329
816
|
"stacks": [
|
|
1330
817
|
"dotnet"
|
|
1331
818
|
],
|
|
1332
|
-
"aliases": [
|
|
1333
|
-
"FluentValidation",
|
|
1334
|
-
"input validation",
|
|
1335
|
-
"request validation",
|
|
1336
|
-
"API validation",
|
|
1337
|
-
"model validation"
|
|
1338
|
-
],
|
|
1339
819
|
"anchors": {
|
|
1340
|
-
"fluent": "#
|
|
1341
|
-
"rules": "#validation-patterns"
|
|
820
|
+
"fluent": "#fluentvalidation-validator-in-the-slice",
|
|
821
|
+
"rules": "#validation-patterns",
|
|
822
|
+
"state": "#state-checks-in-the-handler"
|
|
1342
823
|
},
|
|
1343
|
-
"digest": "
|
|
1344
|
-
"verifiedAgainst": ".NET 10 (pure state-validation pattern — no external package).",
|
|
1345
|
-
"provedBy": []
|
|
824
|
+
"digest": "Request/DTO validation in the VSA slice: one sealed `AbstractValidator<TRequest>` (FluentValidation) next to the handler, checking SHAPE only (required, length, range, format, child items via RuleForEach/ChildRules; no DbContext; time rules read an injected TimeProvider inside the rule). The ValidationDecorator runs it before the handler and short-circuits with `Result.Failure(new ValidationError([...]))`; the endpoint maps it with `error.ToHttpResult()` to 400 ProblemDetails with `errors[]` (decorator and conversion in fluent-validation-vsa.md, ValidationError/ErrorHttpMapping in result-pattern.md). Never throw (ValidationException/InvalidOperationException), never validate in the endpoint lambda, never mix DataAnnotations with FluentValidation (minimal APIs have no ModelState). Existence/uniqueness/state checks live in the handler and return typed `{Entity}Errors` (NotFound 404, Conflict 409, Failure 400), never `Result.Failure(\"string\")`. State checks validate what is INVALID, not what is valid, so a new flow (e.g. a free flow that skips PendingPayment) does not break: ordered status enum (Created=0 ... EmailSent=4) with error states at 100+ (Failed=100, Cancelled=101, Refunded=102), blocked by `status >= Completed`, an error-state list, or `status >= Failed`. FORBIDDEN: `if (status != Expected)`, exhaustive switches over valid states, business rules mixed into the state check (keep `RequiresPayment && !IsPaid` separate). Document all flows in decisions.md and test each."
|
|
1346
825
|
},
|
|
1347
826
|
{
|
|
1348
827
|
"id": "backend-database-postgresql-database",
|
|
@@ -1358,20 +837,11 @@
|
|
|
1358
837
|
"stacks": [
|
|
1359
838
|
"dotnet"
|
|
1360
839
|
],
|
|
1361
|
-
"aliases": [
|
|
1362
|
-
"PostgreSQL",
|
|
1363
|
-
"postgres",
|
|
1364
|
-
"pg",
|
|
1365
|
-
"database design",
|
|
1366
|
-
"SQL schema"
|
|
1367
|
-
],
|
|
1368
840
|
"anchors": {
|
|
1369
841
|
"schema": "#schema-design",
|
|
1370
842
|
"indexes": "#indexes"
|
|
1371
843
|
},
|
|
1372
|
-
"digest": "
|
|
1373
|
-
"verifiedAgainst": "PostgreSQL 16/17 + EF Core 10 + @neondatabase/serverless.",
|
|
1374
|
-
"provedBy": []
|
|
844
|
+
"digest": "Neon PostgreSQL schema rules: snake_case plural tables and snake_case columns, PK 'id uuid primary key' with NO column default -- the app sets it with Guid.CreateVersion7() (rows inserted outside the app pass an explicit id; never gen_random_uuid() defaults for app ids), FK '{table}_id', booleans prefixed is_/has_, indexes 'idx_{table}_{column}'. Always include created_at/updated_at as timestamptz default now() not null, kept fresh by a handle_updated_at trigger. Always enable RLS on user-facing tables, with policies like 'auth.user_id() = user_id' (update takes both using and with check). Migrations via EF Core: dotnet ef migrations add, database update, and migrations script --idempotent to review the SQL; hand-written SQL migrations enable RLS, create indexes and always include a rollback. Index FKs and WHERE/ORDER BY/GROUP BY columns; GIN for full-text search. Select only needed columns, limit results, batch inserts, set a statement timeout, keep RLS policies simple. The Next.js frontend reads through the .NET API (proxy + TanStack Query); @neondatabase/serverless is only for Node-side tooling."
|
|
1375
845
|
},
|
|
1376
846
|
{
|
|
1377
847
|
"id": "backend-database-vector-search-rag",
|
|
@@ -1388,18 +858,11 @@
|
|
|
1388
858
|
"nextjs",
|
|
1389
859
|
"neon"
|
|
1390
860
|
],
|
|
1391
|
-
"aliases": [
|
|
1392
|
-
"vector search",
|
|
1393
|
-
"RAG",
|
|
1394
|
-
"embeddings",
|
|
1395
|
-
"pgvector",
|
|
1396
|
-
"semantic search"
|
|
1397
|
-
],
|
|
1398
861
|
"anchors": {
|
|
1399
862
|
"chunking": "#📥-indexação-de-documentos",
|
|
1400
863
|
"retrieval": "#🔍-vector-search-com-ef-core-10"
|
|
1401
864
|
},
|
|
1402
|
-
"digest": "
|
|
865
|
+
"digest": "Pipeline RAG em slices VSA sobre EF Core 10 + PostgreSQL/pgvector (Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, Pgvector.EntityFrameworkCore 0.3.0; Microsoft.Extensions.AI 10.9.0 e Microsoft.Agents.AI 1.20.0 do pin). Mapeamento (Document com `Pgvector.Vector` `vector(1536)`, EF não mapeia `float[]`; `UseVector()` no `UseNpgsql`; HNSW `vector_cosine_ops`) vem de neon-pgvector.md. Cada operação é um handler que injeta `ApplicationDbContext` e `IEmbeddingGenerator<string, Embedding<float>>` direto — sem EmbeddingService/SearchService; ids `Guid.CreateVersion7()`, `CreatedAt` do `TimeProvider`. Busca: `d.Embedding!.CosineDistance(query)` + OrderBy da distância (traduz para `<=>` ASC e usa o índice; o `HasOperators` tem de casar com a função ou cai em seq scan), similaridade = 1 - distância. Slice AskDocuments: top 3 por distância, `NotFound` (Result) antes de chamar o LLM quando nada é relevante, contexto no `instructions` de `chatClient.AsAIAgent(...)`, `RunAsync(...).Text`; prompt manda usar APENAS os documentos e citar o id; endpoint via `error.ToHttpResult()`. Indexação em lote com uma chamada `GenerateAsync(IEnumerable<string>)` e um SaveChanges; job Hangfire só com razão real, como entrada fina que chama o handler. text-embedding-3-small (1536) recomendado, large 3072; HNSW (m=16, ef_construction=64) padrão; IVFFlat só após popular (lists 50/100/500+)."
|
|
1403
866
|
},
|
|
1404
867
|
{
|
|
1405
868
|
"id": "backend-dotnet-fluent-validation-vsa",
|
|
@@ -1415,21 +878,12 @@
|
|
|
1415
878
|
"stacks": [
|
|
1416
879
|
"dotnet"
|
|
1417
880
|
],
|
|
1418
|
-
"aliases": [
|
|
1419
|
-
"FluentValidation VSA",
|
|
1420
|
-
"validation decorator",
|
|
1421
|
-
"validation pipeline",
|
|
1422
|
-
"AbstractValidator",
|
|
1423
|
-
"ValidationDecorator"
|
|
1424
|
-
],
|
|
1425
881
|
"anchors": {
|
|
1426
882
|
"validator": "#validator-structure",
|
|
1427
883
|
"decorator": "#validationdecorator-pattern",
|
|
1428
884
|
"conversion": "#fluentvalidation-to-validationerror-conversion"
|
|
1429
885
|
},
|
|
1430
|
-
"digest": "
|
|
1431
|
-
"verifiedAgainst": ".NET 10 + FluentValidation 11 (`AbstractValidator`/`RuleFor` API).",
|
|
1432
|
-
"provedBy": []
|
|
886
|
+
"digest": "One sealed `AbstractValidator<TRequest>` per request DTO (FluentValidation 12), in the same feature folder as the handler; no `RuleSet` grouping and no `IValidateOptions<T>`. Never validate request shape inside the handler or endpoint (existence/state checks do belong in the handler): `ValidationDecorator<TRequest, TResponse>` wraps every `IHandler` via Scrutor, collects all `IValidator<TRequest>`, runs them in parallel (`Task.WhenAll`) and on failure short-circuits to `Result.Failure(ValidationError)` (an array of `Error.Validation(code, message)`), supporting both `Result` and `Result<T>`. Never throw `ValidationException`. In Program.cs call `AddValidatorsFromAssembly()` BEFORE `AddHandlersFromAssembly()` so the decorator can resolve validators. Always give user-facing `.WithMessage()` text. Inject `TimeProvider` instead of reading `DateTime.UtcNow` and read it INSIDE the rule (`LessThanOrEqualTo(_ => timeProvider.GetUtcNow().Year)`), so the year is evaluated per validation (a value captured in the constructor goes stale in a singleton/cached validator; AddValidatorsFromAssembly defaults to Scoped) and stays testable with `FakeTimeProvider`."
|
|
1433
887
|
},
|
|
1434
888
|
{
|
|
1435
889
|
"id": "backend-dotnet-result-pattern",
|
|
@@ -1445,21 +899,12 @@
|
|
|
1445
899
|
"stacks": [
|
|
1446
900
|
"dotnet"
|
|
1447
901
|
],
|
|
1448
|
-
"aliases": [
|
|
1449
|
-
"Result<T>",
|
|
1450
|
-
"railway oriented",
|
|
1451
|
-
"error handling",
|
|
1452
|
-
"Match pattern",
|
|
1453
|
-
"Result monad"
|
|
1454
|
-
],
|
|
1455
902
|
"anchors": {
|
|
1456
903
|
"anatomy": "#result-anatomy",
|
|
1457
904
|
"errors": "#error-types-7--custom",
|
|
1458
905
|
"match": "#match-pattern"
|
|
1459
906
|
},
|
|
1460
|
-
"digest": "
|
|
1461
|
-
"verifiedAgainst": ".NET 10 (pure C# pattern — no external package).",
|
|
1462
|
-
"provedBy": []
|
|
907
|
+
"digest": "Expected failures are Result/Result<T> values, never exceptions. Endpoints convert with result.Match(onSuccess: value => Results.Ok(value), onFailure: error => error.ToHttpResult()); never read .Value outside Match (it throws on failure). Implicit conversions let a handler return the value or the Error directly. Error = record(Code, Description, ErrorType) from factories Failure/Unexpected/Validation/Conflict/NotFound/Unauthorized/Forbidden; ValidationError aggregates FluentValidation errors. Status comes from ErrorType, never a blanket BadRequest: Failure/Validation/Custom 400, NotFound 404, Conflict 409, Unauthorized 401, Forbidden 403, Unexpected 500, via Results.Problem. Each feature owns a static {Feature}Errors class. FORBIDDEN: try/catch in handlers (unexpected exceptions bubble to the global handler), returning null, codeless string errors."
|
|
1463
908
|
},
|
|
1464
909
|
{
|
|
1465
910
|
"id": "backend-dotnet-scalar-ui",
|
|
@@ -1475,21 +920,12 @@
|
|
|
1475
920
|
"stacks": [
|
|
1476
921
|
"dotnet"
|
|
1477
922
|
],
|
|
1478
|
-
"aliases": [
|
|
1479
|
-
"Scalar",
|
|
1480
|
-
"API documentation",
|
|
1481
|
-
"OpenAPI UI",
|
|
1482
|
-
"Swagger replacement",
|
|
1483
|
-
"Scalar.AspNetCore"
|
|
1484
|
-
],
|
|
1485
923
|
"anchors": {
|
|
1486
924
|
"setup": "#programcs-configuration",
|
|
1487
925
|
"tags": "#apitags-organization",
|
|
1488
926
|
"metadata": "#endpoint-metadata"
|
|
1489
927
|
},
|
|
1490
|
-
"digest": "
|
|
1491
|
-
"verifiedAgainst": ".NET 10 + Scalar.AspNetCore 2.x + Microsoft.AspNetCore.OpenApi 10.",
|
|
1492
|
-
"provedBy": []
|
|
928
|
+
"digest": "Scalar replaces Swagger UI: packages Microsoft.AspNetCore.OpenApi 10.0.0 + Scalar.AspNetCore 2.12.40, never Swashbuckle (ASP.NET Core 9+ generates OpenAPI natively). Program.cs order: builder.Services.AddOpenApi(); app.MapOpenApi() only inside IsDevelopment() (never expose OpenAPI in production); then app.MapScalarApiReference with WithTheme(ScalarTheme.DeepSpace) and WithDefaultHttpClient(ScalarTarget.CSharp, ScalarClient.HttpClient). MapOpenApi must come before Scalar; the UI is served at /scalar/v1. Every endpoint needs .WithTags(ApiTags.X), with tags as constants in Constants/ApiTags.cs (no magic strings), and .Produces<T>(status) for success plus .ProducesProblem(status) for every failure status the slice can return (failures go out as ProblemDetails via error.ToHttpResult()), so the docs show accurate response types. Also available: .WithName (operation id), .WithSummary, .WithDescription, .ProducesProblem, .RequireAuthorization."
|
|
1493
929
|
},
|
|
1494
930
|
{
|
|
1495
931
|
"id": "backend-dotnet-scrutor",
|
|
@@ -1505,21 +941,12 @@
|
|
|
1505
941
|
"stacks": [
|
|
1506
942
|
"dotnet"
|
|
1507
943
|
],
|
|
1508
|
-
"aliases": [
|
|
1509
|
-
"Scrutor",
|
|
1510
|
-
"auto-discovery",
|
|
1511
|
-
"assembly scanning",
|
|
1512
|
-
"decorator registration",
|
|
1513
|
-
"handler registration"
|
|
1514
|
-
],
|
|
1515
944
|
"anchors": {
|
|
1516
945
|
"handlers": "#handler-auto-discovery",
|
|
1517
946
|
"decorators": "#decorator-order",
|
|
1518
947
|
"endpoints": "#endpoint-auto-discovery"
|
|
1519
948
|
},
|
|
1520
|
-
"digest": "
|
|
1521
|
-
"verifiedAgainst": ".NET 10 + Scrutor 7 (`Scan`/`Decorate` API).",
|
|
1522
|
-
"provedBy": []
|
|
949
|
+
"digest": "Scrutor 7 (.NET 10) handler/decorator registration for VSA. `AddHandlersFromAssembly` reflects over concrete classes filtered by `!t.ContainsGenericParameters` (so open-generic decorators are excluded), registers each `IHandler<,>` as `AddScoped` (handlers use DbContext, never Singleton), then calls `Decorate` for `ValidationDecorator<,>` first and `LoggingDecorator<,>` second: the last registered wraps outermost, so execution is Logging -> Validation -> Handler and logging sees validation failures. `RegisterApiEndpointsFromAssembly` adds every `IApiEndpoint` as Transient via `TryAddEnumerable`; `MapApiEndpoints` calls `MapEndpoint` on each. Program.cs order: DbContext, endpoints, `AddValidatorsFromAssembly` (must precede handlers so the ValidationDecorator can resolve validators), handlers + decorators, cross-cutting. Never register handlers one by one."
|
|
1523
950
|
},
|
|
1524
951
|
{
|
|
1525
952
|
"id": "backend-dotnet-testing",
|
|
@@ -1534,26 +961,12 @@
|
|
|
1534
961
|
"stacks": [
|
|
1535
962
|
"dotnet"
|
|
1536
963
|
],
|
|
1537
|
-
"aliases": [
|
|
1538
|
-
"xunit",
|
|
1539
|
-
"TimeProvider",
|
|
1540
|
-
"FakeTimeProvider",
|
|
1541
|
-
"fake clock",
|
|
1542
|
-
"integration test",
|
|
1543
|
-
"ProblemDetails",
|
|
1544
|
-
"error body assertion",
|
|
1545
|
-
"test budget",
|
|
1546
|
-
"orçamento de teste",
|
|
1547
|
-
"[Theory] table"
|
|
1548
|
-
],
|
|
1549
964
|
"anchors": {
|
|
1550
965
|
"clock": "#rule-1--inject-timeprovider-fake-it-with-faketimeprovider-in-tests",
|
|
1551
966
|
"errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code",
|
|
1552
967
|
"test-budget": "#test-budget"
|
|
1553
968
|
},
|
|
1554
|
-
"digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior (edge cases included) are rows of one [Theory], never copied [Fact]s; no test whose only assertion is a copy, config or prose string (that is an analyzer or a publish-time validation, outside the suite count); shared fixtures before new helpers; bugs and pure logic get tests in any order — the implementer runs its own filter once, never red-green cycles nor production mutation (the Gate 3 mutation proof shows the test bites); a surviving mutant is killed by tightening an assertion or adding a row, not by a new test."
|
|
1555
|
-
"verifiedAgainst": ".NET 10 + xUnit 2 + `Microsoft.Extensions.TimeProvider.Testing`.",
|
|
1556
|
-
"provedBy": []
|
|
969
|
+
"digest": "Inject TimeProvider (never DateTime.UtcNow / DateOnly.FromDateTime(DateTime.UtcNow) directly in a handler or validator) and fake it with FakeTimeProvider (Microsoft.Extensions.TimeProvider.Testing) in integration tests — deterministic, no midnight-UTC flake, boundary dates (today vs. yesterday) become facts about the fake clock instead of when the suite happens to run. xUnit + WebApplicationFactory error-path tests (400/404/409) must assert the ProblemDetails response BODY (title/detail/violated field or error code), never just the HTTP status code — a status-only assertion lets the wrong validation rule or the wrong error branch pass silently as long as the status matches. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior (edge cases included) are rows of one [Theory], never copied [Fact]s; no test whose only assertion is a copy, config or prose string (that is an analyzer or a publish-time validation, outside the suite count); shared fixtures before new helpers; bugs and pure logic get tests in any order — the implementer runs its own filter once, never red-green cycles nor production mutation (the Gate 3 mutation proof shows the test bites); a surviving mutant is killed by tightening an assertion or adding a row, not by a new test."
|
|
1557
970
|
},
|
|
1558
971
|
{
|
|
1559
972
|
"id": "backend-dotnet-vsa-handler-patterns",
|
|
@@ -1569,22 +982,13 @@
|
|
|
1569
982
|
"stacks": [
|
|
1570
983
|
"dotnet"
|
|
1571
984
|
],
|
|
1572
|
-
"aliases": [
|
|
1573
|
-
"VSA handler",
|
|
1574
|
-
"vertical slice handler",
|
|
1575
|
-
"feature slice",
|
|
1576
|
-
"IHandler pattern",
|
|
1577
|
-
"handler endpoint"
|
|
1578
|
-
],
|
|
1579
985
|
"anchors": {
|
|
1580
986
|
"handler": "#ihandlertrequest-tresponse",
|
|
1581
987
|
"folder": "#feature-folder-structure-5-files-per-slice",
|
|
1582
988
|
"endpoint": "#complete-endpoint-example",
|
|
1583
989
|
"registration": "#programcs-di-registration-order"
|
|
1584
990
|
},
|
|
1585
|
-
"digest": "Each VSA feature is 5 files: Request, Response, Handler, Validator, Endpoint.
|
|
1586
|
-
"verifiedAgainst": ".NET 10 (minimal APIs, primary constructors, `Guid.CreateVersion7()`).",
|
|
1587
|
-
"provedBy": []
|
|
991
|
+
"digest": "Each VSA feature is a slice in `Features/{Feature}/{UseCase}/` with 5 files: Request, Response (sealed records, may be inline), Handler, Validator, Endpoint; feature errors in `{Feature}Errors.cs` (`Error.NotFound/Conflict/Failure`). Handlers implement `IHandler<TRequest, TResponse>` (resolved from DI; NOT MediatR, decorators via Scrutor), are `sealed class` with primary constructors, inject `ApplicationDbContext` directly (`DbSet<T>` is the repository, `DbContext` the unit of work: no service layer, no `IRepository<T>`/`IUnitOfWork`), use `Guid.CreateVersion7()` (never `Guid.NewGuid()`), pass `CancellationToken` everywhere and return `Result<T>` instead of throwing for expected failures. Endpoints are `internal sealed` `IApiEndpoint`, only map HTTP and call `Match()`: success lambda receives the value (never read `.Value`), failure is `error.ToHttpResult()` (status by `ErrorType`, never `Results.BadRequest` for every error), with `.ProducesProblem` per failure status. Raw SQL or a dedicated hand-written store (outbox, lease) only with a `decisions.md` justification."
|
|
1588
992
|
},
|
|
1589
993
|
{
|
|
1590
994
|
"id": "backend-integrations-asaas-asaas-api",
|
|
@@ -1600,20 +1004,11 @@
|
|
|
1600
1004
|
"stacks": [
|
|
1601
1005
|
"dotnet"
|
|
1602
1006
|
],
|
|
1603
|
-
"aliases": [
|
|
1604
|
-
"asaas",
|
|
1605
|
-
"payment",
|
|
1606
|
-
"PIX",
|
|
1607
|
-
"boleto",
|
|
1608
|
-
"Brazilian payment"
|
|
1609
|
-
],
|
|
1610
1007
|
"anchors": {
|
|
1611
1008
|
"payments": "#create-pix-payment",
|
|
1612
1009
|
"webhooks": "#webhook-handler"
|
|
1613
1010
|
},
|
|
1614
|
-
"digest": "
|
|
1615
|
-
"verifiedAgainst": "Asaas REST API v3 (no official Node SDK — direct REST).",
|
|
1616
|
-
"provedBy": []
|
|
1011
|
+
"digest": "Asaas from the .NET 10 backend (the Next.js frontend never calls Asaas; it calls the backend through the proxy). No official .NET SDK: REST v3 through a typed HttpClient `AsaasClient` (an external-API port, injected into the slice handler next to the DbContext), registered with AddHttpClient + options bound from config `Asaas` (ValidateOnStart; key in user-secrets/Coolify env, never in code). Base URL https://api.asaas.com/v3/ or https://api-sandbox.asaas.com/v3/ WITH trailing slash and relative paths without leading slash (otherwise HttpClient drops /v3). Every request sends `access_token` (not Bearer), Content-Type and `User-Agent` (mandatory for root accounts created from 06/11/2024). Responses become Result: 400 `{ errors: [{ code, description }] }` -> Error.Validation (ValidationError if several, e.g. invalid_cpfCnpj), 404 -> NotFound, other non-success -> Unexpected; endpoints map with error.ToHttpResult(); no try/catch, network failures propagate. No duplicate charges: Asaas documents no idempotency-key header and create is not idempotent, so set `externalReference` to your id and `GET /payments?externalReference=` before creating; never auto-retry payWithCreditCard. PIX: POST /payments billingType PIX with dueDate as today in America/Sao_Paulo via TimeProvider, then GET /payments/{id}/pixQrCode (payload + encodedImage). Webhooks are not HMAC-signed: compare the `asaas-access-token` header to the configured authToken with CryptographicOperations.FixedTimeEquals, 401 on mismatch; delivery is at-least-once, so store processed event ids (unique) and answer 200 (only 200 counts; 15 consecutive failures interrupt the queue). Validate CPF (11 digits) and CNPJ (14, alphanumeric from July 2026, ASCII-48 mod-11), never store card data; local ids via Guid.CreateVersion7(), no gen_random_uuid defaults."
|
|
1617
1012
|
},
|
|
1618
1013
|
{
|
|
1619
1014
|
"id": "backend-integrations-hangfire-hangfire-jobs",
|
|
@@ -1629,21 +1024,11 @@
|
|
|
1629
1024
|
"stacks": [
|
|
1630
1025
|
"dotnet"
|
|
1631
1026
|
],
|
|
1632
|
-
"aliases": [
|
|
1633
|
-
"hangfire",
|
|
1634
|
-
"background jobs",
|
|
1635
|
-
"scheduled jobs",
|
|
1636
|
-
"recurring",
|
|
1637
|
-
"cron",
|
|
1638
|
-
"fire and forget"
|
|
1639
|
-
],
|
|
1640
1027
|
"anchors": {
|
|
1641
1028
|
"jobs": "#job-types",
|
|
1642
1029
|
"retry": "#retry--error-handling"
|
|
1643
1030
|
},
|
|
1644
|
-
"digest": "
|
|
1645
|
-
"verifiedAgainst": ".NET 10 + Hangfire 1.8 (Core/SqlServer/AspNetCore).",
|
|
1646
|
-
"provedBy": []
|
|
1031
|
+
"digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in decisions.md; work that fits in the request stays in the slice handler. Storage is Neon Postgres via Hangfire.PostgreSql 1.21.1 with `UsePostgreSqlStorage(o => o.UseNpgsqlConnection(cs))` (the string overloads are [Obsolete]) and `HangfireConnection` on the direct/unpooled endpoint (schema install at startup is a migration, which Neon routes to the direct string; EnableLongPolling uses LISTEN/NOTIFY, unsupported by Neon's PgBouncer transaction mode); reference Newtonsoft.Json 13.0.4 explicitly or restore fails with NU1903 (Hangfire.Core pulls 11.0.1). A job is a thin sealed class in the slice that calls the slice handler (`IHandler<TRequest, Result>`); no XService layer. Hangfire.AspNetCore's AspNetCoreJobActivator opens a DI scope per job, so inject the ApplicationDbContext/handler directly (IDbContextFactory only for parallel work inside one job); never capture request-scoped services into Enqueue. Jobs are idempotent (check state, return). Expected failures come back as Result and the job logs and completes; unexpected ones are the exceptions EF/HttpClient already throw, uncaught, which `[AutomaticRetry(Attempts = 3, DelaysInSeconds = [60, 300, 900])]` retries; no try/catch, no manual retry loops. API: `BackgroundJob.Enqueue<TJob>(j => j.RunAsync(id, CancellationToken.None))`, `Schedule`, `RecurringJob.AddOrUpdate` with a stable id and cron (UTC), `ContinueJobWith`. Dashboard: IDashboardAuthorizationFilter requiring authenticated Admin, dev bypass only via IHostEnvironment.IsDevelopment() (never Request.Host, which the client controls)."
|
|
1647
1032
|
},
|
|
1648
1033
|
{
|
|
1649
1034
|
"id": "backend-integrations-neon-auth-neon-auth",
|
|
@@ -1660,26 +1045,6 @@
|
|
|
1660
1045
|
"stacks": [
|
|
1661
1046
|
"neon"
|
|
1662
1047
|
],
|
|
1663
|
-
"aliases": [
|
|
1664
|
-
"neon auth",
|
|
1665
|
-
"better auth",
|
|
1666
|
-
"authentication",
|
|
1667
|
-
"OAuth",
|
|
1668
|
-
"JWT",
|
|
1669
|
-
"session management",
|
|
1670
|
-
"trusted origins",
|
|
1671
|
-
"EdDSA",
|
|
1672
|
-
"Ed25519",
|
|
1673
|
-
"BouncyCastle",
|
|
1674
|
-
"signature validator",
|
|
1675
|
-
"jwks",
|
|
1676
|
-
"middleware runtime",
|
|
1677
|
-
"api prefix",
|
|
1678
|
-
"data api",
|
|
1679
|
-
"postgrest",
|
|
1680
|
-
"bypass backend",
|
|
1681
|
-
"direct database access"
|
|
1682
|
-
],
|
|
1683
1048
|
"anchors": {
|
|
1684
1049
|
"setup": "#installation--setup",
|
|
1685
1050
|
"data-access-boundary": "#data-access-boundary--backend-first-by-default",
|
|
@@ -1690,9 +1055,7 @@
|
|
|
1690
1055
|
"rls": "#row-level-security-integration",
|
|
1691
1056
|
"postmortem": "#known-failure-modes-postmortem"
|
|
1692
1057
|
},
|
|
1693
|
-
"digest": "Use @neondatabase/auth SDK for Next.js. Auth data lives in neon_auth schema — no external provider, no webhooks. Server: createNeonAuth() with NEON_AUTH_BASE_URL + NEON_AUTH_COOKIE_SECRET. Client: createAuthClient(). Pages: AuthView component. Protection: auth.middleware() with runtime='nodejs' (Edge times out minting the session cookie). Register every dev/preview origin in trusted_origins before testing login. Two named prefix constants (BACKEND_API_PREFIX / PROXY_API_PREFIX) — never share one between server-direct and client-proxy calls. Frontend never queries Neon directly for business data by default — only its own auth/session SDK; a Data API bypass requires a decisions.md ADR. .NET: tokens are EdDSA/Ed25519 (OKP) — Microsoft.IdentityModel.Tokens can't verify OKP keys and there's no OIDC discovery; fetch+cache the JWKS yourself and verify signatures manually via BouncyCastle.Cryptography + TokenValidationParameters.SignatureValidator (must return Microsoft.IdentityModel.JsonWebTokens.JsonWebToken, not JwtSecurityToken — else IDX10506). NeonAuth:BaseUrl is mandatory in appsettings.Development.json (public value). RLS: auth.user_id() via pg_session_jwt."
|
|
1694
|
-
"verifiedAgainst": "Neon Auth — @neondatabase/auth + @neondatabase/auth-ui (Better Auth).",
|
|
1695
|
-
"provedBy": []
|
|
1058
|
+
"digest": "Use @neondatabase/auth SDK for Next.js. Auth data lives in neon_auth schema — no external provider, no webhooks. Server: createNeonAuth() with NEON_AUTH_BASE_URL + NEON_AUTH_COOKIE_SECRET. Client: createAuthClient(). Pages: AuthView component. Protection: auth.middleware() with runtime='nodejs' (Edge times out minting the session cookie). Register every dev/preview origin in trusted_origins before testing login. Two named prefix constants (BACKEND_API_PREFIX / PROXY_API_PREFIX) — never share one between server-direct and client-proxy calls. Frontend never queries Neon directly for business data by default — only its own auth/session SDK; a Data API bypass requires a decisions.md ADR. .NET: tokens are EdDSA/Ed25519 (OKP) — Microsoft.IdentityModel.Tokens can't verify OKP keys and there's no OIDC discovery; fetch+cache the JWKS yourself and verify signatures manually via BouncyCastle.Cryptography + TokenValidationParameters.SignatureValidator (must return Microsoft.IdentityModel.JsonWebTokens.JsonWebToken, not JwtSecurityToken — else IDX10506). NeonAuth:BaseUrl is mandatory in appsettings.Development.json (public value). RLS: auth.user_id() via pg_session_jwt."
|
|
1696
1059
|
},
|
|
1697
1060
|
{
|
|
1698
1061
|
"id": "backend-integrations-resend-resend-email",
|
|
@@ -1708,20 +1071,11 @@
|
|
|
1708
1071
|
"stacks": [
|
|
1709
1072
|
"dotnet"
|
|
1710
1073
|
],
|
|
1711
|
-
"aliases": [
|
|
1712
|
-
"resend",
|
|
1713
|
-
"email",
|
|
1714
|
-
"transactional email",
|
|
1715
|
-
"SMTP",
|
|
1716
|
-
"email notification"
|
|
1717
|
-
],
|
|
1718
1074
|
"anchors": {
|
|
1719
1075
|
"sending": "#send-email-api-route",
|
|
1720
1076
|
"templates": "#common-email-templates"
|
|
1721
1077
|
},
|
|
1722
|
-
"digest": "
|
|
1723
|
-
"verifiedAgainst": "Resend Node SDK 4 + Resend .NET SDK + React Email.",
|
|
1724
|
-
"provedBy": []
|
|
1078
|
+
"digest": "Resend transactional email via REST API (not SMTP; 100 free emails/day) for Next.js and .NET. Next.js: `resend` + React Email templates, one `new Resend(process.env.RESEND_API_KEY)` in `lib/resend.ts`. .NET: `Resend` package, `AddHttpClient<ResendClient>()` plus `IResend` registered transient, token from the `Resend:ApiKey` config. Always send asynchronously: do not block HTTP requests, enqueue with Hangfire (`BackgroundJob.Enqueue`); retry with Polly (3 attempts, exponential backoff); rate-limit sends (Upstash sliding window, 10/hour). Webhooks are signed with Svix: verify the RAW body with `resend.webhooks.verify()` using the `svix-id`/`svix-timestamp`/`svix-signature` headers and `RESEND_WEBHOOK_SECRET`; do NOT hand-roll an HMAC (it rejects valid webhooks). Handle `email.delivered`/`bounced`/`complained`. Verify the sending domain first (SPF, DKIM, DMARC; up to 72 hours)."
|
|
1725
1079
|
},
|
|
1726
1080
|
{
|
|
1727
1081
|
"id": "data-nosql-cache-redis",
|
|
@@ -1738,21 +1092,11 @@
|
|
|
1738
1092
|
"dotnet",
|
|
1739
1093
|
"azure"
|
|
1740
1094
|
],
|
|
1741
|
-
"aliases": [
|
|
1742
|
-
"redis",
|
|
1743
|
-
"cache",
|
|
1744
|
-
"distributed cache",
|
|
1745
|
-
"IDistributedCache",
|
|
1746
|
-
"cache-aside",
|
|
1747
|
-
"StackExchange.Redis"
|
|
1748
|
-
],
|
|
1749
1095
|
"anchors": {
|
|
1750
1096
|
"patterns": "#cache-aside-pattern",
|
|
1751
1097
|
"invalidation": "#ttl-strategy"
|
|
1752
1098
|
},
|
|
1753
|
-
"digest": "
|
|
1754
|
-
"verifiedAgainst": ".NET 10 + StackExchange.Redis 2.x + Microsoft.Extensions.Caching.StackExchangeRedis.",
|
|
1755
|
-
"provedBy": []
|
|
1099
|
+
"digest": "Redis (on the Coolify VPS or managed) is a distributed cache for sessions, rate limiting and hot reads, never the primary store. Packages: Microsoft.Extensions.Caching.StackExchangeRedis 10.0.12 (depends on StackExchange.Redis >= 2.7.27); reference StackExchange.Redis explicitly at the latest 2.x (2.13.17) only when injecting IConnectionMultiplexer -- 3.x is a new major the caching package has not adopted. Register one shared IConnectionMultiplexer as a LAZY singleton and hand it to the cache via AddOptions<RedisCacheOptions>().Configure<IServiceProvider>(... ConnectionMultiplexerFactory ...) -- never Connect during registration (an unreachable Redis would block boot) and never BuildServiceProvider inside the factory, InstanceName prefix (e.g. 'myapp:'), connection string from configuration (user-secrets / Coolify env ConnectionStrings__Redis). Cache-aside lives in the slice handler with IDistributedCache next to the ApplicationDbContext (no CachedXService/repository wrapper): read the key, on a miss project the response DTO from the DbContext, return Result NotFound (never null) when absent, write with DistributedCacheEntryOptions; the writing handler invalidates the exact key with RemoveAsync after SaveChangesAsync; key builders in one static class per feature. TTLs: user session 30 min sliding, product catalog 15 min absolute, rate-limit counters 1 min, computed aggregates 1 h, reference data 24 h. Rate limit across instances: StringIncrementAsync, KeyExpireAsync 1 min when count == 1, minute from TimeProvider (single instance: built-in AddRateLimiter). FORBIDDEN: caching passwords/tokens/PII without encryption, omitting TTL, swallowing RedisConnectionException in an empty catch (DB fallback during an outage is a decisions.md decision, implemented once at the cache boundary)."
|
|
1756
1100
|
},
|
|
1757
1101
|
{
|
|
1758
1102
|
"id": "data-nosql-cosmos-db",
|
|
@@ -1768,20 +1112,11 @@
|
|
|
1768
1112
|
"dotnet",
|
|
1769
1113
|
"azure"
|
|
1770
1114
|
],
|
|
1771
|
-
"aliases": [
|
|
1772
|
-
"cosmos",
|
|
1773
|
-
"CosmosDB",
|
|
1774
|
-
"document database",
|
|
1775
|
-
"partition key",
|
|
1776
|
-
"RUs"
|
|
1777
|
-
],
|
|
1778
1115
|
"anchors": {
|
|
1779
1116
|
"modeling": "#container-design",
|
|
1780
|
-
"queries": "#
|
|
1117
|
+
"queries": "#handler-in-the-slice"
|
|
1781
1118
|
},
|
|
1782
|
-
"digest": "
|
|
1783
|
-
"verifiedAgainst": ".NET 10 + Microsoft.Azure.Cosmos SDK v3.",
|
|
1784
|
-
"provedBy": []
|
|
1119
|
+
"digest": "OPTIONAL — outside the baked-in stack. The default store is Neon (Postgres) via EF Core; Cosmos needs a decisions.md entry saying why Neon does not serve (multi-region writes, event-stream throughput, per-tenant schema never joined) — never for joined data, small datasets or 'flexible schema' (jsonb covers it). Stack rules still hold: one singleton CosmosClient with System.Text.Json, the Container as a keyed singleton injected straight into the slice handler (no CosmosRepository<T>), ids from Guid.CreateVersion7(), time from TimeProvider. Use ReadItemStreamAsync/CreateItemStreamAsync and map StatusCode 404/409 to Result errors instead of catching CosmosException. Parameterized queries with the PartitionKey pinned in QueryRequestOptions (no fan-out). Microsoft.Azure.Cosmos 3.x needs an explicit Newtonsoft.Json reference or the build fails."
|
|
1785
1120
|
},
|
|
1786
1121
|
{
|
|
1787
1122
|
"id": "data-vector-search-rag-chunking",
|
|
@@ -1797,20 +1132,11 @@
|
|
|
1797
1132
|
"dotnet",
|
|
1798
1133
|
"azure"
|
|
1799
1134
|
],
|
|
1800
|
-
"aliases": [
|
|
1801
|
-
"RAG chunking",
|
|
1802
|
-
"document chunking",
|
|
1803
|
-
"text splitting",
|
|
1804
|
-
"chunk overlap",
|
|
1805
|
-
"embedding chunks"
|
|
1806
|
-
],
|
|
1807
1135
|
"anchors": {
|
|
1808
1136
|
"strategies": "#chunking-strategies",
|
|
1809
1137
|
"retrieval": "#chunking-strategies"
|
|
1810
1138
|
},
|
|
1811
|
-
"digest": "
|
|
1812
|
-
"verifiedAgainst": ".NET 10 (chunking algorithms — language-level, no external package).",
|
|
1813
|
-
"provedBy": []
|
|
1139
|
+
"digest": "Strategies: fixed-size baseline (sample defaults size 512, overlap 50, applied to characters), sentence-aware (recommended for prose, `maxTokens` 256) and semantic (best quality, higher cost; split at markdown headers `^#{1,3} `). By document type: legal -> sentence-aware 256 tokens; technical docs -> semantic by headers; Q&A -> keep whole Q+A; code -> whole function; news -> paragraph, 512 tokens. Estimate tokens as chars/4 for English (about 3 chars per token for Portuguese), or tiktoken if available. Always store chunk metadata (DocumentId, DocumentTitle, SourceUrl, ChunkIndex, StartCharIndex, Section, IndexedAt) for filtering and citation. Checklist: overlap 10-20% so boundaries do not cut context, minimum 50 tokens, maximum about 512 (far below the 8192-token embedding input limit), and re-index when a document changes, detected via a content hash."
|
|
1814
1140
|
},
|
|
1815
1141
|
{
|
|
1816
1142
|
"id": "frontend-design-system-ai-image-generation",
|
|
@@ -1829,16 +1155,6 @@
|
|
|
1829
1155
|
"stacks": [
|
|
1830
1156
|
"*"
|
|
1831
1157
|
],
|
|
1832
|
-
"aliases": [
|
|
1833
|
-
"Image Generation",
|
|
1834
|
-
"AI Assets",
|
|
1835
|
-
"Gemini Images",
|
|
1836
|
-
"brand fidelity",
|
|
1837
|
-
"reference images",
|
|
1838
|
-
"favicon",
|
|
1839
|
-
"og image",
|
|
1840
|
-
"asset generation"
|
|
1841
|
-
],
|
|
1842
1158
|
"anchors": {
|
|
1843
1159
|
"when-to-use": "#when-to-use-ai-generated-images",
|
|
1844
1160
|
"prompt-patterns": "#prompt-engineering-patterns",
|
|
@@ -1850,9 +1166,7 @@
|
|
|
1850
1166
|
"favicon-og": "#the-two-assets-everyone-forgets",
|
|
1851
1167
|
"variations": "#variations-and-cost"
|
|
1852
1168
|
},
|
|
1853
|
-
"digest": "Guidelines for generating project images with the morph-assets MCP (OpenRouter; default openai/gpt-image-2.5-flare, drafts on mai-image-2.6-flash, gemini-3.1-flash-image for 2K/4K or 6-14 refs; models: [...] compares): when to use AI versus stock versus SVG, prompt patterns, aspect ratios, iterative editing and assets.md format. Core principle is brand fidelity over invention: always pass the client's real assets as references (or @brand with a project) via edit_image rather than generating in a vacuum, never AI-recreate a property developer's architectural render or a physical product (animate or reference the real one), and chain derivatives from an approved hero so the whole set shares light, palette and identity. Frame anything destined to become video WITHOUT a face, because video moderation is stricter (E005), and generate scrub frames at the video's exact ratio with the subject opposite the copy. Always produce the two forgotten assets: a favicon derived from the real logo (never a new one) and a 1200x630 OG share image. Offer two to three genuinely different variations per image (variants returns a contact sheet), varying angle, light or composition since seed is not supported by the GPT Image/Gemini models, and never advance with a weak image because every derivative inherits it. Copy to render goes between double quotes; CONFIRMATION_REQUIRED needs human approval and a confirmToken."
|
|
1854
|
-
"verifiedAgainst": "morph-assets MCP (OpenRouter; default model `openai/gpt-image-2.5-flare`) + Next.js 15 `next/image`.",
|
|
1855
|
-
"provedBy": []
|
|
1169
|
+
"digest": "Guidelines for generating project images with the morph-assets MCP (OpenRouter; default openai/gpt-image-2.5-flare, drafts on mai-image-2.6-flash, gemini-3.1-flash-image for 2K/4K or 6-14 refs; models: [...] compares): when to use AI versus stock versus SVG, prompt patterns, aspect ratios, iterative editing and assets.md format. Core principle is brand fidelity over invention: always pass the client's real assets as references (or @brand with a project) via edit_image rather than generating in a vacuum, never AI-recreate a property developer's architectural render or a physical product (animate or reference the real one), and chain derivatives from an approved hero so the whole set shares light, palette and identity. Frame anything destined to become video WITHOUT a face, because video moderation is stricter (E005), and generate scrub frames at the video's exact ratio with the subject opposite the copy. Always produce the two forgotten assets: a favicon derived from the real logo (never a new one) and a 1200x630 OG share image. Offer two to three genuinely different variations per image (variants returns a contact sheet), varying angle, light or composition since seed is not supported by the GPT Image/Gemini models, and never advance with a weak image because every derivative inherits it. Copy to render goes between double quotes; CONFIRMATION_REQUIRED needs human approval and a confirmToken."
|
|
1856
1170
|
},
|
|
1857
1171
|
{
|
|
1858
1172
|
"id": "frontend-design-system-ai-video-generation",
|
|
@@ -1871,17 +1185,6 @@
|
|
|
1871
1185
|
"stacks": [
|
|
1872
1186
|
"*"
|
|
1873
1187
|
],
|
|
1874
|
-
"aliases": [
|
|
1875
|
-
"video generation",
|
|
1876
|
-
"seedance",
|
|
1877
|
-
"img2vid",
|
|
1878
|
-
"endImage",
|
|
1879
|
-
"directional footage",
|
|
1880
|
-
"loop video",
|
|
1881
|
-
"E005",
|
|
1882
|
-
"animate render",
|
|
1883
|
-
"scroll scrub footage"
|
|
1884
|
-
],
|
|
1885
1188
|
"anchors": {
|
|
1886
1189
|
"loop-vs-directional": "#section-1-the-distinction-that-decides-everything",
|
|
1887
1190
|
"directional": "#section-2-building-directional-footage",
|
|
@@ -1889,9 +1192,7 @@
|
|
|
1889
1192
|
"cost-ladder": "#section-4-the-cost-ladder",
|
|
1890
1193
|
"prompt-skeleton": "#section-5-prompt-skeleton"
|
|
1891
1194
|
},
|
|
1892
|
-
"digest": "The decisive rule: a perfect loop has an end frame identical to its start, which is right for a self-playing ambient background and WRONG for scroll scrubbing, because start and end look the same and the page reads as frozen while the user scrolls. Scroll-driven footage needs directional movement via generate_video with an endImage (push-in/dolly is the most reliable, then reveal, pull-back, slow tilt); avoid orbits and any movement that returns to its origin. Client architectural renders are animated with img2vid, never AI-recreated. Human faces repeatedly trip moderation (E005), so crop the face out of frame (neck down, hands, product) and reserve faces for static image generation. Always climb the cost ladder: seedance-1-5-pro 480p draft to judge framing and movement, then promoteFrom to seedance-2.0 1080p only after approval (same prompt, frames and seed), then re-extract frames with a bumped cache-bust. seedance-2.0-mini does NOT respect endImage and Veo 3.1 Lite/Fast are inconclusive, so scrub only on Seedance 1.5 Pro/2.0 and check the last-frame SSIM QA. Video has no ZDR on OpenRouter: nothing sensitive in the frames. Prompts read like director notes with no hype words, and must reserve a calm dark region for the page copy with the subject on the opposite side. Generation is async via the morph-assets MCP (OpenRouter): a running job is polled with get_video (several ids at once), never by resending generate_video; library reads local history and spends no credits; media_tool extracts scrub frames and posters."
|
|
1893
|
-
"verifiedAgainst": "morph-assets MCP (generate_video, generate_loop_video, get_video, library, media_tool) over OpenRouter; models `bytedance/seedance-1-5-pro` and `bytedance/seedance-2.0`.",
|
|
1894
|
-
"provedBy": []
|
|
1195
|
+
"digest": "The decisive rule: a perfect loop has an end frame identical to its start, which is right for a self-playing ambient background and WRONG for scroll scrubbing, because start and end look the same and the page reads as frozen while the user scrolls. Scroll-driven footage needs directional movement via generate_video with an endImage (push-in/dolly is the most reliable, then reveal, pull-back, slow tilt); avoid orbits and any movement that returns to its origin. Client architectural renders are animated with img2vid, never AI-recreated. Human faces repeatedly trip moderation (E005), so crop the face out of frame (neck down, hands, product) and reserve faces for static image generation. Always climb the cost ladder: seedance-1-5-pro 480p draft to judge framing and movement, then promoteFrom to seedance-2.0 1080p only after approval (same prompt, frames and seed), then re-extract frames with a bumped cache-bust. seedance-2.0-mini does NOT respect endImage and Veo 3.1 Lite/Fast are inconclusive, so scrub only on Seedance 1.5 Pro/2.0 and check the last-frame SSIM QA. Video has no ZDR on OpenRouter: nothing sensitive in the frames. Prompts read like director notes with no hype words, and must reserve a calm dark region for the page copy with the subject on the opposite side. Generation is async via the morph-assets MCP (OpenRouter): a running job is polled with get_video (several ids at once), never by resending generate_video; library reads local history and spends no credits; media_tool extracts scrub frames and posters."
|
|
1895
1196
|
},
|
|
1896
1197
|
{
|
|
1897
1198
|
"id": "frontend-design-system-animations",
|
|
@@ -1906,21 +1207,11 @@
|
|
|
1906
1207
|
"stacks": [
|
|
1907
1208
|
"*"
|
|
1908
1209
|
],
|
|
1909
|
-
"aliases": [
|
|
1910
|
-
"animations",
|
|
1911
|
-
"transitions",
|
|
1912
|
-
"motion",
|
|
1913
|
-
"CSS animations",
|
|
1914
|
-
"keyframes",
|
|
1915
|
-
"prefers-reduced-motion"
|
|
1916
|
-
],
|
|
1917
1210
|
"anchors": {
|
|
1918
1211
|
"tokens": "#stagger--timing",
|
|
1919
1212
|
"principles": "#css-animation-patterns"
|
|
1920
1213
|
},
|
|
1921
|
-
"digest": "
|
|
1922
|
-
"verifiedAgainst": "CSS animations/transitions (language-level, no framework).",
|
|
1923
|
-
"provedBy": []
|
|
1214
|
+
"digest": "CSS animation library for design-system.css: keyframes (fadeIn/Out, slideIn Up/Down/Left/Right, scaleIn, bounce, pulse, spin, shimmer) exposed as `.animate-*` classes (0.3s fade/scale, 0.5s slides; bounce 1s, pulse 2s, spin 1s infinite) plus `.duration-fast/normal/slow` (150/300/500ms), `.stagger-1..10` (0.1s steps) and `.ease-bounce`. For staggered lists set `animation-delay` AND `animation-fill-mode: backwards` (marked CRITICAL: otherwise elements flash before the delay). Skeletons use `.shimmer` (1.5s infinite). Hover/focus transitions run 0.15s to 0.3s. Always include the `@media (prefers-reduced-motion: reduce)` block (0.01ms durations, one iteration). Beyond CSS: Motion (formerly Framer Motion) for simple enter/exit, layout and gesture transitions (`frontend/nextjs/motion-patterns.md`); GSAP for ScrollTrigger, SVG morph, timelines (`frontend/gsap/gsap-core.md`)."
|
|
1924
1215
|
},
|
|
1925
1216
|
{
|
|
1926
1217
|
"id": "frontend-design-system-naming",
|
|
@@ -1935,20 +1226,11 @@
|
|
|
1935
1226
|
"stacks": [
|
|
1936
1227
|
"*"
|
|
1937
1228
|
],
|
|
1938
|
-
"aliases": [
|
|
1939
|
-
"BEM",
|
|
1940
|
-
"CSS naming",
|
|
1941
|
-
"class naming",
|
|
1942
|
-
"design token naming",
|
|
1943
|
-
"component naming"
|
|
1944
|
-
],
|
|
1945
1229
|
"anchors": {
|
|
1946
1230
|
"blocks": "#bem-for-reusable-components",
|
|
1947
1231
|
"tokens": "#css-variables-with-namespace"
|
|
1948
1232
|
},
|
|
1949
|
-
"digest": "BEM for
|
|
1950
|
-
"verifiedAgainst": "CSS (BEM + custom properties — language-level, no framework).",
|
|
1951
|
-
"provedBy": []
|
|
1233
|
+
"digest": "CSS naming rests on four principles: a page prefix prevents conflicts, BEM for components gives clear hierarchy, namespaced variables avoid library collisions, and state suffixes keep modifiers consistent. Page-specific elements take the page name as prefix (.home-hero, .checkout-form, .dashboard-stats, .auth-form); reusable components use BEM: block .card, element .card__header, modifier .card--featured. Rule: BEM for reusable components, page prefix for page-specific elements. Custom properties go in :root under the --app- namespace (--app-primary, --app-spacing-md, --app-font-heading). State modifiers use fixed suffixes: --active, --disabled, --loading, --error (.button, .button--active, .button--disabled)."
|
|
1952
1234
|
},
|
|
1953
1235
|
{
|
|
1954
1236
|
"id": "frontend-design-system-premium-finish",
|
|
@@ -1963,17 +1245,6 @@
|
|
|
1963
1245
|
"stacks": [
|
|
1964
1246
|
"*"
|
|
1965
1247
|
],
|
|
1966
|
-
"aliases": [
|
|
1967
|
-
"premium finish",
|
|
1968
|
-
"polish layer",
|
|
1969
|
-
"film grain",
|
|
1970
|
-
"atmosphere",
|
|
1971
|
-
"load-in stagger",
|
|
1972
|
-
"scrim",
|
|
1973
|
-
"text over video",
|
|
1974
|
-
"metallic accent",
|
|
1975
|
-
"looks like a template"
|
|
1976
|
-
],
|
|
1977
1248
|
"anchors": {
|
|
1978
1249
|
"typography": "#section-1-typography-with-character",
|
|
1979
1250
|
"atmosphere": "#section-2-atmosphere-never-flat-color",
|
|
@@ -1981,9 +1252,7 @@
|
|
|
1981
1252
|
"legibility": "#section-4-legibility-over-video-close-to-a-hard-rule",
|
|
1982
1253
|
"order": "#section-7-order-of-application"
|
|
1983
1254
|
},
|
|
1984
|
-
"digest": "The finish layer that separates a working build from one a client reads as expensive, applied from the start rather than as a final polish pass. Principles, never a fixed recipe, and never converge on the same choices across projects: commit to a display font chosen from the brand (stack defaults and the AI-tell faces like Space Grotesk/Playfair announce machine-generated); use atmosphere instead of flat color (feTurbulence film grain at low opacity, radial brand glow, vignette); concentrate effort on ONE orchestrated hero load-in with staggered animation-delay rather than scattered micro-interactions; text over media always gets a DIRECTIONAL scrim (dark where the copy lives, clear over the subject) plus text-shadow as insurance against bright frames; one precious accent used in three places or fewer. Define brand tokens as CSS variables first, then atmosphere, then hero, then accent, then details."
|
|
1985
|
-
"verifiedAgainst": "Production landing pages, CSS-only techniques, no runtime dependency.",
|
|
1986
|
-
"provedBy": []
|
|
1255
|
+
"digest": "The finish layer that separates a working build from one a client reads as expensive, applied from the start rather than as a final polish pass. Principles, never a fixed recipe, and never converge on the same choices across projects: commit to a display font chosen from the brand (stack defaults and the AI-tell faces like Space Grotesk/Playfair announce machine-generated); use atmosphere instead of flat color (feTurbulence film grain at low opacity, radial brand glow, vignette); concentrate effort on ONE orchestrated hero load-in with staggered animation-delay rather than scattered micro-interactions; text over media always gets a DIRECTIONAL scrim (dark where the copy lives, clear over the subject) plus text-shadow as insurance against bright frames; one precious accent used in three places or fewer. Define brand tokens as CSS variables first, then atmosphere, then hero, then accent, then details."
|
|
1987
1256
|
},
|
|
1988
1257
|
{
|
|
1989
1258
|
"id": "frontend-gsap-core",
|
|
@@ -2000,16 +1269,6 @@
|
|
|
2000
1269
|
"stacks": [
|
|
2001
1270
|
"*"
|
|
2002
1271
|
],
|
|
2003
|
-
"aliases": [
|
|
2004
|
-
"GSAP",
|
|
2005
|
-
"GreenSock",
|
|
2006
|
-
"gsap.to",
|
|
2007
|
-
"gsap.from",
|
|
2008
|
-
"useGSAP",
|
|
2009
|
-
"gsap timeline",
|
|
2010
|
-
"gsap utils",
|
|
2011
|
-
"gsap performance"
|
|
2012
|
-
],
|
|
2013
1272
|
"anchors": {
|
|
2014
1273
|
"apis": "#section-3-core-animation-apis",
|
|
2015
1274
|
"react": "#section-4-reactnextjs-integration",
|
|
@@ -2017,9 +1276,7 @@
|
|
|
2017
1276
|
"performance": "#section-7-performance",
|
|
2018
1277
|
"accessibility": "#section-8-accessibility"
|
|
2019
1278
|
},
|
|
2020
|
-
"digest": "
|
|
2021
|
-
"verifiedAgainst": "GSAP 3.13 + @gsap/react.",
|
|
2022
|
-
"provedBy": []
|
|
1279
|
+
"digest": "GSAP 3.13 + `@gsap/react` in Next.js. Use GSAP for ScrollTrigger, SVG morph/draw, complex timelines, SplitText and Draggable+Inertia; use Framer Motion for simple enter/exit, layout, gestures and AnimatePresence. NEVER animate one DOM element with both (they fight over inline styles). All GSAP code lives in `'use client'` components, inside `useGSAP()` (never `useEffect`) with ALWAYS `{ scope: containerRef }`, which cleans up automatically; handlers outside the callback need `contextSafe()`. Register plugins once at app init (`layout.tsx`/`providers.tsx`), not in components. Prefer `autoAlpha` over `opacity`; animate only transforms and opacity, never width/height/top/left; `gsap.quickTo()` for cursor followers. Honoring `prefers-reduced-motion` is MANDATORY (`gsap.matchMedia()`). Never hardcode durations or easings: import `durations`/`easings` from `@/lib/design-tokens`."
|
|
2023
1280
|
},
|
|
2024
1281
|
{
|
|
2025
1282
|
"id": "frontend-gsap-plugins",
|
|
@@ -2035,25 +1292,13 @@
|
|
|
2035
1292
|
"stacks": [
|
|
2036
1293
|
"*"
|
|
2037
1294
|
],
|
|
2038
|
-
"aliases": [
|
|
2039
|
-
"Flip",
|
|
2040
|
-
"SplitText",
|
|
2041
|
-
"MorphSVG",
|
|
2042
|
-
"DrawSVG",
|
|
2043
|
-
"MotionPath",
|
|
2044
|
-
"Draggable",
|
|
2045
|
-
"Observer",
|
|
2046
|
-
"ScrollToPlugin"
|
|
2047
|
-
],
|
|
2048
1295
|
"anchors": {
|
|
2049
1296
|
"flip": "#flip-plugin-free",
|
|
2050
1297
|
"splittext": "#splittext",
|
|
2051
1298
|
"morphsvg": "#morphsvg",
|
|
2052
1299
|
"drawsvg": "#drawsvg"
|
|
2053
1300
|
},
|
|
2054
|
-
"digest": "Flip
|
|
2055
|
-
"verifiedAgainst": "GSAP 3.13 + @gsap/react.",
|
|
2056
|
-
"provedBy": []
|
|
1301
|
+
"digest": "GSAP 3.13 + @gsap/react: core and every plugin (Flip, SplitText, MorphSVG, DrawSVG, MotionPath, Draggable...) are free from the public `gsap` package (`npm install gsap @gsap/react`); the old `gsap@npm:@gsap/shockingly` private-registry instruction is obsolete. Call `gsap.registerPlugin(...)` ONCE at app init (`'use client'`); animate with `useGSAP` + `scope`. Flip: `Flip.getState()`, change the DOM, `Flip.from(state, { absolute: true })`; with ScrollTrigger, call `ScrollTrigger.refresh()` after the layout change. SplitText: `SplitText.create` with `autoSplit: true`, animate inside `onSplit`; it adds `<div>` wrappers that affect layout, and `useGSAP` reverts it. MorphSVG needs `<path>` elements (`convertToPath` for others); DrawSVG needs a visible stroke (not fill); ScrollToPlugin `autoKill: true`; Draggable `throwProps` needs InertiaPlugin; Observer normalizes wheel/touch/pointer."
|
|
2057
1302
|
},
|
|
2058
1303
|
{
|
|
2059
1304
|
"id": "frontend-gsap-scrolltrigger",
|
|
@@ -2070,23 +1315,13 @@
|
|
|
2070
1315
|
"stacks": [
|
|
2071
1316
|
"*"
|
|
2072
1317
|
],
|
|
2073
|
-
"aliases": [
|
|
2074
|
-
"ScrollTrigger",
|
|
2075
|
-
"scroll animation",
|
|
2076
|
-
"pinning",
|
|
2077
|
-
"scrubbing",
|
|
2078
|
-
"parallax",
|
|
2079
|
-
"scroll-driven"
|
|
2080
|
-
],
|
|
2081
1318
|
"anchors": {
|
|
2082
1319
|
"basic": "#section-2-basic-scrolltrigger",
|
|
2083
1320
|
"scrub": "#section-3-scrubbing",
|
|
2084
1321
|
"pin": "#section-4-pinning",
|
|
2085
1322
|
"batch": "#section-5-batch-processing"
|
|
2086
1323
|
},
|
|
2087
|
-
"digest": "
|
|
2088
|
-
"verifiedAgainst": "GSAP 3.13 ScrollTrigger + @gsap/react.",
|
|
2089
|
-
"provedBy": []
|
|
1324
|
+
"digest": "GSAP 3.13 ScrollTrigger in Next.js: all code lives in 'use client' components inside useGSAP(() => {...}, { scope }), which auto-kills tweens and triggers on unmount (without it, use gsap.context() and ctx.revert()); orphaned triggers leak memory and break later routes. gsap.registerPlugin(ScrollTrigger) once at app init, never inside useEffect or loops; never ScrollTrigger.create() at module scope. Scrub needs ease 'none'; scrub: true is 1:1, use 0.3-1 in production. Pin: pin: true with anticipatePin: 1; pinSpacing: false only for intentional overlap; never nest pins. Use ScrollTrigger.batch for grids (20+ items), snap durations under 0.8s. Responsive: define both desktop and mobile cases, exclusive breakpoints (767/768). Call ScrollTrigger.refresh() after dynamic content, layout shifts, route changes and image onLoad; normalizeScroll(true) helps iOS Safari."
|
|
2090
1325
|
},
|
|
2091
1326
|
{
|
|
2092
1327
|
"id": "frontend-nextjs-app-router",
|
|
@@ -2101,20 +1336,11 @@
|
|
|
2101
1336
|
"stacks": [
|
|
2102
1337
|
"nextjs"
|
|
2103
1338
|
],
|
|
2104
|
-
"aliases": [
|
|
2105
|
-
"app router",
|
|
2106
|
-
"Next.js 14",
|
|
2107
|
-
"server components",
|
|
2108
|
-
"app directory",
|
|
2109
|
-
"RSC"
|
|
2110
|
-
],
|
|
2111
1339
|
"anchors": {
|
|
2112
1340
|
"layout": "#root-layout--required-providers",
|
|
2113
1341
|
"routing": "#app-directory-structure"
|
|
2114
1342
|
},
|
|
2115
|
-
"digest": "Server Components by default
|
|
2116
|
-
"verifiedAgainst": "Next.js 15.",
|
|
2117
|
-
"provedBy": []
|
|
1343
|
+
"digest": "Next.js 15 App Router, TypeScript only (no .js/.jsx). Server Components by default; add `'use client'` only for interaction (onClick, useState/useEffect/useRef), browser APIs, TanStack Query hooks and interactive shadcn/ui. `app/` is for routing only: business logic goes to `features/`, never in `page.tsx`. Never fetch data in a Client Component when a Server Component can, and never fetch in `useEffect`: use a Server Component or TanStack Query. Co-locate `loading.tsx` and `error.tsx` (`error.tsx` must be a Client Component) beside `page.tsx`. Server Components call the .NET backend directly using the dedicated `BACKEND_API_PREFIX` from `@/lib/api/prefixes`, never the same-origin proxy prefix used by Client Components; always check `res.ok`. Avoid `route.ts` API routes (use the .NET API). The root `layout.tsx` wraps children in `QueryProvider` and the sonner `Toaster`."
|
|
2118
1344
|
},
|
|
2119
1345
|
{
|
|
2120
1346
|
"id": "frontend-nextjs-components",
|
|
@@ -2129,20 +1355,11 @@
|
|
|
2129
1355
|
"stacks": [
|
|
2130
1356
|
"nextjs"
|
|
2131
1357
|
],
|
|
2132
|
-
"aliases": [
|
|
2133
|
-
"react components",
|
|
2134
|
-
"server component",
|
|
2135
|
-
"client component",
|
|
2136
|
-
"shadcn/ui",
|
|
2137
|
-
"UI components"
|
|
2138
|
-
],
|
|
2139
1358
|
"anchors": {
|
|
2140
1359
|
"patterns": "#three-tier-hierarchy",
|
|
2141
1360
|
"shadcn": "#tier-1--shadcnui-primitives"
|
|
2142
1361
|
},
|
|
2143
|
-
"digest": "Three tiers: components/ui is shadcn CLI output (
|
|
2144
|
-
"verifiedAgainst": "Next.js 16 + shadcn 4 CLI (the code blocks typecheck under `tsc --strict`).",
|
|
2145
|
-
"provedBy": []
|
|
1362
|
+
"digest": "Three tiers (Next.js 16 + shadcn 4 CLI): `components/ui/` is shadcn CLI output (add via `npx shadcn@latest add`, NEVER edit because the CLI regenerates it; wrap it in `components/` instead); `components/` holds shared composed components with no business logic and NEVER imports from `features/`; `features/*/components/` is feature-scoped and may know the domain. Add `'use client'` only if the component uses hooks, events or browser APIs (not on every component: unnecessary client JS). Never pass raw API/fetch data as props: transform it into typed props and validate with Zod first. Props interfaces always accept an optional `className` for Tailwind override. The shared data table is specified once in `admin-app/data-table.md`: do not write a second `components/data-table.tsx`; a feature list is a Tier 3 component that owns the columns and the URL-to-table mapping."
|
|
2146
1363
|
},
|
|
2147
1364
|
{
|
|
2148
1365
|
"id": "frontend-nextjs-data-fetching",
|
|
@@ -2157,20 +1374,11 @@
|
|
|
2157
1374
|
"stacks": [
|
|
2158
1375
|
"nextjs"
|
|
2159
1376
|
],
|
|
2160
|
-
"aliases": [
|
|
2161
|
-
"TanStack Query",
|
|
2162
|
-
"react-query",
|
|
2163
|
-
"server actions",
|
|
2164
|
-
"data fetching",
|
|
2165
|
-
"useQuery"
|
|
2166
|
-
],
|
|
2167
1377
|
"anchors": {
|
|
2168
1378
|
"server": "#server-component--tanstack-query-handoff",
|
|
2169
1379
|
"client": "#usequery-hook-pattern"
|
|
2170
1380
|
},
|
|
2171
|
-
"digest": "Server Components
|
|
2172
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zod 4.",
|
|
2173
|
-
"provedBy": []
|
|
1381
|
+
"digest": "Server Components do the initial load and call the .NET backend directly with BACKEND_API_PREFIX; Client Components never call the backend — they hit a same-origin proxy route with PROXY_API_PREFIX. Two named constants, never one shared API_PREFIX: the side that drifts breaks silently at runtime. Client data is TanStack Query 5: useQuery for reads, useMutation for writes, then invalidateQueries({ queryKey: userKeys.lists() }) (object syntax). Keys come from a per-feature key factory, never hardcoded ['users']. Every response is parsed with a Zod schema. QueryClient: staleTime 60s, retry 1, created once in useState; the Server Component passes initialData to the client hook. FORBIDDEN: useEffect(() => fetch…, []) and fetching a whole collection to page/sort/filter in the browser — page on the server, query in the URL."
|
|
2174
1382
|
},
|
|
2175
1383
|
{
|
|
2176
1384
|
"id": "frontend-nextjs-forms",
|
|
@@ -2185,20 +1393,11 @@
|
|
|
2185
1393
|
"stacks": [
|
|
2186
1394
|
"nextjs"
|
|
2187
1395
|
],
|
|
2188
|
-
"aliases": [
|
|
2189
|
-
"react-hook-form",
|
|
2190
|
-
"Zod",
|
|
2191
|
-
"form validation",
|
|
2192
|
-
"shadcn Field",
|
|
2193
|
-
"Controller"
|
|
2194
|
-
],
|
|
2195
1396
|
"anchors": {
|
|
2196
1397
|
"validation": "#schema-composition",
|
|
2197
1398
|
"submission": "#complete-form-pattern"
|
|
2198
1399
|
},
|
|
2199
|
-
"digest": "react-hook-form + Zod + shadcn Field components wired through Controller. Define the Zod schema first and derive the type with z.infer (z.input/z.output when it coerces); zodResolver connects it; Field/FieldLabel/FieldError render each field and its error; never useState per field; submit through TanStack Query useMutation and put a failed submit on root. Edit forms reuse the create schema (.partial()). Zod 4: z.email(), a single `error` param. A legacy shadcn Form/FormField screen is migrated when touched, never extended. Admin CRUD forms add the deltas of admin-app/forms.md."
|
|
2200
|
-
"verifiedAgainst": "Next.js 16 + react-hook-form 7 + Zod 4 + @hookform/resolvers 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2201
|
-
"provedBy": []
|
|
1400
|
+
"digest": "react-hook-form + Zod + shadcn Field components wired through Controller. Define the Zod schema first and derive the type with z.infer (z.input/z.output when it coerces); zodResolver connects it; Field/FieldLabel/FieldError render each field and its error; never useState per field; submit through TanStack Query useMutation and put a failed submit on root. Edit forms reuse the create schema (.partial()). Zod 4: z.email(), a single `error` param. A legacy shadcn Form/FormField screen is migrated when touched, never extended. Admin CRUD forms add the deltas of admin-app/forms.md."
|
|
2202
1401
|
},
|
|
2203
1402
|
{
|
|
2204
1403
|
"id": "frontend-nextjs-motion-patterns",
|
|
@@ -2214,22 +1413,13 @@
|
|
|
2214
1413
|
"stacks": [
|
|
2215
1414
|
"nextjs"
|
|
2216
1415
|
],
|
|
2217
|
-
"aliases": [
|
|
2218
|
-
"framer-motion",
|
|
2219
|
-
"animation variants",
|
|
2220
|
-
"motion design",
|
|
2221
|
-
"page transition",
|
|
2222
|
-
"AnimatePresence"
|
|
2223
|
-
],
|
|
2224
1416
|
"anchors": {
|
|
2225
1417
|
"variants": "#section-1-core-variants-library",
|
|
2226
1418
|
"tokens": "#section-2-design-token-bridge",
|
|
2227
1419
|
"scroll": "#section-3-scroll-animations",
|
|
2228
1420
|
"accessibility": "#section-7-wcag-accessibility-mandatory"
|
|
2229
1421
|
},
|
|
2230
|
-
"digest": "
|
|
2231
|
-
"verifiedAgainst": "Motion for React 12 (`motion`, imports from `motion/react`).",
|
|
2232
|
-
"provedBy": []
|
|
1422
|
+
"digest": "Component-level motion with Motion for React (the stack table's 'Framer Motion', its former name; motion 12+, current npm major 14): npm install motion and import from 'motion/react'. framer-motion is not deprecated (motion depends on it and re-exports it), but a codebase keeps one import path, motion/react, and never adds framer-motion alongside it. Variants live in motion/variants.ts (fadeIn, slideInUp/Down/Left/Right, scaleIn, staggerContainer, staggerItem, pageTransition) and take durations and easings from @/lib/design-tokens (fast 0.15, base 0.3); never hardcode duration numbers or easing arrays. Scroll reveals: whileInView with viewport once: true for simple cases, useInView for control. Page transitions: AnimatePresence mode='wait' keyed by usePathname(), subtle (y: 8), inside a 'use client' PageTransition provider -- the root app/layout.tsx stays a Server Component (no 'use client', keeps metadata) and just renders the provider. WCAG is mandatory: useReducedMotion() strips x/y/scale and keeps opacity, plus a prefers-reduced-motion CSS fallback. Skeletons use CSS animate-pulse, not motion. Limit motion to 3-5 moments per page. Scroll-driven scenes (ScrollTrigger, Lenis) belong to GSAP; never share an element with it."
|
|
2233
1423
|
},
|
|
2234
1424
|
{
|
|
2235
1425
|
"id": "frontend-nextjs-naming-conventions",
|
|
@@ -2244,20 +1434,11 @@
|
|
|
2244
1434
|
"stacks": [
|
|
2245
1435
|
"nextjs"
|
|
2246
1436
|
],
|
|
2247
|
-
"aliases": [
|
|
2248
|
-
"naming conventions",
|
|
2249
|
-
"file naming",
|
|
2250
|
-
"component naming",
|
|
2251
|
-
"Next.js naming",
|
|
2252
|
-
"TypeScript naming"
|
|
2253
|
-
],
|
|
2254
1437
|
"anchors": {
|
|
2255
1438
|
"files": "#complete-reference-table",
|
|
2256
1439
|
"components": "#complete-reference-table"
|
|
2257
1440
|
},
|
|
2258
|
-
"digest": "
|
|
2259
|
-
"verifiedAgainst": "Next.js 15 + Zod 4.",
|
|
2260
|
-
"provedBy": []
|
|
1441
|
+
"digest": "Next.js naming (prevents Linux case-sensitivity bugs). File names are ALWAYS kebab-case (`user-card.tsx`, never `UserCard.tsx`); exports are PascalCase for components (`UserCard`) and camelCase with a `use` prefix for hooks, whose files are kebab-case too (`use-users.ts` -> `useUsers`, `use-user.ts` for a single GET, `use-create-user.ts`, `use-update-user.ts`, `use-delete-user.ts`). Zod schema files end in `.schemas.ts` (`createUserSchema`) and type files in `.types.ts`, with types derived via `z.infer` instead of hand-written duplicates. Components use named exports (`export function UserCard`, not `export default`); `page.tsx` exports `default`. A feature `index.ts` re-exports. Never mix cases within a category (no `UserCard.tsx` next to `user-profile.tsx`)."
|
|
2261
1442
|
},
|
|
2262
1443
|
{
|
|
2263
1444
|
"id": "frontend-nextjs-nextjs-patterns",
|
|
@@ -2272,20 +1453,11 @@
|
|
|
2272
1453
|
"stacks": [
|
|
2273
1454
|
"nextjs"
|
|
2274
1455
|
],
|
|
2275
|
-
"aliases": [
|
|
2276
|
-
"Next.js patterns",
|
|
2277
|
-
"middleware",
|
|
2278
|
-
"auth patterns",
|
|
2279
|
-
"caching patterns",
|
|
2280
|
-
"image optimization"
|
|
2281
|
-
],
|
|
2282
1456
|
"anchors": {
|
|
2283
1457
|
"auth": "#route-handlers-bff-pattern",
|
|
2284
1458
|
"caching": "#react-query--neon"
|
|
2285
1459
|
},
|
|
2286
|
-
"digest": "
|
|
2287
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zod 4 + react-hook-form 7.",
|
|
2288
|
-
"provedBy": []
|
|
1460
|
+
"digest": "Next.js 15 App Router (not Pages Router): Server Components by default, `'use client'` only for state, effects, events or browser APIs; colocate `loading.tsx` and `error.tsx` (the latter MUST be `'use client'`) with `page.tsx`. Route Handlers act as a BFF: check `auth.getSession()` (401 if absent) and delegate to the .NET API; database access lives in the .NET backend via EF Core, NEVER in Next.js route handlers. Validate input with Zod on client and server (`safeParse`, 400). Fetch with TanStack Query (`useQuery`/`useMutation`, `staleTime: 60_000`, `retry: 1`, `invalidateQueries` on success), never `useEffect` for data fetching. Forms: react-hook-form + `zodResolver`. shadcn is copied via `npx shadcn@latest add`, not an npm dependency. Keep `\"strict\": true` plus `noUncheckedIndexedAccess` (agents self-correct from compiler errors); no `any` for API responses (Zod schema + `z.infer`)."
|
|
2289
1461
|
},
|
|
2290
1462
|
{
|
|
2291
1463
|
"id": "frontend-nextjs-project-structure",
|
|
@@ -2300,19 +1472,11 @@
|
|
|
2300
1472
|
"stacks": [
|
|
2301
1473
|
"nextjs"
|
|
2302
1474
|
],
|
|
2303
|
-
"aliases": [
|
|
2304
|
-
"project structure",
|
|
2305
|
-
"folder structure",
|
|
2306
|
-
"src directory",
|
|
2307
|
-
"app directory organization"
|
|
2308
|
-
],
|
|
2309
1475
|
"anchors": {
|
|
2310
1476
|
"structure": "#canonical-folder-tree",
|
|
2311
1477
|
"organization": "#feature-index-pattern"
|
|
2312
1478
|
},
|
|
2313
|
-
"digest": "src/
|
|
2314
|
-
"verifiedAgainst": "Next.js 15 (App Router, `src/` layout).",
|
|
2315
|
-
"provedBy": []
|
|
1479
|
+
"digest": "Next.js 15 App Router, src/ layout with the @/ alias, feature-based. src/app/ is routing only (layout, page, loading, error) and imports from features/, never the reverse. Each src/features/{feature-name}/ holds components/, hooks/ (TanStack Query hooks such as use-users.ts, use-create-user.ts), types/ (*.types.ts plus *.schemas.ts with Zod and z.infer) and an index.ts that is the explicit public API: import from '@/features/users', never from deep paths. NEVER import one feature from another (extract to components/ or lib/); components/ must not import features/, is presentation only with NO business logic, and components/ui/ is shadcn, never edited directly. Shared hooks/ make no API calls. lib/ holds api-client.ts (typed fetch wrapper for the .NET API) and query-client.tsx; types/api.ts has PaginatedResponse<T> and ApiError; env.mjs validates env vars with Zod."
|
|
2316
1480
|
},
|
|
2317
1481
|
{
|
|
2318
1482
|
"id": "frontend-nextjs-state-management",
|
|
@@ -2327,20 +1491,11 @@
|
|
|
2327
1491
|
"stacks": [
|
|
2328
1492
|
"nextjs"
|
|
2329
1493
|
],
|
|
2330
|
-
"aliases": [
|
|
2331
|
-
"zustand",
|
|
2332
|
-
"React context",
|
|
2333
|
-
"global state",
|
|
2334
|
-
"state management",
|
|
2335
|
-
"client state"
|
|
2336
|
-
],
|
|
2337
1494
|
"anchors": {
|
|
2338
1495
|
"patterns": "#state-decision-tree",
|
|
2339
1496
|
"server": "#server-state-tanstack-query"
|
|
2340
1497
|
},
|
|
2341
|
-
"digest": "TanStack Query
|
|
2342
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zustand 5.",
|
|
2343
|
-
"provedBy": []
|
|
1498
|
+
"digest": "Server state (anything from the .NET API) lives in TanStack Query, never copied into `useState` via `useEffect`; derive state from server data instead of syncing it. Local UI state (modal open, form step, selected id) uses `useState`. A list's page, sort and filters, and a page's active tab, go in the URL (`searchParams`), never `useState`, so a refresh or shared link reopens the same view. Do not use React Context for server state (no caching or dedup); Context is for truly global theme, auth user or locale, and prop drilling 2-3 levels is fine. NEVER install Zustand or Redux before exhausting Server Components + TanStack Query. Zustand is justified only if ALL hold: state needed in 5+ unrelated components, complex update logic, Context causes measurable re-render problems, and the state is not from the server."
|
|
2344
1499
|
},
|
|
2345
1500
|
{
|
|
2346
1501
|
"id": "frontend-nextjs-testing",
|
|
@@ -2355,25 +1510,12 @@
|
|
|
2355
1510
|
"stacks": [
|
|
2356
1511
|
"nextjs"
|
|
2357
1512
|
],
|
|
2358
|
-
"aliases": [
|
|
2359
|
-
"vitest",
|
|
2360
|
-
"jest",
|
|
2361
|
-
"testing-library",
|
|
2362
|
-
"Playwright",
|
|
2363
|
-
"Next.js testing",
|
|
2364
|
-
"component tests",
|
|
2365
|
-
"test budget",
|
|
2366
|
-
"orçamento de teste",
|
|
2367
|
-
"it.each table"
|
|
2368
|
-
],
|
|
2369
1513
|
"anchors": {
|
|
2370
1514
|
"unit": "#component-test-pattern",
|
|
2371
1515
|
"e2e": "#test-file-co-location",
|
|
2372
1516
|
"test-budget": "#test-budget"
|
|
2373
1517
|
},
|
|
2374
|
-
"digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior are rows of one it.each, never copied it() blocks; no test whose only assertion is a copy string (lint or publish-time validation instead); shared wrappers, MSW handlers and factories before new helpers; bugs and pure logic get tests in any order — the implementer runs its own filter once, never red-green cycles nor production mutation."
|
|
2375
|
-
"verifiedAgainst": "Vitest 3 + React Testing Library 16 + MSW 2.",
|
|
2376
|
-
"provedBy": []
|
|
1518
|
+
"digest": "Vitest for unit tests, React Testing Library for component tests. Mock next/navigation with vi.mock. Test Server Actions with direct function calls. Use MSW for API mocking in component tests. Playwright for E2E tests against running app. Never test implementation details — test user behavior and output. Test budget per task: one test per postcondition plus one regression test per bug, more only with a one-line justification in the task; scenarios of the same behavior are rows of one it.each, never copied it() blocks; no test whose only assertion is a copy string (lint or publish-time validation instead); shared wrappers, MSW handlers and factories before new helpers; bugs and pure logic get tests in any order — the implementer runs its own filter once, never red-green cycles nor production mutation."
|
|
2377
1519
|
},
|
|
2378
1520
|
{
|
|
2379
1521
|
"id": "frontend-scroll-driven-frame-scrub",
|
|
@@ -2389,15 +1531,6 @@
|
|
|
2389
1531
|
"stacks": [
|
|
2390
1532
|
"*"
|
|
2391
1533
|
],
|
|
2392
|
-
"aliases": [
|
|
2393
|
-
"frame scrub",
|
|
2394
|
-
"canvas scrub",
|
|
2395
|
-
"scroll-driven video",
|
|
2396
|
-
"scrollytelling video",
|
|
2397
|
-
"apple scroll video",
|
|
2398
|
-
"ffmpeg frames",
|
|
2399
|
-
"poster fallback"
|
|
2400
|
-
],
|
|
2401
1534
|
"anchors": {
|
|
2402
1535
|
"why": "#section-1-canvas-not-video-currenttime",
|
|
2403
1536
|
"extraction": "#section-2-frame-extraction",
|
|
@@ -2406,9 +1539,7 @@
|
|
|
2406
1539
|
"responsive": "#section-6-responsive-and-reduced-motion",
|
|
2407
1540
|
"verify": "#section-8-verify-with-motion-never-with-a-still"
|
|
2408
1541
|
},
|
|
2409
|
-
"digest": "Scroll-driven video is a preloaded JPG sequence drawn into a <canvas>, never a <video currentTime> driven by scroll (unreliable seek on iOS Safari, black-frame flashes) and never an autoplay loop (reads as a demo, not as user-driven). Extract with ffmpeg (fps=30, ~120-300 frames, -q:v 5), always extract a poster, hflip when the subject collides with the copy, and treat total sequence weight as a performance budget. Always cache-bust the frame URL (?v=N) when re-extracting under the same filenames. Known bug: a section starting hidden measures 0 and the canvas becomes 1x1 drawing one stretched pixel, so keep the canvas display:none and resize on the first loaded frame. Mobile and prefers-reduced-motion collapse to the static poster in both the JS guard and the CSS. Verify by driving the scroll through positions and comparing captured frames (allow ~350ms for scrub:0.4 to settle), never with a single screenshot."
|
|
2410
|
-
"verifiedAgainst": "GSAP 3.13 ScrollTrigger + Lenis 1.x + ffmpeg 6.x, shipped to production.",
|
|
2411
|
-
"provedBy": []
|
|
1542
|
+
"digest": "Scroll-driven video is a preloaded JPG sequence drawn into a <canvas>, never a <video currentTime> driven by scroll (unreliable seek on iOS Safari, black-frame flashes) and never an autoplay loop (reads as a demo, not as user-driven). Extract with ffmpeg (fps=30, ~120-300 frames, -q:v 5), always extract a poster, hflip when the subject collides with the copy, and treat total sequence weight as a performance budget. Always cache-bust the frame URL (?v=N) when re-extracting under the same filenames. Known bug: a section starting hidden measures 0 and the canvas becomes 1x1 drawing one stretched pixel, so keep the canvas display:none and resize on the first loaded frame. Mobile and prefers-reduced-motion collapse to the static poster in both the JS guard and the CSS. Verify by driving the scroll through positions and comparing captured frames (allow ~350ms for scrub:0.4 to settle), never with a single screenshot."
|
|
2412
1543
|
},
|
|
2413
1544
|
{
|
|
2414
1545
|
"id": "frontend-scroll-driven-scroll-components",
|
|
@@ -2424,16 +1555,6 @@
|
|
|
2424
1555
|
"stacks": [
|
|
2425
1556
|
"*"
|
|
2426
1557
|
],
|
|
2427
|
-
"aliases": [
|
|
2428
|
-
"pinned gallery",
|
|
2429
|
-
"horizontal scroll",
|
|
2430
|
-
"scroll reveal",
|
|
2431
|
-
"parallax drift",
|
|
2432
|
-
"count-up",
|
|
2433
|
-
"lightbox",
|
|
2434
|
-
"scroll progress",
|
|
2435
|
-
"expanding modal"
|
|
2436
|
-
],
|
|
2437
1558
|
"anchors": {
|
|
2438
1559
|
"reveal": "#section-1-usereveal-the-base-brick",
|
|
2439
1560
|
"pinned": "#section-2-pinned-horizontal-gallery",
|
|
@@ -2441,9 +1562,7 @@
|
|
|
2441
1562
|
"parallax": "#section-5-parallax-drift",
|
|
2442
1563
|
"rules": "#section-9-cross-cutting-rules"
|
|
2443
1564
|
},
|
|
2444
|
-
"digest": "Toolbox of scroll-driven interaction patterns, not a mandatory list: useReveal (fade/rise with once:true) covers most content; pinned horizontal gallery needs invalidateOnRefresh:true or the travel distance stays frozen after a resize; expanding modal and lightbox mount then add an .in class on the next frame, pause the scroller via window.__lenis?.stop(), and must render OUTSIDE any pinned section because the pin transform breaks position:fixed inside it; parallax drift stays subtle at 5-8% with overflow:hidden on the figure; count-up animates a plain object and formats per locale; scroll progress uses scaleX with transform-origin 0 50% to stay on the compositor; accordions use native <details>. Cross-cutting: anything clickable that is not a button or anchor gets role=button, tabIndex 0 and Enter/Space, Escape closes overlays, every effect has a static reduced-motion fallback and reverts on cleanup."
|
|
2445
|
-
"verifiedAgainst": "GSAP 3.13 (ScrollTrigger, matchMedia, context) + React 18 + Lenis 1.x, shipped to production.",
|
|
2446
|
-
"provedBy": []
|
|
1565
|
+
"digest": "Toolbox of scroll-driven interaction patterns, not a mandatory list: useReveal (fade/rise with once:true) covers most content; pinned horizontal gallery needs invalidateOnRefresh:true or the travel distance stays frozen after a resize; expanding modal and lightbox mount then add an .in class on the next frame, pause the scroller via window.__lenis?.stop(), and must render OUTSIDE any pinned section because the pin transform breaks position:fixed inside it; parallax drift stays subtle at 5-8% with overflow:hidden on the figure; count-up animates a plain object and formats per locale; scroll progress uses scaleX with transform-origin 0 50% to stay on the compositor; accordions use native <details>. Cross-cutting: anything clickable that is not a button or anchor gets role=button, tabIndex 0 and Enter/Space, Escape closes overlays, every effect has a static reduced-motion fallback and reverts on cleanup."
|
|
2447
1566
|
},
|
|
2448
1567
|
{
|
|
2449
1568
|
"id": "frontend-scroll-driven-smooth-scroll",
|
|
@@ -2459,24 +1578,13 @@
|
|
|
2459
1578
|
"stacks": [
|
|
2460
1579
|
"*"
|
|
2461
1580
|
],
|
|
2462
|
-
"aliases": [
|
|
2463
|
-
"lenis",
|
|
2464
|
-
"smooth scroll",
|
|
2465
|
-
"scroll wiring",
|
|
2466
|
-
"gsap ticker",
|
|
2467
|
-
"window.__lenis",
|
|
2468
|
-
"scrolltrigger refresh",
|
|
2469
|
-
"HMR stale triggers"
|
|
2470
|
-
],
|
|
2471
1581
|
"anchors": {
|
|
2472
1582
|
"registry": "#section-1-central-motion-registry",
|
|
2473
1583
|
"raf": "#section-2-the-raf-handoff",
|
|
2474
1584
|
"expose": "#section-3-expose-the-instance-on-window",
|
|
2475
1585
|
"hmr": "#section-4-the-hmr-staleness-trap"
|
|
2476
1586
|
},
|
|
2477
|
-
"digest": "Register GSAP plugins once in a shared lib/motion module that also exports prefersReducedMotion. Drive Lenis from gsap.ticker (never its own RAF loop), bind lenis.on(scroll, ScrollTrigger.update), set gsap.ticker.lagSmoothing(0), and call ScrollTrigger.refresh() on window load so triggers re-measure after fonts and images settle. Expose window.__lenis deliberately: overlays need stop()/start() to freeze the background, and automated verification needs scrollTo(y, {immediate:true}) to step through positions; consumers must use optional chaining because the instance is never created under reduced motion. After many hot reloads ScrollTrigger accumulates stale instances and pins/scrubs silently die, so full-reload before debugging, wrap every effect in gsap.context()/matchMedia() with revert on cleanup, and set invalidateOnRefresh on viewport-dependent triggers."
|
|
2478
|
-
"verifiedAgainst": "Lenis 1.x + GSAP 3.13 ScrollTrigger + Vite 5, shipped to production.",
|
|
2479
|
-
"provedBy": []
|
|
1587
|
+
"digest": "Register GSAP plugins once in a shared lib/motion module that also exports prefersReducedMotion. Drive Lenis from gsap.ticker (never its own RAF loop), bind lenis.on(scroll, ScrollTrigger.update), set gsap.ticker.lagSmoothing(0), and call ScrollTrigger.refresh() on window load so triggers re-measure after fonts and images settle. Expose window.__lenis deliberately: overlays need stop()/start() to freeze the background, and automated verification needs scrollTo(y, {immediate:true}) to step through positions; consumers must use optional chaining because the instance is never created under reduced motion. After many hot reloads ScrollTrigger accumulates stale instances and pins/scrubs silently die, so full-reload before debugging, wrap every effect in gsap.context()/matchMedia() with revert on cleanup, and set invalidateOnRefresh on viewport-dependent triggers."
|
|
2480
1588
|
},
|
|
2481
1589
|
{
|
|
2482
1590
|
"id": "frontend-seo-meta-schema-audit",
|
|
@@ -2492,18 +1600,6 @@
|
|
|
2492
1600
|
"stacks": [
|
|
2493
1601
|
"*"
|
|
2494
1602
|
],
|
|
2495
|
-
"aliases": [
|
|
2496
|
-
"seo",
|
|
2497
|
-
"meta tags",
|
|
2498
|
-
"open graph",
|
|
2499
|
-
"og:image",
|
|
2500
|
-
"schema.org",
|
|
2501
|
-
"json-ld",
|
|
2502
|
-
"rich results",
|
|
2503
|
-
"lighthouse",
|
|
2504
|
-
"go-live audit",
|
|
2505
|
-
"canonical"
|
|
2506
|
-
],
|
|
2507
1603
|
"anchors": {
|
|
2508
1604
|
"meta": "#section-1-essential-meta",
|
|
2509
1605
|
"opengraph": "#section-2-open-graph-and-twitter-cards",
|
|
@@ -2511,9 +1607,7 @@
|
|
|
2511
1607
|
"a11y": "#section-4-accessibility-also-an-seo-signal",
|
|
2512
1608
|
"audit": "#section-5-pre-launch-audit"
|
|
2513
1609
|
},
|
|
2514
|
-
"digest": "Head-level SEO is cheap and pays for itself: title/description/canonical/theme-color/favicon, correct lang, font preconnects, and a full Open Graph set with an absolute 1200x630 og:image (without it a pasted link renders as a bare card). Pick the JSON-LD @type from the business (ClothingStore/Store for retail, LocalBusiness for services, RealEstateAgent or Residence for property, SoftwareApplication for SaaS) and include ONLY true fields: aggregateRating goes in only with a real rating and review count from a real source, otherwise omit the block, because fabricated structured data is a trust and manual-action risk. Accessibility doubles as an SEO signal (alt text, contrast checked against the brightest frames, focus order, one h1, reduced-motion honored). Before go-live run Lighthouse, a clean-console check, a share-card check and cross-device; if a heavy frame sequence costs Performance, confirm the cost sits in the scrub and state the trade rather than letting a low score pass unexamined."
|
|
2515
|
-
"verifiedAgainst": "Schema.org vocabulary + Lighthouse via Chrome DevTools MCP, shipped to production.",
|
|
2516
|
-
"provedBy": []
|
|
1610
|
+
"digest": "Head-level SEO is cheap and pays for itself: title/description/canonical/theme-color/favicon, correct lang, font preconnects, and a full Open Graph set with an absolute 1200x630 og:image (without it a pasted link renders as a bare card). Pick the JSON-LD @type from the business (ClothingStore/Store for retail, LocalBusiness for services, RealEstateAgent or Residence for property, SoftwareApplication for SaaS) and include ONLY true fields: aggregateRating goes in only with a real rating and review count from a real source, otherwise omit the block, because fabricated structured data is a trust and manual-action risk. Accessibility doubles as an SEO signal (alt text, contrast checked against the brightest frames, focus order, one h1, reduced-motion honored). Before go-live run Lighthouse, a clean-console check, a share-card check and cross-device; if a heavy frame sequence costs Performance, confirm the cost sits in the scrub and state the trade rather than letting a low score pass unexamined."
|
|
2517
1611
|
},
|
|
2518
1612
|
{
|
|
2519
1613
|
"id": "infrastructure-docker-coolify-deploy",
|
|
@@ -2528,23 +1622,12 @@
|
|
|
2528
1622
|
"stacks": [
|
|
2529
1623
|
"docker"
|
|
2530
1624
|
],
|
|
2531
|
-
"aliases": [
|
|
2532
|
-
"coolify",
|
|
2533
|
-
"docker deploy",
|
|
2534
|
-
"self-hosted",
|
|
2535
|
-
"VPS deployment",
|
|
2536
|
-
"docker compose",
|
|
2537
|
-
"coolify api",
|
|
2538
|
-
"ssh deploy"
|
|
2539
|
-
],
|
|
2540
1625
|
"anchors": {
|
|
2541
1626
|
"setup": "#coolify-service-config",
|
|
2542
1627
|
"deployment": "#zero-downtime-deploys",
|
|
2543
1628
|
"ssh": "#triggering-a-deploy-via-ssh--api"
|
|
2544
1629
|
},
|
|
2545
|
-
"digest": "Coolify is the self-hosted Docker PaaS.
|
|
2546
|
-
"verifiedAgainst": ".NET 10 + Next.js 15 + Node 22 (Docker base images).",
|
|
2547
|
-
"provedBy": []
|
|
1630
|
+
"digest": "Coolify is the self-hosted Docker PaaS. We use no Coolify MCP/CLI — all access is via SSH into the VPS, hitting the Coolify API at localhost:8000/api/v1 with a server-side token (never printed, never leaves the SSH session). Configure environment variables in Coolify UI/API — not in Dockerfile. Configure /health endpoint for health checks. HTTPS via Let's Encrypt automatic (Traefik). Redeploys trigger via POST /api/v1/deploy?uuid={uuid} over SSH (Coolify 4.3.23 answers GET with \"This endpoint has changed to a POST request\" and deploys nothing); a queued deploy returns a deployment_uuid, its absence means nothing was deployed."
|
|
2548
1631
|
},
|
|
2549
1632
|
{
|
|
2550
1633
|
"id": "infrastructure-docker-local-compose-isolation",
|
|
@@ -2559,17 +1642,6 @@
|
|
|
2559
1642
|
"stacks": [
|
|
2560
1643
|
"docker"
|
|
2561
1644
|
],
|
|
2562
|
-
"aliases": [
|
|
2563
|
-
"compose isolation",
|
|
2564
|
-
"worktree ports",
|
|
2565
|
-
"container_name",
|
|
2566
|
-
"POSTGRES_PORT",
|
|
2567
|
-
"COMPOSE_PROJECT_NAME",
|
|
2568
|
-
"port block",
|
|
2569
|
-
"PG_PORT",
|
|
2570
|
-
"portEnvNames",
|
|
2571
|
-
"local docker compose"
|
|
2572
|
-
],
|
|
2573
1645
|
"anchors": {
|
|
2574
1646
|
"layout": "#port-layout-one-contract-everywhere",
|
|
2575
1647
|
"portNames": "#project-named-port-variables",
|
|
@@ -2577,9 +1649,7 @@
|
|
|
2577
1649
|
"enforcement": "#what-the-harness-enforces",
|
|
2578
1650
|
"skeleton": "#minimal-compose-skeleton"
|
|
2579
1651
|
},
|
|
2580
|
-
"digest": "Every worktree runs its OWN compose stack (project morph-{feature}, own postgres, own volumes). Ports come from the worktree block: PORT +0, API_PORT +1 (ASPNETCORE_URLS on .NET), POSTGRES_PORT +2, REDIS_PORT +3 — publish via ${VAR:-default}:container, never a fixed host port, never container_name (not prefixed by the project → collides). Stable POSTGRES_DB, healthcheck on every service, DATABASE_URL with an in-network default, playwright.config reads PLAYWRIGHT_BASE_URL. A non-isolable compose fails the verify e2e node inside a worktree. finish tears down with volumes (--keep-volumes opts out). Cloud DB credentials go in e2e.auth.envFile, never in .morph/worktree.env. A compose that reads a port by ANOTHER name (PG_PORT) is not fed by the block: declare it in config.json e2e.portEnvNames ({\"postgres\": \"PG_PORT\"}) and the harness exports it with the same value in worktree.env, the compose env and the hook."
|
|
2581
|
-
"verifiedAgainst": "Docker Compose v2 + morph-spec 8.34 (dedicated stack per worktree).",
|
|
2582
|
-
"provedBy": []
|
|
1652
|
+
"digest": "Every worktree runs its OWN compose stack (project morph-{feature}, own postgres, own volumes). Ports come from the worktree block: PORT +0, API_PORT +1 (ASPNETCORE_URLS on .NET), POSTGRES_PORT +2, REDIS_PORT +3 — publish via ${VAR:-default}:container, never a fixed host port, never container_name (not prefixed by the project → collides). Stable POSTGRES_DB, healthcheck on every service, DATABASE_URL with an in-network default, playwright.config reads PLAYWRIGHT_BASE_URL. A non-isolable compose fails the verify e2e node inside a worktree. finish tears down with volumes (--keep-volumes opts out). Cloud DB credentials go in e2e.auth.envFile, never in .morph/worktree.env. A compose that reads a port by ANOTHER name (PG_PORT) is not fed by the block: declare it in config.json e2e.portEnvNames ({\"postgres\": \"PG_PORT\"}) and the harness exports it with the same value in worktree.env, the compose env and the hook."
|
|
2583
1653
|
},
|
|
2584
1654
|
{
|
|
2585
1655
|
"id": "infrastructure-neon-pgrag",
|
|
@@ -2595,20 +1665,11 @@
|
|
|
2595
1665
|
"stacks": [
|
|
2596
1666
|
"neon"
|
|
2597
1667
|
],
|
|
2598
|
-
"aliases": [
|
|
2599
|
-
"pgrag",
|
|
2600
|
-
"RAG in SQL",
|
|
2601
|
-
"embedding pipeline",
|
|
2602
|
-
"Neon RAG",
|
|
2603
|
-
"in-database RAG"
|
|
2604
|
-
],
|
|
2605
1668
|
"anchors": {
|
|
2606
1669
|
"setup": "#install-pgrag",
|
|
2607
1670
|
"pipeline": "#full-rag-pipeline-example"
|
|
2608
1671
|
},
|
|
2609
|
-
"digest": "
|
|
2610
|
-
"verifiedAgainst": "Neon pgrag (`rag` extension family — experimental, see Core Rules).",
|
|
2611
|
-
"provedBy": []
|
|
1672
|
+
"digest": "pgrag (Neon `rag` extension family) is EXPERIMENTAL: use it for prototyping and simple SQL-only RAG, not production without testing. Run `SET neon.allow_unstable_extensions='true'` before `CREATE EXTENSION rag CASCADE` (also `rag_bge_small_en_v15`, `rag_jina_reranker_v1_tiny_en`). Local bge-small embeddings are 384 dims, OpenAI text-embedding-3-small is 1536: NEVER mix models in one column or use the local one when the app uses another model (dimension mismatch breaks search). Chunk with `rag.chunks_by_character_count` (1000 chars, 200 overlap); `embedding_for_passage` for documents, `embedding_for_query` for queries; create the pgvector HNSW index (m=16, ef_construction=64) after the initial load; rerank with `rerank_distance` (sort ASC). Keep API keys out of SQL scripts. For production .NET needing custom logic, use a typed MAF tool over pgvector (`ai-agents-rag-custom-pgvector`)."
|
|
2612
1673
|
},
|
|
2613
1674
|
{
|
|
2614
1675
|
"id": "infrastructure-neon-pgvector",
|
|
@@ -2624,20 +1685,11 @@
|
|
|
2624
1685
|
"stacks": [
|
|
2625
1686
|
"neon"
|
|
2626
1687
|
],
|
|
2627
|
-
"aliases": [
|
|
2628
|
-
"pgvector",
|
|
2629
|
-
"vector embeddings",
|
|
2630
|
-
"similarity search",
|
|
2631
|
-
"Neon vectors",
|
|
2632
|
-
"HNSW index"
|
|
2633
|
-
],
|
|
2634
1688
|
"anchors": {
|
|
2635
1689
|
"setup": "#enable-pgvector",
|
|
2636
1690
|
"queries": "#similarity-search"
|
|
2637
1691
|
},
|
|
2638
|
-
"digest": "
|
|
2639
|
-
"verifiedAgainst": ".NET 10 + Pgvector.EntityFrameworkCore + pgvector (HNSW).",
|
|
2640
|
-
"provedBy": []
|
|
1692
|
+
"digest": "pgvector on Neon with .NET VSA: packages Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + Pgvector.EntityFrameworkCore 0.3.0 (snake_case columns mapped with HasColumnName, no naming-convention package); HasPostgresExtension('vector') in OnModelCreating emits CREATE EXTENSION in the migration. Map the column as Pgvector `Vector` (`vector(1536)` for text-embedding-3-small; EF does not map float[] or ReadOnlyMemory<float>) and call `npgsql.UseVector()` inside UseNpgsql (NpgsqlDataSourceBuilder.UseVector only for raw Npgsql). ALWAYS an HNSW index (HasMethod('hnsw'), vector_cosine_ops, m 16, ef_construction 64); the operator class must match the LINQ function (CosineDistance/L2Distance/MaxInnerProduct) or Postgres seq-scans. Query in the slice handler through ApplicationDbContext: `d.Embedding!.CosineDistance(query)`, filter by distance <= 1 - minSimilarity, OrderBy distance ASC (similarity = 1 - distance, never ORDER BY similarity DESC), return Result; no DocumentRepository, no raw SQL from C# (SQL functions such as match_documents/hybrid search only with a decisions.md justification). Ids from Guid.CreateVersion7() in the handler, never gen_random_uuid() defaults. ef_search (default 40) on the pooled endpoint needs SET LOCAL inside the query's transaction."
|
|
2641
1693
|
},
|
|
2642
1694
|
{
|
|
2643
1695
|
"id": "infrastructure-neon-setup",
|
|
@@ -2653,20 +1705,11 @@
|
|
|
2653
1705
|
"stacks": [
|
|
2654
1706
|
"neon"
|
|
2655
1707
|
],
|
|
2656
|
-
"aliases": [
|
|
2657
|
-
"Neon",
|
|
2658
|
-
"neonctl",
|
|
2659
|
-
"serverless postgres",
|
|
2660
|
-
"Neon CLI",
|
|
2661
|
-
"Neon setup"
|
|
2662
|
-
],
|
|
2663
1708
|
"anchors": {
|
|
2664
1709
|
"setup": "#neon-cli-setup",
|
|
2665
1710
|
"connection": "#connection-strings"
|
|
2666
1711
|
},
|
|
2667
|
-
"digest": "
|
|
2668
|
-
"verifiedAgainst": ".NET 10 + Npgsql/EF Core 10 + neonctl.",
|
|
2669
|
-
"provedBy": []
|
|
1712
|
+
"digest": "Neon serverless Postgres with .NET/EF Core. Two connection strings read through IConfiguration: `ConnectionStrings:DefaultConnection` = pooled `-pooler` endpoint for application traffic (Neon's DATABASE_URL) and `ConnectionStrings:MigrationConnection` = direct endpoint for migrations, `dotnet ef` and advisory locks (Neon's DATABASE_URL_UNPOOLED; PgBouncer does not support advisory locks; the `IDesignTimeDbContextFactory` reads it via appsettings + AddUserSecrets + AddEnvironmentVariables). Never hardcode or commit them: user-secrets in development, Coolify env vars (`ConnectionStrings__DefaultConnection`) in production, appsettings.json keeps empty values. Use the keyword format (`Host=...;SSL Mode=Require`): Npgsql 10 rejects the console's `postgresql://` URI, and `Trust Server Certificate` is obsolete (does nothing). Scale-to-zero suspends compute after 5 minutes idle by default (about 300-500ms cold start), so configure `EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: 10s)` and `CommandTimeout(30)`; suspend timeout is set at branch creation (`--suspend-timeout`, `--cu` range) or for an existing compute via Console/API (`suspend_timeout_seconds`, 60-604800), never by recreating main. Branches are copy-on-write: never leave them without expiration (`--expires-at`); `feature/*` 7 days, `ci-*` 1 day, `main` and `dev` never. neonctl: `auth`, `branches create|reset|delete`."
|
|
2670
1713
|
},
|
|
2671
1714
|
{
|
|
2672
1715
|
"id": "integration-mcp-mcp-tools",
|
|
@@ -2681,20 +1724,11 @@
|
|
|
2681
1724
|
"stacks": [
|
|
2682
1725
|
"*"
|
|
2683
1726
|
],
|
|
2684
|
-
"aliases": [
|
|
2685
|
-
"MCP",
|
|
2686
|
-
"Model Context Protocol",
|
|
2687
|
-
"Claude tools",
|
|
2688
|
-
"MCP server",
|
|
2689
|
-
"MCP client"
|
|
2690
|
-
],
|
|
2691
1727
|
"anchors": {
|
|
2692
1728
|
"tools": "#mcp-providers-by-phase",
|
|
2693
1729
|
"setup": "#mcp-availability-detection"
|
|
2694
1730
|
},
|
|
2695
|
-
"digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Tool names follow where the server is configured: mcp__context7__query-docs from .mcp.json, mcp__plugin_context7_context7__query-docs from the plugin; a deferred tool loads via ToolSearch. Test tools with MCP Inspector before integrating."
|
|
2696
|
-
"verifiedAgainst": "Claude Code 2.1.282 MCP tooling — context7, Playwright (as plugin and as a project MCP server); GitHub via `gh` CLI; Neon via its MCP or `neonctl`.",
|
|
2697
|
-
"provedBy": []
|
|
1731
|
+
"digest": "MCP tools extend Claude with external data and actions. Tools are synchronous request-response with typed inputSchema. Resources provide read-only data access. Prompts are reusable templates. Register MCP servers in .claude/settings.json. Prefer @context7 for live library docs. Tool names follow where the server is configured: mcp__context7__query-docs from .mcp.json, mcp__plugin_context7_context7__query-docs from the plugin; a deferred tool loads via ToolSearch. Test tools with MCP Inspector before integrating."
|
|
2698
1732
|
},
|
|
2699
1733
|
{
|
|
2700
1734
|
"id": "frontend-admin-app-data-table",
|
|
@@ -2709,27 +1743,13 @@
|
|
|
2709
1743
|
"stacks": [
|
|
2710
1744
|
"nextjs"
|
|
2711
1745
|
],
|
|
2712
|
-
"aliases": [
|
|
2713
|
-
"data table",
|
|
2714
|
-
"admin list",
|
|
2715
|
-
"server-side pagination",
|
|
2716
|
-
"paginação",
|
|
2717
|
-
"tabela de dados",
|
|
2718
|
-
"ordenação no servidor",
|
|
2719
|
-
"empty state",
|
|
2720
|
-
"row selection",
|
|
2721
|
-
"bulk actions",
|
|
2722
|
-
"manualPagination"
|
|
2723
|
-
],
|
|
2724
1746
|
"anchors": {
|
|
2725
1747
|
"contract": "#the-list-contract-url-and-api",
|
|
2726
1748
|
"table": "#the-shared-datatable-tier-2",
|
|
2727
1749
|
"states": "#the-three-states",
|
|
2728
1750
|
"mobile": "#mobile"
|
|
2729
1751
|
},
|
|
2730
|
-
"digest": "Every admin list is paged, sorted and filtered on the server, with page/pageSize/sort/dir/filters in the URL (searchParams parsed by a Zod schema with .catch defaults) and the API answering { items, totalCount } over an OrderBy plus a unique id tiebreaker. The Server Component page reads searchParams and wraps the results in a Suspense keyed by the query; one shared components/data-table.tsx on TanStack Table 9 sets manualPagination + rowCount, manualSorting and selection by a stable getRowId, and only writes the URL (router.replace with scroll:false, page reset on any filter/sort change). Three non-happy states always: loading skeleton, empty (no records vs no match for the filters), error as a value with retry via router.refresh(). Compact density, right-aligned tabular numbers, one inline row action plus a menu, a bulk bar over the selected page, a card list below md. Never 'first N + see all' instead of paging."
|
|
2731
|
-
"verifiedAgainst": "Next.js 16 App Router + @tanstack/react-table 9 + Zod 4 + shadcn 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2732
|
-
"provedBy": []
|
|
1752
|
+
"digest": "Every admin list is paged, sorted and filtered on the server, with page/pageSize/sort/dir/filters in the URL (searchParams parsed by a Zod schema with .catch defaults) and the API answering { items, totalCount } over an OrderBy plus a unique id tiebreaker. The Server Component page reads searchParams and wraps the results in a Suspense keyed by the query; one shared components/data-table.tsx on TanStack Table 9 sets manualPagination + rowCount, manualSorting and selection by a stable getRowId, and only writes the URL (router.replace with scroll:false, page reset on any filter/sort change). Three non-happy states always: loading skeleton, empty (no records vs no match for the filters), error as a value with retry via router.refresh(). Compact density, right-aligned tabular numbers, one inline row action plus a menu, a bulk bar over the selected page, a card list below md. Never 'first N + see all' instead of paging."
|
|
2733
1753
|
},
|
|
2734
1754
|
{
|
|
2735
1755
|
"id": "frontend-admin-app-feedback",
|
|
@@ -2744,24 +1764,12 @@
|
|
|
2744
1764
|
"stacks": [
|
|
2745
1765
|
"nextjs"
|
|
2746
1766
|
],
|
|
2747
|
-
"aliases": [
|
|
2748
|
-
"confirmation dialog",
|
|
2749
|
-
"confirm dialog",
|
|
2750
|
-
"delete confirmation",
|
|
2751
|
-
"toast",
|
|
2752
|
-
"sonner",
|
|
2753
|
-
"toast messages",
|
|
2754
|
-
"undo toast",
|
|
2755
|
-
"diálogo de confirmação"
|
|
2756
|
-
],
|
|
2757
1767
|
"anchors": {
|
|
2758
1768
|
"when": "#when-to-confirm",
|
|
2759
1769
|
"dialog": "#the-confirmation-dialog-tier-2",
|
|
2760
1770
|
"catalog": "#the-toast-catalog"
|
|
2761
1771
|
},
|
|
2762
|
-
"digest": "One ConfirmDialog (controlled shadcn AlertDialog, a plain Button that awaits the action, stays open showing the error on failure, optional type-to-confirm) used only for destructive-irreversible, bulk or discard-changes actions; a reversible action gets a 'Desfazer' toast instead. The title names the object and the confirm label repeats the verb. Every toast string comes from a catalog (lib/toast-messages.ts for generic outcomes + per-entity EntityMessages, because Portuguese agrees in gender and number); sonner toast.success/toast.error with an id per action, a longer duration for errors, errors mapped by status (403, 409, network), never the server's raw text, and field validation never in a toast."
|
|
2763
|
-
"verifiedAgainst": "shadcn 4 `AlertDialog` (Radix) + sonner 2 + TanStack Query 5 (the code blocks typecheck under `tsc --strict`).",
|
|
2764
|
-
"provedBy": []
|
|
1772
|
+
"digest": "One ConfirmDialog (controlled shadcn AlertDialog, a plain Button that awaits the action, stays open showing the error on failure, optional type-to-confirm) used only for destructive-irreversible, bulk or discard-changes actions; a reversible action gets a 'Desfazer' toast instead. The title names the object and the confirm label repeats the verb. Every toast string comes from a catalog (lib/toast-messages.ts for generic outcomes + per-entity EntityMessages, because Portuguese agrees in gender and number); sonner toast.success/toast.error with an id per action, a longer duration for errors, errors mapped by status (403, 409, network), never the server's raw text, and field validation never in a toast."
|
|
2765
1773
|
},
|
|
2766
1774
|
{
|
|
2767
1775
|
"id": "frontend-admin-app-forms",
|
|
@@ -2776,22 +1784,12 @@
|
|
|
2776
1784
|
"stacks": [
|
|
2777
1785
|
"nextjs"
|
|
2778
1786
|
],
|
|
2779
|
-
"aliases": [
|
|
2780
|
-
"admin form",
|
|
2781
|
-
"crud form",
|
|
2782
|
-
"server validation errors",
|
|
2783
|
-
"ValidationProblemDetails",
|
|
2784
|
-
"unsaved changes",
|
|
2785
|
-
"formulário"
|
|
2786
|
-
],
|
|
2787
1787
|
"anchors": {
|
|
2788
1788
|
"server-errors": "#server-errors-on-the-field",
|
|
2789
1789
|
"placement": "#where-the-form-lives",
|
|
2790
1790
|
"dirty": "#unsaved-changes"
|
|
2791
1791
|
},
|
|
2792
|
-
"digest": "Admin forms use the one pattern of nextjs/forms.md (Zod schema, zodResolver, react-hook-form Controller + shadcn Field/FieldError, useMutation) plus the CRUD deltas: map .NET ValidationProblemDetails errors onto their fields with setError (unmatched ones go to root.serverError), mode 'onTouched', submit disabled only while saving, form.reset(values) after a successful save, a guard for unsaved changes, the three useForm generics (z.input / unknown / z.output) when the schema coerces, and placement by size: dialog up to 4 fields, sheet or page up to 12, its own page beyond that, a wizard when steps depend on each other."
|
|
2793
|
-
"verifiedAgainst": "react-hook-form 7 + @hookform/resolvers 5 + Zod 4 + TanStack Query 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2794
|
-
"provedBy": []
|
|
1792
|
+
"digest": "Admin forms use the one pattern of nextjs/forms.md (Zod schema, zodResolver, react-hook-form Controller + shadcn Field/FieldError, useMutation) plus the CRUD deltas: map .NET ValidationProblemDetails errors onto their fields with setError (unmatched ones go to root.serverError), mode 'onTouched', submit disabled only while saving, form.reset(values) after a successful save, a guard for unsaved changes, the three useForm generics (z.input / unknown / z.output) when the schema coerces, and placement by size: dialog up to 4 fields, sheet or page up to 12, its own page beyond that, a wizard when steps depend on each other."
|
|
2795
1793
|
},
|
|
2796
1794
|
{
|
|
2797
1795
|
"id": "frontend-admin-app-page-structure",
|
|
@@ -2806,24 +1804,12 @@
|
|
|
2806
1804
|
"stacks": [
|
|
2807
1805
|
"nextjs"
|
|
2808
1806
|
],
|
|
2809
|
-
"aliases": [
|
|
2810
|
-
"tabs",
|
|
2811
|
-
"abas",
|
|
2812
|
-
"long page",
|
|
2813
|
-
"página longa",
|
|
2814
|
-
"wizard",
|
|
2815
|
-
"multi-step form",
|
|
2816
|
-
"stepper",
|
|
2817
|
-
"detail page"
|
|
2818
|
-
],
|
|
2819
1807
|
"anchors": {
|
|
2820
1808
|
"decision": "#tabs-long-page-or-wizard",
|
|
2821
1809
|
"tabs": "#tabs-in-the-url",
|
|
2822
1810
|
"wizard": "#wizard-for-long-flows"
|
|
2823
1811
|
},
|
|
2824
|
-
"digest": "Decide per screen and write it in the mockup checklist: tabs for 3+ independent sections, a long page with sticky section nav when sections are read together or share one save, a plain page for 2 short blocks, a wizard when later fields depend on earlier answers. The active tab lives in ?tab= and only its content is rendered and fetched on the server; switching tab drops the list params of the tab being left. Unbounded related lists get their own paged tab, never a capped preview. A wizard keeps one react-hook-form across steps, validates each step with trigger(stepFields), never loses data going back, and commits on a final review step."
|
|
2825
|
-
"verifiedAgainst": "Next.js 16 App Router + shadcn 4 `Tabs` (Radix) + react-hook-form 7 + Zod 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2826
|
-
"provedBy": []
|
|
1812
|
+
"digest": "Decide per screen and write it in the mockup checklist: tabs for 3+ independent sections, a long page with sticky section nav when sections are read together or share one save, a plain page for 2 short blocks, a wizard when later fields depend on earlier answers. The active tab lives in ?tab= and only its content is rendered and fetched on the server; switching tab drops the list params of the tab being left. Unbounded related lists get their own paged tab, never a capped preview. A wizard keeps one react-hook-form across steps, validates each step with trigger(stepFields), never loses data going back, and commits on a final review step."
|
|
2827
1813
|
},
|
|
2828
1814
|
{
|
|
2829
1815
|
"id": "architecture-code-comments",
|
|
@@ -2838,22 +1824,12 @@
|
|
|
2838
1824
|
"stacks": [
|
|
2839
1825
|
"*"
|
|
2840
1826
|
],
|
|
2841
|
-
"aliases": [
|
|
2842
|
-
"code comments",
|
|
2843
|
-
"comentário de código",
|
|
2844
|
-
"comment policy",
|
|
2845
|
-
"política de comentário",
|
|
2846
|
-
"comentário de processo",
|
|
2847
|
-
"self-documenting code"
|
|
2848
|
-
],
|
|
2849
1827
|
"anchors": {
|
|
2850
1828
|
"comment-policy": "#o-que-merece-comentário",
|
|
2851
1829
|
"not-worth": "#o-que-não-merece",
|
|
2852
1830
|
"size": "#tamanho"
|
|
2853
1831
|
},
|
|
2854
|
-
"digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and decisions.md; in a test the link to the postcondition goes in the test NAME), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. A heavily commented neighbour file is not a model. The comment-guard hook blocks an edit that ADDS process history, a banner/#region, a block over 3 lines or a doc-comment outside a published package; the verify validators node (code-budget) warns on added comments that cite process and on added comment density."
|
|
2855
|
-
"verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo).",
|
|
2856
|
-
"provedBy": []
|
|
1832
|
+
"digest": "Default is NO comment: code explains itself through names, types and tests. Before commenting, rename, extract a function or named constant, encode the state in the type, or write a test; comment only what none of these can express: an invariant the type does not express, an order that matters, an external quirk (third-party API, library bug with its link), or the contract of a package published for consumers outside the repo. Never process history (task, UC, ADR, FR, Gate, evaluator, spec.md, mockup: commit message and decisions.md; in a test the link to the postcondition goes in the test NAME), never restate the code, no XML doc/JSDoc on application members that repeats the signature, no file headers, section banners, #region, commented-out code or loose TODOs, no claims about behavior (make them test assertions). One line is normal, ~3 is the ceiling; more than one comment line per ten code lines means the names are not doing the work. A heavily commented neighbour file is not a model. The comment-guard hook blocks an edit that ADDS process history, a banner/#region, a block over 3 lines or a doc-comment outside a published package; the verify validators node (code-budget) warns on added comments that cite process and on added comment density."
|
|
2857
1833
|
},
|
|
2858
1834
|
{
|
|
2859
1835
|
"id": "ai-agents-tool-adapters",
|
|
@@ -2870,18 +1846,6 @@
|
|
|
2870
1846
|
"stacks": [
|
|
2871
1847
|
"dotnet"
|
|
2872
1848
|
],
|
|
2873
|
-
"aliases": [
|
|
2874
|
-
"tool fina",
|
|
2875
|
-
"adaptador de tool",
|
|
2876
|
-
"Ops",
|
|
2877
|
-
"Desfecho",
|
|
2878
|
-
"Render",
|
|
2879
|
-
"regra de negocio em tool",
|
|
2880
|
-
"descricao da tool",
|
|
2881
|
-
"golden de tool",
|
|
2882
|
-
"refactor neutro",
|
|
2883
|
-
"migracao de teste"
|
|
2884
|
-
],
|
|
2885
1849
|
"anchors": {
|
|
2886
1850
|
"fronteira": "#fronteira-com-os-outros-standards",
|
|
2887
1851
|
"problema": "#o-problema",
|
|
@@ -2892,100 +1856,7 @@
|
|
|
2892
1856
|
"reais": "#exemplos-reais",
|
|
2893
1857
|
"regras": "#regras"
|
|
2894
1858
|
},
|
|
2895
|
-
"digest": "A tool that carries business rule keeps apart three things that change for different reasons: the RULE (sealed class XOps(...), primary constructor, explicit AddScoped; never *Service, which MORPH.md bans as a layer between handler and data), the sentence the MODEL reads, and the EFFECT a human sees. Each Ops method returns a typed outcome (abstract record XDesfecho, one sealed record per case); the model-facing text (description, refusal, self-correction, success, the ToolUnavailable decision) lives in the adapter, in public static object Render(XDesfecho), while human text delivered by code (task body, note, ack) stays in Ops because it is an effect. Checkable by grep: Ops names no tool and instructs no model. The adapter (AIFunctionFactory.Create) only builds the typed request, calls Ops and returns Render: zero rule, not a line count, and a long dynamic description stays byte for byte. Two test layers: Ops tests assert outcome, calls and order on the fake, never text; contract tests assert Render's exact literal and .Description, with a golden of name, description and schema per real tenant. A neutral refactor is proven before any test moves: a proof commit with the old suite untouched and green (offline evals included), then the migration, with mutation run only against the new tests. \"Already covered\" needs proof assertion by assertion (11 of 22 such claims had lost one); the tests left at the origin must be identical to the base; mutation on the new tests found rules the old suite never protected. The C# blocks were compiled against the pin and the three xunit tests run."
|
|
2896
|
-
"verifiedAgainst": "Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08); os dois blocos C# abaixo foram extraídos com `scripts/extract-doc-csharp.mjs` e compilados contra o pin com `TreatWarningsAsErrors`, e os três testes do segundo bloco foram executados e passam. **A separação em si e a migração de teste são verificação documental:** vêm da feature `tools-finas` do MORPH_GHLBrain e o ai-kit não as traz.",
|
|
2897
|
-
"provedBy": []
|
|
1859
|
+
"digest": "A tool that carries business rule keeps apart three things that change for different reasons: the RULE (sealed class XOps(...), primary constructor, explicit AddScoped; never *Service, which MORPH.md bans as a layer between handler and data), the sentence the MODEL reads, and the EFFECT a human sees. Each Ops method returns a typed outcome (abstract record XDesfecho, one sealed record per case); the model-facing text (description, refusal, self-correction, success, the ToolUnavailable decision) lives in the adapter, in public static object Render(XDesfecho), while human text delivered by code (task body, note, ack) stays in Ops because it is an effect. Checkable by grep: Ops names no tool and instructs no model. The adapter (AIFunctionFactory.Create) only builds the typed request, calls Ops and returns Render: zero rule, not a line count, and a long dynamic description stays byte for byte. Two test layers: Ops tests assert outcome, calls and order on the fake, never text; contract tests assert Render's exact literal and .Description, with a golden of name, description and schema per real tenant. A neutral refactor is proven before any test moves: a proof commit with the old suite untouched and green (offline evals included), then the migration, with mutation run only against the new tests. \"Already covered\" needs proof assertion by assertion (11 of 22 such claims had lost one); the tests left at the origin must be identical to the base; mutation on the new tests found rules the old suite never protected. The C# blocks were compiled against the pin and the three xunit tests run."
|
|
2898
1860
|
}
|
|
2899
|
-
]
|
|
2900
|
-
"sharedAliases": {
|
|
2901
|
-
"agent middleware": [
|
|
2902
|
-
"ai-agents-production",
|
|
2903
|
-
"ai-agents-middleware-patterns"
|
|
2904
|
-
],
|
|
2905
|
-
"anti-hallucination": [
|
|
2906
|
-
"ai-agents-production",
|
|
2907
|
-
"ai-agents-middleware-patterns"
|
|
2908
|
-
],
|
|
2909
|
-
"agent observability": [
|
|
2910
|
-
"ai-agents-production",
|
|
2911
|
-
"ai-agents-observability-patterns"
|
|
2912
|
-
],
|
|
2913
|
-
"agent telemetry": [
|
|
2914
|
-
"ai-agents-production",
|
|
2915
|
-
"ai-agents-observability-patterns"
|
|
2916
|
-
],
|
|
2917
|
-
"when not to use workflow": [
|
|
2918
|
-
"ai-agents-workflows",
|
|
2919
|
-
"ai-agents-sweet-spot"
|
|
2920
|
-
],
|
|
2921
|
-
"rag": [
|
|
2922
|
-
"ai-agents-rag-custom-pgvector",
|
|
2923
|
-
"backend-database-vector-search-rag"
|
|
2924
|
-
],
|
|
2925
|
-
"vector search": [
|
|
2926
|
-
"ai-agents-rag-custom-pgvector",
|
|
2927
|
-
"backend-database-vector-search-rag"
|
|
2928
|
-
],
|
|
2929
|
-
"pgvector": [
|
|
2930
|
-
"ai-agents-rag-custom-pgvector",
|
|
2931
|
-
"backend-database-vector-search-rag",
|
|
2932
|
-
"infrastructure-neon-pgvector"
|
|
2933
|
-
],
|
|
2934
|
-
"embeddings": [
|
|
2935
|
-
"ai-agents-rag-custom-pgvector",
|
|
2936
|
-
"backend-database-vector-search-rag"
|
|
2937
|
-
],
|
|
2938
|
-
"semantic search": [
|
|
2939
|
-
"ai-agents-rag-custom-pgvector",
|
|
2940
|
-
"backend-database-vector-search-rag"
|
|
2941
|
-
],
|
|
2942
|
-
"middleware": [
|
|
2943
|
-
"ai-agents-middleware-patterns",
|
|
2944
|
-
"frontend-nextjs-nextjs-patterns"
|
|
2945
|
-
],
|
|
2946
|
-
"image generation": [
|
|
2947
|
-
"ai-agents-modalities-image-gen",
|
|
2948
|
-
"frontend-design-system-ai-image-generation"
|
|
2949
|
-
],
|
|
2950
|
-
"mcp": [
|
|
2951
|
-
"ai-agents-mcp-tools",
|
|
2952
|
-
"integration-mcp-mcp-tools"
|
|
2953
|
-
],
|
|
2954
|
-
"model context protocol": [
|
|
2955
|
-
"ai-agents-mcp-tools",
|
|
2956
|
-
"integration-mcp-mcp-tools"
|
|
2957
|
-
],
|
|
2958
|
-
"mcp client": [
|
|
2959
|
-
"ai-agents-mcp-tools",
|
|
2960
|
-
"integration-mcp-mcp-tools"
|
|
2961
|
-
],
|
|
2962
|
-
"mcp server": [
|
|
2963
|
-
"ai-agents-mcp-server",
|
|
2964
|
-
"integration-mcp-mcp-tools"
|
|
2965
|
-
],
|
|
2966
|
-
"feature slice": [
|
|
2967
|
-
"architecture-vertical-slice-vertical-slice",
|
|
2968
|
-
"backend-dotnet-vsa-handler-patterns"
|
|
2969
|
-
],
|
|
2970
|
-
"component naming": [
|
|
2971
|
-
"frontend-design-system-naming",
|
|
2972
|
-
"frontend-nextjs-naming-conventions"
|
|
2973
|
-
],
|
|
2974
|
-
"video generation": [
|
|
2975
|
-
"ai-agents-media-video",
|
|
2976
|
-
"frontend-design-system-ai-video-generation"
|
|
2977
|
-
],
|
|
2978
|
-
"seedance": [
|
|
2979
|
-
"ai-agents-media-video",
|
|
2980
|
-
"frontend-design-system-ai-video-generation"
|
|
2981
|
-
],
|
|
2982
|
-
"test budget": [
|
|
2983
|
-
"backend-dotnet-testing",
|
|
2984
|
-
"frontend-nextjs-testing"
|
|
2985
|
-
],
|
|
2986
|
-
"orçamento de teste": [
|
|
2987
|
-
"backend-dotnet-testing",
|
|
2988
|
-
"frontend-nextjs-testing"
|
|
2989
|
-
]
|
|
2990
|
-
}
|
|
1861
|
+
]
|
|
2991
1862
|
}
|