@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,44 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gate monotonicity — single source of truth.
|
|
3
|
-
*
|
|
4
|
-
* A gate may only be approved once its predecessors are approved. The strict
|
|
5
|
-
* chain is proposal → plan → review. uiux is optional: it requires proposal but
|
|
6
|
-
* gates nothing downstream, so a feature that skips UI/UX is never blocked.
|
|
7
|
-
*
|
|
8
|
-
* Policy-stamped gates (approvedBy: "policy:{type}", written at birth by
|
|
9
|
-
* `morph-spec create` for a work type's skippedGates) count as approved, so
|
|
10
|
-
* chore/hotfix (proposal+plan pre-stamped) satisfy prereqs naturally.
|
|
11
|
-
*
|
|
12
|
-
* Pure module — no filesystem/CLI side effects. Consumed by
|
|
13
|
-
* `approve`/`advance` (refuse), the state-sync hook (warn), and `list`/`doctor`
|
|
14
|
-
* (report).
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
1
|
export const GATE_PREREQ = {
|
|
18
|
-
|
|
19
|
-
uiux: ['
|
|
20
|
-
plan: ['proposal'],
|
|
2
|
+
plan: [],
|
|
3
|
+
uiux: ['plan'],
|
|
21
4
|
review: ['plan'],
|
|
22
5
|
};
|
|
23
6
|
|
|
24
7
|
const isApproved = (gates, name) => gates?.[name]?.approved === true;
|
|
25
8
|
|
|
26
|
-
/**
|
|
27
|
-
* Prerequisite gates NOT yet approved that block approving `gate`.
|
|
28
|
-
* @param {string} gate - gate about to be approved
|
|
29
|
-
* @param {object} gates - feature.approvalGates
|
|
30
|
-
* @returns {string[]} unmet prerequisite gate names (empty = clear to approve)
|
|
31
|
-
*/
|
|
32
9
|
export function unmetPrereqs(gate, gates) {
|
|
33
10
|
const prereqs = GATE_PREREQ[gate] || [];
|
|
34
11
|
return prereqs.filter((p) => !isApproved(gates, p));
|
|
35
12
|
}
|
|
36
13
|
|
|
37
|
-
/**
|
|
38
|
-
* All monotonicity violations currently present in a feature's gates.
|
|
39
|
-
* @param {object} gates - feature.approvalGates
|
|
40
|
-
* @returns {Array<{gate: string, missing: string[]}>} empty when in order
|
|
41
|
-
*/
|
|
42
14
|
export function gateOrderViolations(gates) {
|
|
43
15
|
if (!gates || typeof gates !== 'object') return [];
|
|
44
16
|
const out = [];
|
|
@@ -1,23 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Feature-lifecycle audit — the consolidated inconsistency scanner behind
|
|
3
|
-
* `morph-spec doctor`. Read-only, non-throwing: scans `.morph/features/*` and
|
|
4
|
-
* the thin root index and reports what's already wrong in the repo, without
|
|
5
|
-
* touching state (never calls loadState — a diagnostic must not mutate).
|
|
6
|
-
*
|
|
7
|
-
* Issue types:
|
|
8
|
-
* • gate-order — a gate approved with a predecessor pending (item 4)
|
|
9
|
-
* • identity — a feature missing description/name/createdAt (item 3)
|
|
10
|
-
* • orphan-folder — a feature folder with no feature.json
|
|
11
|
-
* • stale-index — a root-index entry with no feature folder (auto-heals on
|
|
12
|
-
* the next loadState, but surfaced so it's not a mystery)
|
|
13
|
-
* • branch-drift — the root's copy of a feature.json disagrees with the copy
|
|
14
|
-
* committed on `morph/{f}` (B5 — the root's view lags)
|
|
15
|
-
* • abandoned-scaffold — an `in_progress` feature that never produced
|
|
16
|
-
* anything: no artifacts, no approved gate, and a branch
|
|
17
|
-
* with no commit of its own (see abandonedScaffold)
|
|
18
|
-
* • corrupt — a feature.json that won't parse
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
1
|
import { execFileSync } from 'child_process';
|
|
22
2
|
import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
|
|
23
3
|
import { join } from 'path';
|
|
@@ -26,40 +6,19 @@ import { missingIdentity } from './feature-identity.js';
|
|
|
26
6
|
import { detectDefaultBranch } from '../../lib/git/default-branch.js';
|
|
27
7
|
import { commitsAhead, resolveBaseRef } from '../../lib/git/branch-classifier.js';
|
|
28
8
|
|
|
29
|
-
/** The operational truth of a feature.json: its status and which gates are approved. */
|
|
30
9
|
function operationalShape(fj) {
|
|
31
10
|
const gates = {};
|
|
32
11
|
for (const [g, v] of Object.entries(fj?.approvalGates || {})) gates[g] = v?.approved === true;
|
|
33
12
|
return JSON.stringify({ status: fj?.status ?? null, gates });
|
|
34
13
|
}
|
|
35
14
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* COMMITTED on that feature's own branch?
|
|
39
|
-
*
|
|
40
|
-
* `.morph/state.json` is gitignored and derived, and every worktree keeps its
|
|
41
|
-
* own — so "the index diverges between worktrees" is by design, not a defect to
|
|
42
|
-
* fix by making the index authoritative (that would undo the whole per-feature
|
|
43
|
-
* state model). The defect is that the divergence was INVISIBLE: since v8.4 the
|
|
44
|
-
* authoritative feature.json travels committed on `morph/{f}`, so a primary root
|
|
45
|
-
* parked on the default holds a copy that silently lags, and everything derived
|
|
46
|
-
* from it (status, gates, and the index rebuilt from it) lags with it.
|
|
47
|
-
*
|
|
48
|
-
* Comparison is on the OPERATIONAL fields only — status and approved-gate flags
|
|
49
|
-
* — not on bytes: reformatting or a timestamp bump is not a divergence anyone
|
|
50
|
-
* needs to hear about.
|
|
51
|
-
*
|
|
52
|
-
* Fail-open at every step: no repo, no branch, no blob, unparseable JSON → no
|
|
53
|
-
* issue. A diagnostic that invents problems is worse than one that misses some.
|
|
54
|
-
*
|
|
55
|
-
* @returns {string|null} the issue detail, or null when the copies agree
|
|
56
|
-
*/
|
|
15
|
+
// Compares only status and approved gates (bytes and timestamps are not drift).
|
|
16
|
+
// Fail-open: no repo, branch, blob or parseable JSON means no issue.
|
|
57
17
|
function branchDrift(baseDir, feature, rootFj) {
|
|
58
18
|
const branch = `morph/${feature}`;
|
|
59
19
|
try {
|
|
60
20
|
const current = execFileSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: baseDir, stdio: 'pipe' })
|
|
61
21
|
.toString().trim();
|
|
62
|
-
// On the feature's own branch the working tree IS the branch — nothing to compare.
|
|
63
22
|
if (current === branch) return null;
|
|
64
23
|
|
|
65
24
|
execFileSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: baseDir, stdio: 'pipe' });
|
|
@@ -77,18 +36,9 @@ function branchDrift(baseDir, feature, rootFj) {
|
|
|
77
36
|
}
|
|
78
37
|
}
|
|
79
38
|
|
|
80
|
-
|
|
81
|
-
* How long a scaffold has to sit untouched before it counts as abandoned.
|
|
82
|
-
*
|
|
83
|
-
* A dead scaffold and a feature born five minutes ago have the SAME shape:
|
|
84
|
-
* `create` forks `morph/{f}` off the default branch without committing, so at
|
|
85
|
-
* birth the branch has no commit of its own and the folder holds nothing but
|
|
86
|
-
* `feature.json`. Only elapsed time separates the two — and a check that fires
|
|
87
|
-
* on every fresh `create` is one people learn to scroll past.
|
|
88
|
-
*/
|
|
39
|
+
// A dead scaffold and a feature born minutes ago have the same shape; only age tells them apart.
|
|
89
40
|
const ABANDONED_SCAFFOLD_MIN_AGE_MS = 24 * 60 * 60 * 1000;
|
|
90
41
|
|
|
91
|
-
/** Newest timestamp the feature carries, in ms; falls back to the folder's mtime. */
|
|
92
42
|
function lastTouched(fdir, fj) {
|
|
93
43
|
let newest = 0;
|
|
94
44
|
for (const ts of [fj?.updatedAt, fj?.createdAt]) {
|
|
@@ -99,12 +49,7 @@ function lastTouched(fdir, fj) {
|
|
|
99
49
|
try { return statSync(fdir).mtimeMs; } catch { return 0; }
|
|
100
50
|
}
|
|
101
51
|
|
|
102
|
-
|
|
103
|
-
* Does the feature dir hold any FILE other than its own `feature.json`?
|
|
104
|
-
*
|
|
105
|
-
* Files, not entries: `create` scaffolds the phase folders empty, so an empty
|
|
106
|
-
* `0-proposal/` is the absence of work, not evidence of it.
|
|
107
|
-
*/
|
|
52
|
+
// Files, not entries: `create` scaffolds the phase folders empty.
|
|
108
53
|
function hasArtifacts(fdir) {
|
|
109
54
|
let entries;
|
|
110
55
|
try { entries = readdirSync(fdir, { withFileTypes: true }); } catch { return false; }
|
|
@@ -119,22 +64,7 @@ function hasArtifacts(fdir) {
|
|
|
119
64
|
return false;
|
|
120
65
|
}
|
|
121
66
|
|
|
122
|
-
|
|
123
|
-
* Commits on the feature's branch that the base does not already contain, or
|
|
124
|
-
* `null` when git cannot answer (no repo, no resolvable base, no branch, no
|
|
125
|
-
* rev-list). "Cannot prove" must never read as "proved zero" — the predicate
|
|
126
|
-
* below hinges on this number, so an unanswerable repo has to stay silent.
|
|
127
|
-
*
|
|
128
|
-
* The counting itself is `branch-classifier.js`'s `commitsAhead` against its
|
|
129
|
-
* `resolveBaseRef`, NOT a second copy: `origin/{default}` is the authority on
|
|
130
|
-
* what actually landed, and two drifting answers to "does this branch carry
|
|
131
|
-
* anything of its own?" is exactly what the `doctor`/`branch prune` split was
|
|
132
|
-
* unified to avoid.
|
|
133
|
-
*
|
|
134
|
-
* Note the field evidence this exists for: the branch pointed at the merge
|
|
135
|
-
* commit of somebody else's PR. That is the base's own tip — reachable from
|
|
136
|
-
* the base, and therefore zero commits of its own.
|
|
137
|
-
*/
|
|
67
|
+
// `null` when git cannot answer: "cannot prove" must never read as "proved zero".
|
|
138
68
|
function ownCommitCount(baseDir, branch) {
|
|
139
69
|
try {
|
|
140
70
|
execFileSync('git', ['rev-parse', '--git-dir'], { cwd: baseDir, stdio: 'pipe' });
|
|
@@ -146,31 +76,15 @@ function ownCommitCount(baseDir, branch) {
|
|
|
146
76
|
try {
|
|
147
77
|
execFileSync('git', ['show-ref', '--verify', '--quiet', `refs/heads/${branch}`], { cwd: baseDir, stdio: 'pipe' });
|
|
148
78
|
} catch {
|
|
149
|
-
return 0; // the branch never existed
|
|
79
|
+
return 0; // the branch never existed: nothing was committed on it
|
|
150
80
|
}
|
|
151
81
|
return commitsAhead(baseRef, branch, baseDir);
|
|
152
82
|
}
|
|
153
83
|
|
|
154
|
-
/**
|
|
155
|
-
* The orphan skeleton `create --worktree` can leave behind: a feature.json on
|
|
156
|
-
* its own, `status: in_progress`, no tasks, no gates, and a branch parked on
|
|
157
|
-
* somebody else's commit. Every one of those alone is normal at some point in
|
|
158
|
-
* a feature's life; together, and only after the feature has gone stale, they
|
|
159
|
-
* mean nobody ever came back to it.
|
|
160
|
-
*
|
|
161
|
-
* The artifact test is deliberately broader than the "0 tasks" the report
|
|
162
|
-
* named: a proposal drafted and waiting on Gate 1 also has zero tasks and is
|
|
163
|
-
* very much alive. What a dead scaffold has is NOTHING — checked in the root
|
|
164
|
-
* and, if the feature registered one, in its worktree, where uncommitted work
|
|
165
|
-
* would otherwise be invisible to both the disk scan and `rev-list`.
|
|
166
|
-
*
|
|
167
|
-
* @returns {string|null} the issue detail, or null when the feature is alive
|
|
168
|
-
*/
|
|
169
84
|
function abandonedScaffold(baseDir, feature, fdir, fj) {
|
|
170
85
|
if (fj?.status !== 'in_progress') return null;
|
|
171
86
|
|
|
172
|
-
//
|
|
173
|
-
// never a decision anyone took, so it must not read as progress.
|
|
87
|
+
// A gate stamped `policy:{workType}` at birth was never a decision, so it is not progress.
|
|
174
88
|
const decided = Object.values(fj.approvalGates || {}).some(
|
|
175
89
|
(g) => g?.approved === true && !String(g?.approvedBy ?? '').startsWith('policy:'),
|
|
176
90
|
);
|
|
@@ -185,8 +99,6 @@ function abandonedScaffold(baseDir, feature, fdir, fj) {
|
|
|
185
99
|
const age = Date.now() - lastTouched(fdir, fj);
|
|
186
100
|
if (age < ABANDONED_SCAFFOLD_MIN_AGE_MS) return null;
|
|
187
101
|
|
|
188
|
-
// The registered branch when the feature has one, `morph/{f}` otherwise —
|
|
189
|
-
// same name `branchDrift` above assumes.
|
|
190
102
|
const branch = fj.worktree?.branch || `morph/${feature}`;
|
|
191
103
|
if (ownCommitCount(baseDir, branch) !== 0) return null;
|
|
192
104
|
|
|
@@ -196,10 +108,6 @@ function abandonedScaffold(baseDir, feature, fdir, fj) {
|
|
|
196
108
|
+ 'ou retome a feature';
|
|
197
109
|
}
|
|
198
110
|
|
|
199
|
-
/**
|
|
200
|
-
* @param {string} [baseDir] - project root (defaults to cwd)
|
|
201
|
-
* @returns {Array<{ feature: string, type: string, detail: string }>} issues
|
|
202
|
-
*/
|
|
203
111
|
export function auditFeatureLifecycle(baseDir = process.cwd()) {
|
|
204
112
|
const issues = [];
|
|
205
113
|
const featuresDir = join(baseDir, '.morph', 'features');
|
|
@@ -256,7 +164,7 @@ export function auditFeatureLifecycle(baseDir = process.cwd()) {
|
|
|
256
164
|
issues.push({ feature: name, type: 'stale-index', detail: 'in state.json index but no feature folder' });
|
|
257
165
|
}
|
|
258
166
|
}
|
|
259
|
-
} catch {
|
|
167
|
+
} catch { }
|
|
260
168
|
}
|
|
261
169
|
|
|
262
170
|
return issues;
|