devrites 7.2.8 → 7.3.1

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 (332) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +1 -1
  3. package/docs/candidate-integrity.md +10 -0
  4. package/docs/cli.md +7 -1
  5. package/docs/command-map.md +5 -2
  6. package/docs/engine/commands.md +34 -4
  7. package/docs/orchestration.md +26 -13
  8. package/docs/skills.md +5 -0
  9. package/engine/internal/acceptance/cli.go +27 -26
  10. package/engine/internal/acceptance/preconditions.go +1 -1
  11. package/engine/internal/acceptance/run.go +42 -16
  12. package/engine/internal/acceptance/transcript.go +236 -45
  13. package/engine/internal/devritespaths/paths.go +16 -3
  14. package/engine/internal/gate/gate.go +6 -0
  15. package/engine/internal/gate/readiness_binding.go +4 -3
  16. package/engine/internal/lib/candidate.go +20 -7
  17. package/engine/internal/lib/candidate_coverage.go +2 -19
  18. package/engine/internal/lib/closeout.go +16 -4
  19. package/engine/internal/lib/context.go +64 -45
  20. package/engine/internal/lib/context_io.go +171 -0
  21. package/engine/internal/lib/detect.go +8 -8
  22. package/engine/internal/lib/dispatch.go +2 -0
  23. package/engine/internal/lib/packet.go +37 -7
  24. package/engine/internal/lib/packet_freeze.go +60 -12
  25. package/engine/internal/lib/packet_integrity.go +179 -0
  26. package/engine/internal/lib/perturb.go +3 -3
  27. package/engine/internal/lib/slicecheck.go +7 -1
  28. package/engine/internal/lib/taskgraph.go +20 -36
  29. package/engine/internal/lib/triggers.go +28 -29
  30. package/engine/internal/lib/workspace_pool.go +330 -0
  31. package/engine/internal/parallel/cli.go +190 -49
  32. package/engine/internal/parallel/git.go +47 -14
  33. package/engine/internal/parallel/landing.go +232 -0
  34. package/engine/internal/parallel/lease.go +97 -18
  35. package/engine/internal/parallel/ops.go +312 -155
  36. package/engine/internal/parallel/pathdisjoint.go +48 -4
  37. package/engine/internal/parallel/prune.go +133 -0
  38. package/engine/internal/parallel/repository.go +239 -0
  39. package/engine/internal/parallel/select.go +64 -42
  40. package/engine/internal/state/observation.go +49 -2
  41. package/engine/main.go +2 -1
  42. package/pack/.claude/agents/devrites-proof-runner.md +16 -9
  43. package/pack/.claude/skills/devrites-lib/SKILL.md +2 -0
  44. package/pack/.claude/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  45. package/pack/.claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  46. package/pack/.claude/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  47. package/pack/.claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  48. package/pack/.claude/skills/devrites-lib/reference/standards/agents.md +1 -1
  49. package/pack/.claude/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  50. package/pack/.claude/skills/devrites-lib/reference/standards/gates.md +18 -9
  51. package/pack/.claude/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  52. package/pack/.claude/skills/rite-autocomplete/SKILL.md +12 -10
  53. package/pack/.claude/skills/rite-autocomplete/reference/lookahead.md +4 -4
  54. package/pack/.claude/skills/rite-autocomplete/reference/loop.md +20 -9
  55. package/pack/.claude/skills/rite-build/SKILL.md +13 -10
  56. package/pack/.claude/skills/rite-build/reference/afk-discipline.md +1 -1
  57. package/pack/.claude/skills/rite-build/reference/checkpoint.md +1 -1
  58. package/pack/.claude/skills/rite-build/reference/parallel-batch.md +57 -36
  59. package/pack/.claude/skills/rite-build/reference/phase-contract.md +8 -5
  60. package/pack/.claude/skills/rite-build/reference/wright-dispatch.md +7 -7
  61. package/pack/.claude/skills/rite-clarify/SKILL.md +2 -2
  62. package/pack/.claude/skills/rite-converge/SKILL.md +4 -4
  63. package/pack/.claude/skills/rite-define/SKILL.md +7 -3
  64. package/pack/.claude/skills/rite-define/reference/plan-template.md +4 -2
  65. package/pack/.claude/skills/rite-plan/SKILL.md +11 -8
  66. package/pack/.claude/skills/rite-plan/reference/dependency-graph.md +9 -2
  67. package/pack/.claude/skills/rite-plan/reference/slicing.md +2 -0
  68. package/pack/.claude/skills/rite-polish/SKILL.md +3 -3
  69. package/pack/.claude/skills/rite-prove/SKILL.md +24 -12
  70. package/pack/.claude/skills/rite-resolve/SKILL.md +3 -3
  71. package/pack/.claude/skills/rite-resolve/reference/answer-protocol.md +2 -0
  72. package/pack/.claude/skills/rite-review/SKILL.md +11 -3
  73. package/pack/.claude/skills/rite-review/reference/feature-scoped-review.md +2 -2
  74. package/pack/.claude/skills/rite-seal/SKILL.md +1 -1
  75. package/pack/.claude/skills/rite-seal/reference/phase-contract.md +1 -1
  76. package/pack/.claude/skills/rite-ship/SKILL.md +2 -2
  77. package/pack/.claude/skills/rite-ship/reference/git-ship.md +2 -2
  78. package/pack/.claude/skills/rite-spec/SKILL.md +8 -5
  79. package/pack/.claude/skills/rite-spec/reference/state-workspace.md +5 -0
  80. package/pack/.claude/skills/rite-temper/SKILL.md +3 -3
  81. package/pack/.claude/skills/rite-vet/SKILL.md +19 -6
  82. package/pack/.claude/skills/rite-vet/reference/review-axes.md +12 -5
  83. package/pack/generated/claude/agents/devrites-proof-runner.md +16 -9
  84. package/pack/generated/claude/skills/devrites-lib/SKILL.md +2 -0
  85. package/pack/generated/claude/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  86. package/pack/generated/claude/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  87. package/pack/generated/claude/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  88. package/pack/generated/claude/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  89. package/pack/generated/claude/skills/devrites-lib/reference/standards/agents.md +1 -1
  90. package/pack/generated/claude/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  91. package/pack/generated/claude/skills/devrites-lib/reference/standards/gates.md +18 -9
  92. package/pack/generated/claude/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  93. package/pack/generated/claude/skills/rite-autocomplete/SKILL.md +12 -10
  94. package/pack/generated/claude/skills/rite-autocomplete/reference/lookahead.md +4 -4
  95. package/pack/generated/claude/skills/rite-autocomplete/reference/loop.md +20 -9
  96. package/pack/generated/claude/skills/rite-build/SKILL.md +13 -10
  97. package/pack/generated/claude/skills/rite-build/reference/afk-discipline.md +1 -1
  98. package/pack/generated/claude/skills/rite-build/reference/checkpoint.md +1 -1
  99. package/pack/generated/claude/skills/rite-build/reference/parallel-batch.md +57 -36
  100. package/pack/generated/claude/skills/rite-build/reference/phase-contract.md +8 -5
  101. package/pack/generated/claude/skills/rite-build/reference/wright-dispatch.md +7 -7
  102. package/pack/generated/claude/skills/rite-clarify/SKILL.md +2 -2
  103. package/pack/generated/claude/skills/rite-converge/SKILL.md +4 -4
  104. package/pack/generated/claude/skills/rite-define/SKILL.md +7 -3
  105. package/pack/generated/claude/skills/rite-define/reference/plan-template.md +4 -2
  106. package/pack/generated/claude/skills/rite-plan/SKILL.md +11 -8
  107. package/pack/generated/claude/skills/rite-plan/reference/dependency-graph.md +9 -2
  108. package/pack/generated/claude/skills/rite-plan/reference/slicing.md +2 -0
  109. package/pack/generated/claude/skills/rite-polish/SKILL.md +3 -3
  110. package/pack/generated/claude/skills/rite-prove/SKILL.md +24 -12
  111. package/pack/generated/claude/skills/rite-resolve/SKILL.md +3 -3
  112. package/pack/generated/claude/skills/rite-resolve/reference/answer-protocol.md +2 -0
  113. package/pack/generated/claude/skills/rite-review/SKILL.md +11 -3
  114. package/pack/generated/claude/skills/rite-review/reference/feature-scoped-review.md +2 -2
  115. package/pack/generated/claude/skills/rite-seal/SKILL.md +1 -1
  116. package/pack/generated/claude/skills/rite-seal/reference/phase-contract.md +1 -1
  117. package/pack/generated/claude/skills/rite-ship/SKILL.md +2 -2
  118. package/pack/generated/claude/skills/rite-ship/reference/git-ship.md +2 -2
  119. package/pack/generated/claude/skills/rite-spec/SKILL.md +8 -5
  120. package/pack/generated/claude/skills/rite-spec/reference/state-workspace.md +5 -0
  121. package/pack/generated/claude/skills/rite-temper/SKILL.md +3 -3
  122. package/pack/generated/claude/skills/rite-vet/SKILL.md +19 -6
  123. package/pack/generated/claude/skills/rite-vet/reference/review-axes.md +12 -5
  124. package/pack/generated/codex/agents/devrites-proof-runner.toml +16 -9
  125. package/pack/generated/codex/skills/devrites-lib/SKILL.md +2 -0
  126. package/pack/generated/codex/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  127. package/pack/generated/codex/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  128. package/pack/generated/codex/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  129. package/pack/generated/codex/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  130. package/pack/generated/codex/skills/devrites-lib/reference/standards/agents.md +1 -1
  131. package/pack/generated/codex/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  132. package/pack/generated/codex/skills/devrites-lib/reference/standards/gates.md +18 -9
  133. package/pack/generated/codex/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  134. package/pack/generated/codex/skills/rite-autocomplete/SKILL.md +12 -10
  135. package/pack/generated/codex/skills/rite-autocomplete/reference/lookahead.md +4 -4
  136. package/pack/generated/codex/skills/rite-autocomplete/reference/loop.md +20 -9
  137. package/pack/generated/codex/skills/rite-build/SKILL.md +13 -10
  138. package/pack/generated/codex/skills/rite-build/reference/afk-discipline.md +1 -1
  139. package/pack/generated/codex/skills/rite-build/reference/checkpoint.md +1 -1
  140. package/pack/generated/codex/skills/rite-build/reference/parallel-batch.md +57 -36
  141. package/pack/generated/codex/skills/rite-build/reference/phase-contract.md +8 -5
  142. package/pack/generated/codex/skills/rite-build/reference/wright-dispatch.md +7 -7
  143. package/pack/generated/codex/skills/rite-clarify/SKILL.md +2 -2
  144. package/pack/generated/codex/skills/rite-converge/SKILL.md +4 -4
  145. package/pack/generated/codex/skills/rite-define/SKILL.md +7 -3
  146. package/pack/generated/codex/skills/rite-define/reference/plan-template.md +4 -2
  147. package/pack/generated/codex/skills/rite-plan/SKILL.md +11 -8
  148. package/pack/generated/codex/skills/rite-plan/reference/dependency-graph.md +9 -2
  149. package/pack/generated/codex/skills/rite-plan/reference/slicing.md +2 -0
  150. package/pack/generated/codex/skills/rite-polish/SKILL.md +3 -3
  151. package/pack/generated/codex/skills/rite-prove/SKILL.md +24 -12
  152. package/pack/generated/codex/skills/rite-resolve/SKILL.md +3 -3
  153. package/pack/generated/codex/skills/rite-resolve/reference/answer-protocol.md +2 -0
  154. package/pack/generated/codex/skills/rite-review/SKILL.md +11 -3
  155. package/pack/generated/codex/skills/rite-review/reference/feature-scoped-review.md +2 -2
  156. package/pack/generated/codex/skills/rite-seal/SKILL.md +1 -1
  157. package/pack/generated/codex/skills/rite-seal/reference/phase-contract.md +1 -1
  158. package/pack/generated/codex/skills/rite-ship/SKILL.md +2 -2
  159. package/pack/generated/codex/skills/rite-ship/reference/git-ship.md +2 -2
  160. package/pack/generated/codex/skills/rite-spec/SKILL.md +8 -5
  161. package/pack/generated/codex/skills/rite-spec/reference/state-workspace.md +5 -0
  162. package/pack/generated/codex/skills/rite-temper/SKILL.md +3 -3
  163. package/pack/generated/codex/skills/rite-vet/SKILL.md +19 -6
  164. package/pack/generated/codex/skills/rite-vet/reference/review-axes.md +12 -5
  165. package/pack/generated/devin/agents/devrites-proof-runner.md +16 -9
  166. package/pack/generated/devin/skills/devrites-lib/SKILL.md +2 -0
  167. package/pack/generated/devin/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  168. package/pack/generated/devin/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  169. package/pack/generated/devin/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  170. package/pack/generated/devin/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  171. package/pack/generated/devin/skills/devrites-lib/reference/standards/agents.md +1 -1
  172. package/pack/generated/devin/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  173. package/pack/generated/devin/skills/devrites-lib/reference/standards/gates.md +18 -9
  174. package/pack/generated/devin/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  175. package/pack/generated/devin/skills/rite-autocomplete/SKILL.md +12 -10
  176. package/pack/generated/devin/skills/rite-autocomplete/reference/lookahead.md +4 -4
  177. package/pack/generated/devin/skills/rite-autocomplete/reference/loop.md +20 -9
  178. package/pack/generated/devin/skills/rite-build/SKILL.md +13 -10
  179. package/pack/generated/devin/skills/rite-build/reference/afk-discipline.md +1 -1
  180. package/pack/generated/devin/skills/rite-build/reference/checkpoint.md +1 -1
  181. package/pack/generated/devin/skills/rite-build/reference/parallel-batch.md +57 -36
  182. package/pack/generated/devin/skills/rite-build/reference/phase-contract.md +8 -5
  183. package/pack/generated/devin/skills/rite-build/reference/wright-dispatch.md +7 -7
  184. package/pack/generated/devin/skills/rite-clarify/SKILL.md +2 -2
  185. package/pack/generated/devin/skills/rite-converge/SKILL.md +4 -4
  186. package/pack/generated/devin/skills/rite-define/SKILL.md +7 -3
  187. package/pack/generated/devin/skills/rite-define/reference/plan-template.md +4 -2
  188. package/pack/generated/devin/skills/rite-plan/SKILL.md +11 -8
  189. package/pack/generated/devin/skills/rite-plan/reference/dependency-graph.md +9 -2
  190. package/pack/generated/devin/skills/rite-plan/reference/slicing.md +2 -0
  191. package/pack/generated/devin/skills/rite-polish/SKILL.md +3 -3
  192. package/pack/generated/devin/skills/rite-prove/SKILL.md +24 -12
  193. package/pack/generated/devin/skills/rite-resolve/SKILL.md +3 -3
  194. package/pack/generated/devin/skills/rite-resolve/reference/answer-protocol.md +2 -0
  195. package/pack/generated/devin/skills/rite-review/SKILL.md +11 -3
  196. package/pack/generated/devin/skills/rite-review/reference/feature-scoped-review.md +2 -2
  197. package/pack/generated/devin/skills/rite-seal/SKILL.md +1 -1
  198. package/pack/generated/devin/skills/rite-seal/reference/phase-contract.md +1 -1
  199. package/pack/generated/devin/skills/rite-ship/SKILL.md +2 -2
  200. package/pack/generated/devin/skills/rite-ship/reference/git-ship.md +2 -2
  201. package/pack/generated/devin/skills/rite-spec/SKILL.md +8 -5
  202. package/pack/generated/devin/skills/rite-spec/reference/state-workspace.md +5 -0
  203. package/pack/generated/devin/skills/rite-temper/SKILL.md +3 -3
  204. package/pack/generated/devin/skills/rite-vet/SKILL.md +19 -6
  205. package/pack/generated/devin/skills/rite-vet/reference/review-axes.md +12 -5
  206. package/pack/generated/dsh/agents/devrites-proof-runner.md +16 -9
  207. package/pack/generated/dsh/runtime/cordis.patch.yml +1 -1
  208. package/pack/generated/dsh/skills/devrites-lib/SKILL.md +2 -0
  209. package/pack/generated/dsh/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  210. package/pack/generated/dsh/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  211. package/pack/generated/dsh/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  212. package/pack/generated/dsh/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  213. package/pack/generated/dsh/skills/devrites-lib/reference/standards/agents.md +1 -1
  214. package/pack/generated/dsh/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  215. package/pack/generated/dsh/skills/devrites-lib/reference/standards/gates.md +18 -9
  216. package/pack/generated/dsh/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  217. package/pack/generated/dsh/skills/rite-autocomplete/SKILL.md +12 -10
  218. package/pack/generated/dsh/skills/rite-autocomplete/reference/lookahead.md +4 -4
  219. package/pack/generated/dsh/skills/rite-autocomplete/reference/loop.md +20 -9
  220. package/pack/generated/dsh/skills/rite-build/SKILL.md +13 -10
  221. package/pack/generated/dsh/skills/rite-build/reference/afk-discipline.md +1 -1
  222. package/pack/generated/dsh/skills/rite-build/reference/checkpoint.md +1 -1
  223. package/pack/generated/dsh/skills/rite-build/reference/parallel-batch.md +57 -36
  224. package/pack/generated/dsh/skills/rite-build/reference/phase-contract.md +8 -5
  225. package/pack/generated/dsh/skills/rite-build/reference/wright-dispatch.md +7 -7
  226. package/pack/generated/dsh/skills/rite-clarify/SKILL.md +2 -2
  227. package/pack/generated/dsh/skills/rite-converge/SKILL.md +4 -4
  228. package/pack/generated/dsh/skills/rite-define/SKILL.md +7 -3
  229. package/pack/generated/dsh/skills/rite-define/reference/plan-template.md +4 -2
  230. package/pack/generated/dsh/skills/rite-plan/SKILL.md +11 -8
  231. package/pack/generated/dsh/skills/rite-plan/reference/dependency-graph.md +9 -2
  232. package/pack/generated/dsh/skills/rite-plan/reference/slicing.md +2 -0
  233. package/pack/generated/dsh/skills/rite-polish/SKILL.md +3 -3
  234. package/pack/generated/dsh/skills/rite-prove/SKILL.md +24 -12
  235. package/pack/generated/dsh/skills/rite-resolve/SKILL.md +3 -3
  236. package/pack/generated/dsh/skills/rite-resolve/reference/answer-protocol.md +2 -0
  237. package/pack/generated/dsh/skills/rite-review/SKILL.md +11 -3
  238. package/pack/generated/dsh/skills/rite-review/reference/feature-scoped-review.md +2 -2
  239. package/pack/generated/dsh/skills/rite-seal/SKILL.md +1 -1
  240. package/pack/generated/dsh/skills/rite-seal/reference/phase-contract.md +1 -1
  241. package/pack/generated/dsh/skills/rite-ship/SKILL.md +2 -2
  242. package/pack/generated/dsh/skills/rite-ship/reference/git-ship.md +2 -2
  243. package/pack/generated/dsh/skills/rite-spec/SKILL.md +8 -5
  244. package/pack/generated/dsh/skills/rite-spec/reference/state-workspace.md +5 -0
  245. package/pack/generated/dsh/skills/rite-temper/SKILL.md +3 -3
  246. package/pack/generated/dsh/skills/rite-vet/SKILL.md +19 -6
  247. package/pack/generated/dsh/skills/rite-vet/reference/review-axes.md +12 -5
  248. package/pack/generated/omp/agents/devrites-proof-runner.md +16 -9
  249. package/pack/generated/omp/commands/rite-build.md +1 -1
  250. package/pack/generated/omp/skills/devrites-lib/SKILL.md +2 -0
  251. package/pack/generated/omp/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  252. package/pack/generated/omp/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  253. package/pack/generated/omp/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  254. package/pack/generated/omp/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  255. package/pack/generated/omp/skills/devrites-lib/reference/standards/agents.md +1 -1
  256. package/pack/generated/omp/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  257. package/pack/generated/omp/skills/devrites-lib/reference/standards/gates.md +18 -9
  258. package/pack/generated/omp/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  259. package/pack/generated/omp/skills/rite-autocomplete/SKILL.md +12 -10
  260. package/pack/generated/omp/skills/rite-autocomplete/reference/lookahead.md +4 -4
  261. package/pack/generated/omp/skills/rite-autocomplete/reference/loop.md +20 -9
  262. package/pack/generated/omp/skills/rite-build/SKILL.md +13 -10
  263. package/pack/generated/omp/skills/rite-build/reference/afk-discipline.md +1 -1
  264. package/pack/generated/omp/skills/rite-build/reference/checkpoint.md +1 -1
  265. package/pack/generated/omp/skills/rite-build/reference/parallel-batch.md +57 -36
  266. package/pack/generated/omp/skills/rite-build/reference/phase-contract.md +8 -5
  267. package/pack/generated/omp/skills/rite-build/reference/wright-dispatch.md +7 -7
  268. package/pack/generated/omp/skills/rite-clarify/SKILL.md +2 -2
  269. package/pack/generated/omp/skills/rite-converge/SKILL.md +4 -4
  270. package/pack/generated/omp/skills/rite-define/SKILL.md +7 -3
  271. package/pack/generated/omp/skills/rite-define/reference/plan-template.md +4 -2
  272. package/pack/generated/omp/skills/rite-plan/SKILL.md +11 -8
  273. package/pack/generated/omp/skills/rite-plan/reference/dependency-graph.md +9 -2
  274. package/pack/generated/omp/skills/rite-plan/reference/slicing.md +2 -0
  275. package/pack/generated/omp/skills/rite-polish/SKILL.md +3 -3
  276. package/pack/generated/omp/skills/rite-prove/SKILL.md +24 -12
  277. package/pack/generated/omp/skills/rite-resolve/SKILL.md +3 -3
  278. package/pack/generated/omp/skills/rite-resolve/reference/answer-protocol.md +2 -0
  279. package/pack/generated/omp/skills/rite-review/SKILL.md +11 -3
  280. package/pack/generated/omp/skills/rite-review/reference/feature-scoped-review.md +2 -2
  281. package/pack/generated/omp/skills/rite-seal/SKILL.md +1 -1
  282. package/pack/generated/omp/skills/rite-seal/reference/phase-contract.md +1 -1
  283. package/pack/generated/omp/skills/rite-ship/SKILL.md +2 -2
  284. package/pack/generated/omp/skills/rite-ship/reference/git-ship.md +2 -2
  285. package/pack/generated/omp/skills/rite-spec/SKILL.md +8 -5
  286. package/pack/generated/omp/skills/rite-spec/reference/state-workspace.md +5 -0
  287. package/pack/generated/omp/skills/rite-temper/SKILL.md +3 -3
  288. package/pack/generated/omp/skills/rite-vet/SKILL.md +19 -6
  289. package/pack/generated/omp/skills/rite-vet/reference/review-axes.md +12 -5
  290. package/pack/generated/pi/agents/devrites-proof-runner.md +16 -9
  291. package/pack/generated/pi/prompts/rite-build.md +1 -1
  292. package/pack/generated/pi/skills/devrites-lib/SKILL.md +2 -0
  293. package/pack/generated/pi/skills/devrites-lib/reference/candidate-integrity.md +7 -3
  294. package/pack/generated/pi/skills/devrites-lib/reference/parallel-dispatch.md +3 -0
  295. package/pack/generated/pi/skills/devrites-lib/reference/parallel-workspaces.md +236 -0
  296. package/pack/generated/pi/skills/devrites-lib/reference/standards/afk-hitl.md +9 -12
  297. package/pack/generated/pi/skills/devrites-lib/reference/standards/agents.md +1 -1
  298. package/pack/generated/pi/skills/devrites-lib/reference/standards/context-hygiene.md +1 -1
  299. package/pack/generated/pi/skills/devrites-lib/reference/standards/gates.md +18 -9
  300. package/pack/generated/pi/skills/devrites-lib/reference/workspace-artifact-schema.md +10 -0
  301. package/pack/generated/pi/skills/rite-autocomplete/SKILL.md +12 -10
  302. package/pack/generated/pi/skills/rite-autocomplete/reference/lookahead.md +4 -4
  303. package/pack/generated/pi/skills/rite-autocomplete/reference/loop.md +20 -9
  304. package/pack/generated/pi/skills/rite-build/SKILL.md +13 -10
  305. package/pack/generated/pi/skills/rite-build/reference/afk-discipline.md +1 -1
  306. package/pack/generated/pi/skills/rite-build/reference/checkpoint.md +1 -1
  307. package/pack/generated/pi/skills/rite-build/reference/parallel-batch.md +57 -36
  308. package/pack/generated/pi/skills/rite-build/reference/phase-contract.md +8 -5
  309. package/pack/generated/pi/skills/rite-build/reference/wright-dispatch.md +7 -7
  310. package/pack/generated/pi/skills/rite-clarify/SKILL.md +2 -2
  311. package/pack/generated/pi/skills/rite-converge/SKILL.md +4 -4
  312. package/pack/generated/pi/skills/rite-define/SKILL.md +7 -3
  313. package/pack/generated/pi/skills/rite-define/reference/plan-template.md +4 -2
  314. package/pack/generated/pi/skills/rite-plan/SKILL.md +11 -8
  315. package/pack/generated/pi/skills/rite-plan/reference/dependency-graph.md +9 -2
  316. package/pack/generated/pi/skills/rite-plan/reference/slicing.md +2 -0
  317. package/pack/generated/pi/skills/rite-polish/SKILL.md +3 -3
  318. package/pack/generated/pi/skills/rite-prove/SKILL.md +24 -12
  319. package/pack/generated/pi/skills/rite-resolve/SKILL.md +3 -3
  320. package/pack/generated/pi/skills/rite-resolve/reference/answer-protocol.md +2 -0
  321. package/pack/generated/pi/skills/rite-review/SKILL.md +11 -3
  322. package/pack/generated/pi/skills/rite-review/reference/feature-scoped-review.md +2 -2
  323. package/pack/generated/pi/skills/rite-seal/SKILL.md +1 -1
  324. package/pack/generated/pi/skills/rite-seal/reference/phase-contract.md +1 -1
  325. package/pack/generated/pi/skills/rite-ship/SKILL.md +2 -2
  326. package/pack/generated/pi/skills/rite-ship/reference/git-ship.md +2 -2
  327. package/pack/generated/pi/skills/rite-spec/SKILL.md +8 -5
  328. package/pack/generated/pi/skills/rite-spec/reference/state-workspace.md +5 -0
  329. package/pack/generated/pi/skills/rite-temper/SKILL.md +3 -3
  330. package/pack/generated/pi/skills/rite-vet/SKILL.md +19 -6
  331. package/pack/generated/pi/skills/rite-vet/reference/review-axes.md +12 -5
  332. package/package.json +1 -1
