@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
@@ -27,18 +27,20 @@ def _load_pipeline_modules():
27
27
  import isolation
28
28
  import pipeline_git
29
29
  import pipeline_state
30
- return archive_milestone, isolation, pipeline_git, pipeline_state
30
+ import state_checkpoint
31
+ return archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
31
32
  except ModuleNotFoundError as error:
32
33
  if error.name not in {
33
34
  "archive_milestone",
34
35
  "isolation",
35
36
  "pipeline_git",
36
37
  "pipeline_state",
38
+ "state_checkpoint",
37
39
  }:
38
40
  raise
39
41
  try:
40
- from scripts import archive_milestone, isolation, pipeline_git, pipeline_state
41
- return archive_milestone, isolation, pipeline_git, pipeline_state
42
+ from scripts import archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
43
+ return archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
42
44
  except ModuleNotFoundError as error:
43
45
  if error.name not in {
44
46
  "scripts",
@@ -46,6 +48,7 @@ def _load_pipeline_modules():
46
48
  "scripts.isolation",
47
49
  "scripts.pipeline_git",
48
50
  "scripts.pipeline_state",
51
+ "scripts.state_checkpoint",
49
52
  }:
50
53
  raise
51
54
  shared = Path(__file__).resolve().parents[2] / "gsd-path" / "scripts"
@@ -54,16 +57,17 @@ def _load_pipeline_modules():
54
57
  import isolation
55
58
  import pipeline_git
56
59
  import pipeline_state
57
- return archive_milestone, isolation, pipeline_git, pipeline_state
60
+ import state_checkpoint
61
+ return archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
58
62
 
59
63
 
60
- archive_milestone, isolation, pipeline_git, pipeline_state = _load_pipeline_modules()
64
+ archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint = _load_pipeline_modules()
61
65
  ArchiveError = archive_milestone.ArchiveError
62
66
  IsolationError = isolation.IsolationError
63
67
  PLAN_APPROVAL_SUBJECT = pipeline_state.PLAN_APPROVAL_SUBJECT
64
68
  PipelineStateError = pipeline_state.PipelineStateError
65
- _activate_roadmap_milestone = pipeline_state._activate_roadmap_milestone
66
- _approval_details = pipeline_state._approval_details
69
+ _activate_roadmap_milestone = state_checkpoint._activate_roadmap_milestone
70
+ _approval_details = state_checkpoint._approval_details
67
71
  _commit_subject_body = pipeline_state._commit_subject_body
68
72
  _optional_rev = pipeline_state._optional_rev
69
73
  _render_transition = pipeline_state._render_transition
@@ -15,24 +15,41 @@ import sys
15
15
  from pathlib import Path
16
16
  from typing import Optional, Sequence
17
17
 
18
- import _common
19
- import archive_milestone
20
- import detect_project
18
+ if __package__:
19
+ from . import _common, archive_milestone, detect_project
20
+ from .roadmap import (
21
+ RoadmapError as LookaheadError,
22
+ NoEligibleMilestone as NoEligibleLookahead,
23
+ roadmap_blocks,
24
+ milestone_block,
25
+ dependency_ids,
26
+ next_eligible_pending,
27
+ selection_payload,
28
+ select_lookahead,
29
+ roadmap_contract,
30
+ compare_roadmap_entry,
31
+ )
32
+ else:
33
+ from roadmap import (
34
+ RoadmapError as LookaheadError,
35
+ NoEligibleMilestone as NoEligibleLookahead,
36
+ roadmap_blocks,
37
+ milestone_block,
38
+ dependency_ids,
39
+ next_eligible_pending,
40
+ selection_payload,
41
+ select_lookahead,
42
+ roadmap_contract,
43
+ compare_roadmap_entry,
44
+ )
45
+ import _common
46
+ import archive_milestone
47
+ import detect_project
21
48
 
22
49
 
23
50
  PHASES = {"inspect", "define", "research", "decide", "plan"}
24
51
  STATUSES = {"active", "blocked", "done"}
25
52
  SHA_RE = re.compile(r"^[0-9a-f]{40}$")
26
- ROADMAP_HEADING_RE = re.compile(r"^### (M\d{3,}) — (.+)$")
27
- MUTABLE_ROADMAP_FIELDS_RE = re.compile(r"^(Status|Archive|Integrated):")
28
-
29
-
30
- class LookaheadError(RuntimeError):
31
- pass
32
-
33
-
34
- class NoEligibleLookahead(LookaheadError):
35
- pass
36
53
 
37
54
 
38
55
  def read_text(path: Path, description: str) -> str:
@@ -76,119 +93,6 @@ def state_fields(content: str) -> dict[str, str]:
76
93
  return fields
77
94
 
78
95
 
