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
@@ -20,8 +20,47 @@ var winAbsRE = regexp.MustCompile(`(?i)^[A-Za-z]:[/\\]`)
20
20
 
21
21
  // SlicePaths is one path-disjoint eligibility unit.
22
22
  type SlicePaths struct {
23
- ID string `json:"id"`
24
- Paths []string `json:"paths"`
23
+ ID string `json:"id"`
24
+ Workspace string `json:"workspace,omitempty"`
25
+ Paths []string `json:"paths"`
26
+ }
27
+
28
+ // SliceKey identifies a slice in its source workspace; empty means controller.
29
+ func SliceKey(workspace, id string) string {
30
+ if workspace == "" {
31
+ return id
32
+ }
33
+ return workspace + "/" + id
34
+ }
35
+
36
+ func normalizeSlices(controller string, slices []SlicePaths, root string) ([]SlicePaths, error) {
37
+ normalized := make([]SlicePaths, 0, len(slices))
38
+ seen := make(map[string]bool, len(slices))
39
+ for _, item := range slices {
40
+ if err := validateSliceID(item.ID); err != nil {
41
+ return nil, err
42
+ }
43
+ if item.Workspace != "" {
44
+ if err := validateSlug(item.Workspace); err != nil {
45
+ return nil, err
46
+ }
47
+ if item.Workspace == controller {
48
+ item.Workspace = ""
49
+ }
50
+ }
51
+ key := SliceKey(item.Workspace, item.ID)
52
+ if seen[key] {
53
+ return nil, fmt.Errorf("duplicate slice id %q", key)
54
+ }
55
+ seen[key] = true
56
+ paths, err := validateSlicePaths(item.Paths, fmt.Sprintf("slice %q", key), root)
57
+ if err != nil {
58
+ return nil, err
59
+ }
60
+ item.Paths = paths
61
+ normalized = append(normalized, item)
62
+ }
63
+ return normalized, nil
25
64
  }
26
65
 
27
66
  // NormalizePath mirrors scripts/check-path-disjoint.py: slash-normalize, reject
