@opengsd/gsd-path 1.3.1 → 1.5.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 (481) hide show
  1. package/AGENTS.md +2 -207
  2. package/CHANGELOG.md +331 -69
  3. package/DOCS.md +25 -9
  4. package/FULL.md +12 -5
  5. package/GUIDE.md +1 -1
  6. package/HOOKS.md +104 -14
  7. package/QUICK.md +5 -2
  8. package/README.md +131 -111
  9. package/RUNTIME.md +2 -2
  10. package/UPDATE.md +61 -11
  11. package/WORKFLOW.md +44 -7
  12. package/package.json +13 -9
  13. package/platforms/shared-agents/dispatch.md +13 -0
  14. package/scripts/_common.py +619 -18
  15. package/scripts/archive_milestone.py +160 -86
  16. package/scripts/bootstrap_repository.py +8 -5
  17. package/scripts/build_recovery.py +100 -32
  18. package/scripts/build_state.py +143 -24
  19. package/scripts/check_docs_audit.py +91 -7
  20. package/scripts/check_handoffs.py +662 -50
  21. package/scripts/check_task_briefs.py +338 -30
  22. package/scripts/check_update.py +2 -2
  23. package/scripts/core_hook_gate.py +10 -1
  24. package/scripts/core_hook_settings.py +11 -4
  25. package/scripts/detect_project.py +587 -32
  26. package/scripts/discussion_records.py +19 -13
  27. package/scripts/discussion_validate.py +152 -50
  28. package/scripts/dispatch_driver.py +501 -46
  29. package/scripts/git_guard.py +118 -31
  30. package/scripts/guard_hook.py +1567 -194
  31. package/scripts/install.mjs +77 -46
  32. package/scripts/install.py +611 -50
  33. package/scripts/integration.py +580 -25
  34. package/scripts/isolation.py +1438 -57
  35. package/scripts/lean_verification.py +99 -19
  36. package/scripts/loop_run.py +31 -29
  37. package/scripts/members.py +706 -0
  38. package/scripts/migrate_core.py +2 -2
  39. package/scripts/path_config.py +5 -5
  40. package/scripts/pipeline_diagnose.py +82 -31
  41. package/scripts/pipeline_git.py +124 -34
  42. package/scripts/pipeline_state.py +287 -87
  43. package/scripts/pipeline_undo.py +218 -30
  44. package/scripts/promote_lookahead.py +1 -1
  45. package/scripts/review_panel.py +1 -1
  46. package/scripts/runtime_store.py +55 -11
  47. package/scripts/skill-resources.json +37 -5
  48. package/scripts/state_checkpoint.py +135 -44
  49. package/scripts/state_promote.py +2 -0
  50. package/scripts/status_runtime.py +58 -10
  51. package/scripts/sync_skill_resources.py +8 -7
  52. package/scripts/task_context.py +12 -3
  53. package/scripts/token_budget.py +12 -8
  54. package/scripts/wizard.mjs +1 -0
  55. package/scripts/workflow_run.py +107 -30
  56. package/scripts/worktree_paths.py +72 -10
  57. package/skills/gsd-path/BUILD.md +7 -1
  58. package/skills/gsd-path/DECIDE.md +11 -4
  59. package/skills/gsd-path/DEFINE.md +36 -4
  60. package/skills/gsd-path/DOCS-AUDIT.md +6 -4
  61. package/skills/gsd-path/INSPECT.md +5 -4
  62. package/skills/gsd-path/PLAN.md +18 -4
  63. package/skills/gsd-path/RESEARCH.md +13 -1
  64. package/skills/gsd-path/ROADMAP.md +2 -1
  65. package/skills/gsd-path/SHIP.md +33 -11
  66. package/skills/gsd-path/SKILL.md +45 -1
  67. package/skills/gsd-path/references/build-native.md +63 -15
  68. package/skills/gsd-path/references/codebase-mapper.md +2 -1
  69. package/skills/gsd-path/references/dispatch.md +13 -0
  70. package/skills/gsd-path/references/docs-auditor.md +7 -1
  71. package/skills/gsd-path/references/operating-rules.md +214 -0
  72. package/skills/gsd-path/references/planner.md +22 -4
  73. package/skills/gsd-path/references/reviewer-final.md +20 -2
  74. package/skills/gsd-path/references/reviewer-wave.md +18 -5
  75. package/skills/gsd-path/references/reviewer.md +3 -1
  76. package/skills/gsd-path/references/spec-probes.md +150 -0
  77. package/skills/gsd-path/scripts/_common.py +619 -18
  78. package/skills/gsd-path/scripts/archive_milestone.py +160 -86
  79. package/skills/gsd-path/scripts/bootstrap_repository.py +8 -5
  80. package/skills/gsd-path/scripts/build_recovery.py +100 -32
  81. package/skills/gsd-path/scripts/build_state.py +143 -24
  82. package/skills/gsd-path/scripts/check_docs_audit.py +91 -7
  83. package/skills/gsd-path/scripts/check_handoffs.py +662 -50
  84. package/skills/gsd-path/scripts/check_task_briefs.py +338 -30
  85. package/skills/gsd-path/scripts/check_update.py +2 -2
  86. package/skills/gsd-path/scripts/detect_project.py +587 -32
  87. package/skills/gsd-path/scripts/discussion_records.py +19 -13
  88. package/skills/gsd-path/scripts/discussion_validate.py +152 -50
  89. package/skills/gsd-path/scripts/dispatch_driver.py +501 -46
  90. package/skills/gsd-path/scripts/git_guard.py +118 -31
  91. package/skills/gsd-path/scripts/integration.py +580 -25
  92. package/skills/gsd-path/scripts/isolation.py +1438 -57
  93. package/skills/gsd-path/scripts/lean_verification.py +99 -19
  94. package/skills/gsd-path/scripts/loop_run.py +31 -29
  95. package/skills/gsd-path/scripts/members.py +706 -0
  96. package/skills/gsd-path/scripts/path_config.py +5 -5
  97. package/skills/gsd-path/scripts/pipeline_git.py +124 -34
  98. package/skills/gsd-path/scripts/pipeline_state.py +287 -87
  99. package/skills/gsd-path/scripts/promote_lookahead.py +1 -1
  100. package/skills/gsd-path/scripts/review_panel.py +1 -1
  101. package/skills/gsd-path/scripts/state_checkpoint.py +135 -44
  102. package/skills/gsd-path/scripts/state_promote.py +2 -0
  103. package/skills/gsd-path/scripts/status_runtime.py +58 -10
  104. package/skills/gsd-path/scripts/task_context.py +12 -3
  105. package/skills/gsd-path/scripts/token_budget.py +12 -8
  106. package/skills/gsd-path/scripts/workflow_run.py +107 -30
  107. package/skills/gsd-path/scripts/worktree_paths.py +72 -10
  108. package/skills/gsd-path/templates/codebase.md +1 -1
  109. package/skills/gsd-path/templates/docs-audit.md +6 -2
  110. package/skills/gsd-path/templates/final-review.md +16 -4
  111. package/skills/gsd-path/templates/gap-review.md +2 -1
  112. package/skills/gsd-path/templates/intent.md +30 -0
  113. package/skills/gsd-path/templates/patch-findings.md +3 -3
  114. package/skills/gsd-path/templates/plan.md +17 -2
  115. package/skills/gsd-path/templates/research-handoff.md +14 -3
  116. package/skills/gsd-path/templates/state.md +1 -1
  117. package/skills/gsd-path/templates/task.md +1 -0
  118. package/skills/gsd-path/templates/wave-review.md +16 -4
  119. package/skills/gsd-path-build/SKILL.md +7 -1
  120. package/skills/gsd-path-build/references/build-native.md +63 -15
  121. package/skills/gsd-path-build/references/dispatch.md +13 -0
  122. package/skills/gsd-path-build/references/operating-rules.md +214 -0
  123. package/skills/gsd-path-build/references/reviewer-final.md +20 -2
  124. package/skills/gsd-path-build/references/reviewer-wave.md +18 -5
  125. package/skills/gsd-path-build/references/reviewer.md +3 -1
  126. package/skills/gsd-path-build/references/spec-probes.md +150 -0
  127. package/skills/gsd-path-build/scripts/_common.py +619 -18
  128. package/skills/gsd-path-build/scripts/archive_milestone.py +160 -86
  129. package/skills/gsd-path-build/scripts/build_recovery.py +100 -32
  130. package/skills/gsd-path-build/scripts/build_state.py +143 -24
  131. package/skills/gsd-path-build/scripts/check_handoffs.py +662 -50
  132. package/skills/gsd-path-build/scripts/check_task_briefs.py +338 -30
  133. package/skills/gsd-path-build/scripts/detect_project.py +587 -32
  134. package/skills/gsd-path-build/scripts/discussion_records.py +19 -13
  135. package/skills/gsd-path-build/scripts/discussion_validate.py +152 -50
  136. package/skills/gsd-path-build/scripts/dispatch_driver.py +501 -46
  137. package/skills/gsd-path-build/scripts/integration.py +580 -25
  138. package/skills/gsd-path-build/scripts/isolation.py +1438 -57
  139. package/skills/gsd-path-build/scripts/loop_run.py +31 -29
  140. package/skills/gsd-path-build/scripts/members.py +706 -0
  141. package/skills/gsd-path-build/scripts/path_config.py +5 -5
  142. package/skills/gsd-path-build/scripts/pipeline_git.py +124 -34
  143. package/skills/gsd-path-build/scripts/pipeline_state.py +287 -87
  144. package/skills/gsd-path-build/scripts/promote_lookahead.py +1 -1
  145. package/skills/gsd-path-build/scripts/review_panel.py +1 -1
  146. package/skills/gsd-path-build/scripts/state_checkpoint.py +135 -44
  147. package/skills/gsd-path-build/scripts/state_promote.py +2 -0
  148. package/skills/gsd-path-build/scripts/task_context.py +12 -3
  149. package/skills/gsd-path-build/scripts/token_budget.py +12 -8
  150. package/skills/gsd-path-build/scripts/workflow_run.py +107 -30
  151. package/skills/gsd-path-build/scripts/worktree_paths.py +72 -10
  152. package/skills/gsd-path-build/templates/task.md +1 -0
  153. package/skills/gsd-path-build/templates/wave-review.md +16 -4
  154. package/skills/gsd-path-decide/SKILL.md +11 -4
  155. package/skills/gsd-path-decide/references/dispatch.md +13 -0
  156. package/skills/gsd-path-decide/references/operating-rules.md +214 -0
  157. package/skills/gsd-path-decide/scripts/_common.py +619 -18
  158. package/skills/gsd-path-decide/scripts/archive_milestone.py +160 -86
  159. package/skills/gsd-path-decide/scripts/build_recovery.py +100 -32
  160. package/skills/gsd-path-decide/scripts/check_handoffs.py +662 -50
  161. package/skills/gsd-path-decide/scripts/check_task_briefs.py +338 -30
  162. package/skills/gsd-path-decide/scripts/detect_project.py +587 -32
  163. package/skills/gsd-path-decide/scripts/discussion_records.py +19 -13
  164. package/skills/gsd-path-decide/scripts/discussion_validate.py +152 -50
  165. package/skills/gsd-path-decide/scripts/integration.py +580 -25
  166. package/skills/gsd-path-decide/scripts/isolation.py +1438 -57
  167. package/skills/gsd-path-decide/scripts/loop_run.py +31 -29
  168. package/skills/gsd-path-decide/scripts/members.py +706 -0
  169. package/skills/gsd-path-decide/scripts/path_config.py +5 -5
  170. package/skills/gsd-path-decide/scripts/pipeline_git.py +124 -34
  171. package/skills/gsd-path-decide/scripts/pipeline_state.py +287 -87
  172. package/skills/gsd-path-decide/scripts/promote_lookahead.py +1 -1
  173. package/skills/gsd-path-decide/scripts/review_panel.py +1 -1
  174. package/skills/gsd-path-decide/scripts/state_checkpoint.py +135 -44
  175. package/skills/gsd-path-decide/scripts/state_promote.py +2 -0
  176. package/skills/gsd-path-decide/scripts/worktree_paths.py +72 -10
  177. package/skills/gsd-path-define/SKILL.md +36 -4
  178. package/skills/gsd-path-define/references/operating-rules.md +214 -0
  179. package/skills/gsd-path-define/references/spec-probes.md +150 -0
  180. package/skills/gsd-path-define/scripts/_common.py +619 -18
  181. package/skills/gsd-path-define/scripts/archive_milestone.py +160 -86
  182. package/skills/gsd-path-define/scripts/build_recovery.py +100 -32
  183. package/skills/gsd-path-define/scripts/check_handoffs.py +662 -50
  184. package/skills/gsd-path-define/scripts/check_task_briefs.py +338 -30
  185. package/skills/gsd-path-define/scripts/detect_project.py +587 -32
  186. package/skills/gsd-path-define/scripts/discussion_records.py +19 -13
  187. package/skills/gsd-path-define/scripts/discussion_validate.py +152 -50
  188. package/skills/gsd-path-define/scripts/integration.py +580 -25
  189. package/skills/gsd-path-define/scripts/isolation.py +1438 -57
  190. package/skills/gsd-path-define/scripts/loop_run.py +31 -29
  191. package/skills/gsd-path-define/scripts/members.py +706 -0
  192. package/skills/gsd-path-define/scripts/path_config.py +5 -5
  193. package/skills/gsd-path-define/scripts/pipeline_git.py +124 -34
  194. package/skills/gsd-path-define/scripts/pipeline_state.py +287 -87
  195. package/skills/gsd-path-define/scripts/promote_lookahead.py +1 -1
  196. package/skills/gsd-path-define/scripts/review_panel.py +1 -1
  197. package/skills/gsd-path-define/scripts/state_checkpoint.py +135 -44
  198. package/skills/gsd-path-define/scripts/state_promote.py +2 -0
  199. package/skills/gsd-path-define/scripts/worktree_paths.py +72 -10
  200. package/skills/gsd-path-define/templates/intent.md +30 -0
  201. package/skills/gsd-path-define/templates/state.md +1 -1
  202. package/skills/gsd-path-discuss/SKILL.md +2 -1
  203. package/skills/gsd-path-discuss/references/operating-rules.md +214 -0
  204. package/skills/gsd-path-discuss/scripts/_common.py +619 -18
  205. package/skills/gsd-path-discuss/scripts/discussion_records.py +19 -13
  206. package/skills/gsd-path-discuss/scripts/loop_run.py +31 -29
  207. package/skills/gsd-path-discuss/scripts/path_config.py +5 -5
  208. package/skills/gsd-path-discuss/scripts/review_panel.py +1 -1
  209. package/skills/gsd-path-discuss/scripts/worktree_paths.py +72 -10
  210. package/skills/gsd-path-docs-audit/SKILL.md +6 -4
  211. package/skills/gsd-path-docs-audit/references/dispatch.md +13 -0
  212. package/skills/gsd-path-docs-audit/references/docs-auditor.md +7 -1
  213. package/skills/gsd-path-docs-audit/references/operating-rules.md +214 -0
  214. package/skills/gsd-path-docs-audit/scripts/_common.py +619 -18
  215. package/skills/gsd-path-docs-audit/scripts/archive_milestone.py +160 -86
  216. package/skills/gsd-path-docs-audit/scripts/build_recovery.py +100 -32
  217. package/skills/gsd-path-docs-audit/scripts/check_docs_audit.py +91 -7
  218. package/skills/gsd-path-docs-audit/scripts/check_handoffs.py +662 -50
  219. package/skills/gsd-path-docs-audit/scripts/check_task_briefs.py +338 -30
  220. package/skills/gsd-path-docs-audit/scripts/detect_project.py +587 -32
  221. package/skills/gsd-path-docs-audit/scripts/discussion_records.py +19 -13
  222. package/skills/gsd-path-docs-audit/scripts/discussion_validate.py +152 -50
  223. package/skills/gsd-path-docs-audit/scripts/integration.py +580 -25
  224. package/skills/gsd-path-docs-audit/scripts/isolation.py +1438 -57
  225. package/skills/gsd-path-docs-audit/scripts/loop_run.py +31 -29
  226. package/skills/gsd-path-docs-audit/scripts/members.py +706 -0
  227. package/skills/gsd-path-docs-audit/scripts/path_config.py +5 -5
  228. package/skills/gsd-path-docs-audit/scripts/pipeline_git.py +124 -34
  229. package/skills/gsd-path-docs-audit/scripts/pipeline_state.py +287 -87
  230. package/skills/gsd-path-docs-audit/scripts/promote_lookahead.py +1 -1
  231. package/skills/gsd-path-docs-audit/scripts/review_panel.py +1 -1
  232. package/skills/gsd-path-docs-audit/scripts/state_checkpoint.py +135 -44
  233. package/skills/gsd-path-docs-audit/scripts/state_promote.py +2 -0
  234. package/skills/gsd-path-docs-audit/scripts/worktree_paths.py +72 -10
  235. package/skills/gsd-path-docs-audit/templates/docs-audit.md +6 -2
  236. package/skills/gsd-path-forensics/SKILL.md +2 -1
  237. package/skills/gsd-path-forensics/references/operating-rules.md +214 -0
  238. package/skills/gsd-path-forensics/scripts/_common.py +619 -18
  239. package/skills/gsd-path-forensics/scripts/discussion_records.py +19 -13
  240. package/skills/gsd-path-forensics/scripts/loop_run.py +31 -29
  241. package/skills/gsd-path-forensics/scripts/path_config.py +5 -5
  242. package/skills/gsd-path-forensics/scripts/pipeline_diagnose.py +82 -31
  243. package/skills/gsd-path-forensics/scripts/pipeline_undo.py +218 -30
  244. package/skills/gsd-path-forensics/scripts/review_panel.py +1 -1
  245. package/skills/gsd-path-forensics/scripts/worktree_paths.py +72 -10
  246. package/skills/gsd-path-inspect/SKILL.md +5 -4
  247. package/skills/gsd-path-inspect/references/codebase-mapper.md +2 -1
  248. package/skills/gsd-path-inspect/references/dispatch.md +13 -0
  249. package/skills/gsd-path-inspect/references/docs-auditor.md +7 -1
  250. package/skills/gsd-path-inspect/references/operating-rules.md +214 -0
  251. package/skills/gsd-path-inspect/scripts/_common.py +619 -18
  252. package/skills/gsd-path-inspect/scripts/archive_milestone.py +160 -86
  253. package/skills/gsd-path-inspect/scripts/build_recovery.py +100 -32
  254. package/skills/gsd-path-inspect/scripts/check_docs_audit.py +91 -7
  255. package/skills/gsd-path-inspect/scripts/check_handoffs.py +662 -50
  256. package/skills/gsd-path-inspect/scripts/check_task_briefs.py +338 -30
  257. package/skills/gsd-path-inspect/scripts/detect_project.py +587 -32
  258. package/skills/gsd-path-inspect/scripts/discussion_records.py +19 -13
  259. package/skills/gsd-path-inspect/scripts/discussion_validate.py +152 -50
  260. package/skills/gsd-path-inspect/scripts/integration.py +580 -25
  261. package/skills/gsd-path-inspect/scripts/isolation.py +1438 -57
  262. package/skills/gsd-path-inspect/scripts/loop_run.py +31 -29
  263. package/skills/gsd-path-inspect/scripts/members.py +706 -0
  264. package/skills/gsd-path-inspect/scripts/path_config.py +5 -5
  265. package/skills/gsd-path-inspect/scripts/pipeline_git.py +124 -34
  266. package/skills/gsd-path-inspect/scripts/pipeline_state.py +287 -87
  267. package/skills/gsd-path-inspect/scripts/promote_lookahead.py +1 -1
  268. package/skills/gsd-path-inspect/scripts/review_panel.py +1 -1
  269. package/skills/gsd-path-inspect/scripts/state_checkpoint.py +135 -44
  270. package/skills/gsd-path-inspect/scripts/state_promote.py +2 -0
  271. package/skills/gsd-path-inspect/scripts/workflow_run.py +107 -30
  272. package/skills/gsd-path-inspect/scripts/worktree_paths.py +72 -10
  273. package/skills/gsd-path-inspect/templates/codebase.md +1 -1
  274. package/skills/gsd-path-inspect/templates/docs-audit.md +6 -2
  275. package/skills/gsd-path-inspect/templates/state.md +1 -1
  276. package/skills/gsd-path-loop/SKILL.md +2 -1
  277. package/skills/gsd-path-loop/references/operating-rules.md +214 -0
  278. package/skills/gsd-path-loop/scripts/_common.py +619 -18
  279. package/skills/gsd-path-loop/scripts/discussion_records.py +19 -13
  280. package/skills/gsd-path-loop/scripts/loop_run.py +31 -29
  281. package/skills/gsd-path-loop/scripts/path_config.py +5 -5
  282. package/skills/gsd-path-loop/scripts/review_panel.py +1 -1
  283. package/skills/gsd-path-loop/scripts/worktree_paths.py +72 -10
  284. package/skills/gsd-path-migrate/SKILL.md +2 -1
  285. package/skills/gsd-path-migrate/references/operating-rules.md +214 -0
  286. package/skills/gsd-path-migrate/scripts/_common.py +619 -18
  287. package/skills/gsd-path-migrate/scripts/core_hook_gate.py +10 -1
  288. package/skills/gsd-path-migrate/scripts/core_hook_settings.py +11 -4
  289. package/skills/gsd-path-migrate/scripts/discussion_records.py +19 -13
  290. package/skills/gsd-path-migrate/scripts/loop_run.py +31 -29
  291. package/skills/gsd-path-migrate/scripts/migrate_core.py +2 -2
  292. package/skills/gsd-path-migrate/scripts/path_config.py +5 -5
  293. package/skills/gsd-path-migrate/scripts/review_panel.py +1 -1
  294. package/skills/gsd-path-plan/SKILL.md +18 -4
  295. package/skills/gsd-path-plan/references/dispatch.md +13 -0
  296. package/skills/gsd-path-plan/references/operating-rules.md +214 -0
  297. package/skills/gsd-path-plan/references/planner.md +22 -4
  298. package/skills/gsd-path-plan/references/spec-probes.md +150 -0
  299. package/skills/gsd-path-plan/scripts/_common.py +619 -18
  300. package/skills/gsd-path-plan/scripts/archive_milestone.py +160 -86
  301. package/skills/gsd-path-plan/scripts/build_recovery.py +100 -32
  302. package/skills/gsd-path-plan/scripts/check_handoffs.py +662 -50
  303. package/skills/gsd-path-plan/scripts/check_task_briefs.py +338 -30
  304. package/skills/gsd-path-plan/scripts/detect_project.py +587 -32
  305. package/skills/gsd-path-plan/scripts/discussion_records.py +19 -13
  306. package/skills/gsd-path-plan/scripts/discussion_validate.py +152 -50
  307. package/skills/gsd-path-plan/scripts/integration.py +580 -25
  308. package/skills/gsd-path-plan/scripts/isolation.py +1438 -57
  309. package/skills/gsd-path-plan/scripts/loop_run.py +31 -29
  310. package/skills/gsd-path-plan/scripts/members.py +706 -0
  311. package/skills/gsd-path-plan/scripts/path_config.py +5 -5
  312. package/skills/gsd-path-plan/scripts/pipeline_git.py +124 -34
  313. package/skills/gsd-path-plan/scripts/pipeline_state.py +287 -87
  314. package/skills/gsd-path-plan/scripts/promote_lookahead.py +1 -1
  315. package/skills/gsd-path-plan/scripts/review_panel.py +1 -1
  316. package/skills/gsd-path-plan/scripts/state_checkpoint.py +135 -44
  317. package/skills/gsd-path-plan/scripts/state_promote.py +2 -0
  318. package/skills/gsd-path-plan/scripts/workflow_run.py +107 -30
  319. package/skills/gsd-path-plan/scripts/worktree_paths.py +72 -10
  320. package/skills/gsd-path-plan/templates/patch-findings.md +3 -3
  321. package/skills/gsd-path-plan/templates/plan.md +17 -2
  322. package/skills/gsd-path-plan/templates/task.md +1 -0
  323. package/skills/gsd-path-research/SKILL.md +13 -1
  324. package/skills/gsd-path-research/references/dispatch.md +13 -0
  325. package/skills/gsd-path-research/references/operating-rules.md +214 -0
  326. package/skills/gsd-path-research/scripts/_common.py +619 -18
  327. package/skills/gsd-path-research/scripts/archive_milestone.py +160 -86
  328. package/skills/gsd-path-research/scripts/build_recovery.py +100 -32
  329. package/skills/gsd-path-research/scripts/check_handoffs.py +662 -50
  330. package/skills/gsd-path-research/scripts/check_task_briefs.py +338 -30
  331. package/skills/gsd-path-research/scripts/detect_project.py +587 -32
  332. package/skills/gsd-path-research/scripts/discussion_records.py +19 -13
  333. package/skills/gsd-path-research/scripts/discussion_validate.py +152 -50
  334. package/skills/gsd-path-research/scripts/integration.py +580 -25
  335. package/skills/gsd-path-research/scripts/isolation.py +1438 -57
  336. package/skills/gsd-path-research/scripts/loop_run.py +31 -29
  337. package/skills/gsd-path-research/scripts/members.py +706 -0
  338. package/skills/gsd-path-research/scripts/path_config.py +5 -5
  339. package/skills/gsd-path-research/scripts/pipeline_git.py +124 -34
  340. package/skills/gsd-path-research/scripts/pipeline_state.py +287 -87
  341. package/skills/gsd-path-research/scripts/promote_lookahead.py +1 -1
  342. package/skills/gsd-path-research/scripts/review_panel.py +1 -1
  343. package/skills/gsd-path-research/scripts/state_checkpoint.py +135 -44
  344. package/skills/gsd-path-research/scripts/state_promote.py +2 -0
  345. package/skills/gsd-path-research/scripts/worktree_paths.py +72 -10
  346. package/skills/gsd-path-research/templates/research-handoff.md +14 -3
  347. package/skills/gsd-path-roadmap/SKILL.md +2 -1
  348. package/skills/gsd-path-roadmap/references/dispatch.md +13 -0
  349. package/skills/gsd-path-roadmap/references/operating-rules.md +214 -0
  350. package/skills/gsd-path-roadmap/scripts/_common.py +619 -18
  351. package/skills/gsd-path-roadmap/scripts/archive_milestone.py +160 -86
  352. package/skills/gsd-path-roadmap/scripts/build_recovery.py +100 -32
  353. package/skills/gsd-path-roadmap/scripts/check_handoffs.py +662 -50
  354. package/skills/gsd-path-roadmap/scripts/check_task_briefs.py +338 -30
  355. package/skills/gsd-path-roadmap/scripts/detect_project.py +587 -32
  356. package/skills/gsd-path-roadmap/scripts/discussion_records.py +19 -13
  357. package/skills/gsd-path-roadmap/scripts/discussion_validate.py +152 -50
  358. package/skills/gsd-path-roadmap/scripts/integration.py +580 -25
  359. package/skills/gsd-path-roadmap/scripts/isolation.py +1438 -57
  360. package/skills/gsd-path-roadmap/scripts/loop_run.py +31 -29
  361. package/skills/gsd-path-roadmap/scripts/members.py +706 -0
  362. package/skills/gsd-path-roadmap/scripts/path_config.py +5 -5
  363. package/skills/gsd-path-roadmap/scripts/pipeline_git.py +124 -34
  364. package/skills/gsd-path-roadmap/scripts/pipeline_state.py +287 -87
  365. package/skills/gsd-path-roadmap/scripts/promote_lookahead.py +1 -1
  366. package/skills/gsd-path-roadmap/scripts/review_panel.py +1 -1
  367. package/skills/gsd-path-roadmap/scripts/state_checkpoint.py +135 -44
  368. package/skills/gsd-path-roadmap/scripts/state_promote.py +2 -0
  369. package/skills/gsd-path-roadmap/scripts/worktree_paths.py +72 -10
  370. package/skills/gsd-path-ship/SKILL.md +33 -11
  371. package/skills/gsd-path-ship/references/dispatch.md +13 -0
  372. package/skills/gsd-path-ship/references/operating-rules.md +214 -0
  373. package/skills/gsd-path-ship/references/reviewer-final.md +20 -2
  374. package/skills/gsd-path-ship/references/reviewer-wave.md +18 -5
  375. package/skills/gsd-path-ship/references/reviewer.md +3 -1
  376. package/skills/gsd-path-ship/references/spec-probes.md +150 -0
  377. package/skills/gsd-path-ship/scripts/_common.py +619 -18
  378. package/skills/gsd-path-ship/scripts/archive_milestone.py +160 -86
  379. package/skills/gsd-path-ship/scripts/build_recovery.py +100 -32
  380. package/skills/gsd-path-ship/scripts/build_state.py +143 -24
  381. package/skills/gsd-path-ship/scripts/check_handoffs.py +662 -50
  382. package/skills/gsd-path-ship/scripts/check_task_briefs.py +338 -30
  383. package/skills/gsd-path-ship/scripts/detect_project.py +587 -32
  384. package/skills/gsd-path-ship/scripts/discussion_records.py +19 -13
  385. package/skills/gsd-path-ship/scripts/discussion_validate.py +152 -50
  386. package/skills/gsd-path-ship/scripts/integration.py +580 -25
  387. package/skills/gsd-path-ship/scripts/isolation.py +1438 -57
  388. package/skills/gsd-path-ship/scripts/lean_verification.py +99 -19
  389. package/skills/gsd-path-ship/scripts/loop_run.py +31 -29
  390. package/skills/gsd-path-ship/scripts/members.py +706 -0
  391. package/skills/gsd-path-ship/scripts/path_config.py +5 -5
  392. package/skills/gsd-path-ship/scripts/pipeline_git.py +124 -34
  393. package/skills/gsd-path-ship/scripts/pipeline_state.py +287 -87
  394. package/skills/gsd-path-ship/scripts/promote_lookahead.py +1 -1
  395. package/skills/gsd-path-ship/scripts/review_panel.py +1 -1
  396. package/skills/gsd-path-ship/scripts/state_checkpoint.py +135 -44
  397. package/skills/gsd-path-ship/scripts/state_promote.py +2 -0
  398. package/skills/gsd-path-ship/scripts/workflow_run.py +107 -30
  399. package/skills/gsd-path-ship/scripts/worktree_paths.py +72 -10
  400. package/skills/gsd-path-ship/templates/final-review.md +16 -4
  401. package/skills/gsd-path-ship/templates/gap-review.md +2 -1
  402. package/skills/gsd-path-ship/templates/patch-findings.md +3 -3
  403. package/skills/gsd-path-ship/templates/wave-review.md +16 -4
  404. package/skills/gsd-path-undo/SKILL.md +2 -1
  405. package/skills/gsd-path-undo/references/operating-rules.md +214 -0
  406. package/skills/gsd-path-undo/scripts/_common.py +619 -18
  407. package/skills/gsd-path-undo/scripts/discussion_records.py +19 -13
  408. package/skills/gsd-path-undo/scripts/loop_run.py +31 -29
  409. package/skills/gsd-path-undo/scripts/path_config.py +5 -5
  410. package/skills/gsd-path-undo/scripts/pipeline_undo.py +218 -30
  411. package/skills/gsd-path-undo/scripts/review_panel.py +1 -1
  412. package/skills/gsd-path-undo/scripts/worktree_paths.py +72 -10
  413. package/skills/path/BUILD.md +7 -1
  414. package/skills/path/DECIDE.md +11 -4
  415. package/skills/path/DEFINE.md +36 -4
  416. package/skills/path/DOCS-AUDIT.md +6 -4
  417. package/skills/path/INSPECT.md +5 -4
  418. package/skills/path/PLAN.md +18 -4
  419. package/skills/path/RESEARCH.md +13 -1
  420. package/skills/path/ROADMAP.md +2 -1
  421. package/skills/path/SHIP.md +33 -11
  422. package/skills/path/SKILL.md +45 -1
  423. package/skills/path/references/build-native.md +63 -15
  424. package/skills/path/references/codebase-mapper.md +2 -1
  425. package/skills/path/references/dispatch.md +13 -0
  426. package/skills/path/references/docs-auditor.md +7 -1
  427. package/skills/path/references/operating-rules.md +214 -0
  428. package/skills/path/references/planner.md +22 -4
  429. package/skills/path/references/reviewer-final.md +20 -2
  430. package/skills/path/references/reviewer-wave.md +18 -5
  431. package/skills/path/references/reviewer.md +3 -1
  432. package/skills/path/references/spec-probes.md +150 -0
  433. package/skills/path/scripts/_common.py +619 -18
  434. package/skills/path/scripts/archive_milestone.py +160 -86
  435. package/skills/path/scripts/bootstrap_repository.py +8 -5
  436. package/skills/path/scripts/build_recovery.py +100 -32
  437. package/skills/path/scripts/build_state.py +143 -24
  438. package/skills/path/scripts/check_docs_audit.py +91 -7
  439. package/skills/path/scripts/check_handoffs.py +662 -50
  440. package/skills/path/scripts/check_task_briefs.py +338 -30
  441. package/skills/path/scripts/check_update.py +2 -2
  442. package/skills/path/scripts/detect_project.py +587 -32
  443. package/skills/path/scripts/discussion_records.py +19 -13
  444. package/skills/path/scripts/discussion_validate.py +152 -50
  445. package/skills/path/scripts/dispatch_driver.py +501 -46
  446. package/skills/path/scripts/git_guard.py +118 -31
  447. package/skills/path/scripts/integration.py +580 -25
  448. package/skills/path/scripts/isolation.py +1438 -57
  449. package/skills/path/scripts/lean_verification.py +99 -19
  450. package/skills/path/scripts/loop_run.py +31 -29
  451. package/skills/path/scripts/members.py +706 -0
  452. package/skills/path/scripts/path_config.py +5 -5
  453. package/skills/path/scripts/pipeline_git.py +124 -34
  454. package/skills/path/scripts/pipeline_state.py +287 -87
  455. package/skills/path/scripts/promote_lookahead.py +1 -1
  456. package/skills/path/scripts/review_panel.py +1 -1
  457. package/skills/path/scripts/state_checkpoint.py +135 -44
  458. package/skills/path/scripts/state_promote.py +2 -0
  459. package/skills/path/scripts/status_runtime.py +58 -10
  460. package/skills/path/scripts/task_context.py +12 -3
  461. package/skills/path/scripts/token_budget.py +12 -8
  462. package/skills/path/scripts/workflow_run.py +107 -30
  463. package/skills/path/scripts/worktree_paths.py +72 -10
  464. package/skills/path/templates/codebase.md +1 -1
  465. package/skills/path/templates/docs-audit.md +6 -2
  466. package/skills/path/templates/final-review.md +16 -4
  467. package/skills/path/templates/gap-review.md +2 -1
  468. package/skills/path/templates/intent.md +30 -0
  469. package/skills/path/templates/patch-findings.md +3 -3
  470. package/skills/path/templates/plan.md +17 -2
  471. package/skills/path/templates/research-handoff.md +14 -3
  472. package/skills/path/templates/state.md +1 -1
  473. package/skills/path/templates/task.md +1 -0
  474. package/skills/path/templates/wave-review.md +16 -4
  475. package/skills/gsd-path/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  476. package/skills/gsd-path/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  477. package/skills/gsd-path-build/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  478. package/skills/gsd-path-build/scripts/__pycache__/check_handoffs.cpython-312.pyc +0 -0
  479. package/skills/gsd-path-inspect/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  480. package/skills/gsd-path-plan/scripts/__pycache__/_common.cpython-312.pyc +0 -0
  481. package/skills/gsd-path-ship/scripts/__pycache__/_common.cpython-312.pyc +0 -0
