dorfl 0.0.0 → 0.1.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 (619) hide show
  1. package/dist/advance-ci-template.d.ts +73 -0
  2. package/dist/advance-ci-template.d.ts.map +1 -0
  3. package/dist/advance-ci-template.js +104 -0
  4. package/dist/advance-ci-template.js.map +1 -0
  5. package/dist/advance-classify.d.ts +132 -0
  6. package/dist/advance-classify.d.ts.map +1 -0
  7. package/dist/advance-classify.js +120 -0
  8. package/dist/advance-classify.js.map +1 -0
  9. package/dist/advance-drivers.d.ts +182 -0
  10. package/dist/advance-drivers.d.ts.map +1 -0
  11. package/dist/advance-drivers.js +231 -0
  12. package/dist/advance-drivers.js.map +1 -0
  13. package/dist/advance-isolated.d.ts +156 -0
  14. package/dist/advance-isolated.d.ts.map +1 -0
  15. package/dist/advance-isolated.js +256 -0
  16. package/dist/advance-isolated.js.map +1 -0
  17. package/dist/advance-lifecycle-template.d.ts +107 -0
  18. package/dist/advance-lifecycle-template.d.ts.map +1 -0
  19. package/dist/advance-lifecycle-template.js +668 -0
  20. package/dist/advance-lifecycle-template.js.map +1 -0
  21. package/dist/advance-loop-driver.d.ts +325 -0
  22. package/dist/advance-loop-driver.d.ts.map +1 -0
  23. package/dist/advance-loop-driver.js +437 -0
  24. package/dist/advance-loop-driver.js.map +1 -0
  25. package/dist/advance-treeless-publish.d.ts +108 -0
  26. package/dist/advance-treeless-publish.d.ts.map +1 -0
  27. package/dist/advance-treeless-publish.js +71 -0
  28. package/dist/advance-treeless-publish.js.map +1 -0
  29. package/dist/advance.d.ts +340 -0
  30. package/dist/advance.d.ts.map +1 -0
  31. package/dist/advance.js +1122 -0
  32. package/dist/advance.js.map +1 -0
  33. package/dist/advancing-lock.d.ts +294 -0
  34. package/dist/advancing-lock.d.ts.map +1 -0
  35. package/dist/advancing-lock.js +594 -0
  36. package/dist/advancing-lock.js.map +1 -0
  37. package/dist/agent-launch.d.ts +79 -0
  38. package/dist/agent-launch.d.ts.map +1 -0
  39. package/dist/agent-launch.js +61 -0
  40. package/dist/agent-launch.js.map +1 -0
  41. package/dist/agent-stop.d.ts +149 -0
  42. package/dist/agent-stop.d.ts.map +1 -0
  43. package/dist/agent-stop.js +307 -0
  44. package/dist/agent-stop.js.map +1 -0
  45. package/dist/apply-decide.d.ts +127 -0
  46. package/dist/apply-decide.d.ts.map +1 -0
  47. package/dist/apply-decide.js +176 -0
  48. package/dist/apply-decide.js.map +1 -0
  49. package/dist/apply-merge-action.d.ts +206 -0
  50. package/dist/apply-merge-action.d.ts.map +1 -0
  51. package/dist/apply-merge-action.js +307 -0
  52. package/dist/apply-merge-action.js.map +1 -0
  53. package/dist/apply-persist.d.ts +174 -0
  54. package/dist/apply-persist.d.ts.map +1 -0
  55. package/dist/apply-persist.js +359 -0
  56. package/dist/apply-persist.js.map +1 -0
  57. package/dist/arbiter.d.ts +120 -0
  58. package/dist/arbiter.d.ts.map +1 -0
  59. package/dist/arbiter.js +255 -0
  60. package/dist/arbiter.js.map +1 -0
  61. package/dist/brand.d.ts +70 -0
  62. package/dist/brand.d.ts.map +1 -0
  63. package/dist/brand.js +84 -0
  64. package/dist/brand.js.map +1 -0
  65. package/dist/buildable-body.d.ts +132 -0
  66. package/dist/buildable-body.d.ts.map +1 -0
  67. package/dist/buildable-body.js +131 -0
  68. package/dist/buildable-body.js.map +1 -0
  69. package/dist/categorise.d.ts +66 -0
  70. package/dist/categorise.d.ts.map +1 -0
  71. package/dist/categorise.js +106 -0
  72. package/dist/categorise.js.map +1 -0
  73. package/dist/claim-cas.d.ts +117 -0
  74. package/dist/claim-cas.d.ts.map +1 -0
  75. package/dist/claim-cas.js +312 -0
  76. package/dist/claim-cas.js.map +1 -0
  77. package/dist/cli-spinner.d.ts +112 -0
  78. package/dist/cli-spinner.d.ts.map +1 -0
  79. package/dist/cli-spinner.js +157 -0
  80. package/dist/cli-spinner.js.map +1 -0
  81. package/dist/cli.d.ts +11 -0
  82. package/dist/cli.d.ts.map +1 -0
  83. package/dist/cli.js +3100 -0
  84. package/dist/cli.js.map +1 -0
  85. package/dist/close-job-template.d.ts +70 -0
  86. package/dist/close-job-template.d.ts.map +1 -0
  87. package/dist/close-job-template.js +180 -0
  88. package/dist/close-job-template.js.map +1 -0
  89. package/dist/close-job.d.ts +95 -0
  90. package/dist/close-job.d.ts.map +1 -0
  91. package/dist/close-job.js +226 -0
  92. package/dist/close-job.js.map +1 -0
  93. package/dist/complete.d.ts +361 -0
  94. package/dist/complete.d.ts.map +1 -0
  95. package/dist/complete.js +885 -0
  96. package/dist/complete.js.map +1 -0
  97. package/dist/concurrency.d.ts +68 -0
  98. package/dist/concurrency.d.ts.map +1 -0
  99. package/dist/concurrency.js +112 -0
  100. package/dist/concurrency.js.map +1 -0
  101. package/dist/config-override.d.ts +76 -0
  102. package/dist/config-override.d.ts.map +1 -0
  103. package/dist/config-override.js +50 -0
  104. package/dist/config-override.js.map +1 -0
  105. package/dist/config.d.ts +668 -0
  106. package/dist/config.d.ts.map +1 -0
  107. package/dist/config.js +241 -0
  108. package/dist/config.js.map +1 -0
  109. package/dist/continue-branch.d.ts +249 -0
  110. package/dist/continue-branch.d.ts.map +1 -0
  111. package/dist/continue-branch.js +389 -0
  112. package/dist/continue-branch.js.map +1 -0
  113. package/dist/cwd-section.d.ts +186 -0
  114. package/dist/cwd-section.d.ts.map +1 -0
  115. package/dist/cwd-section.js +209 -0
  116. package/dist/cwd-section.js.map +1 -0
  117. package/dist/decision-engine.d.ts +170 -0
  118. package/dist/decision-engine.d.ts.map +1 -0
  119. package/dist/decision-engine.js +136 -0
  120. package/dist/decision-engine.js.map +1 -0
  121. package/dist/detect.d.ts +17 -0
  122. package/dist/detect.d.ts.map +1 -0
  123. package/dist/detect.js +118 -0
  124. package/dist/detect.js.map +1 -0
  125. package/dist/do-autopick.d.ts +85 -0
  126. package/dist/do-autopick.d.ts.map +1 -0
  127. package/dist/do-autopick.js +112 -0
  128. package/dist/do-autopick.js.map +1 -0
  129. package/dist/do-config.d.ts +312 -0
  130. package/dist/do-config.d.ts.map +1 -0
  131. package/dist/do-config.js +358 -0
  132. package/dist/do-config.js.map +1 -0
  133. package/dist/do-remote-auto.d.ts +75 -0
  134. package/dist/do-remote-auto.d.ts.map +1 -0
  135. package/dist/do-remote-auto.js +111 -0
  136. package/dist/do-remote-auto.js.map +1 -0
  137. package/dist/do.d.ts +621 -0
  138. package/dist/do.d.ts.map +1 -0
  139. package/dist/do.js +1882 -0
  140. package/dist/do.js.map +1 -0
  141. package/dist/drop-source.d.ts +96 -0
  142. package/dist/drop-source.d.ts.map +1 -0
  143. package/dist/drop-source.js +91 -0
  144. package/dist/drop-source.js.map +1 -0
  145. package/dist/eligibility.d.ts +46 -0
  146. package/dist/eligibility.d.ts.map +1 -0
  147. package/dist/eligibility.js +34 -0
  148. package/dist/eligibility.js.map +1 -0
  149. package/dist/env-config.d.ts +51 -0
  150. package/dist/env-config.d.ts.map +1 -0
  151. package/dist/env-config.js +272 -0
  152. package/dist/env-config.js.map +1 -0
  153. package/dist/failure-cause.d.ts +70 -0
  154. package/dist/failure-cause.d.ts.map +1 -0
  155. package/dist/failure-cause.js +126 -0
  156. package/dist/failure-cause.js.map +1 -0
  157. package/dist/format.d.ts +43 -0
  158. package/dist/format.d.ts.map +1 -0
  159. package/dist/format.js +256 -0
  160. package/dist/format.js.map +1 -0
  161. package/dist/frontmatter.d.ts +208 -0
  162. package/dist/frontmatter.d.ts.map +1 -0
  163. package/dist/frontmatter.js +344 -0
  164. package/dist/frontmatter.js.map +1 -0
  165. package/dist/gate-readiness.d.ts +84 -0
  166. package/dist/gate-readiness.d.ts.map +1 -0
  167. package/dist/gate-readiness.js +103 -0
  168. package/dist/gate-readiness.js.map +1 -0
  169. package/dist/gc.d.ts +165 -0
  170. package/dist/gc.d.ts.map +1 -0
  171. package/dist/gc.js +313 -0
  172. package/dist/gc.js.map +1 -0
  173. package/dist/gh-failure.d.ts +42 -0
  174. package/dist/gh-failure.d.ts.map +1 -0
  175. package/dist/gh-failure.js +49 -0
  176. package/dist/gh-failure.js.map +1 -0
  177. package/dist/git.d.ts +75 -0
  178. package/dist/git.d.ts.map +1 -0
  179. package/dist/git.js +130 -0
  180. package/dist/git.js.map +1 -0
  181. package/dist/github.d.ts +187 -0
  182. package/dist/github.d.ts.map +1 -0
  183. package/dist/github.js +343 -0
  184. package/dist/github.js.map +1 -0
  185. package/dist/harness.d.ts +242 -0
  186. package/dist/harness.d.ts.map +1 -0
  187. package/dist/harness.js +157 -0
  188. package/dist/harness.js.map +1 -0
  189. package/dist/identity.d.ts +167 -0
  190. package/dist/identity.d.ts.map +1 -0
  191. package/dist/identity.js +231 -0
  192. package/dist/identity.js.map +1 -0
  193. package/dist/index.d.ts +147 -0
  194. package/dist/index.d.ts.map +1 -0
  195. package/dist/index.js +76 -0
  196. package/dist/index.js.map +1 -0
  197. package/dist/install-ci-branch-protection.d.ts +147 -0
  198. package/dist/install-ci-branch-protection.d.ts.map +1 -0
  199. package/dist/install-ci-branch-protection.js +166 -0
  200. package/dist/install-ci-branch-protection.js.map +1 -0
  201. package/dist/install-ci-capabilities/advance-lifecycle.d.ts +15 -0
  202. package/dist/install-ci-capabilities/advance-lifecycle.d.ts.map +1 -0
  203. package/dist/install-ci-capabilities/advance-lifecycle.js +28 -0
  204. package/dist/install-ci-capabilities/advance-lifecycle.js.map +1 -0
  205. package/dist/install-ci-capabilities/close-job.d.ts +13 -0
  206. package/dist/install-ci-capabilities/close-job.d.ts.map +1 -0
  207. package/dist/install-ci-capabilities/close-job.js +26 -0
  208. package/dist/install-ci-capabilities/close-job.js.map +1 -0
  209. package/dist/install-ci-capabilities/example-noop.d.ts +16 -0
  210. package/dist/install-ci-capabilities/example-noop.d.ts.map +1 -0
  211. package/dist/install-ci-capabilities/example-noop.js +23 -0
  212. package/dist/install-ci-capabilities/example-noop.js.map +1 -0
  213. package/dist/install-ci-capabilities/intake.d.ts +15 -0
  214. package/dist/install-ci-capabilities/intake.d.ts.map +1 -0
  215. package/dist/install-ci-capabilities/intake.js +28 -0
  216. package/dist/install-ci-capabilities/intake.js.map +1 -0
  217. package/dist/install-ci-capabilities/verify.d.ts +14 -0
  218. package/dist/install-ci-capabilities/verify.d.ts.map +1 -0
  219. package/dist/install-ci-capabilities/verify.js +27 -0
  220. package/dist/install-ci-capabilities/verify.js.map +1 -0
  221. package/dist/install-ci-core.d.ts +446 -0
  222. package/dist/install-ci-core.d.ts.map +1 -0
  223. package/dist/install-ci-core.js +760 -0
  224. package/dist/install-ci-core.js.map +1 -0
  225. package/dist/install-ci-github.d.ts +167 -0
  226. package/dist/install-ci-github.d.ts.map +1 -0
  227. package/dist/install-ci-github.js +315 -0
  228. package/dist/install-ci-github.js.map +1 -0
  229. package/dist/install-ci.d.ts +105 -0
  230. package/dist/install-ci.d.ts.map +1 -0
  231. package/dist/install-ci.js +363 -0
  232. package/dist/install-ci.js.map +1 -0
  233. package/dist/intake-event.d.ts +88 -0
  234. package/dist/intake-event.d.ts.map +1 -0
  235. package/dist/intake-event.js +66 -0
  236. package/dist/intake-event.js.map +1 -0
  237. package/dist/intake-marker.d.ts +95 -0
  238. package/dist/intake-marker.d.ts.map +1 -0
  239. package/dist/intake-marker.js +127 -0
  240. package/dist/intake-marker.js.map +1 -0
  241. package/dist/intake-triage.d.ts +48 -0
  242. package/dist/intake-triage.d.ts.map +1 -0
  243. package/dist/intake-triage.js +95 -0
  244. package/dist/intake-triage.js.map +1 -0
  245. package/dist/intake-trigger-template.d.ts +185 -0
  246. package/dist/intake-trigger-template.d.ts.map +1 -0
  247. package/dist/intake-trigger-template.js +449 -0
  248. package/dist/intake-trigger-template.js.map +1 -0
  249. package/dist/intake.d.ts +569 -0
  250. package/dist/intake.d.ts.map +1 -0
  251. package/dist/intake.js +1628 -0
  252. package/dist/intake.js.map +1 -0
  253. package/dist/integration-core.d.ts +539 -0
  254. package/dist/integration-core.d.ts.map +1 -0
  255. package/dist/integration-core.js +2195 -0
  256. package/dist/integration-core.js.map +1 -0
  257. package/dist/integrator.d.ts +343 -0
  258. package/dist/integrator.d.ts.map +1 -0
  259. package/dist/integrator.js +400 -0
  260. package/dist/integrator.js.map +1 -0
  261. package/dist/isolation.d.ts +219 -0
  262. package/dist/isolation.d.ts.map +1 -0
  263. package/dist/isolation.js +261 -0
  264. package/dist/isolation.js.map +1 -0
  265. package/dist/issue-provider.d.ts +349 -0
  266. package/dist/issue-provider.d.ts.map +1 -0
  267. package/dist/issue-provider.js +360 -0
  268. package/dist/issue-provider.js.map +1 -0
  269. package/dist/item-lock.d.ts +626 -0
  270. package/dist/item-lock.d.ts.map +1 -0
  271. package/dist/item-lock.js +1381 -0
  272. package/dist/item-lock.js.map +1 -0
  273. package/dist/item-path.d.ts +49 -0
  274. package/dist/item-path.d.ts.map +1 -0
  275. package/dist/item-path.js +66 -0
  276. package/dist/item-path.js.map +1 -0
  277. package/dist/ledger-lint.d.ts +129 -0
  278. package/dist/ledger-lint.d.ts.map +1 -0
  279. package/dist/ledger-lint.js +249 -0
  280. package/dist/ledger-lint.js.map +1 -0
  281. package/dist/ledger-read.d.ts +357 -0
  282. package/dist/ledger-read.d.ts.map +1 -0
  283. package/dist/ledger-read.js +442 -0
  284. package/dist/ledger-read.js.map +1 -0
  285. package/dist/ledger-write.d.ts +330 -0
  286. package/dist/ledger-write.d.ts.map +1 -0
  287. package/dist/ledger-write.js +411 -0
  288. package/dist/ledger-write.js.map +1 -0
  289. package/dist/lifecycle-gather.d.ts +30 -0
  290. package/dist/lifecycle-gather.d.ts.map +1 -0
  291. package/dist/lifecycle-gather.js +205 -0
  292. package/dist/lifecycle-gather.js.map +1 -0
  293. package/dist/lifecycle-pools.d.ts +180 -0
  294. package/dist/lifecycle-pools.d.ts.map +1 -0
  295. package/dist/lifecycle-pools.js +78 -0
  296. package/dist/lifecycle-pools.js.map +1 -0
  297. package/dist/merge-question-surfacer.d.ts +166 -0
  298. package/dist/merge-question-surfacer.d.ts.map +1 -0
  299. package/dist/merge-question-surfacer.js +297 -0
  300. package/dist/merge-question-surfacer.js.map +1 -0
  301. package/dist/mint-adr.d.ts +126 -0
  302. package/dist/mint-adr.d.ts.map +1 -0
  303. package/dist/mint-adr.js +257 -0
  304. package/dist/mint-adr.js.map +1 -0
  305. package/dist/mirror-pool-scan.d.ts +125 -0
  306. package/dist/mirror-pool-scan.d.ts.map +1 -0
  307. package/dist/mirror-pool-scan.js +104 -0
  308. package/dist/mirror-pool-scan.js.map +1 -0
  309. package/dist/needs-attention.d.ts +341 -0
  310. package/dist/needs-attention.d.ts.map +1 -0
  311. package/dist/needs-attention.js +900 -0
  312. package/dist/needs-attention.js.map +1 -0
  313. package/dist/orphan-sidecar.d.ts +79 -0
  314. package/dist/orphan-sidecar.d.ts.map +1 -0
  315. package/dist/orphan-sidecar.js +71 -0
  316. package/dist/orphan-sidecar.js.map +1 -0
  317. package/dist/output.d.ts +48 -0
  318. package/dist/output.d.ts.map +1 -0
  319. package/dist/output.js +66 -0
  320. package/dist/output.js.map +1 -0
  321. package/dist/pi-harness.d.ts +179 -0
  322. package/dist/pi-harness.d.ts.map +1 -0
  323. package/dist/pi-harness.js +342 -0
  324. package/dist/pi-harness.js.map +1 -0
  325. package/dist/placement.d.ts +99 -0
  326. package/dist/placement.d.ts.map +1 -0
  327. package/dist/placement.js +67 -0
  328. package/dist/placement.js.map +1 -0
  329. package/dist/prd-to-spec.d.ts +329 -0
  330. package/dist/prd-to-spec.d.ts.map +1 -0
  331. package/dist/prd-to-spec.js +706 -0
  332. package/dist/prd-to-spec.js.map +1 -0
  333. package/dist/prepare.d.ts +121 -0
  334. package/dist/prepare.d.ts.map +1 -0
  335. package/dist/prepare.js +140 -0
  336. package/dist/prepare.js.map +1 -0
  337. package/dist/prompt.d.ts +363 -0
  338. package/dist/prompt.d.ts.map +1 -0
  339. package/dist/prompt.js +499 -0
  340. package/dist/prompt.js.map +1 -0
  341. package/dist/protocol/ADR-FORMAT.md +47 -0
  342. package/dist/protocol/CLAIM-PROTOCOL.md +217 -0
  343. package/dist/protocol/REVIEW-PROTOCOL.md +119 -0
  344. package/dist/protocol/SURFACE-PROTOCOL.md +121 -0
  345. package/dist/protocol/TASKING-PROTOCOL.md +122 -0
  346. package/dist/protocol/WORK-CONTRACT.md +276 -0
  347. package/dist/protocol/spec-template.md +71 -0
  348. package/dist/protocol/task-template.md +65 -0
  349. package/dist/readiness.d.ts +66 -0
  350. package/dist/readiness.d.ts.map +1 -0
  351. package/dist/readiness.js +36 -0
  352. package/dist/readiness.js.map +1 -0
  353. package/dist/reap-branches.d.ts +102 -0
  354. package/dist/reap-branches.d.ts.map +1 -0
  355. package/dist/reap-branches.js +149 -0
  356. package/dist/reap-branches.js.map +1 -0
  357. package/dist/recover-isolated.d.ts +72 -0
  358. package/dist/recover-isolated.d.ts.map +1 -0
  359. package/dist/recover-isolated.js +188 -0
  360. package/dist/recover-isolated.js.map +1 -0
  361. package/dist/registry.d.ts +172 -0
  362. package/dist/registry.d.ts.map +1 -0
  363. package/dist/registry.js +296 -0
  364. package/dist/registry.js.map +1 -0
  365. package/dist/repo-config.d.ts +201 -0
  366. package/dist/repo-config.d.ts.map +1 -0
  367. package/dist/repo-config.js +414 -0
  368. package/dist/repo-config.js.map +1 -0
  369. package/dist/repo-key.d.ts +20 -0
  370. package/dist/repo-key.d.ts.map +1 -0
  371. package/dist/repo-key.js +68 -0
  372. package/dist/repo-key.js.map +1 -0
  373. package/dist/repo-mirror.d.ts +177 -0
  374. package/dist/repo-mirror.d.ts.map +1 -0
  375. package/dist/repo-mirror.js +271 -0
  376. package/dist/repo-mirror.js.map +1 -0
  377. package/dist/retry-backoff.d.ts +90 -0
  378. package/dist/retry-backoff.d.ts.map +1 -0
  379. package/dist/retry-backoff.js +98 -0
  380. package/dist/retry-backoff.js.map +1 -0
  381. package/dist/review-gate.d.ts +173 -0
  382. package/dist/review-gate.d.ts.map +1 -0
  383. package/dist/review-gate.js +261 -0
  384. package/dist/review-gate.js.map +1 -0
  385. package/dist/review-verdict.d.ts +149 -0
  386. package/dist/review-verdict.d.ts.map +1 -0
  387. package/dist/review-verdict.js +332 -0
  388. package/dist/review-verdict.js.map +1 -0
  389. package/dist/run.d.ts +221 -0
  390. package/dist/run.d.ts.map +1 -0
  391. package/dist/run.js +963 -0
  392. package/dist/run.js.map +1 -0
  393. package/dist/scan.d.ts +308 -0
  394. package/dist/scan.d.ts.map +1 -0
  395. package/dist/scan.js +374 -0
  396. package/dist/scan.js.map +1 -0
  397. package/dist/select-order.d.ts +75 -0
  398. package/dist/select-order.d.ts.map +1 -0
  399. package/dist/select-order.js +108 -0
  400. package/dist/select-order.js.map +1 -0
  401. package/dist/select-priority.d.ts +188 -0
  402. package/dist/select-priority.d.ts.map +1 -0
  403. package/dist/select-priority.js +80 -0
  404. package/dist/select-priority.js.map +1 -0
  405. package/dist/select.d.ts +25 -0
  406. package/dist/select.d.ts.map +1 -0
  407. package/dist/select.js +43 -0
  408. package/dist/select.js.map +1 -0
  409. package/dist/session-path.d.ts +36 -0
  410. package/dist/session-path.d.ts.map +1 -0
  411. package/dist/session-path.js +129 -0
  412. package/dist/session-path.js.map +1 -0
  413. package/dist/sidecar-apply.d.ts +83 -0
  414. package/dist/sidecar-apply.d.ts.map +1 -0
  415. package/dist/sidecar-apply.js +111 -0
  416. package/dist/sidecar-apply.js.map +1 -0
  417. package/dist/sidecar.d.ts +245 -0
  418. package/dist/sidecar.d.ts.map +1 -0
  419. package/dist/sidecar.js +481 -0
  420. package/dist/sidecar.js.map +1 -0
  421. package/dist/slug-namespace.d.ts +204 -0
  422. package/dist/slug-namespace.d.ts.map +1 -0
  423. package/dist/slug-namespace.js +229 -0
  424. package/dist/slug-namespace.js.map +1 -0
  425. package/dist/spec-complete.d.ts +44 -0
  426. package/dist/spec-complete.d.ts.map +1 -0
  427. package/dist/spec-complete.js +69 -0
  428. package/dist/spec-complete.js.map +1 -0
  429. package/dist/start.d.ts +97 -0
  430. package/dist/start.d.ts.map +1 -0
  431. package/dist/start.js +633 -0
  432. package/dist/start.js.map +1 -0
  433. package/dist/status.d.ts +199 -0
  434. package/dist/status.d.ts.map +1 -0
  435. package/dist/status.js +228 -0
  436. package/dist/status.js.map +1 -0
  437. package/dist/surface-gate.d.ts +162 -0
  438. package/dist/surface-gate.d.ts.map +1 -0
  439. package/dist/surface-gate.js +206 -0
  440. package/dist/surface-gate.js.map +1 -0
  441. package/dist/surface-persist.d.ts +86 -0
  442. package/dist/surface-persist.d.ts.map +1 -0
  443. package/dist/surface-persist.js +129 -0
  444. package/dist/surface-persist.js.map +1 -0
  445. package/dist/tasker-review-loop.d.ts +249 -0
  446. package/dist/tasker-review-loop.d.ts.map +1 -0
  447. package/dist/tasker-review-loop.js +369 -0
  448. package/dist/tasker-review-loop.js.map +1 -0
  449. package/dist/tasking-eligibility.d.ts +74 -0
  450. package/dist/tasking-eligibility.d.ts.map +1 -0
  451. package/dist/tasking-eligibility.js +52 -0
  452. package/dist/tasking-eligibility.js.map +1 -0
  453. package/dist/tasking-lock.d.ts +111 -0
  454. package/dist/tasking-lock.d.ts.map +1 -0
  455. package/dist/tasking-lock.js +256 -0
  456. package/dist/tasking-lock.js.map +1 -0
  457. package/dist/tasking.d.ts +275 -0
  458. package/dist/tasking.d.ts.map +1 -0
  459. package/dist/tasking.js +951 -0
  460. package/dist/tasking.js.map +1 -0
  461. package/dist/triage-gate.d.ts +127 -0
  462. package/dist/triage-gate.d.ts.map +1 -0
  463. package/dist/triage-gate.js +139 -0
  464. package/dist/triage-gate.js.map +1 -0
  465. package/dist/triage-persist.d.ts +163 -0
  466. package/dist/triage-persist.d.ts.map +1 -0
  467. package/dist/triage-persist.js +387 -0
  468. package/dist/triage-persist.js.map +1 -0
  469. package/dist/verdict-json.d.ts +32 -0
  470. package/dist/verdict-json.d.ts.map +1 -0
  471. package/dist/verdict-json.js +74 -0
  472. package/dist/verdict-json.js.map +1 -0
  473. package/dist/verify-workflow-template.d.ts +60 -0
  474. package/dist/verify-workflow-template.d.ts.map +1 -0
  475. package/dist/verify-workflow-template.js +126 -0
  476. package/dist/verify-workflow-template.js.map +1 -0
  477. package/dist/verify.d.ts +60 -0
  478. package/dist/verify.d.ts.map +1 -0
  479. package/dist/verify.js +62 -0
  480. package/dist/verify.js.map +1 -0
  481. package/dist/watch-session.d.ts +112 -0
  482. package/dist/watch-session.d.ts.map +1 -0
  483. package/dist/watch-session.js +347 -0
  484. package/dist/watch-session.js.map +1 -0
  485. package/dist/work-layout.d.ts +198 -0
  486. package/dist/work-layout.d.ts.map +1 -0
  487. package/dist/work-layout.js +217 -0
  488. package/dist/work-layout.js.map +1 -0
  489. package/dist/work-on.d.ts +154 -0
  490. package/dist/work-on.d.ts.map +1 -0
  491. package/dist/work-on.js +387 -0
  492. package/dist/work-on.js.map +1 -0
  493. package/dist/workspace.d.ts +224 -0
  494. package/dist/workspace.d.ts.map +1 -0
  495. package/dist/workspace.js +325 -0
  496. package/dist/workspace.js.map +1 -0
  497. package/package.json +46 -2
  498. package/src/advance-ci-template.ts +203 -0
  499. package/src/advance-classify.ts +197 -0
  500. package/src/advance-drivers.ts +414 -0
  501. package/src/advance-isolated.ts +432 -0
  502. package/src/advance-lifecycle-template.ts +791 -0
  503. package/src/advance-loop-driver.ts +745 -0
  504. package/src/advance-treeless-publish.ts +177 -0
  505. package/src/advance.ts +1564 -0
  506. package/src/advancing-lock.ts +988 -0
  507. package/src/agent-launch.ts +137 -0
  508. package/src/agent-stop.ts +361 -0
  509. package/src/apply-decide.ts +242 -0
  510. package/src/apply-merge-action.ts +502 -0
  511. package/src/apply-persist.ts +518 -0
  512. package/src/arbiter.ts +372 -0
  513. package/src/brand.ts +111 -0
  514. package/src/buildable-body.ts +196 -0
  515. package/src/categorise.ts +158 -0
  516. package/src/claim-cas.ts +513 -0
  517. package/src/cli-spinner.ts +225 -0
  518. package/src/cli.ts +4379 -0
  519. package/src/close-job-template.ts +236 -0
  520. package/src/close-job.ts +319 -0
  521. package/src/complete.ts +1379 -0
  522. package/src/concurrency.ts +151 -0
  523. package/src/config-override.ts +116 -0
  524. package/src/config.ts +883 -0
  525. package/src/continue-branch.ts +542 -0
  526. package/src/cwd-section.ts +392 -0
  527. package/src/decision-engine.ts +272 -0
  528. package/src/detect.ts +124 -0
  529. package/src/do-autopick.ts +223 -0
  530. package/src/do-config.ts +589 -0
  531. package/src/do-remote-auto.ts +197 -0
  532. package/src/do.ts +2623 -0
  533. package/src/drop-source.ts +194 -0
  534. package/src/eligibility.ts +79 -0
  535. package/src/env-config.ts +305 -0
  536. package/src/failure-cause.ts +142 -0
  537. package/src/format.ts +313 -0
  538. package/src/frontmatter.ts +477 -0
  539. package/src/gate-readiness.ts +147 -0
  540. package/src/gc.ts +510 -0
  541. package/src/gh-failure.ts +53 -0
  542. package/src/git.ts +186 -0
  543. package/src/github.ts +468 -0
  544. package/src/harness.ts +355 -0
  545. package/src/identity.ts +322 -0
  546. package/src/index.ts +785 -0
  547. package/src/install-ci-branch-protection.ts +255 -0
  548. package/src/install-ci-capabilities/advance-lifecycle.ts +34 -0
  549. package/src/install-ci-capabilities/close-job.ts +32 -0
  550. package/src/install-ci-capabilities/example-noop.ts +24 -0
  551. package/src/install-ci-capabilities/intake.ts +34 -0
  552. package/src/install-ci-capabilities/verify.ts +33 -0
  553. package/src/install-ci-core.ts +1088 -0
  554. package/src/install-ci-github.ts +376 -0
  555. package/src/install-ci.ts +552 -0
  556. package/src/intake-event.ts +102 -0
  557. package/src/intake-marker.ts +195 -0
  558. package/src/intake-triage.ts +138 -0
  559. package/src/intake-trigger-template.ts +591 -0
  560. package/src/intake.ts +2445 -0
  561. package/src/integration-core.ts +3065 -0
  562. package/src/integrator.ts +771 -0
  563. package/src/isolation.ts +484 -0
  564. package/src/issue-provider.ts +733 -0
  565. package/src/item-lock.ts +1858 -0
  566. package/src/item-path.ts +75 -0
  567. package/src/ledger-lint.ts +332 -0
  568. package/src/ledger-read.ts +924 -0
  569. package/src/ledger-write.ts +865 -0
  570. package/src/lifecycle-gather.ts +298 -0
  571. package/src/lifecycle-pools.ts +250 -0
  572. package/src/merge-question-surfacer.ts +496 -0
  573. package/src/mint-adr.ts +362 -0
  574. package/src/mirror-pool-scan.ts +240 -0
  575. package/src/needs-attention.ts +1506 -0
  576. package/src/orphan-sidecar.ts +150 -0
  577. package/src/output.ts +89 -0
  578. package/src/pi-harness.ts +403 -0
  579. package/src/placement.ts +131 -0
  580. package/src/prd-to-spec.ts +1059 -0
  581. package/src/prepare.ts +230 -0
  582. package/src/prompt.ts +763 -0
  583. package/src/readiness.ts +98 -0
  584. package/src/reap-branches.ts +278 -0
  585. package/src/recover-isolated.ts +276 -0
  586. package/src/registry.ts +475 -0
  587. package/src/repo-config.ts +550 -0
  588. package/src/repo-key.ts +74 -0
  589. package/src/repo-mirror.ts +367 -0
  590. package/src/retry-backoff.ts +130 -0
  591. package/src/review-gate.ts +389 -0
  592. package/src/review-verdict.ts +422 -0
  593. package/src/run.ts +1430 -0
  594. package/src/scan.ts +611 -0
  595. package/src/select-order.ts +143 -0
  596. package/src/select-priority.ts +266 -0
  597. package/src/select.ts +62 -0
  598. package/src/session-path.ts +153 -0
  599. package/src/sidecar-apply.ts +216 -0
  600. package/src/sidecar.ts +700 -0
  601. package/src/slug-namespace.ts +367 -0
  602. package/src/spec-complete.ts +118 -0
  603. package/src/start.ts +974 -0
  604. package/src/status.ts +441 -0
  605. package/src/surface-gate.ts +337 -0
  606. package/src/surface-persist.ts +241 -0
  607. package/src/tasker-review-loop.ts +671 -0
  608. package/src/tasking-eligibility.ts +114 -0
  609. package/src/tasking-lock.ts +416 -0
  610. package/src/tasking.ts +1437 -0
  611. package/src/triage-gate.ts +248 -0
  612. package/src/triage-persist.ts +570 -0
  613. package/src/verdict-json.ts +73 -0
  614. package/src/verify-workflow-template.ts +159 -0
  615. package/src/verify.ts +123 -0
  616. package/src/watch-session.ts +397 -0
  617. package/src/work-layout.ts +262 -0
  618. package/src/work-on.ts +660 -0
  619. package/src/workspace.ts +502 -0
