@opengsd/gsd-path 1.1.0 → 1.3.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 (578) hide show
  1. package/AGENTS.md +5 -2
  2. package/CHANGELOG.md +87 -0
  3. package/DOCS.md +73 -1
  4. package/FULL.md +3 -2
  5. package/HOOKS.md +18 -9
  6. package/QUICK.md +3 -4
  7. package/README.md +72 -5
  8. package/RUNTIME.md +9 -4
  9. package/UPDATE.md +56 -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 +74 -1448
  36. package/scripts/install.py +165 -201
  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 +281 -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/wizard.mjs +8 -2
  57. package/scripts/workflow_run.py +12 -1
  58. package/scripts/worktree_paths.py +94 -0
  59. package/skills/gsd-path/BUILD.md +7 -0
  60. package/skills/gsd-path/DECIDE.md +2 -0
  61. package/skills/gsd-path/DEFINE.md +13 -7
  62. package/skills/gsd-path/DOCS-AUDIT.md +2 -0
  63. package/skills/gsd-path/INSPECT.md +2 -0
  64. package/skills/gsd-path/PLAN.md +4 -2
  65. package/skills/gsd-path/RESEARCH.md +2 -0
  66. package/skills/gsd-path/ROADMAP.md +2 -0
  67. package/skills/gsd-path/SHIP.md +24 -13
  68. package/skills/gsd-path/SKILL.md +23 -9
  69. package/skills/gsd-path/references/build-native.md +15 -7
  70. package/skills/gsd-path/references/config.md +57 -0
  71. package/skills/gsd-path/references/dispatch.md +6 -19
  72. package/skills/gsd-path/references/jev-review.md +82 -0
  73. package/skills/gsd-path/references/model-policy.md +139 -0
  74. package/skills/gsd-path/references/reviewer-panel.md +1 -1
  75. package/skills/gsd-path/references/reviewer.md +2 -0
  76. package/skills/gsd-path/references/runtime-selection.md +25 -0
  77. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  78. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  79. package/skills/gsd-path/scripts/_common.py +5 -0
  80. package/skills/gsd-path/scripts/archive_milestone.py +20 -6
  81. package/skills/gsd-path/scripts/bootstrap_repository.py +8 -1
  82. package/skills/gsd-path/scripts/build_recovery.py +5 -0
  83. package/skills/gsd-path/scripts/build_state.py +2 -0
  84. package/skills/gsd-path/scripts/check_docs_audit.py +2 -0
  85. package/skills/gsd-path/scripts/check_handoffs.py +6 -0
  86. package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
  87. package/skills/gsd-path/scripts/detect_project.py +11 -1
  88. package/skills/gsd-path/scripts/discussion_records.py +3 -0
  89. package/skills/gsd-path/scripts/discussion_validate.py +8 -3
  90. package/skills/gsd-path/scripts/dispatch_driver.py +230 -21
  91. package/skills/gsd-path/scripts/git_guard.py +768 -0
  92. package/skills/gsd-path/scripts/integration.py +28 -8
  93. package/skills/gsd-path/scripts/isolation.py +15 -5
  94. package/skills/gsd-path/scripts/jev_review.py +138 -0
  95. package/skills/gsd-path/scripts/lean_verification.py +2 -0
  96. package/skills/gsd-path/scripts/loop_run.py +2 -0
  97. package/skills/gsd-path/scripts/model_policy.py +279 -0
  98. package/skills/gsd-path/scripts/path_config.py +248 -0
  99. package/skills/gsd-path/scripts/pipeline_git.py +3 -0
  100. package/skills/gsd-path/scripts/pipeline_state.py +4 -1
  101. package/skills/gsd-path/scripts/promote_lookahead.py +3 -0
  102. package/skills/gsd-path/scripts/review_panel.py +11 -0
  103. package/skills/gsd-path/scripts/roadmap.py +5 -0
  104. package/skills/gsd-path/scripts/state_checkpoint.py +5 -0
  105. package/skills/gsd-path/scripts/state_promote.py +5 -0
  106. package/skills/gsd-path/scripts/status_runtime.py +208 -0
  107. package/skills/gsd-path/scripts/workflow_run.py +12 -1
  108. package/skills/gsd-path/scripts/worktree_paths.py +94 -0
  109. package/skills/gsd-path/templates/skeptic.md +1 -1
  110. package/skills/gsd-path/templates/task.md +2 -0
  111. package/skills/gsd-path/templates/wave-panel.md +1 -1
  112. package/skills/gsd-path-build/SKILL.md +7 -0
  113. package/skills/gsd-path-build/references/build-native.md +15 -7
  114. package/skills/gsd-path-build/references/dispatch.md +6 -19
  115. package/skills/gsd-path-build/references/jev-review.md +82 -0
  116. package/skills/gsd-path-build/references/model-policy.md +139 -0
  117. package/skills/gsd-path-build/references/reviewer-panel.md +1 -1
  118. package/skills/gsd-path-build/references/reviewer.md +2 -0
  119. package/skills/gsd-path-build/references/runtime-selection.md +25 -0
  120. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  121. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  122. package/skills/gsd-path-build/scripts/_common.py +5 -0
  123. package/skills/gsd-path-build/scripts/archive_milestone.py +20 -6
  124. package/skills/gsd-path-build/scripts/build_recovery.py +5 -0
  125. package/skills/gsd-path-build/scripts/build_state.py +2 -0
  126. package/skills/gsd-path-build/scripts/check_handoffs.py +6 -0
  127. package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
  128. package/skills/gsd-path-build/scripts/detect_project.py +11 -1
  129. package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
  130. package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
  131. package/skills/gsd-path-build/scripts/dispatch_driver.py +230 -21
  132. package/skills/gsd-path-build/scripts/integration.py +28 -8
  133. package/skills/gsd-path-build/scripts/isolation.py +15 -5
  134. package/skills/gsd-path-build/scripts/jev_review.py +138 -0
  135. package/skills/gsd-path-build/scripts/loop_run.py +2 -0
  136. package/skills/gsd-path-build/scripts/model_policy.py +279 -0
  137. package/skills/gsd-path-build/scripts/path_config.py +248 -0
  138. package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
  139. package/skills/gsd-path-build/scripts/pipeline_state.py +4 -1
  140. package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -0
  141. package/skills/gsd-path-build/scripts/review_panel.py +11 -0
  142. package/skills/gsd-path-build/scripts/roadmap.py +5 -0
  143. package/skills/gsd-path-build/scripts/state_checkpoint.py +5 -0
  144. package/skills/gsd-path-build/scripts/state_promote.py +5 -0
  145. package/skills/gsd-path-build/scripts/workflow_run.py +12 -1
  146. package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
  147. package/skills/gsd-path-build/templates/skeptic.md +1 -1
  148. package/skills/gsd-path-build/templates/task.md +2 -0
  149. package/skills/gsd-path-build/templates/wave-panel.md +1 -1
  150. package/skills/gsd-path-decide/SKILL.md +2 -0
  151. package/skills/gsd-path-decide/references/dispatch.md +6 -19
  152. package/skills/gsd-path-decide/references/model-policy.md +139 -0
  153. package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
  154. package/skills/gsd-path-decide/scripts/_common.py +5 -0
  155. package/skills/gsd-path-decide/scripts/archive_milestone.py +20 -6
  156. package/skills/gsd-path-decide/scripts/build_recovery.py +5 -0
  157. package/skills/gsd-path-decide/scripts/check_handoffs.py +6 -0
  158. package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
  159. package/skills/gsd-path-decide/scripts/detect_project.py +11 -1
  160. package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
  161. package/skills/gsd-path-decide/scripts/discussion_validate.py +8 -3
  162. package/skills/gsd-path-decide/scripts/integration.py +28 -8
  163. package/skills/gsd-path-decide/scripts/isolation.py +15 -5
  164. package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
  165. package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
  166. package/skills/gsd-path-decide/scripts/path_config.py +248 -0
  167. package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
  168. package/skills/gsd-path-decide/scripts/pipeline_state.py +4 -1
  169. package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -0
  170. package/skills/gsd-path-decide/scripts/review_panel.py +11 -0
  171. package/skills/gsd-path-decide/scripts/roadmap.py +5 -0
  172. package/skills/gsd-path-decide/scripts/state_checkpoint.py +5 -0
  173. package/skills/gsd-path-decide/scripts/state_promote.py +5 -0
  174. package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
  175. package/skills/gsd-path-define/SKILL.md +13 -7
  176. package/skills/gsd-path-define/references/runtime-selection.md +25 -0
  177. package/skills/gsd-path-define/scripts/_common.py +5 -0
  178. package/skills/gsd-path-define/scripts/archive_milestone.py +20 -6
  179. package/skills/gsd-path-define/scripts/build_recovery.py +5 -0
  180. package/skills/gsd-path-define/scripts/check_handoffs.py +6 -0
  181. package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
  182. package/skills/gsd-path-define/scripts/detect_project.py +11 -1
  183. package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
  184. package/skills/gsd-path-define/scripts/discussion_validate.py +8 -3
  185. package/skills/gsd-path-define/scripts/integration.py +28 -8
  186. package/skills/gsd-path-define/scripts/isolation.py +15 -5
  187. package/skills/gsd-path-define/scripts/loop_run.py +839 -0
  188. package/skills/gsd-path-define/scripts/model_policy.py +279 -0
  189. package/skills/gsd-path-define/scripts/path_config.py +248 -0
  190. package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
  191. package/skills/gsd-path-define/scripts/pipeline_state.py +4 -1
  192. package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -0
  193. package/skills/gsd-path-define/scripts/review_panel.py +11 -0
  194. package/skills/gsd-path-define/scripts/roadmap.py +5 -0
  195. package/skills/gsd-path-define/scripts/state_checkpoint.py +5 -0
  196. package/skills/gsd-path-define/scripts/state_promote.py +5 -0
  197. package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
  198. package/skills/gsd-path-discuss/SKILL.md +2 -0
  199. package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
  200. package/skills/gsd-path-discuss/scripts/_common.py +5 -0
  201. package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
  202. package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
  203. package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
  204. package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
  205. package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
  206. package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
  207. package/skills/gsd-path-docs-audit/SKILL.md +2 -0
  208. package/skills/gsd-path-docs-audit/references/dispatch.md +6 -19
  209. package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
  210. package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
  211. package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
  212. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +20 -6
  213. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +5 -0
  214. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +2 -0
  215. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +6 -0
  216. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
  217. package/skills/gsd-path-docs-audit/scripts/detect_project.py +11 -1
  218. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
  219. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +8 -3
  220. package/skills/gsd-path-docs-audit/scripts/integration.py +28 -8
  221. package/skills/gsd-path-docs-audit/scripts/isolation.py +15 -5
  222. package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
  223. package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
  224. package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
  225. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
  226. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +4 -1
  227. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -0
  228. package/skills/gsd-path-docs-audit/scripts/review_panel.py +11 -0
  229. package/skills/gsd-path-docs-audit/scripts/roadmap.py +5 -0
  230. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +5 -0
  231. package/skills/gsd-path-docs-audit/scripts/state_promote.py +5 -0
  232. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
  233. package/skills/gsd-path-forensics/SKILL.md +9 -5
  234. package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
  235. package/skills/gsd-path-forensics/scripts/_common.py +5 -0
  236. package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
  237. package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
  238. package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
  239. package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
  240. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +3 -0
  241. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +3 -0
  242. package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
  243. package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
  244. package/skills/gsd-path-inspect/SKILL.md +2 -0
  245. package/skills/gsd-path-inspect/references/dispatch.md +6 -19
  246. package/skills/gsd-path-inspect/references/model-policy.md +139 -0
  247. package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
  248. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  249. package/skills/gsd-path-inspect/scripts/_common.py +5 -0
  250. package/skills/gsd-path-inspect/scripts/archive_milestone.py +20 -6
  251. package/skills/gsd-path-inspect/scripts/build_recovery.py +5 -0
  252. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +2 -0
  253. package/skills/gsd-path-inspect/scripts/check_handoffs.py +6 -0
  254. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
  255. package/skills/gsd-path-inspect/scripts/detect_project.py +11 -1
  256. package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
  257. package/skills/gsd-path-inspect/scripts/discussion_validate.py +8 -3
  258. package/skills/gsd-path-inspect/scripts/integration.py +28 -8
  259. package/skills/gsd-path-inspect/scripts/isolation.py +15 -5
  260. package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
  261. package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
  262. package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
  263. package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
  264. package/skills/gsd-path-inspect/scripts/pipeline_state.py +4 -1
  265. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -0
  266. package/skills/gsd-path-inspect/scripts/review_panel.py +11 -0
  267. package/skills/gsd-path-inspect/scripts/roadmap.py +5 -0
  268. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +5 -0
  269. package/skills/gsd-path-inspect/scripts/state_promote.py +5 -0
  270. package/skills/gsd-path-inspect/scripts/workflow_run.py +12 -1
  271. package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
  272. package/skills/gsd-path-loop/SKILL.md +2 -0
  273. package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
  274. package/skills/gsd-path-loop/scripts/_common.py +5 -0
  275. package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
  276. package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
  277. package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
  278. package/skills/gsd-path-loop/scripts/path_config.py +248 -0
  279. package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
  280. package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
  281. package/skills/gsd-path-migrate/SKILL.md +2 -0
  282. package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
  283. package/skills/gsd-path-migrate/scripts/_common.py +185 -0
  284. package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
  285. package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
  286. package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
  287. package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
  288. package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
  289. package/skills/gsd-path-plan/SKILL.md +4 -2
  290. package/skills/gsd-path-plan/references/dispatch.md +6 -19
  291. package/skills/gsd-path-plan/references/model-policy.md +139 -0
  292. package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
  293. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  294. package/skills/gsd-path-plan/scripts/_common.py +5 -0
  295. package/skills/gsd-path-plan/scripts/archive_milestone.py +20 -6
  296. package/skills/gsd-path-plan/scripts/build_recovery.py +5 -0
  297. package/skills/gsd-path-plan/scripts/check_handoffs.py +6 -0
  298. package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
  299. package/skills/gsd-path-plan/scripts/detect_project.py +11 -1
  300. package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
  301. package/skills/gsd-path-plan/scripts/discussion_validate.py +8 -3
  302. package/skills/gsd-path-plan/scripts/integration.py +28 -8
  303. package/skills/gsd-path-plan/scripts/isolation.py +15 -5
  304. package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
  305. package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
  306. package/skills/gsd-path-plan/scripts/path_config.py +248 -0
  307. package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
  308. package/skills/gsd-path-plan/scripts/pipeline_state.py +4 -1
  309. package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -0
  310. package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
  311. package/skills/gsd-path-plan/scripts/roadmap.py +5 -0
  312. package/skills/gsd-path-plan/scripts/state_checkpoint.py +5 -0
  313. package/skills/gsd-path-plan/scripts/state_promote.py +5 -0
  314. package/skills/gsd-path-plan/scripts/workflow_run.py +12 -1
  315. package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
  316. package/skills/gsd-path-plan/templates/task.md +2 -0
  317. package/skills/gsd-path-research/SKILL.md +2 -0
  318. package/skills/gsd-path-research/references/dispatch.md +6 -19
  319. package/skills/gsd-path-research/references/model-policy.md +139 -0
  320. package/skills/gsd-path-research/references/runtime-selection.md +25 -0
  321. package/skills/gsd-path-research/scripts/_common.py +5 -0
  322. package/skills/gsd-path-research/scripts/archive_milestone.py +20 -6
  323. package/skills/gsd-path-research/scripts/build_recovery.py +5 -0
  324. package/skills/gsd-path-research/scripts/check_handoffs.py +6 -0
  325. package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
  326. package/skills/gsd-path-research/scripts/detect_project.py +11 -1
  327. package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
  328. package/skills/gsd-path-research/scripts/discussion_validate.py +8 -3
  329. package/skills/gsd-path-research/scripts/integration.py +28 -8
  330. package/skills/gsd-path-research/scripts/isolation.py +15 -5
  331. package/skills/gsd-path-research/scripts/loop_run.py +839 -0
  332. package/skills/gsd-path-research/scripts/model_policy.py +279 -0
  333. package/skills/gsd-path-research/scripts/path_config.py +248 -0
  334. package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
  335. package/skills/gsd-path-research/scripts/pipeline_state.py +4 -1
  336. package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -0
  337. package/skills/gsd-path-research/scripts/review_panel.py +11 -0
  338. package/skills/gsd-path-research/scripts/roadmap.py +5 -0
  339. package/skills/gsd-path-research/scripts/state_checkpoint.py +5 -0
  340. package/skills/gsd-path-research/scripts/state_promote.py +5 -0
  341. package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
  342. package/skills/gsd-path-roadmap/SKILL.md +2 -0
  343. package/skills/gsd-path-roadmap/references/dispatch.md +6 -19
  344. package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
  345. package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
  346. package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
  347. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +20 -6
  348. package/skills/gsd-path-roadmap/scripts/build_recovery.py +5 -0
  349. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +6 -0
  350. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
  351. package/skills/gsd-path-roadmap/scripts/detect_project.py +11 -1
  352. package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
  353. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
  354. package/skills/gsd-path-roadmap/scripts/integration.py +28 -8
  355. package/skills/gsd-path-roadmap/scripts/isolation.py +15 -5
  356. package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
  357. package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
  358. package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
  359. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
  360. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +4 -1
  361. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -0
  362. package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
  363. package/skills/gsd-path-roadmap/scripts/roadmap.py +5 -0
  364. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +5 -0
  365. package/skills/gsd-path-roadmap/scripts/state_promote.py +5 -0
  366. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
  367. package/skills/gsd-path-ship/SKILL.md +24 -13
  368. package/skills/gsd-path-ship/references/dispatch.md +6 -19
  369. package/skills/gsd-path-ship/references/jev-review.md +82 -0
  370. package/skills/gsd-path-ship/references/model-policy.md +139 -0
  371. package/skills/gsd-path-ship/references/reviewer-panel.md +1 -1
  372. package/skills/gsd-path-ship/references/reviewer.md +2 -0
  373. package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
  374. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  375. package/skills/gsd-path-ship/scripts/_common.py +5 -0
  376. package/skills/gsd-path-ship/scripts/archive_milestone.py +20 -6
  377. package/skills/gsd-path-ship/scripts/build_recovery.py +5 -0
  378. package/skills/gsd-path-ship/scripts/build_state.py +2 -0
  379. package/skills/gsd-path-ship/scripts/check_handoffs.py +6 -0
  380. package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
  381. package/skills/gsd-path-ship/scripts/detect_project.py +11 -1
  382. package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
  383. package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
  384. package/skills/gsd-path-ship/scripts/integration.py +28 -8
  385. package/skills/gsd-path-ship/scripts/isolation.py +15 -5
  386. package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
  387. package/skills/gsd-path-ship/scripts/lean_verification.py +2 -0
  388. package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
  389. package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
  390. package/skills/gsd-path-ship/scripts/path_config.py +248 -0
  391. package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
  392. package/skills/gsd-path-ship/scripts/pipeline_state.py +4 -1
  393. package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -0
  394. package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
  395. package/skills/gsd-path-ship/scripts/roadmap.py +5 -0
  396. package/skills/gsd-path-ship/scripts/state_checkpoint.py +5 -0
  397. package/skills/gsd-path-ship/scripts/state_promote.py +5 -0
  398. package/skills/gsd-path-ship/scripts/workflow_run.py +12 -1
  399. package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
  400. package/skills/gsd-path-undo/SKILL.md +2 -0
  401. package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
  402. package/skills/gsd-path-undo/scripts/_common.py +5 -0
  403. package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
  404. package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
  405. package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
  406. package/skills/gsd-path-undo/scripts/path_config.py +248 -0
  407. package/skills/gsd-path-undo/scripts/pipeline_undo.py +3 -0
  408. package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
  409. package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
  410. package/skills/path/BUILD.md +7 -0
  411. package/skills/path/DECIDE.md +2 -0
  412. package/skills/path/DEFINE.md +13 -7
  413. package/skills/path/DOCS-AUDIT.md +2 -0
  414. package/skills/path/INSPECT.md +2 -0
  415. package/skills/path/PLAN.md +4 -2
  416. package/skills/path/RESEARCH.md +2 -0
  417. package/skills/path/ROADMAP.md +2 -0
  418. package/skills/path/SHIP.md +24 -13
  419. package/skills/path/SKILL.md +23 -9
  420. package/skills/path/references/build-native.md +15 -7
  421. package/skills/path/references/config.md +57 -0
  422. package/skills/path/references/dispatch.md +6 -19
  423. package/skills/path/references/jev-review.md +82 -0
  424. package/skills/path/references/model-policy.md +139 -0
  425. package/skills/path/references/reviewer-panel.md +1 -1
  426. package/skills/path/references/reviewer.md +2 -0
  427. package/skills/path/references/runtime-selection.md +25 -0
  428. package/skills/path/scripts/_common.py +5 -0
  429. package/skills/path/scripts/archive_milestone.py +20 -6
  430. package/skills/path/scripts/bootstrap_repository.py +8 -1
  431. package/skills/path/scripts/build_recovery.py +5 -0
  432. package/skills/path/scripts/build_state.py +2 -0
  433. package/skills/path/scripts/check_docs_audit.py +2 -0
  434. package/skills/path/scripts/check_handoffs.py +6 -0
  435. package/skills/path/scripts/check_task_briefs.py +3 -0
  436. package/skills/path/scripts/detect_project.py +11 -1
  437. package/skills/path/scripts/discussion_records.py +3 -0
  438. package/skills/path/scripts/discussion_validate.py +8 -3
  439. package/skills/path/scripts/dispatch_driver.py +230 -21
  440. package/skills/path/scripts/git_guard.py +768 -0
  441. package/skills/path/scripts/integration.py +28 -8
  442. package/skills/path/scripts/isolation.py +15 -5
  443. package/skills/path/scripts/jev_review.py +138 -0
  444. package/skills/path/scripts/lean_verification.py +2 -0
  445. package/skills/path/scripts/loop_run.py +2 -0
  446. package/skills/path/scripts/model_policy.py +279 -0
  447. package/skills/path/scripts/path_config.py +248 -0
  448. package/skills/path/scripts/pipeline_git.py +3 -0
  449. package/skills/path/scripts/pipeline_state.py +4 -1
  450. package/skills/path/scripts/promote_lookahead.py +3 -0
  451. package/skills/path/scripts/review_panel.py +11 -0
  452. package/skills/path/scripts/roadmap.py +5 -0
  453. package/skills/path/scripts/state_checkpoint.py +5 -0
  454. package/skills/path/scripts/state_promote.py +5 -0
  455. package/skills/path/scripts/status_runtime.py +208 -0
  456. package/skills/path/scripts/workflow_run.py +12 -1
  457. package/skills/path/scripts/worktree_paths.py +94 -0
  458. package/skills/path/templates/skeptic.md +1 -1
  459. package/skills/path/templates/task.md +2 -0
  460. package/skills/path/templates/wave-panel.md +1 -1
  461. package/skills/gsd-path/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  462. package/skills/gsd-path/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  463. package/skills/gsd-path/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  464. package/skills/gsd-path/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  465. package/skills/gsd-path/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  466. package/skills/gsd-path/scripts/__pycache__/discussion_records.cpython-312.pyc +0 -0
  467. package/skills/gsd-path/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  468. package/skills/gsd-path/scripts/__pycache__/integration.cpython-312.pyc +0 -0
  469. package/skills/gsd-path/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  470. package/skills/gsd-path/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  471. package/skills/gsd-path/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  472. package/skills/gsd-path/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  473. package/skills/gsd-path/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  474. package/skills/gsd-path/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  475. package/skills/gsd-path/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  476. package/skills/gsd-path/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  477. package/skills/gsd-path/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  478. package/skills/gsd-path/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  479. package/skills/gsd-path-build/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  480. package/skills/gsd-path-build/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  481. package/skills/gsd-path-build/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  482. package/skills/gsd-path-build/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  483. package/skills/gsd-path-build/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  484. package/skills/gsd-path-build/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  485. package/skills/gsd-path-build/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  486. package/skills/gsd-path-build/scripts/__pycache__/loop_run.cpython-312.pyc +0 -0
  487. package/skills/gsd-path-build/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  488. package/skills/gsd-path-build/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  489. package/skills/gsd-path-build/scripts/__pycache__/review_findings.cpython-312.pyc +0 -0
  490. package/skills/gsd-path-build/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  491. package/skills/gsd-path-build/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  492. package/skills/gsd-path-build/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  493. package/skills/gsd-path-build/scripts/__pycache__/task_context.cpython-312.pyc +0 -0
  494. package/skills/gsd-path-build/scripts/__pycache__/workflow_run.cpython-312.pyc +0 -0
  495. package/skills/gsd-path-decide/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  496. package/skills/gsd-path-decide/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  497. package/skills/gsd-path-decide/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  498. package/skills/gsd-path-decide/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  499. package/skills/gsd-path-decide/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  500. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  501. package/skills/gsd-path-decide/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  502. package/skills/gsd-path-decide/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  503. package/skills/gsd-path-decide/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  504. package/skills/gsd-path-decide/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  505. package/skills/gsd-path-define/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  506. package/skills/gsd-path-define/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  507. package/skills/gsd-path-define/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  508. package/skills/gsd-path-define/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  509. package/skills/gsd-path-define/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  510. package/skills/gsd-path-define/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  511. package/skills/gsd-path-define/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  512. package/skills/gsd-path-define/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  513. package/skills/gsd-path-define/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  514. package/skills/gsd-path-define/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  515. package/skills/gsd-path-docs-audit/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  516. package/skills/gsd-path-docs-audit/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  517. package/skills/gsd-path-docs-audit/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  518. package/skills/gsd-path-docs-audit/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  519. package/skills/gsd-path-docs-audit/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  520. package/skills/gsd-path-docs-audit/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  521. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  522. package/skills/gsd-path-docs-audit/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  523. package/skills/gsd-path-docs-audit/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  524. package/skills/gsd-path-docs-audit/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  525. package/skills/gsd-path-docs-audit/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  526. package/skills/gsd-path-forensics/scripts/__pycache__/pipeline_undo.cpython-312.pyc +0 -0
  527. package/skills/gsd-path-inspect/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  528. package/skills/gsd-path-inspect/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  529. package/skills/gsd-path-inspect/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  530. package/skills/gsd-path-inspect/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  531. package/skills/gsd-path-inspect/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  532. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  533. package/skills/gsd-path-inspect/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  534. package/skills/gsd-path-inspect/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  535. package/skills/gsd-path-inspect/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  536. package/skills/gsd-path-inspect/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  537. package/skills/gsd-path-plan/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  538. package/skills/gsd-path-plan/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  539. package/skills/gsd-path-plan/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  540. package/skills/gsd-path-plan/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  541. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  542. package/skills/gsd-path-plan/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  543. package/skills/gsd-path-plan/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  544. package/skills/gsd-path-plan/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  545. package/skills/gsd-path-plan/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  546. package/skills/gsd-path-research/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  547. package/skills/gsd-path-research/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  548. package/skills/gsd-path-research/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  549. package/skills/gsd-path-research/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  550. package/skills/gsd-path-research/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  551. package/skills/gsd-path-research/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  552. package/skills/gsd-path-research/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  553. package/skills/gsd-path-research/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  554. package/skills/gsd-path-research/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  555. package/skills/gsd-path-research/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  556. package/skills/gsd-path-roadmap/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  557. package/skills/gsd-path-roadmap/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  558. package/skills/gsd-path-roadmap/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  559. package/skills/gsd-path-roadmap/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  560. package/skills/gsd-path-roadmap/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  561. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  562. package/skills/gsd-path-roadmap/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  563. package/skills/gsd-path-roadmap/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  564. package/skills/gsd-path-roadmap/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  565. package/skills/gsd-path-roadmap/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
  566. package/skills/gsd-path-ship/scripts/__pycache__/archive_milestone.cpython-312.pyc +0 -0
  567. package/skills/gsd-path-ship/scripts/__pycache__/build_recovery.cpython-312.pyc +0 -0
  568. package/skills/gsd-path-ship/scripts/__pycache__/build_state.cpython-312.pyc +0 -0
  569. package/skills/gsd-path-ship/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  570. package/skills/gsd-path-ship/scripts/__pycache__/check_task_briefs.cpython-312.pyc +0 -0
  571. package/skills/gsd-path-ship/scripts/__pycache__/detect_project.cpython-312.pyc +0 -0
  572. package/skills/gsd-path-ship/scripts/__pycache__/discussion_validate.cpython-312.pyc +0 -0
  573. package/skills/gsd-path-ship/scripts/__pycache__/isolation.cpython-312.pyc +0 -0
  574. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_git.cpython-312.pyc +0 -0
  575. package/skills/gsd-path-ship/scripts/__pycache__/pipeline_state.cpython-312.pyc +0 -0
  576. package/skills/gsd-path-ship/scripts/__pycache__/review_panel.cpython-312.pyc +0 -0
  577. package/skills/gsd-path-ship/scripts/__pycache__/roadmap.cpython-312.pyc +0 -0
  578. package/skills/gsd-path-ship/scripts/__pycache__/state_checkpoint.cpython-312.pyc +0 -0
