@opengsd/gsd-path 1.4.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (476) hide show
  1. package/CHANGELOG.md +460 -341
  2. package/DOCS.md +16 -5
  3. package/FULL.md +67 -6
  4. package/HOOKS.md +158 -10
  5. package/MIGRATE.md +12 -0
  6. package/QUICK.md +4 -2
  7. package/README.md +136 -116
  8. package/RUNTIME.md +24 -6
  9. package/UPDATE.md +39 -3
  10. package/WORKFLOW.md +80 -4
  11. package/package.json +5 -2
  12. package/platforms/shared-agents/dispatch.md +13 -0
  13. package/scripts/_common.py +256 -2
  14. package/scripts/archive_milestone.py +14 -13
  15. package/scripts/build_recovery.py +155 -58
  16. package/scripts/build_state.py +274 -8
  17. package/scripts/check_docs_audit.py +77 -4
  18. package/scripts/check_handoffs.py +635 -57
  19. package/scripts/check_task_briefs.py +293 -32
  20. package/scripts/detect_project.py +260 -20
  21. package/scripts/discussion_records.py +9 -1
  22. package/scripts/discussion_validate.py +93 -35
  23. package/scripts/dispatch_driver.py +549 -53
  24. package/scripts/git_guard.py +27 -8
  25. package/scripts/guard_hook.py +2630 -210
  26. package/scripts/install.mjs +99 -11
  27. package/scripts/install.py +231 -46
  28. package/scripts/integration.py +244 -65
  29. package/scripts/isolation.py +539 -70
  30. package/scripts/loop_run.py +16 -4
  31. package/scripts/members.py +77 -32
  32. package/scripts/migrate_core.py +2 -0
  33. package/scripts/model_policy.py +5 -1
  34. package/scripts/path_config.py +3 -3
  35. package/scripts/pipeline_diagnose.py +17 -2
  36. package/scripts/pipeline_git.py +131 -42
  37. package/scripts/pipeline_state.py +139 -39
  38. package/scripts/pipeline_undo.py +118 -16
  39. package/scripts/promote_lookahead.py +3 -3
  40. package/scripts/review_findings.py +180 -44
  41. package/scripts/roadmap.py +9 -1
  42. package/scripts/runtime_store.py +21 -2
  43. package/scripts/skill-resources.json +9 -2
  44. package/scripts/state_checkpoint.py +39 -36
  45. package/scripts/state_promote.py +15 -4
  46. package/scripts/status_runtime.py +22 -4
  47. package/scripts/task_context.py +14 -3
  48. package/scripts/wizard.mjs +1 -0
  49. package/scripts/workflow_run.py +14 -4
  50. package/scripts/worktree_paths.py +54 -2
  51. package/skills/gsd-path/DEFINE.md +34 -5
  52. package/skills/gsd-path/PLAN.md +14 -3
  53. package/skills/gsd-path/RESEARCH.md +11 -0
  54. package/skills/gsd-path/ROADMAP.md +16 -6
  55. package/skills/gsd-path/SHIP.md +32 -5
  56. package/skills/gsd-path/SKILL.md +52 -24
  57. package/skills/gsd-path/references/build-native.md +112 -10
  58. package/skills/gsd-path/references/build-recovery.md +13 -0
  59. package/skills/gsd-path/references/coder.md +7 -3
  60. package/skills/gsd-path/references/dispatch.md +13 -0
  61. package/skills/gsd-path/references/docs-auditor.md +5 -0
  62. package/skills/gsd-path/references/operating-rules.md +11 -3
  63. package/skills/gsd-path/references/planner.md +26 -4
  64. package/skills/gsd-path/references/reviewer-final.md +20 -2
  65. package/skills/gsd-path/references/reviewer-wave.md +56 -12
  66. package/skills/gsd-path/references/reviewer.md +3 -1
  67. package/skills/gsd-path/references/runtime-selection.md +9 -0
  68. package/skills/gsd-path/references/spec-probes.md +150 -0
  69. package/skills/gsd-path/scripts/_common.py +256 -2
  70. package/skills/gsd-path/scripts/archive_milestone.py +14 -13
  71. package/skills/gsd-path/scripts/build_recovery.py +155 -58
  72. package/skills/gsd-path/scripts/build_state.py +274 -8
  73. package/skills/gsd-path/scripts/check_docs_audit.py +77 -4
  74. package/skills/gsd-path/scripts/check_handoffs.py +635 -57
  75. package/skills/gsd-path/scripts/check_task_briefs.py +293 -32
  76. package/skills/gsd-path/scripts/detect_project.py +260 -20
  77. package/skills/gsd-path/scripts/discussion_records.py +9 -1
  78. package/skills/gsd-path/scripts/discussion_validate.py +93 -35
  79. package/skills/gsd-path/scripts/dispatch_driver.py +549 -53
  80. package/skills/gsd-path/scripts/git_guard.py +27 -8
  81. package/skills/gsd-path/scripts/integration.py +244 -65
  82. package/skills/gsd-path/scripts/isolation.py +539 -70
  83. package/skills/gsd-path/scripts/loop_run.py +16 -4
  84. package/skills/gsd-path/scripts/members.py +77 -32
  85. package/skills/gsd-path/scripts/model_policy.py +5 -1
  86. package/skills/gsd-path/scripts/path_config.py +3 -3
  87. package/skills/gsd-path/scripts/pipeline_git.py +131 -42
  88. package/skills/gsd-path/scripts/pipeline_state.py +139 -39
  89. package/skills/gsd-path/scripts/promote_lookahead.py +3 -3
  90. package/skills/gsd-path/scripts/review_findings.py +180 -44
  91. package/skills/gsd-path/scripts/roadmap.py +9 -1
  92. package/skills/gsd-path/scripts/state_checkpoint.py +39 -36
  93. package/skills/gsd-path/scripts/state_promote.py +15 -4
  94. package/skills/gsd-path/scripts/status_runtime.py +22 -4
  95. package/skills/gsd-path/scripts/task_context.py +14 -3
  96. package/skills/gsd-path/scripts/workflow_run.py +14 -4
  97. package/skills/gsd-path/scripts/worktree_paths.py +54 -2
  98. package/skills/gsd-path/templates/docs-audit.md +5 -1
  99. package/skills/gsd-path/templates/final-review.md +11 -2
  100. package/skills/gsd-path/templates/intent.md +30 -0
  101. package/skills/gsd-path/templates/patch-findings.md +3 -3
  102. package/skills/gsd-path/templates/plan.md +17 -2
  103. package/skills/gsd-path/templates/research-handoff.md +14 -3
  104. package/skills/gsd-path/templates/state.md +2 -1
  105. package/skills/gsd-path/templates/task.md +1 -0
  106. package/skills/gsd-path/templates/wave-review.md +28 -9
  107. package/skills/gsd-path-build/references/build-native.md +112 -10
  108. package/skills/gsd-path-build/references/build-recovery.md +13 -0
  109. package/skills/gsd-path-build/references/coder.md +7 -3
  110. package/skills/gsd-path-build/references/dispatch.md +13 -0
  111. package/skills/gsd-path-build/references/operating-rules.md +11 -3
  112. package/skills/gsd-path-build/references/reviewer-final.md +20 -2
  113. package/skills/gsd-path-build/references/reviewer-wave.md +56 -12
  114. package/skills/gsd-path-build/references/reviewer.md +3 -1
  115. package/skills/gsd-path-build/references/runtime-selection.md +9 -0
  116. package/skills/gsd-path-build/references/spec-probes.md +150 -0
  117. package/skills/gsd-path-build/scripts/_common.py +256 -2
  118. package/skills/gsd-path-build/scripts/archive_milestone.py +14 -13
  119. package/skills/gsd-path-build/scripts/build_recovery.py +155 -58
  120. package/skills/gsd-path-build/scripts/build_state.py +274 -8
  121. package/skills/gsd-path-build/scripts/check_handoffs.py +635 -57
  122. package/skills/gsd-path-build/scripts/check_task_briefs.py +293 -32
  123. package/skills/gsd-path-build/scripts/detect_project.py +260 -20
  124. package/skills/gsd-path-build/scripts/discussion_records.py +9 -1
  125. package/skills/gsd-path-build/scripts/discussion_validate.py +93 -35
  126. package/skills/gsd-path-build/scripts/dispatch_driver.py +549 -53
  127. package/skills/gsd-path-build/scripts/integration.py +244 -65
  128. package/skills/gsd-path-build/scripts/isolation.py +539 -70
  129. package/skills/gsd-path-build/scripts/loop_run.py +16 -4
  130. package/skills/gsd-path-build/scripts/members.py +77 -32
  131. package/skills/gsd-path-build/scripts/model_policy.py +5 -1
  132. package/skills/gsd-path-build/scripts/path_config.py +3 -3
  133. package/skills/gsd-path-build/scripts/pipeline_git.py +131 -42
  134. package/skills/gsd-path-build/scripts/pipeline_state.py +139 -39
  135. package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -3
  136. package/skills/gsd-path-build/scripts/review_findings.py +180 -44
  137. package/skills/gsd-path-build/scripts/roadmap.py +9 -1
  138. package/skills/gsd-path-build/scripts/state_checkpoint.py +39 -36
  139. package/skills/gsd-path-build/scripts/state_promote.py +15 -4
  140. package/skills/gsd-path-build/scripts/task_context.py +14 -3
  141. package/skills/gsd-path-build/scripts/workflow_run.py +14 -4
  142. package/skills/gsd-path-build/scripts/worktree_paths.py +54 -2
  143. package/skills/gsd-path-build/templates/task.md +1 -0
  144. package/skills/gsd-path-build/templates/wave-review.md +28 -9
  145. package/skills/gsd-path-decide/references/dispatch.md +13 -0
  146. package/skills/gsd-path-decide/references/operating-rules.md +11 -3
  147. package/skills/gsd-path-decide/references/runtime-selection.md +9 -0
  148. package/skills/gsd-path-decide/scripts/_common.py +256 -2
  149. package/skills/gsd-path-decide/scripts/archive_milestone.py +14 -13
  150. package/skills/gsd-path-decide/scripts/build_recovery.py +155 -58
  151. package/skills/gsd-path-decide/scripts/check_handoffs.py +635 -57
  152. package/skills/gsd-path-decide/scripts/check_task_briefs.py +293 -32
  153. package/skills/gsd-path-decide/scripts/detect_project.py +260 -20
  154. package/skills/gsd-path-decide/scripts/discussion_records.py +9 -1
  155. package/skills/gsd-path-decide/scripts/discussion_validate.py +93 -35
  156. package/skills/gsd-path-decide/scripts/integration.py +244 -65
  157. package/skills/gsd-path-decide/scripts/isolation.py +539 -70
  158. package/skills/gsd-path-decide/scripts/loop_run.py +16 -4
  159. package/skills/gsd-path-decide/scripts/members.py +77 -32
  160. package/skills/gsd-path-decide/scripts/model_policy.py +5 -1
  161. package/skills/gsd-path-decide/scripts/path_config.py +3 -3
  162. package/skills/gsd-path-decide/scripts/pipeline_git.py +131 -42
  163. package/skills/gsd-path-decide/scripts/pipeline_state.py +139 -39
  164. package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -3
  165. package/skills/gsd-path-decide/scripts/roadmap.py +9 -1
  166. package/skills/gsd-path-decide/scripts/state_checkpoint.py +39 -36
  167. package/skills/gsd-path-decide/scripts/state_promote.py +15 -4
  168. package/skills/gsd-path-decide/scripts/worktree_paths.py +54 -2
  169. package/skills/gsd-path-define/SKILL.md +34 -5
  170. package/skills/gsd-path-define/references/build-recovery.md +13 -0
  171. package/skills/gsd-path-define/references/operating-rules.md +11 -3
  172. package/skills/gsd-path-define/references/runtime-selection.md +9 -0
  173. package/skills/gsd-path-define/references/spec-probes.md +150 -0
  174. package/skills/gsd-path-define/scripts/_common.py +256 -2
  175. package/skills/gsd-path-define/scripts/archive_milestone.py +14 -13
  176. package/skills/gsd-path-define/scripts/build_recovery.py +155 -58
  177. package/skills/gsd-path-define/scripts/check_handoffs.py +635 -57
  178. package/skills/gsd-path-define/scripts/check_task_briefs.py +293 -32
  179. package/skills/gsd-path-define/scripts/detect_project.py +260 -20
  180. package/skills/gsd-path-define/scripts/discussion_records.py +9 -1
  181. package/skills/gsd-path-define/scripts/discussion_validate.py +93 -35
  182. package/skills/gsd-path-define/scripts/integration.py +244 -65
  183. package/skills/gsd-path-define/scripts/isolation.py +539 -70
  184. package/skills/gsd-path-define/scripts/loop_run.py +16 -4
  185. package/skills/gsd-path-define/scripts/members.py +77 -32
  186. package/skills/gsd-path-define/scripts/model_policy.py +5 -1
  187. package/skills/gsd-path-define/scripts/path_config.py +3 -3
  188. package/skills/gsd-path-define/scripts/pipeline_git.py +131 -42
  189. package/skills/gsd-path-define/scripts/pipeline_state.py +139 -39
  190. package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -3
  191. package/skills/gsd-path-define/scripts/roadmap.py +9 -1
  192. package/skills/gsd-path-define/scripts/state_checkpoint.py +39 -36
  193. package/skills/gsd-path-define/scripts/state_promote.py +15 -4
  194. package/skills/gsd-path-define/scripts/worktree_paths.py +54 -2
  195. package/skills/gsd-path-define/templates/intent.md +30 -0
  196. package/skills/gsd-path-define/templates/state.md +2 -1
  197. package/skills/gsd-path-discuss/references/operating-rules.md +11 -3
  198. package/skills/gsd-path-discuss/references/runtime-selection.md +9 -0
  199. package/skills/gsd-path-discuss/scripts/_common.py +256 -2
  200. package/skills/gsd-path-discuss/scripts/discussion_records.py +9 -1
  201. package/skills/gsd-path-discuss/scripts/loop_run.py +16 -4
  202. package/skills/gsd-path-discuss/scripts/model_policy.py +5 -1
  203. package/skills/gsd-path-discuss/scripts/path_config.py +3 -3
  204. package/skills/gsd-path-discuss/scripts/worktree_paths.py +54 -2
  205. package/skills/gsd-path-docs-audit/references/dispatch.md +13 -0
  206. package/skills/gsd-path-docs-audit/references/docs-auditor.md +5 -0
  207. package/skills/gsd-path-docs-audit/references/operating-rules.md +11 -3
  208. package/skills/gsd-path-docs-audit/references/runtime-selection.md +9 -0
  209. package/skills/gsd-path-docs-audit/scripts/_common.py +256 -2
  210. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +14 -13
  211. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +155 -58
  212. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +77 -4
  213. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +635 -57
  214. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +293 -32
  215. package/skills/gsd-path-docs-audit/scripts/detect_project.py +260 -20
  216. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +9 -1
  217. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +93 -35
  218. package/skills/gsd-path-docs-audit/scripts/integration.py +244 -65
  219. package/skills/gsd-path-docs-audit/scripts/isolation.py +539 -70
  220. package/skills/gsd-path-docs-audit/scripts/loop_run.py +16 -4
  221. package/skills/gsd-path-docs-audit/scripts/members.py +77 -32
  222. package/skills/gsd-path-docs-audit/scripts/model_policy.py +5 -1
  223. package/skills/gsd-path-docs-audit/scripts/path_config.py +3 -3
  224. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +131 -42
  225. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +139 -39
  226. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -3
  227. package/skills/gsd-path-docs-audit/scripts/roadmap.py +9 -1
  228. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +39 -36
  229. package/skills/gsd-path-docs-audit/scripts/state_promote.py +15 -4
  230. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +54 -2
  231. package/skills/gsd-path-docs-audit/templates/docs-audit.md +5 -1
  232. package/skills/gsd-path-forensics/references/operating-rules.md +11 -3
  233. package/skills/gsd-path-forensics/references/runtime-selection.md +9 -0
  234. package/skills/gsd-path-forensics/scripts/_common.py +256 -2
  235. package/skills/gsd-path-forensics/scripts/discussion_records.py +9 -1
  236. package/skills/gsd-path-forensics/scripts/loop_run.py +16 -4
  237. package/skills/gsd-path-forensics/scripts/model_policy.py +5 -1
  238. package/skills/gsd-path-forensics/scripts/path_config.py +3 -3
  239. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +17 -2
  240. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +118 -16
  241. package/skills/gsd-path-forensics/scripts/worktree_paths.py +54 -2
  242. package/skills/gsd-path-inspect/references/dispatch.md +13 -0
  243. package/skills/gsd-path-inspect/references/docs-auditor.md +5 -0
  244. package/skills/gsd-path-inspect/references/operating-rules.md +11 -3
  245. package/skills/gsd-path-inspect/references/runtime-selection.md +9 -0
  246. package/skills/gsd-path-inspect/scripts/_common.py +256 -2
  247. package/skills/gsd-path-inspect/scripts/archive_milestone.py +14 -13
  248. package/skills/gsd-path-inspect/scripts/build_recovery.py +155 -58
  249. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +77 -4
  250. package/skills/gsd-path-inspect/scripts/check_handoffs.py +635 -57
  251. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +293 -32
  252. package/skills/gsd-path-inspect/scripts/detect_project.py +260 -20
  253. package/skills/gsd-path-inspect/scripts/discussion_records.py +9 -1
  254. package/skills/gsd-path-inspect/scripts/discussion_validate.py +93 -35
  255. package/skills/gsd-path-inspect/scripts/integration.py +244 -65
  256. package/skills/gsd-path-inspect/scripts/isolation.py +539 -70
  257. package/skills/gsd-path-inspect/scripts/loop_run.py +16 -4
  258. package/skills/gsd-path-inspect/scripts/members.py +77 -32
  259. package/skills/gsd-path-inspect/scripts/model_policy.py +5 -1
  260. package/skills/gsd-path-inspect/scripts/path_config.py +3 -3
  261. package/skills/gsd-path-inspect/scripts/pipeline_git.py +131 -42
  262. package/skills/gsd-path-inspect/scripts/pipeline_state.py +139 -39
  263. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -3
  264. package/skills/gsd-path-inspect/scripts/roadmap.py +9 -1
  265. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +39 -36
  266. package/skills/gsd-path-inspect/scripts/state_promote.py +15 -4
  267. package/skills/gsd-path-inspect/scripts/workflow_run.py +14 -4
  268. package/skills/gsd-path-inspect/scripts/worktree_paths.py +54 -2
  269. package/skills/gsd-path-inspect/templates/docs-audit.md +5 -1
  270. package/skills/gsd-path-inspect/templates/state.md +2 -1
  271. package/skills/gsd-path-loop/references/operating-rules.md +11 -3
  272. package/skills/gsd-path-loop/references/runtime-selection.md +9 -0
  273. package/skills/gsd-path-loop/scripts/_common.py +256 -2
  274. package/skills/gsd-path-loop/scripts/discussion_records.py +9 -1
  275. package/skills/gsd-path-loop/scripts/loop_run.py +16 -4
  276. package/skills/gsd-path-loop/scripts/model_policy.py +5 -1
  277. package/skills/gsd-path-loop/scripts/path_config.py +3 -3
  278. package/skills/gsd-path-loop/scripts/worktree_paths.py +54 -2
  279. package/skills/gsd-path-migrate/SKILL.md +8 -0
  280. package/skills/gsd-path-migrate/references/operating-rules.md +11 -3
  281. package/skills/gsd-path-migrate/references/runtime-selection.md +9 -0
  282. package/skills/gsd-path-migrate/scripts/_common.py +256 -2
  283. package/skills/gsd-path-migrate/scripts/discussion_records.py +9 -1
  284. package/skills/gsd-path-migrate/scripts/loop_run.py +16 -4
  285. package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -0
  286. package/skills/gsd-path-migrate/scripts/model_policy.py +5 -1
  287. package/skills/gsd-path-migrate/scripts/path_config.py +3 -3
  288. package/skills/gsd-path-plan/SKILL.md +14 -3
  289. package/skills/gsd-path-plan/references/build-recovery.md +13 -0
  290. package/skills/gsd-path-plan/references/dispatch.md +13 -0
  291. package/skills/gsd-path-plan/references/operating-rules.md +11 -3
  292. package/skills/gsd-path-plan/references/planner.md +26 -4
  293. package/skills/gsd-path-plan/references/runtime-selection.md +9 -0
  294. package/skills/gsd-path-plan/references/spec-probes.md +150 -0
  295. package/skills/gsd-path-plan/scripts/_common.py +256 -2
  296. package/skills/gsd-path-plan/scripts/archive_milestone.py +14 -13
  297. package/skills/gsd-path-plan/scripts/build_recovery.py +155 -58
  298. package/skills/gsd-path-plan/scripts/check_handoffs.py +635 -57
  299. package/skills/gsd-path-plan/scripts/check_task_briefs.py +293 -32
  300. package/skills/gsd-path-plan/scripts/detect_project.py +260 -20
  301. package/skills/gsd-path-plan/scripts/discussion_records.py +9 -1
  302. package/skills/gsd-path-plan/scripts/discussion_validate.py +93 -35
  303. package/skills/gsd-path-plan/scripts/integration.py +244 -65
  304. package/skills/gsd-path-plan/scripts/isolation.py +539 -70
  305. package/skills/gsd-path-plan/scripts/loop_run.py +16 -4
  306. package/skills/gsd-path-plan/scripts/members.py +77 -32
  307. package/skills/gsd-path-plan/scripts/model_policy.py +5 -1
  308. package/skills/gsd-path-plan/scripts/path_config.py +3 -3
  309. package/skills/gsd-path-plan/scripts/pipeline_git.py +131 -42
  310. package/skills/gsd-path-plan/scripts/pipeline_state.py +139 -39
  311. package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -3
  312. package/skills/gsd-path-plan/scripts/roadmap.py +9 -1
  313. package/skills/gsd-path-plan/scripts/state_checkpoint.py +39 -36
  314. package/skills/gsd-path-plan/scripts/state_promote.py +15 -4
  315. package/skills/gsd-path-plan/scripts/workflow_run.py +14 -4
  316. package/skills/gsd-path-plan/scripts/worktree_paths.py +54 -2
  317. package/skills/gsd-path-plan/templates/patch-findings.md +3 -3
  318. package/skills/gsd-path-plan/templates/plan.md +17 -2
  319. package/skills/gsd-path-plan/templates/task.md +1 -0
  320. package/skills/gsd-path-research/SKILL.md +11 -0
  321. package/skills/gsd-path-research/references/dispatch.md +13 -0
  322. package/skills/gsd-path-research/references/operating-rules.md +11 -3
  323. package/skills/gsd-path-research/references/runtime-selection.md +9 -0
  324. package/skills/gsd-path-research/scripts/_common.py +256 -2
  325. package/skills/gsd-path-research/scripts/archive_milestone.py +14 -13
  326. package/skills/gsd-path-research/scripts/build_recovery.py +155 -58
  327. package/skills/gsd-path-research/scripts/check_handoffs.py +635 -57
  328. package/skills/gsd-path-research/scripts/check_task_briefs.py +293 -32
  329. package/skills/gsd-path-research/scripts/detect_project.py +260 -20
  330. package/skills/gsd-path-research/scripts/discussion_records.py +9 -1
  331. package/skills/gsd-path-research/scripts/discussion_validate.py +93 -35
  332. package/skills/gsd-path-research/scripts/integration.py +244 -65
  333. package/skills/gsd-path-research/scripts/isolation.py +539 -70
  334. package/skills/gsd-path-research/scripts/loop_run.py +16 -4
  335. package/skills/gsd-path-research/scripts/members.py +77 -32
  336. package/skills/gsd-path-research/scripts/model_policy.py +5 -1
  337. package/skills/gsd-path-research/scripts/path_config.py +3 -3
  338. package/skills/gsd-path-research/scripts/pipeline_git.py +131 -42
  339. package/skills/gsd-path-research/scripts/pipeline_state.py +139 -39
  340. package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -3
  341. package/skills/gsd-path-research/scripts/roadmap.py +9 -1
  342. package/skills/gsd-path-research/scripts/state_checkpoint.py +39 -36
  343. package/skills/gsd-path-research/scripts/state_promote.py +15 -4
  344. package/skills/gsd-path-research/scripts/worktree_paths.py +54 -2
  345. package/skills/gsd-path-research/templates/research-handoff.md +14 -3
  346. package/skills/gsd-path-roadmap/SKILL.md +16 -6
  347. package/skills/gsd-path-roadmap/references/dispatch.md +13 -0
  348. package/skills/gsd-path-roadmap/references/operating-rules.md +11 -3
  349. package/skills/gsd-path-roadmap/references/runtime-selection.md +9 -0
  350. package/skills/gsd-path-roadmap/scripts/_common.py +256 -2
  351. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +14 -13
  352. package/skills/gsd-path-roadmap/scripts/build_recovery.py +155 -58
  353. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +635 -57
  354. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +293 -32
  355. package/skills/gsd-path-roadmap/scripts/detect_project.py +260 -20
  356. package/skills/gsd-path-roadmap/scripts/discussion_records.py +9 -1
  357. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +93 -35
  358. package/skills/gsd-path-roadmap/scripts/integration.py +244 -65
  359. package/skills/gsd-path-roadmap/scripts/isolation.py +539 -70
  360. package/skills/gsd-path-roadmap/scripts/loop_run.py +16 -4
  361. package/skills/gsd-path-roadmap/scripts/members.py +77 -32
  362. package/skills/gsd-path-roadmap/scripts/model_policy.py +5 -1
  363. package/skills/gsd-path-roadmap/scripts/path_config.py +3 -3
  364. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +131 -42
  365. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +139 -39
  366. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -3
  367. package/skills/gsd-path-roadmap/scripts/roadmap.py +9 -1
  368. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +39 -36
  369. package/skills/gsd-path-roadmap/scripts/state_promote.py +15 -4
  370. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +54 -2
  371. package/skills/gsd-path-ship/SKILL.md +32 -5
  372. package/skills/gsd-path-ship/references/dispatch.md +13 -0
  373. package/skills/gsd-path-ship/references/operating-rules.md +11 -3
  374. package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
  375. package/skills/gsd-path-ship/references/reviewer-wave.md +56 -12
  376. package/skills/gsd-path-ship/references/reviewer.md +3 -1
  377. package/skills/gsd-path-ship/references/runtime-selection.md +9 -0
  378. package/skills/gsd-path-ship/references/spec-probes.md +150 -0
  379. package/skills/gsd-path-ship/scripts/_common.py +256 -2
  380. package/skills/gsd-path-ship/scripts/archive_milestone.py +14 -13
  381. package/skills/gsd-path-ship/scripts/build_recovery.py +155 -58
  382. package/skills/gsd-path-ship/scripts/build_state.py +274 -8
  383. package/skills/gsd-path-ship/scripts/check_handoffs.py +635 -57
  384. package/skills/gsd-path-ship/scripts/check_task_briefs.py +293 -32
  385. package/skills/gsd-path-ship/scripts/detect_project.py +260 -20
  386. package/skills/gsd-path-ship/scripts/discussion_records.py +9 -1
  387. package/skills/gsd-path-ship/scripts/discussion_validate.py +93 -35
  388. package/skills/gsd-path-ship/scripts/integration.py +244 -65
  389. package/skills/gsd-path-ship/scripts/isolation.py +539 -70
  390. package/skills/gsd-path-ship/scripts/loop_run.py +16 -4
  391. package/skills/gsd-path-ship/scripts/members.py +77 -32
  392. package/skills/gsd-path-ship/scripts/model_policy.py +5 -1
  393. package/skills/gsd-path-ship/scripts/path_config.py +3 -3
  394. package/skills/gsd-path-ship/scripts/pipeline_git.py +131 -42
  395. package/skills/gsd-path-ship/scripts/pipeline_state.py +139 -39
  396. package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -3
  397. package/skills/gsd-path-ship/scripts/roadmap.py +9 -1
  398. package/skills/gsd-path-ship/scripts/state_checkpoint.py +39 -36
  399. package/skills/gsd-path-ship/scripts/state_promote.py +15 -4
  400. package/skills/gsd-path-ship/scripts/workflow_run.py +14 -4
  401. package/skills/gsd-path-ship/scripts/worktree_paths.py +54 -2
  402. package/skills/gsd-path-ship/templates/final-review.md +11 -2
  403. package/skills/gsd-path-ship/templates/patch-findings.md +3 -3
  404. package/skills/gsd-path-ship/templates/wave-review.md +28 -9
  405. package/skills/gsd-path-undo/references/operating-rules.md +11 -3
  406. package/skills/gsd-path-undo/references/runtime-selection.md +9 -0
  407. package/skills/gsd-path-undo/scripts/_common.py +256 -2
  408. package/skills/gsd-path-undo/scripts/discussion_records.py +9 -1
  409. package/skills/gsd-path-undo/scripts/loop_run.py +16 -4
  410. package/skills/gsd-path-undo/scripts/model_policy.py +5 -1
  411. package/skills/gsd-path-undo/scripts/path_config.py +3 -3
  412. package/skills/gsd-path-undo/scripts/pipeline_undo.py +118 -16
  413. package/skills/gsd-path-undo/scripts/worktree_paths.py +54 -2
  414. package/skills/path/DEFINE.md +34 -5
  415. package/skills/path/PLAN.md +14 -3
  416. package/skills/path/RESEARCH.md +11 -0
  417. package/skills/path/ROADMAP.md +16 -6
  418. package/skills/path/SHIP.md +32 -5
  419. package/skills/path/SKILL.md +52 -24
  420. package/skills/path/references/build-native.md +112 -10
  421. package/skills/path/references/build-recovery.md +13 -0
  422. package/skills/path/references/coder.md +7 -3
  423. package/skills/path/references/dispatch.md +13 -0
  424. package/skills/path/references/docs-auditor.md +5 -0
  425. package/skills/path/references/operating-rules.md +11 -3
  426. package/skills/path/references/planner.md +26 -4
  427. package/skills/path/references/reviewer-final.md +20 -2
  428. package/skills/path/references/reviewer-wave.md +56 -12
  429. package/skills/path/references/reviewer.md +3 -1
  430. package/skills/path/references/runtime-selection.md +9 -0
  431. package/skills/path/references/spec-probes.md +150 -0
  432. package/skills/path/scripts/_common.py +256 -2
  433. package/skills/path/scripts/archive_milestone.py +14 -13
  434. package/skills/path/scripts/build_recovery.py +155 -58
  435. package/skills/path/scripts/build_state.py +274 -8
  436. package/skills/path/scripts/check_docs_audit.py +77 -4
  437. package/skills/path/scripts/check_handoffs.py +635 -57
  438. package/skills/path/scripts/check_task_briefs.py +293 -32
  439. package/skills/path/scripts/detect_project.py +260 -20
  440. package/skills/path/scripts/discussion_records.py +9 -1
  441. package/skills/path/scripts/discussion_validate.py +93 -35
  442. package/skills/path/scripts/dispatch_driver.py +549 -53
  443. package/skills/path/scripts/git_guard.py +27 -8
  444. package/skills/path/scripts/integration.py +244 -65
  445. package/skills/path/scripts/isolation.py +539 -70
  446. package/skills/path/scripts/loop_run.py +16 -4
  447. package/skills/path/scripts/members.py +77 -32
  448. package/skills/path/scripts/model_policy.py +5 -1
  449. package/skills/path/scripts/path_config.py +3 -3
  450. package/skills/path/scripts/pipeline_git.py +131 -42
  451. package/skills/path/scripts/pipeline_state.py +139 -39
  452. package/skills/path/scripts/promote_lookahead.py +3 -3
  453. package/skills/path/scripts/review_findings.py +180 -44
  454. package/skills/path/scripts/roadmap.py +9 -1
  455. package/skills/path/scripts/state_checkpoint.py +39 -36
  456. package/skills/path/scripts/state_promote.py +15 -4
  457. package/skills/path/scripts/status_runtime.py +22 -4
  458. package/skills/path/scripts/task_context.py +14 -3
  459. package/skills/path/scripts/workflow_run.py +14 -4
  460. package/skills/path/scripts/worktree_paths.py +54 -2
  461. package/skills/path/templates/docs-audit.md +5 -1
  462. package/skills/path/templates/final-review.md +11 -2
  463. package/skills/path/templates/intent.md +30 -0
  464. package/skills/path/templates/patch-findings.md +3 -3
  465. package/skills/path/templates/plan.md +17 -2
  466. package/skills/path/templates/research-handoff.md +14 -3
  467. package/skills/path/templates/state.md +2 -1
  468. package/skills/path/templates/task.md +1 -0
  469. package/skills/path/templates/wave-review.md +28 -9
  470. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  471. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  472. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  473. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  474. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  475. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  476. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
