@opengsd/gsd-core 1.10.0 → 1.12.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-code-fixer.md +1 -1
- package/agents/gsd-debug-session-manager.md +12 -1
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-synthesizer.md +2 -4
- package/agents/gsd-dom-verifier.md +169 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +22 -14
- package/agents/gsd-framework-selector.md +1 -3
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +5 -3
- package/agents/gsd-pattern-mapper.md +11 -0
- package/agents/gsd-phase-researcher.md +23 -2
- package/agents/gsd-plan-checker.md +50 -53
- package/agents/gsd-planner.md +50 -50
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.md +2 -2
- package/agents/gsd-roadmapper.md +15 -11
- package/agents/gsd-ui-checker.md +63 -4
- package/agents/gsd-ui-researcher.md +41 -3
- package/agents/gsd-user-profiler.md +3 -0
- package/agents/gsd-verifier.md +13 -4
- package/bin/install.js +1448 -1103
- package/commands/gsd/code-review.md +1 -1
- package/commands/gsd/discuss-phase.md +1 -1
- package/commands/gsd/execute-phase.md +1 -1
- package/commands/gsd/import.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 +9 -5
- package/commands/gsd/review-backlog.md +2 -1
- package/commands/gsd/verify-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +1035 -138
- package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
- package/gsd-core/bin/lib/adr-parser.cjs +13 -7
- package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
- package/gsd-core/bin/lib/api-coverage.cjs +33 -14
- package/gsd-core/bin/lib/artifacts.cjs +5 -0
- package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
- package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
- package/gsd-core/bin/lib/audit.cjs +1026 -268
- package/gsd-core/bin/lib/broken-windows.cjs +306 -28
- 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-lock.cjs +10 -4
- package/gsd-core/bin/lib/capability-registry.cjs +845 -130
- package/gsd-core/bin/lib/capability-source.cjs +92 -0
- package/gsd-core/bin/lib/capability-state.cjs +18 -3
- package/gsd-core/bin/lib/capability-trust.cjs +444 -25
- package/gsd-core/bin/lib/capability-validator.cjs +700 -40
- package/gsd-core/bin/lib/capability-writer.cjs +3 -2
- package/gsd-core/bin/lib/check-command-router.cjs +216 -42
- package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
- package/gsd-core/bin/lib/cli-exit.cjs +496 -10
- package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
- package/gsd-core/bin/lib/command-aliases.cjs +22 -0
- package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
- package/gsd-core/bin/lib/command-roster.cjs +44 -1
- package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
- package/gsd-core/bin/lib/commands.cjs +1172 -108
- package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
- package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
- package/gsd-core/bin/lib/config-loader.cjs +187 -23
- package/gsd-core/bin/lib/config.cjs +102 -3
- package/gsd-core/bin/lib/configuration.cjs +129 -37
- package/gsd-core/bin/lib/core-utils.cjs +208 -33
- package/gsd-core/bin/lib/decisions.cjs +23 -0
- package/gsd-core/bin/lib/edge-probe.cjs +9 -1
- package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
- package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
- package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
- package/gsd-core/bin/lib/frontmatter.cjs +899 -229
- package/gsd-core/bin/lib/gap-checker.cjs +95 -10
- package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
- 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 +149 -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 +268 -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 +187 -0
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
- package/gsd-core/bin/lib/host-integration.cjs +39 -6
- package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
- package/gsd-core/bin/lib/init-command-router.cjs +118 -21
- package/gsd-core/bin/lib/init.cjs +439 -168
- package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
- package/gsd-core/bin/lib/install-engine.cjs +811 -259
- package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
- package/gsd-core/bin/lib/install-profiles.cjs +212 -61
- 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-report.cjs +3 -0
- package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
- package/gsd-core/bin/lib/intel.cjs +101 -26
- package/gsd-core/bin/lib/io.cjs +170 -15
- package/gsd-core/bin/lib/learnings.cjs +85 -14
- package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
- package/gsd-core/bin/lib/markdown-table.cjs +183 -22
- package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
- package/gsd-core/bin/lib/milestone.cjs +842 -73
- package/gsd-core/bin/lib/model-catalog.cjs +232 -16
- package/gsd-core/bin/lib/model-resolver.cjs +193 -68
- package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
- package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
- package/gsd-core/bin/lib/pattern.cjs +122 -0
- package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
- package/gsd-core/bin/lib/phase-id.cjs +514 -40
- package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
- package/gsd-core/bin/lib/phase-locator.cjs +262 -34
- package/gsd-core/bin/lib/phase.cjs +1038 -214
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
- package/gsd-core/bin/lib/plan-document.cjs +263 -0
- package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
- package/gsd-core/bin/lib/plan-scan.cjs +98 -3
- package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
- package/gsd-core/bin/lib/planning-scope.cjs +31 -0
- package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
- package/gsd-core/bin/lib/probe-core.cjs +5 -2
- package/gsd-core/bin/lib/profile-output.cjs +1 -1
- package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
- package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
- package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
- 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 +59 -11
- package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
- package/gsd-core/bin/lib/roadmap.cjs +442 -96
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -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 +402 -58
- package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
- package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
- package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
- package/gsd-core/bin/lib/security.cjs +104 -5
- package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
- package/gsd-core/bin/lib/smart-entry.cjs +133 -23
- package/gsd-core/bin/lib/spec-section.cjs +12 -7
- package/gsd-core/bin/lib/state-command-router.cjs +52 -19
- package/gsd-core/bin/lib/state-contract.cjs +359 -0
- package/gsd-core/bin/lib/state-document.cjs +338 -8
- package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
- package/gsd-core/bin/lib/state-transition.cjs +846 -176
- package/gsd-core/bin/lib/state.cjs +2589 -369
- package/gsd-core/bin/lib/surface.cjs +33 -11
- package/gsd-core/bin/lib/task-command-router.cjs +111 -1
- package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
- package/gsd-core/bin/lib/teams-status.cjs +4 -1
- 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 +67 -23
- package/gsd-core/bin/lib/uat.cjs +1761 -167
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
- package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
- package/gsd-core/bin/lib/unusable-input.cjs +37 -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-command-router.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +20 -6
- package/gsd-core/bin/lib/vendor/README.md +75 -0
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -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 +272 -9
- package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
- package/gsd-core/bin/lib/verify.cjs +453 -918
- 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-name-policy.cjs +25 -4
- package/gsd-core/bin/lib/workstream.cjs +2 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
- package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
- package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
- package/gsd-core/bin/shared/exit-codes.json +8 -0
- package/gsd-core/bin/shared/exit-codes.sh +20 -0
- package/gsd-core/bin/shared/model-catalog.json +8 -1
- package/gsd-core/references/agent-contracts.md +44 -26
- package/gsd-core/references/api-coverage.md +24 -2
- package/gsd-core/references/autonomous-smart-discuss.md +3 -3
- package/gsd-core/references/checkpoints.md +39 -21
- package/gsd-core/references/context-budget.md +1 -1
- package/gsd-core/references/decimal-phase-calculation.md +5 -5
- package/gsd-core/references/dispatch-isolation-gate.md +138 -0
- package/gsd-core/references/doc-conflict-engine.md +1 -1
- package/gsd-core/references/edge-probe.md +8 -0
- package/gsd-core/references/execute-mvp-tdd.md +4 -6
- package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
- 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 +17 -11
- package/gsd-core/references/failing-direction.md +78 -0
- package/gsd-core/references/gate-prompts.md +1 -1
- package/gsd-core/references/git-integration.md +5 -5
- package/gsd-core/references/git-planning-commit.md +5 -4
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +61 -2
- package/gsd-core/references/model-profiles.md +12 -4
- package/gsd-core/references/mvp-concepts.md +9 -9
- package/gsd-core/references/nyquist-compliance.md +74 -0
- package/gsd-core/references/offer-next.md +3 -5
- package/gsd-core/references/phase-argument-parsing.md +3 -3
- package/gsd-core/references/planner-failing-direction.md +53 -0
- package/gsd-core/references/planner-guidance.md +3 -9
- package/gsd-core/references/planner-human-verify-mode.md +15 -1
- package/gsd-core/references/planner-preconditions.md +1 -1
- package/gsd-core/references/planner-reviews.md +1 -1
- package/gsd-core/references/planner-revision.md +1 -1
- package/gsd-core/references/planner-verify-command-grounding.md +17 -0
- package/gsd-core/references/planning-config.md +44 -13
- package/gsd-core/references/reviewer-instances.md +31 -0
- package/gsd-core/references/revision-loop.md +1 -1
- package/gsd-core/references/runtime-aware-dispatch.md +1 -1
- package/gsd-core/references/specless-probe-fallback.md +1 -1
- package/gsd-core/references/tdd.md +1 -3
- package/gsd-core/references/ui-brand.md +65 -21
- package/gsd-core/references/ui-consideration-probe.md +1 -1
- package/gsd-core/references/universal-anti-patterns.md +5 -5
- package/gsd-core/references/verifier-phase-gates.md +192 -0
- package/gsd-core/references/verify-command-path-resolvability.md +42 -0
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -17
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/SECURITY.md +3 -3
- package/gsd-core/templates/UI-SPEC.md +25 -3
- package/gsd-core/templates/VALIDATION.md +3 -3
- package/gsd-core/templates/discussion-log.md +1 -1
- package/gsd-core/templates/phase-prompt.md +5 -4
- package/gsd-core/templates/state.md +11 -4
- package/gsd-core/templates/verification-report.md +9 -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 +3 -3
- package/gsd-core/workflows/add-tests.md +3 -8
- package/gsd-core/workflows/add-todo.md +1 -1
- package/gsd-core/workflows/ai-integration-phase.md +13 -20
- package/gsd-core/workflows/audit-fix.md +12 -3
- package/gsd-core/workflows/audit-milestone.md +9 -9
- package/gsd-core/workflows/audit-uat.md +17 -2
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
- package/gsd-core/workflows/autonomous.md +11 -27
- package/gsd-core/workflows/check-todos.md +1 -1
- package/gsd-core/workflows/cleanup.md +64 -5
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
- package/gsd-core/workflows/code-review-fix.md +38 -11
- package/gsd-core/workflows/code-review.md +159 -52
- package/gsd-core/workflows/complete-milestone.md +151 -23
- package/gsd-core/workflows/debug.md +12 -8
- package/gsd-core/workflows/diagnose-issues.md +47 -15
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
- package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +3 -6
- package/gsd-core/workflows/docs-update.md +5 -4
- package/gsd-core/workflows/edit-phase.md +27 -2
- package/gsd-core/workflows/eval-review.md +7 -14
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- 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 +24 -4
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
- package/gsd-core/workflows/execute-phase.md +72 -100
- package/gsd-core/workflows/execute-plan.md +52 -15
- package/gsd-core/workflows/explore.md +131 -4
- package/gsd-core/workflows/extract-learnings.md +1 -1
- package/gsd-core/workflows/fast.md +10 -2
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +5 -5
- package/gsd-core/workflows/health.md +76 -10
- package/gsd-core/workflows/import.md +18 -15
- package/gsd-core/workflows/inbox.md +4 -5
- package/gsd-core/workflows/ingest-docs.md +49 -16
- package/gsd-core/workflows/insert-phase.md +5 -5
- package/gsd-core/workflows/list-seeds.md +5 -3
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +12 -23
- package/gsd-core/workflows/map-codebase.md +1 -1
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +8 -5
- package/gsd-core/workflows/new-milestone.md +22 -29
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +26 -40
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.md +14 -2
- package/gsd-core/workflows/pause-work.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
- package/gsd-core/workflows/plan-phase.md +162 -59
- package/gsd-core/workflows/plan-review-convergence.md +96 -11
- package/gsd-core/workflows/plant-seed.md +2 -2
- package/gsd-core/workflows/pr-branch.md +187 -51
- package/gsd-core/workflows/profile-user.md +16 -14
- package/gsd-core/workflows/progress.md +61 -18
- package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
- package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
- package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
- package/gsd-core/workflows/quick.md +55 -44
- package/gsd-core/workflows/remove-phase.md +4 -4
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +8 -12
- package/gsd-core/workflows/review.md +219 -20
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -3
- package/gsd-core/workflows/session-report.md +2 -1
- package/gsd-core/workflows/settings-advanced.md +7 -9
- package/gsd-core/workflows/settings-integrations.md +64 -31
- package/gsd-core/workflows/settings.md +69 -7
- package/gsd-core/workflows/ship.md +116 -50
- package/gsd-core/workflows/sketch-wrap-up.md +11 -17
- package/gsd-core/workflows/sketch.md +12 -18
- package/gsd-core/workflows/smart-entry.md +3 -5
- package/gsd-core/workflows/spec-phase.md +53 -13
- package/gsd-core/workflows/spike-wrap-up.md +7 -11
- package/gsd-core/workflows/spike.md +20 -31
- package/gsd-core/workflows/stats.md +2 -2
- package/gsd-core/workflows/sync-skills.md +64 -9
- package/gsd-core/workflows/thread.md +11 -7
- package/gsd-core/workflows/transition.md +49 -14
- package/gsd-core/workflows/ui-phase.md +15 -21
- package/gsd-core/workflows/ui-review.md +8 -12
- package/gsd-core/workflows/ultraplan-phase.md +5 -13
- package/gsd-core/workflows/undo.md +8 -16
- package/gsd-core/workflows/update.md +7 -11
- package/gsd-core/workflows/validate-phase.md +3 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +66 -25
- package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
- package/hooks/dist/gsd-check-update-worker.js +56 -13
- package/hooks/dist/gsd-check-update.js +19 -1
- package/hooks/dist/gsd-config-reload.js +18 -12
- package/hooks/dist/gsd-context-monitor.js +19 -10
- package/hooks/dist/gsd-cursor-post-tool.js +3 -1
- package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
- package/hooks/dist/gsd-cursor-session-start.js +2 -1
- package/hooks/dist/gsd-cursor-stop.js +2 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
- package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
- package/hooks/dist/gsd-graphify-update.sh +22 -18
- package/hooks/dist/gsd-node-runner.sh +76 -0
- package/hooks/dist/gsd-phase-boundary.sh +1 -0
- package/hooks/dist/gsd-prompt-guard.js +37 -27
- package/hooks/dist/gsd-read-guard.js +16 -7
- package/hooks/dist/gsd-read-injection-scanner.js +55 -32
- package/hooks/dist/gsd-session-state.sh +1 -0
- package/hooks/dist/gsd-statusline.js +231 -24
- package/hooks/dist/gsd-update-banner.js +22 -1
- package/hooks/dist/gsd-validate-commit.sh +80 -6
- package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
- package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
- package/hooks/dist/gsd-workflow-guard.js +162 -46
- package/hooks/dist/gsd-worktree-path-guard.js +36 -21
- package/hooks/dist/gsd-write-guard.js +35 -25
- package/hooks/dist/lib/cli-exit.js +560 -0
- package/hooks/dist/lib/exit-code-registry.js +98 -0
- package/hooks/dist/lib/git-cmd.js +92 -59
- package/hooks/dist/lib/git-probe.js +84 -0
- package/hooks/dist/lib/hook-exit.js +81 -0
- 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/dist/managed-hooks-registry.cjs +3 -0
- package/hooks/gsd-agent-isolation-guard.js +158 -30
- package/hooks/gsd-check-update-worker.js +56 -13
- package/hooks/gsd-check-update.js +19 -1
- package/hooks/gsd-config-reload.js +18 -12
- package/hooks/gsd-context-monitor.js +19 -10
- package/hooks/gsd-cursor-post-tool.js +3 -1
- package/hooks/gsd-cursor-pre-tool.js +2 -3
- package/hooks/gsd-cursor-session-start.js +2 -1
- package/hooks/gsd-cursor-stop.js +2 -1
- package/hooks/gsd-cursor-subagent-start.js +83 -3
- package/hooks/gsd-cursor-subagent-stop.js +6 -3
- package/hooks/gsd-ensure-canonical-path.js +2 -1
- package/hooks/gsd-graphify-update.sh +22 -18
- package/hooks/gsd-node-runner.sh +76 -0
- package/hooks/gsd-phase-boundary.sh +1 -0
- package/hooks/gsd-prompt-guard.js +37 -27
- package/hooks/gsd-read-guard.js +16 -7
- package/hooks/gsd-read-injection-scanner.js +55 -32
- package/hooks/gsd-session-state.sh +1 -0
- package/hooks/gsd-statusline.js +231 -24
- package/hooks/gsd-update-banner.js +22 -1
- package/hooks/gsd-validate-commit.sh +80 -6
- package/hooks/gsd-windsurf-pre-command.js +16 -11
- package/hooks/gsd-windsurf-pre-write.js +22 -13
- package/hooks/gsd-workflow-guard.js +162 -46
- package/hooks/gsd-worktree-path-guard.js +36 -21
- package/hooks/gsd-write-guard.js +35 -25
- package/hooks/lib/cli-exit.js +560 -0
- package/hooks/lib/exit-code-registry.js +98 -0
- package/hooks/lib/git-cmd.js +92 -59
- package/hooks/lib/git-probe.js +84 -0
- package/hooks/lib/hook-exit.js +81 -0
- 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/hooks/managed-hooks-registry.cjs +3 -0
- package/package.json +28 -11
- package/pi/gsd.cjs +19 -5
- package/scripts/base64-scan.sh +74 -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 +5 -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/check-glossary-refs.cjs +77 -15
- package/scripts/check-mutation-score-ratchet.cjs +156 -0
- package/scripts/ci-check-job-near-cap.cjs +49 -0
- package/scripts/ci-pr-mergeability.cjs +262 -0
- package/scripts/ci-test-scope.cjs +64 -14
- package/scripts/ci-timeout-report.cjs +230 -0
- package/scripts/command-contract-helpers.cjs +903 -1
- package/scripts/docs-guard-registry.cjs +396 -0
- package/scripts/gen-adr-index.cjs +728 -38
- package/scripts/gen-capability-registry.cjs +11 -21
- package/scripts/gen-context-index.cjs +2 -11
- package/scripts/gen-exit-code-docs.cjs +318 -0
- package/scripts/gen-exit-code-registry.cjs +891 -0
- package/scripts/gen-features.cjs +836 -0
- package/scripts/gen-health-docs.cjs +390 -0
- package/scripts/gen-hooks-cli-exit.cjs +239 -0
- package/scripts/gen-install-tree-fixtures.cjs +2 -2
- package/scripts/gen-inventory-manifest.cjs +50 -4
- package/scripts/gen-loop-host-contract.cjs +138 -25
- package/scripts/gen-registry.cjs +3 -14
- package/scripts/gen-scripts-cli-exit.cjs +185 -0
- package/scripts/gen-state-md-docs.cjs +727 -0
- package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
- package/scripts/lib/alias-drift-families.cjs +46 -0
- package/scripts/lib/ci-job-timing.cjs +72 -0
- package/scripts/lib/cli-exit.cjs +546 -44
- package/scripts/lib/drift-scan.cjs +308 -0
- package/scripts/lib/exit-code-registry.cjs +98 -0
- package/scripts/lib/ndjson-reporter.cjs +119 -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-docs-guard-registration.cjs +495 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
- package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
- package/scripts/lint-eslint-glob-coverage.cjs +340 -0
- package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
- package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
- package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
- package/scripts/lint-milestone-window-drift.cjs +468 -0
- package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
- package/scripts/lint-phase-enumeration-drift.cjs +492 -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 +471 -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 +488 -0
- package/scripts/lint-seam-enforcement.cjs +182 -0
- package/scripts/lint-slug-derivation-drift.cjs +921 -0
- package/scripts/lint-source-test-name-collision.cjs +241 -0
- package/scripts/lint-state-field-drift.cjs +805 -0
- package/scripts/lint-state-write-path-drift.cjs +950 -0
- package/scripts/lint-test-file-count.allowlist.json +137 -8
- package/scripts/lint-test-file-count.cjs +25 -3
- package/scripts/lint-unreachable-guard-drift.cjs +830 -0
- package/scripts/lint-vendored-deps.cjs +297 -0
- package/scripts/mutation-matrix.cjs +599 -50
- package/scripts/pr-changed-files.cjs +63 -0
- package/scripts/pr-template-policy.cjs +14 -4
- package/scripts/prompt-injection-scan.sh +100 -14
- package/scripts/require-issue-link-policy.cjs +192 -0
- package/scripts/secret-scan.sh +75 -13
- package/scripts/select-docs-guards.cjs +56 -0
- package/scripts/sync-runtime-launcher.cjs +24 -7
- package/skills/gsd-autonomous/SKILL.md +0 -1
- package/skills/gsd-code-review/SKILL.md +1 -1
- package/skills/gsd-discuss-phase/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -2
- package/skills/gsd-import/SKILL.md +1 -1
- 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 +9 -5
- 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/bin/lib/ui-safety-gate.cjs +0 -107
- 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/lint-emitted-drift-ack.cjs +0 -344
- package/scripts/run-affected-tests.cjs +0 -7
- package/scripts/run-tests.cjs +0 -1051
|
@@ -21,12 +21,19 @@
|
|
|
21
21
|
* - Issue #4 (open-gsd/gsd-core)
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.locateProgressTable = locateProgressTable;
|
|
24
25
|
exports.deriveProgressFromRoadmap = deriveProgressFromRoadmap;
|
|
26
|
+
exports.clampPercentFromFraction = clampPercentFromFraction;
|
|
25
27
|
exports.clampPercent = clampPercent;
|
|
26
28
|
const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
|
|
30
|
+
const phaseIdMod = require("./phase-id.cjs");
|
|
31
|
+
const { isSentinelPhaseId } = phaseIdMod;
|
|
27
32
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
33
|
+
* #3227: the single owner of "where is this ROADMAP's Progress table".
|
|
34
|
+
* Lifted verbatim out of deriveProgressFromRoadmap so `state-contract.cts`
|
|
35
|
+
* enumerates phases from THE SAME table this module derives its counts from.
|
|
36
|
+
* A second copy of this locator is the DEFECT.GENERATIVE-FIX shape.
|
|
30
37
|
*
|
|
31
38
|
* ADR-2143 §3 ("addressed by NAME, never ordinal"): the Progress table is
|
|
32
39
|
* located via the markdown-table seam's `findTableWithColumns`, which is
|
|
@@ -45,6 +52,24 @@ const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
|
45
52
|
* to scanning the whole input, preserving the "Progress table not under a
|
|
46
53
|
* `## Progress` heading, or not the first table in the document, still
|
|
47
54
|
* resolves" behaviour.
|
|
55
|
+
*/
|
|
56
|
+
function locateProgressTable(roadmapContent) {
|
|
57
|
+
const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
|
|
58
|
+
let scoped = roadmapContent;
|
|
59
|
+
if (progressMatch && progressMatch.index !== undefined) {
|
|
60
|
+
const afterHeading = roadmapContent.slice(progressMatch.index);
|
|
61
|
+
const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
|
|
62
|
+
scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
|
|
63
|
+
}
|
|
64
|
+
return (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Derive completed_phases, total_phases, and total_plans from ROADMAP content.
|
|
68
|
+
* Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
|
|
69
|
+
*
|
|
70
|
+
* The Progress table itself is located by `locateProgressTable` (ADR-2143 §3,
|
|
71
|
+
* lifted out as #3227's single-owner extraction) — this function consumes
|
|
72
|
+
* that table.
|
|
48
73
|
*
|
|
49
74
|
* Cells are read by column NAME (`r['Status']`, `r['Plans Complete']`,
|
|
50
75
|
* `r['Phase']`), fixing #2137 (the old position-based regex assumed "Status"
|
|
@@ -63,28 +88,16 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
63
88
|
// `{ ok: false, reason }`, not an exception — so the catch was masking
|
|
64
89
|
// nothing but dead code paths. Removed per ADR-2143 §5; the public
|
|
65
90
|
// `RoadmapProgress` contract (nulls = absent) is unchanged.
|
|
66
|
-
|
|
67
|
-
// ADR-2143 §3: read the Progress table by column NAME (order/injection-invariant),
|
|
68
|
-
// via the markdown-table seam. Scope to the `## Progress` section when present
|
|
69
|
-
// (#2012 decoy avoidance); a headingless milestone slice (#1445) falls back to the
|
|
70
|
-
// whole input. Requires the canonical Phase/Plans Complete/Status/Completed columns
|
|
71
|
-
// in any order (extra columns ignored) — supersedes findTableBySchema's exact-schema lookup.
|
|
72
|
-
const progressMatch = roadmapContent.match(/^##[ \t]+Progress\b/im);
|
|
73
|
-
let scoped = roadmapContent;
|
|
74
|
-
if (progressMatch && progressMatch.index !== undefined) {
|
|
75
|
-
const afterHeading = roadmapContent.slice(progressMatch.index);
|
|
76
|
-
const nextHeading = afterHeading.search(/\n#{1,2}[ \t]/);
|
|
77
|
-
scoped = nextHeading >= 0 ? afterHeading.slice(0, nextHeading) : afterHeading;
|
|
78
|
-
}
|
|
79
|
-
const table = (0, markdown_table_cjs_1.findTableWithColumns)(scoped, ['Phase', 'Plans Complete', 'Status', 'Completed']);
|
|
91
|
+
const table = locateProgressTable(roadmapContent);
|
|
80
92
|
if (table) {
|
|
81
93
|
const allRows = table.rows;
|
|
82
94
|
const completed = allRows.filter((r) => /^complete$/i.test((r['Status'] ?? '').trim())).length;
|
|
83
95
|
completedPhases = completed > 0 ? completed : null;
|
|
84
|
-
// Data rows only (exclude
|
|
96
|
+
// Data rows only (exclude sentinel phases 0 and 999.x).
|
|
97
|
+
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
85
98
|
const dataRows = allRows.filter((r) => {
|
|
86
99
|
const phase = (r['Phase'] ?? '').trim();
|
|
87
|
-
return /^\d/.test(phase) &&
|
|
100
|
+
return /^\d/.test(phase) && !isSentinelPhaseId(phase);
|
|
88
101
|
});
|
|
89
102
|
totalPhases = dataRows.length > 0 ? dataRows.length : null;
|
|
90
103
|
let totalPlansSum = 0;
|
|
@@ -98,12 +111,32 @@ function deriveProgressFromRoadmap(roadmapContent) {
|
|
|
98
111
|
}
|
|
99
112
|
return { completedPhases, totalPhases, totalPlans };
|
|
100
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* Compute progress percent clamped to 100 from an already-computed FRACTION.
|
|
116
|
+
*
|
|
117
|
+
* ADR-3180 Decision 7 (#3180): the completion-RATIO derivation has exactly one
|
|
118
|
+
* owner, and this is its kernel — the single place the `fraction -> integer
|
|
119
|
+
* percent` rounding and the 100 ceiling are expressed. `clampPercent` below is
|
|
120
|
+
* the count-shaped entry point and delegates here; a caller that already holds a
|
|
121
|
+
* fraction (rather than a completed/total pair) calls this directly instead of
|
|
122
|
+
* re-deriving `Math.min(100, Math.round(f * 100))` locally.
|
|
123
|
+
*
|
|
124
|
+
* Enforced by `scripts/lint-completion-ratio-drift.cjs`.
|
|
125
|
+
*/
|
|
126
|
+
function clampPercentFromFraction(fraction) {
|
|
127
|
+
return Math.min(100, Math.round(fraction * 100));
|
|
128
|
+
}
|
|
101
129
|
/**
|
|
102
130
|
* Compute progress percent clamped to 100.
|
|
103
131
|
* Root cause fix for issue #4 — see gen-phase-lifecycle.mjs for full documentation.
|
|
132
|
+
*
|
|
133
|
+
* A non-positive (or absent) denominator yields `0` — "nothing to complete" is
|
|
134
|
+
* reported as 0%, never as 100%. Every `.planning/` completion percentage in this
|
|
135
|
+
* codebase routes through here (ADR-3180 Decision 7); the `total > 0 ? ... : 0`
|
|
136
|
+
* ternary that used to precede each inline copy IS this function's first line.
|
|
104
137
|
*/
|
|
105
138
|
function clampPercent(completed, total) {
|
|
106
139
|
if (!total || total <= 0)
|
|
107
140
|
return 0;
|
|
108
|
-
return
|
|
141
|
+
return clampPercentFromFraction(completed / total);
|
|
109
142
|
}
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Dependencies (leaf modules only — no loadConfig):
|
|
14
14
|
* - node:fs / node:path (stdlib)
|
|
15
|
-
* - ./phase-id.cjs (normalizePhaseName,
|
|
15
|
+
* - ./phase-id.cjs (normalizePhaseName, matchPhaseDirs, phaseNumberForMatch)
|
|
16
16
|
* - ./core-utils.cjs (readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath)
|
|
17
17
|
* - ./planning-workspace.cjs (planningDir)
|
|
18
18
|
*/
|
|
@@ -23,19 +23,25 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
23
23
|
const node_path_1 = __importDefault(require("node:path"));
|
|
24
24
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
25
|
const phaseIdModule = require("./phase-id.cjs");
|
|
26
|
-
const { normalizePhaseName,
|
|
26
|
+
const { normalizePhaseName, matchPhaseDirs, phaseNumberForMatch, isSentinelPhaseId, comparePhaseNum } = phaseIdModule;
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
28
28
|
const coreUtilsModule = require("./core-utils.cjs");
|
|
29
|
-
const { readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath } = coreUtilsModule;
|
|
29
|
+
const { readSubdirectories, getPhaseFileStats, extractCanonicalPlanId, toPosixPath, findUnsummarizedPlans } = coreUtilsModule;
|
|
30
30
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
31
31
|
const planningWorkspace = require("./planning-workspace.cjs");
|
|
32
|
-
const { planningDir } = planningWorkspace;
|
|
32
|
+
const { planningDir, planningRoot } = planningWorkspace;
|
|
33
33
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
34
|
const frontmatterModule = require("./frontmatter.cjs");
|
|
35
35
|
const { extractFrontmatter } = frontmatterModule;
|
|
36
36
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
37
37
|
const planDependencyGraphModule = require("./plan-dependency-graph.cjs");
|
|
38
38
|
const { computeHaltPropagation, buildSummaryFileIndex, isSummaryFileHalted } = planDependencyGraphModule;
|
|
39
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
40
|
+
const roadmapParserModule = require("./roadmap-parser.cjs");
|
|
41
|
+
const { getMilestonePhaseFilter } = roadmapParserModule;
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
43
|
+
const planningScopeMod = require("./planning-scope.cjs");
|
|
44
|
+
const { SCOPE } = planningScopeMod;
|
|
39
45
|
/**
|
|
40
46
|
* #2830: parse a plan file's `depends_on` frontmatter. Returns [] — never
|
|
41
47
|
* throws — on a missing/unreadable/malformed plan or absent field, matching
|
|
@@ -68,33 +74,85 @@ function parsePlanDependsOn(phaseDir, planFile) {
|
|
|
68
74
|
* exactly the shape that let the original #2855 bug (hardcoded root path)
|
|
69
75
|
* exist in one copy and not the other. Sharing this seam means a future
|
|
70
76
|
* change to how the archive tree is located only needs to happen once.
|
|
71
|
-
* Most-recent-milestone-first order
|
|
77
|
+
* Most-recent-milestone-first order, compared numerically segment-by-segment
|
|
78
|
+
* on the version (e.g. `v1.10` before `v1.9`) — NOT lexicographically. A
|
|
79
|
+
* lexicographic `.sort().reverse()` (the prior implementation) ranks `v1.9`
|
|
80
|
+
* ahead of `v1.10` because the string `"1.9"` sorts after `"1.10"`; that is
|
|
81
|
+
* deterministic but wrong for every double-digit-or-higher minor/patch
|
|
82
|
+
* version, and #3458 is what first surfaces archived phases in audit output
|
|
83
|
+
* where the misordering becomes user-visible.
|
|
72
84
|
* Never throws: an absent/unreadable milestones/ dir yields [].
|
|
73
85
|
*/
|
|
74
|
-
function
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
function compareArchiveVersionDesc(aName, bName) {
|
|
87
|
+
const aParts = (aName.match(/^v([\d.]+)-phases$/)?.[1] ?? '').split('.').map(Number);
|
|
88
|
+
const bParts = (bName.match(/^v([\d.]+)-phases$/)?.[1] ?? '').split('.').map(Number);
|
|
89
|
+
const len = Math.max(aParts.length, bParts.length);
|
|
90
|
+
for (let i = 0; i < len; i++) {
|
|
91
|
+
const a = aParts[i] ?? 0;
|
|
92
|
+
const b = bParts[i] ?? 0;
|
|
93
|
+
if (a !== b)
|
|
94
|
+
return b - a; // descending: newest (numerically largest) first
|
|
95
|
+
}
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
function listArchiveVersionDirs(cwd, wsOverride) {
|
|
99
|
+
// #3804: enumerate BOTH archive shapes under the CURRENT SCOPE's milestones
|
|
100
|
+
// tree (planningDir — GSD_WORKSTREAM/GSD_PROJECT-aware, exactly the
|
|
101
|
+
// #2855 scoping findPhaseInternal and getArchivedPhaseDirs rely on):
|
|
102
|
+
// flat: <scope>/milestones/vX.Y-phases/<phase-dir>/
|
|
103
|
+
// workstream archive: <scope>/milestones/ws-<slug>-<date>/phases/<phase-dir>/
|
|
104
|
+
// Pre-#3804 only the flat shape matched, so workstream-archived milestones
|
|
105
|
+
// were invisible (the reporter's repo: 20 hidden phase artifacts). The
|
|
106
|
+
// ws-* shape's phase dirs sit one level deeper (under phases/) and its dir
|
|
107
|
+
// name fails ^v[\d.]+-phases$ — both the name AND the level are modeled.
|
|
108
|
+
// Version labels: flat keeps the bare vX.Y; ws-* shapes carry the dir name
|
|
109
|
+
// (no numeric version). Flat-newest-first (compareArchiveVersionDesc), then
|
|
110
|
+
// ws dirs by name descending — deterministic. The AUDIT's cross-workstream
|
|
111
|
+
// enumeration (audit.cts listAuditPhaseTargets) calls this helper once per
|
|
112
|
+
// tree (root + each workstream) rather than widening this scope — the
|
|
113
|
+
// #2855 no-leak contract for findPhaseInternal/getArchivedPhaseDirs is
|
|
114
|
+
// preserved untouched.
|
|
115
|
+
const milestonesDir = node_path_1.default.join(planningDir(cwd, wsOverride ?? undefined), 'milestones');
|
|
116
|
+
const out = [];
|
|
117
|
+
const seen = new Set();
|
|
118
|
+
const pushVersion = (version, archivePath) => {
|
|
119
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, archivePath));
|
|
120
|
+
if (seen.has(rel))
|
|
121
|
+
return;
|
|
122
|
+
seen.add(rel);
|
|
123
|
+
out.push({ version, archivePath });
|
|
124
|
+
};
|
|
125
|
+
let entries;
|
|
78
126
|
try {
|
|
79
|
-
|
|
80
|
-
return milestoneEntries
|
|
81
|
-
.filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
|
|
82
|
-
.map(e => e.name)
|
|
83
|
-
.sort()
|
|
84
|
-
.reverse()
|
|
85
|
-
.map(archiveName => ({
|
|
86
|
-
version: archiveName.match(/^(v[\d.]+)-phases$/)[1],
|
|
87
|
-
archivePath: node_path_1.default.join(milestonesDir, archiveName),
|
|
88
|
-
}));
|
|
127
|
+
entries = node_fs_1.default.readdirSync(milestonesDir, { withFileTypes: true });
|
|
89
128
|
}
|
|
90
129
|
catch {
|
|
91
130
|
return [];
|
|
92
131
|
}
|
|
132
|
+
const flat = entries
|
|
133
|
+
.filter(e => e.isDirectory() && /^v[\d.]+-phases$/.test(e.name))
|
|
134
|
+
.map(e => e.name)
|
|
135
|
+
.sort(compareArchiveVersionDesc);
|
|
136
|
+
for (const archiveName of flat) {
|
|
137
|
+
pushVersion(archiveName.match(/^(v[\d.]+)-phases$/)[1], node_path_1.default.join(milestonesDir, archiveName));
|
|
138
|
+
}
|
|
139
|
+
const wsDirs = entries
|
|
140
|
+
.filter(e => e.isDirectory() && /^ws-/.test(e.name))
|
|
141
|
+
.map(e => e.name)
|
|
142
|
+
.sort()
|
|
143
|
+
.reverse();
|
|
144
|
+
for (const wsName of wsDirs) {
|
|
145
|
+
pushVersion(wsName, node_path_1.default.join(milestonesDir, wsName, 'phases'));
|
|
146
|
+
}
|
|
147
|
+
return out;
|
|
93
148
|
}
|
|
94
149
|
function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
95
150
|
try {
|
|
96
151
|
const dirs = readSubdirectories(baseDir, true);
|
|
97
|
-
|
|
152
|
+
// #2528: canonical two-pass selection (exact token match, then the
|
|
153
|
+
// bare-integer leading-digit-run fallback) shared with the find-phase and
|
|
154
|
+
// phase-plan-index scans — see phase-id.cts::matchPhaseDirs.
|
|
155
|
+
const { matches, usedBareFallback } = matchPhaseDirs(dirs, normalized);
|
|
98
156
|
if (matches.length === 0)
|
|
99
157
|
return null;
|
|
100
158
|
// #2237: fail loud when multiple directories match the same bare phase
|
|
@@ -121,7 +179,7 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
121
179
|
};
|
|
122
180
|
}
|
|
123
181
|
const match = matches[0];
|
|
124
|
-
const phaseToken =
|
|
182
|
+
const phaseToken = phaseNumberForMatch(match, usedBareFallback);
|
|
125
183
|
const phaseNumber = phaseToken || normalized;
|
|
126
184
|
const afterToken = match.slice(phaseToken ? phaseToken.length : 0).replace(/^-/, '');
|
|
127
185
|
const phaseName = afterToken || null;
|
|
@@ -129,16 +187,13 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
129
187
|
const { plans: unsortedPlans, summaries: unsortedSummaries, hasResearch, hasContext, hasVerification, hasReviews } = getPhaseFileStats(phaseDir);
|
|
130
188
|
const plans = unsortedPlans.sort();
|
|
131
189
|
const summaries = unsortedSummaries.sort();
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
const canonical = extractCanonicalPlanId(p);
|
|
140
|
-
return !completedPlanIds.has(planId) && !completedPlanIds.has(canonical);
|
|
141
|
-
});
|
|
190
|
+
// #3183 (ADR-3180 Decision 2): the summary→plan pairing used to be a
|
|
191
|
+
// bespoke rule local to this function (a third pairing rule alongside
|
|
192
|
+
// scanPhasePlans's completion check and countMatchedSummaries). Routed
|
|
193
|
+
// through the canonical core-utils.findUnsummarizedPlans instead, which
|
|
194
|
+
// shares its `summaryCandidates` matching rule with countMatchedSummaries
|
|
195
|
+
// so the count and this named list can never disagree.
|
|
196
|
+
const incompletePlans = findUnsummarizedPlans(plans, summaries);
|
|
142
197
|
// #2830: reverse lookup from a completed plan's id (exact or canonical) to
|
|
143
198
|
// its actual summary filename. Shared builder (also used by phase.cts's
|
|
144
199
|
// cmdPhasePlanIndex) so the two can never disagree about which summary
|
|
@@ -188,7 +243,12 @@ function searchPhaseInDir(baseDir, relBase, normalized) {
|
|
|
188
243
|
directory: toPosixPath(node_path_1.default.join(relBase, match)),
|
|
189
244
|
phase_number: phaseNumber,
|
|
190
245
|
phase_name: phaseName,
|
|
191
|
-
|
|
246
|
+
// #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
|
|
247
|
+
// (generateSlugInternal, core-utils.cts) rather than re-implementing
|
|
248
|
+
// it. `maxLen: null` preserves this site's pre-migration untruncated
|
|
249
|
+
// contract — the 60-char default would drop an on-disk phase slug's
|
|
250
|
+
// reported value out of sync with the real directory name.
|
|
251
|
+
phase_slug: phaseName ? coreUtilsModule.generateSlugInternal(phaseName, null) : null,
|
|
192
252
|
plans,
|
|
193
253
|
summaries,
|
|
194
254
|
incomplete_plans: incompletePlans,
|
|
@@ -233,13 +293,139 @@ function findPhaseInternal(cwd, phase) {
|
|
|
233
293
|
}
|
|
234
294
|
return null;
|
|
235
295
|
}
|
|
236
|
-
|
|
296
|
+
/**
|
|
297
|
+
* #3185 (epic #3180 Phase 3, ADR-3180 Decision 1 row "Phase enumeration"):
|
|
298
|
+
* the SINGLE canonical owner of "which phase directories belong to the current
|
|
299
|
+
* milestone". Applies the milestone window AND the sentinel filter, in that
|
|
300
|
+
* order, and returns the surviving directory names.
|
|
301
|
+
*
|
|
302
|
+
* Before this existed the derivation had four independent implementations and
|
|
303
|
+
* only `cmdRoadmapAnalyze` carried both halves; `cmdProgressRender`,
|
|
304
|
+
* `cmdStats` and `cmdPhasesList` each carried neither or one.
|
|
305
|
+
*
|
|
306
|
+
* TWO THINGS THIS GETS RIGHT THAT A HEADING-SIDE FILTER CANNOT:
|
|
307
|
+
*
|
|
308
|
+
* 1. The sentinel test runs against DIRECTORY NAMES and is UNCONDITIONAL.
|
|
309
|
+
* `getMilestonePhaseFilter` excludes sentinels from its ROADMAP HEADING
|
|
310
|
+
* set, but when that set is empty it degrades to a literal `() => true`
|
|
311
|
+
* pass-all predicate and never consults the heading set at all — so its
|
|
312
|
+
* own sentinel exclusion becomes unreachable exactly when it is needed,
|
|
313
|
+
* and every directory on disk (backlog included) is reported as a
|
|
314
|
+
* current-milestone phase. That degrade is the #3167 symptom path.
|
|
315
|
+
*
|
|
316
|
+
* 2. The sentinel predicate is the canonical `isSentinelPhaseId`
|
|
317
|
+
* (`src/phase-id.cts`, SENTINEL_RANGES [0, 999]), not a local literal.
|
|
318
|
+
* The rule had five copies and three different regexes before this phase,
|
|
319
|
+
* and they disagreed about Phase 0.
|
|
320
|
+
*
|
|
321
|
+
* The pass-all degrade is narrowed MINIMALLY: it stays over-inclusive for
|
|
322
|
+
* non-sentinel directories, so a project whose window declares no phases
|
|
323
|
+
* still sees its real phase directories. Only sentinels are refused.
|
|
324
|
+
*
|
|
325
|
+
* `scope` distinguishes a REAL empty from a NON-answer (ADR-3180 Decision 2):
|
|
326
|
+
* an absent `phasesDir` is a real empty (a new project genuinely has no
|
|
327
|
+
* phases) and inherits the window's scope, whereas a `phasesDir` that exists
|
|
328
|
+
* but cannot be read is UNREADABLE.
|
|
329
|
+
*
|
|
330
|
+
* `opts.ws` is tri-state, matching `planningDir`'s own contract: `undefined`
|
|
331
|
+
* (the default — do not pass `ws` at all) resolves the AMBIENT workstream
|
|
332
|
+
* from `GSD_WORKSTREAM`; `null` FORCES the project root regardless of any
|
|
333
|
+
* ambient workstream; a string forces that specific workstream.
|
|
334
|
+
*/
|
|
335
|
+
function listMilestonePhaseDirs(phasesDir, opts = {}) {
|
|
336
|
+
// #3597: `ws` must default to `undefined`, NOT `null`. `undefined` means
|
|
337
|
+
// "resolve the ambient workstream" (mirrors planningDir's own contract,
|
|
338
|
+
// src/planning-workspace.cts:124); `null` means "force the project root".
|
|
339
|
+
// Every cwd-bearing caller derives `phasesDir` ambiently (planningPaths(cwd)
|
|
340
|
+
// / planningDir(cwd) with no explicit ws), so defaulting `ws` to `null` here
|
|
341
|
+
// forced the milestone WINDOW to the root ROADMAP while the caller's
|
|
342
|
+
// `phasesDir` stayed workstream-scoped — numerator and denominator drawn
|
|
343
|
+
// from different scoped sets (ADR-3180 §7.6 rule 3 violation). That is what
|
|
344
|
+
// made `--ws <name> progress` read `phase_scope: "unreadable"` and withhold
|
|
345
|
+
// `percent` once `workstream create` migrated the root ROADMAP away.
|
|
346
|
+
const { cwd, ws, versionOverride = null, phaseIdConvention = null } = opts;
|
|
347
|
+
// Without a cwd there is nothing to scope AGAINST — the caller asked for an
|
|
348
|
+
// unscoped read, which is a real answer (mirrors extractCurrentMilestoneScoped's
|
|
349
|
+
// row 1). Sentinels are still refused: they are never milestone phases.
|
|
350
|
+
let inWindow = () => true;
|
|
351
|
+
let scope = SCOPE.COMPLETE;
|
|
352
|
+
if (cwd) {
|
|
353
|
+
const filter = getMilestonePhaseFilter(cwd, versionOverride, phaseIdConvention, ws);
|
|
354
|
+
inWindow = filter;
|
|
355
|
+
scope = filter.scope;
|
|
356
|
+
}
|
|
357
|
+
// An ABSENT phases dir is a real empty, not a failure: a freshly-created
|
|
358
|
+
// project genuinely has no phase directories yet. Distinguishing this from
|
|
359
|
+
// the unreadable case below is the whole point of the scope discriminator.
|
|
360
|
+
if (!node_fs_1.default.existsSync(phasesDir))
|
|
361
|
+
return { value: [], scope };
|
|
362
|
+
let names;
|
|
363
|
+
try {
|
|
364
|
+
names = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true })
|
|
365
|
+
.filter((e) => e.isDirectory())
|
|
366
|
+
.map((e) => e.name);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// The directory EXISTS but could not be read (EACCES/EIO). An empty list
|
|
370
|
+
// here is a NON-answer and must not be reported as "this milestone has no
|
|
371
|
+
// phases" — that collapse is the defect class this epic removes.
|
|
372
|
+
return { value: [], scope: SCOPE.UNREADABLE };
|
|
373
|
+
}
|
|
374
|
+
const value = names
|
|
375
|
+
.filter((name) => inWindow(name) && !isSentinelPhaseId(name, phaseIdConvention ?? undefined))
|
|
376
|
+
.sort((a, b) => comparePhaseNum(a, b));
|
|
377
|
+
return { value, scope };
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* #3882 (ADR-3473 §8.2, issue #3882): the single owner of "the PHYSICAL set
|
|
381
|
+
* of phase directories on disk, entirely un-windowed" — the OTHER axis
|
|
382
|
+
* `listMilestonePhaseDirs` above deliberately does not offer. That owner
|
|
383
|
+
* refuses sentinels UNCONDITIONALLY (see its own doc comment); it has no way
|
|
384
|
+
* to say "physical set, sentinels included". That is exactly what an
|
|
385
|
+
* archival, lookup-index, or health-sweep caller needs — e.g. a heading ->
|
|
386
|
+
* directory lookup index that must resolve a directory regardless of
|
|
387
|
+
* milestone window (`cmdRoadmapAnalyze`'s `_phaseDirNames`,
|
|
388
|
+
* `cmdInitMilestoneOp`'s `diskPhaseDirs`) — which is why those callers used
|
|
389
|
+
* to hand-roll a `readdirSync` instead of calling either owner.
|
|
390
|
+
*
|
|
391
|
+
* `includeSentinels` is REQUIRED, with no default value. #3882/ADR-3473 §8.2:
|
|
392
|
+
* "a caller that wants sentinels asks for them explicitly" — obtaining
|
|
393
|
+
* sentinel-inclusion by silent omission is exactly the defect class this
|
|
394
|
+
* axis exists to close, so the call site is refused at COMPILE TIME without
|
|
395
|
+
* it, not merely documented against it here.
|
|
396
|
+
*
|
|
397
|
+
* Mirrors `listMilestonePhaseDirs`'s own absent/unreadable handling
|
|
398
|
+
* (ADR-3180 Decision 2): an ABSENT `phasesDir` is a real empty (a project
|
|
399
|
+
* with no phase directories yet), `scope: SCOPE.COMPLETE`; a `phasesDir`
|
|
400
|
+
* that EXISTS but cannot be read is a NON-answer, `scope: SCOPE.UNREADABLE`
|
|
401
|
+
* — a caller must not treat that empty list as "this project has no
|
|
402
|
+
* phases."
|
|
403
|
+
*/
|
|
404
|
+
function listAllPhaseDirs(phasesDir, opts) {
|
|
405
|
+
const { includeSentinels, phaseIdConvention = null } = opts;
|
|
406
|
+
if (!node_fs_1.default.existsSync(phasesDir))
|
|
407
|
+
return { value: [], scope: SCOPE.COMPLETE };
|
|
408
|
+
let names;
|
|
409
|
+
try {
|
|
410
|
+
names = node_fs_1.default.readdirSync(phasesDir, { withFileTypes: true })
|
|
411
|
+
.filter((e) => e.isDirectory())
|
|
412
|
+
.map((e) => e.name);
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
return { value: [], scope: SCOPE.UNREADABLE };
|
|
416
|
+
}
|
|
417
|
+
const value = names
|
|
418
|
+
.filter((name) => includeSentinels || !isSentinelPhaseId(name, phaseIdConvention ?? undefined))
|
|
419
|
+
.sort((a, b) => comparePhaseNum(a, b));
|
|
420
|
+
return { value, scope: SCOPE.COMPLETE };
|
|
421
|
+
}
|
|
422
|
+
function getArchivedPhaseDirs(cwd, wsOverride) {
|
|
237
423
|
// #2855: same workstream-scoped resolution as findPhaseInternal above, via
|
|
238
424
|
// the shared listArchiveVersionDirs helper. `phase.list --include-archived`
|
|
239
425
|
// (the primary non-init consumer) must not leak a different workstream's
|
|
240
426
|
// archive either.
|
|
241
427
|
const results = [];
|
|
242
|
-
for (const { version, archivePath } of listArchiveVersionDirs(cwd)) {
|
|
428
|
+
for (const { version, archivePath } of listArchiveVersionDirs(cwd, wsOverride)) {
|
|
243
429
|
const dirs = readSubdirectories(archivePath, true);
|
|
244
430
|
for (const dir of dirs) {
|
|
245
431
|
results.push({
|
|
@@ -252,8 +438,50 @@ function getArchivedPhaseDirs(cwd) {
|
|
|
252
438
|
}
|
|
253
439
|
return results;
|
|
254
440
|
}
|
|
441
|
+
/**
|
|
442
|
+
* #3804 — the CROSS-WORKSTREAM archive enumeration the audit surfaces need.
|
|
443
|
+
* getArchivedPhaseDirs above is deliberately #2855-SCOPED (an ambient
|
|
444
|
+
* workstream must not leak other trees' phases to findPhaseInternal), but
|
|
445
|
+
* audit-uat's charter (#2766: outstanding items do not stop mattering) is
|
|
446
|
+
* cross-workstream: enumerate the project root plus every workstream's own
|
|
447
|
+
* milestones tree, labeling workstream entries '<ws>/<version>' so
|
|
448
|
+
* acknowledge-by-label stays unambiguous. Deduped by full path (the same
|
|
449
|
+
* tree cannot be reached twice, but the guard keeps the invariant explicit).
|
|
450
|
+
*/
|
|
451
|
+
function getAllArchivedPhaseDirs(cwd) {
|
|
452
|
+
const out = [];
|
|
453
|
+
const seen = new Set();
|
|
454
|
+
const collect = (labelPrefix, wsOverride) => {
|
|
455
|
+
for (const archived of getArchivedPhaseDirs(cwd, wsOverride)) {
|
|
456
|
+
const rel = toPosixPath(node_path_1.default.relative(cwd, archived.fullPath));
|
|
457
|
+
if (seen.has(rel))
|
|
458
|
+
continue;
|
|
459
|
+
seen.add(rel);
|
|
460
|
+
out.push({ ...archived, milestone: `${labelPrefix}${archived.milestone}` });
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
collect('', null);
|
|
464
|
+
const workstreamsDir = node_path_1.default.join(planningRoot(cwd), 'workstreams');
|
|
465
|
+
try {
|
|
466
|
+
const wsEntries = node_fs_1.default.readdirSync(workstreamsDir, { withFileTypes: true })
|
|
467
|
+
.filter((e) => e.isDirectory())
|
|
468
|
+
.map((e) => e.name)
|
|
469
|
+
.sort()
|
|
470
|
+
.reverse();
|
|
471
|
+
for (const ws of wsEntries) {
|
|
472
|
+
collect(`${ws}/`, ws);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
/* no workstreams dir — the root pass above already ran */
|
|
477
|
+
}
|
|
478
|
+
return out;
|
|
479
|
+
}
|
|
255
480
|
module.exports = {
|
|
256
481
|
searchPhaseInDir,
|
|
257
482
|
findPhaseInternal,
|
|
258
483
|
getArchivedPhaseDirs,
|
|
484
|
+
getAllArchivedPhaseDirs,
|
|
485
|
+
listMilestonePhaseDirs,
|
|
486
|
+
listAllPhaseDirs,
|
|
259
487
|
};
|