@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,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* code-audit.js — duas varreduras sobre os `.cs` do projeto.
|
|
3
|
+
*
|
|
4
|
+
* (a) `removed-api` — nomes renomeados ANTES do 1.0 GA do `Microsoft.Agents.AI`
|
|
5
|
+
* (b) `prompt-source` — `Instructions` recebendo literal de string no ponto de
|
|
6
|
+
* construção do agente
|
|
7
|
+
*
|
|
8
|
+
* Nenhuma das duas é grep cego, e as duas são `warn`:
|
|
9
|
+
*
|
|
10
|
+
* - A #93 apurou, e o standard `ai-agents/agent-session.md` registra, que
|
|
11
|
+
* `AgentThread` → `AgentSession` e `GetNewThread()` → `CreateSessionAsync()`
|
|
12
|
+
* aconteceram no ciclo PRÉ-GA. Dentro do `Microsoft.Agents.AI` >= 1.0 os
|
|
13
|
+
* nomes antigos são nomes mortos — mas o MESMO identificador pertence a
|
|
14
|
+
* outros SDKs de agente na mesma solução, e é por isso que o achado é aviso
|
|
15
|
+
* e a mensagem manda CONFERIR a qual cliente a chamada pertence, em vez de
|
|
16
|
+
* afirmar defeito. Sem o pacote declarado em >= 1.0 a checagem nem roda.
|
|
17
|
+
*
|
|
18
|
+
* - O `prompt-source` é deliberadamente conservador: acusa só o literal no
|
|
19
|
+
* ponto de construção. Leitura de arquivo, banco, configuração ou constante
|
|
20
|
+
* nomeada não é acusada. Falso negativo é aceitável; falso positivo não —
|
|
21
|
+
* uma checagem que grita em código correto é desligada pelo usuário na
|
|
22
|
+
* primeira semana.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { readFileSync } from 'node:fs';
|
|
26
|
+
import { relative } from 'node:path';
|
|
27
|
+
import { collectProjectFiles } from './ai-project-scan.js';
|
|
28
|
+
import { stripComments } from './comment-stripper.js';
|
|
29
|
+
|
|
30
|
+
/** Símbolos renomeados antes do 1.0 GA, e o nome vivo de cada um. */
|
|
31
|
+
export const RENAMED_SYMBOLS = {
|
|
32
|
+
AgentThread: 'AgentSession',
|
|
33
|
+
GetNewThread: 'CreateSessionAsync()',
|
|
34
|
+
ChatMessageStore: 'a conversa persistida do projeto (ver ai-agents/agent-session.md)',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function lineOf(text, index) {
|
|
38
|
+
let line = 1;
|
|
39
|
+
for (let i = 0; i < index && i < text.length; i++) if (text[i] === '\n') line += 1;
|
|
40
|
+
return line;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Todos os `.cs` do projeto, com as mesmas exclusões da varredura de `.csproj`. */
|
|
44
|
+
export function collectCsharpFiles(root) {
|
|
45
|
+
return collectProjectFiles(root, (name) => name.toLowerCase().endsWith('.cs'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Menor major que a versão declarada pode assumir, ou `null` quando a
|
|
50
|
+
* declaração não permite decidir.
|
|
51
|
+
*
|
|
52
|
+
* `1.0.*` e `[1.0,)` têm limite inferior 1 — sobem, nunca descem. `$(Prop)` e
|
|
53
|
+
* versão ausente NÃO permitem decidir, e decidir mesmo assim seria inventar o
|
|
54
|
+
* dado que a checagem reporta.
|
|
55
|
+
*
|
|
56
|
+
* @param {{ declared: string|null, versionKind: string }|null} pkg
|
|
57
|
+
* @returns {number|null}
|
|
58
|
+
*/
|
|
59
|
+
export function lowerBoundMajor(pkg) {
|
|
60
|
+
if (!pkg) return null;
|
|
61
|
+
if (pkg.versionKind === 'unresolved' || pkg.versionKind === 'absent') return null;
|
|
62
|
+
const raw = String(pkg.declared ?? '').trim();
|
|
63
|
+
// Intervalo de limite inferior aberto (`(,2.0]`): nada garante >= 1.0.
|
|
64
|
+
if (/^[[(]\s*,/.test(raw)) return 0;
|
|
65
|
+
const m = /(\d+)/.exec(raw);
|
|
66
|
+
return m ? Number(m[1]) : null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Por que o `removed-api` NÃO roda, quando não roda.
|
|
71
|
+
*
|
|
72
|
+
* @param {{ declared: string|null, versionKind: string }|null} mafPackage
|
|
73
|
+
* @returns {string|null} razão do "não verificado", ou `null` quando pode rodar
|
|
74
|
+
*/
|
|
75
|
+
export function renamedApiGate(mafPackage) {
|
|
76
|
+
if (!mafPackage) {
|
|
77
|
+
return 'o projeto não declara `Microsoft.Agents.AI` — os nomes antigos não pertencem a este código';
|
|
78
|
+
}
|
|
79
|
+
const major = lowerBoundMajor(mafPackage);
|
|
80
|
+
if (major === null) {
|
|
81
|
+
return `a versão declarada de \`Microsoft.Agents.AI\` (\`${mafPackage.declared ?? 'ausente'}\`) `
|
|
82
|
+
+ 'não permite decidir se é >= 1.0';
|
|
83
|
+
}
|
|
84
|
+
if (major < 1) {
|
|
85
|
+
return `\`Microsoft.Agents.AI\` está declarado em ${mafPackage.declared} (< 1.0), onde os nomes `
|
|
86
|
+
+ 'antigos ainda são a API viva';
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* (a) Uso de API renomeada antes do 1.0 GA.
|
|
93
|
+
*
|
|
94
|
+
* @param {string} root
|
|
95
|
+
* @param {{ mafPackage?: object|null }} [options]
|
|
96
|
+
* @returns {null | Array<{ id: string, severity: 'warn', symbol: string, file: string, line: number, message: string }>}
|
|
97
|
+
* `null` = não pude perguntar (gate fechado ou raiz ilegível).
|
|
98
|
+
*/
|
|
99
|
+
export function auditRenamedApi(root, { mafPackage = null } = {}) {
|
|
100
|
+
if (renamedApiGate(mafPackage) !== null) return null;
|
|
101
|
+
|
|
102
|
+
const files = collectCsharpFiles(root);
|
|
103
|
+
const findings = [];
|
|
104
|
+
for (const file of files) {
|
|
105
|
+
let text;
|
|
106
|
+
try {
|
|
107
|
+
text = readFileSync(file, 'utf8');
|
|
108
|
+
} catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
const clean = stripComments(text, { blankStrings: true });
|
|
112
|
+
for (const [dead, alive] of Object.entries(RENAMED_SYMBOLS)) {
|
|
113
|
+
const re = new RegExp(`\\b${dead}\\b`, 'g');
|
|
114
|
+
let m;
|
|
115
|
+
while ((m = re.exec(clean)) !== null) {
|
|
116
|
+
findings.push({
|
|
117
|
+
id: 'renamed-api',
|
|
118
|
+
severity: 'warn',
|
|
119
|
+
symbol: dead,
|
|
120
|
+
file: relative(root, file) || file,
|
|
121
|
+
line: lineOf(clean, m.index),
|
|
122
|
+
message: `\`${dead}\` em ${relative(root, file) || file}:${lineOf(clean, m.index)} — em `
|
|
123
|
+
+ `\`Microsoft.Agents.AI\` >= 1.0 o nome vivo é \`${alive}\`; o renome aconteceu ainda `
|
|
124
|
+
+ 'no ciclo pré-GA. Aviso, não erro: o mesmo identificador é legítimo em outros SDKs '
|
|
125
|
+
+ 'de agente (por exemplo o `PersistentAgentsClient` do Azure AI Agents), então '
|
|
126
|
+
+ 'confirme a qual cliente esta chamada pertence antes de trocar.',
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return findings;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* `Instructions` recebendo literal de string no ponto de construção.
|
|
136
|
+
*
|
|
137
|
+
* Casa `Instructions = "…"`, `Instructions = @"…"`, `Instructions = $"…"`,
|
|
138
|
+
* `Instructions = """…"""` e o argumento nomeado `instructions: "…"`.
|
|
139
|
+
* NÃO casa atribuição de variável, chamada, `await`, constante nomeada nem
|
|
140
|
+
* literal dentro de comentário.
|
|
141
|
+
*/
|
|
142
|
+
const INSTRUCTIONS_LITERAL_RE = /(?<![\w."])(?:Instructions\s*=(?!=)|instructions\s*:)\s*(?:[$@]){0,2}"/g;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* (b) Prompt sem fonte declarada.
|
|
146
|
+
*
|
|
147
|
+
* @param {string} root
|
|
148
|
+
* @returns {null | Array<{ id: string, severity: 'warn', file: string, line: number, message: string }>}
|
|
149
|
+
*/
|
|
150
|
+
export function auditPromptSource(root) {
|
|
151
|
+
const files = collectCsharpFiles(root);
|
|
152
|
+
if (files.length === 0) return null;
|
|
153
|
+
|
|
154
|
+
const findings = [];
|
|
155
|
+
for (const file of files) {
|
|
156
|
+
let text;
|
|
157
|
+
try {
|
|
158
|
+
text = readFileSync(file, 'utf8');
|
|
159
|
+
} catch {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const clean = stripComments(text);
|
|
163
|
+
INSTRUCTIONS_LITERAL_RE.lastIndex = 0;
|
|
164
|
+
let m;
|
|
165
|
+
while ((m = INSTRUCTIONS_LITERAL_RE.exec(clean)) !== null) {
|
|
166
|
+
const line = lineOf(clean, m.index);
|
|
167
|
+
const rel = relative(root, file) || file;
|
|
168
|
+
findings.push({
|
|
169
|
+
id: 'prompt-source',
|
|
170
|
+
severity: 'warn',
|
|
171
|
+
file: rel,
|
|
172
|
+
line,
|
|
173
|
+
message: `${rel}:${line} — as instruções do agente vêm de um literal de string no ponto de `
|
|
174
|
+
+ 'construção. Sem fonte declarada (arquivo, banco ou configuração versionada) não há '
|
|
175
|
+
+ 'como provar qual versão do prompt rodou. Ver o standard de fonte de prompt em '
|
|
176
|
+
+ '`ai-agents/`.',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return findings;
|
|
181
|
+
}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* comment-stripper.js — apagar comentários SEM apagar o que está dentro de uma
|
|
3
|
+
* string, preservando comprimento e quebras de linha.
|
|
4
|
+
*
|
|
5
|
+
* Existe uma vez só, e serve a dois consumidores que fariam a MESMA coisa de
|
|
6
|
+
* jeitos ligeiramente diferentes se cada um escrevesse o seu:
|
|
7
|
+
*
|
|
8
|
+
* - `code-audit.js` — não pode acusar `AgentThread` que só aparece num
|
|
9
|
+
* comentário ou numa mensagem de log;
|
|
10
|
+
* - `registry-audit.js` — precisa ler **JSONC**, a forma que o exemplar
|
|
11
|
+
* canônico `model-registry.v2.json` usa e que o leitor C# do kit aceita de
|
|
12
|
+
* propósito (`JsonCommentHandling.Skip`, `AllowTrailingCommas`).
|
|
13
|
+
*
|
|
14
|
+
* **Por que não um regex.** O exemplar tem `"http://localhost:11434/v1"` e uma
|
|
15
|
+
* `provenance` com URL. Um `replace(/\/\/.*$/gm, '')` decapitaria as duas —
|
|
16
|
+
* apagando dado e chamando isso de comentário. Um varredor que atravessa as
|
|
17
|
+
* strings primeiro é a única forma correta, e é o que está aqui.
|
|
18
|
+
*
|
|
19
|
+
* O comprimento é preservado (os removidos viram espaço, `\n` e `\r` ficam) para
|
|
20
|
+
* que índices e números de linha do texto original continuem válidos no texto
|
|
21
|
+
* limpo. É o que permite `code-audit.js` reportar `arquivo.cs:34` sem um segundo
|
|
22
|
+
* mapeamento.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {string} text
|
|
27
|
+
* @param {object} [options]
|
|
28
|
+
* @param {boolean} [options.blankStrings=false]
|
|
29
|
+
* Apaga também o CONTEÚDO das strings. **As aspas delimitadoras ficam.**
|
|
30
|
+
*
|
|
31
|
+
* Apagá-las junto era um defeito com consequência: um elemento-string virava
|
|
32
|
+
* espaço puro, e o detector de vírgula sobrando de `jsoncToJson` passava a
|
|
33
|
+
* enxergar `[ , , ]` — separadores REAIS entre delimitadores que sumiram —
|
|
34
|
+
* e apagava uma vírgula que era sintaxe. Medido: `{"tags":["a","b"]}` saía
|
|
35
|
+
* `{"tags":["a","b" ]}`, JSON inválido, e o registry voltava a ser
|
|
36
|
+
* `○ não verificado`. O B1 reintroduzido pela correção do B1.
|
|
37
|
+
* @param {{ unterminated?: string|null }} [options.report]
|
|
38
|
+
* Se um objeto for passado, recebe `unterminated` = `'block-comment'`,
|
|
39
|
+
* `'string'`, `'verbatim-string'`, `'raw-string'` ou `null`. É o canal de
|
|
40
|
+
* diagnóstico que `jsoncToJson` usa para ficar tão estrito quanto o leitor
|
|
41
|
+
* de produção, sem que este lexer precise lançar (ele também varre `.cs`,
|
|
42
|
+
* onde um comentário aberto é problema do arquivo, não da varredura).
|
|
43
|
+
* @returns {string} texto do MESMO comprimento
|
|
44
|
+
*/
|
|
45
|
+
export function stripComments(text, { blankStrings = false, report = null } = {}) {
|
|
46
|
+
const out = text.split('');
|
|
47
|
+
const blank = (from, to) => {
|
|
48
|
+
for (let k = from; k < to && k < out.length; k++) {
|
|
49
|
+
if (out[k] !== '\n' && out[k] !== '\r') out[k] = ' ';
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const flag = (kind) => { if (report && !report.unterminated) report.unterminated = kind; };
|
|
53
|
+
if (report) report.unterminated = null;
|
|
54
|
+
|
|
55
|
+
let i = 0;
|
|
56
|
+
while (i < text.length) {
|
|
57
|
+
const c = text[i];
|
|
58
|
+
const next = text[i + 1];
|
|
59
|
+
|
|
60
|
+
if (c === '/' && next === '/') {
|
|
61
|
+
let end = text.indexOf('\n', i);
|
|
62
|
+
if (end === -1) end = text.length;
|
|
63
|
+
blank(i, end);
|
|
64
|
+
i = end;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (c === '/' && next === '*') {
|
|
68
|
+
const close = text.indexOf('*/', i + 2);
|
|
69
|
+
if (close === -1) flag('block-comment');
|
|
70
|
+
const end = close === -1 ? text.length : close + 2;
|
|
71
|
+
blank(i, end);
|
|
72
|
+
i = end;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Raw string literal do C#: """ ... """ — delimitadores de 3 aspas.
|
|
77
|
+
if (c === '"' && next === '"' && text[i + 2] === '"') {
|
|
78
|
+
const end = text.indexOf('"""', i + 3);
|
|
79
|
+
if (end === -1) flag('raw-string');
|
|
80
|
+
const stop = end === -1 ? text.length : end + 3;
|
|
81
|
+
if (blankStrings) blank(i + 3, end === -1 ? stop : stop - 3);
|
|
82
|
+
i = stop;
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Verbatim string do C#: @"..." — aspas duplicadas escapam
|
|
87
|
+
if (c === '@' && next === '"') {
|
|
88
|
+
let j = i + 2;
|
|
89
|
+
let closed = false;
|
|
90
|
+
while (j < text.length) {
|
|
91
|
+
if (text[j] === '"') {
|
|
92
|
+
if (text[j + 1] === '"') { j += 2; continue; }
|
|
93
|
+
j += 1;
|
|
94
|
+
closed = true;
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
j += 1;
|
|
98
|
+
}
|
|
99
|
+
if (!closed) flag('verbatim-string');
|
|
100
|
+
if (blankStrings) blank(i + 2, closed ? j - 1 : j);
|
|
101
|
+
i = j;
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (c === '"') {
|
|
106
|
+
let j = i + 1;
|
|
107
|
+
let closed = false;
|
|
108
|
+
while (j < text.length) {
|
|
109
|
+
if (text[j] === '\\') { j += 2; continue; }
|
|
110
|
+
if (text[j] === '"') { j += 1; closed = true; break; }
|
|
111
|
+
if (text[j] === '\n') break;
|
|
112
|
+
j += 1;
|
|
113
|
+
}
|
|
114
|
+
if (!closed) flag('string');
|
|
115
|
+
if (blankStrings) blank(i + 1, closed ? j - 1 : j);
|
|
116
|
+
i = j;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (c === "'") {
|
|
121
|
+
let j = i + 1;
|
|
122
|
+
let closed = false;
|
|
123
|
+
while (j < text.length) {
|
|
124
|
+
if (text[j] === '\\') { j += 2; continue; }
|
|
125
|
+
if (text[j] === "'") { j += 1; closed = true; break; }
|
|
126
|
+
if (text[j] === '\n') break;
|
|
127
|
+
j += 1;
|
|
128
|
+
}
|
|
129
|
+
if (blankStrings) blank(i + 1, closed ? j - 1 : j);
|
|
130
|
+
i = j;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
i += 1;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return out.join('');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Converte JSONC em JSON: apaga comentários e vírgulas sobrando antes de `}`/`]`.
|
|
142
|
+
*
|
|
143
|
+
* As duas tolerâncias são **exatamente** as que `ModelRegistry.FromJson` do
|
|
144
|
+
* ai-kit declara (`CommentHandling = Skip`, `AllowTrailingCommas = true`). A
|
|
145
|
+
* ferramenta que audita o registry tem de enxergar a mesma forma que o leitor
|
|
146
|
+
* de produção enxerga — senão o framework passa a recomendar uma forma que a
|
|
147
|
+
* sua própria auditoria não lê, e as checagens ficam inertes em silêncio.
|
|
148
|
+
*
|
|
149
|
+
* A vírgula sobrando é localizada num texto onde o conteúdo das strings também
|
|
150
|
+
* foi apagado — **com as aspas preservadas**, senão um elemento-string vira
|
|
151
|
+
* espaço puro e um separador REAL passa por vírgula sobrando (o defeito B4) —
|
|
152
|
+
* e só então removida do texto real: uma vírgula dentro de `"a,}"` é dado.
|
|
153
|
+
*
|
|
154
|
+
* **Um comentário de bloco não fechado LANÇA**, e a razão é a mesma que fez
|
|
155
|
+
* esta função existir: paridade com o leitor de produção. `JsonCommentHandling.Skip`
|
|
156
|
+
* do .NET lança nesse caso, e ser mais frouxo que o kit é o defeito INVERSO do
|
|
157
|
+
* B1 — auditar, e aprovar, um documento que a aplicação recusa carregar.
|
|
158
|
+
* `readJsonSafe` captura e devolve `null`, que o relatório traduz em
|
|
159
|
+
* "não verificado" com razão.
|
|
160
|
+
*
|
|
161
|
+
* @param {string} raw
|
|
162
|
+
* @returns {string} JSON estrito, do mesmo comprimento do original
|
|
163
|
+
* @throws {SyntaxError} comentário de bloco aberto e nunca fechado
|
|
164
|
+
*/
|
|
165
|
+
export function jsoncToJson(raw) {
|
|
166
|
+
const report = {};
|
|
167
|
+
const noComments = stripComments(raw, { report }).split('');
|
|
168
|
+
if (report.unterminated === 'block-comment') {
|
|
169
|
+
throw new SyntaxError(
|
|
170
|
+
'JSONC inválido: comentário de bloco `/*` aberto e nunca fechado — '
|
|
171
|
+
+ '`JsonCommentHandling.Skip` do leitor de produção recusa este documento, e a auditoria '
|
|
172
|
+
+ 'não pode aprovar o que a aplicação não carrega.');
|
|
173
|
+
}
|
|
174
|
+
const masked = stripComments(raw, { blankStrings: true });
|
|
175
|
+
|
|
176
|
+
for (let i = 0; i < masked.length; i++) {
|
|
177
|
+
if (masked[i] !== ',') continue;
|
|
178
|
+
let j = i + 1;
|
|
179
|
+
while (j < masked.length && /\s/.test(masked[j])) j++;
|
|
180
|
+
if (masked[j] === '}' || masked[j] === ']') noComments[i] = ' ';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return noComments.join('');
|
|
184
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pin-drift.js — o pin de pacotes de IA (`framework/ai-pin.json`) contra o que
|
|
3
|
+
* o `.csproj` do projeto declara.
|
|
4
|
+
*
|
|
5
|
+
* É a ÚNICA das cinco checagens do `doctor --ai` que produz `error`, e só na
|
|
6
|
+
* faixa flutuante. O motivo é prejuízo medido, não estética: uma faixa `2.*`
|
|
7
|
+
* sobe sozinha para uma versão com assinatura mudada e o `MissingMethodException`
|
|
8
|
+
* chega DEPOIS de a chamada ao modelo ter sido paga. As outras quatro checagens
|
|
9
|
+
* informam — uma checagem que grita em código correto é desligada pelo usuário
|
|
10
|
+
* na primeira semana.
|
|
11
|
+
*
|
|
12
|
+
* O pin é lido por `readAiPin` (#92), nunca por um segundo leitor: duas cópias
|
|
13
|
+
* do mesmo fato divergem, e é para isso que a Dependency Matrix existe.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { join } from 'node:path';
|
|
17
|
+
import { readAiPin } from '../standards/verified-against.js';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Resolve o pin para um projeto, sem NUNCA lançar.
|
|
21
|
+
*
|
|
22
|
+
* Ordem: (1) `<target>/.morph/framework/ai-pin.json` — a régua com que aquele
|
|
23
|
+
* projeto foi instalado; (2) o pin do pacote da CLI. Nenhum dos dois resolve →
|
|
24
|
+
* `null`, que o relatório traduz em "não verificado". Falta de régua é ausência
|
|
25
|
+
* de dado, nunca defeito do projeto: é literalmente o estado de todo projeto
|
|
26
|
+
* cujo framework é anterior ao pin.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} targetPath - raiz do projeto
|
|
29
|
+
* @returns {{ pinnedAt: string, packages: Record<string, {version: string}> } | null}
|
|
30
|
+
*/
|
|
31
|
+
export function resolveAiPin(targetPath) {
|
|
32
|
+
const roots = [];
|
|
33
|
+
if (targetPath) roots.push(join(targetPath, '.morph'));
|
|
34
|
+
roots.push(undefined); // root default de readAiPin = pacote da CLI
|
|
35
|
+
|
|
36
|
+
for (const root of roots) {
|
|
37
|
+
try {
|
|
38
|
+
const pin = root === undefined ? readAiPin() : readAiPin(root);
|
|
39
|
+
if (pin && pin.packages && Object.keys(pin.packages).length > 0) return pin;
|
|
40
|
+
} catch {
|
|
41
|
+
// Ausente ou inválido nesta raiz — tenta a próxima.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Compara a varredura do projeto com o pin.
|
|
49
|
+
*
|
|
50
|
+
* Três classes de achado, e a diferença entre elas é de quem é o defeito:
|
|
51
|
+
*
|
|
52
|
+
* | classe | severidade | de quem é |
|
|
53
|
+
* |---|---|---|
|
|
54
|
+
* | pacote do pin declarado com faixa flutuante | `error` | do projeto |
|
|
55
|
+
* | pacote do pin declarado com versão fixa ≠ pin | `warn` | do projeto |
|
|
56
|
+
* | pacote de IA presente no projeto e ausente do pin | `warn` | **do pin** (lacuna) |
|
|
57
|
+
*
|
|
58
|
+
* A terceira classe é descrita como lacuna do PIN de propósito: medido,
|
|
59
|
+
* `Microsoft.Agents.AI.OpenAI` aparece em três repositórios do acervo e não
|
|
60
|
+
* está no pin. Acusar o projeto ali seria acusar o certo pelo errado.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} params
|
|
63
|
+
* @param {ReturnType<import('./ai-project-scan.js').scanAiPackageRefs>} params.scan
|
|
64
|
+
* @param {ReturnType<typeof resolveAiPin>} params.pin
|
|
65
|
+
* @returns {null | Array<{ id: string, severity: 'error'|'warn', package: string, message: string }>}
|
|
66
|
+
* `null` = não pude perguntar (sem varredura ou sem pin).
|
|
67
|
+
*/
|
|
68
|
+
export function auditPinDrift({ scan, pin } = {}) {
|
|
69
|
+
if (!scan || !pin || !pin.packages) return null;
|
|
70
|
+
|
|
71
|
+
const findings = [];
|
|
72
|
+
const pinned = pin.packages;
|
|
73
|
+
|
|
74
|
+
for (const pkg of scan.packages) {
|
|
75
|
+
const pinEntry = pinned[pkg.name];
|
|
76
|
+
|
|
77
|
+
if (!pinEntry) {
|
|
78
|
+
if (!pkg.ai) continue;
|
|
79
|
+
findings.push({
|
|
80
|
+
id: 'pin-gap',
|
|
81
|
+
severity: 'warn',
|
|
82
|
+
package: pkg.name,
|
|
83
|
+
message: `\`${pkg.name}\` é pacote de IA declarado pelo projeto e NÃO está em `
|
|
84
|
+
+ `framework/ai-pin.json (pin de ${pin.pinnedAt}). Isto é lacuna do PIN, não defeito `
|
|
85
|
+
+ `do projeto: não há régua para julgar a versão declarada (${pkg.declared ?? 'não declarada'}).`,
|
|
86
|
+
});
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (pkg.versionKind === 'floating') {
|
|
91
|
+
findings.push({
|
|
92
|
+
id: 'floating-range',
|
|
93
|
+
severity: 'error',
|
|
94
|
+
package: pkg.name,
|
|
95
|
+
message: `\`${pkg.name}\` está declarado como faixa flutuante \`${pkg.declared}\`; `
|
|
96
|
+
+ `o pin fixa ${pinEntry.version}. Uma faixa sobe sozinha para uma versão com `
|
|
97
|
+
+ `assinatura mudada e o MissingMethodException chega DEPOIS de a chamada ser paga. `
|
|
98
|
+
+ `Fixe a versão.`,
|
|
99
|
+
});
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (pkg.versionKind === 'fixed' && pkg.version !== pinEntry.version) {
|
|
104
|
+
findings.push({
|
|
105
|
+
id: 'version-mismatch',
|
|
106
|
+
severity: 'warn',
|
|
107
|
+
package: pkg.name,
|
|
108
|
+
message: `\`${pkg.name}\`: projeto declara ${pkg.version}, pin declara ${pinEntry.version} `
|
|
109
|
+
+ `(pin de ${pin.pinnedAt}).`,
|
|
110
|
+
});
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (pkg.versionKind === 'unresolved') {
|
|
115
|
+
findings.push({
|
|
116
|
+
id: 'version-unresolved',
|
|
117
|
+
severity: 'warn',
|
|
118
|
+
package: pkg.name,
|
|
119
|
+
message: `\`${pkg.name}\` tira a versão da propriedade MSBuild \`${pkg.declared}\`, que `
|
|
120
|
+
+ `esta varredura não resolve — não verificado contra o pin (${pinEntry.version}). `
|
|
121
|
+
+ `Chutar o valor seria inventar o dado.`,
|
|
122
|
+
});
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (pkg.versionKind === 'absent') {
|
|
127
|
+
findings.push({
|
|
128
|
+
id: 'version-absent',
|
|
129
|
+
severity: 'warn',
|
|
130
|
+
package: pkg.name,
|
|
131
|
+
message: `\`${pkg.name}\` é referenciado sem versão e sem entrada em `
|
|
132
|
+
+ `Directory.Packages.props; o pin declara ${pinEntry.version}.`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return findings;
|
|
138
|
+
}
|