@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
@@ -239,15 +239,10 @@ class ArchiveError(RuntimeError):
239
239
 
240
240
 
241
241
  PIPELINE_MARKER = _common.PIPELINE_MARKER
242
- PR_CREDIT_LINE = (
243
- "PR prepared with [GSD Path](https://github.com/open-gsd/gsd-path)."
244
- )
245
- GITHUB_HOST = "github.com"
246
242
 
247
243
 
248
- # discussion_validate and integration call back into this module through a
249
- # module reference and import the constants and ArchiveError above, so this
250
- # import must stay below those definitions.
244
+ # discussion_validate calls back into this module and imports its constants
245
+ # and ArchiveError, so this import must stay below those definitions.
251
246
  if __package__: # imported as scripts.archive_milestone
252
247
  from .discussion_validate import (
253
248
  record_sections,
@@ -280,42 +275,6 @@ if __package__: # imported as scripts.archive_milestone
280
275
  render_manifest,
281
276
  validate_manifest,
282
277
  )
283
- from .integration import (
284
- resolve_remote_default,
285
- live_remote_ref,
286
- publish_bound_branch,
287
- refresh_origin,
288
- optional_ref,
289
- integration_names,
290
- registered_worktree,
291
- remove_registered_worktree,
292
- delete_integration_branch,
293
- require_generated_integration_commit,
294
- discard_unpublished_stale_integration,
295
- create_integration_merge,
296
- require_annotated_tag,
297
- ensure_integration_tag,
298
- github_repository,
299
- require_github_authentication,
300
- github_api_json,
301
- require_pull_request_shape,
302
- require_pull_request_pages,
303
- pull_request_head_matches_repository,
304
- require_pull_request_identity,
305
- find_pull_request,
306
- pull_request_body,
307
- update_pull_request_body,
308
- create_pull_request,
309
- pull_request_tag_message,
310
- pull_request_tag_metadata,
311
- require_pull_request_merge,
312
- require_pull_request_merge_provenance,
313
- integrate_pull_request,
314
- integrate,
315
- find_integrate_commit,
316
- validate_integrated,
317
- require_published_integration,
318
- )
319
278
  else: # standalone script or sibling import
320
279
  if __name__ == "__main__":
321
280
  sys.modules.setdefault("archive_milestone", sys.modules[__name__])
@@ -351,42 +310,6 @@ else: # standalone script or sibling import
351
310
  render_manifest,
352
311
  validate_manifest,
353
312
  )
354
- from integration import (
355
- resolve_remote_default,
356
- live_remote_ref,
357
- publish_bound_branch,
358
- refresh_origin,
359
- optional_ref,
360
- integration_names,
361
- registered_worktree,
362
- remove_registered_worktree,
363
- delete_integration_branch,
364
- require_generated_integration_commit,
365
- discard_unpublished_stale_integration,
366
- create_integration_merge,
367
- require_annotated_tag,
368
- ensure_integration_tag,
369
- github_repository,
370
- require_github_authentication,
371
- github_api_json,
372
- require_pull_request_shape,
373
- require_pull_request_pages,
374
- pull_request_head_matches_repository,
375
- require_pull_request_identity,
376
- find_pull_request,
377
- pull_request_body,
378
- update_pull_request_body,
379
- create_pull_request,
380
- pull_request_tag_message,
381
- pull_request_tag_metadata,
382
- require_pull_request_merge,
383
- require_pull_request_merge_provenance,
384
- integrate_pull_request,
385
- integrate,
386
- find_integrate_commit,
387
- validate_integrated,
388
- require_published_integration,
389
- )
390
313
  except ImportError: # pragma: no cover - package import used by tests
391
314
  from scripts.discussion_validate import (
392
315
  record_sections,
@@ -419,42 +342,6 @@ else: # standalone script or sibling import
419
342
  render_manifest,
420
343
  validate_manifest,
421
344
  )
