@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.
- package/.build-project/providers/catalog.json +13951 -0
- package/.build-project/providers/pricing.json +1978 -0
- package/.build-project/providers/registry.json +4103 -0
- package/.github/SECURITY.md +75 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +661 -0
- package/CODE_OF_CONDUCT.md +44 -0
- package/CONTRIBUTING.md +24 -0
- package/INSTALL.md +623 -0
- package/LICENSE +21 -0
- package/README.md +118 -2
- package/THIRD_PARTY_NOTICES.txt +26 -0
- package/dist/ai-judge-UE5UOYOO.js +191 -0
- package/dist/allowlist-grounding-KPLN6NC5.js +16 -0
- package/dist/allowlist-utils-MZZKKXRD.js +31 -0
- package/dist/apply-completion-mode-EM37UZHB.js +110 -0
- package/dist/artifact-migrator-WPACNSEP.js +276 -0
- package/dist/ask-2OZ4JFQR.js +176 -0
- package/dist/auth-6UKBTJUQ.js +185 -0
- package/dist/auth-H5KJL6HL.js +165 -0
- package/dist/auth-LVJRJDMU.js +12 -0
- package/dist/auth-NHZOCNRR.js +94 -0
- package/dist/auth-PBXHWXGW.js +95 -0
- package/dist/auth-T7Y5DCAH.js +91 -0
- package/dist/auth-TVVKCHQW.js +13 -0
- package/dist/auth-UHB2UCSS.js +97 -0
- package/dist/auth-VACA3VUL.js +98 -0
- package/dist/auth-VVLC77S3.js +44 -0
- package/dist/auth-liveness-XO4AGDDD.js +53 -0
- package/dist/autoloop-cleanup-SI3VGAAR.js +106 -0
- package/dist/autoloop-evidence-gates-J5GG5YQB.js +39 -0
- package/dist/autoloop-gate-policy-SUNAEP6Q.js +169 -0
- package/dist/autoloop-helpers-VQGQNTT6.js +52 -0
- package/dist/autoloop-ledger-P6TIDP7C.js +96 -0
- package/dist/autoloop-ledger-subscriber-HFAGOZXH.js +161 -0
- package/dist/autoloop-run-defaults-GWGZBNT7.js +27 -0
- package/dist/autoloop-run-lock-GBWRM5A2.js +197 -0
- package/dist/autoloop-success-C32QVWOP.js +93 -0
- package/dist/autoloop-termination-summary-GI6IBQWD.js +58 -0
- package/dist/autoloop-trajectory-AMKSQISD.js +70 -0
- package/dist/autoloop-wall-clock-T7ZVIQOC.js +12 -0
- package/dist/automatic-branch-cleanup-R62X2C5O.js +49 -0
- package/dist/autonomous-loop-controller-2WL363LW.js +4386 -0
- package/dist/backend-YUTFDZXZ.js +30 -0
- package/dist/background-executor-Z3ODTMD7.js +260 -0
- package/dist/backlog-75QPEAMW.js +403 -0
- package/dist/backlog-execution-intent-GWUV5TUK.js +19 -0
- package/dist/backup-active-store-XHWLN34M.js +16 -0
- package/dist/backup-machine-stores-UI4LTJOY.js +251 -0
- package/dist/backup-telemetry-db-Z4ICYCNW.js +26 -0
- package/dist/branch-archive-manager-FMSQ7YJF.js +18 -0
- package/dist/branch-cleanup-manager-ST6FJGND.js +35 -0
- package/dist/build-gate-7NKWPY62.js +68 -0
- package/dist/build-root-S7DINODY.js +18 -0
- package/dist/change-summary-R6CB6VXJ.js +50 -0
- package/dist/chunk-24MNKKOM.js +173 -0
- package/dist/chunk-2H6VZFIO.js +235 -0
- package/dist/chunk-2JIX3EKV.js +39 -0
- package/dist/chunk-2KO7HVZM.js +106 -0
- package/dist/chunk-2L36U25O.js +100 -0
- package/dist/chunk-2NMNFUQA.js +475 -0
- package/dist/chunk-2OUAEH67.js +83 -0
- package/dist/chunk-2VLJUXJU.js +115 -0
- package/dist/chunk-2VUEDIUM.js +77 -0
- package/dist/chunk-2Y2IIHPI.js +430 -0
- package/dist/chunk-2YRG3LHZ.js +220 -0
- package/dist/chunk-2Z5Q3SUY.js +65 -0
- package/dist/chunk-32IYXIE5.js +873 -0
- package/dist/chunk-33IGPPUI.js +292 -0
- package/dist/chunk-352PCJLK.js +283 -0
- package/dist/chunk-35J4NE6S.js +226 -0
- package/dist/chunk-3C7WBIVK.js +1016 -0
- package/dist/chunk-3H3EY6U7.js +1225 -0
- package/dist/chunk-3H4MWYIC.js +198 -0
- package/dist/chunk-3HHWQR3N.js +2479 -0
- package/dist/chunk-3JKNRYEE.js +14 -0
- package/dist/chunk-3LECCYZY.js +128 -0
- package/dist/chunk-3LOY6YZI.js +852 -0
- package/dist/chunk-3M57DADF.js +44 -0
- package/dist/chunk-3NOTLVLG.js +3803 -0
- package/dist/chunk-3OPP2OZB.js +1480 -0
- package/dist/chunk-3P5IN5E7.js +9 -0
- package/dist/chunk-3PL22IYZ.js +20 -0
- package/dist/chunk-3REDAJUJ.js +14 -0
- package/dist/chunk-3S2R4XJI.js +203 -0
- package/dist/chunk-3UBZI5DW.js +61 -0
- package/dist/chunk-3VV5OBGG.js +308 -0
- package/dist/chunk-3WY5TU2O.js +823 -0
- package/dist/chunk-43OKHTW4.js +82 -0
- package/dist/chunk-45KUJBDI.js +112 -0
- package/dist/chunk-47X5LM7B.js +224 -0
- package/dist/chunk-4B6FQJ77.js +268 -0
- package/dist/chunk-4BTDUT2S.js +42 -0
- package/dist/chunk-4DJL2VXA.js +24 -0
- package/dist/chunk-4EBSXZ73.js +48 -0
- package/dist/chunk-4QKP6I4C.js +294 -0
- package/dist/chunk-4U6XK2DG.js +300 -0
- package/dist/chunk-4UMRUFBD.js +132 -0
- package/dist/chunk-4US4SYJ4.js +569 -0
- package/dist/chunk-4ZPLWEZO.js +154 -0
- package/dist/chunk-56WPYDJH.js +624 -0
- package/dist/chunk-57WRCJJW.js +50 -0
- package/dist/chunk-5AW4RMNG.js +24 -0
- package/dist/chunk-5EQN56OZ.js +155 -0
- package/dist/chunk-5F43TUIN.js +38 -0
- package/dist/chunk-5FKHIKTS.js +115 -0
- package/dist/chunk-5HDP7XZG.js +91 -0
- package/dist/chunk-5KQETTNS.js +152 -0
- package/dist/chunk-5PDVN2KO.js +498 -0
- package/dist/chunk-5UBIFVRD.js +509 -0
- package/dist/chunk-5YDE3Q62.js +46 -0
- package/dist/chunk-626AJVMU.js +302 -0
- package/dist/chunk-633XZ7UF.js +3583 -0
- package/dist/chunk-63DHNHGA.js +236 -0
- package/dist/chunk-666656AR.js +568 -0
- package/dist/chunk-67YRJPJO.js +19 -0
- package/dist/chunk-6CAU7VBA.js +20 -0
- package/dist/chunk-6CB3KJRY.js +21 -0
- package/dist/chunk-6DBMALXL.js +29 -0
- package/dist/chunk-6E25C4IA.js +269 -0
- package/dist/chunk-6FIBNIQU.js +73 -0
- package/dist/chunk-6G57FYBV.js +189 -0
- package/dist/chunk-6KZKRTG3.js +4885 -0
- package/dist/chunk-6LAH2UV2.js +525 -0
- package/dist/chunk-6PZY2TTU.js +921 -0
- package/dist/chunk-6T6NHXK4.js +230 -0
- package/dist/chunk-6TBEX4R4.js +432 -0
- package/dist/chunk-6TRD7TEE.js +911 -0
- package/dist/chunk-6XCP2RAI.js +47 -0
- package/dist/chunk-726G6WDL.js +351 -0
- package/dist/chunk-75EMT7BF.js +616 -0
- package/dist/chunk-762ACDZJ.js +4696 -0
- package/dist/chunk-77I6G4CO.js +1 -0
- package/dist/chunk-7DJ6SQH3.js +510 -0
- package/dist/chunk-7HPCIOMA.js +1383 -0
- package/dist/chunk-7HYBSRPZ.js +17503 -0
- package/dist/chunk-7KBEOPVB.js +73 -0
- package/dist/chunk-7LHJDJB3.js +2222 -0
- package/dist/chunk-7NVJMH52.js +306 -0
- package/dist/chunk-7RMARDVH.js +33 -0
- package/dist/chunk-7SEWU4QG.js +36 -0
- package/dist/chunk-7TUTZTI2.js +911 -0
- package/dist/chunk-7VBDFPUM.js +340 -0
- package/dist/chunk-7VRJHTGZ.js +6406 -0
- package/dist/chunk-7YAOUUKK.js +493 -0
- package/dist/chunk-AAAODV76.js +860 -0
- package/dist/chunk-AB4HUJVQ.js +339 -0
- package/dist/chunk-AEQWV746.js +611 -0
- package/dist/chunk-AHU3FKPA.js +11639 -0
- package/dist/chunk-AISTV5XU.js +226 -0
- package/dist/chunk-AJBTKGJZ.js +48 -0
- package/dist/chunk-AJJNW7XT.js +248 -0
- package/dist/chunk-AJN5Y5YZ.js +160 -0
- package/dist/chunk-ALDTFNZ3.js +150 -0
- package/dist/chunk-ARARVIZK.js +281 -0
- package/dist/chunk-ATBM6AW5.js +53 -0
- package/dist/chunk-ATKEKM3E.js +206 -0
- package/dist/chunk-AUJLW6JZ.js +92 -0
- package/dist/chunk-AY3MHEFM.js +24 -0
- package/dist/chunk-B7PYTV72.js +236 -0
- package/dist/chunk-B7YRSULE.js +1423 -0
- package/dist/chunk-BAWJNMWF.js +339 -0
- package/dist/chunk-BBPIILO2.js +88 -0
- package/dist/chunk-BD3PGW6A.js +957 -0
- package/dist/chunk-BDPYFAB4.js +44 -0
- package/dist/chunk-BIUV23HH.js +729 -0
- package/dist/chunk-BQ3VAGXH.js +243 -0
- package/dist/chunk-BQJOGAWT.js +31 -0
- package/dist/chunk-BT7EG7CO.js +68 -0
- package/dist/chunk-BV3XQQLU.js +27 -0
- package/dist/chunk-BY6ODHNZ.js +78 -0
- package/dist/chunk-BZRLSZNV.js +79 -0
- package/dist/chunk-C3DH3NCY.js +2773 -0
- package/dist/chunk-C3KCMAJP.js +756 -0
- package/dist/chunk-C7AGUJ5K.js +267 -0
- package/dist/chunk-CBSWKQCX.js +342 -0
- package/dist/chunk-CE5OZBYY.js +21 -0
- package/dist/chunk-CFCVYHSW.js +373 -0
- package/dist/chunk-CHA2HDE5.js +126 -0
- package/dist/chunk-CIHWPGS5.js +146 -0
- package/dist/chunk-CJBSZX3F.js +127 -0
- package/dist/chunk-CLA6QIJM.js +43 -0
- package/dist/chunk-CPSVQR2I.js +365 -0
- package/dist/chunk-CRPRVNQ4.js +254 -0
- package/dist/chunk-CSIE4Y7C.js +1093 -0
- package/dist/chunk-CSOPSYDY.js +142 -0
- package/dist/chunk-CSXLLEGB.js +205 -0
- package/dist/chunk-CTDLERWU.js +293 -0
- package/dist/chunk-CULBXMSK.js +110 -0
- package/dist/chunk-D2M2Y5ID.js +21 -0
- package/dist/chunk-D37WY7SH.js +1060 -0
- package/dist/chunk-D4VJFCGZ.js +89 -0
- package/dist/chunk-D6WTZMZB.js +48 -0
- package/dist/chunk-DBHOWDUH.js +976 -0
- package/dist/chunk-DCACX7BV.js +19364 -0
- package/dist/chunk-DFHX4XYA.js +128 -0
- package/dist/chunk-DIKBRRKE.js +176 -0
- package/dist/chunk-DJG5RIOR.js +9 -0
- package/dist/chunk-DNIOIISW.js +616 -0
- package/dist/chunk-DOA5G7IA.js +315 -0
- package/dist/chunk-DP7FKP6W.js +855 -0
- package/dist/chunk-DS2JZ677.js +79 -0
- package/dist/chunk-DSRLVYBS.js +82 -0
- package/dist/chunk-EAEVKNJA.js +209 -0
- package/dist/chunk-EEUESN3T.js +575 -0
- package/dist/chunk-EJ4LUU5Y.js +73 -0
- package/dist/chunk-EJIWTPSZ.js +1063 -0
- package/dist/chunk-EJVCDTAN.js +116 -0
- package/dist/chunk-EKLUBDBQ.js +627 -0
- package/dist/chunk-EN62WLFD.js +849 -0
- package/dist/chunk-ENYRCL43.js +26 -0
- package/dist/chunk-EP22ZMFO.js +273 -0
- package/dist/chunk-EP4SVAVI.js +87 -0
- package/dist/chunk-EPAMVGI3.js +1562 -0
- package/dist/chunk-EPRMH6HO.js +24 -0
- package/dist/chunk-ES6NWJ36.js +4057 -0
- package/dist/chunk-ETA2K4KI.js +30 -0
- package/dist/chunk-ETAD4NKD.js +31 -0
- package/dist/chunk-EUWIIDQ7.js +93 -0
- package/dist/chunk-EVE3OD2W.js +141 -0
- package/dist/chunk-EXA26FFC.js +459 -0
- package/dist/chunk-EXSPAA35.js +244 -0
- package/dist/chunk-F55YABKV.js +258 -0
- package/dist/chunk-F5DNGU5W.js +27 -0
- package/dist/chunk-F622MJYV.js +103 -0
- package/dist/chunk-F6NIQD74.js +5111 -0
- package/dist/chunk-FAWJTC34.js +43 -0
- package/dist/chunk-FAZBRHGG.js +47 -0
- package/dist/chunk-FIEUSGNJ.js +39 -0
- package/dist/chunk-FKCG7K6H.js +1146 -0
- package/dist/chunk-FOVVXV45.js +229 -0
- package/dist/chunk-FRBPKBFE.js +30 -0
- package/dist/chunk-FSA5BPFI.js +268 -0
- package/dist/chunk-FSJ5TTZ3.js +29 -0
- package/dist/chunk-FT7SGLXT.js +193 -0
- package/dist/chunk-FUUVZZUB.js +62 -0
- package/dist/chunk-FYMRVUNZ.js +1697 -0
- package/dist/chunk-FYTAA5FG.js +162 -0
- package/dist/chunk-G3VB2UCZ.js +3892 -0
- package/dist/chunk-G6OIMVXH.js +405 -0
- package/dist/chunk-G7PQCXVH.js +228 -0
- package/dist/chunk-GAB6UW52.js +1087 -0
- package/dist/chunk-GAFJEYLK.js +29 -0
- package/dist/chunk-GC4CRMTS.js +642 -0
- package/dist/chunk-GFHSIWHL.js +5297 -0
- package/dist/chunk-GI5M67YA.js +735 -0
- package/dist/chunk-GIJWHUWF.js +191 -0
- package/dist/chunk-GO6DRECI.js +1 -0
- package/dist/chunk-GOFVBTMN.js +314 -0
- package/dist/chunk-GQIP72ZJ.js +13 -0
- package/dist/chunk-GQXLXRFA.js +28 -0
- package/dist/chunk-GT6HFKJB.js +48 -0
- package/dist/chunk-GVJGSSKM.js +174 -0
- package/dist/chunk-GWHGTG37.js +36 -0
- package/dist/chunk-GZAJHLKO.js +18 -0
- package/dist/chunk-GZK62I3N.js +314 -0
- package/dist/chunk-H3HGMISG.js +483 -0
- package/dist/chunk-H3ISZJMJ.js +403 -0
- package/dist/chunk-H4HXVWEC.js +9 -0
- package/dist/chunk-H6MJGKAW.js +120 -0
- package/dist/chunk-HBTTGVR5.js +353 -0
- package/dist/chunk-HBZ322WZ.js +35 -0
- package/dist/chunk-HEP3ILP4.js +34 -0
- package/dist/chunk-HGFXODKO.js +106 -0
- package/dist/chunk-HHWDTB6S.js +59 -0
- package/dist/chunk-HKO45NLZ.js +286 -0
- package/dist/chunk-HLVTEA6A.js +89 -0
- package/dist/chunk-HS3SY6HN.js +160 -0
- package/dist/chunk-HT5OCGKN.js +899 -0
- package/dist/chunk-HXIIEMGF.js +78 -0
- package/dist/chunk-I56TH7VM.js +46 -0
- package/dist/chunk-I6FN3R5D.js +352 -0
- package/dist/chunk-I7SCBHJN.js +655 -0
- package/dist/chunk-IJM4GKAT.js +529 -0
- package/dist/chunk-IMEYU36R.js +344 -0
- package/dist/chunk-INJ3SS6A.js +936 -0
- package/dist/chunk-IUKOPCCE.js +441 -0
- package/dist/chunk-IVEPZQJO.js +33 -0
- package/dist/chunk-IVZEONFC.js +43 -0
- package/dist/chunk-IWBHRSG7.js +936 -0
- package/dist/chunk-IYY3HQAD.js +484 -0
- package/dist/chunk-J2ICMYDA.js +48 -0
- package/dist/chunk-J2QCFLUN.js +505 -0
- package/dist/chunk-J5HM6V5P.js +1039 -0
- package/dist/chunk-J6NZX6QH.js +398 -0
- package/dist/chunk-JBBB2Y47.js +432 -0
- package/dist/chunk-JDCZQZU7.js +42 -0
- package/dist/chunk-JEDNXGSS.js +169 -0
- package/dist/chunk-JHP7OT6K.js +89 -0
- package/dist/chunk-JLS65NUQ.js +14 -0
- package/dist/chunk-JNMWYMC6.js +38 -0
- package/dist/chunk-JNSTWNNW.js +25 -0
- package/dist/chunk-JOSK6GEU.js +9564 -0
- package/dist/chunk-JQUMSIGC.js +100 -0
- package/dist/chunk-JVHGMURQ.js +1106 -0
- package/dist/chunk-JYXS47FQ.js +951 -0
- package/dist/chunk-K2ZMW2Z5.js +2147 -0
- package/dist/chunk-K7FAFORM.js +14 -0
- package/dist/chunk-KBJXX3ON.js +333 -0
- package/dist/chunk-KHFKVQWY.js +486 -0
- package/dist/chunk-KHMEI3DQ.js +173 -0
- package/dist/chunk-KHW3UD7B.js +69 -0
- package/dist/chunk-KKQKWAMF.js +365 -0
- package/dist/chunk-KOKXJV2U.js +455 -0
- package/dist/chunk-KQ4NGOJI.js +14 -0
- package/dist/chunk-KQYQOXMS.js +342 -0
- package/dist/chunk-KTMW65UX.js +14 -0
- package/dist/chunk-KUEWV2JH.js +593 -0
- package/dist/chunk-KXYDV3HP.js +1523 -0
- package/dist/chunk-KZ4JMGZ5.js +96 -0
- package/dist/chunk-KZWGBUIO.js +2659 -0
- package/dist/chunk-L2CBG5IX.js +1892 -0
- package/dist/chunk-L2S235CU.js +314 -0
- package/dist/chunk-L32WD57F.js +98 -0
- package/dist/chunk-LDJCKAJP.js +123 -0
- package/dist/chunk-LDWNBEN7.js +541 -0
- package/dist/chunk-LJVH57G2.js +241 -0
- package/dist/chunk-LPQ7NZEN.js +209 -0
- package/dist/chunk-LRGH3CCV.js +148 -0
- package/dist/chunk-LULOGMMM.js +1133 -0
- package/dist/chunk-LWGKN2GY.js +8284 -0
- package/dist/chunk-LX4F24ZH.js +157 -0
- package/dist/chunk-M4BUEXXF.js +839 -0
- package/dist/chunk-M6UHOC4I.js +553 -0
- package/dist/chunk-MCBNWZNO.js +657 -0
- package/dist/chunk-MEMSY6VY.js +223 -0
- package/dist/chunk-MHLEZQQI.js +145 -0
- package/dist/chunk-MJ6WNSIX.js +192 -0
- package/dist/chunk-MKASIKWZ.js +442 -0
- package/dist/chunk-MNOVBYFU.js +60 -0
- package/dist/chunk-MO2BYAAF.js +2896 -0
- package/dist/chunk-MP6P7Y2A.js +180 -0
- package/dist/chunk-MQ4TFJAG.js +3365 -0
- package/dist/chunk-MT2WAIR3.js +131 -0
- package/dist/chunk-MTU7VTNN.js +1122 -0
- package/dist/chunk-MULMXX4V.js +150 -0
- package/dist/chunk-MW2DWWVA.js +147 -0
- package/dist/chunk-MY3ZPUN4.js +151 -0
- package/dist/chunk-MYLH2S4G.js +64 -0
- package/dist/chunk-MZ5WYKNA.js +28 -0
- package/dist/chunk-N3Q2JNPV.js +249 -0
- package/dist/chunk-N5DX4JAK.js +12 -0
- package/dist/chunk-NAINGKHF.js +35 -0
- package/dist/chunk-NEQUQHOX.js +223 -0
- package/dist/chunk-NHNPEZS4.js +1408 -0
- package/dist/chunk-NHTIXJCR.js +17 -0
- package/dist/chunk-NKLPMBNV.js +1154 -0
- package/dist/chunk-NMVZ5YES.js +199 -0
- package/dist/chunk-NRFRY3GE.js +256 -0
- package/dist/chunk-NTD6CSI6.js +1266 -0
- package/dist/chunk-NUO7B3SH.js +318 -0
- package/dist/chunk-NVOHIJKG.js +1319 -0
- package/dist/chunk-NXH3RX7O.js +523 -0
- package/dist/chunk-NXZ3DURF.js +257 -0
- package/dist/chunk-O26L6F7F.js +209 -0
- package/dist/chunk-O5RRIUEP.js +124 -0
- package/dist/chunk-O5X6BKAO.js +10 -0
- package/dist/chunk-O6ZQUVTR.js +31 -0
- package/dist/chunk-O7FE75QZ.js +306 -0
- package/dist/chunk-OC7Z6AJQ.js +81 -0
- package/dist/chunk-OH7IEZ63.js +307 -0
- package/dist/chunk-OI734GRS.js +115 -0
- package/dist/chunk-OJBH62DK.js +684 -0
- package/dist/chunk-OKJLVSTJ.js +247 -0
- package/dist/chunk-OMX5VL43.js +79 -0
- package/dist/chunk-ONHAMBN6.js +12 -0
- package/dist/chunk-ONYPLIVE.js +753 -0
- package/dist/chunk-OSK7QWPD.js +637 -0
- package/dist/chunk-OTXLMJKP.js +438 -0
- package/dist/chunk-OV5PKQMA.js +213 -0
- package/dist/chunk-OXK5HA4B.js +31 -0
- package/dist/chunk-OYDWFQA4.js +272 -0
- package/dist/chunk-OZAUKWHB.js +22 -0
- package/dist/chunk-P5P2EKGH.js +94 -0
- package/dist/chunk-P6SSWAKY.js +29 -0
- package/dist/chunk-P7CQGPLS.js +30 -0
- package/dist/chunk-P7RVCHQV.js +197 -0
- package/dist/chunk-PEBWDYIW.js +726 -0
- package/dist/chunk-PFCXFO7A.js +9193 -0
- package/dist/chunk-PFLDWMX6.js +168 -0
- package/dist/chunk-PHZGLQSK.js +15883 -0
- package/dist/chunk-PNFTPNSR.js +20 -0
- package/dist/chunk-PPHXX4W7.js +175 -0
- package/dist/chunk-PPZESPJT.js +262 -0
- package/dist/chunk-PRDHI2L5.js +296 -0
- package/dist/chunk-PSTKWHHN.js +32 -0
- package/dist/chunk-PV53ODOT.js +555 -0
- package/dist/chunk-PVJBTI7P.js +24 -0
- package/dist/chunk-PXKERBKX.js +100 -0
- package/dist/chunk-PZ5AY32C.js +10 -0
- package/dist/chunk-Q3DHEZA5.js +85 -0
- package/dist/chunk-Q5RZDVQK.js +204 -0
- package/dist/chunk-Q64IR2Z2.js +4933 -0
- package/dist/chunk-Q6Z5UGPL.js +398 -0
- package/dist/chunk-QAMAV4DT.js +627 -0
- package/dist/chunk-QBWF4444.js +112 -0
- package/dist/chunk-QDKYT4CI.js +22805 -0
- package/dist/chunk-QFLBRWCJ.js +126 -0
- package/dist/chunk-QILAQACE.js +107 -0
- package/dist/chunk-QJ6OEH4K.js +3683 -0
- package/dist/chunk-QK3NLYTV.js +320 -0
- package/dist/chunk-QOFLUS24.js +535 -0
- package/dist/chunk-QOIASNEJ.js +98 -0
- package/dist/chunk-QP4UTSOV.js +3587 -0
- package/dist/chunk-QP5ST7RI.js +904 -0
- package/dist/chunk-QS5AGRZN.js +167 -0
- package/dist/chunk-QTAOU53N.js +107 -0
- package/dist/chunk-QTCLJHLZ.js +351 -0
- package/dist/chunk-QTOLTG4L.js +119 -0
- package/dist/chunk-QWDV4OSL.js +142 -0
- package/dist/chunk-QWMQ74KD.js +508 -0
- package/dist/chunk-QX6LOMMH.js +158 -0
- package/dist/chunk-QXBKIHY7.js +238 -0
- package/dist/chunk-QXBKTJT6.js +270 -0
- package/dist/chunk-QYQOVOLR.js +196 -0
- package/dist/chunk-R2CKS6WT.js +73 -0
- package/dist/chunk-R2TIYG7U.js +68 -0
- package/dist/chunk-R2W3LESH.js +465 -0
- package/dist/chunk-R5EXHMUN.js +100 -0
- package/dist/chunk-RBBTBCHX.js +289 -0
- package/dist/chunk-RBKLOUXA.js +121 -0
- package/dist/chunk-RCP2Y6DQ.js +86 -0
- package/dist/chunk-RCYEJSD5.js +260 -0
- package/dist/chunk-RFDVSI4N.js +134 -0
- package/dist/chunk-RGJU63FV.js +1717 -0
- package/dist/chunk-RI4EX2QE.js +18 -0
- package/dist/chunk-RIMARPCF.js +152 -0
- package/dist/chunk-RJ6RSNT6.js +42 -0
- package/dist/chunk-RNI2GUIF.js +131 -0
- package/dist/chunk-RPHUWJ3K.js +264 -0
- package/dist/chunk-RPIMAMV7.js +361 -0
- package/dist/chunk-RQFX6YRN.js +49 -0
- package/dist/chunk-RRC5BL7B.js +285 -0
- package/dist/chunk-RSYTSBOO.js +79 -0
- package/dist/chunk-RWQQIKIC.js +35 -0
- package/dist/chunk-RZUBOZH2.js +373 -0
- package/dist/chunk-S2E632FQ.js +380 -0
- package/dist/chunk-S2VQCZO4.js +22 -0
- package/dist/chunk-S2XIMQJQ.js +681 -0
- package/dist/chunk-S47ISY27.js +183 -0
- package/dist/chunk-S6I4KISU.js +142 -0
- package/dist/chunk-S6TAFQI5.js +2403 -0
- package/dist/chunk-SBBNT5ZJ.js +27 -0
- package/dist/chunk-SDJV2DTQ.js +491 -0
- package/dist/chunk-SDPIFNLD.js +33 -0
- package/dist/chunk-SF6YK3JI.js +835 -0
- package/dist/chunk-SK3ZFONO.js +976 -0
- package/dist/chunk-SKTFRFQ7.js +1775 -0
- package/dist/chunk-SNGM452K.js +2002 -0
- package/dist/chunk-STW7OFOA.js +581 -0
- package/dist/chunk-SU6UFUQY.js +1351 -0
- package/dist/chunk-SVJM3LJA.js +27 -0
- package/dist/chunk-SVUNZOPG.js +129 -0
- package/dist/chunk-T5XLUYV3.js +34 -0
- package/dist/chunk-T7E5JS6N.js +57 -0
- package/dist/chunk-TA3N5MMO.js +450 -0
- package/dist/chunk-TAJPCXSB.js +13 -0
- package/dist/chunk-TDEMDRN2.js +1639 -0
- package/dist/chunk-TDSEX5CF.js +24 -0
- package/dist/chunk-TGCVL74Y.js +28 -0
- package/dist/chunk-TGXXYDL4.js +1012 -0
- package/dist/chunk-TIKTYUJO.js +296 -0
- package/dist/chunk-TIZAJD4M.js +6029 -0
- package/dist/chunk-TLSEZZCG.js +5549 -0
- package/dist/chunk-TMBDRHAX.js +19 -0
- package/dist/chunk-TRURQUBP.js +1217 -0
- package/dist/chunk-TSQ256N5.js +152 -0
- package/dist/chunk-TUQMMFEQ.js +2244 -0
- package/dist/chunk-U424MJLC.js +81 -0
- package/dist/chunk-U4DRS6ZW.js +11 -0
- package/dist/chunk-U5KS22IN.js +204 -0
- package/dist/chunk-UAPHFJPN.js +673 -0
- package/dist/chunk-UB7LQITD.js +1040 -0
- package/dist/chunk-UBVRHGAZ.js +237 -0
- package/dist/chunk-UDNNAQN5.js +98 -0
- package/dist/chunk-UJP3XM54.js +342 -0
- package/dist/chunk-UJYDH2MW.js +305 -0
- package/dist/chunk-UKMPASIC.js +399 -0
- package/dist/chunk-UKUL3EHK.js +763 -0
- package/dist/chunk-UPHU64NB.js +1327 -0
- package/dist/chunk-URMSZXAD.js +43 -0
- package/dist/chunk-US3RT6CY.js +525 -0
- package/dist/chunk-UTDEOPKV.js +30 -0
- package/dist/chunk-UUUDKV7N.js +13 -0
- package/dist/chunk-UVKWU3SM.js +39 -0
- package/dist/chunk-V2TVPO3X.js +521 -0
- package/dist/chunk-V3KLVK7B.js +398 -0
- package/dist/chunk-V3YFFHDR.js +17 -0
- package/dist/chunk-VAG6GUBR.js +1162 -0
- package/dist/chunk-VBENAHDA.js +1814 -0
- package/dist/chunk-VBNCKCCI.js +16 -0
- package/dist/chunk-VCGSWN5M.js +1300 -0
- package/dist/chunk-VE2O2I7M.js +115 -0
- package/dist/chunk-VIFHYTI5.js +31 -0
- package/dist/chunk-VJER3FUS.js +5080 -0
- package/dist/chunk-VLMDM56Q.js +268 -0
- package/dist/chunk-VMFZEVVX.js +99 -0
- package/dist/chunk-VNDPQVUZ.js +1853 -0
- package/dist/chunk-VSPJRLGA.js +4783 -0
- package/dist/chunk-VSWC5ZXI.js +103 -0
- package/dist/chunk-VYI4DSZP.js +2017 -0
- package/dist/chunk-W4BAIQ6L.js +1212 -0
- package/dist/chunk-W7VQAL2K.js +2203 -0
- package/dist/chunk-WEI3ZDZH.js +33 -0
- package/dist/chunk-WEIUS5NT.js +26 -0
- package/dist/chunk-WEW7UFWE.js +546 -0
- package/dist/chunk-WGQAXLNA.js +384 -0
- package/dist/chunk-WJAL4JL3.js +206 -0
- package/dist/chunk-WKFOJ5ZG.js +141 -0
- package/dist/chunk-WMWQEFLN.js +2177 -0
- package/dist/chunk-WNECKKO2.js +154 -0
- package/dist/chunk-WPKLYOAM.js +195 -0
- package/dist/chunk-WSH37YQN.js +243 -0
- package/dist/chunk-WV3KQDPE.js +84 -0
- package/dist/chunk-WZFC4V4U.js +389 -0
- package/dist/chunk-XCOIBNET.js +13 -0
- package/dist/chunk-XDM65LBX.js +53 -0
- package/dist/chunk-XGU6PBO5.js +645 -0
- package/dist/chunk-XHOI4V62.js +43 -0
- package/dist/chunk-XIBVFMLQ.js +6858 -0
- package/dist/chunk-XJ2E7YEJ.js +117 -0
- package/dist/chunk-XON4ZCYX.js +69 -0
- package/dist/chunk-XQPEWJPI.js +127 -0
- package/dist/chunk-XSJTIFUY.js +31 -0
- package/dist/chunk-XT7OVWM3.js +64 -0
- package/dist/chunk-XTXYIISX.js +1104 -0
- package/dist/chunk-XV7NZL4B.js +57 -0
- package/dist/chunk-XWIZ6B5S.js +109 -0
- package/dist/chunk-XWLIKOBX.js +240 -0
- package/dist/chunk-XZOXEIN4.js +156 -0
- package/dist/chunk-XZRS7I7W.js +3673 -0
- package/dist/chunk-Y4ZDDHB6.js +44 -0
- package/dist/chunk-Y733RB7T.js +194 -0
- package/dist/chunk-YBHUB4XR.js +14931 -0
- package/dist/chunk-YKC75XK4.js +147 -0
- package/dist/chunk-YKMXFVK7.js +27 -0
- package/dist/chunk-YO5BAJ55.js +225 -0
- package/dist/chunk-YPDCLXNZ.js +568 -0
- package/dist/chunk-YSQPGVFK.js +116 -0
- package/dist/chunk-YTVVTIWJ.js +716 -0
- package/dist/chunk-YVCMDVBB.js +698 -0
- package/dist/chunk-YYTOA6DP.js +114 -0
- package/dist/chunk-Z5WGNULV.js +88 -0
- package/dist/chunk-Z6NGVVXG.js +66 -0
- package/dist/chunk-Z7CDQNH3.js +20 -0
- package/dist/chunk-ZH4G3FU5.js +187 -0
- package/dist/chunk-ZJB6SQPA.js +257 -0
- package/dist/chunk-ZL6E4HMY.js +42 -0
- package/dist/chunk-ZMLOC3DW.js +62 -0
- package/dist/chunk-ZORW64QH.js +101 -0
- package/dist/chunk-ZVVD2XGV.js +1369 -0
- package/dist/cli-utils-ER33ODC5.js +31 -0
- package/dist/cli-validation-6CHPTANV.js +146 -0
- package/dist/commit-utils-3STAY7C7.js +49 -0
- package/dist/compatibility-notice-IHNXR2JR.js +21 -0
- package/dist/compete-config-resolver-V3GDNJMM.js +125 -0
- package/dist/compete-results-XUM3PJHS.js +318 -0
- package/dist/competition-outcome-tracker-NWSMVUPG.js +62 -0
- package/dist/config-4HHE4OIK.js +238 -0
- package/dist/config-D6YAZASH.js +10 -0
- package/dist/config-MIPRDYYV.js +46 -0
- package/dist/config-audit-GA5DCUAI.js +371 -0
- package/dist/config-explain-4LMO556H.js +71 -0
- package/dist/config-loader-RGP634PC.js +51 -0
- package/dist/config-manager-J2UTO635.js +114 -0
- package/dist/config-merger-TVUJBZTS.js +14 -0
- package/dist/config-validator-H4ZVIE56.js +122 -0
- package/dist/cost-HSIV5U2Y.js +85 -0
- package/dist/data-migration-state-OAUL2WPQ.js +35 -0
- package/dist/db-migrator-NUOW5NKR.js +34 -0
- package/dist/default-paths-UILB62BN.js +49 -0
- package/dist/discovery-V6GYUJUC.js +53 -0
- package/dist/doctor-BBSMCDWE.js +271 -0
- package/dist/doctor-context-blocks-LPY75VZA.js +231 -0
- package/dist/doctor-instructions-import-JXHDDOE5.js +70 -0
- package/dist/doctor-purpose-6YJOXPBE.js +30 -0
- package/dist/domain-analyzer-S35ABUNA.js +48 -0
- package/dist/ensure-initialized-Q6PEU55O.js +78 -0
- package/dist/entry.js +922 -0
- package/dist/error-parser-core-MZFMFANN.js +70 -0
- package/dist/evaluation-context-GQBYDAEV.js +63 -0
- package/dist/evidence-plan-resolver-5FUAP3SA.js +111 -0
- package/dist/execution-data-writer-YCG5UH37.js +72 -0
- package/dist/execution-policy-applier-PWZM5LNA.js +63 -0
- package/dist/execution-preflight-DASJWWQF.js +223 -0
- package/dist/execution-roles-KNSNNB2D.js +112 -0
- package/dist/exit-code-error-QIY5DOKF.js +18 -0
- package/dist/external-temp-cleanup-CAW7LQ6R.js +360 -0
- package/dist/factory-YCYCB7AE.js +209 -0
- package/dist/file-utils-ZQYNBQBW.js +45 -0
- package/dist/flywheel-allowlist-revert-H22HA6JE.js +21 -0
- package/dist/flywheel-autoloop-executor-PDOASJEZ.js +316 -0
- package/dist/flywheel-autoloop-unverified-TOKYFCDC.js +36 -0
- package/dist/flywheel-competition-executor-YJUMMEJO.js +320 -0
- package/dist/flywheel-executor-JWU43KAY.js +437 -0
- package/dist/flywheel-git-isolation-FQ2TRWMB.js +56 -0
- package/dist/flywheel-manifest-HDPNMWWF.js +114 -0
- package/dist/flywheel-model-defaults-UHAKTZT3.js +110 -0
- package/dist/flywheel-preflight-III2C7TH.js +163 -0
- package/dist/flywheel-resume-2O45MS7Y.js +31 -0
- package/dist/flywheel-resume-execution-lease-P2BK257J.js +59 -0
- package/dist/flywheel-safety-AVAVFXIJ.js +19 -0
- package/dist/flywheel-scope-decision-ALJUUNGG.js +14 -0
- package/dist/get-telemetry-logger-MPDVO2JA.js +155 -0
- package/dist/git-message-style-KCXGMTDT.js +104 -0
- package/dist/guidance-profile-config-G3K5YQOO.js +113 -0
- package/dist/guidance-profile-renderer-TX4TNDFL.js +23 -0
- package/dist/identity-XLQJ4YJF.js +25 -0
- package/dist/index-query-6NUY4FKX.js +37 -0
- package/dist/index.js +85588 -0
- package/dist/indexer-DL54CM3K.js +44 -0
- package/dist/init-OUZ72344.js +245 -0
- package/dist/instructions-import-offer-NSYXSITC.js +363 -0
- package/dist/instructions-run-NBII7W36.js +114 -0
- package/dist/interrupted-work-E4TLKJLE.js +43 -0
- package/dist/invoke-provider-auth-NEWEIHCI.js +38 -0
- package/dist/judge-scoring.json +64 -0
- package/dist/lab-mode-YXQ2Y3NG.js +44 -0
- package/dist/lab-utils-5QAMDJR5.js +29 -0
- package/dist/linux-host-class-YDWQX65O.js +31 -0
- package/dist/llm-judge-executor-OJSNCTFJ.js +179 -0
- package/dist/local-ci-config-VZ4WWDT5.js +48 -0
- package/dist/local-executor-DCMDDRX2.js +280 -0
- package/dist/logger-5KNKS3O4.js +154 -0
- package/dist/matrix-prompt-builder-4ZBOGZSB.js +527 -0
- package/dist/matrix-result-parser-EZJM4YXP.js +14 -0
- package/dist/merge-batch-RTEEMVDB.js +313 -0
- package/dist/merge-batch-members-VIWGVRHP.js +24 -0
- package/dist/merge-queue-TIIUSOY6.js +126 -0
- package/dist/merge-queue-trust-DUNZ4J65.js +21 -0
- package/dist/metadata-UKSMWM6J.js +29 -0
- package/dist/model-catalog-readiness-R5KJBP5M.js +202 -0
- package/dist/model-invocations-db-XTUPPQ34.js +70 -0
- package/dist/model-resolver-JFZ2MHOJ.js +39 -0
- package/dist/model-resolver-SCQ57KAQ.js +217 -0
- package/dist/model-roles-D3OR4NAH.js +110 -0
- package/dist/monitor-F4SIU5G2.js +96 -0
- package/dist/nested-worktrees-2WUWQ75N.js +23 -0
- package/dist/next-S3IRLVER.js +96 -0
- package/dist/next-UKPFRXOO.js +148 -0
- package/dist/opportunistic-TL4S26VX.js +86 -0
- package/dist/optimization-config-resolver-NAEIYU6A.js +148 -0
- package/dist/package-HJR4QJT4.js +10 -0
- package/dist/parse-git-status-7PIYFPS2.js +21 -0
- package/dist/path-security-5WJSY56G.js +41 -0
- package/dist/paths-JUI7C4KU.js +45 -0
- package/dist/plan-backlog-list-fast-SSTQL5XP.js +89 -0
- package/dist/plan-backlog-show-fast-PFRAQ3JB.js +114 -0
- package/dist/plan-sprint-list-fast-HD2KBZSY.js +110 -0
- package/dist/plan-sprint-status-fast-YFYH7WQF.js +116 -0
- package/dist/plan-task-status-fast-JU5PNLUU.js +114 -0
- package/dist/plan-to-flywheel-PY5MSY2H.js +529 -0
- package/dist/preflight-PJHGTJNZ.js +12 -0
- package/dist/process-registry-B4ZUCN7T.js +28 -0
- package/dist/process-scanner-TU3QHWVB.js +23 -0
- package/dist/processes-6FHVGT72.js +28 -0
- package/dist/processes-json-fast-YWTMUWXB.js +72 -0
- package/dist/profile-HWTC56SG.js +209 -0
- package/dist/project-runtime-paths-2UQNG5Y4.js +41 -0
- package/dist/protected-branches-N36LDBFY.js +71 -0
- package/dist/provider-aware-resource-manager-YTFAHNGJ.js +40 -0
- package/dist/provider-matrix-2KO2V5OM.js +214 -0
- package/dist/provider-matrix-renderer-MEC3PER7.js +139 -0
- package/dist/provider-output-forwarding-2AHL7YMA.js +26 -0
- package/dist/provider-registry-WCZ65QHC.js +136 -0
- package/dist/provider-temp-cleanup-IUNPJCZF.js +14 -0
- package/dist/prune-engine-EOQVWEP6.js +448 -0
- package/dist/publication-turn-S42RZTQF.js +44 -0
- package/dist/quality-gate-JQMVCKOO.js +72 -0
- package/dist/quickstart-3O6FRCFB.js +105 -0
- package/dist/readiness-GW6ABRHB.js +139 -0
- package/dist/readiness-KFZCXDTT.js +215 -0
- package/dist/registry-7XG376SY.js +217 -0
- package/dist/repair-helpers-TTV5FEF4.js +117 -0
- package/dist/repo-root-YG6MQZDW.js +28 -0
- package/dist/resolution-engine-FFEK7HQ2.js +187 -0
- package/dist/resolve-provider-cli-ZEXVZKET.js +44 -0
- package/dist/restore-telemetry-db-E7BR5PO7.js +459 -0
- package/dist/routing-events-AW6SRZ6G.js +13 -0
- package/dist/routing-history-store-FMDDQL6W.js +39 -0
- package/dist/routing-planner-C6OHO3EY.js +221 -0
- package/dist/runner-KHDNQ5KU.js +1194 -0
- package/dist/runner-PIS2BW7Y.js +1801 -0
- package/dist/safety-E3JJVSFL.js +67 -0
- package/dist/safety-stanza-HE6GXP4G.js +49 -0
- package/dist/sandbox-7LBZC5PE.js +72 -0
- package/dist/sandbox-ASWCDYZL.js +80 -0
- package/dist/schema-extensions.sql +234 -0
- package/dist/security-JI22TBEB.js +17 -0
- package/dist/setup-bb-plugin-FQXLA76Y.js +531 -0
- package/dist/setup-claude-code-NF4ZSHVE.js +197 -0
- package/dist/setup-temp-cleanup-2APZ4HMA.js +29 -0
- package/dist/shared-utils-34HJZ6JX.js +40 -0
- package/dist/simple-artifact-goal-MQLDDMMX.js +29 -0
- package/dist/sprint-N7JFTEGL.js +403 -0
- package/dist/sprint-bridge-KJXIBNRJ.js +117 -0
- package/dist/sprint-completion-dependents-34SR7JVL.js +43 -0
- package/dist/sprint-execution-service-A6JPUBWY.js +376 -0
- package/dist/sprint-manager-MU2ZUWN2.js +140 -0
- package/dist/sqlite-timeout-config-U2CT4LKU.js +13 -0
- package/dist/sqlite-wrapper-FSGJ2QEW.js +19 -0
- package/dist/stale-cleanup-orchestrator-4X6324VF.js +71 -0
- package/dist/storage-access-I3V5LER7.js +31 -0
- package/dist/store-XVG64ZR2.js +25 -0
- package/dist/task-classifier-MN2DYE5R.js +558 -0
- package/dist/task-list-fast-7AT6LTTA.js +112 -0
- package/dist/task-manager-LHPBBRRE.js +136 -0
- package/dist/task-report-OLPAWBTK.js +64 -0
- package/dist/task-type-detector-6RG3YNZF.js +27 -0
- package/dist/task-type-resolver-EQHOUIHT.js +64 -0
- package/dist/telemetry-5VYSK772.js +212 -0
- package/dist/telemetry-health-tracker-4GFW2EMP.js +12 -0
- package/dist/telemetry-path-resolver-H3NFHHQY.js +34 -0
- package/dist/telemetry-reliability-URJDTNPZ.js +76 -0
- package/dist/transports-NREZQTJK.js +106 -0
- package/dist/unified-cost-tracker-5MTBJ2CL.js +59 -0
- package/dist/unified-state-cleanup-GGAXLHXF.js +292 -0
- package/dist/universal-cost-calculator-JTFEJVWK.js +70 -0
- package/dist/user-agents-QLIGPLP2.js +30 -0
- package/dist/variant-execution-I4ZZUAWY.js +319 -0
- package/dist/variant-opt-parser-JHCXKY2K.js +251 -0
- package/dist/variant-request-input-PL37N3AN.js +11 -0
- package/dist/variant-state-cleanup-KHMXRBZN.js +287 -0
- package/dist/variant-worker-COB5YTJX.js +4517 -0
- package/dist/variant-worker.js +26 -0
- package/dist/variants-XGDSXYG3.js +267 -0
- package/dist/verification-command-YSPU4VBH.js +66 -0
- package/dist/verify-command-timeout-DQUYH33Z.js +11 -0
- package/dist/verify-commands-LOHMOFKC.js +65 -0
- package/dist/warning-JSKSFKBT.js +13 -0
- package/dist/work-edge-K65NHVWY.js +50 -0
- package/dist/work-item-writer-KY5ZWZWW.js +30 -0
- package/dist/worker-JNXXEIT6.js +1504 -0
- package/dist/worker.js +21 -0
- package/dist/workspace-tree-identity-JO5PSKTW.js +52 -0
- package/dist/worktree-environment-WTVABAQW.js +64 -0
- package/dist/worktree-metrics-VZRQI26T.js +66 -0
- package/dist/writeback-TQFQBJNU.js +50 -0
- package/docs/CLI_REFERENCE.md +8287 -0
- package/docs/PROVIDER_SETUP.md +1737 -0
- package/docs/README.md +96 -0
- package/docs/TROUBLESHOOTING.md +2209 -0
- package/docs/getting-started/QUICK_START.md +260 -0
- package/docs/getting-started/README.md +198 -0
- package/docs/getting-started/SETUP.md +25 -0
- package/docs/reference/AGENT_CONTEXT.md +84 -0
- package/docs/reference/DELIVERY_ONBOARDING.md +198 -0
- package/docs/reference/PROVIDER_RELEASE_TIERS.md +34 -0
- package/docs/reference/SECURITY.md +291 -0
- package/docs/reference/TELEMETRY_PRIVACY.md +259 -0
- package/integrations/bb-plugin-build/README.md +391 -0
- package/integrations/bb-plugin-build/app.css +431 -0
- package/integrations/bb-plugin-build/app.tsx +6210 -0
- package/integrations/bb-plugin-build/package.json +45 -0
- package/integrations/bb-plugin-build/server.ts +488 -0
- package/integrations/bb-plugin-build/src/adapter.ts +3672 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-model.ts +150 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-schema.ts +458 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-service.ts +467 -0
- package/integrations/bb-plugin-build/src/backlog-authoring-view.ts +1025 -0
- package/integrations/bb-plugin-build/src/command-evidence-schema.ts +13 -0
- package/integrations/bb-plugin-build/src/competition-defaults-schema.ts +122 -0
- package/integrations/bb-plugin-build/src/competition-defaults-service.ts +105 -0
- package/integrations/bb-plugin-build/src/competition-defaults-view.ts +138 -0
- package/integrations/bb-plugin-build/src/contract.ts +718 -0
- package/integrations/bb-plugin-build/src/execution-activity-view.ts +98 -0
- package/integrations/bb-plugin-build/src/finalize-model.ts +205 -0
- package/integrations/bb-plugin-build/src/finalize-schema.ts +150 -0
- package/integrations/bb-plugin-build/src/finalize-service.ts +330 -0
- package/integrations/bb-plugin-build/src/finalize-view.ts +159 -0
- package/integrations/bb-plugin-build/src/judge-operations-readback.ts +303 -0
- package/integrations/bb-plugin-build/src/judge-operations-schema.ts +33 -0
- package/integrations/bb-plugin-build/src/judge-operations-view.ts +459 -0
- package/integrations/bb-plugin-build/src/model.ts +1318 -0
- package/integrations/bb-plugin-build/src/monitor-service.ts +233 -0
- package/integrations/bb-plugin-build/src/panel-read-ux.ts +97 -0
- package/integrations/bb-plugin-build/src/patch-preview-schema.ts +166 -0
- package/integrations/bb-plugin-build/src/patch-preview-service.ts +480 -0
- package/integrations/bb-plugin-build/src/patch-preview-view.ts +109 -0
- package/integrations/bb-plugin-build/src/planning-readback.ts +228 -0
- package/integrations/bb-plugin-build/src/planning-service.ts +647 -0
- package/integrations/bb-plugin-build/src/planning-sync-evidence.ts +37 -0
- package/integrations/bb-plugin-build/src/planning-workspace-readback.ts +33 -0
- package/integrations/bb-plugin-build/src/planning-workspace-schema.ts +131 -0
- package/integrations/bb-plugin-build/src/planning-workspace.ts +333 -0
- package/integrations/bb-plugin-build/src/poll-handoff.ts +136 -0
- package/integrations/bb-plugin-build/src/project-target-server.ts +21 -0
- package/integrations/bb-plugin-build/src/project-target.ts +45 -0
- package/integrations/bb-plugin-build/src/reference-index.ts +218 -0
- package/integrations/bb-plugin-build/src/repository-identity.ts +17 -0
- package/integrations/bb-plugin-build/src/repository-schema.ts +30 -0
- package/integrations/bb-plugin-build/src/result-explorer-view.ts +597 -0
- package/integrations/bb-plugin-build/src/result-readback-model.ts +163 -0
- package/integrations/bb-plugin-build/src/result-readback-schema.ts +319 -0
- package/integrations/bb-plugin-build/src/result-readback-service.ts +333 -0
- package/integrations/bb-plugin-build/src/run-page-view.ts +332 -0
- package/integrations/bb-plugin-build/src/setup-config-schema.ts +293 -0
- package/integrations/bb-plugin-build/src/setup-config-service.ts +620 -0
- package/integrations/bb-plugin-build/src/setup-config-view.ts +388 -0
- package/integrations/bb-plugin-build/src/sprint-close-model.ts +150 -0
- package/integrations/bb-plugin-build/src/sprint-close-schema.ts +128 -0
- package/integrations/bb-plugin-build/src/sprint-close-service.ts +293 -0
- package/integrations/bb-plugin-build/src/sprint-close-view.ts +116 -0
- package/integrations/bb-plugin-build/src/sprint-composition-model.ts +709 -0
- package/integrations/bb-plugin-build/src/sprint-composition-readback.ts +231 -0
- package/integrations/bb-plugin-build/src/sprint-composition-schema.ts +597 -0
- package/integrations/bb-plugin-build/src/sprint-composition-service.ts +773 -0
- package/integrations/bb-plugin-build/src/sprint-composition-view.ts +1221 -0
- package/integrations/bb-plugin-build/src/sprint-execution-schema.ts +188 -0
- package/integrations/bb-plugin-build/src/sprint-execution-service.ts +289 -0
- package/integrations/bb-plugin-build/src/sprint-execution-view.ts +54 -0
- package/integrations/bb-plugin-build/src/task-authoring-model.ts +563 -0
- package/integrations/bb-plugin-build/src/task-authoring-schema.ts +629 -0
- package/integrations/bb-plugin-build/src/task-authoring-service.ts +842 -0
- package/integrations/bb-plugin-build/src/task-authoring-view.ts +1227 -0
- package/integrations/bb-plugin-build/src/task-catalog-schema.ts +46 -0
- package/integrations/bb-plugin-build/src/task-catalog-service.ts +44 -0
- package/integrations/bb-plugin-build/src/task-detail-readback.ts +56 -0
- package/integrations/bb-plugin-build/src/task-detail-schema.ts +171 -0
- package/integrations/bb-plugin-build/src/task-navigation.ts +446 -0
- package/integrations/bb-plugin-build/src/workbench-route.ts +74 -0
- package/package.json +306 -4
- package/schemas/README.md +80 -0
- package/schemas/build.config.schema.json +2110 -0
- package/schemas/config-v1.schema.json +136 -0
- package/schemas/execution-config.schema.json +47 -0
- package/schemas/judge-scoring.schema.json +257 -0
- package/schemas/provider-config.schema.json +179 -0
- package/schemas/telemetry-config.schema.json +79 -0
- package/schemas/user-preferences.schema.json +141 -0
- package/scripts/assert-node-runtime.mjs +146 -0
- package/scripts/preflight-native.mjs +49 -0
- package/scripts/setup-git-hooks.mjs +39 -0
- package/scripts/sync.sh +2871 -0
- package/scripts/write-node-launcher.sh +138 -0
- package/src/resources/gemini/slash-packs/default/plan.toml +15 -0
- package/src/resources/gemini/slash-packs/default/summary.toml +16 -0
- package/src/resources/gemini/slash-packs/default/tests.toml +16 -0
- package/templates/.build-project/README.md +41 -0
- package/templates/.build-project/agents/README.md +50 -0
- package/templates/.build-project/agents/architect.md +10 -0
- package/templates/.build-project/agents/code-reviewer.md +10 -0
- package/templates/.build-project/agents/debugger.md +10 -0
- package/templates/.build-project/agents/documenter.md +10 -0
- package/templates/.build-project/agents/test-automator.md +10 -0
- package/templates/.build-project/config/build.config.jsonc +64 -0
- package/templates/.build-project/config/project-context.template.json +6 -0
- package/templates/.build-project/config/task-type-aliases.presets/kanban.yaml +14 -0
- package/templates/.build-project/config/task-type-aliases.presets/scrum.yaml +17 -0
- package/templates/.build-project/config/task-type-aliases.presets/xp.yaml +12 -0
- package/templates/.build-project/config/task-type-aliases.yaml +29 -0
- package/templates/.build-project/gitignore.template +70 -0
- package/templates/.build-project/task-types/analysis.yaml +38 -0
- package/templates/.build-project/task-types/architecture.yaml +35 -0
- package/templates/.build-project/task-types/doc.yaml +34 -0
- package/templates/.build-project/task-types/feature.yaml +22 -0
- package/templates/.build-project/task-types/general.yaml +4 -0
- package/templates/.build-project/task-types/security.yaml +38 -0
- package/templates/AGENTS.template.md +85 -0
- package/templates/CLAUDE.template.md +1 -0
- package/templates/GEMINI.template.md +1 -0
- package/templates/builtin-workflows/code-review.yaml +73 -0
- package/templates/builtin-workflows/skills/code-review/SKILL.md +61 -0
- package/templates/builtin-workflows/skills/execution-contract-analysis/SKILL.md +24 -0
- package/templates/builtin-workflows/skills/execution-contract-code/SKILL.md +19 -0
- package/templates/builtin-workflows/skills/execution-contract-structured/SKILL.md +17 -0
- package/templates/ci/incident-intake.mjs +787 -0
- package/templates/ci/incidents.yml.template +103 -0
- package/templates/ci/node.yml.template +17 -0
- package/templates/ci/python.yml.template +17 -0
- package/templates/guard-kit/GUARD_SETUP.md.template +258 -0
- package/templates/guard-kit/build-guard.yml.template +189 -0
- package/templates/guard-kit/check.mjs.template +932 -0
- package/templates/guard-kit/config.json.template +7 -0
- package/templates/guard-kit/policy.json.template +1 -0
- package/templates/profiles/README.md +57 -0
- package/templates/profiles/frontier-claude.json +23 -0
- package/templates/profiles/frontier-codex.json +23 -0
|
@@ -0,0 +1,4517 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
ProcessLifecycleManager,
|
|
4
|
+
createCliBreadcrumb,
|
|
5
|
+
evaluateExecutionSpec,
|
|
6
|
+
findGateControllingEdits,
|
|
7
|
+
findOffScopeConfigEdits,
|
|
8
|
+
formatGateConfigUnverifiedReason,
|
|
9
|
+
formatOffScopeConfigEdits
|
|
10
|
+
} from "./chunk-WMWQEFLN.js";
|
|
11
|
+
import {
|
|
12
|
+
isNativeResearchEnabledFor,
|
|
13
|
+
scanWebToolEvents
|
|
14
|
+
} from "./chunk-OSK7QWPD.js";
|
|
15
|
+
import {
|
|
16
|
+
hasValidationArtifactTarget
|
|
17
|
+
} from "./chunk-3PL22IYZ.js";
|
|
18
|
+
import "./chunk-OMX5VL43.js";
|
|
19
|
+
import {
|
|
20
|
+
classifyTask
|
|
21
|
+
} from "./chunk-JHP7OT6K.js";
|
|
22
|
+
import {
|
|
23
|
+
isValidationArtifactEntry
|
|
24
|
+
} from "./chunk-GOFVBTMN.js";
|
|
25
|
+
import {
|
|
26
|
+
WORKER_CLEANUP_BUDGET_MS,
|
|
27
|
+
estimateTimeoutCompletenessFromScope,
|
|
28
|
+
parseShortStatSummary,
|
|
29
|
+
resolveVariantEvaluation
|
|
30
|
+
} from "./chunk-DIKBRRKE.js";
|
|
31
|
+
import {
|
|
32
|
+
PROVIDER_ACTIVITY_WORKER_EVENT_PREFIX,
|
|
33
|
+
PROVIDER_OUTPUT_WORKER_EVENT_PREFIX,
|
|
34
|
+
ResultStreamer
|
|
35
|
+
} from "./chunk-WEW7UFWE.js";
|
|
36
|
+
import "./chunk-QILAQACE.js";
|
|
37
|
+
import {
|
|
38
|
+
ACTIVE_HANDLES_MARKER_PREFIX,
|
|
39
|
+
CLEANUP_COMPLETE_MARKER_PREFIX,
|
|
40
|
+
CLEANUP_START_MARKER_PREFIX,
|
|
41
|
+
POST_PROVIDER_PHASES_MARKER_PREFIX,
|
|
42
|
+
RESULT_WRITTEN_MARKER_PREFIX,
|
|
43
|
+
getOrCaptureVerificationBaseline,
|
|
44
|
+
withValidationSlot
|
|
45
|
+
} from "./chunk-3C7WBIVK.js";
|
|
46
|
+
import "./chunk-EKLUBDBQ.js";
|
|
47
|
+
import "./chunk-NEQUQHOX.js";
|
|
48
|
+
import "./chunk-DJG5RIOR.js";
|
|
49
|
+
import {
|
|
50
|
+
presentUntrustedRepairEvidence,
|
|
51
|
+
renderRepairConstraints,
|
|
52
|
+
renderUntrustedCommandOutput,
|
|
53
|
+
renderUntrustedFailingTests
|
|
54
|
+
} from "./chunk-QS5AGRZN.js";
|
|
55
|
+
import {
|
|
56
|
+
isTestRunProhibited
|
|
57
|
+
} from "./chunk-NMVZ5YES.js";
|
|
58
|
+
import "./chunk-N3Q2JNPV.js";
|
|
59
|
+
import "./chunk-H4HXVWEC.js";
|
|
60
|
+
import "./chunk-BZRLSZNV.js";
|
|
61
|
+
import "./chunk-C7AGUJ5K.js";
|
|
62
|
+
import "./chunk-VLMDM56Q.js";
|
|
63
|
+
import "./chunk-HBZ322WZ.js";
|
|
64
|
+
import {
|
|
65
|
+
computeProcessReliabilityMetrics,
|
|
66
|
+
generateBranchName,
|
|
67
|
+
getVerificationResult,
|
|
68
|
+
sanitizeVariantResult
|
|
69
|
+
} from "./chunk-DCACX7BV.js";
|
|
70
|
+
import "./chunk-EUWIIDQ7.js";
|
|
71
|
+
import "./chunk-ARARVIZK.js";
|
|
72
|
+
import "./chunk-HHWDTB6S.js";
|
|
73
|
+
import {
|
|
74
|
+
runDeterministicTestGateWithArtifacts,
|
|
75
|
+
runLintOnDiffWithArtifacts
|
|
76
|
+
} from "./chunk-JOSK6GEU.js";
|
|
77
|
+
import "./chunk-CFCVYHSW.js";
|
|
78
|
+
import "./chunk-VCGSWN5M.js";
|
|
79
|
+
import "./chunk-6CAU7VBA.js";
|
|
80
|
+
import "./chunk-IYY3HQAD.js";
|
|
81
|
+
import "./chunk-ONYPLIVE.js";
|
|
82
|
+
import "./chunk-PVJBTI7P.js";
|
|
83
|
+
import {
|
|
84
|
+
runTypecheckWithArtifacts
|
|
85
|
+
} from "./chunk-IWBHRSG7.js";
|
|
86
|
+
import "./chunk-UKMPASIC.js";
|
|
87
|
+
import "./chunk-CTDLERWU.js";
|
|
88
|
+
import "./chunk-LPQ7NZEN.js";
|
|
89
|
+
import "./chunk-TGCVL74Y.js";
|
|
90
|
+
import {
|
|
91
|
+
applyExactArtifactGoalSuccessGate,
|
|
92
|
+
isSimpleArtifactGoalSatisfied,
|
|
93
|
+
parseSimpleArtifactGoal
|
|
94
|
+
} from "./chunk-QXBKIHY7.js";
|
|
95
|
+
import "./chunk-Q3DHEZA5.js";
|
|
96
|
+
import "./chunk-QTOLTG4L.js";
|
|
97
|
+
import "./chunk-7HYBSRPZ.js";
|
|
98
|
+
import "./chunk-L32WD57F.js";
|
|
99
|
+
import "./chunk-6DBMALXL.js";
|
|
100
|
+
import "./chunk-MKASIKWZ.js";
|
|
101
|
+
import "./chunk-UDNNAQN5.js";
|
|
102
|
+
import "./chunk-GIJWHUWF.js";
|
|
103
|
+
import "./chunk-HKO45NLZ.js";
|
|
104
|
+
import {
|
|
105
|
+
extractChangedFilesMeta
|
|
106
|
+
} from "./chunk-QTCLJHLZ.js";
|
|
107
|
+
import "./chunk-SU6UFUQY.js";
|
|
108
|
+
import "./chunk-FSA5BPFI.js";
|
|
109
|
+
import {
|
|
110
|
+
parsePromptSpec
|
|
111
|
+
} from "./chunk-INJ3SS6A.js";
|
|
112
|
+
import "./chunk-LDJCKAJP.js";
|
|
113
|
+
import "./chunk-3LOY6YZI.js";
|
|
114
|
+
import "./chunk-HS3SY6HN.js";
|
|
115
|
+
import "./chunk-ENYRCL43.js";
|
|
116
|
+
import "./chunk-D37WY7SH.js";
|
|
117
|
+
import "./chunk-Z7CDQNH3.js";
|
|
118
|
+
import "./chunk-PRDHI2L5.js";
|
|
119
|
+
import "./chunk-URMSZXAD.js";
|
|
120
|
+
import "./chunk-JVHGMURQ.js";
|
|
121
|
+
import "./chunk-2YRG3LHZ.js";
|
|
122
|
+
import "./chunk-JYXS47FQ.js";
|
|
123
|
+
import "./chunk-B7PYTV72.js";
|
|
124
|
+
import "./chunk-4QKP6I4C.js";
|
|
125
|
+
import "./chunk-GWHGTG37.js";
|
|
126
|
+
import {
|
|
127
|
+
FIELD_DISPLAY_NAMES,
|
|
128
|
+
generateSpecQualitySignals
|
|
129
|
+
} from "./chunk-UAPHFJPN.js";
|
|
130
|
+
import "./chunk-GI5M67YA.js";
|
|
131
|
+
import "./chunk-XON4ZCYX.js";
|
|
132
|
+
import "./chunk-PPHXX4W7.js";
|
|
133
|
+
import "./chunk-5AW4RMNG.js";
|
|
134
|
+
import {
|
|
135
|
+
ExitCodeError
|
|
136
|
+
} from "./chunk-Y4ZDDHB6.js";
|
|
137
|
+
import "./chunk-BIUV23HH.js";
|
|
138
|
+
import "./chunk-RI4EX2QE.js";
|
|
139
|
+
import "./chunk-ETA2K4KI.js";
|
|
140
|
+
import "./chunk-CRPRVNQ4.js";
|
|
141
|
+
import "./chunk-W4BAIQ6L.js";
|
|
142
|
+
import "./chunk-4UMRUFBD.js";
|
|
143
|
+
import "./chunk-3M57DADF.js";
|
|
144
|
+
import "./chunk-HT5OCGKN.js";
|
|
145
|
+
import "./chunk-FUUVZZUB.js";
|
|
146
|
+
import "./chunk-32IYXIE5.js";
|
|
147
|
+
import "./chunk-7RMARDVH.js";
|
|
148
|
+
import "./chunk-HXIIEMGF.js";
|
|
149
|
+
import {
|
|
150
|
+
stageCommitEligibleChanges
|
|
151
|
+
} from "./chunk-ZMLOC3DW.js";
|
|
152
|
+
import "./chunk-FIEUSGNJ.js";
|
|
153
|
+
import "./chunk-WEIUS5NT.js";
|
|
154
|
+
import "./chunk-KQYQOXMS.js";
|
|
155
|
+
import "./chunk-S47ISY27.js";
|
|
156
|
+
import "./chunk-U424MJLC.js";
|
|
157
|
+
import "./chunk-YKMXFVK7.js";
|
|
158
|
+
import "./chunk-O6ZQUVTR.js";
|
|
159
|
+
import "./chunk-RCYEJSD5.js";
|
|
160
|
+
import "./chunk-FAWJTC34.js";
|
|
161
|
+
import "./chunk-UBVRHGAZ.js";
|
|
162
|
+
import "./chunk-5HDP7XZG.js";
|
|
163
|
+
import "./chunk-H3ISZJMJ.js";
|
|
164
|
+
import "./chunk-ATKEKM3E.js";
|
|
165
|
+
import "./chunk-S6I4KISU.js";
|
|
166
|
+
import "./chunk-EJIWTPSZ.js";
|
|
167
|
+
import "./chunk-MYLH2S4G.js";
|
|
168
|
+
import "./chunk-3P5IN5E7.js";
|
|
169
|
+
import "./chunk-2Y2IIHPI.js";
|
|
170
|
+
import "./chunk-WEI3ZDZH.js";
|
|
171
|
+
import "./chunk-H6MJGKAW.js";
|
|
172
|
+
import "./chunk-67YRJPJO.js";
|
|
173
|
+
import {
|
|
174
|
+
setProcessLifecycleManager
|
|
175
|
+
} from "./chunk-633XZ7UF.js";
|
|
176
|
+
import "./chunk-SF6YK3JI.js";
|
|
177
|
+
import "./chunk-JBBB2Y47.js";
|
|
178
|
+
import "./chunk-WJAL4JL3.js";
|
|
179
|
+
import "./chunk-RBKLOUXA.js";
|
|
180
|
+
import "./chunk-4EBSXZ73.js";
|
|
181
|
+
import "./chunk-CSOPSYDY.js";
|
|
182
|
+
import "./chunk-VMFZEVVX.js";
|
|
183
|
+
import "./chunk-U5KS22IN.js";
|
|
184
|
+
import "./chunk-UUUDKV7N.js";
|
|
185
|
+
import {
|
|
186
|
+
TelemetryLogger
|
|
187
|
+
} from "./chunk-PFCXFO7A.js";
|
|
188
|
+
import "./chunk-SDPIFNLD.js";
|
|
189
|
+
import "./chunk-GO6DRECI.js";
|
|
190
|
+
import "./chunk-XIBVFMLQ.js";
|
|
191
|
+
import "./chunk-QWMQ74KD.js";
|
|
192
|
+
import "./chunk-GAFJEYLK.js";
|
|
193
|
+
import "./chunk-YKC75XK4.js";
|
|
194
|
+
import "./chunk-T7E5JS6N.js";
|
|
195
|
+
import "./chunk-ZJB6SQPA.js";
|
|
196
|
+
import "./chunk-GZK62I3N.js";
|
|
197
|
+
import {
|
|
198
|
+
signalCoordinator
|
|
199
|
+
} from "./chunk-GC4CRMTS.js";
|
|
200
|
+
import "./chunk-P7CQGPLS.js";
|
|
201
|
+
import "./chunk-C3DH3NCY.js";
|
|
202
|
+
import "./chunk-IVZEONFC.js";
|
|
203
|
+
import "./chunk-XTXYIISX.js";
|
|
204
|
+
import "./chunk-MEMSY6VY.js";
|
|
205
|
+
import "./chunk-VBNCKCCI.js";
|
|
206
|
+
import "./chunk-33IGPPUI.js";
|
|
207
|
+
import "./chunk-UPHU64NB.js";
|
|
208
|
+
import "./chunk-JLS65NUQ.js";
|
|
209
|
+
import "./chunk-FRBPKBFE.js";
|
|
210
|
+
import "./chunk-BAWJNMWF.js";
|
|
211
|
+
import "./chunk-LX4F24ZH.js";
|
|
212
|
+
import "./chunk-BBPIILO2.js";
|
|
213
|
+
import "./chunk-2NMNFUQA.js";
|
|
214
|
+
import "./chunk-I7SCBHJN.js";
|
|
215
|
+
import {
|
|
216
|
+
formatVariantIdentifier
|
|
217
|
+
} from "./chunk-TGXXYDL4.js";
|
|
218
|
+
import "./chunk-D2M2Y5ID.js";
|
|
219
|
+
import "./chunk-UJP3XM54.js";
|
|
220
|
+
import "./chunk-MULMXX4V.js";
|
|
221
|
+
import {
|
|
222
|
+
parseGitWorktreeBranches
|
|
223
|
+
} from "./chunk-UTDEOPKV.js";
|
|
224
|
+
import "./chunk-RFDVSI4N.js";
|
|
225
|
+
import "./chunk-M4BUEXXF.js";
|
|
226
|
+
import "./chunk-ZH4G3FU5.js";
|
|
227
|
+
import "./chunk-726G6WDL.js";
|
|
228
|
+
import {
|
|
229
|
+
captureWorktreeDiff,
|
|
230
|
+
normalizeWorktreeDiff
|
|
231
|
+
} from "./chunk-EP22ZMFO.js";
|
|
232
|
+
import {
|
|
233
|
+
getGitChangeSummary
|
|
234
|
+
} from "./chunk-5UBIFVRD.js";
|
|
235
|
+
import "./chunk-O7FE75QZ.js";
|
|
236
|
+
import "./chunk-Q64IR2Z2.js";
|
|
237
|
+
import "./chunk-YVCMDVBB.js";
|
|
238
|
+
import "./chunk-7HPCIOMA.js";
|
|
239
|
+
import {
|
|
240
|
+
parseJsonWithContext
|
|
241
|
+
} from "./chunk-P6SSWAKY.js";
|
|
242
|
+
import "./chunk-ETAD4NKD.js";
|
|
243
|
+
import "./chunk-XQPEWJPI.js";
|
|
244
|
+
import "./chunk-7YAOUUKK.js";
|
|
245
|
+
import {
|
|
246
|
+
analyzeRepairScope,
|
|
247
|
+
buildRepairDiffSnapshot,
|
|
248
|
+
resolveRepairArtifactPolicy
|
|
249
|
+
} from "./chunk-SKTFRFQ7.js";
|
|
250
|
+
import "./chunk-O5X6BKAO.js";
|
|
251
|
+
import "./chunk-R2CKS6WT.js";
|
|
252
|
+
import {
|
|
253
|
+
applyKnownFailureDowngrade,
|
|
254
|
+
compareVerificationOutcomeToBaseline,
|
|
255
|
+
createLogger,
|
|
256
|
+
deriveVariantOutcome,
|
|
257
|
+
detectGateRegressions,
|
|
258
|
+
hasUnavailableVerificationGateEvidence,
|
|
259
|
+
hasVerifiedTouchedTestFailure,
|
|
260
|
+
latestVerificationIteration,
|
|
261
|
+
markLintIdenticalToBase,
|
|
262
|
+
persistingIssuesOnBase,
|
|
263
|
+
repairOutcomeCanSupersedeRawTouchedFailure,
|
|
264
|
+
stableBaselineProvesClean,
|
|
265
|
+
stableComparisonToBaselineComparison,
|
|
266
|
+
trustedLoopBaselineComparison,
|
|
267
|
+
verificationOutcomesFromGateArtifacts
|
|
268
|
+
} from "./chunk-3OPP2OZB.js";
|
|
269
|
+
import "./chunk-EPRMH6HO.js";
|
|
270
|
+
import "./chunk-XHOI4V62.js";
|
|
271
|
+
import "./chunk-S2VQCZO4.js";
|
|
272
|
+
import {
|
|
273
|
+
dominantVerificationStatus
|
|
274
|
+
} from "./chunk-6TBEX4R4.js";
|
|
275
|
+
import "./chunk-PXKERBKX.js";
|
|
276
|
+
import "./chunk-QYQOVOLR.js";
|
|
277
|
+
import "./chunk-QFLBRWCJ.js";
|
|
278
|
+
import "./chunk-AEQWV746.js";
|
|
279
|
+
import "./chunk-SVUNZOPG.js";
|
|
280
|
+
import "./chunk-KUEWV2JH.js";
|
|
281
|
+
import "./chunk-R2TIYG7U.js";
|
|
282
|
+
import "./chunk-5FKHIKTS.js";
|
|
283
|
+
import "./chunk-S2E632FQ.js";
|
|
284
|
+
import "./chunk-XJ2E7YEJ.js";
|
|
285
|
+
import "./chunk-B7YRSULE.js";
|
|
286
|
+
import "./chunk-4U6XK2DG.js";
|
|
287
|
+
import "./chunk-BY6ODHNZ.js";
|
|
288
|
+
import "./chunk-3H4MWYIC.js";
|
|
289
|
+
import "./chunk-DP7FKP6W.js";
|
|
290
|
+
import "./chunk-VIFHYTI5.js";
|
|
291
|
+
import "./chunk-XGU6PBO5.js";
|
|
292
|
+
import "./chunk-F55YABKV.js";
|
|
293
|
+
import {
|
|
294
|
+
captureCurrentWorkspaceTreeIdentity
|
|
295
|
+
} from "./chunk-YO5BAJ55.js";
|
|
296
|
+
import "./chunk-EVE3OD2W.js";
|
|
297
|
+
import {
|
|
298
|
+
mkBuildTempDir
|
|
299
|
+
} from "./chunk-D6WTZMZB.js";
|
|
300
|
+
import "./chunk-5EQN56OZ.js";
|
|
301
|
+
import "./chunk-RBBTBCHX.js";
|
|
302
|
+
import "./chunk-4DJL2VXA.js";
|
|
303
|
+
import {
|
|
304
|
+
channelsFailingIdenticallyOnBase,
|
|
305
|
+
requiredVerificationControlChannels,
|
|
306
|
+
resolveVerificationChannelScope,
|
|
307
|
+
selectScopedTestCommands
|
|
308
|
+
} from "./chunk-AHU3FKPA.js";
|
|
309
|
+
import "./chunk-TUQMMFEQ.js";
|
|
310
|
+
import {
|
|
311
|
+
describeVerificationCommand
|
|
312
|
+
} from "./chunk-EPAMVGI3.js";
|
|
313
|
+
import "./chunk-6CB3KJRY.js";
|
|
314
|
+
import "./chunk-L2S235CU.js";
|
|
315
|
+
import "./chunk-D4VJFCGZ.js";
|
|
316
|
+
import {
|
|
317
|
+
PROJECT_SOURCE_EXTENSIONS,
|
|
318
|
+
isProjectSourcePath
|
|
319
|
+
} from "./chunk-RPIMAMV7.js";
|
|
320
|
+
import "./chunk-ALDTFNZ3.js";
|
|
321
|
+
import "./chunk-QOIASNEJ.js";
|
|
322
|
+
import {
|
|
323
|
+
resolveVerifyCommandTimeoutMs
|
|
324
|
+
} from "./chunk-2VUEDIUM.js";
|
|
325
|
+
import "./chunk-MJ6WNSIX.js";
|
|
326
|
+
import "./chunk-5KQETTNS.js";
|
|
327
|
+
import "./chunk-J2ICMYDA.js";
|
|
328
|
+
import {
|
|
329
|
+
truncateToByteLimit
|
|
330
|
+
} from "./chunk-2VLJUXJU.js";
|
|
331
|
+
import "./chunk-PPZESPJT.js";
|
|
332
|
+
import "./chunk-XCOIBNET.js";
|
|
333
|
+
import "./chunk-Y733RB7T.js";
|
|
334
|
+
import "./chunk-H3HGMISG.js";
|
|
335
|
+
import "./chunk-FAZBRHGG.js";
|
|
336
|
+
import "./chunk-CJBSZX3F.js";
|
|
337
|
+
import {
|
|
338
|
+
initializeLabModeFromEnv
|
|
339
|
+
} from "./chunk-C3KCMAJP.js";
|
|
340
|
+
import "./chunk-FKCG7K6H.js";
|
|
341
|
+
import "./chunk-AJN5Y5YZ.js";
|
|
342
|
+
import {
|
|
343
|
+
execGit
|
|
344
|
+
} from "./chunk-MHLEZQQI.js";
|
|
345
|
+
import "./chunk-HEP3ILP4.js";
|
|
346
|
+
import "./chunk-BQJOGAWT.js";
|
|
347
|
+
import "./chunk-WV3KQDPE.js";
|
|
348
|
+
import {
|
|
349
|
+
isSensitiveKey,
|
|
350
|
+
sanitizeSynthesisArtifactForPersistence,
|
|
351
|
+
sanitizeSynthesisStringForPersistence
|
|
352
|
+
} from "./chunk-VSWC5ZXI.js";
|
|
353
|
+
import "./chunk-XV7NZL4B.js";
|
|
354
|
+
import "./chunk-KKQKWAMF.js";
|
|
355
|
+
import "./chunk-RWQQIKIC.js";
|
|
356
|
+
import "./chunk-GVJGSSKM.js";
|
|
357
|
+
import "./chunk-QJ6OEH4K.js";
|
|
358
|
+
import "./chunk-BD3PGW6A.js";
|
|
359
|
+
import "./chunk-43OKHTW4.js";
|
|
360
|
+
import "./chunk-MNOVBYFU.js";
|
|
361
|
+
import "./chunk-QXBKTJT6.js";
|
|
362
|
+
import "./chunk-4BTDUT2S.js";
|
|
363
|
+
import "./chunk-35J4NE6S.js";
|
|
364
|
+
import "./chunk-RIMARPCF.js";
|
|
365
|
+
import "./chunk-HGFXODKO.js";
|
|
366
|
+
import "./chunk-N5DX4JAK.js";
|
|
367
|
+
import "./chunk-V2TVPO3X.js";
|
|
368
|
+
import {
|
|
369
|
+
ProviderExecutionError
|
|
370
|
+
} from "./chunk-RRC5BL7B.js";
|
|
371
|
+
import "./chunk-352PCJLK.js";
|
|
372
|
+
import "./chunk-RCP2Y6DQ.js";
|
|
373
|
+
import {
|
|
374
|
+
HostTimeoutError,
|
|
375
|
+
VARIANT_WORKER_SIGTERM_SOURCE,
|
|
376
|
+
classifyHostTermination,
|
|
377
|
+
createHostTerminationParsedError,
|
|
378
|
+
hostTerminationMetadataFromUnknown
|
|
379
|
+
} from "./chunk-M6UHOC4I.js";
|
|
380
|
+
import "./chunk-PSTKWHHN.js";
|
|
381
|
+
import "./chunk-KTMW65UX.js";
|
|
382
|
+
import "./chunk-3JKNRYEE.js";
|
|
383
|
+
import "./chunk-MT2WAIR3.js";
|
|
384
|
+
import "./chunk-WGQAXLNA.js";
|
|
385
|
+
import "./chunk-ONHAMBN6.js";
|
|
386
|
+
import "./chunk-XSJTIFUY.js";
|
|
387
|
+
import "./chunk-XWIZ6B5S.js";
|
|
388
|
+
import "./chunk-U4DRS6ZW.js";
|
|
389
|
+
import {
|
|
390
|
+
DEFAULT_PER_VARIANT_TIMEOUT_MINUTES,
|
|
391
|
+
DEFAULT_REPAIR_LOOP_ITERATION_TIMEOUT_MS,
|
|
392
|
+
DEFAULT_REPAIR_LOOP_MAX_ITERATIONS
|
|
393
|
+
} from "./chunk-I56TH7VM.js";
|
|
394
|
+
import {
|
|
395
|
+
getWorktreeBaseDir
|
|
396
|
+
} from "./chunk-Z5WGNULV.js";
|
|
397
|
+
import "./chunk-O26L6F7F.js";
|
|
398
|
+
import "./chunk-RJ6RSNT6.js";
|
|
399
|
+
import {
|
|
400
|
+
assertBuildRuntimeReady
|
|
401
|
+
} from "./chunk-ZORW64QH.js";
|
|
402
|
+
import "./chunk-TAJPCXSB.js";
|
|
403
|
+
import "./chunk-56WPYDJH.js";
|
|
404
|
+
import "./chunk-RSYTSBOO.js";
|
|
405
|
+
import "./chunk-STW7OFOA.js";
|
|
406
|
+
import {
|
|
407
|
+
resolveBuildRoot
|
|
408
|
+
} from "./chunk-CHA2HDE5.js";
|
|
409
|
+
import "./chunk-JNMWYMC6.js";
|
|
410
|
+
import {
|
|
411
|
+
BUILD_ALLOW_OFF_SCOPE_REPAIR,
|
|
412
|
+
BUILD_COMPLETION_MODE,
|
|
413
|
+
BUILD_TELEMETRY_DIR,
|
|
414
|
+
createExecutionContext,
|
|
415
|
+
createWarning,
|
|
416
|
+
emitWarning,
|
|
417
|
+
getRuntimeEnv,
|
|
418
|
+
mergeExecutionContextEnvOverrides,
|
|
419
|
+
readEnv,
|
|
420
|
+
resolveBuildDirFromContext,
|
|
421
|
+
resolveCliRepoRoot,
|
|
422
|
+
setExecutionContext
|
|
423
|
+
} from "./chunk-7VRJHTGZ.js";
|
|
424
|
+
import "./chunk-XDM65LBX.js";
|
|
425
|
+
import {
|
|
426
|
+
sanitizeError
|
|
427
|
+
} from "./chunk-CPSVQR2I.js";
|
|
428
|
+
import "./chunk-PZ5AY32C.js";
|
|
429
|
+
|
|
430
|
+
// src/cli/variant-worker.ts
|
|
431
|
+
import { randomBytes } from "crypto";
|
|
432
|
+
import { closeSync, fsyncSync, openSync } from "fs";
|
|
433
|
+
import * as fs2 from "fs/promises";
|
|
434
|
+
import * as path2 from "path";
|
|
435
|
+
import { fileURLToPath } from "url";
|
|
436
|
+
import chalk from "chalk";
|
|
437
|
+
import { Command } from "commander";
|
|
438
|
+
import { execa as execa3 } from "execa";
|
|
439
|
+
|
|
440
|
+
// src/core/verification/known-pre-existing-failures.ts
|
|
441
|
+
import * as fs from "fs/promises";
|
|
442
|
+
import * as path from "path";
|
|
443
|
+
var logger = createLogger("KnownPreExistingFailures");
|
|
444
|
+
var MIN_ERROR_FINGERPRINT_LENGTH = 4;
|
|
445
|
+
var EMPTY_REGISTRY = Object.freeze({
|
|
446
|
+
schemaVersion: 1,
|
|
447
|
+
generatedAt: "",
|
|
448
|
+
baseCommit: "",
|
|
449
|
+
failures: []
|
|
450
|
+
});
|
|
451
|
+
var _cachedRegistry;
|
|
452
|
+
async function loadKnownPreExistingFailures(repoRoot = process.cwd()) {
|
|
453
|
+
if (_cachedRegistry !== void 0) {
|
|
454
|
+
return _cachedRegistry;
|
|
455
|
+
}
|
|
456
|
+
const registryPath = process.env.BUILD_KNOWN_FAILURES_PATH ?? path.join(repoRoot, ".build-project", "verification", "known-pre-existing-failures.json");
|
|
457
|
+
let raw;
|
|
458
|
+
try {
|
|
459
|
+
raw = await fs.readFile(registryPath, "utf-8");
|
|
460
|
+
} catch (err) {
|
|
461
|
+
const isNotFound = err instanceof Error && "code" in err && err.code === "ENOENT";
|
|
462
|
+
if (!isNotFound) {
|
|
463
|
+
logger.warn("Failed to read known-pre-existing-failures registry", {
|
|
464
|
+
registryPath,
|
|
465
|
+
error: err instanceof Error ? err.message : String(err)
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
_cachedRegistry = { ...EMPTY_REGISTRY };
|
|
469
|
+
return _cachedRegistry;
|
|
470
|
+
}
|
|
471
|
+
try {
|
|
472
|
+
const parsed = JSON.parse(raw);
|
|
473
|
+
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.failures)) {
|
|
474
|
+
throw new Error("Registry JSON does not match expected shape");
|
|
475
|
+
}
|
|
476
|
+
const registry = parsed;
|
|
477
|
+
const validFailures = registry.failures.filter(isValidKnownFailureEntry);
|
|
478
|
+
if (validFailures.length !== registry.failures.length) {
|
|
479
|
+
logger.warn("Ignoring invalid known-pre-existing-failures registry entries", {
|
|
480
|
+
registryPath,
|
|
481
|
+
ignoredCount: registry.failures.length - validFailures.length
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
_cachedRegistry = {
|
|
485
|
+
...registry,
|
|
486
|
+
failures: validFailures
|
|
487
|
+
};
|
|
488
|
+
logger.debug("Loaded known-pre-existing-failures registry", {
|
|
489
|
+
registryPath,
|
|
490
|
+
entryCount: _cachedRegistry.failures.length,
|
|
491
|
+
baseCommit: _cachedRegistry.baseCommit
|
|
492
|
+
});
|
|
493
|
+
return _cachedRegistry;
|
|
494
|
+
} catch (err) {
|
|
495
|
+
logger.warn("Malformed known-pre-existing-failures registry \u2014 treating as empty", {
|
|
496
|
+
registryPath,
|
|
497
|
+
error: err instanceof Error ? err.message : String(err)
|
|
498
|
+
});
|
|
499
|
+
_cachedRegistry = { ...EMPTY_REGISTRY };
|
|
500
|
+
return _cachedRegistry;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
function isValidKnownFailureEntry(entry) {
|
|
504
|
+
return normalizeFingerprint(entry.errorFingerprint) !== null;
|
|
505
|
+
}
|
|
506
|
+
function normalizeFingerprint(fingerprint) {
|
|
507
|
+
const normalized = fingerprint?.trim();
|
|
508
|
+
if (!normalized || normalized.length < MIN_ERROR_FINGERPRINT_LENGTH) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
return normalized.toLowerCase();
|
|
512
|
+
}
|
|
513
|
+
function detectRuntimeEnvironment() {
|
|
514
|
+
if (process.env.CLAUDECODE === "1" || process.env.CLAUDE_CODE === "true" || process.env.AI_SDK === "claude-code") {
|
|
515
|
+
return "claude-code";
|
|
516
|
+
}
|
|
517
|
+
if (process.env.CI === "true" || process.env.CI === "1" || process.env.GITHUB_ACTIONS === "true" || process.env.CIRCLECI === "true" || process.env.TRAVIS === "true") {
|
|
518
|
+
return "ci";
|
|
519
|
+
}
|
|
520
|
+
return "local";
|
|
521
|
+
}
|
|
522
|
+
function looksLikeProjectFileToken(token) {
|
|
523
|
+
const trimmed = token.trim();
|
|
524
|
+
if (trimmed.length === 0) return false;
|
|
525
|
+
if (isProjectSourcePath(trimmed)) return true;
|
|
526
|
+
return (trimmed.includes("/") || trimmed.includes("\\")) && !/\s/.test(trimmed);
|
|
527
|
+
}
|
|
528
|
+
function parseLiveFailure(raw) {
|
|
529
|
+
const trimmed = raw.trim();
|
|
530
|
+
for (const separator of [/\s*>\s*/, /::/]) {
|
|
531
|
+
const match = separator.exec(trimmed);
|
|
532
|
+
if (!match || match.index <= 0) continue;
|
|
533
|
+
const head = trimmed.slice(0, match.index).trim();
|
|
534
|
+
const tail = trimmed.slice(match.index + match[0].length).trim();
|
|
535
|
+
if (looksLikeProjectFileToken(head) && tail.length > 0) {
|
|
536
|
+
return { file: head, testPath: tail, raw };
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
const goMatch = /^(.+?):(\d+)$/.exec(trimmed);
|
|
540
|
+
if (goMatch && looksLikeProjectFileToken(goMatch[1] ?? "")) {
|
|
541
|
+
return { file: (goMatch[1] ?? "").trim(), testPath: trimmed, raw };
|
|
542
|
+
}
|
|
543
|
+
return { file: "", testPath: trimmed, raw };
|
|
544
|
+
}
|
|
545
|
+
function escapeRegExp(value) {
|
|
546
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
547
|
+
}
|
|
548
|
+
var VITEST_FAILURE_MARKER_SOURCE = "(?:\\s*(?:FAIL|FAILURES?|FAILED)\\s+)?[^\\n]*\\.(?:test|spec)\\.[jt]sx?\\s*>\\s+";
|
|
549
|
+
var GENERIC_FAILURE_MARKER_SOURCE = "(?:\\s*(?:FAIL|FAILURES?|FAILED)\\s+)?[^\\n\\s]*[\\\\/][^\\n\\s]*(?:::|\\s>\\s)";
|
|
550
|
+
function findNextFailureMarker(output, startIndex) {
|
|
551
|
+
const rest = output.slice(startIndex + 1);
|
|
552
|
+
const match = new RegExp(
|
|
553
|
+
`\\n(?:${VITEST_FAILURE_MARKER_SOURCE}|${GENERIC_FAILURE_MARKER_SOURCE})`,
|
|
554
|
+
"i"
|
|
555
|
+
).exec(rest);
|
|
556
|
+
return match ? startIndex + 1 + match.index : output.length;
|
|
557
|
+
}
|
|
558
|
+
function hasFailureMarkers(output) {
|
|
559
|
+
return new RegExp(
|
|
560
|
+
`(?:^|\\n)(?:${VITEST_FAILURE_MARKER_SOURCE}|${GENERIC_FAILURE_MARKER_SOURCE})`,
|
|
561
|
+
"i"
|
|
562
|
+
).test(output);
|
|
563
|
+
}
|
|
564
|
+
function extractScopedFailureOutput(live, testOutputTail) {
|
|
565
|
+
const rawNeedle = live.raw.trim();
|
|
566
|
+
const candidates = [
|
|
567
|
+
rawNeedle,
|
|
568
|
+
live.file && live.testPath ? `${live.file} > ${live.testPath}` : "",
|
|
569
|
+
live.testPath
|
|
570
|
+
].filter((candidate) => candidate.length > 0);
|
|
571
|
+
for (const candidate of candidates) {
|
|
572
|
+
const markerPattern = new RegExp(
|
|
573
|
+
`(?:^|\\n)[^\\n]*${escapeRegExp(candidate)}(?:\\s+\\([^\\n)]*\\))?\\s*(?=\\n|$)`,
|
|
574
|
+
"i"
|
|
575
|
+
);
|
|
576
|
+
const marker = markerPattern.exec(testOutputTail);
|
|
577
|
+
if (!marker || marker.index === void 0) continue;
|
|
578
|
+
const start = marker.index;
|
|
579
|
+
const end = findNextFailureMarker(testOutputTail, start);
|
|
580
|
+
return testOutputTail.slice(start, end);
|
|
581
|
+
}
|
|
582
|
+
return null;
|
|
583
|
+
}
|
|
584
|
+
function matchesRegisteredFailure(live, entry, testOutputTail, runtimeEnvironment = detectRuntimeEnvironment()) {
|
|
585
|
+
if (live.file !== entry.file) {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
const normalizedPath = live.testPath.trim();
|
|
589
|
+
const normalizedName = entry.testName.trim();
|
|
590
|
+
if (!normalizedPath.startsWith(normalizedName)) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
const fingerprint = normalizeFingerprint(entry.errorFingerprint);
|
|
594
|
+
if (!fingerprint) {
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
const scopedOutput = extractScopedFailureOutput(live, testOutputTail);
|
|
598
|
+
if (!scopedOutput && hasFailureMarkers(testOutputTail)) {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
const searchableOutput = scopedOutput ?? testOutputTail;
|
|
602
|
+
if (!searchableOutput.toLowerCase().includes(fingerprint)) return false;
|
|
603
|
+
if (entry.environments !== void 0 && entry.environments.length > 0) {
|
|
604
|
+
if (!entry.environments.includes(runtimeEnvironment)) {
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
function allFailuresAreRegistered(liveFailures, registry, testOutputTail, runtimeEnvironment = detectRuntimeEnvironment()) {
|
|
611
|
+
if (liveFailures.length === 0) return true;
|
|
612
|
+
if (registry.failures.length === 0) return false;
|
|
613
|
+
for (const raw of liveFailures) {
|
|
614
|
+
const live = parseLiveFailure(raw);
|
|
615
|
+
const matched = registry.failures.some(
|
|
616
|
+
(entry) => matchesRegisteredFailure(live, entry, testOutputTail, runtimeEnvironment)
|
|
617
|
+
);
|
|
618
|
+
if (!matched) {
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return true;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
// src/orchestrator/verification/verification-loop.ts
|
|
626
|
+
import { execa as execa2 } from "execa";
|
|
627
|
+
|
|
628
|
+
// src/core/verification/repair-artifacts.ts
|
|
629
|
+
import { mkdir, readFile as readFile2, rm, writeFile } from "fs/promises";
|
|
630
|
+
import { dirname, join as join2 } from "path";
|
|
631
|
+
import { execa } from "execa";
|
|
632
|
+
var DEFAULT_CONFIG = {
|
|
633
|
+
modes: ["patch"],
|
|
634
|
+
patchDir: ".build-project/repair-patches",
|
|
635
|
+
refNamespace: "refs/build/repair",
|
|
636
|
+
branchPrefix: "build/repair"
|
|
637
|
+
};
|
|
638
|
+
function sanitizeForPathSegment(input) {
|
|
639
|
+
const trimmed = input.trim();
|
|
640
|
+
if (!trimmed) return "unknown";
|
|
641
|
+
return trimmed.replace(/[^a-zA-Z0-9._-]+/g, "_").slice(0, 200);
|
|
642
|
+
}
|
|
643
|
+
function sanitizeForGitRefSegment(input) {
|
|
644
|
+
const trimmed = input.trim();
|
|
645
|
+
if (!trimmed) return "unknown";
|
|
646
|
+
const safe = trimmed.replace(/[\s~^:?*[\\]+/g, "_").replace(/\.\.+/g, ".").replace(/^\.*/, "").replace(/\/+/g, "/");
|
|
647
|
+
return (safe || "unknown").slice(0, 200);
|
|
648
|
+
}
|
|
649
|
+
async function generatePatch(worktreePath, baseRef) {
|
|
650
|
+
const scratch = await mkBuildTempDir("repair-patch");
|
|
651
|
+
const indexPath = join2(scratch, "index");
|
|
652
|
+
const env = { ...process.env, GIT_INDEX_FILE: indexPath };
|
|
653
|
+
try {
|
|
654
|
+
const readTree = await execa("git", ["read-tree", baseRef], {
|
|
655
|
+
cwd: worktreePath,
|
|
656
|
+
env,
|
|
657
|
+
stdio: "pipe",
|
|
658
|
+
reject: false
|
|
659
|
+
});
|
|
660
|
+
if (readTree.exitCode !== 0) {
|
|
661
|
+
throw new Error(`Failed to read base tree: ${readTree.stderr}`);
|
|
662
|
+
}
|
|
663
|
+
const add = await execa("git", ["add", "-A"], {
|
|
664
|
+
cwd: worktreePath,
|
|
665
|
+
env,
|
|
666
|
+
stdio: "pipe",
|
|
667
|
+
reject: false
|
|
668
|
+
});
|
|
669
|
+
if (add.exitCode !== 0) {
|
|
670
|
+
throw new Error(`Failed to stage worktree changes: ${add.stderr}`);
|
|
671
|
+
}
|
|
672
|
+
const diff = await execa("git", ["diff", "--cached", "--binary", baseRef, "--"], {
|
|
673
|
+
cwd: worktreePath,
|
|
674
|
+
env,
|
|
675
|
+
stdio: "pipe",
|
|
676
|
+
reject: false,
|
|
677
|
+
stripFinalNewline: false
|
|
678
|
+
});
|
|
679
|
+
if (diff.exitCode !== 0) {
|
|
680
|
+
throw new Error(`Failed to generate patch: ${diff.stderr}`);
|
|
681
|
+
}
|
|
682
|
+
return diff.stdout;
|
|
683
|
+
} finally {
|
|
684
|
+
await rm(scratch, { recursive: true, force: true });
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
async function createCommitFromPatch(opts) {
|
|
688
|
+
const scratch = await mkBuildTempDir("repair");
|
|
689
|
+
const indexPath = join2(scratch, "index");
|
|
690
|
+
const env = { ...process.env, GIT_INDEX_FILE: indexPath };
|
|
691
|
+
const patch = opts.patchContent.endsWith("\n") ? opts.patchContent : `${opts.patchContent}
|
|
692
|
+
`;
|
|
693
|
+
try {
|
|
694
|
+
const readTree = await execa("git", ["read-tree", opts.baseRef], {
|
|
695
|
+
cwd: opts.worktreePath,
|
|
696
|
+
env,
|
|
697
|
+
stdio: "pipe",
|
|
698
|
+
reject: false
|
|
699
|
+
});
|
|
700
|
+
if (readTree.exitCode !== 0) {
|
|
701
|
+
throw new Error(`Failed to read base tree: ${readTree.stderr}`);
|
|
702
|
+
}
|
|
703
|
+
const apply = await execa("git", ["apply", "--cached", "-"], {
|
|
704
|
+
cwd: opts.worktreePath,
|
|
705
|
+
env,
|
|
706
|
+
input: patch,
|
|
707
|
+
stdio: "pipe",
|
|
708
|
+
reject: false
|
|
709
|
+
});
|
|
710
|
+
if (apply.exitCode !== 0) {
|
|
711
|
+
throw new Error(`Failed to apply patch to temp index: ${apply.stderr}`);
|
|
712
|
+
}
|
|
713
|
+
const writeTree = await execa("git", ["write-tree"], {
|
|
714
|
+
cwd: opts.worktreePath,
|
|
715
|
+
env,
|
|
716
|
+
stdio: "pipe",
|
|
717
|
+
reject: false
|
|
718
|
+
});
|
|
719
|
+
if (writeTree.exitCode !== 0) {
|
|
720
|
+
throw new Error(`Failed to write tree: ${writeTree.stderr}`);
|
|
721
|
+
}
|
|
722
|
+
const treeSha = writeTree.stdout.trim();
|
|
723
|
+
const commitTree = await execa(
|
|
724
|
+
"git",
|
|
725
|
+
["commit-tree", treeSha, "-p", opts.baseRef, "-m", opts.message],
|
|
726
|
+
{ cwd: opts.worktreePath, env, stdio: "pipe", reject: false }
|
|
727
|
+
);
|
|
728
|
+
if (commitTree.exitCode !== 0) {
|
|
729
|
+
throw new Error(`Failed to create commit: ${commitTree.stderr}`);
|
|
730
|
+
}
|
|
731
|
+
return commitTree.stdout.trim();
|
|
732
|
+
} finally {
|
|
733
|
+
await rm(scratch, { recursive: true, force: true });
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
async function savePatchFile(patchContent, patchDir, taskId, variantId, iteration) {
|
|
737
|
+
const safeTaskId = sanitizeForPathSegment(taskId);
|
|
738
|
+
const safeVariantId = sanitizeForPathSegment(variantId);
|
|
739
|
+
const patchPath = join2(patchDir, safeTaskId, `${safeVariantId}-iteration-${iteration}.patch`);
|
|
740
|
+
await mkdir(dirname(patchPath), { recursive: true });
|
|
741
|
+
await writeFile(patchPath, patchContent, "utf-8");
|
|
742
|
+
return patchPath;
|
|
743
|
+
}
|
|
744
|
+
async function createGitRef(worktreePath, refNamespace, taskId, variantId, iteration, commitSha) {
|
|
745
|
+
const safeTaskId = sanitizeForGitRefSegment(taskId);
|
|
746
|
+
const safeVariantId = sanitizeForGitRefSegment(variantId);
|
|
747
|
+
const refName = `${refNamespace}/${safeTaskId}/${safeVariantId}/${iteration}`;
|
|
748
|
+
const updateResult = await execa("git", ["update-ref", refName, commitSha], {
|
|
749
|
+
cwd: worktreePath,
|
|
750
|
+
stdio: "pipe",
|
|
751
|
+
reject: false
|
|
752
|
+
});
|
|
753
|
+
if (updateResult.exitCode !== 0) {
|
|
754
|
+
throw new Error(`Failed to create ref: ${updateResult.stderr}`);
|
|
755
|
+
}
|
|
756
|
+
return refName;
|
|
757
|
+
}
|
|
758
|
+
async function createBranch(worktreePath, branchPrefix, taskId, variantId, iteration, commitSha) {
|
|
759
|
+
const flat = (part) => sanitizeForGitRefSegment(part).replace(/\//g, "-");
|
|
760
|
+
const branchName = `${branchPrefix}/${flat(taskId)}-${flat(variantId)}-${iteration}`;
|
|
761
|
+
const result = await execa("git", ["branch", "-f", branchName, commitSha], {
|
|
762
|
+
cwd: worktreePath,
|
|
763
|
+
stdio: "pipe",
|
|
764
|
+
reject: false
|
|
765
|
+
});
|
|
766
|
+
if (result.exitCode !== 0) {
|
|
767
|
+
throw new Error(`Failed to create branch: ${result.stderr}`);
|
|
768
|
+
}
|
|
769
|
+
return branchName;
|
|
770
|
+
}
|
|
771
|
+
async function preserveRepairIteration(worktreePath, baseRef, taskId, variantId, iteration, config = {}) {
|
|
772
|
+
const cfg = { ...DEFAULT_CONFIG, ...config };
|
|
773
|
+
const results = [];
|
|
774
|
+
let patchContent = null;
|
|
775
|
+
let preservedCommitSha = null;
|
|
776
|
+
for (const mode of cfg.modes) {
|
|
777
|
+
try {
|
|
778
|
+
let artifactPath;
|
|
779
|
+
switch (mode) {
|
|
780
|
+
case "patch": {
|
|
781
|
+
patchContent = patchContent ?? await generatePatch(worktreePath, baseRef);
|
|
782
|
+
artifactPath = await savePatchFile(
|
|
783
|
+
patchContent,
|
|
784
|
+
cfg.patchDir,
|
|
785
|
+
taskId,
|
|
786
|
+
variantId,
|
|
787
|
+
iteration.iteration
|
|
788
|
+
);
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
case "ref": {
|
|
792
|
+
patchContent = patchContent ?? await generatePatch(worktreePath, baseRef);
|
|
793
|
+
preservedCommitSha = preservedCommitSha ?? await createCommitFromPatch({
|
|
794
|
+
worktreePath,
|
|
795
|
+
baseRef,
|
|
796
|
+
patchContent,
|
|
797
|
+
message: `build: preserve repair (${taskId}, ${variantId}, iter ${iteration.iteration})`
|
|
798
|
+
});
|
|
799
|
+
artifactPath = await createGitRef(
|
|
800
|
+
worktreePath,
|
|
801
|
+
cfg.refNamespace,
|
|
802
|
+
taskId,
|
|
803
|
+
variantId,
|
|
804
|
+
iteration.iteration,
|
|
805
|
+
preservedCommitSha
|
|
806
|
+
);
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
case "branch": {
|
|
810
|
+
patchContent = patchContent ?? await generatePatch(worktreePath, baseRef);
|
|
811
|
+
preservedCommitSha = preservedCommitSha ?? await createCommitFromPatch({
|
|
812
|
+
worktreePath,
|
|
813
|
+
baseRef,
|
|
814
|
+
patchContent,
|
|
815
|
+
message: `build: preserve repair (${taskId}, ${variantId}, iter ${iteration.iteration})`
|
|
816
|
+
});
|
|
817
|
+
artifactPath = await createBranch(
|
|
818
|
+
worktreePath,
|
|
819
|
+
cfg.branchPrefix,
|
|
820
|
+
taskId,
|
|
821
|
+
variantId,
|
|
822
|
+
iteration.iteration,
|
|
823
|
+
preservedCommitSha
|
|
824
|
+
);
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
results.push({
|
|
829
|
+
artifactPath,
|
|
830
|
+
mode,
|
|
831
|
+
success: true
|
|
832
|
+
});
|
|
833
|
+
} catch (error) {
|
|
834
|
+
results.push({
|
|
835
|
+
artifactPath: "",
|
|
836
|
+
mode,
|
|
837
|
+
success: false,
|
|
838
|
+
error: error instanceof Error ? error.message : String(error)
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
return results;
|
|
843
|
+
}
|
|
844
|
+
function findBestIteration(iterations) {
|
|
845
|
+
if (iterations.length === 0) {
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
let bestIndex = 0;
|
|
849
|
+
let bestIteration = iterations[0];
|
|
850
|
+
for (let i = 1; i < iterations.length; i++) {
|
|
851
|
+
const current = iterations[i];
|
|
852
|
+
if (current.passed && !bestIteration.passed) {
|
|
853
|
+
bestIndex = i;
|
|
854
|
+
bestIteration = current;
|
|
855
|
+
continue;
|
|
856
|
+
}
|
|
857
|
+
if (current.passed === bestIteration.passed) {
|
|
858
|
+
if (current.newIssueCount < bestIteration.newIssueCount) {
|
|
859
|
+
bestIndex = i;
|
|
860
|
+
bestIteration = current;
|
|
861
|
+
continue;
|
|
862
|
+
}
|
|
863
|
+
if (current.newIssueCount === bestIteration.newIssueCount && current.fixedIssueCount > bestIteration.fixedIssueCount) {
|
|
864
|
+
bestIndex = i;
|
|
865
|
+
bestIteration = current;
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
return { index: bestIndex, iteration: bestIteration };
|
|
870
|
+
}
|
|
871
|
+
async function applyPatchFile(worktreePath, patchPath) {
|
|
872
|
+
const raw = await readFile2(patchPath, "utf-8");
|
|
873
|
+
if (raw.trim().length === 0) {
|
|
874
|
+
return { success: true };
|
|
875
|
+
}
|
|
876
|
+
const patchContent = raw.endsWith("\n") ? raw : `${raw}
|
|
877
|
+
`;
|
|
878
|
+
const result = await execa("git", ["apply", "--3way", "-"], {
|
|
879
|
+
cwd: worktreePath,
|
|
880
|
+
input: patchContent,
|
|
881
|
+
stdio: "pipe",
|
|
882
|
+
reject: false
|
|
883
|
+
});
|
|
884
|
+
if (result.exitCode !== 0) {
|
|
885
|
+
return {
|
|
886
|
+
success: false,
|
|
887
|
+
error: `Failed to apply patch: ${result.stderr}`
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
return { success: true };
|
|
891
|
+
}
|
|
892
|
+
async function cherryPickFromRef(worktreePath, refName) {
|
|
893
|
+
const result = await execa("git", ["cherry-pick", "--no-commit", refName], {
|
|
894
|
+
cwd: worktreePath,
|
|
895
|
+
stdio: "pipe",
|
|
896
|
+
reject: false
|
|
897
|
+
});
|
|
898
|
+
if (result.exitCode !== 0) {
|
|
899
|
+
return {
|
|
900
|
+
success: false,
|
|
901
|
+
error: `Failed to cherry-pick: ${result.stderr}`
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
return { success: true };
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
// src/orchestrator/verification/repair-diff-inventory.ts
|
|
908
|
+
var EMPTY = { stdout: "" };
|
|
909
|
+
function git(args, cwd) {
|
|
910
|
+
return execGit(args, { cwd }).catch(() => EMPTY);
|
|
911
|
+
}
|
|
912
|
+
async function getUntrackedFiles(worktreePath) {
|
|
913
|
+
const result = await git(["ls-files", "-z", "--others", "--exclude-standard"], worktreePath);
|
|
914
|
+
return result.stdout.split("\0").filter(Boolean);
|
|
915
|
+
}
|
|
916
|
+
function sumNumstat(stdout) {
|
|
917
|
+
let total = 0;
|
|
918
|
+
for (const line of stdout.split(/\r?\n/)) {
|
|
919
|
+
const trimmed = line.trim();
|
|
920
|
+
if (!trimmed) continue;
|
|
921
|
+
const parts = trimmed.split(/\s+/);
|
|
922
|
+
if (parts.length < 3) continue;
|
|
923
|
+
const added = Number.parseInt(parts[0] ?? "0", 10);
|
|
924
|
+
const deleted = Number.parseInt(parts[1] ?? "0", 10);
|
|
925
|
+
total += (Number.isFinite(added) ? added : 0) + (Number.isFinite(deleted) ? deleted : 0);
|
|
926
|
+
}
|
|
927
|
+
return total;
|
|
928
|
+
}
|
|
929
|
+
async function getChangedFiles(worktreePath, baselineRef) {
|
|
930
|
+
const [result, untracked] = await Promise.all([
|
|
931
|
+
git(["diff", "--name-only", "-z", baselineRef, "--"], worktreePath),
|
|
932
|
+
getUntrackedFiles(worktreePath)
|
|
933
|
+
]);
|
|
934
|
+
const tracked = result.stdout.split("\0").filter(Boolean);
|
|
935
|
+
return [.../* @__PURE__ */ new Set([...tracked, ...untracked])].sort();
|
|
936
|
+
}
|
|
937
|
+
async function getDiffSize(worktreePath, baselineRef) {
|
|
938
|
+
const [result, untracked] = await Promise.all([
|
|
939
|
+
git(["diff", "--numstat", baselineRef, "--"], worktreePath),
|
|
940
|
+
getUntrackedFiles(worktreePath)
|
|
941
|
+
]);
|
|
942
|
+
const untrackedStats = await Promise.all(
|
|
943
|
+
untracked.map(
|
|
944
|
+
(file) => git(["diff", "--no-index", "--numstat", "--", "/dev/null", file], worktreePath)
|
|
945
|
+
)
|
|
946
|
+
);
|
|
947
|
+
return [result, ...untrackedStats].reduce((total, stat) => total + sumNumstat(stat.stdout), 0);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// src/orchestrator/verification/verification-coverage.ts
|
|
951
|
+
function taskIrrelevantGate(channel) {
|
|
952
|
+
return {
|
|
953
|
+
status: "skipped",
|
|
954
|
+
exitCode: 0,
|
|
955
|
+
command: `${channel} (not task-relevant)`,
|
|
956
|
+
verificationCoverage: {
|
|
957
|
+
status: "skipped_not_relevant",
|
|
958
|
+
reason: "Channel is not relevant to the changed files."
|
|
959
|
+
},
|
|
960
|
+
durationMs: 0,
|
|
961
|
+
outputTail: `Skipped ${channel} gate: not task-relevant to the changed files.`
|
|
962
|
+
};
|
|
963
|
+
}
|
|
964
|
+
function controlsPassed(gates, controls) {
|
|
965
|
+
return controls.every(
|
|
966
|
+
(channel) => gates[channel === "test" ? "tests" : channel]?.status === "passed"
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
function buildHasNoCoveringCommand(gates) {
|
|
970
|
+
return gates.build.status === "unavailable" && gates.build.verificationCoverage?.status === "skipped_no_command";
|
|
971
|
+
}
|
|
972
|
+
function testsSupersedeUncoveredBuild(gates, checkTestGate) {
|
|
973
|
+
return checkTestGate && buildHasNoCoveringCommand(gates) && gates.tests?.status === "passed";
|
|
974
|
+
}
|
|
975
|
+
function gatesHavePromotableCoverage(gates, checkTestGate, controls = []) {
|
|
976
|
+
if (!controlsPassed(gates, controls)) return false;
|
|
977
|
+
if (gates.build.status === "unavailable" && !testsSupersedeUncoveredBuild(gates, checkTestGate)) {
|
|
978
|
+
return false;
|
|
979
|
+
}
|
|
980
|
+
if (!checkTestGate) return true;
|
|
981
|
+
if (gates.build.status !== "passed" && gates.build.status !== "skipped") return true;
|
|
982
|
+
const testStatus = gates.tests?.status;
|
|
983
|
+
return testStatus === "skipped" && controls.length > 0 || testStatus === "passed" || testStatus === "failed" || testStatus === "timeout" || testStatus === "error";
|
|
984
|
+
}
|
|
985
|
+
function isHardGateStatus(status) {
|
|
986
|
+
return status === "failed" || status === "timeout" || status === "error";
|
|
987
|
+
}
|
|
988
|
+
function gatesHaveNoHardFailures(gates, checkTestGate) {
|
|
989
|
+
if (isHardGateStatus(gates.build.status)) return false;
|
|
990
|
+
if (!checkTestGate) return true;
|
|
991
|
+
const testStatus = gates.tests?.status;
|
|
992
|
+
return testStatus === void 0 || !isHardGateStatus(testStatus);
|
|
993
|
+
}
|
|
994
|
+
function hardGateIncomplete(gates, checkTestGate) {
|
|
995
|
+
const incomplete = (channel) => [channel?.status, ...(channel?.commandResults ?? []).map((r) => r.status)].some(
|
|
996
|
+
(status) => status === "timeout" || status === "error"
|
|
997
|
+
);
|
|
998
|
+
return incomplete(gates.build) || checkTestGate && incomplete(gates.tests);
|
|
999
|
+
}
|
|
1000
|
+
function onlyUnavailableChannelsRemain(gates, options) {
|
|
1001
|
+
const statuses = [
|
|
1002
|
+
gates.build.status,
|
|
1003
|
+
...options.checkTestGate ? [gates.tests?.status] : [],
|
|
1004
|
+
...options.controls.map((channel) => gates[channel === "test" ? "tests" : channel]?.status)
|
|
1005
|
+
].filter((status) => status !== void 0);
|
|
1006
|
+
const notPassing = statuses.filter((status) => status !== "passed" && status !== "skipped");
|
|
1007
|
+
return notPassing.length > 0 && notPassing.every((status) => status === "unavailable");
|
|
1008
|
+
}
|
|
1009
|
+
function iterationPassesBaseline(input) {
|
|
1010
|
+
const { gates, checkTestGate, controls } = input;
|
|
1011
|
+
const provenNewFailures = input.provenNewFailures || checkTestGate && input.comparison.comparableTestTotalRegressed === true;
|
|
1012
|
+
if (hardGateIncomplete(gates, checkTestGate) || !controlsPassed(gates, controls)) return false;
|
|
1013
|
+
const buildPassedOrOutOfScope = gates.build.status === "passed" || checkTestGate && gates.build.status === "skipped" || testsSupersedeUncoveredBuild(gates, checkTestGate);
|
|
1014
|
+
const passedOutright = !provenNewFailures && buildPassedOrOutOfScope && (!checkTestGate || gates.tests?.status === "passed");
|
|
1015
|
+
const identical = input.identicalToBaseChannels ?? [];
|
|
1016
|
+
const hardChannels = (checkTestGate ? ["build", "test"] : ["build"]).filter(
|
|
1017
|
+
(channel) => !identical.includes(channel)
|
|
1018
|
+
);
|
|
1019
|
+
return passedOutright || stableBaselineProvesClean(input.comparison, provenNewFailures, hardChannels);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// src/orchestrator/verification/verification-loop-outcome-iteration.ts
|
|
1023
|
+
function toOutcomeIteration(it, baseline) {
|
|
1024
|
+
return {
|
|
1025
|
+
iteration: it.iteration,
|
|
1026
|
+
passed: it.passed,
|
|
1027
|
+
passedBaseline: it.passedBaseline,
|
|
1028
|
+
newIssueCount: it.newIssueCount,
|
|
1029
|
+
fixedIssueCount: it.fixedIssueCount,
|
|
1030
|
+
durationMs: it.durationMs,
|
|
1031
|
+
costUsd: it.costUsd,
|
|
1032
|
+
cumulativeCostUsd: it.cumulativeCostUsd,
|
|
1033
|
+
constraints: it.constraints,
|
|
1034
|
+
build: it.gates.build,
|
|
1035
|
+
lint: it.gates.lint,
|
|
1036
|
+
tests: it.gates.tests,
|
|
1037
|
+
persistingIssues: it.baselineComparison && persistingIssuesOnBase(it.baselineComparison, baseline, it.gates)
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/orchestrator/verification/verification-loop.ts
|
|
1042
|
+
function channelCommands2(channel) {
|
|
1043
|
+
if (Array.isArray(channel.commands) && channel.commands.length > 0) return channel.commands;
|
|
1044
|
+
if (typeof channel.command === "string" && channel.command.trim()) return [channel.command];
|
|
1045
|
+
return [];
|
|
1046
|
+
}
|
|
1047
|
+
function dominateGateStatus(statuses) {
|
|
1048
|
+
return dominantVerificationStatus(statuses) ?? "unavailable";
|
|
1049
|
+
}
|
|
1050
|
+
function isUnavailableCoverage(coverage) {
|
|
1051
|
+
return coverage?.status === "skipped_no_command" || coverage?.status === "skipped_tool_unavailable";
|
|
1052
|
+
}
|
|
1053
|
+
function buildStatusFromArtifact(build) {
|
|
1054
|
+
if (build.ok) return "passed";
|
|
1055
|
+
if (isUnavailableCoverage(build.verificationCoverage)) return "unavailable";
|
|
1056
|
+
if (build.exitCode === 124) return "timeout";
|
|
1057
|
+
return "failed";
|
|
1058
|
+
}
|
|
1059
|
+
function lintStatusFromArtifact(lint) {
|
|
1060
|
+
if (isUnavailableCoverage(lint.verificationCoverage)) return "unavailable";
|
|
1061
|
+
if (lint.exitCode === 0) return "passed";
|
|
1062
|
+
if (lint.exitCode === 124) return "timeout";
|
|
1063
|
+
return "failed";
|
|
1064
|
+
}
|
|
1065
|
+
function testStatusFromArtifact(status) {
|
|
1066
|
+
if (status === "passed") return "passed";
|
|
1067
|
+
if (status === "timeout") return "timeout";
|
|
1068
|
+
if (status === "error") return "error";
|
|
1069
|
+
if (status === "unavailable") return "unavailable";
|
|
1070
|
+
if (status === "skipped") return "skipped";
|
|
1071
|
+
return "failed";
|
|
1072
|
+
}
|
|
1073
|
+
async function detectRemovedExportsInPublicBarrels(worktreePath, baselineRef) {
|
|
1074
|
+
const PUBLIC_BARRELS = /* @__PURE__ */ new Set(["src/cli/index.ts", "src/orchestrator/index.ts"]);
|
|
1075
|
+
const result = await execa2("git", ["diff", "-U0", baselineRef, "--", "src"], {
|
|
1076
|
+
cwd: worktreePath,
|
|
1077
|
+
stdio: "pipe",
|
|
1078
|
+
reject: false
|
|
1079
|
+
});
|
|
1080
|
+
const diff = String(result.stdout ?? "");
|
|
1081
|
+
const removed = [];
|
|
1082
|
+
let currentFile = null;
|
|
1083
|
+
for (const line of diff.split(/\r?\n/)) {
|
|
1084
|
+
if (line.startsWith("diff --git ")) {
|
|
1085
|
+
const match = /^diff --git a\/(.+?) b\/(.+)$/.exec(line);
|
|
1086
|
+
currentFile = match?.[2] ?? null;
|
|
1087
|
+
continue;
|
|
1088
|
+
}
|
|
1089
|
+
if (!currentFile) continue;
|
|
1090
|
+
if (!PUBLIC_BARRELS.has(currentFile)) continue;
|
|
1091
|
+
if (line.startsWith("-export") || line.startsWith("- export")) {
|
|
1092
|
+
removed.push(`${currentFile}: ${line.slice(1).trim()}`);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
return {
|
|
1096
|
+
removedExportLines: removed.length,
|
|
1097
|
+
sample: removed.slice(0, 3).join("\n") || void 0
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
1100
|
+
async function runDeterministicGates(worktreePath, baselineRef, timeoutMs, changedFiles = [], budget = "shared", skipTests = false) {
|
|
1101
|
+
return withValidationSlot(
|
|
1102
|
+
`gates:${worktreePath}`,
|
|
1103
|
+
() => runDeterministicGatesUnpaced(
|
|
1104
|
+
worktreePath,
|
|
1105
|
+
baselineRef,
|
|
1106
|
+
timeoutMs,
|
|
1107
|
+
changedFiles,
|
|
1108
|
+
budget,
|
|
1109
|
+
skipTests
|
|
1110
|
+
),
|
|
1111
|
+
{ maxWaitMs: Math.min(5 * 60 * 1e3, Math.max(5e3, timeoutMs)) }
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1114
|
+
async function runDeterministicGatesUnpaced(worktreePath, baselineRef, timeoutMs, changedFiles = [], budget = "shared", skipTests = false) {
|
|
1115
|
+
const MIN_GATE_TIMEOUT_MS = 5e3;
|
|
1116
|
+
const effectiveTimeoutMs = Math.max(MIN_GATE_TIMEOUT_MS, timeoutMs);
|
|
1117
|
+
const scope = resolveVerificationChannelScope(
|
|
1118
|
+
worktreePath,
|
|
1119
|
+
changedFiles
|
|
1120
|
+
);
|
|
1121
|
+
const buildCommands = channelCommands2(scope.build);
|
|
1122
|
+
const lintCommands = channelCommands2(scope.lint);
|
|
1123
|
+
const testCommands = selectScopedTestCommands(scope);
|
|
1124
|
+
const buildStart = Date.now();
|
|
1125
|
+
const buildChannelBudgetMs = budget === "per-channel" ? effectiveTimeoutMs : Math.min(resolveVerifyCommandTimeoutMs(), effectiveTimeoutMs);
|
|
1126
|
+
const buildArtifacts = scope.build.availability === "unavailable" ? {
|
|
1127
|
+
status: "unavailable",
|
|
1128
|
+
exitCode: 127,
|
|
1129
|
+
command: "build (verification unavailable)",
|
|
1130
|
+
durationMs: 0,
|
|
1131
|
+
stdoutTail: "",
|
|
1132
|
+
stderrTail: "",
|
|
1133
|
+
outputTail: "Build verification unavailable: no covering command for changed language.",
|
|
1134
|
+
verificationCoverage: {
|
|
1135
|
+
status: "skipped_no_command",
|
|
1136
|
+
reason: "No covering build/typecheck command for changed language."
|
|
1137
|
+
}
|
|
1138
|
+
} : scope.build.relevant ? await (async () => {
|
|
1139
|
+
if (buildCommands.length > 0) {
|
|
1140
|
+
let remaining = buildChannelBudgetMs;
|
|
1141
|
+
const commandResults = [];
|
|
1142
|
+
for (const command of buildCommands) {
|
|
1143
|
+
if (remaining <= 0) {
|
|
1144
|
+
commandResults.push({
|
|
1145
|
+
command,
|
|
1146
|
+
language: describeVerificationCommand(command).language,
|
|
1147
|
+
status: "timeout",
|
|
1148
|
+
exitCode: 124,
|
|
1149
|
+
durationMs: 0,
|
|
1150
|
+
outputTail: "Build command skipped: channel timeout budget exhausted."
|
|
1151
|
+
});
|
|
1152
|
+
continue;
|
|
1153
|
+
}
|
|
1154
|
+
const cmdStart = Date.now();
|
|
1155
|
+
const build2 = await runTypecheckWithArtifacts(worktreePath, {
|
|
1156
|
+
timeoutMs: remaining,
|
|
1157
|
+
command
|
|
1158
|
+
});
|
|
1159
|
+
const durationMs = Math.max(build2.durationMs ?? 0, Date.now() - cmdStart);
|
|
1160
|
+
remaining = Math.max(0, remaining - durationMs);
|
|
1161
|
+
const status = buildStatusFromArtifact(build2);
|
|
1162
|
+
commandResults.push({
|
|
1163
|
+
command: build2.command,
|
|
1164
|
+
language: build2.language ?? describeVerificationCommand(command).language,
|
|
1165
|
+
status,
|
|
1166
|
+
exitCode: build2.exitCode,
|
|
1167
|
+
durationMs,
|
|
1168
|
+
outputTail: build2.outputTail,
|
|
1169
|
+
stdoutTail: build2.stdoutTail,
|
|
1170
|
+
stderrTail: build2.stderrTail
|
|
1171
|
+
});
|
|
1172
|
+
}
|
|
1173
|
+
const statuses = commandResults.map((r) => r.status);
|
|
1174
|
+
const aggregateStatus = dominateGateStatus(statuses);
|
|
1175
|
+
const representative = commandResults.find((r) => r.status === aggregateStatus) ?? commandResults[0];
|
|
1176
|
+
const totalDuration = commandResults.reduce((sum, r) => sum + (r.durationMs ?? 0), 0);
|
|
1177
|
+
return {
|
|
1178
|
+
status: aggregateStatus,
|
|
1179
|
+
exitCode: representative.exitCode,
|
|
1180
|
+
command: representative.command,
|
|
1181
|
+
durationMs: Math.max(totalDuration, Date.now() - buildStart),
|
|
1182
|
+
stdoutTail: representative.stdoutTail ?? "",
|
|
1183
|
+
stderrTail: representative.stderrTail ?? "",
|
|
1184
|
+
outputTail: representative.outputTail,
|
|
1185
|
+
language: representative.language,
|
|
1186
|
+
verificationCoverage: aggregateStatus === "unavailable" ? {
|
|
1187
|
+
status: "skipped_tool_unavailable",
|
|
1188
|
+
command: representative.command,
|
|
1189
|
+
reason: "One or more scoped build commands were unavailable."
|
|
1190
|
+
} : aggregateStatus === "passed" ? { status: "verified", command: representative.command } : { status: "unverified", command: representative.command },
|
|
1191
|
+
commandResults
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
const build = await runTypecheckWithArtifacts(worktreePath, {
|
|
1195
|
+
timeoutMs: buildChannelBudgetMs,
|
|
1196
|
+
...scope.scoped && scope.build.command ? { command: scope.build.command } : {}
|
|
1197
|
+
});
|
|
1198
|
+
const buildDurationMs = Date.now() - buildStart;
|
|
1199
|
+
const buildStatus = buildStatusFromArtifact(build);
|
|
1200
|
+
return {
|
|
1201
|
+
status: buildStatus,
|
|
1202
|
+
exitCode: build.exitCode,
|
|
1203
|
+
command: build.command,
|
|
1204
|
+
durationMs: Math.max(build.durationMs ?? 0, buildDurationMs),
|
|
1205
|
+
stdoutTail: build.stdoutTail,
|
|
1206
|
+
stderrTail: build.stderrTail,
|
|
1207
|
+
outputTail: build.outputTail,
|
|
1208
|
+
verificationCoverage: build.verificationCoverage,
|
|
1209
|
+
// Carry the SOURCE-tagged language through so the current side parses by the
|
|
1210
|
+
// same (channel, language) the baseline recorded (root invariant). Fall back
|
|
1211
|
+
// to the scoped command's descriptor when the gate did not tag one.
|
|
1212
|
+
language: build.language ?? (scope.build.command ? describeVerificationCommand(scope.build.command).language : void 0)
|
|
1213
|
+
};
|
|
1214
|
+
})() : {
|
|
1215
|
+
// OUT-OF-SCOPE: this channel was intentionally not run for the task scope
|
|
1216
|
+
// (e.g. a Python-only change does not exercise the Node build). This is a
|
|
1217
|
+
// 'skipped' status (deliberately not run), DISTINCT from 'unavailable' (the
|
|
1218
|
+
// tool was relevant but missing/could-not-run). gatesHavePromotableCoverage
|
|
1219
|
+
// allows an out-of-scope 'skipped' build but still rejects an in-scope
|
|
1220
|
+
// 'unavailable' build, so an out-of-scope channel does not block promotion
|
|
1221
|
+
// while a genuinely-missing in-scope tool stays non-promotable (fail-closed).
|
|
1222
|
+
...taskIrrelevantGate("build"),
|
|
1223
|
+
stdoutTail: "",
|
|
1224
|
+
stderrTail: ""
|
|
1225
|
+
};
|
|
1226
|
+
const remainingMs = budget === "per-channel" ? effectiveTimeoutMs : Math.max(MIN_GATE_TIMEOUT_MS, effectiveTimeoutMs - (Date.now() - buildStart));
|
|
1227
|
+
const lintTimeoutMs = remainingMs;
|
|
1228
|
+
const testTimeoutMs = remainingMs;
|
|
1229
|
+
const runLintChannel = async () => {
|
|
1230
|
+
if (scope.lint.availability === "unavailable") {
|
|
1231
|
+
return {
|
|
1232
|
+
status: "unavailable",
|
|
1233
|
+
exitCode: 127,
|
|
1234
|
+
command: "lint (verification unavailable)",
|
|
1235
|
+
durationMs: 0,
|
|
1236
|
+
outputTail: "Lint verification unavailable: no covering command for changed language.",
|
|
1237
|
+
verificationCoverage: {
|
|
1238
|
+
status: "skipped_no_command",
|
|
1239
|
+
reason: "No covering lint command for changed language."
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
}
|
|
1243
|
+
if (!scope.lint.relevant) {
|
|
1244
|
+
return taskIrrelevantGate("lint");
|
|
1245
|
+
}
|
|
1246
|
+
if (lintCommands.length > 0) {
|
|
1247
|
+
let remaining = lintTimeoutMs;
|
|
1248
|
+
const commandResults = [];
|
|
1249
|
+
for (const command of lintCommands) {
|
|
1250
|
+
if (remaining <= 0) {
|
|
1251
|
+
commandResults.push({
|
|
1252
|
+
command,
|
|
1253
|
+
language: describeVerificationCommand(command).language,
|
|
1254
|
+
status: "timeout",
|
|
1255
|
+
exitCode: 124,
|
|
1256
|
+
durationMs: 0,
|
|
1257
|
+
outputTail: "Lint command skipped: channel timeout budget exhausted."
|
|
1258
|
+
});
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
const lint3 = await runLintOnDiffWithArtifacts(worktreePath, {
|
|
1262
|
+
baseBranch: baselineRef,
|
|
1263
|
+
timeoutMs: remaining,
|
|
1264
|
+
command
|
|
1265
|
+
});
|
|
1266
|
+
remaining = Math.max(0, remaining - (lint3.durationMs ?? 0));
|
|
1267
|
+
commandResults.push({
|
|
1268
|
+
command: lint3.command,
|
|
1269
|
+
language: lint3.language ?? describeVerificationCommand(command).language,
|
|
1270
|
+
status: lintStatusFromArtifact(lint3),
|
|
1271
|
+
exitCode: lint3.exitCode,
|
|
1272
|
+
durationMs: lint3.durationMs,
|
|
1273
|
+
outputTail: lint3.outputTail
|
|
1274
|
+
});
|
|
1275
|
+
}
|
|
1276
|
+
const aggregateStatus = dominateGateStatus(commandResults.map((r) => r.status));
|
|
1277
|
+
const representative = commandResults.find((r) => r.status === aggregateStatus) ?? commandResults[0];
|
|
1278
|
+
return {
|
|
1279
|
+
status: aggregateStatus,
|
|
1280
|
+
exitCode: representative.exitCode,
|
|
1281
|
+
command: representative.command,
|
|
1282
|
+
durationMs: commandResults.reduce((sum, r) => sum + (r.durationMs ?? 0), 0),
|
|
1283
|
+
outputTail: representative.outputTail,
|
|
1284
|
+
language: representative.language,
|
|
1285
|
+
verificationCoverage: aggregateStatus === "unavailable" ? {
|
|
1286
|
+
status: "skipped_tool_unavailable",
|
|
1287
|
+
command: representative.command,
|
|
1288
|
+
reason: "One or more scoped lint commands were unavailable."
|
|
1289
|
+
} : aggregateStatus === "passed" ? { status: "verified", command: representative.command } : { status: "unverified", command: representative.command },
|
|
1290
|
+
commandResults
|
|
1291
|
+
};
|
|
1292
|
+
}
|
|
1293
|
+
const lint2 = await runLintOnDiffWithArtifacts(worktreePath, {
|
|
1294
|
+
baseBranch: baselineRef,
|
|
1295
|
+
timeoutMs: lintTimeoutMs,
|
|
1296
|
+
...scope.scoped && scope.lint.command ? { command: scope.lint.command } : {}
|
|
1297
|
+
});
|
|
1298
|
+
return {
|
|
1299
|
+
status: lintStatusFromArtifact(lint2),
|
|
1300
|
+
exitCode: lint2.exitCode,
|
|
1301
|
+
command: lint2.command,
|
|
1302
|
+
durationMs: lint2.durationMs,
|
|
1303
|
+
outputTail: lint2.outputTail,
|
|
1304
|
+
verificationCoverage: lint2.verificationCoverage,
|
|
1305
|
+
language: lint2.language ?? (scope.lint.command ? describeVerificationCommand(scope.lint.command).language : void 0)
|
|
1306
|
+
};
|
|
1307
|
+
};
|
|
1308
|
+
const runTestChannel = async () => {
|
|
1309
|
+
if (skipTests) {
|
|
1310
|
+
return {
|
|
1311
|
+
status: "skipped",
|
|
1312
|
+
exitCode: 0,
|
|
1313
|
+
command: "test (prohibited by task)",
|
|
1314
|
+
durationMs: 0,
|
|
1315
|
+
outputTail: "Skipped test gate: the task prohibits running tests.",
|
|
1316
|
+
failingTests: []
|
|
1317
|
+
};
|
|
1318
|
+
}
|
|
1319
|
+
if (scope.test.availability === "unavailable") {
|
|
1320
|
+
return {
|
|
1321
|
+
status: "unavailable",
|
|
1322
|
+
exitCode: 127,
|
|
1323
|
+
command: "test (verification unavailable)",
|
|
1324
|
+
durationMs: 0,
|
|
1325
|
+
outputTail: "Test verification unavailable: no covering command for changed language.",
|
|
1326
|
+
failingTests: []
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
if (!scope.test.relevant) {
|
|
1330
|
+
return {
|
|
1331
|
+
// OUT-OF-SCOPE: intentionally not run for this task scope. 'skipped' (not
|
|
1332
|
+
// run on purpose) is distinct from 'unavailable' (relevant tool missing).
|
|
1333
|
+
...taskIrrelevantGate("test"),
|
|
1334
|
+
failingTests: []
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
if (testCommands.length > 0) {
|
|
1338
|
+
let remaining = testTimeoutMs;
|
|
1339
|
+
const commandResults = [];
|
|
1340
|
+
for (const command of testCommands) {
|
|
1341
|
+
if (remaining <= 0) {
|
|
1342
|
+
commandResults.push({
|
|
1343
|
+
command,
|
|
1344
|
+
language: describeVerificationCommand(command).language,
|
|
1345
|
+
status: "timeout",
|
|
1346
|
+
exitCode: 124,
|
|
1347
|
+
durationMs: 0,
|
|
1348
|
+
outputTail: "Test command skipped: channel timeout budget exhausted.",
|
|
1349
|
+
failingTests: []
|
|
1350
|
+
});
|
|
1351
|
+
continue;
|
|
1352
|
+
}
|
|
1353
|
+
const tests3 = await runDeterministicTestGateWithArtifacts(worktreePath, {
|
|
1354
|
+
baseRef: baselineRef,
|
|
1355
|
+
timeoutMs: remaining,
|
|
1356
|
+
command
|
|
1357
|
+
});
|
|
1358
|
+
remaining = Math.max(0, remaining - (tests3.durationMs ?? 0));
|
|
1359
|
+
const status = testStatusFromArtifact(tests3.status);
|
|
1360
|
+
commandResults.push({
|
|
1361
|
+
command: tests3.command,
|
|
1362
|
+
...tests3.executables?.length ? { executables: tests3.executables } : {},
|
|
1363
|
+
language: tests3.language ?? describeVerificationCommand(command).language,
|
|
1364
|
+
status,
|
|
1365
|
+
exitCode: tests3.exitCode,
|
|
1366
|
+
durationMs: tests3.durationMs,
|
|
1367
|
+
outputTail: tests3.outputTail,
|
|
1368
|
+
failingTests: tests3.failingTests,
|
|
1369
|
+
failingTestIdentities: tests3.failingTestIdentities
|
|
1370
|
+
});
|
|
1371
|
+
}
|
|
1372
|
+
const aggregateStatus = dominateGateStatus(commandResults.map((r) => r.status));
|
|
1373
|
+
const representative = commandResults.find((r) => r.status === aggregateStatus) ?? commandResults[0];
|
|
1374
|
+
const failingTests = Array.from(new Set(commandResults.flatMap((r) => r.failingTests ?? [])));
|
|
1375
|
+
const failingTestIdentities = Array.from(
|
|
1376
|
+
new Set(commandResults.flatMap((r) => r.failingTestIdentities ?? r.failingTests ?? []))
|
|
1377
|
+
);
|
|
1378
|
+
return {
|
|
1379
|
+
status: aggregateStatus,
|
|
1380
|
+
exitCode: representative.exitCode,
|
|
1381
|
+
command: representative.command,
|
|
1382
|
+
durationMs: commandResults.reduce((sum, r) => sum + (r.durationMs ?? 0), 0),
|
|
1383
|
+
outputTail: representative.outputTail,
|
|
1384
|
+
failingTests,
|
|
1385
|
+
failingTestIdentities,
|
|
1386
|
+
language: representative.language,
|
|
1387
|
+
commandResults,
|
|
1388
|
+
...commandResults.some((result) => result.executables?.length) ? { executables: commandResults.flatMap((result) => result.executables ?? []) } : {}
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
const tests2 = await runDeterministicTestGateWithArtifacts(worktreePath, {
|
|
1392
|
+
baseRef: baselineRef,
|
|
1393
|
+
timeoutMs: testTimeoutMs,
|
|
1394
|
+
...scope.scoped && scope.test.command ? { command: scope.test.command } : {}
|
|
1395
|
+
});
|
|
1396
|
+
return {
|
|
1397
|
+
status: testStatusFromArtifact(tests2.status),
|
|
1398
|
+
exitCode: tests2.exitCode,
|
|
1399
|
+
command: tests2.command,
|
|
1400
|
+
...tests2.executables?.length ? { executables: tests2.executables } : {},
|
|
1401
|
+
durationMs: tests2.durationMs,
|
|
1402
|
+
outputTail: tests2.outputTail,
|
|
1403
|
+
failingTests: tests2.failingTests,
|
|
1404
|
+
failingTestIdentities: tests2.failingTestIdentities,
|
|
1405
|
+
language: tests2.language ?? (scope.test.command ? describeVerificationCommand(scope.test.command).language : void 0)
|
|
1406
|
+
};
|
|
1407
|
+
};
|
|
1408
|
+
const [lint, tests] = await Promise.all([runLintChannel(), runTestChannel()]);
|
|
1409
|
+
const gates = {
|
|
1410
|
+
build: buildArtifacts,
|
|
1411
|
+
lint: {
|
|
1412
|
+
status: lint.status,
|
|
1413
|
+
exitCode: lint.exitCode,
|
|
1414
|
+
command: lint.command,
|
|
1415
|
+
durationMs: lint.durationMs,
|
|
1416
|
+
outputTail: lint.outputTail,
|
|
1417
|
+
verificationCoverage: lint.verificationCoverage,
|
|
1418
|
+
language: lint.language,
|
|
1419
|
+
...lint.commandResults ? { commandResults: lint.commandResults } : {}
|
|
1420
|
+
},
|
|
1421
|
+
tests: {
|
|
1422
|
+
verificationCoverage: tests.verificationCoverage,
|
|
1423
|
+
...tests.executables?.length ? { executables: tests.executables } : {},
|
|
1424
|
+
status: tests.status,
|
|
1425
|
+
exitCode: tests.exitCode,
|
|
1426
|
+
command: tests.command,
|
|
1427
|
+
durationMs: tests.durationMs,
|
|
1428
|
+
outputTail: tests.outputTail,
|
|
1429
|
+
failingTests: tests.failingTests,
|
|
1430
|
+
failingTestIdentities: tests.failingTestIdentities,
|
|
1431
|
+
language: tests.language,
|
|
1432
|
+
...tests.commandResults ? { commandResults: tests.commandResults } : {}
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
const passed = buildArtifacts.status === "passed" && (tests.status === "passed" || tests.status === "skipped");
|
|
1436
|
+
return { passed, gates };
|
|
1437
|
+
}
|
|
1438
|
+
function buildVerificationFindings(gates, passedBaseline, controls = []) {
|
|
1439
|
+
if (passedBaseline) return [];
|
|
1440
|
+
const findings = [];
|
|
1441
|
+
for (const kind of ["build", "lint"]) {
|
|
1442
|
+
const channel = gates[kind];
|
|
1443
|
+
if (channel && (kind === "build" || controls.includes(kind)) && isHardGateStatus(channel.status)) {
|
|
1444
|
+
findings.push({
|
|
1445
|
+
kind,
|
|
1446
|
+
command: channel.command,
|
|
1447
|
+
exitCode: channel.exitCode,
|
|
1448
|
+
outputTail: channel.outputTail,
|
|
1449
|
+
severity: "error"
|
|
1450
|
+
});
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
if (gates.tests && gates.tests.status !== "passed" && gates.tests.status !== "skipped" && gates.tests.status !== "unavailable") {
|
|
1454
|
+
findings.push({
|
|
1455
|
+
kind: "test",
|
|
1456
|
+
command: gates.tests.command,
|
|
1457
|
+
exitCode: gates.tests.exitCode,
|
|
1458
|
+
outputTail: gates.tests.outputTail,
|
|
1459
|
+
failingTests: gates.tests.failingTests,
|
|
1460
|
+
severity: "error"
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
return findings;
|
|
1464
|
+
}
|
|
1465
|
+
async function runDeterministicVerificationOnce(params) {
|
|
1466
|
+
const checkTestGate = params.checkTestGate ?? true;
|
|
1467
|
+
const controls = requiredVerificationControlChannels(params.changedFiles ?? []);
|
|
1468
|
+
const started = Date.now();
|
|
1469
|
+
const gateRun = params.precomputedGates ? {
|
|
1470
|
+
gates: params.precomputedGates,
|
|
1471
|
+
passed: params.precomputedGates.build.status === "passed" && (!checkTestGate || params.precomputedGates.tests?.status === "passed")
|
|
1472
|
+
} : await runDeterministicGates(
|
|
1473
|
+
params.worktreePath,
|
|
1474
|
+
params.baselineRef,
|
|
1475
|
+
params.timeoutMs,
|
|
1476
|
+
params.changedFiles ?? [],
|
|
1477
|
+
params.budget,
|
|
1478
|
+
!checkTestGate
|
|
1479
|
+
);
|
|
1480
|
+
let passedBaseline = gatesHaveNoHardFailures(gateRun.gates, checkTestGate) && controlsPassed(gateRun.gates, controls);
|
|
1481
|
+
let newIssueCount = 0;
|
|
1482
|
+
let fixedIssueCount = 0;
|
|
1483
|
+
let persistingIssues;
|
|
1484
|
+
if (params.baseline) {
|
|
1485
|
+
const comparisonOutcomes = verificationOutcomesFromGateArtifacts(gateRun.gates, {
|
|
1486
|
+
source: "verification-loop",
|
|
1487
|
+
cwd: params.worktreePath
|
|
1488
|
+
});
|
|
1489
|
+
const baselineComparison = compareVerificationOutcomeToBaseline(
|
|
1490
|
+
params.baseline,
|
|
1491
|
+
comparisonOutcomes
|
|
1492
|
+
);
|
|
1493
|
+
const gateExitRegressions = cleanBaselineGateExitRegressions(
|
|
1494
|
+
params.baseline,
|
|
1495
|
+
baselineComparison,
|
|
1496
|
+
comparisonOutcomes,
|
|
1497
|
+
checkTestGate
|
|
1498
|
+
);
|
|
1499
|
+
const hardStructuredNewIssueCount = baselineComparison.delta.build.newIssues.size + (checkTestGate ? baselineComparison.delta.test.newIssues.size : 0);
|
|
1500
|
+
passedBaseline = iterationPassesBaseline({
|
|
1501
|
+
gates: gateRun.gates,
|
|
1502
|
+
checkTestGate,
|
|
1503
|
+
controls,
|
|
1504
|
+
comparison: baselineComparison,
|
|
1505
|
+
provenNewFailures: hardStructuredNewIssueCount > 0 || gateExitRegressions.length > 0,
|
|
1506
|
+
identicalToBaseChannels: channelsFailingIdenticallyOnBase(
|
|
1507
|
+
params.baseline,
|
|
1508
|
+
gateRun.gates,
|
|
1509
|
+
checkTestGate ? ["build", "test"] : ["build"],
|
|
1510
|
+
[params.worktreePath]
|
|
1511
|
+
)
|
|
1512
|
+
});
|
|
1513
|
+
const unparsedGateRegressionCount = gateExitRegressions.filter((kind) => {
|
|
1514
|
+
if (kind === "build") return baselineComparison.delta.build.newIssues.size === 0;
|
|
1515
|
+
return baselineComparison.delta.test.newIssues.size === 0;
|
|
1516
|
+
}).length;
|
|
1517
|
+
newIssueCount = hardStructuredNewIssueCount + unparsedGateRegressionCount;
|
|
1518
|
+
fixedIssueCount = baselineComparison.delta.totalFixedIssues;
|
|
1519
|
+
persistingIssues = persistingIssuesOnBase(baselineComparison, params.baseline, gateRun.gates);
|
|
1520
|
+
}
|
|
1521
|
+
const durationMs = Date.now() - started;
|
|
1522
|
+
const findings = buildVerificationFindings(gateRun.gates, passedBaseline, controls);
|
|
1523
|
+
const promotable = passedBaseline && gatesHavePromotableCoverage(gateRun.gates, checkTestGate, controls);
|
|
1524
|
+
return {
|
|
1525
|
+
verification: {
|
|
1526
|
+
passed: promotable,
|
|
1527
|
+
passedBaseline,
|
|
1528
|
+
findings
|
|
1529
|
+
},
|
|
1530
|
+
verificationLoop: {
|
|
1531
|
+
enabled: true,
|
|
1532
|
+
passed: promotable,
|
|
1533
|
+
passedBaseline,
|
|
1534
|
+
stopReason: passedBaseline ? "single_iteration_no_new_regressions" : "single_iteration_gates_failed",
|
|
1535
|
+
bestIterationIndex: 0,
|
|
1536
|
+
bestIterationPromotable: promotable,
|
|
1537
|
+
iterations: [
|
|
1538
|
+
{
|
|
1539
|
+
iteration: 1,
|
|
1540
|
+
passed: gateRun.passed,
|
|
1541
|
+
passedBaseline,
|
|
1542
|
+
newIssueCount,
|
|
1543
|
+
fixedIssueCount,
|
|
1544
|
+
durationMs,
|
|
1545
|
+
costUsd: 0,
|
|
1546
|
+
cumulativeCostUsd: 0,
|
|
1547
|
+
constraints: {
|
|
1548
|
+
maxFilesTouched: 0,
|
|
1549
|
+
diffSizeCap: 0,
|
|
1550
|
+
apiBreakAllowed: false,
|
|
1551
|
+
violations: []
|
|
1552
|
+
},
|
|
1553
|
+
build: gateRun.gates.build,
|
|
1554
|
+
lint: gateRun.gates.lint,
|
|
1555
|
+
tests: gateRun.gates.tests,
|
|
1556
|
+
persistingIssues,
|
|
1557
|
+
...params.precomputedGates ? { gateSource: "precomputed" } : {}
|
|
1558
|
+
}
|
|
1559
|
+
]
|
|
1560
|
+
}
|
|
1561
|
+
};
|
|
1562
|
+
}
|
|
1563
|
+
function gateVector(gates) {
|
|
1564
|
+
const build = gates.build.status === "passed" ? 1 : 0;
|
|
1565
|
+
const lint = 1;
|
|
1566
|
+
const tests = gates.tests?.status === "passed" ? 1 : 0;
|
|
1567
|
+
return [build, lint, tests];
|
|
1568
|
+
}
|
|
1569
|
+
function isRegression(prev, next) {
|
|
1570
|
+
const [pb, pl, pt] = gateVector(prev);
|
|
1571
|
+
const [nb, nl, nt] = gateVector(next);
|
|
1572
|
+
if (nb < pb) return true;
|
|
1573
|
+
const prevSum = pb + pl + pt;
|
|
1574
|
+
const nextSum = nb + nl + nt;
|
|
1575
|
+
return nextSum < prevSum;
|
|
1576
|
+
}
|
|
1577
|
+
function cleanBaselineGateExitRegressions(baseline, comparison, outcomes, checkTestGate) {
|
|
1578
|
+
const regressed = detectGateRegressions(
|
|
1579
|
+
baseline,
|
|
1580
|
+
{ build: outcomes.build, tests: checkTestGate ? outcomes.tests : void 0 },
|
|
1581
|
+
comparison.baseline
|
|
1582
|
+
);
|
|
1583
|
+
return [
|
|
1584
|
+
...regressed.build ? ["build"] : [],
|
|
1585
|
+
...regressed.test ? ["test"] : []
|
|
1586
|
+
];
|
|
1587
|
+
}
|
|
1588
|
+
function buildRepairPrompt(opts) {
|
|
1589
|
+
const { originalPrompt, iteration, gates, constraints, allowedFiles, scopePolicy } = opts;
|
|
1590
|
+
const parts = [];
|
|
1591
|
+
parts.push(originalPrompt.trimEnd());
|
|
1592
|
+
parts.push("\n\n---\n\n");
|
|
1593
|
+
parts.push(
|
|
1594
|
+
`VERIFICATION-DRIVEN REPAIR (iteration ${iteration}): Fix the deterministic gate failures below.`
|
|
1595
|
+
);
|
|
1596
|
+
parts.push(
|
|
1597
|
+
"Stabilization approach:",
|
|
1598
|
+
"- Prioritize high-leverage, low-cost fixes first (e.g., lint/formatting, obvious type errors).",
|
|
1599
|
+
"- Keep the diff cohesive and targeted; preserve the core solution unless a change is clearly required.",
|
|
1600
|
+
"- Only attempt deeper behavioral/test fixes once cheap failures are resolved or if time remains."
|
|
1601
|
+
);
|
|
1602
|
+
parts.push("Make the MINIMUM change set required to improve gate outcomes.");
|
|
1603
|
+
parts.push(
|
|
1604
|
+
`HARD CONSTRAINTS (enforced): maxFilesTouched=${constraints.maxFilesTouched}, diffSizeCap=${constraints.diffSizeCap}, apiBreakAllowed=${constraints.apiBreakAllowed}`
|
|
1605
|
+
);
|
|
1606
|
+
if (constraints.violations.length > 0) {
|
|
1607
|
+
parts.push(
|
|
1608
|
+
`CONSTRAINT VIOLATIONS DETECTED (must be fixed/undone):
|
|
1609
|
+
- ${constraints.violations.join("\n- ")}`
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1612
|
+
if (allowedFiles.length > 0) {
|
|
1613
|
+
parts.push(`ALLOWED FILES (derived from prompt/gates):
|
|
1614
|
+
- ${allowedFiles.join("\n- ")}`);
|
|
1615
|
+
if (scopePolicy === "strict") {
|
|
1616
|
+
parts.push("SCOPE POLICY: strict - do not modify files outside the allowed list.");
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
const gateBlock = (label, g) => {
|
|
1620
|
+
return [
|
|
1621
|
+
`
|
|
1622
|
+
${label}:`,
|
|
1623
|
+
`Command: ${g.command}`,
|
|
1624
|
+
`Exit Code: ${g.exitCode}`,
|
|
1625
|
+
`Output (tail):
|
|
1626
|
+
${renderUntrustedCommandOutput(g.outputTail)}`
|
|
1627
|
+
].join("\n");
|
|
1628
|
+
};
|
|
1629
|
+
parts.push(
|
|
1630
|
+
gateBlock("BUILD/TYPECHECK", {
|
|
1631
|
+
command: gates.build.command,
|
|
1632
|
+
exitCode: gates.build.exitCode,
|
|
1633
|
+
outputTail: gates.build.outputTail
|
|
1634
|
+
})
|
|
1635
|
+
);
|
|
1636
|
+
if (gates.lint && gates.lint.status !== "passed") {
|
|
1637
|
+
parts.push(
|
|
1638
|
+
gateBlock("LINT", {
|
|
1639
|
+
command: gates.lint.command,
|
|
1640
|
+
exitCode: gates.lint.exitCode,
|
|
1641
|
+
outputTail: gates.lint.outputTail
|
|
1642
|
+
})
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1645
|
+
if (gates.tests && gates.tests.status !== "passed" && gates.tests.status !== "skipped" && gates.tests.status !== "unavailable") {
|
|
1646
|
+
const failing = renderUntrustedFailingTests(gates.tests.failingTests);
|
|
1647
|
+
parts.push(
|
|
1648
|
+
`${gateBlock("TESTS", { command: gates.tests.command, exitCode: gates.tests.exitCode, outputTail: gates.tests.outputTail })}
|
|
1649
|
+
${failing}`
|
|
1650
|
+
);
|
|
1651
|
+
}
|
|
1652
|
+
parts.push("\nDo not remove public exports unless explicitly allowed.");
|
|
1653
|
+
parts.push("Keep refactors tightly scoped to what is required for correctness.");
|
|
1654
|
+
parts.push(`
|
|
1655
|
+
${renderRepairConstraints()}`);
|
|
1656
|
+
return parts.join("\n");
|
|
1657
|
+
}
|
|
1658
|
+
var GATE_EVIDENCE_EXTRA_EXTENSIONS = ["json", "css", "scss", "md", "ya?ml", "toml", "snap"];
|
|
1659
|
+
function buildGateEvidencePathPattern() {
|
|
1660
|
+
const escapeForClass = (ext) => ext.replace(/[+.]/g, "\\$&");
|
|
1661
|
+
const sourceExtensions = [...PROJECT_SOURCE_EXTENSIONS].map(
|
|
1662
|
+
(ext) => escapeForClass(ext.replace(/^\./, ""))
|
|
1663
|
+
);
|
|
1664
|
+
const extensionAlternation = [...sourceExtensions, ...GATE_EVIDENCE_EXTRA_EXTENSIONS].join("|");
|
|
1665
|
+
return new RegExp(
|
|
1666
|
+
`(?:^|[\\s'"([])((?:\\.{1,2}\\/)?(?:[A-Za-z0-9_.@+-]+\\/)*[A-Za-z0-9_.@+-]+\\.(?:${extensionAlternation}))(?:[:(]|$)`,
|
|
1667
|
+
"gm"
|
|
1668
|
+
);
|
|
1669
|
+
}
|
|
1670
|
+
var GATE_EVIDENCE_PATH_PATTERN = buildGateEvidencePathPattern();
|
|
1671
|
+
function extractFilePathsFromText(text) {
|
|
1672
|
+
if (!text) return [];
|
|
1673
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1674
|
+
for (const match of text.matchAll(GATE_EVIDENCE_PATH_PATTERN)) {
|
|
1675
|
+
const filePath = match[1]?.trim();
|
|
1676
|
+
if (filePath) paths.add(filePath.replace(/^\.\//, ""));
|
|
1677
|
+
}
|
|
1678
|
+
return [...paths];
|
|
1679
|
+
}
|
|
1680
|
+
function collectIssueFiles(issues) {
|
|
1681
|
+
return issues?.toArray?.().flatMap((issue) => issue.file ? [issue.file] : []) ?? [];
|
|
1682
|
+
}
|
|
1683
|
+
function deriveAllowedFilesFromGateEvidence(gates, comparison) {
|
|
1684
|
+
const files = /* @__PURE__ */ new Set();
|
|
1685
|
+
for (const file of [
|
|
1686
|
+
...collectIssueFiles(comparison?.delta.build.newIssues),
|
|
1687
|
+
...collectIssueFiles(comparison?.delta.lint.newIssues),
|
|
1688
|
+
...collectIssueFiles(comparison?.delta.test.newIssues),
|
|
1689
|
+
...extractFilePathsFromText(gates.build.outputTail),
|
|
1690
|
+
...extractFilePathsFromText(gates.build.stderrTail),
|
|
1691
|
+
...extractFilePathsFromText(gates.build.stdoutTail),
|
|
1692
|
+
...extractFilePathsFromText(gates.lint?.outputTail),
|
|
1693
|
+
...extractFilePathsFromText(gates.tests?.outputTail),
|
|
1694
|
+
...gates.tests?.failingTests.flatMap(extractFilePathsFromText) ?? []
|
|
1695
|
+
]) {
|
|
1696
|
+
files.add(file);
|
|
1697
|
+
}
|
|
1698
|
+
return [...files].sort();
|
|
1699
|
+
}
|
|
1700
|
+
function mergeRepairAllowedFiles(promptAllowedFiles, gateDerivedAllowedFiles) {
|
|
1701
|
+
const merged = /* @__PURE__ */ new Set();
|
|
1702
|
+
for (const file of [...promptAllowedFiles, ...gateDerivedAllowedFiles]) {
|
|
1703
|
+
const normalized = file.trim();
|
|
1704
|
+
if (normalized.length > 0) {
|
|
1705
|
+
merged.add(normalized);
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return [...merged].sort();
|
|
1709
|
+
}
|
|
1710
|
+
function buildScopedRepairPrompt(opts) {
|
|
1711
|
+
const { originalPrompt, iteration, gates, constraints, comparison, allowedFiles, scopePolicy } = opts;
|
|
1712
|
+
const parts = [];
|
|
1713
|
+
parts.push(originalPrompt.trimEnd());
|
|
1714
|
+
parts.push("\n\n---\n\n");
|
|
1715
|
+
parts.push(
|
|
1716
|
+
`BASELINE-AWARE REPAIR (iteration ${iteration}): Fix only the NEW issues introduced by your changes.`
|
|
1717
|
+
);
|
|
1718
|
+
if (allowedFiles.length > 0) {
|
|
1719
|
+
parts.push(`
|
|
1720
|
+
ALLOWED FILES (you may only modify these):
|
|
1721
|
+
- ${allowedFiles.join("\n- ")}`);
|
|
1722
|
+
if (scopePolicy === "strict") {
|
|
1723
|
+
parts.push(
|
|
1724
|
+
"\nSCOPE POLICY: strict - Do NOT modify files outside the allowed list, even if they have errors."
|
|
1725
|
+
);
|
|
1726
|
+
} else {
|
|
1727
|
+
parts.push(
|
|
1728
|
+
"\nSCOPE POLICY: expand - You may modify dependent files if they have new errors caused by your changes."
|
|
1729
|
+
);
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
parts.push("\nStabilization approach:");
|
|
1733
|
+
parts.push("- Focus ONLY on NEW issues listed below - ignore pre-existing issues.");
|
|
1734
|
+
parts.push("- Prioritize high-leverage, low-cost fixes (lint, obvious type errors).");
|
|
1735
|
+
parts.push("- Keep the diff cohesive; preserve your core solution.");
|
|
1736
|
+
parts.push("- Make the MINIMUM change set to resolve new issues.");
|
|
1737
|
+
const newBuild = comparison.delta.build.newIssues.toArray();
|
|
1738
|
+
const newLint = comparison.delta.lint.newIssues.toArray();
|
|
1739
|
+
const newTest = comparison.delta.test.newIssues.toArray();
|
|
1740
|
+
const totalNew = newBuild.length + newLint.length + newTest.length;
|
|
1741
|
+
if (totalNew > 0) {
|
|
1742
|
+
parts.push(`
|
|
1743
|
+
=== NEW ISSUES TO FIX (${totalNew} total) ===`);
|
|
1744
|
+
if (newBuild.length > 0) {
|
|
1745
|
+
parts.push(`
|
|
1746
|
+
NEW TYPE ERRORS (${newBuild.length}):`);
|
|
1747
|
+
for (const issue of newBuild.slice(0, 20)) {
|
|
1748
|
+
const loc = issue.line ? `:${issue.line}${issue.col ? `:${issue.col}` : ""}` : "";
|
|
1749
|
+
const message = presentUntrustedRepairEvidence(issue.originalMessage ?? "");
|
|
1750
|
+
parts.push(`- ${issue.file}${loc}: ${issue.code} - ${message}`);
|
|
1751
|
+
}
|
|
1752
|
+
if (newBuild.length > 20) {
|
|
1753
|
+
parts.push(` ... and ${newBuild.length - 20} more`);
|
|
1754
|
+
}
|
|
1755
|
+
}
|
|
1756
|
+
if (newLint.length > 0) {
|
|
1757
|
+
parts.push(`
|
|
1758
|
+
NEW LINT ERRORS (${newLint.length}):`);
|
|
1759
|
+
for (const issue of newLint.slice(0, 20)) {
|
|
1760
|
+
const loc = issue.line ? `:${issue.line}${issue.col ? `:${issue.col}` : ""}` : "";
|
|
1761
|
+
parts.push(`- ${issue.file}${loc}: ${issue.code}`);
|
|
1762
|
+
}
|
|
1763
|
+
if (newLint.length > 20) {
|
|
1764
|
+
parts.push(` ... and ${newLint.length - 20} more`);
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
if (newTest.length > 0) {
|
|
1768
|
+
parts.push(`
|
|
1769
|
+
NEW TEST FAILURES (${newTest.length}):`);
|
|
1770
|
+
for (const issue of newTest.slice(0, 10)) {
|
|
1771
|
+
parts.push(`- ${issue.file ? `${issue.file}: ` : ""}${issue.code}`);
|
|
1772
|
+
}
|
|
1773
|
+
if (newTest.length > 10) {
|
|
1774
|
+
parts.push(` ... and ${newTest.length - 10} more`);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
} else {
|
|
1778
|
+
parts.push("\n=== NO NEW ISSUES ===");
|
|
1779
|
+
parts.push("Your changes have not introduced any new issues vs baseline.");
|
|
1780
|
+
parts.push("If gates are still failing, it is due to pre-existing issues (listed below).");
|
|
1781
|
+
}
|
|
1782
|
+
if (!comparison.passed && totalNew === 0) {
|
|
1783
|
+
const gateBlock = (label, g) => [
|
|
1784
|
+
`
|
|
1785
|
+
${label}:`,
|
|
1786
|
+
`Status: ${g.status}`,
|
|
1787
|
+
`Command: ${g.command}`,
|
|
1788
|
+
`Exit Code: ${g.exitCode}`,
|
|
1789
|
+
`Output (tail):
|
|
1790
|
+
${renderUntrustedCommandOutput(g.outputTail)}`
|
|
1791
|
+
].join("\n");
|
|
1792
|
+
parts.push("\n=== UNPARSED GATE REGRESSION OUTPUT ===");
|
|
1793
|
+
parts.push(
|
|
1794
|
+
"A clean baseline gate now exits non-zero, but no stable issue identity could be extracted. Fix the gate failure shown below."
|
|
1795
|
+
);
|
|
1796
|
+
if (gates.build.status !== "passed") {
|
|
1797
|
+
parts.push(gateBlock("BUILD/TYPECHECK", gates.build));
|
|
1798
|
+
}
|
|
1799
|
+
if (gates.lint && gates.lint.status !== "passed") {
|
|
1800
|
+
parts.push(gateBlock("LINT", gates.lint));
|
|
1801
|
+
}
|
|
1802
|
+
if (gates.tests && gates.tests.status !== "passed" && gates.tests.status !== "skipped" && gates.tests.status !== "unavailable") {
|
|
1803
|
+
parts.push(gateBlock("TESTS", gates.tests));
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
const persistBuild = comparison.delta.build.persistingIssues.size;
|
|
1807
|
+
const persistLint = comparison.delta.lint.persistingIssues.size;
|
|
1808
|
+
const persistTest = comparison.delta.test.persistingIssues.size;
|
|
1809
|
+
const totalPersist = persistBuild + persistLint + persistTest;
|
|
1810
|
+
if (totalPersist > 0) {
|
|
1811
|
+
parts.push(`
|
|
1812
|
+
=== PRE-EXISTING ISSUES (${totalPersist} total - DO NOT fix these) ===`);
|
|
1813
|
+
parts.push("These existed before your changes and are NOT your responsibility:");
|
|
1814
|
+
if (persistBuild > 0) parts.push(`- ${persistBuild} type error(s)`);
|
|
1815
|
+
if (persistLint > 0) parts.push(`- ${persistLint} lint error(s)`);
|
|
1816
|
+
if (persistTest > 0) parts.push(`- ${persistTest} test failure(s)`);
|
|
1817
|
+
}
|
|
1818
|
+
parts.push(
|
|
1819
|
+
`
|
|
1820
|
+
HARD CONSTRAINTS: maxFilesTouched=${constraints.maxFilesTouched}, diffSizeCap=${constraints.diffSizeCap}`
|
|
1821
|
+
);
|
|
1822
|
+
if (constraints.violations.length > 0) {
|
|
1823
|
+
parts.push(`CONSTRAINT VIOLATIONS:
|
|
1824
|
+
- ${constraints.violations.join("\n- ")}`);
|
|
1825
|
+
}
|
|
1826
|
+
parts.push("\nDo not remove public exports unless explicitly allowed.");
|
|
1827
|
+
parts.push(`
|
|
1828
|
+
${renderRepairConstraints()}`);
|
|
1829
|
+
return parts.join("\n");
|
|
1830
|
+
}
|
|
1831
|
+
async function runVerificationDrivenRepairLoop(params) {
|
|
1832
|
+
const {
|
|
1833
|
+
config,
|
|
1834
|
+
worktreePath,
|
|
1835
|
+
baselineRef,
|
|
1836
|
+
originalPrompt,
|
|
1837
|
+
executeRepair,
|
|
1838
|
+
baseline,
|
|
1839
|
+
taskId,
|
|
1840
|
+
variantId
|
|
1841
|
+
} = params;
|
|
1842
|
+
const repairMode = config.repairMode ?? "auto";
|
|
1843
|
+
const scopePolicy = config.scopePolicy ?? "strict";
|
|
1844
|
+
const allowedFiles = config.allowedFiles ?? [];
|
|
1845
|
+
const repairSaveModes = config.repairSaveModes ?? ["patch"];
|
|
1846
|
+
const checkTestGate = config.checkTestGate ?? true;
|
|
1847
|
+
if (!config.enabled || repairMode === "none") {
|
|
1848
|
+
return {
|
|
1849
|
+
verification: { passed: true, passedBaseline: true, findings: [] },
|
|
1850
|
+
verificationLoop: {
|
|
1851
|
+
enabled: false,
|
|
1852
|
+
passed: true,
|
|
1853
|
+
passedBaseline: true,
|
|
1854
|
+
bestIterationIndex: 0,
|
|
1855
|
+
bestIterationPromotable: true,
|
|
1856
|
+
iterations: []
|
|
1857
|
+
}
|
|
1858
|
+
};
|
|
1859
|
+
}
|
|
1860
|
+
const repairBaseRefResult = await execa2("git", ["rev-parse", "HEAD"], {
|
|
1861
|
+
cwd: worktreePath,
|
|
1862
|
+
stdio: "pipe",
|
|
1863
|
+
reject: false
|
|
1864
|
+
});
|
|
1865
|
+
const repairBaseRef = (repairBaseRefResult.exitCode ?? 0) === 0 ? repairBaseRefResult.stdout.trim() : baselineRef;
|
|
1866
|
+
const iterations = [];
|
|
1867
|
+
const controlScopes = /* @__PURE__ */ new Map();
|
|
1868
|
+
const initialCostUsdRaw = Number(params.initialCostUsd ?? 0);
|
|
1869
|
+
const initialCostUsd = Number.isFinite(initialCostUsdRaw) && initialCostUsdRaw > 0 ? initialCostUsdRaw : 0;
|
|
1870
|
+
let cumulativeCostUsd = initialCostUsd;
|
|
1871
|
+
let initialFilesTouched = 0;
|
|
1872
|
+
let initialDiffSize = 0;
|
|
1873
|
+
let stopReason;
|
|
1874
|
+
let bestIterationIndex = 0;
|
|
1875
|
+
const preserved = [];
|
|
1876
|
+
let unverifiedRepairEdits = false;
|
|
1877
|
+
for (let iteration = 1; iteration <= config.maxIterations; iteration++) {
|
|
1878
|
+
const iterStart = Date.now();
|
|
1879
|
+
unverifiedRepairEdits = false;
|
|
1880
|
+
if (iteration === 1) {
|
|
1881
|
+
const [files, diffSize] = await Promise.all([
|
|
1882
|
+
getChangedFiles(worktreePath, baselineRef),
|
|
1883
|
+
getDiffSize(worktreePath, baselineRef)
|
|
1884
|
+
]);
|
|
1885
|
+
initialFilesTouched = files.length;
|
|
1886
|
+
initialDiffSize = Math.max(1, diffSize);
|
|
1887
|
+
}
|
|
1888
|
+
const maxFilesTouched = initialFilesTouched + 1;
|
|
1889
|
+
const diffSizeCap = initialDiffSize * 2;
|
|
1890
|
+
const constraints = {
|
|
1891
|
+
maxFilesTouched,
|
|
1892
|
+
diffSizeCap,
|
|
1893
|
+
apiBreakAllowed: config.allowApiBreak,
|
|
1894
|
+
violations: []
|
|
1895
|
+
};
|
|
1896
|
+
const [currentFiles, currentDiffSize, removedExports] = await Promise.all([
|
|
1897
|
+
getChangedFiles(worktreePath, baselineRef),
|
|
1898
|
+
getDiffSize(worktreePath, baselineRef),
|
|
1899
|
+
detectRemovedExportsInPublicBarrels(worktreePath, baselineRef)
|
|
1900
|
+
]);
|
|
1901
|
+
if (currentFiles.length > maxFilesTouched) {
|
|
1902
|
+
constraints.violations.push(
|
|
1903
|
+
`max_files_touched exceeded: ${currentFiles.length} > ${maxFilesTouched} (${currentFiles.slice(0, 8).join(", ")}${currentFiles.length > 8 ? "\u2026" : ""})`
|
|
1904
|
+
);
|
|
1905
|
+
}
|
|
1906
|
+
if (currentDiffSize > diffSizeCap) {
|
|
1907
|
+
constraints.violations.push(`diff_size_cap exceeded: ${currentDiffSize} > ${diffSizeCap}`);
|
|
1908
|
+
}
|
|
1909
|
+
if (!config.allowApiBreak && removedExports.removedExportLines > 0) {
|
|
1910
|
+
constraints.violations.push(
|
|
1911
|
+
`api_break_detection: removed export lines detected (${removedExports.removedExportLines})${removedExports.sample ? `
|
|
1912
|
+
${removedExports.sample}` : ""}`
|
|
1913
|
+
);
|
|
1914
|
+
}
|
|
1915
|
+
const timeboxMs = config.iterationTimeoutMs;
|
|
1916
|
+
const timeRemainingMs = Math.max(0, timeboxMs - (Date.now() - iterStart));
|
|
1917
|
+
const gateRun = constraints.violations.length ? {
|
|
1918
|
+
passed: false,
|
|
1919
|
+
gates: {
|
|
1920
|
+
build: {
|
|
1921
|
+
status: "failed",
|
|
1922
|
+
exitCode: 1,
|
|
1923
|
+
command: "constraint-check",
|
|
1924
|
+
durationMs: 0,
|
|
1925
|
+
stdoutTail: "",
|
|
1926
|
+
stderrTail: "",
|
|
1927
|
+
outputTail: `Constraint violations:
|
|
1928
|
+
- ${constraints.violations.join("\n- ")}`
|
|
1929
|
+
}
|
|
1930
|
+
}
|
|
1931
|
+
} : await runDeterministicGates(
|
|
1932
|
+
worktreePath,
|
|
1933
|
+
baselineRef,
|
|
1934
|
+
timeRemainingMs,
|
|
1935
|
+
currentFiles,
|
|
1936
|
+
"shared",
|
|
1937
|
+
config.skipTests === true
|
|
1938
|
+
);
|
|
1939
|
+
const controls = requiredVerificationControlChannels(currentFiles);
|
|
1940
|
+
controlScopes.set(gateRun.gates, controls);
|
|
1941
|
+
const passed = gateRun.passed && controlsPassed(gateRun.gates, controls);
|
|
1942
|
+
const durationMs = Date.now() - iterStart;
|
|
1943
|
+
let baselineComparison;
|
|
1944
|
+
let passedBaseline = gatesHaveNoHardFailures(gateRun.gates, checkTestGate) && controlsPassed(gateRun.gates, controls);
|
|
1945
|
+
let newIssueCount = 0;
|
|
1946
|
+
let fixedIssueCount = 0;
|
|
1947
|
+
if (baseline) {
|
|
1948
|
+
const comparisonOutcomes = verificationOutcomesFromGateArtifacts(gateRun.gates, {
|
|
1949
|
+
source: "verification-loop",
|
|
1950
|
+
cwd: worktreePath
|
|
1951
|
+
});
|
|
1952
|
+
baselineComparison = compareVerificationOutcomeToBaseline(baseline, comparisonOutcomes);
|
|
1953
|
+
const gateExitRegressions = cleanBaselineGateExitRegressions(
|
|
1954
|
+
baseline,
|
|
1955
|
+
baselineComparison,
|
|
1956
|
+
comparisonOutcomes,
|
|
1957
|
+
checkTestGate
|
|
1958
|
+
);
|
|
1959
|
+
const hardStructuredNewIssueCount = baselineComparison.delta.build.newIssues.size + (checkTestGate ? baselineComparison.delta.test.newIssues.size : 0);
|
|
1960
|
+
passedBaseline = iterationPassesBaseline({
|
|
1961
|
+
gates: gateRun.gates,
|
|
1962
|
+
checkTestGate,
|
|
1963
|
+
controls,
|
|
1964
|
+
comparison: baselineComparison,
|
|
1965
|
+
provenNewFailures: hardStructuredNewIssueCount > 0 || gateExitRegressions.length > 0,
|
|
1966
|
+
identicalToBaseChannels: channelsFailingIdenticallyOnBase(
|
|
1967
|
+
baseline,
|
|
1968
|
+
gateRun.gates,
|
|
1969
|
+
checkTestGate ? ["build", "test"] : ["build"],
|
|
1970
|
+
[worktreePath]
|
|
1971
|
+
)
|
|
1972
|
+
});
|
|
1973
|
+
const comparisonForCounts = baselineComparison;
|
|
1974
|
+
const unparsedGateRegressionCount = gateExitRegressions.filter((kind) => {
|
|
1975
|
+
if (kind === "build") return comparisonForCounts.delta.build.newIssues.size === 0;
|
|
1976
|
+
return comparisonForCounts.delta.test.newIssues.size === 0;
|
|
1977
|
+
}).length;
|
|
1978
|
+
newIssueCount = hardStructuredNewIssueCount + unparsedGateRegressionCount;
|
|
1979
|
+
fixedIssueCount = baselineComparison.delta.totalFixedIssues;
|
|
1980
|
+
}
|
|
1981
|
+
const record = {
|
|
1982
|
+
iteration,
|
|
1983
|
+
passed,
|
|
1984
|
+
passedBaseline,
|
|
1985
|
+
newIssueCount,
|
|
1986
|
+
fixedIssueCount,
|
|
1987
|
+
durationMs,
|
|
1988
|
+
costUsd: 0,
|
|
1989
|
+
cumulativeCostUsd,
|
|
1990
|
+
constraints,
|
|
1991
|
+
gates: gateRun.gates,
|
|
1992
|
+
baselineComparison
|
|
1993
|
+
};
|
|
1994
|
+
iterations.push(record);
|
|
1995
|
+
if (taskId && variantId && repairSaveModes.length > 0) {
|
|
1996
|
+
try {
|
|
1997
|
+
const info = {
|
|
1998
|
+
iteration,
|
|
1999
|
+
newIssueCount,
|
|
2000
|
+
fixedIssueCount,
|
|
2001
|
+
passed: passedBaseline,
|
|
2002
|
+
durationMs,
|
|
2003
|
+
costUsd: 0
|
|
2004
|
+
};
|
|
2005
|
+
const artifacts = await preserveRepairIteration(
|
|
2006
|
+
worktreePath,
|
|
2007
|
+
repairBaseRef,
|
|
2008
|
+
taskId,
|
|
2009
|
+
variantId,
|
|
2010
|
+
info,
|
|
2011
|
+
{ modes: repairSaveModes }
|
|
2012
|
+
);
|
|
2013
|
+
preserved.push({ info, artifacts });
|
|
2014
|
+
} catch {
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
if (config.abortIfRegression && iterations.length >= 2) {
|
|
2018
|
+
const prev = iterations[iterations.length - 2];
|
|
2019
|
+
const next = iterations[iterations.length - 1];
|
|
2020
|
+
const hasBaselineRegression = baseline && next.newIssueCount > prev.newIssueCount;
|
|
2021
|
+
const hasGateRegression = isRegression(prev.gates, next.gates);
|
|
2022
|
+
const hasTestRegression = checkTestGate && baseline && next.baselineComparison && prev.baselineComparison && next.baselineComparison.delta.test.newIssues.size > prev.baselineComparison.delta.test.newIssues.size;
|
|
2023
|
+
if (hasBaselineRegression || hasGateRegression || hasTestRegression) {
|
|
2024
|
+
stopReason = "abort_if_regression";
|
|
2025
|
+
break;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
const effectivePassed = repairMode === "strict" ? passed : passedBaseline;
|
|
2029
|
+
if (effectivePassed) {
|
|
2030
|
+
stopReason = passed ? "gates_passed" : "no_new_regressions";
|
|
2031
|
+
break;
|
|
2032
|
+
}
|
|
2033
|
+
if (newIssueCount === 0 && onlyUnavailableChannelsRemain(gateRun.gates, {
|
|
2034
|
+
checkTestGate: checkTestGate || repairMode === "strict",
|
|
2035
|
+
controls
|
|
2036
|
+
})) {
|
|
2037
|
+
stopReason = "verification_unavailable";
|
|
2038
|
+
break;
|
|
2039
|
+
}
|
|
2040
|
+
if (iteration >= config.maxIterations) {
|
|
2041
|
+
stopReason = "max_iterations_reached";
|
|
2042
|
+
break;
|
|
2043
|
+
}
|
|
2044
|
+
if (durationMs > config.iterationTimeoutMs) {
|
|
2045
|
+
stopReason = "iteration_timebox_exceeded";
|
|
2046
|
+
break;
|
|
2047
|
+
}
|
|
2048
|
+
if (baseline && iterations.length >= 2) {
|
|
2049
|
+
const prevRecord = iterations[iterations.length - 2];
|
|
2050
|
+
if (prevRecord.newIssueCount === newIssueCount && newIssueCount > 0) {
|
|
2051
|
+
stopReason = "no_progress";
|
|
2052
|
+
break;
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
let prompt;
|
|
2056
|
+
const repairAllowedFiles = mergeRepairAllowedFiles(
|
|
2057
|
+
allowedFiles,
|
|
2058
|
+
deriveAllowedFilesFromGateEvidence(gateRun.gates, baselineComparison)
|
|
2059
|
+
);
|
|
2060
|
+
if (baseline && baselineComparison) {
|
|
2061
|
+
prompt = buildScopedRepairPrompt({
|
|
2062
|
+
originalPrompt,
|
|
2063
|
+
iteration,
|
|
2064
|
+
gates: gateRun.gates,
|
|
2065
|
+
constraints,
|
|
2066
|
+
comparison: baselineComparison,
|
|
2067
|
+
allowedFiles: repairAllowedFiles,
|
|
2068
|
+
scopePolicy
|
|
2069
|
+
});
|
|
2070
|
+
} else {
|
|
2071
|
+
prompt = buildRepairPrompt({
|
|
2072
|
+
originalPrompt,
|
|
2073
|
+
iteration,
|
|
2074
|
+
gates: gateRun.gates,
|
|
2075
|
+
constraints,
|
|
2076
|
+
allowedFiles: repairAllowedFiles,
|
|
2077
|
+
scopePolicy
|
|
2078
|
+
});
|
|
2079
|
+
}
|
|
2080
|
+
unverifiedRepairEdits = true;
|
|
2081
|
+
const repairStart = Date.now();
|
|
2082
|
+
const repairTimeoutMs = Math.max(1, config.iterationTimeoutMs);
|
|
2083
|
+
const repairAbort = new AbortController();
|
|
2084
|
+
let timeoutId;
|
|
2085
|
+
let repairTimedOut = false;
|
|
2086
|
+
const timeoutRepair = new Promise((_, reject) => {
|
|
2087
|
+
timeoutId = setTimeout(() => {
|
|
2088
|
+
repairTimedOut = true;
|
|
2089
|
+
repairAbort.abort();
|
|
2090
|
+
reject(
|
|
2091
|
+
new Error(
|
|
2092
|
+
`executeRepair exceeded iteration timeout (${repairTimeoutMs}ms) at iteration ${iteration}`
|
|
2093
|
+
)
|
|
2094
|
+
);
|
|
2095
|
+
}, repairTimeoutMs);
|
|
2096
|
+
timeoutId.unref?.();
|
|
2097
|
+
});
|
|
2098
|
+
const repairPromise = executeRepair(prompt, iteration - 1, repairAbort.signal);
|
|
2099
|
+
let repair;
|
|
2100
|
+
try {
|
|
2101
|
+
repair = await Promise.race([repairPromise, timeoutRepair]);
|
|
2102
|
+
} catch (err) {
|
|
2103
|
+
repairAbort.abort();
|
|
2104
|
+
repairPromise.catch((repairErr) => {
|
|
2105
|
+
if (repairTimedOut && process.env.BUILD_DEBUG_REPAIR_ABORT === "1") {
|
|
2106
|
+
console.warn(
|
|
2107
|
+
`[verification-loop] repair subprocess exited after abort: ${repairErr instanceof Error ? repairErr.message : String(repairErr)}`
|
|
2108
|
+
);
|
|
2109
|
+
}
|
|
2110
|
+
});
|
|
2111
|
+
stopReason = repairTimedOut ? "iteration_timebox_exceeded" : "repair_execution_failed";
|
|
2112
|
+
record.durationMs = Math.max(record.durationMs, Date.now() - iterStart);
|
|
2113
|
+
if (err instanceof Error) {
|
|
2114
|
+
console.warn(`[verification-loop] ${err.message}`);
|
|
2115
|
+
}
|
|
2116
|
+
break;
|
|
2117
|
+
} finally {
|
|
2118
|
+
if (timeoutId) {
|
|
2119
|
+
clearTimeout(timeoutId);
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
const repairDuration = Date.now() - repairStart;
|
|
2123
|
+
cumulativeCostUsd += Math.max(0, repair.costUsd);
|
|
2124
|
+
record.costUsd = Math.max(0, repair.costUsd);
|
|
2125
|
+
record.cumulativeCostUsd = cumulativeCostUsd;
|
|
2126
|
+
record.durationMs = Math.max(record.durationMs, Date.now() - iterStart);
|
|
2127
|
+
if (preserved.length > 0) {
|
|
2128
|
+
const lastPreserved = preserved[preserved.length - 1];
|
|
2129
|
+
if (lastPreserved?.info.iteration === iteration) {
|
|
2130
|
+
lastPreserved.info.costUsd = record.costUsd;
|
|
2131
|
+
lastPreserved.info.durationMs = record.durationMs;
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
if (Date.now() - iterStart > config.iterationTimeoutMs || repairDuration > config.iterationTimeoutMs) {
|
|
2135
|
+
stopReason = "iteration_timebox_exceeded";
|
|
2136
|
+
break;
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
const lastIteration = iterations.length > 0 ? iterations[iterations.length - 1] : void 0;
|
|
2140
|
+
let effectiveIteration = lastIteration;
|
|
2141
|
+
let finalPassed = false;
|
|
2142
|
+
let finalPassedBaseline = false;
|
|
2143
|
+
const bestChoice = findBestIteration(
|
|
2144
|
+
iterations.map((it) => ({
|
|
2145
|
+
iteration: it.iteration,
|
|
2146
|
+
newIssueCount: it.newIssueCount,
|
|
2147
|
+
fixedIssueCount: it.fixedIssueCount,
|
|
2148
|
+
passed: it.passedBaseline,
|
|
2149
|
+
durationMs: it.durationMs,
|
|
2150
|
+
costUsd: it.costUsd
|
|
2151
|
+
}))
|
|
2152
|
+
);
|
|
2153
|
+
bestIterationIndex = bestChoice?.index ?? 0;
|
|
2154
|
+
let bestIteration = iterations[bestIterationIndex];
|
|
2155
|
+
let bestIterationPromotable = bestIteration !== void 0 && (bestChoice?.iteration.passed ?? false) ? gatesHavePromotableCoverage(
|
|
2156
|
+
bestIteration.gates,
|
|
2157
|
+
checkTestGate,
|
|
2158
|
+
controlScopes.get(bestIteration.gates)
|
|
2159
|
+
) : false;
|
|
2160
|
+
let artifactSelectionFinding;
|
|
2161
|
+
let restoreFinding;
|
|
2162
|
+
let repairArtifacts;
|
|
2163
|
+
if (taskId && variantId && preserved.length > 0) {
|
|
2164
|
+
let bestIndex = bestIteration ? preserved.findIndex((p) => p.info.iteration === bestIteration?.iteration) : -1;
|
|
2165
|
+
if (bestIndex < 0) {
|
|
2166
|
+
const bestPreserved = findBestIteration(preserved.map((p) => p.info));
|
|
2167
|
+
bestIndex = bestPreserved?.index ?? 0;
|
|
2168
|
+
const fallbackIteration = preserved[bestIndex]?.info;
|
|
2169
|
+
if (fallbackIteration) {
|
|
2170
|
+
const fallbackIndex = iterations.findIndex(
|
|
2171
|
+
(it) => it.iteration === fallbackIteration.iteration
|
|
2172
|
+
);
|
|
2173
|
+
if (fallbackIndex >= 0) {
|
|
2174
|
+
bestIterationIndex = fallbackIndex;
|
|
2175
|
+
const selectedIteration = iterations[fallbackIndex];
|
|
2176
|
+
bestIteration = selectedIteration;
|
|
2177
|
+
bestIterationPromotable = fallbackIteration.passed && selectedIteration !== void 0 && gatesHavePromotableCoverage(
|
|
2178
|
+
selectedIteration.gates,
|
|
2179
|
+
checkTestGate,
|
|
2180
|
+
controlScopes.get(selectedIteration.gates)
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
artifactSelectionFinding = {
|
|
2184
|
+
kind: "other",
|
|
2185
|
+
command: "select-best-repair-artifact",
|
|
2186
|
+
exitCode: 0,
|
|
2187
|
+
severity: "warning",
|
|
2188
|
+
outputTail: "Best verification iteration did not have preserved repair artifacts; selected the best preserved iteration instead."
|
|
2189
|
+
};
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
const bestIterationInfo = preserved[bestIndex]?.info ?? preserved[0]?.info;
|
|
2193
|
+
const bestArtifacts = preserved[bestIndex]?.artifacts ?? [];
|
|
2194
|
+
const bestArtifactPath = bestArtifacts.find((a) => a.success)?.artifactPath;
|
|
2195
|
+
repairArtifacts = {
|
|
2196
|
+
taskId,
|
|
2197
|
+
variantId,
|
|
2198
|
+
iterations: preserved,
|
|
2199
|
+
bestIterationIndex: bestIndex,
|
|
2200
|
+
bestIteration: bestIterationInfo,
|
|
2201
|
+
bestArtifactPath
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
if (repairArtifacts && repairArtifacts.iterations.length > 0 && repairArtifacts.bestIteration.passed && repairArtifacts.bestIterationIndex !== repairArtifacts.iterations.length - 1) {
|
|
2205
|
+
const bestArtifacts = repairArtifacts.iterations[repairArtifacts.bestIterationIndex]?.artifacts ?? [];
|
|
2206
|
+
const lastArtifacts = repairArtifacts.iterations[repairArtifacts.iterations.length - 1]?.artifacts ?? [];
|
|
2207
|
+
const bestPatch = bestArtifacts.find((a) => a.success && a.mode === "patch")?.artifactPath;
|
|
2208
|
+
const bestRef = bestArtifacts.find(
|
|
2209
|
+
(a) => a.success && (a.mode === "ref" || a.mode === "branch")
|
|
2210
|
+
)?.artifactPath;
|
|
2211
|
+
const lastPatch = lastArtifacts.find((a) => a.success && a.mode === "patch")?.artifactPath;
|
|
2212
|
+
const lastRef = lastArtifacts.find(
|
|
2213
|
+
(a) => a.success && (a.mode === "ref" || a.mode === "branch")
|
|
2214
|
+
)?.artifactPath;
|
|
2215
|
+
const resetToBase = async () => {
|
|
2216
|
+
await execa2("git", ["reset", "--hard", repairBaseRef], {
|
|
2217
|
+
cwd: worktreePath,
|
|
2218
|
+
stdio: "pipe",
|
|
2219
|
+
reject: false
|
|
2220
|
+
});
|
|
2221
|
+
await execa2("git", ["clean", "-fd"], {
|
|
2222
|
+
cwd: worktreePath,
|
|
2223
|
+
stdio: "pipe",
|
|
2224
|
+
reject: false
|
|
2225
|
+
});
|
|
2226
|
+
};
|
|
2227
|
+
const applyArtifact = async (patchPath, refName) => {
|
|
2228
|
+
if (patchPath) {
|
|
2229
|
+
const applied = await applyPatchFile(worktreePath, patchPath);
|
|
2230
|
+
return applied.success;
|
|
2231
|
+
}
|
|
2232
|
+
if (refName) {
|
|
2233
|
+
const applied = await cherryPickFromRef(worktreePath, refName);
|
|
2234
|
+
return applied.success;
|
|
2235
|
+
}
|
|
2236
|
+
return false;
|
|
2237
|
+
};
|
|
2238
|
+
try {
|
|
2239
|
+
await resetToBase();
|
|
2240
|
+
const ok = await applyArtifact(bestPatch, bestRef);
|
|
2241
|
+
unverifiedRepairEdits = false;
|
|
2242
|
+
if (ok) {
|
|
2243
|
+
effectiveIteration = bestIteration;
|
|
2244
|
+
} else {
|
|
2245
|
+
await resetToBase();
|
|
2246
|
+
const restoredLast = await applyArtifact(lastPatch, lastRef);
|
|
2247
|
+
if (!restoredLast) {
|
|
2248
|
+
throw new Error("failed to restore best or last repair artifact");
|
|
2249
|
+
}
|
|
2250
|
+
effectiveIteration = lastIteration;
|
|
2251
|
+
bestIterationPromotable = lastIteration?.passedBaseline === true && gatesHavePromotableCoverage(
|
|
2252
|
+
lastIteration.gates,
|
|
2253
|
+
checkTestGate,
|
|
2254
|
+
controlScopes.get(lastIteration.gates)
|
|
2255
|
+
);
|
|
2256
|
+
restoreFinding = {
|
|
2257
|
+
kind: "other",
|
|
2258
|
+
command: "restore-best-repair-artifact",
|
|
2259
|
+
exitCode: 0,
|
|
2260
|
+
severity: "warning",
|
|
2261
|
+
outputTail: "Best repair artifact could not be restored; restored the last iteration artifact instead."
|
|
2262
|
+
};
|
|
2263
|
+
}
|
|
2264
|
+
} catch (restoreError) {
|
|
2265
|
+
effectiveIteration = void 0;
|
|
2266
|
+
bestIterationPromotable = false;
|
|
2267
|
+
stopReason = "artifact_restore_failed";
|
|
2268
|
+
restoreFinding = {
|
|
2269
|
+
kind: "other",
|
|
2270
|
+
command: "restore-best-repair-artifact",
|
|
2271
|
+
exitCode: 1,
|
|
2272
|
+
severity: "error",
|
|
2273
|
+
outputTail: restoreError instanceof Error ? restoreError.message : String(restoreError)
|
|
2274
|
+
};
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
if (unverifiedRepairEdits) {
|
|
2278
|
+
bestIterationPromotable = false;
|
|
2279
|
+
}
|
|
2280
|
+
finalPassed = effectiveIteration?.passed ?? false;
|
|
2281
|
+
finalPassedBaseline = effectiveIteration?.passedBaseline ?? false;
|
|
2282
|
+
const findings = effectiveIteration ? buildVerificationFindings(
|
|
2283
|
+
effectiveIteration.gates,
|
|
2284
|
+
effectiveIteration.passedBaseline,
|
|
2285
|
+
controlScopes.get(effectiveIteration.gates)
|
|
2286
|
+
) : [];
|
|
2287
|
+
return {
|
|
2288
|
+
verification: {
|
|
2289
|
+
passed: finalPassed,
|
|
2290
|
+
passedBaseline: finalPassedBaseline,
|
|
2291
|
+
findings: [
|
|
2292
|
+
...findings,
|
|
2293
|
+
...artifactSelectionFinding ? [artifactSelectionFinding] : [],
|
|
2294
|
+
...restoreFinding ? [restoreFinding] : []
|
|
2295
|
+
]
|
|
2296
|
+
},
|
|
2297
|
+
verificationLoop: {
|
|
2298
|
+
enabled: true,
|
|
2299
|
+
passed: finalPassed,
|
|
2300
|
+
passedBaseline: finalPassedBaseline,
|
|
2301
|
+
stopReason: stopReason ?? (finalPassedBaseline ? "no_new_regressions" : "gates_failed"),
|
|
2302
|
+
bestIterationIndex,
|
|
2303
|
+
bestIterationPromotable,
|
|
2304
|
+
iterations: iterations.map((it) => toOutcomeIteration(it, baseline))
|
|
2305
|
+
},
|
|
2306
|
+
repairArtifacts
|
|
2307
|
+
};
|
|
2308
|
+
}
|
|
2309
|
+
|
|
2310
|
+
// src/cli/variant-off-scope-warning.ts
|
|
2311
|
+
function attachOffScopeConfigWarning(result, context) {
|
|
2312
|
+
const prior = (result.warnings ?? []).filter((warning2) => warning2.code === "WS-204");
|
|
2313
|
+
if (prior.length > 0) {
|
|
2314
|
+
result.warnings = (result.warnings ?? []).filter((warning2) => warning2.code !== "WS-204");
|
|
2315
|
+
result.warningCount = Math.max(0, (result.warningCount ?? 0) - prior.length);
|
|
2316
|
+
}
|
|
2317
|
+
delete result.offScopeGateConfig;
|
|
2318
|
+
const diff = result.worktreeDiff;
|
|
2319
|
+
const changedPaths = context.changedPaths ?? (typeof diff === "string" && diff.trim().length > 0 ? extractChangedFilesMeta(diff).map((meta) => meta.path) : []);
|
|
2320
|
+
const edits = findOffScopeConfigEdits(changedPaths, context.prompt);
|
|
2321
|
+
if (edits.length === 0) return;
|
|
2322
|
+
const warning = createWarning(
|
|
2323
|
+
"WS-204",
|
|
2324
|
+
{
|
|
2325
|
+
taskId: context.taskId,
|
|
2326
|
+
subsystem: "variant-worker",
|
|
2327
|
+
executionPhase: "validation"
|
|
2328
|
+
},
|
|
2329
|
+
{
|
|
2330
|
+
metadata: {
|
|
2331
|
+
paths: edits.map((edit) => edit.path),
|
|
2332
|
+
summary: formatOffScopeConfigEdits(edits)
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
);
|
|
2336
|
+
const gateEdits = findGateControllingEdits(edits);
|
|
2337
|
+
if (gateEdits.length > 0) {
|
|
2338
|
+
result.offScopeGateConfig = {
|
|
2339
|
+
paths: gateEdits.map((edit) => edit.path),
|
|
2340
|
+
reason: formatGateConfigUnverifiedReason(gateEdits)
|
|
2341
|
+
};
|
|
2342
|
+
}
|
|
2343
|
+
result.warnings = [...result.warnings ?? [], warning];
|
|
2344
|
+
result.warningCount = (result.warningCount ?? 0) + 1;
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/cli/variant-worker-timeout-snapshot.ts
|
|
2348
|
+
async function resolveTimeoutSnapshotCommit(commit, baselineRef, resolveParent) {
|
|
2349
|
+
const autoCommitFailed = commit.exitCode !== 0 || Boolean(
|
|
2350
|
+
commit.failed || commit.timedOut || commit.isCanceled || commit.isTerminated || commit.signal
|
|
2351
|
+
);
|
|
2352
|
+
if (!autoCommitFailed && !baselineRef) baselineRef = await resolveParent();
|
|
2353
|
+
return { autoCommitFailed, baselineRef };
|
|
2354
|
+
}
|
|
2355
|
+
|
|
2356
|
+
// src/cli/variant-worker.ts
|
|
2357
|
+
var MIN_ANALYSIS_COMPLETENESS = 60;
|
|
2358
|
+
var PARTIAL_GIT_TIMEOUT_MS = 3e3;
|
|
2359
|
+
var SIGNAL_CLEANUP_TIMEOUT_MS = WORKER_CLEANUP_BUDGET_MS;
|
|
2360
|
+
var SIGNAL_CLEANUP_WRAPPER_TIMEOUT_MS = SIGNAL_CLEANUP_TIMEOUT_MS + 200;
|
|
2361
|
+
var POST_PROVIDER_PHASE_WARN_MS = 3e4;
|
|
2362
|
+
function elapsedMsSince(startTime) {
|
|
2363
|
+
return Number((process.hrtime.bigint() - startTime) / 1000000n);
|
|
2364
|
+
}
|
|
2365
|
+
function addPostProviderPhaseDuration(durations, phase, durationMs) {
|
|
2366
|
+
if (!Number.isFinite(durationMs) || durationMs < 0) return;
|
|
2367
|
+
durations[phase] = (durations[phase] ?? 0) + Math.round(durationMs);
|
|
2368
|
+
}
|
|
2369
|
+
async function measurePostProviderPhase(durations, phase, verbose, run) {
|
|
2370
|
+
const started = process.hrtime.bigint();
|
|
2371
|
+
try {
|
|
2372
|
+
return await run();
|
|
2373
|
+
} finally {
|
|
2374
|
+
const durationMs = Number((process.hrtime.bigint() - started) / 1000000n);
|
|
2375
|
+
addPostProviderPhaseDuration(durations, phase, durationMs);
|
|
2376
|
+
if (verbose && durationMs >= POST_PROVIDER_PHASE_WARN_MS) {
|
|
2377
|
+
console.log(chalk.gray(` post-provider phase ${phase} took ${durationMs}ms`));
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
}
|
|
2381
|
+
function measurePostProviderPhaseSync(durations, phase, verbose, run) {
|
|
2382
|
+
const started = process.hrtime.bigint();
|
|
2383
|
+
try {
|
|
2384
|
+
return run();
|
|
2385
|
+
} finally {
|
|
2386
|
+
const durationMs = Number((process.hrtime.bigint() - started) / 1000000n);
|
|
2387
|
+
addPostProviderPhaseDuration(durations, phase, durationMs);
|
|
2388
|
+
if (verbose && durationMs >= POST_PROVIDER_PHASE_WARN_MS) {
|
|
2389
|
+
console.log(chalk.gray(` post-provider phase ${phase} took ${durationMs}ms`));
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
}
|
|
2393
|
+
function buildShutdownAbortReason(signal) {
|
|
2394
|
+
if (signal === "SIGINT") {
|
|
2395
|
+
return new Error("Variant worker received SIGINT");
|
|
2396
|
+
}
|
|
2397
|
+
return new HostTimeoutError("Variant worker received SIGTERM: host time limit reached", {
|
|
2398
|
+
source: VARIANT_WORKER_SIGTERM_SOURCE
|
|
2399
|
+
});
|
|
2400
|
+
}
|
|
2401
|
+
function shouldDowngradeSuccessfulResultForShutdown(input) {
|
|
2402
|
+
if (!input.shutdownRequested) return false;
|
|
2403
|
+
if (input.shutdownRequestedAtMs === void 0) return true;
|
|
2404
|
+
if (input.executionResultSettledAtMs === void 0) return true;
|
|
2405
|
+
return input.shutdownRequestedAtMs <= input.executionResultSettledAtMs;
|
|
2406
|
+
}
|
|
2407
|
+
function activeHandleSummary() {
|
|
2408
|
+
const getActiveHandles = process._getActiveHandles;
|
|
2409
|
+
if (typeof getActiveHandles !== "function") return [];
|
|
2410
|
+
return getActiveHandles().map((handle) => {
|
|
2411
|
+
const record = handle;
|
|
2412
|
+
const type = record.constructor?.name ?? typeof handle;
|
|
2413
|
+
let detail;
|
|
2414
|
+
if (typeof record.fd === "number") {
|
|
2415
|
+
detail = `fd=${record.fd}`;
|
|
2416
|
+
} else if (typeof record.path === "string") {
|
|
2417
|
+
detail = record.path;
|
|
2418
|
+
} else if (typeof record.address === "function") {
|
|
2419
|
+
try {
|
|
2420
|
+
const address = record.address();
|
|
2421
|
+
detail = typeof address === "string" ? address : JSON.stringify(address);
|
|
2422
|
+
} catch {
|
|
2423
|
+
detail = void 0;
|
|
2424
|
+
}
|
|
2425
|
+
}
|
|
2426
|
+
return detail ? { type, detail } : { type };
|
|
2427
|
+
});
|
|
2428
|
+
}
|
|
2429
|
+
function emitWorkerLifecycleMarker(prefix, value) {
|
|
2430
|
+
try {
|
|
2431
|
+
const payload = typeof value === "number" ? String(value) : JSON.stringify(value);
|
|
2432
|
+
process.stdout.write(`${prefix}${payload}
|
|
2433
|
+
`);
|
|
2434
|
+
} catch {
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
function createProviderOutputForwarder(enabled) {
|
|
2438
|
+
if (!enabled) {
|
|
2439
|
+
return { close: () => {
|
|
2440
|
+
} };
|
|
2441
|
+
}
|
|
2442
|
+
const resultStreamer = new ResultStreamer();
|
|
2443
|
+
const listener = (event) => {
|
|
2444
|
+
try {
|
|
2445
|
+
process.stdout.write(`${PROVIDER_OUTPUT_WORKER_EVENT_PREFIX}${JSON.stringify(event)}
|
|
2446
|
+
`);
|
|
2447
|
+
} catch {
|
|
2448
|
+
}
|
|
2449
|
+
};
|
|
2450
|
+
const activityListener = (event) => {
|
|
2451
|
+
try {
|
|
2452
|
+
process.stdout.write(`${PROVIDER_ACTIVITY_WORKER_EVENT_PREFIX}${JSON.stringify(event)}
|
|
2453
|
+
`);
|
|
2454
|
+
} catch {
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
resultStreamer.on("providerOutput", listener);
|
|
2458
|
+
resultStreamer.on("providerActivity", activityListener);
|
|
2459
|
+
return {
|
|
2460
|
+
resultStreamer,
|
|
2461
|
+
close: () => {
|
|
2462
|
+
resultStreamer.off("providerOutput", listener);
|
|
2463
|
+
resultStreamer.off("providerActivity", activityListener);
|
|
2464
|
+
resultStreamer.close();
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
async function tryLoadPackageJsonScripts(repoRoot) {
|
|
2469
|
+
try {
|
|
2470
|
+
const raw = await fs2.readFile(path2.join(repoRoot, "package.json"), "utf-8");
|
|
2471
|
+
const parsed = JSON.parse(raw);
|
|
2472
|
+
const scripts = parsed.scripts && typeof parsed.scripts === "object" ? parsed.scripts : {};
|
|
2473
|
+
return new Set(Object.keys(scripts));
|
|
2474
|
+
} catch {
|
|
2475
|
+
return void 0;
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
function extractOriginalPrompt(fullPrompt) {
|
|
2479
|
+
const parts = fullPrompt.split(/\n\n---\n\n/);
|
|
2480
|
+
const first = parts[0];
|
|
2481
|
+
if (first?.trimStart().startsWith("[SPEC:") && parts.length > 1) {
|
|
2482
|
+
return parts.slice(1).join("\n\n---\n\n").trim();
|
|
2483
|
+
}
|
|
2484
|
+
return (first ?? fullPrompt).trim();
|
|
2485
|
+
}
|
|
2486
|
+
function toBaselineComparisonResult(comparison) {
|
|
2487
|
+
return {
|
|
2488
|
+
verdict: comparison.verdict,
|
|
2489
|
+
summary: comparison.summary,
|
|
2490
|
+
baselineRef: comparison.baselineRef,
|
|
2491
|
+
tests: {
|
|
2492
|
+
newFailures: comparison.tests.newFailures,
|
|
2493
|
+
fixedIssues: comparison.tests.fixedIssues,
|
|
2494
|
+
persistingIssues: comparison.tests.persistingIssues
|
|
2495
|
+
},
|
|
2496
|
+
types: {
|
|
2497
|
+
newFailures: comparison.types.newFailures,
|
|
2498
|
+
fixedIssues: comparison.types.fixedIssues,
|
|
2499
|
+
persistingIssues: comparison.types.persistingIssues
|
|
2500
|
+
},
|
|
2501
|
+
lint: {
|
|
2502
|
+
newFailures: comparison.lint.newFailures,
|
|
2503
|
+
fixedIssues: comparison.lint.fixedIssues,
|
|
2504
|
+
persistingIssues: comparison.lint.persistingIssues
|
|
2505
|
+
}
|
|
2506
|
+
};
|
|
2507
|
+
}
|
|
2508
|
+
function shouldTrustVerificationLoopBaseline(result) {
|
|
2509
|
+
const loop = result.verificationLoop;
|
|
2510
|
+
const last = latestVerificationIteration(loop);
|
|
2511
|
+
if (hasVerifiedTouchedTestFailure(result) && !repairOutcomeCanSupersedeRawTouchedFailure(result)) {
|
|
2512
|
+
return false;
|
|
2513
|
+
}
|
|
2514
|
+
return loop?.enabled === true && loop.passed === true && loop.passedBaseline === true && (last?.newIssueCount ?? 0) === 0;
|
|
2515
|
+
}
|
|
2516
|
+
function isValidationArtifactOnlyPrompt(prompt) {
|
|
2517
|
+
const hasValidationOnlyDirective = /\bvalidation(?:\s+run)?\s+only\b/i.test(prompt);
|
|
2518
|
+
const targetsValidationArtifact = /\bvalidation\s+artifact\b/i.test(prompt) || hasValidationArtifactTarget(prompt);
|
|
2519
|
+
const forbidsOtherFileChanges = /\b(?:do\s+not|don't)\s+modify\s+(?:any\s+)?other\s+files\b/i.test(prompt) || /\bmodify\s+no\s+other\s+files\b/i.test(prompt);
|
|
2520
|
+
return hasValidationOnlyDirective && targetsValidationArtifact && forbidsOtherFileChanges;
|
|
2521
|
+
}
|
|
2522
|
+
async function isValidationArtifactOnlyDiffSatisfied(input) {
|
|
2523
|
+
if (!input.worktreePath || !isValidationArtifactOnlyPrompt(input.prompt)) return false;
|
|
2524
|
+
try {
|
|
2525
|
+
const summary = await getGitChangeSummary(input.worktreePath, input.baselineRef, true);
|
|
2526
|
+
return summary.changedPaths.length > 0 && summary.changedPaths.every((changedPath) => isValidationArtifactEntry(changedPath));
|
|
2527
|
+
} catch {
|
|
2528
|
+
return false;
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
async function resolveInitialVerificationDisposition(result, runFallbackVerification) {
|
|
2532
|
+
const dispositionOf = (fresh = false) => getVerificationResult({
|
|
2533
|
+
...result,
|
|
2534
|
+
...fresh ? { outcome: void 0 } : {},
|
|
2535
|
+
offScopeGateConfig: void 0
|
|
2536
|
+
});
|
|
2537
|
+
const provider = dispositionOf();
|
|
2538
|
+
if (provider.outcome !== "passed" || result.verificationLoop) return provider;
|
|
2539
|
+
await runFallbackVerification();
|
|
2540
|
+
const observed = dispositionOf(true);
|
|
2541
|
+
return observed.outcome === "failed" ? observed : provider;
|
|
2542
|
+
}
|
|
2543
|
+
function decideRepairAdmission(input) {
|
|
2544
|
+
const baseEvidence = {
|
|
2545
|
+
...input.initialVerificationOutcome ? { initialVerificationOutcome: input.initialVerificationOutcome } : {},
|
|
2546
|
+
hasMissingFileTargets: input.hasMissingFileTargets,
|
|
2547
|
+
isClarifyingNoOp: input.isClarifyingNoOp,
|
|
2548
|
+
providerExecutionSucceeded: input.providerExecutionSucceeded,
|
|
2549
|
+
hasMaterializedDeliverable: input.hasMaterializedDeliverable,
|
|
2550
|
+
validationArtifactSatisfied: input.validationArtifactSatisfied,
|
|
2551
|
+
filesChanged: input.filesChanged
|
|
2552
|
+
};
|
|
2553
|
+
if (!input.repairLoopEnabled) {
|
|
2554
|
+
return {
|
|
2555
|
+
action: "skip",
|
|
2556
|
+
reason: "skip_disabled",
|
|
2557
|
+
summary: "Repair loop disabled",
|
|
2558
|
+
...baseEvidence
|
|
2559
|
+
};
|
|
2560
|
+
}
|
|
2561
|
+
if (input.isAnalysisIntent) {
|
|
2562
|
+
return {
|
|
2563
|
+
action: "skip",
|
|
2564
|
+
reason: "skip_analysis_intent",
|
|
2565
|
+
summary: "Skipped repair loop for analysis intent",
|
|
2566
|
+
...baseEvidence
|
|
2567
|
+
};
|
|
2568
|
+
}
|
|
2569
|
+
if (!input.hasRetainedWorktree || !input.hasBaselineRef) {
|
|
2570
|
+
return {
|
|
2571
|
+
action: "skip",
|
|
2572
|
+
reason: "skip_missing_worktree",
|
|
2573
|
+
summary: "Skipped repair loop because retained worktree or baseline ref is unavailable",
|
|
2574
|
+
...baseEvidence
|
|
2575
|
+
};
|
|
2576
|
+
}
|
|
2577
|
+
if (!input.providerExecutionSucceeded) {
|
|
2578
|
+
return {
|
|
2579
|
+
action: "skip",
|
|
2580
|
+
reason: "skip_provider_failed",
|
|
2581
|
+
summary: "Skipped repair loop because provider execution failed",
|
|
2582
|
+
...baseEvidence
|
|
2583
|
+
};
|
|
2584
|
+
}
|
|
2585
|
+
if (!input.hasMaterializedDeliverable) {
|
|
2586
|
+
return {
|
|
2587
|
+
action: "skip",
|
|
2588
|
+
reason: "skip_no_deliverable",
|
|
2589
|
+
summary: "Skipped repair loop because provider produced no materialized deliverable",
|
|
2590
|
+
...baseEvidence
|
|
2591
|
+
};
|
|
2592
|
+
}
|
|
2593
|
+
if (input.validationArtifactSatisfied === true) {
|
|
2594
|
+
return {
|
|
2595
|
+
action: "skip",
|
|
2596
|
+
reason: "skip_validation_artifact_satisfied",
|
|
2597
|
+
summary: "Skipped repair loop because validation artifact target is already satisfied",
|
|
2598
|
+
...baseEvidence
|
|
2599
|
+
};
|
|
2600
|
+
}
|
|
2601
|
+
if (input.providerGatesAlreadyPassed) {
|
|
2602
|
+
return {
|
|
2603
|
+
action: "skip",
|
|
2604
|
+
reason: "skip_passing_gates",
|
|
2605
|
+
summary: "Skipped repair loop: provider verification already passed",
|
|
2606
|
+
...baseEvidence
|
|
2607
|
+
};
|
|
2608
|
+
}
|
|
2609
|
+
if (input.verificationUnavailable) {
|
|
2610
|
+
return {
|
|
2611
|
+
action: "skip",
|
|
2612
|
+
reason: "skip_verification_unavailable",
|
|
2613
|
+
summary: "Skipped repair loop: verification is unavailable in this environment",
|
|
2614
|
+
...baseEvidence
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
if (input.hasMissingFileTargets && input.isClarifyingNoOp) {
|
|
2618
|
+
return {
|
|
2619
|
+
action: "skip",
|
|
2620
|
+
reason: "skip_clarifying_noop",
|
|
2621
|
+
summary: "Skipped repair loop: missing FILE targets + clarifying no-op response",
|
|
2622
|
+
...baseEvidence
|
|
2623
|
+
};
|
|
2624
|
+
}
|
|
2625
|
+
return {
|
|
2626
|
+
action: "run",
|
|
2627
|
+
reason: "run_gate_failure",
|
|
2628
|
+
summary: "Repair loop admitted for deterministic gate failure",
|
|
2629
|
+
...baseEvidence
|
|
2630
|
+
};
|
|
2631
|
+
}
|
|
2632
|
+
function repairPolicyCompletionMode() {
|
|
2633
|
+
const raw = readEnv(BUILD_COMPLETION_MODE).value;
|
|
2634
|
+
return raw === "branch" || raw === "pr" || raw === "isolated" ? raw : "isolated";
|
|
2635
|
+
}
|
|
2636
|
+
function isOffScopeRepairOverrideEnabled() {
|
|
2637
|
+
return readEnv(BUILD_ALLOW_OFF_SCOPE_REPAIR).value === true;
|
|
2638
|
+
}
|
|
2639
|
+
function resolveRepairPromotionPolicy(input) {
|
|
2640
|
+
const outsideFiles = input.repairDelta?.filesOutsidePreRepairDiff ?? [];
|
|
2641
|
+
const mechanicalFiles = new Set(input.repairDelta?.mechanicalAllowlistMatches ?? []);
|
|
2642
|
+
const strictScopeExpansion = input.scopePolicy === "strict" && outsideFiles.some((file) => !mechanicalFiles.has(file));
|
|
2643
|
+
const classificationForPolicy = strictScopeExpansion && input.classification !== "off_scope" ? "off_scope" : input.classification;
|
|
2644
|
+
const policy = resolveRepairArtifactPolicy({
|
|
2645
|
+
classification: classificationForPolicy,
|
|
2646
|
+
effectiveMode: input.effectiveMode,
|
|
2647
|
+
allowOffScopeRepair: input.allowOffScopeRepair
|
|
2648
|
+
});
|
|
2649
|
+
return strictScopeExpansion ? {
|
|
2650
|
+
...policy,
|
|
2651
|
+
reason: `${policy.reason}; strict repair scope forbids non-mechanical files outside the provider diff`
|
|
2652
|
+
} : policy;
|
|
2653
|
+
}
|
|
2654
|
+
async function readWorktreeHeadCommit(worktreePath) {
|
|
2655
|
+
const head = await execa3("git", ["rev-parse", "HEAD"], {
|
|
2656
|
+
cwd: worktreePath,
|
|
2657
|
+
stdio: "pipe",
|
|
2658
|
+
reject: false
|
|
2659
|
+
});
|
|
2660
|
+
if ((head.exitCode ?? 1) !== 0) return void 0;
|
|
2661
|
+
const commit = String(head.stdout ?? "").trim();
|
|
2662
|
+
return commit.length > 0 ? commit : void 0;
|
|
2663
|
+
}
|
|
2664
|
+
async function captureRepairDiffSnapshot(worktreePath, baselineRef) {
|
|
2665
|
+
try {
|
|
2666
|
+
const [commit, diff] = await Promise.all([
|
|
2667
|
+
readWorktreeHeadCommit(worktreePath),
|
|
2668
|
+
captureWorktreeDiff(worktreePath, baselineRef, true)
|
|
2669
|
+
]);
|
|
2670
|
+
return buildRepairDiffSnapshot(diff, commit);
|
|
2671
|
+
} catch {
|
|
2672
|
+
return void 0;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
function buildRepairTriggerEvidence(repairOutcome) {
|
|
2676
|
+
const firstIteration = repairOutcome.verificationLoop?.iterations?.[0];
|
|
2677
|
+
const stopReason = typeof repairOutcome.verificationLoop?.stopReason === "string" ? repairOutcome.verificationLoop.stopReason : void 0;
|
|
2678
|
+
if (!firstIteration) {
|
|
2679
|
+
return {
|
|
2680
|
+
type: "unknown",
|
|
2681
|
+
gateId: "verification-loop",
|
|
2682
|
+
summary: stopReason ? `Verification-driven repair loop recorded no iterations; stopReason=${stopReason}` : "Verification-driven repair loop recorded no iterations"
|
|
2683
|
+
};
|
|
2684
|
+
}
|
|
2685
|
+
const failedGate = firstIteration.build?.status !== "passed" ? "build" : firstIteration.lint && firstIteration.lint.status !== "passed" ? "lint" : firstIteration.tests && firstIteration.tests.status !== "passed" ? "test" : void 0;
|
|
2686
|
+
if (!failedGate) {
|
|
2687
|
+
return {
|
|
2688
|
+
type: "policy",
|
|
2689
|
+
gateId: "verification-loop",
|
|
2690
|
+
summary: stopReason ? `Verification policy ran without an initial gate failure; stopReason=${stopReason}` : "Verification policy ran without an initial gate failure"
|
|
2691
|
+
};
|
|
2692
|
+
}
|
|
2693
|
+
const failedConstraintCheck = firstIteration.build?.command === "constraint-check" || firstIteration.lint?.command === "constraint-check" || firstIteration.tests?.command === "constraint-check";
|
|
2694
|
+
if (failedConstraintCheck) {
|
|
2695
|
+
return {
|
|
2696
|
+
type: "policy",
|
|
2697
|
+
gateId: "constraint-check",
|
|
2698
|
+
summary: stopReason ? `Verification repair was triggered by policy constraint failure; stopReason=${stopReason}` : "Verification repair was triggered by policy constraint failure"
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
return {
|
|
2702
|
+
type: "gate_failure",
|
|
2703
|
+
gateId: failedGate,
|
|
2704
|
+
summary: stopReason ? `Verification repair was triggered by initial ${failedGate} gate failure; stopReason=${stopReason}` : `Verification repair was triggered by initial ${failedGate} gate failure`
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
async function taskScopedChangedFiles(worktreePath, baselineRef) {
|
|
2708
|
+
try {
|
|
2709
|
+
const diff = await captureWorktreeDiff(worktreePath, baselineRef, true);
|
|
2710
|
+
if (!diff || diff.trim().length === 0) return [];
|
|
2711
|
+
return extractChangedFilesMeta(diff).map((meta) => meta.path).filter(Boolean);
|
|
2712
|
+
} catch {
|
|
2713
|
+
return [];
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
function coerceComparisonGateStatus(status) {
|
|
2717
|
+
switch (status) {
|
|
2718
|
+
case "passed":
|
|
2719
|
+
case "failed":
|
|
2720
|
+
case "skipped":
|
|
2721
|
+
case "timeout":
|
|
2722
|
+
case "error":
|
|
2723
|
+
case "unavailable":
|
|
2724
|
+
case "not_run":
|
|
2725
|
+
return status;
|
|
2726
|
+
default:
|
|
2727
|
+
return void 0;
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
function statusFromExitCode(exitCode) {
|
|
2731
|
+
return exitCode === 0 ? "passed" : "failed";
|
|
2732
|
+
}
|
|
2733
|
+
function toGateArtifactStatus(status) {
|
|
2734
|
+
return status === "not_run" ? "skipped" : status;
|
|
2735
|
+
}
|
|
2736
|
+
function verificationBaselineCacheDir() {
|
|
2737
|
+
return path2.join(resolveBuildDirFromContext(), "cache", "verification-baseline");
|
|
2738
|
+
}
|
|
2739
|
+
async function attachBaselineComparisonIfAvailable(result) {
|
|
2740
|
+
const baselineRef = typeof result.baselineRef === "string" ? result.baselineRef.trim() : "";
|
|
2741
|
+
if (!baselineRef) return;
|
|
2742
|
+
const last = latestVerificationIteration(result.verificationLoop);
|
|
2743
|
+
if (!last) return;
|
|
2744
|
+
if (shouldTrustVerificationLoopBaseline(result)) {
|
|
2745
|
+
result.baselineComparison = trustedLoopBaselineComparison(baselineRef, last);
|
|
2746
|
+
return;
|
|
2747
|
+
}
|
|
2748
|
+
const repoRoot = process.cwd();
|
|
2749
|
+
const cacheDir = verificationBaselineCacheDir();
|
|
2750
|
+
const changedFiles = typeof result.worktreeDiff === "string" && result.worktreeDiff.trim().length > 0 ? extractChangedFilesMeta(result.worktreeDiff).map((meta) => meta.path).filter(Boolean) : [];
|
|
2751
|
+
const baseline = await getOrCaptureVerificationBaseline({
|
|
2752
|
+
repoRoot,
|
|
2753
|
+
baselineRef,
|
|
2754
|
+
cacheDir,
|
|
2755
|
+
captureOptions: { changedFiles }
|
|
2756
|
+
});
|
|
2757
|
+
if (!baseline.metadata?.captureSucceeded) return;
|
|
2758
|
+
const currentArtifacts = {
|
|
2759
|
+
build: {
|
|
2760
|
+
exitCode: last.build.exitCode,
|
|
2761
|
+
outputTail: last.build.outputTail,
|
|
2762
|
+
command: last.build.command,
|
|
2763
|
+
durationMs: 0,
|
|
2764
|
+
stdoutTail: "",
|
|
2765
|
+
stderrTail: "",
|
|
2766
|
+
language: last.build.language,
|
|
2767
|
+
status: toGateArtifactStatus(
|
|
2768
|
+
coerceComparisonGateStatus(last.build.status) ?? statusFromExitCode(last.build.exitCode)
|
|
2769
|
+
)
|
|
2770
|
+
},
|
|
2771
|
+
lint: last.lint ? {
|
|
2772
|
+
exitCode: last.lint.exitCode,
|
|
2773
|
+
outputTail: last.lint.outputTail,
|
|
2774
|
+
command: last.lint.command,
|
|
2775
|
+
durationMs: 0,
|
|
2776
|
+
language: last.lint.language,
|
|
2777
|
+
status: toGateArtifactStatus(
|
|
2778
|
+
coerceComparisonGateStatus(last.lint.status) ?? statusFromExitCode(last.lint.exitCode)
|
|
2779
|
+
)
|
|
2780
|
+
} : void 0,
|
|
2781
|
+
tests: last.tests ? {
|
|
2782
|
+
exitCode: last.tests.exitCode,
|
|
2783
|
+
status: toGateArtifactStatus(
|
|
2784
|
+
coerceComparisonGateStatus(last.tests.status) ?? statusFromExitCode(last.tests.exitCode)
|
|
2785
|
+
),
|
|
2786
|
+
failingTests: last.tests.failingTests,
|
|
2787
|
+
failingTestIdentities: last.tests.failingTestIdentities,
|
|
2788
|
+
outputTail: last.tests.outputTail,
|
|
2789
|
+
command: last.tests.command,
|
|
2790
|
+
durationMs: 0,
|
|
2791
|
+
language: last.tests.language
|
|
2792
|
+
} : void 0
|
|
2793
|
+
};
|
|
2794
|
+
const currentOutcomes = verificationOutcomesFromGateArtifacts(currentArtifacts, {
|
|
2795
|
+
cwd: repoRoot,
|
|
2796
|
+
source: "variant-worker"
|
|
2797
|
+
});
|
|
2798
|
+
const stableComparison = compareVerificationOutcomeToBaseline(baseline, currentOutcomes);
|
|
2799
|
+
result.baselineComparison = markLintIdenticalToBase(
|
|
2800
|
+
toBaselineComparisonResult(stableComparisonToBaselineComparison(stableComparison)),
|
|
2801
|
+
// Per-command lint results, so a lint gate of several commands is never matched.
|
|
2802
|
+
channelsFailingIdenticallyOnBase(
|
|
2803
|
+
baseline,
|
|
2804
|
+
{
|
|
2805
|
+
...currentArtifacts,
|
|
2806
|
+
lint: currentArtifacts.lint && {
|
|
2807
|
+
...currentArtifacts.lint,
|
|
2808
|
+
// The loop records the gate as run; its stored type lags the per-command field.
|
|
2809
|
+
commandResults: last.lint?.commandResults
|
|
2810
|
+
}
|
|
2811
|
+
},
|
|
2812
|
+
["lint"],
|
|
2813
|
+
[repoRoot]
|
|
2814
|
+
)
|
|
2815
|
+
);
|
|
2816
|
+
}
|
|
2817
|
+
async function applyRegistryDowngradeIfApplicable(result) {
|
|
2818
|
+
const comparison = result.baselineComparison;
|
|
2819
|
+
if (!comparison || comparison.summary.shouldPenalize !== true) return;
|
|
2820
|
+
const testNewFailures = comparison.tests?.newFailures ?? [];
|
|
2821
|
+
if (testNewFailures.length === 0) return;
|
|
2822
|
+
if ((comparison.types?.newFailures?.length ?? 0) > 0) return;
|
|
2823
|
+
if ((comparison.lint?.newFailures?.length ?? 0) > 0) return;
|
|
2824
|
+
const last = latestVerificationIteration(result.verificationLoop);
|
|
2825
|
+
const testOutputTail = last?.tests?.outputTail ?? "";
|
|
2826
|
+
const registry = await loadKnownPreExistingFailures(process.cwd());
|
|
2827
|
+
const baselineRef = (result.baselineRef ?? comparison.baselineRef)?.trim().toLowerCase();
|
|
2828
|
+
const registryBaseCommit = registry.baseCommit?.trim().toLowerCase();
|
|
2829
|
+
const MIN_SHA_PREFIX_LEN = 7;
|
|
2830
|
+
if (!baselineRef || !registryBaseCommit || baselineRef.length < MIN_SHA_PREFIX_LEN || registryBaseCommit.length < MIN_SHA_PREFIX_LEN || !baselineRef.startsWith(registryBaseCommit) && !registryBaseCommit.startsWith(baselineRef)) {
|
|
2831
|
+
return;
|
|
2832
|
+
}
|
|
2833
|
+
if (!allFailuresAreRegistered(testNewFailures, registry, testOutputTail)) {
|
|
2834
|
+
return;
|
|
2835
|
+
}
|
|
2836
|
+
applyKnownFailureDowngrade(comparison, testNewFailures.length);
|
|
2837
|
+
const matchedNames = testNewFailures.join(", ");
|
|
2838
|
+
process.stderr.write(
|
|
2839
|
+
`[build:registry] Downgraded shouldPenalize=true \u2192 false for ${testNewFailures.length} registered failure(s): ${matchedNames}
|
|
2840
|
+
`
|
|
2841
|
+
);
|
|
2842
|
+
}
|
|
2843
|
+
function getWarningCodes(result) {
|
|
2844
|
+
const warnings = result.warnings;
|
|
2845
|
+
if (!Array.isArray(warnings)) return /* @__PURE__ */ new Set();
|
|
2846
|
+
return new Set(
|
|
2847
|
+
warnings.map((w) => w?.code).filter((code) => typeof code === "string" && code.trim().length > 0)
|
|
2848
|
+
);
|
|
2849
|
+
}
|
|
2850
|
+
function looksLikeClarifyingQuestion(text) {
|
|
2851
|
+
const lower = text.toLowerCase();
|
|
2852
|
+
if (lower.includes("?")) return true;
|
|
2853
|
+
return /(\bdoes(?:n't| not)\s+exist\b|\bnot\s+found\b|\bcannot\s+find\b|\bcan't\s+find\b|\bcould\s+you\s+clarify\b|\bplease\s+clarify\b|\bwhich\s+file\b|\bwhat\s+file\b)/i.test(
|
|
2854
|
+
lower
|
|
2855
|
+
);
|
|
2856
|
+
}
|
|
2857
|
+
initializeLabModeFromEnv("initializing-worker");
|
|
2858
|
+
function formatElapsedRuntime(elapsedMs) {
|
|
2859
|
+
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
2860
|
+
const minutes = Math.floor(totalSeconds / 60);
|
|
2861
|
+
const seconds = totalSeconds % 60;
|
|
2862
|
+
return `${minutes}m ${seconds}s`;
|
|
2863
|
+
}
|
|
2864
|
+
async function runWithTimeout(promise, timeoutMs, label) {
|
|
2865
|
+
let timeoutHandle;
|
|
2866
|
+
try {
|
|
2867
|
+
return await Promise.race([
|
|
2868
|
+
promise,
|
|
2869
|
+
new Promise((_, reject) => {
|
|
2870
|
+
timeoutHandle = setTimeout(() => {
|
|
2871
|
+
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
2872
|
+
}, timeoutMs);
|
|
2873
|
+
})
|
|
2874
|
+
]);
|
|
2875
|
+
} finally {
|
|
2876
|
+
if (timeoutHandle) {
|
|
2877
|
+
clearTimeout(timeoutHandle);
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
function parseRepairLoopConfig() {
|
|
2882
|
+
const enabledEnv = process.env.BUILD_REPAIR_LOOP_ENABLED;
|
|
2883
|
+
const enabled = enabledEnv === "1";
|
|
2884
|
+
const maxIterationsRaw = Number(process.env.BUILD_REPAIR_LOOP_MAX_ITERATIONS);
|
|
2885
|
+
const maxIterations = Number.isFinite(maxIterationsRaw) && maxIterationsRaw > 0 ? maxIterationsRaw : DEFAULT_REPAIR_LOOP_MAX_ITERATIONS;
|
|
2886
|
+
const iterationTimeoutMsRaw = Number(process.env.BUILD_REPAIR_LOOP_ITERATION_TIMEOUT_MS);
|
|
2887
|
+
const iterationTimeoutMs = Number.isFinite(iterationTimeoutMsRaw) && iterationTimeoutMsRaw > 0 ? iterationTimeoutMsRaw : DEFAULT_REPAIR_LOOP_ITERATION_TIMEOUT_MS;
|
|
2888
|
+
const abortIfRegression = process.env.BUILD_REPAIR_LOOP_ABORT_IF_REGRESSION !== "0";
|
|
2889
|
+
const allowApiBreak = process.env.BUILD_ALLOW_API_BREAK === "1";
|
|
2890
|
+
const repairModeEnv = process.env.BUILD_REPAIR_MODE;
|
|
2891
|
+
const repairMode = repairModeEnv === "strict" || repairModeEnv === "none" || repairModeEnv === "auto" ? repairModeEnv : "auto";
|
|
2892
|
+
const scopePolicyEnv = process.env.BUILD_REPAIR_SCOPE_POLICY;
|
|
2893
|
+
const scopePolicy = scopePolicyEnv === "expand" ? "expand" : "strict";
|
|
2894
|
+
const saveModesEnv = process.env.BUILD_REPAIR_SAVE_MODES;
|
|
2895
|
+
const repairSaveModes = (saveModesEnv?.trim().length ? saveModesEnv : "patch").split(",").map((m) => m.trim()).filter(Boolean).filter((m) => m === "patch" || m === "ref" || m === "branch");
|
|
2896
|
+
const normalizedRepairSaveModes = repairSaveModes.length > 0 ? repairSaveModes : ["patch"];
|
|
2897
|
+
return {
|
|
2898
|
+
enabled,
|
|
2899
|
+
maxIterations,
|
|
2900
|
+
iterationTimeoutMs,
|
|
2901
|
+
abortIfRegression,
|
|
2902
|
+
allowApiBreak,
|
|
2903
|
+
repairMode,
|
|
2904
|
+
scopePolicy,
|
|
2905
|
+
repairSaveModes: [...normalizedRepairSaveModes]
|
|
2906
|
+
};
|
|
2907
|
+
}
|
|
2908
|
+
async function tryCleanupRetainedWorktree(worktreePath, verbose) {
|
|
2909
|
+
const normalized = path2.resolve(worktreePath);
|
|
2910
|
+
const buildRoot = resolveBuildRoot();
|
|
2911
|
+
const allowedRoots = [
|
|
2912
|
+
path2.resolve(getWorktreeBaseDir(buildRoot)),
|
|
2913
|
+
path2.resolve(buildRoot, ".build-project", "tmp", "worktrees"),
|
|
2914
|
+
path2.resolve(buildRoot, "build-worktrees"),
|
|
2915
|
+
path2.join("/tmp", "build-worktrees"),
|
|
2916
|
+
path2.join(process.env.TMPDIR ?? "/tmp", "build-worktrees")
|
|
2917
|
+
];
|
|
2918
|
+
const allowed = allowedRoots.some((root) => {
|
|
2919
|
+
const rootWithSep = root.endsWith(path2.sep) ? root : `${root}${path2.sep}`;
|
|
2920
|
+
return normalized === root || normalized.startsWith(rootWithSep);
|
|
2921
|
+
});
|
|
2922
|
+
if (!allowed) {
|
|
2923
|
+
if (verbose) {
|
|
2924
|
+
console.warn(
|
|
2925
|
+
chalk.yellow(`Refusing to cleanup retained worktree outside known roots: ${worktreePath}`)
|
|
2926
|
+
);
|
|
2927
|
+
}
|
|
2928
|
+
return;
|
|
2929
|
+
}
|
|
2930
|
+
try {
|
|
2931
|
+
await execa3("git", ["worktree", "remove", "--force", normalized], {
|
|
2932
|
+
cwd: buildRoot,
|
|
2933
|
+
stdio: "pipe",
|
|
2934
|
+
timeout: 3e4,
|
|
2935
|
+
reject: false
|
|
2936
|
+
});
|
|
2937
|
+
} catch (error) {
|
|
2938
|
+
if (verbose) {
|
|
2939
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
2940
|
+
console.warn(chalk.yellow(`Failed to cleanup retained worktree: ${msg}`));
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
async function ensureGitMetrics(result) {
|
|
2945
|
+
if (result.gitMetrics) return;
|
|
2946
|
+
const worktreePath = result.retainedWorktreePath;
|
|
2947
|
+
if (worktreePath) {
|
|
2948
|
+
const summary = await getGitChangeSummary(worktreePath, result.baselineRef, true);
|
|
2949
|
+
if (summary.filesChanged > 0) {
|
|
2950
|
+
result.gitMetrics = {
|
|
2951
|
+
filesChanged: summary.filesChanged,
|
|
2952
|
+
linesAdded: summary.linesAdded,
|
|
2953
|
+
linesDeleted: summary.linesRemoved,
|
|
2954
|
+
source: summary.source === "fallback" ? "unknown" : "worktree"
|
|
2955
|
+
};
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
if (result.worktreeSummary) {
|
|
2960
|
+
result.gitMetrics = {
|
|
2961
|
+
filesChanged: result.worktreeSummary.filesChanged,
|
|
2962
|
+
linesAdded: result.worktreeSummary.insertions,
|
|
2963
|
+
linesDeleted: result.worktreeSummary.deletions,
|
|
2964
|
+
source: "unknown"
|
|
2965
|
+
};
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
async function findVariantWorktreePathForTimeout(taskId, variant, repoRoot) {
|
|
2969
|
+
try {
|
|
2970
|
+
const listResult = await execa3("git", ["worktree", "list", "--porcelain"], {
|
|
2971
|
+
cwd: repoRoot,
|
|
2972
|
+
reject: false,
|
|
2973
|
+
timeout: PARTIAL_GIT_TIMEOUT_MS
|
|
2974
|
+
});
|
|
2975
|
+
if ((listResult.exitCode ?? 0) !== 0 || !listResult.stdout?.trim()) {
|
|
2976
|
+
return void 0;
|
|
2977
|
+
}
|
|
2978
|
+
const expectedBranch = generateBranchName(taskId, variant);
|
|
2979
|
+
return parseGitWorktreeBranches(listResult.stdout).find(
|
|
2980
|
+
(entry) => entry.branch === expectedBranch
|
|
2981
|
+
)?.path;
|
|
2982
|
+
} catch {
|
|
2983
|
+
return void 0;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
async function main() {
|
|
2987
|
+
const program = new Command();
|
|
2988
|
+
program.name("variant-worker").description("Execute a single variant task and write result to file").requiredOption("--task-id <id>", "Task identifier").requiredOption("--variant <json>", "Variant configuration as JSON string").requiredOption("--prompt-file <path>", "Path to file containing prompt text").option(
|
|
2989
|
+
"--original-task-file <path>",
|
|
2990
|
+
"Path to file containing the original task text before prompt injection"
|
|
2991
|
+
).requiredOption("--result-file <path>", "Path to write result JSON").option(
|
|
2992
|
+
"--partial-result-file <path>",
|
|
2993
|
+
"Path to write timeout-partial result JSON (best-effort)"
|
|
2994
|
+
).option("--timeout-minutes <minutes>", "Execution timeout in minutes", parseFloat).option("--deadline-at <epochMs>", "Absolute provider deadline (epoch ms)", parseFloat).option(
|
|
2995
|
+
"--idle-warn-minutes <minutes>",
|
|
2996
|
+
"Provider idle warning threshold in minutes",
|
|
2997
|
+
parseFloat
|
|
2998
|
+
).option("--idle-kill-minutes <minutes>", "Provider idle stop threshold in minutes", parseFloat).option("--verbose", "Enable verbose logging", false).action(async (options) => {
|
|
2999
|
+
await executeWorker(options);
|
|
3000
|
+
});
|
|
3001
|
+
await program.parseAsync(process.argv);
|
|
3002
|
+
}
|
|
3003
|
+
async function warnOnce(message, state) {
|
|
3004
|
+
if (state.warned) return;
|
|
3005
|
+
state.warned = true;
|
|
3006
|
+
console.log(chalk.yellow(message));
|
|
3007
|
+
}
|
|
3008
|
+
var RESULT_PERSISTENCE_PATH_KEYS = /* @__PURE__ */ new Set([
|
|
3009
|
+
"artifactManifest",
|
|
3010
|
+
"resultFile",
|
|
3011
|
+
"resultPath",
|
|
3012
|
+
"retainedWorktreePath",
|
|
3013
|
+
"worktreePath"
|
|
3014
|
+
]);
|
|
3015
|
+
function shouldPreserveResultPersistenceString(key) {
|
|
3016
|
+
return key !== void 0 && RESULT_PERSISTENCE_PATH_KEYS.has(key);
|
|
3017
|
+
}
|
|
3018
|
+
function serializeErrorForJson(error) {
|
|
3019
|
+
if (error instanceof Error) {
|
|
3020
|
+
const sanitized = sanitizeError(error);
|
|
3021
|
+
const serialized = {
|
|
3022
|
+
name: sanitized.name,
|
|
3023
|
+
message: sanitized.message,
|
|
3024
|
+
stack: sanitized.stack
|
|
3025
|
+
};
|
|
3026
|
+
if (error instanceof HostTimeoutError) {
|
|
3027
|
+
serialized.reason = "timeout";
|
|
3028
|
+
serialized.limitMs = error.limitMs;
|
|
3029
|
+
serialized.source = error.source;
|
|
3030
|
+
serialized.parsedError = createHostTerminationParsedError("timeout", sanitized.message, {
|
|
3031
|
+
limitMs: error.limitMs
|
|
3032
|
+
});
|
|
3033
|
+
}
|
|
3034
|
+
if (error instanceof ProviderExecutionError) {
|
|
3035
|
+
serialized.code = error.code;
|
|
3036
|
+
serialized.reason = sanitizeSynthesisArtifactForPersistence(error.reason);
|
|
3037
|
+
serialized.retryable = error.retryable;
|
|
3038
|
+
serialized.exitCode = error.exitCode;
|
|
3039
|
+
serialized.statusCode = error.statusCode;
|
|
3040
|
+
serialized.idleTerminated = error.idleTerminated;
|
|
3041
|
+
serialized.signal = sanitizeSynthesisArtifactForPersistence(error.signal);
|
|
3042
|
+
serialized.terminationSource = sanitizeSynthesisArtifactForPersistence(
|
|
3043
|
+
error.terminationSource
|
|
3044
|
+
);
|
|
3045
|
+
serialized.context = sanitizeSynthesisArtifactForPersistence(error.context);
|
|
3046
|
+
serialized.parsedError = sanitizeSynthesisArtifactForPersistence(error.parsedError);
|
|
3047
|
+
}
|
|
3048
|
+
return serialized;
|
|
3049
|
+
}
|
|
3050
|
+
return sanitizeSynthesisArtifactForPersistence(error);
|
|
3051
|
+
}
|
|
3052
|
+
function sanitizeResultPayloadForJson(value, seen = /* @__PURE__ */ new WeakSet(), key = void 0) {
|
|
3053
|
+
if (value instanceof Error) {
|
|
3054
|
+
return serializeErrorForJson(value);
|
|
3055
|
+
}
|
|
3056
|
+
if (typeof value === "string") {
|
|
3057
|
+
return shouldPreserveResultPersistenceString(key) ? value : sanitizeSynthesisStringForPersistence(value);
|
|
3058
|
+
}
|
|
3059
|
+
if (typeof value === "number") {
|
|
3060
|
+
return Number.isFinite(value) ? value : null;
|
|
3061
|
+
}
|
|
3062
|
+
if (typeof value === "boolean" || value === null) {
|
|
3063
|
+
return value;
|
|
3064
|
+
}
|
|
3065
|
+
if (typeof value === "bigint") {
|
|
3066
|
+
return value.toString();
|
|
3067
|
+
}
|
|
3068
|
+
if (typeof value === "symbol" || typeof value === "function" || value === void 0) {
|
|
3069
|
+
return void 0;
|
|
3070
|
+
}
|
|
3071
|
+
if (value instanceof Date) {
|
|
3072
|
+
return value.toISOString();
|
|
3073
|
+
}
|
|
3074
|
+
if (seen.has(value)) {
|
|
3075
|
+
return "[Circular]";
|
|
3076
|
+
}
|
|
3077
|
+
seen.add(value);
|
|
3078
|
+
try {
|
|
3079
|
+
if (Array.isArray(value)) {
|
|
3080
|
+
return value.map((entry) => sanitizeResultPayloadForJson(entry, seen, key));
|
|
3081
|
+
}
|
|
3082
|
+
const sanitized = {};
|
|
3083
|
+
for (const [entryKey, entry] of Object.entries(value)) {
|
|
3084
|
+
const sanitizedEntry = entryKey === "error" ? serializeErrorForJson(entry) : sanitizeResultPayloadForJson(entry, seen, entryKey);
|
|
3085
|
+
const finalValue = isSensitiveKey(entryKey) ? "[REDACTED]" : sanitizedEntry;
|
|
3086
|
+
if (finalValue !== void 0) {
|
|
3087
|
+
sanitized[entryKey] = finalValue;
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
const original = value;
|
|
3091
|
+
if (typeof original.worktreeDiff === "string") {
|
|
3092
|
+
sanitized.worktreeDiffReplayable = original.worktreeDiffReplayable !== false && sanitized.worktreeDiff === original.worktreeDiff;
|
|
3093
|
+
}
|
|
3094
|
+
return sanitized;
|
|
3095
|
+
} finally {
|
|
3096
|
+
seen.delete(value);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
async function writeResultFileAtomically(resultPath, payload) {
|
|
3100
|
+
const resultJson = JSON.stringify(sanitizeResultPayloadForJson(payload), null, 2);
|
|
3101
|
+
const tmpFile = `${resultPath}.tmp.${randomBytes(4).toString("hex")}`;
|
|
3102
|
+
await fs2.writeFile(tmpFile, resultJson, "utf-8");
|
|
3103
|
+
const fd = openSync(tmpFile, "r+");
|
|
3104
|
+
try {
|
|
3105
|
+
fsyncSync(fd);
|
|
3106
|
+
} finally {
|
|
3107
|
+
closeSync(fd);
|
|
3108
|
+
}
|
|
3109
|
+
await fs2.rename(tmpFile, resultPath);
|
|
3110
|
+
}
|
|
3111
|
+
var __testOnlyWriteResultFileAtomically = writeResultFileAtomically;
|
|
3112
|
+
function formatVerboseWorkerCompletionMetric(result) {
|
|
3113
|
+
return `Completeness: ${result.completeness.toFixed(1)}%`;
|
|
3114
|
+
}
|
|
3115
|
+
function initializeWorkerExecutionContext(taskId) {
|
|
3116
|
+
const inheritedBuildDir = readEnv(BUILD_TELEMETRY_DIR).value?.trim();
|
|
3117
|
+
if (!inheritedBuildDir) return;
|
|
3118
|
+
setExecutionContext(
|
|
3119
|
+
createExecutionContext({
|
|
3120
|
+
repoRoot: resolveCliRepoRoot(process.cwd()),
|
|
3121
|
+
buildDir: inheritedBuildDir,
|
|
3122
|
+
taskId
|
|
3123
|
+
})
|
|
3124
|
+
);
|
|
3125
|
+
}
|
|
3126
|
+
async function executeWorker(options) {
|
|
3127
|
+
const {
|
|
3128
|
+
taskId,
|
|
3129
|
+
variant: variantJson,
|
|
3130
|
+
promptFile,
|
|
3131
|
+
originalTaskFile,
|
|
3132
|
+
resultFile,
|
|
3133
|
+
timeoutMinutes,
|
|
3134
|
+
deadlineAt,
|
|
3135
|
+
idleWarnMinutes,
|
|
3136
|
+
idleKillMinutes,
|
|
3137
|
+
verbose
|
|
3138
|
+
} = options;
|
|
3139
|
+
const repairLoopConfig = parseRepairLoopConfig();
|
|
3140
|
+
if (idleWarnMinutes == null !== (idleKillMinutes == null)) {
|
|
3141
|
+
throw new Error("--idle-warn-minutes and --idle-kill-minutes must be provided together");
|
|
3142
|
+
}
|
|
3143
|
+
initializeWorkerExecutionContext(taskId);
|
|
3144
|
+
initializeLabModeFromEnv(taskId);
|
|
3145
|
+
if (verbose) {
|
|
3146
|
+
console.log(chalk.blue("Variant Worker starting"));
|
|
3147
|
+
console.log(chalk.gray(` Task ID: ${taskId}`));
|
|
3148
|
+
console.log(chalk.gray(` Prompt: ${promptFile}`));
|
|
3149
|
+
console.log(chalk.gray(` Result: ${resultFile}`));
|
|
3150
|
+
console.log(
|
|
3151
|
+
chalk.gray(` Partial Result: ${options.partialResultFile ?? `${resultFile}.partial.json`}`)
|
|
3152
|
+
);
|
|
3153
|
+
}
|
|
3154
|
+
let variant;
|
|
3155
|
+
try {
|
|
3156
|
+
variant = parseJsonWithContext(
|
|
3157
|
+
variantJson,
|
|
3158
|
+
"[Variant Worker] variant configuration"
|
|
3159
|
+
);
|
|
3160
|
+
} catch (error) {
|
|
3161
|
+
throw new Error(
|
|
3162
|
+
`Invalid variant JSON: ${error instanceof Error ? error.message : String(error)}`
|
|
3163
|
+
);
|
|
3164
|
+
}
|
|
3165
|
+
const workerProcessLifecycle = new ProcessLifecycleManager({
|
|
3166
|
+
verbose: verbose === true,
|
|
3167
|
+
defaultTimeoutMinutes: timeoutMinutes ?? DEFAULT_PER_VARIANT_TIMEOUT_MINUTES,
|
|
3168
|
+
maxConcurrentProcesses: 10,
|
|
3169
|
+
cleanupOnTermination: true
|
|
3170
|
+
});
|
|
3171
|
+
setProcessLifecycleManager(workerProcessLifecycle);
|
|
3172
|
+
const processLifecycleSignalHandlerName = "variant-worker-process-lifecycle";
|
|
3173
|
+
const signalHandlerName = "variant-worker-timeout-partial-result";
|
|
3174
|
+
let postProviderSignalHandlersInstalled = false;
|
|
3175
|
+
const onPostProviderSigterm = () => {
|
|
3176
|
+
void postProviderShutdown?.("SIGTERM");
|
|
3177
|
+
};
|
|
3178
|
+
const onPostProviderSigint = () => {
|
|
3179
|
+
void postProviderShutdown?.("SIGINT");
|
|
3180
|
+
};
|
|
3181
|
+
let postProviderShutdown;
|
|
3182
|
+
const installPostProviderSignalHandlers = () => {
|
|
3183
|
+
if (postProviderSignalHandlersInstalled) return;
|
|
3184
|
+
signalCoordinator.suspendProcessSignalHandlers();
|
|
3185
|
+
process.on("SIGTERM", onPostProviderSigterm);
|
|
3186
|
+
process.on("SIGINT", onPostProviderSigint);
|
|
3187
|
+
postProviderSignalHandlersInstalled = true;
|
|
3188
|
+
};
|
|
3189
|
+
const uninstallPostProviderSignalHandlers = () => {
|
|
3190
|
+
if (!postProviderSignalHandlersInstalled) return;
|
|
3191
|
+
process.removeListener("SIGTERM", onPostProviderSigterm);
|
|
3192
|
+
process.removeListener("SIGINT", onPostProviderSigint);
|
|
3193
|
+
signalCoordinator.resumeProcessSignalHandlers();
|
|
3194
|
+
postProviderSignalHandlersInstalled = false;
|
|
3195
|
+
};
|
|
3196
|
+
signalCoordinator.register(
|
|
3197
|
+
processLifecycleSignalHandlerName,
|
|
3198
|
+
async (context) => {
|
|
3199
|
+
if (context.reason === "SIGTERM" || context.reason === "SIGINT") {
|
|
3200
|
+
await workerProcessLifecycle.handleSignal(context.reason);
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
await workerProcessLifecycle.close();
|
|
3204
|
+
},
|
|
3205
|
+
{ priority: "critical", phase: "process-termination" }
|
|
3206
|
+
);
|
|
3207
|
+
try {
|
|
3208
|
+
variant.nativeWebSearch = isNativeResearchEnabledFor(variant.provider);
|
|
3209
|
+
const runMode = variant.isBasicCompetition ? "basic-cli-native" : "advanced";
|
|
3210
|
+
const partialResultFile = typeof options.partialResultFile === "string" && options.partialResultFile.trim().length > 0 ? options.partialResultFile.trim() : `${resultFile}.partial.json`;
|
|
3211
|
+
const workerStartMs = Date.now();
|
|
3212
|
+
const startTime = process.hrtime.bigint();
|
|
3213
|
+
const timeoutContext = {};
|
|
3214
|
+
let executorSettled = false;
|
|
3215
|
+
const persistTimeoutPartialResult = async (signal = "SIGTERM") => {
|
|
3216
|
+
const cancelled = signal === "SIGINT";
|
|
3217
|
+
const stopVerb = cancelled ? "Cancelled" : "Timed out";
|
|
3218
|
+
const timeoutWorktreeCandidate = timeoutContext.worktreePath ?? variant.workflowWorktreePath;
|
|
3219
|
+
let timeoutWorktreePath = typeof timeoutWorktreeCandidate === "string" && timeoutWorktreeCandidate.trim().length > 0 ? timeoutWorktreeCandidate.trim() : void 0;
|
|
3220
|
+
if (!timeoutWorktreePath) {
|
|
3221
|
+
timeoutWorktreePath = await findVariantWorktreePathForTimeout(
|
|
3222
|
+
taskId,
|
|
3223
|
+
variant,
|
|
3224
|
+
process.cwd()
|
|
3225
|
+
);
|
|
3226
|
+
if (!timeoutWorktreePath && verbose) {
|
|
3227
|
+
console.warn("[partial-save] Worktree context unavailable; needle discovery also failed");
|
|
3228
|
+
}
|
|
3229
|
+
}
|
|
3230
|
+
const elapsedText = formatElapsedRuntime(Date.now() - workerStartMs);
|
|
3231
|
+
let filesChanged = 0;
|
|
3232
|
+
let linesAdded = 0;
|
|
3233
|
+
let linesRemoved = 0;
|
|
3234
|
+
let statusLines = [];
|
|
3235
|
+
let partialEdits = false;
|
|
3236
|
+
let autoCommitFailed = false;
|
|
3237
|
+
let worktreeDiff;
|
|
3238
|
+
let baselineRef = typeof timeoutContext.baselineRef === "string" && timeoutContext.baselineRef.trim().length > 0 ? timeoutContext.baselineRef.trim() : void 0;
|
|
3239
|
+
let partialErrorDetail;
|
|
3240
|
+
if (timeoutWorktreePath) {
|
|
3241
|
+
try {
|
|
3242
|
+
const canMutateWorktreeForSnapshot = executorSettled;
|
|
3243
|
+
if (canMutateWorktreeForSnapshot) {
|
|
3244
|
+
await stageCommitEligibleChanges(timeoutWorktreePath);
|
|
3245
|
+
} else {
|
|
3246
|
+
partialErrorDetail = "executor still running during timeout; skipped git add/commit partial snapshot";
|
|
3247
|
+
}
|
|
3248
|
+
const [cachedStat, cachedShortStat, statusResult] = await Promise.all([
|
|
3249
|
+
execa3(
|
|
3250
|
+
"git",
|
|
3251
|
+
canMutateWorktreeForSnapshot ? ["diff", "--cached", "--stat"] : ["diff", "--stat"],
|
|
3252
|
+
{
|
|
3253
|
+
cwd: timeoutWorktreePath,
|
|
3254
|
+
stdio: "pipe",
|
|
3255
|
+
reject: false,
|
|
3256
|
+
timeout: 1200
|
|
3257
|
+
}
|
|
3258
|
+
),
|
|
3259
|
+
execa3(
|
|
3260
|
+
"git",
|
|
3261
|
+
canMutateWorktreeForSnapshot ? ["diff", "--cached", "--shortstat"] : ["diff", "--shortstat"],
|
|
3262
|
+
{
|
|
3263
|
+
cwd: timeoutWorktreePath,
|
|
3264
|
+
stdio: "pipe",
|
|
3265
|
+
reject: false,
|
|
3266
|
+
timeout: 1200
|
|
3267
|
+
}
|
|
3268
|
+
),
|
|
3269
|
+
execa3("git", ["status", "--porcelain", "--untracked-files=all"], {
|
|
3270
|
+
cwd: timeoutWorktreePath,
|
|
3271
|
+
stdio: "pipe",
|
|
3272
|
+
reject: false,
|
|
3273
|
+
timeout: 1200
|
|
3274
|
+
})
|
|
3275
|
+
]);
|
|
3276
|
+
partialEdits = cachedStat.stdout.trim().length > 0;
|
|
3277
|
+
const parsedShortStat = parseShortStatSummary(cachedShortStat.stdout);
|
|
3278
|
+
filesChanged = parsedShortStat.filesChanged;
|
|
3279
|
+
linesAdded = parsedShortStat.linesAdded;
|
|
3280
|
+
linesRemoved = parsedShortStat.linesRemoved;
|
|
3281
|
+
statusLines = statusResult.stdout.split("\n").map((line) => line.trimEnd()).filter((line) => line.trim().length > 0);
|
|
3282
|
+
if (partialEdits && filesChanged === 0 && statusLines.length > 0) {
|
|
3283
|
+
filesChanged = statusLines.length;
|
|
3284
|
+
}
|
|
3285
|
+
if (partialEdits && canMutateWorktreeForSnapshot) {
|
|
3286
|
+
const commitMessage = `chore: partial work (${cancelled ? "cancelled" : "timeout"} at ${elapsedText})`;
|
|
3287
|
+
const commit = await execa3("git", ["commit", "--no-verify", "-m", commitMessage], {
|
|
3288
|
+
cwd: timeoutWorktreePath,
|
|
3289
|
+
stdio: "pipe",
|
|
3290
|
+
reject: false,
|
|
3291
|
+
timeout: 1500
|
|
3292
|
+
});
|
|
3293
|
+
({ autoCommitFailed, baselineRef } = await resolveTimeoutSnapshotCommit(
|
|
3294
|
+
commit,
|
|
3295
|
+
baselineRef,
|
|
3296
|
+
async () => {
|
|
3297
|
+
const parent = await execa3("git", ["rev-parse", "HEAD~1"], {
|
|
3298
|
+
cwd: timeoutWorktreePath,
|
|
3299
|
+
stdio: "pipe",
|
|
3300
|
+
reject: false,
|
|
3301
|
+
timeout: 1200
|
|
3302
|
+
});
|
|
3303
|
+
return parent.exitCode === 0 && parent.stdout.trim().length > 0 ? parent.stdout.trim() : void 0;
|
|
3304
|
+
}
|
|
3305
|
+
));
|
|
3306
|
+
try {
|
|
3307
|
+
const diffText = await captureWorktreeDiff(timeoutWorktreePath, baselineRef, true);
|
|
3308
|
+
if (diffText.trim().length > 0) {
|
|
3309
|
+
worktreeDiff = normalizeWorktreeDiff(diffText);
|
|
3310
|
+
}
|
|
3311
|
+
} catch {
|
|
3312
|
+
}
|
|
3313
|
+
}
|
|
3314
|
+
} catch (error) {
|
|
3315
|
+
partialErrorDetail = error instanceof Error ? error.message : String(error);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
if (!partialErrorDetail && partialEdits) {
|
|
3319
|
+
if (autoCommitFailed) {
|
|
3320
|
+
partialErrorDetail = "auto-commit failed";
|
|
3321
|
+
}
|
|
3322
|
+
}
|
|
3323
|
+
const completeness = partialEdits ? estimateTimeoutCompletenessFromScope({
|
|
3324
|
+
filesChanged,
|
|
3325
|
+
linesAdded,
|
|
3326
|
+
linesRemoved,
|
|
3327
|
+
statusEntries: statusLines.length
|
|
3328
|
+
}) : 0;
|
|
3329
|
+
const timeoutErrorMessage = (() => {
|
|
3330
|
+
if (partialErrorDetail) {
|
|
3331
|
+
if (partialEdits && !autoCommitFailed) {
|
|
3332
|
+
return `${stopVerb} at ${elapsedText}; partial edits detected (preservation warning: ${partialErrorDetail})`;
|
|
3333
|
+
}
|
|
3334
|
+
if (partialEdits && autoCommitFailed) {
|
|
3335
|
+
return `${stopVerb} at ${elapsedText}; partial edits detected but auto-commit failed`;
|
|
3336
|
+
}
|
|
3337
|
+
return `${stopVerb} at ${elapsedText}; partial preservation warning: ${partialErrorDetail}`;
|
|
3338
|
+
}
|
|
3339
|
+
if (partialEdits) {
|
|
3340
|
+
return `${stopVerb} at ${elapsedText}; partial edits preserved`;
|
|
3341
|
+
}
|
|
3342
|
+
return `${stopVerb} at ${elapsedText}; no staged edits detected`;
|
|
3343
|
+
})();
|
|
3344
|
+
const partialResult = {
|
|
3345
|
+
variant: formatVariantIdentifier(variant),
|
|
3346
|
+
provider: variant.provider,
|
|
3347
|
+
model: variant.model,
|
|
3348
|
+
executionMode: variant.executionMode,
|
|
3349
|
+
success: false,
|
|
3350
|
+
duration: Number(process.hrtime.bigint() - startTime) / 1e9,
|
|
3351
|
+
completeness,
|
|
3352
|
+
timedOut: !cancelled,
|
|
3353
|
+
partialEdits,
|
|
3354
|
+
terminationReason: cancelled ? "canceled_user" : "timeout",
|
|
3355
|
+
executionIntent: variant.executionIntent,
|
|
3356
|
+
isBasicCompetition: variant.isBasicCompetition,
|
|
3357
|
+
emptyChanges: !partialEdits,
|
|
3358
|
+
filesChanged,
|
|
3359
|
+
linesAdded,
|
|
3360
|
+
linesRemoved,
|
|
3361
|
+
worktreeDiff,
|
|
3362
|
+
gitMetrics: partialEdits ? {
|
|
3363
|
+
filesChanged,
|
|
3364
|
+
linesAdded,
|
|
3365
|
+
linesDeleted: linesRemoved,
|
|
3366
|
+
source: "worktree"
|
|
3367
|
+
} : void 0,
|
|
3368
|
+
worktreeSummary: timeoutWorktreePath ? {
|
|
3369
|
+
filesChanged,
|
|
3370
|
+
insertions: linesAdded,
|
|
3371
|
+
deletions: linesRemoved,
|
|
3372
|
+
statusLines
|
|
3373
|
+
} : void 0,
|
|
3374
|
+
retainedWorktreePath: timeoutWorktreePath,
|
|
3375
|
+
baselineRef,
|
|
3376
|
+
error: cancelled ? new Error(timeoutErrorMessage) : new HostTimeoutError(timeoutErrorMessage, {
|
|
3377
|
+
source: VARIANT_WORKER_SIGTERM_SOURCE
|
|
3378
|
+
})
|
|
3379
|
+
};
|
|
3380
|
+
partialResult.outcome = deriveVariantOutcome(partialResult);
|
|
3381
|
+
sanitizeVariantResult(partialResult);
|
|
3382
|
+
await writeResultFileAtomically(partialResultFile, partialResult);
|
|
3383
|
+
};
|
|
3384
|
+
const executionAbortController = new AbortController();
|
|
3385
|
+
let shutdownRequested = false;
|
|
3386
|
+
let shutdownRequestedAtMs;
|
|
3387
|
+
let shutdownSignal;
|
|
3388
|
+
const signalExitCode = (signal) => signal === "SIGINT" ? 130 : 143;
|
|
3389
|
+
const requestShutdown = async (signal, options2) => {
|
|
3390
|
+
if (shutdownRequested) {
|
|
3391
|
+
return;
|
|
3392
|
+
}
|
|
3393
|
+
shutdownRequested = true;
|
|
3394
|
+
shutdownRequestedAtMs = elapsedMsSince(startTime);
|
|
3395
|
+
shutdownSignal = signal;
|
|
3396
|
+
if (signal === "SIGINT") {
|
|
3397
|
+
mergeExecutionContextEnvOverrides({ BUILD_PRESERVE_WORKTREES: "1" });
|
|
3398
|
+
}
|
|
3399
|
+
if (!executionAbortController.signal.aborted) {
|
|
3400
|
+
executionAbortController.abort(buildShutdownAbortReason(signal));
|
|
3401
|
+
}
|
|
3402
|
+
const exitAfter = options2?.exitAfter ?? true;
|
|
3403
|
+
try {
|
|
3404
|
+
if (verbose) {
|
|
3405
|
+
console.log(chalk.yellow(`Received ${signal}; beginning timeout shutdown handling.`));
|
|
3406
|
+
}
|
|
3407
|
+
try {
|
|
3408
|
+
await runWithTimeout(persistTimeoutPartialResult(signal), 2500, "partial result save");
|
|
3409
|
+
if (verbose) {
|
|
3410
|
+
console.log(chalk.gray(` Partial timeout result written to ${partialResultFile}`));
|
|
3411
|
+
}
|
|
3412
|
+
} catch (error) {
|
|
3413
|
+
if (verbose) {
|
|
3414
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3415
|
+
console.warn(chalk.yellow(`Partial timeout preservation skipped: ${message}`));
|
|
3416
|
+
}
|
|
3417
|
+
}
|
|
3418
|
+
if (exitAfter) {
|
|
3419
|
+
try {
|
|
3420
|
+
await runWithTimeout(
|
|
3421
|
+
signalCoordinator.runCleanupHandlers(`variant-worker ${signal.toLowerCase()}`, {
|
|
3422
|
+
timeoutMs: SIGNAL_CLEANUP_TIMEOUT_MS
|
|
3423
|
+
}),
|
|
3424
|
+
SIGNAL_CLEANUP_WRAPPER_TIMEOUT_MS,
|
|
3425
|
+
"signal cleanup"
|
|
3426
|
+
);
|
|
3427
|
+
} catch {
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
} catch (shutdownError) {
|
|
3431
|
+
console.error(
|
|
3432
|
+
`[variant-worker] Shutdown error (${signal}): ${shutdownError instanceof Error ? shutdownError.message : String(shutdownError)}`
|
|
3433
|
+
);
|
|
3434
|
+
} finally {
|
|
3435
|
+
if (exitAfter) {
|
|
3436
|
+
process.exit(signalExitCode(signal));
|
|
3437
|
+
}
|
|
3438
|
+
}
|
|
3439
|
+
};
|
|
3440
|
+
postProviderShutdown = async (signal) => {
|
|
3441
|
+
await requestShutdown(signal, { exitAfter: false });
|
|
3442
|
+
};
|
|
3443
|
+
signalCoordinator.register(
|
|
3444
|
+
signalHandlerName,
|
|
3445
|
+
async (context) => {
|
|
3446
|
+
if (context.reason === "SIGTERM" || context.reason === "SIGINT") {
|
|
3447
|
+
await requestShutdown(context.reason, { exitAfter: false });
|
|
3448
|
+
}
|
|
3449
|
+
},
|
|
3450
|
+
{ priority: "critical", phase: "artifact-cleanup" }
|
|
3451
|
+
);
|
|
3452
|
+
if (variant.executionMode !== "local") {
|
|
3453
|
+
throw new Error(`Worker only supports local execution mode (got: ${variant.executionMode})`);
|
|
3454
|
+
}
|
|
3455
|
+
let prompt;
|
|
3456
|
+
try {
|
|
3457
|
+
prompt = await fs2.readFile(promptFile, "utf-8");
|
|
3458
|
+
} catch (error) {
|
|
3459
|
+
throw new Error(
|
|
3460
|
+
`Failed to read prompt file: ${error instanceof Error ? error.message : String(error)}`
|
|
3461
|
+
);
|
|
3462
|
+
}
|
|
3463
|
+
if (verbose) {
|
|
3464
|
+
console.log(chalk.blue(`Loaded prompt (${prompt.length} chars)`));
|
|
3465
|
+
console.log(chalk.blue(`Executing ${formatVariantIdentifier(variant)}...`));
|
|
3466
|
+
}
|
|
3467
|
+
const repoRoot = process.cwd();
|
|
3468
|
+
const packageJsonScripts = await tryLoadPackageJsonScripts(repoRoot);
|
|
3469
|
+
let originalPrompt;
|
|
3470
|
+
if (originalTaskFile) {
|
|
3471
|
+
try {
|
|
3472
|
+
originalPrompt = await fs2.readFile(originalTaskFile, "utf-8");
|
|
3473
|
+
} catch (error) {
|
|
3474
|
+
throw new Error(
|
|
3475
|
+
`Failed to read original task file: ${error instanceof Error ? error.message : String(error)}`
|
|
3476
|
+
);
|
|
3477
|
+
}
|
|
3478
|
+
} else {
|
|
3479
|
+
originalPrompt = extractOriginalPrompt(prompt);
|
|
3480
|
+
}
|
|
3481
|
+
const specInjectionUsed = /\[SPEC:/.test(prompt);
|
|
3482
|
+
const specSignal = evaluateExecutionSpec(originalPrompt);
|
|
3483
|
+
let guidanceTier;
|
|
3484
|
+
let specQualitySignals;
|
|
3485
|
+
if (specSignal) {
|
|
3486
|
+
guidanceTier = specSignal.guidanceTier;
|
|
3487
|
+
specQualitySignals = generateSpecQualitySignals(originalPrompt, repoRoot, packageJsonScripts);
|
|
3488
|
+
}
|
|
3489
|
+
const classification = classifyTask(prompt, { stripInjections: true });
|
|
3490
|
+
let telemetry = null;
|
|
3491
|
+
let telemetryEnabled = true;
|
|
3492
|
+
const telemetryWarning = { warned: false };
|
|
3493
|
+
const costTrackerWarning = { warned: false };
|
|
3494
|
+
const applyWorkerEnvOverrides = (overrides) => {
|
|
3495
|
+
mergeExecutionContextEnvOverrides(overrides);
|
|
3496
|
+
};
|
|
3497
|
+
const prevRetainBasicWorktree = getRuntimeEnv("BUILD_RETAIN_BASIC_WORKTREE");
|
|
3498
|
+
const prevPreserveWorktrees = getRuntimeEnv("BUILD_PRESERVE_WORKTREES");
|
|
3499
|
+
const preserveWorktreesForDebug = prevRetainBasicWorktree === "1" && getRuntimeEnv("BUILD_PRESERVE_WORKTREES") !== "1";
|
|
3500
|
+
if (preserveWorktreesForDebug) {
|
|
3501
|
+
applyWorkerEnvOverrides({ BUILD_PRESERVE_WORKTREES: "1" });
|
|
3502
|
+
}
|
|
3503
|
+
try {
|
|
3504
|
+
telemetry = TelemetryLogger.getInstance();
|
|
3505
|
+
telemetry.logVariantEvent(taskId, formatVariantIdentifier(variant), "started", {
|
|
3506
|
+
provider: variant.provider,
|
|
3507
|
+
model: variant.model,
|
|
3508
|
+
executionMode: variant.executionMode,
|
|
3509
|
+
runMode,
|
|
3510
|
+
executionIntent: variant.executionIntent,
|
|
3511
|
+
...typeof guidanceTier === "number" ? { guidanceTier } : {},
|
|
3512
|
+
...specInjectionUsed ? { specInjectionUsed: true } : {},
|
|
3513
|
+
...Array.isArray(specQualitySignals) && specQualitySignals.length > 0 ? { specQualitySignals } : {},
|
|
3514
|
+
...classification.drift ? { classificationDrift: classification.drift } : {}
|
|
3515
|
+
});
|
|
3516
|
+
} catch (_err) {
|
|
3517
|
+
await warnOnce(
|
|
3518
|
+
"Telemetry unavailable for worker; continuing with JSON output only.",
|
|
3519
|
+
telemetryWarning
|
|
3520
|
+
);
|
|
3521
|
+
telemetryEnabled = false;
|
|
3522
|
+
}
|
|
3523
|
+
let result;
|
|
3524
|
+
let cleanupWorktreeAfterRun = false;
|
|
3525
|
+
let executionResultSettledAtMs;
|
|
3526
|
+
const providerOutputForwarder = createProviderOutputForwarder(
|
|
3527
|
+
getRuntimeEnv("BUILD_PROVIDER_OUTPUT_EVENTS") === "1"
|
|
3528
|
+
);
|
|
3529
|
+
try {
|
|
3530
|
+
const { LocalExecutor } = await import("./local-executor-DCMDDRX2.js");
|
|
3531
|
+
if (repairLoopConfig.enabled && prevRetainBasicWorktree === void 0 && getRuntimeEnv("BUILD_RETAIN_BASIC_WORKTREE") !== "1") {
|
|
3532
|
+
applyWorkerEnvOverrides({ BUILD_RETAIN_BASIC_WORKTREE: "1" });
|
|
3533
|
+
cleanupWorktreeAfterRun = getRuntimeEnv("BUILD_PRESERVE_WORKTREES") !== "1";
|
|
3534
|
+
}
|
|
3535
|
+
let costTracker;
|
|
3536
|
+
if (telemetry) {
|
|
3537
|
+
try {
|
|
3538
|
+
const { UnifiedCostTracker } = await import("./unified-cost-tracker-5MTBJ2CL.js");
|
|
3539
|
+
const { UniversalCostCalculator } = await import("./universal-cost-calculator-JTFEJVWK.js");
|
|
3540
|
+
const costCalculator = new UniversalCostCalculator(telemetry);
|
|
3541
|
+
costTracker = new UnifiedCostTracker(costCalculator, telemetry, {
|
|
3542
|
+
enabled: true,
|
|
3543
|
+
budgetThresholds: {
|
|
3544
|
+
warning: 1,
|
|
3545
|
+
critical: 5,
|
|
3546
|
+
daily: 10,
|
|
3547
|
+
monthly: 100
|
|
3548
|
+
},
|
|
3549
|
+
optimization: {
|
|
3550
|
+
enableSuggestions: false,
|
|
3551
|
+
minSavingsThreshold: 1,
|
|
3552
|
+
maxSuggestionsPerHour: 0
|
|
3553
|
+
},
|
|
3554
|
+
updateIntervalMs: 6e4
|
|
3555
|
+
});
|
|
3556
|
+
} catch (costErr) {
|
|
3557
|
+
await warnOnce(
|
|
3558
|
+
"Cost tracking initialization failed in worker; continuing without a UnifiedCostTracker.",
|
|
3559
|
+
costTrackerWarning
|
|
3560
|
+
);
|
|
3561
|
+
if (verbose) {
|
|
3562
|
+
const message = costErr instanceof Error ? costErr.message : String(costErr);
|
|
3563
|
+
console.warn(chalk.yellow(` Cost tracking init error: ${message}`));
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
}
|
|
3567
|
+
const executor = new LocalExecutor({
|
|
3568
|
+
repoRoot,
|
|
3569
|
+
verbose: verbose ?? false,
|
|
3570
|
+
executorTimeoutMs: timeoutMinutes != null ? timeoutMinutes * 6e4 : void 0,
|
|
3571
|
+
executorDeadlineAt: deadlineAt ?? (timeoutMinutes != null ? workerStartMs + timeoutMinutes * 6e4 : void 0),
|
|
3572
|
+
idleWarnMs: idleWarnMinutes != null ? idleWarnMinutes * 6e4 : void 0,
|
|
3573
|
+
idleKillMs: idleKillMinutes != null ? idleKillMinutes * 6e4 : void 0,
|
|
3574
|
+
costTracker,
|
|
3575
|
+
resultStreamer: providerOutputForwarder.resultStreamer,
|
|
3576
|
+
onWorktreeReady: ({ worktreePath, baselineRef }) => {
|
|
3577
|
+
timeoutContext.worktreePath = worktreePath;
|
|
3578
|
+
timeoutContext.baselineRef = baselineRef;
|
|
3579
|
+
}
|
|
3580
|
+
});
|
|
3581
|
+
const executionBreadcrumb = createCliBreadcrumb({
|
|
3582
|
+
label: `Variant ${formatVariantIdentifier(variant)}`,
|
|
3583
|
+
detail: "execution started; this step can stay quiet while the provider works."
|
|
3584
|
+
});
|
|
3585
|
+
let execResult;
|
|
3586
|
+
try {
|
|
3587
|
+
execResult = await executor.executeVariant(taskId, variant, prompt, {
|
|
3588
|
+
abortSignal: executionAbortController.signal
|
|
3589
|
+
});
|
|
3590
|
+
executionResultSettledAtMs = elapsedMsSince(startTime);
|
|
3591
|
+
installPostProviderSignalHandlers();
|
|
3592
|
+
executionBreadcrumb.complete();
|
|
3593
|
+
} finally {
|
|
3594
|
+
executorSettled = true;
|
|
3595
|
+
executionBreadcrumb.dispose();
|
|
3596
|
+
}
|
|
3597
|
+
result = execResult;
|
|
3598
|
+
const postProviderPhaseDurationsMs = {
|
|
3599
|
+
...result.postProviderPhaseDurationsMs ?? {}
|
|
3600
|
+
};
|
|
3601
|
+
result.postProviderPhaseDurationsMs = postProviderPhaseDurationsMs;
|
|
3602
|
+
timeoutContext.worktreePath = result.retainedWorktreePath ?? timeoutContext.worktreePath;
|
|
3603
|
+
timeoutContext.baselineRef = result.baselineRef ?? timeoutContext.baselineRef;
|
|
3604
|
+
measurePostProviderPhaseSync(
|
|
3605
|
+
postProviderPhaseDurationsMs,
|
|
3606
|
+
"resultNormalization",
|
|
3607
|
+
verbose,
|
|
3608
|
+
() => {
|
|
3609
|
+
if (variant.executionIntent === "analysis" && result.success !== false && (result.completeness ?? 0) < MIN_ANALYSIS_COMPLETENESS) {
|
|
3610
|
+
const boosted = Math.max(result.completeness ?? 0, MIN_ANALYSIS_COMPLETENESS);
|
|
3611
|
+
result.completeness = boosted;
|
|
3612
|
+
result.completenessScore = {
|
|
3613
|
+
score: boosted,
|
|
3614
|
+
category: "analysis",
|
|
3615
|
+
fileTargetMatchRate: result.completenessScore?.fileTargetMatchRate
|
|
3616
|
+
};
|
|
3617
|
+
}
|
|
3618
|
+
if (result.completeness < 0) {
|
|
3619
|
+
result.completeness = 0;
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
);
|
|
3623
|
+
const duration = Number(process.hrtime.bigint() - startTime) / 1e9;
|
|
3624
|
+
result.executionDurationMs = Math.round(duration * 1e3);
|
|
3625
|
+
await measurePostProviderPhase(
|
|
3626
|
+
postProviderPhaseDurationsMs,
|
|
3627
|
+
"gitMetrics",
|
|
3628
|
+
verbose,
|
|
3629
|
+
async () => ensureGitMetrics(result)
|
|
3630
|
+
);
|
|
3631
|
+
attachOffScopeConfigWarning(result, { taskId, prompt });
|
|
3632
|
+
const isAnalysisIntent = variant.executionIntent === "analysis";
|
|
3633
|
+
const hasRetainedRepairWorktree = typeof result.retainedWorktreePath === "string" && result.retainedWorktreePath.trim().length > 0;
|
|
3634
|
+
const hasRepairBaselineRef = typeof result.baselineRef === "string" && result.baselineRef.trim().length > 0;
|
|
3635
|
+
const warningCodes = getWarningCodes(result);
|
|
3636
|
+
const hasMissingFileTargets = warningCodes.has("WS-008");
|
|
3637
|
+
const filesChanged = result.gitMetrics?.filesChanged ?? result.filesChanged ?? 0;
|
|
3638
|
+
const hasMaterializedDeliverable = filesChanged > 0 || result.outcome?.delivery?.hasChanges === true;
|
|
3639
|
+
const outputText = `${result.result?.stdout ?? ""}
|
|
3640
|
+
${result.result?.stderr ?? ""}`;
|
|
3641
|
+
const isClarifyingNoOp = filesChanged === 0 && looksLikeClarifyingQuestion(outputText);
|
|
3642
|
+
const exactArtifactGate = await measurePostProviderPhase(
|
|
3643
|
+
postProviderPhaseDurationsMs,
|
|
3644
|
+
"exactArtifactGoalGate",
|
|
3645
|
+
verbose,
|
|
3646
|
+
async () => {
|
|
3647
|
+
if (!hasRetainedRepairWorktree) {
|
|
3648
|
+
return { success: result.success === true };
|
|
3649
|
+
}
|
|
3650
|
+
return applyExactArtifactGoalSuccessGate({
|
|
3651
|
+
prompt,
|
|
3652
|
+
worktreePath: result.retainedWorktreePath,
|
|
3653
|
+
success: result.success === true
|
|
3654
|
+
});
|
|
3655
|
+
}
|
|
3656
|
+
);
|
|
3657
|
+
if (result.success === true && !exactArtifactGate.success) {
|
|
3658
|
+
result.success = false;
|
|
3659
|
+
result.completeness = 0;
|
|
3660
|
+
result.error = new Error(
|
|
3661
|
+
exactArtifactGate.failureReason ?? "Exact artifact content mismatch"
|
|
3662
|
+
);
|
|
3663
|
+
}
|
|
3664
|
+
const validationArtifactSatisfied = await measurePostProviderPhase(
|
|
3665
|
+
postProviderPhaseDurationsMs,
|
|
3666
|
+
"validationArtifactCheck",
|
|
3667
|
+
verbose,
|
|
3668
|
+
async () => {
|
|
3669
|
+
if (!hasRetainedRepairWorktree || result.success !== true) return false;
|
|
3670
|
+
const simpleArtifactGoal = parseSimpleArtifactGoal(prompt);
|
|
3671
|
+
if (simpleArtifactGoal) {
|
|
3672
|
+
return isSimpleArtifactGoalSatisfied(result.retainedWorktreePath, simpleArtifactGoal);
|
|
3673
|
+
}
|
|
3674
|
+
return isValidationArtifactOnlyDiffSatisfied({
|
|
3675
|
+
prompt,
|
|
3676
|
+
worktreePath: result.retainedWorktreePath,
|
|
3677
|
+
baselineRef: result.baselineRef
|
|
3678
|
+
});
|
|
3679
|
+
}
|
|
3680
|
+
);
|
|
3681
|
+
const runFallbackVerification = async () => {
|
|
3682
|
+
if (!result.verificationLoop && validationArtifactSatisfied !== true && !isAnalysisIntent && result.success === true && hasMaterializedDeliverable && typeof result.retainedWorktreePath === "string" && result.retainedWorktreePath.trim().length > 0 && typeof result.baselineRef === "string" && result.baselineRef.trim().length > 0) {
|
|
3683
|
+
const worktreePath = result.retainedWorktreePath.trim();
|
|
3684
|
+
const baselineRef = result.baselineRef.trim();
|
|
3685
|
+
const repoRoot2 = process.cwd();
|
|
3686
|
+
const cacheDir = verificationBaselineCacheDir();
|
|
3687
|
+
const verificationChangedFiles = await taskScopedChangedFiles(worktreePath, baselineRef);
|
|
3688
|
+
const testsProhibited = isTestRunProhibited(prompt);
|
|
3689
|
+
const baselineForVerification = await measurePostProviderPhase(
|
|
3690
|
+
postProviderPhaseDurationsMs,
|
|
3691
|
+
"verificationBaselineCapture",
|
|
3692
|
+
verbose,
|
|
3693
|
+
async () => getOrCaptureVerificationBaseline({
|
|
3694
|
+
repoRoot: repoRoot2,
|
|
3695
|
+
baselineRef,
|
|
3696
|
+
cacheDir,
|
|
3697
|
+
captureOptions: {
|
|
3698
|
+
changedFiles: verificationChangedFiles,
|
|
3699
|
+
skipTests: testsProhibited
|
|
3700
|
+
}
|
|
3701
|
+
}).catch(() => null)
|
|
3702
|
+
);
|
|
3703
|
+
const verificationOutcome = await measurePostProviderPhase(
|
|
3704
|
+
postProviderPhaseDurationsMs,
|
|
3705
|
+
"deterministicVerification",
|
|
3706
|
+
verbose,
|
|
3707
|
+
async () => runDeterministicVerificationOnce({
|
|
3708
|
+
worktreePath,
|
|
3709
|
+
baselineRef,
|
|
3710
|
+
// Same per-channel budget as the baseline capture above, not the
|
|
3711
|
+
// repair-iteration timebox, so a gate the baseline passed is not cut short.
|
|
3712
|
+
timeoutMs: resolveVerifyCommandTimeoutMs(),
|
|
3713
|
+
budget: "per-channel",
|
|
3714
|
+
baseline: baselineForVerification?.metadata?.captureSucceeded ? baselineForVerification : void 0,
|
|
3715
|
+
checkTestGate: !testsProhibited,
|
|
3716
|
+
// Same scope as the baseline capture above (symmetry invariant).
|
|
3717
|
+
changedFiles: verificationChangedFiles
|
|
3718
|
+
})
|
|
3719
|
+
);
|
|
3720
|
+
result.verification = verificationOutcome.verification;
|
|
3721
|
+
result.verificationLoop = verificationOutcome.verificationLoop;
|
|
3722
|
+
}
|
|
3723
|
+
};
|
|
3724
|
+
const initialVerificationDisposition = await resolveInitialVerificationDisposition(
|
|
3725
|
+
result,
|
|
3726
|
+
runFallbackVerification
|
|
3727
|
+
);
|
|
3728
|
+
const providerGatesAlreadyPassed = initialVerificationDisposition.outcome === "passed";
|
|
3729
|
+
const repairAdmission = measurePostProviderPhaseSync(
|
|
3730
|
+
postProviderPhaseDurationsMs,
|
|
3731
|
+
"repairAdmission",
|
|
3732
|
+
verbose,
|
|
3733
|
+
() => {
|
|
3734
|
+
return decideRepairAdmission({
|
|
3735
|
+
repairLoopEnabled: repairLoopConfig.enabled,
|
|
3736
|
+
isAnalysisIntent,
|
|
3737
|
+
hasRetainedWorktree: hasRetainedRepairWorktree,
|
|
3738
|
+
hasBaselineRef: hasRepairBaselineRef,
|
|
3739
|
+
initialVerificationOutcome: initialVerificationDisposition.outcome,
|
|
3740
|
+
providerGatesAlreadyPassed,
|
|
3741
|
+
verificationUnavailable: initialVerificationDisposition.outcome === "not_run" && hasUnavailableVerificationGateEvidence(result),
|
|
3742
|
+
hasMissingFileTargets,
|
|
3743
|
+
isClarifyingNoOp,
|
|
3744
|
+
providerExecutionSucceeded: result.success === true,
|
|
3745
|
+
hasMaterializedDeliverable,
|
|
3746
|
+
validationArtifactSatisfied,
|
|
3747
|
+
filesChanged
|
|
3748
|
+
});
|
|
3749
|
+
}
|
|
3750
|
+
);
|
|
3751
|
+
result.repairAdmission = repairAdmission;
|
|
3752
|
+
if (repairLoopConfig.enabled && result.repairAdmission.action === "skip" && (result.repairAdmission.reason === "skip_passing_gates" || result.repairAdmission.reason === "skip_clarifying_noop")) {
|
|
3753
|
+
emitWarning(
|
|
3754
|
+
"PERF-003",
|
|
3755
|
+
{
|
|
3756
|
+
taskId,
|
|
3757
|
+
subsystem: "repair-loop",
|
|
3758
|
+
executionPhase: "validation"
|
|
3759
|
+
},
|
|
3760
|
+
{
|
|
3761
|
+
metadata: {
|
|
3762
|
+
reason: result.repairAdmission.summary,
|
|
3763
|
+
admissionReason: result.repairAdmission.reason
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3768
|
+
if (result.repairAdmission.action === "run") {
|
|
3769
|
+
let repairOutcome = null;
|
|
3770
|
+
let preRepairDiffSnapshot;
|
|
3771
|
+
{
|
|
3772
|
+
const worktreePath = result.retainedWorktreePath;
|
|
3773
|
+
const baselineRef = result.baselineRef;
|
|
3774
|
+
const initialCostUsd = result.costMetrics?.totalCost ?? result.result?.totalCostUsd ?? 0;
|
|
3775
|
+
preRepairDiffSnapshot = await measurePostProviderPhase(
|
|
3776
|
+
postProviderPhaseDurationsMs,
|
|
3777
|
+
"preRepairDiffSnapshot",
|
|
3778
|
+
verbose,
|
|
3779
|
+
async () => captureRepairDiffSnapshot(worktreePath, baselineRef)
|
|
3780
|
+
);
|
|
3781
|
+
if (!preRepairDiffSnapshot) {
|
|
3782
|
+
emitWarning(
|
|
3783
|
+
"PERF-003",
|
|
3784
|
+
{
|
|
3785
|
+
taskId,
|
|
3786
|
+
subsystem: "repair-loop",
|
|
3787
|
+
executionPhase: "validation"
|
|
3788
|
+
},
|
|
3789
|
+
{
|
|
3790
|
+
metadata: {
|
|
3791
|
+
reason: "pre-repair diff snapshot unavailable; repair-scope classification will be unknown",
|
|
3792
|
+
baselineRef,
|
|
3793
|
+
worktreePath
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
);
|
|
3797
|
+
}
|
|
3798
|
+
const repoRoot2 = process.cwd();
|
|
3799
|
+
const cacheDir = verificationBaselineCacheDir();
|
|
3800
|
+
const repairChangedFiles = await taskScopedChangedFiles(worktreePath, baselineRef);
|
|
3801
|
+
const repairTestsProhibited = isTestRunProhibited(prompt);
|
|
3802
|
+
const baselineForRepair = await measurePostProviderPhase(
|
|
3803
|
+
postProviderPhaseDurationsMs,
|
|
3804
|
+
"verificationBaselineCapture",
|
|
3805
|
+
verbose,
|
|
3806
|
+
async () => getOrCaptureVerificationBaseline({
|
|
3807
|
+
repoRoot: repoRoot2,
|
|
3808
|
+
baselineRef,
|
|
3809
|
+
cacheDir,
|
|
3810
|
+
captureOptions: {
|
|
3811
|
+
changedFiles: repairChangedFiles,
|
|
3812
|
+
skipTests: repairTestsProhibited
|
|
3813
|
+
}
|
|
3814
|
+
}).catch(() => null)
|
|
3815
|
+
);
|
|
3816
|
+
const baselineSucceeded = !!baselineForRepair?.metadata?.captureSucceeded;
|
|
3817
|
+
const allowedFiles = (() => {
|
|
3818
|
+
try {
|
|
3819
|
+
const spec = parsePromptSpec(prompt);
|
|
3820
|
+
return Array.isArray(spec.fileConstraint.targets) ? spec.fileConstraint.targets : [];
|
|
3821
|
+
} catch {
|
|
3822
|
+
return [];
|
|
3823
|
+
}
|
|
3824
|
+
})();
|
|
3825
|
+
repairOutcome = await measurePostProviderPhase(
|
|
3826
|
+
postProviderPhaseDurationsMs,
|
|
3827
|
+
"repairLoop",
|
|
3828
|
+
verbose,
|
|
3829
|
+
async () => runVerificationDrivenRepairLoop({
|
|
3830
|
+
config: {
|
|
3831
|
+
enabled: true,
|
|
3832
|
+
maxIterations: Math.max(1, Math.floor(repairLoopConfig.maxIterations)),
|
|
3833
|
+
iterationTimeoutMs: Math.max(
|
|
3834
|
+
1e3,
|
|
3835
|
+
Math.floor(repairLoopConfig.iterationTimeoutMs)
|
|
3836
|
+
),
|
|
3837
|
+
abortIfRegression: repairLoopConfig.abortIfRegression,
|
|
3838
|
+
allowApiBreak: repairLoopConfig.allowApiBreak,
|
|
3839
|
+
repairMode: repairLoopConfig.repairMode,
|
|
3840
|
+
scopePolicy: repairLoopConfig.scopePolicy,
|
|
3841
|
+
repairSaveModes: repairLoopConfig.repairSaveModes,
|
|
3842
|
+
allowedFiles,
|
|
3843
|
+
// Default to true for the software-aware Multi-Lens judge.
|
|
3844
|
+
// This enables test failure detection in regression checks.
|
|
3845
|
+
checkTestGate: !repairTestsProhibited,
|
|
3846
|
+
skipTests: repairTestsProhibited
|
|
3847
|
+
},
|
|
3848
|
+
worktreePath,
|
|
3849
|
+
baselineRef,
|
|
3850
|
+
baseline: baselineSucceeded ? baselineForRepair : void 0,
|
|
3851
|
+
taskId,
|
|
3852
|
+
variantId: formatVariantIdentifier(variant),
|
|
3853
|
+
originalPrompt: prompt,
|
|
3854
|
+
initialCostUsd: Number.isFinite(initialCostUsd) && initialCostUsd > 0 ? initialCostUsd : 0,
|
|
3855
|
+
executeRepair: async (repairPrompt, iterationIndex, abortSignal) => {
|
|
3856
|
+
const repairTaskId = `${taskId}-verify-${iterationIndex + 1}`;
|
|
3857
|
+
const repairVariant = {
|
|
3858
|
+
...variant,
|
|
3859
|
+
// Enforce same worktree + prevent auto-commit drift during repair loops.
|
|
3860
|
+
workflowWorktreePath: worktreePath,
|
|
3861
|
+
disableAutoCommit: true,
|
|
3862
|
+
isBasicCompetition: true
|
|
3863
|
+
};
|
|
3864
|
+
const repairResult = await executor.executeVariant(
|
|
3865
|
+
repairTaskId,
|
|
3866
|
+
repairVariant,
|
|
3867
|
+
repairPrompt,
|
|
3868
|
+
{ abortSignal }
|
|
3869
|
+
);
|
|
3870
|
+
const costUsd = repairResult.costMetrics?.totalCost ?? repairResult.result?.totalCostUsd ?? 0;
|
|
3871
|
+
return { costUsd: Number.isFinite(costUsd) ? costUsd : 0 };
|
|
3872
|
+
}
|
|
3873
|
+
})
|
|
3874
|
+
);
|
|
3875
|
+
result.verification = repairOutcome.verification;
|
|
3876
|
+
result.verificationLoop = repairOutcome.verificationLoop;
|
|
3877
|
+
}
|
|
3878
|
+
if (repairOutcome) {
|
|
3879
|
+
const { verificationLoop } = repairOutcome;
|
|
3880
|
+
const shouldPromoteRepairs = verificationLoop.iterations.length > 0 && verificationLoop.bestIterationPromotable;
|
|
3881
|
+
if (!shouldPromoteRepairs) {
|
|
3882
|
+
} else {
|
|
3883
|
+
cleanupWorktreeAfterRun = false;
|
|
3884
|
+
try {
|
|
3885
|
+
const worktreePath = result.retainedWorktreePath;
|
|
3886
|
+
const baselineRef = result.baselineRef;
|
|
3887
|
+
const iterCount = verificationLoop.iterations.length;
|
|
3888
|
+
let commitSucceeded = false;
|
|
3889
|
+
const status = await measurePostProviderPhase(
|
|
3890
|
+
postProviderPhaseDurationsMs,
|
|
3891
|
+
"repairPromotionStatus",
|
|
3892
|
+
verbose,
|
|
3893
|
+
async () => execa3("git", ["status", "--porcelain"], {
|
|
3894
|
+
cwd: worktreePath,
|
|
3895
|
+
stdio: "pipe",
|
|
3896
|
+
reject: false
|
|
3897
|
+
})
|
|
3898
|
+
);
|
|
3899
|
+
const isDirty = String(status.stdout ?? "").trim().length > 0;
|
|
3900
|
+
const repairedSummary = await measurePostProviderPhase(
|
|
3901
|
+
postProviderPhaseDurationsMs,
|
|
3902
|
+
"repairPromotionChangeSummary",
|
|
3903
|
+
verbose,
|
|
3904
|
+
async () => getGitChangeSummary(worktreePath, baselineRef, true)
|
|
3905
|
+
);
|
|
3906
|
+
const finalDiffSnapshot = await measurePostProviderPhase(
|
|
3907
|
+
postProviderPhaseDurationsMs,
|
|
3908
|
+
"repairPromotionDiffSnapshot",
|
|
3909
|
+
verbose,
|
|
3910
|
+
async () => captureRepairDiffSnapshot(worktreePath, baselineRef)
|
|
3911
|
+
);
|
|
3912
|
+
if (!finalDiffSnapshot) {
|
|
3913
|
+
emitWarning(
|
|
3914
|
+
"PERF-003",
|
|
3915
|
+
{
|
|
3916
|
+
taskId,
|
|
3917
|
+
subsystem: "repair-loop",
|
|
3918
|
+
executionPhase: "validation"
|
|
3919
|
+
},
|
|
3920
|
+
{
|
|
3921
|
+
metadata: {
|
|
3922
|
+
reason: "post-repair diff snapshot unavailable; repair-scope classification will be unknown",
|
|
3923
|
+
baselineRef,
|
|
3924
|
+
worktreePath
|
|
3925
|
+
}
|
|
3926
|
+
}
|
|
3927
|
+
);
|
|
3928
|
+
}
|
|
3929
|
+
const repairScopeAnalysis = analyzeRepairScope({
|
|
3930
|
+
preRepairDiff: preRepairDiffSnapshot,
|
|
3931
|
+
finalDiff: finalDiffSnapshot
|
|
3932
|
+
});
|
|
3933
|
+
const repairPolicy = resolveRepairPromotionPolicy({
|
|
3934
|
+
classification: repairScopeAnalysis.classification,
|
|
3935
|
+
repairDelta: repairScopeAnalysis.repairDelta,
|
|
3936
|
+
scopePolicy: repairLoopConfig.scopePolicy,
|
|
3937
|
+
effectiveMode: repairPolicyCompletionMode(),
|
|
3938
|
+
allowOffScopeRepair: isOffScopeRepairOverrideEnabled()
|
|
3939
|
+
});
|
|
3940
|
+
const allowRepairCommit = repairPolicy.action === "allow" || repairPolicy.action === "warn";
|
|
3941
|
+
if (isDirty && !allowRepairCommit) {
|
|
3942
|
+
emitWarning(
|
|
3943
|
+
"PERF-003",
|
|
3944
|
+
{
|
|
3945
|
+
taskId,
|
|
3946
|
+
subsystem: "repair-loop",
|
|
3947
|
+
executionPhase: "validation"
|
|
3948
|
+
},
|
|
3949
|
+
{
|
|
3950
|
+
metadata: {
|
|
3951
|
+
reason: `Skipped repair commit: ${repairPolicy.reason}`,
|
|
3952
|
+
policyAction: repairPolicy.action,
|
|
3953
|
+
classification: repairScopeAnalysis.classification,
|
|
3954
|
+
finalArtifactTrust: repairPolicy.finalArtifactTrust,
|
|
3955
|
+
worktreePath
|
|
3956
|
+
}
|
|
3957
|
+
}
|
|
3958
|
+
);
|
|
3959
|
+
} else if (isDirty) {
|
|
3960
|
+
const stage = await measurePostProviderPhase(
|
|
3961
|
+
postProviderPhaseDurationsMs,
|
|
3962
|
+
"repairPromotionStage",
|
|
3963
|
+
verbose,
|
|
3964
|
+
async () => stageCommitEligibleChanges(worktreePath).then(
|
|
3965
|
+
(staged) => ({ staged, failure: void 0 }),
|
|
3966
|
+
(error) => ({
|
|
3967
|
+
staged: false,
|
|
3968
|
+
failure: error instanceof Error ? error.message : String(error)
|
|
3969
|
+
})
|
|
3970
|
+
)
|
|
3971
|
+
);
|
|
3972
|
+
if (stage.failure !== void 0 && verbose) {
|
|
3973
|
+
console.warn(
|
|
3974
|
+
chalk.yellow(
|
|
3975
|
+
`Failed to stage repairs (${stage.failure}); preserving worktree for manual commit.`
|
|
3976
|
+
)
|
|
3977
|
+
);
|
|
3978
|
+
}
|
|
3979
|
+
const isFullyClean = verificationLoop.passed;
|
|
3980
|
+
const commitMsg = isFullyClean ? `fix: repair-loop (${iterCount} iterations) - all gates pass` : `fix: repair-loop (${iterCount} iterations) - no new regressions vs baseline`;
|
|
3981
|
+
const commit = stage.staged ? await measurePostProviderPhase(
|
|
3982
|
+
postProviderPhaseDurationsMs,
|
|
3983
|
+
"repairPromotionCommit",
|
|
3984
|
+
verbose,
|
|
3985
|
+
async () => execa3("git", ["commit", "--no-verify", "-m", commitMsg], {
|
|
3986
|
+
cwd: worktreePath,
|
|
3987
|
+
stdio: "pipe",
|
|
3988
|
+
reject: false
|
|
3989
|
+
})
|
|
3990
|
+
) : void 0;
|
|
3991
|
+
commitSucceeded = commit !== void 0 && (commit.exitCode ?? 0) === 0;
|
|
3992
|
+
if (commit !== void 0 && (commit.exitCode ?? 0) !== 0 && verbose) {
|
|
3993
|
+
const output = String(commit.stdout ?? commit.stderr ?? "").trim();
|
|
3994
|
+
console.warn(
|
|
3995
|
+
chalk.yellow(
|
|
3996
|
+
`Failed to commit repairs (exit ${commit.exitCode ?? "unknown"}); preserving worktree for manual commit.${output ? `
|
|
3997
|
+
${output}` : ""}`
|
|
3998
|
+
)
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
}
|
|
4002
|
+
let restoredBaseline = false;
|
|
4003
|
+
try {
|
|
4004
|
+
const baseTree = await execGit(["rev-parse", `${baselineRef}^{tree}`], {
|
|
4005
|
+
cwd: worktreePath
|
|
4006
|
+
});
|
|
4007
|
+
restoredBaseline = baseTree.exitCode === 0 && await captureCurrentWorkspaceTreeIdentity(worktreePath) === String(baseTree.stdout).trim();
|
|
4008
|
+
} catch {
|
|
4009
|
+
}
|
|
4010
|
+
result.repairOutcome = {
|
|
4011
|
+
attempted: true,
|
|
4012
|
+
passed: verificationLoop.passed,
|
|
4013
|
+
passedBaseline: verificationLoop.passedBaseline,
|
|
4014
|
+
iterations: verificationLoop.iterations.length,
|
|
4015
|
+
stopReason: verificationLoop.stopReason,
|
|
4016
|
+
filesChanged: repairedSummary.filesChanged,
|
|
4017
|
+
committed: commitSucceeded,
|
|
4018
|
+
repairTrigger: buildRepairTriggerEvidence(repairOutcome),
|
|
4019
|
+
...repairScopeAnalysis.preRepairDiff ? { preRepairDiff: repairScopeAnalysis.preRepairDiff } : {},
|
|
4020
|
+
...repairScopeAnalysis.finalDiff ? { finalDiff: repairScopeAnalysis.finalDiff } : {},
|
|
4021
|
+
...repairScopeAnalysis.repairDelta ? { repairDelta: repairScopeAnalysis.repairDelta } : {},
|
|
4022
|
+
classification: repairScopeAnalysis.classification,
|
|
4023
|
+
judgeVerdictApplicability: repairScopeAnalysis.judgeVerdictApplicability,
|
|
4024
|
+
finalArtifactTrust: repairPolicy.finalArtifactTrust,
|
|
4025
|
+
policy: repairPolicy
|
|
4026
|
+
};
|
|
4027
|
+
if (!restoredBaseline && repairedSummary.filesChanged > 0) {
|
|
4028
|
+
result.gitMetrics = {
|
|
4029
|
+
filesChanged: repairedSummary.filesChanged,
|
|
4030
|
+
linesAdded: repairedSummary.linesAdded,
|
|
4031
|
+
linesDeleted: repairedSummary.linesRemoved,
|
|
4032
|
+
source: repairedSummary.source === "fallback" ? "unknown" : "worktree"
|
|
4033
|
+
};
|
|
4034
|
+
} else if (restoredBaseline) {
|
|
4035
|
+
result.gitMetrics = {
|
|
4036
|
+
filesChanged: 0,
|
|
4037
|
+
linesAdded: 0,
|
|
4038
|
+
linesDeleted: 0,
|
|
4039
|
+
source: "worktree"
|
|
4040
|
+
};
|
|
4041
|
+
result.filesChanged = result.linesAdded = result.linesRemoved = 0;
|
|
4042
|
+
result.emptyChanges = true;
|
|
4043
|
+
}
|
|
4044
|
+
if (!restoredBaseline && repairedSummary.filesChanged > 0)
|
|
4045
|
+
result.emptyChanges = false;
|
|
4046
|
+
if (repairedSummary.filesChanged > 0 || repairedSummary.source !== "fallback") {
|
|
4047
|
+
const repairedDiff = normalizeWorktreeDiff(
|
|
4048
|
+
await captureWorktreeDiff(worktreePath, baselineRef, true)
|
|
4049
|
+
);
|
|
4050
|
+
if (repairedDiff || restoredBaseline) {
|
|
4051
|
+
result.worktreeDiff = repairedDiff;
|
|
4052
|
+
}
|
|
4053
|
+
}
|
|
4054
|
+
if (verbose) {
|
|
4055
|
+
const baselineStatus = verificationLoop.passed ? "all gates pass" : "no new regressions";
|
|
4056
|
+
console.log(
|
|
4057
|
+
chalk.green(
|
|
4058
|
+
commitSucceeded ? `Verification repairs committed (${iterCount} iterations, ${repairedSummary.filesChanged} files, ${baselineStatus})` : `Verification repairs preserved (${iterCount} iterations, ${repairedSummary.filesChanged} files, ${baselineStatus})`
|
|
4059
|
+
)
|
|
4060
|
+
);
|
|
4061
|
+
}
|
|
4062
|
+
} catch (commitError) {
|
|
4063
|
+
if (verbose) {
|
|
4064
|
+
const msg = commitError instanceof Error ? commitError.message : String(commitError);
|
|
4065
|
+
console.warn(chalk.yellow(`Failed to commit repairs: ${msg}`));
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
if (result.gitMetrics && result.gitMetrics.filesChanged > 0) {
|
|
4072
|
+
result.filesChanged = result.gitMetrics.filesChanged;
|
|
4073
|
+
result.linesAdded = result.gitMetrics.linesAdded;
|
|
4074
|
+
result.linesRemoved = result.gitMetrics.linesDeleted;
|
|
4075
|
+
}
|
|
4076
|
+
const workspaceEditsApplied = result.workspaceEditsApplied ?? 0;
|
|
4077
|
+
if (result.gitMetrics && result.gitMetrics.filesChanged > 0 && (result.completeness ?? 0) === 0 && workspaceEditsApplied === 0) {
|
|
4078
|
+
console.warn(
|
|
4079
|
+
chalk.yellow(
|
|
4080
|
+
"[Telemetry] Git shows changes but completeness is 0 and no workspace edits were applied."
|
|
4081
|
+
),
|
|
4082
|
+
{
|
|
4083
|
+
filesChanged: result.gitMetrics.filesChanged,
|
|
4084
|
+
linesAdded: result.gitMetrics.linesAdded,
|
|
4085
|
+
linesDeleted: result.gitMetrics.linesDeleted,
|
|
4086
|
+
provider: variant.provider,
|
|
4087
|
+
executionIntent: variant.executionIntent
|
|
4088
|
+
}
|
|
4089
|
+
);
|
|
4090
|
+
}
|
|
4091
|
+
await runFallbackVerification();
|
|
4092
|
+
try {
|
|
4093
|
+
await measurePostProviderPhase(
|
|
4094
|
+
postProviderPhaseDurationsMs,
|
|
4095
|
+
"baselineComparison",
|
|
4096
|
+
verbose,
|
|
4097
|
+
async () => attachBaselineComparisonIfAvailable(result)
|
|
4098
|
+
);
|
|
4099
|
+
} catch {
|
|
4100
|
+
}
|
|
4101
|
+
try {
|
|
4102
|
+
await measurePostProviderPhase(
|
|
4103
|
+
postProviderPhaseDurationsMs,
|
|
4104
|
+
"registryDowngrade",
|
|
4105
|
+
verbose,
|
|
4106
|
+
async () => applyRegistryDowngradeIfApplicable(result)
|
|
4107
|
+
);
|
|
4108
|
+
} catch {
|
|
4109
|
+
}
|
|
4110
|
+
const repairedFiles = result.repairOutcome?.finalDiff?.files;
|
|
4111
|
+
attachOffScopeConfigWarning(result, {
|
|
4112
|
+
taskId,
|
|
4113
|
+
prompt,
|
|
4114
|
+
...repairedFiles ? { changedPaths: repairedFiles } : {}
|
|
4115
|
+
});
|
|
4116
|
+
measurePostProviderPhaseSync(
|
|
4117
|
+
postProviderPhaseDurationsMs,
|
|
4118
|
+
"outcomeDerivation",
|
|
4119
|
+
verbose,
|
|
4120
|
+
() => {
|
|
4121
|
+
result.outcome = deriveVariantOutcome(result);
|
|
4122
|
+
}
|
|
4123
|
+
);
|
|
4124
|
+
if (specSignal) {
|
|
4125
|
+
result.inputSpecWarning = {
|
|
4126
|
+
completenessScore: specSignal.completenessScore,
|
|
4127
|
+
guidanceTier: specSignal.guidanceTier,
|
|
4128
|
+
missingFields: [...specSignal.missingFields],
|
|
4129
|
+
hasFillPlaceholders: specSignal.hasFillPlaceholders
|
|
4130
|
+
};
|
|
4131
|
+
}
|
|
4132
|
+
if (verbose) {
|
|
4133
|
+
console.log(
|
|
4134
|
+
chalk.green(`Execution completed in ${duration.toFixed(2)}s (success: ${result.success})`)
|
|
4135
|
+
);
|
|
4136
|
+
console.log(chalk.gray(` ${formatVerboseWorkerCompletionMetric(result)}`));
|
|
4137
|
+
if (specSignal) {
|
|
4138
|
+
const missingNames = specSignal.missingFields.map(
|
|
4139
|
+
(field) => FIELD_DISPLAY_NAMES[field] ?? field
|
|
4140
|
+
);
|
|
4141
|
+
console.log(
|
|
4142
|
+
chalk.yellow(
|
|
4143
|
+
` Note: execution started from an incomplete spec (${Math.round(specSignal.completenessScore * 100)}% complete; missing: ${missingNames.join(", ")})`
|
|
4144
|
+
)
|
|
4145
|
+
);
|
|
4146
|
+
}
|
|
4147
|
+
}
|
|
4148
|
+
if (telemetryEnabled && telemetry) {
|
|
4149
|
+
try {
|
|
4150
|
+
const processReliability = computeProcessReliabilityMetrics({
|
|
4151
|
+
success: result.success,
|
|
4152
|
+
result: result.result
|
|
4153
|
+
});
|
|
4154
|
+
let terminationReason;
|
|
4155
|
+
if (result.terminationReason) {
|
|
4156
|
+
terminationReason = result.terminationReason;
|
|
4157
|
+
} else if (result.success) {
|
|
4158
|
+
terminationReason = "completed";
|
|
4159
|
+
} else if (result.timedOut) {
|
|
4160
|
+
terminationReason = "timeout";
|
|
4161
|
+
} else {
|
|
4162
|
+
terminationReason = "failed_error";
|
|
4163
|
+
}
|
|
4164
|
+
telemetry.logVariantEvent(taskId, formatVariantIdentifier(variant), "completed", {
|
|
4165
|
+
success: result.success,
|
|
4166
|
+
completeness: result.completeness,
|
|
4167
|
+
duration,
|
|
4168
|
+
provider: variant.provider,
|
|
4169
|
+
model: variant.model,
|
|
4170
|
+
executionMode: variant.executionMode,
|
|
4171
|
+
runMode,
|
|
4172
|
+
executionIntent: variant.executionIntent,
|
|
4173
|
+
behavioralMetrics: { processReliability },
|
|
4174
|
+
terminationReason
|
|
4175
|
+
});
|
|
4176
|
+
telemetry.logVariantEvent(taskId, formatVariantIdentifier(variant), "evaluated", {
|
|
4177
|
+
...resolveVariantEvaluation(result),
|
|
4178
|
+
completeness: result.completeness,
|
|
4179
|
+
provider: variant.provider,
|
|
4180
|
+
model: variant.model,
|
|
4181
|
+
executionMode: variant.executionMode,
|
|
4182
|
+
runMode,
|
|
4183
|
+
executionIntent: variant.executionIntent
|
|
4184
|
+
});
|
|
4185
|
+
await measurePostProviderPhase(
|
|
4186
|
+
postProviderPhaseDurationsMs,
|
|
4187
|
+
"telemetryArtifactWrite",
|
|
4188
|
+
verbose,
|
|
4189
|
+
async () => {
|
|
4190
|
+
const artifactResult = await telemetry.logVariantResultWithArtifact(
|
|
4191
|
+
taskId,
|
|
4192
|
+
formatVariantIdentifier(variant),
|
|
4193
|
+
result,
|
|
4194
|
+
{
|
|
4195
|
+
provider: variant.provider,
|
|
4196
|
+
model: variant.model,
|
|
4197
|
+
executionMode: variant.executionMode,
|
|
4198
|
+
runMode,
|
|
4199
|
+
executionIntent: variant.executionIntent
|
|
4200
|
+
}
|
|
4201
|
+
);
|
|
4202
|
+
if (artifactResult?.success && artifactResult.manifestPath) {
|
|
4203
|
+
result.artifactManifest = artifactResult.manifestPath;
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
);
|
|
4207
|
+
} catch (_err) {
|
|
4208
|
+
await warnOnce(
|
|
4209
|
+
"Telemetry logging failed mid-run; disabling telemetry for this worker.",
|
|
4210
|
+
telemetryWarning
|
|
4211
|
+
);
|
|
4212
|
+
telemetryEnabled = false;
|
|
4213
|
+
}
|
|
4214
|
+
}
|
|
4215
|
+
} catch (error) {
|
|
4216
|
+
const duration = Number(process.hrtime.bigint() - startTime) / 1e9;
|
|
4217
|
+
const errorObj = error instanceof Error ? error : new Error(String(error));
|
|
4218
|
+
result = {
|
|
4219
|
+
variant: formatVariantIdentifier(variant),
|
|
4220
|
+
provider: variant.provider,
|
|
4221
|
+
model: variant.model,
|
|
4222
|
+
executionMode: variant.executionMode,
|
|
4223
|
+
success: false,
|
|
4224
|
+
error: errorObj,
|
|
4225
|
+
duration,
|
|
4226
|
+
completeness: 0,
|
|
4227
|
+
executionIntent: variant.executionIntent,
|
|
4228
|
+
isBasicCompetition: variant.isBasicCompetition,
|
|
4229
|
+
// Record requested prompt mode even on failure
|
|
4230
|
+
promptModeRequested: variant.promptMode,
|
|
4231
|
+
...specSignal ? {
|
|
4232
|
+
inputSpecWarning: {
|
|
4233
|
+
completenessScore: specSignal.completenessScore,
|
|
4234
|
+
guidanceTier: specSignal.guidanceTier,
|
|
4235
|
+
missingFields: [...specSignal.missingFields],
|
|
4236
|
+
hasFillPlaceholders: specSignal.hasFillPlaceholders
|
|
4237
|
+
}
|
|
4238
|
+
} : {}
|
|
4239
|
+
};
|
|
4240
|
+
const hostTerminationMetadata = hostTerminationMetadataFromUnknown(errorObj);
|
|
4241
|
+
if (hostTerminationMetadata && classifyHostTermination(hostTerminationMetadata) === "timeout") {
|
|
4242
|
+
result.timedOut = true;
|
|
4243
|
+
}
|
|
4244
|
+
const errorTerminationReason = result.terminationReason || (result.timedOut ? "timeout" : "failed_crash");
|
|
4245
|
+
result.terminationReason = errorTerminationReason;
|
|
4246
|
+
result.outcome = deriveVariantOutcome(result);
|
|
4247
|
+
if (telemetryEnabled && telemetry) {
|
|
4248
|
+
try {
|
|
4249
|
+
const processReliability = computeProcessReliabilityMetrics({
|
|
4250
|
+
success: false,
|
|
4251
|
+
result: result.result
|
|
4252
|
+
});
|
|
4253
|
+
telemetry.logVariantEvent(taskId, formatVariantIdentifier(variant), "completed", {
|
|
4254
|
+
success: false,
|
|
4255
|
+
completeness: 0,
|
|
4256
|
+
duration,
|
|
4257
|
+
provider: variant.provider,
|
|
4258
|
+
model: variant.model,
|
|
4259
|
+
executionMode: variant.executionMode,
|
|
4260
|
+
runMode,
|
|
4261
|
+
executionIntent: variant.executionIntent,
|
|
4262
|
+
behavioralMetrics: { processReliability },
|
|
4263
|
+
terminationReason: errorTerminationReason
|
|
4264
|
+
});
|
|
4265
|
+
telemetry.logVariantEvent(taskId, formatVariantIdentifier(variant), "evaluated", {
|
|
4266
|
+
verdict: "failure",
|
|
4267
|
+
completeness: 0,
|
|
4268
|
+
codeDelivered: false,
|
|
4269
|
+
provider: variant.provider,
|
|
4270
|
+
model: variant.model,
|
|
4271
|
+
executionMode: variant.executionMode,
|
|
4272
|
+
runMode,
|
|
4273
|
+
executionIntent: variant.executionIntent
|
|
4274
|
+
});
|
|
4275
|
+
const artifactResult = await telemetry.logVariantResultWithArtifact(
|
|
4276
|
+
taskId,
|
|
4277
|
+
formatVariantIdentifier(variant),
|
|
4278
|
+
result,
|
|
4279
|
+
{
|
|
4280
|
+
provider: variant.provider,
|
|
4281
|
+
model: variant.model,
|
|
4282
|
+
executionMode: variant.executionMode,
|
|
4283
|
+
runMode,
|
|
4284
|
+
executionIntent: variant.executionIntent
|
|
4285
|
+
}
|
|
4286
|
+
);
|
|
4287
|
+
if (artifactResult?.success && artifactResult.manifestPath) {
|
|
4288
|
+
result.artifactManifest = artifactResult.manifestPath;
|
|
4289
|
+
}
|
|
4290
|
+
} catch (_err) {
|
|
4291
|
+
await warnOnce(
|
|
4292
|
+
"Telemetry logging failed mid-run; disabling telemetry for this worker.",
|
|
4293
|
+
telemetryWarning
|
|
4294
|
+
);
|
|
4295
|
+
telemetryEnabled = false;
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
if (verbose) {
|
|
4299
|
+
console.error(chalk.red(`Execution failed: ${errorObj.message}`));
|
|
4300
|
+
if (specSignal) {
|
|
4301
|
+
const missingNames = specSignal.missingFields.map(
|
|
4302
|
+
(field) => FIELD_DISPLAY_NAMES[field] ?? field
|
|
4303
|
+
);
|
|
4304
|
+
console.error(
|
|
4305
|
+
chalk.yellow(
|
|
4306
|
+
` Note: execution started from an incomplete spec (${Math.round(specSignal.completenessScore * 100)}% complete; missing: ${missingNames.join(", ")})`
|
|
4307
|
+
)
|
|
4308
|
+
);
|
|
4309
|
+
}
|
|
4310
|
+
}
|
|
4311
|
+
}
|
|
4312
|
+
providerOutputForwarder.close();
|
|
4313
|
+
if (!result.postProviderPhaseDurationsMs) {
|
|
4314
|
+
result.postProviderPhaseDurationsMs = {};
|
|
4315
|
+
}
|
|
4316
|
+
const finalPostProviderPhaseDurationsMs = result.postProviderPhaseDurationsMs;
|
|
4317
|
+
if (result.success === true && shouldDowngradeSuccessfulResultForShutdown({
|
|
4318
|
+
shutdownRequested,
|
|
4319
|
+
shutdownRequestedAtMs,
|
|
4320
|
+
executionResultSettledAtMs
|
|
4321
|
+
})) {
|
|
4322
|
+
const signal = shutdownSignal ?? "SIGTERM";
|
|
4323
|
+
result.success = false;
|
|
4324
|
+
result.timedOut = signal === "SIGTERM";
|
|
4325
|
+
result.terminationReason = signal === "SIGINT" ? "canceled_user" : "timeout";
|
|
4326
|
+
result.error = buildShutdownAbortReason(signal);
|
|
4327
|
+
}
|
|
4328
|
+
measurePostProviderPhaseSync(
|
|
4329
|
+
finalPostProviderPhaseDurationsMs,
|
|
4330
|
+
"finalOutcomeDerivation",
|
|
4331
|
+
verbose,
|
|
4332
|
+
() => {
|
|
4333
|
+
result.outcome = deriveVariantOutcome(result);
|
|
4334
|
+
}
|
|
4335
|
+
);
|
|
4336
|
+
const worktreePathForCleanup = cleanupWorktreeAfterRun && result.success === true && typeof result.retainedWorktreePath === "string" ? result.retainedWorktreePath.trim() : void 0;
|
|
4337
|
+
if (worktreePathForCleanup) {
|
|
4338
|
+
result.retainedWorktreePath = void 0;
|
|
4339
|
+
}
|
|
4340
|
+
measurePostProviderPhaseSync(
|
|
4341
|
+
finalPostProviderPhaseDurationsMs,
|
|
4342
|
+
"resultFinalize",
|
|
4343
|
+
verbose,
|
|
4344
|
+
() => {
|
|
4345
|
+
if (result.result) {
|
|
4346
|
+
if (isNativeResearchEnabledFor(variant.provider)) {
|
|
4347
|
+
const rawTranscript = `${result.result.rawStdout ?? ""}
|
|
4348
|
+
${result.result.rawStderr ?? ""}`;
|
|
4349
|
+
if (rawTranscript.trim().length > 0) {
|
|
4350
|
+
const scan = scanWebToolEvents(rawTranscript);
|
|
4351
|
+
if (scan.corroboratedUrls.size > 0) {
|
|
4352
|
+
result.result.researchCorroboratedUrls = Array.from(scan.corroboratedUrls);
|
|
4353
|
+
}
|
|
4354
|
+
if (scan.observedWebToolEvent) {
|
|
4355
|
+
result.result.researchObservedWebToolEvent = true;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
}
|
|
4359
|
+
result.result.rawStdout = void 0;
|
|
4360
|
+
result.result.rawStderr = void 0;
|
|
4361
|
+
if (result.result.stdout && Buffer.byteLength(result.result.stdout, "utf8") > 2e5) {
|
|
4362
|
+
result.result.stdout = truncateToByteLimit(result.result.stdout, 2e5);
|
|
4363
|
+
}
|
|
4364
|
+
if (result.result.stderr && Buffer.byteLength(result.result.stderr, "utf8") > 2e5) {
|
|
4365
|
+
result.result.stderr = truncateToByteLimit(result.result.stderr, 2e5);
|
|
4366
|
+
}
|
|
4367
|
+
}
|
|
4368
|
+
}
|
|
4369
|
+
);
|
|
4370
|
+
sanitizeVariantResult(result);
|
|
4371
|
+
try {
|
|
4372
|
+
await measurePostProviderPhase(
|
|
4373
|
+
finalPostProviderPhaseDurationsMs,
|
|
4374
|
+
"resultWrite",
|
|
4375
|
+
verbose,
|
|
4376
|
+
async () => writeResultFileAtomically(resultFile, result)
|
|
4377
|
+
);
|
|
4378
|
+
emitWorkerLifecycleMarker(RESULT_WRITTEN_MARKER_PREFIX, elapsedMsSince(startTime));
|
|
4379
|
+
emitWorkerLifecycleMarker(
|
|
4380
|
+
POST_PROVIDER_PHASES_MARKER_PREFIX,
|
|
4381
|
+
finalPostProviderPhaseDurationsMs
|
|
4382
|
+
);
|
|
4383
|
+
if (verbose) {
|
|
4384
|
+
console.log(chalk.green(`Result written to ${resultFile}`));
|
|
4385
|
+
}
|
|
4386
|
+
if (telemetryEnabled && telemetry) {
|
|
4387
|
+
try {
|
|
4388
|
+
TelemetryLogger.disposeShared();
|
|
4389
|
+
} catch (_err) {
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
if (worktreePathForCleanup && worktreePathForCleanup.length > 0) {
|
|
4393
|
+
await measurePostProviderPhase(
|
|
4394
|
+
finalPostProviderPhaseDurationsMs,
|
|
4395
|
+
"cleanup",
|
|
4396
|
+
verbose,
|
|
4397
|
+
async () => tryCleanupRetainedWorktree(worktreePathForCleanup, verbose)
|
|
4398
|
+
);
|
|
4399
|
+
}
|
|
4400
|
+
applyWorkerEnvOverrides({
|
|
4401
|
+
BUILD_RETAIN_BASIC_WORKTREE: prevRetainBasicWorktree
|
|
4402
|
+
});
|
|
4403
|
+
emitWorkerLifecycleMarker(CLEANUP_START_MARKER_PREFIX, elapsedMsSince(startTime));
|
|
4404
|
+
await measurePostProviderPhase(
|
|
4405
|
+
finalPostProviderPhaseDurationsMs,
|
|
4406
|
+
"cleanup",
|
|
4407
|
+
verbose,
|
|
4408
|
+
async () => signalCoordinator.runCleanupHandlers("variant-worker exit", {
|
|
4409
|
+
timeoutMs: SIGNAL_CLEANUP_TIMEOUT_MS
|
|
4410
|
+
})
|
|
4411
|
+
);
|
|
4412
|
+
emitWorkerLifecycleMarker(CLEANUP_COMPLETE_MARKER_PREFIX, elapsedMsSince(startTime));
|
|
4413
|
+
emitWorkerLifecycleMarker(
|
|
4414
|
+
POST_PROVIDER_PHASES_MARKER_PREFIX,
|
|
4415
|
+
finalPostProviderPhaseDurationsMs
|
|
4416
|
+
);
|
|
4417
|
+
emitWorkerLifecycleMarker(ACTIVE_HANDLES_MARKER_PREFIX, activeHandleSummary());
|
|
4418
|
+
if (preserveWorktreesForDebug) {
|
|
4419
|
+
applyWorkerEnvOverrides({
|
|
4420
|
+
BUILD_PRESERVE_WORKTREES: prevPreserveWorktrees
|
|
4421
|
+
});
|
|
4422
|
+
}
|
|
4423
|
+
return;
|
|
4424
|
+
} catch (error) {
|
|
4425
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4426
|
+
console.error(chalk.red(`Failed to write result: ${message}`));
|
|
4427
|
+
if (telemetryEnabled && telemetry) {
|
|
4428
|
+
try {
|
|
4429
|
+
TelemetryLogger.disposeShared();
|
|
4430
|
+
} catch (_err) {
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
applyWorkerEnvOverrides({
|
|
4434
|
+
BUILD_RETAIN_BASIC_WORKTREE: prevRetainBasicWorktree
|
|
4435
|
+
});
|
|
4436
|
+
await signalCoordinator.runCleanupHandlers("variant-worker fatal-exit", {
|
|
4437
|
+
timeoutMs: SIGNAL_CLEANUP_TIMEOUT_MS
|
|
4438
|
+
});
|
|
4439
|
+
if (preserveWorktreesForDebug) {
|
|
4440
|
+
applyWorkerEnvOverrides({
|
|
4441
|
+
BUILD_PRESERVE_WORKTREES: prevPreserveWorktrees
|
|
4442
|
+
});
|
|
4443
|
+
}
|
|
4444
|
+
throw new ExitCodeError(1, message);
|
|
4445
|
+
}
|
|
4446
|
+
} finally {
|
|
4447
|
+
uninstallPostProviderSignalHandlers();
|
|
4448
|
+
signalCoordinator.unregister(signalHandlerName);
|
|
4449
|
+
signalCoordinator.unregister(processLifecycleSignalHandlerName);
|
|
4450
|
+
setProcessLifecycleManager(void 0);
|
|
4451
|
+
}
|
|
4452
|
+
}
|
|
4453
|
+
var workerEntrypoint = fileURLToPath(import.meta.url);
|
|
4454
|
+
var invokedScript = process.argv[1] ? path2.resolve(process.argv[1]) : null;
|
|
4455
|
+
var isDirectInvocation = invokedScript !== null && path2.resolve(workerEntrypoint) === invokedScript;
|
|
4456
|
+
async function runVariantWorkerEntrypoint(entryOptions = {}) {
|
|
4457
|
+
if (!entryOptions.runtimeAlreadyChecked) {
|
|
4458
|
+
assertBuildRuntimeReady({ command: "variant-worker" });
|
|
4459
|
+
}
|
|
4460
|
+
try {
|
|
4461
|
+
await main();
|
|
4462
|
+
return 0;
|
|
4463
|
+
} catch (error) {
|
|
4464
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4465
|
+
console.error(chalk.red(`Unhandled error: ${message}`));
|
|
4466
|
+
try {
|
|
4467
|
+
const argv = process.argv;
|
|
4468
|
+
const flagIdx = argv.indexOf("--result-file");
|
|
4469
|
+
const resultFile = flagIdx !== -1 && flagIdx + 1 < argv.length ? argv[flagIdx + 1] : void 0;
|
|
4470
|
+
if (resultFile) {
|
|
4471
|
+
const payload = {
|
|
4472
|
+
success: false,
|
|
4473
|
+
error: { message, stage: "bootstrap" },
|
|
4474
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4475
|
+
};
|
|
4476
|
+
await writeResultFileAtomically(resultFile, payload);
|
|
4477
|
+
}
|
|
4478
|
+
} catch {
|
|
4479
|
+
}
|
|
4480
|
+
return error instanceof ExitCodeError ? error.exitCode : 1;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
if (process.env.VITEST !== "1" && isDirectInvocation) {
|
|
4484
|
+
runVariantWorkerEntrypoint().then((exitCode) => {
|
|
4485
|
+
if (exitCode === 0) {
|
|
4486
|
+
process.exitCode = 0;
|
|
4487
|
+
} else {
|
|
4488
|
+
process.exit(exitCode);
|
|
4489
|
+
}
|
|
4490
|
+
}).catch((error) => {
|
|
4491
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}
|
|
4492
|
+
`);
|
|
4493
|
+
process.exit(1);
|
|
4494
|
+
});
|
|
4495
|
+
}
|
|
4496
|
+
export {
|
|
4497
|
+
__testOnlyWriteResultFileAtomically,
|
|
4498
|
+
attachBaselineComparisonIfAvailable,
|
|
4499
|
+
buildRepairTriggerEvidence,
|
|
4500
|
+
buildShutdownAbortReason,
|
|
4501
|
+
createProviderOutputForwarder,
|
|
4502
|
+
decideRepairAdmission,
|
|
4503
|
+
executeWorker,
|
|
4504
|
+
extractOriginalPrompt,
|
|
4505
|
+
findVariantWorktreePathForTimeout,
|
|
4506
|
+
formatVerboseWorkerCompletionMetric,
|
|
4507
|
+
initializeWorkerExecutionContext,
|
|
4508
|
+
isValidationArtifactOnlyPrompt,
|
|
4509
|
+
resolveInitialVerificationDisposition,
|
|
4510
|
+
resolveRepairPromotionPolicy,
|
|
4511
|
+
runVariantWorkerEntrypoint,
|
|
4512
|
+
serializeErrorForJson,
|
|
4513
|
+
shouldDowngradeSuccessfulResultForShutdown,
|
|
4514
|
+
shouldTrustVerificationLoopBaseline,
|
|
4515
|
+
tryCleanupRetainedWorktree
|
|
4516
|
+
};
|
|
4517
|
+
//# sourceMappingURL=variant-worker-COB5YTJX.js.map
|