@opengsd/gsd-core 1.18.0 → 1.20.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 +2 -1
- package/.opencode/plugins/gsd-core.js +2 -3
- package/agents/gsd-code-fixer.compact.md +7 -7
- package/agents/gsd-code-fixer.md +13 -7
- package/agents/gsd-code-reviewer.compact.md +49 -8
- package/agents/gsd-code-reviewer.md +53 -14
- package/agents/gsd-executor.md +8 -4
- package/agents/gsd-verifier.md +1 -1
- package/bin/install.js +422 -103
- package/commands/gsd/update.md +5 -2
- package/gsd-core/bin/gsd-tools.cjs +179 -30
- package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
- package/gsd-core/bin/lib/args-terminator.cjs +114 -0
- package/gsd-core/bin/lib/broken-windows.cjs +59 -1
- package/gsd-core/bin/lib/capability-lifecycle.cjs +1 -12
- package/gsd-core/bin/lib/capability-lock.cjs +13 -2
- package/gsd-core/bin/lib/capability-registry.cjs +376 -103
- package/gsd-core/bin/lib/capability-state.cjs +14 -8
- package/gsd-core/bin/lib/capability-validator.cjs +32 -0
- package/gsd-core/bin/lib/commands.cjs +27 -17
- package/gsd-core/bin/lib/config-loader.cjs +13 -3
- package/gsd-core/bin/lib/config.cjs +256 -58
- package/gsd-core/bin/lib/core-utils.cjs +45 -0
- package/gsd-core/bin/lib/decision-coverage-support.cjs +74 -0
- package/gsd-core/bin/lib/frontmatter.cjs +21 -3
- package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
- package/gsd-core/bin/lib/gate-context-drift.cjs +9 -4
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +6 -1
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +6 -1
- package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
- package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
- package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +63 -22
- package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
- package/gsd-core/bin/lib/git-info-exclude.cjs +52 -0
- package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +31 -12
- package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
- package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -3
- package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
- package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
- package/gsd-core/bin/lib/host-integration.cjs +13 -6
- package/gsd-core/bin/lib/init.cjs +245 -68
- package/gsd-core/bin/lib/install-engine.cjs +246 -32
- package/gsd-core/bin/lib/install-profiles.cjs +9 -4
- package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
- package/gsd-core/bin/lib/io.cjs +6 -0
- package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +147 -1
- package/gsd-core/bin/lib/markdown-table.cjs +23 -0
- package/gsd-core/bin/lib/mcp-server.cjs +8 -5
- package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
- package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
- package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
- package/gsd-core/bin/lib/milestone.cjs +61 -51
- package/gsd-core/bin/lib/model-catalog.cjs +8 -0
- package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
- package/gsd-core/bin/lib/phase-entry-grammar.cjs +119 -0
- package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
- package/gsd-core/bin/lib/phase-id.cjs +629 -38
- package/gsd-core/bin/lib/phase-lifecycle.cjs +12 -44
- package/gsd-core/bin/lib/phase-locator.cjs +328 -19
- package/gsd-core/bin/lib/phase.cjs +3066 -653
- package/gsd-core/bin/lib/planning-document.cjs +26 -5
- package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
- package/gsd-core/bin/lib/planning-snapshot.cjs +15 -8
- package/gsd-core/bin/lib/planning-workspace.cjs +21 -3
- package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
- package/gsd-core/bin/lib/read-only-git-env.cjs +79 -0
- package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +57 -2
- package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
- package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
- package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +735 -137
- package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
- package/gsd-core/bin/lib/roadmap-upgrade.cjs +47 -7
- package/gsd-core/bin/lib/roadmap.cjs +383 -123
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +381 -14
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
- package/gsd-core/bin/lib/shell-command-projection.cjs +151 -19
- package/gsd-core/bin/lib/smart-entry.cjs +35 -11
- package/gsd-core/bin/lib/state-contract.cjs +39 -15
- package/gsd-core/bin/lib/state-document.cjs +344 -27
- package/gsd-core/bin/lib/state-transition.cjs +156 -32
- package/gsd-core/bin/lib/state.cjs +527 -186
- package/gsd-core/bin/lib/surface.cjs +9 -0
- package/gsd-core/bin/lib/task-command-router.cjs +5 -25
- package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
- package/gsd-core/bin/lib/template.cjs +2 -2
- package/gsd-core/bin/lib/validate.cjs +3 -2
- package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
- package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
- package/gsd-core/bin/lib/verification.cjs +96 -9
- package/gsd-core/bin/lib/verify.cjs +4 -2
- package/gsd-core/bin/lib/workstream-inventory.cjs +26 -20
- package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
- package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
- package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
- package/gsd-core/bin/verify-reapply-patches.cjs +411 -10
- package/gsd-core/references/cicd-review-patterns.md +45 -0
- package/gsd-core/references/git-integration.md +11 -0
- package/gsd-core/references/loop-hook-dispatch.md +13 -6
- package/gsd-core/references/necessity-review.md +48 -0
- package/gsd-core/references/planning-config.md +2 -0
- package/gsd-core/references/tdd.md +2 -2
- package/gsd-core/templates/DEBUG.md +16 -0
- package/gsd-core/templates/config.json +2 -1
- package/gsd-core/templates/verification-report.md +1 -1
- package/gsd-core/workflows/audit-fix.md +2 -1
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
- package/gsd-core/workflows/code-review-fix.md +46 -5
- package/gsd-core/workflows/code-review.md +64 -7
- package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
- package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
- package/gsd-core/workflows/complete-milestone.md +37 -150
- package/gsd-core/workflows/diagnose-issues.md +29 -5
- package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +33 -2
- package/gsd-core/workflows/discuss-phase-power.md +19 -2
- package/gsd-core/workflows/discuss-phase.md +6 -10
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +7 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +2 -2
- package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
- package/gsd-core/workflows/execute-phase.md +10 -6
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/health.md +2 -1
- package/gsd-core/workflows/import.md +1 -1
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +2 -2
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
- package/gsd-core/workflows/plan-phase.md +14 -14
- package/gsd-core/workflows/pr-branch.md +2 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
- package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
- package/gsd-core/workflows/quick.md +1 -1
- package/gsd-core/workflows/reapply-patches.md +104 -0
- package/gsd-core/workflows/review.md +70 -154
- package/gsd-core/workflows/section-manifest.json +5 -0
- package/gsd-core/workflows/secure-phase.md +1 -1
- package/gsd-core/workflows/settings-advanced.md +57 -9
- package/gsd-core/workflows/settings.md +1 -0
- package/gsd-core/workflows/ship.md +15 -4
- package/gsd-core/workflows/spec-phase.md +3 -6
- package/gsd-core/workflows/verify-work.md +4 -3
- package/hooks/dist/gsd-agent-isolation-guard.js +11 -1
- package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
- package/hooks/dist/gsd-graphify-update.sh +6 -0
- package/hooks/dist/gsd-path-export.js +95 -0
- package/hooks/dist/gsd-secret-read-guard.js +172 -39
- package/hooks/dist/gsd-statusline.js +37 -3
- package/hooks/dist/gsd-windsurf-pre-write.js +29 -2
- package/hooks/dist/gsd-workflow-guard.js +3 -2
- package/hooks/dist/gsd-worktree-path-guard.js +29 -2
- package/hooks/dist/lib/git-probe.js +16 -0
- package/hooks/dist/lib/isolation-sentinel.js +44 -0
- package/hooks/dist/managed-hooks-registry.cjs +1 -0
- package/hooks/gsd-agent-isolation-guard.js +11 -1
- package/hooks/gsd-cursor-subagent-start.js +6 -2
- package/hooks/gsd-graphify-update.sh +6 -0
- package/hooks/gsd-path-export.js +95 -0
- package/hooks/gsd-secret-read-guard.js +172 -39
- package/hooks/gsd-statusline.js +37 -3
- package/hooks/gsd-windsurf-pre-write.js +29 -2
- package/hooks/gsd-workflow-guard.js +3 -2
- package/hooks/gsd-worktree-path-guard.js +29 -2
- package/hooks/hooks.json +1 -0
- package/hooks/lib/git-probe.js +16 -0
- package/hooks/lib/isolation-sentinel.js +44 -0
- package/hooks/managed-hooks-registry.cjs +1 -0
- package/package.json +15 -8
- package/pi/gsd.cjs +1047 -10
- package/pi/package-agents/gsd-advisor-researcher.md +113 -0
- package/pi/package-agents/gsd-ai-researcher.md +111 -0
- package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
- package/pi/package-agents/gsd-code-fixer.md +774 -0
- package/pi/package-agents/gsd-code-reviewer.md +441 -0
- package/pi/package-agents/gsd-codebase-mapper.md +850 -0
- package/pi/package-agents/gsd-debug-session-manager.md +416 -0
- package/pi/package-agents/gsd-debugger.md +1274 -0
- package/pi/package-agents/gsd-doc-classifier.md +269 -0
- package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
- package/pi/package-agents/gsd-doc-verifier.md +212 -0
- package/pi/package-agents/gsd-doc-writer.md +626 -0
- package/pi/package-agents/gsd-dom-verifier.md +164 -0
- package/pi/package-agents/gsd-domain-researcher.md +144 -0
- package/pi/package-agents/gsd-eval-auditor.md +185 -0
- package/pi/package-agents/gsd-eval-planner.md +149 -0
- package/pi/package-agents/gsd-executor.md +898 -0
- package/pi/package-agents/gsd-framework-selector.md +159 -0
- package/pi/package-agents/gsd-integration-checker.md +476 -0
- package/pi/package-agents/gsd-intel-updater.md +338 -0
- package/pi/package-agents/gsd-mempalace-curator.md +49 -0
- package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
- package/pi/package-agents/gsd-pattern-mapper.md +340 -0
- package/pi/package-agents/gsd-phase-researcher.md +897 -0
- package/pi/package-agents/gsd-plan-checker.md +1077 -0
- package/pi/package-agents/gsd-planner.md +1003 -0
- package/pi/package-agents/gsd-project-researcher.md +611 -0
- package/pi/package-agents/gsd-research-synthesizer.md +259 -0
- package/pi/package-agents/gsd-roadmapper.md +779 -0
- package/pi/package-agents/gsd-security-auditor.md +171 -0
- package/pi/package-agents/gsd-ui-auditor.md +581 -0
- package/pi/package-agents/gsd-ui-checker.md +417 -0
- package/pi/package-agents/gsd-ui-researcher.md +469 -0
- package/pi/package-agents/gsd-user-profiler.md +174 -0
- package/pi/package-agents/gsd-verifier.md +963 -0
- package/pi/package-agents/manifest.json +39 -0
- package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
- package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
- package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
- package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
- package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
- package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
- package/pi/package-skills/gsd-capture/SKILL.md +68 -0
- package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
- package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
- package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
- package/pi/package-skills/gsd-config/SKILL.md +60 -0
- package/pi/package-skills/gsd-debug/SKILL.md +64 -0
- package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
- package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
- package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-explore/SKILL.md +26 -0
- package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
- package/pi/package-skills/gsd-fast/SKILL.md +33 -0
- package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
- package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
- package/pi/package-skills/gsd-health/SKILL.md +36 -0
- package/pi/package-skills/gsd-help/SKILL.md +37 -0
- package/pi/package-skills/gsd-import/SKILL.md +46 -0
- package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
- package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
- package/pi/package-skills/gsd-manager/SKILL.md +46 -0
- package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
- package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
- package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
- package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
- package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
- package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
- package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
- package/pi/package-skills/gsd-next/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
- package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
- package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
- package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
- package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
- package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
- package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
- package/pi/package-skills/gsd-phase/SKILL.md +61 -0
- package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
- package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
- package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
- package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
- package/pi/package-skills/gsd-progress/SKILL.md +51 -0
- package/pi/package-skills/gsd-quick/SKILL.md +179 -0
- package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
- package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
- package/pi/package-skills/gsd-review/SKILL.md +50 -0
- package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
- package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-settings/SKILL.md +30 -0
- package/pi/package-skills/gsd-ship/SKILL.md +27 -0
- package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
- package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
- package/pi/package-skills/gsd-spike/SKILL.md +53 -0
- package/pi/package-skills/gsd-stats/SKILL.md +22 -0
- package/pi/package-skills/gsd-surface/SKILL.md +177 -0
- package/pi/package-skills/gsd-thread/SKILL.md +29 -0
- package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
- package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
- package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
- package/pi/package-skills/gsd-undo/SKILL.md +38 -0
- package/pi/package-skills/gsd-update/SKILL.md +54 -0
- package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
- package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
- package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
- package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
- package/scripts/build-hooks.js +4 -0
- package/scripts/ci-next-health.cjs +246 -23
- package/scripts/command-contract-helpers.cjs +1 -1
- package/scripts/docs-guard-registry.cjs +22 -7
- package/scripts/gen-features.cjs +14 -2
- package/scripts/gen-health-docs.cjs +13 -1
- package/scripts/gen-pi-package-skills.cjs +217 -0
- package/scripts/lib/drift-scan.cjs +27 -0
- package/scripts/lib/macos-conformance-tier.generated.cjs +17 -1
- package/scripts/lib/platform-conformance-tier.generated.cjs +37 -1
- package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
- package/scripts/lint-phase-enumeration-drift.cjs +8 -4
- package/scripts/lint-retired-runtime-name.cjs +5 -0
- package/scripts/lint-roadmap-progress-drift.cjs +421 -0
- package/scripts/mutation-matrix.cjs +23 -1
- package/scripts/npm-audit-baseline.cjs +41 -7
- package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
- package/scripts/select-docs-guards.cjs +1 -1
- package/scripts/self-test-report-policy.cjs +350 -0
- package/scripts/sync-next-version.cjs +38 -6
- package/skills/gsd-update/SKILL.md +5 -2
- package/vscode/package.json +1 -1
- package/scripts/diagnose-validate-commit-in-tier.cjs +0 -270
- package/scripts/diagnose-validate-commit-phases.cjs +0 -348
- package/scripts/xtrace-phase-timing.cjs +0 -205
|
@@ -15,6 +15,7 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
15
15
|
const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
|
|
16
16
|
const clock_cjs_1 = require("./clock.cjs");
|
|
17
17
|
const pattern_cjs_1 = require("./pattern.cjs");
|
|
18
|
+
const phase_entry_grammar_cjs_1 = require("./phase-entry-grammar.cjs");
|
|
18
19
|
const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
|
|
19
20
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- io.cjs is an export= CommonJS module
|
|
20
21
|
const io = require("./io.cjs");
|
|
@@ -33,6 +34,8 @@ const roadmapParser = require("./roadmap-parser.cjs");
|
|
|
33
34
|
const coreUtils = require("./core-utils.cjs");
|
|
34
35
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
|
|
35
36
|
const phaseId = require("./phase-id.cjs");
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id-display.cjs is an export= CommonJS module
|
|
38
|
+
const phaseIdDisplayMod = require("./phase-id-display.cjs");
|
|
36
39
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- worktree-safety.cjs is an export= CommonJS module
|
|
37
40
|
const worktreeSafety = require("./worktree-safety.cjs");
|
|
38
41
|
// eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-workspace.cjs is an export= CommonJS module
|
|
@@ -93,12 +96,13 @@ const { harvestPriorVerifyCommands } = verifyCommandGrounding;
|
|
|
93
96
|
const { output, error, ERROR_REASON, formatDiagnosticToken } = io;
|
|
94
97
|
const { loadConfig, loadConfigResolved } = configLoader;
|
|
95
98
|
const { resolveModelInternal, resolveGranularityInternal, assertValidGranularityOverride } = modelResolver;
|
|
96
|
-
const { findPhaseInternal, listMilestonePhaseDirs, listAllPhaseDirs } = phaseLocator;
|
|
99
|
+
const { phaseDirectoryOptions, findPhaseInternal, listMilestonePhaseDirs, listAllPhaseDirs, } = phaseLocator;
|
|
97
100
|
const { getRoadmapPhaseInternal, getMilestoneInfo, stripShippedMilestones, extractCurrentMilestone, } = roadmapParser;
|
|
98
101
|
const { pathExistsInternal, generateSlugInternal, toPosixPath } = coreUtils;
|
|
99
|
-
const { comparePhaseNum, normalizePhaseName, stripProjectCodePrefix, PHASE_NUMBER_TOKEN_SOURCE, PHASE_DEP_REF_SOURCE, isForeignPrefixedPhaseQuery, isSentinelPhaseId, extractPhaseToken, scopeToPhase, renderPhaseBranchName, parsePhaseId, renderPhaseId, phaseHeadingPrefixSrcFor, PHASE_HEADING_BASELINE, buildPhaseHeadingScanRegex, } = phaseId;
|
|
102
|
+
const { comparePhaseNum, phaseDirectoryName, customPhaseQuery, customDirectoryOwner, phaseScanTokenSource, phaseKeyFromToken, phaseDirectoryContext, normalizePhaseName, stripProjectCodePrefix, PHASE_NUMBER_TOKEN_SOURCE, PHASE_DEP_REF_SOURCE, extractPhaseDependencyTokens, isForeignPrefixedPhaseQuery, isSentinelPhaseId, extractPhaseToken, scopeToPhase, renderPhaseBranchName, parsePhaseId, renderPhaseId, bracketQualifiedKey, phaseHeadingPrefixSrcFor, parsePhaseChecklistLine, PHASE_HEADING_BASELINE, buildPhaseHeadingScanRegex, } = phaseId;
|
|
103
|
+
const { phaseToken } = phaseIdDisplayMod;
|
|
100
104
|
const { pruneOrphanedWorktrees } = worktreeSafety;
|
|
101
|
-
const { planningPaths, planningDir, planningRoot, todosDir, listAvailableWorkstreams, peekActiveWorkstream, resolveEnvWorkstream, diagnoseUnresolvedActiveWorkstream, describeUnresolvedWorkstreamReason, findContextMdIn, resolvePhaseIdConvention, worktreesOptedOut, } = planningWorkspace;
|
|
105
|
+
const { planningPaths, planningDir, planningRoot, todosDir, listAvailableWorkstreams, peekActiveWorkstream, resolveEnvWorkstream, diagnoseUnresolvedActiveWorkstream, describeUnresolvedWorkstreamReason, findContextMdIn, findPhaseSpecIn, resolvePhaseIdConvention, worktreesOptedOut, } = planningWorkspace;
|
|
102
106
|
const { extractFrontmatter, frontmatterBlock } = frontmatterMod;
|
|
103
107
|
const { isPhaseComplete, resolveVerificationFile, resolveUatFile, VERIFICATION_STATUS, VerificationStatusError } = verificationMod;
|
|
104
108
|
const { evaluateUatPassed } = uatPredicateMod;
|
|
@@ -134,8 +138,8 @@ function roadmapPhaseMatchesExactPrefix(roadmapPhase, phase) {
|
|
|
134
138
|
// #2104: shared helpers that wrap findPhaseInternal / getRoadmapPhaseInternal
|
|
135
139
|
// with the #2056 foreign-prefix guard, so every init command gets the same
|
|
136
140
|
// protection without duplicating the guard logic at each call site.
|
|
137
|
-
function guardedFindPhase(cwd, phase, projectCode) {
|
|
138
|
-
let phaseInfo = findPhaseInternal(cwd, phase);
|
|
141
|
+
function guardedFindPhase(cwd, phase, projectCode, phaseNaming) {
|
|
142
|
+
let phaseInfo = findPhaseInternal(cwd, phase, undefined, phaseDirectoryOptions(cwd, { phase_naming: phaseNaming, project_code: projectCode }));
|
|
139
143
|
if (isForeignPrefixedPhaseQuery(phase, projectCode) && !phaseInfoMatchesExactPrefix(phaseInfo, phase)) {
|
|
140
144
|
phaseInfo = null;
|
|
141
145
|
}
|
|
@@ -177,6 +181,28 @@ function resolvePhaseDisplayName(phaseInfo, roadmapPhase) {
|
|
|
177
181
|
|| phaseInfo?.['phase_name']
|
|
178
182
|
|| null;
|
|
179
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* #5629: the phase SPEC as init facts, for every init payload a SPEC-consuming workflow parses
|
|
186
|
+
* (discuss-phase via `phase-op`, discuss-phase-assumptions, plan-phase). `has_spec` is always present;
|
|
187
|
+
* `spec_path` (absolute, posix) only when a plain `NN-SPEC.md` exists. One predicate (`findPhaseSpecIn`, shared
|
|
188
|
+
* with the context-drift gate) decides, so a UI-SPEC or AI-SPEC is never mistaken for the requirements lock and
|
|
189
|
+
* no workflow globs for it. A phase with no directory yet, or one that cannot be read, has no SPEC.
|
|
190
|
+
*/
|
|
191
|
+
function phaseSpecFacts(phaseDirAbs) {
|
|
192
|
+
if (!phaseDirAbs)
|
|
193
|
+
return { has_spec: false };
|
|
194
|
+
let files;
|
|
195
|
+
try {
|
|
196
|
+
files = node_fs_1.default.readdirSync(phaseDirAbs);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
return { has_spec: false };
|
|
200
|
+
}
|
|
201
|
+
// Scoped to THIS phase's own artifacts first (#3511), like the sibling artifact lookups: a misfiled
|
|
202
|
+
// `02-SPEC.md` in phase 03's directory must not win (the context-drift gate scopes the same way).
|
|
203
|
+
const spec = findPhaseSpecIn(scopeToPhase(files, node_path_1.default.basename(phaseDirAbs)));
|
|
204
|
+
return spec ? { has_spec: true, spec_path: toPosixPath(node_path_1.default.join(phaseDirAbs, spec)) } : { has_spec: false };
|
|
205
|
+
}
|
|
180
206
|
/**
|
|
181
207
|
* #2994 (review finding, DEFECT.GENERATIVE-FIX): shared archived/not-found
|
|
182
208
|
* fallback applied identically by `cmdInitExecutePhase`, `cmdInitPlanPhase`,
|
|
@@ -600,6 +626,28 @@ function detectFallowConfig(cwd) {
|
|
|
600
626
|
function detectGitCreateTag(cwd) {
|
|
601
627
|
return readConfigJsonValue(cwd, ['git', 'create_tag']) !== false;
|
|
602
628
|
}
|
|
629
|
+
/**
|
|
630
|
+
* `git.repo_tagging` ground truth (#5706, epic #3628): the milestone tagging
|
|
631
|
+
* mode exposed to `complete-milestone.md`. Only the documented `per_repo`
|
|
632
|
+
* opts in; an absent key or any other stored value reads as the default
|
|
633
|
+
* `single` (today's behavior), matching `loadConfig`'s resolution.
|
|
634
|
+
*/
|
|
635
|
+
function detectRepoTagging(cwd) {
|
|
636
|
+
return readConfigJsonValue(cwd, ['git', 'repo_tagging']) === 'per_repo' ? 'per_repo' : 'single';
|
|
637
|
+
}
|
|
638
|
+
/**
|
|
639
|
+
* The configured `planning.sub_repos` (#5706): an array of non-empty repo
|
|
640
|
+
* names, possibly empty. A legacy top-level `sub_repos` is honored when the
|
|
641
|
+
* nested key is absent, the same flat-then-nested order `loadConfig` applies.
|
|
642
|
+
* Non-string members are dropped; any degraded read yields `[]`.
|
|
643
|
+
*/
|
|
644
|
+
function detectSubRepos(cwd) {
|
|
645
|
+
const nested = readConfigJsonValue(cwd, ['planning', 'sub_repos']);
|
|
646
|
+
const raw = nested !== undefined ? nested : readConfigJsonValue(cwd, ['sub_repos']);
|
|
647
|
+
if (!Array.isArray(raw))
|
|
648
|
+
return [];
|
|
649
|
+
return raw.filter((name) => typeof name === 'string' && name.trim() !== '');
|
|
650
|
+
}
|
|
603
651
|
/**
|
|
604
652
|
* `state:phase-mvp-mode` ground truth (design doc §Behavior table: ROADMAP.md
|
|
605
653
|
* `**Mode:** mvp` for the CURRENT phase). Bounded, non-throwing — an absent
|
|
@@ -858,7 +906,7 @@ function cmdInitExecutePhase(cwd, phase, raw, options = {}) {
|
|
|
858
906
|
error('phase required for init execute-phase');
|
|
859
907
|
}
|
|
860
908
|
const config = loadConfig(cwd);
|
|
861
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
909
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
862
910
|
// #3216: getMilestoneInfo now returns a ScopedResult — `.value` carries the
|
|
863
911
|
// MilestoneInfo (or null on any non-COMPLETE scope). NOT display-only: when
|
|
864
912
|
// `branching_strategy === 'milestone'`, `milestone['version']`/`['name']`
|
|
@@ -1033,7 +1081,7 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
1033
1081
|
}
|
|
1034
1082
|
const config = loadConfig(cwd);
|
|
1035
1083
|
// #2056/#2104: foreign-prefixed queries must not collapse to numeric phases.
|
|
1036
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
1084
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1037
1085
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
|
1038
1086
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
|
1039
1087
|
const phaseName = rp['phase_name'];
|
|
@@ -1067,12 +1115,14 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
1067
1115
|
const phaseNamePlan = resolvePhaseDisplayName(phaseInfo, roadmapPhase);
|
|
1068
1116
|
const rawProjectCodePlan = config.project_code || '';
|
|
1069
1117
|
let expectedPhaseDirPlan = null;
|
|
1070
|
-
|
|
1118
|
+
const namingContextPlan = phaseDirectoryContext(config);
|
|
1119
|
+
const customNamingPlan = customPhaseQuery(phase, namingContextPlan) !== null;
|
|
1120
|
+
if ((!phaseDirPlan || customNamingPlan) && phaseNumberPlan && phaseNamePlan) {
|
|
1071
1121
|
const paddedNum = normalizePhaseName(phaseNumberPlan);
|
|
1072
|
-
const
|
|
1122
|
+
const expectedName = roadmapPhase?.['phase_name'] || phaseNamePlan;
|
|
1123
|
+
const slug = (generateSlugInternal(expectedName) || '').substring(0, 60);
|
|
1073
1124
|
if (slug) {
|
|
1074
|
-
const
|
|
1075
|
-
const dirName = `${prefix}${paddedNum}-${slug}`;
|
|
1125
|
+
const dirName = phaseDirectoryName(customPhaseQuery(phaseNumberPlan, namingContextPlan) ? phaseNumberPlan : paddedNum, slug, rawProjectCodePlan);
|
|
1076
1126
|
// #2376: absolute — see comment on phase_dir below.
|
|
1077
1127
|
expectedPhaseDirPlan = toPosixPath(node_path_1.default.join(planningPaths(cwd).phases, dirName));
|
|
1078
1128
|
}
|
|
@@ -1139,6 +1189,8 @@ function cmdInitPlanPhase(cwd, phase, raw, options = {}) {
|
|
|
1139
1189
|
state_path: node_fs_1.default.existsSync(statePath) ? toPosixPath(statePath) : null,
|
|
1140
1190
|
roadmap_path: node_fs_1.default.existsSync(roadmapPath) ? toPosixPath(roadmapPath) : null,
|
|
1141
1191
|
requirements_path: node_fs_1.default.existsSync(requirementsPath) ? toPosixPath(requirementsPath) : null,
|
|
1192
|
+
// #5629: the phase SPEC (plan-phase lifts its Edge Coverage from it; no workflow globs for it).
|
|
1193
|
+
...phaseSpecFacts(phaseInfo?.['directory'] ? node_path_1.default.join(cwd, phaseInfo['directory']) : null),
|
|
1142
1194
|
patterns_path: null,
|
|
1143
1195
|
};
|
|
1144
1196
|
if (phaseInfo?.['directory']) {
|
|
@@ -1661,7 +1713,7 @@ function cmdInitVerifyWork(cwd, phase, raw) {
|
|
|
1661
1713
|
}
|
|
1662
1714
|
const config = loadConfig(cwd);
|
|
1663
1715
|
const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
|
|
1664
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
1716
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1665
1717
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
|
1666
1718
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
|
1667
1719
|
const phaseName = rp['phase_name'];
|
|
@@ -1768,7 +1820,7 @@ function cmdInitVerifyWork(cwd, phase, raw) {
|
|
|
1768
1820
|
*/
|
|
1769
1821
|
function cmdInitCodeReview(cwd, phase, raw, options = {}) {
|
|
1770
1822
|
const config = loadConfig(cwd);
|
|
1771
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
1823
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1772
1824
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
|
1773
1825
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
|
1774
1826
|
const rpName = rp['phase_name'];
|
|
@@ -1834,7 +1886,7 @@ function cmdInitCodeReview(cwd, phase, raw, options = {}) {
|
|
|
1834
1886
|
*/
|
|
1835
1887
|
function cmdInitReview(cwd, phase, raw, options = {}) {
|
|
1836
1888
|
const config = loadConfig(cwd);
|
|
1837
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
1889
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1838
1890
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
|
1839
1891
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => ({
|
|
1840
1892
|
found: true,
|
|
@@ -1893,7 +1945,7 @@ function cmdInitReview(cwd, phase, raw, options = {}) {
|
|
|
1893
1945
|
*/
|
|
1894
1946
|
function cmdInitDiscussPhaseAssumptions(cwd, phase, raw, options = {}) {
|
|
1895
1947
|
const config = loadConfig(cwd);
|
|
1896
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
1948
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1897
1949
|
const roadmapPhase = guardedGetRoadmapPhase(cwd, phase, config.project_code);
|
|
1898
1950
|
phaseInfo = applyRoadmapFallback(phaseInfo, roadmapPhase, (rp) => {
|
|
1899
1951
|
const rpName = rp['phase_name'];
|
|
@@ -1934,6 +1986,8 @@ function cmdInitDiscussPhaseAssumptions(cwd, phase, raw, options = {}) {
|
|
|
1934
1986
|
plan_count: phaseInfo?.['plans']?.length || 0,
|
|
1935
1987
|
roadmap_exists: node_fs_1.default.existsSync(node_path_1.default.join(planningDir(cwd), 'ROADMAP.md')),
|
|
1936
1988
|
planning_exists: node_fs_1.default.existsSync(planningDir(cwd)),
|
|
1989
|
+
// #5629: the phase SPEC, so assumptions mode honours it like the default flow does.
|
|
1990
|
+
...phaseSpecFacts(phaseDir ? node_path_1.default.join(cwd, phaseDir) : null),
|
|
1937
1991
|
};
|
|
1938
1992
|
// #2994 (Phase 6.3): additive, optional field — degrades to null, never throws.
|
|
1939
1993
|
const sectionManifestOptions = {
|
|
@@ -1947,7 +2001,7 @@ function cmdInitDiscussPhaseAssumptions(cwd, phase, raw, options = {}) {
|
|
|
1947
2001
|
}
|
|
1948
2002
|
function cmdInitPhaseOp(cwd, phase, raw) {
|
|
1949
2003
|
const config = loadConfig(cwd);
|
|
1950
|
-
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code);
|
|
2004
|
+
let phaseInfo = guardedFindPhase(cwd, phase, config.project_code, config.phase_naming);
|
|
1951
2005
|
// #2237: surface ambiguous phase-directory collisions instead of silently
|
|
1952
2006
|
// taking the first match when unrelated projects share a .planning/phases/ tree.
|
|
1953
2007
|
if (phaseInfo?.['ambiguous_matches']) {
|
|
@@ -2065,6 +2119,8 @@ function cmdInitPhaseOp(cwd, phase, raw) {
|
|
|
2065
2119
|
state_path: node_fs_1.default.existsSync(statePath) ? toPosixPath(statePath) : null,
|
|
2066
2120
|
roadmap_path: node_fs_1.default.existsSync(roadmapPath) ? toPosixPath(roadmapPath) : null,
|
|
2067
2121
|
requirements_path: node_fs_1.default.existsSync(requirementsPath) ? toPosixPath(requirementsPath) : null,
|
|
2122
|
+
// #5629: the phase SPEC (discuss-phase reads it from here; no workflow globs for it).
|
|
2123
|
+
...phaseSpecFacts(phaseDir ? node_path_1.default.join(cwd, phaseDir) : null),
|
|
2068
2124
|
};
|
|
2069
2125
|
if (phaseInfo?.['directory']) {
|
|
2070
2126
|
const phaseDirFull = node_path_1.default.join(cwd, phaseInfo['directory']);
|
|
@@ -2548,37 +2604,37 @@ function cmdInitManager(cwd, raw) {
|
|
|
2548
2604
|
}
|
|
2549
2605
|
const rawContent = node_fs_1.default.readFileSync(paths.roadmap, 'utf-8');
|
|
2550
2606
|
const content = extractCurrentMilestone(rawContent, cwd);
|
|
2607
|
+
const directoryContext = phaseDirectoryOptions(cwd, config, rawContent);
|
|
2551
2608
|
// #3185 (ADR-3180 Decision 1): "which phase directories belong to the
|
|
2552
2609
|
// CURRENT milestone" is the scoped question listMilestonePhaseDirs owns —
|
|
2553
2610
|
// routed through it instead of a hand-rolled readdirSync + a separate
|
|
2554
2611
|
// getMilestonePhaseFilter window check (which also never excluded
|
|
2555
2612
|
// sentinels, unlike the owner).
|
|
2556
2613
|
const _checkboxStates = new Map();
|
|
2557
|
-
// #4982:
|
|
2558
|
-
// after the checkbox (tolerating only an optional `**`
|
|
2559
|
-
//
|
|
2560
|
-
//
|
|
2561
|
-
// own phase. First-match-wins (`if (!_checkboxStates.has(...))` below)
|
|
2614
|
+
// #4982: parsePhaseChecklistLine is line-anchored, so the phase label must
|
|
2615
|
+
// be the FIRST thing after the checkbox (tolerating only an optional `**`
|
|
2616
|
+
// bold marker), and a later "Phase N" in the line's prose cannot bind the
|
|
2617
|
+
// checkbox. First-match-wins (`if (!_checkboxStates.has(...))` below)
|
|
2562
2618
|
// additionally guards against a later anchored line (e.g. a per-plan
|
|
2563
2619
|
// sub-entry self-titled "Phase N ...") overwriting an already-recorded
|
|
2564
|
-
// phase's own (first) checkbox
|
|
2565
|
-
//
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
const _cbKey = _cbMatch[phaseGroup];
|
|
2571
|
-
if (!_checkboxStates.has(_cbKey)) {
|
|
2572
|
-
_checkboxStates.set(_cbKey, _cbMatch[1].toLowerCase() === 'x');
|
|
2620
|
+
// phase's own (first) checkbox; anchoring alone does not fully close that
|
|
2621
|
+
// "last-match-wins" composition defect.
|
|
2622
|
+
for (const line of content.split(/\r?\n/)) {
|
|
2623
|
+
const checkbox = parsePhaseChecklistLine(line, phaseIdConvention, directoryContext);
|
|
2624
|
+
if (checkbox && !_checkboxStates.has(checkbox.phaseToken)) {
|
|
2625
|
+
_checkboxStates.set(checkbox.phaseToken, checkbox.checked);
|
|
2573
2626
|
}
|
|
2574
2627
|
}
|
|
2575
2628
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag (literal mirror of OPTIONAL_PHASE_TAG_SOURCE).
|
|
2576
|
-
const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefix}(${
|
|
2629
|
+
const phasePattern = new RegExp(`#{2,4}\\s*${phaseHeadingPrefix}(${phaseScanTokenSource(directoryContext)})(?:\\s*\\([^)\\n]{0,200}\\))?\\s*:\\s*([^\\n]+)`, 'gi');
|
|
2577
2630
|
const phases = [];
|
|
2631
|
+
const bracketIdsByPhaseNumber = new Map();
|
|
2578
2632
|
let match;
|
|
2579
2633
|
while ((match = phasePattern.exec(content)) !== null) {
|
|
2580
2634
|
const bracketId = capturesBracketId ? match[1] : undefined;
|
|
2581
2635
|
const phaseNum = capturesBracketId ? match[2] : match[1];
|
|
2636
|
+
if (bracketId)
|
|
2637
|
+
bracketIdsByPhaseNumber.set(phaseNum, bracketId);
|
|
2582
2638
|
const phaseName = (capturesBracketId ? match[3] : match[2])
|
|
2583
2639
|
.replace(/\(INSERTED\)/i, '')
|
|
2584
2640
|
.trim();
|
|
@@ -2594,7 +2650,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2594
2650
|
}
|
|
2595
2651
|
const sectionStart = match.index;
|
|
2596
2652
|
const restOfContent = content.slice(sectionStart);
|
|
2597
|
-
const nextHeader = restOfContent.match(new RegExp(`\\n#{2,4}\\s+${phaseHeadingPrefixNoCapture}
|
|
2653
|
+
const nextHeader = restOfContent.match(new RegExp(`\\n#{2,4}\\s+${phaseHeadingPrefixNoCapture}${phaseScanTokenSource(directoryContext)}`, 'i'));
|
|
2598
2654
|
const sectionEnd = nextHeader
|
|
2599
2655
|
? sectionStart + nextHeader.index
|
|
2600
2656
|
: content.length;
|
|
@@ -2624,7 +2680,7 @@ function cmdInitManager(cwd, raw) {
|
|
|
2624
2680
|
// of reporting no_directory/phase_complete:false. Five other init
|
|
2625
2681
|
// commands already route through this same primitive; this was the
|
|
2626
2682
|
// holdout private copy (#4793-family declared-owner drift).
|
|
2627
|
-
const located = findPhaseInternal(cwd, phaseNum, phaseIdConvention);
|
|
2683
|
+
const located = findPhaseInternal(cwd, phaseNum, phaseIdConvention, directoryContext);
|
|
2628
2684
|
const dirMatch = located && located['found']
|
|
2629
2685
|
? node_path_1.default.posix.basename(String(located['directory']))
|
|
2630
2686
|
: null;
|
|
@@ -2735,15 +2791,83 @@ function cmdInitManager(cwd, raw) {
|
|
|
2735
2791
|
.filter((p) => p['phase_complete'] === true)
|
|
2736
2792
|
.map((p) => normalizePhaseNumber(p['number'])));
|
|
2737
2793
|
const phaseMap = new Map(phases.map((p) => [normalizePhaseNumber(p['number']), p]));
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2794
|
+
const bracketIdentityKey = (value) => {
|
|
2795
|
+
try {
|
|
2796
|
+
const id = parsePhaseId(value);
|
|
2797
|
+
const subphase = id.subphase ? `.${id.subphase}` : '';
|
|
2798
|
+
return bracketQualifiedKey(`${id.project}.${id.milestone}-${id.phase}${subphase}`, 'bracket');
|
|
2799
|
+
}
|
|
2800
|
+
catch {
|
|
2801
|
+
return null;
|
|
2802
|
+
}
|
|
2803
|
+
};
|
|
2804
|
+
const phaseByBracketIdentity = new Map();
|
|
2805
|
+
const bracketIdentityByPhaseNumber = new Map();
|
|
2806
|
+
const completedBracketIdentities = new Set();
|
|
2807
|
+
if (phaseIdConvention === 'bracket') {
|
|
2808
|
+
for (const phase of phases) {
|
|
2809
|
+
const phaseNumber = phase['number'];
|
|
2810
|
+
const bracketId = bracketIdsByPhaseNumber.get(phaseNumber);
|
|
2811
|
+
const canonicalPhaseToken = phaseToken(phaseNumber);
|
|
2812
|
+
// #4304: key each heading on the reader's canonical
|
|
2813
|
+
// token, not on a strict parse of its rendered display. An accepted
|
|
2814
|
+
// unpadded heading (`### [CK.02] 1:`) gets no display_id, because
|
|
2815
|
+
// parsePhaseId rejects `CK.02-1`, so it had no identity at all and a
|
|
2816
|
+
// checked `[CK.02] 01` row completed it while phase_complete was false.
|
|
2817
|
+
// phaseToken pads the number to `01`, and `CK.02-01` is the form
|
|
2818
|
+
// parsePhaseId accepts; canonical headings key exactly as before.
|
|
2819
|
+
const key = bracketId && canonicalPhaseToken
|
|
2820
|
+
? bracketIdentityKey(`${bracketId}-${canonicalPhaseToken}`)
|
|
2821
|
+
: null;
|
|
2822
|
+
if (!key)
|
|
2823
|
+
continue;
|
|
2824
|
+
bracketIdentityByPhaseNumber.set(normalizePhaseNumber(phaseNumber), key);
|
|
2825
|
+
phaseByBracketIdentity.set(key, phase);
|
|
2826
|
+
if (phase['phase_complete'] === true)
|
|
2827
|
+
completedBracketIdentities.add(key);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
if (phaseIdConvention === 'bracket') {
|
|
2831
|
+
// A checked historical row satisfies only its own qualified identity. In
|
|
2832
|
+
// particular, `[CK.01] 01` must not complete bare/current `[CK.02] 01`.
|
|
2833
|
+
for (const line of rawContent.split(/\r?\n/)) {
|
|
2834
|
+
const checkbox = parsePhaseChecklistLine(line, phaseIdConvention);
|
|
2835
|
+
if (!checkbox?.checked || !checkbox.bracketId)
|
|
2836
|
+
continue;
|
|
2837
|
+
const identity = `[${checkbox.bracketId}] ${checkbox.phaseToken}`;
|
|
2838
|
+
const key = bracketIdentityKey(identity);
|
|
2839
|
+
if (!key)
|
|
2840
|
+
continue;
|
|
2841
|
+
const current = phaseMap.get(normalizePhaseNumber(checkbox.phaseToken));
|
|
2842
|
+
const currentKey = current
|
|
2843
|
+
? bracketIdentityByPhaseNumber.get(normalizePhaseNumber(current['number'])) ?? null
|
|
2844
|
+
: null;
|
|
2845
|
+
if (!current || currentKey !== key || current['phase_complete'] === true) {
|
|
2846
|
+
completedBracketIdentities.add(key);
|
|
2847
|
+
}
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
else {
|
|
2851
|
+
// Preserve the historical greedy non-bracket fallback byte-for-byte.
|
|
2852
|
+
const _allCompletedPattern = (0, phase_entry_grammar_cjs_1.phaseEntryRegExp)(`.*${phaseHeadingPrefixNoCapture}(${PHASE_NUMBER_TOKEN_SOURCE})[:\\s]`, 'gi');
|
|
2853
|
+
let _allMatch;
|
|
2854
|
+
while ((_allMatch = _allCompletedPattern.exec(rawContent)) !== null) {
|
|
2855
|
+
if (!(0, phase_entry_grammar_cjs_1.isPhaseEntryChecked)(_allMatch[0]))
|
|
2856
|
+
continue;
|
|
2857
|
+
const phaseNum = normalizePhaseNumber(_allMatch[1]);
|
|
2858
|
+
const phase = phaseMap.get(phaseNum);
|
|
2859
|
+
if (!phase || phase['phase_complete'] === true) {
|
|
2860
|
+
completedNums.add(phaseNum);
|
|
2861
|
+
}
|
|
2745
2862
|
}
|
|
2746
2863
|
}
|
|
2864
|
+
const currentDependencyNumber = (dependency) => {
|
|
2865
|
+
const identity = bracketIdentityKey(dependency);
|
|
2866
|
+
if (!identity)
|
|
2867
|
+
return normalizePhaseNumber(dependency);
|
|
2868
|
+
const current = phaseByBracketIdentity.get(identity);
|
|
2869
|
+
return current ? normalizePhaseNumber(current['number']) : null;
|
|
2870
|
+
};
|
|
2747
2871
|
function reaches(from, to, visited = new Set()) {
|
|
2748
2872
|
const normalizedFrom = normalizePhaseNumber(from);
|
|
2749
2873
|
const normalizedTo = normalizePhaseNumber(to);
|
|
@@ -2753,15 +2877,18 @@ function cmdInitManager(cwd, raw) {
|
|
|
2753
2877
|
const p = phaseMap.get(normalizedFrom);
|
|
2754
2878
|
if (!p || !p['dep_phases'] || p['dep_phases'].length === 0)
|
|
2755
2879
|
return false;
|
|
2756
|
-
if (p['dep_phases'].some((dep) =>
|
|
2880
|
+
if (p['dep_phases'].some((dep) => currentDependencyNumber(dep) === normalizedTo)) {
|
|
2757
2881
|
return true;
|
|
2758
2882
|
}
|
|
2759
|
-
return p['dep_phases'].some((dep) =>
|
|
2883
|
+
return p['dep_phases'].some((dep) => {
|
|
2884
|
+
const dependencyNumber = currentDependencyNumber(dep);
|
|
2885
|
+
return dependencyNumber === null ? false : reaches(dependencyNumber, to, visited);
|
|
2886
|
+
});
|
|
2760
2887
|
}
|
|
2761
2888
|
function hasDepRelationship(numA, numB) {
|
|
2762
2889
|
return reaches(numA, numB) || reaches(numB, numA);
|
|
2763
2890
|
}
|
|
2764
|
-
// #4764: a phase reference in depends_on prose is a PHASE-SHAPED token in
|
|
2891
|
+
// #4764/#4304: a phase reference in depends_on prose is a PHASE-SHAPED token in
|
|
2765
2892
|
// context — directly following "Phase"/"Phases" — never a bare digit run.
|
|
2766
2893
|
// The previous whole-field scrape matched the token grammar against every
|
|
2767
2894
|
// digit run, so calendar dates ("2026-09-14" → 2026, 09, 14), git shas
|
|
@@ -2774,9 +2901,27 @@ function cmdInitManager(cwd, raw) {
|
|
|
2774
2901
|
// "Phase 1-3") — silently dropping a REAL dependency would clear
|
|
2775
2902
|
// deps_satisfied prematurely, the dangerous direction. Negation prose
|
|
2776
2903
|
// ("dropped the dependency on Phase 654") is NOT detected: the issue's own
|
|
2777
|
-
// minimum keeps such tokens.
|
|
2778
|
-
|
|
2779
|
-
|
|
2904
|
+
// minimum keeps such tokens. Bracket repositories route through the same
|
|
2905
|
+
// phase-id owner so `[CK.02] 01` retains its complete qualified identity;
|
|
2906
|
+
// bare `01` still resolves against the active milestone. This prevents a
|
|
2907
|
+
// checked historical `[CK.01] 01` from satisfying incomplete `[CK.02] 01`.
|
|
2908
|
+
// Non-bracket extraction remains this manager surface's exact #4764 code
|
|
2909
|
+
// path below, including its case-insensitive token regex. Planning-inspect's
|
|
2910
|
+
// old token regex was case-sensitive, so forcing both through one widened
|
|
2911
|
+
// helper changed its `Phase 1a` bytes from `1` to `1a`.
|
|
2912
|
+
const legacyManagerDependencyTokens = (prose) => {
|
|
2913
|
+
const depPhaseRefRe = new RegExp(`${PHASE_DEP_REF_SOURCE}`, 'gi');
|
|
2914
|
+
const depTokenRe = new RegExp(`${PHASE_NUMBER_TOKEN_SOURCE}`, 'gi');
|
|
2915
|
+
const tokens = [];
|
|
2916
|
+
let refMatch;
|
|
2917
|
+
while ((refMatch = depPhaseRefRe.exec(prose)) !== null) {
|
|
2918
|
+
let tokenMatch;
|
|
2919
|
+
depTokenRe.lastIndex = 0;
|
|
2920
|
+
while ((tokenMatch = depTokenRe.exec(refMatch[1])) !== null)
|
|
2921
|
+
tokens.push(tokenMatch[0]);
|
|
2922
|
+
}
|
|
2923
|
+
return tokens;
|
|
2924
|
+
};
|
|
2780
2925
|
for (const phase of phases) {
|
|
2781
2926
|
if (!phase['depends_on'] ||
|
|
2782
2927
|
/^none$/i.test(phase['depends_on'].trim())) {
|
|
@@ -2785,24 +2930,39 @@ function cmdInitManager(cwd, raw) {
|
|
|
2785
2930
|
else {
|
|
2786
2931
|
const prose = phase['depends_on'];
|
|
2787
2932
|
const ownNumber = normalizePhaseNumber(phase['number']);
|
|
2933
|
+
const ownIdentity = typeof phase['display_id'] === 'string'
|
|
2934
|
+
? bracketIdentityKey(phase['display_id'])
|
|
2935
|
+
: null;
|
|
2788
2936
|
const depNums = [];
|
|
2789
2937
|
const seen = new Set();
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
if (normalized === ownNumber)
|
|
2798
|
-
continue; // #4764: never the row's own phase
|
|
2799
|
-
if (seen.has(normalized))
|
|
2938
|
+
const dependencyTokens = phaseIdConvention === 'bracket'
|
|
2939
|
+
? extractPhaseDependencyTokens(prose, phaseIdConvention)
|
|
2940
|
+
: legacyManagerDependencyTokens(prose);
|
|
2941
|
+
for (const token of dependencyTokens) {
|
|
2942
|
+
const identity = bracketIdentityKey(token);
|
|
2943
|
+
if (identity) {
|
|
2944
|
+
if (identity === ownIdentity)
|
|
2800
2945
|
continue;
|
|
2801
|
-
seen.
|
|
2802
|
-
|
|
2946
|
+
if (seen.has(identity))
|
|
2947
|
+
continue;
|
|
2948
|
+
seen.add(identity);
|
|
2949
|
+
depNums.push(token);
|
|
2950
|
+
continue;
|
|
2803
2951
|
}
|
|
2952
|
+
const normalized = normalizePhaseNumber(token);
|
|
2953
|
+
if (normalized === ownNumber)
|
|
2954
|
+
continue; // #4764: never the row's own phase
|
|
2955
|
+
if (seen.has(normalized))
|
|
2956
|
+
continue;
|
|
2957
|
+
seen.add(normalized);
|
|
2958
|
+
depNums.push(token);
|
|
2804
2959
|
}
|
|
2805
|
-
phase['deps_satisfied'] = depNums.every((
|
|
2960
|
+
phase['deps_satisfied'] = depNums.every((dependency) => {
|
|
2961
|
+
const identity = bracketIdentityKey(dependency);
|
|
2962
|
+
return identity
|
|
2963
|
+
? completedBracketIdentities.has(identity)
|
|
2964
|
+
: completedNums.has(normalizePhaseNumber(dependency));
|
|
2965
|
+
});
|
|
2806
2966
|
phase['dep_phases'] = depNums;
|
|
2807
2967
|
}
|
|
2808
2968
|
}
|
|
@@ -3016,9 +3176,23 @@ function cmdInitCompleteMilestone(cwd, raw, options = {}) {
|
|
|
3016
3176
|
milestones_path: node_fs_1.default.existsSync(milestonesPath) ? toPosixPath(milestonesPath) : null,
|
|
3017
3177
|
project_path: node_fs_1.default.existsSync(projectPath) ? toPosixPath(projectPath) : null,
|
|
3018
3178
|
requirements_path: node_fs_1.default.existsSync(requirementsPath) ? toPosixPath(requirementsPath) : null,
|
|
3179
|
+
// #5706 (epic #3628): the repositories configured for this project and the
|
|
3180
|
+
// milestone tagging mode, so the workflow reads both from one init call.
|
|
3181
|
+
sub_repos: detectSubRepos(cwd),
|
|
3182
|
+
repo_tagging: detectRepoTagging(cwd),
|
|
3019
3183
|
};
|
|
3184
|
+
// #5195: `state:flat-mode` gates the PROJECT.md evolution review. PROJECT.md
|
|
3185
|
+
// is shared across workstreams (the `projectPath` comment above), so a
|
|
3186
|
+
// workstream's milestone close must not rewrite or stage it — the same
|
|
3187
|
+
// decision `cmdInitNewMilestone` applies to its Part A (#2308), resolved from
|
|
3188
|
+
// the same authority (`GSD_WORKSTREAM` env, else the stored pointer) so the
|
|
3189
|
+
// gate cannot disagree with the workstream-scoped paths returned above.
|
|
3190
|
+
// Read-only peek: no self-heal side effect from building a report field (#3579).
|
|
3191
|
+
const workstreamActive = !!(resolveEnvWorkstream() ?? peekActiveWorkstream(cwd));
|
|
3020
3192
|
result['section_manifest'] = buildSectionManifestField(cwd, null, options, 'complete-milestone', {
|
|
3021
3193
|
gitCreateTag,
|
|
3194
|
+
workstreamActive,
|
|
3195
|
+
flatMode: !workstreamActive,
|
|
3022
3196
|
});
|
|
3023
3197
|
output(withProjectRoot(cwd, result), raw);
|
|
3024
3198
|
}
|
|
@@ -3252,6 +3426,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3252
3426
|
`Available workstreams: ${_availableWorkstreams.join(', ')}`, ERROR_REASON.WORKSTREAM_MODE_NONE_ACTIVE);
|
|
3253
3427
|
}
|
|
3254
3428
|
const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
|
|
3429
|
+
const directoryContext = phaseDirectoryOptions(cwd, config);
|
|
3255
3430
|
const phases = [];
|
|
3256
3431
|
let currentPhase = null;
|
|
3257
3432
|
let nextPhase = null;
|
|
@@ -3264,7 +3439,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3264
3439
|
// #1729: `(?:\s*\([^)\n]{0,200}\))?` tolerates a pre-colon ( ) tag, owned by
|
|
3265
3440
|
// buildPhaseHeadingScanRegex (phase-id.cts) so this scan also recognizes
|
|
3266
3441
|
// bracket-convention headings instead of hand-rolling a literal `Phase\s+`.
|
|
3267
|
-
const { regex: headingPattern, phaseNumGroup: hNumGroup, phaseNameGroup: hNameGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, progressConvention);
|
|
3442
|
+
const { regex: headingPattern, phaseNumGroup: hNumGroup, phaseNameGroup: hNameGroup } = buildPhaseHeadingScanRegex(PHASE_HEADING_BASELINE.ANY_BRACKET, progressConvention, directoryContext);
|
|
3268
3443
|
let hm;
|
|
3269
3444
|
while ((hm = headingPattern.exec(roadmapContent)) !== null) {
|
|
3270
3445
|
roadmapPhaseNums.add(hm[hNumGroup]);
|
|
@@ -3297,12 +3472,12 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3297
3472
|
// matched (by absorbing the bracket text). This does NOT migrate the site
|
|
3298
3473
|
// onto phaseHeadingPrefixSrcFor / the bracket convention — that stays
|
|
3299
3474
|
// out of scope per the comment above (#4984 revert, pinned census).
|
|
3300
|
-
const cbPattern =
|
|
3475
|
+
const cbPattern = (0, phase_entry_grammar_cjs_1.phaseEntryRegExp)(`(?:\\[[^\\]\\n]{0,200}\\]\\s*)?Phase\\s+(${phaseScanTokenSource(directoryContext)})[:\\s]`, 'gim', { state: 'captured' });
|
|
3301
3476
|
let cbm;
|
|
3302
3477
|
while ((cbm = cbPattern.exec(roadmapContent)) !== null) {
|
|
3303
3478
|
const cbKey = cbm[2];
|
|
3304
3479
|
if (!roadmapCheckboxStates.has(cbKey)) {
|
|
3305
|
-
roadmapCheckboxStates.set(cbKey, cbm[1]
|
|
3480
|
+
roadmapCheckboxStates.set(cbKey, (0, phase_entry_grammar_cjs_1.isBoxCharChecked)(cbm[1]));
|
|
3306
3481
|
}
|
|
3307
3482
|
}
|
|
3308
3483
|
}
|
|
@@ -3319,10 +3494,12 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3319
3494
|
// behavior-preserving).
|
|
3320
3495
|
const dirs = listMilestonePhaseDirs(phasesDir, { cwd }).value;
|
|
3321
3496
|
for (const dir of dirs) {
|
|
3322
|
-
const dirMatch = dir.match(new RegExp(`^(${
|
|
3323
|
-
const
|
|
3324
|
-
const
|
|
3325
|
-
|
|
3497
|
+
const dirMatch = dir.match(new RegExp(`^(${phaseScanTokenSource(directoryContext)})-?(.*)`, 'i'));
|
|
3498
|
+
const query = customPhaseQuery(dir, directoryContext);
|
|
3499
|
+
const owner = query && customDirectoryOwner(dir, query);
|
|
3500
|
+
const phaseNumber = owner ? owner.id : dirMatch ? dirMatch[1] : dir;
|
|
3501
|
+
const phaseName = owner ? owner.slug : dirMatch && dirMatch[2] ? dirMatch[2] : null;
|
|
3502
|
+
seenPhaseNums.add((phaseKeyFromToken(phaseNumber, directoryContext) ?? phaseNumber).toLowerCase());
|
|
3326
3503
|
const phasePath = node_path_1.default.join(phasesDir, dir);
|
|
3327
3504
|
const phaseFiles = node_fs_1.default.readdirSync(phasePath);
|
|
3328
3505
|
// #5060: Phase Status Module — the single owner of "what state is
|
|
@@ -3367,7 +3544,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3367
3544
|
/* otherwise intentionally empty */
|
|
3368
3545
|
}
|
|
3369
3546
|
for (const [num, name] of roadmapPhaseNames) {
|
|
3370
|
-
const stripped = num
|
|
3547
|
+
const stripped = (phaseKeyFromToken(num, directoryContext) ?? num).toLowerCase();
|
|
3371
3548
|
if (!seenPhaseNums.has(stripped)) {
|
|
3372
3549
|
const checkboxComplete = roadmapCheckboxStates.get(num) === true ||
|
|
3373
3550
|
roadmapCheckboxStates.get(stripped) === true;
|
|
@@ -3397,7 +3574,7 @@ function cmdInitProgress(cwd, raw, options = {}) {
|
|
|
3397
3574
|
}
|
|
3398
3575
|
}
|
|
3399
3576
|
}
|
|
3400
|
-
phases.sort((a, b) => comparePhaseNum(a['number'], b['number']));
|
|
3577
|
+
phases.sort((a, b) => comparePhaseNum(a['number'], b['number'], directoryContext));
|
|
3401
3578
|
// #3581: the frontier is ROADMAP ORDER, not artifact presence. The disk loop
|
|
3402
3579
|
// above could claim nextPhase from a stray out-of-order artifact directory
|
|
3403
3580
|
// (a phase-9 UAT evidence dir while roadmap phase 8 was pending and
|