@@ -0,0 +1,248 @@
1
+ #!/usr/bin/env python3
2
+ # gsd-path project runtime
3
+ """Show and edit Path preferences without advancing a pipeline phase."""
4
+ import argparse
5
+ import json
6
+ import re
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ sys.dont_write_bytecode = True
12
+
13
+ ROLES = ('coder', 'reviewer', 'review_panel', 'skeptic', 'inspect_codebase',
14
+ 'inspect_docs', 'docs_audit', 'research', 'decide', 'roadmap', 'plan', 'plan_patch')
15
+ HOSTS = ('codex', 'claude', 'grok', 'opencode', 'copilot', 'qwen', 'antigravity',
16
+ 'cursor', 'zed', 'kiro', 'kimi')
17
+
18
+
19
+ def sibling(name):
20
+ import importlib
21
+ return importlib.import_module(f'{__package__}.{name}' if __package__ else name)
22
+
23
+
24
+ def safe_path(path):
25
+ # Resolve host aliases (macOS /var) at the repo/home boundary, not here.
26
+ for item in (path, path.parent):
27
+ if item.is_symlink():
28
+ raise ValueError(f'unsafe symlink: {item}')
29
+ if path.exists() and (not path.is_file() or path.stat().st_nlink != 1):
30
+ raise ValueError(f'expected a regular unlinked file: {path}')
31
+ return path
32
+
33
+
34
+ def read_object(path):
35
+ safe_path(path)
36
+ if not path.exists():
37
+ return {}
38
+ value = json.loads(path.read_text(encoding='utf-8'))
39
+ if not isinstance(value, dict):
40
+ raise ValueError(f'{path} must contain an object')
41
+ return value
42
+
43
+
44
+ def user_path():
45
+ return Path.home().resolve() / '.gsd-path/config.json'
46
+
47
+
48
+ def model_key(key):
49
+ bits = key.split('.')
50
+ if len(bits) == 4 and bits[:2] == ['models', 'roles']:
51
+ role, field = bits[2:]
52
+ elif len(bits) == 5 and bits[:2] == ['models', 'hosts'] and bits[2] in HOSTS:
53
+ role, field = bits[3:]
54
+ else:
55
+ raise ValueError(f'unknown setting: {key}')
56
+ if role not in ROLES or field not in ('model', 'effort'):
57
+ raise ValueError(f'unknown setting: {key}')
58
+ return bits
59
+
60
+
61
+ def flatten(data, prefix=''):
62
+ result = {}
63
+ for key, value in data.items():
64
+ name = f'{prefix}.{key}' if prefix else key
65
+ if isinstance(value, dict):
66
+ result.update(flatten(value, name))
67
+ else:
68
+ result[name] = value
69
+ return result
70
+
71
+
72
+ def validate_value(key, value):
73
+ if not isinstance(value, str) or not value or value != value.strip():
74
+ raise ValueError(f'{key} requires a nonempty string')
75
+ if key == 'integration':
76
+ if value not in ('direct', 'pull-request'):
77
+ raise ValueError('integration must be direct or pull-request')
78
+ elif key == 'review_panel':
79
+ if any(char.isspace() for char in value):
80
+ raise ValueError('use off, detected, or comma-separated families without spaces')
81
+ sibling('review_panel').parse_review_panel_value(value)
82
+ else:
83
+ model_key(key)
84
+
85
+
86
+ def validate_models(data):
87
+ if not isinstance(data, dict) or set(data) - {'roles', 'hosts'}:
88
+ raise ValueError('models must contain roles and/or hosts')
89
+ for group, entries in data.items():
90
+ if not isinstance(entries, dict):
91
+ raise ValueError(f'models.{group} must be an object')
92
+ groups = [('roles', entries)] if group == 'roles' else [(f'hosts.{host}', roles) for host, roles in entries.items()]
93
+ for prefix, roles in groups:
94
+ if not isinstance(roles, dict) or (prefix.startswith('hosts.') and prefix[6:] not in HOSTS):
95
+ raise ValueError(f'unknown or malformed model group: {prefix}')
96
+ for role, fields in roles.items():
97
+ if role not in ROLES or not isinstance(fields, dict):
98
+ raise ValueError(f'unknown or malformed role: {role}')
99
+ for field, value in fields.items():
100
+ validate_value(f'models.{prefix}.{role}.{field}', value)
101
+ return data
102
+
103
+
104
+ def preferences(path, user=False):
105
+ data = read_object(path)
106
+ allowed = {'integration', 'review_panel', 'models'} if user else {'review_panel'}
107
+ if set(data) - allowed:
108
+ raise ValueError(f'{path}: unknown setting')
109
+ for key, value in data.items():
110
+ if key == 'models':
111
+ validate_models(value)
112
+ else:
113
+ validate_value(key, value)
114
+ return data
115
+
116
+
117
+ def user_defaults():
118
+ return preferences(user_path(), user=True)
119
+
120
+
121
+ def initial_state(text):
122
+ mode = user_defaults().get('integration', 'direct')
123
+ for key in ('integration_default', 'integration'):
124
+ text = re.sub(rf'(?m)^{key}: direct\b', f'{key}: {mode}', text)
125
+ return text
126
+
127
+
128
+ def show(repo, scope='project'):
129
+ defaults = user_defaults()
130
+ values = {'integration': {'value': defaults.get('integration', 'direct'),
131
+ 'source': str(user_path()) if 'integration' in defaults else 'built-in'},
132
+ 'review_panel': {'value': defaults.get('review_panel', 'off'),
133
+ 'source': str(user_path()) if 'review_panel' in defaults else 'built-in'}}
134
+ models = {key: {'value': value, 'source': str(user_path())}
135
+ for key, value in flatten(defaults.get('models', {}), 'models').items()}
136
+ result = {'scope': scope, 'settings': values, 'models': models, 'roles': ROLES,
137
+ 'hosts': HOSTS, 'locked': None, 'approved_review_panel': None}
138
+ if scope == 'project':
139
+ state_api = sibling('pipeline_state')
140
+ repo = state_api._repo_root(repo)
141
+ state, text, path = state_api.load_state(repo)
142
+ config = repo / '.project/config.json'
143
+ local = preferences(config)
144
+ if 'review_panel' in local:
145
+ values['review_panel'] = {'value': local['review_panel'], 'source': str(config)}
146
+ policy_path = repo / '.project/model-policy.json'
147
+ policy = validate_models(read_object(policy_path))
148
+ models.update({key: {'value': value, 'source': str(policy_path)}
149
+ for key, value in flatten(policy, 'models').items()})
150
+ values['integration'] = {'value': state.integration_default, 'source': str(path),
151
+ 'current': state.integration, 'current_source': state.integration_source}
152
+ locked = state.phase in ('build', 'ship', 'shipped') or sibling('build_recovery').context(repo, text)
153
+ values['integration']['locked'] = 'Shipping mode is locked when build starts.' if locked else None
154
+ if state.phase in ('ship', 'shipped') or state.archive:
155
+ result['locked'] = 'Project settings are locked during ship and after shipment.'
156
+ panel = sibling('review_panel')
157
+ paths = [repo / '.project/plan/PLAN.md', repo / '.project/intent/INTENT.md', repo / '.project/CHARTER.md']
158
+ texts = [p.read_text(encoding='utf-8') if safe_path(p).exists() else None for p in paths]
159
+ approved = panel.parse_sources(*texts)
160
+ result['approved_review_panel'] = {'value': ','.join(approved['families']) if approved['mode'] == 'named' else approved['mode'],
161
+ 'source': approved['source']}
162
+ return result
163
+
164
+
165
+ def set_value(repo, scope, key, value, reset=False):
166
+ repo = repo.resolve()
167
+ if not reset:
168
+ validate_value(key, value)
169
+ elif key not in ('integration', 'review_panel'):
170
+ model_key(key)
171
+ current = show(repo, scope)
172
+ if current['locked']:
173
+ raise ValueError(current['locked'])
174
+ if scope == 'project':
175
+ state, _, _ = sibling('pipeline_state').load_state(repo)
176
+ branch = subprocess.run(['git', '-C', str(repo), 'branch', '--show-current'],
177
+ capture_output=True, text=True)
178
+ if branch.returncode or not state.branch or branch.stdout.strip() != state.branch:
179
+ raise ValueError('Run the router to bind the project branch before editing settings.')
180
+ head = subprocess.run(['git', '-C', str(repo), 'rev-parse', '--verify', 'HEAD'], capture_output=True)
181
+ if head.returncode == 0:
182
+ closed = subprocess.run([sys.executable, '-B', str(Path(__file__).with_name('git_guard.py')),
183
+ 'closed-milestone'], cwd=repo, capture_output=True, text=True)
184
+ if closed.returncode:
185
+ raise ValueError(closed.stderr.strip() or 'Cannot verify the milestone branch is open.')
186
+ if scope == 'project' and key == 'integration':
187
+ if reset:
188
+ raise ValueError('choose an explicit project shipping mode')
189
+ sibling('pipeline_state').configure_integration(repo, 'default', value)
190
+ return show(repo, scope)
191
+ path = user_path() if scope == 'user' else repo / '.project' / ('model-policy.json' if key.startswith('models.') else 'config.json')
192
+ safe_path(path)
193
+ safe_path(path.with_name(f'.{path.name}.lock'))
194
+ lock = (sibling('pipeline_state')._state_lock(repo / '.project') if scope == 'project'
195
+ else sibling('loop_run').log_lock({'log': str(path)}))
196
+ with lock:
197
+ current = show(repo, scope)
198
+ if current['locked']:
199
+ raise ValueError(current['locked'])
200
+ data = read_object(path)
201
+ bits = key.split('.')
202
+ if scope == 'project' and bits[0] == 'models':
203
+ bits = bits[1:]
204
+ node = data
205
+ for bit in bits[:-1]:
206
+ node = node.setdefault(bit, {})
207
+ if reset:
208
+ node.pop(bits[-1], None)
209
+ else:
210
+ node[bits[-1]] = value
211
+ safe_path(path)
212
+ safe_path(path.with_name(f'.{path.name}.gsd-path-tmp'))
213
+ sibling('_common').atomic_write(path, json.dumps(data, indent=2, sort_keys=True) + '\n')
214
+ return show(repo, scope)
215
+
216
+
217
+ def main():
218
+ parser = argparse.ArgumentParser(description=__doc__)
219
+ parser.add_argument('action', choices=('show', 'validate', 'set', 'reset'))
220
+ parser.add_argument('key', nargs='?')
221
+ parser.add_argument('value', nargs='?')
222
+ parser.add_argument('--scope', choices=('user', 'project'), default='project')
223
+ parser.add_argument('--repo', type=Path, default=Path.cwd())
224
+ args = parser.parse_args()
225
+ try:
226
+ declaration = args.repo / '.gsd-path/runtime.json'
227
+ if args.scope == 'project' and (declaration.exists() or declaration.is_symlink()):
228
+ runtime = sibling('status_runtime').resolve_runtime(args.repo.resolve())
229
+ helper = runtime / 'path_config.py'
230
+ if not helper.is_file():
231
+ raise ValueError('Update the selected project runtime to use Path settings.')
232
+ if helper.resolve() != Path(__file__).resolve():
233
+ return subprocess.call([sys.executable, '-B', str(helper), *sys.argv[1:]])
234
+ if args.action in ('set', 'reset'):
235
+ if not args.key or (args.action == 'set' and args.value is None):
236
+ raise ValueError('set requires KEY VALUE; reset requires KEY')
237
+ result = set_value(args.repo, args.scope, args.key, args.value, args.action == 'reset')
238
+ else:
239
+ result = show(args.repo, args.scope)
240
+ print(json.dumps(result))
241
+ return 0
242
+ except (OSError, ValueError, RuntimeError) as error:
243
+ print(json.dumps({'error': str(error)}))
244
+ return 2
245
+
246
+
247
+ if __name__ == '__main__':
248
+ raise SystemExit(main())
@@ -17,6 +17,9 @@ import re
17
17
  import stat
