@opengsd/gsd-path 1.0.0 → 1.2.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 (498) hide show
  1. package/AGENTS.md +33 -13
  2. package/CHANGELOG.md +68 -0
  3. package/DOCS.md +75 -6
  4. package/FULL.md +12 -15
  5. package/HOOKS.md +18 -9
  6. package/QUICK.md +8 -6
  7. package/README.md +218 -14
  8. package/RUNTIME.md +16 -5
  9. package/UPDATE.md +30 -11
  10. package/WORKFLOW.md +35 -16
  11. package/package.json +23 -1
  12. package/platforms/claude/dispatch.md +4 -4
  13. package/platforms/copilot/dispatch.md +4 -4
  14. package/platforms/cursor/dispatch.md +5 -10
  15. package/platforms/grok/dispatch.md +4 -6
  16. package/platforms/kimi/dispatch.md +4 -4
  17. package/platforms/kiro/dispatch.md +4 -6
  18. package/platforms/opencode/dispatch.md +4 -4
  19. package/platforms/qwen/dispatch.md +4 -4
  20. package/platforms/shared-agents/dispatch.md +12 -19
  21. package/scripts/_common.py +5 -0
  22. package/scripts/archive_milestone.py +30 -124
  23. package/scripts/bootstrap_repository.py +33 -4
  24. package/scripts/build_recovery.py +292 -0
  25. package/scripts/build_state.py +2 -0
  26. package/scripts/check_docs_audit.py +7 -3
  27. package/scripts/check_handoffs.py +7 -1
  28. package/scripts/check_task_briefs.py +3 -0
  29. package/scripts/detect_project.py +22 -3
  30. package/scripts/discussion_records.py +3 -0
  31. package/scripts/discussion_validate.py +8 -3
  32. package/scripts/dispatch_driver.py +233 -22
  33. package/scripts/git_guard.py +14 -5
  34. package/scripts/guard_hook.py +11 -1
  35. package/scripts/install.mjs +54 -1448
  36. package/scripts/install.py +155 -188
  37. package/scripts/integration.py +54 -28
  38. package/scripts/isolation.py +24 -6
  39. package/scripts/jev_review.py +138 -0
  40. package/scripts/lean_verification.py +3 -1
  41. package/scripts/loop_run.py +2 -0
  42. package/scripts/model_policy.py +279 -0
  43. package/scripts/path_config.py +248 -0
  44. package/scripts/pipeline_diagnose.py +15 -3
  45. package/scripts/pipeline_git.py +3 -0
  46. package/scripts/pipeline_state.py +117 -186
  47. package/scripts/pipeline_undo.py +14 -7
  48. package/scripts/promote_lookahead.py +33 -162
  49. package/scripts/review_panel.py +11 -0
  50. package/scripts/roadmap.py +326 -0
  51. package/scripts/runtime_store.py +278 -0
  52. package/scripts/skill-resources.json +182 -4
  53. package/scripts/state_checkpoint.py +114 -72
  54. package/scripts/state_promote.py +47 -44
  55. package/scripts/status_runtime.py +92 -6
  56. package/scripts/task_context.py +73 -0
  57. package/scripts/token_budget.py +40 -1
  58. package/scripts/workflow_run.py +128 -8
  59. package/scripts/worktree_paths.py +94 -0
  60. package/skills/gsd-path/BUILD.md +54 -575
  61. package/skills/gsd-path/DECIDE.md +4 -6
  62. package/skills/gsd-path/DEFINE.md +23 -21
  63. package/skills/gsd-path/DOCS-AUDIT.md +2 -0
  64. package/skills/gsd-path/INSPECT.md +32 -8
  65. package/skills/gsd-path/PLAN.md +29 -19
  66. package/skills/gsd-path/RESEARCH.md +5 -6
  67. package/skills/gsd-path/ROADMAP.md +24 -58
  68. package/skills/gsd-path/SHIP.md +29 -17
  69. package/skills/gsd-path/SKILL.md +46 -22
  70. package/skills/gsd-path/references/build-abandon.md +33 -0
  71. package/skills/gsd-path/references/build-native.md +502 -0
  72. package/skills/gsd-path/references/build-recovery.md +78 -0
  73. package/skills/gsd-path/references/coder.md +8 -3
  74. package/skills/gsd-path/references/config.md +57 -0
  75. package/skills/gsd-path/references/dispatch.md +12 -19
  76. package/skills/gsd-path/references/docs-auditor.md +14 -0
  77. package/skills/gsd-path/references/jev-review.md +82 -0
  78. package/skills/gsd-path/references/model-policy.md +139 -0
  79. package/skills/gsd-path/references/reviewer-final.md +21 -0
  80. package/skills/gsd-path/references/reviewer-panel.md +17 -0
  81. package/skills/gsd-path/references/reviewer-skeptic.md +20 -0
  82. package/skills/gsd-path/references/reviewer-wave.md +89 -0
  83. package/skills/gsd-path/references/reviewer.md +12 -148
  84. package/skills/gsd-path/references/runtime-selection.md +25 -0
  85. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  86. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  87. package/skills/gsd-path/scripts/_common.py +5 -0
  88. package/skills/gsd-path/scripts/archive_milestone.py +30 -124
  89. package/skills/gsd-path/scripts/bootstrap_repository.py +33 -4
  90. package/skills/gsd-path/scripts/build_recovery.py +292 -0
  91. package/skills/gsd-path/scripts/build_state.py +2 -0
  92. package/skills/gsd-path/scripts/check_docs_audit.py +7 -3
  93. package/skills/gsd-path/scripts/check_handoffs.py +7 -1
  94. package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
  95. package/skills/gsd-path/scripts/detect_project.py +22 -3
  96. package/skills/gsd-path/scripts/discussion_records.py +3 -0
  97. package/skills/gsd-path/scripts/discussion_validate.py +8 -3
  98. package/skills/gsd-path/scripts/dispatch_driver.py +233 -22
  99. package/skills/gsd-path/scripts/git_guard.py +768 -0
  100. package/skills/gsd-path/scripts/integration.py +54 -28
  101. package/skills/gsd-path/scripts/isolation.py +24 -6
  102. package/skills/gsd-path/scripts/jev_review.py +138 -0
  103. package/skills/gsd-path/scripts/lean_verification.py +3 -1
  104. package/skills/gsd-path/scripts/loop_run.py +2 -0
  105. package/skills/gsd-path/scripts/model_policy.py +279 -0
  106. package/skills/gsd-path/scripts/path_config.py +248 -0
  107. package/skills/gsd-path/scripts/pipeline_git.py +3 -0
  108. package/skills/gsd-path/scripts/pipeline_state.py +117 -186
  109. package/skills/gsd-path/scripts/promote_lookahead.py +33 -162
  110. package/skills/gsd-path/scripts/review_panel.py +11 -0
  111. package/skills/gsd-path/scripts/roadmap.py +326 -0
  112. package/skills/gsd-path/scripts/state_checkpoint.py +114 -72
  113. package/skills/gsd-path/scripts/state_promote.py +47 -44
  114. package/skills/gsd-path/scripts/status_runtime.py +208 -0
  115. package/skills/gsd-path/scripts/task_context.py +73 -0
  116. package/skills/gsd-path/scripts/token_budget.py +40 -1
  117. package/skills/gsd-path/scripts/workflow_run.py +128 -8
  118. package/skills/gsd-path/scripts/worktree_paths.py +94 -0
  119. package/skills/gsd-path/templates/skeptic.md +1 -1
  120. package/skills/gsd-path/templates/task.md +2 -0
  121. package/skills/gsd-path/templates/wave-panel.md +1 -1
  122. package/skills/gsd-path/templates/wave-review.md +1 -1
  123. package/skills/gsd-path-build/SKILL.md +54 -575
  124. package/skills/gsd-path-build/references/build-abandon.md +33 -0
  125. package/skills/gsd-path-build/references/build-native.md +502 -0
  126. package/skills/gsd-path-build/references/build-recovery.md +78 -0
  127. package/skills/gsd-path-build/references/coder.md +8 -3
  128. package/skills/gsd-path-build/references/dispatch.md +12 -19
  129. package/skills/gsd-path-build/references/jev-review.md +82 -0
  130. package/skills/gsd-path-build/references/model-policy.md +139 -0
  131. package/skills/gsd-path-build/references/reviewer-final.md +21 -0
  132. package/skills/gsd-path-build/references/reviewer-panel.md +17 -0
  133. package/skills/gsd-path-build/references/reviewer-skeptic.md +20 -0
  134. package/skills/gsd-path-build/references/reviewer-wave.md +89 -0
  135. package/skills/gsd-path-build/references/reviewer.md +12 -148
  136. package/skills/gsd-path-build/references/runtime-selection.md +25 -0
  137. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  138. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  139. package/skills/gsd-path-build/scripts/_common.py +5 -0
  140. package/skills/gsd-path-build/scripts/archive_milestone.py +30 -124
  141. package/skills/gsd-path-build/scripts/build_recovery.py +292 -0
  142. package/skills/gsd-path-build/scripts/build_state.py +2 -0
  143. package/skills/gsd-path-build/scripts/check_handoffs.py +7 -1
  144. package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
  145. package/skills/gsd-path-build/scripts/detect_project.py +1536 -0
  146. package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
  147. package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
  148. package/skills/gsd-path-build/scripts/dispatch_driver.py +233 -22
  149. package/skills/gsd-path-build/scripts/integration.py +54 -28
  150. package/skills/gsd-path-build/scripts/isolation.py +24 -6
  151. package/skills/gsd-path-build/scripts/jev_review.py +138 -0
  152. package/skills/gsd-path-build/scripts/loop_run.py +2 -0
  153. package/skills/gsd-path-build/scripts/model_policy.py +279 -0
  154. package/skills/gsd-path-build/scripts/path_config.py +248 -0
  155. package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
  156. package/skills/gsd-path-build/scripts/pipeline_state.py +117 -186
  157. package/skills/gsd-path-build/scripts/promote_lookahead.py +286 -0
  158. package/skills/gsd-path-build/scripts/review_panel.py +11 -0
  159. package/skills/gsd-path-build/scripts/roadmap.py +326 -0
  160. package/skills/gsd-path-build/scripts/state_checkpoint.py +114 -72
  161. package/skills/gsd-path-build/scripts/state_promote.py +47 -44
  162. package/skills/gsd-path-build/scripts/task_context.py +73 -0
  163. package/skills/gsd-path-build/scripts/token_budget.py +40 -1
  164. package/skills/gsd-path-build/scripts/workflow_run.py +128 -8
  165. package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
  166. package/skills/gsd-path-build/templates/skeptic.md +1 -1
  167. package/skills/gsd-path-build/templates/task.md +2 -0
  168. package/skills/gsd-path-build/templates/wave-panel.md +1 -1
  169. package/skills/gsd-path-build/templates/wave-review.md +1 -1
  170. package/skills/gsd-path-decide/SKILL.md +4 -6
  171. package/skills/gsd-path-decide/references/dispatch.md +12 -19
  172. package/skills/gsd-path-decide/references/model-policy.md +139 -0
  173. package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
  174. package/skills/gsd-path-decide/scripts/_common.py +5 -0
  175. package/skills/gsd-path-decide/scripts/archive_milestone.py +2171 -0
  176. package/skills/gsd-path-decide/scripts/build_recovery.py +292 -0
  177. package/skills/gsd-path-decide/scripts/check_handoffs.py +7 -1
  178. package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
  179. package/skills/gsd-path-decide/scripts/detect_project.py +1536 -0
  180. package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
  181. package/skills/gsd-path-decide/scripts/discussion_validate.py +1326 -0
  182. package/skills/gsd-path-decide/scripts/integration.py +1459 -0
  183. package/skills/gsd-path-decide/scripts/isolation.py +24 -6
  184. package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
  185. package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
  186. package/skills/gsd-path-decide/scripts/path_config.py +248 -0
  187. package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
  188. package/skills/gsd-path-decide/scripts/pipeline_state.py +117 -186
  189. package/skills/gsd-path-decide/scripts/promote_lookahead.py +286 -0
  190. package/skills/gsd-path-decide/scripts/review_panel.py +564 -0
  191. package/skills/gsd-path-decide/scripts/roadmap.py +326 -0
  192. package/skills/gsd-path-decide/scripts/state_checkpoint.py +114 -72
  193. package/skills/gsd-path-decide/scripts/state_promote.py +47 -44
  194. package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
  195. package/skills/gsd-path-define/SKILL.md +23 -21
  196. package/skills/gsd-path-define/references/build-recovery.md +78 -0
  197. package/skills/gsd-path-define/references/runtime-selection.md +25 -0
  198. package/skills/gsd-path-define/scripts/_common.py +5 -0
  199. package/skills/gsd-path-define/scripts/archive_milestone.py +2171 -0
  200. package/skills/gsd-path-define/scripts/build_recovery.py +292 -0
  201. package/skills/gsd-path-define/scripts/check_handoffs.py +7 -1
  202. package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
  203. package/skills/gsd-path-define/scripts/detect_project.py +22 -3
  204. package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
  205. package/skills/gsd-path-define/scripts/discussion_validate.py +1326 -0
  206. package/skills/gsd-path-define/scripts/integration.py +1459 -0
  207. package/skills/gsd-path-define/scripts/isolation.py +24 -6
  208. package/skills/gsd-path-define/scripts/loop_run.py +839 -0
  209. package/skills/gsd-path-define/scripts/model_policy.py +279 -0
  210. package/skills/gsd-path-define/scripts/path_config.py +248 -0
  211. package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
  212. package/skills/gsd-path-define/scripts/pipeline_state.py +117 -186
  213. package/skills/gsd-path-define/scripts/promote_lookahead.py +286 -0
  214. package/skills/gsd-path-define/scripts/review_panel.py +564 -0
  215. package/skills/gsd-path-define/scripts/roadmap.py +326 -0
  216. package/skills/gsd-path-define/scripts/state_checkpoint.py +114 -72
  217. package/skills/gsd-path-define/scripts/state_promote.py +47 -44
  218. package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
  219. package/skills/gsd-path-discuss/SKILL.md +2 -0
  220. package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
  221. package/skills/gsd-path-discuss/scripts/_common.py +5 -0
  222. package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
  223. package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
  224. package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
  225. package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
  226. package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
  227. package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
  228. package/skills/gsd-path-docs-audit/SKILL.md +2 -0
  229. package/skills/gsd-path-docs-audit/references/dispatch.md +12 -19
  230. package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
  231. package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
  232. package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
  233. package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
  234. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2171 -0
  235. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +292 -0
  236. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +7 -3
  237. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +7 -1
  238. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
  239. package/skills/gsd-path-docs-audit/scripts/detect_project.py +1536 -0
  240. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
  241. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1326 -0
  242. package/skills/gsd-path-docs-audit/scripts/integration.py +1459 -0
  243. package/skills/gsd-path-docs-audit/scripts/isolation.py +24 -6
  244. package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
  245. package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
  246. package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
  247. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
  248. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +117 -186
  249. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +286 -0
  250. package/skills/gsd-path-docs-audit/scripts/review_panel.py +564 -0
  251. package/skills/gsd-path-docs-audit/scripts/roadmap.py +326 -0
  252. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +114 -72
  253. package/skills/gsd-path-docs-audit/scripts/state_promote.py +47 -44
  254. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
  255. package/skills/gsd-path-forensics/SKILL.md +9 -5
  256. package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
  257. package/skills/gsd-path-forensics/scripts/_common.py +5 -0
  258. package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
  259. package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
  260. package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
  261. package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
  262. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +15 -3
  263. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +14 -7
  264. package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
  265. package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
  266. package/skills/gsd-path-inspect/SKILL.md +32 -8
  267. package/skills/gsd-path-inspect/references/dispatch.md +12 -19
  268. package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
  269. package/skills/gsd-path-inspect/references/model-policy.md +139 -0
  270. package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
  271. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  272. package/skills/gsd-path-inspect/scripts/_common.py +5 -0
  273. package/skills/gsd-path-inspect/scripts/archive_milestone.py +2171 -0
  274. package/skills/gsd-path-inspect/scripts/build_recovery.py +292 -0
  275. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +7 -3
  276. package/skills/gsd-path-inspect/scripts/check_handoffs.py +7 -1
  277. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
  278. package/skills/gsd-path-inspect/scripts/detect_project.py +22 -3
  279. package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
  280. package/skills/gsd-path-inspect/scripts/discussion_validate.py +1326 -0
  281. package/skills/gsd-path-inspect/scripts/integration.py +1459 -0
  282. package/skills/gsd-path-inspect/scripts/isolation.py +24 -6
  283. package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
  284. package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
  285. package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
  286. package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
  287. package/skills/gsd-path-inspect/scripts/pipeline_state.py +117 -186
  288. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +286 -0
  289. package/skills/gsd-path-inspect/scripts/review_panel.py +564 -0
  290. package/skills/gsd-path-inspect/scripts/roadmap.py +326 -0
  291. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +114 -72
  292. package/skills/gsd-path-inspect/scripts/state_promote.py +47 -44
  293. package/skills/gsd-path-inspect/scripts/workflow_run.py +237 -0
  294. package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
  295. package/skills/gsd-path-loop/SKILL.md +2 -0
  296. package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
  297. package/skills/gsd-path-loop/scripts/_common.py +5 -0
  298. package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
  299. package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
  300. package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
  301. package/skills/gsd-path-loop/scripts/path_config.py +248 -0
  302. package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
  303. package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
  304. package/skills/gsd-path-migrate/SKILL.md +2 -0
  305. package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
  306. package/skills/gsd-path-migrate/scripts/_common.py +185 -0
  307. package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
  308. package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
  309. package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
  310. package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
  311. package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
  312. package/skills/gsd-path-plan/SKILL.md +29 -19
  313. package/skills/gsd-path-plan/references/build-recovery.md +78 -0
  314. package/skills/gsd-path-plan/references/dispatch.md +12 -19
  315. package/skills/gsd-path-plan/references/model-policy.md +139 -0
  316. package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
  317. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  318. package/skills/gsd-path-plan/scripts/_common.py +5 -0
  319. package/skills/gsd-path-plan/scripts/archive_milestone.py +2171 -0
  320. package/skills/gsd-path-plan/scripts/build_recovery.py +292 -0
  321. package/skills/gsd-path-plan/scripts/check_handoffs.py +7 -1
  322. package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
  323. package/skills/gsd-path-plan/scripts/detect_project.py +1536 -0
  324. package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
  325. package/skills/gsd-path-plan/scripts/discussion_validate.py +1326 -0
  326. package/skills/gsd-path-plan/scripts/integration.py +1459 -0
  327. package/skills/gsd-path-plan/scripts/isolation.py +24 -6
  328. package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
  329. package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
  330. package/skills/gsd-path-plan/scripts/path_config.py +248 -0
  331. package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
  332. package/skills/gsd-path-plan/scripts/pipeline_state.py +117 -186
  333. package/skills/gsd-path-plan/scripts/promote_lookahead.py +286 -0
  334. package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
  335. package/skills/gsd-path-plan/scripts/roadmap.py +326 -0
  336. package/skills/gsd-path-plan/scripts/state_checkpoint.py +114 -72
  337. package/skills/gsd-path-plan/scripts/state_promote.py +47 -44
  338. package/skills/gsd-path-plan/scripts/workflow_run.py +128 -8
  339. package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
  340. package/skills/gsd-path-plan/templates/task.md +2 -0
  341. package/skills/gsd-path-research/SKILL.md +5 -6
  342. package/skills/gsd-path-research/references/dispatch.md +12 -19
  343. package/skills/gsd-path-research/references/model-policy.md +139 -0
  344. package/skills/gsd-path-research/references/runtime-selection.md +25 -0
  345. package/skills/gsd-path-research/scripts/_common.py +5 -0
  346. package/skills/gsd-path-research/scripts/archive_milestone.py +2171 -0
  347. package/skills/gsd-path-research/scripts/build_recovery.py +292 -0
  348. package/skills/gsd-path-research/scripts/check_handoffs.py +7 -1
  349. package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
  350. package/skills/gsd-path-research/scripts/detect_project.py +1536 -0
  351. package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
  352. package/skills/gsd-path-research/scripts/discussion_validate.py +1326 -0
  353. package/skills/gsd-path-research/scripts/integration.py +1459 -0
  354. package/skills/gsd-path-research/scripts/isolation.py +24 -6
  355. package/skills/gsd-path-research/scripts/loop_run.py +839 -0
  356. package/skills/gsd-path-research/scripts/model_policy.py +279 -0
  357. package/skills/gsd-path-research/scripts/path_config.py +248 -0
  358. package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
  359. package/skills/gsd-path-research/scripts/pipeline_state.py +117 -186
  360. package/skills/gsd-path-research/scripts/promote_lookahead.py +286 -0
  361. package/skills/gsd-path-research/scripts/review_panel.py +564 -0
  362. package/skills/gsd-path-research/scripts/roadmap.py +326 -0
  363. package/skills/gsd-path-research/scripts/state_checkpoint.py +114 -72
  364. package/skills/gsd-path-research/scripts/state_promote.py +47 -44
  365. package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
  366. package/skills/gsd-path-roadmap/SKILL.md +24 -58
  367. package/skills/gsd-path-roadmap/references/dispatch.md +12 -19
  368. package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
  369. package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
  370. package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
  371. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +30 -124
  372. package/skills/gsd-path-roadmap/scripts/build_recovery.py +292 -0
  373. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +7 -1
  374. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
  375. package/skills/gsd-path-roadmap/scripts/detect_project.py +22 -3
  376. package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
  377. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
  378. package/skills/gsd-path-roadmap/scripts/integration.py +54 -28
  379. package/skills/gsd-path-roadmap/scripts/isolation.py +24 -6
  380. package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
  381. package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
  382. package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
  383. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
  384. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +117 -186
  385. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +33 -162
  386. package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
  387. package/skills/gsd-path-roadmap/scripts/roadmap.py +326 -0
  388. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +114 -72
  389. package/skills/gsd-path-roadmap/scripts/state_promote.py +47 -44
  390. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
  391. package/skills/gsd-path-ship/SKILL.md +29 -17
  392. package/skills/gsd-path-ship/references/dispatch.md +12 -19
  393. package/skills/gsd-path-ship/references/jev-review.md +82 -0
  394. package/skills/gsd-path-ship/references/model-policy.md +139 -0
  395. package/skills/gsd-path-ship/references/reviewer-final.md +21 -0
  396. package/skills/gsd-path-ship/references/reviewer-panel.md +17 -0
  397. package/skills/gsd-path-ship/references/reviewer-skeptic.md +20 -0
  398. package/skills/gsd-path-ship/references/reviewer-wave.md +89 -0
  399. package/skills/gsd-path-ship/references/reviewer.md +12 -148
  400. package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
  401. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  402. package/skills/gsd-path-ship/scripts/_common.py +5 -0
  403. package/skills/gsd-path-ship/scripts/archive_milestone.py +30 -124
  404. package/skills/gsd-path-ship/scripts/build_recovery.py +292 -0
  405. package/skills/gsd-path-ship/scripts/build_state.py +2 -0
  406. package/skills/gsd-path-ship/scripts/check_handoffs.py +7 -1
  407. package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
  408. package/skills/gsd-path-ship/scripts/detect_project.py +1536 -0
  409. package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
  410. package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
  411. package/skills/gsd-path-ship/scripts/integration.py +54 -28
  412. package/skills/gsd-path-ship/scripts/isolation.py +24 -6
  413. package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
  414. package/skills/gsd-path-ship/scripts/lean_verification.py +3 -1
  415. package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
  416. package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
  417. package/skills/gsd-path-ship/scripts/path_config.py +248 -0
  418. package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
  419. package/skills/gsd-path-ship/scripts/pipeline_state.py +117 -186
  420. package/skills/gsd-path-ship/scripts/promote_lookahead.py +286 -0
  421. package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
  422. package/skills/gsd-path-ship/scripts/roadmap.py +326 -0
  423. package/skills/gsd-path-ship/scripts/state_checkpoint.py +114 -72
  424. package/skills/gsd-path-ship/scripts/state_promote.py +47 -44
  425. package/skills/gsd-path-ship/scripts/workflow_run.py +128 -8
  426. package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
  427. package/skills/gsd-path-ship/templates/wave-review.md +1 -1
  428. package/skills/gsd-path-undo/SKILL.md +2 -0
  429. package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
  430. package/skills/gsd-path-undo/scripts/_common.py +5 -0
  431. package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
  432. package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
  433. package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
  434. package/skills/gsd-path-undo/scripts/path_config.py +248 -0
  435. package/skills/gsd-path-undo/scripts/pipeline_undo.py +14 -7
  436. package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
  437. package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
  438. package/skills/path/BUILD.md +54 -575
  439. package/skills/path/DECIDE.md +4 -6
  440. package/skills/path/DEFINE.md +23 -21
  441. package/skills/path/DOCS-AUDIT.md +2 -0
  442. package/skills/path/INSPECT.md +32 -8
  443. package/skills/path/PLAN.md +29 -19
  444. package/skills/path/RESEARCH.md +5 -6
  445. package/skills/path/ROADMAP.md +24 -58
  446. package/skills/path/SHIP.md +29 -17
  447. package/skills/path/SKILL.md +46 -22
  448. package/skills/path/references/build-abandon.md +33 -0
  449. package/skills/path/references/build-native.md +502 -0
  450. package/skills/path/references/build-recovery.md +78 -0
  451. package/skills/path/references/coder.md +8 -3
  452. package/skills/path/references/config.md +57 -0
  453. package/skills/path/references/dispatch.md +12 -19
  454. package/skills/path/references/docs-auditor.md +14 -0
  455. package/skills/path/references/jev-review.md +82 -0
  456. package/skills/path/references/model-policy.md +139 -0
  457. package/skills/path/references/reviewer-final.md +21 -0
  458. package/skills/path/references/reviewer-panel.md +17 -0
  459. package/skills/path/references/reviewer-skeptic.md +20 -0
  460. package/skills/path/references/reviewer-wave.md +89 -0
  461. package/skills/path/references/reviewer.md +12 -148
  462. package/skills/path/references/runtime-selection.md +25 -0
  463. package/skills/path/scripts/_common.py +5 -0
  464. package/skills/path/scripts/archive_milestone.py +30 -124
  465. package/skills/path/scripts/bootstrap_repository.py +33 -4
  466. package/skills/path/scripts/build_recovery.py +292 -0
  467. package/skills/path/scripts/build_state.py +2 -0
  468. package/skills/path/scripts/check_docs_audit.py +7 -3
  469. package/skills/path/scripts/check_handoffs.py +7 -1
  470. package/skills/path/scripts/check_task_briefs.py +3 -0
  471. package/skills/path/scripts/detect_project.py +22 -3
  472. package/skills/path/scripts/discussion_records.py +3 -0
  473. package/skills/path/scripts/discussion_validate.py +8 -3
  474. package/skills/path/scripts/dispatch_driver.py +233 -22
  475. package/skills/path/scripts/git_guard.py +768 -0
  476. package/skills/path/scripts/integration.py +54 -28
  477. package/skills/path/scripts/isolation.py +24 -6
  478. package/skills/path/scripts/jev_review.py +138 -0
  479. package/skills/path/scripts/lean_verification.py +3 -1
  480. package/skills/path/scripts/loop_run.py +2 -0
  481. package/skills/path/scripts/model_policy.py +279 -0
  482. package/skills/path/scripts/path_config.py +248 -0
  483. package/skills/path/scripts/pipeline_git.py +3 -0
  484. package/skills/path/scripts/pipeline_state.py +117 -186
  485. package/skills/path/scripts/promote_lookahead.py +33 -162
  486. package/skills/path/scripts/review_panel.py +11 -0
  487. package/skills/path/scripts/roadmap.py +326 -0
  488. package/skills/path/scripts/state_checkpoint.py +114 -72
  489. package/skills/path/scripts/state_promote.py +47 -44
  490. package/skills/path/scripts/status_runtime.py +208 -0
  491. package/skills/path/scripts/task_context.py +73 -0
  492. package/skills/path/scripts/token_budget.py +40 -1
  493. package/skills/path/scripts/workflow_run.py +128 -8
  494. package/skills/path/scripts/worktree_paths.py +94 -0
  495. package/skills/path/templates/skeptic.md +1 -1
  496. package/skills/path/templates/task.md +2 -0
  497. package/skills/path/templates/wave-panel.md +1 -1
  498. package/skills/path/templates/wave-review.md +1 -1
