@maycombhq/atticus 2.0.0-beta.2 → 2.0.0-beta.22

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 (284) hide show
  1. package/dist/bin.js +8 -4
  2. package/dist/chunk-23XVSEV5.js +49 -0
  3. package/dist/{chunk-ZDPAS56H.js → chunk-24OXGB3P.js} +8 -2
  4. package/dist/{chunk-7EOBHUMC.js → chunk-24XLDU72.js} +4 -3
  5. package/dist/{chunk-CR3MTH32.js → chunk-2F4CYZKJ.js} +86 -11
  6. package/dist/{chunk-6IU67AEK.js → chunk-2GF7OLF3.js} +23 -15
  7. package/dist/chunk-2IIMFOPM.js +41 -0
  8. package/dist/chunk-2LGRMVCA.js +617 -0
  9. package/dist/{chunk-XPCM366L.js → chunk-2MMCFP5C.js} +10 -8
  10. package/dist/{chunk-JBTK5OP2.js → chunk-2Q56PQPS.js} +6 -4
  11. package/dist/{chunk-Z6LF7HQN.js → chunk-2Q5EDGEO.js} +7 -5
  12. package/dist/{chunk-3USYZGRF.js → chunk-2RLKFUUL.js} +38 -27
  13. package/dist/chunk-2SKB4O2E.js +59 -0
  14. package/dist/{chunk-SP2I63OO.js → chunk-34S2WGOB.js} +87 -4
  15. package/dist/{chunk-GQ4N4WKF.js → chunk-3NFFJL5D.js} +33 -27
  16. package/dist/{chunk-SJRGMG6A.js → chunk-3NWDLGG6.js} +28 -25
  17. package/dist/{chunk-4WBVUHXN.js → chunk-3RM4UK6W.js} +20 -18
  18. package/dist/chunk-42SIDKEU.js +583 -0
  19. package/dist/{chunk-GLUX4EEP.js → chunk-4EURQWQD.js} +20 -15
  20. package/dist/chunk-4U276ICE.js +39 -0
  21. package/dist/{chunk-ZMFPW66A.js → chunk-53T4O55J.js} +4 -3
  22. package/dist/{chunk-6J5OKA4Y.js → chunk-57TDNMI3.js} +17 -15
  23. package/dist/chunk-5BD2KYLE.js +57 -0
  24. package/dist/{chunk-7EFILTCI.js → chunk-5F7GFN2F.js} +28 -0
  25. package/dist/chunk-5NMKONLP.js +95 -0
  26. package/dist/{chunk-7ZUIMTOU.js → chunk-5SJZW52K.js} +26 -24
  27. package/dist/{chunk-4RS5KZQD.js → chunk-63YRMR6J.js} +6 -5
  28. package/dist/chunk-66PPNOON.js +62 -0
  29. package/dist/chunk-6H3Z5JHH.js +64 -0
  30. package/dist/{chunk-CEXSU4S6.js → chunk-6MUGR4LS.js} +775 -89
  31. package/dist/{chunk-QSXK2RGH.js → chunk-6OJQOPUN.js} +5 -3
  32. package/dist/{chunk-MVZVGYXH.js → chunk-6PPOWY5E.js} +84 -14
  33. package/dist/{chunk-7EKXJZ2Y.js → chunk-6ZGLAACW.js} +299 -38
  34. package/dist/chunk-6ZJ5A7AF.js +222 -0
  35. package/dist/{chunk-L24C55VN.js → chunk-745DAZ4L.js} +142 -30
  36. package/dist/{chunk-Q4D5X7QB.js → chunk-7ASNL4Q4.js} +46 -24
  37. package/dist/chunk-7BR7IPOH.js +35 -0
  38. package/dist/{chunk-7WD63HU2.js → chunk-7DOR27HL.js} +6 -6
  39. package/dist/chunk-7FTTA3HD.js +341 -0
  40. package/dist/{chunk-OLQJTJJU.js → chunk-7GJS6DNX.js} +12 -3
  41. package/dist/{chunk-7JUKDRSZ.js → chunk-7IGKHZ4Z.js} +347 -226
  42. package/dist/{chunk-G2B4ZQGH.js → chunk-7IOWA5D5.js} +11 -6
  43. package/dist/{chunk-EBEW3XXQ.js → chunk-7JITZFJC.js} +7 -5
  44. package/dist/chunk-7MJSQTL5.js +174 -0
  45. package/dist/{chunk-I4JRCD6M.js → chunk-7UOMVMFG.js} +25 -17
  46. package/dist/{chunk-YY6ZQQBP.js → chunk-AFWUDXEM.js} +19 -7
  47. package/dist/chunk-AFZ3QA6W.js +47 -0
  48. package/dist/chunk-AJQ72EBM.js +420 -0
  49. package/dist/chunk-AJYXNTNZ.js +317 -0
  50. package/dist/chunk-AVTBS4OW.js +49 -0
  51. package/dist/{chunk-FHH4I4ZV.js → chunk-BDKC6H5P.js} +12 -6
  52. package/dist/{chunk-IPKHE5D6.js → chunk-BEVTIBRH.js} +1526 -875
  53. package/dist/chunk-BHT7QK4G.js +162 -0
  54. package/dist/{chunk-6JUBZBMW.js → chunk-BIGDQDHD.js} +6 -4
  55. package/dist/{chunk-2BFCZ3RK.js → chunk-BO4KX37X.js} +20 -18
  56. package/dist/chunk-BPK5P77O.js +115 -0
  57. package/dist/chunk-BQ4SQQC6.js +59 -0
  58. package/dist/chunk-BQGDB6ZL.js +7342 -0
  59. package/dist/{chunk-2ANOROX3.js → chunk-BTGPE6CR.js} +11 -9
  60. package/dist/chunk-BTY3HZBO.js +93 -0
  61. package/dist/{chunk-DPZHZQKE.js → chunk-BXYMYEJ4.js} +44 -10
  62. package/dist/{chunk-2RMYG36Q.js → chunk-C3RY6KU6.js} +309 -346
  63. package/dist/chunk-C4R4JLHE.js +40 -0
  64. package/dist/chunk-CADWLE2T.js +18 -0
  65. package/dist/chunk-CBGGOQWU.js +82 -0
  66. package/dist/chunk-CMDXGQTS.js +47 -0
  67. package/dist/{chunk-MC3H7736.js → chunk-CQDGOPGH.js} +58 -18
  68. package/dist/{chunk-HFQCFSJR.js → chunk-CX4RZ2KS.js} +4 -3
  69. package/dist/{chunk-PAMPPUR6.js → chunk-CXJ6FUKN.js} +1852 -207
  70. package/dist/{chunk-ABWHYWNI.js → chunk-D73UEXYW.js} +3 -3
  71. package/dist/chunk-DCZWQLYT.js +1205 -0
  72. package/dist/chunk-DLOBCJL6.js +48 -0
  73. package/dist/{chunk-4BN53TG3.js → chunk-DOBQKHIF.js} +31 -23
  74. package/dist/chunk-E3ISYXO3.js +423 -0
  75. package/dist/{chunk-7SW2QWLU.js → chunk-E6ZIEVOI.js} +16 -13
  76. package/dist/chunk-EBL2R3YG.js +43 -0
  77. package/dist/{chunk-AIOQPLA5.js → chunk-EGQRREQH.js} +41 -15
  78. package/dist/chunk-EHA7RMZ3.js +58 -0
  79. package/dist/{chunk-M766ZJ6W.js → chunk-EIPTLPFC.js} +38 -27
  80. package/dist/chunk-EMRIQUND.js +517 -0
  81. package/dist/{chunk-DFBCSHVT.js → chunk-EPCJQQWD.js} +6 -4
  82. package/dist/{chunk-RZPYKDWS.js → chunk-F3P7N7SF.js} +1 -1
  83. package/dist/{chunk-R77KLRB3.js → chunk-FFLNS6VP.js} +21 -18
  84. package/dist/chunk-FFTRTL6R.js +26 -0
  85. package/dist/{chunk-IHMLFRVV.js → chunk-FPJPLKDW.js} +62 -16
  86. package/dist/{chunk-JCZIKA4C.js → chunk-FSMSFDLD.js} +5 -4
  87. package/dist/{chunk-AVJDSJ2F.js → chunk-FUJM6RTF.js} +8 -7
  88. package/dist/chunk-FZOJFH7Q.js +132 -0
  89. package/dist/{chunk-QRAOANID.js → chunk-G6X77UJZ.js} +23 -21
  90. package/dist/{chunk-3ALD2W2A.js → chunk-GCWRWSU7.js} +10 -4
  91. package/dist/chunk-GIW4ZVDB.js +39 -0
  92. package/dist/{chunk-B2WLAWNJ.js → chunk-GUX3PTYM.js} +3 -3
  93. package/dist/{chunk-DPUYDDQG.js → chunk-GZY6RUZ6.js} +25 -3
  94. package/dist/chunk-H4VO6WG3.js +10 -0
  95. package/dist/{chunk-MMOSZPWJ.js → chunk-H5HBGNXU.js} +5 -4
  96. package/dist/chunk-H7KM7LZE.js +229 -0
  97. package/dist/{chunk-AA6YULYB.js → chunk-HNM2YQT2.js} +7 -7
  98. package/dist/{chunk-3C7EYRFZ.js → chunk-HYU5VLGA.js} +1 -1
  99. package/dist/{chunk-XY3P7JW7.js → chunk-IDKCWSM5.js} +1032 -172
  100. package/dist/chunk-IFKEI4SA.js +237 -0
  101. package/dist/{chunk-F7HYOUFF.js → chunk-IGOE6KPG.js} +67 -22
  102. package/dist/{chunk-TDD55CJH.js → chunk-IH2XURVQ.js} +6 -5
  103. package/dist/chunk-INQHESFB.js +38 -0
  104. package/dist/chunk-IP74LCOQ.js +57 -0
  105. package/dist/chunk-IPHHDDC6.js +45 -0
  106. package/dist/{chunk-T6AIP7H5.js → chunk-ISUAAMGS.js} +110 -153
  107. package/dist/{chunk-A7UPUQFY.js → chunk-IUPG3UKI.js} +624 -319
  108. package/dist/{chunk-L6SGQIR3.js → chunk-IVBOQAGB.js} +7 -3
  109. package/dist/{chunk-27NKUS3X.js → chunk-J2JAKCIW.js} +2205 -796
  110. package/dist/{chunk-V2YTAIQO.js → chunk-J3IOLXWM.js} +24 -7
  111. package/dist/chunk-JA5FI3QV.js +142 -0
  112. package/dist/chunk-JAVI7JSP.js +49 -0
  113. package/dist/{chunk-HLJAXMM4.js → chunk-JB5LINEM.js} +6 -2
  114. package/dist/{chunk-2VHF7OXM.js → chunk-JENSEMXF.js} +1 -1
  115. package/dist/{chunk-6BVONWJ7.js → chunk-JJF6HALY.js} +4 -3
  116. package/dist/chunk-JLUCKAEL.js +38 -0
  117. package/dist/chunk-JRBFANFE.js +221 -0
  118. package/dist/{chunk-R6XF6UR3.js → chunk-KDNTAPLX.js} +4 -4
  119. package/dist/chunk-KFAVQZSX.js +33 -0
  120. package/dist/chunk-KJXD4PNL.js +50 -0
  121. package/dist/chunk-KN5BXMWN.js +44 -0
  122. package/dist/{chunk-PCVDV3UZ.js → chunk-KQGURXFU.js} +9 -7
  123. package/dist/{chunk-WY6PP6AA.js → chunk-KXITGYLF.js} +40 -6
  124. package/dist/{chunk-66YVAGGM.js → chunk-L27SV425.js} +22 -20
  125. package/dist/{chunk-NE2W3OO7.js → chunk-LDLL7LB3.js} +24 -22
  126. package/dist/chunk-LFGTYX5U.js +379 -0
  127. package/dist/{chunk-EYWIDJFU.js → chunk-LGBMTVNT.js} +7 -5
  128. package/dist/{chunk-Q5AMFEWD.js → chunk-LGFL54FP.js} +3 -2
  129. package/dist/{chunk-6TACQN3N.js → chunk-LJXMYKZJ.js} +7 -5
  130. package/dist/{chunk-BKVWTZ6A.js → chunk-LKOR5UET.js} +29 -26
  131. package/dist/chunk-LNBQOP7D.js +294 -0
  132. package/dist/{chunk-O3WI5KWW.js → chunk-LO6P2VYG.js} +2 -2
  133. package/dist/chunk-LQJPN536.js +122 -0
  134. package/dist/{chunk-YEDDOYSC.js → chunk-LYBNHMVZ.js} +29 -5
  135. package/dist/{chunk-A3GY6SOA.js → chunk-LZ7NUC62.js} +20 -18
  136. package/dist/chunk-M32BLWDH.js +37 -0
  137. package/dist/{chunk-VYDBJ73M.js → chunk-M3NANSDZ.js} +17 -15
  138. package/dist/chunk-MFHL5URL.js +31 -0
  139. package/dist/chunk-MFR5OPBD.js +109 -0
  140. package/dist/{chunk-DE76JROX.js → chunk-MG4ZL5RW.js} +32 -20
  141. package/dist/{chunk-N7IXEHK3.js → chunk-MO6P2JZH.js} +10 -8
  142. package/dist/{chunk-65NYPGRC.js → chunk-MUWDB4R4.js} +1 -1
  143. package/dist/chunk-MZCGSXVR.js +34 -0
  144. package/dist/{chunk-MGYZM27B.js → chunk-N3XIYPU3.js} +51 -23
  145. package/dist/chunk-NDCF4R6S.js +172 -0
  146. package/dist/{chunk-DHPACDG7.js → chunk-NR2UJWOC.js} +10 -8
  147. package/dist/chunk-NXJ5URP6.js +51 -0
  148. package/dist/{chunk-UNM6ZOKP.js → chunk-NY2XSR5K.js} +147 -18
  149. package/dist/{chunk-5GDRCDQB.js → chunk-O45HFTFS.js} +3 -2
  150. package/dist/{chunk-ONLS5QA5.js → chunk-O4GXGGBX.js} +14 -12
  151. package/dist/chunk-O5PXQR67.js +249 -0
  152. package/dist/{chunk-PRPPGYZG.js → chunk-O66DM6DJ.js} +6 -4
  153. package/dist/{chunk-KEM4SDDP.js → chunk-O7FDLW7L.js} +11 -8
  154. package/dist/{chunk-T2UYZSCQ.js → chunk-O7XLYEFA.js} +169 -29
  155. package/dist/{chunk-FITWGOJ2.js → chunk-OHNCWBKF.js} +4 -4
  156. package/dist/{chunk-652QWTLZ.js → chunk-OHVTTOPS.js} +3 -3
  157. package/dist/chunk-OMI6FPPN.js +43 -0
  158. package/dist/chunk-ONUPY3H6.js +53 -0
  159. package/dist/chunk-OTLDSZDV.js +69 -0
  160. package/dist/chunk-OUBGGKDU.js +22 -0
  161. package/dist/chunk-OWNCM4ZN.js +330 -0
  162. package/dist/{chunk-YISXLPHN.js → chunk-P2V7QW4D.js} +235 -35
  163. package/dist/{chunk-CNUKCS2T.js → chunk-P32RUW3B.js} +1632 -93
  164. package/dist/{chunk-BWZ77RQT.js → chunk-PAQ2AS5E.js} +1 -1
  165. package/dist/{chunk-ZMAINWBW.js → chunk-PBPBMP2B.js} +4 -3
  166. package/dist/{chunk-ZVM6AKKM.js → chunk-PJSJPMJI.js} +3 -3
  167. package/dist/{chunk-EFHQ3TWP.js → chunk-PKBHS4AA.js} +233 -56
  168. package/dist/{chunk-GT4I4ZWM.js → chunk-PMUAJGLI.js} +20 -16
  169. package/dist/chunk-PPD7HTDP.js +152 -0
  170. package/dist/chunk-PSOFBQWB.js +42 -0
  171. package/dist/{chunk-S2A7YC3Q.js → chunk-Q2TECDIT.js} +10694 -8896
  172. package/dist/chunk-Q4K5WB2W.js +42 -0
  173. package/dist/{chunk-UGLN7QHV.js → chunk-Q6DFNVRJ.js} +14 -12
  174. package/dist/chunk-QA4O2GGC.js +265 -0
  175. package/dist/chunk-QAXW2N3B.js +1216 -0
  176. package/dist/chunk-QBNWF5ZU.js +19 -0
  177. package/dist/chunk-QFWA2DPH.js +224 -0
  178. package/dist/{chunk-23W7VWKR.js → chunk-QKUJMING.js} +1363 -114
  179. package/dist/{chunk-NXFPS2LR.js → chunk-R44CYFPV.js} +16 -13
  180. package/dist/{chunk-CKSITVS4.js → chunk-R7NYHMUU.js} +2687 -786
  181. package/dist/chunk-REWWDVQB.js +162 -0
  182. package/dist/{chunk-JMSCMUCA.js → chunk-RGERPOIV.js} +145 -21
  183. package/dist/{chunk-6JCPI55B.js → chunk-RLDGWLQ7.js} +49 -7
  184. package/dist/chunk-RUGXBPWI.js +384 -0
  185. package/dist/{chunk-CPA6HPVR.js → chunk-RXPJMEOW.js} +18 -12
  186. package/dist/{chunk-3HI6Z55Y.js → chunk-S4PMJU6H.js} +339 -43
  187. package/dist/chunk-S5LSDXV4.js +40 -0
  188. package/dist/chunk-S7FCV7S5.js +51 -0
  189. package/dist/{chunk-5GGOLAKC.js → chunk-SDKLKB6I.js} +28 -26
  190. package/dist/chunk-SFSPGFCV.js +62 -0
  191. package/dist/{chunk-ZNCEKR4J.js → chunk-SFUP7TTO.js} +12 -8
  192. package/dist/{chunk-64EQYWCA.js → chunk-SLXZOMA6.js} +69 -19
  193. package/dist/chunk-SQV37XO2.js +273 -0
  194. package/dist/{chunk-K65BJIMT.js → chunk-SU74NDMQ.js} +19 -17
  195. package/dist/chunk-SYV5LHML.js +165 -0
  196. package/dist/chunk-T4YPZIQN.js +36 -0
  197. package/dist/chunk-T6DQR5TV.js +323 -0
  198. package/dist/{chunk-JIITQXFB.js → chunk-THDMSZ3R.js} +3075 -802
  199. package/dist/{chunk-JGZCG5VM.js → chunk-TNR75OXT.js} +7 -7
  200. package/dist/{chunk-D3PO4DOZ.js → chunk-TUULXQRW.js} +174 -15
  201. package/dist/chunk-TYQBFTBR.js +516 -0
  202. package/dist/chunk-U76OWS3Q.js +49 -0
  203. package/dist/{chunk-YYQN46FY.js → chunk-UCROWG2D.js} +27 -24
  204. package/dist/{chunk-6M5NK3K3.js → chunk-UF4AZOZB.js} +50 -10
  205. package/dist/chunk-UF4GLFBX.js +345 -0
  206. package/dist/{chunk-OT2O76OS.js → chunk-UGUP3ZM6.js} +240 -39
  207. package/dist/chunk-UNAGJYET.js +45 -0
  208. package/dist/{chunk-5WG5YP4T.js → chunk-UOR2DTHH.js} +16 -14
  209. package/dist/{chunk-S3QNRWYR.js → chunk-UPOY4YSQ.js} +14 -12
  210. package/dist/{chunk-EPTBZ3GP.js → chunk-UPS2DGFC.js} +1 -1
  211. package/dist/{chunk-4AK7IBVP.js → chunk-UUU4CLJG.js} +4 -2
  212. package/dist/{chunk-W3BQAZ3Q.js → chunk-UWMJKX5U.js} +14 -12
  213. package/dist/{chunk-QZSX3F7C.js → chunk-UWOJLHFT.js} +27 -23
  214. package/dist/chunk-VBTP56CE.js +535 -0
  215. package/dist/chunk-VCTWPLAO.js +211 -0
  216. package/dist/chunk-VNU26RCO.js +1215 -0
  217. package/dist/{chunk-CSZ4C5N6.js → chunk-VTZMAJ3W.js} +93 -10
  218. package/dist/{chunk-TXLH6WHQ.js → chunk-VW62D6QE.js} +14 -12
  219. package/dist/{chunk-CAQZUNOH.js → chunk-VY72R2XZ.js} +610 -3
  220. package/dist/{chunk-2DIQQJCB.js → chunk-VZALIKIF.js} +18 -15
  221. package/dist/{chunk-TYAID7B7.js → chunk-W62H2XSO.js} +25 -23
  222. package/dist/{chunk-HVUPKCGJ.js → chunk-W7VWI2YH.js} +9 -6
  223. package/dist/{chunk-TANFV3MZ.js → chunk-WERM6KPJ.js} +7 -4
  224. package/dist/{chunk-BOHJZTEQ.js → chunk-WF7B67YE.js} +208 -87
  225. package/dist/{chunk-7FBGJFEE.js → chunk-WG5U3F5V.js} +14 -12
  226. package/dist/{chunk-4NDAAFQT.js → chunk-WJF2CS6M.js} +8 -5
  227. package/dist/{chunk-XAKWMCXI.js → chunk-WKUJ3KPV.js} +4 -4
  228. package/dist/chunk-WMTRHUI5.js +103 -0
  229. package/dist/chunk-WNKPRSGI.js +36 -0
  230. package/dist/chunk-WXFLMQSR.js +223 -0
  231. package/dist/{chunk-RRTKKSLQ.js → chunk-X3WY6MN3.js} +8 -6
  232. package/dist/{chunk-ZCJDOK2S.js → chunk-X4CXA2IL.js} +47 -23
  233. package/dist/{chunk-UBBC2XSG.js → chunk-X5F3Q5LV.js} +560 -105
  234. package/dist/{chunk-C6EKFH7J.js → chunk-XD75VZIH.js} +9 -7
  235. package/dist/chunk-XERYFDB5.js +297 -0
  236. package/dist/chunk-XI2LCCYQ.js +420 -0
  237. package/dist/chunk-Y2EQNWKV.js +933 -0
  238. package/dist/{chunk-LR3R6Z2A.js → chunk-YHCMZC2J.js} +7 -7
  239. package/dist/chunk-YYY3AZFE.js +122 -0
  240. package/dist/{chunk-GDLULHTC.js → chunk-Z5IA3WDR.js} +19 -31
  241. package/dist/{chunk-T6XQL73F.js → chunk-ZFFYNRIO.js} +63 -23
  242. package/dist/{chunk-EDS2L2LD.js → chunk-ZFL7NY4F.js} +56 -27
  243. package/dist/{chunk-YMZ3TNYB.js → chunk-ZGULQAPZ.js} +7 -4
  244. package/dist/{chunk-4SKZDI62.js → chunk-ZIUFM5HT.js} +3 -1
  245. package/dist/{chunk-DKJA76CJ.js → chunk-ZJFRMSJT.js} +15 -13
  246. package/dist/chunk-ZJXLNDH4.js +239 -0
  247. package/dist/chunk-ZQ3ON2VQ.js +252 -0
  248. package/dist/{chunk-HITVBIJS.js → chunk-ZQ7SKESZ.js} +150 -415
  249. package/dist/{chunk-6QSERWXH.js → chunk-ZQXZ2SES.js} +24 -21
  250. package/dist/chunk-ZX57KSEQ.js +39 -0
  251. package/dist/{chunk-I3E7CX4H.js → chunk-ZZK56BLP.js} +5 -3
  252. package/dist/files.json +664 -368
  253. package/dist/packages/naysayer/constitution/constitution.yaml +19 -0
  254. package/dist/packages/sdk/npm/index.js +6194 -2542
  255. package/dist/packages/ui/dist/assets/{elk-api-tma0MCuh.js → elk-api-CIf-0gz5.js} +2 -2
  256. package/dist/packages/ui/dist/assets/index-CoK_Dl0D.js +47 -0
  257. package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +1 -0
  258. package/dist/packages/ui/dist/index.html +3 -2
  259. package/package.json +5 -5
  260. package/dist/chunk-2BF2EYJV.js +0 -174
  261. package/dist/chunk-42EVSKME.js +0 -37
  262. package/dist/chunk-4BKGMUIK.js +0 -53
  263. package/dist/chunk-5KLGBXG5.js +0 -48
  264. package/dist/chunk-6BNC6EQ4.js +0 -41
  265. package/dist/chunk-6D2CQ5II.js +0 -41
  266. package/dist/chunk-7AVHLZGD.js +0 -45
  267. package/dist/chunk-D46WUV3T.js +0 -33
  268. package/dist/chunk-E36QOJMD.js +0 -47
  269. package/dist/chunk-FQXYPF7E.js +0 -147
  270. package/dist/chunk-GBRFVT63.js +0 -45
  271. package/dist/chunk-ICY36XCH.js +0 -34
  272. package/dist/chunk-J4K3KVLD.js +0 -131
  273. package/dist/chunk-JZU3H6UQ.js +0 -54
  274. package/dist/chunk-NN2ZQEB4.js +0 -38
  275. package/dist/chunk-PCOCACY3.js +0 -385
  276. package/dist/chunk-RTEYOSJY.js +0 -29
  277. package/dist/chunk-RUVFXE2L.js +0 -666
  278. package/dist/chunk-RVH5SUW5.js +0 -69
  279. package/dist/chunk-VN3DP4LQ.js +0 -43
  280. package/dist/chunk-W5BVQF44.js +0 -36
  281. package/dist/chunk-WVPJSRRJ.js +0 -52
  282. package/dist/chunk-XPTOBS2D.js +0 -48
  283. package/dist/packages/ui/dist/assets/index-B54fm9FQ.js +0 -29
  284. package/dist/packages/ui/dist/assets/index-DD_hufQz.css +0 -1
