@opengsd/gsd-core 1.15.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/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- 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 +1 -1
- package/agents/gsd-debug-session-manager.md +1 -1
- package/agents/gsd-debugger.md +2 -2
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +4 -4
- 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 +1 -1
- package/agents/gsd-plan-checker.md +7 -2
- package/agents/gsd-planner.md +3 -3
- 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 +42 -51
- package/agents/gsd-ui-researcher.compact.md +1 -1
- package/agents/gsd-ui-researcher.md +1 -1
- package/agents/gsd-verifier.md +31 -9
- package/bin/install.js +126 -196
- 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 +5 -0
- package/commands/gsd/capture.md +8 -4
- 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 +5 -1
- 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 +5 -1
- package/commands/gsd/pr-branch.md +4 -0
- package/commands/gsd/profile-user.md +5 -1
- package/commands/gsd/progress.md +6 -1
- package/commands/gsd/quick-batch.md +20 -8
- package/commands/gsd/quick.md +12 -7
- package/commands/gsd/review.md +5 -1
- 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 +142 -56
- package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
- package/gsd-core/bin/lib/audit.cjs +78 -44
- 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 +183 -103
- package/gsd-core/bin/lib/capability-validator.cjs +16 -0
- package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
- package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
- 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 +11 -8
- 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 +173 -170
- package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
- package/gsd-core/bin/lib/config.cjs +43 -12
- 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/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 +413 -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/health-diagnostic-rules/phase-structure.cjs +43 -47
- package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
- package/gsd-core/bin/lib/init.cjs +162 -96
- package/gsd-core/bin/lib/install-engine.cjs +19 -35
- package/gsd-core/bin/lib/install-profiles.cjs +7 -4
- 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 +19 -1
- package/gsd-core/bin/lib/model-resolver.cjs +18 -18
- 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 +11 -4
- package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
- package/gsd-core/bin/lib/phase-id.cjs +1 -1
- package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
- package/gsd-core/bin/lib/phase-status.cjs +360 -0
- package/gsd-core/bin/lib/phase.cjs +280 -80
- package/gsd-core/bin/lib/plan-document.cjs +93 -19
- package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
- package/gsd-core/bin/lib/planning-document.cjs +273 -40
- package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
- package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
- package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
- 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/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/roadmap-command-router.cjs +13 -15
- package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
- package/gsd-core/bin/lib/roadmap.cjs +169 -72
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
- package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
- package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
- package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
- package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
- package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
- 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.cjs +214 -105
- package/gsd-core/bin/lib/surface.cjs +2 -1
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
- package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
- package/gsd-core/bin/lib/uat.cjs +424 -7
- package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
- 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 +1165 -314
- package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
- package/gsd-core/bin/lib/verify.cjs +286 -567
- 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/shared/config-defaults.manifest.json +5 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +4 -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/execute-mvp-tdd.md +5 -10
- package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
- package/gsd-core/references/execute-phase-response-language.md +1 -1
- package/gsd-core/references/gsd-run-resolver.md +1 -1
- package/gsd-core/references/loop-hook-dispatch.md +7 -1
- package/gsd-core/references/offer-next.md +1 -1
- package/gsd-core/references/planning-config.md +1 -1
- package/gsd-core/references/spidr-splitting.md +1 -1
- package/gsd-core/references/tdd.md +10 -4
- package/gsd-core/references/verifier-phase-gates.md +5 -2
- package/gsd-core/references/verify-mvp-mode.md +2 -2
- package/gsd-core/references/workstream-flag.md +33 -3
- 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 +3 -2
- package/gsd-core/workflows/add-todo.md +4 -3
- package/gsd-core/workflows/ai-integration-phase.md +3 -2
- 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.md +28 -18
- package/gsd-core/workflows/check-todos.md +6 -5
- package/gsd-core/workflows/cleanup.md +1 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
- package/gsd-core/workflows/code-review-fix.md +5 -4
- package/gsd-core/workflows/code-review.md +84 -90
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
- package/gsd-core/workflows/complete-milestone.md +12 -7
- package/gsd-core/workflows/debug.md +30 -5
- 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 +1 -1
- package/gsd-core/workflows/docs-update.md +4 -3
- package/gsd-core/workflows/edit-phase.md +4 -3
- package/gsd-core/workflows/eval-review.md +5 -3
- package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
- 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 +1 -1
- 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 +1 -1
- package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
- 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/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.md +78 -170
- package/gsd-core/workflows/execute-plan.md +13 -19
- package/gsd-core/workflows/explore.md +2 -2
- package/gsd-core/workflows/extract-learnings.md +3 -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 +2 -1
- package/gsd-core/workflows/import.md +3 -2
- package/gsd-core/workflows/inbox.md +1 -1
- package/gsd-core/workflows/ingest-docs.md +1 -1
- 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 +5 -3
- package/gsd-core/workflows/map-codebase.md +4 -3
- 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 +10 -10
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
- package/gsd-core/workflows/new-project.md +1 -1
- package/gsd-core/workflows/new-workspace.md +1 -1
- package/gsd-core/workflows/next.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 +1 -1
- 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 +1 -1
- package/gsd-core/workflows/plan-phase.md +27 -18
- package/gsd-core/workflows/plan-review-convergence.md +1 -1
- package/gsd-core/workflows/plant-seed.md +1 -1
- package/gsd-core/workflows/pr-branch.md +1 -1
- package/gsd-core/workflows/profile-user.md +1 -1
- package/gsd-core/workflows/progress.md +19 -49
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
- 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 +42 -31
- package/gsd-core/workflows/remove-phase.md +3 -2
- package/gsd-core/workflows/remove-workspace.md +1 -1
- package/gsd-core/workflows/resume-project.md +3 -2
- package/gsd-core/workflows/review.md +3 -2
- package/gsd-core/workflows/scan.md +3 -2
- package/gsd-core/workflows/secure-phase.md +11 -12
- package/gsd-core/workflows/settings-advanced.md +1 -1
- package/gsd-core/workflows/settings-integrations.md +1 -1
- package/gsd-core/workflows/settings.md +1 -1
- package/gsd-core/workflows/ship.md +12 -12
- 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 +1 -1
- package/gsd-core/workflows/spec-phase.md +1 -1
- 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 +1 -1
- package/gsd-core/workflows/thread.md +2 -2
- package/gsd-core/workflows/transition.md +13 -23
- package/gsd-core/workflows/ui-phase.md +5 -4
- package/gsd-core/workflows/ui-review.md +4 -3
- package/gsd-core/workflows/ultraplan-phase.md +3 -2
- package/gsd-core/workflows/undo.md +1 -1
- package/gsd-core/workflows/validate-phase.md +10 -12
- 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 +68 -59
- package/hooks/dist/gsd-agent-isolation-guard.js +8 -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 +147 -16
- package/hooks/dist/gsd-statusline.js +15 -7
- package/hooks/dist/gsd-update-banner.js +8 -0
- 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 +8 -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 +147 -16
- package/hooks/gsd-statusline.js +15 -7
- package/hooks/gsd-update-banner.js +8 -0
- 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 +21 -4
- package/scripts/changeset/parse.cjs +52 -4
- package/scripts/ci-timeout-report.cjs +770 -4
- package/scripts/command-contract-helpers.cjs +12 -8
- package/scripts/docs-guard-registry.cjs +6 -0
- package/scripts/gen-features.cjs +13 -8
- package/scripts/gen-hooks-cli-exit.cjs +12 -28
- package/scripts/gen-loop-host-contract.cjs +10 -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 +20 -2
- package/scripts/lib/ndjson-reporter.cjs +28 -3
- package/scripts/lib/platform-conformance-tier.generated.cjs +34 -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 +10 -0
- 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-enumeration-drift.cjs +12 -8
- package/scripts/lint-phase-id-drift.cjs +29 -0
- package/scripts/lint-planning-document-positive-control.cjs +329 -0
- package/scripts/lint-response-language-coverage.cjs +3 -0
- package/scripts/lint-skill-deps.cjs +7 -3
- package/scripts/lint-test-file-count.allowlist.json +11 -1
- package/scripts/lint-test-file-count.cjs +34 -1
- package/scripts/lint-vendored-deps.cjs +41 -5
- package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
- package/scripts/mutation-matrix.cjs +50 -5
- 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 +5 -0
- package/skills/gsd-capture/SKILL.md +8 -4
- 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 +5 -1
- 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 +5 -1
- package/skills/gsd-pr-branch/SKILL.md +4 -0
- package/skills/gsd-profile-user/SKILL.md +5 -1
- package/skills/gsd-progress/SKILL.md +6 -1
- package/skills/gsd-quick/SKILL.md +16 -10
- package/skills/gsd-quick-batch/SKILL.md +20 -8
- package/skills/gsd-review/SKILL.md +5 -1
- 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
- package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
|
@@ -22,6 +22,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
22
22
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
23
23
|
const node_path_1 = __importDefault(require("node:path"));
|
|
24
24
|
const node_child_process_1 = require("node:child_process");
|
|
25
|
+
const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
|
|
25
26
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- io.cjs is an export= CommonJS module
|
|
26
27
|
const ioMod = require("./io.cjs");
|
|
27
28
|
const { output, error, ERROR_REASON, formatDiagnosticToken } = ioMod;
|
|
@@ -38,11 +39,13 @@ const coreUtilsMod = require("./core-utils.cjs");
|
|
|
38
39
|
// generative-fix divergence CLAUDE.md warns about. Collapsed onto core-utils'
|
|
39
40
|
// copy, which was already the leaf owner, so there is no second surface left to
|
|
40
41
|
// drift and no parity test needed to police one.
|
|
41
|
-
const { toPosixPath, generateSlugInternal, readSubdirectories, extractCanonicalPlanId, findUnsummarizedPlans,
|
|
42
|
+
const { toPosixPath, generateSlugInternal, readSubdirectories, extractCanonicalPlanId, findUnsummarizedPlans, } = coreUtilsMod;
|
|
42
43
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
|
|
43
44
|
const phaseIdMod = require("./phase-id.cjs");
|
|
44
|
-
const { normalizePhaseName, phaseMarkdownRegexSource, comparePhaseNum, matchPhaseDirs, isSentinelPhaseId, scopeToPhase, OPTIONAL_PROJECT_CODE_PREFIX_SOURCE, OPTIONAL_PHASE_TAG_SOURCE, PHASE_NUMBER_TOKEN_SOURCE, } = phaseIdMod;
|
|
45
|
+
const { normalizePhaseName, phaseMarkdownRegexSource, comparePhaseNum, matchPhaseDirs, isSentinelPhaseId, scopeToPhase, OPTIONAL_PROJECT_CODE_PREFIX_SOURCE, OPTIONAL_PHASE_TAG_SOURCE, PHASE_NUMBER_TOKEN_SOURCE, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, buildPhaseHeadingScanRegex, } = phaseIdMod;
|
|
45
46
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
47
|
+
const gate_exit_cjs_1 = require("./gate-exit.cjs");
|
|
48
|
+
const gate_verdict_cjs_1 = require("./gate-verdict.cjs");
|
|
46
49
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
|
|
47
50
|
const phaseLocatorMod = require("./phase-locator.cjs");
|
|
48
51
|
const { findPhaseInternal, getArchivedPhaseDirs, listMilestonePhaseDirs, listAllPhaseDirs } = phaseLocatorMod;
|
|
@@ -70,7 +73,12 @@ const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
|
|
|
70
73
|
const clock_cjs_1 = require("./clock.cjs");
|
|
71
74
|
const state_transition_cjs_1 = require("./state-transition.cjs");
|
|
72
75
|
const markdown_table_cjs_1 = require("./markdown-table.cjs");
|
|
76
|
+
// #5060: the Phase Status Module owns the ROADMAP Status-cell token vocabulary.
|
|
77
|
+
const phase_status_cjs_1 = require("./phase-status.cjs");
|
|
73
78
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- roadmap.cjs is an export= CommonJS module
|
|
80
|
+
const roadmapMod = require("./roadmap.cjs");
|
|
81
|
+
const { buildPhaseHeadingRegex } = roadmapMod;
|
|
74
82
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- uat-predicate.cjs is an export= CommonJS module
|
|
75
83
|
const uatPredicate = require("./uat-predicate.cjs");
|
|
76
84
|
const { evaluateUatPassed } = uatPredicate;
|
|
@@ -81,7 +89,7 @@ const verificationMod = require("./verification.cjs");
|
|
|
81
89
|
// cycle (the reverse edge, `state.cts → verify.cjs`, would).
|
|
82
90
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- verify.cjs is an export= CommonJS module
|
|
83
91
|
const verifyMod = require("./verify.cjs");
|
|
84
|
-
const { readVerificationStatus } = verificationMod;
|
|
92
|
+
const { readVerificationStatus, VERIFICATION_STATUS, findVerificationStatusError } = verificationMod;
|
|
85
93
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- plan-dependency-graph.cjs is an export= CommonJS module
|
|
86
94
|
const planDependencyGraphMod = require("./plan-dependency-graph.cjs");
|
|
87
95
|
const { computeHaltPropagation, buildSummaryFileIndex, isSummaryFileHalted, isSummaryFileBlocked } = planDependencyGraphMod;
|
|
@@ -93,8 +101,7 @@ const milestoneLockMod = require("./milestone-lock.cjs");
|
|
|
93
101
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
94
102
|
const planDocumentMod = require("./plan-document.cjs");
|
|
95
103
|
const { parsePlanDocument, planIdFromFile } = planDocumentMod;
|
|
96
|
-
const {
|
|
97
|
-
const { readModifyWriteStateMd, stateExtractField, stateReplaceField, syncAndPreserveStateMd, withStateLock, updatePerformanceMetricsSection, } = stateMod;
|
|
104
|
+
const { readModifyWriteStateMd, stateExtractField, stateReplaceField, syncAndPreserveStateMd, assertVerificationReportsReadable, withStateLock, updatePerformanceMetricsSection, } = stateMod;
|
|
98
105
|
// Any .md file with PLAN anywhere in the basename — diagnostic net
|
|
99
106
|
const PLAN_OUTLINE_RE = /-PLAN-OUTLINE\.md$/i;
|
|
100
107
|
const PLAN_PRE_BOUNCE_RE = /-PLAN.*\.pre-bounce\.md$/i;
|
|
@@ -349,17 +356,25 @@ function getRoadmapModeForPhase(cwd, phaseNum) {
|
|
|
349
356
|
const milestoneContent = extractCurrentMilestone(rawContent, cwd);
|
|
350
357
|
const fullContent = stripShippedMilestones(rawContent);
|
|
351
358
|
const escapedPhase = phaseMarkdownRegexSource(phaseNum);
|
|
352
|
-
//
|
|
353
|
-
|
|
359
|
+
// #5007 (Phase 6 / ADR-4910 §8): buildPhaseHeadingRegex (src/roadmap.cts) is
|
|
360
|
+
// anchored `^...$` with no 'm' flag — it is designed to test ONE
|
|
361
|
+
// already-tokenized heading LINE (mirroring searchPhaseInContent's own
|
|
362
|
+
// usage), not to `.match()` against multi-line content directly. Routing
|
|
363
|
+
// through tokenizeHeadings first is therefore required here, not a pure
|
|
364
|
+
// regex-literal swap (the design doc's "direct swap" plan does not hold
|
|
365
|
+
// once the anchor is accounted for).
|
|
366
|
+
const phaseHeader = buildPhaseHeadingRegex(escapedPhase);
|
|
354
367
|
for (const content of [milestoneContent, fullContent]) {
|
|
355
|
-
const
|
|
356
|
-
if (!
|
|
368
|
+
const heading = (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(content).find((h) => phaseHeader.test(h.text));
|
|
369
|
+
if (!heading)
|
|
357
370
|
continue;
|
|
358
|
-
const
|
|
371
|
+
const headingLineEnd = content.indexOf('\n', heading.offset);
|
|
372
|
+
const headerMatchLength = (headingLineEnd === -1 ? content.length : headingLineEnd) - heading.offset;
|
|
373
|
+
const sectionStart = heading.offset;
|
|
359
374
|
const rest = content.slice(sectionStart);
|
|
360
|
-
const nextHeader = rest.slice(
|
|
375
|
+
const nextHeader = rest.slice(headerMatchLength).match(/\n#{2,4}\s+Phase\s+\S/i);
|
|
361
376
|
const sectionEnd = nextHeader
|
|
362
|
-
? sectionStart +
|
|
377
|
+
? sectionStart + headerMatchLength + nextHeader.index
|
|
363
378
|
: content.length;
|
|
364
379
|
const section = content.slice(sectionStart, sectionEnd);
|
|
365
380
|
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
|
|
@@ -846,6 +861,7 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
846
861
|
filesModified: planDoc.filesModified,
|
|
847
862
|
filesDeleted: planDoc.filesDeleted,
|
|
848
863
|
agentHint: planDoc.agentHint,
|
|
864
|
+
gapClosure: planDoc.gapClosure,
|
|
849
865
|
taskCount: planDoc.taskCount,
|
|
850
866
|
hasSummary,
|
|
851
867
|
halted,
|
|
@@ -981,6 +997,10 @@ function cmdPhasePlanIndex(cwd, phase, raw) {
|
|
|
981
997
|
return planMap.has(lower) ? planMap.get(lower).id : dep;
|
|
982
998
|
}),
|
|
983
999
|
autonomous: rawPlan.autonomous,
|
|
1000
|
+
// #4924: execute-phase's `--gaps-only` filter reads this field off the
|
|
1001
|
+
// index. Before it was emitted, every plan read as non-gap-closure and the
|
|
1002
|
+
// filter selected nothing — a silent, successful-looking empty run.
|
|
1003
|
+
gap_closure: rawPlan.gapClosure,
|
|
984
1004
|
objective: rawPlan.objective,
|
|
985
1005
|
files_modified: rawPlan.filesModified,
|
|
986
1006
|
files_deleted: rawPlan.filesDeleted,
|
|
@@ -1163,8 +1183,14 @@ function collectSiblingWorktreePhaseNums(cwd, used) {
|
|
|
1163
1183
|
const siblingPlanningDir = (wt) => planningDir(wt, ws);
|
|
1164
1184
|
const dirNumPattern = /^(?:[A-Z][A-Z0-9]*-)?(\d+)-/;
|
|
1165
1185
|
// Same header shape the allocators scan locally (#1729 tag tolerance).
|
|
1166
|
-
//
|
|
1167
|
-
|
|
1186
|
+
// #5007 (Phase 6 / ADR-4910 §8): migrated onto buildPhaseHeadingScanRegex
|
|
1187
|
+
// (src/phase-id.cjs), the shared "which phases exist in this content" scan
|
|
1188
|
+
// owner. LABEL_ONLY baseline with no convention argument reproduces the
|
|
1189
|
+
// prior literal `Phase\s+` (no bracket tolerance) byte-for-byte — this is a
|
|
1190
|
+
// counter, matching the LABEL_ONLY docstring guidance exactly, so it is
|
|
1191
|
+
// deliberately NOT widened onto the bracket grammar the way the real-heading
|
|
1192
|
+
// readers (getRoadmapModeForPhase, phaseDisplayNameFromRoadmap) are.
|
|
1193
|
+
const { regex: headerPattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.LABEL_ONLY);
|
|
1168
1194
|
for (const line of porcelain.split('\n')) {
|
|
1169
1195
|
if (!line.startsWith('worktree '))
|
|
1170
1196
|
continue;
|
|
@@ -1189,7 +1215,7 @@ function collectSiblingWorktreePhaseNums(cwd, used) {
|
|
|
1189
1215
|
let m;
|
|
1190
1216
|
headerPattern.lastIndex = 0;
|
|
1191
1217
|
while ((m = headerPattern.exec(content)) !== null) {
|
|
1192
|
-
const num = parseInt(m[
|
|
1218
|
+
const num = parseInt(m[phaseNumGroup], 10);
|
|
1193
1219
|
if (!isSentinelPhaseId(num))
|
|
1194
1220
|
used.add(num);
|
|
1195
1221
|
}
|
|
@@ -1229,8 +1255,13 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
|
|
|
1229
1255
|
// (section header, roadmap bullet, or on-disk directory) is counted:
|
|
1230
1256
|
// 1) Section headers: ### Phase N: / ## Phase N: / #### Phase N:
|
|
1231
1257
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
|
1232
|
-
//
|
|
1233
|
-
|
|
1258
|
+
// #5007 (Phase 6 / ADR-4910 §8): migrated onto buildPhaseHeadingScanRegex
|
|
1259
|
+
// (src/phase-id.cjs) with the LABEL_ONLY baseline and no convention
|
|
1260
|
+
// argument — a counter site, matching phase-id.cts's own LABEL_ONLY
|
|
1261
|
+
// docstring guidance, reproducing the prior `Phase\s+` literal (no
|
|
1262
|
+
// bracket tolerance) byte-for-byte. See collectSiblingWorktreePhaseNums
|
|
1263
|
+
// (above) for the identical migration on the same regex shape.
|
|
1264
|
+
const { regex: headerPattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.LABEL_ONLY);
|
|
1234
1265
|
// 2) Roadmap bullet entries: - [ ] **Phase N: ...** (all checkbox variants)
|
|
1235
1266
|
// The lookahead accepts colon, decimal-dot, whitespace, bold-close asterisk,
|
|
1236
1267
|
// or end-of-line so titleless forms ("- [ ] **Phase 11**", "- [ ] Phase 11")
|
|
@@ -1239,7 +1270,7 @@ function cmdPhaseAdd(cwd, description, raw, customId) {
|
|
|
1239
1270
|
const usedPhaseNums = new Set();
|
|
1240
1271
|
let m;
|
|
1241
1272
|
while ((m = headerPattern.exec(content)) !== null) {
|
|
1242
|
-
const num = parseInt(m[
|
|
1273
|
+
const num = parseInt(m[phaseNumGroup], 10);
|
|
1243
1274
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
1244
1275
|
if (!isSentinelPhaseId(num))
|
|
1245
1276
|
usedPhaseNums.add(num);
|
|
@@ -1339,12 +1370,15 @@ function cmdPhaseAddBatch(cwd, descriptions, raw) {
|
|
|
1339
1370
|
// bullets, on-disk dirs. The bullet scan was missing here — a bullet-only
|
|
1340
1371
|
// `Phase N` row was invisible to batch allocation (#3849 secondary).
|
|
1341
1372
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
|
1342
|
-
//
|
|
1343
|
-
|
|
1373
|
+
// #5007 (Phase 6 / ADR-4910 §8): migrated onto buildPhaseHeadingScanRegex
|
|
1374
|
+
// (src/phase-id.cjs) with the LABEL_ONLY baseline and no convention
|
|
1375
|
+
// argument — same counter-site migration as cmdPhaseAdd's identical
|
|
1376
|
+
// regex above.
|
|
1377
|
+
const { regex: phasePattern, phaseNumGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.LABEL_ONLY);
|
|
1344
1378
|
const bulletPattern = /^[ \t]*-[ \t]*\[[^\]]{0,200}\][ \t]*\*{0,2}Phase[ \t]+(\d+)(?=[:.\s*]|$)/gim;
|
|
1345
1379
|
let m;
|
|
1346
1380
|
while ((m = phasePattern.exec(content)) !== null) {
|
|
1347
|
-
const num = parseInt(m[
|
|
1381
|
+
const num = parseInt(m[phaseNumGroup], 10);
|
|
1348
1382
|
// #3185: canonical sentinel predicate (SENTINEL_RANGES [0,999]) — this was a local 999-only literal that admitted Phase 0.
|
|
1349
1383
|
if (isSentinelPhaseId(num))
|
|
1350
1384
|
continue;
|
|
@@ -1467,9 +1501,19 @@ function scanExistingDecimalPhaseNumbers(phasesDir, rawContent, base) {
|
|
|
1467
1501
|
decimalSet.add(parseInt(dm[1], 10));
|
|
1468
1502
|
}
|
|
1469
1503
|
}
|
|
1470
|
-
|
|
1471
|
-
//
|
|
1472
|
-
|
|
1504
|
+
// #5007 (Phase 6 / ADR-4910 §8): composes phaseHeadingPrefixSrcFor directly
|
|
1505
|
+
// rather than routing through buildPhaseHeadingScanRegex — the owner has no
|
|
1506
|
+
// decimal-subphase capture group. The design doc's suggested composition
|
|
1507
|
+
// passed `capturing=true` to phaseHeadingPrefixSrcFor, but this site has no
|
|
1508
|
+
// use for the bracket-id capture it would add, and doing so would silently
|
|
1509
|
+
// shift `rmMatch[1]` (the decimal digits) to group 2 — a real correctness
|
|
1510
|
+
// hazard. `capturing` is left at its default (false), and the `#{2,4}\s*`
|
|
1511
|
+
// heading marker (also omitted from the design's snippet) is kept explicit,
|
|
1512
|
+
// since phaseHeadingPrefixSrcFor never includes it — callers always add it
|
|
1513
|
+
// themselves (see buildPhaseHeadingScanRegex's own usage). No convention
|
|
1514
|
+
// argument: LABEL_ONLY with convention=undefined reproduces the prior
|
|
1515
|
+
// `Phase\s+` literal byte-for-byte.
|
|
1516
|
+
const rmPhasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY)}${phaseMarkdownRegexSource(base)}\\.(\\d+)${OPTIONAL_PHASE_TAG_SOURCE}\\s*:`, 'gi');
|
|
1473
1517
|
let rmMatch;
|
|
1474
1518
|
while ((rmMatch = rmPhasePattern.exec(rawContent)) !== null) {
|
|
1475
1519
|
decimalSet.add(parseInt(rmMatch[1], 10));
|
|
@@ -1496,12 +1540,27 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested'
|
|
|
1496
1540
|
const content = extractCurrentMilestone(rawContent, cwd);
|
|
1497
1541
|
const normalizedAfter = normalizePhaseName(afterPhase);
|
|
1498
1542
|
const afterPhaseEscaped = phaseMarkdownRegexSource(normalizedAfter);
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1543
|
+
const insertConvention = resolvePhaseIdConvention(cwd);
|
|
1544
|
+
// #5007 (Phase 6 / ADR-4910 §8): buildPhaseHeadingRegex (src/roadmap.cts)
|
|
1545
|
+
// is anchored `^...$` with no 'm' flag — designed to test ONE
|
|
1546
|
+
// already-tokenized heading LINE, not to `.test()` against multi-line
|
|
1547
|
+
// `content` directly (the design doc's "no anchoring change needed"
|
|
1548
|
+
// claim for this site does not hold once the anchor is accounted for —
|
|
1549
|
+
// same discrepancy as getRoadmapModeForPhase and phaseDisplayNameFromRoadmap
|
|
1550
|
+
// above/below). Routed through tokenizeHeadings, mirroring
|
|
1551
|
+
// searchPhaseInContent's own usage.
|
|
1552
|
+
const targetPattern = buildPhaseHeadingRegex(afterPhaseEscaped, insertConvention);
|
|
1553
|
+
const headingMatch = (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(content).some((h) => targetPattern.test(h.text));
|
|
1502
1554
|
const bulletPattern = new RegExp(`-\\s*\\[[ x]\\]\\s*(?:\\*\\*)?Phase\\s+${afterPhaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'i');
|
|
1503
|
-
//
|
|
1504
|
-
|
|
1555
|
+
// #5007 (Phase 6 / ADR-4910 §8): a pure "does ANY phase heading exist"
|
|
1556
|
+
// boolean — neither shared owner exposes this shape (buildPhaseHeadingRegex
|
|
1557
|
+
// is phase-N-specific; buildPhaseHeadingScanRegex always captures a phase
|
|
1558
|
+
// number). Composed directly from phaseHeadingPrefixSrcFor with the
|
|
1559
|
+
// `#{2,4}\s*` marker kept explicit (phaseHeadingPrefixSrcFor never
|
|
1560
|
+
// includes it). Unanchored `.test()` on multi-line content, same as the
|
|
1561
|
+
// pre-migration literal — no tokenize-first refactor needed here since
|
|
1562
|
+
// there is no anchor in this source.
|
|
1563
|
+
const anyHeadingPattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, insertConvention)}\\d`, 'i');
|
|
1505
1564
|
const roadmapHasHeadingPhases = anyHeadingPattern.test(content);
|
|
1506
1565
|
const isBulletStyle = !headingMatch && bulletPattern.test(content) && !roadmapHasHeadingPhases;
|
|
1507
1566
|
if (!headingMatch && !isBulletStyle) {
|
|
@@ -1577,16 +1636,42 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw, allocation = 'nested'
|
|
|
1577
1636
|
}
|
|
1578
1637
|
else {
|
|
1579
1638
|
const phaseEntry = `\n### Phase ${_decimalPhase}: ${description} (INSERTED)\n\n**Goal:** [Urgent work - to be planned]\n**Requirements**: TBD\n**Depends on:** Phase ${afterPhase}\n**Plans:** 0 plans\n\nPlans:\n- [ ] TBD (run ${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', (0, runtime_slash_cjs_1.resolveRuntime)(cwd))} ${_decimalPhase} to break down)\n`;
|
|
1580
|
-
|
|
1581
|
-
//
|
|
1582
|
-
|
|
1639
|
+
// #5007 (Phase 6 / ADR-4910 §8): this site needs the WHOLE matched
|
|
1640
|
+
// heading line plus its trailing newline (for splicing the new entry
|
|
1641
|
+
// in immediately after it) — neither shared owner returns that shape
|
|
1642
|
+
// (buildPhaseHeadingRegex captures only the title). Composed directly
|
|
1643
|
+
// from phaseHeadingPrefixSrcFor, keeping this site's own full-line
|
|
1644
|
+
// capture; audited together with the targetPattern/anyHeadingPattern
|
|
1645
|
+
// migration above (same describe block, same afterPhaseEscaped/
|
|
1646
|
+
// insertConvention inputs).
|
|
1647
|
+
const headerPattern = new RegExp(`(#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, insertConvention)}${afterPhaseEscaped}${OPTIONAL_PHASE_TAG_SOURCE}:[^\\n]*\\n)`, 'i');
|
|
1583
1648
|
const headerMatch = rawContent.match(headerPattern);
|
|
1584
1649
|
if (!headerMatch) {
|
|
1585
1650
|
error(`Could not find Phase ${afterPhase} header`);
|
|
1586
1651
|
}
|
|
1587
1652
|
const headerIdx = rawContent.indexOf(headerMatch[0]);
|
|
1588
1653
|
const afterHeader = rawContent.slice(headerIdx + headerMatch[0].length);
|
|
1589
|
-
|
|
1654
|
+
// #5007 (Phase 6 / ADR-4910 §8): this next-phase-boundary lookup was NOT
|
|
1655
|
+
// one of the 15 originally-marked grandfathered sites, but it hand-rolls
|
|
1656
|
+
// the identical `#{2,4}...Phase\s+` heading-marker shape and sits right
|
|
1657
|
+
// next to headerPattern above, which this same commit series just
|
|
1658
|
+
// widened (via insertConvention) to recognize a bracket-tagged target
|
|
1659
|
+
// heading. Left bracket-blind, this regex would silently defeat that
|
|
1660
|
+
// widening: under the bracket convention a freshly-recognized
|
|
1661
|
+
// bracket-tagged target could still fail to find the NEXT heading as a
|
|
1662
|
+
// boundary (`[CODE.MM] Phase N+1:` never matches a bare `Phase\s+`
|
|
1663
|
+
// literal), so the newly inserted entry falls through to the
|
|
1664
|
+
// rawContent.length branch and lands appended at end-of-file instead of
|
|
1665
|
+
// directly after the target section. Found while migrating the
|
|
1666
|
+
// neighboring sites, not deferred: this is the same defect class the
|
|
1667
|
+
// whole Phase 6 effort exists to drain, just an unmarked instance of it.
|
|
1668
|
+
// Composed with the SAME baseline/convention as headerPattern above so
|
|
1669
|
+
// target-discovery and next-boundary-discovery agree. The base
|
|
1670
|
+
// `#{2,4}\s+` (mandatory whitespace after the hashes, not `\s*`) is
|
|
1671
|
+
// preserved exactly as the prior literal had it — a pre-existing,
|
|
1672
|
+
// deliberately-unchanged asymmetry with the other sites in this file.
|
|
1673
|
+
const nextPhaseHeadingPrefix = `#{2,4}\\s+${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY, insertConvention)}`;
|
|
1674
|
+
const nextPhaseMatch = afterHeader.match(new RegExp(`\\r?\\n${nextPhaseHeadingPrefix}\\d[\\d.]*`, 'i'));
|
|
1590
1675
|
let insertIdx;
|
|
1591
1676
|
if (nextPhaseMatch) {
|
|
1592
1677
|
insertIdx = headerIdx + headerMatch[0].length + nextPhaseMatch.index;
|
|
@@ -1901,9 +1986,30 @@ function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, rem
|
|
|
1901
1986
|
if (!isDecimal) {
|
|
1902
1987
|
// #1729: fold an optional pre-colon ( ) tag into the suffix capture so it
|
|
1903
1988
|
// is re-emitted verbatim — a tagged later phase still gets renumbered.
|
|
1904
|
-
|
|
1905
|
-
//
|
|
1906
|
-
|
|
1989
|
+
// #5007 (Phase 6 / ADR-4910 §8): composes phaseHeadingPrefixSrcFor
|
|
1990
|
+
// directly (needs prefix/number/suffix as three separate capture groups
|
|
1991
|
+
// for a verbatim in-place rewrite — neither shared owner returns that
|
|
1992
|
+
// shape). Two deliberate departures from the design doc's suggested
|
|
1993
|
+
// composition, both kept for zero behavior change on this
|
|
1994
|
+
// highest-blast-radius site (feeds phase remove's renumber path):
|
|
1995
|
+
// 1. The `#{2,4}\s*` heading marker is kept explicit and folded INTO
|
|
1996
|
+
// the prefix capture group — the design's snippet omitted it
|
|
1997
|
+
// entirely (phaseHeadingPrefixSrcFor never includes it; every
|
|
1998
|
+
// caller adds it, e.g. buildPhaseHeadingScanRegex), and the
|
|
1999
|
+
// original group 1 here captured the marker too.
|
|
2000
|
+
// 2. No convention argument is threaded (LABEL_ONLY with
|
|
2001
|
+
// convention=undefined), even though phaseHeadingPrefixSrcFor
|
|
2002
|
+
// COULD accept one — this keeps the composed source byte-identical
|
|
2003
|
+
// to the prior `Phase\s+` literal, a deliberate non-widening for
|
|
2004
|
+
// this specific site (unlike cmdPhaseInsert's targetPattern).
|
|
2005
|
+
// The number group widens from `\d+(?:\.\d+)?` to the full
|
|
2006
|
+
// PHASE_NUMBER_TOKEN_SOURCE (letter suffix + multi-segment decimals) —
|
|
2007
|
+
// this is SAFE, not a behavior change: decrementRoadmapPhaseToken's own
|
|
2008
|
+
// regex (`^(\d+)(\.\d+)?$`) rejects anything wider and returns the
|
|
2009
|
+
// token unchanged, so a heading this widening newly matches (e.g.
|
|
2010
|
+
// "Phase 12A:") is written back byte-for-byte unchanged, identical to
|
|
2011
|
+
// the pre-migration outcome of the whole regex simply not matching it.
|
|
2012
|
+
content = content.replace(new RegExp(`(#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY)})(${PHASE_NUMBER_TOKEN_SOURCE})(${OPTIONAL_PHASE_TAG_SOURCE}\\s*:)`, 'gi'), (_match, prefix, num, suffix) => `${prefix}${decrementRoadmapPhaseToken(num, removedInt)}${suffix}`);
|
|
1907
2013
|
content = content.replace(/(-\s*\[[ x]\]\s*.*?Phase\s+)(\d+)(\s*:|\s+)/gi, (_match, prefix, num, suffix) => `${prefix}${decrementRoadmapPhaseNumber(num, removedInt)}${suffix}`);
|
|
1908
2014
|
// ORDINAL-RENUMBER — CELL EDIT (not row-deletion) — migrated onto
|
|
1909
2015
|
// updateTableCell (ADR-2143 §7, sibling of the deleteTableRow scoping
|
|
@@ -1992,8 +2098,52 @@ function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, rem
|
|
|
1992
2098
|
content = ordinalBefore + ordinalSection + ordinalRest;
|
|
1993
2099
|
}
|
|
1994
2100
|
content = content.replace(/(?<![0-9-])(\d{2})-(\d{2})(?=(?:(?:-[A-Za-z][A-Za-z0-9-]*)?-(?:PLAN|SUMMARY)\.md)|(?![0-9-]))/g, (_match, phaseNum, planNum) => `${decrementRoadmapPaddedPhaseNumber(phaseNum, removedInt)}-${planNum}`);
|
|
1995
|
-
|
|
1996
|
-
|
|
2101
|
+
// #5007 (Phase 6 / ADR-4910): migrated the "Depends on" bold-field
|
|
2102
|
+
// decrement off two hand-rolled regexes — one per bold-placement
|
|
2103
|
+
// variant, `**Depends on**: Phase N` and `**Depends on:** Phase N` —
|
|
2104
|
+
// onto the PlanningDoc `boldField` read/write seam (`writePlansField`
|
|
2105
|
+
// above is the established precedent for this migration shape).
|
|
2106
|
+
// `BOLD_FIELD_RE` (planning-document.cts) recognizes BOTH placements
|
|
2107
|
+
// as the SAME field label ("Depends on") — `parseBoldFieldLine` strips
|
|
2108
|
+
// the token down to its inner text regardless of which side of `**`
|
|
2109
|
+
// the colon lands on — so this single loop naturally replaces both
|
|
2110
|
+
// regexes with one call site; no variant-specific branching remains.
|
|
2111
|
+
// Unlike `writePlansField`'s single `findField` lookup (scoped to one
|
|
2112
|
+
// already-isolated phase section), a whole ROADMAP.md can carry a
|
|
2113
|
+
// "Depends on" field in EVERY phase's detail section, so this walks
|
|
2114
|
+
// every `boldField` node in the whole parsed document whose label is
|
|
2115
|
+
// "Depends on" and rewrites each one independently — a
|
|
2116
|
+
// representability refusal or absent match on one field leaves that
|
|
2117
|
+
// field untouched and never blocks any other field, mirroring the
|
|
2118
|
+
// prior regexes' per-occurrence independence. A parse failure or an
|
|
2119
|
+
// unreadable-nodes refusal on `serialize` leaves `content` byte-for-
|
|
2120
|
+
// byte untouched, the same silent no-op the prior regexes had for any
|
|
2121
|
+
// input they didn't match (neither ever threw or warned).
|
|
2122
|
+
const dependsOnParsed = (0, planning_document_cjs_1.parsePlanningDoc)(content, 'ROADMAP.md');
|
|
2123
|
+
if (dependsOnParsed.ok) {
|
|
2124
|
+
let dependsOnDoc = dependsOnParsed.value;
|
|
2125
|
+
const dependsOnFieldIds = dependsOnDoc.nodes
|
|
2126
|
+
.filter((n) => n.kind === 'boldField' && n.label === 'Depends on')
|
|
2127
|
+
.map((n) => n.id);
|
|
2128
|
+
for (const fieldId of dependsOnFieldIds) {
|
|
2129
|
+
const currentRead = (0, planning_document_cjs_1.readNode)(dependsOnDoc, fieldId);
|
|
2130
|
+
if (!currentRead.ok)
|
|
2131
|
+
continue;
|
|
2132
|
+
const depMatch = /^(Phase\s+)(\d+(?:\.\d+)?)\b/i.exec(currentRead.value);
|
|
2133
|
+
if (!depMatch)
|
|
2134
|
+
continue;
|
|
2135
|
+
const [wholeMatch, prefix, num] = depMatch;
|
|
2136
|
+
const newValue = `${prefix}${decrementRoadmapPhaseToken(num, removedInt)}` + currentRead.value.slice(wholeMatch.length);
|
|
2137
|
+
if (newValue === currentRead.value)
|
|
2138
|
+
continue;
|
|
2139
|
+
const staged = (0, planning_document_cjs_1.setFieldValue)(dependsOnDoc, fieldId, newValue);
|
|
2140
|
+
if (staged.ok)
|
|
2141
|
+
dependsOnDoc = staged.value;
|
|
2142
|
+
}
|
|
2143
|
+
const dependsOnOut = (0, planning_document_cjs_1.serialize)(dependsOnDoc);
|
|
2144
|
+
if (dependsOnOut.ok)
|
|
2145
|
+
content = dependsOnOut.value;
|
|
2146
|
+
}
|
|
1997
2147
|
}
|
|
1998
2148
|
(0, shell_command_projection_cjs_1.platformWriteSync)(roadmapPath, content);
|
|
1999
2149
|
// #3685 / #3691: compare NORMALIZED bytes (what platformWriteSync actually
|
|
@@ -2017,21 +2167,22 @@ function updateRoadmapAfterPhaseRemoval(roadmapPath, targetPhase, isDecimal, rem
|
|
|
2017
2167
|
* phase, a stray 'Total Phases: 0' between fences). A file with no leading
|
|
2018
2168
|
* frontmatter is all body: the field goes to content start, preserving the
|
|
2019
2169
|
* former behavior for that shape.
|
|
2170
|
+
*
|
|
2171
|
+
* The block is the one `locateFrontmatterFence` finds (the one fence owner), so this writer
|
|
2172
|
+
* and every STATE.md reader agree on where the body starts. The blank line and the field go
|
|
2173
|
+
* right after the closing fence line, ended by that line's own line ending — so a CRLF file
|
|
2174
|
+
* gains CRLF lines only (#3572 review: a blanket re-join on '\r\n' doubled every carriage
|
|
2175
|
+
* return; joining the new lines on a bare '\n' mixed line endings).
|
|
2020
2176
|
*/
|
|
2021
2177
|
function insertStateBodyFieldAtTop(content, fieldLine) {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
const lines = content.split('\n');
|
|
2027
|
-
if ((lines[0] ?? '').trim() === '---') {
|
|
2028
|
-
const closeIdx = lines.findIndex((l, i) => i > 0 && l.trim() === '---');
|
|
2029
|
-
if (closeIdx !== -1) {
|
|
2030
|
-
lines.splice(closeIdx + 1, 0, '', fieldLine);
|
|
2031
|
-
return lines.join('\n');
|
|
2032
|
-
}
|
|
2178
|
+
const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
|
|
2179
|
+
if (fence?.closed) {
|
|
2180
|
+
const nl = content[fence.closingFenceEnd] === '\r' ? '\r\n' : '\n';
|
|
2181
|
+
return `${content.slice(0, fence.closingFenceEnd)}${nl}${nl}${fieldLine}${content.slice(fence.closingFenceEnd)}`;
|
|
2033
2182
|
}
|
|
2034
|
-
|
|
2183
|
+
// All body: the field is the new first line, ended like the document's first line.
|
|
2184
|
+
const firstNewline = content.indexOf('\n');
|
|
2185
|
+
return fieldLine + (firstNewline > 0 && content[firstNewline - 1] === '\r' ? '\r\n' : '\n') + content;
|
|
2035
2186
|
}
|
|
2036
2187
|
function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
2037
2188
|
if (!targetPhase)
|
|
@@ -2076,6 +2227,21 @@ function cmdPhaseRemove(cwd, targetPhase, options, raw) {
|
|
|
2076
2227
|
error(`Phase ${targetPhase} has ${summaryCount} executed plan(s). Use --force to remove anyway.`);
|
|
2077
2228
|
}
|
|
2078
2229
|
}
|
|
2230
|
+
// #5118 (no write before the error): the STATE.md rewrite below rebuilds the
|
|
2231
|
+
// frontmatter from EVERY surviving phase's report (buildStateFrontmatter →
|
|
2232
|
+
// isPhaseComplete), and it runs AFTER the directory removal, the sibling
|
|
2233
|
+
// renames and the ROADMAP rewrite. Validate every surviving phase's report
|
|
2234
|
+
// here, BEFORE the first write, so a report whose `status` is outside the
|
|
2235
|
+
// closed set fails this command having written nothing (the removed phase's
|
|
2236
|
+
// own report is not read afterwards — it is excluded). The set is the one the
|
|
2237
|
+
// rebuild scans (`statePhaseDirsToScan`: milestone-scoped, deduped), so a
|
|
2238
|
+
// survivor OUTSIDE that set — another milestone's phase directory — does not
|
|
2239
|
+
// block the remove.
|
|
2240
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'STATE.md'))) {
|
|
2241
|
+
const survivorStatusError = findVerificationStatusError(stateMod.statePhaseDirsToScan(cwd).filter((d) => d !== targetDir).map((d) => node_path_1.default.join(phasesDir, d)), { convention: resolvePhaseIdConvention(cwd) });
|
|
2242
|
+
if (survivorStatusError)
|
|
2243
|
+
throw survivorStatusError;
|
|
2244
|
+
}
|
|
2079
2245
|
if (targetDir)
|
|
2080
2246
|
node_fs_1.default.rmSync(node_path_1.default.join(phasesDir, targetDir), { recursive: true, force: true });
|
|
2081
2247
|
let renamedDirs = [];
|
|
@@ -2236,8 +2402,20 @@ function phaseDisplayNameFromRoadmap(roadmapContent, phaseNum) {
|
|
|
2236
2402
|
if (!roadmapContent || !phaseNum)
|
|
2237
2403
|
return null;
|
|
2238
2404
|
const phaseEscaped = phaseMarkdownRegexSource(phaseNum);
|
|
2239
|
-
//
|
|
2240
|
-
|
|
2405
|
+
// #5007 (Phase 6 / ADR-4910 §8): buildPhaseHeadingRegex (src/roadmap.cts) is
|
|
2406
|
+
// anchored `^...$` with no 'm' flag by design — it expects one
|
|
2407
|
+
// already-tokenized heading LINE, not a `^`/`m` scan against multi-line
|
|
2408
|
+
// content directly. Routed through tokenizeHeadings first (the same real
|
|
2409
|
+
// refactor searchPhaseInContent's own usage already requires), not a pure
|
|
2410
|
+
// regex-literal swap. Adopts buildPhaseHeadingRegex's ANY_BRACKET baseline —
|
|
2411
|
+
// a deliberate widening, matching getRoadmapModeForPhase's identical choice
|
|
2412
|
+
// above: this is a real-heading reader, so it inherits the same bracket-tag
|
|
2413
|
+
// tolerance searchPhaseInContent already applies everywhere else.
|
|
2414
|
+
const headingPattern = buildPhaseHeadingRegex(phaseEscaped);
|
|
2415
|
+
const matchedHeading = (0, markdown_sectionizer_cjs_1.tokenizeHeadings)(roadmapContent).find((h) => headingPattern.test(h.text));
|
|
2416
|
+
if (!matchedHeading)
|
|
2417
|
+
return null;
|
|
2418
|
+
const heading = matchedHeading.text.match(headingPattern);
|
|
2241
2419
|
if (!heading)
|
|
2242
2420
|
return null;
|
|
2243
2421
|
const name = heading[1].replace(/\(INSERTED\)/i, '').trim();
|
|
@@ -3045,27 +3223,12 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3045
3223
|
if (/status: diagnosed/.test(content))
|
|
3046
3224
|
warnings.push(`${file}: has diagnosed gaps`);
|
|
3047
3225
|
}
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
// dropping the human_needed/gaps_found advisory warning below.
|
|
3055
|
-
const content = normalizeLineEndings(node_fs_1.default.readFileSync(verificationFilePath, 'utf-8'));
|
|
3056
|
-
// #1159 (Defect A): read ONLY the frontmatter `status` key to avoid false positives
|
|
3057
|
-
// from historical metadata in the file body (e.g. `previous_status: gaps_found`).
|
|
3058
|
-
// A full-text regex like /status: gaps_found/ matches the substring inside
|
|
3059
|
-
// `previous_status: gaps_found`, producing spurious warnings even when the
|
|
3060
|
-
// current frontmatter status is `passed`.
|
|
3061
|
-
const verFm = extractFrontmatter(content, verificationFilePath);
|
|
3062
|
-
// Normalise to lower-case so `status: Passed` (title-case) is not missed.
|
|
3063
|
-
const verStatus = typeof verFm['status'] === 'string' ? verFm['status'].trim().toLowerCase() : '';
|
|
3064
|
-
if (verStatus === 'human_needed')
|
|
3065
|
-
warnings.push(`${file}: needs human verification`);
|
|
3066
|
-
if (verStatus === 'gaps_found')
|
|
3067
|
-
warnings.push(`${file}: has unresolved gaps`);
|
|
3068
|
-
}
|
|
3226
|
+
// #5118 (ADR-5057 Phase 4): the VERIFICATION report's `status` is no
|
|
3227
|
+
// longer read here. This pre-scan used to read each report's frontmatter
|
|
3228
|
+
// itself (case-folded, bypassing the owner) to warn on human_needed /
|
|
3229
|
+
// gaps_found — but the completion GATE below (readVerificationStatus)
|
|
3230
|
+
// already refuses those statuses and names the route, so the advisory
|
|
3231
|
+
// could only ever duplicate the gate's own answer. One reader: the gate.
|
|
3069
3232
|
}
|
|
3070
3233
|
catch {
|
|
3071
3234
|
/* best-effort (#2245 audit): this is an ADVISORY pre-scan of UAT/
|
|
@@ -3144,6 +3307,9 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3144
3307
|
// #2617: pass the project's runtime so the blocked-completion error below
|
|
3145
3308
|
// suggests the command surface this runtime actually installs
|
|
3146
3309
|
// ($gsd-… on Codex) rather than a hard-coded Claude-style string.
|
|
3310
|
+
// #5118: an out-of-set report status THROWS VerificationStatusError out
|
|
3311
|
+
// of here; withPlanningLock releases the lock on the way out and the CLI
|
|
3312
|
+
// seam reports `verification_status_invalid`.
|
|
3147
3313
|
const verificationStatus = readVerificationStatus(phaseFullDir, {
|
|
3148
3314
|
runtime: (0, runtime_slash_cjs_1.resolveRuntime)(cwd),
|
|
3149
3315
|
convention: resolvePhaseIdConvention(cwd),
|
|
@@ -3161,9 +3327,22 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3161
3327
|
staleCheckIndeterminate = true;
|
|
3162
3328
|
warnings.push(`verification staleness check could not complete for phase ${phaseNum} — routed as not-stale, but this was not actually verified (#3057)`);
|
|
3163
3329
|
}
|
|
3164
|
-
if (verificationStatus.status !==
|
|
3330
|
+
if (verificationStatus.status !== VERIFICATION_STATUS.PASSED) {
|
|
3165
3331
|
return verificationStatus;
|
|
3166
3332
|
}
|
|
3333
|
+
// #5118 (no write before the error): the transaction below writes ROADMAP,
|
|
3334
|
+
// REQUIREMENTS and STATE, and the STATE frontmatter rebuild reads EVERY
|
|
3335
|
+
// phase's report (buildStateFrontmatter → isPhaseComplete). Validate it
|
|
3336
|
+
// here, BEFORE the first write, over EXACTLY the set that rebuild scans
|
|
3337
|
+
// (the milestone-scoped, deduped phase set — the owner runs
|
|
3338
|
+
// buildStateFrontmatter itself, so `state sync` and this command refuse
|
|
3339
|
+
// for the same phases), so a report whose `status` is outside the closed
|
|
3340
|
+
// set fails this command having written nothing (withPlanningLock
|
|
3341
|
+
// releases the lock).
|
|
3342
|
+
const preflightStatePath = node_path_1.default.join(planningDir(cwd), 'STATE.md');
|
|
3343
|
+
if (node_fs_1.default.existsSync(preflightStatePath)) {
|
|
3344
|
+
assertVerificationReportsReadable(node_fs_1.default.readFileSync(preflightStatePath, 'utf-8'), cwd);
|
|
3345
|
+
}
|
|
3167
3346
|
const runPhaseCompleteTransaction = () => {
|
|
3168
3347
|
const writes = [];
|
|
3169
3348
|
let roadmapContent = null;
|
|
@@ -3357,7 +3536,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3357
3536
|
const plansResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Plans Complete', ` ${summaryCount}/${planCount} `);
|
|
3358
3537
|
if (plansResult.ok)
|
|
3359
3538
|
text = plansResult.value;
|
|
3360
|
-
const statusResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Status',
|
|
3539
|
+
const statusResult = (0, markdown_table_cjs_1.updateTableCell)(text, rowMatch, 'Status', ` ${(0, phase_status_cjs_1.toRoadmapStatusCell)(phase_status_cjs_1.PHASE_STATUS.COMPLETE).padEnd(11)} `);
|
|
3361
3540
|
if (statusResult.ok)
|
|
3362
3541
|
text = statusResult.value;
|
|
3363
3542
|
// Preserve only a valid ISO date (#1161: idempotent; self-heal
|
|
@@ -3384,8 +3563,12 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3384
3563
|
if (!planId)
|
|
3385
3564
|
continue;
|
|
3386
3565
|
const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
|
|
3387
|
-
|
|
3388
|
-
|
|
3566
|
+
// ADR-5057 §6 (Phase 13, #5217): the per-plan flip is a bullet
|
|
3567
|
+
// mutation, so it goes through `updateBullet` like the phase
|
|
3568
|
+
// checkbox flip above — first matching bullet only, never a
|
|
3569
|
+
// fenced line, never a mid-line occurrence.
|
|
3570
|
+
const planCheckboxPattern = new RegExp(`^(\\s*-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
|
|
3571
|
+
b = (0, markdown_sectionizer_cjs_1.updateBullet)(b, (_bulletText, rawLine) => planCheckboxPattern.test(rawLine), (rawLine) => rawLine.replace(planCheckboxPattern, '$1x$2'));
|
|
3389
3572
|
}
|
|
3390
3573
|
return b;
|
|
3391
3574
|
});
|
|
@@ -3423,9 +3606,23 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
|
|
|
3423
3606
|
if (node_fs_1.default.existsSync(reqPath)) {
|
|
3424
3607
|
const phaseEsc = phaseMarkdownRegexSource(phaseNum);
|
|
3425
3608
|
const currentMilestoneRoadmap = extractCurrentMilestone(roadmapContent, cwd);
|
|
3426
|
-
|
|
3427
|
-
//
|
|
3428
|
-
`
|
|
3609
|
+
// #5007 (Phase 6 / ADR-4910 §8): this site captures the entire
|
|
3610
|
+
// section BODY through to the next heading (via its own
|
|
3611
|
+
// `[\s\S]*?` lookahead-to-next-heading structure) — neither shared
|
|
3612
|
+
// owner returns that shape, so phaseHeadingPrefixSrcFor is composed
|
|
3613
|
+
// directly, keeping this site's own lookahead. The prior hand-rolled
|
|
3614
|
+
// heading-marker-plus-label literal appeared TWICE in this source
|
|
3615
|
+
// (the main capture prefix AND the lookahead) — both occurrences
|
|
3616
|
+
// were the literal-drift detector's target
|
|
3617
|
+
// (findPhaseHeadingScanLiteralDrift is a pure literal-text match,
|
|
3618
|
+
// blind to what surrounds it), so both are replaced with the same
|
|
3619
|
+
// composed prefix, built once via phaseHeadingPrefixSrcFor and
|
|
3620
|
+
// reused, so the two halves cannot drift from each other. No
|
|
3621
|
+
// convention argument: LABEL_ONLY with convention=undefined
|
|
3622
|
+
// reproduces the prior `Phase\s+` literal byte-for-byte — this
|
|
3623
|
+
// feeds cmdPhaseComplete, so no widening was introduced.
|
|
3624
|
+
const phaseSectionHeadingPrefix = `#{2,4}\\s*${phaseHeadingPrefixSrcFor(PHASE_HEADING_BASELINE.LABEL_ONLY)}`;
|
|
3625
|
+
const phaseSectionMatch = currentMilestoneRoadmap.match(new RegExp(`(${phaseSectionHeadingPrefix}${phaseEsc}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][\\s\\S]*?)(?=${phaseSectionHeadingPrefix}|$)`, 'i'));
|
|
3429
3626
|
const sectionText = phaseSectionMatch ? phaseSectionMatch[1] : '';
|
|
3430
3627
|
// #4731: multiline-aware — hard-wrapped Requirements read past the
|
|
3431
3628
|
// line break before the ID scan. The shared extractor also stops at
|
|
@@ -4209,6 +4406,9 @@ function cmdPhaseUatPassed(cwd, phaseNum, raw, opts = {}) {
|
|
|
4209
4406
|
const phaseFullDir = node_path_1.default.join(cwd, phaseInfo['directory']);
|
|
4210
4407
|
const report = evaluateUatPassed(phaseFullDir, { policy: opts.policy });
|
|
4211
4408
|
output({ phase: phaseNum, ...report }, raw);
|
|
4409
|
+
// #5170: the exit status follows the verdict (status mode: a failing verdict is exit 1). Declared
|
|
4410
|
+
// AFTER output(), which rewrites the pending-outcome cell. The JSON above is unchanged.
|
|
4411
|
+
(0, gate_exit_cjs_1.declareGateExit)((0, gate_verdict_cjs_1.gateVerdict)(report.passed ? 'pass' : 'block', !report.passed, { phase: phaseNum, ...report }), 'status');
|
|
4212
4412
|
}
|
|
4213
4413
|
// #1437 — phase.list-plans: list plan files for a given phase number.
|
|
4214
4414
|
// Returns the full scan result from scanPhasePlans so callers can read plan
|