@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,279 @@
1
+ #!/usr/bin/env python3
2
+ # gsd-path project runtime
3
+ """Resolve explicit sub-agent model choices without model reasoning."""
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ import re
9
+ import sys
10
+ sys.dont_write_bytecode = True
11
+ from pathlib import Path
12
+
13
+ try:
14
+ from ._common import atomic_write
15
+ from .loop_run import log_lock
16
+ from .review_panel import family_of_slug
17
+ except ImportError:
18
+ from _common import atomic_write
19
+ from loop_run import log_lock
20
+ from review_panel import family_of_slug
21
+
22
+ FIELDS = ('model', 'effort')
23
+ HEAVY = ('plan', 'plan_patch', 'decide', 'roadmap')
24
+ LIGHT = ('inspect_docs', 'docs_audit')
25
+ SCHEMA = 'gsd-path/model-selection/v1'
26
+
27
+
28
+ class PolicyError(ValueError):
29
+ """An explicit selection cannot be honored."""
30
+
31
+
32
+ def object_value(value, label):
33
+ if not isinstance(value, dict):
34
+ raise PolicyError(f'{label} must be an object')
35
+ return value
36
+
37
+
38
+ def read_json(path):
39
+ try:
40
+ return object_value(json.loads(Path(path).read_text(encoding='utf-8')), str(path))
41
+ except (OSError, ValueError) as error:
42
+ raise PolicyError(f'{path}: {error}') from error
43
+
44
+
45
+ def settings(value, label):
46
+ object_value(value, label)
47
+ if set(value) - set(FIELDS):
48
+ raise PolicyError(f'{label}: unknown model setting')
49
+ for field, setting in value.items():
50
+ if not isinstance(setting, str) or not setting.strip() or setting != setting.strip():
51
+ raise PolicyError(f'{label}.{field} must be a nonempty string (or inherit)')
52
+ return value
53
+
54
+
55
+ def policy_path(repo, project_dir):
56
+ project = Path(repo) / project_dir
57
+ return (project.parent if project.name == 'next' else project) / 'model-policy.json'
58
+
59
+
60
+ def load_policy(repo, project_dir):
61
+ path = policy_path(repo, project_dir)
62
+ data = read_json(path) if path.exists() else {}
63
+ if set(data) - {'roles', 'hosts'}:
64
+ raise PolicyError(f'{path}: unknown policy field')
65
+ groups = [('roles', object_value(data.get('roles', {}), 'roles'))]
66
+ for host, group in object_value(data.get('hosts', {}), 'hosts').items():
67
+ groups.append((f'hosts.{host}', object_value(group, f'hosts.{host}')))
68
+ for label, group in groups:
69
+ for role, choice in group.items():
70
+ settings(choice, f'{label}.{role}')
71
+ return data
72
+
73
+
74
+ def resolve(repo, project_dir, role, capabilities, overrides, previous=None):
75
+ """Resolve each field once; a saved selection bypasses changed project policy."""
76
+ caps = object_value(capabilities, 'capabilities')
77
+ host = caps.get('host')
78
+ if not isinstance(host, str) or not host:
79
+ raise PolicyError(f'{role}: host identity is required for model selection')
80
+ if previous is not None:
81
+ if (previous.get('schema'), previous.get('role'), previous.get('host')) != (SCHEMA, role, host):
82
+ raise PolicyError(f'{role}: recorded selection belongs to another role or host')
83
+ selected = dict(previous['selected'])
84
+ sources = dict(previous['sources'])
85
+ policy_hash = previous['policy_hash']
86
+ for field, value in settings(overrides, 'task').items():
87
+ if value != selected[field]:
88
+ raise PolicyError(f'explicit {field}={value!r} differs from the pinned selection; reassign explicitly')
89
+ if (selected['model'] == 'inherit' and previous.get('effective_model')
90
+ and caps.get('inherited_model') != previous['effective_model']):
91
+ raise PolicyError('recorded inherited model no longer matches the advertised identity')
92
+ else:
93
+ policy = load_policy(repo, project_dir)
94
+ try:
95
+ from .path_config import user_defaults
96
+ except ImportError:
97
+ from path_config import user_defaults
98
+ try:
99
+ defaults = user_defaults().get('models', {})
100
+ except (OSError, ValueError, RuntimeError) as error:
101
+ raise PolicyError(str(error)) from error
102
+ selected = dict.fromkeys(FIELDS, 'inherit')
103
+ sources = dict.fromkeys(FIELDS, 'default')
104
+ tier = 'heavy' if role in HEAVY else 'light' if role in LIGHT else None
105
+ hint = ('high' if tier == 'heavy' else 'low') if host == 'codex' and tier else tier
106
+ hint = caps.get('effort_hints', {}).get(tier, hint)
107
+ if hint in caps.get('effort', {}).get('values', []):
108
+ selected['effort'] = hint
109
+ for label, group in (
110
+ (f'user.roles.{role}', defaults.get('roles', {}).get(role, {})),
111
+ (f'user.hosts.{host}.{role}', defaults.get('hosts', {}).get(host, {}).get(role, {})),
112
+ (f'roles.{role}', policy.get('roles', {}).get(role, {})),
113
+ (f'hosts.{host}.{role}', policy.get('hosts', {}).get(host, {}).get(role, {})),
114
+ ('task', settings(overrides, 'task')),
115
+ ):
116
+ for field, value in group.items():
117
+ selected[field], sources[field] = value, label
118
+ policy_hash = hashlib.sha256(json.dumps({'user': defaults, 'project': policy}, sort_keys=True).encode()).hexdigest()
119
+ native, cli, controls = {}, {}, {}
120
+ for field, value in selected.items():
121
+ spec = object_value(caps.get(field, {}), f'capabilities.{field}')
122
+ controls[field] = spec.get('args', [])
123
+ if value == 'inherit':
124
+ cli[field] = []
125
+ continue
126
+ if value not in spec.get('values', []):
127
+ raise PolicyError(f'{role}: {sources[field]} requests {field}={value!r}, unavailable on {host}')
128
+ native_field = spec.get('field')
129
+ if native_field:
130
+ if not isinstance(native_field, str) or not re.fullmatch(r'[A-Za-z_]\w*', native_field):
131
+ raise PolicyError(f'{host}: invalid native field for {field}')
132
+ if native_field in native:
133
+ raise PolicyError(f'{host}: model and effort use the same native field')
134
+ native[native_field] = value
135
+ args = spec.get('args', [])
136
+ if not isinstance(args, list) or any(not isinstance(arg, str) for arg in args):
137
+ raise PolicyError(f'{host}: {field} args must be an argument list')
138
+ if args and not any('{value}' in arg for arg in args):
139
+ raise PolicyError(f'{host}: {field} args must deliver {{value}}')
140
+ cli[field] = [arg.replace('{value}', value) for arg in args]
141
+ return {'schema': SCHEMA, 'role': role, 'host': host, 'selected': selected,
142
+ 'sources': sources, 'native': native, 'cli': cli, 'controls': controls, 'policy_hash': policy_hash,
143
+ 'effective_model': (selected['model'] if selected['model'] != 'inherit'
144
+ else caps.get('inherited_model')),
145
+ 'identity_evidence': 'requested controls; host execution is recorded by the dispatcher'}
146
+
147
+
148
+ def validate_panel(selection, family, excluded=()):
149
+ model = selection['effective_model']
150
+ actual = family_of_slug(model) if model else None
151
+ if actual != family or actual in excluded:
152
+ raise PolicyError(f'panel model {model!r} must belong to independent family {family}')
153
+
154
+
155
+ def command_args(argv, selection):
156
+ """Expand whole-argument slots only. Never parse or rewrite arbitrary host commands."""
157
+ result = list(argv)
158
+ if any('{model}' in arg for arg in result):
159
+ raise PolicyError('legacy model placeholder is incompatible with capability-based dispatch')
160
+ for field in FIELDS:
161
+ slot = '{' + field + '_args}'
162
+ explicit = selection['sources'][field] != 'default'
163
+ args = selection['cli'][field]
164
+ # Only match the controls declared by the adapter, not guessed CLI syntax.
165
+ templates = selection['controls'][field]
166
+ prefixes = [arg.split('{value}')[0] for arg in templates if '{value}' in arg and arg != '{value}']
167
+ flags = [arg for arg in templates if arg.startswith('-') and '{value}' not in arg]
168
+ if any(any(arg.startswith(prefix) for prefix in prefixes)
169
+ or (not prefixes and any(arg == flag or arg.startswith(flag + '=') for flag in flags))
170
+ for arg in result):
171
+ raise PolicyError(f'fixed {field} controls conflict with policy selection')
172
+ if result.count(slot) > 1 or any(slot in arg and arg != slot for arg in result):
173
+ raise PolicyError(f'{slot} must occur once as a whole argument')
174
+ if slot not in result:
175
+ if explicit:
176
+ raise PolicyError(f'explicit {field} requires a {slot} slot in the child command')
177
+ if selection['selected'][field] != 'inherit':
178
+ raise PolicyError(f'{field} hint requires a {slot} slot in the child command')
179
+ continue
180
+ if selection['selected'][field] != 'inherit' and not args:
181
+ raise PolicyError(f'host does not advertise CLI arguments for {field}')
182
+ index = result.index(slot)
183
+ result[index:index + 1] = args
184
+ return result
185
+
186
+
187
+ def task_settings(path):
188
+ # Reuse the task contract parser; nested or duplicate overrides are not accepted.
189
+ try:
190
+ from .check_handoffs import _strict_frontmatter
191
+ except ImportError:
192
+ from check_handoffs import _strict_frontmatter
193
+ fields = _strict_frontmatter(Path(path).read_text(encoding='utf-8'), str(path))
194
+ return settings({key: fields[key] for key in FIELDS if key in fields}, str(path))
195
+
196
+
197
+ def native_record(args):
198
+ """The parent owns lifecycle; release requires its recorded terminal-result receipt."""
199
+ record = args.record.resolve()
200
+ with log_lock({'log': str(record)}):
201
+ data = read_json(record) if record.exists() else None
202
+ identity = {'repo': str(args.repo.resolve()), 'project_dir': args.project_dir,
203
+ 'scope': args.scope, 'assignment': args.assignment, 'role': args.role}
204
+ if data is not None and data.get('identity') != identity:
205
+ raise PolicyError('record belongs to another assignment or milestone/track/cycle')
206
+ if args.action == 'release':
207
+ if data is None or not args.terminal_result:
208
+ raise PolicyError('release requires an existing record and terminal-result evidence')
209
+ data.update(active=False, terminal_result=args.terminal_result)
210
+ else:
211
+ if args.capabilities is None:
212
+ raise PolicyError('capabilities are required')
213
+ if args.action == 'reassign' and (data is None or data['active'] or not args.ruling):
214
+ raise PolicyError('reassign requires an inactive assignment and an explicit owner ruling')
215
+ caps = read_json(args.capabilities)
216
+ overrides = task_settings(args.task) if args.task else {}
217
+ for field in FIELDS:
218
+ value = getattr(args, field)
219
+ if value is not None:
220
+ if field in overrides and overrides[field] != value:
221
+ raise PolicyError('reassignment conflicts with the approved task contract')
222
+ overrides[field] = value
223
+ if data and args.action == 'reassign':
224
+ changed_fields = set(overrides)
225
+ overrides = dict(data['selection']['selected'], **overrides)
226
+ previous = data['selection'] if data and args.action == 'resolve' else None
227
+ selection = resolve(args.repo, args.project_dir, args.role, caps, overrides, previous)
228
+ if data and args.action == 'reassign':
229
+ for field in FIELDS:
230
+ if field not in changed_fields:
231
+ selection['sources'][field] = data['selection']['sources'][field]
232
+ if (args.panel_family and selection['sources']['model'] == 'default'
233
+ and selection['selected']['model'] == 'inherit' and args.panel_model):
234
+ selection = resolve(args.repo, args.project_dir, args.role, caps,
235
+ dict(overrides, model=args.panel_model), previous)
236
+ for field in FIELDS:
237
+ if selection['selected'][field] != 'inherit' and not caps.get(field, {}).get('field'):
238
+ raise PolicyError(f'host does not advertise a native field for {field}')
239
+ if args.panel_family:
240
+ validate_panel(selection, args.panel_family, args.exclude_family)
241
+ history = data.get('history', []) if data else []
242
+ if data and args.action == 'reassign':
243
+ history = [*history, {'selection': data['selection'], 'ruling': args.ruling,
244
+ 'terminal_result': data.get('terminal_result')}]
245
+ data = {'identity': identity, 'selection': selection, 'history': history,
246
+ 'active': args.action == 'resolve'}
247
+ atomic_write(record, json.dumps(data, indent=2, sort_keys=True) + '\n')
248
+ return {'status': 'ready', **data}
249
+
250
+
251
+ def main(argv=None):
252
+ parser = argparse.ArgumentParser(description=__doc__)
253
+ parser.add_argument('action', choices=('resolve', 'release', 'reassign'))
254
+ parser.add_argument('--repo', type=Path, required=True)
255
+ parser.add_argument('--project-dir', default='.project')
256
+ parser.add_argument('--scope', required=True, help='milestone, track and review cycle identity')
257
+ parser.add_argument('--assignment', required=True)
258
+ parser.add_argument('--role', required=True)
259
+ parser.add_argument('--record', type=Path, required=True)
260
+ parser.add_argument('--capabilities', type=Path)
261
+ parser.add_argument('--task', type=Path)
262
+ parser.add_argument('--model')
263
+ parser.add_argument('--effort')
264
+ parser.add_argument('--ruling')
265
+ parser.add_argument('--terminal-result')
266
+ parser.add_argument('--panel-family')
267
+ parser.add_argument('--panel-model', help='default model from the panel roster; policy may override it')
268
+ parser.add_argument('--exclude-family', action='append', default=[])
269
+ try:
270
+ result = native_record(parser.parse_args(argv))
271
+ except (ValueError, OSError, KeyError, TypeError) as error:
272
+ print(json.dumps({'status': 'blocked', 'reason': str(error)}))
273
+ return 1
274
+ print(json.dumps(result, sort_keys=True))
275
+ return 0
276
+
277
+
278
+ if __name__ == '__main__':
279
+ raise SystemExit(main())
@@ -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())