@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
@@ -31,7 +31,7 @@ from typing import BinaryIO, Callable, Dict, List, Optional
31
31
 
32
32
  try:
33
33
  from scripts import (_common, archive_milestone, build_state, discussion_validate, isolation,
34
- pipeline_state, review_findings, review_panel, workflow_run, task_context)
34
+ pipeline_state, review_findings, review_panel, workflow_run, task_context, model_policy)
35
35
  from scripts import check_handoffs as contracts
36
36
  except ImportError: # bundled copy inside a skill's scripts directory
37
37
  import _common
@@ -45,6 +45,7 @@ except ImportError: # bundled copy inside a skill's scripts directory
45
45
  import review_findings
46
46
  import review_panel
47
47
  import workflow_run
48
+ import model_policy
48
49
 
49
50
  if os.name == "nt":
50
51
  import msvcrt
@@ -71,7 +72,7 @@ class DriverStop(RuntimeError):
71
72
 
72
73
  STOP_ERRORS = (DriverStop, isolation.IsolationError, build_state.BuildStateError,
73
74
  contracts.HandoffError, review_findings.ReviewFindingsError,
74
- pipeline_state.PipelineStateError, archive_milestone.ArchiveError)
75
+ pipeline_state.PipelineStateError, archive_milestone.ArchiveError, model_policy.PolicyError)
75
76
  RECOVERY_BLOCKED = "recovery blocked"
76
77
  # The stops the build contract answers with build/blocked when the round raises them (steps 1 and 2);
77
78
  # the same stops from review, panel, or skeptics stay with the parent.
@@ -251,10 +252,81 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
251
252
  return "\n".join(lines) + "\n"
252
253
 
253
254
 
255
+ def selected_command(state: Dict[str, object], options: argparse.Namespace, command: str):
256
+ """Shared selection for every CLI child; legacy unconfigured commands remain unchanged."""
257
+ primary = Path(getattr(options, 'repo', state['worktree']))
258
+ project_dir = getattr(options, 'project_dir', '.project')
259
+ overrides = (model_policy.task_settings(Path(str(state['worktree'])) / str(state['task_file']))
260
+ if state.get('task_file') else {})
261
+ for field in model_policy.FIELDS:
262
+ value = getattr(options, field, None)
263
+ if value is not None:
264
+ if field in overrides and overrides[field] != value:
265
+ raise model_policy.PolicyError('dispatch choice conflicts with the task contract')
266
+ overrides[field] = value
267
+ overrides_path = getattr(options, 'model_overrides', None)
268
+ if overrides_path:
269
+ assignments = model_policy.read_json(overrides_path)
270
+ for assignment, values in assignments.items():
271
+ model_policy.settings(values, f'assignment {assignment}')
272
+ for field, value in assignments.get(str(state['task_id']), {}).items():
273
+ if field in overrides and overrides[field] != value:
274
+ raise model_policy.PolicyError('assignment overrides conflict with explicit task/dispatch settings')
275
+ overrides[field] = value
276
+ caps_path = getattr(options, 'model_capabilities', None)
277
+ previous = state.get('model_selection')
278
+ if previous is None and state.get('command'):
279
+ if overrides:
280
+ raise model_policy.PolicyError('legacy assignment requires a new explicit assignment for model controls')
281
+ return list(state['command']), None
282
+ if caps_path is None:
283
+ if overrides or previous or model_policy.policy_path(primary, project_dir).exists():
284
+ raise model_policy.PolicyError('model policy requires --model-capabilities for this dispatch')
285
+ return shlex.split(command), None
286
+ caps = model_policy.read_json(caps_path)
287
+ action = getattr(options, 'action', 'round')
288
+ role = {'round': 'coder', 'review': 'reviewer', 'panel': 'review_panel',
289
+ 'skeptics': 'skeptic'}.get(action, action)
290
+ if state.get('family'):
291
+ role = 'review_panel'
292
+ selection = model_policy.resolve(primary, project_dir, role, caps, overrides, previous)
293
+ if state.get('family'):
294
+ if selection['selected']['model'] == 'inherit' and selection['sources']['model'] == 'default':
295
+ # The panel roster owns the default model, not parent inheritance.
296
+ selection = model_policy.resolve(primary, project_dir, role, caps,
297
+ dict(overrides, model=state['slug']), previous)
298
+ model_policy.validate_panel(selection, state['family'], state.get('excluded_families', []))
299
+ state['slug'] = selection['selected']['model']
300
+ return model_policy.command_args(shlex.split(command), selection), selection
301
+
302
+
303
+ def retain_selection(root: Path, state: Dict[str, object]) -> Dict[str, object]:
304
+ state = dict(state)
305
+ task_dir = root / str(state['task_id'])
306
+ if not state.get('model_selection'):
307
+ attempts = sorted(task_dir.glob('attempt-*/state.json'), key=lambda path: attempt_number(path.parent))
308
+ if attempts:
309
+ prior = read_attempt(attempts[-1])
310
+ if child_running(prior):
311
+ raise DriverStop('cannot redispatch while the previous child is active')
312
+ if prior.get('model_selection'):
313
+ state['model_selection'] = prior['model_selection']
314
+ else:
315
+ state['command'] = prior['command']
316
+ reassignment = task_dir / 'model-reassignment.json'
317
+ if reassignment.exists():
318
+ state['model_selection'] = load_state(reassignment)['selection']
319
+ return state
320
+
321
+
254
322
  def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