@@ -17,12 +17,15 @@ from datetime import date
17
17
  from pathlib import Path, PurePosixPath
18
18
  from typing import Mapping, Optional, Sequence
19
19
 
20
- try:
21
- from isolation import IsolationError, checkpoint as isolation_checkpoint, require_commit, require_full_sha, PROJECT_ENTRIES
22
- except ModuleNotFoundError as error: # pragma: no cover - package imports used by tests
23
- if error.name != "isolation":
24
- raise
25
- from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint, require_commit, require_full_sha, PROJECT_ENTRIES
20
+ if __package__:
21
+ from .isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
22
+ else:
23
+ try:
24
+ from isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
25
+ except ModuleNotFoundError as error: # pragma: no cover - package imports used by tests
26
+ if error.name != "isolation":
27
+ raise
28
+ from scripts.isolation import IsolationError, checkpoint as isolation_checkpoint, PROJECT_ENTRIES
26
29
 
27
30
 
28
31
  if __package__:
@@ -66,6 +69,11 @@ def _sha256(content: str) -> str:
66
69
  return hashlib.sha256(content.encode("utf-8")).hexdigest()
67
70
 
68
71
 
72
+ def _is_junk_file(path: Path) -> bool:
73
+ # Drift digests skip OS junk by name: Finder rewrites it whatever its ignore status.
74
+ return path.name in pipeline_state._common.OS_JUNK_NAMES and path.is_file() and not path.is_symlink()
75
+
76
+
69
77
  def _tree_digest(path: Path, excluded: Sequence[str] = ()) -> str:
