@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
@@ -14,28 +14,41 @@ from datetime import datetime, timezone
14
14
  from pathlib import Path, PurePosixPath
15
15
  from typing import Dict, List, Mapping, Optional, Sequence, Tuple
16
16
 
17
- try:
18
- from check_task_briefs import _frontmatter, _sections
19
- from isolation import (
17
+ if __package__:
18
+ from .check_task_briefs import _frontmatter, _sections
19
+ from .isolation import (
20
20
  VERIFY_LEDGER_PATH,
21
21
  IsolationError,
22
22
  recover as recover_isolation,
23
23
  verify_landed_task_files,
24
24
  )
25
- from pipeline_git import task_commit_subject
26
- from pipeline_state import PipelineStateError, load_state
27
- import _common
28
- except ImportError: # pragma: no cover - package imports used by tests
29
- from scripts.check_task_briefs import _frontmatter, _sections
30
- from scripts.isolation import (
31
- VERIFY_LEDGER_PATH,
32
- IsolationError,
33
- recover as recover_isolation,
34
- verify_landed_task_files,
35
- )
36
- from scripts.pipeline_git import task_commit_subject
37
- from scripts.pipeline_state import PipelineStateError, load_state
38
- from scripts import _common
25
+ from .pipeline_git import task_commit_subject
26
+ from .pipeline_state import PipelineStateError, load_state
27
+ from . import _common, check_handoffs
28
+ else:
29
+ try:
30
+ from check_task_briefs import _frontmatter, _sections
31
+ from isolation import (
32
+ VERIFY_LEDGER_PATH,
33
+ IsolationError,
34
+ recover as recover_isolation,
35
+ verify_landed_task_files,
36
+ )
37
+ from pipeline_git import task_commit_subject
38
+ from pipeline_state import PipelineStateError, load_state
39
+ import _common
40
+ import check_handoffs
41
+ except ImportError: # pragma: no cover - package imports used by tests
42
+ from scripts.check_task_briefs import _frontmatter, _sections
43
+ from scripts.isolation import (
44
+ VERIFY_LEDGER_PATH,
45
+ IsolationError,
46
+ recover as recover_isolation,
47
+ verify_landed_task_files,
48
+ )
49
+ from scripts.pipeline_git import task_commit_subject
50
+ from scripts.pipeline_state import PipelineStateError, load_state
51
+ from scripts import _common, check_handoffs
39
52
 
40
53
 
41
54
  DEFAULT_PROJECT_DIR = ".project"
@@ -45,6 +58,10 @@ FULL_SHA_RE = re.compile(r"^[0-9a-f]{40}$")
45
58
  # recover verdicts that count as landed, mapped to their evidence classification.
46
59
  LANDED_VERDICTS = {"recovered": "proven-landed", "attested": "attested"}
47
60
  WAVE_HEADING_RE = re.compile(r"(?m)^## Wave (?P<wave>\d+)\b.*$")
61
+ REVIEW_VERDICT_RE = re.compile(
62
+ r"(?m)^Wave verdict:\s*(?P<verdict>pass|blocked)\s*(?:<!--.*?-->)?\s*$"
63
+ )
64
+ REVIEW_FINDINGS_HEADING = "Review findings"
48
65
  VERIFY_HEAVY_RE = re.compile(r"(?m)^Heavy:\s*(?P<value>yes|no)\s*(?:<!--.*-->)?\s*$")
49
66
  VALID_TASK_STATUSES = {"pending", "in-progress", "done", "failed", "blocked"}
50
67
  VERIFY_RESULTS = _common.VERIFY_RESULTS
@@ -75,6 +92,8 @@ class Task:
75
92
  task_branch: Optional[str]
76
93
  task_file: str
77
94
  verify_heavy: bool
95
+ repo: str = ""
96
+ review_findings: bool = False
78
97
 
79
98
 
80
99
  @dataclass(frozen=True)
@@ -227,6 +246,8 @@ def _parse_task(path: Path, relative_path: str, task_file: Optional[str] = None)
227
246
  task_branch=_nullable_string(fields, "task_branch", label),
228
247
  task_file=task_file or relative_path,
229
248
  verify_heavy=_verify_heavy(text, label),
249
+ repo=str(fields.get("repo") or ""),
250
+ review_findings=REVIEW_FINDINGS_HEADING in _sections(text),
230
251
  )
231
252
 
232
253
 
