@polymorphism-tech/morph-spec 8.37.1 → 8.40.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 +37 -13
- package/README.md +2 -2
- package/bin/morph-spec.js +75 -49
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +6 -5
- package/framework/MORPH.md +25 -22
- package/framework/agents.json +96 -36
- package/framework/docs/CLI.md +54 -17
- package/framework/evals/rubrics/code-quality.md +5 -0
- package/framework/evals/rubrics/condensed.md +6 -2
- package/framework/evals/rubrics/contracts.md +20 -0
- package/framework/evals/rubrics/test-coverage.md +17 -6
- package/framework/hooks/README.md +9 -3
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +52 -0
- package/framework/hooks/claude-code/core/telemetry-log.js +172 -87
- package/framework/hooks/claude-code/core/validate-completion.js +16 -90
- package/framework/hooks/claude-code/core/worktree-context.js +10 -0
- package/framework/hooks/shared/command-tokens.js +96 -0
- package/framework/hooks/shared/git-exclude.js +44 -0
- package/framework/hooks/shared/git-paths.js +52 -0
- package/framework/hooks/shared/graph-freshness.js +380 -0
- package/framework/hooks/shared/graph-refresh.js +416 -0
- package/framework/hooks/shared/morph-cli.js +17 -5
- package/framework/hooks/shared/redact-secrets.js +55 -0
- package/framework/hooks/shared/telemetry-logger.js +25 -2
- package/framework/hooks/shared/transcript-usage.js +80 -0
- package/framework/mcp/registry.json +33 -2
- package/framework/rubrics/backend.json +2 -2
- package/framework/rubrics/default.json +2 -2
- package/framework/rubrics/frontend.json +2 -2
- package/framework/rules/csharp-standards.md +1 -0
- package/framework/rules/infrastructure-standards.md +0 -1
- package/framework/rules/morph-workflow.md +10 -4
- package/framework/rules/nextjs-standards.md +1 -0
- package/framework/schemas/config.schema.json +41 -1
- package/framework/schemas/feature.schema.json +5 -1
- 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 +40 -11
- package/framework/skills/core/morph-brainstorming/SKILL.md +16 -7
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +1 -1
- package/framework/skills/core/morph-implement/SKILL.md +44 -10
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +73 -15
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +51 -13
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +39 -13
- package/framework/standards/STANDARDS.json +189 -14
- package/framework/standards/ai-agents/context-providers.md +16 -26
- package/framework/standards/ai-agents/durable-workflows-hitl.md +15 -21
- package/framework/standards/ai-agents/modalities-image-gen.md +21 -25
- package/framework/standards/ai-agents/modalities-speech-to-text.md +4 -15
- package/framework/standards/ai-agents/modalities-text-to-speech.md +2 -11
- package/framework/standards/ai-agents/modalities-vision.md +1 -18
- package/framework/standards/ai-agents/prompt-sources.md +0 -4
- package/framework/standards/ai-agents/testing-ai.md +2 -1
- package/framework/standards/architecture/code-comments.md +100 -0
- package/framework/standards/backend/dotnet/testing.md +22 -0
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +63 -50
- package/framework/standards/frontend/admin-app/data-table.md +562 -0
- package/framework/standards/frontend/admin-app/feedback.md +236 -0
- package/framework/standards/frontend/admin-app/forms.md +232 -0
- package/framework/standards/frontend/admin-app/page-structure.md +203 -0
- package/framework/standards/frontend/design-system/ai-image-generation.md +5 -5
- package/framework/standards/frontend/nextjs/components.md +37 -58
- package/framework/standards/frontend/nextjs/data-fetching.md +1 -0
- package/framework/standards/frontend/nextjs/forms.md +31 -30
- package/framework/standards/frontend/nextjs/state-management.md +2 -1
- package/framework/standards/frontend/nextjs/testing.md +18 -0
- package/framework/standards/integration/mcp/mcp-tools.md +48 -117
- package/framework/templates/code/dotnet/abstractions/result.cs +0 -2
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +26 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +28 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +99 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +2 -44
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +40 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +54 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +53 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +192 -281
- package/framework/templates/code/dotnet/database/migration.cs +49 -83
- package/framework/templates/code/dotnet/database/vector-entity.cs +2 -46
- package/framework/templates/code/dotnet/extensions/result-extensions.cs +0 -2
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +11 -15
- package/framework/templates/code/dotnet/jobs/agent.cs +3 -51
- package/framework/templates/code/dotnet/jobs/job.cs +2 -97
- package/framework/templates/code/dotnet/test.cs +2 -87
- package/framework/templates/code/typescript/contracts.ts +140 -167
- package/framework/templates/context/design-system.md +3 -3
- package/framework/templates/docs/proposal.md +11 -0
- package/framework/templates/docs/spec.md +15 -0
- package/framework/templates/docs/ui-mockups.md +115 -98
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +1 -48
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +3 -79
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +1 -70
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +1 -47
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +2 -99
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +0 -6
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +3 -88
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +1 -41
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +0 -21
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +2 -63
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +0 -30
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +1 -52
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +3 -78
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +4 -109
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +0 -22
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +1 -36
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +2 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +0 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +2 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +4 -111
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +0 -34
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +1 -57
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +0 -19
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +2 -74
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +2 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +1 -43
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +1 -96
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +0 -13
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +0 -27
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +0 -33
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +0 -31
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +3 -75
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +0 -42
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +0 -39
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +1 -55
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +3 -59
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +0 -46
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +3 -28
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +7 -61
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +2 -37
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +1 -23
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +2 -82
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +1 -68
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +1 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +2 -62
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +1 -25
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +0 -124
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +1 -73
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +1 -66
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +0 -41
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +2 -112
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +2 -43
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +5 -110
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +7 -103
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +1 -42
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +1 -30
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +0 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +1 -29
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +2 -38
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +1 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +4 -69
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +1 -10
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +1 -57
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +0 -26
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +1 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +0 -14
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +0 -32
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +0 -37
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +0 -78
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +0 -53
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +0 -44
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +0 -19
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +0 -24
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +6 -48
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +1 -13
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +2 -71
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +5 -100
- package/framework/templates/feature/decisions.md +124 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +39 -43
- package/framework/templates/frontend/nextjs/env.mjs.hbs +29 -32
- package/framework/templates/frontend/nextjs/page.tsx.hbs +21 -22
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +42 -54
- package/framework/templates/integrations/asaas-client.cs +332 -387
- package/framework/templates/integrations/asaas-webhook.cs +290 -351
- package/framework/templates/integrations/neon-auth-config.cs +9 -105
- package/framework/templates/project/validate.js +97 -120
- package/package.json +2 -2
- package/src/commands/dashboard/dashboard.js +80 -53
- package/src/commands/graph/affected.js +4 -3
- package/src/commands/graph/build.js +109 -18
- package/src/commands/graph/explain.js +8 -5
- package/src/commands/graph/status.js +14 -8
- package/src/commands/mcp/mcp-setup.js +37 -9
- package/src/commands/observability/cost.js +148 -18
- package/src/commands/project/doctor.js +85 -15
- package/src/commands/project/init.js +6 -3
- package/src/commands/project/install-plugin-cmd.js +1 -1
- package/src/commands/project/update.js +30 -0
- package/src/commands/project/worktree.js +73 -90
- package/src/commands/state/advance.js +18 -0
- package/src/commands/state/approve.js +15 -0
- package/src/commands/state/archive.js +62 -9
- package/src/commands/state/finish.js +62 -14
- package/src/commands/state/gate-check.js +8 -1
- package/src/commands/state/gate-decision.js +8 -4
- package/src/commands/state/report-check.js +90 -9
- package/src/commands/state/usecase.js +66 -0
- package/src/commands/tasks/dag.js +17 -2
- package/src/commands/validation/validate-feature.js +6 -1
- package/src/commands/validation/verify.js +20 -1
- package/src/dashboard/app/App.jsx +80 -16
- package/src/dashboard/app/components/ActivityFeed.jsx +172 -0
- package/src/dashboard/app/components/CommandPalette.jsx +155 -0
- package/src/dashboard/app/components/DetailPanel.jsx +47 -11
- package/src/dashboard/app/components/EntityLink.jsx +55 -0
- package/src/dashboard/app/components/ErrorBoundary.jsx +54 -0
- package/src/dashboard/app/components/FlowGraph.jsx +19 -17
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +53 -0
- package/src/dashboard/app/components/Markdown.jsx +47 -0
- package/src/dashboard/app/components/PhaseRail.jsx +111 -0
- package/src/dashboard/app/components/SearchFilter.jsx +40 -22
- package/src/dashboard/app/components/Sidebar.jsx +146 -37
- package/src/dashboard/app/hooks/useMorphQuery.js +54 -0
- package/src/dashboard/app/hooks/usePref.js +38 -0
- package/src/dashboard/app/lib/format.js +69 -0
- package/src/dashboard/app/lib/phases.js +56 -0
- package/src/dashboard/app/lib/project.js +50 -0
- package/src/dashboard/app/pages/Activity.jsx +24 -0
- package/src/dashboard/app/pages/AgentMap.jsx +371 -370
- package/src/dashboard/app/pages/FeatureDetail.jsx +604 -0
- package/src/dashboard/app/pages/Features.jsx +400 -0
- package/src/dashboard/app/pages/Guide.jsx +146 -0
- package/src/dashboard/app/pages/HookInspector.jsx +357 -322
- package/src/dashboard/app/pages/PhasePipeline.jsx +277 -408
- package/src/dashboard/app/pages/Portfolio.jsx +156 -0
- package/src/dashboard/app/pages/Rubrics.jsx +101 -0
- package/src/dashboard/app/pages/RulesMap.jsx +300 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +483 -366
- package/src/dashboard/app/pages/Standards.jsx +407 -332
- package/src/dashboard/app/styles/index.css +187 -47
- package/src/dashboard/index.html +14 -12
- package/src/dashboard/package.json +32 -24
- package/src/dashboard/scanner/activity-scanner.js +351 -0
- package/src/dashboard/scanner/agents-scanner.js +63 -1
- package/src/dashboard/scanner/docs-scanner.js +59 -0
- package/src/dashboard/scanner/feature-status.js +10 -0
- package/src/dashboard/scanner/features-scanner.js +360 -0
- package/src/dashboard/scanner/hooks-scanner.js +8 -1
- package/src/dashboard/scanner/index.js +14 -7
- package/src/dashboard/scanner/phases-scanner.js +14 -15
- package/src/dashboard/scanner/portfolio.js +153 -0
- package/src/dashboard/scanner/project-api.js +195 -0
- package/src/dashboard/scanner/rules-scanner.js +42 -37
- package/src/dashboard/scanner/skills-scanner.js +105 -43
- package/src/dashboard/scanner/standards-scanner.js +4 -1
- package/src/dashboard/vite-plugin-morph-data.js +134 -17
- package/src/lib/ai/comment-stripper.js +101 -4
- package/src/lib/detectors/claude-config-detector.js +2 -7
- package/src/lib/detectors/index.js +5 -1
- package/src/lib/evaluator-report.js +47 -0
- package/src/lib/generators/handoff-generator.js +4 -18
- package/src/lib/generators/settings-generator.js +208 -209
- package/src/lib/git/default-branch.js +52 -0
- package/src/lib/git/tree-fingerprint.js +58 -0
- package/src/lib/git/tree-state.js +167 -0
- package/src/lib/graph/god-nodes.js +12 -3
- package/src/lib/graph/graph-health.js +48 -2
- package/src/lib/graph/graph-loader.js +99 -25
- package/src/lib/graph/graphifyignore.js +145 -28
- package/src/lib/graph/sanitize.js +117 -11
- package/src/lib/installers/mcp-installer.js +724 -719
- package/src/lib/installers/memory-installer.js +1 -2
- package/src/lib/projects/registry.js +154 -0
- package/src/lib/risk-detector.js +90 -0
- package/src/lib/specs/acervo-format.js +70 -0
- package/src/lib/specs/acervo-lifecycle.js +264 -0
- package/src/lib/specs/adr-index.js +169 -0
- package/src/lib/specs/revocations.js +156 -0
- package/src/lib/specs/screen-checklist.js +357 -0
- package/src/lib/specs/usecase-check.js +20 -16
- package/src/lib/specs/usecase-promoter.js +103 -38
- package/src/lib/specs/usecase-registry.js +7 -7
- package/src/lib/tasks/tasks-json-parser.js +2 -3
- package/src/lib/telemetry/metrics.js +386 -0
- package/src/lib/telemetry/telemetry-reader.js +161 -51
- package/src/lib/telemetry/verify-event.js +79 -0
- package/src/lib/validators/code-budget/code-budget-validator.js +200 -0
- package/src/lib/validators/code-budget/comment-scanner.js +75 -0
- package/src/lib/validators/code-budget/index.js +9 -0
- package/src/lib/validators/reuse/reuse-validator.js +4 -2
- package/src/lib/validators/reuse/symbol-scanner.js +27 -22
- package/src/lib/validators/validation-runner.js +21 -5
- package/src/lib/validators/verify-runner.js +11 -0
- package/src/lib/worktree/finish-backup.js +739 -0
- package/src/lib/worktree/ignored-triage.js +15 -8
- package/src/lib/worktree/worktree-audit.js +5 -0
- package/src/scripts/install-plugin.js +1 -1
- package/src/scripts/setup-infra.js +31 -22
- package/src/utils/agents-installer.js +338 -24
- package/src/utils/banner.js +1 -1
- package/src/utils/hooks-installer.js +75 -15
- package/src/commands/index.js +0 -13
- package/src/commands/project/checkpoint.js +0 -209
- package/src/core/state/phase-state-machine.js +0 -197
- package/src/dashboard/app/hooks/useDetailPanel.js +0 -15
- package/src/dashboard/app/pages/LiveState.jsx +0 -206
- package/src/dashboard/scanner/squads-scanner.js +0 -13
- package/src/dashboard/scanner/state-scanner.js +0 -85
- package/src/lib/generators/index.js +0 -10
- package/src/lib/hooks/hook-executor.js +0 -167
- package/src/lib/validators/checkpoint-persistence.js +0 -39
- package/src/lib/validators/shared/emit-validator-dispatch.js +0 -14
- package/src/lib/validators/shared/index.js +0 -12
- package/src/lib/validators/shared/issue-counter.js +0 -14
- package/src/lib/validators/shared/result-formatter.js +0 -124
package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs
CHANGED
|
@@ -4,16 +4,6 @@ using Xunit;
|
|
|
4
4
|
|
|
5
5
|
namespace Morph.AiKit.Tests.Evals;
|
|
6
6
|
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// Miss de cache sem credencial REPROVA, nomeado. Jamais <c>skip</c>.
|
|
9
|
-
/// </summary>
|
|
10
|
-
/// <remarks>
|
|
11
|
-
/// A tentacao do <c>skip</c> e real ("nao da para avaliar, entao nao avalio") e recria
|
|
12
|
-
/// exatamente o buraco que este harness fechou duas vezes: gate verde sobre verificacao que
|
|
13
|
-
/// nao aconteceu. E ha razao positiva: em CI, com cache commitada, um miss significa UMA
|
|
14
|
-
/// coisa so — o prompt ou o modelo mudou e ninguem regravou. O miss e o veredito, nao o
|
|
15
|
-
/// obstaculo.
|
|
16
|
-
/// </remarks>
|
|
17
7
|
public sealed class EvalCacheMissTests
|
|
18
8
|
{
|
|
19
9
|
private static async Task<EvalWorkspace> SeededAsync(params string[] scenarioNames)
|
|
@@ -41,21 +31,16 @@ public sealed class EvalCacheMissTests
|
|
|
41
31
|
{
|
|
42
32
|
using var ws = await SeededAsync("a-primeiro");
|
|
43
33
|
|
|
44
|
-
//
|
|
45
|
-
// ninguem regravou". O prefixo "b-" e deliberado: os cenarios rodam em ordem ordinal
|
|
46
|
-
// de caminho, e o teste abaixo depende de o cenario BOM ter rodado antes do miss.
|
|
34
|
+
// Prefixo "b-": os cenarios rodam em ordem ordinal de caminho, e o miss tem de vir depois do cenario bom.
|
|
47
35
|
ws.WriteScenario("triagem", "acme", "b-novo", "pergunta inedita", """[ { "kind": "contains", "value": "resposta" } ]""");
|
|
48
36
|
|
|
49
37
|
await using var offline = EvalFixture.Create(ws.Options(EvalMode.Offline));
|
|
50
38
|
var error = await Assert.ThrowsAsync<EvalCacheMissException>(
|
|
51
39
|
() => offline.RunAllAsync(TestContext.Current.CancellationToken));
|
|
52
40
|
|
|
53
|
-
// O cenario que faltou, e nao "algum cenario".
|
|
54
41
|
Assert.Equal("triagem.acme.b-novo", error.ScenarioName);
|
|
55
42
|
Assert.Contains("eval-cache-miss", error.Message, StringComparison.Ordinal);
|
|
56
43
|
|
|
57
|
-
// P3: a mensagem carrega o comando de regravacao e o arquivo de credencial esperado.
|
|
58
|
-
// Um "cache miss" seco mandaria o leitor procurar os dois.
|
|
59
44
|
Assert.Contains(EvalCacheMissException.RefreshCommand, error.Message, StringComparison.Ordinal);
|
|
60
45
|
Assert.Contains(".env.evals", error.Message, StringComparison.Ordinal);
|
|
61
46
|
Assert.Contains(ws.CacheRoot, error.Message, StringComparison.Ordinal);
|
|
@@ -67,33 +52,22 @@ public sealed class EvalCacheMissTests
|
|
|
67
52
|
using var ws = await SeededAsync("a-primeiro");
|
|
68
53
|
ws.WriteScenario("triagem", "acme", "b-novo", "pergunta inedita", """[ { "kind": "contains", "value": "resposta" } ]""");
|
|
69
54
|
|
|
70
|
-
// O sumario da SEMEADURA, byte a byte. Ele e a linha de base: o que este teste cobra
|
|
71
|
-
// e que o run que morreu por miss nao escreva NADA por cima dele.
|
|
72
55
|
var summaryBeforeTheMiss = File.ReadAllBytes(ws.SummaryPath);
|
|
73
56
|
|
|
74
57
|
await using var offline = EvalFixture.Create(ws.Options(EvalMode.Offline));
|
|
75
58
|
await Assert.ThrowsAsync<EvalCacheMissException>(
|
|
76
59
|
() => offline.RunAllAsync(TestContext.Current.CancellationToken));
|
|
77
60
|
|
|
78
|
-
// Nao ha caminho pelo qual o cenario ausente apareca como avaliado: a contagem so tem
|
|
79
|
-
// o que de fato resolveu, e o sumario NAO foi reescrito. Um sumario dizendo
|
|
80
|
-
// "1 cenario, 1 passou, cache warm" aqui seria exatamente a mentira que a excecao
|
|
81
|
-
// existe para impedir — e o CLI, que le o ARQUIVO, acreditaria nela.
|
|
82
61
|
Assert.Single(offline.Results);
|
|
83
62
|
Assert.Equal("triagem.acme.a-primeiro", offline.Results[0].Scenario);
|
|
84
63
|
Assert.Equal(summaryBeforeTheMiss, File.ReadAllBytes(ws.SummaryPath));
|
|
85
64
|
|
|
86
|
-
// E o contador do cliente interno registra a tentativa: a arvore de cache nao
|
|
87
|
-
// respondeu, e isso e um FATO medido, nao uma inferencia.
|
|
88
65
|
Assert.Equal(1, offline.Offline!.Invocations);
|
|
89
66
|
}
|
|
90
67
|
|
|
91
68
|
[Fact]
|
|
92
69
|
public async Task AFailedAssertionMakesTheScenarioREDWithoutAnyCacheMiss()
|
|
93
70
|
{
|
|
94
|
-
// A outra metade do contrato: reprovar por conteudo e diferente de reprovar por
|
|
95
|
-
// cache. Uma so mensagem para os dois casos mandaria o leitor procurar no lugar
|
|
96
|
-
// errado metade das vezes.
|
|
97
71
|
using var ws = new EvalWorkspace();
|
|
98
72
|
ws.WriteScenario("triagem", "acme", "saudacao", "bom dia", """[ { "kind": "contains", "value": "jamais aparece" } ]""");
|
|
99
73
|
|
|
@@ -130,9 +104,6 @@ public sealed class EvalCacheMissTests
|
|
|
130
104
|
await using var offline = EvalFixture.Create(ws.Options(EvalMode.Offline));
|
|
131
105
|
var summary = await offline.RunAllAsync(TestContext.Current.CancellationToken);
|
|
132
106
|
|
|
133
|
-
// Um cenario que nao cobra nada nao pode contar como cobertura. Deixa-lo verde e o
|
|
134
|
-
// mesmo defeito de um teste vazio — pior aqui, porque ele apareceria na contagem
|
|
135
|
-
// como se tivesse verificado algo.
|
|
136
107
|
Assert.Equal(1, summary.Failed);
|
|
137
108
|
Assert.Equal(0, summary.Passed);
|
|
138
109
|
}
|
package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs
CHANGED
|
@@ -4,14 +4,6 @@ using Xunit;
|
|
|
4
4
|
|
|
5
5
|
namespace Morph.AiKit.Tests.Evals;
|
|
6
6
|
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// O carimbo: por que a arvore de cache precisa dizer sob quais versoes foi gravada.
|
|
9
|
-
/// </summary>
|
|
10
|
-
/// <remarks>
|
|
11
|
-
/// A funcao de hash que produz a chave nao promete estabilidade entre versoes da biblioteca.
|
|
12
|
-
/// Sem carimbo, subir o pin chega como N misses inexplicaveis num dia em que ninguem mexeu
|
|
13
|
-
/// em nada; com carimbo, chega como uma frase que nomeia as DUAS versoes.
|
|
14
|
-
/// </remarks>
|
|
15
7
|
public sealed class EvalCacheStampTests
|
|
16
8
|
{
|
|
17
9
|
private static readonly FakeClock Today = new(new DateTimeOffset(2026, 9, 8, 12, 0, 0, TimeSpan.Zero));
|
|
@@ -51,8 +43,6 @@ public sealed class EvalCacheStampTests
|
|
|
51
43
|
using var ws = new EvalWorkspace();
|
|
52
44
|
Stamp(ws.CacheRoot);
|
|
53
45
|
|
|
54
|
-
// Ler de VOLTA do disco, e nao comparar o objeto com ele mesmo: o carimbo e um
|
|
55
|
-
// artefato, e um teste que so olhasse a instancia em memoria seria um espelho.
|
|
56
46
|
var read = EvalCacheStamp.Read(ws.CacheRoot);
|
|
57
47
|
|
|
58
48
|
Assert.NotNull(read);
|
|
@@ -72,7 +62,6 @@ public sealed class EvalCacheStampTests
|
|
|
72
62
|
|
|
73
63
|
Assert.Equal(EvalStampVerdict.PinMismatch, check.Verdict);
|
|
74
64
|
Assert.Contains("eval-cache-stale-pin", check.Reason, StringComparison.Ordinal);
|
|
75
|
-
// Dizer so uma das versoes obriga o leitor a ir buscar a outra.
|
|
76
65
|
Assert.Contains("gravada 10.9.0", check.Reason, StringComparison.Ordinal);
|
|
77
66
|
Assert.Contains("pin 10.11.0", check.Reason, StringComparison.Ordinal);
|
|
78
67
|
}
|
|
@@ -89,9 +78,6 @@ public sealed class EvalCacheStampTests
|
|
|
89
78
|
Assert.Equal(EvalStampVerdict.Missing, missing.Verdict);
|
|
90
79
|
Assert.Contains("eval-cache-unstamped", missing.Reason, StringComparison.Ordinal);
|
|
91
80
|
|
|
92
|
-
// As duas situacoes pedem acoes diferentes: "regrave" versus "regrave PORQUE o
|
|
93
|
-
// pacote mudou". Um veredito unico devolveria ao leitor a pergunta que o carimbo
|
|
94
|
-
// existia para responder.
|
|
95
81
|
Assert.NotEqual(missing.Verdict, divergent.Verdict);
|
|
96
82
|
Assert.DoesNotContain("eval-cache-stale-pin", missing.Reason, StringComparison.Ordinal);
|
|
97
83
|
}
|
|
@@ -116,8 +102,6 @@ public sealed class EvalCacheStampTests
|
|
|
116
102
|
|
|
117
103
|
var check = EvalCacheStamp.Check(ws.CacheRoot, Pin(pinnedAt: "2027-01-01"), Today);
|
|
118
104
|
|
|
119
|
-
// O carimbo CASA com o pin — e mesmo assim reprova, porque a regua esta adulterada.
|
|
120
|
-
// Uma data no futuro renovaria sozinha qualquer janela medida contra ela.
|
|
121
105
|
Assert.Equal(EvalStampVerdict.FuturePin, check.Verdict);
|
|
122
106
|
Assert.Contains("eval-pin-future", check.Reason, StringComparison.Ordinal);
|
|
123
107
|
}
|
|
@@ -135,8 +119,6 @@ public sealed class EvalCacheStampTests
|
|
|
135
119
|
["Microsoft.Extensions.AI.Evaluation"] = "10.11.0",
|
|
136
120
|
});
|
|
137
121
|
|
|
138
|
-
// A recusa acontece na CONSTRUCAO — antes de qualquer cenario. Nao faz sentido pagar
|
|
139
|
-
// o relogio de uma suite para descobrir no fim que a cache nao servia.
|
|
140
122
|
var error = Assert.Throws<InvalidOperationException>(
|
|
141
123
|
() => EvalFixture.Create(ws.Options(EvalMode.Offline, pin), timeProvider: Today));
|
|
142
124
|
|
|
@@ -148,7 +130,7 @@ public sealed class EvalCacheStampTests
|
|
|
148
130
|
{
|
|
149
131
|
using var ws = new EvalWorkspace();
|
|
150
132
|
|
|
151
|
-
// Cobrar carimbo
|
|
133
|
+
// Cobrar carimbo na escrita impediria criar a primeira cache.
|
|
152
134
|
var fixture = EvalFixture.Create(ws.Options(EvalMode.Refresh), new FakeChatClient());
|
|
153
135
|
|
|
154
136
|
Assert.True(fixture.StampCheck.IsOk);
|
package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs
CHANGED
|
@@ -4,37 +4,8 @@ using Xunit;
|
|
|
4
4
|
|
|
5
5
|
namespace Morph.AiKit.Tests.Evals;
|
|
6
6
|
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// A camada que NOMEIA a falha, testada pelo conteudo do que ela produz.
|
|
9
|
-
/// </summary>
|
|
10
|
-
/// <remarks>
|
|
11
|
-
/// <para>
|
|
12
|
-
/// Este arquivo fecha a setima ocorrencia da mesma classe nesta epic, e a mais ironica: o
|
|
13
|
-
/// mecanismo de <c>Failures</c> nasceu para consertar um eval que mordia ANONIMAMENTE, e
|
|
14
|
-
/// nasceu ele proprio sem par. As tres partes podiam ser desligadas sem matar um teste —
|
|
15
|
-
/// <c>CollectFailures</c> devolvendo lista vazia, o ramo da vacuidade nunca disparando, e o
|
|
16
|
-
/// nome da assercao trocado por um literal — e a suite seguia "165 total, 0 falhou".
|
|
17
|
-
/// </para>
|
|
18
|
-
/// <para>
|
|
19
|
-
/// A causa foi uma unica testemunha automatizada: <c>Assert.Empty(summary.Failures)</c> num
|
|
20
|
-
/// run VERDE. Essa asserção é verdadeira exatamente quando o mecanismo esta morto — ela nao
|
|
21
|
-
/// distingue "nao houve falha" de "falha nenhuma e registrada". Uma lista vazia prova a
|
|
22
|
-
/// ausencia de conteudo, jamais a presenca de capacidade.
|
|
23
|
-
/// </para>
|
|
24
|
-
/// <para>
|
|
25
|
-
/// Por isso aqui nao se assere sobre <c>Failures</c> VAZIA: assere-se sobre o CONTEUDO de
|
|
26
|
-
/// <c>Failures</c> num run que falha de verdade — o par, a mesma forma que fechou o motor de
|
|
27
|
-
/// assercao um andar abaixo.
|
|
28
|
-
/// </para>
|
|
29
|
-
/// </remarks>
|
|
30
7
|
public sealed class EvalFailuresTests
|
|
31
8
|
{
|
|
32
|
-
/// <summary>Grava a cache do cenario com o texto dado, e devolve o fixture offline pronto.</summary>
|
|
33
|
-
/// <remarks>
|
|
34
|
-
/// A cache e semeada pelo <see cref="FakeChatClient"/>, NUNCA por chamada paga: o caminho
|
|
35
|
-
/// de escrita exercitado e o da biblioteca, e a leitura offline le exatamente o que aquela
|
|
36
|
-
/// escrita produziu.
|
|
37
|
-
/// </remarks>
|
|
38
9
|
private static async Task<EvalRunSummary> RunOfflineAsync(EvalWorkspace ws, params string[] responses)
|
|
39
10
|
{
|
|
40
11
|
using var fake = new FakeChatClient
|
|
@@ -60,9 +31,6 @@ public sealed class EvalFailuresTests
|
|
|
60
31
|
[Fact]
|
|
61
32
|
public async Task AFailedScenarioIsRegisteredWithScenarioAssertionKindAndReason()
|
|
62
33
|
{
|
|
63
|
-
// O caso central: uma assercao reprova, e a falha chega ao sumario COM NOME. Sem isso
|
|
64
|
-
// o veredito viaja como contador — "Expected 0, Actual 1" — e quem le tem de abrir o
|
|
65
|
-
// dataset para descobrir o que quebrou.
|
|
66
34
|
using var ws = new EvalWorkspace();
|
|
67
35
|
ws.WriteScenario(
|
|
68
36
|
"triagem", "acme", "saudacao", "bom dia",
|
|
@@ -79,7 +47,6 @@ public sealed class EvalFailuresTests
|
|
|
79
47
|
Assert.Contains("FALHOU", failure.Reason, StringComparison.Ordinal);
|
|
80
48
|
Assert.Contains("bom dia", failure.Reason, StringComparison.Ordinal);
|
|
81
49
|
|
|
82
|
-
// A linha legivel, no formato que a rubrica eval-de-modelo.md cita como exemplo.
|
|
83
50
|
Assert.Contains(
|
|
84
51
|
"eval-fail: triagem.acme.saudacao",
|
|
85
52
|
summary.DescribeFailures(),
|
|
@@ -89,9 +56,6 @@ public sealed class EvalFailuresTests
|
|
|
89
56
|
[Fact]
|
|
90
57
|
public async Task TwoReprovedAssertionsInOneScenarioProduceTWOLinesEachNamingItsOwnRule()
|
|
91
58
|
{
|
|
92
|
-
// `Failures` e LISTA, e nao contador, porque as duas linhas dizem coisas diferentes:
|
|
93
|
-
// uma resposta pode quebrar em mais de uma regua, e isso separa "o prompt mudou" de
|
|
94
|
-
// "o modelo parou de responder". Se o tipo fosse um booleano, esta distincao morreria.
|
|
95
59
|
using var ws = new EvalWorkspace();
|
|
96
60
|
ws.WriteScenario(
|
|
97
61
|
"triagem", "acme", "saudacao", "bom dia",
|
|
@@ -110,18 +74,12 @@ public sealed class EvalFailuresTests
|
|
|
110
74
|
Assert.Contains(summary.Failures, f => f.Assertion == "cumprimenta" && f.Kind == "Contains");
|
|
111
75
|
Assert.Contains(summary.Failures, f => f.Assertion == "sem-dado-pessoal" && f.Kind == "NoPii");
|
|
112
76
|
|
|
113
|
-
// Toda linha nomeia o cenario: uma falha sem cenario obriga a mesma busca que ela
|
|
114
|
-
// deveria dispensar.
|
|
115
77
|
Assert.All(summary.Failures, f => Assert.Equal("triagem.acme.saudacao", f.Scenario));
|
|
116
78
|
}
|
|
117
79
|
|
|
118
80
|
[Fact]
|
|
119
81
|
public async Task AScenarioWithNOAssertionsIsCountedANDNamed()
|
|
120
82
|
{
|
|
121
|
-
// O cenario sem assercao nenhuma reprova por VACUIDADE: ele nao cobra nada. Nesse caso
|
|
122
|
-
// nao existe outcome reprovado para nomear, e sem um ramo proprio ele seria contado em
|
|
123
|
-
// `Failed` e invisivel em `Failures` — um numero sem frase, que e metade exata do
|
|
124
|
-
// defeito que este tipo existe para fechar.
|
|
125
83
|
using var ws = new EvalWorkspace();
|
|
126
84
|
ws.WriteScenario("triagem", "acme", "vazio", "bom dia", "[]");
|
|
127
85
|
|
|
@@ -133,16 +91,12 @@ public sealed class EvalFailuresTests
|
|
|
133
91
|
Assert.Equal("triagem.acme.vazio", failure.Scenario);
|
|
134
92
|
Assert.Contains("sem assercao deterministica", failure.Reason, StringComparison.Ordinal);
|
|
135
93
|
|
|
136
|
-
// Contado E nomeado: o numero e a frase tem de concordar sempre.
|
|
137
94
|
Assert.Equal(summary.Failed, summary.Failures.Count);
|
|
138
95
|
}
|
|
139
96
|
|
|
140
97
|
[Fact]
|
|
141
98
|
public async Task AGreenRunRegistersNoFailureAndSaysSoInWords()
|
|
142
99
|
{
|
|
143
|
-
// A reciproca vive AO LADO dos casos que falham de verdade, nunca sozinha: uma suite
|
|
144
|
-
// cuja unica testemunha e `Assert.Empty` num run verde e verdadeira precisamente
|
|
145
|
-
// quando o mecanismo esta morto.
|
|
146
100
|
using var ws = new EvalWorkspace();
|
|
147
101
|
ws.WriteScenario(
|
|
148
102
|
"triagem", "acme", "saudacao", "bom dia",
|
|
@@ -158,8 +112,6 @@ public sealed class EvalFailuresTests
|
|
|
158
112
|
[Fact]
|
|
159
113
|
public async Task TheNamedFailuresSurviveTheROUNDTRIPThroughTheSummaryFileTheCLIReads()
|
|
160
114
|
{
|
|
161
|
-
// O sumario e um ARQUIVO: quem consome as falhas e o CLI, do outro lado do disco. Uma
|
|
162
|
-
// lista que existisse so em memoria seria invisivel exatamente onde precisa ser lida.
|
|
163
115
|
using var ws = new EvalWorkspace();
|
|
164
116
|
ws.WriteScenario(
|
|
165
117
|
"triagem", "acme", "saudacao", "bom dia",
|
|
@@ -5,17 +5,6 @@ using Xunit;
|
|
|
5
5
|
|
|
6
6
|
namespace Morph.AiKit.Tests.Evals;
|
|
7
7
|
|
|
8
|
-
/// <summary>
|
|
9
|
-
/// A prova que decide a feature: <b>com a cache quente, o eval roda sem credencial e sem
|
|
10
|
-
/// rede</b>.
|
|
11
|
-
/// </summary>
|
|
12
|
-
/// <remarks>
|
|
13
|
-
/// Essa frase foi derivada de leitura de fonte antes de existir codigo. Aqui ela vira
|
|
14
|
-
/// medicao: uma arvore de cache e gravada por um fake, o processo troca o cliente interno
|
|
15
|
-
/// por um que RECUSA qualquer chamada, e a suite termina verde. Se o cliente interno fosse
|
|
16
|
-
/// tocado uma unica vez, o contador dele deixaria de ser zero — e nenhuma das assercoes
|
|
17
|
-
/// abaixo passaria.
|
|
18
|
-
/// </remarks>
|
|
19
8
|
public sealed class EvalFixtureTests
|
|
20
9
|
{
|
|
21
10
|
[Fact]
|
|
@@ -25,18 +14,12 @@ public sealed class EvalFixtureTests
|
|
|
25
14
|
ws.WriteScenario("triagem", "acme", "saudacao", "bom dia", """[ { "kind": "contains", "value": "Ola" } ]""");
|
|
26
15
|
ws.WriteScenario("triagem", "acme", "handoff", "quero falar com humano", """[ { "kind": "contains", "value": "humano" } ]""");
|
|
27
16
|
|
|
28
|
-
// 1) GRAVA a arvore contra o fake — nunca contra uma API paga. O caminho de escrita
|
|
29
|
-
// exercitado e o da biblioteca, e a chave e computada pela mesma funcao que a
|
|
30
|
-
// leitura vai usar.
|
|
31
17
|
using var fake = new FakeChatClient
|
|
32
18
|
{
|
|
33
19
|
MetadataProviderName = "morph-fake",
|
|
34
20
|
MetadataDefaultModelId = "modelo-exemplar",
|
|
35
21
|
};
|
|
36
|
-
// A
|
|
37
|
-
// "saudacao". Enfileirar na ordem alfabetica errada foi como esta suite descobriu
|
|
38
|
-
// que LoadAll ordena — e a ordem estavel e o que faz duas maquinas gravarem a mesma
|
|
39
|
-
// arvore de cache.
|
|
22
|
+
// A ordem e a do dataset (ordinal por caminho): "handoff" antes de "saudacao".
|
|
40
23
|
fake.EnqueueText("Encaminhando para um humano agora.").EnqueueText("Ola! Bom dia.");
|
|
41
24
|
|
|
42
25
|
await using (var refresh = EvalFixture.Create(ws.Options(EvalMode.Refresh), fake))
|
|
@@ -50,8 +33,6 @@ public sealed class EvalFixtureTests
|
|
|
50
33
|
Assert.Equal(2, fake.CallCount);
|
|
51
34
|
Assert.True(File.Exists(EvalCacheStamp.PathFor(ws.CacheRoot)), "o refresh tem de deixar o carimbo na raiz da cache");
|
|
52
35
|
|
|
53
|
-
// 2) LE offline. O cliente interno agora RECUSA: se a cache nao respondesse, este
|
|
54
|
-
// bloco lancaria EvalCacheMissException em vez de terminar verde.
|
|
55
36
|
await using var offline = EvalFixture.Create(ws.Options(EvalMode.Offline));
|
|
56
37
|
var summary = await offline.RunAllAsync(TestContext.Current.CancellationToken);
|
|
57
38
|
|
|
@@ -63,8 +44,6 @@ public sealed class EvalFixtureTests
|
|
|
63
44
|
Assert.Equal("ok", summary.Stamp);
|
|
64
45
|
Assert.Equal(0, offline.Offline!.Invocations);
|
|
65
46
|
|
|
66
|
-
// 3) O fake nao foi tocado de novo: a leitura offline nao passou por ele nem por
|
|
67
|
-
// acidente (ele esta noutro processo logico, mas a contagem prova a intencao).
|
|
68
47
|
Assert.Equal(2, fake.CallCount);
|
|
69
48
|
}
|
|
70
49
|
|
|
@@ -84,8 +63,6 @@ public sealed class EvalFixtureTests
|
|
|
84
63
|
await using var offline = EvalFixture.Create(ws.Options(EvalMode.Offline));
|
|
85
64
|
await offline.RunAllAsync(TestContext.Current.CancellationToken);
|
|
86
65
|
|
|
87
|
-
// Ler de volta o ARQUIVO, e nao o objeto em memoria: o contrato com o CLI e o
|
|
88
|
-
// arquivo, e um teste que so olhasse o objeto seria um espelho do gerador.
|
|
89
66
|
var reread = EvalRunSummary.ReadFrom(ws.SummaryPath);
|
|
90
67
|
Assert.NotNull(reread);
|
|
91
68
|
Assert.Equal(1, reread!.Scenarios);
|
|
@@ -103,17 +80,12 @@ public sealed class EvalFixtureTests
|
|
|
103
80
|
var error = Assert.Throws<ArgumentOutOfRangeException>(() => EvalFixture.Create(options));
|
|
104
81
|
|
|
105
82
|
Assert.Contains("eval-cache-ttl-too-short", error.Message, StringComparison.Ordinal);
|
|
106
|
-
// A mensagem tem de dizer POR QUE, e nao so que o numero e pequeno: a expiracao apaga
|
|
107
|
-
// o diretorio da chave durante a LEITURA.
|
|
108
83
|
Assert.Contains("DESTRUTIVA NA LEITURA", error.Message, StringComparison.Ordinal);
|
|
109
84
|
}
|
|
110
85
|
|
|
111
86
|
[Fact]
|
|
112
87
|
public void TheLibraryDefaultTimeToLiveWouldExpireACommittedCacheWithinWeeks()
|
|
113
88
|
{
|
|
114
|
-
// MEDIDO, nao lembrado: e este fato que torna o TTL explicito obrigatorio. Se um dia
|
|
115
|
-
// a biblioteca subir o default para alem do piso do kit, este teste morre e a
|
|
116
|
-
// exigencia pode ser rediscutida com um numero na mao.
|
|
117
89
|
Assert.True(
|
|
118
90
|
Defaults.DefaultTimeToLiveForCacheEntries < EvalCacheOptions.MinimumTimeToLive,
|
|
119
91
|
$"o default da biblioteca e {Defaults.DefaultTimeToLiveForCacheEntries.TotalDays:0} dias, "
|
|
@@ -6,20 +6,9 @@ using Xunit;
|
|
|
6
6
|
|
|
7
7
|
namespace Morph.AiKit.Tests.Evals;
|
|
8
8
|
|
|
9
|
-
/// <summary>
|
|
10
|
-
/// Regravacao, e o invariante barato que protege a premissa de commitabilidade:
|
|
11
|
-
/// <b>uma leitura nao escreve um byte na arvore de cache</b>.
|
|
12
|
-
/// </summary>
|
|
13
9
|
public sealed class EvalRefreshTests
|
|
14
10
|
{
|
|
15
|
-
|
|
16
|
-
/// Impressao digital da arvore: caminho relativo mais SHA-256 de cada arquivo.
|
|
17
|
-
/// </summary>
|
|
18
|
-
/// <remarks>
|
|
19
|
-
/// Comparar CONTEUDO, e nao data de modificacao: o que importa e se o proximo
|
|
20
|
-
/// <c>git status</c> mostra diff, e o git tambem olha conteudo. Um teste sobre mtime
|
|
21
|
-
/// falharia por gravacao identica e passaria por gravacao diferente no mesmo tick.
|
|
22
|
-
/// </remarks>
|
|
11
|
+
// Compara conteudo, nao mtime: e o conteudo que o git ve como diff.
|
|
23
12
|
private static IReadOnlyList<string> Fingerprint(string root)
|
|
24
13
|
{
|
|
25
14
|
if (!Directory.Exists(root))
|
|
@@ -63,9 +52,6 @@ public sealed class EvalRefreshTests
|
|
|
63
52
|
Assert.Equal(3, summary.Passed);
|
|
64
53
|
}
|
|
65
54
|
|
|
66
|
-
// Se algum dia a biblioteca passar a tocar disco no caminho de HIT, este canario morde
|
|
67
|
-
// no dia em que isso acontecer — e nao meses depois, quando alguem estranhar um diff
|
|
68
|
-
// que aparece sozinho em toda rodada.
|
|
69
55
|
Assert.Equal(before, Fingerprint(ws.CacheRoot));
|
|
70
56
|
}
|
|
71
57
|
|
|
@@ -79,9 +65,6 @@ public sealed class EvalRefreshTests
|
|
|
79
65
|
await using var refresh = EvalFixture.Create(ws.Options(EvalMode.Refresh), fake);
|
|
80
66
|
await refresh.RunAllAsync(TestContext.Current.CancellationToken);
|
|
81
67
|
|
|
82
|
-
// Os resultados carregam latencia de cronometro, uso de tokens e o flag de cache hit:
|
|
83
|
-
// commita-los seria diff garantido em toda rodada, sem sinal nenhum. Por CONSTRUCAO
|
|
84
|
-
// (outra raiz), nunca por um .gitignore dentro de um diretorio commitado.
|
|
85
68
|
Assert.True(Directory.Exists(ws.ResultsRoot), "o store de resultados tem de existir depois de um run");
|
|
86
69
|
Assert.NotEmpty(Directory.EnumerateFiles(ws.ResultsRoot, "*", SearchOption.AllDirectories));
|
|
87
70
|
|
|
@@ -102,8 +85,6 @@ public sealed class EvalRefreshTests
|
|
|
102
85
|
await using var refresh = EvalFixture.Create(ws.Options(EvalMode.Refresh), fake);
|
|
103
86
|
var summary = await refresh.RunAllAsync(TestContext.Current.CancellationToken);
|
|
104
87
|
|
|
105
|
-
// O contador do cliente real e o que torna "a cache esta fria" uma MEDICAO: a arvore
|
|
106
|
-
// mudou e precisa ser commitada, e isso e afirmacao com consequencia.
|
|
107
88
|
Assert.Equal(3, summary.Misses);
|
|
108
89
|
Assert.Equal(EvalCacheState.Cold, summary.Cache);
|
|
109
90
|
Assert.Equal(3, fake.CallCount);
|
|
@@ -121,9 +102,6 @@ public sealed class EvalRefreshTests
|
|
|
121
102
|
await first.RunAllAsync(TestContext.Current.CancellationToken);
|
|
122
103
|
}
|
|
123
104
|
|
|
124
|
-
// O cenario do meio some do dataset. Uma regravacao PARCIAL deixaria a entrada antiga
|
|
125
|
-
// dele viva na arvore, e a proxima leitura offline nao teria como saber que ela nao
|
|
126
|
-
// corresponde mais a nenhum cenario declarado.
|
|
127
105
|
File.Delete(Path.Combine(ws.DatasetRoot, "triagem", "acme", "b-segundo.json"));
|
|
128
106
|
|
|
129
107
|
using (var fake = new FakeChatClient { MetadataProviderName = "morph-fake", MetadataDefaultModelId = "modelo-exemplar" })
|
|
@@ -144,24 +122,14 @@ public sealed class EvalRefreshTests
|
|
|
144
122
|
using var ws = new EvalWorkspace();
|
|
145
123
|
using var fake = new FakeChatClient { MetadataProviderName = "morph-fake", MetadataDefaultModelId = "modelo-exemplar" };
|
|
146
124
|
|
|
147
|
-
// A lista vem do KIT, e nao do teste: e ela que da ao pacote .Quality cobertura de
|
|
148
|
-
// COMPILACAO em src/ (sem um using real la, o pin daquele pacote teria so contrato de
|
|
149
|
-
// versao, e um rename de API nao reprovaria PR nenhuma).
|
|
150
125
|
var quality = EvalQualityEvaluators.Stable();
|
|
151
126
|
|
|
152
127
|
await using var fixture = EvalFixture.Create(ws.Options(EvalMode.Refresh), fake, quality);
|
|
153
128
|
|
|
154
|
-
// A superficie existe e COMPILA contra as versoes do pin — que e o que um kit de
|
|
155
|
-
// template tem de garantir. O que ela NAO faz e decidir: o veredito de um cenario sai
|
|
156
|
-
// das assercoes deterministicas, e uma metrica de juizo entra no sumario como numero.
|
|
157
|
-
// Sem isso, a variancia de LLM reprovaria PRs e o no de eval nasceria destinado a ser
|
|
158
|
-
// desligado do CI.
|
|
159
129
|
Assert.Equal(2, quality.Count);
|
|
160
130
|
Assert.All(quality, evaluator => Assert.NotEmpty(evaluator.EvaluationMetricNames));
|
|
161
131
|
Assert.Empty(fixture.Summary.Quality);
|
|
162
132
|
|
|
163
|
-
// Uma lista NOVA a cada chamada: um array compartilhado seria estado global
|
|
164
|
-
// disfarcado de conveniencia, e a ReportingConfiguration guarda o que recebe.
|
|
165
133
|
Assert.NotSame(quality, EvalQualityEvaluators.Stable());
|
|
166
134
|
}
|
|
167
135
|
|
|
@@ -172,11 +140,7 @@ public sealed class EvalRefreshTests
|
|
|
172
140
|
[InlineData("Microsoft.Extensions.AI.Evaluation.Quality.ToolCallAccuracyEvaluator", true)]
|
|
173
141
|
public void WhichQualityEvaluatorsAreExperimentalIsMEASURED_NotRemembered(string typeName, bool experimental)
|
|
174
142
|
{
|
|
175
|
-
//
|
|
176
|
-
// [Experimental("AIEVAL001")] na 10.9.0, e usa-los sob TreatWarningsAsErrors exigiria
|
|
177
|
-
// suprimir um diagnostico — uma decisao consciente, nunca um efeito colateral. Se um
|
|
178
|
-
// dia a Microsoft estabilizar (ou marcar) algum deles, este teste morre e a escolha e
|
|
179
|
-
// rediscutida com o fato na mao, em vez de envelhecer em silencio num comentario.
|
|
143
|
+
// Dois dos quatro avaliadores sao [Experimental("AIEVAL001")] na 10.9.0 e exigiriam supressao sob TreatWarningsAsErrors.
|
|
180
144
|
var type = typeof(RelevanceEvaluator).Assembly.GetType(typeName);
|
|
181
145
|
Assert.NotNull(type);
|
|
182
146
|
|
|
@@ -2,15 +2,6 @@ using Morph.AiKit.Evals;
|
|
|
2
2
|
|
|
3
3
|
namespace Morph.AiKit.Tests.Evals;
|
|
4
4
|
|
|
5
|
-
/// <summary>
|
|
6
|
-
/// Um diretorio temporario com dataset, cache e resultados — a montagem que todo teste de
|
|
7
|
-
/// eval precisa, escrita uma vez.
|
|
8
|
-
/// </summary>
|
|
9
|
-
/// <remarks>
|
|
10
|
-
/// Ele existe para que os testes falem de COMPORTAMENTO e nao de caminhos. E cada instancia
|
|
11
|
-
/// tem raiz propria (GUID v7): dois testes rodando lado a lado nao podem disputar a mesma
|
|
12
|
-
/// arvore de cache, senao a falha aparece como flake e nao como defeito.
|
|
13
|
-
/// </remarks>
|
|
14
5
|
internal sealed class EvalWorkspace : IDisposable
|
|
15
6
|
{
|
|
16
7
|
public EvalWorkspace()
|
|
@@ -30,7 +21,6 @@ internal sealed class EvalWorkspace : IDisposable
|
|
|
30
21
|
|
|
31
22
|
public string SummaryPath => Path.Combine(Root, "summary.json");
|
|
32
23
|
|
|
33
|
-
/// <summary>O pin exemplar: a data e as versoes sob as quais a arvore diz ter sido gravada.</summary>
|
|
34
24
|
public static EvalPin Pin { get; } = new(
|
|
35
25
|
"2026-09-08",
|
|
36
26
|
new Dictionary<string, string>(StringComparer.Ordinal)
|
|
@@ -39,7 +29,6 @@ internal sealed class EvalWorkspace : IDisposable
|
|
|
39
29
|
["Microsoft.Extensions.AI.Evaluation"] = "10.9.0",
|
|
40
30
|
});
|
|
41
31
|
|
|
42
|
-
/// <summary>Escreve um cenario no dataset e devolve o caminho do arquivo.</summary>
|
|
43
32
|
public string WriteScenario(string agent, string tenant, string name, string userText, string assertions)
|
|
44
33
|
{
|
|
45
34
|
var dir = Path.Combine(DatasetRoot, agent, tenant);
|
|
@@ -54,7 +43,6 @@ internal sealed class EvalWorkspace : IDisposable
|
|
|
54
43
|
return file;
|
|
55
44
|
}
|
|
56
45
|
|
|
57
|
-
/// <summary>Opcoes prontas para um modo, com TTL longo e o pin exemplar.</summary>
|
|
58
46
|
public EvalCacheOptions Options(EvalMode mode, EvalPin? pin = null) => new()
|
|
59
47
|
{
|
|
60
48
|
CacheRoot = CacheRoot,
|
|
@@ -79,8 +67,7 @@ internal sealed class EvalWorkspace : IDisposable
|
|
|
79
67
|
}
|
|
80
68
|
catch (IOException)
|
|
81
69
|
{
|
|
82
|
-
//
|
|
83
|
-
// que veio provar.
|
|
70
|
+
// Falha ao apagar o temporario nao deve derrubar um teste que ja provou o que veio provar.
|
|
84
71
|
}
|
|
85
72
|
}
|
|
86
73
|
}
|
|
@@ -4,42 +4,14 @@ using Xunit;
|
|
|
4
4
|
|
|
5
5
|
namespace Morph.AiKit.Tests.Evals;
|
|
6
6
|
|
|
7
|
-
/// <summary>
|
|
8
|
-
/// Os evals DO PROPRIO KIT, contra a arvore de cache COMMITADA — sem credencial, sem rede.
|
|
9
|
-
/// </summary>
|
|
10
|
-
/// <remarks>
|
|
11
|
-
/// <para>
|
|
12
|
-
/// Esta classe e o exemplar que um projeto copia. Ela roda com a categoria <c>Eval</c>, le a
|
|
13
|
-
/// cache commitada em <c>eval-cache/</c> e o dataset versionado em <c>eval-datasets/</c>, e
|
|
14
|
-
/// termina verde com TODAS as variaveis de credencial ausentes. Se algum cenario nao estiver
|
|
15
|
-
/// na cache, ela fica VERMELHA nomeando o cenario — nunca <c>skip</c>.
|
|
16
|
-
/// </para>
|
|
17
|
-
/// <para>
|
|
18
|
-
/// <b>A cache foi semeada por FAKE, jamais por chamada paga</b> (ver <see cref="SeedTheCommittedCacheTree"/>).
|
|
19
|
-
/// O caminho de ESCRITA exercitado e o da biblioteca e a chave e computada pela mesma funcao
|
|
20
|
-
/// que a leitura usa — entao a leitura offline le exatamente o que aquela escrita produziu.
|
|
21
|
-
/// A lacuna residual, declarada em voz alta: isto NAO prova que uma cache gravada contra um
|
|
22
|
-
/// provider real e legivel offline. O que a mitiga e um fato medido — a chave depende de
|
|
23
|
-
/// <c>ProviderName</c> e <c>DefaultModelId</c>, e nao do endpoint, e os dois estao sob nosso
|
|
24
|
-
/// controle no cliente offline.
|
|
25
|
-
/// </para>
|
|
26
|
-
/// </remarks>
|
|
27
7
|
public sealed class KitEvals
|
|
28
8
|
{
|
|
29
|
-
|
|
9
|
+
// Provider e Model entram na chave de cache: trocar qualquer um invalida a arvore commitada.
|
|
30
10
|
private const string Provider = "morph-fake";
|
|
31
11
|
|
|
32
|
-
/// <summary>Idem. Trocar qualquer um dos dois invalida a arvore inteira.</summary>
|
|
33
12
|
private const string Model = "morph-kit-exemplar";
|
|
34
13
|
|
|
35
|
-
|
|
36
|
-
/// O diretorio do PROJETO de teste, achado subindo a partir do assembly.
|
|
37
|
-
/// </summary>
|
|
38
|
-
/// <remarks>
|
|
39
|
-
/// De proposito: a alternativa seria copiar a cache para <c>bin/</c> e ler a copia — e ai
|
|
40
|
-
/// o canario de "um run offline nao suja a arvore commitada" seria verdadeiro por
|
|
41
|
-
/// vacuidade, porque a arvore commitada nem teria sido tocada.
|
|
42
|
-
/// </remarks>
|
|
14
|
+
// Le a arvore commitada no projeto, nao uma copia em bin/: senao o canario de "nao suja a arvore" seria vacuo.
|
|
43
15
|
private static string ProjectDirectory
|
|
44
16
|
{
|
|
45
17
|
get
|
|
@@ -59,7 +31,6 @@ public sealed class KitEvals
|
|
|
59
31
|
|
|
60
32
|
private static string DatasetRoot => Path.Combine(ProjectDirectory, "eval-datasets");
|
|
61
33
|
|
|
62
|
-
/// <summary>O pin sob o qual a arvore commitada foi gravada — o mesmo que o carimbo carrega.</summary>
|
|
63
34
|
private static EvalPin Pin => new(
|
|
64
35
|
"2026-09-08",
|
|
65
36
|
new Dictionary<string, string>(StringComparer.Ordinal)
|
|
@@ -74,8 +45,6 @@ public sealed class KitEvals
|
|
|
74
45
|
{
|
|
75
46
|
CacheRoot = CacheRoot,
|
|
76
47
|
DatasetRoot = DatasetRoot,
|
|
77
|
-
// FORA da arvore commitada: os resultados carregam latencia, uso de tokens e o flag
|
|
78
|
-
// de cache hit, e commita-los seria diff garantido em toda rodada.
|
|
79
48
|
ResultsRoot = Path.Combine(Path.GetTempPath(), "morph-kit-eval-results"),
|
|
80
49
|
TimeToLive = TimeSpan.FromDays(3650),
|
|
81
50
|
ProviderName = Provider,
|
|
@@ -89,9 +58,6 @@ public sealed class KitEvals
|
|
|
89
58
|
[Trait("Category", "Eval")]
|
|
90
59
|
public async Task TheKitEvalsRunGreenFromTheCommittedCacheWithNoCredentialAtAll()
|
|
91
60
|
{
|
|
92
|
-
// Nenhuma variavel de credencial e lida por nada nesta montagem — o cliente interno
|
|
93
|
-
// e o OfflineChatClient, que nao abre socket e nao le ambiente. Se a cache nao
|
|
94
|
-
// respondesse, este teste morreria com EvalCacheMissException nomeando o cenario.
|
|
95
61
|
var summaryPath = Path.Combine(Path.GetTempPath(), $"morph-kit-eval-{Guid.CreateVersion7():N}.json");
|
|
96
62
|
|
|
97
63
|
try
|
|
@@ -102,20 +68,13 @@ public sealed class KitEvals
|
|
|
102
68
|
Assert.Equal(EvalCacheState.Warm, summary.Cache);
|
|
103
69
|
Assert.Equal("ok", summary.Stamp);
|
|
104
70
|
|
|
105
|
-
// NOMEANDO. `Assert.Equal(0, summary.Failed)` produzia "Expected 0, Actual 1" —
|
|
106
|
-
// verdadeiro e inutil: nao dizia qual cenario nem qual regua. A rubrica
|
|
107
|
-
// eval-de-modelo.md exige, na banda 9-10, derrubar o cenario NOMEANDO-O.
|
|
108
71
|
Assert.True(summary.Failed == 0, summary.DescribeFailures());
|
|
109
72
|
Assert.Empty(summary.Failures);
|
|
110
73
|
Assert.Equal(summary.Scenarios, summary.Passed);
|
|
111
74
|
|
|
112
|
-
// A prova central, e ela e um CONTADOR: o cliente interno e o unico caminho para
|
|
113
|
-
// a rede, e ele nao foi invocado nenhuma vez.
|
|
114
75
|
Assert.Equal(0, summary.Misses);
|
|
115
76
|
Assert.Equal(0, fixture.Offline!.Invocations);
|
|
116
77
|
|
|
117
|
-
// O dataset nao pode encolher em silencio: uma suite de eval que passa a avaliar
|
|
118
|
-
// menos cenarios e a mesma classe de defeito que uma baseline de teste caindo.
|
|
119
78
|
Assert.Equal(4, summary.Scenarios);
|
|
120
79
|
}
|
|
121
80
|
finally
|
|
@@ -144,17 +103,10 @@ public sealed class KitEvals
|
|
|
144
103
|
[Trait("Category", "Eval")]
|
|
145
104
|
public void TheCommittedCacheCarriesNoABSOLUTEPathOfTheMachineThatWroteIt()
|
|
146
105
|
{
|
|
147
|
-
// A premissa que torna a arvore commitavel: nada dentro dela aponta para o disco de
|
|
148
|
-
// quem gravou. Medido, e nao suposto — se um dia a biblioteca passar a gravar o
|
|
149
|
-
// storageRootPath dentro das entradas, este canario morde.
|
|
150
106
|
var root = ProjectDirectory.Replace('\\', '/');
|
|
151
107
|
var drive = Path.GetPathRoot(ProjectDirectory)?.Replace('\\', '/');
|
|
152
108
|
|
|
153
|
-
//
|
|
154
|
-
// `GetPathRoot` devolve "/", que nao identifica nada — e exigir que a cache nao
|
|
155
|
-
// contenha "/" reprova qualquer arquivo com um path ou uma URL dentro. O canario
|
|
156
|
-
// passava no Windows e era ABSURDO no Linux: media "nenhuma barra em lugar nenhum".
|
|
157
|
-
// Medido no CI (job ai-kit, ubuntu): 1 falha, `Assert.DoesNotContain() Sub-string found`.
|
|
109
|
+
// Em POSIX GetPathRoot devolve "/", que nao identifica maquina; so unidade nomeada ("R:/") identifica.
|
|
158
110
|
var namedDrive = drive is { Length: > 1 } && drive.Contains(':');
|
|
159
111
|
|
|
160
112
|
foreach (var file in Directory.EnumerateFiles(CacheRoot, "*", SearchOption.AllDirectories))
|
|
@@ -168,21 +120,6 @@ public sealed class KitEvals
|
|
|
168
120
|
}
|
|
169
121
|
}
|
|
170
122
|
|
|
171
|
-
/// <summary>
|
|
172
|
-
/// Regrava a arvore commitada a partir do fake. NAO roda em CI.
|
|
173
|
-
/// </summary>
|
|
174
|
-
/// <remarks>
|
|
175
|
-
/// <para>
|
|
176
|
-
/// Ele existe committed, e nao como um script que alguem rodou uma vez e jogou fora,
|
|
177
|
-
/// porque a proxima pessoa que subir o pin precisa saber COMO regravar — e uma instrucao
|
|
178
|
-
/// em prosa envelhece; um teste executavel nao.
|
|
179
|
-
/// </para>
|
|
180
|
-
/// <para>
|
|
181
|
-
/// A porta e uma variavel de ambiente, e nao um <c>[Fact(Skip=...)]</c> fixo, porque um
|
|
182
|
-
/// skip fixo nunca poderia ser exercitado: seria um seeder que ninguem consegue rodar.
|
|
183
|
-
/// Rode com <c>MORPH_EVAL_SEED=1 dotnet test</c> e commite a arvore que sair.
|
|
184
|
-
/// </para>
|
|
185
|
-
/// </remarks>
|
|
186
123
|
[Fact]
|
|
187
124
|
public async Task SeedTheCommittedCacheTree()
|
|
188
125
|
{
|
|
@@ -196,9 +133,7 @@ public sealed class KitEvals
|
|
|
196
133
|
MetadataDefaultModelId = Model,
|
|
197
134
|
};
|
|
198
135
|
|
|
199
|
-
// Uma resposta por cenario, na
|
|
200
|
-
// orcamento/acme/a-resposta-estruturada, triagem/acme/a-saudacao,
|
|
201
|
-
// triagem/acme/b-escalonamento, triagem/globex/a-saudacao.
|
|
136
|
+
// Uma resposta por cenario, na ordem ordinal de caminho do dataset.
|
|
202
137
|
fake.EnqueueText("""{"valor":"R$ 1.200,00","prazo":"5 dias uteis"}""")
|
|
203
138
|
.EnqueueText("Bom dia! Como posso ajudar voce hoje?")
|
|
204
139
|
.EnqueueText("Encaminhando voce para um atendente humano agora.")
|