@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,3803 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assignDuplicateVariantInstanceIds,
|
|
3
|
+
cleanupSynthesisInputPatches,
|
|
4
|
+
resolveBasicCompetitionInterruption,
|
|
5
|
+
resolveSynthesisInputPatchDir,
|
|
6
|
+
runBasicCompetition,
|
|
7
|
+
selectWinnerWithJudge
|
|
8
|
+
} from "./chunk-YBHUB4XR.js";
|
|
9
|
+
import {
|
|
10
|
+
createEvaluationHandleFinalState,
|
|
11
|
+
finalizeEvaluationHandleAsync,
|
|
12
|
+
isDurableHandleCompletion,
|
|
13
|
+
toEvaluationHandleWinnerMetadata,
|
|
14
|
+
updateEvaluationHandleWithResultAsync
|
|
15
|
+
} from "./chunk-NTD6CSI6.js";
|
|
16
|
+
import {
|
|
17
|
+
evaluateStageCompletionProof,
|
|
18
|
+
proofEstablishesSatisfaction,
|
|
19
|
+
resolveProviderExecutionIntentFromStage,
|
|
20
|
+
stageHasHostDeterministicCompletionSurface
|
|
21
|
+
} from "./chunk-QAMAV4DT.js";
|
|
22
|
+
import {
|
|
23
|
+
collectDeclaredStageTargets
|
|
24
|
+
} from "./chunk-45KUJBDI.js";
|
|
25
|
+
import {
|
|
26
|
+
changesHaveVerifiableCodeSurface
|
|
27
|
+
} from "./chunk-OXK5HA4B.js";
|
|
28
|
+
import {
|
|
29
|
+
providerReportsUsage,
|
|
30
|
+
readCostMetricsUnavailableReason,
|
|
31
|
+
readUsageUnavailableReason
|
|
32
|
+
} from "./chunk-JQUMSIGC.js";
|
|
33
|
+
import {
|
|
34
|
+
JudgeCoordinator,
|
|
35
|
+
evaluateMatrixAuthority,
|
|
36
|
+
extractEvidenceAnchors,
|
|
37
|
+
generateCompetitionId,
|
|
38
|
+
isNegativeClaim,
|
|
39
|
+
resolveMatrixAuthorityPolicy,
|
|
40
|
+
resolveTypedCompletenessAuthority
|
|
41
|
+
} from "./chunk-DCACX7BV.js";
|
|
42
|
+
import {
|
|
43
|
+
resolveJudgeModelRoles
|
|
44
|
+
} from "./chunk-ARARVIZK.js";
|
|
45
|
+
import {
|
|
46
|
+
isAcceptedMatrixVerdict
|
|
47
|
+
} from "./chunk-6PZY2TTU.js";
|
|
48
|
+
import {
|
|
49
|
+
categorizeFile
|
|
50
|
+
} from "./chunk-QTCLJHLZ.js";
|
|
51
|
+
import {
|
|
52
|
+
ArtifactNotFoundError,
|
|
53
|
+
createSecurityBoundary,
|
|
54
|
+
resolvePromptArtifacts
|
|
55
|
+
} from "./chunk-SK3ZFONO.js";
|
|
56
|
+
import {
|
|
57
|
+
isProviderInvocationFailure,
|
|
58
|
+
judgeProviderFailed
|
|
59
|
+
} from "./chunk-UJYDH2MW.js";
|
|
60
|
+
import {
|
|
61
|
+
deriveAiJudgeInvocationArgs
|
|
62
|
+
} from "./chunk-EJVCDTAN.js";
|
|
63
|
+
import {
|
|
64
|
+
applyFlywheelModelDefaults
|
|
65
|
+
} from "./chunk-DSRLVYBS.js";
|
|
66
|
+
import {
|
|
67
|
+
decideStageChangeScope
|
|
68
|
+
} from "./chunk-DS2JZ677.js";
|
|
69
|
+
import {
|
|
70
|
+
findAllowlistViolations,
|
|
71
|
+
isPathAllowed,
|
|
72
|
+
normalizePath
|
|
73
|
+
} from "./chunk-XZOXEIN4.js";
|
|
74
|
+
import {
|
|
75
|
+
getProjectRoot
|
|
76
|
+
} from "./chunk-6TRD7TEE.js";
|
|
77
|
+
import {
|
|
78
|
+
JUDGE_FEATURE_FLAGS
|
|
79
|
+
} from "./chunk-ZJB6SQPA.js";
|
|
80
|
+
import {
|
|
81
|
+
resolveJudgeRunSet
|
|
82
|
+
} from "./chunk-IVZEONFC.js";
|
|
83
|
+
import {
|
|
84
|
+
filterWorktreeRunSentinelPaths
|
|
85
|
+
} from "./chunk-2NMNFUQA.js";
|
|
86
|
+
import {
|
|
87
|
+
formatVariantIdentifier,
|
|
88
|
+
parseVariantIdentifier
|
|
89
|
+
} from "./chunk-TGXXYDL4.js";
|
|
90
|
+
import {
|
|
91
|
+
isRecoverableCompetitionDiff
|
|
92
|
+
} from "./chunk-SKTFRFQ7.js";
|
|
93
|
+
import {
|
|
94
|
+
deriveTestVerificationDisplay,
|
|
95
|
+
gateStatusToDisplayVerdict
|
|
96
|
+
} from "./chunk-R2CKS6WT.js";
|
|
97
|
+
import {
|
|
98
|
+
deriveVariantOutcome,
|
|
99
|
+
hasUnavailableTestVerificationEvidence,
|
|
100
|
+
repairOutcomeCanSupersedeRawTouchedFailure
|
|
101
|
+
} from "./chunk-3OPP2OZB.js";
|
|
102
|
+
import {
|
|
103
|
+
isUnavailableVerificationFinding
|
|
104
|
+
} from "./chunk-PXKERBKX.js";
|
|
105
|
+
import {
|
|
106
|
+
prepareUntrackedIntentIndex
|
|
107
|
+
} from "./chunk-QFLBRWCJ.js";
|
|
108
|
+
import {
|
|
109
|
+
parseDiffGitHeaderPaths,
|
|
110
|
+
parseUnifiedDiffPathLine
|
|
111
|
+
} from "./chunk-5FKHIKTS.js";
|
|
112
|
+
import {
|
|
113
|
+
taskVariantId
|
|
114
|
+
} from "./chunk-F55YABKV.js";
|
|
115
|
+
import {
|
|
116
|
+
readTrackedToolOutputRootsSync,
|
|
117
|
+
shouldExcludeFile
|
|
118
|
+
} from "./chunk-5EQN56OZ.js";
|
|
119
|
+
import {
|
|
120
|
+
runVerifyCommands
|
|
121
|
+
} from "./chunk-AHU3FKPA.js";
|
|
122
|
+
import {
|
|
123
|
+
isUnderRootToolOutputDir
|
|
124
|
+
} from "./chunk-QOIASNEJ.js";
|
|
125
|
+
import {
|
|
126
|
+
LabPathResolver
|
|
127
|
+
} from "./chunk-C3KCMAJP.js";
|
|
128
|
+
import {
|
|
129
|
+
execGit
|
|
130
|
+
} from "./chunk-MHLEZQQI.js";
|
|
131
|
+
import {
|
|
132
|
+
isInvalidExplicitConfigError
|
|
133
|
+
} from "./chunk-QJ6OEH4K.js";
|
|
134
|
+
import {
|
|
135
|
+
normalizeReasoningEffortInput
|
|
136
|
+
} from "./chunk-4BTDUT2S.js";
|
|
137
|
+
import {
|
|
138
|
+
createExecutionContext
|
|
139
|
+
} from "./chunk-7VRJHTGZ.js";
|
|
140
|
+
import {
|
|
141
|
+
sanitizeString
|
|
142
|
+
} from "./chunk-CPSVQR2I.js";
|
|
143
|
+
|
|
144
|
+
// src/automation/flywheel-competition-executor.ts
|
|
145
|
+
import * as fs from "fs/promises";
|
|
146
|
+
import * as path from "path";
|
|
147
|
+
import chalk from "chalk";
|
|
148
|
+
|
|
149
|
+
// src/automation/competition-pick-label.ts
|
|
150
|
+
function competitionPickLabel(successCount) {
|
|
151
|
+
return successCount === 0 ? "Judge pick (gates failed)" : "Winner";
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/automation/gate-evidence.ts
|
|
155
|
+
var DETAIL_TAIL_LINES = 6;
|
|
156
|
+
var DETAIL_LINE_MAX_CHARS = 200;
|
|
157
|
+
var GATE_EVIDENCE_ARTIFACT_NAME = "verification-findings";
|
|
158
|
+
function selectFailingGateFindings(findings) {
|
|
159
|
+
return (findings ?? []).filter(
|
|
160
|
+
(finding) => finding.severity === "error" || Number.isFinite(finding.exitCode) && finding.exitCode !== 0
|
|
161
|
+
).map((finding) => ({
|
|
162
|
+
...finding,
|
|
163
|
+
command: sanitizeString(finding.command),
|
|
164
|
+
...finding.outputTail === void 0 ? {} : { outputTail: sanitizeString(finding.outputTail) }
|
|
165
|
+
}));
|
|
166
|
+
}
|
|
167
|
+
function formatFailingGateDetail(findings) {
|
|
168
|
+
const failing = selectFailingGateFindings(findings);
|
|
169
|
+
const first = failing[0];
|
|
170
|
+
if (!first) return void 0;
|
|
171
|
+
const others = failing.length - 1;
|
|
172
|
+
const more = others > 0 ? ` (+${others} more failing gate${others === 1 ? "" : "s"})` : "";
|
|
173
|
+
const head = isUnavailableVerificationFinding(first) ? `${first.command} unavailable (exit ${first.exitCode})${more}` : `${first.command} exited ${first.exitCode}${more}`;
|
|
174
|
+
const tail = (first.outputTail ?? "").split(/\r?\n/).map((line) => line.trimEnd()).filter((line) => line.length > 0).slice(-DETAIL_TAIL_LINES).map(
|
|
175
|
+
(line) => line.length > DETAIL_LINE_MAX_CHARS ? `${line.slice(0, DETAIL_LINE_MAX_CHARS)}...` : line
|
|
176
|
+
);
|
|
177
|
+
return tail.length > 0 ? `${head}
|
|
178
|
+
${tail.map((line) => ` ${line}`).join("\n")}` : head;
|
|
179
|
+
}
|
|
180
|
+
function gateEvidenceField(verification) {
|
|
181
|
+
const failing = selectFailingGateFindings(verification?.findings);
|
|
182
|
+
return failing.length > 0 ? { verificationFindings: failing } : {};
|
|
183
|
+
}
|
|
184
|
+
async function persistGateEvidenceArtifact(artifactStore, stageId, verification) {
|
|
185
|
+
const failing = selectFailingGateFindings(verification?.findings);
|
|
186
|
+
if (failing.length === 0) return;
|
|
187
|
+
try {
|
|
188
|
+
await artifactStore.save(stageId, GATE_EVIDENCE_ARTIFACT_NAME, "json", { findings: failing });
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/automation/verification-polarity.ts
|
|
194
|
+
function normalizedSignal(value) {
|
|
195
|
+
const trimmed = value?.trim().toLowerCase();
|
|
196
|
+
return trimmed ? trimmed : void 0;
|
|
197
|
+
}
|
|
198
|
+
function testsNotRunWithoutTestableSurface(input) {
|
|
199
|
+
if (input.testSkipReason !== "no-test-files-changed") return false;
|
|
200
|
+
const changedFiles = input.changedFiles;
|
|
201
|
+
if (!changedFiles || changedFiles.length === 0) return false;
|
|
202
|
+
if (changesHaveVerifiableCodeSurface([...changedFiles])) return false;
|
|
203
|
+
if (normalizedSignal(input.testStatus) !== "not_run") return false;
|
|
204
|
+
const raw = normalizedSignal(input.rawTestStatus);
|
|
205
|
+
if (raw !== void 0 && raw !== "not_run") return false;
|
|
206
|
+
if (input.exitCode !== void 0 && input.exitCode !== null) return false;
|
|
207
|
+
if (typeof input.failedTests === "number" && input.failedTests !== 0) return false;
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
function hasExplicitVerificationSignal(input) {
|
|
211
|
+
if (input.verification && typeof input.verification.passed === "boolean") {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (typeof input.verification?.status === "string" && input.verification.status.trim()) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
if (typeof input.testStatus === "string" && input.testStatus.trim()) {
|
|
218
|
+
return true;
|
|
219
|
+
}
|
|
220
|
+
if (typeof input.rawTestStatus === "string" && input.rawTestStatus.trim()) {
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
if (typeof input.exitCode === "number") {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
if (typeof input.failedTests === "number") {
|
|
227
|
+
return true;
|
|
228
|
+
}
|
|
229
|
+
return false;
|
|
230
|
+
}
|
|
231
|
+
function verificationRecordsBaselineClean(verification) {
|
|
232
|
+
if (!verification) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (verification.passedBaseline === false) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
const status = verification.status?.trim().toLowerCase();
|
|
239
|
+
if (status === "baseline_clean") {
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
if (verification.passedBaseline !== true) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
return !status || gateStatusToDisplayVerdict(status) !== "unavailable";
|
|
246
|
+
}
|
|
247
|
+
function verificationPolarityAllowsStageSuccess(input) {
|
|
248
|
+
if (!hasExplicitVerificationSignal(input)) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
if (verificationRecordsBaselineClean(input.verification)) {
|
|
252
|
+
return true;
|
|
253
|
+
}
|
|
254
|
+
if (input.verification?.passed === false) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
const status = input.verification?.status?.trim().toLowerCase();
|
|
258
|
+
if (status === "failed" || status === "unavailable") {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (testsNotRunWithoutTestableSurface(input)) {
|
|
262
|
+
return !status || gateStatusToDisplayVerdict(status) !== "unavailable";
|
|
263
|
+
}
|
|
264
|
+
const hasExplicitTestSignal = typeof input.testStatus === "string" && input.testStatus.trim().length > 0 || typeof input.rawTestStatus === "string" && input.rawTestStatus.trim().length > 0 || typeof input.exitCode === "number" || typeof input.failedTests === "number";
|
|
265
|
+
if (hasExplicitTestSignal) {
|
|
266
|
+
const display = deriveTestVerificationDisplay({
|
|
267
|
+
testStatus: input.testStatus,
|
|
268
|
+
rawTestStatus: input.rawTestStatus,
|
|
269
|
+
exitCode: input.exitCode,
|
|
270
|
+
failedTests: input.failedTests
|
|
271
|
+
});
|
|
272
|
+
return display === "passed";
|
|
273
|
+
}
|
|
274
|
+
return input.verification?.passed === true || status === "passed";
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// src/automation/flywheel-apply-winner.ts
|
|
278
|
+
function toPolarityInput(input) {
|
|
279
|
+
return {
|
|
280
|
+
verification: input.verification,
|
|
281
|
+
testStatus: input.testStatus,
|
|
282
|
+
rawTestStatus: input.rawTestStatus,
|
|
283
|
+
exitCode: input.exitCode,
|
|
284
|
+
failedTests: input.failedTests
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
function classifyVerificationBlocker(input) {
|
|
288
|
+
if (verificationPolarityAllowsStageSuccess(toPolarityInput(input))) {
|
|
289
|
+
return void 0;
|
|
290
|
+
}
|
|
291
|
+
const status = input.verification?.status?.trim().toLowerCase();
|
|
292
|
+
const display = deriveTestVerificationDisplay({
|
|
293
|
+
testStatus: input.testStatus,
|
|
294
|
+
rawTestStatus: input.rawTestStatus,
|
|
295
|
+
exitCode: input.exitCode,
|
|
296
|
+
failedTests: input.failedTests,
|
|
297
|
+
// A recorded unavailable test run keeps its non-zero exit (missing
|
|
298
|
+
// toolchain, zero tests collected) without becoming a gate failure.
|
|
299
|
+
testsUnavailable: hasUnavailableTestVerificationEvidence({
|
|
300
|
+
testStatus: input.rawTestStatus ?? input.testStatus ?? void 0
|
|
301
|
+
})
|
|
302
|
+
});
|
|
303
|
+
if (status === "failed" || display === "failed") {
|
|
304
|
+
return "gates-failed";
|
|
305
|
+
}
|
|
306
|
+
if (input.verification?.passed === false && status !== "unavailable") {
|
|
307
|
+
return "gates-failed";
|
|
308
|
+
}
|
|
309
|
+
if (status === "unavailable" || display === "unavailable" || input.testStatus === "unavailable" || input.testStatus === "not_run") {
|
|
310
|
+
return "tests-unavailable";
|
|
311
|
+
}
|
|
312
|
+
return "gates-failed";
|
|
313
|
+
}
|
|
314
|
+
function classifyStageValidationFailure(input) {
|
|
315
|
+
const verificationBlocker = classifyVerificationBlocker(input);
|
|
316
|
+
if (verificationBlocker) return verificationBlocker;
|
|
317
|
+
if (input.score < input.threshold) return "below-threshold";
|
|
318
|
+
return void 0;
|
|
319
|
+
}
|
|
320
|
+
function formatStageValidationFailureMessage(predicate, score, threshold, findings) {
|
|
321
|
+
const base = formatStageValidationFailureHead(predicate, score, threshold);
|
|
322
|
+
const detail = formatFailingGateDetail(findings);
|
|
323
|
+
return detail ? `${base}: ${detail}` : base;
|
|
324
|
+
}
|
|
325
|
+
function describeStageValidationFailure(input) {
|
|
326
|
+
const predicate = classifyStageValidationFailure(input);
|
|
327
|
+
return {
|
|
328
|
+
predicate,
|
|
329
|
+
message: formatStageValidationFailureMessage(
|
|
330
|
+
predicate,
|
|
331
|
+
input.score,
|
|
332
|
+
input.threshold,
|
|
333
|
+
input.verification?.findings
|
|
334
|
+
)
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
function formatStageValidationFailureHead(predicate, score, threshold) {
|
|
338
|
+
switch (predicate) {
|
|
339
|
+
case "tests-unavailable":
|
|
340
|
+
return `Verification unavailable (score ${score}, threshold ${threshold})`;
|
|
341
|
+
case "gates-failed":
|
|
342
|
+
return `Gates failed (score ${score}, threshold ${threshold})`;
|
|
343
|
+
case "below-threshold":
|
|
344
|
+
return `Score ${score} below threshold ${threshold}`;
|
|
345
|
+
default:
|
|
346
|
+
return `Validation did not pass (score ${score}, threshold ${threshold})`;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
function formatWinnerDeliveryRefusalReason(predicate) {
|
|
350
|
+
switch (predicate) {
|
|
351
|
+
case "tests-unavailable":
|
|
352
|
+
return "tests unavailable";
|
|
353
|
+
case "gates-failed":
|
|
354
|
+
return "gates failed";
|
|
355
|
+
case "below-threshold":
|
|
356
|
+
return "score below threshold";
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
function winnerHasNoApplicableTests(winner) {
|
|
360
|
+
if (winner.testStatus !== "not_run") return false;
|
|
361
|
+
if (winner.testsExecuted?.skipReason !== "no-test-files-changed") return false;
|
|
362
|
+
const changedFiles = winner.changedFiles ?? [];
|
|
363
|
+
if (changedFiles.length === 0) return false;
|
|
364
|
+
return changedFiles.every((file) => categorizeFile(file) === "docs");
|
|
365
|
+
}
|
|
366
|
+
function resolveTrustedCandidateTestStatus(candidate) {
|
|
367
|
+
const status = candidate.testStatus;
|
|
368
|
+
if (candidate.repairedTestStatus !== void 0 && (status === "failed" || status === "error" || status === "timeout")) {
|
|
369
|
+
return candidate.repairedTestStatus;
|
|
370
|
+
}
|
|
371
|
+
return status;
|
|
372
|
+
}
|
|
373
|
+
function isExecutedTestFailure(finding) {
|
|
374
|
+
return finding.kind === "test" && finding.severity === "error" && finding.exitCode !== 0 && finding.exitCode !== 127;
|
|
375
|
+
}
|
|
376
|
+
function isExecutedNonTestGateFailure(finding) {
|
|
377
|
+
return finding.kind !== "test" && finding.severity === "error" && finding.exitCode !== 0;
|
|
378
|
+
}
|
|
379
|
+
function summaryRecordsUnavailable(summary) {
|
|
380
|
+
const status = summary.status;
|
|
381
|
+
if (typeof status === "string") return status.trim().toLowerCase() === "unavailable";
|
|
382
|
+
return summary.passed === false && (summary.findings ?? []).some(isUnavailableVerificationFinding);
|
|
383
|
+
}
|
|
384
|
+
function classifyCompetitionValidationFailure(result) {
|
|
385
|
+
const winner = result.winner;
|
|
386
|
+
const testStatus = winner?.testStatus;
|
|
387
|
+
const verificationSummary = winner?.verification ?? result.verification;
|
|
388
|
+
const statusFromTest = testStatus === "unavailable" || testStatus === "not_run" ? "unavailable" : testStatus === "failed" || testStatus === "error" || testStatus === "timeout" ? "failed" : testStatus === "passed" ? "passed" : void 0;
|
|
389
|
+
const executedGateFailed = verificationSummary?.findings?.some(isExecutedNonTestGateFailure);
|
|
390
|
+
const aggregateUnavailable = !executedGateFailed && !verificationSummary?.findings?.some(isExecutedTestFailure) && verificationSummary && summaryRecordsUnavailable(verificationSummary);
|
|
391
|
+
const gateStatus = statusFromTest === "unavailable" && executedGateFailed ? "failed" : aggregateUnavailable && (statusFromTest === void 0 || statusFromTest === "passed") ? "unavailable" : statusFromTest;
|
|
392
|
+
return classifyStageValidationFailure({
|
|
393
|
+
success: winner?.success === true,
|
|
394
|
+
score: winner?.score ?? result.validationScore ?? 0,
|
|
395
|
+
// Threshold is not persisted on the stage result. A successful winner
|
|
396
|
+
// whose verification does not block and whose validationPassed is false
|
|
397
|
+
// is the below-threshold case; a non-success winner without a
|
|
398
|
+
// verification blocker stays unclassified so the generic fallback remains.
|
|
399
|
+
threshold: winner?.success === true ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY,
|
|
400
|
+
verification: verificationSummary ? {
|
|
401
|
+
passed: verificationSummary.passed && gateStatus !== "failed",
|
|
402
|
+
status: gateStatus === "unavailable" ? "unavailable" : gateStatus === "failed" ? "failed" : verificationSummary.passed ? "passed" : gateStatus ?? "failed"
|
|
403
|
+
} : gateStatus ? { passed: gateStatus === "passed", status: gateStatus } : void 0,
|
|
404
|
+
testStatus,
|
|
405
|
+
rawTestStatus: testStatus,
|
|
406
|
+
exitCode: winner?.testsExecuted?.exitCode
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
function resolveWinnerDeliveryRefusalReason(result) {
|
|
410
|
+
if (result.validationPassed !== true) {
|
|
411
|
+
const predicate = classifyCompetitionValidationFailure(result);
|
|
412
|
+
if (predicate) return formatWinnerDeliveryRefusalReason(predicate);
|
|
413
|
+
return "validation did not pass";
|
|
414
|
+
}
|
|
415
|
+
if (result.winner?.success !== true) return "selected winner is not successful";
|
|
416
|
+
const invariantStatus = result.winner.mergeInvariantValidation?.status;
|
|
417
|
+
if (invariantStatus !== void 0 && invariantStatus !== "passed") {
|
|
418
|
+
return "merge invariant validation did not pass";
|
|
419
|
+
}
|
|
420
|
+
return void 0;
|
|
421
|
+
}
|
|
422
|
+
function normalizeAllowlistEntriesForMatching(allowlist) {
|
|
423
|
+
return allowlist.map((entry) => {
|
|
424
|
+
const hadTrailingSlash = entry.endsWith("/");
|
|
425
|
+
const normalized = normalizePath(entry);
|
|
426
|
+
if (hadTrailingSlash && !normalized.endsWith("/")) {
|
|
427
|
+
return `${normalized}/`;
|
|
428
|
+
}
|
|
429
|
+
return normalized;
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
function isNullDiffEndpoint(filePath) {
|
|
433
|
+
return normalizePath(filePath) === "/dev/null";
|
|
434
|
+
}
|
|
435
|
+
function parseDiffGitHeader(line) {
|
|
436
|
+
const parsed = parseDiffGitHeaderPaths(line);
|
|
437
|
+
if (!parsed) return null;
|
|
438
|
+
return {
|
|
439
|
+
aPath: normalizePath(parsed.aPath),
|
|
440
|
+
bPath: normalizePath(parsed.bPath)
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
function resolveDiffBlockEndpoints(header, blockLines) {
|
|
444
|
+
let aPath = header.aPath;
|
|
445
|
+
let bPath = header.bPath;
|
|
446
|
+
let aIsNull = isNullDiffEndpoint(aPath);
|
|
447
|
+
let bIsNull = isNullDiffEndpoint(bPath);
|
|
448
|
+
let sawRenameOrCopyMarker = false;
|
|
449
|
+
const markerKinds = /* @__PURE__ */ new Set();
|
|
450
|
+
let markerFrom;
|
|
451
|
+
let markerTo;
|
|
452
|
+
const parseMarkerPath = (line, prefix) => {
|
|
453
|
+
const token = line.slice(prefix.length);
|
|
454
|
+
if (!token) return void 0;
|
|
455
|
+
const aToken = token.startsWith('"') ? `"a/${token.slice(1)}` : `a/${token}`;
|
|
456
|
+
const bToken = token.startsWith('"') ? `"b/${token.slice(1)}` : `b/${token}`;
|
|
457
|
+
return parseDiffGitHeaderPaths(`diff --git ${aToken} ${bToken}`)?.aPath;
|
|
458
|
+
};
|
|
459
|
+
for (const line of blockLines) {
|
|
460
|
+
if (line.startsWith("@@")) break;
|
|
461
|
+
if (line.startsWith("rename from ")) {
|
|
462
|
+
sawRenameOrCopyMarker = true;
|
|
463
|
+
markerKinds.add("rename");
|
|
464
|
+
markerFrom = parseMarkerPath(line, "rename from ");
|
|
465
|
+
continue;
|
|
466
|
+
}
|
|
467
|
+
if (line.startsWith("rename to ")) {
|
|
468
|
+
sawRenameOrCopyMarker = true;
|
|
469
|
+
markerKinds.add("rename");
|
|
470
|
+
markerTo = parseMarkerPath(line, "rename to ");
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (line.startsWith("copy from ")) {
|
|
474
|
+
sawRenameOrCopyMarker = true;
|
|
475
|
+
markerKinds.add("copy");
|
|
476
|
+
markerFrom = parseMarkerPath(line, "copy from ");
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
if (line.startsWith("copy to ")) {
|
|
480
|
+
sawRenameOrCopyMarker = true;
|
|
481
|
+
markerKinds.add("copy");
|
|
482
|
+
markerTo = parseMarkerPath(line, "copy to ");
|
|
483
|
+
continue;
|
|
484
|
+
}
|
|
485
|
+
if (line.startsWith("--- ")) {
|
|
486
|
+
const parsed = parseUnifiedDiffPathLine(line);
|
|
487
|
+
if (parsed === "/dev/null") {
|
|
488
|
+
aIsNull = true;
|
|
489
|
+
} else if (parsed) {
|
|
490
|
+
aPath = normalizePath(parsed);
|
|
491
|
+
aIsNull = false;
|
|
492
|
+
}
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
if (line.startsWith("+++ ")) {
|
|
496
|
+
const parsed = parseUnifiedDiffPathLine(line);
|
|
497
|
+
if (parsed === "/dev/null") {
|
|
498
|
+
bIsNull = true;
|
|
499
|
+
} else if (parsed) {
|
|
500
|
+
bPath = normalizePath(parsed);
|
|
501
|
+
bIsNull = false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (aIsNull) aPath = "/dev/null";
|
|
506
|
+
if (bIsNull) bPath = "/dev/null";
|
|
507
|
+
const isRenameOrCopy = sawRenameOrCopyMarker || !aIsNull && !bIsNull && aPath !== bPath;
|
|
508
|
+
const markerPaths = [markerFrom, markerTo].filter((value) => Boolean(value));
|
|
509
|
+
const markersConsistent = !sawRenameOrCopyMarker || markerKinds.size === 1 && markerFrom === aPath && markerTo === bPath;
|
|
510
|
+
return { aPath, bPath, isRenameOrCopy, markersConsistent, markerPaths };
|
|
511
|
+
}
|
|
512
|
+
function isDiffBlockAllowlisted(endpoints, allowlist) {
|
|
513
|
+
if (!endpoints.markersConsistent) return false;
|
|
514
|
+
const aNull = isNullDiffEndpoint(endpoints.aPath);
|
|
515
|
+
const bNull = isNullDiffEndpoint(endpoints.bPath);
|
|
516
|
+
if (aNull && bNull) return false;
|
|
517
|
+
if (aNull) return isPathAllowed(endpoints.bPath, allowlist);
|
|
518
|
+
if (bNull) return isPathAllowed(endpoints.aPath, allowlist);
|
|
519
|
+
if (endpoints.isRenameOrCopy || endpoints.aPath !== endpoints.bPath) {
|
|
520
|
+
return isPathAllowed(endpoints.aPath, allowlist) && isPathAllowed(endpoints.bPath, allowlist);
|
|
521
|
+
}
|
|
522
|
+
return isPathAllowed(endpoints.aPath, allowlist);
|
|
523
|
+
}
|
|
524
|
+
function disallowedEndpointsForBlock(endpoints, allowlist) {
|
|
525
|
+
if (isDiffBlockAllowlisted(endpoints, allowlist)) {
|
|
526
|
+
return [];
|
|
527
|
+
}
|
|
528
|
+
const violations = /* @__PURE__ */ new Set();
|
|
529
|
+
if (!isNullDiffEndpoint(endpoints.aPath) && !isPathAllowed(endpoints.aPath, allowlist)) {
|
|
530
|
+
violations.add(endpoints.aPath);
|
|
531
|
+
}
|
|
532
|
+
if (!isNullDiffEndpoint(endpoints.bPath) && !isPathAllowed(endpoints.bPath, allowlist)) {
|
|
533
|
+
violations.add(endpoints.bPath);
|
|
534
|
+
}
|
|
535
|
+
for (const markerPath of endpoints.markerPaths) {
|
|
536
|
+
if (!isPathAllowed(markerPath, allowlist)) violations.add(markerPath);
|
|
537
|
+
}
|
|
538
|
+
if (violations.size === 0) {
|
|
539
|
+
if (!isNullDiffEndpoint(endpoints.aPath)) violations.add(endpoints.aPath);
|
|
540
|
+
if (!isNullDiffEndpoint(endpoints.bPath)) violations.add(endpoints.bPath);
|
|
541
|
+
}
|
|
542
|
+
return [...violations];
|
|
543
|
+
}
|
|
544
|
+
function iterateDiffFileBlocks(diffText, onBlock) {
|
|
545
|
+
let currentBlock = [];
|
|
546
|
+
let currentHeader = null;
|
|
547
|
+
const flush = () => {
|
|
548
|
+
if (currentBlock.length === 0 || !currentHeader) {
|
|
549
|
+
currentBlock = [];
|
|
550
|
+
currentHeader = null;
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
onBlock(currentHeader, currentBlock);
|
|
554
|
+
currentBlock = [];
|
|
555
|
+
currentHeader = null;
|
|
556
|
+
};
|
|
557
|
+
for (const line of diffText.split("\n")) {
|
|
558
|
+
if (line.startsWith("diff --git ")) {
|
|
559
|
+
flush();
|
|
560
|
+
currentBlock = [line];
|
|
561
|
+
currentHeader = parseDiffGitHeader(line);
|
|
562
|
+
continue;
|
|
563
|
+
}
|
|
564
|
+
if (currentBlock.length === 0) continue;
|
|
565
|
+
currentBlock.push(line);
|
|
566
|
+
}
|
|
567
|
+
flush();
|
|
568
|
+
}
|
|
569
|
+
function findDisallowedDiffEndpoints(diffText, allowlist) {
|
|
570
|
+
const normalizedAllowlist = normalizeAllowlistEntriesForMatching(allowlist);
|
|
571
|
+
const violations = /* @__PURE__ */ new Set();
|
|
572
|
+
iterateDiffFileBlocks(diffText, (header, blockLines) => {
|
|
573
|
+
const endpoints = resolveDiffBlockEndpoints(header, blockLines);
|
|
574
|
+
for (const path2 of disallowedEndpointsForBlock(endpoints, normalizedAllowlist)) {
|
|
575
|
+
violations.add(path2);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
return [...violations];
|
|
579
|
+
}
|
|
580
|
+
function filterUnifiedDiffToAllowlist(diffText, allowlist) {
|
|
581
|
+
const normalizedAllowlist = normalizeAllowlistEntriesForMatching(allowlist);
|
|
582
|
+
const fileBlocks = [];
|
|
583
|
+
iterateDiffFileBlocks(diffText, (header, blockLines) => {
|
|
584
|
+
const endpoints = resolveDiffBlockEndpoints(header, blockLines);
|
|
585
|
+
if (isDiffBlockAllowlisted(endpoints, normalizedAllowlist)) {
|
|
586
|
+
fileBlocks.push(blockLines.join("\n"));
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
const filtered = fileBlocks.join("\n");
|
|
590
|
+
if (filtered.trim().length === 0) return "";
|
|
591
|
+
return filtered.endsWith("\n") ? filtered : `${filtered}
|
|
592
|
+
`;
|
|
593
|
+
}
|
|
594
|
+
function shouldRestrictWinnerPatchToAllowlist(mode, allowlist) {
|
|
595
|
+
if (allowlist.length === 0) return false;
|
|
596
|
+
return mode === "strict" || mode === "filter";
|
|
597
|
+
}
|
|
598
|
+
function applyWinnerAllowlistFilter(patch, allowlist, mode) {
|
|
599
|
+
if (!shouldRestrictWinnerPatchToAllowlist(mode, allowlist)) {
|
|
600
|
+
return patch;
|
|
601
|
+
}
|
|
602
|
+
return filterUnifiedDiffToAllowlist(patch, [...allowlist]);
|
|
603
|
+
}
|
|
604
|
+
function assertWinnerPatchAllowlistEndpoints(patch, allowlist, mode) {
|
|
605
|
+
if (!shouldRestrictWinnerPatchToAllowlist(mode, allowlist)) {
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
if (patch.trim().length === 0) {
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
const violations = findDisallowedDiffEndpoints(patch, [...allowlist]);
|
|
612
|
+
if (violations.length === 0) {
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
throw new Error(
|
|
616
|
+
`Winner patch has non-allowlisted endpoint(s) under ${mode} allowlist mode: ${violations.join(", ")}`
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
async function loadWinnerVariantResult(result, stageId, artifactStore) {
|
|
620
|
+
if (!result.winner?.resultArtifactPath || !artifactStore) {
|
|
621
|
+
return void 0;
|
|
622
|
+
}
|
|
623
|
+
const raw = await artifactStore.load(result.winner.resultArtifactPath, stageId);
|
|
624
|
+
const parsed = JSON.parse(raw);
|
|
625
|
+
const variantResult = parsed && typeof parsed === "object" && "variantResult" in parsed ? parsed.variantResult : void 0;
|
|
626
|
+
return variantResult && typeof variantResult === "object" ? variantResult : void 0;
|
|
627
|
+
}
|
|
628
|
+
function nonEmptyPatch(value) {
|
|
629
|
+
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
630
|
+
}
|
|
631
|
+
async function diffWinnerWorktree(worktreePath, pathspec, baseRef = "HEAD") {
|
|
632
|
+
if (!worktreePath) return void 0;
|
|
633
|
+
const args = ["diff", "--binary", baseRef];
|
|
634
|
+
if (pathspec.length > 0) {
|
|
635
|
+
args.push("--", ...pathspec);
|
|
636
|
+
}
|
|
637
|
+
const intentIndex = await prepareUntrackedIntentIndex({ worktreePath });
|
|
638
|
+
try {
|
|
639
|
+
const diff = await execGit(args, {
|
|
640
|
+
cwd: worktreePath,
|
|
641
|
+
timeoutMs: 0,
|
|
642
|
+
stripFinalNewline: false,
|
|
643
|
+
...intentIndex.indexPath ? { env: { GIT_INDEX_FILE: intentIndex.indexPath } } : {}
|
|
644
|
+
});
|
|
645
|
+
if (diff.exitCode !== 0) throw new Error(diff.stderr);
|
|
646
|
+
return nonEmptyPatch(diff.stdout);
|
|
647
|
+
} finally {
|
|
648
|
+
await intentIndex.dispose();
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
async function resolveWinnerPatch(params) {
|
|
652
|
+
const variantResult = await loadWinnerVariantResult(
|
|
653
|
+
params.result,
|
|
654
|
+
params.stageId,
|
|
655
|
+
params.artifactStore
|
|
656
|
+
);
|
|
657
|
+
const storedPatch = nonEmptyPatch(variantResult?.worktreeDiff);
|
|
658
|
+
const artifactPatchUsable = storedPatch !== void 0 && variantResult?.worktreeDiffReplayable !== false && isRecoverableCompetitionDiff(storedPatch);
|
|
659
|
+
const artifactPatch = artifactPatchUsable ? storedPatch : void 0;
|
|
660
|
+
const artifactRejected = storedPatch !== void 0 && !artifactPatchUsable;
|
|
661
|
+
const truncatedError = () => new Error(
|
|
662
|
+
"Winner diff truncated for context budget and no retained winner worktree is available to recover the full patch"
|
|
663
|
+
);
|
|
664
|
+
const baselineRef = typeof variantResult?.baselineRef === "string" && variantResult.baselineRef.length > 0 ? variantResult.baselineRef : void 0;
|
|
665
|
+
if (artifactRejected && !baselineRef) throw truncatedError();
|
|
666
|
+
const diffBase = artifactRejected ? baselineRef : void 0;
|
|
667
|
+
if (artifactPatch && params.filterTiming === "after-resolution") {
|
|
668
|
+
return {
|
|
669
|
+
patch: applyWinnerAllowlistFilter(artifactPatch, params.allowlist, params.allowlistMode)
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
if (artifactPatch) {
|
|
673
|
+
const filteredPatch = applyWinnerAllowlistFilter(
|
|
674
|
+
artifactPatch,
|
|
675
|
+
params.allowlist,
|
|
676
|
+
params.allowlistMode
|
|
677
|
+
);
|
|
678
|
+
if (filteredPatch.trim().length > 0) {
|
|
679
|
+
return { patch: filteredPatch };
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
const fallbackPathspec = params.filterTiming === "before-fallback" ? params.fallbackDiffPathspec ?? [] : [];
|
|
683
|
+
const retainedPatch = await diffWinnerWorktree(
|
|
684
|
+
typeof variantResult?.retainedWorktreePath === "string" ? variantResult.retainedWorktreePath : void 0,
|
|
685
|
+
fallbackPathspec,
|
|
686
|
+
diffBase
|
|
687
|
+
);
|
|
688
|
+
if (retainedPatch) {
|
|
689
|
+
return {
|
|
690
|
+
patch: params.filterTiming === "after-resolution" ? applyWinnerAllowlistFilter(retainedPatch, params.allowlist, params.allowlistMode) : retainedPatch
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
const winnerPatch = await diffWinnerWorktree(
|
|
694
|
+
params.result.winner?.worktreePath,
|
|
695
|
+
fallbackPathspec,
|
|
696
|
+
diffBase
|
|
697
|
+
);
|
|
698
|
+
if (!winnerPatch) {
|
|
699
|
+
if (artifactRejected) throw truncatedError();
|
|
700
|
+
return void 0;
|
|
701
|
+
}
|
|
702
|
+
return {
|
|
703
|
+
patch: params.filterTiming === "after-resolution" ? applyWinnerAllowlistFilter(winnerPatch, params.allowlist, params.allowlistMode) : winnerPatch
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
async function applyCompetitionWinnerPatch(params) {
|
|
707
|
+
const resolvedPatch = await resolveWinnerPatch(params);
|
|
708
|
+
const patch = resolvedPatch?.patch;
|
|
709
|
+
if (!patch || patch.trim().length === 0) {
|
|
710
|
+
return { applied: false };
|
|
711
|
+
}
|
|
712
|
+
const normalizedPatch = patch.endsWith("\n") ? patch : `${patch}
|
|
713
|
+
`;
|
|
714
|
+
assertWinnerPatchAllowlistEndpoints(normalizedPatch, params.allowlist, params.allowlistMode);
|
|
715
|
+
if (params.artifactStore) {
|
|
716
|
+
try {
|
|
717
|
+
await params.artifactStore.save(params.stageId, "winnerDiff", "diff", normalizedPatch);
|
|
718
|
+
} catch {
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
try {
|
|
722
|
+
const applied = await execGit(["apply", "--3way", "--whitespace=nowarn", "-"], {
|
|
723
|
+
cwd: params.targetWorktreePath,
|
|
724
|
+
timeoutMs: 0,
|
|
725
|
+
input: normalizedPatch
|
|
726
|
+
});
|
|
727
|
+
if (applied.exitCode !== 0) throw new Error(applied.stderr);
|
|
728
|
+
} catch (applyError) {
|
|
729
|
+
try {
|
|
730
|
+
const reversed = await execGit(["apply", "--reverse", "--check", "-"], {
|
|
731
|
+
cwd: params.targetWorktreePath,
|
|
732
|
+
timeoutMs: 0,
|
|
733
|
+
input: normalizedPatch
|
|
734
|
+
});
|
|
735
|
+
if (reversed.exitCode !== 0) throw new Error(reversed.stderr);
|
|
736
|
+
params.onAlreadyApplied?.();
|
|
737
|
+
} catch {
|
|
738
|
+
throw applyError;
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
return { applied: true, normalizedPatch };
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// src/automation/flywheel-competition-identity.ts
|
|
745
|
+
var admissions = /* @__PURE__ */ new WeakMap();
|
|
746
|
+
function admitFlywheelVariantSpecs(specs, configs) {
|
|
747
|
+
return specs.map((spec, index) => {
|
|
748
|
+
const config = configs[index];
|
|
749
|
+
const admitted = { ...spec, model: config.model };
|
|
750
|
+
admissions.set(admitted, { config });
|
|
751
|
+
return admitted;
|
|
752
|
+
});
|
|
753
|
+
}
|
|
754
|
+
function recordedVariant(result) {
|
|
755
|
+
const parsed = parseVariantIdentifier(result.variant ?? "");
|
|
756
|
+
return parsed.provider === result.provider && parsed.executionMode ? parsed : {};
|
|
757
|
+
}
|
|
758
|
+
function recordedEffort(result) {
|
|
759
|
+
return normalizeReasoningEffortInput(
|
|
760
|
+
recordedVariant(result).reasoningEffort ?? result.requestedReasoningEffort
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
function recordedModels(result) {
|
|
764
|
+
const canonical = recordedVariant(result).model;
|
|
765
|
+
return [
|
|
766
|
+
result.model,
|
|
767
|
+
result.resolvedModel,
|
|
768
|
+
canonical === "default" ? void 0 : canonical
|
|
769
|
+
].filter((model) => model !== void 0);
|
|
770
|
+
}
|
|
771
|
+
function configMatchesResult(config, result) {
|
|
772
|
+
if (config.provider !== result.provider) return false;
|
|
773
|
+
if (config.variantInstanceId !== result.variantInstanceId) return false;
|
|
774
|
+
if (result.executionMode && config.executionMode !== result.executionMode) return false;
|
|
775
|
+
const canonical = parseVariantIdentifier(formatVariantIdentifier(config));
|
|
776
|
+
if (config.model) {
|
|
777
|
+
const expectedModels = [config.model, canonical.model, config.fallbackModel];
|
|
778
|
+
const models = result.modelInput ? [result.modelInput] : recordedModels(result);
|
|
779
|
+
if (models.length > 0 && !models.some((model) => expectedModels.includes(model))) return false;
|
|
780
|
+
}
|
|
781
|
+
const effort = normalizeReasoningEffortInput(config.modelReasoningEffort);
|
|
782
|
+
const recorded = recordedEffort(result);
|
|
783
|
+
return recorded === void 0 || effort === recorded;
|
|
784
|
+
}
|
|
785
|
+
function bindFlywheelVariantResults(specs, results) {
|
|
786
|
+
const matches = specs.map((spec) => {
|
|
787
|
+
const config = admissions.get(spec).config;
|
|
788
|
+
const compatible = results.filter((result) => configMatchesResult(config, result));
|
|
789
|
+
const exact = compatible.filter(
|
|
790
|
+
(result) => (result.modelInput ?? result.model ?? recordedVariant(result).model) === config.model && recordedEffort(result) === normalizeReasoningEffortInput(config.modelReasoningEffort)
|
|
791
|
+
);
|
|
792
|
+
return exact.length > 0 ? exact : compatible;
|
|
793
|
+
});
|
|
794
|
+
for (const [index, spec] of specs.entries()) {
|
|
795
|
+
const possible = matches[index];
|
|
796
|
+
const result = possible[0];
|
|
797
|
+
if (possible.length > 1 || result && matches.filter((m) => m.includes(result)).length > 1) {
|
|
798
|
+
throw new Error(`Ambiguous competition execution identity for ${variantIdForSpec(spec)}`);
|
|
799
|
+
}
|
|
800
|
+
admissions.get(spec).result = result;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
function specMatchesResult(spec, result) {
|
|
804
|
+
if (spec.provider !== result.provider) return false;
|
|
805
|
+
const models = result.modelInput ? [result.modelInput] : recordedModels(result);
|
|
806
|
+
if (spec.model !== void 0 && !models.includes(spec.model)) return false;
|
|
807
|
+
if (spec.executionMode && result.executionMode && spec.executionMode !== result.executionMode) {
|
|
808
|
+
return false;
|
|
809
|
+
}
|
|
810
|
+
const effort = normalizeReasoningEffortInput(spec.reasoning);
|
|
811
|
+
const recorded = recordedEffort(result);
|
|
812
|
+
return effort === void 0 || recorded === void 0 || effort === recorded;
|
|
813
|
+
}
|
|
814
|
+
function matchVariantResult(spec, results) {
|
|
815
|
+
const admission = admissions.get(spec);
|
|
816
|
+
if (admission) {
|
|
817
|
+
return admission.result && results.includes(admission.result) ? admission.result : void 0;
|
|
818
|
+
}
|
|
819
|
+
const compatible = results.filter((result) => specMatchesResult(spec, result));
|
|
820
|
+
const effort = normalizeReasoningEffortInput(spec.reasoning);
|
|
821
|
+
const exact = effort === void 0 ? [] : compatible.filter((result) => recordedEffort(result) === effort);
|
|
822
|
+
const matches = exact.length > 0 ? exact : compatible;
|
|
823
|
+
return matches.length === 1 && !matches[0].variantInstanceId ? matches[0] : void 0;
|
|
824
|
+
}
|
|
825
|
+
function candidateMatchesVariantResult(candidate, result) {
|
|
826
|
+
const admission = admissions.get(candidate.variant);
|
|
827
|
+
if (!admission) return !result.variantInstanceId && specMatchesResult(candidate.variant, result);
|
|
828
|
+
const bound = admission.result;
|
|
829
|
+
if (!bound || bound.provider !== result.provider) return false;
|
|
830
|
+
if (bound.variantInstanceId !== result.variantInstanceId) return false;
|
|
831
|
+
if (bound.modelInput && result.modelInput && bound.modelInput !== result.modelInput) return false;
|
|
832
|
+
if (bound.model && result.model && bound.model !== result.model && bound.resolvedModel !== result.model && bound.model !== result.resolvedModel)
|
|
833
|
+
return false;
|
|
834
|
+
if (bound.executionMode && result.executionMode && bound.executionMode !== result.executionMode)
|
|
835
|
+
return false;
|
|
836
|
+
const effort = recordedEffort(bound);
|
|
837
|
+
const recorded = recordedEffort(result);
|
|
838
|
+
return effort === void 0 || recorded === void 0 || effort === recorded;
|
|
839
|
+
}
|
|
840
|
+
function findCandidateForVariantResult(candidates, result) {
|
|
841
|
+
const matches = candidates.filter(
|
|
842
|
+
(candidate) => candidateMatchesVariantResult(candidate, result)
|
|
843
|
+
);
|
|
844
|
+
return matches.length === 1 ? matches[0] : void 0;
|
|
845
|
+
}
|
|
846
|
+
function variantIdForSpec(spec) {
|
|
847
|
+
const admission = admissions.get(spec);
|
|
848
|
+
return admission ? `${formatVariantIdentifier(admission.config)}#${admission.config.variantInstanceId ?? ""}` : `${spec.provider ?? ""}:${spec.model ?? ""}`;
|
|
849
|
+
}
|
|
850
|
+
function candidateScoreId(candidate) {
|
|
851
|
+
return admissions.has(candidate.variant) ? variantIdForSpec(candidate.variant) : [candidate.variant.provider, candidate.variant.model].filter(Boolean).join(":");
|
|
852
|
+
}
|
|
853
|
+
function reconcileJudgeRationaleWinner(packet, judgedCandidate, finalCandidate, hasJudgeVerdict) {
|
|
854
|
+
const finalWinnerId = finalCandidate ? candidateScoreId(finalCandidate) : "none";
|
|
855
|
+
if ((packet.winnerId ?? "none") === finalWinnerId) return packet;
|
|
856
|
+
const selectionChanged = hasJudgeVerdict && judgedCandidate !== finalCandidate;
|
|
857
|
+
return {
|
|
858
|
+
...packet,
|
|
859
|
+
rawWinnerId: packet.rawWinnerId ?? packet.winnerId ?? void 0,
|
|
860
|
+
winnerId: finalWinnerId,
|
|
861
|
+
...selectionChanged ? { winnerOverridden: true } : {}
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function observedVariantSpec(result) {
|
|
865
|
+
return { provider: result.provider, model: result.model, reasoning: recordedEffort(result) };
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
// src/automation/flywheel-judge-preflight.ts
|
|
869
|
+
function validateWorkflowJudgeModels(config, workerCwd) {
|
|
870
|
+
const mainJudge = config.judgeConfig?.disableAiJudge !== true;
|
|
871
|
+
const coverageTieJudge = Boolean(
|
|
872
|
+
workerCwd && config.judgeConfig && config.candidateGate?.coverage?.targets?.length
|
|
873
|
+
);
|
|
874
|
+
if (config.variants.length < 2 || !mainJudge && !coverageTieJudge) return;
|
|
875
|
+
try {
|
|
876
|
+
resolveJudgeModelRoles({
|
|
877
|
+
allowUnsafeModel: config.judgeConfig?.allowUnsafeModel === true,
|
|
878
|
+
activeJudgeTypes: resolveJudgeRunSet(
|
|
879
|
+
deriveAiJudgeInvocationArgs(config.judgeConfig),
|
|
880
|
+
JUDGE_FEATURE_FLAGS.JUDGE_PROFILE === "shadow"
|
|
881
|
+
)
|
|
882
|
+
});
|
|
883
|
+
} catch (error) {
|
|
884
|
+
if (isInvalidExplicitConfigError(error)) throw error;
|
|
885
|
+
throw new Error(
|
|
886
|
+
"Workflow judges require configured quality models. Correct the judge configuration, or use build run competition with --judge-force-model for deliberate fast-model judging.\n" + (error instanceof Error ? error.message : String(error)),
|
|
887
|
+
{ cause: error }
|
|
888
|
+
);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
// src/automation/flywheel-to-core-verification-finding.ts
|
|
893
|
+
function toCoreVerificationFinding(finding) {
|
|
894
|
+
const kind = finding.kind === "coverage" ? "other" : finding.kind;
|
|
895
|
+
const outputParts = [];
|
|
896
|
+
if (finding.kind === "coverage") {
|
|
897
|
+
const coverageDetail = finding.outputTail?.trim() ? finding.outputTail.trim() : finding.command ? `Coverage observation for ${finding.command}` : "Coverage observation";
|
|
898
|
+
outputParts.push(
|
|
899
|
+
coverageDetail.toLowerCase().includes("coverage") ? coverageDetail : `Coverage: ${coverageDetail}`
|
|
900
|
+
);
|
|
901
|
+
} else if (finding.outputTail) {
|
|
902
|
+
outputParts.push(finding.outputTail);
|
|
903
|
+
}
|
|
904
|
+
if (finding.recommendedNextStep) {
|
|
905
|
+
const next = finding.recommendedNextStep.trim();
|
|
906
|
+
if (next && !outputParts.some((part) => part.includes(next))) {
|
|
907
|
+
outputParts.push(next);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
const mapped = {
|
|
911
|
+
kind,
|
|
912
|
+
command: finding.command,
|
|
913
|
+
exitCode: finding.exitCode,
|
|
914
|
+
severity: finding.severity
|
|
915
|
+
};
|
|
916
|
+
if (outputParts.length > 0) {
|
|
917
|
+
mapped.outputTail = outputParts.join("\n");
|
|
918
|
+
}
|
|
919
|
+
return mapped;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
// src/automation/flywheel-competition-executor.ts
|
|
923
|
+
var CANDIDATE_GATE_COMMAND_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
924
|
+
function isNoChangesDetectedError(errorMessage) {
|
|
925
|
+
return Boolean(errorMessage?.includes("No changes detected (git status clean)"));
|
|
926
|
+
}
|
|
927
|
+
function generateCandidateFeedback(variantEvaluation, result) {
|
|
928
|
+
if (!variantEvaluation && !result) return void 0;
|
|
929
|
+
const parts = [];
|
|
930
|
+
const score = variantEvaluation?.qualityScore ?? result.judgeScore ?? result.completeness;
|
|
931
|
+
if (score !== void 0) {
|
|
932
|
+
parts.push(`Score: ${Number(score).toFixed(1)}/100`);
|
|
933
|
+
}
|
|
934
|
+
const changedFiles = extractChangedFilesFromResult(result);
|
|
935
|
+
const fileCount = changedFiles.length;
|
|
936
|
+
const linesAdded = result.gitMetrics?.linesAdded ?? result.linesAdded ?? 0;
|
|
937
|
+
const linesDeleted = result.gitMetrics?.linesDeleted ?? result.linesRemoved ?? 0;
|
|
938
|
+
if (fileCount > 0 || linesAdded > 0 || linesDeleted > 0) {
|
|
939
|
+
parts.push(`Changes: ${fileCount} file(s), +${linesAdded}/-${linesDeleted} lines`);
|
|
940
|
+
}
|
|
941
|
+
const testStatus = result.testStatus;
|
|
942
|
+
if (testStatus) {
|
|
943
|
+
parts.push(`Tests: ${testStatus}`);
|
|
944
|
+
}
|
|
945
|
+
const strengths = variantEvaluation?.strengths;
|
|
946
|
+
if (strengths && strengths.length > 0) {
|
|
947
|
+
const topStrengths = strengths.slice(0, 2).join("; ");
|
|
948
|
+
parts.push(`Strengths: ${topStrengths}`);
|
|
949
|
+
}
|
|
950
|
+
const weaknesses = variantEvaluation?.weaknesses;
|
|
951
|
+
if (weaknesses && weaknesses.length > 0) {
|
|
952
|
+
const topWeaknesses = weaknesses.slice(0, 2).join("; ");
|
|
953
|
+
parts.push(`Areas for improvement: ${topWeaknesses}`);
|
|
954
|
+
}
|
|
955
|
+
const cq = variantEvaluation?.codeQuality;
|
|
956
|
+
if (cq) {
|
|
957
|
+
const metrics = [];
|
|
958
|
+
if (cq.correctness !== void 0) metrics.push(`correctness=${cq.correctness}`);
|
|
959
|
+
if (cq.completeness !== void 0) metrics.push(`completeness=${cq.completeness}`);
|
|
960
|
+
if (metrics.length > 0) {
|
|
961
|
+
parts.push(`Quality: ${metrics.join(", ")}`);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return parts.length > 0 ? `${parts.join(". ")}.` : void 0;
|
|
965
|
+
}
|
|
966
|
+
function extractChangedFilesFromResult(result) {
|
|
967
|
+
if ("changedFiles" in result && Array.isArray(result.changedFiles)) {
|
|
968
|
+
return result.changedFiles;
|
|
969
|
+
}
|
|
970
|
+
const statusLines = result.worktreeSummary?.statusLines;
|
|
971
|
+
if (statusLines && Array.isArray(statusLines) && statusLines.length > 0) {
|
|
972
|
+
return statusLines.map((line) => extractChangedFileFromStatusLine(String(line))).filter((line) => Boolean(line));
|
|
973
|
+
}
|
|
974
|
+
const filesChanged = result.gitMetrics?.filesChanged;
|
|
975
|
+
if (typeof filesChanged === "number" && filesChanged > 0) {
|
|
976
|
+
return Array.from({ length: filesChanged }, (_, i) => `file${i + 1}`);
|
|
977
|
+
}
|
|
978
|
+
return [];
|
|
979
|
+
}
|
|
980
|
+
function resultHasChangeTracking(result) {
|
|
981
|
+
if ("changedFiles" in result && Array.isArray(result.changedFiles)) {
|
|
982
|
+
return true;
|
|
983
|
+
}
|
|
984
|
+
const statusLines = result.worktreeSummary?.statusLines;
|
|
985
|
+
if (Array.isArray(statusLines)) {
|
|
986
|
+
return true;
|
|
987
|
+
}
|
|
988
|
+
return typeof result.gitMetrics?.filesChanged === "number";
|
|
989
|
+
}
|
|
990
|
+
function extractChangedFileFromStatusLine(line) {
|
|
991
|
+
const trimmed = line.trim();
|
|
992
|
+
if (!trimmed) return null;
|
|
993
|
+
const arrowIdx = trimmed.lastIndexOf("->");
|
|
994
|
+
if (arrowIdx !== -1) {
|
|
995
|
+
const candidate = trimmed.slice(arrowIdx + 2).trim();
|
|
996
|
+
return candidate || null;
|
|
997
|
+
}
|
|
998
|
+
const match = trimmed.match(/^[MADRCU?! ]{1,2}\s*(.+)$/);
|
|
999
|
+
return match?.[1]?.trim() ?? null;
|
|
1000
|
+
}
|
|
1001
|
+
function candidateHasChangedFiles(candidate) {
|
|
1002
|
+
return Array.isArray(candidate.changedFiles) && candidate.changedFiles.length > 0;
|
|
1003
|
+
}
|
|
1004
|
+
function candidateHasConcreteZeroChanges(candidate) {
|
|
1005
|
+
return candidate.changeTrackingAvailable === true && !candidateHasChangedFiles(candidate);
|
|
1006
|
+
}
|
|
1007
|
+
function stageHasDeterministicChecks(stage) {
|
|
1008
|
+
return stageHasHostDeterministicCompletionSurface(stage);
|
|
1009
|
+
}
|
|
1010
|
+
function renderCandidateGateCommand(command, candidate) {
|
|
1011
|
+
return command.replaceAll("{{provider}}", shellArg(candidate.variant.provider)).replaceAll("{{model}}", shellArg(candidate.variant.model ?? "")).replaceAll("{{variant}}", shellArg(formatCompetitionCandidateVariantDisplay(candidate)));
|
|
1012
|
+
}
|
|
1013
|
+
function formatCompetitionCandidateVariantDisplay(candidate) {
|
|
1014
|
+
return formatCompetitionVariantDisplay(candidate.variant);
|
|
1015
|
+
}
|
|
1016
|
+
function formatCompetitionVariantDisplay(variant) {
|
|
1017
|
+
const effort = normalizeReasoningEffortInput(variant.reasoning);
|
|
1018
|
+
const parts = [variant.provider, variant.model, effort ? `effort-${effort}` : void 0].filter(
|
|
1019
|
+
(part) => typeof part === "string" && part.length > 0
|
|
1020
|
+
);
|
|
1021
|
+
return parts.join(":");
|
|
1022
|
+
}
|
|
1023
|
+
function shellArg(value) {
|
|
1024
|
+
if (/^[A-Za-z0-9._:@/+,-]+$/.test(value)) {
|
|
1025
|
+
return value;
|
|
1026
|
+
}
|
|
1027
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1028
|
+
}
|
|
1029
|
+
async function validateWorktreeAgainstStageChecks(stage, worktreePath, stdout, options) {
|
|
1030
|
+
if (!stageHasHostDeterministicCompletionSurface(stage)) return false;
|
|
1031
|
+
const proof = await evaluateStageCompletionProof({
|
|
1032
|
+
stage,
|
|
1033
|
+
worktreePath,
|
|
1034
|
+
stdout,
|
|
1035
|
+
attemptChangedFiles: options?.attemptChangedFiles ?? [],
|
|
1036
|
+
allowlist: options?.allowlist ?? computeExpectedAllowlist(stage),
|
|
1037
|
+
stageId: stage.id
|
|
1038
|
+
});
|
|
1039
|
+
return proofEstablishesSatisfaction(proof);
|
|
1040
|
+
}
|
|
1041
|
+
function getVariantTelemetryStatePath(taskId, provider, model, mode, reasoningEffort) {
|
|
1042
|
+
const variantKey = taskVariantId(
|
|
1043
|
+
taskId,
|
|
1044
|
+
provider,
|
|
1045
|
+
model,
|
|
1046
|
+
mode,
|
|
1047
|
+
void 0,
|
|
1048
|
+
void 0,
|
|
1049
|
+
reasoningEffort
|
|
1050
|
+
);
|
|
1051
|
+
return path.join(
|
|
1052
|
+
LabPathResolver.getInstance().getVariantStateDir(path.join(getProjectRoot(), ".build-project")),
|
|
1053
|
+
`${variantKey}.json`
|
|
1054
|
+
);
|
|
1055
|
+
}
|
|
1056
|
+
function getValueAtPath(root, keys) {
|
|
1057
|
+
let current = root;
|
|
1058
|
+
for (const key of keys) {
|
|
1059
|
+
if (!current || typeof current !== "object") return void 0;
|
|
1060
|
+
current = current[key];
|
|
1061
|
+
}
|
|
1062
|
+
return current;
|
|
1063
|
+
}
|
|
1064
|
+
function getFiniteNumberAtPath(root, keys) {
|
|
1065
|
+
const value = getValueAtPath(root, keys);
|
|
1066
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1067
|
+
}
|
|
1068
|
+
function getStringAtPath(root, keys) {
|
|
1069
|
+
const value = getValueAtPath(root, keys);
|
|
1070
|
+
return typeof value === "string" ? value : void 0;
|
|
1071
|
+
}
|
|
1072
|
+
function extractNativeObservedCostFromVariantState(state) {
|
|
1073
|
+
return getFiniteNumberAtPath(state, ["result", "totalCostUsd"]) ?? getFiniteNumberAtPath(state, ["result", "total_cost_usd"]) ?? getFiniteNumberAtPath(state, ["result", "totalCost"]) ?? getFiniteNumberAtPath(state, ["result", "total_cost"]) ?? null;
|
|
1074
|
+
}
|
|
1075
|
+
function extractCostMetricsAmountFromVariantState(state) {
|
|
1076
|
+
return getFiniteNumberAtPath(state, ["costMetrics", "totalCostUsd"]) ?? getFiniteNumberAtPath(state, ["costMetrics", "totalCost"]) ?? null;
|
|
1077
|
+
}
|
|
1078
|
+
function resolveCostMetricsUnavailableReason(costMetrics) {
|
|
1079
|
+
if (!costMetrics || typeof costMetrics !== "object") {
|
|
1080
|
+
return void 0;
|
|
1081
|
+
}
|
|
1082
|
+
const usageReason = readUsageUnavailableReason({ costMetrics });
|
|
1083
|
+
if (usageReason !== void 0) {
|
|
1084
|
+
return usageReason;
|
|
1085
|
+
}
|
|
1086
|
+
const reason = readCostMetricsUnavailableReason(
|
|
1087
|
+
costMetrics
|
|
1088
|
+
);
|
|
1089
|
+
if (reason !== void 0) {
|
|
1090
|
+
return reason;
|
|
1091
|
+
}
|
|
1092
|
+
if (costMetrics.pricingStatus === "error") {
|
|
1093
|
+
return "pricing_unavailable";
|
|
1094
|
+
}
|
|
1095
|
+
return void 0;
|
|
1096
|
+
}
|
|
1097
|
+
function markCandidateCostUnavailable(candidate, reason) {
|
|
1098
|
+
candidate.cost = 0;
|
|
1099
|
+
candidate.costUnavailableReason = reason;
|
|
1100
|
+
}
|
|
1101
|
+
async function hydrateCandidateCostsFromTelemetry(taskId, candidates, stageIdForLogs, verbose) {
|
|
1102
|
+
await Promise.all(
|
|
1103
|
+
candidates.map(async (candidate) => {
|
|
1104
|
+
if (!candidate.success) return;
|
|
1105
|
+
if (typeof candidate.cost === "number" && Number.isFinite(candidate.cost) && candidate.costUnavailableReason === void 0) {
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (typeof candidate.cost === "number" && Number.isFinite(candidate.cost) && candidate.cost > 0) {
|
|
1109
|
+
candidate.costUnavailableReason = void 0;
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const provider = candidate.variant.provider;
|
|
1113
|
+
if (!provider) return;
|
|
1114
|
+
if (!providerReportsUsage(provider)) {
|
|
1115
|
+
candidate.costUnavailableReason = candidate.costUnavailableReason ?? "provider_not_reported";
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
const variantStatePath = getVariantTelemetryStatePath(
|
|
1119
|
+
taskId,
|
|
1120
|
+
provider,
|
|
1121
|
+
candidate.variant.model,
|
|
1122
|
+
candidate.variant.executionMode,
|
|
1123
|
+
normalizeReasoningEffortInput(candidate.variant.reasoning)
|
|
1124
|
+
);
|
|
1125
|
+
try {
|
|
1126
|
+
const raw = await fs.readFile(variantStatePath, "utf8");
|
|
1127
|
+
const state = JSON.parse(raw);
|
|
1128
|
+
const nativeObservedCost = extractNativeObservedCostFromVariantState(state);
|
|
1129
|
+
const metricsCost = extractCostMetricsAmountFromVariantState(state);
|
|
1130
|
+
const costMetrics = getValueAtPath(state, ["costMetrics"]);
|
|
1131
|
+
const unavailableReason = resolveCostMetricsUnavailableReason(costMetrics);
|
|
1132
|
+
if (typeof nativeObservedCost === "number") {
|
|
1133
|
+
candidate.cost = nativeObservedCost;
|
|
1134
|
+
candidate.costUnavailableReason = void 0;
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (typeof metricsCost === "number" && metricsCost > 0) {
|
|
1138
|
+
candidate.cost = metricsCost;
|
|
1139
|
+
candidate.costUnavailableReason = void 0;
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (unavailableReason !== void 0) {
|
|
1143
|
+
markCandidateCostUnavailable(candidate, unavailableReason);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (typeof metricsCost === "number") {
|
|
1147
|
+
candidate.cost = metricsCost;
|
|
1148
|
+
candidate.costUnavailableReason = void 0;
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
markCandidateCostUnavailable(
|
|
1152
|
+
candidate,
|
|
1153
|
+
candidate.costUnavailableReason ?? "provider_not_reported"
|
|
1154
|
+
);
|
|
1155
|
+
} catch {
|
|
1156
|
+
markCandidateCostUnavailable(
|
|
1157
|
+
candidate,
|
|
1158
|
+
candidate.costUnavailableReason ?? "provider_not_reported"
|
|
1159
|
+
);
|
|
1160
|
+
}
|
|
1161
|
+
if (verbose) {
|
|
1162
|
+
console.warn(
|
|
1163
|
+
chalk.yellow(
|
|
1164
|
+
` [${stageIdForLogs}] Unable to hydrate cost from telemetry for ${formatCompetitionCandidateVariantDisplay(candidate)}`
|
|
1165
|
+
)
|
|
1166
|
+
);
|
|
1167
|
+
}
|
|
1168
|
+
})
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
function variantSpecToConfig(spec, executionIntent, stage, defaultProviderExecutionTransport, executionContract) {
|
|
1172
|
+
const model = spec.model ?? applyFlywheelModelDefaults(stage, spec.provider);
|
|
1173
|
+
const modelReasoningEffort = normalizeReasoningEffortInput(spec.reasoning);
|
|
1174
|
+
return {
|
|
1175
|
+
provider: spec.provider,
|
|
1176
|
+
model,
|
|
1177
|
+
...modelReasoningEffort ? { modelReasoningEffort } : {},
|
|
1178
|
+
// Inert slot: variant strategies were removed as a behavioral axis (ADR-025).
|
|
1179
|
+
// A `strategy` in workflow YAML is accepted and ignored.
|
|
1180
|
+
...executionContract ? { promptContractOverride: executionContract } : {},
|
|
1181
|
+
executionMode: "local",
|
|
1182
|
+
// Competition stages always run locally
|
|
1183
|
+
executionIntent,
|
|
1184
|
+
...spec.providerExecutionTransport ?? stage.providerExecutionTransport ?? defaultProviderExecutionTransport ? {
|
|
1185
|
+
executionTransport: spec.providerExecutionTransport ?? stage.providerExecutionTransport ?? defaultProviderExecutionTransport
|
|
1186
|
+
} : {},
|
|
1187
|
+
allowEmptyResults: stage.allowEmptyResults,
|
|
1188
|
+
isBasicCompetition: true,
|
|
1189
|
+
// Don't auto-commit - flywheel manages commits
|
|
1190
|
+
disableAutoCommit: true,
|
|
1191
|
+
structuredFileTargets: computeExpectedAllowlist(stage)
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
function assertNoDuplicateVariantSpecs(variants, options) {
|
|
1195
|
+
if (options.allowDuplicates) return;
|
|
1196
|
+
const keyToVariants = /* @__PURE__ */ new Map();
|
|
1197
|
+
for (const variant of variants) {
|
|
1198
|
+
const modelKey = variant.model ?? "default";
|
|
1199
|
+
const effortKey = variant.modelReasoningEffort ?? "default";
|
|
1200
|
+
const key = `${variant.provider}:${modelKey}:${effortKey}`;
|
|
1201
|
+
const existing = keyToVariants.get(key);
|
|
1202
|
+
if (existing) {
|
|
1203
|
+
existing.push(variant);
|
|
1204
|
+
} else {
|
|
1205
|
+
keyToVariants.set(key, [variant]);
|
|
1206
|
+
}
|
|
1207
|
+
}
|
|
1208
|
+
const duplicates = [...keyToVariants.entries()].filter(([, group]) => group.length > 1);
|
|
1209
|
+
if (duplicates.length === 0) return;
|
|
1210
|
+
const details = duplicates.map(([, group]) => {
|
|
1211
|
+
const variant = group[0];
|
|
1212
|
+
const label = [
|
|
1213
|
+
variant?.provider ?? "unknown",
|
|
1214
|
+
variant?.model ?? "(default)",
|
|
1215
|
+
variant?.modelReasoningEffort ? `effort-${variant.modelReasoningEffort}` : void 0
|
|
1216
|
+
].filter((part) => typeof part === "string" && part.length > 0).join(":");
|
|
1217
|
+
return ` \u2022 ${label} \u2014 appears ${group.length} times`;
|
|
1218
|
+
}).join("\n");
|
|
1219
|
+
throw new Error(
|
|
1220
|
+
`Duplicate variant specs detected in competition stage
|
|
1221
|
+
|
|
1222
|
+
What this means:
|
|
1223
|
+
Competition stages should compare different variants, not the same variant
|
|
1224
|
+
to itself. Duplicates are identified by provider+model+reasoningEffort identity.
|
|
1225
|
+
|
|
1226
|
+
Duplicate variants found:
|
|
1227
|
+
${details}
|
|
1228
|
+
|
|
1229
|
+
How to fix:
|
|
1230
|
+
1. Remove duplicate entries from your competition.variants configuration
|
|
1231
|
+
2. Or change the model or reasoning effort to make variants distinct
|
|
1232
|
+
3. Or use different providers (e.g., claude + codex)
|
|
1233
|
+
|
|
1234
|
+
Escape hatch (not recommended):
|
|
1235
|
+
Set competition.allowDuplicateVariantSpecs: true in your stage config
|
|
1236
|
+
|
|
1237
|
+
Tip: This guard prevents wasted compute on redundant comparisons.`
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
function resolveCompetitionExecutionIntent(stage) {
|
|
1241
|
+
return resolveProviderExecutionIntentFromStage(stage);
|
|
1242
|
+
}
|
|
1243
|
+
function getVariantDurationMs(result) {
|
|
1244
|
+
if (typeof result.executionDurationMs === "number" && Number.isFinite(result.executionDurationMs)) {
|
|
1245
|
+
return result.executionDurationMs;
|
|
1246
|
+
}
|
|
1247
|
+
if (typeof result.totalDurationSec === "number" && Number.isFinite(result.totalDurationSec)) {
|
|
1248
|
+
return result.totalDurationSec * 1e3;
|
|
1249
|
+
}
|
|
1250
|
+
return Math.max(0, Math.round((result.duration ?? 0) * 1e3));
|
|
1251
|
+
}
|
|
1252
|
+
function extractChangedFiles(result) {
|
|
1253
|
+
const statusLines = result.worktreeSummary?.statusLines;
|
|
1254
|
+
if (!Array.isArray(statusLines) || statusLines.length === 0) return void 0;
|
|
1255
|
+
const candidates = statusLines.map((line) => extractChangedFileFromStatusLine(String(line))).filter((v) => Boolean(v));
|
|
1256
|
+
const trackedRoots = result.retainedWorktreePath && candidates.some(isUnderRootToolOutputDir) ? readTrackedToolOutputRootsSync(result.retainedWorktreePath, result.baselineRef || "HEAD") : void 0;
|
|
1257
|
+
const files = candidates.filter((f) => !shouldExcludeFile(f, trackedRoots));
|
|
1258
|
+
return files.length > 0 ? files : void 0;
|
|
1259
|
+
}
|
|
1260
|
+
function normalizeCoveragePath(value) {
|
|
1261
|
+
return value.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+/g, "/");
|
|
1262
|
+
}
|
|
1263
|
+
function tryGetPct(metric) {
|
|
1264
|
+
if (!metric || typeof metric !== "object") return null;
|
|
1265
|
+
const pct = metric.pct;
|
|
1266
|
+
return typeof pct === "number" && Number.isFinite(pct) ? pct : null;
|
|
1267
|
+
}
|
|
1268
|
+
function getCoveragePctForEntry(entry) {
|
|
1269
|
+
if (!entry || typeof entry !== "object") return null;
|
|
1270
|
+
const record = entry;
|
|
1271
|
+
return tryGetPct(record.lines) ?? tryGetPct(record.statements) ?? tryGetPct(record.functions) ?? tryGetPct(record.branches) ?? null;
|
|
1272
|
+
}
|
|
1273
|
+
function getTotalLinesForEntry(entry) {
|
|
1274
|
+
if (!entry || typeof entry !== "object") return null;
|
|
1275
|
+
const record = entry;
|
|
1276
|
+
const lines = record.lines;
|
|
1277
|
+
if (!lines || typeof lines !== "object") return null;
|
|
1278
|
+
const total = lines.total;
|
|
1279
|
+
return typeof total === "number" && Number.isFinite(total) ? total : null;
|
|
1280
|
+
}
|
|
1281
|
+
async function loadCoverageSummaryJson(worktreePath, summaryPath, options = {}) {
|
|
1282
|
+
try {
|
|
1283
|
+
const resolved = path.join(worktreePath, summaryPath);
|
|
1284
|
+
if (typeof options.minMtimeMs === "number") {
|
|
1285
|
+
const stats = await fs.stat(resolved);
|
|
1286
|
+
if (stats.mtimeMs < options.minMtimeMs) return null;
|
|
1287
|
+
}
|
|
1288
|
+
const raw = await fs.readFile(resolved, "utf8");
|
|
1289
|
+
const parsed = JSON.parse(raw);
|
|
1290
|
+
if (!parsed || typeof parsed !== "object") return null;
|
|
1291
|
+
return parsed;
|
|
1292
|
+
} catch {
|
|
1293
|
+
return null;
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
function findCoverageEntry(summary, target) {
|
|
1297
|
+
const normalizedTarget = normalizeCoveragePath(target);
|
|
1298
|
+
const direct = summary[normalizedTarget];
|
|
1299
|
+
if (direct && typeof direct === "object") {
|
|
1300
|
+
return direct;
|
|
1301
|
+
}
|
|
1302
|
+
const candidates = [];
|
|
1303
|
+
for (const [key, value] of Object.entries(summary)) {
|
|
1304
|
+
if (key === "total") continue;
|
|
1305
|
+
if (!value || typeof value !== "object") continue;
|
|
1306
|
+
const normalizedKey = normalizeCoveragePath(key);
|
|
1307
|
+
if (normalizedKey === normalizedTarget || normalizedKey.endsWith(`/${normalizedTarget}`)) {
|
|
1308
|
+
candidates.push({ key, entry: value });
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
if (candidates.length === 0) return null;
|
|
1312
|
+
candidates.sort((a, b) => a.key.length - b.key.length);
|
|
1313
|
+
return candidates[0]?.entry ?? null;
|
|
1314
|
+
}
|
|
1315
|
+
function computeCoverageStatsForTargets(summary, targets) {
|
|
1316
|
+
const byTarget = {};
|
|
1317
|
+
const linesByTarget = {};
|
|
1318
|
+
const missingTargets = [];
|
|
1319
|
+
const pcts = [];
|
|
1320
|
+
const lineCounts = [];
|
|
1321
|
+
for (const target of targets) {
|
|
1322
|
+
if (!summary) {
|
|
1323
|
+
byTarget[target] = null;
|
|
1324
|
+
linesByTarget[target] = null;
|
|
1325
|
+
missingTargets.push(target);
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
const entry = findCoverageEntry(summary, target);
|
|
1329
|
+
const pct = entry ? getCoveragePctForEntry(entry) : null;
|
|
1330
|
+
const lines = entry ? getTotalLinesForEntry(entry) : null;
|
|
1331
|
+
byTarget[target] = pct;
|
|
1332
|
+
linesByTarget[target] = lines;
|
|
1333
|
+
if (pct === null) {
|
|
1334
|
+
missingTargets.push(target);
|
|
1335
|
+
} else {
|
|
1336
|
+
pcts.push(pct);
|
|
1337
|
+
}
|
|
1338
|
+
if (typeof lines === "number") {
|
|
1339
|
+
lineCounts.push(lines);
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1342
|
+
if (pcts.length === 0) {
|
|
1343
|
+
return {
|
|
1344
|
+
byTarget,
|
|
1345
|
+
linesByTarget,
|
|
1346
|
+
avgPct: null,
|
|
1347
|
+
minPct: null,
|
|
1348
|
+
totalLines: null,
|
|
1349
|
+
maxTargetLines: null,
|
|
1350
|
+
missingTargets
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
const avgRaw = pcts.reduce((sum, v) => sum + v, 0) / pcts.length;
|
|
1354
|
+
const minRaw = Math.min(...pcts);
|
|
1355
|
+
const round2 = (v) => Math.round(v * 100) / 100;
|
|
1356
|
+
const totalLines = lineCounts.length > 0 ? lineCounts.reduce((sum, v) => sum + v, 0) : null;
|
|
1357
|
+
const maxTargetLines = lineCounts.length > 0 ? Math.max(...lineCounts) : null;
|
|
1358
|
+
return {
|
|
1359
|
+
byTarget,
|
|
1360
|
+
linesByTarget,
|
|
1361
|
+
avgPct: round2(avgRaw),
|
|
1362
|
+
minPct: round2(minRaw),
|
|
1363
|
+
totalLines,
|
|
1364
|
+
maxTargetLines,
|
|
1365
|
+
missingTargets
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
function parseBaselineLinesPctFromPrompt(prompt) {
|
|
1369
|
+
const match = prompt.match(/Baseline:\s*lines\s+(\d+(?:\.\d+)?)%/i);
|
|
1370
|
+
if (!match) return null;
|
|
1371
|
+
const value = Number.parseFloat(match[1] ?? "");
|
|
1372
|
+
return Number.isFinite(value) ? value : null;
|
|
1373
|
+
}
|
|
1374
|
+
function resolveAutoCoverageMode(params) {
|
|
1375
|
+
const { maxTargetLines, largeFileLineThreshold, baselineAvgPct, threshold, minDeltaPct } = params;
|
|
1376
|
+
if (typeof maxTargetLines === "number" && maxTargetLines > largeFileLineThreshold) {
|
|
1377
|
+
return "delta";
|
|
1378
|
+
}
|
|
1379
|
+
if (typeof threshold === "number" && typeof baselineAvgPct === "number" && threshold - baselineAvgPct > minDeltaPct) {
|
|
1380
|
+
return "delta";
|
|
1381
|
+
}
|
|
1382
|
+
return "absolute";
|
|
1383
|
+
}
|
|
1384
|
+
function computeExpectedAllowlist(stage) {
|
|
1385
|
+
return collectDeclaredStageTargets(stage);
|
|
1386
|
+
}
|
|
1387
|
+
function computeCompetitionDrift(candidates, expectedAllowlist, mode) {
|
|
1388
|
+
const allDeliveredFiles = /* @__PURE__ */ new Set();
|
|
1389
|
+
const allViolations = /* @__PURE__ */ new Set();
|
|
1390
|
+
for (const candidate of candidates) {
|
|
1391
|
+
if (!candidate.success || !candidate.changedFiles) continue;
|
|
1392
|
+
const changedFiles = filterWorktreeRunSentinelPaths(candidate.changedFiles);
|
|
1393
|
+
for (const file of changedFiles) {
|
|
1394
|
+
allDeliveredFiles.add(file);
|
|
1395
|
+
}
|
|
1396
|
+
const decision = decideStageChangeScope({
|
|
1397
|
+
changedPaths: changedFiles,
|
|
1398
|
+
explicitScope: expectedAllowlist,
|
|
1399
|
+
allowlistMode: mode,
|
|
1400
|
+
mutationOrigin: "provider"
|
|
1401
|
+
});
|
|
1402
|
+
for (const violation of decision.allowlistViolations) {
|
|
1403
|
+
allViolations.add(violation);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
const deliveredFiles = [...allDeliveredFiles];
|
|
1407
|
+
const violations = [...allViolations];
|
|
1408
|
+
const hasDrift = violations.length > 0;
|
|
1409
|
+
const lines = [];
|
|
1410
|
+
lines.push(`Competition stage (${candidates.length} candidates)`);
|
|
1411
|
+
lines.push(`Mode: ${mode}`);
|
|
1412
|
+
lines.push(`Total delivered files: ${deliveredFiles.length}`);
|
|
1413
|
+
lines.push(`Expected allowlist entries: ${expectedAllowlist.length}`);
|
|
1414
|
+
if (hasDrift) {
|
|
1415
|
+
lines.push(`Drift: ${violations.length} file(s) outside allowlist`);
|
|
1416
|
+
for (const v of violations.slice(0, 3)) {
|
|
1417
|
+
lines.push(` - ${v}`);
|
|
1418
|
+
}
|
|
1419
|
+
if (violations.length > 3) {
|
|
1420
|
+
lines.push(` ... and ${violations.length - 3} more`);
|
|
1421
|
+
}
|
|
1422
|
+
} else {
|
|
1423
|
+
lines.push("Drift: none");
|
|
1424
|
+
}
|
|
1425
|
+
return {
|
|
1426
|
+
mode,
|
|
1427
|
+
deliveredFiles,
|
|
1428
|
+
expectedFiles: expectedAllowlist,
|
|
1429
|
+
violations,
|
|
1430
|
+
hasDrift,
|
|
1431
|
+
summary: lines.join("\n")
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
var DEFAULT_COHERENCE_THRESHOLD = 0.3;
|
|
1435
|
+
function jaccardSimilarity(setA, setB) {
|
|
1436
|
+
if (setA.size === 0 && setB.size === 0) return 1;
|
|
1437
|
+
const intersection = new Set([...setA].filter((x) => setB.has(x)));
|
|
1438
|
+
const union = /* @__PURE__ */ new Set([...setA, ...setB]);
|
|
1439
|
+
return union.size > 0 ? intersection.size / union.size : 0;
|
|
1440
|
+
}
|
|
1441
|
+
function computeCoherenceMetrics(candidates, threshold = DEFAULT_COHERENCE_THRESHOLD, expectedAllowlist) {
|
|
1442
|
+
const allowlist = (expectedAllowlist ?? []).filter((v) => typeof v === "string" && v.length > 0);
|
|
1443
|
+
const hasAllowlist = allowlist.length > 0;
|
|
1444
|
+
const isAllowlisted = (filePath) => {
|
|
1445
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
1446
|
+
for (const entry of allowlist) {
|
|
1447
|
+
const rule = entry.replace(/\\/g, "/");
|
|
1448
|
+
if (rule.endsWith("/")) {
|
|
1449
|
+
if (normalized.startsWith(rule)) return true;
|
|
1450
|
+
} else if (normalized === rule) {
|
|
1451
|
+
return true;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
return false;
|
|
1455
|
+
};
|
|
1456
|
+
const filterCandidateFiles = (files) => {
|
|
1457
|
+
if (!files) return void 0;
|
|
1458
|
+
if (!hasAllowlist) return files;
|
|
1459
|
+
const filtered = files.filter(isAllowlisted);
|
|
1460
|
+
return filtered.length > 0 ? filtered : void 0;
|
|
1461
|
+
};
|
|
1462
|
+
const successfulCandidates = candidates.filter(
|
|
1463
|
+
(c) => c.success && filterCandidateFiles(c.changedFiles)?.length
|
|
1464
|
+
);
|
|
1465
|
+
if (successfulCandidates.length < 2) {
|
|
1466
|
+
return {
|
|
1467
|
+
pairwiseSimilarity: {},
|
|
1468
|
+
averageSimilarity: 1,
|
|
1469
|
+
// Single candidate = coherent with itself
|
|
1470
|
+
isCoherent: true,
|
|
1471
|
+
coherenceThreshold: threshold
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
const fileSets = /* @__PURE__ */ new Map();
|
|
1475
|
+
for (const [index, candidate] of successfulCandidates.entries()) {
|
|
1476
|
+
const provider = candidate.variant.provider;
|
|
1477
|
+
const model = candidate.variant.model ?? "";
|
|
1478
|
+
const key = `${provider}:${model}:${index}`;
|
|
1479
|
+
fileSets.set(key, new Set(filterCandidateFiles(candidate.changedFiles) || []));
|
|
1480
|
+
}
|
|
1481
|
+
const pairwiseSimilarity = {};
|
|
1482
|
+
const candidateKeys = Array.from(fileSets.keys());
|
|
1483
|
+
let totalSimilarity = 0;
|
|
1484
|
+
let pairCount = 0;
|
|
1485
|
+
for (let i = 0; i < candidateKeys.length; i++) {
|
|
1486
|
+
for (let j = i + 1; j < candidateKeys.length; j++) {
|
|
1487
|
+
const keyA = candidateKeys[i] ?? "";
|
|
1488
|
+
const keyB = candidateKeys[j] ?? "";
|
|
1489
|
+
const setA = fileSets.get(keyA);
|
|
1490
|
+
const setB = fileSets.get(keyB);
|
|
1491
|
+
if (!setA || !setB) continue;
|
|
1492
|
+
const similarity = jaccardSimilarity(setA, setB);
|
|
1493
|
+
const pairKey = `${keyA}|${keyB}`;
|
|
1494
|
+
pairwiseSimilarity[pairKey] = Math.round(similarity * 100) / 100;
|
|
1495
|
+
totalSimilarity += similarity;
|
|
1496
|
+
pairCount++;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
const averageSimilarity = pairCount > 0 ? totalSimilarity / pairCount : 1;
|
|
1500
|
+
const isCoherent = averageSimilarity >= threshold;
|
|
1501
|
+
const allFileSets = Array.from(fileSets.values());
|
|
1502
|
+
const allFiles = new Set(allFileSets.flatMap((s) => [...s]));
|
|
1503
|
+
const commonFiles = [...allFiles].filter((file) => allFileSets.every((set) => set.has(file)));
|
|
1504
|
+
const uniqueFiles = {};
|
|
1505
|
+
for (const [candidateKey, files] of fileSets) {
|
|
1506
|
+
const unique = [...files].filter(
|
|
1507
|
+
(file) => !Array.from(fileSets.entries()).filter(([k]) => k !== candidateKey).some(([, otherSet]) => otherSet.has(file))
|
|
1508
|
+
);
|
|
1509
|
+
if (unique.length > 0) {
|
|
1510
|
+
uniqueFiles[candidateKey] = unique;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return {
|
|
1514
|
+
pairwiseSimilarity,
|
|
1515
|
+
averageSimilarity: Math.round(averageSimilarity * 100) / 100,
|
|
1516
|
+
isCoherent,
|
|
1517
|
+
coherenceThreshold: threshold,
|
|
1518
|
+
fileOverlap: {
|
|
1519
|
+
commonFiles,
|
|
1520
|
+
uniqueFiles
|
|
1521
|
+
}
|
|
1522
|
+
};
|
|
1523
|
+
}
|
|
1524
|
+
function resolveCandidateCompletenessFallback(result) {
|
|
1525
|
+
const typed = resolveTypedCompletenessAuthority(result);
|
|
1526
|
+
if (typed.present) {
|
|
1527
|
+
return typed.usable ? typed.score : -1;
|
|
1528
|
+
}
|
|
1529
|
+
return result.completeness;
|
|
1530
|
+
}
|
|
1531
|
+
function variantResultToCandidate(spec, result) {
|
|
1532
|
+
if (!result) {
|
|
1533
|
+
return {
|
|
1534
|
+
variant: spec,
|
|
1535
|
+
success: false,
|
|
1536
|
+
error: "Variant not found in results",
|
|
1537
|
+
cost: 0,
|
|
1538
|
+
costUnavailableReason: "provider_not_reported",
|
|
1539
|
+
durationMs: 0
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
const variantEvaluation = result.judgeEvaluation?.evaluations?.find(
|
|
1543
|
+
(e) => e.variant === result.variant
|
|
1544
|
+
);
|
|
1545
|
+
const aiJudgeScore = variantEvaluation?.qualityScore != null ? Number(variantEvaluation.qualityScore) : void 0;
|
|
1546
|
+
const score = aiJudgeScore ?? result.judgeScore ?? resolveCandidateCompletenessFallback(result);
|
|
1547
|
+
const feedback = generateCandidateFeedback(variantEvaluation, result);
|
|
1548
|
+
const testStatus = result.testStatus;
|
|
1549
|
+
const testsExecuted = result.testsExecuted;
|
|
1550
|
+
const nativeObservedCost = getFiniteNumberAtPath(result, ["result", "totalCostUsd"]) ?? getFiniteNumberAtPath(result, ["result", "total_cost_usd"]) ?? getFiniteNumberAtPath(result, ["result", "totalCost"]) ?? getFiniteNumberAtPath(result, ["totalCostUsd"]);
|
|
1551
|
+
const metricsCost = getFiniteNumberAtPath(result.costMetrics, ["totalCost"]) ?? getFiniteNumberAtPath(result.costMetrics, ["totalCostUsd"]);
|
|
1552
|
+
const metricsUnavailableReason = resolveCostMetricsUnavailableReason(result.costMetrics);
|
|
1553
|
+
let cost;
|
|
1554
|
+
let costUnavailableReason;
|
|
1555
|
+
if (typeof nativeObservedCost === "number") {
|
|
1556
|
+
cost = nativeObservedCost;
|
|
1557
|
+
costUnavailableReason = void 0;
|
|
1558
|
+
} else if (typeof metricsCost === "number" && metricsCost > 0) {
|
|
1559
|
+
cost = metricsCost;
|
|
1560
|
+
costUnavailableReason = void 0;
|
|
1561
|
+
} else if (metricsUnavailableReason !== void 0) {
|
|
1562
|
+
cost = 0;
|
|
1563
|
+
costUnavailableReason = metricsUnavailableReason;
|
|
1564
|
+
} else if (typeof metricsCost === "number") {
|
|
1565
|
+
cost = metricsCost;
|
|
1566
|
+
costUnavailableReason = void 0;
|
|
1567
|
+
} else {
|
|
1568
|
+
cost = 0;
|
|
1569
|
+
costUnavailableReason = "provider_not_reported";
|
|
1570
|
+
}
|
|
1571
|
+
return {
|
|
1572
|
+
variant: spec,
|
|
1573
|
+
success: result.success,
|
|
1574
|
+
error: result.error?.message,
|
|
1575
|
+
...isProviderInvocationFailure(result) ? { providerFailed: true } : {},
|
|
1576
|
+
score,
|
|
1577
|
+
feedback,
|
|
1578
|
+
cost,
|
|
1579
|
+
costUnavailableReason,
|
|
1580
|
+
durationMs: getVariantDurationMs(result),
|
|
1581
|
+
worktreePath: result.retainedWorktreePath,
|
|
1582
|
+
changedFiles: extractChangedFiles(result),
|
|
1583
|
+
changeTrackingAvailable: resultHasChangeTracking(result),
|
|
1584
|
+
testStatus,
|
|
1585
|
+
testsExecuted,
|
|
1586
|
+
...repairOutcomeCanSupersedeRawTouchedFailure(result) ? { repairedTestStatus: deriveVariantOutcome(result).verification.testStatus } : {},
|
|
1587
|
+
mergeInvariantValidation: result.mergeInvariantValidation
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function resolveCompetitionCostAggregate(candidates) {
|
|
1591
|
+
let sum = 0;
|
|
1592
|
+
let firstExplicitReason;
|
|
1593
|
+
let sawPricingUnavailable = false;
|
|
1594
|
+
let sawUnavailable = false;
|
|
1595
|
+
for (const candidate of candidates) {
|
|
1596
|
+
if (candidate.costUnavailableReason !== void 0) {
|
|
1597
|
+
sawUnavailable = true;
|
|
1598
|
+
if (candidate.costUnavailableReason === "pricing_unavailable") {
|
|
1599
|
+
sawPricingUnavailable = true;
|
|
1600
|
+
} else if (firstExplicitReason === void 0) {
|
|
1601
|
+
firstExplicitReason = candidate.costUnavailableReason;
|
|
1602
|
+
}
|
|
1603
|
+
sum = 0;
|
|
1604
|
+
continue;
|
|
1605
|
+
}
|
|
1606
|
+
if (typeof candidate.cost !== "number" || !Number.isFinite(candidate.cost)) {
|
|
1607
|
+
sawUnavailable = true;
|
|
1608
|
+
if (firstExplicitReason === void 0) {
|
|
1609
|
+
firstExplicitReason = "provider_not_reported";
|
|
1610
|
+
}
|
|
1611
|
+
sum = 0;
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
if (!sawUnavailable) {
|
|
1615
|
+
sum += candidate.cost;
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
if (sawUnavailable) {
|
|
1619
|
+
return {
|
|
1620
|
+
cost: 0,
|
|
1621
|
+
costUnavailableReason: sawPricingUnavailable ? "pricing_unavailable" : firstExplicitReason ?? "provider_not_reported"
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
return { cost: sum };
|
|
1625
|
+
}
|
|
1626
|
+
function isJudgeType(value) {
|
|
1627
|
+
return value === "ai" || value === "multi-lens";
|
|
1628
|
+
}
|
|
1629
|
+
function buildMultiJudgeEvidenceBlock(args) {
|
|
1630
|
+
const synthesisContext = args.judgeResult?.synthesisContext;
|
|
1631
|
+
if (!synthesisContext && !args.matrixRan) {
|
|
1632
|
+
return void 0;
|
|
1633
|
+
}
|
|
1634
|
+
const judgesWithVerdicts = synthesisContext?.judgeResults ? Object.entries(synthesisContext.judgeResults).filter(([, result]) => result !== void 0 && result !== null).map(([judgeId]) => judgeId) : [];
|
|
1635
|
+
return {
|
|
1636
|
+
selectedJudge: synthesisContext?.selectedJudge,
|
|
1637
|
+
judgeRunRequested: args.judgeRunRequested ?? [],
|
|
1638
|
+
judgesWithVerdicts,
|
|
1639
|
+
allJudgesAgree: synthesisContext?.disagreement?.allAgree,
|
|
1640
|
+
matrixAuthority: args.matrixAuthority,
|
|
1641
|
+
matrixRan: args.matrixRan,
|
|
1642
|
+
matrixProfileAgreement: args.matrixProfileAgreement,
|
|
1643
|
+
matrixNonSelectionReasons: args.matrixNonSelectionReasons && args.matrixNonSelectionReasons.length > 0 ? args.matrixNonSelectionReasons : void 0
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
function recordDeterministicTestSignalObservation(candidates, verbose) {
|
|
1647
|
+
const hasVerifiedPassingCandidate = candidates.some(
|
|
1648
|
+
(c) => c.success && c.testsExecuted !== void 0 && c.testStatus === "passed"
|
|
1649
|
+
);
|
|
1650
|
+
if (!hasVerifiedPassingCandidate) return;
|
|
1651
|
+
for (const candidate of candidates) {
|
|
1652
|
+
if (!candidate.success) continue;
|
|
1653
|
+
if (!candidate.testsExecuted) continue;
|
|
1654
|
+
const status = candidate.testStatus ?? "not_run";
|
|
1655
|
+
if (status !== "failed" && status !== "error" && status !== "timeout") continue;
|
|
1656
|
+
const note = `Verified failing test observation recorded (testStatus=${status}).`;
|
|
1657
|
+
candidate.feedback = candidate.feedback ? `${note}
|
|
1658
|
+
|
|
1659
|
+
${candidate.feedback}` : note;
|
|
1660
|
+
if (verbose) {
|
|
1661
|
+
const id = formatCompetitionCandidateVariantDisplay(candidate);
|
|
1662
|
+
console.log(
|
|
1663
|
+
chalk.yellow(` Test observation: ${id} retained score ${candidate.score ?? 0}`)
|
|
1664
|
+
);
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
function variantResultDiagnosticId(result) {
|
|
1669
|
+
const provider = result.provider ?? "unknown";
|
|
1670
|
+
const model = result.model ?? "";
|
|
1671
|
+
const variant = result.variant ? ` (${result.variant})` : "";
|
|
1672
|
+
return `${provider}:${model}${variant}`;
|
|
1673
|
+
}
|
|
1674
|
+
function buildWinnerInfoFromJudgeVerdict(candidates, judgedWinner, judgeVisibleCandidates) {
|
|
1675
|
+
const rankedPool = judgeVisibleCandidates && judgeVisibleCandidates.length > 0 ? judgeVisibleCandidates : candidates.filter((c) => c.success);
|
|
1676
|
+
if (rankedPool.length === 0) {
|
|
1677
|
+
return {
|
|
1678
|
+
winner: null,
|
|
1679
|
+
noWinnerReason: "all-failed",
|
|
1680
|
+
wasTie: false,
|
|
1681
|
+
rankings: []
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
const ranked = [...rankedPool].sort((a, b) => {
|
|
1685
|
+
const scoreA = Number(a.score ?? 0);
|
|
1686
|
+
const scoreB = Number(b.score ?? 0);
|
|
1687
|
+
if (scoreB !== scoreA) return scoreB - scoreA;
|
|
1688
|
+
return variantIdForSpec(a.variant).localeCompare(variantIdForSpec(b.variant));
|
|
1689
|
+
});
|
|
1690
|
+
const rankings = ranked.map((c, i) => ({
|
|
1691
|
+
variant: c.variant,
|
|
1692
|
+
score: c.score ?? 0,
|
|
1693
|
+
rank: i + 1
|
|
1694
|
+
}));
|
|
1695
|
+
const topScore = Number(ranked[0]?.score ?? 0);
|
|
1696
|
+
const tiedAtTop = ranked.filter((c) => Number(c.score ?? 0) === topScore);
|
|
1697
|
+
const wasTie = tiedAtTop.length > 1;
|
|
1698
|
+
if (!judgedWinner) {
|
|
1699
|
+
return {
|
|
1700
|
+
winner: null,
|
|
1701
|
+
wasTie,
|
|
1702
|
+
rankings
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
const winner = findCandidateForVariantResult(candidates, judgedWinner);
|
|
1706
|
+
if (!winner) {
|
|
1707
|
+
return {
|
|
1708
|
+
winner: null,
|
|
1709
|
+
wasTie,
|
|
1710
|
+
rankings,
|
|
1711
|
+
noWinnerReason: "judge-error",
|
|
1712
|
+
noWinnerDiagnostic: `AI judge selected an unmatched winner (${variantResultDiagnosticId(
|
|
1713
|
+
judgedWinner
|
|
1714
|
+
)}); judge-visible candidates: ${rankedPool.map((candidate) => variantIdForSpec(candidate.variant)).join(", ")}`
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
return {
|
|
1718
|
+
winner,
|
|
1719
|
+
wasTie,
|
|
1720
|
+
rankings
|
|
1721
|
+
};
|
|
1722
|
+
}
|
|
1723
|
+
function attachGateObservationsToVariantResult(result, candidate) {
|
|
1724
|
+
const candidateFindings = candidate.verification?.findings ?? [];
|
|
1725
|
+
const gateError = candidate.success === false ? candidate.error : void 0;
|
|
1726
|
+
if (candidateFindings.length === 0 && !gateError) {
|
|
1727
|
+
return result;
|
|
1728
|
+
}
|
|
1729
|
+
const existingFindings = result.verification?.findings ?? [];
|
|
1730
|
+
const mergedFindings = [...existingFindings];
|
|
1731
|
+
for (const finding of candidateFindings) {
|
|
1732
|
+
const mapped = toCoreVerificationFinding(finding);
|
|
1733
|
+
const alreadyPresent = mergedFindings.some(
|
|
1734
|
+
(existing) => existing.kind === mapped.kind && existing.command === mapped.command && existing.exitCode === mapped.exitCode
|
|
1735
|
+
);
|
|
1736
|
+
if (!alreadyPresent) {
|
|
1737
|
+
mergedFindings.push(mapped);
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
if (gateError && mergedFindings.length === existingFindings.length) {
|
|
1741
|
+
mergedFindings.push({
|
|
1742
|
+
kind: "other",
|
|
1743
|
+
command: "candidate-gate",
|
|
1744
|
+
exitCode: 1,
|
|
1745
|
+
severity: "error",
|
|
1746
|
+
outputTail: gateError
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
const verificationPassed = candidate.verification?.passed === false ? false : gateError ? false : result.verification?.passed ?? true;
|
|
1750
|
+
result.verification = {
|
|
1751
|
+
...result.verification ?? {},
|
|
1752
|
+
passed: verificationPassed,
|
|
1753
|
+
findings: mergedFindings
|
|
1754
|
+
};
|
|
1755
|
+
return result;
|
|
1756
|
+
}
|
|
1757
|
+
function buildJudgeVisibleVariantResults(finishedCandidates, basicVariants) {
|
|
1758
|
+
const results = [];
|
|
1759
|
+
for (const candidate of finishedCandidates) {
|
|
1760
|
+
const matching = matchVariantResult(candidate.variant, basicVariants);
|
|
1761
|
+
if (!matching) continue;
|
|
1762
|
+
results.push(attachGateObservationsToVariantResult(matching, candidate));
|
|
1763
|
+
}
|
|
1764
|
+
return results;
|
|
1765
|
+
}
|
|
1766
|
+
function determineWinner(candidates, config, expectedAllowlist) {
|
|
1767
|
+
const SCORE_COLLISION_DECIMALS = 6;
|
|
1768
|
+
const SCORE_COLLISION_MULTIPLIER = 10 ** SCORE_COLLISION_DECIMALS;
|
|
1769
|
+
const quantizeScore = (score) => {
|
|
1770
|
+
if (!Number.isFinite(score)) return 0;
|
|
1771
|
+
return Math.round(score * SCORE_COLLISION_MULTIPLIER) / SCORE_COLLISION_MULTIPLIER;
|
|
1772
|
+
};
|
|
1773
|
+
const variantIdFor = variantIdForSpec;
|
|
1774
|
+
const buildCollisionReport = (args) => {
|
|
1775
|
+
const tiedAtTop2 = [...args.tiedAtTop].map((c) => {
|
|
1776
|
+
const raw = Number(c.score ?? 0);
|
|
1777
|
+
const q = quantizeScore(raw);
|
|
1778
|
+
return { candidate: c, scoreRaw: raw, scoreQuantized: q, id: variantIdFor(c.variant) };
|
|
1779
|
+
}).sort((a, b) => a.id.localeCompare(b.id)).map((x) => ({
|
|
1780
|
+
variant: x.candidate.variant,
|
|
1781
|
+
scoreRaw: x.scoreRaw,
|
|
1782
|
+
scoreQuantized: x.scoreQuantized
|
|
1783
|
+
}));
|
|
1784
|
+
const otherCollisionGroups2 = [...args.otherCollisionGroups].sort((a, b) => b.scoreQuantized - a.scoreQuantized).map((group) => ({
|
|
1785
|
+
scoreQuantized: group.scoreQuantized,
|
|
1786
|
+
entries: [...group.entries].map((e) => ({ ...e, id: variantIdFor(e.variant) })).sort((a, b) => a.id.localeCompare(b.id)).map((e) => ({ variant: e.variant, scoreRaw: e.scoreRaw }))
|
|
1787
|
+
}));
|
|
1788
|
+
return {
|
|
1789
|
+
precisionDecimals: SCORE_COLLISION_DECIMALS,
|
|
1790
|
+
quantization: "round",
|
|
1791
|
+
topScoreRaw: args.topScoreRaw,
|
|
1792
|
+
topScoreQuantized: args.topScoreQuantized,
|
|
1793
|
+
successfulCandidateCount: args.successfulCount,
|
|
1794
|
+
collisionGroupCount: args.collisionGroupsAll.length,
|
|
1795
|
+
collisionPairCount: args.collisionGroupsAll.reduce(
|
|
1796
|
+
(sum, g) => sum + g.entries.length * (g.entries.length - 1) / 2,
|
|
1797
|
+
0
|
|
1798
|
+
),
|
|
1799
|
+
tiedAtTopPairCount: args.tiedAtTop.length * (args.tiedAtTop.length - 1) / 2,
|
|
1800
|
+
tiedAtTop: tiedAtTop2,
|
|
1801
|
+
otherCollisionGroups: otherCollisionGroups2,
|
|
1802
|
+
applied: args.applied
|
|
1803
|
+
};
|
|
1804
|
+
};
|
|
1805
|
+
const computeCollisionGroups = (rankedSuccessful) => {
|
|
1806
|
+
const groups = /* @__PURE__ */ new Map();
|
|
1807
|
+
for (const candidate of rankedSuccessful) {
|
|
1808
|
+
const scoreRaw = Number(candidate.score ?? 0);
|
|
1809
|
+
const scoreQuantized = quantizeScore(scoreRaw);
|
|
1810
|
+
const existing = groups.get(scoreQuantized);
|
|
1811
|
+
if (existing) {
|
|
1812
|
+
existing.push({ variant: candidate.variant, scoreRaw });
|
|
1813
|
+
} else {
|
|
1814
|
+
groups.set(scoreQuantized, [{ variant: candidate.variant, scoreRaw }]);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
const out = [];
|
|
1818
|
+
for (const [scoreQuantized, entries] of groups.entries()) {
|
|
1819
|
+
if (entries.length < 2) continue;
|
|
1820
|
+
out.push({ scoreQuantized, entries });
|
|
1821
|
+
}
|
|
1822
|
+
return out;
|
|
1823
|
+
};
|
|
1824
|
+
const successfulCandidates = candidates.filter((c) => c.success);
|
|
1825
|
+
const threshold = config.judgeConfig?.threshold ?? 0;
|
|
1826
|
+
const allowBelowThresholdWinner = config.judgeConfig?.allowBelowThresholdWinner ?? true;
|
|
1827
|
+
if (successfulCandidates.length === 0) {
|
|
1828
|
+
return {
|
|
1829
|
+
winner: null,
|
|
1830
|
+
noWinnerReason: "all-failed",
|
|
1831
|
+
wasTie: false,
|
|
1832
|
+
rankings: []
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
const cleanCandidates = successfulCandidates.filter(
|
|
1836
|
+
(c) => !c.verification || c.verification.passed
|
|
1837
|
+
);
|
|
1838
|
+
const candidatesWithFindings = successfulCandidates.filter(
|
|
1839
|
+
(c) => c.verification && !c.verification.passed
|
|
1840
|
+
);
|
|
1841
|
+
const candidatesToRank = cleanCandidates.length > 0 ? cleanCandidates : successfulCandidates;
|
|
1842
|
+
const ranked = [...candidatesToRank].sort((a, b) => {
|
|
1843
|
+
const scoreA = Number(a.score ?? 0);
|
|
1844
|
+
const scoreB = Number(b.score ?? 0);
|
|
1845
|
+
const qa = quantizeScore(scoreA);
|
|
1846
|
+
const qb = quantizeScore(scoreB);
|
|
1847
|
+
if (qb !== qa) return qb - qa;
|
|
1848
|
+
return variantIdFor(a.variant).localeCompare(variantIdFor(b.variant));
|
|
1849
|
+
});
|
|
1850
|
+
const rankings = ranked.map((c, i) => ({
|
|
1851
|
+
variant: c.variant,
|
|
1852
|
+
score: c.score ?? 0,
|
|
1853
|
+
rank: i + 1
|
|
1854
|
+
}));
|
|
1855
|
+
const topScore = Number(ranked[0]?.score ?? 0);
|
|
1856
|
+
const topScoreQuantized = quantizeScore(topScore);
|
|
1857
|
+
const tiedAtTop = ranked.filter((c) => quantizeScore(Number(c.score ?? 0)) === topScoreQuantized);
|
|
1858
|
+
const wasTie = tiedAtTop.length > 1;
|
|
1859
|
+
const collisionGroups = computeCollisionGroups(ranked);
|
|
1860
|
+
const otherCollisionGroups = collisionGroups.filter((g) => g.scoreQuantized !== topScoreQuantized).slice(0, 10);
|
|
1861
|
+
const scoreCollisionReportBase = wasTie || otherCollisionGroups.length > 0 ? {
|
|
1862
|
+
ranked,
|
|
1863
|
+
successfulCount: candidatesToRank.length,
|
|
1864
|
+
collisionGroupsAll: collisionGroups,
|
|
1865
|
+
topScoreRaw: topScore,
|
|
1866
|
+
topScoreQuantized,
|
|
1867
|
+
tiedAtTop,
|
|
1868
|
+
otherCollisionGroups
|
|
1869
|
+
} : null;
|
|
1870
|
+
if (wasTie) {
|
|
1871
|
+
if (config.applyWinner) {
|
|
1872
|
+
const tieBreakerConfig = config.tieBreaker ?? (config.candidateGate?.coverage ? { mode: "prefer-low-drift-then-diff" } : { mode: "prefer-first-ranked" });
|
|
1873
|
+
let winner2 = null;
|
|
1874
|
+
let resolvedBy = "rank";
|
|
1875
|
+
const getCandidateChangedFileCount = (candidate) => Array.isArray(candidate.changedFiles) ? candidate.changedFiles.length : 0;
|
|
1876
|
+
const getCandidateDriftViolations = (candidate) => {
|
|
1877
|
+
if (!Array.isArray(candidate.changedFiles) || candidate.changedFiles.length === 0) return 0;
|
|
1878
|
+
if (!Array.isArray(expectedAllowlist) || expectedAllowlist.length === 0) return 0;
|
|
1879
|
+
return findAllowlistViolations(candidate.changedFiles, expectedAllowlist).length;
|
|
1880
|
+
};
|
|
1881
|
+
if (tieBreakerConfig.mode === "inconclusive") {
|
|
1882
|
+
const aiJudgeDisabled = config.judgeConfig?.disableAiJudge === true;
|
|
1883
|
+
if (aiJudgeDisabled) {
|
|
1884
|
+
const scoreCollisionReport2 = scoreCollisionReportBase && scoreCollisionReportBase.tiedAtTop.length > 1 ? buildCollisionReport({
|
|
1885
|
+
...scoreCollisionReportBase,
|
|
1886
|
+
applied: {
|
|
1887
|
+
applyWinner: Boolean(config.applyWinner),
|
|
1888
|
+
wasTie: true,
|
|
1889
|
+
noWinnerReason: "tie",
|
|
1890
|
+
tieBreakerMode: tieBreakerConfig.mode,
|
|
1891
|
+
resolvedBy: void 0,
|
|
1892
|
+
winnerVariant: void 0
|
|
1893
|
+
}
|
|
1894
|
+
}) : void 0;
|
|
1895
|
+
return {
|
|
1896
|
+
winner: null,
|
|
1897
|
+
noWinnerReason: "tie",
|
|
1898
|
+
wasTie: true,
|
|
1899
|
+
rankings,
|
|
1900
|
+
scoreCollisionReport: scoreCollisionReport2
|
|
1901
|
+
};
|
|
1902
|
+
}
|
|
1903
|
+
winner2 = tiedAtTop[0] ?? null;
|
|
1904
|
+
resolvedBy = "rank";
|
|
1905
|
+
} else if (tieBreakerConfig.mode === "prefer-first-ranked") {
|
|
1906
|
+
winner2 = tiedAtTop[0] ?? null;
|
|
1907
|
+
} else if (tieBreakerConfig.mode === "prefer-low-drift") {
|
|
1908
|
+
winner2 = [...tiedAtTop].sort((a, b) => {
|
|
1909
|
+
const driftDelta = getCandidateDriftViolations(a) - getCandidateDriftViolations(b);
|
|
1910
|
+
if (driftDelta !== 0) return driftDelta;
|
|
1911
|
+
return variantIdFor(a.variant).localeCompare(variantIdFor(b.variant));
|
|
1912
|
+
})[0] ?? null;
|
|
1913
|
+
resolvedBy = "drift";
|
|
1914
|
+
} else if (tieBreakerConfig.mode === "prefer-smaller-diff") {
|
|
1915
|
+
winner2 = [...tiedAtTop].sort((a, b) => {
|
|
1916
|
+
const diffDelta = getCandidateChangedFileCount(a) - getCandidateChangedFileCount(b);
|
|
1917
|
+
if (diffDelta !== 0) return diffDelta;
|
|
1918
|
+
return variantIdFor(a.variant).localeCompare(variantIdFor(b.variant));
|
|
1919
|
+
})[0] ?? null;
|
|
1920
|
+
resolvedBy = "diff";
|
|
1921
|
+
} else if (tieBreakerConfig.mode === "prefer-low-drift-then-diff") {
|
|
1922
|
+
winner2 = [...tiedAtTop].sort((a, b) => {
|
|
1923
|
+
const driftDelta = getCandidateDriftViolations(a) - getCandidateDriftViolations(b);
|
|
1924
|
+
if (driftDelta !== 0) return driftDelta;
|
|
1925
|
+
const diffDelta = getCandidateChangedFileCount(a) - getCandidateChangedFileCount(b);
|
|
1926
|
+
if (diffDelta !== 0) return diffDelta;
|
|
1927
|
+
return variantIdFor(a.variant).localeCompare(variantIdFor(b.variant));
|
|
1928
|
+
})[0] ?? null;
|
|
1929
|
+
if (winner2) {
|
|
1930
|
+
const winnerDrift = getCandidateDriftViolations(winner2);
|
|
1931
|
+
const allSameDrift = tiedAtTop.every(
|
|
1932
|
+
(c) => getCandidateDriftViolations(c) === winnerDrift
|
|
1933
|
+
);
|
|
1934
|
+
if (!allSameDrift) {
|
|
1935
|
+
resolvedBy = "drift";
|
|
1936
|
+
} else {
|
|
1937
|
+
const winnerDiff = getCandidateChangedFileCount(winner2);
|
|
1938
|
+
const allSameDiff = tiedAtTop.every(
|
|
1939
|
+
(c) => getCandidateChangedFileCount(c) === winnerDiff
|
|
1940
|
+
);
|
|
1941
|
+
resolvedBy = allSameDiff ? "rank" : "diff";
|
|
1942
|
+
}
|
|
1943
|
+
} else {
|
|
1944
|
+
resolvedBy = "drift";
|
|
1945
|
+
}
|
|
1946
|
+
} else {
|
|
1947
|
+
winner2 = tiedAtTop[0] ?? null;
|
|
1948
|
+
}
|
|
1949
|
+
if (!winner2) winner2 = ranked[0] ?? null;
|
|
1950
|
+
if (winner2) {
|
|
1951
|
+
const scoreCollisionReport2 = scoreCollisionReportBase && scoreCollisionReportBase.tiedAtTop.length > 1 ? buildCollisionReport({
|
|
1952
|
+
...scoreCollisionReportBase,
|
|
1953
|
+
applied: {
|
|
1954
|
+
applyWinner: true,
|
|
1955
|
+
wasTie: true,
|
|
1956
|
+
noWinnerReason: void 0,
|
|
1957
|
+
tieBreakerMode: tieBreakerConfig.mode,
|
|
1958
|
+
resolvedBy,
|
|
1959
|
+
winnerVariant: winner2.variant
|
|
1960
|
+
}
|
|
1961
|
+
}) : void 0;
|
|
1962
|
+
return {
|
|
1963
|
+
winner: winner2,
|
|
1964
|
+
wasTie: true,
|
|
1965
|
+
// Still record that there was a tie
|
|
1966
|
+
rankings,
|
|
1967
|
+
tieBreaker: {
|
|
1968
|
+
mode: tieBreakerConfig.mode,
|
|
1969
|
+
resolvedBy,
|
|
1970
|
+
winnerDriftViolations: getCandidateDriftViolations(winner2),
|
|
1971
|
+
winnerChangedFiles: getCandidateChangedFileCount(winner2),
|
|
1972
|
+
tieScore: topScore,
|
|
1973
|
+
tieScoreRounded: topScoreQuantized,
|
|
1974
|
+
tieScoreRoundedDecimals: SCORE_COLLISION_DECIMALS,
|
|
1975
|
+
tiedCount: tiedAtTop.length
|
|
1976
|
+
},
|
|
1977
|
+
scoreCollisionReport: scoreCollisionReport2
|
|
1978
|
+
};
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
const scoreCollisionReport = scoreCollisionReportBase && scoreCollisionReportBase.tiedAtTop.length > 1 ? buildCollisionReport({
|
|
1982
|
+
...scoreCollisionReportBase,
|
|
1983
|
+
applied: {
|
|
1984
|
+
applyWinner: Boolean(config.applyWinner),
|
|
1985
|
+
wasTie: true,
|
|
1986
|
+
noWinnerReason: "tie",
|
|
1987
|
+
tieBreakerMode: config.tieBreaker?.mode,
|
|
1988
|
+
resolvedBy: void 0,
|
|
1989
|
+
winnerVariant: void 0
|
|
1990
|
+
}
|
|
1991
|
+
}) : void 0;
|
|
1992
|
+
return {
|
|
1993
|
+
winner: null,
|
|
1994
|
+
noWinnerReason: "tie",
|
|
1995
|
+
wasTie: true,
|
|
1996
|
+
rankings,
|
|
1997
|
+
scoreCollisionReport
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
if (topScore < threshold) {
|
|
2001
|
+
const verificationPolicy = config.verificationPolicy ?? "findings";
|
|
2002
|
+
if (verificationPolicy === "findings") {
|
|
2003
|
+
const bestRepairable = candidatesWithFindings.map((c) => ({
|
|
2004
|
+
candidate: c,
|
|
2005
|
+
effectiveScore: c.scoreBeforeTestSignal ?? c.score ?? 0
|
|
2006
|
+
})).filter((x) => x.effectiveScore >= threshold).sort((a, b) => b.effectiveScore - a.effectiveScore)[0]?.candidate;
|
|
2007
|
+
if (bestRepairable) {
|
|
2008
|
+
const restoredScore = bestRepairable.scoreBeforeTestSignal ?? bestRepairable.score;
|
|
2009
|
+
return {
|
|
2010
|
+
winner: {
|
|
2011
|
+
...bestRepairable,
|
|
2012
|
+
score: restoredScore,
|
|
2013
|
+
feedback: bestRepairable.feedback ? `Selected for repair (tests recorded findings; observed score: ${restoredScore}).
|
|
2014
|
+
|
|
2015
|
+
${bestRepairable.feedback}` : `Selected for repair (tests recorded findings; observed score: ${restoredScore}).`
|
|
2016
|
+
},
|
|
2017
|
+
wasTie: false,
|
|
2018
|
+
rankings
|
|
2019
|
+
};
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
if (allowBelowThresholdWinner) {
|
|
2023
|
+
const winner2 = ranked[0] ?? null;
|
|
2024
|
+
if (winner2) {
|
|
2025
|
+
return {
|
|
2026
|
+
winner: winner2,
|
|
2027
|
+
wasTie: false,
|
|
2028
|
+
rankings,
|
|
2029
|
+
belowThresholdWinner: {
|
|
2030
|
+
threshold,
|
|
2031
|
+
winnerScore: topScore
|
|
2032
|
+
},
|
|
2033
|
+
scoreCollisionReport: scoreCollisionReportBase && (scoreCollisionReportBase.tiedAtTop.length > 1 || scoreCollisionReportBase.otherCollisionGroups.length > 0) ? buildCollisionReport({
|
|
2034
|
+
...scoreCollisionReportBase,
|
|
2035
|
+
applied: {
|
|
2036
|
+
applyWinner: Boolean(config.applyWinner),
|
|
2037
|
+
wasTie: false,
|
|
2038
|
+
noWinnerReason: void 0,
|
|
2039
|
+
tieBreakerMode: config.tieBreaker?.mode,
|
|
2040
|
+
resolvedBy: void 0,
|
|
2041
|
+
winnerVariant: winner2.variant
|
|
2042
|
+
}
|
|
2043
|
+
}) : void 0
|
|
2044
|
+
};
|
|
2045
|
+
}
|
|
2046
|
+
}
|
|
2047
|
+
return {
|
|
2048
|
+
winner: null,
|
|
2049
|
+
noWinnerReason: "below-threshold",
|
|
2050
|
+
wasTie: false,
|
|
2051
|
+
rankings,
|
|
2052
|
+
scoreCollisionReport: scoreCollisionReportBase && (scoreCollisionReportBase.tiedAtTop.length > 1 || scoreCollisionReportBase.otherCollisionGroups.length > 0) ? buildCollisionReport({
|
|
2053
|
+
...scoreCollisionReportBase,
|
|
2054
|
+
applied: {
|
|
2055
|
+
applyWinner: Boolean(config.applyWinner),
|
|
2056
|
+
wasTie: false,
|
|
2057
|
+
noWinnerReason: "below-threshold",
|
|
2058
|
+
tieBreakerMode: config.tieBreaker?.mode,
|
|
2059
|
+
resolvedBy: void 0,
|
|
2060
|
+
winnerVariant: void 0
|
|
2061
|
+
}
|
|
2062
|
+
}) : void 0
|
|
2063
|
+
};
|
|
2064
|
+
}
|
|
2065
|
+
const winner = ranked[0] ?? null;
|
|
2066
|
+
return {
|
|
2067
|
+
winner,
|
|
2068
|
+
wasTie: false,
|
|
2069
|
+
rankings,
|
|
2070
|
+
scoreCollisionReport: scoreCollisionReportBase && (scoreCollisionReportBase.tiedAtTop.length > 1 || scoreCollisionReportBase.otherCollisionGroups.length > 0) ? buildCollisionReport({
|
|
2071
|
+
...scoreCollisionReportBase,
|
|
2072
|
+
applied: {
|
|
2073
|
+
applyWinner: Boolean(config.applyWinner),
|
|
2074
|
+
wasTie: false,
|
|
2075
|
+
noWinnerReason: void 0,
|
|
2076
|
+
tieBreakerMode: config.tieBreaker?.mode,
|
|
2077
|
+
resolvedBy: void 0,
|
|
2078
|
+
winnerVariant: winner?.variant
|
|
2079
|
+
}
|
|
2080
|
+
}) : void 0
|
|
2081
|
+
};
|
|
2082
|
+
}
|
|
2083
|
+
var __internalDetermineWinner = determineWinner;
|
|
2084
|
+
var __internalRecordDeterministicTestSignalObservation = recordDeterministicTestSignalObservation;
|
|
2085
|
+
var __internalExtractChangedFilesFromResult = extractChangedFilesFromResult;
|
|
2086
|
+
var __internalGetVariantTelemetryStatePath = getVariantTelemetryStatePath;
|
|
2087
|
+
var __internalHydrateCandidateCostsFromTelemetry = hydrateCandidateCostsFromTelemetry;
|
|
2088
|
+
var __internalVariantResultToCandidate = variantResultToCandidate;
|
|
2089
|
+
var __internalMatchVariantResult = matchVariantResult;
|
|
2090
|
+
var __internalCandidateMatchesVariantResult = candidateMatchesVariantResult;
|
|
2091
|
+
var __internalResolveCompetitionCostAggregate = resolveCompetitionCostAggregate;
|
|
2092
|
+
function synthesizeDeterministicRationalePacket(candidates, winnerInfo, config) {
|
|
2093
|
+
const { winner, wasTie, noWinnerReason, tieBreaker, belowThresholdWinner } = winnerInfo;
|
|
2094
|
+
const buildVariantId = candidateScoreId;
|
|
2095
|
+
const candidateToVariantResult = (candidate) => ({
|
|
2096
|
+
variant: buildVariantId(candidate),
|
|
2097
|
+
success: candidate.success,
|
|
2098
|
+
testStatus: candidate.testStatus,
|
|
2099
|
+
testsExecuted: candidate.testsExecuted ? {
|
|
2100
|
+
scope: candidate.testsExecuted.scope ?? "touched",
|
|
2101
|
+
filesTested: candidate.testsExecuted.filesTested ?? [],
|
|
2102
|
+
exitCode: candidate.testsExecuted.exitCode,
|
|
2103
|
+
durationMs: candidate.testsExecuted.durationMs
|
|
2104
|
+
} : void 0,
|
|
2105
|
+
emptyChanges: !candidate.changedFiles?.length,
|
|
2106
|
+
gitMetrics: candidate.changedFiles ? { filesChanged: candidate.changedFiles.length, linesAdded: 0, linesDeleted: 0 } : void 0,
|
|
2107
|
+
worktreeSummary: candidate.changedFiles ? {
|
|
2108
|
+
filesChanged: candidate.changedFiles.length,
|
|
2109
|
+
insertions: 0,
|
|
2110
|
+
deletions: 0,
|
|
2111
|
+
statusLines: candidate.changedFiles.map((f) => `M ${f}`)
|
|
2112
|
+
} : void 0
|
|
2113
|
+
});
|
|
2114
|
+
const effectiveWinner = winner;
|
|
2115
|
+
const winnerId = effectiveWinner ? buildVariantId(effectiveWinner) : "none";
|
|
2116
|
+
const scores = {};
|
|
2117
|
+
for (const candidate of candidates) {
|
|
2118
|
+
const variantId = buildVariantId(candidate);
|
|
2119
|
+
scores[variantId] = candidate.score ?? 0;
|
|
2120
|
+
}
|
|
2121
|
+
let unverifiedNegativeClaimCount = 0;
|
|
2122
|
+
const scoreBreakdown = {};
|
|
2123
|
+
for (const candidate of candidates) {
|
|
2124
|
+
const variantId = buildVariantId(candidate);
|
|
2125
|
+
const finalScore = candidate.score ?? 0;
|
|
2126
|
+
const subscores = {};
|
|
2127
|
+
const penalties = {};
|
|
2128
|
+
if (typeof candidate.score === "number") {
|
|
2129
|
+
subscores.delivery = candidate.score;
|
|
2130
|
+
}
|
|
2131
|
+
const deductions = [];
|
|
2132
|
+
if (candidate.testStatus === "failed" || candidate.testStatus === "error") {
|
|
2133
|
+
const testPenalty = candidate.scoreBeforeTestSignal ? candidate.scoreBeforeTestSignal - (candidate.score ?? 0) : 10;
|
|
2134
|
+
penalties.testFailure = testPenalty;
|
|
2135
|
+
const testEvidence = [];
|
|
2136
|
+
testEvidence.push({
|
|
2137
|
+
type: "metric",
|
|
2138
|
+
value: `testStatus: ${candidate.testStatus}`,
|
|
2139
|
+
context: "Test execution result"
|
|
2140
|
+
});
|
|
2141
|
+
if (candidate.testsExecuted?.exitCode !== void 0) {
|
|
2142
|
+
testEvidence.push({
|
|
2143
|
+
type: "metric",
|
|
2144
|
+
value: `exit code: ${candidate.testsExecuted.exitCode}`,
|
|
2145
|
+
context: "Test runner exit code"
|
|
2146
|
+
});
|
|
2147
|
+
}
|
|
2148
|
+
deductions.push({
|
|
2149
|
+
code: "test_failure",
|
|
2150
|
+
delta: -testPenalty,
|
|
2151
|
+
evidence: testEvidence,
|
|
2152
|
+
reason: "Tests failed during verification"
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
if (!candidate.changedFiles?.length && candidate.success) {
|
|
2156
|
+
deductions.push({
|
|
2157
|
+
code: "empty_changes",
|
|
2158
|
+
delta: -50,
|
|
2159
|
+
evidence: [
|
|
2160
|
+
{
|
|
2161
|
+
type: "metric",
|
|
2162
|
+
value: "0 files changed",
|
|
2163
|
+
context: "Git status shows no changes"
|
|
2164
|
+
}
|
|
2165
|
+
],
|
|
2166
|
+
reason: "No code changes detected"
|
|
2167
|
+
});
|
|
2168
|
+
}
|
|
2169
|
+
if (Array.isArray(candidate.changedFiles) && candidate.changedFiles.length > 10) {
|
|
2170
|
+
const excessPenalty = Math.min(10, (candidate.changedFiles.length - 10) * 2);
|
|
2171
|
+
penalties.other = excessPenalty;
|
|
2172
|
+
deductions.push({
|
|
2173
|
+
code: "off_target_files",
|
|
2174
|
+
delta: -excessPenalty,
|
|
2175
|
+
evidence: [
|
|
2176
|
+
{
|
|
2177
|
+
type: "metric",
|
|
2178
|
+
value: `${candidate.changedFiles.length} files changed`,
|
|
2179
|
+
context: "Excessive file changes may indicate scope creep"
|
|
2180
|
+
}
|
|
2181
|
+
],
|
|
2182
|
+
reason: "Modified more than 10 files"
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
const weights = {
|
|
2186
|
+
delivery: 1
|
|
2187
|
+
// Deterministic mode uses completeness as sole factor
|
|
2188
|
+
};
|
|
2189
|
+
const testStatus = candidate.testStatus;
|
|
2190
|
+
const testsExecuted = candidate.testsExecuted;
|
|
2191
|
+
scoreBreakdown[variantId] = {
|
|
2192
|
+
finalScore,
|
|
2193
|
+
subscores,
|
|
2194
|
+
weights,
|
|
2195
|
+
penalties: Object.keys(penalties).length > 0 ? penalties : void 0,
|
|
2196
|
+
aggregationMethod: "weighted_average",
|
|
2197
|
+
// Default for deterministic scoring
|
|
2198
|
+
// P2: Include floor (harmonic mean floor = 18 in Judge v2.2)
|
|
2199
|
+
floorApplied: 18,
|
|
2200
|
+
// P2b: Structured test status (first-class signal)
|
|
2201
|
+
...testStatus && {
|
|
2202
|
+
testSignal: {
|
|
2203
|
+
status: testStatus,
|
|
2204
|
+
scope: testsExecuted?.scope,
|
|
2205
|
+
exitCode: testsExecuted?.exitCode,
|
|
2206
|
+
durationMs: testsExecuted?.durationMs,
|
|
2207
|
+
penaltyApplied: testStatus === "failed" || testStatus === "error" ? (penalties.testFailure ?? 0) > 0 : false
|
|
2208
|
+
}
|
|
2209
|
+
},
|
|
2210
|
+
// Include deduction trace with evidence (v0.5.0)
|
|
2211
|
+
deductions: deductions.length > 0 ? deductions : void 0
|
|
2212
|
+
};
|
|
2213
|
+
}
|
|
2214
|
+
const highestScoringCandidate = candidates.filter((candidate) => candidate.success && typeof candidate.score === "number").sort((a, b) => {
|
|
2215
|
+
const scoreDelta = Number(b.score ?? 0) - Number(a.score ?? 0);
|
|
2216
|
+
if (scoreDelta !== 0) return scoreDelta;
|
|
2217
|
+
return buildVariantId(a).localeCompare(buildVariantId(b));
|
|
2218
|
+
})[0];
|
|
2219
|
+
const highestScoringCandidateId = highestScoringCandidate ? buildVariantId(highestScoringCandidate) : void 0;
|
|
2220
|
+
const highestScore = highestScoringCandidate?.score ?? 0;
|
|
2221
|
+
const winnerScore = effectiveWinner?.score ?? 0;
|
|
2222
|
+
const winnerHasHighestDeterministicScore = Boolean(effectiveWinner) && (!highestScoringCandidate || highestScoringCandidateId === winnerId || Number(winnerScore) >= Number(highestScore));
|
|
2223
|
+
const candidateAnalysis = candidates.map((candidate) => {
|
|
2224
|
+
const variantId = buildVariantId(candidate);
|
|
2225
|
+
const isWinner = effectiveWinner && buildVariantId(effectiveWinner) === variantId;
|
|
2226
|
+
const strengths = [];
|
|
2227
|
+
const weaknesses = [];
|
|
2228
|
+
const missedRequirements = [];
|
|
2229
|
+
const riskyAreas = [];
|
|
2230
|
+
if (candidate.success) {
|
|
2231
|
+
strengths.push("Execution succeeded");
|
|
2232
|
+
if (typeof candidate.score === "number" && candidate.score > 80) {
|
|
2233
|
+
strengths.push(`High score: ${candidate.score}`);
|
|
2234
|
+
}
|
|
2235
|
+
if (candidate.testsExecuted && candidate.testStatus) {
|
|
2236
|
+
const scope = candidate.testsExecuted.scope ?? "unknown";
|
|
2237
|
+
if (candidate.testStatus === "passed") {
|
|
2238
|
+
strengths.push(`Tests passed (${scope})`);
|
|
2239
|
+
} else if (candidate.testStatus !== "not_run") {
|
|
2240
|
+
weaknesses.push(`Tests ${candidate.testStatus} (${scope})`);
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
if (Array.isArray(candidate.changedFiles) && candidate.changedFiles.length > 0) {
|
|
2244
|
+
strengths.push(`Modified ${candidate.changedFiles.length} file(s)`);
|
|
2245
|
+
}
|
|
2246
|
+
} else {
|
|
2247
|
+
weaknesses.push("Execution failed");
|
|
2248
|
+
if (candidate.error) {
|
|
2249
|
+
weaknesses.push(`Error: ${candidate.error}`);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
if (!isWinner && candidate.success) {
|
|
2253
|
+
const candidateScore = candidate.score ?? 0;
|
|
2254
|
+
if (effectiveWinner && typeof candidate.score === "number") {
|
|
2255
|
+
if (candidateScore > winnerScore) {
|
|
2256
|
+
weaknesses.push(
|
|
2257
|
+
`Not selected despite higher deterministic score (${candidateScore} vs selected ${winnerScore})`
|
|
2258
|
+
);
|
|
2259
|
+
} else if (candidateScore === winnerScore) {
|
|
2260
|
+
weaknesses.push("Final tie-breaker selected another candidate");
|
|
2261
|
+
} else {
|
|
2262
|
+
weaknesses.push("Lower score than winner");
|
|
2263
|
+
}
|
|
2264
|
+
} else {
|
|
2265
|
+
weaknesses.push("Did not win final selection");
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
const variantForEvidence = candidateToVariantResult(candidate);
|
|
2269
|
+
const weaknessEvidence = {
|
|
2270
|
+
weaknesses: weaknesses.map((weakness, index) => {
|
|
2271
|
+
const isNegative = isNegativeClaim(weakness);
|
|
2272
|
+
const anchors = isNegative ? extractEvidenceAnchors(weakness, variantForEvidence) : [];
|
|
2273
|
+
const verified = !isNegative || anchors.length > 0;
|
|
2274
|
+
if (isNegative && !verified) {
|
|
2275
|
+
unverifiedNegativeClaimCount++;
|
|
2276
|
+
}
|
|
2277
|
+
return {
|
|
2278
|
+
index,
|
|
2279
|
+
anchors: anchors.map((a) => ({
|
|
2280
|
+
type: a.type,
|
|
2281
|
+
value: a.value,
|
|
2282
|
+
context: a.context
|
|
2283
|
+
})),
|
|
2284
|
+
verified
|
|
2285
|
+
};
|
|
2286
|
+
})
|
|
2287
|
+
};
|
|
2288
|
+
return {
|
|
2289
|
+
variantId,
|
|
2290
|
+
strengths,
|
|
2291
|
+
weaknesses,
|
|
2292
|
+
missedRequirements,
|
|
2293
|
+
riskyAreas,
|
|
2294
|
+
evidence: weaknessEvidence.weaknesses?.length ? weaknessEvidence : void 0
|
|
2295
|
+
};
|
|
2296
|
+
});
|
|
2297
|
+
let primaryReason = "Winner selected by deterministic scoring";
|
|
2298
|
+
const keyFactors = [];
|
|
2299
|
+
const selectionEvidence = [];
|
|
2300
|
+
if (noWinnerReason) {
|
|
2301
|
+
const reasons = {
|
|
2302
|
+
"all-failed": "All variants failed execution",
|
|
2303
|
+
tie: "Tie between top variants",
|
|
2304
|
+
"below-threshold": `All scores below threshold (${config.judgeConfig?.threshold ?? 0})`,
|
|
2305
|
+
"judge-error": "Judge evaluation failed",
|
|
2306
|
+
incomparable: "Candidates touched disjoint files (low coherence)"
|
|
2307
|
+
};
|
|
2308
|
+
primaryReason = reasons[noWinnerReason] ?? noWinnerReason;
|
|
2309
|
+
} else if (wasTie && tieBreaker) {
|
|
2310
|
+
primaryReason = `Tie resolved by ${tieBreaker.mode} tiebreaker`;
|
|
2311
|
+
keyFactors.push(`Tiebreaker mode: ${tieBreaker.mode}`);
|
|
2312
|
+
if (tieBreaker.resolvedBy) {
|
|
2313
|
+
keyFactors.push(`Resolved by: ${tieBreaker.resolvedBy}`);
|
|
2314
|
+
}
|
|
2315
|
+
} else if (winner) {
|
|
2316
|
+
primaryReason = winnerHasHighestDeterministicScore ? "Highest deterministic score" : "Final winner selected by judge or selection policy despite lower deterministic score";
|
|
2317
|
+
keyFactors.push(`Score: ${winner.score ?? 0}`);
|
|
2318
|
+
if (!winnerHasHighestDeterministicScore && highestScoringCandidateId) {
|
|
2319
|
+
keyFactors.push(`Top deterministic score: ${highestScoringCandidateId} (${highestScore})`);
|
|
2320
|
+
keyFactors.push(`Selected winner score: ${winnerId} (${winnerScore})`);
|
|
2321
|
+
}
|
|
2322
|
+
selectionEvidence.push({
|
|
2323
|
+
type: "metric",
|
|
2324
|
+
value: `score: ${winner.score ?? 0}`,
|
|
2325
|
+
context: "Winner deterministic score"
|
|
2326
|
+
});
|
|
2327
|
+
if (winner.changedFiles?.length) {
|
|
2328
|
+
selectionEvidence.push({
|
|
2329
|
+
type: "metric",
|
|
2330
|
+
value: `${winner.changedFiles.length} files changed`,
|
|
2331
|
+
context: "Winner modified files"
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
if (belowThresholdWinner) {
|
|
2335
|
+
keyFactors.push(
|
|
2336
|
+
`Below threshold: ${belowThresholdWinner.winnerScore} < ${belowThresholdWinner.threshold}`
|
|
2337
|
+
);
|
|
2338
|
+
}
|
|
2339
|
+
if (winner.testsExecuted && winner.testStatus === "passed") {
|
|
2340
|
+
keyFactors.push("Verified passing tests");
|
|
2341
|
+
selectionEvidence.push({
|
|
2342
|
+
type: "metric",
|
|
2343
|
+
value: "testStatus: passed",
|
|
2344
|
+
context: `Tests verified (${winner.testsExecuted.scope ?? "unknown"} scope)`
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
if (winner.feedback) {
|
|
2348
|
+
keyFactors.push("Has feedback from deterministic evaluation");
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
const rejectionReasons = {};
|
|
2352
|
+
const rejectionReasonsEvidence = {};
|
|
2353
|
+
for (const candidate of candidates) {
|
|
2354
|
+
const variantId = buildVariantId(candidate);
|
|
2355
|
+
if (effectiveWinner && buildVariantId(effectiveWinner) === variantId) continue;
|
|
2356
|
+
let reason;
|
|
2357
|
+
if (!candidate.success) {
|
|
2358
|
+
reason = candidate.error ?? "Execution failed";
|
|
2359
|
+
} else if (candidate.testsExecuted && candidate.testStatus && candidate.testStatus !== "passed") {
|
|
2360
|
+
reason = `Verified failing tests (testStatus=${candidate.testStatus})`;
|
|
2361
|
+
} else if (typeof candidate.score === "number" && typeof effectiveWinner?.score === "number") {
|
|
2362
|
+
if (candidate.score > effectiveWinner.score) {
|
|
2363
|
+
reason = `Not selected despite higher deterministic score (${candidate.score} vs selected ${effectiveWinner.score})`;
|
|
2364
|
+
} else if (candidate.score === effectiveWinner.score) {
|
|
2365
|
+
reason = `Final tie-breaker selected another candidate (${candidate.score} vs selected ${effectiveWinner.score})`;
|
|
2366
|
+
} else {
|
|
2367
|
+
reason = `Lower score (${candidate.score} vs ${effectiveWinner.score})`;
|
|
2368
|
+
}
|
|
2369
|
+
} else {
|
|
2370
|
+
reason = "Did not win deterministic selection";
|
|
2371
|
+
}
|
|
2372
|
+
rejectionReasons[variantId] = reason;
|
|
2373
|
+
if (isNegativeClaim(reason)) {
|
|
2374
|
+
const variantForEvidence = candidateToVariantResult(candidate);
|
|
2375
|
+
const anchors = extractEvidenceAnchors(reason, variantForEvidence);
|
|
2376
|
+
const verified = anchors.length > 0;
|
|
2377
|
+
rejectionReasonsEvidence[variantId] = {
|
|
2378
|
+
anchors: anchors.map((a) => ({
|
|
2379
|
+
type: a.type,
|
|
2380
|
+
value: a.value,
|
|
2381
|
+
context: a.context
|
|
2382
|
+
})),
|
|
2383
|
+
verified
|
|
2384
|
+
};
|
|
2385
|
+
if (!verified) {
|
|
2386
|
+
unverifiedNegativeClaimCount++;
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
const suggestedImprovements = [];
|
|
2391
|
+
const failedCount = candidates.filter((c) => !c.success).length;
|
|
2392
|
+
if (failedCount > 0) {
|
|
2393
|
+
suggestedImprovements.push(`${failedCount} variant(s) failed execution - investigate errors`);
|
|
2394
|
+
}
|
|
2395
|
+
if (wasTie) {
|
|
2396
|
+
suggestedImprovements.push("Consider refining task requirements to reduce ties");
|
|
2397
|
+
}
|
|
2398
|
+
let baseConfidence = noWinnerReason ? 0 : wasTie ? 60 : 100;
|
|
2399
|
+
if (unverifiedNegativeClaimCount > 0) {
|
|
2400
|
+
const penalty = Math.min(0.25, unverifiedNegativeClaimCount * 0.05);
|
|
2401
|
+
baseConfidence = Math.max(0, baseConfidence * (1 - penalty));
|
|
2402
|
+
}
|
|
2403
|
+
return {
|
|
2404
|
+
winnerId,
|
|
2405
|
+
scores,
|
|
2406
|
+
scoreBreakdown,
|
|
2407
|
+
// P1 Fix: Include detailed score breakdown with deduction traces
|
|
2408
|
+
candidateAnalysis,
|
|
2409
|
+
selectionRationale: {
|
|
2410
|
+
primary: primaryReason,
|
|
2411
|
+
keyFactors,
|
|
2412
|
+
confidence: baseConfidence,
|
|
2413
|
+
evidence: selectionEvidence.length > 0 ? selectionEvidence : void 0
|
|
2414
|
+
},
|
|
2415
|
+
rejectionReasons,
|
|
2416
|
+
rejectionReasonsEvidence: Object.keys(rejectionReasonsEvidence).length > 0 ? rejectionReasonsEvidence : void 0,
|
|
2417
|
+
suggestedImprovements,
|
|
2418
|
+
unverifiedNegativeClaimCount: unverifiedNegativeClaimCount > 0 ? unverifiedNegativeClaimCount : void 0
|
|
2419
|
+
};
|
|
2420
|
+
}
|
|
2421
|
+
function buildCompetitionBundle(taskId, candidates, basicResult) {
|
|
2422
|
+
const lines = [];
|
|
2423
|
+
lines.push("# Competition Bundle");
|
|
2424
|
+
lines.push("");
|
|
2425
|
+
lines.push(`Task ID: ${taskId}`);
|
|
2426
|
+
lines.push("");
|
|
2427
|
+
for (const candidate of candidates) {
|
|
2428
|
+
const provider = candidate.variant.provider;
|
|
2429
|
+
const model = candidate.variant.model;
|
|
2430
|
+
const header = [provider, model].filter(Boolean).join(" / ");
|
|
2431
|
+
const result = matchVariantResult(candidate.variant, basicResult.variants);
|
|
2432
|
+
lines.push(`## ${header || provider}`);
|
|
2433
|
+
lines.push("");
|
|
2434
|
+
lines.push(`- Success: ${candidate.success ? "yes" : "no"}`);
|
|
2435
|
+
lines.push(`- Score: ${candidate.score ?? 0}`);
|
|
2436
|
+
lines.push(`- Cost: $${candidate.cost.toFixed(4)}`);
|
|
2437
|
+
lines.push(`- Duration: ${(candidate.durationMs / 1e3).toFixed(1)}s`);
|
|
2438
|
+
if (candidate.error) {
|
|
2439
|
+
lines.push(`- Error: ${candidate.error}`);
|
|
2440
|
+
}
|
|
2441
|
+
const diff = result?.worktreeDiff;
|
|
2442
|
+
if (typeof diff === "string" && diff.trim().length > 0) {
|
|
2443
|
+
lines.push("");
|
|
2444
|
+
lines.push("```diff");
|
|
2445
|
+
lines.push(diff.trimEnd());
|
|
2446
|
+
lines.push("```");
|
|
2447
|
+
} else {
|
|
2448
|
+
lines.push("");
|
|
2449
|
+
lines.push("_No diff captured._");
|
|
2450
|
+
}
|
|
2451
|
+
lines.push("");
|
|
2452
|
+
}
|
|
2453
|
+
return lines.join("\n");
|
|
2454
|
+
}
|
|
2455
|
+
async function executeCompetitionStage(ctx) {
|
|
2456
|
+
const { stage, artifactStore, options } = ctx;
|
|
2457
|
+
const securityBoundary = ctx.securityBoundary ?? createSecurityBoundary(artifactStore.getWorkflowId());
|
|
2458
|
+
const startTime = Date.now();
|
|
2459
|
+
const config = stage.competition;
|
|
2460
|
+
if (!config || !config.variants || config.variants.length === 0) {
|
|
2461
|
+
throw new Error(`Stage '${stage.id}' is type=competition but has no variants configured`);
|
|
2462
|
+
}
|
|
2463
|
+
if (options.verbose) {
|
|
2464
|
+
console.log(chalk.blue(`
|
|
2465
|
+
Competition Stage: ${stage.id}`));
|
|
2466
|
+
console.log(chalk.gray(` Variants: ${config.variants.length}`));
|
|
2467
|
+
console.log(chalk.gray(` Providers: ${config.variants.map((v) => v.provider).join(", ")}`));
|
|
2468
|
+
}
|
|
2469
|
+
let resolvedPrompt = stage.prompt ?? "";
|
|
2470
|
+
try {
|
|
2471
|
+
resolvedPrompt = await resolvePromptArtifacts(
|
|
2472
|
+
stage.prompt ?? "",
|
|
2473
|
+
stage.inputs,
|
|
2474
|
+
artifactStore,
|
|
2475
|
+
stage.id,
|
|
2476
|
+
securityBoundary
|
|
2477
|
+
);
|
|
2478
|
+
} catch (error) {
|
|
2479
|
+
if (error instanceof ArtifactNotFoundError) {
|
|
2480
|
+
const result = {
|
|
2481
|
+
stageId: stage.id,
|
|
2482
|
+
stageType: "competition",
|
|
2483
|
+
validationPassed: false,
|
|
2484
|
+
cost: 0,
|
|
2485
|
+
candidates: [],
|
|
2486
|
+
winner: null,
|
|
2487
|
+
summary: `Failed to resolve input artifacts: ${error.message}`,
|
|
2488
|
+
durationMs: Date.now() - startTime
|
|
2489
|
+
};
|
|
2490
|
+
return result;
|
|
2491
|
+
}
|
|
2492
|
+
throw error;
|
|
2493
|
+
}
|
|
2494
|
+
if (options.verbose) {
|
|
2495
|
+
console.log(chalk.gray(` Prompt length: ${resolvedPrompt.length} chars`));
|
|
2496
|
+
}
|
|
2497
|
+
const effectiveMode = options.allowlistMode ?? "warn";
|
|
2498
|
+
const expectedAllowlistForSkip = computeExpectedAllowlist(stage);
|
|
2499
|
+
const requireChangesDemandsEdits = stage.validation?.requireChanges === true && stage.allowEmptyResults !== true;
|
|
2500
|
+
if (!requireChangesDemandsEdits && stageHasDeterministicChecks(stage) && typeof options.workerCwd === "string" && options.workerCwd.length > 0) {
|
|
2501
|
+
try {
|
|
2502
|
+
const skipProof = await evaluateStageCompletionProof({
|
|
2503
|
+
stage,
|
|
2504
|
+
worktreePath: options.workerCwd,
|
|
2505
|
+
stdout: "",
|
|
2506
|
+
attemptChangedFiles: [],
|
|
2507
|
+
allowlist: expectedAllowlistForSkip,
|
|
2508
|
+
stageId: stage.id
|
|
2509
|
+
});
|
|
2510
|
+
const alreadySatisfied = proofEstablishesSatisfaction(skipProof);
|
|
2511
|
+
if (alreadySatisfied) {
|
|
2512
|
+
const driftContext = computeCompetitionDrift([], expectedAllowlistForSkip, effectiveMode);
|
|
2513
|
+
const coherenceMetrics = computeCoherenceMetrics(
|
|
2514
|
+
[],
|
|
2515
|
+
DEFAULT_COHERENCE_THRESHOLD,
|
|
2516
|
+
expectedAllowlistForSkip
|
|
2517
|
+
);
|
|
2518
|
+
try {
|
|
2519
|
+
await artifactStore.save(stage.id, "stage-change", "json", {
|
|
2520
|
+
stageId: stage.id,
|
|
2521
|
+
stageType: "competition",
|
|
2522
|
+
mode: effectiveMode,
|
|
2523
|
+
deliveredFiles: driftContext.deliveredFiles,
|
|
2524
|
+
expectedFiles: driftContext.expectedFiles,
|
|
2525
|
+
violations: driftContext.violations,
|
|
2526
|
+
hasDrift: driftContext.hasDrift,
|
|
2527
|
+
candidateCount: 0,
|
|
2528
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2529
|
+
skipped: true,
|
|
2530
|
+
reason: "already-satisfied"
|
|
2531
|
+
});
|
|
2532
|
+
await artifactStore.save(
|
|
2533
|
+
stage.id,
|
|
2534
|
+
"stage-change-summary",
|
|
2535
|
+
"text",
|
|
2536
|
+
`${driftContext.summary}
|
|
2537
|
+
|
|
2538
|
+
Skipped: stage already satisfied`
|
|
2539
|
+
);
|
|
2540
|
+
const rationalePacket = {
|
|
2541
|
+
winnerId: "none",
|
|
2542
|
+
scores: {},
|
|
2543
|
+
candidateAnalysis: [],
|
|
2544
|
+
selectionRationale: {
|
|
2545
|
+
primary: "Competition skipped: stage already satisfied by deterministic checks",
|
|
2546
|
+
keyFactors: [
|
|
2547
|
+
"deterministic validation checks passed",
|
|
2548
|
+
"validation.requireChanges=false",
|
|
2549
|
+
"no variants executed"
|
|
2550
|
+
],
|
|
2551
|
+
confidence: 100
|
|
2552
|
+
},
|
|
2553
|
+
rejectionReasons: {},
|
|
2554
|
+
suggestedImprovements: []
|
|
2555
|
+
};
|
|
2556
|
+
await artifactStore.save(stage.id, "judgeRationale", "json", rationalePacket);
|
|
2557
|
+
await artifactStore.save(stage.id, "winnerDiff", "diff", "");
|
|
2558
|
+
} catch {
|
|
2559
|
+
}
|
|
2560
|
+
return {
|
|
2561
|
+
stageId: stage.id,
|
|
2562
|
+
stageType: "competition",
|
|
2563
|
+
validationPassed: true,
|
|
2564
|
+
validationScore: 100,
|
|
2565
|
+
cost: 0,
|
|
2566
|
+
summary: "Competition skipped: stage already satisfied (idempotent)",
|
|
2567
|
+
durationMs: Date.now() - startTime,
|
|
2568
|
+
candidates: [],
|
|
2569
|
+
winner: null,
|
|
2570
|
+
winnerApplied: false,
|
|
2571
|
+
coherenceMetrics,
|
|
2572
|
+
markedIncomparable: false,
|
|
2573
|
+
scoringMode: "deterministic",
|
|
2574
|
+
competitionJudgeResult: {
|
|
2575
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
2576
|
+
aiJudgeAttempted: false,
|
|
2577
|
+
aiJudgeInvoked: false,
|
|
2578
|
+
aiJudgeError: void 0,
|
|
2579
|
+
judgeInvocationReason: "deterministic-shortcut",
|
|
2580
|
+
rankings: [],
|
|
2581
|
+
wasTie: false
|
|
2582
|
+
},
|
|
2583
|
+
driftContext,
|
|
2584
|
+
...skipProof.evidence ? { completionEvidence: skipProof.evidence } : {}
|
|
2585
|
+
};
|
|
2586
|
+
}
|
|
2587
|
+
} catch {
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2590
|
+
validateWorkflowJudgeModels(config, options.workerCwd);
|
|
2591
|
+
const executionIntent = resolveCompetitionExecutionIntent(stage);
|
|
2592
|
+
const variantConfigs = assignDuplicateVariantInstanceIds(
|
|
2593
|
+
config.variants.map(
|
|
2594
|
+
(spec) => variantSpecToConfig(
|
|
2595
|
+
spec,
|
|
2596
|
+
executionIntent,
|
|
2597
|
+
stage,
|
|
2598
|
+
options.providerExecutionTransport,
|
|
2599
|
+
config.executionContract
|
|
2600
|
+
)
|
|
2601
|
+
)
|
|
2602
|
+
);
|
|
2603
|
+
const admittedSpecs = admitFlywheelVariantSpecs(config.variants, variantConfigs);
|
|
2604
|
+
const allowDuplicateVariantSpecs = config.allowDuplicateVariantSpecs ?? config.allowDuplicateStrategies;
|
|
2605
|
+
if (config.allowDuplicateVariantSpecs !== void 0 && config.allowDuplicateStrategies !== void 0 && config.allowDuplicateVariantSpecs !== config.allowDuplicateStrategies) {
|
|
2606
|
+
throw new Error(
|
|
2607
|
+
`Invalid competition config for stage '${stage.id}': \`competition.allowDuplicateVariantSpecs\` and \`competition.allowDuplicateStrategies\` are both set but differ. Use \`competition.allowDuplicateVariantSpecs\` and remove the deprecated \`competition.allowDuplicateStrategies\`.`
|
|
2608
|
+
);
|
|
2609
|
+
}
|
|
2610
|
+
assertNoDuplicateVariantSpecs(variantConfigs, {
|
|
2611
|
+
allowDuplicates: allowDuplicateVariantSpecs
|
|
2612
|
+
});
|
|
2613
|
+
const taskId = generateCompetitionId();
|
|
2614
|
+
const flywheelProjectRoot = getProjectRoot();
|
|
2615
|
+
const declaredVerifyCommands = (stage.verify?.commands ?? []).filter(
|
|
2616
|
+
(command) => command.trim().length > 0
|
|
2617
|
+
);
|
|
2618
|
+
const candidateGateCommands = Array.from(
|
|
2619
|
+
/* @__PURE__ */ new Set([...config.candidateGate?.commands ?? [], ...declaredVerifyCommands])
|
|
2620
|
+
);
|
|
2621
|
+
const preserveCandidateWorktrees = config.preserveCandidates === true || Boolean(config.candidateGate) || candidateGateCommands.length > 0;
|
|
2622
|
+
const competitionOptions = {
|
|
2623
|
+
abortSignal: options.abortSignal,
|
|
2624
|
+
workerCwd: options.workerCwd,
|
|
2625
|
+
verbose: options.verbose,
|
|
2626
|
+
maxConcurrentVariants: options.maxConcurrentVariants,
|
|
2627
|
+
perVariantTimeoutMinutes: options.perVariantTimeoutMinutes,
|
|
2628
|
+
timeoutMinutes: options.timeoutMinutes,
|
|
2629
|
+
// Flywheel never consumes basic-competition synthesis (candidate gates / judge /
|
|
2630
|
+
// apply use variant results only). Always disable synthesis at this boundary so
|
|
2631
|
+
// agentic synthesis compute is not spent on discarded product. (Candidate gates
|
|
2632
|
+
// also run after Basic Competition returns, so early synthesis cannot see that
|
|
2633
|
+
// evidence and can emit misleading "0 eligible variants" warnings.)
|
|
2634
|
+
synthesize: false,
|
|
2635
|
+
deferEvaluationHandleFinalization: true,
|
|
2636
|
+
// Preserve worktrees only when needed for post-processing (candidate gate commands,
|
|
2637
|
+
// declared verify commands, coverage parsing).
|
|
2638
|
+
// Winner application in flywheel-executor prefers `worktreeDiff` and does not require retained worktrees.
|
|
2639
|
+
preserveWorktrees: preserveCandidateWorktrees,
|
|
2640
|
+
executionContext: createExecutionContext({
|
|
2641
|
+
repoRoot: flywheelProjectRoot,
|
|
2642
|
+
buildDir: path.join(flywheelProjectRoot, ".build-project"),
|
|
2643
|
+
taskId,
|
|
2644
|
+
envOverrides: preserveCandidateWorktrees ? {
|
|
2645
|
+
BUILD_PRESERVE_WORKTREES: "1",
|
|
2646
|
+
BUILD_RETAIN_BASIC_WORKTREE: "1"
|
|
2647
|
+
} : {}
|
|
2648
|
+
})
|
|
2649
|
+
};
|
|
2650
|
+
let basicResult;
|
|
2651
|
+
try {
|
|
2652
|
+
basicResult = await runBasicCompetition(
|
|
2653
|
+
taskId,
|
|
2654
|
+
resolvedPrompt,
|
|
2655
|
+
variantConfigs,
|
|
2656
|
+
competitionOptions
|
|
2657
|
+
);
|
|
2658
|
+
} catch (error) {
|
|
2659
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
2660
|
+
await finalizeEvaluationHandleAsync(
|
|
2661
|
+
taskId,
|
|
2662
|
+
createEvaluationHandleFinalState({
|
|
2663
|
+
outcome: "error",
|
|
2664
|
+
winner: null,
|
|
2665
|
+
errorMessage: errorMsg
|
|
2666
|
+
})
|
|
2667
|
+
);
|
|
2668
|
+
const result = {
|
|
2669
|
+
stageId: stage.id,
|
|
2670
|
+
stageType: "competition",
|
|
2671
|
+
validationPassed: false,
|
|
2672
|
+
cost: 0,
|
|
2673
|
+
candidates: [],
|
|
2674
|
+
winner: null,
|
|
2675
|
+
summary: `Competition execution failed: ${errorMsg}`,
|
|
2676
|
+
durationMs: Date.now() - startTime,
|
|
2677
|
+
competitionJudgeResult: {
|
|
2678
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
2679
|
+
aiJudgeAttempted: false,
|
|
2680
|
+
aiJudgeInvoked: false,
|
|
2681
|
+
aiJudgeError: errorMsg,
|
|
2682
|
+
judgeInvocationReason: "error",
|
|
2683
|
+
rankings: [],
|
|
2684
|
+
wasTie: false,
|
|
2685
|
+
noWinnerReason: "judge-error"
|
|
2686
|
+
}
|
|
2687
|
+
};
|
|
2688
|
+
return result;
|
|
2689
|
+
}
|
|
2690
|
+
let deferredHandleFinalized = false;
|
|
2691
|
+
const finalizeDeferredHandle = async (state) => {
|
|
2692
|
+
let finalizeResult = await finalizeEvaluationHandleAsync(taskId, state);
|
|
2693
|
+
if (finalizeResult.kind === "not_terminal") {
|
|
2694
|
+
await Promise.all(
|
|
2695
|
+
basicResult.variants.map(
|
|
2696
|
+
(variant) => updateEvaluationHandleWithResultAsync(taskId, variant)
|
|
2697
|
+
)
|
|
2698
|
+
);
|
|
2699
|
+
finalizeResult = await finalizeEvaluationHandleAsync(taskId, state);
|
|
2700
|
+
}
|
|
2701
|
+
if (finalizeResult.kind !== "absent" && !isDurableHandleCompletion(finalizeResult, state.status)) {
|
|
2702
|
+
throw new Error(
|
|
2703
|
+
`Evaluation handle did not reach durable ${state.status} state (kind=${finalizeResult.kind})`
|
|
2704
|
+
);
|
|
2705
|
+
}
|
|
2706
|
+
deferredHandleFinalized = true;
|
|
2707
|
+
};
|
|
2708
|
+
let observedCandidates;
|
|
2709
|
+
let aiJudgeAttempted = false;
|
|
2710
|
+
let aiJudgeInvoked = false;
|
|
2711
|
+
let tieBreakAiJudgeInvoked = false;
|
|
2712
|
+
try {
|
|
2713
|
+
const basicInterruption = resolveBasicCompetitionInterruption(basicResult);
|
|
2714
|
+
let bindingError;
|
|
2715
|
+
try {
|
|
2716
|
+
bindFlywheelVariantResults(admittedSpecs, basicResult.variants);
|
|
2717
|
+
} catch (error) {
|
|
2718
|
+
observedCandidates = basicResult.variants.map(
|
|
2719
|
+
(variant) => variantResultToCandidate(observedVariantSpec(variant), variant)
|
|
2720
|
+
);
|
|
2721
|
+
if (!basicInterruption) throw error;
|
|
2722
|
+
bindingError = error instanceof Error ? error.message : String(error);
|
|
2723
|
+
}
|
|
2724
|
+
if (basicInterruption) {
|
|
2725
|
+
const interruptedCandidates = observedCandidates ?? admittedSpecs.map(
|
|
2726
|
+
(spec) => variantResultToCandidate(spec, matchVariantResult(spec, basicResult.variants))
|
|
2727
|
+
);
|
|
2728
|
+
observedCandidates = interruptedCandidates;
|
|
2729
|
+
const { cost, costUnavailableReason: costUnavailableReason2 } = resolveCompetitionCostAggregate(interruptedCandidates);
|
|
2730
|
+
const timedOut = basicInterruption.kind === "timed_out";
|
|
2731
|
+
await finalizeDeferredHandle(
|
|
2732
|
+
timedOut ? createEvaluationHandleFinalState({
|
|
2733
|
+
outcome: "timed_out",
|
|
2734
|
+
winner: null,
|
|
2735
|
+
errorMessage: `Competition timed out after ${basicInterruption.timeout.timeoutMs}ms`
|
|
2736
|
+
}) : createEvaluationHandleFinalState({ outcome: "no_winner", winner: null })
|
|
2737
|
+
);
|
|
2738
|
+
await cleanupSynthesisInputPatches(basicResult.variants, {
|
|
2739
|
+
expectedRoot: resolveSynthesisInputPatchDir(
|
|
2740
|
+
competitionOptions.executionContext.buildDir,
|
|
2741
|
+
taskId
|
|
2742
|
+
)
|
|
2743
|
+
});
|
|
2744
|
+
return {
|
|
2745
|
+
stageId: stage.id,
|
|
2746
|
+
stageType: "competition",
|
|
2747
|
+
validationPassed: false,
|
|
2748
|
+
cost,
|
|
2749
|
+
costUnavailableReason: costUnavailableReason2,
|
|
2750
|
+
candidates: interruptedCandidates,
|
|
2751
|
+
warnings: bindingError ? [bindingError] : void 0,
|
|
2752
|
+
winner: null,
|
|
2753
|
+
summary: timedOut ? "Competition timed out; no winner selected" : `Competition canceled by ${basicInterruption.kind === "canceled" ? basicInterruption.signal : "unknown signal"}; no winner selected`,
|
|
2754
|
+
durationMs: Date.now() - startTime,
|
|
2755
|
+
competitionJudgeResult: {
|
|
2756
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
2757
|
+
aiJudgeAttempted: false,
|
|
2758
|
+
aiJudgeInvoked: false,
|
|
2759
|
+
judgeInvocationReason: timedOut ? "error" : "canceled",
|
|
2760
|
+
rankings: [],
|
|
2761
|
+
wasTie: false,
|
|
2762
|
+
noWinnerReason: timedOut ? "judge-error" : "canceled"
|
|
2763
|
+
}
|
|
2764
|
+
};
|
|
2765
|
+
}
|
|
2766
|
+
let judgedWinner = null;
|
|
2767
|
+
let aiJudgeError;
|
|
2768
|
+
let latestJudgeEscalation;
|
|
2769
|
+
let latestMainJudgeResult;
|
|
2770
|
+
let latestTiebreakJudgeResult;
|
|
2771
|
+
let matrixDefaultWinnerSource = "unknown";
|
|
2772
|
+
let eligibleVariantResultsForMatrix = [];
|
|
2773
|
+
let matrixRan = false;
|
|
2774
|
+
let matrixProfileAgreement;
|
|
2775
|
+
let effectiveMatrixAuthority;
|
|
2776
|
+
const matrixAuthorityWarnings = [];
|
|
2777
|
+
const matrixAuthorityNonSelectionReasons = [];
|
|
2778
|
+
const candidates = await Promise.all(
|
|
2779
|
+
admittedSpecs.map(async (spec) => {
|
|
2780
|
+
const matchingResult = matchVariantResult(spec, basicResult.variants);
|
|
2781
|
+
const candidate = variantResultToCandidate(spec, matchingResult);
|
|
2782
|
+
if (candidate.success) return candidate;
|
|
2783
|
+
if (!isNoChangesDetectedError(candidate.error)) return candidate;
|
|
2784
|
+
if (requireChangesDemandsEdits) {
|
|
2785
|
+
return candidate;
|
|
2786
|
+
}
|
|
2787
|
+
if (!stageHasDeterministicChecks(stage)) return candidate;
|
|
2788
|
+
if (!candidate.worktreePath) return candidate;
|
|
2789
|
+
const stdout = getStringAtPath(matchingResult, ["result", "stdout"]) ?? "";
|
|
2790
|
+
const canAcceptNoop = await validateWorktreeAgainstStageChecks(
|
|
2791
|
+
stage,
|
|
2792
|
+
candidate.worktreePath,
|
|
2793
|
+
stdout,
|
|
2794
|
+
{
|
|
2795
|
+
attemptChangedFiles: candidate.changedFiles ?? [],
|
|
2796
|
+
allowlist: expectedAllowlistForSkip
|
|
2797
|
+
}
|
|
2798
|
+
);
|
|
2799
|
+
if (!canAcceptNoop) return candidate;
|
|
2800
|
+
return {
|
|
2801
|
+
...candidate,
|
|
2802
|
+
success: true,
|
|
2803
|
+
error: void 0,
|
|
2804
|
+
score: candidate.score ?? 0
|
|
2805
|
+
};
|
|
2806
|
+
})
|
|
2807
|
+
);
|
|
2808
|
+
observedCandidates = candidates;
|
|
2809
|
+
const finishedExecutionKeys = new Set(
|
|
2810
|
+
candidates.filter((c) => c.success).map((c) => variantIdForSpec(c.variant))
|
|
2811
|
+
);
|
|
2812
|
+
if (stage.validation?.requireChanges === true && stage.allowEmptyResults !== true) {
|
|
2813
|
+
for (let idx = 0; idx < candidates.length; idx++) {
|
|
2814
|
+
const candidate = candidates[idx];
|
|
2815
|
+
if (!candidate?.success || !candidateHasConcreteZeroChanges(candidate)) continue;
|
|
2816
|
+
candidates[idx] = {
|
|
2817
|
+
...candidate,
|
|
2818
|
+
success: false,
|
|
2819
|
+
error: "Candidate produced no applicable changes but validation.requireChanges is enabled"
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
await hydrateCandidateCostsFromTelemetry(taskId, candidates, stage.id, options.verbose);
|
|
2824
|
+
const verificationPolicy = config.verificationPolicy ?? "findings";
|
|
2825
|
+
const candidatesWithTestFindings = [];
|
|
2826
|
+
for (let idx = 0; idx < candidates.length; idx++) {
|
|
2827
|
+
const candidate = candidates[idx];
|
|
2828
|
+
if (!candidate) continue;
|
|
2829
|
+
const trustedTestStatus = resolveTrustedCandidateTestStatus(candidate);
|
|
2830
|
+
if (candidate.success && candidate.testsExecuted && trustedTestStatus && trustedTestStatus !== "passed" && trustedTestStatus !== "not_run") {
|
|
2831
|
+
const variantId = formatCompetitionCandidateVariantDisplay(candidate);
|
|
2832
|
+
const testScope = candidate.testsExecuted.scope ?? "unknown";
|
|
2833
|
+
const exitCode = candidate.testsExecuted.exitCode ?? 1;
|
|
2834
|
+
const testLabel = `touched tests (scope: ${testScope})`;
|
|
2835
|
+
const verificationUnavailable = candidate.testStatus === "unavailable";
|
|
2836
|
+
const finding = {
|
|
2837
|
+
kind: "test",
|
|
2838
|
+
command: testLabel,
|
|
2839
|
+
exitCode,
|
|
2840
|
+
severity: verificationUnavailable ? "warning" : "error",
|
|
2841
|
+
recommendedNextStep: verificationUnavailable ? `Install or enable test tooling for scope: ${testScope}` : `Fix failing tests in scope: ${testScope}`
|
|
2842
|
+
};
|
|
2843
|
+
candidate.verification = {
|
|
2844
|
+
passed: false,
|
|
2845
|
+
findings: [finding]
|
|
2846
|
+
};
|
|
2847
|
+
if (verificationPolicy === "blocking") {
|
|
2848
|
+
candidatesWithTestFindings.push(
|
|
2849
|
+
`${variantId} (${verificationUnavailable ? "verification unavailable" : "disqualified"})`
|
|
2850
|
+
);
|
|
2851
|
+
candidates[idx] = {
|
|
2852
|
+
...candidate,
|
|
2853
|
+
success: false,
|
|
2854
|
+
error: verificationUnavailable ? `Candidate gate unavailable: touched tests unavailable (scope: ${testScope})` : `Candidate gate failed: touched tests ${candidate.testStatus} (scope: ${testScope})`
|
|
2855
|
+
};
|
|
2856
|
+
} else {
|
|
2857
|
+
candidatesWithTestFindings.push(`${variantId} (findings recorded)`);
|
|
2858
|
+
}
|
|
2859
|
+
}
|
|
2860
|
+
}
|
|
2861
|
+
if (candidatesWithTestFindings.length > 0 && options.verbose) {
|
|
2862
|
+
const mode = verificationPolicy === "blocking" ? "disqualified" : "recorded findings for";
|
|
2863
|
+
console.log(
|
|
2864
|
+
chalk.yellow(
|
|
2865
|
+
` ${candidatesWithTestFindings.length} candidate(s) ${mode}: ${candidatesWithTestFindings.join(", ")}`
|
|
2866
|
+
)
|
|
2867
|
+
);
|
|
2868
|
+
}
|
|
2869
|
+
const candidateGatePassedVariantIds = /* @__PURE__ */ new Set();
|
|
2870
|
+
if (candidateGateCommands.length > 0) {
|
|
2871
|
+
const gate = { ...config.candidateGate ?? {}, commands: candidateGateCommands };
|
|
2872
|
+
const gateResults = [];
|
|
2873
|
+
const gatedCandidates = await Promise.all(
|
|
2874
|
+
candidates.map(async (candidate) => {
|
|
2875
|
+
if (!candidate.success) {
|
|
2876
|
+
gateResults.push({
|
|
2877
|
+
provider: candidate.variant.provider,
|
|
2878
|
+
model: candidate.variant.model,
|
|
2879
|
+
worktreePath: candidate.worktreePath,
|
|
2880
|
+
ran: false,
|
|
2881
|
+
passed: false,
|
|
2882
|
+
error: "Candidate did not execute successfully (gate not run)",
|
|
2883
|
+
commandResults: []
|
|
2884
|
+
});
|
|
2885
|
+
return candidate;
|
|
2886
|
+
}
|
|
2887
|
+
if (!candidate.worktreePath) {
|
|
2888
|
+
const msg = "Candidate gate requires a retained worktreePath but none was provided";
|
|
2889
|
+
gateResults.push({
|
|
2890
|
+
provider: candidate.variant.provider,
|
|
2891
|
+
model: candidate.variant.model,
|
|
2892
|
+
worktreePath: candidate.worktreePath,
|
|
2893
|
+
ran: false,
|
|
2894
|
+
passed: false,
|
|
2895
|
+
error: msg,
|
|
2896
|
+
commandResults: []
|
|
2897
|
+
});
|
|
2898
|
+
return { ...candidate, success: false, error: `Candidate gate failed: ${msg}` };
|
|
2899
|
+
}
|
|
2900
|
+
const findings = [
|
|
2901
|
+
...candidate.verification?.findings ?? []
|
|
2902
|
+
];
|
|
2903
|
+
const renderedCommands = gate.commands.map(
|
|
2904
|
+
(command) => renderCandidateGateCommand(command, candidate)
|
|
2905
|
+
);
|
|
2906
|
+
const verifyResult = await runVerifyCommands({
|
|
2907
|
+
cwd: candidate.worktreePath,
|
|
2908
|
+
commands: renderedCommands,
|
|
2909
|
+
commandProvenance: "candidate-gate-config",
|
|
2910
|
+
timeoutMs: CANDIDATE_GATE_COMMAND_TIMEOUT_MS
|
|
2911
|
+
});
|
|
2912
|
+
const commandResults = verifyResult.results;
|
|
2913
|
+
const gatePassed = verifyResult.passed;
|
|
2914
|
+
if (gatePassed) {
|
|
2915
|
+
candidateGatePassedVariantIds.add(variantIdForSpec(candidate.variant));
|
|
2916
|
+
}
|
|
2917
|
+
let gateError;
|
|
2918
|
+
const failedCommand = commandResults.find((result2) => result2.exitCode !== 0);
|
|
2919
|
+
if (failedCommand) {
|
|
2920
|
+
gateError = `Verify command failed (exit ${failedCommand.exitCode}): ${failedCommand.command}`;
|
|
2921
|
+
findings.push({
|
|
2922
|
+
kind: "gate-command",
|
|
2923
|
+
command: failedCommand.command,
|
|
2924
|
+
exitCode: failedCommand.exitCode,
|
|
2925
|
+
outputTail: failedCommand.outputTail ?? failedCommand.stderrTail ?? failedCommand.stdoutTail,
|
|
2926
|
+
severity: "error",
|
|
2927
|
+
recommendedNextStep: `Fix gate command failure: ${failedCommand.command}`
|
|
2928
|
+
});
|
|
2929
|
+
}
|
|
2930
|
+
gateResults.push({
|
|
2931
|
+
provider: candidate.variant.provider,
|
|
2932
|
+
model: candidate.variant.model,
|
|
2933
|
+
worktreePath: candidate.worktreePath,
|
|
2934
|
+
ran: true,
|
|
2935
|
+
passed: gatePassed,
|
|
2936
|
+
error: gateError,
|
|
2937
|
+
commandResults
|
|
2938
|
+
});
|
|
2939
|
+
if (!gatePassed) {
|
|
2940
|
+
candidate.verification = {
|
|
2941
|
+
passed: false,
|
|
2942
|
+
findings
|
|
2943
|
+
};
|
|
2944
|
+
if (verificationPolicy === "blocking") {
|
|
2945
|
+
return { ...candidate, success: false, error: `Candidate gate failed: ${gateError}` };
|
|
2946
|
+
}
|
|
2947
|
+
}
|
|
2948
|
+
return candidate;
|
|
2949
|
+
})
|
|
2950
|
+
);
|
|
2951
|
+
candidates.splice(0, candidates.length, ...gatedCandidates);
|
|
2952
|
+
try {
|
|
2953
|
+
await artifactStore.save(stage.id, "candidate-gates", "json", {
|
|
2954
|
+
taskId,
|
|
2955
|
+
stageId: stage.id,
|
|
2956
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2957
|
+
commands: gate.commands,
|
|
2958
|
+
candidateCount: candidates.length,
|
|
2959
|
+
passedCount: gateResults.filter((r) => r.passed).length,
|
|
2960
|
+
results: gateResults
|
|
2961
|
+
});
|
|
2962
|
+
} catch (error) {
|
|
2963
|
+
if (options.verbose) {
|
|
2964
|
+
console.warn(
|
|
2965
|
+
chalk.yellow(` Warning: Failed to persist candidate gate artifacts: ${error}`)
|
|
2966
|
+
);
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
const coverageConfig = config.candidateGate?.coverage;
|
|
2971
|
+
let usedCoverageScoring = false;
|
|
2972
|
+
let coverageDataAvailable = false;
|
|
2973
|
+
if (coverageConfig && Array.isArray(coverageConfig.targets) && coverageConfig.targets.length > 0) {
|
|
2974
|
+
const summaryPath = coverageConfig.summaryPath ?? "coverage/coverage-summary.json";
|
|
2975
|
+
const threshold = coverageConfig.threshold;
|
|
2976
|
+
const coverageMode = coverageConfig.mode ?? "absolute";
|
|
2977
|
+
const minDeltaPct = coverageConfig.minDeltaPct ?? 15;
|
|
2978
|
+
const largeFileLineThreshold = coverageConfig.largeFileLineThreshold ?? 300;
|
|
2979
|
+
const baselineSummary = typeof options.workerCwd === "string" && options.workerCwd.length > 0 ? await loadCoverageSummaryJson(options.workerCwd, summaryPath) : null;
|
|
2980
|
+
const baselineStats = computeCoverageStatsForTargets(baselineSummary, coverageConfig.targets);
|
|
2981
|
+
const baselineFromPrompt = parseBaselineLinesPctFromPrompt(resolvedPrompt);
|
|
2982
|
+
const effectiveBaselineAvgPct = baselineStats.avgPct ?? baselineFromPrompt;
|
|
2983
|
+
const perCandidateCoverage = [];
|
|
2984
|
+
for (let idx = 0; idx < candidates.length; idx++) {
|
|
2985
|
+
const candidate = candidates[idx];
|
|
2986
|
+
if (!candidate) continue;
|
|
2987
|
+
const worktreePath = candidate.worktreePath;
|
|
2988
|
+
let avgPct = null;
|
|
2989
|
+
let minPct = null;
|
|
2990
|
+
let maxTargetLines = null;
|
|
2991
|
+
let byTarget = {};
|
|
2992
|
+
let missingTargets = [];
|
|
2993
|
+
let disqualified = false;
|
|
2994
|
+
let disqualifyReason;
|
|
2995
|
+
let deltaAvgPct = null;
|
|
2996
|
+
const baselineAvgPct = effectiveBaselineAvgPct;
|
|
2997
|
+
if (candidate.success) {
|
|
2998
|
+
if (!worktreePath) {
|
|
2999
|
+
byTarget = Object.fromEntries(coverageConfig.targets.map((t) => [t, null]));
|
|
3000
|
+
missingTargets = [...coverageConfig.targets];
|
|
3001
|
+
const hasGateConfig = typeof threshold === "number" || coverageMode === "delta" || coverageMode === "auto";
|
|
3002
|
+
if (hasGateConfig) {
|
|
3003
|
+
disqualified = true;
|
|
3004
|
+
disqualifyReason = "Missing worktreePath required for coverage gate evaluation";
|
|
3005
|
+
}
|
|
3006
|
+
} else {
|
|
3007
|
+
const candidateSummary = await loadCoverageSummaryJson(worktreePath, summaryPath);
|
|
3008
|
+
const stats = computeCoverageStatsForTargets(candidateSummary, coverageConfig.targets);
|
|
3009
|
+
avgPct = stats.avgPct;
|
|
3010
|
+
minPct = stats.minPct;
|
|
3011
|
+
maxTargetLines = stats.maxTargetLines;
|
|
3012
|
+
byTarget = stats.byTarget;
|
|
3013
|
+
missingTargets = stats.missingTargets;
|
|
3014
|
+
deltaAvgPct = typeof avgPct === "number" ? Math.round(
|
|
3015
|
+
(avgPct - (typeof baselineAvgPct === "number" ? baselineAvgPct : 0)) * 100
|
|
3016
|
+
) / 100 : null;
|
|
3017
|
+
let effectiveMode2 = coverageMode;
|
|
3018
|
+
if (coverageMode === "auto" && typeof maxTargetLines === "number") {
|
|
3019
|
+
effectiveMode2 = resolveAutoCoverageMode({
|
|
3020
|
+
maxTargetLines,
|
|
3021
|
+
largeFileLineThreshold,
|
|
3022
|
+
baselineAvgPct,
|
|
3023
|
+
threshold,
|
|
3024
|
+
minDeltaPct
|
|
3025
|
+
});
|
|
3026
|
+
}
|
|
3027
|
+
if (effectiveMode2 === "absolute" && typeof threshold === "number") {
|
|
3028
|
+
if (missingTargets.length > 0) {
|
|
3029
|
+
disqualified = true;
|
|
3030
|
+
disqualifyReason = `Missing coverage for targets: ${missingTargets.join(", ")}`;
|
|
3031
|
+
} else if (typeof minPct === "number" && minPct < threshold) {
|
|
3032
|
+
disqualified = true;
|
|
3033
|
+
disqualifyReason = `Coverage below threshold (${minPct.toFixed(1)}% < ${threshold}%)`;
|
|
3034
|
+
} else if (minPct === null) {
|
|
3035
|
+
disqualified = true;
|
|
3036
|
+
disqualifyReason = "Coverage data missing (unable to evaluate threshold)";
|
|
3037
|
+
}
|
|
3038
|
+
} else if (effectiveMode2 === "delta") {
|
|
3039
|
+
if (missingTargets.length > 0) {
|
|
3040
|
+
disqualified = true;
|
|
3041
|
+
disqualifyReason = `Missing coverage for targets: ${missingTargets.join(", ")}`;
|
|
3042
|
+
} else if (typeof deltaAvgPct === "number" && deltaAvgPct < minDeltaPct) {
|
|
3043
|
+
disqualified = true;
|
|
3044
|
+
disqualifyReason = `Coverage improvement below minimum (${deltaAvgPct.toFixed(1)}pp < ${minDeltaPct}pp)`;
|
|
3045
|
+
} else if (deltaAvgPct === null) {
|
|
3046
|
+
disqualified = true;
|
|
3047
|
+
disqualifyReason = "Coverage data missing (unable to evaluate delta)";
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
let effectiveCandidate = candidate;
|
|
3053
|
+
if (effectiveCandidate.success && disqualified) {
|
|
3054
|
+
effectiveCandidate = {
|
|
3055
|
+
...effectiveCandidate,
|
|
3056
|
+
success: false,
|
|
3057
|
+
error: `Candidate gate failed: ${disqualifyReason ?? "coverage threshold"}`
|
|
3058
|
+
};
|
|
3059
|
+
candidates[idx] = effectiveCandidate;
|
|
3060
|
+
}
|
|
3061
|
+
if (effectiveCandidate.success && typeof avgPct === "number") {
|
|
3062
|
+
effectiveCandidate = { ...effectiveCandidate, score: avgPct };
|
|
3063
|
+
candidates[idx] = effectiveCandidate;
|
|
3064
|
+
coverageDataAvailable = true;
|
|
3065
|
+
}
|
|
3066
|
+
perCandidateCoverage.push({
|
|
3067
|
+
provider: effectiveCandidate.variant.provider,
|
|
3068
|
+
model: effectiveCandidate.variant.model,
|
|
3069
|
+
worktreePath,
|
|
3070
|
+
success: effectiveCandidate.success,
|
|
3071
|
+
avgPct,
|
|
3072
|
+
minPct,
|
|
3073
|
+
maxTargetLines,
|
|
3074
|
+
baselineAvgPct,
|
|
3075
|
+
deltaAvgPct,
|
|
3076
|
+
byTarget,
|
|
3077
|
+
missingTargets,
|
|
3078
|
+
disqualified: disqualified || void 0,
|
|
3079
|
+
disqualifyReason
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
try {
|
|
3083
|
+
await artifactStore.save(stage.id, "candidate-coverage", "json", {
|
|
3084
|
+
taskId,
|
|
3085
|
+
stageId: stage.id,
|
|
3086
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3087
|
+
summaryPath,
|
|
3088
|
+
gateConfig: {
|
|
3089
|
+
mode: coverageMode,
|
|
3090
|
+
threshold,
|
|
3091
|
+
minDeltaPct,
|
|
3092
|
+
largeFileLineThreshold
|
|
3093
|
+
},
|
|
3094
|
+
targets: coverageConfig.targets,
|
|
3095
|
+
baseline: {
|
|
3096
|
+
avgPct: effectiveBaselineAvgPct,
|
|
3097
|
+
minPct: baselineStats.minPct ?? baselineFromPrompt,
|
|
3098
|
+
totalLines: baselineStats.totalLines,
|
|
3099
|
+
maxTargetLines: baselineStats.maxTargetLines,
|
|
3100
|
+
byTarget: baselineStats.byTarget,
|
|
3101
|
+
missingTargets: baselineStats.missingTargets
|
|
3102
|
+
},
|
|
3103
|
+
candidates: perCandidateCoverage
|
|
3104
|
+
});
|
|
3105
|
+
} catch (error) {
|
|
3106
|
+
if (options.verbose) {
|
|
3107
|
+
console.warn(
|
|
3108
|
+
chalk.yellow(` Warning: Failed to persist candidate coverage artifacts: ${error}`)
|
|
3109
|
+
);
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
usedCoverageScoring = coverageDataAvailable;
|
|
3113
|
+
}
|
|
3114
|
+
recordDeterministicTestSignalObservation(candidates, options.verbose);
|
|
3115
|
+
const expectedAllowlist = computeExpectedAllowlist(stage);
|
|
3116
|
+
const { cost: totalCost, costUnavailableReason } = resolveCompetitionCostAggregate(candidates);
|
|
3117
|
+
const finishedCandidates = candidates.filter(
|
|
3118
|
+
(c) => finishedExecutionKeys.has(variantIdForSpec(c.variant))
|
|
3119
|
+
);
|
|
3120
|
+
const shouldInvokeAiJudgeForFinished = finishedCandidates.length >= 2 && config.judgeConfig?.disableAiJudge !== true;
|
|
3121
|
+
const allCandidatesDisqualified = candidates.every((c) => !c.success);
|
|
3122
|
+
if (allCandidatesDisqualified && verificationPolicy === "blocking" && !shouldInvokeAiJudgeForFinished) {
|
|
3123
|
+
let disqualificationSummary = "Competition failed: All candidates disqualified by verification gates";
|
|
3124
|
+
const testGateUnavailable = candidates.filter(
|
|
3125
|
+
(c) => c.error?.includes("touched tests unavailable")
|
|
3126
|
+
);
|
|
3127
|
+
const testGateFailures = candidates.filter(
|
|
3128
|
+
(c) => !c.error?.includes("touched tests unavailable") && (c.error?.includes("touched tests") || c.error?.includes("Candidate gate failed"))
|
|
3129
|
+
);
|
|
3130
|
+
const coverageGateFailures = candidates.filter((c) => c.error?.includes("coverage"));
|
|
3131
|
+
const verifyFailures = candidates.filter(
|
|
3132
|
+
(c) => c.error?.includes("Verify stage failed") || c.error?.includes("verify.commands")
|
|
3133
|
+
);
|
|
3134
|
+
const requireChangesNoops = candidates.filter(
|
|
3135
|
+
(c) => c.error?.includes("validation.requireChanges is enabled")
|
|
3136
|
+
);
|
|
3137
|
+
const rootCauses = [];
|
|
3138
|
+
if (testGateUnavailable.length > 0) {
|
|
3139
|
+
rootCauses.push(
|
|
3140
|
+
`${testGateUnavailable.length} candidate(s) had unavailable deterministic test verification (touched tests unavailable)`
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
if (testGateFailures.length > 0) {
|
|
3144
|
+
rootCauses.push(
|
|
3145
|
+
`${testGateFailures.length} candidate(s) failed deterministic test gate (touched tests failed)`
|
|
3146
|
+
);
|
|
3147
|
+
}
|
|
3148
|
+
if (coverageGateFailures.length > 0) {
|
|
3149
|
+
rootCauses.push(
|
|
3150
|
+
`${coverageGateFailures.length} candidate(s) failed coverage gate (below threshold)`
|
|
3151
|
+
);
|
|
3152
|
+
}
|
|
3153
|
+
if (verifyFailures.length > 0) {
|
|
3154
|
+
rootCauses.push(`${verifyFailures.length} candidate(s) failed verify stage commands`);
|
|
3155
|
+
}
|
|
3156
|
+
if (requireChangesNoops.length > 0) {
|
|
3157
|
+
rootCauses.push(
|
|
3158
|
+
`${requireChangesNoops.length} candidate(s) produced no applicable changes while validation.requireChanges is enabled`
|
|
3159
|
+
);
|
|
3160
|
+
}
|
|
3161
|
+
if (rootCauses.length > 0) {
|
|
3162
|
+
disqualificationSummary += `. Root cause: ${rootCauses.join("; ")}`;
|
|
3163
|
+
}
|
|
3164
|
+
const candidateErrors = candidates.filter((c) => c.error).map((c) => {
|
|
3165
|
+
const variantId = formatCompetitionCandidateVariantDisplay(c);
|
|
3166
|
+
return `${variantId} - ${c.error}`;
|
|
3167
|
+
}).join("; ");
|
|
3168
|
+
if (candidateErrors) {
|
|
3169
|
+
disqualificationSummary += `. Candidate details: ${candidateErrors}`;
|
|
3170
|
+
}
|
|
3171
|
+
const result2 = {
|
|
3172
|
+
stageId: stage.id,
|
|
3173
|
+
stageType: "competition",
|
|
3174
|
+
validationPassed: false,
|
|
3175
|
+
validationScore: 0,
|
|
3176
|
+
cost: totalCost,
|
|
3177
|
+
costUnavailableReason,
|
|
3178
|
+
summary: disqualificationSummary,
|
|
3179
|
+
durationMs: Date.now() - startTime,
|
|
3180
|
+
candidates,
|
|
3181
|
+
winner: null,
|
|
3182
|
+
winnerApplied: false,
|
|
3183
|
+
scoringMode: "deterministic",
|
|
3184
|
+
competitionJudgeResult: {
|
|
3185
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
3186
|
+
aiJudgeAttempted: false,
|
|
3187
|
+
aiJudgeInvoked: false,
|
|
3188
|
+
aiJudgeError: void 0,
|
|
3189
|
+
judgeInvocationReason: "no-successful-candidates",
|
|
3190
|
+
rankings: [],
|
|
3191
|
+
wasTie: false,
|
|
3192
|
+
noWinnerReason: "all-failed"
|
|
3193
|
+
}
|
|
3194
|
+
};
|
|
3195
|
+
try {
|
|
3196
|
+
await artifactStore.save(stage.id, "candidates", "json", {
|
|
3197
|
+
taskId,
|
|
3198
|
+
variantCount: candidates.length,
|
|
3199
|
+
successCount: 0,
|
|
3200
|
+
candidates: candidates.map((c) => ({
|
|
3201
|
+
provider: c.variant.provider,
|
|
3202
|
+
model: c.variant.model,
|
|
3203
|
+
success: c.success,
|
|
3204
|
+
error: c.error,
|
|
3205
|
+
cost: c.cost,
|
|
3206
|
+
costUnavailableReason: c.costUnavailableReason
|
|
3207
|
+
})),
|
|
3208
|
+
winner: null,
|
|
3209
|
+
rankings: [],
|
|
3210
|
+
allDisqualifiedByTestGate: true,
|
|
3211
|
+
aiJudgeInvoked: result2.competitionJudgeResult?.aiJudgeInvoked ?? false,
|
|
3212
|
+
scoringMode: result2.scoringMode ?? "deterministic",
|
|
3213
|
+
judgeInvocationReason: result2.competitionJudgeResult?.judgeInvocationReason ?? "no-successful-candidates"
|
|
3214
|
+
});
|
|
3215
|
+
} catch (error) {
|
|
3216
|
+
if (options.verbose) {
|
|
3217
|
+
console.warn(chalk.yellow(` Warning: Failed to persist failure artifacts: ${error}`));
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3220
|
+
await finalizeDeferredHandle(
|
|
3221
|
+
createEvaluationHandleFinalState({ outcome: "all_failed", winner: null })
|
|
3222
|
+
);
|
|
3223
|
+
await cleanupSynthesisInputPatches(basicResult.variants, {
|
|
3224
|
+
expectedRoot: resolveSynthesisInputPatchDir(
|
|
3225
|
+
competitionOptions.executionContext.buildDir,
|
|
3226
|
+
taskId
|
|
3227
|
+
)
|
|
3228
|
+
});
|
|
3229
|
+
return result2;
|
|
3230
|
+
}
|
|
3231
|
+
const shouldInvokeAiJudge = shouldInvokeAiJudgeForFinished;
|
|
3232
|
+
if (shouldInvokeAiJudge) {
|
|
3233
|
+
if (options.verbose) {
|
|
3234
|
+
console.log(chalk.blue(" Invoking AI judge for competition evaluation..."));
|
|
3235
|
+
}
|
|
3236
|
+
const judgeVisibleVariantResults = buildJudgeVisibleVariantResults(
|
|
3237
|
+
finishedCandidates,
|
|
3238
|
+
basicResult.variants
|
|
3239
|
+
);
|
|
3240
|
+
if (judgeVisibleVariantResults.length < 2) {
|
|
3241
|
+
if (options.verbose) {
|
|
3242
|
+
console.warn(
|
|
3243
|
+
chalk.yellow(
|
|
3244
|
+
` Skipping AI judge: expected \u22652 finished variants but resolved ${judgeVisibleVariantResults.length}`
|
|
3245
|
+
)
|
|
3246
|
+
);
|
|
3247
|
+
}
|
|
3248
|
+
} else {
|
|
3249
|
+
aiJudgeAttempted = true;
|
|
3250
|
+
try {
|
|
3251
|
+
const { aiJudgeRun, aiJudgePrimary } = deriveAiJudgeInvocationArgs(config.judgeConfig);
|
|
3252
|
+
eligibleVariantResultsForMatrix = judgeVisibleVariantResults;
|
|
3253
|
+
matrixDefaultWinnerSource = aiJudgePrimary;
|
|
3254
|
+
const judgeResult = await selectWinnerWithJudge(
|
|
3255
|
+
resolvedPrompt,
|
|
3256
|
+
judgeVisibleVariantResults,
|
|
3257
|
+
{
|
|
3258
|
+
taskId,
|
|
3259
|
+
aiJudgeTelemetry: true,
|
|
3260
|
+
aiJudgeRun,
|
|
3261
|
+
aiJudgePrimary,
|
|
3262
|
+
anonymizeJudge: config.judgeConfig?.anonymizeJudge,
|
|
3263
|
+
judgeAllowUnsafeModel: config.judgeConfig?.allowUnsafeModel === true,
|
|
3264
|
+
// The flywheel owns the explicit Matrix escalation block below;
|
|
3265
|
+
// opt out of the coordinator's automatic Matrix run to avoid a
|
|
3266
|
+
// duplicate Matrix call.
|
|
3267
|
+
autoMatrix: false
|
|
3268
|
+
}
|
|
3269
|
+
);
|
|
3270
|
+
latestMainJudgeResult = judgeResult;
|
|
3271
|
+
if (isJudgeType(judgeResult.synthesisContext?.selectedJudge)) {
|
|
3272
|
+
matrixDefaultWinnerSource = judgeResult.synthesisContext.selectedJudge;
|
|
3273
|
+
}
|
|
3274
|
+
latestJudgeEscalation = judgeResult.decision?.escalation;
|
|
3275
|
+
judgedWinner = judgeResult.winner;
|
|
3276
|
+
aiJudgeInvoked = true;
|
|
3277
|
+
for (const candidate of candidates) {
|
|
3278
|
+
if (!finishedExecutionKeys.has(variantIdForSpec(candidate.variant))) continue;
|
|
3279
|
+
const matchingResult = matchVariantResult(candidate.variant, basicResult.variants);
|
|
3280
|
+
if (!matchingResult) continue;
|
|
3281
|
+
const updated = variantResultToCandidate(candidate.variant, matchingResult);
|
|
3282
|
+
candidate.score = updated.score;
|
|
3283
|
+
candidate.feedback = updated.feedback;
|
|
3284
|
+
}
|
|
3285
|
+
recordDeterministicTestSignalObservation(candidates, options.verbose);
|
|
3286
|
+
if (options.verbose) {
|
|
3287
|
+
console.log(chalk.green(" ok AI judge evaluation complete"));
|
|
3288
|
+
}
|
|
3289
|
+
} catch (error) {
|
|
3290
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3291
|
+
if (options.verbose) {
|
|
3292
|
+
console.warn(chalk.yellow(` AI judge invocation failed: ${errorMsg}`));
|
|
3293
|
+
}
|
|
3294
|
+
aiJudgeError = errorMsg;
|
|
3295
|
+
aiJudgeInvoked = false;
|
|
3296
|
+
}
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
let winnerInfo = aiJudgeInvoked ? buildWinnerInfoFromJudgeVerdict(candidates, judgedWinner, finishedCandidates) : determineWinner(candidates, config, expectedAllowlist);
|
|
3300
|
+
if (aiJudgeError && shouldInvokeAiJudge) {
|
|
3301
|
+
winnerInfo = {
|
|
3302
|
+
winner: null,
|
|
3303
|
+
rankings: [],
|
|
3304
|
+
wasTie: false,
|
|
3305
|
+
noWinnerReason: "judge-error",
|
|
3306
|
+
tieBreaker: void 0
|
|
3307
|
+
};
|
|
3308
|
+
}
|
|
3309
|
+
if (!aiJudgeInvoked && usedCoverageScoring && winnerInfo.wasTie && config.judgeConfig && candidates.length > 1) {
|
|
3310
|
+
const successful = candidates.filter((c) => c.success && typeof c.score === "number");
|
|
3311
|
+
const SCORE_COLLISION_DECIMALS = 6;
|
|
3312
|
+
const SCORE_COLLISION_MULTIPLIER = 10 ** SCORE_COLLISION_DECIMALS;
|
|
3313
|
+
const quantizeScore = (score) => {
|
|
3314
|
+
if (!Number.isFinite(score)) return 0;
|
|
3315
|
+
return Math.round(score * SCORE_COLLISION_MULTIPLIER) / SCORE_COLLISION_MULTIPLIER;
|
|
3316
|
+
};
|
|
3317
|
+
const topScoreRaw = successful.reduce((max, c) => Math.max(max, c.score ?? 0), -Infinity);
|
|
3318
|
+
const topScoreQuantized = quantizeScore(topScoreRaw);
|
|
3319
|
+
const tiedAtTop = successful.filter(
|
|
3320
|
+
(c) => quantizeScore(Number(c.score ?? 0)) === topScoreQuantized
|
|
3321
|
+
);
|
|
3322
|
+
if (tiedAtTop.length >= 2) {
|
|
3323
|
+
const first = tiedAtTop[0];
|
|
3324
|
+
const getCandidateChangedFileCount = (candidate) => Array.isArray(candidate.changedFiles) ? candidate.changedFiles.length : 0;
|
|
3325
|
+
const getCandidateDriftViolations = (candidate) => {
|
|
3326
|
+
if (!Array.isArray(candidate.changedFiles) || candidate.changedFiles.length === 0)
|
|
3327
|
+
return 0;
|
|
3328
|
+
if (!Array.isArray(expectedAllowlist) || expectedAllowlist.length === 0) return 0;
|
|
3329
|
+
return findAllowlistViolations(candidate.changedFiles, expectedAllowlist).length;
|
|
3330
|
+
};
|
|
3331
|
+
const allSameDrift = first ? tiedAtTop.every(
|
|
3332
|
+
(c) => getCandidateDriftViolations(c) === getCandidateDriftViolations(first)
|
|
3333
|
+
) : false;
|
|
3334
|
+
const allSameDiff = first ? tiedAtTop.every(
|
|
3335
|
+
(c) => getCandidateChangedFileCount(c) === getCandidateChangedFileCount(first)
|
|
3336
|
+
) : false;
|
|
3337
|
+
if (allSameDrift && allSameDiff && typeof options.workerCwd === "string") {
|
|
3338
|
+
try {
|
|
3339
|
+
const { aiJudgeRun, aiJudgePrimary } = deriveAiJudgeInvocationArgs(config.judgeConfig);
|
|
3340
|
+
const tiedVariantResults = tiedAtTop.map((candidate) => matchVariantResult(candidate.variant, basicResult.variants)).filter(Boolean);
|
|
3341
|
+
if (tiedVariantResults.length >= 2) {
|
|
3342
|
+
const tiebreakJudgeResult = await selectWinnerWithJudge(
|
|
3343
|
+
resolvedPrompt,
|
|
3344
|
+
tiedVariantResults,
|
|
3345
|
+
{
|
|
3346
|
+
taskId,
|
|
3347
|
+
evaluationPhase: "tiebreak",
|
|
3348
|
+
aiJudgeTelemetry: true,
|
|
3349
|
+
aiJudgeRun,
|
|
3350
|
+
aiJudgePrimary,
|
|
3351
|
+
anonymizeJudge: config.judgeConfig?.anonymizeJudge,
|
|
3352
|
+
judgeAllowUnsafeModel: config.judgeConfig?.allowUnsafeModel === true,
|
|
3353
|
+
// Matrix escalation for this stage is owned by the explicit
|
|
3354
|
+
// block keyed off the main judge's escalation; keep the
|
|
3355
|
+
// tie-breaker from launching its own automatic Matrix run.
|
|
3356
|
+
autoMatrix: false
|
|
3357
|
+
}
|
|
3358
|
+
);
|
|
3359
|
+
latestTiebreakJudgeResult = tiebreakJudgeResult;
|
|
3360
|
+
const tiebreakWinner = tiebreakJudgeResult.winner;
|
|
3361
|
+
if (tiebreakWinner) {
|
|
3362
|
+
const winningCandidate = findCandidateForVariantResult(candidates, tiebreakWinner);
|
|
3363
|
+
if (winningCandidate) {
|
|
3364
|
+
tieBreakAiJudgeInvoked = true;
|
|
3365
|
+
winnerInfo = {
|
|
3366
|
+
...winnerInfo,
|
|
3367
|
+
winner: winningCandidate
|
|
3368
|
+
};
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
} catch (error) {
|
|
3373
|
+
if (options.verbose) {
|
|
3374
|
+
console.warn(chalk.yellow(` AI judge tie-breaker failed: ${String(error)}`));
|
|
3375
|
+
}
|
|
3376
|
+
}
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
}
|
|
3380
|
+
const defaultWinnerCandidate = winnerInfo.winner;
|
|
3381
|
+
if (latestJudgeEscalation?.recommended === "matrix" && eligibleVariantResultsForMatrix.length >= 2) {
|
|
3382
|
+
const matrixPolicy = resolveMatrixAuthorityPolicy(config.judgeConfig?.matrixAuthority);
|
|
3383
|
+
effectiveMatrixAuthority = matrixPolicy;
|
|
3384
|
+
try {
|
|
3385
|
+
const matrixCoordinator = new JudgeCoordinator();
|
|
3386
|
+
const matrixResult = await matrixCoordinator.evaluateProfileMatrix(
|
|
3387
|
+
eligibleVariantResultsForMatrix,
|
|
3388
|
+
resolvedPrompt,
|
|
3389
|
+
{
|
|
3390
|
+
taskId,
|
|
3391
|
+
anonymizeJudge: config.judgeConfig?.anonymizeJudge,
|
|
3392
|
+
judgeAllowUnsafeModel: config.judgeConfig?.allowUnsafeModel === true,
|
|
3393
|
+
evaluationContext: latestMainJudgeResult?.evaluationContext,
|
|
3394
|
+
matrixEscalationContext: {
|
|
3395
|
+
reasons: latestJudgeEscalation.reasons
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
);
|
|
3399
|
+
const defaultWinnerResult = defaultWinnerCandidate ? matchVariantResult(defaultWinnerCandidate.variant, basicResult.variants) : void 0;
|
|
3400
|
+
const defaultWinner = defaultWinnerResult ? `${defaultWinnerResult.provider}:${defaultWinnerResult.variant}` : "none";
|
|
3401
|
+
const authorityDecision = evaluateMatrixAuthority({
|
|
3402
|
+
policy: matrixPolicy,
|
|
3403
|
+
defaultWinner,
|
|
3404
|
+
defaultWinnerSource: matrixDefaultWinnerSource,
|
|
3405
|
+
matrixEvaluatorType: matrixResult.matrixEvaluatorType,
|
|
3406
|
+
matrixResult,
|
|
3407
|
+
variants: eligibleVariantResultsForMatrix
|
|
3408
|
+
});
|
|
3409
|
+
matrixRan = true;
|
|
3410
|
+
matrixProfileAgreement = authorityDecision.profileAgreement;
|
|
3411
|
+
if (authorityDecision.selectedByMatrix && authorityDecision.matrixWinner && isAcceptedMatrixVerdict(authorityDecision.winnerStatus)) {
|
|
3412
|
+
const matrixWinnerCandidate = findCandidateForVariantResult(
|
|
3413
|
+
candidates,
|
|
3414
|
+
authorityDecision.matrixWinner
|
|
3415
|
+
);
|
|
3416
|
+
if (matrixWinnerCandidate) {
|
|
3417
|
+
winnerInfo = {
|
|
3418
|
+
...winnerInfo,
|
|
3419
|
+
winner: matrixWinnerCandidate
|
|
3420
|
+
};
|
|
3421
|
+
} else {
|
|
3422
|
+
matrixAuthorityWarnings.push(
|
|
3423
|
+
"Matrix authority selected a variant that could not be resolved to a candidate"
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
} else {
|
|
3427
|
+
if (!isAcceptedMatrixVerdict(authorityDecision.winnerStatus) && authorityDecision.nonSelectionReasons.length === 0) {
|
|
3428
|
+
matrixAuthorityNonSelectionReasons.push(
|
|
3429
|
+
`matrix winner status is ${authorityDecision.winnerStatus}`
|
|
3430
|
+
);
|
|
3431
|
+
}
|
|
3432
|
+
if (authorityDecision.winnerStatus === "conditional") {
|
|
3433
|
+
matrixAuthorityNonSelectionReasons.push(
|
|
3434
|
+
...authorityDecision.winnerCaveats.slice(0, 3)
|
|
3435
|
+
);
|
|
3436
|
+
}
|
|
3437
|
+
if (authorityDecision.nonSelectionReasons.length > 0) {
|
|
3438
|
+
matrixAuthorityNonSelectionReasons.push(
|
|
3439
|
+
...authorityDecision.nonSelectionReasons.slice(0, 3)
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
}
|
|
3443
|
+
} catch (error) {
|
|
3444
|
+
matrixAuthorityWarnings.push(`Matrix authority evaluation failed: ${String(error)}`);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
const successCount = candidates.filter((c) => c.success).length;
|
|
3448
|
+
const persistedAiJudgeInvoked = tieBreakAiJudgeInvoked || aiJudgeInvoked;
|
|
3449
|
+
const persistedScoringMode = aiJudgeInvoked ? "ai-judge" : tieBreakAiJudgeInvoked ? "hybrid" : "deterministic";
|
|
3450
|
+
const persistedJudgeInvocationReason = aiJudgeError ? "error" : tieBreakAiJudgeInvoked || aiJudgeInvoked ? "multiple-successful-candidates" : successCount === 0 ? "no-successful-candidates" : successCount === 1 ? "single-successful-candidate" : "deterministic-shortcut";
|
|
3451
|
+
const judgeResultForEvidence = latestMainJudgeResult ?? latestTiebreakJudgeResult;
|
|
3452
|
+
const hasNewJudgeSchemaFields = config.judgeConfig?.judgeRun !== void 0 || config.judgeConfig?.matrixAuthority !== void 0 || matrixRan;
|
|
3453
|
+
const persistedMatrixAuthority = effectiveMatrixAuthority ?? (config.judgeConfig?.matrixAuthority !== void 0 ? resolveMatrixAuthorityPolicy(config.judgeConfig.matrixAuthority) : void 0);
|
|
3454
|
+
const { judgeRunRequested } = deriveAiJudgeInvocationArgs(config.judgeConfig);
|
|
3455
|
+
const multiJudgeEvidence = hasNewJudgeSchemaFields ? buildMultiJudgeEvidenceBlock({
|
|
3456
|
+
judgeResult: judgeResultForEvidence,
|
|
3457
|
+
judgeRunRequested,
|
|
3458
|
+
matrixAuthority: persistedMatrixAuthority,
|
|
3459
|
+
matrixRan,
|
|
3460
|
+
matrixProfileAgreement,
|
|
3461
|
+
matrixNonSelectionReasons: matrixAuthorityNonSelectionReasons
|
|
3462
|
+
}) : void 0;
|
|
3463
|
+
try {
|
|
3464
|
+
await artifactStore.save(stage.id, "candidates", "json", {
|
|
3465
|
+
taskId,
|
|
3466
|
+
variantCount: candidates.length,
|
|
3467
|
+
successCount: candidates.filter((c) => c.success).length,
|
|
3468
|
+
candidates: candidates.map((c) => ({
|
|
3469
|
+
provider: c.variant.provider,
|
|
3470
|
+
model: c.variant.model,
|
|
3471
|
+
success: c.success,
|
|
3472
|
+
score: c.score,
|
|
3473
|
+
error: c.error,
|
|
3474
|
+
cost: c.cost,
|
|
3475
|
+
costUnavailableReason: c.costUnavailableReason
|
|
3476
|
+
})),
|
|
3477
|
+
winner: winnerInfo.winner ? {
|
|
3478
|
+
provider: winnerInfo.winner.variant.provider,
|
|
3479
|
+
model: winnerInfo.winner.variant.model,
|
|
3480
|
+
score: winnerInfo.winner.score
|
|
3481
|
+
} : null,
|
|
3482
|
+
rankings: winnerInfo.rankings,
|
|
3483
|
+
aiJudgeInvoked: persistedAiJudgeInvoked,
|
|
3484
|
+
scoringMode: persistedScoringMode,
|
|
3485
|
+
judgeInvocationReason: persistedJudgeInvocationReason,
|
|
3486
|
+
...multiJudgeEvidence ? { multiJudgeEvidence } : {}
|
|
3487
|
+
});
|
|
3488
|
+
await artifactStore.save(
|
|
3489
|
+
stage.id,
|
|
3490
|
+
"bundle",
|
|
3491
|
+
"text",
|
|
3492
|
+
buildCompetitionBundle(taskId, candidates, basicResult)
|
|
3493
|
+
);
|
|
3494
|
+
if (winnerInfo.winner) {
|
|
3495
|
+
await artifactStore.save(stage.id, "winner", "json", {
|
|
3496
|
+
variant: winnerInfo.winner.variant,
|
|
3497
|
+
score: winnerInfo.winner.score,
|
|
3498
|
+
feedback: winnerInfo.winner.feedback,
|
|
3499
|
+
commitSha: winnerInfo.winner.commitSha,
|
|
3500
|
+
changedFiles: winnerInfo.winner.changedFiles
|
|
3501
|
+
});
|
|
3502
|
+
}
|
|
3503
|
+
const winnerResult = winnerInfo.winner ? matchVariantResult(winnerInfo.winner.variant, basicResult.variants) : void 0;
|
|
3504
|
+
const winnerDiff = typeof winnerResult?.worktreeDiff === "string" ? winnerResult.worktreeDiff : "";
|
|
3505
|
+
await artifactStore.save(stage.id, "winnerDiff", "diff", winnerDiff);
|
|
3506
|
+
let rationalePacket = basicResult.variants.find((v) => v.judgeEvaluation?.rationalePacket)?.judgeEvaluation?.rationalePacket;
|
|
3507
|
+
if (!rationalePacket) {
|
|
3508
|
+
rationalePacket = synthesizeDeterministicRationalePacket(candidates, winnerInfo, config);
|
|
3509
|
+
} else {
|
|
3510
|
+
const judgedCandidate = judgeResultForEvidence ? judgeResultForEvidence.winner ? findCandidateForVariantResult(candidates, judgeResultForEvidence.winner) : null : void 0;
|
|
3511
|
+
rationalePacket = reconcileJudgeRationaleWinner(
|
|
3512
|
+
rationalePacket,
|
|
3513
|
+
judgedCandidate,
|
|
3514
|
+
winnerInfo.winner,
|
|
3515
|
+
judgeResultForEvidence !== void 0
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
await artifactStore.save(stage.id, "judgeRationale", "json", rationalePacket);
|
|
3519
|
+
for (const [index, candidate] of candidates.entries()) {
|
|
3520
|
+
const artifactName = `candidate-${candidate.variant.provider ? `${candidate.variant.provider}-` : ""}${index + 1}`;
|
|
3521
|
+
const matchingResult = matchVariantResult(candidate.variant, basicResult.variants);
|
|
3522
|
+
await artifactStore.save(stage.id, artifactName, "json", {
|
|
3523
|
+
candidate,
|
|
3524
|
+
variantResult: matchingResult ? {
|
|
3525
|
+
provider: matchingResult.provider,
|
|
3526
|
+
model: matchingResult.model,
|
|
3527
|
+
success: matchingResult.success,
|
|
3528
|
+
completeness: matchingResult.completeness,
|
|
3529
|
+
judgeScore: matchingResult.judgeScore,
|
|
3530
|
+
worktreeDiff: matchingResult.worktreeDiff,
|
|
3531
|
+
worktreeDiffReplayable: matchingResult.worktreeDiffReplayable,
|
|
3532
|
+
baselineRef: matchingResult.baselineRef,
|
|
3533
|
+
worktreeSummary: matchingResult.worktreeSummary,
|
|
3534
|
+
retainedWorktreePath: matchingResult.retainedWorktreePath,
|
|
3535
|
+
logFilePath: matchingResult.logFilePath,
|
|
3536
|
+
logTail: matchingResult.logTail,
|
|
3537
|
+
costMetrics: matchingResult.costMetrics,
|
|
3538
|
+
error: matchingResult.error?.message
|
|
3539
|
+
} : null
|
|
3540
|
+
});
|
|
3541
|
+
candidate.resultArtifactPath = `${stage.id}.${artifactName}`;
|
|
3542
|
+
}
|
|
3543
|
+
} catch (error) {
|
|
3544
|
+
if (options.verbose) {
|
|
3545
|
+
console.warn(chalk.yellow(` Warning: Failed to persist competition artifacts: ${error}`));
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
const coherenceMetrics = computeCoherenceMetrics(
|
|
3549
|
+
candidates,
|
|
3550
|
+
DEFAULT_COHERENCE_THRESHOLD,
|
|
3551
|
+
expectedAllowlist
|
|
3552
|
+
);
|
|
3553
|
+
const markedIncomparable = !coherenceMetrics.isCoherent;
|
|
3554
|
+
const warnings = [];
|
|
3555
|
+
if (markedIncomparable) {
|
|
3556
|
+
warnings.push(
|
|
3557
|
+
`Low coherence between successful candidates (avg ${coherenceMetrics.averageSimilarity.toFixed(2)} < ${coherenceMetrics.coherenceThreshold.toFixed(2)})`
|
|
3558
|
+
);
|
|
3559
|
+
}
|
|
3560
|
+
if (aiJudgeError) {
|
|
3561
|
+
warnings.push(`AI judge invocation failed: ${aiJudgeError}`);
|
|
3562
|
+
}
|
|
3563
|
+
if (winnerInfo.noWinnerDiagnostic) {
|
|
3564
|
+
warnings.push(winnerInfo.noWinnerDiagnostic);
|
|
3565
|
+
}
|
|
3566
|
+
warnings.push(...matrixAuthorityWarnings);
|
|
3567
|
+
if (winnerInfo.belowThresholdWinner) {
|
|
3568
|
+
warnings.push(
|
|
3569
|
+
`Winner selected below threshold (${winnerInfo.belowThresholdWinner.winnerScore} < ${winnerInfo.belowThresholdWinner.threshold})`
|
|
3570
|
+
);
|
|
3571
|
+
}
|
|
3572
|
+
const driftContext = computeCompetitionDrift(candidates, expectedAllowlist, effectiveMode);
|
|
3573
|
+
try {
|
|
3574
|
+
await artifactStore.save(stage.id, "stage-change", "json", {
|
|
3575
|
+
stageId: stage.id,
|
|
3576
|
+
stageType: "competition",
|
|
3577
|
+
mode: effectiveMode,
|
|
3578
|
+
deliveredFiles: driftContext.deliveredFiles,
|
|
3579
|
+
expectedFiles: driftContext.expectedFiles,
|
|
3580
|
+
violations: driftContext.violations,
|
|
3581
|
+
hasDrift: driftContext.hasDrift,
|
|
3582
|
+
candidateCount: candidates.length,
|
|
3583
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
3584
|
+
});
|
|
3585
|
+
await artifactStore.save(stage.id, "stage-change-summary", "text", driftContext.summary);
|
|
3586
|
+
} catch (error) {
|
|
3587
|
+
if (options.verbose) {
|
|
3588
|
+
console.warn(chalk.yellow(` Warning: Failed to persist drift context: ${error}`));
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
const effectiveWinnerInfo = winnerInfo;
|
|
3592
|
+
const winnerTestsNotApplicable = effectiveWinnerInfo.winner !== null && winnerHasNoApplicableTests(effectiveWinnerInfo.winner) && candidateGatePassedVariantIds.has(variantIdForSpec(effectiveWinnerInfo.winner.variant));
|
|
3593
|
+
const winnerTrustedTestStatus = effectiveWinnerInfo.winner ? resolveTrustedCandidateTestStatus(effectiveWinnerInfo.winner) : void 0;
|
|
3594
|
+
const winnerRepairSuperseded = winnerTrustedTestStatus !== effectiveWinnerInfo.winner?.testStatus;
|
|
3595
|
+
const winnerVerificationAllowsSuccess = effectiveWinnerInfo.winner ? verificationPolarityAllowsStageSuccess({
|
|
3596
|
+
verification: effectiveWinnerInfo.winner.verification,
|
|
3597
|
+
testStatus: winnerTestsNotApplicable ? void 0 : winnerTrustedTestStatus,
|
|
3598
|
+
rawTestStatus: winnerTestsNotApplicable ? void 0 : winnerTrustedTestStatus,
|
|
3599
|
+
// The stale pre-repair exit code restates the superseded failure.
|
|
3600
|
+
exitCode: winnerTestsNotApplicable || winnerRepairSuperseded ? void 0 : effectiveWinnerInfo.winner.testsExecuted?.exitCode
|
|
3601
|
+
}) : false;
|
|
3602
|
+
const validationPassed = successCount > 0 && effectiveWinnerInfo.winner !== null && winnerVerificationAllowsSuccess;
|
|
3603
|
+
let summary = buildCompetitionSummary(candidates, effectiveWinnerInfo, config);
|
|
3604
|
+
if (aiJudgeError && shouldInvokeAiJudge) {
|
|
3605
|
+
summary = `Competition failed: AI judge invocation failed: ${aiJudgeError}`;
|
|
3606
|
+
}
|
|
3607
|
+
if (markedIncomparable) {
|
|
3608
|
+
summary += ` (low coherence: ${coherenceMetrics.averageSimilarity.toFixed(2)})`;
|
|
3609
|
+
}
|
|
3610
|
+
const scoringMode = aiJudgeInvoked ? "ai-judge" : tieBreakAiJudgeInvoked ? "hybrid" : "deterministic";
|
|
3611
|
+
const judgeInvocationReason = aiJudgeError ? "error" : tieBreakAiJudgeInvoked || aiJudgeInvoked ? "multiple-successful-candidates" : successCount === 0 ? "no-successful-candidates" : successCount === 1 ? "single-successful-candidate" : "deterministic-shortcut";
|
|
3612
|
+
const winnerNeedsRepair = Boolean(
|
|
3613
|
+
effectiveWinnerInfo.winner?.verification && !effectiveWinnerInfo.winner.verification.passed
|
|
3614
|
+
);
|
|
3615
|
+
const stageVerification = effectiveWinnerInfo.winner?.verification;
|
|
3616
|
+
const result = {
|
|
3617
|
+
stageId: stage.id,
|
|
3618
|
+
stageType: "competition",
|
|
3619
|
+
competitionTaskId: taskId,
|
|
3620
|
+
validationPassed,
|
|
3621
|
+
validationScore: effectiveWinnerInfo.winner?.score,
|
|
3622
|
+
cost: totalCost,
|
|
3623
|
+
costUnavailableReason,
|
|
3624
|
+
warnings: warnings.length > 0 ? warnings : void 0,
|
|
3625
|
+
summary,
|
|
3626
|
+
durationMs: Date.now() - startTime,
|
|
3627
|
+
candidates,
|
|
3628
|
+
winner: effectiveWinnerInfo.winner,
|
|
3629
|
+
winnerApplied: false,
|
|
3630
|
+
// MVP: winner application happens in the workflow executor
|
|
3631
|
+
winnerNeedsRepair,
|
|
3632
|
+
verification: stageVerification,
|
|
3633
|
+
coherenceMetrics,
|
|
3634
|
+
markedIncomparable,
|
|
3635
|
+
scoringMode,
|
|
3636
|
+
competitionJudgeResult: {
|
|
3637
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
3638
|
+
aiJudgeAttempted,
|
|
3639
|
+
aiJudgeInvoked: tieBreakAiJudgeInvoked || aiJudgeInvoked,
|
|
3640
|
+
aiJudgeError,
|
|
3641
|
+
aiJudgeProviderFailed: judgeProviderFailed(latestMainJudgeResult?.decision) || void 0,
|
|
3642
|
+
judgeInvocationReason,
|
|
3643
|
+
rankings: effectiveWinnerInfo.rankings,
|
|
3644
|
+
wasTie: effectiveWinnerInfo.wasTie,
|
|
3645
|
+
noWinnerReason: effectiveWinnerInfo.noWinnerReason,
|
|
3646
|
+
belowThresholdWinner: effectiveWinnerInfo.belowThresholdWinner,
|
|
3647
|
+
tieBreaker: effectiveWinnerInfo.tieBreaker,
|
|
3648
|
+
scoreCollisionReport: effectiveWinnerInfo.scoreCollisionReport
|
|
3649
|
+
},
|
|
3650
|
+
driftContext
|
|
3651
|
+
};
|
|
3652
|
+
if (!validationPassed && effectiveWinnerInfo.winner && successCount > 0 && !aiJudgeError) {
|
|
3653
|
+
const refusal = resolveWinnerDeliveryRefusalReason(result);
|
|
3654
|
+
if (refusal) result.summary = `${summary}; winner not accepted: ${refusal}`;
|
|
3655
|
+
}
|
|
3656
|
+
if (options.verbose) {
|
|
3657
|
+
console.log(chalk.green(" ok Competition complete"));
|
|
3658
|
+
console.log(
|
|
3659
|
+
chalk.gray(
|
|
3660
|
+
` Winner: ${effectiveWinnerInfo.winner ? formatCompetitionCandidateVariantDisplay(effectiveWinnerInfo.winner) : "none"}`
|
|
3661
|
+
)
|
|
3662
|
+
);
|
|
3663
|
+
console.log(
|
|
3664
|
+
chalk.gray(
|
|
3665
|
+
` Total cost: ${costUnavailableReason !== void 0 ? "unavailable" : `$${totalCost.toFixed(4)}`}`
|
|
3666
|
+
)
|
|
3667
|
+
);
|
|
3668
|
+
console.log(
|
|
3669
|
+
chalk.gray(
|
|
3670
|
+
` Coherence: ${coherenceMetrics.averageSimilarity.toFixed(2)} (${coherenceMetrics.isCoherent ? "coherent" : "INCOHERENT"})`
|
|
3671
|
+
)
|
|
3672
|
+
);
|
|
3673
|
+
if (driftContext.hasDrift) {
|
|
3674
|
+
console.log(
|
|
3675
|
+
chalk.yellow(
|
|
3676
|
+
` Drift: ${driftContext.violations.length} file(s) outside allowlist (mode: ${effectiveMode})`
|
|
3677
|
+
)
|
|
3678
|
+
);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
const authoritativeWinner = effectiveWinnerInfo.winner ? matchVariantResult(effectiveWinnerInfo.winner.variant, basicResult.variants) : void 0;
|
|
3682
|
+
if (effectiveWinnerInfo.winner && !authoritativeWinner) {
|
|
3683
|
+
const errorMessage = "Final competition winner could not be resolved to an executed variant";
|
|
3684
|
+
await finalizeDeferredHandle(
|
|
3685
|
+
createEvaluationHandleFinalState({ outcome: "error", winner: null, errorMessage })
|
|
3686
|
+
);
|
|
3687
|
+
await cleanupSynthesisInputPatches(basicResult.variants, {
|
|
3688
|
+
expectedRoot: resolveSynthesisInputPatchDir(
|
|
3689
|
+
competitionOptions.executionContext.buildDir,
|
|
3690
|
+
taskId
|
|
3691
|
+
)
|
|
3692
|
+
});
|
|
3693
|
+
throw new Error(errorMessage);
|
|
3694
|
+
}
|
|
3695
|
+
await finalizeDeferredHandle(
|
|
3696
|
+
authoritativeWinner ? createEvaluationHandleFinalState({
|
|
3697
|
+
outcome: "winner_selected",
|
|
3698
|
+
winner: toEvaluationHandleWinnerMetadata(authoritativeWinner)
|
|
3699
|
+
}) : createEvaluationHandleFinalState({
|
|
3700
|
+
outcome: successCount === 0 ? "all_failed" : "no_winner",
|
|
3701
|
+
winner: null
|
|
3702
|
+
})
|
|
3703
|
+
);
|
|
3704
|
+
await cleanupSynthesisInputPatches(basicResult.variants, {
|
|
3705
|
+
expectedRoot: resolveSynthesisInputPatchDir(
|
|
3706
|
+
competitionOptions.executionContext.buildDir,
|
|
3707
|
+
taskId
|
|
3708
|
+
)
|
|
3709
|
+
});
|
|
3710
|
+
return result;
|
|
3711
|
+
} catch (error) {
|
|
3712
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3713
|
+
if (!deferredHandleFinalized) {
|
|
3714
|
+
await finalizeEvaluationHandleAsync(
|
|
3715
|
+
taskId,
|
|
3716
|
+
createEvaluationHandleFinalState({ outcome: "error", winner: null, errorMessage })
|
|
3717
|
+
).catch(() => {
|
|
3718
|
+
});
|
|
3719
|
+
}
|
|
3720
|
+
await cleanupSynthesisInputPatches(basicResult.variants, {
|
|
3721
|
+
expectedRoot: resolveSynthesisInputPatchDir(
|
|
3722
|
+
competitionOptions.executionContext.buildDir,
|
|
3723
|
+
taskId
|
|
3724
|
+
)
|
|
3725
|
+
}).catch(() => {
|
|
3726
|
+
});
|
|
3727
|
+
return {
|
|
3728
|
+
stageId: stage.id,
|
|
3729
|
+
stageType: "competition",
|
|
3730
|
+
competitionTaskId: taskId,
|
|
3731
|
+
validationPassed: false,
|
|
3732
|
+
...observedCandidates ? resolveCompetitionCostAggregate(observedCandidates) : { cost: 0, costUnavailableReason: "provider_not_reported" },
|
|
3733
|
+
candidates: observedCandidates ?? [],
|
|
3734
|
+
winner: null,
|
|
3735
|
+
summary: `Competition post-processing failed: ${errorMessage}`,
|
|
3736
|
+
durationMs: Date.now() - startTime,
|
|
3737
|
+
competitionJudgeResult: {
|
|
3738
|
+
judgeType: config.judgeConfig?.type ?? "ai",
|
|
3739
|
+
aiJudgeAttempted,
|
|
3740
|
+
aiJudgeInvoked: tieBreakAiJudgeInvoked || aiJudgeInvoked,
|
|
3741
|
+
aiJudgeError: errorMessage,
|
|
3742
|
+
judgeInvocationReason: "error",
|
|
3743
|
+
rankings: [],
|
|
3744
|
+
wasTie: false,
|
|
3745
|
+
noWinnerReason: "judge-error"
|
|
3746
|
+
}
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
3750
|
+
function buildCompetitionSummary(candidates, winnerInfo, config) {
|
|
3751
|
+
const successCount = candidates.filter((c) => c.success).length;
|
|
3752
|
+
const failCount = candidates.length - successCount;
|
|
3753
|
+
let summary = `Competition: ${successCount}/${candidates.length} variants succeeded`;
|
|
3754
|
+
if (failCount > 0) {
|
|
3755
|
+
summary += ` (${failCount} failed)`;
|
|
3756
|
+
}
|
|
3757
|
+
if (winnerInfo.winner) {
|
|
3758
|
+
summary += `. ${competitionPickLabel(successCount)}: ${formatCompetitionCandidateVariantDisplay(winnerInfo.winner)}`;
|
|
3759
|
+
if (winnerInfo.winner.score !== void 0) {
|
|
3760
|
+
summary += ` (score: ${winnerInfo.winner.score})`;
|
|
3761
|
+
}
|
|
3762
|
+
if (winnerInfo.belowThresholdWinner) {
|
|
3763
|
+
summary += ` (below threshold: ${winnerInfo.belowThresholdWinner.winnerScore} < ${winnerInfo.belowThresholdWinner.threshold})`;
|
|
3764
|
+
}
|
|
3765
|
+
} else if (winnerInfo.noWinnerReason) {
|
|
3766
|
+
const reasons = {
|
|
3767
|
+
"all-failed": "all variants failed",
|
|
3768
|
+
tie: "tie between top variants",
|
|
3769
|
+
"below-threshold": `all scores below threshold (${config.judgeConfig?.threshold ?? 0})`,
|
|
3770
|
+
"judge-error": "judge evaluation failed",
|
|
3771
|
+
incomparable: "candidates touched disjoint files (low coherence)"
|
|
3772
|
+
};
|
|
3773
|
+
summary += `. No winner: ${reasons[winnerInfo.noWinnerReason] ?? winnerInfo.noWinnerReason}`;
|
|
3774
|
+
}
|
|
3775
|
+
return summary;
|
|
3776
|
+
}
|
|
3777
|
+
function isCompetitionStage(stage) {
|
|
3778
|
+
return stage.type === "competition" && stage.competition !== void 0;
|
|
3779
|
+
}
|
|
3780
|
+
|
|
3781
|
+
export {
|
|
3782
|
+
selectFailingGateFindings,
|
|
3783
|
+
gateEvidenceField,
|
|
3784
|
+
persistGateEvidenceArtifact,
|
|
3785
|
+
verificationPolarityAllowsStageSuccess,
|
|
3786
|
+
describeStageValidationFailure,
|
|
3787
|
+
resolveWinnerDeliveryRefusalReason,
|
|
3788
|
+
shouldRestrictWinnerPatchToAllowlist,
|
|
3789
|
+
applyCompetitionWinnerPatch,
|
|
3790
|
+
computeCoherenceMetrics,
|
|
3791
|
+
__internalDetermineWinner,
|
|
3792
|
+
__internalRecordDeterministicTestSignalObservation,
|
|
3793
|
+
__internalExtractChangedFilesFromResult,
|
|
3794
|
+
__internalGetVariantTelemetryStatePath,
|
|
3795
|
+
__internalHydrateCandidateCostsFromTelemetry,
|
|
3796
|
+
__internalVariantResultToCandidate,
|
|
3797
|
+
__internalMatchVariantResult,
|
|
3798
|
+
__internalCandidateMatchesVariantResult,
|
|
3799
|
+
__internalResolveCompetitionCostAggregate,
|
|
3800
|
+
executeCompetitionStage,
|
|
3801
|
+
isCompetitionStage
|
|
3802
|
+
};
|
|
3803
|
+
//# sourceMappingURL=chunk-3NOTLVLG.js.map
|