agentic-qe 3.15.4 → 3.15.5
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/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +70 -0
- package/assets/skills/skills-manifest.json +1 -1
- package/dist/bridge/captured-experience-bridge.js +7 -9
- package/dist/cli/bundle.js +5 -5
- package/dist/cli/chunks/adapter-EGIPTX5H.js +2 -0
- package/dist/cli/chunks/{agent-booster-wasm-MHJBRXMK.js → agent-booster-wasm-CCTTGCQS.js} +2 -2
- package/dist/cli/chunks/{agent-handler-G64SD7J2.js → agent-handler-DHYUF3NX.js} +2 -2
- package/dist/cli/chunks/{agent-memory-branch-G4UXFPKH.js → agent-memory-branch-642W7CYX.js} +2 -2
- package/dist/cli/chunks/aqe-learning-engine-WP4GCBDT.js +2 -0
- package/dist/cli/chunks/{arena-QJMKYPHS.js → arena-MUCC7UUN.js} +4 -4
- package/dist/cli/chunks/{arena-63M3M6EW.js → arena-NW2JVAFY.js} +2 -2
- package/dist/cli/chunks/{audit-UDFVTWSX.js → audit-VKQTZJAA.js} +3 -3
- package/dist/cli/chunks/base-6ORFD62I.js +2 -0
- package/dist/cli/chunks/{better-sqlite3-JLX2G56I.js → better-sqlite3-7ZUFVWLQ.js} +2 -2
- package/dist/cli/chunks/{brain-handler-PWF32LLW.js → brain-handler-7LKAWSBI.js} +3 -3
- package/dist/cli/chunks/{branch-enumerator-PB7QWXDP.js → branch-enumerator-HUNXJKM4.js} +2 -2
- package/dist/cli/chunks/{browser-CYNV2F3U.js → browser-ZSZEYUDP.js} +2 -2
- package/dist/cli/chunks/browser-workflow-PIXGERWP.js +2 -0
- package/dist/cli/chunks/{chunk-RWZ4F44Y.js → chunk-2KWI3MT3.js} +2 -2
- package/dist/cli/chunks/{chunk-UORI2CZ4.js → chunk-2WFTZSRT.js} +1 -1
- package/dist/cli/chunks/{chunk-HCDTLV5J.js → chunk-2XYTS2WY.js} +2 -2
- package/dist/cli/chunks/{chunk-DBIK4OFX.js → chunk-35MDATQE.js} +1 -1
- package/dist/cli/chunks/{chunk-54XTJBHV.js → chunk-3AYYGR6W.js} +1 -1
- package/dist/cli/chunks/{chunk-SJB4KMZS.js → chunk-3DTEQHFB.js} +3 -3
- package/dist/cli/chunks/{chunk-RG67RZIX.js → chunk-3OAQPN2N.js} +2 -2
- package/dist/cli/chunks/{chunk-2DRMTRBM.js → chunk-3QD7U3FY.js} +2 -2
- package/dist/cli/chunks/{chunk-VMSO6DMF.js → chunk-3SH23PS3.js} +2 -2
- package/dist/cli/chunks/{chunk-RJ5CZ7B6.js → chunk-3YASZPLO.js} +2 -2
- package/dist/cli/chunks/{chunk-JFQDWAU3.js → chunk-3YNP33GB.js} +2 -2
- package/dist/cli/chunks/{chunk-I53365HG.js → chunk-47GEI2LT.js} +3 -3
- package/dist/cli/chunks/{chunk-PCWZCXBD.js → chunk-4AZP7ZCW.js} +2 -2
- package/dist/cli/chunks/{chunk-54ITMOFM.js → chunk-4PT5KNXL.js} +4 -4
- package/dist/cli/chunks/{chunk-SIUUAKCE.js → chunk-4UXPDV7E.js} +1 -1
- package/dist/cli/chunks/{chunk-LZSLJJII.js → chunk-4XSCZVON.js} +2 -2
- package/dist/cli/chunks/{chunk-DFJGXXCL.js → chunk-5BOZCMXZ.js} +1 -1
- package/dist/cli/chunks/{chunk-B7J3O2WM.js → chunk-644DFAGS.js} +1 -1
- package/dist/cli/chunks/{chunk-2ICC4H6K.js → chunk-663ZFPUL.js} +2 -2
- package/dist/cli/chunks/{chunk-YUWUFYHK.js → chunk-6JFGB2JB.js} +1 -1
- package/dist/cli/chunks/{chunk-MBV57LKC.js → chunk-6LG5UEQE.js} +2 -2
- package/dist/cli/chunks/{chunk-LRTF5RMG.js → chunk-6UYN5PHA.js} +4 -4
- package/dist/cli/chunks/{chunk-5SS2Z7JC.js → chunk-6ZIDZ4TJ.js} +2 -2
- package/dist/cli/chunks/{chunk-2534PXIV.js → chunk-7BIYJRQJ.js} +2 -2
- package/dist/cli/chunks/{chunk-HX5KQK7I.js → chunk-7EZLOC4K.js} +2 -2
- package/dist/cli/chunks/{chunk-ZAOQIVHG.js → chunk-7YB2KELI.js} +2 -2
- package/dist/cli/chunks/{chunk-SOSJFEFB.js → chunk-A2WOQO2N.js} +2 -2
- package/dist/cli/chunks/{chunk-M6SJALLA.js → chunk-A337MD3C.js} +2 -2
- package/dist/cli/chunks/{chunk-YZ2AGC7O.js → chunk-A5J4XJS6.js} +1 -1
- package/dist/cli/chunks/{chunk-PEA4DVXW.js → chunk-AQN5EDBM.js} +1 -1
- package/dist/cli/chunks/{chunk-EGHK6NMH.js → chunk-AUZXRFOX.js} +2 -2
- package/dist/cli/chunks/{chunk-BBKLVHDQ.js → chunk-B2UHTA4Q.js} +1 -1
- package/dist/cli/chunks/{chunk-LY53L6NH.js → chunk-BBL6YXD3.js} +2 -2
- package/dist/cli/chunks/chunk-BJ5P4IV2.js +2 -0
- package/dist/cli/chunks/{chunk-I43QRLLB.js → chunk-BMWAWZAN.js} +2 -2
- package/dist/cli/chunks/chunk-BO4PKNYP.js +102 -0
- package/dist/cli/chunks/{chunk-PMXWDTDK.js → chunk-BY646JOM.js} +2 -2
- package/dist/cli/chunks/{chunk-OFQDM73J.js → chunk-BYXNHGQC.js} +2 -2
- package/dist/cli/chunks/{chunk-73MTUM6F.js → chunk-BZFT4QDQ.js} +2 -2
- package/dist/cli/chunks/{chunk-RA3MI5WK.js → chunk-CFPR24YK.js} +2 -2
- package/dist/cli/chunks/{chunk-WEDVLKRB.js → chunk-CQMHDAJS.js} +2 -2
- package/dist/cli/chunks/{chunk-HFSBONCS.js → chunk-CR2JUANW.js} +2 -2
- package/dist/cli/chunks/{chunk-BY3IL56R.js → chunk-CXOXUBYU.js} +1 -1
- package/dist/cli/chunks/{chunk-AOKSLWON.js → chunk-D3G2NN7F.js} +77 -78
- package/dist/cli/chunks/{chunk-I5A3HGO6.js → chunk-DEGWCUMY.js} +2 -2
- package/dist/cli/chunks/{chunk-CQ3WSTJ2.js → chunk-DFPL5N3Q.js} +2 -2
- package/dist/cli/chunks/{chunk-TVQPYP2Z.js → chunk-DL5JENWD.js} +3 -3
- package/dist/cli/chunks/{chunk-V3NZ23X6.js → chunk-DLY4YEN3.js} +2 -2
- package/dist/cli/chunks/{chunk-HEKCN5XQ.js → chunk-DPV33C77.js} +2 -2
- package/dist/cli/chunks/{chunk-KOLHZSEC.js → chunk-DPYDUKVZ.js} +2 -2
- package/dist/cli/chunks/{chunk-3MSBVOYW.js → chunk-DTJODGCE.js} +2 -2
- package/dist/cli/chunks/{chunk-MGH4XIHH.js → chunk-DWZI3IVB.js} +2 -2
- package/dist/cli/chunks/{chunk-DPL4ZXTE.js → chunk-DXVZBJE4.js} +1 -1
- package/dist/cli/chunks/{chunk-LXYTC62V.js → chunk-E4VIVMDL.js} +2 -2
- package/dist/cli/chunks/{chunk-DVE5SM5G.js → chunk-EAYQCBXL.js} +3 -3
- package/dist/cli/chunks/{chunk-BY5PLEPZ.js → chunk-EOR3RECZ.js} +2 -2
- package/dist/cli/chunks/chunk-ERGWWI4Y.js +3 -0
- package/dist/cli/chunks/{chunk-LPSE6DOQ.js → chunk-EUEHBERE.js} +1 -1
- package/dist/cli/chunks/{chunk-3BDDOFYW.js → chunk-EVCFUYYK.js} +2 -2
- package/dist/cli/chunks/{chunk-SZWXUG36.js → chunk-FBEXG4HD.js} +2 -2
- package/dist/cli/chunks/{chunk-5GWTJJEO.js → chunk-FE3AIZI6.js} +1 -1
- package/dist/cli/chunks/{chunk-4GLR2S2M.js → chunk-FHUJCVT3.js} +2 -2
- package/dist/cli/chunks/{chunk-6TMUZGM2.js → chunk-FNAYDITO.js} +1 -1
- package/dist/cli/chunks/{chunk-U2E5F3VZ.js → chunk-FQKEZVN6.js} +2 -2
- package/dist/cli/chunks/{chunk-2LL35V3A.js → chunk-FWYJ6UDX.js} +2 -2
- package/dist/cli/chunks/{chunk-ISASRHZP.js → chunk-GGDKOJVF.js} +2 -2
- package/dist/cli/chunks/{chunk-ZKXZTIVF.js → chunk-GKPRECLL.js} +2 -2
- package/dist/cli/chunks/{chunk-OHH6BUZK.js → chunk-GRIPCYW5.js} +1 -1
- package/dist/cli/chunks/{chunk-XZ5Z56OP.js → chunk-GTSOFCMQ.js} +1 -1
- package/dist/cli/chunks/{chunk-2MU4WCMQ.js → chunk-HBYQYGXP.js} +2 -2
- package/dist/cli/chunks/{chunk-YQ6BK7QC.js → chunk-HGBJ2QQ4.js} +2 -2
- package/dist/cli/chunks/{chunk-N6CRIUTO.js → chunk-HIUNCO2W.js} +2 -2
- package/dist/cli/chunks/{chunk-5H7MR6HP.js → chunk-HLFDC4SF.js} +1 -1
- package/dist/cli/chunks/{chunk-GAXTPQCZ.js → chunk-HPLMX62I.js} +2 -2
- package/dist/cli/chunks/{chunk-WO4NSPLX.js → chunk-IHFUEPZH.js} +2 -2
- package/dist/cli/chunks/{chunk-ADF6LGHZ.js → chunk-IJ2EXSMO.js} +2 -2
- package/dist/cli/chunks/{chunk-GLKLEHK3.js → chunk-IJOFZBXD.js} +2 -2
- package/dist/cli/chunks/{chunk-IKWAPHJA.js → chunk-JANLATHN.js} +2 -2
- package/dist/cli/chunks/{chunk-J3RNVHDH.js → chunk-JCC7QEKZ.js} +1 -1
- package/dist/cli/chunks/{chunk-C5EN5WB6.js → chunk-JI77KJP7.js} +1 -1
- package/dist/cli/chunks/{chunk-B7GJXQD2.js → chunk-JNMUVBAJ.js} +2 -2
- package/dist/cli/chunks/{chunk-ZP6ANUP7.js → chunk-JYY7AYGA.js} +1 -1
- package/dist/cli/chunks/{chunk-6HTO4BLV.js → chunk-KHCALIFP.js} +2 -2
- package/dist/cli/chunks/{chunk-BND6M2JX.js → chunk-KJ3KBP24.js} +2 -2
- package/dist/cli/chunks/{chunk-O7TMY4GS.js → chunk-KZVJW2SD.js} +2 -2
- package/dist/cli/chunks/chunk-L336HFFE.js +2 -0
- package/dist/cli/chunks/{chunk-W3PPYNHF.js → chunk-L375O3CZ.js} +2 -2
- package/dist/cli/chunks/{chunk-PJAXWCFC.js → chunk-L6SVCNXK.js} +1 -1
- package/dist/cli/chunks/{chunk-VAXQUD5G.js → chunk-LNO5IWGH.js} +1 -1
- package/dist/cli/chunks/{chunk-CFXFGQEN.js → chunk-LRASYSGV.js} +2 -2
- package/dist/cli/chunks/{chunk-MVTUFPM2.js → chunk-MB4VFAJC.js} +2 -2
- package/dist/cli/chunks/{chunk-SCL2OYN7.js → chunk-MNQ36MAF.js} +2 -2
- package/dist/cli/chunks/{chunk-W6TJXK5Y.js → chunk-MQGEZA2A.js} +2 -2
- package/dist/cli/chunks/{chunk-73PCVMGA.js → chunk-MS3Z2CP5.js} +2 -2
- package/dist/cli/chunks/{chunk-WI5AEB7Q.js → chunk-N2TJNIHA.js} +2 -2
- package/dist/cli/chunks/{chunk-DEJM2P5K.js → chunk-NF6AFIAX.js} +1 -1
- package/dist/cli/chunks/{chunk-JL3TI43V.js → chunk-NLDFNAOS.js} +2 -2
- package/dist/cli/chunks/{chunk-UK5HY6LC.js → chunk-NMJVG22Z.js} +1 -1
- package/dist/cli/chunks/{chunk-WW6WUMS7.js → chunk-OGYWWZU7.js} +2 -2
- package/dist/cli/chunks/{chunk-3C2BFLYC.js → chunk-ONCH5AXU.js} +3 -3
- package/dist/cli/chunks/{chunk-OG4KHX7S.js → chunk-OQDC7GCM.js} +2 -2
- package/dist/cli/chunks/{chunk-3ZIVBTX6.js → chunk-ORTBRO7F.js} +2 -2
- package/dist/cli/chunks/{chunk-NIJMLEAX.js → chunk-OU4ZAWCC.js} +2 -2
- package/dist/cli/chunks/{chunk-FDK2GF4G.js → chunk-OW42F2TX.js} +2 -2
- package/dist/cli/chunks/{chunk-TB22Y3WW.js → chunk-PCCIZFD7.js} +1 -1
- package/dist/cli/chunks/{chunk-BZWZTGMF.js → chunk-PDPEMBFV.js} +2 -2
- package/dist/cli/chunks/{chunk-D7RVZ5C2.js → chunk-PHAQWAEV.js} +2 -2
- package/dist/cli/chunks/{chunk-P7GM3EE7.js → chunk-PHP4UJYP.js} +1 -1
- package/dist/cli/chunks/{chunk-6QPW4OVH.js → chunk-PURHGLEC.js} +1 -1
- package/dist/cli/chunks/{chunk-BPGFCUDO.js → chunk-PVAE65B4.js} +1 -1
- package/dist/cli/chunks/{chunk-7X2IVMXN.js → chunk-QPV3OKX2.js} +2 -2
- package/dist/cli/chunks/{chunk-DAYMT7TH.js → chunk-QXY3IRBW.js} +3 -3
- package/dist/cli/chunks/{chunk-QIAX3JJB.js → chunk-RCXMUEZN.js} +2 -2
- package/dist/cli/chunks/{chunk-RB2SL5GD.js → chunk-RMJ7PJOS.js} +2 -2
- package/dist/cli/chunks/{chunk-FRBCX46J.js → chunk-RUJLVJDE.js} +2 -2
- package/dist/cli/chunks/{chunk-S5D3HCGN.js → chunk-RUSLEOMC.js} +2 -2
- package/dist/cli/chunks/chunk-SAPIRK2G.js +3 -0
- package/dist/cli/chunks/{chunk-A4OPQJ4A.js → chunk-SDWMLT7B.js} +5 -5
- package/dist/cli/chunks/{chunk-RJMBHZYL.js → chunk-SEZLYRCS.js} +1 -1
- package/dist/cli/chunks/{chunk-AHVN3UQZ.js → chunk-TDLKXLJC.js} +2 -2
- package/dist/cli/chunks/{chunk-EWUFVWRT.js → chunk-U4TV75AC.js} +2 -2
- package/dist/cli/chunks/{chunk-X5S5UEM6.js → chunk-U6OVPMQX.js} +2 -2
- package/dist/cli/chunks/{chunk-5B5D5KCV.js → chunk-UE22ODHN.js} +1 -1
- package/dist/cli/chunks/{chunk-I6E4X3L3.js → chunk-UXZEVIFX.js} +1 -1
- package/dist/cli/chunks/{chunk-DF4Y3HPO.js → chunk-V7GNUVYI.js} +1 -1
- package/dist/cli/chunks/{chunk-OOK6GPJR.js → chunk-VCKDVOPD.js} +2 -2
- package/dist/cli/chunks/{chunk-G3ZUB3HD.js → chunk-VH3EB6ZG.js} +2 -2
- package/dist/cli/chunks/{chunk-PB2WSCBW.js → chunk-VLOIEVER.js} +3 -3
- package/dist/cli/chunks/{chunk-7INP6HZH.js → chunk-VML4SG3X.js} +3 -3
- package/dist/cli/chunks/{chunk-K2VUYRMG.js → chunk-VRJPH3LG.js} +2 -2
- package/dist/cli/chunks/{chunk-RI4JPBZD.js → chunk-VYDNLQJN.js} +2 -2
- package/dist/cli/chunks/{chunk-35A22AUL.js → chunk-W3ZGNNDV.js} +1 -1
- package/dist/cli/chunks/{chunk-R47LNH5W.js → chunk-WC47GICZ.js} +2 -2
- package/dist/cli/chunks/{chunk-Z56QTRRB.js → chunk-WLIYWBQR.js} +2 -2
- package/dist/cli/chunks/{chunk-ZURYZEHX.js → chunk-WN55MNZ2.js} +2 -2
- package/dist/cli/chunks/chunk-WOZIXXY5.js +7 -0
- package/dist/cli/chunks/{chunk-NBMQOP2R.js → chunk-WR64BKUE.js} +1 -1
- package/dist/cli/chunks/{chunk-T3BMCEQR.js → chunk-WT3JC4A6.js} +2 -2
- package/dist/cli/chunks/{chunk-WYO5VN2L.js → chunk-X52TMBHU.js} +1 -1
- package/dist/cli/chunks/{chunk-6EHVPFHW.js → chunk-XIE7OHML.js} +1 -1
- package/dist/cli/chunks/{chunk-LOLAEDZD.js → chunk-XNEEYXR2.js} +2 -2
- package/dist/cli/chunks/{chunk-EY5IUF7U.js → chunk-XNHB6TLC.js} +2 -2
- package/dist/cli/chunks/{chunk-GJVRQBLI.js → chunk-XQRE2HWM.js} +2 -2
- package/dist/cli/chunks/{chunk-33IUCP34.js → chunk-Y2E3IJ53.js} +2 -2
- package/dist/cli/chunks/{chunk-6HO3XRXE.js → chunk-Y2WMPEUP.js} +2 -2
- package/dist/cli/chunks/{chunk-LGA6J4I4.js → chunk-Y7GPLOXB.js} +2 -2
- package/dist/cli/chunks/{chunk-76PRW2KY.js → chunk-YINFES3E.js} +2 -2
- package/dist/cli/chunks/{chunk-LJRZAALA.js → chunk-ZLBBX24Q.js} +133 -133
- package/dist/cli/chunks/{chunk-ASKPTGUU.js → chunk-ZT3GUYM5.js} +2 -2
- package/dist/cli/chunks/{chunk-XHCCH7UC.js → chunk-ZTC5FBL6.js} +3 -3
- package/dist/cli/chunks/ci-KP4GZKJ7.js +81 -0
- package/dist/cli/chunks/ci-config-XDDZOOEI.js +2 -0
- package/dist/cli/chunks/{ci-output-BMJABAEX.js → ci-output-7ZUQI4U6.js} +2 -2
- package/dist/cli/chunks/{circuit-breaker-GYD5CMJQ.js → circuit-breaker-ALXKCFAL.js} +2 -2
- package/dist/cli/chunks/{claude-flow-setup-MMOYVBVD.js → claude-flow-setup-RL7K2NI2.js} +2 -2
- package/dist/cli/chunks/client-C3KHVRM6.js +2 -0
- package/dist/cli/chunks/{cline-installer-B2SOZ44R.js → cline-installer-2Z2QQKC3.js} +2 -2
- package/dist/cli/chunks/{code-V3FIRAXD.js → code-QSZTDINQ.js} +4 -4
- package/dist/cli/chunks/{code-index-extractor-CV6A756Z.js → code-index-extractor-5CGR5D3Y.js} +2 -2
- package/dist/cli/chunks/{codex-installer-EUC6J47Y.js → codex-installer-7JLSEDE5.js} +2 -2
- package/dist/cli/chunks/{completions-DQQIBAHR.js → completions-U4NBSXEB.js} +2 -2
- package/dist/cli/chunks/{complexity-analyzer-HPXMXP5I.js → complexity-analyzer-WYZD2YSY.js} +2 -2
- package/dist/cli/chunks/{continuedev-installer-RCIYXH6V.js → continuedev-installer-UPXINJFT.js} +2 -2
- package/dist/cli/chunks/{copilot-installer-VUH32SHF.js → copilot-installer-WIVYQRIF.js} +2 -2
- package/dist/cli/chunks/core-handlers-KSQM6Q6S.js +2 -0
- package/dist/cli/chunks/{cost-tracker-YLCKLOAL.js → cost-tracker-RIN4JDDM.js} +2 -2
- package/dist/cli/chunks/coverage-SPFVHRRU.js +43 -0
- package/dist/cli/chunks/coverage-target-GSRBNHXE.js +2 -0
- package/dist/cli/chunks/cross-domain-router-CTGNOVCF.js +2 -0
- package/dist/cli/chunks/{cursor-installer-B42FN62K.js → cursor-installer-POM7NGCC.js} +2 -2
- package/dist/cli/chunks/{daemon-DECQRQII.js → daemon-HFGKQ6LB.js} +3 -3
- package/dist/cli/chunks/{daemon-IPTOSCHM.js → daemon-M5TD5Y6T.js} +4 -4
- package/dist/cli/chunks/{dag-attention-scheduler-RY7CDYSC.js → dag-attention-scheduler-HJXM7Q5P.js} +2 -2
- package/dist/cli/chunks/{detect-NW53BMJN.js → detect-XKQGLZYN.js} +2 -2
- package/dist/cli/chunks/{dist-node-LE6KHXAB.js → dist-node-SSKB53QL.js} +2 -2
- package/dist/cli/chunks/{domain-handler-JGXFT4LW.js → domain-handler-6BNDHATU.js} +2 -2
- package/dist/cli/chunks/{domain-plugins-GY3RYKKK.js → domain-plugins-DOX54YE3.js} +2 -2
- package/dist/cli/chunks/{domain-transfer-CGBRXNTC.js → domain-transfer-FW4ZX3Z7.js} +2 -2
- package/dist/cli/chunks/dream-MIK2E4UL.js +2 -0
- package/dist/cli/chunks/{embed-and-insert-pattern-65H4XHWR.js → embed-and-insert-pattern-QST77T2B.js} +2 -2
- package/dist/cli/chunks/{eval-YPYIZQUR.js → eval-K7E4XXBS.js} +2 -2
- package/dist/cli/chunks/{experience-capture-middleware-4JXVF5PL.js → experience-capture-middleware-X66IYSRV.js} +3 -3
- package/dist/cli/chunks/{fast-paths-HYQTAF2S.js → fast-paths-5JXXTYAG.js} +2 -2
- package/dist/cli/chunks/{feature-flags-HNSAFFX2.js → feature-flags-2CR3CO23.js} +2 -2
- package/dist/cli/chunks/{feature-flags-YACUH6OW.js → feature-flags-RB72LHOJ.js} +2 -2
- package/dist/cli/chunks/{file-discovery-R73BYEXB.js → file-discovery-3NHRUQQB.js} +2 -2
- package/dist/cli/chunks/{fleet-6M3BP4OO.js → fleet-SDU7QEOA.js} +3 -3
- package/dist/cli/chunks/{gnn-wrapper-QLD7KHXV.js → gnn-wrapper-AWCFODUV.js} +2 -2
- package/dist/cli/chunks/{heartbeat-handler-EZP333X4.js → heartbeat-handler-A4PJ3NFP.js} +4 -4
- package/dist/cli/chunks/heartbeat-scheduler-OCKRACUW.js +2 -0
- package/dist/cli/chunks/hnsw-adapter-X67XG73C.js +2 -0
- package/dist/cli/chunks/hnsw-index-I7KCQBQR.js +2 -0
- package/dist/cli/chunks/{hnsw-legacy-bridge-47CUCSAU.js → hnsw-legacy-bridge-KRS7SSFM.js} +2 -2
- package/dist/cli/chunks/{hnswlib-node-SVNYDJZH.js → hnswlib-node-OGGCPKPO.js} +2 -2
- package/dist/cli/chunks/{hooks-6V5LMWP6.js → hooks-XNRNMUBW.js} +10 -10
- package/dist/cli/chunks/hybrid-backend-FOUHTKAK.js +2 -0
- package/dist/cli/chunks/{hypergraph-engine-L2FOBIFF.js → hypergraph-engine-X35LTXYT.js} +2 -2
- package/dist/cli/chunks/{hypergraph-handler-DU6Z57EE.js → hypergraph-handler-M6THKAG6.js} +3 -3
- package/dist/cli/chunks/impact-analyzer-GXYVX46O.js +2 -0
- package/dist/cli/chunks/{init-handler-HETPWU67.js → init-handler-2AXMXHYF.js} +6 -6
- package/dist/cli/chunks/init-wizard-C2MRX2QG.js +2 -0
- package/dist/cli/chunks/kernel-Q2IUDIDC.js +2 -0
- package/dist/cli/chunks/{kilocode-installer-NHKYGJ7Q.js → kilocode-installer-TEPR2ZJD.js} +2 -2
- package/dist/cli/chunks/{kiro-installer-24UK6GGR.js → kiro-installer-RUKHGDEE.js} +2 -2
- package/dist/cli/chunks/knowledge-graph-E2Z6K5EE.js +2 -0
- package/dist/cli/chunks/{learning-QLVRZNHT.js → learning-BEA26RNA.js} +3 -3
- package/dist/cli/chunks/{llm-router-XRMI2VMG.js → llm-router-7GECMDCP.js} +4 -4
- package/dist/cli/chunks/llm-router-service-YATYFEDD.js +2 -0
- package/dist/cli/chunks/{load-W5UWL5C7.js → load-NTJ4Y4ZR.js} +2 -2
- package/dist/cli/chunks/load-test-LV77JXWA.js +2 -0
- package/dist/cli/chunks/{mcp-N7ALQ4BL.js → mcp-4335GTTG.js} +2 -2
- package/dist/cli/chunks/{memory-WV52DLSA.js → memory-HYC4M4XS.js} +5 -5
- package/dist/cli/chunks/memory-backend-XME3X3QX.js +2 -0
- package/dist/cli/chunks/memory-handlers-VG47X5DX.js +2 -0
- package/dist/cli/chunks/{multi-model-executor-3FVTZCSO.js → multi-model-executor-T7FYKM6T.js} +2 -2
- package/dist/cli/chunks/{opencode-installer-NTPWGHUW.js → opencode-installer-3USXVT5M.js} +2 -2
- package/dist/cli/chunks/{orchestrator-ORGJ6VAA.js → orchestrator-VHUXUYIM.js} +5 -5
- package/dist/cli/chunks/pattern-null-store-TAXMLENP.js +2 -0
- package/dist/cli/chunks/{pattern-usage-recorder-WWXHUH5U.js → pattern-usage-recorder-CMZLPX2K.js} +2 -2
- package/dist/cli/chunks/{pipeline-ZOKZFXTH.js → pipeline-BTRZOVSL.js} +2 -2
- package/dist/cli/chunks/{platform-CDNL322F.js → platform-2MCRKC25.js} +2 -2
- package/dist/cli/chunks/{plugin-ZVLFNZZ4.js → plugin-2VAYWAO3.js} +2 -2
- package/dist/cli/chunks/{prime-radiant-advanced-wasm-FVJDU2JY.js → prime-radiant-advanced-wasm-TXFEMBP6.js} +2 -2
- package/dist/cli/chunks/{project-root-UCHQH3QL.js → project-root-CFFPBGPX.js} +2 -2
- package/dist/cli/chunks/protocol-executor-ML76QAVQ.js +2 -0
- package/dist/cli/chunks/{protocol-handler-4YBN5R23.js → protocol-handler-DZ26OYVZ.js} +2 -2
- package/dist/cli/chunks/{prove-BHDHY664.js → prove-QWONEJWX.js} +2 -2
- package/dist/cli/chunks/qe-reasoning-bank-LUZH6RYV.js +2 -0
- package/dist/cli/chunks/quality-WFH54FY7.js +8 -0
- package/dist/cli/chunks/{quality-gate-WANE7TCN.js → quality-gate-ONJB7XU7.js} +3 -3
- package/dist/cli/chunks/queen-coordinator-EWXNIXB6.js +2 -0
- package/dist/cli/chunks/{real-embeddings-56MNGJGJ.js → real-embeddings-GZV4VKK4.js} +2 -2
- package/dist/cli/chunks/{ref-validation-S7UWXQIP.js → ref-validation-HW5Y55ZC.js} +2 -2
- package/dist/cli/chunks/{roocode-installer-TEYOQYL6.js → roocode-installer-5OABDNZT.js} +2 -2
- package/dist/cli/chunks/router-MXOBVEBF.js +2 -0
- package/dist/cli/chunks/routing-feedback-T6CCRUEK.js +2 -0
- package/dist/cli/chunks/{routing-handler-KDTLMRWD.js → routing-handler-CVHVOQTC.js} +2 -2
- package/dist/cli/chunks/{ruvector-commands-AOUDXCYE.js → ruvector-commands-KKDKFGIQ.js} +2 -2
- package/dist/cli/chunks/rvf-dual-writer-WHXVT3V3.js +2 -0
- package/dist/cli/chunks/rvf-migration-adapter-JFPWIRCG.js +2 -0
- package/dist/cli/chunks/{rvf-migration-coordinator-V74MVBHS.js → rvf-migration-coordinator-D4KUBNRZ.js} +2 -2
- package/dist/cli/chunks/rvf-native-adapter-KOXR7TJL.js +2 -0
- package/dist/cli/chunks/{rvf-store-integrity-QI47Y354.js → rvf-store-integrity-UGUXKIFX.js} +2 -2
- package/dist/cli/chunks/safe-db-CI6IPSIN.js +2 -0
- package/dist/cli/chunks/schedule-X4HW6TVJ.js +2 -0
- package/dist/cli/chunks/scheduler-KCNJ2ON3.js +2 -0
- package/dist/cli/chunks/{security-SN6MWOJT.js → security-7723AZZ4.js} +3 -3
- package/dist/cli/chunks/{shared-rvf-adapter-2LB56LP4.js → shared-rvf-adapter-KEQ5CLNI.js} +2 -2
- package/dist/cli/chunks/{shared-rvf-dual-writer-WU7PFKQM.js → shared-rvf-dual-writer-ZSHYG5IA.js} +2 -2
- package/dist/cli/chunks/sqlite-persistence-PGHU6VUR.js +2 -0
- package/dist/cli/chunks/{standalone-memory-F6FSRFWC.js → standalone-memory-MHZEEYMK.js} +2 -2
- package/dist/cli/chunks/{status-handler-DHD5MS4G.js → status-handler-JKXPJ22K.js} +2 -2
- package/dist/cli/chunks/{structural-health-P6HPSQMR.js → structural-health-J4RKCYWF.js} +2 -2
- package/dist/cli/chunks/{sync-SNAXAVCN.js → sync-EFWXMA2H.js} +2 -2
- package/dist/cli/chunks/{sync-LISJGDSE.js → sync-PDPORHVN.js} +2 -2
- package/dist/cli/chunks/{task-handler-ZT44NT5Z.js → task-handler-VERADM44.js} +2 -2
- package/dist/cli/chunks/{task-handlers-R6DQ5324.js → task-handlers-4RUCWUR2.js} +3 -3
- package/dist/cli/chunks/{test-MUFHR4JQ.js → test-DMBJFQNL.js} +4 -4
- package/dist/cli/chunks/{test-scheduling-TAUSLZJP.js → test-scheduling-LUPTVGCD.js} +3 -3
- package/dist/cli/chunks/{token-bootstrap-UGRH7OPU.js → token-bootstrap-S346YBVO.js} +2 -2
- package/dist/cli/chunks/{token-usage-IX65ECBR.js → token-usage-VOA34IKM.js} +2 -2
- package/dist/cli/chunks/{transformers-QKPSVW2W.js → transformers-GU4TYAN5.js} +2 -2
- package/dist/cli/chunks/{tree-sitter-wasm-parser-Z7PPA2JO.js → tree-sitter-wasm-parser-MZPXAH7C.js} +2 -2
- package/dist/cli/chunks/{types-ULNNGGGH.js → types-K4FLXR2K.js} +2 -2
- package/dist/cli/chunks/unified-memory-6CK3TUNQ.js +2 -0
- package/dist/cli/chunks/unified-memory-hnsw-A7BWBGYN.js +2 -0
- package/dist/cli/chunks/unified-persistence-GEPMDBQZ.js +2 -0
- package/dist/cli/chunks/{upgrade-ECI3YUYA.js → upgrade-M3ATDRMK.js} +2 -2
- package/dist/cli/chunks/{validate-3JKRUW2S.js → validate-AAP773B3.js} +2 -2
- package/dist/cli/chunks/{validate-swarm-LZWANCIE.js → validate-swarm-7RGAZX5F.js} +2 -2
- package/dist/cli/chunks/visual-security-L4J7LQWT.js +2 -0
- package/dist/cli/chunks/{web-tree-sitter-N5V4A6XG.js → web-tree-sitter-GQMRHGGI.js} +2 -2
- package/dist/cli/chunks/{windsurf-installer-5TBM5B3C.js → windsurf-installer-HG6HF2GF.js} +2 -2
- package/dist/cli/chunks/witness-chain-CDUMX623.js +2 -0
- package/dist/cli/chunks/{witness-chain-MFP3HBWQ.js → witness-chain-WKU3ZIBQ.js} +2 -2
- package/dist/cli/chunks/{witness-chain-repair-5QWRFVUK.js → witness-chain-repair-WNYH6O4L.js} +2 -2
- package/dist/cli/chunks/{witness-chain-verifier-SRAT7LVY.js → witness-chain-verifier-Q2KW3QEQ.js} +2 -2
- package/dist/cli/chunks/{witness-key-manager-FYZXQTLJ.js → witness-key-manager-NQ5DAAC4.js} +2 -2
- package/dist/cli/chunks/{workflow-M7TUYYXY.js → workflow-CX6SHFBW.js} +4 -4
- package/dist/cli/chunks/workflow-orchestrator-USJDWVDR.js +2 -0
- package/dist/cli/chunks/{wrappers-COU73OQ6.js → wrappers-CZDI6Z3X.js} +2 -2
- package/dist/cli/commands/ci.js +14 -6
- package/dist/cli/commands/code.js +1 -0
- package/dist/cli/commands/coverage.d.ts +28 -0
- package/dist/cli/commands/coverage.js +258 -143
- package/dist/cli/commands/quality.d.ts +12 -1
- package/dist/cli/commands/quality.js +51 -3
- package/dist/cli/utils/ci-output.d.ts +14 -4
- package/dist/cli/utils/ci-output.js +21 -4
- package/dist/cli/utils/coverage-data.d.ts +7 -0
- package/dist/cli/utils/coverage-data.js +21 -27
- package/dist/contracts/verdicts.d.ts +14 -0
- package/dist/contracts/verdicts.js +44 -3
- package/dist/coordination/handlers/coverage-handlers.js +121 -120
- package/dist/coordination/handlers/coverage-runner.d.ts +121 -0
- package/dist/coordination/handlers/coverage-runner.js +367 -0
- package/dist/coordination/handlers/coverage-target.d.ts +144 -0
- package/dist/coordination/handlers/coverage-target.js +311 -0
- package/dist/coordination/handlers/handler-utils.d.ts +9 -0
- package/dist/coordination/handlers/handler-utils.js +44 -1
- package/dist/coordination/handlers/quality-handlers.js +101 -15
- package/dist/coordination/result-saver.js +17 -3
- package/dist/coordination/task-executor.d.ts +5 -0
- package/dist/coordination/task-executor.js +7 -0
- package/dist/domains/code-intelligence/coordinator.d.ts +2 -1
- package/dist/domains/code-intelligence/coordinator.js +10 -32
- package/dist/domains/code-intelligence/interfaces.d.ts +5 -0
- package/dist/domains/code-intelligence/plugin.js +1 -0
- package/dist/domains/code-intelligence/project-root-resolver.d.ts +50 -0
- package/dist/domains/code-intelligence/project-root-resolver.js +121 -0
- package/dist/domains/coverage-analysis/coordinator.js +4 -1
- package/dist/domains/coverage-analysis/interfaces.d.ts +15 -0
- package/dist/domains/coverage-analysis/plugin.js +7 -1
- package/dist/domains/coverage-analysis/services/coverage-analyzer.d.ts +7 -0
- package/dist/domains/coverage-analysis/services/coverage-analyzer.js +29 -9
- package/dist/domains/quality-assessment/coordinator-claim-verifier.js +2 -1
- package/dist/domains/quality-assessment/coordinator.js +8 -8
- package/dist/domains/quality-assessment/coverage-report-event.d.ts +25 -0
- package/dist/domains/quality-assessment/coverage-report-event.js +36 -0
- package/dist/domains/quality-assessment/evidence-sufficiency.d.ts +57 -0
- package/dist/domains/quality-assessment/evidence-sufficiency.js +97 -0
- package/dist/domains/quality-assessment/index.d.ts +1 -1
- package/dist/domains/quality-assessment/index.js +1 -1
- package/dist/domains/quality-assessment/plugin.js +8 -8
- package/dist/domains/quality-assessment/quality-evidence.d.ts +50 -2
- package/dist/domains/quality-assessment/quality-evidence.js +90 -8
- package/dist/domains/quality-assessment/services/quality-analyzer.js +13 -9
- package/dist/mcp/bundle.js +456 -451
- package/dist/mcp/handlers/domain-handler-configs.d.ts +18 -1
- package/dist/mcp/handlers/domain-handler-configs.js +42 -4
- package/dist/mcp/handlers/handler-factory.js +3 -0
- package/dist/mcp/handlers/wrapped-domain-handlers.js +7 -1
- package/dist/mcp/protocol-server.js +3 -2
- package/dist/mcp/tools/coverage-analysis/index.js +1 -0
- package/dist/mcp/types.d.ts +6 -0
- package/dist/shared/events/domain-events.d.ts +2 -0
- package/package.json +1 -1
- package/dist/cli/chunks/adapter-T54G3TBK.js +0 -2
- package/dist/cli/chunks/aqe-learning-engine-HLCJJ3JM.js +0 -2
- package/dist/cli/chunks/base-VLD755UU.js +0 -2
- package/dist/cli/chunks/browser-workflow-6M6VLWRF.js +0 -2
- package/dist/cli/chunks/chunk-5XXLWJU7.js +0 -2
- package/dist/cli/chunks/chunk-HEDLZWPM.js +0 -96
- package/dist/cli/chunks/chunk-XKIFNKHV.js +0 -2
- package/dist/cli/chunks/chunk-ZF5JTQEE.js +0 -4
- package/dist/cli/chunks/ci-NGMGB43T.js +0 -82
- package/dist/cli/chunks/client-FUHNNK5O.js +0 -2
- package/dist/cli/chunks/core-handlers-F7TLX4LM.js +0 -2
- package/dist/cli/chunks/coverage-IVKDV236.js +0 -27
- package/dist/cli/chunks/cross-domain-router-EGWPY67O.js +0 -2
- package/dist/cli/chunks/dream-V4QN6OF5.js +0 -2
- package/dist/cli/chunks/heartbeat-scheduler-3N3L6CRM.js +0 -2
- package/dist/cli/chunks/hnsw-adapter-2BXJYDXI.js +0 -2
- package/dist/cli/chunks/hnsw-index-2DLI5OVO.js +0 -2
- package/dist/cli/chunks/hybrid-backend-OW4J65YH.js +0 -2
- package/dist/cli/chunks/impact-analyzer-ZH5I2RG6.js +0 -2
- package/dist/cli/chunks/init-wizard-NIKM3ZOK.js +0 -2
- package/dist/cli/chunks/kernel-2EOSVM6X.js +0 -2
- package/dist/cli/chunks/knowledge-graph-D3BPWNNO.js +0 -2
- package/dist/cli/chunks/llm-router-service-TJPPZFUN.js +0 -2
- package/dist/cli/chunks/load-test-RFT7IDOL.js +0 -2
- package/dist/cli/chunks/memory-backend-O2O4CFKZ.js +0 -2
- package/dist/cli/chunks/memory-handlers-DAEGUDBD.js +0 -2
- package/dist/cli/chunks/pattern-null-store-TUBNXS34.js +0 -2
- package/dist/cli/chunks/protocol-executor-6FQTTO3Y.js +0 -2
- package/dist/cli/chunks/qe-reasoning-bank-TKPND56C.js +0 -2
- package/dist/cli/chunks/quality-46AK277P.js +0 -7
- package/dist/cli/chunks/queen-coordinator-DWMXLPJG.js +0 -2
- package/dist/cli/chunks/router-QIMUYRCC.js +0 -2
- package/dist/cli/chunks/routing-feedback-4OH5ZNF4.js +0 -2
- package/dist/cli/chunks/rvf-dual-writer-S6JBZL54.js +0 -2
- package/dist/cli/chunks/rvf-migration-adapter-TSGMLMYR.js +0 -2
- package/dist/cli/chunks/rvf-native-adapter-Z7QOW47S.js +0 -2
- package/dist/cli/chunks/safe-db-JLOVBJNC.js +0 -2
- package/dist/cli/chunks/schedule-IM6XOBGR.js +0 -2
- package/dist/cli/chunks/scheduler-XL2SVVK5.js +0 -2
- package/dist/cli/chunks/sqlite-persistence-LWMIIC4U.js +0 -2
- package/dist/cli/chunks/unified-memory-F5IJY7W7.js +0 -2
- package/dist/cli/chunks/unified-memory-hnsw-C3WAMAQL.js +0 -2
- package/dist/cli/chunks/unified-persistence-EWXXOTCB.js +0 -2
- package/dist/cli/chunks/visual-security-CKLTAIAB.js +0 -2
- package/dist/cli/chunks/witness-chain-Z6LWFEZS.js +0 -2
- package/dist/cli/chunks/workflow-orchestrator-OFTAR2NA.js +0 -2
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coverage target resolution (issue #995).
|
|
3
|
+
*
|
|
4
|
+
* A coverage target can be the project root, a subfolder of it, a single source
|
|
5
|
+
* file, or an existing coverage report. Before #995 every target was treated as
|
|
6
|
+
* if it were the project root: `target: "src/"` ran vitest FROM `src/` (which
|
|
7
|
+
* found no tests, reported 0%, and wrote `src/coverage/` into the source tree),
|
|
8
|
+
* and `target: "coverage/lcov.info"` was searched for a `coverage/` folder
|
|
9
|
+
* inside a file and came back empty.
|
|
10
|
+
*
|
|
11
|
+
* This module answers the three questions every collector needs first:
|
|
12
|
+
* 1. Which project does the target belong to? (`findCoverageProjectRoot`)
|
|
13
|
+
* 2. Is the target itself a coverage report? (`isCoverageReportFile` /
|
|
14
|
+
* `loadCoverageReportFile`)
|
|
15
|
+
* 3. Which entries of a project-wide report belong to the target?
|
|
16
|
+
* (`scopeCoverageToTarget`)
|
|
17
|
+
*
|
|
18
|
+
* Kept free of task-executor and child-process imports so both the MCP handler
|
|
19
|
+
* and the CLI `coverage` command can share it.
|
|
20
|
+
*/
|
|
21
|
+
import * as fs from 'fs';
|
|
22
|
+
import * as os from 'os';
|
|
23
|
+
import * as path from 'path';
|
|
24
|
+
import { loadCoverageData, parseCoberturaXml, parseCoverageJson, parseCoverageSummaryJson, parseLcovInfo, } from './handler-utils.js';
|
|
25
|
+
import { isTestPath } from './coverage-collection.js';
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Project root
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Files whose presence marks a directory as a project root, i.e. the directory
|
|
31
|
+
* a test/coverage tool must be run from. `.git` bounds the walk for projects
|
|
32
|
+
* without a manifest.
|
|
33
|
+
*/
|
|
34
|
+
const PROJECT_ROOT_MARKERS = [
|
|
35
|
+
'package.json',
|
|
36
|
+
'pyproject.toml', 'setup.py', 'setup.cfg',
|
|
37
|
+
'go.mod', 'Cargo.toml',
|
|
38
|
+
'pom.xml', 'build.gradle', 'build.gradle.kts',
|
|
39
|
+
'.git',
|
|
40
|
+
];
|
|
41
|
+
/**
|
|
42
|
+
* Walk up from a target to the nearest directory that holds a project marker.
|
|
43
|
+
*
|
|
44
|
+
* NEAREST rather than topmost: in a monorepo, `packages/core/src` belongs to
|
|
45
|
+
* `packages/core` (its own package.json and test config), not the workspace
|
|
46
|
+
* root. This intentionally differs from `kernel/findProjectRoot`, which locates
|
|
47
|
+
* the AQE data root (honouring AQE_PROJECT_ROOT and `.agentic-qe`).
|
|
48
|
+
*
|
|
49
|
+
* Returns `null` when the target does not exist (a typo must not silently
|
|
50
|
+
* resolve to — and run tests for — an ancestor project) or when no marker is
|
|
51
|
+
* found below the home directory / filesystem root within `maxDepth` levels.
|
|
52
|
+
*/
|
|
53
|
+
export function findCoverageProjectRoot(target, maxDepth = 12) {
|
|
54
|
+
let dir;
|
|
55
|
+
try {
|
|
56
|
+
const resolved = path.resolve(target);
|
|
57
|
+
if (!fs.existsSync(resolved))
|
|
58
|
+
return null;
|
|
59
|
+
const real = fs.realpathSync(resolved);
|
|
60
|
+
dir = fs.statSync(real).isDirectory() ? real : path.dirname(real);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
let home = null;
|
|
66
|
+
try {
|
|
67
|
+
home = fs.realpathSync(os.homedir());
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
home = null;
|
|
71
|
+
}
|
|
72
|
+
const start = dir;
|
|
73
|
+
for (let depth = 0; depth <= maxDepth; depth++) {
|
|
74
|
+
// A stray manifest in $HOME must not claim every directory beneath it.
|
|
75
|
+
// $HOME is still a valid root when it is the target itself.
|
|
76
|
+
if (home !== null && dir === home && dir !== start)
|
|
77
|
+
return null;
|
|
78
|
+
if (PROJECT_ROOT_MARKERS.some(m => fs.existsSync(path.join(dir, m))))
|
|
79
|
+
return dir;
|
|
80
|
+
const parent = path.dirname(dir);
|
|
81
|
+
if (parent === dir)
|
|
82
|
+
return null;
|
|
83
|
+
dir = parent;
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
// ============================================================================
|
|
88
|
+
// Coverage report files as targets
|
|
89
|
+
// ============================================================================
|
|
90
|
+
const REPORT_BASENAMES = new Set([
|
|
91
|
+
'lcov.info',
|
|
92
|
+
'coverage-final.json',
|
|
93
|
+
'coverage-summary.json',
|
|
94
|
+
'coverage.xml',
|
|
95
|
+
'cobertura-coverage.xml',
|
|
96
|
+
'cobertura.xml',
|
|
97
|
+
]);
|
|
98
|
+
/** Is this path (by name) a coverage report rather than source code? */
|
|
99
|
+
export function isCoverageReportFile(filePath) {
|
|
100
|
+
const base = path.basename(filePath).toLowerCase();
|
|
101
|
+
return REPORT_BASENAMES.has(base) || base.endsWith('.lcov') || base.endsWith('.lcov.info');
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parse a coverage report file of any supported format.
|
|
105
|
+
*
|
|
106
|
+
* Returns `null` — never an empty "0%" report — when the file can't be read,
|
|
107
|
+
* can't be parsed, or lists no files.
|
|
108
|
+
*/
|
|
109
|
+
export async function loadCoverageReportFile(filePath) {
|
|
110
|
+
let content;
|
|
111
|
+
try {
|
|
112
|
+
content = await fs.promises.readFile(filePath, 'utf-8');
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
if (content.trim().length === 0)
|
|
118
|
+
return null;
|
|
119
|
+
try {
|
|
120
|
+
const base = path.basename(filePath).toLowerCase();
|
|
121
|
+
let data;
|
|
122
|
+
if (base.endsWith('.json')) {
|
|
123
|
+
const parsed = JSON.parse(content);
|
|
124
|
+
// coverage-summary.json carries a `total` entry with per-metric totals.
|
|
125
|
+
if (isSummaryShape(parsed))
|
|
126
|
+
data = parseCoverageSummaryJson(content);
|
|
127
|
+
// Anything else must be istanbul's per-file map. Raw V8 output
|
|
128
|
+
// (`{ result: [...] }`) and arbitrary JSON are rejected, not parsed into
|
|
129
|
+
// a phantom file called "result" with 0/0 coverage.
|
|
130
|
+
else if (isIstanbulShape(parsed))
|
|
131
|
+
data = parseCoverageJson(content);
|
|
132
|
+
else
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
else if (base.endsWith('.xml')) {
|
|
136
|
+
data = parseCoberturaXml(content);
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
data = parseLcovInfo(content);
|
|
140
|
+
}
|
|
141
|
+
return data.files.length > 0 ? data : null;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function isIstanbulShape(parsed) {
|
|
148
|
+
const entries = Object.values(parsed);
|
|
149
|
+
return entries.length > 0 && entries.every(e => typeof e === 'object' && e !== null && !Array.isArray(e) &&
|
|
150
|
+
typeof e.s === 'object' &&
|
|
151
|
+
typeof e.statementMap === 'object');
|
|
152
|
+
}
|
|
153
|
+
function isSummaryShape(parsed) {
|
|
154
|
+
const total = parsed.total;
|
|
155
|
+
return typeof total === 'object' && total !== null && typeof total.lines === 'object';
|
|
156
|
+
}
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Scoping
|
|
159
|
+
// ============================================================================
|
|
160
|
+
/** True when `child` is `parent` or inside it (path-segment aware). */
|
|
161
|
+
function isWithin(child, parent) {
|
|
162
|
+
const rel = path.relative(parent, child);
|
|
163
|
+
return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
|
|
164
|
+
}
|
|
165
|
+
/** Canonicalise a path without requiring it to exist (macOS /tmp → /private/tmp). */
|
|
166
|
+
function canonical(p) {
|
|
167
|
+
try {
|
|
168
|
+
return fs.realpathSync(p);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
return path.resolve(p);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Keep only the report entries that lie under `target`.
|
|
176
|
+
*
|
|
177
|
+
* Report paths come in two shapes: absolute (istanbul `coverage-final.json`)
|
|
178
|
+
* and project-root-relative (vitest/jest `lcov.info` writes `SF:src/math.ts`).
|
|
179
|
+
* Relative entries are resolved against `root` before comparing. Entry paths are
|
|
180
|
+
* returned unchanged.
|
|
181
|
+
*/
|
|
182
|
+
export function scopeCoverageToTarget(data, root, target) {
|
|
183
|
+
const rootAbs = canonical(root);
|
|
184
|
+
const targetAbs = canonical(target);
|
|
185
|
+
if (targetAbs === rootAbs)
|
|
186
|
+
return [...data.files];
|
|
187
|
+
return data.files.filter(f => {
|
|
188
|
+
const abs = path.isAbsolute(f.path) ? canonical(f.path) : path.resolve(rootAbs, f.path);
|
|
189
|
+
return isWithin(abs, targetAbs);
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Decide what a report says about `target`. Shared by the MCP handler and the
|
|
194
|
+
* CLI so the two cannot disagree (#995 review B2).
|
|
195
|
+
*
|
|
196
|
+
* "Unmeasured" is decided ONLY from evidence that nothing was measured:
|
|
197
|
+
* - the runner's own result says it ran 0 tests (`numTotalTests === 0`), or
|
|
198
|
+
* - the report has no entry under the target.
|
|
199
|
+
* Hit counts are never used: a file listed with 0 hits is a measured 0%,
|
|
200
|
+
* whether the report came from our run or from the user (#995 review B3).
|
|
201
|
+
*/
|
|
202
|
+
export function evaluateCoverageForTarget(report, projectRoot, target, options = {}) {
|
|
203
|
+
if (options.numTotalTests === 0)
|
|
204
|
+
return { status: 'no-data', reason: 'no-tests-ran' };
|
|
205
|
+
const scoped = scopeCoverageToTarget(report, projectRoot, target);
|
|
206
|
+
if (scoped.length === 0)
|
|
207
|
+
return { status: 'no-data', reason: 'target-not-covered' };
|
|
208
|
+
// Coverage OF a test file is not production coverage (#569). A report with
|
|
209
|
+
// only test entries yields no production figure — never the unfiltered set.
|
|
210
|
+
const production = scoped.filter(f => !isTestPath(f.path, projectRoot));
|
|
211
|
+
if (production.length === 0)
|
|
212
|
+
return { status: 'no-data', reason: 'test-only-report' };
|
|
213
|
+
return { status: 'measured', files: production };
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Resolve a target to coverage that already exists on disk, without running
|
|
217
|
+
* anything:
|
|
218
|
+
* - a coverage report passed as the target is parsed and analysed whole;
|
|
219
|
+
* - otherwise the PROJECT ROOT's report (not the cwd's, not the subfolder's)
|
|
220
|
+
* is scoped to the target;
|
|
221
|
+
* - `no-report` tells the caller it may run tooling from `projectRoot`.
|
|
222
|
+
*/
|
|
223
|
+
export async function resolveExistingCoverage(targetPath) {
|
|
224
|
+
const target = path.resolve(targetPath);
|
|
225
|
+
let stat;
|
|
226
|
+
try {
|
|
227
|
+
stat = fs.statSync(target);
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
return { status: 'target-missing', target };
|
|
231
|
+
}
|
|
232
|
+
if (stat.isFile() && isCoverageReportFile(target)) {
|
|
233
|
+
const reportDir = path.dirname(target);
|
|
234
|
+
const projectRoot = findCoverageProjectRoot(reportDir)
|
|
235
|
+
?? (path.basename(reportDir) === 'coverage' ? path.dirname(reportDir) : reportDir);
|
|
236
|
+
const report = await loadCoverageReportFile(target);
|
|
237
|
+
const evaluation = report
|
|
238
|
+
? evaluateCoverageForTarget(report, projectRoot, projectRoot)
|
|
239
|
+
: { status: 'no-data', reason: 'unreadable-report' };
|
|
240
|
+
return { status: 'evaluated', target, projectRoot, source: 'report-file', evaluation };
|
|
241
|
+
}
|
|
242
|
+
const projectRoot = findCoverageProjectRoot(target) ?? (stat.isDirectory() ? target : path.dirname(target));
|
|
243
|
+
const existing = await loadCoverageData(projectRoot);
|
|
244
|
+
if (!existing)
|
|
245
|
+
return { status: 'no-report', target, projectRoot };
|
|
246
|
+
return {
|
|
247
|
+
status: 'evaluated', target, projectRoot, source: 'project-report',
|
|
248
|
+
evaluation: evaluateCoverageForTarget(existing, projectRoot, target),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Threshold verdict over the metrics that were actually collected. Shared by
|
|
253
|
+
* MCP and CLI (#995 review NB5): an uncollected metric (no branch data in the
|
|
254
|
+
* report) is null, and a null must not drag the average down as a fake 0.
|
|
255
|
+
*/
|
|
256
|
+
export function thresholdVerdict(summary, threshold, collected) {
|
|
257
|
+
const metrics = [summary.line, summary.statement];
|
|
258
|
+
if (collected.branch)
|
|
259
|
+
metrics.push(summary.branch);
|
|
260
|
+
if (collected.function)
|
|
261
|
+
metrics.push(summary.function);
|
|
262
|
+
const overall = metrics.reduce((a, b) => a + b, 0) / metrics.length;
|
|
263
|
+
return { overall, meetsThreshold: overall >= threshold };
|
|
264
|
+
}
|
|
265
|
+
const SHORTFALL_RE = /^Coverage is [\d.]+% below threshold/i;
|
|
266
|
+
/**
|
|
267
|
+
* Make the analyzer's recommendations agree with `thresholdVerdict`
|
|
268
|
+
* (#995 review NB-A). The analyzer computes its "Coverage is X% below
|
|
269
|
+
* threshold" line from a 4-metric average that counts an uncollected metric as
|
|
270
|
+
* 0 — so a line-only report at 90% got "Met" next to "35.0% below threshold".
|
|
271
|
+
* Here the shortfall line is recomputed from the collected-metrics verdict,
|
|
272
|
+
* and advice about a metric that was never collected is dropped.
|
|
273
|
+
*/
|
|
274
|
+
export function reconcileRecommendations(recommendations, verdict, threshold, collected) {
|
|
275
|
+
const kept = recommendations.filter(rec => !SHORTFALL_RE.test(rec) &&
|
|
276
|
+
(collected.branch || !/branch coverage/i.test(rec)) &&
|
|
277
|
+
(collected.function || !/function coverage/i.test(rec)));
|
|
278
|
+
if (verdict.meetsThreshold)
|
|
279
|
+
return kept;
|
|
280
|
+
return [
|
|
281
|
+
`Coverage is ${(threshold - verdict.overall).toFixed(1)}% below threshold. Focus on high-risk files first.`,
|
|
282
|
+
...kept,
|
|
283
|
+
];
|
|
284
|
+
}
|
|
285
|
+
/** Is this verdict "no measurement exists" (as opposed to an error or test-only report)? */
|
|
286
|
+
export function isUnmeasured(reason) {
|
|
287
|
+
return reason === 'target-not-covered' || reason === 'no-tests-ran';
|
|
288
|
+
}
|
|
289
|
+
/** Caller-facing explanation of a no-data verdict. Shared by MCP and CLI. */
|
|
290
|
+
export function describeNoData(reason, target, projectRoot) {
|
|
291
|
+
switch (reason) {
|
|
292
|
+
case 'target-missing':
|
|
293
|
+
return `Target does not exist: ${target}`;
|
|
294
|
+
case 'unreadable-report':
|
|
295
|
+
return `Unreadable coverage report: ${target}. It could not be parsed or lists no files. ` +
|
|
296
|
+
'Supported: lcov.info, istanbul coverage-final.json / coverage-summary.json, Cobertura XML. ' +
|
|
297
|
+
"Raw V8 output is not supported — convert it with c8 or your runner's istanbul reporter.";
|
|
298
|
+
case 'test-only-report':
|
|
299
|
+
return 'The coverage report contains only test files, so there is no production ' +
|
|
300
|
+
'coverage to report. Coverage OF a test file is not a meaningful metric. ' +
|
|
301
|
+
'Check that your coverage tool is instrumenting your source directory.';
|
|
302
|
+
case 'target-not-covered':
|
|
303
|
+
return `Target is UNMEASURED: the coverage data for project ${projectRoot ?? '?'} has no entry ` +
|
|
304
|
+
`for any production file under ${target}. This is not 0% coverage — no measurement exists ` +
|
|
305
|
+
"for it. Check your coverage tool's include/exclude settings, or regenerate the report.";
|
|
306
|
+
case 'no-tests-ran':
|
|
307
|
+
return `Target is UNMEASURED: the test runner reported running 0 tests from ${projectRoot ?? '?'}. ` +
|
|
308
|
+
'This is not 0% coverage.';
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=coverage-target.js.map
|
|
@@ -78,6 +78,15 @@ export declare function loadCoverageData(targetPath: string): Promise<CoverageDa
|
|
|
78
78
|
* Parse Istanbul/nyc coverage-final.json format
|
|
79
79
|
*/
|
|
80
80
|
export declare function parseCoverageJson(content: string): CoverageData;
|
|
81
|
+
/**
|
|
82
|
+
* Parse istanbul `coverage-summary.json` (the `json-summary` reporter).
|
|
83
|
+
*
|
|
84
|
+
* Shape: `{ total: {...}, "<file>": { lines, statements, functions, branches } }`
|
|
85
|
+
* where each metric is `{ total, covered, skipped, pct }`. There is no
|
|
86
|
+
* per-line detail, so `uncoveredLines` is empty — a summary can report a
|
|
87
|
+
* figure but cannot locate a gap (#995).
|
|
88
|
+
*/
|
|
89
|
+
export declare function parseCoverageSummaryJson(content: string): CoverageData;
|
|
81
90
|
/**
|
|
82
91
|
* Parse LCOV info format
|
|
83
92
|
*/
|
|
@@ -21,6 +21,8 @@ export async function loadCoverageData(targetPath) {
|
|
|
21
21
|
path.join(targetPath, 'coverage', 'lcov.info'),
|
|
22
22
|
path.join(targetPath, '.nyc_output', 'coverage-final.json'),
|
|
23
23
|
path.join(targetPath, 'coverage-final.json'),
|
|
24
|
+
// Summary-only (per-file totals, no line detail) — last JS resort (#995)
|
|
25
|
+
path.join(targetPath, 'coverage', 'coverage-summary.json'),
|
|
24
26
|
// Python (pytest-cov, coverage.py)
|
|
25
27
|
path.join(targetPath, 'coverage.xml'),
|
|
26
28
|
path.join(targetPath, 'htmlcov', 'status.json'),
|
|
@@ -30,7 +32,10 @@ export async function loadCoverageData(targetPath) {
|
|
|
30
32
|
for (const coveragePath of coverageLocations) {
|
|
31
33
|
try {
|
|
32
34
|
const content = await fs.readFile(coveragePath, 'utf-8');
|
|
33
|
-
if (coveragePath.endsWith('.json')) {
|
|
35
|
+
if (coveragePath.endsWith('coverage-summary.json')) {
|
|
36
|
+
return parseCoverageSummaryJson(content);
|
|
37
|
+
}
|
|
38
|
+
else if (coveragePath.endsWith('.json')) {
|
|
34
39
|
return parseCoverageJson(content);
|
|
35
40
|
}
|
|
36
41
|
else if (coveragePath.endsWith('.info')) {
|
|
@@ -121,6 +126,44 @@ export function parseCoverageJson(content) {
|
|
|
121
126
|
},
|
|
122
127
|
};
|
|
123
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Parse istanbul `coverage-summary.json` (the `json-summary` reporter).
|
|
131
|
+
*
|
|
132
|
+
* Shape: `{ total: {...}, "<file>": { lines, statements, functions, branches } }`
|
|
133
|
+
* where each metric is `{ total, covered, skipped, pct }`. There is no
|
|
134
|
+
* per-line detail, so `uncoveredLines` is empty — a summary can report a
|
|
135
|
+
* figure but cannot locate a gap (#995).
|
|
136
|
+
*/
|
|
137
|
+
export function parseCoverageSummaryJson(content) {
|
|
138
|
+
const data = safeJsonParse(content);
|
|
139
|
+
const pick = (m) => ({ covered: Number(m?.covered) || 0, total: Number(m?.total) || 0 });
|
|
140
|
+
const files = [];
|
|
141
|
+
for (const [filePath, entry] of Object.entries(data)) {
|
|
142
|
+
if (filePath === 'total' || typeof entry !== 'object' || entry === null || !entry.lines)
|
|
143
|
+
continue;
|
|
144
|
+
files.push({
|
|
145
|
+
path: filePath,
|
|
146
|
+
lines: pick(entry.lines),
|
|
147
|
+
branches: pick(entry.branches),
|
|
148
|
+
functions: pick(entry.functions),
|
|
149
|
+
statements: pick(entry.statements ?? entry.lines),
|
|
150
|
+
uncoveredLines: [],
|
|
151
|
+
uncoveredBranches: [],
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const sum = (get) => files.reduce((acc, f) => ({ covered: acc.covered + get(f).covered, total: acc.total + get(f).total }), { covered: 0, total: 0 });
|
|
155
|
+
const pct = (m) => (m.total > 0 ? (m.covered / m.total) * 100 : 0);
|
|
156
|
+
return {
|
|
157
|
+
files,
|
|
158
|
+
summary: {
|
|
159
|
+
line: pct(sum(f => f.lines)),
|
|
160
|
+
branch: pct(sum(f => f.branches)),
|
|
161
|
+
function: pct(sum(f => f.functions)),
|
|
162
|
+
statement: pct(sum(f => f.statements)),
|
|
163
|
+
files: files.length,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
124
167
|
/**
|
|
125
168
|
* Parse LCOV info format
|
|
126
169
|
*/
|
|
@@ -5,9 +5,59 @@
|
|
|
5
5
|
* Covers: assess-quality
|
|
6
6
|
*/
|
|
7
7
|
import { ok, err } from '../../shared/types/index.js';
|
|
8
|
-
import { toError } from '../../shared/error-utils.js';
|
|
8
|
+
import { CallerInputError, toError } from '../../shared/error-utils.js';
|
|
9
9
|
import { discoverSourceFiles } from './handler-utils.js';
|
|
10
|
-
import { evaluateQualityEvidence, loadQualityEvidence, } from '../../domains/quality-assessment/quality-evidence.js';
|
|
10
|
+
import { INSUFFICIENT_EVIDENCE, describeMissingMetrics, evaluateQualityEvidence, loadQualityEvidence, } from '../../domains/quality-assessment/quality-evidence.js';
|
|
11
|
+
import { assessEvidenceSufficiency, describeMeasuredFailure, findMeasuredFailures, isMeasuredMetricValue, resolveCoverageOverride, validateRequestedMetrics, } from '../../domains/quality-assessment/evidence-sufficiency.js';
|
|
12
|
+
const DEFAULT_STATIC_METRICS = ['coverage', 'complexity', 'maintainability', 'testability'];
|
|
13
|
+
const UNMEASURED = -1;
|
|
14
|
+
const STATIC_ONLY_NOTE = 'Static analysis only; no quality gate was evaluated. '
|
|
15
|
+
+ 'Call quality_assess with runGate: true (or `aqe quality --gate`) for a pass/fail gate on measured evidence.';
|
|
16
|
+
/** #996: report the -1 sentinel instead of a fabricated number for unmeasured values. */
|
|
17
|
+
function sentinel(value) {
|
|
18
|
+
return isMeasuredMetricValue(value) ? value : UNMEASURED;
|
|
19
|
+
}
|
|
20
|
+
/** #996 (N3): reject a malformed `metrics` list instead of spreading a string into characters. */
|
|
21
|
+
function requestedMetricsFrom(metrics) {
|
|
22
|
+
if (metrics === undefined)
|
|
23
|
+
return DEFAULT_STATIC_METRICS;
|
|
24
|
+
try {
|
|
25
|
+
return validateRequestedMetrics(metrics);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
throw new CallerInputError(toError(error).message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* #996: the user-configured gate coverage threshold (`quality_gate.thresholds.coverage`
|
|
33
|
+
* in .aqe-ci.yml), found the same way `aqe ci` finds its config. Undefined when
|
|
34
|
+
* there is no readable config.
|
|
35
|
+
*/
|
|
36
|
+
async function configuredCoverageThreshold(startDir) {
|
|
37
|
+
try {
|
|
38
|
+
const { findCIConfigFile, parseCIConfigFile } = await import('../../cli/utils/ci-config.js');
|
|
39
|
+
const configPath = findCIConfigFile(startDir);
|
|
40
|
+
if (!configPath)
|
|
41
|
+
return undefined;
|
|
42
|
+
const parsed = parseCIConfigFile(configPath);
|
|
43
|
+
return parsed.success ? parsed.config?.qualityGate.thresholds.coverage : undefined;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* #996: coverage limit for measured failures — the request's threshold, else
|
|
51
|
+
* the configured gate threshold, else the canonical default (undefined).
|
|
52
|
+
*/
|
|
53
|
+
async function coverageOverrideFor(payload) {
|
|
54
|
+
const requested = payload.thresholdSource === 'default' ? undefined : payload.threshold;
|
|
55
|
+
if (requested !== undefined && (typeof requested !== 'number' || !Number.isFinite(requested)
|
|
56
|
+
|| requested < 0 || requested > 100)) {
|
|
57
|
+
throw new CallerInputError('threshold must be a finite number between 0 and 100.');
|
|
58
|
+
}
|
|
59
|
+
return resolveCoverageOverride(requested, requested === undefined ? await configuredCoverageThreshold(process.cwd()) : undefined);
|
|
60
|
+
}
|
|
11
61
|
export function registerQualityHandlers(ctx) {
|
|
12
62
|
// Register quality assessment handler - REAL IMPLEMENTATION
|
|
13
63
|
ctx.registerHandler('assess-quality', async (task) => {
|
|
@@ -15,13 +65,20 @@ export function registerQualityHandlers(ctx) {
|
|
|
15
65
|
try {
|
|
16
66
|
// An explicit gate evaluates the same measured, timestamped evidence as
|
|
17
67
|
// `aqe quality --gate`. Static source scores cannot substitute for it.
|
|
68
|
+
// Missing/stale/malformed evidence throws InsufficientQualityEvidenceError,
|
|
69
|
+
// whose details (status, unmeasured) travel with the failure.
|
|
18
70
|
if (payload.runGate === true) {
|
|
19
71
|
const measured = await loadQualityEvidence(ctx.memory);
|
|
20
72
|
return ok({
|
|
21
73
|
...evaluateQualityEvidence(measured),
|
|
22
74
|
metrics: measured,
|
|
75
|
+
evidenceStatus: 'measured',
|
|
76
|
+
unmeasured: [],
|
|
77
|
+
gateEvaluated: true,
|
|
23
78
|
});
|
|
24
79
|
}
|
|
80
|
+
const requestedMetrics = requestedMetricsFrom(payload.metrics);
|
|
81
|
+
const coverageOverride = await coverageOverrideFor(payload);
|
|
25
82
|
const analyzer = ctx.getQualityAnalyzer();
|
|
26
83
|
const threshold = payload.threshold || 80;
|
|
27
84
|
// Determine source files to analyze
|
|
@@ -42,47 +99,76 @@ export function registerQualityHandlers(ctx) {
|
|
|
42
99
|
sourceFiles = await discoverSourceFiles(targetDir, { includeTests: false });
|
|
43
100
|
}
|
|
44
101
|
if (sourceFiles.length === 0) {
|
|
102
|
+
// Nothing was analyzed: every requested metric is unmeasured (#996).
|
|
45
103
|
return ok({
|
|
46
104
|
qualityScore: 0,
|
|
47
105
|
passed: false,
|
|
48
106
|
threshold,
|
|
49
107
|
metrics: {
|
|
50
|
-
coverage:
|
|
51
|
-
complexity:
|
|
52
|
-
maintainability:
|
|
53
|
-
testability:
|
|
108
|
+
coverage: UNMEASURED,
|
|
109
|
+
complexity: UNMEASURED,
|
|
110
|
+
maintainability: UNMEASURED,
|
|
111
|
+
testability: UNMEASURED,
|
|
54
112
|
},
|
|
55
|
-
recommendations: [
|
|
113
|
+
recommendations: [
|
|
114
|
+
...describeMissingMetrics(requestedMetrics),
|
|
115
|
+
'No source files found for quality assessment',
|
|
116
|
+
],
|
|
56
117
|
warning: 'No source files found',
|
|
118
|
+
evidenceStatus: INSUFFICIENT_EVIDENCE,
|
|
119
|
+
unmeasured: requestedMetrics,
|
|
120
|
+
measuredFailures: [],
|
|
121
|
+
gateEvaluated: false,
|
|
122
|
+
confidence: 0,
|
|
123
|
+
note: STATIC_ONLY_NOTE,
|
|
57
124
|
});
|
|
58
125
|
}
|
|
59
126
|
// Use the real QualityAnalyzerService
|
|
60
127
|
const result = await analyzer.analyzeQuality({
|
|
61
128
|
sourceFiles,
|
|
62
|
-
includeMetrics:
|
|
129
|
+
includeMetrics: requestedMetrics,
|
|
63
130
|
});
|
|
64
131
|
if (!result.success) {
|
|
65
132
|
return result;
|
|
66
133
|
}
|
|
67
134
|
const report = result.value;
|
|
68
|
-
|
|
69
|
-
//
|
|
135
|
+
// Only metrics the analyzer actually computed count as measured; score
|
|
136
|
+
// fields carry the -1 sentinel for the rest.
|
|
70
137
|
const metrics = {};
|
|
71
138
|
for (const metric of report.metrics) {
|
|
72
139
|
metrics[metric.name] = metric.value;
|
|
73
140
|
}
|
|
141
|
+
// #996: never pass on missing data. A static score computed without a
|
|
142
|
+
// requested metric (e.g. coverage = -1) is not evidence of quality, and
|
|
143
|
+
// a measured metric that fails its own limit blocks regardless (N2).
|
|
144
|
+
const sufficiency = assessEvidenceSufficiency(requestedMetrics, metrics);
|
|
145
|
+
const measuredFailures = findMeasuredFailures(sufficiency.measured, metrics, { coverage: coverageOverride });
|
|
146
|
+
const insufficient = sufficiency.evidenceStatus !== 'measured';
|
|
147
|
+
const passed = !insufficient && measuredFailures.length === 0 && report.score.overall >= threshold;
|
|
148
|
+
const recommendations = report.recommendations.map(r => `[${r.type}] ${r.title}: ${r.description}`);
|
|
74
149
|
return ok({
|
|
75
150
|
qualityScore: report.score.overall,
|
|
76
151
|
passed,
|
|
77
152
|
threshold,
|
|
78
153
|
metrics: {
|
|
79
|
-
coverage:
|
|
80
|
-
complexity:
|
|
81
|
-
maintainability:
|
|
82
|
-
security
|
|
154
|
+
coverage: sentinel(metrics.coverage),
|
|
155
|
+
complexity: sentinel(metrics.complexity),
|
|
156
|
+
maintainability: sentinel(metrics.maintainability),
|
|
157
|
+
// The analyzer has no security measurement; never report a placeholder.
|
|
158
|
+
security: sentinel(metrics.security),
|
|
83
159
|
...metrics,
|
|
84
160
|
},
|
|
85
|
-
recommendations:
|
|
161
|
+
recommendations: [
|
|
162
|
+
...measuredFailures.map(describeMeasuredFailure),
|
|
163
|
+
...describeMissingMetrics(sufficiency.unmeasured),
|
|
164
|
+
...recommendations,
|
|
165
|
+
],
|
|
166
|
+
evidenceStatus: sufficiency.evidenceStatus,
|
|
167
|
+
unmeasured: sufficiency.unmeasured,
|
|
168
|
+
measuredFailures,
|
|
169
|
+
gateEvaluated: false,
|
|
170
|
+
confidence: sufficiency.confidence,
|
|
171
|
+
note: STATIC_ONLY_NOTE,
|
|
86
172
|
trends: report.trends.map(t => ({
|
|
87
173
|
metric: t.metric,
|
|
88
174
|
direction: t.direction,
|
|
@@ -466,10 +466,23 @@ ${data.recommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}
|
|
|
466
466
|
`;
|
|
467
467
|
}
|
|
468
468
|
generateQualityReport(data) {
|
|
469
|
+
// #996: unmeasured metrics (-1 sentinel) are not scores, and a static
|
|
470
|
+
// analysis run is not a gate — say both plainly.
|
|
471
|
+
const insufficient = (data.unmeasured?.length ?? 0) > 0 || data.evidenceStatus === 'insufficient-evidence';
|
|
472
|
+
const status = insufficient ? 'INSUFFICIENT EVIDENCE (not passed)' : data.passed ? 'PASSED' : 'FAILED';
|
|
473
|
+
const gateLine = data.gateEvaluated === false
|
|
474
|
+
? '\n**Quality gate:** Not evaluated (static analysis only)'
|
|
475
|
+
: '';
|
|
476
|
+
const unmeasuredLine = insufficient && data.unmeasured?.length
|
|
477
|
+
? `\n**Unmeasured:** ${data.unmeasured.join(', ')}`
|
|
478
|
+
: '';
|
|
479
|
+
const noRecommendations = data.gateEvaluated === false
|
|
480
|
+
? 'No recommendations.'
|
|
481
|
+
: 'No recommendations - all quality gates passed.';
|
|
469
482
|
return `# Quality Assessment Report
|
|
470
483
|
|
|
471
484
|
**Generated:** ${new Date().toISOString()}
|
|
472
|
-
**Status:** ${
|
|
485
|
+
**Status:** ${status}${gateLine}${unmeasuredLine}
|
|
473
486
|
|
|
474
487
|
## Quality Score
|
|
475
488
|
|
|
@@ -479,11 +492,11 @@ ${typeof data.qualityScore === 'number' ? `**${data.qualityScore.toFixed(1)}** /
|
|
|
479
492
|
|
|
480
493
|
| Metric | Score |
|
|
481
494
|
|--------|-------|
|
|
482
|
-
${Object.entries(data.metrics).map(([k, v]) => `| ${k} | ${v.toFixed(1)} |`).join('\n')}
|
|
495
|
+
${Object.entries(data.metrics).map(([k, v]) => `| ${k} | ${Number.isFinite(v) && v >= 0 ? v.toFixed(1) : 'not measured'} |`).join('\n')}
|
|
483
496
|
|
|
484
497
|
## Recommendations
|
|
485
498
|
|
|
486
|
-
${data.recommendations.length === 0 ?
|
|
499
|
+
${data.recommendations.length === 0 ? noRecommendations : data.recommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}
|
|
487
500
|
`;
|
|
488
501
|
}
|
|
489
502
|
// ==========================================================================
|
|
@@ -544,6 +557,7 @@ ${data.recommendations.length === 0 ? 'No recommendations - all quality gates pa
|
|
|
544
557
|
return {
|
|
545
558
|
qualityScore: data.qualityScore,
|
|
546
559
|
passed: data.passed,
|
|
560
|
+
...(data.evidenceStatus !== undefined ? { evidenceStatus: data.evidenceStatus } : {}),
|
|
547
561
|
};
|
|
548
562
|
case 'generate-tests':
|
|
549
563
|
return {
|
|
@@ -44,6 +44,11 @@ export interface TaskResult {
|
|
|
44
44
|
domain: DomainName;
|
|
45
45
|
/** Files saved by result saver */
|
|
46
46
|
savedFiles?: string[];
|
|
47
|
+
/**
|
|
48
|
+
* #996: structured context for a failure (which quality metrics were
|
|
49
|
+
* unmeasured). Only set for InsufficientQualityEvidenceError; absent otherwise.
|
|
50
|
+
*/
|
|
51
|
+
errorDetails?: Record<string, unknown>;
|
|
47
52
|
}
|
|
48
53
|
export declare class DomainTaskExecutor implements TaskHandlerContext {
|
|
49
54
|
private readonly kernel;
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { v4 as uuidv4 } from 'uuid';
|
|
16
16
|
import { isCallerInputError, toErrorMessage } from '../shared/error-utils.js';
|
|
17
|
+
import { InsufficientQualityEvidenceError } from '../domains/quality-assessment/quality-evidence.js';
|
|
17
18
|
import { createResultSaver } from './result-saver.js';
|
|
18
19
|
import { createLogger } from '../logging/logger-factory.js';
|
|
19
20
|
import { getClaudeModelForTier } from '../shared/llm/model-registry.js';
|
|
@@ -53,6 +54,10 @@ function resolveQualityAnalyzerService(memory) {
|
|
|
53
54
|
const factory = DomainServiceRegistry.resolve(ServiceKeys.QualityAnalyzerService);
|
|
54
55
|
return factory(memory);
|
|
55
56
|
}
|
|
57
|
+
/** #996: structured details, only from the one error type that is known to carry them. */
|
|
58
|
+
function errorDetailsOf(error) {
|
|
59
|
+
return error instanceof InsufficientQualityEvidenceError ? { ...error.toDetails() } : undefined;
|
|
60
|
+
}
|
|
56
61
|
// ============================================================================
|
|
57
62
|
// ADR-051: Model Routing Support
|
|
58
63
|
// ============================================================================
|
|
@@ -439,12 +444,14 @@ export class DomainTaskExecutor {
|
|
|
439
444
|
await this.publishTaskFailed(task.id, errorMsg, domain, 'error' in result && isCallerInputError(result.error));
|
|
440
445
|
// ADR-051: Record failed outcome
|
|
441
446
|
this.recordOutcome(task, routingTier, false, Date.now() - startTime).catch((e) => { logger.warn('recordOutcome failed', { error: e instanceof Error ? e.message : String(e), taskId: task.id }); });
|
|
447
|
+
const errorDetails = 'error' in result ? errorDetailsOf(result.error) : undefined;
|
|
442
448
|
return {
|
|
443
449
|
taskId: task.id,
|
|
444
450
|
success: false,
|
|
445
451
|
error: errorMsg,
|
|
446
452
|
duration: Date.now() - startTime,
|
|
447
453
|
domain,
|
|
454
|
+
...(errorDetails ? { errorDetails } : {}),
|
|
448
455
|
};
|
|
449
456
|
}
|
|
450
457
|
await this.publishTaskCompleted(task.id, result.value, domain);
|
|
@@ -261,7 +261,8 @@ export declare class CodeIntelligenceCoordinator extends BaseDomainCoordinator<C
|
|
|
261
261
|
*/
|
|
262
262
|
getMetricCollector(): IMetricCollectorService | undefined;
|
|
263
263
|
/**
|
|
264
|
-
* Determine project root from indexed paths
|
|
264
|
+
* Determine project root from indexed paths (nearest marker, bottom-up; #994).
|
|
265
|
+
* Relative paths resolve against the process cwd, matching the CLI/MCP callers.
|
|
265
266
|
*/
|
|
266
267
|
private getProjectRootFromPaths;
|
|
267
268
|
/**
|