@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,109 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MORPH-SPEC Feature Validation Command
|
|
3
|
-
*
|
|
4
|
-
* Content-aware validation of feature outputs at each phase.
|
|
5
|
-
* Checks section presence, schema consistency, and contract correctness.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* morph-spec validate <feature-name> [--phase <phase>] [--json] [--verbose]
|
|
9
|
-
*
|
|
10
|
-
* Example:
|
|
11
|
-
* morph-spec validate user-portal --phase plan
|
|
12
|
-
* morph-spec validate user-portal --json
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
1
|
import fs from 'fs';
|
|
16
2
|
import path from 'path';
|
|
17
3
|
import chalk from 'chalk';
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* the framework's own output passes its own validator.
|
|
24
|
-
*
|
|
25
|
-
* Three tiers:
|
|
26
|
-
* required → ERROR if absent. Strict subset of what the skills always emit.
|
|
27
|
-
* recommended → WARNING if absent. Skills emit these, but a lean doc may skip.
|
|
28
|
-
* optional → silent. Tracked when present, never penalized.
|
|
29
|
-
*
|
|
30
|
-
* Each entry is a string or an array of acceptable strings (any-of). The array
|
|
31
|
-
* form carries bilingual aliases (pt-BR section names the skills emit AND their
|
|
32
|
-
* English equivalents in the canonical templates), e.g. ['Escopo', 'Scope'].
|
|
33
|
-
*
|
|
34
|
-
* Matching is done against *section markers* (headings and line-start bold
|
|
35
|
-
* labels), not whole-file substrings — see `extractSectionMarkers`.
|
|
36
|
-
*/
|
|
4
|
+
import { parsePlan } from '../../lib/plan/plan-parser.js';
|
|
5
|
+
import { knownPersonas, planHash, validatePlan } from '../../lib/plan/checks.js';
|
|
6
|
+
|
|
7
|
+
// Matched against section markers (headings and line-start bold labels), never whole-file
|
|
8
|
+
// substrings. required → ERROR, recommended → WARNING, optional → never penalized; an array is any-of.
|
|
37
9
|
const OUTPUT_SECTIONS = {
|
|
38
|
-
|
|
39
|
-
// Stack, Riscos e dependências, Compliance de stack, Conflitos com o acervo.
|
|
40
|
-
//
|
|
41
|
-
// "Critérios de aceite" is now OPTIONAL, not required: the behaviour layer
|
|
42
|
-
// moved to the use cases (2-plan/usecases/), whose post-conditions must be
|
|
43
|
-
// observable in a test, and morph-brainstorming no longer emits the section.
|
|
44
|
-
// `optional` — not `recommended` — precisely because the skill never emits it
|
|
45
|
-
// anymore: a warning on every new proposal would be systematic noise. Legacy
|
|
46
|
-
// proposals that DO carry the section keep validating exactly as before.
|
|
47
|
-
//
|
|
48
|
-
// "Conflitos com o acervo" is recommended: morph-brainstorming always emits
|
|
49
|
-
// it ("nenhum" included), so its absence means the acervo check never ran.
|
|
50
|
-
// Not required — proposals written before the section existed stay valid.
|
|
51
|
-
'proposal.md': {
|
|
10
|
+
'verifier.md': {
|
|
52
11
|
required: [
|
|
53
|
-
['
|
|
54
|
-
['Escopo', 'Scope'],
|
|
12
|
+
['Verdict', 'Veredito'],
|
|
55
13
|
],
|
|
56
14
|
recommended: [
|
|
57
|
-
['
|
|
58
|
-
|
|
59
|
-
],
|
|
60
|
-
optional: [
|
|
61
|
-
['Critérios de aceite', 'Critério', 'Aceite', 'Acceptance Criteria', 'Acceptance', 'Success Criteria', 'Success Metrics'],
|
|
62
|
-
'Stack',
|
|
63
|
-
['Usuários', 'Usuarios', 'Users', 'User Journey', 'User Stories', 'Fluxos'],
|
|
64
|
-
['Dependências', 'Dependencias', 'Dependencies'],
|
|
65
|
-
['Compliance'],
|
|
66
|
-
['Overview', 'Visão geral', 'Visao geral', 'Resumo'],
|
|
67
|
-
],
|
|
68
|
-
},
|
|
69
|
-
// morph-plan (spec.md) emits: Arquitetura da solução, Edge cases,
|
|
70
|
-
// Integrações externas e contratos esperados.
|
|
71
|
-
'spec.md': {
|
|
72
|
-
required: [
|
|
73
|
-
['Arquitetura', 'Architecture'],
|
|
74
|
-
['Integrações', 'Integracoes', 'Integrations', 'Contratos', 'Contracts', 'Data Model', 'Modelo de Dados'],
|
|
75
|
-
],
|
|
76
|
-
recommended: [
|
|
77
|
-
['Edge cases', 'Edge Cases', 'Casos limite', 'Casos Limite', 'Casos de borda'],
|
|
78
|
-
],
|
|
79
|
-
optional: [
|
|
80
|
-
['Overview', 'Visão geral', 'Resumo'],
|
|
81
|
-
['Requirements', 'Requisitos'],
|
|
82
|
-
['Security', 'Segurança', 'Seguranca'],
|
|
83
|
-
['Testing', 'Testes', 'Estratégia de Testes'],
|
|
84
|
-
['Performance', 'Desempenho'],
|
|
15
|
+
['Bloqueadores', 'Bloqueantes', 'Blockers'],
|
|
16
|
+
'Checks',
|
|
85
17
|
],
|
|
18
|
+
optional: [],
|
|
86
19
|
},
|
|
87
|
-
|
|
88
|
-
// `**Status:** / **Context:** / **Decision:**` line-start bold labels.
|
|
89
|
-
'decisions.md': {
|
|
20
|
+
'reviewer.md': {
|
|
90
21
|
required: [
|
|
91
|
-
'
|
|
92
|
-
'Status',
|
|
93
|
-
['Contexto', 'Context'],
|
|
94
|
-
['Decisão', 'Decisao', 'Decision'],
|
|
22
|
+
['Verdict', 'Veredito'],
|
|
95
23
|
],
|
|
96
24
|
recommended: [
|
|
97
|
-
['
|
|
98
|
-
],
|
|
99
|
-
optional: [
|
|
100
|
-
['Alternativas', 'Alternatives'],
|
|
101
|
-
['Custos', 'Costs'],
|
|
25
|
+
['Bloqueadores', 'Bloqueantes', 'Blockers'],
|
|
102
26
|
],
|
|
27
|
+
optional: [],
|
|
103
28
|
},
|
|
104
|
-
'
|
|
29
|
+
'security.md': {
|
|
105
30
|
required: [
|
|
106
|
-
['
|
|
31
|
+
['Verdict', 'Veredito'],
|
|
107
32
|
],
|
|
108
33
|
recommended: [
|
|
109
34
|
['Bloqueadores', 'Bloqueantes', 'Blockers'],
|
|
@@ -112,51 +37,21 @@ const OUTPUT_SECTIONS = {
|
|
|
112
37
|
},
|
|
113
38
|
};
|
|
114
39
|
|
|
115
|
-
/**
|
|
116
|
-
* Phase-to-output mapping (v5 NLH)
|
|
117
|
-
*/
|
|
118
40
|
const PHASE_OUTPUTS = {
|
|
119
|
-
|
|
41
|
+
plan: ['plan.md', 'checks.json'],
|
|
120
42
|
uiux: ['design-system.md', 'mockups.md', 'components.md', 'flows.md'],
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
review: ['review-report.md'],
|
|
43
|
+
implement: [],
|
|
44
|
+
review: ['verifier.md', 'reviewer.md'],
|
|
124
45
|
};
|
|
125
46
|
|
|
126
|
-
// Work types whose plan phase (see src/core/work-types.js) skips proposal.md/
|
|
127
|
-
// spec.md — chore and hotfix stamp proposal+plan gates at birth
|
|
128
|
-
// (policy-approved, no human deliberation over those docs) and go straight to
|
|
129
|
-
// tasks.json. Validating the full plan doc set against a feature that was
|
|
130
|
-
// never asked to write those docs would be a false failure, not a real gap.
|
|
131
|
-
const LEAN_PLAN_WORK_TYPES = ['chore', 'hotfix'];
|
|
132
|
-
const LEAN_PLAN_OUTPUTS = ['tasks.json'];
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Phase-to-folder mapping (v5 filesystem convention)
|
|
136
|
-
*/
|
|
137
47
|
const PHASE_FOLDERS = {
|
|
138
|
-
proposal: '0-proposal',
|
|
139
|
-
uiux: '1-design',
|
|
140
48
|
plan: '2-plan',
|
|
49
|
+
uiux: '1-design',
|
|
141
50
|
implement: '3-implement',
|
|
142
51
|
review: '4-review',
|
|
143
52
|
};
|
|
144
53
|
|
|
145
|
-
|
|
146
|
-
* Extract section markers from markdown content.
|
|
147
|
-
*
|
|
148
|
-
* A marker is a line that structurally introduces a section:
|
|
149
|
-
* - a markdown heading → `## Arquitetura da solução`
|
|
150
|
-
* - a line-start bold label → `**Status:** Aceito`
|
|
151
|
-
*
|
|
152
|
-
* Bold labels are included on purpose (deviation from heading-only): the ADR
|
|
153
|
-
* convention used in `decisions.md` — and in the spec.md ADR block — writes
|
|
154
|
-
* Status/Context/Decision as line-start bold labels, not headings. They are
|
|
155
|
-
* structural, not prose, so this keeps ADRs valid without reintroducing the
|
|
156
|
-
* accidental-prose-match problem that whole-file substring matching had.
|
|
157
|
-
*
|
|
158
|
-
* Returns lowercased marker texts.
|
|
159
|
-
*/
|
|
54
|
+
// Line-start bold labels count: reports write `**Verdict:**` as a label, not a heading.
|
|
160
55
|
function extractSectionMarkers(content) {
|
|
161
56
|
const markers = [];
|
|
162
57
|
for (const rawLine of content.split('\n')) {
|
|
@@ -174,9 +69,6 @@ function extractSectionMarkers(content) {
|
|
|
174
69
|
return markers;
|
|
175
70
|
}
|
|
176
71
|
|
|
177
|
-
/**
|
|
178
|
-
* Validate a single output file
|
|
179
|
-
*/
|
|
180
72
|
function validateOutput(featurePath, outputFile) {
|
|
181
73
|
const filePath = path.join(featurePath, outputFile);
|
|
182
74
|
const result = {
|
|
@@ -197,22 +89,22 @@ function validateOutput(featurePath, outputFile) {
|
|
|
197
89
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
198
90
|
result.lineCount = content.split('\n').length;
|
|
199
91
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
92
|
+
if (outputFile === 'plan.md') {
|
|
93
|
+
validatePlanOutput(content, featurePath, result);
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
if (outputFile === 'checks.json') {
|
|
97
|
+
validateChecksOutput(content, featurePath, result);
|
|
203
98
|
return result;
|
|
204
99
|
}
|
|
205
100
|
|
|
206
|
-
// Skip section checks for files without defined sections
|
|
207
101
|
const sections = OUTPUT_SECTIONS[outputFile];
|
|
208
102
|
if (!sections) {
|
|
209
103
|
return result;
|
|
210
104
|
}
|
|
211
105
|
|
|
212
|
-
// v5: only markdown outputs — contracts moved into spec.md
|
|
213
106
|
validateMarkdownSections(content, sections, result);
|
|
214
107
|
|
|
215
|
-
// Size warnings
|
|
216
108
|
if (result.lineCount < 10) {
|
|
217
109
|
result.warnings.push(`File is very short (${result.lineCount} lines) — may be incomplete`);
|
|
218
110
|
}
|
|
@@ -220,40 +112,32 @@ function validateOutput(featurePath, outputFile) {
|
|
|
220
112
|
return result;
|
|
221
113
|
}
|
|
222
114
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
115
|
+
// `planDir` is `.morph/features/{f}/2-plan`, so the project is four levels up.
|
|
116
|
+
function validatePlanOutput(content, planDir, result) {
|
|
117
|
+
const plan = parsePlan(content);
|
|
118
|
+
const { errors, warnings } = validatePlan(plan, { personas: knownPersonas(path.resolve(planDir, '..', '..', '..', '..')) });
|
|
119
|
+
result.errors.push(...errors);
|
|
120
|
+
result.warnings.push(...warnings);
|
|
121
|
+
result.sections.found.push(...plan.sections.filter((x) => x.key).map((x) => x.name));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function validateChecksOutput(content, planDir, result) {
|
|
125
|
+
let doc;
|
|
229
126
|
try {
|
|
230
|
-
|
|
127
|
+
doc = JSON.parse(content);
|
|
231
128
|
} catch (err) {
|
|
232
|
-
result.errors.push(`
|
|
129
|
+
result.errors.push(`checks.json is not valid JSON: ${err.message}`);
|
|
233
130
|
return;
|
|
234
131
|
}
|
|
235
|
-
const
|
|
236
|
-
if (!
|
|
237
|
-
|
|
132
|
+
const planPath = path.join(planDir, 'plan.md');
|
|
133
|
+
if (!fs.existsSync(planPath)) return;
|
|
134
|
+
if (doc?.planHash !== planHash(fs.readFileSync(planPath, 'utf8'))) {
|
|
135
|
+
result.errors.push('checks.json is stale — plan.md changed after it was generated; run `morph-spec plan-check`');
|
|
238
136
|
return;
|
|
239
137
|
}
|
|
240
|
-
|
|
241
|
-
result.errors.push('tasks.json has no task entries');
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
result.sections.found.push(`${data.tasks.length} task(s)`);
|
|
138
|
+
result.sections.found.push(`${doc.checks?.length ?? 0} check(s), ${doc.slices?.length ?? 0} slice(s)`);
|
|
245
139
|
}
|
|
246
140
|
|
|
247
|
-
/**
|
|
248
|
-
* Validate markdown sections against section markers (headings + bold labels).
|
|
249
|
-
*
|
|
250
|
-
* Each `entry` is a string or array of acceptable aliases (any-of). A section
|
|
251
|
-
* is present when at least one alias is a substring of at least one marker.
|
|
252
|
-
*
|
|
253
|
-
* required → missing produces an ERROR
|
|
254
|
-
* recommended → missing produces a WARNING
|
|
255
|
-
* optional → tracked when present, never penalized
|
|
256
|
-
*/
|
|
257
141
|
function validateMarkdownSections(content, sections, result) {
|
|
258
142
|
const markers = extractSectionMarkers(content);
|
|
259
143
|
const match = (entry) => {
|
|
@@ -289,30 +173,17 @@ function validateMarkdownSections(content, sections, result) {
|
|
|
289
173
|
}
|
|
290
174
|
}
|
|
291
175
|
|
|
292
|
-
|
|
293
|
-
* Resolve the list of `{ file, dir }` outputs to validate.
|
|
294
|
-
*
|
|
295
|
-
* @param {string} featurePath Absolute path to the feature directory.
|
|
296
|
-
* @param {string|null} phase Phase key, or null to validate everything.
|
|
297
|
-
* @param {string|undefined} workType Feature's workType (see src/core/work-types.js).
|
|
298
|
-
* When `phase === 'plan'` and workType is 'chore'/'hotfix', only tasks.json
|
|
299
|
-
* is required (see LEAN_PLAN_WORK_TYPES above).
|
|
300
|
-
* @returns {{ outputFiles?: Array<{file:string, dir:string}>, error?: string }}
|
|
301
|
-
*/
|
|
302
|
-
function resolveOutputFiles(featurePath, phase, workType) {
|
|
176
|
+
function resolveOutputFiles(featurePath, phase) {
|
|
303
177
|
if (phase) {
|
|
304
|
-
const files =
|
|
305
|
-
? LEAN_PLAN_OUTPUTS
|
|
306
|
-
: PHASE_OUTPUTS[phase];
|
|
178
|
+
const files = PHASE_OUTPUTS[phase];
|
|
307
179
|
if (!files) return { error: `Unknown phase: ${phase}` };
|
|
308
180
|
const phaseFolder = PHASE_FOLDERS[phase];
|
|
309
181
|
const searchPath = phaseFolder ? path.join(featurePath, phaseFolder) : featurePath;
|
|
310
182
|
return { outputFiles: files.map(file => ({ file, dir: searchPath })) };
|
|
311
183
|
}
|
|
312
184
|
|
|
313
|
-
// Validate every existing output across the feature root + phase folders.
|
|
314
185
|
const outputFiles = [];
|
|
315
|
-
const isOutput = name => name.endsWith('.md') || name.endsWith('.cs') || name === '
|
|
186
|
+
const isOutput = name => name.endsWith('.md') || name.endsWith('.cs') || name === 'checks.json';
|
|
316
187
|
const entries = fs.readdirSync(featurePath, { withFileTypes: true });
|
|
317
188
|
for (const entry of entries) {
|
|
318
189
|
if (entry.isDirectory()) {
|
|
@@ -327,23 +198,12 @@ function resolveOutputFiles(featurePath, phase, workType) {
|
|
|
327
198
|
return { outputFiles };
|
|
328
199
|
}
|
|
329
200
|
|
|
330
|
-
/**
|
|
331
|
-
* Pure validation core — no console output, no process.exit.
|
|
332
|
-
*
|
|
333
|
-
* @param {string} featurePath Absolute path to the feature directory.
|
|
334
|
-
* @param {{ phase?: string, workType?: string }} [options]
|
|
335
|
-
* @returns {{
|
|
336
|
-
* feature: string, phase: string, valid: boolean, error?: string,
|
|
337
|
-
* totalErrors: number, totalWarnings: number, totalFound: number,
|
|
338
|
-
* results: object[]
|
|
339
|
-
* }}
|
|
340
|
-
*/
|
|
201
|
+
/** Pure core: no console output, no process.exit. */
|
|
341
202
|
export function runFeatureValidation(featurePath, options = {}) {
|
|
342
203
|
const phase = options.phase || null;
|
|
343
|
-
const workType = options.workType || null;
|
|
344
204
|
const feature = path.basename(featurePath);
|
|
345
205
|
|
|
346
|
-
const { outputFiles, error } = resolveOutputFiles(featurePath, phase
|
|
206
|
+
const { outputFiles, error } = resolveOutputFiles(featurePath, phase);
|
|
347
207
|
if (error) {
|
|
348
208
|
return {
|
|
349
209
|
feature,
|
|
@@ -373,9 +233,6 @@ export function runFeatureValidation(featurePath, options = {}) {
|
|
|
373
233
|
};
|
|
374
234
|
}
|
|
375
235
|
|
|
376
|
-
/**
|
|
377
|
-
* Main validate command (CLI wrapper around `runFeatureValidation`)
|
|
378
|
-
*/
|
|
379
236
|
export async function validateFeatureCommand(featureName, options = {}) {
|
|
380
237
|
if (!featureName) {
|
|
381
238
|
console.error(chalk.red('Error: Feature name required'));
|
|
@@ -397,25 +254,10 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
397
254
|
process.exit(1);
|
|
398
255
|
}
|
|
399
256
|
|
|
400
|
-
|
|
401
|
-
// state-manager's getFeature/loadState) so a broken/missing root state.json
|
|
402
|
-
// elsewhere in the project can never make `validate` itself throw — it just
|
|
403
|
-
// falls back to the full (non-lean) output set, same as today.
|
|
404
|
-
let workType;
|
|
405
|
-
try {
|
|
406
|
-
const fjPath = path.join(featurePath, 'feature.json');
|
|
407
|
-
if (fs.existsSync(fjPath)) {
|
|
408
|
-
workType = JSON.parse(fs.readFileSync(fjPath, 'utf8')).workType;
|
|
409
|
-
}
|
|
410
|
-
} catch {
|
|
411
|
-
workType = undefined;
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const validation = runFeatureValidation(featurePath, { phase, workType });
|
|
257
|
+
const validation = runFeatureValidation(featurePath, { phase });
|
|
415
258
|
const { results, totalErrors, totalWarnings, totalFound } = validation;
|
|
416
|
-
const schemaWarnings = [];
|
|
259
|
+
const schemaWarnings = [];
|
|
417
260
|
|
|
418
|
-
// JSON output mode
|
|
419
261
|
if (options.json) {
|
|
420
262
|
const output = {
|
|
421
263
|
feature: featureName,
|
|
@@ -442,7 +284,6 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
442
284
|
return;
|
|
443
285
|
}
|
|
444
286
|
|
|
445
|
-
// Visual output
|
|
446
287
|
console.log(chalk.cyan('\n┌────────────────────────────────────────────────────────────┐'));
|
|
447
288
|
console.log(chalk.cyan('│ MORPH-SPEC FEATURE VALIDATOR │'));
|
|
448
289
|
console.log(chalk.cyan('└────────────────────────────────────────────────────────────┘\n'));
|
|
@@ -451,7 +292,6 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
451
292
|
if (phase) console.log(chalk.gray(`Phase: ${phase}`));
|
|
452
293
|
console.log('');
|
|
453
294
|
|
|
454
|
-
// File-by-file results
|
|
455
295
|
for (const result of results) {
|
|
456
296
|
const symbol = result.exists
|
|
457
297
|
? (result.errors.length === 0 ? '✅' : '❌')
|
|
@@ -462,25 +302,21 @@ export async function validateFeatureCommand(featureName, options = {}) {
|
|
|
462
302
|
|
|
463
303
|
console.log(`${symbol} ${name}${result.exists ? chalk.gray(` (${result.lineCount} lines)`) : ''}`);
|
|
464
304
|
|
|
465
|
-
// Sections found
|
|
466
305
|
if (result.sections.found.length > 0 && (options.verbose || result.errors.length > 0)) {
|
|
467
306
|
result.sections.found.forEach(s => {
|
|
468
307
|
console.log(chalk.green(` ✓ ${s}`));
|
|
469
308
|
});
|
|
470
309
|
}
|
|
471
310
|
|
|
472
|
-
// Errors
|
|
473
311
|
result.errors.forEach(err => {
|
|
474
312
|
console.log(chalk.red(` ✗ ${err}`));
|
|
475
313
|
});
|
|
476
314
|
|
|
477
|
-
// Warnings
|
|
478
315
|
result.warnings.forEach(warn => {
|
|
479
316
|
console.log(chalk.yellow(` ⚠ ${warn}`));
|
|
480
317
|
});
|
|
481
318
|
}
|
|
482
319
|
|
|
483
|
-
// Summary
|
|
484
320
|
console.log('');
|
|
485
321
|
console.log(chalk.gray('─'.repeat(60)));
|
|
486
322
|
|