@polymorphism-tech/morph-spec 9.0.0 → 9.2.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 +8 -9
- package/README.md +38 -36
- package/bin/morph-spec.js +11 -63
- package/bin/validate.js +1 -39
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/CLAUDE.md +2 -1
- package/framework/MORPH.md +3 -3
- package/framework/agents.json +19 -11
- package/framework/commands/morph-apply.md +1 -1
- package/framework/commands/morph-status.md +1 -1
- package/framework/docs/CLI.md +22 -31
- package/framework/evals/review-checklist.md +12 -2
- package/framework/hooks/README.md +8 -44
- 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 +2 -16
- package/framework/hooks/claude-code/core/enforce-phase-writes.js +6 -50
- package/framework/hooks/claude-code/core/gate-guard.js +7 -67
- 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 +0 -4
- package/framework/hooks/claude-code/core/protect-spec-files.js +7 -67
- package/framework/hooks/claude-code/core/state-sync.js +12 -68
- package/framework/hooks/claude-code/core/telemetry-log.js +12 -93
- package/framework/hooks/claude-code/core/validate-completion.js +11 -57
- package/framework/hooks/claude-code/core/warn-bash-source-write.js +1 -9
- package/framework/hooks/claude-code/core/worktree-context.js +10 -110
- 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.sh +11 -11
- 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 +6 -75
- package/framework/hooks/shared/claim-registry.js +9 -119
- package/framework/hooks/shared/command-tokens.js +6 -51
- package/framework/hooks/shared/comment-rules.js +5 -20
- package/framework/hooks/shared/comment-scanner.js +6 -25
- package/framework/hooks/shared/comment-stripper.js +11 -85
- package/framework/hooks/shared/e2e-check.js +0 -0
- package/framework/hooks/shared/errata.js +2 -15
- package/framework/hooks/shared/feature-locator.js +11 -95
- package/framework/hooks/shared/frontend-locations.js +1 -3
- package/framework/hooks/shared/gate-question.js +1 -7
- 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 +1 -42
- 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 +4 -23
- package/framework/hooks/shared/source-files.js +4 -17
- package/framework/hooks/shared/state-reader.js +13 -183
- package/framework/hooks/shared/state-writer.js +8 -52
- 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/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/rules/nextjs-standards.md +1 -1
- package/framework/skills/core/create-umbraco-lp/SKILL.md +1 -1
- package/framework/skills/core/migrate-lp-to-umbraco/SKILL.md +1 -1
- package/framework/skills/core/morph-apply/SKILL.md +4 -4
- package/framework/skills/core/morph-hotfix/SKILL.md +3 -3
- package/framework/skills/core/morph-machine/SKILL.md +1 -1
- package/framework/skills/core/morph-media/SKILL.md +1 -1
- package/framework/skills/core/morph-plan/SKILL.md +9 -7
- package/framework/skills/core/morph-review/SKILL.md +9 -6
- package/framework/skills/core/morph-standards/SKILL.md +1 -1
- package/framework/skills/core/morph-uiux/SKILL.md +29 -12
- package/framework/standards/STANDARDS.json +36 -14
- package/framework/standards/STANDARDS.meta.json +16 -2
- 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/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 +2 -2
- 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 +1 -1
- 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/testing.md +10 -4
- 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/frontend/nextjs/testing.md +10 -6
- package/framework/standards/infrastructure/neon/neon-pgvector.md +5 -5
- package/framework/templates/.idea/morph-templates.xml +92 -0
- package/framework/templates/.vscode/morph-templates.code-snippets +186 -0
- package/framework/templates/CLAUDE.md.core.template +3 -3
- package/framework/templates/IDE-SNIPPETS.md +262 -262
- package/framework/templates/REGISTRY.json +9 -9
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- 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/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -91
- package/framework/templates/docs/plan.md +3 -1
- package/framework/templates/docs/ui-mockups.md +4 -0
- package/framework/templates/frontend/nextjs/client-component.tsx.hbs +26 -26
- package/framework/templates/frontend/nextjs/feature-form.tsx.hbs +56 -56
- package/framework/templates/frontend/nextjs/tsconfig.json.hbs +26 -26
- 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/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/meta-prompts/fusion/fusion-agent.md +76 -76
- package/framework/templates/meta-prompts/fusion/fusion-aggregator.md +100 -100
- package/framework/templates/state.template.json +17 -17
- package/framework/templates/ui/design-system.css +226 -226
- 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 +17 -104
- 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 +5 -57
- 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.js +48 -302
- package/src/commands/project/env.js +1 -27
- package/src/commands/project/fleet.js +3 -32
- package/src/commands/project/init.js +4 -68
- package/src/commands/project/install-plugin-cmd.js +0 -1
- package/src/commands/project/standards.js +1 -62
- package/src/commands/project/status.js +0 -53
- package/src/commands/project/update.js +31 -239
- package/src/commands/project/worktree.js +108 -882
- package/src/commands/scaffold/scaffold.js +0 -25
- package/src/commands/state/advance.js +14 -85
- package/src/commands/state/approve.js +15 -97
- package/src/commands/state/archive.js +12 -64
- package/src/commands/state/auto-approval.js +2 -5
- package/src/commands/state/create.js +22 -109
- package/src/commands/state/delete.js +11 -63
- package/src/commands/state/finish.js +98 -475
- package/src/commands/state/gate-check.js +22 -132
- package/src/commands/state/gate-decision.js +3 -39
- package/src/commands/state/handoff.js +3 -30
- package/src/commands/state/list.js +14 -84
- package/src/commands/state/report-check.js +8 -58
- package/src/commands/state/retype.js +4 -26
- package/src/commands/validation/index.js +0 -3
- package/src/commands/validation/mutate.js +11 -11
- package/src/commands/validation/plan-check.js +4 -2
- package/src/commands/validation/tests-audit.js +7 -0
- package/src/commands/validation/validate-feature.js +10 -97
- package/src/commands/validation/verify.js +30 -113
- package/src/core/paths/output-schema.js +2 -101
- package/src/core/state/feature-identity.js +1 -25
- package/src/core/state/gate-order.js +0 -27
- package/src/core/state/lifecycle-audit.js +8 -100
- package/src/core/state/state-manager.js +22 -304
- 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 +0 -1
- package/src/core/work-types.js +1 -36
- package/src/dashboard/app/App.jsx +1 -8
- package/src/dashboard/app/components/ActivityFeed.jsx +0 -5
- package/src/dashboard/app/components/CommandPalette.jsx +0 -7
- 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 +1 -5
- package/src/dashboard/app/lib/project.js +0 -12
- package/src/dashboard/app/main.jsx +13 -13
- package/src/dashboard/app/pages/AgentMap.jsx +1 -8
- package/src/dashboard/app/pages/FeatureDetail.jsx +1 -5
- package/src/dashboard/app/pages/Features.jsx +1 -8
- package/src/dashboard/app/pages/Guide.jsx +0 -1
- package/src/dashboard/app/pages/HookInspector.jsx +2 -23
- package/src/dashboard/app/pages/PhasePipeline.jsx +0 -3
- 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 +10 -69
- package/src/dashboard/scanner/agents-scanner.js +1 -23
- package/src/dashboard/scanner/docs-scanner.js +0 -6
- package/src/dashboard/scanner/feature-status.js +2 -5
- package/src/dashboard/scanner/features-scanner.js +10 -66
- 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 +2 -6
- package/src/dashboard/vite-plugin-morph-data.js +5 -19
- package/src/dashboard/vite.config.js +24 -24
- 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 +4 -19
- package/src/lib/ci/github-ci.js +9 -37
- package/src/lib/ci/workflow-scaffold.js +14 -10
- package/src/lib/detectors/claude-config-detector.js +13 -74
- package/src/lib/detectors/index.js +4 -50
- package/src/lib/e2e/applicability.js +37 -0
- 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 +9 -53
- package/src/lib/generators/scaffold-generator.js +5 -101
- package/src/lib/generators/settings-generator.js +4 -37
- 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 +1 -0
- 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 +3 -15
- 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 +1 -1
- package/src/lib/installers/effects.js +8 -93
- package/src/lib/installers/mcp-installer.js +9 -169
- package/src/lib/installers/memory-installer.js +4 -27
- package/src/lib/installers/templates-reconcile.js +5 -60
- package/src/lib/mutation/adapters.js +6 -17
- package/src/lib/mutation/dotnet-introspection.js +20 -106
- package/src/lib/mutation/runner.js +23 -75
- package/src/lib/mutation/subset.js +1 -4
- package/src/lib/mutation/trx.js +0 -1
- package/src/lib/plan/artifact-vocabulary.js +5 -25
- package/src/lib/plan/checks.js +34 -33
- package/src/lib/plan/plan-parser.js +7 -8
- package/src/lib/projects/registry.js +7 -44
- package/src/lib/review-reports.js +11 -26
- package/src/lib/risk-detector.js +41 -173
- package/src/lib/specs/adr-index.js +6 -34
- package/src/lib/specs/frontmatter.js +2 -15
- package/src/lib/specs/gfm-table.js +1 -7
- package/src/lib/specs/revocations.js +1 -14
- package/src/lib/specs/screen-checklist.js +47 -124
- package/src/lib/stack/dotnet-build-ignores.js +2 -6
- package/src/lib/stack/stack-profile.js +1 -28
- package/src/lib/stack/stack-scan.js +1 -8
- package/src/lib/stack-filter.js +4 -27
- package/src/lib/standards/registry.js +1 -7
- package/src/lib/standards/sections.js +2 -13
- package/src/lib/standards/verified-against.js +19 -191
- package/src/lib/tasks/eval-node.js +21 -169
- package/src/lib/tasks/linux-runner.js +18 -142
- package/src/lib/tasks/test-nodes.js +43 -240
- package/src/lib/tasks/test-runner.js +43 -341
- package/src/lib/telemetry/metrics.js +7 -36
- package/src/lib/telemetry/telemetry-reader.js +13 -125
- package/src/lib/telemetry/verify-event.js +2 -30
- package/src/lib/tests-audit/audit.js +96 -0
- 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 +2 -24
- 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 +11 -101
- package/src/lib/validators/reuse/symbol-scanner.js +6 -62
- package/src/lib/validators/traceability-check.js +5 -63
- package/src/lib/validators/validation-runner.js +21 -158
- package/src/lib/validators/verify-runner.js +85 -376
- package/src/lib/work-type-detector.js +5 -67
- package/src/lib/worktree/docker-audit.js +17 -136
- 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 +13 -95
- 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 -147
- package/src/utils/agents-installer.js +29 -276
- package/src/utils/banner.js +4 -25
- package/src/utils/claude-md-injector.js +1 -37
- package/src/utils/claude-settings-manager.js +1 -52
- package/src/utils/file-copier.js +11 -95
- package/src/utils/hooks-installer.js +21 -298
- package/src/utils/skills-installer.js +4 -101
- package/src/utils/version-checker.js +4 -83
- package/framework/commands/morph-proposal.md +0 -9
- package/framework/hooks/git/commit-msg/conventional-commits.sh +0 -33
- package/framework/hooks/git/pre-commit/agents.sh +0 -25
- package/framework/hooks/git/pre-commit/orchestrator.sh +0 -64
- package/framework/hooks/git/pre-push/run-tests.sh +0 -44
- package/src/commands/utils/index.js +0 -3
- package/src/lib/detectors/design-system-detector.js +0 -188
- package/src/lib/generators/design-system-generator.js +0 -508
- package/src/lib/specs/old-format.js +0 -68
- package/src/utils/agent-id-mapper.js +0 -11
|
@@ -1,55 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* cited under `**Arquivos:**` in the slice reports (`3-implement/reports/F{n}.md`)
|
|
4
|
-
* belong to some slice of the plan (its `arquivos:`, as extracted by plan-check)?
|
|
5
|
-
*
|
|
6
|
-
* It catches the reports and the plan DISAGREEING: a file the implementation
|
|
7
|
-
* says it touched that no slice declared. A file absent from both documents
|
|
8
|
-
* is invisible to it by construction — that drift is the reuse/contract
|
|
9
|
-
* reviewer's job.
|
|
10
|
-
*
|
|
11
|
-
* Advisory, never blocking: `status` is only ever 'pass' | 'warn' | 'skip'.
|
|
12
|
-
* `runVerify()` keeps this result OUTSIDE `nodes` so it can never flip the
|
|
13
|
-
* overall build/tests/validators/e2e status.
|
|
14
|
-
*
|
|
15
|
-
* Fail-open throughout: no slice report, no extracted plan or a parse error
|
|
16
|
-
* all degrade to `{ status: 'skip' }`.
|
|
17
|
-
*
|
|
18
|
-
* @module traceability-check
|
|
19
|
-
*/
|
|
1
|
+
// Advisory: status is only 'pass' | 'warn' | 'skip', and runVerify() keeps it outside
|
|
2
|
+
// `nodes` so it never flips the verify rollup.
|
|
20
3
|
|
|
21
4
|
import { existsSync, readFileSync } from 'fs';
|
|
22
5
|
import { join } from 'path';
|
|
23
6
|
import { readPlanChecks } from '../plan/checks.js';
|
|
24
7
|
import { sliceProgress, sliceReportPath } from '../../../framework/hooks/shared/slice-progress.js';
|
|
25
8
|
|
|
26
|
-
/**
|
|
27
|
-
* An `**Arquivos:**` marker and what follows it: the rest of its own line
|
|
28
|
-
* (inline list) plus any `- ` bullet lines right below.
|
|
29
|
-
*/
|
|
30
9
|
const FILES_BLOCK_RE = /\*\*Arquivos:\*\*([^\r\n]*)\r?\n?((?:^[ \t]*-[ \t].*\r?\n?)*)/gm;
|
|
31
10
|
|
|
32
|
-
/** Backtick-quoted spans within a line. */
|
|
33
11
|
const BACKTICK_RE = /`([^`]+)`/g;
|
|
34
12
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* slice report, applying a conservative decomposition of the shorthand real
|
|
38
|
-
* reports use:
|
|
39
|
-
*
|
|
40
|
-
* - A line with exactly one backtick span: that span is a candidate,
|
|
41
|
-
* whether or not it contains a `/`.
|
|
42
|
-
* - A line with several comma-separated spans: only the FIRST span and any
|
|
43
|
-
* LATER span that itself contains `/` are candidates. A later span with
|
|
44
|
-
* no `/` is ambiguous shorthand and is skipped rather than guessed —
|
|
45
|
-
* false negatives are acceptable for advisory tooling, false positives
|
|
46
|
-
* are not.
|
|
47
|
-
* - Any span containing `{`/`}` (brace expansion) is skipped.
|
|
48
|
-
* - Any span ending in `/` is a directory reference, not a file — skipped.
|
|
49
|
-
*
|
|
50
|
-
* @param {string} reportText
|
|
51
|
-
* @returns {string[]} de-duplicated, normalized (forward-slash) candidate paths
|
|
52
|
-
*/
|
|
13
|
+
// A later span without `/` is ambiguous shorthand and is skipped, not guessed: false
|
|
14
|
+
// negatives are acceptable for advisory tooling, false positives are not.
|
|
53
15
|
export function extractCitedFiles(reportText) {
|
|
54
16
|
const cited = new Set();
|
|
55
17
|
if (!reportText) return [];
|
|
@@ -72,16 +34,11 @@ export function extractCitedFiles(reportText) {
|
|
|
72
34
|
return [...cited];
|
|
73
35
|
}
|
|
74
36
|
|
|
75
|
-
/** Backslashes to forward slashes, strip a leading `./`. */
|
|
76
37
|
export function normalizePath(p) {
|
|
77
38
|
return String(p).replace(/\\/g, '/').replace(/^\.\//, '');
|
|
78
39
|
}
|
|
79
40
|
|
|
80
|
-
/**
|
|
81
|
-
* Union of the `arquivos:` of every slice of the plan, normalized.
|
|
82
|
-
*
|
|
83
|
-
* @returns {string[]|null} null when the plan has no readable extraction
|
|
84
|
-
*/
|
|
41
|
+
/** null when the plan has no readable extraction. */
|
|
85
42
|
export function collectSliceFiles(projectPath, featureName) {
|
|
86
43
|
const checks = readPlanChecks(projectPath, featureName).checks;
|
|
87
44
|
if (!checks) return null;
|
|
@@ -90,14 +47,6 @@ export function collectSliceFiles(projectPath, featureName) {
|
|
|
90
47
|
.map(normalizePath));
|
|
91
48
|
}
|
|
92
49
|
|
|
93
|
-
/**
|
|
94
|
-
* A cited file is covered if some slice names it exactly, or names a
|
|
95
|
-
* directory (an entry ending in `/`) that the cited file falls under.
|
|
96
|
-
*
|
|
97
|
-
* @param {string} file
|
|
98
|
-
* @param {string[]} declared
|
|
99
|
-
* @returns {boolean}
|
|
100
|
-
*/
|
|
101
50
|
function isCovered(file, declared) {
|
|
102
51
|
for (const entry of declared) {
|
|
103
52
|
if (entry === file) return true;
|
|
@@ -114,13 +63,6 @@ function readOrNull(path) {
|
|
|
114
63
|
}
|
|
115
64
|
}
|
|
116
65
|
|
|
117
|
-
/**
|
|
118
|
-
* Run the reports-vs-slices traceability check for a feature.
|
|
119
|
-
*
|
|
120
|
-
* @param {string} projectPath
|
|
121
|
-
* @param {string} featureName
|
|
122
|
-
* @returns {{ status: 'pass'|'warn'|'skip', warnings: string[], reason: string|null, citedCount: number }}
|
|
123
|
-
*/
|
|
124
66
|
export function checkOutputsTraceability(projectPath, featureName) {
|
|
125
67
|
const featureDir = join(projectPath, '.morph', 'features', featureName);
|
|
126
68
|
const progress = sliceProgress(featureDir);
|
|
@@ -1,31 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MORPH-SPEC Validation Runner
|
|
3
|
-
*
|
|
4
|
-
* Unified orchestrator that runs the right validators based on the project's
|
|
5
|
-
* stack profile. Called by task-done before marking tasks complete.
|
|
6
|
-
*
|
|
7
|
-
* MORPH-SPEC 3.0 - Phase A: Active Validation Pipeline
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
1
|
import { existsSync } from 'fs';
|
|
11
2
|
import chalk from 'chalk';
|
|
12
3
|
import { getAbsoluteOutputPath, resolveContractsOutputType } from '../../core/paths/output-schema.js';
|
|
13
4
|
import { getStackProfile } from '../stack/stack-profile.js';
|
|
14
5
|
|
|
15
|
-
// Skipped in 'light' mode (checkpoint-only); light keeps `nextjs-component`.
|
|
16
|
-
const HEAVY_VALIDATORS = new Set([
|
|
17
|
-
'packages', 'contract-compliance', 'reuse', 'code-budget'
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
6
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @param {string} projectPath - Project root path
|
|
24
|
-
* @param {string} featureName - Feature name from state.json
|
|
25
|
-
* @param {Object} options - Options
|
|
26
|
-
* @param {boolean} options.verbose - Show detailed output
|
|
27
|
-
* @param {string[]} options.validators - Specific validators to run (overrides stack-based detection)
|
|
28
|
-
* @returns {Object} { passed, errors, warnings, issues, summary }
|
|
7
|
+
* @param {string[]} [options.validators] - overrides stack-based detection
|
|
8
|
+
* @returns {Object} { passed, errors, warnings, issues, summary, results, skipped, _skipReason? }
|
|
29
9
|
*/
|
|
30
10
|
export async function runValidation(projectPath, featureName, options = {}) {
|
|
31
11
|
const result = {
|
|
@@ -34,114 +14,38 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
34
14
|
warnings: 0,
|
|
35
15
|
issues: [],
|
|
36
16
|
summary: '',
|
|
37
|
-
results: {}
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
// Decision constraints support (stub - decision-constraint-loader removed as orphaned code)
|
|
41
|
-
const constraints = {
|
|
42
|
-
enabledValidators: [], // No constraint-based validators
|
|
43
|
-
validators: {} // No validator-specific constraints
|
|
17
|
+
results: {}
|
|
44
18
|
};
|
|
45
19
|
|
|
46
|
-
//
|
|
47
|
-
// COPY: the mode filter below used to splice in place, and when the caller
|
|
48
|
-
// passed `options.validators` that array WAS this array — so a light run
|
|
49
|
-
// emptied the caller's own list, and running the same list twice validated
|
|
50
|
-
// less the second time.
|
|
20
|
+
// A copy: filtering in place would empty the caller's own `options.validators`.
|
|
51
21
|
const detected = options.validators || detectValidators(projectPath);
|
|
52
|
-
|
|
22
|
+
const validatorIds = [...detected];
|
|
53
23
|
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (mode === 'light') {
|
|
58
|
-
validatorIds = validatorIds.filter(id => {
|
|
59
|
-
if (HEAVY_VALIDATORS.has(id)) { droppedByMode.push(id); return false; }
|
|
60
|
-
return true;
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Merge constraint-enabled validators
|
|
65
|
-
validatorIds.push(...constraints.enabledValidators);
|
|
66
|
-
|
|
67
|
-
// Reuse is stack-agnostic: "isto já existe?" é a mesma pergunta em C# e em
|
|
68
|
-
// TypeScript, e o validador se cala sozinho quando não há grafo (ADR-002).
|
|
69
|
-
// Por isso ele não entra no `defaultValidators` de stack profile nenhum —
|
|
70
|
-
// entra aqui, ANTES do early-return de "nenhum validador": num projeto de
|
|
71
|
-
// stack `empty`/`nodejs-cli` não há outro validador para carregá-lo, e é
|
|
72
|
-
// exatamente lá que ele seria a única verificação a rodar.
|
|
73
|
-
// Em `light` (checkpoint) ele fica de fora, como todo validador pesado — é
|
|
74
|
-
// uma leitura de graph.json (11-24 MB nos repos medidos) — e por isso está
|
|
75
|
-
// em HEAVY_VALIDATORS, que é onde essa política se declara. Não entra em
|
|
76
|
-
// `droppedByMode`: essa lista responde "o que o modo tirou da SUA lista", e
|
|
77
|
-
// é ela que distingue "light pulou os pesados" de "este stack não tem
|
|
78
|
-
// validador nenhum" na mensagem final.
|
|
79
|
-
if (!options.skipReuse && mode !== 'light') {
|
|
80
|
-
validatorIds.push('reuse');
|
|
81
|
-
}
|
|
82
|
-
// Mesma política, pelo mesmo motivo: comentário e orçamento de teste valem
|
|
83
|
-
// em qualquer stack, e a medida é da feature, não de um checkpoint.
|
|
84
|
-
if (mode !== 'light') {
|
|
85
|
-
validatorIds.push('code-budget');
|
|
86
|
-
}
|
|
24
|
+
// Stack-agnostic: on an `empty`/`nodejs-cli` stack they are the only check.
|
|
25
|
+
if (!options.skipReuse) validatorIds.push('reuse');
|
|
26
|
+
validatorIds.push('code-budget');
|
|
87
27
|
|
|
88
|
-
if (validatorIds.length === 0) {
|
|
89
|
-
// Say which of the two it is. On a pure `dotnet` project every default
|
|
90
|
-
// validator is heavy, so a light run legitimately runs none — but the old
|
|
91
|
-
// message blamed the stack ("no validators configured"), which reads as
|
|
92
|
-
// "your stack has nothing to check" when the full run has two.
|
|
93
|
-
result.summary = droppedByMode.length > 0
|
|
94
|
-
? `No validators ran — light mode skipped ${droppedByMode.length} heavy validator(s): ${droppedByMode.join(', ')}`
|
|
95
|
-
: 'No validators ran — no validators configured for this stack';
|
|
96
|
-
console.log(chalk.gray(`\n ℹ️ ${result.summary.replace('No validators ran — ', '')}`));
|
|
97
|
-
result._skipReason = result.summary;
|
|
98
|
-
result._droppedByMode = droppedByMode;
|
|
99
|
-
return result;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Always show what's running (even non-verbose) — helps confirm validators are active
|
|
103
28
|
console.log(chalk.gray(`\n Running ${validatorIds.length} validator(s): ${validatorIds.join(', ')}`));
|
|
104
|
-
if (constraints.enabledValidators.length > 0) {
|
|
105
|
-
console.log(chalk.gray(` (+ ${constraints.enabledValidators.length} from decisions)`));
|
|
106
|
-
}
|
|
107
29
|
|
|
108
|
-
// Always run contract compliance if the contracts file exists (stack-aware)
|
|
109
30
|
if (!options.skipContracts) {
|
|
110
31
|
const _profileForContracts = getStackProfile(projectPath);
|
|
111
32
|
const contractsType = resolveContractsOutputType(_profileForContracts.stack);
|
|
112
33
|
const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
|
|
113
|
-
// Positive
|
|
114
|
-
|
|
115
|
-
// "dotnet-nextjs" stack that has a .NET backend but is ALSO isNextjs.
|
|
116
|
-
// `mode: 'light'` has to hold here too. This push sits AFTER the mode
|
|
117
|
-
// filter, so contract-compliance — which is in HEAVY_VALIDATORS — used to
|
|
118
|
-
// slip into every light run that had a contracts file, making light mode
|
|
119
|
-
// heavier than declared on exactly the compound stacks it matters for.
|
|
120
|
-
const contractsIsHeavyAndSkipped = mode === 'light' && HEAVY_VALIDATORS.has('contract-compliance');
|
|
121
|
-
if (existsSync(contractsPath) && _profileForContracts.isDotnet && !contractsIsHeavyAndSkipped) {
|
|
34
|
+
// Positive `isDotnet`, not `!isNextjs`: a "dotnet-nextjs" stack is both.
|
|
35
|
+
if (existsSync(contractsPath) && _profileForContracts.isDotnet) {
|
|
122
36
|
validatorIds.push('contract-compliance');
|
|
123
37
|
}
|
|
124
38
|
}
|
|
125
39
|
|
|
126
|
-
// Deduplicate
|
|
127
40
|
const uniqueValidators = [...new Set(validatorIds)];
|
|
128
41
|
|
|
129
|
-
//
|
|
130
|
-
// projeto sem grafo). Um validador assim não acha nada — e "não achou nada"
|
|
131
|
-
// é indistinguível de "verificou e está limpo" se ninguém contar.
|
|
42
|
+
// A validator that declared itself inapplicable found nothing, which is not "clean".
|
|
132
43
|
const selfSkipped = [];
|
|
133
44
|
result.skipped = selfSkipped;
|
|
134
45
|
|
|
135
|
-
// Run each validator
|
|
136
46
|
for (const validatorId of uniqueValidators) {
|
|
137
47
|
try {
|
|
138
|
-
|
|
139
|
-
const validatorOptions = {
|
|
140
|
-
...options,
|
|
141
|
-
constraints: constraints.validators[validatorId] || {}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, validatorOptions);
|
|
48
|
+
const validatorResult = await runSingleValidator(validatorId, projectPath, featureName, options);
|
|
145
49
|
|
|
146
50
|
if (validatorResult) {
|
|
147
51
|
if (validatorResult._skipReason) {
|
|
@@ -156,8 +60,6 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
156
60
|
result.warnings += warnings;
|
|
157
61
|
result.issues.push(...issues);
|
|
158
62
|
|
|
159
|
-
// A validator that skipped itself checked nothing: in `results` it would
|
|
160
|
-
// read as a validator that ran clean.
|
|
161
63
|
if (!validatorResult._skipReason) result.results[validatorId] = { passed: errors === 0, issues };
|
|
162
64
|
}
|
|
163
65
|
} catch (err) {
|
|
@@ -178,10 +80,7 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
178
80
|
}
|
|
179
81
|
}
|
|
180
82
|
|
|
181
|
-
//
|
|
182
|
-
// verde. Sem isto, um projeto de stack sem validadores e sem grafo receberia
|
|
183
|
-
// `validators: pass` do `morph-spec verify` tendo verificado exatamente nada
|
|
184
|
-
// — o modo de falha que este repositório já pagou caro (guardrail inerte).
|
|
83
|
+
// Every validator self-skipped: SKIP, never a green over nothing checked.
|
|
185
84
|
if (selfSkipped.length > 0 && selfSkipped.length === uniqueValidators.length) {
|
|
186
85
|
result._skipReason = `No validators ran — ${selfSkipped.map((s) => `${s.validator}: ${s.reason}`).join('; ')}`;
|
|
187
86
|
result.summary = result._skipReason;
|
|
@@ -196,44 +95,20 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
196
95
|
return result;
|
|
197
96
|
}
|
|
198
97
|
|
|
199
|
-
/**
|
|
200
|
-
* Detect which validators to run, driven by the project's stack profile.
|
|
201
|
-
* @param {string} projectPath - Project root path (default: current directory)
|
|
202
|
-
* @returns {string[]} Array of validator IDs to run
|
|
203
|
-
*/
|
|
204
98
|
function detectValidators(projectPath = '.') {
|
|
205
99
|
const profile = getStackProfile(projectPath);
|
|
206
100
|
return [...profile.defaultValidators];
|
|
207
101
|
}
|
|
208
102
|
|
|
209
103
|
/**
|
|
210
|
-
* Reduce any validator's return
|
|
211
|
-
* `
|
|
212
|
-
*
|
|
213
|
-
* Validators were written at different times and return different shapes. The
|
|
214
|
-
* runner used to read `errors`/`warnings`/`issues` off the raw object, so a
|
|
215
|
-
* validator that reports through any other shape contributed ZERO — and since
|
|
216
|
-
* `passed = errors === 0`, its findings were recorded as a pass. Measured: of
|
|
217
|
-
* seven validators only two returned all three keys. `packages` — one of the
|
|
218
|
-
* two defaults for the `dotnet` stack — returned
|
|
219
|
-
* `{status, totalFiles, filesWithIssues, results}` and had every NuGet
|
|
220
|
-
* incompatibility it found thrown away, while `verify` reported green.
|
|
221
|
-
*
|
|
222
|
-
* Reading the nested `results` shape here keeps that contract in ONE place
|
|
223
|
-
* instead of asking every validator to remember it.
|
|
224
|
-
*
|
|
225
|
-
* @param {string} validatorId
|
|
226
|
-
* @param {Object} raw - whatever the validator returned
|
|
227
|
-
* @returns {{errors: number, warnings: number, issues: Array}}
|
|
104
|
+
* Reduce any validator's return shape to `{ errors, warnings, issues }`, so a
|
|
105
|
+
* validator reporting through nested `results` never contributes zero and reads as a pass.
|
|
228
106
|
*/
|
|
229
107
|
export function normalizeValidatorResult(validatorId, raw) {
|
|
230
108
|
if (!raw || typeof raw !== 'object') return { errors: 0, warnings: 0, issues: [] };
|
|
231
109
|
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
// them keyed by check name. The nested read is a FALLBACK,
|
|
235
|
-
// never additive: validators that expose both would otherwise report every
|
|
236
|
-
// finding twice.
|
|
110
|
+
// The nested read is a FALLBACK, never additive: validators exposing both
|
|
111
|
+
// shapes would otherwise report every finding twice.
|
|
237
112
|
const topLevel = Array.isArray(raw.issues) ? raw.issues : [];
|
|
238
113
|
const nested = raw.results && typeof raw.results === 'object'
|
|
239
114
|
? (Array.isArray(raw.results) ? raw.results : Object.values(raw.results))
|
|
@@ -242,8 +117,7 @@ export function normalizeValidatorResult(validatorId, raw) {
|
|
|
242
117
|
const issues = (topLevel.length ? topLevel : nested)
|
|
243
118
|
.map(i => ({ validator: validatorId, ...i }));
|
|
244
119
|
|
|
245
|
-
//
|
|
246
|
-
// issues differently than one-each. Otherwise derive from the issue levels.
|
|
120
|
+
// The validator's own counts win: it may weigh issues differently than one-each.
|
|
247
121
|
const counted = {
|
|
248
122
|
errors: issues.filter(i => i.level === 'error').length,
|
|
249
123
|
warnings: issues.filter(i => i.level === 'warning').length,
|
|
@@ -251,8 +125,7 @@ export function normalizeValidatorResult(validatorId, raw) {
|
|
|
251
125
|
const errors = Number.isFinite(raw.errors) ? raw.errors : counted.errors;
|
|
252
126
|
const warnings = Number.isFinite(raw.warnings) ? raw.warnings : counted.warnings;
|
|
253
127
|
|
|
254
|
-
//
|
|
255
|
-
// countable detail still must not read as a pass.
|
|
128
|
+
// `status: 'error'` with no countable detail still must not read as a pass.
|
|
256
129
|
if (errors === 0 && raw.status === 'error') {
|
|
257
130
|
return {
|
|
258
131
|
errors: 1,
|
|
@@ -268,9 +141,6 @@ export function normalizeValidatorResult(validatorId, raw) {
|
|
|
268
141
|
return { errors, warnings, issues };
|
|
269
142
|
}
|
|
270
143
|
|
|
271
|
-
/**
|
|
272
|
-
* Run a single validator by ID
|
|
273
|
-
*/
|
|
274
144
|
async function runSingleValidator(validatorId, projectPath, featureName, options) {
|
|
275
145
|
switch (validatorId) {
|
|
276
146
|
case 'packages': {
|
|
@@ -299,11 +169,8 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
|
|
|
299
169
|
}
|
|
300
170
|
|
|
301
171
|
default:
|
|
302
|
-
// A warning, not silence
|
|
303
|
-
// a
|
|
304
|
-
// found nothing. A typo in a validator list would have read as a clean
|
|
305
|
-
// pass. Not an error — an unknown id is a configuration mistake, not a
|
|
306
|
-
// defect in the code under validation.
|
|
172
|
+
// A warning, not silence (a typo would read as a clean pass) and not an
|
|
173
|
+
// error (a config mistake, not a defect in the code under validation).
|
|
307
174
|
if (options.verbose) {
|
|
308
175
|
console.log(chalk.gray(` Unknown validator: ${validatorId}`));
|
|
309
176
|
}
|
|
@@ -320,9 +187,6 @@ async function runSingleValidator(validatorId, projectPath, featureName, options
|
|
|
320
187
|
}
|
|
321
188
|
}
|
|
322
189
|
|
|
323
|
-
/**
|
|
324
|
-
* Format validation results for console output
|
|
325
|
-
*/
|
|
326
190
|
export function formatValidationResults(result) {
|
|
327
191
|
if (result.passed && result.warnings === 0) {
|
|
328
192
|
const ranCount = Object.keys(result.results || {}).length;
|
|
@@ -337,7 +201,6 @@ export function formatValidationResults(result) {
|
|
|
337
201
|
console.log(chalk.red(`\n ❌ Validation FAILED: ${result.errors} error(s), ${result.warnings} warning(s)\n`));
|
|
338
202
|
}
|
|
339
203
|
|
|
340
|
-
// Group issues by level
|
|
341
204
|
const errors = result.issues.filter(i => i.level === 'error');
|
|
342
205
|
const warnings = result.issues.filter(i => i.level === 'warning');
|
|
343
206
|
|