@opengsd/gsd-path 1.5.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 (352) hide show
  1. package/CHANGELOG.md +138 -0
  2. package/DOCS.md +4 -3
  3. package/FULL.md +64 -4
  4. package/HOOKS.md +63 -2
  5. package/MIGRATE.md +12 -0
  6. package/QUICK.md +4 -2
  7. package/README.md +67 -15
  8. package/RUNTIME.md +23 -5
  9. package/UPDATE.md +35 -0
  10. package/WORKFLOW.md +58 -3
  11. package/package.json +1 -1
  12. package/scripts/_common.py +107 -1
  13. package/scripts/build_recovery.py +62 -30
  14. package/scripts/build_state.py +195 -5
  15. package/scripts/check_handoffs.py +92 -30
  16. package/scripts/check_task_briefs.py +19 -14
  17. package/scripts/detect_project.py +17 -2
  18. package/scripts/discussion_records.py +9 -1
  19. package/scripts/discussion_validate.py +7 -6
  20. package/scripts/dispatch_driver.py +157 -31
  21. package/scripts/git_guard.py +24 -5
  22. package/scripts/guard_hook.py +1216 -47
  23. package/scripts/install.mjs +76 -3
  24. package/scripts/install.py +182 -37
  25. package/scripts/integration.py +98 -64
  26. package/scripts/isolation.py +148 -11
  27. package/scripts/members.py +68 -25
  28. package/scripts/migrate_core.py +2 -0
  29. package/scripts/model_policy.py +5 -1
  30. package/scripts/pipeline_diagnose.py +17 -2
  31. package/scripts/pipeline_git.py +68 -30
  32. package/scripts/pipeline_state.py +28 -6
  33. package/scripts/pipeline_undo.py +17 -8
  34. package/scripts/promote_lookahead.py +3 -3
  35. package/scripts/review_findings.py +180 -44
  36. package/scripts/roadmap.py +9 -1
  37. package/scripts/runtime_store.py +1 -0
  38. package/scripts/state_checkpoint.py +30 -9
  39. package/scripts/state_promote.py +15 -4
  40. package/scripts/task_context.py +2 -0
  41. package/scripts/workflow_run.py +9 -2
  42. package/skills/gsd-path/DEFINE.md +6 -2
  43. package/skills/gsd-path/PLAN.md +3 -0
  44. package/skills/gsd-path/ROADMAP.md +16 -6
  45. package/skills/gsd-path/SHIP.md +26 -4
  46. package/skills/gsd-path/SKILL.md +52 -24
  47. package/skills/gsd-path/references/build-native.md +96 -9
  48. package/skills/gsd-path/references/build-recovery.md +13 -0
  49. package/skills/gsd-path/references/coder.md +7 -3
  50. package/skills/gsd-path/references/operating-rules.md +11 -3
  51. package/skills/gsd-path/references/planner.md +13 -1
  52. package/skills/gsd-path/references/reviewer-final.md +5 -5
  53. package/skills/gsd-path/references/reviewer-wave.md +46 -13
  54. package/skills/gsd-path/references/runtime-selection.md +9 -0
  55. package/skills/gsd-path/scripts/_common.py +107 -1
  56. package/skills/gsd-path/scripts/build_recovery.py +62 -30
  57. package/skills/gsd-path/scripts/build_state.py +195 -5
  58. package/skills/gsd-path/scripts/check_handoffs.py +92 -30
  59. package/skills/gsd-path/scripts/check_task_briefs.py +19 -14
  60. package/skills/gsd-path/scripts/detect_project.py +17 -2
  61. package/skills/gsd-path/scripts/discussion_records.py +9 -1
  62. package/skills/gsd-path/scripts/discussion_validate.py +7 -6
  63. package/skills/gsd-path/scripts/dispatch_driver.py +157 -31
  64. package/skills/gsd-path/scripts/git_guard.py +24 -5
  65. package/skills/gsd-path/scripts/integration.py +98 -64
  66. package/skills/gsd-path/scripts/isolation.py +148 -11
  67. package/skills/gsd-path/scripts/members.py +68 -25
  68. package/skills/gsd-path/scripts/model_policy.py +5 -1
  69. package/skills/gsd-path/scripts/pipeline_git.py +68 -30
  70. package/skills/gsd-path/scripts/pipeline_state.py +28 -6
  71. package/skills/gsd-path/scripts/promote_lookahead.py +3 -3
  72. package/skills/gsd-path/scripts/review_findings.py +180 -44
  73. package/skills/gsd-path/scripts/roadmap.py +9 -1
  74. package/skills/gsd-path/scripts/state_checkpoint.py +30 -9
  75. package/skills/gsd-path/scripts/state_promote.py +15 -4
  76. package/skills/gsd-path/scripts/task_context.py +2 -0
  77. package/skills/gsd-path/scripts/workflow_run.py +9 -2
  78. package/skills/gsd-path/templates/final-review.md +5 -5
  79. package/skills/gsd-path/templates/state.md +1 -0
  80. package/skills/gsd-path/templates/task.md +1 -0
  81. package/skills/gsd-path/templates/wave-review.md +18 -9
  82. package/skills/gsd-path-build/references/build-native.md +96 -9
  83. package/skills/gsd-path-build/references/build-recovery.md +13 -0
  84. package/skills/gsd-path-build/references/coder.md +7 -3
  85. package/skills/gsd-path-build/references/operating-rules.md +11 -3
  86. package/skills/gsd-path-build/references/reviewer-final.md +5 -5
  87. package/skills/gsd-path-build/references/reviewer-wave.md +46 -13
  88. package/skills/gsd-path-build/references/runtime-selection.md +9 -0
  89. package/skills/gsd-path-build/scripts/_common.py +107 -1
  90. package/skills/gsd-path-build/scripts/build_recovery.py +62 -30
  91. package/skills/gsd-path-build/scripts/build_state.py +195 -5
  92. package/skills/gsd-path-build/scripts/check_handoffs.py +92 -30
  93. package/skills/gsd-path-build/scripts/check_task_briefs.py +19 -14
  94. package/skills/gsd-path-build/scripts/detect_project.py +17 -2
  95. package/skills/gsd-path-build/scripts/discussion_records.py +9 -1
  96. package/skills/gsd-path-build/scripts/discussion_validate.py +7 -6
  97. package/skills/gsd-path-build/scripts/dispatch_driver.py +157 -31
  98. package/skills/gsd-path-build/scripts/integration.py +98 -64
  99. package/skills/gsd-path-build/scripts/isolation.py +148 -11
  100. package/skills/gsd-path-build/scripts/members.py +68 -25
  101. package/skills/gsd-path-build/scripts/model_policy.py +5 -1
  102. package/skills/gsd-path-build/scripts/pipeline_git.py +68 -30
  103. package/skills/gsd-path-build/scripts/pipeline_state.py +28 -6
  104. package/skills/gsd-path-build/scripts/promote_lookahead.py +3 -3
  105. package/skills/gsd-path-build/scripts/review_findings.py +180 -44
  106. package/skills/gsd-path-build/scripts/roadmap.py +9 -1
  107. package/skills/gsd-path-build/scripts/state_checkpoint.py +30 -9
  108. package/skills/gsd-path-build/scripts/state_promote.py +15 -4
  109. package/skills/gsd-path-build/scripts/task_context.py +2 -0
  110. package/skills/gsd-path-build/scripts/workflow_run.py +9 -2
  111. package/skills/gsd-path-build/templates/task.md +1 -0
  112. package/skills/gsd-path-build/templates/wave-review.md +18 -9
  113. package/skills/gsd-path-decide/references/operating-rules.md +11 -3
  114. package/skills/gsd-path-decide/references/runtime-selection.md +9 -0
  115. package/skills/gsd-path-decide/scripts/_common.py +107 -1
  116. package/skills/gsd-path-decide/scripts/build_recovery.py +62 -30
  117. package/skills/gsd-path-decide/scripts/check_handoffs.py +92 -30
  118. package/skills/gsd-path-decide/scripts/check_task_briefs.py +19 -14
  119. package/skills/gsd-path-decide/scripts/detect_project.py +17 -2
  120. package/skills/gsd-path-decide/scripts/discussion_records.py +9 -1
  121. package/skills/gsd-path-decide/scripts/discussion_validate.py +7 -6
  122. package/skills/gsd-path-decide/scripts/integration.py +98 -64
  123. package/skills/gsd-path-decide/scripts/isolation.py +148 -11
  124. package/skills/gsd-path-decide/scripts/members.py +68 -25
  125. package/skills/gsd-path-decide/scripts/model_policy.py +5 -1
  126. package/skills/gsd-path-decide/scripts/pipeline_git.py +68 -30
  127. package/skills/gsd-path-decide/scripts/pipeline_state.py +28 -6
  128. package/skills/gsd-path-decide/scripts/promote_lookahead.py +3 -3
  129. package/skills/gsd-path-decide/scripts/roadmap.py +9 -1
  130. package/skills/gsd-path-decide/scripts/state_checkpoint.py +30 -9
  131. package/skills/gsd-path-decide/scripts/state_promote.py +15 -4
  132. package/skills/gsd-path-define/SKILL.md +6 -2
  133. package/skills/gsd-path-define/references/build-recovery.md +13 -0
  134. package/skills/gsd-path-define/references/operating-rules.md +11 -3
  135. package/skills/gsd-path-define/references/runtime-selection.md +9 -0
  136. package/skills/gsd-path-define/scripts/_common.py +107 -1
  137. package/skills/gsd-path-define/scripts/build_recovery.py +62 -30
  138. package/skills/gsd-path-define/scripts/check_handoffs.py +92 -30
  139. package/skills/gsd-path-define/scripts/check_task_briefs.py +19 -14
  140. package/skills/gsd-path-define/scripts/detect_project.py +17 -2
  141. package/skills/gsd-path-define/scripts/discussion_records.py +9 -1
  142. package/skills/gsd-path-define/scripts/discussion_validate.py +7 -6
  143. package/skills/gsd-path-define/scripts/integration.py +98 -64
  144. package/skills/gsd-path-define/scripts/isolation.py +148 -11
  145. package/skills/gsd-path-define/scripts/members.py +68 -25
  146. package/skills/gsd-path-define/scripts/model_policy.py +5 -1
  147. package/skills/gsd-path-define/scripts/pipeline_git.py +68 -30
  148. package/skills/gsd-path-define/scripts/pipeline_state.py +28 -6
  149. package/skills/gsd-path-define/scripts/promote_lookahead.py +3 -3
  150. package/skills/gsd-path-define/scripts/roadmap.py +9 -1
  151. package/skills/gsd-path-define/scripts/state_checkpoint.py +30 -9
  152. package/skills/gsd-path-define/scripts/state_promote.py +15 -4
  153. package/skills/gsd-path-define/templates/state.md +1 -0
  154. package/skills/gsd-path-discuss/references/operating-rules.md +11 -3
  155. package/skills/gsd-path-discuss/references/runtime-selection.md +9 -0
  156. package/skills/gsd-path-discuss/scripts/_common.py +107 -1
  157. package/skills/gsd-path-discuss/scripts/discussion_records.py +9 -1
  158. package/skills/gsd-path-discuss/scripts/model_policy.py +5 -1
  159. package/skills/gsd-path-docs-audit/references/operating-rules.md +11 -3
  160. package/skills/gsd-path-docs-audit/references/runtime-selection.md +9 -0
  161. package/skills/gsd-path-docs-audit/scripts/_common.py +107 -1
  162. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +62 -30
  163. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +92 -30
  164. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +19 -14
  165. package/skills/gsd-path-docs-audit/scripts/detect_project.py +17 -2
  166. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +9 -1
  167. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +7 -6
  168. package/skills/gsd-path-docs-audit/scripts/integration.py +98 -64
  169. package/skills/gsd-path-docs-audit/scripts/isolation.py +148 -11
  170. package/skills/gsd-path-docs-audit/scripts/members.py +68 -25
  171. package/skills/gsd-path-docs-audit/scripts/model_policy.py +5 -1
  172. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +68 -30
  173. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +28 -6
  174. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +3 -3
  175. package/skills/gsd-path-docs-audit/scripts/roadmap.py +9 -1
  176. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +30 -9
  177. package/skills/gsd-path-docs-audit/scripts/state_promote.py +15 -4
  178. package/skills/gsd-path-forensics/references/operating-rules.md +11 -3
  179. package/skills/gsd-path-forensics/references/runtime-selection.md +9 -0
  180. package/skills/gsd-path-forensics/scripts/_common.py +107 -1
  181. package/skills/gsd-path-forensics/scripts/discussion_records.py +9 -1
  182. package/skills/gsd-path-forensics/scripts/model_policy.py +5 -1
  183. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +17 -2
  184. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +17 -8
  185. package/skills/gsd-path-inspect/references/operating-rules.md +11 -3
  186. package/skills/gsd-path-inspect/references/runtime-selection.md +9 -0
  187. package/skills/gsd-path-inspect/scripts/_common.py +107 -1
  188. package/skills/gsd-path-inspect/scripts/build_recovery.py +62 -30
  189. package/skills/gsd-path-inspect/scripts/check_handoffs.py +92 -30
  190. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +19 -14
  191. package/skills/gsd-path-inspect/scripts/detect_project.py +17 -2
  192. package/skills/gsd-path-inspect/scripts/discussion_records.py +9 -1
  193. package/skills/gsd-path-inspect/scripts/discussion_validate.py +7 -6
  194. package/skills/gsd-path-inspect/scripts/integration.py +98 -64
  195. package/skills/gsd-path-inspect/scripts/isolation.py +148 -11
  196. package/skills/gsd-path-inspect/scripts/members.py +68 -25
  197. package/skills/gsd-path-inspect/scripts/model_policy.py +5 -1
  198. package/skills/gsd-path-inspect/scripts/pipeline_git.py +68 -30
  199. package/skills/gsd-path-inspect/scripts/pipeline_state.py +28 -6
  200. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +3 -3
  201. package/skills/gsd-path-inspect/scripts/roadmap.py +9 -1
  202. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +30 -9
  203. package/skills/gsd-path-inspect/scripts/state_promote.py +15 -4
  204. package/skills/gsd-path-inspect/scripts/workflow_run.py +9 -2
  205. package/skills/gsd-path-inspect/templates/state.md +1 -0
  206. package/skills/gsd-path-loop/references/operating-rules.md +11 -3
  207. package/skills/gsd-path-loop/references/runtime-selection.md +9 -0
  208. package/skills/gsd-path-loop/scripts/_common.py +107 -1
  209. package/skills/gsd-path-loop/scripts/discussion_records.py +9 -1
  210. package/skills/gsd-path-loop/scripts/model_policy.py +5 -1
  211. package/skills/gsd-path-migrate/SKILL.md +8 -0
  212. package/skills/gsd-path-migrate/references/operating-rules.md +11 -3
  213. package/skills/gsd-path-migrate/references/runtime-selection.md +9 -0
  214. package/skills/gsd-path-migrate/scripts/_common.py +107 -1
  215. package/skills/gsd-path-migrate/scripts/discussion_records.py +9 -1
  216. package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -0
  217. package/skills/gsd-path-migrate/scripts/model_policy.py +5 -1
  218. package/skills/gsd-path-plan/SKILL.md +3 -0
  219. package/skills/gsd-path-plan/references/build-recovery.md +13 -0
  220. package/skills/gsd-path-plan/references/operating-rules.md +11 -3
  221. package/skills/gsd-path-plan/references/planner.md +13 -1
  222. package/skills/gsd-path-plan/references/runtime-selection.md +9 -0
  223. package/skills/gsd-path-plan/scripts/_common.py +107 -1
  224. package/skills/gsd-path-plan/scripts/build_recovery.py +62 -30
  225. package/skills/gsd-path-plan/scripts/check_handoffs.py +92 -30
  226. package/skills/gsd-path-plan/scripts/check_task_briefs.py +19 -14
  227. package/skills/gsd-path-plan/scripts/detect_project.py +17 -2
  228. package/skills/gsd-path-plan/scripts/discussion_records.py +9 -1
  229. package/skills/gsd-path-plan/scripts/discussion_validate.py +7 -6
  230. package/skills/gsd-path-plan/scripts/integration.py +98 -64
  231. package/skills/gsd-path-plan/scripts/isolation.py +148 -11
  232. package/skills/gsd-path-plan/scripts/members.py +68 -25
  233. package/skills/gsd-path-plan/scripts/model_policy.py +5 -1
  234. package/skills/gsd-path-plan/scripts/pipeline_git.py +68 -30
  235. package/skills/gsd-path-plan/scripts/pipeline_state.py +28 -6
  236. package/skills/gsd-path-plan/scripts/promote_lookahead.py +3 -3
  237. package/skills/gsd-path-plan/scripts/roadmap.py +9 -1
  238. package/skills/gsd-path-plan/scripts/state_checkpoint.py +30 -9
  239. package/skills/gsd-path-plan/scripts/state_promote.py +15 -4
  240. package/skills/gsd-path-plan/scripts/workflow_run.py +9 -2
  241. package/skills/gsd-path-plan/templates/task.md +1 -0
  242. package/skills/gsd-path-research/references/operating-rules.md +11 -3
  243. package/skills/gsd-path-research/references/runtime-selection.md +9 -0
  244. package/skills/gsd-path-research/scripts/_common.py +107 -1
  245. package/skills/gsd-path-research/scripts/build_recovery.py +62 -30
  246. package/skills/gsd-path-research/scripts/check_handoffs.py +92 -30
  247. package/skills/gsd-path-research/scripts/check_task_briefs.py +19 -14
  248. package/skills/gsd-path-research/scripts/detect_project.py +17 -2
  249. package/skills/gsd-path-research/scripts/discussion_records.py +9 -1
  250. package/skills/gsd-path-research/scripts/discussion_validate.py +7 -6
  251. package/skills/gsd-path-research/scripts/integration.py +98 -64
  252. package/skills/gsd-path-research/scripts/isolation.py +148 -11
  253. package/skills/gsd-path-research/scripts/members.py +68 -25
  254. package/skills/gsd-path-research/scripts/model_policy.py +5 -1
  255. package/skills/gsd-path-research/scripts/pipeline_git.py +68 -30
  256. package/skills/gsd-path-research/scripts/pipeline_state.py +28 -6
  257. package/skills/gsd-path-research/scripts/promote_lookahead.py +3 -3
  258. package/skills/gsd-path-research/scripts/roadmap.py +9 -1
  259. package/skills/gsd-path-research/scripts/state_checkpoint.py +30 -9
  260. package/skills/gsd-path-research/scripts/state_promote.py +15 -4
  261. package/skills/gsd-path-roadmap/SKILL.md +16 -6
  262. package/skills/gsd-path-roadmap/references/operating-rules.md +11 -3
  263. package/skills/gsd-path-roadmap/references/runtime-selection.md +9 -0
  264. package/skills/gsd-path-roadmap/scripts/_common.py +107 -1
  265. package/skills/gsd-path-roadmap/scripts/build_recovery.py +62 -30
  266. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +92 -30
  267. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +19 -14
  268. package/skills/gsd-path-roadmap/scripts/detect_project.py +17 -2
  269. package/skills/gsd-path-roadmap/scripts/discussion_records.py +9 -1
  270. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +7 -6
  271. package/skills/gsd-path-roadmap/scripts/integration.py +98 -64
  272. package/skills/gsd-path-roadmap/scripts/isolation.py +148 -11
  273. package/skills/gsd-path-roadmap/scripts/members.py +68 -25
  274. package/skills/gsd-path-roadmap/scripts/model_policy.py +5 -1
  275. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +68 -30
  276. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +28 -6
  277. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +3 -3
  278. package/skills/gsd-path-roadmap/scripts/roadmap.py +9 -1
  279. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +30 -9
  280. package/skills/gsd-path-roadmap/scripts/state_promote.py +15 -4
  281. package/skills/gsd-path-ship/SKILL.md +26 -4
  282. package/skills/gsd-path-ship/references/operating-rules.md +11 -3
  283. package/skills/gsd-path-ship/references/reviewer-final.md +5 -5
  284. package/skills/gsd-path-ship/references/reviewer-wave.md +46 -13
  285. package/skills/gsd-path-ship/references/runtime-selection.md +9 -0
  286. package/skills/gsd-path-ship/scripts/_common.py +107 -1
  287. package/skills/gsd-path-ship/scripts/build_recovery.py +62 -30
  288. package/skills/gsd-path-ship/scripts/build_state.py +195 -5
  289. package/skills/gsd-path-ship/scripts/check_handoffs.py +92 -30
  290. package/skills/gsd-path-ship/scripts/check_task_briefs.py +19 -14
  291. package/skills/gsd-path-ship/scripts/detect_project.py +17 -2
  292. package/skills/gsd-path-ship/scripts/discussion_records.py +9 -1
  293. package/skills/gsd-path-ship/scripts/discussion_validate.py +7 -6
  294. package/skills/gsd-path-ship/scripts/integration.py +98 -64
  295. package/skills/gsd-path-ship/scripts/isolation.py +148 -11
  296. package/skills/gsd-path-ship/scripts/members.py +68 -25
  297. package/skills/gsd-path-ship/scripts/model_policy.py +5 -1
  298. package/skills/gsd-path-ship/scripts/pipeline_git.py +68 -30
  299. package/skills/gsd-path-ship/scripts/pipeline_state.py +28 -6
  300. package/skills/gsd-path-ship/scripts/promote_lookahead.py +3 -3
  301. package/skills/gsd-path-ship/scripts/roadmap.py +9 -1
  302. package/skills/gsd-path-ship/scripts/state_checkpoint.py +30 -9
  303. package/skills/gsd-path-ship/scripts/state_promote.py +15 -4
  304. package/skills/gsd-path-ship/scripts/workflow_run.py +9 -2
  305. package/skills/gsd-path-ship/templates/final-review.md +5 -5
  306. package/skills/gsd-path-ship/templates/wave-review.md +18 -9
  307. package/skills/gsd-path-undo/references/operating-rules.md +11 -3
  308. package/skills/gsd-path-undo/references/runtime-selection.md +9 -0
  309. package/skills/gsd-path-undo/scripts/_common.py +107 -1
  310. package/skills/gsd-path-undo/scripts/discussion_records.py +9 -1
  311. package/skills/gsd-path-undo/scripts/model_policy.py +5 -1
  312. package/skills/gsd-path-undo/scripts/pipeline_undo.py +17 -8
  313. package/skills/path/DEFINE.md +6 -2
  314. package/skills/path/PLAN.md +3 -0
  315. package/skills/path/ROADMAP.md +16 -6
  316. package/skills/path/SHIP.md +26 -4
  317. package/skills/path/SKILL.md +52 -24
  318. package/skills/path/references/build-native.md +96 -9
  319. package/skills/path/references/build-recovery.md +13 -0
  320. package/skills/path/references/coder.md +7 -3
  321. package/skills/path/references/operating-rules.md +11 -3
  322. package/skills/path/references/planner.md +13 -1
  323. package/skills/path/references/reviewer-final.md +5 -5
  324. package/skills/path/references/reviewer-wave.md +46 -13
  325. package/skills/path/references/runtime-selection.md +9 -0
  326. package/skills/path/scripts/_common.py +107 -1
  327. package/skills/path/scripts/build_recovery.py +62 -30
  328. package/skills/path/scripts/build_state.py +195 -5
  329. package/skills/path/scripts/check_handoffs.py +92 -30
  330. package/skills/path/scripts/check_task_briefs.py +19 -14
  331. package/skills/path/scripts/detect_project.py +17 -2
  332. package/skills/path/scripts/discussion_records.py +9 -1
  333. package/skills/path/scripts/discussion_validate.py +7 -6
  334. package/skills/path/scripts/dispatch_driver.py +157 -31
  335. package/skills/path/scripts/git_guard.py +24 -5
  336. package/skills/path/scripts/integration.py +98 -64
  337. package/skills/path/scripts/isolation.py +148 -11
  338. package/skills/path/scripts/members.py +68 -25
  339. package/skills/path/scripts/model_policy.py +5 -1
  340. package/skills/path/scripts/pipeline_git.py +68 -30
  341. package/skills/path/scripts/pipeline_state.py +28 -6
  342. package/skills/path/scripts/promote_lookahead.py +3 -3
  343. package/skills/path/scripts/review_findings.py +180 -44
  344. package/skills/path/scripts/roadmap.py +9 -1
  345. package/skills/path/scripts/state_checkpoint.py +30 -9
  346. package/skills/path/scripts/state_promote.py +15 -4
  347. package/skills/path/scripts/task_context.py +2 -0
  348. package/skills/path/scripts/workflow_run.py +9 -2
  349. package/skills/path/templates/final-review.md +5 -5
  350. package/skills/path/templates/state.md +1 -0
  351. package/skills/path/templates/task.md +1 -0
  352. package/skills/path/templates/wave-review.md +18 -9
