dorfl 0.14.2 → 0.15.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 (418) hide show
  1. package/dist/advance-ci-template.d.ts +17 -16
  2. package/dist/advance-ci-template.d.ts.map +1 -1
  3. package/dist/advance-ci-template.js +42 -53
  4. package/dist/advance-ci-template.js.map +1 -1
  5. package/dist/advance-drivers.d.ts +8 -0
  6. package/dist/advance-drivers.d.ts.map +1 -1
  7. package/dist/advance-drivers.js +28 -4
  8. package/dist/advance-drivers.js.map +1 -1
  9. package/dist/advance-isolated.d.ts.map +1 -1
  10. package/dist/advance-isolated.js +3 -2
  11. package/dist/advance-isolated.js.map +1 -1
  12. package/dist/advance-lifecycle-template.d.ts +41 -40
  13. package/dist/advance-lifecycle-template.d.ts.map +1 -1
  14. package/dist/advance-lifecycle-template.js +309 -489
  15. package/dist/advance-lifecycle-template.js.map +1 -1
  16. package/dist/advance-loop-driver.d.ts.map +1 -1
  17. package/dist/advance-loop-driver.js +3 -2
  18. package/dist/advance-loop-driver.js.map +1 -1
  19. package/dist/advance-treeless-publish.d.ts +31 -0
  20. package/dist/advance-treeless-publish.d.ts.map +1 -1
  21. package/dist/advance-treeless-publish.js +40 -0
  22. package/dist/advance-treeless-publish.js.map +1 -1
  23. package/dist/advance.d.ts +63 -3
  24. package/dist/advance.d.ts.map +1 -1
  25. package/dist/advance.js +80 -12
  26. package/dist/advance.js.map +1 -1
  27. package/dist/advancing-lock.d.ts +9 -0
  28. package/dist/advancing-lock.d.ts.map +1 -1
  29. package/dist/advancing-lock.js +21 -1
  30. package/dist/advancing-lock.js.map +1 -1
  31. package/dist/agent-env.d.ts +61 -0
  32. package/dist/agent-env.d.ts.map +1 -0
  33. package/dist/agent-env.js +144 -0
  34. package/dist/agent-env.js.map +1 -0
  35. package/dist/agent-stop.d.ts +35 -0
  36. package/dist/agent-stop.d.ts.map +1 -1
  37. package/dist/agent-stop.js +62 -0
  38. package/dist/agent-stop.js.map +1 -1
  39. package/dist/apply-merge-action.d.ts +76 -34
  40. package/dist/apply-merge-action.d.ts.map +1 -1
  41. package/dist/apply-merge-action.js +339 -129
  42. package/dist/apply-merge-action.js.map +1 -1
  43. package/dist/apply-stuck-action.js +2 -2
  44. package/dist/apply-stuck-action.js.map +1 -1
  45. package/dist/ci-agent-result.d.ts +151 -0
  46. package/dist/ci-agent-result.d.ts.map +1 -0
  47. package/dist/ci-agent-result.js +314 -0
  48. package/dist/ci-agent-result.js.map +1 -0
  49. package/dist/ci-handoff-apply.d.ts +162 -0
  50. package/dist/ci-handoff-apply.d.ts.map +1 -0
  51. package/dist/ci-handoff-apply.js +497 -0
  52. package/dist/ci-handoff-apply.js.map +1 -0
  53. package/dist/ci-handoff-format.d.ts +369 -0
  54. package/dist/ci-handoff-format.d.ts.map +1 -0
  55. package/dist/ci-handoff-format.js +646 -0
  56. package/dist/ci-handoff-format.js.map +1 -0
  57. package/dist/ci-handoff-lfs.d.ts +146 -0
  58. package/dist/ci-handoff-lfs.d.ts.map +1 -0
  59. package/dist/ci-handoff-lfs.js +397 -0
  60. package/dist/ci-handoff-lfs.js.map +1 -0
  61. package/dist/ci-handoff.d.ts +99 -0
  62. package/dist/ci-handoff.d.ts.map +1 -0
  63. package/dist/ci-handoff.js +277 -0
  64. package/dist/ci-handoff.js.map +1 -0
  65. package/dist/ci-lock-outputs.d.ts +93 -0
  66. package/dist/ci-lock-outputs.d.ts.map +1 -0
  67. package/dist/ci-lock-outputs.js +223 -0
  68. package/dist/ci-lock-outputs.js.map +1 -0
  69. package/dist/ci-phase-build.d.ts +178 -0
  70. package/dist/ci-phase-build.d.ts.map +1 -0
  71. package/dist/ci-phase-build.js +989 -0
  72. package/dist/ci-phase-build.js.map +1 -0
  73. package/dist/ci-phase-driver.d.ts +247 -0
  74. package/dist/ci-phase-driver.d.ts.map +1 -0
  75. package/dist/ci-phase-driver.js +370 -0
  76. package/dist/ci-phase-driver.js.map +1 -0
  77. package/dist/ci-phase-intake.d.ts +172 -0
  78. package/dist/ci-phase-intake.d.ts.map +1 -0
  79. package/dist/ci-phase-intake.js +555 -0
  80. package/dist/ci-phase-intake.js.map +1 -0
  81. package/dist/ci-phase-tasking.d.ts +205 -0
  82. package/dist/ci-phase-tasking.d.ts.map +1 -0
  83. package/dist/ci-phase-tasking.js +906 -0
  84. package/dist/ci-phase-tasking.js.map +1 -0
  85. package/dist/ci-phase-treeless.d.ts +224 -0
  86. package/dist/ci-phase-treeless.d.ts.map +1 -0
  87. package/dist/ci-phase-treeless.js +1482 -0
  88. package/dist/ci-phase-treeless.js.map +1 -0
  89. package/dist/ci-read-token.d.ts +47 -0
  90. package/dist/ci-read-token.d.ts.map +1 -0
  91. package/dist/ci-read-token.js +80 -0
  92. package/dist/ci-read-token.js.map +1 -0
  93. package/dist/cli.d.ts.map +1 -1
  94. package/dist/cli.js +447 -22
  95. package/dist/cli.js.map +1 -1
  96. package/dist/close-job-template.d.ts +10 -1
  97. package/dist/close-job-template.d.ts.map +1 -1
  98. package/dist/close-job-template.js +46 -14
  99. package/dist/close-job-template.js.map +1 -1
  100. package/dist/close-job.d.ts +20 -5
  101. package/dist/close-job.d.ts.map +1 -1
  102. package/dist/close-job.js +65 -12
  103. package/dist/close-job.js.map +1 -1
  104. package/dist/complete.d.ts.map +1 -1
  105. package/dist/complete.js +20 -1
  106. package/dist/complete.js.map +1 -1
  107. package/dist/config.d.ts +41 -44
  108. package/dist/config.d.ts.map +1 -1
  109. package/dist/config.js +2 -4
  110. package/dist/config.js.map +1 -1
  111. package/dist/continue-branch.d.ts +63 -15
  112. package/dist/continue-branch.d.ts.map +1 -1
  113. package/dist/continue-branch.js +75 -8
  114. package/dist/continue-branch.js.map +1 -1
  115. package/dist/crashed-run-save.d.ts +73 -0
  116. package/dist/crashed-run-save.d.ts.map +1 -0
  117. package/dist/crashed-run-save.js +86 -0
  118. package/dist/crashed-run-save.js.map +1 -0
  119. package/dist/cwd-section.d.ts.map +1 -1
  120. package/dist/cwd-section.js +10 -2
  121. package/dist/cwd-section.js.map +1 -1
  122. package/dist/decision-engine.d.ts +10 -0
  123. package/dist/decision-engine.d.ts.map +1 -1
  124. package/dist/decision-engine.js.map +1 -1
  125. package/dist/do-config.d.ts +2 -24
  126. package/dist/do-config.d.ts.map +1 -1
  127. package/dist/do-config.js +1 -33
  128. package/dist/do-config.js.map +1 -1
  129. package/dist/do.d.ts +149 -1
  130. package/dist/do.d.ts.map +1 -1
  131. package/dist/do.js +115 -25
  132. package/dist/do.js.map +1 -1
  133. package/dist/dorfl-item-template.d.ts +98 -0
  134. package/dist/dorfl-item-template.d.ts.map +1 -0
  135. package/dist/dorfl-item-template.js +504 -0
  136. package/dist/dorfl-item-template.js.map +1 -0
  137. package/dist/env-config.d.ts.map +1 -1
  138. package/dist/env-config.js +7 -5
  139. package/dist/env-config.js.map +1 -1
  140. package/dist/frontmatter.d.ts +52 -0
  141. package/dist/frontmatter.d.ts.map +1 -1
  142. package/dist/frontmatter.js +136 -2
  143. package/dist/frontmatter.js.map +1 -1
  144. package/dist/gc.d.ts +13 -0
  145. package/dist/gc.d.ts.map +1 -1
  146. package/dist/gc.js +16 -2
  147. package/dist/gc.js.map +1 -1
  148. package/dist/git.d.ts +18 -1
  149. package/dist/git.d.ts.map +1 -1
  150. package/dist/git.js +70 -5
  151. package/dist/git.js.map +1 -1
  152. package/dist/harness.d.ts +44 -5
  153. package/dist/harness.d.ts.map +1 -1
  154. package/dist/harness.js +24 -1
  155. package/dist/harness.js.map +1 -1
  156. package/dist/index.d.ts +11 -4
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +7 -3
  159. package/dist/index.js.map +1 -1
  160. package/dist/install-ci-action-pins.d.ts +123 -0
  161. package/dist/install-ci-action-pins.d.ts.map +1 -0
  162. package/dist/install-ci-action-pins.js +190 -0
  163. package/dist/install-ci-action-pins.js.map +1 -0
  164. package/dist/install-ci-actions-pr-creation.d.ts +70 -0
  165. package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
  166. package/dist/install-ci-actions-pr-creation.js +106 -0
  167. package/dist/install-ci-actions-pr-creation.js.map +1 -0
  168. package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
  169. package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
  170. package/dist/install-ci-capabilities/dorfl-item.js +26 -0
  171. package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
  172. package/dist/install-ci-core.d.ts +155 -37
  173. package/dist/install-ci-core.d.ts.map +1 -1
  174. package/dist/install-ci-core.js +304 -200
  175. package/dist/install-ci-core.js.map +1 -1
  176. package/dist/install-ci-github.d.ts +47 -2
  177. package/dist/install-ci-github.d.ts.map +1 -1
  178. package/dist/install-ci-github.js +113 -1
  179. package/dist/install-ci-github.js.map +1 -1
  180. package/dist/install-ci.d.ts +25 -6
  181. package/dist/install-ci.d.ts.map +1 -1
  182. package/dist/install-ci.js +72 -36
  183. package/dist/install-ci.js.map +1 -1
  184. package/dist/intake-marker.d.ts +6 -1
  185. package/dist/intake-marker.d.ts.map +1 -1
  186. package/dist/intake-marker.js.map +1 -1
  187. package/dist/intake-triage.d.ts.map +1 -1
  188. package/dist/intake-triage.js +6 -2
  189. package/dist/intake-triage.js.map +1 -1
  190. package/dist/intake-trigger-template.d.ts +31 -25
  191. package/dist/intake-trigger-template.d.ts.map +1 -1
  192. package/dist/intake-trigger-template.js +107 -292
  193. package/dist/intake-trigger-template.js.map +1 -1
  194. package/dist/intake.d.ts +186 -4
  195. package/dist/intake.d.ts.map +1 -1
  196. package/dist/intake.js +533 -416
  197. package/dist/intake.js.map +1 -1
  198. package/dist/integration-core.d.ts +154 -1
  199. package/dist/integration-core.d.ts.map +1 -1
  200. package/dist/integration-core.js +759 -486
  201. package/dist/integration-core.js.map +1 -1
  202. package/dist/integrator.d.ts +12 -0
  203. package/dist/integrator.d.ts.map +1 -1
  204. package/dist/integrator.js +21 -0
  205. package/dist/integrator.js.map +1 -1
  206. package/dist/isolation.d.ts +12 -2
  207. package/dist/isolation.d.ts.map +1 -1
  208. package/dist/isolation.js +19 -8
  209. package/dist/isolation.js.map +1 -1
  210. package/dist/item-lock.d.ts +93 -11
  211. package/dist/item-lock.d.ts.map +1 -1
  212. package/dist/item-lock.js +233 -65
  213. package/dist/item-lock.js.map +1 -1
  214. package/dist/land-question-residue.d.ts +58 -0
  215. package/dist/land-question-residue.d.ts.map +1 -0
  216. package/dist/land-question-residue.js +95 -0
  217. package/dist/land-question-residue.js.map +1 -0
  218. package/dist/lifecycle-gather.d.ts +16 -0
  219. package/dist/lifecycle-gather.d.ts.map +1 -1
  220. package/dist/lifecycle-gather.js +54 -6
  221. package/dist/lifecycle-gather.js.map +1 -1
  222. package/dist/merge-answer.d.ts +41 -0
  223. package/dist/merge-answer.d.ts.map +1 -0
  224. package/dist/merge-answer.js +60 -0
  225. package/dist/merge-answer.js.map +1 -0
  226. package/dist/merge-question-surfacer.d.ts +105 -30
  227. package/dist/merge-question-surfacer.d.ts.map +1 -1
  228. package/dist/merge-question-surfacer.js +202 -91
  229. package/dist/merge-question-surfacer.js.map +1 -1
  230. package/dist/merge-question-tick.d.ts +64 -0
  231. package/dist/merge-question-tick.d.ts.map +1 -0
  232. package/dist/merge-question-tick.js +96 -0
  233. package/dist/merge-question-tick.js.map +1 -0
  234. package/dist/mint-adr.d.ts.map +1 -1
  235. package/dist/mint-adr.js +20 -2
  236. package/dist/mint-adr.js.map +1 -1
  237. package/dist/needs-attention.d.ts +72 -2
  238. package/dist/needs-attention.d.ts.map +1 -1
  239. package/dist/needs-attention.js +275 -38
  240. package/dist/needs-attention.js.map +1 -1
  241. package/dist/phase-recorder.d.ts +168 -0
  242. package/dist/phase-recorder.d.ts.map +1 -0
  243. package/dist/phase-recorder.js +218 -0
  244. package/dist/phase-recorder.js.map +1 -0
  245. package/dist/phase.d.ts +94 -0
  246. package/dist/phase.d.ts.map +1 -0
  247. package/dist/phase.js +148 -0
  248. package/dist/phase.js.map +1 -0
  249. package/dist/pi-harness.d.ts.map +1 -1
  250. package/dist/pi-harness.js +19 -6
  251. package/dist/pi-harness.js.map +1 -1
  252. package/dist/prepare.d.ts.map +1 -1
  253. package/dist/prepare.js +4 -0
  254. package/dist/prepare.js.map +1 -1
  255. package/dist/prompt.d.ts +4 -0
  256. package/dist/prompt.d.ts.map +1 -1
  257. package/dist/prompt.js +48 -1
  258. package/dist/prompt.js.map +1 -1
  259. package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
  260. package/dist/protocol/WORK-CONTRACT.md +1 -1
  261. package/dist/reap-branches.d.ts +48 -0
  262. package/dist/reap-branches.d.ts.map +1 -1
  263. package/dist/reap-branches.js +64 -0
  264. package/dist/reap-branches.js.map +1 -1
  265. package/dist/ref-write.d.ts +198 -0
  266. package/dist/ref-write.d.ts.map +1 -0
  267. package/dist/ref-write.js +79 -0
  268. package/dist/ref-write.js.map +1 -0
  269. package/dist/repo-config.js +8 -8
  270. package/dist/repo-config.js.map +1 -1
  271. package/dist/repo-mirror.d.ts +36 -0
  272. package/dist/repo-mirror.d.ts.map +1 -1
  273. package/dist/repo-mirror.js +91 -1
  274. package/dist/repo-mirror.js.map +1 -1
  275. package/dist/review-verdict.d.ts +1 -1
  276. package/dist/review-verdict.js +1 -1
  277. package/dist/run.d.ts +7 -1
  278. package/dist/run.d.ts.map +1 -1
  279. package/dist/run.js +28 -4
  280. package/dist/run.js.map +1 -1
  281. package/dist/scan.d.ts +19 -1
  282. package/dist/scan.d.ts.map +1 -1
  283. package/dist/scan.js +21 -8
  284. package/dist/scan.js.map +1 -1
  285. package/dist/sidecar.d.ts +14 -0
  286. package/dist/sidecar.d.ts.map +1 -1
  287. package/dist/sidecar.js +21 -0
  288. package/dist/sidecar.js.map +1 -1
  289. package/dist/single-job-warning.d.ts +63 -0
  290. package/dist/single-job-warning.d.ts.map +1 -0
  291. package/dist/single-job-warning.js +110 -0
  292. package/dist/single-job-warning.js.map +1 -0
  293. package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
  294. package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
  295. package/dist/spec-complete.d.ts +13 -6
  296. package/dist/spec-complete.d.ts.map +1 -1
  297. package/dist/spec-complete.js +23 -11
  298. package/dist/spec-complete.js.map +1 -1
  299. package/dist/start.d.ts.map +1 -1
  300. package/dist/start.js +47 -14
  301. package/dist/start.js.map +1 -1
  302. package/dist/status.d.ts +12 -0
  303. package/dist/status.d.ts.map +1 -1
  304. package/dist/status.js +11 -3
  305. package/dist/status.js.map +1 -1
  306. package/dist/tasking.d.ts +246 -1
  307. package/dist/tasking.d.ts.map +1 -1
  308. package/dist/tasking.js +452 -358
  309. package/dist/tasking.js.map +1 -1
  310. package/dist/triage-persist.d.ts.map +1 -1
  311. package/dist/triage-persist.js +21 -2
  312. package/dist/triage-persist.js.map +1 -1
  313. package/dist/verify-workflow-template.d.ts.map +1 -1
  314. package/dist/verify-workflow-template.js +8 -4
  315. package/dist/verify-workflow-template.js.map +1 -1
  316. package/dist/verify.d.ts.map +1 -1
  317. package/dist/verify.js +4 -0
  318. package/dist/verify.js.map +1 -1
  319. package/dist/watch-session.d.ts +42 -13
  320. package/dist/watch-session.d.ts.map +1 -1
  321. package/dist/watch-session.js +52 -23
  322. package/dist/watch-session.js.map +1 -1
  323. package/dist/work-layout.d.ts +10 -4
  324. package/dist/work-layout.d.ts.map +1 -1
  325. package/dist/work-layout.js +11 -3
  326. package/dist/work-layout.js.map +1 -1
  327. package/dist/workspace.d.ts +65 -4
  328. package/dist/workspace.d.ts.map +1 -1
  329. package/dist/workspace.js +159 -11
  330. package/dist/workspace.js.map +1 -1
  331. package/package.json +1 -1
  332. package/src/advance-ci-template.ts +80 -81
  333. package/src/advance-drivers.ts +45 -6
  334. package/src/advance-isolated.ts +3 -5
  335. package/src/advance-lifecycle-template.ts +375 -538
  336. package/src/advance-loop-driver.ts +3 -5
  337. package/src/advance-treeless-publish.ts +61 -0
  338. package/src/advance.ts +145 -15
  339. package/src/advancing-lock.ts +36 -1
  340. package/src/agent-env.ts +174 -0
  341. package/src/agent-stop.ts +81 -0
  342. package/src/apply-merge-action.ts +401 -154
  343. package/src/apply-stuck-action.ts +2 -2
  344. package/src/ci-agent-result.ts +421 -0
  345. package/src/ci-handoff-apply.ts +737 -0
  346. package/src/ci-handoff-format.ts +926 -0
  347. package/src/ci-handoff-lfs.ts +519 -0
  348. package/src/ci-handoff.ts +392 -0
  349. package/src/ci-lock-outputs.ts +273 -0
  350. package/src/ci-phase-build.ts +1328 -0
  351. package/src/ci-phase-driver.ts +548 -0
  352. package/src/ci-phase-intake.ts +861 -0
  353. package/src/ci-phase-tasking.ts +1255 -0
  354. package/src/ci-phase-treeless.ts +2028 -0
  355. package/src/ci-read-token.ts +90 -0
  356. package/src/cli.ts +591 -29
  357. package/src/close-job-template.ts +52 -15
  358. package/src/close-job.ts +77 -12
  359. package/src/complete.ts +19 -1
  360. package/src/config.ts +43 -48
  361. package/src/continue-branch.ts +121 -16
  362. package/src/crashed-run-save.ts +162 -0
  363. package/src/cwd-section.ts +10 -0
  364. package/src/decision-engine.ts +10 -0
  365. package/src/do-config.ts +2 -57
  366. package/src/do.ts +170 -28
  367. package/src/dorfl-item-template.ts +556 -0
  368. package/src/env-config.ts +7 -5
  369. package/src/frontmatter.ts +155 -2
  370. package/src/gc.ts +27 -2
  371. package/src/git.ts +83 -5
  372. package/src/harness.ts +58 -6
  373. package/src/index.ts +50 -1
  374. package/src/install-ci-action-pins.ts +209 -0
  375. package/src/install-ci-actions-pr-creation.ts +186 -0
  376. package/src/install-ci-capabilities/dorfl-item.ts +33 -0
  377. package/src/install-ci-core.ts +388 -233
  378. package/src/install-ci-github.ts +149 -1
  379. package/src/install-ci.ts +83 -37
  380. package/src/intake-marker.ts +6 -1
  381. package/src/intake-triage.ts +7 -3
  382. package/src/intake-trigger-template.ts +126 -327
  383. package/src/intake.ts +725 -544
  384. package/src/integration-core.ts +975 -523
  385. package/src/integrator.ts +28 -0
  386. package/src/isolation.ts +29 -10
  387. package/src/item-lock.ts +308 -92
  388. package/src/land-question-residue.ts +122 -0
  389. package/src/lifecycle-gather.ts +82 -10
  390. package/src/merge-answer.ts +82 -0
  391. package/src/merge-question-surfacer.ts +320 -112
  392. package/src/merge-question-tick.ts +182 -0
  393. package/src/mint-adr.ts +28 -2
  394. package/src/needs-attention.ts +350 -42
  395. package/src/phase-recorder.ts +350 -0
  396. package/src/phase.ts +177 -0
  397. package/src/pi-harness.ts +25 -5
  398. package/src/prepare.ts +4 -0
  399. package/src/prompt.ts +68 -1
  400. package/src/reap-branches.ts +107 -0
  401. package/src/ref-write.ts +324 -0
  402. package/src/repo-config.ts +8 -8
  403. package/src/repo-mirror.ts +107 -1
  404. package/src/review-verdict.ts +1 -1
  405. package/src/run.ts +47 -6
  406. package/src/scan.ts +37 -7
  407. package/src/sidecar.ts +36 -0
  408. package/src/single-job-warning.ts +135 -0
  409. package/src/spec-complete.ts +36 -14
  410. package/src/start.ts +55 -16
  411. package/src/status.ts +29 -3
  412. package/src/tasking.ts +593 -378
  413. package/src/triage-persist.ts +24 -2
  414. package/src/verify-workflow-template.ts +8 -4
  415. package/src/verify.ts +4 -0
  416. package/src/watch-session.ts +86 -28
  417. package/src/work-layout.ts +11 -3
  418. package/src/workspace.ts +210 -14
