@maycombhq/atticus 2.0.0-beta.21 → 2.0.0-beta.23

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 (299) hide show
  1. package/dist/bin.js +12 -7
  2. package/dist/{chunk-BXBCMGIQ.js → chunk-24OXGB3P.js} +1 -1
  3. package/dist/{chunk-PRW3HXM3.js → chunk-27AJAKX7.js} +29 -5
  4. package/dist/{chunk-PQH56YF6.js → chunk-2QPJ4E4Z.js} +6 -4
  5. package/dist/{chunk-7CZHUZBL.js → chunk-2UMSHYCB.js} +38 -27
  6. package/dist/{chunk-2V2ZPQQU.js → chunk-2WUTCPPJ.js} +9 -7
  7. package/dist/{chunk-YF5DZCPN.js → chunk-2YSD6E6V.js} +38 -34
  8. package/dist/{chunk-QHJJMZLX.js → chunk-3424TLIJ.js} +204 -97
  9. package/dist/{chunk-62DLJSLV.js → chunk-3ASYYMOX.js} +10 -8
  10. package/dist/{chunk-XOL3GFW2.js → chunk-3EWKO457.js} +111 -43
  11. package/dist/{chunk-G5YJBNDZ.js → chunk-3EX5HCZD.js} +8 -6
  12. package/dist/{chunk-WFU77T3A.js → chunk-3MYBT62N.js} +5 -5
  13. package/dist/{chunk-6EXDI5GI.js → chunk-3UHV76UD.js} +72 -41
  14. package/dist/{chunk-LU4MQVAL.js → chunk-3ZA3AK2D.js} +11 -10
  15. package/dist/{chunk-P2VBXX7N.js → chunk-4ETO2FXE.js} +11 -9
  16. package/dist/{chunk-GK4PYVEC.js → chunk-4FLNCK6Q.js} +31 -14
  17. package/dist/{chunk-64AADMTW.js → chunk-4GAB5ZOZ.js} +943 -336
  18. package/dist/{chunk-FLVZFTLQ.js → chunk-4MYHETJ3.js} +20 -18
  19. package/dist/chunk-4R3TO4VL.js +94 -0
  20. package/dist/{chunk-7EFILTCI.js → chunk-5F7GFN2F.js} +28 -0
  21. package/dist/chunk-5MYE42AP.js +43 -0
  22. package/dist/chunk-5O7U67UA.js +57 -0
  23. package/dist/{chunk-MZGRDONP.js → chunk-5OXKCH4Z.js} +54 -37
  24. package/dist/{chunk-PUEWDYGP.js → chunk-5VTIWEWP.js} +3 -2
  25. package/dist/{chunk-3G6S7LAA.js → chunk-63NFFAGI.js} +14 -12
  26. package/dist/{chunk-LIJJCBQI.js → chunk-66AQ2JIU.js} +15 -16
  27. package/dist/chunk-6JVPBGQG.js +50 -0
  28. package/dist/{chunk-WAZEXEJH.js → chunk-6NCPLIIP.js} +167 -2
  29. package/dist/{chunk-D57MFJPP.js → chunk-6NN36LVK.js} +3 -3
  30. package/dist/{chunk-G6IQYM25.js → chunk-6R5VHR3N.js} +8 -6
  31. package/dist/{chunk-Z3QFH7YG.js → chunk-6RQOINER.js} +5 -4
  32. package/dist/{chunk-TQTRWENN.js → chunk-6RYB3EET.js} +14 -12
  33. package/dist/{chunk-FYBF5MB2.js → chunk-6SNTHK43.js} +13 -11
  34. package/dist/{chunk-FRHD22WM.js → chunk-6Y4FXOMI.js} +27 -23
  35. package/dist/{chunk-LWDNKE4X.js → chunk-6ZFF7XSL.js} +6 -6
  36. package/dist/{chunk-FQ6WH6PB.js → chunk-6ZSYLZ7O.js} +6 -4
  37. package/dist/{chunk-LQH4P45K.js → chunk-73CC73I4.js} +1 -1
  38. package/dist/{chunk-Q46QDDMF.js → chunk-73GFVW2L.js} +14 -9
  39. package/dist/{chunk-QKMRFNMA.js → chunk-73V2OONC.js} +17 -15
  40. package/dist/{chunk-FVMPFCDS.js → chunk-75OMRKY2.js} +4 -4
  41. package/dist/{chunk-XEQCQO66.js → chunk-7IFTSAHP.js} +10 -8
  42. package/dist/{chunk-4FOAYLI4.js → chunk-7K5OH45X.js} +139 -17
  43. package/dist/{chunk-3SSTNB4E.js → chunk-7NQAGYK4.js} +34 -14
  44. package/dist/{chunk-URIKUID7.js → chunk-7XWJ6ED2.js} +14 -12
  45. package/dist/{chunk-TYD6I4CZ.js → chunk-7XYLNQFO.js} +7 -7
  46. package/dist/{chunk-BPPU3PW7.js → chunk-7YQBDE5W.js} +10 -4
  47. package/dist/{chunk-CPK7QSBG.js → chunk-A4JXVO5O.js} +8 -6
  48. package/dist/{chunk-YJ3TBOTK.js → chunk-AFBCNZRZ.js} +29 -26
  49. package/dist/{chunk-TW3KOSQK.js → chunk-AGOLEMHK.js} +13 -13
  50. package/dist/chunk-AHFQHPFF.js +49 -0
  51. package/dist/{chunk-NTAVOWYD.js → chunk-APDN6UOR.js} +118 -18
  52. package/dist/chunk-AQRUQNFB.js +322 -0
  53. package/dist/{chunk-KYZMRSPS.js → chunk-AVMWESFZ.js} +6 -3
  54. package/dist/chunk-B4AEL3RF.js +1011 -0
  55. package/dist/{chunk-GS76RXQE.js → chunk-BAY4K55I.js} +15 -12
  56. package/dist/{chunk-4CLK2RJO.js → chunk-BFL4PO64.js} +9 -7
  57. package/dist/{chunk-GCS3FZOB.js → chunk-BFMTGNAQ.js} +15 -4
  58. package/dist/{chunk-YBOVNMHC.js → chunk-BP33TBLI.js} +29 -16
  59. package/dist/chunk-BQGDB6ZL.js +7342 -0
  60. package/dist/{chunk-BXZ4PKIT.js → chunk-BUUJ7N4O.js} +15 -11
  61. package/dist/{chunk-HGHRSF3E.js → chunk-BYBU2EIB.js} +38 -19
  62. package/dist/{chunk-TQ4TG57V.js → chunk-C6ORPLCN.js} +4 -4
  63. package/dist/{chunk-HWD4JPU7.js → chunk-CA3DKO4D.js} +27 -3
  64. package/dist/{chunk-YTTNTQOX.js → chunk-CFUDAW3R.js} +17 -15
  65. package/dist/{chunk-OPQZISG3.js → chunk-CJEBRJBS.js} +5 -4
  66. package/dist/{chunk-RALY3E5U.js → chunk-CKVBDVIW.js} +6 -4
  67. package/dist/{chunk-DUPZPDCJ.js → chunk-CMB7FXHJ.js} +42 -16
  68. package/dist/{chunk-YIGBJNH4.js → chunk-CQ7RF4QN.js} +5 -4
  69. package/dist/{chunk-YAH5IQAL.js → chunk-CS4BMJLL.js} +6 -5
  70. package/dist/{chunk-455SYIIE.js → chunk-CT6NINGR.js} +5 -3
  71. package/dist/chunk-CXTG6AWM.js +40 -0
  72. package/dist/{chunk-JYMIB5XY.js → chunk-CZ2ZS7AT.js} +170 -45
  73. package/dist/{chunk-K332XW7K.js → chunk-D6BVLTDV.js} +100 -33
  74. package/dist/chunk-DCRYOMTM.js +37 -0
  75. package/dist/{chunk-G6X4YL4D.js → chunk-DHKDH464.js} +5 -4
  76. package/dist/{chunk-JRVNXLZP.js → chunk-DSHRA72L.js} +14 -12
  77. package/dist/{chunk-LNWEWYCM.js → chunk-E3PDCQRH.js} +2813 -621
  78. package/dist/chunk-EHXDB3SA.js +25 -0
  79. package/dist/{chunk-EZIN5E5B.js → chunk-EMVZIOOD.js} +11 -9
  80. package/dist/{chunk-IFHDV6KQ.js → chunk-ENH6Z4TY.js} +21 -20
  81. package/dist/{chunk-YKTE2RU3.js → chunk-EVTI22PQ.js} +7 -2
  82. package/dist/{chunk-2D7PWSOK.js → chunk-EXJZBL5N.js} +22 -20
  83. package/dist/{chunk-77O633L5.js → chunk-FBCVC2GJ.js} +14 -12
  84. package/dist/{chunk-HS4BGB4G.js → chunk-FJVUHQPS.js} +37 -24
  85. package/dist/{chunk-4KNOJC6J.js → chunk-FL344WQD.js} +270 -221
  86. package/dist/{chunk-GNWAOVFQ.js → chunk-FMJYKLDY.js} +5 -5
  87. package/dist/chunk-FMR2D3AZ.js +41 -0
  88. package/dist/{chunk-PAINMSID.js → chunk-FPBUTZI5.js} +3102 -7859
  89. package/dist/{chunk-ZOSD4FXV.js → chunk-FQVSLBTV.js} +3 -3
  90. package/dist/chunk-FRAEQVYN.js +52 -0
  91. package/dist/{chunk-UDXWIPCB.js → chunk-FRCOKUU4.js} +9 -7
  92. package/dist/{chunk-UYLBRRFK.js → chunk-FSN6JFXB.js} +129 -27
  93. package/dist/{chunk-BC6KKZ67.js → chunk-FTLYXMHA.js} +9 -6
  94. package/dist/{chunk-FAJDKNSD.js → chunk-FVYZ4Y52.js} +16 -3
  95. package/dist/{chunk-TWQRPM76.js → chunk-G6EFXTUO.js} +23 -16
  96. package/dist/{chunk-ZFQMMU5Y.js → chunk-G7VBMOZH.js} +6 -2
  97. package/dist/{chunk-VYOCPTUB.js → chunk-GM4EFHUX.js} +17 -15
  98. package/dist/chunk-GOZLTLRY.js +232 -0
  99. package/dist/chunk-GPNMSX5H.js +36 -0
  100. package/dist/{chunk-OZJ3JLYI.js → chunk-GUGQSIQM.js} +10 -9
  101. package/dist/chunk-H4VO6WG3.js +10 -0
  102. package/dist/{chunk-QJCJOMW3.js → chunk-HJQVUOUL.js} +16 -18
  103. package/dist/chunk-HMWJWYE2.js +31 -0
  104. package/dist/{chunk-4TKVU2I2.js → chunk-HMZ2DRBC.js} +2 -2
  105. package/dist/{chunk-HK3VOHIE.js → chunk-HRS5O7VV.js} +609 -171
  106. package/dist/{chunk-7FUIVIBG.js → chunk-HUGQZQ2K.js} +13 -8
  107. package/dist/{chunk-QOOEYDNJ.js → chunk-ICBGVVRI.js} +10 -10
  108. package/dist/{chunk-XG4WJHLQ.js → chunk-IGOE6KPG.js} +49 -20
  109. package/dist/{chunk-4SLBCEPD.js → chunk-IKAW2KJU.js} +95 -64
  110. package/dist/{chunk-L4CESDPB.js → chunk-IMZFOMVX.js} +1 -1
  111. package/dist/{chunk-XQ3YZ4QW.js → chunk-ISG547IS.js} +24 -22
  112. package/dist/{chunk-BA2HUMDN.js → chunk-IYNCSYU4.js} +5 -2
  113. package/dist/{chunk-UEVZZZU2.js → chunk-J25YOUIZ.js} +5 -4
  114. package/dist/chunk-J2CDBSFP.js +69 -0
  115. package/dist/chunk-J2H7YG3K.js +34 -0
  116. package/dist/{chunk-O4IKAIZ2.js → chunk-J32IPTVQ.js} +28 -22
  117. package/dist/{chunk-3AQ2R5NL.js → chunk-J4CPHJUL.js} +11 -9
  118. package/dist/{chunk-VUVGX5L2.js → chunk-JFFKHIM6.js} +1 -1
  119. package/dist/chunk-JMEWJPF2.js +384 -0
  120. package/dist/{chunk-LO3BLMVG.js → chunk-JSP73CDW.js} +13 -5
  121. package/dist/chunk-JXAKYP45.js +63 -0
  122. package/dist/{chunk-PNQLCRSS.js → chunk-K4OXYSGS.js} +7 -5
  123. package/dist/{chunk-3J5I7ZOU.js → chunk-K4RNVDR4.js} +4 -4
  124. package/dist/{chunk-LEBR5NHW.js → chunk-K757G7AD.js} +18 -15
  125. package/dist/{chunk-AHW3RY3X.js → chunk-KDJ2YKAZ.js} +8 -7
  126. package/dist/{chunk-4WXRPQDE.js → chunk-KT6Q44CA.js} +178 -31
  127. package/dist/{chunk-EFCQSMXB.js → chunk-KTNOBTV5.js} +10 -4
  128. package/dist/chunk-KVVNAUIG.js +66 -0
  129. package/dist/{chunk-26Z7OGQW.js → chunk-L3RCQWVQ.js} +64 -31
  130. package/dist/{chunk-3WON4G3M.js → chunk-LHH5KG5A.js} +21 -6
  131. package/dist/{chunk-NCB6656Z.js → chunk-LM45GMMB.js} +11 -8
  132. package/dist/{chunk-2HH3UCZM.js → chunk-LO6Y7OTG.js} +5 -4
  133. package/dist/{chunk-36RY7ZXM.js → chunk-LPDTDFKA.js} +88 -26
  134. package/dist/{chunk-67BP7HXL.js → chunk-LSHSDC6S.js} +19 -17
  135. package/dist/{chunk-GDZB57IU.js → chunk-LZVHP7CT.js} +7 -5
  136. package/dist/{chunk-GC2VK3JT.js → chunk-M3EOFSPY.js} +28 -25
  137. package/dist/chunk-MBYYQHPM.js +31 -0
  138. package/dist/{chunk-HXXP2NTE.js → chunk-MEJ63LDG.js} +35 -12
  139. package/dist/{chunk-2UCRFXFZ.js → chunk-MH2ZK67S.js} +13 -11
  140. package/dist/{chunk-BEPCKDCP.js → chunk-MLGWSBF4.js} +5447 -4165
  141. package/dist/{chunk-NRQ65LTQ.js → chunk-MNIPPBCT.js} +6 -5
  142. package/dist/{chunk-3LZ6P5EP.js → chunk-MUWDB4R4.js} +1 -1
  143. package/dist/{chunk-KDAHXQIE.js → chunk-MWF4PXQR.js} +38 -34
  144. package/dist/{chunk-WNTIOBOG.js → chunk-N2YX6JMY.js} +20 -18
  145. package/dist/{chunk-FY3QIRTL.js → chunk-NAPHHWW2.js} +3 -3
  146. package/dist/{chunk-D2WSW4GU.js → chunk-NC755EKE.js} +8 -2
  147. package/dist/{chunk-BA3U7ALS.js → chunk-NCLZJCXA.js} +26 -22
  148. package/dist/chunk-NKJDO4UC.js +39 -0
  149. package/dist/chunk-NOYOFYAJ.js +59 -0
  150. package/dist/{chunk-LPV6T362.js → chunk-NROSSWMS.js} +8 -6
  151. package/dist/{chunk-ZKYRDV7A.js → chunk-OBUB3H72.js} +14 -12
  152. package/dist/{chunk-ZQLRCA2V.js → chunk-OCQWSFJD.js} +10 -8
  153. package/dist/{chunk-42EPP7OB.js → chunk-OK6JGLE7.js} +14 -12
  154. package/dist/{chunk-MD7KUEJ3.js → chunk-OPWGPQ2N.js} +9 -7
  155. package/dist/{chunk-SH27FYPF.js → chunk-OSYHGGL3.js} +20 -18
  156. package/dist/chunk-OXXZZ7HQ.js +50 -0
  157. package/dist/chunk-P22EPEBR.js +57 -0
  158. package/dist/chunk-P4YUT7W7.js +45 -0
  159. package/dist/chunk-PCCLVIDP.js +217 -0
  160. package/dist/chunk-PFULMC2D.js +48 -0
  161. package/dist/chunk-PGAMTKBI.js +16 -0
  162. package/dist/{chunk-Y6K3CJBU.js → chunk-PNSSE7JM.js} +152 -29
  163. package/dist/{chunk-3UTYPHN3.js → chunk-POEMZECG.js} +1 -1
  164. package/dist/{chunk-GSGIZ2OO.js → chunk-POSDBRKV.js} +27 -24
  165. package/dist/{chunk-YZDZOEV4.js → chunk-POSZFDSQ.js} +3 -3
  166. package/dist/{chunk-7AB6OWTV.js → chunk-PQQKZPBU.js} +31 -25
  167. package/dist/{chunk-K6JB6XTX.js → chunk-PRMUQUWN.js} +20 -15
  168. package/dist/{chunk-ICHDSWEY.js → chunk-PT4USJNN.js} +10 -8
  169. package/dist/{chunk-265LWMOA.js → chunk-PWXB5GBB.js} +18 -16
  170. package/dist/{chunk-DCWN3HFV.js → chunk-PXMV2LLY.js} +6 -5
  171. package/dist/{chunk-THN26IEF.js → chunk-PXW2RZGQ.js} +4 -1
  172. package/dist/{chunk-CG6CULP6.js → chunk-Q367UGYN.js} +7 -5
  173. package/dist/chunk-QA4O2GGC.js +265 -0
  174. package/dist/{chunk-7FHNFCHK.js → chunk-QAVRDONY.js} +225 -375
  175. package/dist/{chunk-VAMHOHUU.js → chunk-QBMLBCQH.js} +16 -13
  176. package/dist/{chunk-WEOMJHPV.js → chunk-QLQIFJP3.js} +18 -15
  177. package/dist/{chunk-UZACEG2C.js → chunk-QLWDIKO4.js} +84 -27
  178. package/dist/{chunk-ZZFHLRQW.js → chunk-QVVGJVVX.js} +18 -16
  179. package/dist/{chunk-WIWTJMMR.js → chunk-QX4MAMUG.js} +23 -21
  180. package/dist/{chunk-JYTIHTIK.js → chunk-QZWRPQTI.js} +6 -6
  181. package/dist/{chunk-5WERBKLE.js → chunk-RCG5IAAT.js} +55 -227
  182. package/dist/chunk-RGZFOKVO.js +106 -0
  183. package/dist/{chunk-C7CEK3KW.js → chunk-RLZFL76D.js} +7 -5
  184. package/dist/{chunk-PTQ3ZALZ.js → chunk-RPQITJNX.js} +14 -12
  185. package/dist/{chunk-JS6WMC3J.js → chunk-RQQYEDAC.js} +10 -8
  186. package/dist/chunk-RQSLHZ7T.js +36 -0
  187. package/dist/{chunk-API5TOUB.js → chunk-RRQUQAXP.js} +1 -1
  188. package/dist/chunk-RYQ4JCFT.js +65 -0
  189. package/dist/chunk-S35DIFJH.js +38 -0
  190. package/dist/{chunk-YWAKDNMH.js → chunk-S5GSQPIN.js} +70 -19
  191. package/dist/chunk-S5XFO4GY.js +42 -0
  192. package/dist/chunk-S6LW2RRC.js +25 -0
  193. package/dist/{chunk-TSM6T6GQ.js → chunk-S6W3D5GS.js} +25 -23
  194. package/dist/{chunk-5PV7S2ZP.js → chunk-S7GUZ45I.js} +4 -3
  195. package/dist/{chunk-MMZUN7ZO.js → chunk-SCT4CEF7.js} +22 -20
  196. package/dist/{chunk-AOCWFA7T.js → chunk-SK5RVP7X.js} +8 -8
  197. package/dist/chunk-SRTUETF5.js +50 -0
  198. package/dist/{chunk-E57AOFYN.js → chunk-SVGGMBJI.js} +15 -13
  199. package/dist/{chunk-OV57AHCG.js → chunk-SWNKO4C4.js} +35 -26
  200. package/dist/chunk-SYP56T2D.js +65 -0
  201. package/dist/{chunk-5CMPVC3N.js → chunk-T33TPFGI.js} +25 -23
  202. package/dist/{chunk-Q26LDIUV.js → chunk-T3FNG2H2.js} +18 -15
  203. package/dist/{chunk-UFE36WJH.js → chunk-T6MAWL5P.js} +6 -4
  204. package/dist/{chunk-LN77DXBM.js → chunk-T6MX7WXM.js} +14 -11
  205. package/dist/{chunk-WCEF7H6L.js → chunk-T7IIEDA3.js} +10 -7
  206. package/dist/{chunk-2HTEMYTB.js → chunk-TABH4RBZ.js} +163 -84
  207. package/dist/{chunk-YO2PKTXT.js → chunk-TDSCKP3V.js} +5 -3
  208. package/dist/{chunk-BVWWPBKD.js → chunk-TMFMXQEN.js} +23 -21
  209. package/dist/{chunk-4B6BT75O.js → chunk-TNCF26JK.js} +83 -26
  210. package/dist/{chunk-75ASLHBT.js → chunk-TUM52SOE.js} +21 -15
  211. package/dist/{chunk-ZLS6PU2O.js → chunk-TV56QLLV.js} +4 -3
  212. package/dist/{chunk-JKIGL3XQ.js → chunk-TVQSXWWV.js} +36 -33
  213. package/dist/{chunk-OKUB6RJA.js → chunk-TXNSQ24G.js} +14 -11
  214. package/dist/{chunk-4CYTJ5TZ.js → chunk-UALE35WK.js} +4 -3
  215. package/dist/{chunk-CNU5Q2D5.js → chunk-UJF7PIMP.js} +35 -27
  216. package/dist/chunk-UMCDODBX.js +53 -0
  217. package/dist/{chunk-M6D2U3YM.js → chunk-UTPIFH4I.js} +4 -3
  218. package/dist/{chunk-COYN3UAN.js → chunk-V7PEHQZU.js} +95 -26
  219. package/dist/{chunk-U3NBK4EU.js → chunk-VJFXQISZ.js} +8 -8
  220. package/dist/chunk-VJZI6KFP.js +47 -0
  221. package/dist/{chunk-ECLXZWAG.js → chunk-VK7WYA64.js} +14 -12
  222. package/dist/{chunk-DDJPM5YX.js → chunk-VKTXFFOZ.js} +5 -3
  223. package/dist/{chunk-LIRVU3HW.js → chunk-VNIN7Q6Q.js} +32 -20
  224. package/dist/{chunk-7QPCGTPS.js → chunk-VRO7KSGL.js} +24 -21
  225. package/dist/{chunk-AZDITBCT.js → chunk-W3AZE7LN.js} +3 -3
  226. package/dist/chunk-W4TNCEI4.js +44 -0
  227. package/dist/{chunk-S2RY3BOD.js → chunk-W5VYJWOG.js} +31 -21
  228. package/dist/{chunk-X2CUXTWU.js → chunk-WF3RCZNO.js} +6 -6
  229. package/dist/{chunk-4D62IZII.js → chunk-WOMQQWTO.js} +7 -5
  230. package/dist/{chunk-UF4UHCIG.js → chunk-WXVTYLVH.js} +6 -4
  231. package/dist/{chunk-EDZ45WIR.js → chunk-WYGTSZHR.js} +18 -14
  232. package/dist/{chunk-HSZMPH5T.js → chunk-WZYM5M4U.js} +592 -133
  233. package/dist/chunk-X5ZHDSLU.js +49 -0
  234. package/dist/chunk-X7XVMYIL.js +5 -0
  235. package/dist/chunk-X7ZKNSK4.js +41 -0
  236. package/dist/{chunk-QE527XRY.js → chunk-XAPXKFIS.js} +26 -23
  237. package/dist/{chunk-HE3HRDNZ.js → chunk-XF6TSZW2.js} +2 -2
  238. package/dist/{chunk-GX74YWR7.js → chunk-XFDDJQT7.js} +16 -14
  239. package/dist/{chunk-5QUOPNLN.js → chunk-XGO74QWR.js} +4 -4
  240. package/dist/chunk-XLZAAAPX.js +43 -0
  241. package/dist/{chunk-NYOABJNK.js → chunk-XMUWPA56.js} +26 -24
  242. package/dist/{chunk-M4H2UG4R.js → chunk-XNFM6WUK.js} +19 -16
  243. package/dist/{chunk-TGVVUKTG.js → chunk-XOMEQVLK.js} +2 -2
  244. package/dist/{chunk-A2G72UI4.js → chunk-Y2H7X3VI.js} +7 -7
  245. package/dist/{chunk-37WE3LS2.js → chunk-Y5IE6YKD.js} +5 -3
  246. package/dist/{chunk-IFFRGC3N.js → chunk-YD3KTW4H.js} +14 -12
  247. package/dist/{chunk-75ZOZFGH.js → chunk-YDCOZOER.js} +20 -18
  248. package/dist/{chunk-6DQYZU2W.js → chunk-YERAAEO4.js} +20 -20
  249. package/dist/{chunk-J5AAZ6OJ.js → chunk-YHMPDIRG.js} +5 -4
  250. package/dist/{chunk-CNPY4NTZ.js → chunk-Z2D4P3R3.js} +15 -13
  251. package/dist/{chunk-E2VXBSIR.js → chunk-Z3H27OMV.js} +6 -4
  252. package/dist/{chunk-AE4P57KK.js → chunk-Z3RAFU5B.js} +17 -15
  253. package/dist/{chunk-RA33EYS4.js → chunk-ZENHDXRZ.js} +23 -17
  254. package/dist/{chunk-TJH2EUAZ.js → chunk-ZFHFC74B.js} +20 -18
  255. package/dist/{chunk-4PYC2UGC.js → chunk-ZGULQAPZ.js} +7 -4
  256. package/dist/{chunk-4SKZDI62.js → chunk-ZIUFM5HT.js} +3 -1
  257. package/dist/{chunk-JAABT3DA.js → chunk-ZJGCGKA4.js} +4 -3
  258. package/dist/{chunk-GIFMBCTW.js → chunk-ZLQEQR3D.js} +10 -8
  259. package/dist/{chunk-I2A3Z5AK.js → chunk-ZNHQGG7J.js} +7 -7
  260. package/dist/{chunk-5TDRBOAV.js → chunk-ZNTTW4IT.js} +7 -5
  261. package/dist/{chunk-ZX7235KR.js → chunk-ZR6J4QFQ.js} +44 -383
  262. package/dist/{chunk-F6H2RJ3N.js → chunk-ZSNOILL5.js} +1 -1
  263. package/dist/files.json +585 -509
  264. package/dist/packages/sdk/npm/index.js +1491 -110
  265. package/dist/packages/ui/dist/assets/{elk-api-CowTnhEJ.js → elk-api-DxfAMOJq.js} +2 -2
  266. package/dist/packages/ui/dist/assets/index-Buit9TfP.js +62 -0
  267. package/dist/packages/ui/dist/assets/index-Jyc9dFeZ.css +1 -0
  268. package/dist/packages/ui/dist/index.html +3 -2
  269. package/package.json +3 -3
  270. package/dist/chunk-26WBY34Y.js +0 -37
  271. package/dist/chunk-3TD6S4TC.js +0 -49
  272. package/dist/chunk-5G7ILBUG.js +0 -66
  273. package/dist/chunk-6FNAKCET.js +0 -38
  274. package/dist/chunk-AM6FP6JW.js +0 -43
  275. package/dist/chunk-CFH3QH26.js +0 -29
  276. package/dist/chunk-CLD4RVTA.js +0 -45
  277. package/dist/chunk-D5KV5PSK.js +0 -45
  278. package/dist/chunk-D5MASS4I.js +0 -48
  279. package/dist/chunk-DN4A2Q2O.js +0 -57
  280. package/dist/chunk-FGAGWYZB.js +0 -47
  281. package/dist/chunk-GUYL7RMK.js +0 -60
  282. package/dist/chunk-J2O4LFVI.js +0 -80
  283. package/dist/chunk-JITJP64K.js +0 -34
  284. package/dist/chunk-JRXHON4E.js +0 -41
  285. package/dist/chunk-JU4QNJSR.js +0 -62
  286. package/dist/chunk-N3RUGTGV.js +0 -39
  287. package/dist/chunk-ODMBPDDV.js +0 -298
  288. package/dist/chunk-OMPENNWY.js +0 -39
  289. package/dist/chunk-PKJAV25S.js +0 -43
  290. package/dist/chunk-PRTBPXYA.js +0 -50
  291. package/dist/chunk-PRZ7KMWL.js +0 -42
  292. package/dist/chunk-QSIDVFB7.js +0 -45
  293. package/dist/chunk-SB5GRUFM.js +0 -54
  294. package/dist/chunk-T74WQQGS.js +0 -40
  295. package/dist/chunk-X7EMBM5X.js +0 -34
  296. package/dist/chunk-Z6VLC5X4.js +0 -36
  297. package/dist/chunk-ZPDDEVQX.js +0 -47
  298. package/dist/packages/ui/dist/assets/index-Dw20IRhQ.css +0 -1
  299. package/dist/packages/ui/dist/assets/index-z2wv0d5d.js +0 -47