@@ -161,7 +161,8 @@ def _reject_placeholder(value: str, label: str) -> None:
161
161
  if placeholder is not None:
162
162
  raise HandoffError(
163
163
  f"{label} still contains the placeholder {placeholder.group(0)}; "
164
- "replace it with the real value"
164
+ "replace it with the real value. A value cannot be only a placeholder; "
165
+ "literal text such as an HTML tag must be inside a sentence, in backticks"
165
166
  )
166
167
 
167
168
 
@@ -667,22 +668,30 @@ def _strip_comments(text: str) -> str:
667
668
  return COMMENT_PATTERN.sub("", text)
668
669
 
669
670
 
670
- def _numbered_items(section: str, *, continuations: bool = False) -> Dict[int, str]:
671
- items: Dict[int, str] = {}
672
- current = None
671
+ def _numbered_entries(section: str, *, continuations: bool = False) -> List[Tuple[int, str]]:
672
+ """Each numbered item in document order; a repeated number stays a separate entry."""
673
+ entries: List[Tuple[int, str]] = []
674
+ open_item = False
673
675
  for line in _strip_comments(section).splitlines():
674
676
  match = NUMBERED_ITEM_PATTERN.fullmatch(line.strip())
675
677
  if not match:
676
- if continuations and current is not None and line[:1].isspace() and line.strip():
677
- items[current] += " " + line.strip()
678
+ if continuations and open_item and line[:1].isspace() and line.strip():
679
+ number, text = entries[-1]
680
+ entries[-1] = (number, text + " " + line.strip())
678
681
  elif line.strip():
