@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
@@ -17,16 +17,26 @@ tasks in this wave as the rubric. For each task:
17
17
  criterion. Never use the primary worktree or branch tip as task
18
18
  evidence. Do not create or remove Git worktrees yourself.
19
19
  4. Mark `pass` or `fail`. Before the evidence separator (` — `), copy the
20
- complete task acceptance or interface criterion verbatim, joining wrapped
21
- lines with spaces. Do not substitute an AC id or summary: finding grouping
22
- binds this text to the task. Give the observed result, `file:line`, and
20
+ complete task acceptance or interface criterion (see Review file format
21
+ below). Do not substitute an AC id or summary: finding grouping binds
22
+ this text to the task. Give the observed result, `file:line`, and
23
23
  concrete fix direction for each failure.
24
24
 
25
+ A Log entry `orchestrator regression Verify of <task id> retired: superseded
26
+ by <task id>` is not evidence: judge the superseding task's acceptance
27
+ criteria. The same applies to the entry `orchestrator regression Verify of
28
+ <fix task id>: part of <task id> retired: superseded by <task id>`.
29
+
25
30
  Then check each INTENT.md success criterion owned by a task in this wave
26
- against that same isolated product. Copy the criterion verbatim from
27
- INTENT.md. Mark `pass` or `fail` with evidence; a failed owned SC blocks
28
- the wave. Omit the Intent coverage section when no task in the wave owns
29
- an SC.
31
+ against that same isolated product. In each `### SCn —` heading, copy the
32
+ complete criterion text from INTENT.md `## Success criteria` (see Review
33
+ file format below). Mark `pass` or `fail` with evidence; a failed owned SC
34
+ blocks the wave. Omit the Intent coverage section when no task in the wave owns
35
+ an SC. When INTENT.md `## Edge coverage` or `## Prohibitions` has rows for
36
+ an owned SC, judge it against those rulings too: cite each `held-out` edge
37
+ id with its held-out test's recorded output and each `judgment` prohibition
38
+ id with what you observed. A missing held-out result or a violated ruling
39
+ fails the SC.
30
40
 
31
41
  Allow changed paths only in the task's `files` plus its assigned task file.
32
42
  Within the task file, allow orchestrator-owned `base`, `worktree`,
@@ -40,6 +50,30 @@ For cycle greater than 1, read the previous review, re-check every failure,
40
50
  and regression-check affected passes. State when the same criterion fails for
41
51
  a different reason. Use only the wave-review template.
42
52
 
53
+ ### Review file format
54
+
55
+ `scripts/check_handoffs.py wave` rejects a review file that breaks one of
56
+ these rules, and `scripts/review_findings.py` cannot group a failure whose
57
+ criterion text does not match. Apply the rules before you write the file.
58
+
59
+ - **Section order.** Put the `## T### —` sections in task id order (T001
60
+ before T002). The order of the PLAN.md wave table does not apply. A `pass`
61
+ review has one section for each task of the wave.
62
+ - **Section heading.** Write `## T### — <title>: pass` or `: fail`, where
63
+ `<title>` is the exact `title:` value of the task file.
64
+ - **Evidence lines.** An evidence line starts with `- ✅ ` for pass or
65
+ `- ❌ ` for fail. Each task section and each `### SCn —` block needs one
66
+ or more lines with the marker of its own verdict. A failed task needs a
67
+ `- ❌ ` criterion line, also when other text describes the failure.
68
+ - **Criterion text.** In a task evidence line and in a `### SCn —` heading,
69
+ copy every word of the criterion. Join wrapped lines and sub-bullets with
70
+ one space. You can keep or remove the `- ` markers of the sub-bullets.
71
+ A changed, added, removed or moved word does not match.
72
+ - **Angle brackets.** Text such as `<b>` or `<path>` outside backticks reads
73
+ as an unfilled template placeholder and is rejected. Write literal markup
74
+ and code in backticks inside a sentence, for example: the output has
75
+ `<b>hello</b>`.
76
+
43
77
  ### Re-review after a proven repair
44
78
 
45
79
  When the brief supplies a `review_findings.py repair-evidence` receipt, use
@@ -47,14 +81,21 @@ its isolated repair product for the linked original criteria, including owned
47
81
  INTENT criteria. The original faulty landing and earlier verdicts remain
48
82
  historical evidence; they do not force the later verdict to remain failed.
49
83
  Reconstruct the receipt's repair `base` plus only its `commit^..commit` patch
