@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
@@ -9,6 +9,7 @@ sys.dont_write_bytecode = True
9
9
 
10
10
  import json
11
11
  import re
12
+ import contextlib
12
13
  from pathlib import Path, PurePosixPath
13
14
  from typing import Optional
14
15
 
@@ -300,7 +301,9 @@ def discard_unpublished_stale_integration(
300
301
  project, "merge-base", "--is-ancestor", merge_commit, remote_default_sha
301
302
  )
302
303
  if published.returncode == 0:
303
- raise ArchiveError("stale integration merge is already published on origin/main")
304
+ raise ArchiveError(
305
+ f"stale integration merge is already published on origin/{default_name}"
306
+ )
304
307
  if published.returncode != 1:
305
308
  archive_milestone.require_git_success(published, "inspect stale integration publication")
306
309
 
@@ -658,9 +661,10 @@ def require_pull_request_identity(
658
661
  pull: dict,
659
662
  branch: str,
660
663
  ship_commit: str,
664
+ base: str = "main",
661
665
  ) -> None:
662
- if pull["base"].get("ref") != "main":
663
- raise ArchiveError("GitHub pull request base is not main")
666
+ if pull["base"].get("ref") != base:
667
+ raise ArchiveError(f"GitHub pull request base is not {base}")
664
668
  if pull["head"].get("ref") != branch:
665
669
  raise ArchiveError("GitHub pull request head is not the bound branch")
666
670
  if pull["head"].get("sha") != ship_commit:
@@ -669,7 +673,9 @@ def require_pull_request_identity(
669
673
  raise ArchiveError("GitHub pull request head repository is not origin")
670
674
 
671
675
 
672
- def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optional[dict]:
676
+ def find_pull_request(
677
+ repository: str, branch: str, ship_commit: str, base: str = "main"
678
+ ) -> Optional[dict]:
673
679
  pulls = require_pull_request_pages(
674
680
  github_api_json(
675
681
  f"repos/{repository}/pulls",
@@ -678,7 +684,7 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
678
684
  "-f",
679
685
  "state=all",
680
686
  "-f",
681
- "base=main",
687
+ f"base={base}",
682
688
  "--paginate",
683
689
  "--slurp",
684
690
  )
@@ -686,7 +692,7 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
686
692
  pulls = [
687
693
  pull
688
694
  for pull in pulls
689
- if pull["base"].get("ref") == "main"
695
+ if pull["base"].get("ref") == base
690
696
  and (
691
697
  pull["head"].get("sha") == ship_commit
692
698
  or (
@@ -696,17 +702,19 @@ def find_pull_request(repository: str, branch: str, ship_commit: str) -> Optiona
696
702
  )
697
703
  ]
698
704
  if len(pulls) > 1:
699
- raise ArchiveError("multiple pull requests target main from the ship commit")
705
+ raise ArchiveError(f"multiple pull requests target {base} from the ship commit")
700
706
  if not pulls:
701
707
  return None
702
708
  pull = pulls[0]
703
- require_pull_request_identity(repository, pull, branch, ship_commit)
709
+ require_pull_request_identity(repository, pull, branch, ship_commit, base)
704
710
  return pull
705
711
 
706
712
 
707
- def pull_request_body(archive_path: str, ship_commit: str, branch: str) -> str:
713
+ def pull_request_body(
714
+ archive_path: str, ship_commit: str, branch: str, base: str = "main"
715
+ ) -> str:
708
716
  return (
709
- f"{integrate_commit_body(archive_path, ship_commit, 'main', branch)}"
717
+ f"{integrate_commit_body(archive_path, ship_commit, base, branch)}"
710
718
  f"\n\n---\n{PR_CREDIT_LINE}"
711
719
  )
712
720
 
@@ -718,8 +726,12 @@ def update_pull_request_body(
718
726
  ship_commit: str,
719
727
  branch: str,
720
728
  expected_body: Optional[str] = None,
729
+ base: str = "main",
721
730
  ) -> dict:
722
- expected = expected_body if expected_body is not None else pull_request_body(archive_path, ship_commit, branch)
731
+ expected = (
732
+ expected_body if expected_body is not None
733
+ else pull_request_body(archive_path, ship_commit, branch, base)
734
+ )
723
735
  current = pull.get("body")
724
736
  if isinstance(current, str) and current.rstrip().endswith(PR_CREDIT_LINE):
725
737
  return pull
@@ -731,7 +743,7 @@ def update_pull_request_body(
731
743
  f"body={expected}",
732
744
  )
733
745
  updated = require_pull_request_shape(response)
734
- require_pull_request_identity(repository, updated, branch, ship_commit)
746
+ require_pull_request_identity(repository, updated, branch, ship_commit, base)
735
747
  if updated.get("body") != expected:
736
748
  raise ArchiveError("updated GitHub pull request is missing the credit footer")
737
749
  return updated
@@ -743,23 +755,24 @@ def create_pull_request(
743
755
  archive_path: str,
744
756
  archive_name: str,
745
757
  ship_commit: str,
758
+ base: str = "main",
746
759
  ) -> dict:
747
- body = pull_request_body(archive_path, ship_commit, branch)
760
+ body = pull_request_body(archive_path, ship_commit, branch, base)
748
761
  response = github_api_json(
749
762
  f"repos/{repository}/pulls",
750
763
  "--method",
751
764
  "POST",
752
765
  "-f",
753
- f"title={integrate_subject(archive_name, 'main')}",
766
+ f"title={integrate_subject(archive_name, base)}",
754
767
  "-f",
755
768
  f"head={branch}",
756
769
  "-f",
757
- "base=main",
770
+ f"base={base}",
758
771
  "-f",
759
772
  f"body={body}",
760
773
  )
761
774
  pull = require_pull_request_shape(response)
762
- require_pull_request_identity(repository, pull, branch, ship_commit)
775
+ require_pull_request_identity(repository, pull, branch, ship_commit, base)
763
776
  return pull
764
777
 
765
778
 
@@ -797,6 +810,36 @@ def pull_request_tag_metadata(project: Path, tag_ref: str) -> dict[str, str]:
797
810
  return fields
798
811
 
799
812
 
813
+ def external_landing_tag_message(
814
+ archive_name: str, ship_commit: str, landing: str
815
+ ) -> str:
816
+ return (
817
+ f"milestone {archive_name}\n\n"
818
+ "Mode: external-landing\n"
819
+ f"Ship: {ship_commit}\n"
820
+ f"Landing: {landing}"
821
+ )
822
+
823
+
824
+ def external_landing_tag_metadata(project: Path, tag_ref: str) -> dict[str, str]:
825
+ message = archive_milestone.require_git_success(
826
+ run_git(project, "for-each-ref", "--format=%(contents)", tag_ref),
827
+ "inspect external-landing milestone tag",
828
+ )
829
+ fields: dict[str, str] = {}
830
+ for line in message.splitlines():
831
+ match = re.fullmatch(r"(Mode|Ship|Landing): (.+)", line)
832
+ if match:
833
+ if match.group(1) in fields:
834
+ raise ArchiveError("external-landing milestone tag repeats metadata")
835
+ fields[match.group(1)] = match.group(2)
836
+ if set(fields) != {"Mode", "Ship", "Landing"}:
837
+ raise ArchiveError("external-landing milestone tag is missing metadata")
838
+ if fields["Mode"] != "external-landing":
839
+ raise ArchiveError("external-landing milestone tag has the wrong mode")
840
+ return fields
841
+
842
+
800
843
  def require_pull_request_merge(
801
844
  project: Path,
802
845
  merge_commit: str,
@@ -819,7 +862,9 @@ def require_pull_request_merge(
819
862
  "inspect remote-default first-parent history",
820
863
  ).splitlines()
821
864
  if merge_commit not in first_parent:
822
- raise ArchiveError("pull-request merge is not on origin/main first-parent history")
865
+ raise ArchiveError(
866
+ f"pull-request merge is not on {remote_default} first-parent history"
867
+ )
823
868
 
824
869
 
825
870
  def require_pull_request_merge_provenance(
@@ -936,7 +981,8 @@ def integrate_pull_request(
936
981
  raise ArchiveError("pull-request integration requires a bound branch")
937
982
  require_github_authentication()
938
983
  repository = github_repository(project)
939
- pull = find_pull_request(repository, branch, ship_commit)
984
+ base = state.default_branch
985
+ pull = find_pull_request(repository, branch, ship_commit, base)
940
986
  if pull is None:
941
987
  publish_bound_branch(project, branch, ship_commit)
942
988
  pull = create_pull_request(
@@ -945,6 +991,7 @@ def integrate_pull_request(
945
991
  archive_path,
946
992
  archive_name,
947
993
  ship_commit,
994
+ base,
948
995
  )
949
996
  elif pull["merged_at"] is None:
950
997
  if pull["state"] != "open":
@@ -955,6 +1002,7 @@ def integrate_pull_request(
955
1002
  archive_path,
956
1003
  ship_commit,
957
1004
  branch,
1005
+ base=base,
958
1006
  )
959
1007
  if pull["merged_at"] is None:
960
1008
  if pull["state"] != "open":
@@ -985,6 +1033,7 @@ def integrate_pull_request(
985
1033
  archive_path,
986
1034
  ship_commit,
987
1035
  branch,
1036
+ base=base,
988
1037
  )
989
1038
  tag_name = f"milestone/{archive_name}"
990
1039
  message = pull_request_tag_message(
@@ -1021,6 +1070,83 @@ def integrate_pull_request(
1021
1070
  return validate_integrated(project, archive_milestone.milestone_slug(state))
1022
1071
 
1023
1072
 
1073
+ def integrate_external_landing(
1074
+ project: Path,
1075
+ state: PipelineState,
1076
+ archive_path: str,
1077
+ archive_name: str,
1078
+ ship_commit: str,
1079
+ remote_default: str,
1080
+ default_name: str,
1081
+ bound_branch: str,
1082
+ ) -> dict:
1083
+ if state.branch is None:
1084
+ raise ArchiveError("external-landing integration requires a bound branch")
1085
+ publishable_bound_branch(project, bound_branch, ship_commit)
1086
+ contains_ship = run_git(
1087
+ project, "merge-base", "--is-ancestor", ship_commit, remote_default
1088
+ )
1089
+ if contains_ship.returncode == 1:
1090
+ publish_bound_branch(project, bound_branch, ship_commit)
1091
+ return {
1092
+ "status": "awaiting-merge",
1093
+ "mode": "external-landing",
1094
+ "archive": archive_path,
1095
+ "commit": ship_commit,
1096
+ "branch": bound_branch,
1097
+ "merge_subject": integrate_subject(archive_name, default_name),
1098
+ "merge_body": integrate_commit_body(
1099
+ archive_path, ship_commit, default_name, bound_branch
1100
+ ),
1101
+ }
1102
+ if contains_ship.returncode != 0:
1103
+ archive_milestone.require_git_success(
1104
+ contains_ship, "inspect ship ancestry on the remote default"
1105
+ )
1106
+ raise AssertionError("unreachable")
1107
+
1108
+ merge_commit = find_integrate_commit(
1109
+ project, remote_default, archive_name, ship_commit
1110
+ )
1111
+ require_generated_integration_commit(
1112
+ project,
1113
+ merge_commit,
1114
+ archive_path,
1115
+ archive_name,
1116
+ ship_commit,
1117
+ default_name,
1118
+ bound_branch,
1119
+ )
1120
+ tag_name = f"milestone/{archive_name}"
1121
+ message = external_landing_tag_message(archive_name, ship_commit, merge_commit)
1122
+ tag_object, tag_published = ensure_integration_tag(
1123
+ project,
1124
+ tag_name,
1125
+ merge_commit,
1126
+ message,
1127
+ )
1128
+ if not tag_published:
1129
+ archive_milestone.require_git_success(
1130
+ run_git(
1131
+ project,
1132
+ "push",
1133
+ "origin",
1134
+ f"refs/tags/{tag_name}:refs/tags/{tag_name}",
1135
+ ),
1136
+ "push milestone tag",
1137
+ )
1138
+ archive_milestone.require_git_success(
1139
+ run_git(
1140
+ project,
1141
+ "update-ref",
1142
+ f"refs/remotes/origin/tags/{tag_name}",
1143
+ tag_object,
1144
+ ),
1145
+ "refresh local milestone-tag ref",
1146
+ )
1147
+ return validate_integrated(project, archive_milestone.milestone_slug(state))
1148
+
1149
+
1024
1150
  def integrate(repo: Path, slug: str) -> dict:
1025
1151
  project = repo.resolve()
1026
1152
  active_root = archive_milestone.require_project_layout(project)
@@ -1055,8 +1181,10 @@ def integrate(repo: Path, slug: str) -> dict:
1055
1181
 
1056
1182
  remote_default = refresh_origin(project)["remote_default"]
1057
1183
  default_name = default_branch_name(remote_default)
1058
- if default_name != "main":
1059
- raise ArchiveError(f"remote default must be main, got {default_name!r}")
1184
+ if default_name != state.default_branch:
1185
+ raise ArchiveError(
1186
+ f"remote default must be {state.default_branch}, got {default_name!r}"
1187
+ )
1060
1188
  if bound_branch == default_name:
1061
1189
  raise ArchiveError("bound branch must not be the remote default")
1062
1190
  remote_default_sha = archive_milestone.require_git_success(
@@ -1070,6 +1198,17 @@ def integrate(repo: Path, slug: str) -> dict:
1070
1198
  archive_name,
1071
1199
  ship_commit,
1072
1200
  )
1201
+ if state.integration == "external-landing":
1202
+ return integrate_external_landing(
1203
+ project,
1204
+ state,
1205
+ archive_path,
1206
+ archive_name,
1207
+ ship_commit,
1208
+ remote_default,
1209
+ default_name,
1210
+ bound_branch,
1211
+ )
1073
1212
  publishable_bound_branch(project, bound_branch, ship_commit)
1074
1213
  integration_branch, worktree = integration_names(project, archive_name, pin=True)
1075
1214
  interrupted_worktree = registered_worktree(project, integration_branch)
@@ -1152,7 +1291,7 @@ def integrate(repo: Path, slug: str) -> dict:
1152
1291
  "origin",
1153
1292
  f"{merge_commit}:refs/heads/{default_name}",
1154
1293
  ),
1155
- "push integration merge to main",
1294
+ f"push integration merge to {default_name}",
1156
1295
  )
1157
1296
  archive_milestone.require_git_success(
1158
1297
  run_git(project, "update-ref", f"refs/remotes/{remote_default}", merge_commit),
@@ -1297,10 +1436,12 @@ def validate_integrated(
1297
1436
  if bound_branch == default_name:
1298
1437
  raise ArchiveError(
1299
1438
  f"bound branch {bound_branch!r} is the remote default; "
1300
- "ship merges onto main, never onto the work branch"
1439
+ "ship merges onto the default branch, never onto the work branch"
1440
+ )
1441
+ if default_name != state.default_branch:
1442
+ raise ArchiveError(
1443
+ f"remote default must be {state.default_branch}, got {default_name!r}"
1301
1444
  )
1302
- if default_name != "main":
1303
- raise ArchiveError(f"remote default must be main, got {default_name!r}")
1304
1445
 
1305
1446
  # (b) Resolve the integration proof selected before build.
1306
1447
  tag_name = f"milestone/{archive_name}"
@@ -1316,7 +1457,7 @@ def validate_integrated(
1316
1457
  pull_request = metadata["Pull-Request"]
1317
1458
  require_github_authentication()
1318
1459
  repository = github_repository(project)
1319
- pull = find_pull_request(repository, bound_branch, ship_commit)
1460
+ pull = find_pull_request(repository, bound_branch, ship_commit, default_name)
1320
1461
  if pull is None:
1321
1462
  raise ArchiveError("GitHub pull request for the ship commit is missing")
1322
1463
  if pull["html_url"] != pull_request:
@@ -1345,6 +1486,32 @@ def validate_integrated(
1345
1486
  merge_commit,
1346
1487
  ),
1347
1488
  )
1489
+ elif state.integration == "external-landing":
1490
+ merge_commit = find_integrate_commit(
1491
+ project, remote_default, archive_name, ship_commit
1492
+ )
1493
+ archive_milestone.require_canonical_commit_body(
1494
+ project,
1495
+ merge_commit,
1496
+ integrate_subject(archive_name, default_name),
1497
+ integrate_commit_body(configured, ship_commit, default_name, bound_branch),
1498
+ "integration",
1499
+ )
1500
+ require_pull_request_merge(project, merge_commit, ship_commit, remote_default)
1501
+ if run_git(project, "rev-parse", "--verify", "--quiet", tag_ref).returncode != 0:
1502
+ raise ArchiveError(f"missing milestone tag: {tag_name}")
1503
+ metadata = external_landing_tag_metadata(project, tag_ref)
1504
+ if metadata["Ship"] != ship_commit:
1505
+ raise ArchiveError("external-landing milestone tag names the wrong ship commit")
1506
+ if metadata["Landing"] != merge_commit:
1507
+ raise ArchiveError("external-landing milestone tag names the wrong landing")
1508
+ require_annotated_tag(
1509
+ project,
1510
+ tag_ref,
1511
+ merge_commit,
1512
+ f"milestone tag {tag_name}",
1513
+ external_landing_tag_message(archive_name, ship_commit, merge_commit),
1514
+ )
1348
1515
  else:
1349
1516
  merge_commit = find_integrate_commit(
1350
1517
  project, remote_default, archive_name, ship_commit
@@ -1381,7 +1548,8 @@ def validate_integrated(
1381
1548
  ship_commit,
1382
1549
  tag_name,
1383
1550
  merge_commit,
1384
- allow_missing_bound=state.integration == "pull-request" or (historical and has_origin),
1551
+ allow_missing_bound=state.integration in ("pull-request", "external-landing")
1552
+ or (historical and has_origin),
1385
1553
  retired=historical,
1386
1554
  )
1387
1555
 
@@ -1482,21 +1650,23 @@ def require_published_integration(
1482
1650
  )
1483
1651
 
1484
1652
 
1485
- def _member_names(coordinator: Path, member: str, archive_path: str) -> tuple[Path, str, str, str, str]:
1486
- """A member's checkout, coordinator project, bound branch, merge subject, and tag."""
1653
+ def _member_names(coordinator: Path, member: str, archive_path: str) -> tuple[Path, str, str, str, str, str]:
1654
+ """A member's checkout, coordinator project, bound branch, merge subject, tag,
1655
+ and the default branch that MEMBERS.md records for it."""
1487
1656
  try:
1488
1657
  import isolation
1489
1658
  except ImportError: # pragma: no cover - package import used by tests
1490
1659
  from scripts import isolation
1491
1660
  try:
1492
1661
  checkout, project = isolation.member_checkout(coordinator.resolve(), member)
1662
+ default = isolation.member_default_branch(coordinator.resolve(), member)
1493
1663
  except isolation.IsolationError as error:
1494
1664
  raise ArchiveError(str(error)) from error
1495
1665
  archive_name = PurePosixPath(archive_path).name
1496
1666
  identifier = milestone_id(milestone_number(archive_name))
1497
1667
  bound = f"gsd-path/{project}-{identifier}"
1498
- subject = f"integrate: {project} {identifier} — merge {bound} into main"
1499
- return checkout, project, bound, subject, f"milestone/{project}-{archive_name}"
1668
+ subject = f"integrate: {project} {identifier} — merge {bound} into {default}"
1669
+ return checkout, project, bound, subject, f"milestone/{project}-{archive_name}", default
1500
1670
 
1501
1671
 
1502
1672
  def _member_merge_body(archive_path: str, member: str, reviewed_head: str) -> str:
@@ -1523,16 +1693,16 @@ def member_pull_request_tag_metadata(contents: str) -> dict[str, str]:
1523
1693
  return fields
1524
1694
 
1525
1695
 
1526
- def _find_member_merge(checkout: Path, subject: str, body: str, reviewed_head: str) -> Optional[str]:
1527
- """The member merge in origin/main's first-parent history, if published."""
1696
+ def _find_member_merge(checkout: Path, subject: str, body: str, reviewed_head: str, default: str) -> Optional[str]:
1697
+ """The member merge in the first-parent history of origin/<default>, if published."""
1528
1698
  log = archive_milestone.require_git_success(
1529
- run_git(checkout, "log", "--first-parent", "--format=%H%x00%s", "origin/main"),
1530
- "inspect member origin/main first-parent history",
1699
+ run_git(checkout, "log", "--first-parent", "--format=%H%x00%s", f"origin/{default}"),
1700
+ f"inspect member origin/{default} first-parent history",
1531
1701
  )
1532
1702
  matches = [commit for commit, _, text in (line.partition("\x00") for line in log.splitlines())
1533
1703
  if text == subject]
1534
1704
  if len(matches) > 1:
1535
- raise ArchiveError(f"multiple member merges named {subject!r} on origin/main")
1705
+ raise ArchiveError(f"multiple member merges named {subject!r} on origin/{default}")
1536
1706
  if not matches:
1537
1707
  return None
1538
1708
  _require_member_merge(checkout, matches[0], subject, body, reviewed_head)
@@ -1587,7 +1757,7 @@ def _create_member_merge(checkout: Path, project: str, archive_path: str, subjec
1587
1757
  + (", ".join(conflicts) or (merged.stderr or merged.stdout).strip()))
1588
1758
  merge = archive_milestone.require_git_success(run_git(worktree, "rev-parse", "HEAD"), "resolve member merge")
1589
1759
  if _require_member_merge(worktree, merge, subject, body, reviewed_head) != origin_main:
1590
- raise ArchiveError("member merge first parent is not origin/main")
1760
+ raise ArchiveError("member merge first parent is not the origin default branch")
1591
1761
  remove_registered_worktree(checkout, branch, worktree)
1592
1762
  return merge
1593
1763
 
@@ -1614,34 +1784,39 @@ def _publish_member_bound(checkout: Path, project: str, member: str, bound: str,
1614
1784
  raise ArchiveError(f"member {member} origin/{bound} moved: {remote_bound}")
1615
1785
 
1616
1786
 
1617
- def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str) -> None:
1787
+ def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str, default: str) -> None:
1618
1788
  if optional_ref(checkout, f"refs/heads/{bound}") != reviewed_head:
1619
1789
  raise ArchiveError(f"member {member} {bound} is not at its reviewed HEAD {reviewed_head}")
1620
- if default_branch_name(refresh_origin(checkout)["remote_default"]) != "main":
1621
- raise ArchiveError(f"member {member} remote default must be main")
1790
+ live = default_branch_name(refresh_origin(checkout)["remote_default"])
1791
+ if live != default:
1792
+ raise ArchiveError(f"member {member} remote default must be {default}, got {live!r}")
1622
1793
 
1623
1794
 
1624
1795
  def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
1625
- """Direct-mode member close: publish the bound branch, merge it into main, then tag."""
1626
- checkout, project, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
1796
+ """Direct-mode member close: publish the bound branch, merge it into the member
1797
+ default branch, then tag."""
1798
+ checkout, project, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
1799
+ origin_default = f"origin/{default}"
1627
1800
  body = _member_merge_body(archive_path, member, reviewed_head)
1628
- _require_member_close(checkout, member, bound, reviewed_head)
1629
- merge = _find_member_merge(checkout, subject, body, reviewed_head)
1801
+ _require_member_close(checkout, member, bound, reviewed_head, default)
1802
+ merge = _find_member_merge(checkout, subject, body, reviewed_head, default)
1630
1803
  if merge is None:
1631
1804
  origin_main = archive_milestone.require_git_success(
1632
- run_git(checkout, "rev-parse", "origin/main"), "resolve member origin/main")
1805
+ run_git(checkout, "rev-parse", origin_default), f"resolve member {origin_default}")
1633
1806
  ancestry = run_git(checkout, "merge-base", "--is-ancestor", reviewed_head, origin_main)
1634
1807
  if ancestry.returncode == 0:
1635
- raise ArchiveError("member reviewed HEAD reached origin/main outside Path; user-approved recovery required")
1808
+ raise ArchiveError(
1809
+ f"member reviewed HEAD reached {origin_default} outside Path; user-approved recovery required")
1636
1810
  if ancestry.returncode != 1:
1637
1811
  archive_milestone.require_git_success(ancestry, "inspect member reviewed HEAD ancestry")
1638
1812
  merge = _create_member_merge(checkout, project, archive_path, subject, body, reviewed_head, origin_main)
1639
- # Publish in order: bound branch, main, tag; a rerun publishes only what is missing.
1813
+ # Publish in order: bound branch, default branch, tag; a rerun publishes only what is missing.
1640
1814
  _publish_member_bound(checkout, project, member, bound, reviewed_head)
1641
- if run_git(checkout, "merge-base", "--is-ancestor", merge, "origin/main").returncode != 0:
1642
- _push_member_ref(checkout, project, merge, "refs/heads/main", merge, None)
1815
+ if run_git(checkout, "merge-base", "--is-ancestor", merge, origin_default).returncode != 0:
1816
+ _push_member_ref(checkout, project, merge, f"refs/heads/{default}", merge, None)
1643
1817
  archive_milestone.require_git_success(
1644
- run_git(checkout, "update-ref", "refs/remotes/origin/main", merge), "refresh member origin/main")
1818
+ run_git(checkout, "update-ref", f"refs/remotes/{origin_default}", merge),
1819
+ f"refresh member {origin_default}")
1645
1820
  tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge)
1646
1821
  if not tag_published:
1647
1822
  _push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
@@ -1655,12 +1830,12 @@ def integrate_member(coordinator: Path, member: str, archive_path: str, reviewed
1655
1830
 
1656
1831
  def integrate_member_pull_request(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
1657
1832
  """Pull-request member close: open one PR for the bound branch; after a human merge, tag it."""
1658
- checkout, project, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
1833
+ checkout, project, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
1659
1834
  body = _member_merge_body(archive_path, member, reviewed_head)
1660
- _require_member_close(checkout, member, bound, reviewed_head)
1835
+ _require_member_close(checkout, member, bound, reviewed_head, default)
1661
1836
  require_github_authentication()
1662
1837
  repository = github_repository(checkout)
1663
- pull = find_pull_request(repository, bound, reviewed_head)
1838
+ pull = find_pull_request(repository, bound, reviewed_head, default)
1664
1839
  if pull is None or pull["merged_at"] is None:
1665
1840
  if pull is not None and pull["state"] != "open":
1666
1841
  raise ArchiveError(f"member {member} pull request was closed without merging")
@@ -1668,12 +1843,12 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
1668
1843
  if pull is None:
1669
1844
  pull = require_pull_request_shape(github_api_json(
1670
1845
  f"repos/{repository}/pulls", "--method", "POST", "-f", f"title={subject}",
1671
- "-f", f"head={bound}", "-f", "base=main", "-f", f"body={body}\n\n---\n{PR_CREDIT_LINE}"))
1672
- require_pull_request_identity(repository, pull, bound, reviewed_head)
1846
+ "-f", f"head={bound}", "-f", f"base={default}", "-f", f"body={body}\n\n---\n{PR_CREDIT_LINE}"))
1847
+ require_pull_request_identity(repository, pull, bound, reviewed_head, default)
1673
1848
  else:
1674
1849
  pull = update_pull_request_body(
1675
1850
  repository, pull, archive_path, reviewed_head, bound,
1676
- f"{body}\n\n---\n{PR_CREDIT_LINE}",
1851
+ f"{body}\n\n---\n{PR_CREDIT_LINE}", default,
1677
1852
  )
1678
1853
  require_pull_request_merge_provenance(repository, pull["number"])
1679
1854
  return {"status": "awaiting-merge", "mode": "pull-request", "member": member,
@@ -1683,10 +1858,10 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
1683
1858
  raise ArchiveError(f"member {member} pull request merge metadata is invalid")
1684
1859
  require_pull_request_merge_provenance(repository, pull["number"], merge)
1685
1860
  refresh_origin(checkout)
1686
- require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
1861
+ require_pull_request_merge(checkout, merge, reviewed_head, f"origin/{default}")
1687
1862
  pull = update_pull_request_body(
1688
1863
  repository, pull, archive_path, reviewed_head, bound,
1689
- f"{body}\n\n---\n{PR_CREDIT_LINE}",
1864
+ f"{body}\n\n---\n{PR_CREDIT_LINE}", default,
1690
1865
  )
1691
1866
  message = member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge)
1692
1867
  tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge, message)
@@ -1701,8 +1876,8 @@ def integrate_member_pull_request(coordinator: Path, member: str, archive_path:
1701
1876
  def validate_member_integrated(
1702
1877
  coordinator: Path, member: str, archive_path: str, reviewed_head: str, *, refresh: bool = True
1703
1878
  ) -> dict:
1704
- """Prove the member merge is on origin/main and its published tag points at it."""
1705
- checkout, _, bound, subject, tag_name = _member_names(coordinator, member, archive_path)
1879
+ """Prove the member merge is on the member default branch and its published tag points at it."""
1880
+ checkout, _, bound, subject, tag_name, default = _member_names(coordinator, member, archive_path)
1706
1881
  tracking = f"refs/remotes/origin/tags/{tag_name}"
1707
1882
  if refresh:
1708
1883
  refresh_origin(checkout)
@@ -1725,7 +1900,7 @@ def validate_member_integrated(
1725
1900
  raise ArchiveError(f"member {member} tag {tag_name} names a different landing")
1726
1901
  require_github_authentication()
1727
1902
  repository = github_repository(checkout)
1728
- pull = find_pull_request(repository, bound, reviewed_head)
1903
+ pull = find_pull_request(repository, bound, reviewed_head, default)
1729
1904
  if pull is None:
1730
1905
  raise ArchiveError(f"member {member} GitHub pull request is missing")
1731
1906
  if pull["html_url"] != metadata["Pull-Request"]:
@@ -1734,16 +1909,16 @@ def validate_member_integrated(
1734
1909
  or pull["merge_commit_sha"] != merge):
1735
1910
  raise ArchiveError(f"member {member} GitHub pull request is not merged at the tagged landing")
1736
1911
  require_pull_request_merge_provenance(repository, pull["number"], merge)
1737
- require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
1912
+ require_pull_request_merge(checkout, merge, reviewed_head, f"origin/{default}")
1738
1913
  require_annotated_tag(
1739
1914
  checkout, tracking, merge, f"member tag {tag_name}",
1740
1915
  member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge),
1741
1916
  )
1742
1917
  else:
1743
1918
  merge = _find_member_merge(checkout, subject, _member_merge_body(archive_path, member, reviewed_head),
1744
- reviewed_head)
1919
+ reviewed_head, default)
1745
1920
  if merge is None:
1746
- raise ArchiveError(f"member {member} has no {subject!r} merge on origin/main")
1921
+ raise ArchiveError(f"member {member} has no {subject!r} merge on origin/{default}")
1747
1922
  if published is None:
1748
1923
  raise ArchiveError(f"member {member} tag {tag_name} is not published")
1749
1924
  require_annotated_tag(checkout, tracking, merge, f"member tag {tag_name}")
@@ -1757,7 +1932,7 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
1757
1932
  import members
1758
1933
  except ImportError: # pragma: no cover - package import used by tests
1759
1934
  from scripts import members
1760
- checkout, project, bound, _, tag_name = _member_names(coordinator, member, archive_path)
1935
+ checkout, project, bound, _, tag_name, default = _member_names(coordinator, member, archive_path)
1761
1936
  proven = validate_member_integrated(coordinator, member, archive_path, reviewed_head)
1762
1937
  if (proven["merge"], proven["tag"]) != (merge, tag):
1763
1938
  raise ArchiveError(f"member {member} integration differs from the ship commit")
@@ -1796,9 +1971,13 @@ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_he
1796
1971
  archive_milestone.require_git_success(run_git(checkout, "update-ref", "-d", bound_ref, reviewed_head),
1797
1972
  "delete member bound branch")
1798
1973
  run_git(checkout, "update-ref", "-d", f"refs/remotes/origin/{bound}")
1799
- for kind, ref in (("push", bound_ref), ("push", "refs/heads/main"), ("push", f"refs/tags/{tag_name}"),
1974
+ for kind, ref in (("push", bound_ref), ("push", f"refs/heads/{default}"), ("push", f"refs/tags/{tag_name}"),
1800
1975
  ("delete", bound_ref)):
1801
1976
  members.clear_authorization(checkout, project, kind, ref)
1977
+ # The last sidecar retires here at milestone end; isolation's retires also
1978
+ # release the pin whenever the workspace is idle, so a new root can apply.
1979
+ with contextlib.suppress(ValueError, OSError):
1980
+ worktree_paths.release_workspace(checkout)
1802
1981
  return {"member": member, "retired": bound}
1803
1982
 
1804
1983