679
- current = None
682
+ open_item = False
680
683
  continue
681
- number = int(match.group(1))
684
+ entries.append((int(match.group(1)), match.group(2).strip()))
685
+ open_item = True
686
+ return entries
687
+
688
+
689
+ def _numbered_items(section: str, *, continuations: bool = False) -> Dict[int, str]:
690
+ items: Dict[int, str] = {}
691
+ for number, text in _numbered_entries(section, continuations=continuations):
682
692
  if number in items:
683
693
  raise HandoffError(f"repeated numbered item {number}")
684
- items[number] = match.group(2).strip()
685
- current = number
694
+ items[number] = text
686
695
  return items
687
696
 
688
697
 
@@ -693,7 +702,11 @@ def _success_criteria(intent: str) -> Dict[str, str]:
693
702
  expected = list(range(1, max(items) + 1))
694
703
  if sorted(items) != expected:
695
704
  raise HandoffError("INTENT.md success criteria must be contiguous from 1")
696
- return {f"SC{number}": text for number, text in items.items()}
705
+ # Issue #392: a review can quote a criterion without the markers of its sub-bullet lines.
706
+ bare = _numbered_items(
707
+ _section(_common.without_sub_bullet_markers(intent), "Success criteria"), continuations=True
708
+ )
709
+ return {f"SC{number}": _common.Criterion(text, bare[number]) for number, text in items.items()}
697
710
 