18
18
  import subprocess
19
19
  import sys
20
+
21
+ # Runtime helpers must not modify their immutable installation.
22
+ sys.dont_write_bytecode = True
20
23
  from pathlib import Path
21
24
  from typing import Optional, Sequence
22
25
 
@@ -19,6 +19,9 @@ import shutil
19
19
  import stat
20
20
  import subprocess
21
21
  import sys
22
+
23
+ # Runtime helpers must not modify their immutable installation.
24
+ sys.dont_write_bytecode = True
22
25
  from dataclasses import asdict, dataclass
23
26
  from datetime import date
24
27
  from pathlib import Path, PurePosixPath
@@ -615,7 +618,7 @@ def _completion_status(repo: Path, state: Mapping[str, object], project_dir: str
615
618
  import integration
616
619
  try:
617
620
  proof = integration.validate_integrated(
618
- repo, str(state.get("milestone") or ""), refresh=False)
621
+ repo, str(state.get("milestone") or ""), refresh=False, historical=True)
619
622
  except (integration.ArchiveError, OSError) as error:
620
623
  return {"status": "unverified", "reason": str(error)}
621
624
  return {"status": "verified", "proof": proof}
@@ -12,6 +12,9 @@ import re
12
12
  import stat
13
13
  import subprocess
14
14
  import sys
