@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.
Files changed (308) 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 +7 -7
  5. package/agents/gsd-code-fixer.md +13 -7
  6. package/agents/gsd-code-reviewer.compact.md +49 -8
  7. package/agents/gsd-code-reviewer.md +53 -14
  8. package/agents/gsd-executor.md +8 -4
  9. package/agents/gsd-verifier.md +1 -1
  10. package/bin/install.js +422 -103
  11. package/commands/gsd/update.md +5 -2
  12. package/gsd-core/bin/gsd-tools.cjs +179 -30
  13. package/gsd-core/bin/lib/agent-install-check.cjs +29 -3
  14. package/gsd-core/bin/lib/args-terminator.cjs +114 -0
  15. package/gsd-core/bin/lib/broken-windows.cjs +59 -1
  16. package/gsd-core/bin/lib/capability-lifecycle.cjs +1 -12
  17. package/gsd-core/bin/lib/capability-lock.cjs +13 -2
  18. package/gsd-core/bin/lib/capability-registry.cjs +376 -103
  19. package/gsd-core/bin/lib/capability-state.cjs +14 -8
  20. package/gsd-core/bin/lib/capability-validator.cjs +32 -0
  21. package/gsd-core/bin/lib/commands.cjs +27 -17
  22. package/gsd-core/bin/lib/config-loader.cjs +13 -3
  23. package/gsd-core/bin/lib/config.cjs +256 -58
  24. package/gsd-core/bin/lib/core-utils.cjs +45 -0
  25. package/gsd-core/bin/lib/decision-coverage-support.cjs +74 -0
  26. package/gsd-core/bin/lib/frontmatter.cjs +21 -3
  27. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +2 -2
  28. package/gsd-core/bin/lib/gate-context-drift.cjs +9 -4
  29. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +6 -1
  30. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +6 -1
  31. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +134 -13
  32. package/gsd-core/bin/lib/gate-phase-context.cjs +7 -8
  33. package/gsd-core/bin/lib/gate-schema-drift.cjs +1 -1
  34. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +63 -22
  35. package/gsd-core/bin/lib/gate-ui-safety.cjs +23 -5
  36. package/gsd-core/bin/lib/git-info-exclude.cjs +52 -0
  37. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +31 -12
  38. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +9 -7
  39. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +3 -3
  40. package/gsd-core/bin/lib/health-diagnostic-types.cjs +4 -0
  41. package/gsd-core/bin/lib/health-diagnostic.cjs +52 -1
  42. package/gsd-core/bin/lib/host-integration.cjs +13 -6
  43. package/gsd-core/bin/lib/init.cjs +245 -68
  44. package/gsd-core/bin/lib/install-engine.cjs +246 -32
  45. package/gsd-core/bin/lib/install-profiles.cjs +9 -4
  46. package/gsd-core/bin/lib/installer-migration-report.cjs +2 -0
  47. package/gsd-core/bin/lib/io.cjs +6 -0
  48. package/gsd-core/bin/lib/markdown-line-diff.cjs +147 -0
  49. package/gsd-core/bin/lib/markdown-sectionizer.cjs +147 -1
  50. package/gsd-core/bin/lib/markdown-table.cjs +23 -0
  51. package/gsd-core/bin/lib/mcp-server.cjs +8 -5
  52. package/gsd-core/bin/lib/milestone-lock.cjs +8 -1
  53. package/gsd-core/bin/lib/milestone-release-map.cjs +244 -0
  54. package/gsd-core/bin/lib/milestone-release-tags.cjs +440 -0
  55. package/gsd-core/bin/lib/milestone.cjs +61 -51
  56. package/gsd-core/bin/lib/model-catalog.cjs +8 -0
  57. package/gsd-core/bin/lib/normalize-test-command.cjs +67 -0
  58. package/gsd-core/bin/lib/phase-entry-grammar.cjs +119 -0
  59. package/gsd-core/bin/lib/phase-id-display.cjs +2 -0
  60. package/gsd-core/bin/lib/phase-id.cjs +629 -38
  61. package/gsd-core/bin/lib/phase-lifecycle.cjs +12 -44
  62. package/gsd-core/bin/lib/phase-locator.cjs +328 -19
  63. package/gsd-core/bin/lib/phase.cjs +3066 -653
  64. package/gsd-core/bin/lib/planning-document.cjs +26 -5
  65. package/gsd-core/bin/lib/planning-inspect.cjs +27 -35
  66. package/gsd-core/bin/lib/planning-snapshot.cjs +15 -8
  67. package/gsd-core/bin/lib/planning-workspace.cjs +21 -3
  68. package/gsd-core/bin/lib/pristine-baseline.cjs +11 -3
  69. package/gsd-core/bin/lib/read-only-git-env.cjs +79 -0
  70. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +2 -2
  71. package/gsd-core/bin/lib/review-lane-descriptor.cjs +57 -2
  72. package/gsd-core/bin/lib/review-lane-invocation.cjs +125 -18
  73. package/gsd-core/bin/lib/review-lane-runner.cjs +33 -4
  74. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +11 -9
  75. package/gsd-core/bin/lib/roadmap-parser.cjs +735 -137
  76. package/gsd-core/bin/lib/roadmap-progress.cjs +136 -0
  77. package/gsd-core/bin/lib/roadmap-upgrade.cjs +47 -7
  78. package/gsd-core/bin/lib/roadmap.cjs +383 -123
  79. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +381 -14
  80. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +8 -4
  81. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +60 -16
  82. package/gsd-core/bin/lib/shell-command-projection.cjs +151 -19
  83. package/gsd-core/bin/lib/smart-entry.cjs +35 -11
  84. package/gsd-core/bin/lib/state-contract.cjs +39 -15
  85. package/gsd-core/bin/lib/state-document.cjs +344 -27
  86. package/gsd-core/bin/lib/state-transition.cjs +156 -32
  87. package/gsd-core/bin/lib/state.cjs +527 -186
  88. package/gsd-core/bin/lib/surface.cjs +9 -0
  89. package/gsd-core/bin/lib/task-command-router.cjs +5 -25
  90. package/gsd-core/bin/lib/task-content-resolution.cjs +46 -1
  91. package/gsd-core/bin/lib/template.cjs +2 -2
  92. package/gsd-core/bin/lib/validate.cjs +3 -2
  93. package/gsd-core/bin/lib/vendor/re2js.cjs +863 -577
  94. package/gsd-core/bin/lib/vendor/re2js.d.cts +284 -219
  95. package/gsd-core/bin/lib/verification.cjs +96 -9
  96. package/gsd-core/bin/lib/verify.cjs +4 -2
  97. package/gsd-core/bin/lib/workstream-inventory.cjs +26 -20
  98. package/gsd-core/bin/lib/worktree-safety.cjs +22 -1
  99. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  100. package/gsd-core/bin/shared/config-schema.manifest.json +1 -0
  101. package/gsd-core/bin/verify-reapply-patches.cjs +411 -10
  102. package/gsd-core/references/cicd-review-patterns.md +45 -0
  103. package/gsd-core/references/git-integration.md +11 -0
  104. package/gsd-core/references/loop-hook-dispatch.md +13 -6
  105. package/gsd-core/references/necessity-review.md +48 -0
  106. package/gsd-core/references/planning-config.md +2 -0
  107. package/gsd-core/references/tdd.md +2 -2
  108. package/gsd-core/templates/DEBUG.md +16 -0
  109. package/gsd-core/templates/config.json +2 -1
  110. package/gsd-core/templates/verification-report.md +1 -1
  111. package/gsd-core/workflows/audit-fix.md +2 -1
  112. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +10 -1
  113. package/gsd-core/workflows/code-review-fix.md +46 -5
  114. package/gsd-core/workflows/code-review.md +64 -7
  115. package/gsd-core/workflows/complete-milestone/steps/project-md-evolution-review.md +145 -0
  116. package/gsd-core/workflows/complete-milestone/steps/release-map.md +80 -0
  117. package/gsd-core/workflows/complete-milestone.md +37 -150
  118. package/gsd-core/workflows/diagnose-issues.md +29 -5
  119. package/gsd-core/workflows/discuss-phase/modes/power.md +1 -1
  120. package/gsd-core/workflows/discuss-phase-assumptions.md +33 -2
  121. package/gsd-core/workflows/discuss-phase-power.md +19 -2
  122. package/gsd-core/workflows/discuss-phase.md +6 -10
  123. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +13 -7
  124. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +7 -2
  125. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -1
  126. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +2 -2
  127. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +11 -2
  128. package/gsd-core/workflows/execute-phase.md +10 -6
  129. package/gsd-core/workflows/execute-plan.md +1 -1
  130. package/gsd-core/workflows/health.md +2 -1
  131. package/gsd-core/workflows/import.md +1 -1
  132. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +2 -2
  133. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +13 -9
  134. package/gsd-core/workflows/plan-phase.md +14 -14
  135. package/gsd-core/workflows/pr-branch.md +2 -0
  136. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -1
  137. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +8 -1
  138. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +2 -1
  139. package/gsd-core/workflows/quick.md +1 -1
  140. package/gsd-core/workflows/reapply-patches.md +104 -0
  141. package/gsd-core/workflows/review.md +70 -154
  142. package/gsd-core/workflows/section-manifest.json +5 -0
  143. package/gsd-core/workflows/secure-phase.md +1 -1
  144. package/gsd-core/workflows/settings-advanced.md +57 -9
  145. package/gsd-core/workflows/settings.md +1 -0
  146. package/gsd-core/workflows/ship.md +15 -4
  147. package/gsd-core/workflows/spec-phase.md +3 -6
  148. package/gsd-core/workflows/verify-work.md +4 -3
  149. package/hooks/dist/gsd-agent-isolation-guard.js +11 -1
  150. package/hooks/dist/gsd-cursor-subagent-start.js +6 -2
  151. package/hooks/dist/gsd-graphify-update.sh +6 -0
  152. package/hooks/dist/gsd-path-export.js +95 -0
  153. package/hooks/dist/gsd-secret-read-guard.js +172 -39
  154. package/hooks/dist/gsd-statusline.js +37 -3
  155. package/hooks/dist/gsd-windsurf-pre-write.js +29 -2
  156. package/hooks/dist/gsd-workflow-guard.js +3 -2
  157. package/hooks/dist/gsd-worktree-path-guard.js +29 -2
  158. package/hooks/dist/lib/git-probe.js +16 -0
  159. package/hooks/dist/lib/isolation-sentinel.js +44 -0
  160. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  161. package/hooks/gsd-agent-isolation-guard.js +11 -1
  162. package/hooks/gsd-cursor-subagent-start.js +6 -2
  163. package/hooks/gsd-graphify-update.sh +6 -0
  164. package/hooks/gsd-path-export.js +95 -0
  165. package/hooks/gsd-secret-read-guard.js +172 -39
  166. package/hooks/gsd-statusline.js +37 -3
  167. package/hooks/gsd-windsurf-pre-write.js +29 -2
  168. package/hooks/gsd-workflow-guard.js +3 -2
  169. package/hooks/gsd-worktree-path-guard.js +29 -2
  170. package/hooks/hooks.json +1 -0
  171. package/hooks/lib/git-probe.js +16 -0
  172. package/hooks/lib/isolation-sentinel.js +44 -0
  173. package/hooks/managed-hooks-registry.cjs +1 -0
  174. package/package.json +15 -8
  175. package/pi/gsd.cjs +1047 -10
  176. package/pi/package-agents/gsd-advisor-researcher.md +113 -0
  177. package/pi/package-agents/gsd-ai-researcher.md +111 -0
  178. package/pi/package-agents/gsd-assumptions-analyzer.md +110 -0
  179. package/pi/package-agents/gsd-code-fixer.md +774 -0
  180. package/pi/package-agents/gsd-code-reviewer.md +441 -0
  181. package/pi/package-agents/gsd-codebase-mapper.md +850 -0
  182. package/pi/package-agents/gsd-debug-session-manager.md +416 -0
  183. package/pi/package-agents/gsd-debugger.md +1274 -0
  184. package/pi/package-agents/gsd-doc-classifier.md +269 -0
  185. package/pi/package-agents/gsd-doc-synthesizer.md +259 -0
  186. package/pi/package-agents/gsd-doc-verifier.md +212 -0
  187. package/pi/package-agents/gsd-doc-writer.md +626 -0
  188. package/pi/package-agents/gsd-dom-verifier.md +164 -0
  189. package/pi/package-agents/gsd-domain-researcher.md +144 -0
  190. package/pi/package-agents/gsd-eval-auditor.md +185 -0
  191. package/pi/package-agents/gsd-eval-planner.md +149 -0
  192. package/pi/package-agents/gsd-executor.md +898 -0
  193. package/pi/package-agents/gsd-framework-selector.md +159 -0
  194. package/pi/package-agents/gsd-integration-checker.md +476 -0
  195. package/pi/package-agents/gsd-intel-updater.md +338 -0
  196. package/pi/package-agents/gsd-mempalace-curator.md +49 -0
  197. package/pi/package-agents/gsd-nyquist-auditor.md +200 -0
  198. package/pi/package-agents/gsd-pattern-mapper.md +340 -0
  199. package/pi/package-agents/gsd-phase-researcher.md +897 -0
  200. package/pi/package-agents/gsd-plan-checker.md +1077 -0
  201. package/pi/package-agents/gsd-planner.md +1003 -0
  202. package/pi/package-agents/gsd-project-researcher.md +611 -0
  203. package/pi/package-agents/gsd-research-synthesizer.md +259 -0
  204. package/pi/package-agents/gsd-roadmapper.md +779 -0
  205. package/pi/package-agents/gsd-security-auditor.md +171 -0
  206. package/pi/package-agents/gsd-ui-auditor.md +581 -0
  207. package/pi/package-agents/gsd-ui-checker.md +417 -0
  208. package/pi/package-agents/gsd-ui-researcher.md +469 -0
  209. package/pi/package-agents/gsd-user-profiler.md +174 -0
  210. package/pi/package-agents/gsd-verifier.md +963 -0
  211. package/pi/package-agents/manifest.json +39 -0
  212. package/pi/package-skills/gsd-add-tests/SKILL.md +39 -0
  213. package/pi/package-skills/gsd-ai-integration-phase/SKILL.md +36 -0
  214. package/pi/package-skills/gsd-audit-fix/SKILL.md +34 -0
  215. package/pi/package-skills/gsd-audit-milestone/SKILL.md +40 -0
  216. package/pi/package-skills/gsd-audit-uat/SKILL.md +26 -0
  217. package/pi/package-skills/gsd-autonomous/SKILL.md +52 -0
  218. package/pi/package-skills/gsd-capture/SKILL.md +68 -0
  219. package/pi/package-skills/gsd-cleanup/SKILL.md +25 -0
  220. package/pi/package-skills/gsd-code-review/SKILL.md +71 -0
  221. package/pi/package-skills/gsd-complete-milestone/SKILL.md +147 -0
  222. package/pi/package-skills/gsd-config/SKILL.md +60 -0
  223. package/pi/package-skills/gsd-debug/SKILL.md +64 -0
  224. package/pi/package-skills/gsd-discuss-phase/SKILL.md +74 -0
  225. package/pi/package-skills/gsd-docs-update/SKILL.md +50 -0
  226. package/pi/package-skills/gsd-eval-review/SKILL.md +35 -0
  227. package/pi/package-skills/gsd-execute-phase/SKILL.md +62 -0
  228. package/pi/package-skills/gsd-explore/SKILL.md +26 -0
  229. package/pi/package-skills/gsd-extract-learnings/SKILL.md +25 -0
  230. package/pi/package-skills/gsd-fast/SKILL.md +33 -0
  231. package/pi/package-skills/gsd-forensics/SKILL.md +59 -0
  232. package/pi/package-skills/gsd-graphify/SKILL.md +210 -0
  233. package/pi/package-skills/gsd-health/SKILL.md +36 -0
  234. package/pi/package-skills/gsd-help/SKILL.md +37 -0
  235. package/pi/package-skills/gsd-import/SKILL.md +46 -0
  236. package/pi/package-skills/gsd-inbox/SKILL.md +42 -0
  237. package/pi/package-skills/gsd-ingest-docs/SKILL.md +43 -0
  238. package/pi/package-skills/gsd-manager/SKILL.md +46 -0
  239. package/pi/package-skills/gsd-map-codebase/SKILL.md +85 -0
  240. package/pi/package-skills/gsd-mempalace-capture/SKILL.md +111 -0
  241. package/pi/package-skills/gsd-mempalace-recall/SKILL.md +111 -0
  242. package/pi/package-skills/gsd-milestone-summary/SKILL.md +54 -0
  243. package/pi/package-skills/gsd-mvp-phase/SKILL.md +45 -0
  244. package/pi/package-skills/gsd-new-milestone/SKILL.md +49 -0
  245. package/pi/package-skills/gsd-new-project/SKILL.md +49 -0
  246. package/pi/package-skills/gsd-next/SKILL.md +34 -0
  247. package/pi/package-skills/gsd-ns-context/SKILL.md +31 -0
  248. package/pi/package-skills/gsd-ns-ideate/SKILL.md +30 -0
  249. package/pi/package-skills/gsd-ns-manage/SKILL.md +42 -0
  250. package/pi/package-skills/gsd-ns-project/SKILL.md +34 -0
  251. package/pi/package-skills/gsd-ns-review/SKILL.md +35 -0
  252. package/pi/package-skills/gsd-ns-workflow/SKILL.md +44 -0
  253. package/pi/package-skills/gsd-onboard/SKILL.md +46 -0
  254. package/pi/package-skills/gsd-pause-work/SKILL.md +48 -0
  255. package/pi/package-skills/gsd-phase/SKILL.md +61 -0
  256. package/pi/package-skills/gsd-plan-phase/SKILL.md +62 -0
  257. package/pi/package-skills/gsd-plan-review-convergence/SKILL.md +63 -0
  258. package/pi/package-skills/gsd-pr-branch/SKILL.md +31 -0
  259. package/pi/package-skills/gsd-profile-user/SKILL.md +48 -0
  260. package/pi/package-skills/gsd-progress/SKILL.md +51 -0
  261. package/pi/package-skills/gsd-quick/SKILL.md +179 -0
  262. package/pi/package-skills/gsd-quick-batch/SKILL.md +114 -0
  263. package/pi/package-skills/gsd-resume-work/SKILL.md +31 -0
  264. package/pi/package-skills/gsd-review/SKILL.md +50 -0
  265. package/pi/package-skills/gsd-review-backlog/SKILL.md +65 -0
  266. package/pi/package-skills/gsd-secure-phase/SKILL.md +37 -0
  267. package/pi/package-skills/gsd-settings/SKILL.md +30 -0
  268. package/pi/package-skills/gsd-ship/SKILL.md +27 -0
  269. package/pi/package-skills/gsd-sketch/SKILL.md +56 -0
  270. package/pi/package-skills/gsd-spec-phase/SKILL.md +63 -0
  271. package/pi/package-skills/gsd-spike/SKILL.md +53 -0
  272. package/pi/package-skills/gsd-stats/SKILL.md +22 -0
  273. package/pi/package-skills/gsd-surface/SKILL.md +177 -0
  274. package/pi/package-skills/gsd-thread/SKILL.md +29 -0
  275. package/pi/package-skills/gsd-ui-phase/SKILL.md +35 -0
  276. package/pi/package-skills/gsd-ui-review/SKILL.md +35 -0
  277. package/pi/package-skills/gsd-ultraplan-phase/SKILL.md +38 -0
  278. package/pi/package-skills/gsd-undo/SKILL.md +38 -0
  279. package/pi/package-skills/gsd-update/SKILL.md +54 -0
  280. package/pi/package-skills/gsd-validate-phase/SKILL.md +37 -0
  281. package/pi/package-skills/gsd-verify-work/SKILL.md +41 -0
  282. package/pi/package-skills/gsd-workspace/SKILL.md +57 -0
  283. package/pi/package-skills/gsd-workstreams/SKILL.md +73 -0
  284. package/scripts/build-hooks.js +4 -0
  285. package/scripts/ci-next-health.cjs +246 -23
  286. package/scripts/command-contract-helpers.cjs +1 -1
  287. package/scripts/docs-guard-registry.cjs +22 -7
  288. package/scripts/gen-features.cjs +14 -2
  289. package/scripts/gen-health-docs.cjs +13 -1
  290. package/scripts/gen-pi-package-skills.cjs +217 -0
  291. package/scripts/lib/drift-scan.cjs +27 -0
  292. package/scripts/lib/macos-conformance-tier.generated.cjs +17 -1
  293. package/scripts/lib/platform-conformance-tier.generated.cjs +37 -1
  294. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +3 -3
  295. package/scripts/lint-phase-enumeration-drift.cjs +8 -4
  296. package/scripts/lint-retired-runtime-name.cjs +5 -0
  297. package/scripts/lint-roadmap-progress-drift.cjs +421 -0
  298. package/scripts/mutation-matrix.cjs +23 -1
  299. package/scripts/npm-audit-baseline.cjs +41 -7
  300. package/scripts/reviewed-or-maintainer-policy.cjs +181 -0
  301. package/scripts/select-docs-guards.cjs +1 -1
  302. package/scripts/self-test-report-policy.cjs +350 -0
  303. package/scripts/sync-next-version.cjs +38 -6
  304. package/skills/gsd-update/SKILL.md +5 -2
  305. package/vscode/package.json +1 -1
  306. package/scripts/diagnose-validate-commit-in-tier.cjs +0 -270
  307. package/scripts/diagnose-validate-commit-phases.cjs +0 -348
  308. package/scripts/xtrace-phase-timing.cjs +0 -205