698
711
 
699
712
  def _split_top_level_commas(value: str) -> List[str]:
@@ -1297,18 +1310,22 @@ def _normalize_ws(value: str) -> str:
1297
1310
 
1298
1311
 
1299
1312
  def task_review_observation(item: str, task_text: str) -> str:
1300
- """Separate an exact quoted acceptance criterion from its observed evidence."""
1313
+ """Separate a quoted acceptance criterion from its observed evidence."""
1301
1314
  item = _normalize_ws(item)
1302
1315
  section = _common.section_body(task_text, "Acceptance criteria")
1303
1316
  if section is not None:
1304
- for criterion in _numbered_items(section, continuations=True).values():
1305
- quoted = _normalize_ws(criterion)
1306
- prefix = quoted + " — "
1307
- if item.startswith(prefix):
1308
- return item[len(prefix) :]
1309
- if item in {quoted, quoted + " —"}:
1310
- return ""
1311
- return item
1317
+ # Issue #374: frozen landed bytes may repeat a criterion number. Only the
1318
+ # criterion text matters here, so each repeated entry is still split
1319
+ # from its evidence and the evidence gets the same checks.
1320
+ criteria = [criterion for _number, criterion in _numbered_entries(section, continuations=True)]
1321
+ quoted = _common.quoted_criterion(item, criteria)
1322
+ if quoted is not None:
1323
+ return item[quoted[1] + len(" — ") :]
1324
+ # Issue #392: a quote can leave out the `- ` markers of the criterion's
1325
+ # sub-bullet lines, so read the criteria again without them.
1326
+ bare = _common.without_sub_bullet_markers(task_text)
1327
+ return item if bare == task_text else task_review_observation(item, bare)
1328
+
1312
1329
 