package/src/prompt.ts CHANGED
@@ -242,6 +242,8 @@ export function wrapper(
242
242
  protocolPath?: string;
243
243
  cwd?: string;
244
244
  promptGuidance?: {testFirst?: boolean};
245
+ /** The folder the body was resolved from; see {@link retargetBodyFolder}. */
246
+ taskFolder?: TaskFolder;
245
247
  } = {},
246
248
  ): string {
247
249
  const protocolPath = resolveProtocolDoc(
@@ -251,10 +253,72 @@ export function wrapper(
251
253
  );
252
254
  const protocol = readFileSync(protocolPath, 'utf8');
253
255
  const template = extractCanonicalWrapperTemplate(protocol);
254
- const resolved = applyPromptGuidance(template, options.promptGuidance);
256
+ const resolved = retargetSpecFolder(
257
+ retargetBodyFolder(
258
+ applyPromptGuidance(template, options.promptGuidance),
259
+ options.taskFolder,
260
+ ),
261
+ spec,
262
+ options.cwd,
263
+ );
255
264
  return resolved.replace(/<slug>/g, slug).replace(/<spec>/g, spec ?? '<spec>');
256
265
  }
257
266
 
267
+ /**
268
+ * Point the wrapper's source-spec mention at the folder the spec ACTUALLY rests
269
+ * in. The canonical wrapper assumes `work/specs/ready/<spec>.md`, but once a spec
270
+ * has been tasked it rests in `work/specs/tasked/`, so builds of its tasks were
271
+ * told to read a file that does not exist. Located with {@link findSpecPath} (the
272
+ * same ready-then-tasked lookup the per-item prompt-guidance layer uses). No
273
+ * `cwd`, no spec, or a spec in `specs/ready/` (or nowhere) leaves the text as is.
274
+ */
275
+ function retargetSpecFolder(
276
+ template: string,
277
+ spec: string | undefined,
278
+ cwd: string | undefined,
279
+ ): string {
280
+ if (spec === undefined || cwd === undefined) {
281
+ return template;
282
+ }
283
+ const found = findSpecPath(cwd, spec);
284
+ if (found === undefined || found === workItemPath(cwd, 'specs-ready', spec)) {
285
+ return template;
286
+ }
287
+ return template
288
+ .split(`${workFolderRel('specs-ready')}/<spec>.md`)
289
+ .join(`${workFolderRel('specs-tasked')}/<spec>.md`);
290
+ }
291
+
292
+ /**
293
+ * Point the wrapper's body-path mentions at the folder the body was ACTUALLY
294
+ * resolved from. The canonical wrapper (CLAIM-PROTOCOL.md) describes the normal
295
+ * case, a claim from the pool, so it names `work/tasks/ready/<slug>.md`. Under
296
+ * `do --allow-backlog` the body rests in `work/tasks/backlog/` (and a stranded
297
+ * continue resolves it from `work/tasks/done/`), and telling the agent the wrong
298
+ * path made build agents file observations about it.
299
+ *
300
+ * A TEXT rewrite of the extracted template, not a new placeholder: a target repo's
301
+ * adopted `work/protocol/CLAIM-PROTOCOL.md` may predate any placeholder, and the
302
+ * rewrite still works on it. Pool bodies (`tasks-ready`) and an omitted folder
303
+ * leave the wrapper byte-identical to the canonical text.
304
+ */
305
+ function retargetBodyFolder(
306
+ template: string,
307
+ folder: TaskFolder | undefined,
308
+ ): string {
309
+ if (folder === undefined || folder === 'tasks-ready') {
310
+ return template;
311
+ }
312
+ const pool = workFolderRel('tasks-ready');
313
+ const actual = workFolderRel(folder);
314
+ const poolBare = pool.slice(`${WORK_ROOT}/`.length);
315
+ return template
316
+ .split(`${pool}/<slug>.md`)
317
+ .join(`${actual}/<slug>.md`)
318
+ .split(`body ${poolBare}/ ->`)
319
+ .join(`body ${actual}/ ->`);
320
+ }
321
+
258
322
  /**
259
323
  * The CONTINUE context that turns the fresh-start assembly into a continue-mode
260
324
  * one (the `agent-prompt-continue-context` task). It is OPT-IN state inherited
@@ -434,6 +498,8 @@ export function buildAgentPrompt(
434
498
  cwd?: string;
435
499
  continueContext?: ContinueContext;
436
500
  promptGuidance?: {testFirst?: boolean};
501
+ /** The folder the task body was resolved from (`ResolvedTask.folder`). */
502
+ taskFolder?: TaskFolder;
437
503
  } = {},
