@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
|
@@ -28,7 +28,7 @@ const planningScopeMod = require("./planning-scope.cjs");
|
|
|
28
28
|
const { SCOPE } = planningScopeMod;
|
|
29
29
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
30
30
|
const roadmapParserModule = require("./roadmap-parser.cjs");
|
|
31
|
-
const { stripShippedMilestones, extractCurrentMilestone, extractCurrentMilestoneScoped, replaceInCurrentMilestone, listMilestoneHeadings, scanMilestonePhaseIds, collectTablePhaseRows } = roadmapParserModule;
|
|
31
|
+
const { stripShippedMilestones, extractCurrentMilestone, extractCurrentMilestoneScoped, replaceInCurrentMilestone, listMilestoneHeadings, scanMilestonePhaseIds, collectTablePhaseRows, hasPhaseListingTableHeader } = roadmapParserModule;
|
|
32
32
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
33
33
|
const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
34
34
|
const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
|
|
@@ -45,13 +45,18 @@ const { loadConfig: loadConfigForScope } = configLoaderForScope;
|
|
|
45
45
|
const scanPhasePlans = require("./plan-scan.cjs");
|
|
46
46
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
47
47
|
const coreUtils = require("./core-utils.cjs");
|
|
48
|
-
const {
|
|
48
|
+
const { findUnsummarizedPlans } = coreUtils;
|
|
49
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
50
50
|
const frontmatter = require("./frontmatter.cjs");
|
|
51
|
-
const { extractFrontmatter, parseMustHavesBlock } = frontmatter;
|
|
51
|
+
const { extractFrontmatter, frontmatterRegion, parseMustHavesBlock } = frontmatter;
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
53
53
|
const verificationMod = require("./verification.cjs");
|
|
54
|
-
const { isPhaseComplete } = verificationMod;
|
|
54
|
+
const { isPhaseComplete, failOnVerificationStatusError, firstStatusError } = verificationMod;
|
|
55
|
+
// #4906 Phase 2 (#4917/ADR-4910): the PlanningDoc parse -> mutate -> serialize
|
|
56
|
+
// seam, mirroring phase.cts's already-migrated `writePlansField` site.
|
|
57
|
+
const planning_document_cjs_1 = require("./planning-document.cjs");
|
|
58
|
+
// #5060: the Phase Status Module owns the ladder; roadmap.cts is a consumer.
|
|
59
|
+
const phase_status_cjs_1 = require("./phase-status.cjs");
|
|
55
60
|
// ─── coerceTruthToString ──────────────────────────────────────────────────────
|
|
56
61
|
/**
|
|
57
62
|
* Coerce an arbitrary YAML scalar/object into a string for cross-cutting
|
|
@@ -170,9 +175,9 @@ function searchPhaseInContent(content, escapedPhase, phaseNum, convention) {
|
|
|
170
175
|
.find((candidate) => candidate.level <= currentHeading.level);
|
|
171
176
|
const sectionEnd = nextHeading ? nextHeading.offset : content.length;
|
|
172
177
|
const section = content.slice(headerIndex, sectionEnd).trim();
|
|
173
|
-
// Extract goal if present (supports both **Goal:** and **Goal**: formats)
|
|
174
|
-
|
|
175
|
-
const goal =
|
|
178
|
+
// Extract goal if present (supports both **Goal:** and **Goal**: formats).
|
|
179
|
+
// #4731: multiline-aware — hard-wrapped Goals read past the line break.
|
|
180
|
+
const goal = roadmapParserModule.extractPhaseFieldMultiline(section, 'Goal');
|
|
176
181
|
// Mode: vertical-MVP slice mode flag. Lowercased + trimmed for canonical
|
|
177
182
|
// comparison; unrecognized values are preserved verbatim for forward-compat.
|
|
178
183
|
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
@@ -209,7 +214,8 @@ function searchPhaseInContent(content, escapedPhase, phaseNum, convention) {
|
|
|
209
214
|
* Returns the phase section string if found, null if ROADMAP.md is missing,
|
|
210
215
|
* or throws if ROADMAP.md read fails.
|
|
211
216
|
*
|
|
212
|
-
* Used by
|
|
217
|
+
* Used by the shared gate phase context (lookupRoadmapPhase in gate-phase-context.cts, which
|
|
218
|
+
* computeUiPlanGate in gate-ui-plan.cts calls) so ui-plan-gate uses the SAME
|
|
213
219
|
* phase resolution as `roadmap.get-phase` — not a milestone-only subset.
|
|
214
220
|
*/
|
|
215
221
|
function getRoadmapPhaseWithFallback(cwd, phaseNum) {
|
|
@@ -307,6 +313,14 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
|
|
|
307
313
|
error('Failed to read ROADMAP.md: ' + e.message);
|
|
308
314
|
}
|
|
309
315
|
}
|
|
316
|
+
/**
|
|
317
|
+
* #4757: the phase records whose `roadmap_complete` was READ from a ROADMAP
|
|
318
|
+
* checkbox. Table rows and headings with no checklist entry also report
|
|
319
|
+
* `roadmap_complete: false`, but there is no checkbox behind it, so they must
|
|
320
|
+
* not be compared against disk status by `checkbox_conflict`. A WeakSet keeps
|
|
321
|
+
* this out of the emitted `phases[]` shape.
|
|
322
|
+
*/
|
|
323
|
+
const PHASES_WITH_CHECKBOX = new WeakSet();
|
|
310
324
|
// #612 composes the convention-qualified sentinel reading with upstream's
|
|
311
325
|
// canonical legacy sentinel owner. A reserved bracket milestone OR a reserved
|
|
312
326
|
// phase token excludes the occurrence.
|
|
@@ -370,6 +384,7 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
370
384
|
// The caller needs the exact occurrence identities from the same scan that
|
|
371
385
|
// built `phases`; returning them together also keeps fallback rescans atomic.
|
|
372
386
|
const detailKeys = new Set();
|
|
387
|
+
let statusError;
|
|
373
388
|
while ((match = phasePattern.exec(content)) !== null) {
|
|
374
389
|
const bracketId = G ? match[1] : undefined;
|
|
375
390
|
const phaseNum = match[1 + G];
|
|
@@ -392,15 +407,14 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
392
407
|
const nextHeader = restOfContent.match(new RegExp(`\\n {0,3}#{2,4}\\s+${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.ANY_BRACKET, convention)}[A-Za-z]?\\d[\\d.-]*`, 'i'));
|
|
393
408
|
const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
|
|
394
409
|
const section = content.slice(sectionStart, sectionEnd);
|
|
395
|
-
const
|
|
396
|
-
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
410
|
+
const goal = roadmapParserModule.extractPhaseFieldMultiline(section, 'Goal');
|
|
397
411
|
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
398
412
|
const mode = modeMatch ? modeMatch[1].trim().toLowerCase() : null;
|
|
399
413
|
const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
400
414
|
const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
|
|
401
415
|
// Check completion on disk
|
|
402
416
|
const normalized = normalizePhaseName(phaseNum);
|
|
403
|
-
let diskStatus =
|
|
417
|
+
let diskStatus = phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY;
|
|
404
418
|
let planCount = 0;
|
|
405
419
|
let summaryCount = 0;
|
|
406
420
|
let hasContext = false;
|
|
@@ -444,33 +458,32 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
444
458
|
hasResearch = counts.hasResearch;
|
|
445
459
|
contextReadError = counts.contextReadError;
|
|
446
460
|
contextScope = counts.scope;
|
|
447
|
-
//
|
|
448
|
-
//
|
|
449
|
-
//
|
|
450
|
-
//
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
// resolves and scopes its verification report like its legacy twin.
|
|
456
|
-
const completionResult = isPhaseComplete(node_path_1.default.join(phasesDir, dirMatch), { convention });
|
|
457
|
-
if (completionResult.value.complete)
|
|
458
|
-
diskStatus = 'complete';
|
|
459
|
-
else if (summaryCount > 0)
|
|
460
|
-
diskStatus = 'partial';
|
|
461
|
-
else if (planCount > 0)
|
|
462
|
-
diskStatus = 'planned';
|
|
463
|
-
else if (hasResearch)
|
|
464
|
-
diskStatus = 'researched';
|
|
465
|
-
else if (hasContext)
|
|
466
|
-
diskStatus = 'discussed';
|
|
467
|
-
else
|
|
468
|
-
diskStatus = 'empty';
|
|
461
|
+
// #5060: route the disk-status ladder through the Phase Status Module's
|
|
462
|
+
// single owner (`phaseStatus`/`toDiskStatus`) rather than a local
|
|
463
|
+
// if/else re-derivation. `convention` still threads through so a
|
|
464
|
+
// bracket phase's verification report resolves and scopes like its
|
|
465
|
+
// legacy twin (#612).
|
|
466
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dirMatch), { convention });
|
|
467
|
+
statusError = firstStatusError(statusError, ps.value.statusError);
|
|
468
|
+
diskStatus = (0, phase_status_cjs_1.toDiskStatus)(ps.value.status, { hasResearch, hasContext });
|
|
469
469
|
}
|
|
470
|
-
// Check ROADMAP checkbox status. #
|
|
471
|
-
//
|
|
472
|
-
//
|
|
473
|
-
//
|
|
470
|
+
// Check ROADMAP checkbox status. #4965: line-anchored, label-first — the
|
|
471
|
+
// old fragment allowed `\s*.*` between the checkbox and the label, which
|
|
472
|
+
// let "Phase N" match ANYWHERE in any checklist line's free-form
|
|
473
|
+
// description (not just this phase's own line), and `content.match`
|
|
474
|
+
// returns only the first hit in the document — so an earlier, unrelated
|
|
475
|
+
// line whose prose merely mentioned "Phase N" shadowed that phase's own
|
|
476
|
+
// checkbox state (both a false negative and a false positive were
|
|
477
|
+
// reported against real ROADMAPs). The label must now be the first thing
|
|
478
|
+
// after the checkbox, tolerating only an optional `**` bold marker and
|
|
479
|
+
// horizontal whitespace — matching `cmdRoadmapAnalyze`'s own
|
|
480
|
+
// `checklistPattern` (the missing-detail-section scan a bit further down
|
|
481
|
+
// in this file), which already hard-requires `\*\*` immediately after the
|
|
482
|
+
// checkbox for the same reason. A decoration the old `.*` used to tolerate
|
|
483
|
+
// by accident — a blockquoted bullet, an emoji/badge before the label —
|
|
484
|
+
// now resolves to `roadmap_complete: false` instead of matching; this is
|
|
485
|
+
// the deliberate, precedent-consistent narrowing (see 10-diagnosis.md's
|
|
486
|
+
// "Negative space" note), not a new gap.
|
|
474
487
|
//
|
|
475
488
|
// ADR-3180 §7.4 (disk-strict, #2957, maintainer decision 2026-08-08):
|
|
476
489
|
// `roadmapComplete` is reported below as metadata ONLY — it carries NO
|
|
@@ -481,10 +494,10 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
481
494
|
// checkbox — no passing `*-VERIFICATION.md`, plans outstanding — now
|
|
482
495
|
// reports incomplete; this is the deliberate Tier-2 break (ADR-3180 §7.4
|
|
483
496
|
// Decision 3).
|
|
484
|
-
const checkboxPattern = new RegExp(
|
|
497
|
+
const checkboxPattern = new RegExp(`^[ \\t]*-[ \\t]*\\[(x| )\\][ \\t]*(?:\\*\\*)?${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, convention)}${phaseMarkdownRegexSource(phaseNum)}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'im');
|
|
485
498
|
const checkboxMatch = content.match(checkboxPattern);
|
|
486
499
|
const roadmapComplete = checkboxMatch ? checkboxMatch[1] === 'x' : false;
|
|
487
|
-
|
|
500
|
+
const headingPhase = {
|
|
488
501
|
number: phaseNum,
|
|
489
502
|
name: phaseName,
|
|
490
503
|
goal,
|
|
@@ -498,7 +511,10 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
498
511
|
roadmap_complete: roadmapComplete,
|
|
499
512
|
context_read_error: contextReadError,
|
|
500
513
|
context_scope: contextScope,
|
|
501
|
-
}
|
|
514
|
+
};
|
|
515
|
+
if (checkboxMatch)
|
|
516
|
+
PHASES_WITH_CHECKBOX.add(headingPhase);
|
|
517
|
+
phases.push(headingPhase);
|
|
502
518
|
}
|
|
503
519
|
// #3577: markdown-table row declarations join the enumeration — same
|
|
504
520
|
// enrichment contract as headings (disk counts when the directory exists),
|
|
@@ -512,7 +528,7 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
512
528
|
detailKeys.add(occurrenceKey(tr.id));
|
|
513
529
|
if (seen.has(stripPadA(tr.id)))
|
|
514
530
|
continue;
|
|
515
|
-
const dirMatchA = matchPhaseDirs(phaseDirNames, normalizePhaseName(tr.id)).matches[0];
|
|
531
|
+
const dirMatchA = matchPhaseDirs(phaseDirNames, normalizePhaseName(tr.id), convention).matches[0];
|
|
516
532
|
let tPlanCount = 0;
|
|
517
533
|
let tSummaryCount = 0;
|
|
518
534
|
let tHasContext = false;
|
|
@@ -521,8 +537,9 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
521
537
|
// #4014 (epic #3473 B4): additive sibling, same default rule as the
|
|
522
538
|
// heading-declared branch above.
|
|
523
539
|
let tContextScope = SCOPE.COMPLETE;
|
|
540
|
+
let tDiskStatus = phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY;
|
|
524
541
|
if (dirMatchA) {
|
|
525
|
-
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatchA));
|
|
542
|
+
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatchA), convention);
|
|
526
543
|
tPlanCount = counts.planCount;
|
|
527
544
|
tSummaryCount = counts.summaryCount;
|
|
528
545
|
tHasContext = node_fs_1.default.existsSync(node_path_1.default.join(phasesDir, dirMatchA, 'CONTEXT.md'));
|
|
@@ -533,6 +550,11 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
533
550
|
// an unreadable phase directory is still surfaced via the sibling call.
|
|
534
551
|
tContextReadError = counts.contextReadError;
|
|
535
552
|
tContextScope = counts.scope;
|
|
553
|
+
// #5060: route through the Phase Status Module's owner rather than the
|
|
554
|
+
// fixed 'ok' sentinel this row used to emit.
|
|
555
|
+
const tps = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dirMatchA), { convention });
|
|
556
|
+
statusError = firstStatusError(statusError, tps.value.statusError);
|
|
557
|
+
tDiskStatus = (0, phase_status_cjs_1.toDiskStatus)(tps.value.status, { hasResearch: tHasResearch, hasContext: tHasContext });
|
|
536
558
|
}
|
|
537
559
|
phases.push({
|
|
538
560
|
number: tr.id,
|
|
@@ -544,13 +566,13 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
544
566
|
summary_count: tSummaryCount,
|
|
545
567
|
has_context: tHasContext,
|
|
546
568
|
has_research: tHasResearch,
|
|
547
|
-
disk_status:
|
|
569
|
+
disk_status: tDiskStatus,
|
|
548
570
|
roadmap_complete: false,
|
|
549
571
|
context_read_error: tContextReadError,
|
|
550
572
|
context_scope: tContextScope,
|
|
551
573
|
});
|
|
552
574
|
}
|
|
553
|
-
return { phases, detailKeys };
|
|
575
|
+
return { phases, detailKeys, statusError };
|
|
554
576
|
}
|
|
555
577
|
function cmdRoadmapAnalyze(cwd, raw) {
|
|
556
578
|
const roadmapPath = planningPaths(cwd).roadmap;
|
|
@@ -586,6 +608,8 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
586
608
|
let collected = collectAnalyzePhases(content, phasesDir, _phaseDirNames, convention);
|
|
587
609
|
let phases = collected.phases;
|
|
588
610
|
let detailKeys = collected.detailKeys;
|
|
611
|
+
// #5118: carried from every phaseStatus read this command makes.
|
|
612
|
+
let statusError = collected.statusError;
|
|
589
613
|
// `effectiveContent` is what the downstream checklist scan (missing_details)
|
|
590
614
|
// iterates. Defaults to the scoped window; switched to the fallback document
|
|
591
615
|
// when the recovery path below fires, so a phase found via fallback is not
|
|
@@ -612,6 +636,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
612
636
|
collected = fallbackCollection;
|
|
613
637
|
phases = collected.phases;
|
|
614
638
|
detailKeys = collected.detailKeys;
|
|
639
|
+
statusError = firstStatusError(statusError, collected.statusError);
|
|
615
640
|
effectiveContent = fallbackContent;
|
|
616
641
|
}
|
|
617
642
|
}
|
|
@@ -624,12 +649,12 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
624
649
|
version: m.version,
|
|
625
650
|
}));
|
|
626
651
|
// Find current and next phase
|
|
627
|
-
const currentPhase = phases.find(p => p.disk_status ===
|
|
628
|
-
const nextPhase = phases.find(p => p.disk_status ===
|
|
652
|
+
const currentPhase = phases.find(p => p.disk_status === phase_status_cjs_1.DISK_STATUS.PLANNED || p.disk_status === phase_status_cjs_1.DISK_STATUS.PARTIAL || p.disk_status === phase_status_cjs_1.DISK_STATUS.EXECUTED) || null;
|
|
653
|
+
const nextPhase = phases.find(p => p.disk_status === phase_status_cjs_1.DISK_STATUS.EMPTY || p.disk_status === phase_status_cjs_1.DISK_STATUS.NO_DIRECTORY || p.disk_status === phase_status_cjs_1.DISK_STATUS.DISCUSSED || p.disk_status === phase_status_cjs_1.DISK_STATUS.RESEARCHED) || null;
|
|
629
654
|
// Aggregated stats
|
|
630
655
|
const totalPlans = phases.reduce((sum, p) => sum + p.plan_count, 0);
|
|
631
656
|
const totalSummaries = phases.reduce((sum, p) => sum + p.summary_count, 0);
|
|
632
|
-
const completedPhases = phases.filter(p => p.disk_status ===
|
|
657
|
+
const completedPhases = phases.filter(p => p.disk_status === phase_status_cjs_1.DISK_STATUS.COMPLETE).length;
|
|
633
658
|
// Detect phases in summary list without detail sections (malformed ROADMAP).
|
|
634
659
|
// The char class must allow `-` (not just `.`) so dash-separated milestone-prefixed
|
|
635
660
|
// IDs (e.g. `1-01`) match the detail-heading scanner above; otherwise they truncate
|
|
@@ -658,7 +683,87 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
658
683
|
if (seenChecklistKeys.has(key))
|
|
659
684
|
continue;
|
|
660
685
|
seenChecklistKeys.add(key);
|
|
661
|
-
|
|
686
|
+
// #4899: the checkbox state (`[x]` vs `[ ]`) isn't a capturing group in
|
|
687
|
+
// `checklistPattern` — adding one would shift every downstream `1 + G` /
|
|
688
|
+
// `G` index this loop and `missingDetails` below already depend on.
|
|
689
|
+
// Reading it off the full match text instead is index-safe.
|
|
690
|
+
checklistOccurrences.push({ token, bracketId, checked: /\[x\]/i.test(checklistMatch[0]) });
|
|
691
|
+
}
|
|
692
|
+
// #4899: `collectAnalyzePhases` only recognises `### Phase N:` headings and
|
|
693
|
+
// progress-table rows — the shape `templates/roadmap.md` itself emits
|
|
694
|
+
// (checklist entries only, no detail sections yet) produces `phases: []`
|
|
695
|
+
// even though `checklistOccurrences` above just proved real phases exist.
|
|
696
|
+
// That evidence was computed and then discarded on the way to the final
|
|
697
|
+
// `phases: []` output (ADR-4910 §5/Phase 4). When the heading/table scan
|
|
698
|
+
// found nothing but the checklist did, synthesize a minimal `AnalyzePhase`
|
|
699
|
+
// per non-sentinel checklist occurrence — same enrichment calls
|
|
700
|
+
// (`matchPhaseDirs`, `countPhasePlansAndSummaries`, `isPhaseComplete`) the
|
|
701
|
+
// heading branch of `collectAnalyzePhases` already uses, so a synthesized
|
|
702
|
+
// phase's on-disk status is derived identically, not approximated.
|
|
703
|
+
//
|
|
704
|
+
// `missing_phase_details` is intentionally left untouched by this branch:
|
|
705
|
+
// its contract is "this checklist token has no `### Phase N:` heading or
|
|
706
|
+
// progress-table row", and that remains true for every synthesized token —
|
|
707
|
+
// synthesis fills in `phases[]` from the checklist itself, it does not
|
|
708
|
+
// manufacture a detail section. Removing these tokens from
|
|
709
|
+
// `missing_phase_details` would hide the very malformed-ROADMAP signal
|
|
710
|
+
// ADR-4910 (#4899) treats as evidence to surface, not evidence to launder.
|
|
711
|
+
//
|
|
712
|
+
// Gated on `!hasPhaseListingTableHeader(effectiveContent)` (found by the
|
|
713
|
+
// pre-existing #4480 regression "a status table cannot hide missing phase
|
|
714
|
+
// details"): a `| Phase | Status |`-shaped table with no Name column
|
|
715
|
+
// correctly declares no phases via `collectAnalyzePhases`, but its mere
|
|
716
|
+
// presence signals this roadmap has already moved to table-based tracking —
|
|
717
|
+
// synthesizing phantom phases from checklist entries alongside a
|
|
718
|
+
// deliberately thin tracking table would manufacture phase_count where the
|
|
719
|
+
// roadmap's own structure says "not yet detailed", the same laundering this
|
|
720
|
+
// branch exists to avoid for `missing_phase_details`. #4899's actual target
|
|
721
|
+
// shape (`templates/roadmap.md`: checklist only, no Progress section at
|
|
722
|
+
// all) has no such table and is unaffected.
|
|
723
|
+
if (phases.length === 0 && checklistOccurrences.length > 0 && !hasPhaseListingTableHeader(effectiveContent)) {
|
|
724
|
+
for (const occ of checklistOccurrences) {
|
|
725
|
+
if (isSentinelPhase(occ.token, occ.bracketId))
|
|
726
|
+
continue;
|
|
727
|
+
const normalized = normalizePhaseName(occ.token);
|
|
728
|
+
const dirMatch = matchPhaseDirs(_phaseDirNames, normalized, convention).matches[0];
|
|
729
|
+
let diskStatus = 'no_directory';
|
|
730
|
+
let planCount = 0;
|
|
731
|
+
let summaryCount = 0;
|
|
732
|
+
let hasContext = false;
|
|
733
|
+
let hasResearch = false;
|
|
734
|
+
let contextReadError = null;
|
|
735
|
+
let contextScope = SCOPE.COMPLETE;
|
|
736
|
+
if (dirMatch) {
|
|
737
|
+
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatch), convention);
|
|
738
|
+
planCount = counts.planCount;
|
|
739
|
+
summaryCount = counts.summaryCount;
|
|
740
|
+
hasContext = counts.hasContext;
|
|
741
|
+
hasResearch = counts.hasResearch;
|
|
742
|
+
contextReadError = counts.contextReadError;
|
|
743
|
+
contextScope = counts.scope;
|
|
744
|
+
// #5060: same owner call as the heading branch above.
|
|
745
|
+
const ps = (0, phase_status_cjs_1.phaseStatus)(node_path_1.default.join(phasesDir, dirMatch), { convention });
|
|
746
|
+
statusError = firstStatusError(statusError, ps.value.statusError);
|
|
747
|
+
diskStatus = (0, phase_status_cjs_1.toDiskStatus)(ps.value.status, { hasResearch, hasContext });
|
|
748
|
+
}
|
|
749
|
+
const synthesized = {
|
|
750
|
+
number: occ.token,
|
|
751
|
+
name: `Phase ${occ.token}`,
|
|
752
|
+
goal: null,
|
|
753
|
+
mode: null,
|
|
754
|
+
depends_on: null,
|
|
755
|
+
plan_count: planCount,
|
|
756
|
+
summary_count: summaryCount,
|
|
757
|
+
has_context: hasContext,
|
|
758
|
+
has_research: hasResearch,
|
|
759
|
+
disk_status: diskStatus,
|
|
760
|
+
roadmap_complete: occ.checked,
|
|
761
|
+
context_read_error: contextReadError,
|
|
762
|
+
context_scope: contextScope,
|
|
763
|
+
};
|
|
764
|
+
PHASES_WITH_CHECKBOX.add(synthesized);
|
|
765
|
+
phases.push(synthesized);
|
|
766
|
+
}
|
|
662
767
|
}
|
|
663
768
|
// The EMITTED value stays the bare token, unchanged: `phases[].number` is a
|
|
664
769
|
// token under every convention, and `missing_phase_details` is read against
|
|
@@ -701,6 +806,29 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
701
806
|
const progressPercent = progressScope === SCOPE.COMPLETE
|
|
702
807
|
? (0, phase_lifecycle_cjs_1.clampPercent)(scopedTotalSummaries, scopedTotalPlans)
|
|
703
808
|
: null;
|
|
809
|
+
// #4757: `current_phase` / `next_phase` / `completed_phases` are disk-
|
|
810
|
+
// authoritative (ADR-3180 §7.4, #2957 — a ticked ROADMAP checkbox carries no
|
|
811
|
+
// machine authority), so a phase whose checkbox disagrees with its disk
|
|
812
|
+
// status can be handed back as the phase to work on while the operator has
|
|
813
|
+
// already ticked it (a backfilled phase with a SUMMARY and no PLAN reads
|
|
814
|
+
// `empty`; an all-summarized phase with no VERIFICATION reads `executed`), or
|
|
815
|
+
// withheld from `completed_phases` while ticked-complete. The selectors stay
|
|
816
|
+
// disk-authoritative; the disagreement is SURFACED here rather than silently
|
|
817
|
+
// resolved either way. Computed after the heading/table/checklist phase
|
|
818
|
+
// enumeration is final so synthesized phases are covered too. Only phases
|
|
819
|
+
// that HAVE a checkbox can conflict: a progress-table row or a heading with
|
|
820
|
+
// no checklist entry reports `roadmap_complete: false` because there is no
|
|
821
|
+
// checkbox to read, not because one is unticked.
|
|
822
|
+
const checkboxConflict = phases
|
|
823
|
+
.filter(p => PHASES_WITH_CHECKBOX.has(p)
|
|
824
|
+
&& p.roadmap_complete !== (p.disk_status === phase_status_cjs_1.DISK_STATUS.COMPLETE))
|
|
825
|
+
.map(p => ({
|
|
826
|
+
number: p.number,
|
|
827
|
+
roadmap_complete: p.roadmap_complete,
|
|
828
|
+
disk_status: p.disk_status,
|
|
829
|
+
plan_count: p.plan_count,
|
|
830
|
+
summary_count: p.summary_count,
|
|
831
|
+
}));
|
|
704
832
|
const result = {
|
|
705
833
|
milestones,
|
|
706
834
|
phases,
|
|
@@ -728,6 +856,9 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
728
856
|
progress_scope: progressScope,
|
|
729
857
|
current_phase: currentPhase ? currentPhase.number : null,
|
|
730
858
|
next_phase: nextPhase ? nextPhase.number : null,
|
|
859
|
+
// #4757: phases whose ROADMAP checkbox disagrees with `disk_status`
|
|
860
|
+
// (`roadmap_complete` vs `disk_status === "complete"`); always an array.
|
|
861
|
+
checkbox_conflict: checkboxConflict,
|
|
731
862
|
missing_phase_details: missingDetails.length > 0 ? missingDetails : null,
|
|
732
863
|
// #3184/#3165: distinguishes a genuinely empty milestone (`scope:
|
|
733
864
|
// "complete"`, `phase_count: 0`) from a window that could not be fully
|
|
@@ -735,6 +866,10 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
735
866
|
// were previously output-identical.
|
|
736
867
|
scope,
|
|
737
868
|
};
|
|
869
|
+
// #5118: a read-only aggregate over a refused report prints nothing and
|
|
870
|
+
// fails with the error's own reason.
|
|
871
|
+
if (statusError)
|
|
872
|
+
failOnVerificationStatusError(statusError);
|
|
738
873
|
output(result, raw, undefined);
|
|
739
874
|
}
|
|
740
875
|
// ─── cmdRoadmapMilestoneScope ────────────────────────────────────────────────
|
|
@@ -778,11 +913,11 @@ function cmdRoadmapMilestoneScope(cwd, raw) {
|
|
|
778
913
|
phaseIdConvention = undefined;
|
|
779
914
|
}
|
|
780
915
|
if (phaseIdConvention === undefined || phaseIdConvention === null) {
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
const
|
|
784
|
-
if (
|
|
785
|
-
const kvMatch =
|
|
916
|
+
// The block is the one the one fence owner finds (`frontmatterRegion`), the same one
|
|
917
|
+
// `roadmap validate` reads, so the two cannot disagree on where it ends (#3641 NEW-1).
|
|
918
|
+
const found = frontmatterRegion(rawContent);
|
|
919
|
+
if (found?.terminated) {
|
|
920
|
+
const kvMatch = found.region.match(/^phase_id_convention:\s*(.*)$/m);
|
|
786
921
|
if (kvMatch) {
|
|
787
922
|
const val = kvMatch[1].trim();
|
|
788
923
|
if (val !== 'null' && val !== '') {
|
|
@@ -839,11 +974,20 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
839
974
|
if (!phaseInfo) {
|
|
840
975
|
error(`Phase ${phaseNum} not found`);
|
|
841
976
|
}
|
|
842
|
-
const
|
|
843
|
-
//
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
|
|
977
|
+
const phaseDir = node_path_1.default.join(cwd, phaseInfo.directory);
|
|
978
|
+
// #5060: ONE count source for this verb — `scanPhasePlans` (the ADR-3180
|
|
979
|
+
// §7.5 count owner) — rather than `phaseInfo.plans.length` /
|
|
980
|
+
// `countMatchedSummaries(phaseInfo.plans, phaseInfo.summaries)`. The two
|
|
981
|
+
// diverge when a plan's frontmatter declares `status: superseded`:
|
|
982
|
+
// scanPhasePlans excludes it (and its summary) from both counts (#2349),
|
|
983
|
+
// the phaseInfo-based counts do not. Using scanPhasePlans everywhere in
|
|
984
|
+
// this verb — the `planCount === 0` decline, the `Plans Complete` cell,
|
|
985
|
+
// the `Plans:` line, the checkbox gate, and the JSON output — keeps every
|
|
986
|
+
// one of those in agreement with the Status-cell ladder below, which
|
|
987
|
+
// already read scanPhasePlans's counts.
|
|
988
|
+
const coverageScan = scanPhasePlans(phaseDir);
|
|
989
|
+
const planCount = coverageScan.planCount;
|
|
990
|
+
const summaryCount = coverageScan.summaryCount;
|
|
847
991
|
if (planCount === 0) {
|
|
848
992
|
declineNoOp(raw, 'updated', 'No plans found', 'roadmap update-plan-progress skipped — no plans found for this phase. ROADMAP.md was left unchanged.', { plan_count: 0, summary_count: 0 });
|
|
849
993
|
return;
|
|
@@ -860,12 +1004,15 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
860
1004
|
// agrees with `roadmap analyze` / `init manager` / `phase complete` for
|
|
861
1005
|
// the same phase (ADR-3180 §7.4's headline: one predicate for the read
|
|
862
1006
|
// path and the write path).
|
|
863
|
-
const phaseDir = node_path_1.default.join(cwd, phaseInfo.directory);
|
|
864
1007
|
// ADR-3180 §7.4 read/write-path symmetry with the threaded site at ~583:
|
|
865
1008
|
// thread convention here too, so this write path's completion reading
|
|
866
1009
|
// agrees with the read path's under the bracket convention.
|
|
867
1010
|
const convention = resolvePhaseIdConvention(cwd);
|
|
868
1011
|
const completionResult = isPhaseComplete(phaseDir, { convention });
|
|
1012
|
+
// #5118: no write before the error — a report whose `status` is outside the
|
|
1013
|
+
// closed set fails this writer before ROADMAP.md is touched.
|
|
1014
|
+
if (completionResult.value.statusError)
|
|
1015
|
+
failOnVerificationStatusError(completionResult.value.statusError);
|
|
869
1016
|
const verificationResult = completionResult.value.verification;
|
|
870
1017
|
// #2648 precedent, applied at this write site (ADR-3180 §7.4 / #3186):
|
|
871
1018
|
// `isPhaseComplete` deliberately carries NO plan-count precondition — the
|
|
@@ -881,14 +1028,29 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
881
1028
|
// (phase.cts:~1995, #2648: "a coverage gate that passes when it cannot
|
|
882
1029
|
// read the plans is no gate at all"), composed explicitly here rather than
|
|
883
1030
|
// folded into the predicate: complete AND all plans executed.
|
|
884
|
-
|
|
1031
|
+
// #5060: `coverageScan` (declared above, before the `planCount === 0`
|
|
1032
|
+
// decline) is now this verb's ONE count source — the `Plans Complete`
|
|
1033
|
+
// cell, the `Plans:` line, the checkbox gate, the JSON output, and the
|
|
1034
|
+
// Status-cell ladder below all read `planCount`/`summaryCount` from it, so
|
|
1035
|
+
// a phase with a `status: superseded` plan (#2349) reports the same
|
|
1036
|
+
// counts everywhere instead of diverging between the ladder and the rest
|
|
1037
|
+
// of this verb's output.
|
|
885
1038
|
const unsummarizedPlans = findUnsummarizedPlans(coverageScan.planFiles, coverageScan.summaryFiles);
|
|
886
1039
|
const isComplete = completionResult.value.complete && unsummarizedPlans.length === 0;
|
|
887
1040
|
// #3057 B3: routing above is unchanged (an indeterminate staleness check
|
|
888
1041
|
// still routes as if nothing were stale) — this only makes the fact visible
|
|
889
1042
|
// to whatever reads this command's JSON output.
|
|
890
|
-
const verificationStaleCheckIndeterminate = verificationResult.staleCheckIndeterminate === true;
|
|
891
|
-
|
|
1043
|
+
const verificationStaleCheckIndeterminate = 'staleCheckIndeterminate' in verificationResult && verificationResult.staleCheckIndeterminate === true;
|
|
1044
|
+
// #5060: route the Status-cell token through the Phase Status Module's
|
|
1045
|
+
// owner ladder rather than a local if/else, over the same `planCount`/
|
|
1046
|
+
// `summaryCount` (from `coverageScan`) every other output in this verb
|
|
1047
|
+
// uses.
|
|
1048
|
+
const status = (0, phase_status_cjs_1.toRoadmapStatusCell)((0, phase_status_cjs_1.phaseStatusFromFacts)({
|
|
1049
|
+
planCount,
|
|
1050
|
+
summaryCount,
|
|
1051
|
+
complete: isComplete,
|
|
1052
|
+
verificationStatus: verificationResult.status,
|
|
1053
|
+
}));
|
|
892
1054
|
const today = clock_cjs_1.realClock.localToday();
|
|
893
1055
|
if (!node_fs_1.default.existsSync(roadmapPath)) {
|
|
894
1056
|
declineNoOp(raw, 'updated', 'ROADMAP.md not found', 'roadmap update-plan-progress skipped — ROADMAP.md not found.', { plan_count: planCount, summary_count: summaryCount });
|
|
@@ -923,10 +1085,19 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
923
1085
|
let roadmapContent = originalContent;
|
|
924
1086
|
const phasePattern = phaseMarkdownRegexSource(phaseNum);
|
|
925
1087
|
// #4247: ONE local source for the ATX phase-heading anchor that every
|
|
926
|
-
// section-scoped writer below (`
|
|
1088
|
+
// section-scoped writer below (`planSectionPattern`,
|
|
927
1089
|
// `insertRowsPatternA|B`) starts with — extracted so the target-detection
|
|
928
1090
|
// gate below reads the SAME grammar the writers anchor on, and a future
|
|
929
1091
|
// edit to one cannot drift from the other three copies.
|
|
1092
|
+
// #4906 Phase 5 (#4984): NOT migrated onto the heading-baseline selector —
|
|
1093
|
+
// this write-path anchor was never one of the five ADR-4910 §8 call sites
|
|
1094
|
+
// this phase owns (init.cts/milestone.cts's buildPhaseHeadingScanRegex
|
|
1095
|
+
// sites); a direct any-bracket selector call here would have added a
|
|
1096
|
+
// fourth uncounted ANY_BRACKET consumer to roadmap.cts's pinned
|
|
1097
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census. Left
|
|
1098
|
+
// bracket-blind deliberately, matching this call's pre-#4984 behavior;
|
|
1099
|
+
// folding it into the census is Phase 6 work.
|
|
1100
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
930
1101
|
const phaseHeadingAnchor = `#{2,4}\\s*Phase\\s+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}(?=[:\\s])`;
|
|
931
1102
|
// #4247: target detection runs against the ORIGINAL content's active
|
|
932
1103
|
// (post-</details>) region — the same milestone scoping every writer
|
|
@@ -936,7 +1107,7 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
936
1107
|
const gateActiveRegion = gateDetailsClose === -1
|
|
937
1108
|
? originalContent
|
|
938
1109
|
: originalContent.slice(gateDetailsClose + '</details>'.length);
|
|
939
|
-
// Heading target: the exact grammar `
|
|
1110
|
+
// Heading target: the exact grammar `planSectionPattern` /
|
|
940
1111
|
// `insertRowsPatternA|B` anchor on (an ATX phase heading for this phase).
|
|
941
1112
|
const headingTargetFound = new RegExp(phaseHeadingAnchor, 'i').test(gateActiveRegion);
|
|
942
1113
|
// Checklist target: when the phase is complete, its own checklist bullet
|
|
@@ -970,7 +1141,29 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
970
1141
|
text = plansResult.value;
|
|
971
1142
|
tableRowFound = true;
|
|
972
1143
|
}
|
|
973
|
-
|
|
1144
|
+
// #4925: the verb owns the Status cell's leading status TOKEN only — the
|
|
1145
|
+
// same split #2853/#3584 made for the `Plans:` line below. It used to
|
|
1146
|
+
// overwrite the whole cell with ` ${status.padEnd(11)}` (no trailing
|
|
1147
|
+
// space), discarding any operator prose kept after the token. Three arms:
|
|
1148
|
+
// 1. empty or a dash placeholder → write the token, padded exactly as
|
|
1149
|
+
// phase complete writes ` Complete ` (phase.cts).
|
|
1150
|
+
// 2. a leading status token → rewrite the token only and keep the prose
|
|
1151
|
+
// after it; an unchanged token leaves the cell byte-identical.
|
|
1152
|
+
// 3. freeform prose with no leading token → operator-owned, untouched.
|
|
1153
|
+
// #5060: routed through the Phase Status Module's `matchRoadmapStatusCell`
|
|
1154
|
+
// owner rather than a local statusTokenRe. `ROADMAP_STATUS_TOKEN.DEFERRED`
|
|
1155
|
+
// is now a recognized token (the old regex never matched "deferred" at
|
|
1156
|
+
// all, so a Deferred cell fell through to arm 3 unchanged) — the DEFERRED
|
|
1157
|
+
// guard below preserves that same "leave it alone" outcome explicitly.
|
|
1158
|
+
const statusResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Status', (current) => {
|
|
1159
|
+
if (current === '' || /^[-–—]$/.test(current))
|
|
1160
|
+
return ` ${status.padEnd(11)} `;
|
|
1161
|
+
const m = (0, phase_status_cjs_1.matchRoadmapStatusCell)(current);
|
|
1162
|
+
if (!m || m.token === phase_status_cjs_1.ROADMAP_STATUS_TOKEN.DEFERRED || m.text === status)
|
|
1163
|
+
return current;
|
|
1164
|
+
const rest = current.slice(m.text.length);
|
|
1165
|
+
return rest === '' ? ` ${status.padEnd(11)} ` : ` ${(0, markdown_table_cjs_1.escapeCell)(status + rest)} `;
|
|
1166
|
+
});
|
|
974
1167
|
if (statusResult.ok) {
|
|
975
1168
|
text = statusResult.value;
|
|
976
1169
|
tableRowFound = true;
|
|
@@ -987,7 +1180,9 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
987
1180
|
if (isComplete) {
|
|
988
1181
|
return dateShape.test(current.trim()) ? current : ` ${today} `;
|
|
989
1182
|
}
|
|
990
|
-
|
|
1183
|
+
// #4925: only a stale ISO completion date — this verb's own stamp — is
|
|
1184
|
+
// cleared; a `-` placeholder or operator text stays byte-identical.
|
|
1185
|
+
return dateShape.test(current) ? ' ' : current;
|
|
991
1186
|
});
|
|
992
1187
|
if (completedResult.ok) {
|
|
993
1188
|
text = completedResult.value;
|
|
@@ -1033,7 +1228,14 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1033
1228
|
// `_match` unchanged. An untouched first line cannot orphan its own
|
|
1034
1229
|
// continuation on the next line, since the pattern never spans past
|
|
1035
1230
|
// `\n` in the first place.
|
|
1036
|
-
|
|
1231
|
+
// #4906 Phase 2 (#4917/ADR-4910): migrated off the one-capture-group
|
|
1232
|
+
// regex that replaced to end of line onto the PlanningDoc `boldField`
|
|
1233
|
+
// write seam. `planSectionPattern` scopes the match to phase N's OWN
|
|
1234
|
+
// detail section (heading + body up to the next heading) — the same
|
|
1235
|
+
// window `phaseHeadingAnchor`'s siblings anchor on — and the callback
|
|
1236
|
+
// below runs parse -> classify -> (maybe) mutate -> serialize entirely
|
|
1237
|
+
// within that section text, mirroring phase.cts's `writePlansField`.
|
|
1238
|
+
const planSectionPattern = new RegExp(`${phaseHeadingAnchor}(?:(?!\\n#{1,4}\\s)[\\s\\S])*`, 'i');
|
|
1037
1239
|
const planCountText = isComplete
|
|
1038
1240
|
? `${summaryCount}/${planCount} plans complete`
|
|
1039
1241
|
: `${summaryCount}/${planCount} plans executed`;
|
|
@@ -1042,24 +1244,107 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1042
1244
|
// gsd-core/templates/roadmap.md actually ships, not to "anything in
|
|
1043
1245
|
// brackets" — a bracketed human annotation like `[Deferred pending
|
|
1044
1246
|
// re-scope]` is structurally bracketed too but carries none of this
|
|
1045
|
-
// wording, so it correctly falls through to arm 3 untouched.
|
|
1247
|
+
// wording, so it correctly falls through to arm 3 untouched. Kept as a
|
|
1248
|
+
// caller-side classifier (NOT seam grammar) reused below against the
|
|
1249
|
+
// `boldField` node's own parsed `value`.
|
|
1046
1250
|
const isTemplatePlaceholder = (value) => {
|
|
1047
1251
|
const trimmed = value.trim();
|
|
1048
1252
|
return /^\[\s*Number of plans\b[\s\S]*\]$/i.test(trimmed);
|
|
1049
1253
|
};
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1254
|
+
// Positive detectors for the two "real count token" shapes (#2853 /
|
|
1255
|
+
// #3584 Finding B): a fraction count (`N/M plans complete|executed`) or a
|
|
1256
|
+
// bare singular/plural count (`N plan`/`N plans`, the fresh single-plan
|
|
1257
|
+
// template shape at gsd-core/templates/roadmap.md:62). Either one is an
|
|
1258
|
+
// existing count token to overwrite (arm 1), never template placeholder
|
|
1259
|
+
// (arm 2) or freeform prose (arm 3).
|
|
1260
|
+
// #4906 regression fix: PREFIX match (not full-string) — a real count
|
|
1261
|
+
// token may have a glued-on annotation with no ` — ` separator (e.g.
|
|
1262
|
+
// `0/1 plans executed (11-16 are gap closure from VERIFICATION)`), which
|
|
1263
|
+
// `parseBoldFieldLine`'s em-dash-only trailing-content split leaves
|
|
1264
|
+
// entirely inside `value` (TRAILING_SEPARATOR_RE in planning-document.cts
|
|
1265
|
+
// is unchanged and correct — this is a caller-side classification fix,
|
|
1266
|
+
// not a seam fix). Returns the matched prefix length, or -1 if no match.
|
|
1267
|
+
const fractionCountPrefixLength = (value) => {
|
|
1268
|
+
const m = value.match(/^\d+\s*\/\s*\d+\s+plans(?:\s+(?:complete|executed))?/i);
|
|
1269
|
+
return m ? m[0].length : -1;
|
|
1270
|
+
};
|
|
1271
|
+
const bareCountPrefixLength = (value) => {
|
|
1272
|
+
const m = value.match(/^\d+\s+plans?/i);
|
|
1273
|
+
return m ? m[0].length : -1;
|
|
1274
|
+
};
|
|
1275
|
+
roadmapContent = replaceInCurrentMilestone(roadmapContent, planSectionPattern, (sectionText) => {
|
|
1276
|
+
const parsed = (0, planning_document_cjs_1.parsePlanningDoc)(sectionText, 'ROADMAP.md');
|
|
1277
|
+
if (!parsed.ok) {
|
|
1278
|
+
// Unreadable section (e.g. an unterminated frontmatter fence) —
|
|
1279
|
+
// leave it byte-identical rather than throwing.
|
|
1280
|
+
return sectionText;
|
|
1054
1281
|
}
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1282
|
+
const fieldId = (0, planning_document_cjs_1.findField)(parsed.value, 'Plans');
|
|
1283
|
+
if (!fieldId) {
|
|
1284
|
+
// #4906 regression (#1163, caught by gsd-test): a hand-edited or
|
|
1285
|
+
// pre-template ROADMAP.md may carry a PLAIN (non-bold) `Plans:` line
|
|
1286
|
+
// rather than the canonical `**Plans**:`/`**Plans:**` bold field.
|
|
1287
|
+
// BOLD_FIELD_RE is deliberately bold-only (widening it to any bare
|
|
1288
|
+
// `Label:` would register ordinary prose like "Note: see below" as a
|
|
1289
|
+
// spurious field seam-wide) — this is domain knowledge about ONE
|
|
1290
|
+
// field's legacy tolerated shape, the same class of thing
|
|
1291
|
+
// `isTemplatePlaceholder` above already keeps caller-side rather
|
|
1292
|
+
// than seam grammar, so the fallback lives here, not in
|
|
1293
|
+
// planning-document.cts.
|
|
1294
|
+
const plainMatch = sectionText.match(/^([ \t]*)Plans:([ \t]*)([^\r\n]*)$/m);
|
|
1295
|
+
if (!plainMatch) {
|
|
1296
|
+
// No `**Plans:**`/`**Plans**:`/plain `Plans:` line in this phase's
|
|
1297
|
+
// own section — nothing to write; not a failure (mirrors the old
|
|
1298
|
+
// regex's silent no-match no-op).
|
|
1299
|
+
return sectionText;
|
|
1300
|
+
}
|
|
1301
|
+
const [whole, indent, spacing, plainValue] = plainMatch;
|
|
1302
|
+
const plainFractionLen = fractionCountPrefixLength(plainValue);
|
|
1303
|
+
const plainBareLen = bareCountPrefixLength(plainValue);
|
|
1304
|
+
const plainCountPrefixLen = plainFractionLen >= 0 ? plainFractionLen : plainBareLen;
|
|
1305
|
+
if (plainCountPrefixLen < 0 && !isTemplatePlaceholder(plainValue)) {
|
|
1306
|
+
// Arm 3: freeform prose, TBD, a bracketed human annotation, or an
|
|
1307
|
+
// empty value — leave the section exactly as it was.
|
|
1308
|
+
return sectionText;
|
|
1309
|
+
}
|
|
1310
|
+
const plainSuffix = plainCountPrefixLen >= 0 ? plainValue.slice(plainCountPrefixLen) : '';
|
|
1311
|
+
const newPlainLine = `${indent}Plans:${spacing}${planCountText}${plainSuffix}`;
|
|
1312
|
+
const start = plainMatch.index ?? sectionText.indexOf(whole);
|
|
1313
|
+
return sectionText.slice(0, start) + newPlainLine + sectionText.slice(start + whole.length);
|
|
1314
|
+
}
|
|
1315
|
+
const current = (0, planning_document_cjs_1.readNode)(parsed.value, fieldId);
|
|
1316
|
+
if (!current.ok) {
|
|
1317
|
+
return sectionText;
|
|
1318
|
+
}
|
|
1319
|
+
const currentValue = current.value;
|
|
1320
|
+
const fractionPrefixLen = fractionCountPrefixLength(currentValue);
|
|
1321
|
+
const barePrefixLen = bareCountPrefixLength(currentValue);
|
|
1322
|
+
const countPrefixLen = fractionPrefixLen >= 0 ? fractionPrefixLen : barePrefixLen;
|
|
1323
|
+
if (countPrefixLen < 0 && !isTemplatePlaceholder(currentValue)) {
|
|
1324
|
+
// Arm 3: freeform prose, TBD, a bracketed human annotation, or an
|
|
1325
|
+
// empty value — leave the section exactly as it was.
|
|
1326
|
+
return sectionText;
|
|
1058
1327
|
}
|
|
1059
|
-
// Arm
|
|
1060
|
-
//
|
|
1061
|
-
//
|
|
1062
|
-
|
|
1328
|
+
// Arm 1 (real count token, possibly with a glued-on no-separator
|
|
1329
|
+
// annotation re-attached verbatim as `suffix`) or arm 2 (fresh-template
|
|
1330
|
+
// placeholder, whole value replaced): write the computed count.
|
|
1331
|
+
// `setFieldValue`'s valueSpan/trailingSpan split additionally preserves
|
|
1332
|
+
// any EM-DASH-separated trailing annotation (#2853) automatically — no
|
|
1333
|
+
// separate "preserve trailing" branch needed for that shape.
|
|
1334
|
+
const suffix = countPrefixLen >= 0 ? currentValue.slice(countPrefixLen) : '';
|
|
1335
|
+
const newValueToWrite = planCountText + suffix;
|
|
1336
|
+
const staged = (0, planning_document_cjs_1.setFieldValue)(parsed.value, fieldId, newValueToWrite);
|
|
1337
|
+
if (!staged.ok) {
|
|
1338
|
+
return sectionText;
|
|
1339
|
+
}
|
|
1340
|
+
const out = (0, planning_document_cjs_1.serialize)(staged.value);
|
|
1341
|
+
if (!out.ok) {
|
|
1342
|
+
// `hasUnreadableNodes` refusal (ADR-4910 amendment) — a ragged
|
|
1343
|
+
// SIBLING node elsewhere in this same section refuses the whole
|
|
1344
|
+
// splice. Never throw; leave the section unchanged.
|
|
1345
|
+
return sectionText;
|
|
1346
|
+
}
|
|
1347
|
+
return out.value;
|
|
1063
1348
|
});
|
|
1064
1349
|
// If complete: check checkbox
|
|
1065
1350
|
if (isComplete) {
|
|
@@ -1067,10 +1352,17 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1067
1352
|
roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
|
|
1068
1353
|
}
|
|
1069
1354
|
// Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md", "- [ ] 50-01:", or "- [ ] **50-01**")
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1355
|
+
// #4741: tick only plans the phase's own COUNT still counts. `plans` is the
|
|
1356
|
+
// superseded-filtered set (#2349 via scanPhasePlans) while `summaries` is
|
|
1357
|
+
// the raw *-SUMMARY.md listing — a superseded plan can carry a SUMMARY
|
|
1358
|
+
// (e.g. `status: halted`), and ticking it read as "executed" right under a
|
|
1359
|
+
// count line that excludes it. The prefix match mirrors the checkbox regex
|
|
1360
|
+
// below (rows match by planId prefix, which the PLAN-01.md naming shape
|
|
1361
|
+
// relies on), so non-superseded plans tick exactly as before.
|
|
1362
|
+
const tickableSummaries = phaseInfo.summaries
|
|
1363
|
+
.map((summaryFile) => ({ summaryFile, planId: summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '') }))
|
|
1364
|
+
.filter(({ planId }) => planId !== '' && phaseInfo.plans.some((planFile) => planFile.startsWith(planId)));
|
|
1365
|
+
for (const { planId } of tickableSummaries) {
|
|
1074
1366
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
|
|
1075
1367
|
const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
|
|
1076
1368
|
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
|
@@ -1094,9 +1386,26 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1094
1386
|
// Detection is scoped to the active region so a plan that appears in an
|
|
1095
1387
|
// archived <details> block is still correctly detected as missing from the
|
|
1096
1388
|
// active milestone section.
|
|
1389
|
+
//
|
|
1390
|
+
// #4786: a plan row may be written WITH the `-PLAN.md` suffix (canonical
|
|
1391
|
+
// template form) or WITHOUT it (hand-written form: `- [x] 659-01 — desc`).
|
|
1392
|
+
// The tick loop above keys on the bare planId stem with a PREFIX match
|
|
1393
|
+
// (pre-existing, out of scope here), while detection required the full
|
|
1394
|
+
// `-PLAN.md` filename — so every suffix-less row was counted missing and
|
|
1395
|
+
// the insertion fired BESIDE the recognized list — 32 checkbox lines for
|
|
1396
|
+
// 16 plans, exit 0. The stem arm below accepts the bare id only up to a
|
|
1397
|
+
// boundary (whitespace / `:` / dashes / `-PLAN.md` / `.md` / `**` / `)`),
|
|
1398
|
+
// so `5-011` never satisfies `5-01`. Deliberately NOT in the boundary set:
|
|
1399
|
+
// `.` — a dotted sub-id (`5-01.5`) is a real distinct plan, and counting
|
|
1400
|
+
// its row as 5-01's presence would suppress a genuine insertion.
|
|
1097
1401
|
const missingPlans = phaseInfo.plans.filter((planFile) => {
|
|
1098
1402
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planFile);
|
|
1099
|
-
|
|
1403
|
+
if (new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${planEscaped}`, 'i').test(activeRegion))
|
|
1404
|
+
return false;
|
|
1405
|
+
const stem = planFile.replace(/-PLAN\.md$/i, '');
|
|
1406
|
+
const stemEscaped = (0, pattern_cjs_1.escapeRegex)(stem);
|
|
1407
|
+
const stemPresent = new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${stemEscaped}(?=$|\\s|:|—|–|-PLAN\\.md|\\.md|\\*\\*|\\))`, 'i').test(activeRegion);
|
|
1408
|
+
return !stemPresent;
|
|
1100
1409
|
});
|
|
1101
1410
|
if (missingPlans.length > 0) {
|
|
1102
1411
|
// Insert missing plan checklist rows (#1163). We prefer to anchor to the
|
|
@@ -1139,10 +1448,9 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1139
1448
|
if (withRows !== roadmapContent) {
|
|
1140
1449
|
roadmapContent = withRows;
|
|
1141
1450
|
// Mark any newly-inserted rows that already have summaries as complete
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
continue;
|
|
1451
|
+
// (#4741: same superseded-filtered tick list as the loop above — a
|
|
1452
|
+
// pre-existing superseded row must stay unchecked on this path too).
|
|
1453
|
+
for (const { planId } of tickableSummaries) {
|
|
1146
1454
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
|
|
1147
1455
|
const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
|
|
1148
1456
|
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
|
@@ -1299,6 +1607,15 @@ function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
|
|
|
1299
1607
|
// #3537: padding-tolerant fragment so the caller's resolved padded id
|
|
1300
1608
|
// matches un-padded ROADMAP headings.
|
|
1301
1609
|
const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
|
|
1610
|
+
// #4906 Phase 5 (#4984): NOT migrated onto the heading-baseline selector —
|
|
1611
|
+
// this site was never one of the five ADR-4910 §8 call sites this phase
|
|
1612
|
+
// owns (init.cts/milestone.cts's buildPhaseHeadingScanRegex sites); a
|
|
1613
|
+
// direct any-bracket selector call here would have added a fifth
|
|
1614
|
+
// uncounted ANY_BRACKET consumer to roadmap.cts's pinned
|
|
1615
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census. Left
|
|
1616
|
+
// bracket-blind deliberately, matching this call's pre-#4984 behavior;
|
|
1617
|
+
// folding it into the census is Phase 6 work.
|
|
1618
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
1302
1619
|
const phaseHeaderPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}:[^\\n]*)`, 'i');
|
|
1303
1620
|
const phaseMatch = content.match(phaseHeaderPattern);
|
|
1304
1621
|
if (!phaseMatch)
|