@@ -6,24 +6,26 @@ var __atticusPath = (p) => __atticusFileURLToPath(__atticusUrl(p));
6
6
  import {
7
7
  STORE_MARKER,
8
8
  addStoreReference,
9
+ archiveRefuses,
10
+ blockingIssues,
9
11
  readStoreMarker,
10
12
  readStoreVia
11
- } from "./chunk-D2WSW4GU.js";
13
+ } from "./chunk-NC755EKE.js";
12
14
  import {
13
15
  AdapterError,
14
16
  mergeBlockers,
15
17
  requireApproval
16
- } from "./chunk-4KNOJC6J.js";
18
+ } from "./chunk-FL344WQD.js";
17
19
  import {
18
20
  outsideProfileSystems,
19
21
  readCatalog
20
- } from "./chunk-4SLBCEPD.js";
22
+ } from "./chunk-IKAW2KJU.js";
21
23
  import {
22
24
  baseClonePath,
23
25
  createGit,
24
26
  readChangeRepos,
25
27
  writeChangeRepos
26
- } from "./chunk-XOL3GFW2.js";
28
+ } from "./chunk-3EWKO457.js";
27
29
  import {
28
30
  ChangeArtifact,
29
31
  ChangeLifecycle,
@@ -40,23 +42,28 @@ import {
40
42
  external_exports,
41
43
  fastGateOf,
42
44
  fastPathOf,
45
+ handleKey,
43
46
  handoffBranch,
44
47
  isAssistantResolution,
45
48
  listQuestions,
46
49
  loadOpenSpecSettings,
47
50
  loadOrgPack,
51
+ nextOwnersOf,
48
52
  openspecDirOf,
49
53
  orgPackDirOf,
50
54
  questionsRelOf,
51
55
  readFlow,
52
56
  readQuestion,
53
- require_dist,
54
57
  specFlowFiles,
58
+ taskBranch,
55
59
  toCanonicalYaml
56
- } from "./chunk-PAINMSID.js";
60
+ } from "./chunk-FPBUTZI5.js";
57
61
  import {
58
62
  writeFileAtomic
59
- } from "./chunk-ODMBPDDV.js";
63
+ } from "./chunk-B4AEL3RF.js";
64
+ import {
65
+ require_dist
66
+ } from "./chunk-BQGDB6ZL.js";
60
67
  import {
61
68
  __toESM
62
69
  } from "./chunk-TV45EUGM.js";