@@ -5,6 +5,7 @@ import (
5
5
  "fmt"
6
6
  "io"
7
7
  "os"
8
+ "path/filepath"
8
9
  "strconv"
9
10
  "strings"
10
11
  )
@@ -79,18 +80,27 @@ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
79
80
  capN := 0
80
81
  sawCap := false
81
82
  fromPlan := false
83
+ workspaces := ""
84
+ sawWorkspaces, sawInput := false, false
82
85
  for i := 0; i < len(args); i++ {
83
86
  switch args[i] {
84
- case "--root", "--slug":
87
+ case "--root", "--slug", "--workspaces":
85
88
  if i+1 >= len(args) {
86
89
  fmt.Fprintln(stderr, usageSelect)
87
90
  return ExitUsage
88
91
  }
89
92
  i++
90
- if args[i-1] == "--root" {
93
+ switch args[i-1] {
94
+ case "--root":
91
95
  root = args[i]
92
- } else {
96
+ case "--slug":
93
97
  slug = args[i]
98
+ case "--workspaces":
99
+ if sawWorkspaces {
100
+ fmt.Fprintln(stderr, "parallel select: duplicate --workspaces")
101
+ return ExitUsage
102
+ }
103
+ workspaces, sawWorkspaces = args[i], true
94
104
  }
95
105
  case "--cap":
96
106
  if i+1 >= len(args) {
@@ -116,28 +126,90 @@ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
116
126
  return ExitUsage
117
127
  }
118
128
  jsonPath = args[i]
129
+ if sawInput {
130
+ fmt.Fprintln(stderr, "parallel select: multiple JSON inputs")
131
+ return ExitUsage
132
+ }
133
+ sawInput = true
119
134
  }
120
135
  }
121
- if !sawCap {
136
+ if !sawCap || capN < 1 || capN > MaxParallelSlices {
122
137
  fmt.Fprintln(stderr, usageSelect)
123
138
  return ExitUsage
124
139
  }
140
+ if sawWorkspaces && (!fromPlan || root == "" || slug == "" || sawInput) {
141
+ fmt.Fprintln(stderr, "parallel select: --workspaces requires --from-plan, --root and --slug and takes no JSON input")
142
+ return ExitUsage
143
+ }
125
144
  var slices []SlicePaths
126
145
  if fromPlan {
127
- if slug == "" || jsonPath != "-" {
146
+ if slug == "" || sawInput {
128
147
  fmt.Fprintln(stderr, "parallel select: --from-plan needs --slug and takes no JSON input")
129
148
  return ExitUsage
130
149
  }
131
- if PlanReady == nil {
132
- fmt.Fprintln(stderr, "parallel select: --from-plan is unavailable in this build")
133
- return ExitBlocked
134
- }
135
- ready, err := PlanReady(root, slug)
136
- if err != nil {
137
- fmt.Fprintf(stderr, "parallel select: %v\n", err)
138
- return ExitBlocked
150
+ if sawWorkspaces {
151
+ order := []string{slug}
152
+ seen := make(map[string]bool)
153
+ if err := validateSlug(slug); err != nil {
154
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
155
+ return ExitUsage
156
+ }
157
+ for _, target := range strings.Split(workspaces, ",") {
158
+ if err := validateSlug(target); err != nil || seen[target] {
159
+ fmt.Fprintf(stderr, "parallel select: invalid or duplicate workspace %q\n", target)
160
+ return ExitUsage
161
+ }
162
+ seen[target] = true
163
+ if target != slug {
164
+ order = append(order, target)
165
+ }
166
+ }
167
+ if _, err := controllerLease(root, slug); err != nil {
168
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
169
+ return ExitBlocked
170
+ }
171
+ if WorkspaceReady == nil {
172
+ fmt.Fprintln(stderr, "parallel select: --workspaces is unavailable in this build")
173
+ return ExitBlocked
174
+ }
175
+ for _, target := range order {
176
+ ready, notices, err := WorkspaceReady(root, slug, target)
177
+ for _, notice := range notices {
178
+ fmt.Fprintf(stderr, "parallel select: workspace %s: %s\n", target, notice)
179
+ }
180
+ if err == nil {
181
+ ready, err = normalizeSlices(slug, ready, root)
182
+ }
183
+ if err == nil {
184
+ for _, sp := range ready {
185
+ source := sp.Workspace
186
+ if source == "" {
187
+ source = slug
188
+ }
189
+ if source != target {
190
+ err = fmt.Errorf("candidate source %s does not match requested workspace", source)
191
+ break
192
+ }
193
+ }
194
+ }
195
+ if err != nil {
196
+ fmt.Fprintf(stderr, "parallel select: workspace %s: %v\n", target, err)
197
+ continue
198
+ }
199
+ slices = append(slices, ready...)
200
+ }
201
+ } else {
202
+ if PlanReady == nil {
203
+ fmt.Fprintln(stderr, "parallel select: --from-plan is unavailable in this build")
204
+ return ExitBlocked
205
+ }
206
+ ready, err := PlanReady(root, slug)
207
+ if err != nil {
208
+ fmt.Fprintf(stderr, "parallel select: %v\n", err)
209
+ return ExitBlocked
210
+ }
211
+ slices = ready
139
212
  }
140
- slices = ready
141
213
  } else {
142
214
  data, err := readJSONInput(jsonPath, stdin)
143
215
  if err != nil {
@@ -161,26 +233,6 @@ func cmdSelect(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
161
233
  return ExitOK
162
234
  }
163
235
 
164
- // runningLease returns slug's lease when it is still in flight
165
- // (running|integrate-failed), or nil when there is none to seed from.
166
- func runningLease(root, slug string) (*Lease, error) {
167
- path, err := LeasePath(root, slug)
168
- if err != nil {
169
- return nil, err
170
- }
171
- lease, err := ReadLease(path)
172
- if os.IsNotExist(err) {
173
- return nil, nil
174
- }
175
- if err != nil {
176
- return nil, err
177
- }
178
- if lease.Status != StatusRunning && lease.Status != StatusIntegrateFailed {
179
- return nil, nil
180
- }
181
- return lease, nil
182
- }
183
-
184
236
  func runParallel(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
185
237
  if len(args) == 0 {
186
238
  fmt.Fprintln(stderr, parallelUsage())
@@ -200,6 +252,8 @@ func runParallel(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
200
252
  return cmdIntegrate(rest, stdout, stderr)
201
253
  case "cleanup":
202
254
  return cmdCleanup(rest, stdout, stderr)
255
+ case "prune":
256
+ return cmdPrune(rest, stdout, stderr)
203
257
  case "status":
204
258
  return cmdStatus(rest, stdout, stderr)
205
259
  case "lease-write":
@@ -220,35 +274,37 @@ func runParallel(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
220
274
  }
221
275
 
222
276
  const (
223
- usageCreate = "usage: parallel create --root --slug --batch --base --json"
224
- usageAdd = "usage: parallel add --root --slug --json <json-file>|-"
277
+ usageCreate = "usage: parallel create --root --slug --batch --base --json [--cap <1-10>]"
278
+ usageAdd = "usage: parallel add --root --slug --json <json-file>|- [--cap <1-10>]"
225
279
  usageRecordGreen = "usage: parallel record-green --root --slug --slice --commit"
226
280
  usageAbort = "usage: parallel abort --root --slug"
227
281
  usageIntegrate = "usage: parallel integrate --root --slug [--apply-to-control [--slice <id>]]"
228
282
  usageCleanup = "usage: parallel cleanup --root --slug [--force]"
283
+ usagePrune = "usage: parallel prune --root --slug"
229
284
  usageStatus = "usage: parallel status --root --slug"
230
285
  usageLeaseWrite = "usage: parallel lease-write --root --slug --json"
231
286
  usageLeaseRead = "usage: parallel lease-read --root --slug [--field name]"
232
287
  usageLeaseClear = "usage: parallel lease-clear --root --slug"
233
288
  usagePathDisjoint = "usage: check path-disjoint [--root <dir>] [<json-file>|-]"
234
- usageSelect = "usage: parallel select --cap <1-10> [--root <dir>] [--slug <slug>] [--from-plan | <json-file>|-]"
289
+ usageSelect = "usage: parallel select --cap <1-10> [--root <dir>] [--slug <slug>] [--from-plan [--workspaces <slugs>] | <json-file>|-]"
235
290
  )
236
291
 
237
292
  func parallelUsage() string {
238
293
  return strings.TrimSpace(`usage: parallel <subcommand> [options]
239
294
 
240
295
  Subcommands:
241
- create --root --slug --batch --base --json
242
- add --root --slug --json
296
+ create --root --slug --batch --base --json [--cap <1-10>]
297
+ add --root --slug --json [--cap <1-10>]
243
298
  record-green --root --slug --slice --commit
244
299
  abort --root --slug
245
300
  integrate --root --slug [--apply-to-control [--slice <id>]]
246
301
  cleanup --root --slug [--force]
302
+ prune --root --slug
247
303
  status --root --slug
248
304
  lease-write --root --slug --json
249
305
  lease-read --root --slug [--field name]
250
306
  lease-clear --root --slug
251
- select --cap <1-10> [--root] [--slug] [--from-plan | <json-file>|-]
307
+ select --cap <1-10> [--root] [--slug] [--from-plan [--workspaces <slugs>] | <json-file>|-]
252
308
 
253
309
  Exit codes: 0 ok, 2 usage, 3 blocked`)
254
310
  }
@@ -269,6 +325,8 @@ func CommandUsage(name string) string {
269
325
  return usageIntegrate
270
326
  case "cleanup":
271
327
  return usageCleanup
328
+ case "prune":
329
+ return usagePrune
272
330
  case "status":
273
331
  return usageStatus
274
332
  case "lease-write":
@@ -288,7 +346,8 @@ func CommandUsage(name string) string {
288
346
 
289
347
  type flagSet struct {
290
348
  Root, Slug, Batch, Base, Slice, Commit, JSON, Field, Session string
291
- ApplyToControl, Force, Help bool
349
+ ApplyToControl, Force, Help, HasCapacity bool
350
+ Capacity int
292
351
  }
293
352
 
294
353
  func parseFlags(args []string, stderr io.Writer) (flagSet, []string, int) {
@@ -359,6 +418,17 @@ func parseFlags(args []string, stderr io.Writer) (flagSet, []string, int) {
359
418
  return f, nil, ExitUsage
360
419
  }
361
420
  f.Session = v
421
+ case "--cap":
422
+ v, ok := need(a)
423
+ if !ok {
424
+ return f, nil, ExitUsage
425
+ }
426
+ n, err := strconv.Atoi(v)
427
+ if err != nil || n < 1 || n > MaxParallelSlices || f.HasCapacity {
428
+ fmt.Fprintf(stderr, "parallel: --cap must be one integer from 1-%d\n", MaxParallelSlices)
429
+ return f, nil, ExitUsage
430
+ }
431
+ f.Capacity, f.HasCapacity = n, true
362
432
  case "--apply-to-control":
363
433
  f.ApplyToControl = true
364
434
  case "--force":
@@ -386,6 +456,10 @@ func parseFlagsOrHelp(args []string, stdout, stderr io.Writer, usage string) (fl
386
456
  fmt.Fprintln(stdout, usage)
387
457
  return f, ExitOK, true
388
458
  }
459
+ if f.HasCapacity && usage != usageCreate && usage != usageAdd {
460
+ fmt.Fprintln(stderr, "parallel: --cap is only supported by create and add")
461
+ return f, ExitUsage, true
462
+ }
389
463
  return f, ExitOK, false
390
464
  }
391
465
 
@@ -450,12 +524,13 @@ func cmdCreate(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
450
524
  return ExitBlocked
451
525
  }
452
526
  lease, err := Create(CreateOpts{
453
- Root: f.Root,
454
- Slug: f.Slug,
455
- BatchID: f.Batch,
456
- BaseSHA: f.Base,
457
- Session: f.Session,
458
- Slices: slices,
527
+ Root: f.Root,
528
+ Slug: f.Slug,
529
+ BatchID: f.Batch,
530
+ BaseSHA: f.Base,
531
+ Session: f.Session,
532
+ Slices: slices,
533
+ Capacity: f.Capacity,
459
534
  })
460
535
  if err != nil {
461
536
  fmt.Fprintf(stderr, "parallel create: %v\n", err)
@@ -490,11 +565,15 @@ func cmdAdd(args []string, stdin io.Reader, stdout, stderr io.Writer) int {
490
565
  fmt.Fprintf(stderr, "parallel add: %v\n", err)
491
566
  return ExitBlocked
492
567
  }
493
- lease, err := Add(AddOpts{Root: f.Root, Slug: f.Slug, Slices: slices})
568
+ lease, err := Add(AddOpts{Root: f.Root, Slug: f.Slug, Slices: slices, Capacity: f.Capacity})
494
569
  if err != nil {
495
570
  fmt.Fprintf(stderr, "parallel add: %v\n", err)
496
571
  return ExitBlocked
497
572
  }
573
+ if len(slices) == 0 {
574
+ fmt.Fprintf(stdout, "capacity: %d; batch=%s active=%d\n", leaseCapacity(lease), lease.BatchID, len(activeSlices(lease)))
575
+ return ExitOK
576
+ }
498
577
  fmt.Fprintf(stdout, "added: %d worktrees; batch=%s active=%d base=%s\n",
499
578
  len(slices), lease.BatchID, len(activeSlices(lease)), lease.Slices[len(lease.Slices)-1].BaseSHA)
500
579
  return ExitOK
@@ -620,6 +699,29 @@ func cmdCleanup(args []string, stdout, stderr io.Writer) int {
620
699
  return ExitOK
621
700
  }
622
701
 
702
+ func cmdPrune(args []string, stdout, stderr io.Writer) int {
703
+ f, code, done := parseFlagsOrHelp(args, stdout, stderr, usagePrune)
704
+ if done {
705
+ return code
706
+ }
707
+ if code := requireRootSlug(f, stderr); code != ExitOK {
708
+ return code
709
+ }
710
+ if code := rejectUnsupportedFlags(f, stderr, false, false); code != ExitOK {
711
+ return code
712
+ }
713
+ removed, err := Prune(f.Root, f.Slug)
714
+ for _, path := range removed {
715
+ fmt.Fprintf(stdout, "pruned: %s\n", path)
716
+ }
717
+ if err != nil {
718
+ fmt.Fprintf(stderr, "parallel prune: %v\n", err)
719
+ return ExitBlocked
720
+ }
721
+ fmt.Fprintln(stdout, "prune: done; branches and lease retained")
722
+ return ExitOK
723
+ }
724
+
623
725
  func cmdStatus(args []string, stdout, stderr io.Writer) int {
624
726
  f, code, done := parseFlagsOrHelp(args, stdout, stderr, usageStatus)
625
727
  if done {
@@ -673,7 +775,40 @@ func cmdLeaseWrite(args []string, stdin io.Reader, stdout, stderr io.Writer) int
673
775
  fmt.Fprintf(stderr, "parallel lease-write: %v\n", err)
674
776
  return ExitUsage
675
777
  }
676
- err = withLeaseLock(f.Root, f.Slug, func() error { return WriteLease(path, &lease) })
778
+ err = withLeaseLock(f.Root, f.Slug, func() error {
779
+ found, err := controllerLease(f.Root, f.Slug)
780
+ if err != nil {
781
+ return err
782
+ }
783
+ var current *Lease
784
+ if found != nil {
785
+ current = found.lease
786
+ if lease.BatchID != current.BatchID || lease.BaseSHA != current.BaseSHA || expectedControl(&lease) != expectedControl(current) {
787
+ return fmt.Errorf("lease-write cannot replace retained batch or control tip; cleanup the current controller explicitly")
788
+ }
789
+ if lease.Capacity == 0 {
790
+ lease.Capacity = current.Capacity
791
+ }
792
+ }
793
+ if err := normalizeLease(&lease, f.Slug); err != nil {
794
+ return err
795
+ }
796
+ if err := protectRawLease(current, &lease); err != nil {
797
+ return err
798
+ }
799
+ if current != nil {
800
+ if err := ensureControllerIdentity(f.Root, current); err != nil {
801
+ return err
802
+ }
803
+ if current.PendingLanding != nil {
804
+ return nil
805
+ }
806
+ }
807
+ if err := validateLeaseSources(filepath.Join(f.Root, ".devrites"), &lease); err != nil {
808
+ return err
809
+ }
810
+ return WriteLease(path, &lease)
811
+ })
677
812
  if err != nil {
678
813
  fmt.Fprintf(stderr, "parallel lease-write: %v\n", err)
679
814
  return ExitBlocked
@@ -703,6 +838,10 @@ func cmdLeaseRead(args []string, stdout, stderr io.Writer) int {
703
838
  fmt.Fprintf(stderr, "parallel lease-read: %v\n", err)
704
839
  return ExitBlocked
705
840
  }
841
+ if err := normalizeLease(lease, f.Slug); err != nil {
842
+ fmt.Fprintf(stderr, "parallel lease-read: %v\n", err)
843
+ return ExitBlocked
844
+ }
706
845
  switch f.Field {
707
846
  case "", "yaml", "md":
708
847
  text, err := EmitLeaseMarkdown(lease)
@@ -726,6 +865,8 @@ func cmdLeaseRead(args []string, stdout, stderr io.Writer) int {
726
865
  fmt.Fprintln(stdout, lease.BatchID)
727
866
  case "n":
728
867
  fmt.Fprintln(stdout, lease.N)
868
+ case "capacity":
869
+ fmt.Fprintln(stdout, leaseCapacity(lease))
729
870
  default:
730
871
  fmt.Fprintf(stderr, "parallel lease-read: unknown field %q\n", f.Field)
731
872
  return ExitUsage
@@ -192,12 +192,30 @@ func deleteBranch(repo, branch string) error {
192
192
  if err != nil || !exists {
193
193
  return err
194
194
  }
195
- _, err = git(repo, "branch", "-D", branch)
195
+ tip, err := revParse(repo, "refs/heads/"+branch)
196
+ if err != nil {
197
+ return err
198
+ }
199
+ return deleteBranchAt(repo, branch, tip)
200
+ }
201
+
202
+ // Bind deletion to the observed tip without bypassing checked-out branch safety.
203
+ func deleteBranchAt(repo, branch, tip string) error {
204
+ worktrees, err := git(repo, "worktree", "list", "--porcelain", "-z")
205
+ if err != nil {
206
+ return err
207
+ }
208
+ for _, field := range strings.Split(worktrees, "\x00") {
209
+ if field == "branch refs/heads/"+branch {
210
+ return fmt.Errorf("branch %s remains checked out in a registered worktree", branch)
211
+ }
212
+ }
213
+ _, err = git(repo, "update-ref", "-d", "refs/heads/"+branch, tip)
196
214
  return err
197
215
  }
198
216
 
199
217
  func worktreeAdd(repo, path, branch, startPoint string) error {
200
- _, err := git(repo, "worktree", "add", "-b", branch, path, startPoint)
218
+ _, err := git(repo, "worktree", "add", "-b", branch, resolvedPath(path), startPoint)
201
219
  return err
202
220
  }
203
221
 
@@ -208,27 +226,27 @@ func AddDetachedWorktree(repo, path, rev string) error {
208
226
  return err
209
227
  }
210
228
 
211
- // RemoveWorktree force-removes a worktree, falling back to delete plus prune.
212
- func RemoveWorktree(repo, path string) error { return worktreeRemove(repo, path) }
229
+ // RemoveWorktree force-removes a caller-owned throwaway worktree. A Git
230
+ // refusal never grants permission to recursively delete its remaining data.
231
+ func RemoveWorktree(repo, path string) error {
232
+ _, err := git(repo, "worktree", "remove", "--force", resolvedPath(path))
233
+ return err
234
+ }
213
235
 
214
236
  func worktreeRemove(repo, path string) error {
215
- _, err := git(repo, "worktree", "remove", "--force", path)
216
- if err != nil {
217
- _ = os.RemoveAll(path)
218
- _, _ = git(repo, "worktree", "prune")
219
- }
237
+ _, err := git(repo, "worktree", "remove", resolvedPath(path))
220
238
  return err
221
239
  }
222
240
 
223
241
  // registeredWorktrees returns the symlink-resolved paths git lists as
224
242
  // worktrees of repo: the only authoritative record of what worktree add made.
225
243
  func registeredWorktrees(repo string) (map[string]bool, error) {
226
- out, err := git(repo, "worktree", "list", "--porcelain")
244
+ out, err := git(repo, "worktree", "list", "--porcelain", "-z")
227
245
  if err != nil {
228
246
  return nil, err
229
247
  }
230
248
  set := map[string]bool{}
231
- for _, line := range strings.Split(out, "\n") {
249
+ for _, line := range strings.Split(out, "\x00") {
232
250
  if p, ok := strings.CutPrefix(line, "worktree "); ok {
233
251
  set[resolvedPath(p)] = true
234
252
  }
@@ -240,10 +258,25 @@ func registeredWorktrees(repo string) (map[string]bool, error) {
240
258
  func RegisteredWorktrees(repo string) (map[string]bool, error) { return registeredWorktrees(repo) }
241
259
 
242
260
  func resolvedPath(p string) string {
243
- if r, err := filepath.EvalSymlinks(p); err == nil {
244
- return r
261
+ if abs, err := filepath.Abs(p); err == nil {
262
+ p = abs
263
+ }
264
+ original := filepath.Clean(p)
265
+ prefix, suffix := original, ""
266
+ for {
267
+ if resolved, err := filepath.EvalSymlinks(prefix); err == nil {
268
+ return filepath.Join(resolved, suffix)
269
+ } else if !os.IsNotExist(err) {
270
+ return original
271
+ }
272
+ parent := filepath.Dir(prefix)
273
+ if parent == prefix {
274
+ return original
275
+ }
276
+ // Missing leaves must retain their existing ancestors' canonical identity.
277
+ suffix = filepath.Join(filepath.Base(prefix), suffix)
278
+ prefix = parent
245
279
  }
246
- return filepath.Clean(p)
247
280
  }
248
281
 
249
282
  // createdWorktree reports whether p is a registered worktree. A path that is