1313
1330
 
1314
1331
  def _verify_command(task_text: str) -> str:
@@ -1528,11 +1545,30 @@ def _validate_task_graph(
1528
1545
  if _task_wave(tasks[dependency], dependency) > wave:
1529
1546
  raise HandoffError(f"{task_id} depends on later-wave {dependency}")
1530
1547
 
1548
+ # `supersedes` retires the regression Verify of an earlier task: the same overlap rule selects both.
1549
+ for task_id, text in tasks.items():
1550
+ superseded = _strict_frontmatter(text, task_id).get("supersedes", [])
1551
+ if (
1552
+ not isinstance(superseded, list)
1553
+ or any(not re.fullmatch(r"T\d{3}", item) for item in superseded)
1554
+ or len(superseded) != len(set(superseded))
1555
+ ):
1556
+ raise HandoffError(f"{task_id} supersedes must be a list of different task ids, for example [T001]")
1557
+ for earlier in superseded:
1558
+ if earlier not in tasks:
1559
+ raise HandoffError(f"{task_id} supersedes unknown task {earlier}")
1560
+ if task_waves[earlier] >= task_waves[task_id]:
1561
+ raise HandoffError(f"{task_id} supersedes {earlier}, which is not in an earlier wave")
1562
+ if task_repos[earlier] != task_repos[task_id]:
1563
+ raise HandoffError(f"{task_id} supersedes {earlier}, which is in another repo")
1564
+ if not _common.overlapping_paths(task_files[earlier], task_files[task_id]):
1565
+ raise HandoffError(f"{task_id} supersedes {earlier}, which shares no declared file with it")
1566
+
1531
1567
  for index, left in enumerate(task_ids):
1532
1568
  for right in task_ids[index + 1 :]:
1533
1569
  if task_waves[left] != task_waves[right] or task_repos[left] != task_repos[right]:
1534
1570
  continue
1535
- overlap = sorted(set(task_files[left]) & set(task_files[right]))
1571
+ overlap = _common.overlapping_paths(task_files[left], task_files[right])
1536
1572
  if overlap:
1537
1573
  if (
1538
1574
  _task_scalar(tasks[left], left, "status") == "done"
@@ -1746,7 +1782,24 @@ def validate_roadmap(
1746
1782
  ) -> Dict[str, object]:
1747
1783
  """Validate roadmap structure and dependency ordering."""
1748
1784
 
1749
- _require_state(root, "roadmap", "active", project_dir)
1785
+ # A milestone-boundary re-slice keeps its entering inspect or define state.
1786
+ _require_state_one_of(
1787
+ root,
1788
+ {("roadmap", "active"), ("inspect", "active"), ("define", "active")},
1789
+ project_dir,
1790
+ )
1791
+ if _require_pipeline(root, project_dir)["phase"] == "define":
1792
+ if __package__:
1793
+ from . import build_recovery
1794
+ else:
1795
+ import build_recovery
1796
+ try:
1797
+ # Define entered again after intent approval is not a boundary.
1798
+ reopened = (build_recovery.context(root.resolve()) or {}).get("active")
1799
+ except PipelineStateError as error:
1800
+ raise HandoffError(str(error)) from error
1801
+ if reopened:
1802
+ raise HandoffError("roadmap re-slice is not legal after intent approval")
1750
1803
  relative = f"{project_dir}/ROADMAP.md"
1751
1804
  text = _read(root, relative)
1752
1805
  if re.search(r"(?m)^## Wave |^\|\s*Task\s*\||^Files:\s*", text):
@@ -2045,25 +2098,28 @@ def validate_wave_evidence(
2045
2098
  if enforce_plan_tasks:
2046
2099
  if reviewed_tasks != expected_tasks:
2047
2100
  raise HandoffError(
2048
- f"{name} must review exactly {', '.join(expected_tasks)} in plan order"
2101
+ f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
2049
2102
  )
2050
2103
  else:
2051
2104
  unknown = [task_id for task_id in reviewed_tasks if task_id not in expected_tasks]
2052
2105
  if unknown:
2053
2106
  raise HandoffError(
2054
- f"{name} must review exactly {', '.join(expected_tasks)} in plan order"
2107
+ f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
2055
2108
  )
2056
2109
  plan_order = [task_id for task_id in expected_tasks if task_id in reviewed_tasks]
2057
2110
  if reviewed_tasks != plan_order:
2058
2111
  raise HandoffError(
2059
- f"{name} must review exactly {', '.join(expected_tasks)} in plan order"
2112
+ f"{name} must review exactly {', '.join(expected_tasks)} in task id order"
2060
2113
  )
2061
2114
  task_verdicts = []
2062
2115
  for heading_index, heading in task_headings:
2063
2116
  task_id = heading.group("task")
2064
2117
  expected_title = _normalize_ws(_task_scalar(tasks[task_id], task_id, "title"))
2065
2118
  if _normalize_ws(heading.group("title")) != expected_title:
2066
- raise HandoffError(f"{name} title for {task_id} differs from task file")
2119
+ raise HandoffError(
2120
+ f"{name} title for {task_id} differs from task file; "
2121
+ f"expected the exact `title:` value: {expected_title}"
2122
+ )
2067
2123
  end = next(
2068
2124
  (
2069
2125
  index
@@ -2080,7 +2136,10 @@ def validate_wave_evidence(
2080
2136
  if line.strip().startswith(f"- {marker} ")
2081
2137
  ]
2082
2138
  if not evidence:
2083
- raise HandoffError(f"{name} task {task_id} lacks {verdict} evidence")
2139
+ raise HandoffError(
2140
+ f"{name} task {task_id} lacks {verdict} evidence; "
2141
+ f"add a line that starts with `- {marker} ` under its heading"
2142
+ )
2084
2143
  for item in evidence:
2085
2144
  item = task_review_observation(item, tasks[task_id])
2086
2145
  _non_placeholder(item, f"{name} task {task_id} {verdict} evidence")
@@ -2132,7 +2191,7 @@ def validate_wave_evidence(
2132
2191
  sc_id in criteria
2133
2192
  and (
2134
2193
  not separator
2135
- or _normalize_ws(heading_text) != _normalize_ws(criteria[sc_id])
2194
+ or not _common.quotes_criterion(heading_text, criteria[sc_id])
2136
2195
  )
2137
2196
  ):
2138
2197
  raise HandoffError(f"{name} {sc_id} heading text differs from INTENT.md")
@@ -2150,7 +2209,10 @@ def validate_wave_evidence(
2150
2209
  if line.strip().startswith(f"- {marker} ")
2151
2210
  ]
2152
2211
  if not evidence:
2153
- raise HandoffError(f"{name} {sc_id} lacks {verdict} evidence")
2212
+ raise HandoffError(
2213
+ f"{name} {sc_id} lacks {verdict} evidence; "
2214
+ f"add a line that starts with `- {marker} ` under its heading"
2215
+ )
2154
2216
  for item in evidence:
2155
2217
  _non_placeholder(item, f"{name} {sc_id} {verdict} evidence")
2156
2218
  verdicts[sc_id] = verdict
@@ -2230,7 +2292,7 @@ def validate_final(
2230
2292
  verdicts: Dict[str, str] = {}
2231
2293
  for index, heading in enumerate(blocks):
2232
2294
  sc_id = heading.group("id")
2233
- if _normalize_ws(heading.group("title")) != _normalize_ws(criteria[sc_id]):
2295
+ if not _common.quotes_criterion(heading.group("title"), criteria[sc_id]):
2234
2296
  raise HandoffError(f"FINAL.md {sc_id} heading text differs from INTENT.md")
2235
2297
  end = blocks[index + 1].start() if index + 1 < len(blocks) else len(text)
2236
2298
  block = text[heading.end() : end]
@@ -100,13 +100,15 @@ def _supplied_contract(repo: Path, task: Path, token: str) -> bool:
100
100
  return path.is_file() and path.resolve() == repo.resolve() / token
101
101
 
102
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
+
103
109
  def _pipeline_workspace_file(repo: Path, task: Path, token: str) -> bool:
104
110
  """Pipeline-owned paths may exist only in the working tree before checkpoint."""
105
- track = _project_track(task, repo)
106
- if track is None:
107
- return False
108
- prefixes = tuple(f"{track}/{name}/" for name in PIPELINE_WORKSPACE_DIRS)
109
- if not any(token.startswith(prefix) for prefix in prefixes):
111
+ if not _pipeline_workspace_path(repo, task, token):
110
112
  return False
111
113
  path = repo / token
112
114
  return path.is_file() and path.resolve() == repo.resolve() / token
@@ -277,7 +279,7 @@ def _verify_tokens(
277
279
 
278
280
 
279
281
  def _member_bases(root: Path):
280
- """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."""
281
283
  cache: Dict[str, Optional[Tuple[Path, str]]] = {}
282
284
 
283
285
  def locate(name: str) -> Optional[Tuple[Path, str]]:
@@ -290,21 +292,22 @@ def _member_bases(root: Path):
290
292
  except ImportError: # pragma: no cover - package import used by tests
291
293
  from scripts import members
292
294
  try:
293
- listed = {member["name"]: Path(member["checkout"]) for member in members.read_members(root)}
294
- checkout = listed.get(name)
295
- if checkout is None:
295
+ listed = {member["name"]: member for member in members.read_members(root)}
296
+ if name not in listed:
296
297
  cache[name] = None
297
298
  else:
299
+ checkout = Path(listed[name]["checkout"])
298
300
  # A build lock selects the bound tip, including during Plan recovery;
299
- # 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.
300
303
  lock = root / members.LOCK_PATH
301
304
  locked = [entry for entry in (json.loads(lock.read_text(encoding="utf-8"))["members"]
302
305
  if lock.is_file() else []) if entry["name"] == name]
303
306
  if locked:
304
307
  cache[name] = (checkout, _resolve_base(checkout, f"refs/heads/{locked[0]['branch']}"))
305
308
  else:
306
- members.require_origin_main(checkout)
307
- 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])))
308
311
  except (members.MembersError, BriefError) as error:
309
312
  raise BriefError(f"member {name} is unavailable: {error}") from error
310
313
  return cache[name]
@@ -328,8 +331,8 @@ def _lint_task(
328
331
  checked = 0
329
332
  if fields is None:
330
333
  return task_id, [error or "invalid frontmatter"], None, checked
331
- # A member task's paths resolve in the member at its origin/main, where the
332
- # 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.
333
336
  git_root = repo
334
337
  member = fields.get("repo")
335
338
  if member is not None:
@@ -397,6 +400,8 @@ def _lint_task(
397
400
  and token not in supplied
398
401
  and not _supplied_contract(repo, path, token)
399
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))
400
405
  and not _base_exists(git_root, base, token)
401
406
  ):
402
407
  problems.append(f"## {name} names a path missing at the layer base: {token}")
@@ -1291,10 +1291,17 @@ def project_slug(root: Path) -> str:
1291
1291
  return slug or "project"
1292
1292
 
1293
1293
 
1294
- def filled_state_template(template: str, slug: str, phase: str) -> str:
1294
+ def filled_state_template(
1295
+ template: str, slug: str, phase: str, default_branch: str = "main"
1296
+ ) -> str:
1295
1297
  if "<slug>" not in template:
1296
1298
  raise DetectError("state template missing slug placeholder")
1297
1299
  text = template.replace("<slug>", slug)
1300
+ text, count = re.subn(
1301
+ r"(?m)^default_branch: main\b", lambda _: f"default_branch: {default_branch}", text, count=1
1302
+ )
1303
+ if count != 1:
1304
+ raise DetectError("state template missing default_branch: main line")
1298
1305
  text, count = re.subn(r"(?m)^phase: define\b", f"phase: {phase}", text, count=1)
1299
1306
  if count != 1:
1300
1307
  raise DetectError("state template missing phase: define line")
@@ -2030,6 +2037,14 @@ def initialize(
2030
2037
  )
2031
2038
  if git.returncode != 0 or git.stdout.strip() != "true":
2032
2039
  return {**payload, "route": "setup-repository", "wrote_state": False}
2040
+ default_branch = _common.origin_default_branch(root)
2041
+ if not _common.valid_default_branch(default_branch):
2042
+ return {**payload, "wrote_state": False, "error": (
2043
+ f"origin default branch {default_branch!r} cannot be recorded in STATE.md: "
2044
+ "it is a GSD Path reserved name or it has a character that STATE frontmatter "
2045
+ "cannot hold. Rename the default branch, then run "
2046
+ "`git remote set-head origin --auto`."
2047
+ )}
2033
2048
  expected = "inspect" if payload["verdict"] == "brownfield" else "define"
2034
2049
  if phase is None:
2035
2050
  phase = expected
@@ -2043,7 +2058,7 @@ def initialize(
2043
2058
  return payload
2044
2059
  write_state_anchored(
2045
2060
  root,
2046
- filled_state_template(raw, project_slug(root), phase),
2061
+ filled_state_template(raw, project_slug(root), phase, default_branch),
2047
2062
  root_status,
2048
2063
  project_status,
2049
2064
  )
@@ -7,6 +7,7 @@ import json
7
7
  import os
8
8
  import shutil
9
9
  import sys
10
+ import traceback
10
11
 
11
12
  # Runtime helpers must not modify their immutable installation.
12
13
  sys.dont_write_bytecode = True
@@ -33,7 +34,7 @@ def _load_pipeline_modules():
33
34
  try:
34
35
  from scripts import archive_milestone, pipeline_state, _common
35
36
  return _common, archive_milestone, pipeline_state
36
- except ModuleNotFoundError as error:
37
+ except ImportError as error: # a foreign `scripts` package
37
38
  if error.name not in {
38
39
  "scripts",
39
40
  "scripts.archive_milestone",
@@ -41,6 +42,13 @@ def _load_pipeline_modules():
41
42
  "scripts._common",
42
43
  }:
43
44
  raise
45
+ # A plain ImportError raised while a `scripts` module loaded is not a
46
+ # foreign package. A missing module falls back, as before.
47
+ if not isinstance(error, ModuleNotFoundError) and any(
48
+ frame.f_globals.get("__name__", "").startswith("scripts.")
49
+ for frame, _ in traceback.walk_tb(error.__traceback__)
50
+ ):
51
+ raise
44
52
  shared_scripts = Path(__file__).resolve().parents[2] / "gsd-path" / "scripts"
45
53
  sys.path.insert(0, str(shared_scripts))
46
54
  import archive_milestone
@@ -507,7 +507,8 @@ def archived_intent_criteria(archive: Path) -> dict[int, str]:
507
507
  criteria = check_handoffs._success_criteria(text)
508
508
  except check_handoffs.HandoffError as error:
509
509
  raise ArchiveError(str(error)) from error
510
- return {int(key[2:]): " ".join(value.split()) for key, value in criteria.items()}
510
+ # Each value keeps the form without sub-bullet markers that quotes_criterion reads.
511
+ return {int(key[2:]): value for key, value in criteria.items()}
511
512
 
512
513
 
513
514
  def parse_final_review(archive: Path) -> tuple:
@@ -563,7 +564,7 @@ def _final_review(archive: Path) -> tuple:
563
564
  if len(headings) != len(intent_criteria):
564
565
  raise ArchiveError("FINAL.md success criteria do not exactly cover INTENT.md")
565
566
  for _, number, criterion in headings:
566
- if " ".join(criterion.split()) != intent_criteria[number]:
567
+ if not _common.quotes_criterion(criterion, intent_criteria[number]):
567
568
  raise ArchiveError(
568
569
  f"FINAL.md SC{number} heading text differs from archived INTENT.md"
569
570
  )
@@ -951,10 +952,10 @@ def validate_wave_review(
951
952
  (index, match.group(1), " ".join(match.group(2).split()), match.group(3))
952
953
  )
953
954
  actual = [(sc_id, text) for _, sc_id, text, _ in criterion_headings]
954
- normalized_expected_criteria = [
955
- (sc_id, " ".join(text.split())) for sc_id, text in expected_criteria
956
- ]
957
- if actual != normalized_expected_criteria:
955
+ if len(actual) != len(expected_criteria) or any(
956
+ sc_id != expected_id or not _common.quotes_criterion(text, expected)
957
+ for (sc_id, text), (expected_id, expected) in zip(actual, expected_criteria)
958
+ ):
958
959
  raise ArchiveError(
959
960
  f"{path.name} Intent coverage does not match its owned success criteria"
960
961
  )