@@ -284,6 +305,8 @@ def _load_tasks(
284
305
  raise BuildStateError("missing-input", f"task directory is missing or unsafe: {tasks_dir}")
285
306
  task_paths: Dict[str, Path] = {}
286
307
  for path in sorted(tasks_dir.iterdir()):
308
+ if _common.is_ignored_junk(path):
309
+ continue
287
310
  match = TASK_FILE_RE.fullmatch(path.name)
288
311
  if match is None or not path.is_file() or path.is_symlink():
289
312
  raise BuildStateError(
@@ -464,9 +487,41 @@ def _validate_ready_metadata(project: Project) -> None:
464
487
 
465
488
 
466
489
  def _overlap(left: Task, right: Task) -> List[str]:
490
+ if left.repo != right.repo:
491
+ return []
467
492
  return sorted(set(left.files) & set(right.files))
468
493
 
469
494
 
495
+ def _blocked_wave_reviews(project: Project) -> Dict[int, int]:
496
+ """Return waves whose highest canonical review cycle has a blocked lens."""
497
+
498
+ review_dir = project.tasks_dir.parent / "review"
499
+ if review_dir.is_symlink() or not review_dir.is_dir():
500
+ return {}
501
+
502
+ latest: Dict[int, int] = {}
503
+ verdicts: Dict[Tuple[int, int], List[str]] = {}
504
+ for path in sorted(review_dir.iterdir()):
505
+ named = check_handoffs.WAVE_REVIEW_NAME.fullmatch(path.name)
506
+ if named is None or path.is_symlink() or not path.is_file():
507
+ continue
508
+ wave, cycle = int(named.group("wave")), int(named.group("cycle"))
509
+ latest[wave] = max(latest.get(wave, 0), cycle)
510
+ try:
511
+ text = path.read_text(encoding="utf-8")
512
+ except (OSError, UnicodeDecodeError):
513
+ continue
514
+ verdict = REVIEW_VERDICT_RE.search(text)
515
+ if verdict is not None:
516
+ verdicts.setdefault((wave, cycle), []).append(verdict.group("verdict"))
517
+
518
+ return {
519
+ wave: cycle
520
+ for wave, cycle in latest.items()
521
+ if "blocked" in verdicts.get((wave, cycle), [])
522
+ }
523
+
524
+
470
525
  def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
471
526
  allow_done: bool = False) -> Dict[str, object]:
472
527
  """Return task readiness, optionally accepting build/done for completion checks."""
@@ -476,6 +531,39 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
476
531
  by_id = {task.task_id: task for task in project.tasks}
477
532
  unfinished_waves = sorted({task.wave for task in project.tasks if task.status != "done"})
478
533
  current_wave = unfinished_waves[0] if unfinished_waves else None
534
+ blocked_reviews = _blocked_wave_reviews(project)
535
+ blocked_wave = min(
536
+ (
537
+ wave
538
+ for wave in blocked_reviews
539
+ if (current_wave is None or wave < current_wave)
540
+ and any(task.wave == wave for task in project.tasks)
541
+ and all(task.status == "done" for task in project.tasks if task.wave == wave)
542
+ ),
543
+ default=None,
544
+ )
545
+ repair_for_wave: Optional[int] = None
546
+ blocked_review: Optional[Dict[str, object]] = None
547
+ if blocked_wave is not None:
548
+ source_ids = {task.task_id for task in project.tasks if task.wave == blocked_wave}
549
+ repairs = [
550
+ task
551
+ for task in project.tasks
552
+ if task.status != "done"
553
+ and task.wave > blocked_wave
554
+ and task.review_findings
555
+ and source_ids.intersection(task.deps)
556
+ ]
557
+ if repairs:
558
+ repair_for_wave = blocked_wave
559
+ current_wave = min(task.wave for task in repairs)
560
+ else:
561
+ current_wave = blocked_wave
562
+ blocked_review = {
563
+ "wave": blocked_wave,
564
+ "cycle": blocked_reviews[blocked_wave],
565
+ "waiting_waves": unfinished_waves,
566
+ }
479
567
  active = [task for task in project.tasks if task.status == "in-progress"]
480
568
  if current_wave is not None and any(task.wave != current_wave for task in active):
481
569
  task = next(task for task in active if task.wave != current_wave)
@@ -518,7 +606,7 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
518
606
  )
519
607
 
520
608
  wave_active = any(task.wave == current_wave for task in active)