@@ -628,22 +635,33 @@ function changeStore(root) {
628
635
  await writeFileAtomic(path, serializeChange(value));
629
636
  return path;
630
637
  },
638
+ listWithProblems,
631
639
  async list() {
632
- let names;
640
+ return (await listWithProblems()).changes;
641
+ }
642
+ };
643
+ async function listWithProblems() {
644
+ let names;
645
+ try {
646
+ names = (await readdir2(changesDirOf(root), { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== "archive" && RecordId.safeParse(d.name).success).map((d) => d.name).sort();
647
+ } catch {
648
+ return { changes: [], problems: [] };
649
+ }
650
+ const changes = [];
651
+ const problems = [];
652
+ for (const n of names) {
633
653
  try {
634
- names = (await readdir2(changesDirOf(root), { withFileTypes: true })).filter((d) => d.isDirectory() && d.name !== "archive" && RecordId.safeParse(d.name).success).map((d) => d.name).sort();
635
- } catch {
636
- return [];
637
- }
638
- const out = [];
639
- for (const n of names) {
640
654
  const c = await tryRead(n);
641
655
  if (c)
642
- out.push(c);
656
+ changes.push(c);
657
+ } catch (e) {
658
+ if (!(e instanceof ChangeFileError))
659
+ throw e;
660
+ problems.push({ file: e.path, message: e.message });
643
661
  }
644
- return out;
645
662
  }
646
- };
663
+ return { changes, problems };
664
+ }
647
665
  }
648
666
 
649
667
  // packages/changes/dist/codeowners.js
