@opengsd/gsd-path 1.3.0 → 1.4.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 (434) hide show
  1. package/AGENTS.md +2 -207
  2. package/CHANGELOG.md +294 -0
  3. package/DOCS.md +19 -10
  4. package/FULL.md +9 -3
  5. package/GUIDE.md +1 -1
  6. package/HOOKS.md +9 -6
  7. package/QUICK.md +4 -1
  8. package/README.md +63 -11
  9. package/RUNTIME.md +1 -1
  10. package/UPDATE.md +61 -12
  11. package/WORKFLOW.md +22 -6
  12. package/package.json +9 -8
  13. package/scripts/_common.py +472 -19
  14. package/scripts/archive_milestone.py +146 -73
  15. package/scripts/bootstrap_repository.py +8 -5
  16. package/scripts/build_recovery.py +4 -1
  17. package/scripts/build_state.py +66 -23
  18. package/scripts/check_docs_audit.py +14 -3
  19. package/scripts/check_handoffs.py +116 -20
  20. package/scripts/check_task_briefs.py +60 -8
  21. package/scripts/check_update.py +2 -2
  22. package/scripts/core_hook_gate.py +10 -1
  23. package/scripts/core_hook_settings.py +11 -4
  24. package/scripts/detect_project.py +345 -15
  25. package/scripts/discussion_records.py +19 -13
  26. package/scripts/discussion_validate.py +66 -21
  27. package/scripts/dispatch_driver.py +111 -26
  28. package/scripts/git_guard.py +116 -29
  29. package/scripts/guard_hook.py +151 -29
  30. package/scripts/install.mjs +55 -39
  31. package/scripts/install.py +564 -41
  32. package/scripts/integration.py +434 -24
  33. package/scripts/isolation.py +1077 -28
  34. package/scripts/lean_verification.py +99 -19
  35. package/scripts/loop_run.py +20 -30
  36. package/scripts/members.py +704 -0
  37. package/scripts/migrate_core.py +2 -2
  38. package/scripts/path_config.py +2 -2
  39. package/scripts/pipeline_diagnose.py +82 -31
  40. package/scripts/pipeline_git.py +61 -22
  41. package/scripts/pipeline_state.py +176 -54
  42. package/scripts/pipeline_undo.py +117 -22
  43. package/scripts/promote_lookahead.py +1 -1
  44. package/scripts/review_panel.py +1 -1
  45. package/scripts/runtime_store.py +49 -11
  46. package/scripts/skill-resources.json +28 -3
  47. package/scripts/state_checkpoint.py +140 -31
  48. package/scripts/state_promote.py +2 -0
  49. package/scripts/status_runtime.py +36 -6
  50. package/scripts/sync_skill_resources.py +8 -7
  51. package/scripts/token_budget.py +12 -8
  52. package/scripts/workflow_run.py +103 -29
  53. package/scripts/worktree_paths.py +18 -8
  54. package/skills/gsd-path/BUILD.md +7 -1
  55. package/skills/gsd-path/DECIDE.md +11 -4
  56. package/skills/gsd-path/DEFINE.md +8 -1
  57. package/skills/gsd-path/DOCS-AUDIT.md +6 -4
  58. package/skills/gsd-path/INSPECT.md +5 -4
  59. package/skills/gsd-path/PLAN.md +7 -1
  60. package/skills/gsd-path/RESEARCH.md +2 -1
  61. package/skills/gsd-path/ROADMAP.md +2 -1
  62. package/skills/gsd-path/SHIP.md +27 -10
  63. package/skills/gsd-path/SKILL.md +45 -1
  64. package/skills/gsd-path/references/build-native.md +47 -14
  65. package/skills/gsd-path/references/codebase-mapper.md +2 -1
  66. package/skills/gsd-path/references/docs-auditor.md +2 -1
  67. package/skills/gsd-path/references/operating-rules.md +214 -0
  68. package/skills/gsd-path/references/planner.md +8 -0
  69. package/skills/gsd-path/references/reviewer-wave.md +3 -1
  70. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  71. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  72. package/skills/gsd-path/scripts/_common.py +472 -19
  73. package/skills/gsd-path/scripts/archive_milestone.py +146 -73
  74. package/skills/gsd-path/scripts/bootstrap_repository.py +8 -5
  75. package/skills/gsd-path/scripts/build_recovery.py +4 -1
  76. package/skills/gsd-path/scripts/build_state.py +66 -23
  77. package/skills/gsd-path/scripts/check_docs_audit.py +14 -3
  78. package/skills/gsd-path/scripts/check_handoffs.py +116 -20
  79. package/skills/gsd-path/scripts/check_task_briefs.py +60 -8
  80. package/skills/gsd-path/scripts/check_update.py +2 -2
  81. package/skills/gsd-path/scripts/detect_project.py +345 -15
  82. package/skills/gsd-path/scripts/discussion_records.py +19 -13
  83. package/skills/gsd-path/scripts/discussion_validate.py +66 -21
  84. package/skills/gsd-path/scripts/dispatch_driver.py +111 -26
  85. package/skills/gsd-path/scripts/git_guard.py +116 -29
  86. package/skills/gsd-path/scripts/integration.py +434 -24
  87. package/skills/gsd-path/scripts/isolation.py +1077 -28
  88. package/skills/gsd-path/scripts/lean_verification.py +99 -19
  89. package/skills/gsd-path/scripts/loop_run.py +20 -30
  90. package/skills/gsd-path/scripts/members.py +704 -0
  91. package/skills/gsd-path/scripts/path_config.py +2 -2
  92. package/skills/gsd-path/scripts/pipeline_git.py +61 -22
  93. package/skills/gsd-path/scripts/pipeline_state.py +176 -54
  94. package/skills/gsd-path/scripts/promote_lookahead.py +1 -1
  95. package/skills/gsd-path/scripts/review_panel.py +1 -1
  96. package/skills/gsd-path/scripts/state_checkpoint.py +140 -31
  97. package/skills/gsd-path/scripts/state_promote.py +2 -0
  98. package/skills/gsd-path/scripts/status_runtime.py +36 -6
  99. package/skills/gsd-path/scripts/token_budget.py +12 -8
  100. package/skills/gsd-path/scripts/workflow_run.py +103 -29
  101. package/skills/gsd-path/scripts/worktree_paths.py +18 -8
  102. package/skills/gsd-path/templates/codebase.md +1 -1
  103. package/skills/gsd-path/templates/docs-audit.md +1 -1
  104. package/skills/gsd-path/templates/final-review.md +5 -2
  105. package/skills/gsd-path/templates/gap-review.md +2 -1
  106. package/skills/gsd-path/templates/task.md +1 -0
  107. package/skills/gsd-path/templates/wave-review.md +3 -1
  108. package/skills/gsd-path-build/SKILL.md +7 -1
  109. package/skills/gsd-path-build/references/build-native.md +47 -14
  110. package/skills/gsd-path-build/references/operating-rules.md +214 -0
  111. package/skills/gsd-path-build/references/reviewer-wave.md +3 -1
  112. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  113. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  114. package/skills/gsd-path-build/scripts/_common.py +472 -19
  115. package/skills/gsd-path-build/scripts/archive_milestone.py +146 -73
  116. package/skills/gsd-path-build/scripts/build_recovery.py +4 -1
  117. package/skills/gsd-path-build/scripts/build_state.py +66 -23
  118. package/skills/gsd-path-build/scripts/check_handoffs.py +116 -20
  119. package/skills/gsd-path-build/scripts/check_task_briefs.py +60 -8
  120. package/skills/gsd-path-build/scripts/detect_project.py +345 -15
  121. package/skills/gsd-path-build/scripts/discussion_records.py +19 -13
  122. package/skills/gsd-path-build/scripts/discussion_validate.py +66 -21
  123. package/skills/gsd-path-build/scripts/dispatch_driver.py +111 -26
  124. package/skills/gsd-path-build/scripts/integration.py +434 -24
  125. package/skills/gsd-path-build/scripts/isolation.py +1077 -28
  126. package/skills/gsd-path-build/scripts/loop_run.py +20 -30
  127. package/skills/gsd-path-build/scripts/members.py +704 -0
  128. package/skills/gsd-path-build/scripts/path_config.py +2 -2
  129. package/skills/gsd-path-build/scripts/pipeline_git.py +61 -22
  130. package/skills/gsd-path-build/scripts/pipeline_state.py +176 -54
  131. package/skills/gsd-path-build/scripts/promote_lookahead.py +1 -1
  132. package/skills/gsd-path-build/scripts/review_panel.py +1 -1
  133. package/skills/gsd-path-build/scripts/state_checkpoint.py +140 -31
  134. package/skills/gsd-path-build/scripts/state_promote.py +2 -0
  135. package/skills/gsd-path-build/scripts/token_budget.py +12 -8
  136. package/skills/gsd-path-build/scripts/workflow_run.py +103 -29
  137. package/skills/gsd-path-build/scripts/worktree_paths.py +18 -8
  138. package/skills/gsd-path-build/templates/task.md +1 -0
  139. package/skills/gsd-path-build/templates/wave-review.md +3 -1
  140. package/skills/gsd-path-decide/SKILL.md +11 -4
  141. package/skills/gsd-path-decide/references/operating-rules.md +214 -0
  142. package/skills/gsd-path-decide/scripts/_common.py +472 -19
  143. package/skills/gsd-path-decide/scripts/archive_milestone.py +146 -73
  144. package/skills/gsd-path-decide/scripts/build_recovery.py +4 -1
  145. package/skills/gsd-path-decide/scripts/check_handoffs.py +116 -20
  146. package/skills/gsd-path-decide/scripts/check_task_briefs.py +60 -8
  147. package/skills/gsd-path-decide/scripts/detect_project.py +345 -15
  148. package/skills/gsd-path-decide/scripts/discussion_records.py +19 -13
  149. package/skills/gsd-path-decide/scripts/discussion_validate.py +66 -21
  150. package/skills/gsd-path-decide/scripts/integration.py +434 -24
  151. package/skills/gsd-path-decide/scripts/isolation.py +1077 -28
  152. package/skills/gsd-path-decide/scripts/loop_run.py +20 -30
  153. package/skills/gsd-path-decide/scripts/members.py +704 -0
  154. package/skills/gsd-path-decide/scripts/path_config.py +2 -2
  155. package/skills/gsd-path-decide/scripts/pipeline_git.py +61 -22
  156. package/skills/gsd-path-decide/scripts/pipeline_state.py +176 -54
  157. package/skills/gsd-path-decide/scripts/promote_lookahead.py +1 -1
  158. package/skills/gsd-path-decide/scripts/review_panel.py +1 -1
  159. package/skills/gsd-path-decide/scripts/state_checkpoint.py +140 -31
  160. package/skills/gsd-path-decide/scripts/state_promote.py +2 -0
  161. package/skills/gsd-path-decide/scripts/worktree_paths.py +18 -8
  162. package/skills/gsd-path-define/SKILL.md +8 -1
  163. package/skills/gsd-path-define/references/operating-rules.md +214 -0
  164. package/skills/gsd-path-define/scripts/_common.py +472 -19
  165. package/skills/gsd-path-define/scripts/archive_milestone.py +146 -73
  166. package/skills/gsd-path-define/scripts/build_recovery.py +4 -1
  167. package/skills/gsd-path-define/scripts/check_handoffs.py +116 -20
  168. package/skills/gsd-path-define/scripts/check_task_briefs.py +60 -8
  169. package/skills/gsd-path-define/scripts/detect_project.py +345 -15
  170. package/skills/gsd-path-define/scripts/discussion_records.py +19 -13
  171. package/skills/gsd-path-define/scripts/discussion_validate.py +66 -21
  172. package/skills/gsd-path-define/scripts/integration.py +434 -24
  173. package/skills/gsd-path-define/scripts/isolation.py +1077 -28
  174. package/skills/gsd-path-define/scripts/loop_run.py +20 -30
  175. package/skills/gsd-path-define/scripts/members.py +704 -0
  176. package/skills/gsd-path-define/scripts/path_config.py +2 -2
  177. package/skills/gsd-path-define/scripts/pipeline_git.py +61 -22
  178. package/skills/gsd-path-define/scripts/pipeline_state.py +176 -54
  179. package/skills/gsd-path-define/scripts/promote_lookahead.py +1 -1
  180. package/skills/gsd-path-define/scripts/review_panel.py +1 -1
  181. package/skills/gsd-path-define/scripts/state_checkpoint.py +140 -31
  182. package/skills/gsd-path-define/scripts/state_promote.py +2 -0
  183. package/skills/gsd-path-define/scripts/worktree_paths.py +18 -8
  184. package/skills/gsd-path-discuss/SKILL.md +2 -1
  185. package/skills/gsd-path-discuss/references/operating-rules.md +214 -0
  186. package/skills/gsd-path-discuss/scripts/_common.py +472 -19
  187. package/skills/gsd-path-discuss/scripts/discussion_records.py +19 -13
  188. package/skills/gsd-path-discuss/scripts/loop_run.py +20 -30
  189. package/skills/gsd-path-discuss/scripts/path_config.py +2 -2
  190. package/skills/gsd-path-discuss/scripts/review_panel.py +1 -1
  191. package/skills/gsd-path-discuss/scripts/worktree_paths.py +18 -8
  192. package/skills/gsd-path-docs-audit/SKILL.md +6 -4
  193. package/skills/gsd-path-docs-audit/references/docs-auditor.md +2 -1
  194. package/skills/gsd-path-docs-audit/references/operating-rules.md +214 -0
  195. package/skills/gsd-path-docs-audit/scripts/_common.py +472 -19
  196. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +146 -73
  197. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +4 -1
  198. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +14 -3
  199. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +116 -20
  200. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +60 -8
  201. package/skills/gsd-path-docs-audit/scripts/detect_project.py +345 -15
  202. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +19 -13
  203. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +66 -21
  204. package/skills/gsd-path-docs-audit/scripts/integration.py +434 -24
  205. package/skills/gsd-path-docs-audit/scripts/isolation.py +1077 -28
  206. package/skills/gsd-path-docs-audit/scripts/loop_run.py +20 -30
  207. package/skills/gsd-path-docs-audit/scripts/members.py +704 -0
  208. package/skills/gsd-path-docs-audit/scripts/path_config.py +2 -2
  209. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +61 -22
  210. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +176 -54
  211. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +1 -1
  212. package/skills/gsd-path-docs-audit/scripts/review_panel.py +1 -1
  213. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +140 -31
  214. package/skills/gsd-path-docs-audit/scripts/state_promote.py +2 -0
  215. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +18 -8
  216. package/skills/gsd-path-docs-audit/templates/docs-audit.md +1 -1
  217. package/skills/gsd-path-forensics/SKILL.md +2 -1
  218. package/skills/gsd-path-forensics/references/operating-rules.md +214 -0
  219. package/skills/gsd-path-forensics/scripts/_common.py +472 -19
  220. package/skills/gsd-path-forensics/scripts/discussion_records.py +19 -13
  221. package/skills/gsd-path-forensics/scripts/loop_run.py +20 -30
  222. package/skills/gsd-path-forensics/scripts/path_config.py +2 -2
  223. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +82 -31
  224. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +117 -22
  225. package/skills/gsd-path-forensics/scripts/review_panel.py +1 -1
  226. package/skills/gsd-path-forensics/scripts/worktree_paths.py +18 -8
  227. package/skills/gsd-path-inspect/SKILL.md +5 -4
  228. package/skills/gsd-path-inspect/references/codebase-mapper.md +2 -1
  229. package/skills/gsd-path-inspect/references/docs-auditor.md +2 -1
  230. package/skills/gsd-path-inspect/references/operating-rules.md +214 -0
  231. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  232. package/skills/gsd-path-inspect/scripts/_common.py +472 -19
  233. package/skills/gsd-path-inspect/scripts/archive_milestone.py +146 -73
  234. package/skills/gsd-path-inspect/scripts/build_recovery.py +4 -1
  235. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +14 -3
  236. package/skills/gsd-path-inspect/scripts/check_handoffs.py +116 -20
  237. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +60 -8
  238. package/skills/gsd-path-inspect/scripts/detect_project.py +345 -15
  239. package/skills/gsd-path-inspect/scripts/discussion_records.py +19 -13
  240. package/skills/gsd-path-inspect/scripts/discussion_validate.py +66 -21
  241. package/skills/gsd-path-inspect/scripts/integration.py +434 -24
  242. package/skills/gsd-path-inspect/scripts/isolation.py +1077 -28
  243. package/skills/gsd-path-inspect/scripts/loop_run.py +20 -30
  244. package/skills/gsd-path-inspect/scripts/members.py +704 -0
  245. package/skills/gsd-path-inspect/scripts/path_config.py +2 -2
  246. package/skills/gsd-path-inspect/scripts/pipeline_git.py +61 -22
  247. package/skills/gsd-path-inspect/scripts/pipeline_state.py +176 -54
  248. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +1 -1
  249. package/skills/gsd-path-inspect/scripts/review_panel.py +1 -1
  250. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +140 -31
  251. package/skills/gsd-path-inspect/scripts/state_promote.py +2 -0
  252. package/skills/gsd-path-inspect/scripts/workflow_run.py +103 -29
  253. package/skills/gsd-path-inspect/scripts/worktree_paths.py +18 -8
  254. package/skills/gsd-path-inspect/templates/codebase.md +1 -1
  255. package/skills/gsd-path-inspect/templates/docs-audit.md +1 -1
  256. package/skills/gsd-path-loop/SKILL.md +2 -1
  257. package/skills/gsd-path-loop/references/operating-rules.md +214 -0
  258. package/skills/gsd-path-loop/scripts/_common.py +472 -19
  259. package/skills/gsd-path-loop/scripts/discussion_records.py +19 -13
  260. package/skills/gsd-path-loop/scripts/loop_run.py +20 -30
  261. package/skills/gsd-path-loop/scripts/path_config.py +2 -2
  262. package/skills/gsd-path-loop/scripts/review_panel.py +1 -1
  263. package/skills/gsd-path-loop/scripts/worktree_paths.py +18 -8
  264. package/skills/gsd-path-migrate/SKILL.md +2 -1
  265. package/skills/gsd-path-migrate/references/operating-rules.md +214 -0
  266. package/skills/gsd-path-migrate/scripts/_common.py +472 -19
  267. package/skills/gsd-path-migrate/scripts/core_hook_gate.py +10 -1
  268. package/skills/gsd-path-migrate/scripts/core_hook_settings.py +11 -4
  269. package/skills/gsd-path-migrate/scripts/discussion_records.py +19 -13
  270. package/skills/gsd-path-migrate/scripts/loop_run.py +20 -30
  271. package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -2
  272. package/skills/gsd-path-migrate/scripts/path_config.py +2 -2
  273. package/skills/gsd-path-migrate/scripts/review_panel.py +1 -1
  274. package/skills/gsd-path-plan/SKILL.md +7 -1
  275. package/skills/gsd-path-plan/references/operating-rules.md +214 -0
  276. package/skills/gsd-path-plan/references/planner.md +8 -0
  277. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  278. package/skills/gsd-path-plan/scripts/_common.py +472 -19
  279. package/skills/gsd-path-plan/scripts/archive_milestone.py +146 -73
  280. package/skills/gsd-path-plan/scripts/build_recovery.py +4 -1
  281. package/skills/gsd-path-plan/scripts/check_handoffs.py +116 -20
  282. package/skills/gsd-path-plan/scripts/check_task_briefs.py +60 -8
  283. package/skills/gsd-path-plan/scripts/detect_project.py +345 -15
  284. package/skills/gsd-path-plan/scripts/discussion_records.py +19 -13
  285. package/skills/gsd-path-plan/scripts/discussion_validate.py +66 -21
  286. package/skills/gsd-path-plan/scripts/integration.py +434 -24
  287. package/skills/gsd-path-plan/scripts/isolation.py +1077 -28
  288. package/skills/gsd-path-plan/scripts/loop_run.py +20 -30
  289. package/skills/gsd-path-plan/scripts/members.py +704 -0
  290. package/skills/gsd-path-plan/scripts/path_config.py +2 -2
  291. package/skills/gsd-path-plan/scripts/pipeline_git.py +61 -22
  292. package/skills/gsd-path-plan/scripts/pipeline_state.py +176 -54
  293. package/skills/gsd-path-plan/scripts/promote_lookahead.py +1 -1
  294. package/skills/gsd-path-plan/scripts/review_panel.py +1 -1
  295. package/skills/gsd-path-plan/scripts/state_checkpoint.py +140 -31
  296. package/skills/gsd-path-plan/scripts/state_promote.py +2 -0
  297. package/skills/gsd-path-plan/scripts/workflow_run.py +103 -29
  298. package/skills/gsd-path-plan/scripts/worktree_paths.py +18 -8
  299. package/skills/gsd-path-plan/templates/task.md +1 -0
  300. package/skills/gsd-path-research/SKILL.md +2 -1
  301. package/skills/gsd-path-research/references/operating-rules.md +214 -0
  302. package/skills/gsd-path-research/scripts/_common.py +472 -19
  303. package/skills/gsd-path-research/scripts/archive_milestone.py +146 -73
  304. package/skills/gsd-path-research/scripts/build_recovery.py +4 -1
  305. package/skills/gsd-path-research/scripts/check_handoffs.py +116 -20
  306. package/skills/gsd-path-research/scripts/check_task_briefs.py +60 -8
  307. package/skills/gsd-path-research/scripts/detect_project.py +345 -15
  308. package/skills/gsd-path-research/scripts/discussion_records.py +19 -13
  309. package/skills/gsd-path-research/scripts/discussion_validate.py +66 -21
  310. package/skills/gsd-path-research/scripts/integration.py +434 -24
  311. package/skills/gsd-path-research/scripts/isolation.py +1077 -28
  312. package/skills/gsd-path-research/scripts/loop_run.py +20 -30
  313. package/skills/gsd-path-research/scripts/members.py +704 -0
  314. package/skills/gsd-path-research/scripts/path_config.py +2 -2
  315. package/skills/gsd-path-research/scripts/pipeline_git.py +61 -22
  316. package/skills/gsd-path-research/scripts/pipeline_state.py +176 -54
  317. package/skills/gsd-path-research/scripts/promote_lookahead.py +1 -1
  318. package/skills/gsd-path-research/scripts/review_panel.py +1 -1
  319. package/skills/gsd-path-research/scripts/state_checkpoint.py +140 -31
  320. package/skills/gsd-path-research/scripts/state_promote.py +2 -0
  321. package/skills/gsd-path-research/scripts/worktree_paths.py +18 -8
  322. package/skills/gsd-path-roadmap/SKILL.md +2 -1
  323. package/skills/gsd-path-roadmap/references/operating-rules.md +214 -0
  324. package/skills/gsd-path-roadmap/scripts/_common.py +472 -19
  325. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +146 -73
  326. package/skills/gsd-path-roadmap/scripts/build_recovery.py +4 -1
  327. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +116 -20
  328. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +60 -8
  329. package/skills/gsd-path-roadmap/scripts/detect_project.py +345 -15
  330. package/skills/gsd-path-roadmap/scripts/discussion_records.py +19 -13
  331. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +66 -21
  332. package/skills/gsd-path-roadmap/scripts/integration.py +434 -24
  333. package/skills/gsd-path-roadmap/scripts/isolation.py +1077 -28
  334. package/skills/gsd-path-roadmap/scripts/loop_run.py +20 -30
  335. package/skills/gsd-path-roadmap/scripts/members.py +704 -0
  336. package/skills/gsd-path-roadmap/scripts/path_config.py +2 -2
  337. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +61 -22
  338. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +176 -54
  339. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +1 -1
  340. package/skills/gsd-path-roadmap/scripts/review_panel.py +1 -1
  341. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +140 -31
  342. package/skills/gsd-path-roadmap/scripts/state_promote.py +2 -0
  343. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +18 -8
  344. package/skills/gsd-path-ship/SKILL.md +27 -10
  345. package/skills/gsd-path-ship/references/operating-rules.md +214 -0
  346. package/skills/gsd-path-ship/references/reviewer-wave.md +3 -1
  347. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  348. package/skills/gsd-path-ship/scripts/_common.py +472 -19
  349. package/skills/gsd-path-ship/scripts/archive_milestone.py +146 -73
  350. package/skills/gsd-path-ship/scripts/build_recovery.py +4 -1
  351. package/skills/gsd-path-ship/scripts/build_state.py +66 -23
  352. package/skills/gsd-path-ship/scripts/check_handoffs.py +116 -20
  353. package/skills/gsd-path-ship/scripts/check_task_briefs.py +60 -8
  354. package/skills/gsd-path-ship/scripts/detect_project.py +345 -15
  355. package/skills/gsd-path-ship/scripts/discussion_records.py +19 -13
  356. package/skills/gsd-path-ship/scripts/discussion_validate.py +66 -21
  357. package/skills/gsd-path-ship/scripts/integration.py +434 -24
  358. package/skills/gsd-path-ship/scripts/isolation.py +1077 -28
  359. package/skills/gsd-path-ship/scripts/lean_verification.py +99 -19
  360. package/skills/gsd-path-ship/scripts/loop_run.py +20 -30
  361. package/skills/gsd-path-ship/scripts/members.py +704 -0
  362. package/skills/gsd-path-ship/scripts/path_config.py +2 -2
  363. package/skills/gsd-path-ship/scripts/pipeline_git.py +61 -22
  364. package/skills/gsd-path-ship/scripts/pipeline_state.py +176 -54
  365. package/skills/gsd-path-ship/scripts/promote_lookahead.py +1 -1
  366. package/skills/gsd-path-ship/scripts/review_panel.py +1 -1
  367. package/skills/gsd-path-ship/scripts/state_checkpoint.py +140 -31
  368. package/skills/gsd-path-ship/scripts/state_promote.py +2 -0
  369. package/skills/gsd-path-ship/scripts/workflow_run.py +103 -29
  370. package/skills/gsd-path-ship/scripts/worktree_paths.py +18 -8
  371. package/skills/gsd-path-ship/templates/final-review.md +5 -2
  372. package/skills/gsd-path-ship/templates/gap-review.md +2 -1
  373. package/skills/gsd-path-ship/templates/wave-review.md +3 -1
  374. package/skills/gsd-path-undo/SKILL.md +2 -1
  375. package/skills/gsd-path-undo/references/operating-rules.md +214 -0
  376. package/skills/gsd-path-undo/scripts/_common.py +472 -19
  377. package/skills/gsd-path-undo/scripts/discussion_records.py +19 -13
  378. package/skills/gsd-path-undo/scripts/loop_run.py +20 -30
  379. package/skills/gsd-path-undo/scripts/path_config.py +2 -2
  380. package/skills/gsd-path-undo/scripts/pipeline_undo.py +117 -22
  381. package/skills/gsd-path-undo/scripts/review_panel.py +1 -1
  382. package/skills/gsd-path-undo/scripts/worktree_paths.py +18 -8
  383. package/skills/path/BUILD.md +7 -1
  384. package/skills/path/DECIDE.md +11 -4
  385. package/skills/path/DEFINE.md +8 -1
  386. package/skills/path/DOCS-AUDIT.md +6 -4
  387. package/skills/path/INSPECT.md +5 -4
  388. package/skills/path/PLAN.md +7 -1
  389. package/skills/path/RESEARCH.md +2 -1
  390. package/skills/path/ROADMAP.md +2 -1
  391. package/skills/path/SHIP.md +27 -10
  392. package/skills/path/SKILL.md +45 -1
  393. package/skills/path/references/build-native.md +47 -14
  394. package/skills/path/references/codebase-mapper.md +2 -1
  395. package/skills/path/references/docs-auditor.md +2 -1
  396. package/skills/path/references/operating-rules.md +214 -0
  397. package/skills/path/references/planner.md +8 -0
  398. package/skills/path/references/reviewer-wave.md +3 -1
  399. package/skills/path/scripts/_common.py +472 -19
  400. package/skills/path/scripts/archive_milestone.py +146 -73
  401. package/skills/path/scripts/bootstrap_repository.py +8 -5
  402. package/skills/path/scripts/build_recovery.py +4 -1
  403. package/skills/path/scripts/build_state.py +66 -23
  404. package/skills/path/scripts/check_docs_audit.py +14 -3
  405. package/skills/path/scripts/check_handoffs.py +116 -20
  406. package/skills/path/scripts/check_task_briefs.py +60 -8
  407. package/skills/path/scripts/check_update.py +2 -2
  408. package/skills/path/scripts/detect_project.py +345 -15
  409. package/skills/path/scripts/discussion_records.py +19 -13
  410. package/skills/path/scripts/discussion_validate.py +66 -21
  411. package/skills/path/scripts/dispatch_driver.py +111 -26
  412. package/skills/path/scripts/git_guard.py +116 -29
  413. package/skills/path/scripts/integration.py +434 -24
  414. package/skills/path/scripts/isolation.py +1077 -28
  415. package/skills/path/scripts/lean_verification.py +99 -19
  416. package/skills/path/scripts/loop_run.py +20 -30
  417. package/skills/path/scripts/members.py +704 -0
  418. package/skills/path/scripts/path_config.py +2 -2
  419. package/skills/path/scripts/pipeline_git.py +61 -22
  420. package/skills/path/scripts/pipeline_state.py +176 -54
  421. package/skills/path/scripts/promote_lookahead.py +1 -1
  422. package/skills/path/scripts/review_panel.py +1 -1
  423. package/skills/path/scripts/state_checkpoint.py +140 -31
  424. package/skills/path/scripts/state_promote.py +2 -0
  425. package/skills/path/scripts/status_runtime.py +36 -6
  426. package/skills/path/scripts/token_budget.py +12 -8
  427. package/skills/path/scripts/workflow_run.py +103 -29
  428. package/skills/path/scripts/worktree_paths.py +18 -8
  429. package/skills/path/templates/codebase.md +1 -1
  430. package/skills/path/templates/docs-audit.md +1 -1
  431. package/skills/path/templates/final-review.md +5 -2
  432. package/skills/path/templates/gap-review.md +2 -1
  433. package/skills/path/templates/task.md +1 -0
  434. package/skills/path/templates/wave-review.md +3 -1