@@ -6,55 +6,60 @@ var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
6
6
  import {
7
7
  slug,
8
8
  writeGateRound
9
- } from "./chunk-2ANOROX3.js";
9
+ } from "./chunk-BTGPE6CR.js";
10
10
  import {
11
+ catchUpChangeBranch,
11
12
  changeWorktree,
12
13
  handDown,
13
14
  loadTask,
14
15
  openTaskWorktree,
15
- readChangeTasks
16
- } from "./chunk-OT2O76OS.js";
17
- import {
18
- buildEngineFor
19
- } from "./chunk-IPKHE5D6.js";
16
+ readChangeTasks,
17
+ setTaskDone,
18
+ taskBranchOwnCommits
19
+ } from "./chunk-UGUP3ZM6.js";
20
20
  import {
21
21
  conform,
22
22
  readConformDeltas,
23
23
  readTaskSteps,
24
24
  renderTaskContext,
25
- taskContext
26
- } from "./chunk-CKSITVS4.js";
25
+ taskContext,
26
+ withBuildLine
27
+ } from "./chunk-R7NYHMUU.js";
28
+ import {
29
+ buildEngineFor
30
+ } from "./chunk-BEVTIBRH.js";
27
31
  import {
28
32
  readChangeFiles,
29
33
  readRound,
34
+ readStoryIndex,
30
35
  roundsOf,
31
36
  writeQuarantineItems
32
- } from "./chunk-3HI6Z55Y.js";
37
+ } from "./chunk-S4PMJU6H.js";
33
38
  import {
34
39
  changeFolder,
35
40
  changeStore
36
- } from "./chunk-XY3P7JW7.js";
41
+ } from "./chunk-IDKCWSM5.js";
37
42
  import {
38
43
  AdapterError,
39
44
  createAdapters,
40
45
  fileStepStore,
41
46
  requireApproval
42
- } from "./chunk-7JUKDRSZ.js";
47
+ } from "./chunk-7IGKHZ4Z.js";
43
48
  import {
44
49
  scanEstate
45
- } from "./chunk-27NKUS3X.js";
50
+ } from "./chunk-J2JAKCIW.js";
46
51
  import {
47
52
  baseClonePath,
48
53
  createGit,
49
54
  toRepoRelative
50
- } from "./chunk-EFHQ3TWP.js";
55
+ } from "./chunk-PKBHS4AA.js";
51
56
  import {
52
57
  HandoffRuntime,
53
58
  buildContextPack,
54
59
  createRuntime,
55
60
  quarantineItems,
56
61
  toRunSummary
57
- } from "./chunk-CNUKCS2T.js";
62
+ } from "./chunk-P32RUW3B.js";
58
63
  import {
59
64
  AgentDefinition,
60
65
  ChecklistPack,
@@ -68,16 +73,20 @@ import {
68
73
  changesRelOf,
69
74
  external_exports,
70
75
  readRepoSpec,
71
- require_dist,
72
76
  runsDirOf,
73
77
  runsRelOf,
74
78
  stateDirOf,
75
79
  taskBranch,
76
80
  toCanonicalYaml
77
- } from "./chunk-S2A7YC3Q.js";
81
+ } from "./chunk-Q2TECDIT.js";
78
82
  import {
83
+ LockHeldError,
84
+ acquireLock,
79
85
  writeFileAtomic
80
- } from "./chunk-D3PO4DOZ.js";
86
+ } from "./chunk-TUULXQRW.js";
87
+ import {
88
+ require_dist
89
+ } from "./chunk-BQGDB6ZL.js";
81
90
  import {
82
91
  __toESM
83
92
  } from "./chunk-TV45EUGM.js";