422
- from scripts.integration import (
423
- resolve_remote_default,
424
- live_remote_ref,
425
- publish_bound_branch,
426
- refresh_origin,
427
- optional_ref,
428
- integration_names,
429
- registered_worktree,
430
- remove_registered_worktree,
431
- delete_integration_branch,
432
- require_generated_integration_commit,
433
- discard_unpublished_stale_integration,
434
- create_integration_merge,
435
- require_annotated_tag,
436
- ensure_integration_tag,
437
- github_repository,
438
- require_github_authentication,
439
- github_api_json,
440
- require_pull_request_shape,
441
- require_pull_request_pages,
442
- pull_request_head_matches_repository,
443
- require_pull_request_identity,
444
- find_pull_request,
445
- pull_request_body,
446
- update_pull_request_body,
447
- create_pull_request,
448
- pull_request_tag_message,
449
- pull_request_tag_metadata,
450
- require_pull_request_merge,
451
- require_pull_request_merge_provenance,
452
- integrate_pull_request,
453
- integrate,
454
- find_integrate_commit,
455
- validate_integrated,
456
- require_published_integration,
457
- )
458
345
 
459
346
 
460
347
  @contextmanager
@@ -2238,6 +2125,11 @@ def parser() -> argparse.ArgumentParser:
2238
2125
  def main(argv: Optional[Sequence[str]] = None) -> int:
2239
2126
  arguments = parser().parse_args(argv)
2240
2127
  try:
2128
+ if arguments.command in {"validate-integrated", "refresh-origin", "integrate"}:
2129
+ if __package__:
2130
+ from . import integration
2131
+ else:
2132
+ import integration
2241
2133
  if arguments.command == "prepare":
2242
2134
  result = prepare(arguments.repo, arguments.slug)
2243
2135
  elif arguments.command == "render-manifest":
@@ -2247,11 +2139,11 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
2247
2139
  elif arguments.command == "abandon":
2248
2140
  result = abandon(arguments.repo, arguments.slug, arguments.reason)
2249
2141
  elif arguments.command == "validate-integrated":
2250
- result = validate_integrated(arguments.repo, arguments.slug)
2142
+ result = integration.validate_integrated(arguments.repo, arguments.slug)
2251
2143
  elif arguments.command == "refresh-origin":
2252
- result = refresh_origin(arguments.repo)
2144
+ result = integration.refresh_origin(arguments.repo)
2253
2145
  elif arguments.command == "integrate":
2254
- result = integrate(arguments.repo, arguments.slug)
2146
+ result = integration.integrate(arguments.repo, arguments.slug)
2255
2147
  else:
2256
2148
  result = validate(arguments.repo)
2257
2149
  except (ArchiveError, OSError) as error:
@@ -32,6 +32,7 @@ class BootstrapRequest:
32
32
  worktree: str
33
33
  branch: str
34
34
  description: Optional[str]
35
+ reuse_empty_worktree: bool = False
35
36
 
36
37
  @property
37
38
  def repository(self) -> str:
@@ -95,6 +96,8 @@ def request_from_arguments(arguments: argparse.Namespace) -> BootstrapRequest:
95
96
  if arguments.visibility not in {"public", "private", "internal"}:
96
97
  raise BootstrapError("visibility must be public, private, or internal")
97
98
 
99
+ if arguments.reuse_empty_worktree and arguments.worktree.is_symlink():
100
+ raise BootstrapError("linked worktree must not be a symlink")
98
101
  workspace = arguments.workspace.resolve()
99
102
  checkout = arguments.default_checkout.resolve()
100
103
  worktree = arguments.worktree.resolve()
@@ -116,6 +119,7 @@ def request_from_arguments(arguments: argparse.Namespace) -> BootstrapRequest:
116
119
  worktree=str(worktree),
117
120
  branch="gsd-path/M001",
118
121
  description=arguments.description,
122
+ reuse_empty_worktree=arguments.reuse_empty_worktree,
119
123
  )
120
124
 
121
125
 
@@ -148,7 +152,10 @@ def transaction_directory(request: BootstrapRequest, create: bool = False) -> Pa
148
152
 
149
153
 
150
154
  def request_payload(request: BootstrapRequest) -> dict:
