@opengsd/gsd-core 1.18.0 → 1.19.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.
Files changed (257) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.opencode/plugins/gsd-core.js +2 -3
  4. package/agents/gsd-code-fixer.compact.md +2 -2
  5. package/agents/gsd-code-fixer.md +4 -3
  6. package/agents/gsd-verifier.md +1 -1
  7. package/bin/install.js +422 -103
  8. package/gsd-core/bin/gsd-tools.cjs +106 -7
  9. package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
  10. package/gsd-core/bin/lib/args-terminator.cjs +114 -0
  11. package/gsd-core/bin/lib/broken-windows.cjs +59 -1
  12. package/gsd-core/bin/lib/capability-lock.cjs +13 -2
  13. package/gsd-core/bin/lib/capability-registry.cjs +156 -74
  14. package/gsd-core/bin/lib/capability-state.cjs +14 -8
  15. package/gsd-core/bin/lib/capability-validator.cjs +17 -0
  16. package/gsd-core/bin/lib/config-loader.cjs +10 -0
  17. package/gsd-core/bin/lib/config.cjs +256 -58
  18. package/gsd-core/bin/lib/core-utils.cjs +34 -0
  19. package/gsd-core/bin/lib/decision-coverage-support.cjs +74 -0
  20. package/gsd-core/bin/lib/gate-context-drift.cjs +8 -3
  21. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +6 -1
  22. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +6 -1
  23. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
  24. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +63 -22
  25. package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
  26. package/gsd-core/bin/lib/git-info-exclude.cjs +57 -0
  27. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +18 -3
  28. package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
  29. package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
  30. package/gsd-core/bin/lib/host-integration.cjs +13 -6
  31. package/gsd-core/bin/lib/init.cjs +65 -1
  32. package/gsd-core/bin/lib/install-engine.cjs +246 -32
  33. package/gsd-core/bin/lib/install-profiles.cjs +9 -4
  34. package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
  35. package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
  36. package/gsd-core/bin/lib/markdown-sectionizer.cjs +113 -0
  37. package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
  38. package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
  39. package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
  40. package/gsd-core/bin/lib/milestone.cjs +43 -1
  41. package/gsd-core/bin/lib/model-catalog.cjs +8 -0
  42. package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
  43. package/gsd-core/bin/lib/phase-id.cjs +21 -4
  44. package/gsd-core/bin/lib/phase-lifecycle.cjs +37 -4
  45. package/gsd-core/bin/lib/phase.cjs +115 -26
  46. package/gsd-core/bin/lib/planning-snapshot.cjs +3 -1
  47. package/gsd-core/bin/lib/planning-workspace.cjs +18 -0
  48. package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
  49. package/gsd-core/bin/lib/read-only-git-env.cjs +58 -0
  50. package/gsd-core/bin/lib/review-lane-descriptor.cjs +35 -2
  51. package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
  52. package/gsd-core/bin/lib/roadmap-parser.cjs +12 -0
  53. package/gsd-core/bin/lib/roadmap-upgrade.cjs +39 -2
  54. package/gsd-core/bin/lib/roadmap.cjs +33 -25
  55. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +381 -14
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
  58. package/gsd-core/bin/lib/shell-command-projection.cjs +121 -16
  59. package/gsd-core/bin/lib/smart-entry.cjs +13 -0
  60. package/gsd-core/bin/lib/state-contract.cjs +29 -9
  61. package/gsd-core/bin/lib/state-document.cjs +12 -4
  62. package/gsd-core/bin/lib/state-transition.cjs +119 -24
  63. package/gsd-core/bin/lib/state.cjs +50 -28
  64. package/gsd-core/bin/lib/surface.cjs +9 -0
  65. package/gsd-core/bin/lib/task-command-router.cjs +5 -25
  66. package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
  67. package/gsd-core/bin/lib/verification.cjs +96 -9
  68. package/gsd-core/bin/lib/verify.cjs +2 -0
  69. package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
  70. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  71. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  72. package/gsd-core/references/git-integration.md +11 -0
  73. package/gsd-core/references/loop-hook-dispatch.md +13 -6
  74. package/gsd-core/references/planning-config.md +2 -0
  75. package/gsd-core/references/tdd.md +2 -2
  76. package/gsd-core/templates/DEBUG.md +16 -0
  77. package/gsd-core/templates/config.json +2 -1
  78. package/gsd-core/templates/verification-report.md +1 -1
  79. package/gsd-core/workflows/audit-fix.md +2 -1
  80. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
  81. package/gsd-core/workflows/code-review.md +61 -7
  82. package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
  83. package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
  84. package/gsd-core/workflows/complete-milestone.md +37 -150
  85. package/gsd-core/workflows/diagnose-issues.md +29 -5
  86. package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
  87. package/gsd-core/workflows/discuss-phase-assumptions.md +33 -2
  88. package/gsd-core/workflows/discuss-phase-power.md +19 -2
  89. package/gsd-core/workflows/discuss-phase.md +6 -10
  90. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
  91. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +7 -2
  92. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -1
  93. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +2 -2
  94. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
  95. package/gsd-core/workflows/execute-phase.md +9 -5
  96. package/gsd-core/workflows/health.md +2 -1
  97. package/gsd-core/workflows/import.md +1 -1
  98. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +2 -2
  99. package/gsd-core/workflows/plan-phase.md +13 -13
  100. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
  101. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
  102. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
  103. package/gsd-core/workflows/quick.md +1 -1
  104. package/gsd-core/workflows/review.md +16 -13
  105. package/gsd-core/workflows/section-manifest.json +5 -0
  106. package/gsd-core/workflows/settings.md +1 -0
  107. package/gsd-core/workflows/ship.md +3 -4
  108. package/gsd-core/workflows/spec-phase.md +3 -6
  109. package/gsd-core/workflows/verify-work.md +4 -3
  110. package/hooks/dist/gsd-agent-isolation-guard.js +11 -1
  111. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  112. package/hooks/dist/gsd-path-export.js +95 -0
  113. package/hooks/dist/gsd-secret-read-guard.js +172 -39
  114. package/hooks/dist/gsd-statusline.js +35 -2
  115. package/hooks/dist/gsd-windsurf-pre-write.js +28 -1
  116. package/hooks/dist/gsd-workflow-guard.js +1 -1
  117. package/hooks/dist/gsd-worktree-path-guard.js +28 -1
  118. package/hooks/dist/lib/isolation-sentinel.js +44 -0
  119. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  120. package/hooks/gsd-agent-isolation-guard.js +11 -1
  121. package/hooks/gsd-cursor-subagent-start.js +6 -2
  122. package/hooks/gsd-path-export.js +95 -0
  123. package/hooks/gsd-secret-read-guard.js +172 -39
  124. package/hooks/gsd-statusline.js +35 -2
  125. package/hooks/gsd-windsurf-pre-write.js +28 -1
  126. package/hooks/gsd-workflow-guard.js +1 -1
  127. package/hooks/gsd-worktree-path-guard.js +28 -1
  128. package/hooks/hooks.json +1 -0
  129. package/hooks/lib/isolation-sentinel.js +44 -0
  130. package/hooks/managed-hooks-registry.cjs +1 -0
  131. package/package.json +13 -6
  132. package/pi/gsd.cjs +1047 -10
  133. package/pi/package-agents/gsd-advisor-researcher.md +113 -0
  134. package/pi/package-agents/gsd-ai-researcher.md +111 -0
  135. package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
  136. package/pi/package-agents/gsd-code-fixer.md +769 -0
  137. package/pi/package-agents/gsd-code-reviewer.md +402 -0
  138. package/pi/package-agents/gsd-codebase-mapper.md +850 -0
  139. package/pi/package-agents/gsd-debug-session-manager.md +416 -0
  140. package/pi/package-agents/gsd-debugger.md +1274 -0
  141. package/pi/package-agents/gsd-doc-classifier.md +269 -0
  142. package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
  143. package/pi/package-agents/gsd-doc-verifier.md +212 -0
  144. package/pi/package-agents/gsd-doc-writer.md +626 -0
  145. package/pi/package-agents/gsd-dom-verifier.md +164 -0
  146. package/pi/package-agents/gsd-domain-researcher.md +144 -0
  147. package/pi/package-agents/gsd-eval-auditor.md +185 -0
  148. package/pi/package-agents/gsd-eval-planner.md +149 -0
  149. package/pi/package-agents/gsd-executor.md +894 -0
  150. package/pi/package-agents/gsd-framework-selector.md +159 -0
  151. package/pi/package-agents/gsd-integration-checker.md +476 -0
  152. package/pi/package-agents/gsd-intel-updater.md +338 -0
  153. package/pi/package-agents/gsd-mempalace-curator.md +49 -0
  154. package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
  155. package/pi/package-agents/gsd-pattern-mapper.md +340 -0
  156. package/pi/package-agents/gsd-phase-researcher.md +897 -0
  157. package/pi/package-agents/gsd-plan-checker.md +1077 -0
  158. package/pi/package-agents/gsd-planner.md +1003 -0
  159. package/pi/package-agents/gsd-project-researcher.md +611 -0
  160. package/pi/package-agents/gsd-research-synthesizer.md +259 -0
  161. package/pi/package-agents/gsd-roadmapper.md +779 -0
  162. package/pi/package-agents/gsd-security-auditor.md +171 -0
  163. package/pi/package-agents/gsd-ui-auditor.md +581 -0
  164. package/pi/package-agents/gsd-ui-checker.md +417 -0
  165. package/pi/package-agents/gsd-ui-researcher.md +469 -0
  166. package/pi/package-agents/gsd-user-profiler.md +174 -0
  167. package/pi/package-agents/gsd-verifier.md +963 -0
  168. package/pi/package-agents/manifest.json +39 -0
  169. package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
  170. package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
  171. package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
  172. package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
  173. package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
  174. package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
  175. package/pi/package-skills/gsd-capture/SKILL.md +68 -0
  176. package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
  177. package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
  178. package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
  179. package/pi/package-skills/gsd-config/SKILL.md +60 -0
  180. package/pi/package-skills/gsd-debug/SKILL.md +64 -0
  181. package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
  182. package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
  183. package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
  184. package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
  185. package/pi/package-skills/gsd-explore/SKILL.md +26 -0
  186. package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
  187. package/pi/package-skills/gsd-fast/SKILL.md +33 -0
  188. package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
  189. package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
  190. package/pi/package-skills/gsd-health/SKILL.md +36 -0
  191. package/pi/package-skills/gsd-help/SKILL.md +37 -0
  192. package/pi/package-skills/gsd-import/SKILL.md +46 -0
  193. package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
  194. package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
  195. package/pi/package-skills/gsd-manager/SKILL.md +46 -0
  196. package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
  197. package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
  198. package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
  199. package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
  200. package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
  201. package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
  202. package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
  203. package/pi/package-skills/gsd-next/SKILL.md +34 -0
  204. package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
  205. package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
  206. package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
  207. package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
  208. package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
  209. package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
  210. package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
  211. package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
  212. package/pi/package-skills/gsd-phase/SKILL.md +61 -0
  213. package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
  214. package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
  215. package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
  216. package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
  217. package/pi/package-skills/gsd-progress/SKILL.md +51 -0
  218. package/pi/package-skills/gsd-quick/SKILL.md +179 -0
  219. package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
  220. package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
  221. package/pi/package-skills/gsd-review/SKILL.md +50 -0
  222. package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
  223. package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
  224. package/pi/package-skills/gsd-settings/SKILL.md +30 -0
  225. package/pi/package-skills/gsd-ship/SKILL.md +27 -0
  226. package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
  227. package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
  228. package/pi/package-skills/gsd-spike/SKILL.md +53 -0
  229. package/pi/package-skills/gsd-stats/SKILL.md +22 -0
  230. package/pi/package-skills/gsd-surface/SKILL.md +177 -0
  231. package/pi/package-skills/gsd-thread/SKILL.md +29 -0
  232. package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
  233. package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
  234. package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
  235. package/pi/package-skills/gsd-undo/SKILL.md +38 -0
  236. package/pi/package-skills/gsd-update/SKILL.md +51 -0
  237. package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
  238. package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
  239. package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
  240. package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
  241. package/scripts/build-hooks.js +4 -0
  242. package/scripts/command-contract-helpers.cjs +1 -1
  243. package/scripts/docs-guard-registry.cjs +20 -7
  244. package/scripts/gen-health-docs.cjs +13 -1
  245. package/scripts/gen-pi-package-skills.cjs +217 -0
  246. package/scripts/lib/macos-conformance-tier.generated.cjs +11 -1
  247. package/scripts/lib/platform-conformance-tier.generated.cjs +26 -1
  248. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
  249. package/scripts/lint-retired-runtime-name.cjs +5 -0
  250. package/scripts/npm-audit-baseline.cjs +41 -7
  251. package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
  252. package/scripts/select-docs-guards.cjs +1 -1
  253. package/scripts/self-test-report-policy.cjs +350 -0
  254. package/vscode/package.json +1 -1
  255. package/scripts/diagnose-validate-commit-in-tier.cjs +0 -270
  256. package/scripts/diagnose-validate-commit-phases.cjs +0 -348
  257. package/scripts/xtrace-phase-timing.cjs +0 -205
