@opengsd/gsd-path 1.0.0 → 1.1.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 (395) hide show
  1. package/AGENTS.md +28 -11
  2. package/DOCS.md +6 -5
  3. package/FULL.md +9 -13
  4. package/QUICK.md +3 -3
  5. package/README.md +151 -14
  6. package/RUNTIME.md +7 -1
  7. package/WORKFLOW.md +30 -14
  8. package/package.json +15 -1
  9. package/platforms/shared-agents/dispatch.md +6 -0
  10. package/scripts/archive_milestone.py +10 -118
  11. package/scripts/bootstrap_repository.py +25 -3
  12. package/scripts/build_recovery.py +287 -0
  13. package/scripts/check_docs_audit.py +5 -3
  14. package/scripts/check_handoffs.py +1 -1
  15. package/scripts/detect_project.py +11 -2
  16. package/scripts/dispatch_driver.py +4 -2
  17. package/scripts/git_guard.py +2 -1
  18. package/scripts/install.mjs +2 -0
  19. package/scripts/install.py +2 -0
  20. package/scripts/integration.py +28 -22
  21. package/scripts/isolation.py +9 -1
  22. package/scripts/lean_verification.py +1 -1
  23. package/scripts/pipeline_diagnose.py +12 -3
  24. package/scripts/pipeline_state.py +114 -186
  25. package/scripts/pipeline_undo.py +11 -7
  26. package/scripts/promote_lookahead.py +30 -162
  27. package/scripts/roadmap.py +321 -0
  28. package/scripts/skill-resources.json +79 -1
  29. package/scripts/state_checkpoint.py +109 -72
  30. package/scripts/state_promote.py +42 -44
  31. package/scripts/task_context.py +73 -0
  32. package/scripts/token_budget.py +40 -1
  33. package/scripts/workflow_run.py +116 -7
  34. package/skills/gsd-path/BUILD.md +47 -575
  35. package/skills/gsd-path/DECIDE.md +2 -6
  36. package/skills/gsd-path/DEFINE.md +10 -14
  37. package/skills/gsd-path/INSPECT.md +30 -8
  38. package/skills/gsd-path/PLAN.md +25 -17
  39. package/skills/gsd-path/RESEARCH.md +3 -6
  40. package/skills/gsd-path/ROADMAP.md +22 -58
  41. package/skills/gsd-path/SHIP.md +5 -4
  42. package/skills/gsd-path/SKILL.md +24 -14
  43. package/skills/gsd-path/references/build-abandon.md +33 -0
  44. package/skills/gsd-path/references/build-native.md +494 -0
  45. package/skills/gsd-path/references/build-recovery.md +78 -0
  46. package/skills/gsd-path/references/coder.md +8 -3
  47. package/skills/gsd-path/references/dispatch.md +6 -0
  48. package/skills/gsd-path/references/docs-auditor.md +14 -0
  49. package/skills/gsd-path/references/reviewer-final.md +21 -0
  50. package/skills/gsd-path/references/reviewer-panel.md +17 -0
  51. package/skills/gsd-path/references/reviewer-skeptic.md +20 -0
  52. package/skills/gsd-path/references/reviewer-wave.md +89 -0
  53. package/skills/gsd-path/references/reviewer.md +12 -150
  54. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  55. package/skills/gsd-path/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  56. package/skills/gsd-path/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  57. package/skills/gsd-path/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  58. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  59. package/skills/gsd-path/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  60. package/skills/gsd-path/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  61. package/skills/gsd-path/scripts/__pycache__/discussion_records.cpython-312.pyc +0 -0
  62. package/skills/gsd-path/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  63. package/skills/gsd-path/scripts/__pycache__/integration.cpython-312.pyc +0 -0
  64. package/skills/gsd-path/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  65. package/skills/gsd-path/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  66. package/skills/gsd-path/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  67. package/skills/gsd-path/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  68. package/skills/gsd-path/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  69. package/skills/gsd-path/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  70. package/skills/gsd-path/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  71. package/skills/gsd-path/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  72. package/skills/gsd-path/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  73. package/skills/gsd-path/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  74. package/skills/gsd-path/scripts/archive_milestone.py +10 -118
  75. package/skills/gsd-path/scripts/bootstrap_repository.py +25 -3
  76. package/skills/gsd-path/scripts/build_recovery.py +287 -0
  77. package/skills/gsd-path/scripts/check_docs_audit.py +5 -3
  78. package/skills/gsd-path/scripts/check_handoffs.py +1 -1
  79. package/skills/gsd-path/scripts/detect_project.py +11 -2
  80. package/skills/gsd-path/scripts/dispatch_driver.py +4 -2
  81. package/skills/gsd-path/scripts/integration.py +28 -22
  82. package/skills/gsd-path/scripts/isolation.py +9 -1
  83. package/skills/gsd-path/scripts/lean_verification.py +1 -1
  84. package/skills/gsd-path/scripts/pipeline_state.py +114 -186
  85. package/skills/gsd-path/scripts/promote_lookahead.py +30 -162
  86. package/skills/gsd-path/scripts/roadmap.py +321 -0
  87. package/skills/gsd-path/scripts/state_checkpoint.py +109 -72
  88. package/skills/gsd-path/scripts/state_promote.py +42 -44
  89. package/skills/gsd-path/scripts/task_context.py +73 -0
  90. package/skills/gsd-path/scripts/token_budget.py +40 -1
  91. package/skills/gsd-path/scripts/workflow_run.py +116 -7
  92. package/skills/gsd-path/templates/wave-review.md +1 -1
  93. package/skills/gsd-path-build/SKILL.md +47 -575
  94. package/skills/gsd-path-build/references/build-abandon.md +33 -0
  95. package/skills/gsd-path-build/references/build-native.md +494 -0
  96. package/skills/gsd-path-build/references/build-recovery.md +78 -0
  97. package/skills/gsd-path-build/references/coder.md +8 -3
  98. package/skills/gsd-path-build/references/dispatch.md +6 -0
  99. package/skills/gsd-path-build/references/reviewer-final.md +21 -0
  100. package/skills/gsd-path-build/references/reviewer-panel.md +17 -0
  101. package/skills/gsd-path-build/references/reviewer-skeptic.md +20 -0
  102. package/skills/gsd-path-build/references/reviewer-wave.md +89 -0
  103. package/skills/gsd-path-build/references/reviewer.md +12 -150
  104. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  105. package/skills/gsd-path-build/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  106. package/skills/gsd-path-build/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  107. package/skills/gsd-path-build/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  108. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  109. package/skills/gsd-path-build/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  110. package/skills/gsd-path-build/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  111. package/skills/gsd-path-build/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  112. package/skills/gsd-path-build/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  113. package/skills/gsd-path-build/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  114. package/skills/gsd-path-build/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  115. package/skills/gsd-path-build/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  116. package/skills/gsd-path-build/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  117. package/skills/gsd-path-build/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  118. package/skills/gsd-path-build/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  119. package/skills/gsd-path-build/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  120. package/skills/gsd-path-build/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  121. package/skills/gsd-path-build/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  122. package/skills/gsd-path-build/scripts/archive_milestone.py +10 -118
  123. package/skills/gsd-path-build/scripts/build_recovery.py +287 -0
  124. package/skills/gsd-path-build/scripts/check_handoffs.py +1 -1
  125. package/skills/gsd-path-build/scripts/detect_project.py +1526 -0
  126. package/skills/gsd-path-build/scripts/dispatch_driver.py +4 -2
  127. package/skills/gsd-path-build/scripts/integration.py +28 -22
  128. package/skills/gsd-path-build/scripts/isolation.py +9 -1
  129. package/skills/gsd-path-build/scripts/pipeline_state.py +114 -186
  130. package/skills/gsd-path-build/scripts/promote_lookahead.py +283 -0
  131. package/skills/gsd-path-build/scripts/roadmap.py +321 -0
  132. package/skills/gsd-path-build/scripts/state_checkpoint.py +109 -72
  133. package/skills/gsd-path-build/scripts/state_promote.py +42 -44
  134. package/skills/gsd-path-build/scripts/task_context.py +73 -0
  135. package/skills/gsd-path-build/scripts/token_budget.py +40 -1
  136. package/skills/gsd-path-build/scripts/workflow_run.py +116 -7
  137. package/skills/gsd-path-build/templates/wave-review.md +1 -1
  138. package/skills/gsd-path-decide/SKILL.md +2 -6
  139. package/skills/gsd-path-decide/references/dispatch.md +6 -0
  140. package/skills/gsd-path-decide/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  141. package/skills/gsd-path-decide/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  142. package/skills/gsd-path-decide/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  143. package/skills/gsd-path-decide/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  144. package/skills/gsd-path-decide/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  145. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  146. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  147. package/skills/gsd-path-decide/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  148. package/skills/gsd-path-decide/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  149. package/skills/gsd-path-decide/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  150. package/skills/gsd-path-decide/scripts/archive_milestone.py +2157 -0
  151. package/skills/gsd-path-decide/scripts/build_recovery.py +287 -0
  152. package/skills/gsd-path-decide/scripts/check_handoffs.py +1 -1
  153. package/skills/gsd-path-decide/scripts/detect_project.py +1526 -0
  154. package/skills/gsd-path-decide/scripts/discussion_validate.py +1321 -0
  155. package/skills/gsd-path-decide/scripts/integration.py +1439 -0
  156. package/skills/gsd-path-decide/scripts/isolation.py +9 -1
  157. package/skills/gsd-path-decide/scripts/pipeline_state.py +114 -186
  158. package/skills/gsd-path-decide/scripts/promote_lookahead.py +283 -0
  159. package/skills/gsd-path-decide/scripts/review_panel.py +553 -0
  160. package/skills/gsd-path-decide/scripts/roadmap.py +321 -0
  161. package/skills/gsd-path-decide/scripts/state_checkpoint.py +109 -72
  162. package/skills/gsd-path-decide/scripts/state_promote.py +42 -44
  163. package/skills/gsd-path-define/SKILL.md +10 -14
  164. package/skills/gsd-path-define/references/build-recovery.md +78 -0
  165. package/skills/gsd-path-define/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  166. package/skills/gsd-path-define/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  167. package/skills/gsd-path-define/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  168. package/skills/gsd-path-define/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  169. package/skills/gsd-path-define/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  170. package/skills/gsd-path-define/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  171. package/skills/gsd-path-define/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  172. package/skills/gsd-path-define/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  173. package/skills/gsd-path-define/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  174. package/skills/gsd-path-define/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  175. package/skills/gsd-path-define/scripts/archive_milestone.py +2157 -0
  176. package/skills/gsd-path-define/scripts/build_recovery.py +287 -0
  177. package/skills/gsd-path-define/scripts/check_handoffs.py +1 -1
  178. package/skills/gsd-path-define/scripts/detect_project.py +11 -2
  179. package/skills/gsd-path-define/scripts/discussion_validate.py +1321 -0
  180. package/skills/gsd-path-define/scripts/integration.py +1439 -0
  181. package/skills/gsd-path-define/scripts/isolation.py +9 -1
  182. package/skills/gsd-path-define/scripts/pipeline_state.py +114 -186
  183. package/skills/gsd-path-define/scripts/promote_lookahead.py +283 -0
  184. package/skills/gsd-path-define/scripts/review_panel.py +553 -0
  185. package/skills/gsd-path-define/scripts/roadmap.py +321 -0
  186. package/skills/gsd-path-define/scripts/state_checkpoint.py +109 -72
  187. package/skills/gsd-path-define/scripts/state_promote.py +42 -44
  188. package/skills/gsd-path-docs-audit/references/dispatch.md +6 -0
  189. package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
  190. package/skills/gsd-path-docs-audit/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  191. package/skills/gsd-path-docs-audit/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  192. package/skills/gsd-path-docs-audit/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  193. package/skills/gsd-path-docs-audit/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  194. package/skills/gsd-path-docs-audit/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  195. package/skills/gsd-path-docs-audit/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  196. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  197. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  198. package/skills/gsd-path-docs-audit/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  199. package/skills/gsd-path-docs-audit/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  200. package/skills/gsd-path-docs-audit/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  201. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2157 -0
  202. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +287 -0
  203. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +5 -3
  204. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +1 -1
  205. package/skills/gsd-path-docs-audit/scripts/detect_project.py +1526 -0
  206. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1321 -0
  207. package/skills/gsd-path-docs-audit/scripts/integration.py +1439 -0
  208. package/skills/gsd-path-docs-audit/scripts/isolation.py +9 -1
  209. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +114 -186
  210. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +283 -0
  211. package/skills/gsd-path-docs-audit/scripts/review_panel.py +553 -0
  212. package/skills/gsd-path-docs-audit/scripts/roadmap.py +321 -0
  213. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +109 -72
  214. package/skills/gsd-path-docs-audit/scripts/state_promote.py +42 -44
  215. package/skills/gsd-path-forensics/scripts/__pycache__/pipeline_undo.cpython-312.pyc +0 -0
  216. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +12 -3
  217. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +11 -7
  218. package/skills/gsd-path-inspect/SKILL.md +30 -8
  219. package/skills/gsd-path-inspect/references/dispatch.md +6 -0
  220. package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
  221. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  222. package/skills/gsd-path-inspect/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  223. package/skills/gsd-path-inspect/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  224. package/skills/gsd-path-inspect/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  225. package/skills/gsd-path-inspect/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  226. package/skills/gsd-path-inspect/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  227. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  228. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  229. package/skills/gsd-path-inspect/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  230. package/skills/gsd-path-inspect/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  231. package/skills/gsd-path-inspect/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  232. package/skills/gsd-path-inspect/scripts/archive_milestone.py +2157 -0
  233. package/skills/gsd-path-inspect/scripts/build_recovery.py +287 -0
  234. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +5 -3
  235. package/skills/gsd-path-inspect/scripts/check_handoffs.py +1 -1
  236. package/skills/gsd-path-inspect/scripts/detect_project.py +11 -2
  237. package/skills/gsd-path-inspect/scripts/discussion_validate.py +1321 -0
  238. package/skills/gsd-path-inspect/scripts/integration.py +1439 -0
  239. package/skills/gsd-path-inspect/scripts/isolation.py +9 -1
  240. package/skills/gsd-path-inspect/scripts/pipeline_state.py +114 -186
  241. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +283 -0
  242. package/skills/gsd-path-inspect/scripts/review_panel.py +553 -0
  243. package/skills/gsd-path-inspect/scripts/roadmap.py +321 -0
  244. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +109 -72
  245. package/skills/gsd-path-inspect/scripts/state_promote.py +42 -44
  246. package/skills/gsd-path-inspect/scripts/workflow_run.py +226 -0
  247. package/skills/gsd-path-plan/SKILL.md +25 -17
  248. package/skills/gsd-path-plan/references/build-recovery.md +78 -0
  249. package/skills/gsd-path-plan/references/dispatch.md +6 -0
  250. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  251. package/skills/gsd-path-plan/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  252. package/skills/gsd-path-plan/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  253. package/skills/gsd-path-plan/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  254. package/skills/gsd-path-plan/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  255. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  256. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  257. package/skills/gsd-path-plan/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  258. package/skills/gsd-path-plan/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  259. package/skills/gsd-path-plan/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  260. package/skills/gsd-path-plan/scripts/archive_milestone.py +2157 -0
  261. package/skills/gsd-path-plan/scripts/build_recovery.py +287 -0
  262. package/skills/gsd-path-plan/scripts/check_handoffs.py +1 -1
  263. package/skills/gsd-path-plan/scripts/detect_project.py +1526 -0
  264. package/skills/gsd-path-plan/scripts/discussion_validate.py +1321 -0
  265. package/skills/gsd-path-plan/scripts/integration.py +1439 -0
  266. package/skills/gsd-path-plan/scripts/isolation.py +9 -1
  267. package/skills/gsd-path-plan/scripts/pipeline_state.py +114 -186
  268. package/skills/gsd-path-plan/scripts/promote_lookahead.py +283 -0
  269. package/skills/gsd-path-plan/scripts/roadmap.py +321 -0
  270. package/skills/gsd-path-plan/scripts/state_checkpoint.py +109 -72
  271. package/skills/gsd-path-plan/scripts/state_promote.py +42 -44
  272. package/skills/gsd-path-plan/scripts/workflow_run.py +116 -7
  273. package/skills/gsd-path-research/SKILL.md +3 -6
  274. package/skills/gsd-path-research/references/dispatch.md +6 -0
  275. package/skills/gsd-path-research/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  276. package/skills/gsd-path-research/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  277. package/skills/gsd-path-research/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  278. package/skills/gsd-path-research/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  279. package/skills/gsd-path-research/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  280. package/skills/gsd-path-research/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  281. package/skills/gsd-path-research/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  282. package/skills/gsd-path-research/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  283. package/skills/gsd-path-research/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  284. package/skills/gsd-path-research/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  285. package/skills/gsd-path-research/scripts/archive_milestone.py +2157 -0
  286. package/skills/gsd-path-research/scripts/build_recovery.py +287 -0
  287. package/skills/gsd-path-research/scripts/check_handoffs.py +1 -1
  288. package/skills/gsd-path-research/scripts/detect_project.py +1526 -0
  289. package/skills/gsd-path-research/scripts/discussion_validate.py +1321 -0
  290. package/skills/gsd-path-research/scripts/integration.py +1439 -0
  291. package/skills/gsd-path-research/scripts/isolation.py +9 -1
  292. package/skills/gsd-path-research/scripts/pipeline_state.py +114 -186
  293. package/skills/gsd-path-research/scripts/promote_lookahead.py +283 -0
  294. package/skills/gsd-path-research/scripts/review_panel.py +553 -0
  295. package/skills/gsd-path-research/scripts/roadmap.py +321 -0
  296. package/skills/gsd-path-research/scripts/state_checkpoint.py +109 -72
  297. package/skills/gsd-path-research/scripts/state_promote.py +42 -44
  298. package/skills/gsd-path-roadmap/SKILL.md +22 -58
  299. package/skills/gsd-path-roadmap/references/dispatch.md +6 -0
  300. package/skills/gsd-path-roadmap/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  301. package/skills/gsd-path-roadmap/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  302. package/skills/gsd-path-roadmap/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  303. package/skills/gsd-path-roadmap/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  304. package/skills/gsd-path-roadmap/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  305. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  306. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  307. package/skills/gsd-path-roadmap/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  308. package/skills/gsd-path-roadmap/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  309. package/skills/gsd-path-roadmap/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  310. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +10 -118
  311. package/skills/gsd-path-roadmap/scripts/build_recovery.py +287 -0
  312. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +1 -1
  313. package/skills/gsd-path-roadmap/scripts/detect_project.py +11 -2
  314. package/skills/gsd-path-roadmap/scripts/integration.py +28 -22
  315. package/skills/gsd-path-roadmap/scripts/isolation.py +9 -1
  316. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +114 -186
  317. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +30 -162
  318. package/skills/gsd-path-roadmap/scripts/roadmap.py +321 -0
  319. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +109 -72
  320. package/skills/gsd-path-roadmap/scripts/state_promote.py +42 -44
  321. package/skills/gsd-path-ship/SKILL.md +5 -4
  322. package/skills/gsd-path-ship/references/dispatch.md +6 -0
  323. package/skills/gsd-path-ship/references/reviewer-final.md +21 -0
  324. package/skills/gsd-path-ship/references/reviewer-panel.md +17 -0
  325. package/skills/gsd-path-ship/references/reviewer-skeptic.md +20 -0
  326. package/skills/gsd-path-ship/references/reviewer-wave.md +89 -0
  327. package/skills/gsd-path-ship/references/reviewer.md +12 -150
  328. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  329. package/skills/gsd-path-ship/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  330. package/skills/gsd-path-ship/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  331. package/skills/gsd-path-ship/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  332. package/skills/gsd-path-ship/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  333. package/skills/gsd-path-ship/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  334. package/skills/gsd-path-ship/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  335. package/skills/gsd-path-ship/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  336. package/skills/gsd-path-ship/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  337. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  338. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  339. package/skills/gsd-path-ship/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  340. package/skills/gsd-path-ship/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  341. package/skills/gsd-path-ship/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  342. package/skills/gsd-path-ship/scripts/archive_milestone.py +10 -118
  343. package/skills/gsd-path-ship/scripts/build_recovery.py +287 -0
  344. package/skills/gsd-path-ship/scripts/check_handoffs.py +1 -1
  345. package/skills/gsd-path-ship/scripts/detect_project.py +1526 -0
  346. package/skills/gsd-path-ship/scripts/integration.py +28 -22
  347. package/skills/gsd-path-ship/scripts/isolation.py +9 -1
  348. package/skills/gsd-path-ship/scripts/lean_verification.py +1 -1
  349. package/skills/gsd-path-ship/scripts/pipeline_state.py +114 -186
  350. package/skills/gsd-path-ship/scripts/promote_lookahead.py +283 -0
  351. package/skills/gsd-path-ship/scripts/roadmap.py +321 -0
  352. package/skills/gsd-path-ship/scripts/state_checkpoint.py +109 -72
  353. package/skills/gsd-path-ship/scripts/state_promote.py +42 -44
  354. package/skills/gsd-path-ship/scripts/workflow_run.py +116 -7
  355. package/skills/gsd-path-ship/templates/wave-review.md +1 -1
  356. package/skills/gsd-path-undo/scripts/pipeline_undo.py +11 -7
  357. package/skills/path/BUILD.md +47 -575
  358. package/skills/path/DECIDE.md +2 -6
  359. package/skills/path/DEFINE.md +10 -14
  360. package/skills/path/INSPECT.md +30 -8
  361. package/skills/path/PLAN.md +25 -17
  362. package/skills/path/RESEARCH.md +3 -6
  363. package/skills/path/ROADMAP.md +22 -58
  364. package/skills/path/SHIP.md +5 -4
  365. package/skills/path/SKILL.md +24 -14
  366. package/skills/path/references/build-abandon.md +33 -0
  367. package/skills/path/references/build-native.md +494 -0
  368. package/skills/path/references/build-recovery.md +78 -0
  369. package/skills/path/references/coder.md +8 -3
  370. package/skills/path/references/dispatch.md +6 -0
  371. package/skills/path/references/docs-auditor.md +14 -0
  372. package/skills/path/references/reviewer-final.md +21 -0
  373. package/skills/path/references/reviewer-panel.md +17 -0
  374. package/skills/path/references/reviewer-skeptic.md +20 -0
  375. package/skills/path/references/reviewer-wave.md +89 -0
  376. package/skills/path/references/reviewer.md +12 -150
  377. package/skills/path/scripts/archive_milestone.py +10 -118
  378. package/skills/path/scripts/bootstrap_repository.py +25 -3
  379. package/skills/path/scripts/build_recovery.py +287 -0
  380. package/skills/path/scripts/check_docs_audit.py +5 -3
  381. package/skills/path/scripts/check_handoffs.py +1 -1
  382. package/skills/path/scripts/detect_project.py +11 -2
  383. package/skills/path/scripts/dispatch_driver.py +4 -2
  384. package/skills/path/scripts/integration.py +28 -22
  385. package/skills/path/scripts/isolation.py +9 -1
  386. package/skills/path/scripts/lean_verification.py +1 -1
  387. package/skills/path/scripts/pipeline_state.py +114 -186
  388. package/skills/path/scripts/promote_lookahead.py +30 -162
  389. package/skills/path/scripts/roadmap.py +321 -0
  390. package/skills/path/scripts/state_checkpoint.py +109 -72
  391. package/skills/path/scripts/state_promote.py +42 -44
  392. package/skills/path/scripts/task_context.py +73 -0
  393. package/skills/path/scripts/token_budget.py +40 -1
  394. package/skills/path/scripts/workflow_run.py +116 -7
  395. package/skills/path/templates/wave-review.md +1 -1
