@opengsd/gsd-core 1.13.0 → 1.15.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/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-advisor-researcher.compact.md +85 -0
- package/agents/gsd-ai-researcher.compact.md +96 -0
- package/agents/gsd-assumptions-analyzer.compact.md +81 -0
- package/agents/gsd-code-fixer.compact.md +459 -0
- package/agents/gsd-code-fixer.md +9 -8
- package/agents/gsd-code-reviewer.compact.md +269 -0
- package/agents/gsd-code-reviewer.md +15 -3
- package/agents/gsd-codebase-mapper.compact.md +760 -0
- package/agents/gsd-debug-session-manager.compact.md +360 -0
- package/agents/gsd-debug-session-manager.md +17 -2
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-doc-classifier.compact.md +192 -0
- package/agents/gsd-doc-synthesizer.compact.md +200 -0
- package/agents/gsd-doc-verifier.compact.md +143 -0
- package/agents/gsd-doc-writer.compact.md +440 -0
- package/agents/gsd-dom-verifier.compact.md +138 -0
- package/agents/gsd-domain-researcher.compact.md +141 -0
- package/agents/gsd-eval-auditor.compact.md +160 -0
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-eval-planner.compact.md +137 -0
- package/agents/gsd-executor.md +13 -8
- package/agents/gsd-framework-selector.compact.md +82 -0
- package/agents/gsd-integration-checker.compact.md +245 -0
- package/agents/gsd-intel-updater.compact.md +226 -0
- package/agents/gsd-intel-updater.md +1 -1
- package/agents/gsd-mempalace-curator.compact.md +45 -0
- package/agents/gsd-nyquist-auditor.compact.md +179 -0
- package/agents/gsd-pattern-mapper.compact.md +275 -0
- package/agents/gsd-phase-researcher.md +19 -11
- package/agents/gsd-plan-checker.md +8 -7
- package/agents/gsd-planner.md +12 -8
- package/agents/gsd-project-researcher.compact.md +587 -0
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +212 -0
- package/agents/gsd-research-synthesizer.md +1 -1
- package/agents/gsd-roadmapper.compact.md +454 -0
- package/agents/gsd-roadmapper.md +13 -0
- package/agents/gsd-security-auditor.compact.md +162 -0
- package/agents/gsd-ui-auditor.compact.md +404 -0
- package/agents/gsd-ui-auditor.md +155 -17
- package/agents/gsd-ui-checker.compact.md +277 -0
- package/agents/gsd-ui-researcher.compact.md +282 -0
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-user-profiler.compact.md +108 -0
- package/agents/gsd-verifier.md +10 -9
- package/bin/install.js +848 -163
- package/commands/gsd/autonomous.md +2 -2
- package/commands/gsd/capture.md +1 -1
- package/commands/gsd/cleanup.md +1 -0
- package/commands/gsd/code-review.md +2 -1
- package/commands/gsd/complete-milestone.md +1 -0
- package/commands/gsd/config.md +1 -0
- package/commands/gsd/debug.md +1 -0
- package/commands/gsd/graphify.md +1 -0
- package/commands/gsd/health.md +1 -0
- package/commands/gsd/mempalace-capture.md +8 -3
- package/commands/gsd/mempalace-recall.md +1 -0
- package/commands/gsd/new-milestone.md +1 -0
- package/commands/gsd/new-project.md +1 -0
- package/commands/gsd/next.md +1 -0
- package/commands/gsd/pause-work.md +1 -0
- package/commands/gsd/phase.md +1 -0
- package/commands/gsd/plan-review-convergence.md +6 -6
- package/commands/gsd/pr-branch.md +1 -0
- package/commands/gsd/progress.md +1 -1
- package/commands/gsd/quick-batch.md +1 -1
- package/commands/gsd/resume-work.md +1 -0
- package/commands/gsd/review-backlog.md +1 -0
- package/commands/gsd/review.md +2 -3
- package/commands/gsd/settings.md +2 -1
- package/commands/gsd/stats.md +1 -0
- package/commands/gsd/thread.md +1 -0
- package/commands/gsd/workspace.md +1 -0
- package/commands/gsd/workstreams.md +1 -0
- package/gsd-core/bin/check-latest-version.cjs +8 -3
- package/gsd-core/bin/gsd-tools.cjs +672 -146
- package/gsd-core/bin/lib/adr-parser.cjs +4 -2
- package/gsd-core/bin/lib/artifacts.cjs +2 -1
- package/gsd-core/bin/lib/audit.cjs +119 -34
- package/gsd-core/bin/lib/broken-windows.cjs +168 -49
- package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
- package/gsd-core/bin/lib/capability-loader.cjs +135 -1
- package/gsd-core/bin/lib/capability-registry.cjs +96 -189
- package/gsd-core/bin/lib/capability-source.cjs +19 -2
- package/gsd-core/bin/lib/capability-validator.cjs +14 -2
- package/gsd-core/bin/lib/check-command-router.cjs +213 -49
- package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
- package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
- package/gsd-core/bin/lib/commands.cjs +823 -112
- package/gsd-core/bin/lib/config-loader.cjs +66 -4
- package/gsd-core/bin/lib/config.cjs +186 -45
- package/gsd-core/bin/lib/coverage.cjs +1 -1
- package/gsd-core/bin/lib/decisions.cjs +164 -45
- package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
- package/gsd-core/bin/lib/frontmatter.cjs +13 -0
- package/gsd-core/bin/lib/graphify.cjs +10 -2
- package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
- package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
- package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
- package/gsd-core/bin/lib/init.cjs +614 -86
- package/gsd-core/bin/lib/install-engine.cjs +29 -3
- package/gsd-core/bin/lib/install-profiles.cjs +14 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
- package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
- package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
- package/gsd-core/bin/lib/milestone.cjs +37 -13
- package/gsd-core/bin/lib/model-resolver.cjs +253 -53
- package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
- 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 +268 -27
- package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
- package/gsd-core/bin/lib/phase-locator.cjs +29 -10
- package/gsd-core/bin/lib/phase.cjs +393 -88
- package/gsd-core/bin/lib/plan-document.cjs +49 -1
- package/gsd-core/bin/lib/planning-document.cjs +459 -0
- package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
- package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
- package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
- package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
- package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
- package/gsd-core/bin/lib/probe-core.cjs +7 -1
- package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
- package/gsd-core/bin/lib/project-root.cjs +41 -2
- package/gsd-core/bin/lib/quick-batch.cjs +1 -1
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
- package/gsd-core/bin/lib/research-store.cjs +11 -12
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
- package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
- package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
- package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
- package/gsd-core/bin/lib/roadmap.cjs +356 -42
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
- package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
- package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
- package/gsd-core/bin/lib/security.cjs +126 -7
- package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
- package/gsd-core/bin/lib/state-document.cjs +130 -28
- package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
- package/gsd-core/bin/lib/state-transition.cjs +181 -30
- package/gsd-core/bin/lib/state.cjs +265 -27
- package/gsd-core/bin/lib/surface.cjs +77 -3
- package/gsd-core/bin/lib/task-command-router.cjs +12 -6
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
- package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
- package/gsd-core/bin/lib/uat.cjs +9 -1
- package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
- package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
- package/gsd-core/bin/lib/update-context.cjs +30 -24
- package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
- package/gsd-core/bin/lib/verification.cjs +315 -30
- package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
- package/gsd-core/bin/lib/verify.cjs +320 -48
- package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
- package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
- package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
- package/gsd-core/references/checkpoints.md +5 -3
- package/gsd-core/references/compact-content-gate.md +66 -0
- 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-phase-between-wave-reset.md +7 -6
- 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 +18 -0
- package/gsd-core/references/model-profiles.md +13 -4
- 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 +14 -2
- package/gsd-core/references/tdd.md +30 -4
- package/gsd-core/references/thinking-models-planning.md +18 -2
- package/gsd-core/references/ui-consideration-probe.md +10 -5
- package/gsd-core/references/verification-patterns.md +17 -4
- package/gsd-core/references/verify-command-path-resolvability.md +10 -2
- package/gsd-core/references/worktree-path-safety.md +433 -2
- package/gsd-core/templates/README.md +7 -1
- package/gsd-core/templates/state.md +6 -3
- package/gsd-core/templates/summary.compact.md +212 -0
- package/gsd-core/templates/user-setup.compact.md +199 -0
- package/gsd-core/templates/user-setup.md +0 -9
- 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 +1 -1
- package/gsd-core/workflows/add-tests.md +2 -2
- package/gsd-core/workflows/add-todo.md +6 -5
- package/gsd-core/workflows/ai-integration-phase.md +11 -3
- package/gsd-core/workflows/audit-fix.md +1 -1
- package/gsd-core/workflows/audit-milestone.md +1 -1
- 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 +29 -16
- package/gsd-core/workflows/check-todos.md +6 -4
- package/gsd-core/workflows/cleanup.md +5 -3
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
- package/gsd-core/workflows/code-review-fix.md +108 -22
- package/gsd-core/workflows/code-review.md +216 -73
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
- package/gsd-core/workflows/complete-milestone.md +41 -264
- package/gsd-core/workflows/debug.md +3 -3
- package/gsd-core/workflows/diagnose-issues.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/discuss-phase.md +1 -1
- package/gsd-core/workflows/do.md +2 -2
- package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
- package/gsd-core/workflows/docs-update.md +17 -158
- package/gsd-core/workflows/edit-phase.md +1 -1
- package/gsd-core/workflows/eval-review.md +10 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
- package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
- package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +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/sequential-root-pin.md +35 -0
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -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/wave-post-gate-hooks.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
- package/gsd-core/workflows/execute-phase.md +83 -172
- package/gsd-core/workflows/execute-plan.md +24 -10
- package/gsd-core/workflows/explore.md +3 -3
- package/gsd-core/workflows/extract-learnings.md +2 -1
- 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 +2 -2
- package/gsd-core/workflows/help/modes/full.compact.md +398 -0
- package/gsd-core/workflows/help/modes/full.md +5 -5
- package/gsd-core/workflows/help/modes/topic.md +15 -5
- package/gsd-core/workflows/help.md +1 -1
- package/gsd-core/workflows/import.md +2 -2
- package/gsd-core/workflows/inbox.md +2 -2
- package/gsd-core/workflows/ingest-docs.md +3 -3
- package/gsd-core/workflows/insert-phase.md +1 -1
- package/gsd-core/workflows/list-seeds.md +1 -1
- package/gsd-core/workflows/list-workspaces.md +1 -1
- package/gsd-core/workflows/manager.md +2 -2
- package/gsd-core/workflows/map-codebase.md +52 -5
- package/gsd-core/workflows/milestone-summary.md +1 -1
- package/gsd-core/workflows/mvp-phase.md +1 -1
- package/gsd-core/workflows/new-milestone.md +56 -14
- package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
- 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 +39 -209
- 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 +1 -1
- package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
- 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 +45 -187
- 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 +132 -20
- package/gsd-core/workflows/profile-user.md +2 -2
- package/gsd-core/workflows/progress.md +1 -1
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
- package/gsd-core/workflows/quick-batch.md +1 -1
- package/gsd-core/workflows/quick.md +29 -10
- package/gsd-core/workflows/reapply-patches.md +86 -6
- package/gsd-core/workflows/remove-phase.md +1 -1
- package/gsd-core/workflows/remove-workspace.md +2 -2
- package/gsd-core/workflows/resume-project.md +1 -1
- package/gsd-core/workflows/review.md +31 -16
- package/gsd-core/workflows/scan.md +1 -1
- package/gsd-core/workflows/secure-phase.md +3 -2
- package/gsd-core/workflows/settings-advanced.md +30 -10
- package/gsd-core/workflows/settings-integrations.md +2 -3
- package/gsd-core/workflows/settings.md +22 -9
- package/gsd-core/workflows/ship.md +3 -2
- 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 +1 -1
- package/gsd-core/workflows/transition.md +1 -1
- package/gsd-core/workflows/ui-phase.md +44 -8
- package/gsd-core/workflows/ui-review.md +18 -4
- package/gsd-core/workflows/ultraplan-phase.md +1 -1
- package/gsd-core/workflows/undo.md +339 -20
- package/gsd-core/workflows/update.md +14 -12
- package/gsd-core/workflows/validate-phase.md +3 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
- package/gsd-core/workflows/verify-work.md +101 -196
- package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
- package/hooks/dist/gsd-context-monitor.js +88 -15
- package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
- package/hooks/dist/gsd-secret-read-guard.js +71 -19
- package/hooks/dist/gsd-statusline.js +81 -20
- package/hooks/dist/gsd-validate-commit.sh +97 -8
- package/hooks/dist/gsd-worktree-path-guard.js +25 -14
- package/hooks/dist/gsd-write-guard.js +46 -1
- package/hooks/dist/lib/dispatch-identity.js +187 -0
- package/hooks/dist/lib/filename-classification.js +64 -0
- package/hooks/dist/lib/isolation-deny-reason.js +53 -1
- package/hooks/dist/lib/isolation-sentinel.js +58 -19
- package/hooks/gsd-agent-isolation-guard.js +66 -16
- package/hooks/gsd-context-monitor.js +88 -15
- package/hooks/gsd-cursor-subagent-start.js +34 -14
- package/hooks/gsd-secret-read-guard.js +71 -19
- package/hooks/gsd-statusline.js +81 -20
- package/hooks/gsd-validate-commit.sh +97 -8
- package/hooks/gsd-worktree-path-guard.js +25 -14
- package/hooks/gsd-write-guard.js +46 -1
- package/hooks/lib/dispatch-identity.js +187 -0
- package/hooks/lib/filename-classification.js +64 -0
- package/hooks/lib/isolation-deny-reason.js +53 -1
- package/hooks/lib/isolation-sentinel.js +58 -19
- package/package.json +11 -6
- package/scripts/benchmark-compact-content-variants.cjs +298 -0
- package/scripts/benchmark-compact-content.cjs +368 -0
- package/scripts/build-hooks.js +15 -6
- package/scripts/check-contract-drift.cjs +131 -12
- package/scripts/check-env.cjs +36 -8
- package/scripts/check-glossary-refs.cjs +25 -21
- package/scripts/ci-next-health.cjs +271 -0
- package/scripts/ci-prepare-test-scope.cjs +7 -7
- package/scripts/ci-test-scope.cjs +126 -20
- package/scripts/ci-timeout-report.cjs +1 -1
- package/scripts/command-contract-helpers.cjs +3 -0
- package/scripts/diff-touches-shipped-paths.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +35 -2
- package/scripts/gen-adr-index.cjs +8 -2
- package/scripts/gen-inventory-manifest.cjs +12 -0
- package/scripts/gen-loop-host-contract.cjs +69 -0
- package/scripts/gen-platform-conformance-tier.cjs +557 -0
- package/scripts/lib/drift-scan.cjs +1 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
- package/scripts/lib/ndjson-reporter.cjs +3 -2
- package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
- package/scripts/lib/suite-detection.cjs +32 -0
- package/scripts/lint-allowed-tools-parity.cjs +221 -0
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
- package/scripts/lint-phase-arg-assignment.cjs +257 -0
- package/scripts/lint-phase-id-drift.cjs +623 -13
- package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
- package/scripts/lint-response-language-coverage.cjs +9 -3
- package/scripts/lint-retired-runtime-name.cjs +619 -0
- package/scripts/lint-source-test-name-collision.cjs +1 -1
- package/scripts/lint-state-write-path-drift.cjs +93 -0
- package/scripts/lint-test-file-count.allowlist.json +29 -9
- package/scripts/lint-vendored-deps.cjs +128 -17
- package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
- package/scripts/prompt-injection-scan.sh +18 -0
- package/scripts/release-tarball-smoke.cjs +194 -1
- package/scripts/workflow-size.cjs +139 -0
- package/skills/gsd-autonomous/SKILL.md +2 -2
- package/skills/gsd-capture/SKILL.md +1 -1
- package/skills/gsd-cleanup/SKILL.md +1 -0
- package/skills/gsd-code-review/SKILL.md +2 -1
- package/skills/gsd-complete-milestone/SKILL.md +1 -0
- package/skills/gsd-config/SKILL.md +1 -0
- package/skills/gsd-debug/SKILL.md +1 -0
- package/skills/gsd-graphify/SKILL.md +1 -0
- package/skills/gsd-health/SKILL.md +1 -0
- package/skills/gsd-mempalace-capture/SKILL.md +8 -3
- package/skills/gsd-mempalace-recall/SKILL.md +1 -0
- package/skills/gsd-new-milestone/SKILL.md +1 -0
- package/skills/gsd-new-project/SKILL.md +1 -0
- package/skills/gsd-next/SKILL.md +1 -0
- package/skills/gsd-pause-work/SKILL.md +1 -0
- package/skills/gsd-phase/SKILL.md +1 -0
- package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
- package/skills/gsd-pr-branch/SKILL.md +1 -0
- package/skills/gsd-progress/SKILL.md +1 -1
- package/skills/gsd-quick-batch/SKILL.md +1 -1
- package/skills/gsd-resume-work/SKILL.md +1 -0
- package/skills/gsd-review/SKILL.md +2 -3
- package/skills/gsd-review-backlog/SKILL.md +1 -0
- package/skills/gsd-settings/SKILL.md +2 -1
- package/skills/gsd-stats/SKILL.md +1 -0
- package/skills/gsd-thread/SKILL.md +1 -0
- package/skills/gsd-workspace/SKILL.md +1 -0
- package/skills/gsd-workstreams/SKILL.md +1 -0
- package/vscode/package.json +1 -1
- package/gsd-core/templates/claude-md.md +0 -145
- package/gsd-core/templates/codebase/concerns.md +0 -310
- package/gsd-core/templates/codebase/conventions.md +0 -307
- package/gsd-core/templates/codebase/integrations.md +0 -280
- package/gsd-core/templates/codebase/structure.md +0 -285
- package/gsd-core/templates/codebase/testing.md +0 -480
- package/gsd-core/templates/debug-subagent-prompt.md +0 -91
- package/gsd-core/templates/discovery.md +0 -146
|
@@ -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");
|
|
@@ -52,6 +52,9 @@ const { extractFrontmatter, parseMustHavesBlock } = frontmatter;
|
|
|
52
52
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
53
53
|
const verificationMod = require("./verification.cjs");
|
|
54
54
|
const { isPhaseComplete } = 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");
|
|
55
58
|
// ─── coerceTruthToString ──────────────────────────────────────────────────────
|
|
56
59
|
/**
|
|
57
60
|
* Coerce an arbitrary YAML scalar/object into a string for cross-cutting
|
|
@@ -89,7 +92,7 @@ function coerceTruthToString(t) {
|
|
|
89
92
|
return '';
|
|
90
93
|
}
|
|
91
94
|
// ─── countPhasePlansAndSummaries ──────────────────────────────────────────────
|
|
92
|
-
function countPhasePlansAndSummaries(phaseDir) {
|
|
95
|
+
function countPhasePlansAndSummaries(phaseDir, convention) {
|
|
93
96
|
const { planCount, summaryCount } = scanPhasePlans(phaseDir);
|
|
94
97
|
// hasContext and hasResearch are not plan-scan concerns — read the directory
|
|
95
98
|
// once and share the listing for all non-plan metadata that cmdRoadmapAnalyze needs.
|
|
@@ -112,7 +115,9 @@ function countPhasePlansAndSummaries(phaseDir) {
|
|
|
112
115
|
// summaryCount above stay on scanPhasePlans's own unscoped listing since a
|
|
113
116
|
// PLAN/SUMMARY leading number is a plan sequence number, not a phase
|
|
114
117
|
// number. Mirrors core-utils.cts's getPhaseFileStats.
|
|
115
|
-
|
|
118
|
+
// #612: `convention` threaded from the one caller (which already threads it
|
|
119
|
+
// into matchPhaseDirs) so a bracket dir scopes by its real token.
|
|
120
|
+
const scopedFiles = scopeToPhase(phaseFiles, node_path_1.default.basename(phaseDir), convention);
|
|
116
121
|
return {
|
|
117
122
|
planCount,
|
|
118
123
|
summaryCount,
|
|
@@ -168,9 +173,9 @@ function searchPhaseInContent(content, escapedPhase, phaseNum, convention) {
|
|
|
168
173
|
.find((candidate) => candidate.level <= currentHeading.level);
|
|
169
174
|
const sectionEnd = nextHeading ? nextHeading.offset : content.length;
|
|
170
175
|
const section = content.slice(headerIndex, sectionEnd).trim();
|
|
171
|
-
// Extract goal if present (supports both **Goal:** and **Goal**: formats)
|
|
172
|
-
|
|
173
|
-
const goal =
|
|
176
|
+
// Extract goal if present (supports both **Goal:** and **Goal**: formats).
|
|
177
|
+
// #4731: multiline-aware — hard-wrapped Goals read past the line break.
|
|
178
|
+
const goal = roadmapParserModule.extractPhaseFieldMultiline(section, 'Goal');
|
|
174
179
|
// Mode: vertical-MVP slice mode flag. Lowercased + trimmed for canonical
|
|
175
180
|
// comparison; unrecognized values are preserved verbatim for forward-compat.
|
|
176
181
|
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
@@ -349,8 +354,17 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
349
354
|
// #3036: widen the id capture to accept non-numeric-leading ids (e.g. B7, P0.3-2)
|
|
350
355
|
// that get-phase/execute-phase already resolve. An optional leading letter prefix
|
|
351
356
|
// ([A-Za-z]?) covers letter-prefixed ids without breaking numeric-leading ones.
|
|
357
|
+
// #4478: line-anchored (`^ {0,3}`, `/m`) — unanchored, `#{2,4}` matched a
|
|
358
|
+
// `### Phase N:`-shaped mention ANYWHERE `exec()`'s scan reached: mid-sentence
|
|
359
|
+
// prose, inside a blockquote, inside an inline code span (backtick-quoted on
|
|
360
|
+
// the same line, not a fenced block `tokenizeHeadings` would exclude). Any
|
|
361
|
+
// such line minted a phantom phase entry, inflating phase_count and able to
|
|
362
|
+
// collide on a phase NUMBER with a real heading nearby. `{0,3}` leading
|
|
363
|
+
// spaces mirrors `tokenizeHeadings`'s own CommonMark ATX-heading tolerance
|
|
364
|
+
// (src/markdown-sectionizer.cts:453) so a legitimately-indented heading that
|
|
365
|
+
// matched before this fix still matches after it.
|
|
352
366
|
// phase-id-owner: uses the [.-] (dot-or-dash) separator variant, not the canonical dot-only token; a swap to PHASE_NUMBER_TOKEN_SOURCE would drop hyphenated phase-id matches.
|
|
353
|
-
const phasePattern = new RegExp(
|
|
367
|
+
const phasePattern = new RegExp(`^ {0,3}#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.ANY_BRACKET, convention, true)}([A-Za-z]?\\d+[A-Z]?(?:[.-]\\d+)*)(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:\\s*([^\\n]+)`, 'gim');
|
|
354
368
|
// The capturing intro inserts the bracket id at group 1 only under the
|
|
355
369
|
// bracket convention; the token and name shift by the same offset.
|
|
356
370
|
const G = convention === 'bracket' ? 1 : 0;
|
|
@@ -372,11 +386,16 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
372
386
|
// #3691: `\d` → `\d[\d.]*` so decimal phase headings (e.g. `### Phase 02.3:`) are
|
|
373
387
|
// recognised as section boundaries. #3036: `[A-Za-z]?\d` so non-numeric-leading ids
|
|
374
388
|
// (e.g. B7) are also recognised.
|
|
375
|
-
|
|
389
|
+
// #4478 follow-up (independent code review on this same fix): ` {0,3}` after
|
|
390
|
+
// the literal `\n` mirrors phasePattern's own new leading-space tolerance
|
|
391
|
+
// above -- without it, a legitimately-indented (1-3 space) NEXT phase
|
|
392
|
+
// heading was invisible to this boundary lookup, letting the PRIOR phase's
|
|
393
|
+
// goal/mode/depends_on extraction bleed across the section boundary into
|
|
394
|
+
// the next phase's own body.
|
|
395
|
+
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'));
|
|
376
396
|
const sectionEnd = nextHeader ? sectionStart + nextHeader.index : content.length;
|
|
377
397
|
const section = content.slice(sectionStart, sectionEnd);
|
|
378
|
-
const
|
|
379
|
-
const goal = goalMatch ? goalMatch[1].trim() : null;
|
|
398
|
+
const goal = roadmapParserModule.extractPhaseFieldMultiline(section, 'Goal');
|
|
380
399
|
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
381
400
|
const mode = modeMatch ? modeMatch[1].trim().toLowerCase() : null;
|
|
382
401
|
const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
@@ -420,7 +439,7 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
420
439
|
// convention into that owner rather than reviving the primitive `.find()`.
|
|
421
440
|
const dirMatch = matchPhaseDirs(phaseDirNames, normalized, convention).matches[0];
|
|
422
441
|
if (dirMatch) {
|
|
423
|
-
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatch));
|
|
442
|
+
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatch), convention);
|
|
424
443
|
planCount = counts.planCount;
|
|
425
444
|
summaryCount = counts.summaryCount;
|
|
426
445
|
hasContext = counts.hasContext;
|
|
@@ -433,7 +452,10 @@ function collectAnalyzePhases(content, phasesDir, phaseDirNames, convention) {
|
|
|
433
452
|
// NOT a precondition, so a zero-plan phase with a passing
|
|
434
453
|
// `*-VERIFICATION.md` reports complete here too, not just via
|
|
435
454
|
// `phase.complete`.
|
|
436
|
-
|
|
455
|
+
// #612: `convention` (a parameter of this function, same thread as
|
|
456
|
+
// matchPhaseDirs above) rides into completion so a bracket phase dir
|
|
457
|
+
// resolves and scopes its verification report like its legacy twin.
|
|
458
|
+
const completionResult = isPhaseComplete(node_path_1.default.join(phasesDir, dirMatch), { convention });
|
|
437
459
|
if (completionResult.value.complete)
|
|
438
460
|
diskStatus = 'complete';
|
|
439
461
|
else if (summaryCount > 0)
|
|
@@ -638,7 +660,94 @@ function cmdRoadmapAnalyze(cwd, raw) {
|
|
|
638
660
|
if (seenChecklistKeys.has(key))
|
|
639
661
|
continue;
|
|
640
662
|
seenChecklistKeys.add(key);
|
|
641
|
-
|
|
663
|
+
// #4899: the checkbox state (`[x]` vs `[ ]`) isn't a capturing group in
|
|
664
|
+
// `checklistPattern` — adding one would shift every downstream `1 + G` /
|
|
665
|
+
// `G` index this loop and `missingDetails` below already depend on.
|
|
666
|
+
// Reading it off the full match text instead is index-safe.
|
|
667
|
+
checklistOccurrences.push({ token, bracketId, checked: /\[x\]/i.test(checklistMatch[0]) });
|
|
668
|
+
}
|
|
669
|
+
// #4899: `collectAnalyzePhases` only recognises `### Phase N:` headings and
|
|
670
|
+
// progress-table rows — the shape `templates/roadmap.md` itself emits
|
|
671
|
+
// (checklist entries only, no detail sections yet) produces `phases: []`
|
|
672
|
+
// even though `checklistOccurrences` above just proved real phases exist.
|
|
673
|
+
// That evidence was computed and then discarded on the way to the final
|
|
674
|
+
// `phases: []` output (ADR-4910 §5/Phase 4). When the heading/table scan
|
|
675
|
+
// found nothing but the checklist did, synthesize a minimal `AnalyzePhase`
|
|
676
|
+
// per non-sentinel checklist occurrence — same enrichment calls
|
|
677
|
+
// (`matchPhaseDirs`, `countPhasePlansAndSummaries`, `isPhaseComplete`) the
|
|
678
|
+
// heading branch of `collectAnalyzePhases` already uses, so a synthesized
|
|
679
|
+
// phase's on-disk status is derived identically, not approximated.
|
|
680
|
+
//
|
|
681
|
+
// `missing_phase_details` is intentionally left untouched by this branch:
|
|
682
|
+
// its contract is "this checklist token has no `### Phase N:` heading or
|
|
683
|
+
// progress-table row", and that remains true for every synthesized token —
|
|
684
|
+
// synthesis fills in `phases[]` from the checklist itself, it does not
|
|
685
|
+
// manufacture a detail section. Removing these tokens from
|
|
686
|
+
// `missing_phase_details` would hide the very malformed-ROADMAP signal
|
|
687
|
+
// ADR-4910 (#4899) treats as evidence to surface, not evidence to launder.
|
|
688
|
+
//
|
|
689
|
+
// Gated on `!hasPhaseListingTableHeader(effectiveContent)` (found by the
|
|
690
|
+
// pre-existing #4480 regression "a status table cannot hide missing phase
|
|
691
|
+
// details"): a `| Phase | Status |`-shaped table with no Name column
|
|
692
|
+
// correctly declares no phases via `collectAnalyzePhases`, but its mere
|
|
693
|
+
// presence signals this roadmap has already moved to table-based tracking —
|
|
694
|
+
// synthesizing phantom phases from checklist entries alongside a
|
|
695
|
+
// deliberately thin tracking table would manufacture phase_count where the
|
|
696
|
+
// roadmap's own structure says "not yet detailed", the same laundering this
|
|
697
|
+
// branch exists to avoid for `missing_phase_details`. #4899's actual target
|
|
698
|
+
// shape (`templates/roadmap.md`: checklist only, no Progress section at
|
|
699
|
+
// all) has no such table and is unaffected.
|
|
700
|
+
if (phases.length === 0 && checklistOccurrences.length > 0 && !hasPhaseListingTableHeader(effectiveContent)) {
|
|
701
|
+
for (const occ of checklistOccurrences) {
|
|
702
|
+
if (isSentinelPhase(occ.token, occ.bracketId))
|
|
703
|
+
continue;
|
|
704
|
+
const normalized = normalizePhaseName(occ.token);
|
|
705
|
+
const dirMatch = matchPhaseDirs(_phaseDirNames, normalized, convention).matches[0];
|
|
706
|
+
let diskStatus = 'no_directory';
|
|
707
|
+
let planCount = 0;
|
|
708
|
+
let summaryCount = 0;
|
|
709
|
+
let hasContext = false;
|
|
710
|
+
let hasResearch = false;
|
|
711
|
+
let contextReadError = null;
|
|
712
|
+
let contextScope = SCOPE.COMPLETE;
|
|
713
|
+
if (dirMatch) {
|
|
714
|
+
const counts = countPhasePlansAndSummaries(node_path_1.default.join(phasesDir, dirMatch), convention);
|
|
715
|
+
planCount = counts.planCount;
|
|
716
|
+
summaryCount = counts.summaryCount;
|
|
717
|
+
hasContext = counts.hasContext;
|
|
718
|
+
hasResearch = counts.hasResearch;
|
|
719
|
+
contextReadError = counts.contextReadError;
|
|
720
|
+
contextScope = counts.scope;
|
|
721
|
+
const completionResult = isPhaseComplete(node_path_1.default.join(phasesDir, dirMatch), { convention });
|
|
722
|
+
if (completionResult.value.complete)
|
|
723
|
+
diskStatus = 'complete';
|
|
724
|
+
else if (summaryCount > 0)
|
|
725
|
+
diskStatus = 'partial';
|
|
726
|
+
else if (planCount > 0)
|
|
727
|
+
diskStatus = 'planned';
|
|
728
|
+
else if (hasResearch)
|
|
729
|
+
diskStatus = 'researched';
|
|
730
|
+
else if (hasContext)
|
|
731
|
+
diskStatus = 'discussed';
|
|
732
|
+
else
|
|
733
|
+
diskStatus = 'empty';
|
|
734
|
+
}
|
|
735
|
+
phases.push({
|
|
736
|
+
number: occ.token,
|
|
737
|
+
name: `Phase ${occ.token}`,
|
|
738
|
+
goal: null,
|
|
739
|
+
mode: null,
|
|
740
|
+
depends_on: null,
|
|
741
|
+
plan_count: planCount,
|
|
742
|
+
summary_count: summaryCount,
|
|
743
|
+
has_context: hasContext,
|
|
744
|
+
has_research: hasResearch,
|
|
745
|
+
disk_status: diskStatus,
|
|
746
|
+
roadmap_complete: occ.checked,
|
|
747
|
+
context_read_error: contextReadError,
|
|
748
|
+
context_scope: contextScope,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
642
751
|
}
|
|
643
752
|
// The EMITTED value stays the bare token, unchanged: `phases[].number` is a
|
|
644
753
|
// token under every convention, and `missing_phase_details` is read against
|
|
@@ -841,7 +950,11 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
841
950
|
// the same phase (ADR-3180 §7.4's headline: one predicate for the read
|
|
842
951
|
// path and the write path).
|
|
843
952
|
const phaseDir = node_path_1.default.join(cwd, phaseInfo.directory);
|
|
844
|
-
|
|
953
|
+
// ADR-3180 §7.4 read/write-path symmetry with the threaded site at ~583:
|
|
954
|
+
// thread convention here too, so this write path's completion reading
|
|
955
|
+
// agrees with the read path's under the bracket convention.
|
|
956
|
+
const convention = resolvePhaseIdConvention(cwd);
|
|
957
|
+
const completionResult = isPhaseComplete(phaseDir, { convention });
|
|
845
958
|
const verificationResult = completionResult.value.verification;
|
|
846
959
|
// #2648 precedent, applied at this write site (ADR-3180 §7.4 / #3186):
|
|
847
960
|
// `isPhaseComplete` deliberately carries NO plan-count precondition — the
|
|
@@ -872,6 +985,23 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
872
985
|
}
|
|
873
986
|
// Wrap entire read-modify-write in lock to prevent concurrent corruption
|
|
874
987
|
let updated = false;
|
|
988
|
+
// #4247: the refusal flag. The write/report decision below must be keyed to
|
|
989
|
+
// "a writable roadmap representation of THIS phase was found", never to "any
|
|
990
|
+
// byte moved". On a checklist-form ROADMAP (`- [ ] **Phase N: …**`, the
|
|
991
|
+
// roadmapper's own summary-checklist form) every phase-targeted grammar
|
|
992
|
+
// below requires an ATX `#{2,4} Phase N` heading and therefore finds
|
|
993
|
+
// nothing; the Progress-table row is the only other writable target, and
|
|
994
|
+
// when its Phase cell does not match `phaseCellRe` (e.g. a word-prefixed
|
|
995
|
+
// `Phase 68` cell — deliberately unrecognized on the read side too,
|
|
996
|
+
// `deriveProgressFromRoadmap`'s `/^\d/` data-row filter) the command used
|
|
997
|
+
// to fall through to unrelated byte deltas (an UN-scoped plan-checkbox mark
|
|
998
|
+
// anywhere in the document) and report `updated: true` while the phase's
|
|
999
|
+
// own row stayed untouched — with the file-global write then letting the
|
|
1000
|
+
// platform write seam's markdown normalization inject blank lines around
|
|
1001
|
+
// other phases' bullets, splitting hand-wrapped sentences mid-entry. The
|
|
1002
|
+
// refusal below declines with the analyzer's own `missing_phase_details`
|
|
1003
|
+
// vocabulary and leaves ROADMAP.md byte-identical.
|
|
1004
|
+
let missingPhaseDetails = false;
|
|
875
1005
|
withPlanningLock(cwd, () => {
|
|
876
1006
|
// #3957 (B9.4): captured BEFORE any transform runs, so the write/report
|
|
877
1007
|
// decision below reflects whether the transforms actually changed
|
|
@@ -881,6 +1011,41 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
881
1011
|
const originalContent = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
|
|
882
1012
|
let roadmapContent = originalContent;
|
|
883
1013
|
const phasePattern = phaseMarkdownRegexSource(phaseNum);
|
|
1014
|
+
// #4247: ONE local source for the ATX phase-heading anchor that every
|
|
1015
|
+
// section-scoped writer below (`planSectionPattern`,
|
|
1016
|
+
// `insertRowsPatternA|B`) starts with — extracted so the target-detection
|
|
1017
|
+
// gate below reads the SAME grammar the writers anchor on, and a future
|
|
1018
|
+
// edit to one cannot drift from the other three copies.
|
|
1019
|
+
// #4906 Phase 5 (#4984): NOT migrated onto the heading-baseline selector —
|
|
1020
|
+
// this write-path anchor was never one of the five ADR-4910 §8 call sites
|
|
1021
|
+
// this phase owns (init.cts/milestone.cts's buildPhaseHeadingScanRegex
|
|
1022
|
+
// sites); a direct any-bracket selector call here would have added a
|
|
1023
|
+
// fourth uncounted ANY_BRACKET consumer to roadmap.cts's pinned
|
|
1024
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census. Left
|
|
1025
|
+
// bracket-blind deliberately, matching this call's pre-#4984 behavior;
|
|
1026
|
+
// folding it into the census is Phase 6 work.
|
|
1027
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
1028
|
+
const phaseHeadingAnchor = `#{2,4}\\s*Phase\\s+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}(?=[:\\s])`;
|
|
1029
|
+
// #4247: target detection runs against the ORIGINAL content's active
|
|
1030
|
+
// (post-</details>) region — the same milestone scoping every writer
|
|
1031
|
+
// below applies — so the gate asks "does the file carry a writable phase
|
|
1032
|
+
// representation" rather than "did some regex fire mid-transform".
|
|
1033
|
+
const gateDetailsClose = originalContent.lastIndexOf('</details>');
|
|
1034
|
+
const gateActiveRegion = gateDetailsClose === -1
|
|
1035
|
+
? originalContent
|
|
1036
|
+
: originalContent.slice(gateDetailsClose + '</details>'.length);
|
|
1037
|
+
// Heading target: the exact grammar `planSectionPattern` /
|
|
1038
|
+
// `insertRowsPatternA|B` anchor on (an ATX phase heading for this phase).
|
|
1039
|
+
const headingTargetFound = new RegExp(phaseHeadingAnchor, 'i').test(gateActiveRegion);
|
|
1040
|
+
// Checklist target: when the phase is complete, its own checklist bullet
|
|
1041
|
+
// (`- [ ] **Phase N: …**`) IS a writable phase row — the completion
|
|
1042
|
+
// checkbox stamp below updates it. Same grammar as that writer, widened
|
|
1043
|
+
// one notch to `[ x]` so an ALREADY-checked bullet still counts as a
|
|
1044
|
+
// found target: an idempotent re-run then takes the honest
|
|
1045
|
+
// "no changes were needed" decline instead of this refusal.
|
|
1046
|
+
const checklistTargetFound = isComplete && new RegExp(`-\\s*\\[[ x]\\]\\s*.*Phase\\s+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'i').test(gateActiveRegion);
|
|
1047
|
+
// Table-row target: set by the row-scoped cell updates below.
|
|
1048
|
+
let tableRowFound = false;
|
|
884
1049
|
// Progress table row: update Plans Complete/Status/Completed columns BY
|
|
885
1050
|
// COLUMN NAME (handles 4- or 5-column RoadmapProgress tables regardless of
|
|
886
1051
|
// Milestone-column presence) via the markdown-table seam (ADR-2143 §7) —
|
|
@@ -899,11 +1064,15 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
899
1064
|
roadmapContent = editProgressTableSlice(roadmapContent, (scoped) => {
|
|
900
1065
|
let text = scoped;
|
|
901
1066
|
const plansResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Plans Complete', ` ${summaryCount}/${planCount} `);
|
|
902
|
-
if (plansResult.ok)
|
|
1067
|
+
if (plansResult.ok) {
|
|
903
1068
|
text = plansResult.value;
|
|
1069
|
+
tableRowFound = true;
|
|
1070
|
+
}
|
|
904
1071
|
const statusResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Status', ` ${status.padEnd(11)}`);
|
|
905
|
-
if (statusResult.ok)
|
|
1072
|
+
if (statusResult.ok) {
|
|
906
1073
|
text = statusResult.value;
|
|
1074
|
+
tableRowFound = true;
|
|
1075
|
+
}
|
|
907
1076
|
// Preserve only a valid ISO date (#1161: idempotent; self-heal garbage).
|
|
908
1077
|
// Ragged-tolerant (#2245 Blocker 2): probe the CURRENT Completed cell via
|
|
909
1078
|
// a no-op updateTableCell write (its own tolerant row scan) rather than
|
|
@@ -918,8 +1087,10 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
918
1087
|
}
|
|
919
1088
|
return ' ';
|
|
920
1089
|
});
|
|
921
|
-
if (completedResult.ok)
|
|
1090
|
+
if (completedResult.ok) {
|
|
922
1091
|
text = completedResult.value;
|
|
1092
|
+
tableRowFound = true;
|
|
1093
|
+
}
|
|
923
1094
|
return text;
|
|
924
1095
|
});
|
|
925
1096
|
// Update plan count in phase detail section.
|
|
@@ -960,7 +1131,14 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
960
1131
|
// `_match` unchanged. An untouched first line cannot orphan its own
|
|
961
1132
|
// continuation on the next line, since the pattern never spans past
|
|
962
1133
|
// `\n` in the first place.
|
|
963
|
-
|
|
1134
|
+
// #4906 Phase 2 (#4917/ADR-4910): migrated off the one-capture-group
|
|
1135
|
+
// regex that replaced to end of line onto the PlanningDoc `boldField`
|
|
1136
|
+
// write seam. `planSectionPattern` scopes the match to phase N's OWN
|
|
1137
|
+
// detail section (heading + body up to the next heading) — the same
|
|
1138
|
+
// window `phaseHeadingAnchor`'s siblings anchor on — and the callback
|
|
1139
|
+
// below runs parse -> classify -> (maybe) mutate -> serialize entirely
|
|
1140
|
+
// within that section text, mirroring phase.cts's `writePlansField`.
|
|
1141
|
+
const planSectionPattern = new RegExp(`${phaseHeadingAnchor}(?:(?!\\n#{1,4}\\s)[\\s\\S])*`, 'i');
|
|
964
1142
|
const planCountText = isComplete
|
|
965
1143
|
? `${summaryCount}/${planCount} plans complete`
|
|
966
1144
|
: `${summaryCount}/${planCount} plans executed`;
|
|
@@ -969,24 +1147,107 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
969
1147
|
// gsd-core/templates/roadmap.md actually ships, not to "anything in
|
|
970
1148
|
// brackets" — a bracketed human annotation like `[Deferred pending
|
|
971
1149
|
// re-scope]` is structurally bracketed too but carries none of this
|
|
972
|
-
// wording, so it correctly falls through to arm 3 untouched.
|
|
1150
|
+
// wording, so it correctly falls through to arm 3 untouched. Kept as a
|
|
1151
|
+
// caller-side classifier (NOT seam grammar) reused below against the
|
|
1152
|
+
// `boldField` node's own parsed `value`.
|
|
973
1153
|
const isTemplatePlaceholder = (value) => {
|
|
974
1154
|
const trimmed = value.trim();
|
|
975
1155
|
return /^\[\s*Number of plans\b[\s\S]*\]$/i.test(trimmed);
|
|
976
1156
|
};
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1157
|
+
// Positive detectors for the two "real count token" shapes (#2853 /
|
|
1158
|
+
// #3584 Finding B): a fraction count (`N/M plans complete|executed`) or a
|
|
1159
|
+
// bare singular/plural count (`N plan`/`N plans`, the fresh single-plan
|
|
1160
|
+
// template shape at gsd-core/templates/roadmap.md:62). Either one is an
|
|
1161
|
+
// existing count token to overwrite (arm 1), never template placeholder
|
|
1162
|
+
// (arm 2) or freeform prose (arm 3).
|
|
1163
|
+
// #4906 regression fix: PREFIX match (not full-string) — a real count
|
|
1164
|
+
// token may have a glued-on annotation with no ` — ` separator (e.g.
|
|
1165
|
+
// `0/1 plans executed (11-16 are gap closure from VERIFICATION)`), which
|
|
1166
|
+
// `parseBoldFieldLine`'s em-dash-only trailing-content split leaves
|
|
1167
|
+
// entirely inside `value` (TRAILING_SEPARATOR_RE in planning-document.cts
|
|
1168
|
+
// is unchanged and correct — this is a caller-side classification fix,
|
|
1169
|
+
// not a seam fix). Returns the matched prefix length, or -1 if no match.
|
|
1170
|
+
const fractionCountPrefixLength = (value) => {
|
|
1171
|
+
const m = value.match(/^\d+\s*\/\s*\d+\s+plans(?:\s+(?:complete|executed))?/i);
|
|
1172
|
+
return m ? m[0].length : -1;
|
|
1173
|
+
};
|
|
1174
|
+
const bareCountPrefixLength = (value) => {
|
|
1175
|
+
const m = value.match(/^\d+\s+plans?/i);
|
|
1176
|
+
return m ? m[0].length : -1;
|
|
1177
|
+
};
|
|
1178
|
+
roadmapContent = replaceInCurrentMilestone(roadmapContent, planSectionPattern, (sectionText) => {
|
|
1179
|
+
const parsed = (0, planning_document_cjs_1.parsePlanningDoc)(sectionText, 'ROADMAP.md');
|
|
1180
|
+
if (!parsed.ok) {
|
|
1181
|
+
// Unreadable section (e.g. an unterminated frontmatter fence) —
|
|
1182
|
+
// leave it byte-identical rather than throwing.
|
|
1183
|
+
return sectionText;
|
|
1184
|
+
}
|
|
1185
|
+
const fieldId = (0, planning_document_cjs_1.findField)(parsed.value, 'Plans');
|
|
1186
|
+
if (!fieldId) {
|
|
1187
|
+
// #4906 regression (#1163, caught by gsd-test): a hand-edited or
|
|
1188
|
+
// pre-template ROADMAP.md may carry a PLAIN (non-bold) `Plans:` line
|
|
1189
|
+
// rather than the canonical `**Plans**:`/`**Plans:**` bold field.
|
|
1190
|
+
// BOLD_FIELD_RE is deliberately bold-only (widening it to any bare
|
|
1191
|
+
// `Label:` would register ordinary prose like "Note: see below" as a
|
|
1192
|
+
// spurious field seam-wide) — this is domain knowledge about ONE
|
|
1193
|
+
// field's legacy tolerated shape, the same class of thing
|
|
1194
|
+
// `isTemplatePlaceholder` above already keeps caller-side rather
|
|
1195
|
+
// than seam grammar, so the fallback lives here, not in
|
|
1196
|
+
// planning-document.cts.
|
|
1197
|
+
const plainMatch = sectionText.match(/^([ \t]*)Plans:([ \t]*)([^\r\n]*)$/m);
|
|
1198
|
+
if (!plainMatch) {
|
|
1199
|
+
// No `**Plans:**`/`**Plans**:`/plain `Plans:` line in this phase's
|
|
1200
|
+
// own section — nothing to write; not a failure (mirrors the old
|
|
1201
|
+
// regex's silent no-match no-op).
|
|
1202
|
+
return sectionText;
|
|
1203
|
+
}
|
|
1204
|
+
const [whole, indent, spacing, plainValue] = plainMatch;
|
|
1205
|
+
const plainFractionLen = fractionCountPrefixLength(plainValue);
|
|
1206
|
+
const plainBareLen = bareCountPrefixLength(plainValue);
|
|
1207
|
+
const plainCountPrefixLen = plainFractionLen >= 0 ? plainFractionLen : plainBareLen;
|
|
1208
|
+
if (plainCountPrefixLen < 0 && !isTemplatePlaceholder(plainValue)) {
|
|
1209
|
+
// Arm 3: freeform prose, TBD, a bracketed human annotation, or an
|
|
1210
|
+
// empty value — leave the section exactly as it was.
|
|
1211
|
+
return sectionText;
|
|
1212
|
+
}
|
|
1213
|
+
const plainSuffix = plainCountPrefixLen >= 0 ? plainValue.slice(plainCountPrefixLen) : '';
|
|
1214
|
+
const newPlainLine = `${indent}Plans:${spacing}${planCountText}${plainSuffix}`;
|
|
1215
|
+
const start = plainMatch.index ?? sectionText.indexOf(whole);
|
|
1216
|
+
return sectionText.slice(0, start) + newPlainLine + sectionText.slice(start + whole.length);
|
|
1217
|
+
}
|
|
1218
|
+
const current = (0, planning_document_cjs_1.readNode)(parsed.value, fieldId);
|
|
1219
|
+
if (!current.ok) {
|
|
1220
|
+
return sectionText;
|
|
1221
|
+
}
|
|
1222
|
+
const currentValue = current.value;
|
|
1223
|
+
const fractionPrefixLen = fractionCountPrefixLength(currentValue);
|
|
1224
|
+
const barePrefixLen = bareCountPrefixLength(currentValue);
|
|
1225
|
+
const countPrefixLen = fractionPrefixLen >= 0 ? fractionPrefixLen : barePrefixLen;
|
|
1226
|
+
if (countPrefixLen < 0 && !isTemplatePlaceholder(currentValue)) {
|
|
1227
|
+
// Arm 3: freeform prose, TBD, a bracketed human annotation, or an
|
|
1228
|
+
// empty value — leave the section exactly as it was.
|
|
1229
|
+
return sectionText;
|
|
981
1230
|
}
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
1231
|
+
// Arm 1 (real count token, possibly with a glued-on no-separator
|
|
1232
|
+
// annotation re-attached verbatim as `suffix`) or arm 2 (fresh-template
|
|
1233
|
+
// placeholder, whole value replaced): write the computed count.
|
|
1234
|
+
// `setFieldValue`'s valueSpan/trailingSpan split additionally preserves
|
|
1235
|
+
// any EM-DASH-separated trailing annotation (#2853) automatically — no
|
|
1236
|
+
// separate "preserve trailing" branch needed for that shape.
|
|
1237
|
+
const suffix = countPrefixLen >= 0 ? currentValue.slice(countPrefixLen) : '';
|
|
1238
|
+
const newValueToWrite = planCountText + suffix;
|
|
1239
|
+
const staged = (0, planning_document_cjs_1.setFieldValue)(parsed.value, fieldId, newValueToWrite);
|
|
1240
|
+
if (!staged.ok) {
|
|
1241
|
+
return sectionText;
|
|
985
1242
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1243
|
+
const out = (0, planning_document_cjs_1.serialize)(staged.value);
|
|
1244
|
+
if (!out.ok) {
|
|
1245
|
+
// `hasUnreadableNodes` refusal (ADR-4910 amendment) — a ragged
|
|
1246
|
+
// SIBLING node elsewhere in this same section refuses the whole
|
|
1247
|
+
// splice. Never throw; leave the section unchanged.
|
|
1248
|
+
return sectionText;
|
|
1249
|
+
}
|
|
1250
|
+
return out.value;
|
|
990
1251
|
});
|
|
991
1252
|
// If complete: check checkbox
|
|
992
1253
|
if (isComplete) {
|
|
@@ -994,10 +1255,17 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
994
1255
|
roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
|
|
995
1256
|
}
|
|
996
1257
|
// Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md", "- [ ] 50-01:", or "- [ ] **50-01**")
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1258
|
+
// #4741: tick only plans the phase's own COUNT still counts. `plans` is the
|
|
1259
|
+
// superseded-filtered set (#2349 via scanPhasePlans) while `summaries` is
|
|
1260
|
+
// the raw *-SUMMARY.md listing — a superseded plan can carry a SUMMARY
|
|
1261
|
+
// (e.g. `status: halted`), and ticking it read as "executed" right under a
|
|
1262
|
+
// count line that excludes it. The prefix match mirrors the checkbox regex
|
|
1263
|
+
// below (rows match by planId prefix, which the PLAN-01.md naming shape
|
|
1264
|
+
// relies on), so non-superseded plans tick exactly as before.
|
|
1265
|
+
const tickableSummaries = phaseInfo.summaries
|
|
1266
|
+
.map((summaryFile) => ({ summaryFile, planId: summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '') }))
|
|
1267
|
+
.filter(({ planId }) => planId !== '' && phaseInfo.plans.some((planFile) => planFile.startsWith(planId)));
|
|
1268
|
+
for (const { planId } of tickableSummaries) {
|
|
1001
1269
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
|
|
1002
1270
|
const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
|
|
1003
1271
|
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
|
@@ -1021,9 +1289,26 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1021
1289
|
// Detection is scoped to the active region so a plan that appears in an
|
|
1022
1290
|
// archived <details> block is still correctly detected as missing from the
|
|
1023
1291
|
// active milestone section.
|
|
1292
|
+
//
|
|
1293
|
+
// #4786: a plan row may be written WITH the `-PLAN.md` suffix (canonical
|
|
1294
|
+
// template form) or WITHOUT it (hand-written form: `- [x] 659-01 — desc`).
|
|
1295
|
+
// The tick loop above keys on the bare planId stem with a PREFIX match
|
|
1296
|
+
// (pre-existing, out of scope here), while detection required the full
|
|
1297
|
+
// `-PLAN.md` filename — so every suffix-less row was counted missing and
|
|
1298
|
+
// the insertion fired BESIDE the recognized list — 32 checkbox lines for
|
|
1299
|
+
// 16 plans, exit 0. The stem arm below accepts the bare id only up to a
|
|
1300
|
+
// boundary (whitespace / `:` / dashes / `-PLAN.md` / `.md` / `**` / `)`),
|
|
1301
|
+
// so `5-011` never satisfies `5-01`. Deliberately NOT in the boundary set:
|
|
1302
|
+
// `.` — a dotted sub-id (`5-01.5`) is a real distinct plan, and counting
|
|
1303
|
+
// its row as 5-01's presence would suppress a genuine insertion.
|
|
1024
1304
|
const missingPlans = phaseInfo.plans.filter((planFile) => {
|
|
1025
1305
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planFile);
|
|
1026
|
-
|
|
1306
|
+
if (new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${planEscaped}`, 'i').test(activeRegion))
|
|
1307
|
+
return false;
|
|
1308
|
+
const stem = planFile.replace(/-PLAN\.md$/i, '');
|
|
1309
|
+
const stemEscaped = (0, pattern_cjs_1.escapeRegex)(stem);
|
|
1310
|
+
const stemPresent = new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${stemEscaped}(?=$|\\s|:|—|–|-PLAN\\.md|\\.md|\\*\\*|\\))`, 'i').test(activeRegion);
|
|
1311
|
+
return !stemPresent;
|
|
1027
1312
|
});
|
|
1028
1313
|
if (missingPlans.length > 0) {
|
|
1029
1314
|
// Insert missing plan checklist rows (#1163). We prefer to anchor to the
|
|
@@ -1040,8 +1325,8 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1040
1325
|
//
|
|
1041
1326
|
// Pattern A: anchor to bare `Plans:` header (preferred).
|
|
1042
1327
|
// Pattern B: fallback to bold summary when no bare header exists.
|
|
1043
|
-
const insertRowsPatternA = new RegExp(`(
|
|
1044
|
-
const insertRowsPatternB = new RegExp(`(
|
|
1328
|
+
const insertRowsPatternA = new RegExp(`(${phaseHeadingAnchor}(?:(?!\\n#{1,4}\\s)[\\s\\S])*?(?:^|\\n)(?:Plans:)[^\\n]*)`, 'i');
|
|
1329
|
+
const insertRowsPatternB = new RegExp(`(${phaseHeadingAnchor}(?:(?!\\n#{1,4}\\s)[\\s\\S])*?(?:\\*\\*Plans\\*\\*:|\\*\\*Plans:\\*\\*)[^\\n]*)`, 'i');
|
|
1045
1330
|
const sortedMissing = [...missingPlans].sort();
|
|
1046
1331
|
const newRows = sortedMissing.map(p => `- [ ] ${p}`).join('\n');
|
|
1047
1332
|
const inserter = (match) => `${match}\n${newRows}`;
|
|
@@ -1066,10 +1351,9 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1066
1351
|
if (withRows !== roadmapContent) {
|
|
1067
1352
|
roadmapContent = withRows;
|
|
1068
1353
|
// Mark any newly-inserted rows that already have summaries as complete
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
continue;
|
|
1354
|
+
// (#4741: same superseded-filtered tick list as the loop above — a
|
|
1355
|
+
// pre-existing superseded row must stay unchecked on this path too).
|
|
1356
|
+
for (const { planId } of tickableSummaries) {
|
|
1073
1357
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
|
|
1074
1358
|
const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
|
|
1075
1359
|
roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
|
|
@@ -1081,10 +1365,24 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1081
1365
|
// `cmdRoadmapAnnotateDependencies`'s existing `nextContent !== content`
|
|
1082
1366
|
// gate. Previously this wrote and reported `updated: true`
|
|
1083
1367
|
// unconditionally, even on an idempotent re-run that changed nothing.
|
|
1084
|
-
|
|
1368
|
+
//
|
|
1369
|
+
// #4247: ...but ONLY when a writable representation of THIS phase was
|
|
1370
|
+
// found. Without the target gate, a byte delta from an unrelated
|
|
1371
|
+
// transform (the un-scoped plan-checkbox mark) satisfied the #3957 gate
|
|
1372
|
+
// and produced a success-shaped `updated: true` while the phase's own
|
|
1373
|
+
// row stayed untouched — and the file-global write let the platform
|
|
1374
|
+
// write seam's markdown normalization reflow unrelated entries. When no
|
|
1375
|
+
// target exists the command refuses: no write at all, so ROADMAP.md is
|
|
1376
|
+
// left byte-identical, and the caller gets a typed
|
|
1377
|
+
// `missing_phase_details` decline instead of a false green.
|
|
1378
|
+
const phaseRepresentationFound = tableRowFound || headingTargetFound || checklistTargetFound;
|
|
1379
|
+
if (phaseRepresentationFound && roadmapContent !== originalContent) {
|
|
1085
1380
|
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, roadmapContent);
|
|
1086
1381
|
updated = true;
|
|
1087
1382
|
}
|
|
1383
|
+
if (!phaseRepresentationFound) {
|
|
1384
|
+
missingPhaseDetails = true;
|
|
1385
|
+
}
|
|
1088
1386
|
});
|
|
1089
1387
|
const computed = {
|
|
1090
1388
|
phase: phaseNum,
|
|
@@ -1097,6 +1395,13 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
|
|
|
1097
1395
|
if (updated) {
|
|
1098
1396
|
output({ updated: true, ...computed }, raw, `${summaryCount}/${planCount} ${status}`);
|
|
1099
1397
|
}
|
|
1398
|
+
else if (missingPhaseDetails) {
|
|
1399
|
+
// #4247: honest refusal — the reason names the real condition (the
|
|
1400
|
+
// analyzer's `missing_phase_details` vocabulary), never "already
|
|
1401
|
+
// reflects", which was false: the ROADMAP was never able to record this
|
|
1402
|
+
// phase's progress in the first place.
|
|
1403
|
+
declineNoOp(raw, 'updated', 'missing_phase_details', `roadmap update-plan-progress skipped — ROADMAP.md has no writable entry for phase ${formatDiagnosticToken(String(phaseNum))} (no matching Progress-table row, no phase detail section, and no checklist entry this command can update). ROADMAP.md was left unchanged.`, computed);
|
|
1404
|
+
}
|
|
1100
1405
|
else {
|
|
1101
1406
|
declineNoOp(raw, 'updated', "no changes were needed — ROADMAP.md already reflects this phase's plan/summary counts and status", "roadmap update-plan-progress skipped — no changes were needed; ROADMAP.md already reflects this phase's plan/summary counts and status.", computed);
|
|
1102
1407
|
}
|
|
@@ -1205,6 +1510,15 @@ function cmdRoadmapAnnotateDependencies(cwd, phaseNum, raw) {
|
|
|
1205
1510
|
// #3537: padding-tolerant fragment so the caller's resolved padded id
|
|
1206
1511
|
// matches un-padded ROADMAP headings.
|
|
1207
1512
|
const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
|
|
1513
|
+
// #4906 Phase 5 (#4984): NOT migrated onto the heading-baseline selector —
|
|
1514
|
+
// this site was never one of the five ADR-4910 §8 call sites this phase
|
|
1515
|
+
// owns (init.cts/milestone.cts's buildPhaseHeadingScanRegex sites); a
|
|
1516
|
+
// direct any-bracket selector call here would have added a fifth
|
|
1517
|
+
// uncounted ANY_BRACKET consumer to roadmap.cts's pinned
|
|
1518
|
+
// tests/adr-612-bracket-heading-selection.test.cjs census. Left
|
|
1519
|
+
// bracket-blind deliberately, matching this call's pre-#4984 behavior;
|
|
1520
|
+
// folding it into the census is Phase 6 work.
|
|
1521
|
+
// phase-id-owner: deliberately unmigrated (#4984 revert) — see comment above.
|
|
1208
1522
|
const phaseHeaderPattern = new RegExp(`(#{2,4}\\s*Phase\\s+${phaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}:[^\\n]*)`, 'i');
|
|
1209
1523
|
const phaseMatch = content.match(phaseHeaderPattern);
|
|
1210
1524
|
if (!phaseMatch)
|