@@ -47,6 +47,8 @@ const coreUtilsMod = require("./core-utils.cjs");
47
47
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-scope.cjs is an export= CommonJS module
48
48
  const planningScopeMod = require("./planning-scope.cjs");
49
49
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
50
+ const text_lines_cjs_1 = require("./text-lines.cjs");
51
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
50
52
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
51
53
  const security_cjs_1 = require("./security.cjs");
52
54
  const pattern_cjs_1 = require("./pattern.cjs");
@@ -607,17 +609,19 @@ function phaseArtifactPaths(phaseDir, projectRoot) {
607
609
  * `.planning/<project>/workstreams/<ws>`), covering a
608
610
  * per-project/per-workstream store symlink (amendment 1,
609
611
  * revised R7).
612
+ * v4 (#5351) — v3 with each CRLF byte pair hashed as LF. Every other
613
+ * byte, including a lone CR and non-UTF-8 input, is preserved.
610
614
  *
611
615
  * A stored digest names its own version (`v<N>:sha256:…`), and
612
616
  * `readVerificationStatus` recomputes under the STORED version rather than
613
617
  * this constant — so bumping it does not flip every already-verified phase
614
- * to `stale` on upgrade. A legacy v1/v2 report keeps its own semantics
615
- * (shared documents included for v1; no report filter or artifact union for
616
- * either) until it is re-fingerprinted; only a version outside
618
+ * to `stale` on upgrade. A legacy v1/v2/v3 report keeps its own raw-byte
619
+ * semantics (shared documents included for v1; no report filter or artifact
620
+ * union for v1/v2) until it is re-fingerprinted; only a version outside
617
621
  * `KNOWN_FINGERPRINT_VERSIONS` is unrecomputable and fails closed.
618
622
  */
619
- const FINGERPRINT_VERSION = 3;
620
- const KNOWN_FINGERPRINT_VERSIONS = new Set([1, 2, 3]);
623
+ const FINGERPRINT_VERSION = 4;
624
+ const KNOWN_FINGERPRINT_VERSIONS = new Set([1, 2, 3, 4]);
621
625
  /**
622
626
  * #4623: the planning roots whose DIRECT children are repo-wide planning
623
627
  * documents, as project-root-relative posix paths. Always `.planning`; plus
@@ -815,7 +819,22 @@ function deriveCoveredDigest(projectRoot, coveredFiles, version = FINGERPRINT_VE
815
819
  catch {
816
820
  return { ok: true, digest: null, files: declared, mappedPhaseDir: artifactMappedPhaseDir };
817
821
  }
818
- const fileHash = node_crypto_1.default.createHash('sha256').update(bytes).digest('hex');
822
+ const hash = node_crypto_1.default.createHash('sha256');
823
+ if (version >= 4) {
824
+ // #5351: drop only the CR in each CRLF pair without decoding the
825
+ // covered input — binary files and lone CR bytes keep their meaning.
826
+ // Hash Buffer slices directly, retaining the LF at each new start.
827
+ let start = 0;
828
+ for (let crlf = bytes.indexOf('\r\n'); crlf !== -1; crlf = bytes.indexOf('\r\n', start)) {
829
+ hash.update(bytes.subarray(start, crlf));
830
+ start = crlf + 1;
831
+ }
832
+ hash.update(bytes.subarray(start));
833
+ }
834
+ else {
835
+ hash.update(bytes);
836
+ }
837
+ const fileHash = hash.digest('hex');
819
838
  parts.push(`${rel}\n${fileHash}\n`);
820
839
  hashed++;
821
840
  }
@@ -1175,6 +1194,72 @@ function findVerificationStatusError(phaseDirs, deps = {}) {
1175
1194
  }
1176
1195
  return null;
1177
1196
  }
1197
+ /**
1198
+ * #5291: legacy report statuses `validate health --repair` may rewrite, and the
1199
+ * closed-set status each becomes. Deliberately minimal — only the value the
1200
+ * maintainer ruled on (`pass_with_caveats` -> `passed`). Every other status
1201
+ * outside the closed set (`pending`, `partial`, …) has NO mapping: it stays
1202
+ * refused and its W030 stays advise-only (fail closed; only `passed` satisfies
1203
+ * the gate). The original value is preserved in `legacy_status`.
1204
+ */
1205
+ const LEGACY_STATUS_REPAIRS = Object.freeze({
1206
+ pass_with_caveats: VERIFICATION_STATUS.PASSED,
1207
+ });
1208
+ /** The closed-set status a known legacy `raw` status repairs to, or `null` when it has no mapping. */
1209
+ function legacyStatusRepairTarget(raw) {
1210
+ return typeof raw === 'string' && Object.prototype.hasOwnProperty.call(LEGACY_STATUS_REPAIRS, raw)
1211
+ ? LEGACY_STATUS_REPAIRS[raw]
1212
+ : null;
1213
+ }
1214
+ /**
1215
+ * #5291: rewrite one report's frontmatter `status:` from a known legacy value
1216
+ * to its closed-set equivalent and record the original as `legacy_status:`
1217
+ * right after it. Touches only those lines — every other byte (body, other
1218
+ * keys, line endings) is preserved — and never overwrites an existing
1219
+ * `legacy_status`. Refuses (writes nothing) for a path that is not a report
1220
+ * contained in its own phase directory, a report with no frontmatter `status`,
1221
+ * and any status without a mapping.
1222
+ */
1223
+ function repairLegacyVerificationStatus(filePath) {
1224
+ if (!isReportContained(node_path_1.default.dirname(filePath), filePath)) {
1225
+ return { ok: false, reason: 'the report is not contained in its phase directory' };
1226
+ }
1227
+ let content;
1228
+ try {
1229
+ content = node_fs_1.default.readFileSync(filePath, 'utf-8');
1230
+ }
1231
+ catch (err) {
1232
+ return { ok: false, reason: err instanceof Error ? err.message : String(err) };
1233
+ }
1234
+ // The fence owner (`locateFrontmatterFence`) is the one place that knows where
1235
+ // the frontmatter body starts and ends (BOM, CRLF, lenient closing fence).
1236
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
1237
+ if (!fence || !fence.closed)
1238
+ return { ok: false, reason: 'the report has no closed frontmatter' };
1239
+ const lines = (0, text_lines_cjs_1.splitLines)(content.slice(fence.openEnd, fence.bodyEnd));
1240
+ // The YAML reader keeps the LAST of duplicate keys, so the line the repair
1241
+ // edits is the last `status:` line — the very value the refusal named. A
1242
+ // trailing comment (`status: pass_with_caveats # x`) is not part of the value.
1243
+ let statusAt = -1;
1244
+ lines.forEach((l, i) => { if (/^status:/.test(l))
1245
+ statusAt = i; });
1246
+ if (statusAt === -1)
1247
+ return { ok: false, reason: 'the report frontmatter has no status' };
1248
+ const raw = lines[statusAt]
1249
+ .replace(/^status:\s*/, '')
1250
+ .replace(/\s+#.*$/, '')
1251
+ .replace(/\s+$/, '')
1252
+ .replace(/^(["'])(.*)\1$/, '$2');
1253
+ const to = legacyStatusRepairTarget(raw);
1254
+ if (to === null)
1255
+ return { ok: false, reason: `${describeRawStatus(raw)} has no known repair` };
1256
+ const legacyStatusKept = lines.some((l) => /^legacy_status:/.test(l));
1257
+ lines[statusAt] = `status: ${to}`;
1258
+ if (!legacyStatusKept)
1259
+ lines.splice(statusAt + 1, 0, `legacy_status: ${raw}`);
1260
+ (0, shell_command_projection_cjs_1.platformWriteSync)(filePath, content.slice(0, fence.openEnd) + (0, text_lines_cjs_1.joinLines)(lines, fence.eol) + content.slice(fence.bodyEnd), { verbatim: true });
1261
+ return { ok: true, from: raw, to, legacyStatusKept };
1262
+ }
1178
1263
  /**
1179
1264
  * #3518: the shared phase-pinned artifact-selection core BOTH single-pick
1180
1265
  * resolvers (`resolveVerificationFile` for `*-VERIFICATION.md`,
@@ -1499,11 +1584,11 @@ function readVerificationStatus(phaseDir, opts = {}) {
1499
1584
  isStale = true;
1500
1585
  }
1501
1586
  else if (storedVersion >= 3) {
1502
- // #5095 (R1): v3's digest is computed over the declared set UNIONED
1587
+ // #5095 (R1): the v3+ digest is computed over the declared set UNIONED
1503
1588
  // with the phase's own live artifacts — a plan/summary added after
1504
1589
  // fingerprinting already moves the digest itself, so the separate
1505
1590
  // live-directory re-scan (`uncoveredCurrentArtifacts`) is redundant
1506
- // for v3 and is not run.
1591
+ // for v3+ and is not run.
1507
1592
  isStale =
1508
1593
  computeCoveredDigest(projectRoot, coveredFilesVal, storedVersion, { phaseDir }) !== coveredDigestVal;
1509
1594
  if (isStale) {
@@ -1784,7 +1869,7 @@ function parseFingerprintFileArgs(tokens) {
1784
1869
  * directly assignable. #5095 (ADR-5057 Phase 2): `raw`
1785
1870
  * returning only the digest is safe even though the
1786
1871
  * emitted `covered_files` is a SUPERSET of the caller's
1787
- * declared list — the v3 digest is computed over
1872
+ * declared list — the v3+ digest is computed over
1788
1873
  * `canonicalize(declared ∪ phaseArtifactPaths)` on BOTH
1789
1874
  * the emit side (here) and the check side
1790
1875
  * (`readVerificationStatus`), so a `--raw` caller that
@@ -2134,6 +2219,8 @@ const verificationModule = {
2134
2219
  isReportContained,
2135
2220
  routeResult,
2136
2221
  findVerificationStatusError,
2222
+ legacyStatusRepairTarget,
2223
+ repairLegacyVerificationStatus,
2137
2224
  defaultPhaseCleanCommitTimesMs,
2138
2225
  resolvePhaseArtifactFile,
2139
2226
  resolveVerificationFile,
@@ -42,7 +42,7 @@ const ioMod = require("./io.cjs");
42
42
  const { output, error } = ioMod;
43
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports
44
44
  const phaseLocatorMod = require("./phase-locator.cjs");
45
- const { findPhaseInternal } = phaseLocatorMod;
45
+ const { phaseDirectoryOptions, findPhaseInternal } = phaseLocatorMod;
46
46
  // eslint-disable-next-line @typescript-eslint/no-require-imports
47
47
  const roadmapParserMod = require("./roadmap-parser.cjs");
48
48
  const { stripShippedMilestones } = roadmapParserMod;
@@ -1282,7 +1282,7 @@ function cmdVerifyPhaseCompleteness(cwd, phase, raw) {
1282
1282
  if (!phase) {
1283
1283
  error('phase required');
1284
1284
  }
1285
- const phaseInfoRaw = findPhaseInternal(cwd, phase);
1285
+ const phaseInfoRaw = findPhaseInternal(cwd, phase, undefined, phaseDirectoryOptions(cwd));
1286
1286
  if (!phaseInfoRaw || !phaseInfoRaw['found']) {
1287
1287
  // An unresolvable phase is "could not look" (#5170): there is no phase to evaluate.
1288
1288
  emitVerbVerdict((0, gate_verdict_cjs_1.gateUnreadable)(false, { error: 'Phase not found', phase }), 'status', raw);
@@ -1809,6 +1809,8 @@ function repairFixText(slash, action) {
1809
1809
  return `Run ${slash('health')} --repair to add key`;
1810
1810
  case REMEDY_ACTION.BACKFILL_MILESTONES:
1811
1811
  return `Run ${slash('health')} --backfill to synthesize missing entries from archive snapshots`;
1812
+ case REMEDY_ACTION.REPAIR_VERIFICATION_STATUS:
1813
+ return `Run ${slash('health')} --repair to rewrite the legacy status (original kept as legacy_status)`;
1812
1814
  default:
1813
1815
  return '';
1814
1816
  }
@@ -30,7 +30,7 @@ const state_document_cjs_1 = require("./state-document.cjs");
30
30
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
31
31
  const frontmatterMod = require("./frontmatter.cjs");
32
32
  const { extractFrontmatter, stripFrontmatter } = frontmatterMod;
33
- const markdown_table_cjs_1 = require("./markdown-table.cjs");
33
+ const roadmap_progress_cjs_1 = require("./roadmap-progress.cjs");
34
34
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- verification.cjs is an export= CommonJS module
35
35
  const verificationMod = require("./verification.cjs");
36
36
  const { isPhaseComplete, isVerificationStatus, VERIFICATION_STATUS } = verificationMod;
@@ -41,6 +41,9 @@ const { phaseKeyFromDir, phaseKeyFromProse, parentPhaseKey } = phaseIdMod;
41
41
  const roadmapParserMod = require("./roadmap-parser.cjs");
42
42
  const { getMilestonePhaseFilter, isMilestoneShippedInRoadmap } = roadmapParserMod;
43
43
  const workstream_inventory_builder_cjs_1 = require("./workstream-inventory-builder.cjs");
44
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
45
+ const phaseLocatorMod = require("./phase-locator.cjs");
46
+ const { phaseDirectoryOptions } = phaseLocatorMod;
44
47
  // ─── Implementation ───────────────────────────────────────────────────────────
45
48
  function workstreamsRoot(cwd) {
46
49
  return node_path_1.default.join(planningRoot(cwd), 'workstreams');
@@ -75,7 +78,7 @@ function countRoadmapPhases(roadmapPath, fallbackCount, cwd, ws, versionOverride
75
78
  // `null` -> 0 (fallback), `undefined` -> 3. A non-bracket workstream
76
79
  // resolves to a value that compiles the same base grammar `null` did, so
77
80
  // its count is unchanged (verified against a legacy control).
78
- const filter = getMilestonePhaseFilter(cwd, versionOverride ?? null, undefined, ws ?? null);
81
+ const filter = getMilestonePhaseFilter(cwd, versionOverride ?? null, undefined, ws ?? null, phaseDirectoryOptions(cwd, undefined, undefined, ws ?? null));
79
82
  // A pass-all degrade (phaseCount 0) means the window declared no phases —
80
83
  // fall back rather than reporting a confident zero.
81
84
  return filter.phaseCount > 0 ? filter.phaseCount : fallbackCount;
@@ -98,25 +101,22 @@ function countRoadmapPhases(roadmapPath, fallbackCount, cwd, ws, versionOverride
98
101
  * `phaseKeyFromDir` uses for directories, so a `| 01. … |` row and a `1-slug`
99
102
  * directory cannot land in different key spaces (the padding-asymmetry defect).
100
103
  */
101
- function parseRoadmapProgressRows(roadmapPath) {
102
- let content;
103
- try {
104
- content = node_fs_1.default.readFileSync(roadmapPath, 'utf-8');
105
- }
106
- catch {
107
- return []; /* no roadmap */
108
- }
104
+ function parseRoadmapProgressRows(content, directoryContext) {
109
105
  // Milestone-ATTRIBUTING shape first. Both shapes carry `Plans Complete`, so
110
106
  // probing that column first would pick a flat table appearing earlier in the
111
107
  // document over a milestone-grouped one later — every row would come back
112
108
  // unattributed and be treated as current-milestone, silently over-including.
113
- const table = (0, markdown_table_cjs_1.findTableWithColumns)(content, ['Phase', 'Milestone'])
114
- ?? (0, markdown_table_cjs_1.findTableWithColumns)(content, ['Phase', 'Plans Complete']);
115
- if (!table)
116
- return [];
109
+ // #5761: the roadmap-progress owner picks the table (heading-scoped, so a
110
+ // decoy table elsewhere cannot be mistaken for it); this reader only supplies
111
+ // the probe order. A table outside a table-less `## Progress` section has
112
+ // always been read here (#5692: no behavior change), hence `sectionMiss`.
113
+ const { rows: tableRows } = (0, roadmap_progress_cjs_1.locateRoadmapProgress)(content, {
114
+ columns: [['Phase', 'Milestone'], ['Phase', 'Plans Complete']],
115
+ sectionMiss: 'document',
116
+ });
117
117
  const rows = [];
118
- for (const row of table.rows) {
119
- const key = phaseKeyFromProse(row['Phase']);
118
+ for (const row of tableRows) {
119
+ const key = phaseKeyFromProse(row['Phase'], directoryContext);
120
120
  if (key === null)
121
121
  continue;
122
122
  const cell = (row['Milestone'] ?? '').trim();
@@ -428,12 +428,18 @@ function inspectWorkstream(cwd, name, options = {}) {
428
428
  const writeDiagnostic = options.writeDiagnostic ?? ((message) => process.stderr.write(message));
429
429
  const p = planningPaths(cwd, name);
430
430
  const phaseDirNames = readSubdirectories(p.phases);
431
+ let roadmap = '';
432
+ try {
433
+ roadmap = node_fs_1.default.readFileSync(p.roadmap, 'utf8');
434
+ }
435
+ catch { /* no roadmap */ }
436
+ const directoryContext = phaseDirectoryOptions(cwd, undefined, roadmap, name);
431
437
  // #2562: scope progress to the CURRENT milestone. Membership and the
432
438
  // denominator are derived in ONE key space (`phaseKeyFromDir` /
433
439
  // `phaseKeyFromProse`, both from the phase-id owner module) so the two sides
434
440
  // of the rollup cannot disagree.
435
441
  const currentVersion = readCurrentMilestoneVersion(p.state, p.roadmap);
436
- const progressRows = parseRoadmapProgressRows(p.roadmap);
442
+ const progressRows = parseRoadmapProgressRows(roadmap, directoryContext);
437
443
  // Phase keys the ROADMAP attributes to the current milestone. A row whose
438
444
  // Milestone cell is blank or malformed is INCLUDED rather than dropped: a
439
445
  // phase we cannot attribute must still be visible to the rollup. Dropping it
@@ -458,7 +464,7 @@ function inspectWorkstream(cwd, name, options = {}) {
458
464
  // it is exactly the caller that cannot set `GSD_WORKSTREAM`; spelling `null`
459
465
  // pinned every workstream to the legacy grammar and made `headingScoped`
460
466
  // unreachable (`phaseCount > 0` never held) on a bracket workstream.
461
- const headingFilter = getMilestonePhaseFilter(cwd, currentVersion, undefined, name);
467
+ const headingFilter = getMilestonePhaseFilter(cwd, currentVersion, undefined, name, directoryContext);
462
468
  const headingScoped = headingFilter.versionScoped && headingFilter.phaseCount > 0;
463
469
  // Phase keys the ROADMAP attributes to some OTHER milestone. A row carrying an
464
470
  // explicit version that is not the current one is a positive claim by a prior
@@ -507,7 +513,7 @@ function inspectWorkstream(cwd, name, options = {}) {
507
513
  const isDirInCurrentMilestone = (dir) => {
508
514
  if (!scoped)
509
515
  return true;
510
- const key = phaseKeyFromDir(dir);
516
+ const key = phaseKeyFromDir(dir, undefined, directoryContext);
511
517
  if (currentMilestoneKeys.has(key))
512
518
  return true;
513
519
  const parent = parentPhaseKey(key);
@@ -563,7 +569,7 @@ function inspectWorkstream(cwd, name, options = {}) {
563
569
  }
564
570
  return {
565
571
  directory: dir,
566
- phaseKey: phaseKeyFromDir(dir),
572
+ phaseKey: phaseKeyFromDir(dir, undefined, directoryContext),
567
573
  mtimeMs: phaseDirMtime(phaseDir),
568
574
  planCount: counts.planCount,
569
575
  summaryCount: counts.summaryCount,
@@ -176,7 +176,28 @@ function resolveWorktreeContext(cwd, deps = {}) {
176
176
  reason: 'has_local_planning',
177
177
  };
178
178
  }
179
- return resolveWorktreeLinkage(cwd, deps);
179
+ const linkage = resolveWorktreeLinkage(cwd, deps);
180
+ if (linkage.reason !== 'linked_worktree')
181
+ return linkage;
182
+ // #4885: from a SUBDIRECTORY of a LINKED worktree the cwd has no .planning of its own, but the
183
+ // worktree's own top-level may. That checkout is the project the caller means; remapping to the
184
+ // main checkout would make `commit` and every planning write land in the wrong repository. A linked
185
+ // worktree with no .planning anywhere still remaps to the main checkout, and the main checkout's own
186
+ // subdirectories are untouched.
187
+ const execGit = deps.execGit || execGitDefault;
188
+ const top = execGit(['rev-parse', '--show-toplevel'], { cwd });
189
+ const topText = String(top.stdout || '').trim();
190
+ if (!top.timedOut && top.exitCode === 0 && topText !== '') {
191
+ const topLevel = node_path_1.default.resolve(cwd, topText);
192
+ if (existsSync(node_path_1.default.join(topLevel, '.planning'))) {
193
+ return {
194
+ effectiveRoot: topLevel,
195
+ mode: 'current_directory',
196
+ reason: 'has_local_planning',
197
+ };
198
+ }
199
+ }
200
+ return linkage;
180
201
  }
181
202
  function planWorktreePrune(repoRoot, options = {}, deps = {}) {
182
203
  const parsePorcelain = deps.parseWorktreePorcelain || parseWorktreePorcelain;
@@ -22,6 +22,7 @@
22
22
  "git": {
23
23
  "branching_strategy": "none",
24
24
  "create_tag": true,
25
+ "repo_tagging": "single",
25
26
  "base_branch": null,
26
27
  "phase_branch_template": "gsd/phase-{phase}-{slug}",
27
28
  "milestone_branch_template": "gsd/{milestone}-{slug}",
@@ -51,6 +51,7 @@
51
51
  "git.protected_branches",
52
52
  "git.allow_default_branch_commits",
53
53
  "git.create_tag",
54
+ "git.repo_tagging",
54
55
  "git.phase_branch_template",
55
56
  "git.milestone_branch_template",
56
57
  "git.quick_branch_template",