@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
@@ -19,6 +19,7 @@ exports.STATE_MD_SECTIONS = exports.FRONTMATTER_BODY_SOURCE = exports.FIELD_CLAS
19
19
  exports.formatProgressMachineSegment = formatProgressMachineSegment;
20
20
  exports.stateReplaceProgressPercent = stateReplaceProgressPercent;
21
21
  exports.beginFrontmatterReassembly = beginFrontmatterReassembly;
22
+ exports.isSessionScopedBodyField = isSessionScopedBodyField;
22
23
  exports.getFrontmatterBodySource = getFrontmatterBodySource;
23
24
  exports.frontmatterKeyForBodyField = frontmatterKeyForBodyField;
24
25
  exports.getFieldClassification = getFieldClassification;
@@ -30,13 +31,18 @@ exports.applyPreserveWhenUnchanged = applyPreserveWhenUnchanged;
30
31
  exports.applyStatePreservation = applyStatePreservation;
31
32
  exports.transitionCore = transitionCore;
32
33
  exports.sliceCurrentPositionSection = sliceCurrentPositionSection;
34
+ exports.isStatePastPhase = isStatePastPhase;
33
35
  // eslint-disable-next-line @typescript-eslint/no-require-imports
34
36
  const frontmatter = require("./frontmatter.cjs");
35
37
  const state_document_cjs_1 = require("./state-document.cjs");
36
38
  const state_document_cjs_2 = require("./state-document.cjs");
37
39
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
38
40
  const phase_lifecycle_cjs_1 = require("./phase-lifecycle.cjs");
41
+ const roadmap_progress_cjs_1 = require("./roadmap-progress.cjs");
39
42
  const pattern_cjs_1 = require("./pattern.cjs");
43
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-id.cjs is an export= CommonJS module
44
+ const phaseIdMod = require("./phase-id.cjs");
45
+ const { comparePhaseNum } = phaseIdMod;
40
46
  // #4129: the completion-ratio kernel for the resync-arm ratchet's percent
41
47
  // (planning-scope's SCOPE — state-document's own dependency, no cycle here:
42
48
  // state-document never imports this module).
@@ -246,6 +252,13 @@ exports.FRONTMATTER_BODY_SOURCE = Object.freeze(FRONTMATTER_BODY_SOURCE_KEY_ORDE
246
252
  * docstring). `updateCore` was still doing the whole-body replace.
247
253
  */
248
254
  const SESSION_SCOPED_KEYS = new Set(['stopped_at', 'paused_at']);
255
+ /**
256
+ * #4998 review: the `## Session` fields with no frontmatter key. `state-snapshot`
257
+ * reads them from the session section only, with fenced blocks blanked, so
258
+ * `state update` / `state patch` write them there too — never a fenced example
259
+ * above the live line, and never an archive section's copy.
260
+ */
261
+ const SESSION_BODY_FIELDS = new Set(['resume file', 'last session', 'last date']);
249
262
  /**
250
263
  * The `(primary, fallback)` label pair for a session-scoped frontmatter KEY.
251
264
  */
@@ -257,7 +270,8 @@ function sessionLabelsForKey(key) {
257
270
  }
258
271
  /**
259
272
  * The same pair, resolved from a BODY LABEL the caller named (`Stopped At`,
260
- * `Stopped at`, `Paused At`). `null` for anything else.
273
+ * `Stopped at`, `Paused At`, or a SESSION_BODY_FIELDS label as named). `null`
274
+ * for anything else.
261
275
  *
262
276
  * Deliberately does NOT accept a frontmatter key. An earlier cut resolved both
263
277
  * spellings through one function and used it for the write, which made
@@ -268,7 +282,13 @@ function sessionLabelsForKey(key) {
268
282
  */
269
283
  function sessionLabelsForBodyField(field) {
270
284
  const key = frontmatterKeyForBodyField(field);
271
- return key === null ? null : sessionLabelsForKey(key);
285
+ if (key !== null)
286
+ return sessionLabelsForKey(key);
287
+ return SESSION_BODY_FIELDS.has(field.trim().toLowerCase()) ? { primary: field, fallback: null } : null;
288
+ }
289
+ /** #4998 review: whether `state update` / `state patch` write the body field `field` inside the session section. */
290
+ function isSessionScopedBodyField(field) {
291
+ return sessionLabelsForBodyField(field) !== null;
272
292
  }