@@ -156,27 +165,162 @@ async function applyChanges(worktree, changes) {
156
165
 
157
166
  // packages/taskrun/dist/commands.js
158
167
  import { spawn } from "node:child_process";
168
+ var KEEP_OUTPUT = 4 * 1024 * 1024;
169
+ function tail(max = KEEP_OUTPUT) {
170
+ let text = "";
171
+ let dropped = 0;
172
+ return {
173
+ add(d) {
174
+ text += d;
175
+ if (text.length > max) {
176
+ dropped += text.length - max;
177
+ text = text.slice(text.length - max);
178
+ }
179
+ },
180
+ get text() {
181
+ return dropped ? `[${dropped} characters of output left out]
182
+ ${text}` : text;
183
+ }
184
+ };
185
+ }
186
+ function stopTree(child, signal = "SIGTERM") {
187
+ if (child.pid === void 0)
188
+ return;
189
+ if (process.platform === "win32") {
190
+ const k = spawn("taskkill", ["/pid", String(child.pid), "/T", "/F"], { windowsHide: true });
191
+ k.on("error", () => child.kill());
192
+ return;
193
+ }
194
+ try {
195
+ process.kill(-child.pid, signal);
196
+ } catch {
197
+ child.kill(signal);
198
+ }
199
+ }
200
+ var live = /* @__PURE__ */ new Set();
201
+ var SIGNALS = ["SIGINT", "SIGTERM", "SIGHUP"];
202
+ var stopping = false;
203
+ var stopRequested = () => stopping;
204
+ var stopper = new AbortController();
205
+ var stopSignal = stopper.signal;
206
+ function stopAllBuilds() {
207
+ stopping = true;
208
+ for (const c of live)
209
+ stopTree(c);
210
+ if (!stopper.signal.aborted)
211
+ stopper.abort();
212
+ }
213
+ var holds = 0;
214
+ var pending;
215
+ var SIGNAL_GRACE_MS = 1e4;
216
+ var onSignal = (sig) => {
217
+ stopAllBuilds();
218
+ if (process.listenerCount(sig) > 1)
219
+ return;
220
+ if (!pending) {
221
+ pending = sig;
222
+ setTimeout(raise, SIGNAL_GRACE_MS);
223
+ }
224
+ settle();
225
+ };
226
+ function raise() {
227
+ const sig = pending;
228
+ if (!sig)
229
+ return;
230
+ unhook();
231
+ process.kill(process.pid, sig);
232
+ }
233
+ function settle() {
234
+ if (holds > 0 || live.size > 0)
235
+ return;
236
+ if (pending)
237
+ raise();
238
+ else
239
+ unhook();
240
+ }
241
+ var onExit = () => {
242
+ for (const c of live)
243
+ stopTree(c, "SIGKILL");
244
+ };
245
+ var hooked = false;
246
+ function hook() {
247
+ if (hooked)
248
+ return;
249
+ hooked = true;
250
+ for (const s of SIGNALS)
251
+ process.prependListener(s, onSignal);
252
+ process.on("exit", onExit);
253
+ }
254
+ function unhook() {
255
+ if (!hooked)
256
+ return;
257
+ hooked = false;
258
+ for (const s of SIGNALS)
259
+ process.off(s, onSignal);
260
+ process.off("exit", onExit);
261
+ }
262
+ function holdSignals() {
263
+ holds++;
264
+ hook();
265
+ let released = false;
266
+ return () => {
267
+ if (released)
268
+ return;
269
+ released = true;
270
+ holds--;
271
+ settle();
272
+ };
273
+ }
274
+ var track = (c) => {
275
+ live.add(c);
276
+ hook();
277
+ };
278
+ var untrack = (c) => {
279
+ live.delete(c);
280
+ settle();
281
+ };
282
+ var STOP_GRACE_MS = 5e3;
159
283
  function shellCommandRunner() {
160
284
  return (command, cwd, o = {}) => new Promise((resolve) => {
285
+ if (stopping) {
286
+ resolve({ exitCode: null, stdout: "", stderr: "Atticus is stopping.", stopped: true });
287
+ return;
288
+ }
161
289
  const child = spawn(command, {
162
290
  cwd,
163
291
  shell: true,
164
292
  env: { ...process.env, ...o.env },
165
- windowsHide: true
293
+ windowsHide: true,
294
+ // Its own process group off Windows, so a timeout stops the build, not only the shell.
295
+ detached: process.platform !== "win32"
166
296
  });
167
- let stdout = "";
168
- let stderr = "";
169
- const timer = setTimeout(() => child.kill(), o.timeoutMs ?? 30 * 6e4);
170
- child.stdout.setEncoding("utf8").on("data", (d) => stdout += d);
171
- child.stderr.setEncoding("utf8").on("data", (d) => stderr += d);
172
- child.on("error", (e) => {
297
+ track(child);
298
+ const out = tail();
299
+ const err = tail();
300
+ let done2 = false;
301
+ let kill;
302
+ let drain;
303
+ const finish2 = (r) => {
304
+ if (done2)
305
+ return;
306
+ done2 = true;
173
307
  clearTimeout(timer);
174
- resolve({ exitCode: null, stdout, stderr: stderr + e.message });
175
- });
176
- child.on("close", (code) => {
177
- clearTimeout(timer);
178
- resolve({ exitCode: code, stdout, stderr });
308
+ clearTimeout(kill);
309
+ clearTimeout(drain);
310
+ untrack(child);
311
+ resolve(stopping ? { ...r, stopped: true } : r);
312
+ };
313
+ const timer = setTimeout(() => {
314
+ stopTree(child);
315
+ kill = setTimeout(() => stopTree(child, "SIGKILL"), STOP_GRACE_MS);
316
+ }, o.timeoutMs ?? 30 * 6e4);
317
+ child.stdout.setEncoding("utf8").on("data", (d) => out.add(d));
318
+ child.stderr.setEncoding("utf8").on("data", (d) => err.add(d));
319
+ child.on("error", (e) => finish2({ exitCode: null, stdout: out.text, stderr: err.text + e.message }));
320
+ child.on("exit", (code) => {
321
+ drain = setTimeout(() => finish2({ exitCode: code, stdout: out.text, stderr: err.text }), 2e3);
179
322
  });
323
+ child.on("close", (code) => finish2({ exitCode: code, stdout: out.text, stderr: err.text }));
180
324
  });
181
325
  }
182
326
 
@@ -189,13 +333,16 @@ var TaskRunState = external_exports.enum([
189
333
  "planned",
190
334
  /** Hand-off mode: the person's assistant is doing the step. */
191
335
  "waiting",
192
- /** Draft finished with a passing build, committed on the task branch. */
336
+ /**
337
+ * Draft finished with a passing build, committed on the task branch. Also a PR that wasn't
338
+ * raised (the push or the Git host failed): its note says why, and it can be raised again.
339
+ */
193
340
  "built",
194
341
  /** Draft finished but the repo has no build command, so nothing was checked. */
195
342
  "unverified",
196
343
  /** The PR from the task branch to the change branch is open. */
197
344
  "pr-open",
198
- /** The build still fails after the allowed fix rounds, or a PR can't be opened: a person takes over. */
345
+ /** The build still fails after the allowed fix rounds, or a step after it can't be done: a person takes over. */
199
346
  "needs-person",
200
347
  /** The AI run itself failed. */
201
348
  "failed"
@@ -234,6 +381,16 @@ var TaskRunRecord = external_exports.strictObject({
234
381
  changed: external_exports.array(external_exports.string()).default([]),
235
382
  commit: external_exports.string().optional(),
236
383
  pr: external_exports.strictObject({ number: external_exports.number().int().positive(), url: external_exports.string() }).optional(),
384
+ /**
385
+ * Seventh UX walk: when Atticus ticked the task in tasks.md because its PR merged. It ticks a
386
+ * task once, so a person who unticks it keeps it unticked.
387
+ */
388
+ ticked: external_exports.string().optional(),
389
+ /**
390
+ * Eighth UX walk: who last ticked (done) or unticked the task by hand on the Task board, and
391
+ * when, so a task Atticus ticked and a person ticked again says who did.
392
+ */
393
+ marked: external_exports.strictObject({ done: external_exports.boolean(), by: external_exports.string().min(1), at: external_exports.string() }).optional(),
237
394
  startedAt: external_exports.string(),
238
395
  finishedAt: external_exports.string().optional(),
239
396
  notes: external_exports.array(external_exports.string()).default([])
@@ -266,6 +423,23 @@ async function saveRunSummary(root, change, task, s) {
266
423
  await writeFileAtomic(join2(changeFolder(root, change), rel), toCanonicalYaml(RunSummary.parse(s), ["schemaVersion", "runId"]));
267
424
  return rel;
268
425
  }
426
+ async function taskRunOfRun(root, change, runId) {
427
+ for (const t of (await readdir(join2(changeFolder(root, change), "tasks"), { withFileTypes: true }).catch(() => [])).filter((d) => d.isDirectory())) {
428
+ const r = await readTaskRun(root, change, t.name).catch(() => void 0);
429
+ if (r && (r.waiting === runId || r.runs.includes(runId)))
430
+ return r;
431
+ }
432
+ return void 0;
433
+ }
434
+ async function findTaskRunOfRun(root, runId) {
435
+ const changes = changesDirOf(root);
436
+ for (const c of (await readdir(changes, { withFileTypes: true }).catch(() => [])).filter((d) => d.isDirectory() && d.name !== "archive")) {
437
+ const r = await taskRunOfRun(root, c.name, runId).catch(() => void 0);
438
+ if (r)
439
+ return r;
440
+ }
441
+ return void 0;
442
+ }
269
443
  async function findWaitingTaskRun(root, runId) {
270
444
  const changes = changesDirOf(root);
271
445
  for (const c of (await readdir(changes, { withFileTypes: true }).catch(() => [])).filter((d) => d.isDirectory())) {
@@ -374,30 +548,34 @@ ${s.body.trim()}`,
374
548
  trusted: true
375
549
  }))
376
550
  ];
377
- const artifact = (rel, priority) => {
551
+ const artifact = (rel, priority, text = (raw) => raw) => {
378
552
  const d = t.files.docs.get(rel);
379
553
  if (d)
380
554
  sources.push({
381
555
  id: rel,
382
556
  kind: "artifact",
383
557
  origin: `${changesRelOf(t.root)}/${t.change.id}/${rel}`,
384
- text: d.raw,
558
+ text: text(d.raw),
385
559
  classification: "internal",
386
560
  priority
387
561
  });
388
562
  };
389
- const context = await taskContext(t.root, t.change.id, t.task.id).catch(() => void 0);
563
+ const context = await taskContext(t.root, t.change.id, t.task.id, {
564
+ at: { worktree: p.worktreeRel, branch: taskBranch(t.change.id, t.task.id) }
565
+ }).catch(() => void 0);
390
566
  if (context)
391
567
  sources.push({
392
568
  id: "task-context",
393
569
  kind: "artifact",
394
570
  origin: `atticus task context ${t.change.id} ${t.task.id}`,
395
- text: renderTaskContext(context).replace(/\r\n/g, "\n"),
571
+ // The pack wraps and screens the whole file as one source, so its parts aren't fenced
572
+ // again inside it (review round 47).
573
+ text: renderTaskContext(context, { fence: false }).replace(/\r\n/g, "\n"),
396
574
  classification: "internal",
397
575
  priority: 35
398
576
  });
399
577
  for (const s of t.task.stories)
400
- artifact(`stories/${s}.md`, 30);
578
+ artifact(`stories/${s}.md`, 30, (raw) => withBuildLine(raw, t.repo.id, t.repo.build));
401
579
  for (const a of ["design.md", "interfaces.md", "mappings.md", "data.md"])
402
580
  artifact(a, 20);
403
581
  const needs = await readIf(join3(t.root, ...changesRelOf(t.root).split("/"), t.change.id, "needs.md"));
@@ -501,6 +679,7 @@ function gitScanAt(root, git) {
501
679
  return {
502
680
  records: s.records,
503
681
  files,
682
+ basis: s.basis?.[repo] ?? { from: "readers" },
504
683
  problems: s.problems.filter((p) => !p.repo || p.repo === repo).map((p) => `${p.repo ? `${p.repo}: ` : ""}${p.source}: ${p.message}`)
505
684
  };
506
685
  } finally {
@@ -654,7 +833,8 @@ async function conformChange(root, change, o = {}) {
654
833
  tasks,
655
834
  before: before.records,
656
835
  after: after.records,
657
- files: after.files
836
+ files: after.files,
837
+ basis: after.basis
658
838
  });
659
839
  const count = (s) => r.items.filter((i) => i.status === s).length;
660
840
  const result = {
@@ -814,7 +994,7 @@ async function aiStep(c, buildLog) {
814
994
  if (c.runtime instanceof HandoffRuntime) {
815
995
  await writeFile2(join5(runsDirOf(c.root), request.change, prepared.runId, "prompt.md"), handoffPrompt(c, prepared), "utf8");
816
996
  }
817
- const run = await c.runtime.run(prepared.runId);
997
+ const run = await c.runtime.run(prepared.runId, stopSignal);
818
998
  await summarise(c, run);
819
999
  return run;
820
1000
  }
@@ -827,7 +1007,7 @@ function handoffPrompt(c, run) {
827
1007
  "",
828
1008
  run.request.pack.prompt.trimEnd(),
829
1009
  "",
830
- `When you are done, run: atticus handoff done ${run.runId}`,
1010
+ c.record.level === "pr" ? `When you are done, stop and ask the person to run: atticus handoff done ${run.runId} --approve (it pushes the branch and opens a PR, so the person runs it, never you)` : `When you are done, run: atticus handoff done ${run.runId}`,
831
1011
  ""
832
1012
  ].join("\n");
833
1013
  }
@@ -837,6 +1017,8 @@ async function build(c, runId, fixRun) {
837
1017
  return void 0;
838
1018
  const n = c.record.attempts.length + 1;
839
1019
  const res = await c.commands(command, c.worktree);
1020
+ if (res.stopped)
1021
+ c.stopped = true;
840
1022
  const logRel = `${runsRelOf(c.root)}/${c.t.change.id}/${runId}/build-${n}.log`;
841
1023
  await mkdir3(join5(runsDirOf(c.root), c.t.change.id, runId), { recursive: true });
842
1024
  await writeFile2(join5(c.root, logRel), `$ ${command}
@@ -856,14 +1038,24 @@ ${res.stderr}` : ""}
856
1038
  c.record.attempts.push(a);
857
1039
  return a;
858
1040
  }
1041
+ async function changedFiles(c) {
1042
+ const r = await c.git(["-c", "core.quotePath=false", "status", "--porcelain", "-z"], c.worktree);
1043
+ const parts = r.stdout.split("\0").filter(Boolean);
1044
+ const out = [];
1045
+ for (let k = 0; k < parts.length; k++) {
1046
+ const e = parts[k];
1047
+ if (/^[RC]/.test(e))
1048
+ k++;
1049
+ if (e.slice(3))
1050
+ out.push(e.slice(3));
1051
+ }
1052
+ return out;
1053
+ }
859
1054
  async function applyAnswer(c, run) {
860
1055
  if (c.runtime.kind === "headless-claude-code") {
861
- const changed = await c.git(["status", "--porcelain"], c.worktree);
862
- for (const l of changed.stdout.split("\n").filter(Boolean)) {
863
- const p = l.slice(3).trim();
1056
+ for (const p of await changedFiles(c))
864
1057
  if (!c.record.changed.includes(p))
865
1058
  c.record.changed.push(p);
866
- }
867
1059
  await writeReport(c, run.output ?? "");
868
1060
  return [];
869
1061
  }
@@ -892,7 +1084,8 @@ async function buildAndFix(c, runId) {
892
1084
  return;
893
1085
  }
894
1086
  let fixes = 0;
895
- while (!a.passed && fixes < c.maxFix && c.runtime.kind !== "hand-off") {
1087
+ const halted = () => c.stopped || stopRequested();
1088
+ while (!a.passed && !halted() && fixes < c.maxFix && c.runtime.kind !== "hand-off") {
896
1089
  fixes++;
897
1090
  const log = await import("node:fs/promises").then((fs) => fs.readFile(join5(c.root, a.log), "utf8"));
898
1091
  const fix = await aiStep(c, { command: a.command, text: log, attempt: fixes });
@@ -900,6 +1093,8 @@ async function buildAndFix(c, runId) {
900
1093
  c.record.notes.push(`Fix round ${fixes} failed: ${fix.errors.join("; ") || fix.state}.`);
901
1094
  break;
902
1095
  }
1096
+ if (halted())
1097
+ break;
903
1098
  const problems = await applyAnswer(c, fix);
904
1099
  if (problems.length) {
905
1100
  c.record.notes.push(`Fix round ${fixes} was not applied: ${problems.join("; ")}.`);
@@ -907,6 +1102,11 @@ async function buildAndFix(c, runId) {
907
1102
  }
908
1103
  a = await build(c, runId, fix.runId);
909
1104
  }
1105
+ if (halted()) {
1106
+ c.record.state = "needs-person";
1107
+ c.record.notes.push(`Atticus was stopped while the build ran, so nothing more was done; a person takes over in ${c.record.worktree}. See ${a.log}.`);
1108
+ return;
1109
+ }
910
1110
  if (!a.passed) {
911
1111
  c.record.state = "needs-person";
912
1112
  c.record.notes.push(`The build still fails after ${fixes} fix round${fixes === 1 ? "" : "s"}; a person takes over in ${c.record.worktree}. See ${a.log}.`);
@@ -918,8 +1118,15 @@ async function commit(c, runId) {
918
1118
  await c.git(["add", "-A", "--", "."], c.worktree);
919
1119
  const staged = (await c.git(["diff", "--cached", "--name-only"], c.worktree)).stdout.trim();
920
1120
  if (!staged) {
921
- c.record.notes.push("Nothing to commit.");
922
- return;
1121
+ const own = await ownTaskCommit(c);
1122
+ if (own) {
1123
+ c.record.commit = own;
1124
+ c.record.notes.push(`Nothing new to commit: ${c.record.branch} already has this task's commit ${own.slice(0, 12)}, ahead of ${changeBranch(c.t.change.id)}, so it is the one built and raised.`);
1125
+ return true;
1126
+ }
1127
+ c.record.state = "needs-person";
1128
+ c.record.notes.push(`The AI's answer changed nothing in ${c.record.branch}, so there is nothing to commit: run the task again, or do it by hand in ${c.record.worktree}.`);
1129
+ return false;
923
1130
  }
924
1131
  await c.git([
925
1132
  "commit",
@@ -933,10 +1140,22 @@ Atticus-Run: ${runId}
933
1140
  Requested-By: ${c.record.by}`
934
1141
  ], c.worktree);
935
1142
  c.record.commit = (await c.git(["rev-parse", "HEAD"], c.worktree)).stdout.trim();
1143
+ return true;
1144
+ }
1145
+ async function ownTaskCommit(c) {
1146
+ const base = changeBranch(c.t.change.id);
1147
+ const log = await c.git(["log", "--format=%B%x00", `refs/heads/${base}..HEAD`], c.worktree).catch(() => ({ stdout: "" }));
1148
+ const mine = log.stdout.split("\0").some((m) => {
1149
+ const lines = m.split("\n").map((l) => l.trim());
1150
+ return lines.includes(`Atticus-Change: ${c.t.change.id}`) && lines.includes(`Atticus-Task: ${c.t.task.id}`);
1151
+ });
1152
+ if (!mine)
1153
+ return void 0;
1154
+ return (await c.git(["rev-parse", "HEAD"], c.worktree)).stdout.trim() || void 0;
936
1155
  }
937
1156
  async function openPr(c) {
938
1157
  if (c.record.state !== "built") {
939
- c.record.notes.push(c.record.state === "unverified" ? "No PR: a pull request needs a passing build, and this repo has no build command." : "No PR: the build doesn't pass.");
1158
+ c.record.notes.push(c.record.state === "unverified" ? `No PR: a pull request needs a passing build, and ${c.t.repo.id} has no build command: set a build command for ${c.t.repo.id} (build: under its entry in repos.manifest.yaml), then run task ${c.t.task.id} again.` : "No PR: the build doesn't pass.");
940
1159
  if (c.record.state === "unverified")
941
1160
  c.record.state = "needs-person";
942
1161
  return;
@@ -948,20 +1167,25 @@ async function openPr(c) {
948
1167
  return;
949
1168
  }
950
1169
  const base = changeBranch(c.t.change.id);
951
- const info = await c.gitHost.getRepo(c.t.repo.id);
952
- for (const b of [c.record.branch, base])
953
- if (info.protectedBranches.includes(b))
954
- throw new TaskRunError("refused", `${b} is protected in ${c.t.repo.id}; Run with AI never pushes to or opens a PR into a protected branch.`);
955
- if (base === info.defaultBranch)
956
- throw new TaskRunError("refused", `${base} is ${c.t.repo.id}'s default branch; task PRs go into the change branch.`);
957
- await c.git([
958
- "push",
959
- "-q",
960
- "origin",
961
- `refs/heads/${base}:refs/heads/${base}`,
962
- `refs/heads/${c.record.branch}:refs/heads/${c.record.branch}`
963
- ], c.worktree);
1170
+ if (!c.record.commit)
1171
+ throw new TaskRunError("refused", `Task ${c.t.task.id} has no commit to raise: run the task again, or do it by hand in ${c.record.worktree}.`);
1172
+ const head = (await c.git(["rev-parse", "--verify", "-q", `refs/heads/${c.record.branch}`], c.worktree).catch(() => ({ stdout: "" }))).stdout.trim();
1173
+ if (!head)
1174
+ throw new TaskRunError("refused", `Task ${c.t.task.id}'s branch ${c.record.branch} isn't in ${c.record.worktree} any more: run the task again.`);
1175
+ if (head !== c.record.commit)
1176
+ throw new TaskRunError("refused", `Task ${c.t.task.id}'s branch ${c.record.branch} has commits that weren't built (it is at ${head.slice(0, 12)}; the build passed at ${c.record.commit.slice(0, 12)}): run the task again so its build checks them, or push ${c.record.branch} and open its PR into ${base} yourself.`);
964
1177
  try {
1178
+ const info = await c.gitHost.getRepo(c.t.repo.id);
1179
+ for (const b of [c.record.branch, base])
1180
+ if (info.protectedBranches.includes(b))
1181
+ throw new TaskRunError("refused", `${b} is protected in ${c.t.repo.id}; Run with AI never pushes to or opens a PR into a protected branch.`);
1182
+ if (base === info.defaultBranch)
1183
+ throw new TaskRunError("refused", `${base} is ${c.t.repo.id}'s default branch; task PRs go into the change branch.`);
1184
+ const onHost = (await c.git(["ls-remote", "--heads", "origin", `refs/heads/${base}`], c.worktree)).stdout.trim();
1185
+ const refs = [`refs/heads/${c.record.branch}:refs/heads/${c.record.branch}`];
1186
+ if (!onHost)
1187
+ refs.unshift(`refs/heads/${base}:refs/heads/${base}`);
1188
+ await c.git(["push", "-q", ...refs.length > 1 ? ["--atomic"] : [], "origin", ...refs], c.worktree);
965
1189
  const pr = await c.gitHost.createPullRequest({
966
1190
  repo: c.t.repo.id,
967
1191
  head: c.record.branch,
@@ -979,15 +1203,27 @@ async function openPr(c) {
979
1203
  c.record.pr = { number: pr.number, url: pr.url };
980
1204
  c.record.state = "pr-open";
981
1205
  } catch (e) {
982
- if (e instanceof AdapterError) {
983
- c.record.state = "needs-person";
984
- c.record.notes.push(`The PR couldn't be opened: ${e.message}`);
985
- return;
1206
+ if (e instanceof TaskRunError)
1207
+ throw e;
1208
+ if (e instanceof AdapterError && e.code === "conflict") {
1209
+ const open = (await c.gitHost.listPullRequests(c.t.repo.id, { head: c.record.branch }).catch(() => [])).filter((p) => p.base === base && (p.state === "open" || p.state === "draft")).at(0);
1210
+ if (open) {
1211
+ c.record.pr = { number: open.number, url: open.url };
1212
+ c.record.state = "pr-open";
1213
+ c.record.notes.push(`PR #${open.number} from ${c.record.branch} into ${base} was already open on the Git host; recorded it (${open.url}).`);
1214
+ return;
1215
+ }
986
1216
  }
987
- throw e;
1217
+ c.record.notes.push(`${PR_NOT_RAISED} ${(e instanceof Error ? e.message : String(e)).replace(/\.?$/, ".")} The build still passes: fix that and raise the task's PR again (Raise the task's PR on the Task board), or push ${c.record.branch} and open its PR into ${base} yourself.`);
988
1218
  }
989
1219
  }
1220
+ var PR_NOT_RAISED = "The PR wasn't raised:";
990
1221
  async function finish(c, run) {
1222
+ if (stopRequested()) {
1223
+ c.record.state = "needs-person";
1224
+ c.record.notes.push(c.runtime.kind === "headless-claude-code" ? `Atticus was stopped while Claude Code worked, so nothing more was done; the worktree ${c.record.worktree} may hold some of its edits.` : `Atticus was stopped before the answer was applied, so nothing was written; run the task again.`);
1225
+ return done(c);
1226
+ }
991
1227
  if (c.record.level === "plan") {
992
1228
  if (c.runtime.kind !== "hand-off")
993
1229
  await writeFileAtomic(join5(changeFolder(c.root, c.t.change.id), run.request.outputPath), `${(run.output ?? "").trim()}
@@ -1003,45 +1239,255 @@ async function finish(c, run) {
1003
1239
  }
1004
1240
  }
1005
1241
  await buildAndFix(c, run.runId);
1006
- if (c.record.state === "built" || c.record.state === "unverified")
1007
- await commit(c, run.runId);
1242
+ await afterBuild(c, run.runId, c.record.state === "built" || c.record.state === "unverified");
1243
+ }
1244
+ return done(c);
1245
+ }
1246
+ async function afterBuild(c, runId, commitIt, first) {
1247
+ try {
1248
+ await first?.();
1249
+ if (commitIt && !await commit(c, runId))
1250
+ return;
1008
1251
  await conformAfterBuild(c);
1009
1252
  if (c.record.level === "pr")
1010
1253
  await openPr(c);
1254
+ } catch (e) {
1255
+ c.record.state = "needs-person";
1256
+ c.record.notes.push(`${e instanceof Error ? e.message : String(e)} \u2014 the work is in ${c.record.worktree} (branch ${c.record.branch}): finish it there by hand, or fix that and run the task again.`);
1011
1257
  }
1012
- return done(c);
1013
1258
  }
1014
1259
  async function done(c) {
1015
1260
  delete c.record.waiting;
1016
1261
  c.record.finishedAt = stamp2(c.now());
1017
1262
  return saveTaskRun(c.root, c.record);
1018
1263
  }
1264
+ function levelOf(t, asked) {
1265
+ const rt = t.ctx.orgPack.runtime;
1266
+ const cap = rt.autonomy.capPerRepo[t.repo.id];
1267
+ const capped = cap ? AUTONOMY[cap] : void 0;
1268
+ if (asked === void 0) {
1269
+ const level = AUTONOMY[rt.autonomy.default];
1270
+ return capped && LEVEL_RANK[level] > LEVEL_RANK[capped] ? capped : level;
1271
+ }
1272
+ if (capped && LEVEL_RANK[asked] > LEVEL_RANK[capped])
1273
+ throw new TaskRunError("refused", `${t.repo.id} allows Run with AI up to ${cap} (runtime.yaml autonomy.capPerRepo); ask for a lower level.`);
1274
+ return asked;
1275
+ }
1276
+ async function ownTaskPr(host, r, held) {
1277
+ const pr = await host.getPullRequest(r.repo, held.number);
1278
+ if (pr.head === r.branch)
1279
+ return pr;
1280
+ const mine = await host.listPullRequests(r.repo, { head: r.branch });
1281
+ const own = mine.find((x) => x.url === held.url) ?? mine.at(-1);
1282
+ if (own)
1283
+ return own;
1284
+ throw new TaskRunError("refused", `PR #${held.number} on the Git host is from ${pr.head}, not ${r.branch}, and no PR from ${r.branch} was found`);
1285
+ }
1286
+ async function refuseIfHeld(root, t, host, git) {
1287
+ const before = await readTaskRun(root, t.change.id, t.task.id);
1288
+ if (before?.state === "pr-open") {
1289
+ let state;
1290
+ let why = "";
1291
+ try {
1292
+ const h = host();
1293
+ if (!h)
1294
+ why = "no Git host is connected (runtime.yaml adapters.gitHost)";
1295
+ else if (before.pr)
1296
+ state = (await ownTaskPr(h, before, before.pr)).state;
1297
+ } catch (e) {
1298
+ why = e instanceof Error ? e.message : String(e);
1299
+ }
1300
+ if (before.pr && (state === "merged" || state === "closed")) {
1301
+ const own = await taskBranchOwnCommits(root, t.change.id, t.task.id, t.repo.id, git);
1302
+ if (own > 0) {
1303
+ const branch = taskBranch(t.change.id, t.task.id);
1304
+ const from = changeBranch(t.change.id);
1305
+ const clone = baseClonePath(root, t.repo.id);
1306
+ const carried = state === "closed" ? "the commits that PR carried" : "commits the change branch hasn't (a squash or rebase merge took them in another form)";
1307
+ throw new TaskRunError("refused", `Task ${t.task.id}'s earlier PR ${before.pr.url} is ${state}, and its branch ${branch} still has ${carried} (${own} not in ${from}): running the task again would raise them again. Atticus doesn't delete or rewrite them. To start the task afresh, remove its worktree and branch here (git -C ${clone} worktree remove ${join5(root, before.worktree)}, then git -C ${clone} branch -D ${branch}) and delete ${branch} on the Git host, then run the task again; or ask whoever looks after the workspace for a fresh start.`);
1308
+ }
1309
+ return { notes: [`Its earlier PR ${before.pr.url} is ${state}.`], settled: true };
1310
+ }
1311
+ throw new TaskRunError("refused", `Task ${t.task.id}'s PR is open (${before.pr?.url ?? "see its run"}): running the task again would start over beside it. Merge or close the PR first, then run the task again.${why ? ` (Atticus couldn't ask the Git host whether it still is: ${why}.)` : ""}`);
1312
+ }
1313
+ const open = ["prepared", "waiting", "produced", "failed"];
1314
+ if (before?.state === "waiting" && before.waiting && open.includes(await handoffRunState(root, t.change.id, before.waiting) ?? ""))
1315
+ throw new TaskRunError("refused", `Task ${t.task.id} already waits on your assistant (run ${before.waiting}, in ${before.worktree}): finish it with Done on its run page (Model runs) or atticus handoff done ${before.waiting}, or Cancel it there and run the task again.`);
1316
+ return { notes: [] };
1317
+ }
1318
+ async function taskPrState(root, changeId, taskId, o = {}) {
1319
+ const r = await readTaskRun(root, changeId, taskId).catch(() => void 0);
1320
+ if (r?.state !== "pr-open" || !r.pr)
1321
+ return void 0;
1322
+ const pr = r.pr;
1323
+ const ask = async () => {
1324
+ const host = o.gitHost ?? gitHostOf(root, await loadTask(root, changeId, taskId));
1325
+ if (!host)
1326
+ return void 0;
1327
+ const s = (await ownTaskPr(host, r, pr)).state;
1328
+ return s === "merged" || s === "closed" ? s : "open";
1329
+ };
1330
+ let timer;
1331
+ const late = new Promise((res) => {
1332
+ timer = setTimeout(() => res(void 0), o.timeoutMs ?? 3e3);
1333
+ });
1334
+ try {
1335
+ return await Promise.race([ask(), late]);
1336
+ } catch {
1337
+ return void 0;
1338
+ } finally {
1339
+ clearTimeout(timer);
1340
+ }
1341
+ }
1342
+ async function settleTaskPr(root, changeId, taskId, o = {}) {
1343
+ const r = await readTaskRun(root, changeId, taskId).catch(() => void 0);
1344
+ if (r?.state !== "pr-open" || !r.pr)
1345
+ return {};
1346
+ const recorded = r.pr;
1347
+ const ask = async () => {
1348
+ const host = o.gitHost ?? gitHostOf(root, await loadTask(root, changeId, taskId));
1349
+ if (!host)
1350
+ return void 0;
1351
+ return ownTaskPr(host, r, recorded);
1352
+ };
1353
+ let timer;
1354
+ const late = new Promise((res) => {
1355
+ timer = setTimeout(() => res(void 0), o.timeoutMs ?? 3e3);
1356
+ });
1357
+ const pr = await Promise.race([ask(), late]).catch(() => void 0).finally(() => clearTimeout(timer));
1358
+ if (!pr)
1359
+ return {};
1360
+ const state = pr.state === "merged" || pr.state === "closed" ? pr.state : "open";
1361
+ const cur = await readTaskRun(root, changeId, taskId).catch(() => void 0);
1362
+ if (cur?.state !== "pr-open" || cur.pr?.url !== recorded.url)
1363
+ return { state };
1364
+ let changed = false;
1365
+ if (pr.number !== recorded.number || pr.url !== recorded.url) {
1366
+ cur.pr = { number: pr.number, url: pr.url };
1367
+ changed = true;
1368
+ }
1369
+ let ticked = false;
1370
+ if (state === "merged" && !cur.ticked) {
1371
+ ticked = await setTaskDone(root, changeId, taskId, true);
1372
+ cur.ticked = stamp2((o.now ?? (() => /* @__PURE__ */ new Date()))());
1373
+ if (ticked)
1374
+ cur.notes.push(`Ticked ${taskId} in tasks.md: PR #${pr.number} merged on the Git host. The tick rides the change's build hand-off.`);
1375
+ changed = true;
1376
+ }
1377
+ if (changed)
1378
+ await saveTaskRun(root, cur);
1379
+ return { state, ...ticked ? { ticked: true } : {} };
1380
+ }
1381
+ function gitHostConnected(root, t, given) {
1382
+ try {
1383
+ return !!(given ?? gitHostOf(root, t));
1384
+ } catch {
1385
+ return false;
1386
+ }
1387
+ }
1388
+ async function previewTaskRun(root, changeId, taskId, o = {}) {
1389
+ const t = await loadTask(root, changeId, taskId);
1390
+ const level = levelOf(t, o.level);
1391
+ await refuseIfHeld(root, t, () => o.gitHost ?? gitHostOf(root, t), createGit());
1392
+ return {
1393
+ change: t.change.id,
1394
+ task: t.task.id,
1395
+ repo: t.repo.id,
1396
+ level,
1397
+ branch: taskBranch(t.change.id, t.task.id),
1398
+ gitHost: level === "pr" && !o.skipGitHost ? gitHostConnected(root, t, o.gitHost) : false
1399
+ };
1400
+ }
1401
+ async function cancelTaskRunWait(root, runId, o) {
1402
+ const r = await findWaitingTaskRun(root, runId);
1403
+ if (!r) {
1404
+ await o.cancelRun?.();
1405
+ return void 0;
1406
+ }
1407
+ const lock = await holdTask(root, r.change, r.task);
1408
+ try {
1409
+ const cur = await findWaitingTaskRun(root, runId);
1410
+ await o.cancelRun?.();
1411
+ if (!cur)
1412
+ return void 0;
1413
+ delete cur.waiting;
1414
+ cur.state = "failed";
1415
+ cur.notes.push(`Run ${runId} was cancelled by ${o.by}, so nothing more was done; run the task again.`);
1416
+ cur.finishedAt = stamp2((o.now ?? (() => /* @__PURE__ */ new Date()))());
1417
+ return await saveTaskRun(root, cur);
1418
+ } finally {
1419
+ await lock.release();
1420
+ }
1421
+ }
1422
+ async function handoffRunState(root, change, runId) {
1423
+ const text = await import("node:fs/promises").then((fs) => fs.readFile(join5(runsDirOf(root), change, runId, "run.json"), "utf8").catch(() => void 0));
1424
+ if (!text)
1425
+ return void 0;
1426
+ try {
1427
+ return JSON.parse(text).state;
1428
+ } catch {
1429
+ return void 0;
1430
+ }
1431
+ }
1432
+ async function holdTask(root, change, task) {
1433
+ try {
1434
+ return await acquireLock(root, `task-run-${change}-${task}`, `run of task ${task} of ${change}`, void 0, { maxAgeMs: 12 * 60 * 6e4 });
1435
+ } catch (e) {
1436
+ if (e instanceof LockHeldError)
1437
+ throw new TaskRunError("refused", e.message);
1438
+ throw e;
1439
+ }
1440
+ }
1019
1441
  async function runTask(root, changeId, taskId, o) {
1442
+ const lock = await holdTask(root, changeId, taskId);
1443
+ const release = holdSignals();
1444
+ try {
1445
+ return await runTaskHeld(root, changeId, taskId, o);
1446
+ } finally {
1447
+ await lock.release();
1448
+ release();
1449
+ }
1450
+ }
1451
+ async function runTaskHeld(root, changeId, taskId, o) {
1020
1452
  const now = o.now ?? (() => /* @__PURE__ */ new Date());
1021
1453
  const git = o.git ?? createGit();
1022
1454
  let t = await loadTask(root, changeId, taskId);
1023
1455
  const rt = t.ctx.orgPack.runtime;
1024
- const level = o.level ?? AUTONOMY[rt.autonomy.default];
1025
- const cap = rt.autonomy.capPerRepo[t.repo.id];
1026
- if (cap && LEVEL_RANK[level] > LEVEL_RANK[AUTONOMY[cap]])
1027
- throw new TaskRunError("refused", `${t.repo.id} allows Run with AI up to ${cap} (runtime.yaml autonomy.capPerRepo); ask for a lower level.`);
1456
+ const level = levelOf(t, o.level);
1457
+ const earlier = await refuseIfHeld(root, t, () => o.gitHost ?? gitHostOf(root, t), git);
1028
1458
  if (level === "pr") {
1029
1459
  try {
1030
1460
  requireApproval("git-host", o.approval);
1031
1461
  } catch {
1032
- throw new TaskRunError("refused", "The pr level pushes the task branch and opens a PR, which needs your approval: run it again with --approve.");
1462
+ throw new TaskRunError("refused", "The pr level pushes the task branch and opens a PR, which needs the person's yes: ask them to run it with --approve (an assistant never approves a push).");
1033
1463
  }
1034
1464
  }
1035
1465
  const engine = buildEngineFor(t.ctx.orgPack, t.repo.id);
1036
1466
  const changeDir = await changeWorktree(root, t.change.id, t.repo.id);
1037
1467
  const handed = engine === "speckit" ? `specs/${t.task.id}/plan.md` : `.atticus/tasks/${t.task.id}.md`;
1038
- const notes = [];
1468
+ const notes = [...earlier.notes];
1469
+ try {
1470
+ const caught = await catchUpChangeBranch(root, t.change.id, t.task.id, t.repo.id, changeDir, git, earlier.settled ? { settled: true } : {});
1471
+ if (caught)
1472
+ notes.push(caught);
1473
+ } catch (e) {
1474
+ if (e instanceof Error && e.name === "SpecKitError")
1475
+ throw new TaskRunError("refused", e.message);
1476
+ throw e;
1477
+ }
1039
1478
  if (!existsSync2(join5(changeDir, handed))) {
1040
1479
  const h = await handDown(root, t.change.id, t.task.id, { git });
1041
1480
  notes.push(`Handed the task down first (${h.feature} in ${h.repo}${h.commit ? `, commit ${h.commit.slice(0, 12)}` : ""}).`);
1042
1481
  t = await loadTask(root, changeId, taskId);
1043
1482
  }
1044
- const wt = await openTaskWorktree(root, t.change.id, t.task.id, t.repo.id, git);
1483
+ let wt;
1484
+ try {
1485
+ wt = await openTaskWorktree(root, t.change.id, t.task.id, t.repo.id, git, earlier.settled ? { fresh: true } : {});
1486
+ } catch (e) {
1487
+ if (e instanceof Error && e.name === "SpecKitError")
1488
+ throw new TaskRunError("refused", e.message);
1489
+ throw e;
1490
+ }
1045
1491
  const runtime = o.runtime ?? await createRuntime(t.ctx.orgPack, {
1046
1492
  root,
1047
1493
  ...o.mode ? { mode: o.mode } : {},
@@ -1097,16 +1543,36 @@ async function runTask(root, changeId, taskId, o) {
1097
1543
  record.waiting = run.runId;
1098
1544
  return saveTaskRun(root, record);
1099
1545
  }
1100
- if (run.state !== "validated") {
1546
+ if (run.state !== "validated" && !stopRequested()) {
1101
1547
  record.notes.push(`The AI run ended ${run.state}: ${run.errors.join("; ") || "no reason given"}.`);
1102
1548
  return done(c);
1103
1549
  }
1104
1550
  return finish(c, run);
1105
1551
  }
1106
1552
  async function completeTaskHandoff(root, runId, o = {}) {
1107
- const record = await findWaitingTaskRun(root, runId);
1108
- if (!record)
1553
+ const waiting = await findWaitingTaskRun(root, runId);
1554
+ if (!waiting)
1109
1555
  throw new TaskRunError("not-found", `No task run is waiting on ${runId}.`);
1556
+ if (waiting.level === "pr" && !o.approval)
1557
+ throw new TaskRunError("refused", `Task ${waiting.task} runs at the pr level, which pushes ${waiting.branch} and opens a PR: ask the person to run atticus handoff done ${runId} --approve (an assistant never approves a push).`);
1558
+ const lock = await holdTask(root, waiting.change, waiting.task);
1559
+ const release = holdSignals();
1560
+ try {
1561
+ const state = await handoffRunState(root, waiting.change, runId);
1562
+ if (state === void 0)
1563
+ throw new TaskRunError("refused", `Run ${runId} isn't on this machine (its files are kept where it was started): run the task again here (atticus task run ${waiting.change} ${waiting.task}).`);
1564
+ if (state === "cancelled")
1565
+ throw new TaskRunError("refused", `Run ${runId} was cancelled; run the task again (atticus task run ${waiting.change} ${waiting.task}).`);
1566
+ const record = await findWaitingTaskRun(root, runId);
1567
+ if (!record)
1568
+ throw new TaskRunError("not-found", `No task run is waiting on ${runId}.`);
1569
+ return await completeHeld(root, runId, record, o);
1570
+ } finally {
1571
+ await lock.release();
1572
+ release();
1573
+ }
1574
+ }
1575
+ async function completeHeld(root, runId, record, o) {
1110
1576
  const now = o.now ?? (() => /* @__PURE__ */ new Date());
1111
1577
  const git = o.git ?? createGit();
1112
1578
  const t = await loadTask(root, record.change, record.task);
@@ -1138,7 +1604,11 @@ async function completeTaskHandoff(root, runId, o = {}) {
1138
1604
  return [`${file.slice(root.length + 1)} is empty`];
1139
1605
  if (record.level === "plan")
1140
1606
  return [];
1607
+ if (stopRequested())
1608
+ return ["Atticus was stopped"];
1141
1609
  attempt = await build(c, runId);
1610
+ if (c.stopped)
1611
+ return ["Atticus was stopped while the build ran"];
1142
1612
  if (!attempt)
1143
1613
  return [];
1144
1614
  return attempt.passed ? [] : [`${attempt.command} failed; see ${attempt.log}`];
@@ -1154,19 +1624,32 @@ async function completeTaskHandoff(root, runId, o = {}) {
1154
1624
  record.state = attempt ? "built" : "unverified";
1155
1625
  if (!attempt)
1156
1626
  record.notes.push(`${t.repo.id} has no build command in repos.manifest.yaml, so the change wasn't built or tested.`);
1157
- const changed = await git(["status", "--porcelain"], c.worktree);
1158
- for (const l of changed.stdout.split("\n").filter(Boolean)) {
1159
- const p = l.slice(3).trim();
1160
- if (!record.changed.includes(p))
1161
- record.changed.push(p);
1162
- }
1163
- await commit(c, runId);
1164
- await conformAfterBuild(c);
1165
- if (record.level === "pr")
1166
- await openPr(c);
1627
+ await afterBuild(c, runId, true, async () => {
1628
+ for (const p of await changedFiles(c))
1629
+ if (!record.changed.includes(p))
1630
+ record.changed.push(p);
1631
+ });
1167
1632
  }
1168
1633
  return done(c);
1169
1634
  }
1635
+ if (c.stopped || stopRequested()) {
1636
+ if (c.stopped && record.attempts.at(-1)?.passed === false)
1637
+ record.attempts.pop();
1638
+ record.state = "waiting";
1639
+ record.waiting = runId;
1640
+ await handoff.retry(runId);
1641
+ await handoff.run(runId);
1642
+ record.notes.push(`Atticus was stopped while it checked the work, so nothing more was done; run atticus handoff done ${runId} again.`);
1643
+ return saveTaskRun(root, record);
1644
+ }
1645
+ if (!attempt) {
1646
+ record.state = "waiting";
1647
+ record.waiting = runId;
1648
+ await handoff.retry(runId);
1649
+ await handoff.run(runId);
1650
+ record.notes.push(`${run.errors.join("; ") || "The answer isn't there yet"}: write it, then run atticus handoff done ${runId} again.`);
1651
+ return saveTaskRun(root, record);
1652
+ }
1170
1653
  const builds = record.attempts.length;
1171
1654
  if (attempt && !attempt.passed && builds <= c.maxFix) {
1172
1655
  await handoff.retry(runId);
@@ -1178,15 +1661,207 @@ async function completeTaskHandoff(root, runId, o = {}) {
1178
1661
  record.notes.push(attempt && !attempt.passed ? `The build still fails after ${builds} attempts; a person takes over in ${record.worktree}.` : `The hand-off failed: ${run.errors.join("; ")}.`);
1179
1662
  return done(c);
1180
1663
  }
1664
+ async function raiseTaskPr(root, changeId, taskId, o) {
1665
+ try {
1666
+ requireApproval("git-host", o.approval);
1667
+ } catch {
1668
+ throw new TaskRunError("refused", "A task's PR pushes its branch and opens a PR, which needs the person's yes (an assistant never approves a push).");
1669
+ }
1670
+ const lock = await holdTask(root, changeId, taskId);
1671
+ try {
1672
+ const record = await readTaskRun(root, changeId, taskId);
1673
+ if (!record || record.level === "plan" || ["planned", "failed"].includes(record.state))
1674
+ throw new TaskRunError("refused", `Task ${taskId} hasn't been drafted yet: run it with AI first; its PR comes after the draft.`);
1675
+ if (record.state === "pr-open")
1676
+ throw new TaskRunError("refused", `Task ${taskId}'s PR is already open: ${record.pr?.url ?? "see its run"}.`);
1677
+ if (record.state === "waiting")
1678
+ throw new TaskRunError("refused", `Task ${taskId} still waits on your assistant (run ${record.waiting ?? "on its page"}): finish it with Done first; its PR comes after the build.`);
1679
+ const change = changeBranch(changeId);
1680
+ if (record.state === "unverified")
1681
+ throw new TaskRunError("refused", `Atticus opens a task's PR only after a passing build, and ${record.repo} has no build command in repos.manifest.yaml: add one and run task ${taskId} again, or push ${record.branch} and open its PR into ${change} yourself.`);
1682
+ if (record.state !== "built" || !record.commit)
1683
+ throw new TaskRunError("refused", `Task ${taskId} has no passing build to raise (it is ${record.state}): see its run's notes, then run it again.`);
1684
+ const t = await loadTask(root, changeId, taskId);
1685
+ const gitHost = o.gitHost ?? gitHostOf(root, t);
1686
+ if (!gitHost)
1687
+ throw new TaskRunError("refused", `No Git host is connected (runtime.yaml adapters.gitHost): push ${record.branch} and open its PR into ${change} yourself.`);
1688
+ const now = o.now ?? (() => /* @__PURE__ */ new Date());
1689
+ const c = {
1690
+ root,
1691
+ t,
1692
+ record,
1693
+ worktree: join5(root, record.worktree),
1694
+ runtime: void 0,
1695
+ commands: shellCommandRunner(),
1696
+ git: o.git ?? createGit(),
1697
+ gitHost,
1698
+ approval: o.approval,
1699
+ now,
1700
+ maxFix: t.ctx.orgPack.runtime.autonomy.maxFixRounds
1701
+ };
1702
+ await openPr(c);
1703
+ if (record.state === "pr-open")
1704
+ record.notes.push(`PR raised by ${o.approval.by} after the draft (${o.approval.ref}).`);
1705
+ return await done(c);
1706
+ } finally {
1707
+ await lock.release();
1708
+ }
1709
+ }
1181
1710
  async function isTaskRun(root, runId) {
1182
1711
  return await findWaitingTaskRun(root, runId) !== void 0;
1183
1712
  }
1184
1713
 
1714
+ // packages/taskrun/dist/ready.js
1715
+ var PAST = {
1716
+ waiting: "it is with your assistant: Done on its run carries on",
1717
+ built: "it is drafted: its PR is the next step",
1718
+ unverified: "it is drafted but not built: its repo has no build command",
1719
+ "pr-open": "its PR is open on the Git host",
1720
+ "needs-person": "it needs a person: carry on in its worktree, or Run with AI on it"
1721
+ };
1722
+ function sameRepoBefore(tasks, t) {
1723
+ const mine = tasks.slice(0, tasks.indexOf(t)).filter((x) => x.repo === t.repo);
1724
+ let cut = mine.length;
1725
+ if (t.parallel)
1726
+ while (cut > 0 && mine[cut - 1].parallel)
1727
+ cut--;
1728
+ return mine.slice(0, cut).filter((x) => !x.done).map((x) => x.id);
1729
+ }
1730
+ function planReadyTasks(o) {
1731
+ const deps = new Map(o.stories.map((s) => [s.id, s.dependsOn]));
1732
+ const rank = (repo) => {
1733
+ const i = o.repoOrder?.indexOf(repo) ?? -1;
1734
+ return i < 0 ? Number.MAX_SAFE_INTEGER : i;
1735
+ };
1736
+ const firstOfRepo = /* @__PURE__ */ new Map();
1737
+ o.tasks.forEach((t, i) => {
1738
+ if (!firstOfRepo.has(t.repo))
1739
+ firstOfRepo.set(t.repo, i);
1740
+ });
1741
+ const order = o.tasks.map((t, i) => ({ t, i })).sort((a, b) => rank(a.t.repo) - rank(b.t.repo) || firstOfRepo.get(a.t.repo) - firstOfRepo.get(b.t.repo) || a.i - b.i).map((x) => x.t);
1742
+ const out = [];
1743
+ for (const t of order) {
1744
+ if (t.done)
1745
+ continue;
1746
+ const before = new Set(t.stories.flatMap((s) => deps.get(s) ?? []));
1747
+ const byStory = o.tasks.filter((x) => x.id !== t.id && !x.done && x.stories.some((s) => before.has(s))).map((x) => x.id);
1748
+ const sameRepo = sameRepoBefore(o.tasks, t).filter((id) => !byStory.includes(id));
1749
+ const waitsFor = o.tasks.map((x) => x.id).filter((id) => byStory.includes(id) || sameRepo.includes(id));
1750
+ const past = t.state && !(t.state === "pr-open" && t.prSettled) ? PAST[t.state] : void 0;
1751
+ const named = [
1752
+ ...byStory,
1753
+ ...sameRepo.length ? [`${sameRepo.join(", ")} (same repo)`] : []
1754
+ ].join(", ");
1755
+ const why = waitsFor.length ? `it waits for ${waitsFor.length === 1 ? "task" : "tasks"} ${named} to be done first` : past;
1756
+ out.push({
1757
+ id: t.id,
1758
+ repo: t.repo,
1759
+ ready: !why,
1760
+ waitsFor,
1761
+ ...sameRepo.length ? { sameRepo } : {},
1762
+ ...why ? { why } : {}
1763
+ });
1764
+ }
1765
+ return { tasks: out, ready: out.filter((t) => t.ready).map((t) => t.id) };
1766
+ }
1767
+ async function readyTasksOf(root, change, o = {}) {
1768
+ const files = await readChangeFiles(changeFolder(root, change));
1769
+ const tasks = [];
1770
+ for (const t of readChangeTasks(files)) {
1771
+ const r = await readTaskRun(root, change, t.id).catch(() => void 0);
1772
+ const pr = o.prStates?.get(t.id);
1773
+ tasks.push({
1774
+ id: t.id,
1775
+ repo: t.repo,
1776
+ done: t.done,
1777
+ stories: t.stories,
1778
+ ...t.parallel ? { parallel: true } : {},
1779
+ ...r ? { state: r.state } : {},
1780
+ ...pr === "merged" || pr === "closed" ? { prSettled: pr } : {}
1781
+ });
1782
+ }
1783
+ return planReadyTasks({
1784
+ tasks,
1785
+ stories: readStoryIndex(files),
1786
+ ...o.repoOrder ? { repoOrder: o.repoOrder } : {}
1787
+ });
1788
+ }
1789
+ function needsPerson(r) {
1790
+ if (r.state === "failed")
1791
+ return r.notes.at(-1) ?? `the run for task ${r.task} failed`;
1792
+ if (r.state === "needs-person")
1793
+ return r.notes.at(-1) ?? `task ${r.task} needs a person`;
1794
+ if (r.state === "waiting")
1795
+ return `task ${r.task} is with your assistant: Done on its run first`;
1796
+ if (r.state === "unverified")
1797
+ return `task ${r.task} is drafted but ${r.repo} has no build command, so nothing was built`;
1798
+ if (r.level === "pr" && r.state === "built")
1799
+ return r.notes.at(-1) ?? `task ${r.task}'s PR wasn't raised`;
1800
+ return void 0;
1801
+ }
1802
+ async function runReadyTasks(root, change, o) {
1803
+ const plan = await readyTasksOf(root, change, {
1804
+ ...o.repoOrder ? { repoOrder: o.repoOrder } : {},
1805
+ ...o.prStates ? { prStates: o.prStates } : {}
1806
+ });
1807
+ const planned = o.only ? plan.ready.filter((id) => o.only.includes(id)) : plan.ready;
1808
+ const repoOf = new Map(plan.tasks.map((t) => [t.id, t.repo]));
1809
+ if (o.dryRun) {
1810
+ const previews = [];
1811
+ for (const task of planned)
1812
+ try {
1813
+ previews.push(await previewTaskRun(root, change, task, {
1814
+ ...o.level ? { level: o.level } : {},
1815
+ ...o.gitHost ? { gitHost: o.gitHost } : {}
1816
+ }));
1817
+ } catch (e) {
1818
+ previews.push({ task, refused: e.message });
1819
+ }
1820
+ return { planned, ran: [], previews };
1821
+ }
1822
+ const { only: _only, before, levelFor, progress, cancelled, repoOrder: _r, prStates: _p, dryRun: _d, ...run } = o;
1823
+ const ran = [];
1824
+ for (const [i, task] of planned.entries()) {
1825
+ const repo = repoOf.get(task) ?? "";
1826
+ if (cancelled?.())
1827
+ return { planned, ran, stopped: { task, why: "the run was cancelled" } };
1828
+ progress?.(i, planned.length, `Task ${task} on ${repo}`);
1829
+ const refusedBefore = await before?.(task);
1830
+ if (refusedBefore) {
1831
+ ran.push({ task, repo, refused: refusedBefore });
1832
+ return { planned, ran, stopped: { task, why: refusedBefore } };
1833
+ }
1834
+ let record;
1835
+ try {
1836
+ const level = run.level ?? await levelFor?.(task);
1837
+ record = await runTask(root, change, task, { ...run, ...level ? { level } : {} });
1838
+ } catch (e) {
1839
+ const why2 = e.message;
1840
+ ran.push({ task, repo, refused: why2 });
1841
+ return { planned, ran, stopped: { task, why: why2 } };
1842
+ }
1843
+ ran.push({ task, repo, record });
1844
+ const why = needsPerson(record);
1845
+ if (why)
1846
+ return { planned, ran, stopped: { task, why } };
1847
+ }
1848
+ progress?.(planned.length, planned.length, "Done");
1849
+ return { planned, ran };
1850
+ }
1851
+
1185
1852
  export {
1186
1853
  FILE_FORMAT,
1187
1854
  refusedPath,
1188
1855
  parseAnswer,
1189
1856
  applyChanges,
1857
+ KEEP_OUTPUT,
1858
+ tail,
1859
+ stopRequested,
1860
+ stopSignal,
1861
+ stopAllBuilds,
1862
+ SIGNAL_GRACE_MS,
1863
+ holdSignals,
1864
+ STOP_GRACE_MS,
1190
1865
  shellCommandRunner,
1191
1866
  TaskRunState,
1192
1867
  RunLevel,
@@ -1196,6 +1871,8 @@ export {
1196
1871
  saveTaskRun,
1197
1872
  readTaskRun,
1198
1873
  saveRunSummary,
1874
+ taskRunOfRun,
1875
+ findTaskRunOfRun,
1199
1876
  findWaitingTaskRun,
1200
1877
  TASK_RUNNER,
1201
1878
  taskRunnerAgent,
@@ -1211,7 +1888,16 @@ export {
1211
1888
  conformanceOf,
1212
1889
  conformAfterBuild,
1213
1890
  TaskRunError,
1891
+ taskPrState,
1892
+ settleTaskPr,
1893
+ previewTaskRun,
1894
+ cancelTaskRunWait,
1214
1895
  runTask,
1215
1896
  completeTaskHandoff,
1216
- isTaskRun
1897
+ raiseTaskPr,
1898
+ isTaskRun,
1899
+ planReadyTasks,
1900
+ readyTasksOf,
1901
+ needsPerson,
1902
+ runReadyTasks
1217
1903
  };