@@ -116,11 +155,16 @@ func CheckPathDisjoint(slices []SlicePaths, root string) ([]string, error) {
116
155
  for index, item := range slices {
117
156
  label := fmt.Sprintf("slice %d", index)
118
157
  if item.ID != "" {
119
- label = fmt.Sprintf("slice %q", item.ID)
120
- ids = append(ids, item.ID)
158
+ label = fmt.Sprintf("slice %q", SliceKey(item.Workspace, item.ID))
159
+ ids = append(ids, SliceKey(item.Workspace, item.ID))
121
160
  } else {
122
161
  ids = append(ids, fmt.Sprintf("%d", index))
123
162
  }
163
+ if item.Workspace != "" {
164
+ if err := validateSlug(item.Workspace); err != nil {
165
+ return nil, err
166
+ }
167
+ }
124
168
  paths, err := validateSlicePaths(item.Paths, label, root)
125
169
  if err != nil {
126
170
  return nil, err
@@ -0,0 +1,133 @@
1
+ package parallel
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+ "os"
7
+ )
8
+
9
+ func fullyClean(worktree string) error {
10
+ out, err := git(worktree, "status", "--porcelain", "--untracked-files=all", "--ignored=matching")
11
+ if err != nil {
12
+ return err
13
+ }
14
+ if out != "" {
15
+ return fmt.Errorf("worktree has tracked, untracked, or ignored data: %s", worktree)
16
+ }
17
+ return nil
18
+ }
19
+
20
+ // acceptedWorker checks the ref even when its checkout has already disappeared.
21
+ // Control rewrites WIP commits, so exact transfer identity replaces ancestry.
22
+ func acceptedWorker(root, slug string, lease *Lease, sl LeaseSlice, registered map[string]bool) error {
23
+ if sl.Branch != workerBranch(slug, lease.BatchID, sl.Workspace, sl.ID) || !ownedWorktreePath(root, lease.BatchID, sl) {
24
+ return fmt.Errorf("worker identity is not owned: %s (%s)", sl.WorktreePath, sl.Branch)
25
+ }
26
+ accepted, err := revParse(root, sl.TransferCommit)
27
+ if err != nil || sl.TransferCommit == "" {
28
+ return fmt.Errorf("worker has no usable accepted transfer: %s", sl.Branch)
29
+ }
30
+ tip, err := revParse(root, "refs/heads/"+sl.Branch)
31
+ if err != nil || tip != accepted {
32
+ return fmt.Errorf("worker branch differs from accepted transfer %s: %s", accepted, sl.Branch)
33
+ }
34
+ if _, err := os.Lstat(sl.WorktreePath); os.IsNotExist(err) {
35
+ if registered[resolvedPath(sl.WorktreePath)] {
36
+ return fmt.Errorf("missing worker checkout remains registered; preserve its index and recovery metadata: %s", sl.WorktreePath)
37
+ }
38
+ return nil
39
+ } else if err != nil {
40
+ return err
41
+ }
42
+ if !registered[resolvedPath(sl.WorktreePath)] {
43
+ return fmt.Errorf("worker path is not registered; leaving replacement untouched: %s", sl.WorktreePath)
44
+ }
45
+ ref, err := git(sl.WorktreePath, "symbolic-ref", "--quiet", "HEAD")
46
+ if err != nil || ref != "refs/heads/"+sl.Branch {
47
+ return fmt.Errorf("worker checkout switched branch: %s", sl.WorktreePath)
48
+ }
49
+ head, err := headSHA(sl.WorktreePath)
50
+ if err != nil || head != accepted {
51
+ return fmt.Errorf("worker checkout differs from accepted transfer: %s", sl.WorktreePath)
52
+ }
53
+ return fullyClean(sl.WorktreePath)
54
+ }
55
+
56
+ // Prune removes only unchanged integrated checkouts, not refs or lease rows.
57
+ // Worker-bound review/proof and source bookkeeping must finish before callers prune.
58
+ func Prune(root, slug string) (removed []string, err error) {
59
+ err = withLeaseLock(root, slug, func() error {
60
+ path, err := LeasePath(root, slug)
61
+ if err != nil {
62
+ return err
63
+ }
64
+ lease, err := ReadLease(path)
65
+ if err != nil {
66
+ return err
67
+ }
68
+ if err := normalizeLease(lease, slug); err != nil {
69
+ return err
70
+ }
71
+ if _, err := controllerLease(root, slug); err != nil {
72
+ return err
73
+ }
74
+ if _, err := recoverLanding(root, slug, path, lease, false, false); err != nil {
75
+ return err
76
+ }
77
+ registered, err := registeredWorktrees(root)
78
+ if err != nil {
79
+ return err
80
+ }
81
+ var failures []error
82
+ for _, sl := range lease.Slices {
83
+ if sl.WrightStatus != WrightIntegrated {
84
+ continue
85
+ }
86
+ if err := acceptedWorker(root, slug, lease, sl, registered); err != nil {
87
+ failures = append(failures, fmt.Errorf("%s: %w", SliceKey(sl.Workspace, sl.ID), err))
88
+ continue
89
+ }
90
+ if _, err := os.Lstat(sl.WorktreePath); os.IsNotExist(err) {
91
+ continue
92
+ }
93
+ if err := removeWorktree(root, sl.WorktreePath); err != nil {
94
+ failures = append(failures, fmt.Errorf("kept recovery path %s and ref %s: %w", sl.WorktreePath, sl.Branch, err))
95
+ continue
96
+ }
97
+ removed = append(removed, sl.WorktreePath)
98
+ }
99
+ return errors.Join(failures...)
100
+ })
101
+ return removed, err
102
+ }
103
+
104
+ func prepareStaging(root, slug string, lease *Lease, path, branch string) error {
105
+ claim := LeaseSlice{ID: "integrate", WorktreePath: path}
106
+ if !ownedWorktreePath(root, lease.BatchID, claim) {
107
+ return fmt.Errorf("staging path is not owned: %s", path)
108
+ }
109
+ registered, err := registeredWorktrees(root)
110
+ if err != nil {
111
+ return err
112
+ }
113
+ if _, err := os.Lstat(path); err == nil {
114
+ if !registered[resolvedPath(path)] {
115
+ return fmt.Errorf("staging path is not registered; leaving replacement untouched: %s", path)
116
+ }
117
+ ref, err := git(path, "symbolic-ref", "--quiet", "HEAD")
118
+ if err != nil || ref != "refs/heads/"+branch {
119
+ return fmt.Errorf("staging checkout changed branch: %s", path)
120
+ }
121
+ if err := fullyClean(path); err != nil {
122
+ return err
123
+ }
124
+ if err := removeWorktree(root, path); err != nil {
125
+ return fmt.Errorf("staging checkout kept at %s: %w", path, err)
126
+ }
127
+ } else if !os.IsNotExist(err) {
128
+ return err
129
+ } else if registered[resolvedPath(path)] {
130
+ return fmt.Errorf("missing staging checkout remains registered; preserve its metadata and recover explicitly: %s", path)
131
+ }
132
+ return deleteBranch(root, branch)
133
+ }
@@ -0,0 +1,239 @@
1
+ package parallel
2
+
3
+ import (
4
+ "fmt"
5
+ "os"
6
+ "path/filepath"
7
+ "sort"
8
+
9
+ "github.com/devrites/devrites/internal/state"
10
+ )
11
+
12
+ // WithControlLock serializes controller admission and control-tip mutations.
13
+ // devritesRoot is explicit; callers take this lock before any feature lock.
14
+ func WithControlLock(devritesRoot string, fn func() error) error {
15
+ devritesRoot = filepath.Clean(devritesRoot)
16
+ if err := validateControlRoot(devritesRoot); err != nil {
17
+ return err
18
+ }
19
+ dir := devritesRoot
20
+ root := filepath.Dir(devritesRoot)
21
+ common, err := commonGitDir(root)
22
+ if err != nil {
23
+ return err
24
+ }
25
+ if common != "" {
26
+ dir = common
27
+ }
28
+ if err := os.MkdirAll(dir, 0o755); err != nil {
29
+ return err
30
+ }
31
+ return state.WithLock(filepath.Join(dir, "parallel.lock"), fn)
32
+ }
33
+
34
+ func commonGitDir(root string) (string, error) {
35
+ common, err := git(root, "rev-parse", "--git-common-dir")
36
+ if err == nil {
37
+ if !filepath.IsAbs(common) {
38
+ common = filepath.Join(root, common)
39
+ }
40
+ return resolvedPath(common), nil
41
+ }
42
+ dir, absErr := filepath.Abs(root)
43
+ if absErr != nil {
44
+ return "", absErr
45
+ }
46
+ for {
47
+ if _, statErr := os.Lstat(filepath.Join(dir, ".git")); !os.IsNotExist(statErr) {
48
+ return "", fmt.Errorf("parallel repository discovery: %w", err)
49
+ }
50
+ parent := filepath.Dir(dir)
51
+ if parent == dir {
52
+ return "", nil
53
+ }
54
+ dir = parent
55
+ }
56
+ }
57
+
58
+ func validateControlRoot(root string) error {
59
+ info, err := os.Lstat(root)
60
+ if os.IsNotExist(err) {
61
+ return nil
62
+ }
63
+ if err != nil {
64
+ return err
65
+ }
66
+ if !info.IsDir() {
67
+ return fmt.Errorf("parallel control root is not a safe directory: %s", root)
68
+ }
69
+ return nil
70
+ }
71
+
72
+ type repositoryLease struct {
73
+ root string
74
+ controller string
75
+ lease *Lease
76
+ }
77
+
78
+ // controlLeases reads explicit paths, never the ambient workspace override.
79
+ func controlLeases(devritesRoot string) ([]repositoryLease, error) {
80
+ devritesRoot = filepath.Clean(devritesRoot)
81
+ if err := validateControlRoot(devritesRoot); err != nil {
82
+ return nil, err
83
+ }
84
+ root := filepath.Dir(devritesRoot)
85
+ roots := map[string]bool{resolvedPath(devritesRoot): true}
86
+ common, err := commonGitDir(root)
87
+ if err != nil {
88
+ return nil, err
89
+ }
90
+ if common != "" {
91
+ registered, err := registeredWorktrees(root)
92
+ if err != nil {
93
+ return nil, err
94
+ }
95
+ for path := range registered {
96
+ roots[filepath.Join(path, ".devrites")] = true
97
+ }
98
+ }
99
+ ordered := make([]string, 0, len(roots))
100
+ for path := range roots {
101
+ ordered = append(ordered, path)
102
+ }
103
+ sort.Strings(ordered)
104
+ var leases []repositoryLease
105
+ for _, control := range ordered {
106
+ work := filepath.Join(control, "work")
107
+ for _, path := range []string{control, work} {
108
+ if info, err := os.Lstat(path); err == nil && info.Mode()&os.ModeSymlink != 0 {
109
+ return nil, fmt.Errorf("parallel workspace path is a symlink: %s", path)
110
+ } else if err != nil && !os.IsNotExist(err) {
111
+ return nil, err
112
+ }
113
+ }
114
+ entries, err := os.ReadDir(work)
115
+ if os.IsNotExist(err) {
116
+ continue
117
+ }
118
+ if err != nil {
119
+ return nil, err
120
+ }
121
+ for _, entry := range entries {
122
+ if !entry.IsDir() && entry.Type()&os.ModeSymlink == 0 {
123
+ continue
124
+ }
125
+ path := filepath.Join(work, entry.Name(), "parallel-lease.md")
126
+ info, err := os.Lstat(path)
127
+ if os.IsNotExist(err) {
128
+ continue
129
+ }
130
+ if err != nil {
131
+ return nil, err
132
+ }
133
+ if err := validateSlug(entry.Name()); err != nil {
134
+ return nil, err
135
+ }
136
+ if entry.Type()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {
137
+ return nil, fmt.Errorf("parallel lease path is not an owned regular file: %s", path)
138
+ }
139
+ lease, err := ReadLease(path)
140
+ if err != nil {
141
+ return nil, fmt.Errorf("controller %s: %w", entry.Name(), err)
142
+ }
143
+ if err := normalizeLease(lease, entry.Name()); err != nil {
144
+ return nil, err
145
+ }
146
+ leases = append(leases, repositoryLease{filepath.Dir(control), entry.Name(), lease})
147
+ }
148
+ }
149
+ return leases, nil
150
+ }
151
+
152
+ func controllerLease(root, controller string) (*repositoryLease, error) {
153
+ leases, err := controlLeases(filepath.Join(root, ".devrites"))
154
+ if err != nil {
155
+ return nil, err
156
+ }
157
+ if len(leases) > 1 {
158
+ return nil, fmt.Errorf("multiple retained repository controller leases; cleanup explicitly before admission")
159
+ }
160
+ if len(leases) == 0 {
161
+ return nil, nil
162
+ }
163
+ found := &leases[0]
164
+ if controller != "" && (found.controller != controller || found.root != resolvedPath(root)) {
165
+ return nil, fmt.Errorf("repository controller lease retained by %s at %s; use its explicit root and --slug for mutations", found.controller, found.root)
166
+ }
167
+ return found, nil
168
+ }
169
+
170
+ // WorkspaceLeased protects both controller and source workspaces until cleanup.
171
+ // devritesRoot is explicit; closeout holds WithControlLock through its rename.
172
+ func WorkspaceLeased(devritesRoot, workspace string) (bool, error) {
173
+ if err := validateSlug(workspace); err != nil {
174
+ return false, err
175
+ }
176
+ leases, err := controlLeases(devritesRoot)
177
+ if err != nil {
178
+ return false, err
179
+ }
180
+ for _, found := range leases {
181
+ if found.controller == workspace {
182
+ return true, nil
183
+ }
184
+ for _, sl := range found.lease.Slices {
185
+ if sl.Workspace == workspace {
186
+ return true, nil
187
+ }
188
+ }
189
+ }
190
+ return false, nil
191
+ }
192
+
193
+ func validateSources(devritesRoot string, slices []SlicePaths) error {
194
+ seen := make(map[string]bool)
195
+ for _, sp := range slices {
196
+ if sp.Workspace == "" || seen[sp.Workspace] {
197
+ continue
198
+ }
199
+ seen[sp.Workspace] = true
200
+ if err := validateSource(devritesRoot, sp.Workspace); err != nil {
201
+ return err
202
+ }
203
+ }
204
+ return nil
205
+ }
206
+
207
+ func validateSource(devritesRoot, workspace string) error {
208
+ if workspace == "" {
209
+ return nil
210
+ }
211
+ path := devritesRoot
212
+ for index, component := range []string{"", "work", workspace, state.LedgerFile} {
213
+ if component != "" {
214
+ path = filepath.Join(path, component)
215
+ }
216
+ info, err := os.Lstat(path)
217
+ if err != nil {
218
+ return fmt.Errorf("source workspace %s is not live: %w", workspace, err)
219
+ }
220
+ if (index == 3 && !info.Mode().IsRegular()) || (index != 3 && !info.IsDir()) {
221
+ return fmt.Errorf("source workspace %s has an unsafe live path: %s", workspace, path)
222
+ }
223
+ }
224
+ return nil
225
+ }
226
+
227
+ func validateLeaseSources(devritesRoot string, lease *Lease) error {
228
+ seen := make(map[string]bool)
229
+ for _, sl := range lease.Slices {
230
+ if seen[sl.Workspace] {
231
+ continue
232
+ }
233
+ if err := validateSource(devritesRoot, sl.Workspace); err != nil {
234
+ return err
235
+ }
236
+ seen[sl.Workspace] = true
237
+ }
238
+ return nil
239
+ }
@@ -23,39 +23,25 @@ func SelectGreedySeeded(cap int, seed, ready []SlicePaths, root string) ([]Slice
23
23
  if cap < 1 || cap > MaxParallelSlices {
24
24
  return nil, fmt.Errorf("cap must be 1-%d (got %d)", MaxParallelSlices, cap)
25
25
  }
26
- normalized := make([]SlicePaths, 0, len(ready))
27
- seen := make(map[string]struct{}, len(ready))
28
- for i, item := range ready {
29
- label := fmt.Sprintf("slice %d", i)
30
- if item.ID != "" {
31
- if err := validateSliceID(item.ID); err != nil {
32
- return nil, fmt.Errorf("%s: %w", label, err)
33
- }
34
- label = fmt.Sprintf("slice %q", item.ID)
35
- } else {
36
- return nil, fmt.Errorf("%s: missing id", label)
37
- }
38
- if _, ok := seen[item.ID]; ok {
39
- return nil, fmt.Errorf("duplicate slice id %q", item.ID)
40
- }
41
- seen[item.ID] = struct{}{}
42
- paths, err := validateSlicePaths(item.Paths, label, root)
43
- if err != nil {
44
- return nil, err
45
- }
46
- normalized = append(normalized, SlicePaths{ID: item.ID, Paths: paths})
26
+ normalized, err := normalizeSlices("", ready, root)
27
+ if err != nil {
28
+ return nil, err
29
+ }
30
+ seed, err = normalizeSlices("", seed, root)
31
+ if err != nil {
32
+ return nil, err
47
33
  }
48
34
 
49
35
  running := make(map[string]bool, len(seed))
50
36
  for _, s := range seed {
51
- running[s.ID] = true
37
+ running[SliceKey(s.Workspace, s.ID)] = true
52
38
  }
53
39
  selected := append([]SlicePaths{}, seed...)
54
40
  for _, cand := range normalized {
55
41
  if len(selected) >= cap {
56
42
  break
57
43
  }
58
- if running[cand.ID] {
44
+ if running[SliceKey(cand.Workspace, cand.ID)] {
59
45
  continue
60
46
  }
61
47
  if len(selected) == 0 {
@@ -79,32 +65,68 @@ func SelectGreedySeeded(cap int, seed, ready []SlicePaths, root string) ([]Slice
79
65
  // imports this package.
80
66
  var PlanReady func(root, slug string) ([]SlicePaths, error)
81
67
 
82
- // SelectForSlug is SelectGreedySeeded seeded from slug's running lease: its
83
- // active slices keep their slots and paths, and nothing it names (active or
84
- // landed) is picked again. eligible is ready minus those names; running is -1
85
- // when no lease seeded the selection. An empty slug skips the lease.
68
+ // WorkspaceReady supplies native-authorized candidates and per-workspace notices.
69
+ // main wires this hook from lib, which owns planning and dependency readiness.
70
+ var WorkspaceReady func(root, controller, target string) ([]SlicePaths, []string, error)
71
+
72
+ // SelectForSlug seeds every retained repository slot, regardless of source.
73
+ // Bare candidates belong to slug; returned foreign identities stay qualified
74
+ // relative to the retained controller. An omitted slug means controller-local.
86
75
  func SelectForSlug(root, slug string, capN int, ready []SlicePaths) (eligible, picks []SlicePaths, running int, err error) {
87
- eligible, running = ready, -1
88
- var seed []SlicePaths
76
+ if capN < 1 || capN > MaxParallelSlices {
77
+ return nil, nil, -1, fmt.Errorf("cap must be 1-%d (got %d)", MaxParallelSlices, capN)
78
+ }
89
79
  if slug != "" {
90
- lease, err := runningLease(root, slug)
80
+ if err := validateSlug(slug); err != nil {
81
+ return nil, nil, -1, err
82
+ }
83
+ }
84
+ running = -1
85
+ var seed []SlicePaths
86
+ controller := slug
87
+ var found *repositoryLease
88
+ if root != "" || slug != "" {
89
+ found, err = controllerLease(root, "")
91
90
  if err != nil {
92
91
  return nil, nil, -1, err
93
92
  }
94
- if lease != nil {
95
- seed = activeSlices(lease)
96
- running = len(seed)
97
- named := make(map[string]bool, len(lease.Slices))
98
- for _, sl := range lease.Slices {
99
- named[sl.ID] = true
100
- }
101
- eligible = nil
102
- for _, s := range ready {
103
- if !named[s.ID] {
104
- eligible = append(eligible, s)
93
+ }
94
+ candidates := make([]SlicePaths, len(ready))
95
+ copy(candidates, ready)
96
+ if found != nil {
97
+ if found.lease.PendingLanding != nil {
98
+ return nil, nil, -1, fmt.Errorf("pending landing blocks admission; recover controller %s at %s with integrate --apply-to-control or abort", found.controller, found.root)
99
+ }
100
+ controller = found.controller
101
+ if slug != "" && slug != controller {
102
+ for i := range candidates {
103
+ if candidates[i].Workspace == "" {
104
+ candidates[i].Workspace = slug
105
105
  }
106
106
  }
107
107
  }
108
+ seed = activeSlices(found.lease)
109
+ running = len(seed)
110
+ if capacity := leaseCapacity(found.lease); capN > capacity {
111
+ capN = capacity
112
+ }
113
+ }
114
+ eligible, err = normalizeSlices(controller, candidates, root)
115
+ if err != nil {
116
+ return nil, nil, -1, err
117
+ }
118
+ if found != nil {
119
+ named := make(map[string]bool, len(found.lease.Slices))
120
+ for _, sl := range found.lease.Slices {
121
+ named[SliceKey(sl.Workspace, sl.ID)] = true
122
+ }
123
+ filtered := eligible[:0]
124
+ for _, sp := range eligible {
125
+ if !named[SliceKey(sp.Workspace, sp.ID)] {
126
+ filtered = append(filtered, sp)
127
+ }
128
+ }
129
+ eligible = filtered
108
130
  }
109
131
  picks, err = SelectGreedySeeded(capN, seed, eligible, root)
110
132
  return eligible, picks, running, err
@@ -127,7 +149,7 @@ func selectMode(n int) string {
127
149
  func formatSelect(picks []SlicePaths, running int) string {
128
150
  ids := make([]string, len(picks))
129
151
  for i, item := range picks {
130
- ids[i] = item.ID
152
+ ids[i] = SliceKey(item.Workspace, item.ID)
131
153
  }
132
154
  mode := selectMode(len(picks))
133
155
  if running > 0 && len(picks) > 0 {
@@ -244,11 +244,40 @@ func ObserveWorkspace(root, slug string) (*WorkspaceObservation, error) {
244
244
  return observeWorkspace(root, slug, nil)
245
245
  }
246
246
 
247
+ // ObserveReferencedWorkspace acquires a read-only planning source, live first
248
+ // and archived only when its canonical live directory is absent. A different
249
+ // valid active-workspace override never redirects this explicit source.
250
+ func ObserveReferencedWorkspace(root, slug string) (*WorkspaceObservation, error) {
251
+ return observeWorkspaceSource(root, slug, true, nil)
252
+ }
253
+
247
254
  func observeWorkspace(root, slug string, callback observationCallback) (*WorkspaceObservation, error) {
248
- if !observationWorkspaceOverrideValid(root, slug) {
255
+ return observeWorkspaceSource(root, slug, false, callback)
256
+ }
257
+
258
+ func observeWorkspaceSource(root, slug string, allowArchive bool, callback observationCallback) (*WorkspaceObservation, error) {
259
+ override := os.Getenv("DEVRITES_WORKSPACE")
260
+ overrideSlug := slug
261
+ if allowArchive && override != "" {
262
+ overrideSlug = filepath.Base(filepath.Clean(override))
263
+ }
264
+ if !observationWorkspaceOverrideValid(root, overrideSlug) {
249
265
  return nil, ObservationWorkspaceInvalid
250
266
  }
251
- workspacePath, err := devritespaths.ExistingFeatureDirChecked(root, slug)
267
+ workspacePath, err := devritespaths.ExistingFeatureDirChecked(root, overrideSlug)
268
+ canArchive := allowArchive && override == ""
269
+ if overrideSlug != slug {
270
+ if err != nil {
271
+ return nil, ObservationWorkspaceInvalid
272
+ }
273
+ workspacePath, err = devritespaths.ExistingCanonicalFeatureDirChecked(root, slug)
274
+ canArchive = allowArchive
275
+ }
276
+ archived := false
277
+ if err != nil && canArchive && errors.Is(err, fs.ErrNotExist) {
278
+ workspacePath, err = devritespaths.ExistingArchivedFeatureDirChecked(root, slug)
279
+ archived = err == nil
280
+ }
252
281
  if err != nil {
253
282
  return nil, ObservationWorkspaceInvalid
254
283
  }
@@ -270,6 +299,9 @@ func observeWorkspace(root, slug string, callback observationCallback) (*Workspa
270
299
  }
271
300
  defer func() { _ = roots.devrites.Close() }()
272
301
  defer func() { _ = roots.workspace.Close() }()
302
+ if archived && !liveWorkspaceAbsent(roots.devrites, slug) {
303
+ return nil, ObservationWorkspaceInvalid
304
+ }
273
305
 
274
306
  inventory := observationInventory()
275
307
  facts := make([]ArtifactFact, 0, len(inventory))
@@ -315,9 +347,24 @@ func observeWorkspace(root, slug string, callback observationCallback) (*Workspa
315
347
  if err != nil {
316
348
  return nil, ObservationWorkspaceInvalid
317
349
  }
350
+ if archived && !liveWorkspaceAbsent(roots.devrites, slug) {
351
+ return nil, ObservationConcurrentChange
352
+ }
318
353
  return &WorkspaceObservation{slug: slug, facts: facts, index: index, rootFiles: rootFiles}, nil
319
354
  }
320
355
 
356
+ func liveWorkspaceAbsent(root *os.Root, slug string) bool {
357
+ work := inspectEntry(root, "work")
358
+ if work.state == entryAbsent {
359
+ return true
360
+ }
361
+ if work.state != entryPresent || work.info.Mode()&os.ModeSymlink != 0 || !work.info.IsDir() {
362
+ return false
363
+ }
364
+ workspace := inspectEntry(root, filepath.Join("work", slug))
365
+ return workspace.state == entryAbsent && sameObservedEntry(work, inspectEntry(root, "work"))
366
+ }
367
+
321
368
  // readRootFiles lists the workspace directory's regular entries through the
322
369
  // already-verified root handle, so the list describes the same snapshot as the
323
370
  // artifact facts.
package/engine/main.go CHANGED
@@ -74,7 +74,7 @@ Usage:
74
74
  devrites-engine check indexes [--root <dir>] Report manifest and code-index presence as JSON
75
75
  devrites-engine detect commands [--root <dir>] [--json]
76
76
  Resolve test/lint/vet/build commands from the repository's own wiring
77
- devrites-engine parallel <subcommand> Deterministic parallel worktree lease/create/integrate/cleanup
77
+ devrites-engine parallel <subcommand> Deterministic parallel worktree lease/create/integrate/prune/cleanup
78
78
  devrites-engine gates <subcommand> <slug> Machine-checked gates.md ledger: scaffold/status/run/reverify/lint/precheck/attest/abandon
79
79
  devrites-engine state resolve <qid> "<ans>" Resolve an open question and update state atomically
80
80
  devrites-engine state merge-manifest <slug> [pred...] Fold the recorded predecessor chain's manifests into the release candidate manifest
@@ -125,6 +125,7 @@ func init() {
125
125
  }
126
126
  // parallel cannot import lib (lib imports it), so `parallel select --from-plan` gets the plan-derived ready set through this hook.
127
127
  parallel.PlanReady = lib.PlanReady
128
+ parallel.WorkspaceReady = lib.WorkspaceReady
128
129
  // `gates precheck` runs `check windows` through this hook for the same reason.
129
130
  acceptance.WindowsCheck = lib.RunCheckWindows
130
131
  // ...and `gates precheck --slice` learns which writer gates to skip.