@@ -19,6 +19,9 @@ import shutil
19
19
  import stat
20
20
  import subprocess
21
21
  import sys
22
+
23
+ # Runtime helpers must not modify their immutable installation.
24
+ sys.dont_write_bytecode = True
22
25
  from dataclasses import asdict, dataclass
23
26
  from datetime import date
24
27
  from pathlib import Path, PurePosixPath
@@ -32,8 +35,9 @@ try:
32
35
  collect_artifact_recoveries,
33
36
  )
34
37
  import _common
38
+ import roadmap
35
39
  except ModuleNotFoundError as error: # pragma: no cover - package imports used by tests
36
- if error.name not in {"isolation", "_common"}:
40
+ if error.name not in {"isolation", "_common", "roadmap"}:
37
41
  raise
38
42
  from scripts.isolation import (
39
43
  IsolationError,
@@ -41,7 +45,7 @@ except ModuleNotFoundError as error: # pragma: no cover - package imports used
41
45
  checkpoint as isolation_checkpoint,
42
46
  collect_artifact_recoveries,
43
47
  )
44
- from scripts import _common
48
+ from scripts import _common, roadmap
45
49
 
46
50
  try: # pragma: no cover - exercised only on Windows
47
51
  import fcntl
@@ -82,7 +86,6 @@ SLUG_RE = re.compile(r"^[a-z0-9][a-z0-9-]*$")
82
86
  BOUND_BRANCH_RE = _common.BOUND_BRANCH_RE