@@ -12,23 +12,8 @@ import re
12
12
  from pathlib import Path, PurePosixPath
13
13
  from typing import Optional
14
14
 
15
- try:
16
- from pipeline_git import (
17
- bound_branch_name,
18
- default_branch_name,
19
- integrate_commit_body,
20
- integrate_subject,
21
- is_bound_branch,
22
- milestone_id,
23
- milestone_number,
24
- ship_subject,
25
- )
26
- from pipeline_state import PipelineState
27
- from isolation import publication_base
28
- import _common
29
- import worktree_paths
30
- except ImportError: # pragma: no cover - package import used by tests
31
- from scripts.pipeline_git import (
15
+ if __package__:
16
+ from .pipeline_git import (
32
17
  bound_branch_name,
33
18
  default_branch_name,
34
19
  integrate_commit_body,
@@ -38,10 +23,41 @@ except ImportError: # pragma: no cover - package import used by tests
38
23
  milestone_number,
39
24
  ship_subject,
40
25
  )
41
- from scripts.pipeline_state import PipelineState
42
- from scripts.isolation import publication_base
43
- from scripts import _common
44
- from scripts import worktree_paths
26
+ from .pipeline_state import PipelineState
27
+ from .isolation import publication_base
28
+ from . import _common
29
+ from . import worktree_paths
30
+ else:
31
+ try:
32
+ from pipeline_git import (
33
+ bound_branch_name,
34
+ default_branch_name,
35
+ integrate_commit_body,
36
+ integrate_subject,
37
+ is_bound_branch,
38
+ milestone_id,
39
+ milestone_number,
40
+ ship_subject,
41
+ )
42
+ from pipeline_state import PipelineState
43
+ from isolation import publication_base
44
+ import _common
45
+ import worktree_paths
46
+ except ImportError: # pragma: no cover - package import used by tests
47
+ from scripts.pipeline_git import (
48
+ bound_branch_name,
49
+ default_branch_name,
50
+ integrate_commit_body,
51
+ integrate_subject,
52
+ is_bound_branch,
53
+ milestone_id,
54
+ milestone_number,
55
+ ship_subject,
56
+ )
57
+ from scripts.pipeline_state import PipelineState
58
+ from scripts.isolation import publication_base
59
+ from scripts import _common
60
+ from scripts import worktree_paths
45
61
 
46
62
 
47
63
  if __package__:
@@ -541,8 +557,8 @@ def ensure_integration_tag(
541
557
 
542
558
  def github_repository(project: Path) -> str:
543
559
  remote = archive_milestone.require_git_success(
544
- run_git(project, "remote", "get-url", "origin"),
545
- "resolve origin URL",
560
+ run_git(project, "config", "--get", "remote.origin.url"),
561
+ "read configured origin URL",
546
562
  )
547
563
  patterns = (
548
564
  r"https://github\.com/([^/]+/[^/]+?)(?:\.git)?$",
@@ -701,8 +717,9 @@ def update_pull_request_body(
701
717
  archive_path: str,
702
718
  ship_commit: str,
703
719
  branch: str,
720
+ expected_body: Optional[str] = None,
704
721
  ) -> dict:
705
- expected = pull_request_body(archive_path, ship_commit, branch)
722
+ expected = expected_body if expected_body is not None else pull_request_body(archive_path, ship_commit, branch)
706
723
  current = pull.get("body")
707
724
  if isinstance(current, str) and current.rstrip().endswith(PR_CREDIT_LINE):
708
725
  return pull
@@ -1394,6 +1411,12 @@ def validate_integrated(
1394
1411
  raise ArchiveError(f"published {ref} differs from local integration evidence")
1395
1412
  if pull_request is not None:
1396
1413
  result["pull_request"] = pull_request
1414
+ for row in shipped.get("members", []):
1415
+ proven = validate_member_integrated(project, row["name"], configured, row["reviewed_head"], refresh=refresh)
1416
+ if (proven["merge"], proven["tag"]) != (row["merge"], row["tag"]):
1417
+ raise ArchiveError(f"member {row['name']} integration differs from the ship commit")
1418
+ if "members" in shipped:
1419
+ result["members"] = shipped["members"]
1397
1420
  return result
1398
1421
 
1399
1422
 
@@ -1457,3 +1480,390 @@ def require_published_integration(
1457
1480
  raise ArchiveError(
1458
1481
  f"published milestone tag {tag_name} does not point at the integration merge"
1459
1482
  )
1483
+
1484
+
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."""
1487
+ try:
1488
+ import isolation
1489
+ except ImportError: # pragma: no cover - package import used by tests
1490
+ from scripts import isolation
1491
+ try:
1492
+ checkout, project = isolation.member_checkout(coordinator.resolve(), member)
1493
+ except isolation.IsolationError as error:
1494
+ raise ArchiveError(str(error)) from error
1495
+ archive_name = PurePosixPath(archive_path).name
1496
+ identifier = milestone_id(milestone_number(archive_name))
1497
+ 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}"
1500
+
1501
+
1502
+ def _member_merge_body(archive_path: str, member: str, reviewed_head: str) -> str:
1503
+ return f"Archive: {archive_path}\nMember: {member}\nReviewed-HEAD: {reviewed_head}"
1504
+
1505
+
1506
+ def member_pull_request_tag_message(tag_name: str, pull_request: str, reviewed_head: str, landing: str) -> str:
1507
+ return (f"milestone {tag_name.removeprefix('milestone/')}\n\nMode: pull-request\n"
1508
+ f"Pull-Request: {pull_request}\nReviewed-HEAD: {reviewed_head}\nLanding: {landing}")
1509
+
1510
+
1511
+ def member_pull_request_tag_metadata(contents: str) -> dict[str, str]:
1512
+ fields: dict[str, str] = {}
1513
+ for line in contents.splitlines():
1514
+ match = re.fullmatch(r"(Mode|Pull-Request|Reviewed-HEAD|Landing): (.+)", line)
1515
+ if match:
1516
+ if match.group(1) in fields:
1517
+ raise ArchiveError("pull-request member tag repeats metadata")
1518
+ fields[match.group(1)] = match.group(2)
1519
+ if set(fields) != {"Mode", "Pull-Request", "Reviewed-HEAD", "Landing"}:
1520
+ raise ArchiveError("pull-request member tag is missing metadata")
1521
+ if fields["Mode"] != "pull-request":
1522
+ raise ArchiveError("pull-request member tag has the wrong mode")
1523
+ return fields
1524
+
1525
+
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."""
1528
+ 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",
1531
+ )
1532
+ matches = [commit for commit, _, text in (line.partition("\x00") for line in log.splitlines())
1533
+ if text == subject]
1534
+ if len(matches) > 1:
1535
+ raise ArchiveError(f"multiple member merges named {subject!r} on origin/main")
1536
+ if not matches:
1537
+ return None
1538
+ _require_member_merge(checkout, matches[0], subject, body, reviewed_head)
1539
+ return matches[0]
1540
+
1541
+
1542
+ def _require_member_merge(checkout: Path, commit: str, subject: str, body: str, reviewed_head: str) -> str:
1543
+ """Check a canonical member merge; return its first parent."""
1544
+ archive_milestone.require_canonical_commit_body(checkout, commit, subject, body, "member integration")
1545
+ parents = archive_milestone.require_git_success(
1546
+ run_git(checkout, "rev-list", "--parents", "-n", "1", commit), "inspect member merge parents"
1547
+ ).split()
1548
+ if len(parents) != 3 or parents[2] != reviewed_head:
1549
+ raise ArchiveError("member merge second parent is not the member reviewed HEAD")
1550
+ return parents[1]
1551
+
1552
+
1553
+ def _create_member_merge(checkout: Path, project: str, archive_path: str, subject: str, body: str,
1554
+ reviewed_head: str, origin_main: str) -> str:
1555
+ identifier = milestone_id(milestone_number(PurePosixPath(archive_path).name))
1556
+ branch = f"gsd-path-integrate/{project}-{identifier}"
1557
+ try:
1558
+ worktree = worktree_paths.worktree_path(checkout, "integrate", f"{project}-{identifier}", pin=True)
1559
+ except (ValueError, OSError) as error:
1560
+ raise ArchiveError(str(error)) from error
1561
+ interrupted = registered_worktree(checkout, branch)
1562
+ if interrupted is not None and optional_ref(interrupted, "MERGE_HEAD") is not None:
1563
+ archive_milestone.require_git_success(run_git(interrupted, "merge", "--abort"),
1564
+ "abort interrupted member merge")
1565
+ remove_registered_worktree(checkout, branch, worktree)
1566
+ tip = optional_ref(checkout, f"refs/heads/{branch}")
1567
+ if tip is not None:
1568
+ base_tip = run_git(checkout, "merge-base", "--is-ancestor", tip, origin_main)
1569
+ if base_tip.returncode != 0:
1570
+ if base_tip.returncode != 1:
1571
+ archive_milestone.require_git_success(base_tip, "inspect interrupted member integration base")
1572
+ if _require_member_merge(checkout, tip, subject, body, reviewed_head) == origin_main:
1573
+ return tip
1574
+ delete_integration_branch(checkout, branch, tip)
1575
+ archive_milestone.require_git_success(
1576
+ run_git(checkout, "worktree", "add", "--quiet", "-b", branch, str(worktree), origin_main),
1577
+ "create member integration worktree",
1578
+ )
1579
+ merged = run_git(worktree, "merge", "--no-ff", "-m", subject, "-m", body, reviewed_head)
1580
+ if merged.returncode != 0:
1581
+ conflicts = run_git(worktree, "diff", "--name-only", "--diff-filter=U").stdout.split()
1582
+ if optional_ref(worktree, "MERGE_HEAD") is not None:
1583
+ run_git(worktree, "merge", "--abort")
1584
+ remove_registered_worktree(checkout, branch, worktree)
1585
+ delete_integration_branch(checkout, branch, origin_main)
1586
+ raise ArchiveError("member integration merge failed; Git aborted it without resolving files: "
1587
+ + (", ".join(conflicts) or (merged.stderr or merged.stdout).strip()))
1588
+ merge = archive_milestone.require_git_success(run_git(worktree, "rev-parse", "HEAD"), "resolve member merge")
1589
+ if _require_member_merge(worktree, merge, subject, body, reviewed_head) != origin_main:
1590
+ raise ArchiveError("member merge first parent is not origin/main")
1591
+ remove_registered_worktree(checkout, branch, worktree)
1592
+ return merge
1593
+
1594
+
1595
+ def _push_member_ref(checkout: Path, project: str, source: str, ref: str, sha: str, lease: Optional[str]) -> None:
1596
+ """Authorize `ref` at `sha` for the member pre-push hook, then publish it."""
1597
+ try:
1598
+ import members
1599
+ except ImportError: # pragma: no cover - package import used by tests
1600
+ from scripts import members
1601
+ members.authorize_push(checkout, project, ref, sha)
1602
+ arguments = ["push"] + ([f"--force-with-lease={ref}:{lease}"] if lease is not None else [])
1603
+ archive_milestone.require_git_success(
1604
+ run_git(checkout, *arguments, "origin", f"{source}:{ref}"), f"publish member {ref}"
1605
+ )
1606
+
1607
+
1608
+ def _publish_member_bound(checkout: Path, project: str, member: str, bound: str, reviewed_head: str) -> None:
1609
+ bound_ref = f"refs/heads/{bound}"
1610
+ remote_bound = live_remote_ref(checkout, bound_ref)
1611
+ if remote_bound is None:
1612
+ _push_member_ref(checkout, project, reviewed_head, bound_ref, reviewed_head, "")
1613
+ elif remote_bound != reviewed_head:
1614
+ raise ArchiveError(f"member {member} origin/{bound} moved: {remote_bound}")
1615
+
1616
+
1617
+ def _require_member_close(checkout: Path, member: str, bound: str, reviewed_head: str) -> None:
1618
+ if optional_ref(checkout, f"refs/heads/{bound}") != reviewed_head:
1619
+ 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")
1622
+
1623
+
1624
+ 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)
1627
+ 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)
1630
+ if merge is None:
1631
+ origin_main = archive_milestone.require_git_success(
1632
+ run_git(checkout, "rev-parse", "origin/main"), "resolve member origin/main")
1633
+ ancestry = run_git(checkout, "merge-base", "--is-ancestor", reviewed_head, origin_main)
1634
+ if ancestry.returncode == 0:
1635
+ raise ArchiveError("member reviewed HEAD reached origin/main outside Path; user-approved recovery required")
1636
+ if ancestry.returncode != 1:
1637
+ archive_milestone.require_git_success(ancestry, "inspect member reviewed HEAD ancestry")
1638
+ 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.
1640
+ _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)
1643
+ archive_milestone.require_git_success(
1644
+ run_git(checkout, "update-ref", "refs/remotes/origin/main", merge), "refresh member origin/main")
1645
+ tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge)
1646
+ if not tag_published:
1647
+ _push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
1648
+ archive_milestone.require_git_success(
1649
+ run_git(checkout, "update-ref", f"refs/remotes/origin/tags/{tag_name}", tag_object),
1650
+ "refresh member milestone-tag ref")
1651
+ identifier = milestone_id(milestone_number(PurePosixPath(archive_path).name))
1652
+ delete_integration_branch(checkout, f"gsd-path-integrate/{project}-{identifier}", merge)
1653
+ return validate_member_integrated(coordinator, member, archive_path, reviewed_head)
1654
+
1655
+
1656
+ def integrate_member_pull_request(coordinator: Path, member: str, archive_path: str, reviewed_head: str) -> dict:
1657
+ """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)
1659
+ body = _member_merge_body(archive_path, member, reviewed_head)
1660
+ _require_member_close(checkout, member, bound, reviewed_head)
1661
+ require_github_authentication()
1662
+ repository = github_repository(checkout)
1663
+ pull = find_pull_request(repository, bound, reviewed_head)
1664
+ if pull is None or pull["merged_at"] is None:
1665
+ if pull is not None and pull["state"] != "open":
1666
+ raise ArchiveError(f"member {member} pull request was closed without merging")
1667
+ _publish_member_bound(checkout, project, member, bound, reviewed_head)
1668
+ if pull is None:
1669
+ pull = require_pull_request_shape(github_api_json(
1670
+ 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)
1673
+ else:
1674
+ pull = update_pull_request_body(
1675
+ repository, pull, archive_path, reviewed_head, bound,
1676
+ f"{body}\n\n---\n{PR_CREDIT_LINE}",
1677
+ )
1678
+ require_pull_request_merge_provenance(repository, pull["number"])
1679
+ return {"status": "awaiting-merge", "mode": "pull-request", "member": member,
1680
+ "pull_request": pull["html_url"]}
1681
+ merge = pull["merge_commit_sha"]
1682
+ if pull["state"] != "closed" or not isinstance(merge, str):
1683
+ raise ArchiveError(f"member {member} pull request merge metadata is invalid")
1684
+ require_pull_request_merge_provenance(repository, pull["number"], merge)
1685
+ refresh_origin(checkout)
1686
+ require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
1687
+ pull = update_pull_request_body(
1688
+ repository, pull, archive_path, reviewed_head, bound,
1689
+ f"{body}\n\n---\n{PR_CREDIT_LINE}",
1690
+ )
1691
+ message = member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge)
1692
+ tag_object, tag_published = ensure_integration_tag(checkout, tag_name, merge, message)
1693
+ if not tag_published:
1694
+ _push_member_ref(checkout, project, f"refs/tags/{tag_name}", f"refs/tags/{tag_name}", tag_object, None)
1695
+ archive_milestone.require_git_success(
1696
+ run_git(checkout, "update-ref", f"refs/remotes/origin/tags/{tag_name}", tag_object),
1697
+ "refresh member milestone-tag ref")
1698
+ return validate_member_integrated(coordinator, member, archive_path, reviewed_head)
1699
+
1700
+
1701
+ def validate_member_integrated(
1702
+ coordinator: Path, member: str, archive_path: str, reviewed_head: str, *, refresh: bool = True
1703
+ ) -> 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)
1706
+ tracking = f"refs/remotes/origin/tags/{tag_name}"
1707
+ if refresh:
1708
+ refresh_origin(checkout)
1709
+ published = live_remote_ref(checkout, f"refs/tags/{tag_name}")
1710
+ else:
1711
+ published = optional_ref(checkout, tracking)
1712
+ contents = ""
1713
+ if refresh and published is not None:
1714
+ archive_milestone.require_git_success(
1715
+ run_git(checkout, "update-ref", tracking, published), "refresh member milestone-tag ref")
1716
+ if published is not None:
1717
+ contents = run_git(checkout, "for-each-ref", "--format=%(contents)", tracking).stdout
1718
+ if "Mode: pull-request" in contents.splitlines():
1719
+ metadata = member_pull_request_tag_metadata(contents)
1720
+ if metadata["Reviewed-HEAD"] != reviewed_head:
1721
+ raise ArchiveError(f"member {member} tag {tag_name} names a different reviewed HEAD")
1722
+ merge = archive_milestone.require_git_success(
1723
+ run_git(checkout, "rev-parse", f"{tracking}^{{commit}}"), "resolve member tag target")
1724
+ if metadata["Landing"] != merge:
1725
+ raise ArchiveError(f"member {member} tag {tag_name} names a different landing")
1726
+ require_github_authentication()
1727
+ repository = github_repository(checkout)
1728
+ pull = find_pull_request(repository, bound, reviewed_head)
1729
+ if pull is None:
1730
+ raise ArchiveError(f"member {member} GitHub pull request is missing")
1731
+ if pull["html_url"] != metadata["Pull-Request"]:
1732
+ raise ArchiveError(f"member {member} tag {tag_name} names the wrong pull request")
1733
+ if (pull["state"] != "closed" or pull["merged_at"] is None
1734
+ or pull["merge_commit_sha"] != merge):
1735
+ raise ArchiveError(f"member {member} GitHub pull request is not merged at the tagged landing")
1736
+ require_pull_request_merge_provenance(repository, pull["number"], merge)
1737
+ require_pull_request_merge(checkout, merge, reviewed_head, "origin/main")
1738
+ require_annotated_tag(
1739
+ checkout, tracking, merge, f"member tag {tag_name}",
1740
+ member_pull_request_tag_message(tag_name, pull["html_url"], reviewed_head, merge),
1741
+ )
1742
+ else:
1743
+ merge = _find_member_merge(checkout, subject, _member_merge_body(archive_path, member, reviewed_head),
1744
+ reviewed_head)
1745
+ if merge is None:
1746
+ raise ArchiveError(f"member {member} has no {subject!r} merge on origin/main")
1747
+ if published is None:
1748
+ raise ArchiveError(f"member {member} tag {tag_name} is not published")
1749
+ require_annotated_tag(checkout, tracking, merge, f"member tag {tag_name}")
1750
+ return {"member": member, "merge": merge, "tag": tag_name, "tag_object": published}
1751
+
1752
+
1753
+ def retire_member(coordinator: Path, member: str, archive_path: str, reviewed_head: str,
1754
+ merge: str, tag: str) -> dict:
1755
+ """After integration, delete a member's bound branch everywhere and clear its authorizations."""
1756
+ try:
1757
+ import members
1758
+ except ImportError: # pragma: no cover - package import used by tests
1759
+ from scripts import members
1760
+ checkout, project, bound, _, tag_name = _member_names(coordinator, member, archive_path)
1761
+ proven = validate_member_integrated(coordinator, member, archive_path, reviewed_head)
1762
+ if (proven["merge"], proven["tag"]) != (merge, tag):
1763
+ raise ArchiveError(f"member {member} integration differs from the ship commit")
1764
+ bound_ref = f"refs/heads/{bound}"
1765
+ local = optional_ref(checkout, bound_ref)
1766
+ if local is not None and local != reviewed_head:
1767
+ raise ArchiveError(f"member {member} {bound} moved: {local}")
1768
+ holder = registered_worktree(checkout, bound)
1769
+ if holder is not None:
1770
+ try:
1771
+ expected = worktree_paths.worktree_path(
1772
+ checkout, "bound", bound.removeprefix("gsd-path/")
1773
+ ).resolve()
1774
+ except (ValueError, OSError) as error:
1775
+ raise ArchiveError(str(error)) from error
1776
+ if holder != expected:
1777
+ raise ArchiveError(f"member {member} bound checkout is outside Path's workspace: {holder}")
1778
+ status = archive_milestone.require_git_success(
1779
+ run_git(holder, "status", "--porcelain", "--untracked-files=all"),
1780
+ "inspect member bound checkout",
1781
+ )
1782
+ if status:
1783
+ raise ArchiveError(f"member {member} bound checkout is not clean: {holder}")
1784
+ remote = live_remote_ref(checkout, bound_ref)
1785
+ if remote is not None:
1786
+ if remote != reviewed_head:
1787
+ raise ArchiveError(f"member {member} origin/{bound} moved: {remote}")
1788
+ members.authorize_delete(checkout, project, bound_ref, reviewed_head)
1789
+ archive_milestone.require_git_success(
1790
+ run_git(checkout, "push", f"--force-with-lease={bound_ref}:{reviewed_head}", "origin", "--delete", bound),
1791
+ f"delete member origin/{bound}")
1792
+ if holder is not None:
1793
+ archive_milestone.require_git_success(run_git(checkout, "worktree", "remove", str(holder)),
1794
+ "remove member bound checkout")
1795
+ if local is not None:
1796
+ archive_milestone.require_git_success(run_git(checkout, "update-ref", "-d", bound_ref, reviewed_head),
1797
+ "delete member bound branch")
1798
+ 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}"),
1800
+ ("delete", bound_ref)):
1801
+ members.clear_authorization(checkout, project, kind, ref)
1802
+ return {"member": member, "retired": bound}
1803
+
1804
+
1805
+ def close_members(repo: Path) -> dict:
1806
+ """Close each locked member in lock order before the coordinator ship commit; resumable."""
1807
+ try:
1808
+ import check_handoffs
1809
+ import members
1810
+ from pipeline_git import ship_commit_body
1811
+ except ImportError: # pragma: no cover - package import used by tests
1812
+ from scripts import check_handoffs, members
1813
+ from scripts.pipeline_git import ship_commit_body
1814
+ project = repo.resolve()
1815
+ state, _, _ = archive_milestone.strict_state(project)
1816
+ if (state.phase, state.status) != ("ship", "active") or state.archive is None:
1817
+ raise ArchiveError("close-members requires ship/active with a prepared archive")
1818
+ archive = project / state.archive
1819
+ final_text = (archive / "review" / "FINAL.md").read_text(encoding="utf-8")
1820
+ reviewed = check_handoffs._reviewed_head(final_text, "FINAL.md")
1821
+ lock = archive / "build" / "members.json"
1822
+ locked = json.loads(lock.read_text(encoding="utf-8"))["members"] if lock.is_file() else []
1823
+ try:
1824
+ heads = check_handoffs.member_reviewed_heads(final_text, "FINAL.md")
1825
+ except check_handoffs.HandoffError as error:
1826
+ raise ArchiveError(str(error)) from error
1827
+ if sorted(heads) != sorted(entry["name"] for entry in locked):
1828
+ raise ArchiveError("FINAL.md Member reviewed HEAD lines must name each locked member")
1829
+ common = Path(archive_milestone.require_git_success(
1830
+ run_git(project, "rev-parse", "--path-format=absolute", "--git-common-dir"), "resolve Git common dir"))
1831
+ archive_name = PurePosixPath(state.archive).name
1832
+ try:
1833
+ modes = {item["name"]: item["integration"] for item in members.read_members(project)} if locked else {}
1834
+ rows = {row["name"]: row for row in members.read_member_close(common, archive_name)}
1835
+ except members.MembersError as error:
1836
+ raise ArchiveError(str(error)) from error
1837
+ def save() -> None:
1838
+ members.write_member_close(common, archive_name, [rows[item["name"]] for item in locked if item["name"] in rows])
1839
+
1840
+ closed = []
1841
+ for entry in locked:
1842
+ name = entry["name"]
1843
+ mode = modes.get(name, "default")
1844
+ mode = state.integration if mode == "default" else mode
1845
+ row = rows.get(name) or {"name": name, "mode": mode, "reviewed_head": heads[name],
1846
+ "status": "pending", "merge": None, "tag": None}
1847
+ if row["reviewed_head"] != heads[name] or row["mode"] != mode:
1848
+ raise ArchiveError(f"member-close journal for {name} differs from FINAL.md or MEMBERS.md")
1849
+ if row["status"] == "integrated":
1850
+ result = validate_member_integrated(project, name, state.archive, heads[name])
1851
+ if (result["merge"], result["tag"]) != (row["merge"], row["tag"]):
1852
+ raise ArchiveError(f"member {name} integration differs from its member-close journal")
1853
+ closed.append(row)
1854
+ continue
1855
+ # Journal pending before the member moves; a later member waits for this one.
1856
+ rows[name] = row
1857
+ save()
1858
+ if mode == "direct":
1859
+ result = integrate_member(project, name, state.archive, heads[name])
1860
+ else:
1861
+ result = integrate_member_pull_request(project, name, state.archive, heads[name])
1862
+ if result.get("status") == "awaiting-merge":
1863
+ row["pull_request"] = result["pull_request"]
1864
+ save()
1865
+ return {"status": "awaiting-merge", "member": name, "pull_request": result["pull_request"]}
1866
+ row.update(status="integrated", merge=result["merge"], tag=result["tag"])
1867
+ save()
1868
+ closed.append(row)
1869
+ return {"status": "integrated", "members": closed, "body": ship_commit_body(state.archive, reviewed, closed)}