151
- return {"schema": "gsd-path/new-github/v1", **asdict(request)}
155
+ payload = {"schema": "gsd-path/new-github/v1", **asdict(request)}
156
+ if not request.reuse_empty_worktree:
157
+ del payload["reuse_empty_worktree"]
158
+ return payload
152
159
 
153
160
 
154
161
  def read_journal(request: BootstrapRequest) -> Optional[dict]:
@@ -272,6 +279,16 @@ def transaction_stages(request: BootstrapRequest) -> dict:
272
279
  }
273
280
 
274
281
 
282
+ def reusable_empty_worktree(request: BootstrapRequest) -> bool:
283
+ path = request.worktree_path
284
+ return (
285
+ request.reuse_empty_worktree
286
+ and not path.is_symlink()
287
+ and path.is_dir()
288
+ and not any(path.iterdir())
289
+ )
290
+
291
+
275
292
  def preview(request: BootstrapRequest) -> dict:
276
293
  gh_authentication()
277
294
  stages = transaction_stages(request)
@@ -288,8 +305,12 @@ def preview(request: BootstrapRequest) -> dict:
288
305
  ("default checkout", request.checkout_path),
289
306
  ("linked worktree", request.worktree_path),
290
307
  ):
308
+ if path == request.worktree_path and reusable_empty_worktree(request):
309
+ continue
291
310
  if path.exists() or path.is_symlink():
292
- raise BootstrapError(f"{label} path already exists: {path}")
311
+ raise BootstrapError(
312
+ f"{label} path already exists: {path}; preserve it and choose another path"
313
+ )
293
314
  return {**request_payload(request), "mode": "create", "stages": stages}
294
315
 
295
316
 
@@ -448,7 +469,7 @@ def allowed_partial_project_status(worktree: Path) -> bool:
448
469
  def create_or_verify_worktree(request: BootstrapRequest, base: str) -> None:
449
470
  checkout = request.checkout_path
450
471
  worktree = request.worktree_path
451
- if not worktree.exists():
472
+ if not worktree.exists() or reusable_empty_worktree(request):
452
473
  branch_ref = f"refs/heads/{request.branch}"
453
474
  branch = run("git", "-C", str(checkout), "rev-parse", "--verify", branch_ref)
454
475
  if branch.returncode == 0:
@@ -680,6 +701,7 @@ def parser() -> argparse.ArgumentParser:
680
701
  argument_parser.add_argument("--default-checkout", type=Path, required=True)
681
702
  argument_parser.add_argument("--worktree", type=Path, required=True)
682
703
  argument_parser.add_argument("--description")
704
+ argument_parser.add_argument("--reuse-empty-worktree", action="store_true")
683
705
  argument_parser.add_argument("--state-template", type=Path)
684
706
  argument_parser.add_argument("--repository-template", type=Path)
685
707
  return argument_parser
