@opengsd/gsd-core 1.9.1 → 1.11.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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +27 -3
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +12 -10
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +46 -0
- package/agents/gsd-planner.md +49 -54
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +26 -73
- package/bin/install.js +1272 -1238
- package/bin/lib/ui-safety-gate.cjs +2 -0
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/map-codebase.md +1 -1
- package/commands/gsd/mempalace-capture.md +2 -2
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.md +2 -2
- package/commands/gsd/plan-phase.md +1 -1
- package/commands/gsd/quick.md +1 -1
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1009 -115
- package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
- package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
- package/gsd-core/bin/lib/api-coverage.cjs +123 -5
- package/gsd-core/bin/lib/artifacts.cjs +3 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +926 -202
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- package/gsd-core/bin/lib/capability-consent.cjs +149 -15
- package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
- package/gsd-core/bin/lib/capability-registry.cjs +608 -148
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +507 -24
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +114 -38
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
- package/gsd-core/bin/lib/command-aliases.cjs +94 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +665 -99
- package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +76 -0
- package/gsd-core/bin/lib/config.cjs +22 -2
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +217 -40
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +239 -32
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
- package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1325 -169
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +805 -264
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
- package/gsd-core/bin/lib/install-profiles.cjs +160 -57
- package/gsd-core/bin/lib/install-scope.cjs +270 -0
- package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
- package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
- package/gsd-core/bin/lib/io.cjs +38 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +821 -109
- package/gsd-core/bin/lib/model-catalog.cjs +59 -1
- package/gsd-core/bin/lib/model-resolver.cjs +183 -40
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
- package/gsd-core/bin/lib/phase-id.cjs +507 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +258 -58
- package/gsd-core/bin/lib/phase.cjs +891 -156
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +86 -2
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
- package/gsd-core/bin/lib/probe-core.cjs +1 -1
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +405 -84
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
- package/gsd-core/bin/lib/smart-entry.cjs +154 -22
- package/gsd-core/bin/lib/state-command-router.cjs +5 -1
- package/gsd-core/bin/lib/state-document.cjs +152 -8
- package/gsd-core/bin/lib/state-transition.cjs +424 -105
- package/gsd-core/bin/lib/state.cjs +1927 -401
- package/gsd-core/bin/lib/surface.cjs +35 -10
- package/gsd-core/bin/lib/text-lines.cjs +80 -0
- package/gsd-core/bin/lib/token-scanner.cjs +76 -0
- package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
- package/gsd-core/bin/lib/uat.cjs +706 -64
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
- package/gsd-core/bin/lib/unusable-input.cjs +33 -0
- package/gsd-core/bin/lib/update-context.cjs +8 -2
- package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +37 -0
- package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
- package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +287 -20
- package/gsd-core/bin/lib/verify.cjs +368 -880
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
- package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
- package/gsd-core/bin/lib/workstream.cjs +8 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/execute-mvp-tdd.md +3 -3
- package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
- package/gsd-core/references/execute-phase-context-guard.md +1 -1
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +6 -2
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-planning-commit.md +2 -1
- package/gsd-core/references/loop-hook-dispatch.md +39 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planning-config.md +8 -6
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +8 -7
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +2 -4
- package/gsd-core/templates/state.md +4 -4
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/templates/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +33 -70
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +74 -166
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +160 -95
- package/gsd-core/workflows/debug.md +16 -17
- package/gsd-core/workflows/diagnose-issues.md +56 -8
- package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/edit-phase.md +26 -1
- package/gsd-core/workflows/eval-review.md +3 -5
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +103 -187
- package/gsd-core/workflows/execute-plan.md +36 -4
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/health.md +73 -4
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +7 -6
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +35 -47
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +27 -240
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +89 -209
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +45 -159
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +55 -405
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +41 -13
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings.md +66 -2
- package/gsd-core/workflows/ship.md +104 -44
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +41 -20
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +106 -13
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +53 -31
- package/gsd-core/workflows/ui-phase.md +13 -12
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +19 -8
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +17 -65
- package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
- package/hooks/dist/gsd-check-update-worker.js +64 -12
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
- package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
- package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/dist/gsd-prompt-guard.js +21 -20
- package/hooks/dist/gsd-read-injection-scanner.js +45 -24
- package/hooks/dist/gsd-statusline.js +90 -6
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/injection-patterns.js +45 -0
- package/hooks/dist/lib/isolation-deny-reason.js +39 -0
- package/hooks/dist/lib/isolation-sentinel.js +277 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +517 -0
- package/hooks/gsd-check-update-worker.js +64 -12
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +607 -26
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +45 -24
- package/hooks/gsd-statusline.js +90 -6
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/injection-patterns.js +45 -0
- package/hooks/lib/isolation-deny-reason.js +39 -0
- package/hooks/lib/isolation-sentinel.js +277 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +31 -10
- package/pi/gsd.cjs +71 -12
- package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
- package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
- package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
- package/scripts/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +68 -6
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/check-alias-drift.cjs +7 -43
- package/scripts/check-contract-drift.cjs +297 -0
- package/scripts/ci-test-scope.cjs +19 -2
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-capability-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +439 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +150 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/drift-scan.cjs +278 -0
- package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
- package/scripts/lint-canary-version-leak.cjs +73 -0
- package/scripts/lint-command-contract.cjs +96 -13
- package/scripts/lint-compiled-artifact-sync.cjs +6 -1
- package/scripts/lint-completion-predicate-drift.cjs +933 -0
- package/scripts/lint-completion-ratio-drift.cjs +214 -0
- package/scripts/lint-default-flip-documentation.cjs +193 -0
- package/scripts/lint-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-phase-enumeration-drift.cjs +479 -0
- package/scripts/lint-plan-count-drift.cjs +318 -0
- package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
- package/scripts/lint-planning-prompt-drift.cjs +434 -0
- package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
- package/scripts/lint-regression-test-names.cjs +15 -13
- package/scripts/lint-removed-but-needed.cjs +320 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +1045 -0
- package/scripts/lint-test-file-count.allowlist.json +40 -3
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +52 -6
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/state-write-path-drift-baseline.json +19 -0
- package/scripts/sync-runtime-launcher.cjs +2 -4
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-map-codebase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +2 -2
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +2 -2
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +1 -2
- package/skills/gsd-progress/SKILL.md +0 -1
- package/skills/gsd-quick/SKILL.md +1 -1
- package/skills/gsd-review-backlog/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +0 -1
- package/skills/gsd-verify-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
- package/gsd-core/workflows/discovery-phase.md +0 -298
- package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
- package/gsd-core/workflows/verify-phase.md +0 -577
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/gen-emitted-baseline.cjs +0 -145
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1050
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
const cjsCommandRouterAdapter = require("./cjs-command-router-adapter.cjs");
|
|
14
14
|
const { routeCjsCommandFamily } = cjsCommandRouterAdapter;
|
|
15
15
|
// ─── Implementation ───────────────────────────────────────────────────────────
|
|
16
|
-
const VERIFICATION_SUBCOMMANDS = ['status'];
|
|
16
|
+
const VERIFICATION_SUBCOMMANDS = ['status', 'resolve-file'];
|
|
17
17
|
function routeVerificationCommand({ verification, args, cwd, raw, error, }) {
|
|
18
18
|
routeCjsCommandFamily({
|
|
19
19
|
args,
|
|
@@ -23,6 +23,7 @@ function routeVerificationCommand({ verification, args, cwd, raw, error, }) {
|
|
|
23
23
|
unknownMessage: (_subcommand, available) => `Unknown verification subcommand. Available: ${available.join(', ')}`,
|
|
24
24
|
handlers: {
|
|
25
25
|
status: () => verification.cmdVerificationStatus(cwd, args[2], raw),
|
|
26
|
+
'resolve-file': () => verification.cmdVerificationResolveFile(cwd, args[2], raw),
|
|
26
27
|
},
|
|
27
28
|
});
|
|
28
29
|
}
|
|
@@ -40,11 +40,14 @@ const phaseId = require("./phase-id.cjs");
|
|
|
40
40
|
const frontmatterMod = require("./frontmatter.cjs");
|
|
41
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module
|
|
42
42
|
const scanPhasePlans = require("./plan-scan.cjs");
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-scope.cjs is an export= CommonJS module
|
|
44
|
+
const planningScopeMod = require("./planning-scope.cjs");
|
|
43
45
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
44
46
|
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
45
47
|
const { output, error } = io;
|
|
46
|
-
const { extractPhaseToken } = phaseId;
|
|
48
|
+
const { extractPhaseToken, scopeToPhase } = phaseId;
|
|
47
49
|
const { extractFrontmatter } = frontmatterMod;
|
|
50
|
+
const { SCOPE } = planningScopeMod;
|
|
48
51
|
// ─── Constants ────────────────────────────────────────────────────────────────
|
|
49
52
|
/** The set of status values that the gsd-verifier agent emits. */
|
|
50
53
|
const VERIFIER_STATUSES = ['passed', 'gaps_found', 'human_needed'];
|
|
@@ -100,7 +103,7 @@ const VERIFICATION_ROUTING_TABLE = {
|
|
|
100
103
|
// the file has no parseable frontmatter status. Never emitted by the verifier.
|
|
101
104
|
missing: {
|
|
102
105
|
status: 'missing',
|
|
103
|
-
next_action: 'No verification report found — the verify step never completed.
|
|
106
|
+
next_action: 'No verification report found — the verify step never completed. Running execute-phase is safe here: it resumes at the verification gates and does not re-run plans that already have a SUMMARY.md (see #2868).',
|
|
104
107
|
next_command: 'execute-phase',
|
|
105
108
|
},
|
|
106
109
|
// INTERNAL SENTINEL: constructed when the file has a status value not in
|
|
@@ -211,25 +214,173 @@ function missingResult(runtime, phaseArg) {
|
|
|
211
214
|
next_command: projectNextCommand(route.next_command, runtime, phaseArg),
|
|
212
215
|
};
|
|
213
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* #3518: the shared phase-pinned artifact-selection core BOTH single-pick
|
|
219
|
+
* resolvers (`resolveVerificationFile` for `*-VERIFICATION.md`,
|
|
220
|
+
* `resolveUatFile` for `*-UAT.md`) delegate to — one rule, not two grammars
|
|
221
|
+
* that agree today and drift tomorrow (epic #3473 F2's defect class).
|
|
222
|
+
*
|
|
223
|
+
* `bareName` is the artifact filename WITHOUT the leading dash (`'UAT.md'`);
|
|
224
|
+
* a "dashed" candidate is any entry ending `-${bareName}`.
|
|
225
|
+
*
|
|
226
|
+
* Selection order:
|
|
227
|
+
* 1. `options.phaseToken` given and `<phaseToken>-${bareName}` is among
|
|
228
|
+
* the candidates — that exact file always wins: it is THIS phase's own
|
|
229
|
+
* artifact, and no other candidate (whichever phase's token it carries)
|
|
230
|
+
* can outrank it (#3492 / #3518).
|
|
231
|
+
* 2. Fallback — no exact phase-token match (or no token given): alphabetically
|
|
232
|
+
* first of the dashed candidates that are THIS phase's own, per
|
|
233
|
+
* `scopeToPhase(candidates, options.phaseDirName)` (#3511 reconciliation,
|
|
234
|
+
* below). Load-bearing: a phase whose only artifact is non-canonically
|
|
235
|
+
* named must keep resolving to it, not to null — this fix must not turn
|
|
236
|
+
* "found an artifact" into "found nothing" for anyone. A
|
|
237
|
+
* non-canonically-named artifact of THIS phase (e.g.
|
|
238
|
+
* `03-CORRECTION-VERIFICATION.md` in `03-foo`) still passes
|
|
239
|
+
* `isPhaseArtifact` (it names phase 03, same as the directory), so it
|
|
240
|
+
* is still returned here.
|
|
241
|
+
* 3. `options.allowBare` only — a bare `${bareName}`, ranked BELOW both
|
|
242
|
+
* of the above. Rationale: a dashed file names its phase, a bare one
|
|
243
|
+
* does not, so a dashed file (canonical or not) is always the better
|
|
244
|
+
* answer when both exist. Reached when neither (1) nor (2) found any
|
|
245
|
+
* candidate — including when (2)'s scoping filtered every dashed
|
|
246
|
+
* candidate out as belonging to some OTHER phase.
|
|
247
|
+
*
|
|
248
|
+
* #3511 RECONCILIATION with `isPhaseArtifact` (`src/phase-id.cts`): that
|
|
249
|
+
* predicate's own docblock used to flag this fallback as an open gap — its
|
|
250
|
+
* aggregate scans exclude a cross-phase stray, but this single-pick resolver
|
|
251
|
+
* did not, so it could return a stray as THE artifact while the aggregate
|
|
252
|
+
* scans correctly ignored it. Closed by scoping step (2) above through
|
|
253
|
+
* `scopeToPhase` (`src/phase-id.cts`, itself built on `isPhaseArtifact`):
|
|
254
|
+
* `options.phaseDirName` threads the phase directory's basename in, and the
|
|
255
|
+
* fallback now filters candidates through `scopeToPhase(candidates,
|
|
256
|
+
* phaseDirName)` before picking alphabetically-first. This does NOT reopen
|
|
257
|
+
* the #3357 guarantee — that guarantee is "a phase whose only report is
|
|
258
|
+
* non-canonically named must keep working", and a non-canonically-named
|
|
259
|
+
* artifact of THIS phase still passes `isPhaseArtifact` (it is membership by
|
|
260
|
+
* phase number, not by canonical shape), so it is still returned. Only a
|
|
261
|
+
* file belonging to a DIFFERENT phase is now excluded — and excluding it is
|
|
262
|
+
* correct: returning another phase's artifact as this phase's own is worse
|
|
263
|
+
* than reporting none (confidently wrong beats honestly empty).
|
|
264
|
+
* The fail-safe now lives entirely inside `isPhaseArtifact`, not in
|
|
265
|
+
* `scopeToPhase` (which is a plain filter with no unfiltered fallback):
|
|
266
|
+
* (a) when phase-number membership cannot be determined for `phaseDirName` at
|
|
267
|
+
* all (no reliable token — the zero-token directory case), every candidate is
|
|
268
|
+
* treated as belonging to the phase; (b) the `firstLetterPrefixed`
|
|
269
|
+
* bracket-ambiguity case, where a letter-prefixed-decimal dir is
|
|
270
|
+
* string-indistinguishable from a bracket-dir token, also includes
|
|
271
|
+
* everything rather than guess; (c) a token-less filename (bare
|
|
272
|
+
* `${bareName}`) is accepted by directory containment alone. Outside
|
|
273
|
+
* those cases, when scoping DOES remove every dashed candidate — a real
|
|
274
|
+
* cross-phase stray, or a phase whose own artifact is genuinely absent — the
|
|
275
|
+
* fallback below correctly falls through to `allowBare`/`null`: reporting no
|
|
276
|
+
* artifact, not another phase's. `options.phaseDirName` omitted entirely skips
|
|
277
|
+
* the filter outright (the ternary below), which is unscoped, pre-#3511
|
|
278
|
+
* behavior.
|
|
279
|
+
*
|
|
280
|
+
* Pure — takes an already-read directory listing and does no I/O of its own,
|
|
281
|
+
* so every call site keeps its existing `fsImpl` seam and no-throw contract
|
|
282
|
+
* untouched.
|
|
283
|
+
*/
|
|
284
|
+
function resolvePhaseArtifactFile(entries, bareName, options = {}) {
|
|
285
|
+
const candidates = entries.filter((f) => f.endsWith(`-${bareName}`)).sort();
|
|
286
|
+
if (candidates.length > 0) {
|
|
287
|
+
if (options.phaseToken) {
|
|
288
|
+
const thisPhaseFile = `${options.phaseToken}-${bareName}`;
|
|
289
|
+
if (candidates.includes(thisPhaseFile))
|
|
290
|
+
return thisPhaseFile;
|
|
291
|
+
}
|
|
292
|
+
// #3511: scope the fallback to files that belong to THIS phase, so a
|
|
293
|
+
// stray cross-phase file can no longer outrank a return of null.
|
|
294
|
+
// `phaseDirName` omitted, or membership undeterminable for it, →
|
|
295
|
+
// unscoped `candidates` (pre-#3511 behavior); otherwise strays are
|
|
296
|
+
// filtered out, and if that leaves nothing the code falls through to
|
|
297
|
+
// `allowBare`/`null` deliberately.
|
|
298
|
+
const scoped = options.phaseDirName
|
|
299
|
+
? scopeToPhase(candidates, options.phaseDirName)
|
|
300
|
+
: candidates;
|
|
301
|
+
if (scoped.length > 0)
|
|
302
|
+
return scoped[0];
|
|
303
|
+
}
|
|
304
|
+
if (options.allowBare && entries.includes(bareName))
|
|
305
|
+
return bareName;
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Resolve which `*-VERIFICATION.md` entry in a phase directory's listing IS
|
|
310
|
+
* the phase's verification report, when more than one such file exists.
|
|
311
|
+
*
|
|
312
|
+
* #3357: a phase dir can legitimately hold more than one `*-VERIFICATION.md`
|
|
313
|
+
* — the real per-phase report (`03-VERIFICATION.md`) alongside an ad-hoc plan
|
|
314
|
+
* worksheet (`03-CORRECTION-VERIFICATION.md`). Picking "alphabetically first"
|
|
315
|
+
* (`'C' < 'V'`) silently chose the worksheet, which usually has no
|
|
316
|
+
* frontmatter `status:`, so a phase with a PASSING report read as `missing`.
|
|
317
|
+
* This was two independent hand-rolled `.sort()[0]` picks
|
|
318
|
+
* (findStaleVerificationSummary and readVerificationStatus) — this is the
|
|
319
|
+
* single resolver both now call (#3473 F2).
|
|
320
|
+
*
|
|
321
|
+
* Selection order: see `resolvePhaseArtifactFile` (the shared core this
|
|
322
|
+
* delegates to since #3518, itself phase-scoped since #3511) —
|
|
323
|
+
* phase-token-pinned, then phase-scoped alphabetically-first dashed
|
|
324
|
+
* fallback, then (allowBare only) a bare `VERIFICATION.md`. #3518 extracted
|
|
325
|
+
* this into the shared core without changing behavior; #3511's
|
|
326
|
+
* `phaseDirName` scoping now lives inside that shared core rather than here.
|
|
327
|
+
*/
|
|
328
|
+
function resolveVerificationFile(entries, options = {}) {
|
|
329
|
+
return resolvePhaseArtifactFile(entries, 'VERIFICATION.md', options);
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* #3518: resolve which `*-UAT.md` entry in a phase directory's listing IS
|
|
333
|
+
* the phase's UAT artifact, when more than one such file exists — the UAT
|
|
334
|
+
* counterpart of `resolveVerificationFile`, sharing its exact selection rule
|
|
335
|
+
* via `resolvePhaseArtifactFile`.
|
|
336
|
+
*
|
|
337
|
+
* The bug this closes: both `uat_path` projectors in `src/init.cts` picked
|
|
338
|
+
* with a bare `.find((f) => f.endsWith('-UAT.md') || f === 'UAT.md')` over an
|
|
339
|
+
* unsorted `readdir` listing — no phase-membership check and no ordering — so
|
|
340
|
+
* a stray or cross-phase `04-UAT.md` sitting in phase 03's directory could
|
|
341
|
+
* become phase 03's `uat_path`, and WHICH file won was filesystem-dependent
|
|
342
|
+
* (creation order on APFS, hash order on ext4/XFS): two machines on the same
|
|
343
|
+
* commit could emit different `uat_path` values for the same phase. `uat_path`
|
|
344
|
+
* is consumed downstream by workflows that then read the named file, so a
|
|
345
|
+
* wrong path routes UAT state from another phase.
|
|
346
|
+
*
|
|
347
|
+
* Deterministic by construction: same answer on every machine. Phase-scoped
|
|
348
|
+
* (#3511): passing `options.phaseDirName` filters the alphabetically-first
|
|
349
|
+
* fallback (tier 2) to artifacts that belong to THIS phase — see
|
|
350
|
+
* `resolvePhaseArtifactFile` for the full selection order and scoping
|
|
351
|
+
* rationale.
|
|
352
|
+
*/
|
|
353
|
+
function resolveUatFile(entries, options = {}) {
|
|
354
|
+
return resolvePhaseArtifactFile(entries, 'UAT.md', options);
|
|
355
|
+
}
|
|
214
356
|
function findStaleVerificationSummary(phaseDir, fsImpl = node_fs_1.default, phaseCleanCommitTimesMs = defaultPhaseCleanCommitTimesMs) {
|
|
215
357
|
// FS errors (TOCTOU: a SUMMARY listed by scanPhasePlans then removed before statSync;
|
|
216
|
-
// unreadable dir; broken symlink; file->dir swap) must degrade
|
|
217
|
-
//
|
|
218
|
-
//
|
|
219
|
-
//
|
|
220
|
-
// (Review B1 on #1548.)
|
|
358
|
+
// unreadable dir; broken symlink; file->dir swap) must degrade rather than throw
|
|
359
|
+
// uncaught into callers that are NOT under the planning lock (init.manager /
|
|
360
|
+
// init.progress / uat-predicate). Mirrors readVerificationStatus's no-throw
|
|
361
|
+
// contract; `fsImpl` threads the same injectable-fs seam for parity/testing.
|
|
362
|
+
// (Review B1 on #1548.) The degraded result is `{determined:false}`, NOT the
|
|
363
|
+
// same value as a completed "nothing is stale" check — see StaleCheckResult
|
|
364
|
+
// doc and #3057 B3. The caller decides how to route an indeterminate result;
|
|
365
|
+
// this function only reports what it actually knows.
|
|
221
366
|
try {
|
|
222
367
|
const phaseFiles = fsImpl.readdirSync(phaseDir);
|
|
223
|
-
|
|
368
|
+
// #3492: pin selection to THIS phase's own token so a stray cross-phase
|
|
369
|
+
// or sentinel-numbered canonically-shaped file cannot outrank this
|
|
370
|
+
// phase's own (possibly non-canonical) report. #3511: phaseDirName scopes
|
|
371
|
+
// the fallback path to this same phase (see resolveVerificationFile docs).
|
|
372
|
+
const phaseDirName = node_path_1.default.basename(phaseDir);
|
|
373
|
+
const phaseToken = extractPhaseToken(phaseDirName);
|
|
374
|
+
const verificationFile = resolveVerificationFile(phaseFiles, { phaseToken, phaseDirName });
|
|
224
375
|
if (!verificationFile)
|
|
225
|
-
return
|
|
376
|
+
return { determined: true, stale: false };
|
|
226
377
|
const summaryFiles = scanPhasePlans(phaseDir).summaryFiles
|
|
227
378
|
.slice()
|
|
228
379
|
.sort();
|
|
229
380
|
// No summary can be newer than the verification → never stale. Return before
|
|
230
381
|
// touching git so a phase with no summaries costs zero subprocesses. (#2348)
|
|
231
382
|
if (summaryFiles.length === 0)
|
|
232
|
-
return
|
|
383
|
+
return { determined: true, stale: false };
|
|
233
384
|
// Each file's effective "last changed" time = its commit time when committed
|
|
234
385
|
// AND clean (content-tied and clone-stable), else its filesystem mtime (the
|
|
235
386
|
// uncommitted working-tree edit). Both are real wall-clock change times, so
|
|
@@ -244,13 +395,13 @@ function findStaleVerificationSummary(phaseDir, fsImpl = node_fs_1.default, phas
|
|
|
244
395
|
// The caller only needs whether the phase is stale, not which summary —
|
|
245
396
|
// the first stale summary (in sorted order) is enough. Short-circuit.
|
|
246
397
|
if (effectiveTimeMs(summaryFile) > verificationTimeMs) {
|
|
247
|
-
return { verificationFile, summaryFile };
|
|
398
|
+
return { determined: true, stale: true, verificationFile, summaryFile };
|
|
248
399
|
}
|
|
249
400
|
}
|
|
250
|
-
return
|
|
401
|
+
return { determined: true, stale: false };
|
|
251
402
|
}
|
|
252
403
|
catch {
|
|
253
|
-
return
|
|
404
|
+
return { determined: false };
|
|
254
405
|
}
|
|
255
406
|
}
|
|
256
407
|
/**
|
|
@@ -258,13 +409,21 @@ function findStaleVerificationSummary(phaseDir, fsImpl = node_fs_1.default, phas
|
|
|
258
409
|
* phaseDir and return the routing result.
|
|
259
410
|
*
|
|
260
411
|
* Behavior:
|
|
261
|
-
* 1. Find the
|
|
262
|
-
*
|
|
412
|
+
* 1. Find the phase's verification report via `resolveVerificationFile`
|
|
413
|
+
* (canonical `<phase-token>-VERIFICATION.md` preferred; falls back to the
|
|
414
|
+
* alphabetically-first `*-VERIFICATION.md` that belongs to THIS phase when
|
|
415
|
+
* none is canonical — #3357/#3511). If none → status 'missing'.
|
|
263
416
|
* 2. Extract `status` from FRONTMATTER ONLY via the shared extractFrontmatter
|
|
264
417
|
* parser (DEFECT.FRONTMATTER-SCALAR-BROAD-GREP fix — parser anchors at byte 0).
|
|
265
418
|
* If no frontmatter block or no `status` key → status 'missing'.
|
|
266
419
|
* 3. Map to routing table. Unknown non-empty value → status 'unknown'.
|
|
267
420
|
*
|
|
421
|
+
* The internal staleness check can itself fail (fs / scanPhasePlans / clock
|
|
422
|
+
* error); when it does, `status` is routed as if nothing were stale (the
|
|
423
|
+
* pre-existing no-throw fail-open contract — unchanged), but the returned
|
|
424
|
+
* result carries `staleCheckIndeterminate: true` so a caller can distinguish
|
|
425
|
+
* "checked; nothing is stale" from "could not check" (#3057 B3).
|
|
426
|
+
*
|
|
268
427
|
* @param phaseDir - Absolute path to the phase directory.
|
|
269
428
|
* @param opts - Options. `opts.fs` allows test injection (defaults to node:fs).
|
|
270
429
|
* `opts.runtime` selects the command surface `next_command` is
|
|
@@ -290,8 +449,12 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
290
449
|
let verificationFile = null;
|
|
291
450
|
try {
|
|
292
451
|
const entries = fsImpl.readdirSync(phaseDir);
|
|
293
|
-
|
|
294
|
-
|
|
452
|
+
// #3492: pin selection to THIS phase's own token (already derived above
|
|
453
|
+
// for the routed command argument) so a stray cross-phase or
|
|
454
|
+
// sentinel-numbered canonically-shaped file cannot outrank this phase's
|
|
455
|
+
// own (possibly non-canonical) report. #3511: baseName also scopes the
|
|
456
|
+
// fallback path to this same phase (see resolveVerificationFile docs).
|
|
457
|
+
verificationFile = resolveVerificationFile(entries, { phaseToken, phaseDirName: baseName });
|
|
295
458
|
}
|
|
296
459
|
catch {
|
|
297
460
|
// Directory unreadable → treat as missing
|
|
@@ -331,8 +494,8 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
331
494
|
next_command: projectNextCommand('plan-phase', runtime, `${phaseArg} --gaps`),
|
|
332
495
|
};
|
|
333
496
|
}
|
|
334
|
-
const
|
|
335
|
-
if (
|
|
497
|
+
const staleCheck = findStaleVerificationSummary(phaseDir, fsImpl, phaseCleanCommitTimesMs);
|
|
498
|
+
if (staleCheck.determined && staleCheck.stale) {
|
|
336
499
|
const entry = VERIFICATION_ROUTING_TABLE['stale'];
|
|
337
500
|
return {
|
|
338
501
|
status: entry.status,
|
|
@@ -340,6 +503,12 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
340
503
|
next_command: projectNextCommand('verify-work', runtime, phaseArg),
|
|
341
504
|
};
|
|
342
505
|
}
|
|
506
|
+
// staleCheck is either {determined:true, stale:false} (checked; nothing
|
|
507
|
+
// stale) or {determined:false} (could not check — fs/scan/clock failure).
|
|
508
|
+
// Both fall through to normal routing below (the pre-existing no-throw
|
|
509
|
+
// fail-open contract is unchanged), but the indeterminate case is flagged
|
|
510
|
+
// on the returned result so a caller can tell the two apart (#3057 B3).
|
|
511
|
+
const staleCheckIndeterminate = !staleCheck.determined;
|
|
343
512
|
// 3. Route — exclude internal sentinels from raw-file lookup (they are
|
|
344
513
|
// constructed internally above, never written by the verifier).
|
|
345
514
|
if (rawStatus in VERIFICATION_ROUTING_TABLE &&
|
|
@@ -352,14 +521,68 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
352
521
|
status: entry.status,
|
|
353
522
|
next_action: entry.next_action,
|
|
354
523
|
next_command: projectNextCommand(entry.next_command, runtime, phaseArg),
|
|
524
|
+
...(staleCheckIndeterminate ? { staleCheckIndeterminate: true } : {}),
|
|
355
525
|
};
|
|
356
526
|
}
|
|
357
527
|
// Unknown value
|
|
358
528
|
const unknownRoute = VERIFICATION_ROUTING_TABLE['unknown'];
|
|
359
529
|
return {
|
|
360
530
|
status: unknownRoute.status,
|
|
361
|
-
next_action: `Unexpected verification status '${rawStatus}'.
|
|
531
|
+
next_action: `Unexpected verification status '${rawStatus}'. If this is an intentional non-standard marker (e.g. a hand-set failed/superseded state), no action is needed. Otherwise, run execute-phase to regenerate verification — it will not re-run plans that already have a SUMMARY.md.`,
|
|
362
532
|
next_command: projectNextCommand(unknownRoute.next_command, runtime, phaseArg),
|
|
533
|
+
...(staleCheckIndeterminate ? { staleCheckIndeterminate: true } : {}),
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* isPhaseComplete — the single canonical owner of "is phase P complete?"
|
|
538
|
+
* (ADR-3180 §7.4, Decision 1). Sited beside readVerificationStatus, which it
|
|
539
|
+
* wraps.
|
|
540
|
+
*
|
|
541
|
+
* DISK-STRICT (#2957, maintainer decision 2026-08-08; ADR-3180 §7.4 amended
|
|
542
|
+
* af92fd4c9): readVerificationStatus is called UNCONDITIONALLY here — plan
|
|
543
|
+
* count is NOT a precondition. A phase with zero plans and a passing
|
|
544
|
+
* `*-VERIFICATION.md` is complete (#3168). A ROADMAP checkbox has no machine
|
|
545
|
+
* authority and is never consulted — this function never reads ROADMAP.md.
|
|
546
|
+
*
|
|
547
|
+
* `complete` is exactly `verification.status === 'passed'`. `verification`
|
|
548
|
+
* carries the FULL routing result (status/next_action/next_command), so a
|
|
549
|
+
* caller can distinguish a failing verdict (`gaps_found`/`human_needed`/
|
|
550
|
+
* `stale`/`unknown`) from an absent one (`missing`) — both are "not
|
|
551
|
+
* complete", but they are not the same non-answer.
|
|
552
|
+
*
|
|
553
|
+
* `scope` is UNREADABLE when `phaseDir` itself could not be listed — this is
|
|
554
|
+
* INDEPENDENT of readVerificationStatus's own no-throw fail-open contract for
|
|
555
|
+
* a missing `*-VERIFICATION.md` file (a well-formed answer,
|
|
556
|
+
* `verification.status === 'missing'`, scope COMPLETE): a caller must not
|
|
557
|
+
* read `value.complete: false` here as a confident "not complete" the way it
|
|
558
|
+
* can for a genuinely-checked missing file.
|
|
559
|
+
*
|
|
560
|
+
* Does NOT import scanPhasePlans / plan-scan.cjs — the owner consumes plan
|
|
561
|
+
* counts from its caller when a caller needs them for a different question
|
|
562
|
+
* (e.g. buildPhaseCompletionProjection's own `implementation_complete`); it
|
|
563
|
+
* never re-derives or requires them itself.
|
|
564
|
+
*/
|
|
565
|
+
function isPhaseComplete(phaseDir, deps = {}) {
|
|
566
|
+
const fsImpl = deps.fs ?? node_fs_1.default;
|
|
567
|
+
let readable = true;
|
|
568
|
+
try {
|
|
569
|
+
fsImpl.readdirSync(phaseDir);
|
|
570
|
+
}
|
|
571
|
+
catch {
|
|
572
|
+
readable = false;
|
|
573
|
+
}
|
|
574
|
+
const verification = readVerificationStatus(phaseDir, {
|
|
575
|
+
fs: deps.fs,
|
|
576
|
+
phaseCleanCommitTimesMs: deps.phaseCleanCommitTimesMs,
|
|
577
|
+
runtime: deps.runtime,
|
|
578
|
+
phaseNumber: deps.phaseNumber,
|
|
579
|
+
});
|
|
580
|
+
return {
|
|
581
|
+
value: {
|
|
582
|
+
complete: verification.status === 'passed',
|
|
583
|
+
verification,
|
|
584
|
+
},
|
|
585
|
+
scope: readable ? SCOPE.COMPLETE : SCOPE.UNREADABLE,
|
|
363
586
|
};
|
|
364
587
|
}
|
|
365
588
|
/**
|
|
@@ -379,11 +602,55 @@ function cmdVerificationStatus(cwd, phaseDirArg, raw) {
|
|
|
379
602
|
const result = readVerificationStatus(phaseDir, { runtime: (0, runtime_slash_cjs_1.resolveRuntime)(cwd) });
|
|
380
603
|
output(result, raw);
|
|
381
604
|
}
|
|
605
|
+
/**
|
|
606
|
+
* CLI command handler: resolve which `*-VERIFICATION.md` in `phaseDirArg` is
|
|
607
|
+
* the phase's own report, via the shared `resolveVerificationFile` seam, and
|
|
608
|
+
* emit its absolute path.
|
|
609
|
+
*
|
|
610
|
+
* #3492 F3: the ONE seam shell callers (verify-work.md's writer, transition.md's
|
|
611
|
+
* awk reader) route through instead of hand-rolling `ls *-VERIFICATION.md |
|
|
612
|
+
* head -1` / an awk glob scan — both of which pick alphabetically-first and so
|
|
613
|
+
* diverge from every JS reader now pinned to the phase's own token.
|
|
614
|
+
*
|
|
615
|
+
* Emits `{ verification_file: "<absolute path>" | "" }` (empty when no
|
|
616
|
+
* candidate resolves, including an unreadable directory). `raw` emits the
|
|
617
|
+
* bare path string (possibly empty) so `VAR=$(gsd_run query
|
|
618
|
+
* verification.resolve-file "$PHASE_DIR" --raw)` is directly assignable.
|
|
619
|
+
*
|
|
620
|
+
* @param cwd - Current working directory (used to resolve phaseDirArg).
|
|
621
|
+
* @param phaseDirArg - Phase directory path (absolute or relative to cwd).
|
|
622
|
+
* @param raw - Whether to emit raw (non-JSON) output.
|
|
623
|
+
*/
|
|
624
|
+
function cmdVerificationResolveFile(cwd, phaseDirArg, raw) {
|
|
625
|
+
if (!phaseDirArg) {
|
|
626
|
+
error('phase directory required for verification.resolve-file');
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
const phaseDir = node_path_1.default.resolve(cwd, phaseDirArg);
|
|
630
|
+
let verificationPath = '';
|
|
631
|
+
try {
|
|
632
|
+
const entries = node_fs_1.default.readdirSync(phaseDir);
|
|
633
|
+
const phaseDirName = node_path_1.default.basename(phaseDir);
|
|
634
|
+
const phaseToken = extractPhaseToken(phaseDirName);
|
|
635
|
+
const verificationFile = resolveVerificationFile(entries, { allowBare: true, phaseToken, phaseDirName });
|
|
636
|
+
if (verificationFile) {
|
|
637
|
+
verificationPath = node_path_1.default.join(phaseDir, verificationFile);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
catch {
|
|
641
|
+
verificationPath = '';
|
|
642
|
+
}
|
|
643
|
+
output({ verification_file: verificationPath }, raw, verificationPath);
|
|
644
|
+
}
|
|
382
645
|
module.exports = {
|
|
383
646
|
VERIFIER_STATUSES,
|
|
384
647
|
VERIFICATION_ROUTING_TABLE,
|
|
385
648
|
defaultPhaseCleanCommitTimesMs,
|
|
649
|
+
resolveVerificationFile,
|
|
650
|
+
resolveUatFile,
|
|
386
651
|
findStaleVerificationSummary,
|
|
387
652
|
readVerificationStatus,
|
|
653
|
+
isPhaseComplete,
|
|
388
654
|
cmdVerificationStatus,
|
|
655
|
+
cmdVerificationResolveFile,
|
|
389
656
|
};
|