@opengsd/gsd-path 1.1.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 (577) hide show
  1. package/AGENTS.md +5 -2
  2. package/CHANGELOG.md +68 -0
  3. package/DOCS.md +69 -1
  4. package/FULL.md +3 -2
  5. package/HOOKS.md +18 -9
  6. package/QUICK.md +5 -3
  7. package/README.md +72 -5
  8. package/RUNTIME.md +9 -4
  9. package/UPDATE.md +30 -11
  10. package/WORKFLOW.md +5 -2
  11. package/package.json +9 -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 +6 -19
  21. package/scripts/_common.py +5 -0
  22. package/scripts/archive_milestone.py +20 -6
  23. package/scripts/bootstrap_repository.py +8 -1
  24. package/scripts/build_recovery.py +5 -0
  25. package/scripts/build_state.py +2 -0
  26. package/scripts/check_docs_audit.py +2 -0
  27. package/scripts/check_handoffs.py +6 -0
  28. package/scripts/check_task_briefs.py +3 -0
  29. package/scripts/detect_project.py +11 -1
  30. package/scripts/discussion_records.py +3 -0
  31. package/scripts/discussion_validate.py +8 -3
  32. package/scripts/dispatch_driver.py +230 -21
  33. package/scripts/git_guard.py +12 -4
  34. package/scripts/guard_hook.py +11 -1
  35. package/scripts/install.mjs +52 -1448
  36. package/scripts/install.py +155 -190
  37. package/scripts/integration.py +28 -8
  38. package/scripts/isolation.py +15 -5
  39. package/scripts/jev_review.py +138 -0
  40. package/scripts/lean_verification.py +2 -0
  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 +3 -0
  45. package/scripts/pipeline_git.py +3 -0
  46. package/scripts/pipeline_state.py +4 -1
  47. package/scripts/pipeline_undo.py +3 -0
  48. package/scripts/promote_lookahead.py +3 -0
  49. package/scripts/review_panel.py +11 -0
  50. package/scripts/roadmap.py +5 -0
  51. package/scripts/runtime_store.py +278 -0
  52. package/scripts/skill-resources.json +104 -4
  53. package/scripts/state_checkpoint.py +5 -0
  54. package/scripts/state_promote.py +5 -0
  55. package/scripts/status_runtime.py +92 -6
  56. package/scripts/workflow_run.py +12 -1
  57. package/scripts/worktree_paths.py +94 -0
  58. package/skills/gsd-path/BUILD.md +7 -0
  59. package/skills/gsd-path/DECIDE.md +2 -0
  60. package/skills/gsd-path/DEFINE.md +13 -7
  61. package/skills/gsd-path/DOCS-AUDIT.md +2 -0
  62. package/skills/gsd-path/INSPECT.md +2 -0
  63. package/skills/gsd-path/PLAN.md +4 -2
  64. package/skills/gsd-path/RESEARCH.md +2 -0
  65. package/skills/gsd-path/ROADMAP.md +2 -0
  66. package/skills/gsd-path/SHIP.md +24 -13
  67. package/skills/gsd-path/SKILL.md +23 -9
  68. package/skills/gsd-path/references/build-native.md +15 -7
  69. package/skills/gsd-path/references/config.md +57 -0
  70. package/skills/gsd-path/references/dispatch.md +6 -19
  71. package/skills/gsd-path/references/jev-review.md +82 -0
  72. package/skills/gsd-path/references/model-policy.md +139 -0
  73. package/skills/gsd-path/references/reviewer-panel.md +1 -1
  74. package/skills/gsd-path/references/reviewer.md +2 -0
  75. package/skills/gsd-path/references/runtime-selection.md +25 -0
  76. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  77. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  78. package/skills/gsd-path/scripts/_common.py +5 -0
  79. package/skills/gsd-path/scripts/archive_milestone.py +20 -6
  80. package/skills/gsd-path/scripts/bootstrap_repository.py +8 -1
  81. package/skills/gsd-path/scripts/build_recovery.py +5 -0
  82. package/skills/gsd-path/scripts/build_state.py +2 -0
  83. package/skills/gsd-path/scripts/check_docs_audit.py +2 -0
  84. package/skills/gsd-path/scripts/check_handoffs.py +6 -0
  85. package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
  86. package/skills/gsd-path/scripts/detect_project.py +11 -1
  87. package/skills/gsd-path/scripts/discussion_records.py +3 -0
  88. package/skills/gsd-path/scripts/discussion_validate.py +8 -3
  89. package/skills/gsd-path/scripts/dispatch_driver.py +230 -21
  90. package/skills/gsd-path/scripts/git_guard.py +768 -0
  91. package/skills/gsd-path/scripts/integration.py +28 -8
  92. package/skills/gsd-path/scripts/isolation.py +15 -5
  93. package/skills/gsd-path/scripts/jev_review.py +138 -0
  94. package/skills/gsd-path/scripts/lean_verification.py +2 -0
  95. package/skills/gsd-path/scripts/loop_run.py +2 -0
  96. package/skills/gsd-path/scripts/model_policy.py +279 -0
  97. package/skills/gsd-path/scripts/path_config.py +248 -0
  98. package/skills/gsd-path/scripts/pipeline_git.py +3 -0
  99. package/skills/gsd-path/scripts/pipeline_state.py +4 -1
  100. package/skills/gsd-path/scripts/promote_lookahead.py +3 -0
  101. package/skills/gsd-path/scripts/review_panel.py +11 -0
  102. package/skills/gsd-path/scripts/roadmap.py +5 -0
  103. package/skills/gsd-path/scripts/state_checkpoint.py +5 -0
  104. package/skills/gsd-path/scripts/state_promote.py +5 -0
  105. package/skills/gsd-path/scripts/status_runtime.py +208 -0
  106. package/skills/gsd-path/scripts/workflow_run.py +12 -1
  107. package/skills/gsd-path/scripts/worktree_paths.py +94 -0
  108. package/skills/gsd-path/templates/skeptic.md +1 -1
  109. package/skills/gsd-path/templates/task.md +2 -0
  110. package/skills/gsd-path/templates/wave-panel.md +1 -1
  111. package/skills/gsd-path-build/SKILL.md +7 -0
  112. package/skills/gsd-path-build/references/build-native.md +15 -7
  113. package/skills/gsd-path-build/references/dispatch.md +6 -19
  114. package/skills/gsd-path-build/references/jev-review.md +82 -0
  115. package/skills/gsd-path-build/references/model-policy.md +139 -0
  116. package/skills/gsd-path-build/references/reviewer-panel.md +1 -1
  117. package/skills/gsd-path-build/references/reviewer.md +2 -0
  118. package/skills/gsd-path-build/references/runtime-selection.md +25 -0
  119. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  120. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  121. package/skills/gsd-path-build/scripts/_common.py +5 -0
  122. package/skills/gsd-path-build/scripts/archive_milestone.py +20 -6
  123. package/skills/gsd-path-build/scripts/build_recovery.py +5 -0
  124. package/skills/gsd-path-build/scripts/build_state.py +2 -0
  125. package/skills/gsd-path-build/scripts/check_handoffs.py +6 -0
  126. package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
  127. package/skills/gsd-path-build/scripts/detect_project.py +11 -1
  128. package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
  129. package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
  130. package/skills/gsd-path-build/scripts/dispatch_driver.py +230 -21
  131. package/skills/gsd-path-build/scripts/integration.py +28 -8
  132. package/skills/gsd-path-build/scripts/isolation.py +15 -5
  133. package/skills/gsd-path-build/scripts/jev_review.py +138 -0
  134. package/skills/gsd-path-build/scripts/loop_run.py +2 -0
  135. package/skills/gsd-path-build/scripts/model_policy.py +279 -0
  136. package/skills/gsd-path-build/scripts/path_config.py +248 -0
  137. package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
  138. package/skills/gsd-path-build/scripts/pipeline_state.py +4 -1
  139. package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -0
  140. package/skills/gsd-path-build/scripts/review_panel.py +11 -0
  141. package/skills/gsd-path-build/scripts/roadmap.py +5 -0
  142. package/skills/gsd-path-build/scripts/state_checkpoint.py +5 -0
  143. package/skills/gsd-path-build/scripts/state_promote.py +5 -0
  144. package/skills/gsd-path-build/scripts/workflow_run.py +12 -1
  145. package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
  146. package/skills/gsd-path-build/templates/skeptic.md +1 -1
  147. package/skills/gsd-path-build/templates/task.md +2 -0
  148. package/skills/gsd-path-build/templates/wave-panel.md +1 -1
  149. package/skills/gsd-path-decide/SKILL.md +2 -0
  150. package/skills/gsd-path-decide/references/dispatch.md +6 -19
  151. package/skills/gsd-path-decide/references/model-policy.md +139 -0
  152. package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
  153. package/skills/gsd-path-decide/scripts/_common.py +5 -0
  154. package/skills/gsd-path-decide/scripts/archive_milestone.py +20 -6
  155. package/skills/gsd-path-decide/scripts/build_recovery.py +5 -0
  156. package/skills/gsd-path-decide/scripts/check_handoffs.py +6 -0
  157. package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
  158. package/skills/gsd-path-decide/scripts/detect_project.py +11 -1
  159. package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
  160. package/skills/gsd-path-decide/scripts/discussion_validate.py +8 -3
  161. package/skills/gsd-path-decide/scripts/integration.py +28 -8
  162. package/skills/gsd-path-decide/scripts/isolation.py +15 -5
  163. package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
  164. package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
  165. package/skills/gsd-path-decide/scripts/path_config.py +248 -0
  166. package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
  167. package/skills/gsd-path-decide/scripts/pipeline_state.py +4 -1
  168. package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -0
  169. package/skills/gsd-path-decide/scripts/review_panel.py +11 -0
  170. package/skills/gsd-path-decide/scripts/roadmap.py +5 -0
  171. package/skills/gsd-path-decide/scripts/state_checkpoint.py +5 -0
  172. package/skills/gsd-path-decide/scripts/state_promote.py +5 -0
  173. package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
  174. package/skills/gsd-path-define/SKILL.md +13 -7
  175. package/skills/gsd-path-define/references/runtime-selection.md +25 -0
  176. package/skills/gsd-path-define/scripts/_common.py +5 -0
  177. package/skills/gsd-path-define/scripts/archive_milestone.py +20 -6
  178. package/skills/gsd-path-define/scripts/build_recovery.py +5 -0
  179. package/skills/gsd-path-define/scripts/check_handoffs.py +6 -0
  180. package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
  181. package/skills/gsd-path-define/scripts/detect_project.py +11 -1
  182. package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
  183. package/skills/gsd-path-define/scripts/discussion_validate.py +8 -3
  184. package/skills/gsd-path-define/scripts/integration.py +28 -8
  185. package/skills/gsd-path-define/scripts/isolation.py +15 -5
  186. package/skills/gsd-path-define/scripts/loop_run.py +839 -0
  187. package/skills/gsd-path-define/scripts/model_policy.py +279 -0
  188. package/skills/gsd-path-define/scripts/path_config.py +248 -0
  189. package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
  190. package/skills/gsd-path-define/scripts/pipeline_state.py +4 -1
  191. package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -0
  192. package/skills/gsd-path-define/scripts/review_panel.py +11 -0
  193. package/skills/gsd-path-define/scripts/roadmap.py +5 -0
  194. package/skills/gsd-path-define/scripts/state_checkpoint.py +5 -0
  195. package/skills/gsd-path-define/scripts/state_promote.py +5 -0
  196. package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
  197. package/skills/gsd-path-discuss/SKILL.md +2 -0
  198. package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
  199. package/skills/gsd-path-discuss/scripts/_common.py +5 -0
  200. package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
  201. package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
  202. package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
  203. package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
  204. package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
  205. package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
  206. package/skills/gsd-path-docs-audit/SKILL.md +2 -0
  207. package/skills/gsd-path-docs-audit/references/dispatch.md +6 -19
  208. package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
  209. package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
  210. package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
  211. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +20 -6
  212. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +5 -0
  213. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +2 -0
  214. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +6 -0
  215. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
  216. package/skills/gsd-path-docs-audit/scripts/detect_project.py +11 -1
  217. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
  218. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +8 -3
  219. package/skills/gsd-path-docs-audit/scripts/integration.py +28 -8
  220. package/skills/gsd-path-docs-audit/scripts/isolation.py +15 -5
  221. package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
  222. package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
  223. package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
  224. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
  225. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +4 -1
  226. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -0
  227. package/skills/gsd-path-docs-audit/scripts/review_panel.py +11 -0
  228. package/skills/gsd-path-docs-audit/scripts/roadmap.py +5 -0
  229. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +5 -0
  230. package/skills/gsd-path-docs-audit/scripts/state_promote.py +5 -0
  231. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
  232. package/skills/gsd-path-forensics/SKILL.md +9 -5
  233. package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
  234. package/skills/gsd-path-forensics/scripts/_common.py +5 -0
  235. package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
  236. package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
  237. package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
  238. package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
  239. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +3 -0
  240. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +3 -0
  241. package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
  242. package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
  243. package/skills/gsd-path-inspect/SKILL.md +2 -0
  244. package/skills/gsd-path-inspect/references/dispatch.md +6 -19
  245. package/skills/gsd-path-inspect/references/model-policy.md +139 -0
  246. package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
  247. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  248. package/skills/gsd-path-inspect/scripts/_common.py +5 -0
  249. package/skills/gsd-path-inspect/scripts/archive_milestone.py +20 -6
  250. package/skills/gsd-path-inspect/scripts/build_recovery.py +5 -0
  251. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +2 -0
  252. package/skills/gsd-path-inspect/scripts/check_handoffs.py +6 -0
  253. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
  254. package/skills/gsd-path-inspect/scripts/detect_project.py +11 -1
  255. package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
  256. package/skills/gsd-path-inspect/scripts/discussion_validate.py +8 -3
  257. package/skills/gsd-path-inspect/scripts/integration.py +28 -8
  258. package/skills/gsd-path-inspect/scripts/isolation.py +15 -5
  259. package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
  260. package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
  261. package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
  262. package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
  263. package/skills/gsd-path-inspect/scripts/pipeline_state.py +4 -1
  264. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -0
  265. package/skills/gsd-path-inspect/scripts/review_panel.py +11 -0
  266. package/skills/gsd-path-inspect/scripts/roadmap.py +5 -0
  267. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +5 -0
  268. package/skills/gsd-path-inspect/scripts/state_promote.py +5 -0
  269. package/skills/gsd-path-inspect/scripts/workflow_run.py +12 -1
  270. package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
  271. package/skills/gsd-path-loop/SKILL.md +2 -0
  272. package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
  273. package/skills/gsd-path-loop/scripts/_common.py +5 -0
  274. package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
  275. package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
  276. package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
  277. package/skills/gsd-path-loop/scripts/path_config.py +248 -0
  278. package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
  279. package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
  280. package/skills/gsd-path-migrate/SKILL.md +2 -0
  281. package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
  282. package/skills/gsd-path-migrate/scripts/_common.py +185 -0
  283. package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
  284. package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
  285. package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
  286. package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
  287. package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
  288. package/skills/gsd-path-plan/SKILL.md +4 -2
  289. package/skills/gsd-path-plan/references/dispatch.md +6 -19
  290. package/skills/gsd-path-plan/references/model-policy.md +139 -0
  291. package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
  292. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  293. package/skills/gsd-path-plan/scripts/_common.py +5 -0
  294. package/skills/gsd-path-plan/scripts/archive_milestone.py +20 -6
  295. package/skills/gsd-path-plan/scripts/build_recovery.py +5 -0
  296. package/skills/gsd-path-plan/scripts/check_handoffs.py +6 -0
  297. package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
  298. package/skills/gsd-path-plan/scripts/detect_project.py +11 -1
  299. package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
  300. package/skills/gsd-path-plan/scripts/discussion_validate.py +8 -3
  301. package/skills/gsd-path-plan/scripts/integration.py +28 -8
  302. package/skills/gsd-path-plan/scripts/isolation.py +15 -5
  303. package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
  304. package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
  305. package/skills/gsd-path-plan/scripts/path_config.py +248 -0
  306. package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
  307. package/skills/gsd-path-plan/scripts/pipeline_state.py +4 -1
  308. package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -0
  309. package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
  310. package/skills/gsd-path-plan/scripts/roadmap.py +5 -0
  311. package/skills/gsd-path-plan/scripts/state_checkpoint.py +5 -0
  312. package/skills/gsd-path-plan/scripts/state_promote.py +5 -0
  313. package/skills/gsd-path-plan/scripts/workflow_run.py +12 -1
  314. package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
  315. package/skills/gsd-path-plan/templates/task.md +2 -0
  316. package/skills/gsd-path-research/SKILL.md +2 -0
  317. package/skills/gsd-path-research/references/dispatch.md +6 -19
  318. package/skills/gsd-path-research/references/model-policy.md +139 -0
  319. package/skills/gsd-path-research/references/runtime-selection.md +25 -0
  320. package/skills/gsd-path-research/scripts/_common.py +5 -0
  321. package/skills/gsd-path-research/scripts/archive_milestone.py +20 -6
  322. package/skills/gsd-path-research/scripts/build_recovery.py +5 -0
  323. package/skills/gsd-path-research/scripts/check_handoffs.py +6 -0
  324. package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
  325. package/skills/gsd-path-research/scripts/detect_project.py +11 -1
  326. package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
  327. package/skills/gsd-path-research/scripts/discussion_validate.py +8 -3
  328. package/skills/gsd-path-research/scripts/integration.py +28 -8
  329. package/skills/gsd-path-research/scripts/isolation.py +15 -5
  330. package/skills/gsd-path-research/scripts/loop_run.py +839 -0
  331. package/skills/gsd-path-research/scripts/model_policy.py +279 -0
  332. package/skills/gsd-path-research/scripts/path_config.py +248 -0
  333. package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
  334. package/skills/gsd-path-research/scripts/pipeline_state.py +4 -1
  335. package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -0
  336. package/skills/gsd-path-research/scripts/review_panel.py +11 -0
  337. package/skills/gsd-path-research/scripts/roadmap.py +5 -0
  338. package/skills/gsd-path-research/scripts/state_checkpoint.py +5 -0
  339. package/skills/gsd-path-research/scripts/state_promote.py +5 -0
  340. package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
  341. package/skills/gsd-path-roadmap/SKILL.md +2 -0
  342. package/skills/gsd-path-roadmap/references/dispatch.md +6 -19
  343. package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
  344. package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
  345. package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
  346. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +20 -6
  347. package/skills/gsd-path-roadmap/scripts/build_recovery.py +5 -0
  348. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +6 -0
  349. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
  350. package/skills/gsd-path-roadmap/scripts/detect_project.py +11 -1
  351. package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
  352. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
  353. package/skills/gsd-path-roadmap/scripts/integration.py +28 -8
  354. package/skills/gsd-path-roadmap/scripts/isolation.py +15 -5
  355. package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
  356. package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
  357. package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
  358. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
  359. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +4 -1
  360. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -0
  361. package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
  362. package/skills/gsd-path-roadmap/scripts/roadmap.py +5 -0
  363. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +5 -0
  364. package/skills/gsd-path-roadmap/scripts/state_promote.py +5 -0
  365. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
  366. package/skills/gsd-path-ship/SKILL.md +24 -13
  367. package/skills/gsd-path-ship/references/dispatch.md +6 -19
  368. package/skills/gsd-path-ship/references/jev-review.md +82 -0
  369. package/skills/gsd-path-ship/references/model-policy.md +139 -0
  370. package/skills/gsd-path-ship/references/reviewer-panel.md +1 -1
  371. package/skills/gsd-path-ship/references/reviewer.md +2 -0
  372. package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
  373. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  374. package/skills/gsd-path-ship/scripts/_common.py +5 -0
  375. package/skills/gsd-path-ship/scripts/archive_milestone.py +20 -6
  376. package/skills/gsd-path-ship/scripts/build_recovery.py +5 -0
  377. package/skills/gsd-path-ship/scripts/build_state.py +2 -0
  378. package/skills/gsd-path-ship/scripts/check_handoffs.py +6 -0
  379. package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
  380. package/skills/gsd-path-ship/scripts/detect_project.py +11 -1
  381. package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
  382. package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
  383. package/skills/gsd-path-ship/scripts/integration.py +28 -8
  384. package/skills/gsd-path-ship/scripts/isolation.py +15 -5
  385. package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
  386. package/skills/gsd-path-ship/scripts/lean_verification.py +2 -0
  387. package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
  388. package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
  389. package/skills/gsd-path-ship/scripts/path_config.py +248 -0
  390. package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
  391. package/skills/gsd-path-ship/scripts/pipeline_state.py +4 -1
  392. package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -0
  393. package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
  394. package/skills/gsd-path-ship/scripts/roadmap.py +5 -0
  395. package/skills/gsd-path-ship/scripts/state_checkpoint.py +5 -0
  396. package/skills/gsd-path-ship/scripts/state_promote.py +5 -0
  397. package/skills/gsd-path-ship/scripts/workflow_run.py +12 -1
  398. package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
  399. package/skills/gsd-path-undo/SKILL.md +2 -0
  400. package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
  401. package/skills/gsd-path-undo/scripts/_common.py +5 -0
  402. package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
  403. package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
  404. package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
  405. package/skills/gsd-path-undo/scripts/path_config.py +248 -0
  406. package/skills/gsd-path-undo/scripts/pipeline_undo.py +3 -0
  407. package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
  408. package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
  409. package/skills/path/BUILD.md +7 -0
  410. package/skills/path/DECIDE.md +2 -0
  411. package/skills/path/DEFINE.md +13 -7
  412. package/skills/path/DOCS-AUDIT.md +2 -0
  413. package/skills/path/INSPECT.md +2 -0
  414. package/skills/path/PLAN.md +4 -2
  415. package/skills/path/RESEARCH.md +2 -0
  416. package/skills/path/ROADMAP.md +2 -0
  417. package/skills/path/SHIP.md +24 -13
  418. package/skills/path/SKILL.md +23 -9
  419. package/skills/path/references/build-native.md +15 -7
  420. package/skills/path/references/config.md +57 -0
  421. package/skills/path/references/dispatch.md +6 -19
  422. package/skills/path/references/jev-review.md +82 -0
  423. package/skills/path/references/model-policy.md +139 -0
  424. package/skills/path/references/reviewer-panel.md +1 -1
  425. package/skills/path/references/reviewer.md +2 -0
  426. package/skills/path/references/runtime-selection.md +25 -0
  427. package/skills/path/scripts/_common.py +5 -0
  428. package/skills/path/scripts/archive_milestone.py +20 -6
  429. package/skills/path/scripts/bootstrap_repository.py +8 -1
  430. package/skills/path/scripts/build_recovery.py +5 -0
  431. package/skills/path/scripts/build_state.py +2 -0
  432. package/skills/path/scripts/check_docs_audit.py +2 -0
  433. package/skills/path/scripts/check_handoffs.py +6 -0
  434. package/skills/path/scripts/check_task_briefs.py +3 -0
  435. package/skills/path/scripts/detect_project.py +11 -1
  436. package/skills/path/scripts/discussion_records.py +3 -0
  437. package/skills/path/scripts/discussion_validate.py +8 -3
  438. package/skills/path/scripts/dispatch_driver.py +230 -21
  439. package/skills/path/scripts/git_guard.py +768 -0
  440. package/skills/path/scripts/integration.py +28 -8
  441. package/skills/path/scripts/isolation.py +15 -5
  442. package/skills/path/scripts/jev_review.py +138 -0
  443. package/skills/path/scripts/lean_verification.py +2 -0
  444. package/skills/path/scripts/loop_run.py +2 -0
  445. package/skills/path/scripts/model_policy.py +279 -0
  446. package/skills/path/scripts/path_config.py +248 -0
  447. package/skills/path/scripts/pipeline_git.py +3 -0
  448. package/skills/path/scripts/pipeline_state.py +4 -1
  449. package/skills/path/scripts/promote_lookahead.py +3 -0
  450. package/skills/path/scripts/review_panel.py +11 -0
  451. package/skills/path/scripts/roadmap.py +5 -0
  452. package/skills/path/scripts/state_checkpoint.py +5 -0
  453. package/skills/path/scripts/state_promote.py +5 -0
  454. package/skills/path/scripts/status_runtime.py +208 -0
  455. package/skills/path/scripts/workflow_run.py +12 -1
  456. package/skills/path/scripts/worktree_paths.py +94 -0
  457. package/skills/path/templates/skeptic.md +1 -1
  458. package/skills/path/templates/task.md +2 -0
  459. package/skills/path/templates/wave-panel.md +1 -1
  460. package/skills/gsd-path/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  461. package/skills/gsd-path/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  462. package/skills/gsd-path/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  463. package/skills/gsd-path/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  464. package/skills/gsd-path/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  465. package/skills/gsd-path/scripts/__pycache__/discussion_records.cpython-312.pyc +0 -0
  466. package/skills/gsd-path/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  467. package/skills/gsd-path/scripts/__pycache__/integration.cpython-312.pyc +0 -0
  468. package/skills/gsd-path/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  469. package/skills/gsd-path/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  470. package/skills/gsd-path/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  471. package/skills/gsd-path/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  472. package/skills/gsd-path/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  473. package/skills/gsd-path/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  474. package/skills/gsd-path/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  475. package/skills/gsd-path/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  476. package/skills/gsd-path/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  477. package/skills/gsd-path/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  478. package/skills/gsd-path-build/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  479. package/skills/gsd-path-build/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  480. package/skills/gsd-path-build/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  481. package/skills/gsd-path-build/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  482. package/skills/gsd-path-build/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  483. package/skills/gsd-path-build/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  484. package/skills/gsd-path-build/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  485. package/skills/gsd-path-build/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  486. package/skills/gsd-path-build/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  487. package/skills/gsd-path-build/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  488. package/skills/gsd-path-build/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  489. package/skills/gsd-path-build/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  490. package/skills/gsd-path-build/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  491. package/skills/gsd-path-build/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  492. package/skills/gsd-path-build/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  493. package/skills/gsd-path-build/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  494. package/skills/gsd-path-decide/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  495. package/skills/gsd-path-decide/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  496. package/skills/gsd-path-decide/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  497. package/skills/gsd-path-decide/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  498. package/skills/gsd-path-decide/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  499. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  500. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  501. package/skills/gsd-path-decide/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  502. package/skills/gsd-path-decide/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  503. package/skills/gsd-path-decide/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  504. package/skills/gsd-path-define/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  505. package/skills/gsd-path-define/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  506. package/skills/gsd-path-define/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  507. package/skills/gsd-path-define/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  508. package/skills/gsd-path-define/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  509. package/skills/gsd-path-define/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  510. package/skills/gsd-path-define/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  511. package/skills/gsd-path-define/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  512. package/skills/gsd-path-define/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  513. package/skills/gsd-path-define/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  514. package/skills/gsd-path-docs-audit/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  515. package/skills/gsd-path-docs-audit/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  516. package/skills/gsd-path-docs-audit/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  517. package/skills/gsd-path-docs-audit/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  518. package/skills/gsd-path-docs-audit/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  519. package/skills/gsd-path-docs-audit/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  520. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  521. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  522. package/skills/gsd-path-docs-audit/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  523. package/skills/gsd-path-docs-audit/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  524. package/skills/gsd-path-docs-audit/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  525. package/skills/gsd-path-forensics/scripts/__pycache__/pipeline_undo.cpython-312.pyc +0 -0
  526. package/skills/gsd-path-inspect/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  527. package/skills/gsd-path-inspect/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  528. package/skills/gsd-path-inspect/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  529. package/skills/gsd-path-inspect/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  530. package/skills/gsd-path-inspect/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  531. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  532. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  533. package/skills/gsd-path-inspect/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  534. package/skills/gsd-path-inspect/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  535. package/skills/gsd-path-inspect/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  536. package/skills/gsd-path-plan/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  537. package/skills/gsd-path-plan/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  538. package/skills/gsd-path-plan/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  539. package/skills/gsd-path-plan/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  540. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  541. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  542. package/skills/gsd-path-plan/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  543. package/skills/gsd-path-plan/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  544. package/skills/gsd-path-plan/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  545. package/skills/gsd-path-research/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  546. package/skills/gsd-path-research/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  547. package/skills/gsd-path-research/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  548. package/skills/gsd-path-research/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  549. package/skills/gsd-path-research/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  550. package/skills/gsd-path-research/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  551. package/skills/gsd-path-research/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  552. package/skills/gsd-path-research/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  553. package/skills/gsd-path-research/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  554. package/skills/gsd-path-research/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  555. package/skills/gsd-path-roadmap/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  556. package/skills/gsd-path-roadmap/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  557. package/skills/gsd-path-roadmap/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  558. package/skills/gsd-path-roadmap/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  559. package/skills/gsd-path-roadmap/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  560. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  561. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  562. package/skills/gsd-path-roadmap/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  563. package/skills/gsd-path-roadmap/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  564. package/skills/gsd-path-roadmap/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  565. package/skills/gsd-path-ship/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  566. package/skills/gsd-path-ship/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  567. package/skills/gsd-path-ship/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  568. package/skills/gsd-path-ship/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  569. package/skills/gsd-path-ship/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  570. package/skills/gsd-path-ship/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  571. package/skills/gsd-path-ship/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  572. package/skills/gsd-path-ship/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  573. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  574. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  575. package/skills/gsd-path-ship/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  576. package/skills/gsd-path-ship/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  577. package/skills/gsd-path-ship/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
