@opengsd/gsd-core 1.14.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.opencode/plugins/gsd-core.js +85 -5
- package/README.ja-JP.md +3 -3
- package/README.ko-KR.md +3 -3
- package/README.pt-BR.md +3 -3
- package/README.zh-CN.md +3 -3
- package/agents/gsd-code-fixer.compact.md +7 -6
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +5 -3
- package/agents/gsd-code-reviewer.md +8 -6
- package/agents/gsd-debug-session-manager.compact.md +17 -2
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +3 -3
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +17 -12
- package/agents/gsd-intel-updater.compact.md +1 -1
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.md +2 -2
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +15 -9
- package/agents/gsd-planner.md +15 -11
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +1 -1
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +21 -30
- package/agents/gsd-ui-auditor.md +166 -37
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +37 -14
- package/bin/install.js +764 -287
- package/commands/gsd/add-tests.md +6 -1
- package/commands/gsd/ai-integration-phase.md +6 -1
- package/commands/gsd/audit-fix.md +5 -0
- package/commands/gsd/audit-milestone.md +6 -1
- package/commands/gsd/autonomous.md +7 -2
- package/commands/gsd/capture.md +9 -5
- package/commands/gsd/code-review.md +7 -2
- package/commands/gsd/complete-milestone.md +4 -0
- package/commands/gsd/config.md +7 -3
- package/commands/gsd/debug.md +11 -7
- package/commands/gsd/discuss-phase.md +7 -3
- package/commands/gsd/docs-update.md +12 -7
- package/commands/gsd/eval-review.md +6 -1
- package/commands/gsd/execute-phase.md +12 -7
- package/commands/gsd/extract-learnings.md +5 -0
- package/commands/gsd/fast.md +4 -0
- package/commands/gsd/forensics.md +5 -1
- package/commands/gsd/graphify.md +10 -6
- package/commands/gsd/health.md +5 -0
- package/commands/gsd/help.md +7 -2
- package/commands/gsd/import.md +7 -3
- package/commands/gsd/inbox.md +5 -0
- package/commands/gsd/ingest-docs.md +5 -1
- package/commands/gsd/manager.md +6 -1
- package/commands/gsd/map-codebase.md +7 -3
- package/commands/gsd/mempalace-capture.md +12 -4
- package/commands/gsd/mempalace-recall.md +5 -1
- package/commands/gsd/milestone-summary.md +5 -1
- package/commands/gsd/mvp-phase.md +8 -3
- package/commands/gsd/new-milestone.md +6 -1
- package/commands/gsd/new-project.md +5 -0
- package/commands/gsd/next.md +6 -1
- package/commands/gsd/ns-context.md +4 -0
- package/commands/gsd/ns-ideate.md +4 -0
- package/commands/gsd/ns-manage.md +4 -0
- package/commands/gsd/ns-project.md +4 -0
- package/commands/gsd/ns-review.md +4 -0
- package/commands/gsd/ns-workflow.md +4 -0
- package/commands/gsd/onboard.md +6 -1
- package/commands/gsd/pause-work.md +5 -1
- package/commands/gsd/phase.md +8 -4
- package/commands/gsd/plan-phase.md +6 -1
- package/commands/gsd/plan-review-convergence.md +11 -7
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +7 -2
- package/commands/gsd/quick-batch.md +21 -9
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +7 -4
- package/commands/gsd/secure-phase.md +6 -1
- package/commands/gsd/ship.md +5 -0
- package/commands/gsd/sketch.md +7 -2
- package/commands/gsd/spec-phase.md +5 -1
- package/commands/gsd/spike.md +8 -3
- package/commands/gsd/surface.md +5 -1
- package/commands/gsd/thread.md +4 -0
- package/commands/gsd/ui-phase.md +6 -1
- package/commands/gsd/ui-review.md +6 -1
- package/commands/gsd/ultraplan-phase.md +5 -1
- package/commands/gsd/undo.md +5 -1
- package/commands/gsd/update.md +6 -2
- package/commands/gsd/validate-phase.md +6 -1
- package/commands/gsd/verify-work.md +6 -1
- package/commands/gsd/workspace.md +7 -3
- package/gsd-core/bin/gsd-tools.cjs +477 -78
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/adr-parser.cjs +3 -1
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +144 -42
- package/gsd-core/bin/lib/broken-windows.cjs +13 -13
- package/gsd-core/bin/lib/capability-activation.cjs +9 -4
- package/gsd-core/bin/lib/capability-registry.cjs +197 -222
- package/gsd-core/bin/lib/capability-validator.cjs +16 -1
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1625
- package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
- package/gsd-core/bin/lib/cli-exit.cjs +12 -0
- package/gsd-core/bin/lib/codex-agent-toml.cjs +32 -33
- package/gsd-core/bin/lib/command-aliases.cjs +7 -0
- package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
- package/gsd-core/bin/lib/commands.cjs +343 -207
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config-loader.cjs +65 -4
- package/gsd-core/bin/lib/config.cjs +76 -19
- package/gsd-core/bin/lib/core-utils.cjs +6 -1
- package/gsd-core/bin/lib/coverage.cjs +4 -8
- package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
- package/gsd-core/bin/lib/decisions.cjs +30 -14
- package/gsd-core/bin/lib/drift.cjs +177 -42
- package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
- package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
- package/gsd-core/bin/lib/frontmatter.cjs +426 -234
- package/gsd-core/bin/lib/gap-checker.cjs +72 -29
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
- package/gsd-core/bin/lib/gate-args.cjs +53 -0
- package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
- package/gsd-core/bin/lib/gate-config.cjs +46 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
- package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
- package/gsd-core/bin/lib/gate-exit.cjs +27 -0
- package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
- package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
- package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
- package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
- package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
- package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
- package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
- package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
- package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
- package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
- package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +364 -132
- package/gsd-core/bin/lib/install-engine.cjs +30 -33
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- package/gsd-core/bin/lib/installer-migrations.cjs +8 -1
- package/gsd-core/bin/lib/io.cjs +122 -3
- package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
- package/gsd-core/bin/lib/milestone.cjs +37 -6
- package/gsd-core/bin/lib/model-resolver.cjs +171 -62
- package/gsd-core/bin/lib/observability/event.cjs +1 -1
- package/gsd-core/bin/lib/observability/logger.cjs +46 -1
- package/gsd-core/bin/lib/pattern.cjs +10 -0
- package/gsd-core/bin/lib/phase-command-router.cjs +20 -5
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
- package/gsd-core/bin/lib/phase-id.cjs +110 -8
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +489 -88
- package/gsd-core/bin/lib/plan-document.cjs +142 -20
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +692 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +60 -9
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +83 -55
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/profile-output.cjs +6 -3
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
- package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
- package/gsd-core/bin/lib/report-parser.cjs +269 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/roadmap-command-router.cjs +25 -19
- package/gsd-core/bin/lib/roadmap-parser.cjs +242 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1653 -65
- package/gsd-core/bin/lib/roadmap.cjs +405 -88
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +373 -187
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +5 -2
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +14 -7
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +522 -624
- package/gsd-core/bin/lib/runtime-name-policy.cjs +246 -21
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +47 -8
- package/gsd-core/bin/lib/smart-entry.cjs +19 -3
- package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
- package/gsd-core/bin/lib/state-contract.cjs +15 -18
- package/gsd-core/bin/lib/state-document.cjs +100 -22
- package/gsd-core/bin/lib/state-transition.cjs +39 -2
- package/gsd-core/bin/lib/state.cjs +256 -105
- package/gsd-core/bin/lib/surface.cjs +19 -2
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -79
- package/gsd-core/bin/lib/uat-predicate.cjs +359 -38
- package/gsd-core/bin/lib/uat.cjs +432 -7
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +167 -40
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/vendor/README.md +31 -9
- package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
- package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
- package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
- package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
- package/gsd-core/bin/lib/verification.cjs +1378 -274
- package/gsd-core/bin/lib/verify-command-grounding.cjs +46 -2
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +357 -531
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
- package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
- package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
- package/gsd-core/bin/lib/workstream.cjs +11 -2
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +784 -51
- package/gsd-core/bin/shared/config-defaults.manifest.json +8 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
- package/gsd-core/references/autonomous-smart-discuss.md +2 -1
- package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
- package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
- package/gsd-core/references/edge-probe.md +195 -21
- package/gsd-core/references/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +10 -6
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/execute-phase-wave-guard.md +22 -11
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/model-profiles.md +1 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/phase-argument-parsing.md +9 -7
- package/gsd-core/references/phase-id-convention.md +28 -0
- package/gsd-core/references/planner-gap-closure.md +2 -0
- package/gsd-core/references/planner-load-graph-context.md +24 -13
- package/gsd-core/references/planner-verify-command-grounding.md +14 -0
- package/gsd-core/references/planning-config.md +12 -3
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +37 -8
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- package/gsd-core/references/worktree-path-safety.md +321 -0
- package/gsd-core/templates/README.md +1 -1
- package/gsd-core/templates/UAT.md +17 -1
- package/gsd-core/templates/config.json +2 -11
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
- package/gsd-core/workflows/add-backlog.md +1 -1
- package/gsd-core/workflows/add-phase.md +8 -7
- package/gsd-core/workflows/add-tests.md +4 -3
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +13 -4
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +4 -3
- package/gsd-core/workflows/audit-uat.md +1 -1
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
- package/gsd-core/workflows/autonomous.md +43 -23
- package/gsd-core/workflows/check-todos.md +7 -6
- package/gsd-core/workflows/cleanup.md +2 -2
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +112 -25
- package/gsd-core/workflows/code-review.md +146 -135
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +13 -8
- package/gsd-core/workflows/debug.md +32 -7
- package/gsd-core/workflows/diagnose-issues.md +3 -2
- package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
- package/gsd-core/workflows/discuss-phase.md +4 -3
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update.md +6 -5
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +14 -5
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1019 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +37 -3
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
- package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +96 -178
- package/gsd-core/workflows/execute-plan.md +18 -23
- package/gsd-core/workflows/explore.md +4 -4
- package/gsd-core/workflows/extract-learnings.md +4 -2
- package/gsd-core/workflows/fast.md +1 -1
- package/gsd-core/workflows/forensics.md +1 -1
- package/gsd-core/workflows/graduation.md +1 -1
- package/gsd-core/workflows/health.md +3 -2
- package/gsd-core/workflows/help/modes/full.compact.md +3 -3
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/import.md +4 -3
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +4 -3
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +6 -4
- package/gsd-core/workflows/map-codebase.md +5 -4
- package/gsd-core/workflows/milestone-summary.md +3 -2
- package/gsd-core/workflows/mvp-phase.md +14 -14
- package/gsd-core/workflows/new-milestone.md +11 -11
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
- package/gsd-core/workflows/new-project.md +7 -7
- package/gsd-core/workflows/new-workspace.md +2 -2
- package/gsd-core/workflows/next.md +1 -1
- package/gsd-core/workflows/note.md +1 -1
- package/gsd-core/workflows/onboard.md +1 -1
- package/gsd-core/workflows/pause-work.md +2 -2
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
- package/gsd-core/workflows/plan-phase.md +50 -24
- package/gsd-core/workflows/plan-review-convergence.md +21 -5
- package/gsd-core/workflows/plant-seed.md +62 -20
- package/gsd-core/workflows/pr-branch.md +113 -13
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +27 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
- package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
- package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +15 -10
- package/gsd-core/workflows/quick.md +62 -39
- package/gsd-core/workflows/reapply-patches.md +9 -3
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +33 -17
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +13 -13
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +4 -4
- package/gsd-core/workflows/ship.md +14 -13
- package/gsd-core/workflows/sketch-wrap-up.md +1 -1
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/smart-entry.md +2 -2
- package/gsd-core/workflows/spec-phase.md +15 -5
- package/gsd-core/workflows/spike-wrap-up.md +1 -1
- package/gsd-core/workflows/spike.md +1 -1
- package/gsd-core/workflows/stats.md +1 -1
- package/gsd-core/workflows/sync-skills.md +5 -5
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +48 -11
- package/gsd-core/workflows/ui-review.md +21 -6
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +7 -7
- package/gsd-core/workflows/validate-phase.md +12 -13
- package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
- package/gsd-core/workflows/verify-work.md +129 -55
- package/hooks/dist/gsd-agent-isolation-guard.js +32 -0
- package/hooks/dist/gsd-check-update-worker.js +8 -0
- package/hooks/dist/gsd-check-update.js +8 -0
- package/hooks/dist/gsd-context-monitor.js +31 -8
- package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
- package/hooks/dist/gsd-secret-read-guard.js +161 -4
- package/hooks/dist/gsd-statusline.js +85 -20
- package/hooks/dist/gsd-update-banner.js +8 -0
- package/hooks/dist/gsd-validate-commit.sh +63 -4
- package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
- package/hooks/dist/gsd-workflow-guard.js +5 -4
- package/hooks/dist/gsd-worktree-path-guard.js +6 -2
- package/hooks/dist/lib/cli-exit.js +12 -0
- package/hooks/dist/lib/git-probe.js +17 -1
- package/hooks/dist/lib/isolation-sentinel.js +2 -2
- package/hooks/gsd-agent-isolation-guard.js +32 -0
- package/hooks/gsd-check-update-worker.js +8 -0
- package/hooks/gsd-check-update.js +8 -0
- package/hooks/gsd-context-monitor.js +31 -8
- package/hooks/gsd-cursor-subagent-start.js +8 -0
- package/hooks/gsd-secret-read-guard.js +161 -4
- package/hooks/gsd-statusline.js +85 -20
- package/hooks/gsd-update-banner.js +8 -0
- package/hooks/gsd-validate-commit.sh +63 -4
- package/hooks/gsd-windsurf-pre-write.js +11 -2
- package/hooks/gsd-workflow-guard.js +5 -4
- package/hooks/gsd-worktree-path-guard.js +6 -2
- package/hooks/hooks.json +5 -5
- package/hooks/lib/cli-exit.js +12 -0
- package/hooks/lib/git-probe.js +17 -1
- package/hooks/lib/isolation-sentinel.js +2 -2
- package/package.json +22 -4
- package/scripts/build-hooks.js +15 -6
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/check-contract-drift.cjs +127 -11
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +15 -8
- package/scripts/docs-guard-registry.cjs +34 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +79 -1
- package/scripts/gen-platform-conformance-tier.cjs +187 -1
- package/scripts/gen-plugin-skills.cjs +87 -1
- package/scripts/gen-research-agents.cjs +24 -31
- package/scripts/gen-scripts-cli-exit.cjs +30 -3
- package/scripts/gen-test-timings.cjs +32 -7
- package/scripts/lib/cli-exit.cjs +12 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +34 -2
- package/scripts/lib/ndjson-reporter.cjs +31 -5
- package/scripts/lib/platform-conformance-tier.generated.cjs +45 -5
- package/scripts/lib/registration-ledger-preload.cjs +155 -0
- package/scripts/lib/vendor-bundle.cjs +59 -0
- package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
- package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
- package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
- package/scripts/lint-completion-predicate-drift.cjs +18 -19
- package/scripts/lint-descriptions.cjs +7 -3
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +38 -1
- package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
- package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
- package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +319 -5
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +38 -9
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +25 -10
- package/scripts/mutation-matrix.cjs +50 -5
- package/scripts/prompt-injection-scan.sh +4 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/require-issue-link-policy.cjs +6 -2
- package/scripts/sync-runtime-launcher.cjs +184 -2
- package/scripts/verify-npm-publish.cjs +76 -20
- package/skills/gsd-add-tests/SKILL.md +6 -1
- package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
- package/skills/gsd-audit-fix/SKILL.md +5 -0
- package/skills/gsd-audit-milestone/SKILL.md +6 -1
- package/skills/gsd-autonomous/SKILL.md +7 -2
- package/skills/gsd-capture/SKILL.md +9 -5
- package/skills/gsd-code-review/SKILL.md +7 -2
- package/skills/gsd-complete-milestone/SKILL.md +4 -0
- package/skills/gsd-config/SKILL.md +8 -4
- package/skills/gsd-debug/SKILL.md +11 -7
- package/skills/gsd-discuss-phase/SKILL.md +7 -3
- package/skills/gsd-docs-update/SKILL.md +12 -7
- package/skills/gsd-eval-review/SKILL.md +6 -1
- package/skills/gsd-execute-phase/SKILL.md +12 -7
- package/skills/gsd-extract-learnings/SKILL.md +5 -0
- package/skills/gsd-fast/SKILL.md +4 -0
- package/skills/gsd-forensics/SKILL.md +5 -1
- package/skills/gsd-graphify/SKILL.md +10 -6
- package/skills/gsd-health/SKILL.md +5 -0
- package/skills/gsd-help/SKILL.md +7 -2
- package/skills/gsd-import/SKILL.md +7 -3
- package/skills/gsd-inbox/SKILL.md +5 -0
- package/skills/gsd-ingest-docs/SKILL.md +5 -1
- package/skills/gsd-manager/SKILL.md +6 -1
- package/skills/gsd-map-codebase/SKILL.md +7 -3
- package/skills/gsd-mempalace-capture/SKILL.md +12 -4
- package/skills/gsd-mempalace-recall/SKILL.md +5 -1
- package/skills/gsd-milestone-summary/SKILL.md +5 -1
- package/skills/gsd-mvp-phase/SKILL.md +8 -3
- package/skills/gsd-new-milestone/SKILL.md +6 -1
- package/skills/gsd-new-project/SKILL.md +5 -0
- package/skills/gsd-next/SKILL.md +6 -1
- package/skills/gsd-ns-context/SKILL.md +4 -0
- package/skills/gsd-ns-ideate/SKILL.md +4 -0
- package/skills/gsd-ns-manage/SKILL.md +4 -0
- package/skills/gsd-ns-project/SKILL.md +4 -0
- package/skills/gsd-ns-review/SKILL.md +4 -0
- package/skills/gsd-ns-workflow/SKILL.md +4 -0
- package/skills/gsd-onboard/SKILL.md +6 -1
- package/skills/gsd-pause-work/SKILL.md +5 -1
- package/skills/gsd-phase/SKILL.md +8 -4
- package/skills/gsd-plan-phase/SKILL.md +6 -1
- package/skills/gsd-plan-review-convergence/SKILL.md +10 -6
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +7 -2
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +21 -9
- package/skills/gsd-review/SKILL.md +7 -4
- package/skills/gsd-review-backlog/SKILL.md +3 -2
- package/skills/gsd-secure-phase/SKILL.md +6 -1
- package/skills/gsd-ship/SKILL.md +5 -0
- package/skills/gsd-sketch/SKILL.md +7 -2
- package/skills/gsd-spec-phase/SKILL.md +5 -1
- package/skills/gsd-spike/SKILL.md +8 -3
- package/skills/gsd-surface/SKILL.md +5 -1
- package/skills/gsd-thread/SKILL.md +4 -0
- package/skills/gsd-ui-phase/SKILL.md +6 -1
- package/skills/gsd-ui-review/SKILL.md +6 -1
- package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
- package/skills/gsd-undo/SKILL.md +5 -1
- package/skills/gsd-update/SKILL.md +6 -2
- package/skills/gsd-validate-phase/SKILL.md +6 -1
- package/skills/gsd-verify-work/SKILL.md +6 -1
- package/skills/gsd-workspace/SKILL.md +7 -3
- package/skills/gsd-workstreams/SKILL.md +6 -6
- package/vscode/package.json +1 -1
|
@@ -41,17 +41,24 @@ const { SCOPE } = planningScopeMod;
|
|
|
41
41
|
const secrets_cjs_1 = require("./secrets.cjs");
|
|
42
42
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-scan.cjs is an export= CommonJS module
|
|
43
43
|
const scanPhasePlans = require("./plan-scan.cjs");
|
|
44
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-document.cjs is an export= CommonJS module
|
|
45
|
+
const planDocument = require("./plan-document.cjs");
|
|
44
46
|
const state_document_cjs_1 = require("./state-document.cjs");
|
|
45
47
|
const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
46
48
|
const host_runtime_detection_cjs_1 = require("./host-runtime-detection.cjs");
|
|
47
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports -- commands.cjs is an export= CommonJS module
|
|
48
|
-
const commandsMod = require("./commands.cjs");
|
|
49
49
|
const security_cjs_1 = require("./security.cjs");
|
|
50
50
|
const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
|
|
51
51
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
|
|
52
52
|
const frontmatterMod = require("./frontmatter.cjs");
|
|
53
53
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- verification.cjs is an export= CommonJS module
|
|
54
54
|
const verificationMod = require("./verification.cjs");
|
|
55
|
+
// #5060: Phase Status Module — the single owner of "what state is phase P
|
|
56
|
+
// in?" (ADR-5057 §1/§2). Replaces this file's own determinePhaseStatus/
|
|
57
|
+
// projectCompletionStatus derivations, and cmdInitMilestoneOp's
|
|
58
|
+
// summary-count-only completion check (the former listPhaseSummaryFiles
|
|
59
|
+
// helper, since removed). listPhasePlanFiles (below) is unrelated — it backs
|
|
60
|
+
// only the STATE.md disk-plan-count validation, not a completion verdict.
|
|
61
|
+
const phase_status_cjs_1 = require("./phase-status.cjs");
|
|
55
62
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- uat-predicate.cjs is an export= CommonJS module
|
|
56
63
|
const uatPredicateMod = require("./uat-predicate.cjs");
|
|
57
64
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- agent-install-check.cjs is an export= CommonJS module
|
|
@@ -87,12 +94,11 @@ const { resolveModelInternal, resolveGranularityInternal, assertValidGranularity
|
|
|
87
94
|
const { findPhaseInternal, listMilestonePhaseDirs, listAllPhaseDirs } = phaseLocator;
|
|
88
95
|
const { getRoadmapPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, } = roadmapParser;
|
|
89
96
|
const { pathExistsInternal, generateSlugInternal, toPosixPath } = coreUtils;
|
|
90
|
-
const { comparePhaseNum, normalizePhaseName,
|
|
97
|
+
const { comparePhaseNum, normalizePhaseName, stripProjectCodePrefix, PHASE_NUMBER_TOKEN_SOURCE, PHASE_DEP_REF_SOURCE, isForeignPrefixedPhaseQuery, isSentinelPhaseId, extractPhaseToken, scopeToPhase, renderPhaseBranchName, parsePhaseId, renderPhaseId, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, buildPhaseHeadingScanRegex, } = phaseId;
|
|
91
98
|
const { pruneOrphanedWorktrees } = worktreeSafety;
|
|
92
|
-
const { planningPaths, planningDir, planningRoot, todosDir, listAvailableWorkstreams, peekActiveWorkstream, diagnoseUnresolvedActiveWorkstream, describeUnresolvedWorkstreamReason, findContextMdIn, } = planningWorkspace;
|
|
93
|
-
const {
|
|
94
|
-
const {
|
|
95
|
-
const { isPhaseComplete, resolveVerificationFile, resolveUatFile } = verificationMod;
|
|
99
|
+
const { planningPaths, planningDir, planningRoot, todosDir, listAvailableWorkstreams, peekActiveWorkstream, resolveEnvWorkstream, diagnoseUnresolvedActiveWorkstream, describeUnresolvedWorkstreamReason, findContextMdIn, resolvePhaseIdConvention, worktreesOptedOut, } = planningWorkspace;
|
|
100
|
+
const { extractFrontmatter, frontmatterBlock } = frontmatterMod;
|
|
101
|
+
const { isPhaseComplete, resolveVerificationFile, resolveUatFile, VERIFICATION_STATUS, VerificationStatusError } = verificationMod;
|
|
96
102
|
const { evaluateUatPassed } = uatPredicateMod;
|
|
97
103
|
const { resolveLoopHooks } = loopResolverMod;
|
|
98
104
|
const { loadRegistry } = capabilityLoaderMod;
|
|
@@ -100,7 +106,6 @@ const { resolveCapabilityRuntimeState } = capabilityStateMod;
|
|
|
100
106
|
// Unused but imported for structural parity
|
|
101
107
|
void stripShippedMilestones;
|
|
102
108
|
// Accept all bold/colon variants of the Requirements header (#2769)
|
|
103
|
-
const REQUIREMENTS_HEADER_RE = /^\*\*Requirements:?\*\*[^\S\n]*:?[^\S\n]*([^\n]*)$/m;
|
|
104
109
|
// #2056/#2104: isForeignPrefixedPhaseQuery is imported from phase-id.cts
|
|
105
110
|
// (the canonical predicate). parsePhasePrefix is no longer needed locally.
|
|
106
111
|
// phaseInfoMatchesExactPrefix and roadmapPhaseMatchesExactPrefix are local
|
|
@@ -110,9 +115,18 @@ function phaseInfoMatchesExactPrefix(phaseInfo, phase) {
|
|
|
110
115
|
const numStr = typeof num === 'string' ? num : (typeof num === 'number' ? String(num) : '');
|
|
111
116
|
return numStr.toUpperCase() === phase.toUpperCase();
|
|
112
117
|
}
|
|
118
|
+
// #4906 Phase 5 (#4984): NOT migrated onto the heading-baseline selector —
|
|
119
|
+
// this site was never one of the five ADR-4910 §8 call sites this phase owns
|
|
120
|
+
// (init milestone/progress heading scans + milestone.cts's unstarted-phase
|
|
121
|
+
// guard, all via buildPhaseHeadingScanRegex), and adding a NEW direct
|
|
122
|
+
// any-bracket selector consumer here inflated init.cts's
|
|
123
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census past its pinned
|
|
124
|
+
// count. Left bracket-blind deliberately, matching this call's pre-#4984
|
|
125
|
+
// behavior; folding it into the census is Phase 6 work.
|
|
113
126
|
function roadmapPhaseMatchesExactPrefix(roadmapPhase, phase) {
|
|
114
127
|
const sectionRaw = roadmapPhase?.['section'];
|
|
115
128
|
const section = typeof sectionRaw === 'string' ? sectionRaw : '';
|
|
129
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above the function.
|
|
116
130
|
return new RegExp(`^#{2,4}\\s*Phase\\s+${(0, pattern_cjs_1.escapeRegex)(phase)}(?:\\b|\\s|:)`, 'i').test(section);
|
|
117
131
|
}
|
|
118
132
|
// #2104: shared helpers that wrap findPhaseInternal / getRoadmapPhaseInternal
|
|
@@ -176,20 +190,10 @@ function applyRoadmapFallback(phaseInfo, roadmapPhase, buildFallback) {
|
|
|
176
190
|
}
|
|
177
191
|
return phaseInfo;
|
|
178
192
|
}
|
|
179
|
-
function listPhaseSummaryFiles(phaseDir) {
|
|
180
|
-
return scanPhasePlans(phaseDir)['summaryFiles'];
|
|
181
|
-
}
|
|
182
193
|
function listPhasePlanFiles(phaseDir) {
|
|
183
194
|
return scanPhasePlans(phaseDir)['planFiles'];
|
|
184
195
|
}
|
|
185
|
-
function
|
|
186
|
-
if (phaseComplete)
|
|
187
|
-
return 'complete';
|
|
188
|
-
if (implementationComplete)
|
|
189
|
-
return 'executed';
|
|
190
|
-
return 'incomplete';
|
|
191
|
-
}
|
|
192
|
-
function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, summaryCount, slashRuntime) {
|
|
196
|
+
function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, summaryCount, slashRuntime, convention) {
|
|
193
197
|
// ADR-3180 §7.4 (issue #3186) / DO-NOT-MIGRATE exemption
|
|
194
198
|
// (scripts/lint-completion-predicate-drift.cjs FUNCTION_SCOPED_EXEMPTIONS,
|
|
195
199
|
// declared deviation): `implementation_complete` answers "are the plans
|
|
@@ -209,20 +213,35 @@ function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, s
|
|
|
209
213
|
// #2617: the router still owns both the message content and the runtime
|
|
210
214
|
// projection; init passes the phase number it already knows (its phaseDir
|
|
211
215
|
// is unresolved in some branches, where the router could not derive one).
|
|
212
|
-
const completionResult = isPhaseComplete(phaseFullDir, { runtime: slashRuntime, phaseNumber });
|
|
216
|
+
const completionResult = isPhaseComplete(phaseFullDir, { runtime: slashRuntime, phaseNumber, convention });
|
|
217
|
+
// #5118: the owner carried an out-of-set report status in its result
|
|
218
|
+
// (`statusError`, `verification.status: null`); an init bundle is never
|
|
219
|
+
// assembled over a report the owner refused — the carried error is raised
|
|
220
|
+
// here and every `init *` surface built on this projection fails with the
|
|
221
|
+
// error's own reason (the CLI entry seam), printing nothing.
|
|
222
|
+
if (completionResult.value.statusError)
|
|
223
|
+
throw completionResult.value.statusError;
|
|
213
224
|
const verificationStatus = completionResult.value.verification;
|
|
214
225
|
const projectedVerificationStatus = verificationStatus.status;
|
|
215
226
|
const projectedVerificationAction = verificationStatus.next_action;
|
|
216
|
-
const verificationPassed = projectedVerificationStatus ===
|
|
227
|
+
const verificationPassed = projectedVerificationStatus === VERIFICATION_STATUS.PASSED;
|
|
217
228
|
const phaseComplete = completionResult.value.complete;
|
|
218
229
|
return {
|
|
219
230
|
implementation_complete: implementationComplete,
|
|
220
231
|
verification_status: projectedVerificationStatus,
|
|
221
232
|
verification_passed: verificationPassed,
|
|
222
233
|
phase_complete: phaseComplete,
|
|
223
|
-
|
|
234
|
+
// #5060: Phase Status Module — the single owner of "what state is phase P
|
|
235
|
+
// in?" (ADR-5057 §1/§2).
|
|
236
|
+
completion_status: (0, phase_status_cjs_1.toCompletionStatus)((0, phase_status_cjs_1.phaseStatusFromFacts)({
|
|
237
|
+
planCount,
|
|
238
|
+
summaryCount,
|
|
239
|
+
complete: phaseComplete,
|
|
240
|
+
verificationStatus: projectedVerificationStatus,
|
|
241
|
+
})),
|
|
224
242
|
verification_next_action: projectedVerificationAction,
|
|
225
243
|
verification_next_command: verificationStatus.next_command,
|
|
244
|
+
verification_route: verificationStatus.route,
|
|
226
245
|
// #3057 B3: readVerificationStatus's result carries this flag when its
|
|
227
246
|
// internal staleness check could not run to completion.
|
|
228
247
|
verification_stale_check_indeterminate: 'staleCheckIndeterminate' in verificationStatus
|
|
@@ -469,7 +488,8 @@ function detectHasPriorPhases(cwd, phaseInfo) {
|
|
|
469
488
|
* `false` — strict `=== true`, never coerced, mirrors `detectHasPriorPhases`'s
|
|
470
489
|
* degrade-to-false discipline. `keyPath` is always a fixed literal supplied
|
|
471
490
|
* by this module, never attacker/user input, so a plain bracket traversal
|
|
472
|
-
* carries no prototype hazard here.
|
|
491
|
+
* carries no prototype hazard here. Only for keys whose default is OFF — a
|
|
492
|
+
* default-ON key read here silently inverts its unset value (#4977).
|
|
473
493
|
*/
|
|
474
494
|
function readConfigJsonBoolean(cwd, keyPath) {
|
|
475
495
|
try {
|
|
@@ -584,6 +604,13 @@ function detectPhaseMvpMode(cwd, phaseNumber) {
|
|
|
584
604
|
const rawContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
585
605
|
const content = extractCurrentMilestone(rawContent, cwd);
|
|
586
606
|
const escapedPhase = (0, pattern_cjs_1.escapeRegex)(phaseNumber);
|
|
607
|
+
// #4906 Phase 5 (#4984): NOT migrated onto phaseHeadingPrefixSrcFor — same
|
|
608
|
+
// out-of-scope reasoning as roadmapPhaseMatchesExactPrefix above (only the
|
|
609
|
+
// five buildPhaseHeadingScanRegex sites in init.cts/milestone.cts are this
|
|
610
|
+
// phase's owned migration); a direct selector call here would have added
|
|
611
|
+
// a fourth uncounted ANY_BRACKET consumer to init.cts's pinned
|
|
612
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census.
|
|
613
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
587
614
|
const phaseHeader = new RegExp(`#{2,4}\\s*Phase\\s+${escapedPhase}(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:`, 'i');
|
|
588
615
|
const headerMatch = content.match(phaseHeader);
|
|
589
616
|
if (!headerMatch || headerMatch.index === undefined)
|
|
@@ -710,7 +737,13 @@ function buildSectionManifestField(cwd, phaseInfo, options, workflow, overrides
|
|
|
710
737
|
flags,
|
|
711
738
|
phaseNumber,
|
|
712
739
|
hasPriorPhases: detectHasPriorPhases(cwd, phaseInfo),
|
|
713
|
-
|
|
740
|
+
// #4977: `workflow.use_worktrees` defaults ON, so this fact must come from
|
|
741
|
+
// the #3972 owner `query dispatch-isolation` and the isolation guard
|
|
742
|
+
// already share — not `readConfigJsonBoolean`, whose absent ⇒ false
|
|
743
|
+
// polarity (right for the opt-in keys around it) excluded the quick
|
|
744
|
+
// pre-dispatch plan commit while the executor was still dispatched
|
|
745
|
+
// isolated. The ladder also brings root→workstream inheritance along.
|
|
746
|
+
worktreesEnabled: !worktreesOptedOut(cwd),
|
|
714
747
|
phaseMvpMode: detectPhaseMvpMode(cwd, phaseNumber),
|
|
715
748
|
needsCodebaseMap: overrides.needsCodebaseMap,
|
|
716
749
|
chunkedMode,
|
|
@@ -751,6 +784,54 @@ function buildSectionManifestField(cwd, phaseInfo, options, workflow, overrides
|
|
|
751
784
|
function milestoneRecord(cwd) {
|
|
752
785
|
return (getMilestoneInfo(cwd).value ?? {});
|
|
753
786
|
}
|
|
787
|
+
/**
|
|
788
|
+
* #4683 — threat IDs claimed by more than one of the phase's live PLAN files.
|
|
789
|
+
* `scanPhasePlans().planFiles` is the right input set twice over: it excludes
|
|
790
|
+
* derivative files (OUTLINE / PLAN-REVIEW / pre-bounce) AND `status: superseded`
|
|
791
|
+
* plans (#2349) — a superseded plan's IDs were deliberately reassigned to its
|
|
792
|
+
* replacement, so they must not hold against it. The per-document row parse is
|
|
793
|
+
* planDocument.extractThreatRegisterIds; only `<threat_model>` register rows
|
|
794
|
+
* count, and the reserved `T-{phase}-SC` shape is excluded there by grammar
|
|
795
|
+
* (every plan keeps that row by design). A missing/unreadable phase directory
|
|
796
|
+
* degrades to "no duplicates" — planning a brand-new phase has nothing to
|
|
797
|
+
* collide with.
|
|
798
|
+
*/
|
|
799
|
+
function findDuplicateThreatIds(cwd, phaseDirRel) {
|
|
800
|
+
if (!phaseDirRel)
|
|
801
|
+
return [];
|
|
802
|
+
const phaseDir = node_path_1.default.join(cwd, phaseDirRel);
|
|
803
|
+
let planFiles;
|
|
804
|
+
try {
|
|
805
|
+
planFiles = scanPhasePlans(phaseDir).planFiles;
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
return [];
|
|
809
|
+
}
|
|
810
|
+
const owners = new Map();
|
|
811
|
+
for (const planFile of planFiles) {
|
|
812
|
+
let content;
|
|
813
|
+
try {
|
|
814
|
+
content = node_fs_1.default.readFileSync(node_path_1.default.join(phaseDir, planFile), 'utf-8');
|
|
815
|
+
}
|
|
816
|
+
catch {
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
for (const id of planDocument.extractThreatRegisterIds(content)) {
|
|
820
|
+
const claimed = owners.get(id);
|
|
821
|
+
if (claimed) {
|
|
822
|
+
if (!claimed.includes(planFile))
|
|
823
|
+
claimed.push(planFile);
|
|
824
|
+
}
|
|
825
|
+
else {
|
|
826
|
+
owners.set(id, [planFile]);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
return [...owners.entries()]
|
|
831
|
+
.filter(([, claims]) => claims.length > 1)
|
|
832
|
+
.map(([id, plans]) => ({ id, plans: [...plans].sort() }))
|
|
833
|
+
.sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
834
|
+
}
|
|
754
835
|
function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
755
836
|
if (!phase) {
|
|
756
837
|
error('phase required for init execute-phase');
|
|
@@ -788,9 +869,14 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
|
788
869
|
has_reviews: false,
|
|
789
870
|
};
|
|
790
871
|
});
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
872
|
+
// #4731: multiline-aware — the Requirements field may hard-wrap, so the
|
|
873
|
+
// value is extracted past the line break before the ID scan.
|
|
874
|
+
const phaseSection = roadmapPhase?.['section'];
|
|
875
|
+
const reqLine = phaseSection
|
|
876
|
+
? roadmapParser.extractPhaseFieldMultiline(phaseSection, 'Requirements')
|
|
877
|
+
: null;
|
|
878
|
+
const reqExtracted = reqLine
|
|
879
|
+
? reqLine.replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
|
794
880
|
: null;
|
|
795
881
|
const phase_req_ids = reqExtracted && reqExtracted !== 'TBD' ? reqExtracted : null;
|
|
796
882
|
// #3188: these paths are null when the file is absent, matching the contract
|
|
@@ -800,6 +886,8 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
|
800
886
|
const statePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
|
|
801
887
|
const roadmapPath = node_path_1.default.join(planningDir(cwd), 'ROADMAP.md');
|
|
802
888
|
const requirementsPath = node_path_1.default.join(planningDir(cwd), 'REQUIREMENTS.md');
|
|
889
|
+
// #4683: computed once — see the threat_id_duplicates fields in the payload.
|
|
890
|
+
const threatIdDuplicates = findDuplicateThreatIds(cwd, phaseInfo?.['directory']);
|
|
803
891
|
const result = {
|
|
804
892
|
executor_model: resolveModelInternal(cwd, 'gsd-executor'),
|
|
805
893
|
verifier_model: resolveModelInternal(cwd, 'gsd-verifier'),
|
|
@@ -819,6 +907,13 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
|
819
907
|
? toPosixPath(node_path_1.default.join(cwd, phaseInfo['directory']))
|
|
820
908
|
: null,
|
|
821
909
|
phase_number: phaseInfo?.['phase_number'] || null,
|
|
910
|
+
// #4748: the disk path hands back the directory's padded number (`03A`)
|
|
911
|
+
// but the ROADMAP fallback above hands back the heading's bare one (`3A`),
|
|
912
|
+
// and execute-phase.md's review lookup needs the padded form for
|
|
913
|
+
// `{PADDED}-REVIEW.md`. It used to re-pad in shell with `printf "%02d"`,
|
|
914
|
+
// which cannot pad a letter id and reads an already-padded `08` as octal.
|
|
915
|
+
// Emit the canonical normalization, as the plan-phase/code-review inits do.
|
|
916
|
+
padded_phase: phaseInfo?.['phase_number'] ? normalizePhaseName(phaseInfo['phase_number']) : null,
|
|
822
917
|
// #3171: prefer the ROADMAP's curated display name for `phase_name`. When
|
|
823
918
|
// the phase directory already exists on disk, the disk-lookup path
|
|
824
919
|
// (searchPhaseInDir) derives phase_name from the directory-name remainder
|
|
@@ -837,6 +932,12 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
|
837
932
|
incomplete_plans: phaseInfo?.['incomplete_plans'] || [],
|
|
838
933
|
plan_count: phaseInfo?.['plans']?.length || 0,
|
|
839
934
|
incomplete_count: phaseInfo?.['incomplete_plans']?.length || 0,
|
|
935
|
+
// #4683: cross-plan threat-ID collisions (gap-closure plans renumbering
|
|
936
|
+
// from T-{phase}-01 again). execute-phase.md hard-stops on a non-empty
|
|
937
|
+
// list BEFORE any dispatch — SECURITY.md rows and VALIDATION.md's Threat
|
|
938
|
+
// Ref column key on this ID, so a reused ID is ambiguous downstream.
|
|
939
|
+
threat_id_duplicates: threatIdDuplicates,
|
|
940
|
+
threat_id_duplicate_count: threatIdDuplicates.length,
|
|
840
941
|
// #2830: the halt-aware view, forwarded from the shared computation in
|
|
841
942
|
// phase-locator. Additive — `incomplete_plans`/`incomplete_count` above keep
|
|
842
943
|
// their exact name, type and semantics. Without this passthrough the shared
|
|
@@ -930,9 +1031,14 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
930
1031
|
has_reviews: false,
|
|
931
1032
|
};
|
|
932
1033
|
});
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
1034
|
+
// #4731: multiline-aware — the Requirements field may hard-wrap, so the
|
|
1035
|
+
// value is extracted past the line break before the ID scan.
|
|
1036
|
+
const phaseSection = roadmapPhase?.['section'];
|
|
1037
|
+
const reqLine = phaseSection
|
|
1038
|
+
? roadmapParser.extractPhaseFieldMultiline(phaseSection, 'Requirements')
|
|
1039
|
+
: null;
|
|
1040
|
+
const reqExtracted = reqLine
|
|
1041
|
+
? reqLine.replace(/[\[\]]/g, '').split(',').map((s) => s.trim()).filter(Boolean).join(', ')
|
|
936
1042
|
: null;
|
|
937
1043
|
const phase_req_ids = reqExtracted && reqExtracted !== 'TBD' ? reqExtracted : null;
|
|
938
1044
|
const phaseDirPlan = phaseInfo?.['directory'] || null;
|
|
@@ -952,6 +1058,8 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
952
1058
|
}
|
|
953
1059
|
const granularityOverride = options['granularity'];
|
|
954
1060
|
assertValidGranularityOverride(granularityOverride, error);
|
|
1061
|
+
// #4683: computed once — see the threat_id_duplicates fields in the payload.
|
|
1062
|
+
const threatIdDuplicatesPlan = findDuplicateThreatIds(cwd, phaseDirPlan);
|
|
955
1063
|
const granularity = resolveGranularityInternal(cwd, 'planning', granularityOverride || undefined);
|
|
956
1064
|
// #3188: see cmdInitExecutePhase — null when absent, parity with the
|
|
957
1065
|
// conditional sibling fields in this same result object.
|
|
@@ -983,8 +1091,10 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
983
1091
|
phase_slug: phaseInfo?.['phase_slug'] || null,
|
|
984
1092
|
padded_phase: phaseNumberPlan ? normalizePhaseName(phaseNumberPlan) : null,
|
|
985
1093
|
phase_req_ids,
|
|
1094
|
+
// #5060: Phase Status Module — the single owner of "what state is phase P
|
|
1095
|
+
// in?" (ADR-5057 §1/§2).
|
|
986
1096
|
phase_status: phaseDirPlan
|
|
987
|
-
?
|
|
1097
|
+
? (0, phase_status_cjs_1.toDisplayLabel)((0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(cwd, phaseDirPlan), { convention: resolvePhaseIdConvention(cwd) }).value.status, { pendingWord: 'Pending' })
|
|
988
1098
|
: 'Pending',
|
|
989
1099
|
has_research: phaseInfo?.['has_research'] || false,
|
|
990
1100
|
has_context: phaseInfo?.['has_context'] || false,
|
|
@@ -996,6 +1106,11 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
996
1106
|
has_reviews: phaseInfo?.['has_reviews'] || false,
|
|
997
1107
|
has_plans: (phaseInfo?.['plans']?.length || 0) > 0,
|
|
998
1108
|
plan_count: phaseInfo?.['plans']?.length || 0,
|
|
1109
|
+
// #4683: the same cross-plan threat-ID duplicate list execute-phase gates
|
|
1110
|
+
// on, surfaced at PLAN time so the checker/reviewer catches the collision
|
|
1111
|
+
// before the plans are approved — not just before execution.
|
|
1112
|
+
threat_id_duplicates: threatIdDuplicatesPlan,
|
|
1113
|
+
threat_id_duplicate_count: threatIdDuplicatesPlan.length,
|
|
999
1114
|
planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
|
|
1000
1115
|
roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
|
|
1001
1116
|
// #2376: absolute — see comment on phase_dir above.
|
|
@@ -1176,12 +1291,15 @@ function buildInitCompletenessFields(cwd) {
|
|
|
1176
1291
|
}
|
|
1177
1292
|
function cmdInitNewProject(cwd, raw, options = {}) {
|
|
1178
1293
|
const config = loadConfig(cwd);
|
|
1179
|
-
|
|
1180
|
-
|
|
1294
|
+
// #4976: probe the GSD-owned store the config loader resolves
|
|
1295
|
+
// (`GSD_HOME || homedir()`), so these flags agree with config-new-project's
|
|
1296
|
+
// key probes in the same /gsd-new-project run.
|
|
1297
|
+
const gsdHome = process.env['GSD_HOME'] || node_os_1.default.homedir();
|
|
1298
|
+
const braveKeyFile = node_path_1.default.join(gsdHome, '.gsd', 'brave_api_key');
|
|
1181
1299
|
const hasBraveSearch = !!(process.env['BRAVE_API_KEY'] || node_fs_1.default.existsSync(braveKeyFile));
|
|
1182
|
-
const firecrawlKeyFile = node_path_1.default.join(
|
|
1300
|
+
const firecrawlKeyFile = node_path_1.default.join(gsdHome, '.gsd', 'firecrawl_api_key');
|
|
1183
1301
|
const hasFirecrawl = !!(process.env['FIRECRAWL_API_KEY'] || node_fs_1.default.existsSync(firecrawlKeyFile));
|
|
1184
|
-
const exaKeyFile = node_path_1.default.join(
|
|
1302
|
+
const exaKeyFile = node_path_1.default.join(gsdHome, '.gsd', 'exa_api_key');
|
|
1185
1303
|
const hasExaSearch = !!(process.env['EXA_API_KEY'] || node_fs_1.default.existsSync(exaKeyFile));
|
|
1186
1304
|
const hasCode = hasCodeFilesInternal(cwd);
|
|
1187
1305
|
const hasPackageFile = hasPackageFileInternal(cwd);
|
|
@@ -1336,7 +1454,7 @@ function cmdInitNewMilestone(cwd, raw, options = {}) {
|
|
|
1336
1454
|
// would otherwise silently delete a stale/invalid pointer as a side effect
|
|
1337
1455
|
// of building a JSON report field, and (per #3579) could change what a
|
|
1338
1456
|
// LATER resolution in the same process observes.
|
|
1339
|
-
const resolvedWorkstream =
|
|
1457
|
+
const resolvedWorkstream = resolveEnvWorkstream() ?? peekActiveWorkstream(cwd);
|
|
1340
1458
|
const workstreamActive = !!resolvedWorkstream;
|
|
1341
1459
|
const flatMode = !workstreamActive;
|
|
1342
1460
|
// #2992 (Phase 6.1): additive, optional field — degrades to null, never throws.
|
|
@@ -1541,9 +1659,21 @@ function cmdInitVerifyWork(cwd, phase, raw) {
|
|
|
1541
1659
|
};
|
|
1542
1660
|
});
|
|
1543
1661
|
const phaseDir = phaseInfo?.['directory'] || null;
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1662
|
+
// #5060: Phase Status Module — matched counts from the §7.5 owner
|
|
1663
|
+
// (scanPhasePlans via phaseStatus), not phaseInfo's raw plans/summaries
|
|
1664
|
+
// arrays, so this projection's counts agree with buildPhaseCompletionProjection's
|
|
1665
|
+
// own isPhaseComplete call below (see the convention thread there).
|
|
1666
|
+
const verifyWorkConvention = resolvePhaseIdConvention(cwd);
|
|
1667
|
+
const verifyWorkPs = phaseDir
|
|
1668
|
+
? (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(cwd, phaseDir), { convention: verifyWorkConvention })
|
|
1669
|
+
: null;
|
|
1670
|
+
const planCount = verifyWorkPs
|
|
1671
|
+
? verifyWorkPs.value.planCount
|
|
1672
|
+
: phaseInfo?.['plans']?.length || 0;
|
|
1673
|
+
const summaryCount = verifyWorkPs
|
|
1674
|
+
? verifyWorkPs.value.summaryCount
|
|
1675
|
+
: phaseInfo?.['summaries']?.length || 0;
|
|
1676
|
+
const completion = buildPhaseCompletionProjection(cwd, phaseInfo?.['phase_number'] || phase, phaseDir, planCount, summaryCount, _slashRuntime, verifyWorkConvention);
|
|
1547
1677
|
const uatReport = phaseDir
|
|
1548
1678
|
? evaluateUatPassed(node_path_1.default.join(cwd, phaseDir), {
|
|
1549
1679
|
policy: { requireVerification: true },
|
|
@@ -2217,6 +2347,12 @@ function cmdInitTodos(cwd, area, raw) {
|
|
|
2217
2347
|
function cmdInitMilestoneOp(cwd, raw) {
|
|
2218
2348
|
const config = loadConfig(cwd);
|
|
2219
2349
|
const milestone = milestoneRecord(cwd);
|
|
2350
|
+
// #5060: Phase Status Module — the single owner of "what state is phase P
|
|
2351
|
+
// in?" (ADR-5057 §1/§2). A phase counts as completed only when the ladder
|
|
2352
|
+
// itself says COMPLETE, not "has any *-SUMMARY.md" (a counts-only verdict
|
|
2353
|
+
// that bypasses isPhaseComplete and disagrees with every other completion
|
|
2354
|
+
// consumer on a stale `passed` report).
|
|
2355
|
+
const milestoneOpConvention = resolvePhaseIdConvention(cwd);
|
|
2220
2356
|
let phaseCount = 0;
|
|
2221
2357
|
let completedPhases = 0;
|
|
2222
2358
|
const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
|
|
@@ -2225,14 +2361,16 @@ function cmdInitMilestoneOp(cwd, raw) {
|
|
|
2225
2361
|
const roadmapPath = node_path_1.default.join(planningDir(cwd), 'ROADMAP.md');
|
|
2226
2362
|
const roadmapRaw = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
2227
2363
|
const currentSection = extractCurrentMilestone(roadmapRaw, cwd);
|
|
2228
|
-
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag
|
|
2229
|
-
|
|
2364
|
+
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag, owned by
|
|
2365
|
+
// buildPhaseHeadingScanRegex (phase-id.cts) so this scan also recognizes
|
|
2366
|
+
// bracket-convention headings instead of hand-rolling a literal `Phase\s+`.
|
|
2367
|
+
const { regex: phasePattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, milestoneOpConvention);
|
|
2230
2368
|
let m;
|
|
2231
2369
|
while ((m = phasePattern.exec(currentSection)) !== null) {
|
|
2232
2370
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
2233
|
-
if (isSentinelPhaseId(m[
|
|
2371
|
+
if (isSentinelPhaseId(m[phaseNumGroup]))
|
|
2234
2372
|
continue;
|
|
2235
|
-
roadmapPhaseNumbers.push(m[
|
|
2373
|
+
roadmapPhaseNumbers.push(m[phaseNumGroup]);
|
|
2236
2374
|
}
|
|
2237
2375
|
}
|
|
2238
2376
|
catch {
|
|
@@ -2267,8 +2405,8 @@ function cmdInitMilestoneOp(cwd, raw) {
|
|
|
2267
2405
|
if (!dirName)
|
|
2268
2406
|
continue;
|
|
2269
2407
|
try {
|
|
2270
|
-
const
|
|
2271
|
-
if (
|
|
2408
|
+
const isComplete = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dirName), { convention: milestoneOpConvention }).value.status === phase_status_cjs_1.PHASE_STATUS.COMPLETE;
|
|
2409
|
+
if (isComplete)
|
|
2272
2410
|
completedPhases++;
|
|
2273
2411
|
}
|
|
2274
2412
|
catch {
|
|
@@ -2287,8 +2425,8 @@ function cmdInitMilestoneOp(cwd, raw) {
|
|
|
2287
2425
|
phaseCount = dirs.length;
|
|
2288
2426
|
for (const dir of dirs) {
|
|
2289
2427
|
try {
|
|
2290
|
-
const
|
|
2291
|
-
if (
|
|
2428
|
+
const isComplete = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dir), { convention: milestoneOpConvention }).value.status === phase_status_cjs_1.PHASE_STATUS.COMPLETE;
|
|
2429
|
+
if (isComplete)
|
|
2292
2430
|
completedPhases++;
|
|
2293
2431
|
}
|
|
2294
2432
|
catch {
|
|
@@ -2375,6 +2513,10 @@ function cmdInitManager(cwd, raw) {
|
|
|
2375
2513
|
const config = loadConfig(cwd);
|
|
2376
2514
|
const milestone = milestoneRecord(cwd);
|
|
2377
2515
|
const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
|
|
2516
|
+
const phaseIdConvention = resolvePhaseIdConvention(cwd);
|
|
2517
|
+
const capturesBracketId = phaseIdConvention === 'bracket';
|
|
2518
|
+
const phaseHeadingPrefix = phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, phaseIdConvention, capturesBracketId);
|
|
2519
|
+
const phaseHeadingPrefixNoCapture = phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, phaseIdConvention);
|
|
2378
2520
|
const paths = planningPaths(cwd);
|
|
2379
2521
|
if (!node_fs_1.default.existsSync(paths.roadmap)) {
|
|
2380
2522
|
error(`No ROADMAP.md found. Run ${(0, runtime_slash_cjs_1.formatGsdSlash)('new-milestone', _slashRuntime)} first.`);
|
|
@@ -2384,39 +2526,61 @@ function cmdInitManager(cwd, raw) {
|
|
|
2384
2526
|
}
|
|
2385
2527
|
const rawContent = node_fs_1.default.readFileSync(paths.roadmap, 'utf-8');
|
|
2386
2528
|
const content = extractCurrentMilestone(rawContent, cwd);
|
|
2387
|
-
const phasesDir = paths.phases;
|
|
2388
2529
|
// #3185 (ADR-3180 Decision 1): "which phase directories belong to the
|
|
2389
2530
|
// CURRENT milestone" is the scoped question listMilestonePhaseDirs owns —
|
|
2390
2531
|
// routed through it instead of a hand-rolled readdirSync + a separate
|
|
2391
2532
|
// getMilestonePhaseFilter window check (which also never excluded
|
|
2392
2533
|
// sentinels, unlike the owner).
|
|
2393
|
-
const _phaseDirEntries = listMilestonePhaseDirs(phasesDir, { cwd }).value;
|
|
2394
2534
|
const _checkboxStates = new Map();
|
|
2395
|
-
|
|
2535
|
+
// #4982: anchored (`^`, `m`) so the phase label must be the FIRST thing
|
|
2536
|
+
// after the checkbox (tolerating only an optional `**` bold marker) —
|
|
2537
|
+
// the prior unanchored `\s*.*` let the greedy `.*` bind to the LAST
|
|
2538
|
+
// "Phase N" mentioned anywhere in the line's prose instead of the line's
|
|
2539
|
+
// own phase. First-match-wins (`if (!_checkboxStates.has(...))` below)
|
|
2540
|
+
// additionally guards against a later anchored line (e.g. a per-plan
|
|
2541
|
+
// sub-entry self-titled "Phase N ...") overwriting an already-recorded
|
|
2542
|
+
// phase's own (first) checkbox — anchoring alone does not fully close
|
|
2543
|
+
// that "last-match-wins" composition defect.
|
|
2544
|
+
const _cbPattern = new RegExp(`^[ \\t]*-[ \\t]*\\[(x| )\\][ \\t]*(?:\\*\\*)?${phaseHeadingPrefix}(${PHASE_NUMBER_TOKEN_SOURCE})[:\\s]`, 'gim');
|
|
2396
2545
|
let _cbMatch;
|
|
2397
2546
|
while ((_cbMatch = _cbPattern.exec(content)) !== null) {
|
|
2398
|
-
|
|
2547
|
+
const phaseGroup = capturesBracketId ? 3 : 2;
|
|
2548
|
+
const _cbKey = _cbMatch[phaseGroup];
|
|
2549
|
+
if (!_checkboxStates.has(_cbKey)) {
|
|
2550
|
+
_checkboxStates.set(_cbKey, _cbMatch[1].toLowerCase() === 'x');
|
|
2551
|
+
}
|
|
2399
2552
|
}
|
|
2400
2553
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
|
2401
|
-
const phasePattern = new RegExp(`#{2,4}\\s
|
|
2554
|
+
const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefix}(${PHASE_NUMBER_TOKEN_SOURCE})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:\\s*([^\\n]+)`, 'gi');
|
|
2402
2555
|
const phases = [];
|
|
2403
2556
|
let match;
|
|
2404
2557
|
while ((match = phasePattern.exec(content)) !== null) {
|
|
2405
|
-
const
|
|
2406
|
-
const
|
|
2558
|
+
const bracketId = capturesBracketId ? match[1] : undefined;
|
|
2559
|
+
const phaseNum = capturesBracketId ? match[2] : match[1];
|
|
2560
|
+
const phaseName = (capturesBracketId ? match[3] : match[2])
|
|
2561
|
+
.replace(/\(INSERTED\)/i, '')
|
|
2562
|
+
.trim();
|
|
2563
|
+
let displayId;
|
|
2564
|
+
if (bracketId) {
|
|
2565
|
+
try {
|
|
2566
|
+
displayId = renderPhaseId(parsePhaseId(`${bracketId}-${phaseNum}`));
|
|
2567
|
+
}
|
|
2568
|
+
catch {
|
|
2569
|
+
// The bracket selector is deliberately read-tolerant. If a heading is
|
|
2570
|
+
// non-canonical, retain the manager row without fabricating display_id.
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2407
2573
|
const sectionStart = match.index;
|
|
2408
2574
|
const restOfContent = content.slice(sectionStart);
|
|
2409
|
-
const nextHeader = restOfContent.match(
|
|
2575
|
+
const nextHeader = restOfContent.match(new RegExp(`\\n#{2,4}\\s+${phaseHeadingPrefixNoCapture}\\d[\\d.]*`, 'i'));
|
|
2410
2576
|
const sectionEnd = nextHeader
|
|
2411
2577
|
? sectionStart + nextHeader.index
|
|
2412
2578
|
: content.length;
|
|
2413
2579
|
const section = content.slice(sectionStart, sectionEnd);
|
|
2414
|
-
const
|
|
2415
|
-
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
2580
|
+
const goal = roadmapParser.extractPhaseFieldMultiline(section, 'Goal');
|
|
2416
2581
|
const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
2417
2582
|
const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
|
|
2418
|
-
|
|
2419
|
-
let diskStatus = 'no_directory';
|
|
2583
|
+
let diskStatus = phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY;
|
|
2420
2584
|
let planCount = 0;
|
|
2421
2585
|
let summaryCount = 0;
|
|
2422
2586
|
let hasContext = false;
|
|
@@ -2426,16 +2590,25 @@ function cmdInitManager(cwd, raw) {
|
|
|
2426
2590
|
// #4014 (epic #3473 B4-unreadable): default COMPLETE — no directory at
|
|
2427
2591
|
// all (dirMatch not found) is a genuine, not-unreadable answer.
|
|
2428
2592
|
let contextScope = SCOPE.COMPLETE;
|
|
2429
|
-
let completion = buildPhaseCompletionProjection(cwd, phaseNum, null, planCount, summaryCount, _slashRuntime);
|
|
2593
|
+
let completion = buildPhaseCompletionProjection(cwd, phaseNum, null, planCount, summaryCount, _slashRuntime, phaseIdConvention);
|
|
2430
2594
|
try {
|
|
2431
|
-
// #
|
|
2432
|
-
// milestone-
|
|
2433
|
-
//
|
|
2434
|
-
//
|
|
2435
|
-
|
|
2595
|
+
// #4801: resolve through the canonical locator instead of a private
|
|
2596
|
+
// current-milestone-only scan. findPhaseInternal searches the live
|
|
2597
|
+
// .planning/phases directory FIRST (same set the retired
|
|
2598
|
+
// matchPhaseDirs/_phaseDirEntries pair scanned — the #3185 physical-dir
|
|
2599
|
+
// scope choice is inherited by the locator's live arm) and then falls
|
|
2600
|
+
// back through listArchiveVersionDirs (workstream-scoped, #2855), so an
|
|
2601
|
+
// ARCHIVED phase directory with a passing verification resolves instead
|
|
2602
|
+
// of reporting no_directory/phase_complete:false. Five other init
|
|
2603
|
+
// commands already route through this same primitive; this was the
|
|
2604
|
+
// holdout private copy (#4793-family declared-owner drift).
|
|
2605
|
+
const located = findPhaseInternal(cwd, phaseNum, phaseIdConvention);
|
|
2606
|
+
const dirMatch = located && located['found']
|
|
2607
|
+
? node_path_1.default.posix.basename(String(located['directory']))
|
|
2608
|
+
: null;
|
|
2436
2609
|
if (dirMatch) {
|
|
2437
|
-
const fullDir = node_path_1.default.join(
|
|
2438
|
-
const phaseDirRel =
|
|
2610
|
+
const fullDir = node_path_1.default.join(cwd, String(located['directory']));
|
|
2611
|
+
const phaseDirRel = String(located['directory']);
|
|
2439
2612
|
// #4014 (epic #3473 B4-unreadable): this whole block used to swallow
|
|
2440
2613
|
// ANY readdirSync failure below into the bare `catch { /* empty */ }`
|
|
2441
2614
|
// at the bottom — an unreadable phase directory reported the exact
|
|
@@ -2448,8 +2621,11 @@ function cmdInitManager(cwd, raw) {
|
|
|
2448
2621
|
// only, the failure control-flow is untouched.
|
|
2449
2622
|
contextScope = findContextMdIn(fullDir).scope;
|
|
2450
2623
|
const phaseFiles = node_fs_1.default.readdirSync(fullDir);
|
|
2451
|
-
|
|
2452
|
-
|
|
2624
|
+
// #5060: Phase Status Module — the single owner of "what state is
|
|
2625
|
+
// phase P in?" (ADR-5057 §1/§2).
|
|
2626
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(fullDir, { convention: phaseIdConvention });
|
|
2627
|
+
planCount = ps.value.planCount;
|
|
2628
|
+
summaryCount = ps.value.summaryCount;
|
|
2453
2629
|
// #3511-class: scope the raw listing to THIS phase's own artifacts
|
|
2454
2630
|
// before the hasContext/hasResearch predicates run, so a stray
|
|
2455
2631
|
// cross-phase `-CONTEXT.md`/`-RESEARCH.md` sitting in this directory
|
|
@@ -2457,21 +2633,8 @@ function cmdInitManager(cwd, raw) {
|
|
|
2457
2633
|
const scopedFiles = scopeToPhase(phaseFiles, dirMatch);
|
|
2458
2634
|
hasContext = findContextMdIn(scopedFiles) !== null;
|
|
2459
2635
|
hasResearch = scopedFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
|
|
2460
|
-
completion = buildPhaseCompletionProjection(cwd, phaseNum, phaseDirRel, planCount, summaryCount, _slashRuntime);
|
|
2461
|
-
|
|
2462
|
-
diskStatus = 'complete';
|
|
2463
|
-
else if (completion.implementation_complete)
|
|
2464
|
-
diskStatus = 'executed';
|
|
2465
|
-
else if (summaryCount > 0)
|
|
2466
|
-
diskStatus = 'partial';
|
|
2467
|
-
else if (planCount > 0)
|
|
2468
|
-
diskStatus = 'planned';
|
|
2469
|
-
else if (hasResearch)
|
|
2470
|
-
diskStatus = 'researched';
|
|
2471
|
-
else if (hasContext)
|
|
2472
|
-
diskStatus = 'discussed';
|
|
2473
|
-
else
|
|
2474
|
-
diskStatus = 'empty';
|
|
2636
|
+
completion = buildPhaseCompletionProjection(cwd, phaseNum, phaseDirRel, planCount, summaryCount, _slashRuntime, phaseIdConvention);
|
|
2637
|
+
diskStatus = (0, phase_status_cjs_1.toDiskStatus)(ps.value.status, { hasResearch, hasContext });
|
|
2475
2638
|
const nowMs = clock_cjs_1.realClock.now();
|
|
2476
2639
|
let newestMtime = 0;
|
|
2477
2640
|
for (const f of phaseFiles) {
|
|
@@ -2509,6 +2672,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2509
2672
|
const roadmapComplete = _checkboxStates.get(phaseNum) || false;
|
|
2510
2673
|
phases.push({
|
|
2511
2674
|
number: phaseNum,
|
|
2675
|
+
...(displayId ? { display_id: displayId } : {}),
|
|
2512
2676
|
name: phaseName,
|
|
2513
2677
|
goal,
|
|
2514
2678
|
depends_on,
|
|
@@ -2549,7 +2713,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2549
2713
|
.filter((p) => p['phase_complete'] === true)
|
|
2550
2714
|
.map((p) => normalizePhaseNumber(p['number'])));
|
|
2551
2715
|
const phaseMap = new Map(phases.map((p) => [normalizePhaseNumber(p['number']), p]));
|
|
2552
|
-
const _allCompletedPattern = new RegExp(`-\\s*\\[x\\]\\s
|
|
2716
|
+
const _allCompletedPattern = new RegExp(`-\\s*\\[x\\]\\s*.*${phaseHeadingPrefixNoCapture}(${PHASE_NUMBER_TOKEN_SOURCE})[:\\s]`, 'gi');
|
|
2553
2717
|
let _allMatch;
|
|
2554
2718
|
while ((_allMatch = _allCompletedPattern.exec(rawContent)) !== null) {
|
|
2555
2719
|
const phaseNum = normalizePhaseNumber(_allMatch[1]);
|
|
@@ -2575,13 +2739,47 @@ function cmdInitManager(cwd, raw) {
|
|
|
2575
2739
|
function hasDepRelationship(numA, numB) {
|
|
2576
2740
|
return reaches(numA, numB) || reaches(numB, numA);
|
|
2577
2741
|
}
|
|
2742
|
+
// #4764: a phase reference in depends_on prose is a PHASE-SHAPED token in
|
|
2743
|
+
// context — directly following "Phase"/"Phases" — never a bare digit run.
|
|
2744
|
+
// The previous whole-field scrape matched the token grammar against every
|
|
2745
|
+
// digit run, so calendar dates ("2026-09-14" → 2026, 09, 14), git shas
|
|
2746
|
+
// ("8bf403100d" → 8b, 403100d, …), bracketed ledger ids (WINDOWS #1843) and
|
|
2747
|
+
// the row's OWN number all became "dependencies", and deps_satisfied came
|
|
2748
|
+
// back false for phases whose prose declares none (50 of 92 phases in the
|
|
2749
|
+
// reporter's milestone). The anchored grammar (owned by phase-id.cts as
|
|
2750
|
+
// PHASE_DEP_REF_SOURCE, shared with planning-inspect's dependencies) keeps
|
|
2751
|
+
// lists fully extracted ("Phases 601 and 602", "Phase 601, 602, and 603",
|
|
2752
|
+
// "Phase 1-3") — silently dropping a REAL dependency would clear
|
|
2753
|
+
// deps_satisfied prematurely, the dangerous direction. Negation prose
|
|
2754
|
+
// ("dropped the dependency on Phase 654") is NOT detected: the issue's own
|
|
2755
|
+
// minimum keeps such tokens.
|
|
2756
|
+
const depPhaseRefRe = new RegExp(`${PHASE_DEP_REF_SOURCE}`, 'gi');
|
|
2757
|
+
const depTokenRe = new RegExp(`${PHASE_NUMBER_TOKEN_SOURCE}`, 'gi');
|
|
2578
2758
|
for (const phase of phases) {
|
|
2579
2759
|
if (!phase['depends_on'] ||
|
|
2580
2760
|
/^none$/i.test(phase['depends_on'].trim())) {
|
|
2581
2761
|
phase['deps_satisfied'] = true;
|
|
2582
2762
|
}
|
|
2583
2763
|
else {
|
|
2584
|
-
const
|
|
2764
|
+
const prose = phase['depends_on'];
|
|
2765
|
+
const ownNumber = normalizePhaseNumber(phase['number']);
|
|
2766
|
+
const depNums = [];
|
|
2767
|
+
const seen = new Set();
|
|
2768
|
+
let refMatch;
|
|
2769
|
+
depPhaseRefRe.lastIndex = 0;
|
|
2770
|
+
while ((refMatch = depPhaseRefRe.exec(prose)) !== null) {
|
|
2771
|
+
let tok;
|
|
2772
|
+
depTokenRe.lastIndex = 0;
|
|
2773
|
+
while ((tok = depTokenRe.exec(refMatch[1])) !== null) {
|
|
2774
|
+
const normalized = normalizePhaseNumber(tok[0]);
|
|
2775
|
+
if (normalized === ownNumber)
|
|
2776
|
+
continue; // #4764: never the row's own phase
|
|
2777
|
+
if (seen.has(normalized))
|
|
2778
|
+
continue;
|
|
2779
|
+
seen.add(normalized);
|
|
2780
|
+
depNums.push(tok[0]);
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2585
2783
|
phase['deps_satisfied'] = depNums.every((n) => completedNums.has(normalizePhaseNumber(n)));
|
|
2586
2784
|
phase['dep_phases'] = depNums;
|
|
2587
2785
|
}
|
|
@@ -2594,7 +2792,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2594
2792
|
}
|
|
2595
2793
|
for (const phase of phases) {
|
|
2596
2794
|
phase['is_next_to_discuss'] =
|
|
2597
|
-
(phase['disk_status'] ===
|
|
2795
|
+
(phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.EMPTY || phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY) &&
|
|
2598
2796
|
phase['deps_satisfied'];
|
|
2599
2797
|
}
|
|
2600
2798
|
let waitingSignal = null;
|
|
@@ -2616,12 +2814,12 @@ function cmdInitManager(cwd, raw) {
|
|
|
2616
2814
|
}
|
|
2617
2815
|
const recommendedActions = [];
|
|
2618
2816
|
for (const phase of phases) {
|
|
2619
|
-
if (phase['disk_status'] ===
|
|
2817
|
+
if (phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.COMPLETE)
|
|
2620
2818
|
continue;
|
|
2621
2819
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
2622
2820
|
if (isSentinelPhaseId(phase['number']))
|
|
2623
2821
|
continue;
|
|
2624
|
-
if (phase['disk_status'] ===
|
|
2822
|
+
if (phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.EXECUTED) {
|
|
2625
2823
|
recommendedActions.push({
|
|
2626
2824
|
phase: phase['number'],
|
|
2627
2825
|
phase_name: phase['name'],
|
|
@@ -2630,7 +2828,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2630
2828
|
command: phase['verification_next_command'],
|
|
2631
2829
|
});
|
|
2632
2830
|
}
|
|
2633
|
-
else if (phase['disk_status'] ===
|
|
2831
|
+
else if (phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.PLANNED && phase['deps_satisfied']) {
|
|
2634
2832
|
recommendedActions.push({
|
|
2635
2833
|
phase: phase['number'],
|
|
2636
2834
|
phase_name: phase['name'],
|
|
@@ -2639,8 +2837,8 @@ function cmdInitManager(cwd, raw) {
|
|
|
2639
2837
|
command: `${(0, runtime_slash_cjs_1.formatGsdSlash)('execute-phase', _slashRuntime)} ${phase['number']}`,
|
|
2640
2838
|
});
|
|
2641
2839
|
}
|
|
2642
|
-
else if (phase['disk_status'] ===
|
|
2643
|
-
phase['disk_status'] ===
|
|
2840
|
+
else if (phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.DISCUSSED ||
|
|
2841
|
+
phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.RESEARCHED) {
|
|
2644
2842
|
recommendedActions.push({
|
|
2645
2843
|
phase: phase['number'],
|
|
2646
2844
|
phase_name: phase['name'],
|
|
@@ -2649,7 +2847,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2649
2847
|
command: `${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', _slashRuntime)} ${phase['number']}`,
|
|
2650
2848
|
});
|
|
2651
2849
|
}
|
|
2652
|
-
else if ((phase['disk_status'] ===
|
|
2850
|
+
else if ((phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.EMPTY || phase['disk_status'] === phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY) &&
|
|
2653
2851
|
phase['is_next_to_discuss']) {
|
|
2654
2852
|
recommendedActions.push({
|
|
2655
2853
|
phase: phase['number'],
|
|
@@ -2660,10 +2858,10 @@ function cmdInitManager(cwd, raw) {
|
|
|
2660
2858
|
});
|
|
2661
2859
|
}
|
|
2662
2860
|
}
|
|
2663
|
-
const activeExecuting = phases.filter((p) => p['disk_status'] ===
|
|
2664
|
-
(p['disk_status'] ===
|
|
2861
|
+
const activeExecuting = phases.filter((p) => p['disk_status'] === phase_status_cjs_1.DISK_STATUS.PARTIAL ||
|
|
2862
|
+
(p['disk_status'] === phase_status_cjs_1.DISK_STATUS.PLANNED && p['is_active']));
|
|
2665
2863
|
const activePlanning = phases.filter((p) => p['is_active'] &&
|
|
2666
|
-
(p['disk_status'] ===
|
|
2864
|
+
(p['disk_status'] === phase_status_cjs_1.DISK_STATUS.DISCUSSED || p['disk_status'] === phase_status_cjs_1.DISK_STATUS.RESEARCHED));
|
|
2667
2865
|
const filteredActions = recommendedActions.filter((action) => {
|
|
2668
2866
|
if (action['action'] === 'execute' && activeExecuting.length > 0) {
|
|
2669
2867
|
return activeExecuting.every((active) => !hasDepRelationship(action['phase'], active['number']));
|
|
@@ -2706,7 +2904,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2706
2904
|
phases,
|
|
2707
2905
|
phase_count: phases.length,
|
|
2708
2906
|
completed_count: completedCount,
|
|
2709
|
-
in_progress_count: phases.filter((p) => [
|
|
2907
|
+
in_progress_count: phases.filter((p) => [phase_status_cjs_1.DISK_STATUS.EXECUTED, phase_status_cjs_1.DISK_STATUS.PARTIAL, phase_status_cjs_1.DISK_STATUS.PLANNED, phase_status_cjs_1.DISK_STATUS.DISCUSSED, phase_status_cjs_1.DISK_STATUS.RESEARCHED].includes(p['disk_status'])).length,
|
|
2710
2908
|
recommended_actions: filteredActions,
|
|
2711
2909
|
waiting_signal: waitingSignal,
|
|
2712
2910
|
all_complete: completedCount === nonBacklogPhases.length && nonBacklogPhases.length > 0,
|
|
@@ -2921,7 +3119,7 @@ function cmdInitUpdate(cwd, raw, options = {}) {
|
|
|
2921
3119
|
function cmdInitTransition(cwd, raw, options = {}) {
|
|
2922
3120
|
// #3579 root-cause fix: read-only informational field — peek, don't
|
|
2923
3121
|
// self-heal (see cmdInitNewMilestone's identical rationale above).
|
|
2924
|
-
const resolvedWorkstream =
|
|
3122
|
+
const resolvedWorkstream = resolveEnvWorkstream() ?? peekActiveWorkstream(cwd);
|
|
2925
3123
|
const workstreamActive = !!resolvedWorkstream;
|
|
2926
3124
|
const result = {
|
|
2927
3125
|
other_active_workstreams: workstreamActive
|
|
@@ -3013,7 +3211,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3013
3211
|
// non-mutating peek so an unresolvable pointer isn't self-healed (cleared)
|
|
3014
3212
|
// here and then found "absent" by diagnoseUnresolvedActiveWorkstream below,
|
|
3015
3213
|
// which would misreport a present-but-bad marker as no marker at all.
|
|
3016
|
-
const _resolvedWorkstream =
|
|
3214
|
+
const _resolvedWorkstream = resolveEnvWorkstream() ?? peekActiveWorkstream(cwd);
|
|
3017
3215
|
if (_availableWorkstreams.length > 0 && !_resolvedWorkstream) {
|
|
3018
3216
|
// #3579: getActiveWorkstream now inherits a pointer-less session's read
|
|
3019
3217
|
// from the shared .planning/active-workstream marker, so reaching this
|
|
@@ -3040,17 +3238,50 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3040
3238
|
const roadmapCheckboxStates = new Map();
|
|
3041
3239
|
try {
|
|
3042
3240
|
const roadmapContent = extractCurrentMilestone(node_fs_1.default.readFileSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md'), 'utf-8'), cwd);
|
|
3043
|
-
|
|
3044
|
-
|
|
3241
|
+
const progressConvention = resolvePhaseIdConvention(cwd);
|
|
3242
|
+
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag, owned by
|
|
3243
|
+
// buildPhaseHeadingScanRegex (phase-id.cts) so this scan also recognizes
|
|
3244
|
+
// bracket-convention headings instead of hand-rolling a literal `Phase\s+`.
|
|
3245
|
+
const { regex: headingPattern, phaseNumGroup: hNumGroup, phaseNameGroup: hNameGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, progressConvention);
|
|
3045
3246
|
let hm;
|
|
3046
3247
|
while ((hm = headingPattern.exec(roadmapContent)) !== null) {
|
|
3047
|
-
roadmapPhaseNums.add(hm[
|
|
3048
|
-
roadmapPhaseNames.set(hm[
|
|
3248
|
+
roadmapPhaseNums.add(hm[hNumGroup]);
|
|
3249
|
+
roadmapPhaseNames.set(hm[hNumGroup], hm[hNameGroup].replace(/\(INSERTED\)/i, '').trim());
|
|
3049
3250
|
}
|
|
3050
|
-
|
|
3251
|
+
// #4906 Phase 5 (#4984): NOT migrated onto phaseHeadingPrefixSrcFor — same
|
|
3252
|
+
// out-of-scope reasoning as roadmapPhaseMatchesExactPrefix/detectPhaseMvpMode
|
|
3253
|
+
// above; a direct LABEL_ONLY selector call here would have added a third
|
|
3254
|
+
// uncounted LABEL_ONLY consumer to init.cts's pinned
|
|
3255
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census.
|
|
3256
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
3257
|
+
// #4982: anchored (`^`, `m`) — the phase label must be the FIRST thing
|
|
3258
|
+
// after the checkbox (tolerating only an optional `**` bold marker) — so
|
|
3259
|
+
// the greedy `.*` this replaces can no longer bind a line's checkbox to
|
|
3260
|
+
// the LAST "Phase N" mentioned anywhere in that line's prose. This value
|
|
3261
|
+
// feeds `roadmap_complete` AND directly gates next_phase/current_phase
|
|
3262
|
+
// selection below, so getting the wrong phase here is load-bearing, not
|
|
3263
|
+
// metadata-only (contrast the `_cbPattern` site in cmdInitManager).
|
|
3264
|
+
// First-match-wins (`if (!roadmapCheckboxStates.has(...))`) additionally
|
|
3265
|
+
// guards against a later anchored line (e.g. a per-plan sub-entry
|
|
3266
|
+
// self-titled "Phase N ...") overwriting an already-recorded phase's own
|
|
3267
|
+
// (first) checkbox — anchoring alone does not fully close that
|
|
3268
|
+
// "last-match-wins" composition defect.
|
|
3269
|
+
// `(?:\[[^\]\n]{0,200}\]\s*)?` tolerates an optional leading bracket tag
|
|
3270
|
+
// (literal mirror of BASE_ANY_BRACKET_HEADING_PREFIX_SRC, phase-id.cts)
|
|
3271
|
+
// BEFORE the literal "Phase" word — decoration tolerance only, no capture
|
|
3272
|
+
// group, no convention gating. Without it the anchor fix above regresses
|
|
3273
|
+
// a checklist line combining a bracket tag with literal "Phase N" wording
|
|
3274
|
+
// (e.g. `**[GSD.02] Phase 3: ...**`), which the old unanchored `.*`
|
|
3275
|
+
// matched (by absorbing the bracket text). This does NOT migrate the site
|
|
3276
|
+
// onto phaseHeadingPrefixSrcFor / the bracket convention — that stays
|
|
3277
|
+
// out of scope per the comment above (#4984 revert, pinned census).
|
|
3278
|
+
const cbPattern = new RegExp(`^[ \\t]*-[ \\t]*\\[(x| )\\][ \\t]*(?:\\*\\*)?(?:\\[[^\\]\\n]{0,200}\\]\\s*)?Phase\\s+(${PHASE_NUMBER_TOKEN_SOURCE})[:\\s]`, 'gim');
|
|
3051
3279
|
let cbm;
|
|
3052
3280
|
while ((cbm = cbPattern.exec(roadmapContent)) !== null) {
|
|
3053
|
-
|
|
3281
|
+
const cbKey = cbm[2];
|
|
3282
|
+
if (!roadmapCheckboxStates.has(cbKey)) {
|
|
3283
|
+
roadmapCheckboxStates.set(cbKey, cbm[1].toLowerCase() === 'x');
|
|
3284
|
+
}
|
|
3054
3285
|
}
|
|
3055
3286
|
}
|
|
3056
3287
|
catch {
|
|
@@ -3072,8 +3303,9 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3072
3303
|
seenPhaseNums.add(phaseNumber.replace(/^0+/, '') || '0');
|
|
3073
3304
|
const phasePath = node_path_1.default.join(phasesDir, dir);
|
|
3074
3305
|
const phaseFiles = node_fs_1.default.readdirSync(phasePath);
|
|
3075
|
-
|
|
3076
|
-
|
|
3306
|
+
// #5060: Phase Status Module — the single owner of "what state is
|
|
3307
|
+
// phase P in?" (ADR-5057 §1/§2).
|
|
3308
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(phasePath, { convention: resolvePhaseIdConvention(cwd) });
|
|
3077
3309
|
// #3511-class: scope the raw listing to THIS phase's own artifacts
|
|
3078
3310
|
// before the hasResearch predicate runs, so a stray cross-phase
|
|
3079
3311
|
// `-RESEARCH.md` sitting in this directory cannot win this phase's
|
|
@@ -3081,16 +3313,8 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3081
3313
|
const scopedPhaseFiles = scopeToPhase(phaseFiles, dir);
|
|
3082
3314
|
const hasResearch = scopedPhaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
|
|
3083
3315
|
const phaseDirRel = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'phases', dir)));
|
|
3084
|
-
const completion = buildPhaseCompletionProjection(cwd, phaseNumber, phaseDirRel,
|
|
3085
|
-
const status =
|
|
3086
|
-
? 'complete'
|
|
3087
|
-
: completion.implementation_complete
|
|
3088
|
-
? 'executed'
|
|
3089
|
-
: plans.length > 0
|
|
3090
|
-
? 'in_progress'
|
|
3091
|
-
: hasResearch
|
|
3092
|
-
? 'researched'
|
|
3093
|
-
: 'pending';
|
|
3316
|
+
const completion = buildPhaseCompletionProjection(cwd, phaseNumber, phaseDirRel, ps.value.planCount, ps.value.summaryCount, _slashRuntime, resolvePhaseIdConvention(cwd));
|
|
3317
|
+
const status = (0, phase_status_cjs_1.toProgressStatus)(ps.value.status, { hasResearch });
|
|
3094
3318
|
const phaseInfo = {
|
|
3095
3319
|
number: phaseNumber,
|
|
3096
3320
|
name: phaseName,
|
|
@@ -3099,30 +3323,37 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3099
3323
|
// above still joins it against cwd.
|
|
3100
3324
|
directory: toPosixPath(node_path_1.default.join(cwd, phaseDirRel)),
|
|
3101
3325
|
status,
|
|
3102
|
-
plan_count:
|
|
3103
|
-
summary_count:
|
|
3326
|
+
plan_count: ps.value.planCount,
|
|
3327
|
+
summary_count: ps.value.summaryCount,
|
|
3104
3328
|
has_research: hasResearch,
|
|
3105
3329
|
...completion,
|
|
3106
3330
|
};
|
|
3107
3331
|
phases.push(phaseInfo);
|
|
3108
|
-
if (!currentPhase && (status ===
|
|
3332
|
+
if (!currentPhase && (status === phase_status_cjs_1.PROGRESS_STATUS.EXECUTED || status === phase_status_cjs_1.PROGRESS_STATUS.IN_PROGRESS || status === phase_status_cjs_1.PROGRESS_STATUS.RESEARCHED)) {
|
|
3109
3333
|
currentPhase = phaseInfo;
|
|
3110
3334
|
}
|
|
3111
|
-
if (!nextPhase && status ===
|
|
3335
|
+
if (!nextPhase && status === phase_status_cjs_1.PROGRESS_STATUS.PENDING) {
|
|
3112
3336
|
nextPhase = phaseInfo;
|
|
3113
3337
|
}
|
|
3114
3338
|
}
|
|
3115
3339
|
}
|
|
3116
|
-
catch {
|
|
3117
|
-
|
|
3340
|
+
catch (err) {
|
|
3341
|
+
// #5118: the owner's out-of-set report error is not a scan failure to
|
|
3342
|
+
// degrade over — the bundle is never assembled over a refused report.
|
|
3343
|
+
if (err instanceof VerificationStatusError)
|
|
3344
|
+
throw err;
|
|
3345
|
+
/* otherwise intentionally empty */
|
|
3118
3346
|
}
|
|
3119
3347
|
for (const [num, name] of roadmapPhaseNames) {
|
|
3120
3348
|
const stripped = num.replace(/^0+/, '') || '0';
|
|
3121
3349
|
if (!seenPhaseNums.has(stripped)) {
|
|
3122
3350
|
const checkboxComplete = roadmapCheckboxStates.get(num) === true ||
|
|
3123
3351
|
roadmapCheckboxStates.get(stripped) === true;
|
|
3124
|
-
const completion = buildPhaseCompletionProjection(cwd, num, null, 0, 0, _slashRuntime);
|
|
3125
|
-
|
|
3352
|
+
const completion = buildPhaseCompletionProjection(cwd, num, null, 0, 0, _slashRuntime, resolvePhaseIdConvention(cwd));
|
|
3353
|
+
// #5060: this synthetic row has no phase directory at all — the
|
|
3354
|
+
// roadmap heading exists but no `seenPhaseNums` disk match was found —
|
|
3355
|
+
// so it is the ladder's own NOT_STARTED word, not a derived guess.
|
|
3356
|
+
const status = phase_status_cjs_1.PROGRESS_STATUS.NOT_STARTED;
|
|
3126
3357
|
const phaseInfo = {
|
|
3127
3358
|
number: num,
|
|
3128
3359
|
// #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
|
|
@@ -3159,7 +3390,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3159
3390
|
{
|
|
3160
3391
|
const frontier = phases.find((p) => {
|
|
3161
3392
|
const st = p['status'];
|
|
3162
|
-
return (st ===
|
|
3393
|
+
return (st === phase_status_cjs_1.PROGRESS_STATUS.PENDING || st === phase_status_cjs_1.PROGRESS_STATUS.NOT_STARTED) && p['roadmap_complete'] !== true;
|
|
3163
3394
|
});
|
|
3164
3395
|
if (frontier)
|
|
3165
3396
|
nextPhase = frontier;
|
|
@@ -3196,8 +3427,8 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3196
3427
|
milestone_name: milestone['name'] ?? null,
|
|
3197
3428
|
phases,
|
|
3198
3429
|
phase_count: phases.length,
|
|
3199
|
-
completed_count: phases.filter((p) => p['status'] ===
|
|
3200
|
-
in_progress_count: phases.filter((p) => [
|
|
3430
|
+
completed_count: phases.filter((p) => p['status'] === phase_status_cjs_1.PROGRESS_STATUS.COMPLETE).length,
|
|
3431
|
+
in_progress_count: phases.filter((p) => [phase_status_cjs_1.PROGRESS_STATUS.EXECUTED, phase_status_cjs_1.PROGRESS_STATUS.IN_PROGRESS].includes(p['status'])).length,
|
|
3201
3432
|
current_phase: currentPhase,
|
|
3202
3433
|
next_phase: nextPhase,
|
|
3203
3434
|
paused_at: pausedAt,
|
|
@@ -3789,10 +4020,11 @@ function buildSkillManifest(cwd, skillsDir = null) {
|
|
|
3789
4020
|
seenNamesInRoot.add(name);
|
|
3790
4021
|
const description = frontmatter['description'] || '';
|
|
3791
4022
|
const triggers = [];
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
4023
|
+
// TRIGGER lines are read from the body after the block `extractFrontmatter` read (the
|
|
4024
|
+
// one fence owner), so a `---` inside a frontmatter value cannot start the body early.
|
|
4025
|
+
const block = frontmatterBlock(content);
|
|
4026
|
+
if (block) {
|
|
4027
|
+
const triggerLines = block.rest.match(/^TRIGGER\s+when:\s*(.+)$/gmi);
|
|
3796
4028
|
if (triggerLines) {
|
|
3797
4029
|
for (const line of triggerLines) {
|
|
3798
4030
|
const m = line.match(/^TRIGGER\s+when:\s*(.+)$/i);
|