15
+
16
+ # Runtime helpers must not modify their immutable installation.
17
+ sys.dont_write_bytecode = True
15
18
  from pathlib import Path
16
19
  from typing import Optional, Sequence
17
20
 
@@ -15,6 +15,9 @@ import argparse
15
15
  import json
16
16
  import re
17
17
  import sys
18
+
19
+ # Runtime helpers must not modify their immutable installation.
20
+ sys.dont_write_bytecode = True
18
21
  from pathlib import Path
19
22
  from typing import Iterable, Optional, Sequence
20
23
 
@@ -214,6 +217,7 @@ def resolve_panel(
214
217
  advertised: Sequence[str],
215
218
  parent_family: Optional[str] = None,
216
219
  parent_slug: Optional[str] = None,
220
+ excluded_families: Sequence[str] = (),
217
221
  ) -> dict:
218
222
  inferred_parent = parent_family or (
219
223
  family_of_slug(parent_slug) if parent_slug else None
@@ -244,7 +248,12 @@ def resolve_panel(
244
248
  "named review_panel families are not advertised: " + ", ".join(missing)
245
249
  )
246
250
 
251
+ if config["mode"] == "named" and set(wanted) & set(excluded_families):
252
+ raise ReviewPanelError("named review_panel must use an independent family")
247
253
  for family in wanted:
254
+ if family in excluded_families:
255
+ skipped.append({"family": family, "reason": "excluded reviewer family"})
256
+ continue
248
257
  if family == inferred_parent:
249
258
  skipped.append({"family": family, "reason": "parent family"})
250
259
  continue
@@ -488,6 +497,7 @@ def parser() -> argparse.ArgumentParser:
488
497
  result.add_argument("--advertised", default="")
489
498
  result.add_argument("--parent-family")
490
499
  result.add_argument("--parent-slug")
500
+ result.add_argument("--exclude-family", action="append", choices=KNOWN_FAMILIES, default=[])
491
501
  result.add_argument("--kind", choices=("plan", "wave"))
492
502
  result.add_argument("--inputs", default="")
493
503
  result.add_argument("--output", type=Path)
@@ -541,6 +551,7 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
541
551
  split_csv(arguments.advertised),
542
552
  arguments.parent_family,
543
553
  arguments.parent_slug,
554
+ arguments.exclude_family,
544
555
  )