@@ -78,6 +78,17 @@ function isClosedMilestoneHeading(headingText) {
78
78
  function stripShippedMilestones(content) {
79
79
  return (0, markdown_sectionizer_cjs_1.stripTaggedBlocks)(content, 'details');
80
80
  }
81
+ /**
82
+ * #5543: run `edit` over the ROADMAP with every shipped `<details>` block hidden
83
+ * and restored byte-for-byte afterwards. For writers whose active milestone
84
+ * could not be resolved: the first `### Phase N:` in document order must never
85
+ * be the one inside a shipped block. Layout-independent (the active phase may
86
+ * sit before or after a shipped block) and, unlike a "region after the last
87
+ * `</details>`" cut, not fooled by a `</details>` in a fence or inline code.
88
+ */
89
+ function editOutsideShippedBlocks(content, edit) {
90
+ return (0, markdown_sectionizer_cjs_1.editOutsideTaggedBlocks)(content, 'details', edit);
91
+ }
81
92
  /**
82
93
  * #3982: strip only <details> blocks whose <summary> marks a CLOSED milestone
83
94
  * (ARCHIVED/SHIPPED/✅/… without an active marker) — the narrow form of
@@ -2287,6 +2298,7 @@ module.exports = {
2287
2298
  getMilestonePhaseFilter,
2288
2299
  currentMilestoneRawRanges,
2289
2300
  withPhaseSection,
2301
+ editOutsideShippedBlocks,
2290
2302
  computeMilestoneSectionEnd,
2291
2303
  locateMilestoneHeadings,
2292
2304
  listMilestoneHeadings,
@@ -12,7 +12,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  };
13
13
  const node_fs_1 = __importDefault(require("node:fs"));
14
14
  const node_path_1 = __importDefault(require("node:path"));
15
- const node_child_process_1 = require("node:child_process");
16
15
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
17
16
  // eslint-disable-next-line @typescript-eslint/no-require-imports
18
17
  const planningWorkspace = require("./planning-workspace.cjs");
@@ -1923,7 +1922,45 @@ function applyMigration(cwd, plan, options = {}) {
1923
1922
  // ── Real run: verify clean working tree ───────────────────────────────────
1924
1923
  let gitStatus;
1925
1924
  try {
1926
- gitStatus = (0, node_child_process_1.execSync)('git status --porcelain', { cwd, encoding: 'utf8', windowsHide: true, timeout: 10_000 });
1925
+ // #5048: this is a read-only spawn, but git still refreshes the index under
1926
+ // it and takes the optional `.git/index.lock` to write the refreshed copy
1927
+ // back — which can lose a race against a real `git add` / `git commit` in
1928
+ // the same repo. Routing through the shared execGit seam (CONTEXT.md, OS
1929
+ // Shell Projection) is what gets GIT_OPTIONAL_LOCKS=0 here; a bare execSync
1930
+ // bypassed the seam and therefore the opt-out, so this call site was the one
1931
+ // read-only `git status` the seam change did not reach. shell-free, so no
1932
+ // argv quoting or PATH-resolution policy is introduced.
1933
+ const statusTimeoutMs = 10_000;
1934
+ const result = (0, shell_command_projection_cjs_1.execGit)(['status', '--porcelain'], { cwd, timeout: statusTimeoutMs });
1935
+ // #5048: execSync carried the cause on the thrown error. execGit reports a
1936
+ // timeout, a spawn/buffer failure or a signal through `timedOut`/`error`/
1937
+ // `signal` with an exit code of `status ?? 1` and often an empty stderr.
1938
+ // Order matters: a maxBuffer overflow returns signal SIGTERM *and*
1939
+ // error.code ENOBUFS — check `error` before `signal` so the real cause is
1940
+ // not reported as "killed by SIGTERM". Only a genuine non-zero exit falls
1941
+ // through. (ENOENT is rewritten by `_spawnResult` to exit 127 + stderr.)
1942
+ if (result.timedOut) {
1943
+ throw new Error(`git status timed out after ${statusTimeoutMs} ms`);
1944
+ }
1945
+ if (result.error) {
1946
+ // The cause is not always a failed spawn: ENOBUFS and EPERM reach this
1947
+ // branch with the process already started (a maxBuffer overflow returns
1948
+ // signal SIGTERM *and* error.code ENOBUFS). So the message stays neutral
1949
+ // about starting and names the code instead of inferring a cause from it.
1950
+ const code = result.error.code;
1951
+ const why = code === 'ENOBUFS' ? 'output exceeded the buffer' : code;
1952
+ const detail = why && !result.error.message.includes(why)
1953
+ ? `${result.error.message} (${why})`
1954
+ : result.error.message;
1955
+ throw new Error(detail);
1956
+ }
1957
+ if (result.signal) {
1958
+ throw new Error(`git status was killed by ${result.signal}`);
1959
+ }
1960
+ if (result.exitCode !== 0) {
1961
+ throw new Error(result.stderr || `git exited ${result.exitCode}`);
1962
+ }
1963
+ gitStatus = result.stdout;
1927
1964
  }
1928
1965
  catch (err) {
1929
1966
  throw new Error(`git status failed: ${err.message}`);
@@ -1111,13 +1111,26 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
1111
1111
  // Heading target: the exact grammar `planSectionPattern` /
1112
1112
  // `insertRowsPatternA|B` anchor on (an ATX phase heading for this phase).
1113
1113
  const headingTargetFound = new RegExp(phaseHeadingAnchor, 'i').test(gateActiveRegion);
1114
+ // #5545: the phase-checklist bullet grammar, defined ONCE for the writer
1115
+ // (an unchecked bullet to tick) and the gate probe (`anyState`).
1116
+ // Line-anchored, with only an optional `**` before the label and horizontal
1117
+ // whitespace, so a bullet that merely MENTIONS "Phase N" is not the phase's
1118
+ // bullet and no `\s` can run past the line end (which used to land the
1119
+ // `(completed …)` annotation on the NEXT line). A decorated bullet (emoji,
1120
+ // blockquote, link) is deliberately NOT the phase's bullet either — the
1121
+ // same narrowing #4965 made for `roadmap analyze`'s checklist reader.
1122
+ // The lookahead only PEEKS (its `\s` may see a newline but consumes none):
1123
+ // the label must be followed by `:`, whitespace or the end of the line, so
1124
+ // `Phase 2.1` / `Phase 20` are not `Phase 2`. Capture group 1 is the opening
1125
+ // `- [`, group 2 the rest of the line after the box.
1126
+ const phaseChecklistBullet = (anyState) => new RegExp(`^([ \\t]*-[ \\t]*\\[)${anyState ? '[ x]' : ' '}(\\][ \\t]*(?:\\*\\*)?[ \\t]*Phase[ \\t]+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}(?=[:\\s]|$)[^\\n]*)`, 'im');
1114
1127
  // Checklist target: when the phase is complete, its own checklist bullet
1115
1128
  // (`- [ ] **Phase N: …**`) IS a writable phase row — the completion
1116
1129
  // checkbox stamp below updates it. Same grammar as that writer, widened
1117
1130
  // one notch to `[ x]` so an ALREADY-checked bullet still counts as a
1118
1131
  // found target: an idempotent re-run then takes the honest
1119
1132
  // "no changes were needed" decline instead of this refusal.
1120
- const checklistTargetFound = isComplete && new RegExp(`-\\s*\\[[ x]\\]\\s*.*Phase\\s+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s]`, 'i').test(gateActiveRegion);
1133
+ const checklistTargetFound = isComplete && phaseChecklistBullet(true).test(gateActiveRegion);
1121
1134
  // Table-row target: set by the row-scoped cell updates below.
1122
1135
  let tableRowFound = false;
1123
1136
  // Progress table row: update Plans Complete/Status/Completed columns BY
@@ -1399,24 +1412,25 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
1399
1412
  });
1400
1413
  // If complete: check checkbox
1401
1414
  if (isComplete) {
1402
- const checkboxPattern = new RegExp(`(-\\s*\\[)[ ](\\]\\s*.*Phase\\s+${phasePattern}${OPTIONAL_PHASE_TAG_SOURCE}[:\\s][^\\n]*)`, 'i');
1403
- roadmapContent = replaceInCurrentMilestone(roadmapContent, checkboxPattern, `$1x$2 (completed ${today})`);
1415
+ roadmapContent = replaceInCurrentMilestone(roadmapContent, phaseChecklistBullet(false), `$1x$2 (completed ${today})`);
1404
1416
  }
1405
1417
  // Mark completed plan checkboxes (e.g. "- [ ] 50-01-PLAN.md", "- [ ] 50-01:", or "- [ ] **50-01**")
1406
1418
  // #4741: tick only plans the phase's own COUNT still counts. `plans` is the
1407
1419
  // superseded-filtered set (#2349 via scanPhasePlans) while `summaries` is
1408
1420
  // the raw *-SUMMARY.md listing — a superseded plan can carry a SUMMARY
1409
1421
  // (e.g. `status: halted`), and ticking it read as "executed" right under a
1410
- // count line that excludes it. The prefix match mirrors the checkbox regex
1411
- // below (rows match by planId prefix, which the PLAN-01.md naming shape
1412
- // relies on), so non-superseded plans tick exactly as before.
1422
+ // count line that excludes it. #5542: a summary belongs to a live plan only
1423
+ // by the SAME pairing rule the counts use (`summaryCandidates` via
1424
+ // `countMatchedSummaries`), never by id prefix — a leftover `47-1` summary
1425
+ // must not survive because a live `47-10` plan starts with `47-1`. Rows
1426
+ // match the id up to a boundary (`planRowPattern`), so `47-1` never ticks
1427
+ // `47-10-PLAN.md`.
1413
1428
  const tickableSummaries = phaseInfo.summaries
1414
1429
  .map((summaryFile) => ({ summaryFile, planId: summaryFile.replace('-SUMMARY.md', '').replace('SUMMARY.md', '') }))
1415
- .filter(({ planId }) => planId !== '' && phaseInfo.plans.some((planFile) => planFile.startsWith(planId)));
1430
+ .filter(({ summaryFile, planId }) => planId !== ''
1431
+ && coreUtils.summaryPairsWithAnyPlan(phaseInfo.plans, summaryFile));
1416
1432
  for (const { planId } of tickableSummaries) {
1417
- const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
1418
- const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
1419
- roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
1433
+ roadmapContent = roadmapContent.replace(coreUtils.planRowPattern(planId), '$1x$2');
1420
1434
  }
1421
1435
  // Compute the active (post-</details>) region offset ONCE. Both the
1422
1436
  // missing-plan DETECTION and the row INSERTION must use the same active
@@ -1440,23 +1454,19 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
1440
1454
  //
1441
1455
  // #4786: a plan row may be written WITH the `-PLAN.md` suffix (canonical
1442
1456
  // template form) or WITHOUT it (hand-written form: `- [x] 659-01 — desc`).
1443
- // The tick loop above keys on the bare planId stem with a PREFIX match
1444
- // (pre-existing, out of scope here), while detection required the full
1445
- // `-PLAN.md` filename — so every suffix-less row was counted missing and
1446
- // the insertion fired BESIDE the recognized list — 32 checkbox lines for
1447
- // 16 plans, exit 0. The stem arm below accepts the bare id only up to a
1448
- // boundary (whitespace / `:` / dashes / `-PLAN.md` / `.md` / `**` / `)`),
1449
- // so `5-011` never satisfies `5-01`. Deliberately NOT in the boundary set:
1450
- // `.` — a dotted sub-id (`5-01.5`) is a real distinct plan, and counting
1451
- // its row as 5-01's presence would suppress a genuine insertion.
1457
+ // The tick loop above keys on the bare planId stem, while detection required
1458
+ // the full `-PLAN.md` filename — so every suffix-less row was counted
1459
+ // missing and the insertion fired BESIDE the recognized list — 32 checkbox
1460
+ // lines for 16 plans, exit 0. The stem arm below accepts the bare id only
1461
+ // up to the SAME boundary the tick loop uses (`planRowPattern`, #5542), so
1462
+ // `5-011` never satisfies `5-01` and a dotted sub-id (`5-01.5`) is a real
1463
+ // distinct plan whose row does not count as 5-01's presence.
1452
1464
  const missingPlans = phaseInfo.plans.filter((planFile) => {
1453
1465
  const planEscaped = (0, pattern_cjs_1.escapeRegex)(planFile);
1454
1466
  if (new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${planEscaped}`, 'i').test(activeRegion))
1455
1467
  return false;
1456
1468
  const stem = planFile.replace(/-PLAN\.md$/i, '');
1457
- const stemEscaped = (0, pattern_cjs_1.escapeRegex)(stem);
1458
- const stemPresent = new RegExp(`-\\s*\\[[x ]\\]\\s*(?:\\*\\*)?${stemEscaped}(?=$|\\s|:|—|–|-PLAN\\.md|\\.md|\\*\\*|\\))`, 'i').test(activeRegion);
1459
- return !stemPresent;
1469
+ return !coreUtils.planRowPattern(stem, { anyState: true }).test(activeRegion);
1460
1470
  });
1461
1471
  if (missingPlans.length > 0) {
1462
1472
  // Insert missing plan checklist rows (#1163). We prefer to anchor to the
@@ -1502,9 +1512,7 @@ function cmdRoadmapUpdatePlanProgress(cwd, phaseNum, raw) {
1502
1512
  // (#4741: same superseded-filtered tick list as the loop above — a
1503
1513
  // pre-existing superseded row must stay unchecked on this path too).
1504
1514
  for (const { planId } of tickableSummaries) {
1505
- const planEscaped = (0, pattern_cjs_1.escapeRegex)(planId);
1506
- const planCheckboxPattern = new RegExp(`(-\\s*\\[) (\\]\\s*(?:\\*\\*)?${planEscaped}(?:\\*\\*)?)`, 'i');
1507
- roadmapContent = roadmapContent.replace(planCheckboxPattern, '$1x$2');
1515
+ roadmapContent = roadmapContent.replace(coreUtils.planRowPattern(planId), '$1x$2');
1508
1516
  }
1509
1517
  }
1510
1518
  }