@opengsd/gsd-path 1.3.1 → 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 +281 -0
  3. package/DOCS.md +13 -7
  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 +57 -8
  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 +54 -38
  31. package/scripts/install.py +562 -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 +35 -9
  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
@@ -19,8 +19,6 @@ import hashlib
19
19
  import json
20
20
  import os
21
21
  import re
22
- import shlex
23
- import signal
24
22
  import subprocess
25
23
  import sys
26
24
  import tempfile
@@ -170,12 +168,7 @@ def child_running(state: Dict[str, object]) -> bool:
170
168
  return state.get("finished_at") is None and bool(state.get("pid")) and process_alive(int(state["pid"]))
171
169
 
172
170
 
173
- def process_alive(pid: int) -> bool:
174
- try:
175
- os.kill(pid, 0)
176
- except ProcessLookupError:
177
- return False
178
- return True
171
+ process_alive = _common.process_alive
179
172
 
180
173
 
181
174
  # --- child process ---------------------------------------------------------
@@ -208,13 +201,13 @@ def child_main(state_path: Path) -> int:
208
201
  attempt_dir = state_path.parent
209
202
  with open(attempt_dir / "brief.md", "rb") as brief, \
210
203
  open(attempt_dir / "stdout", "wb") as out, open(attempt_dir / "stderr", "wb") as err:
211
- process = subprocess.Popen(state["command"], cwd=state["worktree"], stdin=brief,
212
- stdout=out, stderr=err, start_new_session=True)
204
+ process = _common.popen_group(_common.resolve_argv(state["command"]), cwd=state["worktree"],
205
+ stdin=brief, stdout=out, stderr=err)
213
206
  try:
214
207
  exit_code: Optional[int] = process.wait(timeout=state.get("child_timeout"))
215
208
  timed_out = False
216
209
  except subprocess.TimeoutExpired:
217
- os.killpg(process.pid, signal.SIGKILL)
210
+ _common.kill_tree(process)
218
211
  process.wait()
219
212
  exit_code, timed_out = None, True
220
213
  save_state(attempt_dir / "exit.json",
@@ -224,6 +217,7 @@ def child_main(state_path: Path) -> int:
224
217
 
225
218
  def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path) -> str:
226
219
  worktree = Path(str(state["worktree"]))
220
+ project_root = Path(str(state.get("project_root") or worktree))
227
221
  task_id = state["task_id"]
228
222
  agents = worktree / "AGENTS.md"
229
223
  workflow = worktree / "WORKFLOW.md"
@@ -234,12 +228,14 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
234
228
  f"Work only in this worktree root: {worktree}",
235
229
  f"Task file: {worktree / str(state['task_file'])}",
236
230
  f"Task template: {task_template}",
237
- f"INTENT.md: {worktree / '.project/intent/INTENT.md'}",
231
+ f"INTENT.md: {project_root / '.project/intent/INTENT.md'}",
238
232
  f"AGENTS.md: {agents if agents.exists() else 'absent'}",
239
233
  f"WORKFLOW.md: {workflow if workflow.exists() else 'absent'}",
240
234
  f"Recorded base: {state['base']}",
241
235
  f"Dispatch mode: {state['mode']} ("
242
236
  + ("the primary worktree on the bound branch" if state["mode"] == "serial"
237
+ else f"an isolated worktree of member {state['member']}; never touch the member team's "
238
+ "checkout or the coordinator" if state["mode"] == "member"
243
239
  else "an isolated task worktree; never touch the primary") + ").",
244
240
  "Next consumer: the build orchestrator reruns Verify in an isolated checkout and lands "
245
241
  "the task. Gate: Verify passes and only the task's declared files plus its Log changed.",
@@ -248,7 +244,7 @@ def brief_text(state: Dict[str, object], role_brief: Path, task_template: Path)
248
244
  if state.get("answered"):
249
245
  lines.append(f"The task Log now records an `{ANSWER_MARK}` to your earlier question; "
250
246
  "continue from it.")
251
- lines.append(task_context.render(worktree, worktree / str(state['task_file'])))
247
+ lines.append(task_context.render(project_root, worktree / str(state['task_file'])))
252
248
  return "\n".join(lines) + "\n"
253
249
 
254
250
 