521
- if current_wave is not None and not selectable and not wave_active:
609
+ if current_wave is not None and blocked_review is None and not selectable and not wave_active:
522
610
  stalled = [
523
611
  {
524
612
  "id": task.task_id,
@@ -538,11 +626,17 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
538
626
  {"wave": current_wave, "tasks": stalled},
539
627
  )
540
628
 
629
+ typed: Dict[str, object] = {}
630
+ if repair_for_wave is not None:
631
+ typed["repair_for_wave"] = repair_for_wave
632
+ if blocked_review is not None:
633
+ typed["blocked_review"] = blocked_review
541
634
  return {
542
635
  "command": "ready",
543
636
  "branch": project.branch,
544
637
  "head": project.head,
545
638
  "current_wave": current_wave,
639
+ **typed,
546
640
  "ready": [
547
641
  {
548
642
  "id": task.task_id,
@@ -553,6 +647,7 @@ def ready(repo: str, project_dir: str = DEFAULT_PROJECT_DIR, *,
553
647
  "files": list(task.files),
554
648
  "task_file": task.task_file,
555
649
  "verify_heavy": task.verify_heavy,
650
+ **({"repo": task.repo} if task.repo else {}),
556
651
  }
557
652
  for task in selectable
558
653
  ],
@@ -726,6 +821,22 @@ def _ledger_path(repo: Path) -> Path:
726
821
  return path
727
822
 
728
823
 
824
+ def _member_checkout(repo: Path, member: Optional[str]) -> Path:
825
+ if member is None:
826
+ return repo
827
+ try:
828
+ import members
829
+ except ImportError: # pragma: no cover - package import used by tests
830
+ from scripts import members
831
+ try:
832
+ listed = {item["name"]: Path(item["checkout"]) for item in members.read_members(repo)}
833
+ except members.MembersError as error:
834
+ raise BuildStateError("invalid-member", str(error)) from error
835
+ if member not in listed:
836
+ raise BuildStateError("invalid-member", f"{member} is not in MEMBERS.md")
837
+ return listed[member]
838
+
839
+
729
840
  def _ledger_key(repo: Path, command: str, commit: str) -> Tuple[str, str]:
730
841
  if not command.strip():
731
842
  raise BuildStateError("invalid-command", "--command must not be empty")
@@ -742,13 +853,20 @@ def _ledger_entries(path: Path) -> List[Dict[str, object]]:
742
853
 
743
854
 
744
855
  def verify_record(repo: str, command: str, commit: str, result: str,
745
- execution: Optional[Dict[str, object]] = None) -> Dict[str, object]:
746
- """Append one verify run (command, commit, result, timestamp) to the ledger."""
856
+ execution: Optional[Dict[str, object]] = None,
857
+ member: Optional[str] = None) -> Dict[str, object]:
858
+ """Append one verify run (command, commit, result, timestamp) to the ledger.
859
+
860
+ A member run names its member and a commit in that member's repository.
861
+ """
747
862
 
748
863
  repository = _repo_root(repo)
749
864
  if result not in VERIFY_RESULTS:
750
865
  raise BuildStateError("invalid-result", "--result must be pass or fail")
751
- normalized, commit = _ledger_key(repository, command, commit)
866
+ normalized, commit = _ledger_key(_member_checkout(repository, member), command, commit)
867
+ ignore_error = _common.project_ignore_error(repository)
868
+ if ignore_error:
869
+ raise BuildStateError("ignored-ledger", ignore_error)
752
870
  path = _ledger_path(repository)
753
871
  _ledger_entries(path)
754
872
  entry = {
@@ -757,6 +875,7 @@ def verify_record(repo: str, command: str, commit: str, result: str,
757
875
  "commit": commit,
758
876
  "result": result,
759
877
  "recorded_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
878
+ **({"repo": member} if member else {}),
760
879
  }
761
880
  if execution is not None:
762
881
  if (not isinstance(execution.get("stdout"), str)
@@ -771,12 +890,12 @@ def verify_record(repo: str, command: str, commit: str, result: str,
771
890
  return {"command": "verify-record", "ledger": VERIFY_LEDGER_PATH, "entry": entry}
772
891
 
773
892
 
774
- def verify_lookup(repo: str, command: str, commit: str) -> Dict[str, object]:
893
+ def verify_lookup(repo: str, command: str, commit: str, member: Optional[str] = None) -> Dict[str, object]:
775
894
  """Return the latest recorded run of this command at this commit, or a miss."""
776
895
 
777
896
  repository = _repo_root(repo)
778
- normalized, commit = _ledger_key(repository, command, commit)
779
- entry = _common.latest_verify_entry(_ledger_entries(_ledger_path(repository)), normalized, commit)
897
+ normalized, commit = _ledger_key(_member_checkout(repository, member), command, commit)
898
+ entry = _common.latest_verify_entry(_ledger_entries(_ledger_path(repository)), normalized, commit, member)
780
899
  return {
781
900
  "command": "verify-lookup",
782
901
  "ledger": VERIFY_LEDGER_PATH,
@@ -8,6 +8,10 @@ with testable claims where every claim row carries a valid type, a valid
8
8
  verdict, and real evidence, a `## Descriptive docs` list for claimless docs,
9
9
  the two sets disjoint and together equal to the frozen inventory, and a
10
10
  remediation queue with one classified row per non-verified claim.
11
+ Queue numbers are unique, positive and strictly increasing, not necessarily
12
+ contiguous. With `--prior-audit`, persisting rows keep their number and new
13
+ rows use a number above every prior queue number and User rulings Queue #.
14
+ Numbers stay bound to their original (doc, claim); never reuse a retired one.
11
15
  `## User rulings` is fixed-format durable memory. Pass the pre-rewrite audit
12
16
  with `--prior-audit FILE`; every prior row, including its Planned value, must
13
17
  remain an exact ordered prefix.
@@ -26,6 +30,10 @@ gate derives the inventory from tracked Markdown files, excluding `.git`,
26
30
  `*/skills/path` bundles,
27
31
  the audit itself, and — unless the
28
32
  audit declares `Alignment mode: yes` — everything else under `.project/`.
33
+
34
+ `Repo root` names the primary repository. When the audit is gated inside a
35
+ disposable sidecar, pass that sidecar as `--repo` and the primary as
36
+ `--primary`; the header then stays valid after the sidecar is retired.
29
37
  """
30
38
 
31
39
  import argparse
@@ -92,6 +100,12 @@ def _table_rows(body: str, width: int, label: str) -> List[List[str]]:
92
100
  return rows
93
101
 
94
102
 
103
+ def _remediation_queue_rows(body: str, label: str, required: bool = False) -> List[List[str]]:
104
+ if not required and "|" not in body:
105
+ return []
106
+ return _table_rows(body, 6, label)
107
+
108
+
95
109
  def _not_placeholder(value: str, label: str) -> None:
96
110
  normalized = value.strip().strip("`\"").strip()
97
111
  if (
@@ -243,12 +257,70 @@ def _check_carried(
243
257
  raise AuditError(f"{label}: doc or evidence changed since the prior audit: {', '.join(moved)}")
244
258
 
245
259
 
260
+ def _queue_number(number: str, label: str) -> int:
261
+ if not re.fullmatch(r"[1-9][0-9]*", number):
262
+ raise AuditError(f"{label}: queue numbers must be positive integers")
263
+ return int(number)
264
+
265
+
266
+ def _check_queue_number_history(
267
+ queue: Sequence[Sequence[str]],
268
+ prior_sections: Dict[str, str],
269
+ ) -> None:
270
+ if "Remediation queue" not in prior_sections:
271
+ raise AuditError("prior audit is missing section: ## Remediation queue")
272
+ if "User rulings" not in prior_sections:
273
+ raise AuditError("prior audit is missing section: ## User rulings")
274
+
275
+ prior_queue = _remediation_queue_rows(
276
+ prior_sections["Remediation queue"], "Prior remediation queue"
277
+ )
278
+ prior_items_by_number: Dict[int, tuple] = {}
279
+ prior_numbers_by_item: Dict[tuple, int] = {}
280
+ for number, doc, claim, *_ in prior_queue:
281
+ numeric_number = _queue_number(number, "Prior remediation queue")
282
+ identity = (doc, claim)
283
+ prior_items_by_number[numeric_number] = identity
284
+ prior_numbers_by_item[identity] = numeric_number
285
+
286
+ prior_rulings = _user_rulings(
287
+ prior_sections["User rulings"], "Prior user rulings"
288
+ )
289
+ prior_ruling_numbers = {int(row[0]) for row in prior_rulings}
290
+ prior_max = max(
291
+ [*prior_items_by_number, *prior_ruling_numbers],
292
+ default=0,
293
+ )
294
+
295
+ for number, doc, claim, *_ in queue:
296
+ numeric_number = _queue_number(number, "Remediation queue")
297
+ identity = (doc, claim)
298
+ prior_number = prior_numbers_by_item.get(identity)
299
+ if numeric_number in prior_ruling_numbers and prior_items_by_number.get(numeric_number) != identity:
300
+ raise AuditError(
301
+ f"Remediation queue #{number}: Queue # {number} is already retired by a prior "
302
+ "User rulings row for a different item; give new items a number above every "
303
+ "prior queue number and User rulings Queue #"
304
+ )
305
+ if prior_number is not None and numeric_number != prior_number:
306
+ raise AuditError(
307
+ f"Remediation queue #{number}: persisting item must keep its prior queue "
308
+ f"number #{prior_number}"
309
+ )
310
+ if prior_number is None and numeric_number <= prior_max:
311
+ raise AuditError(
312
+ f"Remediation queue #{number}: new items must use a number above every prior "
313
+ f"queue number and User rulings Queue # (maximum {prior_max})"
314
+ )
315
+
316
+
246
317
  def validate(
247
318
  repo: Path,
248
319
  audit_relative: str,
249
320
  inventory: Optional[Sequence[str]],
250
321
  prior_text: Optional[str] = None,
251
322
  changed: Optional[Sequence[str]] = None,
323
+ primary: Optional[Path] = None,
252
324
  ) -> Dict[str, object]:
253
325
  audit = repo / audit_relative
254
326
  if not audit.is_file() or audit.is_symlink():
@@ -263,8 +335,8 @@ def validate(
263
335
  raise AuditError(f"header must contain exactly one {key} line")
264
336
  header[key] = values[0]
265
337
  declared_root = Path(header["Repo root"])
266
- if not declared_root.is_absolute() or declared_root.resolve() != repo.resolve():
267
- raise AuditError("Repo root does not match --repo")
338
+ if not declared_root.is_absolute() or declared_root.resolve() != (primary or repo).resolve():
339
+ raise AuditError("Repo root does not match --primary (default --repo)")
268
340
  try:
269
341
  date.fromisoformat(header["Audited"])
270
342
  except ValueError as error:
@@ -349,7 +421,9 @@ def validate(
349
421
 
350
422
  queue_body = sections["Remediation queue"]
351
423
  non_verified = sum(tallies[verdict] for verdict in VERDICTS if verdict != "verified")
352
- queue = _table_rows(queue_body, 6, "Remediation queue") if non_verified or "|" in queue_body else []
424
+ queue = _remediation_queue_rows(
425
+ queue_body, "Remediation queue", required=bool(non_verified)
426
+ )
353
427
  expected_queue = sorted(
354
428
  (doc, claim["claim"], claim["verdict"])
355
429
  for doc, claims in docs.items()
@@ -366,9 +440,13 @@ def validate(
366
440
  _not_placeholder(action, f"Remediation queue #{number}: action")
367
441
  if len(queue) != non_verified:
368
442
  raise AuditError(f"Remediation queue has {len(queue)} rows for {non_verified} non-verified claims")
369
- numbers = [row[0] for row in queue]
370
- if numbers != [str(number) for number in range(1, len(queue) + 1)]:
371
- raise AuditError("Remediation queue numbers must be contiguous and ordered from 1")
443
+ numbers = [_queue_number(row[0], "Remediation queue") for row in queue]
444
+ if any(later <= earlier for earlier, later in zip(numbers, numbers[1:])):
445
+ raise AuditError(
446
+ "Remediation queue numbers must be unique and strictly increasing"
447
+ )
448
+ if prior_sections is not None:
449
+ _check_queue_number_history(queue, prior_sections)
372
450
  actual_queue = sorted((doc, claim, verdict) for _, doc, claim, verdict, _, _ in queue)
373
451
  if actual_queue != expected_queue:
374
452
  raise AuditError(
@@ -392,6 +470,11 @@ def parser() -> argparse.ArgumentParser:
392
470
  argument_parser.add_argument("--repo", type=Path, required=True)
393
471
  argument_parser.add_argument("--audit", default=DEFAULT_AUDIT, help="relative audit path (default: %(default)s)")
394
472
  argument_parser.add_argument("--inventory", help="file with one inventoried POSIX path per line, or - for stdin")
473
+ argument_parser.add_argument(
474
+ "--primary",
475
+ type=Path,
476
+ help="primary repository the Repo root header must name (default: --repo)",
477
+ )
395
478
  argument_parser.add_argument(
396
479
  "--prior-audit",
397
480
  type=Path,
@@ -454,7 +537,8 @@ def main(argv: Optional[Sequence[str]] = None) -> int:
454
537
  changed = _path_lines(arguments.changed) if arguments.changed is not None else None
455
538
  try:
456
539
  result = validate(
457
- arguments.repo.resolve(), arguments.audit, inventory, prior_text, changed
540
+ arguments.repo.resolve(), arguments.audit, inventory, prior_text, changed,
541
+ arguments.primary,
458
542
  )
459
543
  except AuditError as error:
460
544
  print(f"docs audit validation failed: {error}", file=sys.stderr)