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
@@ -0,0 +1,865 @@
1
+ import {randomUUID} from 'node:crypto';
2
+ import {runAsync, type RunResult} from './git.js';
3
+ import {
4
+ Integrator,
5
+ type IntegrateResult,
6
+ type ReviewProvider,
7
+ } from './integrator.js';
8
+ import type {IntegrationMode} from './config.js';
9
+ import {
10
+ routeToNeedsAttention,
11
+ returnToBacklog,
12
+ type RouteToNeedsAttentionOptions,
13
+ type RouteToNeedsAttentionResult,
14
+ type ReturnToBacklogOptions,
15
+ type ReturnToBacklogResult,
16
+ type SurfaceToNeedsAttentionOptions,
17
+ type SurfaceToNeedsAttentionResult,
18
+ type BranchPushOutcome,
19
+ } from './needs-attention.js';
20
+ import {
21
+ markStuckItemLock,
22
+ resumeItemLock,
23
+ type TransitionOutcome,
24
+ } from './item-lock.js';
25
+
26
+ /**
27
+ * The **write half** of the ledger-transition seam (ADR
28
+ * `docs/adr/claim-ledger-vs-protected-main.md`, status: accepted — the "Write
29
+ * seam"). ONE entry point — "apply this `work/` transition" — that every
30
+ * transition (claim / complete / needs-attention) routes through, so a FUTURE
31
+ * strategy could publish a transition elsewhere (e.g. a dedicated `main`-free
32
+ * ledger ref) without the transition call sites learning a new mechanism.
33
+ *
34
+ * It is a PURE REFACTOR: there is exactly ONE strategy ({@link
35
+ * currentLedgerWrite}) and it does EXACTLY what the code did before — it
36
+ * CAS-publishes the prepared transition commit to the arbiter's `main` with
37
+ * `--force-with-lease` and then verifies `<arbiter>/main` is now that commit. No
38
+ * mode, no config, no `ledgerMode`, no new ref.
39
+ *
40
+ * The seam stays at the SEMANTIC level: the caller hands the seam a *prepared*
41
+ * transition (a local branch carrying the commit, the base the ledger must still
42
+ * be at for the CAS, and the commit it expects to land) plus the transition
43
+ * KIND, and asks the seam to publish it. The public input is storage-agnostic —
44
+ * it does NOT name `main`; that `main` is the publish/verify target is an
45
+ * implementation detail of the sole strategy below.
46
+ *
47
+ * This task routes the CLAIM transition through the seam (see `claim-cas.ts`).
48
+ * The `complete` and `needs-attention` kinds are named here so the companion
49
+ * tasks route through the SAME entry point; they are not yet wired.
50
+ *
51
+ * The NEEDS-ATTENTION transition is wired here too: the abort paths in
52
+ * `complete.ts` (red gate, rebase conflict), the runner's stuck routing in
53
+ * `run.ts`, and the human `return` command all drive the
54
+ * `* → needs-attention` move (and its `needs-attention → backlog` re-queue)
55
+ * through this SAME seam rather than calling the move helpers directly. The
56
+ * sole strategy delegates to the folder-native mechanism in
57
+ * `needs-attention.ts` UNCHANGED (reason-in-the-body — WORK-CONTRACT rule 3,
58
+ * bounce from in-progress OR done, ONE atomic commit, optional branch push); the
59
+ * seam only relocates WHERE the "apply the needs-attention transition" call is
60
+ * expressed, so the later cherry-pick-to-`main` surfacing is built AGAINST the
61
+ * seam, not bolted onto the move code.
62
+ */
63
+
64
+ /**
65
+ * The `work/` lifecycle transitions the write seam can apply.
66
+ *
67
+ * `tasking` is the legacy tasking-lock MARKER transition. It is now VESTIGIAL: the
68
+ * capstone cut-over (task
69
+ * `cutover-retire-slicing-advancing-markers-and-trim-folder-sets`) retired the
70
+ * `git mv work/prd/<slug>.md → work/tasking/<slug>.md` marker (the historical
71
+ * marker paths), so the tasking lock
72
+ * is the unified per-item lock ref (`tasking-lock.ts`) and no longer routes through
73
+ * {@link applyTransition} with this kind. The member is kept so the strategy
74
+ * interface and any historical reference stay valid; nothing publishes it.
75
+ *
76
+ * `advancing` is the kind {@link createItemThroughCas} (`advancing-lock.ts`) uses
77
+ * to publish a NEW `work/` item (the triage observation→promote path) through the
78
+ * SAME CAS the `claim` transition uses, keyed on the new item's identity (its
79
+ * path). The advancing-lock BORROW itself no longer rides this kind: the
80
+ * `work/advancing/<entry>.md` presence-marker is retired (the borrow is the unified
81
+ * `action: advance` lock ref now). So `advancing` survives only as the create-item
82
+ * CAS kind.
83
+ */
84
+ export type LedgerTransitionKind =
85
+ | 'claim'
86
+ | 'complete'
87
+ | 'needs-attention'
88
+ | 'requeue'
89
+ | 'tasking'
90
+ | 'advancing'
91
+ /**
92
+ * The **promote** transition (prd `staging-pool-position-gate-and-trust-model`,
93
+ * task `pre-backlog-staging-folder-and-promote-step-a`): move a STAGED task
94
+ * `work/pre-backlog/<slug>.md → work/backlog/<slug>.md` to enter the
95
+ * agent-eligible pool. A durable `main` move, the same category as `requeue`
96
+ * (tree-less CAS via {@link applyTransition}). RUNNER/human-owned — no
97
+ * agent-facing path performs it (governing ADR
98
+ * `placement-is-runner-deterministic-humanonly-is-agent-judgement`).
99
+ */
100
+ | 'promote';
101
+
102
+ /**
103
+ * A *prepared* COMPLETE transition the caller asks the seam to publish: a
104
+ * finished work branch whose code should be integrated back to the arbiter. Like
105
+ * {@link ApplyTransitionInput} it is storage-agnostic — it names the work branch
106
+ * + the integration MODE + the review provider, NOT *where* the integration
107
+ * lands (the sole strategy decides that; today `merge` ff's to the arbiter's
108
+ * `main`, `propose` pushes the branch + requests review). The caller has already
109
+ * done the gate / done-move / commit / rebase-onto-arbiter — the seam only
110
+ * APPLIES the integration of that prepared branch.
111
+ */
112
+ export interface ApplyCompleteTransitionInput {
113
+ /** Name of the arbiter git remote the integration is published to. */
114
+ arbiter: string;
115
+ /** The prepared (gated, committed, rebased) work branch to integrate. */
116
+ branch: string;
117
+ /** Integration mode: `merge` (ff to the ledger) or `propose` (push + review). */
118
+ mode: IntegrationMode;
119
+ /** The review-request provider (propose mode); push-only `none` otherwise. */
120
+ provider: ReviewProvider;
121
+ /**
122
+ * **The PR-INTENT axis** (config `noPR`, ADR §6): when `true` on the propose
123
+ * path, push the branch but SKIP the review request (the explicit suppress-PR
124
+ * intent). Threaded to {@link Integrator.integrate}. Ignored in `merge` mode.
125
+ */
126
+ noPR?: boolean;
127
+ /**
128
+ * Optional single-line review-request TITLE (propose mode), threaded straight
129
+ * to the provider. Absent ⇒ the provider's `--fill` default (no regression).
130
+ */
131
+ title?: string;
132
+ /**
133
+ * Optional review-request BODY (propose mode) — advisory prose, gates nothing —
134
+ * threaded straight to the provider. Absent ⇒ the provider's `--fill` default.
135
+ */
136
+ body?: string;
137
+ /**
138
+ * **Reap the remote head branch inline after a merge lands** (the merged-branch
139
+ * hygiene task's part (b)). When `true` on the `merge` path, delete the remote
140
+ * `work/<slug>` head AFTER the work landed on `main` (provably merged, so
141
+ * ancestor-safe; idempotent no-op when no remote head exists). Threaded straight
142
+ * to {@link Integrator.integrate}. Ignored in `propose` mode (the branch is the
143
+ * review surface, reaped later by `gc --remote-branches`).
144
+ */
145
+ deleteMergedHead?: boolean;
146
+ /** Working clone/worktree the integration runs in. */
147
+ cwd: string;
148
+ /** Environment for child git processes. */
149
+ env?: NodeJS.ProcessEnv;
150
+ }
151
+
152
+ /**
153
+ * The outcome of asking the seam to apply (publish) a prepared COMPLETE
154
+ * transition. It is exactly the integration result the {@link Integrator}
155
+ * produces — the seam adds NO interpretation, it only relocates WHERE the
156
+ * "apply the complete transition" call is expressed.
157
+ */
158
+ export type ApplyCompleteTransitionResult = IntegrateResult;
159
+
160
+ /**
161
+ * A *prepared* NEEDS-ATTENTION transition the caller asks the seam to apply: a
162
+ * stuck claimed item to bounce to `work/needs-attention/` with its reason. Like
163
+ * the other inputs it is storage-agnostic — it names the slug, the reason prose,
164
+ * any surfaced questions, and an OPTIONAL arbiter to also push the branch to,
165
+ * NOT *where* the move commits/publishes (the sole strategy decides that: a
166
+ * `git mv` from whichever of in-progress/ or done/ holds the item, the
167
+ * reason-in-the-body, the ONE atomic commit, the optional branch push). This
168
+ * mirrors {@link RouteToNeedsAttentionOptions} so the move mechanism is unchanged.
169
+ */
170
+ export type ApplyNeedsAttentionTransitionInput = RouteToNeedsAttentionOptions;
171
+
172
+ /**
173
+ * The outcome of asking the seam to apply a NEEDS-ATTENTION transition — the
174
+ * move result the folder-native mechanism produces. The RECOVERABLE branch push
175
+ * outcome rides on `branchPush` (the caller reads it rather than assuming
176
+ * "pushed" off the local move). The OBSERVABLE half is now the per-item lock
177
+ * `state: stuck` amend (prd `ledger-status-per-item-lock-refs`); there is no
178
+ * separate on-`main` surface outcome to report.
179
+ */
180
+ export type ApplyNeedsAttentionTransitionResult = RouteToNeedsAttentionResult;
181
+
182
+ /**
183
+ * A *prepared* RETURN-TO-BACKLOG transition: re-queue a STUCK item so it can be
184
+ * re-claimed — recovered from EITHER `needs-attention/` (the resolved-surface
185
+ * path) OR `in-progress/` (a claim that never surfaced), the slug's actual
186
+ * current folder resolved on the arbiter and moved to `backlog/`.
187
+ * Storage-agnostic, mirroring {@link ReturnToBacklogOptions}.
188
+ */
189
+ export type ApplyReturnToBacklogTransitionInput = ReturnToBacklogOptions;
190
+
191
+ /**
192
+ * The outcome of asking the seam to apply a RETURN-TO-BACKLOG transition. It is
193
+ * a Promise: like `claim`, the tree-less strategy fetches + CAS-pushes to the
194
+ * arbiter (async).
195
+ */
196
+ export type ApplyReturnToBacklogTransitionResult =
197
+ Promise<ReturnToBacklogResult>;
198
+
199
+ /**
200
+ * A *prepared* TREE-LESS NEEDS-ATTENTION (surface) transition: surface a stuck
201
+ * AFTER-COMMIT, LEDGER-ONLY item (`in-progress/ → needs-attention/`, with the
202
+ * reason in the body) WITHOUT a checkout — the SURFACE-direction sibling of the
203
+ * tree-less requeue, sharing its exact mechanism. The work is ALREADY committed
204
+ * on the kept `work/<slug>` branch (intact on the arbiter, recoverable), so the
205
+ * surface is purely the one-file `.md` move + reason — no `pushBranch`, no
206
+ * worktree. Storage-agnostic, mirroring {@link SurfaceToNeedsAttentionOptions}.
207
+ * NOT for the wip-save / gate-failed / agent-failed surfaces (which may carry
208
+ * uncommitted work — those keep {@link applyNeedsAttentionTransition}, the
209
+ * cwd-bound path that can commit wip first).
210
+ */
211
+ export type ApplyTreelessNeedsAttentionTransitionInput =
212
+ SurfaceToNeedsAttentionOptions;
213
+
214
+ /**
215
+ * The outcome of asking the seam to apply a TREE-LESS NEEDS-ATTENTION (surface)
216
+ * transition. A Promise: like `requeue`/`claim`, the tree-less strategy fetches +
217
+ * CAS-pushes to the arbiter (async).
218
+ */
219
+ export type ApplyTreelessNeedsAttentionTransitionResult =
220
+ Promise<SurfaceToNeedsAttentionResult>;
221
+
222
+ /**
223
+ * A *prepared* RESOLVE-NEEDS-ATTENTION transition: a human is picking up a stuck
224
+ * item, so the seam must **clear the stuck state** and restore it to `active`.
225
+ * Storage-agnostic — it names the slug + the working clone (and an OPTIONAL
226
+ * arbiter to amend the lock on), NOT *where* the stuck state lives. The sole
227
+ * strategy clears it by amending the per-item lock `stuck → active` on the
228
+ * arbiter (task `cutover-needs-attention-becomes-lock-stuck-recovery-surface`).
229
+ */
230
+ export interface ApplyResolveNeedsAttentionTransitionInput {
231
+ /** The working clone the `work/` tree lives in. */
232
+ cwd: string;
233
+ /** The slug of the stuck item to resolve back to active. */
234
+ slug: string;
235
+ /**
236
+ * The arbiter remote whose lock ref to amend (`stuck → active`). Omitted ⇒ a
237
+ * recorded no-op success (no lock ref to amend; the human-local face).
238
+ * Storage-agnostic: it names the remote, not `main`.
239
+ */
240
+ arbiter?: string;
241
+ /** Environment for child git processes. */
242
+ env?: NodeJS.ProcessEnv;
243
+ /** Sink for human-readable progress notes. */
244
+ note?: (message: string) => void;
245
+ }
246
+
247
+ /** The outcome of asking the seam to apply a RESOLVE-NEEDS-ATTENTION transition. */
248
+ export interface ApplyResolveNeedsAttentionTransitionResult {
249
+ /** True iff the lock was amended back to active (or the no-arbiter no-op). */
250
+ moved: boolean;
251
+ /** When NOT moved, why (no held stuck lock, contention, …). */
252
+ reasonNotMoved?: string;
253
+ }
254
+
255
+ /**
256
+ * A *prepared* transition the caller asks the seam to publish. Storage-agnostic:
257
+ * it describes the transition semantically (a kind + a prepared local commit +
258
+ * the CAS lease), NOT *where* it should be published. The sole strategy decides
259
+ * that (today: the arbiter's `main`).
260
+ */
261
+ export interface ApplyTransitionInput {
262
+ /** Which `work/` transition this is (claim / complete / needs-attention). */
263
+ kind: LedgerTransitionKind;
264
+ /** Name of the arbiter git remote the transition is published to. */
265
+ arbiter: string;
266
+ /** Local branch carrying the prepared transition commit (its tip = {@link head}). */
267
+ localBranch: string;
268
+ /**
269
+ * The ledger commit the publish must be a fast-forward FROM — the
270
+ * compare-and-swap lease. If the ledger has moved past this, the publish is
271
+ * rejected (someone else advanced it under us).
272
+ */
273
+ expectedBase: string;
274
+ /** The commit sha the caller expects to become the ledger tip after publish. */
275
+ head: string;
276
+ /** Working clone/worktree the publish runs in. */
277
+ cwd: string;
278
+ /** Show the intended publish without mutating the arbiter (dry-run). */
279
+ dryRun?: boolean;
280
+ /** Environment for child git processes. */
281
+ env?: NodeJS.ProcessEnv;
282
+ /** Sink for human-readable progress notes. */
283
+ note?: (message: string) => void;
284
+ }
285
+
286
+ /** The outcome of asking the seam to publish a prepared transition. */
287
+ export interface ApplyTransitionResult {
288
+ /**
289
+ * `published` — the transition landed (and was verified to be the ledger tip).
290
+ * `rejected` — the CAS lease failed (the ledger moved under us); the caller
291
+ * decides whether to refetch+retry or give up. The seam never throws for this
292
+ * expected contended case.
293
+ */
294
+ kind: 'published' | 'rejected';
295
+ /** Human-readable summary of the terminal condition. */
296
+ message: string;
297
+ /**
298
+ * The sha that ACTUALLY landed on the ledger when `published`. The seam stamps
299
+ * each attempt's tip with a fresh `CAS-Nonce` trailer (so the pushed sha is
300
+ * unique), so the landed commit is NOT the caller's pre-nonce `head` — it is
301
+ * this nonce'd descendant-in-content. Callers that branch/track/report off the
302
+ * landed commit (e.g. `claim`'s work-branch hint) MUST use this, not their
303
+ * input `head`. Absent on `rejected` and on `dryRun`.
304
+ */
305
+ publishedHead?: string;
306
+ }
307
+
308
+ /**
309
+ * The write-seam interface: ONE entry point — apply (publish) a prepared `work/`
310
+ * transition. A future strategy implements this same interface to publish the
311
+ * transition elsewhere — without any transition call site changing.
312
+ */
313
+ export interface LedgerWriteStrategy {
314
+ applyTransition(input: ApplyTransitionInput): Promise<ApplyTransitionResult>;
315
+ /**
316
+ * Apply (publish) a prepared COMPLETE transition: integrate a finished work
317
+ * branch back to the arbiter per its mode. The sole strategy delegates to the
318
+ * integration mechanism unchanged; a future strategy could integrate elsewhere
319
+ * without `complete.ts` changing.
320
+ */
321
+ applyCompleteTransition(
322
+ input: ApplyCompleteTransitionInput,
323
+ ): Promise<ApplyCompleteTransitionResult>;
324
+ /**
325
+ * Apply a NEEDS-ATTENTION transition: bounce a stuck claimed item to
326
+ * `work/needs-attention/` with its reason recorded in the body. The sole
327
+ * strategy delegates to the folder-native move mechanism unchanged; a future
328
+ * strategy could surface the stuck item elsewhere (e.g. the cherry-pick-to-
329
+ * `main` follow-on) without `complete.ts`/`run.ts` changing.
330
+ */
331
+ applyNeedsAttentionTransition(
332
+ input: ApplyNeedsAttentionTransitionInput,
333
+ ): Promise<ApplyNeedsAttentionTransitionResult>;
334
+ /**
335
+ * Apply a TREE-LESS NEEDS-ATTENTION (surface) transition: surface a stuck
336
+ * AFTER-COMMIT, LEDGER-ONLY item (`in-progress/ → needs-attention/`) WITHOUT a
337
+ * checkout, reusing the SAME tree-less mechanism `requeue` uses for the reverse
338
+ * direction. The sole strategy delegates to the tree-less surface (now a pure lock amend)
339
+ * (fetch + scratch-index move + throwaway-ref + leased fast-forward push). NOT
340
+ * for the uncommitted-wip surfaces, which keep {@link
341
+ * applyNeedsAttentionTransition} (the cwd-bound path that can commit wip first).
342
+ */
343
+ applyTreelessNeedsAttentionTransition(
344
+ input: ApplyTreelessNeedsAttentionTransitionInput,
345
+ ): ApplyTreelessNeedsAttentionTransitionResult;
346
+ /**
347
+ * Apply a RETURN-TO-BACKLOG transition: re-queue a STUCK item (in
348
+ * `needs-attention/` OR `in-progress/` — resolved on the arbiter) for
349
+ * re-claiming, routed through the SAME seam. Like `claim`, it is TREE-LESS —
350
+ * the move is a compare-and-swap push to the arbiter ref (it never writes the
351
+ * cwd tree).
352
+ */
353
+ applyReturnToBacklogTransition(
354
+ input: ApplyReturnToBacklogTransitionInput,
355
+ ): ApplyReturnToBacklogTransitionResult;
356
+ /**
357
+ * Apply a RESOLVE-NEEDS-ATTENTION transition: a human is picking up a stuck
358
+ * item, so **clear the stuck surface** and restore it to `in-progress`. The
359
+ * seam carries only that INTENT — "clear the surface" — NOT *how*; the sole
360
+ * (mode-M) strategy clears it by reverse-moving needs-attention → in-progress
361
+ * on the arbiter's `main`, but a future strategy could clear it differently
362
+ * without `start.ts` changing.
363
+ */
364
+ applyResolveNeedsAttentionTransition(
365
+ input: ApplyResolveNeedsAttentionTransitionInput,
366
+ ): Promise<ApplyResolveNeedsAttentionTransitionResult>;
367
+ }
368
+
369
+ // --- The sole strategy: exactly today's behaviour -------------------------
370
+
371
+ /** Run git, returning the raw result (no throw) — for soft checks. */
372
+ function gitSoft(
373
+ args: string[],
374
+ cwd: string,
375
+ env: NodeJS.ProcessEnv | undefined,
376
+ ): Promise<RunResult> {
377
+ return runAsync('git', args, cwd, {env});
378
+ }
379
+
380
+ /** Run git; throw on non-zero (genuinely unexpected plumbing failures). */
381
+ async function gitHard(
382
+ args: string[],
383
+ cwd: string,
384
+ env: NodeJS.ProcessEnv | undefined,
385
+ ): Promise<RunResult> {
386
+ const result = await runAsync('git', args, cwd, {env});
387
+ if (result.status !== 0) {
388
+ throw new Error(
389
+ `git ${args.join(' ')} failed (exit ${result.status}): ${result.stderr.trim()}`,
390
+ );
391
+ }
392
+ return result;
393
+ }
394
+
395
+ /** The git trailer key the per-attempt CAS nonce rides in (greppable, round-trips). */
396
+ export const CAS_NONCE_TRAILER = 'CAS-Nonce';
397
+
398
+ /**
399
+ * Rebuild the transition tip ({@link localBranch}'s commit, whose sha is {@link
400
+ * head}) into a NEW commit object that is byte-for-byte identical EXCEPT for a
401
+ * freshly-appended `CAS-Nonce: <uuid>` trailer, and return the new sha. This is
402
+ * the ONE chokepoint that makes EVERY {@link
403
+ * LedgerWriteStrategy.applyTransition} caller's CAS commit unique — create,
404
+ * claim, tasking-lock, advancing-lock, and the needs-attention/requeue surface
405
+ * all route their publish through `applyTransition`, so stamping HERE (the seam
406
+ * AMENDING the tip's message just before the push) covers all of them WITHOUT a
407
+ * shared commit-building helper and WITHOUT touching each commit site.
408
+ *
409
+ * It is built with `commit-tree` plumbing on the same tree + parent + ambient
410
+ * env identity as the tip, so it NEVER mutates the caller's working tree, index,
411
+ * or HEAD (safe from a job worktree mid-flight), and it preserves WHO/WHAT — it
412
+ * ONLY appends the trailer to the original message. The author/committer
413
+ * identity is pinned from the original commit so the stamp does not silently
414
+ * re-attribute the transition.
415
+ *
416
+ * A FRESH nonce is generated on EACH call — and `applyTransition` calls this once
417
+ * per ATTEMPT (each retry of a caller's outer refetch loop re-enters
418
+ * `applyTransition`), so two concurrent same-identity racers, and any single
419
+ * racer across its own retries, ALWAYS get distinct shas.
420
+ */
421
+ async function stampNonce(params: {
422
+ localBranch: string;
423
+ head: string;
424
+ cwd: string;
425
+ env: NodeJS.ProcessEnv | undefined;
426
+ }): Promise<string> {
427
+ const {localBranch, head, cwd, env} = params;
428
+ const nonce = randomUUID();
429
+
430
+ // Read the tip's tree + parents + author/committer identity + original message
431
+ // in ONE `git log` (one git subprocess, not four) — the body (%B, multi-line)
432
+ // MUST be last. Fields are NUL-separated (%x00); the trailing field is the raw
433
+ // body so its embedded newlines do not confuse the split.
434
+ const FIELD_SEP = '\u0000';
435
+ const raw = (
436
+ await gitHard(
437
+ [
438
+ 'log',
439
+ '-1',
440
+ `--format=%T%x00%P%x00%an%x00%ae%x00%aI%x00%cn%x00%ce%x00%B`,
441
+ head,
442
+ ],
443
+ cwd,
444
+ env,
445
+ )
446
+ ).stdout;
447
+ const fields = raw.split(FIELD_SEP);
448
+ const tree = (fields[0] ?? '').trim();
449
+ const parents = (fields[1] ?? '')
450
+ .trim()
451
+ .split(/\s+/)
452
+ .filter((p) => p.length > 0); // the tip's parents (usually exactly one)
453
+ const authorName = fields[2] ?? '';
454
+ const authorEmail = fields[3] ?? '';
455
+ const authorDate = fields[4] ?? '';
456
+ const committerName = fields[5] ?? '';
457
+ const committerEmail = fields[6] ?? '';
458
+ // The body is the last field; strip trailing whitespace before we append.
459
+ const message = (fields[7] ?? '').replace(/\s+$/, '');
460
+
461
+ // Pin the original author/committer so the stamp re-attributes nothing; we are
462
+ // only appending a trailer to the message.
463
+ const stampEnv: NodeJS.ProcessEnv = {
464
+ ...(env ?? process.env),
465
+ GIT_AUTHOR_NAME: authorName,
466
+ GIT_AUTHOR_EMAIL: authorEmail,
467
+ GIT_AUTHOR_DATE: authorDate,
468
+ GIT_COMMITTER_NAME: committerName,
469
+ GIT_COMMITTER_EMAIL: committerEmail,
470
+ // Deliberately do NOT pin GIT_COMMITTER_DATE: even two stamps with identical
471
+ // everything-else would still differ by the nonce, but leaving the committer
472
+ // date current is the honest "when this attempt was published".
473
+ };
474
+
475
+ // Append the trailer as its own block so it round-trips as a real git trailer.
476
+ const noncedMessage = `${message}\n\n${CAS_NONCE_TRAILER}: ${nonce}\n`;
477
+ const parentArgs = parents.flatMap((p) => ['-p', p]);
478
+ const result = await runAsync(
479
+ 'git',
480
+ ['commit-tree', tree, ...parentArgs, '-m', noncedMessage],
481
+ cwd,
482
+ {env: stampEnv},
483
+ );
484
+ if (result.status !== 0) {
485
+ throw new Error(
486
+ `git commit-tree (CAS nonce stamp) failed (exit ${result.status}): ${result.stderr.trim()} — localBranch=${localBranch}`,
487
+ );
488
+ }
489
+ return result.stdout.trim();
490
+ }
491
+
492
+ /**
493
+ * The ONLY ledger-write strategy: current behaviour. It CAS-publishes the
494
+ * prepared transition commit to the arbiter's `main` — the `main` push and the
495
+ * `--force-with-lease=main:<base>` lease live HERE, not in the public input — and
496
+ * verifies the arbiter's `main` is now that commit (guarding against an
497
+ * "Everything up-to-date" push masquerading as a successful transition). A future
498
+ * strategy would be a different object implementing the same interface — chosen
499
+ * NOWHERE today (no mode/config selects it).
500
+ */
501
+ export const currentLedgerWrite: LedgerWriteStrategy = {
502
+ async applyTransition({
503
+ arbiter,
504
+ localBranch,
505
+ expectedBase,
506
+ head,
507
+ cwd,
508
+ dryRun,
509
+ env,
510
+ note,
511
+ }): Promise<ApplyTransitionResult> {
512
+ const emit = note ?? (() => {});
513
+
514
+ if (dryRun) {
515
+ const message = `[dry-run] would: git push ${arbiter} ${localBranch}:main --force-with-lease=main:${expectedBase}`;
516
+ emit(message);
517
+ return {kind: 'published', message};
518
+ }
519
+
520
+ // Stamp this ATTEMPT's transition commit with a FRESH random nonce (a real
521
+ // `CAS-Nonce: <uuid>` git trailer) so the pushed sha is UNIQUE per attempt.
522
+ // This is what makes the lease authoritative even for SAME-IDENTITY,
523
+ // SAME-CONTENT racers: without it, two racers who build an identical tree +
524
+ // message off the same base within git's 1-second timestamp resolution
525
+ // produce an IDENTICAL sha X. The first push ff's main to X; the second
526
+ // push of the SAME X degrades to "Everything up-to-date" (git exits 0, the
527
+ // lease has nothing to reject), and the post-push verify X === X passes — so
528
+ // BOTH would return `published`. The nonce gives each attempt a DISTINCT sha,
529
+ // so the loser's push finds main moved past <base> and is GENUINELY rejected
530
+ // by the lease, and the verify below correctly fails for it. The stamp is
531
+ // built with `commit-tree` plumbing (no checkout/HEAD mutation), reusing the
532
+ // tip's tree + parent + author/committer identity — it ONLY appends the
533
+ // trailer, leaving WHO/WHAT/the original message intact. Called per ATTEMPT
534
+ // (each retry of the caller's outer refetch loop re-enters here), so every
535
+ // attempt gets a fresh nonce, not one per process.
536
+ const nonced = await stampNonce({localBranch, head, cwd, env});
537
+
538
+ // The atomic compare-and-swap. --force-with-lease=main:<base> asserts the
539
+ // arbiter's main is STILL <base> (unchanged since our fetch); the push then
540
+ // fast-forwards main to our (nonce'd, thus unique) commit. If main moved, the
541
+ // lease fails → rejected.
542
+ const push = await gitSoft(
543
+ [
544
+ 'push',
545
+ arbiter,
546
+ `${nonced}:main`,
547
+ `--force-with-lease=main:${expectedBase}`,
548
+ ],
549
+ cwd,
550
+ env,
551
+ );
552
+ if (push.status === 0) {
553
+ // Verify the arbiter main now points at OUR (nonce'd) commit. INVARIANT:
554
+ // because the nonce makes our sha unique, a successful push can ONLY mean
555
+ // either (a) we genuinely ff'd main to our nonce'd commit (published), or
556
+ // (b) someone else's commit (a DIFFERENT nonce ⇒ a DIFFERENT sha) is already
557
+ // there and ours did NOT land — i.e. an "up-to-date / no change of our
558
+ // making" no-op, which is a LOSS. The nonce makes the two naturally
559
+ // distinguishable: `arbiterHead === nonced` iff WE won. So an up-to-date
560
+ // no-op can never satisfy this and is classified REJECTED, never published.
561
+ await gitHard(['fetch', '--quiet', arbiter], cwd, env);
562
+ const arbiterHead = (
563
+ await gitHard(['rev-parse', `${arbiter}/main`], cwd, env)
564
+ ).stdout.trim();
565
+ if (arbiterHead === nonced) {
566
+ return {
567
+ kind: 'published',
568
+ message: 'transition published',
569
+ publishedHead: nonced,
570
+ };
571
+ }
572
+ emit(
573
+ `push reported up-to-date / no change of our making — ${arbiter}/main is not our commit — treating as rejected.`,
574
+ );
575
+ }
576
+ return {kind: 'rejected', message: 'push rejected / lease failed'};
577
+ },
578
+
579
+ /**
580
+ * The complete transition under the SAME strategy: integrate the prepared work
581
+ * branch back to the arbiter exactly as `complete.ts` did before — it builds
582
+ * the {@link Integrator} with the chosen provider and calls `integrate` (the
583
+ * branch was already rebased onto the latest arbiter ledger by the caller, so
584
+ * this is the non-rebasing `integrate`, never `--force`). `merge` ff's the
585
+ * branch to the arbiter's `main`; `propose` pushes the branch + asks the
586
+ * provider to request review. That `merge` targets `main` is an implementation
587
+ * detail of THIS strategy — the public input never names it.
588
+ */
589
+ async applyCompleteTransition({
590
+ arbiter,
591
+ branch,
592
+ mode,
593
+ provider,
594
+ noPR,
595
+ title,
596
+ body,
597
+ deleteMergedHead,
598
+ cwd,
599
+ env,
600
+ }): Promise<ApplyCompleteTransitionResult> {
601
+ const integrator = new Integrator({provider});
602
+ return integrator.integrate({
603
+ cwd,
604
+ arbiter,
605
+ branch,
606
+ mode,
607
+ noPR,
608
+ title,
609
+ body,
610
+ deleteMergedHead,
611
+ env,
612
+ });
613
+ },
614
+
615
+ /**
616
+ * The needs-attention transition under the SAME strategy. The seam's contract
617
+ * is transition-kind-agnostic: durably record a stuck job =
618
+ *
619
+ * - **OBSERVABLE** — publish the stuck state to the ledger surface so
620
+ * `scan`/`status`/a fresh checkout/another machine can see it. (THIS mode-M
621
+ * strategy does that by CHERRY-PICKING the move-only commit — the reason +
622
+ * the `git mv` — onto the arbiter's `main`, all-or-nothing, never `--force`d,
623
+ * so the half-finished wip below it never lands there. A future mode-P
624
+ * strategy could make it observable WITHOUT writing `main`, e.g. by reading
625
+ * work-branch tips.)
626
+ * - **RECOVERABLE** — push the work branch (when there IS one), so the saved
627
+ * work travels cross-machine and a requeue continues from its tip. (Mode M
628
+ * does that by `git push`ing the branch; the WHICH branch is the caller's,
629
+ * not assumed `work/<slug>` — a build bounce pushes `work/<slug>`, a tasking
630
+ * bounce its `work/prd-<slug>`, a temp-branch caller pushes NOTHING.)
631
+ *
632
+ * Both halves are ONE operation done in ONE place: it delegates to {@link
633
+ * routeToNeedsAttention}, which appends the reason as body prose (never a
634
+ * frontmatter field — WORK-CONTRACT rule 3), saves the aborted work as a
635
+ * **wip** commit, `git mv`s the item to `work/needs-attention/` as the
636
+ * **move-only** commit (the tip), and — when an `arbiter` is given — pushes the
637
+ * work branch (best-effort, branch-parameterised, emptiness-guarded; SURFACE-
638
+ * ONLY when `pushBranch: false`). The seam does NOT strip the arbiter: the same
639
+ * arbiter both publishes the surface (here) AND drives the helper's branch push,
640
+ * so "record stuck" and "save the work" can never drift apart. The human-vs-
641
+ * autonomous gate rides on whether an `arbiter` is given at all (human
642
+ * `complete` passes none → no surface, no push, local-only; autonomous `do`/`run`
643
+ * pass it → both).
644
+ */
645
+ async applyNeedsAttentionTransition(
646
+ input: ApplyNeedsAttentionTransitionInput,
647
+ ): Promise<ApplyNeedsAttentionTransitionResult> {
648
+ // BOUNCE = SAVE WIP + PUSH BRANCH (recoverable) + MARK LOCK STUCK (observable)
649
+ // — task `cutover-needs-attention-becomes-lock-stuck-recovery-surface`
650
+ // (decision i+). The OBSERVABLE half is now the per-item lock `state: stuck`
651
+ // (full reason prose + any agent-surfaced questions on the entry), NOT a
652
+ // `git mv` to `needs-attention/` and NOT an on-`main` surface — so a
653
+ // protected-`main` bounce succeeds and a branch cut from `main` inherits no
654
+ // stuck record. The RECOVERABLE half is UNCHANGED: {@link routeToNeedsAttention}
655
+ // commits the agent's uncommitted wip to the `work/<slug>` branch tip and
656
+ // pushes the branch (best-effort, branch-parameterised, emptiness-guarded,
657
+ // outage-retried), so the partial work travels cross-machine and a `requeue`
658
+ // continues from the tip. The work-branch push is NOT a `main` write.
659
+ const saved = await routeToNeedsAttention(input);
660
+ const stuck = await bounceToStuckLock({
661
+ cwd: input.cwd,
662
+ slug: input.slug,
663
+ reason: input.reason,
664
+ questions: input.questions,
665
+ arbiter: input.arbiter,
666
+ env: input.env,
667
+ note: input.note,
668
+ });
669
+ return {
670
+ // `moved` reflects the OBSERVABLE half (the stuck record): the lock amend.
671
+ moved: stuck.moved,
672
+ reasonNotMoved: stuck.reasonNotMoved,
673
+ // The branch push outcome is the honest RECOVERABLE report (`do`/`run` read
674
+ // it). The on-`main` surface half is GONE (the lock is the surface now).
675
+ branchPush: saved.branchPush,
676
+ pushError: saved.pushError,
677
+ };
678
+ },
679
+
680
+ /**
681
+ * The return-to-backlog transition under the SAME strategy: re-queue the
682
+ * stuck item by delegating to {@link returnToBacklog}, which moves the slug's
683
+ * current `work/<needs-attention|in-progress>/<slug>.md → work/backlog/<slug>.md`
684
+ * TREE-LESSLY — it
685
+ * builds the move off `<arbiter>/main` with plumbing and CAS-publishes it back
686
+ * THROUGH this same write seam (`applyTransition`, the very push+lease+verify
687
+ * `claim` uses), never staging/committing in the cwd working tree.
688
+ */
689
+ applyReturnToBacklogTransition(
690
+ input: ApplyReturnToBacklogTransitionInput,
691
+ ): ApplyReturnToBacklogTransitionResult {
692
+ return returnToBacklog(input);
693
+ },
694
+
695
+ /**
696
+ * The tree-less surface transition under the SAME strategy: surface the stuck
697
+ * AFTER-COMMIT, LEDGER-ONLY item by delegating to the tree-less surface (now a pure lock amend),
698
+ * which moves `work/in-progress/<slug>.md → work/needs-attention/<slug>.md`
699
+ * (reason in the body) TREE-LESSLY — it builds the move off `<arbiter>/main` with
700
+ * plumbing and CAS-publishes it THROUGH this same write seam
701
+ * (`applyTransition`, the very push+lease+verify `claim`/`requeue` use), never
702
+ * staging/committing in the cwd working tree. The reverse of
703
+ * {@link applyReturnToBacklogTransition}, the same one mechanism.
704
+ */
705
+ async applyTreelessNeedsAttentionTransition(
706
+ input: ApplyTreelessNeedsAttentionTransitionInput,
707
+ ): ApplyTreelessNeedsAttentionTransitionResult {
708
+ // PURE LOCK AMEND (task
709
+ // `cutover-needs-attention-becomes-lock-stuck-recovery-surface`, decision i+):
710
+ // the after-commit / ledger-only surface (continue-push-failure /
711
+ // continue-rebase-conflict) is now the SAME `active → stuck` lock amend as the
712
+ // cwd-bound bounce — NO `git mv`, NO `main` write. The recoverable work is
713
+ // already committed on the kept `work/<slug>` branch (intact on the arbiter);
714
+ // the stuck reason + questions ride on the lock entry. A re-surface of an
715
+ // already-stuck item is a tolerated idempotent no-op (`wrong-state`).
716
+ const moved = await bounceToStuckLock({
717
+ cwd: input.cwd,
718
+ slug: input.slug,
719
+ reason: input.reason,
720
+ questions: input.questions,
721
+ arbiter: input.arbiter,
722
+ env: input.env,
723
+ note: input.note,
724
+ });
725
+ return {moved: moved.moved, reasonNotMoved: moved.reasonNotMoved};
726
+ },
727
+
728
+ /**
729
+ * The resolve-needs-attention transition under the SAME strategy — satisfying
730
+ * the INTENT "clear the stuck surface + restore in-progress." It delegates to
731
+ * the resume lock amend (reverse `git mv` needs-attention →
732
+ * in-progress, committed) and, when an `arbiter` is given, publishes that
733
+ * reverse move-only commit to the arbiter's `main` — CLEARING the stuck surface
734
+ * there (the item is back in in-progress on the ledger). Same all-or-nothing,
735
+ * never-`--force` publish. "Reverse-move on `main`" is a detail of THIS
736
+ * strategy; the seam's contract is only the intent "clear the surface."
737
+ */
738
+ async applyResolveNeedsAttentionTransition(
739
+ input: ApplyResolveNeedsAttentionTransitionInput,
740
+ ): Promise<ApplyResolveNeedsAttentionTransitionResult> {
741
+ // PURE LOCK AMEND (task
742
+ // `cutover-needs-attention-becomes-lock-stuck-recovery-surface`, decision i+):
743
+ // resolving a stuck item is `stuck → active` on the per-item lock (a human is
744
+ // picking it up), NOT a `needs-attention/ → in-progress/` folder move. NO `main`
745
+ // write — the body already rests in `backlog/` (task 9a) and the work stays on
746
+ // the kept `work/<slug>` branch. Without an arbiter there is no lock ref to
747
+ // amend (the human-local face), so it is a recorded no-op success.
748
+ if (!input.arbiter) {
749
+ return {moved: true};
750
+ }
751
+ const r = await resumeItemLock({
752
+ item: `task:${input.slug}`,
753
+ cwd: input.cwd,
754
+ arbiter: input.arbiter,
755
+ env: input.env,
756
+ });
757
+ if (r.outcome === 'transitioned') {
758
+ return {moved: true};
759
+ }
760
+ // `wrong-state` (already active — not actually stuck) is tolerated as a no-op
761
+ // success: the item is already in-flight, the caller onboards onto it anyway.
762
+ if (r.outcome === 'wrong-state') {
763
+ return {moved: true};
764
+ }
765
+ return {
766
+ moved: false,
767
+ reasonNotMoved: `could not resume '${input.slug}' (${r.outcome}: ${r.message}).`,
768
+ };
769
+ },
770
+ };
771
+
772
+ /**
773
+ * The SOLE stuck-state RECORD: amend the item's HELD per-item lock
774
+ * `active → stuck` + the FULL reason prose + any agent-surfaced questions, via the
775
+ * state machine's mark-stuck CAS amend ({@link markStuckItemLock}) — task
776
+ * `cutover-needs-attention-becomes-lock-stuck-recovery-surface` (decision i+; prd
777
+ * `ledger-status-per-item-lock-refs` US #5/#8; ADR
778
+ * `ledger-status-on-per-item-lock-refs`). This REPLACES the `git mv →
779
+ * needs-attention/` folder bounce + its on-`main` surface + branch push: the
780
+ * bounce now touches ONLY the lock ref (NO `main` write — so a protected-`main`
781
+ * bounce succeeds, and a work branch cut from `main` inherits no stuck record).
782
+ *
783
+ * The bounce holds the TASK's `implement` lock that `claim` acquired (task
784
+ * `claim-acquires-unified-lock-no-body-move`), so the normal stuck path is a plain
785
+ * `active → stuck` amend (`transitioned`). The OUTCOME MAPPING onto `{moved}`:
786
+ * - `transitioned` — the lock is now stuck (the stuck state is recorded) ⇒
787
+ * `moved: true`.
788
+ * - `wrong-state` — the lock is ALREADY `stuck` (an idempotent re-surface of a
789
+ * still-stuck item) ⇒ `moved: true` (the stuck record stands).
790
+ * - `not-held` — there is NO held lock to amend (an item that predates the
791
+ * lock, or a flow where claim did not acquire) ⇒ `moved: false` honestly: with
792
+ * the folder retired there is no other substrate to record the stuck state on,
793
+ * so the caller reports the bounce did NOT land (retry/resolve) rather than
794
+ * fake a success.
795
+ * - `lost`/`error` — a concurrent CAS race / environment fault ⇒ `moved: false`.
796
+ * - no arbiter — a human local-only `complete` (no arbiter handle): the lock
797
+ * ref lives on the arbiter, so there is no lock to amend; treat as a recorded
798
+ * no-op (`moved: true`) — a human is right there (the same human-vs-autonomous
799
+ * posture the old local-only folder move took). NOTE this records nothing
800
+ * durable, by design: the human is in the loop.
801
+ *
802
+ * Keyed on `task:<slug>` (the bounce surfaces a TASK; the prd/observation locks
803
+ * are tasking/advance holds whose own bounce paths are separate).
804
+ */
805
+ async function bounceToStuckLock(params: {
806
+ cwd: string;
807
+ slug: string;
808
+ reason: string;
809
+ questions?: string[];
810
+ arbiter?: string;
811
+ env?: NodeJS.ProcessEnv;
812
+ note?: (message: string) => void;
813
+ }): Promise<{moved: boolean; reasonNotMoved?: string}> {
814
+ const {cwd, slug, reason, questions, arbiter, env} = params;
815
+ const note = params.note ?? (() => {});
816
+ if (!arbiter) {
817
+ // No arbiter handle ⇒ no lock ref to amend. A human local-only `complete`:
818
+ // the human is right there, so this is a recorded no-op (parity with the old
819
+ // local-only folder move that wrote nothing cross-machine).
820
+ note(
821
+ `'${slug}' bounced locally (no arbiter) — the stuck reason is not recorded ` +
822
+ 'on a lock ref (a human is right here).',
823
+ );
824
+ return {moved: true};
825
+ }
826
+ try {
827
+ const r = await markStuckItemLock({
828
+ item: `task:${slug}`,
829
+ reason,
830
+ questions,
831
+ cwd,
832
+ arbiter,
833
+ env,
834
+ });
835
+ const movedOutcomes: TransitionOutcome[] = ['transitioned', 'wrong-state'];
836
+ if (movedOutcomes.includes(r.outcome)) {
837
+ if (r.outcome === 'transitioned') {
838
+ note(`Marked the per-item lock for '${slug}' stuck: ${reason}`);
839
+ } else {
840
+ note(`The per-item lock for '${slug}' is already stuck (re-surface).`);
841
+ }
842
+ return {moved: true};
843
+ }
844
+ const reasonNotMoved =
845
+ r.outcome === 'not-held'
846
+ ? `'${slug}' has no held lock to mark stuck (the bounce could not record ` +
847
+ 'the stuck state — the item is not lock-held).'
848
+ : `could not mark the per-item lock for '${slug}' stuck (${r.outcome}: ` +
849
+ `${r.message}).`;
850
+ note(reasonNotMoved);
851
+ return {moved: false, reasonNotMoved};
852
+ } catch (err) {
853
+ const reasonNotMoved =
854
+ `could not mark the per-item lock for '${slug}' stuck ` +
855
+ `(${err instanceof Error ? err.message : String(err)}).`;
856
+ note(reasonNotMoved);
857
+ return {moved: false, reasonNotMoved};
858
+ }
859
+ }
860
+
861
+ /**
862
+ * The active ledger-write strategy. There is exactly one (current behaviour);
863
+ * this indirection is the seam's single insertion point — NOT a selectable mode.
864
+ */
865
+ export const ledgerWrite: LedgerWriteStrategy = currentLedgerWrite;