50
- for `files` inside the supplied sidecar. Read the repair task's recorded
51
- Verify, inspect the patch against the original unchanged contract, and reuse
84
+ for `files` inside the supplied sidecar. For a member repair (the receipt
85
+ names `member`), use `member_base` and `member_commit` in that member's
86
+ repository. `earlier_repairs` are linked repairs of the same findings that
87
+ are already in the repair base. When the receipt has `later_repairs`, linked
88
+ repairs changed the same files after this repair: apply their patches in
89
+ order too. Read the repair task's recorded
90
+ Verify, or with `later_repairs` that of the last one, which is the receipt's
91
+ `verification`; inspect the patch against the original unchanged contract, and reuse
52
92
  settled checks. Check unaffected criteria from their prior evidence plus the
53
93
  repair diff for regressions. Cite the receipt, repair landing and task Log in
54
94
  the new review. A receipt proves provenance, not acceptance: judge whether the
55
95
  repair actually satisfies the criterion. A missing or rejected receipt blocks
56
- using that repair; the parent resolves it rather than creating another product
57
- fix for the unchanged historical failure.
96
+ using that repair; the parent resolves it, or briefs this review in full at
97
+ the current HEAD without a receipt, rather than creating another product fix
98
+ for the unchanged historical failure.
58
99
 
59
100
  ## Final scope in a quick full wave
60
101
 
@@ -65,7 +106,10 @@ criterion's Intent coverage block includes `- **Surface**:`, `- **Check**:`, and
65
106
  `- **Observed**:` with the walked states and actual result. `Surface` holds
66
107
  exactly the surface name from PLAN's Surface contract; walkthrough detail belongs
67
108
  in Check and Observed. Reuse recorded task
68
- output if it proves that exact walkthrough; run only a missing check. Final-scope
109
+ output if it proves that exact walkthrough; run only a missing check. Cite
110
+ every `held-out` edge and `judgment` prohibition id of each criterion in its
111
+ evidence; shipping derives FINAL.md from these records, and an uncited id
112
+ sends the milestone to a dispatched final review instead. Final-scope
69
113
  coverage shares this wave artifact; do not write a second report. Shipping's
70
114
  runtime checks freshness and derives the final view from these records.
71
115
 
@@ -46,7 +46,9 @@ Resolve sibling links against this role file's directory.
46
46
  from the same model family is one evidence path; independence comes
47
47
  from re-run evidence, different sources, or a different model family.
48
48
  - Pass any reasonable satisfied reading of an ambiguous wave criterion and
49
- warn that it needs tightening.
49
+ warn that it needs tightening, unless INTENT.md `## Edge coverage` or
50
+ `## Prohibitions` rules on that reading: a ruling there binds, and a
51
+ reading that contradicts it fails.
50
52
  - Do not edit code, tasks, plans, or implementation state.
51
53
 
52
54
  Return verdicts and the review output path without extra prose.
@@ -20,6 +20,15 @@ references to bundled copies of those helpers. Other skill helpers, templates,
20
20
  and role briefs remain in the installed skill bundle. Pass `-B` to Python helper
21
21
  invocations so execution does not create bytecode in the runtime store.
22
22
 
23
+ The selected runtime is authoritative for each helper in its `manifest.json`;
24
+ this includes `review_findings.py` and `task_context.py`. A selected runtime
25
+ that was published before a helper became a runtime file does not list that
26
+ helper: run the bundled copy until the project runtime is upgraded.
27
+ `dispatch_driver.py` and `workflow_run.py` always run from the installed skill
28
+ bundle, because they read role briefs and templates from it; a skill update
29
+ replaces them, a runtime upgrade does not. The driver reloads the helper modules
30
+ that it imports from the selected runtime.
31
+
23
32
  With no declaration, retain the legacy helper lookup. An existing
24
33
  `.gsd-path/runtime/` requires the explicit `--runtime-migrate --project PATH`
25
34
  operation before adopting external storage; review the resulting Git diff.