@@ -0,0 +1,287 @@
1
+ # gsd-path project runtime
2
+ """Guard a blocked build's return to Define or Plan using its committed base."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ import re
8
+ from pathlib import Path
9
+
10
+
11
+ EVENTS = {
12
+ "define": "build intent corrections requested",
13
+ "plan": "build plan repair requested",
14
+ }
15
+ MARKER = "build recovery: "
16
+
17
+
18
+ def runtime():
19
+ if __package__:
20
+ from scripts import pipeline_state
21
+ else:
22
+ import pipeline_state
23
+ return pipeline_state
24
+
25
+
26
+ def checkpoint_runtime():
27
+ if __package__:
28
+ from scripts import state_checkpoint
29
+ else:
30
+ import state_checkpoint
31
+ return state_checkpoint
32
+
33
+
34
+ def context(repo: Path, text: str | None = None) -> dict | None:
35
+ state = runtime()
36
+ current, loaded, _ = state.load_state(repo)
37
+ text = loaded if text is None else text
38
+ recovery = None
39
+ for line in text.splitlines():
40
+ if " — " not in line:
41
+ continue
42
+ event = line.split(" — ", 2)[-1]
43
+ if event.startswith(MARKER):
44
+ try:
45
+ value = json.loads(event[len(MARKER):])
46
+ except ValueError as error:
47
+ raise state.PipelineStateError("invalid build recovery record") from error
48
+ if (not isinstance(value, dict) or set(value) != {"base", "branch", "kind"}
49
+ or value["kind"] not in EVENTS
50
+ or not isinstance(value["base"], str)
51
+ or not re.fullmatch(r"[0-9a-f]{40}", value["base"])):
52
+ raise state.PipelineStateError("invalid build recovery record")
53
+ recovery = {**value, "active": True} if value["branch"] == current.branch else None
54
+ elif event == "build started" and recovery:
55
+ recovery["active"] = False
56
+ if recovery:
57
+ if not state._is_ancestor(repo, recovery["base"], "HEAD"):
58
+ raise state.PipelineStateError("build recovery base is not an ancestor of HEAD")
59
+ source = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/STATE.md")
60
+ if source is None:
61
+ raise state.PipelineStateError("build recovery base has no STATE.md")
62
+ before = state._state_from_text(source)
63
+ if ((before.phase, before.status) != ("build", "blocked")
64
+ or before.branch != current.branch or before.milestone != current.milestone):
65
+ raise state.PipelineStateError("build recovery base does not prove this blocked milestone")
66
+ return recovery
67
+
68
+
69
+ def task_fields(text: str, label: str) -> dict:
70
+ if __package__:
71
+ from scripts.isolation import task_frontmatter
72
+ else:
73
+ from isolation import task_frontmatter
74
+ fields, error = task_frontmatter(text)
75
+ if fields is None:
76
+ raise runtime().PipelineStateError(f"{label}: {error}")
77
+ return fields
78
+
79
+
80
+ def settled_tasks(repo: Path) -> dict[str, tuple[str, dict]]:
81
+ """Read task ownership without requiring the graph that Plan must repair."""
82
+ state = runtime()
83
+ directory = repo / ".project/tasks"
84
+ if directory.is_symlink() or not directory.is_dir():
85
+ raise state.PipelineStateError("build recovery requires real task artifacts")
86
+ tasks = {}
87
+ for path in sorted(directory.iterdir()):
88
+ if not re.fullmatch(r"T[0-9]{3}-[a-z0-9][a-z0-9-]*\.md", path.name):
89
+ raise state.PipelineStateError(f"invalid recovery task path: {path.name}")
90
+ text = state._read_real_file(path, path.name)
91
+ fields = task_fields(text, path.name)
92
+ if fields.get("status") not in {"pending", "done"}:
93
+ raise state.PipelineStateError(f"settle task ownership before recovery: {path.name}")
94
+ if fields["status"] == "pending" and any(
95
+ fields.get(key) != "null" for key in ("agent", "base", "worktree", "task_branch")
96
+ ):
97
+ raise state.PipelineStateError(f"pending task retains dispatch metadata: {path.name}")
98
+ tasks[path.name] = (text, fields)
99
+ if not tasks:
100
+ raise state.PipelineStateError("build recovery requires task artifacts")
101
+ return tasks
102
+
103
+
104
+ def begin(repo: Path, before, after, event: str) -> dict:
105
+ state = runtime()
106
+ if (before.phase, before.status) != ("build", "blocked") or after.phase not in EVENTS:
107
+ raise state.PipelineStateError("build recovery requires build/blocked")
108
+ if event != EVENTS[after.phase]:
109
+ raise state.PipelineStateError(f"state transition requires event: {EVENTS[after.phase]}")
110
+ if before.archive is not None or before.branch != state._current_branch(repo):
111
+ raise state.PipelineStateError("build recovery requires the active bound branch")
112
+ if state._worktree_changes(repo):
113
+ raise state.PipelineStateError("checkpoint the blocked build before recovery")
114
+ if any(state.transaction_journals(repo).values()):
115
+ raise state.PipelineStateError("finish pending transactions before build recovery")
116
+ records = records_root(repo)
117
+ for directory in {path.parent.parent for path in records.rglob("attempt-*/state.json")}:
118
+ latest = max(directory.glob("attempt-*/state.json"), key=lambda path: int(path.parent.name.split("-")[1]))
119
+ record = state._read_json(latest)
120
+ if (record.get("cleanup_pending")
121
+ or (record.get("worktree") and record.get("branch") and not record.get("cleanup_complete"))
122
+ or record.get("outcome") not in {"landed", "collected", "blocked", "resolved", "redispatched"}):
123
+ raise state.PipelineStateError(f"settle dispatch records before recovery: {latest}")
124
+ tasks = settled_tasks(repo)
125
+ state._read_real_file(repo / ".project/plan/PLAN.md", "PLAN.md")
126
+ state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md")
127
+ base = state._run_git(repo, "rev-parse", "HEAD").stdout.strip()
128
+ done = [repo / ".project/tasks" / name for name, (_, fields) in tasks.items()
129
+ if fields["status"] == "done"]
130
+ if done:
131
+ if __package__:
132
+ from scripts.isolation import verify_landed_task_files
133
+ else:
134
+ from isolation import verify_landed_task_files
135
+ verify_landed_task_files(repo, done, ".project/tasks", base)
136
+ return {"kind": after.phase, "base": base, "branch": before.branch}
137
+
138
+
139
+ def records_root(repo: Path) -> Path:
140
+ if __package__:
141
+ from scripts import isolation
142
+ else:
143
+ import isolation
144
+ root = isolation.common_git_dir(repo) / "gsd-path/dispatch" / isolation.require_bound(repo).replace("/", "-")
145
+ recovery = context(repo)
146
+ return root / recovery["base"] if recovery else root
147
+
148
+
149
+ def review_backup(repo: Path, recovery: dict) -> Path:
150
+ return repo / ".project/plan" / f"build-recovery-{recovery['base']}" / "review"
151
+
152
+
153
+ def validate_review_backup(repo: Path, recovery: dict, directory: Path) -> None:
154
+ state = runtime()
155
+ if directory.exists():
156
+ checkpoint_runtime()._tree_digest(directory) # Reject symlinks and special files before reading.
157
+ expected = {}
158
+ for path in state._run_git(repo, "ls-tree", "-r", "--name-only", "-z",
159
+ recovery["base"], "--", ".project/review").stdout.split("\0"):
160
+ if path:
161
+ expected[str(Path(path).relative_to(".project/review"))] = checkpoint_runtime()._git_text_at(repo, recovery["base"], path)
162
+ actual = {str(path.relative_to(directory)): path.read_text(encoding="utf-8")
163
+ for path in directory.rglob("*") if path.is_file()} if directory.exists() else {}
164
+ if actual != expected:
165
+ raise state.PipelineStateError("review backup differs from the recovery base")
166
+
167
+
168
+ def prepare(repo: Path) -> dict:
169
+ """Move old reviews once; the durable STATE record owns interruption recovery."""
170
+ state = runtime()
171
+ repo = state._repo_root(repo)
172
+ with state._state_lock(repo / ".project"):
173
+ recovery = context(repo)
174
+ if not recovery or not recovery["active"]:
175
+ raise state.PipelineStateError("no active build recovery")
176
+ target = review_backup(repo, recovery)
177
+ if (repo / ".project/plan").is_symlink() or target.parent.is_symlink() or target.is_symlink():
178
+ raise state.PipelineStateError("unsafe recovery review backup")
179
+ source = repo / ".project/review"
180
+ if source.is_symlink():
181
+ raise state.PipelineStateError("unsafe recovery review directory")
182
+ if not target.exists():
183
+ validate_review_backup(repo, recovery, source)
184
+ target.parent.mkdir(exist_ok=True)
185
+ if source.exists():
186
+ source.rename(target)
187
+ else:
188
+ target.mkdir()
189
+ validate_review_backup(repo, recovery, target)
190
+ source.mkdir(exist_ok=True)
191
+ return {"status": "prepared", "recovery": recovery, "path": str(target)}
192
+
193
+
194
+ def validate_plan(repo: Path) -> None:
195
+ state = runtime()
196
+ recovery = context(repo)
197
+ if not recovery or not recovery["active"]:
198
+ return
199
+ if not review_backup(repo, recovery).is_dir():
200
+ raise state.PipelineStateError("run prepare-build-recovery before planning")
201
+ validate_review_backup(repo, recovery, review_backup(repo, recovery))
202
+ source = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
203
+ current = settled_tasks(repo)
204
+ done = set()
205
+ for _, (path, text) in source.items():
206
+ fields = task_fields(text, path)
207
+ if fields.get("status") == "done":
208
+ name = Path(path).name
209
+ done.add(name)
210
+ if name not in current or current[name][0] != text:
211
+ raise state.PipelineStateError(f"build recovery must preserve landed task: {name}")
212
+ if any(fields["status"] == "done" and name not in done
213
+ for name, (_, fields) in current.items()):
214
+ raise state.PipelineStateError("build recovery cannot manufacture landed tasks")
215
+ if recovery["kind"] == "plan":
216
+ intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
217
+ if state._read_real_file(repo / ".project/intent/INTENT.md", "INTENT.md") != intent:
218
+ raise state.PipelineStateError("plan repair cannot change approved intent")
219
+ unchanged = unchanged_waves(repo, recovery)
220
+ for path in (repo / ".project/review").iterdir():
221
+ match = re.match(r"wave-([1-9]\d*)[.]", path.name)
222
+ if match and int(match[1]) not in unchanged:
223
+ raise state.PipelineStateError("changed wave must be reviewed after build resumes")
224
+
225
+
226
+ def unchanged_waves(repo: Path, recovery: dict) -> set[int]:
227
+ """Reuse evidence only when its intent, wave contract, and task bytes match."""
228
+ state = runtime()
229
+ project = repo / ".project"
230
+ old_intent = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/intent/INTENT.md")
231
+ if state._read_real_file(project / "intent/INTENT.md", "INTENT.md") != old_intent:
232
+ return set()
233
+ old_plan = checkpoint_runtime()._git_text_at(repo, recovery["base"], ".project/plan/PLAN.md") or ""
234
+ new_plan = state._read_real_file(project / "plan/PLAN.md", "PLAN.md")
235
+
236
+ def sections(text):
237
+ return {match[1]: match[2] for match in re.finditer(
238
+ r"(?ms)^## ([^\n]+)\n(.*?)(?=^## |\Z)", text,
239
+ )}
240
+
241
+ old_sections, new_sections = sections(old_plan), sections(new_plan)
242
+ # Non-wave settings govern every wave, including coverage and surfaces.
243
+ if ({k: v for k, v in old_sections.items() if not k.startswith("Wave ")}
244
+ != {k: v for k, v in new_sections.items() if not k.startswith("Wave ")}):
245
+ return set()
246
+ old_tasks = checkpoint_runtime()._task_contracts_at(repo, recovery["base"], ".project/tasks")
247
+ current = settled_tasks(repo)
248
+ unchanged = set()
249
+ for title, body in new_sections.items():
250
+ match = re.match(r"Wave ([1-9]\d*)\b", title)
251
+ if not match or old_sections.get(title) != body:
252
+ continue
253
+ wave = match[1]
254
+ before = {Path(path).name: text for path, text in old_tasks.values()
255
+ if task_fields(text, path).get("wave") == wave}
256
+ after = {name: text for name, (text, fields) in current.items() if fields.get("wave") == wave}
257
+ if before and before == after:
258
+ unchanged.add(int(wave))
259
+ return unchanged
260
+
261
+
262
+ def restore_unchanged_reviews(repo: Path) -> None:
263
+ recovery = context(repo)
264
+ if not recovery or not recovery["active"]:
265
+ return
266
+ unchanged = unchanged_waves(repo, recovery)
267
+ for path in review_backup(repo, recovery).iterdir():
268
+ match = re.match(r"wave-([1-9]\d*)[.]", path.name)
269
+ if match and int(match[1]) in unchanged:
270
+ destination = repo / ".project/review" / path.name
271
+ content = path.read_text(encoding="utf-8")
272
+ if not destination.exists() or runtime()._read_real_file(destination, destination.name) != content:
273
+ runtime()._atomic_write(destination, content)
274
+
275
+
276
+ def inventory_checkpoint(repo: Path, head: str) -> bool:
277
+ state = runtime()
278
+ if state._run_git(repo, "show", "-s", "--format=%s", head).stdout.strip() != state.PLAN_APPROVAL_SUBJECT:
279
+ return False
280
+ text = checkpoint_runtime()._git_text_at(repo, head, ".project/STATE.md")
281
+ if text is None:
282
+ return False
283
+ approved = state._state_from_text(text)
284
+ if (approved.phase, approved.status) != ("plan", "done"):
285
+ return False
286
+ recovery = context(repo, text)
287
+ return bool(recovery and recovery["active"])
@@ -21,7 +21,8 @@ are rejected.
21
21
  The frozen inventory travels in the dispatch brief; pass it with
22
22
  `--inventory FILE` (one POSIX path per line, `-` for stdin). Without it the
23
23
  gate derives the inventory from tracked Markdown files, excluding `.git`,
24
- `node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*` bundles,
24
+ `node_modules`, `.project/archive/**`, installed `*/skills/gsd-path*` and
25
+ `*/skills/path` bundles,
25
26
  the audit itself, and — unless the
26
27
  audit declares `Alignment mode: yes` — everything else under `.project/`.
27
28
  """
