@polymorphism-tech/morph-spec 8.47.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +51 -43
- package/README.md +39 -38
- package/bin/morph-spec.js +41 -113
- package/bin/validate.js +1 -147
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +41 -101
- package/framework/MORPH.md +95 -252
- package/framework/agents.json +86 -21
- package/framework/commands/morph-apply.md +5 -28
- package/framework/commands/morph-archive.md +1 -1
- package/framework/commands/morph-hotfix.md +8 -8
- package/framework/commands/morph-infra.md +1 -1
- package/framework/commands/morph-preflight.md +2 -3
- package/framework/commands/morph-status.md +4 -4
- package/framework/docs/CLI.md +574 -286
- package/framework/evals/review-checklist.md +82 -41
- package/framework/hooks/README.md +10 -11
- package/framework/hooks/claude-code/core/block-destructive-worktree-removal.js +6 -49
- package/framework/hooks/claude-code/core/block-raw-worktree-add.js +2 -35
- package/framework/hooks/claude-code/core/chain.js +4 -65
- package/framework/hooks/claude-code/core/comment-guard.js +3 -17
- package/framework/hooks/claude-code/core/denylist.json +33 -3
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +15 -63
- package/framework/hooks/claude-code/core/gate-guard.js +9 -78
- package/framework/hooks/claude-code/core/graph-refresh-kick.js +3 -24
- package/framework/hooks/claude-code/core/heavy-gate.js +3 -21
- package/framework/hooks/claude-code/core/heavy-release.js +2 -13
- package/framework/hooks/claude-code/core/loop-detect.js +2 -31
- package/framework/hooks/claude-code/core/pre-bash-denylist.js +36 -2
- package/framework/hooks/claude-code/core/protect-spec-files.js +25 -297
- package/framework/hooks/claude-code/core/state-sync.js +15 -85
- package/framework/hooks/claude-code/core/telemetry-log.js +15 -96
- package/framework/hooks/claude-code/core/validate-completion.js +37 -147
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +23 -0
- package/framework/hooks/claude-code/core/worktree-context.js +15 -114
- package/framework/hooks/claude-code/core/worktree-create.js +6 -70
- package/framework/hooks/claude-code/core/worktree-env-inject.js +5 -56
- package/framework/hooks/claude-code/core/worktree-remove.js +5 -31
- package/framework/hooks/claude-code/statusline.py +55 -41
- package/framework/hooks/package.json +3 -0
- package/framework/hooks/shared/activity-logger.js +9 -89
- package/framework/hooks/shared/agent-reap.js +2 -17
- package/framework/hooks/shared/build-check.js +11 -76
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +168 -44
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +11 -43
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +59 -0
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +4 -0
- package/framework/hooks/shared/gate-question.js +12 -0
- package/framework/hooks/shared/git-exclude.js +3 -20
- package/framework/hooks/shared/git-paths.js +8 -104
- package/framework/hooks/shared/graph-freshness.js +22 -166
- package/framework/hooks/shared/graph-refresh.js +21 -149
- package/framework/hooks/shared/heavy-command.js +4 -12
- package/framework/hooks/shared/hook-response.js +8 -83
- package/framework/hooks/shared/morph-cli.js +8 -52
- package/framework/hooks/shared/payload-utils.js +10 -132
- package/framework/hooks/shared/phase-utils.js +14 -72
- package/framework/hooks/shared/redact-secrets.js +4 -15
- package/framework/hooks/shared/reparse-scan.js +6 -66
- package/framework/hooks/shared/slice-progress.js +99 -0
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/spawn-command.js +23 -0
- package/framework/hooks/shared/state-reader.js +51 -247
- package/framework/hooks/shared/state-writer.js +9 -54
- package/framework/hooks/shared/stdin-reader.js +1 -11
- package/framework/hooks/shared/telemetry-logger.js +7 -67
- package/framework/hooks/shared/transcript-usage.js +7 -29
- package/framework/hooks/shared/worktree-root.js +7 -29
- package/framework/mcp/registry.json +3 -4
- package/framework/phases.json +23 -39
- package/framework/rules/csharp-standards.md +1 -1
- package/framework/rules/frontend-standards.md +1 -0
- package/framework/rules/hangfire-standards.md +1 -1
- package/framework/rules/neon-standards.md +1 -1
- package/framework/schemas/config.schema.json +14 -9
- package/framework/schemas/feature.schema.json +15 -50
- package/framework/skills/README.md +3 -4
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +149 -222
- package/framework/skills/core/morph-hotfix/SKILL.md +33 -42
- package/framework/skills/core/morph-machine/SKILL.md +2 -2
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +137 -299
- package/framework/skills/core/morph-review/SKILL.md +111 -136
- package/framework/skills/core/morph-standards/SKILL.md +27 -39
- package/framework/skills/core/morph-uiux/SKILL.md +94 -152
- package/framework/standards/STANDARDS.json +109 -1219
- package/framework/standards/STANDARDS.meta.json +1319 -0
- package/framework/standards/ai-agents/agent-archetypes.md +2 -2
- package/framework/standards/ai-agents/agent-session.md +1 -1
- package/framework/standards/ai-agents/conversational-agent-with-phases.md +4 -4
- package/framework/standards/ai-agents/durable-workflows-hitl.md +3 -3
- package/framework/standards/ai-agents/evals-with-cache.md +1 -2
- package/framework/standards/ai-agents/guardrails.md +1 -1
- package/framework/standards/ai-agents/multi-agent-patterns.md +2 -2
- package/framework/standards/ai-agents/observability-patterns.md +1 -1
- package/framework/standards/ai-agents/production.md +1 -1
- package/framework/standards/ai-agents/prompt-sources.md +2 -2
- package/framework/standards/ai-agents/providers/model-registry.md +1 -1
- package/framework/standards/ai-agents/rag-custom-pgvector.md +2 -2
- package/framework/standards/ai-agents/setup.md +3 -3
- package/framework/standards/ai-agents/sweet-spot.md +2 -2
- package/framework/standards/ai-agents/tool-adapters.md +0 -2
- package/framework/standards/ai-agents/vector-data-extensions.md +4 -4
- package/framework/standards/ai-agents/workflows.md +1 -1
- package/framework/standards/architecture/code-comments.md +7 -7
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +1 -1
- package/framework/standards/backend/api/validation.md +1 -1
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +5 -5
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +1 -1
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +2 -2
- package/framework/standards/data/nosql/cache/redis.md +1 -1
- package/framework/standards/data/nosql/cosmos-db.md +3 -3
- package/framework/standards/frontend/admin-app/forms.md +1 -1
- package/framework/standards/frontend/design-system/art-direction.md +88 -0
- package/framework/standards/frontend/design-system/premium-finish.md +77 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/standards/integration/mcp/mcp-tools.md +7 -7
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/README.md +2 -3
- package/framework/templates/REGISTRY.json +15 -179
- package/framework/templates/docs/plan.md +72 -0
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/dotnet/ai-kit/README.md +1 -9
- package/framework/templates/dotnet/ai-kit/tests/Morph.AiKit.Tests/Morph.AiKit.Tests.csproj +0 -10
- package/framework/templates/meta-prompts/squad-leaders/backend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/frontend-squad.md +1 -1
- package/framework/templates/meta-prompts/squad-leaders/squad-leader.md +1 -1
- package/framework/templates/meta-prompts/validators/pre-commit-validator.md +3 -4
- package/package.json +1 -1
- package/src/commands/context/context-refresh.js +0 -16
- package/src/commands/dashboard/dashboard.js +2 -8
- package/src/commands/e2e/e2e.js +18 -105
- package/src/commands/graph/affected.js +0 -14
- package/src/commands/graph/build.js +11 -50
- package/src/commands/graph/explain.js +0 -16
- package/src/commands/graph/status.js +1 -12
- package/src/commands/mcp/mcp-setup.js +4 -62
- package/src/commands/observability/cost.js +17 -111
- package/src/commands/observability/telemetry.js +1 -14
- package/src/commands/project/branch.js +4 -45
- package/src/commands/project/claim.js +4 -48
- package/src/commands/project/doctor-procs.js +1 -1
- package/src/commands/project/doctor.js +53 -307
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +6 -35
- package/src/commands/project/heavy.js +3 -3
- package/src/commands/project/init.js +8 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/setup-infra-cmd.js +2 -1
- package/src/commands/project/stack-scan.js +58 -0
- package/src/commands/project/standards.js +65 -72
- package/src/commands/project/status.js +28 -121
- package/src/commands/project/update.js +31 -223
- package/src/commands/project/worktree.js +109 -883
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +65 -120
- package/src/commands/state/approve.js +87 -229
- package/src/commands/state/archive.js +23 -139
- package/src/commands/state/auto-approval.js +65 -0
- package/src/commands/state/create.js +49 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +105 -494
- package/src/commands/state/gate-check.js +74 -183
- package/src/commands/state/gate-decision.js +8 -42
- package/src/commands/state/handoff.js +4 -31
- package/src/commands/state/list.js +21 -91
- package/src/commands/state/report-check.js +134 -338
- package/src/commands/state/retype.js +12 -28
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +79 -19
- package/src/commands/validation/plan-check.js +81 -0
- package/src/commands/validation/validate-feature.js +52 -216
- package/src/commands/validation/verify.js +45 -160
- package/src/core/paths/output-schema.js +9 -155
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +2 -30
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +32 -395
- package/src/core/templates/template-registry.js +2 -97
- package/src/core/templates/template-renderer.js +13 -93
- package/src/core/templates/template-seed.js +21 -0
- package/src/core/work-types.js +10 -45
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +2 -8
- package/src/dashboard/app/components/CommandPalette.jsx +4 -11
- package/src/dashboard/app/components/DetailPanel.jsx +1 -5
- package/src/dashboard/app/components/EntityLink.jsx +0 -12
- package/src/dashboard/app/components/ErrorBoundary.jsx +1 -8
- package/src/dashboard/app/components/KnowledgeMarkdown.jsx +0 -11
- package/src/dashboard/app/components/Markdown.jsx +0 -9
- package/src/dashboard/app/components/PhaseRail.jsx +0 -11
- package/src/dashboard/app/components/SearchFilter.jsx +0 -4
- package/src/dashboard/app/components/Sidebar.jsx +0 -5
- package/src/dashboard/app/hooks/useMorphQuery.js +1 -11
- package/src/dashboard/app/hooks/usePref.js +1 -5
- package/src/dashboard/app/lib/format.js +0 -2
- package/src/dashboard/app/lib/phases.js +3 -9
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/pages/Activity.jsx +1 -1
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +139 -133
- package/src/dashboard/app/pages/Features.jsx +9 -16
- package/src/dashboard/app/pages/Guide.jsx +2 -3
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +3 -7
- package/src/dashboard/app/pages/Portfolio.jsx +0 -1
- package/src/dashboard/app/pages/RulesMap.jsx +1 -16
- package/src/dashboard/app/pages/SkillsLibrary.jsx +1 -16
- package/src/dashboard/app/pages/Standards.jsx +2 -17
- package/src/dashboard/scanner/activity-scanner.js +14 -83
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +1 -7
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +92 -113
- package/src/dashboard/scanner/hooks-scanner.js +5 -28
- package/src/dashboard/scanner/index.js +0 -4
- package/src/dashboard/scanner/portfolio.js +5 -29
- package/src/dashboard/scanner/project-api.js +8 -54
- package/src/dashboard/scanner/rules-scanner.js +1 -2
- package/src/dashboard/scanner/skills-scanner.js +2 -12
- package/src/dashboard/scanner/standards-scanner.js +4 -7
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/lib/ai/ai-checks.js +7 -52
- package/src/lib/ai/ai-project-scan.js +14 -108
- package/src/lib/ai/code-audit.js +7 -63
- package/src/lib/ai/pin-drift.js +8 -51
- package/src/lib/ai/registry-audit.js +11 -90
- package/src/lib/ci/ci-verify.js +14 -13
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +2 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +8 -59
- package/src/lib/e2e/compose-env.js +5 -66
- package/src/lib/e2e/stack-teardown.js +6 -40
- package/src/lib/generators/architecture-scanner.js +18 -156
- package/src/lib/generators/handoff-generator.js +37 -112
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +11 -122
- package/src/lib/git/branch-classifier.js +11 -84
- package/src/lib/git/default-branch.js +14 -169
- package/src/lib/git/evidence-staleness.js +2 -1
- package/src/lib/git/gh.js +2 -17
- package/src/lib/git/pending-prs.js +6 -37
- package/src/lib/git/pr-files.js +3 -28
- package/src/lib/git/pr-index.js +3 -34
- package/src/lib/git/tree-fingerprint.js +17 -17
- package/src/lib/git/tree-state.js +6 -33
- package/src/lib/graph/god-nodes.js +2 -22
- package/src/lib/graph/graph-health.js +3 -24
- package/src/lib/graph/graph-loader.js +5 -56
- package/src/lib/graph/graphify-runtime.js +7 -37
- package/src/lib/graph/graphifyignore.js +8 -74
- package/src/lib/graph/sanitize.js +16 -136
- package/src/lib/heavy/cleanup.js +2 -6
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +8 -33
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/commands.js +3 -2
- package/src/lib/mutation/dotnet-introspection.js +281 -0
- package/src/lib/mutation/plan.js +14 -6
- package/src/lib/mutation/runner.js +1 -1
- package/src/lib/mutation/subset.js +47 -13
- package/src/lib/plan/artifact-vocabulary.js +40 -0
- package/src/lib/plan/checks.js +208 -0
- package/src/lib/plan/plan-parser.js +204 -0
- package/src/lib/plan/plan-template.js +18 -0
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +204 -0
- package/src/lib/risk-detector.js +131 -274
- package/src/lib/specs/adr-index.js +38 -85
- package/src/lib/specs/design-seed.js +18 -0
- package/src/lib/specs/frontmatter.js +31 -0
- package/src/lib/specs/gfm-table.js +11 -0
- package/src/lib/specs/revocations.js +31 -102
- package/src/lib/specs/screen-checklist.js +56 -132
- package/src/lib/stack/dotnet-build-ignores.js +43 -0
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +260 -0
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +37 -0
- package/src/lib/standards/sections.js +47 -0
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +23 -179
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -313
- package/src/lib/tasks/test-runner.js +107 -447
- package/src/lib/telemetry/metrics.js +13 -103
- package/src/lib/telemetry/telemetry-reader.js +13 -238
- package/src/lib/telemetry/verify-event.js +4 -34
- package/src/lib/validators/code-budget/code-budget-validator.js +4 -44
- package/src/lib/validators/code-budget/index.js +0 -7
- package/src/lib/validators/contracts/contract-compliance-validator.js +4 -26
- package/src/lib/validators/contracts/index.js +0 -6
- package/src/lib/validators/nextjs/index.js +0 -5
- package/src/lib/validators/nextjs/next-component-validator.js +4 -81
- package/src/lib/validators/packages/index.js +0 -6
- package/src/lib/validators/packages/package-validator.js +6 -56
- package/src/lib/validators/reuse/index.js +0 -8
- package/src/lib/validators/reuse/reuse-validator.js +24 -137
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +54 -156
- package/src/lib/validators/validation-runner.js +20 -125
- package/src/lib/validators/verify-runner.js +152 -575
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -137
- package/src/lib/worktree/env-allocator.js +10 -98
- package/src/lib/worktree/feature-truth.js +12 -115
- package/src/lib/worktree/finish-backup.js +32 -242
- package/src/lib/worktree/fleet-scanner.js +31 -126
- package/src/lib/worktree/ignored-triage.js +15 -91
- package/src/lib/worktree/vscode-settings.js +2 -3
- package/src/lib/worktree/worktree-audit.js +21 -213
- package/src/lib/worktree/worktree-include.js +3 -5
- package/src/scripts/global-install.js +3 -9
- package/src/scripts/install-plugin.js +1 -13
- package/src/scripts/setup-infra.js +17 -138
- package/src/utils/agents-installer.js +92 -272
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -87
- package/src/utils/file-copier.js +32 -90
- package/src/utils/hooks-installer.js +26 -296
- package/src/utils/skills-installer.js +4 -102
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -57
- package/framework/evals/trace-schema.md +0 -67
- package/framework/hooks/claude-code/core/tasks-json-guard.js +0 -83
- package/framework/hooks/claude-code/core/trace-autogen.js +0 -277
- package/framework/hooks/git/pre-commit/specs.sh +0 -50
- package/framework/hooks/shared/tasks-validator.js +0 -111
- package/framework/rules/morph-task-tracking.md +0 -54
- package/framework/rules/morph-workflow.md +0 -244
- package/framework/schemas/tasks.schema.json +0 -79
- package/framework/skills/core/morph-brainstorming/SKILL.md +0 -161
- package/framework/skills/core/morph-implement/SKILL.md +0 -194
- package/framework/skills/core/morph-proposal/SKILL.md +0 -152
- package/framework/skills/core/morph-stack-scan/SKILL.md +0 -145
- package/framework/templates/docs/proposal.md +0 -192
- package/framework/templates/docs/spec.md +0 -57
- package/framework/templates/docs/ui-components.md +0 -52
- package/framework/templates/docs/ui-design-system.md +0 -76
- package/framework/templates/docs/ui-flows.md +0 -167
- package/framework/templates/examples/spec-examples.md +0 -91
- package/framework/templates/feature/decisions.md +0 -124
- package/framework/templates/feature/recap.md +0 -145
- package/framework/templates/tasks.template.json +0 -17
- package/src/commands/state/usecase.js +0 -143
- package/src/commands/tasks/dag.js +0 -181
- package/src/commands/utils/index.js +0 -3
- package/src/lib/agents/output-collector.js +0 -76
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/evaluator-report.js +0 -104
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/acervo-format.js +0 -70
- package/src/lib/specs/acervo-lifecycle.js +0 -264
- package/src/lib/specs/done-criteria-check.js +0 -200
- package/src/lib/specs/usecase-check.js +0 -301
- package/src/lib/specs/usecase-promoter.js +0 -301
- package/src/lib/specs/usecase-registry.js +0 -237
- package/src/lib/tasks/dispatch-planner.js +0 -319
- package/src/lib/tasks/dotnet-test-introspection.js +0 -632
- package/src/lib/tasks/mermaid-renderer.js +0 -132
- package/src/lib/tasks/task-classifier.js +0 -30
- package/src/lib/tasks/task-parser.js +0 -209
- package/src/lib/tasks/tasks-json-parser.js +0 -296
- package/src/lib/tasks/test-scope.js +0 -597
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,20 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Code Budget Validator — o que a feature adicionou além do código.
|
|
3
|
-
*
|
|
4
|
-
* Mede, só nas linhas que esta árvore adicionou contra a branch default:
|
|
5
|
-
* (a) linhas de comentário que citam processo (task, UC, ADR, Gate...), que o
|
|
6
|
-
* standard `architecture-code-comments` manda para o commit;
|
|
7
|
-
* (b) linhas de teste ÷ linhas de produção, contra o orçamento por task do
|
|
8
|
-
* `morph-plan` (um teste por pós-condição + um de regressão por bug);
|
|
9
|
-
* (c) linhas de comentário ÷ linhas de código, porque o padrão do standard é
|
|
10
|
-
* nenhum comentário. Doc-comment (`///`, JSDoc) conta como qualquer outro.
|
|
11
|
-
*
|
|
12
|
-
* Sempre `warning`: é medida de volume, e o excesso pode estar justificado na
|
|
13
|
-
* task. Um aviso por medida, não por linha, porque cada aviso custa 1 ponto no
|
|
14
|
-
* score do validador. Qualquer falha de medição vira skip com razão.
|
|
15
|
-
*
|
|
16
|
-
* @module code-budget-validator
|
|
17
|
-
*/
|
|
1
|
+
// Sempre `warning`: é medida de volume, e o excesso pode estar justificado no plano.
|
|
18
2
|
|
|
19
3
|
import { readFileSync } from 'fs';
|
|
20
4
|
import { join } from 'path';
|
|
@@ -25,31 +9,21 @@ import { isTestFile } from '../reuse/symbol-scanner.js';
|
|
|
25
9
|
import { isGenerated, isProjectSource } from '../../../../framework/hooks/shared/source-files.js';
|
|
26
10
|
import { citesProcess, commentLines } from '../../../../framework/hooks/shared/comment-scanner.js';
|
|
27
11
|
|
|
28
|
-
/** Razão teste ÷ produção acima da qual se avisa. */
|
|
29
12
|
export const MAX_TEST_RATIO = 1.5;
|
|
30
13
|
|
|
31
14
|
/** Abaixo disto a razão não é julgada: a regressão de um bug de 2 linhas cabe aqui. */
|
|
32
15
|
export const MIN_TEST_LINES = 40;
|
|
33
16
|
|
|
34
|
-
/** Mais de uma linha de comentário a cada dez de código: os nomes não estão fazendo o trabalho. */
|
|
35
17
|
export const MAX_COMMENT_RATIO = 0.1;
|
|
36
18
|
|
|
37
19
|
/** Abaixo disto a densidade não é julgada: um comentário legítimo num arquivo pequeno já passaria da razão. */
|
|
38
20
|
export const MIN_CODE_LINES = 40;
|
|
39
21
|
|
|
40
|
-
/** Quantos locais citar no aviso de comentário antes de resumir com "(+N)". */
|
|
41
22
|
const MAX_CITED = 4;
|
|
42
23
|
|
|
43
24
|
const decimal = (n) => n.toFixed(1).replace('.', ',');
|
|
44
25
|
|
|
45
|
-
/**
|
|
46
|
-
* Puro, sem I/O.
|
|
47
|
-
*
|
|
48
|
-
* @param {Array<{path: string, content: string, added: number[]|null}>} files -
|
|
49
|
-
* `added` são as linhas adicionadas; `null` é o arquivo inteiro (novo)
|
|
50
|
-
* @returns {{commentLines: number, processLines: Array<{file: string, line: number, text: string}>,
|
|
51
|
-
* testLines: number, productionLines: number, commentsByFile: Record<string, number>}}
|
|
52
|
-
*/
|
|
26
|
+
/** `added` são as linhas adicionadas de cada arquivo; `null` é o arquivo inteiro (novo). */
|
|
53
27
|
export function measureCodeBudget(files) {
|
|
54
28
|
const measured = { commentLines: 0, processLines: [], testLines: 0, productionLines: 0, commentsByFile: {} };
|
|
55
29
|
|
|
@@ -74,10 +48,6 @@ export function measureCodeBudget(files) {
|
|
|
74
48
|
return measured;
|
|
75
49
|
}
|
|
76
50
|
|
|
77
|
-
/**
|
|
78
|
-
* @param {ReturnType<typeof measureCodeBudget>} measured
|
|
79
|
-
* @returns {Array<object>} avisos no formato do validation-runner
|
|
80
|
-
*/
|
|
81
51
|
export function budgetIssues(measured) {
|
|
82
52
|
const issues = [];
|
|
83
53
|
const { commentLines: comments, processLines, testLines, productionLines, commentsByFile = {} } = measured;
|
|
@@ -89,11 +59,11 @@ export function budgetIssues(measured) {
|
|
|
89
59
|
level: 'warning',
|
|
90
60
|
message:
|
|
91
61
|
`${processLines.length} de ${comments} linhas de comentário adicionadas citam processo ` +
|
|
92
|
-
`(UC, ADR,
|
|
62
|
+
`(UC, ADR, FR, C{n}, F{n}, Gate, plan.md, checks.json, mockup): ${shown.join(', ')}${more > 0 ? ` (+${more})` : ''}`,
|
|
93
63
|
file: processLines[0].file,
|
|
94
64
|
line: processLines[0].line,
|
|
95
65
|
solution:
|
|
96
|
-
'A história vai na mensagem de commit e no
|
|
66
|
+
'A história vai na mensagem de commit e no plan.md; no código fica só o porquê que ' +
|
|
97
67
|
'ele não mostra (standard architecture-code-comments).',
|
|
98
68
|
});
|
|
99
69
|
}
|
|
@@ -133,16 +103,6 @@ export function budgetIssues(measured) {
|
|
|
133
103
|
return issues;
|
|
134
104
|
}
|
|
135
105
|
|
|
136
|
-
/**
|
|
137
|
-
* Validador plugado no `validation-runner` (e por ele no nó `validators` do
|
|
138
|
-
* `morph-spec verify`).
|
|
139
|
-
*
|
|
140
|
-
* @param {string} projectPath
|
|
141
|
-
* @param {string|null} [featureName] - não usado: a medida é da árvore inteira contra a default
|
|
142
|
-
* @param {object} [options]
|
|
143
|
-
* @param {Map<string, number[]|null>} [options.addedLines] - override da coleta (testes)
|
|
144
|
-
* @returns {Promise<{errors: number, warnings: number, issues: object[], _skipReason?: string}>}
|
|
145
|
-
*/
|
|
146
106
|
export async function validateCodeBudget(projectPath, featureName = null, options = {}) {
|
|
147
107
|
const result = { errors: 0, warnings: 0, issues: [] };
|
|
148
108
|
|
|
@@ -1,9 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Code Budget Validators
|
|
3
|
-
*
|
|
4
|
-
* Volume que a feature adicionou além do código: comentário que conta a
|
|
5
|
-
* história do processo e teste acima do orçamento (ver code-budget-validator.js).
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
export * from './code-budget-validator.js';
|
|
9
2
|
export * from '../../../../framework/hooks/shared/comment-scanner.js';
|
|
@@ -9,16 +9,15 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
9
9
|
|
|
10
10
|
const skipped = (message) => ({ status: 'ok', errors: 0, warnings: 0, issues: [], coverage: null, message });
|
|
11
11
|
|
|
12
|
-
if (!existsSync(contractsPath)) return skipped('No
|
|
12
|
+
if (!existsSync(contractsPath)) return skipped('No plan.md found — skipping');
|
|
13
13
|
|
|
14
14
|
const contractsContent = readFileSync(contractsPath, 'utf-8');
|
|
15
15
|
const contracts = parseContracts(contractsContent);
|
|
16
16
|
|
|
17
17
|
if (contracts.interfaces.length === 0 && contracts.records.length === 0 && contracts.enums.length === 0) {
|
|
18
|
-
return skipped('
|
|
18
|
+
return skipped('plan.md declares no interfaces/records/enums — skipping');
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
// Find all .cs files in the project (excluding .morph outputs, bin, obj)
|
|
22
21
|
const csFiles = await glob('**/*.cs', {
|
|
23
22
|
cwd: projectPath,
|
|
24
23
|
ignore: ['.morph/**', '**/bin/**', '**/obj/**', '**/node_modules/**']
|
|
@@ -30,7 +29,6 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
30
29
|
try {
|
|
31
30
|
projectCode[file] = readFileSync(fullPath, 'utf-8');
|
|
32
31
|
} catch {
|
|
33
|
-
// Skip unreadable files
|
|
34
32
|
}
|
|
35
33
|
}
|
|
36
34
|
|
|
@@ -39,7 +37,6 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
39
37
|
let total = 0;
|
|
40
38
|
const interfaceResults = [];
|
|
41
39
|
|
|
42
|
-
// Validate interfaces
|
|
43
40
|
for (const iface of contracts.interfaces) {
|
|
44
41
|
total++;
|
|
45
42
|
const result = validateInterface(iface, projectCode, options);
|
|
@@ -58,7 +55,6 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
58
55
|
});
|
|
59
56
|
}
|
|
60
57
|
|
|
61
|
-
// Check for missing methods
|
|
62
58
|
for (const method of result.missingMethods || []) {
|
|
63
59
|
issues.push({
|
|
64
60
|
level: 'error',
|
|
@@ -69,7 +65,6 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
// Validate records/DTOs
|
|
73
68
|
for (const record of contracts.records) {
|
|
74
69
|
total++;
|
|
75
70
|
const found = Object.entries(projectCode).some(([, content]) =>
|
|
@@ -90,7 +85,6 @@ export async function validateContracts(projectPath, featureName, options = {})
|
|
|
90
85
|
}
|
|
91
86
|
}
|
|
92
87
|
|
|
93
|
-
// Validate enums
|
|
94
88
|
for (const enumDef of contracts.enums) {
|
|
95
89
|
total++;
|
|
96
90
|
const found = Object.entries(projectCode).some(([, content]) =>
|
|
@@ -138,7 +132,6 @@ function parseContracts(content) {
|
|
|
138
132
|
const records = [];
|
|
139
133
|
const enums = [];
|
|
140
134
|
|
|
141
|
-
// Extract interfaces with their methods
|
|
142
135
|
const interfaceRegex = /public\s+interface\s+(I\w+)(?:\s*:\s*[\w<>,\s]+)?\s*\{([^}]*)\}/gs;
|
|
143
136
|
let match;
|
|
144
137
|
|
|
@@ -150,18 +143,15 @@ function parseContracts(content) {
|
|
|
150
143
|
interfaces.push({ name, methods });
|
|
151
144
|
}
|
|
152
145
|
|
|
153
|
-
// Extract records/classes (DTOs)
|
|
154
146
|
const recordRegex = /public\s+(record|class|struct)\s+(\w+)/g;
|
|
155
147
|
while ((match = recordRegex.exec(content)) !== null) {
|
|
156
148
|
const type = match[1];
|
|
157
149
|
const name = match[2];
|
|
158
|
-
// Skip interface names that were already captured
|
|
159
150
|
if (!name.startsWith('I') || !interfaces.some(i => i.name === name)) {
|
|
160
151
|
records.push({ type, name });
|
|
161
152
|
}
|
|
162
153
|
}
|
|
163
154
|
|
|
164
|
-
// Extract enums
|
|
165
155
|
const enumRegex = /public\s+enum\s+(\w+)/g;
|
|
166
156
|
while ((match = enumRegex.exec(content)) !== null) {
|
|
167
157
|
enums.push({ name: match[1] });
|
|
@@ -170,9 +160,6 @@ function parseContracts(content) {
|
|
|
170
160
|
return { interfaces, records, enums };
|
|
171
161
|
}
|
|
172
162
|
|
|
173
|
-
/**
|
|
174
|
-
* Extract method signatures from an interface body
|
|
175
|
-
*/
|
|
176
163
|
function extractMethods(body) {
|
|
177
164
|
const methods = [];
|
|
178
165
|
|
|
@@ -182,7 +169,6 @@ function extractMethods(body) {
|
|
|
182
169
|
|
|
183
170
|
while ((match = methodRegex.exec(body)) !== null) {
|
|
184
171
|
const methodName = match[1];
|
|
185
|
-
// Skip property getters/setters
|
|
186
172
|
if (!['get', 'set', 'init'].includes(methodName)) {
|
|
187
173
|
methods.push(methodName);
|
|
188
174
|
}
|
|
@@ -191,9 +177,6 @@ function extractMethods(body) {
|
|
|
191
177
|
return methods;
|
|
192
178
|
}
|
|
193
179
|
|
|
194
|
-
/**
|
|
195
|
-
* Validate that an interface is implemented in project code
|
|
196
|
-
*/
|
|
197
180
|
function validateInterface(iface, projectCode, options) {
|
|
198
181
|
const result = {
|
|
199
182
|
name: iface.name,
|
|
@@ -205,7 +188,6 @@ function validateInterface(iface, projectCode, options) {
|
|
|
205
188
|
missingMethods: []
|
|
206
189
|
};
|
|
207
190
|
|
|
208
|
-
// Search for classes implementing this interface
|
|
209
191
|
const implRegex = new RegExp(`class\\s+(\\w+)\\s*(?:<[^>]*>)?\\s*:\\s*[^{]*${escapeRegex(iface.name)}`, 'g');
|
|
210
192
|
|
|
211
193
|
for (const [file, content] of Object.entries(projectCode)) {
|
|
@@ -215,7 +197,6 @@ function validateInterface(iface, projectCode, options) {
|
|
|
215
197
|
result.implementingClass = match[1];
|
|
216
198
|
result.implementingFile = file;
|
|
217
199
|
|
|
218
|
-
// Check which methods are implemented
|
|
219
200
|
for (const method of iface.methods) {
|
|
220
201
|
const methodExists = new RegExp(`(public|protected|private|internal)?\\s*(async\\s+)?\\S+\\s+${escapeRegex(method)}\\s*\\(`, 'g').test(content);
|
|
221
202
|
if (methodExists) {
|
|
@@ -225,17 +206,14 @@ function validateInterface(iface, projectCode, options) {
|
|
|
225
206
|
}
|
|
226
207
|
}
|
|
227
208
|
|
|
228
|
-
break;
|
|
209
|
+
break;
|
|
229
210
|
}
|
|
230
|
-
implRegex.lastIndex = 0;
|
|
211
|
+
implRegex.lastIndex = 0;
|
|
231
212
|
}
|
|
232
213
|
|
|
233
214
|
return result;
|
|
234
215
|
}
|
|
235
216
|
|
|
236
|
-
/**
|
|
237
|
-
* Escape special regex characters in a string
|
|
238
|
-
*/
|
|
239
217
|
function escapeRegex(str) {
|
|
240
218
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
241
219
|
}
|
|
@@ -2,25 +2,8 @@ import { glob } from 'glob';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Next.js Component Validator
|
|
7
|
-
*
|
|
8
|
-
* Validates .tsx files for:
|
|
9
|
-
* 1. Unnecessary 'use client' directives (no interactivity)
|
|
10
|
-
* 2. Missing 'use client' when React hooks are used
|
|
11
|
-
* 3. File naming convention (kebab-case)
|
|
12
|
-
*
|
|
13
|
-
* @module next-component-validator
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
// ============================================
|
|
17
|
-
// CONSTANTS
|
|
18
|
-
// ============================================
|
|
19
|
-
|
|
20
|
-
/** Hooks/patterns that require 'use client' */
|
|
21
5
|
const CLIENT_HOOKS = ['useState', 'useEffect', 'useRef', 'useReducer', 'useCallback', 'useMemo', 'useContext'];
|
|
22
6
|
|
|
23
|
-
/** Event handler patterns that indicate interactivity (hooks are checked via CLIENT_HOOKS) */
|
|
24
7
|
const EVENT_HANDLER_PATTERNS = [
|
|
25
8
|
/\bonClick\b/,
|
|
26
9
|
/\bonChange\b/,
|
|
@@ -28,66 +11,29 @@ const EVENT_HANDLER_PATTERNS = [
|
|
|
28
11
|
/\bonFocus\b/,
|
|
29
12
|
/\bonBlur\b/,
|
|
30
13
|
/\bonKeyDown\b/,
|
|
31
|
-
// Qualquer outro handler de evento em JSX (onMouseEnter, onKeyUp, onScroll, onDrop…).
|
|
32
|
-
// Sem isto, a lista fechada acima transforma interatividade legítima em falso-positivo.
|
|
33
14
|
/\bon[A-Z]\w*\s*=\s*\{/,
|
|
34
15
|
];
|
|
35
16
|
|
|
36
|
-
|
|
37
|
-
* Chamada de QUALQUER hook, não só os sete nativos: `useRouter()`, `useQuery()`, `useForm<T>()`.
|
|
38
|
-
* O `[<(]` é o que faz `useState<Record<string, string>>(` contar — um hook com argumento de
|
|
39
|
-
* tipo não casa com `useState\s*\(` e passava despercebido.
|
|
40
|
-
*/
|
|
17
|
+
// `[<(]` faz hook com argumento de tipo (`useState<Record<string, string>>(`) contar.
|
|
41
18
|
const HOOK_CALL_PATTERN = /\buse[A-Z]\w*\s*[<(]/;
|
|
42
19
|
|
|
43
|
-
|
|
44
|
-
* APIs de React que só existem no cliente. `forwardRef` e `createContext` lançam em Server
|
|
45
|
-
* Component, então um módulo que os usa PRECISA da diretiva, mesmo sem hook nem handler.
|
|
46
|
-
*/
|
|
20
|
+
// Lançam em Server Component: o módulo precisa da diretiva mesmo sem hook nem handler.
|
|
47
21
|
const CLIENT_ONLY_APIS = [/\bforwardRef\b/, /\bcreateContext\b/];
|
|
48
22
|
|
|
49
|
-
/** Globais de browser: quem os toca não roda no servidor. */
|
|
50
23
|
const BROWSER_GLOBALS = /\b(window|document|localStorage|sessionStorage|navigator)\s*[.[]/;
|
|
51
24
|
|
|
52
|
-
|
|
53
|
-
* Pacotes cujos componentes são client-only. O caso que mais gerava ruído é o wrapper de
|
|
54
|
-
* shadcn/Radix: ele só reexporta primitivas, então não tem hook nem handler no próprio
|
|
55
|
-
* arquivo — e era acusado para sempre, apesar de estar correto.
|
|
56
|
-
*/
|
|
25
|
+
// Wrapper shadcn/Radix só reexporta primitivas client-only, sem hook nem handler no arquivo.
|
|
57
26
|
const CLIENT_ONLY_IMPORTS = /from\s+['"](@radix-ui\/|sonner|@react-pdf\/|file-saver|framer-motion|motion\/|recharts|next\/navigation|react-hook-form|@tanstack\/react-query|embla-carousel|cmdk|vaul|react-day-picker|input-otp|@dnd-kit\/)/;
|
|
58
27
|
|
|
59
|
-
/** Next.js special files exempt from naming checks */
|
|
60
28
|
const NEXTJS_SPECIAL_FILES = ['page.tsx', 'layout.tsx', 'loading.tsx', 'error.tsx', 'not-found.tsx', 'route.ts', 'middleware.ts'];
|
|
61
29
|
|
|
62
|
-
// ============================================
|
|
63
|
-
// MAIN VALIDATION FUNCTION
|
|
64
|
-
// ============================================
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* @typedef {Object} ValidationIssue
|
|
68
|
-
* @property {'error' | 'warning' | 'info'} type
|
|
69
|
-
* @property {string} message
|
|
70
|
-
* @property {string} [suggestion]
|
|
71
|
-
* @property {string} file
|
|
72
|
-
* @property {number} [line]
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Validates a Next.js component file.
|
|
77
|
-
*
|
|
78
|
-
* @param {string} content - File content
|
|
79
|
-
* @param {string} filePath - Relative file path
|
|
80
|
-
* @returns {ValidationIssue[]}
|
|
81
|
-
*/
|
|
82
30
|
export function validateNextComponent(content, filePath) {
|
|
83
31
|
const issues = [];
|
|
84
32
|
|
|
85
|
-
// Only validate .tsx and .ts files
|
|
86
33
|
if (!filePath.endsWith('.tsx') && !filePath.endsWith('.ts')) {
|
|
87
34
|
return issues;
|
|
88
35
|
}
|
|
89
36
|
|
|
90
|
-
// Skip non-component utility files (no JSX, no hooks)
|
|
91
37
|
const hasJsx = /(?<![a-zA-Z0-9])<[A-Z][a-zA-Z]*[\s/>]|(?<![a-zA-Z0-9])<[a-z][a-z]+[\s>/]/.test(content);
|
|
92
38
|
const hasHooks = CLIENT_HOOKS.some(hook => content.includes(hook));
|
|
93
39
|
|
|
@@ -97,13 +43,10 @@ export function validateNextComponent(content, filePath) {
|
|
|
97
43
|
|
|
98
44
|
const hasUseClient = /^'use client';?|^"use client";?/.test(content.trimStart());
|
|
99
45
|
|
|
100
|
-
// Check 1: use client present but no interactivity
|
|
101
46
|
if (hasUseClient) {
|
|
102
47
|
const hasEventHandlers = EVENT_HANDLER_PATTERNS.some(pattern => pattern.test(content));
|
|
103
48
|
const hasClientHooks = CLIENT_HOOKS.some(hook => new RegExp(`\\b${hook}\\b`).test(content));
|
|
104
|
-
//
|
|
105
|
-
// pede a remoção de uma diretiva que o build precisa, então a checagem erra para o lado
|
|
106
|
-
// de ficar calada.
|
|
49
|
+
// Falso-positivo pediria remover uma diretiva que o build precisa: erra para o lado calado.
|
|
107
50
|
const needsClient =
|
|
108
51
|
hasEventHandlers ||
|
|
109
52
|
hasClientHooks ||
|
|
@@ -121,7 +64,6 @@ export function validateNextComponent(content, filePath) {
|
|
|
121
64
|
}
|
|
122
65
|
}
|
|
123
66
|
|
|
124
|
-
// Check 2: hooks used without use client
|
|
125
67
|
// .ts files (custom hooks, utilities) never render JSX — skip this check
|
|
126
68
|
if (!hasUseClient) {
|
|
127
69
|
if (!filePath.endsWith('.tsx')) return issues;
|
|
@@ -137,7 +79,6 @@ export function validateNextComponent(content, filePath) {
|
|
|
137
79
|
}
|
|
138
80
|
}
|
|
139
81
|
|
|
140
|
-
// Check 3: file naming convention (kebab-case)
|
|
141
82
|
const fileName = filePath.split('/').pop() ?? '';
|
|
142
83
|
const isSpecialFile = NEXTJS_SPECIAL_FILES.includes(fileName);
|
|
143
84
|
|
|
@@ -157,10 +98,6 @@ export function validateNextComponent(content, filePath) {
|
|
|
157
98
|
return issues;
|
|
158
99
|
}
|
|
159
100
|
|
|
160
|
-
// ============================================
|
|
161
|
-
// HELPERS
|
|
162
|
-
// ============================================
|
|
163
|
-
|
|
164
101
|
function toKebabCase(str) {
|
|
165
102
|
return str
|
|
166
103
|
.replace(/([A-Z])/g, '-$1')
|
|
@@ -168,22 +105,9 @@ function toKebabCase(str) {
|
|
|
168
105
|
.replace(/^-/, '');
|
|
169
106
|
}
|
|
170
107
|
|
|
171
|
-
// ============================================
|
|
172
|
-
// PROJECT-LEVEL SCANNER
|
|
173
|
-
// ============================================
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Validates all Next.js component files in a project directory.
|
|
177
|
-
* Wraps validateNextComponent for use in validation-runner.js.
|
|
178
|
-
*
|
|
179
|
-
* @param {string} projectPath - Root path of the project
|
|
180
|
-
* @param {Object} _options - Validator options (unused, for interface compatibility)
|
|
181
|
-
* @returns {Promise<{ errors: number, warnings: number, issues: Array }>}
|
|
182
|
-
*/
|
|
183
108
|
export async function validateNextComponentFiles(projectPath, _options = {}) {
|
|
184
109
|
const result = { errors: 0, warnings: 0, issues: [] };
|
|
185
110
|
|
|
186
|
-
// Find all .tsx and .ts files under src/
|
|
187
111
|
const srcDir = join(projectPath, 'src');
|
|
188
112
|
const normalizedSrc = srcDir.replace(/\\/g, '/');
|
|
189
113
|
const pattern = normalizedSrc + '/**/*.{tsx,ts}';
|
|
@@ -197,7 +121,6 @@ export async function validateNextComponentFiles(projectPath, _options = {}) {
|
|
|
197
121
|
continue;
|
|
198
122
|
}
|
|
199
123
|
|
|
200
|
-
// Make path relative for cleaner output
|
|
201
124
|
const normFilePath = filePath.replace(/\\/g, '/');
|
|
202
125
|
const relPath = normFilePath.replace(projectPath.replace(/\\/g, '/') + '/', '');
|
|
203
126
|
const fileIssues = validateNextComponent(content, relPath);
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Package Compatibility Validator
|
|
3
|
-
*
|
|
4
|
-
* Validates NuGet package versions against .NET compatibility matrix.
|
|
5
|
-
* Critical for .NET 10 migration - prevents runtime errors.
|
|
6
|
-
*
|
|
7
|
-
* MORPH-SPEC 3.0 - Sprint 4
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { readFileSync } from 'fs';
|
|
11
2
|
import { join } from 'path';
|
|
12
3
|
import { glob } from 'glob';
|
|
13
4
|
import chalk from 'chalk';
|
|
14
5
|
|
|
15
|
-
|
|
16
|
-
* Compatibility matrix for .NET versions
|
|
17
|
-
* Source: framework/standards/dotnet10-compatibility.md
|
|
18
|
-
*/
|
|
6
|
+
// Source: framework/standards/dotnet10-compatibility.md
|
|
19
7
|
const COMPATIBILITY_MATRIX = {
|
|
20
8
|
'Hangfire.AspNetCore': {
|
|
21
9
|
9: '1.8.0',
|
|
@@ -50,7 +38,7 @@ const COMPATIBILITY_MATRIX = {
|
|
|
50
38
|
breaking: false
|
|
51
39
|
},
|
|
52
40
|
'Microsoft.SemanticKernel': {
|
|
53
|
-
9: null,
|
|
41
|
+
9: null,
|
|
54
42
|
10: null,
|
|
55
43
|
deprecated: true,
|
|
56
44
|
replacement: 'Microsoft.Extensions.AI.Abstractions',
|
|
@@ -58,17 +46,11 @@ const COMPATIBILITY_MATRIX = {
|
|
|
58
46
|
}
|
|
59
47
|
};
|
|
60
48
|
|
|
61
|
-
/**
|
|
62
|
-
* Package Validator Class
|
|
63
|
-
*/
|
|
64
49
|
export class PackageValidator {
|
|
65
50
|
constructor(projectPath = '.') {
|
|
66
51
|
this.projectPath = projectPath;
|
|
67
52
|
}
|
|
68
53
|
|
|
69
|
-
/**
|
|
70
|
-
* Validate all .csproj files in project
|
|
71
|
-
*/
|
|
72
54
|
async validateAll() {
|
|
73
55
|
const csprojFiles = await glob('**/*.csproj', {
|
|
74
56
|
cwd: this.projectPath,
|
|
@@ -86,10 +68,7 @@ export class PackageValidator {
|
|
|
86
68
|
const allIssues = [];
|
|
87
69
|
|
|
88
70
|
for (const file of csprojFiles) {
|
|
89
|
-
// glob
|
|
90
|
-
// resolve against projectPath — reading them relative to process.cwd()
|
|
91
|
-
// raised ENOENT whenever the two differed (worktree, fleet, a verify
|
|
92
|
-
// launched from the primary root), and the throw read as a clean pass.
|
|
71
|
+
// glob paths are relative to `cwd: this.projectPath`, never to process.cwd().
|
|
93
72
|
const result = await this.validateFile(join(this.projectPath, file));
|
|
94
73
|
if (result.issues.length > 0) {
|
|
95
74
|
results.push({ file, ...result });
|
|
@@ -101,9 +80,7 @@ export class PackageValidator {
|
|
|
101
80
|
status: results.length === 0 ? 'ok' : 'error',
|
|
102
81
|
totalFiles: csprojFiles.length,
|
|
103
82
|
filesWithIssues: results.length,
|
|
104
|
-
// The runner's contract
|
|
105
|
-
// read as `errors: 0` and the run reported "✅ All validations passed"
|
|
106
|
-
// while holding a list of NuGet packages that do not build on .NET 10.
|
|
83
|
+
// The runner's contract: without these counts the findings read as `errors: 0`.
|
|
107
84
|
errors: allIssues.filter(i => i.level === 'error').length,
|
|
108
85
|
warnings: allIssues.filter(i => i.level === 'warning').length,
|
|
109
86
|
issues: allIssues,
|
|
@@ -111,13 +88,9 @@ export class PackageValidator {
|
|
|
111
88
|
};
|
|
112
89
|
}
|
|
113
90
|
|
|
114
|
-
/**
|
|
115
|
-
* Validate single .csproj file
|
|
116
|
-
*/
|
|
117
91
|
async validateFile(filePath) {
|
|
118
92
|
const content = readFileSync(filePath, 'utf-8');
|
|
119
93
|
|
|
120
|
-
// Extract .NET version
|
|
121
94
|
const tfmMatch = content.match(/<TargetFramework>(.*?)<\/TargetFramework>/);
|
|
122
95
|
if (!tfmMatch) {
|
|
123
96
|
return {
|
|
@@ -142,10 +115,8 @@ export class PackageValidator {
|
|
|
142
115
|
};
|
|
143
116
|
}
|
|
144
117
|
|
|
145
|
-
// Extract package references
|
|
146
118
|
const packages = this.extractPackages(content);
|
|
147
119
|
|
|
148
|
-
// Validate each package
|
|
149
120
|
const issues = [];
|
|
150
121
|
|
|
151
122
|
for (const pkg of packages) {
|
|
@@ -162,9 +133,6 @@ export class PackageValidator {
|
|
|
162
133
|
};
|
|
163
134
|
}
|
|
164
135
|
|
|
165
|
-
/**
|
|
166
|
-
* Extract package references from .csproj content
|
|
167
|
-
*/
|
|
168
136
|
extractPackages(csprojContent) {
|
|
169
137
|
const packages = [];
|
|
170
138
|
const packageRegex = /<PackageReference\s+Include="([^"]+)"\s+Version="([^"]+)"/g;
|
|
@@ -180,18 +148,13 @@ export class PackageValidator {
|
|
|
180
148
|
return packages;
|
|
181
149
|
}
|
|
182
150
|
|
|
183
|
-
/**
|
|
184
|
-
* Validate single package against compatibility matrix
|
|
185
|
-
*/
|
|
186
151
|
validatePackage(pkg, dotnetVersion) {
|
|
187
152
|
const rule = COMPATIBILITY_MATRIX[pkg.name];
|
|
188
153
|
|
|
189
154
|
if (!rule) {
|
|
190
|
-
// Package not in matrix - no validation
|
|
191
155
|
return null;
|
|
192
156
|
}
|
|
193
157
|
|
|
194
|
-
// Check if deprecated
|
|
195
158
|
if (rule.deprecated) {
|
|
196
159
|
return {
|
|
197
160
|
level: 'error',
|
|
@@ -204,7 +167,6 @@ export class PackageValidator {
|
|
|
204
167
|
};
|
|
205
168
|
}
|
|
206
169
|
|
|
207
|
-
// Check if version is sufficient
|
|
208
170
|
const requiredVersion = rule[dotnetVersion];
|
|
209
171
|
|
|
210
172
|
if (!requiredVersion) {
|
|
@@ -235,13 +197,10 @@ export class PackageValidator {
|
|
|
235
197
|
};
|
|
236
198
|
}
|
|
237
199
|
|
|
238
|
-
return null;
|
|
200
|
+
return null;
|
|
239
201
|
}
|
|
240
202
|
|
|
241
|
-
|
|
242
|
-
* Compare semantic versions
|
|
243
|
-
* Returns: -1 (v1 < v2), 0 (equal), 1 (v1 > v2)
|
|
244
|
-
*/
|
|
203
|
+
// -1 (v1 < v2), 0 (equal), 1 (v1 > v2)
|
|
245
204
|
compareVersions(v1, v2) {
|
|
246
205
|
const parts1 = v1.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);
|
|
247
206
|
const parts2 = v2.split('.').map(p => parseInt(p.replace(/[^0-9]/g, '')) || 0);
|
|
@@ -259,9 +218,6 @@ export class PackageValidator {
|
|
|
259
218
|
return 0;
|
|
260
219
|
}
|
|
261
220
|
|
|
262
|
-
/**
|
|
263
|
-
* Format validation results for console output
|
|
264
|
-
*/
|
|
265
221
|
formatResults(results) {
|
|
266
222
|
if (results.status === 'ok') {
|
|
267
223
|
console.log(chalk.green('✅ All packages are compatible'));
|
|
@@ -314,9 +270,6 @@ export class PackageValidator {
|
|
|
314
270
|
}
|
|
315
271
|
}
|
|
316
272
|
|
|
317
|
-
/**
|
|
318
|
-
* Auto-fix compatible issues
|
|
319
|
-
*/
|
|
320
273
|
async autoFix(results) {
|
|
321
274
|
const { execSync } = await import('child_process');
|
|
322
275
|
let fixedCount = 0;
|
|
@@ -340,9 +293,6 @@ export class PackageValidator {
|
|
|
340
293
|
}
|
|
341
294
|
}
|
|
342
295
|
|
|
343
|
-
/**
|
|
344
|
-
* Quick validation function (for imports)
|
|
345
|
-
*/
|
|
346
296
|
export async function validatePackages(projectPath = '.', options = {}) {
|
|
347
297
|
const validator = new PackageValidator(projectPath);
|
|
348
298
|
const results = await validator.validateAll();
|
|
@@ -1,10 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reuse Validators
|
|
3
|
-
*
|
|
4
|
-
* Cobrança determinística de reuso de código: símbolo novo cujo nome já existe
|
|
5
|
-
* no grafo do codebase, na mesma linguagem. É a terceira camada do ciclo
|
|
6
|
-
* PROSA → ARTEFATO → VERIFICAÇÃO (ver reuse-validator.js).
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
1
|
export * from './reuse-validator.js';
|
|
10
2
|
export * from './symbol-scanner.js';
|