@@ -0,0 +1,150 @@
1
+ # Spec-reach probes
2
+
3
+ Define runs two probes over the success criteria before approval: the edge
4
+ probe (what each criterion must do at its edges) and the prohibition probe
5
+ (what each criterion must never silently become). Both leave a table in
6
+ INTENT.md. Plan, build review, and final review read those tables; the
7
+ `check_handoffs.py intent`, `plan`, and `final` gates enforce them.
8
+
9
+ ## Why define owns this
10
+
11
+ A reviewer can only check what the intent states. An edge nobody wrote down
12
+ is never checked: final review covers exactly the INTENT success criteria,
13
+ and a reviewer passes any reasonable reading of an ambiguous one. Model
14
+ reviewers are also confidently wrong on exactly these gaps. On three tasks
15
+ whose correct behavior the spec left unstated, LLM verifier verdicts caught
16
+ the defect 0 times in 12 while a held-out test caught all three; in a larger
17
+ replication, stating the surfaced edge in the spec raised the verifier's
18
+ catch rate to 98% ([Verifier Reach Is Spec Reach](https://github.com/davesienkowski/verifier-reach-is-spec-reach)).
19
+ A smarter reviewer does not close that gap; a wider intent does. The user is
20
+ present at define, so define is where the edge gets its ruling.
21
+
22
+ ## Edge probe
23
+
24
+ Walk every success criterion:
25
+
26
+ 1. Name the data shapes the criterion handles, then raise only the
27
+ categories those shapes reach:
28
+
29
+ | Shape | Categories |
30
+ |-------|------------|
31
+ | numeric range, dates, money, counts | boundary, precision |
32
+ | list, set, rows, intervals | adjacency, empty, ordering |
33
+ | text, names, identifiers | empty, encoding |
34
+ | state that persists or repeats | idempotency, concurrency |
35
+ | files, network, processes | concurrency |
36
+
37
+ 2. For each raised category, write one concrete candidate edge and the
38
+ ruling you recommend:
39
+
40
+ | Category | Probe question |
41
+ |----------|----------------|
42
+ | boundary | What happens exactly at each minimum, maximum, or threshold, and one step either side? |
43
+ | adjacency | When two items are equal or just touch, do they merge, collide, or stay separate? |
44
+ | empty | What is the result for empty, single-item, or missing input? |
45
+ | encoding | Whose length or equality applies: bytes, code points, grapheme clusters, normalized form, case? |
46
+ | ordering | When items compare equal, is output order specified and stable? |
47
+ | precision | Where can rounding, overflow, or truncation happen, and which rule applies? |
48
+ | idempotency | What happens when this runs twice on the same input? |
49
+ | concurrency | If interrupted or run in parallel, what is guaranteed? |
50
+
51
+ 3. Ask the user in one small round, all criteria together, recommended
52
+ ruling first. Never rule on an edge's behavior yourself: the correct
53
+ answer is exactly what the criteria did not say, so a guess here is the
54
+ defect this probe exists to prevent. Under pre-approval, an edge whose
55
+ behavior the user has not stated is still a required answer: stop and
56
+ ask instead of ruling.
57
+
58
+ 4. Record each ruling as one row:
59
+ - `criterion SCn` when the answer is observable where the user sees the
60
+ work. Add or amend that success criterion (define owns criteria), then
61
+ point the row at it. An existing criterion that already states the
62
+ edge may be named directly.
63
+ - `held-out` when the answer is a precise rule or a set of examples that
64
+ belongs in a test rather than a user-facing criterion (a normalization
65
+ form, a rounding rule, an inclusive bound on an internal API). Write the
66
+ ruling or the user's examples verbatim in Detail. The plan must schedule
67
+ a named test that pins it.
68
+ - `dismissed` when the edge cannot occur for this criterion. Detail says
69
+ why ("input is a closed enum"). "Not important" is not a reason.
70
+
71
+ Every criterion gets at least one row. A criterion with no data shape gets
72
+ one row with category `none`, dismissed with its reason.
73
+
74
+ ## Prohibition probe
75
+
76
+ Walk every success criterion with one adversarial question: what could this
77
+ silently become that the user would not want, although nothing in INTENT.md
78
+ forbids it? Generate candidates privately, then keep only values, safety,
79
+ privacy, fairness, and transparency items: "must not email unsubscribed
80
+ users", "must not store raw card numbers in logs", "must not use guilt
81
+ wording". Drop routine engineering items (mutating input, throwing on empty
82
+ input, leaking handles); those are edges or ordinary review. Do not mint
83
+ generic security or compliance canon (injection, path traversal, data
84
+ retention law); record a real compliance need under Constraints instead.
85
+ Expect two or three kept items for work people touch, and often none for
86
+ internals.
87
+
88
+ Present the kept items with a recommended ruling and record each as a row:
89
+
90
+ - `criterion SCn` when a test or command can check it. State it as a
91
+ success criterion and point the row at it.
92
+ - `judgment` when it is real but not mechanically checkable. Detail says
93
+ what the final reviewer looks for.
94
+ - `dismissed` when the user rules it out. Detail records the reason.
95
+
96
+ Work that affects no person directly records a single row instead, and no
97
+ other: `| N1 | all | none | dismissed | <why no person is affected> |`.
98
+
99
+ ## INTENT.md tables
100
+
101
+ ```markdown
102
+ ## Edge coverage
103
+
104
+ | Edge | Criterion | Category | Disposition | Detail |
105
+ |------|-----------|----------|-------------|--------|
106
+ | E1 | SC1 | boundary | criterion SC4 | |
107
+ | E2 | SC1 | empty | dismissed | the command always receives at least one file |
108
+ | E3 | SC2 | encoding | held-out | names compare by NFC code points, case-sensitive |
109
+
110
+ ## Prohibitions
111
+
112
+ | Prohibition | Criterion | Must not | Disposition | Detail |
113
+ |-------------|-----------|----------|-------------|--------|
114
+ | N1 | SC2 | send a reminder to an unsubscribed user | criterion SC5 | |
115
+ | N2 | SC2 | use guilt or shame wording | judgment | reminder copy is neutral and states only the due item |
116
+ ```
117
+
118
+ Ids are contiguous from `E1` and `N1`. Detail is required for every row that
119
+ is not `criterion SCn`. The headings are exact: `## Edge coverage` and
120
+ `## Prohibitions`. An INTENT.md without them predates the probes and is not
121
+ rechecked; a near-miss heading fails the gate rather than switching it off.
122
+
123
+ ## Downstream
124
+
125
+ - **Plan.** Every `held-out` edge gets one PLAN.md `## Held-out checks` row
126
+ naming the task that owns the edge's criterion and the test file (a
127
+ `path::test_name` node id is fine). That file is listed in the task's
128
+ `files`, the task's Verify runs it, and the task's Acceptance criteria
129
+ state the edge id and its ruling. A `criterion SCn` row needs nothing extra: SCn is
130
+ already covered like any criterion. A `judgment` prohibition belongs in
131
+ the owning task's Approach so the coder knows the line.
132
+ - **Review.** A row pins the reading of its criterion. A reviewer does not
133
+ pass a reading that contradicts a ruling in either table, and cites each
134
+ held-out edge and judgment prohibition id with its evidence.
135
+ - **Final review.** A criterion that carries a held-out edge or a judgment
136
+ prohibition is `met` only when its Check, Observed, or Reference cites
137
+ every such id with the evidence: the held-out test's recorded passing
138
+ output, or the observed behavior judged against Detail. When that
139
+ evidence is missing, did not run, or cannot be judged, record
140
+ `unverifiable` with `Finding: insufficient spec evidence: <id> ...`;
141
+ never `met` on inference. The trigger is the tag, not reviewer doubt:
142
+ criteria without a tag are graded as usual and never abstained.
143
+
144
+ ## What the gates can and cannot prove
145
+
146
+ `check_handoffs.py intent` (before approval and again when define records
147
+ approval), `plan`, and `final` prove the tables are complete, every held-out
148
+ edge has a scheduled test, and every tagged `met` cites its tags. They cannot
149
+ prove who made a ruling or that a cited result says what the verdict claims.
150
+ Those stay with the define conversation and the reviewer's recorded evidence.
@@ -25,7 +25,8 @@ import signal
25
25
  import subprocess