@@ -282,7 +278,7 @@ def selected_command(state: Dict[str, object], options: argparse.Namespace, comm
282
278
  if caps_path is None:
283
279
  if overrides or previous or model_policy.policy_path(primary, project_dir).exists():
284
280
  raise model_policy.PolicyError('model policy requires --model-capabilities for this dispatch')
285
- return shlex.split(command), None
281
+ return _common.split_command(command), None
286
282
  caps = model_policy.read_json(caps_path)
287
283
  action = getattr(options, 'action', 'round')
288
284
  role = {'round': 'coder', 'review': 'reviewer', 'panel': 'review_panel',
@@ -297,7 +293,7 @@ def selected_command(state: Dict[str, object], options: argparse.Namespace, comm
297
293
  dict(overrides, model=state['slug']), previous)
298
294
  model_policy.validate_panel(selection, state['family'], state.get('excluded_families', []))
299
295
  state['slug'] = selection['selected']['model']
300
- return model_policy.command_args(shlex.split(command), selection), selection
296
+ return model_policy.command_args(_common.split_command(command), selection), selection
301
297
 
302
298
 
303
299
  def retain_selection(root: Path, state: Dict[str, object]) -> Dict[str, object]:
@@ -340,12 +336,12 @@ def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
340
336
  "child_timeout": options.child_timeout, "outcome": None,
341
337
  "dispatched_at": now(), "origin": "question" if state.get("answered") else "dispatch"})
342
338
  state_path = attempt_dir / "state.json"
343
- (attempt_dir / "brief.md").write_text(brief(state), encoding="utf-8")
339
+ (attempt_dir / "brief.md").write_bytes(brief(state).encode("utf-8"))
344
340
  save_state(state_path, state)