@@ -3,6 +3,7 @@
3
3
 
4
4
  import argparse
5
5
  import errno
6
+ import hashlib
6
7
  import json
7
8
  import os
8
9
  import re
@@ -17,9 +18,11 @@ from typing import Callable, Iterable, List, Mapping, Optional, Sequence, Tuple,
17
18
  sys.dont_write_bytecode = True
18
19
 
19
20
  try:
20
- from . import sync_skill_resources
21
+ from . import sync_skill_resources, runtime_store, status_runtime
21
22
  except ImportError: # Direct execution from scripts/.
22
23
  import sync_skill_resources # type: ignore
24
+ import runtime_store
25
+ import status_runtime
23
26
 
24
27
 
25
28
  def skill_names_for_manifest(manifest: Mapping) -> Tuple[str, ...]:
@@ -46,27 +49,7 @@ CLAUDE_BRIDGE = "@../AGENTS.md\n@../WORKFLOW.md\n"
46
49
  HOOKS_DIRECTORY = ".gsd-path"
47
50
  GUARD_SCRIPTS = ("guard_hook.py", "git_guard.py")
48
51
  GUARD_MARKER = "gsd-path guard"
49
- PROJECT_RUNTIME_SCRIPTS = (
50
- "pipeline_state.py",
51
- "roadmap.py",
52
- "check_handoffs.py",
53
- "check_task_briefs.py",
54
- "isolation.py",
55
- "discussion_records.py",
56
- "pipeline_git.py",
57
- "promote_lookahead.py",
58
- "detect_project.py",
59
- "pipeline_diagnose.py",
60
- "pipeline_undo.py",
61
- "archive_milestone.py",
62
- "review_panel.py",
63
- "_common.py",
64
- "build_recovery.py",
65
- "state_checkpoint.py",
66
- "state_promote.py",
67
- "discussion_validate.py",
68
- "integration.py",
69
- )
52
+ PROJECT_RUNTIME_SCRIPTS = runtime_store.RUNTIME_FILES
70
53
  PROJECT_RUNTIME_MARKER = "gsd-path project runtime"