26
26
  import time
27
27
  from pathlib import Path
28
- from typing import BinaryIO, Iterator, List, Optional, Sequence
28
+ from typing import BinaryIO, Iterable, Iterator, List, Optional, Sequence, Tuple
29
+
29
30
 
30
31
  # Chosen by what imports, not os.name, so tests can emulate Windows on POSIX.
31
32
  try:
@@ -65,6 +66,49 @@ VERIFY_RESULTS = ("pass", "fail")
65
66
  VERIFY_LEDGER_SCHEMA = "gsd-path/verify-ledger/v2"
66
67
  VERIFY_BLOCK_PATTERN = re.compile(r"```bash[ \t]*\n(?P<block>.*?)```", re.DOTALL)
67
68
 
69
+ REVIEW_BULLET_HEADING_PATTERN = re.compile(r"^#{2,3} ")
70
+ REVIEW_BULLET_FIELD_LABEL_PATTERN = re.compile(r"^- \*\*[^*]+\*\*:")
71
+
72
+
73
+ def review_bullet_field_label_pattern(field: str) -> re.Pattern[str]:
74
+ return re.compile(rf"^- \*\*{re.escape(field)}\*\*:[ \t]*(.*)$")
75
+
76
+
77
+ def _review_bullet_field_value(lines: Sequence[str], index: int, inline: str) -> str:
78
+ if inline:
79
+ return inline
80
+ collected: list[str] = []
81
+ for line in lines[index + 1 :]:
82
+ if REVIEW_BULLET_FIELD_LABEL_PATTERN.match(line) or REVIEW_BULLET_HEADING_PATTERN.match(
83
+ line
84
+ ):
85
+ break
86
+ if line and not line[0].isspace():
87
+ break
88
+ stripped = line.strip()
89
+ if stripped:
90
+ collected.append(stripped)
91
+ return "\n".join(collected)
92
+
93
+
94
+ def find_review_bullet_field_values(lines: Sequence[str], field: str) -> list[str]:
95
+ pattern = review_bullet_field_label_pattern(field)
96
+ values: list[str] = []
97
+ for index, line in enumerate(lines):
98
+ match = pattern.fullmatch(line)
99
+ if match is None:
100
+ continue
101
+ values.append(_review_bullet_field_value(lines, index, match.group(1)))
102
+ return values
103
+
104
+
105
+ def parse_review_bullet_field(lines: Sequence[str], field: str) -> str:
106
+ """Parse one `- **Field**:` review bullet, including indented continuations."""
107
+ values = find_review_bullet_field_values(lines, field)
108
+ if len(values) != 1:
109
+ raise ValueError(field)
110
+ return values[0]
111
+
68
112
 