545
556
  return emit(resolved)
546
557
  except ReviewPanelError as error:
@@ -3,6 +3,11 @@
3
3
  """Interpret Roadmap text for selection, approval, promotion and milestone close."""
4
4
  from __future__ import annotations
5
5
 
6
+ import sys
7
+
8
+ # Runtime helpers must not modify their immutable installation.
9
+ sys.dont_write_bytecode = True
10
+
6
11
  import re
7
12
  from typing import Optional
8
13
 
@@ -4,6 +4,11 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import sys
8
+
9
+ # Runtime helpers must not modify their immutable installation.
10
+ sys.dont_write_bytecode = True
11
+
7
12
  import hashlib
8
13
  import json
9
14
  import os
@@ -4,6 +4,11 @@
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
+ import sys
8
+
9
+ # Runtime helpers must not modify their immutable installation.
10
+ sys.dont_write_bytecode = True
11
+
7
12
  import json
8
13
  import re
9
14
  import shutil
@@ -0,0 +1,208 @@
1
+ #!/usr/bin/env python3
2
+ """Launch the project status runtime across an atomic refresh handoff."""
3
+
4
+ import argparse
5
+ import hashlib
6
+ import errno
7
+ import json
8
+ import os
9
+ import subprocess
10
+ import sys
11
+ import time
12
+ from pathlib import Path
13
+ from typing import Sequence
14
+
15
+ PROJECT_STATUS_MARKER = "gsd-path project status launcher"
16
+ INSTALL_LOCK_OWNER = "owner.json"
17
+ INSTALL_LOCK_SCHEMA = "gsd-path/install-lock/v2"
18
+
19
+
20
+ def process_identity(pid: int):
21
+ if os.name == "nt":
22
+ command = [
23
+ "powershell.exe",
24
+ "-NoProfile",
25
+ "-NonInteractive",
26
+ "-Command",
27
+ f"(Get-Process -Id {pid} -ErrorAction Stop).StartTime.ToUniversalTime().Ticks",
28
+ ]
29
+ else:
30
+ command = ["ps", "-o", "lstart=", "-p", str(pid)]
31
+ try:
32
+ result = subprocess.run(command, capture_output=True, text=True, check=False)
33
+ except OSError:
34
+ return None
35
+ value = result.stdout.strip()
36
+ return f"{os.name}:{value}" if result.returncode == 0 and value else None
37
+
38
+
39
+ def runtime_identity(runtime: Path):
40
+ try:
41
+ status = runtime.stat()
42
+ except FileNotFoundError:
43
+ return None
44
+ return (status.st_dev, status.st_ino, status.st_mtime_ns, status.st_size)
45
+
46
+
47
+ def process_alive(pid: int) -> bool:
48
+ try:
49
+ os.kill(pid, 0)
50
+ except OSError as error:
51
+ return error.errno == errno.EPERM
52
+ return True
53
+
54
+
55
+ def install_lock_active(lock: Path) -> bool:
56
+ if lock.is_symlink() or not lock.is_dir():
57
+ return False
58
+ try:
59
+ owner = json.loads((lock / INSTALL_LOCK_OWNER).read_text(encoding="utf-8"))
60
+ except (OSError, UnicodeError, json.JSONDecodeError):
61
+ return False
62
+ pid = owner.get("pid")
63
+ valid_pid = isinstance(pid, int) and not isinstance(pid, bool) and pid > 0
64
+ identity = owner.get("identity")
65
+ if (
66
+ owner.get("schema") != INSTALL_LOCK_SCHEMA
67
+ or not valid_pid
68
+ or not isinstance(identity, str)
69
+ ):
70
+ return False
71
+ current_identity = process_identity(pid)
72
+ if current_identity is None:
73
+ return process_alive(pid)
74
+ return current_identity == identity
75
+
76
+
77
+ RUNTIME_SCHEMA = "gsd-path/runtime/v1"
78
+
79
+
80
+ def runtime_home() -> Path:
81
+ return Path.home() / ".gsd-path" / "runtimes"
82
+
83
+
84
+ def declaration(repo: Path) -> dict:
85
+ path = repo / ".gsd-path" / "runtime.json"
86
+ if path.is_symlink() or path.parent.is_symlink():
87
+ raise ValueError(f"unsafe runtime declaration: {path}")
88
+ data = json.loads(path.read_text(encoding="utf-8"))
89
+ if (not isinstance(data, dict) or data.get("schema") != RUNTIME_SCHEMA
90
+ or not isinstance(data.get("version"), str)
91
+ or not isinstance(data.get("digest"), str)
92
+ or len(data["digest"]) != 64
93
+ or any(c not in "0123456789abcdef" for c in data["digest"])):
94
+ raise ValueError(f"invalid runtime declaration: {path}")
95
+ return data
96
+
97
+
98
+ def manifest_digest(manifest: dict) -> str:
99
+ return hashlib.sha256(json.dumps(manifest, sort_keys=True, separators=(",", ":")).encode()).hexdigest()
100
+
101
+
102
+ def validate_runtime(data: dict) -> Path:
103
+ root = runtime_home() / data["digest"]
104
+ hint = (f"Runtime {data['version']} ({data['digest']}) is unavailable or invalid at {root}. "
105
+ "Run gsd-path --runtime-restore --project <project-root> "
106
+ "--source-root <matching-package-root> to restore this exact runtime.")
107
+ try:
108
+ if any(p.is_symlink() for p in (root, root.parent, root.parent.parent)):
109
+ raise ValueError("symlinked runtime storage")
110
+ manifest_path = root / "manifest.json"
111
+ if manifest_path.is_symlink():
112
+ raise ValueError("symlinked manifest")
113
+ manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
114
+ if (not isinstance(manifest, dict) or manifest_digest(manifest) != data["digest"]
115
+ or manifest.get("version") != data["version"]):
116
+ raise ValueError("manifest identity mismatch")
117
+ files = manifest.get("files")
118
+ if not isinstance(files, dict) or "pipeline_state.py" not in files:
119
+ raise ValueError("invalid runtime file inventory")
120
+ for name, expected in files.items():
121
+ if Path(name).name != name or not name.endswith(".py"):
122
+ raise ValueError("invalid runtime file name")
123
+ path = root / name
124
+ if path.is_symlink() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
125
+ raise ValueError(f"runtime file changed: {name}")
126
+ if set(p.name for p in root.iterdir()) != set(files) | {"manifest.json"}:
127
+ raise ValueError("unexpected runtime files")
128
+ except (OSError, ValueError, TypeError) as error:
129
+ raise ValueError(hint + f" ({error})") from error
130
+ return root
131
+
132
+
133
+ def resolve_runtime(repo: Path) -> Path:
134
+ return validate_runtime(declaration(repo))
135
+
136
+
137
+ def run_guard(repo: Path, name: str) -> None:
138
+ # Keep the guard's project identity while executing verified external bytes.
139
+ root = resolve_runtime(repo)
140
+ sys.dont_write_bytecode = True
141
+ sys.path.insert(0, str(root))
142
+ code = compile((root / name).read_bytes(), str(root / name), "exec")
143
+ exec(code, {"__name__": "__main__", "__file__": str(repo / ".gsd-path" / name)})
144
+
145
+
146
+ def launch(repo: Path) -> int:
147
+ parent = repo / ".gsd-path"
148
+ if os.path.lexists(parent / "runtime.json"):
149
+ try:
150
+ runtime = resolve_runtime(repo) / "pipeline_state.py"
151
+ except (OSError, ValueError) as error:
152
+ print(str(error), file=sys.stderr)
153
+ return 2
154
+ return subprocess.call([sys.executable, "-B", str(runtime), "status", "--repo", str(repo)],
155
+ env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"})
156
+ runtime = parent / "runtime" / "pipeline_state.py"
157
+ lock = repo / ".gsd-path-install-lock"
158
+ command = [sys.executable, "-B", str(runtime), "status", "--repo", str(repo)]
159
+ environment = os.environ.copy()
160
+ environment["GIT_OPTIONAL_LOCKS"] = "0"
161
+ while True:
162
+ if install_lock_active(lock):
163
+ time.sleep(0.05)
164
+ continue
165
+ before = runtime_identity(runtime)
166
+ if before is None:
167
+ if install_lock_active(lock):
168
+ continue
169
+ if runtime_identity(runtime) is not None:
170
+ continue
171
+ if install_lock_active(lock):
172
+ continue
173
+ print(f"GSD Path status runtime is unavailable: {runtime}", file=sys.stderr)
174
+ return 2
175
+ try:
176
+ result = subprocess.run(
177
+ command, capture_output=True, check=False, env=environment
178
+ )
179
+ except OSError as error:
180
+ print(f"GSD Path status runtime failed: {error}", file=sys.stderr)
181
+ return 2
182
+ if install_lock_active(lock) or runtime_identity(runtime) != before:
183
+ continue
184
+ sys.stdout.write(result.stdout.decode())
185
+ sys.stderr.write(result.stderr.decode())
186
+ return result.returncode
187
+
188
+
189
+ def main(argv: Sequence[str] = sys.argv[1:]) -> int:
190
+ parser = argparse.ArgumentParser(description=__doc__)
191
+ parser.add_argument("--repo", required=True, type=Path)
192
+ parser.add_argument("--runtime-path", action="store_true")
193
+ args = parser.parse_args(argv)
194
+ if not args.repo.is_absolute():
195
+ print("GSD Path status repository must be absolute", file=sys.stderr)
196
+ return 2
197
+ if args.runtime_path:
198
+ try:
199
+ print(resolve_runtime(args.repo.resolve()))
200
+ return 0
201
+ except (OSError, ValueError) as error:
202
+ print(str(error), file=sys.stderr)
203
+ return 2
204
+ return launch(args.repo.resolve())
205
+
206
+
207
+ if __name__ == "__main__":
208
+ raise SystemExit(main())
@@ -4,6 +4,7 @@
4
4
  import argparse
5
5
  import hashlib
6
6
  import json
7
+ import os
7
8
  import subprocess
8
9
  import sys
9
10
  import tempfile
@@ -28,9 +29,10 @@ def run_workflow(repo: Path, action: str, project_dir: str, expected_head: str =
28
29
  steps = []
29
30
  outputs = {}
30
31
  scripts = Path(__file__).resolve().parent
32
+ helpers = scripts
31
33
 
32
34
  def step(script, *arguments, raw=False):
33
- command = [sys.executable, "-B", str(scripts / script), *arguments]
35
+ command = [sys.executable, "-B", str(helpers / script), *arguments]
34
36
  completed = subprocess.run(command, cwd=repo, capture_output=True, text=True)
35
37
  receipt = {"script": script, "command": command, "exit_code": completed.returncode,
36
38
  "stdout": completed.stdout, "stderr": completed.stderr}
@@ -49,6 +51,15 @@ def run_workflow(repo: Path, action: str, project_dir: str, expected_head: str =
49
51
 
50
52
  common = ["--repo", str(repo), "--project-dir", project_dir]
51
53
  try:
54
+ if os.path.lexists(repo / ".gsd-path/runtime.json"):
55
+ resolved = subprocess.run(
56
+ [sys.executable, "-B", str(repo / ".gsd-path/status_runtime.py"),
57
+ "--repo", str(repo), "--runtime-path"],
58
+ cwd=repo, capture_output=True, text=True)
59
+ if resolved.returncode:
60
+ sys.stderr.write(resolved.stderr)
61
+ raise StepFailed(resolved.stderr.strip())
62
+ helpers = Path(resolved.stdout.strip())
52
63
  if action == "route":
53
64
  step("pipeline_state.py", "route", *common)
54
65
  elif action == "prepare-inspect":