@ebrindley/build 0.0.0-stage → 1.42.1-rc28

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 (878) hide show
  1. package/.build-project/providers/catalog.json +13951 -0
  2. package/.build-project/providers/pricing.json +1978 -0
  3. package/.build-project/providers/registry.json +4103 -0
  4. package/.github/SECURITY.md +75 -0
  5. package/.nvmrc +1 -0
  6. package/CHANGELOG.md +661 -0
  7. package/CODE_OF_CONDUCT.md +44 -0
  8. package/CONTRIBUTING.md +24 -0
  9. package/INSTALL.md +623 -0
  10. package/LICENSE +21 -0
  11. package/README.md +118 -2
  12. package/THIRD_PARTY_NOTICES.txt +26 -0
  13. package/dist/ai-judge-UE5UOYOO.js +191 -0
  14. package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
  15. package/dist/allowlist-utils-MZZKKXRD.js +31 -0
  16. package/dist/apply-completion-mode-EM37UZHB.js +110 -0
  17. package/dist/artifact-migrator-WPACNSEP.js +276 -0
  18. package/dist/ask-2OZ4JFQR.js +176 -0
  19. package/dist/auth-6UKBTJUQ.js +185 -0
  20. package/dist/auth-H5KJL6HL.js +165 -0
  21. package/dist/auth-LVJRJDMU.js +12 -0
  22. package/dist/auth-NHZOCNRR.js +94 -0
  23. package/dist/auth-PBXHWXGW.js +95 -0
  24. package/dist/auth-T7Y5DCAH.js +91 -0
  25. package/dist/auth-TVVKCHQW.js +13 -0
  26. package/dist/auth-UHB2UCSS.js +97 -0
  27. package/dist/auth-VACA3VUL.js +98 -0
  28. package/dist/auth-VVLC77S3.js +44 -0
  29. package/dist/auth-liveness-XO4AGDDD.js +53 -0
  30. package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
  31. package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
  32. package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
  33. package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
  34. package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
  35. package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
  36. package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
  37. package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
  38. package/dist/autoloop-success-C32QVWOP.js +93 -0
  39. package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
  40. package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
  41. package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
  42. package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
  43. package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
  44. package/dist/backend-YUTFDZXZ.js +30 -0
  45. package/dist/background-executor-Z3ODTMD7.js +260 -0
  46. package/dist/backlog-75QPEAMW.js +403 -0
  47. package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
  48. package/dist/backup-active-store-XHWLN34M.js +16 -0
  49. package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
  50. package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
  51. package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
  52. package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
  53. package/dist/build-gate-7NKWPY62.js +68 -0
  54. package/dist/build-root-S7DINODY.js +18 -0
  55. package/dist/change-summary-R6CB6VXJ.js +50 -0
  56. package/dist/chunk-24MNKKOM.js +173 -0
  57. package/dist/chunk-2H6VZFIO.js +235 -0
  58. package/dist/chunk-2JIX3EKV.js +39 -0
  59. package/dist/chunk-2KO7HVZM.js +106 -0
  60. package/dist/chunk-2L36U25O.js +100 -0
  61. package/dist/chunk-2NMNFUQA.js +475 -0
  62. package/dist/chunk-2OUAEH67.js +83 -0
  63. package/dist/chunk-2VLJUXJU.js +115 -0
  64. package/dist/chunk-2VUEDIUM.js +77 -0
  65. package/dist/chunk-2Y2IIHPI.js +430 -0
  66. package/dist/chunk-2YRG3LHZ.js +220 -0
  67. package/dist/chunk-2Z5Q3SUY.js +65 -0
  68. package/dist/chunk-32IYXIE5.js +873 -0
  69. package/dist/chunk-33IGPPUI.js +292 -0
  70. package/dist/chunk-352PCJLK.js +283 -0
  71. package/dist/chunk-35J4NE6S.js +226 -0
  72. package/dist/chunk-3C7WBIVK.js +1016 -0
  73. package/dist/chunk-3H3EY6U7.js +1225 -0
  74. package/dist/chunk-3H4MWYIC.js +198 -0
  75. package/dist/chunk-3HHWQR3N.js +2479 -0
  76. package/dist/chunk-3JKNRYEE.js +14 -0
  77. package/dist/chunk-3LECCYZY.js +128 -0
  78. package/dist/chunk-3LOY6YZI.js +852 -0
  79. package/dist/chunk-3M57DADF.js +44 -0
  80. package/dist/chunk-3NOTLVLG.js +3803 -0
  81. package/dist/chunk-3OPP2OZB.js +1480 -0
  82. package/dist/chunk-3P5IN5E7.js +9 -0
  83. package/dist/chunk-3PL22IYZ.js +20 -0
  84. package/dist/chunk-3REDAJUJ.js +14 -0
  85. package/dist/chunk-3S2R4XJI.js +203 -0
  86. package/dist/chunk-3UBZI5DW.js +61 -0
  87. package/dist/chunk-3VV5OBGG.js +308 -0
  88. package/dist/chunk-3WY5TU2O.js +823 -0
  89. package/dist/chunk-43OKHTW4.js +82 -0
  90. package/dist/chunk-45KUJBDI.js +112 -0
  91. package/dist/chunk-47X5LM7B.js +224 -0
  92. package/dist/chunk-4B6FQJ77.js +268 -0
  93. package/dist/chunk-4BTDUT2S.js +42 -0
  94. package/dist/chunk-4DJL2VXA.js +24 -0
  95. package/dist/chunk-4EBSXZ73.js +48 -0
  96. package/dist/chunk-4QKP6I4C.js +294 -0
  97. package/dist/chunk-4U6XK2DG.js +300 -0
  98. package/dist/chunk-4UMRUFBD.js +132 -0
  99. package/dist/chunk-4US4SYJ4.js +569 -0
  100. package/dist/chunk-4ZPLWEZO.js +154 -0
  101. package/dist/chunk-56WPYDJH.js +624 -0
  102. package/dist/chunk-57WRCJJW.js +50 -0
  103. package/dist/chunk-5AW4RMNG.js +24 -0
  104. package/dist/chunk-5EQN56OZ.js +155 -0
  105. package/dist/chunk-5F43TUIN.js +38 -0
  106. package/dist/chunk-5FKHIKTS.js +115 -0
  107. package/dist/chunk-5HDP7XZG.js +91 -0
  108. package/dist/chunk-5KQETTNS.js +152 -0
  109. package/dist/chunk-5PDVN2KO.js +498 -0
  110. package/dist/chunk-5UBIFVRD.js +509 -0
  111. package/dist/chunk-5YDE3Q62.js +46 -0
  112. package/dist/chunk-626AJVMU.js +302 -0
  113. package/dist/chunk-633XZ7UF.js +3583 -0
  114. package/dist/chunk-63DHNHGA.js +236 -0
  115. package/dist/chunk-666656AR.js +568 -0
  116. package/dist/chunk-67YRJPJO.js +19 -0
  117. package/dist/chunk-6CAU7VBA.js +20 -0
  118. package/dist/chunk-6CB3KJRY.js +21 -0
  119. package/dist/chunk-6DBMALXL.js +29 -0
  120. package/dist/chunk-6E25C4IA.js +269 -0
  121. package/dist/chunk-6FIBNIQU.js +73 -0
  122. package/dist/chunk-6G57FYBV.js +189 -0
  123. package/dist/chunk-6KZKRTG3.js +4885 -0
  124. package/dist/chunk-6LAH2UV2.js +525 -0
  125. package/dist/chunk-6PZY2TTU.js +921 -0
  126. package/dist/chunk-6T6NHXK4.js +230 -0
  127. package/dist/chunk-6TBEX4R4.js +432 -0
  128. package/dist/chunk-6TRD7TEE.js +911 -0
  129. package/dist/chunk-6XCP2RAI.js +47 -0
  130. package/dist/chunk-726G6WDL.js +351 -0
  131. package/dist/chunk-75EMT7BF.js +616 -0
  132. package/dist/chunk-762ACDZJ.js +4696 -0
  133. package/dist/chunk-77I6G4CO.js +1 -0
  134. package/dist/chunk-7DJ6SQH3.js +510 -0
  135. package/dist/chunk-7HPCIOMA.js +1383 -0
  136. package/dist/chunk-7HYBSRPZ.js +17503 -0
  137. package/dist/chunk-7KBEOPVB.js +73 -0
  138. package/dist/chunk-7LHJDJB3.js +2222 -0
  139. package/dist/chunk-7NVJMH52.js +306 -0
  140. package/dist/chunk-7RMARDVH.js +33 -0
  141. package/dist/chunk-7SEWU4QG.js +36 -0
  142. package/dist/chunk-7TUTZTI2.js +911 -0
  143. package/dist/chunk-7VBDFPUM.js +340 -0
  144. package/dist/chunk-7VRJHTGZ.js +6406 -0
  145. package/dist/chunk-7YAOUUKK.js +493 -0
  146. package/dist/chunk-AAAODV76.js +860 -0
  147. package/dist/chunk-AB4HUJVQ.js +339 -0
  148. package/dist/chunk-AEQWV746.js +611 -0
  149. package/dist/chunk-AHU3FKPA.js +11639 -0
  150. package/dist/chunk-AISTV5XU.js +226 -0
  151. package/dist/chunk-AJBTKGJZ.js +48 -0
  152. package/dist/chunk-AJJNW7XT.js +248 -0
  153. package/dist/chunk-AJN5Y5YZ.js +160 -0
  154. package/dist/chunk-ALDTFNZ3.js +150 -0
  155. package/dist/chunk-ARARVIZK.js +281 -0
  156. package/dist/chunk-ATBM6AW5.js +53 -0
  157. package/dist/chunk-ATKEKM3E.js +206 -0
  158. package/dist/chunk-AUJLW6JZ.js +92 -0
  159. package/dist/chunk-AY3MHEFM.js +24 -0
  160. package/dist/chunk-B7PYTV72.js +236 -0
  161. package/dist/chunk-B7YRSULE.js +1423 -0
  162. package/dist/chunk-BAWJNMWF.js +339 -0
  163. package/dist/chunk-BBPIILO2.js +88 -0
  164. package/dist/chunk-BD3PGW6A.js +957 -0
  165. package/dist/chunk-BDPYFAB4.js +44 -0
  166. package/dist/chunk-BIUV23HH.js +729 -0
  167. package/dist/chunk-BQ3VAGXH.js +243 -0
  168. package/dist/chunk-BQJOGAWT.js +31 -0
  169. package/dist/chunk-BT7EG7CO.js +68 -0
  170. package/dist/chunk-BV3XQQLU.js +27 -0
  171. package/dist/chunk-BY6ODHNZ.js +78 -0
  172. package/dist/chunk-BZRLSZNV.js +79 -0
  173. package/dist/chunk-C3DH3NCY.js +2773 -0
  174. package/dist/chunk-C3KCMAJP.js +756 -0
  175. package/dist/chunk-C7AGUJ5K.js +267 -0
  176. package/dist/chunk-CBSWKQCX.js +342 -0
  177. package/dist/chunk-CE5OZBYY.js +21 -0
  178. package/dist/chunk-CFCVYHSW.js +373 -0
  179. package/dist/chunk-CHA2HDE5.js +126 -0
  180. package/dist/chunk-CIHWPGS5.js +146 -0
  181. package/dist/chunk-CJBSZX3F.js +127 -0
  182. package/dist/chunk-CLA6QIJM.js +43 -0
  183. package/dist/chunk-CPSVQR2I.js +365 -0
  184. package/dist/chunk-CRPRVNQ4.js +254 -0
  185. package/dist/chunk-CSIE4Y7C.js +1093 -0
  186. package/dist/chunk-CSOPSYDY.js +142 -0
  187. package/dist/chunk-CSXLLEGB.js +205 -0
  188. package/dist/chunk-CTDLERWU.js +293 -0
  189. package/dist/chunk-CULBXMSK.js +110 -0
  190. package/dist/chunk-D2M2Y5ID.js +21 -0
  191. package/dist/chunk-D37WY7SH.js +1060 -0
  192. package/dist/chunk-D4VJFCGZ.js +89 -0
  193. package/dist/chunk-D6WTZMZB.js +48 -0
  194. package/dist/chunk-DBHOWDUH.js +976 -0
  195. package/dist/chunk-DCACX7BV.js +19364 -0
  196. package/dist/chunk-DFHX4XYA.js +128 -0
  197. package/dist/chunk-DIKBRRKE.js +176 -0
  198. package/dist/chunk-DJG5RIOR.js +9 -0
  199. package/dist/chunk-DNIOIISW.js +616 -0
  200. package/dist/chunk-DOA5G7IA.js +315 -0
  201. package/dist/chunk-DP7FKP6W.js +855 -0
  202. package/dist/chunk-DS2JZ677.js +79 -0
  203. package/dist/chunk-DSRLVYBS.js +82 -0
  204. package/dist/chunk-EAEVKNJA.js +209 -0
  205. package/dist/chunk-EEUESN3T.js +575 -0
  206. package/dist/chunk-EJ4LUU5Y.js +73 -0
  207. package/dist/chunk-EJIWTPSZ.js +1063 -0
  208. package/dist/chunk-EJVCDTAN.js +116 -0
  209. package/dist/chunk-EKLUBDBQ.js +627 -0
  210. package/dist/chunk-EN62WLFD.js +849 -0
  211. package/dist/chunk-ENYRCL43.js +26 -0
  212. package/dist/chunk-EP22ZMFO.js +273 -0
  213. package/dist/chunk-EP4SVAVI.js +87 -0
  214. package/dist/chunk-EPAMVGI3.js +1562 -0
  215. package/dist/chunk-EPRMH6HO.js +24 -0
  216. package/dist/chunk-ES6NWJ36.js +4057 -0
  217. package/dist/chunk-ETA2K4KI.js +30 -0
  218. package/dist/chunk-ETAD4NKD.js +31 -0
  219. package/dist/chunk-EUWIIDQ7.js +93 -0
  220. package/dist/chunk-EVE3OD2W.js +141 -0
  221. package/dist/chunk-EXA26FFC.js +459 -0
  222. package/dist/chunk-EXSPAA35.js +244 -0
  223. package/dist/chunk-F55YABKV.js +258 -0
  224. package/dist/chunk-F5DNGU5W.js +27 -0
  225. package/dist/chunk-F622MJYV.js +103 -0
  226. package/dist/chunk-F6NIQD74.js +5111 -0
  227. package/dist/chunk-FAWJTC34.js +43 -0
  228. package/dist/chunk-FAZBRHGG.js +47 -0
  229. package/dist/chunk-FIEUSGNJ.js +39 -0
  230. package/dist/chunk-FKCG7K6H.js +1146 -0
  231. package/dist/chunk-FOVVXV45.js +229 -0
  232. package/dist/chunk-FRBPKBFE.js +30 -0
  233. package/dist/chunk-FSA5BPFI.js +268 -0
  234. package/dist/chunk-FSJ5TTZ3.js +29 -0
  235. package/dist/chunk-FT7SGLXT.js +193 -0
  236. package/dist/chunk-FUUVZZUB.js +62 -0
  237. package/dist/chunk-FYMRVUNZ.js +1697 -0
  238. package/dist/chunk-FYTAA5FG.js +162 -0
  239. package/dist/chunk-G3VB2UCZ.js +3892 -0
  240. package/dist/chunk-G6OIMVXH.js +405 -0
  241. package/dist/chunk-G7PQCXVH.js +228 -0
  242. package/dist/chunk-GAB6UW52.js +1087 -0
  243. package/dist/chunk-GAFJEYLK.js +29 -0
  244. package/dist/chunk-GC4CRMTS.js +642 -0
  245. package/dist/chunk-GFHSIWHL.js +5297 -0
  246. package/dist/chunk-GI5M67YA.js +735 -0
  247. package/dist/chunk-GIJWHUWF.js +191 -0
  248. package/dist/chunk-GO6DRECI.js +1 -0
  249. package/dist/chunk-GOFVBTMN.js +314 -0
  250. package/dist/chunk-GQIP72ZJ.js +13 -0
  251. package/dist/chunk-GQXLXRFA.js +28 -0
  252. package/dist/chunk-GT6HFKJB.js +48 -0
  253. package/dist/chunk-GVJGSSKM.js +174 -0
  254. package/dist/chunk-GWHGTG37.js +36 -0
  255. package/dist/chunk-GZAJHLKO.js +18 -0
  256. package/dist/chunk-GZK62I3N.js +314 -0
  257. package/dist/chunk-H3HGMISG.js +483 -0
  258. package/dist/chunk-H3ISZJMJ.js +403 -0
  259. package/dist/chunk-H4HXVWEC.js +9 -0
  260. package/dist/chunk-H6MJGKAW.js +120 -0
  261. package/dist/chunk-HBTTGVR5.js +353 -0
  262. package/dist/chunk-HBZ322WZ.js +35 -0
  263. package/dist/chunk-HEP3ILP4.js +34 -0
  264. package/dist/chunk-HGFXODKO.js +106 -0
  265. package/dist/chunk-HHWDTB6S.js +59 -0
  266. package/dist/chunk-HKO45NLZ.js +286 -0
  267. package/dist/chunk-HLVTEA6A.js +89 -0
  268. package/dist/chunk-HS3SY6HN.js +160 -0
  269. package/dist/chunk-HT5OCGKN.js +899 -0
  270. package/dist/chunk-HXIIEMGF.js +78 -0
  271. package/dist/chunk-I56TH7VM.js +46 -0
  272. package/dist/chunk-I6FN3R5D.js +352 -0
  273. package/dist/chunk-I7SCBHJN.js +655 -0
  274. package/dist/chunk-IJM4GKAT.js +529 -0
  275. package/dist/chunk-IMEYU36R.js +344 -0
  276. package/dist/chunk-INJ3SS6A.js +936 -0
  277. package/dist/chunk-IUKOPCCE.js +441 -0
  278. package/dist/chunk-IVEPZQJO.js +33 -0
  279. package/dist/chunk-IVZEONFC.js +43 -0
  280. package/dist/chunk-IWBHRSG7.js +936 -0
  281. package/dist/chunk-IYY3HQAD.js +484 -0
  282. package/dist/chunk-J2ICMYDA.js +48 -0
  283. package/dist/chunk-J2QCFLUN.js +505 -0
  284. package/dist/chunk-J5HM6V5P.js +1039 -0
  285. package/dist/chunk-J6NZX6QH.js +398 -0
  286. package/dist/chunk-JBBB2Y47.js +432 -0
  287. package/dist/chunk-JDCZQZU7.js +42 -0
  288. package/dist/chunk-JEDNXGSS.js +169 -0
  289. package/dist/chunk-JHP7OT6K.js +89 -0
  290. package/dist/chunk-JLS65NUQ.js +14 -0
  291. package/dist/chunk-JNMWYMC6.js +38 -0
  292. package/dist/chunk-JNSTWNNW.js +25 -0
  293. package/dist/chunk-JOSK6GEU.js +9564 -0
  294. package/dist/chunk-JQUMSIGC.js +100 -0
  295. package/dist/chunk-JVHGMURQ.js +1106 -0
  296. package/dist/chunk-JYXS47FQ.js +951 -0
  297. package/dist/chunk-K2ZMW2Z5.js +2147 -0
  298. package/dist/chunk-K7FAFORM.js +14 -0
  299. package/dist/chunk-KBJXX3ON.js +333 -0
  300. package/dist/chunk-KHFKVQWY.js +486 -0
  301. package/dist/chunk-KHMEI3DQ.js +173 -0
  302. package/dist/chunk-KHW3UD7B.js +69 -0
  303. package/dist/chunk-KKQKWAMF.js +365 -0
  304. package/dist/chunk-KOKXJV2U.js +455 -0
  305. package/dist/chunk-KQ4NGOJI.js +14 -0
  306. package/dist/chunk-KQYQOXMS.js +342 -0
  307. package/dist/chunk-KTMW65UX.js +14 -0
  308. package/dist/chunk-KUEWV2JH.js +593 -0
  309. package/dist/chunk-KXYDV3HP.js +1523 -0
  310. package/dist/chunk-KZ4JMGZ5.js +96 -0
  311. package/dist/chunk-KZWGBUIO.js +2659 -0
  312. package/dist/chunk-L2CBG5IX.js +1892 -0
  313. package/dist/chunk-L2S235CU.js +314 -0
  314. package/dist/chunk-L32WD57F.js +98 -0
  315. package/dist/chunk-LDJCKAJP.js +123 -0
  316. package/dist/chunk-LDWNBEN7.js +541 -0
  317. package/dist/chunk-LJVH57G2.js +241 -0
  318. package/dist/chunk-LPQ7NZEN.js +209 -0
  319. package/dist/chunk-LRGH3CCV.js +148 -0
  320. package/dist/chunk-LULOGMMM.js +1133 -0
  321. package/dist/chunk-LWGKN2GY.js +8284 -0
  322. package/dist/chunk-LX4F24ZH.js +157 -0
  323. package/dist/chunk-M4BUEXXF.js +839 -0
  324. package/dist/chunk-M6UHOC4I.js +553 -0
  325. package/dist/chunk-MCBNWZNO.js +657 -0
  326. package/dist/chunk-MEMSY6VY.js +223 -0
  327. package/dist/chunk-MHLEZQQI.js +145 -0
  328. package/dist/chunk-MJ6WNSIX.js +192 -0
  329. package/dist/chunk-MKASIKWZ.js +442 -0
  330. package/dist/chunk-MNOVBYFU.js +60 -0
  331. package/dist/chunk-MO2BYAAF.js +2896 -0
  332. package/dist/chunk-MP6P7Y2A.js +180 -0
  333. package/dist/chunk-MQ4TFJAG.js +3365 -0
  334. package/dist/chunk-MT2WAIR3.js +131 -0
  335. package/dist/chunk-MTU7VTNN.js +1122 -0
  336. package/dist/chunk-MULMXX4V.js +150 -0
  337. package/dist/chunk-MW2DWWVA.js +147 -0
  338. package/dist/chunk-MY3ZPUN4.js +151 -0
  339. package/dist/chunk-MYLH2S4G.js +64 -0
  340. package/dist/chunk-MZ5WYKNA.js +28 -0
  341. package/dist/chunk-N3Q2JNPV.js +249 -0
  342. package/dist/chunk-N5DX4JAK.js +12 -0
  343. package/dist/chunk-NAINGKHF.js +35 -0
  344. package/dist/chunk-NEQUQHOX.js +223 -0
  345. package/dist/chunk-NHNPEZS4.js +1408 -0
  346. package/dist/chunk-NHTIXJCR.js +17 -0
  347. package/dist/chunk-NKLPMBNV.js +1154 -0
  348. package/dist/chunk-NMVZ5YES.js +199 -0
  349. package/dist/chunk-NRFRY3GE.js +256 -0
  350. package/dist/chunk-NTD6CSI6.js +1266 -0
  351. package/dist/chunk-NUO7B3SH.js +318 -0
  352. package/dist/chunk-NVOHIJKG.js +1319 -0
  353. package/dist/chunk-NXH3RX7O.js +523 -0
  354. package/dist/chunk-NXZ3DURF.js +257 -0
  355. package/dist/chunk-O26L6F7F.js +209 -0
  356. package/dist/chunk-O5RRIUEP.js +124 -0
  357. package/dist/chunk-O5X6BKAO.js +10 -0
  358. package/dist/chunk-O6ZQUVTR.js +31 -0
  359. package/dist/chunk-O7FE75QZ.js +306 -0
  360. package/dist/chunk-OC7Z6AJQ.js +81 -0
  361. package/dist/chunk-OH7IEZ63.js +307 -0
  362. package/dist/chunk-OI734GRS.js +115 -0
  363. package/dist/chunk-OJBH62DK.js +684 -0
  364. package/dist/chunk-OKJLVSTJ.js +247 -0
  365. package/dist/chunk-OMX5VL43.js +79 -0
  366. package/dist/chunk-ONHAMBN6.js +12 -0
  367. package/dist/chunk-ONYPLIVE.js +753 -0
  368. package/dist/chunk-OSK7QWPD.js +637 -0
  369. package/dist/chunk-OTXLMJKP.js +438 -0
  370. package/dist/chunk-OV5PKQMA.js +213 -0
  371. package/dist/chunk-OXK5HA4B.js +31 -0
  372. package/dist/chunk-OYDWFQA4.js +272 -0
  373. package/dist/chunk-OZAUKWHB.js +22 -0
  374. package/dist/chunk-P5P2EKGH.js +94 -0
  375. package/dist/chunk-P6SSWAKY.js +29 -0
  376. package/dist/chunk-P7CQGPLS.js +30 -0
  377. package/dist/chunk-P7RVCHQV.js +197 -0
  378. package/dist/chunk-PEBWDYIW.js +726 -0
  379. package/dist/chunk-PFCXFO7A.js +9193 -0
  380. package/dist/chunk-PFLDWMX6.js +168 -0
  381. package/dist/chunk-PHZGLQSK.js +15883 -0
  382. package/dist/chunk-PNFTPNSR.js +20 -0
  383. package/dist/chunk-PPHXX4W7.js +175 -0
  384. package/dist/chunk-PPZESPJT.js +262 -0
  385. package/dist/chunk-PRDHI2L5.js +296 -0
  386. package/dist/chunk-PSTKWHHN.js +32 -0
  387. package/dist/chunk-PV53ODOT.js +555 -0
  388. package/dist/chunk-PVJBTI7P.js +24 -0
  389. package/dist/chunk-PXKERBKX.js +100 -0
  390. package/dist/chunk-PZ5AY32C.js +10 -0
  391. package/dist/chunk-Q3DHEZA5.js +85 -0
  392. package/dist/chunk-Q5RZDVQK.js +204 -0
  393. package/dist/chunk-Q64IR2Z2.js +4933 -0
  394. package/dist/chunk-Q6Z5UGPL.js +398 -0
  395. package/dist/chunk-QAMAV4DT.js +627 -0
  396. package/dist/chunk-QBWF4444.js +112 -0
  397. package/dist/chunk-QDKYT4CI.js +22805 -0
  398. package/dist/chunk-QFLBRWCJ.js +126 -0
  399. package/dist/chunk-QILAQACE.js +107 -0
  400. package/dist/chunk-QJ6OEH4K.js +3683 -0
  401. package/dist/chunk-QK3NLYTV.js +320 -0
  402. package/dist/chunk-QOFLUS24.js +535 -0
  403. package/dist/chunk-QOIASNEJ.js +98 -0
  404. package/dist/chunk-QP4UTSOV.js +3587 -0
  405. package/dist/chunk-QP5ST7RI.js +904 -0
  406. package/dist/chunk-QS5AGRZN.js +167 -0
  407. package/dist/chunk-QTAOU53N.js +107 -0
  408. package/dist/chunk-QTCLJHLZ.js +351 -0
  409. package/dist/chunk-QTOLTG4L.js +119 -0
  410. package/dist/chunk-QWDV4OSL.js +142 -0
  411. package/dist/chunk-QWMQ74KD.js +508 -0
  412. package/dist/chunk-QX6LOMMH.js +158 -0
  413. package/dist/chunk-QXBKIHY7.js +238 -0
  414. package/dist/chunk-QXBKTJT6.js +270 -0
  415. package/dist/chunk-QYQOVOLR.js +196 -0
  416. package/dist/chunk-R2CKS6WT.js +73 -0
  417. package/dist/chunk-R2TIYG7U.js +68 -0
  418. package/dist/chunk-R2W3LESH.js +465 -0
  419. package/dist/chunk-R5EXHMUN.js +100 -0
  420. package/dist/chunk-RBBTBCHX.js +289 -0
  421. package/dist/chunk-RBKLOUXA.js +121 -0
  422. package/dist/chunk-RCP2Y6DQ.js +86 -0
  423. package/dist/chunk-RCYEJSD5.js +260 -0
  424. package/dist/chunk-RFDVSI4N.js +134 -0
  425. package/dist/chunk-RGJU63FV.js +1717 -0
  426. package/dist/chunk-RI4EX2QE.js +18 -0
  427. package/dist/chunk-RIMARPCF.js +152 -0
  428. package/dist/chunk-RJ6RSNT6.js +42 -0
  429. package/dist/chunk-RNI2GUIF.js +131 -0
  430. package/dist/chunk-RPHUWJ3K.js +264 -0
  431. package/dist/chunk-RPIMAMV7.js +361 -0
  432. package/dist/chunk-RQFX6YRN.js +49 -0
  433. package/dist/chunk-RRC5BL7B.js +285 -0
  434. package/dist/chunk-RSYTSBOO.js +79 -0
  435. package/dist/chunk-RWQQIKIC.js +35 -0
  436. package/dist/chunk-RZUBOZH2.js +373 -0
  437. package/dist/chunk-S2E632FQ.js +380 -0
  438. package/dist/chunk-S2VQCZO4.js +22 -0
  439. package/dist/chunk-S2XIMQJQ.js +681 -0
  440. package/dist/chunk-S47ISY27.js +183 -0
  441. package/dist/chunk-S6I4KISU.js +142 -0
  442. package/dist/chunk-S6TAFQI5.js +2403 -0
  443. package/dist/chunk-SBBNT5ZJ.js +27 -0
  444. package/dist/chunk-SDJV2DTQ.js +491 -0
  445. package/dist/chunk-SDPIFNLD.js +33 -0
  446. package/dist/chunk-SF6YK3JI.js +835 -0
  447. package/dist/chunk-SK3ZFONO.js +976 -0
  448. package/dist/chunk-SKTFRFQ7.js +1775 -0
  449. package/dist/chunk-SNGM452K.js +2002 -0
  450. package/dist/chunk-STW7OFOA.js +581 -0
  451. package/dist/chunk-SU6UFUQY.js +1351 -0
  452. package/dist/chunk-SVJM3LJA.js +27 -0
  453. package/dist/chunk-SVUNZOPG.js +129 -0
  454. package/dist/chunk-T5XLUYV3.js +34 -0
  455. package/dist/chunk-T7E5JS6N.js +57 -0
  456. package/dist/chunk-TA3N5MMO.js +450 -0
  457. package/dist/chunk-TAJPCXSB.js +13 -0
  458. package/dist/chunk-TDEMDRN2.js +1639 -0
  459. package/dist/chunk-TDSEX5CF.js +24 -0
  460. package/dist/chunk-TGCVL74Y.js +28 -0
  461. package/dist/chunk-TGXXYDL4.js +1012 -0
  462. package/dist/chunk-TIKTYUJO.js +296 -0
  463. package/dist/chunk-TIZAJD4M.js +6029 -0
  464. package/dist/chunk-TLSEZZCG.js +5549 -0
  465. package/dist/chunk-TMBDRHAX.js +19 -0
  466. package/dist/chunk-TRURQUBP.js +1217 -0
  467. package/dist/chunk-TSQ256N5.js +152 -0
  468. package/dist/chunk-TUQMMFEQ.js +2244 -0
  469. package/dist/chunk-U424MJLC.js +81 -0
  470. package/dist/chunk-U4DRS6ZW.js +11 -0
  471. package/dist/chunk-U5KS22IN.js +204 -0
  472. package/dist/chunk-UAPHFJPN.js +673 -0
  473. package/dist/chunk-UB7LQITD.js +1040 -0
  474. package/dist/chunk-UBVRHGAZ.js +237 -0
  475. package/dist/chunk-UDNNAQN5.js +98 -0
  476. package/dist/chunk-UJP3XM54.js +342 -0
  477. package/dist/chunk-UJYDH2MW.js +305 -0
  478. package/dist/chunk-UKMPASIC.js +399 -0
  479. package/dist/chunk-UKUL3EHK.js +763 -0
  480. package/dist/chunk-UPHU64NB.js +1327 -0
  481. package/dist/chunk-URMSZXAD.js +43 -0
  482. package/dist/chunk-US3RT6CY.js +525 -0
  483. package/dist/chunk-UTDEOPKV.js +30 -0
  484. package/dist/chunk-UUUDKV7N.js +13 -0
  485. package/dist/chunk-UVKWU3SM.js +39 -0
  486. package/dist/chunk-V2TVPO3X.js +521 -0
  487. package/dist/chunk-V3KLVK7B.js +398 -0
  488. package/dist/chunk-V3YFFHDR.js +17 -0
  489. package/dist/chunk-VAG6GUBR.js +1162 -0
  490. package/dist/chunk-VBENAHDA.js +1814 -0
  491. package/dist/chunk-VBNCKCCI.js +16 -0
  492. package/dist/chunk-VCGSWN5M.js +1300 -0
  493. package/dist/chunk-VE2O2I7M.js +115 -0
  494. package/dist/chunk-VIFHYTI5.js +31 -0
  495. package/dist/chunk-VJER3FUS.js +5080 -0
  496. package/dist/chunk-VLMDM56Q.js +268 -0
  497. package/dist/chunk-VMFZEVVX.js +99 -0
  498. package/dist/chunk-VNDPQVUZ.js +1853 -0
  499. package/dist/chunk-VSPJRLGA.js +4783 -0
  500. package/dist/chunk-VSWC5ZXI.js +103 -0
  501. package/dist/chunk-VYI4DSZP.js +2017 -0
  502. package/dist/chunk-W4BAIQ6L.js +1212 -0
  503. package/dist/chunk-W7VQAL2K.js +2203 -0
  504. package/dist/chunk-WEI3ZDZH.js +33 -0
  505. package/dist/chunk-WEIUS5NT.js +26 -0
  506. package/dist/chunk-WEW7UFWE.js +546 -0
  507. package/dist/chunk-WGQAXLNA.js +384 -0
  508. package/dist/chunk-WJAL4JL3.js +206 -0
  509. package/dist/chunk-WKFOJ5ZG.js +141 -0
  510. package/dist/chunk-WMWQEFLN.js +2177 -0
  511. package/dist/chunk-WNECKKO2.js +154 -0
  512. package/dist/chunk-WPKLYOAM.js +195 -0
  513. package/dist/chunk-WSH37YQN.js +243 -0
  514. package/dist/chunk-WV3KQDPE.js +84 -0
  515. package/dist/chunk-WZFC4V4U.js +389 -0
  516. package/dist/chunk-XCOIBNET.js +13 -0
  517. package/dist/chunk-XDM65LBX.js +53 -0
  518. package/dist/chunk-XGU6PBO5.js +645 -0
  519. package/dist/chunk-XHOI4V62.js +43 -0
  520. package/dist/chunk-XIBVFMLQ.js +6858 -0
  521. package/dist/chunk-XJ2E7YEJ.js +117 -0
  522. package/dist/chunk-XON4ZCYX.js +69 -0
  523. package/dist/chunk-XQPEWJPI.js +127 -0
  524. package/dist/chunk-XSJTIFUY.js +31 -0
  525. package/dist/chunk-XT7OVWM3.js +64 -0
  526. package/dist/chunk-XTXYIISX.js +1104 -0
  527. package/dist/chunk-XV7NZL4B.js +57 -0
  528. package/dist/chunk-XWIZ6B5S.js +109 -0
  529. package/dist/chunk-XWLIKOBX.js +240 -0
  530. package/dist/chunk-XZOXEIN4.js +156 -0
  531. package/dist/chunk-XZRS7I7W.js +3673 -0
  532. package/dist/chunk-Y4ZDDHB6.js +44 -0
  533. package/dist/chunk-Y733RB7T.js +194 -0
  534. package/dist/chunk-YBHUB4XR.js +14931 -0
  535. package/dist/chunk-YKC75XK4.js +147 -0
  536. package/dist/chunk-YKMXFVK7.js +27 -0
  537. package/dist/chunk-YO5BAJ55.js +225 -0
  538. package/dist/chunk-YPDCLXNZ.js +568 -0
  539. package/dist/chunk-YSQPGVFK.js +116 -0
  540. package/dist/chunk-YTVVTIWJ.js +716 -0
  541. package/dist/chunk-YVCMDVBB.js +698 -0
  542. package/dist/chunk-YYTOA6DP.js +114 -0
  543. package/dist/chunk-Z5WGNULV.js +88 -0
  544. package/dist/chunk-Z6NGVVXG.js +66 -0
  545. package/dist/chunk-Z7CDQNH3.js +20 -0
  546. package/dist/chunk-ZH4G3FU5.js +187 -0
  547. package/dist/chunk-ZJB6SQPA.js +257 -0
  548. package/dist/chunk-ZL6E4HMY.js +42 -0
  549. package/dist/chunk-ZMLOC3DW.js +62 -0
  550. package/dist/chunk-ZORW64QH.js +101 -0
  551. package/dist/chunk-ZVVD2XGV.js +1369 -0
  552. package/dist/cli-utils-ER33ODC5.js +31 -0
  553. package/dist/cli-validation-6CHPTANV.js +146 -0
  554. package/dist/commit-utils-3STAY7C7.js +49 -0
  555. package/dist/compatibility-notice-IHNXR2JR.js +21 -0
  556. package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
  557. package/dist/compete-results-XUM3PJHS.js +318 -0
  558. package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
  559. package/dist/config-4HHE4OIK.js +238 -0
  560. package/dist/config-D6YAZASH.js +10 -0
  561. package/dist/config-MIPRDYYV.js +46 -0
  562. package/dist/config-audit-GA5DCUAI.js +371 -0
  563. package/dist/config-explain-4LMO556H.js +71 -0
  564. package/dist/config-loader-RGP634PC.js +51 -0
  565. package/dist/config-manager-J2UTO635.js +114 -0
  566. package/dist/config-merger-TVUJBZTS.js +14 -0
  567. package/dist/config-validator-H4ZVIE56.js +122 -0
  568. package/dist/cost-HSIV5U2Y.js +85 -0
  569. package/dist/data-migration-state-OAUL2WPQ.js +35 -0
  570. package/dist/db-migrator-NUOW5NKR.js +34 -0
  571. package/dist/default-paths-UILB62BN.js +49 -0
  572. package/dist/discovery-V6GYUJUC.js +53 -0
  573. package/dist/doctor-BBSMCDWE.js +271 -0
  574. package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
  575. package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
  576. package/dist/doctor-purpose-6YJOXPBE.js +30 -0
  577. package/dist/domain-analyzer-S35ABUNA.js +48 -0
  578. package/dist/ensure-initialized-Q6PEU55O.js +78 -0
  579. package/dist/entry.js +922 -0
  580. package/dist/error-parser-core-MZFMFANN.js +70 -0
  581. package/dist/evaluation-context-GQBYDAEV.js +63 -0
  582. package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
  583. package/dist/execution-data-writer-YCG5UH37.js +72 -0
  584. package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
  585. package/dist/execution-preflight-DASJWWQF.js +223 -0
  586. package/dist/execution-roles-KNSNNB2D.js +112 -0
  587. package/dist/exit-code-error-QIY5DOKF.js +18 -0
  588. package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
  589. package/dist/factory-YCYCB7AE.js +209 -0
  590. package/dist/file-utils-ZQYNBQBW.js +45 -0
  591. package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
  592. package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
  593. package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
  594. package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
  595. package/dist/flywheel-executor-JWU43KAY.js +437 -0
  596. package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
  597. package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
  598. package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
  599. package/dist/flywheel-preflight-III2C7TH.js +163 -0
  600. package/dist/flywheel-resume-2O45MS7Y.js +31 -0
  601. package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
  602. package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
  603. package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
  604. package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
  605. package/dist/git-message-style-KCXGMTDT.js +104 -0
  606. package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
  607. package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
  608. package/dist/identity-XLQJ4YJF.js +25 -0
  609. package/dist/index-query-6NUY4FKX.js +37 -0
  610. package/dist/index.js +85588 -0
  611. package/dist/indexer-DL54CM3K.js +44 -0
  612. package/dist/init-OUZ72344.js +245 -0
  613. package/dist/instructions-import-offer-NSYXSITC.js +363 -0
  614. package/dist/instructions-run-NBII7W36.js +114 -0
  615. package/dist/interrupted-work-E4TLKJLE.js +43 -0
  616. package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
  617. package/dist/judge-scoring.json +64 -0
  618. package/dist/lab-mode-YXQ2Y3NG.js +44 -0
  619. package/dist/lab-utils-5QAMDJR5.js +29 -0
  620. package/dist/linux-host-class-YDWQX65O.js +31 -0
  621. package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
  622. package/dist/local-ci-config-VZ4WWDT5.js +48 -0
  623. package/dist/local-executor-DCMDDRX2.js +280 -0
  624. package/dist/logger-5KNKS3O4.js +154 -0
  625. package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
  626. package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
  627. package/dist/merge-batch-RTEEMVDB.js +313 -0
  628. package/dist/merge-batch-members-VIWGVRHP.js +24 -0
  629. package/dist/merge-queue-TIIUSOY6.js +126 -0
  630. package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
  631. package/dist/metadata-UKSMWM6J.js +29 -0
  632. package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
  633. package/dist/model-invocations-db-XTUPPQ34.js +70 -0
  634. package/dist/model-resolver-JFZ2MHOJ.js +39 -0
  635. package/dist/model-resolver-SCQ57KAQ.js +217 -0
  636. package/dist/model-roles-D3OR4NAH.js +110 -0
  637. package/dist/monitor-F4SIU5G2.js +96 -0
  638. package/dist/nested-worktrees-2WUWQ75N.js +23 -0
  639. package/dist/next-S3IRLVER.js +96 -0
  640. package/dist/next-UKPFRXOO.js +148 -0
  641. package/dist/opportunistic-TL4S26VX.js +86 -0
  642. package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
  643. package/dist/package-HJR4QJT4.js +10 -0
  644. package/dist/parse-git-status-7PIYFPS2.js +21 -0
  645. package/dist/path-security-5WJSY56G.js +41 -0
  646. package/dist/paths-JUI7C4KU.js +45 -0
  647. package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
  648. package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
  649. package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
  650. package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
  651. package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
  652. package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
  653. package/dist/preflight-PJHGTJNZ.js +12 -0
  654. package/dist/process-registry-B4ZUCN7T.js +28 -0
  655. package/dist/process-scanner-TU3QHWVB.js +23 -0
  656. package/dist/processes-6FHVGT72.js +28 -0
  657. package/dist/processes-json-fast-YWTMUWXB.js +72 -0
  658. package/dist/profile-HWTC56SG.js +209 -0
  659. package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
  660. package/dist/protected-branches-N36LDBFY.js +71 -0
  661. package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
  662. package/dist/provider-matrix-2KO2V5OM.js +214 -0
  663. package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
  664. package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
  665. package/dist/provider-registry-WCZ65QHC.js +136 -0
  666. package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
  667. package/dist/prune-engine-EOQVWEP6.js +448 -0
  668. package/dist/publication-turn-S42RZTQF.js +44 -0
  669. package/dist/quality-gate-JQMVCKOO.js +72 -0
  670. package/dist/quickstart-3O6FRCFB.js +105 -0
  671. package/dist/readiness-GW6ABRHB.js +139 -0
  672. package/dist/readiness-KFZCXDTT.js +215 -0
  673. package/dist/registry-7XG376SY.js +217 -0
  674. package/dist/repair-helpers-TTV5FEF4.js +117 -0
  675. package/dist/repo-root-YG6MQZDW.js +28 -0
  676. package/dist/resolution-engine-FFEK7HQ2.js +187 -0
  677. package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
  678. package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
  679. package/dist/routing-events-AW6SRZ6G.js +13 -0
  680. package/dist/routing-history-store-FMDDQL6W.js +39 -0
  681. package/dist/routing-planner-C6OHO3EY.js +221 -0
  682. package/dist/runner-KHDNQ5KU.js +1194 -0
  683. package/dist/runner-PIS2BW7Y.js +1801 -0
  684. package/dist/safety-E3JJVSFL.js +67 -0
  685. package/dist/safety-stanza-HE6GXP4G.js +49 -0
  686. package/dist/sandbox-7LBZC5PE.js +72 -0
  687. package/dist/sandbox-ASWCDYZL.js +80 -0
  688. package/dist/schema-extensions.sql +234 -0
  689. package/dist/security-JI22TBEB.js +17 -0
  690. package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
  691. package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
  692. package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
  693. package/dist/shared-utils-34HJZ6JX.js +40 -0
  694. package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
  695. package/dist/sprint-N7JFTEGL.js +403 -0
  696. package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
  697. package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
  698. package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
  699. package/dist/sprint-manager-MU2ZUWN2.js +140 -0
  700. package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
  701. package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
  702. package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
  703. package/dist/storage-access-I3V5LER7.js +31 -0
  704. package/dist/store-XVG64ZR2.js +25 -0
  705. package/dist/task-classifier-MN2DYE5R.js +558 -0
  706. package/dist/task-list-fast-7AT6LTTA.js +112 -0
  707. package/dist/task-manager-LHPBBRRE.js +136 -0
  708. package/dist/task-report-OLPAWBTK.js +64 -0
  709. package/dist/task-type-detector-6RG3YNZF.js +27 -0
  710. package/dist/task-type-resolver-EQHOUIHT.js +64 -0
  711. package/dist/telemetry-5VYSK772.js +212 -0
  712. package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
  713. package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
  714. package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
  715. package/dist/transports-NREZQTJK.js +106 -0
  716. package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
  717. package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
  718. package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
  719. package/dist/user-agents-QLIGPLP2.js +30 -0
  720. package/dist/variant-execution-I4ZZUAWY.js +319 -0
  721. package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
  722. package/dist/variant-request-input-PL37N3AN.js +11 -0
  723. package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
  724. package/dist/variant-worker-COB5YTJX.js +4517 -0
  725. package/dist/variant-worker.js +26 -0
  726. package/dist/variants-XGDSXYG3.js +267 -0
  727. package/dist/verification-command-YSPU4VBH.js +66 -0
  728. package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
  729. package/dist/verify-commands-LOHMOFKC.js +65 -0
  730. package/dist/warning-JSKSFKBT.js +13 -0
  731. package/dist/work-edge-K65NHVWY.js +50 -0
  732. package/dist/work-item-writer-KY5ZWZWW.js +30 -0
  733. package/dist/worker-JNXXEIT6.js +1504 -0
  734. package/dist/worker.js +21 -0
  735. package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
  736. package/dist/worktree-environment-WTVABAQW.js +64 -0
  737. package/dist/worktree-metrics-VZRQI26T.js +66 -0
  738. package/dist/writeback-TQFQBJNU.js +50 -0
  739. package/docs/CLI_REFERENCE.md +8287 -0
  740. package/docs/PROVIDER_SETUP.md +1737 -0
  741. package/docs/README.md +96 -0
  742. package/docs/TROUBLESHOOTING.md +2209 -0
  743. package/docs/getting-started/QUICK_START.md +260 -0
  744. package/docs/getting-started/README.md +198 -0
  745. package/docs/getting-started/SETUP.md +25 -0
  746. package/docs/reference/AGENT_CONTEXT.md +84 -0
  747. package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
  748. package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
  749. package/docs/reference/SECURITY.md +291 -0
  750. package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
  751. package/integrations/bb-plugin-build/README.md +391 -0
  752. package/integrations/bb-plugin-build/app.css +431 -0
  753. package/integrations/bb-plugin-build/app.tsx +6210 -0
  754. package/integrations/bb-plugin-build/package.json +45 -0
  755. package/integrations/bb-plugin-build/server.ts +488 -0
  756. package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
  757. package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
  758. package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
  759. package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
  760. package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
  761. package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
  762. package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
  763. package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
  764. package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
  765. package/integrations/bb-plugin-build/src/contract.ts +718 -0
  766. package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
  767. package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
  768. package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
  769. package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
  770. package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
  771. package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
  772. package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
  773. package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
  774. package/integrations/bb-plugin-build/src/model.ts +1318 -0
  775. package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
  776. package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
  777. package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
  778. package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
  779. package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
  780. package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
  781. package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
  782. package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
  783. package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
  784. package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
  785. package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
  786. package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
  787. package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
  788. package/integrations/bb-plugin-build/src/project-target.ts +45 -0
  789. package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
  790. package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
  791. package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
  792. package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
  793. package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
  794. package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
  795. package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
  796. package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
  797. package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
  798. package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
  799. package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
  800. package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
  801. package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
  802. package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
  803. package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
  804. package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
  805. package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
  806. package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
  807. package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
  808. package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
  809. package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
  810. package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
  811. package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
  812. package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
  813. package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
  814. package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
  815. package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
  816. package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
  817. package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
  818. package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
  819. package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
  820. package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
  821. package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
  822. package/package.json +306 -4
  823. package/schemas/README.md +80 -0
  824. package/schemas/build.config.schema.json +2110 -0
  825. package/schemas/config-v1.schema.json +136 -0
  826. package/schemas/execution-config.schema.json +47 -0
  827. package/schemas/judge-scoring.schema.json +257 -0
  828. package/schemas/provider-config.schema.json +179 -0
  829. package/schemas/telemetry-config.schema.json +79 -0
  830. package/schemas/user-preferences.schema.json +141 -0
  831. package/scripts/assert-node-runtime.mjs +146 -0
  832. package/scripts/preflight-native.mjs +49 -0
  833. package/scripts/setup-git-hooks.mjs +39 -0
  834. package/scripts/sync.sh +2871 -0
  835. package/scripts/write-node-launcher.sh +138 -0
  836. package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
  837. package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
  838. package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
  839. package/templates/.build-project/README.md +41 -0
  840. package/templates/.build-project/agents/README.md +50 -0
  841. package/templates/.build-project/agents/architect.md +10 -0
  842. package/templates/.build-project/agents/code-reviewer.md +10 -0
  843. package/templates/.build-project/agents/debugger.md +10 -0
  844. package/templates/.build-project/agents/documenter.md +10 -0
  845. package/templates/.build-project/agents/test-automator.md +10 -0
  846. package/templates/.build-project/config/build.config.jsonc +64 -0
  847. package/templates/.build-project/config/project-context.template.json +6 -0
  848. package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
  849. package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
  850. package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
  851. package/templates/.build-project/config/task-type-aliases.yaml +29 -0
  852. package/templates/.build-project/gitignore.template +70 -0
  853. package/templates/.build-project/task-types/analysis.yaml +38 -0
  854. package/templates/.build-project/task-types/architecture.yaml +35 -0
  855. package/templates/.build-project/task-types/doc.yaml +34 -0
  856. package/templates/.build-project/task-types/feature.yaml +22 -0
  857. package/templates/.build-project/task-types/general.yaml +4 -0
  858. package/templates/.build-project/task-types/security.yaml +38 -0
  859. package/templates/AGENTS.template.md +85 -0
  860. package/templates/CLAUDE.template.md +1 -0
  861. package/templates/GEMINI.template.md +1 -0
  862. package/templates/builtin-workflows/code-review.yaml +73 -0
  863. package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
  864. package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
  865. package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
  866. package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
  867. package/templates/ci/incident-intake.mjs +787 -0
  868. package/templates/ci/incidents.yml.template +103 -0
  869. package/templates/ci/node.yml.template +17 -0
  870. package/templates/ci/python.yml.template +17 -0
  871. package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
  872. package/templates/guard-kit/build-guard.yml.template +189 -0
  873. package/templates/guard-kit/check.mjs.template +932 -0
  874. package/templates/guard-kit/config.json.template +7 -0
  875. package/templates/guard-kit/policy.json.template +1 -0
  876. package/templates/profiles/README.md +57 -0
  877. package/templates/profiles/frontier-claude.json +23 -0
  878. package/templates/profiles/frontier-codex.json +23 -0