69
113
  # Sample paths the pipeline must commit. A product rule such as an unanchored
70
114
  # `build/` would otherwise drop them silently from every commit.
@@ -118,6 +162,33 @@ def project_ignore_error(repo: Path) -> Optional[str]:
118
162
  + ". Anchor the product rule (for example `/build/`) in a task that owns it.")
119
163
 
120
164
 
165
+ _DEFAULT_BRANCH_RE = re.compile(
166
+ r"[^\s#\"'\\~^:?*\[\x00-\x1f\x7f-][^\s#\"'\\~^:?*\[\x00-\x1f\x7f]*"
167
+ )
168
+ _RESERVED_BRANCH_PREFIXES = ("gsd-path/", "gsd-path-integrate/", "gsd-path-task/")
169
+
170
+
171
+ def valid_default_branch(name: str) -> bool:
172
+ """A git branch name that STATE frontmatter can hold and Path does not reserve."""
173
+ return (
174
+ bool(_DEFAULT_BRANCH_RE.fullmatch(name))
175
+ and name != "null"
176
+ and not name.startswith(_RESERVED_BRANCH_PREFIXES)
177
+ and not name.endswith(("/", ".", ".lock"))
178
+ and not any(part in name for part in ("..", "//", "/.", "@{"))
179
+ )
180
+
181
+
182
+ def origin_default_branch(repo: Path) -> str:
183
+ """The origin default branch that initialization records; an unset origin/HEAD means main."""
184
+ result = run_git(repo, "symbolic-ref", "--quiet", "refs/remotes/origin/HEAD")
185
+ prefix = "refs/remotes/origin/"
186
+ remote_ref = result.stdout.strip()
187
+ if result.returncode != 0 or not remote_ref.startswith(prefix):
188
+ return "main"
189
+ return remote_ref.removeprefix(prefix)
190
+
191
+
121
192
  def section_body(text: str, heading: str) -> Optional[str]:
122
193
  """The text under `## <heading>` up to the next `## `, or None when absent."""