@@ -53,6 +53,9 @@ COMMENT_PATTERN = re.compile(r"<!--.*?-->", re.DOTALL)
53
53
  BACKTICK_PATTERN = re.compile(r"`([^`\n]+)`")
54
54
  PATH_TOKEN_PATTERN = re.compile(r"[A-Za-z0-9._~/-]+")
55
55
  VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
56
+ TEXT_TOOL_COMMANDS = frozenset({"awk", "egrep", "fgrep", "gawk", "grep", "gsed", "rg", "sed"})
57
+ REGEX_PATTERN_CHARS = frozenset("|*+?[]()^$.\\")
58
+ PIPELINE_WORKSPACE_DIRS = ("review", "discuss", "build", "plan")
56
59
 
57
60
 
58
61
  class BriefError(RuntimeError):
@@ -73,10 +76,20 @@ def _base_exists(repo: Path, base: str, path: str) -> bool:
73
76
  return _run_git(repo, "cat-file", "-e", f"{base}:{path}").returncode == 0
74
77
 
75
78
 
79
+ def _project_track(task: Path, repo: Path) -> Optional[str]:
80
+ try:
81
+ track = task.parent.parent.relative_to(repo).as_posix()
82
+ except ValueError:
83
+ return None
84
+ if track not in {".project", ".project/next"}:
85
+ return None
86
+ return track
87
+
88
+
76
89
  def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