71
54
  PROJECT_STATUS_LAUNCHER = "status_runtime.py"
72
55
  PROJECT_STATUS_MARKER = "gsd-path project status launcher"
@@ -888,10 +871,15 @@ def _create_install_lock(lock: Path) -> None:
888
871
  raise
889
872
 
890
873
 
874
+ def _install_lock_path(root: Path) -> Path:
875
+ identity = hashlib.sha256(os.fsencode(root.resolve())).hexdigest()
876
+ return Path.home() / ".gsd-path" / "install-locks" / identity / INSTALL_LOCK_NAME
877
+
878
+
891
879
  def _acquire_install_locks(roots: Iterable[Path]) -> Tuple[List[Path], List[Path]]:
892
880
  locks: List[Path] = []
893
881
  for root in roots:
894
- candidate = root.parent / INSTALL_LOCK_NAME
882
+ candidate = _install_lock_path(root)
895
883
  if not any(_same_path(candidate, existing) for existing in locks):
896
884
  locks.append(candidate)
897
885
  locks.sort(key=lambda candidate: os.path.normcase(os.fspath(candidate)))
@@ -1030,15 +1018,7 @@ def _project_destinations(
1030
1018
  False,
1031
1019
  ),
1032
1020
  ]
1033
- destinations.extend(
1034
- (
1035
- project / HOOKS_DIRECTORY / "runtime" / name,
1036
- f"scripts/{name}",
1037
- None,
1038
- False,
1039
- )
1040
- for name in PROJECT_RUNTIME_SCRIPTS
1041
- )
1021
+ destinations.append((project / HOOKS_DIRECTORY / "runtime.json", None, "", False))
1042
1022
  if "claude" in selected:
1043
1023
  destinations.append(
1044
1024
  (project / ".claude" / "CLAUDE.md", None, CLAUDE_BRIDGE, False)
@@ -1046,7 +1026,7 @@ def _project_destinations(
1046
1026
  if hooks:
1047
1027
  for name in GUARD_SCRIPTS:
1048
1028
  destinations.append(
1049
- (project / HOOKS_DIRECTORY / name, f"scripts/{name}", None, False)
1029
+ (project / HOOKS_DIRECTORY / name, None, runtime_store.guard_launcher(name), False)
1050
1030
  )
1051
1031
  if "claude" in selected:
1052
1032
  destinations.append(
@@ -1106,6 +1086,8 @@ def _update_replacement(
1106
1086
  the managed-file check for a replaceable destination, or None for a
1107
1087
  kept one."""
1108
1088
  parent = project / HOOKS_DIRECTORY
1089
+ if (parent / "runtime.json").is_file() and destination.parent == parent:
1090
+ return None # Stable wiring and selection change only through explicit operations.
1109
1091
  directory = destination.parent
1110
1092
  if _same_path(directory, parent / "runtime"):
1111
1093
  return _is_managed_project_runtime
@@ -1246,6 +1228,10 @@ def _validate_project(
1246
1228
  source = source_root / source_name
1247
1229
  if source.is_symlink() or not source.is_file():
1248
1230
  raise InstallerError(f"missing project contract: {source}")
1231
+ try:
1232
+ runtime_store.prepare(source_root, project, dry_run=True)
1233
+ except (OSError, ValueError) as error:
1234
+ raise InstallerError(str(error)) from error
1249
1235
  mergers = _native_settings_mergers(project, selected, hooks)
1250
1236
  for destination, _, _, _ in _project_destinations(
1251
1237
  project, selected, hooks, interpreter, hooks_dir
@@ -1280,11 +1266,17 @@ def _apply_project(
1280
1266
  hooks_dir: Optional[Path],
1281
1267
  update: bool = False,
1282
1268
  ) -> None:
1269
+ try:
1270
+ pin = runtime_store.prepare(source_root, project)
1271
+ except (OSError, ValueError) as error:
1272
+ raise InstallerError(str(error)) from error
1283
1273
  _create_directory(project, transaction.created_directories)
1284
1274
  mergers = _native_settings_mergers(project, selected, hooks)
1285
1275
  for destination, source_name, content, executable in _project_destinations(
1286
1276
  project, selected, hooks, interpreter, hooks_dir
1287
1277
  ):
1278
+ if destination.name == "runtime.json" and destination.parent == project / HOOKS_DIRECTORY:
1279
+ content = runtime_store.pin_text(pin)
1288
1280
  _create_directory(destination.parent, transaction.created_directories)
1289
1281
  merge = mergers.get(destination)
1290
1282
  if _lexists(destination) and not destination.is_symlink():
@@ -1300,7 +1292,7 @@ def _apply_project(
1300
1292
  elif source_name:
1301
1293
  replacement = (source_root / source_name).read_bytes()
1302
1294
  else:
1303
- replacement = content or ""
1295
+ replacement = (content or "").encode("utf-8")
1304
1296
  _atomic_write(
1305
1297
  destination, replacement, 0o755 if executable else mode
1306
1298
  )
@@ -1316,9 +1308,9 @@ def _apply_project(
1316
1308
  created = True
1317
1309
  shutil.copyfileobj(source, output)
1318
1310
  else:
1319
- with destination.open("x", encoding="utf-8") as output:
1311
+ with destination.open("xb") as output:
1320
1312
  created = True
1321
- output.write(content)
1313
+ output.write(content.encode("utf-8"))
1322
1314
  if executable:
1323
1315
  destination.chmod(0o755)
1324
1316
  transaction.copied.append(destination)
@@ -1642,7 +1634,7 @@ def _validate_hooks_refresh(
1642
1634
  project / HOOKS_DIRECTORY / "runtime", "project runtime directory"
1643
1635
  )
1644
1636
  refresh_guards = _refreshes_guards(project, full, initialize)
1645
- runtime_exists = any(
1637
+ runtime_exists = (project / HOOKS_DIRECTORY / "runtime.json").is_file() or any(
1646
1638
  _lexists(project / HOOKS_DIRECTORY / "runtime" / name)
1647
1639
  for name in PROJECT_RUNTIME_SCRIPTS
1648
1640
  )
@@ -1733,162 +1725,63 @@ def _validate_hooks_refresh(
1733
1725
  )
1734
1726
 
1735
1727
 
1736
- def _refresh_hooks_unlocked(
1737
- source_root: Path,
1738
- project: Path,
1739
- full: bool,
1740
- dry_run: bool = False,
1741
- selected: Sequence[str] = (),
1742
- initialize: bool = False,
1743
- ) -> List[str]:
1744
- """Refreshed project-relative paths; "note:"-prefixed entries are
1745
- user-facing notes rather than refreshed files."""
1746
- hooks_dir = _git_hooks_directory(project) if full else None
1747
- interpreter: Optional[str] = None
1748
- if not dry_run:
1749
- if full:
1750
- interpreter, hooks_dir = _required_hook_runtime(
1751
- project,
1752
- "--hooks-init" if initialize else "--hooks-refresh-full",
1753
- selected,
1754
- )
1755
- else:
1756
- interpreter = _required_python_runtime("--hooks-refresh", selected)
1757
- _validate_hooks_refresh(
1758
- source_root, project, full, hooks_dir, selected, initialize
1759
- )
1760
- refreshed: List[str] = []
1761
- refresh_guards = _refreshes_guards(project, full, initialize)
1762
- runtime = project / HOOKS_DIRECTORY / "runtime"
1763
- if not dry_run:
1764
- runtime.parent.mkdir(parents=True, exist_ok=True)
1765
- staging = Path(tempfile.mkdtemp(prefix=".runtime-stage-", dir=runtime.parent))
1766
- previous = staging.with_name(staging.name + "-previous")
1767
- moved_previous = False
1768
- published_runtime = False
1769
- launcher = runtime.parent / PROJECT_STATUS_LAUNCHER
1770
- launcher_original = launcher.read_bytes() if _lexists(launcher) else None
1771
- launcher_mode = (
1772
- launcher.stat().st_mode & 0o777 if launcher_original is not None else None
1773
- )
1774
- guard_originals: List[Tuple[Path, Optional[bytes], Optional[int]]] = []
1775
- try:
1776
- for name in PROJECT_RUNTIME_SCRIPTS:
1777
- shutil.copy2(source_root / "scripts" / name, staging / name)
1778
- _atomic_copy(source_root / "scripts" / PROJECT_STATUS_LAUNCHER, launcher)
1779
- if _lexists(runtime):
1780
- os.replace(runtime, previous)
1781
- moved_previous = True
1782
- os.replace(staging, runtime)
1783
- published_runtime = True
1784
- if refresh_guards:
1785
- for name in GUARD_SCRIPTS:
1786
- destination = project / HOOKS_DIRECTORY / name
1787
- original = destination.read_bytes() if _lexists(destination) else None
1788
- mode = (
1789
- destination.stat().st_mode & 0o777
1790
- if original is not None
1791
- else None
1792
- )
1793
- guard_originals.append((destination, original, mode))
1794
- _atomic_copy(source_root / "scripts" / name, destination)
1795
- if moved_previous:
1796
- _remove_path(previous)
1797
- except BaseException as error:
1798
- for destination, original, mode in reversed(guard_originals):
1799
- if original is None:
1800
- _remove_path(destination)
1801
- else:
1802
- _atomic_write(destination, original, mode)
1803
- if launcher_original is None:
1804
- _remove_path(launcher)
1805
- else:
1806
- _atomic_write(launcher, launcher_original, launcher_mode)
1807
- if published_runtime and _lexists(runtime):
1808
- _remove_path(runtime)
1809
- if not _lexists(runtime) and moved_previous and _lexists(previous):
1810
- os.replace(previous, runtime)
1811
- if isinstance(error, Exception):
1812
- raise InstallerError(
1813
- f"project runtime refresh failed: {error}"
1814
- ) from error
1815
- raise
1816
- finally:
1817
- _remove_path(staging)
1818
- for name in PROJECT_RUNTIME_SCRIPTS:
1819
- refreshed.append(_describe_project_path(project, runtime / name))
1820
- refreshed.append(
1821
- _describe_project_path(
1822
- project, project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
1823
- )
1824
- )
1825
- if refresh_guards:
1826
- for name in GUARD_SCRIPTS:
1827
- destination = project / HOOKS_DIRECTORY / name
1828
- refreshed.append(_describe_project_path(project, destination))
1829
- if full:
1830
- for target, settings, merge, generated in (
1831
- (
1832
- "claude",
1833
- project / ".claude" / "settings.json",
1834
- _merged_claude_settings,
1835
- claude_hooks_settings,
1836
- ),
1837
- (
1838
- "codex",
1839
- project / ".codex" / "hooks.json",
1840
- _merged_codex_settings,
1841
- codex_hooks_settings,
1842
- ),
1843
- (
1844
- "cursor",
1845
- project / ".cursor" / "hooks.json",
1846
- _merged_cursor_settings,
1847
- cursor_hooks_settings,
1848
- ),
1849
- ):
1850
- if initialize and target not in selected:
1851
- continue
1852
- exists = _lexists(settings)
1853
- if exists or target in selected:
1854
- if not dry_run:
1855
- settings.parent.mkdir(parents=True, exist_ok=True)
1856
- content = (
1857
- merge(settings, interpreter)
1858
- if exists
1859
- else generated(interpreter)
1860
- )
1861
- _atomic_write(settings, content)
1862
- refreshed.append(_describe_project_path(project, settings))
1863
- if hooks_dir is not None:
1864
- for hook_name, generator in GIT_HOOKS:
1865
- hook_path = hooks_dir / hook_name
1866
- if not dry_run:
1867
- content = generator(interpreter)
1868
- hook_path.parent.mkdir(parents=True, exist_ok=True)
1869
- _atomic_write(hook_path, content, mode=0o755)
1870
- refreshed.append(_describe_project_path(project, hook_path))
1871
- return refreshed
1872
-
1873
-
1874
1728
  def refresh_hooks(
1875
- source_root: Path,
1876
- project: Path,
1877
- full: bool,
1878
- dry_run: bool = False,
1879
- selected: Sequence[str] = (),
1880
- initialize: bool = False,
1729
+ source_root: Path, project: Path, full: bool, dry_run: bool = False,
1730
+ selected: Sequence[str] = (), initialize: bool = False,
1881
1731
  ) -> List[str]:
1882
- if dry_run:
1883
- return _refresh_hooks_unlocked(
1884
- source_root, project, full, True, selected, initialize
1885
- )
1886
1732
  _validate_directory_destination(project, "project path")
1733
+ if full and not dry_run:
1734
+ _required_hook_runtime(project, "--hooks-init" if initialize else "--hooks-refresh-full", selected)
1735
+ pinned = os.path.lexists(project / HOOKS_DIRECTORY / "runtime.json")
1736
+ if os.path.lexists(project / HOOKS_DIRECTORY / "runtime"):
1737
+ raise InstallerError("legacy runtime requires --runtime-migrate before refresh")
1738
+ if pinned:
1739
+ try:
1740
+ status_runtime.resolve_runtime(project)
1741
+ except (OSError, ValueError) as error:
1742
+ raise InstallerError(str(error)) from error
1743
+ _validate_hooks_refresh(source_root, project, full, _git_hooks_directory(project) if full else None, selected, initialize)
1744
+ if not dry_run:
1745
+ _required_python_runtime("--hooks-refresh", selected)
1746
+ elif not initialize and not _has_legacy_project_contracts(source_root, project):
1747
+ _validate_hooks_refresh(source_root, project, full, _git_hooks_directory(project) if full else None, selected, initialize)
1748
+ raise InstallerError(f"no managed GSD Path hooks or runtime found in project: {project}")
1749
+ if dry_run:
1750
+ if pinned:
1751
+ return ["note: selected runtime retained; use --runtime-upgrade for an explicit upgrade"]
1752
+ _validate_hooks_refresh(source_root, project, full,
1753
+ _git_hooks_directory(project) if full else None, selected, True)
1754
+ runtime_store.prepare(source_root, project, dry_run=True)
1755
+ return [".gsd-path/runtime.json", ".gsd-path/status_runtime.py"]
1887
1756
  locks, created = _acquire_install_locks([project / HOOKS_DIRECTORY])
1888
1757
  try:
1889
- return _refresh_hooks_unlocked(
1890
- source_root, project, full, False, selected, initialize
1891
- )
1758
+ if not pinned:
1759
+ interpreter = _required_python_runtime("--hooks-init", selected)
1760
+ hooks_dir = _git_hooks_directory(project) if full else None
1761
+ _validate_hooks_refresh(source_root, project, full, hooks_dir, selected, True)
1762
+ transaction = ProjectTransaction()
1763
+ try:
1764
+ _apply_project(source_root, project, selected, full, transaction, interpreter, hooks_dir, update=True)
1765
+ except BaseException:
1766
+ _rollback_project(transaction)
1767
+ raise
1768
+ return [".gsd-path/runtime.json", ".gsd-path/status_runtime.py"]
1769
+ if not full and not _has_managed_guard_wiring(project):
1770
+ return ["note: selected runtime retained; use --runtime-upgrade for an explicit upgrade"]
1771
+ interpreter = _required_python_runtime("--hooks-refresh", selected)
1772
+ hooks_dir = _git_hooks_directory(project) if full else None
1773
+ refresh_targets = list(selected)
1774
+ if full and not initialize:
1775
+ for target, relative in (("claude", ".claude/settings.json"), ("codex", ".codex/hooks.json"), ("cursor", ".cursor/hooks.json")):
1776
+ if (project / relative).exists() and target not in refresh_targets:
1777
+ refresh_targets.append(target)
1778
+ transaction = ProjectTransaction()
1779
+ try:
1780
+ _apply_project(source_root, project, refresh_targets, True, transaction, interpreter, hooks_dir, update=True)
1781
+ except BaseException:
1782
+ _rollback_project(transaction)
1783
+ raise
1784
+ return ["note: selected runtime retained; stable hooks initialized/refreshed"]
1892
1785
  finally:
1893
1786
  _release_install_locks(locks, created)
1894
1787
 
@@ -2037,6 +1930,11 @@ def _read_package_version(manifest: Path) -> Optional[str]:
2037
1930
 
2038
1931
  def _validated_project_state(source_root: Path, project: Path) -> dict:
2039
1932
  validator = source_root / "scripts" / "pipeline_state.py"
1933
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
1934
+ try:
1935
+ validator = status_runtime.resolve_runtime(project) / "pipeline_state.py"
1936
+ except (OSError, ValueError) as error:
1937
+ raise InstallerError(str(error)) from error
2040
1938
  if validator.is_symlink() or not validator.is_file():
2041
1939
  raise InstallerError(f"canonical state validator is unavailable: {validator}")
2042
1940
  interpreter = _required_python_runtime("--doctor")
@@ -2074,6 +1972,11 @@ def _validated_project_state(source_root: Path, project: Path) -> dict:
2074
1972
  def _validate_project_runtime_status(source_root: Path, project: Path) -> None:
2075
1973
  interpreter = _required_python_runtime("--doctor")
2076
1974
  runtime = source_root / "scripts" / "pipeline_state.py"
1975
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
1976
+ try:
1977
+ runtime = status_runtime.resolve_runtime(project) / "pipeline_state.py"
1978
+ except (OSError, ValueError) as error:
1979
+ raise InstallerError(f"runtime changed during doctor validation: {error}") from error
2077
1980
  environment = os.environ.copy()
2078
1981
  environment["GIT_OPTIONAL_LOCKS"] = "0"
2079
1982
  try:
@@ -2099,6 +2002,13 @@ def _validate_project_runtime_status(source_root: Path, project: Path) -> None:
2099
2002
 
2100
2003
 
2101
2004
  def _project_runtime_matches(source_root: Path, project: Path) -> bool:
2005
+ if (project / HOOKS_DIRECTORY / "runtime.json").exists():
2006
+ try:
2007
+ root = status_runtime.resolve_runtime(project)
2008
+ launcher = project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
2009
+ return not launcher.is_symlink() and launcher.read_bytes() == (root / PROJECT_STATUS_LAUNCHER).read_bytes()
2010
+ except (OSError, ValueError):
2011
+ return False
2102
2012
  pairs = [
2103
2013
  (
2104
2014
  project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER,
@@ -2370,7 +2280,15 @@ def doctor(
2370
2280
  "project: .claude/CLAUDE.md exists but is not the managed bridge",
2371
2281
  )
2372
2282
 
2283
+ pinned = os.path.lexists(project / HOOKS_DIRECTORY / "runtime.json")
2373
2284
  runtime = project / HOOKS_DIRECTORY / "runtime"
2285
+ if pinned:
2286
+ try:
2287
+ runtime = status_runtime.resolve_runtime(project)
2288
+ push("ok", f"project: selected runtime {status_runtime.declaration(project)['version']}")
2289
+ except (OSError, ValueError) as error:
2290
+ push("fail", str(error))
2291
+ return findings
2374
2292
  launcher = project / HOOKS_DIRECTORY / PROJECT_STATUS_LAUNCHER
2375
2293
  runtime_current = True
2376
2294
  if launcher.is_symlink():
@@ -2382,7 +2300,7 @@ def doctor(
2382
2300
  else:
2383
2301
  launcher_content = read_project_file(launcher, "project: status launcher")
2384
2302
  launcher_source = read_project_file(
2385
- source_root / "scripts" / PROJECT_STATUS_LAUNCHER,
2303
+ (runtime if pinned else source_root / "scripts") / PROJECT_STATUS_LAUNCHER,
2386
2304
  "package: status launcher",
2387
2305
  )
2388
2306
  if launcher_content is None or launcher_source is None:
@@ -2423,7 +2341,7 @@ def doctor(
2423
2341
  runtime_current = False
2424
2342
  continue
2425
2343
  source = read_project_file(
2426
- source_root / "scripts" / name, f"package: runtime {name}"
2344
+ (runtime if pinned else source_root / "scripts") / name, f"package: runtime {name}"
2427
2345
  )
2428
2346
  if source is None:
2429
2347
  runtime_current = False
@@ -2494,6 +2412,8 @@ def doctor(
2494
2412
  source = read_project_file(
2495
2413
  source_root / "scripts" / name, f"package: guard {name}"
2496
2414
  )
2415
+ if pinned:
2416
+ source = runtime_store.guard_launcher(name).encode()
2497
2417
  if source is None:
2498
2418
  continue
2499
2419
  if GUARD_MARKER.encode() not in content:
@@ -2853,12 +2773,16 @@ def parser() -> argparse.ArgumentParser:
2853
2773
  for target in TARGETS:
2854
2774
  argument_parser.add_argument(f"--{target}", action="store_true")
2855
2775
  argument_parser.add_argument(f"--{target}-root", type=Path)
2776
+ argument_parser.add_argument("--adapter-request", action="store_true", help=argparse.SUPPRESS)
2856
2777
  argument_parser.add_argument("--all", action="store_true", dest="all_targets")
2857
2778
  argument_parser.add_argument("--update", action="store_true")
2858
2779
  argument_parser.add_argument("--local", action="store_true")
2859
2780
  argument_parser.add_argument("--dry-run", action="store_true")
2860
2781
  argument_parser.add_argument("--project", type=Path)
2861
2782
  argument_parser.add_argument("--doctor", action="store_true")
2783
+ runtime_actions = argument_parser.add_mutually_exclusive_group()
2784
+ for action in ("restore", "upgrade", "migrate"):
2785
+ runtime_actions.add_argument(f"--runtime-{action}", action="store_true")
2862
2786
  argument_parser.add_argument("--hooks", action="store_true")
2863
2787
  argument_parser.add_argument("--hooks-init", action="store_true")
2864
2788
  argument_parser.add_argument("--hooks-refresh", action="store_true")
@@ -2875,10 +2799,51 @@ def parser() -> argparse.ArgumentParser:
2875
2799
  def main(argv: Optional[Sequence[str]] = None) -> int:
2876
2800
  argument_parser = parser()
2877
2801
  arguments = argument_parser.parse_args(argv)
2802
+ if arguments.adapter_request:
2803
+ try:
2804
+ request = json.load(sys.stdin)
2805
+ source = Path(request["source"])
2806
+ project = Path(request["project"])
2807
+ action = request["action"]
2808
+ if action == "install":
2809
+ plans = [TargetPlan(p["name"], Path(p["root"])) for p in request["plans"]]
2810
+ result = install(source, plans, project=project, dry_run=request["dryRun"],
2811
+ hooks=request["hooks"], migrate_legacy=request["migrateLegacy"],
2812
+ update=request["update"])
2813
+ elif action == "refresh":
2814
+ result = refresh_hooks(source, project, request["full"], request["dryRun"],
2815
+ request["selected"], request["initialize"])
2816
+ elif action == "doctor":
2817
+ result = doctor(source, request["targets"], lambda target: Path(request["roots"][target]), project)
2818
+ else:
2819
+ raise InstallerError("unknown installer adapter action")
2820
+ print(json.dumps(result))
2821
+ return 0
2822
+ except (InstallerError, OSError, ValueError, KeyError) as error:
2823
+ print(str(error), file=sys.stderr)
2824
+ return 1
2878
2825
  source_root = absolute_path(arguments.source_root)
2879
2826
  project = (
2880
2827
  absolute_path(arguments.project) if arguments.project is not None else None
2881
2828
  )
2829
+ runtime_action = next((action for action in ("restore", "upgrade", "migrate")
2830
+ if getattr(arguments, "runtime_" + action)), None)
2831
+ if runtime_action:
2832
+ if project is None:
2833
+ argument_parser.error(f"--runtime-{runtime_action} requires --project")
2834
+ try:
2835
+ _validate_project_git_root(project)
2836
+ locks, created = ([], []) if arguments.dry_run else _acquire_install_locks([project / HOOKS_DIRECTORY])
2837
+ try:
2838
+ pin = runtime_store.operate(source_root, project, runtime_action, dry_run=arguments.dry_run)
2839
+ finally:
2840
+ _release_install_locks(locks, created)
2841
+ print(f"runtime: {runtime_action} {pin['version']} ({pin['digest']})"
2842
+ + (" (dry run)" if arguments.dry_run else ""))
2843
+ return 0
2844
+ except (InstallerError, OSError, ValueError) as error:
2845
+ print(f"error: {error}", file=sys.stderr)
2846
+ return 1
2882
2847
  if arguments.doctor:
2883
2848
  named = [target for target in TARGETS if getattr(arguments, target)]
2884
2849
  selected = named if named and not arguments.all_targets else list(TARGETS)
@@ -2,6 +2,11 @@
2
2
  # gsd-path project runtime
3
3
  """Own milestone integration, publication, tags, and completion proof."""
4
4
 
5
+ import sys
6
+
7
+ # Runtime helpers must not modify their immutable installation.
8
+ sys.dont_write_bytecode = True
9
+
5
10
  import json
6
11
  import re
7
12
  from pathlib import Path, PurePosixPath
@@ -21,6 +26,7 @@ try:
21
26
  from pipeline_state import PipelineState
22
27
  from isolation import publication_base
23
28
  import _common
29
+ import worktree_paths
24
30
  except ImportError: # pragma: no cover - package import used by tests
25
31
  from scripts.pipeline_git import (
26
32
  bound_branch_name,
@@ -35,6 +41,7 @@ except ImportError: # pragma: no cover - package import used by tests
35
41
  from scripts.pipeline_state import PipelineState
36
42
  from scripts.isolation import publication_base
37
43
  from scripts import _common
44
+ from scripts import worktree_paths
38
45
 
39
46
 
40
47
  if __package__:
@@ -156,10 +163,13 @@ def optional_ref(project: Path, ref: str) -> Optional[str]:
156
163
  return result.stdout.strip() if result.returncode == 0 else None
157
164
 
158
165
 
159
- def integration_names(project: Path, archive_name: str) -> tuple[str, Path]:
166
+ def integration_names(project: Path, archive_name: str, *, pin: bool = False) -> tuple[str, Path]:
160
167
  identifier = milestone_id(milestone_number(archive_name))
161
168
  branch = f"gsd-path-integrate/{identifier}"
162
- worktree = project.parent / f".{project.name}-gsd-path-integrate-{identifier}"
169
+ try:
170
+ worktree = worktree_paths.worktree_path(project, "integrate", identifier, pin=pin)
171
+ except (ValueError, OSError) as error:
172
+ raise ArchiveError(str(error)) from error
163
173
  return branch, worktree
164
174
 
165
175
 
@@ -1044,7 +1054,7 @@ def integrate(repo: Path, slug: str) -> dict:
1044
1054
  ship_commit,
1045
1055
  )
1046
1056
  publishable_bound_branch(project, bound_branch, ship_commit)
1047
- integration_branch, worktree = integration_names(project, archive_name)
1057
+ integration_branch, worktree = integration_names(project, archive_name, pin=True)
1048
1058
  interrupted_worktree = registered_worktree(project, integration_branch)
1049
1059
  if (
1050
1060
  interrupted_worktree is not None
@@ -1222,7 +1232,9 @@ def find_integrate_commit(
1222
1232
  return merge_commit
1223
1233
 
1224
1234
 
1225
- def validate_integrated(repo: Path, slug: str, *, refresh: bool = True) -> dict:
1235
+ def validate_integrated(
1236
+ repo: Path, slug: str, *, refresh: bool = True, historical: bool = False
1237
+ ) -> dict:
1226
1238
  project = repo.resolve()
1227
1239
  active_root = archive_milestone.require_project_layout(project)
1228
1240
  state, _, loaded_state_path = archive_milestone.strict_state(project)
@@ -1235,7 +1247,7 @@ def validate_integrated(repo: Path, slug: str, *, refresh: bool = True) -> dict:
1235
1247
  )
1236
1248
 
1237
1249
  # (a) The shipped transaction itself must still validate.
1238
- shipped = archive_milestone.validate(repo)
1250
+ shipped = archive_milestone.validate(repo, historical=historical)
1239
1251
  configured = shipped["archive"]
1240
1252
  ship_commit = shipped["commit"]
1241
1253
  archive_name = PurePosixPath(configured).name
@@ -1340,6 +1352,10 @@ def validate_integrated(repo: Path, slug: str, *, refresh: bool = True) -> dict:
1340
1352
  contains = run_git(project, "merge-base", "--is-ancestor", merge_commit, remote_default)
1341
1353
  if contains.returncode != 0:
1342
1354
  raise ArchiveError(f"{remote_default} does not contain the integration merge")
1355
+ if (historical
1356
+ and run_git(project, "branch", "--show-current").stdout.strip() != bound_branch
1357
+ and run_git(project, "merge-base", "--is-ancestor", merge_commit, "HEAD").returncode):
1358
+ raise ArchiveError("HEAD does not contain the integration merge")
1343
1359
 
1344
1360
  # (e) The bound branch tip and annotated tag must be published on origin.
1345
1361
  require_published_integration(
@@ -1348,7 +1364,8 @@ def validate_integrated(repo: Path, slug: str, *, refresh: bool = True) -> dict:
1348
1364
  ship_commit,
1349
1365
  tag_name,
1350
1366
  merge_commit,
1351
- allow_missing_bound=state.integration == "pull-request",
1367
+ allow_missing_bound=state.integration == "pull-request" or (historical and has_origin),
1368
+ retired=historical,
1352
1369
  )
1353
1370
 
1354
1371
  result = {
@@ -1369,7 +1386,9 @@ def validate_integrated(repo: Path, slug: str, *, refresh: bool = True) -> dict:
1369
1386
  refs = [(f"refs/heads/{default_name}", result["base"]),
1370
1387
  (f"refs/tags/{tag_name}", optional_ref(project, tag_ref))]
1371
1388
  if state.integration == "direct":
1372
- refs.append((f"refs/heads/{bound_branch}", ship_commit))
1389
+ bound_ref = f"refs/heads/{bound_branch}"
1390
+ if not historical or live_remote_ref(project, bound_ref) is not None:
1391
+ refs.append((bound_ref, ship_commit))
1373
1392
  for ref, expected in refs:
1374
1393
  if live_remote_ref(project, ref) != expected:
1375
1394
  raise ArchiveError(f"published {ref} differs from local integration evidence")
@@ -1385,6 +1404,7 @@ def require_published_integration(
1385
1404
  tag_name: str,
1386
1405
  merge_commit: str,
1387
1406
  allow_missing_bound: bool = False,
1407
+ retired: bool = False,
1388
1408
  ) -> None:
1389
1409
  if allow_missing_bound:
1390
1410
  published_ship = live_remote_ref(project, f"refs/heads/{bound_branch}")
@@ -1408,7 +1428,7 @@ def require_published_integration(
1408
1428
 
1409
1429
  bound_ref = f"refs/remotes/origin/{bound_branch}"
1410
1430
  if run_git(project, "rev-parse", "--verify", "--quiet", bound_ref).returncode != 0:
1411
- if not allow_missing_bound:
1431
+ if not retired:
1412
1432
  raise ArchiveError(f"missing published bound branch: origin/{bound_branch}")
1413
1433
  else:
1414
1434
  published_ship = archive_milestone.require_git_success(