@@ -0,0 +1,900 @@
1
+ import { rmSync } from 'node:fs';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { workFolderPrefix, workFolderRel, workItemRel, isWorkItemFile, } from './work-layout.js';
5
+ import { run, runAsync } from './git.js';
6
+ import { branchAheadOf } from './continue-branch.js';
7
+ import { acquireItemLock, releaseItemLock, readItemLock, itemLockRef, lockEntryFor, parseLockEntry, } from './item-lock.js';
8
+ import { ledgerWrite } from './ledger-write.js';
9
+ import { workBranchRef } from './slug-namespace.js';
10
+ import { retryWithBackoff, realSleep, } from './retry-backoff.js';
11
+ /**
12
+ * The **needs-attention mechanism** (ADR `ledger-status-on-per-item-lock-refs`;
13
+ * prd `ledger-status-per-item-lock-refs`; ADR §12 for the original folder model).
14
+ * Every "couldn't finish, a human must look" outcome (a failed acceptance gate
15
+ * (red `verify`), a rebase/merge conflict (ADR §10), a task the agent reported
16
+ * too ambiguous to build, a timeout, or a rejected review) resolves to ONE
17
+ * observable move: the RUNNER AMENDS the claimed item's HELD per-item lock
18
+ * `active → stuck` (`refs/dorfl/lock/<entry>`), writing the reason (+ any
19
+ * agent-surfaced questions) into the lock-entry BODY. There is NO `git mv` to a
20
+ * `work/needs-attention/` folder and NO on-`main` surface (the lock cut-over,
21
+ * task `cutover-needs-attention-becomes-lock-stuck-recovery-surface`): so a
22
+ * protected-`main` bounce succeeds, and a work branch cut from `main` inherits no
23
+ * stuck record. The RECOVERABLE half is the kept `work/<slug>` branch.
24
+ *
25
+ * This is the conflict-safe form of "surfacing": the surface is the lock
26
+ * `state: stuck`, read by `scan`/`status`/`gc --ledger` reading the lock refs (a
27
+ * COMMAND a human runs, not an `ls` of a folder). There is **no status/label
28
+ * field** on `main` (honours WORK-CONTRACT rule 3). The reason is prose in the
29
+ * lock-entry body, never a source-of-truth frontmatter field.
30
+ *
31
+ * Ownership: this module OWNS the mechanism (the move helper + the surface
32
+ * reader + the return path). Consumers (`complete.ts`'s gate-failed/rebase-
33
+ * conflict abort paths, the runner's stuck routing in `run.ts`, the human
34
+ * `return` command) drive these through the ledger write seam's NEEDS-ATTENTION
35
+ * transition (`ledgerWrite.applyNeedsAttentionTransition` /
36
+ * `applyReturnToBacklogTransition` in `ledger-write.ts`), whose sole strategy
37
+ * delegates to `routeToNeedsAttention` / `returnToBacklog` here UNCHANGED — so
38
+ * the later cherry-pick-to-`main` surfacing is built AGAINST the seam, not
39
+ * bolted onto this move code. The build agent NEVER does this — agents do no git
40
+ * (ADR §12).
41
+ */
42
+ /** Marker that opens the appended reason block in a needs-attention item body. */
43
+ const REASON_HEADING = '## Needs attention';
44
+ /**
45
+ * Save the RECOVERABLE half of a stuck-item bounce (ADR §12). The RUNNER calls
46
+ * this; the build agent never does.
47
+ *
48
+ * **Post `ledger-status-per-item-lock-refs` cut-over (tasks 9a–9d, decision i+):**
49
+ * a bounce is now a PURE LOCK AMEND — the seam (`bounceToStuckLock` →
50
+ * `markStuckItemLock`) marks the per-item lock `state: stuck` and records the
51
+ * reason/questions ON THE LOCK ENTRY. There is NO `git mv` to a
52
+ * `needs-attention/` folder and NO on-`main` surface commit. The item body never
53
+ * moves (it rests in `backlog/` since claim stopped moving it, task 9a). So what
54
+ * REMAINS here is purely the never-lose-work half:
55
+ *
56
+ * 1. A **wip** commit on the `work/<slug>` branch tip holding whatever the agent
57
+ * left uncommitted (`git add -A`). Skipped when the tree is already clean
58
+ * (no aborted work to save). No bookkeeping trailer — after the cut-over NO
59
+ * transient-status move-only commit lands on a branch, so a branch rebases
60
+ * PLAINLY with nothing to drop (`drop-bookkeeping-rebase` is deleted, 9d).
61
+ * 2. Optionally PUSH the work branch to the arbiter so the saved wip travels
62
+ * cross-machine and a `requeue` continues from the branch tip. BEST-EFFORT
63
+ * (an unreachable arbiter leaves the local branch standing — recovery
64
+ * degrades, never crashes the bounce; retried with bounded backoff on an
65
+ * outage), BRANCH-PARAMETERISED (default `work/<slug>`; an explicit `branch`
66
+ * overrides — the tasking bounce passes `work/prds/ready-<slug>`; `pushBranch: false`
67
+ * ⇒ push NOTHING), and EMPTINESS-GUARDED (a branch with no commits beyond
68
+ * main, or an absent branch, is skipped). The branch MUST be the one HEAD is
69
+ * on. The work-branch push is NOT a `main` write.
70
+ *
71
+ * The stuck STATE itself (the `state: stuck` + reason/questions) is owned by the
72
+ * lock amend in the seam, not by this function. NEVER throws for the expected
73
+ * case — returns `{moved, ...}` so consumers can branch cleanly; genuine git
74
+ * plumbing failures still throw (they are unexpected).
75
+ */
76
+ export async function routeToNeedsAttention(options) {
77
+ const note = options.note ?? (() => { });
78
+ const { cwd, slug, env } = options;
79
+ // TASK `cutover-needs-attention-becomes-lock-stuck-recovery-surface`
80
+ // (decision i+): the bounce is now a PURE lock amend (done by the seam) — there
81
+ // is NO `git mv` to `needs-attention/` and NO on-`main` surface. What REMAINS
82
+ // here is the RECOVERABLE half: SAVE the agent's uncommitted work as a wip
83
+ // commit on the `work/<slug>` branch tip and PUSH the branch to the arbiter, so
84
+ // the partial work travels cross-machine and a `requeue` continues from the
85
+ // branch tip. The reason/questions ride on the lock entry (the seam amends it),
86
+ // NOT a moved `.md`. The work branch push is NOT a `main` write.
87
+ // 1. WIP commit: save whatever the agent left uncommitted to the work branch
88
+ // tip. Skip when the tree is clean (no aborted work to save). NOTE this no
89
+ // longer needs a folder source — the body rests in `backlog/` (task 9a) and
90
+ // never moves on a bounce.
91
+ gitHard(['add', '-A'], cwd, env);
92
+ const hadWip = !nothingStaged(cwd, env);
93
+ if (hadWip) {
94
+ // Save the agent's uncommitted work as a plain wip commit on the work-branch
95
+ // tip (the RECOVERABLE half — a `requeue` continues from it). No bookkeeping
96
+ // trailer: after the per-item-lock cut-over (tasks 9a–9d) NO transient status
97
+ // (no `needs-attention/` move-only commit) lands on a branch, so a branch cut
98
+ // from `main` rebases PLAINLY with nothing to drop — the `drop-bookkeeping-rebase`
99
+ // machinery and its `Dorfl-Bookkeeping` trailer are gone (9d).
100
+ const wipBody = `chore(${slug}): save aborted work (wip)`;
101
+ gitHard(['commit', '-q', '-m', wipBody], cwd, env);
102
+ }
103
+ const commitMessage = `chore(${slug}): bounce to stuck; ${options.reason}`;
104
+ const moveCommit = hadWip ? revParseHead(cwd, env) : undefined;
105
+ note(`Bounced '${slug}' to stuck (lock): ${options.reason}`);
106
+ // 2. Push the work branch to the arbiter — the RECOVERABLE half of the bounce
107
+ // (so the saved wip travels cross-machine and a requeue continues from the
108
+ // branch tip). Three behaviours: SURFACE-ONLY (no push) when
109
+ // `pushBranch === false`; an explicit `branch` target; else the default
110
+ // `work/<slug>`. BEST-EFFORT (no throw on a failed/unreachable push), RETRIED
111
+ // with bounded backoff on an OUTAGE, and EMPTINESS-GUARDED (a branch with no
112
+ // work beyond main / an absent branch is skipped). The OUTCOME is CAPTURED +
113
+ // RETURNED (`branchPush`) so the caller reports what ACTUALLY landed.
114
+ let branchPush = 'not-attempted';
115
+ let pushError;
116
+ if (options.arbiter && options.pushBranch !== false) {
117
+ // DEFAULT to the task-namespaced build-bounce branch; a non-task caller
118
+ // (the tasking bounce) passes its own `work/prds/ready-<slug>` via `branch`.
119
+ const branch = options.branch ?? workBranchRef('task', slug);
120
+ if (branchAheadOf(cwd, branch, 'main', env)) {
121
+ const arbiter = options.arbiter;
122
+ const result = await retryWithBackoff(async () => {
123
+ const r = gitSoftRun(['push', arbiter, `${branch}:${branch}`], cwd, env);
124
+ return r.status === 0
125
+ ? { ok: true, value: undefined }
126
+ : { ok: false, error: r.stderr.trim() };
127
+ }, { sleep: options.sleep ?? realSleep, ...options.backoff });
128
+ if (result.ok) {
129
+ branchPush = 'pushed';
130
+ }
131
+ else {
132
+ branchPush = 'failed';
133
+ pushError = result.lastError;
134
+ note(`Could not push ${branch} to ${arbiter} after ${result.attempts} ` +
135
+ `attempt(s) (${pushError ?? 'unknown error'}) — the work is saved ` +
136
+ 'LOCALLY only; push the branch when online, then `requeue`.');
137
+ }
138
+ }
139
+ else {
140
+ branchPush = 'skipped-empty';
141
+ note(`Skipped pushing ${branch} (no work beyond main / branch absent) — ` +
142
+ 'nothing to recover.');
143
+ }
144
+ }
145
+ return { moved: true, commitMessage, moveCommit, branchPush, pushError };
146
+ }
147
+ /**
148
+ * The clean re-queue (ADR §12 / WORK-CONTRACT return path): once the human has
149
+ * resolved the cause, move the stuck item back to `work/backlog/<slug>.md` and
150
+ * commit it so the item can be re-claimed (it must not rot stuck). It recovers a
151
+ * task stuck in EITHER `work/needs-attention/<slug>.md` (the resolved-surface
152
+ * path) OR `work/in-progress/<slug>.md` (a claim that never surfaced — an
153
+ * un-surfaced abort, a killed run, or an in-place requeue note; defect 2, story
154
+ * 4): the slug's ACTUAL current folder is resolved on the arbiter and moved to
155
+ * `backlog/` via the SAME tree-less CAS. Any recorded reason/handoff block stays
156
+ * in the body as a durable note of what happened; the resolution itself is the
157
+ * human's.
158
+ *
159
+ * The `requeue` verb's THREE behaviours (ADR §14 / task
160
+ * `requeue-continue-and-reset`) are realised here:
161
+ * - **default = KEEP + CONTINUE.** The `work/<slug>` branch is left UNTOUCHED;
162
+ * it is the durable artifact the next claim CONTINUES from (the continue-
163
+ * detection in `continue-branch.ts` feeds both onboarding paths). This
164
+ * function only does the ledger move.
165
+ * - **`--reset` = DISCARD + FRESH.** When `reset` is set, DELETE the remote
166
+ * `work/<slug>` branch on `arbiter` FIRST (+ drop any stale local branch),
167
+ * THEN the backlog move. Delete-before-move closes the claim-race window; a
168
+ * FAILED delete ABORTS (no backlog move) so the item stays in
169
+ * needs-attention. The next claim then finds NO arbiter branch and cuts
170
+ * fresh — no special claim-time logic.
171
+ * - **`-m "<note>"` = HANDOFF NOTE.** When `message` is set, APPEND a dated
172
+ * `## Requeue YYYY-MM-DD` section to the item BODY (append-only; accumulates
173
+ * over repeated requeues) for the next agent. Applies to BOTH modes.
174
+ *
175
+ * Like the move, NEVER throws for the expected "not in needs-attention" case.
176
+ */
177
+ /**
178
+ * Read the item's lock entry from the LOCAL lock ref (no fetch) — the resilient
179
+ * fall-back for {@link returnToBacklog} when an arbiter fetch fails (e.g. a
180
+ * `--reset` against a moved-away arbiter). The up-front soft fetch already
181
+ * refreshed the local refs, so reading them locally is the best-effort truth
182
+ * without throwing.
183
+ */
184
+ async function readLocalItemLock(slug, cwd, env) {
185
+ const ref = itemLockRef(lockEntryFor(`task:${slug}`));
186
+ const show = await gitSoftAsync(['show', `${ref}:lock.md`], cwd, env);
187
+ if (show.status !== 0) {
188
+ return undefined;
189
+ }
190
+ return parseLockEntry(show.stdout);
191
+ }
192
+ export async function returnToBacklog(options) {
193
+ const note = options.note ?? (() => { });
194
+ const { cwd, slug, env } = options;
195
+ // Tree-less CAS, EXACTLY like `claim` (`performClaim`): the move is published
196
+ // to the arbiter ref via the shared `ledger-write` write seam — it NEVER stages
197
+ // or commits in the cwd working tree (so a `requeue` in a shared checkout can no
198
+ // longer sweep up a concurrent writer's uncommitted files — the `8c92f63`
199
+ // incident, see
200
+ // `work/notes/observations/drive-backlog-skill-assumes-in-place-do-not-remote.md`).
201
+ // `--cwd` is purely the ORIGIN SOURCE (it resolves the arbiter remote + holds
202
+ // the object store the plumbing writes into), never a write TARGET. A tree-less
203
+ // CAS needs a ref to push to, so an `arbiter` is REQUIRED (parity with `claim`).
204
+ if (!options.arbiter) {
205
+ return {
206
+ moved: false,
207
+ reasonNotMoved: `requeue for '${slug}' needs an --arbiter: the move is published as a ` +
208
+ 'tree-less compare-and-swap to the arbiter ref (like claim), so there ' +
209
+ 'is no local-only mode — pass --arbiter.',
210
+ };
211
+ }
212
+ const arbiter = options.arbiter;
213
+ if ((await gitSoftAsync(['remote', 'get-url', arbiter], cwd, env)).status !== 0) {
214
+ return {
215
+ moved: false,
216
+ reasonNotMoved: `no git remote named '${arbiter}' (set one, or pass --arbiter).`,
217
+ };
218
+ }
219
+ // Refresh the remote-tracking refs so every check below (the item's residence,
220
+ // the continue-branch guard, the CAS base) sees the arbiter's TRUTH, not a stale
221
+ // local copy. This is a fetch, not a checkout — the working tree is untouched.
222
+ await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
223
+ // Is the item LOCK-HELD on the arbiter? (task
224
+ // `cutover-needs-attention-becomes-lock-stuck-recovery-surface`, decision i+:
225
+ // stuck-state is the per-item lock `state: stuck`, NOT a `needs-attention/`
226
+ // folder file). `requeue` recovers a STUCK hold (the resolved-recovery path) and
227
+ // tolerates an ACTIVE hold (a killed run that never surfaced) — both legitimate
228
+ // in-flight states the human's recovery verb returns to the pool. We read the
229
+ // LOCK ref (arbiter-is-truth), NOT a folder. No held lock ⇒ nothing to requeue
230
+ // (the item is already at rest — unclaimed in `backlog/`, or terminal).
231
+ // Read the held lock TOLERANTLY: a broken/unreachable arbiter (e.g. a
232
+ // `--reset` against a moved-away arbiter) must NOT throw out of requeue — the
233
+ // up-front soft fetch above already refreshed the local lock refs, so on a
234
+ // fetch fault we fall back to the local lock ref (best-effort) rather than
235
+ // crashing. A genuinely absent lock still refuses below.
236
+ let held;
237
+ try {
238
+ held = await readItemLock({ item: `task:${slug}`, cwd, arbiter, env });
239
+ }
240
+ catch {
241
+ held = await readLocalItemLock(slug, cwd, env);
242
+ }
243
+ if (!held) {
244
+ return {
245
+ moved: false,
246
+ reasonNotMoved: `'${slug}' has no held per-item lock on ${arbiter} — nothing to requeue ` +
247
+ '(wrong slug, or already at rest in backlog/done?). requeue recovers a ' +
248
+ 'task whose lock is held stuck (needs-attention) or active (a killed ' +
249
+ 'in-progress run).',
250
+ };
251
+ }
252
+ // `--reset`: DELETE the remote work branch (before the backlog move). The
253
+ // deletion is WRITE-THROUGH: the LOCAL refs that drive continue-detection
254
+ // (`refs/remotes/<arbiter>/work/<slug>` AND any local head `work/<slug>`) are
255
+ // deleted FIRST, THEN the arbiter `git push --delete`. The asymmetry is the
256
+ // point: the arbiter is the source of truth and the local ref is derived, so
257
+ // inverting today's arbiter-first ordering converts a dangerous failure mode
258
+ // (local AHEAD of arbiter — a permanent stale-continue) into a self-healing
259
+ // one (local BEHIND — the next fetch restores it from the arbiter). A FAILED
260
+ // arbiter delete still ABORTS the requeue (no backlog move); the local
261
+ // behind-state is recoverable by a subsequent fetch and CANNOT drive a wrong
262
+ // continue. Delete-before-move also closes the claim-race window.
263
+ let deletedRemoteBranch = false;
264
+ if (options.reset) {
265
+ const branch = workBranchRef('task', slug);
266
+ // LOCAL-FIRST: the tracking ref `branchAheadOf` reads (the one whose
267
+ // staleness today silently turns `--reset` into a no-op — verified live in
268
+ // `work/notes/observations/requeue-reset-does-not-prune-hub-mirror-stale-branch-ref.md`,
269
+ // where `--reset` deleted the arbiter branch but the local tracking ref
270
+ // survived and resurrected a "continue" on the next `do`). Both deletes
271
+ // are best-effort — their absence is fine, what matters is they are not
272
+ // LEFT BEHIND when the arbiter delete succeeds.
273
+ await gitSoftAsync(['update-ref', '-d', `refs/remotes/${arbiter}/${branch}`], cwd, env);
274
+ await gitSoftAsync(['branch', '-D', branch], cwd, env);
275
+ // THEN the arbiter delete (explicit/guarded departure from the "never delete
276
+ // the remote branch" invariant; only on the `--reset` path, never the
277
+ // default).
278
+ const del = await gitSoftAsync(['push', arbiter, '--delete', branch], cwd, env);
279
+ if (del.status !== 0) {
280
+ const stderr = del.stderr.trim();
281
+ // Tolerate "remote ref does not exist" (already gone): treat as deleted.
282
+ const alreadyGone = /remote ref does not exist|unable to delete/i.test(stderr);
283
+ if (!alreadyGone) {
284
+ const message = `requeue --reset for '${slug}': failed to delete the remote branch ` +
285
+ `${branch} on ${arbiter} (${stderr || 'unknown error'}); ` +
286
+ 'aborting the requeue — item left in needs-attention (no backlog move). ' +
287
+ 'The local tracking ref was already cleared (write-through ordering); ' +
288
+ 'a subsequent fetch will restore it from the arbiter — the local store ' +
289
+ 'is BEHIND the arbiter (self-healing), never AHEAD (which would drive a ' +
290
+ 'stale continue).';
291
+ note(message);
292
+ return { moved: false, reasonNotMoved: message };
293
+ }
294
+ }
295
+ deletedRemoteBranch = true;
296
+ note(`Deleted the remote branch ${branch} on ${arbiter} (--reset).`);
297
+ }
298
+ // DEFAULT (keep+continue) REQUEUE-SAFETY GUARD: a claimable item's continue-
299
+ // branch MUST be reachable by ANY worker, so before releasing the lock verify
300
+ // the ARBITER branch `<arbiter>/work/<slug>` exists + is ahead of main — the
301
+ // EXACT "is the continue-branch on the arbiter?" question the continue-path asks
302
+ // in `isolation.ts`. We check the ARBITER ref (already fetched above), NOT the
303
+ // local `work/<slug>` (which SURVIVES a failed push). NOT on `--reset` (which
304
+ // discards the branch by design).
305
+ if (!options.reset) {
306
+ const branch = workBranchRef('task', slug);
307
+ const onArbiter = branchAheadOf(cwd, `${arbiter}/${branch}`, `${arbiter}/main`, env);
308
+ if (!onArbiter) {
309
+ const message = `the work branch ${branch} isn't on ${arbiter} (the continue ` +
310
+ `branch a cross-machine worker would resume from) — push it first, or ` +
311
+ '`requeue --reset` to discard and start fresh. Item left stuck (lock not ' +
312
+ 'released).';
313
+ note(message);
314
+ return { moved: false, reasonNotMoved: message };
315
+ }
316
+ }
317
+ const commitMessage = `chore(${slug}): return to backlog for re-claiming`;
318
+ const handoff = options.message && options.message.trim() !== ''
319
+ ? options.message.trim()
320
+ : undefined;
321
+ // The body rests EITHER in the pool (`tasks-ready`) OR — for a staged item driven
322
+ // with `--allow-backlog` — in staging (`tasks-backlog`). Probe in the SAME
323
+ // precedence `resolveTask`/`--allow-backlog` uses (ready first, then backlog), so
324
+ // the handoff note finds a staged body too (obs
325
+ // `requeue-dash-m-fails-and-strands-lock-for-staged-backlog-item`).
326
+ const bodyResidenceCandidates = [
327
+ 'tasks-ready',
328
+ 'tasks-backlog',
329
+ ];
330
+ // `-m "<note>"` (the handoff steer): APPEND a dated `## Requeue YYYY-MM-DD`
331
+ // section to the item BODY where it already rests (pool or staging), via the SAME
332
+ // tree-less CAS move (same-folder rewrite with the body transform) — it NEVER
333
+ // stages/commits in the cwd tree. The handoff is OPTIONAL and NON-FATAL: a failed
334
+ // append degrades to a WARNING and the lock release below STILL runs, because the
335
+ // lock release is the load-bearing recovery and must never be stranded by an
336
+ // optional note (obs `requeue-dash-m-fails-and-strands-lock-for-staged-backlog-item`).
337
+ if (handoff !== undefined) {
338
+ const noted = await runTreelessLedgerMove({
339
+ cwd,
340
+ slug,
341
+ arbiter,
342
+ kind: 'requeue',
343
+ onContended: 'requeue',
344
+ explicitMainRefspec: false,
345
+ env,
346
+ note,
347
+ plan: (base) => {
348
+ const bodyRel = bodyResidenceCandidates
349
+ .map((folder) => workItemRel(folder, `${slug}.md`))
350
+ .find((rel) => pathInCommit(base, rel, cwd, env));
351
+ if (bodyRel === undefined) {
352
+ // The body is in neither tasks/ready/ nor tasks/backlog/ on this base —
353
+ // nothing to annotate (the durable move that placed it must land first).
354
+ return 'missing';
355
+ }
356
+ return prepareTreelessMoveCommit({
357
+ cwd,
358
+ slug,
359
+ base,
360
+ sourceRel: bodyRel,
361
+ destRel: bodyRel,
362
+ transformBody: (body) => appendRequeueNoteText(body, handoff),
363
+ commitMessage: `chore(${slug}): requeue handoff note`,
364
+ refNamespace: 'requeue',
365
+ env,
366
+ });
367
+ },
368
+ });
369
+ if (!noted) {
370
+ // NON-FATAL: warn and fall through to the lock release. We do NOT strand the
371
+ // lock for a failed OPTIONAL note (the previous behaviour, which left a
372
+ // half-applied state: branch deleted on --reset, lock still held).
373
+ note(`requeue for '${slug}': could not append the -m handoff note (the body ` +
374
+ `is in neither tasks/ready/ nor tasks/backlog/ on ${arbiter}/main, or ` +
375
+ 'main kept moving). Releasing the lock anyway — the requeue still ' +
376
+ 'recovers the item; only the note was skipped.');
377
+ }
378
+ }
379
+ // RELEASE the held lock (`stuck → released` / give up the hold): the item
380
+ // returns to the claimable pool (its body already rests in `backlog/`). Use the
381
+ // tolerant {@link releaseItemLock} (idempotent) so requeue recovers BOTH a
382
+ // `stuck` hold (the resolved-recovery path) and an `active` hold (a killed run
383
+ // that never surfaced) — the human asserting "put it back".
384
+ const released = await releaseItemLock({
385
+ item: `task:${slug}`,
386
+ cwd,
387
+ arbiter,
388
+ env,
389
+ });
390
+ if (released.outcome === 'error') {
391
+ const message = `requeue for '${slug}': could not release the per-item lock ` +
392
+ `(${released.message}). The item is left stuck. Try again shortly.`;
393
+ note(message);
394
+ return { moved: false, reasonNotMoved: message };
395
+ }
396
+ note(`Returned '${slug}' to backlog (released the lock; body rests in pool).`);
397
+ return { moved: true, commitMessage, deletedRemoteBranch };
398
+ }
399
+ export async function promoteFromPreBacklog(options) {
400
+ const note = options.note ?? (() => { });
401
+ const { cwd, slug, env } = options;
402
+ if (!options.arbiter) {
403
+ return {
404
+ moved: false,
405
+ reasonNotMoved: `promote for '${slug}' needs an --arbiter: the move is published as a ` +
406
+ 'tree-less compare-and-swap to the arbiter ref (like requeue/claim), so ' +
407
+ 'there is no local-only mode — pass --arbiter.',
408
+ };
409
+ }
410
+ const arbiter = options.arbiter;
411
+ if ((await gitSoftAsync(['remote', 'get-url', arbiter], cwd, env)).status !== 0) {
412
+ return {
413
+ moved: false,
414
+ reasonNotMoved: `no git remote named '${arbiter}' (set one, or pass --arbiter).`,
415
+ };
416
+ }
417
+ // Refresh `<arbiter>/main` so the residence probe + the CAS base see the
418
+ // arbiter's TRUTH. A fetch, not a checkout — the working tree is untouched.
419
+ await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
420
+ // UNIFIED PER-ITEM LOCK around the CAS window (prd
421
+ // `staging-surface-and-apply-promote-safety`, task
422
+ // `f3b-promote-takes-per-item-advancing-lock`): promote and apply BOTH key onto
423
+ // the item's `refs/dorfl/lock/<entry>` ref with `action: advance` (the
424
+ // SAME action `apply` takes via `advancing-lock.ts`), so an apply mid-flight
425
+ // and a promote attempt on the SAME item are mutually exclusive BY
426
+ // CONSTRUCTION (the second acquirer loses the create-only ref CAS). Reusing
427
+ // the existing `advance` action value (rather than introducing a distinct
428
+ // `'promote'` axis) is deliberate — the lock entry is keyed on the item
429
+ // identity, and what matters is that ALL three transitions of one item
430
+ // (implement/task/advance) serialise on ONE ref. A lock `lost` exits CLEAN
431
+ // (no partial state on `main`, mirroring claim-cas loss semantics); on success
432
+ // or failure we release the lock in `finally`. Crash-safe release mirrors the
433
+ // apply rung: a crashed promote leaves an `advance`-active lock that the
434
+ // existing `release-lock` / `gc --ledger` recovery surface clears.
435
+ const item = `task:${slug}`;
436
+ const acquired = await acquireItemLock({
437
+ item,
438
+ action: 'advance',
439
+ cwd,
440
+ arbiter,
441
+ env,
442
+ });
443
+ if (acquired.outcome !== 'acquired') {
444
+ const message = acquired.outcome === 'lost'
445
+ ? `promote for '${slug}' lost the per-item lock race (another implement/task/advance hold is in flight). No move on ${arbiter}/main. Try again shortly.`
446
+ : `promote for '${slug}': could not acquire the per-item lock (${acquired.message}).`;
447
+ note(message);
448
+ return { moved: false, reasonNotMoved: message };
449
+ }
450
+ try {
451
+ const sourceRel = workItemRel('tasks-backlog', `${slug}.md`);
452
+ const destRel = workItemRel('tasks-ready', `${slug}.md`);
453
+ // Early-exit message: if NEITHER staged nor already-in-pool, there is
454
+ // nothing to promote (the per-attempt `plan` is the authoritative
455
+ // resolution against the live base).
456
+ const hasSource = (await gitSoftAsync(['cat-file', '-e', `${arbiter}/main:${sourceRel}`], cwd, env)).status === 0;
457
+ const hasDest = (await gitSoftAsync(['cat-file', '-e', `${arbiter}/main:${destRel}`], cwd, env)).status === 0;
458
+ if (!hasSource && !hasDest) {
459
+ const message = `'${slug}' is not staged in work/pre-backlog/ on ${arbiter}/main (and not ` +
460
+ 'already in work/backlog/) — nothing to promote (wrong slug, or never ' +
461
+ 'staged?).';
462
+ note(message);
463
+ return { moved: false, reasonNotMoved: message };
464
+ }
465
+ const commitMessage = `chore(${slug}): promote work/pre-backlog/ -> work/backlog/`;
466
+ const moved = await runTreelessLedgerMove({
467
+ cwd,
468
+ slug,
469
+ arbiter,
470
+ kind: 'promote',
471
+ onContended: 'promote',
472
+ // The surface direction's main-only refspec works here too: this runs in
473
+ // the project checkout, but we only need `<arbiter>/main` resolved, and
474
+ // the explicit refspec is the safer default (mirrors `surface`).
475
+ explicitMainRefspec: true,
476
+ env,
477
+ note,
478
+ plan: (base) => {
479
+ // If already in the pool on this base, a prior attempt landed
480
+ // (idempotent).
481
+ if (pathInCommit(base, destRel, cwd, env)) {
482
+ return 'already-done';
483
+ }
484
+ if (!pathInCommit(base, sourceRel, cwd, env)) {
485
+ return 'missing';
486
+ }
487
+ return prepareTreelessMoveCommit({
488
+ cwd,
489
+ slug,
490
+ base,
491
+ sourceRel,
492
+ destRel,
493
+ // The body is carried byte-for-byte from pre-backlog into the
494
+ // pool — promotion is a placement decision, not a content transform.
495
+ transformBody: (body) => body,
496
+ commitMessage,
497
+ refNamespace: 'promote',
498
+ env,
499
+ });
500
+ },
501
+ });
502
+ if (moved) {
503
+ note(`Promoted '${slug}' from pre-backlog to backlog (claimable).`);
504
+ return { moved: true, commitMessage };
505
+ }
506
+ const message = `promote for '${slug}': the arbiter's main kept moving (contended) after ` +
507
+ `${TREELESS_CONTENTION_ATTEMPTS} attempts — item left in pre-backlog (no ` +
508
+ 'move). Try again shortly.';
509
+ note(message);
510
+ return { moved: false, reasonNotMoved: message };
511
+ }
512
+ finally {
513
+ await releaseItemLock({ item, cwd, arbiter, env });
514
+ }
515
+ }
516
+ export async function promoteFromPreSpec(options) {
517
+ const note = options.note ?? (() => { });
518
+ const { cwd, slug, env } = options;
519
+ if (!options.arbiter) {
520
+ return {
521
+ moved: false,
522
+ reasonNotMoved: `promote for '${slug}' needs an --arbiter: the move is published as ` +
523
+ 'a tree-less compare-and-swap to the arbiter ref (like requeue/claim), so ' +
524
+ 'there is no local-only mode — pass --arbiter.',
525
+ };
526
+ }
527
+ const arbiter = options.arbiter;
528
+ if ((await gitSoftAsync(['remote', 'get-url', arbiter], cwd, env)).status !== 0) {
529
+ return {
530
+ moved: false,
531
+ reasonNotMoved: `no git remote named '${arbiter}' (set one, or pass --arbiter).`,
532
+ };
533
+ }
534
+ // Refresh `<arbiter>/main` so the residence probe + the CAS base see the
535
+ // arbiter's TRUTH. A fetch, not a checkout — the working tree is untouched.
536
+ await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
537
+ // UNIFIED PER-ITEM LOCK around the CAS window — symmetric with
538
+ // {@link promoteFromPreBacklog} (prd `staging-surface-and-apply-promote-safety`,
539
+ // task `f3b-promote-takes-per-item-advancing-lock`, decisive prd q4 answer:
540
+ // specs share the apply×promote mutual-exclusion fix with tasks). The lock
541
+ // keys on `spec:${slug}` (a distinct ref from a task with the same slug, via
542
+ // {@link lockEntryFor}'s `<type>-<slug>` encoding), with `action: advance` —
543
+ // the SAME action an apply for a spec would take — so spec promote and spec
544
+ // apply on the same item are mutually exclusive by construction. MIGRATE step
545
+ // (prd `prd-to-spec-vocabulary-cutover-and-migration-command`): the lock
546
+ // identity is `spec:${slug}` to match the `spec-<slug>` entry the tasking/apply
547
+ // path now acquires (`tasking.ts` releases under `spec:${slug}`); a stale
548
+ // `prd:${slug}` here would key a DIFFERENT ref and break the mutual exclusion.
549
+ // Loss / crash semantics mirror the task case.
550
+ const item = `spec:${slug}`;
551
+ const acquired = await acquireItemLock({
552
+ item,
553
+ action: 'advance',
554
+ cwd,
555
+ arbiter,
556
+ env,
557
+ });
558
+ if (acquired.outcome !== 'acquired') {
559
+ const message = acquired.outcome === 'lost'
560
+ ? `promote for '${slug}' lost the per-item lock race (another implement/task/advance hold is in flight). No move on ${arbiter}/main. Try again shortly.`
561
+ : `promote for '${slug}': could not acquire the per-item lock (${acquired.message}).`;
562
+ note(message);
563
+ return { moved: false, reasonNotMoved: message };
564
+ }
565
+ try {
566
+ const sourceRel = workItemRel('specs-proposed', `${slug}.md`);
567
+ const destRel = workItemRel('specs-ready', `${slug}.md`);
568
+ const hasSource = (await gitSoftAsync(['cat-file', '-e', `${arbiter}/main:${sourceRel}`], cwd, env)).status === 0;
569
+ const hasDest = (await gitSoftAsync(['cat-file', '-e', `${arbiter}/main:${destRel}`], cwd, env)).status === 0;
570
+ if (!hasSource && !hasDest) {
571
+ const message = `'${slug}' is not staged in ${workFolderPrefix('specs-proposed')} on ${arbiter}/main ` +
572
+ `(and not already in ${workFolderPrefix('specs-ready')}) — nothing to promote ` +
573
+ '(wrong slug, or never staged?).';
574
+ note(message);
575
+ return { moved: false, reasonNotMoved: message };
576
+ }
577
+ const commitMessage = `chore(${slug}): promote ${workFolderPrefix('specs-proposed')} -> ${workFolderPrefix('specs-ready')}`;
578
+ const moved = await runTreelessLedgerMove({
579
+ cwd,
580
+ slug,
581
+ arbiter,
582
+ kind: 'promote',
583
+ onContended: 'promote',
584
+ explicitMainRefspec: true,
585
+ env,
586
+ note,
587
+ plan: (base) => {
588
+ if (pathInCommit(base, destRel, cwd, env)) {
589
+ return 'already-done';
590
+ }
591
+ if (!pathInCommit(base, sourceRel, cwd, env)) {
592
+ return 'missing';
593
+ }
594
+ return prepareTreelessMoveCommit({
595
+ cwd,
596
+ slug,
597
+ base,
598
+ sourceRel,
599
+ destRel,
600
+ // The body is carried byte-for-byte from prds/proposed into the pool —
601
+ // promotion is a placement decision, not a content transform.
602
+ transformBody: (body) => body,
603
+ commitMessage,
604
+ refNamespace: 'promote',
605
+ env,
606
+ });
607
+ },
608
+ });
609
+ if (moved) {
610
+ note(`Promoted prd '${slug}' from prds/proposed to prds/ready (auto-taskable).`);
611
+ return { moved: true, commitMessage };
612
+ }
613
+ const message = `promote for '${slug}': the arbiter's main kept moving (contended) ` +
614
+ `after ${TREELESS_CONTENTION_ATTEMPTS} attempts — item left in prds/proposed ` +
615
+ '(no move). Try again shortly.';
616
+ note(message);
617
+ return { moved: false, reasonNotMoved: message };
618
+ }
619
+ finally {
620
+ await releaseItemLock({ item, cwd, arbiter, env });
621
+ }
622
+ }
623
+ /**
624
+ * LIST every staged item awaiting a runner/human promotion — the tasks in
625
+ * `work/pre-backlog/` and the prds in `work/prds/proposed/` on `<arbiter>/main` (the
626
+ * discovery half of the `promote` verb, so `promote` with no argument answers
627
+ * "what is staged waiting for me?"). It reads the ARBITER's truth (a fetch + a
628
+ * tree read), NOT the local working tree (which may be stale) — the same source
629
+ * the promotion functions act against, so the list and the move never disagree.
630
+ * Read-only: it never fetches a checkout, never moves anything.
631
+ */
632
+ export async function listPromotable(options) {
633
+ const { cwd, arbiter, env } = options;
634
+ if ((await gitSoftAsync(['remote', 'get-url', arbiter], cwd, env)).status !== 0) {
635
+ return {
636
+ items: [],
637
+ error: `no git remote named '${arbiter}' (set one, or pass --arbiter).`,
638
+ };
639
+ }
640
+ // Refresh `<arbiter>/main` so the listing sees the arbiter's TRUTH (a fetch,
641
+ // not a checkout — the working tree is untouched), exactly as the promote
642
+ // functions do before their residence probe.
643
+ await gitSoftAsync(['fetch', '--quiet', arbiter], cwd, env);
644
+ const tasks = await listMarkdownSlugsInTree(`${arbiter}/main:${workFolderRel('tasks-backlog')}`, cwd, env);
645
+ const specs = await listMarkdownSlugsInTree(`${arbiter}/main:${workFolderRel('specs-proposed')}`, cwd, env);
646
+ return {
647
+ items: [
648
+ ...tasks.map((slug) => ({ namespace: 'task', slug })),
649
+ ...specs.map((slug) => ({ namespace: 'spec', slug })),
650
+ ],
651
+ };
652
+ }
653
+ /**
654
+ * `git ls-tree --name-only <base>` → the `.md` filenames' SLUGS (filename minus
655
+ * `.md`), sorted. An absent folder on the ref reads as empty (the staging folder
656
+ * may not exist yet). The bare-repo-safe read (`ls-tree`, no working tree), the
657
+ * SAME mechanism the ledger read seam uses.
658
+ */
659
+ async function listMarkdownSlugsInTree(base, cwd, env) {
660
+ const tree = await gitSoftAsync(['ls-tree', '--name-only', base], cwd, env);
661
+ if (tree.status !== 0) {
662
+ return [];
663
+ }
664
+ return tree.stdout
665
+ .split('\n')
666
+ .map((s) => s.trim())
667
+ .filter((name) => isWorkItemFile(name))
668
+ .map((name) => name.replace(/\.md$/i, ''))
669
+ .sort();
670
+ }
671
+ /** The contention-retry cap shared by the tree-less requeue + surface moves. */
672
+ const TREELESS_CONTENTION_ATTEMPTS = 5;
673
+ /**
674
+ * The SHARED tree-less ledger-move core (ONE mechanism for BOTH directions — the
675
+ * requeue `needs-attention|in-progress → backlog` and the surface `in-progress →
676
+ * needs-attention`). It runs the contention-retry loop: fetch `<arbiter>/main`,
677
+ * resolve `expectedBase`, ask the caller's `plan(base)` to build the one-file move
678
+ * on a SCRATCH INDEX via {@link prepareTreelessMoveCommit} (so the caller's
679
+ * index/HEAD/working tree are NEVER touched), CAS-publish it THROUGH the shared
680
+ * write seam (`ledgerWrite.applyTransition`, the very push+lease+verify `claim`
681
+ * uses), drop the throwaway ref, and on a CONTENTION rejection refetch + REPLAN
682
+ * against the advanced base and retry. Re-planning per attempt is what makes the
683
+ * retry safe when the item itself moved under us (e.g. a prior attempt landed but
684
+ * the CAS verify reported rejected): the next plan sees it `already-done`.
685
+ */
686
+ async function runTreelessLedgerMove(params) {
687
+ const { cwd, arbiter, kind, plan, onContended, explicitMainRefspec, env, note, } = params;
688
+ const fetchArgs = explicitMainRefspec
689
+ ? [
690
+ 'fetch',
691
+ '--quiet',
692
+ arbiter,
693
+ `+refs/heads/main:refs/remotes/${arbiter}/main`,
694
+ ]
695
+ : ['fetch', '--quiet', arbiter];
696
+ for (let i = 0; i < TREELESS_CONTENTION_ATTEMPTS; i++) {
697
+ if (i > 0) {
698
+ await gitSoftAsync(fetchArgs, cwd, env);
699
+ }
700
+ const base = (await gitHardAsync(['rev-parse', `${arbiter}/main`], cwd, env)).stdout.trim();
701
+ // Plan the move FRESH against this (possibly re-fetched) base. The item may
702
+ // already be at the destination (idempotent / a prior landed-but-reported-
703
+ // rejected attempt) or absent — both are terminal, no push.
704
+ const prepared = plan(base);
705
+ if (prepared === 'already-done') {
706
+ return true;
707
+ }
708
+ if (prepared === 'missing') {
709
+ return false;
710
+ }
711
+ // Publish THROUGH the shared seam (the same `:main` push + force-with-lease +
712
+ // verify `claim` uses). The transition's WHO stays the caller's ambient env
713
+ // (threaded by `commit-tree` above) — tree-less is orthogonal to attribution.
714
+ const result = await ledgerWrite.applyTransition({
715
+ kind,
716
+ arbiter,
717
+ localBranch: prepared.ref,
718
+ expectedBase: base,
719
+ head: prepared.commit,
720
+ cwd,
721
+ env,
722
+ note,
723
+ });
724
+ // Drop the throwaway ref either way (it served only as the push source).
725
+ await gitSoftAsync(['update-ref', '-d', prepared.ref], cwd, env);
726
+ if (result.kind === 'published') {
727
+ // Advance the LOCAL remote-tracking `<arbiter>/main` so it INCLUDES the
728
+ // move (the push only moved the arbiter's main). Best-effort.
729
+ await gitSoftAsync(fetchArgs, cwd, env);
730
+ return true;
731
+ }
732
+ // rejected: main moved under us — refetch + REPLAN against the new base.
733
+ note(`main advanced under us — ${onContended} refetch and retry (${i + 1}/${TREELESS_CONTENTION_ATTEMPTS})...`);
734
+ }
735
+ return false;
736
+ }
737
+ /** True iff `path` exists in the given commit's tree (a soft cat-file probe). */
738
+ function pathInCommit(commit, path, cwd, env) {
739
+ return (run('git', ['cat-file', '-e', `${commit}:${path}`], cwd, { env }).status === 0);
740
+ }
741
+ /**
742
+ * Build a one-file ledger MOVE as a commit off the arbiter's `main`, using
743
+ * PLUMBING on a SCRATCH INDEX — it never touches the caller's index, HEAD, or
744
+ * working tree (so a concurrent writer's uncommitted cwd files can never be swept
745
+ * in). It loads `base`'s tree into a throwaway index, relocates ONLY this slug's
746
+ * ledger file from `sourceRel` to `destRel` (applying `transformBody` to its body
747
+ * first — read from the blob on `main`, NOT from any cwd file: the requeue note,
748
+ * or the needs-attention reason), writes the tree, commits it parented on `base`,
749
+ * and points a throwaway local ref at the commit. Returns that ref + the commit
750
+ * sha for the seam's CAS push. The SHARED prep for BOTH tree-less directions.
751
+ */
752
+ function prepareTreelessMoveCommit(params) {
753
+ const { cwd, slug, base, sourceRel, destRel, transformBody, commitMessage, refNamespace, env, } = params;
754
+ // The item's body on `main`, with the caller's body transform applied.
755
+ const original = catBlob(`${base}:${sourceRel}`, cwd, env);
756
+ const content = transformBody(original);
757
+ // Hash the (possibly transformed) blob INTO the cwd's object store. A blob
758
+ // write does not touch the working tree.
759
+ const blob = hashObject(content, cwd, env);
760
+ // A scratch index so read-tree/update-index never disturb the caller's index.
761
+ const scratchIndex = join(tmpdir(), `dorfl-${refNamespace}-${process.pid}-${Date.now()}.index`);
762
+ const withIndex = {
763
+ ...(env ?? process.env),
764
+ GIT_INDEX_FILE: scratchIndex,
765
+ };
766
+ try {
767
+ gitHard(['read-tree', base], cwd, withIndex);
768
+ // Remove the item from its source folder, add it under the dest folder. When
769
+ // source === dest (an idempotent re-surface), force-remove + re-add the same
770
+ // path is a no-op move that still carries any body change. One file changes.
771
+ gitHard(['update-index', '--force-remove', sourceRel], cwd, withIndex);
772
+ gitHard(['update-index', '--add', '--cacheinfo', `100644,${blob},${destRel}`], cwd, withIndex);
773
+ const tree = runHard(['write-tree'], cwd, withIndex).stdout.trim();
774
+ // commit-tree threads the caller's ambient identity (env) — tree-less only
775
+ // changed the WHERE-it-writes (the arbiter ref, not the cwd tree), not the WHO.
776
+ const commit = runHard(['commit-tree', tree, '-p', base, '-m', commitMessage], cwd, env).stdout.trim();
777
+ // A throwaway local ref the seam's push uses as its source (`<ref>:main`).
778
+ const ref = `refs/dorfl/${refNamespace}/${slug}`;
779
+ gitHard(['update-ref', ref, commit], cwd, env);
780
+ return { ref, commit };
781
+ }
782
+ finally {
783
+ rmSync(scratchIndex, { force: true });
784
+ }
785
+ }
786
+ /** The heading that opens an appended requeue handoff note in the item body. */
787
+ const REQUEUE_HEADING_PREFIX = '## Requeue';
788
+ /**
789
+ * Append a dated `## Requeue YYYY-MM-DD` handoff section to an item body's TEXT
790
+ * (append-only — never overwrites; repeated requeues accumulate a handoff log).
791
+ * Body prose only (never a frontmatter field — WORK-CONTRACT rule 3). The date is
792
+ * UTC `YYYY-MM-DD`; multiple notes on the same day are distinct appended blocks.
793
+ *
794
+ * A PURE string transform (it operates on the body CONTENT, not a file path) so
795
+ * the tree-less requeue can apply it to the blob read from `<arbiter>/main`
796
+ * without touching the cwd working tree.
797
+ */
798
+ function appendRequeueNoteText(content, message) {
799
+ const date = new Date().toISOString().slice(0, 10);
800
+ const base = content.replace(/\s*$/, '');
801
+ return [base, '', `${REQUEUE_HEADING_PREFIX} ${date}`, '', message, ''].join('\n');
802
+ }
803
+ /**
804
+ * Extract the prose written under the `## Needs attention` heading from an item
805
+ * body. Returns the first non-empty line(s) of the block as a single line
806
+ * (stops at the next `## ` heading); '' when no block is present. The
807
+ * needs-attention REASON is now recorded on the per-item lock entry (task
808
+ * `cutover-needs-attention-becomes-lock-stuck-recovery-surface`, decision i+),
809
+ * NOT in the body, but this extractor stays for any historical body text that
810
+ * still carries the heading (a tolerant best-effort read).
811
+ */
812
+ export function extractReason(content) {
813
+ const normalized = content.replace(/\r\n/g, '\n');
814
+ const lines = normalized.split('\n');
815
+ const start = lines.findIndex((l) => l.trim() === REASON_HEADING);
816
+ if (start === -1) {
817
+ return '';
818
+ }
819
+ const collected = [];
820
+ for (let i = start + 1; i < lines.length; i++) {
821
+ const line = lines[i];
822
+ if (/^##\s/.test(line)) {
823
+ break;
824
+ }
825
+ if (/^###\s/.test(line)) {
826
+ // The questions sub-section starts here; the reason itself is above it.
827
+ break;
828
+ }
829
+ if (line.trim() === '') {
830
+ if (collected.length > 0) {
831
+ // Stop at the first blank line AFTER we captured the reason text.
832
+ break;
833
+ }
834
+ continue;
835
+ }
836
+ collected.push(line.trim());
837
+ }
838
+ return collected.join(' ').trim();
839
+ }
840
+ /** Run git; throw on non-zero (genuinely unexpected plumbing failures). */
841
+ function gitHard(args, cwd, env) {
842
+ runHard(args, cwd, env);
843
+ }
844
+ /** Like {@link gitHard} but returns the raw result (for plumbing that emits stdout). */
845
+ function runHard(args, cwd, env) {
846
+ const result = run('git', args, cwd, { env });
847
+ if (result.status !== 0) {
848
+ throw new Error(`git ${args.join(' ')} failed (exit ${result.status}): ${result.stderr.trim()}`);
849
+ }
850
+ return result;
851
+ }
852
+ /** Async soft git (no throw) — for the tree-less requeue's remote checks. */
853
+ function gitSoftAsync(args, cwd, env) {
854
+ return runAsync('git', args, cwd, { env });
855
+ }
856
+ /** Async git; throw on non-zero (unexpected plumbing failures). */
857
+ async function gitHardAsync(args, cwd, env) {
858
+ const result = await runAsync('git', args, cwd, { env });
859
+ if (result.status !== 0) {
860
+ throw new Error(`git ${args.join(' ')} failed (exit ${result.status}): ${result.stderr.trim()}`);
861
+ }
862
+ return result;
863
+ }
864
+ /** Read an object's content (`git cat-file -p <object>`) from the cwd's store. */
865
+ function catBlob(object, cwd, env) {
866
+ return runHard(['cat-file', '-p', object], cwd, env).stdout;
867
+ }
868
+ /** Write a blob into the cwd's object store (`git hash-object -w`), return its sha. */
869
+ function hashObject(content, cwd, env) {
870
+ const result = run('git', ['hash-object', '-w', '--stdin'], cwd, {
871
+ env,
872
+ input: content,
873
+ });
874
+ if (result.status !== 0) {
875
+ throw new Error(`git hash-object failed (exit ${result.status}): ${result.stderr.trim()}`);
876
+ }
877
+ return result.stdout.trim();
878
+ }
879
+ /**
880
+ * Run git, returning the raw result (no throw) — for soft checks like the
881
+ * `--reset` remote-branch delete, whose non-zero exit is a meaningful outcome
882
+ * (the requeue aborts) rather than an unexpected plumbing failure.
883
+ */
884
+ function gitSoftRun(args, cwd, env) {
885
+ return run('git', args, cwd, { env });
886
+ }
887
+ /** True when the index has no staged changes against HEAD (nothing to commit). */
888
+ function nothingStaged(cwd, env) {
889
+ // `diff --cached --quiet` exits 0 when NOTHING is staged, 1 when there is.
890
+ return run('git', ['diff', '--cached', '--quiet'], cwd, { env }).status === 0;
891
+ }
892
+ /** The current HEAD commit sha (the just-made commit's tip). */
893
+ function revParseHead(cwd, env) {
894
+ const result = run('git', ['rev-parse', 'HEAD'], cwd, { env });
895
+ if (result.status !== 0) {
896
+ throw new Error(`git rev-parse HEAD failed (exit ${result.status}): ${result.stderr.trim()}`);
897
+ }
898
+ return result.stdout.trim();
899
+ }
900
+ //# sourceMappingURL=needs-attention.js.map