273
293
  /**
274
294
  * Would a session-scoped write actually land? Asks by attempting the real write
@@ -643,18 +663,24 @@ function mergeResyncProgressRatchet(curatedRecord, derivedRecord) {
643
663
  merged[key] = value;
644
664
  }
645
665
  }
646
- if ((0, state_document_cjs_2.toFiniteNumber)(derivedRecord.percent) !== null) {
647
- const recomputed = (0, state_document_cjs_2.computeProgressPercent)((0, state_document_cjs_2.toFiniteNumber)(merged.completed_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.total_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.completed_phases), (0, state_document_cjs_2.toFiniteNumber)(merged.total_phases), planningScopeMod.SCOPE.COMPLETE);
648
- // Same verbatim rule for percent: assign only when the recomputed value
649
- // numerically differs, so a string-spelled derived percent ("67") is not
650
- // churned into a number-spelled 67 (phantom-divergence noise, not a
651
- // change).
652
- if (recomputed !== null && recomputed !== (0, state_document_cjs_2.toFiniteNumber)(merged.percent)) {
653
- merged.percent = recomputed;
654
- }
655
- }
666
+ recomputeMergedProgressPercent(merged, derivedRecord);
656
667
  return merged;
657
668
  }
669
+ /**
670
+ * Make `merged.percent` the progress computation over the counters `merged` actually carries (#5619,
671
+ * #4129). Only when the derived side measured a percent: a derivation that withheld one (an incomplete
672
+ * scope, #1761/#3217) stays withheld, never invented. Same verbatim rule as the counters: assign only
673
+ * when the recomputed value numerically differs, so a string-spelled derived percent ("67") is not
674
+ * churned into a number-spelled 67 (phantom-divergence noise, not a change).
675
+ */
676
+ function recomputeMergedProgressPercent(merged, derivedRecord) {
677
+ if ((0, state_document_cjs_2.toFiniteNumber)(derivedRecord.percent) === null)
678
+ return;
679
+ const recomputed = (0, state_document_cjs_2.computeProgressPercent)((0, state_document_cjs_2.toFiniteNumber)(merged.completed_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.total_plans), (0, state_document_cjs_2.toFiniteNumber)(merged.completed_phases), (0, state_document_cjs_2.toFiniteNumber)(merged.total_phases), planningScopeMod.SCOPE.COMPLETE);
680
+ if (recomputed !== null && recomputed !== (0, state_document_cjs_2.toFiniteNumber)(merged.percent)) {
681
+ merged.percent = recomputed;
682
+ }
683
+ }
658
684
  /**
659
685
  * Executor for `preservation: 'preserve-always'` (ADR-3408 §8.1). Only
660
686
  * `progress` carries this policy today. Preserves #3242/#1446/#2440/#2969
@@ -719,18 +745,23 @@ function applyPreserveAlways(field, cls, ctx) {
719
745
  // completed after the plan count grew) — ratcheting UP only, never
720
746
  // deriving downward (preserves the #3242 curated-progress protection
721
747
  // for cases unrelated to plan-count growth, e.g. a deleted SUMMARY).
722
- // percent also takes the derived value — the resync recomputed it from
723
- // disk counts, and a stale curated percent would be incoherent against
724
- // the ratcheted-up completed counts (e.g. 54/54 at 93%).
748
+ // percent is recomputed over the counters actually written (#5619): the
749
+ // disk-derived percent is only coherent with the DERIVED counters, and a
750
+ // stale curated percent would be incoherent against the ratcheted-up
751
+ // completed counts (e.g. 54/54 at 93%).
725
752
  const ratchetUpKeys = new Set(['completed_plans', 'completed_phases']);
726
753
  for (const [key, value] of Object.entries(curatedRecord)) {
727
754
  if (key === 'total_plans' || key === 'total_phases' || key === 'percent')
728
755
  continue;
729
756
  if (ratchetUpKeys.has(key)) {
730
- const derivedNum = typeof derivedRecord[key] === 'number' ? derivedRecord[key] : -Infinity;
731
- const curatedNum = typeof value === 'number' ? value : -Infinity;
757
+ // Frontmatter scalars arrive as STRINGS ("2", not 2): coerce through
758
+ // `toFiniteNumber`, never a `typeof === 'number'` test (#5619 — that
759
+ // test made both sides -Infinity and the ratchet unreachable).
760
+ const derivedNum = (0, state_document_cjs_2.toFiniteNumber)(derivedRecord[key]) ?? -Infinity;
761
+ const curatedNum = (0, state_document_cjs_2.toFiniteNumber)(value) ?? -Infinity;
732
762
  // Take the derived value only when it ratchets up (strictly
733
- // greater — #2969's `>` not `>=`); else keep curated.
763
+ // greater — #2969's `>` not `>=`); else keep curated, with its
764
+ // spelling, on a tie too.
734
765
  if (derivedNum > curatedNum)
735
766
  continue;
736
767
  merged[key] = value;
@@ -740,6 +771,7 @@ function applyPreserveAlways(field, cls, ctx) {
740
771
  }
741
772
  }
742
773
  }
774
+ recomputeMergedProgressPercent(merged, derivedRecord);
743
775
  next = merged;
744
776
  }
745
777
  else {
@@ -876,7 +908,36 @@ exports.STATE_MD_SECTIONS = {
876
908
  * Phases 2–7 add cases for the remaining 9 intent kinds. A missing case is
877
909
  * a compile-time error (the function would not return on that path).
878
910
  */