@@ -0,0 +1,4783 @@
1
+ import {
2
+ renderRepairConstraints,
3
+ renderUntrustedCommandOutput
4
+ } from "./chunk-QS5AGRZN.js";
5
+ import {
6
+ extractRequestedVerification,
7
+ hasExplicitVerificationIntent
8
+ } from "./chunk-NMVZ5YES.js";
9
+ import {
10
+ getProviderDetectionMode,
11
+ readAttemptMutationState
12
+ } from "./chunk-666656AR.js";
13
+ import {
14
+ normalizeProviderRunDiagnostics,
15
+ providerDiagnosticsReportFatalRuntimeIssue,
16
+ resolveTokenAccounting
17
+ } from "./chunk-MW2DWWVA.js";
18
+ import {
19
+ evaluateStageCompletionProof,
20
+ hostSurfaceEvaluatorFailureProof,
21
+ mergeIterationRetryFeedback,
22
+ resolveProviderExecutionIntentFromStage,
23
+ resolveStageExecutionIntent,
24
+ stageHasHostDeterministicCompletionSurface,
25
+ toAutoloopCompletionEvidence
26
+ } from "./chunk-QAMAV4DT.js";
27
+ import {
28
+ collectDeclaredStageTargets
29
+ } from "./chunk-45KUJBDI.js";
30
+ import {
31
+ changesHaveVerifiableCodeSurface
32
+ } from "./chunk-OXK5HA4B.js";
33
+ import {
34
+ readCostMetricsUnavailableReason
35
+ } from "./chunk-JQUMSIGC.js";
36
+ import {
37
+ extractAcceptanceCriteria,
38
+ formatAcceptanceCriteriaForPrompt,
39
+ prepareInitialAutoloopPolicy,
40
+ validateRequirements
41
+ } from "./chunk-OTXLMJKP.js";
42
+ import {
43
+ autoloopPresetIncludesGateKind
44
+ } from "./chunk-GQIP72ZJ.js";
45
+ import {
46
+ tailUtf8
47
+ } from "./chunk-K7FAFORM.js";
48
+ import {
49
+ buildCustomGateSelectionRecord,
50
+ formatCustomGateSelectionSummary,
51
+ mergeCustomGateSelectionRecords
52
+ } from "./chunk-J6NZX6QH.js";
53
+ import {
54
+ completionStatusAllowsSuccess,
55
+ isSuccessLikeControllerReason,
56
+ unsafeCompletionAllowsSuccess
57
+ } from "./chunk-IJM4GKAT.js";
58
+ import {
59
+ flattenIterationCommits,
60
+ readWorktreeHead,
61
+ revertFilesToHead
62
+ } from "./chunk-G7PQCXVH.js";
63
+ import {
64
+ detectUnavailableGateVerification,
65
+ gateRunUnavailableSignal,
66
+ isDeliveredUnverified,
67
+ isFailingGateCommandResult,
68
+ isVerificationUnavailableError,
69
+ namesVerificationUnavailable,
70
+ realFailedMustPassGates,
71
+ unavailableGateDisposition
72
+ } from "./chunk-QTAOU53N.js";
73
+ import {
74
+ isFailoverEnabled
75
+ } from "./chunk-SBBNT5ZJ.js";
76
+ import {
77
+ buildFailoverEvent,
78
+ classifyProviderResultFailure,
79
+ hasCommandProviderFailure,
80
+ printProviderSwitchNotice,
81
+ recordCommandProviderFailure
82
+ } from "./chunk-VLMDM56Q.js";
83
+ import {
84
+ automaticProviderSelection,
85
+ isInjectedReviewStage,
86
+ rebindPromptForProvider,
87
+ rebindStageForProvider
88
+ } from "./chunk-YTVVTIWJ.js";
89
+ import {
90
+ generateCompetitionId
91
+ } from "./chunk-DCACX7BV.js";
92
+ import {
93
+ isSimpleArtifactGoalSatisfiedForChanges,
94
+ parseSimpleArtifactGoal
95
+ } from "./chunk-QXBKIHY7.js";
96
+ import {
97
+ FAILURE_POLICY,
98
+ classifyFailure,
99
+ classifyNoWorkFailure
100
+ } from "./chunk-PRDHI2L5.js";
101
+ import {
102
+ normalizeTaskStatus
103
+ } from "./chunk-NAINGKHF.js";
104
+ import {
105
+ isExitCodeError
106
+ } from "./chunk-Y4ZDDHB6.js";
107
+ import {
108
+ isProviderInvocationFailure,
109
+ markProviderFailureStop,
110
+ providerFailureStopMessage
111
+ } from "./chunk-UJYDH2MW.js";
112
+ import {
113
+ getPlanningRoot,
114
+ withIndexRegeneration,
115
+ withPlanningLock
116
+ } from "./chunk-CSXLLEGB.js";
117
+ import {
118
+ getBucketArchivePathByKind,
119
+ getBucketPathByKind
120
+ } from "./chunk-V3KLVK7B.js";
121
+ import {
122
+ decideStageChangeScope
123
+ } from "./chunk-DS2JZ677.js";
124
+ import {
125
+ buildProviderOutputExcerpt
126
+ } from "./chunk-MYLH2S4G.js";
127
+ import {
128
+ emitBoundaryTriggered
129
+ } from "./chunk-633XZ7UF.js";
130
+ import {
131
+ keepInstalledProviders,
132
+ resolveCandidates,
133
+ resolveConfiguredProviderOrder,
134
+ resolveDefaultProvider,
135
+ resolveProviderModel,
136
+ resolveProviderOrder
137
+ } from "./chunk-C3DH3NCY.js";
138
+ import {
139
+ filterWorktreeRunSentinelPaths,
140
+ isWorktreeRunSentinelPath
141
+ } from "./chunk-2NMNFUQA.js";
142
+ import {
143
+ atomicWriteJson
144
+ } from "./chunk-RFDVSI4N.js";
145
+ import {
146
+ parsePorcelainPathsZ
147
+ } from "./chunk-EVE3OD2W.js";
148
+ import {
149
+ readTrackedToolOutputRootsFor,
150
+ shouldExcludeFile
151
+ } from "./chunk-5EQN56OZ.js";
152
+ import {
153
+ detectProjectCapabilities,
154
+ parseFailureOutput,
155
+ resolveBaselineErrorParser,
156
+ runVerifyCommands
157
+ } from "./chunk-AHU3FKPA.js";
158
+ import {
159
+ formatUnverifiedDeliveryLabel
160
+ } from "./chunk-TUQMMFEQ.js";
161
+ import {
162
+ describeVerificationCommand
163
+ } from "./chunk-EPAMVGI3.js";
164
+ import {
165
+ isProjectConfigPath,
166
+ isProjectTestPath,
167
+ languageForProjectPath
168
+ } from "./chunk-RPIMAMV7.js";
169
+ import {
170
+ safeRmDir
171
+ } from "./chunk-ALDTFNZ3.js";
172
+ import {
173
+ resolveExecutionBoundary,
174
+ resolveVerifyCommandTimeoutMs
175
+ } from "./chunk-2VUEDIUM.js";
176
+ import {
177
+ parseGitStatus,
178
+ parseGitStatusPaths
179
+ } from "./chunk-5KQETTNS.js";
180
+ import {
181
+ truncateTail
182
+ } from "./chunk-2VLJUXJU.js";
183
+ import {
184
+ firstDirWithFile,
185
+ resolveRunStateDir,
186
+ resolveRunStateReadDirs
187
+ } from "./chunk-AJN5Y5YZ.js";
188
+ import {
189
+ execGit
190
+ } from "./chunk-MHLEZQQI.js";
191
+ import {
192
+ normalizeEngineId
193
+ } from "./chunk-KKQKWAMF.js";
194
+ import {
195
+ ProviderExecutionError
196
+ } from "./chunk-RRC5BL7B.js";
197
+ import {
198
+ listMarkdownFiles,
199
+ listMarkdownFilesRecursive,
200
+ readMarkdownFile,
201
+ writeMarkdownFile
202
+ } from "./chunk-352PCJLK.js";
203
+ import {
204
+ getAutoloopHistoryLocation
205
+ } from "./chunk-Z5WGNULV.js";
206
+
207
+ // src/automation/flywheel-autoloop-executor.ts
208
+ import * as fs4 from "fs/promises";
209
+ import * as path5 from "path";
210
+ import { performance as performance2 } from "perf_hooks";
211
+ import chalk from "chalk";
212
+ import { execa as execa2 } from "execa";
213
+
214
+ // src/core/loop/autoloop-custom-gate-union.ts
215
+ function dedupKey(command) {
216
+ return command.trim().replace(/\s+/g, " ");
217
+ }
218
+ function unionCustomGateCommands(promptSelection, evidencePlanSelection) {
219
+ const byKey = /* @__PURE__ */ new Map();
220
+ const add = (command) => {
221
+ const key = dedupKey(command);
222
+ if (key.length === 0 || byKey.has(key)) return;
223
+ byKey.set(key, command);
224
+ };
225
+ for (const command of promptSelection?.selectedCommands ?? []) add(command);
226
+ for (const command of evidencePlanSelection?.selectedCommands ?? []) add(command);
227
+ return [...byKey.values()];
228
+ }
229
+ function formatCustomGateSelectionDetailLines(record) {
230
+ if (!record.filtered) {
231
+ return [
232
+ ` Custom gates unchanged: ${record.originalCount} command(s) (timebox allows full evidence plan)`,
233
+ ...record.originalCommands.map((cmd) => ` kept: ${cmd}`)
234
+ ];
235
+ }
236
+ return [
237
+ ` Custom gate selection: original=${record.originalCount} selected=${record.selectedCount} reason=${record.reason ?? "unfiltered"}`,
238
+ ...record.originalCommands.map(
239
+ (cmd) => ` ${record.selectedCommands.includes(cmd) ? "kept" : "dropped"}: ${cmd}`
240
+ )
241
+ ];
242
+ }
243
+ function createCustomGateSelectionReporter(deps) {
244
+ let merged = null;
245
+ return {
246
+ record: () => merged,
247
+ report: (source, selection) => {
248
+ if (selection.originalCommands.length === 0) return;
249
+ const record = buildCustomGateSelectionRecord(selection, {
250
+ source,
251
+ timeboxMs: selection.timeboxMs ?? deps.timeboxMs
252
+ });
253
+ merged = merged === null ? record : mergeCustomGateSelectionRecords(merged, record);
254
+ deps.logEvent(record);
255
+ if (record.filtered) deps.info(`Info: ${formatCustomGateSelectionSummary(record)}`);
256
+ if (!deps.verbose) return;
257
+ for (const line of formatCustomGateSelectionDetailLines(record)) deps.detail(line);
258
+ }
259
+ };
260
+ }
261
+
262
+ // src/orchestrator/autoloop-termination-map.ts
263
+ function mapControllerReasonToFlywheel(reason) {
264
+ switch (reason) {
265
+ case "max_iterations_reached":
266
+ case "timebox_exceeded":
267
+ case "progress_stagnation":
268
+ case "no_op_loop":
269
+ case "duplicate_change_loop":
270
+ case "resource_exhaustion":
271
+ return reason;
272
+ case "identical_error_pattern":
273
+ return "progress_stagnation";
274
+ case "recursion_limit":
275
+ return "resource_exhaustion";
276
+ case "system_error":
277
+ case "ai_judge_recommendation":
278
+ case "max_retries_exceeded":
279
+ return "error";
280
+ case "completion_signal":
281
+ case "semantic_complete":
282
+ case "deterministic_complete":
283
+ return "success";
284
+ case "heuristic_complete":
285
+ return "heuristic_complete";
286
+ case "baseline_blocked":
287
+ return "baseline_blocked";
288
+ case "verification_unavailable":
289
+ return "verification_unavailable";
290
+ case "compilation_errors":
291
+ return "quality_gate_failure";
292
+ case "semantic_criteria_unsatisfied":
293
+ case "completion_unconfirmable":
294
+ return "evidence_unsatisfied";
295
+ default: {
296
+ const exhaustive = reason;
297
+ return exhaustive;
298
+ }
299
+ }
300
+ }
301
+
302
+ // src/core/providers/claude/simple-artifact-tool-policy.ts
303
+ var CLAUDE_SIMPLE_ARTIFACT_TOOLS = ["Read", "Write", "Edit", "Glob", "Grep"];
304
+ var CLAUDE_SIMPLE_ARTIFACT_DISALLOWED_TOOLS = ["Bash"];
305
+ function claudeSimpleArtifactToolPolicy() {
306
+ return {
307
+ allowedTools: [...CLAUDE_SIMPLE_ARTIFACT_TOOLS],
308
+ disallowedTools: [...CLAUDE_SIMPLE_ARTIFACT_DISALLOWED_TOOLS],
309
+ providerOutputSuppression: "claude-disallowed-bash"
310
+ };
311
+ }
312
+
313
+ // src/core/providers/simple-artifact-tool-policy.ts
314
+ var SIMPLE_ARTIFACT_TOOL_POLICIES = {
315
+ claude: claudeSimpleArtifactToolPolicy
316
+ };
317
+ function simpleArtifactToolPolicy(provider) {
318
+ const factory = SIMPLE_ARTIFACT_TOOL_POLICIES[normalizeEngineId(provider)];
319
+ return factory ? factory() : null;
320
+ }
321
+
322
+ // src/automation/flywheel-provider-defaults.ts
323
+ function resolveFlywheelProvider(provider, cwd = process.cwd()) {
324
+ const explicitProvider = provider?.trim();
325
+ return resolveDefaultProvider(
326
+ explicitProvider && explicitProvider.length > 0 ? { provider: explicitProvider } : void 0,
327
+ cwd
328
+ ).value;
329
+ }
330
+ function resolveFlywheelStageProvider(stage, cwd = process.cwd()) {
331
+ return resolveFlywheelProvider(stage.provider, cwd);
332
+ }
333
+
334
+ // src/core/execution/attempt-failure-category.ts
335
+ var TERMINATION_CATEGORIES = /* @__PURE__ */ new Set([
336
+ "user_cancelled",
337
+ "startup_timeout",
338
+ "idle_terminated"
339
+ ]);
340
+ function classifyAttemptFailure(error) {
341
+ if (!(error instanceof ProviderExecutionError)) return classifyNoWorkFailure(error);
342
+ if (error.reason === "command_not_found") return "cli_missing";
343
+ const typed = classifyNoWorkFailure(error);
344
+ if (TERMINATION_CATEGORIES.has(typed)) return typed;
345
+ const fromTypedFields = classifyFailure({ reason: error.reason, statusCode: error.statusCode });
346
+ if (fromTypedFields !== "unknown") return fromTypedFields;
347
+ const fromStreams = classifyProviderResultFailure({
348
+ stdout: error.rawStdout ?? error.stdout,
349
+ stderr: error.rawStderr ?? error.stderr,
350
+ exitCode: error.exitCode
351
+ });
352
+ return fromStreams === "unknown" ? typed : fromStreams;
353
+ }
354
+
355
+ // src/core/quick-mode/provider-attempt-error.ts
356
+ var QuickProviderAttemptError = class extends Error {
357
+ provider;
358
+ /**
359
+ * Whether the failed invocation changed the worktree, from the turn's
360
+ * evidence (provider-turn.ts). Undefined when no evidence was taken, which
361
+ * reads as unknown.
362
+ */
363
+ attemptMutationState;
364
+ constructor(provider, message, cause, attemptMutationState) {
365
+ super(message, cause === void 0 ? void 0 : { cause });
366
+ this.name = "QuickProviderAttemptError";
367
+ this.provider = provider;
368
+ if (attemptMutationState) this.attemptMutationState = attemptMutationState;
369
+ }
370
+ };
371
+ function providerExitAttemptError(provider, result, failure) {
372
+ return new QuickProviderAttemptError(
373
+ provider,
374
+ result.stderr || result.stdout || "Provider execution failed",
375
+ failure?.error,
376
+ failure?.mutationState
377
+ );
378
+ }
379
+ function thrownProviderAttemptError(provider, error, failure) {
380
+ if (isExitCodeError(error)) return error;
381
+ const message = error instanceof Error ? error.message : String(error);
382
+ return new QuickProviderAttemptError(
383
+ provider,
384
+ message,
385
+ failure?.error ?? error,
386
+ failure?.mutationState
387
+ );
388
+ }
389
+
390
+ // src/core/build/provider-failover-turn.ts
391
+ function nextCandidate(cwd, input) {
392
+ const { candidates, attempted, failed } = input;
393
+ if (!keepInstalledProviders([...candidates]).installedFilterApplied) return void 0;
394
+ for (let index = candidates.indexOf(failed) + 1; index < candidates.length; index++) {
395
+ const provider = candidates[index];
396
+ if (attempted.has(provider)) continue;
397
+ let model;
398
+ try {
399
+ model = resolveProviderModel(provider, void 0, cwd).value;
400
+ } catch {
401
+ continue;
402
+ }
403
+ if (!hasCommandProviderFailure(provider, model)) return { provider, model, index };
404
+ }
405
+ return void 0;
406
+ }
407
+ function decideBuildProviderSwitch(input) {
408
+ if (input.mutationState !== "none-observed" || !input.failoverAllowed) return void 0;
409
+ const category = classifyAttemptFailure(input.error);
410
+ if (!FAILURE_POLICY[category].switchProvider || !isFailoverEnabled(input.cwd)) return void 0;
411
+ recordCommandProviderFailure(input.failed.provider, input.failed.model, category);
412
+ input.attempted.add(input.failed.provider);
413
+ const next = nextCandidate(input.cwd, { ...input, failed: input.failed.provider });
414
+ if (!next) return void 0;
415
+ const event = buildFailoverEvent({
416
+ callSite: input.callSite,
417
+ failed: input.failed,
418
+ category,
419
+ next
420
+ });
421
+ return { next: { provider: next.provider, model: next.model }, category, event };
422
+ }
423
+ function switchQuickTurnProvider(options, input) {
424
+ const { error, current, requested } = input;
425
+ if (!options || !(error instanceof QuickProviderAttemptError)) return void 0;
426
+ const decision = decideBuildProviderSwitch({
427
+ cwd: input.cwd,
428
+ callSite: "build",
429
+ failed: { provider: current.id, ...current.model ? { model: current.model } : {} },
430
+ error: error.cause,
431
+ mutationState: error.attemptMutationState,
432
+ failoverAllowed: options.failoverAllowed,
433
+ candidates: options.candidates,
434
+ attempted: options.attempted
435
+ });
436
+ if (!decision) return void 0;
437
+ (options.onSwitch ?? printProviderSwitchNotice)(decision.event);
438
+ const { provider, model } = decision.next;
439
+ return {
440
+ id: provider,
441
+ ...model ? { model } : {},
442
+ requestedProvider: requested.id,
443
+ ...requested.model ? { requestedModel: requested.model } : {},
444
+ failoverFromProvider: current.id,
445
+ failoverReason: decision.category
446
+ };
447
+ }
448
+
449
+ // src/automation/flywheel-stage-failover.ts
450
+ function createStageProviderSwitch(scope, input) {
451
+ if (scope.keepWorkOnProviderFailure !== true) return void 0;
452
+ const { stage, current } = input;
453
+ if (stage.type === "competition" || isInjectedReviewStage(stage)) return void 0;
454
+ const selection = automaticProviderSelection(stage, current, input.unpinned);
455
+ if (!selection) return void 0;
456
+ return {
457
+ cwd: input.cwd,
458
+ selection,
459
+ current: { ...current },
460
+ requested: { ...current },
461
+ attempted: new Set((input.exclude ?? []).filter((p) => Boolean(p))),
462
+ worked: [],
463
+ ...input.retriesSameAttempt ? { history: { mayHaveWorked: false } } : {}
464
+ };
465
+ }
466
+ var subStageSwitches = /* @__PURE__ */ new WeakMap();
467
+ function subStageProviderSwitch(owner, subStageId, create, apply) {
468
+ let byId = subStageSwitches.get(owner);
469
+ if (!byId) {
470
+ byId = /* @__PURE__ */ new Map();
471
+ subStageSwitches.set(owner, byId);
472
+ }
473
+ if (!byId.has(subStageId)) byId.set(subStageId, create());
474
+ const sw = byId.get(subStageId);
475
+ if (!sw) return void 0;
476
+ sw.onSwitch = apply;
477
+ if (sw.record) apply(sw.current);
478
+ return sw;
479
+ }
480
+ function switchProvenance(record) {
481
+ if (!record) return {};
482
+ const { requestedProvider, requestedModel, failoverFromProvider, failoverReason } = record;
483
+ return {
484
+ requestedProvider,
485
+ ...requestedModel ? { requestedModel } : {},
486
+ failoverFromProvider,
487
+ failoverReason
488
+ };
489
+ }
490
+ function switchedVariantFields(sw) {
491
+ if (!sw?.record) return {};
492
+ return { provider: sw.current.provider, model: sw.current.model, ...switchProvenance(sw.record) };
493
+ }
494
+ function stageProviderSwitchFields(sw) {
495
+ return sw?.record ? { providerSwitch: sw.record } : {};
496
+ }
497
+ function withProviderSwitchRecord(result, sw) {
498
+ return sw?.record ? { ...result, providerSwitch: sw.record } : result;
499
+ }
500
+ function durableStageProviderSwitch(sw) {
501
+ if (!sw?.record) return void 0;
502
+ return {
503
+ provider: sw.current.provider,
504
+ ...sw.current.model ? { model: sw.current.model } : {},
505
+ providerSwitch: sw.record,
506
+ attempted: [...sw.attempted]
507
+ };
508
+ }
509
+ function isStringList(value) {
510
+ return Array.isArray(value) && value.every((entry) => typeof entry === "string");
511
+ }
512
+ function isDurableStageProviderSwitch(value) {
513
+ const durable = value;
514
+ const record = durable?.providerSwitch;
515
+ return typeof durable?.provider === "string" && (durable.model === void 0 || typeof durable.model === "string") && isStringList(durable.attempted) && typeof record?.requestedProvider === "string" && typeof record.failoverFromProvider === "string" && typeof record.failoverReason === "string" && (record.ranBy === void 0 || isStringList(record.ranBy));
516
+ }
517
+ function restoreStageProviderSwitch(sw, durable, completedInvocations) {
518
+ if (!sw) return;
519
+ if (!isDurableStageProviderSwitch(durable) || durable.providerSwitch.requestedProvider !== sw.requested.provider) {
520
+ if (completedInvocations > 0 && !sw.worked.includes(sw.current.provider)) {
521
+ sw.worked.push(sw.current.provider);
522
+ }
523
+ return;
524
+ }
525
+ sw.current = { provider: durable.provider, ...durable.model ? { model: durable.model } : {} };
526
+ sw.record = durable.providerSwitch;
527
+ for (const provider of durable.attempted) sw.attempted.add(provider);
528
+ sw.worked = [.../* @__PURE__ */ new Set([...sw.worked, ...durable.providerSwitch.ranBy ?? []])];
529
+ sw.onSwitch?.(sw.current);
530
+ }
531
+ function withStageProviderRun(result, sw) {
532
+ if (!sw?.record) return result;
533
+ return {
534
+ ...result,
535
+ provider: sw.current.provider,
536
+ model: sw.current.model,
537
+ providerSwitch: sw.record
538
+ };
539
+ }
540
+ function stageProviderRun(result) {
541
+ if (!result.provider) return void 0;
542
+ return {
543
+ provider: result.provider,
544
+ ...result.model ? { model: result.model } : {},
545
+ ...result.providerSwitch ? { providerSwitch: result.providerSwitch } : {}
546
+ };
547
+ }
548
+ function stageDeadlineFailure(budget, stageId) {
549
+ if (budget.hasExceededWorkflowTimeLimit?.()) {
550
+ return "Workflow exceeded time limit before provider execution";
551
+ }
552
+ if (budget.hasExceededStageTimeLimit?.(stageId)) {
553
+ return "Stage exceeded time limit before provider execution";
554
+ }
555
+ return void 0;
556
+ }
557
+ function stageLaunchAllowed(budget, stageId, signal) {
558
+ return !signal?.aborted && stageDeadlineFailure(budget, stageId) === void 0;
559
+ }
560
+ function planningOrder(cwd) {
561
+ const kept = resolveCandidates("decomposition", { cwd, preserveFallbackChain: true }).map(
562
+ (candidate) => candidate.provider
563
+ );
564
+ const configured = resolveConfiguredProviderOrder({ cwd, purpose: "decomposition" }).providers;
565
+ const lead = kept[0] !== void 0 && kept[0] !== configured.find((p) => kept.includes(p));
566
+ const ordered = lead ? [kept[0], ...configured] : configured;
567
+ return { ordered, kept: new Set(keepInstalledProviders(kept).providers) };
568
+ }
569
+ function switchCandidates(selection, cwd, failed) {
570
+ let ordered;
571
+ let installed;
572
+ try {
573
+ if (selection.candidates === "planning-order") {
574
+ ({ ordered, kept: installed } = planningOrder(cwd));
575
+ } else {
576
+ ordered = resolveProviderOrder({ cwd }).ordered;
577
+ installed = new Set(keepInstalledProviders(ordered).providers);
578
+ }
579
+ } catch {
580
+ return [];
581
+ }
582
+ return [...new Set(ordered)].filter((provider) => provider === failed || installed.has(provider));
583
+ }
584
+ function launchCost(result) {
585
+ const cost = result.costMetrics?.totalCost;
586
+ return typeof cost === "number" && Number.isFinite(cost) && cost > 0 ? cost : 0;
587
+ }
588
+ function withFailedLaunchCost(result, failedCost) {
589
+ if (failedCost <= 0) return result;
590
+ const metrics = result.costMetrics ?? {
591
+ inputTokens: 0,
592
+ outputTokens: 0,
593
+ totalCost: 0,
594
+ costBreakdown: {
595
+ inputCost: 0,
596
+ outputCost: 0,
597
+ cacheCost: 0,
598
+ toolCosts: 0,
599
+ infrastructureCost: 0
600
+ },
601
+ currency: "USD"
602
+ };
603
+ return { ...result, costMetrics: { ...metrics, totalCost: launchCost(result) + failedCost } };
604
+ }
605
+ function replacementTimeoutMs(budget, startedAtMs) {
606
+ const bounds = [
607
+ ...budget.timeoutMs !== void 0 ? [budget.timeoutMs - (Date.now() - startedAtMs)] : [],
608
+ ...budget.timeLeftMs ? [budget.timeLeftMs()] : []
609
+ ];
610
+ return bounds.length > 0 ? Math.max(0, Math.min(...bounds)) : void 0;
611
+ }
612
+ function carriedMutationState(error) {
613
+ const state = error?.attemptMutationState;
614
+ return state === "none-observed" || state === "observed" || state === "unknown" ? state : void 0;
615
+ }
616
+ async function runWithStageProviderSwitch(sw, launch, options = {}) {
617
+ if (!sw) return launch(void 0, options.timeoutMs);
618
+ const startedAtMs = Date.now();
619
+ let failedCost = 0;
620
+ for (let replacement = false; ; replacement = true) {
621
+ const target = { ...sw.current };
622
+ const timeoutMs = replacement ? replacementTimeoutMs(options, startedAtMs) : options.timeoutMs;
623
+ let result;
624
+ let thrown;
625
+ try {
626
+ result = await launch({ ...target, ...switchProvenance(sw.record) }, timeoutMs);
627
+ } catch (error) {
628
+ thrown = error;
629
+ }
630
+ const state = result?.success ? "observed" : result ? readAttemptMutationState(result) : carriedMutationState(thrown);
631
+ if (state !== "none-observed" && !sw.worked.includes(target.provider)) {
632
+ sw.worked.push(target.provider);
633
+ }
634
+ if (result?.success) {
635
+ if (sw.history) sw.history.mayHaveWorked = true;
636
+ return withFailedLaunchCost(result, failedCost);
637
+ }
638
+ const earlierMayHaveWorked = sw.history?.mayHaveWorked === true;
639
+ if (sw.history && state !== "none-observed") sw.history.mayHaveWorked = true;
640
+ const eligible = state === "none-observed" && !earlierMayHaveWorked && (!result || isProviderInvocationFailure(result)) && (options.canLaunch?.() ?? true) && (replacementTimeoutMs(options, startedAtMs) ?? 1) > 0;
641
+ const decision = eligible ? decideBuildProviderSwitch({
642
+ cwd: sw.cwd,
643
+ callSite: options.callSite ?? "build-stage",
644
+ failed: target,
645
+ error: result ? result.error : thrown,
646
+ mutationState: state,
647
+ failoverAllowed: true,
648
+ candidates: switchCandidates(sw.selection, sw.cwd, target.provider),
649
+ attempted: sw.attempted
650
+ }) : void 0;
651
+ if (!decision) {
652
+ if (!result) throw thrown;
653
+ return withFailedLaunchCost(result, failedCost);
654
+ }
655
+ if (result) failedCost += launchCost(result);
656
+ printProviderSwitchNotice(decision.event);
657
+ sw.record = {
658
+ requestedProvider: sw.requested.provider,
659
+ ...sw.requested.model ? { requestedModel: sw.requested.model } : {},
660
+ failoverFromProvider: target.provider,
661
+ failoverReason: decision.category,
662
+ ranBy: [
663
+ ...sw.worked.filter((provider) => provider !== decision.next.provider),
664
+ decision.next.provider
665
+ ]
666
+ };
667
+ sw.current = { ...decision.next };
668
+ sw.onSwitch?.(sw.current);
669
+ }
670
+ }
671
+ function standardStageProviderSwitch(options, input, onSwitch) {
672
+ const { stage, variantConfig } = input;
673
+ const sw = options.routingHints?.forceProvider ? void 0 : createStageProviderSwitch(options, {
674
+ stage,
675
+ current: { provider: variantConfig.provider, model: variantConfig.model },
676
+ unpinned: "provider-order",
677
+ cwd: process.cwd(),
678
+ retriesSameAttempt: true
679
+ });
680
+ if (sw) {
681
+ sw.onSwitch = (target) => {
682
+ const rebound = rebindStageForProvider(stage, target);
683
+ Object.assign(variantConfig, switchedVariantFields(sw), {
684
+ executionTransport: rebound.providerExecutionTransport ?? options.providerExecutionTransport
685
+ });
686
+ onSwitch(
687
+ target,
688
+ (prompt) => rebindPromptForProvider(prompt, stage.providerSelection, target.provider)
689
+ );
690
+ };
691
+ }
692
+ return {
693
+ run: (launch) => runWithStageProviderSwitch(sw, launch, {
694
+ canLaunch: () => stageLaunchAllowed(input.budget, stage.id, input.signal)
695
+ }),
696
+ get ran() {
697
+ return sw?.record ? sw.current.provider : void 0;
698
+ },
699
+ resultFields: () => stageProviderSwitchFields(sw)
700
+ };
701
+ }
702
+ function reviewRepairProviderSwitch(scope, config, attemptsUsed) {
703
+ const current = {
704
+ provider: config.repairProvider,
705
+ ...config.repairModel ? { model: config.repairModel } : {}
706
+ };
707
+ const sw = createStageProviderSwitch(scope, {
708
+ stage: { ...current, providerSelection: config.repairSelection },
709
+ current,
710
+ unpinned: "provider-order",
711
+ cwd: process.cwd(),
712
+ exclude: [config.verifierProvider],
713
+ // Each attempt edits the same checkpoint: once one may have worked, a later clean failure stops.
714
+ retriesSameAttempt: true
715
+ });
716
+ if (sw?.history && (attemptsUsed ?? 0) > 0) sw.history.mayHaveWorked = true;
717
+ return sw;
718
+ }
719
+
720
+ // src/automation/autoloop-provider-iteration.ts
721
+ function resolveAutoloopWorkspaceMode(provider, executionIntent) {
722
+ if (executionIntent !== "code" && executionIntent !== "mixed") return void 0;
723
+ const detection = getProviderDetectionMode(provider);
724
+ if (!detection.emitsJsonl) return void 0;
725
+ return "enhanced";
726
+ }
727
+ async function executeProviderIteration(opts) {
728
+ const provider = resolveFlywheelStageProvider(opts.stage, opts.worktreePath);
729
+ const executionIntent = resolveProviderExecutionIntentFromStage(opts.stage);
730
+ const workspaceMode = resolveAutoloopWorkspaceMode(provider, executionIntent);
731
+ const { applyFlywheelModelDefaults } = await import("./flywheel-model-defaults-UHAKTZT3.js");
732
+ const model = applyFlywheelModelDefaults(opts.stage, provider);
733
+ const simpleArtifactGoal = parseSimpleArtifactGoal(opts.prompt);
734
+ const simpleArtifactTools = simpleArtifactGoal !== void 0 ? simpleArtifactToolPolicy(provider) : null;
735
+ const variantConfig = {
736
+ provider,
737
+ model,
738
+ executionMode: "local",
739
+ executionIntent,
740
+ ...opts.stage.providerExecutionTransport ?? opts.defaultProviderExecutionTransport ? {
741
+ executionTransport: opts.stage.providerExecutionTransport ?? opts.defaultProviderExecutionTransport
742
+ } : {},
743
+ ...workspaceMode ? { workspaceMode } : {},
744
+ // Force basic CLI execution against the provided stage worktree.
745
+ isBasicCompetition: true,
746
+ workflowWorktreePath: opts.worktreePath,
747
+ disableAutoCommit: true,
748
+ structuredFileTargets: collectDeclaredStageTargets(opts.stage),
749
+ ...simpleArtifactTools ? {
750
+ allowedTools: simpleArtifactTools.allowedTools,
751
+ tools: simpleArtifactTools.allowedTools,
752
+ disallowedTools: simpleArtifactTools.disallowedTools,
753
+ providerOutputSuppression: simpleArtifactTools.providerOutputSuppression
754
+ } : {},
755
+ ...typeof opts.timeoutMs === "number" && opts.timeoutMs > 0 ? { timeoutMs: Math.max(1, Math.floor(opts.timeoutMs)) } : {},
756
+ ...opts.provenance
757
+ };
758
+ return opts.abortSignal ? opts.executor.executeVariant(generateCompetitionId(), variantConfig, opts.prompt, {
759
+ abortSignal: opts.abortSignal
760
+ }) : opts.executor.executeVariant(generateCompetitionId(), variantConfig, opts.prompt);
761
+ }
762
+ function autoloopProviderSwitch(ctx) {
763
+ if (ctx.keepWorkOnProviderFailure !== true) return void 0;
764
+ const sw = createStageProviderSwitch(ctx, {
765
+ stage: ctx.stage,
766
+ current: {
767
+ provider: resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath),
768
+ ...ctx.stage.model ? { model: ctx.stage.model } : {}
769
+ },
770
+ unpinned: "provider-order",
771
+ cwd: process.cwd()
772
+ });
773
+ if (sw) {
774
+ sw.onSwitch = (target) => {
775
+ ctx.stage = rebindStageForProvider(ctx.stage, target);
776
+ };
777
+ }
778
+ return sw;
779
+ }
780
+ function runSwitchingProviderIteration(sw, opts) {
781
+ return runWithStageProviderSwitch(
782
+ sw,
783
+ (_target, timeoutMs) => {
784
+ const stage = opts.stage();
785
+ const { provider: _provider, model: _model, ...provenance } = switchedVariantFields(sw);
786
+ const prompt = sw?.record ? rebindPromptForProvider(opts.prompt, stage.providerSelection, sw.current.provider) : opts.prompt;
787
+ return executeProviderIteration({ ...opts, stage, prompt, timeoutMs, provenance });
788
+ },
789
+ {
790
+ ...opts.timeoutMs !== void 0 ? { timeoutMs: opts.timeoutMs } : {},
791
+ timeLeftMs: opts.loopTimeLeftMs,
792
+ canLaunch: () => !opts.abortSignal?.aborted
793
+ }
794
+ );
795
+ }
796
+
797
+ // src/automation/autoloop-termination.ts
798
+ var AutoloopTerminationError = class extends Error {
799
+ terminationSignal;
800
+ iterationFingerprint;
801
+ constructor(message, terminationSignal, iterationFingerprint) {
802
+ super(message);
803
+ this.name = "AutoloopTerminationError";
804
+ this.terminationSignal = terminationSignal;
805
+ this.iterationFingerprint = iterationFingerprint;
806
+ }
807
+ };
808
+ function policyForTerminationReason(reason) {
809
+ if (reason === "max_iterations_reached") return "max_iterations";
810
+ if (reason === "timebox_exceeded") return "timebox";
811
+ if (reason === "quality_gate_failure" || reason === "baseline_blocked" || reason === "allowlist_violation" || reason === "tests_required_not_met") {
812
+ return "quality_gate";
813
+ }
814
+ if (reason === "resource_exhaustion") return "resource_limit";
815
+ if (reason === "progress_stagnation") return "provider_pivot";
816
+ if (reason === "success" || reason === "heuristic_complete") return "success";
817
+ return "goal_gate";
818
+ }
819
+ function inferFailureTerminationReason(error, iterationHistory = []) {
820
+ const lastIterationSignal = iterationHistory.at(-1)?.terminationSignal;
821
+ if (lastIterationSignal) {
822
+ return lastIterationSignal;
823
+ }
824
+ if (error instanceof AutoloopTerminationError) {
825
+ return error.terminationSignal;
826
+ }
827
+ return "error";
828
+ }
829
+
830
+ // src/automation/autoloop-termination-events.ts
831
+ function createAutoloopEventRecorder() {
832
+ const events = [];
833
+ return {
834
+ events,
835
+ emit(event) {
836
+ const recorded = {
837
+ ...event,
838
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
839
+ };
840
+ events.push(recorded);
841
+ return recorded;
842
+ }
843
+ };
844
+ }
845
+
846
+ // src/automation/flywheel-autoloop-durability.ts
847
+ import { createHash, randomUUID } from "crypto";
848
+ import * as fs from "fs/promises";
849
+ import * as path2 from "path";
850
+ import { execa } from "execa";
851
+
852
+ // src/automation/flywheel-autoloop-trajectory.ts
853
+ import * as path from "path";
854
+ var MAX_CHANGED_FILES = 200;
855
+ var MAX_PROVIDER_OUTPUT_BYTES = 4e3;
856
+ var MAX_OUTPUT_TAIL_BYTES = 1500;
857
+ var MAX_POLICY_EVENTS = 100;
858
+ var boundedText = (value) => typeof value === "string" ? tailUtf8(value, MAX_OUTPUT_TAIL_BYTES) : void 0;
859
+ var boundedList = (value) => Array.isArray(value) && value.every((item) => typeof item === "string") ? value.slice(0, MAX_CHANGED_FILES).map((item) => boundedText(item) ?? "") : void 0;
860
+ function buildAutoloopResumeFeedback(record) {
861
+ const results = record.qualityGateCommandResults;
862
+ const gate = results?.find(isFailingGateCommandResult) ?? results?.at(-1);
863
+ return {
864
+ iteration: record.iteration,
865
+ error: boundedText(record.error),
866
+ qualityGatePassed: record.qualityGatePassed,
867
+ qualityGateError: boundedText(record.qualityGateError),
868
+ qualityGateCommandResults: gate && [
869
+ {
870
+ command: boundedText(gate.command) ?? "",
871
+ exitCode: gate.exitCode,
872
+ durationMs: 0,
873
+ outputTail: boundedText(gate.outputTail)
874
+ }
875
+ ],
876
+ changedFiles: boundedList(record.changedFiles) ?? [],
877
+ allowlistDrift: record.allowlistDrift,
878
+ allowlistViolations: boundedList(record.allowlistViolations),
879
+ allowlistRevertedFiles: boundedList(record.allowlistRevertedFiles)
880
+ };
881
+ }
882
+ function parseAutoloopResumeFeedback(input) {
883
+ if (!input || typeof input !== "object") return void 0;
884
+ const o = input;
885
+ if (typeof o.iteration !== "number" || !Number.isFinite(o.iteration)) return void 0;
886
+ const gate = Array.isArray(o.qualityGateCommandResults) ? o.qualityGateCommandResults[0] : void 0;
887
+ const flag = (v) => typeof v === "boolean" ? v : void 0;
888
+ return {
889
+ iteration: o.iteration,
890
+ error: boundedText(o.error),
891
+ qualityGatePassed: flag(o.qualityGatePassed),
892
+ qualityGateError: boundedText(o.qualityGateError),
893
+ qualityGateCommandResults: gate && typeof gate.exitCode === "number" ? [
894
+ {
895
+ command: boundedText(gate.command) ?? "",
896
+ exitCode: gate.exitCode,
897
+ durationMs: 0,
898
+ outputTail: boundedText(gate.outputTail)
899
+ }
900
+ ] : void 0,
901
+ changedFiles: boundedList(o.changedFiles) ?? [],
902
+ allowlistDrift: flag(o.allowlistDrift),
903
+ allowlistViolations: boundedList(o.allowlistViolations),
904
+ allowlistRevertedFiles: boundedList(o.allowlistRevertedFiles)
905
+ };
906
+ }
907
+ function safeFilename(input) {
908
+ return input.replace(/[^a-zA-Z0-9-_]/g, "_");
909
+ }
910
+ function detectCompletionSignal(events) {
911
+ if (!events || events.length === 0) return false;
912
+ return events.some((event) => {
913
+ const details = event.details;
914
+ return details?.completionSignalDetected === true;
915
+ });
916
+ }
917
+ function semanticValidationStatusFor(record) {
918
+ if (record.aiValidationShortCircuited === true) return "short_circuited";
919
+ return "unknown";
920
+ }
921
+ function buildIteration(record) {
922
+ const failing = record.qualityGateCommandResults?.find(isFailingGateCommandResult);
923
+ const unavailable = record.qualityGateCommandResults?.find((r) => r.unavailableReason);
924
+ const changedFiles = record.changedFiles ?? [];
925
+ const policyEvents = record.policyEvents;
926
+ return {
927
+ iteration: record.iteration,
928
+ success: record.success,
929
+ providerExecutionStatus: record.providerExecutionStatus,
930
+ providerOutputStatus: record.providerOutputStatus,
931
+ providerStatusDetail: record.providerStatusDetail,
932
+ durationMs: record.durationMs,
933
+ cost: record.cost,
934
+ changedFiles: changedFiles.slice(0, MAX_CHANGED_FILES),
935
+ changedFileCount: changedFiles.length,
936
+ progressScore: record.progressScore,
937
+ hadDiff: record.progressMetrics?.hadDiff,
938
+ wasNoOp: record.progressMetrics?.wasNoOp,
939
+ qualityGatePassed: record.qualityGatePassed,
940
+ qualityGateError: record.qualityGateError,
941
+ failingCommand: failing?.command,
942
+ failingExitCode: failing?.exitCode,
943
+ failingOutputTail: tailUtf8(failing?.outputTail, MAX_OUTPUT_TAIL_BYTES),
944
+ unavailableCommand: unavailable?.command,
945
+ unavailableReason: unavailable?.unavailableReason,
946
+ allowlistDrift: record.allowlistDrift,
947
+ allowlistViolationCount: record.allowlistViolations?.length,
948
+ repairAttempted: record.repairAttempted,
949
+ repairSuccesses: record.repairSuccesses,
950
+ testsCreated: record.testsCreated,
951
+ aiValidationShortCircuited: record.aiValidationShortCircuited,
952
+ completionEvidence: record.completionEvidence,
953
+ completionSignalDetected: detectCompletionSignal(policyEvents),
954
+ semanticValidationStatus: semanticValidationStatusFor(record),
955
+ error: record.error,
956
+ providerOutputExcerpt: tailUtf8(record.providerOutputText, MAX_PROVIDER_OUTPUT_BYTES)
957
+ };
958
+ }
959
+ function buildAutoloopTrajectory(input) {
960
+ const { result, summary } = input;
961
+ const iterations = (result.iterationHistory ?? []).map(buildIteration);
962
+ const policyEvents = result.policyEvents ? result.policyEvents.slice(-MAX_POLICY_EVENTS) : void 0;
963
+ const completionEvidence = iterations.slice().reverse().find((iteration) => iteration.completionEvidence !== void 0)?.completionEvidence;
964
+ return {
965
+ schemaVersion: 1,
966
+ runId: summary.runId,
967
+ taskId: summary.taskId,
968
+ workflowId: input.workflowId,
969
+ stageId: input.stage.id,
970
+ startedAt: summary.startedAt,
971
+ completedAt: summary.completedAt,
972
+ durationMs: summary.durationMs,
973
+ provider: summary.provider,
974
+ model: summary.model,
975
+ terminationReason: result.terminationReason,
976
+ outcome: summary.outcome,
977
+ success: summary.success,
978
+ loopSuccess: result.loopSuccess,
979
+ totalIterations: result.totalIterations,
980
+ finalProgressScore: result.finalProgressScore,
981
+ completionEvidence,
982
+ totalCost: summary.totalCost,
983
+ noOpIterationCount: summary.noOpIterationCount,
984
+ repairAttempts: summary.repairAttempts,
985
+ repairSuccesses: summary.repairSuccesses,
986
+ aiValidationShortCircuited: summary.aiValidationShortCircuited,
987
+ iterationHistoryArtifact: result.iterationHistoryArtifact,
988
+ resultArtifact: result.resultArtifact,
989
+ failureArtifact: result.failureArtifact,
990
+ iterations,
991
+ policyEvents
992
+ };
993
+ }
994
+ function getAutoloopTrajectoryPath(input) {
995
+ const root = input.projectRoot ?? getPlanningRoot();
996
+ return path.join(
997
+ resolveRunStateDir(path.join(root, ".build-project")),
998
+ "flywheel",
999
+ safeFilename(input.workflowId),
1000
+ "autoloop",
1001
+ `${safeFilename(input.stageId)}.trajectory.json`
1002
+ );
1003
+ }
1004
+ async function persistAutoloopTrajectory(input) {
1005
+ const trajectory = buildAutoloopTrajectory(input);
1006
+ const workflowId = input.workflowId;
1007
+ if (!workflowId) {
1008
+ return { trajectory, path: null };
1009
+ }
1010
+ const filePath = getAutoloopTrajectoryPath({
1011
+ workflowId,
1012
+ stageId: input.stage.id
1013
+ });
1014
+ try {
1015
+ await atomicWriteJson(filePath, trajectory);
1016
+ return { trajectory, path: filePath };
1017
+ } catch (err) {
1018
+ console.warn(
1019
+ `[flywheel-autoloop-trajectory] Persist failed: ${err instanceof Error ? err.message : String(err)}`
1020
+ );
1021
+ return { trajectory, path: null };
1022
+ }
1023
+ }
1024
+
1025
+ // src/automation/flywheel-autoloop-durability.ts
1026
+ async function computeWorktreeFingerprint(worktreePath) {
1027
+ try {
1028
+ const head = await execa("git", ["rev-parse", "HEAD"], {
1029
+ cwd: worktreePath,
1030
+ stdio: "pipe",
1031
+ timeout: 3e4,
1032
+ reject: false
1033
+ });
1034
+ const headSha = head.stdout.trim();
1035
+ if ((head.exitCode ?? 1) !== 0 || headSha.length === 0) return void 0;
1036
+ const runGit = async (args) => {
1037
+ const result = await execa("git", args, {
1038
+ cwd: worktreePath,
1039
+ stdio: "pipe",
1040
+ timeout: 3e4,
1041
+ reject: false
1042
+ });
1043
+ if ((result.exitCode ?? 1) !== 0) {
1044
+ throw new Error(`git ${args[0] ?? ""} failed while fingerprinting`);
1045
+ }
1046
+ return result.stdout;
1047
+ };
1048
+ const [porcelainZ, unstagedDiff, stagedDiff] = await Promise.all([
1049
+ // -z gives NUL-delimited entries with unquoted paths, which is robust for
1050
+ // extracting the changed-path set used by the resume superset check.
1051
+ runGit(["status", "--porcelain", "--untracked-files=all", "-z"]),
1052
+ runGit(["diff", "--binary", "--no-ext-diff"]),
1053
+ runGit(["diff", "--cached", "--binary", "--no-ext-diff"])
1054
+ ]);
1055
+ const changedPaths = parsePorcelainPathsZ(porcelainZ);
1056
+ const digest = createHash("sha256").update("status-z\0").update(porcelainZ).update("\0unstaged\0").update(unstagedDiff.replace(/\r/g, "")).update("\0staged\0").update(stagedDiff.replace(/\r/g, ""));
1057
+ const root = path2.resolve(worktreePath);
1058
+ for (const relativePath of changedPaths) {
1059
+ const absolutePath = path2.resolve(root, relativePath);
1060
+ const insideRoot = absolutePath === root || !path2.relative(root, absolutePath).startsWith("..") && !path2.isAbsolute(path2.relative(root, absolutePath));
1061
+ if (!insideRoot) throw new Error(`Dirty path escapes worktree: ${relativePath}`);
1062
+ digest.update("\0path\0").update(relativePath);
1063
+ try {
1064
+ const stat3 = await fs.lstat(absolutePath);
1065
+ if (stat3.isSymbolicLink()) {
1066
+ digest.update("\0symlink\0").update(await fs.readlink(absolutePath));
1067
+ } else if (stat3.isFile()) {
1068
+ digest.update("\0file\0").update(String(stat3.mode & 73)).update("\0");
1069
+ digest.update(await fs.readFile(absolutePath));
1070
+ } else {
1071
+ throw new Error(`Unsupported dirty path type: ${relativePath}`);
1072
+ }
1073
+ } catch (error) {
1074
+ if (error.code === "ENOENT") {
1075
+ digest.update("\0missing\0");
1076
+ } else {
1077
+ throw error;
1078
+ }
1079
+ }
1080
+ }
1081
+ return { headSha, dirtyDigest: digest.digest("hex"), changedPaths };
1082
+ } catch {
1083
+ return void 0;
1084
+ }
1085
+ }
1086
+ function worktreeFingerprintExactlyMatches(recorded, current) {
1087
+ return Boolean(
1088
+ recorded && current && recorded.headSha === current.headSha && recorded.dirtyDigest === current.dirtyDigest
1089
+ );
1090
+ }
1091
+ function worktreeFingerprintResumable(recorded, current) {
1092
+ if (!recorded || !current) return true;
1093
+ if (recorded.headSha !== current.headSha) return false;
1094
+ if (recorded.dirtyDigest === current.dirtyDigest) return true;
1095
+ if (recorded.changedPaths === void 0 || current.changedPaths === void 0) {
1096
+ return false;
1097
+ }
1098
+ const currentPaths = new Set(current.changedPaths);
1099
+ return recorded.changedPaths.every((p) => currentPaths.has(p));
1100
+ }
1101
+ function safeFilename2(input) {
1102
+ return input.replace(/[^a-zA-Z0-9-_]/g, "_");
1103
+ }
1104
+ function isValidOptionalFingerprint(value) {
1105
+ if (value === void 0) return true;
1106
+ if (!value || typeof value !== "object") return false;
1107
+ const fp = value;
1108
+ if (typeof fp.headSha !== "string" || typeof fp.dirtyDigest !== "string") return false;
1109
+ if (fp.changedPaths !== void 0) {
1110
+ if (!Array.isArray(fp.changedPaths)) return false;
1111
+ if (!fp.changedPaths.every((p) => typeof p === "string")) return false;
1112
+ }
1113
+ return true;
1114
+ }
1115
+ function isDurableAutoloopRunRecord(input) {
1116
+ if (!input || typeof input !== "object") return false;
1117
+ const obj = input;
1118
+ return obj.schemaVersion === 1 && typeof obj.recordedAt === "string" && typeof obj.workflowId === "string" && typeof obj.stageId === "string" && obj.stageType === "autoloop" && typeof obj.validationPassed === "boolean" && typeof obj.loopSuccess === "boolean" && typeof obj.terminationReason === "string" && typeof obj.totalIterations === "number" && typeof obj.finalProgressScore === "number" && typeof obj.cost === "number" && isValidOptionalFingerprint(obj.worktreeFingerprint);
1119
+ }
1120
+ function isDurableAutoloopIterationSpill(input) {
1121
+ if (!input || typeof input !== "object") return false;
1122
+ const obj = input;
1123
+ return obj.schemaVersion === 1 && typeof obj.updatedAt === "string" && typeof obj.workflowId === "string" && typeof obj.stageId === "string" && typeof obj.writer === "string" && (obj.runStartedAtMs === void 0 || typeof obj.runStartedAtMs === "number" && Number.isFinite(obj.runStartedAtMs)) && isValidOptionalFingerprint(obj.worktreeFingerprint) && Array.isArray(obj.iterations);
1124
+ }
1125
+ function pickFailureFromIterations(result) {
1126
+ const last = result.iterationHistory.at(-1);
1127
+ if (!last) return void 0;
1128
+ const failing = last.qualityGateCommandResults?.find(isFailingGateCommandResult);
1129
+ const unavailable = last.qualityGateCommandResults?.find((r) => r.unavailableReason);
1130
+ const allowlistViolations = (last.allowlistViolations?.length ? last.allowlistViolations : void 0) ?? result.iterationHistory.find((r) => r.allowlistViolations && r.allowlistViolations.length > 0)?.allowlistViolations;
1131
+ const allowlistRevertedFiles = (last.allowlistRevertedFiles?.length ? last.allowlistRevertedFiles : void 0) ?? result.iterationHistory.find(
1132
+ (r) => r.allowlistRevertedFiles && r.allowlistRevertedFiles.length > 0
1133
+ )?.allowlistRevertedFiles;
1134
+ const changedFiles = last.changedFiles?.length ? last.changedFiles : void 0;
1135
+ if (result.validationPassed && result.loopSuccess && !last.allowlistDrift && last.qualityGatePassed !== false) {
1136
+ return void 0;
1137
+ }
1138
+ return {
1139
+ iteration: last.iteration,
1140
+ qualityGateError: last.qualityGateError,
1141
+ failingCommand: failing?.command,
1142
+ failingExitCode: failing?.exitCode,
1143
+ outputTail: failing?.outputTail,
1144
+ unavailableCommand: unavailable?.command,
1145
+ unavailableReason: unavailable?.unavailableReason,
1146
+ allowlistViolations,
1147
+ allowlistRevertedFiles,
1148
+ changedFiles
1149
+ };
1150
+ }
1151
+ function buildDurableAutoloopRunRecord(input) {
1152
+ const recordedAt = input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString();
1153
+ const failure = input.result.validationPassed ? void 0 : pickFailureFromIterations(input.result);
1154
+ return {
1155
+ schemaVersion: 1,
1156
+ recordedAt,
1157
+ workflowId: input.workflowId,
1158
+ stageId: input.stage.id,
1159
+ stageType: "autoloop",
1160
+ validationPassed: input.result.validationPassed,
1161
+ loopSuccess: input.result.loopSuccess,
1162
+ terminationReason: input.result.terminationReason,
1163
+ totalIterations: input.result.totalIterations,
1164
+ finalProgressScore: input.result.finalProgressScore,
1165
+ cost: input.result.cost,
1166
+ outcomeMetadata: input.result.outcomeMetadata,
1167
+ ...input.worktreeFingerprint ? { worktreeFingerprint: input.worktreeFingerprint } : {},
1168
+ allowlist: input.stage.allowlist,
1169
+ allowlistMode: input.stage.allowlistMode,
1170
+ ...input.providerSwitch ? { providerSwitch: input.providerSwitch } : {},
1171
+ ...failure ? { failure } : {}
1172
+ };
1173
+ }
1174
+ async function writeJsonAtomic(filePath, json) {
1175
+ const dir = path2.dirname(filePath);
1176
+ const base = path2.basename(filePath);
1177
+ const tmpPath = path2.join(dir, `${base}.tmp-${process.pid}-${Date.now()}-${randomUUID()}`);
1178
+ await fs.mkdir(dir, { recursive: true });
1179
+ const file = await fs.open(tmpPath, "w");
1180
+ try {
1181
+ await file.writeFile(JSON.stringify(json, null, 2), "utf-8");
1182
+ await file.sync();
1183
+ } finally {
1184
+ await file.close().catch(() => {
1185
+ });
1186
+ }
1187
+ try {
1188
+ await fs.rename(tmpPath, filePath);
1189
+ } catch (renameError) {
1190
+ await fs.unlink(tmpPath).catch(() => {
1191
+ });
1192
+ throw renameError;
1193
+ }
1194
+ try {
1195
+ const dirHandle = await fs.open(dir, "r");
1196
+ try {
1197
+ await dirHandle.sync();
1198
+ } finally {
1199
+ await dirHandle.close().catch(() => {
1200
+ });
1201
+ }
1202
+ } catch {
1203
+ }
1204
+ }
1205
+ function taskIdMatchesBasename(basename2, taskId) {
1206
+ if (!basename2.startsWith(taskId)) return false;
1207
+ const rest = basename2.slice(taskId.length);
1208
+ return rest.length === 0 || /^[^a-zA-Z0-9]/.test(rest);
1209
+ }
1210
+ async function findTaskFilePath(taskId) {
1211
+ const planningRoot = getPlanningRoot();
1212
+ const tasksDir = getBucketPathByKind("task", planningRoot);
1213
+ const activeFiles = await listMarkdownFiles(tasksDir).catch(() => []);
1214
+ const active = activeFiles.find((f) => taskIdMatchesBasename(path2.basename(f), taskId));
1215
+ if (active) return active;
1216
+ const archiveDir = getBucketArchivePathByKind("task", planningRoot);
1217
+ const archivedFiles = await listMarkdownFilesRecursive(archiveDir).catch(() => []);
1218
+ const archived = archivedFiles.find((f) => taskIdMatchesBasename(path2.basename(f), taskId));
1219
+ return archived ?? null;
1220
+ }
1221
+ async function persistToTaskFrontmatter(record) {
1222
+ const planningRoot = getPlanningRoot();
1223
+ const taskFile = await findTaskFilePath(record.stageId);
1224
+ if (!taskFile) return;
1225
+ await withIndexRegeneration(planningRoot, async () => {
1226
+ const { frontMatter, body } = await readMarkdownFile(taskFile);
1227
+ const now = record.recordedAt;
1228
+ const currentStatus = normalizeTaskStatus(frontMatter.status);
1229
+ const shouldBlock = record.validationPassed === false && currentStatus !== "blocked" && currentStatus !== "done";
1230
+ const nextFrontMatter = {
1231
+ ...frontMatter,
1232
+ updated_at: now,
1233
+ version: (typeof frontMatter.version === "number" ? frontMatter.version : 0) + 1,
1234
+ lastRun: {
1235
+ at: now,
1236
+ workflowId: record.workflowId,
1237
+ stageType: record.stageType,
1238
+ terminationReason: record.terminationReason,
1239
+ success: record.validationPassed,
1240
+ iterations: record.totalIterations,
1241
+ finalProgressScore: record.finalProgressScore,
1242
+ cost: record.cost,
1243
+ allowlistMode: record.allowlistMode
1244
+ },
1245
+ ...record.validationPassed === false ? {
1246
+ lastFailure: {
1247
+ at: now,
1248
+ workflowId: record.workflowId,
1249
+ terminationReason: record.terminationReason,
1250
+ ...record.failure
1251
+ }
1252
+ } : {},
1253
+ ...shouldBlock ? { status: "blocked" } : {}
1254
+ };
1255
+ await writeMarkdownFile(taskFile, nextFrontMatter, body);
1256
+ });
1257
+ }
1258
+ async function persistToTelemetryState(record) {
1259
+ const root = getPlanningRoot();
1260
+ const stageSafe = safeFilename2(record.stageId);
1261
+ const workflowSafe = safeFilename2(record.workflowId);
1262
+ const outPath = path2.join(
1263
+ resolveRunStateDir(path2.join(root, ".build-project")),
1264
+ "flywheel",
1265
+ workflowSafe,
1266
+ "autoloop",
1267
+ `${stageSafe}.json`
1268
+ );
1269
+ await writeJsonAtomic(outPath, record);
1270
+ }
1271
+ function getIterationSpillPath(input) {
1272
+ const root = getPlanningRoot();
1273
+ const stageSafe = safeFilename2(input.stageId);
1274
+ const workflowSafe = safeFilename2(input.workflowId);
1275
+ return path2.join(
1276
+ resolveRunStateDir(path2.join(root, ".build-project")),
1277
+ "flywheel",
1278
+ workflowSafe,
1279
+ "autoloop",
1280
+ `${stageSafe}.iterations.json`
1281
+ );
1282
+ }
1283
+ function resolveAutoloopReadPath(workflowId, stageId, suffix) {
1284
+ const fileName = `${safeFilename2(stageId)}${suffix}`;
1285
+ const dirs = resolveRunStateReadDirs(path2.join(getPlanningRoot(), ".build-project")).map(
1286
+ (dir) => path2.join(dir, "flywheel", safeFilename2(workflowId), "autoloop")
1287
+ );
1288
+ return path2.join(firstDirWithFile(dirs, fileName), fileName);
1289
+ }
1290
+ async function loadDurableAutoloopIterationSpill(input) {
1291
+ const filePath = resolveAutoloopReadPath(input.workflowId, input.stageId, ".iterations.json");
1292
+ try {
1293
+ const raw = await fs.readFile(filePath, "utf-8");
1294
+ const parsed = JSON.parse(raw);
1295
+ return isDurableAutoloopIterationSpill(parsed) ? parsed : null;
1296
+ } catch {
1297
+ return null;
1298
+ }
1299
+ }
1300
+ var iterationSpillWriteLocks = /* @__PURE__ */ new Map();
1301
+ async function persistDurableAutoloopIterationSpill(input) {
1302
+ const lockKey = `${input.workflowId}::${input.stageId}`;
1303
+ const prior = iterationSpillWriteLocks.get(lockKey) ?? Promise.resolve();
1304
+ const run = prior.then(async () => {
1305
+ const existing = await loadDurableAutoloopIterationSpill({
1306
+ workflowId: input.workflowId,
1307
+ stageId: input.stageId
1308
+ });
1309
+ const iterationsByIndex = /* @__PURE__ */ new Map();
1310
+ for (const record of existing?.iterations ?? []) {
1311
+ iterationsByIndex.set(record.index, record);
1312
+ }
1313
+ iterationsByIndex.set(input.record.index, input.record);
1314
+ const worktreeFingerprint = input.worktreeFingerprint ?? existing?.worktreeFingerprint;
1315
+ const runStartedAtMs = input.runStartedAtMs ?? existing?.runStartedAtMs;
1316
+ const providerSwitch = input.providerSwitch ?? existing?.providerSwitch;
1317
+ const priorFeedback = input.priorFeedback && input.priorFeedback.iteration >= (existing?.priorFeedback?.iteration ?? 0) ? input.priorFeedback : existing?.priorFeedback;
1318
+ const spill = {
1319
+ schemaVersion: 1,
1320
+ updatedAt: new Date(input.record.timestamp).toISOString(),
1321
+ workflowId: input.workflowId,
1322
+ stageId: input.stageId,
1323
+ writer: input.writer,
1324
+ ...runStartedAtMs !== void 0 ? { runStartedAtMs } : {},
1325
+ ...worktreeFingerprint ? { worktreeFingerprint } : {},
1326
+ ...providerSwitch ? { providerSwitch } : {},
1327
+ ...priorFeedback ? { priorFeedback } : {},
1328
+ iterations: [...iterationsByIndex.values()].sort((left, right) => left.index - right.index)
1329
+ };
1330
+ await writeJsonAtomic(getIterationSpillPath(input), spill);
1331
+ return spill;
1332
+ });
1333
+ const tail = run.catch(() => void 0);
1334
+ iterationSpillWriteLocks.set(lockKey, tail);
1335
+ void tail.then(() => {
1336
+ if (iterationSpillWriteLocks.get(lockKey) === tail) {
1337
+ iterationSpillWriteLocks.delete(lockKey);
1338
+ }
1339
+ });
1340
+ return run;
1341
+ }
1342
+ async function loadDurableAutoloopDiagnostics(input) {
1343
+ const filePath = resolveAutoloopReadPath(input.workflowId, input.stageId, ".json");
1344
+ try {
1345
+ const raw = await fs.readFile(filePath, "utf-8");
1346
+ const parsed = JSON.parse(raw);
1347
+ return isDurableAutoloopRunRecord(parsed) ? parsed : null;
1348
+ } catch {
1349
+ return null;
1350
+ }
1351
+ }
1352
+ function priorOutcomeFromDiagnostics(diagnostics) {
1353
+ return {
1354
+ loopSuccess: diagnostics.loopSuccess,
1355
+ validationPassed: diagnostics.validationPassed,
1356
+ terminationReason: diagnostics.terminationReason,
1357
+ finalProgressScore: diagnostics.finalProgressScore,
1358
+ totalIterations: diagnostics.totalIterations,
1359
+ cost: diagnostics.cost,
1360
+ outcomeMetadata: diagnostics.outcomeMetadata,
1361
+ failure: diagnostics.failure
1362
+ };
1363
+ }
1364
+ async function loadDurableAutoloopResumeState(input) {
1365
+ const current = input.worktreePath ? await computeWorktreeFingerprint(input.worktreePath) : void 0;
1366
+ const diagnostics = await loadDurableAutoloopDiagnostics(input);
1367
+ const diagnosticsExact = diagnostics?.stageType === "autoloop" && worktreeFingerprintExactlyMatches(diagnostics.worktreeFingerprint, current);
1368
+ const spill = await loadDurableAutoloopIterationSpill(input);
1369
+ if (spill !== null && spill.iterations.length > 0) {
1370
+ const maxIndex = spill.iterations.reduce((max, r) => Math.max(max, r.index), -1);
1371
+ const completedIterations = maxIndex + 1;
1372
+ const diagnosticsRecordedAt = diagnostics ? Date.parse(diagnostics.recordedAt) : Number.NaN;
1373
+ const spillUpdatedAt = Date.parse(spill.updatedAt);
1374
+ if (diagnostics !== null && diagnosticsExact && Number.isFinite(diagnosticsRecordedAt) && Number.isFinite(spillUpdatedAt) && diagnosticsRecordedAt >= spillUpdatedAt) {
1375
+ return {
1376
+ completedIterations: diagnostics.totalIterations,
1377
+ source: "diagnostics",
1378
+ finalProgressScore: diagnostics.finalProgressScore,
1379
+ priorOutcome: priorOutcomeFromDiagnostics(diagnostics),
1380
+ ...diagnostics.providerSwitch ? { providerSwitch: diagnostics.providerSwitch } : {}
1381
+ };
1382
+ }
1383
+ if (input.worktreePath && !worktreeFingerprintResumable(spill.worktreeFingerprint, current)) {
1384
+ await clearDurableAutoloopIterationSpill(input);
1385
+ return { completedIterations: 0, source: "spill", fingerprintMismatch: true };
1386
+ }
1387
+ const priorFeedback = parseAutoloopResumeFeedback(spill.priorFeedback);
1388
+ return {
1389
+ completedIterations,
1390
+ source: "spill",
1391
+ // Only feedback of the iteration the count ends at; anything else is stale.
1392
+ ...priorFeedback?.iteration === completedIterations ? { priorFeedback } : {},
1393
+ ...spill.runStartedAtMs !== void 0 ? { runStartedAtMs: spill.runStartedAtMs } : {},
1394
+ ...spill.providerSwitch ? { providerSwitch: spill.providerSwitch } : {}
1395
+ };
1396
+ }
1397
+ if (diagnostics !== null && diagnostics.stageType === "autoloop") {
1398
+ if (!diagnosticsExact) {
1399
+ return { completedIterations: 0, source: "diagnostics", fingerprintMismatch: true };
1400
+ }
1401
+ return {
1402
+ completedIterations: diagnostics.totalIterations,
1403
+ source: "diagnostics",
1404
+ finalProgressScore: diagnostics.finalProgressScore,
1405
+ priorOutcome: priorOutcomeFromDiagnostics(diagnostics),
1406
+ ...diagnostics.providerSwitch ? { providerSwitch: diagnostics.providerSwitch } : {}
1407
+ };
1408
+ }
1409
+ return null;
1410
+ }
1411
+ async function clearDurableAutoloopIterationSpill(input) {
1412
+ const fileName = `${safeFilename2(input.stageId)}.iterations.json`;
1413
+ const dirs = resolveRunStateReadDirs(path2.join(getPlanningRoot(), ".build-project"));
1414
+ for (const dir of dirs) {
1415
+ try {
1416
+ await fs.unlink(
1417
+ path2.join(dir, "flywheel", safeFilename2(input.workflowId), "autoloop", fileName)
1418
+ );
1419
+ } catch {
1420
+ }
1421
+ }
1422
+ }
1423
+ async function persistDurableAutoloopDiagnostics(input) {
1424
+ const record = buildDurableAutoloopRunRecord(input);
1425
+ const results = await Promise.allSettled([
1426
+ persistToTelemetryState(record),
1427
+ persistToTaskFrontmatter(record)
1428
+ ]);
1429
+ const canonicalPersisted = results[0].status === "fulfilled";
1430
+ const errors = results.map((r, idx) => ({ r, idx })).filter(({ r }) => r.status === "rejected").map(({ r, idx }) => ({
1431
+ target: idx === 0 ? "telemetry_state" : "task_frontmatter",
1432
+ error: r.reason
1433
+ }));
1434
+ if (errors.length > 0) {
1435
+ console.warn(
1436
+ `[flywheel-autoloop-durability] Best-effort persistence errors: ${errors.map((e) => `${e.target}: ${e.error instanceof Error ? e.error.message : String(e.error)}`).join("; ")}`
1437
+ );
1438
+ }
1439
+ return { record, canonicalPersisted };
1440
+ }
1441
+
1442
+ // src/automation/flywheel-gate-repair.ts
1443
+ import { performance } from "perf_hooks";
1444
+
1445
+ // src/automation/repair-evidence.ts
1446
+ import * as fs2 from "fs/promises";
1447
+ import * as path3 from "path";
1448
+ function formatRepairSuppressionWarning(evidence) {
1449
+ return `Review required: ${evidence.kind} in ${evidence.path}: ${evidence.addedLine}`;
1450
+ }
1451
+ var SUPPRESSION_PATTERNS = [
1452
+ /\bbiome-ignore\b/i,
1453
+ /\beslint-disable(?:-next-line|-line)?\b/i,
1454
+ /@ts-(?:ignore|nocheck|expect-error)\b/i,
1455
+ /#\s*noqa\b/i,
1456
+ /#\s*nolint\b/i,
1457
+ /\bnoinspection\b/i,
1458
+ /\bshellcheck\s+disable\b/i,
1459
+ /\bpragma:\s*no\s+cover\b/i,
1460
+ /\bistanbul\s+ignore\b/i,
1461
+ /\bc8\s+ignore\b/i,
1462
+ /\bpytest\.mark\.(?:skip|skipif|xfail)\b/i
1463
+ ];
1464
+ async function snapshotRepairSuppressions(cwd) {
1465
+ let stdout;
1466
+ try {
1467
+ const diff = await execGit(["diff", "--unified=0", "--no-ext-diff", "HEAD"], {
1468
+ cwd,
1469
+ timeoutMs: 3e4
1470
+ });
1471
+ if (diff.exitCode !== 0) return void 0;
1472
+ stdout = diff.stdout;
1473
+ } catch {
1474
+ return void 0;
1475
+ }
1476
+ const counts = /* @__PURE__ */ new Map();
1477
+ let currentPath = "";
1478
+ for (const line of stdout.split(/\r?\n/)) {
1479
+ if (line.startsWith("+++ b/")) {
1480
+ currentPath = line.slice("+++ b/".length);
1481
+ continue;
1482
+ }
1483
+ if (!currentPath || !line.startsWith("+") || line.startsWith("+++")) continue;
1484
+ const addedLine = line.slice(1).trim();
1485
+ if (!SUPPRESSION_PATTERNS.some((pattern) => pattern.test(addedLine))) continue;
1486
+ const key = `${currentPath}\0${addedLine}`;
1487
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1488
+ }
1489
+ try {
1490
+ const untracked = await execGit(["ls-files", "--others", "--exclude-standard", "-z"], {
1491
+ cwd,
1492
+ timeoutMs: 3e4
1493
+ });
1494
+ if (untracked.exitCode !== 0) return void 0;
1495
+ const untrackedPaths = untracked.stdout.split("\0").filter(Boolean);
1496
+ const trackedRoots = await readTrackedToolOutputRootsFor(cwd, untrackedPaths);
1497
+ for (const relativePath of untrackedPaths) {
1498
+ if (isWorktreeRunSentinelPath(relativePath) || shouldExcludeFile(relativePath, trackedRoots)) {
1499
+ continue;
1500
+ }
1501
+ const absolutePath = path3.resolve(cwd, relativePath);
1502
+ const stat3 = await fs2.stat(absolutePath);
1503
+ if (!stat3.isFile() || stat3.size > 1e6) continue;
1504
+ const content = await fs2.readFile(absolutePath, "utf8");
1505
+ for (const line of content.split(/\r?\n/)) {
1506
+ const addedLine = line.trim();
1507
+ if (!SUPPRESSION_PATTERNS.some((pattern) => pattern.test(addedLine))) continue;
1508
+ const key = `${relativePath}\0${addedLine}`;
1509
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1510
+ }
1511
+ }
1512
+ } catch {
1513
+ return void 0;
1514
+ }
1515
+ return counts;
1516
+ }
1517
+ async function detectNewRepairSuppressions(cwd, before) {
1518
+ if (!before) return [];
1519
+ const after = await snapshotRepairSuppressions(cwd);
1520
+ if (!after) return [];
1521
+ const evidence = [];
1522
+ for (const [key, count] of after) {
1523
+ const added = count - (before.get(key) ?? 0);
1524
+ if (added <= 0) continue;
1525
+ const [path6, addedLine] = key.split("\0");
1526
+ for (let i = 0; i < added; i++) {
1527
+ evidence.push({
1528
+ kind: "new_suppression_directive",
1529
+ path: path6 ?? "",
1530
+ addedLine: addedLine ?? "",
1531
+ reviewRequired: true
1532
+ });
1533
+ }
1534
+ }
1535
+ return evidence;
1536
+ }
1537
+
1538
+ // src/automation/repair-scope-enforcement.ts
1539
+ import * as fs3 from "fs/promises";
1540
+ import * as path4 from "path";
1541
+
1542
+ // src/core/project/verification-control-paths.ts
1543
+ var SOURCE_LIKE_VERIFICATION_CONTROLS = /* @__PURE__ */ new Set(["build.rs", "conftest.py"]);
1544
+ function isVerificationControlPath(filePath) {
1545
+ const normalized = filePath.replaceAll("\\", "/").replace(/^\.\//, "").toLowerCase();
1546
+ const basename2 = normalized.slice(normalized.lastIndexOf("/") + 1);
1547
+ return isProjectConfigPath(filePath) || SOURCE_LIKE_VERIFICATION_CONTROLS.has(basename2);
1548
+ }
1549
+ function isAutomaticRepairProtectedPath(filePath) {
1550
+ return isProjectTestPath(filePath) || isVerificationControlPath(filePath);
1551
+ }
1552
+
1553
+ // src/automation/repair-scope-enforcement.ts
1554
+ var LITERAL_PATHSPEC_ENV = { GIT_LITERAL_PATHSPECS: "1" };
1555
+ async function repairHead(worktreePath) {
1556
+ const options = { cwd: worktreePath, timeoutMs: 3e4 };
1557
+ const head = await execGit(["rev-parse", "--verify", "HEAD"], options);
1558
+ if (head.exitCode === 0) return head.stdout.trim();
1559
+ const branch = await execGit(["symbolic-ref", "-q", "HEAD"], options);
1560
+ if (branch.exitCode === 0 && branch.stdout.trim().startsWith("refs/heads/")) {
1561
+ const exists = await execGit(
1562
+ ["show-ref", "--verify", "--quiet", branch.stdout.trim()],
1563
+ options
1564
+ );
1565
+ if (exists.exitCode === 1) return null;
1566
+ }
1567
+ throw new Error("Automatic repair scope HEAD is unavailable");
1568
+ }
1569
+ function isRelevantRepairPath(filePath) {
1570
+ return !isWorktreeRunSentinelPath(filePath) && !shouldExcludeFile(filePath);
1571
+ }
1572
+ async function detectRepairScopeEvidence(worktreePath) {
1573
+ const status = await execGit(["status", "--porcelain=v1", "--untracked-files=all"], {
1574
+ cwd: worktreePath,
1575
+ timeoutMs: 3e4
1576
+ });
1577
+ if (status.exitCode !== 0) {
1578
+ throw new Error("Automatic repair scope status is unavailable");
1579
+ }
1580
+ const changedFiles = /* @__PURE__ */ new Set();
1581
+ const linkedEndpoints = /* @__PURE__ */ new Map();
1582
+ for (const entry of parseGitStatus(status.stdout)) {
1583
+ if (isRelevantRepairPath(entry.path)) changedFiles.add(entry.path);
1584
+ if (entry.originalPath && isRelevantRepairPath(entry.originalPath)) {
1585
+ changedFiles.add(entry.originalPath);
1586
+ if (isRelevantRepairPath(entry.path)) {
1587
+ linkedEndpoints.set(entry.originalPath, entry.path);
1588
+ linkedEndpoints.set(entry.path, entry.originalPath);
1589
+ }
1590
+ }
1591
+ }
1592
+ return {
1593
+ changedFiles: [...changedFiles].sort((a, b) => a.localeCompare(b)),
1594
+ linkedEndpoints
1595
+ };
1596
+ }
1597
+ async function snapshotPath(worktreePath, relativePath) {
1598
+ const absolutePath = path4.resolve(worktreePath, relativePath);
1599
+ let worktree;
1600
+ try {
1601
+ const stat3 = await fs3.lstat(absolutePath);
1602
+ if (stat3.isSymbolicLink()) {
1603
+ worktree = { kind: "symlink", target: await fs3.readlink(absolutePath) };
1604
+ } else if (stat3.isFile()) {
1605
+ worktree = {
1606
+ kind: "file",
1607
+ contents: await fs3.readFile(absolutePath),
1608
+ executable: (stat3.mode & 73) !== 0
1609
+ };
1610
+ } else {
1611
+ throw new Error(`Unsupported repair snapshot path type: ${relativePath}`);
1612
+ }
1613
+ } catch (error) {
1614
+ if (error.code !== "ENOENT") throw error;
1615
+ worktree = { kind: "missing" };
1616
+ }
1617
+ const indexResult = await execGit(["ls-files", "--stage", "--", relativePath], {
1618
+ cwd: worktreePath,
1619
+ timeoutMs: 3e4,
1620
+ env: LITERAL_PATHSPEC_ENV
1621
+ });
1622
+ const match = /^(\d+)\s+([0-9a-f]+)\s+0\t/.exec(indexResult.stdout);
1623
+ return {
1624
+ worktree,
1625
+ ...indexResult.exitCode === 0 && match?.[1] && match[2] ? { index: { mode: match[1], blob: match[2] } } : {}
1626
+ };
1627
+ }
1628
+ async function captureAutomaticRepairScopeBaseline(worktreePath) {
1629
+ const evidence = await detectRepairScopeEvidence(worktreePath);
1630
+ const entries = /* @__PURE__ */ new Map();
1631
+ for (const relativePath of evidence.changedFiles) {
1632
+ entries.set(relativePath, await snapshotPath(worktreePath, relativePath));
1633
+ }
1634
+ return { entries, head: await repairHead(worktreePath) };
1635
+ }
1636
+ async function pathMatchesSnapshot(worktreePath, relativePath, snapshot) {
1637
+ const current = await snapshotPath(worktreePath, relativePath);
1638
+ if (current.index?.mode !== snapshot.index?.mode || current.index?.blob !== snapshot.index?.blob) {
1639
+ return false;
1640
+ }
1641
+ if (current.worktree.kind !== snapshot.worktree.kind) return false;
1642
+ if (current.worktree.kind === "missing") return true;
1643
+ if (current.worktree.kind === "symlink" && snapshot.worktree.kind === "symlink") {
1644
+ return current.worktree.target === snapshot.worktree.target;
1645
+ }
1646
+ if (current.worktree.kind === "file" && snapshot.worktree.kind === "file") {
1647
+ return current.worktree.executable === snapshot.worktree.executable && current.worktree.contents.equals(snapshot.worktree.contents);
1648
+ }
1649
+ return false;
1650
+ }
1651
+ async function restoreSnapshotPath(worktreePath, relativePath, snapshot) {
1652
+ const removeRepairPath = async (absolutePath2) => {
1653
+ try {
1654
+ await fs3.lstat(absolutePath2);
1655
+ } catch (error) {
1656
+ if (error.code === "ENOENT") return;
1657
+ throw error;
1658
+ }
1659
+ const removal = await safeRmDir(absolutePath2, {
1660
+ managedRoots: [worktreePath],
1661
+ protectedPaths: [worktreePath]
1662
+ });
1663
+ if (!removal.deleted) {
1664
+ throw new Error(removal.reason ?? `Refusing to remove repair path ${absolutePath2}`);
1665
+ }
1666
+ };
1667
+ if (!snapshot) {
1668
+ const checkout = await execGit(["checkout", "HEAD", "--", relativePath], {
1669
+ cwd: worktreePath,
1670
+ timeoutMs: 3e4,
1671
+ env: LITERAL_PATHSPEC_ENV
1672
+ }).catch(() => void 0);
1673
+ if (checkout?.exitCode === 0) return;
1674
+ await execGit(["update-index", "--force-remove", "--", relativePath], {
1675
+ cwd: worktreePath,
1676
+ timeoutMs: 3e4,
1677
+ env: LITERAL_PATHSPEC_ENV
1678
+ }).catch(() => void 0);
1679
+ await removeRepairPath(path4.resolve(worktreePath, relativePath));
1680
+ return;
1681
+ }
1682
+ const absolutePath = path4.resolve(worktreePath, relativePath);
1683
+ if (snapshot.worktree.kind === "missing") {
1684
+ await removeRepairPath(absolutePath);
1685
+ } else {
1686
+ await fs3.mkdir(path4.dirname(absolutePath), { recursive: true });
1687
+ await removeRepairPath(absolutePath);
1688
+ if (snapshot.worktree.kind === "symlink") {
1689
+ await fs3.symlink(snapshot.worktree.target, absolutePath);
1690
+ } else {
1691
+ await fs3.writeFile(absolutePath, snapshot.worktree.contents, {
1692
+ mode: snapshot.worktree.executable ? 493 : 420
1693
+ });
1694
+ }
1695
+ }
1696
+ if (snapshot.index) {
1697
+ await execGit(
1698
+ [
1699
+ "update-index",
1700
+ "--add",
1701
+ "--cacheinfo",
1702
+ snapshot.index.mode,
1703
+ snapshot.index.blob,
1704
+ relativePath
1705
+ ],
1706
+ { cwd: worktreePath, timeoutMs: 3e4, env: LITERAL_PATHSPEC_ENV }
1707
+ );
1708
+ } else {
1709
+ await execGit(["update-index", "--force-remove", "--", relativePath], {
1710
+ cwd: worktreePath,
1711
+ timeoutMs: 3e4,
1712
+ env: LITERAL_PATHSPEC_ENV
1713
+ });
1714
+ }
1715
+ }
1716
+ function decideAutomaticRepairScope(changedFiles, explicitScope) {
1717
+ const protectedPaths = changedFiles.filter(isAutomaticRepairProtectedPath);
1718
+ return decideStageChangeScope({
1719
+ changedPaths: changedFiles,
1720
+ explicitScope,
1721
+ allowlistMode: explicitScope.length > 0 ? "strict" : "off",
1722
+ denylistPolicy: {
1723
+ denylist: protectedPaths,
1724
+ mode: "strict",
1725
+ allow: explicitScope
1726
+ },
1727
+ mutationOrigin: "provider"
1728
+ });
1729
+ }
1730
+ async function changedSinceBaseline(worktreePath, baseline) {
1731
+ const current = await detectRepairScopeEvidence(worktreePath);
1732
+ const head = await repairHead(worktreePath);
1733
+ const committedFiles = /* @__PURE__ */ new Set();
1734
+ if (head !== baseline.head) {
1735
+ if (head === null) throw new Error("Automatic repair removed HEAD; scope cannot be verified");
1736
+ const committed = await execGit(
1737
+ baseline.head === null ? ["ls-tree", "-r", "-z", "--name-only", head] : ["diff", "--name-only", "--no-renames", "-z", baseline.head, head, "--"],
1738
+ { cwd: worktreePath, timeoutMs: 3e4 }
1739
+ );
1740
+ if (committed.exitCode !== 0)
1741
+ throw new Error("Automatic repair committed scope is unavailable");
1742
+ for (const file of committed.stdout.split("\0").filter(isRelevantRepairPath)) {
1743
+ if (file) committedFiles.add(file);
1744
+ }
1745
+ }
1746
+ const candidates = /* @__PURE__ */ new Set([
1747
+ ...baseline.entries.keys(),
1748
+ ...current.changedFiles,
1749
+ ...committedFiles
1750
+ ]);
1751
+ const changed = [];
1752
+ for (const relativePath of candidates) {
1753
+ const prior = baseline.entries.get(relativePath);
1754
+ if (!prior || !await pathMatchesSnapshot(worktreePath, relativePath, prior)) {
1755
+ changed.push(relativePath);
1756
+ }
1757
+ }
1758
+ return {
1759
+ changedFiles: changed.sort((a, b) => a.localeCompare(b)),
1760
+ linkedEndpoints: current.linkedEndpoints,
1761
+ committedFiles
1762
+ };
1763
+ }
1764
+ async function enforceAutomaticRepairScope(input) {
1765
+ const explicitScope = [...input.explicitScope ?? []];
1766
+ const repairEvidence = await changedSinceBaseline(input.worktreePath, input.baseline);
1767
+ const decision = decideAutomaticRepairScope(repairEvidence.changedFiles, explicitScope);
1768
+ const violationSet = /* @__PURE__ */ new Set([
1769
+ .../* @__PURE__ */ new Set([...decision.allowlistViolations, ...decision.denylistViolations])
1770
+ ]);
1771
+ for (const violation of [...violationSet]) {
1772
+ const counterpart = repairEvidence.linkedEndpoints.get(violation);
1773
+ if (counterpart) violationSet.add(counterpart);
1774
+ }
1775
+ const violations = [...violationSet].sort((a, b) => a.localeCompare(b));
1776
+ const revertedFiles = [];
1777
+ for (const relativePath of violations) {
1778
+ if (repairEvidence.committedFiles?.has(relativePath)) continue;
1779
+ await restoreSnapshotPath(
1780
+ input.worktreePath,
1781
+ relativePath,
1782
+ input.baseline.entries.get(relativePath)
1783
+ );
1784
+ revertedFiles.push(relativePath);
1785
+ }
1786
+ const residualEvidence = await changedSinceBaseline(input.worktreePath, input.baseline);
1787
+ const residual = decideAutomaticRepairScope(residualEvidence.changedFiles, explicitScope);
1788
+ const residualViolations = [
1789
+ .../* @__PURE__ */ new Set([...residual.allowlistViolations, ...residual.denylistViolations])
1790
+ ];
1791
+ return {
1792
+ changedFiles: residualEvidence.changedFiles,
1793
+ violations,
1794
+ revertedFiles,
1795
+ ...residualViolations.length > 0 ? { error: `Automatic repair scope violation: ${residualViolations.join(", ")}` } : {}
1796
+ };
1797
+ }
1798
+
1799
+ // src/automation/flywheel-gate-repair.ts
1800
+ function normalizeGates(gates) {
1801
+ return gates.map((gate, index) => {
1802
+ if (typeof gate === "string") {
1803
+ return {
1804
+ id: `gate-${index + 1}`,
1805
+ command: gate,
1806
+ deterministic: true,
1807
+ severity: "error",
1808
+ mustPass: true
1809
+ };
1810
+ }
1811
+ const severity = gate.severity ?? "error";
1812
+ return {
1813
+ ...gate,
1814
+ deterministic: gate.deterministic ?? true,
1815
+ severity,
1816
+ mustPass: gate.mustPass ?? severity !== "warn"
1817
+ };
1818
+ });
1819
+ }
1820
+ async function enforceAllowlistAfterEdit(input) {
1821
+ const result = await enforceAutomaticRepairScope({
1822
+ worktreePath: input.worktreePath,
1823
+ baseline: input.baseline,
1824
+ explicitScope: input.allowlist
1825
+ });
1826
+ return {
1827
+ changedFiles: result.changedFiles,
1828
+ violations: result.violations,
1829
+ autoRevertedFiles: result.revertedFiles,
1830
+ error: result.error
1831
+ };
1832
+ }
1833
+ async function executeGate(gate, cwd, options) {
1834
+ const start = performance.now();
1835
+ try {
1836
+ const result = await runVerifyCommands({
1837
+ cwd,
1838
+ commands: [gate.command],
1839
+ commandProvenance: "candidate-gate-config",
1840
+ timeoutMs: resolveVerifyCommandTimeoutMs(gate.timeoutMs),
1841
+ signal: options.abortSignal
1842
+ });
1843
+ const commandResult = result.results[0];
1844
+ const durationMs = performance.now() - start;
1845
+ const exitCode = commandResult?.exitCode ?? 1;
1846
+ const expectedExitCode = gate.expectedExitCode ?? 0;
1847
+ return {
1848
+ result: {
1849
+ gate,
1850
+ passed: exitCode === expectedExitCode,
1851
+ exitCode,
1852
+ durationMs,
1853
+ complete: commandResult?.complete !== false,
1854
+ ...commandResult?.unavailableReason ? { unavailableReason: commandResult.unavailableReason } : {},
1855
+ outputTail: result.combinedOutput ? truncateTail(result.combinedOutput, 4e3) : commandResult?.outputTail
1856
+ },
1857
+ output: result.combinedOutput ?? ""
1858
+ };
1859
+ } catch (error) {
1860
+ const durationMs = performance.now() - start;
1861
+ const message = error instanceof Error ? error.message : String(error);
1862
+ return {
1863
+ result: {
1864
+ gate,
1865
+ passed: false,
1866
+ exitCode: 124,
1867
+ durationMs,
1868
+ complete: false,
1869
+ outputTail: truncateTail(message, 4e3)
1870
+ },
1871
+ output: message
1872
+ };
1873
+ }
1874
+ }
1875
+ function signatureNewErrorCount(gate, baselineOutput, currentOutput) {
1876
+ if ((gate.expectedExitCode ?? 0) !== 0) return void 0;
1877
+ const parser = resolveBaselineErrorParser(gate.command);
1878
+ if (!parser || parser.count(currentOutput) === 0) return void 0;
1879
+ return parser.newCount(baselineOutput, currentOutput);
1880
+ }
1881
+ async function runAllGates(gates, cwd, options = {}) {
1882
+ const results = [];
1883
+ const remainingDeadlineMs = () => options.deadlineMs === void 0 ? void 0 : Math.floor(options.deadlineMs - Date.now());
1884
+ const withRemainingDeadline = (gate) => {
1885
+ const remainingMs = remainingDeadlineMs();
1886
+ if (remainingMs === void 0) return gate;
1887
+ const boundedRemainingMs = Math.max(1, remainingMs);
1888
+ return {
1889
+ ...gate,
1890
+ timeoutMs: typeof gate.timeoutMs === "number" && gate.timeoutMs > 0 ? Math.min(gate.timeoutMs, boundedRemainingMs) : boundedRemainingMs
1891
+ };
1892
+ };
1893
+ for (const [index, gate] of gates.entries()) {
1894
+ const remainingBeforeGate = remainingDeadlineMs();
1895
+ if (remainingBeforeGate !== void 0 && remainingBeforeGate <= 0) {
1896
+ for (const unrunGate of gates.slice(index)) {
1897
+ results.push({
1898
+ gate: unrunGate,
1899
+ passed: false,
1900
+ exitCode: 124,
1901
+ durationMs: 0,
1902
+ complete: false,
1903
+ outputTail: "Gate did not run: shared autoloop deadline exhausted before execution."
1904
+ });
1905
+ }
1906
+ break;
1907
+ }
1908
+ const current = await executeGate(withRemainingDeadline(gate), cwd, {
1909
+ abortSignal: options.abortSignal
1910
+ });
1911
+ const result = current.result;
1912
+ if (!result.passed && gate.mustPass && options.baselineCwd && result.complete !== false) {
1913
+ const remainingBeforeBaseline = remainingDeadlineMs();
1914
+ if (remainingBeforeBaseline === void 0 || remainingBeforeBaseline > 0) {
1915
+ const baseline = await executeGate(withRemainingDeadline(gate), options.baselineCwd, {
1916
+ abortSignal: options.abortSignal
1917
+ });
1918
+ const baselineResult = baseline.result;
1919
+ if (baselineResult.complete !== false && gateRunUnavailableSignal(baselineResult) === void 0) {
1920
+ result.baselinePassed = baselineResult.passed;
1921
+ const newErrorCount = signatureNewErrorCount(gate, baseline.output, current.output);
1922
+ if (newErrorCount !== void 0) result.newErrorCount = newErrorCount;
1923
+ result.isRegression = newErrorCount !== void 0 ? newErrorCount > 0 : baselineResult.passed;
1924
+ }
1925
+ }
1926
+ }
1927
+ results.push(result);
1928
+ if (!result.passed && gate.mustPass && result.isRegression !== false && gateRunUnavailableSignal(result) === void 0) {
1929
+ break;
1930
+ }
1931
+ }
1932
+ const allMustPassPassed = results.every((r) => r.passed || !r.gate.mustPass);
1933
+ const regressionsPassed = results.every(
1934
+ (r) => r.passed || !r.gate.mustPass || r.isRegression === false
1935
+ );
1936
+ return {
1937
+ passed: allMustPassPassed,
1938
+ regressionsPassed,
1939
+ results
1940
+ };
1941
+ }
1942
+ function buildRepairPrompt(opts) {
1943
+ const { failedGate, gateResult, changedFiles, repairConfig } = opts;
1944
+ const template = repairConfig?.promptTemplate || `The following quality gate is failing:
1945
+
1946
+ Command: ${failedGate.command}
1947
+ Exit Code: ${gateResult.exitCode}
1948
+ Expected: ${failedGate.expectedExitCode ?? 0}
1949
+
1950
+ Output (last 4000 chars):
1951
+ ${renderUntrustedCommandOutput(gateResult.outputTail)}
1952
+
1953
+ Changed Files:
1954
+ ${changedFiles.length > 0 ? changedFiles.map((f) => `- ${f}`).join("\n") : "(none)"}
1955
+
1956
+ Please make the MINIMUM changes required to make this gate pass. Fix the underlying cause of the specific issue reported by the gate command.`;
1957
+ return `${template}
1958
+
1959
+ ${renderRepairConstraints()}`;
1960
+ }
1961
+ async function attemptGateRepair(opts) {
1962
+ const start = performance.now();
1963
+ const {
1964
+ failedGate,
1965
+ gateResult,
1966
+ allGates,
1967
+ worktreePath,
1968
+ changedFiles,
1969
+ executor,
1970
+ provider,
1971
+ model,
1972
+ attemptNumber,
1973
+ allowlist,
1974
+ allowlistMode,
1975
+ baselineCwd,
1976
+ abortSignal,
1977
+ deadlineMs,
1978
+ timeoutMs
1979
+ } = opts;
1980
+ const repairConfig = failedGate.repair;
1981
+ const repairScopeBaseline = await captureAutomaticRepairScopeBaseline(worktreePath);
1982
+ const suppressionBaseline = await snapshotRepairSuppressions(worktreePath);
1983
+ if (repairConfig?.preCommands) {
1984
+ for (const cmd of repairConfig.preCommands) {
1985
+ await runVerifyCommands({
1986
+ cwd: worktreePath,
1987
+ commands: [cmd],
1988
+ commandProvenance: "repair-config",
1989
+ signal: abortSignal
1990
+ });
1991
+ }
1992
+ }
1993
+ const repairPrompt = buildRepairPrompt({
1994
+ failedGate,
1995
+ gateResult,
1996
+ changedFiles,
1997
+ repairConfig
1998
+ });
1999
+ let providerError;
2000
+ let providerCost = 0;
2001
+ let providerCostUnavailableReason;
2002
+ let providerInputTokens;
2003
+ let providerOutputTokens;
2004
+ let tokenUsageSource;
2005
+ let providerFailed = false;
2006
+ try {
2007
+ const variantConfig = {
2008
+ provider,
2009
+ model,
2010
+ executionMode: "local",
2011
+ isBasicCompetition: true,
2012
+ workflowWorktreePath: worktreePath,
2013
+ disableAutoCommit: true,
2014
+ structuredFileTargets: allowlist ?? [],
2015
+ ...typeof timeoutMs === "number" && timeoutMs > 0 ? { timeoutMs } : {}
2016
+ };
2017
+ const result = await runWithStageProviderSwitch(
2018
+ opts.providerSwitch,
2019
+ (target, launchTimeoutMs) => {
2020
+ const runsOn = target && target.provider !== provider ? target : { ...target, provider, model };
2021
+ return executor.executeVariant(
2022
+ generateCompetitionId(),
2023
+ {
2024
+ ...variantConfig,
2025
+ ...runsOn,
2026
+ ...launchTimeoutMs ? { timeoutMs: launchTimeoutMs } : {}
2027
+ },
2028
+ repairPrompt,
2029
+ { abortSignal }
2030
+ );
2031
+ },
2032
+ {
2033
+ callSite: "build-repair",
2034
+ // A replacement gets what is left of this repair's timeout and of the gate deadline.
2035
+ ...variantConfig.timeoutMs ? { timeoutMs: variantConfig.timeoutMs } : {},
2036
+ ...deadlineMs !== void 0 ? { timeLeftMs: () => deadlineMs - Date.now() } : {},
2037
+ canLaunch: () => !abortSignal?.aborted
2038
+ }
2039
+ );
2040
+ providerCostUnavailableReason = readCostMetricsUnavailableReason(result.costMetrics);
2041
+ const reportedCost = result.costMetrics?.totalCost;
2042
+ if (providerCostUnavailableReason === void 0 && typeof reportedCost === "number" && Number.isFinite(reportedCost)) {
2043
+ providerCost = reportedCost;
2044
+ }
2045
+ providerInputTokens = result.costMetrics?.inputTokens;
2046
+ providerOutputTokens = result.costMetrics?.outputTokens;
2047
+ tokenUsageSource = result.costMetrics?.tokenUsageSource;
2048
+ providerError = result.success ? void 0 : result.error?.message ?? "Repair provider failed";
2049
+ providerFailed = isProviderInvocationFailure(result);
2050
+ } catch (error) {
2051
+ providerError = error instanceof Error ? error.message : String(error);
2052
+ providerFailed = true;
2053
+ }
2054
+ if (providerFailed && opts.keepWorkOnProviderFailure) {
2055
+ return {
2056
+ success: false,
2057
+ attemptNumber,
2058
+ cost: providerCost,
2059
+ costUnavailableReason: providerCostUnavailableReason,
2060
+ inputTokens: providerInputTokens,
2061
+ outputTokens: providerOutputTokens,
2062
+ tokenUsageSource,
2063
+ durationMs: performance.now() - start,
2064
+ gatesPassedAfterRepair: false,
2065
+ gateResults: [],
2066
+ providerError,
2067
+ abortFurtherRepairs: true,
2068
+ providerFailed
2069
+ };
2070
+ }
2071
+ if (repairConfig?.postCommands) {
2072
+ for (const cmd of repairConfig.postCommands) {
2073
+ await runVerifyCommands({
2074
+ cwd: worktreePath,
2075
+ commands: [cmd],
2076
+ commandProvenance: "repair-config",
2077
+ signal: abortSignal
2078
+ });
2079
+ }
2080
+ }
2081
+ const allowlistState = await enforceAllowlistAfterEdit({
2082
+ worktreePath,
2083
+ baseline: repairScopeBaseline,
2084
+ allowlist,
2085
+ allowlistMode: allowlistMode ?? "warn"
2086
+ });
2087
+ const reviewRequiredEvidence = await detectNewRepairSuppressions(
2088
+ worktreePath,
2089
+ suppressionBaseline
2090
+ );
2091
+ if (allowlistState.error) {
2092
+ const durationMs2 = performance.now() - start;
2093
+ const combinedError = [providerError, allowlistState.error].filter((value) => Boolean(value)).join("\n");
2094
+ return {
2095
+ success: false,
2096
+ attemptNumber,
2097
+ cost: providerCost,
2098
+ costUnavailableReason: providerCostUnavailableReason,
2099
+ inputTokens: providerInputTokens,
2100
+ outputTokens: providerOutputTokens,
2101
+ tokenUsageSource,
2102
+ durationMs: durationMs2,
2103
+ gatesPassedAfterRepair: false,
2104
+ gateResults: [],
2105
+ providerError: combinedError,
2106
+ allowlistState,
2107
+ abortFurtherRepairs: true,
2108
+ ...providerFailed ? { providerFailed } : {}
2109
+ };
2110
+ }
2111
+ const gateCheck = await runAllGates(allGates, worktreePath, {
2112
+ baselineCwd,
2113
+ abortSignal,
2114
+ deadlineMs
2115
+ });
2116
+ const durationMs = performance.now() - start;
2117
+ return {
2118
+ success: !providerError && gateCheck.regressionsPassed,
2119
+ attemptNumber,
2120
+ cost: providerCost,
2121
+ costUnavailableReason: providerCostUnavailableReason,
2122
+ inputTokens: providerInputTokens,
2123
+ outputTokens: providerOutputTokens,
2124
+ tokenUsageSource,
2125
+ durationMs,
2126
+ gatesPassedAfterRepair: gateCheck.passed,
2127
+ gateResults: gateCheck.results,
2128
+ providerError,
2129
+ allowlistState,
2130
+ ...reviewRequiredEvidence.length > 0 ? { reviewRequiredEvidence } : {},
2131
+ ...providerFailed ? { providerFailed } : {}
2132
+ };
2133
+ }
2134
+ function shouldAttemptRepair(gate) {
2135
+ if (!gate.mustPass) return false;
2136
+ if (!gate.deterministic) return false;
2137
+ if (gate.repair === void 0) return false;
2138
+ return true;
2139
+ }
2140
+ function getMaxRepairAttempts(gate) {
2141
+ return gate.repair?.maxAttempts ?? 2;
2142
+ }
2143
+
2144
+ // src/automation/flywheel-autoloop-executor.ts
2145
+ var FLYWHEEL_AUTOLOOP_CONTROLLER_RESOURCE_LIMITS = {
2146
+ maxMemoryMB: 1048576
2147
+ };
2148
+ function mapQualityGateCommandResultsToCustomGateResults(commandResults, toDurationMs) {
2149
+ if (!commandResults || commandResults.length === 0) {
2150
+ return [];
2151
+ }
2152
+ return commandResults.filter((r) => r.mustPass !== false).map((r, idx) => {
2153
+ const expectedExitCode = r.expectedExitCode ?? 0;
2154
+ const passed = typeof r.passed === "boolean" ? r.passed : r.exitCode === expectedExitCode;
2155
+ const mapped = {
2156
+ id: r.id && r.id.length > 0 ? r.id : `gate-${idx + 1}`,
2157
+ command: r.command,
2158
+ passed,
2159
+ durationMs: toDurationMs(Math.max(0, Number.isFinite(r.durationMs) ? r.durationMs : 0)),
2160
+ exitCode: r.exitCode
2161
+ };
2162
+ if (r.outputTail !== void 0) {
2163
+ mapped.outputTail = r.outputTail;
2164
+ }
2165
+ if (r.isRegression !== void 0) {
2166
+ mapped.isRegression = r.isRegression;
2167
+ }
2168
+ return mapped;
2169
+ });
2170
+ }
2171
+ var MIN_GATE_REPAIR_TIMEOUT_MS = 3e4;
2172
+ var DEFAULT_TEST_FILE_PATTERNS = [
2173
+ "**/*.test.ts",
2174
+ "**/*.spec.ts",
2175
+ "tests/**/*.ts",
2176
+ "__tests__/**/*.ts"
2177
+ ];
2178
+ var TEST_FILE_PATTERNS_BY_LANGUAGE = {
2179
+ node: DEFAULT_TEST_FILE_PATTERNS,
2180
+ typescript: DEFAULT_TEST_FILE_PATTERNS,
2181
+ javascript: ["**/*.test.js", "**/*.spec.js", "tests/**/*.js", "__tests__/**/*.js"],
2182
+ python: ["test_*.py", "*_test.py", "tests/**/*.py"],
2183
+ go: ["*_test.go"],
2184
+ rust: ["tests/**/*.rs"],
2185
+ java: ["**/*Test.java", "**/*Tests.java", "src/test/**/*.java"],
2186
+ dotnet: ["**/*Tests.cs", "**/*Test.cs"],
2187
+ swift: ["**/*Tests.swift", "Tests/**/*.swift"],
2188
+ kotlin: ["**/*Test.kt", "**/*Tests.kt", "src/test/**/*.kt"],
2189
+ dart: ["*_test.dart", "test/**/*_test.dart"],
2190
+ ruby: ["*_test.rb", "*_spec.rb", "spec/**/*_spec.rb", "test/**/*_test.rb"],
2191
+ php: ["*Test.php", "tests/**/*Test.php"],
2192
+ c: ["tests/**/*.c"],
2193
+ cpp: ["*_test.cc", "*_test.cpp", "tests/**/*.cc", "tests/**/*.cpp"],
2194
+ shell: ["tests/**/*.sh"],
2195
+ powershell: ["*.Tests.ps1"],
2196
+ r: ["tests/testthat/test-*.R"]
2197
+ };
2198
+ function selectDisplayTestFilePatterns(language, configuredPatterns) {
2199
+ if (configuredPatterns && configuredPatterns.length > 0) {
2200
+ return configuredPatterns;
2201
+ }
2202
+ if (!language) return null;
2203
+ return TEST_FILE_PATTERNS_BY_LANGUAGE[language] ?? null;
2204
+ }
2205
+ function selectPrimaryPromptLanguage(languages) {
2206
+ const concrete = languages.filter(
2207
+ (language) => language !== "docs" && language !== "unknown" && language !== "node"
2208
+ );
2209
+ if (concrete.length === 1) return concrete[0] ?? null;
2210
+ if (concrete.length > 1) {
2211
+ const nonJs = concrete.filter(
2212
+ (language) => language !== "typescript" && language !== "javascript"
2213
+ );
2214
+ if (nonJs.length === 0) {
2215
+ return concrete.includes("typescript") ? "typescript" : "javascript";
2216
+ }
2217
+ return null;
2218
+ }
2219
+ if (languages.includes("node")) {
2220
+ return "typescript";
2221
+ }
2222
+ return null;
2223
+ }
2224
+ function renderTestRequirementBody(language, configuredPatterns) {
2225
+ const patterns = selectDisplayTestFilePatterns(language, configuredPatterns);
2226
+ if (patterns && patterns.length > 0) {
2227
+ return `Expected patterns:
2228
+ ${patterns.map((p) => `- ${p}`).join("\n")}`;
2229
+ }
2230
+ return "Follow the existing test conventions of the target project (match the naming, directory layout, and framework already used for tests in this repository).";
2231
+ }
2232
+ function isCreatedTargetTestFile(filePath) {
2233
+ if (!isProjectTestPath(filePath)) return false;
2234
+ const language = languageForProjectPath(filePath);
2235
+ return language !== null && language !== "docs";
2236
+ }
2237
+ async function detectTestFiles(changedFiles, patterns, rootDir) {
2238
+ if (patterns && patterns.length > 0) {
2239
+ const minimatchModule = await import("minimatch");
2240
+ const minimatch = typeof minimatchModule.minimatch === "function" ? minimatchModule.minimatch : typeof minimatchModule.default === "function" ? minimatchModule.default : void 0;
2241
+ if (!minimatch) {
2242
+ throw new Error("Failed to load minimatch for test file detection");
2243
+ }
2244
+ const matches2 = changedFiles.filter(
2245
+ (file) => patterns.some((pattern) => minimatch(file, pattern))
2246
+ );
2247
+ return rootDir ? filterExistingPaths(matches2, rootDir) : matches2;
2248
+ }
2249
+ const matches = changedFiles.filter((file) => isCreatedTargetTestFile(file));
2250
+ return rootDir ? filterExistingPaths(matches, rootDir) : matches;
2251
+ }
2252
+ async function filterExistingPaths(files, rootDir) {
2253
+ const uniqueFiles = Array.from(new Set(files));
2254
+ const existing = await Promise.all(
2255
+ uniqueFiles.map(async (file) => {
2256
+ try {
2257
+ await fs4.access(path5.resolve(rootDir, file));
2258
+ return file;
2259
+ } catch {
2260
+ return null;
2261
+ }
2262
+ })
2263
+ );
2264
+ return existing.filter((file) => file !== null);
2265
+ }
2266
+ function verifyRuntimeEvidence(input) {
2267
+ if (!input.config) {
2268
+ return { passed: true, blockingIssues: [] };
2269
+ }
2270
+ if (input.config.requireAcceptanceCriteria !== true) {
2271
+ return { passed: true, blockingIssues: [] };
2272
+ }
2273
+ const validation = validateRequirements({
2274
+ requirementsTaskText: input.basePrompt,
2275
+ providerOutputText: input.providerOutputText,
2276
+ workingDir: input.worktreePath,
2277
+ changedFiles: input.changedFiles,
2278
+ testVerification: input.testVerification,
2279
+ config: {
2280
+ mode: "on",
2281
+ minCoverage: input.config.minCoverage ?? 0.9,
2282
+ requireEvidence: input.config.requireEvidence ?? true,
2283
+ blockNewRepoRootMarkdown: input.config.blockNewRepoRootMarkdown ?? true
2284
+ }
2285
+ });
2286
+ return {
2287
+ passed: validation.passed,
2288
+ blockingIssues: validation.blockingIssues,
2289
+ validation
2290
+ };
2291
+ }
2292
+ var AUTOLOOP_TERMINATION_REASON_SET = /* @__PURE__ */ new Set([
2293
+ "max_iterations_reached",
2294
+ "timebox_exceeded",
2295
+ "progress_stagnation",
2296
+ "no_op_loop",
2297
+ "duplicate_change_loop",
2298
+ "resource_exhaustion",
2299
+ "quality_gate_failure",
2300
+ "allowlist_violation",
2301
+ "tests_required_not_met",
2302
+ "evidence_unsatisfied",
2303
+ "verification_unavailable",
2304
+ "no_op_intervention_exceeded",
2305
+ "goal_satisfied",
2306
+ "heuristic_complete",
2307
+ "baseline_blocked",
2308
+ "success",
2309
+ "error"
2310
+ ]);
2311
+ function coerceAutoloopTerminationReason(reason) {
2312
+ return AUTOLOOP_TERMINATION_REASON_SET.has(reason) ? reason : "max_iterations_reached";
2313
+ }
2314
+ function reconstructExhaustedAutoloopOutcome(input) {
2315
+ const successCompatibleReasons = /* @__PURE__ */ new Set([
2316
+ "success",
2317
+ "goal_satisfied",
2318
+ "heuristic_complete",
2319
+ "max_iterations_reached"
2320
+ ]);
2321
+ if (!input.priorOutcome) {
2322
+ return {
2323
+ terminationReason: "max_iterations_reached",
2324
+ loopSuccess: false,
2325
+ finalProgressScore: input.finalProgressScore ?? 0,
2326
+ evidenceComplete: false
2327
+ };
2328
+ }
2329
+ if (input.priorOutcome.loopSuccess !== true) {
2330
+ return {
2331
+ terminationReason: coerceAutoloopTerminationReason(input.priorOutcome.terminationReason),
2332
+ loopSuccess: false,
2333
+ finalProgressScore: input.priorOutcome.finalProgressScore,
2334
+ evidenceComplete: true
2335
+ };
2336
+ }
2337
+ const recordedUnverified = input.priorOutcome.terminationReason === "verification_unavailable" && input.priorOutcome.outcomeMetadata?.terminationClass === "unverified";
2338
+ if (input.priorOutcome.validationPassed !== true || !(successCompatibleReasons.has(input.priorOutcome.terminationReason) || recordedUnverified)) {
2339
+ return {
2340
+ terminationReason: coerceAutoloopTerminationReason(input.priorOutcome.terminationReason),
2341
+ loopSuccess: false,
2342
+ finalProgressScore: input.priorOutcome.finalProgressScore,
2343
+ evidenceComplete: false
2344
+ };
2345
+ }
2346
+ return {
2347
+ terminationReason: coerceAutoloopTerminationReason(input.priorOutcome.terminationReason),
2348
+ loopSuccess: true,
2349
+ finalProgressScore: input.priorOutcome.finalProgressScore,
2350
+ evidenceComplete: true
2351
+ };
2352
+ }
2353
+ var GATE_TYPE_ORDER = {
2354
+ lint: 0,
2355
+ typecheck: 1,
2356
+ unit: 2,
2357
+ integration: 3,
2358
+ build: 4,
2359
+ other: 5
2360
+ };
2361
+ function classifyGateType(command) {
2362
+ const descriptor = describeVerificationCommand(command);
2363
+ if (descriptor.kind === "lint" || descriptor.kind === "typecheck" || descriptor.kind === "build") {
2364
+ return descriptor.kind;
2365
+ }
2366
+ if (descriptor.kind === "test") {
2367
+ return /\b(?:integration|e2e|playwright|cypress)\b/i.test(command) ? "integration" : "unit";
2368
+ }
2369
+ return "other";
2370
+ }
2371
+ function classifyAutoloopGateTypeForTest(command) {
2372
+ return classifyGateType(command);
2373
+ }
2374
+ function orderGatesByType(gates) {
2375
+ return [...gates].sort((a, b) => {
2376
+ const orderA = GATE_TYPE_ORDER[classifyGateType(a.command)];
2377
+ const orderB = GATE_TYPE_ORDER[classifyGateType(b.command)];
2378
+ return orderA !== orderB ? orderA - orderB : gates.indexOf(a) - gates.indexOf(b);
2379
+ });
2380
+ }
2381
+ async function resolveGatePreset(input) {
2382
+ const { configuredPreset, escalation, changedFiles, repairAttempts, failedGateIds } = input;
2383
+ const basePreset = configuredPreset ?? "fast";
2384
+ if (!escalation || basePreset === "full") {
2385
+ return { preset: basePreset };
2386
+ }
2387
+ const reasons = [];
2388
+ if (escalation.escalationPaths && escalation.escalationPaths.length > 0 && changedFiles.length > 0) {
2389
+ try {
2390
+ const minimatchModule = await import("minimatch");
2391
+ const minimatch = typeof minimatchModule.minimatch === "function" ? minimatchModule.minimatch : typeof minimatchModule.default === "function" ? minimatchModule.default : void 0;
2392
+ if (minimatch) {
2393
+ const escalationPatterns = escalation.escalationPaths;
2394
+ const matchedFile = changedFiles.find(
2395
+ (file) => escalationPatterns.some((pattern) => minimatch(file, pattern))
2396
+ );
2397
+ if (matchedFile) {
2398
+ reasons.push(`path match: ${matchedFile}`);
2399
+ }
2400
+ }
2401
+ } catch {
2402
+ }
2403
+ }
2404
+ const repairThreshold = escalation.escalationRepairThreshold ?? 2;
2405
+ if (repairAttempts >= repairThreshold) {
2406
+ reasons.push(`repair attempts (${repairAttempts}) >= threshold (${repairThreshold})`);
2407
+ }
2408
+ if (escalation.baselineRegressionGateIds && escalation.baselineRegressionGateIds.length > 0) {
2409
+ const baselineRegressionGateIds = escalation.baselineRegressionGateIds;
2410
+ const regressionMatch = failedGateIds.find((id) => baselineRegressionGateIds.includes(id));
2411
+ if (regressionMatch) {
2412
+ reasons.push(`baseline regression gate failed: ${regressionMatch}`);
2413
+ }
2414
+ }
2415
+ if (escalation.escalateOnTestChanges === true && changedFiles.length > 0) {
2416
+ const testFile = changedFiles.find((file) => isCreatedTargetTestFile(file));
2417
+ if (testFile) {
2418
+ reasons.push(`test file changed: ${testFile}`);
2419
+ }
2420
+ }
2421
+ if (reasons.length > 0) {
2422
+ return { preset: "full", escalationReason: reasons.join(", ") };
2423
+ }
2424
+ return { preset: basePreset };
2425
+ }
2426
+ function filterGatesByPreset(gates, preset) {
2427
+ return gates.filter(
2428
+ (gate) => autoloopPresetIncludesGateKind(preset, classifyGateType(gate.command))
2429
+ );
2430
+ }
2431
+ var ZERO_TIME_METRICS = {
2432
+ totalProviderMs: 0,
2433
+ totalGateMs: 0,
2434
+ totalRepairMs: 0,
2435
+ totalTestMs: 0,
2436
+ totalBuildMs: 0,
2437
+ totalOverheadMs: 0,
2438
+ avgIterationMs: 0,
2439
+ noOpIterationCount: 0,
2440
+ providerTimePct: 0,
2441
+ deterministicTimePct: 0
2442
+ };
2443
+ function buildAutoloopRunSummary(ctx, result, options) {
2444
+ const timeMetrics = result.aggregateTimeMetrics ?? ZERO_TIME_METRICS;
2445
+ return {
2446
+ runId: options.runId,
2447
+ taskId: ctx.workflowId ?? ctx.stage.id,
2448
+ stageId: ctx.stage.id,
2449
+ startedAt: options.startedAt,
2450
+ completedAt: (/* @__PURE__ */ new Date()).toISOString(),
2451
+ durationMs: result.durationMs ?? 0,
2452
+ totalIterations: result.totalIterations,
2453
+ terminationReason: result.terminationReason,
2454
+ finalProgressScore: result.finalProgressScore,
2455
+ success: result.loopSuccess,
2456
+ outcome: result.outcomeMetadata?.outcome ?? "red",
2457
+ terminationClass: result.outcomeMetadata?.terminationClass,
2458
+ totalCost: result.cost ?? 0,
2459
+ costUnavailableReason: result.costUnavailableReason,
2460
+ timeMetrics,
2461
+ provider: resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath),
2462
+ model: ctx.stage.model,
2463
+ gatePreset: options.gatePreset,
2464
+ noOpIterationCount: options.noOpIterationCount,
2465
+ repairAttempts: options.repairAttempts,
2466
+ repairSuccesses: options.repairSuccesses,
2467
+ aiValidationShortCircuited: options.aiValidationShortCircuited,
2468
+ policyEvents: result.policyEvents
2469
+ };
2470
+ }
2471
+ function firstCostUnavailableReason(records) {
2472
+ return records.find((record) => record.costUnavailableReason)?.costUnavailableReason;
2473
+ }
2474
+ function formatAutoloopIterationCost(record) {
2475
+ if (record.costUnavailableReason === "provider_not_reported") {
2476
+ return "cost not reported";
2477
+ }
2478
+ if (record.costUnavailableReason === "pricing_unavailable") {
2479
+ return "cost unavailable";
2480
+ }
2481
+ return `$${record.cost.toFixed(4)}`;
2482
+ }
2483
+ async function finalizeAutoloopRun(ctx, result, summary, options = {}) {
2484
+ const now = Date.now();
2485
+ const writer = `flywheel-autoloop:${ctx.stage.id}`;
2486
+ const updateHistoryPersistenceBestEffort = async (history) => {
2487
+ if (!ctx.stateManager || !ctx.workflowId) {
2488
+ return;
2489
+ }
2490
+ try {
2491
+ const state = await ctx.stateManager.load(ctx.workflowId);
2492
+ const current = state.autoloopFinal;
2493
+ if (!current) {
2494
+ return;
2495
+ }
2496
+ await ctx.stateManager.recordAutoloopFinal(
2497
+ ctx.workflowId,
2498
+ {
2499
+ ...current,
2500
+ persistence: {
2501
+ ...current.persistence,
2502
+ history
2503
+ }
2504
+ },
2505
+ writer
2506
+ );
2507
+ } catch (err) {
2508
+ console.warn(
2509
+ `[Autoloop] State persistence update failed: ${err instanceof Error ? err.message : String(err)}`
2510
+ );
2511
+ }
2512
+ };
2513
+ if (ctx.stateManager && ctx.workflowId) {
2514
+ const marker = {
2515
+ completedAt: now,
2516
+ terminationReason: result.terminationReason,
2517
+ success: result.loopSuccess,
2518
+ totalIterations: result.totalIterations,
2519
+ summary,
2520
+ persistence: {
2521
+ history: {
2522
+ written: false,
2523
+ lastAttemptAt: now,
2524
+ lastError: void 0
2525
+ }
2526
+ }
2527
+ };
2528
+ try {
2529
+ await ctx.stateManager.recordAutoloopFinal(ctx.workflowId, marker, writer);
2530
+ } catch (err) {
2531
+ console.warn(
2532
+ `[Autoloop] State persistence failed (canonical summary not written): ${err instanceof Error ? err.message : String(err)}`
2533
+ );
2534
+ }
2535
+ }
2536
+ try {
2537
+ const { root: projectRoot, historyPath } = getAutoloopHistoryLocation(ctx.worktreePath);
2538
+ await withPlanningLock(projectRoot, async () => {
2539
+ await fs4.mkdir(path5.dirname(historyPath), { recursive: true });
2540
+ await fs4.appendFile(historyPath, `${JSON.stringify(summary)}
2541
+ `, "utf8");
2542
+ });
2543
+ await updateHistoryPersistenceBestEffort({
2544
+ written: true,
2545
+ lastAttemptAt: now,
2546
+ lastError: void 0
2547
+ });
2548
+ } catch (err) {
2549
+ const errorMsg = err instanceof Error ? err.message : String(err);
2550
+ console.warn(`[Autoloop] History persistence failed: ${errorMsg}`);
2551
+ await updateHistoryPersistenceBestEffort({
2552
+ written: false,
2553
+ lastAttemptAt: now,
2554
+ lastError: errorMsg
2555
+ });
2556
+ }
2557
+ try {
2558
+ await persistAutoloopTrajectory({
2559
+ workflowId: ctx.workflowId,
2560
+ stage: ctx.stage,
2561
+ result,
2562
+ summary
2563
+ });
2564
+ } catch (err) {
2565
+ console.warn(
2566
+ `[Autoloop] Trajectory persistence failed: ${err instanceof Error ? err.message : String(err)}`
2567
+ );
2568
+ }
2569
+ let diagnosticsPersisted = options.preserveDurableDiagnostics === true;
2570
+ if (ctx.workflowId && !options.preserveDurableDiagnostics) {
2571
+ try {
2572
+ const worktreeFingerprint = await computeWorktreeFingerprint(ctx.worktreePath);
2573
+ const { canonicalPersisted } = await persistDurableAutoloopDiagnostics({
2574
+ workflowId: ctx.workflowId,
2575
+ stage: ctx.stage,
2576
+ result,
2577
+ worktreeFingerprint,
2578
+ ...options.providerSwitch ? { providerSwitch: options.providerSwitch } : {}
2579
+ });
2580
+ diagnosticsPersisted = canonicalPersisted;
2581
+ } catch (err) {
2582
+ console.warn(
2583
+ `[Autoloop] Durable diagnostics persistence failed: ${err instanceof Error ? err.message : String(err)}`
2584
+ );
2585
+ }
2586
+ }
2587
+ if (ctx.workflowId && diagnosticsPersisted) {
2588
+ await clearDurableAutoloopIterationSpill({
2589
+ workflowId: ctx.workflowId,
2590
+ stageId: ctx.stage.id
2591
+ });
2592
+ }
2593
+ }
2594
+ async function computeEvidencePlanFromPrompt(input) {
2595
+ const [{ DomainAnalyzer }, { resolveEvidencePlan }] = await Promise.all([
2596
+ import("./domain-analyzer-S35ABUNA.js"),
2597
+ import("./evidence-plan-resolver-5FUAP3SA.js")
2598
+ ]);
2599
+ const analyzer = new DomainAnalyzer();
2600
+ const analysis = await analyzer.analyze(input.prompt);
2601
+ return resolveEvidencePlan(analysis, input.prompt, input.cwd);
2602
+ }
2603
+ function buildRecoveryPrompt(input) {
2604
+ const { config, iteration, lastDurationMs, consecutiveNoOps } = input;
2605
+ if (config.recoveryPromptTemplate) {
2606
+ return config.recoveryPromptTemplate.replace("{iteration}", String(iteration)).replace("{lastDuration}", String(Math.round(lastDurationMs / 1e3))).replace("{consecutiveCount}", String(consecutiveNoOps));
2607
+ }
2608
+ return `
2609
+ ## Recovery Notice
2610
+
2611
+ This is iteration ${iteration}. The previous ${consecutiveNoOps} iteration(s) made no changes to the codebase.
2612
+
2613
+ Please take a different approach:
2614
+ 1. Review the task requirements carefully
2615
+ 2. Consider if you're blocked by something not mentioned
2616
+ 3. Try a minimal first step to verify you can make changes
2617
+ 4. If truly stuck, emit the completion signal with a note about what's blocking progress
2618
+
2619
+ Last iteration took ${Math.round(lastDurationMs / 1e3)}s without producing any file changes.
2620
+ `.trim();
2621
+ }
2622
+ function isAutoloopStage(stage) {
2623
+ return stage.type === "autoloop";
2624
+ }
2625
+ async function detectNonEmptyEditsJsonl(worktreePath) {
2626
+ try {
2627
+ const stat3 = await fs4.stat(path5.join(worktreePath, ".build-project", "edits.jsonl"));
2628
+ if (!stat3.isFile()) return null;
2629
+ if (stat3.size <= 0) return null;
2630
+ return { bytes: stat3.size };
2631
+ } catch {
2632
+ return null;
2633
+ }
2634
+ }
2635
+ async function executeAutoloopStage(ctx) {
2636
+ const startTime = performance2.now();
2637
+ const invocationStartedAtMs = Date.now();
2638
+ let result;
2639
+ let runSummary;
2640
+ let executionError;
2641
+ const iterationHistory = [];
2642
+ const policyRecorder = createAutoloopEventRecorder();
2643
+ let runStartedAtMsForSummary = invocationStartedAtMs;
2644
+ let previousIterationCountForResult = 0;
2645
+ let resumedFromTerminalDiagnostics = false;
2646
+ let priorTerminalCostForResult;
2647
+ const repairReviewWarnings = /* @__PURE__ */ new Set();
2648
+ let iterationHistoryArtifactPersisted = false;
2649
+ let resultArtifactPersisted = false;
2650
+ let failureArtifactPersisted = false;
2651
+ const providerFailure = {};
2652
+ const providerSwitch = autoloopProviderSwitch(ctx);
2653
+ const pendingIterationRecords = [];
2654
+ const iterationFingerprints = /* @__PURE__ */ new Map();
2655
+ try {
2656
+ const config = ctx.stage.autoloop ?? {};
2657
+ const repairScopeBaseline = ctx.stage.automaticRepair === true ? ctx.repairScopeBaseline ?? await captureAutomaticRepairScopeBaseline(ctx.worktreePath) : void 0;
2658
+ let promptLanguage = null;
2659
+ try {
2660
+ promptLanguage = selectPrimaryPromptLanguage(
2661
+ detectProjectCapabilities(ctx.worktreePath).languages
2662
+ );
2663
+ } catch {
2664
+ promptLanguage = null;
2665
+ }
2666
+ const {
2667
+ AutonomousLoopController,
2668
+ createDurationMs,
2669
+ createIterationCount,
2670
+ createProgressScore,
2671
+ matchesCompletionSignal,
2672
+ parseCompletionSignal
2673
+ } = await import("./autonomous-loop-controller-2WL363LW.js");
2674
+ const log = (msg) => {
2675
+ if (ctx.verbose) console.log(chalk.gray(` [${ctx.stage.id}] ${msg}`));
2676
+ };
2677
+ const maxIterations = config.maxIterations ?? 3;
2678
+ const { resolveAutoloopTimeboxMs, DEFAULT_AUTOLOOP_VALIDATION_MODE } = await import("./autoloop-run-defaults-GWGZBNT7.js");
2679
+ const timeboxMs = resolveAutoloopTimeboxMs(config.timeboxMs);
2680
+ const minProgress = config.minProgress ?? 0.05;
2681
+ const validationMode = config.validationMode === "heuristic" || config.validationMode === "none" || config.validationMode === "deterministic" ? config.validationMode : DEFAULT_AUTOLOOP_VALIDATION_MODE;
2682
+ const iterationTimeboxMs = config.iterationTimeboxMs;
2683
+ const requireEarlyDiff = config.requireEarlyDiff ?? true;
2684
+ let completionSignal = validationMode === "deterministic" ? null : typeof config.completionSignal === "string" && config.completionSignal.length > 0 ? parseCompletionSignal(config.completionSignal) : null;
2685
+ let completionSignalXmlTag = completionSignal?.type === "xml" ? completionSignal.tag : void 0;
2686
+ const backoffMaxRetries = (() => {
2687
+ const raw = config.backoffMaxRetries;
2688
+ if (raw === void 0) return 0;
2689
+ const n = Number(raw);
2690
+ if (!Number.isFinite(n)) return 0;
2691
+ return Math.max(0, Math.floor(n));
2692
+ })();
2693
+ let rawGateCommands = [];
2694
+ let evidencePlanUsed = false;
2695
+ let validationSourceExplanation = "";
2696
+ const explicitValidationConfigured = Array.isArray(config.gates) && config.gates.length > 0 || Array.isArray(config.qualityGateCommands) && config.qualityGateCommands.length > 0 || config.stopOnQualityGate === true;
2697
+ const simpleArtifactGoal = parseSimpleArtifactGoal(ctx.stage.prompt ?? "");
2698
+ const explicitVerificationCommands = extractRequestedVerification(ctx.stage.prompt ?? "");
2699
+ const explicitVerificationIntent = hasExplicitVerificationIntent(ctx.stage.prompt ?? "") || explicitVerificationCommands.length > 0;
2700
+ const shouldSuppressInferredGatesForSimpleArtifact = simpleArtifactGoal !== void 0 && !explicitValidationConfigured && !explicitVerificationIntent;
2701
+ const shouldComputeEvidencePlan = !explicitValidationConfigured && !shouldSuppressInferredGatesForSimpleArtifact;
2702
+ let evidencePlan = ctx.evidencePlan;
2703
+ if (!evidencePlan && shouldComputeEvidencePlan) {
2704
+ try {
2705
+ const basePrompt = ctx.stage.prompt ?? "";
2706
+ if (basePrompt.trim().length > 0) {
2707
+ evidencePlan = await computeEvidencePlanFromPrompt({
2708
+ prompt: basePrompt,
2709
+ cwd: ctx.worktreePath
2710
+ });
2711
+ }
2712
+ } catch (err) {
2713
+ if (ctx.verbose) {
2714
+ log(
2715
+ `Evidence plan unavailable (skipping): ${err instanceof Error ? err.message : String(err)}`
2716
+ );
2717
+ }
2718
+ }
2719
+ }
2720
+ if (Array.isArray(config.gates) && config.gates.length > 0) {
2721
+ validationSourceExplanation = "explicit autoloop.gates config";
2722
+ } else if (Array.isArray(config.qualityGateCommands) && config.qualityGateCommands.length > 0) {
2723
+ rawGateCommands = config.qualityGateCommands;
2724
+ validationSourceExplanation = "explicit autoloop.qualityGateCommands config";
2725
+ } else if (config.stopOnQualityGate === true) {
2726
+ rawGateCommands = ctx.stage.verify?.commands ?? [];
2727
+ validationSourceExplanation = "stage verify.commands";
2728
+ } else if (explicitVerificationCommands.length > 0) {
2729
+ const evidenceCommands = evidencePlan && !evidencePlan.isFallback && !shouldSuppressInferredGatesForSimpleArtifact ? evidencePlan.surfaces.flatMap((surface) => surface.commands) : [];
2730
+ rawGateCommands = unionCustomGateCommands(
2731
+ { selectedCommands: explicitVerificationCommands },
2732
+ { selectedCommands: evidenceCommands }
2733
+ );
2734
+ evidencePlanUsed = evidenceCommands.length > 0;
2735
+ validationSourceExplanation = "explicit verification commands from prompt";
2736
+ if (evidencePlanUsed) validationSourceExplanation += " and evidence plan";
2737
+ } else if (evidencePlan && !shouldSuppressInferredGatesForSimpleArtifact) {
2738
+ rawGateCommands = evidencePlan.surfaces.flatMap((surface) => surface.commands);
2739
+ evidencePlanUsed = true;
2740
+ validationSourceExplanation = `evidence plan (${evidencePlan.surfaces.length} surface(s))`;
2741
+ if (ctx.verbose) {
2742
+ log(
2743
+ `Using validation surface from evidence plan (${evidencePlan.surfaces.length} surface(s))`
2744
+ );
2745
+ for (const surface of evidencePlan.surfaces) {
2746
+ log(` - ${surface.description} (source: ${surface.source})`);
2747
+ if (surface.rationale && surface.rationale.length > 0) {
2748
+ for (const reason of surface.rationale) {
2749
+ log(` \u2022 ${reason}`);
2750
+ }
2751
+ }
2752
+ }
2753
+ if (evidencePlan.isFallback) {
2754
+ log("Evidence plan is fallback (minimal safe checks)");
2755
+ }
2756
+ }
2757
+ }
2758
+ const shouldDefaultGateOrdering = (!Array.isArray(config.qualityGateCommands) || config.qualityGateCommands.length === 0) && (!Array.isArray(config.gates) || config.gates.length === 0) && !evidencePlanUsed;
2759
+ const gatesFromConfig = Array.isArray(config.gates) && config.gates.length > 0 ? normalizeGates(config.gates) : null;
2760
+ const rawGatesFromCommands = rawGateCommands.length > 0 ? normalizeGates(rawGateCommands) : [];
2761
+ const defaultGatesFromCommands = shouldDefaultGateOrdering && rawGatesFromCommands.length > 1 ? orderGatesByType(rawGatesFromCommands) : rawGatesFromCommands;
2762
+ const gatesConfigured = (gatesFromConfig ?? defaultGatesFromCommands).map(
2763
+ (g, idx) => ({
2764
+ ...g,
2765
+ id: g.id || `gate-${idx + 1}`,
2766
+ repair: g.repair ?? (g.deterministic !== false && g.mustPass !== false ? { maxAttempts: 2 } : void 0)
2767
+ })
2768
+ );
2769
+ const qualityGatePolicy = config.qualityGatePolicy ?? "deterministic";
2770
+ const { gatesToRun, filteredGates } = qualityGatePolicy === "deterministic" ? filterDeterministicGates(gatesConfigured) : { gatesToRun: gatesConfigured, filteredGates: [] };
2771
+ const gatesEnabled = gatesToRun.length > 0;
2772
+ const durableResumeState = ctx.workflowId !== void 0 ? await loadDurableAutoloopResumeState({
2773
+ workflowId: ctx.workflowId,
2774
+ stageId: ctx.stage.id,
2775
+ // Verify the worktree still holds the prior iterations' work before
2776
+ // trusting the count; on mismatch this resets to 0 (re-run from
2777
+ // scratch) rather than silently under-iterating against a fresh tree.
2778
+ worktreePath: ctx.worktreePath
2779
+ }) : null;
2780
+ if (durableResumeState?.fingerprintMismatch) {
2781
+ log(
2782
+ "Warning: prior autoloop state does not match the current worktree (HEAD or working-tree changes differ); re-running from iteration 0."
2783
+ );
2784
+ }
2785
+ const previousIterationCount = durableResumeState?.completedIterations ?? 0;
2786
+ restoreStageProviderSwitch(
2787
+ providerSwitch,
2788
+ durableResumeState?.providerSwitch,
2789
+ previousIterationCount
2790
+ );
2791
+ previousIterationCountForResult = previousIterationCount;
2792
+ const remainingIterations = Math.max(0, maxIterations - previousIterationCount);
2793
+ const hasTerminalDurableOutcome = durableResumeState?.source === "diagnostics" && durableResumeState.priorOutcome !== void 0;
2794
+ resumedFromTerminalDiagnostics = hasTerminalDurableOutcome;
2795
+ priorTerminalCostForResult = durableResumeState?.priorOutcome?.cost;
2796
+ const durableRunStartedAtMs = durableResumeState?.runStartedAtMs ?? invocationStartedAtMs;
2797
+ const wallClockRunStartMs = invocationStartedAtMs;
2798
+ runStartedAtMsForSummary = durableRunStartedAtMs;
2799
+ const elapsedLoopMs = () => Math.max(0, Date.now() - wallClockRunStartMs, performance2.now() - startTime);
2800
+ log(
2801
+ `Starting autoloop (maxIterations: ${maxIterations}, timeboxMs: ${timeboxMs}, previousIterations: ${previousIterationCount})`
2802
+ );
2803
+ if (validationSourceExplanation && ctx.verbose) {
2804
+ log(`Validation source: ${validationSourceExplanation}`);
2805
+ }
2806
+ {
2807
+ const provider = resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath);
2808
+ const executionIntent = resolveStageExecutionIntent(ctx.stage);
2809
+ const detection = getProviderDetectionMode(provider);
2810
+ if (executionIntent === "edit") {
2811
+ log(
2812
+ detection.emitsJsonl ? `Workspace mode: enhanced (provider '${provider}' supports JSONL edit contract)` : `Workspace mode: passthrough (provider '${provider}' is git-only; JSONL protocol disabled)`
2813
+ );
2814
+ }
2815
+ }
2816
+ if (typeof config.completionSignal === "string" && config.completionSignal.length > 0) {
2817
+ if (validationMode === "deterministic") {
2818
+ log("Completion signal ignored in deterministic mode");
2819
+ } else if (!completionSignal) {
2820
+ log(`Invalid completionSignal: ${config.completionSignal}`);
2821
+ } else {
2822
+ log(`Completion signal enabled (${completionSignal.type})`);
2823
+ }
2824
+ }
2825
+ if (validationMode !== "heuristic") {
2826
+ log(`Validation mode: ${validationMode}`);
2827
+ }
2828
+ if (gatesEnabled && filteredGates.length > 0) {
2829
+ log(
2830
+ `Filtered ${filteredGates.length} non-deterministic gate(s) (policy: ${qualityGatePolicy})`
2831
+ );
2832
+ if (ctx.verbose) {
2833
+ for (const g of filteredGates.slice(0, 6)) log(` - ${g.command}`);
2834
+ if (filteredGates.length > 6) log(` ... and ${filteredGates.length - 6} more`);
2835
+ }
2836
+ }
2837
+ if (Array.isArray(config.qualityGateCommands) && config.qualityGateCommands.length > 0 || config.stopOnQualityGate === true || Array.isArray(config.gates) && config.gates.length > 0) {
2838
+ if (!gatesEnabled) {
2839
+ log(
2840
+ "Quality gates enabled but no deterministic gates were available; check autoloop.gates/qualityGateCommands and policy."
2841
+ );
2842
+ }
2843
+ }
2844
+ if (validationMode !== "deterministic" && config.completionOnQualityGatePass === true && !completionSignal) {
2845
+ const { createAutoloopCompletionToken } = await import("./autoloop-helpers-VQGQNTT6.js");
2846
+ const generated = createAutoloopCompletionToken({});
2847
+ completionSignal = parseCompletionSignal(generated.completionSignal);
2848
+ completionSignalXmlTag = completionSignal?.type === "xml" ? completionSignal.tag : void 0;
2849
+ log(
2850
+ `completionOnQualityGatePass: auto-generated completion signal (${completionSignalXmlTag ?? "none"})`
2851
+ );
2852
+ }
2853
+ const dynamicGates = [];
2854
+ let totalTokens = 0;
2855
+ let finalProgressScore = 0;
2856
+ const configuredGatePreset = config.gatePreset;
2857
+ const gateEscalation = config.gateEscalation;
2858
+ const noOpIntervention = config.noOpIntervention;
2859
+ let consecutiveNoOps = 0;
2860
+ let interventionActive = false;
2861
+ let lastIterationDurationMs = 0;
2862
+ let totalRepairAttemptsForEscalation = 0;
2863
+ const failedGateIdsForEscalation = [];
2864
+ let currentGatePreset = configuredGatePreset ?? "fast";
2865
+ let aiValidationShortCircuited = false;
2866
+ const gateCommandsForPrompt = () => [...gatesToRun, ...dynamicGates].map((g) => g.command);
2867
+ const loopController = new AutonomousLoopController({
2868
+ maxIterations: createIterationCount(Math.max(1, remainingIterations)),
2869
+ timeboxMs: createDurationMs(timeboxMs),
2870
+ minProgressThreshold: createProgressScore(minProgress),
2871
+ validationMode,
2872
+ ...completionSignal ? { completionSignal } : {},
2873
+ requirementsTaskText: ctx.stage.prompt ?? "",
2874
+ requirements: {
2875
+ mode: "auto",
2876
+ minCoverage: 1,
2877
+ requireEvidence: true,
2878
+ blockNewRepoRootMarkdown: true
2879
+ },
2880
+ wallClockRunStartMs,
2881
+ ...ctx.abortSignal ? { abortSignal: ctx.abortSignal } : {},
2882
+ resourceLimits: FLYWHEEL_AUTOLOOP_CONTROLLER_RESOURCE_LIMITS,
2883
+ // Flywheel runs quality gates in this executor so we can:
2884
+ // - use stage-specific commands (matching verify)
2885
+ // - feed failures back into subsequent iteration prompts
2886
+ runQualityGates: false,
2887
+ backoffConfig: {
2888
+ initialDelayMs: createDurationMs(0),
2889
+ maxDelayMs: createDurationMs(0),
2890
+ multiplier: 1,
2891
+ maxRetries: backoffMaxRetries
2892
+ },
2893
+ progressConfig: {
2894
+ windowSize: 3,
2895
+ trendThreshold: 0.01,
2896
+ confidenceThreshold: 0.7,
2897
+ // Compare working tree against the stage HEAD so uncommitted changes count as progress.
2898
+ baseRef: "HEAD"
2899
+ }
2900
+ });
2901
+ loopController.setWorkingDir(ctx.worktreePath);
2902
+ const flywheelInitialPolicy = prepareInitialAutoloopPolicy({
2903
+ taskText: ctx.stage.prompt ?? "",
2904
+ userProvidedValidationMode: validationMode,
2905
+ userProvidedAIJudge: false
2906
+ });
2907
+ loopController.setInitialPolicyLevel(flywheelInitialPolicy.level);
2908
+ let terminationReason = "success";
2909
+ let loopSuccess = false;
2910
+ let runtimeEvidenceCheck;
2911
+ if (ctx.workflowId && ctx.stateManager) {
2912
+ const writer = `flywheel-autoloop:${ctx.stage.id}`;
2913
+ loopController.on("iteration_complete", (payload) => {
2914
+ pendingIterationRecords.push(
2915
+ (async () => {
2916
+ const result2 = payload?.result;
2917
+ const iteration = typeof result2?._iteration === "number" ? result2._iteration : void 0;
2918
+ const score = typeof result2?.progress?.score === "number" ? result2.progress.score : void 0;
2919
+ if (iteration === void 0) return;
2920
+ const recordIdx = Math.max(0, iteration - 1);
2921
+ const durableIdx = previousIterationCount + recordIdx;
2922
+ const local = iterationHistory[recordIdx];
2923
+ if (local && score !== void 0) {
2924
+ local.progressScore = score;
2925
+ }
2926
+ const success = local ? local.success : typeof result2?.success === "boolean" ? result2.success : true;
2927
+ const durationMs2 = local ? local.durationMs : typeof result2?.duration === "number" ? result2.duration : 0;
2928
+ if (ctx.stateManager && ctx.workflowId) {
2929
+ const iterationRecord = {
2930
+ index: durableIdx,
2931
+ success,
2932
+ duration: Math.max(0, Math.round(durationMs2)),
2933
+ timestamp: Date.now(),
2934
+ ...score !== void 0 ? { score } : {}
2935
+ };
2936
+ let spillWritten = false;
2937
+ try {
2938
+ const worktreeFingerprint = iterationFingerprints.get(iteration);
2939
+ await persistDurableAutoloopIterationSpill({
2940
+ workflowId: ctx.workflowId,
2941
+ stageId: ctx.stage.id,
2942
+ writer,
2943
+ record: iterationRecord,
2944
+ priorFeedback: local ? buildAutoloopResumeFeedback(local) : void 0,
2945
+ worktreeFingerprint,
2946
+ runStartedAtMs: durableRunStartedAtMs,
2947
+ providerSwitch: durableStageProviderSwitch(providerSwitch)
2948
+ });
2949
+ spillWritten = true;
2950
+ } catch (spillError) {
2951
+ console.warn(
2952
+ `[flywheel-autoloop] iteration spill persistence failed for ${ctx.stage.id} (iteration ${durableIdx}): ${spillError instanceof Error ? spillError.message : String(spillError)}`
2953
+ );
2954
+ }
2955
+ try {
2956
+ await ctx.stateManager.recordIteration(ctx.workflowId, iterationRecord, writer);
2957
+ } catch (error) {
2958
+ if (spillWritten) {
2959
+ console.warn(
2960
+ `[flywheel-autoloop] recordIteration failed for ${ctx.stage.id} (iteration ${durableIdx}; durable spill already written): ${error instanceof Error ? error.message : String(error)}`
2961
+ );
2962
+ } else {
2963
+ console.warn(
2964
+ `[flywheel-autoloop] recordIteration failed for ${ctx.stage.id} (iteration ${durableIdx}) AND durable spill was NOT written \u2014 this iteration is not durably recorded in either store and may be lost on resume: ${error instanceof Error ? error.message : String(error)}`
2965
+ );
2966
+ }
2967
+ }
2968
+ }
2969
+ })()
2970
+ );
2971
+ });
2972
+ }
2973
+ let previousIterationWorktreeFingerprint;
2974
+ try {
2975
+ previousIterationWorktreeFingerprint = await computeWorktreeFingerprint(ctx.worktreePath);
2976
+ } catch {
2977
+ previousIterationWorktreeFingerprint = void 0;
2978
+ }
2979
+ const captureIterationFingerprint = async (iteration) => {
2980
+ try {
2981
+ const current = await computeWorktreeFingerprint(ctx.worktreePath);
2982
+ iterationFingerprints.set(iteration, current);
2983
+ const prior = previousIterationWorktreeFingerprint;
2984
+ previousIterationWorktreeFingerprint = current;
2985
+ if (!prior || !current) return void 0;
2986
+ if (prior.headSha === current.headSha && prior.dirtyDigest === current.dirtyDigest) {
2987
+ return "";
2988
+ }
2989
+ return `${current.headSha}:${current.dirtyDigest}`;
2990
+ } catch {
2991
+ iterationFingerprints.set(iteration, void 0);
2992
+ previousIterationWorktreeFingerprint = void 0;
2993
+ return void 0;
2994
+ }
2995
+ };
2996
+ try {
2997
+ const finalIteration = hasTerminalDurableOutcome || remainingIterations === 0 ? 0 : await loopController.start(async (iteration, loopContext) => {
2998
+ const effectiveIteration = previousIterationCount + iteration;
2999
+ const iterationAbortSignal = loopContext.abortSignal ?? ctx.abortSignal;
3000
+ const iterationStart = performance2.now();
3001
+ const timeBreakdown = {
3002
+ providerMs: 0,
3003
+ gateMs: 0,
3004
+ repairMs: 0,
3005
+ testMs: 0,
3006
+ buildMs: 0,
3007
+ overheadMs: 0
3008
+ };
3009
+ const recordEarlyFailure = async (message, terminationSignal, cost = 0) => {
3010
+ const changedFiles2 = (await getWorktreeChangedFiles(ctx.worktreePath)).files;
3011
+ const durationMs2 = performance2.now() - iterationStart;
3012
+ const record2 = {
3013
+ iteration: effectiveIteration,
3014
+ success: false,
3015
+ providerExecutionStatus: "failed",
3016
+ providerOutputStatus: "missing",
3017
+ providerStatusDetail: message,
3018
+ changedFiles: changedFiles2,
3019
+ cost,
3020
+ durationMs: durationMs2,
3021
+ timeBreakdown: {
3022
+ ...timeBreakdown,
3023
+ overheadMs: Math.max(0, durationMs2)
3024
+ },
3025
+ error: message,
3026
+ terminationSignal,
3027
+ policyEvents: policyRecorder.events.filter(
3028
+ (event) => event.iteration === effectiveIteration
3029
+ )
3030
+ };
3031
+ iterationHistory.push(record2);
3032
+ if (ctx.artifactStore) {
3033
+ await saveBestEffort(
3034
+ ctx.artifactStore,
3035
+ ctx.stage.id,
3036
+ `iteration-${effectiveIteration}`,
3037
+ record2
3038
+ );
3039
+ }
3040
+ return record2;
3041
+ };
3042
+ let basePromptWithIntervention = ctx.stage.prompt ?? "";
3043
+ if (interventionActive && noOpIntervention) {
3044
+ const recoveryPrompt = buildRecoveryPrompt({
3045
+ config: noOpIntervention,
3046
+ iteration: effectiveIteration,
3047
+ lastDurationMs: lastIterationDurationMs,
3048
+ consecutiveNoOps
3049
+ });
3050
+ basePromptWithIntervention = `${recoveryPrompt}
3051
+
3052
+ ---
3053
+
3054
+ ${basePromptWithIntervention}`;
3055
+ log(
3056
+ `Intervention active: injecting recovery prompt (${consecutiveNoOps} consecutive no-ops)`
3057
+ );
3058
+ }
3059
+ const prompt = buildIterationPrompt({
3060
+ basePrompt: basePromptWithIntervention,
3061
+ iteration: effectiveIteration,
3062
+ iterationHistory,
3063
+ resumedFeedback: durableResumeState?.priorFeedback,
3064
+ gateCommands: gateCommandsForPrompt(),
3065
+ requiresTests: config.requiresTests,
3066
+ testFilePatterns: config.testFilePatterns,
3067
+ promptLanguage
3068
+ });
3069
+ log(`Iteration ${effectiveIteration}: executing provider...`);
3070
+ let providerResult;
3071
+ let providerCost = 0;
3072
+ let providerCostUnavailableReason;
3073
+ let providerTokens = 0;
3074
+ let providerError;
3075
+ let providerOutputText;
3076
+ let providerExecutionStatus;
3077
+ let providerOutputStatus;
3078
+ let providerStatusDetail;
3079
+ let providerHardExitFailure = false;
3080
+ let providerVerificationUnavailableRecovered = false;
3081
+ const iterationStartHead = ctx.stage.allowlist !== void 0 && (ctx.stage.allowlistMode ?? "warn") !== "off" ? await readWorktreeHead(ctx.worktreePath) : void 0;
3082
+ const providerStart = performance2.now();
3083
+ try {
3084
+ const elapsedLoopMsForProvider = elapsedLoopMs();
3085
+ const remainingLoopMs = Math.max(0, timeboxMs - elapsedLoopMsForProvider);
3086
+ if (remainingLoopMs <= 0) {
3087
+ const message = "Autoloop timebox exhausted before provider execution";
3088
+ policyRecorder.emit({
3089
+ policy: "timebox",
3090
+ eventType: "terminated",
3091
+ iteration: effectiveIteration,
3092
+ reason: message,
3093
+ details: {
3094
+ elapsedLoopMs: elapsedLoopMsForProvider,
3095
+ timeboxMs
3096
+ }
3097
+ });
3098
+ await recordEarlyFailure(message, "timebox_exceeded");
3099
+ throw new AutoloopTerminationError(message, "timebox_exceeded");
3100
+ }
3101
+ const perIterationCapMs = typeof iterationTimeboxMs === "number" && iterationTimeboxMs > 0 ? Math.min(remainingLoopMs, iterationTimeboxMs) : remainingLoopMs;
3102
+ providerResult = await runSwitchingProviderIteration(providerSwitch, {
3103
+ executor: ctx.executor,
3104
+ stage: () => ctx.stage,
3105
+ worktreePath: ctx.worktreePath,
3106
+ prompt,
3107
+ timeoutMs: perIterationCapMs,
3108
+ loopTimeLeftMs: () => Math.max(0, timeboxMs - elapsedLoopMs()),
3109
+ abortSignal: iterationAbortSignal,
3110
+ ...ctx.providerExecutionTransport !== void 0 ? {
3111
+ defaultProviderExecutionTransport: ctx.providerExecutionTransport
3112
+ } : {}
3113
+ });
3114
+ const rawProviderExitCode = providerResult.result?.exitCode;
3115
+ providerHardExitFailure = typeof rawProviderExitCode === "number" && rawProviderExitCode !== 0;
3116
+ providerCostUnavailableReason = readCostMetricsUnavailableReason(
3117
+ providerResult.costMetrics
3118
+ );
3119
+ const reportedProviderCost = providerResult.costMetrics?.totalCost;
3120
+ if (providerCostUnavailableReason === void 0 && typeof reportedProviderCost === "number" && Number.isFinite(reportedProviderCost)) {
3121
+ providerCost = reportedProviderCost;
3122
+ }
3123
+ providerTokens = resolveTokenAccounting(providerResult.costMetrics).totalTokens;
3124
+ timeBreakdown.providerMs = performance2.now() - providerStart;
3125
+ const { selectProviderEngineStdout } = await import("./autoloop-helpers-VQGQNTT6.js");
3126
+ const engineStdout = selectProviderEngineStdout(
3127
+ providerResult.result
3128
+ );
3129
+ const engineStderr = providerResult.result?.stderr ?? "";
3130
+ const stdoutText = typeof engineStdout === "string" ? engineStdout : String(engineStdout ?? "");
3131
+ const stderrText = typeof engineStderr === "string" ? engineStderr : String(engineStderr ?? "");
3132
+ const trimmed = buildProviderOutputExcerpt(
3133
+ resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath),
3134
+ stdoutText,
3135
+ stderrText
3136
+ );
3137
+ if (trimmed.length > 0) {
3138
+ providerOutputText = trimmed;
3139
+ } else if (typeof providerResult.output === "string" && providerResult.output.length > 0) {
3140
+ providerOutputText = providerResult.output;
3141
+ } else if (providerResult.result !== void 0 && providerResult.result !== null) {
3142
+ try {
3143
+ providerOutputText = JSON.stringify(providerResult.result);
3144
+ } catch {
3145
+ providerOutputText = String(providerResult.result);
3146
+ }
3147
+ }
3148
+ const providerStatus = classifyAutoloopProviderStatus({
3149
+ providerResult,
3150
+ stdoutText,
3151
+ stderrText
3152
+ });
3153
+ providerExecutionStatus = providerStatus.executionStatus;
3154
+ providerOutputStatus = providerStatus.outputStatus;
3155
+ providerStatusDetail = providerStatus.detail;
3156
+ providerError = providerResult.success ? providerStatus.error : providerResult.error?.message ?? providerStatus.error;
3157
+ } catch (error) {
3158
+ if (error instanceof AutoloopTerminationError) {
3159
+ throw error;
3160
+ }
3161
+ timeBreakdown.providerMs = performance2.now() - providerStart;
3162
+ providerResult = {
3163
+ success: false,
3164
+ error: error instanceof Error ? error : new Error(String(error))
3165
+ };
3166
+ providerError = error instanceof Error ? error.message : String(error);
3167
+ providerExecutionStatus = "failed";
3168
+ providerOutputStatus = "missing";
3169
+ providerStatusDetail = providerError;
3170
+ }
3171
+ totalTokens += providerTokens;
3172
+ const stageProvider = resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath);
3173
+ providerFailure.stop = providerFailureStopMessage(
3174
+ ctx,
3175
+ stageProvider,
3176
+ providerResult,
3177
+ providerError
3178
+ );
3179
+ if (providerFailure.stop) {
3180
+ await recordEarlyFailure(providerFailure.stop, "error", providerCost);
3181
+ throw new AutoloopTerminationError(providerFailure.stop, "error");
3182
+ }
3183
+ const allowlist = ctx.stage.allowlist;
3184
+ const allowlistMode2 = ctx.stage.allowlistMode ?? "warn";
3185
+ let allowlistViolations = [];
3186
+ let allowlistRevertedFiles;
3187
+ let allowlistDrift = false;
3188
+ let allowlistError;
3189
+ allowlistError = await flattenIterationCommits(ctx.worktreePath, iterationStartHead);
3190
+ providerError = providerError ?? allowlistError;
3191
+ let changedFilesResult = await getWorktreeChangedFiles(ctx.worktreePath);
3192
+ let changedFiles = changedFilesResult.files;
3193
+ let simpleArtifactGoalSatisfied;
3194
+ if (!changedFilesResult.readable) {
3195
+ allowlistError = "Changed-file status unavailable: git status failed";
3196
+ providerError = providerError ?? allowlistError;
3197
+ }
3198
+ if (changedFiles.length === 0) {
3199
+ const provider = resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath);
3200
+ const detection = getProviderDetectionMode(provider);
3201
+ if (!detection.emitsJsonl) {
3202
+ const jsonl = await detectNonEmptyEditsJsonl(ctx.worktreePath);
3203
+ if (jsonl) {
3204
+ providerError = providerError ?? `Protocol mismatch: detected .build-project/edits.jsonl (${jsonl.bytes} bytes) but provider '${provider}' is configured as git-only; no tracked changes were applied.`;
3205
+ }
3206
+ }
3207
+ }
3208
+ if (ctx.stage.automaticRepair === true && changedFilesResult.readable) {
3209
+ const repairScope = await enforceAutomaticRepairScope({
3210
+ worktreePath: ctx.worktreePath,
3211
+ baseline: repairScopeBaseline,
3212
+ explicitScope: allowlist
3213
+ });
3214
+ changedFiles = repairScope.changedFiles;
3215
+ allowlistViolations = repairScope.violations;
3216
+ allowlistRevertedFiles = repairScope.revertedFiles;
3217
+ allowlistDrift = repairScope.violations.length > 0;
3218
+ if (repairScope.error) {
3219
+ allowlistError = repairScope.error;
3220
+ providerError = providerError ?? repairScope.error;
3221
+ }
3222
+ } else if (allowlistMode2 !== "off" && allowlist !== void 0 && changedFiles.length > 0) {
3223
+ let scopeDecision = decideStageChangeScope({
3224
+ changedPaths: changedFiles,
3225
+ explicitScope: allowlist ?? [],
3226
+ allowlistMode: allowlistMode2,
3227
+ mutationOrigin: "provider"
3228
+ });
3229
+ allowlistViolations = scopeDecision.allowlistViolations;
3230
+ allowlistDrift = allowlistViolations.length > 0;
3231
+ if (scopeDecision.disposition === "rollback") {
3232
+ allowlistRevertedFiles = await revertFilesToHead(
3233
+ ctx.worktreePath,
3234
+ allowlistViolations
3235
+ );
3236
+ changedFilesResult = await getWorktreeChangedFiles(ctx.worktreePath);
3237
+ changedFiles = changedFilesResult.files;
3238
+ if (!changedFilesResult.readable) {
3239
+ allowlistError = "Changed-file status unavailable after allowlist revert";
3240
+ providerError = providerError ?? allowlistError;
3241
+ }
3242
+ scopeDecision = decideStageChangeScope({
3243
+ changedPaths: changedFiles,
3244
+ explicitScope: allowlist ?? [],
3245
+ allowlistMode: allowlistMode2,
3246
+ mutationOrigin: "provider"
3247
+ });
3248
+ allowlistViolations = scopeDecision.allowlistViolations;
3249
+ allowlistDrift = allowlistViolations.length > 0;
3250
+ if (allowlistDrift) {
3251
+ allowlistError = `Allowlist violation: ${allowlistViolations.length} file(s) outside allowlist`;
3252
+ providerError = providerError ?? allowlistError;
3253
+ }
3254
+ } else if (scopeDecision.disposition === "filter") {
3255
+ const filteredViolations = allowlistViolations;
3256
+ allowlistRevertedFiles = await revertFilesToHead(
3257
+ ctx.worktreePath,
3258
+ filteredViolations
3259
+ );
3260
+ changedFilesResult = await getWorktreeChangedFiles(ctx.worktreePath);
3261
+ changedFiles = changedFilesResult.files;
3262
+ if (!changedFilesResult.readable) {
3263
+ allowlistError = "Changed-file status unavailable after allowlist filter";
3264
+ providerError = providerError ?? allowlistError;
3265
+ }
3266
+ const residualViolations = decideStageChangeScope({
3267
+ changedPaths: changedFiles,
3268
+ explicitScope: allowlist ?? [],
3269
+ allowlistMode: allowlistMode2,
3270
+ mutationOrigin: "provider"
3271
+ }).allowlistViolations;
3272
+ allowlistViolations = filteredViolations;
3273
+ allowlistDrift = true;
3274
+ if (residualViolations.length > 0) {
3275
+ allowlistError = `Allowlist filter violation: ${residualViolations.length} file(s) outside allowlist could not be filtered`;
3276
+ providerError = providerError ?? allowlistError;
3277
+ allowlistViolations = residualViolations;
3278
+ }
3279
+ } else if (allowlistDrift && allowlistMode2 === "warn") {
3280
+ providerError = providerError ?? `Allowlist drift (warn): ${allowlistViolations.length} file(s) outside allowlist`;
3281
+ }
3282
+ }
3283
+ if (!allowlistError && !explicitValidationConfigured && !explicitVerificationIntent && changedFilesResult.readable) {
3284
+ const simpleGoalResult = await evaluateSimpleArtifactGoal({
3285
+ prompt: ctx.stage.prompt ?? "",
3286
+ worktreePath: ctx.worktreePath,
3287
+ changedFiles
3288
+ });
3289
+ if (simpleGoalResult.satisfied && simpleGoalResult.goal) {
3290
+ simpleArtifactGoalSatisfied = simpleGoalResult.goal;
3291
+ policyRecorder.emit({
3292
+ policy: "goal_gate",
3293
+ eventType: "terminated",
3294
+ iteration: effectiveIteration,
3295
+ reason: "simple artifact goal satisfied",
3296
+ details: { path: simpleArtifactGoalSatisfied.path }
3297
+ });
3298
+ log(`Simple artifact goal satisfied: ${simpleArtifactGoalSatisfied.path}`);
3299
+ }
3300
+ }
3301
+ let iterationCompletionProof;
3302
+ if (stageHasHostDeterministicCompletionSurface(ctx.stage)) {
3303
+ try {
3304
+ iterationCompletionProof = await evaluateStageCompletionProof({
3305
+ stage: ctx.stage,
3306
+ worktreePath: ctx.worktreePath,
3307
+ stdout: providerOutputText ?? "",
3308
+ providerText: providerOutputText,
3309
+ attemptChangedFiles: changedFiles,
3310
+ allowlist: collectDeclaredStageTargets(ctx.stage),
3311
+ stageId: ctx.stage.id
3312
+ });
3313
+ if (iterationCompletionProof.contractsSatisfied && iterationCompletionProof.mayAcceptOutcome) {
3314
+ policyRecorder.emit({
3315
+ policy: "goal_gate",
3316
+ eventType: "evaluated",
3317
+ iteration: effectiveIteration,
3318
+ reason: "host completion surface satisfied",
3319
+ details: {
3320
+ alreadySatisfied: iterationCompletionProof.alreadySatisfied,
3321
+ source: iterationCompletionProof.evidence?.source
3322
+ }
3323
+ });
3324
+ } else if (iterationCompletionProof.hasHostSurface && !iterationCompletionProof.contractsSatisfied) {
3325
+ policyRecorder.emit({
3326
+ policy: "goal_gate",
3327
+ eventType: "evaluated",
3328
+ iteration: effectiveIteration,
3329
+ reason: "host completion surface not satisfied",
3330
+ details: {
3331
+ feedback: iterationCompletionProof.feedback
3332
+ }
3333
+ });
3334
+ }
3335
+ } catch (error) {
3336
+ iterationCompletionProof = hostSurfaceEvaluatorFailureProof(error);
3337
+ providerError = mergeIterationRetryFeedback(
3338
+ providerError,
3339
+ iterationCompletionProof.feedback
3340
+ );
3341
+ policyRecorder.emit({
3342
+ policy: "goal_gate",
3343
+ eventType: "evaluated",
3344
+ iteration: effectiveIteration,
3345
+ reason: "host completion evaluator error",
3346
+ details: {
3347
+ feedback: iterationCompletionProof.feedback
3348
+ }
3349
+ });
3350
+ }
3351
+ }
3352
+ const hostContractsBlockShortCircuit = iterationCompletionProof?.hasHostSurface === true && iterationCompletionProof.mayAcceptOutcome !== true;
3353
+ const hostContractsBackCompletion = !iterationCompletionProof?.hasHostSurface || iterationCompletionProof.mayAcceptOutcome === true;
3354
+ const allConfiguredGates = [...gatesToRun, ...dynamicGates];
3355
+ let normalizedGates;
3356
+ if (configuredGatePreset) {
3357
+ const presetResult = await resolveGatePreset({
3358
+ configuredPreset: configuredGatePreset,
3359
+ escalation: gateEscalation,
3360
+ changedFiles,
3361
+ repairAttempts: totalRepairAttemptsForEscalation,
3362
+ failedGateIds: failedGateIdsForEscalation
3363
+ });
3364
+ currentGatePreset = presetResult.preset;
3365
+ if (presetResult.escalationReason) {
3366
+ log(`Gate escalation: ${currentGatePreset} (${presetResult.escalationReason})`);
3367
+ }
3368
+ const presetFilteredGates = filterGatesByPreset(
3369
+ allConfiguredGates,
3370
+ currentGatePreset
3371
+ );
3372
+ normalizedGates = orderGatesByType(presetFilteredGates);
3373
+ if (normalizedGates.length !== allConfiguredGates.length) {
3374
+ log(
3375
+ `Gates filtered by preset '${currentGatePreset}': ${normalizedGates.length}/${allConfiguredGates.length}`
3376
+ );
3377
+ }
3378
+ } else {
3379
+ normalizedGates = orderGatesByType(allConfiguredGates);
3380
+ currentGatePreset = "full";
3381
+ }
3382
+ let finalGateResults;
3383
+ let iterRepairAttempted = false;
3384
+ let iterRepairSuccesses = 0;
3385
+ if (normalizedGates.length > 0 && !allowlistError && !simpleArtifactGoalSatisfied) {
3386
+ const gateNow = performance2.now();
3387
+ const elapsedLoopMsForGates = elapsedLoopMs();
3388
+ const elapsedIterationMsForGates = Math.max(0, gateNow - iterationStart);
3389
+ const hasIterationGateCeiling = typeof iterationTimeboxMs === "number" && iterationTimeboxMs > 0;
3390
+ const iterationGateRemainingMs = hasIterationGateCeiling ? Math.floor(iterationTimeboxMs - elapsedIterationMsForGates) : void 0;
3391
+ if (iterationGateRemainingMs !== void 0 && iterationGateRemainingMs <= 0) {
3392
+ providerError = providerError ?? "Autoloop timebox exhausted before quality gates";
3393
+ log(providerError);
3394
+ } else {
3395
+ const loopGateRemainingMs = Math.floor(timeboxMs - elapsedLoopMsForGates);
3396
+ if (loopGateRemainingMs <= 0) {
3397
+ providerError = providerError ?? "Autoloop timebox exhausted before quality gates";
3398
+ log(providerError);
3399
+ } else {
3400
+ const effectiveGateRemainingMs = Math.max(
3401
+ 1,
3402
+ iterationGateRemainingMs !== void 0 ? Math.min(iterationGateRemainingMs, loopGateRemainingMs) : loopGateRemainingMs
3403
+ );
3404
+ const gateDeadlineMs = Date.now() + effectiveGateRemainingMs;
3405
+ const gateStart = performance2.now();
3406
+ const gateRun = await runAllGates(normalizedGates, ctx.worktreePath, {
3407
+ baselineCwd: ctx.baselineWorktreePath,
3408
+ abortSignal: iterationAbortSignal,
3409
+ deadlineMs: gateDeadlineMs
3410
+ });
3411
+ timeBreakdown.gateMs = performance2.now() - gateStart;
3412
+ finalGateResults = gateRun.results;
3413
+ if (!gateRun.passed) {
3414
+ const failedMustPassGates = realFailedMustPassGates(gateRun.results);
3415
+ const failedGate = failedMustPassGates.find(
3416
+ (r) => shouldAttemptRepair(r.gate)
3417
+ );
3418
+ if (failedGate && shouldAttemptRepair(failedGate.gate)) {
3419
+ const holisticFailedGate = failedMustPassGates.length > 1 ? {
3420
+ ...failedGate,
3421
+ outputTail: failedMustPassGates.map(
3422
+ (r) => `Gate ${r.gate.id} (${r.gate.command}) failed with exit ${r.exitCode}:
3423
+ ${r.outputTail || "(no output)"}`
3424
+ ).join("\n\n")
3425
+ } : failedGate;
3426
+ const maxAttempts = getMaxRepairAttempts(failedGate.gate);
3427
+ const provider = resolveFlywheelStageProvider(ctx.stage, ctx.worktreePath);
3428
+ const { applyFlywheelModelDefaults } = await import("./flywheel-model-defaults-UHAKTZT3.js");
3429
+ const model = applyFlywheelModelDefaults(ctx.stage, provider);
3430
+ const recentFailingGates = [failedGate.gate.id];
3431
+ if (!failedGateIdsForEscalation.includes(failedGate.gate.id)) {
3432
+ failedGateIdsForEscalation.push(failedGate.gate.id);
3433
+ }
3434
+ const repairStart = performance2.now();
3435
+ for (let attemptNum = 1; attemptNum <= maxAttempts; attemptNum++) {
3436
+ const repairNow = performance2.now();
3437
+ const loopRepairRemainingMs = Math.floor(timeboxMs - elapsedLoopMs());
3438
+ const iterationRepairRemainingMs = typeof iterationTimeboxMs === "number" && iterationTimeboxMs > 0 ? Math.floor(
3439
+ iterationTimeboxMs - Math.max(0, repairNow - iterationStart)
3440
+ ) : void 0;
3441
+ const effectiveRepairRemainingMs = iterationRepairRemainingMs !== void 0 ? Math.min(iterationRepairRemainingMs, loopRepairRemainingMs) : loopRepairRemainingMs;
3442
+ if (effectiveRepairRemainingMs < MIN_GATE_REPAIR_TIMEOUT_MS) {
3443
+ log(
3444
+ `Skipping gate repair ${attemptNum}/${maxAttempts} for ${failedGate.gate.id}: insufficient remaining timebox (${Math.max(0, effectiveRepairRemainingMs)}ms < ${MIN_GATE_REPAIR_TIMEOUT_MS}ms)`
3445
+ );
3446
+ break;
3447
+ }
3448
+ const repairTimeoutMs = effectiveRepairRemainingMs;
3449
+ const repairDeadlineMs = Date.now() + repairTimeoutMs;
3450
+ if (!iterRepairAttempted) {
3451
+ iterRepairAttempted = true;
3452
+ totalRepairAttemptsForEscalation++;
3453
+ }
3454
+ log(
3455
+ `Attempting gate repair ${attemptNum}/${maxAttempts} for ${failedGate.gate.id}`
3456
+ );
3457
+ const repairResult = await attemptGateRepair({
3458
+ failedGate: failedGate.gate,
3459
+ gateResult: holisticFailedGate,
3460
+ allGates: normalizedGates,
3461
+ worktreePath: ctx.worktreePath,
3462
+ changedFiles,
3463
+ executor: ctx.executor,
3464
+ provider,
3465
+ model,
3466
+ attemptNumber: attemptNum,
3467
+ // Use the SAME raw allowlist the main iteration enforces
3468
+ // against (ctx.stage.allowlist). collectDeclaredStageTargets
3469
+ // returns a different set — it normalizes/filters entries
3470
+ // (drops node_modules/absolute/.. paths) and folds in
3471
+ // requiredFiles, specHarness targets, and prompt-declared
3472
+ // targets — so the repair allowlist diverged from the main
3473
+ // check and could flag files the iteration had permitted.
3474
+ allowlist: ctx.stage.allowlist,
3475
+ allowlistMode: allowlistMode2,
3476
+ baselineCwd: ctx.baselineWorktreePath,
3477
+ abortSignal: iterationAbortSignal,
3478
+ deadlineMs: repairDeadlineMs,
3479
+ timeoutMs: repairTimeoutMs,
3480
+ keepWorkOnProviderFailure: ctx.keepWorkOnProviderFailure,
3481
+ providerSwitch
3482
+ });
3483
+ providerCost += repairResult.cost;
3484
+ providerCostUnavailableReason ??= repairResult.costUnavailableReason;
3485
+ const repairTokenAccounting = resolveTokenAccounting({
3486
+ inputTokens: repairResult.inputTokens ?? 0,
3487
+ outputTokens: repairResult.outputTokens ?? 0,
3488
+ totalCost: repairResult.cost,
3489
+ tokenUsageSource: repairResult.tokenUsageSource
3490
+ });
3491
+ providerTokens += repairTokenAccounting.totalTokens;
3492
+ totalTokens += repairTokenAccounting.totalTokens;
3493
+ for (const evidence of repairResult.reviewRequiredEvidence ?? []) {
3494
+ repairReviewWarnings.add(formatRepairSuppressionWarning(evidence));
3495
+ }
3496
+ if (repairResult.allowlistState) {
3497
+ changedFiles = repairResult.allowlistState.changedFiles;
3498
+ allowlistViolations = repairResult.allowlistState.violations;
3499
+ allowlistDrift = allowlistViolations.length > 0;
3500
+ if (repairResult.allowlistState.autoRevertedFiles.length > 0) {
3501
+ allowlistRevertedFiles = repairResult.allowlistState.autoRevertedFiles;
3502
+ }
3503
+ if (repairResult.allowlistState.error) {
3504
+ allowlistError = repairResult.allowlistState.error;
3505
+ providerError = providerError ?? allowlistError;
3506
+ }
3507
+ }
3508
+ if (repairResult.providerFailed && ctx.keepWorkOnProviderFailure) {
3509
+ providerFailure.stop = `Repair provider ${providerSwitch?.current.provider ?? provider} failed: ${repairResult.providerError ?? "no error output"}`;
3510
+ break;
3511
+ }
3512
+ if (repairResult.abortFurtherRepairs) {
3513
+ finalGateResults = repairResult.gateResults;
3514
+ break;
3515
+ }
3516
+ if (repairResult.success) {
3517
+ iterRepairSuccesses++;
3518
+ finalGateResults = repairResult.gateResults;
3519
+ log(`Gate repair successful after ${attemptNum} attempt(s)`);
3520
+ break;
3521
+ }
3522
+ const nowFailingGate = repairResult.gateResults?.find(
3523
+ (r) => !r.passed && r.gate.mustPass
3524
+ );
3525
+ if (nowFailingGate) {
3526
+ recentFailingGates.push(nowFailingGate.gate.id);
3527
+ if (recentFailingGates.length > 4) recentFailingGates.shift();
3528
+ const uniqueRecent = new Set(recentFailingGates);
3529
+ const isFlipPattern = recentFailingGates.length >= 3 && recentFailingGates[0] === recentFailingGates[2] && recentFailingGates[0] !== recentFailingGates[1];
3530
+ const isTooManyDistinct = uniqueRecent.size >= 3;
3531
+ if (isFlipPattern || isTooManyDistinct) {
3532
+ const pattern = [...uniqueRecent].join("<->");
3533
+ const reason = isFlipPattern ? "flip pattern detected" : "too many distinct failing gates";
3534
+ log(
3535
+ `Gate repair thrashing: ${pattern} (${reason}). Stopping repairs this iteration.`
3536
+ );
3537
+ providerError = providerError ?? `Gate repair thrashing (${pattern}): fixes for one gate break another. Next iteration should address all failing gates holistically.`;
3538
+ finalGateResults = repairResult.gateResults;
3539
+ break;
3540
+ }
3541
+ }
3542
+ if (attemptNum < maxAttempts) {
3543
+ log(`Gate repair attempt ${attemptNum} failed, retrying...`);
3544
+ } else {
3545
+ log(`Gate repair exhausted all ${maxAttempts} attempts`);
3546
+ finalGateResults = repairResult.gateResults;
3547
+ }
3548
+ }
3549
+ timeBreakdown.repairMs = performance2.now() - repairStart;
3550
+ }
3551
+ }
3552
+ }
3553
+ }
3554
+ }
3555
+ const qualityGate = finalGateResults ? {
3556
+ passed: finalGateResults.every(
3557
+ (r) => r.passed || !r.gate.mustPass || r.isRegression === false
3558
+ ),
3559
+ error: (() => {
3560
+ const failedGate = finalGateResults.find(
3561
+ (r) => !r.passed && r.gate.mustPass && r.isRegression !== false
3562
+ );
3563
+ return failedGate ? `Verify command failed (exit ${failedGate.exitCode}): ${failedGate.gate.command}` : void 0;
3564
+ })(),
3565
+ commandResults: finalGateResults.map((r) => ({
3566
+ id: r.gate.id,
3567
+ command: r.gate.command,
3568
+ exitCode: r.exitCode,
3569
+ expectedExitCode: r.gate.expectedExitCode ?? 0,
3570
+ passed: r.passed,
3571
+ durationMs: r.durationMs,
3572
+ outputTail: r.outputTail,
3573
+ baselinePassed: r.baselinePassed,
3574
+ isRegression: r.isRegression,
3575
+ mustPass: r.gate.mustPass ?? true,
3576
+ unavailableReason: gateRunUnavailableSignal(r)?.unavailableReason
3577
+ }))
3578
+ } : void 0;
3579
+ if (qualityGate) {
3580
+ policyRecorder.emit({
3581
+ policy: "quality_gate",
3582
+ eventType: qualityGate.passed ? "evaluated" : "blocked",
3583
+ iteration: effectiveIteration,
3584
+ reason: qualityGate.passed ? "Quality gates passed" : qualityGate.error,
3585
+ details: {
3586
+ gateCount: qualityGate.commandResults.length,
3587
+ failedCommandCount: qualityGate.commandResults.filter((r) => !r.passed).length
3588
+ }
3589
+ });
3590
+ }
3591
+ if (qualityGate?.passed === true && finalGateResults !== void 0 && finalGateResults.length > 0 && !providerHardExitFailure && isVerificationUnavailableError(providerError) && !hostContractsBlockShortCircuit && hostContractsBackCompletion) {
3592
+ providerVerificationUnavailableRecovered = false;
3593
+ }
3594
+ const testFilesInIteration = config.requiresTests === true ? await detectTestFiles(changedFiles, config.testFilePatterns) : [];
3595
+ const testsCreated = testFilesInIteration.length > 0;
3596
+ const totalTrackedMs = timeBreakdown.providerMs + timeBreakdown.gateMs + timeBreakdown.repairMs + (timeBreakdown.testMs ?? 0) + (timeBreakdown.buildMs ?? 0);
3597
+ const iterationTotalMs = performance2.now() - iterationStart;
3598
+ timeBreakdown.overheadMs = Math.max(0, iterationTotalMs - totalTrackedMs);
3599
+ const iterationFingerprint = await captureIterationFingerprint(iteration);
3600
+ const progressMetrics = computeIterationProgressMetrics({
3601
+ changedFiles,
3602
+ qualityGate,
3603
+ previousIteration: iterationHistory.at(-1),
3604
+ iterationFingerprint
3605
+ });
3606
+ let shouldTerminateNoOp = false;
3607
+ let noOpTerminationMessage = "";
3608
+ let shouldTerminateIterationTimebox = false;
3609
+ let iterationTimeboxMessage = "";
3610
+ let iterationTerminationSignal;
3611
+ if (providerResult.success && requireEarlyDiff && progressMetrics.wasNoOp && effectiveIteration > 1) {
3612
+ consecutiveNoOps++;
3613
+ lastIterationDurationMs = iterationTotalMs;
3614
+ providerError = providerError ?? "No-op iteration: no file changes detected";
3615
+ log(`No-op iteration detected (consecutive: ${consecutiveNoOps})`);
3616
+ if (noOpIntervention) {
3617
+ const maxConsecutive = noOpIntervention.maxConsecutiveNoOps ?? 2;
3618
+ const triggerDelay = noOpIntervention.triggerDelayMs ?? 45e3;
3619
+ if (consecutiveNoOps >= maxConsecutive) {
3620
+ shouldTerminateNoOp = true;
3621
+ iterationTerminationSignal = "no_op_intervention_exceeded";
3622
+ noOpTerminationMessage = `No-op intervention exceeded: ${consecutiveNoOps} consecutive no-ops`;
3623
+ providerError = noOpTerminationMessage;
3624
+ log(`Max consecutive no-ops reached (${consecutiveNoOps}/${maxConsecutive})`);
3625
+ } else if (iterationTotalMs >= triggerDelay) {
3626
+ interventionActive = true;
3627
+ log(
3628
+ `Intervention triggered: iteration took ${iterationTotalMs}ms >= ${triggerDelay}ms`
3629
+ );
3630
+ }
3631
+ }
3632
+ } else if (providerResult.success && !progressMetrics.wasNoOp) {
3633
+ consecutiveNoOps = 0;
3634
+ interventionActive = false;
3635
+ }
3636
+ if (iterationTimeboxMs && iterationTotalMs > iterationTimeboxMs) {
3637
+ iterationTimeboxMessage = `Iteration exceeded per-iteration timebox (${iterationTotalMs.toFixed(0)}ms > ${iterationTimeboxMs}ms)`;
3638
+ log(iterationTimeboxMessage);
3639
+ shouldTerminateIterationTimebox = true;
3640
+ iterationTerminationSignal ??= "timebox_exceeded";
3641
+ providerError = providerError ?? iterationTimeboxMessage;
3642
+ }
3643
+ if (simpleArtifactGoalSatisfied && !iterationTerminationSignal) {
3644
+ iterationTerminationSignal = "goal_satisfied";
3645
+ }
3646
+ let shouldTerminateVerificationUnavailable = false;
3647
+ let verificationUnavailableMessage = "";
3648
+ let unverifiedDisposition;
3649
+ const unavailableGateSignal = detectUnavailableGateVerification(
3650
+ finalGateResults,
3651
+ normalizedGates.length
3652
+ );
3653
+ if (unavailableGateSignal && providerResult.success && !allowlistError && !providerError && !providerVerificationUnavailableRecovered) {
3654
+ unverifiedDisposition = unavailableGateDisposition(
3655
+ unavailableGateSignal,
3656
+ changedFiles
3657
+ );
3658
+ verificationUnavailableMessage = `Verification unavailable: ${unavailableGateSignal.message}`;
3659
+ shouldTerminateVerificationUnavailable = true;
3660
+ iterationTerminationSignal = "verification_unavailable";
3661
+ log(verificationUnavailableMessage);
3662
+ policyRecorder.emit({
3663
+ policy: "goal_gate",
3664
+ eventType: "terminated",
3665
+ iteration: effectiveIteration,
3666
+ reason: "verification_unavailable",
3667
+ details: { changedFiles: changedFiles.length, deliveredUnverified: true }
3668
+ });
3669
+ } else if (providerResult.success && !allowlistError && isVerificationUnavailableError(providerError) && (!changesHaveVerifiableCodeSurface(changedFiles) || namesVerificationUnavailable(providerError))) {
3670
+ shouldTerminateVerificationUnavailable = true;
3671
+ verificationUnavailableMessage = unavailableGateSignal ? `Verification unavailable: ${unavailableGateSignal.message} Stopping instead of asking the provider to install or repair target tooling; this is not reported as success.` : "Verification unavailable: post-execution tests did not run and the changed files expose no verifiable code surface (read-only / docs-only task). Stopping instead of re-iterating; this is not reported as success.";
3672
+ providerError = verificationUnavailableMessage;
3673
+ iterationTerminationSignal = "verification_unavailable";
3674
+ log(verificationUnavailableMessage);
3675
+ policyRecorder.emit({
3676
+ policy: "goal_gate",
3677
+ eventType: "terminated",
3678
+ iteration: effectiveIteration,
3679
+ reason: "verification_unavailable",
3680
+ details: { changedFiles: changedFiles.length }
3681
+ });
3682
+ }
3683
+ const record = {
3684
+ iteration: effectiveIteration,
3685
+ success: providerResult.success && !providerVerificationUnavailableRecovered && !allowlistError && !providerError,
3686
+ providerExecutionStatus,
3687
+ providerOutputStatus,
3688
+ providerStatusDetail,
3689
+ changedFiles,
3690
+ providerOutputText,
3691
+ cost: providerCost,
3692
+ costUnavailableReason: providerCostUnavailableReason,
3693
+ durationMs: iterationTotalMs,
3694
+ timeBreakdown,
3695
+ progressMetrics,
3696
+ progressScore: void 0,
3697
+ error: providerError,
3698
+ terminationSignal: iterationTerminationSignal ?? (allowlistError ? "allowlist_violation" : void 0),
3699
+ qualityGatePassed: qualityGate?.passed,
3700
+ qualityGateError: qualityGate?.error,
3701
+ qualityGateCommandResults: qualityGate?.commandResults,
3702
+ ...unverifiedDisposition ? { verification: unverifiedDisposition } : {},
3703
+ allowlistViolations: allowlistDrift ? allowlistViolations : void 0,
3704
+ allowlistRevertedFiles: allowlistRevertedFiles?.length ? allowlistRevertedFiles : void 0,
3705
+ allowlistDrift,
3706
+ repairAttempted: iterRepairAttempted,
3707
+ repairSuccesses: iterRepairSuccesses,
3708
+ testsRequired: config.requiresTests === true,
3709
+ testsCreated,
3710
+ // Priority 3: Short-circuit detection
3711
+ aiValidationShortCircuited: false,
3712
+ // Set below if conditions are met
3713
+ completionEvidence: simpleArtifactGoalSatisfied ? {
3714
+ durable: true,
3715
+ source: "simple-artifact",
3716
+ detail: simpleArtifactGoal?.path ?? "simple artifact",
3717
+ ...iterationCompletionProof?.evidence?.providerAdvisory ? {
3718
+ providerAdvisory: iterationCompletionProof.evidence.providerAdvisory
3719
+ } : {},
3720
+ ...iterationCompletionProof?.evidence?.attemptScopedEdits ? {
3721
+ attemptScopedEdits: iterationCompletionProof.evidence.attemptScopedEdits
3722
+ } : {},
3723
+ ...iterationCompletionProof?.evidence?.contractResults ? {
3724
+ contractResults: iterationCompletionProof.evidence.contractResults
3725
+ } : {}
3726
+ } : toAutoloopCompletionEvidence(
3727
+ iterationCompletionProof ?? {
3728
+ contractsSatisfied: false,
3729
+ requireChangesSatisfied: "n/a",
3730
+ alreadySatisfied: false,
3731
+ mayAcceptOutcome: false,
3732
+ hasHostSurface: false,
3733
+ reasons: []
3734
+ }
3735
+ ),
3736
+ policyEvents: policyRecorder.events.filter(
3737
+ (event) => event.iteration === effectiveIteration
3738
+ )
3739
+ };
3740
+ const cumulativeAutoloopCost = iterationHistory.reduce((sum, item) => sum + item.cost, 0) + record.cost;
3741
+ const incrementalTracker = ctx.budgetTracker;
3742
+ incrementalTracker.trackIncrementalStageCost?.(ctx.stage.id, cumulativeAutoloopCost);
3743
+ let completionSignalDetected = false;
3744
+ if (completionSignal && providerOutputText) {
3745
+ completionSignalDetected = matchesCompletionSignal(
3746
+ providerOutputText,
3747
+ completionSignal
3748
+ );
3749
+ }
3750
+ const canShortCircuitAiValidation = qualityGate?.passed === true && record.success && !interventionActive && !hostContractsBlockShortCircuit && hostContractsBackCompletion && (completionSignalDetected || validationMode === "deterministic" || iterationCompletionProof?.alreadySatisfied === true || record.completionEvidence?.durable === true);
3751
+ if (canShortCircuitAiValidation) {
3752
+ record.aiValidationShortCircuited = true;
3753
+ aiValidationShortCircuited = true;
3754
+ const reason = iterationCompletionProof?.alreadySatisfied ? "host completion surface already satisfied" : completionSignalDetected ? "deterministic gates passed + completion signal detected" : "deterministic validation mode + gates passed";
3755
+ policyRecorder.emit({
3756
+ policy: "goal_gate",
3757
+ eventType: "terminated",
3758
+ iteration: effectiveIteration,
3759
+ reason,
3760
+ details: {
3761
+ completionSignalDetected,
3762
+ validationMode,
3763
+ hostContractsSatisfied: iterationCompletionProof?.contractsSatisfied
3764
+ }
3765
+ });
3766
+ record.policyEvents = policyRecorder.events.filter(
3767
+ (event) => event.iteration === effectiveIteration
3768
+ );
3769
+ log(`Short-circuiting AI validation (${reason})`);
3770
+ }
3771
+ const hasPassingHardQualityGate = (record.qualityGateCommandResults ?? []).some(
3772
+ (gate) => gate.mustPass !== false && gate.passed === true
3773
+ );
3774
+ if (validationMode !== "deterministic" && config.completionOnQualityGatePass === true && completionSignalXmlTag && record.success && record.qualityGatePassed === true && hasPassingHardQualityGate && !hostContractsBlockShortCircuit && hostContractsBackCompletion) {
3775
+ const cumulativeChangedFiles = [
3776
+ ...iterationHistory.flatMap((item) => item.changedFiles),
3777
+ ...record.changedFiles
3778
+ ];
3779
+ const requiredTestsSatisfied = config.requiresTests !== true || (await detectTestFiles(
3780
+ cumulativeChangedFiles,
3781
+ config.testFilePatterns,
3782
+ ctx.worktreePath
3783
+ )).length > 0;
3784
+ const currentTestGateResults = (record.qualityGateCommandResults ?? []).filter(
3785
+ (gate) => gate.mustPass !== false && describeVerificationCommand(gate.command).kind === "test"
3786
+ );
3787
+ const runtimeEvidenceSatisfied = verifyRuntimeEvidence({
3788
+ config: config.runtimeEvidence,
3789
+ providerOutputText: record.providerOutputText,
3790
+ worktreePath: ctx.worktreePath,
3791
+ basePrompt: ctx.stage.prompt ?? "",
3792
+ changedFiles: record.changedFiles,
3793
+ testVerification: {
3794
+ status: currentTestGateResults.length === 0 ? "unavailable" : currentTestGateResults.every((gate) => gate.passed === true) ? "passed" : "failed",
3795
+ checksRun: currentTestGateResults.length
3796
+ }
3797
+ }).passed;
3798
+ if (requiredTestsSatisfied && runtimeEvidenceSatisfied) {
3799
+ record.completionEvidence ??= {
3800
+ durable: true,
3801
+ source: "external",
3802
+ detail: "Flywheel quality gates passed"
3803
+ };
3804
+ record.providerOutputText = `${record.providerOutputText ?? ""}
3805
+ ${completionSignalXmlTag}`.trim().concat("\n");
3806
+ }
3807
+ }
3808
+ const hostCustomGateResults = mapQualityGateCommandResultsToCustomGateResults(
3809
+ record.qualityGateCommandResults,
3810
+ createDurationMs
3811
+ );
3812
+ if (hostCustomGateResults.length > 0) {
3813
+ Object.assign(record, { customGateResults: hostCustomGateResults });
3814
+ }
3815
+ record.iterationFingerprint = iterationFingerprint;
3816
+ iterationHistory.push(record);
3817
+ if (ctx.artifactStore) {
3818
+ await saveBestEffort(
3819
+ ctx.artifactStore,
3820
+ ctx.stage.id,
3821
+ `iteration-${effectiveIteration}`,
3822
+ record
3823
+ );
3824
+ }
3825
+ const driftMsg = record.allowlistDrift ? ` [drift: ${record.allowlistViolations?.length ?? 0}]` : "";
3826
+ log(
3827
+ `Iteration ${effectiveIteration} ${record.success ? "ok" : "failed"} (${record.changedFiles.length} files, ${formatAutoloopIterationCost(record)})${driftMsg}`
3828
+ );
3829
+ if (shouldTerminateNoOp) {
3830
+ try {
3831
+ loopController.terminate("no_op_loop");
3832
+ } catch {
3833
+ }
3834
+ throw new AutoloopTerminationError(
3835
+ noOpTerminationMessage,
3836
+ "no_op_intervention_exceeded",
3837
+ record.iterationFingerprint
3838
+ );
3839
+ }
3840
+ if (providerFailure.stop) {
3841
+ throw new AutoloopTerminationError(providerFailure.stop, "error");
3842
+ }
3843
+ if (shouldTerminateVerificationUnavailable && record.verification) {
3844
+ try {
3845
+ loopController.terminate("verification_unavailable");
3846
+ } catch {
3847
+ }
3848
+ return record;
3849
+ }
3850
+ if (shouldTerminateVerificationUnavailable) {
3851
+ throw new AutoloopTerminationError(
3852
+ verificationUnavailableMessage,
3853
+ "verification_unavailable"
3854
+ );
3855
+ }
3856
+ if (shouldTerminateIterationTimebox) {
3857
+ try {
3858
+ loopController.terminate("timebox_exceeded");
3859
+ } catch {
3860
+ }
3861
+ throw new Error(iterationTimeboxMessage);
3862
+ }
3863
+ if (simpleArtifactGoalSatisfied) {
3864
+ try {
3865
+ loopController.terminate("deterministic_complete");
3866
+ } catch {
3867
+ }
3868
+ }
3869
+ return record;
3870
+ });
3871
+ await Promise.allSettled(pendingIterationRecords);
3872
+ if (hasTerminalDurableOutcome || remainingIterations === 0) {
3873
+ const reconstructed = reconstructExhaustedAutoloopOutcome({
3874
+ priorOutcome: durableResumeState?.priorOutcome,
3875
+ finalProgressScore: durableResumeState?.finalProgressScore
3876
+ });
3877
+ terminationReason = reconstructed.terminationReason;
3878
+ loopSuccess = reconstructed.loopSuccess;
3879
+ finalProgressScore = reconstructed.finalProgressScore;
3880
+ policyRecorder.emit({
3881
+ policy: policyForTerminationReason(terminationReason),
3882
+ eventType: reconstructed.loopSuccess ? "evaluated" : "terminated",
3883
+ reason: terminationReason,
3884
+ details: {
3885
+ finalProgressScore,
3886
+ iterations: 0,
3887
+ reconstructedFromDurableEvidence: true,
3888
+ evidenceComplete: reconstructed.evidenceComplete,
3889
+ source: durableResumeState?.source
3890
+ }
3891
+ });
3892
+ } else {
3893
+ finalProgressScore = finalIteration !== 0 ? finalIteration.progress.score : 0;
3894
+ const loopIterations = loopController.getIterationHistory();
3895
+ const lastIterationRecord = iterationHistory.at(-1);
3896
+ const testGateResults = (lastIterationRecord?.qualityGateCommandResults ?? []).filter(
3897
+ (gate) => gate.mustPass !== false && describeVerificationCommand(gate.command).kind === "test"
3898
+ );
3899
+ runtimeEvidenceCheck = verifyRuntimeEvidence({
3900
+ config: config.runtimeEvidence,
3901
+ providerOutputText: iterationHistory.length > 0 ? iterationHistory.at(-1)?.providerOutputText : void 0,
3902
+ worktreePath: ctx.worktreePath,
3903
+ basePrompt: ctx.stage.prompt ?? "",
3904
+ changedFiles: lastIterationRecord?.changedFiles,
3905
+ testVerification: {
3906
+ status: testGateResults.length === 0 ? "unavailable" : testGateResults.every((gate) => gate.passed === true) ? "passed" : "failed",
3907
+ checksRun: testGateResults.length
3908
+ }
3909
+ });
3910
+ const controllerState = loopController.getState();
3911
+ const acceptanceCriteriaEvidenceConfigured = config.runtimeEvidence?.requireAcceptanceCriteria === true;
3912
+ terminationReason = await inferTerminationReason({
3913
+ maxIterations: remainingIterations,
3914
+ timeboxMs,
3915
+ iterationHistory,
3916
+ loopIterations,
3917
+ minProgress,
3918
+ gatesEnabled: gatesEnabled || dynamicGates.length > 0,
3919
+ requiresTests: config.requiresTests,
3920
+ testFilePatterns: config.testFilePatterns,
3921
+ runtimeEvidenceCheck,
3922
+ runtimeEvidenceConfigured: acceptanceCriteriaEvidenceConfigured,
3923
+ worktreePath: ctx.worktreePath,
3924
+ controllerTerminationReason: controllerState.status === "terminated" ? controllerState.reason : controllerState.status === "completed" ? controllerState.completionReason : void 0
3925
+ });
3926
+ policyRecorder.emit({
3927
+ policy: policyForTerminationReason(terminationReason),
3928
+ eventType: terminationReason === "success" ? "evaluated" : "terminated",
3929
+ reason: terminationReason,
3930
+ details: {
3931
+ finalProgressScore,
3932
+ iterations: iterationHistory.length,
3933
+ loopStatus: controllerState.status
3934
+ }
3935
+ });
3936
+ const requiresPassingGates = terminationReason === "goal_satisfied" ? false : gatesEnabled || dynamicGates.length > 0;
3937
+ const finalGatePassed = requiresPassingGates ? iterationHistory.at(-1)?.qualityGatePassed === true : true;
3938
+ const controllerUnsafeCompletion = finalIteration !== 0 && finalIteration.completionSummary?.unsafeCompletion === true;
3939
+ const completionSummarySupportsSuccess = finalIteration !== 0 && finalIteration.completionSummary !== void 0 && completionStatusAllowsSuccess(finalIteration.completionSummary.status) && finalIteration.completionSummary.hasSubstantiveEvidence === true && finalIteration.completionSummary.blockingIssues.length === 0;
3940
+ const controllerReportedSuccess = controllerState.status === "terminated" && isSuccessLikeControllerReason(controllerState.reason);
3941
+ const lastIterRecord = iterationHistory.at(-1);
3942
+ const lastIterationOk = lastIterRecord?.success !== false;
3943
+ const hardFailure = (
3944
+ // Parity with the controller start() consumer: unsafeCompletion forces
3945
+ // non-success. Shared predicate keeps the two surfaces fail-closed in
3946
+ // lockstep (see autoloop-success.ts:unsafeCompletionAllowsSuccess).
3947
+ !unsafeCompletionAllowsSuccess(controllerUnsafeCompletion) || controllerReportedSuccess && !completionSummarySupportsSuccess || terminationReason === "resource_exhaustion" || terminationReason === "error" || terminationReason === "timebox_exceeded" || terminationReason === "allowlist_violation" || terminationReason === "quality_gate_failure" || terminationReason === "baseline_blocked" || terminationReason === "tests_required_not_met" || terminationReason === "evidence_unsatisfied" || terminationReason === "progress_stagnation" || terminationReason === "no_op_intervention_exceeded"
3948
+ );
3949
+ if (isDeliveredUnverified(terminationReason, lastIterRecord)) {
3950
+ loopSuccess = true;
3951
+ } else if (!finalGatePassed || !lastIterationOk || hardFailure) {
3952
+ loopSuccess = false;
3953
+ } else if (terminationReason === "max_iterations_reached") {
3954
+ const maxIterationsSuccessThreshold = ctx.stage.validation?.enabled === false ? minProgress : 0.5;
3955
+ loopSuccess = finalProgressScore >= maxIterationsSuccessThreshold && completionSummarySupportsSuccess;
3956
+ const lastControllerIteration = loopIterations.at(-1);
3957
+ if (lastControllerIteration?.requirements?.enabled && !lastControllerIteration.requirements.passed) {
3958
+ loopSuccess = false;
3959
+ }
3960
+ } else if (terminationReason === "success" || terminationReason === "goal_satisfied" || terminationReason === "heuristic_complete") {
3961
+ loopSuccess = true;
3962
+ } else {
3963
+ loopSuccess = false;
3964
+ }
3965
+ }
3966
+ } catch (error) {
3967
+ terminationReason = inferFailureTerminationReason(error, iterationHistory);
3968
+ loopSuccess = false;
3969
+ policyRecorder.emit({
3970
+ policy: policyForTerminationReason(terminationReason),
3971
+ eventType: "terminated",
3972
+ reason: terminationReason,
3973
+ details: {
3974
+ error: error instanceof Error ? error.message : String(error),
3975
+ iterations: iterationHistory.length
3976
+ }
3977
+ });
3978
+ if (ctx.artifactStore) {
3979
+ const failureDiagnostic = createFailureDiagnostic({
3980
+ error,
3981
+ iterationHistory,
3982
+ stageId: ctx.stage.id,
3983
+ terminationReason,
3984
+ allowlist: ctx.stage.allowlist
3985
+ });
3986
+ failureArtifactPersisted = await saveBestEffort(
3987
+ ctx.artifactStore,
3988
+ ctx.stage.id,
3989
+ "autoloop-failure",
3990
+ failureDiagnostic
3991
+ );
3992
+ }
3993
+ }
3994
+ const durationMs = performance2.now() - startTime;
3995
+ if (terminationReason === "timebox_exceeded") {
3996
+ emitBoundaryTriggered({
3997
+ trigger: "ceiling",
3998
+ workloadClass: "bounded-loop",
3999
+ effectiveCeilingMs: timeboxMs,
4000
+ elapsedMs: Math.round(durationMs),
4001
+ source: "autoloop",
4002
+ taskId: ctx.stage.id,
4003
+ metadata: {
4004
+ stageId: ctx.stage.id
4005
+ }
4006
+ });
4007
+ } else if (terminationReason === "progress_stagnation" || terminationReason === "no_op_intervention_exceeded") {
4008
+ emitBoundaryTriggered({
4009
+ trigger: "progress",
4010
+ workloadClass: "bounded-loop",
4011
+ effectiveCeilingMs: timeboxMs,
4012
+ elapsedMs: Math.round(durationMs),
4013
+ source: "autoloop",
4014
+ taskId: ctx.stage.id,
4015
+ stagnantChecks: terminationReason === "progress_stagnation" ? resolveExecutionBoundary("bounded-loop").progress?.maxStagnantChecks : void 0,
4016
+ metadata: {
4017
+ stageId: ctx.stage.id,
4018
+ terminationReason
4019
+ }
4020
+ });
4021
+ }
4022
+ const totalCost = hasTerminalDurableOutcome ? durableResumeState?.priorOutcome?.cost ?? 0 : iterationHistory.reduce((sum, r) => sum + r.cost, 0);
4023
+ const totalIterationsOverall = previousIterationCount + iterationHistory.length;
4024
+ if (terminationReason === "tests_required_not_met" && iterationHistory.length > 0) {
4025
+ const lastIter2 = iterationHistory[iterationHistory.length - 1];
4026
+ if (lastIter2) {
4027
+ const displayPatterns = selectDisplayTestFilePatterns(
4028
+ promptLanguage,
4029
+ config.testFilePatterns
4030
+ );
4031
+ const expectation = displayPatterns && displayPatterns.length > 0 ? `Expected patterns: ${displayPatterns.join(", ")}` : "Follow the existing test conventions of the target project.";
4032
+ lastIter2.error = lastIter2.error ?? `Test files required but none created. ${expectation}`;
4033
+ }
4034
+ }
4035
+ if (terminationReason === "evidence_unsatisfied" && iterationHistory.length > 0) {
4036
+ const lastIter2 = iterationHistory[iterationHistory.length - 1];
4037
+ if (lastIter2 && runtimeEvidenceCheck && runtimeEvidenceCheck.blockingIssues.length > 0) {
4038
+ const issueList = runtimeEvidenceCheck.blockingIssues.join("; ");
4039
+ lastIter2.error = lastIter2.error ?? `Runtime evidence requirements not satisfied: ${issueList}. Completion blocked until evidence is verified.`;
4040
+ }
4041
+ }
4042
+ const aggregateTimeMetrics = computeAggregateTimeMetrics(iterationHistory);
4043
+ const costUnavailableReason = firstCostUnavailableReason(iterationHistory);
4044
+ if (ctx.verbose && aggregateTimeMetrics) {
4045
+ log("Aggregate time breakdown:");
4046
+ log(
4047
+ ` Provider (model): ${aggregateTimeMetrics.totalProviderMs.toFixed(0)}ms (${aggregateTimeMetrics.providerTimePct.toFixed(1)}%)`
4048
+ );
4049
+ log(` Gates: ${aggregateTimeMetrics.totalGateMs.toFixed(0)}ms`);
4050
+ log(` Repair: ${aggregateTimeMetrics.totalRepairMs.toFixed(0)}ms`);
4051
+ log(` Deterministic work: ${aggregateTimeMetrics.deterministicTimePct.toFixed(1)}%`);
4052
+ log(
4053
+ ` No-op iterations: ${aggregateTimeMetrics.noOpIterationCount}/${iterationHistory.length}`
4054
+ );
4055
+ log(` Avg iteration: ${aggregateTimeMetrics.avgIterationMs.toFixed(0)}ms`);
4056
+ }
4057
+ if (ctx.artifactStore) {
4058
+ iterationHistoryArtifactPersisted = await saveBestEffort(
4059
+ ctx.artifactStore,
4060
+ ctx.stage.id,
4061
+ "iteration-history",
4062
+ iterationHistory
4063
+ );
4064
+ resultArtifactPersisted = await saveBestEffort(
4065
+ ctx.artifactStore,
4066
+ ctx.stage.id,
4067
+ "autoloop-result",
4068
+ {
4069
+ totalIterations: totalIterationsOverall,
4070
+ terminationReason,
4071
+ finalProgressScore,
4072
+ loopSuccess,
4073
+ totalDurationMs: durationMs,
4074
+ totalCost,
4075
+ costUnavailableReason,
4076
+ totalTokens,
4077
+ aggregateTimeMetrics,
4078
+ policyEvents: policyRecorder.events
4079
+ }
4080
+ );
4081
+ if (!loopSuccess || iterationHistory.some((r) => r.allowlistDrift)) {
4082
+ const failureDiagnostic = createFailureDiagnostic({
4083
+ error: new Error(`Autoloop terminated: ${terminationReason}`),
4084
+ iterationHistory,
4085
+ stageId: ctx.stage.id,
4086
+ terminationReason,
4087
+ allowlist: ctx.stage.allowlist
4088
+ });
4089
+ await saveBestEffort(
4090
+ ctx.artifactStore,
4091
+ ctx.stage.id,
4092
+ "autoloop-diagnostics",
4093
+ failureDiagnostic
4094
+ );
4095
+ }
4096
+ }
4097
+ const allowlistMode = ctx.stage.allowlistMode ?? "warn";
4098
+ const gatesForOutcome = [...gatesToRun, ...dynamicGates];
4099
+ let finalGateResultsForOutcome;
4100
+ const lastIter = iterationHistory.at(-1);
4101
+ if (lastIter?.qualityGateCommandResults && gatesForOutcome.length > 0) {
4102
+ finalGateResultsForOutcome = lastIter.qualityGateCommandResults.map((cmdResult, idx) => {
4103
+ const gate = (cmdResult.id ? gatesForOutcome.find((g) => g.id === cmdResult.id) : void 0) ?? gatesForOutcome[idx] ?? {
4104
+ id: `gate-${idx + 1}`,
4105
+ command: cmdResult.command,
4106
+ deterministic: true,
4107
+ severity: cmdResult.mustPass === false ? "warn" : "error",
4108
+ // Preserve persisted authority; default hard when field is absent (legacy).
4109
+ mustPass: cmdResult.mustPass ?? true
4110
+ };
4111
+ const expectedExitCode = gate.expectedExitCode ?? cmdResult.expectedExitCode ?? 0;
4112
+ const passed = typeof cmdResult.passed === "boolean" ? cmdResult.passed : cmdResult.exitCode === expectedExitCode;
4113
+ return {
4114
+ gate,
4115
+ passed,
4116
+ exitCode: cmdResult.exitCode,
4117
+ durationMs: cmdResult.durationMs,
4118
+ outputTail: cmdResult.outputTail,
4119
+ baselinePassed: cmdResult.baselinePassed,
4120
+ isRegression: cmdResult.isRegression
4121
+ };
4122
+ });
4123
+ }
4124
+ const totalRepairAttempted = iterationHistory.some((r) => r.repairAttempted === true);
4125
+ const totalRepairSuccesses = iterationHistory.reduce(
4126
+ (sum, r) => sum + (r.repairSuccesses ?? 0),
4127
+ 0
4128
+ );
4129
+ const outcomeMetadata = durableResumeState?.priorOutcome?.outcomeMetadata ?? computeAutoloopOutcome({
4130
+ finalGateResults: finalGateResultsForOutcome,
4131
+ iterationHistory,
4132
+ terminationReason,
4133
+ allowlistMode,
4134
+ repairAttempted: totalRepairAttempted,
4135
+ repairSuccesses: totalRepairSuccesses
4136
+ });
4137
+ const noOpCount = iterationHistory.filter((r) => r.progressMetrics?.wasNoOp).length;
4138
+ const finalIterationCompletionEvidence = iterationHistory.slice().reverse().find((item) => item.completionEvidence !== void 0)?.completionEvidence;
4139
+ result = {
4140
+ stageId: ctx.stage.id,
4141
+ stageType: "autoloop",
4142
+ validationPassed: loopSuccess,
4143
+ cost: totalCost,
4144
+ costUnavailableReason,
4145
+ ...repairReviewWarnings.size > 0 ? { warnings: [...repairReviewWarnings] } : {},
4146
+ durationMs,
4147
+ summary: `Autoloop: ${totalIterationsOverall} iterations, ${terminationReason} (${outcomeMetadata.outcome})`,
4148
+ iterationHistory,
4149
+ totalIterations: totalIterationsOverall,
4150
+ terminationReason,
4151
+ finalProgressScore,
4152
+ loopSuccess,
4153
+ outcomeMetadata,
4154
+ aggregateTimeMetrics,
4155
+ policyEvents: policyRecorder.events,
4156
+ iterationHistoryArtifact: iterationHistoryArtifactPersisted ? `${ctx.stage.id}.iteration-history` : void 0,
4157
+ resultArtifact: resultArtifactPersisted ? `${ctx.stage.id}.autoloop-result` : void 0,
4158
+ failureArtifact: terminationReason === "error" && failureArtifactPersisted ? `${ctx.stage.id}.autoloop-failure` : void 0,
4159
+ ...finalIterationCompletionEvidence ? { completionEvidence: finalIterationCompletionEvidence } : {}
4160
+ };
4161
+ runSummary = buildAutoloopRunSummary(ctx, result, {
4162
+ runId: generateCompetitionId(),
4163
+ startedAt: new Date(runStartedAtMsForSummary).toISOString(),
4164
+ noOpIterationCount: noOpCount,
4165
+ repairAttempts: totalRepairAttemptsForEscalation,
4166
+ repairSuccesses: totalRepairSuccesses,
4167
+ gatePreset: currentGatePreset,
4168
+ aiValidationShortCircuited
4169
+ });
4170
+ } catch (err) {
4171
+ executionError = err instanceof Error ? err : new Error(String(err));
4172
+ const durationMs = performance2.now() - startTime;
4173
+ policyRecorder.emit({
4174
+ policy: "goal_gate",
4175
+ eventType: "terminated",
4176
+ reason: "error",
4177
+ details: { error: executionError.message }
4178
+ });
4179
+ const outerCatchCost = resumedFromTerminalDiagnostics ? priorTerminalCostForResult ?? 0 : iterationHistory.reduce((sum, record) => sum + record.cost, 0);
4180
+ const costUnavailableReason = firstCostUnavailableReason(iterationHistory);
4181
+ if (ctx.artifactStore) {
4182
+ if (iterationHistory.length > 0) {
4183
+ iterationHistoryArtifactPersisted = await saveBestEffort(
4184
+ ctx.artifactStore,
4185
+ ctx.stage.id,
4186
+ "iteration-history",
4187
+ iterationHistory
4188
+ ) || iterationHistoryArtifactPersisted;
4189
+ }
4190
+ failureArtifactPersisted = await saveBestEffort(
4191
+ ctx.artifactStore,
4192
+ ctx.stage.id,
4193
+ "autoloop-failure",
4194
+ createFailureDiagnostic({
4195
+ error: executionError,
4196
+ iterationHistory,
4197
+ stageId: ctx.stage.id,
4198
+ terminationReason: "error",
4199
+ allowlist: ctx.stage.allowlist
4200
+ })
4201
+ ) || failureArtifactPersisted;
4202
+ }
4203
+ result = {
4204
+ stageId: ctx.stage.id,
4205
+ stageType: "autoloop",
4206
+ validationPassed: false,
4207
+ cost: outerCatchCost,
4208
+ costUnavailableReason,
4209
+ ...repairReviewWarnings.size > 0 ? { warnings: [...repairReviewWarnings] } : {},
4210
+ durationMs,
4211
+ summary: `Autoloop: ${iterationHistory.length} iteration(s), error (red)`,
4212
+ iterationHistory,
4213
+ totalIterations: previousIterationCountForResult + iterationHistory.length,
4214
+ terminationReason: "error",
4215
+ finalProgressScore: iterationHistory.length > 0 ? iterationHistory.at(-1)?.progressScore ?? 0 : 0,
4216
+ loopSuccess: false,
4217
+ outcomeMetadata: {
4218
+ outcome: "red",
4219
+ terminationClass: "execution_error",
4220
+ failedMustPassGates: 0,
4221
+ failedWarnGates: 0,
4222
+ repairAttempted: false,
4223
+ repairSuccesses: 0,
4224
+ hadDrift: false,
4225
+ summary: executionError.message
4226
+ },
4227
+ policyEvents: policyRecorder.events,
4228
+ iterationHistoryArtifact: iterationHistory.length > 0 && iterationHistoryArtifactPersisted ? `${ctx.stage.id}.iteration-history` : void 0,
4229
+ resultArtifact: void 0,
4230
+ failureArtifact: failureArtifactPersisted ? `${ctx.stage.id}.autoloop-failure` : void 0
4231
+ };
4232
+ runSummary = buildAutoloopRunSummary(ctx, result, {
4233
+ runId: generateCompetitionId(),
4234
+ startedAt: new Date(runStartedAtMsForSummary).toISOString(),
4235
+ noOpIterationCount: 0,
4236
+ repairAttempts: 0,
4237
+ repairSuccesses: 0,
4238
+ gatePreset: "fast",
4239
+ aiValidationShortCircuited: false
4240
+ });
4241
+ } finally {
4242
+ await Promise.allSettled(pendingIterationRecords);
4243
+ await finalizeAutoloopRun(ctx, result, runSummary, {
4244
+ preserveDurableDiagnostics: resumedFromTerminalDiagnostics,
4245
+ providerSwitch: durableStageProviderSwitch(providerSwitch)
4246
+ });
4247
+ }
4248
+ if (executionError) {
4249
+ throw executionError;
4250
+ }
4251
+ const stopped = providerFailure.stop ? markProviderFailureStop(result, providerFailure.stop) : result;
4252
+ return withStageProviderRun(stopped, providerSwitch);
4253
+ }
4254
+ function filterDeterministicGates(gates) {
4255
+ const filteredGates = [];
4256
+ const gatesToRun = [];
4257
+ const isNonDeterministic = (gate) => {
4258
+ const c = gate.command.trim();
4259
+ if (!c) return true;
4260
+ if (gate.deterministic === false) return true;
4261
+ const patterns = [
4262
+ /\bbuild\s+(compete|run|flywheel|sprint)\b/i,
4263
+ /\btests\/e2e\b/i,
4264
+ /\btest:e2e\b/i,
4265
+ /\bvitest\b.*\btests\/e2e\b/i,
4266
+ /\bplaywright\b/i,
4267
+ /\bcypress\b/i,
4268
+ /\b--ai-judge\b/i
4269
+ ];
4270
+ return patterns.some((re) => re.test(c));
4271
+ };
4272
+ for (const gate of gates) {
4273
+ const c = gate.command.trim();
4274
+ if (!c) continue;
4275
+ if (isNonDeterministic(gate)) {
4276
+ filteredGates.push(gate);
4277
+ continue;
4278
+ }
4279
+ gatesToRun.push({ ...gate, command: c });
4280
+ }
4281
+ return { gatesToRun, filteredGates };
4282
+ }
4283
+ function classifyAutoloopProviderStatus(input) {
4284
+ const runResult = input.providerResult.result;
4285
+ const providerDiagnostics = normalizeProviderRunDiagnostics(runResult?.providerDiagnostics) ?? normalizeProviderRunDiagnostics(input.providerResult.providerDiagnostics);
4286
+ const exitCode = typeof runResult?.exitCode === "number" ? runResult.exitCode : void 0;
4287
+ const executionStatus = exitCode === void 0 ? input.providerResult.success ? "completed" : "failed" : exitCode === 0 ? "completed" : "failed";
4288
+ const hasLegacyUsableStdout = input.stdoutText.trim().length > 0 || typeof input.providerResult.output === "string" && input.providerResult.output.trim().length > 0;
4289
+ const hasUsableOutput = typeof providerDiagnostics?.finalAnswerPresent === "boolean" ? providerDiagnostics.finalAnswerPresent : hasLegacyUsableStdout;
4290
+ const stderrText = input.stderrText.trim();
4291
+ const diagnosticsLookFatal = providerDiagnosticsReportFatalRuntimeIssue(providerDiagnostics);
4292
+ const stderrLooksFatal = providerDiagnostics === void 0 && looksLikeProviderToolOrRuntimeError(stderrText);
4293
+ if (executionStatus === "failed") {
4294
+ const detail = exitCode === void 0 ? "Provider execution failed before reporting an exit code" : `Provider execution failed with exit code ${exitCode}`;
4295
+ return {
4296
+ executionStatus,
4297
+ outputStatus: hasUsableOutput ? "usable" : "missing",
4298
+ detail,
4299
+ error: input.providerResult.error?.message ?? detail
4300
+ };
4301
+ }
4302
+ if (!hasUsableOutput && (diagnosticsLookFatal || stderrLooksFatal)) {
4303
+ const detail = providerDiagnostics === void 0 ? "Provider completed but stderr reports tool/runtime errors and no usable stdout" : "Provider completed but structured diagnostics report tool/runtime errors and no usable output";
4304
+ const stderrTail = truncateTail(stderrText, 500);
4305
+ return {
4306
+ executionStatus,
4307
+ outputStatus: "invalid",
4308
+ detail,
4309
+ error: stderrTail ? `${detail}: ${stderrTail}` : detail
4310
+ };
4311
+ }
4312
+ return {
4313
+ executionStatus,
4314
+ outputStatus: hasUsableOutput ? "usable" : "missing",
4315
+ detail: hasUsableOutput ? void 0 : "Provider completed without usable output"
4316
+ };
4317
+ }
4318
+ function looksLikeProviderToolOrRuntimeError(stderrText) {
4319
+ if (!stderrText) return false;
4320
+ return [
4321
+ /\btool(?:\s+\w+)?\s+(?:error|failed|failure)\b/i,
4322
+ /\btool call\b.*\b(?:error|failed|failure)\b/i,
4323
+ /\bfailed to (?:call|execute|run) tool\b/i,
4324
+ /\berror executing tool\b/i,
4325
+ /\btransport channel closed\b/i,
4326
+ /\bAuthorizationRequired\b/i,
4327
+ /^error[:\s]/i,
4328
+ /\buncaught (?:exception|error)\b/i
4329
+ ].some((pattern) => pattern.test(stderrText));
4330
+ }
4331
+ async function evaluateSimpleArtifactGoal(input) {
4332
+ const goal = parseSimpleArtifactGoal(input.prompt);
4333
+ if (!goal) return { satisfied: false };
4334
+ return {
4335
+ satisfied: await isSimpleArtifactGoalSatisfiedForChanges(
4336
+ input.worktreePath,
4337
+ goal,
4338
+ input.changedFiles
4339
+ ),
4340
+ goal
4341
+ };
4342
+ }
4343
+ async function getWorktreeChangedFiles(worktreePath) {
4344
+ try {
4345
+ const { stdout } = await execa2("git", ["status", "--porcelain", "--untracked-files=all"], {
4346
+ cwd: worktreePath,
4347
+ stdio: "pipe",
4348
+ timeout: 3e4
4349
+ });
4350
+ const files = parseGitStatusPaths(stdout, { includeOriginalPaths: true }).filter(
4351
+ (file) => file.length > 0
4352
+ );
4353
+ return {
4354
+ files: filterWorktreeRunSentinelPaths(Array.from(new Set(files))).sort(
4355
+ (a, b) => a.localeCompare(b)
4356
+ ),
4357
+ readable: true
4358
+ };
4359
+ } catch {
4360
+ return { files: [], readable: false };
4361
+ }
4362
+ }
4363
+ function buildIterationPrompt(input) {
4364
+ const { basePrompt, iteration, iterationHistory, gateCommands } = input;
4365
+ const acceptanceCriteria = extractAcceptanceCriteria(basePrompt);
4366
+ const acceptanceCriteriaForPrompt = formatAcceptanceCriteriaForPrompt(acceptanceCriteria);
4367
+ const acceptanceCriteriaSection = acceptanceCriteria.length > 0 ? `
4368
+ ## Acceptance Criteria (treat as a checklist)
4369
+ ${acceptanceCriteriaForPrompt}
4370
+ ` : "";
4371
+ const criteriaReportSection = acceptanceCriteria.length > 0 ? '\n## Criteria Report (required every iteration)\nAt the end of your response, output a machine-parseable JSON report exactly like:\n\nBUILD_CRITERIA_REPORT:\n{\n "criteria": [\n { "id": "AC1", "status": "met", "evidence": ["src/path.ts: explains how criterion is satisfied"] },\n { "id": "AC2", "status": "partial", "evidence": ["..."] },\n { "id": "AC3", "status": "not_met", "evidence": [] }\n ]\n}\n\nRules:\n- Status must be one of: met | partial | not_met\n- Evidence must be concrete (file paths, flags, behavior, outputs)\n- Do not add features beyond the acceptance criteria unless explicitly requested\n' : "";
4372
+ if (iteration <= 1) {
4373
+ const testRequirementSection = input.requiresTests === true ? "\n\n## Test Requirement\nThis autoloop requires test files to be created. " + renderTestRequirementBody(input.promptLanguage, input.testFilePatterns) + "\n" : "";
4374
+ if (gateCommands.length === 0)
4375
+ return `${basePrompt}${acceptanceCriteriaSection}${testRequirementSection}${criteriaReportSection}`;
4376
+ return `${basePrompt}
4377
+
4378
+ ` + acceptanceCriteriaSection + testRequirementSection + "## Autoloop Quality Gates\nMake these commands pass (they will be re-run after each iteration):\n" + gateCommands.map((c) => `- ${c}`).join("\n") + `
4379
+ ${criteriaReportSection}`;
4380
+ }
4381
+ const prev = iterationHistory.at(-1) ?? input.resumedFeedback;
4382
+ const parts = [`${basePrompt}${acceptanceCriteriaSection}${criteriaReportSection}`];
4383
+ if (input.requiresTests === true) {
4384
+ parts.push(
4385
+ "\n## Test Requirement\nThis autoloop requires test files to be created. " + renderTestRequirementBody(input.promptLanguage, input.testFilePatterns)
4386
+ );
4387
+ }
4388
+ if (gateCommands.length > 0) {
4389
+ parts.push(
4390
+ "\n## Autoloop Quality Gates\nMake these commands pass (they will be re-run after each iteration):\n" + gateCommands.map((c) => `- ${c}`).join("\n")
4391
+ );
4392
+ }
4393
+ if (prev) {
4394
+ const changed = prev.changedFiles.slice(0, 50).map((f) => `- ${f}`).join("\n");
4395
+ parts.push(`
4396
+ ## Previous Iteration Feedback (iteration ${prev.iteration})`);
4397
+ if (prev.error) parts.push(`- Provider error: ${prev.error}`);
4398
+ if (prev.allowlistDrift && prev.allowlistViolations && prev.allowlistViolations.length > 0) {
4399
+ const driftFiles = prev.allowlistViolations.slice(0, 20).map((f) => ` - ${f}`).join("\n");
4400
+ parts.push(
4401
+ `
4402
+ ### Allowlist Drift Detected
4403
+ The following ${prev.allowlistViolations.length} file(s) are outside the stage allowlist:
4404
+ ${driftFiles}` + (prev.allowlistViolations.length > 20 ? `
4405
+ ... and ${prev.allowlistViolations.length - 20} more` : "") + "\n\n**Action required**: Revert changes to these files or ensure they are within the allowlist scope."
4406
+ );
4407
+ }
4408
+ if (prev.allowlistRevertedFiles && prev.allowlistRevertedFiles.length > 0) {
4409
+ const reverted = prev.allowlistRevertedFiles.slice(0, 20).map((f) => ` - ${f}`).join("\n");
4410
+ parts.push(
4411
+ `
4412
+ ### Allowlist Auto-Revert (strict mode)
4413
+ Flywheel automatically reverted edits to ${prev.allowlistRevertedFiles.length} file(s) outside the stage allowlist:
4414
+ ${reverted}` + (prev.allowlistRevertedFiles.length > 20 ? `
4415
+ ... and ${prev.allowlistRevertedFiles.length - 20} more` : "") + "\n\n**Action required**: Keep all edits within the allowlist; do not reintroduce these changes."
4416
+ );
4417
+ }
4418
+ if (prev.qualityGatePassed === false) {
4419
+ parts.push(`- Quality gate failed: ${prev.qualityGateError ?? "unknown"}`);
4420
+ const tail = prev.qualityGateCommandResults?.find(isFailingGateCommandResult)?.outputTail ?? prev.qualityGateCommandResults?.at(-1)?.outputTail;
4421
+ if (tail) {
4422
+ parts.push(`
4423
+ ### Failing Command Output (tail)
4424
+ \`\`\`
4425
+ ${tail}
4426
+ \`\`\``);
4427
+ }
4428
+ }
4429
+ if (changed) parts.push(`
4430
+ ### Changed Files (latest)
4431
+ ${changed}`);
4432
+ }
4433
+ return parts.join("\n");
4434
+ }
4435
+ async function saveBestEffort(store, stageId, name, payload) {
4436
+ try {
4437
+ await store.save(stageId, name, "json", JSON.stringify(payload, null, 2));
4438
+ return true;
4439
+ } catch {
4440
+ return false;
4441
+ }
4442
+ }
4443
+ function createFailureDiagnostic(input) {
4444
+ const lastIteration = input.iterationHistory.at(-1);
4445
+ const failingGateCommand = lastIteration?.qualityGateCommandResults?.find(
4446
+ isFailingGateCommandResult
4447
+ );
4448
+ return {
4449
+ stageId: input.stageId,
4450
+ terminationReason: input.terminationReason,
4451
+ errorMessage: input.error instanceof Error ? input.error.message : String(input.error),
4452
+ errorStack: input.error instanceof Error ? input.error.stack : void 0,
4453
+ lastIteration: lastIteration ? {
4454
+ iteration: lastIteration.iteration,
4455
+ changedFiles: lastIteration.changedFiles,
4456
+ allowlistViolations: lastIteration.allowlistViolations,
4457
+ qualityGateError: lastIteration.qualityGateError,
4458
+ failingCommand: failingGateCommand ? {
4459
+ command: failingGateCommand.command,
4460
+ exitCode: failingGateCommand.exitCode,
4461
+ outputTail: failingGateCommand.outputTail
4462
+ } : void 0
4463
+ } : void 0,
4464
+ allowlist: input.allowlist,
4465
+ totalIterations: input.iterationHistory.length,
4466
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
4467
+ };
4468
+ }
4469
+ async function inferTerminationReason(input) {
4470
+ const lastIteration = input.iterationHistory.at(-1);
4471
+ if (input.controllerTerminationReason) {
4472
+ const controllerReason = input.controllerTerminationReason;
4473
+ if (controllerReason === "no_op_loop" && lastIteration?.terminationSignal === "no_op_intervention_exceeded") {
4474
+ return "no_op_intervention_exceeded";
4475
+ }
4476
+ if (controllerReason === "completion_signal" || controllerReason === "semantic_complete" || controllerReason === "deterministic_complete" || controllerReason === "heuristic_complete" || controllerReason === "progress_stagnation") {
4477
+ } else if (controllerReason === "verification_unavailable" && lastIteration?.success === true && lastIteration.verification?.outcome === "unverified") {
4478
+ if (input.requiresTests === true) {
4479
+ const testFiles = await detectTestFiles(
4480
+ input.iterationHistory.flatMap((r) => r.changedFiles),
4481
+ input.testFilePatterns,
4482
+ input.worktreePath
4483
+ );
4484
+ if (testFiles.length === 0) return "tests_required_not_met";
4485
+ }
4486
+ if (input.runtimeEvidenceCheck && !input.runtimeEvidenceCheck.passed) {
4487
+ return "evidence_unsatisfied";
4488
+ }
4489
+ return "verification_unavailable";
4490
+ } else {
4491
+ return mapControllerReasonToFlywheel(controllerReason);
4492
+ }
4493
+ }
4494
+ if (lastIteration?.terminationSignal && lastIteration.terminationSignal !== "goal_satisfied") {
4495
+ return lastIteration.terminationSignal;
4496
+ }
4497
+ if (input.gatesEnabled && input.iterationHistory.length > 0 && input.iterationHistory.at(-1)?.qualityGatePassed !== true) {
4498
+ const errorText = input.iterationHistory.at(-1)?.error ?? "";
4499
+ if (/timebox exhausted before quality gates/i.test(errorText)) {
4500
+ return "timebox_exceeded";
4501
+ }
4502
+ return "quality_gate_failure";
4503
+ }
4504
+ const hasBaselineOnlyFailure = lastIteration?.qualityGateCommandResults?.some(
4505
+ (gate) => gate.mustPass !== false && gate.passed === false && gate.isRegression === false
4506
+ );
4507
+ const hasPassingGateEvidence = lastIteration?.qualityGateCommandResults?.some(
4508
+ (gate) => gate.mustPass !== false && gate.passed === true
4509
+ );
4510
+ if (hasBaselineOnlyFailure && !hasPassingGateEvidence) {
4511
+ return "baseline_blocked";
4512
+ }
4513
+ if (input.requiresTests === true) {
4514
+ const allChangedFiles = input.iterationHistory.flatMap((r) => r.changedFiles);
4515
+ const testFiles = await detectTestFiles(
4516
+ allChangedFiles,
4517
+ input.testFilePatterns,
4518
+ input.worktreePath
4519
+ );
4520
+ if (testFiles.length === 0) {
4521
+ return "tests_required_not_met";
4522
+ }
4523
+ }
4524
+ if (input.runtimeEvidenceCheck && !input.runtimeEvidenceCheck.passed) {
4525
+ return "evidence_unsatisfied";
4526
+ }
4527
+ if (lastIteration?.terminationSignal === "goal_satisfied") {
4528
+ return "goal_satisfied";
4529
+ }
4530
+ if (input.controllerTerminationReason === "progress_stagnation") {
4531
+ return "progress_stagnation";
4532
+ }
4533
+ const definitiveControllerCompletion = input.controllerTerminationReason === "completion_signal" || input.controllerTerminationReason === "semantic_complete" || input.controllerTerminationReason === "deterministic_complete";
4534
+ if (input.iterationHistory.length >= input.maxIterations && !definitiveControllerCompletion) {
4535
+ return "max_iterations_reached";
4536
+ }
4537
+ const lastTwo = input.loopIterations.slice(-2);
4538
+ if (!definitiveControllerCompletion && lastTwo.length === 2) {
4539
+ const prev = lastTwo[0];
4540
+ const curr = lastTwo[1];
4541
+ const noDelta = Math.abs(curr.progress.score - prev.progress.score) < input.minProgress;
4542
+ const bothNotSignificant = !prev.progress.isSignificant && !curr.progress.isSignificant;
4543
+ const stable = curr.progress.trend === "stable";
4544
+ if (stable && noDelta && bothNotSignificant) return "progress_stagnation";
4545
+ }
4546
+ if (input.controllerTerminationReason) {
4547
+ if (!hasFlywheelSubstantiveCompletionEvidence(input) && isSuccessLikeControllerReason(input.controllerTerminationReason)) {
4548
+ return "evidence_unsatisfied";
4549
+ }
4550
+ return mapControllerReasonToFlywheel(input.controllerTerminationReason);
4551
+ }
4552
+ return "error";
4553
+ }
4554
+ function hasFlywheelSubstantiveCompletionEvidence(input) {
4555
+ if (input.iterationHistory.some(
4556
+ (iteration) => iteration.changedFiles.length > 0 || iteration.qualityGatePassed === true || iteration.terminationSignal === "goal_satisfied"
4557
+ )) {
4558
+ return true;
4559
+ }
4560
+ return input.runtimeEvidenceConfigured === true && input.runtimeEvidenceCheck?.passed === true;
4561
+ }
4562
+ function computeAutoloopOutcome(input) {
4563
+ const {
4564
+ finalGateResults,
4565
+ iterationHistory,
4566
+ terminationReason,
4567
+ allowlistMode,
4568
+ repairAttempted,
4569
+ repairSuccesses
4570
+ } = input;
4571
+ const failedMustPassGates = finalGateResults?.filter((r) => !r.passed && r.gate.mustPass && r.isRegression !== false).length ?? 0;
4572
+ const failedWarnGates = finalGateResults?.filter((r) => !r.passed && !r.gate.mustPass).length ?? 0;
4573
+ const baselineOnlyFailures = finalGateResults?.filter((r) => !r.passed && r.gate.mustPass && r.isRegression === false).length ?? 0;
4574
+ const lastFailingGate = finalGateResults?.find((r) => !r.passed && r.isRegression !== false);
4575
+ const gatesExecuted = finalGateResults !== void 0 && finalGateResults.length > 0;
4576
+ const hadDrift = iterationHistory.some((r) => r.allowlistDrift === true);
4577
+ let outcome;
4578
+ let summary;
4579
+ const terminationClassByReason = {
4580
+ allowlist_violation: "allowlist_violation",
4581
+ baseline_blocked: "quality_gate",
4582
+ error: "execution_error",
4583
+ duplicate_change_loop: "no_op_loop",
4584
+ evidence_unsatisfied: "evidence_unsatisfied",
4585
+ goal_satisfied: "success",
4586
+ heuristic_complete: "success",
4587
+ max_iterations_reached: "iteration_budget",
4588
+ no_op_intervention_exceeded: "no_op_loop",
4589
+ no_op_loop: "no_op_loop",
4590
+ progress_stagnation: "progress_stagnation",
4591
+ quality_gate_failure: "quality_gate",
4592
+ resource_exhaustion: "resource_limit",
4593
+ success: "success",
4594
+ tests_required_not_met: "quality_gate",
4595
+ timebox_exceeded: "timebox",
4596
+ verification_unavailable: "verification_unavailable"
4597
+ };
4598
+ const unverifiedDelivery = isDeliveredUnverified(terminationReason, iterationHistory.at(-1)) ? iterationHistory.at(-1)?.verification : void 0;
4599
+ const terminationClass = unverifiedDelivery ? "unverified" : terminationClassByReason[terminationReason];
4600
+ if (terminationReason === "allowlist_violation" && allowlistMode === "strict") {
4601
+ outcome = "red";
4602
+ summary = "Hard allowlist violation (strict mode)";
4603
+ } else if (terminationReason === "resource_exhaustion") {
4604
+ outcome = "red";
4605
+ summary = "Resource limit (memory/CPU/process) exhausted before completion";
4606
+ } else if (terminationReason === "error") {
4607
+ outcome = "red";
4608
+ summary = "Unrecoverable execution error";
4609
+ } else if (terminationReason === "verification_unavailable") {
4610
+ outcome = "yellow";
4611
+ summary = unverifiedDelivery && formatUnverifiedDeliveryLabel(unverifiedDelivery) || "Verification unavailable; completion is unverified by deterministic gates";
4612
+ } else if (terminationReason === "baseline_blocked") {
4613
+ outcome = "yellow";
4614
+ summary = `${baselineOnlyFailures} baseline gate failure(s) remain; zero-green verification is not success`;
4615
+ } else if (failedMustPassGates > 0) {
4616
+ outcome = "yellow";
4617
+ summary = `${failedMustPassGates} must-pass gate(s) still failing after ${repairAttempted ? "repair attempts" : "iterations"}`;
4618
+ } else if (hadDrift && allowlistMode === "warn") {
4619
+ outcome = "yellow";
4620
+ summary = "Allowlist drift detected (warn mode)";
4621
+ } else if (hadDrift && allowlistMode === "filter") {
4622
+ outcome = "yellow";
4623
+ summary = "Allowlist drift filtered (filter mode)";
4624
+ } else if (failedWarnGates > 0) {
4625
+ outcome = "yellow";
4626
+ summary = `${failedWarnGates} warn-level gate(s) failing`;
4627
+ } else if (!gatesExecuted) {
4628
+ outcome = "yellow";
4629
+ summary = "No deterministic quality gates were executed; completion is unverified by gates";
4630
+ } else if (terminationClass !== "success") {
4631
+ outcome = "yellow";
4632
+ summary = `Loop terminated before successful completion (${terminationReason})`;
4633
+ } else {
4634
+ outcome = "green";
4635
+ summary = baselineOnlyFailures > 0 ? `No new gate regressions; ${baselineOnlyFailures} baseline gate failure(s) remain` : `All gates passed${repairAttempted ? ` (after ${repairSuccesses} repair iteration(s))` : ""}`;
4636
+ }
4637
+ return {
4638
+ outcome,
4639
+ terminationClass,
4640
+ failedMustPassGates,
4641
+ failedWarnGates,
4642
+ lastFailingGateId: lastFailingGate?.gate.id,
4643
+ repairAttempted,
4644
+ repairSuccesses,
4645
+ hadDrift,
4646
+ summary
4647
+ };
4648
+ }
4649
+ function computeAggregateTimeMetrics(iterationHistory) {
4650
+ if (iterationHistory.length === 0) {
4651
+ return {
4652
+ totalProviderMs: 0,
4653
+ totalGateMs: 0,
4654
+ totalRepairMs: 0,
4655
+ totalTestMs: 0,
4656
+ totalBuildMs: 0,
4657
+ totalOverheadMs: 0,
4658
+ avgIterationMs: 0,
4659
+ noOpIterationCount: 0,
4660
+ providerTimePct: 0,
4661
+ deterministicTimePct: 0
4662
+ };
4663
+ }
4664
+ let totalProviderMs = 0;
4665
+ let totalGateMs = 0;
4666
+ let totalRepairMs = 0;
4667
+ let totalTestMs = 0;
4668
+ let totalBuildMs = 0;
4669
+ let totalOverheadMs = 0;
4670
+ let totalDurationMs = 0;
4671
+ let noOpIterationCount = 0;
4672
+ for (const iter of iterationHistory) {
4673
+ if (iter.timeBreakdown) {
4674
+ totalProviderMs += iter.timeBreakdown.providerMs;
4675
+ totalGateMs += iter.timeBreakdown.gateMs;
4676
+ totalRepairMs += iter.timeBreakdown.repairMs;
4677
+ totalTestMs += iter.timeBreakdown.testMs ?? 0;
4678
+ totalBuildMs += iter.timeBreakdown.buildMs ?? 0;
4679
+ totalOverheadMs += iter.timeBreakdown.overheadMs;
4680
+ }
4681
+ totalDurationMs += iter.durationMs;
4682
+ if (iter.progressMetrics?.wasNoOp) {
4683
+ noOpIterationCount++;
4684
+ }
4685
+ }
4686
+ const avgIterationMs = totalDurationMs / iterationHistory.length;
4687
+ const providerTimePct = totalDurationMs > 0 ? totalProviderMs / totalDurationMs * 100 : 0;
4688
+ const deterministicMs = totalGateMs + totalRepairMs + totalTestMs + totalBuildMs;
4689
+ const deterministicTimePct = totalDurationMs > 0 ? deterministicMs / totalDurationMs * 100 : 0;
4690
+ return {
4691
+ totalProviderMs,
4692
+ totalGateMs,
4693
+ totalRepairMs,
4694
+ totalTestMs,
4695
+ totalBuildMs,
4696
+ totalOverheadMs,
4697
+ avgIterationMs,
4698
+ noOpIterationCount,
4699
+ providerTimePct,
4700
+ deterministicTimePct
4701
+ };
4702
+ }
4703
+ function computeIterationProgressMetrics(input) {
4704
+ const { changedFiles, qualityGate, previousIteration, iterationFingerprint } = input;
4705
+ const hadDiff = changedFiles.length > 0;
4706
+ const wasNoOp = typeof iterationFingerprint === "string" ? iterationFingerprint === "" : !hadDiff;
4707
+ let failingTestsEnd;
4708
+ let coverageEnd;
4709
+ if (qualityGate?.commandResults) {
4710
+ for (const result of qualityGate.commandResults) {
4711
+ const isTestCommand = describeVerificationCommand(result.command).kind === "test";
4712
+ if (isTestCommand && result.exitCode !== 0) {
4713
+ const parsedFailingTestCount = parseFailingTestCount(result.command, result.outputTail);
4714
+ if (parsedFailingTestCount !== void 0) {
4715
+ failingTestsEnd = parsedFailingTestCount;
4716
+ }
4717
+ }
4718
+ }
4719
+ }
4720
+ const failingTestsStart = previousIteration?.progressMetrics?.failingTestsEnd;
4721
+ const failingTestsDelta = failingTestsStart !== void 0 && failingTestsEnd !== void 0 ? failingTestsEnd - failingTestsStart : void 0;
4722
+ const coverageStart = previousIteration?.progressMetrics?.coverageEnd;
4723
+ const coverageDelta = coverageStart !== void 0 && coverageEnd !== void 0 ? coverageEnd - coverageStart : void 0;
4724
+ return {
4725
+ failingTestsStart,
4726
+ failingTestsEnd,
4727
+ failingTestsDelta,
4728
+ coverageStart,
4729
+ coverageEnd,
4730
+ coverageDelta,
4731
+ hadDiff,
4732
+ wasNoOp
4733
+ };
4734
+ }
4735
+ function parseFailingTestCount(command, output) {
4736
+ const text = output?.trim();
4737
+ if (!text) return void 0;
4738
+ const signal = parseFailureOutput(command, text, "test");
4739
+ return signal.parsed && signal.errorCount !== null && signal.errorCount > 0 ? signal.errorCount : void 0;
4740
+ }
4741
+
4742
+ export {
4743
+ isAutomaticRepairProtectedPath,
4744
+ classifyAttemptFailure,
4745
+ QuickProviderAttemptError,
4746
+ providerExitAttemptError,
4747
+ thrownProviderAttemptError,
4748
+ switchQuickTurnProvider,
4749
+ createStageProviderSwitch,
4750
+ subStageProviderSwitch,
4751
+ switchedVariantFields,
4752
+ stageProviderSwitchFields,
4753
+ withProviderSwitchRecord,
4754
+ stageProviderRun,
4755
+ stageDeadlineFailure,
4756
+ stageLaunchAllowed,
4757
+ runWithStageProviderSwitch,
4758
+ standardStageProviderSwitch,
4759
+ reviewRepairProviderSwitch,
4760
+ captureAutomaticRepairScopeBaseline,
4761
+ enforceAutomaticRepairScope,
4762
+ unionCustomGateCommands,
4763
+ createCustomGateSelectionReporter,
4764
+ mapControllerReasonToFlywheel,
4765
+ simpleArtifactToolPolicy,
4766
+ resolveFlywheelProvider,
4767
+ formatRepairSuppressionWarning,
4768
+ snapshotRepairSuppressions,
4769
+ detectNewRepairSuppressions,
4770
+ normalizeGates,
4771
+ runAllGates,
4772
+ TEST_FILE_PATTERNS_BY_LANGUAGE,
4773
+ selectDisplayTestFilePatterns,
4774
+ selectPrimaryPromptLanguage,
4775
+ detectTestFiles,
4776
+ verifyRuntimeEvidence,
4777
+ reconstructExhaustedAutoloopOutcome,
4778
+ classifyAutoloopGateTypeForTest,
4779
+ isAutoloopStage,
4780
+ executeAutoloopStage,
4781
+ computeIterationProgressMetrics
4782
+ };
4783
+ //# sourceMappingURL=chunk-VSPJRLGA.js.map