345
- wrapper = subprocess.Popen(
341
+ wrapper = _common.popen_detached(
346
342
  [sys.executable, "-B", str(Path(__file__).resolve()), "_child", "--state", str(state_path)],
347
343
  cwd=state["worktree"], stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
348
- stderr=subprocess.DEVNULL, start_new_session=True,
344
+ stderr=subprocess.DEVNULL,
349
345
  )
350
346
  state["_path"] = str(state_path)
351
347
  update_state(state, pid=wrapper.pid)
@@ -356,8 +352,11 @@ def spawn(root: Path, state: Dict[str, object], options: argparse.Namespace,
356
352
 
357
353
 
358
354
  def log_delta(primary: Path, state: Dict[str, object]) -> str:
359
- base_text = isolation.git_text(primary, "show", f"{state['base']}:{state['task_file']}")
355
+ contract_file = state.get("contract_file") or state["task_file"]
356
+ base_text = isolation.git_text(primary, "show", f"{state['base']}:{contract_file}")
360
357
  text = (Path(str(state["worktree"])) / str(state["task_file"])).read_text(encoding="utf-8")
358
+ if state["mode"] == "member":
359
+ return isolation.member_log_delta(base_text, text)
361
360
  return isolation.task_log_delta(base_text, text)
362
361
 
363
362
 
@@ -374,14 +373,14 @@ def snapshot_tree(primary: Path) -> str:
374
373
  env = dict(os.environ, GIT_INDEX_FILE=str(Path(temporary) / "index"))
375
374
  for arguments in (("read-tree", "HEAD"), ("add", "-A"), ("write-tree",)):
376
375
  completed = subprocess.run(("git", "-C", str(primary), *arguments), env=env,
377
- text=True, capture_output=True)
376
+ encoding="utf-8", errors="replace", capture_output=True)
378
377
  if completed.returncode:
379
378
  raise DriverStop(f"git {' '.join(arguments)} failed: {completed.stderr.strip()}")
380
379
  return completed.stdout.strip()
381
380
 
382
381
 
383
382
  def run_verify(command: str, cwd: Path) -> Dict[str, object]:
384
- completed = _common.run_command("bash", "-c", command, cwd=cwd)
383
+ completed = _common.run_command(*_common.bash_argv(command), cwd=cwd)
385
384
  return {"exit_code": completed.returncode, "stdout": completed.stdout, "stderr": completed.stderr}
386
385
 
387
386
 
@@ -394,6 +393,30 @@ def state_from_task(primary: Path, task_id: str) -> Dict[str, object]:
394
393
  break
395
394
  else:
396
395
  raise DriverStop(f"task {task_id} has no task file")
396
+ member = fields.get("repo")
397
+ if member:
398
+ # A member activation lives in the live copy inside its member sidecar.
399
+ try:
400
+ checkout, project, _ = isolation._member_context(primary, str(member))
401
+ except isolation.IsolationError as error:
402
+ raise DriverStop(str(error), task=task_id) from error
403
+ task_file = task_path.relative_to(primary).as_posix()
404
+ sidecar = isolation.sidecar_root(checkout, "task", f"{project}-{task_id}").resolve()
405
+ copy = isolation.member_task_copy(sidecar, task_file)
406
+ live, _ = isolation.task_frontmatter(copy.read_text(encoding="utf-8")) if copy.is_file() else ({}, None)
407
+ if not live or live.get("status") != "in-progress" or not live.get("base") or not live.get("member_base"):
408
+ raise DriverStop(f"member task {task_id} has no activated live copy in {sidecar}", task=task_id)
409
+ return {"task_id": task_id, "title": str(live.get("title")), "task_file": copy.relative_to(sidecar).as_posix(),
410
+ "contract_file": task_file, "files": list(live.get("files") or []), "base": str(live["base"]),
411
+ "worktree": str(sidecar), "task_branch": f"{isolation.TASK_BRANCH_PREFIX}{project}-{task_id}",
412
+ "mode": "member", "member": str(member), "member_base": str(live["member_base"]),
413
+ "project_root": str(primary), "sidecar": None}
414
+ if fields.get("status") != "in-progress" or not fields.get("base"):
415
+ # A parallel activation lives only in its isolate; the primary copy stays pending.
416
+ isolate = isolation.sidecar_root(primary, "task", task_id).resolve()
417
+ if isolation.authorized_task_worktree(isolate, isolation.require_bound(primary)):
418
+ fields, _ = isolation.task_frontmatter(
419
+ (isolate / task_path.relative_to(primary)).read_text(encoding="utf-8"))
397
420
  if fields.get("status") != "in-progress" or not fields.get("base"):
398
421
  raise DriverStop(f"task {task_id} is not an in-progress task with a recorded base")
399
422
  def value(key: str) -> Optional[str]:
@@ -453,7 +476,21 @@ def finish_task(primary: Path, state: Dict[str, object]) -> Dict[str, object]:
453
476
  if not passed:
454
477
  if state["mode"] == "parallel":
455
478
  isolation.deactivate_task(worktree, task_id, str(state["task_branch"]))
479
+ elif state["mode"] == "member":
480
+ isolation.deactivate_member_task(primary, str(state["member"]), task_id)
456
481
  raise DriverStop("Verify failed in the isolate", task=task_id, execution=execution)
482
+ if state["mode"] == "member":
483
+ member = str(state["member"])
484
+ landed = isolation.land_member(primary, member, task_id, str(state["title"]),
485
+ str(state["contract_file"]), base, str(state["member_base"]))
486
+ landing = str(landed["landing"])
487
+ # Only a landing whose parent is the member base proves the verified member tree.
488
+ ledger = isolation.git_output(worktree, "rev-parse", f"{landing}^") == str(state["member_base"])
489
+ if ledger:
490
+ build_state.verify_record(str(primary), command, landing, "pass", execution=execution, member=member)
491
+ isolation.retire_member_task(primary, member, task_id)
492
+ return {"task": task_id, "commit": str(landed["commit"]), "mode": "member",
493
+ "landing": landing, "verify": execution, "ledger": ledger}
457
494
  landed = isolation.land(primary, worktree, base, task_id, str(state["title"]), task_file,
458
495
  list(state["files"]))
459
496
  commit = str(landed["commit"])
@@ -485,7 +522,8 @@ def recover_report(primary: Path, project_dir: str, receipt: Dict[str, object])
485
522
 
486
523
  def checkpoint_project(primary: Path, receipt: Dict[str, object], subject: str, body: str) -> Optional[str]:
487
524
  """Commit .project bookkeeping through the canonical checkpoint; None when nothing is dirty."""
488
- if not any(path.startswith(".project/") for path in isolation.uncommitted_paths(primary)):
525
+ if not any(path.startswith(".project/") for path in isolation.uncommitted_paths(primary)) \
526
+ and not isolation.ignored_legacy_ledger(primary):
489
527
  return None
490
528
  result = isolation.checkpoint(primary, isolation.current_sha(primary), subject, body, [".project"])
491
529
  receipt["steps"].append({"script": "isolation.py checkpoint", "result": result})
@@ -582,7 +620,7 @@ class Round:
582
620
  """Run the bundled token_budget.py against this milestone's ledger; blocked is a stop."""
583
621
  command = [sys.executable, "-B", str(Path(__file__).resolve().parent / "token_budget.py"),
584
622
  action, "--ledger", str(budget_ledger(self.primary)), *arguments]
585
- completed = subprocess.run(command, cwd=self.primary, capture_output=True, text=True)
623
+ completed = subprocess.run(command, cwd=self.primary, capture_output=True, encoding="utf-8", errors="replace")
586
624
  result = json.loads(completed.stdout) if completed.stdout.strip() else {}
587
625
  self.receipt["steps"].append({"script": f"token_budget.py {action}", "command": command,
588
626
  "exit_code": completed.returncode, "result": result,
@@ -605,12 +643,24 @@ class Round:
605
643
  # recovery -------------------------------------------------------------
606
644
 
607
645
  def recover(self) -> None:
646
+ # A journaled member landing is safe to finish; it never lands the same work twice.
647
+ journals = isolation.common_git_dir(self.primary).joinpath(*isolation.MEMBER_LANDING_DIR)
648
+ retired_members = set()
649
+ for journal in sorted(journals.glob("*.json")) if journals.is_dir() else []:
650
+ result = isolation.recover_member_landing(self.primary, journal.stem)
651
+ self.receipt["steps"].append({"script": "isolation.recover_member_landing", "task": journal.stem,
652
+ "result": result})
653
+ if result["state"] == "landed":
654
+ isolation.retire_member_task(self.primary, str(result["member"]), journal.stem)
655
+ retired_members.add(journal.stem)
608
656
  report = recover_report(self.primary, self.project_dir, self.receipt)
609
657
  for task in report["tasks"]:
610
658
  worktree = task.get("worktree")
611
659
  verdict = task["verdict"]
612
660
  if verdict in ("recovered", "attested"):
613
661
  self.proven[str(task.get("task_id"))] = str(task.get("commit"))
662
+ if verdict == "recovered" and task.get("member") and task.get("task_id") not in retired_members:
663
+ isolation.retire_member_task(self.primary, str(task["member"]), str(task["task_id"]))
614
664
  if verdict == "recovered" and worktree and worktree["clean"] and task.get("task_branch"):
615
665
  isolation.retire(self.primary, Path(str(worktree["path"])),
616
666
  str(task["task_branch"]), False)
@@ -730,6 +780,8 @@ class Round:
730
780
  if state.get("wave") != self.receipt["wave"]:
731
781
  raise DriverStop("task is outside the requested wave", task=state["task_id"],
732
782
  wave=state.get("wave"), requested_wave=self.receipt["wave"])
783
+ if state.get("mode") == "member":
784
+ isolation.ensure_member_hooks(self.primary, str(state["member"]), str(state["task_id"]))
733
785
  fresh = spawn(self.root, state, self.options,
734
786
  lambda fresh: brief_text(fresh, self.options.role_brief, self.options.task_template))
735
787
  self.receipt["dispatched"].append(self.summary(fresh))
@@ -805,6 +857,28 @@ class Round:
805
857
  self.runner("lint-round", head)
806
858
  round_size = len(selected) + len(in_flight)
807
859
  for task in selected:
860
+ if task.get("repo"):
861
+ member = str(task["repo"])
862
+ retained = isolation.retained_member_task(self.primary, member, task["id"],
863
+ task["task_file"], head)
864
+ if retained and retained["state"] == "unused":
865
+ isolation.retire_member_task(self.primary, member, task["id"])
866
+ retained = None
867
+ if retained:
868
+ isolate, active = retained["isolate"], retained["active"]
869
+ else:
870
+ isolate = isolation.isolate_member_task(self.primary, member, task["id"])
871
+ active = isolation.activate_member_task(self.primary, member, task["id"],
872
+ f"build_{task['id'].lower()}", task["task_file"], head)
873
+ sidecar_path = Path(str(active["worktree"]))
874
+ self.launch({"task_id": task["id"], "title": task["title"],
875
+ "task_file": Path(str(active["copy"])).relative_to(sidecar_path).as_posix(),
876
+ "contract_file": task["task_file"], "files": task["files"], "wave": task["wave"],
877
+ "verify_heavy": task["verify_heavy"], "base": head, "worktree": str(sidecar_path),
878
+ "task_branch": isolate["task_branch"], "mode": "member", "member": member,
879
+ "member_base": isolate["member_base"], "project_root": str(self.primary),
880
+ "sidecar": None, "answered": False, "model_selection": task['model_selection']})
881
+ continue
808
882
  prepared = self.runner("prepare-task", head, task["id"], round_size)
809
883
  isolate = prepared[0]["result"]
810
884
  sidecar = prepared[1]["result"] if len(prepared) > 1 else None
@@ -1178,7 +1252,7 @@ class Review:
1178
1252
  def helper_json(receipt: Dict[str, object], script: str, *arguments: str, cwd: Path) -> Dict[str, object]:
1179
1253
  """Run a bundled helper and return its JSON stdout; a non-zero exit is a typed stop."""
1180
1254
  command = [sys.executable, "-B", str(Path(__file__).resolve().parent / script), *arguments]
1181
- completed = subprocess.run(command, cwd=cwd, capture_output=True, text=True)
1255
+ completed = subprocess.run(command, cwd=cwd, capture_output=True, encoding="utf-8", errors="replace")
1182
1256
  step = {"script": script, "command": command, "exit_code": completed.returncode,
1183
1257
  "stdout": completed.stdout, "stderr": completed.stderr}
1184
1258
  receipt["steps"].append(step)
@@ -1746,7 +1820,7 @@ Heavy: {'yes' if heavy else 'no'}
1746
1820
  receipt["plan_wave"] = fix_wave
1747
1821
  lint = subprocess.run([sys.executable, "-B", str(Path(__file__).resolve().parent / "check_task_briefs.py"),
1748
1822
  "--repo", str(primary), "--base", isolation.current_sha(primary),
1749
- "--tasks-dir", f"{options.project_dir}/tasks"], capture_output=True, text=True)
1823
+ "--tasks-dir", f"{options.project_dir}/tasks"], capture_output=True, encoding="utf-8", errors="replace")
1750
1824
  receipt["steps"].append({"script": "check_task_briefs.py", "exit_code": lint.returncode,
1751
1825
  "stdout": lint.stdout, "stderr": lint.stderr})
1752
1826
  if lint.returncode:
@@ -1981,6 +2055,17 @@ def main(argv=None) -> int:
1981
2055
  current.recover()
1982
2056
  records = [state for state in latest_states(current.root)
1983
2057
  if state["task_id"] == arguments.task_id]
2058
+ native = None
2059
+ if records and records[0].get("outcome") is not None:
2060
+ # A settled driver record at an older base is superseded by a native retry.
2061
+ try:
2062
+ native = state_from_task(primary, arguments.task_id)
2063
+ except DriverStop:
2064
+ pass
2065
+ if native and native["base"] != records[0].get("base"):
2066
+ records = []
2067
+ else:
2068
+ native = None
1984
2069
  if arguments.task_id in current.proven:
1985
2070
  commit = current.proven[arguments.task_id]
1986
2071
  if records and records[0].get("outcome") is None:
@@ -1997,7 +2082,7 @@ def main(argv=None) -> int:
1997
2082
  current.classify(record)
1998
2083
  else: # a coder dispatched by hand: derive the isolate from the task frontmatter
1999
2084
  current.receipt["landed"].append(
2000
- finish_task(primary, state_from_task(primary, arguments.task_id)))
2085
+ finish_task(primary, native or state_from_task(primary, arguments.task_id)))
2001
2086
  result = {**current.receipt, "status": "landed" if current.receipt["landed"] else "blocked"}
2002
2087
  elif arguments.action == "answer":
2003
2088
  result = answer(primary, arguments.task_id, arguments.answer)