dorfl 0.1.1 → 0.2.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 (492) hide show
  1. package/dist/advance-ci-template.d.ts +3 -3
  2. package/dist/advance-ci-template.js +1 -1
  3. package/dist/advance-ci-template.js.map +1 -1
  4. package/dist/advance-classify.d.ts +5 -5
  5. package/dist/advance-classify.d.ts.map +1 -1
  6. package/dist/advance-classify.js +4 -4
  7. package/dist/advance-drivers.d.ts +8 -8
  8. package/dist/advance-drivers.d.ts.map +1 -1
  9. package/dist/advance-drivers.js +20 -8
  10. package/dist/advance-drivers.js.map +1 -1
  11. package/dist/advance-isolated.d.ts +3 -3
  12. package/dist/advance-isolated.d.ts.map +1 -1
  13. package/dist/advance-isolated.js +1 -1
  14. package/dist/advance-lifecycle-template.d.ts +2 -2
  15. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  16. package/dist/advance-lifecycle-template.js +85 -13
  17. package/dist/advance-lifecycle-template.js.map +1 -1
  18. package/dist/advance-loop-driver.d.ts +3 -3
  19. package/dist/advance-loop-driver.d.ts.map +1 -1
  20. package/dist/advance-loop-driver.js +1 -1
  21. package/dist/advance-treeless-publish.d.ts +24 -1
  22. package/dist/advance-treeless-publish.d.ts.map +1 -1
  23. package/dist/advance-treeless-publish.js +41 -0
  24. package/dist/advance-treeless-publish.js.map +1 -1
  25. package/dist/advance.d.ts +69 -17
  26. package/dist/advance.d.ts.map +1 -1
  27. package/dist/advance.js +409 -102
  28. package/dist/advance.js.map +1 -1
  29. package/dist/advancing-lock.d.ts +31 -3
  30. package/dist/advancing-lock.d.ts.map +1 -1
  31. package/dist/advancing-lock.js +55 -5
  32. package/dist/advancing-lock.js.map +1 -1
  33. package/dist/agent-launch.d.ts +12 -0
  34. package/dist/agent-launch.d.ts.map +1 -1
  35. package/dist/agent-launch.js +22 -12
  36. package/dist/agent-launch.js.map +1 -1
  37. package/dist/agent-stop.d.ts +40 -2
  38. package/dist/agent-stop.d.ts.map +1 -1
  39. package/dist/agent-stop.js +30 -2
  40. package/dist/agent-stop.js.map +1 -1
  41. package/dist/apply-decide.d.ts +19 -5
  42. package/dist/apply-decide.d.ts.map +1 -1
  43. package/dist/apply-decide.js +38 -9
  44. package/dist/apply-decide.js.map +1 -1
  45. package/dist/apply-merge-action.d.ts +20 -8
  46. package/dist/apply-merge-action.d.ts.map +1 -1
  47. package/dist/apply-merge-action.js +44 -9
  48. package/dist/apply-merge-action.js.map +1 -1
  49. package/dist/apply-persist.d.ts +62 -31
  50. package/dist/apply-persist.d.ts.map +1 -1
  51. package/dist/apply-persist.js +173 -44
  52. package/dist/apply-persist.js.map +1 -1
  53. package/dist/apply-stuck-action.d.ts +151 -0
  54. package/dist/apply-stuck-action.d.ts.map +1 -0
  55. package/dist/apply-stuck-action.js +125 -0
  56. package/dist/apply-stuck-action.js.map +1 -0
  57. package/dist/brand.d.ts +12 -1
  58. package/dist/brand.d.ts.map +1 -1
  59. package/dist/brand.js +2 -1
  60. package/dist/brand.js.map +1 -1
  61. package/dist/buildable-body.d.ts +14 -14
  62. package/dist/buildable-body.js +9 -9
  63. package/dist/claim-cas.d.ts +3 -3
  64. package/dist/claim-cas.js +1 -1
  65. package/dist/claim-cas.js.map +1 -1
  66. package/dist/cli-spinner.d.ts +1 -1
  67. package/dist/cli-spinner.js +1 -1
  68. package/dist/cli.d.ts +10 -0
  69. package/dist/cli.d.ts.map +1 -1
  70. package/dist/cli.js +385 -128
  71. package/dist/cli.js.map +1 -1
  72. package/dist/close-job-template.d.ts +3 -3
  73. package/dist/close-job-template.js +9 -9
  74. package/dist/close-job.d.ts +8 -8
  75. package/dist/close-job.js +25 -25
  76. package/dist/close-job.js.map +1 -1
  77. package/dist/complete.d.ts +9 -6
  78. package/dist/complete.d.ts.map +1 -1
  79. package/dist/complete.js +99 -45
  80. package/dist/complete.js.map +1 -1
  81. package/dist/concurrency.d.ts +1 -1
  82. package/dist/concurrency.js +1 -1
  83. package/dist/config.d.ts +86 -40
  84. package/dist/config.d.ts.map +1 -1
  85. package/dist/config.js +61 -9
  86. package/dist/config.js.map +1 -1
  87. package/dist/continue-branch.d.ts +1 -1
  88. package/dist/continue-branch.d.ts.map +1 -1
  89. package/dist/continue-branch.js +23 -1
  90. package/dist/continue-branch.js.map +1 -1
  91. package/dist/cwd-section.js +1 -1
  92. package/dist/cwd-section.js.map +1 -1
  93. package/dist/decision-engine.d.ts +39 -11
  94. package/dist/decision-engine.d.ts.map +1 -1
  95. package/dist/decision-engine.js +10 -6
  96. package/dist/decision-engine.js.map +1 -1
  97. package/dist/do-autopick.d.ts +6 -6
  98. package/dist/do-autopick.d.ts.map +1 -1
  99. package/dist/do-autopick.js +16 -6
  100. package/dist/do-autopick.js.map +1 -1
  101. package/dist/do-config.d.ts +4 -4
  102. package/dist/do-config.js +3 -3
  103. package/dist/do-config.js.map +1 -1
  104. package/dist/do-remote-auto.d.ts +2 -2
  105. package/dist/do-remote-auto.js +1 -1
  106. package/dist/do.d.ts +74 -81
  107. package/dist/do.d.ts.map +1 -1
  108. package/dist/do.js +439 -50
  109. package/dist/do.js.map +1 -1
  110. package/dist/drop-source.d.ts +2 -2
  111. package/dist/env-config.d.ts.map +1 -1
  112. package/dist/env-config.js +19 -11
  113. package/dist/env-config.js.map +1 -1
  114. package/dist/failure-cause.d.ts +3 -2
  115. package/dist/failure-cause.d.ts.map +1 -1
  116. package/dist/failure-cause.js +28 -1
  117. package/dist/failure-cause.js.map +1 -1
  118. package/dist/format.d.ts +6 -6
  119. package/dist/format.d.ts.map +1 -1
  120. package/dist/format.js +13 -30
  121. package/dist/format.js.map +1 -1
  122. package/dist/frontmatter.d.ts +28 -15
  123. package/dist/frontmatter.d.ts.map +1 -1
  124. package/dist/frontmatter.js +38 -24
  125. package/dist/frontmatter.js.map +1 -1
  126. package/dist/gc.d.ts +65 -6
  127. package/dist/gc.d.ts.map +1 -1
  128. package/dist/gc.js +126 -9
  129. package/dist/gc.js.map +1 -1
  130. package/dist/github.d.ts +14 -0
  131. package/dist/github.d.ts.map +1 -1
  132. package/dist/github.js +73 -0
  133. package/dist/github.js.map +1 -1
  134. package/dist/harness.d.ts +25 -0
  135. package/dist/harness.d.ts.map +1 -1
  136. package/dist/harness.js.map +1 -1
  137. package/dist/index.d.ts +4 -1
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +2 -1
  140. package/dist/index.js.map +1 -1
  141. package/dist/install-ci-branch-protection.d.ts +133 -39
  142. package/dist/install-ci-branch-protection.d.ts.map +1 -1
  143. package/dist/install-ci-branch-protection.js +191 -49
  144. package/dist/install-ci-branch-protection.js.map +1 -1
  145. package/dist/install-ci-capabilities/advance-lifecycle.d.ts +1 -1
  146. package/dist/install-ci-capabilities/advance-lifecycle.js +1 -1
  147. package/dist/install-ci-capabilities/close-job.d.ts +1 -1
  148. package/dist/install-ci-capabilities/close-job.js +1 -1
  149. package/dist/install-ci-capabilities/example-noop.d.ts +1 -1
  150. package/dist/install-ci-capabilities/example-noop.js +1 -1
  151. package/dist/install-ci-capabilities/intake.d.ts +2 -2
  152. package/dist/install-ci-capabilities/intake.js +2 -2
  153. package/dist/install-ci-capabilities/verify.d.ts +1 -1
  154. package/dist/install-ci-capabilities/verify.js +1 -1
  155. package/dist/install-ci-core.d.ts +24 -4
  156. package/dist/install-ci-core.d.ts.map +1 -1
  157. package/dist/install-ci-core.js +12 -5
  158. package/dist/install-ci-core.js.map +1 -1
  159. package/dist/install-ci-github.d.ts +33 -1
  160. package/dist/install-ci-github.d.ts.map +1 -1
  161. package/dist/install-ci-github.js +74 -1
  162. package/dist/install-ci-github.js.map +1 -1
  163. package/dist/install-ci.d.ts +1 -1
  164. package/dist/install-ci.js +4 -4
  165. package/dist/install-ci.js.map +1 -1
  166. package/dist/install-skills.d.ts +123 -0
  167. package/dist/install-skills.d.ts.map +1 -0
  168. package/dist/install-skills.js +100 -0
  169. package/dist/install-skills.js.map +1 -0
  170. package/dist/intake-event.d.ts +6 -6
  171. package/dist/intake-event.js +6 -6
  172. package/dist/intake-marker.d.ts +3 -3
  173. package/dist/intake-marker.d.ts.map +1 -1
  174. package/dist/intake-triage.d.ts +1 -1
  175. package/dist/intake-triage.js +2 -2
  176. package/dist/intake-trigger-template.d.ts +8 -8
  177. package/dist/intake-trigger-template.d.ts.map +1 -1
  178. package/dist/intake-trigger-template.js +17 -17
  179. package/dist/intake-trigger-template.js.map +1 -1
  180. package/dist/intake.d.ts +65 -52
  181. package/dist/intake.d.ts.map +1 -1
  182. package/dist/intake.js +92 -76
  183. package/dist/intake.js.map +1 -1
  184. package/dist/integration-core.d.ts +27 -24
  185. package/dist/integration-core.d.ts.map +1 -1
  186. package/dist/integration-core.js +191 -77
  187. package/dist/integration-core.js.map +1 -1
  188. package/dist/integrator.d.ts +1 -1
  189. package/dist/integrator.d.ts.map +1 -1
  190. package/dist/integrator.js +19 -3
  191. package/dist/integrator.js.map +1 -1
  192. package/dist/isolation.d.ts +4 -4
  193. package/dist/isolation.d.ts.map +1 -1
  194. package/dist/isolation.js +9 -0
  195. package/dist/isolation.js.map +1 -1
  196. package/dist/issue-provider.d.ts +4 -4
  197. package/dist/issue-provider.js +1 -1
  198. package/dist/item-lock.d.ts +216 -149
  199. package/dist/item-lock.d.ts.map +1 -1
  200. package/dist/item-lock.js +345 -270
  201. package/dist/item-lock.js.map +1 -1
  202. package/dist/item-path.d.ts +2 -2
  203. package/dist/item-path.js +2 -2
  204. package/dist/ledger-lint.d.ts +9 -9
  205. package/dist/ledger-lint.js +9 -9
  206. package/dist/ledger-read.d.ts +57 -57
  207. package/dist/ledger-read.d.ts.map +1 -1
  208. package/dist/ledger-read.js +24 -24
  209. package/dist/ledger-read.js.map +1 -1
  210. package/dist/ledger-write.d.ts +33 -28
  211. package/dist/ledger-write.d.ts.map +1 -1
  212. package/dist/ledger-write.js +100 -122
  213. package/dist/ledger-write.js.map +1 -1
  214. package/dist/lifecycle-gather.d.ts +18 -1
  215. package/dist/lifecycle-gather.d.ts.map +1 -1
  216. package/dist/lifecycle-gather.js +23 -17
  217. package/dist/lifecycle-gather.js.map +1 -1
  218. package/dist/lifecycle-pools.d.ts +44 -7
  219. package/dist/lifecycle-pools.d.ts.map +1 -1
  220. package/dist/lifecycle-pools.js +27 -7
  221. package/dist/lifecycle-pools.js.map +1 -1
  222. package/dist/merge-question-surfacer.d.ts +18 -4
  223. package/dist/merge-question-surfacer.d.ts.map +1 -1
  224. package/dist/merge-question-surfacer.js +20 -5
  225. package/dist/merge-question-surfacer.js.map +1 -1
  226. package/dist/migrate-stuck-locks.d.ts +129 -0
  227. package/dist/migrate-stuck-locks.d.ts.map +1 -0
  228. package/dist/migrate-stuck-locks.js +355 -0
  229. package/dist/migrate-stuck-locks.js.map +1 -0
  230. package/dist/mint-adr.js +4 -4
  231. package/dist/mirror-pool-scan.d.ts +2 -2
  232. package/dist/mirror-pool-scan.js +3 -3
  233. package/dist/mirror-pool-scan.js.map +1 -1
  234. package/dist/needs-attention.d.ts +304 -27
  235. package/dist/needs-attention.d.ts.map +1 -1
  236. package/dist/needs-attention.js +542 -67
  237. package/dist/needs-attention.js.map +1 -1
  238. package/dist/orphan-sidecar.d.ts +10 -6
  239. package/dist/orphan-sidecar.d.ts.map +1 -1
  240. package/dist/orphan-sidecar.js +35 -2
  241. package/dist/orphan-sidecar.js.map +1 -1
  242. package/dist/pi-harness.d.ts +16 -0
  243. package/dist/pi-harness.d.ts.map +1 -1
  244. package/dist/pi-harness.js +82 -2
  245. package/dist/pi-harness.js.map +1 -1
  246. package/dist/placement.d.ts +8 -8
  247. package/dist/placement.js +3 -3
  248. package/dist/prd-to-spec.d.ts.map +1 -1
  249. package/dist/prd-to-spec.js +9 -5
  250. package/dist/prd-to-spec.js.map +1 -1
  251. package/dist/prompt.d.ts +16 -19
  252. package/dist/prompt.d.ts.map +1 -1
  253. package/dist/prompt.js +17 -19
  254. package/dist/prompt.js.map +1 -1
  255. package/dist/protocol/CLAIM-PROTOCOL.md +17 -10
  256. package/dist/protocol/REVIEW-PROTOCOL.md +4 -1
  257. package/dist/protocol/SURFACE-PROTOCOL.md +16 -2
  258. package/dist/protocol/TASKING-PROTOCOL.md +3 -1
  259. package/dist/protocol/WORK-CONTRACT.md +22 -18
  260. package/dist/protocol/task-template.md +1 -1
  261. package/dist/readiness.d.ts +1 -1
  262. package/dist/reap-branches.d.ts +12 -9
  263. package/dist/reap-branches.d.ts.map +1 -1
  264. package/dist/reap-branches.js +25 -7
  265. package/dist/reap-branches.js.map +1 -1
  266. package/dist/recover-isolated.d.ts +12 -0
  267. package/dist/recover-isolated.d.ts.map +1 -1
  268. package/dist/recover-isolated.js +6 -1
  269. package/dist/recover-isolated.js.map +1 -1
  270. package/dist/repo-config.d.ts +23 -2
  271. package/dist/repo-config.d.ts.map +1 -1
  272. package/dist/repo-config.js +64 -18
  273. package/dist/repo-config.js.map +1 -1
  274. package/dist/repo-mirror.d.ts.map +1 -1
  275. package/dist/repo-mirror.js +18 -2
  276. package/dist/repo-mirror.js.map +1 -1
  277. package/dist/review-gate.d.ts +3 -3
  278. package/dist/review-gate.d.ts.map +1 -1
  279. package/dist/review-gate.js +11 -10
  280. package/dist/review-gate.js.map +1 -1
  281. package/dist/review-verdict.d.ts +2 -2
  282. package/dist/review-verdict.d.ts.map +1 -1
  283. package/dist/review-verdict.js +2 -2
  284. package/dist/review-verdict.js.map +1 -1
  285. package/dist/run.d.ts +2 -2
  286. package/dist/run.d.ts.map +1 -1
  287. package/dist/run.js +58 -23
  288. package/dist/run.js.map +1 -1
  289. package/dist/scan.d.ts +26 -17
  290. package/dist/scan.d.ts.map +1 -1
  291. package/dist/scan.js +29 -15
  292. package/dist/scan.js.map +1 -1
  293. package/dist/select-order.d.ts +1 -1
  294. package/dist/select-priority.d.ts +14 -14
  295. package/dist/select-priority.d.ts.map +1 -1
  296. package/dist/select-priority.js +5 -5
  297. package/dist/select-priority.js.map +1 -1
  298. package/dist/sidecar-apply.js +1 -1
  299. package/dist/sidecar-apply.js.map +1 -1
  300. package/dist/sidecar.d.ts +47 -12
  301. package/dist/sidecar.d.ts.map +1 -1
  302. package/dist/sidecar.js +84 -4
  303. package/dist/sidecar.js.map +1 -1
  304. package/dist/skills/answer-questions/SKILL.md +89 -0
  305. package/dist/skills/capture-signal/SKILL.md +52 -0
  306. package/dist/skills/convert-from-prd-to-spec/SKILL.md +90 -0
  307. package/dist/skills/drive-tasks/SKILL.md +218 -0
  308. package/dist/skills/from-idea/SKILL.md +83 -0
  309. package/dist/skills/merge-prs/SKILL.md +70 -0
  310. package/dist/skills/orchestrate/SKILL.md +101 -0
  311. package/dist/skills/promote/SKILL.md +35 -0
  312. package/dist/skills/review/SKILL.md +16 -0
  313. package/dist/skills/setup/SKILL.md +258 -0
  314. package/dist/skills/setup/protocol/ADR-FORMAT.md +47 -0
  315. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +224 -0
  316. package/dist/skills/setup/protocol/REVIEW-PROTOCOL.md +122 -0
  317. package/dist/skills/setup/protocol/SURFACE-PROTOCOL.md +135 -0
  318. package/dist/skills/setup/protocol/TASKING-PROTOCOL.md +124 -0
  319. package/dist/skills/setup/protocol/WORK-CONTRACT.md +280 -0
  320. package/dist/skills/setup/protocol/spec-template.md +71 -0
  321. package/dist/skills/setup/protocol/task-template.md +65 -0
  322. package/dist/skills/surface-questions/SKILL.md +16 -0
  323. package/dist/skills/to-spec/SKILL.md +34 -0
  324. package/dist/skills/to-task/SKILL.md +19 -0
  325. package/dist/skills/triage-observations/SKILL.md +78 -0
  326. package/dist/skills/work/SKILL.md +51 -0
  327. package/dist/slug-namespace.d.ts +6 -6
  328. package/dist/slug-namespace.js +8 -8
  329. package/dist/slug-namespace.js.map +1 -1
  330. package/dist/spec-complete.d.ts.map +1 -1
  331. package/dist/spec-complete.js +4 -5
  332. package/dist/spec-complete.js.map +1 -1
  333. package/dist/start.d.ts +1 -1
  334. package/dist/start.d.ts.map +1 -1
  335. package/dist/start.js +54 -60
  336. package/dist/start.js.map +1 -1
  337. package/dist/status.d.ts +3 -3
  338. package/dist/status.js +4 -4
  339. package/dist/status.js.map +1 -1
  340. package/dist/surface-gate.d.ts +2 -2
  341. package/dist/surface-gate.d.ts.map +1 -1
  342. package/dist/surface-gate.js +10 -3
  343. package/dist/surface-gate.js.map +1 -1
  344. package/dist/surface-persist.d.ts +2 -2
  345. package/dist/surface-persist.d.ts.map +1 -1
  346. package/dist/surface-persist.js +1 -1
  347. package/dist/surface-persist.js.map +1 -1
  348. package/dist/tasker-review-loop.d.ts +11 -10
  349. package/dist/tasker-review-loop.d.ts.map +1 -1
  350. package/dist/tasker-review-loop.js +8 -8
  351. package/dist/tasker-review-loop.js.map +1 -1
  352. package/dist/tasking-eligibility.d.ts +21 -21
  353. package/dist/tasking-eligibility.d.ts.map +1 -1
  354. package/dist/tasking-eligibility.js +10 -10
  355. package/dist/tasking-lock.d.ts +10 -10
  356. package/dist/tasking-lock.d.ts.map +1 -1
  357. package/dist/tasking-lock.js +69 -49
  358. package/dist/tasking-lock.js.map +1 -1
  359. package/dist/tasking.d.ts +73 -39
  360. package/dist/tasking.d.ts.map +1 -1
  361. package/dist/tasking.js +309 -95
  362. package/dist/tasking.js.map +1 -1
  363. package/dist/triage-gate.d.ts +1 -1
  364. package/dist/triage-persist.d.ts +15 -11
  365. package/dist/triage-persist.d.ts.map +1 -1
  366. package/dist/triage-persist.js +48 -18
  367. package/dist/triage-persist.js.map +1 -1
  368. package/dist/vendor/incur/agents.d.ts +58 -0
  369. package/dist/vendor/incur/agents.d.ts.map +1 -0
  370. package/dist/vendor/incur/agents.js +343 -0
  371. package/dist/vendor/incur/agents.js.map +1 -0
  372. package/dist/verify-workflow-template.d.ts +1 -1
  373. package/dist/verify-workflow-template.js +3 -3
  374. package/dist/watch-session.d.ts +11 -3
  375. package/dist/watch-session.d.ts.map +1 -1
  376. package/dist/watch-session.js +94 -7
  377. package/dist/watch-session.js.map +1 -1
  378. package/dist/work-layout.d.ts +15 -9
  379. package/dist/work-layout.d.ts.map +1 -1
  380. package/dist/work-layout.js +14 -9
  381. package/dist/work-layout.js.map +1 -1
  382. package/dist/workspace.d.ts +2 -2
  383. package/package.json +2 -2
  384. package/src/advance-ci-template.ts +4 -4
  385. package/src/advance-classify.ts +5 -5
  386. package/src/advance-drivers.ts +26 -13
  387. package/src/advance-isolated.ts +3 -3
  388. package/src/advance-lifecycle-template.ts +98 -13
  389. package/src/advance-loop-driver.ts +3 -3
  390. package/src/advance-treeless-publish.ts +46 -1
  391. package/src/advance.ts +495 -115
  392. package/src/advancing-lock.ts +102 -7
  393. package/src/agent-launch.ts +37 -12
  394. package/src/agent-stop.ts +60 -2
  395. package/src/apply-decide.ts +38 -9
  396. package/src/apply-merge-action.ts +49 -11
  397. package/src/apply-persist.ts +236 -62
  398. package/src/apply-stuck-action.ts +260 -0
  399. package/src/brand.ts +14 -2
  400. package/src/buildable-body.ts +14 -14
  401. package/src/claim-cas.ts +4 -4
  402. package/src/cli-spinner.ts +1 -1
  403. package/src/cli.ts +472 -141
  404. package/src/close-job-template.ts +9 -9
  405. package/src/close-job.ts +26 -26
  406. package/src/complete.ts +121 -63
  407. package/src/concurrency.ts +1 -1
  408. package/src/config.ts +143 -49
  409. package/src/continue-branch.ts +23 -1
  410. package/src/cwd-section.ts +1 -1
  411. package/src/decision-engine.ts +57 -19
  412. package/src/do-autopick.ts +21 -10
  413. package/src/do-config.ts +7 -7
  414. package/src/do-remote-auto.ts +2 -2
  415. package/src/do.ts +558 -85
  416. package/src/drop-source.ts +2 -2
  417. package/src/env-config.ts +19 -11
  418. package/src/failure-cause.ts +30 -1
  419. package/src/format.ts +13 -33
  420. package/src/frontmatter.ts +55 -35
  421. package/src/gc.ts +172 -9
  422. package/src/github.ts +78 -0
  423. package/src/harness.ts +25 -0
  424. package/src/index.ts +10 -0
  425. package/src/install-ci-branch-protection.ts +283 -58
  426. package/src/install-ci-capabilities/advance-lifecycle.ts +1 -1
  427. package/src/install-ci-capabilities/close-job.ts +1 -1
  428. package/src/install-ci-capabilities/example-noop.ts +1 -1
  429. package/src/install-ci-capabilities/intake.ts +2 -2
  430. package/src/install-ci-capabilities/verify.ts +1 -1
  431. package/src/install-ci-core.ts +34 -7
  432. package/src/install-ci-github.ts +87 -1
  433. package/src/install-ci.ts +4 -4
  434. package/src/install-skills.ts +166 -0
  435. package/src/intake-event.ts +6 -6
  436. package/src/intake-marker.ts +3 -3
  437. package/src/intake-triage.ts +2 -2
  438. package/src/intake-trigger-template.ts +18 -18
  439. package/src/intake.ts +138 -103
  440. package/src/integration-core.ts +234 -100
  441. package/src/integrator.ts +22 -9
  442. package/src/isolation.ts +13 -4
  443. package/src/issue-provider.ts +4 -4
  444. package/src/item-lock.ts +505 -361
  445. package/src/item-path.ts +2 -2
  446. package/src/ledger-lint.ts +9 -9
  447. package/src/ledger-read.ts +81 -81
  448. package/src/ledger-write.ts +137 -152
  449. package/src/lifecycle-gather.ts +45 -22
  450. package/src/lifecycle-pools.ts +71 -13
  451. package/src/merge-question-surfacer.ts +32 -8
  452. package/src/migrate-stuck-locks.ts +451 -0
  453. package/src/mint-adr.ts +4 -4
  454. package/src/mirror-pool-scan.ts +5 -5
  455. package/src/needs-attention.ts +877 -104
  456. package/src/orphan-sidecar.ts +49 -8
  457. package/src/pi-harness.ts +82 -2
  458. package/src/placement.ts +8 -8
  459. package/src/prd-to-spec.ts +13 -5
  460. package/src/prompt.ts +20 -25
  461. package/src/readiness.ts +1 -1
  462. package/src/reap-branches.ts +38 -14
  463. package/src/recover-isolated.ts +18 -1
  464. package/src/repo-config.ts +66 -17
  465. package/src/repo-mirror.ts +23 -1
  466. package/src/review-gate.ts +12 -11
  467. package/src/review-verdict.ts +3 -3
  468. package/src/run.ts +69 -23
  469. package/src/scan.ts +36 -22
  470. package/src/select-order.ts +1 -1
  471. package/src/select-priority.ts +15 -15
  472. package/src/sidecar-apply.ts +1 -1
  473. package/src/sidecar.ts +141 -14
  474. package/src/slug-namespace.ts +10 -10
  475. package/src/spec-complete.ts +4 -5
  476. package/src/start.ts +57 -63
  477. package/src/status.ts +7 -7
  478. package/src/surface-gate.ts +12 -5
  479. package/src/surface-persist.ts +3 -3
  480. package/src/tasker-review-loop.ts +18 -17
  481. package/src/tasking-eligibility.ts +21 -21
  482. package/src/tasking-lock.ts +75 -60
  483. package/src/tasking.ts +381 -135
  484. package/src/triage-gate.ts +1 -1
  485. package/src/triage-persist.ts +71 -27
  486. package/src/vendor/incur/LICENSE +21 -0
  487. package/src/vendor/incur/README.md +19 -0
  488. package/src/vendor/incur/agents.ts +392 -0
  489. package/src/verify-workflow-template.ts +3 -3
  490. package/src/watch-session.ts +102 -7
  491. package/src/work-layout.ts +14 -9
  492. package/src/workspace.ts +2 -2