@@ -0,0 +1,321 @@
1
+ #!/usr/bin/env python3
2
+ # gsd-path project runtime
3
+ """Interpret Roadmap text for selection, approval, promotion and milestone close."""
4
+ from __future__ import annotations
5
+
6
+ import re
7
+ from typing import Optional
8
+
9
+
10
+ class RoadmapError(RuntimeError):
11
+ pass
12
+
13
+
14
+ class NoEligibleMilestone(RoadmapError):
15
+ pass
16
+
17
+
18
+ ROADMAP_HEADING_RE = re.compile(r"^### (M\d{3,}) — (.+)$")
19
+ STRICT_HEADING_RE = re.compile(r"^### (M\d{3,}) — ([a-z0-9][a-z0-9-]*)\s*$")
20
+ MUTABLE_ROADMAP_FIELDS_RE = re.compile(r"^(Status|Archive|Integrated):")
21
+
22
+ def normalized_slug(value: str) -> str:
23
+ slug = re.sub(r"[^a-z0-9]+", "-", value.lower()).strip("-")
24
+ return slug or "project"
25
+
26
+ def roadmap_blocks(content: str) -> list[dict]:
27
+ lines = content.splitlines(keepends=True)
28
+ headings = []
29
+ for index, line in enumerate(lines):
30
+ match = ROADMAP_HEADING_RE.fullmatch(line.rstrip("\r\n"))
31
+ if match:
32
+ headings.append((index, match.group(1), match.group(2).strip()))
33
+ blocks = []
34
+ for position, (start, milestone_id, title) in enumerate(headings):
35
+ end = headings[position + 1][0] if position + 1 < len(headings) else len(lines)
36
+ fields = {}
37
+ for index in range(start + 1, end):
38
+ match = re.match(
39
+ r"^(Status|Archive|Integrated|Depends on):\s*([^#]*?)\s*(?:#.*)?$",
40
+ lines[index].rstrip("\r\n"),
41
+ )
42
+ if match:
43
+ if match.group(1) in fields:
44
+ raise RoadmapError(
45
+ f"roadmap milestone {milestone_id} has duplicate "
46
+ f"{match.group(1)} field"
47
+ )
48
+ fields[match.group(1)] = (index, match.group(2).strip())
49
+ blocks.append(
50
+ {
51
+ "id": milestone_id,
52
+ "title": title,
53
+ "start": start,
54
+ "end": end,
55
+ "fields": fields,
56
+ }
57
+ )
58
+ return blocks
59
+
60
+
61
+ def milestone_block(content: str, milestone: str) -> dict:
62
+ normalized = normalized_slug(milestone)
63
+ matches = [
64
+ block
65
+ for block in roadmap_blocks(content)
66
+ if normalized_slug(block["title"]) == normalized
67
+ ]
68
+ if len(matches) != 1:
69
+ raise RoadmapError(f"ROADMAP.md must contain exactly one entry for {milestone}")
70
+ return matches[0]
71
+
72
+
73
+ def dependency_ids(block: dict) -> list[str]:
74
+ dependencies = block["fields"].get("Depends on")
75
+ if dependencies is None:
76
+ raise RoadmapError(f"roadmap milestone {block['id']} lacks dependencies")
77
+ dependency_list = re.fullmatch(
78
+ r"\[\s*(M\d{3,}(?:\s*,\s*M\d{3,})*)?\s*\]",
79
+ dependencies[1],
80
+ )
81
+ if dependency_list is None:
82
+ raise RoadmapError(f"roadmap milestone {block['id']} has invalid dependencies")
83
+ return re.findall(r"M\d{3,}", dependency_list.group(1) or "")
84
+
85
+
86
+ def next_eligible_pending(content: str, active_milestone: Optional[str] = None) -> dict:
87
+ blocks = roadmap_blocks(content)
88
+ blocks_by_id = {block["id"]: block for block in blocks}
89
+ if len(blocks_by_id) != len(blocks):
90
+ raise RoadmapError("ROADMAP.md milestone ids must be unique")
91
+ active_id = None
92
+ if active_milestone is not None:
93
+ active = milestone_block(content, active_milestone)
94
+ if active["fields"].get("Status", (-1, ""))[1] != "active":
95
+ raise RoadmapError(f"roadmap milestone {active_milestone} is not active")
96
+ active_id = active["id"]
97
+ for block in blocks:
98
+ if block["fields"].get("Status", (-1, ""))[1] != "pending":
99
+ continue
100
+ dependencies = dependency_ids(block)
101
+ if all(
102
+ dependency in blocks_by_id
103
+ and (
104
+ blocks_by_id[dependency]["fields"].get("Status", (-1, ""))[1]
105
+ == "shipped"
106
+ or dependency == active_id
107
+ )
108
+ for dependency in dependencies
109
+ ):
110
+ return block
111
+ raise NoEligibleMilestone("ROADMAP.md has no dependency-ready pending milestone")
112
+
113
+
114
+ def selection_payload(selected: dict) -> dict:
115
+ milestone = normalized_slug(selected["title"])
116
+ return {
117
+ "status": "selected",
118
+ "id": selected["id"],
119
+ "milestone": milestone,
120
+ "branch": f"gsd-path/{selected['id']}",
121
+ }
122
+
123
+
124
+ def select_lookahead(content: str, active_milestone: Optional[str] = None) -> dict:
125
+ try:
126
+ selected = next_eligible_pending(content, active_milestone)
127
+ except NoEligibleMilestone:
128
+ blocks = roadmap_blocks(content)
129
+ if blocks and all(
130
+ block["fields"].get("Status", (-1, ""))[1]
131
+ in {"shipped", "abandoned"}
132
+ for block in blocks
133
+ ):
134
+ return {"status": "complete"}
135
+ return {"status": "none"}
136
+ return selection_payload(selected)
137
+
138
+
139
+ def roadmap_contract(content: str, milestone: str) -> tuple[str, ...]:
140
+ block = milestone_block(content, milestone)
141
+ lines = content.splitlines()
142
+ return tuple(
143
+ line.rstrip()
144
+ for line in lines[block["start"] : block["end"]]
145
+ if not MUTABLE_ROADMAP_FIELDS_RE.match(line)
146
+ )
147
+
148
+
149
+ def compare_roadmap_entry(
150
+ before: str,
151
+ after: str,
152
+ milestone: str,
153
+ active_milestone: Optional[str] = None,
154
+ ) -> dict:
155
+ before_contract = roadmap_contract(before, milestone)
156
+ try:
157
+ after_contract = roadmap_contract(after, milestone)
158
+ selected = milestone_block(after, milestone)
159
+ eligible = next_eligible_pending(after, active_milestone)
160
+ except RoadmapError:
161
+ after_contract = ()
162
+ selected = None
163
+ eligible = None
164
+ status = (
165
+ "unchanged"
166
+ if before_contract == after_contract
167
+ and selected is not None
168
+ and eligible is not None
169
+ and selected["id"] == eligible["id"]
170
+ else "changed"
171
+ )
172
+ return {"status": status, "milestone": milestone}
173
+
174
+
175
+ def strict_sections(text: str) -> dict[str, tuple[str, int, int]]:
176
+ lines = text.splitlines(keepends=True)
177
+ headings: list[tuple[str, str, int]] = []
178
+ for index, line in enumerate(lines):
179
+ match = STRICT_HEADING_RE.fullmatch(line.rstrip("\r\n"))
180
+ if match:
181
+ if int(match.group(1).removeprefix("M")) < 1:
182
+ raise RoadmapError(f"ROADMAP.md has invalid milestone id: {match.group(1)}")
183
+ headings.append((match.group(1), match.group(2), index))
184
+ sections: dict[str, tuple[str, int, int]] = {}
185
+ for position, (milestone_id, slug, start) in enumerate(headings):
186
+ end = headings[position + 1][2] if position + 1 < len(headings) else len(lines)
187
+ if slug in sections:
188
+ raise RoadmapError(f"ROADMAP.md repeats milestone slug: {slug}")
189
+ sections[slug] = (milestone_id, start, end)
190
+ return sections
191
+
192
+
193
+ def replace_field(
194
+ lines: list[str],
195
+ start: int,
196
+ end: int,
197
+ field: str,
198
+ expected: set[str],
199
+ value: str,
200
+ ) -> None:
201
+ found: Optional[int] = None
202
+ for index in range(start, end):
203
+ raw = lines[index].rstrip("\r\n")
204
+ ending = lines[index][len(raw) :]
205
+ match = re.match(rf"^({re.escape(field)}:\s*)(\S+)(.*)$", raw)
206
+ if match is None:
207
+ continue
208
+ if found is not None:
209
+ raise RoadmapError(f"ROADMAP.md repeats {field} in one milestone")
210
+ if match.group(2) not in expected:
211
+ raise RoadmapError(
212
+ f"ROADMAP.md {field} is {match.group(2)}, expected {sorted(expected)}"
213
+ )
214
+ lines[index] = f"{match.group(1)}{value}{match.group(3)}{ending}"
215
+ found = index
216
+ if found is None:
217
+ raise RoadmapError(f"ROADMAP.md milestone is missing {field}")
218
+
219
+
220
+ def milestone_status(lines: list[str], start: int, end: int) -> str:
221
+ values = []
222
+ for line in lines[start:end]:
223
+ match = re.fullmatch(r"Status:\s*(\S+)\s*", line.rstrip("\r\n"))
224
+ if match:
225
+ values.append(match.group(1))
226
+ if len(values) != 1:
227
+ raise RoadmapError("ROADMAP.md milestone must have one Status field")
228
+ return values[0]
229
+
230
+
231
+ def activate_milestone(text: str, milestone: str) -> str:
232
+ sections = strict_sections(text)
233
+ if milestone not in sections:
234
+ raise RoadmapError(f"ROADMAP.md is missing selected milestone: {milestone}")
235
+ lines = text.splitlines(keepends=True)
236
+ active = [
237
+ slug
238
+ for slug, (_, start, end) in sections.items()
239
+ if milestone_status(lines, start, end) == "active"
240
+ ]
241
+ if active:
242
+ raise RoadmapError(
243
+ "ROADMAP.md already has an active milestone: " + ", ".join(active)
244
+ )
245
+ _, start, end = sections[milestone]
246
+ replace_field(lines, start, end, "Status", {"pending"}, "active")
247
+ return "".join(lines)
248
+
249
+
250
+ def reslice(before: str, candidate: str, phase: str, selected: str) -> str:
251
+ old_blocks = roadmap_blocks(before)
252
+ before_lines = before.splitlines(keepends=True)
253
+ candidate_lines = candidate.splitlines(keepends=True)
254
+ for block in old_blocks:
255
+ status = block["fields"].get("Status", (-1, ""))[1]
256
+ if status not in {"active", "shipped", "abandoned"}:
257
+ continue
258
+ slug = normalized_slug(block["title"])
259
+ new = milestone_block(candidate, slug)
260
+ if status == "shipped":
261
+ changed = roadmap_contract(before, slug) != roadmap_contract(candidate, slug)
262
+ else:
263
+ changed = before_lines[block["start"]:block["end"]] != candidate_lines[new["start"]:new["end"]]
264
+ if changed:
265
+ raise RoadmapError(f"{status} roadmap entry changed: {slug}")
266
+ if phase in {"inspect", "define"}:
267
+ active = [b for b in roadmap_blocks(candidate) if b["fields"].get("Status", (-1, ""))[1] == "active"]
268
+ if len(active) != 1 or selection_payload(active[0])["milestone"] != selected:
269
+ raise RoadmapError("roadmap re-slice must retain the active entry")
270
+ return candidate
271
+ if not any(b["fields"].get("Status", (-1, ""))[1] == "abandoned" for b in old_blocks):
272
+ raise RoadmapError("post-abandon re-slice requires an abandoned milestone")
273
+ selection = select_lookahead(candidate)
274
+ if selection.get("milestone") != selected:
275
+ raise RoadmapError("roadmap re-slice must use the selected dependency-ready milestone")
276
+ return activate_milestone(candidate, selected)
277
+
278
+
279
+ def has_questions(text: str, milestone: Optional[str] = None) -> bool:
280
+ """Report open questions for the active entry, or for ``milestone`` when given."""
281
+ lines = text.splitlines()
282
+ active_section: Optional[list[str]] = None
283
+ for index, line in enumerate(lines):
284
+ heading = STRICT_HEADING_RE.fullmatch(line)
285
+ if heading is None:
286
+ continue
287
+ if int(heading.group(1).removeprefix("M")) < 1:
288
+ raise RoadmapError(
289
+ f"ROADMAP.md has invalid milestone id: {heading.group(1)}"
290
+ )
291
+ end = next(
292
+ (
293
+ cursor
294
+ for cursor in range(index + 1, len(lines))
295
+ if STRICT_HEADING_RE.fullmatch(lines[cursor]) is not None
296
+ ),
297
+ len(lines),
298
+ )
299
+ section = lines[index:end]
300
+ if milestone is not None:
301
+ if heading.group(2) == milestone:
302
+ active_section = section
303
+ continue
304
+ if any(re.fullmatch(r"Status:\s*active(?:\s+#.*)?", item) for item in section):
305
+ if active_section is not None:
306
+ raise RoadmapError("ROADMAP.md has more than one active milestone")
307
+ active_section = section
308
+ if active_section is None:
309
+ if milestone is not None:
310
+ raise RoadmapError(f"ROADMAP.md is missing lookahead milestone: {milestone}")
311
+ raise RoadmapError("ROADMAP.md has no active milestone")
312
+ try:
313
+ start = active_section.index("Open questions") + 1
314
+ except ValueError as error:
315
+ raise RoadmapError("active roadmap milestone has no Open questions") from error
316
+ questions = [
317
+ line[2:].strip()
318
+ for line in active_section[start:]
319
+ if line.startswith("- ")
320
+ ]
321
+ return any(question.lower() != "none" for question in questions)
@@ -20,6 +20,43 @@ except ModuleNotFoundError as error: # pragma: no cover - package imports used
20
20
  from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint, require_commit, require_full_sha, PROJECT_ENTRIES