123
194
  match = re.search(
@@ -126,6 +197,79 @@ def section_body(text: str, heading: str) -> Optional[str]:
126
197
  return match.group("body") if match else None
127
198
 
128
199
 
200
+ def overlapping_paths(left: Sequence[str], right: Sequence[str]) -> List[str]:
201
+ """Return the sorted paths that two task `files` lists both cover.
202
+
203
+ An entry covers itself and the paths below it. For a declared directory
204
+ and a path below it, the result names the lower path. The callers have no
205
+ task base, so this does not ask if an entry is a file: the task-brief
206
+ lint rejects a path below a file at the base.
207
+ """
208
+ return sorted(
209
+ {
210
+ max(one, other, key=len)
211
+ for one in left
212
+ for other in right
213
+ if one == other or one.startswith(other + "/") or other.startswith(one + "/")
214
+ }
215
+ )
216
+
217
+
218
+ # The `- ` of an indented sub-bullet line. A line that would then read as a
219
+ # numbered item (`- 2. text`) keeps its marker, so both readings of a source
220
+ # have the same criteria.
221
+ SUB_BULLET_MARKER = re.compile(r"(?m)^([ \t]+)-[ \t]+(?![ \t]*\d+\.\s)")
222
+
223
+
224
+ def without_sub_bullet_markers(source: str) -> str:
225
+ """`source` with the `- ` marker of each indented sub-bullet line removed."""
226
+ return SUB_BULLET_MARKER.sub(r"\1", source)
227
+
228
+
229
+ class Criterion(str):
230
+ """A criterion as its parser joins it, with the `- ` markers of its sub-bullet lines.
231
+
232
+ `bare` is the same criterion parsed from `without_sub_bullet_markers(source)`.
233
+ The joined text cannot tell a marker from a minus sign; the source lines can.
234
+ """
235
+
236
+ bare: str
237
+
238
+ def __new__(cls, text: str, bare: str) -> "Criterion":
239
+ criterion = super().__new__(cls, text)
240
+ criterion.bare = bare
241
+ return criterion
242
+
243
+
244
+ def quotes_criterion(quote: str, criterion: str) -> bool:
245
+ """Whether a review quote is `criterion`, for every reader.
246
+
247
+ Only whitespace is collapsed. The quote must equal the criterion with all
248
+ the markers of its sub-bullet lines or with none of them; every other
249
+ character, a hyphen inside a line too, is compared.
250
+ """
251
+ forms = (criterion, getattr(criterion, "bare", criterion))
252
+ return " ".join(quote.split()) in {" ".join(form.split()) for form in forms}
253
+
254
+
255
+ def quoted_criterion(item: str, criteria: Sequence[str]) -> Optional[Tuple[int, int]]:
256
+ """Index of the criterion quoted at the start of `item`, and where the quote ends.
257
+
258
+ A criterion can contain ` — ` itself, so each separator and the end of
259
+ `item` is a candidate, the longest quote first. A criterion as written
260
+ wins over any criterion read without its sub-bullet markers.
261
+ """
262
+ ends = [match.start() for match in re.finditer("(?= —(?: |$))|$", item)][::-1]
263
+ for bare in (False, True):
264
+ for end in ends:
265
+ quote = " ".join(item[:end].split())
266
+ for index, criterion in enumerate(criteria):
267
+ form = getattr(criterion, "bare", criterion) if bare else criterion
268
+ if quote == " ".join(form.split()):
269
+ return index, end
270
+ return None
271
+
272
+
129
273
  def task_verify_command(task_text: str) -> str:
130
274
  """The Verify shell text, excluding the closing fence's separator newline."""
131
275
  body = section_body(task_text, "Verify")
@@ -133,6 +277,11 @@ def task_verify_command(task_text: str) -> str:
133
277
  return block.group("block").removesuffix("\n") if block else ""
134
278
 
135
279
 
280
+ def fix_verify(commands: Iterable[str]) -> str:
281
+ """The Verify of a fix task: each distinct source Verify in its own subshell, under `set -e`."""
282
+ return "set -e\n" + "\n".join(f"(\n{command}\n)" for command in dict.fromkeys(commands))
283
+
284
+
136
285
  def latest_verify_entry(entries: list, command: str, commit: str,
137
286
  repo: Optional[str] = None) -> Optional[dict]:
138
287
  """Rows are keyed by (command, repo, commit); a row without `repo` is a coordinator row."""
@@ -150,6 +299,11 @@ def verify_ledger_entries(path: Path) -> list:
150
299
  return parse_verify_ledger(path.read_text(encoding="utf-8"))
151
300
 
152
301
 
302
+ def read_user_text(path: Path) -> str:
303
+ """Read a user-edited text file with CRLF normalized to LF."""
304
+ return path.read_text(encoding="utf-8").replace("\r\n", "\n").replace("\r", "\n")
305
+
306
+
153
307
  def parse_verify_ledger(text: str) -> list:
154
308
  entries = []
155
309
  for number, line in enumerate(text.splitlines(), start=1):
@@ -204,7 +358,43 @@ def run_command(
204
358
  def run_git(
205
359
  repo: Path, *arguments: str, input: Optional[str] = None
206
360
  ) -> subprocess.CompletedProcess[str]:
207
- return _run_exact(("git", "-C", str(repo), *arguments), input=input)
361
+ return _run_exact(git_command(repo, *arguments), input=input)
362
+
363
+
364
+ def git_command(repo: Path, *arguments: str) -> tuple[str, ...]:
365
+ """Git argv for repo, including WSL drvfs workarounds when needed."""
366
+ return ("git", "-C", str(repo), *git_drvfs_config_flags(repo), *arguments)
367
+
368
+
369
+ def git_drvfs_config_flags(repo: Path) -> tuple[str, ...]:
370
+ """Disable index preload on 9p/drvfs mounts where git commit races on index.lock (#207)."""
371
+ if os.name != "posix":
372
+ return ()
373
+ try:
374
+ root = repo.resolve()
375
+ mounts = Path("/proc/mounts").read_text(encoding="utf-8", errors="replace").splitlines()
376
+ except OSError:
377
+ return ()
378
+ best = ""
379
+ fstype = ""
380
+ for line in mounts:
381
+ parts = line.split()
382
+ if len(parts) < 3:
383
+ continue
384
+ mount_point, mount_type = parts[1], parts[2]
385
+ if mount_type not in {"9p", "drvfs"}:
386
+ continue
387
+ if root == Path(mount_point) or root.is_relative_to(mount_point):
388
+ if len(mount_point) >= len(best):
389
+ best, fstype = mount_point, mount_type
390
+ if not fstype:
391
+ return ()
392
+ return ("-c", "core.preloadindex=false", "-c", "index.threads=1")
393
+
394
+
395
+ def git_index_lock_retryable(stderr: str) -> bool:
396
+ text = stderr.casefold()
397
+ return "index.lock" in text and "file exists" in text
208
398
 
209
399
 
210
400
  # OS files that are never pipeline artifacts when git ignores them.
@@ -227,6 +417,70 @@ def git_visible_entries(directory: Path) -> set:
227
417
  return {path.split("/", 1)[0] for path in result.stdout.split("\0") if path}
228
418
 
229
419
 
420
+ RUNTIME_PIN_PATH = ".gsd-path/runtime.json"
421
+ RUNTIME_PIN_ALLOWANCE = (
422
+ "only runtime-pin commits (.gsd-path/runtime.json) may follow the reviewed HEAD"
423
+ )
424
+
425
+
426
+ def reviewed_head_covers(repo: Path, reviewed: str, head: str) -> bool:
427
+ """True when a review of `reviewed` still covers `head`.
428
+
429
+ That is the same commit, or `reviewed` is an ancestor of `head` and every
430
+ commit on the first-parent path in ``reviewed..head`` is a non-merge commit
431
+ whose only change is ``.gsd-path/runtime.json``. An empty commit does not
432
+ qualify. Any other path, any merge commit, an unresolvable SHA, or a
433
+ non-ancestor returns False.
434
+ """
435
+ if not reviewed or not head:
436
+ return False
437
+ resolved_repo = Path(repo)
438
+ resolved: List[str] = []
439
+ for value in (reviewed, head):
440
+ result = run_git(
441
+ resolved_repo, "rev-parse", "--verify", "--quiet", f"{value}^{{commit}}"
442
+ )
443
+ if result.returncode != 0 or not result.stdout.strip():
444
+ return False
445
+ resolved.append(result.stdout.strip())
446
+ reviewed_sha, head_sha = resolved
447
+ if reviewed_sha == head_sha:
448
+ return True
449
+ if (
450
+ run_git(resolved_repo, "merge-base", "--is-ancestor", reviewed_sha, head_sha).returncode
451
+ != 0
452
+ ):
453
+ return False
454
+ listing = run_git(
455
+ resolved_repo,
456
+ "rev-list",
457
+ "--first-parent",
458
+ "--parents",
459
+ f"{reviewed_sha}..{head_sha}",
460
+ )
461
+ if listing.returncode != 0:
462
+ return False
463
+ for line in listing.stdout.splitlines():
464
+ fields = line.split()
465
+ if len(fields) != 2:
466
+ return False
467
+ commit_sha, parent_sha = fields[0], fields[1]
468
+ changed = run_git(
469
+ resolved_repo,
470
+ "diff",
471
+ "--name-only",
472
+ "--no-renames",
473
+ "--no-ext-diff",
474
+ parent_sha,
475
+ commit_sha,
476
+ )
477
+ if changed.returncode != 0:
478
+ return False
479
+ if {path for path in changed.stdout.splitlines() if path} != {RUNTIME_PIN_PATH}:
480
+ return False
481
+ return True
482
+
483
+
230
484
  def atomic_replace(path: Path, temporary_path: Path, content: str) -> None:
231
485
  temporary_path.parent.mkdir(parents=True, exist_ok=True)
232
486
  descriptor = None
@@ -426,10 +426,10 @@ def frontmatter_value(content: str, key: str) -> Optional[str]:
426
426
  for line in lines[1:]:
427
427
  if line == "---":
428
428
  break
429
- match = re.match(rf"^{re.escape(key)}:\s*([^#]*?)\s*(?:#.*)?$", line)
429
+ match = re.match(rf"^{re.escape(key)}:(.*)$", line)
430
430
  if match:
431
- value = match.group(1).strip().strip('"').strip("'")
432
- return value
431
+ value = _common.strip_yaml_comment(match.group(1))
432
+ return value.strip().strip('"').strip("'")
433
433
  return None
434
434
 
435
435
 
@@ -586,14 +586,11 @@ def resolved_archive_target(
586
586
  expected_number = milestone_number(branch)
587
587
  configured = parsed_state.archive
588
588
  entries = archive_sequence_entries(project)
589
- missing_prior = [
590
- number for number in range(1, expected_number) if number not in entries
591
- ]
592
589
  later = sorted(number for number in entries if number > expected_number)
593
- if missing_prior or later:
590
+ if later:
594
591
  raise ArchiveError(
595
592
  f"archive sequence does not match bound branch {branch}: "
596
- f"missing={missing_prior}, later={later}"
593
+ f"later={later}"
597
594
  )
598
595
 
599
596
  if configured is not None:
@@ -1065,9 +1062,10 @@ def prepare_locked(project: Path, active_root: Path, slug: str) -> dict:
1065
1062
  final = active_root / "review" / "FINAL.md"
1066
1063
  reviewed = re.search(r"(?m)^Reviewed HEAD:\s*(\S+)", final.read_text(encoding="utf-8")) if final.is_file() else None
1067
1064
  head = require_git_success(run_git(project, "rev-parse", "HEAD"), "resolve HEAD").strip()
1068
- if reviewed and reviewed.group(1) != head:
1065
+ if reviewed and not _common.reviewed_head_covers(project, reviewed.group(1), head):
1069
1066
  raise ArchiveError(
1070
1067
  f"FINAL.md Reviewed HEAD {reviewed.group(1)} does not match current HEAD {head}; "
1068
+ f"{_common.RUNTIME_PIN_ALLOWANCE}; "
1071
1069
  "re-run the final review on the current commit before archiving"
1072
1070
  )
1073
1071
  target = resolved_archive_target(project, slug, parsed_state)
@@ -1680,6 +1678,7 @@ def abandon_locked(
1680
1678
 
1681
1679
 
1682
1680
  run_git = _common.run_git
1681
+ reviewed_head_covers = _common.reviewed_head_covers
1683
1682
  run_command = _common.run_command
1684
1683
 
1685
1684
 
@@ -1899,9 +1898,10 @@ def preflight(repo: Path) -> dict:
1899
1898
  require_stageable_carry_forward(project, archive)
1900
1899
  require_clean_older_archives(project, configured)
1901
1900
  head = require_git_success(run_git(project, "rev-parse", "HEAD"), "resolve preflight HEAD")
1902
- if reviewed_head != head:
1901
+ if not reviewed_head_covers(project, reviewed_head, head):
1903
1902
  raise ArchiveError(
1904
- f"FINAL.md Reviewed HEAD {reviewed_head} does not match current HEAD {head}"
1903
+ f"FINAL.md Reviewed HEAD {reviewed_head} does not match current HEAD {head}; "
1904
+ f"{_common.RUNTIME_PIN_ALLOWANCE}"
1905
1905
  )
1906
1906
  return {
1907
1907
  "archive": configured,
@@ -2093,9 +2093,10 @@ def validate(repo: Path, *, historical: bool = False) -> dict:
2093
2093
 
2094
2094
  members = member_ship_rows(project, archive, ship_commit)
2095
2095
  ship_parent = require_git_success(run_git(project, "rev-parse", f"{ship_commit}^"), "resolve ship parent")
2096
- if reviewed_head != ship_parent:
2096
+ if not reviewed_head_covers(project, reviewed_head, ship_parent):
2097
2097
  raise ArchiveError(
2098
- f"FINAL.md Reviewed HEAD {reviewed_head} does not match ship parent {ship_parent}"
2098
+ f"FINAL.md Reviewed HEAD {reviewed_head} does not match ship parent {ship_parent}; "
2099
+ f"{_common.RUNTIME_PIN_ALLOWANCE}"
2099
2100
  )
2100
2101
  require_canonical_commit_body(
2101
2102
  project,