@opengsd/gsd-path 1.4.0 → 1.6.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 (476) hide show
  1. package/CHANGELOG.md +460 -341
  2. package/DOCS.md +16 -5
  3. package/FULL.md +67 -6
  4. package/HOOKS.md +158 -10
  5. package/MIGRATE.md +12 -0
  6. package/QUICK.md +4 -2
  7. package/README.md +136 -116
  8. package/RUNTIME.md +24 -6
  9. package/UPDATE.md +39 -3
  10. package/WORKFLOW.md +80 -4
  11. package/package.json +5 -2
  12. package/platforms/shared-agents/dispatch.md +13 -0
  13. package/scripts/_common.py +256 -2
  14. package/scripts/archive_milestone.py +14 -13
  15. package/scripts/build_recovery.py +155 -58
  16. package/scripts/build_state.py +274 -8
  17. package/scripts/check_docs_audit.py +77 -4
  18. package/scripts/check_handoffs.py +635 -57
  19. package/scripts/check_task_briefs.py +293 -32
  20. package/scripts/detect_project.py +260 -20
  21. package/scripts/discussion_records.py +9 -1
  22. package/scripts/discussion_validate.py +93 -35
  23. package/scripts/dispatch_driver.py +549 -53
  24. package/scripts/git_guard.py +27 -8
  25. package/scripts/guard_hook.py +2630 -210
  26. package/scripts/install.mjs +99 -11
  27. package/scripts/install.py +231 -46
  28. package/scripts/integration.py +244 -65
  29. package/scripts/isolation.py +539 -70
  30. package/scripts/loop_run.py +16 -4
  31. package/scripts/members.py +77 -32
  32. package/scripts/migrate_core.py +2 -0
  33. package/scripts/model_policy.py +5 -1
  34. package/scripts/path_config.py +3 -3
  35. package/scripts/pipeline_diagnose.py +17 -2
  36. package/scripts/pipeline_git.py +131 -42
  37. package/scripts/pipeline_state.py +139 -39
  38. package/scripts/pipeline_undo.py +118 -16
  39. package/scripts/promote_lookahead.py +3 -3
  40. package/scripts/review_findings.py +180 -44
  41. package/scripts/roadmap.py +9 -1
  42. package/scripts/runtime_store.py +21 -2
  43. package/scripts/skill-resources.json +9 -2
  44. package/scripts/state_checkpoint.py +39 -36
  45. package/scripts/state_promote.py +15 -4
  46. package/scripts/status_runtime.py +22 -4
  47. package/scripts/task_context.py +14 -3
  48. package/scripts/wizard.mjs +1 -0
  49. package/scripts/workflow_run.py +14 -4
  50. package/scripts/worktree_paths.py +54 -2
  51. package/skills/gsd-path/DEFINE.md +34 -5
  52. package/skills/gsd-path/PLAN.md +14 -3
  53. package/skills/gsd-path/RESEARCH.md +11 -0
  54. package/skills/gsd-path/ROADMAP.md +16 -6
  55. package/skills/gsd-path/SHIP.md +32 -5
  56. package/skills/gsd-path/SKILL.md +52 -24
  57. package/skills/gsd-path/references/build-native.md +112 -10
  58. package/skills/gsd-path/references/build-recovery.md +13 -0
  59. package/skills/gsd-path/references/coder.md +7 -3
  60. package/skills/gsd-path/references/dispatch.md +13 -0
  61. package/skills/gsd-path/references/docs-auditor.md +5 -0
  62. package/skills/gsd-path/references/operating-rules.md +11 -3
  63. package/skills/gsd-path/references/planner.md +26 -4
  64. package/skills/gsd-path/references/reviewer-final.md +20 -2
  65. package/skills/gsd-path/references/reviewer-wave.md +56 -12
  66. package/skills/gsd-path/references/reviewer.md +3 -1
  67. package/skills/gsd-path/references/runtime-selection.md +9 -0
  68. package/skills/gsd-path/references/spec-probes.md +150 -0
  69. package/skills/gsd-path/scripts/_common.py +256 -2
  70. package/skills/gsd-path/scripts/archive_milestone.py +14 -13
  71. package/skills/gsd-path/scripts/build_recovery.py +155 -58
  72. package/skills/gsd-path/scripts/build_state.py +274 -8
  73. package/skills/gsd-path/scripts/check_docs_audit.py +77 -4
  74. package/skills/gsd-path/scripts/check_handoffs.py +635 -57
  75. package/skills/gsd-path/scripts/check_task_briefs.py +293 -32
  76. package/skills/gsd-path/scripts/detect_project.py +260 -20
  77. package/skills/gsd-path/scripts/discussion_records.py +9 -1
  78. package/skills/gsd-path/scripts/discussion_validate.py +93 -35
  79. package/skills/gsd-path/scripts/dispatch_driver.py +549 -53
  80. package/skills/gsd-path/scripts/git_guard.py +27 -8
  81. package/skills/gsd-path/scripts/integration.py +244 -65
  82. package/skills/gsd-path/scripts/isolation.py +539 -70
  83. package/skills/gsd-path/scripts/loop_run.py +16 -4
  84. package/skills/gsd-path/scripts/members.py +77 -32
  85. package/skills/gsd-path/scripts/model_policy.py +5 -1
  86. package/skills/gsd-path/scripts/path_config.py +3 -3
  87. package/skills/gsd-path/scripts/pipeline_git.py +131 -42
  88. package/skills/gsd-path/scripts/pipeline_state.py +139 -39
  89. package/skills/gsd-path/scripts/promote_lookahead.py +3 -3
  90. package/skills/gsd-path/scripts/review_findings.py +180 -44
  91. package/skills/gsd-path/scripts/roadmap.py +9 -1
  92. package/skills/gsd-path/scripts/state_checkpoint.py +39 -36
  93. package/skills/gsd-path/scripts/state_promote.py +15 -4
  94. package/skills/gsd-path/scripts/status_runtime.py +22 -4
  95. package/skills/gsd-path/scripts/task_context.py +14 -3
  96. package/skills/gsd-path/scripts/workflow_run.py +14 -4
  97. package/skills/gsd-path/scripts/worktree_paths.py +54 -2
  98. package/skills/gsd-path/templates/docs-audit.md +5 -1
  99. package/skills/gsd-path/templates/final-review.md +11 -2
  100. package/skills/gsd-path/templates/intent.md +30 -0
  101. package/skills/gsd-path/templates/patch-findings.md +3 -3
  102. package/skills/gsd-path/templates/plan.md +17 -2
  103. package/skills/gsd-path/templates/research-handoff.md +14 -3
  104. package/skills/gsd-path/templates/state.md +2 -1
  105. package/skills/gsd-path/templates/task.md +1 -0
  106. package/skills/gsd-path/templates/wave-review.md +28 -9
  107. package/skills/gsd-path-build/references/build-native.md +112 -10
  108. package/skills/gsd-path-build/references/build-recovery.md +13 -0
  109. package/skills/gsd-path-build/references/coder.md +7 -3
  110. package/skills/gsd-path-build/references/dispatch.md +13 -0
  111. package/skills/gsd-path-build/references/operating-rules.md +11 -3
  112. package/skills/gsd-path-build/references/reviewer-final.md +20 -2
  113. package/skills/gsd-path-build/references/reviewer-wave.md +56 -12
  114. package/skills/gsd-path-build/references/reviewer.md +3 -1
  115. package/skills/gsd-path-build/references/runtime-selection.md +9 -0
  116. package/skills/gsd-path-build/references/spec-probes.md +150 -0
  117. package/skills/gsd-path-build/scripts/_common.py +256 -2
  118. package/skills/gsd-path-build/scripts/archive_milestone.py +14 -13
  119. package/skills/gsd-path-build/scripts/build_recovery.py +155 -58
  120. package/skills/gsd-path-build/scripts/build_state.py +274 -8
  121. package/skills/gsd-path-build/scripts/check_handoffs.py +635 -57
  122. package/skills/gsd-path-build/scripts/check_task_briefs.py +293 -32
  123. package/skills/gsd-path-build/scripts/detect_project.py +260 -20
  124. package/skills/gsd-path-build/scripts/discussion_records.py +9 -1
  125. package/skills/gsd-path-build/scripts/discussion_validate.py +93 -35
  126. package/skills/gsd-path-build/scripts/dispatch_driver.py +549 -53
  127. package/skills/gsd-path-build/scripts/integration.py +244 -65
  128. package/skills/gsd-path-build/scripts/isolation.py +539 -70
  129. package/skills/gsd-path-build/scripts/loop_run.py +16 -4
  130. package/skills/gsd-path-build/scripts/members.py +77 -32
  131. package/skills/gsd-path-build/scripts/model_policy.py +5 -1
  132. package/skills/gsd-path-build/scripts/path_config.py +3 -3
  133. package/skills/gsd-path-build/scripts/pipeline_git.py +131 -42
  134. package/skills/gsd-path-build/scripts/pipeline_state.py +139 -39
  135. package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -3
  136. package/skills/gsd-path-build/scripts/review_findings.py +180 -44
  137. package/skills/gsd-path-build/scripts/roadmap.py +9 -1
  138. package/skills/gsd-path-build/scripts/state_checkpoint.py +39 -36
  139. package/skills/gsd-path-build/scripts/state_promote.py +15 -4
  140. package/skills/gsd-path-build/scripts/task_context.py +14 -3
  141. package/skills/gsd-path-build/scripts/workflow_run.py +14 -4
  142. package/skills/gsd-path-build/scripts/worktree_paths.py +54 -2
  143. package/skills/gsd-path-build/templates/task.md +1 -0
  144. package/skills/gsd-path-build/templates/wave-review.md +28 -9
  145. package/skills/gsd-path-decide/references/dispatch.md +13 -0
  146. package/skills/gsd-path-decide/references/operating-rules.md +11 -3
  147. package/skills/gsd-path-decide/references/runtime-selection.md +9 -0
  148. package/skills/gsd-path-decide/scripts/_common.py +256 -2
  149. package/skills/gsd-path-decide/scripts/archive_milestone.py +14 -13
  150. package/skills/gsd-path-decide/scripts/build_recovery.py +155 -58
  151. package/skills/gsd-path-decide/scripts/check_handoffs.py +635 -57
  152. package/skills/gsd-path-decide/scripts/check_task_briefs.py +293 -32
  153. package/skills/gsd-path-decide/scripts/detect_project.py +260 -20
  154. package/skills/gsd-path-decide/scripts/discussion_records.py +9 -1
  155. package/skills/gsd-path-decide/scripts/discussion_validate.py +93 -35
  156. package/skills/gsd-path-decide/scripts/integration.py +244 -65
  157. package/skills/gsd-path-decide/scripts/isolation.py +539 -70
  158. package/skills/gsd-path-decide/scripts/loop_run.py +16 -4
  159. package/skills/gsd-path-decide/scripts/members.py +77 -32
  160. package/skills/gsd-path-decide/scripts/model_policy.py +5 -1
  161. package/skills/gsd-path-decide/scripts/path_config.py +3 -3
  162. package/skills/gsd-path-decide/scripts/pipeline_git.py +131 -42
  163. package/skills/gsd-path-decide/scripts/pipeline_state.py +139 -39
  164. package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -3
  165. package/skills/gsd-path-decide/scripts/roadmap.py +9 -1
  166. package/skills/gsd-path-decide/scripts/state_checkpoint.py +39 -36
  167. package/skills/gsd-path-decide/scripts/state_promote.py +15 -4
  168. package/skills/gsd-path-decide/scripts/worktree_paths.py +54 -2
  169. package/skills/gsd-path-define/SKILL.md +34 -5
  170. package/skills/gsd-path-define/references/build-recovery.md +13 -0
  171. package/skills/gsd-path-define/references/operating-rules.md +11 -3
  172. package/skills/gsd-path-define/references/runtime-selection.md +9 -0
  173. package/skills/gsd-path-define/references/spec-probes.md +150 -0
  174. package/skills/gsd-path-define/scripts/_common.py +256 -2
  175. package/skills/gsd-path-define/scripts/archive_milestone.py +14 -13
  176. package/skills/gsd-path-define/scripts/build_recovery.py +155 -58
  177. package/skills/gsd-path-define/scripts/check_handoffs.py +635 -57
  178. package/skills/gsd-path-define/scripts/check_task_briefs.py +293 -32
  179. package/skills/gsd-path-define/scripts/detect_project.py +260 -20
  180. package/skills/gsd-path-define/scripts/discussion_records.py +9 -1
  181. package/skills/gsd-path-define/scripts/discussion_validate.py +93 -35
  182. package/skills/gsd-path-define/scripts/integration.py +244 -65
  183. package/skills/gsd-path-define/scripts/isolation.py +539 -70
  184. package/skills/gsd-path-define/scripts/loop_run.py +16 -4
  185. package/skills/gsd-path-define/scripts/members.py +77 -32
  186. package/skills/gsd-path-define/scripts/model_policy.py +5 -1
  187. package/skills/gsd-path-define/scripts/path_config.py +3 -3
  188. package/skills/gsd-path-define/scripts/pipeline_git.py +131 -42
  189. package/skills/gsd-path-define/scripts/pipeline_state.py +139 -39
  190. package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -3
  191. package/skills/gsd-path-define/scripts/roadmap.py +9 -1
  192. package/skills/gsd-path-define/scripts/state_checkpoint.py +39 -36
  193. package/skills/gsd-path-define/scripts/state_promote.py +15 -4
  194. package/skills/gsd-path-define/scripts/worktree_paths.py +54 -2
  195. package/skills/gsd-path-define/templates/intent.md +30 -0
  196. package/skills/gsd-path-define/templates/state.md +2 -1
  197. package/skills/gsd-path-discuss/references/operating-rules.md +11 -3
  198. package/skills/gsd-path-discuss/references/runtime-selection.md +9 -0
  199. package/skills/gsd-path-discuss/scripts/_common.py +256 -2
  200. package/skills/gsd-path-discuss/scripts/discussion_records.py +9 -1
  201. package/skills/gsd-path-discuss/scripts/loop_run.py +16 -4
  202. package/skills/gsd-path-discuss/scripts/model_policy.py +5 -1
  203. package/skills/gsd-path-discuss/scripts/path_config.py +3 -3
  204. package/skills/gsd-path-discuss/scripts/worktree_paths.py +54 -2
  205. package/skills/gsd-path-docs-audit/references/dispatch.md +13 -0
  206. package/skills/gsd-path-docs-audit/references/docs-auditor.md +5 -0
  207. package/skills/gsd-path-docs-audit/references/operating-rules.md +11 -3
  208. package/skills/gsd-path-docs-audit/references/runtime-selection.md +9 -0
  209. package/skills/gsd-path-docs-audit/scripts/_common.py +256 -2
  210. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +14 -13
  211. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +155 -58
  212. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +77 -4
  213. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +635 -57
  214. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +293 -32
  215. package/skills/gsd-path-docs-audit/scripts/detect_project.py +260 -20
  216. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +9 -1
  217. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +93 -35
  218. package/skills/gsd-path-docs-audit/scripts/integration.py +244 -65
  219. package/skills/gsd-path-docs-audit/scripts/isolation.py +539 -70
  220. package/skills/gsd-path-docs-audit/scripts/loop_run.py +16 -4
  221. package/skills/gsd-path-docs-audit/scripts/members.py +77 -32
  222. package/skills/gsd-path-docs-audit/scripts/model_policy.py +5 -1
  223. package/skills/gsd-path-docs-audit/scripts/path_config.py +3 -3
  224. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +131 -42
  225. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +139 -39
  226. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -3
  227. package/skills/gsd-path-docs-audit/scripts/roadmap.py +9 -1
  228. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +39 -36
  229. package/skills/gsd-path-docs-audit/scripts/state_promote.py +15 -4
  230. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +54 -2
  231. package/skills/gsd-path-docs-audit/templates/docs-audit.md +5 -1
  232. package/skills/gsd-path-forensics/references/operating-rules.md +11 -3
  233. package/skills/gsd-path-forensics/references/runtime-selection.md +9 -0
  234. package/skills/gsd-path-forensics/scripts/_common.py +256 -2
  235. package/skills/gsd-path-forensics/scripts/discussion_records.py +9 -1
  236. package/skills/gsd-path-forensics/scripts/loop_run.py +16 -4
  237. package/skills/gsd-path-forensics/scripts/model_policy.py +5 -1
  238. package/skills/gsd-path-forensics/scripts/path_config.py +3 -3
  239. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +17 -2
  240. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +118 -16
  241. package/skills/gsd-path-forensics/scripts/worktree_paths.py +54 -2
  242. package/skills/gsd-path-inspect/references/dispatch.md +13 -0
  243. package/skills/gsd-path-inspect/references/docs-auditor.md +5 -0
  244. package/skills/gsd-path-inspect/references/operating-rules.md +11 -3
  245. package/skills/gsd-path-inspect/references/runtime-selection.md +9 -0
  246. package/skills/gsd-path-inspect/scripts/_common.py +256 -2
  247. package/skills/gsd-path-inspect/scripts/archive_milestone.py +14 -13
  248. package/skills/gsd-path-inspect/scripts/build_recovery.py +155 -58
  249. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +77 -4
  250. package/skills/gsd-path-inspect/scripts/check_handoffs.py +635 -57
  251. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +293 -32
  252. package/skills/gsd-path-inspect/scripts/detect_project.py +260 -20
  253. package/skills/gsd-path-inspect/scripts/discussion_records.py +9 -1
  254. package/skills/gsd-path-inspect/scripts/discussion_validate.py +93 -35
  255. package/skills/gsd-path-inspect/scripts/integration.py +244 -65
  256. package/skills/gsd-path-inspect/scripts/isolation.py +539 -70
  257. package/skills/gsd-path-inspect/scripts/loop_run.py +16 -4
  258. package/skills/gsd-path-inspect/scripts/members.py +77 -32
  259. package/skills/gsd-path-inspect/scripts/model_policy.py +5 -1
  260. package/skills/gsd-path-inspect/scripts/path_config.py +3 -3
  261. package/skills/gsd-path-inspect/scripts/pipeline_git.py +131 -42
  262. package/skills/gsd-path-inspect/scripts/pipeline_state.py +139 -39
  263. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -3
  264. package/skills/gsd-path-inspect/scripts/roadmap.py +9 -1
  265. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +39 -36
  266. package/skills/gsd-path-inspect/scripts/state_promote.py +15 -4
  267. package/skills/gsd-path-inspect/scripts/workflow_run.py +14 -4
  268. package/skills/gsd-path-inspect/scripts/worktree_paths.py +54 -2
  269. package/skills/gsd-path-inspect/templates/docs-audit.md +5 -1
  270. package/skills/gsd-path-inspect/templates/state.md +2 -1
  271. package/skills/gsd-path-loop/references/operating-rules.md +11 -3
  272. package/skills/gsd-path-loop/references/runtime-selection.md +9 -0
  273. package/skills/gsd-path-loop/scripts/_common.py +256 -2
  274. package/skills/gsd-path-loop/scripts/discussion_records.py +9 -1
  275. package/skills/gsd-path-loop/scripts/loop_run.py +16 -4
  276. package/skills/gsd-path-loop/scripts/model_policy.py +5 -1
  277. package/skills/gsd-path-loop/scripts/path_config.py +3 -3
  278. package/skills/gsd-path-loop/scripts/worktree_paths.py +54 -2
  279. package/skills/gsd-path-migrate/SKILL.md +8 -0
  280. package/skills/gsd-path-migrate/references/operating-rules.md +11 -3
  281. package/skills/gsd-path-migrate/references/runtime-selection.md +9 -0
  282. package/skills/gsd-path-migrate/scripts/_common.py +256 -2
  283. package/skills/gsd-path-migrate/scripts/discussion_records.py +9 -1
  284. package/skills/gsd-path-migrate/scripts/loop_run.py +16 -4
  285. package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -0
  286. package/skills/gsd-path-migrate/scripts/model_policy.py +5 -1
  287. package/skills/gsd-path-migrate/scripts/path_config.py +3 -3
  288. package/skills/gsd-path-plan/SKILL.md +14 -3
  289. package/skills/gsd-path-plan/references/build-recovery.md +13 -0
  290. package/skills/gsd-path-plan/references/dispatch.md +13 -0
  291. package/skills/gsd-path-plan/references/operating-rules.md +11 -3
  292. package/skills/gsd-path-plan/references/planner.md +26 -4
  293. package/skills/gsd-path-plan/references/runtime-selection.md +9 -0
  294. package/skills/gsd-path-plan/references/spec-probes.md +150 -0
  295. package/skills/gsd-path-plan/scripts/_common.py +256 -2
  296. package/skills/gsd-path-plan/scripts/archive_milestone.py +14 -13
  297. package/skills/gsd-path-plan/scripts/build_recovery.py +155 -58
  298. package/skills/gsd-path-plan/scripts/check_handoffs.py +635 -57
  299. package/skills/gsd-path-plan/scripts/check_task_briefs.py +293 -32
  300. package/skills/gsd-path-plan/scripts/detect_project.py +260 -20
  301. package/skills/gsd-path-plan/scripts/discussion_records.py +9 -1
  302. package/skills/gsd-path-plan/scripts/discussion_validate.py +93 -35
  303. package/skills/gsd-path-plan/scripts/integration.py +244 -65
  304. package/skills/gsd-path-plan/scripts/isolation.py +539 -70
  305. package/skills/gsd-path-plan/scripts/loop_run.py +16 -4
  306. package/skills/gsd-path-plan/scripts/members.py +77 -32
  307. package/skills/gsd-path-plan/scripts/model_policy.py +5 -1
  308. package/skills/gsd-path-plan/scripts/path_config.py +3 -3
  309. package/skills/gsd-path-plan/scripts/pipeline_git.py +131 -42
  310. package/skills/gsd-path-plan/scripts/pipeline_state.py +139 -39
  311. package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -3
  312. package/skills/gsd-path-plan/scripts/roadmap.py +9 -1
  313. package/skills/gsd-path-plan/scripts/state_checkpoint.py +39 -36
  314. package/skills/gsd-path-plan/scripts/state_promote.py +15 -4
  315. package/skills/gsd-path-plan/scripts/workflow_run.py +14 -4
  316. package/skills/gsd-path-plan/scripts/worktree_paths.py +54 -2
  317. package/skills/gsd-path-plan/templates/patch-findings.md +3 -3
  318. package/skills/gsd-path-plan/templates/plan.md +17 -2
  319. package/skills/gsd-path-plan/templates/task.md +1 -0
  320. package/skills/gsd-path-research/SKILL.md +11 -0
  321. package/skills/gsd-path-research/references/dispatch.md +13 -0
  322. package/skills/gsd-path-research/references/operating-rules.md +11 -3
  323. package/skills/gsd-path-research/references/runtime-selection.md +9 -0
  324. package/skills/gsd-path-research/scripts/_common.py +256 -2
  325. package/skills/gsd-path-research/scripts/archive_milestone.py +14 -13
  326. package/skills/gsd-path-research/scripts/build_recovery.py +155 -58
  327. package/skills/gsd-path-research/scripts/check_handoffs.py +635 -57
  328. package/skills/gsd-path-research/scripts/check_task_briefs.py +293 -32
  329. package/skills/gsd-path-research/scripts/detect_project.py +260 -20
  330. package/skills/gsd-path-research/scripts/discussion_records.py +9 -1
  331. package/skills/gsd-path-research/scripts/discussion_validate.py +93 -35
  332. package/skills/gsd-path-research/scripts/integration.py +244 -65
  333. package/skills/gsd-path-research/scripts/isolation.py +539 -70
  334. package/skills/gsd-path-research/scripts/loop_run.py +16 -4
  335. package/skills/gsd-path-research/scripts/members.py +77 -32
  336. package/skills/gsd-path-research/scripts/model_policy.py +5 -1
  337. package/skills/gsd-path-research/scripts/path_config.py +3 -3
  338. package/skills/gsd-path-research/scripts/pipeline_git.py +131 -42
  339. package/skills/gsd-path-research/scripts/pipeline_state.py +139 -39
  340. package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -3
  341. package/skills/gsd-path-research/scripts/roadmap.py +9 -1
  342. package/skills/gsd-path-research/scripts/state_checkpoint.py +39 -36
  343. package/skills/gsd-path-research/scripts/state_promote.py +15 -4
  344. package/skills/gsd-path-research/scripts/worktree_paths.py +54 -2
  345. package/skills/gsd-path-research/templates/research-handoff.md +14 -3
  346. package/skills/gsd-path-roadmap/SKILL.md +16 -6
  347. package/skills/gsd-path-roadmap/references/dispatch.md +13 -0
  348. package/skills/gsd-path-roadmap/references/operating-rules.md +11 -3
  349. package/skills/gsd-path-roadmap/references/runtime-selection.md +9 -0
  350. package/skills/gsd-path-roadmap/scripts/_common.py +256 -2
  351. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +14 -13
  352. package/skills/gsd-path-roadmap/scripts/build_recovery.py +155 -58
  353. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +635 -57
  354. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +293 -32
  355. package/skills/gsd-path-roadmap/scripts/detect_project.py +260 -20
  356. package/skills/gsd-path-roadmap/scripts/discussion_records.py +9 -1
  357. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +93 -35
  358. package/skills/gsd-path-roadmap/scripts/integration.py +244 -65
  359. package/skills/gsd-path-roadmap/scripts/isolation.py +539 -70
  360. package/skills/gsd-path-roadmap/scripts/loop_run.py +16 -4
  361. package/skills/gsd-path-roadmap/scripts/members.py +77 -32
  362. package/skills/gsd-path-roadmap/scripts/model_policy.py +5 -1
  363. package/skills/gsd-path-roadmap/scripts/path_config.py +3 -3
  364. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +131 -42
  365. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +139 -39
  366. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -3
  367. package/skills/gsd-path-roadmap/scripts/roadmap.py +9 -1
  368. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +39 -36
  369. package/skills/gsd-path-roadmap/scripts/state_promote.py +15 -4
  370. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +54 -2
  371. package/skills/gsd-path-ship/SKILL.md +32 -5
  372. package/skills/gsd-path-ship/references/dispatch.md +13 -0
  373. package/skills/gsd-path-ship/references/operating-rules.md +11 -3
  374. package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
  375. package/skills/gsd-path-ship/references/reviewer-wave.md +56 -12
  376. package/skills/gsd-path-ship/references/reviewer.md +3 -1
  377. package/skills/gsd-path-ship/references/runtime-selection.md +9 -0
  378. package/skills/gsd-path-ship/references/spec-probes.md +150 -0
  379. package/skills/gsd-path-ship/scripts/_common.py +256 -2
  380. package/skills/gsd-path-ship/scripts/archive_milestone.py +14 -13
  381. package/skills/gsd-path-ship/scripts/build_recovery.py +155 -58
  382. package/skills/gsd-path-ship/scripts/build_state.py +274 -8
  383. package/skills/gsd-path-ship/scripts/check_handoffs.py +635 -57
  384. package/skills/gsd-path-ship/scripts/check_task_briefs.py +293 -32
  385. package/skills/gsd-path-ship/scripts/detect_project.py +260 -20
  386. package/skills/gsd-path-ship/scripts/discussion_records.py +9 -1
  387. package/skills/gsd-path-ship/scripts/discussion_validate.py +93 -35
  388. package/skills/gsd-path-ship/scripts/integration.py +244 -65
  389. package/skills/gsd-path-ship/scripts/isolation.py +539 -70
  390. package/skills/gsd-path-ship/scripts/loop_run.py +16 -4
  391. package/skills/gsd-path-ship/scripts/members.py +77 -32
  392. package/skills/gsd-path-ship/scripts/model_policy.py +5 -1
  393. package/skills/gsd-path-ship/scripts/path_config.py +3 -3
  394. package/skills/gsd-path-ship/scripts/pipeline_git.py +131 -42
  395. package/skills/gsd-path-ship/scripts/pipeline_state.py +139 -39
  396. package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -3
  397. package/skills/gsd-path-ship/scripts/roadmap.py +9 -1
  398. package/skills/gsd-path-ship/scripts/state_checkpoint.py +39 -36
  399. package/skills/gsd-path-ship/scripts/state_promote.py +15 -4
  400. package/skills/gsd-path-ship/scripts/workflow_run.py +14 -4
  401. package/skills/gsd-path-ship/scripts/worktree_paths.py +54 -2
  402. package/skills/gsd-path-ship/templates/final-review.md +11 -2
  403. package/skills/gsd-path-ship/templates/patch-findings.md +3 -3
  404. package/skills/gsd-path-ship/templates/wave-review.md +28 -9
  405. package/skills/gsd-path-undo/references/operating-rules.md +11 -3
  406. package/skills/gsd-path-undo/references/runtime-selection.md +9 -0
  407. package/skills/gsd-path-undo/scripts/_common.py +256 -2
  408. package/skills/gsd-path-undo/scripts/discussion_records.py +9 -1
  409. package/skills/gsd-path-undo/scripts/loop_run.py +16 -4
  410. package/skills/gsd-path-undo/scripts/model_policy.py +5 -1
  411. package/skills/gsd-path-undo/scripts/path_config.py +3 -3
  412. package/skills/gsd-path-undo/scripts/pipeline_undo.py +118 -16
  413. package/skills/gsd-path-undo/scripts/worktree_paths.py +54 -2
  414. package/skills/path/DEFINE.md +34 -5
  415. package/skills/path/PLAN.md +14 -3
  416. package/skills/path/RESEARCH.md +11 -0
  417. package/skills/path/ROADMAP.md +16 -6
  418. package/skills/path/SHIP.md +32 -5
  419. package/skills/path/SKILL.md +52 -24
  420. package/skills/path/references/build-native.md +112 -10
  421. package/skills/path/references/build-recovery.md +13 -0
  422. package/skills/path/references/coder.md +7 -3
  423. package/skills/path/references/dispatch.md +13 -0
  424. package/skills/path/references/docs-auditor.md +5 -0
  425. package/skills/path/references/operating-rules.md +11 -3
  426. package/skills/path/references/planner.md +26 -4
  427. package/skills/path/references/reviewer-final.md +20 -2
  428. package/skills/path/references/reviewer-wave.md +56 -12
  429. package/skills/path/references/reviewer.md +3 -1
  430. package/skills/path/references/runtime-selection.md +9 -0
  431. package/skills/path/references/spec-probes.md +150 -0
  432. package/skills/path/scripts/_common.py +256 -2
  433. package/skills/path/scripts/archive_milestone.py +14 -13
  434. package/skills/path/scripts/build_recovery.py +155 -58
  435. package/skills/path/scripts/build_state.py +274 -8
  436. package/skills/path/scripts/check_docs_audit.py +77 -4
  437. package/skills/path/scripts/check_handoffs.py +635 -57
  438. package/skills/path/scripts/check_task_briefs.py +293 -32
  439. package/skills/path/scripts/detect_project.py +260 -20
  440. package/skills/path/scripts/discussion_records.py +9 -1
  441. package/skills/path/scripts/discussion_validate.py +93 -35
  442. package/skills/path/scripts/dispatch_driver.py +549 -53
  443. package/skills/path/scripts/git_guard.py +27 -8
  444. package/skills/path/scripts/integration.py +244 -65
  445. package/skills/path/scripts/isolation.py +539 -70
  446. package/skills/path/scripts/loop_run.py +16 -4
  447. package/skills/path/scripts/members.py +77 -32
  448. package/skills/path/scripts/model_policy.py +5 -1
  449. package/skills/path/scripts/path_config.py +3 -3
  450. package/skills/path/scripts/pipeline_git.py +131 -42
  451. package/skills/path/scripts/pipeline_state.py +139 -39
  452. package/skills/path/scripts/promote_lookahead.py +3 -3
  453. package/skills/path/scripts/review_findings.py +180 -44
  454. package/skills/path/scripts/roadmap.py +9 -1
  455. package/skills/path/scripts/state_checkpoint.py +39 -36
  456. package/skills/path/scripts/state_promote.py +15 -4
  457. package/skills/path/scripts/status_runtime.py +22 -4
  458. package/skills/path/scripts/task_context.py +14 -3
  459. package/skills/path/scripts/workflow_run.py +14 -4
  460. package/skills/path/scripts/worktree_paths.py +54 -2
  461. package/skills/path/templates/docs-audit.md +5 -1
  462. package/skills/path/templates/final-review.md +11 -2
  463. package/skills/path/templates/intent.md +30 -0
  464. package/skills/path/templates/patch-findings.md +3 -3
  465. package/skills/path/templates/plan.md +17 -2
  466. package/skills/path/templates/research-handoff.md +14 -3
  467. package/skills/path/templates/state.md +2 -1
  468. package/skills/path/templates/task.md +1 -0
  469. package/skills/path/templates/wave-review.md +28 -9
  470. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  471. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  472. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  473. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  474. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  475. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  476. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