70
78
  if path.is_symlink() or not path.is_dir():
71
79
  raise PipelineStateError(f"promotion path must be a real directory: {path}")
@@ -74,7 +82,7 @@ def _tree_digest(path: Path, excluded: Sequence[str] = ()) -> str:
74
82
  if child.is_symlink():
75
83
  raise PipelineStateError(f"promotion path contains a symlink: {child}")
76
84
  relative = child.relative_to(path).as_posix()
77
- if PurePosixPath(relative).parts[0] in excluded:
85
+ if PurePosixPath(relative).parts[0] in excluded or _is_junk_file(child):
78
86
  continue
79
87
  if child.is_dir():
80
88
  digest.update(f"d\0{relative}\0".encode())
@@ -87,15 +95,15 @@ def _tree_digest(path: Path, excluded: Sequence[str] = ()) -> str:
87
95
 
88
96
 
89
97
  def _checkpoint_artifact_digest(project: Path, mutable_paths: set[str]) -> str:
90
- """Hash every approval artifact except the helper-owned metadata files."""
98
+ """Hash approval artifacts except helper-owned metadata and OS junk."""
91
99
  digest = hashlib.sha256()
92
100
  for child in sorted(project.rglob("*")):
93
101
  if child.is_symlink():
94
102
  raise PipelineStateError(f"checkpoint path contains a symlink: {child}")