21
21
 
22
22
 
23
+ if __package__:
24
+ from . import roadmap
25
+ else:
26
+ import roadmap
27
+
28
+ if __package__: # imported as scripts.state_checkpoint
29
+ from . import pipeline_state
30
+ from .pipeline_state import (
31
+ CHECKPOINT_JOURNAL_NAME,
32
+ CHECKPOINT_KINDS,
33
+ CHECKPOINT_SCHEMA,
34
+ PLAN_APPROVAL_SUBJECT,
35
+ PROMOTION_TRACKS,
36
+ SLUG_RE,
37
+ TASK_FILE_RE,
38
+ TASK_ID_RE,
39
+ ApprovedTaskContract,
40
+ PipelineState,
41
+ PipelineStateError,
42
+ )
43
+ else: # standalone script or sibling import
44
+ import pipeline_state
45
+ from pipeline_state import (
46
+ CHECKPOINT_JOURNAL_NAME,
47
+ CHECKPOINT_KINDS,
48
+ CHECKPOINT_SCHEMA,
49
+ PLAN_APPROVAL_SUBJECT,
50
+ PROMOTION_TRACKS,
51
+ SLUG_RE,
52
+ TASK_FILE_RE,
53
+ TASK_ID_RE,
54
+ ApprovedTaskContract,
55
+ PipelineState,
56
+ PipelineStateError,
57
+ )
58
+
59
+
23
60
  def _sha256(content: str) -> str:
24
61
  return hashlib.sha256(content.encode("utf-8")).hexdigest()
25
62
 
@@ -65,41 +102,29 @@ def _checkpoint_artifact_digest(project: Path, mutable_paths: set[str]) -> str:
65
102
  return digest.hexdigest()
66
103
 
67
104
 
68
- def _roadmap_status(lines: list[str], start: int, end: int) -> str:
69
- values = []
70
- for line in lines[start:end]:
71
- match = re.fullmatch(r"Status:\s*(\S+)\s*", line.rstrip("\r\n"))
72
- if match:
73
- values.append(match.group(1))
74
- if len(values) != 1:
75
- raise PipelineStateError("ROADMAP.md milestone must have one Status field")
76
- return values[0]
77
-
78
-
79
105
  def _activate_roadmap_milestone(text: str, milestone: str) -> str:
80
- sections = pipeline_state._roadmap_sections(text)
81
- if milestone not in sections:
82
- raise PipelineStateError(f"ROADMAP.md is missing selected milestone: {milestone}")
83
- lines = text.splitlines(keepends=True)
84
- active = [
85
- slug
86
- for slug, (_, start, end) in sections.items()
87
- if _roadmap_status(lines, start, end) == "active"
88
- ]
89
- if active:
90
- raise PipelineStateError(
91
- "ROADMAP.md already has an active milestone: " + ", ".join(active)
92
- )
93
- _, start, end = sections[milestone]
94
- pipeline_state._replace_roadmap_field(lines, start, end, "Status", {"pending"}, "active")
95
- return "".join(lines)
96
-
106
+ try:
107
+ return roadmap.activate_milestone(text, milestone)
108
+ except roadmap.RoadmapError as error:
109
+ raise PipelineStateError(str(error)) from error
97
110
 