@@ -100,9 +101,10 @@ def _not_placeholder(value: str, label: str) -> None:
100
101
 
101
102
 
102
103
  def _installed_skill(parts: Sequence[str]) -> bool:
103
- """True for files inside an installed GSD Path skill bundle (<host>/skills/gsd-path*/…)."""
104
+ """Exclude installed GSD Path bundles, including the path router alias."""
104
105
  return any(
105
- parts[index] == "skills" and parts[index + 1].startswith("gsd-path")
106
+ parts[index] == "skills" and
107
+ (parts[index + 1].startswith("gsd-path") or parts[index + 1] == "path")
106
108
  for index in range(len(parts) - 2)
107
109
  )
108
110
 
@@ -1462,7 +1462,7 @@ def validate_wave_evidence(
1462
1462
  raise HandoffError(f"{name} Cycle field does not match its filename")
1463
1463
  if _line_value(text, "Depth:") != expected_depth:
1464
1464
  raise HandoffError(f"{name} Depth does not match PLAN.md")
1465
- lens_fields = re.findall(r"(?m)^Lens:\s*(\S.*?)\s*$", text)
1465
+ lens_fields = re.findall(r"(?m)^Lens:[ \t]*(\S.*?)[ \t]*$", text)
1466
1466
  if lens is None and lens_fields:
1467
1467
  raise HandoffError(f"{name} must not declare a review lens")
1468
1468
  if lens is not None and lens_fields != [lens]:
@@ -1439,7 +1439,7 @@ def write_state_anchored(
1439
1439
 
1440
1440
 
1441
1441
  def initialize(
1442
- repo: Path, template: Path, phase: Optional[str] = None
1442
+ repo: Path, template: Path, phase: Optional[str] = None, *, require_git: bool = False
1443
1443
  ) -> dict:
1444
1444
  if phase is not None and phase not in {"inspect", "define"}:
1445
1445
  raise DetectError(f"initialize phase must be inspect or define: {phase}")
@@ -1458,6 +1458,13 @@ def initialize(
1458
1458
  if payload["verdict"] not in {"brownfield", "greenfield"}:
1459
1459
  payload["wrote_state"] = False
1460
1460
  return payload
1461
+ if require_git:
1462
+ git = subprocess.run(
1463
+ ["git", "-C", str(root), "rev-parse", "--is-inside-work-tree"],
1464
+ env=git_environment(), capture_output=True, text=True, check=False,
1465
+ )
1466
+ if git.returncode != 0 or git.stdout.strip() != "true":
1467
+ return {**payload, "route": "setup-repository", "wrote_state": False}
1461
1468
  expected = "inspect" if payload["verdict"] == "brownfield" else "define"
1462
1469
  if phase is None:
1463
1470
  phase = expected
@@ -1491,6 +1498,7 @@ def parser() -> argparse.ArgumentParser:
1491
1498
  result.add_argument("--repo", type=Path, required=True)
1492
1499
  result.add_argument("--template", type=Path)
1493
1500
  result.add_argument("--phase", choices=("inspect", "define"))
1501
+ result.add_argument("--require-git", action="store_true")
1494
1502
  return result
1495
1503
 
1496
1504
 
@@ -1503,7 +1511,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
1503
1511
  if arguments.template is None:
1504
1512
  raise DetectError("initialize requires --template")
1505
1513
  payload = initialize(
1506
- arguments.repo, arguments.template, arguments.phase
1514
+ arguments.repo, arguments.template, arguments.phase,
1515
+ require_git=arguments.require_git
1507
1516
  )
1508
1517
  return emit(payload, 2 if payload.get("error") else 0)
1509
1518
  raise DetectError(f"unknown command: {arguments.command}")
@@ -31,13 +31,14 @@ from typing import BinaryIO, Callable, Dict, List, Optional
31
31
 
32
32
  try:
33
33
  from scripts import (_common, archive_milestone, build_state, discussion_validate, isolation,
34
- pipeline_state, review_findings, review_panel, workflow_run)
34
+ pipeline_state, review_findings, review_panel, workflow_run, task_context)
35
35
  from scripts import check_handoffs as contracts
36
36
  except ImportError: # bundled copy inside a skill's scripts directory
37
37
  import _common
38
38
  import archive_milestone
39
39
  import build_state
40
40
  import check_handoffs as contracts
41
+ import task_context
41
42
  import discussion_validate
42
43
  import isolation
43
44
  import pipeline_state
@@ -94,7 +95,7 @@ def milestone_slug(primary: Path) -> str:
94
95
 
95
96
 
96
97
  def records_root(primary: Path) -> Path:
97
- return isolation.common_git_dir(primary) / "gsd-path" / "dispatch" / milestone_slug(primary)
98
+ return pipeline_state._build_recovery().records_root(primary)
98
99
 
99
100
 
100
101
  def budget_ledger(primary: Path) -> Path:
@@ -246,6 +247,7 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
246
247
  if state.get("answered"):
247
248
  lines.append(f"The task Log now records an `{ANSWER_MARK}` to your earlier question; "
248
249
  "continue from it.")
250
+ lines.append(task_context.render(worktree, worktree / str(state['task_file'])))
249
251
  return "\n".join(lines) + "\n"
250
252
 
251
253
 
@@ -114,7 +114,8 @@ def message_of(message_file):
114
114
  lines.pop(0)
115
115
  if not lines:
116
116
  return "", ""
117
- return lines[0].strip(), "\n".join(lines[1:]).strip("\n")
117
+ subject_end = lines.index("") if "" in lines else len(lines)
118
+ return " ".join(line.strip() for line in lines[:subject_end]), "\n".join(lines[subject_end:]).strip("\n")
118
119
 
119
120
 
120
121
  def archive_root(path):
@@ -15,6 +15,7 @@ export const GUARD_SCRIPTS = ["guard_hook.py", "git_guard.py"];
15
15
  export const GUARD_MARKER = "gsd-path guard";
16
16
  export const PROJECT_RUNTIME_SCRIPTS = [
17
17
  "pipeline_state.py",
18
+ "roadmap.py",
18
19
  "check_handoffs.py",
19
20
  "check_task_briefs.py",
20
21
  "isolation.py",
@@ -27,6 +28,7 @@ export const PROJECT_RUNTIME_SCRIPTS = [
27
28
  "archive_milestone.py",
28
29
  "review_panel.py",
29
30
  "_common.py",
31
+ "build_recovery.py",
30
32
  "state_checkpoint.py",
31
33
  "state_promote.py",
32
34
  "discussion_validate.py",