@opengsd/gsd-path 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/AGENTS.md +33 -13
  2. package/CHANGELOG.md +68 -0
  3. package/DOCS.md +75 -6
  4. package/FULL.md +12 -15
  5. package/HOOKS.md +18 -9
  6. package/QUICK.md +8 -6
  7. package/README.md +218 -14
  8. package/RUNTIME.md +16 -5
  9. package/UPDATE.md +30 -11
  10. package/WORKFLOW.md +35 -16
  11. package/package.json +23 -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 +12 -19
  21. package/scripts/_common.py +5 -0
  22. package/scripts/archive_milestone.py +30 -124
  23. package/scripts/bootstrap_repository.py +33 -4
  24. package/scripts/build_recovery.py +292 -0
  25. package/scripts/build_state.py +2 -0
  26. package/scripts/check_docs_audit.py +7 -3
  27. package/scripts/check_handoffs.py +7 -1
  28. package/scripts/check_task_briefs.py +3 -0
  29. package/scripts/detect_project.py +22 -3
  30. package/scripts/discussion_records.py +3 -0
  31. package/scripts/discussion_validate.py +8 -3
  32. package/scripts/dispatch_driver.py +233 -22
  33. package/scripts/git_guard.py +14 -5
  34. package/scripts/guard_hook.py +11 -1
  35. package/scripts/install.mjs +54 -1448
  36. package/scripts/install.py +155 -188
  37. package/scripts/integration.py +54 -28
  38. package/scripts/isolation.py +24 -6
  39. package/scripts/jev_review.py +138 -0
  40. package/scripts/lean_verification.py +3 -1
  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 +15 -3
  45. package/scripts/pipeline_git.py +3 -0
  46. package/scripts/pipeline_state.py +117 -186
  47. package/scripts/pipeline_undo.py +14 -7
  48. package/scripts/promote_lookahead.py +33 -162
  49. package/scripts/review_panel.py +11 -0
  50. package/scripts/roadmap.py +326 -0
  51. package/scripts/runtime_store.py +278 -0
  52. package/scripts/skill-resources.json +182 -4
  53. package/scripts/state_checkpoint.py +114 -72
  54. package/scripts/state_promote.py +47 -44
  55. package/scripts/status_runtime.py +92 -6
  56. package/scripts/task_context.py +73 -0
  57. package/scripts/token_budget.py +40 -1
  58. package/scripts/workflow_run.py +128 -8
  59. package/scripts/worktree_paths.py +94 -0
  60. package/skills/gsd-path/BUILD.md +54 -575
  61. package/skills/gsd-path/DECIDE.md +4 -6
  62. package/skills/gsd-path/DEFINE.md +23 -21
  63. package/skills/gsd-path/DOCS-AUDIT.md +2 -0
  64. package/skills/gsd-path/INSPECT.md +32 -8
  65. package/skills/gsd-path/PLAN.md +29 -19
  66. package/skills/gsd-path/RESEARCH.md +5 -6
  67. package/skills/gsd-path/ROADMAP.md +24 -58
  68. package/skills/gsd-path/SHIP.md +29 -17
  69. package/skills/gsd-path/SKILL.md +46 -22
  70. package/skills/gsd-path/references/build-abandon.md +33 -0
  71. package/skills/gsd-path/references/build-native.md +502 -0
  72. package/skills/gsd-path/references/build-recovery.md +78 -0
  73. package/skills/gsd-path/references/coder.md +8 -3
  74. package/skills/gsd-path/references/config.md +57 -0
  75. package/skills/gsd-path/references/dispatch.md +12 -19
  76. package/skills/gsd-path/references/docs-auditor.md +14 -0
  77. package/skills/gsd-path/references/jev-review.md +82 -0
  78. package/skills/gsd-path/references/model-policy.md +139 -0
  79. package/skills/gsd-path/references/reviewer-final.md +21 -0
  80. package/skills/gsd-path/references/reviewer-panel.md +17 -0
  81. package/skills/gsd-path/references/reviewer-skeptic.md +20 -0
  82. package/skills/gsd-path/references/reviewer-wave.md +89 -0
  83. package/skills/gsd-path/references/reviewer.md +12 -148
  84. package/skills/gsd-path/references/runtime-selection.md +25 -0
  85. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  86. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  87. package/skills/gsd-path/scripts/_common.py +5 -0
  88. package/skills/gsd-path/scripts/archive_milestone.py +30 -124
  89. package/skills/gsd-path/scripts/bootstrap_repository.py +33 -4
  90. package/skills/gsd-path/scripts/build_recovery.py +292 -0
  91. package/skills/gsd-path/scripts/build_state.py +2 -0
  92. package/skills/gsd-path/scripts/check_docs_audit.py +7 -3
  93. package/skills/gsd-path/scripts/check_handoffs.py +7 -1
  94. package/skills/gsd-path/scripts/check_task_briefs.py +3 -0
  95. package/skills/gsd-path/scripts/detect_project.py +22 -3
  96. package/skills/gsd-path/scripts/discussion_records.py +3 -0
  97. package/skills/gsd-path/scripts/discussion_validate.py +8 -3
  98. package/skills/gsd-path/scripts/dispatch_driver.py +233 -22
  99. package/skills/gsd-path/scripts/git_guard.py +768 -0
  100. package/skills/gsd-path/scripts/integration.py +54 -28
  101. package/skills/gsd-path/scripts/isolation.py +24 -6
  102. package/skills/gsd-path/scripts/jev_review.py +138 -0
  103. package/skills/gsd-path/scripts/lean_verification.py +3 -1
  104. package/skills/gsd-path/scripts/loop_run.py +2 -0
  105. package/skills/gsd-path/scripts/model_policy.py +279 -0
  106. package/skills/gsd-path/scripts/path_config.py +248 -0
  107. package/skills/gsd-path/scripts/pipeline_git.py +3 -0
  108. package/skills/gsd-path/scripts/pipeline_state.py +117 -186
  109. package/skills/gsd-path/scripts/promote_lookahead.py +33 -162
  110. package/skills/gsd-path/scripts/review_panel.py +11 -0
  111. package/skills/gsd-path/scripts/roadmap.py +326 -0
  112. package/skills/gsd-path/scripts/state_checkpoint.py +114 -72
  113. package/skills/gsd-path/scripts/state_promote.py +47 -44
  114. package/skills/gsd-path/scripts/status_runtime.py +208 -0
  115. package/skills/gsd-path/scripts/task_context.py +73 -0
  116. package/skills/gsd-path/scripts/token_budget.py +40 -1
  117. package/skills/gsd-path/scripts/workflow_run.py +128 -8
  118. package/skills/gsd-path/scripts/worktree_paths.py +94 -0
  119. package/skills/gsd-path/templates/skeptic.md +1 -1
  120. package/skills/gsd-path/templates/task.md +2 -0
  121. package/skills/gsd-path/templates/wave-panel.md +1 -1
  122. package/skills/gsd-path/templates/wave-review.md +1 -1
  123. package/skills/gsd-path-build/SKILL.md +54 -575
  124. package/skills/gsd-path-build/references/build-abandon.md +33 -0
  125. package/skills/gsd-path-build/references/build-native.md +502 -0
  126. package/skills/gsd-path-build/references/build-recovery.md +78 -0
  127. package/skills/gsd-path-build/references/coder.md +8 -3
  128. package/skills/gsd-path-build/references/dispatch.md +12 -19
  129. package/skills/gsd-path-build/references/jev-review.md +82 -0
  130. package/skills/gsd-path-build/references/model-policy.md +139 -0
  131. package/skills/gsd-path-build/references/reviewer-final.md +21 -0
  132. package/skills/gsd-path-build/references/reviewer-panel.md +17 -0
  133. package/skills/gsd-path-build/references/reviewer-skeptic.md +20 -0
  134. package/skills/gsd-path-build/references/reviewer-wave.md +89 -0
  135. package/skills/gsd-path-build/references/reviewer.md +12 -148
  136. package/skills/gsd-path-build/references/runtime-selection.md +25 -0
  137. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  138. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  139. package/skills/gsd-path-build/scripts/_common.py +5 -0
  140. package/skills/gsd-path-build/scripts/archive_milestone.py +30 -124
  141. package/skills/gsd-path-build/scripts/build_recovery.py +292 -0
  142. package/skills/gsd-path-build/scripts/build_state.py +2 -0
  143. package/skills/gsd-path-build/scripts/check_handoffs.py +7 -1
  144. package/skills/gsd-path-build/scripts/check_task_briefs.py +3 -0
  145. package/skills/gsd-path-build/scripts/detect_project.py +1536 -0
  146. package/skills/gsd-path-build/scripts/discussion_records.py +3 -0
  147. package/skills/gsd-path-build/scripts/discussion_validate.py +8 -3
  148. package/skills/gsd-path-build/scripts/dispatch_driver.py +233 -22
  149. package/skills/gsd-path-build/scripts/integration.py +54 -28
  150. package/skills/gsd-path-build/scripts/isolation.py +24 -6
  151. package/skills/gsd-path-build/scripts/jev_review.py +138 -0
  152. package/skills/gsd-path-build/scripts/loop_run.py +2 -0
  153. package/skills/gsd-path-build/scripts/model_policy.py +279 -0
  154. package/skills/gsd-path-build/scripts/path_config.py +248 -0
  155. package/skills/gsd-path-build/scripts/pipeline_git.py +3 -0
  156. package/skills/gsd-path-build/scripts/pipeline_state.py +117 -186
  157. package/skills/gsd-path-build/scripts/promote_lookahead.py +286 -0
  158. package/skills/gsd-path-build/scripts/review_panel.py +11 -0
  159. package/skills/gsd-path-build/scripts/roadmap.py +326 -0
  160. package/skills/gsd-path-build/scripts/state_checkpoint.py +114 -72
  161. package/skills/gsd-path-build/scripts/state_promote.py +47 -44
  162. package/skills/gsd-path-build/scripts/task_context.py +73 -0
  163. package/skills/gsd-path-build/scripts/token_budget.py +40 -1
  164. package/skills/gsd-path-build/scripts/workflow_run.py +128 -8
  165. package/skills/gsd-path-build/scripts/worktree_paths.py +94 -0
  166. package/skills/gsd-path-build/templates/skeptic.md +1 -1
  167. package/skills/gsd-path-build/templates/task.md +2 -0
  168. package/skills/gsd-path-build/templates/wave-panel.md +1 -1
  169. package/skills/gsd-path-build/templates/wave-review.md +1 -1
  170. package/skills/gsd-path-decide/SKILL.md +4 -6
  171. package/skills/gsd-path-decide/references/dispatch.md +12 -19
  172. package/skills/gsd-path-decide/references/model-policy.md +139 -0
  173. package/skills/gsd-path-decide/references/runtime-selection.md +25 -0
  174. package/skills/gsd-path-decide/scripts/_common.py +5 -0
  175. package/skills/gsd-path-decide/scripts/archive_milestone.py +2171 -0
  176. package/skills/gsd-path-decide/scripts/build_recovery.py +292 -0
  177. package/skills/gsd-path-decide/scripts/check_handoffs.py +7 -1
  178. package/skills/gsd-path-decide/scripts/check_task_briefs.py +3 -0
  179. package/skills/gsd-path-decide/scripts/detect_project.py +1536 -0
  180. package/skills/gsd-path-decide/scripts/discussion_records.py +3 -0
  181. package/skills/gsd-path-decide/scripts/discussion_validate.py +1326 -0
  182. package/skills/gsd-path-decide/scripts/integration.py +1459 -0
  183. package/skills/gsd-path-decide/scripts/isolation.py +24 -6
  184. package/skills/gsd-path-decide/scripts/loop_run.py +839 -0
  185. package/skills/gsd-path-decide/scripts/model_policy.py +279 -0
  186. package/skills/gsd-path-decide/scripts/path_config.py +248 -0
  187. package/skills/gsd-path-decide/scripts/pipeline_git.py +3 -0
  188. package/skills/gsd-path-decide/scripts/pipeline_state.py +117 -186
  189. package/skills/gsd-path-decide/scripts/promote_lookahead.py +286 -0
  190. package/skills/gsd-path-decide/scripts/review_panel.py +564 -0
  191. package/skills/gsd-path-decide/scripts/roadmap.py +326 -0
  192. package/skills/gsd-path-decide/scripts/state_checkpoint.py +114 -72
  193. package/skills/gsd-path-decide/scripts/state_promote.py +47 -44
  194. package/skills/gsd-path-decide/scripts/worktree_paths.py +94 -0
  195. package/skills/gsd-path-define/SKILL.md +23 -21
  196. package/skills/gsd-path-define/references/build-recovery.md +78 -0
  197. package/skills/gsd-path-define/references/runtime-selection.md +25 -0
  198. package/skills/gsd-path-define/scripts/_common.py +5 -0
  199. package/skills/gsd-path-define/scripts/archive_milestone.py +2171 -0
  200. package/skills/gsd-path-define/scripts/build_recovery.py +292 -0
  201. package/skills/gsd-path-define/scripts/check_handoffs.py +7 -1
  202. package/skills/gsd-path-define/scripts/check_task_briefs.py +3 -0
  203. package/skills/gsd-path-define/scripts/detect_project.py +22 -3
  204. package/skills/gsd-path-define/scripts/discussion_records.py +3 -0
  205. package/skills/gsd-path-define/scripts/discussion_validate.py +1326 -0
  206. package/skills/gsd-path-define/scripts/integration.py +1459 -0
  207. package/skills/gsd-path-define/scripts/isolation.py +24 -6
  208. package/skills/gsd-path-define/scripts/loop_run.py +839 -0
  209. package/skills/gsd-path-define/scripts/model_policy.py +279 -0
  210. package/skills/gsd-path-define/scripts/path_config.py +248 -0
  211. package/skills/gsd-path-define/scripts/pipeline_git.py +3 -0
  212. package/skills/gsd-path-define/scripts/pipeline_state.py +117 -186
  213. package/skills/gsd-path-define/scripts/promote_lookahead.py +286 -0
  214. package/skills/gsd-path-define/scripts/review_panel.py +564 -0
  215. package/skills/gsd-path-define/scripts/roadmap.py +326 -0
  216. package/skills/gsd-path-define/scripts/state_checkpoint.py +114 -72
  217. package/skills/gsd-path-define/scripts/state_promote.py +47 -44
  218. package/skills/gsd-path-define/scripts/worktree_paths.py +94 -0
  219. package/skills/gsd-path-discuss/SKILL.md +2 -0
  220. package/skills/gsd-path-discuss/references/runtime-selection.md +25 -0
  221. package/skills/gsd-path-discuss/scripts/_common.py +5 -0
  222. package/skills/gsd-path-discuss/scripts/discussion_records.py +3 -0
  223. package/skills/gsd-path-discuss/scripts/loop_run.py +839 -0
  224. package/skills/gsd-path-discuss/scripts/model_policy.py +279 -0
  225. package/skills/gsd-path-discuss/scripts/path_config.py +248 -0
  226. package/skills/gsd-path-discuss/scripts/review_panel.py +564 -0
  227. package/skills/gsd-path-discuss/scripts/worktree_paths.py +94 -0
  228. package/skills/gsd-path-docs-audit/SKILL.md +2 -0
  229. package/skills/gsd-path-docs-audit/references/dispatch.md +12 -19
  230. package/skills/gsd-path-docs-audit/references/docs-auditor.md +14 -0
  231. package/skills/gsd-path-docs-audit/references/model-policy.md +139 -0
  232. package/skills/gsd-path-docs-audit/references/runtime-selection.md +25 -0
  233. package/skills/gsd-path-docs-audit/scripts/_common.py +5 -0
  234. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +2171 -0
  235. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +292 -0
  236. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +7 -3
  237. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +7 -1
  238. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +3 -0
  239. package/skills/gsd-path-docs-audit/scripts/detect_project.py +1536 -0
  240. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +3 -0
  241. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +1326 -0
  242. package/skills/gsd-path-docs-audit/scripts/integration.py +1459 -0
  243. package/skills/gsd-path-docs-audit/scripts/isolation.py +24 -6
  244. package/skills/gsd-path-docs-audit/scripts/loop_run.py +839 -0
  245. package/skills/gsd-path-docs-audit/scripts/model_policy.py +279 -0
  246. package/skills/gsd-path-docs-audit/scripts/path_config.py +248 -0
  247. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +3 -0
  248. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +117 -186
  249. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +286 -0
  250. package/skills/gsd-path-docs-audit/scripts/review_panel.py +564 -0
  251. package/skills/gsd-path-docs-audit/scripts/roadmap.py +326 -0
  252. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +114 -72
  253. package/skills/gsd-path-docs-audit/scripts/state_promote.py +47 -44
  254. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +94 -0
  255. package/skills/gsd-path-forensics/SKILL.md +9 -5
  256. package/skills/gsd-path-forensics/references/runtime-selection.md +25 -0
  257. package/skills/gsd-path-forensics/scripts/_common.py +5 -0
  258. package/skills/gsd-path-forensics/scripts/discussion_records.py +3 -0
  259. package/skills/gsd-path-forensics/scripts/loop_run.py +839 -0
  260. package/skills/gsd-path-forensics/scripts/model_policy.py +279 -0
  261. package/skills/gsd-path-forensics/scripts/path_config.py +248 -0
  262. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +15 -3
  263. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +14 -7
  264. package/skills/gsd-path-forensics/scripts/review_panel.py +564 -0
  265. package/skills/gsd-path-forensics/scripts/worktree_paths.py +94 -0
  266. package/skills/gsd-path-inspect/SKILL.md +32 -8
  267. package/skills/gsd-path-inspect/references/dispatch.md +12 -19
  268. package/skills/gsd-path-inspect/references/docs-auditor.md +14 -0
  269. package/skills/gsd-path-inspect/references/model-policy.md +139 -0
  270. package/skills/gsd-path-inspect/references/runtime-selection.md +25 -0
  271. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  272. package/skills/gsd-path-inspect/scripts/_common.py +5 -0
  273. package/skills/gsd-path-inspect/scripts/archive_milestone.py +2171 -0
  274. package/skills/gsd-path-inspect/scripts/build_recovery.py +292 -0
  275. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +7 -3
  276. package/skills/gsd-path-inspect/scripts/check_handoffs.py +7 -1
  277. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +3 -0
  278. package/skills/gsd-path-inspect/scripts/detect_project.py +22 -3
  279. package/skills/gsd-path-inspect/scripts/discussion_records.py +3 -0
  280. package/skills/gsd-path-inspect/scripts/discussion_validate.py +1326 -0
  281. package/skills/gsd-path-inspect/scripts/integration.py +1459 -0
  282. package/skills/gsd-path-inspect/scripts/isolation.py +24 -6
  283. package/skills/gsd-path-inspect/scripts/loop_run.py +839 -0
  284. package/skills/gsd-path-inspect/scripts/model_policy.py +279 -0
  285. package/skills/gsd-path-inspect/scripts/path_config.py +248 -0
  286. package/skills/gsd-path-inspect/scripts/pipeline_git.py +3 -0
  287. package/skills/gsd-path-inspect/scripts/pipeline_state.py +117 -186
  288. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +286 -0
  289. package/skills/gsd-path-inspect/scripts/review_panel.py +564 -0
  290. package/skills/gsd-path-inspect/scripts/roadmap.py +326 -0
  291. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +114 -72
  292. package/skills/gsd-path-inspect/scripts/state_promote.py +47 -44
  293. package/skills/gsd-path-inspect/scripts/workflow_run.py +237 -0
  294. package/skills/gsd-path-inspect/scripts/worktree_paths.py +94 -0
  295. package/skills/gsd-path-loop/SKILL.md +2 -0
  296. package/skills/gsd-path-loop/references/runtime-selection.md +25 -0
  297. package/skills/gsd-path-loop/scripts/_common.py +5 -0
  298. package/skills/gsd-path-loop/scripts/discussion_records.py +3 -0
  299. package/skills/gsd-path-loop/scripts/loop_run.py +2 -0
  300. package/skills/gsd-path-loop/scripts/model_policy.py +279 -0
  301. package/skills/gsd-path-loop/scripts/path_config.py +248 -0
  302. package/skills/gsd-path-loop/scripts/review_panel.py +564 -0
  303. package/skills/gsd-path-loop/scripts/worktree_paths.py +94 -0
  304. package/skills/gsd-path-migrate/SKILL.md +2 -0
  305. package/skills/gsd-path-migrate/references/runtime-selection.md +25 -0
  306. package/skills/gsd-path-migrate/scripts/_common.py +185 -0
  307. package/skills/gsd-path-migrate/scripts/discussion_records.py +3 -0
  308. package/skills/gsd-path-migrate/scripts/loop_run.py +839 -0
  309. package/skills/gsd-path-migrate/scripts/model_policy.py +279 -0
  310. package/skills/gsd-path-migrate/scripts/path_config.py +248 -0
  311. package/skills/gsd-path-migrate/scripts/review_panel.py +564 -0
  312. package/skills/gsd-path-plan/SKILL.md +29 -19
  313. package/skills/gsd-path-plan/references/build-recovery.md +78 -0
  314. package/skills/gsd-path-plan/references/dispatch.md +12 -19
  315. package/skills/gsd-path-plan/references/model-policy.md +139 -0
  316. package/skills/gsd-path-plan/references/runtime-selection.md +25 -0
  317. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  318. package/skills/gsd-path-plan/scripts/_common.py +5 -0
  319. package/skills/gsd-path-plan/scripts/archive_milestone.py +2171 -0
  320. package/skills/gsd-path-plan/scripts/build_recovery.py +292 -0
  321. package/skills/gsd-path-plan/scripts/check_handoffs.py +7 -1
  322. package/skills/gsd-path-plan/scripts/check_task_briefs.py +3 -0
  323. package/skills/gsd-path-plan/scripts/detect_project.py +1536 -0
  324. package/skills/gsd-path-plan/scripts/discussion_records.py +3 -0
  325. package/skills/gsd-path-plan/scripts/discussion_validate.py +1326 -0
  326. package/skills/gsd-path-plan/scripts/integration.py +1459 -0
  327. package/skills/gsd-path-plan/scripts/isolation.py +24 -6
  328. package/skills/gsd-path-plan/scripts/loop_run.py +839 -0
  329. package/skills/gsd-path-plan/scripts/model_policy.py +279 -0
  330. package/skills/gsd-path-plan/scripts/path_config.py +248 -0
  331. package/skills/gsd-path-plan/scripts/pipeline_git.py +3 -0
  332. package/skills/gsd-path-plan/scripts/pipeline_state.py +117 -186
  333. package/skills/gsd-path-plan/scripts/promote_lookahead.py +286 -0
  334. package/skills/gsd-path-plan/scripts/review_panel.py +11 -0
  335. package/skills/gsd-path-plan/scripts/roadmap.py +326 -0
  336. package/skills/gsd-path-plan/scripts/state_checkpoint.py +114 -72
  337. package/skills/gsd-path-plan/scripts/state_promote.py +47 -44
  338. package/skills/gsd-path-plan/scripts/workflow_run.py +128 -8
  339. package/skills/gsd-path-plan/scripts/worktree_paths.py +94 -0
  340. package/skills/gsd-path-plan/templates/task.md +2 -0
  341. package/skills/gsd-path-research/SKILL.md +5 -6
  342. package/skills/gsd-path-research/references/dispatch.md +12 -19
  343. package/skills/gsd-path-research/references/model-policy.md +139 -0
  344. package/skills/gsd-path-research/references/runtime-selection.md +25 -0
  345. package/skills/gsd-path-research/scripts/_common.py +5 -0
  346. package/skills/gsd-path-research/scripts/archive_milestone.py +2171 -0
  347. package/skills/gsd-path-research/scripts/build_recovery.py +292 -0
  348. package/skills/gsd-path-research/scripts/check_handoffs.py +7 -1
  349. package/skills/gsd-path-research/scripts/check_task_briefs.py +3 -0
  350. package/skills/gsd-path-research/scripts/detect_project.py +1536 -0
  351. package/skills/gsd-path-research/scripts/discussion_records.py +3 -0
  352. package/skills/gsd-path-research/scripts/discussion_validate.py +1326 -0
  353. package/skills/gsd-path-research/scripts/integration.py +1459 -0
  354. package/skills/gsd-path-research/scripts/isolation.py +24 -6
  355. package/skills/gsd-path-research/scripts/loop_run.py +839 -0
  356. package/skills/gsd-path-research/scripts/model_policy.py +279 -0
  357. package/skills/gsd-path-research/scripts/path_config.py +248 -0
  358. package/skills/gsd-path-research/scripts/pipeline_git.py +3 -0
  359. package/skills/gsd-path-research/scripts/pipeline_state.py +117 -186
  360. package/skills/gsd-path-research/scripts/promote_lookahead.py +286 -0
  361. package/skills/gsd-path-research/scripts/review_panel.py +564 -0
  362. package/skills/gsd-path-research/scripts/roadmap.py +326 -0
  363. package/skills/gsd-path-research/scripts/state_checkpoint.py +114 -72
  364. package/skills/gsd-path-research/scripts/state_promote.py +47 -44
  365. package/skills/gsd-path-research/scripts/worktree_paths.py +94 -0
  366. package/skills/gsd-path-roadmap/SKILL.md +24 -58
  367. package/skills/gsd-path-roadmap/references/dispatch.md +12 -19
  368. package/skills/gsd-path-roadmap/references/model-policy.md +139 -0
  369. package/skills/gsd-path-roadmap/references/runtime-selection.md +25 -0
  370. package/skills/gsd-path-roadmap/scripts/_common.py +5 -0
  371. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +30 -124
  372. package/skills/gsd-path-roadmap/scripts/build_recovery.py +292 -0
  373. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +7 -1
  374. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +3 -0
  375. package/skills/gsd-path-roadmap/scripts/detect_project.py +22 -3
  376. package/skills/gsd-path-roadmap/scripts/discussion_records.py +3 -0
  377. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +8 -3
  378. package/skills/gsd-path-roadmap/scripts/integration.py +54 -28
  379. package/skills/gsd-path-roadmap/scripts/isolation.py +24 -6
  380. package/skills/gsd-path-roadmap/scripts/loop_run.py +839 -0
  381. package/skills/gsd-path-roadmap/scripts/model_policy.py +279 -0
  382. package/skills/gsd-path-roadmap/scripts/path_config.py +248 -0
  383. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +3 -0
  384. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +117 -186
  385. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +33 -162
  386. package/skills/gsd-path-roadmap/scripts/review_panel.py +11 -0
  387. package/skills/gsd-path-roadmap/scripts/roadmap.py +326 -0
  388. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +114 -72
  389. package/skills/gsd-path-roadmap/scripts/state_promote.py +47 -44
  390. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +94 -0
  391. package/skills/gsd-path-ship/SKILL.md +29 -17
  392. package/skills/gsd-path-ship/references/dispatch.md +12 -19
  393. package/skills/gsd-path-ship/references/jev-review.md +82 -0
  394. package/skills/gsd-path-ship/references/model-policy.md +139 -0
  395. package/skills/gsd-path-ship/references/reviewer-final.md +21 -0
  396. package/skills/gsd-path-ship/references/reviewer-panel.md +17 -0
  397. package/skills/gsd-path-ship/references/reviewer-skeptic.md +20 -0
  398. package/skills/gsd-path-ship/references/reviewer-wave.md +89 -0
  399. package/skills/gsd-path-ship/references/reviewer.md +12 -148
  400. package/skills/gsd-path-ship/references/runtime-selection.md +25 -0
  401. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  402. package/skills/gsd-path-ship/scripts/_common.py +5 -0
  403. package/skills/gsd-path-ship/scripts/archive_milestone.py +30 -124
  404. package/skills/gsd-path-ship/scripts/build_recovery.py +292 -0
  405. package/skills/gsd-path-ship/scripts/build_state.py +2 -0
  406. package/skills/gsd-path-ship/scripts/check_handoffs.py +7 -1
  407. package/skills/gsd-path-ship/scripts/check_task_briefs.py +3 -0
  408. package/skills/gsd-path-ship/scripts/detect_project.py +1536 -0
  409. package/skills/gsd-path-ship/scripts/discussion_records.py +3 -0
  410. package/skills/gsd-path-ship/scripts/discussion_validate.py +8 -3
  411. package/skills/gsd-path-ship/scripts/integration.py +54 -28
  412. package/skills/gsd-path-ship/scripts/isolation.py +24 -6
  413. package/skills/gsd-path-ship/scripts/jev_review.py +138 -0
  414. package/skills/gsd-path-ship/scripts/lean_verification.py +3 -1
  415. package/skills/gsd-path-ship/scripts/loop_run.py +839 -0
  416. package/skills/gsd-path-ship/scripts/model_policy.py +279 -0
  417. package/skills/gsd-path-ship/scripts/path_config.py +248 -0
  418. package/skills/gsd-path-ship/scripts/pipeline_git.py +3 -0
  419. package/skills/gsd-path-ship/scripts/pipeline_state.py +117 -186
  420. package/skills/gsd-path-ship/scripts/promote_lookahead.py +286 -0
  421. package/skills/gsd-path-ship/scripts/review_panel.py +11 -0
  422. package/skills/gsd-path-ship/scripts/roadmap.py +326 -0
  423. package/skills/gsd-path-ship/scripts/state_checkpoint.py +114 -72
  424. package/skills/gsd-path-ship/scripts/state_promote.py +47 -44
  425. package/skills/gsd-path-ship/scripts/workflow_run.py +128 -8
  426. package/skills/gsd-path-ship/scripts/worktree_paths.py +94 -0
  427. package/skills/gsd-path-ship/templates/wave-review.md +1 -1
  428. package/skills/gsd-path-undo/SKILL.md +2 -0
  429. package/skills/gsd-path-undo/references/runtime-selection.md +25 -0
  430. package/skills/gsd-path-undo/scripts/_common.py +5 -0
  431. package/skills/gsd-path-undo/scripts/discussion_records.py +3 -0
  432. package/skills/gsd-path-undo/scripts/loop_run.py +839 -0
  433. package/skills/gsd-path-undo/scripts/model_policy.py +279 -0
  434. package/skills/gsd-path-undo/scripts/path_config.py +248 -0
  435. package/skills/gsd-path-undo/scripts/pipeline_undo.py +14 -7
  436. package/skills/gsd-path-undo/scripts/review_panel.py +564 -0
  437. package/skills/gsd-path-undo/scripts/worktree_paths.py +94 -0
  438. package/skills/path/BUILD.md +54 -575
  439. package/skills/path/DECIDE.md +4 -6
  440. package/skills/path/DEFINE.md +23 -21
  441. package/skills/path/DOCS-AUDIT.md +2 -0
  442. package/skills/path/INSPECT.md +32 -8
  443. package/skills/path/PLAN.md +29 -19
  444. package/skills/path/RESEARCH.md +5 -6
  445. package/skills/path/ROADMAP.md +24 -58
  446. package/skills/path/SHIP.md +29 -17
  447. package/skills/path/SKILL.md +46 -22
  448. package/skills/path/references/build-abandon.md +33 -0
  449. package/skills/path/references/build-native.md +502 -0
  450. package/skills/path/references/build-recovery.md +78 -0
  451. package/skills/path/references/coder.md +8 -3
  452. package/skills/path/references/config.md +57 -0
  453. package/skills/path/references/dispatch.md +12 -19
  454. package/skills/path/references/docs-auditor.md +14 -0
  455. package/skills/path/references/jev-review.md +82 -0
  456. package/skills/path/references/model-policy.md +139 -0
  457. package/skills/path/references/reviewer-final.md +21 -0
  458. package/skills/path/references/reviewer-panel.md +17 -0
  459. package/skills/path/references/reviewer-skeptic.md +20 -0
  460. package/skills/path/references/reviewer-wave.md +89 -0
  461. package/skills/path/references/reviewer.md +12 -148
  462. package/skills/path/references/runtime-selection.md +25 -0
  463. package/skills/path/scripts/_common.py +5 -0
  464. package/skills/path/scripts/archive_milestone.py +30 -124
  465. package/skills/path/scripts/bootstrap_repository.py +33 -4
  466. package/skills/path/scripts/build_recovery.py +292 -0
  467. package/skills/path/scripts/build_state.py +2 -0
  468. package/skills/path/scripts/check_docs_audit.py +7 -3
  469. package/skills/path/scripts/check_handoffs.py +7 -1
  470. package/skills/path/scripts/check_task_briefs.py +3 -0
  471. package/skills/path/scripts/detect_project.py +22 -3
  472. package/skills/path/scripts/discussion_records.py +3 -0
  473. package/skills/path/scripts/discussion_validate.py +8 -3
  474. package/skills/path/scripts/dispatch_driver.py +233 -22
  475. package/skills/path/scripts/git_guard.py +768 -0
  476. package/skills/path/scripts/integration.py +54 -28
  477. package/skills/path/scripts/isolation.py +24 -6
  478. package/skills/path/scripts/jev_review.py +138 -0
  479. package/skills/path/scripts/lean_verification.py +3 -1
  480. package/skills/path/scripts/loop_run.py +2 -0
  481. package/skills/path/scripts/model_policy.py +279 -0
  482. package/skills/path/scripts/path_config.py +248 -0
  483. package/skills/path/scripts/pipeline_git.py +3 -0
  484. package/skills/path/scripts/pipeline_state.py +117 -186
  485. package/skills/path/scripts/promote_lookahead.py +33 -162
  486. package/skills/path/scripts/review_panel.py +11 -0
  487. package/skills/path/scripts/roadmap.py +326 -0
  488. package/skills/path/scripts/state_checkpoint.py +114 -72
  489. package/skills/path/scripts/state_promote.py +47 -44
  490. package/skills/path/scripts/status_runtime.py +208 -0
  491. package/skills/path/scripts/task_context.py +73 -0
  492. package/skills/path/scripts/token_budget.py +40 -1
  493. package/skills/path/scripts/workflow_run.py +128 -8
  494. package/skills/path/scripts/worktree_paths.py +94 -0
  495. package/skills/path/templates/skeptic.md +1 -1
  496. package/skills/path/templates/task.md +2 -0
  497. package/skills/path/templates/wave-panel.md +1 -1
  498. package/skills/path/templates/wave-review.md +1 -1