@@ -17,10 +17,12 @@ import os
17
17
  import re
18
18
  import shutil
19
19
  import sys
20
+ from datetime import date
20
21
 
21
22
  # Runtime helpers must not modify their immutable installation.
22
23
  sys.dont_write_bytecode = True
23
24
  import tempfile
25
+ import traceback
24
26
  from pathlib import Path, PurePosixPath
25
27
  from typing import Optional, Sequence
26
28
 
@@ -52,7 +54,7 @@ def _load_pipeline_modules():
52
54
  try:
53
55
  from scripts import archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
54
56
  return archive_milestone, isolation, pipeline_git, pipeline_state, state_checkpoint
55
- except ModuleNotFoundError as error:
57
+ except ImportError as error: # a foreign `scripts` package
56
58
  if error.name not in {
57
59
  "scripts",
58
60
  "scripts.archive_milestone",
@@ -62,6 +64,13 @@ def _load_pipeline_modules():
62
64
  "scripts.state_checkpoint",
63
65
  }:
64
66
  raise
67
+ # A plain ImportError raised while a `scripts` module loaded is not a
68
+ # foreign package. A missing module falls back, as before.
69
+ if not isinstance(error, ModuleNotFoundError) and any(
70
+ frame.f_globals.get("__name__", "").startswith("scripts.")
71
+ for frame, _ in traceback.walk_tb(error.__traceback__)
72
+ ):
73
+ raise
65
74
  shared = Path(__file__).resolve().parents[2] / "gsd-path" / "scripts"