@@ -1650,8 +1668,19 @@ async function checkFindings(root, change, gates) {
1650
1668
  continue;
1651
1669
  const round2 = Math.max(...rounds);
1652
1670
  const dir = join7(base, String(round2));
1671
+ const files = (await readdir4(dir)).filter((f) => /\.ya?ml$/.test(f)).sort();
1672
+ const unfinished = {
1673
+ code: "round-unfinished",
1674
+ message: `Critic round ${round2} on the ${gate} gate didn't finish: run atticus critic ${change.id} --gate ${gate} again.`
1675
+ };
1676
+ let hasMeta = false;
1653
1677
  try {
1654
1678
  const meta = (0, import_yaml5.parse)(await readFile6(join7(dir, "meta", "round.yaml"), "utf8"));
1679
+ hasMeta = true;
1680
+ if (meta?.state === "in-progress") {
1681
+ reasons.push(unfinished);
1682
+ continue;
1683
+ }
1655
1684
  for (const l of meta?.lenses ?? [])
1656
1685
  if (l.state === "waiting")
1657
1686
  reasons.push({
@@ -1665,7 +1694,10 @@ async function checkFindings(root, change, gates) {
1665
1694
  });
1666
1695
  } catch {
1667
1696
  }
1668
- const files = (await readdir4(dir)).filter((f) => /\.ya?ml$/.test(f)).sort();
1697
+ if (!hasMeta && files.length === 0) {
1698
+ reasons.push(unfinished);
1699
+ continue;
1700
+ }
1669
1701
  for (const f of files) {
1670
1702
  const rel = `review/${gate}/${round2}/${f}`;
1671
1703
  let data;
@@ -1813,14 +1845,195 @@ function prApprovers(handoff2, approvers) {
1813
1845
  }
1814
1846
 
1815
1847
  // packages/changes/dist/assess.js
1816
- import { readFile as readFile9 } from "node:fs/promises";
1817
- import { join as join10 } from "node:path";
1848
+ import { readFile as readFile10 } from "node:fs/promises";
1849
+ import { join as join11 } from "node:path";
1818
1850
 
1819
1851
  // packages/changes/dist/release-prs.js
1852
+ var import_yaml8 = __toESM(require_dist(), 1);
1853
+ import { readFile as readFile9, writeFile } from "node:fs/promises";
1854
+ import { existsSync as existsSync2 } from "node:fs";
1855
+ import { join as join10 } from "node:path";
1856
+
1857
+ // packages/changes/dist/task-branches.js
1820
1858
  var import_yaml7 = __toESM(require_dist(), 1);
1821
- import { readFile as readFile8, writeFile } from "node:fs/promises";
1822
1859
  import { existsSync } from "node:fs";
1860
+ import { readdir as readdir5, readFile as readFile8 } from "node:fs/promises";
1823
1861
  import { join as join9 } from "node:path";
1862
+ var TASK_DECISION_FILE = "decision.yaml";
1863
+ var TaskDecision = external_exports.strictObject({
1864
+ schemaVersion: external_exports.literal(1),
1865
+ task: external_exports.string().min(1),
1866
+ branch: external_exports.string().min(1),
1867
+ /** The task branch's commit the person decided over. */
1868
+ commit: external_exports.string().regex(/^[0-9a-f]{7,64}$/),
1869
+ by: external_exports.string().min(1),
1870
+ at: external_exports.string().min(1),
1871
+ reason: external_exports.string().min(1)
1872
+ });
1873
+ async function taskRefs(root, id) {
1874
+ const dir = join9(changeFolder(root, id), "tasks");
1875
+ const out = [];
1876
+ for (const d of await readdir5(dir, { withFileTypes: true }).catch(() => [])) {
1877
+ if (!d.isDirectory())
1878
+ continue;
1879
+ const text = await readFile8(join9(dir, d.name, "task-run.yaml"), "utf8").catch(() => void 0);
1880
+ let y;
1881
+ try {
1882
+ y = text ? (0, import_yaml7.parse)(text) : void 0;
1883
+ } catch {
1884
+ y = void 0;
1885
+ }
1886
+ const task = String(y?.task ?? d.name);
1887
+ let branch;
1888
+ try {
1889
+ branch = typeof y?.branch === "string" ? y.branch : taskBranch(id, task);
1890
+ } catch {
1891
+ continue;
1892
+ }
1893
+ const pr = y?.pr;
1894
+ out.push({
1895
+ task,
1896
+ ...typeof y?.repo === "string" ? { repo: y.repo } : {},
1897
+ branch,
1898
+ ...typeof y?.state === "string" ? { state: y.state } : {},
1899
+ ...pr && typeof pr.number === "number" && typeof pr.url === "string" ? { pr: { number: pr.number, url: pr.url } } : {},
1900
+ ...y?.ticked ? { ticked: true } : {},
1901
+ ...typeof y?.commit === "string" && /^[0-9a-f]{7,64}$/.test(y.commit) ? { commit: y.commit } : {}
1902
+ });
1903
+ }
1904
+ return out;
1905
+ }
1906
+ function repoDir(root, r) {
1907
+ const dirs = [join9(root, r.worktree)];
1908
+ try {
1909
+ dirs.push(baseClonePath(root, r.id));
1910
+ } catch {
1911
+ }
1912
+ return dirs.find((d) => existsSync(d));
1913
+ }
1914
+ async function readTaskDecision(root, id, task) {
1915
+ const text = await readFile8(join9(changeFolder(root, id), "tasks", task, TASK_DECISION_FILE), "utf8").catch(() => void 0);
1916
+ if (text === void 0)
1917
+ return void 0;
1918
+ try {
1919
+ const r = TaskDecision.safeParse((0, import_yaml7.parse)(text));
1920
+ return r.success ? r.data : void 0;
1921
+ } catch {
1922
+ return void 0;
1923
+ }
1924
+ }
1925
+ async function unmergedTaskBranches(root, id, o = {}) {
1926
+ const cr = await readChangeRepos(root, id).catch(() => void 0);
1927
+ if (!cr?.repos.length)
1928
+ return [];
1929
+ const git = o.git ?? createGit({ timeoutMs: 3e4 });
1930
+ const remote = o.remote ?? "origin";
1931
+ const from = changeBranch(id);
1932
+ const records = await taskRefs(root, id);
1933
+ const out = [];
1934
+ const sha = (dir, ref) => git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], dir).then((x) => x.stdout.trim() || void 0, () => void 0);
1935
+ for (const r of cr.repos) {
1936
+ const dir = repoDir(root, r);
1937
+ if (!dir)
1938
+ continue;
1939
+ const listed = await git(["for-each-ref", "--format=%(refname)", `refs/heads/task/${id}/`], dir).then((x) => x.stdout.split(/\r?\n/).map((l) => l.trim().replace(/^refs\/heads\//, "")).filter(Boolean), () => []);
1940
+ const branches = /* @__PURE__ */ new Map();
1941
+ for (const b of listed) {
1942
+ const mine = records.find((x) => x.branch === b && (!x.repo || x.repo === r.id));
1943
+ const other = records.find((x) => x.branch === b);
1944
+ branches.set(b, mine ?? { task: other?.task ?? b.split("/").at(-1), branch: b });
1945
+ }
1946
+ for (const x of records)
1947
+ if (x.repo === r.id && !branches.has(x.branch))
1948
+ branches.set(x.branch, x);
1949
+ const refs = [...branches.values()].filter((x) => (!x.repo || x.repo === r.id) && (!o.task || x.task === o.task));
1950
+ if (!refs.length)
1951
+ continue;
1952
+ const not = [];
1953
+ if (await sha(dir, `refs/heads/${from}`))
1954
+ not.push(`refs/heads/${from}`);
1955
+ if (!o.offline) {
1956
+ const onHost = await git(["ls-remote", "--heads", remote, `refs/heads/${from}`], dir).then((x) => x.stdout.trim().split(/\s+/)[0] || void 0, () => void 0);
1957
+ if (onHost && !await sha(dir, onHost))
1958
+ await git(["fetch", "-q", remote, `+refs/heads/${from}:refs/remotes/${remote}/${from}`], dir).catch(() => void 0);
1959
+ if (onHost && await sha(dir, onHost))
1960
+ not.push(onHost);
1961
+ } else if (await sha(dir, `refs/remotes/${remote}/${from}`))
1962
+ not.push(`refs/remotes/${remote}/${from}`);
1963
+ for (const x of refs) {
1964
+ const tip = await sha(dir, `refs/heads/${x.branch}`);
1965
+ if (!tip)
1966
+ continue;
1967
+ const merged = x.ticked && x.commit ? await sha(dir, x.commit) : void 0;
1968
+ if (x.ticked && !merged)
1969
+ continue;
1970
+ const decided = await readTaskDecision(root, id, x.task);
1971
+ if (decided && decided.branch === x.branch && sameSha(decided.commit, tip))
1972
+ continue;
1973
+ const commits = await git([
1974
+ "rev-list",
1975
+ "--count",
1976
+ `refs/heads/${x.branch}`,
1977
+ ...not.length || merged ? ["--not", ...not, ...merged ? [merged] : []] : []
1978
+ ], dir).then((y) => Number(y.stdout.trim()) || 0, () => 0);
1979
+ if (commits > 0)
1980
+ out.push({
1981
+ task: x.task,
1982
+ repo: r.id,
1983
+ branch: x.branch,
1984
+ commits,
1985
+ ...x.state ? { state: x.state } : {},
1986
+ ...x.pr ? { pr: x.pr } : {}
1987
+ });
1988
+ }
1989
+ }
1990
+ return out.sort((a, b) => a.task < b.task ? -1 : a.task > b.task ? 1 : 0);
1991
+ }
1992
+ var sameSha = (a, b) => a.startsWith(b) || b.startsWith(a);
1993
+ function unmergedTaskWords(id, u) {
1994
+ const from = changeBranch(id);
1995
+ const n = `${u.commits} commit${u.commits === 1 ? "" : "s"}`;
1996
+ const what = u.state === "pr-open" && u.pr ? `its PR #${u.pr.number} isn't merged yet: a person merges it on the Git host (${u.pr.url}), then Sync records it` : u.state === "unverified" || u.state === "needs-person" || u.state === "failed" ? `it was never built and raised: Build and raise PR on the Task board (or Run again), then merge its PR into ${from}` : `raise its PR (Raise the task's PR on the Task board) and merge it into ${from}`;
1997
+ return `Task ${u.task}'s branch ${u.branch} in ${u.repo} has ${n} not in ${from}, so its code isn't in the change: ${what}. If its work isn't needed, mark the task done with a reason.`;
1998
+ }
1999
+ async function taskBranchReasons(root, id, o = {}) {
2000
+ return (await unmergedTaskBranches(root, id, o)).map((u) => ({
2001
+ code: "task-not-merged",
2002
+ message: unmergedTaskWords(id, u)
2003
+ }));
2004
+ }
2005
+ async function recordTaskDecision(root, id, task, o) {
2006
+ const reason = o.reason.trim();
2007
+ if (!reason)
2008
+ throw new ChangeError("invalid", `Say why task ${task}'s work isn't needed in ${id}: a reason is recorded with the decision.`);
2009
+ const branch = taskBranch(id, task);
2010
+ const git = o.git ?? createGit({ timeoutMs: 3e4 });
2011
+ const cr = await readChangeRepos(root, id).catch(() => void 0);
2012
+ let commit;
2013
+ for (const r of cr?.repos ?? []) {
2014
+ const dir = repoDir(root, r);
2015
+ if (!dir)
2016
+ continue;
2017
+ commit = await git(["rev-parse", "--verify", "--quiet", `refs/heads/${branch}^{commit}`], dir).then((x) => x.stdout.trim() || void 0, () => void 0);
2018
+ if (commit)
2019
+ break;
2020
+ }
2021
+ if (!commit)
2022
+ throw new ChangeError("refused", `Task ${task} has no branch ${branch} here, so there is nothing to decide: mark it done.`);
2023
+ const d = TaskDecision.parse({
2024
+ schemaVersion: 1,
2025
+ task,
2026
+ branch,
2027
+ commit,
2028
+ by: o.by.trim(),
2029
+ at: o.at,
2030
+ reason
2031
+ });
2032
+ await writeFileAtomic(join9(changeFolder(root, id), "tasks", task, TASK_DECISION_FILE), toCanonicalYaml(d, ["schemaVersion", "task", "branch", "commit"]));
2033
+ return d;
2034
+ }
2035
+
2036
+ // packages/changes/dist/release-prs.js
1824
2037
  var RELEASE_PR_STATES = [
1825
2038
  "qa",
1826
2039
  "release-ready",
@@ -1837,18 +2050,87 @@ var NothingFile = external_exports.strictObject({
1837
2050
  commit: external_exports.string().regex(/^[0-9a-f]{7,64}$/).optional(),
1838
2051
  at: external_exports.string().min(1),
1839
2052
  by: external_exports.string().min(1)
1840
- }))
2053
+ })),
2054
+ /**
2055
+ * Review round 48: the change branch's commit when Sync first read each release PR merged, so
2056
+ * a merged PR stands only while the branch is there (work landed since needs a PR of its own).
2057
+ */
2058
+ merged: external_exports.array(external_exports.strictObject({
2059
+ repo: external_exports.string().min(1),
2060
+ number: external_exports.number().int().positive(),
2061
+ commit: external_exports.string().regex(/^[0-9a-f]{7,64}$/),
2062
+ at: external_exports.string().min(1)
2063
+ })).optional()
1841
2064
  });
1842
- async function readNothing(root, id) {
1843
- const text = await readFile8(join9(changeFolder(root, id), RELEASE_PRS_FILE), "utf8").catch(() => void 0);
2065
+ async function readReleaseFile(root, id) {
2066
+ const empty = { schemaVersion: 1, nothingToRelease: [] };
2067
+ const text = await readFile9(join10(changeFolder(root, id), RELEASE_PRS_FILE), "utf8").catch(() => void 0);
1844
2068
  if (text === void 0)
1845
- return [];
2069
+ return empty;
1846
2070
  try {
1847
- const r = NothingFile.safeParse((0, import_yaml7.parse)(text));
1848
- return r.success ? r.data.nothingToRelease : [];
2071
+ const r = NothingFile.safeParse((0, import_yaml8.parse)(text));
2072
+ return r.success ? r.data : empty;
1849
2073
  } catch {
1850
- return [];
2074
+ return empty;
2075
+ }
2076
+ }
2077
+ async function readNothing(root, id) {
2078
+ return (await readReleaseFile(root, id)).nothingToRelease;
2079
+ }
2080
+ async function writeReleaseFile(root, id, nothing, merged) {
2081
+ const byRepo = (xs) => [...xs].sort((a, b) => a.repo < b.repo ? -1 : a.repo > b.repo ? 1 : 0);
2082
+ await writeFile(join10(changeFolder(root, id), RELEASE_PRS_FILE), toCanonicalYaml(NothingFile.parse({
2083
+ schemaVersion: 1,
2084
+ nothingToRelease: byRepo(nothing),
2085
+ ...merged.length ? { merged: byRepo(merged) } : {}
2086
+ })), "utf8");
2087
+ }
2088
+ function repoDir2(root, r) {
2089
+ const dirs = [join10(root, r.worktree)];
2090
+ try {
2091
+ dirs.push(baseClonePath(root, r.id));
2092
+ } catch {
2093
+ }
2094
+ return dirs.find((d) => existsSync2(d));
2095
+ }
2096
+ async function staleReleasePrs(root, id, o = {}) {
2097
+ const out = /* @__PURE__ */ new Set();
2098
+ const cr = await readChangeRepos(root, id).catch(() => void 0);
2099
+ const merged = (await readReleaseFile(root, id)).merged ?? [];
2100
+ if (!cr || !merged.length)
2101
+ return out;
2102
+ const git = o.git ?? createGit({ timeoutMs: 3e4 });
2103
+ const remote = o.remote ?? "origin";
2104
+ for (const r of cr.repos) {
2105
+ if (r.pr?.state !== "merged")
2106
+ continue;
2107
+ const entry = merged.find((x) => x.repo === r.id && x.number === r.pr.number);
2108
+ if (!entry)
2109
+ continue;
2110
+ const dir = repoDir2(root, r);
2111
+ if (!dir)
2112
+ continue;
2113
+ if (await movedPast(git, dir, remote, r.branch, entry.commit, o.offline))
2114
+ out.add(r.id);
1851
2115
  }
2116
+ return out;
2117
+ }
2118
+ var lastFetched = (git, dir, remote, branch) => git(["rev-parse", "--verify", "--quiet", `refs/remotes/${remote}/${branch}^{commit}`], dir).then((x) => x.stdout.trim() || void 0, () => void 0);
2119
+ async function movedPast(git, dir, remote, branch, commit, offline) {
2120
+ const ref = `refs/heads/${branch}`;
2121
+ const known = (sha) => git(["cat-file", "-e", `${sha}^{commit}`], dir).then(() => true, () => false);
2122
+ const here = await git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], dir).then((x) => x.stdout.trim() || void 0, () => void 0);
2123
+ const onHost = offline ? await lastFetched(git, dir, remote, branch) : await git(["ls-remote", "--heads", remote, ref], dir).then((x) => x.stdout.trim().split(/\s+/)[0] || void 0, () => void 0);
2124
+ if (!offline && onHost && !sameSha2(onHost, commit) && !await known(onHost))
2125
+ await git(["fetch", "-q", remote, `+${ref}:refs/remotes/${remote}/${branch}`], dir).catch(() => void 0);
2126
+ const ahead = async (sha) => {
2127
+ if (!sha || sameSha2(sha, commit))
2128
+ return false;
2129
+ if (!await known(sha) || !await known(commit))
2130
+ return true;
2131
+ return git(["merge-base", "--is-ancestor", sha, commit], dir).then(() => false, () => true);
2132
+ };
2133
+ return await ahead(here) || await ahead(onHost);
1852
2134
  }
1853
2135
  async function nothingToRelease(root, id, o = {}) {
1854
2136
  const entries = await readNothing(root, id);
@@ -1866,22 +2148,19 @@ async function nothingToRelease(root, id, o = {}) {
1866
2148
  }
1867
2149
  return out;
1868
2150
  async function moved(r, commit) {
1869
- const dirs = [join9(root, r.worktree)];
1870
- try {
1871
- dirs.push(baseClonePath(root, r.id));
1872
- } catch {
1873
- }
1874
- const dir = dirs.find((d) => existsSync(d));
2151
+ const dir = repoDir2(root, r);
1875
2152
  if (!dir)
1876
2153
  return false;
1877
2154
  const ref = `refs/heads/${r.branch}`;
1878
2155
  const here = await git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], dir).then((x) => x.stdout.trim() || void 0, () => void 0);
