@polymorphism-tech/morph-spec 8.47.0 → 9.1.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 +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- 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/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -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 +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- 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/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/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- 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/dotnet-test-introspection.js +0 -632
- 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/utils/agent-id-mapper.js +0 -11
|
@@ -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 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()`."
|
|
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. Em 2026-10-03 os blocos da API, do `TranscriptionService`, do agente downstream (dentro de um método-invólucro) e do registro DI compilaram com `TreatWarningsAsErrors` contra `OpenAI` 2.13.0 + `Microsoft.Agents.AI` 1.20.0 (o pin de 2026-09-08, que é a data que este campo carrega).",
|
|
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": "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."
|
|
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 (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."
|
|
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 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
|
|
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. Em 2026-10-03, contra o mesmo pin, compilaram com `TreatWarningsAsErrors` os blocos de registro do `IEmbeddingGenerator`, do `VectorSearchTool` (EF Core + Pgvector.EntityFrameworkCore 0.3.0) e do *Exemplo de uso* (a ponte `Microsoft.Extensions.AI.OpenAI` 10.9.0 resolveu `OpenAI` 2.12.0); a forma anterior `RunAsync(question, ct)` não compilava (CS1503).",
|
|
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 plan.md (## Decisões) 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": "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 `
|
|
1316
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + EF Core 10.0.12 + FluentValidation 12.1.1 + Scrutor 7. Os blocos da slice (Handler, Validator, Endpoint) compilam com `TreatWarningsAsErrors` sobre o `Result`/`Error`/`ErrorHttpMapping` de `result-pattern.md` nos namespaces da árvore acima (`{ProjectName}` substituído; o Handler com `global using` de Abstractions/Database/Entities, o Endpoint só com os próprios `using`); Abstractions, Pipelines e DI são verificação documental.",
|
|
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 `plan.md` (`## Decisões`). Não misture VSA com DDD."
|
|
1318
805
|
},
|
|
1319
806
|
{
|
|
1320
807
|
"id": "backend-api-validation",
|
|
@@ -1329,21 +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
820
|
"fluent": "#fluentvalidation-validator-in-the-slice",
|
|
1341
821
|
"rules": "#validation-patterns",
|
|
1342
822
|
"state": "#state-checks-in-the-handler"
|
|
1343
823
|
},
|
|
1344
|
-
"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
|
|
1345
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + FluentValidation 12.1.1 + EF Core 10.0.12. The validator, `OrderStatus`, `OrderErrors` and state-check handler blocks compile with `TreatWarningsAsErrors` over the `Result`/`Error`/`ValidationError` of `result-pattern.md`.",
|
|
1346
|
-
"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 plan.md (## Decisões) and test each."
|
|
1347
825
|
},
|
|
1348
826
|
{
|
|
1349
827
|
"id": "backend-database-postgresql-database",
|
|
@@ -1359,20 +837,11 @@
|
|
|
1359
837
|
"stacks": [
|
|
1360
838
|
"dotnet"
|
|
1361
839
|
],
|
|
1362
|
-
"aliases": [
|
|
1363
|
-
"PostgreSQL",
|
|
1364
|
-
"postgres",
|
|
1365
|
-
"pg",
|
|
1366
|
-
"database design",
|
|
1367
|
-
"SQL schema"
|
|
1368
|
-
],
|
|
1369
840
|
"anchors": {
|
|
1370
841
|
"schema": "#schema-design",
|
|
1371
842
|
"indexes": "#indexes"
|
|
1372
843
|
},
|
|
1373
|
-
"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."
|
|
1374
|
-
"verifiedAgainst": "PostgreSQL 16/17 + EF Core 10.0.12 (Npgsql provider 10.0.3) + @neondatabase/serverless. The EF Core query blocks compile with `TreatWarningsAsErrors` inside a wrapper method over stand-in entities; the SQL is documentary.",
|
|
1375
|
-
"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."
|
|
1376
845
|
},
|
|
1377
846
|
{
|
|
1378
847
|
"id": "backend-database-vector-search-rag",
|
|
@@ -1389,20 +858,11 @@
|
|
|
1389
858
|
"nextjs",
|
|
1390
859
|
"neon"
|
|
1391
860
|
],
|
|
1392
|
-
"aliases": [
|
|
1393
|
-
"vector search",
|
|
1394
|
-
"RAG",
|
|
1395
|
-
"embeddings",
|
|
1396
|
-
"pgvector",
|
|
1397
|
-
"semantic search"
|
|
1398
|
-
],
|
|
1399
861
|
"anchors": {
|
|
1400
862
|
"chunking": "#📥-indexação-de-documentos",
|
|
1401
863
|
"retrieval": "#🔍-vector-search-com-ef-core-10"
|
|
1402
864
|
},
|
|
1403
|
-
"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+)."
|
|
1404
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + Pgvector.EntityFrameworkCore 0.3.0 + Microsoft.Extensions.AI 10.9.0 + Microsoft.Agents.AI 1.20.0 (ai-pin 2026-09-08). Os blocos de indexação, indexação em lote, busca+geração (RAG), endpoint e job compilam com `TreatWarningsAsErrors` sobre o `Result`/`IHandler`/`ErrorHttpMapping` de `result-pattern.md`/`vertical-slice.md` e o `Document`/`ApplicationDbContext` de `neon-pgvector.md`; o registro do `IEmbeddingGenerator` por provedor, os custos e o SQL são verificação documental.",
|
|
1405
|
-
"provedBy": []
|
|
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+)."
|
|
1406
866
|
},
|
|
1407
867
|
{
|
|
1408
868
|
"id": "backend-dotnet-fluent-validation-vsa",
|
|
@@ -1418,21 +878,12 @@
|
|
|
1418
878
|
"stacks": [
|
|
1419
879
|
"dotnet"
|
|
1420
880
|
],
|
|
1421
|
-
"aliases": [
|
|
1422
|
-
"FluentValidation VSA",
|
|
1423
|
-
"validation decorator",
|
|
1424
|
-
"validation pipeline",
|
|
1425
|
-
"AbstractValidator",
|
|
1426
|
-
"ValidationDecorator"
|
|
1427
|
-
],
|
|
1428
881
|
"anchors": {
|
|
1429
882
|
"validator": "#validator-structure",
|
|
1430
883
|
"decorator": "#validationdecorator-pattern",
|
|
1431
884
|
"conversion": "#fluentvalidation-to-validationerror-conversion"
|
|
1432
885
|
},
|
|
1433
|
-
"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`."
|
|
1434
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + FluentValidation 12.1.1. The validator block and the decorator (its three blocks assembled into one class) compile with `TreatWarningsAsErrors` over the `Result`/`ValidationError` of `result-pattern.md`, and the validator was run with `FakeTimeProvider` across a year change to show the year is read per validation.",
|
|
1435
|
-
"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`."
|
|
1436
887
|
},
|
|
1437
888
|
{
|
|
1438
889
|
"id": "backend-dotnet-result-pattern",
|
|
@@ -1448,21 +899,12 @@
|
|
|
1448
899
|
"stacks": [
|
|
1449
900
|
"dotnet"
|
|
1450
901
|
],
|
|
1451
|
-
"aliases": [
|
|
1452
|
-
"Result<T>",
|
|
1453
|
-
"railway oriented",
|
|
1454
|
-
"error handling",
|
|
1455
|
-
"Match pattern",
|
|
1456
|
-
"Result monad"
|
|
1457
|
-
],
|
|
1458
902
|
"anchors": {
|
|
1459
903
|
"anatomy": "#result-anatomy",
|
|
1460
904
|
"errors": "#error-types-7--custom",
|
|
1461
905
|
"match": "#match-pattern"
|
|
1462
906
|
},
|
|
1463
|
-
"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."
|
|
1464
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + the shared framework `Microsoft.AspNetCore.App` 10 — the HTTP mapping uses `Results`/`IResult`/`StatusCodes`; the `Error`, `ValidationError`, `ResultExtensions`, `ErrorHttpMapping`, endpoint usage and `BookErrors` blocks compile with `TreatWarningsAsErrors` (the Anatomy blocks carry `{ ... }` placeholders and were compiled through a concrete stand-in).",
|
|
1465
|
-
"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."
|
|
1466
908
|
},
|
|
1467
909
|
{
|
|
1468
910
|
"id": "backend-dotnet-scalar-ui",
|
|
@@ -1478,21 +920,12 @@
|
|
|
1478
920
|
"stacks": [
|
|
1479
921
|
"dotnet"
|
|
1480
922
|
],
|
|
1481
|
-
"aliases": [
|
|
1482
|
-
"Scalar",
|
|
1483
|
-
"API documentation",
|
|
1484
|
-
"OpenAPI UI",
|
|
1485
|
-
"Swagger replacement",
|
|
1486
|
-
"Scalar.AspNetCore"
|
|
1487
|
-
],
|
|
1488
923
|
"anchors": {
|
|
1489
924
|
"setup": "#programcs-configuration",
|
|
1490
925
|
"tags": "#apitags-organization",
|
|
1491
926
|
"metadata": "#endpoint-metadata"
|
|
1492
927
|
},
|
|
1493
|
-
"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."
|
|
1494
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Scalar.AspNetCore 2.x + Microsoft.AspNetCore.OpenApi 10. The `ApiTags` and tagged-endpoint blocks compile with `TreatWarningsAsErrors` over the `Result`/`ErrorHttpMapping` of `result-pattern.md` (ApiTags placed in `ProjectName.Constants`); the startup and metadata snippets (`async (...) => { ... }`) are fragments, checked against the docs only.",
|
|
1495
|
-
"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."
|
|
1496
929
|
},
|
|
1497
930
|
{
|
|
1498
931
|
"id": "backend-dotnet-scrutor",
|
|
@@ -1508,21 +941,12 @@
|
|
|
1508
941
|
"stacks": [
|
|
1509
942
|
"dotnet"
|
|
1510
943
|
],
|
|
1511
|
-
"aliases": [
|
|
1512
|
-
"Scrutor",
|
|
1513
|
-
"auto-discovery",
|
|
1514
|
-
"assembly scanning",
|
|
1515
|
-
"decorator registration",
|
|
1516
|
-
"handler registration"
|
|
1517
|
-
],
|
|
1518
944
|
"anchors": {
|
|
1519
945
|
"handlers": "#handler-auto-discovery",
|
|
1520
946
|
"decorators": "#decorator-order",
|
|
1521
947
|
"endpoints": "#endpoint-auto-discovery"
|
|
1522
948
|
},
|
|
1523
|
-
"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."
|
|
1524
|
-
"verifiedAgainst": ".NET 10 + Scrutor 7 (`Scan`/`Decorate` API).",
|
|
1525
|
-
"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."
|
|
1526
950
|
},
|
|
1527
951
|
{
|
|
1528
952
|
"id": "backend-dotnet-testing",
|
|
@@ -1537,26 +961,12 @@
|
|
|
1537
961
|
"stacks": [
|
|
1538
962
|
"dotnet"
|
|
1539
963
|
],
|
|
1540
|
-
"aliases": [
|
|
1541
|
-
"xunit",
|
|
1542
|
-
"TimeProvider",
|
|
1543
|
-
"FakeTimeProvider",
|
|
1544
|
-
"fake clock",
|
|
1545
|
-
"integration test",
|
|
1546
|
-
"ProblemDetails",
|
|
1547
|
-
"error body assertion",
|
|
1548
|
-
"test budget",
|
|
1549
|
-
"orçamento de teste",
|
|
1550
|
-
"[Theory] table"
|
|
1551
|
-
],
|
|
1552
964
|
"anchors": {
|
|
1553
965
|
"clock": "#rule-1--inject-timeprovider-fake-it-with-faketimeprovider-in-tests",
|
|
1554
966
|
"errorbody": "#rule-2--error-path-tests-assert-the-response-body-not-just-the-status-code",
|
|
1555
967
|
"test-budget": "#test-budget"
|
|
1556
968
|
},
|
|
1557
|
-
"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."
|
|
1558
|
-
"verifiedAgainst": ".NET 10 + xUnit 2 + `Microsoft.Extensions.TimeProvider.Testing`.",
|
|
1559
|
-
"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."
|
|
1560
970
|
},
|
|
1561
971
|
{
|
|
1562
972
|
"id": "backend-dotnet-vsa-handler-patterns",
|
|
@@ -1572,22 +982,13 @@
|
|
|
1572
982
|
"stacks": [
|
|
1573
983
|
"dotnet"
|
|
1574
984
|
],
|
|
1575
|
-
"aliases": [
|
|
1576
|
-
"VSA handler",
|
|
1577
|
-
"vertical slice handler",
|
|
1578
|
-
"feature slice",
|
|
1579
|
-
"IHandler pattern",
|
|
1580
|
-
"handler endpoint"
|
|
1581
|
-
],
|
|
1582
985
|
"anchors": {
|
|
1583
986
|
"handler": "#ihandlertrequest-tresponse",
|
|
1584
987
|
"folder": "#feature-folder-structure-5-files-per-slice",
|
|
1585
988
|
"endpoint": "#complete-endpoint-example",
|
|
1586
989
|
"registration": "#programcs-di-registration-order"
|
|
1587
990
|
},
|
|
1588
|
-
"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 `
|
|
1589
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview; minimal APIs, primary constructors, `Guid.CreateVersion7()`). The \"Complete Endpoint Example\" block compiles with `TreatWarningsAsErrors` over the `Result`/`ErrorHttpMapping` of `result-pattern.md` (in `ProjectName.Abstractions`/`ProjectName.Extensions`, reached only through the block's own `using`s); the other blocks are documentary.",
|
|
1590
|
-
"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 `plan.md` (`## Decisões`) justification."
|
|
1591
992
|
},
|
|
1592
993
|
{
|
|
1593
994
|
"id": "backend-integrations-asaas-asaas-api",
|
|
@@ -1603,20 +1004,11 @@
|
|
|
1603
1004
|
"stacks": [
|
|
1604
1005
|
"dotnet"
|
|
1605
1006
|
],
|
|
1606
|
-
"aliases": [
|
|
1607
|
-
"asaas",
|
|
1608
|
-
"payment",
|
|
1609
|
-
"PIX",
|
|
1610
|
-
"boleto",
|
|
1611
|
-
"Brazilian payment"
|
|
1612
|
-
],
|
|
1613
1007
|
"anchors": {
|
|
1614
1008
|
"payments": "#create-pix-payment",
|
|
1615
1009
|
"webhooks": "#webhook-handler"
|
|
1616
1010
|
},
|
|
1617
|
-
"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."
|
|
1618
|
-
"verifiedAgainst": "Asaas REST API v3 (docs read via context7 on 2026-10-03: headers, `POST /payments`, `GET /payments?externalReference=`, `pixQrCode`, webhook payload, delivery and error rules) + .NET 10 (SDK 10.0.400-preview, `IHttpClientFactory` from the ASP.NET Core shared framework) + EF Core 10.0.12. The options, client, registration, PIX slice, webhook slice/endpoint and CPF/CNPJ blocks compile with `TreatWarningsAsErrors` over the `Result`/`ErrorHttpMapping` of `result-pattern.md`; the CPF/CNPJ validator was run against valid and invalid documents (numeric and the alphanumeric `12.ABC.345/01DE-35`), and the `BaseUrl` trailing-slash rule and the `dueDate` JSON shape were checked by running them; the SQL is documentary.",
|
|
1619
|
-
"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."
|
|
1620
1012
|
},
|
|
1621
1013
|
{
|
|
1622
1014
|
"id": "backend-integrations-hangfire-hangfire-jobs",
|
|
@@ -1632,21 +1024,11 @@
|
|
|
1632
1024
|
"stacks": [
|
|
1633
1025
|
"dotnet"
|
|
1634
1026
|
],
|
|
1635
|
-
"aliases": [
|
|
1636
|
-
"hangfire",
|
|
1637
|
-
"background jobs",
|
|
1638
|
-
"scheduled jobs",
|
|
1639
|
-
"recurring",
|
|
1640
|
-
"cron",
|
|
1641
|
-
"fire and forget"
|
|
1642
|
-
],
|
|
1643
1027
|
"anchors": {
|
|
1644
1028
|
"jobs": "#job-types",
|
|
1645
1029
|
"retry": "#retry--error-handling"
|
|
1646
1030
|
},
|
|
1647
|
-
"digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in
|
|
1648
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Hangfire.Core/Hangfire.AspNetCore 1.8.25 + Hangfire.PostgreSql 1.21.1 + Newtonsoft.Json 13.0.4. The startup, job/handler, dashboard-filter and idempotency blocks compile with `TreatWarningsAsErrors`; the Job Types calls compile inside a wrapper method; the per-job DI scope (`AspNetCoreJobActivator` → `IServiceScopeFactory`) and the `PostgreSqlStorageOptions` defaults were read by reflection on those assemblies; the pooled-endpoint limits (LISTEN/NOTIFY, migrations on the direct string) come from the Neon connection-pooling docs. Cron strings are documentary.",
|
|
1649
|
-
"provedBy": []
|
|
1031
|
+
"digest": "Hangfire 1.8.25 on .NET 10, only with a real reason (survive restarts, schedules, deferred retries) recorded in plan.md (## Decisões); 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)."
|
|
1650
1032
|
},
|
|
1651
1033
|
{
|
|
1652
1034
|
"id": "backend-integrations-neon-auth-neon-auth",
|
|
@@ -1663,26 +1045,6 @@
|
|
|
1663
1045
|
"stacks": [
|
|
1664
1046
|
"neon"
|
|
1665
1047
|
],
|
|
1666
|
-
"aliases": [
|
|
1667
|
-
"neon auth",
|
|
1668
|
-
"better auth",
|
|
1669
|
-
"authentication",
|
|
1670
|
-
"OAuth",
|
|
1671
|
-
"JWT",
|
|
1672
|
-
"session management",
|
|
1673
|
-
"trusted origins",
|
|
1674
|
-
"EdDSA",
|
|
1675
|
-
"Ed25519",
|
|
1676
|
-
"BouncyCastle",
|
|
1677
|
-
"signature validator",
|
|
1678
|
-
"jwks",
|
|
1679
|
-
"middleware runtime",
|
|
1680
|
-
"api prefix",
|
|
1681
|
-
"data api",
|
|
1682
|
-
"postgrest",
|
|
1683
|
-
"bypass backend",
|
|
1684
|
-
"direct database access"
|
|
1685
|
-
],
|
|
1686
1048
|
"anchors": {
|
|
1687
1049
|
"setup": "#installation--setup",
|
|
1688
1050
|
"data-access-boundary": "#data-access-boundary--backend-first-by-default",
|
|
@@ -1693,9 +1055,7 @@
|
|
|
1693
1055
|
"rls": "#row-level-security-integration",
|
|
1694
1056
|
"postmortem": "#known-failure-modes-postmortem"
|
|
1695
1057
|
},
|
|
1696
|
-
"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
|
|
1697
|
-
"verifiedAgainst": "Neon Auth — @neondatabase/auth + @neondatabase/auth-ui (Better Auth).",
|
|
1698
|
-
"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 plan.md (## Decisões) 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."
|
|
1699
1059
|
},
|
|
1700
1060
|
{
|
|
1701
1061
|
"id": "backend-integrations-resend-resend-email",
|
|
@@ -1711,20 +1071,11 @@
|
|
|
1711
1071
|
"stacks": [
|
|
1712
1072
|
"dotnet"
|
|
1713
1073
|
],
|
|
1714
|
-
"aliases": [
|
|
1715
|
-
"resend",
|
|
1716
|
-
"email",
|
|
1717
|
-
"transactional email",
|
|
1718
|
-
"SMTP",
|
|
1719
|
-
"email notification"
|
|
1720
|
-
],
|
|
1721
1074
|
"anchors": {
|
|
1722
1075
|
"sending": "#send-email-api-route",
|
|
1723
1076
|
"templates": "#common-email-templates"
|
|
1724
1077
|
},
|
|
1725
|
-
"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)."
|
|
1726
|
-
"verifiedAgainst": "Resend Node SDK 4 + Resend .NET SDK + React Email.",
|
|
1727
|
-
"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)."
|
|
1728
1079
|
},
|
|
1729
1080
|
{
|
|
1730
1081
|
"id": "data-nosql-cache-redis",
|
|
@@ -1741,21 +1092,11 @@
|
|
|
1741
1092
|
"dotnet",
|
|
1742
1093
|
"azure"
|
|
1743
1094
|
],
|
|
1744
|
-
"aliases": [
|
|
1745
|
-
"redis",
|
|
1746
|
-
"cache",
|
|
1747
|
-
"distributed cache",
|
|
1748
|
-
"IDistributedCache",
|
|
1749
|
-
"cache-aside",
|
|
1750
|
-
"StackExchange.Redis"
|
|
1751
|
-
],
|
|
1752
1095
|
"anchors": {
|
|
1753
1096
|
"patterns": "#cache-aside-pattern",
|
|
1754
1097
|
"invalidation": "#ttl-strategy"
|
|
1755
1098
|
},
|
|
1756
|
-
"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
|
|
1757
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Microsoft.Extensions.Caching.StackExchangeRedis 10.0.12 + StackExchange.Redis 2.13.17 + EF Core 10.0.12. The registration, cache-aside handler, invalidation and rate-limit blocks compile with `TreatWarningsAsErrors` over the `Result` of `result-pattern.md`; package dependencies read from the nuget.org nuspec on 2026-10-03.",
|
|
1758
|
-
"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 plan.md (## Decisões) decision, implemented once at the cache boundary)."
|
|
1759
1100
|
},
|
|
1760
1101
|
{
|
|
1761
1102
|
"id": "data-nosql-cosmos-db",
|
|
@@ -1771,20 +1112,11 @@
|
|
|
1771
1112
|
"dotnet",
|
|
1772
1113
|
"azure"
|
|
1773
1114
|
],
|
|
1774
|
-
"aliases": [
|
|
1775
|
-
"cosmos",
|
|
1776
|
-
"CosmosDB",
|
|
1777
|
-
"document database",
|
|
1778
|
-
"partition key",
|
|
1779
|
-
"RUs"
|
|
1780
|
-
],
|
|
1781
1115
|
"anchors": {
|
|
1782
1116
|
"modeling": "#container-design",
|
|
1783
1117
|
"queries": "#handler-in-the-slice"
|
|
1784
1118
|
},
|
|
1785
|
-
"digest": "OPTIONAL — outside the baked-in stack. The default store is Neon (Postgres) via EF Core; Cosmos needs a
|
|
1786
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Microsoft.Azure.Cosmos 3.63.2 + Newtonsoft.Json 13.0.4 — every `csharp` block below compiles with `TreatWarningsAsErrors` against the `Result`/`Error` types of `backend-dotnet-result-pattern`.",
|
|
1787
|
-
"provedBy": []
|
|
1119
|
+
"digest": "OPTIONAL — outside the baked-in stack. The default store is Neon (Postgres) via EF Core; Cosmos needs a plan.md (## Decisões) 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."
|
|
1788
1120
|
},
|
|
1789
1121
|
{
|
|
1790
1122
|
"id": "data-vector-search-rag-chunking",
|
|
@@ -1800,20 +1132,11 @@
|
|
|
1800
1132
|
"dotnet",
|
|
1801
1133
|
"azure"
|
|
1802
1134
|
],
|
|
1803
|
-
"aliases": [
|
|
1804
|
-
"RAG chunking",
|
|
1805
|
-
"document chunking",
|
|
1806
|
-
"text splitting",
|
|
1807
|
-
"chunk overlap",
|
|
1808
|
-
"embedding chunks"
|
|
1809
|
-
],
|
|
1810
1135
|
"anchors": {
|
|
1811
1136
|
"strategies": "#chunking-strategies",
|
|
1812
1137
|
"retrieval": "#chunking-strategies"
|
|
1813
1138
|
},
|
|
1814
|
-
"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."
|
|
1815
|
-
"verifiedAgainst": ".NET 10 (chunking algorithms — language-level, no external package).",
|
|
1816
|
-
"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."
|
|
1817
1140
|
},
|
|
1818
1141
|
{
|
|
1819
1142
|
"id": "frontend-design-system-ai-image-generation",
|
|
@@ -1832,16 +1155,6 @@
|
|
|
1832
1155
|
"stacks": [
|
|
1833
1156
|
"*"
|
|
1834
1157
|
],
|
|
1835
|
-
"aliases": [
|
|
1836
|
-
"Image Generation",
|
|
1837
|
-
"AI Assets",
|
|
1838
|
-
"Gemini Images",
|
|
1839
|
-
"brand fidelity",
|
|
1840
|
-
"reference images",
|
|
1841
|
-
"favicon",
|
|
1842
|
-
"og image",
|
|
1843
|
-
"asset generation"
|
|
1844
|
-
],
|
|
1845
1158
|
"anchors": {
|
|
1846
1159
|
"when-to-use": "#when-to-use-ai-generated-images",
|
|
1847
1160
|
"prompt-patterns": "#prompt-engineering-patterns",
|
|
@@ -1853,9 +1166,7 @@
|
|
|
1853
1166
|
"favicon-og": "#the-two-assets-everyone-forgets",
|
|
1854
1167
|
"variations": "#variations-and-cost"
|
|
1855
1168
|
},
|
|
1856
|
-
"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."
|
|
1857
|
-
"verifiedAgainst": "morph-assets MCP (OpenRouter; default model `openai/gpt-image-2.5-flare`) + Next.js 15 `next/image`.",
|
|
1858
|
-
"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."
|
|
1859
1170
|
},
|
|
1860
1171
|
{
|
|
1861
1172
|
"id": "frontend-design-system-ai-video-generation",
|
|
@@ -1874,17 +1185,6 @@
|
|
|
1874
1185
|
"stacks": [
|
|
1875
1186
|
"*"
|
|
1876
1187
|
],
|
|
1877
|
-
"aliases": [
|
|
1878
|
-
"video generation",
|
|
1879
|
-
"seedance",
|
|
1880
|
-
"img2vid",
|
|
1881
|
-
"endImage",
|
|
1882
|
-
"directional footage",
|
|
1883
|
-
"loop video",
|
|
1884
|
-
"E005",
|
|
1885
|
-
"animate render",
|
|
1886
|
-
"scroll scrub footage"
|
|
1887
|
-
],
|
|
1888
1188
|
"anchors": {
|
|
1889
1189
|
"loop-vs-directional": "#section-1-the-distinction-that-decides-everything",
|
|
1890
1190
|
"directional": "#section-2-building-directional-footage",
|
|
@@ -1892,9 +1192,7 @@
|
|
|
1892
1192
|
"cost-ladder": "#section-4-the-cost-ladder",
|
|
1893
1193
|
"prompt-skeleton": "#section-5-prompt-skeleton"
|
|
1894
1194
|
},
|
|
1895
|
-
"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."
|
|
1896
|
-
"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`.",
|
|
1897
|
-
"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."
|
|
1898
1196
|
},
|
|
1899
1197
|
{
|
|
1900
1198
|
"id": "frontend-design-system-animations",
|
|
@@ -1909,21 +1207,11 @@
|
|
|
1909
1207
|
"stacks": [
|
|
1910
1208
|
"*"
|
|
1911
1209
|
],
|
|
1912
|
-
"aliases": [
|
|
1913
|
-
"animations",
|
|
1914
|
-
"transitions",
|
|
1915
|
-
"motion",
|
|
1916
|
-
"CSS animations",
|
|
1917
|
-
"keyframes",
|
|
1918
|
-
"prefers-reduced-motion"
|
|
1919
|
-
],
|
|
1920
1210
|
"anchors": {
|
|
1921
1211
|
"tokens": "#stagger--timing",
|
|
1922
1212
|
"principles": "#css-animation-patterns"
|
|
1923
1213
|
},
|
|
1924
|
-
"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`)."
|
|
1925
|
-
"verifiedAgainst": "CSS animations/transitions (language-level, no framework).",
|
|
1926
|
-
"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`)."
|
|
1927
1215
|
},
|
|
1928
1216
|
{
|
|
1929
1217
|
"id": "frontend-design-system-naming",
|
|
@@ -1938,20 +1226,11 @@
|
|
|
1938
1226
|
"stacks": [
|
|
1939
1227
|
"*"
|
|
1940
1228
|
],
|
|
1941
|
-
"aliases": [
|
|
1942
|
-
"BEM",
|
|
1943
|
-
"CSS naming",
|
|
1944
|
-
"class naming",
|
|
1945
|
-
"design token naming",
|
|
1946
|
-
"component naming"
|
|
1947
|
-
],
|
|
1948
1229
|
"anchors": {
|
|
1949
1230
|
"blocks": "#bem-for-reusable-components",
|
|
1950
1231
|
"tokens": "#css-variables-with-namespace"
|
|
1951
1232
|
},
|
|
1952
|
-
"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)."
|
|
1953
|
-
"verifiedAgainst": "CSS (BEM + custom properties — language-level, no framework).",
|
|
1954
|
-
"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)."
|
|
1955
1234
|
},
|
|
1956
1235
|
{
|
|
1957
1236
|
"id": "frontend-design-system-premium-finish",
|
|
@@ -1966,27 +1245,15 @@
|
|
|
1966
1245
|
"stacks": [
|
|
1967
1246
|
"*"
|
|
1968
1247
|
],
|
|
1969
|
-
"aliases": [
|
|
1970
|
-
"premium finish",
|
|
1971
|
-
"polish layer",
|
|
1972
|
-
"film grain",
|
|
1973
|
-
"atmosphere",
|
|
1974
|
-
"load-in stagger",
|
|
1975
|
-
"scrim",
|
|
1976
|
-
"text over video",
|
|
1977
|
-
"metallic accent",
|
|
1978
|
-
"looks like a template"
|
|
1979
|
-
],
|
|
1980
1248
|
"anchors": {
|
|
1981
1249
|
"typography": "#section-1-typography-with-character",
|
|
1982
1250
|
"atmosphere": "#section-2-atmosphere-never-flat-color",
|
|
1983
1251
|
"loadin": "#section-3-one-orchestrated-load-in-beats-ten-micro-interactions",
|
|
1984
1252
|
"legibility": "#section-4-legibility-over-video-close-to-a-hard-rule",
|
|
1985
|
-
"order": "#section-7-order-of-application"
|
|
1253
|
+
"order": "#section-7-order-of-application",
|
|
1254
|
+
"apppatterns": "#section-8-patterns-for-app-screens"
|
|
1986
1255
|
},
|
|
1987
|
-
"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."
|
|
1988
|
-
"verifiedAgainst": "Production landing pages, CSS-only techniques, no runtime dependency.",
|
|
1989
|
-
"provedBy": []
|
|
1256
|
+
"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. App screens too (every ui-designer surface): bottom sheet on mobile with 44px targets, stories-style progress and synchronized highlight sharing --i/--beat via animation-delay, scene cards without real data (never a made-up number), dot grid faded with mask-image, and a composed static frame for prefers-reduced-motion."
|
|
1990
1257
|
},
|
|
1991
1258
|
{
|
|
1992
1259
|
"id": "frontend-gsap-core",
|
|
@@ -2003,16 +1270,6 @@
|
|
|
2003
1270
|
"stacks": [
|
|
2004
1271
|
"*"
|
|
2005
1272
|
],
|
|
2006
|
-
"aliases": [
|
|
2007
|
-
"GSAP",
|
|
2008
|
-
"GreenSock",
|
|
2009
|
-
"gsap.to",
|
|
2010
|
-
"gsap.from",
|
|
2011
|
-
"useGSAP",
|
|
2012
|
-
"gsap timeline",
|
|
2013
|
-
"gsap utils",
|
|
2014
|
-
"gsap performance"
|
|
2015
|
-
],
|
|
2016
1273
|
"anchors": {
|
|
2017
1274
|
"apis": "#section-3-core-animation-apis",
|
|
2018
1275
|
"react": "#section-4-reactnextjs-integration",
|
|
@@ -2020,9 +1277,7 @@
|
|
|
2020
1277
|
"performance": "#section-7-performance",
|
|
2021
1278
|
"accessibility": "#section-8-accessibility"
|
|
2022
1279
|
},
|
|
2023
|
-
"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`."
|
|
2024
|
-
"verifiedAgainst": "GSAP 3.13 + @gsap/react.",
|
|
2025
|
-
"provedBy": []
|
|
1280
|
+
"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`."
|
|
2026
1281
|
},
|
|
2027
1282
|
{
|
|
2028
1283
|
"id": "frontend-gsap-plugins",
|
|
@@ -2038,25 +1293,13 @@
|
|
|
2038
1293
|
"stacks": [
|
|
2039
1294
|
"*"
|
|
2040
1295
|
],
|
|
2041
|
-
"aliases": [
|
|
2042
|
-
"Flip",
|
|
2043
|
-
"SplitText",
|
|
2044
|
-
"MorphSVG",
|
|
2045
|
-
"DrawSVG",
|
|
2046
|
-
"MotionPath",
|
|
2047
|
-
"Draggable",
|
|
2048
|
-
"Observer",
|
|
2049
|
-
"ScrollToPlugin"
|
|
2050
|
-
],
|
|
2051
1296
|
"anchors": {
|
|
2052
1297
|
"flip": "#flip-plugin-free",
|
|
2053
1298
|
"splittext": "#splittext",
|
|
2054
1299
|
"morphsvg": "#morphsvg",
|
|
2055
1300
|
"drawsvg": "#drawsvg"
|
|
2056
1301
|
},
|
|
2057
|
-
"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."
|
|
2058
|
-
"verifiedAgainst": "GSAP 3.13 + @gsap/react.",
|
|
2059
|
-
"provedBy": []
|
|
1302
|
+
"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."
|
|
2060
1303
|
},
|
|
2061
1304
|
{
|
|
2062
1305
|
"id": "frontend-gsap-scrolltrigger",
|
|
@@ -2073,23 +1316,13 @@
|
|
|
2073
1316
|
"stacks": [
|
|
2074
1317
|
"*"
|
|
2075
1318
|
],
|
|
2076
|
-
"aliases": [
|
|
2077
|
-
"ScrollTrigger",
|
|
2078
|
-
"scroll animation",
|
|
2079
|
-
"pinning",
|
|
2080
|
-
"scrubbing",
|
|
2081
|
-
"parallax",
|
|
2082
|
-
"scroll-driven"
|
|
2083
|
-
],
|
|
2084
1319
|
"anchors": {
|
|
2085
1320
|
"basic": "#section-2-basic-scrolltrigger",
|
|
2086
1321
|
"scrub": "#section-3-scrubbing",
|
|
2087
1322
|
"pin": "#section-4-pinning",
|
|
2088
1323
|
"batch": "#section-5-batch-processing"
|
|
2089
1324
|
},
|
|
2090
|
-
"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."
|
|
2091
|
-
"verifiedAgainst": "GSAP 3.13 ScrollTrigger + @gsap/react.",
|
|
2092
|
-
"provedBy": []
|
|
1325
|
+
"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."
|
|
2093
1326
|
},
|
|
2094
1327
|
{
|
|
2095
1328
|
"id": "frontend-nextjs-app-router",
|
|
@@ -2104,20 +1337,11 @@
|
|
|
2104
1337
|
"stacks": [
|
|
2105
1338
|
"nextjs"
|
|
2106
1339
|
],
|
|
2107
|
-
"aliases": [
|
|
2108
|
-
"app router",
|
|
2109
|
-
"Next.js 14",
|
|
2110
|
-
"server components",
|
|
2111
|
-
"app directory",
|
|
2112
|
-
"RSC"
|
|
2113
|
-
],
|
|
2114
1340
|
"anchors": {
|
|
2115
1341
|
"layout": "#root-layout--required-providers",
|
|
2116
1342
|
"routing": "#app-directory-structure"
|
|
2117
1343
|
},
|
|
2118
|
-
"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`."
|
|
2119
|
-
"verifiedAgainst": "Next.js 15.",
|
|
2120
|
-
"provedBy": []
|
|
1344
|
+
"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`."
|
|
2121
1345
|
},
|
|
2122
1346
|
{
|
|
2123
1347
|
"id": "frontend-nextjs-components",
|
|
@@ -2132,20 +1356,11 @@
|
|
|
2132
1356
|
"stacks": [
|
|
2133
1357
|
"nextjs"
|
|
2134
1358
|
],
|
|
2135
|
-
"aliases": [
|
|
2136
|
-
"react components",
|
|
2137
|
-
"server component",
|
|
2138
|
-
"client component",
|
|
2139
|
-
"shadcn/ui",
|
|
2140
|
-
"UI components"
|
|
2141
|
-
],
|
|
2142
1359
|
"anchors": {
|
|
2143
1360
|
"patterns": "#three-tier-hierarchy",
|
|
2144
1361
|
"shadcn": "#tier-1--shadcnui-primitives"
|
|
2145
1362
|
},
|
|
2146
|
-
"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."
|
|
2147
|
-
"verifiedAgainst": "Next.js 16 + shadcn 4 CLI (the code blocks typecheck under `tsc --strict`).",
|
|
2148
|
-
"provedBy": []
|
|
1363
|
+
"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."
|
|
2149
1364
|
},
|
|
2150
1365
|
{
|
|
2151
1366
|
"id": "frontend-nextjs-data-fetching",
|
|
@@ -2160,20 +1375,11 @@
|
|
|
2160
1375
|
"stacks": [
|
|
2161
1376
|
"nextjs"
|
|
2162
1377
|
],
|
|
2163
|
-
"aliases": [
|
|
2164
|
-
"TanStack Query",
|
|
2165
|
-
"react-query",
|
|
2166
|
-
"server actions",
|
|
2167
|
-
"data fetching",
|
|
2168
|
-
"useQuery"
|
|
2169
|
-
],
|
|
2170
1378
|
"anchors": {
|
|
2171
1379
|
"server": "#server-component--tanstack-query-handoff",
|
|
2172
1380
|
"client": "#usequery-hook-pattern"
|
|
2173
1381
|
},
|
|
2174
|
-
"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."
|
|
2175
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zod 4.",
|
|
2176
|
-
"provedBy": []
|
|
1382
|
+
"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."
|
|
2177
1383
|
},
|
|
2178
1384
|
{
|
|
2179
1385
|
"id": "frontend-nextjs-forms",
|
|
@@ -2188,20 +1394,11 @@
|
|
|
2188
1394
|
"stacks": [
|
|
2189
1395
|
"nextjs"
|
|
2190
1396
|
],
|
|
2191
|
-
"aliases": [
|
|
2192
|
-
"react-hook-form",
|
|
2193
|
-
"Zod",
|
|
2194
|
-
"form validation",
|
|
2195
|
-
"shadcn Field",
|
|
2196
|
-
"Controller"
|
|
2197
|
-
],
|
|
2198
1397
|
"anchors": {
|
|
2199
1398
|
"validation": "#schema-composition",
|
|
2200
1399
|
"submission": "#complete-form-pattern"
|
|
2201
1400
|
},
|
|
2202
|
-
"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."
|
|
2203
|
-
"verifiedAgainst": "Next.js 16 + react-hook-form 7 + Zod 4 + @hookform/resolvers 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2204
|
-
"provedBy": []
|
|
1401
|
+
"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."
|
|
2205
1402
|
},
|
|
2206
1403
|
{
|
|
2207
1404
|
"id": "frontend-nextjs-motion-patterns",
|
|
@@ -2217,22 +1414,13 @@
|
|
|
2217
1414
|
"stacks": [
|
|
2218
1415
|
"nextjs"
|
|
2219
1416
|
],
|
|
2220
|
-
"aliases": [
|
|
2221
|
-
"framer-motion",
|
|
2222
|
-
"animation variants",
|
|
2223
|
-
"motion design",
|
|
2224
|
-
"page transition",
|
|
2225
|
-
"AnimatePresence"
|
|
2226
|
-
],
|
|
2227
1417
|
"anchors": {
|
|
2228
1418
|
"variants": "#section-1-core-variants-library",
|
|
2229
1419
|
"tokens": "#section-2-design-token-bridge",
|
|
2230
1420
|
"scroll": "#section-3-scroll-animations",
|
|
2231
1421
|
"accessibility": "#section-7-wcag-accessibility-mandatory"
|
|
2232
1422
|
},
|
|
2233
|
-
"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."
|
|
2234
|
-
"verifiedAgainst": "Motion for React 12+ (`motion`, imports from `motion/react`); the npm registry and the `motion`/`framer-motion`/`motion-dom` 14.0.0 typings were read on 2026-10-03 (`motion/react` re-exports `framer-motion`, which re-exports `motion-dom`: `Variants`, `useInView`, `AnimatePresence`, `useReducedMotion`, `staggerChildren`/`delayChildren` all present). The TSX snippets are documentary.",
|
|
2235
|
-
"provedBy": []
|
|
1423
|
+
"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."
|
|
2236
1424
|
},
|
|
2237
1425
|
{
|
|
2238
1426
|
"id": "frontend-nextjs-naming-conventions",
|
|
@@ -2247,20 +1435,11 @@
|
|
|
2247
1435
|
"stacks": [
|
|
2248
1436
|
"nextjs"
|
|
2249
1437
|
],
|
|
2250
|
-
"aliases": [
|
|
2251
|
-
"naming conventions",
|
|
2252
|
-
"file naming",
|
|
2253
|
-
"component naming",
|
|
2254
|
-
"Next.js naming",
|
|
2255
|
-
"TypeScript naming"
|
|
2256
|
-
],
|
|
2257
1438
|
"anchors": {
|
|
2258
1439
|
"files": "#complete-reference-table",
|
|
2259
1440
|
"components": "#complete-reference-table"
|
|
2260
1441
|
},
|
|
2261
|
-
"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`)."
|
|
2262
|
-
"verifiedAgainst": "Next.js 15 + Zod 4.",
|
|
2263
|
-
"provedBy": []
|
|
1442
|
+
"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`)."
|
|
2264
1443
|
},
|
|
2265
1444
|
{
|
|
2266
1445
|
"id": "frontend-nextjs-nextjs-patterns",
|
|
@@ -2275,20 +1454,11 @@
|
|
|
2275
1454
|
"stacks": [
|
|
2276
1455
|
"nextjs"
|
|
2277
1456
|
],
|
|
2278
|
-
"aliases": [
|
|
2279
|
-
"Next.js patterns",
|
|
2280
|
-
"middleware",
|
|
2281
|
-
"auth patterns",
|
|
2282
|
-
"caching patterns",
|
|
2283
|
-
"image optimization"
|
|
2284
|
-
],
|
|
2285
1457
|
"anchors": {
|
|
2286
1458
|
"auth": "#route-handlers-bff-pattern",
|
|
2287
1459
|
"caching": "#react-query--neon"
|
|
2288
1460
|
},
|
|
2289
|
-
"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`)."
|
|
2290
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zod 4 + react-hook-form 7.",
|
|
2291
|
-
"provedBy": []
|
|
1461
|
+
"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`)."
|
|
2292
1462
|
},
|
|
2293
1463
|
{
|
|
2294
1464
|
"id": "frontend-nextjs-project-structure",
|
|
@@ -2303,19 +1473,11 @@
|
|
|
2303
1473
|
"stacks": [
|
|
2304
1474
|
"nextjs"
|
|
2305
1475
|
],
|
|
2306
|
-
"aliases": [
|
|
2307
|
-
"project structure",
|
|
2308
|
-
"folder structure",
|
|
2309
|
-
"src directory",
|
|
2310
|
-
"app directory organization"
|
|
2311
|
-
],
|
|
2312
1476
|
"anchors": {
|
|
2313
1477
|
"structure": "#canonical-folder-tree",
|
|
2314
1478
|
"organization": "#feature-index-pattern"
|
|
2315
1479
|
},
|
|
2316
|
-
"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."
|
|
2317
|
-
"verifiedAgainst": "Next.js 15 (App Router, `src/` layout).",
|
|
2318
|
-
"provedBy": []
|
|
1480
|
+
"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."
|
|
2319
1481
|
},
|
|
2320
1482
|
{
|
|
2321
1483
|
"id": "frontend-nextjs-state-management",
|
|
@@ -2330,20 +1492,11 @@
|
|
|
2330
1492
|
"stacks": [
|
|
2331
1493
|
"nextjs"
|
|
2332
1494
|
],
|
|
2333
|
-
"aliases": [
|
|
2334
|
-
"zustand",
|
|
2335
|
-
"React context",
|
|
2336
|
-
"global state",
|
|
2337
|
-
"state management",
|
|
2338
|
-
"client state"
|
|
2339
|
-
],
|
|
2340
1495
|
"anchors": {
|
|
2341
1496
|
"patterns": "#state-decision-tree",
|
|
2342
1497
|
"server": "#server-state-tanstack-query"
|
|
2343
1498
|
},
|
|
2344
|
-
"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."
|
|
2345
|
-
"verifiedAgainst": "Next.js 15 + TanStack Query 5 + Zustand 5.",
|
|
2346
|
-
"provedBy": []
|
|
1499
|
+
"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."
|
|
2347
1500
|
},
|
|
2348
1501
|
{
|
|
2349
1502
|
"id": "frontend-nextjs-testing",
|
|
@@ -2358,25 +1511,12 @@
|
|
|
2358
1511
|
"stacks": [
|
|
2359
1512
|
"nextjs"
|
|
2360
1513
|
],
|
|
2361
|
-
"aliases": [
|
|
2362
|
-
"vitest",
|
|
2363
|
-
"jest",
|
|
2364
|
-
"testing-library",
|
|
2365
|
-
"Playwright",
|
|
2366
|
-
"Next.js testing",
|
|
2367
|
-
"component tests",
|
|
2368
|
-
"test budget",
|
|
2369
|
-
"orçamento de teste",
|
|
2370
|
-
"it.each table"
|
|
2371
|
-
],
|
|
2372
1514
|
"anchors": {
|
|
2373
1515
|
"unit": "#component-test-pattern",
|
|
2374
1516
|
"e2e": "#test-file-co-location",
|
|
2375
1517
|
"test-budget": "#test-budget"
|
|
2376
1518
|
},
|
|
2377
|
-
"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."
|
|
2378
|
-
"verifiedAgainst": "Vitest 3 + React Testing Library 16 + MSW 2.",
|
|
2379
|
-
"provedBy": []
|
|
1519
|
+
"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."
|
|
2380
1520
|
},
|
|
2381
1521
|
{
|
|
2382
1522
|
"id": "frontend-scroll-driven-frame-scrub",
|
|
@@ -2392,15 +1532,6 @@
|
|
|
2392
1532
|
"stacks": [
|
|
2393
1533
|
"*"
|
|
2394
1534
|
],
|
|
2395
|
-
"aliases": [
|
|
2396
|
-
"frame scrub",
|
|
2397
|
-
"canvas scrub",
|
|
2398
|
-
"scroll-driven video",
|
|
2399
|
-
"scrollytelling video",
|
|
2400
|
-
"apple scroll video",
|
|
2401
|
-
"ffmpeg frames",
|
|
2402
|
-
"poster fallback"
|
|
2403
|
-
],
|
|
2404
1535
|
"anchors": {
|
|
2405
1536
|
"why": "#section-1-canvas-not-video-currenttime",
|
|
2406
1537
|
"extraction": "#section-2-frame-extraction",
|
|
@@ -2409,9 +1540,7 @@
|
|
|
2409
1540
|
"responsive": "#section-6-responsive-and-reduced-motion",
|
|
2410
1541
|
"verify": "#section-8-verify-with-motion-never-with-a-still"
|
|
2411
1542
|
},
|
|
2412
|
-
"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."
|
|
2413
|
-
"verifiedAgainst": "GSAP 3.13 ScrollTrigger + Lenis 1.x + ffmpeg 6.x, shipped to production.",
|
|
2414
|
-
"provedBy": []
|
|
1543
|
+
"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."
|
|
2415
1544
|
},
|
|
2416
1545
|
{
|
|
2417
1546
|
"id": "frontend-scroll-driven-scroll-components",
|
|
@@ -2427,16 +1556,6 @@
|
|
|
2427
1556
|
"stacks": [
|
|
2428
1557
|
"*"
|
|
2429
1558
|
],
|
|
2430
|
-
"aliases": [
|
|
2431
|
-
"pinned gallery",
|
|
2432
|
-
"horizontal scroll",
|
|
2433
|
-
"scroll reveal",
|
|
2434
|
-
"parallax drift",
|
|
2435
|
-
"count-up",
|
|
2436
|
-
"lightbox",
|
|
2437
|
-
"scroll progress",
|
|
2438
|
-
"expanding modal"
|
|
2439
|
-
],
|
|
2440
1559
|
"anchors": {
|
|
2441
1560
|
"reveal": "#section-1-usereveal-the-base-brick",
|
|
2442
1561
|
"pinned": "#section-2-pinned-horizontal-gallery",
|
|
@@ -2444,9 +1563,7 @@
|
|
|
2444
1563
|
"parallax": "#section-5-parallax-drift",
|
|
2445
1564
|
"rules": "#section-9-cross-cutting-rules"
|
|
2446
1565
|
},
|
|
2447
|
-
"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."
|
|
2448
|
-
"verifiedAgainst": "GSAP 3.13 (ScrollTrigger, matchMedia, context) + React 18 + Lenis 1.x, shipped to production.",
|
|
2449
|
-
"provedBy": []
|
|
1566
|
+
"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."
|
|
2450
1567
|
},
|
|
2451
1568
|
{
|
|
2452
1569
|
"id": "frontend-scroll-driven-smooth-scroll",
|
|
@@ -2462,24 +1579,13 @@
|
|
|
2462
1579
|
"stacks": [
|
|
2463
1580
|
"*"
|
|
2464
1581
|
],
|
|
2465
|
-
"aliases": [
|
|
2466
|
-
"lenis",
|
|
2467
|
-
"smooth scroll",
|
|
2468
|
-
"scroll wiring",
|
|
2469
|
-
"gsap ticker",
|
|
2470
|
-
"window.__lenis",
|
|
2471
|
-
"scrolltrigger refresh",
|
|
2472
|
-
"HMR stale triggers"
|
|
2473
|
-
],
|
|
2474
1582
|
"anchors": {
|
|
2475
1583
|
"registry": "#section-1-central-motion-registry",
|
|
2476
1584
|
"raf": "#section-2-the-raf-handoff",
|
|
2477
1585
|
"expose": "#section-3-expose-the-instance-on-window",
|
|
2478
1586
|
"hmr": "#section-4-the-hmr-staleness-trap"
|
|
2479
1587
|
},
|
|
2480
|
-
"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."
|
|
2481
|
-
"verifiedAgainst": "Lenis 1.x + GSAP 3.13 ScrollTrigger + Vite 5, shipped to production.",
|
|
2482
|
-
"provedBy": []
|
|
1588
|
+
"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."
|
|
2483
1589
|
},
|
|
2484
1590
|
{
|
|
2485
1591
|
"id": "frontend-seo-meta-schema-audit",
|
|
@@ -2495,18 +1601,6 @@
|
|
|
2495
1601
|
"stacks": [
|
|
2496
1602
|
"*"
|
|
2497
1603
|
],
|
|
2498
|
-
"aliases": [
|
|
2499
|
-
"seo",
|
|
2500
|
-
"meta tags",
|
|
2501
|
-
"open graph",
|
|
2502
|
-
"og:image",
|
|
2503
|
-
"schema.org",
|
|
2504
|
-
"json-ld",
|
|
2505
|
-
"rich results",
|
|
2506
|
-
"lighthouse",
|
|
2507
|
-
"go-live audit",
|
|
2508
|
-
"canonical"
|
|
2509
|
-
],
|
|
2510
1604
|
"anchors": {
|
|
2511
1605
|
"meta": "#section-1-essential-meta",
|
|
2512
1606
|
"opengraph": "#section-2-open-graph-and-twitter-cards",
|
|
@@ -2514,9 +1608,7 @@
|
|
|
2514
1608
|
"a11y": "#section-4-accessibility-also-an-seo-signal",
|
|
2515
1609
|
"audit": "#section-5-pre-launch-audit"
|
|
2516
1610
|
},
|
|
2517
|
-
"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."
|
|
2518
|
-
"verifiedAgainst": "Schema.org vocabulary + Lighthouse via Chrome DevTools MCP, shipped to production.",
|
|
2519
|
-
"provedBy": []
|
|
1611
|
+
"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."
|
|
2520
1612
|
},
|
|
2521
1613
|
{
|
|
2522
1614
|
"id": "infrastructure-docker-coolify-deploy",
|
|
@@ -2531,23 +1623,12 @@
|
|
|
2531
1623
|
"stacks": [
|
|
2532
1624
|
"docker"
|
|
2533
1625
|
],
|
|
2534
|
-
"aliases": [
|
|
2535
|
-
"coolify",
|
|
2536
|
-
"docker deploy",
|
|
2537
|
-
"self-hosted",
|
|
2538
|
-
"VPS deployment",
|
|
2539
|
-
"docker compose",
|
|
2540
|
-
"coolify api",
|
|
2541
|
-
"ssh deploy"
|
|
2542
|
-
],
|
|
2543
1626
|
"anchors": {
|
|
2544
1627
|
"setup": "#coolify-service-config",
|
|
2545
1628
|
"deployment": "#zero-downtime-deploys",
|
|
2546
1629
|
"ssh": "#triggering-a-deploy-via-ssh--api"
|
|
2547
1630
|
},
|
|
2548
|
-
"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."
|
|
2549
|
-
"verifiedAgainst": ".NET 10 + Next.js 15 + Node 22 (Docker base images) + Coolify 4.x API. The `POST /api/v1/deploy` contract was checked against the Coolify docs/OpenAPI on 2026-10-03, and Coolify 4.3.23 was observed answering `GET /deploy` with \"This endpoint has changed to a POST request\"; Dockerfiles and snippets are documentary.",
|
|
2550
|
-
"provedBy": []
|
|
1631
|
+
"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."
|
|
2551
1632
|
},
|
|
2552
1633
|
{
|
|
2553
1634
|
"id": "infrastructure-docker-local-compose-isolation",
|
|
@@ -2562,17 +1643,6 @@
|
|
|
2562
1643
|
"stacks": [
|
|
2563
1644
|
"docker"
|
|
2564
1645
|
],
|
|
2565
|
-
"aliases": [
|
|
2566
|
-
"compose isolation",
|
|
2567
|
-
"worktree ports",
|
|
2568
|
-
"container_name",
|
|
2569
|
-
"POSTGRES_PORT",
|
|
2570
|
-
"COMPOSE_PROJECT_NAME",
|
|
2571
|
-
"port block",
|
|
2572
|
-
"PG_PORT",
|
|
2573
|
-
"portEnvNames",
|
|
2574
|
-
"local docker compose"
|
|
2575
|
-
],
|
|
2576
1646
|
"anchors": {
|
|
2577
1647
|
"layout": "#port-layout-one-contract-everywhere",
|
|
2578
1648
|
"portNames": "#project-named-port-variables",
|
|
@@ -2580,9 +1650,7 @@
|
|
|
2580
1650
|
"enforcement": "#what-the-harness-enforces",
|
|
2581
1651
|
"skeleton": "#minimal-compose-skeleton"
|
|
2582
1652
|
},
|
|
2583
|
-
"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."
|
|
2584
|
-
"verifiedAgainst": "Docker Compose v2 + morph-spec 8.34 (dedicated stack per worktree).",
|
|
2585
|
-
"provedBy": []
|
|
1653
|
+
"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."
|
|
2586
1654
|
},
|
|
2587
1655
|
{
|
|
2588
1656
|
"id": "infrastructure-neon-pgrag",
|
|
@@ -2598,20 +1666,11 @@
|
|
|
2598
1666
|
"stacks": [
|
|
2599
1667
|
"neon"
|
|
2600
1668
|
],
|
|
2601
|
-
"aliases": [
|
|
2602
|
-
"pgrag",
|
|
2603
|
-
"RAG in SQL",
|
|
2604
|
-
"embedding pipeline",
|
|
2605
|
-
"Neon RAG",
|
|
2606
|
-
"in-database RAG"
|
|
2607
|
-
],
|
|
2608
1669
|
"anchors": {
|
|
2609
1670
|
"setup": "#install-pgrag",
|
|
2610
1671
|
"pipeline": "#full-rag-pipeline-example"
|
|
2611
1672
|
},
|
|
2612
|
-
"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`)."
|
|
2613
|
-
"verifiedAgainst": "Neon pgrag (`rag` extension family — experimental, see Core Rules).",
|
|
2614
|
-
"provedBy": []
|
|
1673
|
+
"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`)."
|
|
2615
1674
|
},
|
|
2616
1675
|
{
|
|
2617
1676
|
"id": "infrastructure-neon-pgvector",
|
|
@@ -2627,20 +1686,11 @@
|
|
|
2627
1686
|
"stacks": [
|
|
2628
1687
|
"neon"
|
|
2629
1688
|
],
|
|
2630
|
-
"aliases": [
|
|
2631
|
-
"pgvector",
|
|
2632
|
-
"vector embeddings",
|
|
2633
|
-
"similarity search",
|
|
2634
|
-
"Neon vectors",
|
|
2635
|
-
"HNSW index"
|
|
2636
|
-
],
|
|
2637
1689
|
"anchors": {
|
|
2638
1690
|
"setup": "#enable-pgvector",
|
|
2639
1691
|
"queries": "#similarity-search"
|
|
2640
1692
|
},
|
|
2641
|
-
"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
|
|
2642
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + Pgvector.EntityFrameworkCore 0.3.0 + Microsoft.Extensions.AI 10.9.0 (ai-pin 2026-09-08). The entity, configuration, DbContext, search-handler and raw-Npgsql blocks compile with `TreatWarningsAsErrors` (handler over the `Result`/`IHandler` of `result-pattern.md`/`vertical-slice.md`), and the handler's query shape was translated with `ToQueryString()` to `ORDER BY d.embedding <=> @query`; the SQL (index DDL, `ef_search`, the two SQL functions) is documentary, checked against the pgvector README.",
|
|
2643
|
-
"provedBy": []
|
|
1693
|
+
"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 plan.md (## Decisões) 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."
|
|
2644
1694
|
},
|
|
2645
1695
|
{
|
|
2646
1696
|
"id": "infrastructure-neon-setup",
|
|
@@ -2656,20 +1706,11 @@
|
|
|
2656
1706
|
"stacks": [
|
|
2657
1707
|
"neon"
|
|
2658
1708
|
],
|
|
2659
|
-
"aliases": [
|
|
2660
|
-
"Neon",
|
|
2661
|
-
"neonctl",
|
|
2662
|
-
"serverless postgres",
|
|
2663
|
-
"Neon CLI",
|
|
2664
|
-
"Neon setup"
|
|
2665
|
-
],
|
|
2666
1709
|
"anchors": {
|
|
2667
1710
|
"setup": "#neon-cli-setup",
|
|
2668
1711
|
"connection": "#connection-strings"
|
|
2669
1712
|
},
|
|
2670
|
-
"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`."
|
|
2671
|
-
"verifiedAgainst": ".NET 10 (SDK 10.0.400-preview) + Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 + neonctl. The DbContext registration, design-time factory and cold-start blocks compile with `TreatWarningsAsErrors`; that Npgsql 10 rejects `postgresql://` URIs and marks `Trust Server Certificate` as \"no longer needed and does nothing\" was measured on 10.0.3; the suspend-timeout API and `--cu` ranges were checked against the Neon docs (context7). neonctl commands are documentary.",
|
|
2672
|
-
"provedBy": []
|
|
1713
|
+
"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`."
|
|
2673
1714
|
},
|
|
2674
1715
|
{
|
|
2675
1716
|
"id": "integration-mcp-mcp-tools",
|
|
@@ -2684,20 +1725,11 @@
|
|
|
2684
1725
|
"stacks": [
|
|
2685
1726
|
"*"
|
|
2686
1727
|
],
|
|
2687
|
-
"aliases": [
|
|
2688
|
-
"MCP",
|
|
2689
|
-
"Model Context Protocol",
|
|
2690
|
-
"Claude tools",
|
|
2691
|
-
"MCP server",
|
|
2692
|
-
"MCP client"
|
|
2693
|
-
],
|
|
2694
1728
|
"anchors": {
|
|
2695
1729
|
"tools": "#mcp-providers-by-phase",
|
|
2696
1730
|
"setup": "#mcp-availability-detection"
|
|
2697
1731
|
},
|
|
2698
|
-
"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."
|
|
2699
|
-
"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`.",
|
|
2700
|
-
"provedBy": []
|
|
1732
|
+
"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."
|
|
2701
1733
|
},
|
|
2702
1734
|
{
|
|
2703
1735
|
"id": "frontend-admin-app-data-table",
|
|
@@ -2712,27 +1744,13 @@
|
|
|
2712
1744
|
"stacks": [
|
|
2713
1745
|
"nextjs"
|
|
2714
1746
|
],
|
|
2715
|
-
"aliases": [
|
|
2716
|
-
"data table",
|
|
2717
|
-
"admin list",
|
|
2718
|
-
"server-side pagination",
|
|
2719
|
-
"paginação",
|
|
2720
|
-
"tabela de dados",
|
|
2721
|
-
"ordenação no servidor",
|
|
2722
|
-
"empty state",
|
|
2723
|
-
"row selection",
|
|
2724
|
-
"bulk actions",
|
|
2725
|
-
"manualPagination"
|
|
2726
|
-
],
|
|
2727
1747
|
"anchors": {
|
|
2728
1748
|
"contract": "#the-list-contract-url-and-api",
|
|
2729
1749
|
"table": "#the-shared-datatable-tier-2",
|
|
2730
1750
|
"states": "#the-three-states",
|
|
2731
1751
|
"mobile": "#mobile"
|
|
2732
1752
|
},
|
|
2733
|
-
"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."
|
|
2734
|
-
"verifiedAgainst": "Next.js 16 App Router + @tanstack/react-table 9 + Zod 4 + shadcn 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2735
|
-
"provedBy": []
|
|
1753
|
+
"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."
|
|
2736
1754
|
},
|
|
2737
1755
|
{
|
|
2738
1756
|
"id": "frontend-admin-app-feedback",
|
|
@@ -2747,24 +1765,12 @@
|
|
|
2747
1765
|
"stacks": [
|
|
2748
1766
|
"nextjs"
|
|
2749
1767
|
],
|
|
2750
|
-
"aliases": [
|
|
2751
|
-
"confirmation dialog",
|
|
2752
|
-
"confirm dialog",
|
|
2753
|
-
"delete confirmation",
|
|
2754
|
-
"toast",
|
|
2755
|
-
"sonner",
|
|
2756
|
-
"toast messages",
|
|
2757
|
-
"undo toast",
|
|
2758
|
-
"diálogo de confirmação"
|
|
2759
|
-
],
|
|
2760
1768
|
"anchors": {
|
|
2761
1769
|
"when": "#when-to-confirm",
|
|
2762
1770
|
"dialog": "#the-confirmation-dialog-tier-2",
|
|
2763
1771
|
"catalog": "#the-toast-catalog"
|
|
2764
1772
|
},
|
|
2765
|
-
"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."
|
|
2766
|
-
"verifiedAgainst": "shadcn 4 `AlertDialog` (Radix) + sonner 2 + TanStack Query 5 (the code blocks typecheck under `tsc --strict`).",
|
|
2767
|
-
"provedBy": []
|
|
1773
|
+
"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."
|
|
2768
1774
|
},
|
|
2769
1775
|
{
|
|
2770
1776
|
"id": "frontend-admin-app-forms",
|
|
@@ -2779,22 +1785,12 @@
|
|
|
2779
1785
|
"stacks": [
|
|
2780
1786
|
"nextjs"
|
|
2781
1787
|
],
|
|
2782
|
-
"aliases": [
|
|
2783
|
-
"admin form",
|
|
2784
|
-
"crud form",
|
|
2785
|
-
"server validation errors",
|
|
2786
|
-
"ValidationProblemDetails",
|
|
2787
|
-
"unsaved changes",
|
|
2788
|
-
"formulário"
|
|
2789
|
-
],
|
|
2790
1788
|
"anchors": {
|
|
2791
1789
|
"server-errors": "#server-errors-on-the-field",
|
|
2792
1790
|
"placement": "#where-the-form-lives",
|
|
2793
1791
|
"dirty": "#unsaved-changes"
|
|
2794
1792
|
},
|
|
2795
|
-
"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."
|
|
2796
|
-
"verifiedAgainst": "react-hook-form 7 + @hookform/resolvers 5 + Zod 4 + TanStack Query 5 + shadcn 4 `Field` (the code blocks typecheck under `tsc --strict`).",
|
|
2797
|
-
"provedBy": []
|
|
1793
|
+
"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."
|
|
2798
1794
|
},
|
|
2799
1795
|
{
|
|
2800
1796
|
"id": "frontend-admin-app-page-structure",
|
|
@@ -2809,24 +1805,12 @@
|
|
|
2809
1805
|
"stacks": [
|
|
2810
1806
|
"nextjs"
|
|
2811
1807
|
],
|
|
2812
|
-
"aliases": [
|
|
2813
|
-
"tabs",
|
|
2814
|
-
"abas",
|
|
2815
|
-
"long page",
|
|
2816
|
-
"página longa",
|
|
2817
|
-
"wizard",
|
|
2818
|
-
"multi-step form",
|
|
2819
|
-
"stepper",
|
|
2820
|
-
"detail page"
|
|
2821
|
-
],
|
|
2822
1808
|
"anchors": {
|
|
2823
1809
|
"decision": "#tabs-long-page-or-wizard",
|
|
2824
1810
|
"tabs": "#tabs-in-the-url",
|
|
2825
1811
|
"wizard": "#wizard-for-long-flows"
|
|
2826
1812
|
},
|
|
2827
|
-
"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."
|
|
2828
|
-
"verifiedAgainst": "Next.js 16 App Router + shadcn 4 `Tabs` (Radix) + react-hook-form 7 + Zod 4 (the code blocks typecheck under `tsc --strict`).",
|
|
2829
|
-
"provedBy": []
|
|
1813
|
+
"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."
|
|
2830
1814
|
},
|
|
2831
1815
|
{
|
|
2832
1816
|
"id": "architecture-code-comments",
|
|
@@ -2841,22 +1825,12 @@
|
|
|
2841
1825
|
"stacks": [
|
|
2842
1826
|
"*"
|
|
2843
1827
|
],
|
|
2844
|
-
"aliases": [
|
|
2845
|
-
"code comments",
|
|
2846
|
-
"comentário de código",
|
|
2847
|
-
"comment policy",
|
|
2848
|
-
"política de comentário",
|
|
2849
|
-
"comentário de processo",
|
|
2850
|
-
"self-documenting code"
|
|
2851
|
-
],
|
|
2852
1828
|
"anchors": {
|
|
2853
1829
|
"comment-policy": "#o-que-merece-comentário",
|
|
2854
1830
|
"not-worth": "#o-que-não-merece",
|
|
2855
1831
|
"size": "#tamanho"
|
|
2856
1832
|
},
|
|
2857
|
-
"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
|
|
2858
|
-
"verifiedAgainst": "C#, TypeScript e JavaScript (convenção de linguagem, sem pacote externo).",
|
|
2859
|
-
"provedBy": []
|
|
1833
|
+
"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 plan.md (## Decisões); 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."
|
|
2860
1834
|
},
|
|
2861
1835
|
{
|
|
2862
1836
|
"id": "ai-agents-tool-adapters",
|
|
@@ -2873,18 +1847,6 @@
|
|
|
2873
1847
|
"stacks": [
|
|
2874
1848
|
"dotnet"
|
|
2875
1849
|
],
|
|
2876
|
-
"aliases": [
|
|
2877
|
-
"tool fina",
|
|
2878
|
-
"adaptador de tool",
|
|
2879
|
-
"Ops",
|
|
2880
|
-
"Desfecho",
|
|
2881
|
-
"Render",
|
|
2882
|
-
"regra de negocio em tool",
|
|
2883
|
-
"descricao da tool",
|
|
2884
|
-
"golden de tool",
|
|
2885
|
-
"refactor neutro",
|
|
2886
|
-
"migracao de teste"
|
|
2887
|
-
],
|
|
2888
1850
|
"anchors": {
|
|
2889
1851
|
"fronteira": "#fronteira-com-os-outros-standards",
|
|
2890
1852
|
"problema": "#o-problema",
|
|
@@ -2895,100 +1857,28 @@
|
|
|
2895
1857
|
"reais": "#exemplos-reais",
|
|
2896
1858
|
"regras": "#regras"
|
|
2897
1859
|
},
|
|
2898
|
-
"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."
|
|
2899
|
-
|
|
2900
|
-
|
|
1860
|
+
"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."
|
|
1861
|
+
},
|
|
1862
|
+
{
|
|
1863
|
+
"id": "frontend-design-system-art-direction",
|
|
1864
|
+
"name": "art direction",
|
|
1865
|
+
"path": "frontend/design-system/art-direction.md",
|
|
1866
|
+
"category": "frontend",
|
|
1867
|
+
"subcategory": "design-system",
|
|
1868
|
+
"tags": [
|
|
1869
|
+
"frontend",
|
|
1870
|
+
"design-system"
|
|
1871
|
+
],
|
|
1872
|
+
"anchors": {
|
|
1873
|
+
"direction": "#section-1-write-the-direction-first",
|
|
1874
|
+
"explore": "#section-2-explore-two-or-three-directions",
|
|
1875
|
+
"examples": "#section-3-good-and-bad-directions",
|
|
1876
|
+
"tells": "#section-4-template-tells-to-avoid",
|
|
1877
|
+
"mobile": "#section-5-mobile-is-designed-not-derived",
|
|
1878
|
+
"motion": "#section-6-motion-with-intent",
|
|
1879
|
+
"prototype": "#section-7-prototype-and-self-review"
|
|
1880
|
+
},
|
|
1881
|
+
"digest": "Order beats talent: before any component of a new or changed screen, write 1-design/direcao.md with the concept in one sentence, the visual metaphor, what makes it memorable, and what it must NOT look like. New screens and brand moments (login, onboarding, first screen, empty state, success) explore 2-3 DISTINCT directions and recommend one with a product reason. Avoid template tells (raw shadcn look, white card centered on gray, generic icon list, default fonts, flat backgrounds, mobile as stacked desktop, decorative fade-ins, made-up numbers); three or more means raise it. Mobile is composed at 390px on its own (bottom sheet, thumb zone, 44px targets). Motion tells something with the lightest tool (CSS keyframes > scroll-driven CSS > Framer Motion > GSAP), transform/opacity only, and a composed reduced-motion version. Prove it in 1-design/prototipos/{screen}.html with the project REAL tokens, screenshotted at 1280 and 390 and revised until it passes."
|
|
2901
1882
|
}
|
|
2902
|
-
]
|
|
2903
|
-
"sharedAliases": {
|
|
2904
|
-
"agent middleware": [
|
|
2905
|
-
"ai-agents-production",
|
|
2906
|
-
"ai-agents-middleware-patterns"
|
|
2907
|
-
],
|
|
2908
|
-
"anti-hallucination": [
|
|
2909
|
-
"ai-agents-production",
|
|
2910
|
-
"ai-agents-middleware-patterns"
|
|
2911
|
-
],
|
|
2912
|
-
"agent observability": [
|
|
2913
|
-
"ai-agents-production",
|
|
2914
|
-
"ai-agents-observability-patterns"
|
|
2915
|
-
],
|
|
2916
|
-
"agent telemetry": [
|
|
2917
|
-
"ai-agents-production",
|
|
2918
|
-
"ai-agents-observability-patterns"
|
|
2919
|
-
],
|
|
2920
|
-
"when not to use workflow": [
|
|
2921
|
-
"ai-agents-workflows",
|
|
2922
|
-
"ai-agents-sweet-spot"
|
|
2923
|
-
],
|
|
2924
|
-
"rag": [
|
|
2925
|
-
"ai-agents-rag-custom-pgvector",
|
|
2926
|
-
"backend-database-vector-search-rag"
|
|
2927
|
-
],
|
|
2928
|
-
"vector search": [
|
|
2929
|
-
"ai-agents-rag-custom-pgvector",
|
|
2930
|
-
"backend-database-vector-search-rag"
|
|
2931
|
-
],
|
|
2932
|
-
"pgvector": [
|
|
2933
|
-
"ai-agents-rag-custom-pgvector",
|
|
2934
|
-
"backend-database-vector-search-rag",
|
|
2935
|
-
"infrastructure-neon-pgvector"
|
|
2936
|
-
],
|
|
2937
|
-
"embeddings": [
|
|
2938
|
-
"ai-agents-rag-custom-pgvector",
|
|
2939
|
-
"backend-database-vector-search-rag"
|
|
2940
|
-
],
|
|
2941
|
-
"semantic search": [
|
|
2942
|
-
"ai-agents-rag-custom-pgvector",
|
|
2943
|
-
"backend-database-vector-search-rag"
|
|
2944
|
-
],
|
|
2945
|
-
"middleware": [
|
|
2946
|
-
"ai-agents-middleware-patterns",
|
|
2947
|
-
"frontend-nextjs-nextjs-patterns"
|
|
2948
|
-
],
|
|
2949
|
-
"image generation": [
|
|
2950
|
-
"ai-agents-modalities-image-gen",
|
|
2951
|
-
"frontend-design-system-ai-image-generation"
|
|
2952
|
-
],
|
|
2953
|
-
"mcp": [
|
|
2954
|
-
"ai-agents-mcp-tools",
|
|
2955
|
-
"integration-mcp-mcp-tools"
|
|
2956
|
-
],
|
|
2957
|
-
"model context protocol": [
|
|
2958
|
-
"ai-agents-mcp-tools",
|
|
2959
|
-
"integration-mcp-mcp-tools"
|
|
2960
|
-
],
|
|
2961
|
-
"mcp client": [
|
|
2962
|
-
"ai-agents-mcp-tools",
|
|
2963
|
-
"integration-mcp-mcp-tools"
|
|
2964
|
-
],
|
|
2965
|
-
"mcp server": [
|
|
2966
|
-
"ai-agents-mcp-server",
|
|
2967
|
-
"integration-mcp-mcp-tools"
|
|
2968
|
-
],
|
|
2969
|
-
"feature slice": [
|
|
2970
|
-
"architecture-vertical-slice-vertical-slice",
|
|
2971
|
-
"backend-dotnet-vsa-handler-patterns"
|
|
2972
|
-
],
|
|
2973
|
-
"component naming": [
|
|
2974
|
-
"frontend-design-system-naming",
|
|
2975
|
-
"frontend-nextjs-naming-conventions"
|
|
2976
|
-
],
|
|
2977
|
-
"video generation": [
|
|
2978
|
-
"ai-agents-media-video",
|
|
2979
|
-
"frontend-design-system-ai-video-generation"
|
|
2980
|
-
],
|
|
2981
|
-
"seedance": [
|
|
2982
|
-
"ai-agents-media-video",
|
|
2983
|
-
"frontend-design-system-ai-video-generation"
|
|
2984
|
-
],
|
|
2985
|
-
"test budget": [
|
|
2986
|
-
"backend-dotnet-testing",
|
|
2987
|
-
"frontend-nextjs-testing"
|
|
2988
|
-
],
|
|
2989
|
-
"orçamento de teste": [
|
|
2990
|
-
"backend-dotnet-testing",
|
|
2991
|
-
"frontend-nextjs-testing"
|
|
2992
|
-
]
|
|
2993
|
-
}
|
|
1883
|
+
]
|
|
2994
1884
|
}
|