66
75
  sys.path.insert(0, str(shared))
67
76
  import archive_milestone
@@ -106,6 +115,16 @@ KINDS = ("checkpoint", "task", "member-task", "uncommitted-archive", "lookahead"
106
115
  PROMOTION_PREFIX = "router: promote lookahead milestone "
107
116
  ABANDON_PREFIX = "build: abandon milestone "
108
117
  INTEGRATE_PREFIX = "integrate: "
118
+ SHIP_LOG_ONLY_LINE = re.compile(
119
+ r"- (?P<date>\d{4}-\d{2}-\d{2}) — ship — (?P<event>[^\r\n]+)\n\Z"
120
+ )
121
+ SHIPMENT_RECORD_LINE = re.compile(
122
+ r"- (?P<date>\d{4}-\d{2}-\d{2}) — shipped — (?P<event>[^\r\n]+)\n\Z"
123
+ )
124
+ SHIPMENT_EVENTS = {
125
+ "archive preflight passed; shipment recorded",
126
+ "archive preflight passed; shipment recorded; program complete",
127
+ }
109
128
 
110
129
 
111
130
  class UndoError(RuntimeError):
@@ -375,6 +394,80 @@ def pending_transaction(repo: Path) -> Optional[dict[str, object]]:
375
394
  raise UndoError(str(error)) from error
376
395
 
377
396
 
397
+ def _valid_state_event_line(line: str, phase: str) -> bool:
398
+ pattern = SHIP_LOG_ONLY_LINE if phase == "ship" else SHIPMENT_RECORD_LINE
399
+ match = pattern.fullmatch(line)
400
+ if match is None:
401
+ return False
402
+ try:
403
+ event_date = date.fromisoformat(match.group("date"))
404
+ except ValueError:
405
+ return False
406
+ event = match.group("event")
407
+ return event_date.isoformat() == match.group("date") and event == " ".join(event.split())
408
+
409
+
410
+ def _owned_ship_log_events(
411
+ prepared_state: str,
412
+ current_state: str,
413
+ phase: object,
414
+ status: object,
415
+ ) -> Optional[str]:
416
+ """Return only ship log lines appended around a helper-owned archive event.
417
+
418
+ The prepared text must remain byte-for-byte unchanged. For shipped/done,
419
+ record-shipment changes only phase and status before appending its one
420
+ generated shipment event, so validate that exact prefix and event shape.
421
+ """
422
+ if (phase, status) == ("ship", "active"):
423
+ if current_state == prepared_state:
424
+ return ""
425
+ if not current_state.startswith(prepared_state):
426
+ return None
427
+ suffix = current_state[len(prepared_state):]
428
+ separator = "" if prepared_state.endswith("\n") else "\n"
429
+ if separator:
430
+ if not suffix.startswith(separator):
431
+ return None
432
+ suffix = suffix[len(separator):]
433
+ lines = suffix.splitlines(keepends=True)
434
+ if not lines or "".join(lines) != suffix:
435
+ return None
436
+ if not all(_valid_state_event_line(line, "ship") for line in lines):
437
+ return None
438
+ return separator + suffix
439
+
440
+ if (phase, status) != ("shipped", "done"):
441
+ return None
442
+ try:
443
+ shipped_prefix = pipeline_state._set_frontmatter(
444
+ prepared_state,
445
+ {"phase": "shipped", "status": "done"},
446
+ )
447
+ except PipelineStateError:
448
+ return None
449
+ if not current_state.startswith(shipped_prefix):
450
+ return None
451
+ suffix = current_state[len(shipped_prefix):]
452
+ separator = "" if shipped_prefix.endswith("\n") else "\n"
453
+ if separator:
454
+ if not suffix.startswith(separator):
455
+ return None
456
+ suffix = suffix[len(separator):]
457
+ lines = suffix.splitlines(keepends=True)
458
+ if not lines or "".join(lines) != suffix:
459
+ return None
460
+ shipment_event = lines[-1]
461
+ if not _valid_state_event_line(shipment_event, "shipped"):
462
+ return None
463
+ if SHIPMENT_RECORD_LINE.fullmatch(shipment_event).group("event") not in SHIPMENT_EVENTS:
464
+ return None
465
+ ship_events = lines[:-1]
466
+ if not all(_valid_state_event_line(line, "ship") for line in ship_events):
467
+ return None
468
+ return separator + "".join(ship_events) if ship_events else ""
469
+
470
+
378
471
  def _archive_metadata_error(
379
472
  repo: Path,
380
473
  state_fields: dict[str, object],
@@ -420,8 +513,13 @@ def _archive_metadata_error(
420
513
  if final_review.exists() or gap_reviews:
421
514
  reviewed_head, _ = archive_milestone.parse_final_review(archive_path)
422
515
  archive_milestone.validate_gap_reviews(archive_path, reviewed_head)
423
- if reviewed_head != _optional_rev(repo, "HEAD"):
424
- return "archive undo final review does not match current HEAD"
516
+ if not archive_milestone.reviewed_head_covers(
517
+ repo, reviewed_head, _optional_rev(repo, "HEAD")
518
+ ):
519
+ return (
520
+ "archive undo final review does not match current HEAD; "
521
+ f"{archive_milestone._common.RUNTIME_PIN_ALLOWANCE}"
522
+ )
425
523
  expected_inventory.add("review/FINAL.md")
426
524
  expected_inventory.update(
427
525
  path.relative_to(archive_path).as_posix() for path in gap_reviews
@@ -476,10 +574,14 @@ def _archive_metadata_error(
476
574
  "archive",
477
575
  configured,
478
576
  )
479
- current_state = (repo / ".project" / "STATE.md").read_text(encoding="utf-8")
577
+ current_state = (repo / ".project" / "STATE.md").read_bytes().decode("utf-8")
480
578
  phase = state_fields.get("phase")
481
579
  status = state_fields.get("status")
580
+ ship_events = _owned_ship_log_events(prepared_state, current_state, phase, status)
482
581
  if (phase, status) == ("ship", "active"):
582
+ if ship_events is None:
583
+ return "archive undo found unowned STATE.md changes"
584
+ prepared_state += ship_events
483
585
  if current_state != prepared_state:
484
586
  return "archive undo found unowned STATE.md changes"
485
587
  if ".project/ROADMAP.md" in dirty:
@@ -487,14 +589,13 @@ def _archive_metadata_error(
487
589
  return None
488
590
  if (phase, status) != ("shipped", "done"):
489
591
  return "archive undo requires ship/active or shipped/done"
592
+ if ship_events is None:
593
+ return "archive undo found unowned shipment STATE.md changes"
594
+ prepared_state += ship_events
490
595
 
491
596
  prepared = _state_from_text(prepared_state, "prepared archive STATE.md")
492
- events = (
493
- "archive preflight passed; shipment recorded",
494
- "archive preflight passed; shipment recorded; program complete",
495
- )
496
597
  rendered_states = []
497
- for event in events:
598
+ for event in SHIPMENT_EVENTS:
498
599
  try:
499
600
  _, _, rendered = _render_transition(
500
601
  prepared,
@@ -679,14 +780,15 @@ def _member_undo_parent(repo: Path, member: str, landing: str) -> str:
679
780
  bound = entry["branch"]
680
781
  if _run_git(checkout, "rev-parse", f"refs/heads/{bound}").stdout.strip() != landing:
681
782
  raise UndoError(f"member {member} {bound} moved past this landing")
682
- _require_member_unpublished(checkout, member, landing, bound)
783
+ _require_member_unpublished(repo, checkout, member, landing, bound)
683
784
  return _run_git(checkout, "rev-parse", f"{landing}^").stdout.strip()
684
785
 
685
786
 
686
- def _require_member_unpublished(checkout: Path, member: str, landing: str, bound: str) -> None:
687
- for ref, where in (("refs/remotes/origin/main", "origin/main"), (f"refs/remotes/origin/{bound}", f"origin/{bound}")):
787
+ def _require_member_unpublished(repo: Path, checkout: Path, member: str, landing: str, bound: str) -> None:
788
+ for branch in (isolation.member_default_branch(repo, member), bound):
789
+ ref = f"refs/remotes/origin/{branch}"
688
790
  if _run_git(checkout, "merge-base", "--is-ancestor", landing, ref, check=False).returncode == 0:
689
- raise UndoError(f"member {member} landing is already on {where}")
791
+ raise UndoError(f"member {member} landing is already on origin/{branch}")
690
792
 
691
793
 
692
794
  def _require_no_member_landing_journal(repo: Path) -> None:
@@ -701,7 +803,7 @@ def _undo_member_landing(repo: Path, transaction: dict[str, object]) -> None:
701
803
  checkout, _, entry = isolation._member_context(repo, member)
702
804
  tip = _run_git(checkout, "rev-parse", f"refs/heads/{entry['branch']}").stdout.strip()
703
805
  if tip == transaction["member_parent"]:
704
- _require_member_unpublished(checkout, member, str(transaction["landing"]), entry["branch"])
806
+ _require_member_unpublished(repo, checkout, member, str(transaction["landing"]), entry["branch"])
705
807
  return
706
808
  if tip != transaction["landing"]:
707
809
  raise UndoError(f"member {member} bound branch moved during undo")
@@ -768,7 +870,7 @@ def classify_undo(repo: Path) -> dict[str, object]:
768
870
  [f"an in-progress helper journal exists ({names}); resume that transaction first"]
769
871
  )
770
872
  if git.get("ancestor_of_origin_main"):
771
- return _blocked(["HEAD is an ancestor of origin/main; undo would rewrite shipped history"])
873
+ return _blocked(["HEAD is an ancestor of the origin default branch; undo would rewrite shipped history"])
772
874
 
773
875
  product = _product_dirty(dirty)
774
876
  if product:
@@ -988,7 +1090,7 @@ def _require_unpublished_reset(repo: Path, expected_branch: str) -> None:
988
1090
  if git.get("published"):
989
1091
  raise UndoError("HEAD became published on its recorded remote branch")
990
1092
  if git.get("ancestor_of_origin_main"):
991
- raise UndoError("HEAD became published through origin/main")
1093
+ raise UndoError("HEAD became published through the origin default branch")
992
1094
 
993
1095
 
994
1096
  def _remove_untracked_tree(repo: Path, relative: str) -> None:
@@ -22,6 +22,27 @@ else:
22
22
  from scripts import _common
23
23
 
24
24
 
25
+ def _worktree_list_porcelain(primary: Path) -> tuple[str, bool]:
26
+ try:
27
+ if __package__:
28
+ from . import pipeline_git as pg
29
+ else:
30
+ from scripts import pipeline_git as pg
31
+ except ImportError:
32
+ try:
33
+ import pipeline_git as pg
34
+ except ImportError:
35
+ pg = None
36
+ if pg is not None:
37
+ return pg.worktree_list_porcelain(primary)
38
+ try:
39
+ output = _git(primary, "worktree", "list", "--porcelain", "-z")
40
+ return output, True
41
+ except ValueError:
42
+ output = _git(primary, "worktree", "list", "--porcelain")
43
+ return output, False
44
+
45
+
25
46
  def _git(primary: Path, *args: str) -> str:
26
47
  result = _common.run_git(primary, *args)
27
48
  if result.returncode:
@@ -82,6 +103,33 @@ def _workspace(primary: Path, pin: bool) -> Path:
82
103
  return workspace
83
104
 
84
105
 
106
+ def release_workspace(primary: Path) -> bool:
107
+ """Forget the pinned placement receipt once no linked worktrees remain.
108
+
109
+ Lets GSD_PATH_WORKTREE_ROOT (or a new default) apply again at the next
110
+ milestone's first allocation. Returns True when a receipt was removed;
111
+ any remaining linked worktree (including one only still registered) keeps
112
+ the pin.
113
+ """
114
+ primary = primary.resolve()
115
+ common = Path(_git(primary, "rev-parse", "--git-common-dir"))
116
+ common = (primary / common).resolve()
117
+ receipt = common / "gsd-path" / "workspaces" / f"{_identity(primary)}.json"
118
+ if not receipt.is_file():
119
+ return False
120
+ output, nul_separated = _worktree_list_porcelain(primary)
121
+ records = output.split("\0\0") if nul_separated else [
122
+ block for block in output.strip().split("\n\n") if block.strip()
123
+ ]
124
+ for record in records:
125
+ fields = record.split("\0") if nul_separated else record.splitlines()
126
+ for field in fields:
127
+ if field.startswith("worktree ") and Path(field[len("worktree "):]).resolve() != primary:
128
+ return False
129
+ receipt.unlink()
130
+ return True
131
+
132
+
85
133
  def worktree_path(primary: Path, kind: str, name: str, *, pin: bool = False) -> Path:
86
134
  primary = primary.resolve()
87
135
  if kind not in {"task", "verify", "integrate", "bound"} or not name or Path(name).name != name or name in {".", ".."}:
@@ -91,9 +139,13 @@ def worktree_path(primary: Path, kind: str, name: str, *, pin: bool = False) ->
91
139
  legacy = (primary.parent / f".{primary.name}-gsd-path-integrate-{name}"
92
140
  if kind == "integrate" else primary.parent / f"{primary.name}.gsd-path" / kind / name)
93
141
  branch = f"refs/heads/gsd-path-{kind}/{name}"
94
- records = _git(primary, "worktree", "list", "--porcelain", "-z").split("\0\0")
142
+ output, nul_separated = _worktree_list_porcelain(primary)
143
+ if nul_separated:
144
+ records = output.split("\0\0")
145
+ else:
146
+ records = [block for block in output.strip().split("\n\n") if block.strip()]
95
147
  for record in records:
96
- fields = record.split("\0")
148
+ fields = record.split("\0") if nul_separated else record.splitlines()
97
149
  # Compare as paths: git prints forward slashes on Windows.
98
150
  worktrees = [Path(field[len("worktree "):]) for field in fields if field.startswith("worktree ")]
99
151
  if legacy in worktrees and f"branch {branch}" in fields:
@@ -100,6 +100,10 @@ Obtain a confident answer in every area before finishing:
100
100
  give every named surface at least one success criterion observable
101
101
  there. A criterion satisfied by a passing test alone does not cover a
102
102
  surface.
103
+ 9. **Edges and prohibitions** — once the success criteria are drafted, run
104
+ the [spec-reach probes](references/spec-probes.md): every applicable edge
105
+ and every kept prohibition gets the user's ruling in INTENT.md
106
+ `## Edge coverage` and `## Prohibitions`.
103
107
 
104
108
  ## Program mode
105
109
 
@@ -149,7 +153,10 @@ State ownership. There is no re-interview of charter or roadmap scope:
149
153
  `Lane: milestone`, `Surfaces:` copied verbatim from the entry, and
150
154
  `Review panel:` copied from CHARTER.md (default
151
155
  the configured preference when CHARTER omits it). The confirmation may override the copied
152
- panel value; do not invent `detected` or a named list. When brownfield,
156
+ panel value; do not invent `detected` or a named list. Run the
157
+ [spec-reach probes](references/spec-probes.md) over the derived criteria
158
+ and draft `## Edge coverage` and `## Prohibitions` with recommended
159
+ rulings. When brownfield,
153
160
  fill `## Current state` from the map, record doc-vs-code rulings as in
154
161
  Brownfield mode, and add protected existing behavior under Scope out.
155
162
  Write Ground truth paths from the track root: `.project` normally, or
@@ -157,10 +164,16 @@ State ownership. There is no re-interview of charter or roadmap scope:
157
164
  active `research/` paths.
158
165
  3. Present one confirmation, not an interview: playback the derivation (and
159
166
  brownfield ground truth when present), link the resolved absolute INTENT.md
160
- path, and ask approve or adjust. A requested change that contradicts the
167
+ path, and ask approve or adjust. The confirmation lists each proposed edge
168
+ and prohibition ruling for the user to accept or change; it never rules on
169
+ them silently. A requested change that contradicts the
161
170
  charter or the resolved roadmap entry is a scope change — this phase never
162
- edits the roadmap; surface it to the user for a `$gsd-path-roadmap` re-slice
163
- at the next milestone boundary.
171
+ edits the roadmap; surface it to the user for a `$gsd-path-roadmap`
172
+ re-slice. Before intent approval, that re-slice keeps `define/active` and
173
+ can change the scope of this milestone's `active` entry; after its
174
+ approval, draft INTENT.md again from the changed entry. In `corrections`
175
+ mode the intent is already approved, so the re-slice waits for the next
176
+ milestone boundary.
164
177
  4. On approval, finalize per the Output contract, setting `milestone` to the
165
178
  resolved entry's slug. The router continues with milestone-scoped research
166
179
  when the entry lists open questions, otherwise planning.
@@ -254,7 +267,15 @@ question. The rules change for every primary mode:
254
267
  the brownfield map already established. Milestone mode skips this step.
255
268
  5. Stop when coverage is complete or the user says `enough`. Record remaining
256
269
  uncertainty under `## Open questions` with `RESEARCH` or `NEEDS-USER`.
257
- 6. Classify the proposed milestone lane before writing the approval draft.
270
+ `enough` does not skip the next step.
271
+ 6. In standard mode, run the [spec-reach probes](references/spec-probes.md)
272
+ over the drafted success criteria: one question round for edges, then one
273
+ for kept prohibitions, each with a recommended ruling first. A ruling
274
+ observable where the user sees the work becomes or amends a success
275
+ criterion; write every ruling into `## Edge coverage` and
276
+ `## Prohibitions`. Program mode skips this step (each milestone runs it
277
+ in milestone mode); milestone mode runs it inside its confirmation.
278
+ 7. Classify the proposed milestone lane before writing the approval draft.
258
279
  Milestone mode keeps `Lane: milestone` and skips this classification.
259
280
  Program mode writes CHARTER.md instead of INTENT.md. Otherwise choose
260
281
  `quick` when scope fits at most two deliverable-sized tasks (project
@@ -275,6 +296,11 @@ question. The rules change for every primary mode:
275
296
  ## Output contract
276
297
 
277
298
  At approval, retain the already reviewed `Lane:` value and its one-line reason.
299
+ Before every approval question on INTENT.md, run the bundled `python3
300
+ <absolute-bundled-script> intent --repo <absolute-root>` helper
301
+ (`scripts/check_handoffs.py`, plus `--project-dir .project/next` in
302
+ lookahead). It checks the success criteria, Surfaces, and both probe tables;
303
+ fix a failure in the draft before asking. It approves nothing.
278
304
  Before presenting an approval draft without a CHARTER or explicit panel choice,
279
305
  read `settings.review_panel.value` from the selected runtime's `path_config.py
280
306
  show --repo <absolute-root>` (bundle for legacy projects; off for older selected
@@ -302,5 +328,8 @@ Use the executable phase handoff in AGENTS.md for the next action.
302
328
  - In brownfield mode, state mapped facts for correction; never re-ask them.
303
329
  - Treat solution-shaped problems as hypotheses and uncover the underlying pain.
304
330
  - Preserve vetoes and corrections verbatim.
331
+ - Never rule on an edge's behavior or a prohibition yourself; recommend,
332
+ then record the user's ruling. An edge the user has not ruled on is a
333
+ required answer, including under pre-approval.
305
334
  - Wrap up when every coverage-checklist area is answered or the user signals
306
335
  done, and tag remaining gaps.
@@ -115,9 +115,13 @@ as stated in Lookahead mode.
115
115
  In patch mode, tasks already `done` keep their landed metadata; only
116
116
  the appended patch tasks must start clean.
117
117
  - Require every dependency id to exist, forbid later-wave dependencies,
118
- detect cycles, and forbid file overlap between planned tasks in the same
119
- wave. Same-wave dependency chains are allowed only when their files do
120
- not overlap; the build executes them in dependency layers.
118
+ detect cycles, and forbid unordered file overlap between planned tasks
119
+ in the same wave. Same-wave tasks may share files only when one
120
+ transitively depends on the other through `deps`; the build executes
121
+ them in dependency layers.
122
+ - Require an optional `supersedes` field to be a list of different task
123
+ ids; each one names an existing task of an earlier wave, in the same
124
+ repo, that shares a declared file with the task.
121
125
  - Reject a declared dependency that carries neither data nor a prerequisite
122
126
  effect. A `deps` edge is valid only when the dependent consumes a symbol,
123
127
  signature, schema, endpoint, file format, or path named in that
@@ -157,6 +161,13 @@ as stated in Lookahead mode.
157
161
  command's output — not an internal unit test.
158
162
  Require the surface to land in the same wave as the capability behind
159
163
  it, never in a later polish wave.
164
+ - When INTENT.md has `## Edge coverage`, require a PLAN.md
165
+ `## Held-out checks` row for every `held-out` edge and for no other
166
+ edge. The named test is in that task's `files`, the task's Verify runs
167
+ it, and the task's Acceptance criteria state the edge id and its ruling
168
+ (see [spec-reach probes](references/spec-probes.md)). An edge or
169
+ prohibition ruled `criterion SCn` needs no extra row; it is covered as
170
+ SCn. Put each `judgment` prohibition in its owning task's Approach.
160
171
  - Prove every intent constraint and synthesis decision is covered, that no
161
172
  scope-out veto appears in a task, and that `Project verify` is a real,
162
173
  non-placeholder command in PLAN.md. Every Verify — task and project —
@@ -84,6 +84,17 @@ settled brownfield input.
84
84
  template before dispatch. Record every standard dimension exactly once as
85
85
  `dispatched` or `skipped` with a reason, and assign every research question
86
86
  to at least one dispatched dimension. No question may remain unassigned.
87
+ Copy each question to exactly one `Question assignments` row. Assign one or
88
+ more dispatched dimension slugs there, each in its own code span and
89
+ separated by commas, as in:
90
+
91
+ ```md
92
+ - `[RESEARCH] Which approach is viable?` → `stack`, `pitfalls`
93
+ ```
94
+
95
+ List a dimension only once per row. Every dispatched dimension must own at
96
+ least one question, and its evidence file must answer every question
97
+ assigned to it. Use `- none` only when there are no research questions.
87
98
  2. Decide which standard dimensions have work: dispatch a dimension only when
88
99
  it has at least one assigned `RESEARCH` question from INTENT.md or
89
100
  CHARTER.md. Unsettled choices and risks need that source question before
@@ -27,7 +27,8 @@ return to `$gsd-path` for ownership checking. Legal entry is
27
27
  yet, `roadmap/active|blocked`, or a
28
28
  milestone-boundary re-slice: `inspect/active` or `define/active` with no
29
29
  approved INTENT.md for the next milestone and a user request to re-scope the
30
- remaining `pending` entries,
30
+ remaining `pending` entries or, at `define/active` only, to change the scope
31
+ of the `active` entry,
31
32
  or `roadmap/active` with STATE.milestone null and an `abandoned` entry in
32
33
  ROADMAP.md (the post-abandon re-slice the build orchestrator hands off).
33
34
  Enter from `decide/done` only with `pipeline_state.py transition`, exact
@@ -35,6 +36,8 @@ expected phase/status/milestone/branch/archive fields, event `roadmap started`,
35
36
  and `--set-phase roadmap --set-status active`; never edit STATE directly. In a
36
37
  milestone-boundary re-slice, preserve the entering `inspect/active` or
37
38
  `define/active` state throughout the re-slice.
39
+ A `define/active` state that the router reports in `corrections` mode already
40
+ has an approved INTENT.md; it blocks.
38
41
  Any later phase blocks; mid-milestone
39
42
  re-scope is never legal — finish or ship the active milestone first. A
40
43
  `decide/done` state with ROADMAP.md already present is milestone-scope decide;
@@ -71,8 +74,11 @@ precondition fails.
71
74
  give `.project/ROADMAP.before-reslice.md` with the instruction to preserve
72
75
  `shipped` entries byte-for-byte except Status/Archive/Integrated fields and
73
76
  to never modify `abandoned` entries at all. For a milestone-boundary
74
- re-slice, also preserve the existing `active` entry byte-for-byte; only
75
- `pending` entries are in scope.
77
+ re-slice entered at `inspect/active`, also preserve the existing `active`
78
+ entry byte-for-byte; only `pending` entries are in scope. Entered at
79
+ `define/active`, the `active` entry may also change as the user ruled, but
80
+ its heading (id and slug) and its Status, Depends on, Archive, and
81
+ Integrated fields stay unchanged.
76
82
  The output is exactly `.project/ROADMAP.md` — no other location is
77
83
  canonical.
78
84
  3. Gate ROADMAP.md:
@@ -96,8 +102,11 @@ precondition fails.
96
102
  - On a re-slice, `shipped` entries differ from
97
103
  `.project/ROADMAP.before-reslice.md` only in Status/Archive/Integrated;
98
104
  `abandoned` entries are byte-for-byte identical.
99
- - On a milestone-boundary re-slice, the prior `active` entry is byte-for-byte
100
- identical to `.project/ROADMAP.before-reslice.md`.
105
+ - On a milestone-boundary re-slice entered at `inspect/active`, the prior
106
+ `active` entry is byte-for-byte identical to
107
+ `.project/ROADMAP.before-reslice.md`. Entered at `define/active`, it
108
+ stays the only `active` entry and keeps its heading and its Status,
109
+ Depends on, Archive, and Integrated fields.
101
110
  4. Redispatch one complete corrected brief under logical task name `roadmap`,
102
111
  following the runtime dispatch contract and including all gate failures.
103
112
  Allow one revision round. If it still fails, keep the entering
@@ -160,7 +169,8 @@ precondition fails.
160
169
 
161
170
  The helper owns roadmap selection, expected state, baseline cleanup and
162
171
  the approval checkpoint. It journals before mutation, retains the baseline
163
- bytes for recovery, and refuses changes to settled or active entries.
172
+ bytes for recovery, and refuses changes to settled entries and every
173
+ `active` entry change that step 2 does not permit.
164
174
  A milestone-boundary re-slice retains Inspect or Define; a post-abandon
165
175
  re-slice enters Inspect. Require its typed result to report `status:
166
176
  approved`, the requested kind, milestone and returned commit. After an
@@ -159,7 +159,12 @@ so the position is unchanged. Then:
159
159
  then stop and do not invent a patch finding. A `not-met`,
160
160
  `unverifiable`, or blocked gap with valid evidence sets `ship/blocked`,
161
161
  writes `.project/review/PATCH-FINDINGS.md` from the patch-findings template,
162
- and runs `python3 <absolute check_handoffs.py> patch --repo <absolute repo
162
+ copying the full `Risk: ...` line into each gap finding's Locator, its
163
+ `Found` field into Evidence, and its `Fix direction` field verbatim. For a
164
+ `FINAL.md` finding, use its `SC<n>` id as Locator and copy its `Finding` and
165
+ `Fix direction` fields verbatim. The patch validator requires these source
166
+ field values to match exactly; do not shorten or paraphrase them. Run
167
+ `python3 <absolute check_handoffs.py> patch --repo <absolute repo
163
168
  root>`. Present the blocked outcome, link the resolved absolute
164
169
  PATCH-FINDINGS.md path, and state that patch planning is next. The manifest
165
170
  contains exactly those FINAL.md and gap rows. When routed by an active
@@ -262,7 +267,11 @@ The persisted `STATE.archive` field is the transaction identity.
262
267
  `python3 <absolute archive_milestone.py> close-members --repo <root>`
263
268
  after preflight passes and before recording shipment. It closes each
264
269
  locked member in lock order in that member's integration mode and
265
- journals each one. When it returns `awaiting-merge`, report the member PR
270
+ journals each one. Each member closes onto the default branch that
271
+ MEMBERS.md records for it (`main` when its section has no
272
+ `Default branch`); when the command reports
273
+ `remote default must be <recorded>`, stop and report it. When it returns
274
+ `awaiting-merge`, report the member PR
266
275
  link, stop, and rerun the same command after the user merges it; never
267
276
  merge a member PR yourself. When it returns `integrated`, keep its `body`
268
277
  for the ship commit below.
@@ -305,8 +314,10 @@ The persisted `STATE.archive` field is the transaction identity.
305
314
  milestone is not shipped until integration below passes.
306
315
  7. Integrate only after the postcommit `validate` passes, with the recorded
307
316
  ship commit and exact reviewed HEAD unchanged. Run `python3 <absolute archive_milestone.py> integrate --repo <root> --slug <STATE.milestone>`.
308
- This helper validates the ship commit, fetches origin, requires remote
309
- default `main`, and follows the locked `STATE.integration` mode:
317
+ This helper validates the ship commit, fetches origin, requires the remote
318
+ default to equal STATE `default_branch` (`main` when the field is absent),
319
+ and follows the locked `STATE.integration` mode. `main` and `origin/main`
320
+ below mean that recorded branch:
310
321
 
311
322
  - `direct` owns the resumable merge transaction. It creates the canonical
312
323
  named integration worktree, performs the hook-verified `--no-ff` merge,
@@ -339,6 +350,22 @@ The persisted `STATE.archive` field is the transaction identity.
339
350
  landing SHA. A closed unmerged PR, squash, rebase, merge queue, moved
340
351
  head, duplicate PR, or non-GitHub.com origin blocks. The remote bound
341
352
  branch may be absent after a valid merge because GitHub may auto-delete it.
353
+ - `external-landing` is for an origin where Path cannot open the review
354
+ itself, such as GitLab, Gitea, or GitHub Enterprise Server. It needs no
355
+ forge CLI. The helper publishes the bound branch at the exact ship commit,
356
+ never pushes `main`, and returns `status: awaiting-merge` with `branch`,
357
+ `merge_subject`, and `merge_body`. Present **Outcome**, and state in
358
+ **Next** that the user must open a merge request from `branch` to `main`
359
+ in their own forge and merge it with a merge commit. The merge commit
360
+ message must be exactly `merge_subject`, a blank line, then `merge_body`;
361
+ show both values verbatim in a code block. Stop this ship invocation;
362
+ Path never opens or merges the request. The forge may delete the source
363
+ branch after the merge. On rerun after the merge, the helper requires exactly
364
+ one commit with that subject on `origin/main` first-parent history, that
365
+ exact body, and two parents with the ship commit as second parent. It
366
+ then writes and pushes the annotated milestone tag containing the ship
367
+ SHA and landing SHA. A squash, rebase, fast-forward, or different merge
368
+ message blocks.
342
369
 
343
370
  A passing run returns the same proof exposed by `validate-integrated`.
344
371
  A non-zero result blocks; rerun the exact `integrate` command to resume a
@@ -349,7 +376,7 @@ The persisted `STATE.archive` field is the transaction identity.
349
376
  continuing. Never merge into or re-ship the closed branch.
350
377
  For a later validation recheck with origin network access, run
351
378
  `python3 <absolute archive_milestone.py> validate-integrated --repo <root> --slug <STATE.milestone>`.
352
- Both integration modes refresh the remote-default and mirrored milestone-tag
379
+ All integration modes refresh the remote-default and mirrored milestone-tag
353
380
  refs when an `origin` remote exists. Without that remote, validation resolves
354
381
  the existing remote-default refs locally, preserving offline direct-mode
355
382
  fixtures. PR mode also revalidates the PR identity, state, merge provenance,