95
103
  relative = child.relative_to(project).as_posix()
96
- # Unsupported entries (OS junk) are refused by the checkpoint itself; leaving them
97
- # out lets a refused approval resume after the owner removes them.
98
- if relative in mutable_paths or relative.split("/", 1)[0] not in PROJECT_ENTRIES:
104
+ # Finder can rewrite OS junk during approval; it must not cause artifact drift.
105
+ if (relative in mutable_paths or relative.split("/", 1)[0] not in PROJECT_ENTRIES
106
+ or _is_junk_file(child)):
99
107
  continue
100
108
  if child.is_dir():
101
109
  digest.update(f"d\0{relative}\0".encode())
@@ -333,11 +341,7 @@ def _converge_checkpoint_file(path: Path, before: str, after: str, label: str) -
333
341
 
334
342
  def _unlink_checkpoint_journal(path: Path) -> None:
335
343
  path.unlink()
336
- descriptor = os.open(path.parent, os.O_RDONLY)
337
- try:
338
- os.fsync(descriptor)
339
- finally:
340
- os.close(descriptor)
344
+ pipeline_state._common.fsync_directory(path.parent)
341
345
 
342
346
 
343
347
  def _resume_checkpoint_locked(
@@ -439,41 +443,85 @@ def _validate_plan_briefs(repo: Path, kind: str, project_dir: str) -> None:
439
443
  if head.returncode != 0:
440
444
  # Pre-Git approval defers base-dependent checks to build.
441
445
  return
446
+ if __package__:
447
+ from .check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
448
+ else:
449
+ try:
450
+ from check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
451
+ except ModuleNotFoundError as error: # pragma: no cover - package imports
452
+ if error.name != "check_task_briefs":
453
+ raise
454
+ from scripts.check_task_briefs import BriefError, _member_bases, plan_validation_bases, validate_task_briefs
455
+ if __package__:
456
+ from . import check_handoffs
457
+ else:
458
+ try:
459
+ import check_handoffs
460
+ except ModuleNotFoundError as error:
461
+ if error.name != "check_handoffs":
462
+ raise
463
+ from scripts import check_handoffs
442
464
  try:
443
- from check_task_briefs import BriefError, validate_task_briefs
444
- except ModuleNotFoundError as error: # pragma: no cover - package imports
445
- if error.name != "check_task_briefs":
446
- raise
447
- from scripts.check_task_briefs import BriefError, validate_task_briefs
448
- try:
449
- import check_handoffs
450
- except ModuleNotFoundError as error:
451
- if error.name != "check_handoffs":
452
- raise
453
- from scripts import check_handoffs
454
- try:
455
- tasks, dependency_files = check_handoffs.plan_brief_inputs(repo, project_dir)
456
- landed_bases = {}
457
- for task_id, text in tasks.items():
458
- if check_handoffs._task_scalar(text, task_id, "status") != "done":
459
- continue
460
- agent = check_handoffs._task_scalar(text, task_id, "agent")
461
- if agent in {"", "null"}:
462
- raise BriefError(f"{task_id} landed task has no recorded agent")
463
- recorded_base = check_handoffs._task_scalar(text, task_id, "base")
464
- try:
465
- landed_bases[task_id] = require_commit(repo, require_full_sha(recorded_base))
466
- except IsolationError as error:
467
- raise BriefError(f"{task_id} landed task has invalid historical base: {error}") from error
468
- dependency_files[task_id] = set()
465
+ member_base = _member_bases(repo)
466
+ tasks, dependency_files, landed_bases = plan_validation_bases(
467
+ repo, project_dir, member_base, initial=True
468
+ )
469
469
  validate_task_briefs(
470
470
  repo, head.stdout.strip(), f"{project_dir}/tasks",
471
471
  dependency_files=dependency_files, landed_bases=landed_bases,
472
+ member_base=member_base,
472
473
  )
474
+ if project_dir == ".project/next":
475
+ # Promotion checks lookahead member tasks from the bases their briefs were checked at.
476
+ named = {
477
+ str(check_handoffs._strict_frontmatter(text, task_id).get("repo"))
478
+ for task_id, text in tasks.items()
479
+ if check_handoffs._strict_frontmatter(text, task_id).get("repo")
480
+ and check_handoffs._task_scalar(text, task_id, "status") != "done"
481
+ }
482
+ if named:
483
+ bases = {name: member_base(name)[1] for name in sorted(named)}
484
+ milestone = pipeline_state.load_state(repo, project_dir)[0].milestone
485
+ path = _lookahead_member_bases_path(repo, str(milestone))
486
+ path.parent.mkdir(parents=True, exist_ok=True)
487
+ pipeline_state._common.atomic_write(path, json.dumps(
488
+ {"schema": LOOKAHEAD_MEMBER_BASES_SCHEMA, "milestone": milestone, "bases": bases},
489
+ indent=2, sort_keys=True) + "\n")
473
490
  except (BriefError, check_handoffs.HandoffError) as error:
474
491
  raise PipelineStateError(f"task brief validation failed: {error}") from error
475
492
 
476
493
 
494
+ LOOKAHEAD_MEMBER_BASES_SCHEMA = "gsd-path/lookahead-member-bases/v1"
495
+
496
+
497
+ def _lookahead_member_bases_path(repo: Path, milestone: str) -> Path:
498
+ common = pipeline_state._run_git(repo, "rev-parse", "--path-format=absolute", "--git-common-dir").stdout.strip()
499
+ return Path(common) / "gsd-path" / "lookahead-member-bases" / f"{milestone}.json"
500
+
501
+
502
+ def _lookahead_member_bases(repo: Path, milestone: str) -> Optional[dict]:
503
+ path = _lookahead_member_bases_path(repo, milestone)
504
+ if not path.is_file():
505
+ return None
506
+ value = json.loads(path.read_text(encoding="utf-8"))
507
+ if value.get("schema") != LOOKAHEAD_MEMBER_BASES_SCHEMA or value.get("milestone") != milestone:
508
+ return None
509
+ return value.get("bases")
510
+
511
+
512
+ def _shipped_member_merges(repo: Path, landing: str) -> dict:
513
+ """Member merges named by the ship commit the integration landing merged."""
514
+ if __package__:
515
+ from .pipeline_git import ship_member_rows
516
+ else:
517
+ from pipeline_git import ship_member_rows
518
+ ship = pipeline_state._run_git(repo, "rev-parse", "--verify", "--quiet", f"{landing}^2", check=False)
519
+ if ship.returncode != 0:
520
+ return {}
521
+ message = pipeline_state._run_git(repo, "show", "-s", "--format=%B", ship.stdout.strip()).stdout
522
+ return {row["name"]: row["merge"] for row in ship_member_rows(message)}
523
+
524
+
477
525
  def checkpoint_approval(
478
526
  repo: Path,
479
527
  kind: str,
@@ -1027,6 +1075,46 @@ def _classify_plan_drift(
1027
1075
  }
1028
1076
  approved = _approved_task_contracts(repo, checkpoint)
1029
1077
  current = _task_contracts_at(repo, revision)
1078
+ if __package__:
1079
+ from . import check_handoffs
1080
+ else:
1081
+ import check_handoffs
1082
+ # Member task paths live in member repos: compare each member from the base its brief
1083
+ # was checked at to the member merge the shipped milestone made (both fixed commits).
1084
+ task_repos = {
1085
+ task_id: str(check_handoffs._strict_frontmatter(contract.text, contract.path).get("repo") or "")
1086
+ for task_id, contract in approved.items()
1087
+ }
1088
+ active_member_tasks = {
1089
+ task_id for task_id, name in task_repos.items()
1090
+ if name and check_handoffs._task_scalar(approved[task_id].text, task_id, "status") != "done"
1091
+ }
1092
+ active_members = {task_repos[task_id] for task_id in active_member_tasks}
1093
+ member_changes: dict[str, list] = {}
1094
+ if active_members:
1095
+ bases = _lookahead_member_bases(repo, str(state.milestone)) or {}
1096
+ if any(name not in bases for name in active_members):
1097
+ return {
1098
+ "class": "unverifiable",
1099
+ "checkpoint": checkpoint,
1100
+ "task_ids": [],
1101
+ "changed_paths": [],
1102
+ "contract_paths": [],
1103
+ "member_tasks": True,
1104
+ "reason": "member bases were not recorded at plan approval",
1105
+ }
1106
+ if __package__:
1107
+ from . import members
1108
+ else:
1109
+ import members
1110
+ checkouts = {item["name"]: Path(item["checkout"]) for item in members.read_members(repo)}
1111
+ merges = _shipped_member_merges(repo, landing or revision)
1112
+ for name in active_members:
1113
+ member_changes[name] = [
1114
+ item for item in pipeline_state._run_git(
1115
+ checkouts[name], "diff", "--no-renames", "--name-only", "-z", bases[name], merges[name]
1116
+ ).stdout.split("\0") if item
1117
+ ] if name in merges else []
1030
1118
 
1031
1119
  contract_paths: set[str] = set()
1032
1120
  flagged_ids: set[str] = set()
@@ -1050,9 +1138,12 @@ def _classify_plan_drift(
1050
1138
 
1051
1139
  product_paths: set[str] = set()
1052
1140
  for task_id, contract in approved.items():
1141
+ member = task_repos.get(task_id)
1142
+ if member and task_id not in active_member_tasks:
1143
+ continue
1053
1144
  matches = sorted(
1054
- changed_path
1055
- for changed_path in changed
1145
+ (f"{member}:{changed_path}" if member else changed_path)
1146
+ for changed_path in (member_changes[member] if member else changed)
1056
1147
  if any(
1057
1148
  changed_path == path
1058
1149
  or changed_path.startswith(f"{path.rstrip('/')}/")
@@ -82,6 +82,8 @@ def _promotion_event(
82
82
  if isinstance(task_ids, list) and task_ids:
83
83
  return f"{prefix}; plan drift flagged tasks {', '.join(task_ids)}"
84
84
  return f"{prefix}; plan contract changed after approval"
85
+ if drift_class == "unverifiable" and drift.get("member_tasks"):
86
+ return f"{prefix}; plan drift unverifiable because member bases were not recorded at plan approval"
85
87
  if drift_class == "unverifiable":
86
88
  return f"{prefix}; plan drift unverifiable because approval checkpoint is missing"
87
89
  return prefix
@@ -6,6 +6,7 @@ import hashlib
6
6
  import errno
7
7
  import json
8
8
  import os
9
+ import stat
9
10
  import subprocess
10
11
  import sys
11
12
  import time
@@ -29,7 +30,7 @@ def process_identity(pid: int):
29
30
  else:
30
31
  command = ["ps", "-o", "lstart=", "-p", str(pid)]
31
32
  try:
32
- result = subprocess.run(command, capture_output=True, text=True, check=False)
33
+ result = subprocess.run(command, capture_output=True, encoding="utf-8", errors="replace", check=False)
33
34
  except OSError:
34
35
  return None
35
36
  value = result.stdout.strip()
@@ -45,11 +46,39 @@ def runtime_identity(runtime: Path):
45
46
 
46
47
 
47
48
  def process_alive(pid: int) -> bool:
49
+ """Whether pid names a running process, without signalling it.
50
+
51
+ Windows os.kill(pid, 0) sends CTRL_C_EVENT, so Windows asks the kernel.
52
+ status_runtime and install carry verbatim copies (they cannot import this
53
+ module); tests/test_common_platform.py keeps them identical.
54
+ """
55
+ if pid <= 0:
56
+ return False
57
+ if os.name != "nt":
58
+ try:
59
+ os.kill(pid, 0)
60
+ except OSError as error:
61
+ return error.errno == errno.EPERM
62
+ return True
63
+ import ctypes
64
+ from ctypes import wintypes
65
+
66
+ kernel32 = ctypes.WinDLL("kernel32", use_last_error=True)
67
+ kernel32.OpenProcess.restype = wintypes.HANDLE
68
+ kernel32.OpenProcess.argtypes = (wintypes.DWORD, wintypes.BOOL, wintypes.DWORD)
69
+ kernel32.GetExitCodeProcess.argtypes = (wintypes.HANDLE, ctypes.POINTER(wintypes.DWORD))
70
+ kernel32.CloseHandle.argtypes = (wintypes.HANDLE,)
71
+ handle = kernel32.OpenProcess(0x1000, False, pid) # PROCESS_QUERY_LIMITED_INFORMATION
72
+ if not handle:
73
+ # ERROR_ACCESS_DENIED: a protected process exists but refuses the query.
74
+ return ctypes.get_last_error() == 5
48
75
  try:
49
- os.kill(pid, 0)
50
- except OSError as error:
51
- return error.errno == errno.EPERM
52
- return True
76
+ code = wintypes.DWORD()
77
+ if not kernel32.GetExitCodeProcess(handle, ctypes.byref(code)):
78
+ return True
79
+ return code.value == 259 # STILL_ACTIVE
80
+ finally:
81
+ kernel32.CloseHandle(handle)
53
82
 
54
83
 
55
84
  def install_lock_active(lock: Path) -> bool:
@@ -123,7 +152,17 @@ def validate_runtime(data: dict) -> Path:
123
152
  path = root / name
124
153
  if path.is_symlink() or hashlib.sha256(path.read_bytes()).hexdigest() != expected:
125
154
  raise ValueError(f"runtime file changed: {name}")
126
- if set(p.name for p in root.iterdir()) != set(files) | {"manifest.json"}:
155
+ runtime_entries = set()
156
+ for path in root.iterdir():
157
+ if path.name == "__pycache__":
158
+ if not stat.S_ISDIR(path.lstat().st_mode):
159
+ raise ValueError("unexpected runtime files")
160
+ for cached in path.iterdir():
161
+ if cached.suffix != ".pyc" or not stat.S_ISREG(cached.lstat().st_mode):
162
+ raise ValueError("unexpected runtime files")
163
+ continue
164
+ runtime_entries.add(path.name)
165
+ if runtime_entries != set(files) | {"manifest.json"}:
127
166
  raise ValueError("unexpected runtime files")
128
167
  except (OSError, ValueError, TypeError) as error:
129
168
  raise ValueError(hint + f" ({error})") from error
@@ -139,8 +178,13 @@ def run_guard(repo: Path, name: str) -> None:
139
178
  root = resolve_runtime(repo)
140
179
  sys.dont_write_bytecode = True
141
180
  sys.path.insert(0, str(root))
142
- code = compile((root / name).read_bytes(), str(root / name), "exec")
143
- exec(code, {"__name__": "__main__", "__file__": str(repo / ".gsd-path" / name)})
181
+ previous_pycache_prefix = sys.pycache_prefix
182
+ sys.pycache_prefix = str(root / "manifest.json")
183
+ try:
184
+ code = compile((root / name).read_bytes(), str(root / name), "exec")
185
+ exec(code, {"__name__": "__main__", "__file__": str(repo / ".gsd-path" / name)})
186
+ finally:
187
+ sys.pycache_prefix = previous_pycache_prefix
144
188
 
145
189
 
146
190
  def launch(repo: Path) -> int:
@@ -151,7 +195,9 @@ def launch(repo: Path) -> int:
151
195
  except (OSError, ValueError) as error:
152
196
  print(str(error), file=sys.stderr)
153
197
  return 2
154
- return subprocess.call([sys.executable, "-B", str(runtime), "status", "--repo", str(repo)],
198
+ return subprocess.call([sys.executable, "-B", "-X",
199
+ f"pycache_prefix={runtime.parent / 'manifest.json'}",
200
+ str(runtime), "status", "--repo", str(repo)],
155
201
  env={**os.environ, "GIT_OPTIONAL_LOCKS": "0"})
156
202
  runtime = parent / "runtime" / "pipeline_state.py"
157
203
  lock = repo / ".gsd-path-install-lock"
@@ -196,7 +242,9 @@ def main(argv: Sequence[str] = sys.argv[1:]) -> int:
196
242
  return 2
197
243
  if args.runtime_path:
198
244
  try:
199
- print(resolve_runtime(args.repo.resolve()))
245
+ # Forward slashes: agents paste this into Git Bash on Windows, where
246
+ # unquoted backslashes are escapes.
247
+ print(resolve_runtime(args.repo.resolve()).as_posix())
200
248
  return 0
201
249
  except (OSError, ValueError) as error:
202
250
  print(str(error), file=sys.stderr)
@@ -129,8 +129,8 @@ def divergence_warning(source: Path, destination: Path, root: Path) -> Optional[
129
129
  if destination.stat().st_mtime > source.stat().st_mtime:
130
130
  return (
131
131
  f"generated resource diverges from canonical and is newer "
132
- f"(possible wrong-direction edit; edit {source.relative_to(root)} "
133
- f"instead): {destination.relative_to(root)}"
132
+ f"(possible wrong-direction edit; edit {source.relative_to(root).as_posix()} "
133
+ f"instead): {destination.relative_to(root).as_posix()}"
134
134
  )
135
135
  return None
136
136
 
@@ -166,9 +166,9 @@ def mismatches(root: Path) -> Sequence[str]:
166
166
  problems.append("package.json files do not match manifest package_files")
167
167
  for source, destination in resource_pairs(root):
168
168
  if not source.is_file():
169
- problems.append(f"missing canonical resource: {source.relative_to(root)}")
169
+ problems.append(f"missing canonical resource: {source.relative_to(root).as_posix()}")
170
170
  elif not destination.is_file():
171
- problems.append(f"missing generated resource: {destination.relative_to(root)}")
171
+ problems.append(f"missing generated resource: {destination.relative_to(root).as_posix()}")
172
172
  else:
173
173
  try:
174
174
  expected = materialized_resource_bytes(source, destination)
@@ -181,10 +181,10 @@ def mismatches(root: Path) -> Sequence[str]:
181
181
  problems.append(warning)
182
182
  else:
183
183
  problems.append(
184
- f"stale generated resource: {destination.relative_to(root)}"
184
+ f"stale generated resource: {destination.relative_to(root).as_posix()}"
185
185
  )
186
186
  for path in package_metadata(root):
187
- problems.append(f"unexpected package metadata: {path.relative_to(root)}")
187
+ problems.append(f"unexpected package metadata: {path.relative_to(root).as_posix()}")
188
188
  return problems
189
189
 
190
190
 
@@ -216,7 +216,8 @@ def synchronize(root: Path) -> int:
216
216
  package_path = root / "package.json"
217
217
  package = json.loads(package_path.read_text(encoding="utf-8"))
218
218
  package["files"] = list(PACKAGE_FILES)
219
- package_path.write_text(json.dumps(package, indent=2) + "\n", encoding="utf-8")
219
+ # Bytes, so Windows text mode never writes CRLF.
220
+ package_path.write_bytes((json.dumps(package, indent=2) + "\n").encode("utf-8"))
220
221
  print(
221
222
  json.dumps(
222
223
  {
@@ -9,15 +9,17 @@ from pathlib import Path
9
9
 
10
10
  try:
11
11
  from scripts import check_handoffs as contracts
12
+ from scripts import _common
12
13
  except ImportError:
13
14
  import check_handoffs as contracts
15
+ import _common
14
16
 
15
17
 
16
18
  def render(repo: Path, task: Path) -> str:
17
19
  intent_path = repo.resolve() / '.project/intent/INTENT.md'
18
20
  task = task.resolve()
19
- intent = intent_path.read_bytes().decode('utf-8')
20
- task_text = task.read_bytes().decode('utf-8')
21
+ intent = _common.read_user_text(intent_path)
22
+ task_text = _common.read_user_text(task)
21
23
  owned = contracts._owned_criteria(task_text, task.stem)
22
24
  criteria = contracts._success_criteria(intent)
23
25
  unknown = set(owned) - set(criteria)
@@ -40,7 +42,14 @@ def render(repo: Path, task: Path) -> str:
40
42
  if simple:
41
43
  before, after = intent[:section.start('body')], intent[section.end('body'):]
42
44
  # Corrections and task interfaces can explicitly refer to another SC.
43
- required = set(owned) | set(re.findall(r'\bSC\d+\b', before + after + task_text))
45
+ # The spec-probe tables name every SC by design, so they select nothing.
46
+ scanned = re.sub(r'(?ms)^## (?:Edge coverage|Prohibitions)[ \t]*\n.*?(?=^## |\Z)', '',
47
+ before + after)
48
+ required = set(owned) | set(re.findall(r'\bSC\d+\b', scanned + task_text))
49
+ # An owned criterion's edge or prohibition ruled into another SC needs that SC's text.
50
+ for rows in (contracts.edge_coverage(intent, criteria), contracts.prohibitions(intent, criteria)):
51
+ required |= {row['target'] for row in (rows or {}).values()
52
+ if row['target'] and row['criterion'] in owned}
44
53
  selected = ''.join(chunk for item, chunk in zip(items, chunks) if f'SC{item[1]}' in required)
45
54
  projected = before + '\n' + (selected or '- No criteria assigned.\n\n') + after
46
55
  mode = 'owned-criteria'
@@ -10,12 +10,16 @@ import json
10
10
  import sys
11
11
  from pathlib import Path
12
12
 
13
- try:
14
- from _common import atomic_write
15
- from loop_run import log_lock
16
- except ModuleNotFoundError: # pragma: no cover - package imports
17
- from scripts._common import atomic_write
18
- from scripts.loop_run import log_lock
13
+ if __package__:
14
+ from ._common import atomic_write
15
+ from .loop_run import log_lock
16
+ else:
17
+ try:
18
+ from _common import atomic_write
19
+ from loop_run import log_lock
20
+ except ModuleNotFoundError: # pragma: no cover - package imports
21
+ from scripts._common import atomic_write
22
+ from scripts.loop_run import log_lock
19
23
 
20
24
 
21
25
  SCHEMA = "gsd-path/token-budget/v1"
@@ -97,14 +101,14 @@ def operate(args):
97
101
  "task_limit": args.task_limit, "session_limit": args.session_limit,
98
102
  "authority": args.authority}
99
103
  if ledger.exists():
100
- data = json.loads(ledger.read_text())
104
+ data = json.loads(ledger.read_text(encoding="utf-8"))
101
105
  if any(data.get(key) != value for key, value in policy.items()):
102
106
  raise ValueError("existing session policy differs; configuration cannot reset usage")
103
107
  else:
104
108
  data = {**policy, "observations": {}}
105
109
  atomic_write(ledger, json.dumps(data, indent=2) + "\n")
106
110
  return {"status": "configured", "hard_cap_supported": False}
107
- data = json.loads(ledger.read_text())
111
+ data = json.loads(ledger.read_text(encoding="utf-8"))
108
112
  if data.get("schema") != SCHEMA or data.get("host") != "codex-cli":
109
113
  raise ValueError("unsupported budget ledger")
110
114
  if args.action == "record":
@@ -15,6 +15,7 @@ export const HOST_LABELS = {
15
15
  zed: "Zed",
16
16
  kiro: "Kiro",
17
17
  kimi: "Kimi Code",
18
+ muse: "Muse Code",
18
19
  };
19
20
 
20
21
  // ponytail: hand-drawn block letters; no figlet dependency.