911
+ /**
912
+ * The transitions that rewrite single-line body fields (`Status:`, `Current focus:`, `Plan:` …) with
913
+ * plain line-anchored patterns. Those patterns match a fenced EXAMPLE of a field as readily as the
914
+ * live line (#5460), so these run over the document body with fenced blocks hidden.
915
+ * `milestone*`, `prune` and `rebuild` restructure sections or re-derive the document and are not
916
+ * field-by-field writers.
917
+ */
918
+ const FENCE_MASKED_TRANSITIONS = new Set([
919
+ 'beginPhase', 'advancePlan', 'completePhase', 'plannedPhase', 'patch', 'update', 'sync',
920
+ ]);
879
921
  function transitionCore(content, intent, deps) {
922
+ if (!FENCE_MASKED_TRANSITIONS.has(intent.kind))
923
+ return transitionCoreUnmasked(content, intent, deps);
924
+ // Only the body is masked: a fence opener inside a frontmatter block scalar must not swallow the
925
+ // closing `---` (the frontmatter is not where the live fields are written).
926
+ const body = stripFrontmatter(content);
927
+ if (!content.endsWith(body))
928
+ return transitionCoreUnmasked(content, intent, deps);
929
+ const prefix = content.slice(0, content.length - body.length);
930
+ const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body);
931
+ const result = transitionCoreUnmasked(prefix + fenceMask.masked, intent, deps);
932
+ const restored = fenceMask.restore(result.content);
933
+ // Cannot happen short of a writer bug (a transition dropped or duplicated a placeholder line); a
934
+ // refusal is the one outcome that cannot report a rewrite that did not land.
935
+ if (restored === null) {
936
+ throw new Error(`${intent.kind}: fenced code blocks could not be restored after the edit; refusing to write STATE.md`);
937
+ }
938
+ return { ...result, content: restored };
939
+ }
940
+ function transitionCoreUnmasked(content, intent, deps) {
880
941
  switch (intent.kind) {
881
942
  case 'beginPhase':
882
943
  return beginPhaseCore(content, intent, deps);
@@ -980,7 +1041,7 @@ function beginPhaseCore(content, intent, deps) {
980
1041
  tryField('Current Phase Name', intent.phaseName);
981
1042
  }
982
1043
  tryField('Current Plan', String(startPlan));
983
- if (intent.planCount) {
1044
+ if (intent.planCount != null) {
984
1045
  tryField('Total Plans in Phase', String(intent.planCount));
985
1046
  }
986
1047
  // **Current focus:** body text line (#1104).
@@ -1104,7 +1165,9 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
1104
1165
  const span = locateCurrentPosition(body);
1105
1166
  if (span === null)
1106
1167
  return body;
1107
- let sectionBody = body.slice(span.start, span.end);
1168
+ // #5460: the section's fenced examples are hidden while the live lines are rewritten.
1169
+ const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body.slice(span.start, span.end));
1170
+ let sectionBody = fenceMask.masked;
1108
1171
  // Phase line — inline first, then pipe-table fallback (#1257).
1109
1172
  const phaseLabel = `${intent.phaseNumber}${intent.phaseName ? ` (${intent.phaseName})` : ''} — EXECUTING`;
1110
1173
  if (/^Phase:/m.test(sectionBody)) {
@@ -1116,7 +1179,7 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
1116
1179
  sectionBody = replaced;
1117
1180
  }
1118
1181
  // Plan line.
1119
- const planValue = `${intent.startPlan} of ${intent.planCount || '?'}`;
1182
+ const planValue = `${intent.startPlan} of ${intent.planCount ?? '?'}`;
1120
1183
  if (/^Plan:/m.test(sectionBody)) {
1121
1184
  sectionBody = sectionBody.replace(/^Plan:.*$/m, `Plan: ${planValue}`);
1122
1185
  }
@@ -1146,8 +1209,11 @@ function mutateCurrentPositionFirstTime(body, intent, today, updated) {
1146
1209
  if (replaced !== null)
1147
1210
  sectionBody = replaced;
1148
1211
  }
1212
+ const restored = fenceMask.restore(sectionBody);
1213
+ if (restored === null)
1214
+ return body;
1149
1215
  updated.push('Current Position');
1150
- return body.slice(0, span.start) + sectionBody + body.slice(span.end);
1216
+ return body.slice(0, span.start) + restored + body.slice(span.end);
1151
1217
  }
1152
1218
  /**
1153
1219
  * Resume ## Current Position mutation: only update Last activity line
@@ -1158,7 +1224,8 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
1158
1224
  const span = locateCurrentPosition(body);
1159
1225
  if (span === null)
1160
1226
  return body;
1161
- let sectionBody = body.slice(span.start, span.end);
1227
+ const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(body.slice(span.start, span.end));
1228
+ let sectionBody = fenceMask.masked;
1162
1229
  const resumeActivity = `Last activity: ${today} — Phase ${intent.phaseNumber} execution resumed (wave continue)`;
1163
1230
  if (/^Last activity:/im.test(sectionBody)) {
1164
1231
  sectionBody = sectionBody.replace(/^Last activity:.*$/im, resumeActivity);
@@ -1173,7 +1240,10 @@ function mutateCurrentPositionResume(body, intent, today, updated) {
1173
1240
  updated.push('Last activity (resume)');
1174
1241
  }
1175
1242
  }
1176
- return body.slice(0, span.start) + sectionBody + body.slice(span.end);
1243
+ const restored = fenceMask.restore(sectionBody);
1244
+ if (restored === null)
1245
+ return body;
1246
+ return body.slice(0, span.start) + restored + body.slice(span.end);
1177
1247
  }
1178
1248
  const PLAN_SHAPE_N = /^(\d+)(?:\s.*)?$/;
1179
1249
  const PLAN_SHAPE_N_OF_M = /^(\d+)\s+of\s+(\d+)(?:\s.*)?$/;
@@ -1230,7 +1300,8 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
1230
1300
  const span = locateCurrentPosition(content);
1231
1301
  if (span === null)
1232
1302
  return content;
1233
- let sectionBody = content.slice(span.start, span.end);
1303
+ const fenceMask = (0, markdown_sectionizer_cjs_1.maskFences)(content.slice(span.start, span.end));
1304
+ let sectionBody = fenceMask.masked;
1234
1305
  let mutated = false;
1235
1306
  // #3395: Phase is always replaced when a caller passes it — system-derived,
1236
1307
  // not executor-authored (same rule as Plan below). plannedPhaseCore uses
@@ -1343,7 +1414,10 @@ function mutateCurrentPositionForAdvance(content, fields, statusDefaults, lastAc
1343
1414
  }
1344
1415
  if (!mutated)
1345
1416
  return content;
1346
- return content.slice(0, span.start) + sectionBody + content.slice(span.end);
1417
+ const restored = fenceMask.restore(sectionBody);
1418
+ if (restored === null)
1419
+ return content;
1420
+ return content.slice(0, span.start) + restored + content.slice(span.end);
1347
1421
  }
1348
1422
  // ----------------------------------------------------------------------------
1349
1423
  // advancePlan — intent implementation (Phase 2)
@@ -1642,6 +1716,29 @@ function nextPhaseWording(disk) {
1642
1716
  return { status: 'Ready to plan', currentPlan: 'Not started', stoppedAt: 'ready to plan' };
1643
1717
  }
1644
1718
  }
1719
+ /**
1720
+ * #5165: is STATE.md already positioned PAST phase `phaseNum`? True when its
1721
+ * current phase (frontmatter `current_phase`, else the body `Current Phase` /
1722
+ * `Phase` field's leading phase token) compares greater than `phaseNum`.
1723
+ * Re-completing a phase STATE has already moved past must not rewind the
1724
+ * position; the caller combines this with "the phase is already complete in
1725
+ * ROADMAP.md" so a first-time out-of-order completion (#2028) still
1726
+ * transitions. An unparseable or absent current phase is "not past".
1727
+ */
1728
+ function isStatePastPhase(content, phaseNum, sourcePath) {
1729
+ const fm = extractFrontmatter(content, sourcePath);
1730
+ const fromFm = fm['current_phase'];
1731
+ let raw = typeof fromFm === 'string' || typeof fromFm === 'number' ? String(fromFm) : null;
1732
+ if (raw === null || raw.trim() === '') {
1733
+ const body = stripFrontmatter(content);
1734
+ raw = (0, state_document_cjs_1.stateExtractField)(body, 'Current Phase') || (0, state_document_cjs_1.stateExtractField)(body, 'Phase') || null;
1735
+ }
1736
+ // `Phase 4` / `phase 4 of 9` name phase 4 as surely as `4 of 9` does.
1737
+ const m = raw === null ? null : /^\s*(?:phase\s+)?(\d+(?:\.\d+)*)/i.exec(raw);
1738
+ if (!m)
1739
+ return false;
1740
+ return comparePhaseNum(m[1], String(phaseNum)) > 0;
1741
+ }
1645
1742
  /**
1646
1743
  * Apply a `completePhase` transition to STATE.md content.
1647
1744
  *
@@ -1669,6 +1766,12 @@ function completePhaseCore(content, intent, deps) {
1669
1766
  const updated = [];
1670
1767
  const today = deps.clock.localToday();
1671
1768
  const nextWording = nextPhaseWording(intent.isLastPhase ? null : intent.nextPhaseDisk ?? null);
1769
+ // #5165: the milestone the caller resolved (ROADMAP-bound identity first) wins;
1770
+ // else the milestone STATE asserts. It scopes the roadmap-derived counters
1771
+ // below to that milestone's Progress-table rows, the same milestone
1772
+ // `phase complete` scopes its frontmatter progress to.
1773
+ const currentMilestone = (0, roadmap_progress_cjs_1.milestoneVersionOf)(intent.milestone)
1774
+ ?? (0, roadmap_progress_cjs_1.milestoneVersionOf)(extractFrontmatter(content, deps.sourcePath)['milestone']);
1672
1775
  // Consult the field-classification table for the frontmatter keys this
1673
1776
  // transition touches (same guard beginPhaseCore applies). A missing row is a
1674
1777
  // substrate defect — fail loudly rather than silently re-encoding policy.
@@ -1812,7 +1915,7 @@ function completePhaseCore(content, intent, deps) {
1812
1915
  let derivedTotalPhases = null;
1813
1916
  const roadmapContent = deps.roadmapProvider ? deps.roadmapProvider() : null;
1814
1917
  if (roadmapContent) {
1815
- const derived = (0, phase_lifecycle_cjs_1.deriveProgressFromRoadmap)(roadmapContent);
1918
+ const derived = (0, phase_lifecycle_cjs_1.deriveProgressFromRoadmap)(roadmapContent, currentMilestone);
1816
1919
  // #4901: completed counters only ratchet up, like the frontmatter twin
1817
1920
  // (applyAuthoritativeProgressSubkeys, completedOnlyRaise). A derivation
1818
1921
  // that lands BELOW the recorded counter read a narrower scope than the
@@ -1933,8 +2036,16 @@ function plannedPhaseCore(content, intent, deps) {
1933
2036
  // Phase is system-derived, always replaced (Knuth invariant does not apply);
1934
2037
  // Status / Last activity stay template-aware.
1935
2038
  const beforePos = body;
2039
+ // #5369: with no --name the label reuses the curated frontmatter name (when it is a real one), so the
2040
+ // body line and the frontmatter agree on it (the #3834 reassert keeps that name in frontmatter). A
2041
+ // status fragment a pre-fix write left there is never copied back into the name slot.
2042
+ const curatedName = existingFm['current_phase_name'];
2043
+ const labelName = intent.phaseName
2044
+ ?? (typeof curatedName === 'string' && curatedName.trim() !== '' && !phaseIdMod.isPlannedPhaseStatusPhrase(curatedName)
2045
+ ? curatedName.trim()
2046
+ : null);
1936
2047
  body = mutateCurrentPositionForAdvance(body, {
1937
- phase: `${intent.phaseNumber}${intent.phaseName ? ` (${intent.phaseName})` : ''} — READY TO EXECUTE`,
2048
+ phase: `${intent.phaseNumber}${labelName ? ` (${labelName})` : ''} — READY TO EXECUTE`,
1938
2049
  status: 'Ready to execute',
1939
2050
  lastActivity: `${today} — Phase ${intent.phaseNumber} planning complete`,
1940
2051
  }, statusDefaults, lastActivityDefaults);
@@ -2255,7 +2366,20 @@ function patchCore(content, intent) {
2255
2366
  // `^field:` pattern — e.g. `status` matching both the frontmatter key
2256
2367
  // and a `Status:` body line). Frontmatter is only consulted when the
2257
2368
  // body has no match for this key.
2258
- const replaced = (0, state_document_cjs_1.stateReplaceField)(body, field, value);
2369
+ // #4998: a session field is written the way `state update` writes it —
2370
+ // inside the session section, never a fenced example there — so the
2371
+ // patch agrees with the session readers the frontmatter sync uses.
2372
+ const sessionWriteLabels = sessionLabelsForBodyField(field);
2373
+ let replaced;
2374
+ if (sessionWriteLabels) {
2375
+ const inSession = (0, state_document_cjs_1.stateReplaceFieldInSession)(body, sessionWriteLabels.primary, sessionWriteLabels.fallback, value);
2376
+ // Unchanged is a miss only when the field is absent: one already holding
2377
+ // the value matched (#4488), as stateReplaceField reports it.
2378
+ replaced = inSession !== body || sessionSourceExists(body, sessionWriteLabels) ? inSession : null;
2379
+ }
2380
+ else {
2381
+ replaced = (0, state_document_cjs_1.stateReplaceField)(body, field, value);
2382
+ }
2259
2383
  if (replaced !== null) {
2260
2384
  body = replaced;
2261
2385
  updated.push(field);
@@ -2321,10 +2445,10 @@ function updateCore(content, intent) {
2321
2445
  const sessionWriteLabels = sessionLabelsForBodyField(intent.field);
2322
2446
  let result;
2323
2447
  if (sessionWriteLabels) {
2324
- // Replace-only by contract: unchanged content means the field is not in the
2325
- // session section, which is a miss, not a write.
2448
+ // Replace-only by contract: the session writer never inserts a missing field.
2326
2449
  const replaced = (0, state_document_cjs_1.stateReplaceFieldInSession)(body, sessionWriteLabels.primary, sessionWriteLabels.fallback, intent.value);
2327
- result = replaced === body ? null : replaced;
2450
+ // A field already holding the value matched; unchanged with no field is the miss (#4488).
2451
+ result = replaced !== body || sessionSourceExists(body, sessionWriteLabels) ? replaced : null;
2328
2452
  }
2329
2453
  else {
2330
2454
  result = (0, state_document_cjs_1.stateReplaceField)(body, intent.field, intent.value);