@opengsd/gsd-core 1.10.0 → 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 +1 -1
- package/agents/gsd-debug-session-manager.md +11 -0
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-executor.md +5 -5
- package/agents/gsd-mempalace-curator.md +5 -2
- package/agents/gsd-phase-researcher.md +20 -1
- package/agents/gsd-plan-checker.md +37 -0
- package/agents/gsd-planner.md +44 -46
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +12 -3
- package/bin/install.js +841 -971
- 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 +1 -1
- package/commands/gsd/mempalace-recall.md +1 -1
- package/commands/gsd/new-milestone.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 +469 -88
- package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
- package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
- package/gsd-core/bin/lib/api-coverage.cjs +3 -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 +876 -240
- package/gsd-core/bin/lib/broken-windows.cjs +1 -1
- 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 +575 -101
- 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 +495 -22
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +71 -37
- 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 +22 -0
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/commands.cjs +651 -86
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
- package/gsd-core/bin/lib/config-loader.cjs +75 -0
- package/gsd-core/bin/lib/config.cjs +10 -1
- package/gsd-core/bin/lib/core-utils.cjs +127 -29
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +155 -20
- package/gsd-core/bin/lib/gap-checker.cjs +68 -7
- package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
- package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
- 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-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init.cjs +321 -129
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +745 -258
- 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 +134 -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-migrations.cjs +138 -31
- package/gsd-core/bin/lib/io.cjs +10 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +133 -20
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +754 -70
- 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 +444 -36
- package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
- package/gsd-core/bin/lib/phase-locator.cjs +125 -18
- package/gsd-core/bin/lib/phase.cjs +646 -143
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- 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 +56 -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/refactor-trigger-command-router.cjs +740 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
- package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
- package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
- package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +385 -94
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
- package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
- package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
- 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/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
- package/gsd-core/bin/lib/smart-entry.cjs +142 -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 +371 -117
- package/gsd-core/bin/lib/state.cjs +1794 -357
- package/gsd-core/bin/lib/surface.cjs +23 -9
- 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 +9 -3
- package/gsd-core/bin/lib/uat.cjs +399 -56
- 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 +24 -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 +258 -8
- package/gsd-core/bin/lib/verify.cjs +368 -888
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
- package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
- package/gsd-core/references/agent-contracts.md +43 -26
- package/gsd-core/references/checkpoints.md +2 -2
- package/gsd-core/references/context-budget.md +1 -1
- 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/revision-loop.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +3 -3
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-mvp-mode.md +1 -1
- package/gsd-core/references/workstream-flag.md +22 -6
- 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/verification-report.md +9 -1
- package/gsd-core/workflows/ai-integration-phase.md +9 -11
- package/gsd-core/workflows/autonomous.md +1 -1
- package/gsd-core/workflows/cleanup.md +62 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
- package/gsd-core/workflows/code-review-fix.md +37 -10
- package/gsd-core/workflows/code-review.md +38 -12
- package/gsd-core/workflows/complete-milestone.md +141 -18
- package/gsd-core/workflows/debug.md +7 -5
- package/gsd-core/workflows/diagnose-issues.md +35 -9
- 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.md +2 -1
- 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 +31 -6
- 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 +2 -0
- package/gsd-core/workflows/execute-phase.md +38 -50
- 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/import.md +4 -4
- package/gsd-core/workflows/ingest-docs.md +5 -5
- package/gsd-core/workflows/mvp-phase.md +6 -3
- package/gsd-core/workflows/new-milestone.md +14 -9
- package/gsd-core/workflows/new-project.md +14 -14
- package/gsd-core/workflows/next.md +12 -0
- package/gsd-core/workflows/plan-phase.md +41 -17
- package/gsd-core/workflows/plan-review-convergence.md +50 -2
- package/gsd-core/workflows/progress.md +34 -6
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
- package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
- package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
- package/gsd-core/workflows/quick.md +35 -15
- package/gsd-core/workflows/review.md +26 -5
- 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/spec-phase.md +30 -12
- package/gsd-core/workflows/sync-skills.md +63 -8
- package/gsd-core/workflows/transition.md +46 -11
- package/gsd-core/workflows/ui-phase.md +5 -5
- package/gsd-core/workflows/ui-review.md +2 -2
- package/gsd-core/workflows/update.md +1 -1
- package/gsd-core/workflows/validate-phase.md +1 -1
- package/gsd-core/workflows/verify-work.md +9 -7
- package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- 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 +77 -2
- 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 +38 -24
- package/hooks/dist/gsd-statusline.js +18 -0
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-workflow-guard.js +134 -36
- 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 +9 -0
- package/hooks/gsd-agent-isolation-guard.js +103 -14
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-cursor-pre-tool.js +0 -3
- package/hooks/gsd-cursor-subagent-start.js +77 -2
- package/hooks/gsd-cursor-subagent-stop.js +3 -2
- package/hooks/gsd-prompt-guard.js +21 -20
- package/hooks/gsd-read-injection-scanner.js +38 -24
- package/hooks/gsd-statusline.js +18 -0
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-workflow-guard.js +134 -36
- 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 +9 -0
- package/package.json +21 -9
- package/pi/gsd.cjs +19 -5
- 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/changeset/lint.cjs +60 -5
- 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-registry.cjs +3 -15
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +4 -24
- package/scripts/gen-registry.cjs +3 -14
- 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 +1 -26
- 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-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-eslint-glob-coverage.allowlist.json +34 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -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 +21 -10
- package/scripts/lint-unreachable-guard-drift.cjs +843 -0
- package/scripts/lint-vendored-deps.cjs +124 -0
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +25 -0
- 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 +1 -1
- package/skills/gsd-mempalace-recall/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-next/SKILL.md +0 -1
- package/skills/gsd-plan-phase/SKILL.md +0 -1
- 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 -574
- package/scripts/affected-tests-lib.cjs +0 -554
- package/scripts/lint-allow-test-rule-refs.cjs +0 -162
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -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,6 +214,145 @@ 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
358
|
// unreadable dir; broken symlink; file->dir swap) must degrade rather than throw
|
|
@@ -223,7 +365,13 @@ function findStaleVerificationSummary(phaseDir, fsImpl = node_fs_1.default, phas
|
|
|
223
365
|
// this function only reports what it actually knows.
|
|
224
366
|
try {
|
|
225
367
|
const phaseFiles = fsImpl.readdirSync(phaseDir);
|
|
226
|
-
|
|
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 });
|
|
227
375
|
if (!verificationFile)
|
|
228
376
|
return { determined: true, stale: false };
|
|
229
377
|
const summaryFiles = scanPhasePlans(phaseDir).summaryFiles
|
|
@@ -261,8 +409,10 @@ function findStaleVerificationSummary(phaseDir, fsImpl = node_fs_1.default, phas
|
|
|
261
409
|
* phaseDir and return the routing result.
|
|
262
410
|
*
|
|
263
411
|
* Behavior:
|
|
264
|
-
* 1. Find the
|
|
265
|
-
*
|
|
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'.
|
|
266
416
|
* 2. Extract `status` from FRONTMATTER ONLY via the shared extractFrontmatter
|
|
267
417
|
* parser (DEFECT.FRONTMATTER-SCALAR-BROAD-GREP fix — parser anchors at byte 0).
|
|
268
418
|
* If no frontmatter block or no `status` key → status 'missing'.
|
|
@@ -299,8 +449,12 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
299
449
|
let verificationFile = null;
|
|
300
450
|
try {
|
|
301
451
|
const entries = fsImpl.readdirSync(phaseDir);
|
|
302
|
-
|
|
303
|
-
|
|
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 });
|
|
304
458
|
}
|
|
305
459
|
catch {
|
|
306
460
|
// Directory unreadable → treat as missing
|
|
@@ -374,11 +528,63 @@ function readVerificationStatus(phaseDir, opts = {}) {
|
|
|
374
528
|
const unknownRoute = VERIFICATION_ROUTING_TABLE['unknown'];
|
|
375
529
|
return {
|
|
376
530
|
status: unknownRoute.status,
|
|
377
|
-
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.`,
|
|
378
532
|
next_command: projectNextCommand(unknownRoute.next_command, runtime, phaseArg),
|
|
379
533
|
...(staleCheckIndeterminate ? { staleCheckIndeterminate: true } : {}),
|
|
380
534
|
};
|
|
381
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,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
382
588
|
/**
|
|
383
589
|
* CLI command handler: resolve phaseDir against cwd, call readVerificationStatus,
|
|
384
590
|
* emit via io.output().
|
|
@@ -396,11 +602,55 @@ function cmdVerificationStatus(cwd, phaseDirArg, raw) {
|
|
|
396
602
|
const result = readVerificationStatus(phaseDir, { runtime: (0, runtime_slash_cjs_1.resolveRuntime)(cwd) });
|
|
397
603
|
output(result, raw);
|
|
398
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
|
+
}
|
|
399
645
|
module.exports = {
|
|
400
646
|
VERIFIER_STATUSES,
|
|
401
647
|
VERIFICATION_ROUTING_TABLE,
|
|
402
648
|
defaultPhaseCleanCommitTimesMs,
|
|
649
|
+
resolveVerificationFile,
|
|
650
|
+
resolveUatFile,
|
|
403
651
|
findStaleVerificationSummary,
|
|
404
652
|
readVerificationStatus,
|
|
653
|
+
isPhaseComplete,
|
|
405
654
|
cmdVerificationStatus,
|
|
655
|
+
cmdVerificationResolveFile,
|
|
406
656
|
};
|