package/src/do.ts CHANGED
@@ -59,7 +59,14 @@ import {
59
59
  parseStopSentinel,
60
60
  isWorkBranchDiffEmpty,
61
61
  emptyDiffStopReason,
62
+ emptyDiffDisposeEnvelope,
63
+ type AgentStopClass,
62
64
  } from './agent-stop.js';
65
+ import {
66
+ surfaceStuckToNeedsAttention,
67
+ routeToNeedsAttention,
68
+ } from './needs-attention.js';
69
+ import {resolveItemPathByIdentity} from './item-path.js';
63
70
  import {
64
71
  classifyFailureCause,
65
72
  failureCauseLabel,
@@ -117,6 +124,149 @@ import {
117
124
  * `complete` (the caller threads the resolved mode in as `integration`).
118
125
  */
119
126
 
127
+ /**
128
+ * The commit-message subject the deadline-checkpoint save writes, and the
129
+ * pattern used to COUNT consecutive checkpoints on the branch (spec
130
+ * `graceful-pre-timeout-wip-checkpoint` — the anti-loop ceiling). Living
131
+ * on-branch means the counter naturally RESETS when the item completes (the
132
+ * branch is discarded on integration) and when a `--reset` requeue discards
133
+ * the branch: a genuinely-progressing task cannot silently accumulate a
134
+ * ceiling breach across generations.
135
+ */
136
+ const DEADLINE_CHECKPOINT_COMMIT_SUBJECT_PREFIX = 'chore(deadline-checkpoint)';
137
+
138
+ /**
139
+ * Distinct reason strings for the two deadline-checkpoint branches (spec
140
+ * `graceful-pre-timeout-wip-checkpoint` — the CI/human report must NEVER
141
+ * collapse to a generic "agent-failed"). Auto-continue and surface carry
142
+ * different phrasing so the counter + ceiling are legible.
143
+ */
144
+ function deadlineAutoContinueReason(params: {
145
+ slug: string;
146
+ count: number;
147
+ max: number;
148
+ }): string {
149
+ return (
150
+ `deadline checkpoint (auto-continued ${params.count}/${params.max}): ` +
151
+ `'${params.slug}' hit the dorfl-internal deadline with new work-branch ` +
152
+ `progress this session; saved the WIP and released the lock so the next ` +
153
+ `tick continues from the branch tip.`
154
+ );
155
+ }
156
+ function deadlineSurfaceReason(params: {
157
+ slug: string;
158
+ kind: 'no-progress' | 'ceiling';
159
+ count?: number;
160
+ max?: number;
161
+ }): string {
162
+ if (params.kind === 'no-progress') {
163
+ return (
164
+ `deadline checkpoint (no progress / ceiling): '${params.slug}' hit ` +
165
+ `the dorfl-internal deadline WITHOUT any work-branch progress this ` +
166
+ `session — indistinguishable from a wedge; surfaced for a human to ` +
167
+ `decide (continue / split / cancel).`
168
+ );
169
+ }
170
+ return (
171
+ `deadline checkpoint (no progress / ceiling): '${params.slug}' has hit ` +
172
+ `the dorfl-internal deadline ${params.count}/${params.max} times ` +
173
+ `consecutively — it may be too big for one CI leg. Split it or run ` +
174
+ `it locally; surfaced for a human to decide.`
175
+ );
176
+ }
177
+
178
+ /**
179
+ * Count how many DEADLINE-CHECKPOINT commits (subject prefix
180
+ * `chore(deadline-checkpoint)`) currently sit on the work branch ahead of
181
+ * `<arbiter>/main` — the branch-based anti-loop counter (spec
182
+ * `graceful-pre-timeout-wip-checkpoint`). A best-effort count: an unreachable
183
+ * arbiter / a bare mirror reads as 0 (the SAFE direction — never falsely block
184
+ * an auto-continue) so a genuine plumbing fault degrades to "under the ceiling"
185
+ * rather than surfacing a spurious ceiling breach. Resets naturally when the
186
+ * item completes (the branch is discarded on integration).
187
+ */
188
+ async function countDeadlineCheckpointsOnBranch(params: {
189
+ cwd: string;
190
+ arbiter: string;
191
+ branch: string;
192
+ env: NodeJS.ProcessEnv | undefined;
193
+ }): Promise<number> {
194
+ const {cwd, arbiter, branch, env} = params;
195
+ const range = `${arbiter}/main..HEAD`;
196
+ void branch;
197
+ const r = await runAsync('git', ['log', '--format=%s', range], cwd, {env});
198
+ if (r.status !== 0) {
199
+ return 0;
200
+ }
201
+ const lines = r.stdout.split('\n').filter((s) => s.length > 0);
202
+ return lines.filter((s) =>
203
+ s.startsWith(DEADLINE_CHECKPOINT_COMMIT_SUBJECT_PREFIX),
204
+ ).length;
205
+ }
206
+
207
+ /**
208
+ * SAVE the WIP on a deadline stop (spec `graceful-pre-timeout-wip-checkpoint`,
209
+ * build step 3): commit the tree as-is + push `work/<slug>`. Reuses the SAVE
210
+ * HALF of the needs-attention mechanism ({@link routeToNeedsAttention}) but
211
+ * ADDS a distinguishing checkpoint-marker commit on top so
212
+ * {@link countDeadlineCheckpointsOnBranch} can count auto-continues. Distinct
213
+ * from `applyNeedsAttentionTransition` (the whole thing) which ALSO marks the
214
+ * lock stuck — auto-continue must NOT mark stuck; only the surface branch does.
215
+ */
216
+ async function saveDeadlineCheckpoint(params: {
217
+ slug: string;
218
+ branch: string;
219
+ cwd: string;
220
+ arbiter: string;
221
+ reason: string;
222
+ env: NodeJS.ProcessEnv | undefined;
223
+ note: (message: string) => void;
224
+ }): Promise<import('./needs-attention.js').RouteToNeedsAttentionResult> {
225
+ const {slug, branch, cwd, arbiter, reason, env, note} = params;
226
+ void reason;
227
+ // Save any uncommitted work first (the routeToNeedsAttention save half also
228
+ // commits + pushes; but we ALSO want a distinct checkpoint-marker commit so
229
+ // countDeadlineCheckpointsOnBranch can enumerate them). Stage everything and
230
+ // commit a MARKER commit if there's anything to record OR if there is not
231
+ // already a marker on the tip — the marker's job is to make the checkpoint
232
+ // COUNTABLE, not to represent new work.
233
+ const addResult = await runAsync('git', ['add', '-A'], cwd, {env});
234
+ void addResult;
235
+ const diffCached = await runAsync(
236
+ 'git',
237
+ ['diff', '--cached', '--quiet'],
238
+ cwd,
239
+ {env},
240
+ );
241
+ const hasStaged = diffCached.status !== 0;
242
+ if (hasStaged) {
243
+ await runAsync(
244
+ 'git',
245
+ [
246
+ 'commit',
247
+ '-q',
248
+ '-m',
249
+ `${DEADLINE_CHECKPOINT_COMMIT_SUBJECT_PREFIX}: save wip for '${slug}'`,
250
+ ],
251
+ cwd,
252
+ {env},
253
+ );
254
+ }
255
+ // Push the branch to the arbiter (best-effort). Reuse routeToNeedsAttention's
256
+ // save-half exact contract: it commits any residue + pushes the branch with
257
+ // bounded backoff. We already committed above; the wip commit inside
258
+ // routeToNeedsAttention will be a no-op (clean tree) and the push runs.
259
+ return routeToNeedsAttention({
260
+ cwd,
261
+ slug,
262
+ reason: `deadline-checkpoint save for '${slug}' (see branch)`,
263
+ arbiter,
264
+ branch,
265
+ note,
266
+ env,
267
+ });
268
+ }
269
+
120
270
  /** The terminal status of one in-place `do` run. */
121
271
  export type DoOutcome =
122
272
  | 'completed' // claimed/onboarded → agent → gate green → integrated → exited
@@ -126,18 +276,21 @@ export type DoOutcome =
126
276
  | 'surface-unmoved' // the tree-less surface to needs-attention did NOT land on the arbiter (lost the CAS race / no arbiter) — the item is STILL in-progress on the arbiter; retry/resolve
127
277
  | 'agent-failed' // the agent ran but produced bad/empty output (the conservative generic), OR the cause is unknown — work SAVED + surfaced
128
278
  | 'transient-infra' // a harness-surfaced model/connection outage (post-retry) or a git/provider outage — RETRY the same work (FAILURE-CAUSE axis)
279
+ | 'needs-reauth' // a credential expired / was revoked (OAuth refresh) — retry cannot help; a human must RE-AUTH (FAILURE-CAUSE axis)
129
280
  | 'config-error' // a thrown CORE wiring/config error (e.g. review on, no reviewGate) — fix the WIRING, not the task (FAILURE-CAUSE axis)
130
281
  | 'agent-stopped' // the agent DELIBERATELY stopped (task drifted/ambiguous) OR produced no change → surfaced; gate + Gate-2 SKIPPED
282
+ | 'deadline-auto-continued' // the dorfl-internal deadline fired; WIP saved + branch pushed + lock RELEASED (no sidecar) so the next tick continues (spec `graceful-pre-timeout-wip-checkpoint`)
283
+ | 'deadline-surfaced' // the dorfl-internal deadline fired but auto-continue was refused (no progress this session, or the maxAutoCheckpoints ceiling was hit); WIP saved + question SURFACED for a human
131
284
  | 'refused' // refused (dirty tree, wrong folder, nothing to complete, …)
132
285
  | 'usage-error' // usage / environment problem, or a slug-resolution error
133
- | 'tasked' // `do prd:<slug>` — the prd was tasked into work/backlog/ (runner-owned)
134
- | 'gate-refused' // `do prd:<slug>` — the tasking gate refused (honest skip)
135
- | 'stale'; // `do prd:<slug>` — the held prd was edited under the lock (stale tasking)
286
+ | 'tasked' // `do spec:<slug>` — the spec was tasked into work/backlog/ (runner-owned)
287
+ | 'gate-refused' // `do spec:<slug>` — the tasking gate refused (honest skip)
288
+ | 'stale'; // `do spec:<slug>` — the held spec was edited under the lock (stale tasking)
136
289
 
137
290
  export interface DoResult {
138
291
  exitCode: 0 | 1 | 2 | 3;
139
292
  outcome: DoOutcome;
140
- /** The resolved bare slug acted on (task or prd), when one was resolved. */
293
+ /** The resolved bare slug acted on (task or spec), when one was resolved. */
141
294
  slug?: string;
142
295
  /** The work branch the run operated on, when one was created/switched-to. */
143
296
  branch?: string;
@@ -168,10 +321,18 @@ export type DoDorfl = (input: {
168
321
  * Absent ⇒ no body ⇒ the provider degrades to `--fill` (no regression).
169
322
  */
170
323
  output?: string;
324
+ /**
325
+ * **True iff the injected agent simulates a dorfl-internal DEADLINE stop**
326
+ * (spec `graceful-pre-timeout-wip-checkpoint`). Test-only signal so an
327
+ * injected `DoDorfl` can drive the checkpoint routing (save WIP +
328
+ * auto-continue / surface). Production `runDoAgent` never sets this on the
329
+ * injected path — the real deadline race lives in `PiHarness.launchAsync`.
330
+ */
331
+ timedOut?: boolean;
171
332
  };
172
333
 
173
334
  export interface DoOptions {
174
- /** The raw CLI slug argument: bare (= task), `task:<slug>`, or `prd:<slug>`. */
335
+ /** The raw CLI slug argument: bare (= task), `task:<slug>`, or `spec:<slug>`. */
175
336
  arg: string;
176
337
  /** The working clone/checkout to run in-place in. */
177
338
  cwd: string;
@@ -187,10 +348,10 @@ export interface DoOptions {
187
348
  /**
188
349
  * Per-repo `autoTask` policy (resolved by `autoslice-gate`: flag > env >
189
350
  * per-repo > global > default false). It gates the AUTO-PICK / pool path only
190
- * (`do-autopick.ts`'s taskable-prd pool): "may an agent auto-task an
191
- * UNDECLARED prd in this repo?". An EXPLICITLY-named `do prd:<slug>` tasks
351
+ * (`do-autopick.ts`'s taskable-spec pool): "may an agent auto-task an
352
+ * UNDECLARED spec in this repo?". An EXPLICITLY-named `do spec:<slug>` tasks
192
353
  * REGARDLESS of this policy (the dispatch passes `explicit: true` to
193
- * `performTask` — naming the prd IS the authorization, exactly as `do <task>`
354
+ * `performTask` — naming the spec IS the authorization, exactly as `do <task>`
194
355
  * builds regardless of `autoBuild`). Ignored by the task-build path.
195
356
  */
196
357
  autoTask?: boolean;
@@ -205,7 +366,7 @@ export interface DoOptions {
205
366
  promptGuidance?: PromptGuidance;
206
367
  /**
207
368
  * **The tasker review→edit→converge LOOP seam** (`slicer-review-edit-loop`):
208
- * consumed ONLY by the `do prd:<slug>` tasking path — after the agent produces
369
+ * consumed ONLY by the `do spec:<slug>` tasking path — after the agent produces
209
370
  * candidate tasks, run the `review` SKILL as a review→edit→re-review loop that
210
371
  * improves them, routing the verdict through the needsAnswers / needs-attention
211
372
  * sink. Ignored by the task-build path. Omitted ⇒ no loop (candidate tasks land
@@ -234,27 +395,27 @@ export interface DoOptions {
234
395
  explicitMerge?: boolean;
235
396
  /**
236
397
  * **Per-TRANSITION override for the TASKING transition only** (config
237
- * `taskingIntegration`). Consumed ONLY by the `do prd:<slug>` tasking path: the
398
+ * `taskingIntegration`). Consumed ONLY by the `do spec:<slug>` tasking path: the
238
399
  * value threaded into {@link performTask} is `taskingIntegration ?? integration`,
239
400
  * so an unset override is byte-for-byte today's behaviour (tasking uses
240
401
  * `integration`). The task-BUILD path ALWAYS threads `integration` (never this
241
402
  * key). An explicit `--merge`/`--propose` flag wins over BOTH (the flag-override
242
403
  * layer sets `integration` AND `taskingIntegration` to the typed mode — see
243
- * `do-config.ts`). DISTINCT from intake's per-EMITTED-TYPE `{task, prd}` resolver.
404
+ * `do-config.ts`). DISTINCT from intake's per-EMITTED-TYPE `{task, spec}` resolver.
244
405
  */
245
406
  taskingIntegration?: IntegrationMode;
246
407
  /**
247
- * **The per-repo TASK-PLACEMENT default** (prd
408
+ * **The per-repo TASK-PLACEMENT default** (spec
248
409
  * `staging-pool-position-gate-and-trust-model` US #5, task
249
410
  * `runner-deterministic-slice-placement-policy-and-precedence`). Consumed by
250
- * the `do prd:<slug>` tasking path: the value is fed as the
411
+ * the `do spec:<slug>` tasking path: the value is fed as the
251
412
  * CONFIGURED-DEFAULT rung into the runner-deterministic placement resolver
252
413
  * (`src/placement.ts`). Resolved per-repo through the SAME chain as
253
414
  * `taskingIntegration` (flag > env > per-repo > global > built-in
254
- * `pre-backlog`). The task-BUILD path ignores it (placement is a TASKING
415
+ * `backlog`). The task-BUILD path ignores it (placement is a TASKING
255
416
  * lifecycle concern).
256
417
  */
257
- tasksLandIn?: 'pre-backlog' | 'ready';
418
+ tasksLandIn?: 'backlog' | 'ready';
258
419
  /**
259
420
  * **The OPERATOR's EXPLICIT task-placement override** (the TOP precedence
260
421
  * rung in the placement resolver). Set ONLY when the operator typed
@@ -263,7 +424,7 @@ export interface DoOptions {
263
424
  * CLI always wins, no special force-key) — the positional analogue of
264
425
  * `explicitMerge` overriding the untrusted-origin build-propose rule.
265
426
  */
266
- explicitTasksLandIn?: 'pre-backlog' | 'ready';
427
+ explicitTasksLandIn?: 'backlog' | 'ready';
267
428
  /**
268
429
  * Override the pre-flight DIVERGENCE guard (`--ignore-diverged-main`, mirroring
269
430
  * `--ignore-not-ready`): proceed even when local `main` is ahead of
@@ -274,7 +435,7 @@ export interface DoOptions {
274
435
  */
275
436
  ignoreDivergedMain?: boolean;
276
437
  /**
277
- * **`--allow-backlog`** (prd
438
+ * **`--allow-backlog`** (spec
278
439
  * `do-allow-backlog-drive-staged-tasks-without-promotion`): let this `do task:`
279
440
  * invocation FIND, CLAIM, and COMPLETE a task that lives in `tasks/backlog/`
280
441
  * (staging) IN PLACE, WITHOUT first promoting it to the pool — so no `advance`
@@ -284,7 +445,7 @@ export interface DoOptions {
284
445
  * build/gate are identical, and the done-move goes `tasks/backlog/ → tasks/done/`
285
446
  * directly (the explicit drive IS the promotion). EXPLICIT-INVOCATION-ONLY:
286
447
  * default off, never set by `run`/auto-pick/`advance` (the leak-fence). The
287
- * task-build path only; the `do prd:` path is unaffected.
448
+ * task-build path only; the `do spec:` path is unaffected.
288
449
  */
289
450
  allowBacklog?: boolean;
290
451
  /** The declared per-repo ENV-PREP step (string | list), run ONCE before the
@@ -300,7 +461,7 @@ export interface DoOptions {
300
461
  */
301
462
  freshWorktreeGate?: boolean;
302
463
  /**
303
- * **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, prd
464
+ * **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, spec
304
465
  * `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1
305
466
  * (a)). Threaded VERBATIM into `performComplete` → `performIntegration` so a
306
467
  * wide-matrix CI's raised cap actually reaches the cross-job land queue (the
@@ -335,7 +496,7 @@ export interface DoOptions {
335
496
  */
336
497
  providerInstance?: ReviewProvider;
337
498
  /**
338
- * **Gate 2 — the PR/code review gate** (GATES prd `work/prds/tasked/review.md`):
499
+ * **Gate 2 — the PR/code review gate** (GATES spec `work/specs/tasked/review.md`):
339
500
  * threaded VERBATIM into `performComplete` (the gate rides inside the shared
340
501
  * `do`/`complete` pipeline, so CI inherits it for free). When `review` is on,
341
502
  * the `review` SKILL runs as a fresh-context agent AFTER the green `verify` and
@@ -349,7 +510,7 @@ export interface DoOptions {
349
510
  reviewGate?: ReviewGate;
350
511
  /**
351
512
  * **The task-SET ACCEPTANCE GATE seam** (task `slice-acceptance-gate`):
352
- * consumed ONLY by the `do prd:<slug>` tasking path. When `review` resolves on,
513
+ * consumed ONLY by the `do spec:<slug>` tasking path. When `review` resolves on,
353
514
  * a fresh-context review of the produced task SET runs BEFORE the tasks
354
515
  * integrate (riding `performIntegration`'s review block); `block` routes the set
355
516
  * to needs-attention, `approve` lets it integrate. It rides the SAME BUILD
@@ -408,6 +569,22 @@ export interface DoOptions {
408
569
  * a sink to assert the surfaced lines without a real terminal.
409
570
  */
410
571
  watchSink?: (line: string) => void;
572
+ /**
573
+ * The dorfl-internal agent-session deadline in minutes (spec
574
+ * `graceful-pre-timeout-wip-checkpoint`). Threaded through {@link runDoAgent}
575
+ * to `launchWithOptionalWatch` — the harness races the child against it and
576
+ * flags a graceful stop on fire, which `performDo` routes as a CHECKPOINT
577
+ * (save WIP + auto-continue on progress under the ceiling, else surface).
578
+ * Absent ⇒ no deadline (a run that finishes on its own is byte-for-byte
579
+ * unchanged).
580
+ */
581
+ agentDeadlineMinutes?: number;
582
+ /**
583
+ * Anti-loop ceiling on CONSECUTIVE deadline auto-continues (spec
584
+ * `graceful-pre-timeout-wip-checkpoint`). On hitting this the next
585
+ * deadline checkpoint surfaces to a human instead of auto-continuing.
586
+ */
587
+ maxAutoCheckpoints?: number;
411
588
  }
412
589
 
413
590
  /**
@@ -433,6 +610,25 @@ interface DoAgentLaunchOptions {
433
610
  watch?: boolean;
434
611
  watchSink?: (line: string) => void;
435
612
  color?: boolean;
613
+ /**
614
+ * **The dorfl-internal agent deadline in minutes** (spec
615
+ * `graceful-pre-timeout-wip-checkpoint`). Threaded to {@link runDoAgent},
616
+ * which converts it to a wall-clock `deadlineMs` and passes it to
617
+ * {@link launchWithOptionalWatch} — the pi harness races the child against
618
+ * that deadline. When the deadline fires, the agent is gracefully stopped
619
+ * (SIGTERM + 10s grace + SIGKILL) and the caller routes the run as a
620
+ * CHECKPOINT (save WIP + auto-continue / surface). Absent ⇒ no deadline
621
+ * (byte-for-byte the pre-task behaviour).
622
+ */
623
+ agentDeadlineMinutes?: number;
624
+ /**
625
+ * **The anti-loop ceiling** for consecutive deadline auto-continues (spec
626
+ * `graceful-pre-timeout-wip-checkpoint`). On hitting this the next deadline
627
+ * checkpoint SURFACES to a human instead of auto-continuing. Absent ⇒
628
+ * unlimited (⇒ defensive callers should always thread the resolved config
629
+ * value; the default is 5).
630
+ */
631
+ maxAutoCheckpoints?: number;
436
632
  env?: NodeJS.ProcessEnv;
437
633
  /**
438
634
  * The optional runner IDENTITY (a bot). Threaded from the host-only
@@ -453,7 +649,7 @@ interface DoAgentLaunchOptions {
453
649
  * there is no checkout, so the worktree is materialised under `workspacesDir`.
454
650
  */
455
651
  export interface DoRemoteOptions extends DoAgentLaunchOptions {
456
- /** The raw CLI slug argument: bare (= task), `task:<slug>`, or `prd:<slug>`. */
652
+ /** The raw CLI slug argument: bare (= task), `task:<slug>`, or `spec:<slug>`. */
457
653
  arg: string;
458
654
  /**
459
655
  * The registered remote spec/URL to run against (`do --remote <r>`). Resolved
@@ -473,12 +669,12 @@ export interface DoRemoteOptions extends DoAgentLaunchOptions {
473
669
  arbiter?: string;
474
670
  /**
475
671
  * Per-repo `autoTask` policy — gates the AUTO-PICK / pool path only. An
476
- * EXPLICITLY-named `do --remote prd:<slug>` tasks regardless of it (the
672
+ * EXPLICITLY-named `do --remote spec:<slug>` tasks regardless of it (the
477
673
  * dispatch passes `explicit: true`), mirroring `do <task>` vs `autoBuild`.
478
674
  * Ignored by the task-build path.
479
675
  */
480
676
  autoTask?: boolean;
481
- /** The tasker review→edit→converge loop seam — `do --remote prd:<slug>` path only (see {@link DoOptions.reviewLoop}). */
677
+ /** The tasker review→edit→converge loop seam — `do --remote spec:<slug>` path only (see {@link DoOptions.reviewLoop}). */
482
678
  reviewLoop?: TaskReviewGate;
483
679
  /** The tasker improver loop's `taskerLoopMax` cap. Loop only. */
484
680
  taskerLoopMax?: number;
@@ -495,24 +691,24 @@ export interface DoRemoteOptions extends DoAgentLaunchOptions {
495
691
  explicitMerge?: boolean;
496
692
  /**
497
693
  * **Per-TRANSITION override for the TASKING transition only** (config
498
- * `taskingIntegration`) on the `do --remote prd:<slug>` path: threaded into
694
+ * `taskingIntegration`) on the `do --remote spec:<slug>` path: threaded into
499
695
  * {@link performTask} as `taskingIntegration ?? integration`. Unset ⇒ tasking
500
696
  * uses `integration` (today's behaviour); the task-BUILD path always threads
501
697
  * `integration`. See {@link DoOptions.taskingIntegration}.
502
698
  */
503
699
  taskingIntegration?: IntegrationMode;
504
700
  /**
505
- * **The per-repo TASK-PLACEMENT default** (prd
701
+ * **The per-repo TASK-PLACEMENT default** (spec
506
702
  * `staging-pool-position-gate-and-trust-model` US #5) on the `do --remote
507
- * prd:<slug>` path: threaded into {@link performTask} as the
703
+ * spec:<slug>` path: threaded into {@link performTask} as the
508
704
  * configured-default rung. See {@link DoOptions.tasksLandIn}.
509
705
  */
510
- tasksLandIn?: 'pre-backlog' | 'ready';
706
+ tasksLandIn?: 'backlog' | 'ready';
511
707
  /**
512
708
  * **The OPERATOR's EXPLICIT task-placement override** on the `do --remote
513
- * prd:` path. See {@link DoOptions.explicitTasksLandIn}.
709
+ * spec:` path. See {@link DoOptions.explicitTasksLandIn}.
514
710
  */
515
- explicitTasksLandIn?: 'pre-backlog' | 'ready';
711
+ explicitTasksLandIn?: 'backlog' | 'ready';
516
712
  /** The declared per-repo ENV-PREP step (string | list), run ONCE before the
517
713
  * first `verify` on a fresh worktree. Unset ⇒ a no-op (NO default install). */
518
714
  prepare?: VerifyConfig;
@@ -526,7 +722,7 @@ export interface DoRemoteOptions extends DoAgentLaunchOptions {
526
722
  */
527
723
  freshWorktreeGate?: boolean;
528
724
  /**
529
- * **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, prd
725
+ * **The cross-job merge-serialiser CAS-retry cap** (config `mergeRetries`, spec
530
726
  * `land-time-reverify-and-parallel-merge-ceiling` Story 5 / Applied Answer q1
531
727
  * (a)). Threaded VERBATIM into `performComplete` → `performIntegration` so the
532
728
  * resolved cap reaches the cross-job land queue (the CAS loop IS the queue
@@ -564,7 +760,7 @@ export interface DoRemoteOptions extends DoAgentLaunchOptions {
564
760
  reviewModel?: string;
565
761
  reviewMaxRounds?: number;
566
762
  reviewGate?: ReviewGate;
567
- /** The task-SET ACCEPTANCE GATE seam — `do --remote prd:<slug>` path only (see {@link DoOptions.taskReviewGate}). */
763
+ /** The task-SET ACCEPTANCE GATE seam — `do --remote spec:<slug>` path only (see {@link DoOptions.taskReviewGate}). */
568
764
  taskReviewGate?: ReviewGate;
569
765
  /**
570
766
  * **`--allow-backlog`** on the no-checkout `do --remote`/`--isolated` path (see
@@ -586,7 +782,7 @@ export interface DoRemoteOptions extends DoAgentLaunchOptions {
586
782
  const DEFAULT_ARBITER = 'origin';
587
783
 
588
784
  /**
589
- * Map a `do prd:<slug>` {@link TaskResult} onto the `do` {@link DoResult}
785
+ * Map a `do spec:<slug>` {@link TaskResult} onto the `do` {@link DoResult}
590
786
  * contract: outcomes pass through (tasked / gate-refused / stale / agent-failed /
591
787
  * usage-error), the lock-lost outcome splits into `lost` (exit 2) vs `contended`
592
788
  * (exit 3) by its exit code, and the tasking-only exit 4 (stale) is reported on
@@ -620,7 +816,7 @@ function taskResultToDoResult(tasked: TaskResult): DoResult {
620
816
  break;
621
817
  case 'needs-attention':
622
818
  // The tasker review→edit loop found the decomposition unclear and routed the
623
- // prd to needs-attention (no guessed tasks). Same exit class as a stuck
819
+ // spec to needs-attention (no guessed tasks). Same exit class as a stuck
624
820
  // build (1).
625
821
  outcome = 'needs-attention';
626
822
  exitCode = 1;
@@ -684,8 +880,8 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
684
880
  }
685
881
 
686
882
  // 1. Resolve the slug across BOTH namespaces — `do` is the ONE command that
687
- // spans them (ADR §3a): bare → task (after a no-prd-collision check;
688
- // ERROR on collision), `task:`/`prd:` explicit. A collision / resolution
883
+ // spans them (ADR §3a): bare → task (after a no-spec-collision check;
884
+ // ERROR on collision), `task:`/`spec:` explicit. A collision / resolution
689
885
  // failure is a loud usage error (exit 1).
690
886
  let resolved;
691
887
  try {
@@ -717,10 +913,10 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
717
913
  arbiter,
718
914
  doer: 'agent',
719
915
  autoTask: options.autoTask,
720
- // EXPLICIT dispatch: a `do prd:<slug>` target was NAMED (the operator typed
916
+ // EXPLICIT dispatch: a `do spec:<slug>` target was NAMED (the operator typed
721
917
  // it, or the auto-pick POOL already filtered it on `autoTask` before
722
918
  // dispatching here — the single policy-enforcement point). So the tasking gate
723
- // drops the `autoTask` policy term and binds only the prd's own readiness
919
+ // drops the `autoTask` policy term and binds only the spec's own readiness
724
920
  // (`humanOnly`/`needsAnswers`) + `taskedAfter`, EXACTLY as `do <task>` builds a
725
921
  // named task regardless of `autoBuild` (the pool gates the policy, not the
726
922
  // explicit claim).
@@ -741,19 +937,24 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
741
937
  // `integration:'propose'` + `taskingIntegration:'merge'` lands the task FILES
742
938
  // on main here while the BUILD path below still threads plain `integration`.
743
939
  integration: options.taskingIntegration ?? options.integration,
940
+ // The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) —
941
+ // threaded so the `do spec:` tasking-transition's land tail respects the
942
+ // same per-repo cap the build path uses (task
943
+ // `thread-merge-retries-cross-task-and-ratify-default`).
944
+ mergeRetries: options.mergeRetries,
744
945
  // The per-repo TASK-PLACEMENT default + the operator's explicit
745
946
  // override (task `runner-deterministic-slice-placement-policy-and-
746
947
  // precedence`). The tasker reads them as the configured-default + the
747
948
  // top rung of the runner-deterministic placement resolver; the
748
949
  // `originTrust: untrusted` force is read inside the tasker from the
749
- // prd's stamped frontmatter.
950
+ // spec's stamped frontmatter.
750
951
  tasksLandIn: options.tasksLandIn,
751
952
  explicitTasksLandIn: options.explicitTasksLandIn,
752
953
  noPR: options.noPR,
753
954
  providerInstance: options.providerInstance,
754
955
  // The tasker review→edit→converge loop (slicer-review-edit-loop): improves the
755
956
  // candidate tasks in place + routes the verdict through the needsAnswers /
756
- // needs-attention sink. Threaded only on the `do prd:` path; omitted ⇒ no loop.
957
+ // needs-attention sink. Threaded only on the `do spec:` path; omitted ⇒ no loop.
757
958
  reviewLoop: options.reviewLoop,
758
959
  taskerLoopMax: options.taskerLoopMax,
759
960
  reviewExecutions: options.reviewExecutions,
@@ -942,15 +1143,18 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
942
1143
  // auto-resolved). Surface to needs-attention TREE-LESSLY via the SAME `#89`
943
1144
  // mechanism `requeue` uses for the reverse direction — the rebase was
944
1145
  // ABORTED, so the kept `work/<slug>` tip == the arbiter tip (already on the
945
- // arbiter, after-commit, recoverable). The surface is purely the one-file
946
- // `in-progress/ needs-attention/` ledger move + reason (no branch push, no
947
- // worktree mutation) instead of running the agent — the §10 path. The work
1146
+ // arbiter, after-commit, recoverable). The surface is purely the lock amend
1147
+ // to `state: stuck` with the reason (post lock-cutover no folder move, no
1148
+ // branch push, no worktree mutation) instead of running the agent — the §10
1149
+ // path. The work
948
1150
  // did NOT onboard; the runner owns the bounce.
949
1151
  if (tree.continueRebaseConflict) {
950
1152
  const reason =
951
1153
  `continuing the kept ${tree.branch}: rebase onto the latest main ` +
952
- 'conflicted (aborted, never auto-resolved) — resolve against the latest ' +
953
- 'main, or `requeue --reset` to discard and start fresh';
1154
+ 'conflicted (aborted, never auto-resolved) — run `requeue --reconcile` ' +
1155
+ 'to non-destructively re-sync the mirror and retry the rebase (keeps ' +
1156
+ 'the work). Last resort: `requeue --reset` DESTRUCTIVELY discards the ' +
1157
+ 'branch and starts fresh.';
954
1158
  const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
955
1159
  cwd: tree.dir,
956
1160
  slug,
@@ -984,8 +1188,10 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
984
1188
  const reason =
985
1189
  `continuing the kept ${tree.branch}: publishing the rebased work branch ` +
986
1190
  `to the arbiter failed terminally (${tree.continuePushFailure}) — the kept ` +
987
- 'branch is left intact on the arbiter (recoverable); `requeue` to retry ' +
988
- 'once the churn settles, or `requeue --reset` to discard and start fresh';
1191
+ 'branch is left intact on the arbiter (recoverable); `requeue --reconcile` ' +
1192
+ 'to non-destructively re-sync the mirror and retry once the churn ' +
1193
+ 'settles (keeps the work). Last resort: `requeue --reset` DESTRUCTIVELY ' +
1194
+ 'discards the branch and starts fresh.';
989
1195
  const surfaced = await ledgerWrite.applyTreelessNeedsAttentionTransition({
990
1196
  cwd: tree.dir,
991
1197
  slug,
@@ -1008,7 +1214,7 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1008
1214
 
1009
1215
  // 5. Run the agent autonomously in the checkout, ON the work branch — the
1010
1216
  // SAME prompt assembly `dorfl prompt` emits (canonical wrapper +
1011
- // source prd + the task's ## Prompt). The agent only edits code (it does
1217
+ // source spec + the task's ## Prompt). The agent only edits code (it does
1012
1218
  // no git). This is the one NEW middle step `ar-run.sh` shelled out for
1013
1219
  // (`prompt | pi`).
1014
1220
  // The post-claim pipeline reads the uniform `IsolatedTree` handle (`tree.dir`)
@@ -1047,7 +1253,7 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1047
1253
  content: readFileSync(task.path, 'utf8'),
1048
1254
  env,
1049
1255
  });
1050
- // Per-item override layer: a task or prd may pin `promptGuidance.testFirst`
1256
+ // Per-item override layer: a task or spec may pin `promptGuidance.testFirst`
1051
1257
  // in its frontmatter, superseding the resolved repo policy for THIS item.
1052
1258
  const itemGuidance = resolvePromptGuidanceForItem({
1053
1259
  cwd: tree.dir,
@@ -1074,7 +1280,12 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1074
1280
  throw err;
1075
1281
  }
1076
1282
 
1077
- let agent: {ok: boolean; detail?: string; output?: string};
1283
+ let agent: {
1284
+ ok: boolean;
1285
+ detail?: string;
1286
+ output?: string;
1287
+ timedOut?: boolean;
1288
+ };
1078
1289
  try {
1079
1290
  agent = await runDoAgent(options, tree.dir, prompt, slug);
1080
1291
  } catch (err) {
@@ -1089,6 +1300,23 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1089
1300
  note,
1090
1301
  });
1091
1302
  }
1303
+ // DEADLINE CHECKPOINT (spec `graceful-pre-timeout-wip-checkpoint`): the
1304
+ // dorfl-internal deadline fired — stop is graceful, NOT a failure. Route
1305
+ // through the shared checkpoint path (save WIP + auto-continue on progress
1306
+ // under the ceiling, else surface). Distinct from `saveAgentFailure` on
1307
+ // purpose — the deadline path must NOT mark the item stuck when it
1308
+ // auto-continues.
1309
+ if (agent.timedOut) {
1310
+ return await routeDeadlineCheckpoint({
1311
+ slug,
1312
+ branch,
1313
+ cwd: tree.dir,
1314
+ arbiter: tree.arbiterRemote,
1315
+ maxAutoCheckpoints: options.maxAutoCheckpoints ?? 5,
1316
+ env,
1317
+ note,
1318
+ });
1319
+ }
1092
1320
  if (!agent.ok) {
1093
1321
  const detail = agent.detail ?? `the agent failed to build '${slug}'.`;
1094
1322
  return await saveAgentFailure({
@@ -1123,7 +1351,8 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1123
1351
  branch,
1124
1352
  cwd: tree.dir,
1125
1353
  arbiter: tree.arbiterRemote,
1126
- reason: stopReason,
1354
+ reason: stopReason.reason,
1355
+ kind: stopReason.kind,
1127
1356
  env,
1128
1357
  note,
1129
1358
  });
@@ -1175,7 +1404,7 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1175
1404
  verify: options.verify,
1176
1405
  freshWorktreeGate: options.freshWorktreeGate,
1177
1406
  // The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) — the
1178
- // git-alone FLOOR of the cross-job land queue (prd `land-time-reverify-and-
1407
+ // git-alone FLOOR of the cross-job land queue (spec `land-time-reverify-and-
1179
1408
  // parallel-merge-ceiling` Story 5 / Applied Answer q1 (a)). Threaded so a
1180
1409
  // wide-matrix CI's raised cap actually reaches the merge loop.
1181
1410
  mergeRetries: options.mergeRetries,
@@ -1275,8 +1504,10 @@ export async function performDo(options: DoOptions): Promise<DoResult> {
1275
1504
  // (`runRemotePipeline`) uses, so `advance task:<slug>` (via the default
1276
1505
  // `doDriver = performDo`) agrees with the remote path on the caller-visible
1277
1506
  // label.
1507
+ //
1508
+ // PR-2b D3: a clean surface (routedToNeedsAttention:true) is GREEN.
1278
1509
  return {
1279
- exitCode: 1,
1510
+ exitCode: completed.routedToNeedsAttention ? 0 : 1,
1280
1511
  outcome: 'needs-attention',
1281
1512
  slug,
1282
1513
  branch,
@@ -1373,7 +1604,9 @@ function routeReport(
1373
1604
  return {
1374
1605
  fragment:
1375
1606
  `push of ${branch} FAILED — the work is saved LOCALLY only; push it ` +
1376
- 'when online, then `requeue` (continue), or `requeue --reset` to discard',
1607
+ 'when online, then `requeue` (continue) or `requeue --reconcile` ' +
1608
+ '(non-destructive retry). Last resort: `requeue --reset` ' +
1609
+ 'DESTRUCTIVELY discards.',
1377
1610
  pushFailed: true,
1378
1611
  };
1379
1612
  }
@@ -1389,7 +1622,10 @@ function routeReport(
1389
1622
  landed = 'saved locally';
1390
1623
  }
1391
1624
  return {
1392
- fragment: `${landed}. Recover via \`requeue\` (continue) or \`requeue --reset\` to discard`,
1625
+ fragment:
1626
+ `${landed}. Recover via \`requeue\` (continue) or \`requeue --reconcile\` ` +
1627
+ '(non-destructive retry). Last resort: `requeue --reset` ' +
1628
+ 'DESTRUCTIVELY discards.',
1393
1629
  pushFailed: false,
1394
1630
  };
1395
1631
  }
@@ -1403,10 +1639,12 @@ function routeReport(
1403
1639
  *
1404
1640
  * This routes it through the SAME work-preserving machinery a RED GATE uses: the
1405
1641
  * ledger write seam's needs-attention transition (`git add -A` + a wip commit
1406
- * capturing the agent's work + the `git mv needs-attention/` move-only commit
1407
- * with the failure detail recorded as the reason in the body), surfaced on the
1408
- * arbiter's `main` (the autonomous, cross-machine-visible mode-M surfacing `do`
1409
- * already uses for the gate-fail path) so `scan`/`status`/another machine see it.
1642
+ * capturing the agent's work, then post lock-cutover a LOCK AMEND to
1643
+ * `state: stuck` with the failure detail as the reason on the lock entry, in
1644
+ * place of the retired `git mv → needs-attention/` move-only commit), surfaced on
1645
+ * the arbiter's `main` (the autonomous, cross-machine-visible mode-M surfacing
1646
+ * `do` already uses for the gate-fail path) so `scan`/`status`/another machine
1647
+ * see it.
1410
1648
  *
1411
1649
  * It ALSO pushes the `work/<slug>` branch to the arbiter so the saved partial
1412
1650
  * commits travel cross-machine and the item is RECOVERABLE via `requeue`
@@ -1429,7 +1667,8 @@ function routeReport(
1429
1667
  * contract stays coherent). Only the WORK-PRESERVING side-effect (unchanged here)
1430
1668
  * matches the gate-failure path. We do NOT validate or "fix" the partial work — a broken
1431
1669
  * tree committed + surfaced (with the reason) is recoverable; the human chooses
1432
- * `requeue` (continue) vs `requeue --reset` (discard).
1670
+ * `requeue` (continue) / `requeue --reconcile` (non-destructive retry) vs
1671
+ * `requeue --reset` (destructive last resort).
1433
1672
  */
1434
1673
  async function saveAgentFailure(params: {
1435
1674
  slug: string;
@@ -1477,8 +1716,10 @@ async function saveAgentFailure(params: {
1477
1716
  : `Agent run failed building '${slug}' [${cause}] (${detail}); could not ` +
1478
1717
  `mark '${slug}' stuck (${routed.reasonNotMoved ?? 'unknown'}).`;
1479
1718
  note(message);
1719
+ // PR-2b D3: a cleanly-surfaced bounce is GREEN (exitCode 0); a FAILED surface
1720
+ // stays non-zero.
1480
1721
  return {
1481
- exitCode: 1,
1722
+ exitCode: routed.moved ? 0 : 1,
1482
1723
  outcome: failureCauseToDoOutcome(cause),
1483
1724
  slug,
1484
1725
  branch,
@@ -1499,7 +1740,9 @@ function failureCauseToDoOutcome(cause: FailureCause): DoOutcome {
1499
1740
 
1500
1741
  /**
1501
1742
  * Build the HONEST result for a CONTINUE-site surface that did NOT land on the
1502
- * arbiter (`{moved: false}`). The tree-less `in-progress/ needs-attention/` move
1743
+ * arbiter (`{moved: false}`). The tree-less stuck-lock amend (post lock-cutover
1744
+ * `state: stuck` with the reason, in place of the retired `in-progress/ →
1745
+ * needs-attention/` move)
1503
1746
  * lost the CAS race against a busy arbiter (its contention-retry cap exhausted) or
1504
1747
  * had no arbiter to publish to, so the item is STILL in-progress on the arbiter —
1505
1748
  * a clean `needs-attention` would mislead (it claims the surface landed). Distinct
@@ -1544,14 +1787,14 @@ async function resolveStopReason(params: {
1544
1787
  cwd: string;
1545
1788
  arbiter: string;
1546
1789
  env: NodeJS.ProcessEnv | undefined;
1547
- }): Promise<string | undefined> {
1790
+ }): Promise<AgentStopClass | undefined> {
1548
1791
  const {output, slug, cwd, arbiter, env} = params;
1549
1792
  const sentinel = parseStopSentinel(output);
1550
1793
  if (sentinel !== undefined) {
1551
- return sentinel.reason;
1794
+ return {kind: 'sentinel', reason: sentinel.reason};
1552
1795
  }
1553
1796
  if (await isWorkBranchDiffEmpty({cwd, arbiter, env})) {
1554
- return emptyDiffStopReason(slug);
1797
+ return {kind: 'empty-diff', reason: emptyDiffStopReason(slug)};
1555
1798
  }
1556
1799
  return undefined;
1557
1800
  }
@@ -1570,12 +1813,66 @@ async function saveAgentStop(params: {
1570
1813
  cwd: string;
1571
1814
  arbiter: string;
1572
1815
  reason: string;
1816
+ kind: 'sentinel' | 'empty-diff';
1573
1817
  env: NodeJS.ProcessEnv | undefined;
1574
1818
  note: (message: string) => void;
1575
1819
  }): Promise<DoResult> {
1576
- const {slug, cwd, arbiter, reason, env, note} = params;
1820
+ const {slug, cwd, arbiter, reason, kind, env, note} = params;
1577
1821
  const branch = params.branch ?? workBranchRef('task', slug);
1578
1822
 
1823
+ // EMPTY-DIFF branch (spec resolved decision #2, task
1824
+ // `empty-diff-bounce-surfaces-dispose-defaulted-question`): surface a sidecar
1825
+ // on `<arbiter>/main` carrying an engine-authored DISPOSE-DEFAULTED envelope
1826
+ // question, THEN release the lock. "Nothing to do" is a non-deterministic LLM
1827
+ // judgement, so this seam MUST NOT blindly requeue (that would infinite-loop);
1828
+ // the surfaced sidecar + `needsAnswers:true` on `main` parks the item as a
1829
+ // human-visible question a `dispose` answer settles to `tasks/cancelled/`.
1830
+ if (kind === 'empty-diff') {
1831
+ const item = `task:${slug}`;
1832
+ const itemPath = resolveItemPathByIdentity(cwd, item);
1833
+ if (itemPath === undefined) {
1834
+ const message =
1835
+ `The agent STOPPED building '${slug}' (empty diff) but its body could ` +
1836
+ `not be located on the arbiter to surface a sidecar. Reason: ${reason}`;
1837
+ note(message);
1838
+ return {
1839
+ exitCode: 1,
1840
+ outcome: 'agent-stopped',
1841
+ slug,
1842
+ branch,
1843
+ routedToNeedsAttention: false,
1844
+ message,
1845
+ };
1846
+ }
1847
+ const surfaced = await surfaceStuckToNeedsAttention({
1848
+ cwd,
1849
+ slug,
1850
+ item,
1851
+ itemPath,
1852
+ reason,
1853
+ envelope: emptyDiffDisposeEnvelope({item, reason}),
1854
+ arbiter,
1855
+ env,
1856
+ note,
1857
+ });
1858
+ const message = surfaced.surfaced
1859
+ ? `The agent STOPPED building '${slug}' (empty diff); surfaced a ` +
1860
+ `dispose-defaulted question on ${arbiter}/main and released the lock. ` +
1861
+ `Reason: ${reason}`
1862
+ : `The agent STOPPED building '${slug}' (empty diff) but the surface ` +
1863
+ `did not land (${surfaced.reasonNotSurfaced ?? 'unknown'}). ` +
1864
+ `Reason: ${reason}`;
1865
+ note(message);
1866
+ return {
1867
+ exitCode: 1,
1868
+ outcome: 'agent-stopped',
1869
+ slug,
1870
+ branch,
1871
+ routedToNeedsAttention: surfaced.surfaced,
1872
+ message,
1873
+ };
1874
+ }
1875
+
1579
1876
  const routed = await ledgerWrite.applyNeedsAttentionTransition({
1580
1877
  cwd,
1581
1878
  slug,
@@ -1593,8 +1890,11 @@ async function saveAgentStop(params: {
1593
1890
  : `The agent STOPPED building '${slug}' but it could not be marked stuck ` +
1594
1891
  `(${routed.reasonNotMoved ?? 'unknown'}). Reason: ${reason}`;
1595
1892
  note(message);
1893
+ // PR-2b D3: agent-stopped (sentinel) on a clean surface is GREEN (exitCode 0);
1894
+ // a FAILED surface stays non-zero. The empty-diff branch above is UNCHANGED
1895
+ // (owned by `empty-diff-bounce-surfaces-dispose-defaulted-question`).
1596
1896
  return {
1597
- exitCode: 1,
1897
+ exitCode: routed.moved ? 0 : 1,
1598
1898
  outcome: 'agent-stopped',
1599
1899
  slug,
1600
1900
  branch,
@@ -1626,11 +1926,26 @@ async function runDoAgent(
1626
1926
  cwd: string,
1627
1927
  prompt: string,
1628
1928
  slug: string,
1629
- ): Promise<{ok: boolean; detail?: string; output?: string}> {
1929
+ ): Promise<{
1930
+ ok: boolean;
1931
+ detail?: string;
1932
+ output?: string;
1933
+ timedOut?: boolean;
1934
+ }> {
1630
1935
  if (options.dorfl) {
1631
1936
  return options.dorfl({cwd, prompt, slug, env: options.env});
1632
1937
  }
1633
1938
  const harness = options.harness ?? new NullHarness();
1939
+ // Convert the dorfl-internal deadline (minutes) into a wall-clock epoch-ms so
1940
+ // the harness (`launchAsync`) can race the child against it (spec
1941
+ // `graceful-pre-timeout-wip-checkpoint`). Absent ⇒ no deadline; a run that
1942
+ // finishes before it is byte-for-byte unchanged (the harness clears the
1943
+ // timer on normal exit). Forcing the async path when a deadline is set is
1944
+ // {@link launchWithOptionalWatch}'s job.
1945
+ const deadlineMs =
1946
+ options.agentDeadlineMinutes !== undefined
1947
+ ? Date.now() + options.agentDeadlineMinutes * 60_000
1948
+ : undefined;
1634
1949
  const launched = await launchWithOptionalWatch({
1635
1950
  harness,
1636
1951
  dir: cwd,
@@ -1642,6 +1957,7 @@ async function runDoAgent(
1642
1957
  sessionId: slug,
1643
1958
  sessionsDir: options.sessionsDir,
1644
1959
  watch: options.watch,
1960
+ deadlineMs,
1645
1961
  watchSink: options.watchSink,
1646
1962
  color: options.color,
1647
1963
  env: options.env,
@@ -1649,7 +1965,134 @@ async function runDoAgent(
1649
1965
  // Surface the agent's FINAL SUMMARY (`LaunchResult.output`) — the source channel
1650
1966
  // for the propose-mode PR body — instead of dropping it. Absent (no parseable
1651
1967
  // assistant text) ⇒ undefined ⇒ the body degrades to `--fill` (no regression).
1652
- return {ok: launched.ok, detail: launched.detail, output: launched.output};
1968
+ return {
1969
+ ok: launched.ok,
1970
+ detail: launched.detail,
1971
+ output: launched.output,
1972
+ timedOut: launched.timedOut,
1973
+ };
1974
+ }
1975
+
1976
+ /**
1977
+ * Route a DEADLINE STOP (spec `graceful-pre-timeout-wip-checkpoint`) through
1978
+ * the checkpoint machinery: ALWAYS save the WIP (commit + push), then
1979
+ * decide — progress AND under ceiling ⇒ AUTO-CONTINUE (release lock, keep
1980
+ * branch, NO sidecar, exit 0); else ⇒ SURFACE a `needsAnswers:true` question.
1981
+ * Shared by {@link performDo} and {@link runRemotePipeline} so the in-place
1982
+ * and remote forms route a deadline stop identically.
1983
+ */
1984
+ async function routeDeadlineCheckpoint(params: {
1985
+ slug: string;
1986
+ branch: string;
1987
+ cwd: string;
1988
+ arbiter: string;
1989
+ maxAutoCheckpoints: number;
1990
+ env: NodeJS.ProcessEnv | undefined;
1991
+ note: (message: string) => void;
1992
+ }): Promise<DoResult> {
1993
+ const {slug, branch, cwd, arbiter, maxAutoCheckpoints, env, note} = params;
1994
+
1995
+ // 1. ALWAYS save the WIP first: commit any residue + push the work branch.
1996
+ const savedReason = `deadline checkpoint save for '${slug}'`;
1997
+ const saved = await saveDeadlineCheckpoint({
1998
+ slug,
1999
+ branch,
2000
+ cwd,
2001
+ arbiter,
2002
+ reason: savedReason,
2003
+ env,
2004
+ note,
2005
+ });
2006
+ void saved;
2007
+
2008
+ // 2. Progress this session? Reuse the SAME primitive the STOP backstop uses.
2009
+ const emptyDiff = await isWorkBranchDiffEmpty({cwd, arbiter, env});
2010
+ const madeProgressThisSession = !emptyDiff;
2011
+
2012
+ // 3. Count consecutive deadline checkpoints on the branch (ceiling gate). The
2013
+ // marker commit we just saved is included — so the count is the number
2014
+ // of checkpoints INCLUDING this one.
2015
+ const checkpointCount = await countDeadlineCheckpointsOnBranch({
2016
+ cwd,
2017
+ arbiter,
2018
+ branch,
2019
+ env,
2020
+ });
2021
+
2022
+ if (madeProgressThisSession && checkpointCount <= maxAutoCheckpoints) {
2023
+ // AUTO-CONTINUE: release the lock via the SAME default keep+continue path
2024
+ // `requeue` uses (no --reset, no --reconcile, no sidecar). The branch is
2025
+ // KEPT on the arbiter so the next claim continues from its tip.
2026
+ const reason = deadlineAutoContinueReason({
2027
+ slug,
2028
+ count: checkpointCount,
2029
+ max: maxAutoCheckpoints,
2030
+ });
2031
+ const returned = await ledgerWrite.applyReturnToBacklogTransition({
2032
+ cwd,
2033
+ slug,
2034
+ arbiter,
2035
+ env,
2036
+ note,
2037
+ });
2038
+ if (returned.moved) {
2039
+ const message =
2040
+ `Auto-continued '${slug}' at the dorfl-internal deadline (checkpoint ` +
2041
+ `${checkpointCount}/${maxAutoCheckpoints}): WIP saved + branch pushed, ` +
2042
+ `lock released so the next tick continues from ${branch}. ${reason}`;
2043
+ note(message);
2044
+ return {
2045
+ exitCode: 0,
2046
+ outcome: 'deadline-auto-continued',
2047
+ slug,
2048
+ branch,
2049
+ message,
2050
+ };
2051
+ }
2052
+ // The auto-continue lock release did not land — fall through to SURFACE.
2053
+ note(
2054
+ `Auto-continue lock release for '${slug}' did not land ` +
2055
+ `(${returned.reasonNotMoved ?? 'unknown'}); surfacing instead.`,
2056
+ );
2057
+ }
2058
+
2059
+ // SURFACE: mark the lock stuck via the whole applyNeedsAttentionTransition
2060
+ // (save + push + stuck). The WIP was already saved above; a second save is
2061
+ // idempotent (empty commit is skipped inside routeToNeedsAttention).
2062
+ const surfaceReason = madeProgressThisSession
2063
+ ? deadlineSurfaceReason({
2064
+ slug,
2065
+ kind: 'ceiling',
2066
+ count: checkpointCount,
2067
+ max: maxAutoCheckpoints,
2068
+ })
2069
+ : deadlineSurfaceReason({slug, kind: 'no-progress'});
2070
+ const routed = await ledgerWrite.applyNeedsAttentionTransition({
2071
+ cwd,
2072
+ slug,
2073
+ reason: surfaceReason,
2074
+ arbiter,
2075
+ env,
2076
+ note,
2077
+ });
2078
+ const report = routed.moved ? routeReport(routed, branch) : undefined;
2079
+ const message = routed.moved
2080
+ ? `Surfaced '${slug}' at the deadline checkpoint (${
2081
+ madeProgressThisSession
2082
+ ? `ceiling ${checkpointCount}/${maxAutoCheckpoints}`
2083
+ : 'no progress this session'
2084
+ }); ${report!.fragment}. ${surfaceReason}`
2085
+ : `Could not surface '${slug}' at the deadline checkpoint ` +
2086
+ `(${routed.reasonNotMoved ?? 'unknown'}). ${surfaceReason}`;
2087
+ note(message);
2088
+ return {
2089
+ exitCode: 1,
2090
+ outcome: 'deadline-surfaced',
2091
+ slug,
2092
+ branch,
2093
+ routedToNeedsAttention: routed.moved,
2094
+ message,
2095
+ };
1653
2096
  }
1654
2097
 
1655
2098
  /**
@@ -1875,7 +2318,7 @@ export async function performDoRemote(
1875
2318
  try {
1876
2319
  // 2a. Resolve the slug across BOTH namespaces against the claim clone (it
1877
2320
  // carries `work/` from the mirror's main). A collision / resolution
1878
- // failure is a loud usage error; a `prd:` arg reaches the not-yet-wired
2321
+ // failure is a loud usage error; a `spec:` arg reaches the not-yet-wired
1879
2322
  // stub — identical behaviour to in-place `do`.
1880
2323
  let resolved;
1881
2324
  try {
@@ -1905,9 +2348,9 @@ export async function performDoRemote(
1905
2348
  arbiter: 'origin',
1906
2349
  doer: 'agent',
1907
2350
  autoTask: options.autoTask,
1908
- // EXPLICIT dispatch (same as the in-place path above): the `prd:<slug>` was
2351
+ // EXPLICIT dispatch (same as the in-place path above): the `spec:<slug>` was
1909
2352
  // NAMED (typed, or pool-filtered on `autoTask` before reaching here), so the
1910
- // tasking gate drops the policy term — only the prd's own readiness +
2353
+ // tasking gate drops the policy term — only the spec's own readiness +
1911
2354
  // `taskedAfter` bind, mirroring the build path vs `autoBuild`.
1912
2355
  explicit: true,
1913
2356
  dorfl: options.dorfl,
@@ -1919,22 +2362,26 @@ export async function performDoRemote(
1919
2362
  // `provider` is the SAME the task-build path threads (arg parity), but the
1920
2363
  // MODE is the per-TRANSITION TASKING resolution
1921
2364
  // (`per-transition-integration-mode-slicing-vs-build`):
1922
- // `taskingIntegration ?? integration`, so the `--remote prd:` output ALSO
2365
+ // `taskingIntegration ?? integration`, so the `--remote spec:` output ALSO
1923
2366
  // routes through the shared core with the tasking-resolved mode.
1924
2367
  integration: options.taskingIntegration ?? options.integration,
2368
+ // The cross-job merge-serialiser CAS-retry cap (config `mergeRetries`) —
2369
+ // threaded on the no-checkout `do --remote spec:` tasking path too,
2370
+ // mirroring the in-place site above.
2371
+ mergeRetries: options.mergeRetries,
1925
2372
  // The per-repo TASK-PLACEMENT default + the operator's explicit
1926
2373
  // override (task `runner-deterministic-slice-placement-policy-and-
1927
- // precedence`). Same threading as the in-place `do prd:` path.
2374
+ // precedence`). Same threading as the in-place `do spec:` path.
1928
2375
  tasksLandIn: options.tasksLandIn,
1929
2376
  explicitTasksLandIn: options.explicitTasksLandIn,
1930
2377
  noPR: options.noPR,
1931
2378
  providerInstance: options.providerInstance,
1932
- // The tasker review→edit→converge loop on the `do --remote prd:` path too.
2379
+ // The tasker review→edit→converge loop on the `do --remote spec:` path too.
1933
2380
  reviewLoop: options.reviewLoop,
1934
2381
  taskerLoopMax: options.taskerLoopMax,
1935
2382
  reviewExecutions: options.reviewExecutions,
1936
2383
  taskerLoopModel: options.taskerLoopModel,
1937
- // The task-SET ACCEPTANCE GATE on the `do --remote prd:` path too.
2384
+ // The task-SET ACCEPTANCE GATE on the `do --remote spec:` path too.
1938
2385
  review: options.review,
1939
2386
  reviewGate: options.taskReviewGate,
1940
2387
  acceptanceReviewModel: options.reviewModel,
@@ -2094,8 +2541,10 @@ async function runRemotePipeline(
2094
2541
  if (tree.continueRebaseConflict) {
2095
2542
  const reason =
2096
2543
  `continuing the kept ${tree.branch}: rebase onto the latest main ` +
2097
- 'conflicted (aborted, never auto-resolved) — resolve against the latest ' +
2098
- 'main, or `requeue --reset` to discard and start fresh';
2544
+ 'conflicted (aborted, never auto-resolved) — run `requeue --reconcile` ' +
2545
+ 'to non-destructively re-sync the mirror and retry the rebase (keeps ' +
2546
+ 'the work). Last resort: `requeue --reset` DESTRUCTIVELY discards the ' +
2547
+ 'branch and starts fresh.';
2099
2548
  await ledgerWrite.applyNeedsAttentionTransition({
2100
2549
  cwd,
2101
2550
  slug,
@@ -2127,8 +2576,10 @@ async function runRemotePipeline(
2127
2576
  const reason =
2128
2577
  `continuing the kept ${tree.branch}: publishing the rebased work branch ` +
2129
2578
  `to the arbiter failed terminally (${tree.continuePushFailure}) — the kept ` +
2130
- 'branch is left intact on the arbiter (recoverable); `requeue` to retry ' +
2131
- 'once the churn settles, or `requeue --reset` to discard and start fresh';
2579
+ 'branch is left intact on the arbiter (recoverable); `requeue --reconcile` ' +
2580
+ 'to non-destructively re-sync the mirror and retry once the churn ' +
2581
+ 'settles (keeps the work). Last resort: `requeue --reset` DESTRUCTIVELY ' +
2582
+ 'discards the branch and starts fresh.';
2132
2583
  await ledgerWrite.applyNeedsAttentionTransition({
2133
2584
  cwd,
2134
2585
  slug,
@@ -2238,7 +2689,7 @@ async function runRemotePipeline(
2238
2689
  content: readFileSync(task.path, 'utf8'),
2239
2690
  env,
2240
2691
  });
2241
- // Per-item override layer (mirrors in-place `do`): the task/prd frontmatter
2692
+ // Per-item override layer (mirrors in-place `do`): the task/spec frontmatter
2242
2693
  // may override the resolved repo `promptGuidance.testFirst` for THIS item.
2243
2694
  const itemGuidance = resolvePromptGuidanceForItem({
2244
2695
  cwd,
@@ -2265,7 +2716,12 @@ async function runRemotePipeline(
2265
2716
  throw err;
2266
2717
  }
2267
2718
 
2268
- let agent: {ok: boolean; detail?: string; output?: string};
2719
+ let agent: {
2720
+ ok: boolean;
2721
+ detail?: string;
2722
+ output?: string;
2723
+ timedOut?: boolean;
2724
+ };
2269
2725
  try {
2270
2726
  agent = await runDoAgent(options, cwd, prompt, slug);
2271
2727
  } catch (err) {
@@ -2280,6 +2736,19 @@ async function runRemotePipeline(
2280
2736
  note,
2281
2737
  });
2282
2738
  }
2739
+ // Deadline checkpoint on the no-checkout `do --remote` path — same routing
2740
+ // as the in-place path (see performDo).
2741
+ if (agent.timedOut) {
2742
+ return await routeDeadlineCheckpoint({
2743
+ slug,
2744
+ branch: branch ?? workBranchRef('task', slug),
2745
+ cwd,
2746
+ arbiter: arbiterRemote,
2747
+ maxAutoCheckpoints: options.maxAutoCheckpoints ?? 5,
2748
+ env,
2749
+ note,
2750
+ });
2751
+ }
2283
2752
  if (!agent.ok) {
2284
2753
  const detail = agent.detail ?? `the agent failed to build '${slug}'.`;
2285
2754
  return await saveRemoteAgentFailure({
@@ -2311,7 +2780,8 @@ async function runRemotePipeline(
2311
2780
  branch,
2312
2781
  cwd,
2313
2782
  arbiter: arbiterRemote,
2314
- reason: stopReason,
2783
+ reason: stopReason.reason,
2784
+ kind: stopReason.kind,
2315
2785
  env,
2316
2786
  note,
2317
2787
  });
@@ -2500,8 +2970,9 @@ async function saveRemoteAgentFailure(params: {
2500
2970
  : `Agent run failed building '${slug}' [${cause}] (${detail}); could not ` +
2501
2971
  `mark '${slug}' stuck (${routed.reasonNotMoved ?? 'unknown'}).`;
2502
2972
  note(message);
2973
+ // PR-2b D3: cleanly-surfaced remote agent failure is GREEN.
2503
2974
  return {
2504
- exitCode: 1,
2975
+ exitCode: routed.moved ? 0 : 1,
2505
2976
  outcome: failureCauseToDoOutcome(cause),
2506
2977
  slug,
2507
2978
  branch,
@@ -2568,7 +3039,7 @@ async function primeWorktreeTrackingRef(
2568
3039
  * identity) ride VERBATIM from the threaded `DoOptions` onto the structurally-
2569
3040
  * matching {@link DoRemoteOptions} fields; `cwd` is DROPPED (the worktree replaces
2570
3041
  * it) and `remote` + `workspacesDir` are supplied from this driver's closure (the
2571
- * mirror's arbiter URL + the agents' execution area). A `prd:` arg flows through
3042
+ * mirror's arbiter URL + the agents' execution area). A `spec:` arg flows through
2572
3043
  * unchanged — `performDoRemote` tasks it against the claim clone with NO build
2573
3044
  * worktree (the tasking/surface/triage/apply rungs are tree-less ledger moves, the
2574
3045
  * substrate the task's criterion 4 preserves).
@@ -2613,6 +3084,8 @@ export function jobWorktreeDoDriver(closure: {
2613
3084
  watch: options.watch,
2614
3085
  watchSink: options.watchSink,
2615
3086
  color: options.color,
3087
+ agentDeadlineMinutes: options.agentDeadlineMinutes,
3088
+ maxAutoCheckpoints: options.maxAutoCheckpoints,
2616
3089
  read: options.read,
2617
3090
  env: options.env,
2618
3091
  note: options.note,