79
- def roadmap_blocks(content: str) -> list[dict]:
80
- lines = content.splitlines(keepends=True)
81
- headings = []
82
- for index, line in enumerate(lines):
83
- match = ROADMAP_HEADING_RE.fullmatch(line.rstrip("\r\n"))
84
- if match:
85
- headings.append((index, match.group(1), match.group(2).strip()))
86
- blocks = []
87
- for position, (start, milestone_id, title) in enumerate(headings):
88
- end = headings[position + 1][0] if position + 1 < len(headings) else len(lines)
89
- fields = {}
90
- for index in range(start + 1, end):
91
- match = re.match(
92
- r"^(Status|Archive|Integrated|Depends on):\s*([^#]*?)\s*(?:#.*)?$",
93
- lines[index].rstrip("\r\n"),
94
- )
95
- if match:
96
- if match.group(1) in fields:
97
- raise LookaheadError(
98
- f"roadmap milestone {milestone_id} has duplicate "
99
- f"{match.group(1)} field"
100
- )
101
- fields[match.group(1)] = (index, match.group(2).strip())
102
- blocks.append(
103
- {
104
- "id": milestone_id,
105
- "title": title,
106
- "start": start,
107
- "end": end,
108
- "fields": fields,
109
- }
110
- )
111
- return blocks
112
-
113
-
114
- def milestone_block(content: str, milestone: str) -> dict:
115
- normalized = archive_milestone.normalized_slug(milestone)
116
- matches = [
117
- block
118
- for block in roadmap_blocks(content)
119
- if archive_milestone.normalized_slug(block["title"]) == normalized
120
- ]
121
- if len(matches) != 1:
122
- raise LookaheadError(f"ROADMAP.md must contain exactly one entry for {milestone}")
123
- return matches[0]
124
-
125
-
126
- def dependency_ids(block: dict) -> list[str]:
127
- dependencies = block["fields"].get("Depends on")
128
- if dependencies is None:
129
- raise LookaheadError(f"roadmap milestone {block['id']} lacks dependencies")
130
- dependency_list = re.fullmatch(
131
- r"\[\s*(M\d{3,}(?:\s*,\s*M\d{3,})*)?\s*\]",
132
- dependencies[1],
133
- )
134
- if dependency_list is None:
135
- raise LookaheadError(f"roadmap milestone {block['id']} has invalid dependencies")
136
- return re.findall(r"M\d{3,}", dependency_list.group(1) or "")
137
-
138
-
139
- def next_eligible_pending(content: str, active_milestone: Optional[str] = None) -> dict:
140
- blocks = roadmap_blocks(content)
141
- blocks_by_id = {block["id"]: block for block in blocks}
142
- if len(blocks_by_id) != len(blocks):
143
- raise LookaheadError("ROADMAP.md milestone ids must be unique")
144
- active_id = None
145
- if active_milestone is not None:
146
- active = milestone_block(content, active_milestone)
147
- if active["fields"].get("Status", (-1, ""))[1] != "active":
148
- raise LookaheadError(f"roadmap milestone {active_milestone} is not active")
149
- active_id = active["id"]
150
- for block in blocks:
151
- if block["fields"].get("Status", (-1, ""))[1] != "pending":
152
- continue
153
- dependencies = dependency_ids(block)
154
- if all(
155
- dependency in blocks_by_id
156
- and (
157
- blocks_by_id[dependency]["fields"].get("Status", (-1, ""))[1]
158
- == "shipped"
159
- or dependency == active_id
160
- )
161
- for dependency in dependencies
162
- ):
163
- return block
164
- raise NoEligibleLookahead("ROADMAP.md has no dependency-ready pending milestone")
165
-
166
-
167
- def selection_payload(selected: dict) -> dict:
168
- milestone = archive_milestone.normalized_slug(selected["title"])
169
- return {
170
- "status": "selected",
171
- "id": selected["id"],
172
- "milestone": milestone,
173
- "branch": f"gsd-path/{selected['id']}",
174
- }
175
-
176
-
177
- def select_lookahead(content: str, active_milestone: Optional[str] = None) -> dict:
178
- try:
179
- selected = next_eligible_pending(content, active_milestone)
180
- except NoEligibleLookahead:
181
- blocks = roadmap_blocks(content)
182
- if blocks and all(
183
- block["fields"].get("Status", (-1, ""))[1]
184
- in {"shipped", "abandoned"}
185
- for block in blocks
186
- ):
187
- return {"status": "complete"}
188
- return {"status": "none"}
189
- return selection_payload(selected)
190
-
191
-
192
96
  def select_track_branch(content: str, track_state: str) -> dict:
193
97
  fields = state_fields(track_state)
194
98
  if fields.get("pipeline", "") != archive_milestone.PIPELINE_MARKER:
@@ -247,42 +151,6 @@ def snapshot_roadmap(source: Path, destination: Path) -> dict:
247
151
  }
248
152
 
249
153
 
250
- def roadmap_contract(content: str, milestone: str) -> tuple[str, ...]:
251
- block = milestone_block(content, milestone)
252
- lines = content.splitlines()
253
- return tuple(
254
- line.rstrip()
255
- for line in lines[block["start"] : block["end"]]
256
- if not MUTABLE_ROADMAP_FIELDS_RE.match(line)
257
- )
258
-
259
-
260
- def compare_roadmap_entry(
261
- before: str,
262
- after: str,
263
- milestone: str,
264
- active_milestone: Optional[str] = None,
265
- ) -> dict:
266
- before_contract = roadmap_contract(before, milestone)
267
- try:
268
- after_contract = roadmap_contract(after, milestone)
269
- selected = milestone_block(after, milestone)
270
- eligible = next_eligible_pending(after, active_milestone)
271
- except LookaheadError:
272
- after_contract = ()
273
- selected = None
274
- eligible = None
275
- status = (
276
- "unchanged"
277
- if before_contract == after_contract
278
- and selected is not None
279
- and eligible is not None
280
- and selected["id"] == eligible["id"]
281
- else "changed"
282
- )
283
- return {"status": status, "milestone": milestone}
284
-
285
-
286
154
  def git_commit_file(root: Path, commit: str, relative: str) -> str:
287
155
  require_directory(root, "repository")
288
156
  if not SHA_RE.fullmatch(commit):
@@ -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)