@opengsd/gsd-core 1.14.0 → 1.16.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-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check verify-codebase-drift` as a gate module (#5219, epic #5056, ADR-5057 §4 closing arm C): it
|
|
4
|
+
* returns a `GateResult`; the command router formats it. Imports no io module and performs no direct
|
|
5
|
+
* console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* The `drift` capability's gate at `execute:wave:post`. It diffs the repository's HEAD against the
|
|
8
|
+
* commit `.planning/codebase/STRUCTURE.md` was last mapped at (`last_mapped_commit`, stamped by
|
|
9
|
+
* `stamp-codebase-map`) and reports structural change the map does not describe (`detectDrift`,
|
|
10
|
+
* `drift.cjs`). Non-blocking unless the drift crosses the configured threshold (`block` = the drift
|
|
11
|
+
* library's `actionRequired`).
|
|
12
|
+
*
|
|
13
|
+
* Dispatched gate, payload exit mode (see `gate-schema-drift.cts`): a blocking verdict is exit 0 and
|
|
14
|
+
* "could not look" is exit 69 (#5170).
|
|
15
|
+
*
|
|
16
|
+
* Non-blocking contract: a throw anywhere yields a non-blocking `unreadable` verdict, never a crash.
|
|
17
|
+
*
|
|
18
|
+
* Argv after the verb: none.
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.evaluateCodebaseDriftGate = evaluateCodebaseDriftGate;
|
|
25
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
26
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
27
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
28
|
+
const gate_config_cjs_1 = require("./gate-config.cjs");
|
|
29
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
30
|
+
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
32
|
+
const planningWorkspace = require("./planning-workspace.cjs");
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- worktree-safety.cjs is an export= CommonJS module
|
|
34
|
+
const worktreeSafetyMod = require("./worktree-safety.cjs");
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- onboard-projection.cjs is an export= CommonJS module
|
|
36
|
+
const onboardProjectionMod = require("./onboard-projection.cjs");
|
|
37
|
+
// drift.cjs is a pure library that imports no gate or verify module, so it is loaded at the top.
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- drift.cjs is an export= CommonJS module
|
|
39
|
+
const driftModule = require("./drift.cjs");
|
|
40
|
+
const drift = driftModule;
|
|
41
|
+
/**
|
|
42
|
+
* A mapped-commit stamp must be a hex object id (abbreviated to at least 7, or a full SHA-1 / SHA-256
|
|
43
|
+
* id) before git sees it. A ref name (`HEAD`), a shorter abbreviation and an option-shaped value
|
|
44
|
+
* (`--batch`) are not baselines: each is routed to `unresolvable-mapped-commit`, so no stamp can reach
|
|
45
|
+
* `git cat-file` / `git diff` as anything but an object id.
|
|
46
|
+
*/
|
|
47
|
+
const HEX_OBJECT_ID_RE = /^[0-9a-f]{7,64}$/i;
|
|
48
|
+
const { planningDir, planningRoot } = planningWorkspace;
|
|
49
|
+
// Single owner of git C-quoted-path decoding (see #4081 note at the --name-status parse loop).
|
|
50
|
+
const { decodeGitQuotedPath } = worktreeSafetyMod;
|
|
51
|
+
const { REQUIRED_CODEBASE_MAP_FILES } = onboardProjectionMod;
|
|
52
|
+
/**
|
|
53
|
+
* A generated document is read only when it is a regular file (symlinks followed) no larger than
|
|
54
|
+
* this: a FIFO would block the gate forever and a huge file would exhaust memory.
|
|
55
|
+
*/
|
|
56
|
+
const MAX_DOCUMENT_BYTES = 1048576;
|
|
57
|
+
function readDocument(file) {
|
|
58
|
+
const st = node_fs_1.default.statSync(file);
|
|
59
|
+
if (!st.isFile())
|
|
60
|
+
throw new Error('not a regular file');
|
|
61
|
+
if (st.size > MAX_DOCUMENT_BYTES)
|
|
62
|
+
throw new Error(`larger than ${MAX_DOCUMENT_BYTES} bytes`);
|
|
63
|
+
return node_fs_1.default.readFileSync(file, 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The drift gates' fixed non-answer payload; `skip` is a documented "nothing to compare", `unreadable`
|
|
67
|
+
* is "could not look" (#5170) and exits UNAVAILABLE. Both keep the non-blocking payload.
|
|
68
|
+
*/
|
|
69
|
+
function nonAnswer(reason, extra = {}) {
|
|
70
|
+
return {
|
|
71
|
+
// Uniform gate contract: block = action_required (false when skipped).
|
|
72
|
+
block: false,
|
|
73
|
+
skipped: true,
|
|
74
|
+
reason,
|
|
75
|
+
action_required: false,
|
|
76
|
+
directive: 'none',
|
|
77
|
+
elements: [],
|
|
78
|
+
...extra,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function skipped(reason, extra = {}) {
|
|
82
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, nonAnswer(reason, extra));
|
|
83
|
+
}
|
|
84
|
+
function unreadable(reason, extra = {}) {
|
|
85
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(false, nonAnswer(reason, extra));
|
|
86
|
+
}
|
|
87
|
+
function runCodebaseDriftGate(cwd) {
|
|
88
|
+
const codebaseDir = node_path_1.default.join(planningDir(cwd), 'codebase');
|
|
89
|
+
const structurePath = node_path_1.default.join(codebaseDir, 'STRUCTURE.md');
|
|
90
|
+
let structureMd;
|
|
91
|
+
try {
|
|
92
|
+
structureMd = readDocument(structurePath);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
if (err?.code === 'ENOENT') {
|
|
96
|
+
return skipped('no-structure-md');
|
|
97
|
+
}
|
|
98
|
+
return unreadable('cannot-read-structure-md: ' + (err instanceof Error ? err.message : String(err)));
|
|
99
|
+
}
|
|
100
|
+
const lastMapped = drift['readMappedCommit'](structurePath);
|
|
101
|
+
const revProbe = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', 'HEAD'], { cwd });
|
|
102
|
+
if (revProbe.exitCode !== 0) {
|
|
103
|
+
return skipped('not-a-git-repo');
|
|
104
|
+
}
|
|
105
|
+
// #3418: an absent or unresolvable baseline means NO COMPARISON IS POSSIBLE.
|
|
106
|
+
// It is neither zero drift nor total drift, and reporting it as either is a
|
|
107
|
+
// lie the consumer cannot detect. The former fallback diffed HEAD against
|
|
108
|
+
// the empty tree, so every tracked file read as newly added and the gate
|
|
109
|
+
// reported maximum drift identically on every run -- which made a genuinely
|
|
110
|
+
// stale map indistinguishable from a fresh one, and let `spawn_mapper` fire
|
|
111
|
+
// a whole-repo remap while presenting itself as an incremental one.
|
|
112
|
+
if (!lastMapped) {
|
|
113
|
+
return skipped('no-mapped-commit', { last_mapped_commit: null });
|
|
114
|
+
}
|
|
115
|
+
// A stamp that is not a hex object id is not a baseline (a ref name such as `HEAD` would "resolve" to
|
|
116
|
+
// a commit and diff against the wrong object; an option-shaped value must never reach git): same arm
|
|
117
|
+
// as a stamp git cannot resolve, and no git call is made with it.
|
|
118
|
+
if (!HEX_OBJECT_ID_RE.test(lastMapped)) {
|
|
119
|
+
return unreadable('unresolvable-mapped-commit', { last_mapped_commit: lastMapped });
|
|
120
|
+
}
|
|
121
|
+
const baseProbe = (0, shell_command_projection_cjs_1.execGit)(['cat-file', '-t', lastMapped], { cwd });
|
|
122
|
+
if (baseProbe.exitCode !== 0 || baseProbe.stdout.trim() !== 'commit') {
|
|
123
|
+
// A stamp git cannot resolve: history rewrite, GC, or a shallow clone.
|
|
124
|
+
// Distinct reason from 'no-mapped-commit' -- the map claims a baseline,
|
|
125
|
+
// this repository just cannot see it, which is an operator-actionable
|
|
126
|
+
// difference (re-map vs. unshallow). A resolvable non-commit (a tree or
|
|
127
|
+
// blob sha) is the same class of bad baseline: git would happily diff
|
|
128
|
+
// against it and report drift against the wrong object.
|
|
129
|
+
// The repository cannot resolve the baseline the map claims: it could not look (#5170).
|
|
130
|
+
return unreadable('unresolvable-mapped-commit', { last_mapped_commit: lastMapped });
|
|
131
|
+
}
|
|
132
|
+
const base = lastMapped;
|
|
133
|
+
const diff = (0, shell_command_projection_cjs_1.execGit)(['diff', '--name-status', base, 'HEAD'], { cwd });
|
|
134
|
+
if (diff.exitCode !== 0) {
|
|
135
|
+
return unreadable('git-diff-failed');
|
|
136
|
+
}
|
|
137
|
+
// #3418: GSD's own planning artifacts are not codebase structure. A
|
|
138
|
+
// map-codebase run commits `.planning/codebase/*.md`, so a correctly
|
|
139
|
+
// stamped baseline would be re-poisoned by the very commit that carries
|
|
140
|
+
// the stamp -- the next gate invocation would report the map's own seven
|
|
141
|
+
// documents as seven new directories, back over the default threshold of
|
|
142
|
+
// three. Derived from planningRoot() rather than a hardcoded literal so a
|
|
143
|
+
// repoint of the planning root cannot leave this filter behind.
|
|
144
|
+
//
|
|
145
|
+
// `git diff --name-status` always prints repo-root-relative paths, so a cwd
|
|
146
|
+
// below the root needs the `sub/` prefix or the filter matches nothing.
|
|
147
|
+
// That prefix comes from git (`--show-prefix`: root-relative, forward
|
|
148
|
+
// slashes, trailing slash, empty at the root). The rejected alternative was
|
|
149
|
+
// path.relative(`--show-toplevel`, cwd), which mixes two path producers: on
|
|
150
|
+
// Windows os.tmpdir() hands back the 8.3 short form while git resolves the
|
|
151
|
+
// long one, so relative() between them yields a `../..` chain that matches
|
|
152
|
+
// nothing. The `.planning` half below is safe to compute with relative()
|
|
153
|
+
// because both of its sides are the same cwd string.
|
|
154
|
+
const prefixProbe = (0, shell_command_projection_cjs_1.execGit)(['rev-parse', '--show-prefix'], { cwd });
|
|
155
|
+
const repoPrefix = prefixProbe.exitCode === 0 ? prefixProbe.stdout.trim() : '';
|
|
156
|
+
const planningPrefix = repoPrefix + node_path_1.default.relative(cwd, planningRoot(cwd)).split(node_path_1.default.sep).join('/') + '/';
|
|
157
|
+
const isPlanningArtifact = (file) => file.split('\\').join('/').startsWith(planningPrefix);
|
|
158
|
+
const added = [];
|
|
159
|
+
const modified = [];
|
|
160
|
+
const deleted = [];
|
|
161
|
+
for (const line of diff.stdout.split(/\r?\n/)) {
|
|
162
|
+
if (!line.trim())
|
|
163
|
+
continue;
|
|
164
|
+
const m = line.match(/^([A-Z])\d*\t(.+?)(?:\t(.+))?$/);
|
|
165
|
+
if (!m)
|
|
166
|
+
continue;
|
|
167
|
+
const status = m[1];
|
|
168
|
+
// execGit sets no core.quotepath config, so git's default `true` applies:
|
|
169
|
+
// any path containing non-ASCII bytes (or `"`, `\`, control bytes) is
|
|
170
|
+
// C-quoted — `"docs/\350\256\276…/overview.md"`. Capturing that verbatim
|
|
171
|
+
// garbles affected_paths/elements and makes isPathMapped compare the
|
|
172
|
+
// quoted prefix (`"docs`) against STRUCTURE.md, misclassifying DOCUMENTED
|
|
173
|
+
// directories as new_dir (#4081). Decode with the single owner of the
|
|
174
|
+
// git C-quote seam (worktree-safety.cjs); a non-quoted value — the plain
|
|
175
|
+
// ASCII common case — passes through untouched. Both capture groups are
|
|
176
|
+
// decoded: R/C lines carry old AND new paths, either may be quoted.
|
|
177
|
+
// A rename is a deletion of the old path plus an addition of the new
|
|
178
|
+
// one; a copy leaves its source in place and adds only the new path.
|
|
179
|
+
const oldPath = decodeGitQuotedPath(m[2]);
|
|
180
|
+
const newPath = m[3] ? decodeGitQuotedPath(m[3]) : oldPath;
|
|
181
|
+
const put = (file, into) => {
|
|
182
|
+
if (!isPlanningArtifact(file))
|
|
183
|
+
into.push(file);
|
|
184
|
+
};
|
|
185
|
+
if (status === 'R') {
|
|
186
|
+
put(oldPath, deleted);
|
|
187
|
+
put(newPath, added);
|
|
188
|
+
}
|
|
189
|
+
else if (status === 'C')
|
|
190
|
+
put(newPath, added);
|
|
191
|
+
else if (status === 'A')
|
|
192
|
+
put(newPath, added);
|
|
193
|
+
else if (status === 'M' || status === 'T')
|
|
194
|
+
put(newPath, modified);
|
|
195
|
+
else if (status === 'D')
|
|
196
|
+
put(newPath, deleted);
|
|
197
|
+
}
|
|
198
|
+
// Every generated document is territory the map describes, so all seven are
|
|
199
|
+
// read (the one owner of the names is REQUIRED_CODEBASE_MAP_FILES).
|
|
200
|
+
// STRUCTURE.md was read above; an unreadable other document is omitted and
|
|
201
|
+
// named rather than sinking the whole check, and an absent one is simply
|
|
202
|
+
// not part of this map (a `--fast` map writes four of the seven).
|
|
203
|
+
const documents = {};
|
|
204
|
+
const documentsRead = [];
|
|
205
|
+
const documentsUnreadable = [];
|
|
206
|
+
for (const name of REQUIRED_CODEBASE_MAP_FILES) {
|
|
207
|
+
if (name === 'STRUCTURE.md') {
|
|
208
|
+
documents[name] = structureMd;
|
|
209
|
+
documentsRead.push(name);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
documents[name] = readDocument(node_path_1.default.join(codebaseDir, name));
|
|
214
|
+
documentsRead.push(name);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
if (err?.code !== 'ENOENT')
|
|
218
|
+
documentsUnreadable.push(name);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// loadConfig() returns a flattened object — there is no nested `workflow`
|
|
222
|
+
// key. Read the workflow-scoped keys through the quiet gate-config reader (the dot-path
|
|
223
|
+
// resolver `config-get workflow.*` shares: workstream config first, then the project
|
|
224
|
+
// root's; a missing or malformed config is "key absent", nothing is printed).
|
|
225
|
+
const configuredThreshold = (0, gate_config_cjs_1.readWorkflowConfigValue)(cwd, 'workflow.drift_threshold').value;
|
|
226
|
+
const threshold = Number.isInteger(configuredThreshold) && configuredThreshold >= 1
|
|
227
|
+
? configuredThreshold
|
|
228
|
+
: 3;
|
|
229
|
+
const action = (0, gate_config_cjs_1.readWorkflowConfigValue)(cwd, 'workflow.drift_action').value === 'auto-remap' ? 'auto-remap' : 'warn';
|
|
230
|
+
const driftResult = drift['detectDrift']({
|
|
231
|
+
addedFiles: added,
|
|
232
|
+
modifiedFiles: modified,
|
|
233
|
+
deletedFiles: deleted,
|
|
234
|
+
documents,
|
|
235
|
+
threshold,
|
|
236
|
+
action,
|
|
237
|
+
runtime: (0, runtime_slash_cjs_1.resolveRuntime)(cwd),
|
|
238
|
+
});
|
|
239
|
+
const actionRequired = !!driftResult['actionRequired'];
|
|
240
|
+
// Paths are attacker-controlled (they come from git); the raw values stay
|
|
241
|
+
// in the library result, the CLI JSON carries display-safe renderings and
|
|
242
|
+
// a bounded withheld list with its true size alongside.
|
|
243
|
+
const display = drift['displaySafePath'];
|
|
244
|
+
const WITHHELD_LIST_CAP = 50;
|
|
245
|
+
const withheldAll = driftResult['withheldPaths'] || [];
|
|
246
|
+
const elementsRaw = driftResult['elements'] || [];
|
|
247
|
+
const isSkipped = !!driftResult['skipped'];
|
|
248
|
+
const payload = {
|
|
249
|
+
// Uniform gate contract: block = action_required.
|
|
250
|
+
block: actionRequired,
|
|
251
|
+
skipped: isSkipped,
|
|
252
|
+
reason: driftResult['reason'] || null,
|
|
253
|
+
action_required: actionRequired,
|
|
254
|
+
directive: driftResult['directive'],
|
|
255
|
+
spawn_mapper: !!driftResult['spawnMapper'],
|
|
256
|
+
affected_paths: driftResult['affectedPaths'] || [],
|
|
257
|
+
withheld_paths: withheldAll.slice(0, WITHHELD_LIST_CAP).map((p) => display(p)),
|
|
258
|
+
withheld_count: withheldAll.length,
|
|
259
|
+
documents_read: documentsRead,
|
|
260
|
+
documents_unreadable: documentsUnreadable,
|
|
261
|
+
elements: elementsRaw.map((e) => ({ category: e.category, path: display(e.path) })),
|
|
262
|
+
threshold,
|
|
263
|
+
action,
|
|
264
|
+
last_mapped_commit: lastMapped,
|
|
265
|
+
message: driftResult['message'] || '',
|
|
266
|
+
};
|
|
267
|
+
// #5170: the drift was detected over fewer documents than the map has. A BLOCKING verdict stands
|
|
268
|
+
// (more documents could only add territory the map describes); anything else was computed from
|
|
269
|
+
// evidence the gate never saw and is `unreadable` (exit UNAVAILABLE), never a clean pass or skip.
|
|
270
|
+
return actionRequired
|
|
271
|
+
? (0, gate_verdict_cjs_1.gateVerdict)('block', true, payload)
|
|
272
|
+
: documentsUnreadable.length > 0
|
|
273
|
+
? (0, gate_verdict_cjs_1.gateUnreadable)(false, payload)
|
|
274
|
+
: (0, gate_verdict_cjs_1.gateVerdict)(isSkipped ? 'skip' : 'pass', false, payload);
|
|
275
|
+
}
|
|
276
|
+
function evaluateCodebaseDriftGate(input) {
|
|
277
|
+
// Non-blocking contract: a throw anywhere yields the non-blocking payload; the exit status says
|
|
278
|
+
// "could not look" (#5170) rather than a clean exit 0.
|
|
279
|
+
try {
|
|
280
|
+
return runCodebaseDriftGate(input.projectDir);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
return unreadable('exception: ' + (err && err instanceof Error ? err.message : String(err)));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gate config — the one place a gate reads a `workflow.*` switch (#5139, epic #5056, ADR-5057
|
|
4
|
+
* Phase 6, design D5; fixes #4978).
|
|
5
|
+
*
|
|
6
|
+
* A gate module may not import `./io.cjs` and may not hand-parse `config.json`. It reads its switch
|
|
7
|
+
* through the shared dot-path resolver `config-get workflow.*` is defined by
|
|
8
|
+
* (`capability-activation.cts` `resolveConfigKey`): own-property traversal (a prototype-chain key
|
|
9
|
+
* is never a value), the active workstream's config first, then the project root's, and a missing,
|
|
10
|
+
* unreadable or non-object config is simply "the key is absent".
|
|
11
|
+
*
|
|
12
|
+
* The lookup is NESTED-ONLY. A top-level `context_coverage_gate` / `auto_advance` /
|
|
13
|
+
* `_auto_chain_active` is never read — `config-get workflow.<key>` does not answer it either, so a
|
|
14
|
+
* gate and `config-get` can no longer disagree about the same config file (#4978).
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.readWorkflowConfigValue = readWorkflowConfigValue;
|
|
18
|
+
exports.isDecisionCoverageGateEnabled = isDecisionCoverageGateEnabled;
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
20
|
+
const capabilityActivationMod = require("./capability-activation.cjs");
|
|
21
|
+
const { resolveConfigKey } = capabilityActivationMod;
|
|
22
|
+
/**
|
|
23
|
+
* The resolved raw value of a dotted config key, or `{ found: false }` when absent/unreadable.
|
|
24
|
+
* QUIET: a malformed config.json is "key absent" with nothing written to stderr (the pre-#5139
|
|
25
|
+
* gate readers printed nothing either); `config-get` keeps its one-time parse warning.
|
|
26
|
+
*/
|
|
27
|
+
function readWorkflowConfigValue(projectDir, dotKey) {
|
|
28
|
+
return resolveConfigKey(dotKey, { config: {}, cwd: projectDir, registry: {}, quiet: true });
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `workflow.context_coverage_gate`: the decision-coverage gates' on/off switch.
|
|
32
|
+
* A boolean is taken as written; the strings `'true'`/`'false'` (any case) are coerced; anything
|
|
33
|
+
* else — including an absent key or an unreadable config — leaves the gate ENABLED.
|
|
34
|
+
*/
|
|
35
|
+
function isDecisionCoverageGateEnabled(projectDir) {
|
|
36
|
+
const resolved = readWorkflowConfigValue(projectDir, 'workflow.context_coverage_gate');
|
|
37
|
+
const value = resolved.found ? resolved.value : undefined;
|
|
38
|
+
if (typeof value === 'boolean')
|
|
39
|
+
return value;
|
|
40
|
+
if (typeof value === 'string') {
|
|
41
|
+
const lower = value.toLowerCase();
|
|
42
|
+
if (lower === 'false' || lower === 'true')
|
|
43
|
+
return lower !== 'false';
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check verify-context-drift` as a gate module (#5219, epic #5056, ADR-5057 §4 closing arm C): it
|
|
4
|
+
* returns a `GateResult`; the command router formats it. Imports no io module and performs no direct
|
|
5
|
+
* console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* The `drift` capability's gate at `plan:pre` (#3348). It reports the phase's upstream artifacts
|
|
8
|
+
* (RESEARCH, PATTERNS, VALIDATION, SPEC) whose effective last-changed time is STRICTLY BEFORE the
|
|
9
|
+
* phase CONTEXT.md's own: the decisions moved after the artifact was written. Non-blocking unless
|
|
10
|
+
* `workflow.context_drift_action` is `block`.
|
|
11
|
+
*
|
|
12
|
+
* Dispatched gate, payload exit mode (see `gate-schema-drift.cts`): a blocking verdict is exit 0 and
|
|
13
|
+
* "could not look" is exit 69 (#5170).
|
|
14
|
+
*
|
|
15
|
+
* Non-blocking contract: a throw anywhere (an invalid GSD_WORKSTREAM, an unreadable file) yields a
|
|
16
|
+
* non-blocking `unreadable` verdict, never a crash.
|
|
17
|
+
*
|
|
18
|
+
* Argv after the verb: `<phase>`.
|
|
19
|
+
*/
|
|
20
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
21
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
22
|
+
};
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.computeContextDrift = computeContextDrift;
|
|
25
|
+
exports.evaluateContextDriftGate = evaluateContextDriftGate;
|
|
26
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
27
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
28
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
29
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
30
|
+
const gate_config_cjs_1 = require("./gate-config.cjs");
|
|
31
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
33
|
+
const planningWorkspace = require("./planning-workspace.cjs");
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- verification.cjs is an export= CommonJS module
|
|
35
|
+
const verificationMod = require("./verification.cjs");
|
|
36
|
+
const { planningDir } = planningWorkspace;
|
|
37
|
+
const { defaultPhaseCleanCommitTimesMs } = verificationMod;
|
|
38
|
+
/**
|
|
39
|
+
* Pure comparator: which of `entries` have an effective last-changed time
|
|
40
|
+
* STRICTLY BEFORE `contextEffectiveMs` (CONTEXT.md's own effective time)? Strict
|
|
41
|
+
* `<` is "stale" (matches findStaleVerificationSummary's own strict `>` convention
|
|
42
|
+
* for "newer than" elsewhere in this codebase — an artifact committed in the SAME
|
|
43
|
+
* commit/second as CONTEXT.md is in sync, not stale).
|
|
44
|
+
*/
|
|
45
|
+
function computeContextDrift(contextEffectiveMs, entries) {
|
|
46
|
+
return entries.filter((e) => e.effectiveMs < contextEffectiveMs).map((e) => e.file);
|
|
47
|
+
}
|
|
48
|
+
function buildContextDriftMessage(staleArtifacts, phaseArg) {
|
|
49
|
+
const parts = [`CONTEXT.md decisions are newer than: ${staleArtifacts.join(', ')}.`];
|
|
50
|
+
if (staleArtifacts.some((f) => f.endsWith('-RESEARCH.md'))) {
|
|
51
|
+
parts.push(`Regenerate research: /gsd:plan-phase ${phaseArg} --research.`);
|
|
52
|
+
}
|
|
53
|
+
if (staleArtifacts.some((f) => f.endsWith('-PATTERNS.md'))) {
|
|
54
|
+
parts.push('Regenerate patterns: delete the PATTERNS.md file, then re-run /gsd:plan-phase.');
|
|
55
|
+
}
|
|
56
|
+
if (staleArtifacts.some((f) => f.endsWith('-VALIDATION.md') || (f.endsWith('-SPEC.md') && !f.endsWith('-AI-SPEC.md') && !f.endsWith('-UI-SPEC.md')))) {
|
|
57
|
+
parts.push('Regenerate or manually reconcile VALIDATION.md / SPEC.md against the current decisions.');
|
|
58
|
+
}
|
|
59
|
+
parts.push('Do not hand-inject the newer decisions into a prompt as a substitute for regenerating — that carries the staleness forward.');
|
|
60
|
+
return parts.join(' ');
|
|
61
|
+
}
|
|
62
|
+
/** The non-blocking context-drift payload for an arm that did not compare artifacts: `skip` or `unreadable`. */
|
|
63
|
+
function contextDriftPayload(build, reason, message = '') {
|
|
64
|
+
return build(false, { block: false, skipped: true, reason, stale_artifacts: [], message });
|
|
65
|
+
}
|
|
66
|
+
function runContextDriftGate(cwd, phaseArg) {
|
|
67
|
+
const pDir = planningDir(cwd);
|
|
68
|
+
const phasesDir = node_path_1.default.join(pDir, 'phases');
|
|
69
|
+
// `none`: a documented "nothing to compare" (no CONTEXT.md, no upstream artifact).
|
|
70
|
+
const skip = (reason, message = '') => contextDriftPayload((block, payload) => (0, gate_verdict_cjs_1.gateVerdict)('skip', block, payload), reason, message);
|
|
71
|
+
// An unresolvable phase is "could not look" (#5170): there is no phase directory to compare.
|
|
72
|
+
const unresolvable = () => contextDriftPayload(gate_verdict_cjs_1.gateUnreadable, 'phase-not-found', `Phase directory not found: ${phaseArg}`);
|
|
73
|
+
// The same policy as `verify schema-drift`: an ABSENT phases tree is `none` (the documented
|
|
74
|
+
// "nothing to compare" skip); one that exists but cannot be examined is `unreadable`.
|
|
75
|
+
const phasesRoot = (0, gate_evidence_cjs_1.statEvidence)(phasesDir);
|
|
76
|
+
if (phasesRoot.kind === 'unreadable') {
|
|
77
|
+
return contextDriftPayload(gate_verdict_cjs_1.gateUnreadable, 'phases-dir-unreadable', `Could not examine ${phasesDir} (${phasesRoot.reason})`);
|
|
78
|
+
}
|
|
79
|
+
if (phasesRoot.kind === 'none') {
|
|
80
|
+
return skip('no-phases-directory', 'No phases directory');
|
|
81
|
+
}
|
|
82
|
+
// Same phase-directory resolution rule the schema-drift gate uses (#1571, #2528):
|
|
83
|
+
// matchPhaseDirs, never a naive substring test.
|
|
84
|
+
const phaseDir = (0, gate_phase_context_cjs_1.resolvePhaseDirByToken)(phasesDir, phaseArg);
|
|
85
|
+
if (!phaseDir) {
|
|
86
|
+
return unresolvable();
|
|
87
|
+
}
|
|
88
|
+
const phaseEntries = (0, gate_evidence_cjs_1.readDirEvidence)(phaseDir);
|
|
89
|
+
if (phaseEntries.kind !== 'found') {
|
|
90
|
+
return unresolvable();
|
|
91
|
+
}
|
|
92
|
+
const phaseFiles = phaseEntries.value.slice().sort();
|
|
93
|
+
const contextFile = phaseFiles.find((f) => f.endsWith('-CONTEXT.md'));
|
|
94
|
+
if (!contextFile) {
|
|
95
|
+
return skip('no-context-md');
|
|
96
|
+
}
|
|
97
|
+
const researchFile = phaseFiles.find((f) => f.endsWith('-RESEARCH.md'));
|
|
98
|
+
const patternsFile = phaseFiles.find((f) => f.endsWith('-PATTERNS.md'));
|
|
99
|
+
const validationFile = phaseFiles.find((f) => f.endsWith('-VALIDATION.md'));
|
|
100
|
+
const specFile = phaseFiles.find((f) => f.endsWith('-SPEC.md') && !f.endsWith('-AI-SPEC.md') && !f.endsWith('-UI-SPEC.md'));
|
|
101
|
+
const upstreamFiles = [researchFile, patternsFile, validationFile, specFile].filter((f) => !!f);
|
|
102
|
+
if (upstreamFiles.length === 0) {
|
|
103
|
+
return skip('no-upstream-artifacts');
|
|
104
|
+
}
|
|
105
|
+
const allFiles = [contextFile, ...upstreamFiles];
|
|
106
|
+
const cleanCommitMs = defaultPhaseCleanCommitTimesMs(phaseDir, allFiles);
|
|
107
|
+
const effectiveTimeMs = (file) => cleanCommitMs.has(file)
|
|
108
|
+
? cleanCommitMs.get(file)
|
|
109
|
+
: node_fs_1.default.statSync(node_path_1.default.join(phaseDir, file)).mtimeMs;
|
|
110
|
+
const contextMs = effectiveTimeMs(contextFile);
|
|
111
|
+
const driftEntries = upstreamFiles.map((f) => ({ file: f, effectiveMs: effectiveTimeMs(f) }));
|
|
112
|
+
const staleArtifacts = computeContextDrift(contextMs, driftEntries);
|
|
113
|
+
// Through the quiet gate-config reader (workstream config first, then the project root's; a
|
|
114
|
+
// missing or malformed config is "key absent", nothing is printed).
|
|
115
|
+
const action = (0, gate_config_cjs_1.readWorkflowConfigValue)(cwd, 'workflow.context_drift_action').value === 'block' ? 'block' : 'warn';
|
|
116
|
+
const block = staleArtifacts.length > 0 && action === 'block';
|
|
117
|
+
const message = staleArtifacts.length > 0 ? buildContextDriftMessage(staleArtifacts, phaseArg) : '';
|
|
118
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(block ? 'block' : (staleArtifacts.length > 0 ? 'advisory' : 'pass'), block, {
|
|
119
|
+
block,
|
|
120
|
+
skipped: false,
|
|
121
|
+
stale_artifacts: staleArtifacts,
|
|
122
|
+
action,
|
|
123
|
+
message,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function evaluateContextDriftGate(input) {
|
|
127
|
+
const phaseArg = input.args[0] || '';
|
|
128
|
+
if (!phaseArg) {
|
|
129
|
+
// UNKNOWN: the pre-move `error('Usage…')` named no reason, and the reason is observable (#5219).
|
|
130
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.UNKNOWN, 'Usage: verify context-drift <phase>');
|
|
131
|
+
}
|
|
132
|
+
// Non-blocking contract: a throw anywhere (an invalid GSD_WORKSTREAM, an unreadable file)
|
|
133
|
+
// yields the non-blocking payload; the exit status says "could not look" (#5170) rather than
|
|
134
|
+
// a clean exit 0.
|
|
135
|
+
try {
|
|
136
|
+
return runContextDriftGate(input.projectDir, phaseArg);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
return contextDriftPayload(gate_verdict_cjs_1.gateUnreadable, 'exception: ' + (err instanceof Error ? err.message : String(err)));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check decision-coverage-plan` — blocking plan-phase decision-coverage gate
|
|
4
|
+
* (#2492, #1365 fail-loud, #2770 empty-arg fail-closed), as a gate module (#5139, epic #5056,
|
|
5
|
+
* ADR-5057 §4 first bullet): it returns a `GateResult`; the command router formats it.
|
|
6
|
+
* Imports no io module and performs no direct console/stdout/stderr write (ESLint-enforced).
|
|
7
|
+
*
|
|
8
|
+
* Invocation (the context path may be supplied EITHER way; #4130 follow-up), argv after the verb:
|
|
9
|
+
* <phase-dir> <context-path> (positional, the workflow caller's form)
|
|
10
|
+
* --context <path> [<phase-dir>]
|
|
11
|
+
*
|
|
12
|
+
* `--context <path>` follows the sibling flag convention (`check predicate`,
|
|
13
|
+
* #2008): `--flag value` pairs parsed by the shared partitionPredicateArgs
|
|
14
|
+
* pass, the flag WINNING over a same-purpose positional when both appear,
|
|
15
|
+
* and a valueless `--context` counting as no context at all (it falls
|
|
16
|
+
* through to the #2770 caller-error branch, not to the "CONTEXT.md missing"
|
|
17
|
+
* green skip). The positional form keeps working unchanged — no sibling
|
|
18
|
+
* check verb deprecates positionals and the plan-phase workflow passes them.
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.evaluateDecisionCoveragePlan = evaluateDecisionCoveragePlan;
|
|
22
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
23
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
24
|
+
const gate_args_cjs_1 = require("./gate-args.cjs");
|
|
25
|
+
const gate_phase_context_cjs_1 = require("./gate-phase-context.cjs");
|
|
26
|
+
const gate_config_cjs_1 = require("./gate-config.cjs");
|
|
27
|
+
const decision_coverage_support_cjs_1 = require("./decision-coverage-support.cjs");
|
|
28
|
+
/**
|
|
29
|
+
* The verdict when CONTEXT.md or a plan could not be read. The blocking policy is unchanged
|
|
30
|
+
* (`block: true`, fail-closed like the neighbouring arms); the outcome is `unreadable`, so the exit
|
|
31
|
+
* status is UNAVAILABLE and nothing is certified from content the gate never saw (#5170).
|
|
32
|
+
*/
|
|
33
|
+
function unreadableDecisionGate(readError) {
|
|
34
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(true, {
|
|
35
|
+
passed: false,
|
|
36
|
+
skipped: false,
|
|
37
|
+
reason: 'unreadable evidence',
|
|
38
|
+
total: null,
|
|
39
|
+
covered: null,
|
|
40
|
+
readError,
|
|
41
|
+
message: `Decision coverage gate could not read its evidence (${readError}). Fix the file permissions or encoding, then re-run the gate.`,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/** The verdict when there is no CONTEXT.md: the legitimate green skip (authoritatively `none`, never `unreadable`). */
|
|
45
|
+
function contextMissingSkip() {
|
|
46
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { passed: true, skipped: true, reason: 'CONTEXT.md missing', total: 0, covered: 0, uncovered: [], message: 'No CONTEXT.md - nothing to check.' });
|
|
47
|
+
}
|
|
48
|
+
function evaluateDecisionCoveragePlan(input) {
|
|
49
|
+
const { projectDir, args } = input;
|
|
50
|
+
// Partition the argv AFTER the verb so flag tokens and their values never land in a
|
|
51
|
+
// positional slot.
|
|
52
|
+
const { flags, positionals } = (0, gate_args_cjs_1.partitionPredicateArgs)(args);
|
|
53
|
+
let phaseDir = '';
|
|
54
|
+
if (positionals[0]) {
|
|
55
|
+
const resolved = (0, gate_phase_context_cjs_1.resolveContainedPath)(positionals[0], projectDir);
|
|
56
|
+
if ((0, gate_verdict_cjs_1.isGateUsageFailure)(resolved))
|
|
57
|
+
return resolved;
|
|
58
|
+
phaseDir = resolved;
|
|
59
|
+
}
|
|
60
|
+
// A VALUELESS `--context` stays a bare token in the positionals (sibling
|
|
61
|
+
// parser semantics); it must not then be read as the context PATH — a
|
|
62
|
+
// `--`-prefixed "path" is a caller mistake, and #2770's law says a missing
|
|
63
|
+
// context argument fails CLOSED, never a silent "CONTEXT.md missing" green
|
|
64
|
+
// skip. So only a non-flag positional may serve as the context.
|
|
65
|
+
const positionalContext = positionals[1] && !positionals[1].startsWith('--') ? positionals[1] : '';
|
|
66
|
+
const contextArg = flags['context'] ?? positionalContext ?? '';
|
|
67
|
+
let contextPath = '';
|
|
68
|
+
if (contextArg) {
|
|
69
|
+
const resolved = (0, gate_phase_context_cjs_1.resolveContainedPath)(contextArg, projectDir);
|
|
70
|
+
if ((0, gate_verdict_cjs_1.isGateUsageFailure)(resolved))
|
|
71
|
+
return resolved;
|
|
72
|
+
contextPath = resolved;
|
|
73
|
+
}
|
|
74
|
+
if (!(0, gate_config_cjs_1.isDecisionCoverageGateEnabled)(projectDir)) {
|
|
75
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { passed: true, skipped: true, reason: 'workflow.context_coverage_gate is false', total: 0, covered: 0, uncovered: [], message: 'Decision coverage gate disabled by config.' });
|
|
76
|
+
}
|
|
77
|
+
// #2770: an EMPTY/MISSING contextPath argument is a CALLER ERROR (the workflow
|
|
78
|
+
// forgot to pass the path — e.g. a shell variable lost between Bash blocks), not
|
|
79
|
+
// evidence the phase has no CONTEXT.md. Fail closed (mirrors #1365 fail-loud) so a
|
|
80
|
+
// blocking gate cannot silently certify success on a caller mistake.
|
|
81
|
+
if (!contextArg || contextArg === '') {
|
|
82
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, { passed: false, skipped: false, reason: 'missing context path argument', total: 0, covered: 0, uncovered: [], message: 'Decision coverage gate called without a context path argument — the caller (e.g. the plan-phase workflow) must pass the CONTEXT.md path. An empty argument is a caller error, not evidence there is nothing to check (#2770).' });
|
|
83
|
+
}
|
|
84
|
+
// One stat answers both questions (#5170): `none` (ENOENT/ENOTDIR) is a REAL path whose file
|
|
85
|
+
// genuinely does not exist — the LEGITIMATE green skip. `fs.existsSync` answered `false` for an
|
|
86
|
+
// EACCES on a parent too, certifying "nothing to check" over a CONTEXT.md the gate never saw.
|
|
87
|
+
const contextStat = (0, gate_evidence_cjs_1.statEvidence)(contextPath);
|
|
88
|
+
if (contextStat.kind === 'none') {
|
|
89
|
+
return contextMissingSkip();
|
|
90
|
+
}
|
|
91
|
+
// #4794: a NON-FILE path (a directory — the adjacent same-looking positional
|
|
92
|
+
// swapped, the issue's repro 2) is a caller error like #2770's empty argument:
|
|
93
|
+
// the read yields nothing, and the gate used to certify passed:true on a phase
|
|
94
|
+
// full of decisions. Fail closed, naming it. A stat failure other than "absent"
|
|
95
|
+
// answers the SAME fail-closed JSON (outcome `unreadable`), never a throw.
|
|
96
|
+
const contextIsFile = contextStat.kind === 'found' && contextStat.value.isFile();
|
|
97
|
+
if (!contextIsFile) {
|
|
98
|
+
const contextKind = contextStat.kind === 'found'
|
|
99
|
+
? (contextStat.value.isDirectory() ? 'directory' : 'non-file entry')
|
|
100
|
+
: 'unreadable path';
|
|
101
|
+
const notAFile = { passed: false, skipped: false, reason: 'context path is not a file', total: null, covered: null, message: `Decision coverage gate: the context path "${contextArg}" is not a readable file (${contextKind}). Swap the adjacent positionals or pass --context <path-to-CONTEXT.md>.` };
|
|
102
|
+
// Fail-closed policy is unchanged (`block: true`); a path that could not even be examined is
|
|
103
|
+
// "could not look" (#5170), so its outcome is `unreadable` and the exit status follows it.
|
|
104
|
+
return contextStat.kind === 'unreadable' ? (0, gate_verdict_cjs_1.gateUnreadable)(true, notAFile) : (0, gate_verdict_cjs_1.gateVerdict)('block', true, notAFile);
|
|
105
|
+
}
|
|
106
|
+
const extracted = (0, decision_coverage_support_cjs_1.loadDecisionExtraction)(contextPath);
|
|
107
|
+
if (extracted.kind === 'unreadable')
|
|
108
|
+
return unreadableDecisionGate(`${extracted.span ?? contextPath}: ${extracted.reason}`);
|
|
109
|
+
if (extracted.kind === 'none') {
|
|
110
|
+
return contextMissingSkip();
|
|
111
|
+
}
|
|
112
|
+
const { trackable: decisions, outcome, unreadableIds } = extracted.value;
|
|
113
|
+
// #1365 fail-loud gate: any could-not-parse outcome must NOT silently pass —
|
|
114
|
+
// even when some decisions were extracted (e.g. D-01 valid but D-02 malformed).
|
|
115
|
+
// A parse-miss on ANY bullet means the gate cannot certify full coverage.
|
|
116
|
+
// Fire independent of decisions.length so a partial-parse still blocks.
|
|
117
|
+
if (outcome === 'could-not-parse') {
|
|
118
|
+
// #4794: nothing was measured — the answer must not carry the fields of a
|
|
119
|
+
// gate that did. total/covered are null (a type change is the point:
|
|
120
|
+
// 0 reads as data, null does not), `uncovered` is OMITTED (the list was
|
|
121
|
+
// never built), and the ids that failed to parse are carried so a caller
|
|
122
|
+
// capturing stdout knows which decision to fix.
|
|
123
|
+
const partialParse = decisions.length > 0;
|
|
124
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
125
|
+
passed: false,
|
|
126
|
+
skipped: false,
|
|
127
|
+
reason: 'could-not-parse',
|
|
128
|
+
total: null,
|
|
129
|
+
covered: null,
|
|
130
|
+
unreadable: unreadableIds,
|
|
131
|
+
message: (partialParse
|
|
132
|
+
? 'Decision coverage gate: decisions could not be fully parsed — one or more ' +
|
|
133
|
+
'`- **D-NN ...**` bullets appear malformed (missing `:` or ` — ` separator, or a phase ' +
|
|
134
|
+
'prefix that is not a digit run, e.g. `D4x-01`). Fix the bullet format so all decisions ' +
|
|
135
|
+
'can be read before re-running the gate.'
|
|
136
|
+
: 'Decision coverage gate: could not parse decisions — possible format mismatch. ' +
|
|
137
|
+
'The CONTEXT.md appears to be decision-shaped (has a <decisions> block, a decisions heading, ' +
|
|
138
|
+
'or D- tokens) but no decision bullets could be extracted. Check the formatting of the decisions ' +
|
|
139
|
+
'block and ensure bullets follow the `- **D-NN:** text`, `- **D4-NN:** text` (phase-prefixed), ' +
|
|
140
|
+
'or `- **D-NN — title** body` form. An ID grammar the parser does not support (e.g. `DEC-01`) ' +
|
|
141
|
+
'also lands here.')
|
|
142
|
+
+ (unreadableIds.length > 0 ? ' Unreadable ids: ' + unreadableIds.join(', ') + '.' : ''),
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
if (decisions.length === 0) {
|
|
146
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('skip', false, { passed: true, skipped: true, reason: 'no trackable decisions', total: 0, covered: 0, uncovered: [], message: 'No trackable decisions in CONTEXT.md.' });
|
|
147
|
+
}
|
|
148
|
+
const planContents = (0, decision_coverage_support_cjs_1.loadPlanContents)(phaseDir);
|
|
149
|
+
if (planContents.kind === 'unreadable')
|
|
150
|
+
return unreadableDecisionGate(`${planContents.span ?? phaseDir}: ${planContents.reason}`);
|
|
151
|
+
const sections = planContents.value.map(decision_coverage_support_cjs_1.extractPlanDesignatedSections);
|
|
152
|
+
const uncovered = [];
|
|
153
|
+
let covered = 0;
|
|
154
|
+
for (const decision of decisions) {
|
|
155
|
+
if (sections.some((section) => (0, decision_coverage_support_cjs_1.decisionMentioned)(section, decision)))
|
|
156
|
+
covered++;
|
|
157
|
+
else
|
|
158
|
+
uncovered.push({ id: decision.id, text: decision.text, category: decision.category });
|
|
159
|
+
}
|
|
160
|
+
const passed = uncovered.length === 0;
|
|
161
|
+
return (0, gate_verdict_cjs_1.gateVerdict)(passed ? 'pass' : 'block', !passed, {
|
|
162
|
+
passed,
|
|
163
|
+
skipped: false,
|
|
164
|
+
total: decisions.length,
|
|
165
|
+
covered,
|
|
166
|
+
uncovered,
|
|
167
|
+
message: (0, decision_coverage_support_cjs_1.buildPlanMessage)(uncovered),
|
|
168
|
+
});
|
|
169
|
+
}
|