@@ -0,0 +1,564 @@
1
+ #!/usr/bin/env python3
2
+ # gsd-path project runtime
3
+ """Resolve optional GSD Path cross-model review panels without model reasoning.
4
+
5
+ The orchestrator inspects the host's advertised child-model slugs and passes
6
+ them here. This helper parses PLAN.md / INTENT.md / CHARTER.md (PLAN wins,
7
+ then INTENT, then CHARTER, else off), groups slugs into families, skips the
8
+ parent family, caps the panel at three, and merges family artifacts. It never
9
+ probes a host or overrides the canonical inherit reviewer.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import re
17
+ import sys
18
+
19
+ # Runtime helpers must not modify their immutable installation.
20
+ sys.dont_write_bytecode = True
21
+ from pathlib import Path
22
+ from typing import Iterable, Optional, Sequence
23
+
24
+
25
+ KNOWN_FAMILIES = (
26
+ "claude",
27
+ "gpt",
28
+ "grok",
29
+ "composer",
30
+ "gemini",
31
+ "deepseek",
32
+ "kimi",
33
+ "qwen",
34
+ )
35
+ FAMILY_ALIASES = {
36
+ "claude": "claude",
37
+ "opus": "claude",
38
+ "sonnet": "claude",
39
+ "haiku": "claude",
40
+ "gpt": "gpt",
41
+ "openai": "gpt",
42
+ "grok": "grok",
43
+ "composer": "composer",
44
+ "gemini": "gemini",
45
+ "deepseek": "deepseek",
46
+ "kimi": "kimi",
47
+ "moonshot": "kimi",
48
+ "qwen": "qwen",
49
+ }
50
+ MAX_FAMILIES = 3
51
+ PLAN_LINE = re.compile(
52
+ r"^-\s*review_panel:\s*(\S.*?)\s*(?:<!--.*-->)?\s*$", re.MULTILINE
53
+ )
54
+ INTENT_LINE = re.compile(
55
+ r"^Review panel:\s*(\S.*?)\s*(?:<!--.*-->)?\s*$", re.MULTILINE
56
+ )
57
+ VERSION_NUMBERS = re.compile(r"\d+")
58
+ TOKEN_SPLIT = re.compile(r"[^a-z0-9]+")
59
+ FINDING_BLOCK = re.compile(
60
+ r"^###\s+(?P<id>F\d+)\s*$"
61
+ r"(?P<body>.*?)(?=^###\s+F\d+\s*$|\Z)",
62
+ re.MULTILINE | re.DOTALL,
63
+ )
64
+ FIELD_LINE = re.compile(r"^-\s*(?P<key>[A-Za-z][A-Za-z ]*):\s*(?P<value>.*)\s*$")
65
+
66
+
67
+ class ReviewPanelError(RuntimeError):
68
+ pass
69
+
70
+
71
+ def _first_token(value: str) -> str:
72
+ return value.strip().split()[0].strip().strip("`")
73
+
74
+
75
+ def _split_families(value: str) -> tuple[str, ...]:
76
+ tokens = [token.strip().casefold() for token in value.split(",") if token.strip()]
77
+ if not tokens:
78
+ raise ReviewPanelError("review_panel named list is empty")
79
+ unknown = [token for token in tokens if token not in KNOWN_FAMILIES]
80
+ if unknown:
81
+ raise ReviewPanelError(
82
+ f"unknown review_panel families: {', '.join(unknown)}"
83
+ )
84
+ seen = []
85
+ for token in tokens:
86
+ if token not in seen:
87
+ seen.append(token)
88
+ if len(seen) > MAX_FAMILIES:
89
+ raise ReviewPanelError(
90
+ f"review_panel names at most {MAX_FAMILIES} families"
91
+ )
92
+ return tuple(seen)
93
+
94
+
95
+ def parse_review_panel_value(raw: str) -> dict:
96
+ value = raw.strip()
97
+ if not value:
98
+ raise ReviewPanelError("review_panel value is empty")
99
+ token = _first_token(value).casefold()
100
+ if token == "off":
101
+ return {"mode": "off", "families": ()}
102
+ if token == "detected":
103
+ return {"mode": "detected", "families": ()}
104
+ families = _split_families(value)
105
+ return {"mode": "named", "families": families}
106
+
107
+
108
+ def extract_config_value(text: str, pattern: re.Pattern[str], source: str) -> Optional[str]:
109
+ matches = pattern.findall(text)
110
+ if not matches:
111
+ return None
112
+ if len(matches) > 1:
113
+ raise ReviewPanelError(f"{source} has multiple review_panel values")
114
+ return matches[0]
115
+
116
+
117
+ def parse_sources(
118
+ plan_text: Optional[str],
119
+ intent_text: Optional[str],
120
+ charter_text: Optional[str] = None,
121
+ ) -> dict:
122
+ if plan_text is not None:
123
+ plan_value = extract_config_value(plan_text, PLAN_LINE, "PLAN.md")
124
+ if plan_value is not None:
125
+ parsed = parse_review_panel_value(plan_value)
126
+ parsed["source"] = "plan"
127
+ return parsed
128
+ if intent_text is not None:
129
+ intent_value = extract_config_value(intent_text, INTENT_LINE, "INTENT.md")
130
+ if intent_value is not None:
131
+ parsed = parse_review_panel_value(intent_value)
132
+ parsed["source"] = "intent"
133
+ return parsed
134
+ if charter_text is not None:
135
+ charter_value = extract_config_value(charter_text, INTENT_LINE, "CHARTER.md")
136
+ if charter_value is not None:
137
+ parsed = parse_review_panel_value(charter_value)
138
+ parsed["source"] = "charter"
139
+ return parsed
140
+ return {"mode": "off", "families": (), "source": "default"}
141
+
142
+
143
+ def read_optional(path: Optional[Path]) -> Optional[str]:
144
+ if path is None:
145
+ return None
146
+ resolved = path.resolve()
147
+ if resolved.is_symlink() or not resolved.is_file():
148
+ raise ReviewPanelError(f"path must be a real file: {resolved}")
149
+ return resolved.read_text(encoding="utf-8")
150
+
151
+
152
+ def parse_files(
153
+ plan: Optional[Path],
154
+ intent: Optional[Path],
155
+ charter: Optional[Path] = None,
156
+ ) -> dict:
157
+ return parse_sources(
158
+ read_optional(plan), read_optional(intent), read_optional(charter)
159
+ )
160
+
161
+
162
+ def plan_review_panel_enabled(plan_text: str) -> bool:
163
+ """Archive-safe enabled check: anything other than absent/off is on."""
164
+ try:
165
+ parsed = parse_sources(plan_text, None)
166
+ except ReviewPanelError:
167
+ return True
168
+ return parsed["mode"] != "off"
169
+
170
+
171
+ def family_of_slug(slug: str) -> Optional[str]:
172
+ normalized = slug.strip().casefold()
173
+ if not normalized or normalized == "inherit":
174
+ return None
175
+ tokens = TOKEN_SPLIT.split(normalized)
176
+ for token in tokens:
177
+ if token in FAMILY_ALIASES:
178
+ return FAMILY_ALIASES[token]
179
+ for token in tokens:
180
+ stem = token.rstrip("0123456789")
181
+ if stem and stem in FAMILY_ALIASES:
182
+ return FAMILY_ALIASES[stem]
183
+ return None
184
+
185
+
186
+ def version_key(slug: str) -> tuple[int, ...]:
187
+ numbers = tuple(int(part) for part in VERSION_NUMBERS.findall(slug))
188
+ return numbers if numbers else (0,)
189
+
190
+
191
+ def latest_slug(slugs: Sequence[str]) -> str:
192
+ return max(slugs, key=lambda slug: (version_key(slug), len(slug), slug))
193
+
194
+
195
+ def group_advertised(slugs: Iterable[str]) -> dict[str, list[str]]:
196
+ grouped: dict[str, list[str]] = {family: [] for family in KNOWN_FAMILIES}
197
+ for slug in slugs:
198
+ cleaned = slug.strip()
199
+ if not cleaned:
200
+ continue
201
+ family = family_of_slug(cleaned)
202
+ if family is None:
203
+ continue
204
+ if cleaned not in grouped[family]:
205
+ grouped[family].append(cleaned)
206
+ return {family: values for family, values in grouped.items() if values}
207
+
208
+
209
+ def split_csv(raw: Optional[str]) -> tuple[str, ...]:
210
+ if not raw:
211
+ return ()
212
+ return tuple(part.strip() for part in raw.split(",") if part.strip())
213
+
214
+
215
+ def resolve_panel(
216
+ config: dict,
217
+ advertised: Sequence[str],
218
+ parent_family: Optional[str] = None,
219
+ parent_slug: Optional[str] = None,
220
+ excluded_families: Sequence[str] = (),
221
+ ) -> dict:
222
+ inferred_parent = parent_family or (
223
+ family_of_slug(parent_slug) if parent_slug else None
224
+ )
225
+ grouped = group_advertised(advertised)
226
+ skipped = []
227
+ selected = []
228
+
229
+ if config["mode"] == "off":
230
+ return {
231
+ "mode": "off",
232
+ "status": "off",
233
+ "reason": "review_panel is off",
234
+ "parent_family": inferred_parent,
235
+ "selected": [],
236
+ "skipped": [],
237
+ "missing": [],
238
+ }
239
+
240
+ wanted = (
241
+ list(config["families"])
242
+ if config["mode"] == "named"
243
+ else [family for family in KNOWN_FAMILIES if family in grouped]
244
+ )
245
+ missing = [family for family in wanted if family not in grouped]
246
+ if config["mode"] == "named" and missing:
247
+ raise ReviewPanelError(
248
+ "named review_panel families are not advertised: " + ", ".join(missing)
249
+ )
250
+
251
+ if config["mode"] == "named" and set(wanted) & set(excluded_families):
252
+ raise ReviewPanelError("named review_panel must use an independent family")
253
+ for family in wanted:
254
+ if family in excluded_families:
255
+ skipped.append({"family": family, "reason": "excluded reviewer family"})
256
+ continue
257
+ if family == inferred_parent:
258
+ skipped.append({"family": family, "reason": "parent family"})
259
+ continue
260
+ slugs = grouped.get(family)
261
+ if not slugs:
262
+ skipped.append({"family": family, "reason": "not advertised"})
263
+ continue
264
+ selected.append({"family": family, "slug": latest_slug(slugs)})
265
+ if len(selected) >= MAX_FAMILIES:
266
+ overflow = wanted[wanted.index(family) + 1 :]
267
+ for extra in overflow:
268
+ if extra != inferred_parent and extra in grouped:
269
+ skipped.append({"family": extra, "reason": "cap"})
270
+ break
271
+
272
+ if selected:
273
+ return {
274
+ "mode": config["mode"],
275
+ "status": "ready",
276
+ "reason": "panel resolved",
277
+ "parent_family": inferred_parent,
278
+ "selected": selected,
279
+ "skipped": skipped,
280
+ "missing": missing if config["mode"] == "detected" else [],
281
+ }
282
+
283
+ if config["mode"] == "named":
284
+ raise ReviewPanelError(
285
+ "named review_panel would only repeat the parent family or is empty"
286
+ )
287
+ return {
288
+ "mode": config["mode"],
289
+ "status": "skipped",
290
+ "reason": "no advertised cross-model families",
291
+ "parent_family": inferred_parent,
292
+ "selected": [],
293
+ "skipped": skipped,
294
+ "missing": missing,
295
+ }
296
+
297
+
298
+ def _require_field(body: str, key: str, source: str) -> str:
299
+ for line in body.splitlines():
300
+ match = FIELD_LINE.fullmatch(line.strip())
301
+ if match and match.group("key").casefold() == key.casefold():
302
+ value = match.group("value").strip()
303
+ if not value:
304
+ raise ReviewPanelError(f"{source} has an empty {key} field")
305
+ return value
306
+ raise ReviewPanelError(f"{source} is missing the {key} field")
307
+
308
+
309
+ def parse_findings(text: str, source: str) -> list[dict]:
310
+ findings = []
311
+ for match in FINDING_BLOCK.finditer(text):
312
+ body = match.group("body")
313
+ severity = _require_field(body, "Severity", f"{source} {match.group('id')}").casefold()
314
+ kind = _require_field(body, "Kind", f"{source} {match.group('id')}").casefold()
315
+ criterion = _require_field(body, "Criterion", f"{source} {match.group('id')}")
316
+ evidence = _require_field(body, "Evidence", f"{source} {match.group('id')}")
317
+ found = _require_field(body, "Found", f"{source} {match.group('id')}")
318
+ fix = _require_field(body, "Fix direction", f"{source} {match.group('id')}")
319
+ if severity not in {"high", "medium", "low"}:
320
+ raise ReviewPanelError(f"{source} {match.group('id')} has an invalid Severity")
321
+ if kind not in {"criterion", "preference"}:
322
+ raise ReviewPanelError(f"{source} {match.group('id')} has an invalid Kind")
323
+ if kind == "criterion" and criterion.casefold() == "none":
324
+ raise ReviewPanelError(
325
+ f"{source} {match.group('id')} criterion findings must name a criterion"
326
+ )
327
+ findings.append(
328
+ {
329
+ "id": match.group("id"),
330
+ "severity": severity,
331
+ "kind": kind,
332
+ "criterion": criterion,
333
+ "evidence": evidence,
334
+ "found": found,
335
+ "fix": fix,
336
+ "source": source,
337
+ }
338
+ )
339
+ if not findings:
340
+ findings_heading = re.search(r"^## Findings\s*$", text, re.MULTILINE)
341
+ if findings_heading:
342
+ rest = text[findings_heading.end() :]
343
+ next_heading = re.search(r"^## ", rest, re.MULTILINE)
344
+ section = rest[: next_heading.start()] if next_heading else rest
345
+ if re.search(r"^-\s*none\s*$", section, re.MULTILINE):
346
+ return []
347
+ raise ReviewPanelError(f"{source} has no findings")
348
+ return findings
349
+
350
+
351
+ def parse_family_artifact(path: Path, expected_kind: str) -> dict:
352
+ if path.is_symlink() or not path.is_file():
353
+ raise ReviewPanelError(f"panel artifact must be a real file: {path}")
354
+ text = path.read_text(encoding="utf-8")
355
+ lines = text.splitlines()
356
+ if expected_kind == "plan":
357
+ if not lines or not lines[0].startswith("# Plan panel — "):
358
+ raise ReviewPanelError(f"{path.name} heading must start with '# Plan panel — '")
359
+ elif expected_kind == "wave":
360
+ if not lines or not lines[0].startswith("# Panel — wave "):
361
+ raise ReviewPanelError(f"{path.name} heading must start with '# Panel — wave '")
362
+ else:
363
+ raise ReviewPanelError(f"unknown panel kind: {expected_kind}")
364
+ family = _require_field(text, "Family", path.name).casefold()
365
+ model = _require_field(text, "Model", path.name)
366
+ if family not in KNOWN_FAMILIES:
367
+ raise ReviewPanelError(f"{path.name} has an unknown Family")
368
+ if "## Findings" not in text:
369
+ raise ReviewPanelError(f"{path.name} is missing ## Findings")
370
+ findings = parse_findings(text, path.name)
371
+ return {
372
+ "path": str(path),
373
+ "family": family,
374
+ "model": model,
375
+ "text": text.rstrip() + "\n",
376
+ "findings": findings,
377
+ }
378
+
379
+
380
+ def actionable(finding: dict) -> bool:
381
+ return finding["kind"] == "criterion" and finding["severity"] == "high"
382
+
383
+
384
+ def detect_conflicts(findings: Sequence[dict]) -> list[dict]:
385
+ by_criterion: dict[str, list[dict]] = {}
386
+ for finding in findings:
387
+ key = finding["criterion"].strip().casefold()
388
+ if key in {"", "none"}:
389
+ continue
390
+ by_criterion.setdefault(key, []).append(finding)
391
+ conflicts = []
392
+ for criterion, group in by_criterion.items():
393
+ severities = {item["severity"] for item in group}
394
+ # A conflict is families disagreeing on whether the criterion needs a
395
+ # fix; wording differences in Found are not conflicts.
396
+ if len({actionable(item) for item in group}) > 1:
397
+ conflicts.append(
398
+ {
399
+ "criterion": group[0]["criterion"],
400
+ "families": [item["source"] for item in group],
401
+ "severities": sorted(severities),
402
+ }
403
+ )
404
+ return conflicts
405
+
406
+
407
+ def render_finding(finding: dict) -> str:
408
+ return (
409
+ f"- {finding['severity']} / {finding['kind']} — {finding['criterion']}\n"
410
+ f" evidence: {finding['evidence']}\n"
411
+ f" found: {finding['found']}\n"
412
+ f" fix: {finding['fix']}\n"
413
+ f" source: {finding['source']}\n"
414
+ )
415
+
416
+
417
+ def merge_artifacts(
418
+ kind: str,
419
+ inputs: Sequence[Path],
420
+ output: Path,
421
+ header_fields: Sequence[tuple[str, str]],
422
+ ) -> dict:
423
+ artifacts = [parse_family_artifact(path, kind) for path in inputs]
424
+ families = [artifact["family"] for artifact in artifacts]
425
+ if len(families) != len(set(families)):
426
+ raise ReviewPanelError("panel merge repeats a family")
427
+ findings = [finding for artifact in artifacts for finding in artifact["findings"]]
428
+ action = [finding for finding in findings if actionable(finding)]
429
+ warnings = [finding for finding in findings if not actionable(finding)]
430
+ conflicts = detect_conflicts(findings)
431
+ title = "# Plan panel" if kind == "plan" else "# Wave panel"
432
+ lines = [title, ""]
433
+ for key, value in header_fields:
434
+ lines.append(f"{key}: {value}")
435
+ lines.extend(
436
+ [
437
+ f"Families: {', '.join(families)}",
438
+ f"Actionable: {len(action)}",
439
+ f"Warnings: {len(warnings)}",
440
+ f"Conflicts: {len(conflicts)}",
441
+ "",
442
+ "## Actionable",
443
+ "",
444
+ ]
445
+ )
446
+ if action:
447
+ lines.extend(render_finding(finding) for finding in action)
448
+ else:
449
+ lines.append("- none")
450
+ lines.append("")
451
+ lines.extend(["## Warnings", ""])
452
+ if warnings:
453
+ lines.extend(render_finding(finding) for finding in warnings)
454
+ else:
455
+ lines.append("- none")
456
+ lines.append("")
457
+ lines.extend(["## Conflicts", ""])
458
+ if conflicts:
459
+ for conflict in conflicts:
460
+ lines.append(
461
+ f"- {conflict['criterion']} — families {', '.join(conflict['families'])}; "
462
+ f"severities {', '.join(conflict['severities'])}"
463
+ )
464
+ lines.append("")
465
+ else:
466
+ lines.append("- none")
467
+ lines.append("")
468
+ lines.extend(["## By family", ""])
469
+ for artifact in artifacts:
470
+ lines.append(artifact["text"].rstrip())
471
+ lines.append("")
472
+ output.parent.mkdir(parents=True, exist_ok=True)
473
+ output.write_text("\n".join(lines).rstrip() + "\n", encoding="utf-8")
474
+ return {
475
+ "output": str(output.resolve()),
476
+ "families": families,
477
+ "actionable": len(action),
478
+ "warnings": len(warnings),
479
+ "conflicts": len(conflicts),
480
+ }
481
+
482
+
483
+ def emit(payload: dict) -> int:
484
+ json.dump(payload, sys.stdout, indent=2, sort_keys=True)
485
+ sys.stdout.write("\n")
486
+ return 0
487
+
488
+
489
+ def parser() -> argparse.ArgumentParser:
490
+ result = argparse.ArgumentParser(description=__doc__)
491
+ result.add_argument(
492
+ "command", choices=("parse", "resolve", "families", "merge", "validate-plan")
493
+ )
494
+ result.add_argument("--plan", type=Path)
495
+ result.add_argument("--intent", type=Path)
496
+ result.add_argument("--charter", type=Path)
497
+ result.add_argument("--advertised", default="")
498
+ result.add_argument("--parent-family")
499
+ result.add_argument("--parent-slug")
500
+ result.add_argument("--exclude-family", action="append", choices=KNOWN_FAMILIES, default=[])
501
+ result.add_argument("--kind", choices=("plan", "wave"))
502
+ result.add_argument("--inputs", default="")
503
+ result.add_argument("--output", type=Path)
504
+ result.add_argument("--wave")
505
+ result.add_argument("--cycle")
506
+ result.add_argument("--mode")
507
+ return result
508
+
509
+
510
+ def main(argv: Optional[Sequence[str]] = None) -> int:
511
+ arguments = parser().parse_args(argv)
512
+ try:
513
+ if arguments.command == "families":
514
+ grouped = group_advertised(split_csv(arguments.advertised))
515
+ return emit(
516
+ {
517
+ "families": {
518
+ family: {"slugs": slugs, "latest": latest_slug(slugs)}
519
+ for family, slugs in grouped.items()
520
+ }
521
+ }
522
+ )
523
+ if arguments.command == "merge":
524
+ if arguments.kind is None or arguments.output is None:
525
+ raise ReviewPanelError("merge requires --kind and --output")
526
+ inputs = [Path(part) for part in split_csv(arguments.inputs)]
527
+ if not inputs:
528
+ raise ReviewPanelError("merge requires --inputs")
529
+ header = []
530
+ if arguments.mode:
531
+ header.append(("Mode", arguments.mode))
532
+ if arguments.kind == "wave":
533
+ if not arguments.wave or not arguments.cycle:
534
+ raise ReviewPanelError("wave merge requires --wave and --cycle")
535
+ header.extend(
536
+ (("Wave", arguments.wave), ("Cycle", arguments.cycle))
537
+ )
538
+ return emit(merge_artifacts(arguments.kind, inputs, arguments.output, header))
539
+ if arguments.plan is None and arguments.command in {"parse", "resolve", "validate-plan"}:
540
+ if arguments.command != "parse" or (
541
+ arguments.intent is None and arguments.charter is None
542
+ ):
543
+ raise ReviewPanelError(
544
+ f"{arguments.command} requires --plan, --intent, or --charter"
545
+ )
546
+ config = parse_files(arguments.plan, arguments.intent, arguments.charter)
547
+ if arguments.command in {"parse", "validate-plan"}:
548
+ return emit(config)
549
+ resolved = resolve_panel(
550
+ config,
551
+ split_csv(arguments.advertised),
552
+ arguments.parent_family,
553
+ arguments.parent_slug,
554
+ arguments.exclude_family,
555
+ )
556
+ return emit(resolved)
557
+ except ReviewPanelError as error:
558
+ json.dump({"status": "error", "error": str(error)}, sys.stdout, indent=2)
559
+ sys.stdout.write("\n")
560
+ return 2
561
+
562
+
563
+ if __name__ == "__main__":
564
+ sys.exit(main())
@@ -3,6 +3,8 @@ name: gsd-path-plan
3
3
  description: Create and validate dependency-ordered GSD Path build waves and complete task contracts. Use only when the user explicitly invokes $gsd-path-plan or an active $gsd-path router explicitly routes to this phase.
4
4
  ---
5
5
 
6
+ Before executing project helpers, read [runtime selection](references/runtime-selection.md).
7
+
6
8
  # GSD Path Planning Phase
7
9
 
8
10
  Dispatch one planner, gate its artifacts, and obtain the single approval that
@@ -29,8 +31,11 @@ Before dispatch, enter from `decide/done` or `define/done` with
29
31
  `--set-phase plan --set-status active`, and event `planning started`. Resume
30
32
  `plan/blocked` through the same helper with event `planning resumed`. Require
31
33
  the returned track state to be `plan/active`; never edit STATE directly.
32
- Any later phase blocks instead of replacing an approved plan. Patch mode is
33
- the only reopen exception: require an approved plan, no in-progress task, and
34
+ For a blocked build, use the [build recovery contract](references/build-recovery.md).
35
+ The runtime's `build-repair` mode owns structural changes to the remaining task
36
+ inventory and the normal approval checkpoint; it is distinct from append-only
37
+ patch mode. Otherwise later phases block instead of replacing an approved plan.
38
+ Patch mode requires an approved plan, no in-progress task, and
34
39
  state `plan/done` or `ship/blocked`; any other state blocks. `build/done` is
35
40
  reserved exclusively for the build orchestrator's transition recovery and
36
41
  must return there instead of reopening planning. When an active router
@@ -74,10 +79,17 @@ router supplied lookahead mode. Every per-milestone path below, including
74
79
  review-panel evidence, is rooted there. Program inputs remain at `.project/`
75
80
  as stated in Lookahead mode.
76
81
 
77
- 1. Read the local [plan template](templates/plan.md), [task template](templates/task.md),
78
- [plan-panel template](templates/plan-panel.md), `scripts/check_handoffs.py`,
79
- and `scripts/review_panel.py`; resolve them to absolute paths.
80
- 2. Read the local [planner role](references/planner.md), then follow the
82
+ 1. Resolve the local [plan template](templates/plan.md), [task template](templates/task.md),
83
+ `scripts/check_handoffs.py`, and `scripts/review_panel.py` to absolute paths.
84
+ Quick mode reads the plan and task templates because the parent writes
85
+ those artifacts. Other modes pass their paths to the planner. Execute the
86
+ helpers through the commands below and consume their results; their Python
87
+ source is not planning input. Read helper source only when diagnosing a
88
+ concrete failure that the returned error and documented recovery cannot
89
+ explain.
90
+ 2. In quick mode, skip planner dispatch and follow Quick mode below; do not
91
+ load the planner role or dispatch contract. In other modes, resolve the
92
+ local [planner role](references/planner.md) for the child, then follow the
81
93
  local [runtime dispatch contract](references/dispatch.md) with deterministic
82
94
  logical task name `plan`. Give it absolute role, `AGENTS.md`, `WORKFLOW.md`,
83
95
  input, template, and output paths, including `.project/LESSONS.md` when it
@@ -133,7 +145,7 @@ as stated in Lookahead mode.
133
145
  - Require a `Review depth` value (`full`, `deep`, or `verify-only`) on
134
146
  every wave. Wave 1 and any wave touching authentication, authorization,
135
147
  payments, data migration, or concurrency requires `full` or `deep`;
136
- quick-lane single waves may use `verify-only`. The planner assigns `deep`
148
+ this includes quick-lane single waves. The planner assigns `deep`
137
149
  sparingly to waves where a wrong merge is irreversible or
138
150
  security-critical.
139
151
  - When INTENT.md `Surfaces:` is not `none`, require a PLAN.md
@@ -199,11 +211,13 @@ as stated in Lookahead mode.
199
211
  - `status: error` or exit 2 — use the same guarded transition to set
200
212
  `plan/blocked` with an event naming the helper error, link PLAN.md, and stop.
201
213
  A named family that is not advertised is an assertion failure.
202
- - `status: ready` — for each selected family, spawn one independent child
214
+ - `status: ready` — resolve the local
215
+ [plan-panel template](templates/plan-panel.md) for the reviewers; other
216
+ statuses do not load this template. For each selected family, spawn one independent child
203
217
  with logical task name `review_plan_panel_<family>`, the reviewer role
204
218
  in plan-panel mode, the plan-panel template, and the exact helper-returned
205
- model slug when the host advertises model selection. Never override the
206
- model on the planner. Each child stages its family file under a
219
+ model slug through the dispatch model-policy contract. Planner choices
220
+ use that same contract. Each child stages its family file under a
207
221
  disposable root; the parent validates and copies those files, removes
208
222
  any stale `<track>/review/PLAN-PANEL.skipped.json`, then runs
209
223
  `python3 <absolute review_panel.py> merge --kind plan --inputs <family
@@ -257,12 +271,8 @@ as stated in Lookahead mode.
257
271
  (event `patch plan approved`) without a plan checkpoint because build's
258
272
  patch re-entry commits the artifacts with its `build/active` transition.
259
273
  `pipeline_state.py transition` never approves a plan. Confirm approval,
260
- link PLAN.md again, and state that
261
- build starts next. Do not add another approval gate. When routed by an
262
- active `$gsd-path`, return control to that router so its bundled build
263
- contract starts. When invoked directly, stop and tell the user to explicitly
264
- invoke `$gsd-path`, which routes to build; do not invoke an explicit-only
265
- sibling skill yourself.
274
+ link PLAN.md again, and use the executable phase handoff in AGENTS.md.
275
+ The plan approval also authorizes build; do not add another approval gate.
266
276
 
267
277
  ## Ordering rules
268
278
 
@@ -273,8 +283,8 @@ as stated in Lookahead mode.
273
283
  ## Quick mode
274
284
 
275
285
  Legal entry: `define/done` where INTENT.md records `Lane: quick` (transition
276
- to `plan/active`, logging that research and decide were skipped for the
277
- quick lane). Quick mode dispatches no planner agent — the orchestrator writes
286
+ to `plan/active` using exactly the Preconditions event `planning started`;
287
+ record the skipped research and decide phases in the synthesis). Quick mode dispatches no planner agent — the orchestrator writes
278
288
  the artifacts directly:
279
289
 
280
290
  1. Write `.project/research/SYNTHESIS.md` containing only `## Settled` lines
@@ -282,7 +292,7 @@ the artifacts directly:
282
292
  plus a minimal `## For the planner` naming the walking skeleton. No
283
293
  invented decisions or runner-ups.
284
294
  2. Write `.project/plan/PLAN.md` with exactly one wave — `Review depth:
285
- verify-only` permitted — and at most two deliverable-sized task files
295
+ full` or `deep` — and at most two deliverable-sized task files
286
296
  (project policy),
287
297
  honoring every task-contract rule above and `.project/LESSONS.md` when it
288
298
  exists. Write `review_panel: off` and `finding_skeptics: off` regardless