255
323
  brief: Callable[[Dict[str, object]], str], command: Optional[str] = None) -> Dict[str, object]:
256
324
  """Create the next attempt record, write the brief the callback builds, and start the wrapper."""
325
+ if child_running(state):
326
+ raise DriverStop('cannot redispatch while the previous child is active')
327
+ state = retain_selection(root, state)
257
328
  task_dir = root / str(state["task_id"])
329
+ argv, selection = selected_command(state, options, command or options.child_command)
258
330
  attempt = max((attempt_number(path) for path in task_dir.glob("attempt-*")), default=0) + 1
259
331
  attempt_dir = task_dir / f"attempt-{attempt}"
260
332
  attempt_dir.mkdir(parents=True)
@@ -262,7 +334,9 @@ def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
262
334
  "usage_recorded")
263
335
  state = {key: value for key, value in state.items()
264
336
  if not key.startswith("_") and key not in stale}
265
- state.update({"attempt": attempt, "command": shlex.split(command or options.child_command),
337
+ if selection is not None:
338
+ state['model_selection'] = selection
339
+ state.update({"attempt": attempt, "command": argv,
266
340
  "child_timeout": options.child_timeout, "outcome": None,
267
341
  "dispatched_at": now(), "origin": "question" if state.get("answered") else "dispatch"})
268
342
  state_path = attempt_dir / "state.json"
@@ -397,7 +471,8 @@ def finish_task(primary: Path, state: Dict[str, object]) -> Dict[str, object]:
397
471
 
398
472
  def summary(state: Dict[str, object]) -> Dict[str, object]:
399
473
  return {key: state.get(key) for key in
400
- ("task_id", "attempt", "mode", "worktree", "wave", "cycle", "verify_heavy", "dispatched_at")}
474
+ ("task_id", "attempt", "mode", "worktree", "wave", "cycle", "verify_heavy", "dispatched_at",
475
+ "model_selection")}
401
476
 
402
477
 
403
478
  def recover_report(primary: Path, project_dir: str, receipt: Dict[str, object]) -> Dict[str, object]:
@@ -558,14 +633,23 @@ class Round:
558
633
  def settle(self) -> bool:
559
634
  """Classify every exited child. Returns True when a landing or redispatch changed the round."""
560
635
  changed = False
636
+ rejected = {item['task_id'] for item in self.receipt['blocked']
637
+ if item.get('code') == 'model-selection'}
561
638
  for state in latest_states(self.root):
562
639
  if state.get("wave") != self.receipt["wave"]:
563
640
  continue
564
641
  outcome = state.get("outcome")
565
642
  if outcome == "question":
566
643
  if state.get("answered"):
644
+ if state['task_id'] in rejected:
645
+ continue
567
646
  self.admit(str(state["task_id"]))
568
- self.launch(dict(state, answered=True))
647
+ try:
648
+ self.launch(dict(state, answered=True))
649
+ except model_policy.PolicyError as error:
650
+ self.receipt['blocked'].append({'task_id': state['task_id'], 'reason': str(error),
651
+ 'code': 'model-selection'})
652
+ continue
569
653
  update_state(state, outcome="redispatched")
570
654
  changed = True
571
655
  else:
@@ -690,23 +774,33 @@ class Round:
690
774
  heavy_busy = any(state.get("verify_heavy") for state in in_flight)
691
775
  capacity = self.options.capacity
692
776
  selected = []
777
+ rejected = {item["task_id"] for item in self.receipt["blocked"]
778
+ if item.get("code") == "model-selection"}
693
779
  for task in ready["ready"]:
694
780
  if capacity is not None and len(in_flight) + len(selected) >= capacity:
695
781
  break
696
782
  if task["wave"] != self.receipt["wave"]:
697
783
  continue
698
- if task["id"] in active_ids or (task["verify_heavy"] and heavy_busy):
784
+ if task["id"] in active_ids | rejected or (task["verify_heavy"] and heavy_busy):
699
785
  continue
700
- heavy_busy = heavy_busy or task["verify_heavy"]
701
- selected.append(task)
702
- if not selected:
703
- return False
704
- for task in selected:
705
786
  used = attempts_used(self.root, task["id"])
706
787
  if used >= self.options.max_attempts:
707
788
  raise DriverStop(f"task {task['id']} reached the attempt limit ({self.options.max_attempts}) "
708
789
  "for this milestone; a person must rule", task=task["id"], attempts=used)
709
790
  self.admit(task["id"])
791
+ candidate = retain_selection(self.root, {'task_id': task['id'], 'task_file': task['task_file'],
792
+ 'worktree': str(self.primary)})
793
+ try:
794
+ _, selection = selected_command(candidate, self.options, self.options.child_command)
795
+ except model_policy.PolicyError as error:
796
+ self.receipt['blocked'].append({'task_id': task['id'], 'reason': str(error),
797
+ 'code': 'model-selection'})
798
+ continue
799
+ task['model_selection'] = selection
800
+ heavy_busy = heavy_busy or task['verify_heavy']
801
+ selected.append(task)
802
+ if not selected:
803
+ return False
710
804
  head = isolation.current_sha(self.primary)
711
805
  self.runner("lint-round", head)
712
806
  round_size = len(selected) + len(in_flight)
@@ -721,7 +815,8 @@ class Round:
721
815
  "files": task["files"], "wave": task["wave"],
722
816
  "verify_heavy": task["verify_heavy"], "base": head, "worktree": str(worktree),
723
817
  "task_branch": isolate["task_branch"], "mode": isolate["mode"],
724
- "sidecar": sidecar, "answered": False})
818
+ "sidecar": sidecar, "answered": False,
819
+ "model_selection": task['model_selection']})
725
820
  return False
