@polymorphism-tech/morph-spec 8.34.2 → 8.35.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +20 -3
- package/README.md +2 -2
- package/bin/morph-spec.js +18 -4
- package/bin/validate.js +379 -379
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +5 -4
- package/docs/QUICKSTART.md +1 -1
- package/framework/MORPH.md +16 -17
- package/framework/agents.json +197 -44
- package/framework/ai-pin.json +22 -0
- package/framework/commands/morph-media.md +45 -0
- package/framework/docs/CLI.md +234 -9
- package/framework/evals/proof-rubrics/README.md +76 -0
- package/framework/evals/proof-rubrics/eval-de-modelo.md +64 -0
- package/framework/evals/proof-rubrics/fiacao.md +54 -0
- package/framework/evals/proof-rubrics/guard-cego.md +52 -0
- package/framework/evals/proof-rubrics/media-claims.md +109 -0
- package/framework/evals/proof-rubrics/mutation-proof.md +77 -0
- package/framework/evals/proof-rubrics/observabilidade-genai.md +119 -0
- package/framework/evals/proof-rubrics/verde-nao-e-coberto.md +48 -0
- package/framework/evals/rubrics/architecture.md +30 -0
- package/framework/evals/rubrics/code-quality.md +26 -0
- package/framework/evals/rubrics/test-coverage.md +20 -0
- package/framework/hooks/claude-code/core/protect-spec-files.js +41 -4
- package/framework/hooks/claude-code/statusline.sh +11 -11
- package/framework/hooks/git/commit-msg/conventional-commits.sh +33 -33
- package/framework/hooks/git/pre-commit/agents.sh +25 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +64 -64
- package/framework/hooks/git/pre-commit/specs.sh +50 -50
- package/framework/hooks/git/pre-push/run-tests.sh +44 -44
- package/framework/hooks/shared/e2e-check.js +135 -22
- package/framework/hooks/shared/stdin-reader.js +26 -26
- package/framework/rules/ai-agents-standards.md +14 -2
- package/framework/schemas/ai-pin.schema.json +97 -0
- package/framework/schemas/config.schema.json +107 -0
- package/framework/schemas/feature.schema.json +23 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +26 -5
- package/framework/skills/core/morph-brainstorming/SKILL.md +1 -1
- package/framework/skills/core/morph-eval/SKILL.md +1 -1
- package/framework/skills/core/morph-hotfix/SKILL.md +2 -2
- package/framework/skills/core/morph-implement/SKILL.md +18 -2
- package/framework/skills/core/morph-media/SKILL.md +215 -0
- package/framework/skills/core/morph-plan/SKILL.md +33 -2
- package/framework/skills/core/morph-proposal/SKILL.md +1 -1
- package/framework/skills/core/morph-review/SKILL.md +166 -9
- package/framework/skills/core/morph-stack-scan/SKILL.md +1 -1
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +1 -1
- package/framework/standards/PROVENANCE.json +81 -3
- package/framework/standards/STANDARDS.json +1483 -839
- package/framework/standards/ai-agents/agent-archetypes.md +160 -0
- package/framework/standards/ai-agents/agent-session.md +164 -68
- package/framework/standards/ai-agents/agent-spec.md +196 -0
- package/framework/standards/ai-agents/batching.md +61 -37
- package/framework/standards/ai-agents/context-providers.md +97 -24
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +239 -0
- package/framework/standards/ai-agents/cost-and-budget.md +256 -0
- package/framework/standards/ai-agents/durable-workflows-hitl.md +98 -5
- package/framework/standards/ai-agents/evals-with-cache.md +289 -0
- package/framework/standards/ai-agents/guardrails.md +222 -0
- package/framework/standards/ai-agents/llm-runtime-defaults.md +226 -0
- package/framework/standards/ai-agents/mcp-server.md +119 -18
- package/framework/standards/ai-agents/mcp-tools.md +85 -32
- package/framework/standards/ai-agents/media-pipeline.md +352 -0
- package/framework/standards/ai-agents/media-video.md +267 -0
- package/framework/standards/ai-agents/middleware-patterns.md +39 -5
- package/framework/standards/ai-agents/modalities-image-gen.md +354 -105
- package/framework/standards/ai-agents/modalities-speech-to-text.md +8 -2
- package/framework/standards/ai-agents/modalities-text-to-speech.md +10 -2
- package/framework/standards/ai-agents/modalities-vision.md +6 -2
- package/framework/standards/ai-agents/multi-agent-patterns.md +61 -5
- package/framework/standards/ai-agents/observability-patterns.md +227 -17
- package/framework/standards/ai-agents/production.md +38 -9
- package/framework/standards/ai-agents/prompt-sources.md +389 -0
- package/framework/standards/ai-agents/providers/model-registry.md +174 -39
- package/framework/standards/ai-agents/rag-custom-pgvector.md +69 -4
- package/framework/standards/ai-agents/service-tier-flex.md +27 -19
- package/framework/standards/ai-agents/setup.md +166 -50
- package/framework/standards/ai-agents/structured-output.md +34 -3
- package/framework/standards/ai-agents/sweet-spot.md +23 -3
- package/framework/standards/ai-agents/testing-ai.md +236 -0
- package/framework/standards/ai-agents/vector-data-extensions.md +50 -20
- package/framework/standards/ai-agents/workflows.md +27 -7
- package/framework/standards/frontend/design-system/ai-image-generation.md +12 -0
- package/framework/standards/frontend/design-system/ai-video-generation.md +13 -0
- package/framework/standards/infrastructure/docker/local-compose-isolation.md +3 -1
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +13 -0
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -39
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -68
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -117
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +1 -1
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -61
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -107
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -58
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +281 -281
- package/framework/templates/code/dotnet/database/migration.cs +83 -83
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +21 -21
- package/framework/templates/code/dotnet/feature/request.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/response.cs.hbs +4 -4
- package/framework/templates/code/dotnet/feature/validator.cs.hbs +15 -15
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +1 -1
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/code/typescript/contracts.ts +167 -167
- package/framework/templates/docs/clarifications.md +253 -253
- package/framework/templates/docs/schema-analysis.md +119 -119
- package/framework/templates/docs/ui-components.md +52 -52
- package/framework/templates/docs/ui-design-system.md +76 -76
- package/framework/templates/docs/ui-flows.md +167 -167
- package/framework/templates/docs/ui-mockups.md +98 -98
- package/framework/templates/dotnet/ai-kit/Directory.Build.props +25 -0
- package/framework/templates/dotnet/ai-kit/Directory.Packages.props +26 -0
- package/framework/templates/dotnet/ai-kit/Morph.AiKit.sln +56 -0
- package/framework/templates/dotnet/ai-kit/NuGet.config +12 -0
- package/framework/templates/dotnet/ai-kit/README.md +128 -0
- package/framework/templates/dotnet/ai-kit/global.json +9 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentApi.cs +65 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactory.cs +289 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentFactoryOptions.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpec.cs +91 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Agents/AgentSpecOptions.cs +181 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/AiKitAssemblyMarker.cs +14 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/ConversationStore.cs +174 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/IConversationStore.cs +54 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Conversation/InMemoryConversationStore.cs +75 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/CountingChatClient.cs +71 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalAssertion.cs +246 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheMissException.cs +57 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheOptions.cs +78 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalCacheStamp.cs +225 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalFixture.cs +463 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalMode.cs +32 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalQualityEvaluators.cs +49 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalScenario.cs +214 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/EvalSummary.cs +168 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/OfflineChatClient.cs +142 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Evals/ShortPathResponseCache.cs +349 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaGenerator.cs +42 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/IMediaProvider.cs +77 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaArtifact.cs +28 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaDisclosure.cs +107 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaGenerator.cs +253 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaIdempotencyKey.cs +103 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJob.cs +259 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobId.cs +21 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaJobState.cs +47 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaPricing.cs +102 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaProvenance.cs +44 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaRequest.cs +192 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaResult.cs +94 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaReview.cs +99 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaSeams.cs +97 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MediaStore.cs +133 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/MorphMedia.cs +93 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/GoogleImageProvider.cs +112 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/OpenAIImageProvider.cs +288 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Media/Providers/VeoVideoProvider.cs +158 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/IUsageCostSink.cs +29 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostMiddleware.cs +208 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Middleware/UsageCostRecord.cs +95 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Morph.AiKit.csproj +38 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphOpenTelemetryExtensions.cs +119 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Observability/MorphTelemetryOptions.cs +82 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/IProviderClientFactory.cs +33 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelAlias.cs +267 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/ModelRegistry.cs +474 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Providers/model-registry.v2.json +87 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaIngest.cs +169 -0
- package/framework/templates/dotnet/ai-kit/src/Morph.AiKit/Rag/DeltaPlan.cs +49 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentFactoryTests.cs +495 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/AgentSpecTests.cs +260 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ConversationStoreTests.cs +405 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/DeltaIngestTests.cs +379 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalAssertionTests.cs +197 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheMissTests.cs +139 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalCacheStampTests.cs +157 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFailuresTests.cs +178 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalFixtureTests.cs +153 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalRefreshTests.cs +194 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/EvalWorkspace.cs +86 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/KitEvals.cs +213 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Evals/OfflineChatClientTests.cs +95 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClient.cs +225 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeChatClientTests.cs +215 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeClock.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeMediaProvider.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeProviderClientFactory.cs +55 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/FakeUsageCostSink.cs +60 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Fakes/InMemoryMediaStore.cs +131 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAccessibilityTests.cs +133 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaAliasIsNotAnAgentTests.cs +127 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaDisclosureTests.cs +216 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaGeneratorTests.cs +264 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaIdempotencyKeyTests.cs +148 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaJobStateTests.cs +184 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaReviewTests.cs +89 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/MediaStoreTests.cs +98 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/ModelRegistryTests.cs +451 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +76 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/SkeletonSmokeTests.cs +33 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/TelemetryTests.cs +261 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/UsageCostMiddlewareTests.cs +374 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/.morph-eval-cache.json +12 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/orcamento.acme.a-resposta-estruturada/1/CF70A7CE27BBD66A/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.a-saudacao/1/19F631228C6E00BA/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.acme.b-escalonamento/1/A24A7C9BDD8239D8/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/contents.data +13 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-cache/cache/triagem.globex.a-saudacao/1/E38CD37DF003EDB7/entry.json +7 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/orcamento/acme/a-resposta-estruturada.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/a-saudacao.json +23 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/acme/b-escalonamento.json +24 -0
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/eval-datasets/triagem/globex/a-saudacao.json +23 -0
- package/framework/templates/feature/decisions.md +122 -122
- package/framework/templates/frontend/nextjs/Dockerfile.nextjs.hbs +43 -43
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/env.mjs.hbs +32 -32
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/page.tsx.hbs +22 -22
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- package/framework/templates/frontend/nextjs/use-feature.ts.hbs +54 -54
- package/framework/templates/infrastructure/docker/Dockerfile.template +58 -58
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/infrastructure/github/actions/docker-build-push/action.yml.hbs +45 -45
- package/framework/templates/infrastructure/github/actions/health-check/action.yml.hbs +27 -27
- package/framework/templates/infrastructure/github/workflows/docker-build-push.yml.hbs +59 -59
- package/framework/templates/infrastructure/github/workflows/dotnet-build.yml.hbs +39 -39
- package/framework/templates/integrations/asaas-client.cs +387 -387
- package/framework/templates/integrations/asaas-webhook.cs +351 -351
- package/framework/templates/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +95 -95
- package/framework/templates/project/validate.js +120 -120
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- package/package.json +3 -3
- package/src/commands/dashboard/dashboard.js +53 -53
- package/src/commands/e2e/e2e.js +15 -2
- package/src/commands/index.js +13 -13
- package/src/commands/project/checkpoint.js +209 -209
- package/src/commands/project/doctor.js +140 -5
- package/src/commands/project/update.js +469 -153
- package/src/commands/project/worktree.js +8 -1
- package/src/commands/state/advance.js +24 -2
- package/src/commands/state/approve.js +33 -1
- package/src/commands/state/create.js +72 -11
- package/src/commands/state/finish.js +333 -21
- package/src/commands/state/gate-check.js +190 -6
- package/src/commands/state/score.js +36 -1
- package/src/commands/validation/verify.js +134 -25
- package/src/core/state/phase-state-machine.js +197 -197
- package/src/core/state/state-manager.js +9 -1
- package/src/core/templates/template-renderer.js +477 -477
- package/src/dashboard/app/components/DetailPanel.jsx +11 -11
- package/src/dashboard/app/components/FlowGraph.jsx +17 -17
- package/src/dashboard/app/components/SearchFilter.jsx +22 -22
- package/src/dashboard/app/hooks/useDetailPanel.js +15 -15
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +370 -370
- package/src/dashboard/app/pages/PhasePipeline.jsx +408 -408
- package/src/dashboard/app/pages/RulesMap.jsx +273 -273
- package/src/dashboard/app/pages/SkillsLibrary.jsx +366 -366
- package/src/dashboard/app/pages/Standards.jsx +332 -332
- package/src/dashboard/app/styles/index.css +47 -47
- package/src/dashboard/index.html +12 -12
- package/src/dashboard/package.json +24 -24
- package/src/dashboard/scanner/phases-scanner.js +15 -15
- package/src/dashboard/scanner/rubrics-scanner.js +12 -12
- package/src/dashboard/scanner/rules-scanner.js +37 -37
- package/src/dashboard/scanner/skills-scanner.js +43 -43
- package/src/dashboard/scanner/squads-scanner.js +13 -13
- package/src/dashboard/vite-plugin-morph-data.js +17 -17
- package/src/dashboard/vite.config.js +24 -24
- package/src/lib/ai/ai-checks.js +182 -0
- package/src/lib/ai/ai-project-scan.js +347 -0
- package/src/lib/ai/code-audit.js +181 -0
- package/src/lib/ai/comment-stripper.js +184 -0
- package/src/lib/ai/pin-drift.js +138 -0
- package/src/lib/ai/registry-audit.js +285 -0
- package/src/lib/detectors/design-system-detector.js +188 -188
- package/src/lib/detectors/index.js +217 -217
- package/src/lib/e2e/compose-env.js +50 -4
- package/src/lib/evaluator-report.js +269 -15
- package/src/lib/generators/design-system-generator.js +508 -508
- package/src/lib/generators/settings-generator.js +209 -209
- package/src/lib/git/default-branch.js +92 -4
- package/src/lib/hooks/hook-executor.js +167 -167
- package/src/lib/installers/effects.js +193 -0
- package/src/lib/installers/mcp-installer.js +719 -719
- package/src/lib/installers/templates-reconcile.js +3 -3
- package/src/lib/risk-detector.js +284 -0
- package/src/lib/standards/verified-against.js +276 -0
- package/src/lib/tasks/eval-node.js +485 -0
- package/src/lib/tasks/linux-runner.js +297 -0
- package/src/lib/tasks/task-classifier.js +30 -30
- package/src/lib/tasks/task-parser.js +209 -209
- package/src/lib/tasks/test-nodes.js +486 -0
- package/src/lib/tasks/test-runner.js +410 -33
- package/src/lib/validators/checkpoint-persistence.js +39 -39
- package/src/lib/validators/css/css-validator.js +340 -340
- package/src/lib/validators/shared/emit-validator-dispatch.js +14 -14
- package/src/lib/validators/shared/issue-counter.js +14 -14
- package/src/lib/validators/verify-runner.js +583 -28
- package/src/lib/worktree/docker-audit.js +287 -0
- package/src/lib/worktree/env-allocator.js +24 -0
- package/src/lib/worktree/worktree-audit.js +30 -1
- package/src/scripts/setup-infra.js +24 -11
- package/src/utils/agent-id-mapper.js +11 -11
- package/src/utils/agents-installer.js +18 -8
- package/src/utils/claude-md-injector.js +10 -5
- package/src/utils/claude-settings-manager.js +300 -300
- package/src/utils/file-copier.js +11 -4
- package/src/utils/hooks-installer.js +29 -26
- package/src/utils/skills-installer.js +36 -29
- package/src/utils/version-checker.js +68 -3
- package/framework/standards/ai-agents/prompts-in-db.md +0 -214
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Prova: Guard Cego
|
|
2
|
+
|
|
3
|
+
> Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
|
|
4
|
+
> Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").
|
|
5
|
+
|
|
6
|
+
## O que esta prova responde
|
|
7
|
+
|
|
8
|
+
Um guard só vale contra o ataque que ele consegue VER. O jeito de descobrir o que ele não vê não é
|
|
9
|
+
mudar o conteúdo que ele já inspeciona — é mudar a **FORMA** do que chega até ele.
|
|
10
|
+
|
|
11
|
+
A pergunta é: **qual entrada válida este guard deixa passar sem enxergar, e o ramo que ele abriu
|
|
12
|
+
tem teste próprio?**
|
|
13
|
+
|
|
14
|
+
## Bandas 0-10
|
|
15
|
+
|
|
16
|
+
| 0-3 | 4-6 | 7-8 | 9-10 |
|
|
17
|
+
|---|---|---|---|
|
|
18
|
+
| Guard sem teste próprio. Ou testado só com o conteúdo que ele já rejeita, nunca com forma diferente | Ataque por conteúdo apenas. Guard que compara `Serialize(A) == Serialize(B)` aceito sem cross-check | Ataque por forma feito; falta o cross-check por contador independente, ou o predicado do guard continua mais estreito que a ação que ele governa | Forma atacada (multi-linha, aninhamento, ordem, codificação), cross-check por contador independente, predicado tão largo quanto a ação, e todo ramo recém-alcançável com teste próprio |
|
|
19
|
+
|
|
20
|
+
## Anti-padrões literais
|
|
21
|
+
|
|
22
|
+
- **Atacar o guard só pelo conteúdo** — mude a **FORMA**. Medido: o guard era um parser linha a
|
|
23
|
+
linha e o YAML **multi-linha** era perfeitamente válido; o conteúdo proibido passou inteiro
|
|
24
|
+
porque chegou numa forma que o parser não montava.
|
|
25
|
+
- **`Serialize(A) == Serialize(B)` como prova de igualdade** — o guard é cego ao próprio
|
|
26
|
+
serializador: se ele perde um campo, perde nos dois lados e a comparação dá igual. Faça
|
|
27
|
+
**cross-check por um contador independente** (número de itens, soma, hash de um campo-chave).
|
|
28
|
+
- **Predicado mais estreito que a ação que ele governa** — um publish que só olhava o *spec* e
|
|
29
|
+
virava no-op sobre tudo o mais. Afirme sobre o **texto publicado**, não sobre a intenção de
|
|
30
|
+
publicar.
|
|
31
|
+
- **Early-return depois do filtro** — o `return` sai antes da limpeza e o estado fica meio mutado.
|
|
32
|
+
Quando o filtro é um `skip`, transforme-o em `else`: o caminho de saída precisa passar pelo
|
|
33
|
+
mesmo fecho.
|
|
34
|
+
- **Ramo antes inalcançável que passou a ser alcançável** — isso é **código NOVO**, ainda que a
|
|
35
|
+
linha seja antiga e o diff não a toque. Ele precisa de teste próprio; nenhuma cobertura anterior
|
|
36
|
+
responde por ele.
|
|
37
|
+
- **Guard novo sem prova de que morde** — canário que não morde é canário errado, e canário que
|
|
38
|
+
morde pelo motivo errado é pior: mute a produção, exija que o teste FALHE **nomeando o que
|
|
39
|
+
quebrou**, e restaure por cópia de bytes feita antes.
|
|
40
|
+
|
|
41
|
+
## Como registrar no relatório
|
|
42
|
+
|
|
43
|
+
Uma linha por guard tocado, dizendo a forma atacada e o resultado:
|
|
44
|
+
|
|
45
|
+
```markdown
|
|
46
|
+
- `chain.js:blockRawWorktreeAdd` — atacado por FORMA: comando com `\` de continuação em duas linhas
|
|
47
|
+
e com aspas simples. Bloqueou nas duas. Cross-check: contador de comandos bloqueados subiu 2.
|
|
48
|
+
- Ramo `options.discardIgnored === true` era inalcançável antes de T4 — agora é alcançável e ganhou
|
|
49
|
+
teste próprio (`finish.test.js:412`).
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
E no `**Proof-Scores:**`, a chave `guard`.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Prova: Media Claims
|
|
2
|
+
|
|
3
|
+
> Escopo: Gate 3 (avaliador independente), **somente** em feature que gera mídia. Não é dimensão
|
|
4
|
+
> do score composto — o composto continua `arquitetura + contratos + qualidade + testes`. Esta prova
|
|
5
|
+
> impõe **teto** em Arquitetura (tabela em `evals/rubrics/architecture.md`, seção "Tetos de prova de
|
|
6
|
+
> mídia").
|
|
7
|
+
|
|
8
|
+
## O que esta prova responde
|
|
9
|
+
|
|
10
|
+
Uma feature que gera imagem ou vídeo faz **quatro afirmações** que custam caro quando são falsas, e
|
|
11
|
+
as quatro são fáceis de escrever e difíceis de provar: *controlamos o custo*, *tem revisão humana*,
|
|
12
|
+
*a mídia é marcada*, *o hot path não bloqueia*.
|
|
13
|
+
|
|
14
|
+
A pergunta desta rubrica é sempre a mesma: **onde está o código que torna a afirmação verdadeira,
|
|
15
|
+
e o que acontece se alguém tentar burlá-la?**
|
|
16
|
+
|
|
17
|
+
Ela vive em Arquitetura, e não em Cobertura, porque as quatro são **propriedades estruturais** —
|
|
18
|
+
onde a chamada mora, que estados existem, o que é persistido, o que é publicado. Uma feature que
|
|
19
|
+
gera mídia dentro do turno de WhatsApp está arquiteturalmente errada, não mal testada; contá-la como
|
|
20
|
+
falha de teste diria a coisa errada a quem lê o relatório.
|
|
21
|
+
|
|
22
|
+
As quatro afirmações, com o que conta e o que não conta:
|
|
23
|
+
|
|
24
|
+
| Afirmação | Evidência que conta | Evidência que **não** conta |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| **Custo** | Custo estimado gravado **antes** da chamada, com a tarifa e a data da tabela de preços; teto por tenant/dia exercitado em teste, com o caso que ESTOURA | "usamos o modelo mais barato"; uma constante de preço sem fonte datada |
|
|
27
|
+
| **Revisão humana** | Teste que prova que a publicação falha a partir de **todo** estado que não seja aprovado, e que aprovar carimba quem e quando | Um comentário dizendo que há revisão; um campo booleano `approved` |
|
|
28
|
+
| **Marcação** | A publicação **recusa** quando a finalidade exige rótulo e ele não veio — com texto e superfície nomeados, gravados junto do artefato — mais a proveniência persistida e o C2PA preservado sem re-encode. No exemplar: `MediaJob.Publish(MediaDisclosure?, at)`, terceira porta | "o SynthID vem embutido" — vem, e cai no primeiro re-encode ou screenshot; um campo de rótulo que a publicação aceita vazio; a obrigação escrita só no comentário do estado `Published` |
|
|
29
|
+
| **Hot path não bloqueado** | O tipo que produz bytes é **inacessível** do handler (teste de acessibilidade por reflexão) **e** o handler de mensagem devolve id de job | "o handler chama o serviço assíncrono"; um `Task.Run` |
|
|
30
|
+
|
|
31
|
+
O conhecimento por trás das quatro está em `.morph/framework/standards/ai-agents/media-pipeline.md`.
|
|
32
|
+
Esta rubrica não o repete: ela cobra a prova.
|
|
33
|
+
|
|
34
|
+
## Bandas 0-10
|
|
35
|
+
|
|
36
|
+
| 0-3 | 4-6 | 7-8 | 9-10 |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| Nenhuma das quatro afirmações tem código apontado. Ou a geração acontece dentro do turno de conversa, ou a publicação é alcançável sem aprovação humana — qualquer um dos dois é **bloqueante**, independentemente do resto | As quatro estão implementadas, mas a prova é leitura de código: nenhum teste exercita o caminho de recusa. Teto de custo declarado e nunca estourado em teste; marcação prometida na prosa e ausente no ponto de publicação | As quatro provadas por teste, com o caminho de recusa exercitado ao menos uma vez cada. Falta a mutação: nenhuma delas foi vista MORDENDO, ou a proveniência grava menos campos do que o standard nomeia | As quatro provadas, cada caminho de recusa com teste próprio, **e a acessibilidade do tipo que produz bytes asserida por reflexão com a mutação rodada** (tornar público, ver o teste falhar nomeando o defeito, restaurar por bytes e conferir por md5). Custo estimado antes da chamada, com tarifa datada; teto exercitado no caso que estoura. A marcação é **parâmetro da transição de publicação**, não campo opcional: existe o teste que publica sem rótulo e vê a recusa |
|
|
39
|
+
|
|
40
|
+
## Anti-padrões literais
|
|
41
|
+
|
|
42
|
+
- **Prender a regra dura só na prosa.** "O standard diz para não gerar no turno" não impede nada. O
|
|
43
|
+
que impede é a acessibilidade do tipo: se um handler consegue **nomear** o tipo que produz bytes,
|
|
44
|
+
a regra não está presa — mesmo que ninguém a tenha violado ainda. (memória: é a diferença entre
|
|
45
|
+
"o standard diz para não fazer" e "o compilador recusa")
|
|
46
|
+
- **Testar a fachada em vez do dono do estado.** Se `Review.Publish(job)` reimplementa a regra em
|
|
47
|
+
vez de delegar a `job.Publish()`, o teste da fachada mede uma **cópia** do objeto — e as duas
|
|
48
|
+
divergem no primeiro conserto. Exercite as duas contra o MESMO job e compare.
|
|
49
|
+
- **Confundir a chave de idempotência do PEDIDO com o hash do CONTEÚDO.** Uma dedupe chamada paga, a
|
|
50
|
+
outra dedupe storage. Um teste que só confere "o hash é o mesmo" **não distingue dedupe de
|
|
51
|
+
regravação**: as duas produzem o mesmo hash. Conte as gravações.
|
|
52
|
+
- **Aceitar `estimatedCostUsd` gravado DEPOIS da chamada.** Isso é contabilidade, não controle: o
|
|
53
|
+
dinheiro já saiu. A prova é a ordem, e a ordem é verificável.
|
|
54
|
+
- **Preço em código sem fonte datada.** Uma constante `0.211m` sem a página e a data de consulta é
|
|
55
|
+
folclore com força de orçamento. A tarifa vem da tabela do standard da modalidade.
|
|
56
|
+
- **Tratar bloqueio de moderação como erro técnico.** Se o estado de bloqueio for retryable, um laço
|
|
57
|
+
de retry vira uma fatura — e no provedor que cobra o bloqueio, cobra duas vezes. Exija dois
|
|
58
|
+
estados distintos e um teste por estado.
|
|
59
|
+
- **`not_detected` lido como "feito por humano".** Ausência de marca é ausência de informação. Um
|
|
60
|
+
verificador de proveniência que devolve "não detectado" não prova origem humana.
|
|
61
|
+
- **Marcação delegada ao provedor.** C2PA e SynthID vêm embutidos e **caem** com screenshot ou
|
|
62
|
+
re-encode. A marcação visível é obrigação separada, e ela mora no ponto de publicação — não no de
|
|
63
|
+
geração.
|
|
64
|
+
- **Marcação presa só por prosa.** É o anti-padrão mais fácil de cometer porque parece resolvido: o
|
|
65
|
+
comentário do estado `Published` diz "com a marcação aplicada", a finalidade `Marketing` diz
|
|
66
|
+
"marcação visível obrigatória", e um `grep` por rótulo, watermark ou disclosure no código volta
|
|
67
|
+
**vazio**. Se as outras três afirmações estão presas por tipo ou por estado e a marcação — que é a
|
|
68
|
+
obrigação legal — está presa por nada, a assimetria é o achado. Prova mínima: a publicação recusa
|
|
69
|
+
sem rótulo.
|
|
70
|
+
- **Ler "o tipo prende" como "nada mais é possível".** O `internal` fecha *obter bytes* e *nomear o
|
|
71
|
+
produtor*; ele **não** fecha *aguardar inline*. O gatilho do worker (`IMediaJobRunner.RunAsync` no
|
|
72
|
+
exemplar) é público por necessidade — quem o dispara é o `BackgroundService` do projeto — e um
|
|
73
|
+
`await runner.RunAsync(id)` dentro de um handler de turno **compila** e paga a latência inteira.
|
|
74
|
+
Procure por esse `await`: é o que o compilador não procura. Uma prova que só cita a acessibilidade
|
|
75
|
+
e não olha os call sites do runner cobriu metade da afirmação.
|
|
76
|
+
- **Teste que toca API paga.** Uma chave de API num teste é uma fatura por execução de CI e um teste
|
|
77
|
+
que fica vermelho quando o provedor tem incidente. `grep` por chave, host do provedor e leitura de
|
|
78
|
+
ambiente nos arquivos de teste tem de voltar vazio.
|
|
79
|
+
- **Aceitar o número de testes sem a suíte inteira.** "Derruba 4" medido sobre um arquivo e "derruba
|
|
80
|
+
4 de 203" são afirmações diferentes. Só a suíte inteira autoriza a frase.
|
|
81
|
+
- **Declarar as quatro afirmações numa feature que não gera mídia.** A isenção é automática: se
|
|
82
|
+
nenhuma task pina um standard de mídia, a seção não é exigida. Escrever a seção "por precaução"
|
|
83
|
+
gasta o Gate 3 com cerimônia vazia, que é como uma regra vira ruído e depois é desligada.
|
|
84
|
+
|
|
85
|
+
## Como registrar no relatório
|
|
86
|
+
|
|
87
|
+
Na seção `## Mídia gerada` do `evaluator-report.md`, uma linha por afirmação, com os quatro campos:
|
|
88
|
+
|
|
89
|
+
```markdown
|
|
90
|
+
## Mídia gerada
|
|
91
|
+
|
|
92
|
+
| Afirmação | Onde está presa (arquivo:linha) | Teste que a prova | Rodada de mutação |
|
|
93
|
+
|---|---|---|---|
|
|
94
|
+
| Custo | src/…/MediaGenerator.cs:88 (reserva ANTES da chamada) | MediaGeneratorTests.ABudgetOverrun… | — |
|
|
95
|
+
| Revisão humana | src/…/MediaJob.cs:… (Publish só de Approved) | MediaJobStateTests.PublishFails… (6 estados) | — |
|
|
96
|
+
| Marcação | src/…/MediaJob.cs:… (3ª porta de Publish) + MediaDisclosure.cs | MediaDisclosureTests.PublishFailsWithoutADisclosure… | porta removida → 2 de 220 caem, motivo lido, md5 restaurado |
|
|
97
|
+
| Hot path | src/…/IMediaProvider.cs (internal) | MediaAccessibilityTests.TheTypeThat… | público → 4 de 220 caem, motivo lido, md5 restaurado |
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
E a linha machine-readable, no mesmo estilo do `**Composite-Score:**`, junto das demais provas:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
**Proof-Scores:** mutation=9 verde=8 fiacao=9 guard=7 media=9
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**A chave `media` só entra quando a feature gera mídia.** Feature que não pina nenhum standard de
|
|
107
|
+
mídia não declara a seção e não declara a chave — e o gate não a cobra. Feature que gera mídia **sem
|
|
108
|
+
a seção** pausa o Gate 3 por conta própria (`src/lib/risk-detector.js`), antes de qualquer discussão
|
|
109
|
+
de nota.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Prova: Mutação
|
|
2
|
+
|
|
3
|
+
> Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — o composto continua
|
|
4
|
+
> `arquitetura + contratos + qualidade + testes`. Esta prova impõe **teto** em Cobertura de Testes
|
|
5
|
+
> (tabela em `evals/rubrics/test-coverage.md`, seção "Tetos de prova").
|
|
6
|
+
|
|
7
|
+
## O que esta prova responde
|
|
8
|
+
|
|
9
|
+
Um teste verde prova que o código roda. Só a **mutação** prova que o teste MORDE: quebre de
|
|
10
|
+
propósito a linha que carrega a decisão e veja quem grita. Se ninguém grita, a suíte é decoração —
|
|
11
|
+
e foi assim que 11 testes verdes cobriram um artefato que o build recusava.
|
|
12
|
+
|
|
13
|
+
A pergunta desta rubrica é sempre a mesma: **você mutou o call site que carrega a decisão, rodou a
|
|
14
|
+
suíte inteira, leu o MOTIVO da falha, e devolveu o arquivo byte a byte?**
|
|
15
|
+
|
|
16
|
+
## Bandas 0-10
|
|
17
|
+
|
|
18
|
+
| 0-3 | 4-6 | 7-8 | 9-10 |
|
|
19
|
+
|---|---|---|---|
|
|
20
|
+
| Nenhuma mutação. Ou mutação declarada sem diff, sem md5 e sem testes nomeados — afirmação, não prova. Ou revert por `git checkout --`, que descarta trabalho não commitado de terceiros | Mutou, mas no lugar errado (o `if` refatorado, não o call site que decide) ou com filtro estreito ("derrubou 3" medido sobre 40 testes de um arquivo só). Reportou md5 sem ler o motivo da falha | Mutação no call site certo, suíte inteira, diff e md5 antes/depois. Falta o MOTIVO literal da falha, ou uma cláusula de um predicado composto ficou sem rodada própria | Uma rodada por cláusula, cada uma com arquivo:linha, diff, md5 antes/depois, contagem sobre a suíte INTEIRA e a mensagem literal de falha. Revert por snapshot de bytes + `os.utime`, com `git status --porcelain src/` vazio ao fim |
|
|
21
|
+
|
|
22
|
+
## Anti-padrões literais
|
|
23
|
+
|
|
24
|
+
- **Mutar o `if` refatorado em vez do call site que carrega a decisão** — o extract-method moveu a
|
|
25
|
+
regra; o teste continua batendo no invólucro. Ancore onde o valor é DECIDIDO. (memória: guard do
|
|
26
|
+
remédio, cuja checagem real vivia duas camadas acima do `if` mutado)
|
|
27
|
+
- **Rodar com filtro e reportar "derruba N"** — "3 de 40" e "3 de 2684" são afirmações diferentes.
|
|
28
|
+
Só a suíte **inteira** autoriza a frase "derruba N". (memória: F30, filtro no Gate 3)
|
|
29
|
+
- **Filtro tão estreito que a prova fica vácua** — mutar e rodar só o teste que você já sabia que
|
|
30
|
+
quebraria não testa nada além da sua própria expectativa.
|
|
31
|
+
- **Aceitar um seed sem perguntar "que sequência real produz este seed?"** — fixture que só existe
|
|
32
|
+
no teste prova o teste, não o sistema.
|
|
33
|
+
- **Imprimir `diff` + md5 e parar por aí** — md5 prova que ALGO mudou, nunca O QUÊ. Sem a mensagem
|
|
34
|
+
literal de falha (`expected 3, got 0` em `x.test.js:88`), a prova é de que o arquivo mudou, não de
|
|
35
|
+
que o teste morde a regra. (memória: três rodadas de correção nesta epic)
|
|
36
|
+
- **Reverter por replace reverso, ou por `git checkout --`** — o replace reverso erra em arquivo com
|
|
37
|
+
ocorrências repetidas; o `git checkout --` **apaga alterações não commitadas de outra pessoa** e
|
|
38
|
+
aconteceu de verdade num Gate 3 desta epic. Reverta por **snapshot/restore de bytes** (cópia feita
|
|
39
|
+
ANTES) + `os.utime` para devolver o mtime, e confirme por `md5sum`.
|
|
40
|
+
- **Escrever a mutação por heredoc do shell quando ela contém barra invertida** — o transporte come
|
|
41
|
+
o escape: `\b` vira o byte `0x08`, `\n` vira quebra de linha real, e o regex mutado **não é o que
|
|
42
|
+
você escreveu**. Sintoma: a mutação parece aplicada e o resultado não muda, ou o arquivo quebra
|
|
43
|
+
num ponto que você não tocou. Diagnóstico em um comando: `cat -A` mostra `^H` onde devia estar
|
|
44
|
+
`\b`. Saída: patch por **índice de linha** com o conteúdo montado sem barra invertida no fonte
|
|
45
|
+
(`String.fromCharCode(92)`), ou ferramenta de edição estrutural — nunca heredoc.
|
|
46
|
+
(memória: **três agentes** desta epic tropeçaram nisto; dois descartaram o resultado, um só
|
|
47
|
+
descobriu ao conferir os bytes)
|
|
48
|
+
- **Mutar antes de commitar o fix** — se a sessão morrer no meio, o que sobrevive é a mutação, não a
|
|
49
|
+
correção. **Commite o fix ANTES de mutar**, sempre.
|
|
50
|
+
- **Ler mais do que a primeira assertiva que falha** — a mutação só prova até ali; o resto do teste
|
|
51
|
+
nem rodou. Não conte cobertura que a mutação não exercitou.
|
|
52
|
+
- **Uma rodada só para um predicado `A && B`** — derrubar `A` não diz nada sobre `B`. **Uma rodada
|
|
53
|
+
por cláusula.** O mesmo vale para um guard de duas classes: toque **cada** classe.
|
|
54
|
+
- **Deixar loader, validador e superfície de publicação fora do alvo** — o defeito costuma estar em
|
|
55
|
+
quem LÊ o arquivo, não em quem o escreve. (memória: 11 testes verdes contra o render do próprio
|
|
56
|
+
gerador, nenhum deles CARREGANDO o artefato)
|
|
57
|
+
- **Fixture fora da ordem de grandeza da tese** — tese sobre 2 600 testes provada num fixture de 3.
|
|
58
|
+
- **Mutação de prosa presa por regex** — texto de skill/rubrica não se prende por `assert.match` de
|
|
59
|
+
vocabulário: mude a FORMA (mova a seção, troque o cabeçalho) e veja se o canário ainda morde.
|
|
60
|
+
- **Número de mutação sem SHA e sem data** — "derruba 12" envelhece em uma semana. Anote o SHA
|
|
61
|
+
revisado e a data ao lado do número.
|
|
62
|
+
- **Terminar sem `git status --porcelain src/` vazio** — mutação esquecida em produção é o pior
|
|
63
|
+
desfecho possível deste método, e já aconteceu com avaliador morto no meio da rodada.
|
|
64
|
+
|
|
65
|
+
## Como registrar no relatório
|
|
66
|
+
|
|
67
|
+
Na seção `## Mutações` do `evaluator-report.md`, uma linha por mutação, com os cinco campos:
|
|
68
|
+
|
|
69
|
+
```markdown
|
|
70
|
+
| # | Arquivo:linha | Mutação (diff) | md5 antes | md5 depois | Testes derrubados | Motivo da falha |
|
|
71
|
+
|---|---|---|---|---|---|---|
|
|
72
|
+
| M1 | src/lib/x.js:42 | `- if (a && b)` / `+ if (a)` | abc… | def… | 3 de 2684 | `expected 3 got 0` em x.test.js:88 |
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
E, ao fim da seção: **Reversão:** snapshot/restore de bytes + `os.utime`; `git status --porcelain
|
|
76
|
+
src/` vazio. Feature sem código de produção declara a seção com a linha
|
|
77
|
+
`não aplicável — feature sem código de produção`.
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Prova: Observabilidade GenAI
|
|
2
|
+
|
|
3
|
+
> Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
|
|
4
|
+
> Qualidade de Código (`evals/rubrics/code-quality.md`, seção "Teto de prova de observabilidade
|
|
5
|
+
> GenAI"). Aplica-se **somente** a feature que chama LLM; feature sem LLM é `not_evaluated`, e
|
|
6
|
+
> `not_evaluated` **não** aplica teto.
|
|
7
|
+
|
|
8
|
+
## O que esta prova responde
|
|
9
|
+
|
|
10
|
+
Uma feature que chama um modelo gasta dinheiro, pode falhar de formas que nenhum teste cobre, e
|
|
11
|
+
produz texto que ninguém revisou. A pergunta que importa depois do incidente é sempre a mesma, e
|
|
12
|
+
raramente tem resposta:
|
|
13
|
+
|
|
14
|
+
**Dá para reconstruir o que foi ao provedor, e o que ele respondeu, neste turno?**
|
|
15
|
+
|
|
16
|
+
Repare no que a pergunta **não** é. Ela não é "tem span OpenTelemetry?". Essa formulação foi medida
|
|
17
|
+
contra o acervo e reprova **5 de 5** repositórios — e, pior, daria a mesma nota a um projeto cuja
|
|
18
|
+
observabilidade inteira é a mensagem da exceção e a outro que tem hash por bloco de prompt, vocabulário
|
|
19
|
+
fechado de desfecho de tool, veredito ternário de guard, sete testes provando que a telemetria é
|
|
20
|
+
inerte, e a invariante escrita de que o texto enviado é derivado sem cache *"para a telemetria não
|
|
21
|
+
poder mentir"*. **Régua que pune o melhor aluno não é régua.**
|
|
22
|
+
|
|
23
|
+
Span com as convenções GenAI é a forma **recomendada** (`ai-agents-observability-patterns`) e alcança
|
|
24
|
+
a faixa mais alta. Telemetria própria também alcança — desde que exista **teste de fidelidade**, isto
|
|
25
|
+
é, algo que prove que o registrado é o enviado. O que a rubrica cobra é o **resultado**; o standard é
|
|
26
|
+
quem recomenda o meio.
|
|
27
|
+
|
|
28
|
+
### Aplicabilidade — filtro mecânico, e depois uma determinação nomeada
|
|
29
|
+
|
|
30
|
+
**Esta rubrica NÃO se declara determinística, e a razão é medida:** nenhuma leitura de `tasks.json`
|
|
31
|
+
ou do `mandate.md` mede a propriedade que importa — *esta feature entrega código que chama um
|
|
32
|
+
modelo?*. A versão anterior deste bloco prometia determinismo e **errou no primeiro cliente que
|
|
33
|
+
encontrou**: a própria feature que criou esta rubrica satisfazia as duas condições (tasks com
|
|
34
|
+
`group: "tests"`, mandate citando `ai-agents` dez vezes) e não chama LLM nenhum.
|
|
35
|
+
|
|
36
|
+
São **duas etapas**, e a segunda não é opcional:
|
|
37
|
+
|
|
38
|
+
**1. Filtro mecânico, de alta recall** (barato, lido do disco):
|
|
39
|
+
|
|
40
|
+
> o `mandate.md` cita ao menos um standard `ai-agents`.
|
|
41
|
+
|
|
42
|
+
Falso ⇒ `not_evaluated`, sem mais discussão. É deliberadamente **largo**: erra para o lado de
|
|
43
|
+
convocar a determinação, não para o de dispensá-la.
|
|
44
|
+
|
|
45
|
+
**2. Determinação, escrita pelo avaliador.** Passado o filtro, a pergunta é
|
|
46
|
+
*a feature entrega código que chama um modelo?* — e o relatório **registra a resposta com o motivo**:
|
|
47
|
+
|
|
48
|
+
```markdown
|
|
49
|
+
- Aplicabilidade: LLM SIM — T4 acrescenta `TriagemAgent` sobre `IChatClient` (`AgentCatalog.cs:31`).
|
|
50
|
+
- Aplicabilidade: `not_evaluated` — o mandate cita `ai-agents`, mas o diff é 100% markdown de
|
|
51
|
+
standard; nenhuma chamada a modelo entra no produto.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Sinal auxiliar, não determinação:** toda task com `group` em `{docs, tests}` é indício **contra**
|
|
55
|
+
código de produção que chama modelo. Indício, porque um harness de agente pode viver inteiro numa
|
|
56
|
+
task `tests` — por isso ele informa a determinação em vez de substituí-la.
|
|
57
|
+
|
|
58
|
+
**Um `not_evaluated` sem motivo escrito não é dispensa: é o escape que o standard
|
|
59
|
+
`ai-agents-guardrails` proíbe.** O motivo é o que torna a decisão auditável na revisão seguinte, e é o que impede que
|
|
60
|
+
`not_evaluated` vire a saída fácil.
|
|
61
|
+
|
|
62
|
+
## Bandas 0-10
|
|
63
|
+
|
|
64
|
+
| 0-3 | 4-6 | 7-8 | 9-10 |
|
|
65
|
+
|---|---|---|---|
|
|
66
|
+
| A feature chama LLM e **nada** permite saber o que foi enviado, quanto custou, ou se falhou. Observabilidade é a mensagem da exceção | Chamada de LLM registrada **só por log de texto**: sem contagem de tokens, sem correlação com a feature ou a requisição, sem forma de amarrar o registro ao turno | Instrumentação presente e correta — mas a evidência no relatório é a **linha de wiring**, não a saída. O avaliador leu o `AddSource`, não leu um span | Span por chamada com `gen_ai.operation.name` e `gen_ai.provider.name`, tokens registrados, correlação com a feature, e sensível desligado fora de dev — **ou** telemetria própria equivalente cuja fidelidade tem teste. Nos dois casos, o relatório **mostra um span ou registro capturado** |
|
|
67
|
+
|
|
68
|
+
A fronteira que decide 7-8 × 9-10 é uma só, e é a mesma de toda rubrica desta classe: **evidência
|
|
69
|
+
capturada, não evidência declarada.** Ler o código que instrumenta prova que alguém escreveu o
|
|
70
|
+
wiring; ler um span prova que ele emite.
|
|
71
|
+
|
|
72
|
+
## Anti-padrões literais
|
|
73
|
+
|
|
74
|
+
- **Pontuar o wiring como se fosse a saída** — `builder.AddSource(...)` no diff é 7-8, nunca 9-10.
|
|
75
|
+
Para subir, capture: rode o caminho, pegue **um** span ou **uma** linha de registro, e cole no
|
|
76
|
+
relatório com os atributos que ela carrega.
|
|
77
|
+
- **Aceitar "temos logs"** — log de texto sem tokens e sem correlação é banda 4-6. A pergunta
|
|
78
|
+
operacional é "quanto custou este turno e o que foi enviado nele"; um log que não responde nenhuma
|
|
79
|
+
das duas não é observabilidade, é rastro.
|
|
80
|
+
- **Reprovar telemetria própria por não ser OpenTelemetry** — a rubrica cobra a propriedade. Um
|
|
81
|
+
registro próprio com **teste de fidelidade** vale 9-10. Sem teste de fidelidade, não vale: é uma
|
|
82
|
+
afirmação sobre o que o código faz, feita pelo mesmo código.
|
|
83
|
+
- **Aceitar hash sem a invariante** — um hash do prompt só prova algo se o texto de que ele é hash
|
|
84
|
+
**é** o texto enviado. Se existe cache entre a montagem e o envio, telemetria e requisição podem
|
|
85
|
+
divergir em silêncio. Procure a derivação sem cache; sem ela, o hash é decoração e a nota não passa
|
|
86
|
+
de 6.
|
|
87
|
+
- **Prompt cru em produção contado como ponto a favor** — é o oposto: dado sensível persistido é
|
|
88
|
+
achado, não evidência. Cru só em dev ou harness, com opt-in; em produção, hash.
|
|
89
|
+
- **Confundir "não tem LLM" com "tem e não instrumentou"** — o primeiro é `not_evaluated` e **não**
|
|
90
|
+
aplica teto; o segundo é 0-3. Colapsar os dois num booleano é o mesmo defeito que o veredito
|
|
91
|
+
ternário de `ai-agents-guardrails` existe para impedir.
|
|
92
|
+
- **Aplicar teto sem ter avaliado** — sem a linha da chave `obs` no `**Proof-Scores:**` não há teto a
|
|
93
|
+
aplicar. Um teto inventado a partir de impressão é pior que teto nenhum.
|
|
94
|
+
- **Pontuar telemetria de turno cancelado como cobertura** — registro de algo que não aconteceu é
|
|
95
|
+
achado, não evidência.
|
|
96
|
+
|
|
97
|
+
## Como registrar no relatório
|
|
98
|
+
|
|
99
|
+
Uma linha dizendo o que foi capturado e como, e — quando a nota for 9-10 — o artefato capturado:
|
|
100
|
+
|
|
101
|
+
```markdown
|
|
102
|
+
- Aplicabilidade: filtro passou (mandate cita `ai-agents-cost-and-budget`); determinação: LLM SIM — T4 acrescenta `TriagemAgent` sobre `IChatClient` (`AgentCatalog.cs:31`).
|
|
103
|
+
- Capturado 1 span de `Morph.Agents.Triagem` rodando o caminho real com o fake:
|
|
104
|
+
`chat gpt-5-mini` · gen_ai.operation.name=chat · gen_ai.provider.name=openai ·
|
|
105
|
+
gen_ai.usage.input_tokens=1841 · output_tokens=212 · morph.feature=triagem-lead
|
|
106
|
+
- Fidelidade: `PromptDecompositionTests.TextoEnviadoEhAConcatenacaoDosBlocos` prova que o hash
|
|
107
|
+
registrado é o do texto enviado (derivação sem cache).
|
|
108
|
+
- EnableSensitiveData=false confirmado fora de dev (`appsettings.Production.json`).
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
E no `**Proof-Scores:**`, a chave `obs`.
|
|
112
|
+
|
|
113
|
+
Quando não se aplica, uma linha basta — e ela precisa dizer **o que** foi determinado, e por quê. Um `not_evaluated` mudo não é dispensa:
|
|
114
|
+
|
|
115
|
+
```markdown
|
|
116
|
+
- Observabilidade GenAI: `not_evaluated` — o filtro passou (o mandate cita `ai-agents`), mas o
|
|
117
|
+
diff é 100% markdown de standard: nenhuma chamada a modelo entra no produto. Sem teto sobre
|
|
118
|
+
code-quality.
|
|
119
|
+
```
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Prova: Verde Não É Coberto
|
|
2
|
+
|
|
3
|
+
> Escopo: Gate 3 (avaliador independente). Não é dimensão do score composto — impõe **teto** em
|
|
4
|
+
> Cobertura de Testes (`evals/rubrics/test-coverage.md`, seção "Tetos de prova").
|
|
5
|
+
|
|
6
|
+
## O que esta prova responde
|
|
7
|
+
|
|
8
|
+
Um teste que **nasce verde** nunca teve fase RED: ele afirma uma pós-condição que o backend já
|
|
9
|
+
garantia antes da mudança. Ele não está errado — está **vazio**. E é indistinguível, no relatório de
|
|
10
|
+
execução, de um teste que protege alguma coisa.
|
|
11
|
+
|
|
12
|
+
A pergunta é: **qual asserção deste teste teria falhado ANTES da mudança?** Se nenhuma, o teste
|
|
13
|
+
documenta, não cobre — e isso precisa estar **declarado**, não escondido no verde.
|
|
14
|
+
|
|
15
|
+
## Bandas 0-10
|
|
16
|
+
|
|
17
|
+
| 0-3 | 4-6 | 7-8 | 9-10 |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| Testes novos apresentados como cobertura sem que nenhum tenha tido fase RED. Pré-condições afirmadas em prosa/comentário, nunca medidas | Alguns testes nascem verdes e isso não é declarado. `DoesNotContain` de um item só, deixando os irmãos abertos | O relatório reconhece pelo enunciado quais testes nascem verdes e os declara. Falta a asserção positiva no campo novo ao lado do `DoesNotContain` | Cada teste que nasce verde está declarado como tal, com o motivo. Pré-condições medidas (`Assert.NotNull`), listas EXATAS em vez de negativas pontuais, e mudança de semântica coberta pelo par positivo+negativo |
|
|
20
|
+
|
|
21
|
+
## Anti-padrões literais
|
|
22
|
+
|
|
23
|
+
- **Pós-condição que o backend já garante apresentada como cobertura nova** — a fase RED não
|
|
24
|
+
aconteceu. Reconheça pelo ENUNCIADO ("o campo X nunca vem nulo" num campo `NOT NULL` desde a
|
|
25
|
+
migração) e **declare no relatório**. (memória: pós-condição verde-de-nascença numa feature cujo
|
|
26
|
+
contrato já a garantia no schema)
|
|
27
|
+
- **Comentário que afirma um fato em vez de asserção** — `// aqui o id já existe` não falha nunca.
|
|
28
|
+
Comentário que afirma fato vira asserção **ou some**.
|
|
29
|
+
- **`DoesNotContain` de UM item** — provar que "banana" sumiu deixa "maçã" e "pera" livres. Quando o
|
|
30
|
+
contrato é "só isto e nada mais", asserte a **lista exata**.
|
|
31
|
+
- **Pré-condição afirmada em prosa** — "o registro existe neste ponto" precisa ser
|
|
32
|
+
`Assert.NotNull(x)`. Sem isso, um teste que passa por vacuidade (coleção vazia, nulo silencioso)
|
|
33
|
+
se lê como verde legítimo.
|
|
34
|
+
- **Mudar semântica e asserir só o lado novo** — ao trocar o texto/o campo, o par é obrigatório:
|
|
35
|
+
asserção **positiva** no campo novo **e** `DoesNotContain` do texto substituído. Sem o negativo, o
|
|
36
|
+
teste passa com os dois presentes.
|
|
37
|
+
|
|
38
|
+
## Como registrar no relatório
|
|
39
|
+
|
|
40
|
+
Na seção de análise de cobertura, uma linha por teste que nasce verde:
|
|
41
|
+
|
|
42
|
+
```markdown
|
|
43
|
+
- `test/x.test.js:41` — nasce VERDE: a pós-condição P3 já era garantida pelo `NOT NULL` da coluna
|
|
44
|
+
desde a migração 0007. Documenta, não cobre. Não conta para a faixa 9-10 de Cobertura.
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
E no `**Proof-Scores:**`, a chave `verde`. Um relatório que não distingue "cobre" de "documenta"
|
|
48
|
+
não pode passar de 8 nesta prova, por mais testes que tenha.
|
|
@@ -71,3 +71,33 @@ que o mandate não as mencione explicitamente. Uma violação em qualquer uma de
|
|
|
71
71
|
`AddTransient` correspondente em `Program.cs`?
|
|
72
72
|
7. Se o diff tem chamada server-direct ao backend E chamada client-side via proxy para o mesmo
|
|
73
73
|
recurso, elas usam duas constantes de prefixo distintas — nenhuma reusada para o outro propósito?
|
|
74
|
+
|
|
75
|
+
## Tetos de prova de mídia (Gate 3)
|
|
76
|
+
|
|
77
|
+
Quando — e **somente** quando — a feature gera mídia (alguma task de `tasks.json` cita, em
|
|
78
|
+
`standards[]`, um dos três standards de mídia de `ai-agents/`), o avaliador independente também
|
|
79
|
+
aplica `.morph/framework/evals/proof-rubrics/media-claims.md` e grava ` media={n}` na linha
|
|
80
|
+
`**Proof-Scores:**`.
|
|
81
|
+
|
|
82
|
+
Ela **não** é uma quinta dimensão do composto — nenhum peso muda por causa dela. Ela entra aqui,
|
|
83
|
+
como teto sobre esta dimensão, pelo mesmo mecanismo do teto de prova em Cobertura.
|
|
84
|
+
|
|
85
|
+
**Por que aqui e não em Cobertura:** as quatro afirmações que ela cobra — custo, revisão humana,
|
|
86
|
+
marcação, hot path não bloqueado — são **propriedades estruturais**: onde a chamada mora, que
|
|
87
|
+
estados existem, o que é persistido, o que é publicado. Uma feature que gera mídia dentro do turno
|
|
88
|
+
de conversa está arquiteturalmente errada, não mal testada. E há uma consequência técnica: o `min`
|
|
89
|
+
que limita Cobertura é calculado sobre a lista explícita das quatro provas de evidência, com `media`
|
|
90
|
+
**fora** dela — senão um `media=4` derrubaria o teto de teste por um motivo que não tem nada a ver
|
|
91
|
+
com teste.
|
|
92
|
+
|
|
93
|
+
| Proof-Score de media | teto nesta dimensão |
|
|
94
|
+
|---|---|
|
|
95
|
+
| 9 a 10 | sem teto |
|
|
96
|
+
| 7 a 8 | 8 |
|
|
97
|
+
| 4 a 6 | 6 |
|
|
98
|
+
| 0 a 3 | 3, e o item entra como **bloqueante** listado no relatório |
|
|
99
|
+
|
|
100
|
+
Sem a chave `media`, não há teto a aplicar. Mas a ausência da seção `## Mídia gerada` num relatório
|
|
101
|
+
de feature que gera mídia **pausa o Gate 3** por conta própria (`src/lib/risk-detector.js`), antes
|
|
102
|
+
de qualquer discussão de nota. Feature que não gera mídia é isenta automaticamente — a chave não
|
|
103
|
+
aparece e o sinal é falso.
|
|
@@ -57,6 +57,32 @@ Avalia naming, ausência de code smells e aderência aos padrões do stack do pr
|
|
|
57
57
|
> `reuse`. Um símbolo criado contra um veredito `REUSAR` é achado desta
|
|
58
58
|
> dimensão, mesmo que o código esteja impecável por dentro.
|
|
59
59
|
|
|
60
|
+
## Teto de prova de observabilidade GenAI
|
|
61
|
+
|
|
62
|
+
Quando a feature **chama LLM**, o avaliador aplica também
|
|
63
|
+
`.morph/framework/evals/proof-rubrics/observabilidade-genai.md` e grava a nota sob a chave `obs` na
|
|
64
|
+
linha `**Proof-Scores:**`. Ela **não** é dimensão do composto — nenhum peso muda — e entra aqui como
|
|
65
|
+
teto sobre esta dimensão:
|
|
66
|
+
|
|
67
|
+
| Proof-Score `obs` | teto nesta dimensão |
|
|
68
|
+
|---|---|
|
|
69
|
+
| 9 a 10 | sem teto |
|
|
70
|
+
| 7 a 8 | 9 |
|
|
71
|
+
| 4 a 6 | 7 |
|
|
72
|
+
| 0 a 3 | 5 |
|
|
73
|
+
|
|
74
|
+
A rubrica pergunta a **propriedade** ("dá para reconstruir o que foi ao provedor, e o que ele
|
|
75
|
+
respondeu, neste turno?"), não a tecnologia: telemetria própria com teste de fidelidade alcança a
|
|
76
|
+
faixa mais alta tanto quanto um span OpenTelemetry.
|
|
77
|
+
|
|
78
|
+
**Aplicabilidade tem duas etapas, e ela NÃO se declara determinística** (a versão anterior se
|
|
79
|
+
declarava, e errava na primeira feature que encontrou): (1) **filtro mecânico de alta recall** — o
|
|
80
|
+
`mandate.md` cita ao menos um standard `ai-agents`; falso ⇒ `not_evaluated`. (2) **determinação
|
|
81
|
+
escrita pelo avaliador** — a feature entrega código que **chama um modelo**? — registrada **com o
|
|
82
|
+
motivo** no relatório. Tasks com `group` em `{docs, tests}` são **indício** contra, não veredito.
|
|
83
|
+
`not_evaluated` **não aplica teto**, e um `not_evaluated` sem motivo escrito não é dispensa. Sem a
|
|
84
|
+
chave `obs` no `**Proof-Scores:**`, também não há teto a aplicar.
|
|
85
|
+
|
|
60
86
|
## Perguntas de avaliação
|
|
61
87
|
|
|
62
88
|
1. É possível entender o que uma função faz apenas pelo seu nome e assinatura?
|
|
@@ -58,3 +58,23 @@ Avalia se os testes cobrem os comportamentos esperados, são isolados e têm nom
|
|
|
58
58
|
min; o fecho de um fail-open abriu um P1; a correção desse P1 saiu com meio predicado. Nenhuma
|
|
59
59
|
apareceu no score, porque score não pergunta isso. **Sem resposta a esta pergunta numa correção,
|
|
60
60
|
o teto de Cobertura é 8.**
|
|
61
|
+
|
|
62
|
+
## Tetos de prova (Gate 3)
|
|
63
|
+
|
|
64
|
+
No Gate 3 o avaliador independente pontua **a própria evidência** 0-10 nas cinco rubricas de
|
|
65
|
+
`.morph/framework/evals/proof-rubrics/` (`mutation-proof.md`, `verde-nao-e-coberto.md`,
|
|
66
|
+
`fiacao.md`, `guard-cego.md`, `eval-de-modelo.md`) e grava a linha `**Proof-Scores:**` no `evaluator-report.md`.
|
|
67
|
+
|
|
68
|
+
Elas **não** são dimensões do composto — nenhum peso muda por causa delas. Elas entram aqui, como
|
|
69
|
+
teto sobre esta dimensão, pelo mesmo mecanismo que "pós-condição sem teste impede a faixa 9-10":
|
|
70
|
+
|
|
71
|
+
| menor Proof-Score das cinco | teto nesta dimensão |
|
|
72
|
+
|---|---|
|
|
73
|
+
| 9 a 10 | sem teto |
|
|
74
|
+
| 7 a 8 | 8 |
|
|
75
|
+
| 4 a 6 | 6 |
|
|
76
|
+
| 0 a 3 | 3, e o item entra como **bloqueante** listado no relatório |
|
|
77
|
+
|
|
78
|
+
Sem a linha `**Proof-Scores:**`, não há teto a aplicar — mas a ausência da seção `## Mutações` num
|
|
79
|
+
relatório de feature com código de produção **pausa o Gate 3** por conta própria
|
|
80
|
+
(`src/lib/risk-detector.js`), antes de qualquer discussão de nota.
|
|
@@ -20,10 +20,14 @@
|
|
|
20
20
|
* This is the sanctioned progress path — the agent never has to rewrite the
|
|
21
21
|
* whole file nor reach for a Node script to record task status.
|
|
22
22
|
*
|
|
23
|
+
* A gate freezes the APPROVED ARTIFACT, so a protected file that does not yet
|
|
24
|
+
* exist on disk is a CREATION and passes — the criterion is the file's
|
|
25
|
+
* existence, never who signed the gate (see the carve-out in run()).
|
|
26
|
+
*
|
|
23
27
|
* Fail-open: exits 0 on any error.
|
|
24
28
|
*/
|
|
25
29
|
|
|
26
|
-
import { readFileSync } from 'fs';
|
|
30
|
+
import { existsSync, readFileSync } from 'fs';
|
|
27
31
|
import { stateExists, isGateApproved } from '../../shared/state-reader.js';
|
|
28
32
|
import {
|
|
29
33
|
isFeaturePath,
|
|
@@ -238,6 +242,35 @@ export async function run(payload) {
|
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
// ── Gate approved — file is locked ──────────────────────────────────────
|
|
245
|
+
const absPath = resolveTargetPath(filePath, root);
|
|
246
|
+
|
|
247
|
+
// Um gate aprovado congela o ARTEFATO APROVADO. Se o arquivo ainda não
|
|
248
|
+
// existe no disco, não há artefato aprovado a proteger: isto é a CRIAÇÃO
|
|
249
|
+
// inicial, não a edição de uma spec fechada. O caso é real e não é raro —
|
|
250
|
+
// `chore`/`hotfix` nascem com `proposal`+`plan` carimbados `policy:{tipo}`
|
|
251
|
+
// (create.js → work-types.js skippedGates), então o primeiro `Write` de
|
|
252
|
+
// `2-plan/spec.md` já chega aqui com o gate "aprovado" sem que humano
|
|
253
|
+
// nenhum tenha aprovado coisa alguma — e era barrado com uma mensagem
|
|
254
|
+
// mandando revogar o gate de um arquivo inexistente.
|
|
255
|
+
//
|
|
256
|
+
// O critério é a EXISTÊNCIA, nunca o `approvedBy`. Liberar por assinatura
|
|
257
|
+
// (`policy:` passa, `lucas` não) transformaria este carve-out em licença
|
|
258
|
+
// para reescrever qualquer spec aprovada: bastaria apagá-la antes. Por ser
|
|
259
|
+
// sobre o disco, a janela fecha sozinha no instante em que o arquivo
|
|
260
|
+
// existe — os contra-canários de test/hooks/protect-spec-files.test.js
|
|
261
|
+
// pinam exatamente isso.
|
|
262
|
+
//
|
|
263
|
+
// A decisão é explícita e vale para as DUAS metades do hook. Antes, o ramo
|
|
264
|
+
// de campos mutáveis (tasks.json) já deixava a primeira criação passar, mas
|
|
265
|
+
// por acidente — o ENOENT caía no catch fail-open da leitura — enquanto o
|
|
266
|
+
// ramo travado bloqueava. Duas metades respondendo à mesma pergunta em
|
|
267
|
+
// direções opostas; agora respondem aqui, uma vez só. O `result` distinto
|
|
268
|
+
// no log é o que torna essa unificação observável.
|
|
269
|
+
if (!existsSync(absPath)) {
|
|
270
|
+
logHookActivity('protect-spec-files', 'PreToolUse', 'ok:absent');
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
|
|
241
274
|
const mutableFields = MUTABLE_SPEC_FIELDS[filename];
|
|
242
275
|
|
|
243
276
|
// Files with mutable progress fields (e.g. tasks.json): allow a full Write
|
|
@@ -247,8 +280,9 @@ export async function run(payload) {
|
|
|
247
280
|
|
|
248
281
|
// Build the proposed document from either a full Write or a targeted Edit,
|
|
249
282
|
// then semantic-diff it against the current file on disk. Any failure to
|
|
250
|
-
// read / parse / apply → fail-open (pass), never a false block.
|
|
251
|
-
|
|
283
|
+
// read / parse / apply → fail-open (pass), never a false block. O arquivo
|
|
284
|
+
// EXISTE aqui (a inexistência já foi decidida acima), então este catch
|
|
285
|
+
// volta a significar só o que dizia originalmente: não deu para diffar.
|
|
252
286
|
let oldDoc, newDoc;
|
|
253
287
|
try {
|
|
254
288
|
const original = readFileSync(absPath, 'utf-8');
|
|
@@ -312,8 +346,11 @@ export async function run(payload) {
|
|
|
312
346
|
// de leitura/reconstrução cai no bloqueio (o conservador aqui é BLOQUEAR —
|
|
313
347
|
// ao contrário do ramo do tasks.json, onde não conseguir diffar significa
|
|
314
348
|
// não ter como provar violação; aqui a regra base é "não se edita").
|
|
349
|
+
//
|
|
350
|
+
// O arquivo EXISTE neste ponto — a inexistência foi decidida lá em cima, e
|
|
351
|
+
// é a única coisa que este catch NÃO significa mais. ENOENT tratado como
|
|
352
|
+
// "ilegível" era o defeito F14; "ilegível de verdade" continua bloqueando.
|
|
315
353
|
const toolName = getToolName(payload);
|
|
316
|
-
const absPath = resolveTargetPath(filePath, root);
|
|
317
354
|
let errataCheck = null;
|
|
318
355
|
try {
|
|
319
356
|
const original = readFileSync(absPath, 'utf-8');
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
-
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
-
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
-
|
|
6
|
-
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
-
if command -v python3 &>/dev/null; then
|
|
8
|
-
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
-
elif command -v python &>/dev/null; then
|
|
10
|
-
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
-
fi
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# morph-spec statusline — installed globally to ~/.claude/statusline.sh
|
|
3
|
+
# Claude Code invokes this with JSON via stdin after each response.
|
|
4
|
+
# Requires: Python 3 available as `python3` or `python` on PATH.
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
if command -v python3 &>/dev/null; then
|
|
8
|
+
python3 "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
9
|
+
elif command -v python &>/dev/null; then
|
|
10
|
+
python "${SCRIPT_DIR}/statusline.py" 2>/dev/null || true
|
|
11
|
+
fi
|