98
111
  def _approval_details(
99
112
  kind: str,
100
113
  state: PipelineState,
101
114
  selected_milestone: Optional[str],
102
115
  ) -> tuple[dict[str, Optional[str]], str, str, str]:
116
+ if kind == "roadmap-reslice":
117
+ if state.status != "active" or state.archive is not None:
118
+ raise PipelineStateError("roadmap re-slice requires active, unarchived state")
119
+ if state.phase in {"inspect", "define"} and selected_milestone == state.milestone and state.milestone:
120
+ changes = {}
121
+ event = "program roadmap re-slice approved"
122
+ elif state.phase == "roadmap" and state.milestone is None and selected_milestone and SLUG_RE.fullmatch(selected_milestone):
123
+ changes = {"phase": "inspect", "milestone": selected_milestone}
124
+ event = "roadmap approved after abandoned milestone"
125
+ else:
126
+ raise PipelineStateError("roadmap re-slice must preserve the active milestone or select after abandon")
127
+ return changes, event, "roadmap: program roadmap approved", "Why: approved roadmap checkpoint"
103
128
  if kind == "plan":
104
129
  if selected_milestone is not None:
105
130
  raise PipelineStateError("plan approval does not accept a selected milestone")
@@ -126,6 +151,12 @@ def _approval_details(
126
151
  raise PipelineStateError(f"unsupported approval kind: {kind}")
127
152
 
128
153
 
154
+ def _reslice_roadmap(before: str, candidate: str, state: PipelineState, selected: str) -> str:
155
+ try:
156
+ return roadmap.reslice(before, candidate, state.phase, selected)
157
+ except roadmap.RoadmapError as error:
158
+ raise PipelineStateError(str(error)) from error
159
+
129
160
  def _checkpoint_request_fields(
130
161
  kind: str,
131
162
  project_dir: str,
@@ -175,6 +206,10 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
175
206
  "roadmap_before",
176
207
  "roadmap_after",
177
208
  }
209
+ if journal.get("kind") == "roadmap-reslice":
210
+ expected_keys.add("baseline_before")
211
+ if not isinstance(journal.get("baseline_before"), str):
212
+ raise PipelineStateError("checkpoint journal has an invalid re-slice baseline")
178
213
  if set(journal) != expected_keys:
179
214
  raise PipelineStateError("checkpoint journal has invalid fields")
180
215
  if journal.get("schema") != CHECKPOINT_SCHEMA or journal.get("repo") != str(repo):
@@ -253,12 +288,14 @@ def _approval_journal(repo: Path, journal: Mapping[str, object]) -> dict[str, ob
253
288
  or journal["state_after"] != rendered
254
289
  ):
255
290
  raise PipelineStateError("checkpoint journal approval contract is invalid")
256
- if str(journal["kind"]) == "roadmap":
291
+ if journal["kind"] in {"roadmap", "roadmap-reslice"}:
257
292
  if project_dir != ".project":
258
293
  raise PipelineStateError("roadmap approval is legal only on the active track")
259
294
  if journal["roadmap_path"] != ".project/ROADMAP.md":
260
295
  raise PipelineStateError("checkpoint journal has an invalid ROADMAP path")
261
- expected_roadmap = _activate_roadmap_milestone(
296
+ expected_roadmap = _reslice_roadmap(
297
+ str(journal["baseline_before"]), str(journal["roadmap_before"]), before, str(selected),
298
+ ) if journal["kind"] == "roadmap-reslice" else _activate_roadmap_milestone(
262
299
  str(journal["roadmap_before"]),
263
300
  str(selected),
264
301
  )
@@ -307,6 +344,12 @@ def _resume_checkpoint_locked(
307
344
  transaction = _approval_journal(repo, journal)
308
345
  state_path = _checkpoint_file(repo, str(transaction["state_path"]), "STATE.md")
309
346
  mutable = {state_path.relative_to(project).as_posix()}
347
+ baseline = project / "ROADMAP.before-reslice.md" if transaction["kind"] == "roadmap-reslice" else None
348
+ if baseline is not None:
349
+ mutable.add(baseline.name)
350
+ if baseline.exists() or baseline.is_symlink():
351
+ if pipeline_state._read_real_file(baseline, "re-slice baseline") != transaction["baseline_before"]:
352
+ raise PipelineStateError("re-slice baseline drifted during checkpoint recovery")
310
353
  roadmap_path: Optional[Path] = None
311
354
  if transaction["roadmap_path"] is not None:
312
355
  roadmap_path = _checkpoint_file(
@@ -354,6 +397,8 @@ def _resume_checkpoint_locked(
354
397
  raise PipelineStateError("completed checkpoint has the wrong ROADMAP.md")
355
398
 
356
399
  try:
400
+ if baseline is not None and baseline.exists():
401
+ baseline.unlink()
357
402
  committed = isolation_checkpoint(
358
403
  repo,
359
404
  expected_head,
@@ -362,6 +407,8 @@ def _resume_checkpoint_locked(
362
407
  [".project"],
363
408
  )
364
409
  except IsolationError as error:
410
+ if baseline is not None and pipeline_state._run_git(repo, "rev-parse", "HEAD").stdout.strip() == expected_head:
411
+ pipeline_state._atomic_write(baseline, str(transaction["baseline_before"]))
365
412
  raise PipelineStateError(f"approval checkpoint failed: {error}") from error
366
413
  if committed.get("commit") != pipeline_state._run_git(repo, "rev-parse", "HEAD").stdout.strip():
367
414
  raise PipelineStateError("approval checkpoint returned the wrong commit")
@@ -381,6 +428,8 @@ def _resume_checkpoint_locked(
381
428
  def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
382
429
  if kind != "plan":
383
430
  return
431
+ if project_dir == ".project":
432
+ pipeline_state._build_recovery().validate_plan(repo)
384
433
  head = pipeline_state._run_git(repo, "rev-parse", "--verify", "HEAD", check=False)
385
434
  if head.returncode != 0:
386
435
  # Pre-Git approval defers base-dependent checks to build.
@@ -476,6 +525,8 @@ def checkpoint_approval(
476
525
  state, state_before, state_path = pipeline_state.load_state(resolved, project_dir)
477
526
  changes, event, subject, body = _approval_details(kind, state, selected_milestone)
478
527
  _validate_plan_briefs(resolved, kind, project_dir)
528
+ if kind == "plan" and project_dir == ".project":
529
+ pipeline_state._build_recovery().restore_unchanged_reviews(resolved)
479
530
  expected = {
480
531
  "phase": state.phase,
481
532
  "status": state.status,
@@ -497,13 +548,18 @@ def checkpoint_approval(
497
548
  roadmap_path: Optional[Path] = None
498
549
  roadmap_before: Optional[str] = None
499
550
  roadmap_after: Optional[str] = None
500
- if kind == "roadmap":
551
+ baseline_before = None
552
+ if kind in {"roadmap", "roadmap-reslice"}:
501
553
  if project_dir != ".project":
502
554
  raise PipelineStateError("roadmap approval is legal only on the active track")
503
555
  assert selected_milestone is not None
504
556
  roadmap_path = project / "ROADMAP.md"
505
557
  roadmap_before = pipeline_state._read_real_file(roadmap_path, "ROADMAP.md")
506
- roadmap_after = _activate_roadmap_milestone(
558
+ if kind == "roadmap-reslice":
559
+ baseline_before = pipeline_state._read_real_file(project / "ROADMAP.before-reslice.md", "re-slice baseline")
560
+ roadmap_after = _reslice_roadmap(
561
+ baseline_before, roadmap_before, state, selected_milestone,
562
+ ) if kind == "roadmap-reslice" else _activate_roadmap_milestone(
507
563
  roadmap_before,
508
564
  selected_milestone,
509
565
  )
@@ -512,6 +568,8 @@ def checkpoint_approval(
512
568
  mutable = {state_relative}
513
569
  if roadmap_path is not None:
514
570
  mutable.add(roadmap_path.relative_to(project).as_posix())
571
+ if baseline_before is not None:
572
+ mutable.add("ROADMAP.before-reslice.md")
515
573
  journal: dict[str, object] = {
516
574
  "schema": CHECKPOINT_SCHEMA,
517
575
  "repo": str(resolved),
@@ -528,6 +586,8 @@ def checkpoint_approval(
528
586
  "roadmap_before": roadmap_before,
529
587
  "roadmap_after": roadmap_after,
530
588
  }
589
+ if baseline_before is not None:
590
+ journal["baseline_before"] = baseline_before
531
591
  pipeline_state._write_json(journal_path, journal)
532
592
  return _resume_checkpoint_locked(resolved, project, journal_path, journal)
533
593
 
@@ -576,6 +636,10 @@ def defer_approval(
576
636
  with pipeline_state._state_lock(project):
577
637
  state, state_before, state_path = pipeline_state.load_state(resolved, project_dir)
578
638
  changes, event, _, _ = _approval_details(kind, state, selected_milestone)
639
+ if project_dir == ".project":
640
+ recovery = pipeline_state._build_recovery().context(resolved, state_before)
641
+ if recovery and recovery["active"]:
642
+ raise PipelineStateError("build recovery requires a plan checkpoint")
579
643
  _validate_plan_briefs(resolved, kind, project_dir)
580
644
  if patch:
581
645
  event = "patch plan approved"
@@ -595,17 +659,25 @@ def defer_approval(
595
659
  project_dir,
596
660
  approval_kind=kind,
597
661
  )
598
- if kind == "roadmap":
662
+ baseline = None
663
+ if kind in {"roadmap", "roadmap-reslice"}:
599
664
  if project_dir != ".project":
600
665
  raise PipelineStateError("roadmap approval is legal only on the active track")
601
666
  assert selected_milestone is not None
602
667
  roadmap_path = project / "ROADMAP.md"
603
- roadmap_after = _activate_roadmap_milestone(
604
- pipeline_state._read_real_file(roadmap_path, "ROADMAP.md"),
605
- selected_milestone,
606
- )
668
+ roadmap_before = pipeline_state._read_real_file(roadmap_path, "ROADMAP.md")
669
+ if kind == "roadmap-reslice":
670
+ baseline = project / "ROADMAP.before-reslice.md"
671
+ roadmap_after = _reslice_roadmap(
672
+ pipeline_state._read_real_file(baseline, "re-slice baseline"),
673
+ roadmap_before, state, selected_milestone,
674
+ )
675
+ else:
676
+ roadmap_after = _activate_roadmap_milestone(roadmap_before, selected_milestone)
607
677
  pipeline_state._atomic_write(roadmap_path, roadmap_after)
608
678
  pipeline_state._atomic_write(state_path, state_after)
679
+ if baseline is not None:
680
+ baseline.unlink()
609
681
  return {
610
682
  "schema": CHECKPOINT_SCHEMA,
611
683
  "status": "approved",
@@ -1002,38 +1074,3 @@ def _classify_plan_drift(
1002
1074
  "contract_paths": sorted(contract_paths),
1003
1075
  "reason": "; ".join(reasons) if reasons else "approval contracts and declared paths are unchanged",
1004
1076
  }
1005
-
1006
-
1007
- # pipeline_state imports this module, so the parent is resolved after the
1008
- # definitions above. Parent functions are looked up on the module at call time
1009
- # so patches applied to pipeline_state stay visible here.
1010
- if __package__: # imported as scripts.state_checkpoint
1011
- from . import pipeline_state
1012
- from .pipeline_state import (
1013
- CHECKPOINT_JOURNAL_NAME,
1014
- CHECKPOINT_KINDS,
1015
- CHECKPOINT_SCHEMA,
1016
- PLAN_APPROVAL_SUBJECT,
1017
- PROMOTION_TRACKS,
1018
- SLUG_RE,
1019
- TASK_FILE_RE,
1020
- TASK_ID_RE,
1021
- ApprovedTaskContract,
1022
- PipelineState,
1023
- PipelineStateError,
1024
- )
1025
- else: # standalone script or sibling import
1026
- import pipeline_state
1027
- from pipeline_state import (
1028
- CHECKPOINT_JOURNAL_NAME,
1029
- CHECKPOINT_KINDS,
1030
- CHECKPOINT_SCHEMA,
1031
- PLAN_APPROVAL_SUBJECT,
1032
- PROMOTION_TRACKS,
1033
- SLUG_RE,
1034
- TASK_FILE_RE,
1035
- TASK_ID_RE,
1036
- ApprovedTaskContract,
1037
- PipelineState,
1038
- PipelineStateError,
1039
- )