77
90
  """Plan approval checkpoints these supplied inputs after the brief gate."""
78
- track = task.parent.parent.relative_to(repo).as_posix()
79
- if track not in {".project", ".project/next"}:
91
+ track = _project_track(task, repo)
92
+ if track is None:
80
93
  return False
81
94
  if token not in {
82
95
  f"{track}/intent/INTENT.md",
@@ -87,6 +100,20 @@ def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
87
100
  return path.is_file() and path.resolve() == repo.resolve() / token
88
101
 
89
102
 
103
+ def _pipeline_workspace_path(repo: Path, task: Path, token: str) -> bool:
104
+ track = _project_track(task, repo)
105
+ return track is not None and token.startswith(
106
+ tuple(f"{track}/{name}/" for name in PIPELINE_WORKSPACE_DIRS))
107
+
108
+
109
+ def _pipeline_workspace_file(repo: Path, task: Path, token: str) -> bool:
110
+ """Pipeline-owned paths may exist only in the working tree before checkpoint."""
111
+ if not _pipeline_workspace_path(repo, task, token):
112
+ return False
113
+ path = repo / token
114
+ return path.is_file() and path.resolve() == repo.resolve() / token
115
+
116
+
90
117
  _strip_yaml_comment = _common.strip_yaml_comment
91
118
  _unquote = _common.unquote
92
119
 
@@ -151,7 +178,7 @@ def _clean(body: str) -> str:
151
178
  return COMMENT_PATTERN.sub("", body).strip()
152
179
 
153
180
 
154
- def _looks_like_path(token: str) -> bool:
181
+ def _slash_token_shape(token: str) -> bool:
155
182
  return (
156
183
  "/" in token
157
184
  and PATH_TOKEN_PATTERN.fullmatch(token) is not None
@@ -162,32 +189,97 @@ def _looks_like_path(token: str) -> bool:
162
189
  )
163
190
 
164
191
 
192
+ def _last_segment_has_extension(token: str) -> bool:
193
+ name = PurePosixPath(token).name
194
+ return "." in name and not name.startswith(".")
195
+
196
+
197
+ def _looks_like_path(
198
+ token: str,
199
+ *,
200
+ repo: Path,
201
+ git_root: Path,
202
+ base: str,
203
+ task: Path,
204
+ ) -> bool:
205
+ if not _slash_token_shape(token):
206
+ return False
207
+ if _last_segment_has_extension(token):
208
+ return True
209
+ if _base_exists(git_root, base, token):
210
+ return True
211
+ if _pipeline_workspace_file(repo, task, token):
212
+ return True
213
+ return False
214
+
215
+
216
+ def _looks_like_regex_pattern(token: str) -> bool:
217
+ return any(character in token for character in REGEX_PATTERN_CHARS)
218
+
219
+
220
+ def _text_tool_command(command: str) -> bool:
221
+ return Path(command).name.casefold() in TEXT_TOOL_COMMANDS
222
+
223
+
165
224
  def _normalize(path: str) -> str:
166
225
  return str(PurePosixPath(path))
167
226
 
168
227
 
169
- def _prose_tokens(body: str) -> List[str]:
228
+ def _prose_tokens(
229
+ body: str,
230
+ *,
231
+ repo: Path,
232
+ git_root: Path,
233
+ base: str,
234
+ task: Path,
235
+ ) -> List[str]:
170
236
  cleaned = COMMENT_PATTERN.sub("", body)
171
- return [
172
- _normalize(match.group(1).strip())
173
- for match in BACKTICK_PATTERN.finditer(cleaned)
174
- if _looks_like_path(match.group(1).strip())
175
- ]
237
+ tokens: List[str] = []
238
+ for match in BACKTICK_PATTERN.finditer(cleaned):
239
+ candidate = match.group(1).strip()
240
+ if _looks_like_path(
241
+ candidate, repo=repo, git_root=git_root, base=base, task=task
242
+ ):
243
+ tokens.append(_normalize(candidate))
244
+ return tokens
176
245
 
177
246
 
178
- def _verify_tokens(block: str) -> List[str]:
179
- tokens = []
247
+ def _verify_tokens(
248
+ block: str,
249
+ *,
250
+ repo: Path,
251
+ git_root: Path,
252
+ base: str,
253
+ task: Path,
254
+ ) -> List[str]:
255
+ tokens: List[str] = []
180
256
  for line in block.splitlines():
181
257
  parts = line.split()
182
- for token in parts[1:]:
183
- token = token.strip("\"'")
184
- if _looks_like_path(token):
258
+ if len(parts) < 2:
259
+ continue
260
+ command = parts[0]
261
+ extended_grep = False
262
+ index = 1
263
+ while index < len(parts) and parts[index].startswith("-") and "=" not in parts[index]:
264
+ flag = parts[index]
265
+ if flag in {"-E", "-G"} or flag.startswith("-E"):
266
+ extended_grep = True
267
+ index += 1
268
+ text_tool = _text_tool_command(command)
269
+ for raw in parts[index:]:
270
+ quoted = raw.startswith(("'", '"'))
271
+ token = raw.strip("\"'")
272
+ if text_tool and quoted:
273
+ continue
274
+ if text_tool and command.casefold() == "grep" and extended_grep and _looks_like_regex_pattern(token):
275
+ continue
276
+ if _looks_like_path(token, repo=repo, git_root=git_root, base=base, task=task):
185
277
  tokens.append(_normalize(token))
186
278
  return tokens
187
279
 
188
280
 
189
281
  def _member_bases(root: Path):
190
- """Resolve a member name to (checkout, origin/main SHA), reading MEMBERS.md once."""
282
+ """Resolve a member name to (checkout, base SHA), reading MEMBERS.md once."""
191
283
  cache: Dict[str, Optional[Tuple[Path, str]]] = {}
192
284
 
193
285
  def locate(name: str) -> Optional[Tuple[Path, str]]:
@@ -200,21 +292,22 @@ def _member_bases(root: Path):
200
292
  except ImportError: # pragma: no cover - package import used by tests
201
293
  from scripts import members
202
294
  try:
203
- listed = {member["name"]: Path(member["checkout"]) for member in members.read_members(root)}
204
- checkout = listed.get(name)
205
- if checkout is None:
295
+ listed = {member["name"]: member for member in members.read_members(root)}
296
+ if name not in listed:
206
297
  cache[name] = None
207
298
  else:
299
+ checkout = Path(listed[name]["checkout"])
208
300
  # A build lock selects the bound tip, including during Plan recovery;
209
- # without the lock, use origin/main before the first build start.
301
+ # without the lock, use the member's recorded default branch on origin
302
+ # before the first build start.
210
303
  lock = root / members.LOCK_PATH
211
304
  locked = [entry for entry in (json.loads(lock.read_text(encoding="utf-8"))["members"]
212
305
  if lock.is_file() else []) if entry["name"] == name]
213
306
  if locked:
214
307
  cache[name] = (checkout, _resolve_base(checkout, f"refs/heads/{locked[0]['branch']}"))
215
308
  else:
216
- members.require_origin_main(checkout)
217
- cache[name] = (checkout, _resolve_base(checkout, "refs/remotes/origin/main"))
309
+ cache[name] = (checkout, members.require_origin_branch(
310
+ checkout, members.default_branch(listed[name])))
218
311
  except (members.MembersError, BriefError) as error:
219
312
  raise BriefError(f"member {name} is unavailable: {error}") from error
220
313
  return cache[name]
@@ -238,8 +331,8 @@ def _lint_task(
238
331
  checked = 0
239
332
  if fields is None:
240
333
  return task_id, [error or "invalid frontmatter"], None, checked
241
- # A member task's paths resolve in the member at its origin/main, where the
242
- # member bound branch starts; `repo:` absent means the coordinator.
334
+ # A member task's paths resolve in the member at its default branch on origin,
335
+ # where the member bound branch starts; `repo:` absent means the coordinator.
243
336
  git_root = repo
244
337
  member = fields.get("repo")
245
338
  if member is not None:
@@ -298,12 +391,17 @@ def _lint_task(
298
391
  body = sections.get(name)
299
392
  if body is None or not _clean(body):
300
393
  continue
301
- for token in _prose_tokens(body):
394
+ for token in _prose_tokens(
395
+ body, repo=repo, git_root=git_root, base=base, task=path
396
+ ):
302
397
  checked += 1
303
398
  if (
304
399
  token not in declared
305
400
  and token not in supplied
306
401
  and not _supplied_contract(repo, path, token)
402
+ and not _pipeline_workspace_file(repo, path, token)
403
+ # A landed brief is history: the pipeline moves its own files (build recovery, archive) later.
404
+ and not (task_id in (landed_bases or {}) and _pipeline_workspace_path(repo, path, token))
307
405
  and not _base_exists(git_root, base, token)
308
406
  ):
309
407
  problems.append(f"## {name} names a path missing at the layer base: {token}")
@@ -314,9 +412,20 @@ def _lint_task(
314
412
  if block is None or not block.group("block").strip():
315
413
  problems.append("## Verify must contain a non-empty fenced bash block")
316
414
  else:
317
- for token in _verify_tokens(block.group("block")):
415
+ for token in _verify_tokens(
416
+ block.group("block"),
417
+ repo=repo,
418
+ git_root=git_root,
419
+ base=base,
420
+ task=path,
421
+ ):
318
422
  checked += 1
319
- if token not in declared and token not in supplied and not _base_exists(git_root, base, token):
423
+ if (
424
+ token not in declared
425
+ and token not in supplied
426
+ and not _pipeline_workspace_file(repo, path, token)
427
+ and not _base_exists(git_root, base, token)
428
+ ):
320
429
  problems.append(f"## Verify names a path missing at the layer base: {token}")
321
430
 
322
431
  contract = None
@@ -337,9 +446,9 @@ def validate_task_briefs(
337
446
  ) -> Dict[str, object]:
338
447
  """Lint task briefs using the caller's validation bases.
339
448
 
340
- Dispatch uses the layer base without overrides. Plan approval supplies
341
- transitive dependency files for pending tasks and resolved historical bases
342
- for landed tasks, whose immutable briefs must survive later file changes.
449
+ Plain lint uses the layer base without overrides. Plan approval and dispatch
450
+ supply transitive dependency files for unlanded tasks and resolved historical
451
+ bases for landed tasks, whose immutable briefs must survive later file changes.
343
452
  The caller validates the task graph and landed metadata before supplying
344
453
  these mappings; landed tasks receive no dependency-file allowance.
345
454
  """
@@ -381,6 +490,136 @@ def validate_task_briefs(
381
490
  return {"base": resolved_base, "checked": checked, "tasks": len(task_files)}
382
491
 
383
492
 
493
+ def _resolve_landed_task_base(
494
+ task_id: str,
495
+ recorded_base: str,
496
+ recorded_member_base: object,
497
+ coordinator_repo: Path,
498
+ member_repo: Optional[Path],
499
+ ) -> str:
500
+ """Resolve a landed task's historical base in the repository it names."""
501
+ if __package__:
502
+ from .isolation import IsolationError, require_commit, require_full_sha
503
+ else:
504
+ try:
505
+ from isolation import IsolationError, require_commit, require_full_sha
506
+ except ModuleNotFoundError as error:
507
+ if error.name != "isolation":
508
+ raise
509
+ from scripts.isolation import IsolationError, require_commit, require_full_sha
510
+
511
+ recorded_full = require_full_sha(recorded_base)
512
+ if member_repo is None:
513
+ try:
514
+ return require_commit(coordinator_repo, recorded_full)
515
+ except IsolationError as error:
516
+ raise BriefError(
517
+ f"{task_id} landed task has invalid historical base: {error}"
518
+ ) from error
519
+
520
+ legacy_member_base = (
521
+ recorded_member_base is None
522
+ or (isinstance(recorded_member_base, str)
523
+ and recorded_member_base.strip().casefold() in {"", "null", "~"})
524
+ or (isinstance(recorded_member_base, list) and not recorded_member_base)
525
+ )
526
+ try:
527
+ if not legacy_member_base:
528
+ if not isinstance(recorded_member_base, str):
529
+ raise IsolationError(
530
+ f"member_base must be a full 40-character SHA: {recorded_member_base}"
531
+ )
532
+ member_full = require_full_sha(recorded_member_base)
533
+ return require_commit(member_repo, member_full)
534
+ try:
535
+ return require_commit(coordinator_repo, recorded_full)
536
+ except IsolationError:
537
+ return require_commit(member_repo, recorded_full)
538
+ except IsolationError as error:
539
+ raise BriefError(
540
+ f"{task_id} landed task has invalid historical base: {error}"
541
+ ) from error
542
+
543
+ def plan_validation_bases(
544
+ root: Path, project_dir: str, member_base=None, *, initial: bool = True,
545
+ ) -> Tuple[Dict[str, str], Dict[str, Set[str]], Dict[str, str]]:
546
+ """Share dependency files and landed historical bases across approval and dispatch.
547
+
548
+ Approval requires unlanded tasks to start clean. Dispatch may run beside
549
+ in-flight tasks, while retaining the same graph and path checks.
550
+ """
551
+ if __package__:
552
+ from . import check_handoffs
553
+ from .isolation import IsolationError
554
+ else:
555
+ try:
556
+ import check_handoffs
557
+ from isolation import IsolationError
558
+ except ModuleNotFoundError as error:
559
+ if error.name not in {"check_handoffs", "isolation"}:
560
+ raise
561
+ from scripts import check_handoffs
562
+ from scripts.isolation import IsolationError
563
+ if member_base is None:
564
+ member_base = _member_bases(root)
565
+ try:
566
+ tasks, dependency_files = check_handoffs.plan_brief_inputs(
567
+ root, project_dir, initial=initial
568
+ )
569
+ landed_bases: Dict[str, str] = {}
570
+ for task_id, text in tasks.items():
571
+ if check_handoffs._task_scalar(text, task_id, "status") != "done":
572
+ continue
573
+ agent = check_handoffs._task_scalar(text, task_id, "agent")
574
+ if agent in {"", "null"}:
575
+ raise BriefError(f"{task_id} landed task has no recorded agent")
576
+ fields = check_handoffs._strict_frontmatter(text, task_id)
577
+ member = fields.get("repo")
578
+ base_repo = root
579
+ if member is not None:
580
+ located = member_base(member) if isinstance(member, str) and member else None
581
+ if located is None:
582
+ raise BriefError(f"repo: names no member in MEMBERS.md: {member}")
583
+ base_repo = located[0]
584
+ recorded_base = check_handoffs._task_scalar(text, task_id, "base")
585
+ try:
586
+ landed_bases[task_id] = _resolve_landed_task_base(
587
+ task_id,
588
+ recorded_base,
589
+ fields.get("member_base") if member is not None else None,
590
+ root,
591
+ base_repo if member is not None else None,
592
+ )
593
+ except IsolationError as error:
594
+ raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
595
+ dependency_files[task_id] = set()
596
+ except check_handoffs.HandoffError as error:
597
+ raise BriefError(str(error)) from error
598
+ return tasks, dependency_files, landed_bases
599
+
600
+
601
+ def validate_plan_task_briefs(
602
+ root: Path,
603
+ base: str,
604
+ project_dir: str = ".project",
605
+ tasks_dir: Optional[str] = None,
606
+ ) -> Dict[str, object]:
607
+ """Dispatch lint with dependency files and landed historical bases."""
608
+ member_base = _member_bases(root)
609
+ _, dependency_files, landed_bases = plan_validation_bases(
610
+ root, project_dir, member_base, initial=False
611
+ )
612
+ resolved_tasks_dir = tasks_dir or f"{project_dir}/tasks"
613
+ return validate_task_briefs(
614
+ root,
615
+ base,
616
+ resolved_tasks_dir,
617
+ dependency_files=dependency_files,
618
+ landed_bases=landed_bases,
619
+ member_base=member_base,
620
+ )
621
+
622
+
384
623
  def _tasks_dir(value: str) -> str:
385
624
  """Argparse type for --tasks-dir: a relative POSIX-style path."""
386
625
 
@@ -411,15 +650,37 @@ def parser() -> argparse.ArgumentParser:
411
650
  help="relative POSIX-style directory under --repo holding the task files "
412
651
  "(default: .project/tasks)",
413
652
  )
653
+ argument_parser.add_argument(
654
+ "--project-dir",
655
+ default=None,
656
+ help="apply plan dependency files and landed-task bases for this track "
657
+ "(inferred from --tasks-dir when its sibling plan/PLAN.md exists)",
658
+ )
414
659
  return argument_parser
415
660
 
416
661
 
417
662
  def main(argv: Optional[Sequence[str]] = None) -> int:
418
663
  arguments = parser().parse_args(argv)
419
664
  try:
420
- result = validate_task_briefs(
421
- arguments.repo.resolve(), arguments.base, arguments.tasks_dir
422
- )
665
+ root = arguments.repo.resolve()
666
+ project_dir = arguments.project_dir
667
+ if project_dir is None:
668
+ tasks_path = PurePosixPath(arguments.tasks_dir)
669
+ if tasks_path.name == "tasks" and len(tasks_path.parts) > 1:
670
+ track = tasks_path.parent.as_posix()
671
+ if (root / track / "plan/PLAN.md").is_file():
672
+ project_dir = track
673
+ if project_dir is not None:
674
+ result = validate_plan_task_briefs(
675
+ root,
676
+ arguments.base,
677
+ project_dir,
678
+ None if arguments.tasks_dir == DEFAULT_TASKS_DIR else arguments.tasks_dir,
679
+ )
680
+ else:
681
+ result = validate_task_briefs(
682
+ root, arguments.base, arguments.tasks_dir
683
+ )
423
684
  except BriefError as error:
424
685
  print(f"task brief validation failed: {error}", file=sys.stderr)
425
686
  return 1