1879
- const onHost = await git(["ls-remote", "--heads", remote, ref], dir).then((x) => x.stdout.trim().split(/\s+/)[0] || void 0, () => void 0);
1880
- const at = (sha) => sha !== void 0 && (commit === void 0 || !sameSha(sha, commit));
2156
+ const fetched = o.offline ? await lastFetched(git, dir, remote, r.branch) : void 0;
2157
+ const behind = !!fetched && !!commit && await git(["merge-base", "--is-ancestor", fetched, commit], dir).then(() => true, () => false);
2158
+ const onHost = o.offline ? behind ? void 0 : fetched : await git(["ls-remote", "--heads", remote, ref], dir).then((x) => x.stdout.trim().split(/\s+/)[0] || void 0, () => void 0);
2159
+ const at = (sha) => sha !== void 0 && (commit === void 0 || !sameSha2(sha, commit));
1881
2160
  return at(here) || at(onHost);
1882
2161
  }
1883
2162
  }
1884
- var sameSha = (a, b) => a.startsWith(b) || b.startsWith(a);
2163
+ var sameSha2 = (a, b) => a.startsWith(b) || b.startsWith(a);
1885
2164
  var firstLine = (e) => (e instanceof Error ? e.message : String(e)).split("\n")[0].replace(/\.$/, "");
