dorfl 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +3094 -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 +215 -0
  162. package/dist/frontmatter.d.ts.map +1 -0
  163. package/dist/frontmatter.js +345 -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 +315 -0
  330. package/dist/prd-to-spec.d.ts.map +1 -0
  331. package/dist/prd-to-spec.js +684 -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 +360 -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 +952 -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 +4369 -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 +485 -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 +1023 -0
  581. package/src/prepare.ts +230 -0
  582. package/src/prompt.ts +760 -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 +1438 -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
package/src/start.ts ADDED
@@ -0,0 +1,974 @@
1
+ import {performClaim} from './claim-cas.js';
2
+ import {readItemLock} from './item-lock.js';
3
+ import {ledgerWrite} from './ledger-write.js';
4
+ import type {SurfaceToNeedsAttentionResult} from './needs-attention.js';
5
+ import {
6
+ branchAheadOfArbiter,
7
+ rebaseContinuedBranchOntoMain,
8
+ pushContinuedBranchWithStaleLeaseRetry,
9
+ } from './continue-branch.js';
10
+ import {runAsync, type RunResult} from './git.js';
11
+ import {workFolderRel} from './work-layout.js';
12
+ import {workBranchRef, parseWorkBranchRef} from './slug-namespace.js';
13
+ import type {InteractiveLauncher} from './harness.js';
14
+
15
+ /**
16
+ * `dorfl start [<slug>]` — the human convenience that claims a task (only
17
+ * if necessary) and onboards the human onto its work branch in their CURRENT
18
+ * checkout. It is a thin sequencer over the `claim` CAS (claim-cas.ts): it never
19
+ * reimplements or weakens the claim, and it creates the work branch ONLY after a
20
+ * claim provably lands.
21
+ *
22
+ * The decision of WHAT to do is made on the item's current FOLDER on
23
+ * `<arbiter>/main` — never on the advisory `claimed_by` frontmatter field
24
+ * (WORK-CONTRACT rule 6: the folder + git history are the source of truth).
25
+ *
26
+ * work/backlog/<slug>.md → claim it; on a winning claim, switch to
27
+ * work/<slug> off the arbiter. On a lost/contended
28
+ * claim, behave exactly like `claim`: user
29
+ * restored, NO work branch, exit code propagated.
30
+ * work/in-progress/<slug>.md → already claimed. Refuse by default with a clear
31
+ * message; with --resume, switch to work/<slug>
32
+ * off the arbiter WITHOUT claiming (the human
33
+ * explicitly asserts ownership).
34
+ * work/needs-attention/<slug>.md → stuck (a runner bounced it). Print the
35
+ * recorded reason, transition it back to
36
+ * in-progress THROUGH the write seam (which in
37
+ * mode M clears the `main` surface via the reverse
38
+ * move), then switch onto work/<slug>. UNGUARDED
39
+ * (no --resume): a stuck item is up-for-grabs, so a
40
+ * human can just pick it up with no manual move.
41
+ * work/done/<slug>.md or absent → refuse (nothing to start).
42
+ *
43
+ * It is harness-agnostic: it lands the human on the work branch and gets out of
44
+ * the way. It launches no agent/editor.
45
+ *
46
+ * Exit codes:
47
+ * 0 on the work branch (claimed-and-switched, or resumed)
48
+ * 1 usage / environment error, or a refusal (in-progress without --resume,
49
+ * done/absent, no slug + not on a work/<slug> branch)
50
+ * 2 lost the race (propagated from `claim`)
51
+ * 3 contended (propagated from `claim`)
52
+ */
53
+
54
+ export type StartOutcome =
55
+ | 'started' // claimed a backlog item and switched to its work branch
56
+ | 'resumed' // switched to an in-progress item's work branch (--resume)
57
+ | 'resolved' // picked up a stuck needs-attention item (surface cleared)
58
+ | 'needs-attention' // continued branch's rebase onto main conflicted → routed (§10)
59
+ | 'surface-unmoved' // the tree-less surface to needs-attention did NOT land on the arbiter (lost the CAS race) — the item is STILL in-progress on the arbiter; retry/resolve
60
+ | 'refused' // refused (in-progress without --resume, done/absent, or not-ready)
61
+ | 'lost' // claim lost the race (propagated from claim)
62
+ | 'contended' // claim push kept being rejected (propagated from claim)
63
+ | 'usage-error'; // usage / environment problem
64
+
65
+ export interface StartOptions {
66
+ /** The slug to start. If omitted, inferred from a `work/<slug>` branch. */
67
+ slug?: string;
68
+ /** The working clone/checkout to onboard the human into. */
69
+ cwd: string;
70
+ /** Name of the arbiter git remote. Defaults to `origin`. */
71
+ arbiter?: string;
72
+ /** Assert ownership of an already-in-progress item (switch without claiming). */
73
+ resume?: boolean;
74
+ /**
75
+ * Override the readiness guard (`--force` / `--ignore-not-ready`): claim a task
76
+ * with an unmet `blockedBy` anyway, and silence the `needsAnswers` warning —
77
+ * loudly. Forwarded to the claim CAS's human-path guard.
78
+ */
79
+ override?: boolean;
80
+ /**
81
+ * `--allow-backlog` (prd
82
+ * `do-allow-backlog-drive-staged-tasks-without-promotion`): recognise a
83
+ * `tasks/backlog/`-resident body (staging) as a valid start/onboard residence,
84
+ * so the no-checkout `do --remote/--isolated … --allow-backlog` flow (which
85
+ * onboards via `start` after a backlog-aware claim) does not read the staged
86
+ * task as `absent`. It widens ONLY the folder RESOLUTION (`tasks-backlog` is
87
+ * treated like the pool for the lock re-key + the backlog onboard); claim/lock/
88
+ * build are identical. Default off ⇒ staging is invisible, exactly as today.
89
+ */
90
+ allowBacklog?: boolean;
91
+ /**
92
+ * `--agent` (task `agent-interactive-launch`): after onboarding onto
93
+ * `work/<slug>`, launch the configured harness INTERACTIVELY in the checkout —
94
+ * a foreground session the human drives (no prepared prompt). Injected as a
95
+ * thin launcher so `start.ts` stays decoupled from `createHarness`: the CLI
96
+ * wires it to the resolved harness's `launchInteractive` (model/session/cwd
97
+ * resolved there). When omitted, start onboards and gets out of the way (its
98
+ * historical behaviour, unchanged). It is NOT a tracked job (decision #3): no
99
+ * job record, no gate — after exit the human drives `complete`/`requeue`.
100
+ */
101
+ launchInteractive?: InteractiveLauncher;
102
+ /** Environment for child git processes (identity etc.). */
103
+ env?: NodeJS.ProcessEnv;
104
+ /** Sink for human-readable progress notes. */
105
+ note?: (message: string) => void;
106
+ }
107
+
108
+ export interface StartResult {
109
+ exitCode: 0 | 1 | 2 | 3;
110
+ outcome: StartOutcome;
111
+ /** The work branch the user landed on, when one was created/switched-to. */
112
+ branch?: string;
113
+ /** Human-readable summary of the terminal condition. */
114
+ message: string;
115
+ }
116
+
117
+ const DEFAULT_ARBITER = 'origin';
118
+
119
+ /** Raised for usage/environment errors (exit 1, outcome 'usage-error'). */
120
+ class StartUsageError extends Error {}
121
+
122
+ /**
123
+ * Raised for a deliberate REFUSAL (exit 1, outcome 'refused') — distinct from a
124
+ * usage/environment error. Used when the item exists but start declines to act
125
+ * (an in-progress item without --resume). The decision is folder-based.
126
+ */
127
+ class StartRefusal extends Error {}
128
+
129
+ /** The folder a work item currently lives in on the arbiter's main. */
130
+ type Folder =
131
+ | 'tasks-ready'
132
+ | 'in-progress'
133
+ | 'needs-attention'
134
+ | 'done'
135
+ | 'absent';
136
+
137
+ /**
138
+ * Run the start ritual. Never throws for the expected lost/contended/refused
139
+ * cases — those are returned with the appropriate exit code. Usage/environment
140
+ * problems and refusals surface as exit 1.
141
+ */
142
+ export async function performStart(
143
+ options: StartOptions,
144
+ ): Promise<StartResult> {
145
+ const note = options.note ?? (() => {});
146
+ try {
147
+ return await runStart(options, note);
148
+ } catch (err) {
149
+ if (err instanceof StartRefusal) {
150
+ return {exitCode: 1, outcome: 'refused', message: err.message};
151
+ }
152
+ if (err instanceof StartUsageError) {
153
+ return {exitCode: 1, outcome: 'usage-error', message: err.message};
154
+ }
155
+ const message = err instanceof Error ? err.message : String(err);
156
+ return {exitCode: 1, outcome: 'usage-error', message};
157
+ }
158
+ }
159
+
160
+ async function runStart(
161
+ options: StartOptions,
162
+ note: (m: string) => void,
163
+ ): Promise<StartResult> {
164
+ const arbiter = options.arbiter ?? DEFAULT_ARBITER;
165
+ const cwd = options.cwd;
166
+ const env = options.env;
167
+
168
+ if ((await gitSoft(['rev-parse', '--git-dir'], cwd, env)).status !== 0) {
169
+ throw new StartUsageError('not inside a git repository');
170
+ }
171
+ if ((await gitSoft(['remote', 'get-url', arbiter], cwd, env)).status !== 0) {
172
+ throw new StartUsageError(
173
+ `no git remote named '${arbiter}' (set one, or pass --arbiter)`,
174
+ );
175
+ }
176
+
177
+ // Resolve the slug: explicit wins; otherwise infer from a `work/<slug>` branch.
178
+ const slug = options.slug || (await inferSlugFromBranch(cwd, env));
179
+ if (!slug) {
180
+ throw new StartUsageError(
181
+ 'missing <slug> and the current branch is not a work/<slug> branch. ' +
182
+ 'usage: dorfl start [<slug>] [--arbiter remote] [--resume]',
183
+ );
184
+ }
185
+
186
+ // Folder on the arbiter's main is the source of truth (NOT claimed_by) for the
187
+ // DURABLE residence; HELD-NESS is read from the per-item LOCK ref, not a folder.
188
+ await gitHard(['fetch', '--quiet', arbiter], cwd, env);
189
+ const folder = await dispatchFolder(
190
+ slug,
191
+ arbiter,
192
+ cwd,
193
+ env,
194
+ options.allowBacklog === true,
195
+ );
196
+
197
+ const result = await onboardFromFolder(folder, {
198
+ options,
199
+ slug,
200
+ arbiter,
201
+ cwd,
202
+ env,
203
+ note,
204
+ });
205
+
206
+ // `--agent` (task `agent-interactive-launch`): when the human onboarded onto a
207
+ // work branch (started/resumed/resolved — exit 0 with a branch), launch the
208
+ // configured harness INTERACTIVELY in the checkout so they can immediately drive
209
+ // the agent. It is NOT a tracked job (decision #3): no record, no gate — it
210
+ // blocks in the foreground until the human exits, then control returns and they
211
+ // drive `complete`/`requeue`. On a failed/refused onboard we never launch.
212
+ if (
213
+ options.launchInteractive &&
214
+ result.exitCode === 0 &&
215
+ result.branch !== undefined
216
+ ) {
217
+ note(`Launching the configured harness interactively in ${cwd}.`);
218
+ options.launchInteractive({slug, dir: cwd, env});
219
+ }
220
+ return result;
221
+ }
222
+
223
+ /**
224
+ * Resolve the EFFECTIVE dispatch folder, combining the durable `<arbiter>/main`
225
+ * folder with the per-item LOCK ref. Since claim no longer moves the body, a
226
+ * CLAIMED-AND-IN-FLIGHT task RESTS in `backlog/` on `main` (task
227
+ * `cutover-claim-body-stays-and-complete-sources-from-backlog`), and a STUCK item
228
+ * is the per-item lock `state: stuck` (NOT a `needs-attention/` folder file —
229
+ * task `cutover-needs-attention-becomes-lock-stuck-recovery-surface`, decision
230
+ * i+: the folder is retired). So a folder-only check would mis-read either as
231
+ * unclaimed and let `start` re-claim it. We re-key the dispatch off the LOCK when
232
+ * the body is in `backlog/`:
233
+ * - held `active` (`action: implement`) ⇒ `in-progress` (already claimed:
234
+ * refuse / `--resume`);
235
+ * - held `stuck` ⇒ `needs-attention` (the recovery dispatch: print the reason,
236
+ * resume the lock `stuck → active`, onboard).
237
+ * Everything else dispatches by folder unchanged.
238
+ *
239
+ * Under `--allow-backlog` (prd
240
+ * `do-allow-backlog-drive-staged-tasks-without-promotion`) a body resting in
241
+ * `tasks/backlog/` (staging) is treated EXACTLY like the pool for this re-key
242
+ * (held active ⇒ in-progress; held stuck ⇒ needs-attention; else the backlog
243
+ * onboard), so the no-checkout `do … --allow-backlog` flow that claimed a staged
244
+ * task (the body stays in `tasks/backlog/`) onboards instead of reading `absent`.
245
+ */
246
+ async function dispatchFolder(
247
+ slug: string,
248
+ arbiter: string,
249
+ cwd: string,
250
+ env: NodeJS.ProcessEnv | undefined,
251
+ allowBacklog = false,
252
+ ): Promise<Folder> {
253
+ const folder = await folderOnArbiterMain(
254
+ slug,
255
+ arbiter,
256
+ cwd,
257
+ env,
258
+ allowBacklog,
259
+ );
260
+ // `--allow-backlog`: a staged (`tasks-backlog`) body re-keys like the pool.
261
+ const inPool = folder === 'tasks-ready' || folder === 'tasks-backlog';
262
+ if (!inPool) {
263
+ return folder;
264
+ }
265
+ // In the pool/staging: claimed-ness + stuck-ness are the per-item lock, not the folder.
266
+ const lock = await readItemLock({item: `task:${slug}`, cwd, arbiter, env});
267
+ if (lock && lock.state === 'stuck') {
268
+ return 'needs-attention';
269
+ }
270
+ if (lock && lock.state === 'active') {
271
+ return 'in-progress';
272
+ }
273
+ return 'tasks-ready';
274
+ }
275
+
276
+ /** Dispatch the onboard by the slug's resolved (folder + lock) state. */
277
+ async function onboardFromFolder(
278
+ folder: Folder,
279
+ params: {
280
+ options: StartOptions;
281
+ slug: string;
282
+ arbiter: string;
283
+ cwd: string;
284
+ env: NodeJS.ProcessEnv | undefined;
285
+ note: (m: string) => void;
286
+ },
287
+ ): Promise<StartResult> {
288
+ const {options, slug, arbiter, cwd, env, note} = params;
289
+ switch (folder) {
290
+ case 'tasks-ready':
291
+ return startFromBacklog({
292
+ slug,
293
+ arbiter,
294
+ cwd,
295
+ env,
296
+ override: options.override ?? false,
297
+ note,
298
+ });
299
+ case 'in-progress':
300
+ return startFromInProgress({
301
+ slug,
302
+ arbiter,
303
+ cwd,
304
+ env,
305
+ resume: options.resume ?? false,
306
+ note,
307
+ });
308
+ case 'needs-attention':
309
+ return startFromNeedsAttention({slug, arbiter, cwd, env, note});
310
+ case 'done':
311
+ throw new StartUsageError(
312
+ `'${slug}' is already done on ${arbiter}/main — nothing to start.`,
313
+ );
314
+ case 'absent':
315
+ throw new StartUsageError(
316
+ `'${slug}' is not present on ${arbiter}/main (no backlog/in-progress item — wrong slug?).`,
317
+ );
318
+ }
319
+ }
320
+
321
+ /**
322
+ * Backlog → claim via the CAS, and ONLY on a winning claim (exit 0) switch to
323
+ * the work branch. A lost (exit 2) / contended (exit 3) claim is propagated
324
+ * verbatim: the user is left exactly as `claim` left them, with NO work branch.
325
+ */
326
+ async function startFromBacklog(params: {
327
+ slug: string;
328
+ arbiter: string;
329
+ cwd: string;
330
+ env: NodeJS.ProcessEnv | undefined;
331
+ override: boolean;
332
+ note: (m: string) => void;
333
+ }): Promise<StartResult> {
334
+ const {slug, arbiter, cwd, env, override, note} = params;
335
+
336
+ // The human-path readiness guard lives in the claim CAS (so both `claim` and
337
+ // `start` inherit it): an unmet `blockedBy` is refused (exit 1, 'not-ready')
338
+ // unless overridden; a `needsAnswers` task warns but still claims.
339
+ const claim = await performClaim({
340
+ slug,
341
+ cwd,
342
+ arbiter,
343
+ env,
344
+ humanPath: true,
345
+ override,
346
+ note,
347
+ });
348
+ if (claim.exitCode !== 0) {
349
+ // not-ready (1) / lost (2) / contended (3) / usage (1): behave exactly like
350
+ // `claim`. Create no work branch; propagate the exit code and outcome. A
351
+ // readiness refusal surfaces as 'refused' (a deliberate decline), distinct
352
+ // from a usage/environment error.
353
+ const outcome: StartOutcome =
354
+ claim.outcome === 'not-ready'
355
+ ? 'refused'
356
+ : claim.exitCode === 2
357
+ ? 'lost'
358
+ : claim.exitCode === 3
359
+ ? 'contended'
360
+ : 'usage-error';
361
+ return {exitCode: claim.exitCode, outcome, message: claim.message};
362
+ }
363
+
364
+ // The claim landed — the item is now in-progress on the arbiter. Onboard the
365
+ // human onto the work branch: CONTINUE from a kept arbiter work/<slug> when one
366
+ // exists ahead of main (a requeue), else cut fresh off the NEW main (which
367
+ // includes the claim).
368
+ const switched = await switchToWorkBranch({slug, arbiter, cwd, env, note});
369
+ if (switched.rebaseConflict) {
370
+ return continueConflictResult({slug, arbiter, cwd, env, note});
371
+ }
372
+ if (switched.pushFailure !== undefined) {
373
+ return continuePushFailureResult({
374
+ slug,
375
+ arbiter,
376
+ cwd,
377
+ pushFailure: switched.pushFailure,
378
+ env,
379
+ note,
380
+ });
381
+ }
382
+ const branch = switched.branch;
383
+ const message = switched.continued
384
+ ? `Started '${slug}': claimed and continued ${branch} from the kept branch.`
385
+ : `Started '${slug}': claimed and switched to ${branch}.`;
386
+ note(message);
387
+ return {exitCode: 0, outcome: 'started', branch, message};
388
+ }
389
+
390
+ /**
391
+ * Build the terminal StartResult for a CONTINUE rebase conflict: route the item
392
+ * to needs-attention (the §10 path) and return the 'needs-attention' outcome.
393
+ */
394
+ async function continueConflictResult(params: {
395
+ slug: string;
396
+ arbiter: string;
397
+ cwd: string;
398
+ env: NodeJS.ProcessEnv | undefined;
399
+ note: (m: string) => void;
400
+ }): Promise<StartResult> {
401
+ const surfaced = await routeContinueConflict(params);
402
+ if (!surfaced.moved) {
403
+ return surfaceUnmovedStartResult({slug: params.slug, surfaced});
404
+ }
405
+ const message =
406
+ `Could not continue '${params.slug}': the kept work branch did not rebase ` +
407
+ `cleanly onto ${params.arbiter}/main; routed to work/needs-attention/ ` +
408
+ '(surfaced by status). Resolve against the latest main, or `requeue --reset` ' +
409
+ 'to discard and start fresh.';
410
+ return {
411
+ exitCode: 1,
412
+ outcome: 'needs-attention',
413
+ branch: workBranchRef('task', params.slug),
414
+ message,
415
+ };
416
+ }
417
+
418
+ /**
419
+ * Build the terminal StartResult for a CONTINUE reconcile-push that FAILED
420
+ * TERMINALLY (the stale-lease retry cap exhausted, or a non-connectivity
421
+ * rejection like a protected ref) \u2014 NOT a tolerated offline arbiter. Route the
422
+ * item to needs-attention (the SAME \u00a712 surface the conflict path uses) so the
423
+ * already-committed kept work is NOT left silently in-progress (the
424
+ * stale-lease-strand bug this task kills), then return the 'needs-attention'
425
+ * outcome with a message naming the push failure.
426
+ */
427
+ async function continuePushFailureResult(params: {
428
+ slug: string;
429
+ arbiter: string;
430
+ cwd: string;
431
+ pushFailure: string;
432
+ env: NodeJS.ProcessEnv | undefined;
433
+ note: (m: string) => void;
434
+ }): Promise<StartResult> {
435
+ const surfaced = await routeContinuePushFailure(params);
436
+ if (!surfaced.moved) {
437
+ return surfaceUnmovedStartResult({slug: params.slug, surfaced});
438
+ }
439
+ const message =
440
+ `Could not continue '${params.slug}': publishing the rebased work branch to ` +
441
+ `${params.arbiter} failed terminally (${params.pushFailure}); routed to ` +
442
+ 'work/needs-attention/ (surfaced by status), the kept branch left intact on ' +
443
+ 'the arbiter (recoverable). `requeue` to retry once the churn settles.';
444
+ return {
445
+ exitCode: 1,
446
+ outcome: 'needs-attention',
447
+ branch: workBranchRef('task', params.slug),
448
+ message,
449
+ };
450
+ }
451
+
452
+ /**
453
+ * needs-attention → a runner bounced this stuck item. Picking it up is
454
+ * UNGUARDED (a stuck item is explicitly up-for-grabs — no --resume): print the
455
+ * recorded reason, transition it back to in-progress THROUGH the write seam
456
+ * (mode M clears the `main` surface via the reverse move), then switch onto
457
+ * work/<slug>. NO manual file move — the seam owns the transition.
458
+ */
459
+ async function startFromNeedsAttention(params: {
460
+ slug: string;
461
+ arbiter: string;
462
+ cwd: string;
463
+ env: NodeJS.ProcessEnv | undefined;
464
+ note: (m: string) => void;
465
+ }): Promise<StartResult> {
466
+ const {slug, arbiter, cwd, env, note} = params;
467
+
468
+ // Surface the recorded reason for the human, read from the STUCK LOCK ENTRY
469
+ // (task `cutover-needs-attention-becomes-lock-stuck-recovery-surface`: the lock
470
+ // is the sole stuck record — no `needs-attention/` folder file). The full reason
471
+ // prose + any surfaced questions ride on the lock entry body.
472
+ const stuck = await readItemLock({item: `task:${slug}`, cwd, arbiter, env});
473
+ if (stuck?.reason) {
474
+ note(`'${slug}' is stuck (needs-attention): ${stuck.reason}`);
475
+ } else {
476
+ note(`'${slug}' is stuck (needs-attention) — picking it up.`);
477
+ }
478
+ if (stuck?.questions && stuck.questions.length > 0) {
479
+ note('Surfaced questions:');
480
+ for (const q of stuck.questions) {
481
+ note(` - ${q}`);
482
+ }
483
+ }
484
+
485
+ // Resume the lock `stuck → active` THROUGH the write seam (a pure lock amend, NO
486
+ // `main` write, NO temp checkout — the body already rests in `backlog/` and the
487
+ // work stays on the kept `work/<slug>` branch), then onboard onto work/<slug>.
488
+ const resolved = await ledgerWrite.applyResolveNeedsAttentionTransition({
489
+ cwd,
490
+ slug,
491
+ arbiter,
492
+ env,
493
+ note,
494
+ });
495
+ if (!resolved.moved) {
496
+ throw new StartUsageError(
497
+ resolved.reasonNotMoved ??
498
+ `could not resolve '${slug}' from needs-attention.`,
499
+ );
500
+ }
501
+
502
+ // The lock is back to active (the human owns it). Onboard onto the work branch:
503
+ // CONTINUE from a kept arbiter work/<slug> when present, else fresh off main.
504
+ const switched = await switchToWorkBranch({slug, arbiter, cwd, env, note});
505
+ if (switched.rebaseConflict) {
506
+ return continueConflictResult({slug, arbiter, cwd, env, note});
507
+ }
508
+ if (switched.pushFailure !== undefined) {
509
+ return continuePushFailureResult({
510
+ slug,
511
+ arbiter,
512
+ cwd,
513
+ pushFailure: switched.pushFailure,
514
+ env,
515
+ note,
516
+ });
517
+ }
518
+ const branch = switched.branch;
519
+ const message = `Started '${slug}': resolved from needs-attention and switched to ${branch}.`;
520
+ note(message);
521
+ return {exitCode: 0, outcome: 'resolved', branch, message};
522
+ }
523
+
524
+ /**
525
+ * In-progress → already claimed (it is no longer in backlog, so the CAS cannot
526
+ * apply). Refuse by default. With --resume the human explicitly asserts
527
+ * ownership, so we switch to the work branch WITHOUT claiming. The decision is
528
+ * folder-based; we never read the advisory `claimed_by` to guess ownership.
529
+ */
530
+ async function startFromInProgress(params: {
531
+ slug: string;
532
+ arbiter: string;
533
+ cwd: string;
534
+ env: NodeJS.ProcessEnv | undefined;
535
+ resume: boolean;
536
+ note: (m: string) => void;
537
+ }): Promise<StartResult> {
538
+ const {slug, arbiter, cwd, env, resume, note} = params;
539
+
540
+ if (!resume) {
541
+ // The folder drove the decision (WORK-CONTRACT rule 6: the folder + git
542
+ // history are the source of truth). We do NOT name the claimer in the
543
+ // message; whoever holds it is read from git history
544
+ // (`git log work/in-progress/<slug>.md`).
545
+ throw new StartRefusal(
546
+ `'${slug}' is already in-progress; see \`git log\` for who claimed it; ` +
547
+ 'if this is your own work, re-run with --resume.',
548
+ );
549
+ }
550
+
551
+ const switched = await switchToWorkBranch({slug, arbiter, cwd, env, note});
552
+ if (switched.rebaseConflict) {
553
+ return continueConflictResult({slug, arbiter, cwd, env, note});
554
+ }
555
+ if (switched.pushFailure !== undefined) {
556
+ return continuePushFailureResult({
557
+ slug,
558
+ arbiter,
559
+ cwd,
560
+ pushFailure: switched.pushFailure,
561
+ env,
562
+ note,
563
+ });
564
+ }
565
+ const branch = switched.branch;
566
+ const message = `Resumed '${slug}': switched to ${branch} (no claim).`;
567
+ note(message);
568
+ return {exitCode: 0, outcome: 'resumed', branch, message};
569
+ }
570
+
571
+ /**
572
+ /** The outcome of onboarding the checkout onto `work/<slug>`. */
573
+ interface SwitchResult {
574
+ /** The work branch landed on (`work/<slug>`). */
575
+ branch: string;
576
+ /** True iff we CONTINUED from a kept arbiter `work/<slug>` (not a fresh cut). */
577
+ continued: boolean;
578
+ /**
579
+ * True iff a CONTINUE rebase onto fresh main CONFLICTED (and was aborted,
580
+ * never auto-resolved). The caller routes the item to needs-attention (§10).
581
+ */
582
+ rebaseConflict: boolean;
583
+ /**
584
+ * Set iff the CONTINUE reconcile push to the arbiter FAILED TERMINALLY (the
585
+ * stale-lease retry cap was exhausted, or a non-connectivity rejection such as
586
+ * a protected ref) — NOT a tolerated offline/unreachable arbiter (which leaves
587
+ * the local rebased branch standing for `complete`'s later push, exactly as
588
+ * before). When present, the caller routes the item to needs-attention so the
589
+ * already-committed kept work is NOT left silently in-progress (the
590
+ * stale-lease-strand bug). Absent on a fresh cut, a clean continue that pushed,
591
+ * and the tolerated-offline case.
592
+ */
593
+ pushFailure?: string;
594
+ }
595
+
596
+ /**
597
+ * Onboard the checkout onto `work/<slug>`, CONTINUE-AWARE (the keystone of the
598
+ * `requeue-continue-and-reset` task). `git fetch <arbiter>`, then:
599
+ *
600
+ * - **CONTINUE** when the arbiter has a `work/<slug>` ref AHEAD of main (a
601
+ * `requeue` kept it): switch onto that kept branch (a local tracking branch
602
+ * off `<arbiter>/work/<slug>`) and REBASE it onto the freshly-fetched
603
+ * `<arbiter>/main` at onboard-time, so the agent builds on a CURRENT base
604
+ * (ADR §10: rebase, not merge). A CLEAN rebase continues; a CONFLICTING
605
+ * rebase is aborted (never auto-resolved) and reported so the caller routes
606
+ * to needs-attention. The rebased tip is published to the already-pushed
607
+ * work branch with `--force-with-lease` on the WORK branch only (a requeued
608
+ * item is unshared) — NEVER `--force` to main (§11).
609
+ * - **FRESH** otherwise (no kept branch — a first attempt, or a
610
+ * `requeue --reset` deleted it): `git switch -c work/<slug> <arbiter>/main`
611
+ * (today's behaviour). If the branch already exists LOCALLY (a re-run /
612
+ * resume) plain-switch to it.
613
+ */
614
+ async function switchToWorkBranch(params: {
615
+ slug: string;
616
+ arbiter: string;
617
+ cwd: string;
618
+ env: NodeJS.ProcessEnv | undefined;
619
+ note: (m: string) => void;
620
+ }): Promise<SwitchResult> {
621
+ const {slug, arbiter, cwd, env, note} = params;
622
+ // `start` is a TASK-only command: the branch is the task-namespaced
623
+ // `work/task-<slug>` (distinct from a same-slug prd-tasking branch).
624
+ const branch = workBranchRef('task', slug);
625
+
626
+ await gitHard(['fetch', '--quiet', arbiter], cwd, env);
627
+
628
+ // CONTINUE-detection (shared with the job-worktree path): does the arbiter
629
+ // have a `work/<slug>` ref AHEAD of main? ARBITER-AUTHORITATIVE: `ls-remote`
630
+ // the arbiter so a STALE local remote-tracking ref (a plain `git fetch` does
631
+ // NOT prune unless `fetch.prune` is set — verified live in the task obs)
632
+ // pointing at a branch the arbiter no longer has cannot resurrect a deleted
633
+ // branch as a "continue". In a normal clone the local refs are the
634
+ // remote-tracking `<arbiter>/work/<slug>` and `<arbiter>/main`.
635
+ const ahead = branchAheadOfArbiter({
636
+ cwd,
637
+ arbiterRemote: arbiter,
638
+ branch,
639
+ branchRef: `${arbiter}/${branch}`,
640
+ mainRef: `${arbiter}/main`,
641
+ env,
642
+ });
643
+ if (ahead) {
644
+ return continueFromKeptBranch({slug, arbiter, cwd, env, note});
645
+ }
646
+
647
+ // FRESH cut: prefer creating the branch off the latest arbiter main (it
648
+ // includes the claim move). If it already exists locally, plain-switch to it.
649
+ const created = await gitSoft(
650
+ ['switch', '--quiet', '-c', branch, `${arbiter}/main`],
651
+ cwd,
652
+ env,
653
+ );
654
+ if (created.status === 0) {
655
+ return {branch, continued: false, rebaseConflict: false};
656
+ }
657
+ const switched = await gitSoft(['switch', '--quiet', branch], cwd, env);
658
+ if (switched.status === 0) {
659
+ return {branch, continued: false, rebaseConflict: false};
660
+ }
661
+ throw new StartUsageError(
662
+ `failed to switch to ${branch}: ${created.stderr.trim() || switched.stderr.trim()}`,
663
+ );
664
+ }
665
+
666
+ /**
667
+ * CONTINUE onto a kept arbiter `work/<slug>`: switch onto it (resetting any
668
+ * stale local branch to the arbiter tip), rebase onto the freshly-fetched
669
+ * `<arbiter>/main`, and — on a CLEAN rebase — update the already-pushed work
670
+ * branch with `--force-with-lease` on the WORK branch only (never main, §11). A
671
+ * CONFLICTING rebase is aborted and reported (`rebaseConflict: true`).
672
+ */
673
+ async function continueFromKeptBranch(params: {
674
+ slug: string;
675
+ arbiter: string;
676
+ cwd: string;
677
+ env: NodeJS.ProcessEnv | undefined;
678
+ note: (m: string) => void;
679
+ }): Promise<SwitchResult> {
680
+ const {slug, arbiter, cwd, env, note} = params;
681
+ const branch = workBranchRef('task', slug);
682
+ note(`Continuing '${slug}' from the kept ${arbiter}/${branch} (requeue).`);
683
+
684
+ // Land the local work branch ON the kept arbiter tip (force-reset a stale
685
+ // local branch so we continue the SAME single branch, not a divergent copy).
686
+ await gitHard(
687
+ ['switch', '--quiet', '-C', branch, `${arbiter}/${branch}`],
688
+ cwd,
689
+ env,
690
+ );
691
+
692
+ // REBASE onto the freshly-fetched main at onboard-time (§10: rebase, not
693
+ // merge) so the agent builds on a CURRENT base. Conflict → aborted + reported.
694
+ const rebase = rebaseContinuedBranchOntoMain(cwd, `${arbiter}/main`, env);
695
+ if (rebase.kind === 'conflict') {
696
+ return {branch, continued: true, rebaseConflict: true};
697
+ }
698
+
699
+ // The arbiter `work/<slug>` tip the fetch above brought down, READ AFTER the
700
+ // rebase — the rebase rewrote only the LOCAL branch, so the remote-tracking ref
701
+ // `<arbiter>/<branch>` still holds the PRE-rebase arbiter sha (the value the
702
+ // --force-with-lease push expects the arbiter to still hold). It is non-empty
703
+ // here: `branchAheadOf` proved the ref resolves (the CONTINUE precondition).
704
+ const expectedRemoteTip = (
705
+ await gitHard(['rev-parse', `${arbiter}/${branch}`], cwd, env)
706
+ ).stdout.trim();
707
+
708
+ // The rebase may have rewritten SHAs vs the already-pushed tip, so updating it
709
+ // is a non-fast-forward. Reconcile with --force-with-lease on the WORK branch
710
+ // ONLY (a requeued item is unshared) — NEVER --force, and NEVER to main (§11),
711
+ // SURVIVING a stale-lease ("stale info") rejection by re-fetching + re-rebasing
712
+ // + retrying (the SAME helper `workspace.ts`/`isolation.ts` use).
713
+ //
714
+ // BEST-EFFORT, but DISCRIMINATING (NOT a blanket swallow — the silent-strand
715
+ // bug this guard kills): the helper THROWS, so we catch it and split the cause.
716
+ // An OFFLINE / unreachable-arbiter throw is TOLERATED exactly as the bare
717
+ // `gitSoft` push was — the local rebased branch is left, and `complete`'s push
718
+ // handles it later. A REAL terminal failure (the stale-lease retry cap, or a
719
+ // non-connectivity rejection like a protected ref) is NOT swallowed: it is
720
+ // reported up as `pushFailure` so the caller surfaces the item to
721
+ // needs-attention rather than leaving the committed kept work silently
722
+ // in-progress.
723
+ try {
724
+ const pushed = pushContinuedBranchWithStaleLeaseRetry({
725
+ cwd,
726
+ branch,
727
+ arbiter,
728
+ mainRef: `${arbiter}/main`,
729
+ expectedRemoteTip,
730
+ env,
731
+ note,
732
+ });
733
+ if (pushed.kind === 'conflict') {
734
+ return {branch, continued: true, rebaseConflict: true};
735
+ }
736
+ } catch (err) {
737
+ const message = err instanceof Error ? err.message : String(err);
738
+ if (isOfflinePushFailure(message)) {
739
+ // Tolerated (today's behaviour): the arbiter is unreachable, so the local
740
+ // rebased branch is left and `complete`'s later push handles it.
741
+ note(
742
+ `Could not push ${branch} to ${arbiter} (arbiter unreachable) — the ` +
743
+ 'local rebased branch is left; `complete` pushes it later.',
744
+ );
745
+ } else {
746
+ // A REAL terminal failure — surface it (do NOT silently strand the work).
747
+ return {
748
+ branch,
749
+ continued: true,
750
+ rebaseConflict: false,
751
+ pushFailure: message,
752
+ };
753
+ }
754
+ }
755
+ return {branch, continued: true, rebaseConflict: false};
756
+ }
757
+
758
+ /**
759
+ * Discriminate the THROW from {@link pushContinuedBranchWithStaleLeaseRetry} at
760
+ * the `start` continue-push site: is it a TOLERATED offline/unreachable-arbiter
761
+ * outage (the existing best-effort case — `complete` pushes later), or a REAL
762
+ * terminal failure that must SURFACE (the stale-lease retry cap exhausted, or a
763
+ * non-connectivity rejection like a protected ref)?
764
+ *
765
+ * The helper throws ONLY two shapes: a `(not a stale lease)` push failure
766
+ * carrying the raw git stderr, or a `kept failing with a stale --force-with-lease`
767
+ * cap-exhausted message. The cap message is ALWAYS a real give-up. A non-stale
768
+ * push failure is OFFLINE only when its stderr matches a git connectivity
769
+ * signature (unable to access / could not read from remote / connection refused /
770
+ * could not resolve host / ssh connect) — anything else (a protected ref, a hook
771
+ * rejection) is a real failure that must surface. Conservative: an unmatched
772
+ * cause is treated as a REAL failure (surface), never silently swallowed.
773
+ */
774
+ function isOfflinePushFailure(message: string): boolean {
775
+ // The retry-cap give-up is never "offline" — it is a real terminal failure.
776
+ if (/kept failing with a stale --force-with-lease/i.test(message)) {
777
+ return false;
778
+ }
779
+ return /unable to access|could not read from remote repository|connection (?:refused|reset|timed out)|could not resolve host|ssh: connect to host|network is unreachable|\bECONN(?:REFUSED|RESET)\b|\bENOTFOUND\b|\bETIMEDOUT\b/i.test(
780
+ message,
781
+ );
782
+ }
783
+
784
+ /**
785
+ * Build the HONEST {@link StartResult} for a CONTINUE-site surface that did NOT
786
+ * land on the arbiter (`{moved: false}`). The tree-less `in-progress/ →
787
+ * needs-attention/` move lost the CAS race against a busy arbiter (its
788
+ * contention-retry cap exhausted), so the item is STILL in-progress on the
789
+ * arbiter — a clean `needs-attention` outcome would mislead (it claims the
790
+ * surface landed). The DISTINCT `surface-unmoved` outcome carries
791
+ * `reasonNotMoved` so the human can tell it apart from a successful surface and
792
+ * retry/resolve.
793
+ */
794
+ function surfaceUnmovedStartResult(params: {
795
+ slug: string;
796
+ surfaced: SurfaceToNeedsAttentionResult;
797
+ }): StartResult {
798
+ const {slug, surfaced} = params;
799
+ const message =
800
+ `'${slug}' could NOT be surfaced to needs-attention — the surface did not ` +
801
+ `reach the arbiter's main; the item is still IN-PROGRESS on the arbiter ` +
802
+ `(retry/resolve). ${surfaced.reasonNotMoved ?? ''}`.trim();
803
+ return {
804
+ exitCode: 1,
805
+ outcome: 'surface-unmoved',
806
+ branch: workBranchRef('task', slug),
807
+ message,
808
+ };
809
+ }
810
+
811
+ /**
812
+ * Route a CONTINUE rebase conflict to needs-attention (the §10 path): the kept
813
+ * branch's commits did not replay cleanly onto the current main, so a human must
814
+ * resolve (or `requeue --reset`).
815
+ *
816
+ * TREE-LESS (`#89` mechanism), the SAME-PROFILE sibling of
817
+ * {@link routeContinuePushFailure}: the rebase was ABORTED, so the REAL continued
818
+ * `work/<slug>` is already on the arbiter from the prior requeue, untouched
819
+ * (after-commit, durable, recoverable). The surface is PURELY the one-file
820
+ * `in-progress/ → needs-attention/` ledger `.md` move + reason — we publish it via
821
+ * the tree-less surface CAS (the SAME no-checkout primitive `requeue` uses for the
822
+ * reverse direction), NO temp-branch switch/restore, NO `pushBranch`, NO worktree.
823
+ */
824
+ async function routeContinueConflict(params: {
825
+ slug: string;
826
+ arbiter: string;
827
+ cwd: string;
828
+ env: NodeJS.ProcessEnv | undefined;
829
+ note: (m: string) => void;
830
+ }): Promise<SurfaceToNeedsAttentionResult> {
831
+ const {slug, arbiter, cwd, env, note} = params;
832
+ const reason =
833
+ `continuing the kept ${arbiter}/${workBranchRef('task', slug)}: rebase onto ` +
834
+ `${arbiter}/main conflicted (aborted, never auto-resolved) — resolve ` +
835
+ 'against the latest main, or `requeue --reset` to discard and start fresh';
836
+ note(reason);
837
+ // PROPAGATE the {moved, reasonNotMoved} result to the caller so a `moved:false`
838
+ // (the surface did NOT reach main — lost the CAS race) is surfaced honestly
839
+ // instead of swallowed as `void`.
840
+ return ledgerWrite.applyTreelessNeedsAttentionTransition({
841
+ cwd,
842
+ slug,
843
+ reason,
844
+ arbiter,
845
+ env,
846
+ note,
847
+ });
848
+ }
849
+
850
+ /**
851
+ * Route a CONTINUE reconcile-push TERMINAL failure to needs-attention (the §12
852
+ * surface): the rebased work branch could not be published to the arbiter (the
853
+ * stale-lease retry cap exhausted, or a non-connectivity rejection) — so rather
854
+ * than leave the committed kept work silently in-progress, surface it on the
855
+ * arbiter's main. Mirrors {@link routeContinueConflict}'s temp-branch pattern.
856
+ *
857
+ * TREE-LESS (`#89` mechanism): the recoverable artifact — the kept `work/<slug>`
858
+ * — is ALREADY on the arbiter from the prior requeue (our local rebased tip is
859
+ * what FAILED to push, so it is not the cross-machine truth), and the work is
860
+ * already committed. So the surface is PURELY the one-file `in-progress/ →
861
+ * needs-attention/` ledger `.md` move + reason: we publish it via the tree-less
862
+ * surface CAS (the SAME no-checkout primitive `requeue` uses for the reverse
863
+ * direction) — NO temp-branch switch/restore, NO `pushBranch`, NO worktree. A
864
+ * `requeue` then continues from the kept arbiter tip.
865
+ */
866
+ async function routeContinuePushFailure(params: {
867
+ slug: string;
868
+ arbiter: string;
869
+ cwd: string;
870
+ pushFailure: string;
871
+ env: NodeJS.ProcessEnv | undefined;
872
+ note: (m: string) => void;
873
+ }): Promise<SurfaceToNeedsAttentionResult> {
874
+ const {slug, arbiter, cwd, pushFailure, env, note} = params;
875
+ const reason =
876
+ `continuing the kept ${arbiter}/${workBranchRef('task', slug)}: publishing ` +
877
+ `the rebased work branch to ${arbiter} failed terminally (${pushFailure}) — ` +
878
+ 'the kept branch is left intact on the arbiter (recoverable); `requeue` to ' +
879
+ 'retry once the churn settles, or `requeue --reset` to discard and start fresh';
880
+ note(reason);
881
+ // PROPAGATE the {moved, reasonNotMoved} result to the caller so a `moved:false`
882
+ // (the surface did NOT reach main — lost the CAS race) is surfaced honestly
883
+ // instead of swallowed as `void`.
884
+ return ledgerWrite.applyTreelessNeedsAttentionTransition({
885
+ cwd,
886
+ slug,
887
+ reason,
888
+ arbiter,
889
+ env,
890
+ note,
891
+ });
892
+ }
893
+
894
+ /** If HEAD is a `work/<type>-<slug>` branch, return `<slug>`; else ''. */
895
+ async function inferSlugFromBranch(
896
+ cwd: string,
897
+ env: NodeJS.ProcessEnv | undefined,
898
+ ): Promise<string> {
899
+ const sym = await gitSoft(
900
+ ['symbolic-ref', '--quiet', '--short', 'HEAD'],
901
+ cwd,
902
+ env,
903
+ );
904
+ if (sym.status !== 0) {
905
+ return '';
906
+ }
907
+ return parseWorkBranchRef(sym.stdout.trim())?.slug ?? '';
908
+ }
909
+
910
+ /**
911
+ * Which work/ folder the slug currently lives in on `<arbiter>/main`. Under
912
+ * `--allow-backlog` (prd `do-allow-backlog-drive-staged-tasks-without-promotion`)
913
+ * it ALSO recognises `tasks/backlog/` (staging) at LOWEST priority — after the
914
+ * pool — so a same-slug pool copy still wins; otherwise staging is invisible.
915
+ */
916
+ async function folderOnArbiterMain(
917
+ slug: string,
918
+ arbiter: string,
919
+ cwd: string,
920
+ env: NodeJS.ProcessEnv | undefined,
921
+ allowBacklog = false,
922
+ ): Promise<
923
+ | 'tasks-ready'
924
+ | 'tasks-backlog'
925
+ | 'in-progress'
926
+ | 'needs-attention'
927
+ | 'done'
928
+ | 'absent'
929
+ > {
930
+ const folders: (
931
+ | 'tasks-ready'
932
+ | 'tasks-backlog'
933
+ | 'in-progress'
934
+ | 'needs-attention'
935
+ | 'done'
936
+ )[] = [
937
+ 'tasks-ready',
938
+ ...(allowBacklog ? (['tasks-backlog'] as const) : []),
939
+ 'in-progress',
940
+ 'needs-attention',
941
+ 'done',
942
+ ];
943
+ for (const folder of folders) {
944
+ const object = `${arbiter}/main:${workFolderRel(folder)}/${slug}.md`;
945
+ if ((await gitSoft(['cat-file', '-e', object], cwd, env)).status === 0) {
946
+ return folder;
947
+ }
948
+ }
949
+ return 'absent';
950
+ }
951
+
952
+ /** Run git, returning the raw result (no throw) — for soft checks. */
953
+ function gitSoft(
954
+ args: string[],
955
+ cwd: string,
956
+ env: NodeJS.ProcessEnv | undefined,
957
+ ): Promise<RunResult> {
958
+ return runAsync('git', args, cwd, {env});
959
+ }
960
+
961
+ /** Run git; throw on non-zero (genuinely unexpected plumbing failures). */
962
+ async function gitHard(
963
+ args: string[],
964
+ cwd: string,
965
+ env: NodeJS.ProcessEnv | undefined,
966
+ ): Promise<RunResult> {
967
+ const result = await runAsync('git', args, cwd, {env});
968
+ if (result.status !== 0) {
969
+ throw new Error(
970
+ `git ${args.join(' ')} failed (exit ${result.status}): ${result.stderr.trim()}`,
971
+ );
972
+ }
973
+ return result;
974
+ }