438
504
  ): string {
439
505
  const head = wrapper(slug, spec, options);
@@ -754,5 +820,6 @@ export function renderPrompt(options: PromptOptions): string {
754
820
  protocolPath: options.protocolPath,
755
821
  cwd: options.cwd,
756
822
  promptGuidance: resolvedGuidance,
823
+ taskFolder: task.folder,
757
824
  });
758
825
  }
@@ -1,5 +1,9 @@
1
1
  import {run} from './git.js';
2
2
  import {isProvablyMergedForReap} from './gc.js';
3
+ import {
4
+ classifyTerminalItemLocks,
5
+ reconcileTerminalItemLocks,
6
+ } from './item-lock.js';
3
7
  import {parseWorkBranchRef} from './slug-namespace.js';
4
8
  import type {SidecarType} from './sidecar.js';
5
9
 
@@ -178,6 +182,9 @@ export function sweepRemoteMergedBranches(
178
182
  input.cwd,
179
183
  {env},
180
184
  );
185
+ // WRITE-SEAM EXEMPT: `gc --remote-branches` runs in a no-agent job (or by
186
+ // a human), never in a CI agent job, so this merged-branch delete stays a
187
+ // direct push (task `ci-split-route-direct-writes-through-seams`).
181
188
  const del = run(
182
189
  'git',
183
190
  ['push', input.arbiter, '--delete', branch],
@@ -209,6 +216,106 @@ export function sweepRemoteMergedBranches(
209
216
  return {reaped, retained, wouldReap};
210
217
  }
211
218
 
219
+ export interface SweepTerminalItemLocksInput {
220
+ /**
221
+ * A local repo (a checkout or a hub mirror) whose `<arbiter>` remote points at
222
+ * the arbiter whose per-item locks to sweep. Only refs are read + deleted.
223
+ */
224
+ cwd: string;
225
+ /** The arbiter remote name (the SAME one the branch sweep targets). */
226
+ arbiter: string;
227
+ /** Sink for human-readable progress notes (per released / kept lock). */
228
+ note?: (message: string) => void;
229
+ /** Only REPORT which locks WOULD be released (the `--dry-run` preview). */
230
+ dryRun?: boolean;
231
+ env?: NodeJS.ProcessEnv;
232
+ }
233
+
234
+ export interface SweepTerminalItemLocksResult {
235
+ /** Lock entries whose item is terminal on main and whose ref was deleted. */
236
+ released: string[];
237
+ /** On `dryRun`, the lock entries that WOULD be released (nothing deleted). */
238
+ wouldRelease: string[];
239
+ /** Lock entries left HELD because the item is not terminal on main. */
240
+ kept: string[];
241
+ /**
242
+ * Locks left HELD because their classification or release faulted, including
243
+ * a lease REJECTED because the ref moved between the read and the delete.
244
+ */
245
+ errors: {entry: string; message: string}[];
246
+ }
247
+
248
+ /**
249
+ * Release every per-item lock whose item is already TERMINAL on the arbiter's
250
+ * `main` (task `ci-releases-locks-of-terminal-items`). It rides the SAME
251
+ * `gc --remote-branches` invocation the scheduled `reap-merged-branches` CI job
252
+ * runs, so the locks a merged propose PR leaves behind stop accumulating in CI
253
+ * (where no later claim of that item ever comes to release them) WITHOUT any
254
+ * change to the generated workflows.
255
+ *
256
+ * It is NOT a second mechanism: it is exactly the terminal-lock reconcile behind
257
+ * `status --reconcile-locks` and the claim path
258
+ * ({@link reconcileTerminalItemLocks}): ONE predicate (the item's body rests in a
259
+ * terminal folder on `<arbiter>/main`), every delete leased on the sha that was
260
+ * read (`--force-with-lease`, never forced), every uncertainty resolved towards
261
+ * KEEPING the lock. SCOPE is the one arbiter the branch sweep targets (the
262
+ * `<arbiter>` remote of `cwd`). `dryRun` uses the read-only twin
263
+ * ({@link classifyTerminalItemLocks}). Best-effort and never throws.
264
+ */
265
+ export async function sweepTerminalItemLocks(
266
+ input: SweepTerminalItemLocksInput,
267
+ ): Promise<SweepTerminalItemLocksResult> {
268
+ const note = input.note ?? (() => {});
269
+ const out: SweepTerminalItemLocksResult = {
270
+ released: [],
271
+ wouldRelease: [],
272
+ kept: [],
273
+ errors: [],
274
+ };
275
+ try {
276
+ if (input.dryRun === true) {
277
+ const classified = await classifyTerminalItemLocks(
278
+ input.cwd,
279
+ input.arbiter,
280
+ input.env,
281
+ );
282
+ out.wouldRelease = classified.terminal.map((l) => l.entry);
283
+ out.kept = classified.inFlight.map((l) => l.entry);
284
+ out.errors = classified.errors;
285
+ } else {
286
+ const rec = await reconcileTerminalItemLocks(
287
+ input.cwd,
288
+ input.arbiter,
289
+ input.env,
290
+ );
291
+ out.released = rec.released;
292
+ const errored = new Set(rec.errors.map((e) => e.entry));
293
+ // `kept` in the reconcile report ALSO lists a terminal lock whose release
294
+ // was refused; that one is reported under `errors` instead, so the two
295
+ // lists here stay disjoint.
296
+ out.kept = rec.kept.filter((entry) => !errored.has(entry));
297
+ out.errors = rec.errors;
298
+ }
299
+ } catch (err) {
300
+ out.errors.push({
301
+ entry: '(locks)',
302
+ message: err instanceof Error ? err.message : String(err),
303
+ });
304
+ }
305
+ for (const entry of out.wouldRelease) {
306
+ note(
307
+ `Would release lock ${entry} (item terminal on main) on ${input.arbiter}.`,
308
+ );
309
+ }
310
+ for (const entry of out.released) {
311
+ note(`Released lock ${entry} (item terminal on main) on ${input.arbiter}.`);
312
+ }
313
+ for (const e of out.errors) {
314
+ note(`Kept lock ${e.entry}: ${e.message}`);
315
+ }
316
+ return out;
317
+ }
318
+
212
319
  /** Push a retained branch into the result + emit its note. */
213
320
  function retain(
214
321
  retained: RetainedBranch[],
@@ -0,0 +1,324 @@
1
+ import {runAsync, type RunResult} from './git.js';
2
+ import {
3
+ pushContinuedBranchWithStaleLeaseRetry,
4
+ type ContinuedPushResult,
5
+ } from './continue-branch.js';
6
+ import {
7
+ routeToNeedsAttention,
8
+ deleteRemoteWorkBranchIfPresent,
9
+ type RouteToNeedsAttentionOptions,
10
+ type RouteToNeedsAttentionResult,
11
+ type DeleteRemoteWorkBranchOptions,
12
+ type DeleteRemoteWorkBranchResult,
13
+ } from './needs-attention.js';
14
+ import {
15
+ pushTreelessResult,
16
+ type PushTreelessResultParams,
17
+ } from './advance-treeless-publish.js';
18
+
19
+ /**
20
+ * The **ref-write seam**: the sibling of the ledger-transition write seam
21
+ * ({@link import('./ledger-write.js').LedgerWriteStrategy}) for every network
22
+ * write a CI code path reaches that is NOT a `work/` ledger transition, the
23
+ * review provider, the issue provider or the integrator: the per-item lock ref
24
+ * create / amend / leased delete, the continued work-branch rebase push, the
25
+ * work-branch save (commit WIP + push, without a surface), the remote work-branch
26
+ * delete, the tasking candidates branch push, the tree-less `HEAD:main`
27
+ * publish and the Git LFS object upload.
28
+ *
29
+ * Why it exists (spec `ci-agent-job-without-write-token`, ADR
30
+ * `ci-agent-job-holds-no-write-token`, task
31
+ * `ci-split-route-direct-writes-through-seams`): the CI phase mode records every
32
+ * write in the agent phase and performs it in the apply phase, so every write
33
+ * must be VISIBLE at a seam. Before this seam these sites pushed directly.
34
+ *
35
+ * It is a PURE REFACTOR, the same shape as the ledger seam: exactly ONE strategy
36
+ * ({@link currentRefWrite}) that does exactly what each call site did before (the
37
+ * same git argument lists, the same helpers), no mode, no config. Each method is
38
+ * named for the write's INTENT, not the git plumbing, so a recording strategy can
39
+ * capture "what was about to be written" rather than a raw argument list.
40
+ *
41
+ * A test swaps a method with `vi.spyOn(refWrite, '<method>')` (the same pattern
42
+ * the `ledgerWrite` / `ledgerRead` seams use). The guard test
43
+ * `write-sites-through-seams.test.ts` fails on any new direct `git push` or `gh`
44
+ * write outside the seam implementations and the documented exempt sites.
45
+ */
46
+ export interface RefWriteStrategy {
47
+ /**
48
+ * CREATE a per-item lock ref on the arbiter: push `<commit>:<ref>` with the
49
+ * create-only lease `--force-with-lease=<ref>:` (succeeds iff the ref is
50
+ * absent). Returns the raw push result; the caller interprets it (`acquired`
51
+ * vs contended). CI phase: lock.
52
+ */
53
+ createLockRef(input: LockRefCreateInput): Promise<RunResult>;
54
+ /**
55
+ * DELETE a held per-item lock ref on the arbiter, leased on the sha the caller
56
+ * read (`push --delete <ref> --force-with-lease=<ref>:<expectedSha>`). CI
57
+ * phase: apply (the lease is on the lock job's `lockSha`).
58
+ */
59
+ deleteLockRef(input: LockRefDeleteInput): Promise<RunResult>;
60
+ /**
61
+ * REPLACE a held per-item lock ref on the arbiter with a new lock commit,
62
+ * leased on the sha the caller read (`push <commit>:<ref>
63
+ * --force-with-lease=<ref>:<expectedSha>`): the amend of a held lock (the
64
+ * propose-kept marker, and the answered-merge takeover of a lock carrying
65
+ * it). A concurrent change to the ref rejects the push; it is never forced.
66
+ * CI phase: lock (the takeover) and apply (the marker).
67
+ */
68
+ replaceLockRef(input: LockRefReplaceInput): Promise<RunResult>;
69
+ /**
70
+ * Push the CONTINUED (rebased) kept `work/<slug>` branch with a lease on the
71
+ * tip observed before the rebase, surviving a stale lease by re-fetching and
72
+ * re-rebasing (see {@link pushContinuedBranchWithStaleLeaseRetry}). Throws
73
+ * exactly as that helper does. CI phase: apply (ADR decision 7).
74
+ */
75
+ pushContinuedBranch(input: ContinuedBranchPushInput): ContinuedPushResult;
76
+ /**
77
+ * Push an already-rebased work branch ONCE with a lease on the tip observed
78
+ * earlier (`<commit>:refs/heads/<branch>
79
+ * --force-with-lease=refs/heads/<branch>:<expectedTip>`), with NO retry and NO
80
+ * re-rebase: a stale lease is returned for the caller to report. The CI apply
81
+ * phase's continue push (ADR `ci-agent-job-holds-no-write-token` decision 7:
82
+ * the agent job rebased the kept branch locally, and the lease is the lock
83
+ * job's `continueTip`). CI phase: apply.
84
+ */
85
+ pushLeasedWorkBranch(input: LeasedWorkBranchPushInput): Promise<RunResult>;
86
+ /**
87
+ * SAVE the work branch: commit any uncommitted work as a wip commit and push
88
+ * the work branch to the arbiter, WITHOUT surfacing the item (the recoverable
89
+ * half of a needs-attention route, see {@link routeToNeedsAttention}). Used by
90
+ * the deadline checkpoint save. CI phase: apply.
91
+ */
92
+ saveWorkBranch(
93
+ input: RouteToNeedsAttentionOptions,
94
+ ): Promise<RouteToNeedsAttentionResult>;
95
+ /**
96
+ * DELETE the remote `work/task-<slug>` branch on the arbiter (write-through:
97
+ * local refs first, then the arbiter delete; already-gone is tolerated, see
98
+ * {@link deleteRemoteWorkBranchIfPresent}). CI phase: apply.
99
+ */
100
+ deleteRemoteWorkBranch(
101
+ input: DeleteRemoteWorkBranchOptions,
102
+ ): Promise<DeleteRemoteWorkBranchResult>;
103
+ /**
104
+ * Push the tasking candidates branch (`work/spec-<slug>`) to the arbiter, a
105
+ * plain `git push <arbiter> <branch>` (best-effort at the caller). CI phase:
106
+ * apply.
107
+ */
108
+ pushTaskingCandidatesBranch(
109
+ input: TaskingCandidatesPushInput,
110
+ ): Promise<RunResult>;
111
+ /**
112
+ * Publish a tree-less rung's local commit to the arbiter's `main` (`git push
113
+ * HEAD:main` with the bounded re-fetch + rebase retry, never `--force`, see
114
+ * {@link pushTreelessResult}). CI phase: apply.
115
+ */
116
+ publishTreelessResult(input: PushTreelessResultParams): Promise<void>;
117
+ /**
118
+ * UPLOAD Git LFS objects already in `cwd`'s local LFS store to the arbiter's
119
+ * LFS endpoint (`git lfs push --object-id <arbiter> <oids>`, in batches),
120
+ * BEFORE any ref that points at them (ADR `ci-agent-job-holds-no-write-token`
121
+ * decision 6). Returns the first failing batch's result, else the last one.
122
+ * CI phase: apply.
123
+ */
124
+ pushLfsObjects(input: LfsObjectsPushInput): Promise<RunResult>;
125
+ }
126
+
127
+ /** Input of {@link RefWriteStrategy.pushLfsObjects}. */
128
+ export interface LfsObjectsPushInput {
129
+ /** The arbiter remote name (its LFS endpoint comes from `cwd`'s config and `.lfsconfig`). */
130
+ arbiter: string;
131
+ /** The sha256 oids to upload (each already in the local store). */
132
+ oids: readonly string[];
133
+ /** Working clone the push runs in. */
134
+ cwd: string;
135
+ /** Environment for the child git process. */
136
+ env: NodeJS.ProcessEnv | undefined;
137
+ }
138
+
139
+ /** How many oids one `git lfs push --object-id` is given. */
140
+ const LFS_PUSH_BATCH = 100;
141
+
142
+ /** Input of {@link RefWriteStrategy.createLockRef}. */
143
+ export interface LockRefCreateInput {
144
+ /** The arbiter remote name. */
145
+ arbiter: string;
146
+ /** The full lock ref (`refs/dorfl/lock/<entry>`). */
147
+ ref: string;
148
+ /** The prepared (parentless) lock commit to publish at {@link ref}. */
149
+ commit: string;
150
+ /** Working clone the push runs in. */
151
+ cwd: string;
152
+ /** Environment for the child git process. */
153
+ env: NodeJS.ProcessEnv | undefined;
154
+ }
155
+
156
+ /** Input of {@link RefWriteStrategy.deleteLockRef}. */
157
+ export interface LockRefDeleteInput {
158
+ /** The arbiter remote name. */
159
+ arbiter: string;
160
+ /** The full lock ref (`refs/dorfl/lock/<entry>`). */
161
+ ref: string;
162
+ /** The ref's sha the caller read; the delete is leased on it. */
163
+ expectedSha: string;
164
+ /** Working clone the push runs in. */
165
+ cwd: string;
166
+ /** Environment for the child git process. */
167
+ env: NodeJS.ProcessEnv | undefined;
168
+ }
169
+
170
+ /** Input of {@link RefWriteStrategy.replaceLockRef}. */
171
+ export interface LockRefReplaceInput {
172
+ /** The arbiter remote name. */
173
+ arbiter: string;
174
+ /** The full lock ref (`refs/dorfl/lock/<entry>`). */
175
+ ref: string;
176
+ /** The prepared (parentless) lock commit to publish at {@link ref}. */
177
+ commit: string;
178
+ /** The ref's sha the caller read; the replace is leased on it. */
179
+ expectedSha: string;
180
+ /** Working clone the push runs in. */
181
+ cwd: string;
182
+ /** Environment for the child git process. */
183
+ env: NodeJS.ProcessEnv | undefined;
184
+ }
185
+
186
+ /** Input of {@link RefWriteStrategy.pushContinuedBranch}. */
187
+ export type ContinuedBranchPushInput = Parameters<
188
+ typeof pushContinuedBranchWithStaleLeaseRetry
189
+ >[0];
190
+
191
+ /** Input of {@link RefWriteStrategy.pushLeasedWorkBranch}. */
192
+ export interface LeasedWorkBranchPushInput {
193
+ /** The arbiter remote name. */
194
+ arbiter: string;
195
+ /** The work branch (`work/task-<slug>`), unqualified. */
196
+ branch: string;
197
+ /** The commit to publish as the branch's tip. */
198
+ commit: string;
199
+ /** The branch tip the caller observed on the arbiter; the push is leased on it. */
200
+ expectedTip: string;
201
+ /** Working clone the push runs in. */
202
+ cwd: string;
203
+ /** Environment for the child git process. */
204
+ env: NodeJS.ProcessEnv | undefined;
205
+ }
206
+
207
+ /** Input of {@link RefWriteStrategy.pushTaskingCandidatesBranch}. */
208
+ export interface TaskingCandidatesPushInput {
209
+ /** The arbiter remote name. */
210
+ arbiter: string;
211
+ /** The candidates branch (`work/spec-<slug>`). */
212
+ branch: string;
213
+ /** Working clone the push runs in. */
214
+ cwd: string;
215
+ /** Environment for the child git process. */
216
+ env: NodeJS.ProcessEnv | undefined;
217
+ }
218
+
219
+ /**
220
+ * The ONLY ref-write strategy: current behaviour, the exact git argument lists
221
+ * and helpers the call sites used before they were routed through the seam.
222
+ */
223
+ export const currentRefWrite: RefWriteStrategy = {
224
+ createLockRef({arbiter, ref, commit, cwd, env}) {
225
+ return runAsync(
226
+ 'git',
227
+ ['push', arbiter, `${commit}:${ref}`, `--force-with-lease=${ref}:`],
228
+ cwd,
229
+ {env},
230
+ );
231
+ },
232
+
233
+ deleteLockRef({arbiter, ref, expectedSha, cwd, env}) {
234
+ return runAsync(
235
+ 'git',
236
+ [
237
+ 'push',
238
+ arbiter,
239
+ '--delete',
240
+ ref,
241
+ `--force-with-lease=${ref}:${expectedSha}`,
242
+ ],
243
+ cwd,
244
+ {env},
245
+ );
246
+ },
247
+
248
+ replaceLockRef({arbiter, ref, commit, expectedSha, cwd, env}) {
249
+ return runAsync(
250
+ 'git',
251
+ [
252
+ 'push',
253
+ arbiter,
254
+ `${commit}:${ref}`,
255
+ `--force-with-lease=${ref}:${expectedSha}`,
256
+ ],
257
+ cwd,
258
+ {env},
259
+ );
260
+ },
261
+
262
+ pushContinuedBranch(input) {
263
+ return pushContinuedBranchWithStaleLeaseRetry(input);
264
+ },
265
+
266
+ pushLeasedWorkBranch({arbiter, branch, commit, expectedTip, cwd, env}) {
267
+ const ref = `refs/heads/${branch}`;
268
+ return runAsync(
269
+ 'git',
270
+ [
271
+ 'push',
272
+ arbiter,
273
+ `${commit}:${ref}`,
274
+ `--force-with-lease=${ref}:${expectedTip}`,
275
+ ],
276
+ cwd,
277
+ {env},
278
+ );
279
+ },
280
+
281
+ saveWorkBranch(input) {
282
+ return routeToNeedsAttention(input);
283
+ },
284
+
285
+ deleteRemoteWorkBranch(input) {
286
+ return deleteRemoteWorkBranchIfPresent(input);
287
+ },
288
+
289
+ pushTaskingCandidatesBranch({arbiter, branch, cwd, env}) {
290
+ return runAsync('git', ['push', arbiter, branch], cwd, {env});
291
+ },
292
+
293
+ publishTreelessResult(input) {
294
+ return pushTreelessResult(input);
295
+ },
296
+
297
+ async pushLfsObjects({arbiter, oids, cwd, env}) {
298
+ let last: RunResult = {status: 0, stdout: '', stderr: ''};
299
+ for (let i = 0; i < oids.length; i += LFS_PUSH_BATCH) {
300
+ last = await runAsync(
301
+ 'git',
302
+ [
303
+ 'lfs',
304
+ 'push',
305
+ '--object-id',
306
+ arbiter,
307
+ ...oids.slice(i, i + LFS_PUSH_BATCH),
308
+ ],
309
+ cwd,
310
+ {env},
311
+ );
312
+ if (last.status !== 0) return last;
313
+ }
314
+ return last;
315
+ },
316
+ };
317
+
318
+ /**
319
+ * The active ref-write strategy. There is exactly one (current behaviour); this
320
+ * indirection is the seam's single insertion point, NOT a selectable mode. Call
321
+ * sites go through `refWrite.<method>(...)` so a test (and later the CI phase
322
+ * mode) can replace a method on this object.
323
+ */
324
+ export const refWrite: RefWriteStrategy = {...currentRefWrite};
@@ -176,16 +176,15 @@ export const REPO_ALLOWED_KEYS = [
176
176
  // same chain as `integration`/`autoBuild`. It REPLACES the old `autoTriage`
177
177
  // boolean (no alias; no external users owed a migration window, 2026-06-12).
178
178
  'observationTriage',
179
- // `mergeQuestions` (the 3-state `off|ask|auto` gate over the MERGE-QUESTION
179
+ // `mergeQuestions` (the 2-state `off|ask` gate over the MERGE-QUESTION
180
180
  // SURFACER — spec `land-time-reverify-and-parallel-merge-ceiling` Story 17 /
181
181
  // task `merge-questions-gate-axis`) is a genuine repo property exactly like
182
182
  // `observationTriage`: whether THIS repo's unmerged `work/*` branches surface
183
- // as merge-questions (and whether they auto-land) is agreed by all
184
- // collaborators + travels with the repo. SEPARATE axis from
185
- // `observationTriage` (never rides it) with a HIGHER default (`ask`, never
186
- // `off` — a dropped merge-question means pushed work never lands). Resolved
187
- // per-repo through the SAME precedence chain as the rest of the gate family
188
- // (flag > env > per-repo > global > default `ask`).
183
+ // as merge-questions is agreed by all collaborators + travels with the repo.
184
+ // SEPARATE axis from `observationTriage` (never rides it) with a HIGHER
185
+ // default (`ask`, never `off`: a dropped merge-question means pushed work
186
+ // never lands). Resolved per-repo through the SAME precedence chain as the
187
+ // rest of the gate family (env > per-repo > global > default `ask`).
189
188
  'mergeQuestions',
190
189
  // `surfaceBlockers` (the BOOLEAN gate over DECLARED blocked work — whether a
191
190
  // task/spec carrying `needsAnswers:true` is rendered into a question sidecar) is a
@@ -256,7 +255,8 @@ export const REPO_ALLOWED_KEYS = [
256
255
  // is agreed by all collaborators in a wide-matrix CI repo + travels with the
257
256
  // repo. Resolved per-repo through the SAME chain as `freshWorktreeGate`
258
257
  // (flag > env > per-repo > global > default). Safety is unchanged — a lost
259
- // CAS still costs only a re-rebase + re-gate retry, never a `--force`.
258
+ // CAS still costs only a re-rebase and a retried push (no re-gate), never a
259
+ // `--force`.
260
260
  'mergeRetries',
261
261
  // `strictMergeApproval` (the OPT-IN strictness layered on the OQ6
262
262
  // stale-approval default — spec `land-time-reverify-and-parallel-merge-ceiling`