726
821
 
727
822
  def runner(self, action: str, head: str, task_id: Optional[str] = None,
@@ -738,7 +833,8 @@ class Round:
738
833
  while True:
739
834
  self.receipt["in_flight"] = []
740
835
  changed = self.settle() or changed
741
- if self.receipt["questions"] or self.receipt["blocked"]:
836
+ if self.receipt["questions"] or any(
837
+ item.get("code") != "model-selection" for item in self.receipt["blocked"]):
742
838
  self.receipt["status"] = "question" if self.receipt["questions"] else "blocked"
743
839
  return self.receipt
744
840
  # ready is expensive and its inputs only move when a landing or redispatch happened
@@ -748,7 +844,8 @@ class Round:
748
844
  changed = False
749
845
  live = self.in_flight_states()
750
846
  if deadline is None or time.monotonic() >= deadline or not live:
751
- self.receipt["status"] = "in-flight" if live else "done"
847
+ self.receipt["status"] = ("blocked" if self.receipt["blocked"]
848
+ else "in-flight" if live else "done")
752
849
  return self.receipt
753
850
  time.sleep(POLL_SECONDS)
754
851
  except STOP_ERRORS as error:
@@ -803,6 +900,15 @@ def spawn_in_sidecar(root: Path, primary: Path, receipt: Dict[str, object], opti
803
900
  name: str, state: Dict[str, object], brief: Callable[[Dict[str, object]], str],
804
901
  command: Optional[str] = None) -> None:
805
902
  """Cut a verify sidecar at the state's base and start one child in it."""
903
+ candidate = retain_selection(root, dict(state, worktree=str(primary)))
904
+ try:
905
+ _, selection = selected_command(candidate, options, command or options.child_command)
906
+ except model_policy.PolicyError as error:
907
+ receipt['blocked'].append({'task_id': state['task_id'], 'reason': str(error),
908
+ 'code': 'model-selection'})
909
+ return
910
+ if selection is not None:
911
+ state = dict(state, model_selection=selection)
806
912
  sidecar = isolation.isolate_verify(primary, str(state["base"]), name)
807
913
  receipt["steps"].append({"script": "isolation.py isolate-verify", "result": sidecar})
808
914
  spawn(root, dict(state, worktree=str(sidecar["worktree"]), branch=str(sidecar["branch"])), options, brief,
@@ -874,9 +980,21 @@ class Review:
874
980
  return wave_tasks(self.primary, self.project_dir, self.wave, self.receipt)
875
981
 
876
982
  def dispatch(self, lenses: Dict[str, str], depth: str, base: Optional[str] = None) -> None:
877
- dirty = sorted(isolation.uncommitted_paths(self.primary))
878
- if dirty: # the review base must include every bookkeeping change
879
- raise DriverStop("review collection base requires a clean primary", paths=dirty)
983
+ dirty = isolation.uncommitted_paths(self.primary)
984
+ if base is not None:
985
+ paths = review_findings.review_paths(self.primary / self.project_dir, self.wave, self.cycle, depth)
986
+ for key, state in self.current_states(list(paths)).items():
987
+ if state.get('outcome') != 'collected':
988
+ continue
989
+ path = paths[key]
990
+ relative = path.relative_to(self.primary).as_posix()
991
+ if (state.get('base') != base or state.get('relative') != relative
992
+ or not path.is_file()
993
+ or hashlib.sha256(path.read_bytes()).hexdigest() != state.get('validated_sha256')):
994
+ raise DriverStop('canonical review artifact changed after collection', path=relative)
995
+ dirty.discard(relative)
996
+ if dirty:
997
+ raise DriverStop("review collection base requires a clean primary", paths=sorted(dirty))
880
998
  tasks = self.wave_tasks()
881
999
  plan_text = (self.primary / self.project_dir / "plan/PLAN.md").read_text(encoding="utf-8")
882
1000
  intent = (self.primary / self.project_dir / "intent/INTENT.md").read_text(encoding="utf-8")
@@ -957,7 +1075,8 @@ class Review:
957
1075
 
958
1076
  def settle(self, states: Dict[str, Dict[str, object]]) -> None:
959
1077
  """Advance every lens and render the receipt from the records; settle is their only writer."""
960
- self.receipt["in_flight"], self.receipt["blocked"], self.receipt["lenses"] = [], [], {}
1078
+ rejected = [item for item in self.receipt['blocked'] if item.get('code') == 'model-selection']
1079
+ self.receipt["in_flight"], self.receipt["blocked"], self.receipt["lenses"] = [], rejected, {}
961
1080
  for key, state in states.items():
962
1081
  if not advance(state, self.collect):
963
1082
  self.receipt["in_flight"].append(summary(state))
@@ -1037,7 +1156,10 @@ class Review:
1037
1156
  states = self.current_states(list(lenses))
1038
1157
  while True:
1039
1158
  self.settle(states)
1040
- if set(states) == set(lenses) and all(
1159
+ settled = {state['task_id'] for state in states.values()}
1160
+ settled.update(item['task_id'] for item in self.receipt['blocked']
1161
+ if item.get('code') == 'model-selection')
1162
+ if settled == {self.lens_name(key) for key in lenses} and all(
1041
1163
  state.get("outcome") in ("collected", "blocked") for state in states.values()):
1042
1164
  self.conclude()
1043
1165
  return self.receipt
@@ -1102,7 +1224,8 @@ class Children:
1102
1224
  def poll(self, states: Dict[str, Dict[str, object]], deadline: Optional[float]) -> bool:
1103
1225
  """Advance every child; True once all are terminal, False when the deadline passes first."""
1104
1226
  while True:
1105
- self.receipt["in_flight"], self.receipt["blocked"], self.receipt[self.results] = [], [], {}
1227
+ rejected = [item for item in self.receipt['blocked'] if item.get('code') == 'model-selection']
1228
+ self.receipt["in_flight"], self.receipt["blocked"], self.receipt[self.results] = [], rejected, {}
1106
1229
  for key, state in states.items():
1107
1230
  if not advance(state, self.collect):
1108
1231
  self.receipt["in_flight"].append(summary(state))
@@ -1220,6 +1343,12 @@ class Panel(Children):
1220
1343
  else:
1221
1344
  if states:
1222
1345
  raise DriverStop("panel records exist without a persisted roster")
1346
+ excluded = {review_panel.family_of_slug(self.options.parent_slug or '')}
1347
+ for canonical in latest_states(records_root(self.primary) / 'reviews'):
1348
+ if canonical.get('wave') == self.wave and canonical.get('cycle') == self.cycle:
1349
+ model = canonical.get('model_selection', {}).get('effective_model')
1350
+ if model:
1351
+ excluded.add(review_panel.family_of_slug(model))
1223
1352
  arguments = ["resolve", "--plan", str(project / "plan/PLAN.md"),
1224
1353
  "--intent", str(project / "intent/INTENT.md"),
1225
1354
  "--advertised", self.options.advertised]
@@ -1227,6 +1356,8 @@ class Panel(Children):
1227
1356
  arguments += ["--parent-slug", self.options.parent_slug]
1228
1357
  if (project / "CHARTER.md").exists():
1229
1358
  arguments += ["--charter", str(project / "CHARTER.md")]
1359
+ for family in sorted(family for family in excluded if family):
1360
+ arguments += ['--exclude-family', family]
1230
1361
  resolved = helper_json(self.receipt, "review_panel.py", *arguments, cwd=self.primary)
1231
1362
  self.receipt["resolve"] = resolved
1232
1363
  if resolved["status"] == "off":
@@ -1259,6 +1390,21 @@ class Panel(Children):
1259
1390
  roster = {"families": [item["family"] for item in resolved["selected"]],
1260
1391
  "slugs": {item["family"]: item["slug"] for item in resolved["selected"]},
1261
1392
  "mode": resolved["mode"], "base": isolation.current_sha(self.primary)}
1393
+ roster['excluded_families'] = sorted(family for family in excluded if family)
1394
+ roster['selections'] = {}
1395
+ for family in roster['families']:
1396
+ candidate = {'task_id': self.family_name(family), 'family': family,
1397
+ 'slug': roster['slugs'][family], 'worktree': str(self.primary),
1398
+ 'excluded_families': roster['excluded_families']}
1399
+ command = self.options.child_command
1400
+ if not getattr(self.options, 'model_capabilities', None):
1401
+ command = command.replace('{model}', candidate['slug'])
1402
+ _, selection = selected_command(candidate, self.options, command)
1403
+ model_policy.validate_panel(selection or {'effective_model': candidate['slug']},
1404
+ family, roster['excluded_families'])
1405
+ if selection is not None:
1406
+ roster['selections'][family] = selection
1407
+ roster['slugs'][family] = candidate['slug']
1262
1408
  save_state(roster_path, roster)
1263
1409
  self.receipt["mode"] = roster["mode"]
1264
1410
  if set(states) - set(roster["families"]) or any(
@@ -1275,10 +1421,16 @@ class Panel(Children):
1275
1421
  state = {"task_id": self.family_name(family), "family": family, "slug": slug,
1276
1422
  "wave": self.wave, "cycle": self.cycle, "base": base, "mode": roster["mode"],
1277
1423
  "relative": self.relative(family)}
1424
+ if roster.get('selections', {}).get(family):
1425
+ state['model_selection'] = roster['selections'][family]
1426
+ state['excluded_families'] = roster.get('excluded_families', [])
1427
+ command = self.options.child_command
1428
+ if not getattr(self.options, 'model_capabilities', None):
1429
+ command = command.replace('{model}', slug)
1278
1430
  spawn_in_sidecar(self.root, self.primary, self.receipt, self.options,
1279
1431
  f"wave-{self.wave}-cycle-{self.cycle}-panel-{family}", state,
1280
1432
  lambda fresh: self.brief(fresh, tasks, depth, review),
1281
- command=self.options.child_command.replace("{model}", slug))
1433
+ command=command)
1282
1434
  states = self.states()
1283
1435
  if not self.poll(states, deadline):
1284
1436
  return self.receipt
@@ -1656,6 +1808,43 @@ def answer(primary: Path, task_id: str, text: str) -> Dict[str, object]:
1656
1808
  "next": "run `round` to redispatch the retained isolate"}
1657
1809
 
1658
1810
 
1811
+ def reassign_model(root: Path, task_id: str, options: argparse.Namespace) -> Dict[str, object]:
1812
+ states = [state for state in latest_states(root) if state['task_id'] == task_id]
1813
+ if not states:
1814
+ raise DriverStop('assignment has no recorded attempt')
1815
+ state = states[0]
1816
+ if child_running(state) or state.get('finished_at') is None:
1817
+ raise DriverStop('cannot reassign an active or unproven child; reconcile its lifecycle first')
1818
+ if state.get('family') or state.get('outcome') in ('landed', 'collected'):
1819
+ raise DriverStop('completed work or a pinned panel roster requires a new legal review/task assignment')
1820
+ if not options.ruling or not options.ruling.strip() or not options.model_capabilities:
1821
+ raise DriverStop('reassignment requires an owner ruling and advertised capabilities')
1822
+ old = state.get('model_selection')
1823
+ if old is None:
1824
+ raise DriverStop('legacy assignment has no proven model selection to reassign')
1825
+ path = root / task_id / 'model-reassignment.json'
1826
+ history = load_state(path).get('history', []) if path.exists() else []
1827
+ if path.exists():
1828
+ old = load_state(path)['selection']
1829
+ changes = {field: getattr(options, field) for field in model_policy.FIELDS
1830
+ if getattr(options, field) is not None}
1831
+ if not changes:
1832
+ raise DriverStop('reassignment requires an explicit model or effort')
1833
+ task = (model_policy.task_settings(Path(state['worktree']) / state['task_file'])
1834
+ if state.get('task_file') else {})
1835
+ if any(field in task and task[field] != value for field, value in changes.items()):
1836
+ raise DriverStop('reassignment conflicts with the approved task contract')
1837
+ selection = model_policy.resolve(options.repo, options.project_dir, old['role'],
1838
+ model_policy.read_json(options.model_capabilities),
1839
+ dict(old['selected'], **changes))
1840
+ for field in model_policy.FIELDS:
1841
+ if field not in changes:
1842
+ selection['sources'][field] = old['sources'][field]
1843
+ save_state(path, {'selection': selection, 'history': [*history, {
1844
+ 'selection': old, 'ruling': options.ruling, 'attempt': state['attempt']} ]})
1845
+ return {'status': 'reassigned', 'task': task_id, 'selection': selection, 'record': str(path)}
1846
+
1847
+
1659
1848
  def require_files(parser: argparse.ArgumentParser, arguments: argparse.Namespace, *names: str) -> None:
1660
1849
  for name in names:
1661
1850
  value = getattr(arguments, name)
@@ -1708,6 +1897,13 @@ def main(argv=None) -> int:
1708
1897
  cycle_child.add_argument("--wait", type=float, help="seconds to keep polling before returning")
1709
1898
  cycle_child.add_argument("--child-timeout", type=float, help="per-child wall clock in seconds")
1710
1899
  cycle_child.add_argument("--role-brief", type=Path, default=default_resource("references/reviewer.md"))
1900
+ for child in (round_parser, cycle_child):
1901
+ child.add_argument('--model-capabilities', type=Path,
1902
+ help='advertised model/effort controls; required with project model policy')
1903
+ cycle_child.add_argument('--model', help='explicit model override for this review assignment')
1904
+ cycle_child.add_argument('--effort', help='explicit effort override for this review assignment')
1905
+ cycle_child.add_argument('--model-overrides', type=Path,
1906
+ help='JSON mapping logical assignment names to explicit model/effort overrides')
1711
1907
  review_parser = commands.add_parser("review", parents=[cycle_child],
1712
1908
  help="run one wave review cycle at full or deep depth")
1713
1909
  review_parser.add_argument("--repair-evidence", type=Path,
@@ -1741,6 +1937,14 @@ def main(argv=None) -> int:
1741
1937
  answer_parser.add_argument("--answer", required=True)
1742
1938
  status_parser = commands.add_parser("status", help="list dispatch records")
1743
1939
  status_parser.add_argument("--repo", type=Path, required=True)
1940
+ model_parser = commands.add_parser('reassign-model', help='record an explicit model reassignment')
1941
+ model_parser.add_argument('--repo', type=Path, required=True)
1942
+ model_parser.add_argument('--project-dir', default='.project')
1943
+ model_parser.add_argument('--task-id', required=True)
1944
+ model_parser.add_argument('--model-capabilities', type=Path, required=True)
1945
+ model_parser.add_argument('--model')
1946
+ model_parser.add_argument('--effort')
1947
+ model_parser.add_argument('--ruling', required=True)
1744
1948
  child_parser = commands.add_parser("_child", help=argparse.SUPPRESS)
1745
1949
  child_parser.add_argument("--state", type=Path, required=True)
1746
1950
  arguments = parser.parse_args(argv)
@@ -1797,6 +2001,11 @@ def main(argv=None) -> int:
1797
2001
  result = {**current.receipt, "status": "landed" if current.receipt["landed"] else "blocked"}
1798
2002
  elif arguments.action == "answer":
1799
2003
  result = answer(primary, arguments.task_id, arguments.answer)
2004
+ elif arguments.action == 'reassign-model':
2005
+ roots = [records_root(primary), *(records_root(primary) / name
2006
+ for name in ('reviews', 'panels', 'skeptics'))]
2007
+ root = next((root for root in roots if (root / arguments.task_id).is_dir()), roots[0])
2008
+ result = reassign_model(root, arguments.task_id, arguments)
1800
2009
  else:
1801
2010
  result = {"status": "ok", "dispatches": [
1802
2011
  {key: value for key, value in state.items() if key not in ("_path", "command")}