83
87
  ARCHIVE_RE = re.compile(r"^\.project/archive/(\d{3,})-([a-z0-9][a-z0-9-]*)/?$")
84
88
  FRONTMATTER_RE = re.compile(r"^([a-z_]+):\s*([^#]*?)(?:\s+#.*)?$")
85
- ROADMAP_HEADING_RE = re.compile(r"^### (M\d{3,}) — ([a-z0-9][a-z0-9-]*)\s*$")
86
89
  TASK_ID_RE = re.compile(r"^T\d{3,}$")
87
90
  TASK_FILE_RE = re.compile(r"^(T\d{3,})-[a-z0-9][a-z0-9-]*\.md$")
88
91
  PLAN_APPROVAL_SUBJECT = "plan: build plan approved"
@@ -110,7 +113,7 @@ LOOKAHEAD_EVIDENCE_RE = re.compile(r"^research/evidence-[a-z0-9][a-z0-9-]*\.md$"
110
113
  PROMOTION_RESIDUAL = ".gsd-path-promote-next-residual"
111
114
  CHECKPOINT_SCHEMA = "gsd-path/state-checkpoint/v1"
112
115
  CHECKPOINT_JOURNAL_NAME = "gsd-path-state-checkpoint.json"
113
- CHECKPOINT_KINDS = ("plan", "roadmap")
116
+ CHECKPOINT_KINDS = ("plan", "roadmap", "roadmap-reslice")
114
117
  SHIPMENT_SCHEMA = "gsd-path/shipment/v1"
115
118
  SHIPMENT_JOURNAL_NAME = "gsd-path-shipment.json"
116
119
  UNDO_TRANSACTION_SCHEMA = "gsd-path/undo-transaction/v2"
@@ -126,6 +129,8 @@ CROSS_PHASE_TRANSITIONS = {
126
129
  ("decide", "done", "plan", "active"),
127
130
  ("roadmap", "done", "define", "active"),
128
131
  ("plan", "done", "build", "active"),
132
+ ("build", "blocked", "define", "active"),
133
+ ("build", "blocked", "plan", "active"),
129
134
  ("build", "active", "ship", "active"),
130
135
  ("build", "done", "ship", "active"),
131
136
  ("ship", "active", "shipped", "done"),
@@ -166,99 +171,10 @@ class ApprovedTaskContract:
166
171
  files: tuple[str, ...]
167
172
 
168
173
 
169
- # state_checkpoint and state_promote call back into this module through a
170
- # module reference and import the constants and classes above, so this import
171
- # must stay below those definitions.
172
- if __package__: # imported as scripts.pipeline_state
173
- from .state_checkpoint import (
174
- _sha256,
175
- _tree_digest,
176
- _checkpoint_artifact_digest,
177
- _roadmap_status,
178
- _activate_roadmap_milestone,
179
- _approval_details,
180
- _checkpoint_request_fields,
181
- _checkpoint_file,
182
- _approval_journal,
183
- _remove_checkpoint_temporary,
184
- _converge_checkpoint_file,
185
- _unlink_checkpoint_journal,
186
- _resume_checkpoint_locked,
187
- checkpoint_approval,
188
- _checkpoint_deferral_error,
189
- defer_approval,
190
- resume_checkpoint,
191
- _task_files,
192
- _approval_checkpoint,
193
- _validate_approval_track,
194
- _safe_declared_paths,
195
- _git_text_at,
196
- _approved_task_contracts,
197
- _task_contracts_at,
198
- _classify_plan_drift,
199
- )
200
- from .state_promote import (
201
- _render_roadmap,
202
- _promotion_event,
203
- _promotion_state,
204
- _head_with_exact_message,
205
- _tree_entries,
206
- _promotion_tree_mapping,
207
- _completed_promotion,
208
- _prepare_promotion,
209
- _require_journal_request,
210
- _resume_track_moves,
211
- _resume_metadata,
212
- _resume_next_removal,
213
- _commit_promotion,
214
- promote_next,
215
- )
216
- else: # standalone script or sibling import
217
- if __name__ == "__main__":
218
- sys.modules.setdefault("pipeline_state", sys.modules[__name__])
219
- from state_checkpoint import (
220
- _sha256,
221
- _tree_digest,
222
- _checkpoint_artifact_digest,
223
- _roadmap_status,
224
- _activate_roadmap_milestone,
225
- _approval_details,
226
- _checkpoint_request_fields,
227
- _checkpoint_file,
228
- _approval_journal,
229
- _remove_checkpoint_temporary,
230
- _converge_checkpoint_file,
231
- _unlink_checkpoint_journal,
232
- _resume_checkpoint_locked,
233
- checkpoint_approval,
234
- _checkpoint_deferral_error,
235
- defer_approval,
236
- resume_checkpoint,
237
- _task_files,
238
- _approval_checkpoint,
239
- _validate_approval_track,
240
- _safe_declared_paths,
241
- _git_text_at,
242
- _approved_task_contracts,
243
- _task_contracts_at,
244
- _classify_plan_drift,
245
- )
246
- from state_promote import (
247
- _render_roadmap,
248
- _promotion_event,
249
- _promotion_state,
250
- _head_with_exact_message,
251
- _tree_entries,
252
- _promotion_tree_mapping,
253
- _completed_promotion,
254
- _prepare_promotion,
255
- _require_journal_request,
256
- _resume_track_moves,
257
- _resume_metadata,
258
- _resume_next_removal,
259
- _commit_promotion,
260
- promote_next,
261
- )
174
+ # Standalone transaction imports must reuse this state module and its types.
175
+ if __name__ == "__main__":
176
+ sys.modules.setdefault("pipeline_state", sys.modules[__name__])
177
+
262
178
 
263
179
  def _run_git(
264
180
  repo: Path,
@@ -693,6 +609,21 @@ def _next_skill(route: Mapping[str, object]) -> Optional[str]:
693
609
  return None
694
610
 
695
611
 
612
+ def _completion_status(repo: Path, state: Mapping[str, object], project_dir: str) -> dict:
613
+ if project_dir != ".project" or state.get("phase") != "shipped":
614
+ return {"status": "not-shipped"}
615
+ if __package__:
616
+ from . import integration
617
+ else:
618
+ import integration
619
+ try:
620
+ proof = integration.validate_integrated(
621
+ repo, str(state.get("milestone") or ""), refresh=False, historical=True)
622
+ except (integration.ArchiveError, OSError) as error:
623
+ return {"status": "unverified", "reason": str(error)}
624
+ return {"status": "verified", "proof": proof}
625
+
626
+
696
627
  def status_state(repo: Path, project_dir: str = ".project") -> dict[str, object]:
697
628
  """Report owned state, route, and git facts without advancing a phase."""
698
629
  resolved = _repo_root(repo)
@@ -726,9 +657,11 @@ def status_state(repo: Path, project_dir: str = ".project") -> dict[str, object]
726
657
  "undo": None,
727
658
  "collect_artifact": None,
728
659
  }
660
+ completion = _completion_status(resolved, state, project_dir)
729
661
  return {
730
662
  "schema": STATUS_SCHEMA,
731
663
  "advance": False,
664
+ "completion": completion,
732
665
  "state": state,
733
666
  "route": route,
734
667
  "path": str(_track_root(resolved, project_dir) / "STATE.md"),
@@ -753,6 +686,28 @@ def status_state(repo: Path, project_dir: str = ".project") -> dict[str, object]
753
686
  "lookahead": lookahead.exists() or lookahead.is_symlink(),
754
687
  "journals": journals,
755
688
  "next_skill": _next_skill(route if isinstance(route, dict) else {}),
689
+ "handoff": phase_handoff(state, route, _track_root(resolved, project_dir) / "STATE.md", completion),
690
+ }
691
+
692
+
693
+ def phase_handoff(state: Mapping[str, object], route: Mapping[str, object], path: Path,
694
+ completion: Mapping[str, object]) -> dict[str, str]:
695
+ """Present the authoritative route without granting continuation authority."""
696
+ action = route["action"]
697
+ if action == "run-phase":
698
+ next_action = f"Invoke $gsd-path to continue with {route['phase']}."
699
+ router_next = f"Continue with {route['phase']}; honor its input and approval gates."
700
+ else:
701
+ next_action = router_next = f"{action}: {route['reason']}"
702
+ outcome = f"{state['phase']}/{state['status']}"
703
+ if completion["status"] == "unverified":
704
+ outcome = f"Unverified: {outcome}"
705
+ return {
706
+ "outcome": outcome,
707
+ "review": str(path),
708
+ "next": next_action,
709
+ "phase_next": next_action,
710
+ "router_next": router_next,
756
711
  }
757
712
 
758
713
 
@@ -784,50 +739,11 @@ def _intent_lane(track: Path) -> Optional[str]:
784
739
 
785
740
 
786
741
  def _active_roadmap_has_questions(project: Path, milestone: Optional[str] = None) -> bool:
787
- """Report open questions for the active entry, or for ``milestone`` when given."""
788
742
  text = _read_real_file(project / "ROADMAP.md", "ROADMAP.md")
789
- lines = text.splitlines()
790
- active_section: Optional[list[str]] = None
791
- for index, line in enumerate(lines):
792
- heading = ROADMAP_HEADING_RE.fullmatch(line)
793
- if heading is None:
794
- continue
795
- if int(heading.group(1).removeprefix("M")) < 1:
796
- raise PipelineStateError(
797
- f"ROADMAP.md has invalid milestone id: {heading.group(1)}"
798
- )
799
- end = next(
800
- (
801
- cursor
802
- for cursor in range(index + 1, len(lines))
803
- if ROADMAP_HEADING_RE.fullmatch(lines[cursor]) is not None
804
- ),
805
- len(lines),
806
- )
807
- section = lines[index:end]
808
- if milestone is not None:
809
- if heading.group(2) == milestone:
810
- active_section = section
811
- continue
812
- if any(re.fullmatch(r"Status:\s*active(?:\s+#.*)?", item) for item in section):
813
- if active_section is not None:
814
- raise PipelineStateError("ROADMAP.md has more than one active milestone")
815
- active_section = section
816
- if active_section is None:
817
- if milestone is not None:
818
- raise PipelineStateError(f"ROADMAP.md is missing lookahead milestone: {milestone}")
819
- raise PipelineStateError("ROADMAP.md has no active milestone")
820
743
  try:
821
- start = active_section.index("Open questions") + 1
822
- except ValueError as error:
823
- raise PipelineStateError("active roadmap milestone has no Open questions") from error
824
- questions = [
825
- line[2:].strip()
826
- for line in active_section[start:]
827
- if line.startswith("- ")
828
- ]
829
- return any(question.lower() != "none" for question in questions)
830
-
744
+ return roadmap.has_questions(text, milestone)
745
+ except roadmap.RoadmapError as error:
746
+ raise PipelineStateError(str(error)) from error
831
747
 
832
748
  def _valid_patch_findings(repo: Path) -> bool:
833
749
  validator = Path(__file__).resolve().with_name("check_handoffs.py")
@@ -1106,6 +1022,14 @@ def route_state(repo: Path, project_dir: str = ".project") -> dict[str, object]:
1106
1022
  """Route ordinary phase work only when its pending discussion owner can enter."""
1107
1023
  result = _route_state(repo, project_dir)
1108
1024
  route = result["route"]
1025
+ if project_dir == ".project" and route["action"] == "run-phase":
1026
+ recovery = _build_recovery().context(_repo_root(repo))
1027
+ if recovery and recovery["active"]:
1028
+ result["recovery"] = recovery
1029
+ if route.get("phase") == "define":
1030
+ route["mode"] = "corrections"
1031
+ elif route.get("phase") == "plan":
1032
+ route["mode"] = "build-repair"
1109
1033
  if route["action"] == "run-phase" and result["state"]["archive"] is None:
1110
1034
  reason = _pending_discussion_block(_repo_root(repo), route.get("phase"))
1111
1035
  if reason:
@@ -1159,7 +1083,11 @@ def _route_state(repo: Path, project_dir: str = ".project") -> dict[str, object]
1159
1083
  return result
1160
1084
  checkpoint_journal = _git_path(resolved, CHECKPOINT_JOURNAL_NAME)
1161
1085
  if checkpoint_journal.exists() or checkpoint_journal.is_symlink():
1162
- transaction = _approval_journal(
1086
+ if __package__:
1087
+ from . import state_checkpoint
1088
+ else:
1089
+ import state_checkpoint
1090
+ transaction = state_checkpoint._approval_journal(
1163
1091
  resolved,
1164
1092
  _read_json(checkpoint_journal),
1165
1093
  )
@@ -1736,6 +1664,16 @@ def transition_state(
1736
1664
  event,
1737
1665
  project_dir,
1738
1666
  )
1667
+ recovery_context = _build_recovery().context(resolved, text) if project_dir == ".project" else None
1668
+ if recovery_context and recovery_context["active"]:
1669
+ if (state.milestone, state.branch, state.archive) != (after.milestone, after.branch, after.archive):
1670
+ raise PipelineStateError("build recovery must preserve milestone identity")
1671
+ if state.phase == "build" and after.phase in {"define", "plan"}:
1672
+ recovery = _build_recovery().begin(resolved, state, after, event)
1673
+ rendered = _append_event(
1674
+ rendered, after.phase,
1675
+ _build_recovery().MARKER + json.dumps(recovery, sort_keys=True),
1676
+ )
1739
1677
  if after.status != "blocked":
1740
1678
  reason = _pending_discussion_block(
1741
1679
  resolved, after.phase if after.status == "active" else None
@@ -1767,6 +1705,14 @@ def transition_state(
1767
1705
  }
1768
1706
 
1769
1707
 
1708
+ def _build_recovery():
1709
+ if __package__:
1710
+ from scripts import build_recovery
1711
+ else:
1712
+ import build_recovery
1713
+ return build_recovery
1714
+
1715
+
1770
1716
  def configure_integration(
1771
1717
  repo: Path,
1772
1718
  scope: str,
@@ -1786,7 +1732,9 @@ def configure_integration(
1786
1732
  project = _track_root(resolved, project_dir)
1787
1733
  with _state_lock(project):
1788
1734
  state, text, path = load_state(resolved, project_dir)
1789
- if state.phase in {"build", "ship", "shipped"}:
1735
+ if state.phase in {"build", "ship", "shipped"} or (
1736
+ project_dir == ".project" and _build_recovery().context(resolved, text)
1737
+ ):
1790
1738
  raise PipelineStateError("integration mode is locked when build starts")
1791
1739
  if scope == "default":
1792
1740
  integration = (
@@ -2022,49 +1970,18 @@ def _render_transition(
2022
1970
 
2023
1971
 
2024
1972
  def _roadmap_sections(text: str) -> dict[str, tuple[str, int, int]]:
2025
- lines = text.splitlines(keepends=True)
2026
- headings: list[tuple[str, str, int]] = []
2027
- for index, line in enumerate(lines):
2028
- match = ROADMAP_HEADING_RE.fullmatch(line.rstrip("\r\n"))
2029
- if match:
2030
- if int(match.group(1).removeprefix("M")) < 1:
2031
- raise PipelineStateError(f"ROADMAP.md has invalid milestone id: {match.group(1)}")
2032
- headings.append((match.group(1), match.group(2), index))
2033
- sections: dict[str, tuple[str, int, int]] = {}
2034
- for position, (milestone_id, slug, start) in enumerate(headings):
2035
- end = headings[position + 1][2] if position + 1 < len(headings) else len(lines)
2036
- if slug in sections:
2037
- raise PipelineStateError(f"ROADMAP.md repeats milestone slug: {slug}")
2038
- sections[slug] = (milestone_id, start, end)
2039
- return sections
2040
-
1973
+ try:
1974
+ return roadmap.strict_sections(text)
1975
+ except roadmap.RoadmapError as error:
1976
+ raise PipelineStateError(str(error)) from error
2041
1977
 
2042
1978
  def _replace_roadmap_field(
2043
- lines: list[str],
2044
- start: int,
2045
- end: int,
2046
- field: str,
2047
- expected: set[str],
2048
- value: str,
1979
+ lines: list[str], start: int, end: int, field: str, expected: set[str], value: str,
2049
1980
  ) -> None:
2050
- found: Optional[int] = None
2051
- for index in range(start, end):
2052
- raw = lines[index].rstrip("\r\n")
2053
- ending = lines[index][len(raw) :]
2054
- match = re.match(rf"^({re.escape(field)}:\s*)(\S+)(.*)$", raw)
2055
- if match is None:
2056
- continue
2057
- if found is not None:
2058
- raise PipelineStateError(f"ROADMAP.md repeats {field} in one milestone")
2059
- if match.group(2) not in expected:
2060
- raise PipelineStateError(
2061
- f"ROADMAP.md {field} is {match.group(2)}, expected {sorted(expected)}"
2062
- )
2063
- lines[index] = f"{match.group(1)}{value}{match.group(3)}{ending}"
2064
- found = index
2065
- if found is None:
2066
- raise PipelineStateError(f"ROADMAP.md milestone is missing {field}")
2067
-
1981
+ try:
1982
+ return roadmap.replace_field(lines, start, end, field, expected, value)
1983
+ except roadmap.RoadmapError as error:
1984
+ raise PipelineStateError(str(error)) from error
2068
1985
 
2069
1986
  def _git_path(repo: Path, name: str) -> Path:
2070
1987
  value = _run_git(repo, "rev-parse", "--git-path", name).stdout.strip()
@@ -2163,7 +2080,7 @@ def _specified_fields(arguments: argparse.Namespace, prefix: str) -> dict[str, O
2163
2080
  def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
2164
2081
  parser = argparse.ArgumentParser(description=__doc__)
2165
2082
  subparsers = parser.add_subparsers(dest="command", required=True)
2166
- for command in ("validate", "route", "status"):
2083
+ for command in ("validate", "route", "status", "prepare-build-recovery"):
2167
2084
  subparser = subparsers.add_parser(command)
2168
2085
  subparser.add_argument("--repo", required=True, type=Path)
2169
2086
  subparser.add_argument("--project-dir", default=".project")
@@ -2204,6 +2121,16 @@ def parse_args(argv: Optional[Sequence[str]] = None) -> argparse.Namespace:
2204
2121
 
2205
2122
  def main(argv: Optional[Sequence[str]] = None) -> int:
2206
2123
  args = parse_args(argv)
2124
+ if args.command in {"approve", "resume-checkpoint"}:
2125
+ if __package__:
2126
+ from . import state_checkpoint
2127
+ else:
2128
+ import state_checkpoint
2129
+ elif args.command == "promote-next":
2130
+ if __package__:
2131
+ from . import state_promote
2132
+ else:
2133
+ import state_promote
2207
2134
  try:
2208
2135
  if args.command == "validate":
2209
2136
  result = validate_state(args.repo, args.project_dir)
@@ -2211,6 +2138,10 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2211
2138
  result = route_state(args.repo, args.project_dir)
2212
2139
  elif args.command == "status":
2213
2140
  result = status_state(args.repo, args.project_dir)
2141
+ elif args.command == "prepare-build-recovery":
2142
+ if args.project_dir != ".project":
2143
+ raise PipelineStateError("build recovery requires the active track")
2144
+ result = _build_recovery().prepare(args.repo)
2214
2145
  elif args.command == "transition":
2215
2146
  result = transition_state(
2216
2147
  args.repo,
@@ -2224,7 +2155,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2224
2155
  raise PipelineStateError(
2225
2156
  "--expected-head is not accepted with --defer-checkpoint or --patch"
2226
2157
  )
2227
- result = defer_approval(
2158
+ result = state_checkpoint.defer_approval(
2228
2159
  args.repo,
2229
2160
  args.kind,
2230
2161
  args.project_dir,
@@ -2236,7 +2167,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2236
2167
  raise PipelineStateError(
2237
2168
  "approve requires --expected-head unless --defer-checkpoint or --patch"
2238
2169
  )
2239
- result = checkpoint_approval(
2170
+ result = state_checkpoint.checkpoint_approval(
2240
2171
  args.repo,
2241
2172
  args.kind,
2242
2173
  args.expected_head,
@@ -2244,7 +2175,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2244
2175
  args.milestone,
2245
2176
  )
2246
2177
  elif args.command == "resume-checkpoint":
2247
- result = resume_checkpoint(args.repo)
2178
+ result = state_checkpoint.resume_checkpoint(args.repo)
2248
2179
  elif args.command == "promote-next":
2249
2180
  if args.integrate is not None and (
2250
2181
  args.base is not None or args.landing is not None
@@ -2262,7 +2193,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2262
2193
  raise PipelineStateError(
2263
2194
  "promote-next requires --base or legacy --integrate"
2264
2195
  )
2265
- result = promote_next(
2196
+ result = state_promote.promote_next(
2266
2197
  args.repo,
2267
2198
  args.milestone,
2268
2199
  args.branch,
@@ -12,27 +12,47 @@ import re
12
12
  import stat
13
13
  import subprocess
14
14
  import sys
15
+
16
+ # Runtime helpers must not modify their immutable installation.
17
+ sys.dont_write_bytecode = True
15
18
  from pathlib import Path
16
19
  from typing import Optional, Sequence
17
20
 
18
- import _common
19
- import archive_milestone
20
- import detect_project
21
+ if __package__:
22
+ from . import _common, archive_milestone, detect_project
23
+ from .roadmap import (
24
+ RoadmapError as LookaheadError,
25
+ NoEligibleMilestone as NoEligibleLookahead,
26
+ roadmap_blocks,
27
+ milestone_block,
28
+ dependency_ids,
29
+ next_eligible_pending,
30
+ selection_payload,
31
+ select_lookahead,
32
+ roadmap_contract,
33
+ compare_roadmap_entry,
34
+ )
35
+ else:
36
+ from roadmap import (
37
+ RoadmapError as LookaheadError,
38
+ NoEligibleMilestone as NoEligibleLookahead,
39
+ roadmap_blocks,
40
+ milestone_block,
41
+ dependency_ids,
42
+ next_eligible_pending,
43
+ selection_payload,
44
+ select_lookahead,
45
+ roadmap_contract,
46
+ compare_roadmap_entry,
47
+ )
48
+ import _common
49
+ import archive_milestone
50
+ import detect_project
21
51
 
22
52
 
23
53
  PHASES = {"inspect", "define", "research", "decide", "plan"}
24
54
  STATUSES = {"active", "blocked", "done"}
25
55
  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
56
 
37
57
 
38
58
  def read_text(path: Path, description: str) -> str:
@@ -76,119 +96,6 @@ def state_fields(content: str) -> dict[str, str]:
76
96
  return fields
77
97
 
78
98
 
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
99
  def select_track_branch(content: str, track_state: str) -> dict:
193
100
  fields = state_fields(track_state)
194
101
  if fields.get("pipeline", "") != archive_milestone.PIPELINE_MARKER:
@@ -247,42 +154,6 @@ def snapshot_roadmap(source: Path, destination: Path) -> dict:
247
154
  }
248
155
 
249
156
 
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
157
  def git_commit_file(root: Path, commit: str, relative: str) -> str:
287
158
  require_directory(root, "repository")
288
159
  if not SHA_RE.fullmatch(commit):