1886
2165
  var prOf = (pr, now) => ({
1887
2166
  url: pr.url,
@@ -1912,12 +2191,16 @@ async function raiseReleasePrs(root, id, o) {
1912
2191
  const rows = [];
1913
2192
  let changed2 = false;
1914
2193
  const repos = cr.repos.map((r) => ({ ...r }));
1915
- const before = await readNothing(root, id);
2194
+ const file = await readReleaseFile(root, id);
2195
+ const before = file.nothingToRelease;
1916
2196
  const nothing = new Map(before.map((x) => [x.repo, x]));
2197
+ const stale = await staleReleasePrs(root, id, { git, remote });
2198
+ const unmerged = await unmergedTaskBranches(root, id, { git, remote });
1917
2199
  for (const r of repos) {
1918
- if (!live(r.pr))
2200
+ const movedOn = r.pr?.state === "merged" && stale.has(r.id) ? `Release PR #${r.pr.number} merged before ${r.branch} moved on; this one carries what landed since.` : void 0;
2201
+ if (!live(r.pr) || movedOn)
1919
2202
  nothing.delete(r.id);
1920
- if (live(r.pr)) {
2203
+ if (live(r.pr) && !movedOn) {
1921
2204
  rows.push({
1922
2205
  repo: r.id,
1923
2206
  outcome: "kept",
@@ -1926,6 +2209,15 @@ async function raiseReleasePrs(root, id, o) {
1926
2209
  });
1927
2210
  continue;
1928
2211
  }
2212
+ const held = unmerged.filter((u) => u.repo === r.id);
2213
+ if (held.length) {
2214
+ rows.push({
2215
+ repo: r.id,
2216
+ outcome: "refused",
2217
+ message: `${r.id}: no release PR, since ${r.branch} lacks the code of ${held.length === 1 ? `task ${held[0].task}` : `tasks ${held.map((u) => u.task).join(", ")}`}. ${held.map((u) => unmergedTaskWords(id, u)).join(" ")}`
2218
+ });
2219
+ continue;
2220
+ }
1929
2221
  if (r.branch !== expected) {
1930
2222
  rows.push({
1931
2223
  repo: r.id,
@@ -1964,9 +2256,9 @@ async function raiseReleasePrs(root, id, o) {
1964
2256
  });
1965
2257
  continue;
1966
2258
  }
1967
- const notes = [];
1968
- const dir = join9(root, r.worktree);
1969
- if (existsSync(dir)) {
2259
+ const notes = movedOn ? [movedOn] : [];
2260
+ const dir = join10(root, r.worktree);
2261
+ if (existsSync2(dir)) {
1970
2262
  const onHost = await git(["ls-remote", "--heads", remote, `refs/heads/${r.branch}`], dir).then((x) => x.stdout.trim() ? "yes" : "no").catch((e) => {
1971
2263
  notes.push(`Atticus couldn't reach ${remote} (${firstLine(e)}): push ${r.branch} yourself if the host lacks it.`);
1972
2264
  return "unknown";
@@ -2048,8 +2340,10 @@ async function raiseReleasePrs(root, id, o) {
2048
2340
  if (changed2)
2049
2341
  await writeChangeRepos(root, { ...cr, repos });
2050
2342
  const after = [...nothing.values()].sort((a, b) => a.repo < b.repo ? -1 : 1);
2051
- if (JSON.stringify(after) !== JSON.stringify(before))
2052
- await writeFile(join9(changeFolder(root, id), RELEASE_PRS_FILE), toCanonicalYaml(NothingFile.parse({ schemaVersion: 1, nothingToRelease: after })), "utf8");
2343
+ const mergedBefore = file.merged ?? [];
2344
+ const mergedAfter = mergedBefore.filter((x) => repos.some((r) => r.id === x.repo && r.pr?.number === x.number && r.pr.state === "merged"));
2345
+ if (JSON.stringify(after) !== JSON.stringify(before) || mergedAfter.length !== mergedBefore.length)
2346
+ await writeReleaseFile(root, id, after, mergedAfter);
2053
2347
  return { change: id, rows };
2054
2348
  }
2055
2349
  async function syncReleasePrs(root, id, o) {
@@ -2060,6 +2354,9 @@ async function syncReleasePrs(root, id, o) {
2060
2354
  const now = o.now ?? nowIso();
2061
2355
  let changed2 = false;
2062
2356
  const repos = cr.repos.map((r) => ({ ...r }));
2357
+ const file = await readReleaseFile(root, id);
2358
+ const merged = [...file.merged ?? []];
2359
+ let mergedChanged = false;
2063
2360
  for (const r of repos) {
2064
2361
  if (!r.pr)
2065
2362
  continue;
@@ -2100,6 +2397,16 @@ async function syncReleasePrs(root, id, o) {
2100
2397
  r.build = { ...r.build, status: build, at: now };
2101
2398
  changed2 = true;
2102
2399
  }
2400
+ if (pr.state === "merged" && !merged.some((x) => x.repo === r.id && x.number === pr.number)) {
2401
+ const commit = await branchTip(root, r, o.git, o.remote ?? "origin");
2402
+ if (commit) {
2403
+ for (let i = merged.length - 1; i >= 0; i--)
2404
+ if (merged[i].repo === r.id)
2405
+ merged.splice(i, 1);
2406
+ merged.push({ repo: r.id, number: pr.number, commit, at: now });
2407
+ mergedChanged = true;
2408
+ }
2409
+ }
2103
2410
  rows.push({
2104
2411
  repo: r.id,
2105
2412
  outcome: "kept",
@@ -2109,8 +2416,25 @@ async function syncReleasePrs(root, id, o) {
2109
2416
  }
2110
2417
  if (changed2)
2111
2418
  await writeChangeRepos(root, { ...cr, repos });
2419
+ if (mergedChanged)
2420
+ await writeReleaseFile(root, id, file.nothingToRelease, merged);
2112
2421
  return { change: id, rows };
2113
2422
  }
2423
+ async function branchTip(root, r, g, remote) {
2424
+ const dir = repoDir2(root, r);
2425
+ if (!dir)
2426
+ return void 0;
2427
+ const git = g ?? createGit({ timeoutMs: 3e4 });
2428
+ const ref = `refs/heads/${r.branch}`;
2429
+ const onHost = await git(["ls-remote", "--heads", remote, ref], dir).then((x) => x.stdout.trim().split(/\s+/)[0] || void 0, () => void 0);
2430
+ if (onHost) {
2431
+ await git(["fetch", "-q", remote, `+${ref}:refs/remotes/${remote}/${r.branch}`], dir).catch(() => void 0);
2432
+ if (/^[0-9a-f]{7,64}$/.test(onHost))
2433
+ return onHost;
2434
+ }
2435
+ const here = await git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], dir).then((x) => x.stdout.trim(), () => "");
2436
+ return /^[0-9a-f]{7,64}$/.test(here) ? here : void 0;
2437
+ }
2114
2438
  function buildOf(pr) {
2115
2439
  if (!pr.checks.length)
2116
2440
  return void 0;
@@ -2121,11 +2445,11 @@ function buildOf(pr) {
2121
2445
  return "passed";
2122
2446
  }
2123
2447
  async function requireMergedPrs(root) {
2124
- const text = await readFile8(join9(orgPackDirOf(root), "process", "ci.yaml"), "utf8").catch(() => void 0);
2448
+ const text = await readFile9(join10(orgPackDirOf(root), "process", "ci.yaml"), "utf8").catch(() => void 0);
2125
2449
  if (text === void 0)
2126
2450
  return true;
2127
2451
  try {
2128
- const v = (0, import_yaml7.parse)(text)?.archive?.requireMergedPrs;
2452
+ const v = (0, import_yaml8.parse)(text)?.archive?.requireMergedPrs;
2129
2453
  return v !== false;
2130
2454
  } catch {
2131
2455
  return true;
@@ -2137,10 +2461,18 @@ async function releasePrReasons(root, id, o = {}) {
2137
2461
  return [];
2138
2462
  const merged = await requireMergedPrs(root);
2139
2463
  const nothing = await nothingToRelease(root, id, o);
2140
- const out = [];
2464
+ const stale = await staleReleasePrs(root, id, o);
2465
+ const out = await taskBranchReasons(root, id, o);
2141
2466
  for (const r of cr.repos) {
2142
2467
  if (!r.pr && nothing.has(r.id))
2143
2468
  continue;
2469
+ if (r.pr?.state === "merged" && stale.has(r.id)) {
2470
+ out.push({
2471
+ code: "release-pr-stale",
2472
+ message: `${r.id}: ${r.branch} has commits since release PR #${r.pr.number} merged, so they haven't reached its default branch: raise the release PRs again (Release \u203A Raise the release PRs, or atticus change release-prs ${id} --approve).`
2473
+ });
2474
+ continue;
2475
+ }
2144
2476
  if (!r.pr || r.pr.state === "closed")
2145
2477
  out.push({
2146
2478
  code: "release-pr-missing",
@@ -2202,7 +2534,11 @@ async function assess(ctx, change) {
2202
2534
  ...stageReasons.length ? [] : await checkMandatoryRounds(ctx.root, change, phaseGates(ctx, phase).filter((g) => mandatoryGates(ctx).includes(g))),
2203
2535
  // ADR 0184: go/no-go is decided over the release PRs, each repo's change branch into its
2204
2536
  // default branch, merged (unless the org pack's ci.yaml says archive.requireMergedPrs: false).
2205
- ...phase === "release" ? await releasePrReasons(ctx.root, change.id) : []
2537
+ // Both read refs here only (the host's branches as last fetched): an assessment runs on every
2538
+ // page load (Home's cockpit), which never reaches the Git host; Sync and the release steps do.
2539
+ ...phase === "release" ? await releasePrReasons(ctx.root, change.id, { offline: true }) : [],
2540
+ // App-only audit: Build ready waits for every task's work to be in the change branch.
2541
+ ...phase === "build" ? await taskBranchReasons(ctx.root, change.id, { offline: true }) : []
2206
2542
  ];
2207
2543
  const next = change.state === "returned" ? phaseState(phase) : nextState(type, phase);
2208
2544
  const live2 = change.state === "returned" ? void 0 : liveHandoff(change);
@@ -2245,7 +2581,7 @@ async function changeReport(root, id) {
2245
2581
  return { ...a, nextStep: nextStep(a, tasks), problems: ctx.problems };
2246
2582
  }
2247
2583
  async function taskTicks(root, id) {
2248
- const text = await readFile9(join10(changeFolder(root, id), "tasks.md"), "utf8").catch(() => "");
2584
+ const text = await readFile10(join11(changeFolder(root, id), "tasks.md"), "utf8").catch(() => "");
2249
2585
  const boxes = [...text.matchAll(/^\s*[-*]\s+\[([ xX])\]/gm)].map((m) => m[1] !== " ");
2250
2586
  return boxes.length ? { done: boxes.filter(Boolean).length, total: boxes.length } : void 0;
2251
2587
  }
@@ -2276,9 +2612,9 @@ function nextStep(a, tasks) {
2276
2612
  }
2277
2613
 
2278
2614
  // packages/changes/dist/new-change.js
2279
- var import_yaml8 = __toESM(require_dist(), 1);
2280
- import { mkdir, readFile as readFile10, writeFile as writeFile2 } from "node:fs/promises";
2281
- import { join as join11 } from "node:path";
2615
+ var import_yaml9 = __toESM(require_dist(), 1);
2616
+ import { mkdir, readFile as readFile11, writeFile as writeFile2 } from "node:fs/promises";
2617
+ import { join as join12 } from "node:path";
2282
2618
  var nowIso2 = () => (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
2283
2619
  var strip2 = (h) => h.startsWith("@") ? h.slice(1) : h;
2284
2620
  function windowsUnsafeName(id) {
@@ -2290,7 +2626,7 @@ function slugify(title) {
2290
2626
  }
2291
2627
  async function exists(path) {
2292
2628
  try {
2293
- await readFile10(path);
2629
+ await readFile11(path);
2294
2630
  return true;
2295
2631
  } catch {
2296
2632
  return false;
@@ -2298,7 +2634,7 @@ async function exists(path) {
2298
2634
  }
2299
2635
  async function defaultSchema(root) {
2300
2636
  try {
2301
- const cfg = (0, import_yaml8.parse)(await readFile10(join11(openspecDirOf(root), "config.yaml"), "utf8"));
2637
+ const cfg = (0, import_yaml9.parse)(await readFile11(join12(openspecDirOf(root), "config.yaml"), "utf8"));
2302
2638
  if (cfg && typeof cfg.schema === "string")
2303
2639
  return cfg.schema;
2304
2640
  } catch {
@@ -2322,17 +2658,17 @@ async function newChange(root, opts) {
2322
2658
  const now = opts.now ?? nowIso2();
2323
2659
  const by = strip2(opts.by);
2324
2660
  const dir = changeFolder(root, id);
2325
- if (opts.openspec && !await exists(join11(dir, ".openspec.yaml"))) {
2661
+ if (opts.openspec && !await exists(join12(dir, ".openspec.yaml"))) {
2326
2662
  await opts.openspec.newChange(id, { description: opts.title });
2327
2663
  } else {
2328
2664
  await mkdir(dir, { recursive: true });
2329
- if (!await exists(join11(dir, ".openspec.yaml")))
2330
- await writeFile2(join11(dir, ".openspec.yaml"), `schema: ${await defaultSchema(root)}
2665
+ if (!await exists(join12(dir, ".openspec.yaml")))
2666
+ await writeFile2(join12(dir, ".openspec.yaml"), `schema: ${await defaultSchema(root)}
2331
2667
  created: ${now.slice(0, 10)}
2332
2668
  `, "utf8");
2333
2669
  }
2334
- if (!await exists(join11(dir, "brief.md")))
2335
- await writeFileAtomic(join11(dir, "brief.md"), renderTemplate(template, {
2670
+ if (!await exists(join12(dir, "brief.md")))
2671
+ await writeFileAtomic(join12(dir, "brief.md"), renderTemplate(template, {
2336
2672
  title: opts.title,
2337
2673
  id,
2338
2674
  type: type.id,
@@ -2372,7 +2708,7 @@ created: ${now.slice(0, 10)}
2372
2708
  // packages/changes/dist/publish.js
2373
2709
  import { rm } from "node:fs/promises";
2374
2710
  import { randomUUID } from "node:crypto";
2375
- import { join as join12, resolve } from "node:path";
2711
+ import { join as join13, resolve } from "node:path";
2376
2712
  function gitHandoffPublisher(opts = {}) {
2377
2713
  const remote = opts.remote ?? "origin";
2378
2714
  const env = opts.env ?? process.env;
@@ -2394,18 +2730,19 @@ function gitHandoffPublisher(opts = {}) {
2394
2730
  const gitDir = await out(["rev-parse", "--absolute-git-dir"]);
2395
2731
  await tryOut(["fetch", "--quiet", remote, base]);
2396
2732
  await tryOut(["fetch", "--quiet", remote, branch]);
2397
- const start = await tryOut([
2733
+ const onRemote = await tryOut([
2398
2734
  "rev-parse",
2399
2735
  "--verify",
2400
2736
  "--quiet",
2401
2737
  `refs/remotes/${remote}/${branch}^{commit}`
2402
- ]) ?? await tryOut([
2738
+ ]);
2739
+ const start = onRemote ?? await tryOut([
2403
2740
  "rev-parse",
2404
2741
  "--verify",
2405
2742
  "--quiet",
2406
2743
  `refs/remotes/${remote}/${base}^{commit}`
2407
2744
  ]) ?? await out(["rev-parse", "--verify", `refs/heads/${base}^{commit}`]);
2408
- const index = join12(gitDir, `atticus-handoff-index-${process.pid}-${randomUUID()}`);
2745
+ const index = join13(gitDir, `atticus-handoff-index-${process.pid}-${randomUUID()}`);
2409
2746
  const staged = createGit({ env: { ...env, GIT_INDEX_FILE: index } });
2410
2747
  try {
2411
2748
  await staged(["read-tree", start], root);
@@ -2414,9 +2751,9 @@ function gitHandoffPublisher(opts = {}) {
2414
2751
  const tree = (await staged(["write-tree"], root)).stdout.trim();
2415
2752
  const startTree = await out(["rev-parse", `${start}^{tree}`]);
2416
2753
  const commit = tree === startTree ? start : await out(["commit-tree", tree, "-p", start, "-m", message]);
2417
- await out(["update-ref", `refs/heads/${branch}`, commit]);
2754
+ await keepLocalBranch(out, tryOut, branch, start, commit);
2418
2755
  await out(["push", "--quiet", remote, `${commit}:refs/heads/${branch}`]);
2419
- return { commit };
2756
+ return { commit, changed: commit !== onRemote };
2420
2757
  } finally {
2421
2758
  await rm(index, { force: true });
2422
2759
  }
@@ -2424,15 +2761,49 @@ function gitHandoffPublisher(opts = {}) {
2424
2761
  }
2425
2762
  }
2426
2763
  var queue = /* @__PURE__ */ new Map();
2764
+ async function handoffBranchPushed(root, branch, o = {}) {
2765
+ const git = createGit({ env: o.env ?? process.env });
2766
+ const sha = (ref) => git(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`], root).then((x) => x.stdout.trim() || void 0, () => void 0);
2767
+ const here = await sha(`refs/heads/${branch}`);
2768
+ if (!here)
2769
+ return true;
2770
+ return await sha(`refs/remotes/${o.remote ?? "origin"}/${branch}`) === here;
2771
+ }
2772
+ async function pushHandoffBranch(root, branch, o) {
2773
+ requireApproval("git", o.approval);
2774
+ if (!/^handoff\//.test(branch))
2775
+ throw new Error(`${branch} isn't a hand-off branch`);
2776
+ const git = createGit({ env: o.env ?? process.env });
2777
+ const commit = (await git(["rev-parse", "--verify", `refs/heads/${branch}^{commit}`], root)).stdout.trim();
2778
+ await git(["push", "--quiet", o.remote ?? "origin", `refs/heads/${branch}:refs/heads/${branch}`], root);
2779
+ return { branch, commit };
2780
+ }
2781
+ async function keepLocalBranch(out, tryOut, branch, start, commit) {
2782
+ const ref = `refs/heads/${branch}`;
2783
+ const local = await tryOut(["rev-parse", "--verify", "--quiet", `${ref}^{commit}`]);
2784
+ if (local === commit)
2785
+ return;
2786
+ const list = await tryOut(["worktree", "list", "--porcelain"]) ?? "";
2787
+ if (list.split(/\r?\n/).includes(`branch ${ref}`))
2788
+ return;
2789
+ if (!local) {
2790
+ await tryOut(["update-ref", ref, commit, ""]);
2791
+ return;
2792
+ }
2793
+ const own = await tryOut(["rev-list", "--count", `${start}..${local}`]);
2794
+ if (own === void 0 || own !== "0")
2795
+ return;
2796
+ await out(["update-ref", ref, commit, local]).catch(() => void 0);
2797
+ }
2427
2798
 
2428
2799
  // packages/changes/dist/handoff.js
2429
- import { readdir as readdir6, readFile as readFile12 } from "node:fs/promises";
2430
- import { join as join14 } from "node:path";
2800
+ import { readdir as readdir7, readFile as readFile13 } from "node:fs/promises";
2801
+ import { join as join15 } from "node:path";
2431
2802
 
2432
2803
  // packages/changes/dist/drafted.js
2433
- var import_yaml9 = __toESM(require_dist(), 1);
2434
- import { mkdir as mkdir2, readdir as readdir5, readFile as readFile11, rm as rm2 } from "node:fs/promises";
2435
- import { dirname, join as join13, relative, sep } from "node:path";
2804
+ var import_yaml10 = __toESM(require_dist(), 1);
2805
+ import { mkdir as mkdir2, readdir as readdir6, readFile as readFile12, rm as rm2 } from "node:fs/promises";
2806
+ import { dirname, join as join14, relative, sep } from "node:path";
2436
2807
  var CAPTURE_SCHEMA_VERSION = 1;
2437
2808
  var DraftCapture = external_exports.object({
2438
2809
  schemaVersion: external_exports.literal(CAPTURE_SCHEMA_VERSION),
@@ -2507,7 +2878,7 @@ var sameText = (text, recorded) => sha256(text) === recorded || sha256(text.repl
2507
2878
  async function walk2(dir, rel = "") {
2508
2879
  let entries;
2509
2880
  try {
2510
- entries = await readdir5(join13(dir, rel), { withFileTypes: true });
2881
+ entries = await readdir6(join14(dir, rel), { withFileTypes: true });
2511
2882
  } catch {
2512
2883
  return [];
2513
2884
  }
@@ -2652,7 +3023,7 @@ async function captureHandoff(input) {
2652
3023
  } else {
2653
3024
  const texts = await Promise.all(recorded.map(async (r) => [
2654
3025
  r,
2655
- await readFile11(join13(folder, ...r.path.split("/")), "utf8").catch(() => void 0)
3026
+ await readFile12(join14(folder, ...r.path.split("/")), "utf8").catch(() => void 0)
2656
3027
  ]));
2657
3028
  if (texts.every(([r, t]) => t !== void 0 && sameText(t, r.sha256))) {
2658
3029
  from = "working-tree";
@@ -2716,10 +3087,10 @@ async function captureHandoff(input) {
2716
3087
  } : {}
2717
3088
  };
2718
3089
  const p = capturePaths(name);
2719
- await rm2(join13(folder, ...p.drafted.split("/")), { recursive: true, force: true });
2720
- await rm2(join13(folder, ...p.merged.split("/")), { recursive: true, force: true });
3090
+ await rm2(join14(folder, ...p.drafted.split("/")), { recursive: true, force: true });
3091
+ await rm2(join14(folder, ...p.merged.split("/")), { recursive: true, force: true });
2721
3092
  const put = async (base, rel, text) => {
2722
- const f = join13(folder, ...base.split("/"), ...rel.split("/"));
3093
+ const f = join14(folder, ...base.split("/"), ...rel.split("/"));
2723
3094
  await mkdir2(dirname(f), { recursive: true });
2724
3095
  await writeFileAtomic(f, text);
2725
3096
  };
@@ -2728,37 +3099,37 @@ async function captureHandoff(input) {
2728
3099
  if (state === "captured")
2729
3100
  for (const [rel, t] of mergedFiles)
2730
3101
  await put(p.merged, rel, t);
2731
- await mkdir2(join13(folder, "stage"), { recursive: true });
2732
- await writeFileAtomic(join13(folder, ...p.capture.split("/")), HEADER + (0, import_yaml9.stringify)(capture, { lineWidth: 0 }));
3102
+ await mkdir2(join14(folder, "stage"), { recursive: true });
3103
+ await writeFileAtomic(join14(folder, ...p.capture.split("/")), HEADER + (0, import_yaml10.stringify)(capture, { lineWidth: 0 }));
2733
3104
  return { capture, file: p.capture };
2734
3105
  }
2735
3106
  async function readFolderFiles(dir) {
2736
3107
  const out = /* @__PURE__ */ new Map();
2737
3108
  for (const f of await walk2(dir))
2738
- out.set(f, (await readFile11(join13(dir, ...f.split("/")), "utf8")).replace(/\r\n/g, "\n"));
3109
+ out.set(f, (await readFile12(join14(dir, ...f.split("/")), "utf8")).replace(/\r\n/g, "\n"));
2739
3110
  return out;
2740
3111
  }
2741
3112
  async function changeFolders(root) {
2742
- const base = join13(root, ...changesRelOf(root).split("/"));
3113
+ const base = join14(root, ...changesRelOf(root).split("/"));
2743
3114
  const out = [];
2744
- const list = async (dir) => (await readdir5(dir, { withFileTypes: true }).catch(() => [])).filter((e) => e.isDirectory()).map((e) => e.name).sort();
3115
+ const list = async (dir) => (await readdir6(dir, { withFileTypes: true }).catch(() => [])).filter((e) => e.isDirectory()).map((e) => e.name).sort();
2745
3116
  for (const name of await list(base)) {
2746
3117
  if (name === "archive")
2747
- for (const a of await list(join13(base, "archive")))
2748
- out.push(join13(base, "archive", a));
3118
+ for (const a of await list(join14(base, "archive")))
3119
+ out.push(join14(base, "archive", a));
2749
3120
  else
2750
- out.push(join13(base, name));
3121
+ out.push(join14(base, name));
2751
3122
  }
2752
3123
  return out;
2753
3124
  }
2754
3125
  async function readDraftPairs(root) {
2755
3126
  const out = [];
2756
3127
  for (const folder of await changeFolders(root)) {
2757
- const names = (await readdir5(join13(folder, "stage")).catch(() => [])).filter((f) => f.endsWith(".capture.yaml")).sort();
3128
+ const names = (await readdir6(join14(folder, "stage")).catch(() => [])).filter((f) => f.endsWith(".capture.yaml")).sort();
2758
3129
  for (const f of names) {
2759
3130
  let capture;
2760
3131
  try {
2761
- capture = DraftCapture.parse((0, import_yaml9.parse)(await readFile11(join13(folder, "stage", f), "utf8")));
3132
+ capture = DraftCapture.parse((0, import_yaml10.parse)(await readFile12(join14(folder, "stage", f), "utf8")));
2762
3133
  } catch {
2763
3134
  continue;
2764
3135
  }
@@ -2766,8 +3137,8 @@ async function readDraftPairs(root) {
2766
3137
  out.push({
2767
3138
  capture,
2768
3139
  folder: relative(root, folder).split(sep).join("/"),
2769
- drafted: await readFolderFiles(join13(folder, ...p.drafted.split("/"))),
2770
- merged: await readFolderFiles(join13(folder, ...p.merged.split("/")))
3140
+ drafted: await readFolderFiles(join14(folder, ...p.drafted.split("/"))),
3141
+ merged: await readFolderFiles(join14(folder, ...p.merged.split("/")))
2771
3142
  });
2772
3143
  }
2773
3144
  }
@@ -2787,8 +3158,8 @@ async function completeCaptures(root, ctx, o) {
2787
3158
  continue;
2788
3159
  for (const h of change.handoffs.filter((x) => x.state === "merged")) {
2789
3160
  const { name } = await captureStageOf(ctx, change, h.phase);
2790
- const file = join13(changeFolder(root, change.id), ...capturePaths(name).capture.split("/"));
2791
- const prior = await readFile11(file, "utf8").then((t) => DraftCapture.safeParse((0, import_yaml9.parse)(t)), () => void 0);
3161
+ const file = join14(changeFolder(root, change.id), ...capturePaths(name).capture.split("/"));
3162
+ const prior = await readFile12(file, "utf8").then((t) => DraftCapture.safeParse((0, import_yaml10.parse)(t)), () => void 0);
2792
3163
  if (prior?.success && prior.data.pr.number === h.pr.number && prior.data.state !== "waiting-for-merge")
2793
3164
  continue;
2794
3165
  const base = prior?.success ? prior.data.base : o.defaultBase ?? "main";
@@ -2812,6 +3183,7 @@ async function completeCaptures(root, ctx, o) {
2812
3183
  // packages/changes/dist/handoff.js
2813
3184
  var nowIso3 = () => (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z");
2814
3185
  var strip3 = (h) => h.startsWith("@") ? h.slice(1) : h;
3186
+ var sameHandle = (a, b) => handleKey(a) === handleKey(b);
2815
3187
  function metarepo(ctx, repo) {
2816
3188
  const fromRuntime = ctx.orgPack.runtime.adapters.gitHost?.settings.metarepo;
2817
3189
  const r = repo ?? (typeof fromRuntime === "string" ? fromRuntime : void 0);
@@ -2828,16 +3200,16 @@ async function load(root, id) {
2828
3200
  }
2829
3201
  async function walkReview(dir, gate) {
2830
3202
  const out = [];
2831
- const base = join14(dir, "review", gate);
3203
+ const base = join15(dir, "review", gate);
2832
3204
  let rounds;
2833
3205
  try {
2834
- rounds = (await readdir6(base)).filter((r) => /^\d+$/.test(r)).sort((a, b) => Number(a) - Number(b));
3206
+ rounds = (await readdir7(base)).filter((r) => /^\d+$/.test(r)).sort((a, b) => Number(a) - Number(b));
2835
3207
  } catch {
2836
3208
  return [];
2837
3209
  }
2838
3210
  for (const r of rounds) {
2839
3211
  try {
2840
- for (const f of (await readdir6(join14(base, r))).filter((x) => /\.ya?ml$/.test(x)).sort())
3212
+ for (const f of (await readdir7(join15(base, r))).filter((x) => /\.ya?ml$/.test(x)).sort())
2841
3213
  out.push(`review/${gate}/${r}/${f}`);
2842
3214
  } catch {
2843
3215
  }
@@ -2848,12 +3220,12 @@ async function keptPairFiles(dir) {
2848
3220
  const out = [];
2849
3221
  let entries;
2850
3222
  try {
2851
- entries = await readdir6(join14(dir, "stage"), { withFileTypes: true });
3223
+ entries = await readdir7(join15(dir, "stage"), { withFileTypes: true });
2852
3224
  } catch {
2853
3225
  return out;
2854
3226
  }
2855
3227
  const walk3 = async (rel) => {
2856
- for (const e of await readdir6(join14(dir, ...rel.split("/")), { withFileTypes: true }).catch(() => [])) {
3228
+ for (const e of await readdir7(join15(dir, ...rel.split("/")), { withFileTypes: true }).catch(() => [])) {
2857
3229
  const p = `${rel}/${e.name}`;
2858
3230
  if (e.isDirectory())
2859
3231
  await walk3(p);
@@ -2887,15 +3259,20 @@ async function handoffFiles(ctx, a, phase) {
2887
3259
  for (const f of await stageFiles(dir, step))
2888
3260
  files.add(f);
2889
3261
  }
2890
- const present = async (f) => readFile12(join14(dir, ...f.split("/"))).then(() => true, () => false);
2891
- for (const own of ["stage/corrections.yaml", "stage/drafted.yaml"])
3262
+ const present = async (f) => readFile13(join15(dir, ...f.split("/"))).then(() => true, () => false);
3263
+ for (const own of [
3264
+ "stage/corrections.yaml",
3265
+ "stage/drafted.yaml",
3266
+ "stage/stories-numbers.yaml",
3267
+ "stage/stories-shape.yaml"
3268
+ ])
2892
3269
  if (await present(own))
2893
3270
  files.add(own);
2894
3271
  if (phase === "build") {
2895
3272
  for (const extra of ["tasks.md", "repos.yaml"])
2896
3273
  if (await present(extra))
2897
3274
  files.add(extra);
2898
- for (const t of await readdir6(join14(dir, "tasks"), { withFileTypes: true }).catch(() => []))
3275
+ for (const t of await readdir7(join15(dir, "tasks"), { withFileTypes: true }).catch(() => []))
2899
3276
  if (t.isDirectory() && await present(`tasks/${t.name}/report.md`))
2900
3277
  files.add(`tasks/${t.name}/report.md`);
2901
3278
  }
@@ -2918,6 +3295,11 @@ async function changeQuestionFiles(root, id) {
2918
3295
  const rel = questionsRelOf(root);
2919
3296
  return questions.filter((q) => namesChange(q.askedBy.command, id) || namesChange(q.rerun, id)).map((q) => `${rel}/${q.id}.yaml`).sort();
2920
3297
  }
3298
+ function nextRoleOf(c, phase, person) {
3299
+ const n = nextOwnersOf(phase, c.owners);
3300
+ return n?.people.includes(person) ? ` (${n.role.replace(/-/g, " ")})` : "";
3301
+ }
3302
+ var nextRoleName = (phase) => (nextOwnersOf(phase, {})?.role ?? "reviewer").replace(/-/g, " ");
2921
3303
  function prBody(a, phase, next, paths, reviewers, review) {
2922
3304
  const c = a.change;
2923
3305
  const lines2 = [
@@ -2933,7 +3315,7 @@ function prBody(a, phase, next, paths, reviewers, review) {
2933
3315
  "",
2934
3316
  "### Reviewers",
2935
3317
  "",
2936
- ...reviewers.length ? reviewers.map((r) => `- @${r}`) : ["- None"],
3318
+ ...reviewers.length ? reviewers.map((r) => `- @${r}${nextRoleOf(c, phase, r)}`) : [`- None: name the ${nextRoleName(phase)} under the change's owners`],
2937
3319
  "",
2938
3320
  "### Risk",
2939
3321
  "",
@@ -2964,7 +3346,7 @@ async function handoff(root, id, opts) {
2964
3346
  await requireValid(opts.openspec, id);
2965
3347
  const { files, artifacts } = await handoffFiles(ctx, a, phase);
2966
3348
  const dir = changeFolder(root, id);
2967
- const hashed = await Promise.all(files.map(async (path) => ({ path, sha256: sha256(await readFile12(join14(dir, path))) })));
3349
+ const hashed = await Promise.all(files.map(async (path) => ({ path, sha256: sha256(await readFile13(join15(dir, path))) })));
2968
3350
  const authors = { ...change.authors };
2969
3351
  for (const art of artifacts) {
2970
3352
  const people = (opts.authors?.[art] ?? [by]).map(strip3);
@@ -2979,13 +3361,16 @@ async function handoff(root, id, opts) {
2979
3361
  ...questionFiles
2980
3362
  ];
2981
3363
  const reviewers = [
2982
- ...new Set(files.flatMap((f) => ownersFor(ctx.codeowners, `${rel}/${id}/${f}`)))
2983
- ].filter((p) => p !== by).sort();
3364
+ .../* @__PURE__ */ new Set([
3365
+ ...files.flatMap((f) => ownersFor(ctx.codeowners, `${rel}/${id}/${f}`)),
3366
+ ...nextOwnersOf(phase, change.owners)?.people ?? []
3367
+ ])
3368
+ ].filter((p) => sameHandle(p, by) === false).sort();
2984
3369
  const live2 = change.state === "returned" ? void 0 : liveHandoff(change);
2985
3370
  if (live2 && live2.phase === phase) {
2986
3371
  const pr2 = await handoffPr(opts.gitHost, repo, id, live2);
2987
3372
  if (pr2.state === "open" || pr2.state === "draft") {
2988
- await opts.publisher?.publish({
3373
+ const published = await opts.publisher?.publish({
2989
3374
  root,
2990
3375
  branch,
2991
3376
  base: pr2.base,
@@ -2993,7 +3378,9 @@ async function handoff(root, id, opts) {
2993
3378
  message: `${handoffName(a.type, phase)}: update`,
2994
3379
  approval: opts.approval
2995
3380
  });
2996
- await opts.gitHost.comment(repo, pr2.number, `${by} updated the hand-off for another review.`, opts.approval);
3381
+ const unchanged = published ? published.changed === false : sameArtifacts(hashed, live2.artifacts);
3382
+ if (!unchanged)
3383
+ await opts.gitHost.comment(repo, pr2.number, `${by} updated the hand-off for another review.`, opts.approval);
2997
3384
  const updated = {
2998
3385
  ...live2,
2999
3386
  state: "open",
@@ -3014,6 +3401,13 @@ async function handoff(root, id, opts) {
3014
3401
  live2.state = "closed";
3015
3402
  live2.updatedAt = now;
3016
3403
  }
3404
+ if (change.state === "returned") {
3405
+ const open = (await opts.gitHost.listPullRequests(repo, { head: branch })).find((p) => p.state === "open" || p.state === "draft");
3406
+ if (open) {
3407
+ const message = `PR #${open.number} from ${branch} is still open on the Git host (${open.url}): close it there first (the return asked for that), then resubmit the change.`;
3408
+ throw new ChangeError("refused", message, [{ code: "host-refused", message }]);
3409
+ }
3410
+ }
3017
3411
  const base = opts.base ?? (await opts.gitHost.getRepo(repo)).defaultBranch;
3018
3412
  const next = a.next;
3019
3413
  await opts.publisher?.publish({
@@ -3024,13 +3418,23 @@ async function handoff(root, id, opts) {
3024
3418
  message: `${handoffName(a.type, phase)}: ${change.title}`,
3025
3419
  approval: opts.approval
3026
3420
  });
3027
- const pr = await opts.gitHost.createPullRequest({
3028
- repo,
3029
- head: branch,
3030
- base,
3031
- title: `${change.title}: ${handoffName(a.type, phase)}`,
3032
- body: prBody(a, phase, change.state === "returned" ? nextAfterResubmit(a, phase) : next, [...files.map((f) => `${rel}/${id}/${f}`), ...questionFiles], reviewers, opts.review)
3033
- }, opts.approval);
3421
+ let pr;
3422
+ try {
3423
+ pr = await opts.gitHost.createPullRequest({
3424
+ repo,
3425
+ head: branch,
3426
+ base,
3427
+ title: `${change.title}: ${handoffName(a.type, phase)}`,
3428
+ body: prBody(a, phase, change.state === "returned" ? nextAfterResubmit(a, phase) : next, [...files.map((f) => `${rel}/${id}/${f}`), ...questionFiles], reviewers, opts.review)
3429
+ }, opts.approval);
3430
+ } catch (e) {
3431
+ if (!(e instanceof AdapterError) || e.code !== "conflict")
3432
+ throw e;
3433
+ const open = (await opts.gitHost.listPullRequests(repo, { head: branch }).catch(() => [])).filter((p) => p.base === base && (p.state === "open" || p.state === "draft")).at(0);
3434
+ if (!open)
3435
+ throw e;
3436
+ pr = open;
3437
+ }
3034
3438
  const h = {
3035
3439
  phase,
3036
3440
  branch,
@@ -3057,6 +3461,10 @@ async function handoff(root, id, opts) {
3057
3461
  });
3058
3462
  return h;
3059
3463
  }
3464
+ function sameArtifacts(a, b) {
3465
+ const key = (xs) => JSON.stringify([...xs].map((x) => `${x.path} ${x.sha256}`).sort());
3466
+ return key(a) === key(b);
3467
+ }
3060
3468
  function nextAfterResubmit(a, phase) {
3061
3469
  const next = a.type.phases[a.type.phases.indexOf(phase) + 1];
3062
3470
  return next ? phaseState(next) : "released";
@@ -3255,10 +3663,15 @@ async function returnToPo(root, id, opts) {
3255
3663
  async function requireValid(openspec, id) {
3256
3664
  const r = await openspec.validate({ item: id, type: "change", strict: true });
3257
3665
  const item = r.items[0];
3258
- if (!item || item.valid)
3666
+ const issues = item ? blockingIssues(item.issues) : [];
3667
+ if (!item || item.valid && issues.length === 0)
3259
3668
  return;
3260
- throw new ChangeError("refused", `${id} doesn't pass openspec validate --strict.`, item.issues.filter((i) => i.level !== "INFO").map((i) => ({ code: "openspec-invalid", message: `${i.path}: ${i.message}` })));
3669
+ throw new ChangeError("refused", `${id} doesn't pass openspec validate --strict.`, issues.map((i) => ({
3670
+ code: "openspec-invalid",
3671
+ message: `${i.path}: ${i.message}${archiveRefuses(i) ? ` ${ADDED_ONLY}` : ""}`
3672
+ })));
3261
3673
  }
3674
+ var ADDED_ONLY = `A spec that openspec/specs doesn't have yet takes "## ADDED Requirements" only: change the delta's MODIFIED (or RENAMED, REMOVED) heading to ADDED, or run the stage that wrote it again.`;
3262
3675
  async function archiveChange(root, id, opts) {
3263
3676
  const { ctx, change } = await load(root, id);
3264
3677
  const a = await assess(ctx, change);
@@ -3279,14 +3692,25 @@ async function archiveChange(root, id, opts) {
3279
3692
  };
3280
3693
  await changeStore(root).write(updated);
3281
3694
  if (opts.openspec)
3282
- await opts.openspec.archive(id);
3695
+ try {
3696
+ await opts.openspec.archive(id);
3697
+ } catch (e) {
3698
+ await changeStore(root).write(change);
3699
+ const words = (e instanceof Error ? e.message : String(e)).trim();
3700
+ throw new ChangeError("refused", `OpenSpec refused to archive ${id}, so nothing was archived.`, [
3701
+ {
3702
+ code: "openspec-invalid",
3703
+ message: `${words.replace(/\.?$/, ".")}${/target spec does not exist|only ADDED/i.test(words) ? ` ${ADDED_ONLY}` : " Fix what it says, then archive again (atticus openspec validate " + id + " --strict says more)."}`
3704
+ }
3705
+ ]);
3706
+ }
3283
3707
  return updated;
3284
3708
  }
3285
3709
 
3286
3710
  // packages/changes/dist/learn.js
3287
- var import_yaml10 = __toESM(require_dist(), 1);
3288
- import { readFile as readFile13 } from "node:fs/promises";
3289
- import { join as join15 } from "node:path";
3711
+ var import_yaml11 = __toESM(require_dist(), 1);
3712
+ import { readFile as readFile14 } from "node:fs/promises";
3713
+ import { join as join16 } from "node:path";
3290
3714
  var LEARN_SCHEMA_VERSION = 1;
3291
3715
  var MAX_CELLS = 4e6;
3292
3716
  var lines = (text) => {
@@ -3633,7 +4057,7 @@ async function pairTouches(root, p) {
3633
4057
  let entry;
3634
4058
  const yaml = (t) => {
3635
4059
  try {
3636
- const v = (0, import_yaml10.parse)(t);
4060
+ const v = (0, import_yaml11.parse)(t);
3637
4061
  return v && typeof v === "object" ? v : void 0;
3638
4062
  } catch {
3639
4063
  return void 0;
@@ -3675,7 +4099,7 @@ async function pairTouches(root, p) {
3675
4099
  flows.push(m[1]);
3676
4100
  }
3677
4101
  }
3678
- const change = yaml(await readFile13(join15(root, ...p.folder.split("/"), "change.yaml"), "utf8").catch(() => ""));
4102
+ const change = yaml(await readFile14(join16(root, ...p.folder.split("/"), "change.yaml"), "utf8").catch(() => ""));
3679
4103
  const touches = change?.touches ?? {};
3680
4104
  for (const r of Array.isArray(touches.repos) ? touches.repos : [])
3681
4105
  if (typeof r === "string")
@@ -3768,8 +4192,8 @@ async function detectConflicts(root) {
3768
4192
  }
3769
4193
 
3770
4194
  // packages/changes/dist/size.js
3771
- import { readFile as readFile14 } from "node:fs/promises";
3772
- import { join as join16 } from "node:path";
4195
+ import { readFile as readFile15 } from "node:fs/promises";
4196
+ import { join as join17 } from "node:path";
3773
4197
  async function changeSize(ctx, change) {
3774
4198
  const type = ctx.orgPack.changeTypes.types.find((t) => t.id === change.type);
3775
4199
  if (!type)
@@ -3778,7 +4202,7 @@ async function changeSize(ctx, change) {
3778
4202
  why: "standard",
3779
4203
  words: `${change.type} isn't a change type in change-types.yaml, so ${change.id} takes the full path.`
3780
4204
  };
3781
- const brief = await readFile14(join16(changeFolder(ctx.root, change.id), "brief.md"), "utf8").catch(() => "");
4205
+ const brief = await readFile15(join17(changeFolder(ctx.root, change.id), "brief.md"), "utf8").catch(() => "");
3782
4206
  const f = fastPathOf(type, brief);
3783
4207
  const gate = f.fast ? fastGateOf(type, ctx.orgPack.gates) : void 0;
3784
4208
  const words = {
@@ -3792,13 +4216,13 @@ async function changeSize(ctx, change) {
3792
4216
  }
3793
4217
 
3794
4218
  // packages/changes/dist/openspec-stores.js
3795
- import { existsSync as existsSync2 } from "node:fs";
3796
- import { readFile as readFile15, writeFile as writeFile3 } from "node:fs/promises";
4219
+ import { existsSync as existsSync3 } from "node:fs";
4220
+ import { readFile as readFile16, writeFile as writeFile3 } from "node:fs/promises";
3797
4221
  import { homedir } from "node:os";
3798
- import { basename, isAbsolute, join as join17, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
4222
+ import { basename, isAbsolute, join as join18, relative as relative2, resolve as resolve2, sep as sep2 } from "node:path";
3799
4223
  function storeDirOf(root, path) {
3800
4224
  if (path === "~" || path.startsWith("~/") || path.startsWith("~\\"))
3801
- return join17(homedir(), path.slice(2));
4225
+ return join18(homedir(), path.slice(2));
3802
4226
  return isAbsolute(path) ? resolve2(path) : resolve2(root, path);
3803
4227
  }
3804
4228
  function packRel(root) {
@@ -3828,7 +4252,7 @@ async function storeSpecLookup(root, capability) {
3828
4252
  var REMOTE = /^[a-z][a-z0-9+.-]*:\/\//i;
3829
4253
  var SCP = /^[^/\\]+@[^:/\\]+:/;
3830
4254
  async function metarepoOrigin(root, git = createGit()) {
3831
- if (!existsSync2(join17(root, ".git")))
4255
+ if (!existsSync3(join18(root, ".git")))
3832
4256
  return void 0;
3833
4257
  try {
3834
4258
  const url = (await git(["remote", "get-url", "origin"], root)).stdout.trim();
@@ -3839,7 +4263,7 @@ async function metarepoOrigin(root, git = createGit()) {
3839
4263
  return void 0;
3840
4264
  }
3841
4265
  }
3842
- async function referenceMetarepoIn(root, repoDir, o = {}) {
4266
+ async function referenceMetarepoIn(root, repoDir3, o = {}) {
3843
4267
  const { settings } = await loadOpenSpecSettings(orgPackDirOf(root));
3844
4268
  if (!settings.referenceInRepos)
3845
4269
  return { action: "off" };
@@ -3851,8 +4275,8 @@ async function referenceMetarepoIn(root, repoDir, o = {}) {
3851
4275
  file,
3852
4276
  why: marker.missing ? `this metarepo isn't an OpenSpec store yet (no ${STORE_MARKER}): run atticus upgrade --openspec-store, commit the file, then hand down again` : `this metarepo's ${STORE_MARKER} can't be read (${marker.notes.join("; ")})`
3853
4277
  };
3854
- const path = join17(repoDir, "openspec", "config.yaml");
3855
- const text = await readFile15(path, "utf8").catch(() => void 0);
4278
+ const path = join18(repoDir3, "openspec", "config.yaml");
4279
+ const text = await readFile16(path, "utf8").catch(() => void 0);
3856
4280
  if (text === void 0)
3857
4281
  return {
3858
4282
  action: "skipped",
@@ -3869,7 +4293,7 @@ async function referenceMetarepoIn(root, repoDir, o = {}) {
3869
4293
  }
3870
4294
 
3871
4295
  // packages/changes/dist/merge-record.js
3872
- var import_yaml11 = __toESM(require_dist(), 1);
4296
+ var import_yaml12 = __toESM(require_dist(), 1);
3873
4297
  import { isDeepStrictEqual } from "node:util";
3874
4298
  var HANDOFF_RANK = {
3875
4299
  open: 0,
@@ -3987,7 +4411,7 @@ function mergeChangeRecords(base, ours, theirs) {
3987
4411
  }
3988
4412
  function readRecord(text) {
3989
4413
  try {
3990
- const r = ChangeLifecycle.safeParse((0, import_yaml11.parse)(text));
4414
+ const r = ChangeLifecycle.safeParse((0, import_yaml12.parse)(text));
3991
4415
  return r.success ? r.data : void 0;
3992
4416
  } catch {
3993
4417
  return void 0;
@@ -4038,6 +4462,29 @@ async function setChangeOwners(root, id, owners) {
4038
4462
  return r.data;
4039
4463
  }
4040
4464
 
4465
+ // packages/changes/dist/edit-hash.js
4466
+ import { createHash as createHash2 } from "node:crypto";
4467
+ function editText(file, text) {
4468
+ const lf = text.replace(/\r\n?/g, "\n");
4469
+ return isTasks(file) ? lf.replace(/^(\s*[-*]\s+\[)[ xX]?(\])/gm, "$1 $2") : lf;
4470
+ }
4471
+ function editSha(file, text) {
4472
+ const h = createHash2("sha256");
4473
+ if (typeof text !== "string" && !text.includes(13) && !isTasks(file))
4474
+ return h.update(text).digest("hex");
4475
+ const t = typeof text === "string" ? text : text.toString("utf8");
4476
+ return h.update(editText(file, t), "utf8").digest("hex");
4477
+ }
4478
+ function wroteThis(file, text, hash) {
4479
+ if (hash === void 0)
4480
+ return false;
4481
+ if (editSha(file, text) === hash)
4482
+ return true;
4483
+ const t = typeof text === "string" ? text : text.toString("utf8");
4484
+ return createHash2("sha256").update(t.replace(/\r\n?/g, "\n"), "utf8").digest("hex") === hash;
4485
+ }
4486
+ var isTasks = (file) => file.split(/[\\/]/).at(-1) === "tasks.md";
4487
+
4041
4488
  export {
4042
4489
  ChangeError,
4043
4490
  readTables,
@@ -4104,8 +4551,15 @@ export {
4104
4551
  workProgress,
4105
4552
  privacyImpact,
4106
4553
  prApprovers,
4554
+ TASK_DECISION_FILE,
4555
+ readTaskDecision,
4556
+ unmergedTaskBranches,
4557
+ unmergedTaskWords,
4558
+ taskBranchReasons,
4559
+ recordTaskDecision,
4107
4560
  RELEASE_PR_STATES,
4108
4561
  RELEASE_PRS_FILE,
4562
+ staleReleasePrs,
4109
4563
  nothingToRelease,
4110
4564
  raiseReleasePrs,
4111
4565
  syncReleasePrs,
@@ -4121,6 +4575,8 @@ export {
4121
4575
  slugify,
4122
4576
  newChange,
4123
4577
  gitHandoffPublisher,
4578
+ handoffBranchPushed,
4579
+ pushHandoffBranch,
4124
4580
  CAPTURE_SCHEMA_VERSION,
4125
4581
  DraftCapture,
4126
4582
  capturePaths,
@@ -4160,5 +4616,8 @@ export {
4160
4616
  mergeChangeText,
4161
4617
  OWNER_ROLE_KEYS,
4162
4618
  ChangeOwnersError,
4163
- setChangeOwners
4619
+ setChangeOwners,
4620
+ editText,
4621
+ editSha,
4622
+ wroteThis
4164
4623
  };