@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
|
@@ -19,21 +19,18 @@
|
|
|
19
19
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
20
20
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
21
21
|
};
|
|
22
|
-
const node_fs_1 = __importDefault(require("node:fs"));
|
|
23
22
|
const node_path_1 = __importDefault(require("node:path"));
|
|
24
23
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
24
|
const io = require("./io.cjs");
|
|
26
25
|
const { output, error, formatDiagnosticToken } = io;
|
|
27
26
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
27
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
28
28
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
29
29
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
30
30
|
const { planningPaths, planningDir, findContextMdIn } = planningWorkspace;
|
|
31
31
|
const decisions_cjs_1 = require("./decisions.cjs");
|
|
32
32
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
-
const planScanMod = require("./plan-scan.cjs");
|
|
35
|
-
const { scanPhasePlans } = planScanMod;
|
|
36
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
34
|
const phaseIdMod = require("./phase-id.cjs");
|
|
38
35
|
const { scopeToPhase } = phaseIdMod;
|
|
39
36
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
@@ -143,21 +140,42 @@ function formatGapTable(rows) {
|
|
|
143
140
|
}).join('\n');
|
|
144
141
|
return `## Post-Planning Gap Analysis\n\n${header}\n${body}\n`;
|
|
145
142
|
}
|
|
143
|
+
function parseJsonEvidence(text, span) {
|
|
144
|
+
try {
|
|
145
|
+
return (0, gate_evidence_cjs_1.evidenceFound)(JSON.parse(text));
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
return (0, gate_evidence_cjs_1.evidenceFromError)(err, span);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Is the gap analysis enabled? An ABSENT config means the default (enabled). A config that exists
|
|
153
|
+
* but cannot be read or parsed also runs the analysis (the conservative direction: the check is not
|
|
154
|
+
* silently switched off), and is reported in `unreadable` so the verdict is never a clean pass
|
|
155
|
+
* (#5170, ADR-5057 §4).
|
|
156
|
+
*/
|
|
146
157
|
function readGate(cwd) {
|
|
147
158
|
const cfgPath = node_path_1.default.join(planningDir(cwd), 'config.json');
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
const text = (0, gate_evidence_cjs_1.readTextEvidence)(cfgPath);
|
|
160
|
+
if (text.kind === 'none')
|
|
161
|
+
return { enabled: true, unreadable: [] };
|
|
162
|
+
if (text.kind === 'unreadable')
|
|
163
|
+
return { enabled: true, unreadable: [{ reason: text.reason, span: cfgPath }] };
|
|
164
|
+
const parsed = parseJsonEvidence(text.value, cfgPath);
|
|
165
|
+
if (parsed.kind === 'unreadable')
|
|
166
|
+
return { enabled: true, unreadable: [{ reason: parsed.reason, span: cfgPath }] };
|
|
167
|
+
if (parsed.kind === 'found') {
|
|
168
|
+
const raw = parsed.value;
|
|
150
169
|
if (raw && typeof raw === 'object' && 'workflow' in raw) {
|
|
151
170
|
const wf = raw['workflow'];
|
|
152
171
|
if (wf && typeof wf === 'object' && 'post_planning_gaps' in wf) {
|
|
153
172
|
const val = wf['post_planning_gaps'];
|
|
154
173
|
if (typeof val === 'boolean')
|
|
155
|
-
return val;
|
|
174
|
+
return { enabled: val, unreadable: [] };
|
|
156
175
|
}
|
|
157
176
|
}
|
|
158
177
|
}
|
|
159
|
-
|
|
160
|
-
return true;
|
|
178
|
+
return { enabled: true, unreadable: [] };
|
|
161
179
|
}
|
|
162
180
|
/**
|
|
163
181
|
* Same-prefix ascending numeric range, e.g. `SEL-01..SEL-03`. Both sides must
|
|
@@ -298,7 +316,10 @@ function normalizePhaseReqIds(rawVal) {
|
|
|
298
316
|
}
|
|
299
317
|
function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
300
318
|
const phaseReqIds = normalizePhaseReqIds(options.phaseReqIds);
|
|
301
|
-
|
|
319
|
+
const gate = readGate(cwd);
|
|
320
|
+
const unreadable = [...gate.unreadable];
|
|
321
|
+
const unreadableField = () => (unreadable.length > 0 ? { unreadable } : {});
|
|
322
|
+
if (!gate.enabled) {
|
|
302
323
|
return {
|
|
303
324
|
enabled: false,
|
|
304
325
|
rows: [],
|
|
@@ -311,7 +332,12 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
311
332
|
}
|
|
312
333
|
const absPhaseDir = node_path_1.default.isAbsolute(phaseDir) ? phaseDir : node_path_1.default.join(cwd, phaseDir);
|
|
313
334
|
const reqPath = planningPaths(cwd).requirements;
|
|
314
|
-
|
|
335
|
+
// An absent REQUIREMENTS.md has no requirements; one that exists but cannot be read is `unreadable`
|
|
336
|
+
// (a thrown read used to crash, and `existsSync` said "absent" for an EACCES on a parent).
|
|
337
|
+
const reqRead = (0, gate_evidence_cjs_1.readTextEvidence)(reqPath);
|
|
338
|
+
if (reqRead.kind === 'unreadable')
|
|
339
|
+
unreadable.push({ reason: reqRead.reason, span: reqPath });
|
|
340
|
+
const reqMd = reqRead.kind === 'found' ? reqRead.value : '';
|
|
315
341
|
let reqItems = parseRequirements(reqMd).map(r => ({ ...r, source: 'REQUIREMENTS.md' }));
|
|
316
342
|
// Scope the requirements comparison to the phase's mapped REQ-IDs (#447).
|
|
317
343
|
// A phase that maps no requirements (phase_req_ids null/TBD) must not report
|
|
@@ -325,7 +351,9 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
325
351
|
const wanted = new Set(phaseReqIds);
|
|
326
352
|
const foundIds = new Set(reqItems.map(r => r.id));
|
|
327
353
|
reqItems = reqItems.filter(r => wanted.has(r.id));
|
|
328
|
-
|
|
354
|
+
// An unreadable REQUIREMENTS.md registers nothing, so "missing from REQUIREMENTS.md" would be a
|
|
355
|
+
// false claim about every ID; the unreadable read is reported instead.
|
|
356
|
+
ghostReqIds = reqRead.kind === 'unreadable' ? [] : phaseReqIds.filter(id => !foundIds.has(id));
|
|
329
357
|
}
|
|
330
358
|
// Read the phase directory once; reuse the listing for both context detection
|
|
331
359
|
// and plan-file enumeration (avoids redundant readdirSync calls).
|
|
@@ -343,6 +371,8 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
343
371
|
const phaseDirReadError = phaseDirScope === SCOPE.UNREADABLE
|
|
344
372
|
? `Could not read phase directory ${formatDiagnosticToken(absPhaseDir)}`
|
|
345
373
|
: null;
|
|
374
|
+
if (phaseDirScope === SCOPE.UNREADABLE)
|
|
375
|
+
unreadable.push({ reason: 'the directory could not be listed', span: absPhaseDir });
|
|
346
376
|
// #3511-class: scope the raw listing to this phase dir before the
|
|
347
377
|
// phase-numbered -CONTEXT.md predicate. `phaseDirFiles` itself stays raw —
|
|
348
378
|
// it is also reused below only as a `.length > 0` guard ahead of
|
|
@@ -351,31 +381,40 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
351
381
|
const scopedPhaseDirFiles = scopeToPhase(phaseDirFiles, node_path_1.default.basename(absPhaseDir));
|
|
352
382
|
const ctxFile = findContextMdIn(scopedPhaseDirFiles);
|
|
353
383
|
const ctxPath = ctxFile ? node_path_1.default.join(absPhaseDir, ctxFile) : null;
|
|
354
|
-
const
|
|
384
|
+
const ctxRead = ctxPath ? (0, gate_evidence_cjs_1.readTextEvidence)(ctxPath) : null;
|
|
385
|
+
if (ctxPath && ctxRead && ctxRead.kind === 'unreadable')
|
|
386
|
+
unreadable.push({ reason: ctxRead.reason, span: ctxPath });
|
|
387
|
+
const ctxMd = ctxRead && ctxRead.kind === 'found' ? ctxRead.value : '';
|
|
355
388
|
// Use extractDecisions so gap-checker can distinguish could-not-parse from none-present.
|
|
356
389
|
const ctxExtraction = (0, decisions_cjs_1.extractDecisions)(ctxMd);
|
|
357
390
|
const dItems = ctxExtraction.decisions.map(d => ({ ...d, source: 'CONTEXT.md' }));
|
|
358
391
|
const items = [...reqItems, ...dItems];
|
|
359
392
|
let planText = '';
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
393
|
+
if (phaseDirFiles.length > 0) {
|
|
394
|
+
// #3183 (lint-plan-count-drift): source the live plan-file list from
|
|
395
|
+
// the single owner (scanPhasePlans) instead of a local `-PLAN\.md$`
|
|
396
|
+
// filter on the already-read listing — picks up bare PLAN.md, nested
|
|
397
|
+
// plans/, and excludes superseded plans, none of which the prior
|
|
398
|
+
// root-only exact-suffix filter did.
|
|
399
|
+
// #5170: a scan that did not see every plan (an existing nested plans/ that could not be read,
|
|
400
|
+
// SCOPE.TRUNCATED) is `unreadable`, never a short plan set that reports the missing plans' IDs
|
|
401
|
+
// as "Not covered".
|
|
402
|
+
const planSet = (0, gate_evidence_cjs_1.readPlanSetEvidence)(absPhaseDir);
|
|
403
|
+
if (planSet.kind === 'unreadable') {
|
|
404
|
+
unreadable.push({ reason: planSet.reason, span: planSet.span ?? absPhaseDir });
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
planText = planSet.value.map(f => {
|
|
408
|
+
const planPath = node_path_1.default.join(absPhaseDir, f);
|
|
409
|
+
const plan = (0, gate_evidence_cjs_1.readTextEvidence)(planPath);
|
|
410
|
+
// A plan that exists but cannot be read is `unreadable`, never an empty slice of the text the
|
|
411
|
+
// coverage is detected in (it would report every ID it holds as "Not covered").
|
|
412
|
+
if (plan.kind === 'unreadable')
|
|
413
|
+
unreadable.push({ reason: plan.reason, span: planPath });
|
|
414
|
+
return plan.kind === 'found' ? plan.value : '';
|
|
375
415
|
}).join('\n');
|
|
376
416
|
}
|
|
377
417
|
}
|
|
378
|
-
catch { /* unreadable */ }
|
|
379
418
|
// FIX D (#1365): surface decision could-not-parse independently of whether
|
|
380
419
|
// requirements items exist. Without this, a could-not-parse on decisions is
|
|
381
420
|
// silently masked whenever REQUIREMENTS.md has ≥1 item — the mismatch must
|
|
@@ -411,6 +450,7 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
411
450
|
counts: { total: rows.length, covered, uncovered },
|
|
412
451
|
phase_dir_read_error: phaseDirReadError,
|
|
413
452
|
phase_dir_scope: phaseDirScope,
|
|
453
|
+
...unreadableField(),
|
|
414
454
|
};
|
|
415
455
|
}
|
|
416
456
|
return {
|
|
@@ -421,6 +461,7 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
421
461
|
counts: { total: 0, covered: 0, uncovered: 0 },
|
|
422
462
|
phase_dir_read_error: phaseDirReadError,
|
|
423
463
|
phase_dir_scope: phaseDirScope,
|
|
464
|
+
...unreadableField(),
|
|
424
465
|
};
|
|
425
466
|
}
|
|
426
467
|
// #1365: if no items at all, surface a clean no-check message.
|
|
@@ -439,6 +480,7 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
439
480
|
counts: { total: 0, covered: 0, uncovered: 0 },
|
|
440
481
|
phase_dir_read_error: phaseDirReadError,
|
|
441
482
|
phase_dir_scope: phaseDirScope,
|
|
483
|
+
...unreadableField(),
|
|
442
484
|
};
|
|
443
485
|
}
|
|
444
486
|
const rows = sortRows([
|
|
@@ -458,6 +500,7 @@ function runGapAnalysis(cwd, phaseDir, options = {}) {
|
|
|
458
500
|
counts: { total: rows.length, covered, uncovered },
|
|
459
501
|
phase_dir_read_error: phaseDirReadError,
|
|
460
502
|
phase_dir_scope: phaseDirScope,
|
|
503
|
+
...unreadableField(),
|
|
461
504
|
};
|
|
462
505
|
}
|
|
463
506
|
function cmdGapAnalysis(cwd, args, raw) {
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `check api-coverage.verify-pre` as a gate module (#5139, epic #5056, ADR-5057 §4 first bullet):
|
|
4
|
+
* it returns a `GateResult`; the command router formats it. Imports no io module and performs no
|
|
5
|
+
* direct console/stdout/stderr write (ESLint-enforced).
|
|
6
|
+
*
|
|
7
|
+
* BLOCKING seal-time gate for the ai-integration capability (#1562). Enforces "Full API Coverage by
|
|
8
|
+
* Default — Opt Out, Never Opt In." A phase that integrates an external API/SDK/service may not seal
|
|
9
|
+
* until a COVERAGE.md matrix enumerates the surface and every non-integrated capability is an
|
|
10
|
+
* explicit, reasoned opt-out.
|
|
11
|
+
*
|
|
12
|
+
* Contract (two touch points composed into one check):
|
|
13
|
+
* 1. If COVERAGE.md exists in the phase dir → validate it (acceptance #2).
|
|
14
|
+
* Block on any validation error (empty matrix, OPT-OUT without reason,
|
|
15
|
+
* duplicate/empty capability).
|
|
16
|
+
* 2. If COVERAGE.md is absent → run detectApiIntegration over the phase scope
|
|
17
|
+
* (PLAN.md body, then ROADMAP phase section as fallback). If a strong
|
|
18
|
+
* external-API-integration signal is detected → BLOCK ("integration
|
|
19
|
+
* detected without coverage matrix"). If no signal → PASS (treat as a
|
|
20
|
+
* non-API phase; acceptance #4 — low false positives).
|
|
21
|
+
*
|
|
22
|
+
* The detector is the FALLBACK for the "nobody decided / forgot the matrix" case; the primary path
|
|
23
|
+
* is the plan:pre contribution prompting COVERAGE.md.
|
|
24
|
+
*
|
|
25
|
+
* Argv after the verb: `<phase-dir-or-token>`. Payload: the uniform gate contract
|
|
26
|
+
* `{ block, passed, message, ...details }`.
|
|
27
|
+
*/
|
|
28
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
29
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
|
+
};
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.readPhaseScope = readPhaseScope;
|
|
33
|
+
exports.evaluateApiCoverageVerifyPre = evaluateApiCoverageVerifyPre;
|
|
34
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
35
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
36
|
+
const gate_evidence_cjs_1 = require("./gate-evidence.cjs");
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
38
|
+
const planningWorkspaceMod = require("./planning-workspace.cjs");
|
|
39
|
+
const { planningDir } = planningWorkspaceMod;
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
41
|
+
const phaseLocatorMod = require("./phase-locator.cjs");
|
|
42
|
+
const { findPhaseInternal } = phaseLocatorMod;
|
|
43
|
+
const security_cjs_1 = require("./security.cjs");
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
45
|
+
const roadmapModule = require("./roadmap.cjs");
|
|
46
|
+
const { getRoadmapPhaseWithFallback } = roadmapModule;
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
48
|
+
const apiCoverageMod = require("./api-coverage.cjs");
|
|
49
|
+
const { detectApiIntegration, validateCoverageMatrix } = apiCoverageMod;
|
|
50
|
+
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
52
|
+
const planScanMod = require("./plan-scan.cjs");
|
|
53
|
+
const { scanPhasePlans } = planScanMod;
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
55
|
+
const planningScopeMod = require("./planning-scope.cjs");
|
|
56
|
+
const { SCOPE } = planningScopeMod;
|
|
57
|
+
// NOTE: `fs` is used as a namespace object at call time (never destructured at load) so tests can
|
|
58
|
+
// monkeypatch its methods for failure injection.
|
|
59
|
+
function readPhaseScope(projectDir, phaseDir, phaseNumber) {
|
|
60
|
+
const chunks = [];
|
|
61
|
+
let readError = null;
|
|
62
|
+
// A MISSING phase directory is fine (no plans yet → fall through to the
|
|
63
|
+
// roadmap). Checked up front (rather than via a readdirSync catch) because
|
|
64
|
+
// #3183 (lint-plan-count-drift) now sources the plan-file list from the
|
|
65
|
+
// single owner (scanPhasePlans) instead of a local `-PLAN\.md$` readdirSync
|
|
66
|
+
// filter — picks up bare PLAN.md and nested plans/, and excludes
|
|
67
|
+
// superseded plans, none of which the prior root-only exact-suffix filter
|
|
68
|
+
// did.
|
|
69
|
+
// `fs.existsSync` answered `false` for an EACCES on a parent, skipping plans the gate never saw and
|
|
70
|
+
// letting detection run over a roadmap fallback; the stat keeps that case an unreadable scope (#5170).
|
|
71
|
+
const phaseDirStat = (0, gate_evidence_cjs_1.statEvidence)(phaseDir);
|
|
72
|
+
if (phaseDirStat.kind === 'unreadable') {
|
|
73
|
+
return {
|
|
74
|
+
text: '',
|
|
75
|
+
readError: `could not read the phase directory: ${phaseDirStat.reason}`,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (phaseDirStat.kind === 'found') {
|
|
79
|
+
const scan = scanPhasePlans(phaseDir);
|
|
80
|
+
if (scan.scope !== SCOPE.COMPLETE) {
|
|
81
|
+
// Directory exists but scanPhasePlans could not see all of it: its own readdirSync(phaseDir)
|
|
82
|
+
// failed (UNREADABLE) or the nested plans/ directory exists and could not be read
|
|
83
|
+
// (TRUNCATED). Only COMPLETE is a real answer; a short plan set is never evidence of "no
|
|
84
|
+
// plans" (#2365 review, #5170), so the gate must not pass over it.
|
|
85
|
+
return {
|
|
86
|
+
text: '',
|
|
87
|
+
readError: `could not read the phase directory: scanPhasePlans reported scope ${scan.scope.toUpperCase()}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const plans = [...scan.planFiles].sort();
|
|
91
|
+
for (const p of plans) {
|
|
92
|
+
const plan = (0, gate_evidence_cjs_1.readTextEvidence)(node_path_1.default.join(phaseDir, p));
|
|
93
|
+
if (plan.kind === 'found') {
|
|
94
|
+
chunks.push(plan.value);
|
|
95
|
+
}
|
|
96
|
+
else if (!readError) {
|
|
97
|
+
// A plan file the scan listed that cannot be read (or vanished since) — record it and keep
|
|
98
|
+
// reading the rest so the message names the first failure.
|
|
99
|
+
readError = `could not read ${p}: ${plan.kind === 'unreadable' ? plan.reason : 'it disappeared since the scan'}`;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (readError)
|
|
104
|
+
return { text: chunks.join('\n\n'), readError };
|
|
105
|
+
if (chunks.join('').trim().length > 0)
|
|
106
|
+
return { text: chunks.join('\n\n'), readError: null };
|
|
107
|
+
// Fallback: ONLY this phase's ROADMAP section (not the whole file, which
|
|
108
|
+
// would pollute detection with sibling-phase prose). A MISSING roadmap/section
|
|
109
|
+
// is non-fatal; a roadmap that exists but cannot be read is a real failure.
|
|
110
|
+
if (phaseNumber) {
|
|
111
|
+
try {
|
|
112
|
+
const section = getRoadmapPhaseWithFallback(projectDir, phaseNumber);
|
|
113
|
+
if (section)
|
|
114
|
+
return { text: section, readError: null };
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
// An absent roadmap/section is "not there yet"; any other failure is a real read failure.
|
|
118
|
+
if ((0, gate_evidence_cjs_1.evidenceFromError)(err, 'ROADMAP.md').kind === 'unreadable') {
|
|
119
|
+
return {
|
|
120
|
+
text: '',
|
|
121
|
+
readError: `could not read the roadmap fallback: ${err instanceof Error ? err.message : String(err)}`,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { text: '', readError: null };
|
|
127
|
+
}
|
|
128
|
+
function evaluateApiCoverageVerifyPre(input) {
|
|
129
|
+
const { projectDir, args } = input;
|
|
130
|
+
const phaseArg = typeof args[0] === 'string' ? args[0] : '';
|
|
131
|
+
if (!phaseArg) {
|
|
132
|
+
return (0, gate_verdict_cjs_1.gateUsageFailure)(gate_verdict_cjs_1.GATE_FAILURE_CODE.SDK_MISSING_ARG, 'api-coverage.verify-pre requires a phase argument: check api-coverage.verify-pre <phase-dir-or-token>');
|
|
133
|
+
}
|
|
134
|
+
const pDir = planningDir(projectDir);
|
|
135
|
+
const phasesRoot = node_path_1.default.join(pDir, 'phases');
|
|
136
|
+
// SECURITY (path traversal): the phase argument is taken ONLY as a phase
|
|
137
|
+
// token — its basename — and resolved by findPhaseInternal strictly under
|
|
138
|
+
// .planning/phases/ (or a milestone archive). The raw arg is never used as a
|
|
139
|
+
// path, so `..`, absolute paths, and arbitrary directories cannot reach a
|
|
140
|
+
// file read. Mirrors the schema-drift gate's (gate-schema-drift.cts) token-match approach.
|
|
141
|
+
let token = (0, shell_command_projection_cjs_1.posixNormalize)(phaseArg).split('/').filter(Boolean).pop() || '';
|
|
142
|
+
// A token like ".." or "." carries no phase identity → unresolvable.
|
|
143
|
+
if (token === '.' || token === '..')
|
|
144
|
+
token = '';
|
|
145
|
+
// Not a GSD project (no phases tree at all) → fail-open: nothing to gate. Only an ABSENT tree
|
|
146
|
+
// (`none`) is that answer; one that exists but cannot be examined (an EACCES on a parent) is
|
|
147
|
+
// `unreadable` — `fs.existsSync` said `false` for it and certified "not a GSD project" (#5170).
|
|
148
|
+
const phasesRootStat = (0, gate_evidence_cjs_1.statEvidence)(phasesRoot);
|
|
149
|
+
if (phasesRootStat.kind === 'unreadable') {
|
|
150
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(true, {
|
|
151
|
+
block: true,
|
|
152
|
+
passed: false,
|
|
153
|
+
coverage_present: false,
|
|
154
|
+
detected: false,
|
|
155
|
+
read_error: phasesRootStat.reason,
|
|
156
|
+
message: `api-coverage: could not examine .planning/phases (${phasesRootStat.reason}) — ` +
|
|
157
|
+
'refusing to treat an unreadable phases tree as "not a GSD project". ' +
|
|
158
|
+
'Fix the directory permissions before sealing.',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
if (phasesRootStat.kind === 'none') {
|
|
162
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('pass', false, {
|
|
163
|
+
block: false,
|
|
164
|
+
passed: true,
|
|
165
|
+
coverage_present: false,
|
|
166
|
+
detected: false,
|
|
167
|
+
message: 'api-coverage: no .planning/phases directory; gate skipped (not a GSD project layout)',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
// Resolve the phase dir under the contained phases root.
|
|
171
|
+
let resolvedDir = null;
|
|
172
|
+
let phaseNumber = '';
|
|
173
|
+
if (token) {
|
|
174
|
+
const found = findPhaseInternal(projectDir, token);
|
|
175
|
+
if (found && found.directory) {
|
|
176
|
+
// findPhaseInternal's `directory` is relative to the project root; anchor it to `projectDir`
|
|
177
|
+
// (not the process cwd) so the gate reads the same files in-process as it does from the CLI.
|
|
178
|
+
resolvedDir = node_path_1.default.resolve(projectDir, found.directory);
|
|
179
|
+
phaseNumber = found.phase_number || '';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (!resolvedDir) {
|
|
183
|
+
// The phases tree EXISTS but THIS phase could not be resolved. For a
|
|
184
|
+
// BLOCKING gate, fail-closed: a missing phase dir must not silently bypass
|
|
185
|
+
// the coverage requirement. (Distinguished from "no .planning at all"
|
|
186
|
+
// above, which is a genuine non-GSD-project → pass.)
|
|
187
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
188
|
+
block: true,
|
|
189
|
+
passed: false,
|
|
190
|
+
coverage_present: false,
|
|
191
|
+
detected: false,
|
|
192
|
+
phase_lookup_failed: true,
|
|
193
|
+
message: `api-coverage: could not resolve phase "${phaseArg}" under .planning/phases/. ` +
|
|
194
|
+
'Resolve the phase directory (or produce COVERAGE.md) before sealing.',
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
// Defense-in-depth: the resolved dir must be inside the phases root (or a
|
|
198
|
+
// milestone archive under .planning/milestones).
|
|
199
|
+
const milestonesRoot = node_path_1.default.join(pDir, 'milestones');
|
|
200
|
+
// Lexical containment (ADR-4650): resolvedDir is a directory path, not read
|
|
201
|
+
// through here — mirrors the prior path.resolve(root, candidate)-based check
|
|
202
|
+
// without introducing a filesystem/realpath dependency this defense-in-depth
|
|
203
|
+
// recheck never had.
|
|
204
|
+
if ((0, security_cjs_1.tryWithinRootLexical)(resolvedDir, phasesRoot) === null &&
|
|
205
|
+
(0, security_cjs_1.tryWithinRootLexical)(resolvedDir, milestonesRoot) === null) {
|
|
206
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
207
|
+
block: true,
|
|
208
|
+
passed: false,
|
|
209
|
+
coverage_present: false,
|
|
210
|
+
detected: false,
|
|
211
|
+
message: 'api-coverage: resolved phase dir escapes .planning/ — refusing to evaluate',
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
// (1) locate COVERAGE.md — prefer the exact name, then a single *-COVERAGE.md.
|
|
215
|
+
let coverageFile = '';
|
|
216
|
+
let suffixed = [];
|
|
217
|
+
// #5170 (ADR-5057 §4): a phase directory that EXISTS but cannot be listed is `unreadable` —
|
|
218
|
+
// a COVERAGE.md may be in it — and never falls through to the detector, whose pass would be
|
|
219
|
+
// certified from a directory the gate never saw. An ABSENT directory (`none`) has no matrix and
|
|
220
|
+
// proceeds to detection, as before. The blocking policy is unchanged (fail-closed).
|
|
221
|
+
const listing = (0, gate_evidence_cjs_1.readDirEntriesEvidence)(resolvedDir);
|
|
222
|
+
if (listing.kind === 'unreadable') {
|
|
223
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(true, {
|
|
224
|
+
block: true,
|
|
225
|
+
passed: false,
|
|
226
|
+
coverage_present: false,
|
|
227
|
+
detected: false,
|
|
228
|
+
read_error: listing.reason,
|
|
229
|
+
message: `api-coverage: could not read the phase directory (${listing.reason}) — ` +
|
|
230
|
+
'refusing to certify the coverage matrix from a directory that could not be listed. ' +
|
|
231
|
+
'Fix the directory permissions before sealing.',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
if (listing.kind === 'found') {
|
|
235
|
+
const files = listing.value.filter((e) => e.isFile()).map((e) => e.name);
|
|
236
|
+
const exact = files.find((f) => /^COVERAGE\.md$/i.test(f));
|
|
237
|
+
if (exact) {
|
|
238
|
+
coverageFile = exact;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
suffixed = files.filter((f) => /-COVERAGE\.md$/i.test(f)).sort();
|
|
242
|
+
if (suffixed.length === 1)
|
|
243
|
+
coverageFile = suffixed[0];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (coverageFile) {
|
|
247
|
+
const matrix = (0, gate_evidence_cjs_1.readTextEvidence)(node_path_1.default.join(resolvedDir, coverageFile));
|
|
248
|
+
if (matrix.kind !== 'found') {
|
|
249
|
+
// COVERAGE.md exists but is unreadable (EACCES/EIO/encoding), or vanished between the
|
|
250
|
+
// listing and the read. Fail-closed (policy unchanged) and `unreadable` (outcome).
|
|
251
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(true, {
|
|
252
|
+
block: true,
|
|
253
|
+
passed: false,
|
|
254
|
+
coverage_present: true,
|
|
255
|
+
message: `api-coverage: COVERAGE.md exists but is unreadable — fix file permissions/encoding before sealing`,
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const matrixText = matrix.value;
|
|
259
|
+
const v = validateCoverageMatrix(matrixText);
|
|
260
|
+
if (v.valid) {
|
|
261
|
+
if (v.none_declared) {
|
|
262
|
+
// The declaration is the human override for the detector — it PASSES
|
|
263
|
+
// even when detection fires (that is acceptance #5's point: the
|
|
264
|
+
// detector is fallible and the declaration is the reasoned overrule).
|
|
265
|
+
// But a contradiction must be VISIBLE, not silent: re-run detection
|
|
266
|
+
// over the phase scope and surface any signals it still finds
|
|
267
|
+
// (#2365 review S-1).
|
|
268
|
+
const declScope = readPhaseScope(projectDir, resolvedDir, phaseNumber);
|
|
269
|
+
const declDetection = detectApiIntegration(declScope.text);
|
|
270
|
+
const declSignals = declDetection.signals.map((s) => ({ verb: s.verb, noun: s.noun }));
|
|
271
|
+
// The declaration legitimately wins even over a read error (it is the
|
|
272
|
+
// human overrule), but if scope was incomplete we say so — the contract
|
|
273
|
+
// is that contradictions stay visible, not silent (#2365 review).
|
|
274
|
+
const baseMsg = declDetection.detected
|
|
275
|
+
? `api-coverage: COVERAGE.md declares no external API integration, overriding ${declSignals.length} detected signal(s) — confirm the declaration is accurate`
|
|
276
|
+
: 'api-coverage: COVERAGE.md declares no external API integration — matrix not required';
|
|
277
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('pass', false, {
|
|
278
|
+
block: false,
|
|
279
|
+
passed: true,
|
|
280
|
+
coverage_present: true,
|
|
281
|
+
matrix: coverageFile,
|
|
282
|
+
counts: v.counts,
|
|
283
|
+
none_declared: true,
|
|
284
|
+
detected: declDetection.detected,
|
|
285
|
+
...(declDetection.detected ? { signals: declSignals } : {}),
|
|
286
|
+
...(declScope.readError ? { scope_read_error: declScope.readError } : {}),
|
|
287
|
+
message: declScope.readError
|
|
288
|
+
? `${baseMsg} (note: phase scope was incompletely read — ${declScope.readError})`
|
|
289
|
+
: baseMsg,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('pass', false, {
|
|
293
|
+
block: false,
|
|
294
|
+
passed: true,
|
|
295
|
+
coverage_present: true,
|
|
296
|
+
matrix: coverageFile,
|
|
297
|
+
counts: v.counts,
|
|
298
|
+
message: `api-coverage: matrix present (${v.counts.surface} capabilities, ${v.counts.optout} opt-out)`,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
// Fixed-template message (no raw cell content echoed into the LLM-facing
|
|
302
|
+
// message). The structured `errors` array is safe (row-indexed, no cell
|
|
303
|
+
// values) and travels as data for tooling that wants detail.
|
|
304
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
305
|
+
block: true,
|
|
306
|
+
passed: false,
|
|
307
|
+
coverage_present: true,
|
|
308
|
+
matrix: coverageFile,
|
|
309
|
+
error_count: v.errors.length,
|
|
310
|
+
errors: v.errors,
|
|
311
|
+
message: `api-coverage: COVERAGE.md has ${v.errors.length} problem(s) — fix the matrix (every capability INTEGRATE or OPT-OUT with a reason) before sealing`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (suffixed.length > 1) {
|
|
315
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
316
|
+
block: true,
|
|
317
|
+
passed: false,
|
|
318
|
+
coverage_present: false,
|
|
319
|
+
message: `api-coverage: multiple *-COVERAGE.md files found (${suffixed.length}) — consolidate into one COVERAGE.md before sealing`,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
// (2) no matrix — detect whether this phase integrates an external API.
|
|
323
|
+
const scope = readPhaseScope(projectDir, resolvedDir, phaseNumber);
|
|
324
|
+
if (scope.readError) {
|
|
325
|
+
// Fail-closed: an unreadable plan could be the one describing the
|
|
326
|
+
// integration, so we cannot certify "no integration" — block and surface it. The blocking
|
|
327
|
+
// policy is unchanged; the outcome is `unreadable` (exit UNAVAILABLE, #5170).
|
|
328
|
+
return (0, gate_verdict_cjs_1.gateUnreadable)(true, {
|
|
329
|
+
block: true,
|
|
330
|
+
passed: false,
|
|
331
|
+
coverage_present: false,
|
|
332
|
+
detected: false,
|
|
333
|
+
message: `api-coverage: could not read the phase scope (${scope.readError}); ` +
|
|
334
|
+
'refusing to certify no external-API integration from incomplete scope. ' +
|
|
335
|
+
'Fix the unreadable plan file, or add a COVERAGE.md declaration.',
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// An EMPTY scope is not a negative verdict. This gate's neighbouring arms
|
|
339
|
+
// already fail closed (unresolvable phase → block; unreadable plan → block),
|
|
340
|
+
// but a phase with no plan body AND no roadmap section fell through to
|
|
341
|
+
// detection over zero bytes and CERTIFIED "no external-API integration" —
|
|
342
|
+
// clearing a blocking seal gate on a probe that examined nothing
|
|
343
|
+
// (ADR-3889 failure class (c), #3909). The discriminator is BYTES EXAMINED,
|
|
344
|
+
// never SIGNALS FOUND: a phase with real plans and no API vocabulary still
|
|
345
|
+
// reaches the pass below unchanged.
|
|
346
|
+
if (scope.text.trim() === '') {
|
|
347
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
348
|
+
block: true,
|
|
349
|
+
passed: false,
|
|
350
|
+
coverage_present: false,
|
|
351
|
+
detected: false,
|
|
352
|
+
scope_unavailable: true,
|
|
353
|
+
message: 'api-coverage: the phase scope is empty — no plan body and no roadmap section were ' +
|
|
354
|
+
'found, so nothing was examined. Refusing to certify no external-API integration ' +
|
|
355
|
+
'from an unestablished scope. Add the phase plan, or add a COVERAGE.md declaration.',
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
const detection = detectApiIntegration(scope.text);
|
|
359
|
+
if (detection.detected) {
|
|
360
|
+
// Surface only verb/noun (typed, bounded) — NOT raw prose snippets — so the
|
|
361
|
+
// gate output cannot relay injected PLAN.md instructions to the orchestrator.
|
|
362
|
+
const signals = detection.signals.map((s) => ({ verb: s.verb, noun: s.noun }));
|
|
363
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('block', true, {
|
|
364
|
+
block: true,
|
|
365
|
+
passed: false,
|
|
366
|
+
coverage_present: false,
|
|
367
|
+
detected: true,
|
|
368
|
+
signals,
|
|
369
|
+
message: 'api-coverage: external-API integration detected without a coverage matrix. ' +
|
|
370
|
+
'Produce COVERAGE.md enumerating the API surface (every capability INTEGRATE or ' +
|
|
371
|
+
'OPT-OUT with a reason) before sealing. Full coverage is the default.',
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
return (0, gate_verdict_cjs_1.gateVerdict)('pass', false, {
|
|
375
|
+
block: false,
|
|
376
|
+
passed: true,
|
|
377
|
+
coverage_present: false,
|
|
378
|
+
detected: false,
|
|
379
|
+
message: 'api-coverage: no external-API integration detected; coverage matrix not required',
|
|
380
|
+
});
|
|
381
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Gate argv parsing — the one `--flag value` / positional splitter the flag-taking `check` verbs
|
|
4
|
+
* share (#5139, epic #5056, design D2/D3).
|
|
5
|
+
*
|
|
6
|
+
* Moved verbatim from `check-command-router.cts` so a gate module can take the argv after its verb
|
|
7
|
+
* and the router's `check predicate` arm can keep using the same parser: there is exactly one, so
|
|
8
|
+
* the flag-taking check verbs cannot drift apart. Pure; no I/O.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.partitionPredicateArgs = partitionPredicateArgs;
|
|
12
|
+
exports.parsePredicateFlags = parsePredicateFlags;
|
|
13
|
+
/**
|
|
14
|
+
* Split an args array into `--flag value` pairs and the leftover positional
|
|
15
|
+
* tokens, in ONE pass, with the semantics `check predicate` established
|
|
16
|
+
* (#2008): a `--flag` followed by a non-`--` token consumes it as the value
|
|
17
|
+
* (last write wins); a `--flag` with no value stays a bare token and moves to
|
|
18
|
+
* the positionals; everything else is positional. `parsePredicateFlags` is
|
|
19
|
+
* the flags half of this same pass — there is exactly one parser, so the
|
|
20
|
+
* flag-taking check verbs cannot drift apart (#4130 follow-up: `check
|
|
21
|
+
* decision-coverage-plan --context <path>` shares it).
|
|
22
|
+
*/
|
|
23
|
+
function partitionPredicateArgs(args) {
|
|
24
|
+
const flags = {};
|
|
25
|
+
const positionals = [];
|
|
26
|
+
for (let i = 0; i < args.length; i++) {
|
|
27
|
+
const a = args[i];
|
|
28
|
+
if (typeof a !== 'string')
|
|
29
|
+
continue;
|
|
30
|
+
if (!a.startsWith('--')) {
|
|
31
|
+
positionals.push(a);
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const key = a.slice(2);
|
|
35
|
+
const next = args[i + 1];
|
|
36
|
+
if (key.length > 0 && typeof next === 'string' && !next.startsWith('--')) {
|
|
37
|
+
// defineProperty, not `flags[key] = next`: an own data property for EVERY caller-supplied
|
|
38
|
+
// key. A plain assignment of `--__proto__ v` set nothing (the pair was consumed and its
|
|
39
|
+
// value silently lost), and assigning through a caller-controlled key is the
|
|
40
|
+
// prototype-pollution sink shape.
|
|
41
|
+
Object.defineProperty(flags, key, { value: next, enumerable: true, writable: true, configurable: true });
|
|
42
|
+
i++;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
positionals.push(a);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { flags, positionals };
|
|
49
|
+
}
|
|
50
|
+
/** Parse `--flag value` pairs from an args array into a map (last write wins). */
|
|
51
|
+
function parsePredicateFlags(args) {
|
|
52
|
+
return partitionPredicateArgs(args).flags;
|
|
53
|
+
}
|