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.
Files changed (402) hide show
  1. package/.claude/skills/skills-manifest.json +1 -1
  2. package/CHANGELOG.md +70 -0
  3. package/assets/skills/skills-manifest.json +1 -1
  4. package/dist/bridge/captured-experience-bridge.js +7 -9
  5. package/dist/cli/bundle.js +5 -5
  6. package/dist/cli/chunks/adapter-EGIPTX5H.js +2 -0
  7. package/dist/cli/chunks/{agent-booster-wasm-MHJBRXMK.js → agent-booster-wasm-CCTTGCQS.js} +2 -2
  8. package/dist/cli/chunks/{agent-handler-G64SD7J2.js → agent-handler-DHYUF3NX.js} +2 -2
  9. package/dist/cli/chunks/{agent-memory-branch-G4UXFPKH.js → agent-memory-branch-642W7CYX.js} +2 -2
  10. package/dist/cli/chunks/aqe-learning-engine-WP4GCBDT.js +2 -0
  11. package/dist/cli/chunks/{arena-QJMKYPHS.js → arena-MUCC7UUN.js} +4 -4
  12. package/dist/cli/chunks/{arena-63M3M6EW.js → arena-NW2JVAFY.js} +2 -2
  13. package/dist/cli/chunks/{audit-UDFVTWSX.js → audit-VKQTZJAA.js} +3 -3
  14. package/dist/cli/chunks/base-6ORFD62I.js +2 -0
  15. package/dist/cli/chunks/{better-sqlite3-JLX2G56I.js → better-sqlite3-7ZUFVWLQ.js} +2 -2
  16. package/dist/cli/chunks/{brain-handler-PWF32LLW.js → brain-handler-7LKAWSBI.js} +3 -3
  17. package/dist/cli/chunks/{branch-enumerator-PB7QWXDP.js → branch-enumerator-HUNXJKM4.js} +2 -2
  18. package/dist/cli/chunks/{browser-CYNV2F3U.js → browser-ZSZEYUDP.js} +2 -2
  19. package/dist/cli/chunks/browser-workflow-PIXGERWP.js +2 -0
  20. package/dist/cli/chunks/{chunk-RWZ4F44Y.js → chunk-2KWI3MT3.js} +2 -2
  21. package/dist/cli/chunks/{chunk-UORI2CZ4.js → chunk-2WFTZSRT.js} +1 -1
  22. package/dist/cli/chunks/{chunk-HCDTLV5J.js → chunk-2XYTS2WY.js} +2 -2
  23. package/dist/cli/chunks/{chunk-DBIK4OFX.js → chunk-35MDATQE.js} +1 -1
  24. package/dist/cli/chunks/{chunk-54XTJBHV.js → chunk-3AYYGR6W.js} +1 -1
  25. package/dist/cli/chunks/{chunk-SJB4KMZS.js → chunk-3DTEQHFB.js} +3 -3
  26. package/dist/cli/chunks/{chunk-RG67RZIX.js → chunk-3OAQPN2N.js} +2 -2
  27. package/dist/cli/chunks/{chunk-2DRMTRBM.js → chunk-3QD7U3FY.js} +2 -2
  28. package/dist/cli/chunks/{chunk-VMSO6DMF.js → chunk-3SH23PS3.js} +2 -2
  29. package/dist/cli/chunks/{chunk-RJ5CZ7B6.js → chunk-3YASZPLO.js} +2 -2
  30. package/dist/cli/chunks/{chunk-JFQDWAU3.js → chunk-3YNP33GB.js} +2 -2
  31. package/dist/cli/chunks/{chunk-I53365HG.js → chunk-47GEI2LT.js} +3 -3
  32. package/dist/cli/chunks/{chunk-PCWZCXBD.js → chunk-4AZP7ZCW.js} +2 -2
  33. package/dist/cli/chunks/{chunk-54ITMOFM.js → chunk-4PT5KNXL.js} +4 -4
  34. package/dist/cli/chunks/{chunk-SIUUAKCE.js → chunk-4UXPDV7E.js} +1 -1
  35. package/dist/cli/chunks/{chunk-LZSLJJII.js → chunk-4XSCZVON.js} +2 -2
  36. package/dist/cli/chunks/{chunk-DFJGXXCL.js → chunk-5BOZCMXZ.js} +1 -1
  37. package/dist/cli/chunks/{chunk-B7J3O2WM.js → chunk-644DFAGS.js} +1 -1
  38. package/dist/cli/chunks/{chunk-2ICC4H6K.js → chunk-663ZFPUL.js} +2 -2
  39. package/dist/cli/chunks/{chunk-YUWUFYHK.js → chunk-6JFGB2JB.js} +1 -1
  40. package/dist/cli/chunks/{chunk-MBV57LKC.js → chunk-6LG5UEQE.js} +2 -2
  41. package/dist/cli/chunks/{chunk-LRTF5RMG.js → chunk-6UYN5PHA.js} +4 -4
  42. package/dist/cli/chunks/{chunk-5SS2Z7JC.js → chunk-6ZIDZ4TJ.js} +2 -2
  43. package/dist/cli/chunks/{chunk-2534PXIV.js → chunk-7BIYJRQJ.js} +2 -2
  44. package/dist/cli/chunks/{chunk-HX5KQK7I.js → chunk-7EZLOC4K.js} +2 -2
  45. package/dist/cli/chunks/{chunk-ZAOQIVHG.js → chunk-7YB2KELI.js} +2 -2
  46. package/dist/cli/chunks/{chunk-SOSJFEFB.js → chunk-A2WOQO2N.js} +2 -2
  47. package/dist/cli/chunks/{chunk-M6SJALLA.js → chunk-A337MD3C.js} +2 -2
  48. package/dist/cli/chunks/{chunk-YZ2AGC7O.js → chunk-A5J4XJS6.js} +1 -1
  49. package/dist/cli/chunks/{chunk-PEA4DVXW.js → chunk-AQN5EDBM.js} +1 -1
  50. package/dist/cli/chunks/{chunk-EGHK6NMH.js → chunk-AUZXRFOX.js} +2 -2
  51. package/dist/cli/chunks/{chunk-BBKLVHDQ.js → chunk-B2UHTA4Q.js} +1 -1
  52. package/dist/cli/chunks/{chunk-LY53L6NH.js → chunk-BBL6YXD3.js} +2 -2
  53. package/dist/cli/chunks/chunk-BJ5P4IV2.js +2 -0
  54. package/dist/cli/chunks/{chunk-I43QRLLB.js → chunk-BMWAWZAN.js} +2 -2
  55. package/dist/cli/chunks/chunk-BO4PKNYP.js +102 -0
  56. package/dist/cli/chunks/{chunk-PMXWDTDK.js → chunk-BY646JOM.js} +2 -2
  57. package/dist/cli/chunks/{chunk-OFQDM73J.js → chunk-BYXNHGQC.js} +2 -2
  58. package/dist/cli/chunks/{chunk-73MTUM6F.js → chunk-BZFT4QDQ.js} +2 -2
  59. package/dist/cli/chunks/{chunk-RA3MI5WK.js → chunk-CFPR24YK.js} +2 -2
  60. package/dist/cli/chunks/{chunk-WEDVLKRB.js → chunk-CQMHDAJS.js} +2 -2
  61. package/dist/cli/chunks/{chunk-HFSBONCS.js → chunk-CR2JUANW.js} +2 -2
  62. package/dist/cli/chunks/{chunk-BY3IL56R.js → chunk-CXOXUBYU.js} +1 -1
  63. package/dist/cli/chunks/{chunk-AOKSLWON.js → chunk-D3G2NN7F.js} +77 -78
  64. package/dist/cli/chunks/{chunk-I5A3HGO6.js → chunk-DEGWCUMY.js} +2 -2
  65. package/dist/cli/chunks/{chunk-CQ3WSTJ2.js → chunk-DFPL5N3Q.js} +2 -2
  66. package/dist/cli/chunks/{chunk-TVQPYP2Z.js → chunk-DL5JENWD.js} +3 -3
  67. package/dist/cli/chunks/{chunk-V3NZ23X6.js → chunk-DLY4YEN3.js} +2 -2
  68. package/dist/cli/chunks/{chunk-HEKCN5XQ.js → chunk-DPV33C77.js} +2 -2
  69. package/dist/cli/chunks/{chunk-KOLHZSEC.js → chunk-DPYDUKVZ.js} +2 -2
  70. package/dist/cli/chunks/{chunk-3MSBVOYW.js → chunk-DTJODGCE.js} +2 -2
  71. package/dist/cli/chunks/{chunk-MGH4XIHH.js → chunk-DWZI3IVB.js} +2 -2
  72. package/dist/cli/chunks/{chunk-DPL4ZXTE.js → chunk-DXVZBJE4.js} +1 -1
  73. package/dist/cli/chunks/{chunk-LXYTC62V.js → chunk-E4VIVMDL.js} +2 -2
  74. package/dist/cli/chunks/{chunk-DVE5SM5G.js → chunk-EAYQCBXL.js} +3 -3
  75. package/dist/cli/chunks/{chunk-BY5PLEPZ.js → chunk-EOR3RECZ.js} +2 -2
  76. package/dist/cli/chunks/chunk-ERGWWI4Y.js +3 -0
  77. package/dist/cli/chunks/{chunk-LPSE6DOQ.js → chunk-EUEHBERE.js} +1 -1
  78. package/dist/cli/chunks/{chunk-3BDDOFYW.js → chunk-EVCFUYYK.js} +2 -2
  79. package/dist/cli/chunks/{chunk-SZWXUG36.js → chunk-FBEXG4HD.js} +2 -2
  80. package/dist/cli/chunks/{chunk-5GWTJJEO.js → chunk-FE3AIZI6.js} +1 -1
  81. package/dist/cli/chunks/{chunk-4GLR2S2M.js → chunk-FHUJCVT3.js} +2 -2
  82. package/dist/cli/chunks/{chunk-6TMUZGM2.js → chunk-FNAYDITO.js} +1 -1
  83. package/dist/cli/chunks/{chunk-U2E5F3VZ.js → chunk-FQKEZVN6.js} +2 -2
  84. package/dist/cli/chunks/{chunk-2LL35V3A.js → chunk-FWYJ6UDX.js} +2 -2
  85. package/dist/cli/chunks/{chunk-ISASRHZP.js → chunk-GGDKOJVF.js} +2 -2
  86. package/dist/cli/chunks/{chunk-ZKXZTIVF.js → chunk-GKPRECLL.js} +2 -2
  87. package/dist/cli/chunks/{chunk-OHH6BUZK.js → chunk-GRIPCYW5.js} +1 -1
  88. package/dist/cli/chunks/{chunk-XZ5Z56OP.js → chunk-GTSOFCMQ.js} +1 -1
  89. package/dist/cli/chunks/{chunk-2MU4WCMQ.js → chunk-HBYQYGXP.js} +2 -2
  90. package/dist/cli/chunks/{chunk-YQ6BK7QC.js → chunk-HGBJ2QQ4.js} +2 -2
  91. package/dist/cli/chunks/{chunk-N6CRIUTO.js → chunk-HIUNCO2W.js} +2 -2
  92. package/dist/cli/chunks/{chunk-5H7MR6HP.js → chunk-HLFDC4SF.js} +1 -1
  93. package/dist/cli/chunks/{chunk-GAXTPQCZ.js → chunk-HPLMX62I.js} +2 -2
  94. package/dist/cli/chunks/{chunk-WO4NSPLX.js → chunk-IHFUEPZH.js} +2 -2
  95. package/dist/cli/chunks/{chunk-ADF6LGHZ.js → chunk-IJ2EXSMO.js} +2 -2
  96. package/dist/cli/chunks/{chunk-GLKLEHK3.js → chunk-IJOFZBXD.js} +2 -2
  97. package/dist/cli/chunks/{chunk-IKWAPHJA.js → chunk-JANLATHN.js} +2 -2
  98. package/dist/cli/chunks/{chunk-J3RNVHDH.js → chunk-JCC7QEKZ.js} +1 -1
  99. package/dist/cli/chunks/{chunk-C5EN5WB6.js → chunk-JI77KJP7.js} +1 -1
  100. package/dist/cli/chunks/{chunk-B7GJXQD2.js → chunk-JNMUVBAJ.js} +2 -2
  101. package/dist/cli/chunks/{chunk-ZP6ANUP7.js → chunk-JYY7AYGA.js} +1 -1
  102. package/dist/cli/chunks/{chunk-6HTO4BLV.js → chunk-KHCALIFP.js} +2 -2
  103. package/dist/cli/chunks/{chunk-BND6M2JX.js → chunk-KJ3KBP24.js} +2 -2
  104. package/dist/cli/chunks/{chunk-O7TMY4GS.js → chunk-KZVJW2SD.js} +2 -2
  105. package/dist/cli/chunks/chunk-L336HFFE.js +2 -0
  106. package/dist/cli/chunks/{chunk-W3PPYNHF.js → chunk-L375O3CZ.js} +2 -2
  107. package/dist/cli/chunks/{chunk-PJAXWCFC.js → chunk-L6SVCNXK.js} +1 -1
  108. package/dist/cli/chunks/{chunk-VAXQUD5G.js → chunk-LNO5IWGH.js} +1 -1
  109. package/dist/cli/chunks/{chunk-CFXFGQEN.js → chunk-LRASYSGV.js} +2 -2
  110. package/dist/cli/chunks/{chunk-MVTUFPM2.js → chunk-MB4VFAJC.js} +2 -2
  111. package/dist/cli/chunks/{chunk-SCL2OYN7.js → chunk-MNQ36MAF.js} +2 -2
  112. package/dist/cli/chunks/{chunk-W6TJXK5Y.js → chunk-MQGEZA2A.js} +2 -2
  113. package/dist/cli/chunks/{chunk-73PCVMGA.js → chunk-MS3Z2CP5.js} +2 -2
  114. package/dist/cli/chunks/{chunk-WI5AEB7Q.js → chunk-N2TJNIHA.js} +2 -2
  115. package/dist/cli/chunks/{chunk-DEJM2P5K.js → chunk-NF6AFIAX.js} +1 -1
  116. package/dist/cli/chunks/{chunk-JL3TI43V.js → chunk-NLDFNAOS.js} +2 -2
  117. package/dist/cli/chunks/{chunk-UK5HY6LC.js → chunk-NMJVG22Z.js} +1 -1
  118. package/dist/cli/chunks/{chunk-WW6WUMS7.js → chunk-OGYWWZU7.js} +2 -2
  119. package/dist/cli/chunks/{chunk-3C2BFLYC.js → chunk-ONCH5AXU.js} +3 -3
  120. package/dist/cli/chunks/{chunk-OG4KHX7S.js → chunk-OQDC7GCM.js} +2 -2
  121. package/dist/cli/chunks/{chunk-3ZIVBTX6.js → chunk-ORTBRO7F.js} +2 -2
  122. package/dist/cli/chunks/{chunk-NIJMLEAX.js → chunk-OU4ZAWCC.js} +2 -2
  123. package/dist/cli/chunks/{chunk-FDK2GF4G.js → chunk-OW42F2TX.js} +2 -2
  124. package/dist/cli/chunks/{chunk-TB22Y3WW.js → chunk-PCCIZFD7.js} +1 -1
  125. package/dist/cli/chunks/{chunk-BZWZTGMF.js → chunk-PDPEMBFV.js} +2 -2
  126. package/dist/cli/chunks/{chunk-D7RVZ5C2.js → chunk-PHAQWAEV.js} +2 -2
  127. package/dist/cli/chunks/{chunk-P7GM3EE7.js → chunk-PHP4UJYP.js} +1 -1
  128. package/dist/cli/chunks/{chunk-6QPW4OVH.js → chunk-PURHGLEC.js} +1 -1
  129. package/dist/cli/chunks/{chunk-BPGFCUDO.js → chunk-PVAE65B4.js} +1 -1
  130. package/dist/cli/chunks/{chunk-7X2IVMXN.js → chunk-QPV3OKX2.js} +2 -2
  131. package/dist/cli/chunks/{chunk-DAYMT7TH.js → chunk-QXY3IRBW.js} +3 -3
  132. package/dist/cli/chunks/{chunk-QIAX3JJB.js → chunk-RCXMUEZN.js} +2 -2
  133. package/dist/cli/chunks/{chunk-RB2SL5GD.js → chunk-RMJ7PJOS.js} +2 -2
  134. package/dist/cli/chunks/{chunk-FRBCX46J.js → chunk-RUJLVJDE.js} +2 -2
  135. package/dist/cli/chunks/{chunk-S5D3HCGN.js → chunk-RUSLEOMC.js} +2 -2
  136. package/dist/cli/chunks/chunk-SAPIRK2G.js +3 -0
  137. package/dist/cli/chunks/{chunk-A4OPQJ4A.js → chunk-SDWMLT7B.js} +5 -5
  138. package/dist/cli/chunks/{chunk-RJMBHZYL.js → chunk-SEZLYRCS.js} +1 -1
  139. package/dist/cli/chunks/{chunk-AHVN3UQZ.js → chunk-TDLKXLJC.js} +2 -2
  140. package/dist/cli/chunks/{chunk-EWUFVWRT.js → chunk-U4TV75AC.js} +2 -2
  141. package/dist/cli/chunks/{chunk-X5S5UEM6.js → chunk-U6OVPMQX.js} +2 -2
  142. package/dist/cli/chunks/{chunk-5B5D5KCV.js → chunk-UE22ODHN.js} +1 -1
  143. package/dist/cli/chunks/{chunk-I6E4X3L3.js → chunk-UXZEVIFX.js} +1 -1
  144. package/dist/cli/chunks/{chunk-DF4Y3HPO.js → chunk-V7GNUVYI.js} +1 -1
  145. package/dist/cli/chunks/{chunk-OOK6GPJR.js → chunk-VCKDVOPD.js} +2 -2
  146. package/dist/cli/chunks/{chunk-G3ZUB3HD.js → chunk-VH3EB6ZG.js} +2 -2
  147. package/dist/cli/chunks/{chunk-PB2WSCBW.js → chunk-VLOIEVER.js} +3 -3
  148. package/dist/cli/chunks/{chunk-7INP6HZH.js → chunk-VML4SG3X.js} +3 -3
  149. package/dist/cli/chunks/{chunk-K2VUYRMG.js → chunk-VRJPH3LG.js} +2 -2
  150. package/dist/cli/chunks/{chunk-RI4JPBZD.js → chunk-VYDNLQJN.js} +2 -2
  151. package/dist/cli/chunks/{chunk-35A22AUL.js → chunk-W3ZGNNDV.js} +1 -1
  152. package/dist/cli/chunks/{chunk-R47LNH5W.js → chunk-WC47GICZ.js} +2 -2
  153. package/dist/cli/chunks/{chunk-Z56QTRRB.js → chunk-WLIYWBQR.js} +2 -2
  154. package/dist/cli/chunks/{chunk-ZURYZEHX.js → chunk-WN55MNZ2.js} +2 -2
  155. package/dist/cli/chunks/chunk-WOZIXXY5.js +7 -0
  156. package/dist/cli/chunks/{chunk-NBMQOP2R.js → chunk-WR64BKUE.js} +1 -1
  157. package/dist/cli/chunks/{chunk-T3BMCEQR.js → chunk-WT3JC4A6.js} +2 -2
  158. package/dist/cli/chunks/{chunk-WYO5VN2L.js → chunk-X52TMBHU.js} +1 -1
  159. package/dist/cli/chunks/{chunk-6EHVPFHW.js → chunk-XIE7OHML.js} +1 -1
  160. package/dist/cli/chunks/{chunk-LOLAEDZD.js → chunk-XNEEYXR2.js} +2 -2
  161. package/dist/cli/chunks/{chunk-EY5IUF7U.js → chunk-XNHB6TLC.js} +2 -2
  162. package/dist/cli/chunks/{chunk-GJVRQBLI.js → chunk-XQRE2HWM.js} +2 -2
  163. package/dist/cli/chunks/{chunk-33IUCP34.js → chunk-Y2E3IJ53.js} +2 -2
  164. package/dist/cli/chunks/{chunk-6HO3XRXE.js → chunk-Y2WMPEUP.js} +2 -2
  165. package/dist/cli/chunks/{chunk-LGA6J4I4.js → chunk-Y7GPLOXB.js} +2 -2
  166. package/dist/cli/chunks/{chunk-76PRW2KY.js → chunk-YINFES3E.js} +2 -2
  167. package/dist/cli/chunks/{chunk-LJRZAALA.js → chunk-ZLBBX24Q.js} +133 -133
  168. package/dist/cli/chunks/{chunk-ASKPTGUU.js → chunk-ZT3GUYM5.js} +2 -2
  169. package/dist/cli/chunks/{chunk-XHCCH7UC.js → chunk-ZTC5FBL6.js} +3 -3
  170. package/dist/cli/chunks/ci-KP4GZKJ7.js +81 -0
  171. package/dist/cli/chunks/ci-config-XDDZOOEI.js +2 -0
  172. package/dist/cli/chunks/{ci-output-BMJABAEX.js → ci-output-7ZUQI4U6.js} +2 -2
  173. package/dist/cli/chunks/{circuit-breaker-GYD5CMJQ.js → circuit-breaker-ALXKCFAL.js} +2 -2
  174. package/dist/cli/chunks/{claude-flow-setup-MMOYVBVD.js → claude-flow-setup-RL7K2NI2.js} +2 -2
  175. package/dist/cli/chunks/client-C3KHVRM6.js +2 -0
  176. package/dist/cli/chunks/{cline-installer-B2SOZ44R.js → cline-installer-2Z2QQKC3.js} +2 -2
  177. package/dist/cli/chunks/{code-V3FIRAXD.js → code-QSZTDINQ.js} +4 -4
  178. package/dist/cli/chunks/{code-index-extractor-CV6A756Z.js → code-index-extractor-5CGR5D3Y.js} +2 -2
  179. package/dist/cli/chunks/{codex-installer-EUC6J47Y.js → codex-installer-7JLSEDE5.js} +2 -2
  180. package/dist/cli/chunks/{completions-DQQIBAHR.js → completions-U4NBSXEB.js} +2 -2
  181. package/dist/cli/chunks/{complexity-analyzer-HPXMXP5I.js → complexity-analyzer-WYZD2YSY.js} +2 -2
  182. package/dist/cli/chunks/{continuedev-installer-RCIYXH6V.js → continuedev-installer-UPXINJFT.js} +2 -2
  183. package/dist/cli/chunks/{copilot-installer-VUH32SHF.js → copilot-installer-WIVYQRIF.js} +2 -2
  184. package/dist/cli/chunks/core-handlers-KSQM6Q6S.js +2 -0
  185. package/dist/cli/chunks/{cost-tracker-YLCKLOAL.js → cost-tracker-RIN4JDDM.js} +2 -2
  186. package/dist/cli/chunks/coverage-SPFVHRRU.js +43 -0
  187. package/dist/cli/chunks/coverage-target-GSRBNHXE.js +2 -0
  188. package/dist/cli/chunks/cross-domain-router-CTGNOVCF.js +2 -0
  189. package/dist/cli/chunks/{cursor-installer-B42FN62K.js → cursor-installer-POM7NGCC.js} +2 -2
  190. package/dist/cli/chunks/{daemon-DECQRQII.js → daemon-HFGKQ6LB.js} +3 -3
  191. package/dist/cli/chunks/{daemon-IPTOSCHM.js → daemon-M5TD5Y6T.js} +4 -4
  192. package/dist/cli/chunks/{dag-attention-scheduler-RY7CDYSC.js → dag-attention-scheduler-HJXM7Q5P.js} +2 -2
  193. package/dist/cli/chunks/{detect-NW53BMJN.js → detect-XKQGLZYN.js} +2 -2
  194. package/dist/cli/chunks/{dist-node-LE6KHXAB.js → dist-node-SSKB53QL.js} +2 -2
  195. package/dist/cli/chunks/{domain-handler-JGXFT4LW.js → domain-handler-6BNDHATU.js} +2 -2
  196. package/dist/cli/chunks/{domain-plugins-GY3RYKKK.js → domain-plugins-DOX54YE3.js} +2 -2
  197. package/dist/cli/chunks/{domain-transfer-CGBRXNTC.js → domain-transfer-FW4ZX3Z7.js} +2 -2
  198. package/dist/cli/chunks/dream-MIK2E4UL.js +2 -0
  199. package/dist/cli/chunks/{embed-and-insert-pattern-65H4XHWR.js → embed-and-insert-pattern-QST77T2B.js} +2 -2
  200. package/dist/cli/chunks/{eval-YPYIZQUR.js → eval-K7E4XXBS.js} +2 -2
  201. package/dist/cli/chunks/{experience-capture-middleware-4JXVF5PL.js → experience-capture-middleware-X66IYSRV.js} +3 -3
  202. package/dist/cli/chunks/{fast-paths-HYQTAF2S.js → fast-paths-5JXXTYAG.js} +2 -2
  203. package/dist/cli/chunks/{feature-flags-HNSAFFX2.js → feature-flags-2CR3CO23.js} +2 -2
  204. package/dist/cli/chunks/{feature-flags-YACUH6OW.js → feature-flags-RB72LHOJ.js} +2 -2
  205. package/dist/cli/chunks/{file-discovery-R73BYEXB.js → file-discovery-3NHRUQQB.js} +2 -2
  206. package/dist/cli/chunks/{fleet-6M3BP4OO.js → fleet-SDU7QEOA.js} +3 -3
  207. package/dist/cli/chunks/{gnn-wrapper-QLD7KHXV.js → gnn-wrapper-AWCFODUV.js} +2 -2
  208. package/dist/cli/chunks/{heartbeat-handler-EZP333X4.js → heartbeat-handler-A4PJ3NFP.js} +4 -4
  209. package/dist/cli/chunks/heartbeat-scheduler-OCKRACUW.js +2 -0
  210. package/dist/cli/chunks/hnsw-adapter-X67XG73C.js +2 -0
  211. package/dist/cli/chunks/hnsw-index-I7KCQBQR.js +2 -0
  212. package/dist/cli/chunks/{hnsw-legacy-bridge-47CUCSAU.js → hnsw-legacy-bridge-KRS7SSFM.js} +2 -2
  213. package/dist/cli/chunks/{hnswlib-node-SVNYDJZH.js → hnswlib-node-OGGCPKPO.js} +2 -2
  214. package/dist/cli/chunks/{hooks-6V5LMWP6.js → hooks-XNRNMUBW.js} +10 -10
  215. package/dist/cli/chunks/hybrid-backend-FOUHTKAK.js +2 -0
  216. package/dist/cli/chunks/{hypergraph-engine-L2FOBIFF.js → hypergraph-engine-X35LTXYT.js} +2 -2
  217. package/dist/cli/chunks/{hypergraph-handler-DU6Z57EE.js → hypergraph-handler-M6THKAG6.js} +3 -3
  218. package/dist/cli/chunks/impact-analyzer-GXYVX46O.js +2 -0
  219. package/dist/cli/chunks/{init-handler-HETPWU67.js → init-handler-2AXMXHYF.js} +6 -6
  220. package/dist/cli/chunks/init-wizard-C2MRX2QG.js +2 -0
  221. package/dist/cli/chunks/kernel-Q2IUDIDC.js +2 -0
  222. package/dist/cli/chunks/{kilocode-installer-NHKYGJ7Q.js → kilocode-installer-TEPR2ZJD.js} +2 -2
  223. package/dist/cli/chunks/{kiro-installer-24UK6GGR.js → kiro-installer-RUKHGDEE.js} +2 -2
  224. package/dist/cli/chunks/knowledge-graph-E2Z6K5EE.js +2 -0
  225. package/dist/cli/chunks/{learning-QLVRZNHT.js → learning-BEA26RNA.js} +3 -3
  226. package/dist/cli/chunks/{llm-router-XRMI2VMG.js → llm-router-7GECMDCP.js} +4 -4
  227. package/dist/cli/chunks/llm-router-service-YATYFEDD.js +2 -0
  228. package/dist/cli/chunks/{load-W5UWL5C7.js → load-NTJ4Y4ZR.js} +2 -2
  229. package/dist/cli/chunks/load-test-LV77JXWA.js +2 -0
  230. package/dist/cli/chunks/{mcp-N7ALQ4BL.js → mcp-4335GTTG.js} +2 -2
  231. package/dist/cli/chunks/{memory-WV52DLSA.js → memory-HYC4M4XS.js} +5 -5
  232. package/dist/cli/chunks/memory-backend-XME3X3QX.js +2 -0
  233. package/dist/cli/chunks/memory-handlers-VG47X5DX.js +2 -0
  234. package/dist/cli/chunks/{multi-model-executor-3FVTZCSO.js → multi-model-executor-T7FYKM6T.js} +2 -2
  235. package/dist/cli/chunks/{opencode-installer-NTPWGHUW.js → opencode-installer-3USXVT5M.js} +2 -2
  236. package/dist/cli/chunks/{orchestrator-ORGJ6VAA.js → orchestrator-VHUXUYIM.js} +5 -5
  237. package/dist/cli/chunks/pattern-null-store-TAXMLENP.js +2 -0
  238. package/dist/cli/chunks/{pattern-usage-recorder-WWXHUH5U.js → pattern-usage-recorder-CMZLPX2K.js} +2 -2
  239. package/dist/cli/chunks/{pipeline-ZOKZFXTH.js → pipeline-BTRZOVSL.js} +2 -2
  240. package/dist/cli/chunks/{platform-CDNL322F.js → platform-2MCRKC25.js} +2 -2
  241. package/dist/cli/chunks/{plugin-ZVLFNZZ4.js → plugin-2VAYWAO3.js} +2 -2
  242. package/dist/cli/chunks/{prime-radiant-advanced-wasm-FVJDU2JY.js → prime-radiant-advanced-wasm-TXFEMBP6.js} +2 -2
  243. package/dist/cli/chunks/{project-root-UCHQH3QL.js → project-root-CFFPBGPX.js} +2 -2
  244. package/dist/cli/chunks/protocol-executor-ML76QAVQ.js +2 -0
  245. package/dist/cli/chunks/{protocol-handler-4YBN5R23.js → protocol-handler-DZ26OYVZ.js} +2 -2
  246. package/dist/cli/chunks/{prove-BHDHY664.js → prove-QWONEJWX.js} +2 -2
  247. package/dist/cli/chunks/qe-reasoning-bank-LUZH6RYV.js +2 -0
  248. package/dist/cli/chunks/quality-WFH54FY7.js +8 -0
  249. package/dist/cli/chunks/{quality-gate-WANE7TCN.js → quality-gate-ONJB7XU7.js} +3 -3
  250. package/dist/cli/chunks/queen-coordinator-EWXNIXB6.js +2 -0
  251. package/dist/cli/chunks/{real-embeddings-56MNGJGJ.js → real-embeddings-GZV4VKK4.js} +2 -2
  252. package/dist/cli/chunks/{ref-validation-S7UWXQIP.js → ref-validation-HW5Y55ZC.js} +2 -2
  253. package/dist/cli/chunks/{roocode-installer-TEYOQYL6.js → roocode-installer-5OABDNZT.js} +2 -2
  254. package/dist/cli/chunks/router-MXOBVEBF.js +2 -0
  255. package/dist/cli/chunks/routing-feedback-T6CCRUEK.js +2 -0
  256. package/dist/cli/chunks/{routing-handler-KDTLMRWD.js → routing-handler-CVHVOQTC.js} +2 -2
  257. package/dist/cli/chunks/{ruvector-commands-AOUDXCYE.js → ruvector-commands-KKDKFGIQ.js} +2 -2
  258. package/dist/cli/chunks/rvf-dual-writer-WHXVT3V3.js +2 -0
  259. package/dist/cli/chunks/rvf-migration-adapter-JFPWIRCG.js +2 -0
  260. package/dist/cli/chunks/{rvf-migration-coordinator-V74MVBHS.js → rvf-migration-coordinator-D4KUBNRZ.js} +2 -2
  261. package/dist/cli/chunks/rvf-native-adapter-KOXR7TJL.js +2 -0
  262. package/dist/cli/chunks/{rvf-store-integrity-QI47Y354.js → rvf-store-integrity-UGUXKIFX.js} +2 -2
  263. package/dist/cli/chunks/safe-db-CI6IPSIN.js +2 -0
  264. package/dist/cli/chunks/schedule-X4HW6TVJ.js +2 -0
  265. package/dist/cli/chunks/scheduler-KCNJ2ON3.js +2 -0
  266. package/dist/cli/chunks/{security-SN6MWOJT.js → security-7723AZZ4.js} +3 -3
  267. package/dist/cli/chunks/{shared-rvf-adapter-2LB56LP4.js → shared-rvf-adapter-KEQ5CLNI.js} +2 -2
  268. package/dist/cli/chunks/{shared-rvf-dual-writer-WU7PFKQM.js → shared-rvf-dual-writer-ZSHYG5IA.js} +2 -2
  269. package/dist/cli/chunks/sqlite-persistence-PGHU6VUR.js +2 -0
  270. package/dist/cli/chunks/{standalone-memory-F6FSRFWC.js → standalone-memory-MHZEEYMK.js} +2 -2
  271. package/dist/cli/chunks/{status-handler-DHD5MS4G.js → status-handler-JKXPJ22K.js} +2 -2
  272. package/dist/cli/chunks/{structural-health-P6HPSQMR.js → structural-health-J4RKCYWF.js} +2 -2
  273. package/dist/cli/chunks/{sync-SNAXAVCN.js → sync-EFWXMA2H.js} +2 -2
  274. package/dist/cli/chunks/{sync-LISJGDSE.js → sync-PDPORHVN.js} +2 -2
  275. package/dist/cli/chunks/{task-handler-ZT44NT5Z.js → task-handler-VERADM44.js} +2 -2
  276. package/dist/cli/chunks/{task-handlers-R6DQ5324.js → task-handlers-4RUCWUR2.js} +3 -3
  277. package/dist/cli/chunks/{test-MUFHR4JQ.js → test-DMBJFQNL.js} +4 -4
  278. package/dist/cli/chunks/{test-scheduling-TAUSLZJP.js → test-scheduling-LUPTVGCD.js} +3 -3
  279. package/dist/cli/chunks/{token-bootstrap-UGRH7OPU.js → token-bootstrap-S346YBVO.js} +2 -2
  280. package/dist/cli/chunks/{token-usage-IX65ECBR.js → token-usage-VOA34IKM.js} +2 -2
  281. package/dist/cli/chunks/{transformers-QKPSVW2W.js → transformers-GU4TYAN5.js} +2 -2
  282. package/dist/cli/chunks/{tree-sitter-wasm-parser-Z7PPA2JO.js → tree-sitter-wasm-parser-MZPXAH7C.js} +2 -2
  283. package/dist/cli/chunks/{types-ULNNGGGH.js → types-K4FLXR2K.js} +2 -2
  284. package/dist/cli/chunks/unified-memory-6CK3TUNQ.js +2 -0
  285. package/dist/cli/chunks/unified-memory-hnsw-A7BWBGYN.js +2 -0
  286. package/dist/cli/chunks/unified-persistence-GEPMDBQZ.js +2 -0
  287. package/dist/cli/chunks/{upgrade-ECI3YUYA.js → upgrade-M3ATDRMK.js} +2 -2
  288. package/dist/cli/chunks/{validate-3JKRUW2S.js → validate-AAP773B3.js} +2 -2
  289. package/dist/cli/chunks/{validate-swarm-LZWANCIE.js → validate-swarm-7RGAZX5F.js} +2 -2
  290. package/dist/cli/chunks/visual-security-L4J7LQWT.js +2 -0
  291. package/dist/cli/chunks/{web-tree-sitter-N5V4A6XG.js → web-tree-sitter-GQMRHGGI.js} +2 -2
  292. package/dist/cli/chunks/{windsurf-installer-5TBM5B3C.js → windsurf-installer-HG6HF2GF.js} +2 -2
  293. package/dist/cli/chunks/witness-chain-CDUMX623.js +2 -0
  294. package/dist/cli/chunks/{witness-chain-MFP3HBWQ.js → witness-chain-WKU3ZIBQ.js} +2 -2
  295. package/dist/cli/chunks/{witness-chain-repair-5QWRFVUK.js → witness-chain-repair-WNYH6O4L.js} +2 -2
  296. package/dist/cli/chunks/{witness-chain-verifier-SRAT7LVY.js → witness-chain-verifier-Q2KW3QEQ.js} +2 -2
  297. package/dist/cli/chunks/{witness-key-manager-FYZXQTLJ.js → witness-key-manager-NQ5DAAC4.js} +2 -2
  298. package/dist/cli/chunks/{workflow-M7TUYYXY.js → workflow-CX6SHFBW.js} +4 -4
  299. package/dist/cli/chunks/workflow-orchestrator-USJDWVDR.js +2 -0
  300. package/dist/cli/chunks/{wrappers-COU73OQ6.js → wrappers-CZDI6Z3X.js} +2 -2
  301. package/dist/cli/commands/ci.js +14 -6
  302. package/dist/cli/commands/code.js +1 -0
  303. package/dist/cli/commands/coverage.d.ts +28 -0
  304. package/dist/cli/commands/coverage.js +258 -143
  305. package/dist/cli/commands/quality.d.ts +12 -1
  306. package/dist/cli/commands/quality.js +51 -3
  307. package/dist/cli/utils/ci-output.d.ts +14 -4
  308. package/dist/cli/utils/ci-output.js +21 -4
  309. package/dist/cli/utils/coverage-data.d.ts +7 -0
  310. package/dist/cli/utils/coverage-data.js +21 -27
  311. package/dist/contracts/verdicts.d.ts +14 -0
  312. package/dist/contracts/verdicts.js +44 -3
  313. package/dist/coordination/handlers/coverage-handlers.js +121 -120
  314. package/dist/coordination/handlers/coverage-runner.d.ts +121 -0
  315. package/dist/coordination/handlers/coverage-runner.js +367 -0
  316. package/dist/coordination/handlers/coverage-target.d.ts +144 -0
  317. package/dist/coordination/handlers/coverage-target.js +311 -0
  318. package/dist/coordination/handlers/handler-utils.d.ts +9 -0
  319. package/dist/coordination/handlers/handler-utils.js +44 -1
  320. package/dist/coordination/handlers/quality-handlers.js +101 -15
  321. package/dist/coordination/result-saver.js +17 -3
  322. package/dist/coordination/task-executor.d.ts +5 -0
  323. package/dist/coordination/task-executor.js +7 -0
  324. package/dist/domains/code-intelligence/coordinator.d.ts +2 -1
  325. package/dist/domains/code-intelligence/coordinator.js +10 -32
  326. package/dist/domains/code-intelligence/interfaces.d.ts +5 -0
  327. package/dist/domains/code-intelligence/plugin.js +1 -0
  328. package/dist/domains/code-intelligence/project-root-resolver.d.ts +50 -0
  329. package/dist/domains/code-intelligence/project-root-resolver.js +121 -0
  330. package/dist/domains/coverage-analysis/coordinator.js +4 -1
  331. package/dist/domains/coverage-analysis/interfaces.d.ts +15 -0
  332. package/dist/domains/coverage-analysis/plugin.js +7 -1
  333. package/dist/domains/coverage-analysis/services/coverage-analyzer.d.ts +7 -0
  334. package/dist/domains/coverage-analysis/services/coverage-analyzer.js +29 -9
  335. package/dist/domains/quality-assessment/coordinator-claim-verifier.js +2 -1
  336. package/dist/domains/quality-assessment/coordinator.js +8 -8
  337. package/dist/domains/quality-assessment/coverage-report-event.d.ts +25 -0
  338. package/dist/domains/quality-assessment/coverage-report-event.js +36 -0
  339. package/dist/domains/quality-assessment/evidence-sufficiency.d.ts +57 -0
  340. package/dist/domains/quality-assessment/evidence-sufficiency.js +97 -0
  341. package/dist/domains/quality-assessment/index.d.ts +1 -1
  342. package/dist/domains/quality-assessment/index.js +1 -1
  343. package/dist/domains/quality-assessment/plugin.js +8 -8
  344. package/dist/domains/quality-assessment/quality-evidence.d.ts +50 -2
  345. package/dist/domains/quality-assessment/quality-evidence.js +90 -8
  346. package/dist/domains/quality-assessment/services/quality-analyzer.js +13 -9
  347. package/dist/mcp/bundle.js +456 -451
  348. package/dist/mcp/handlers/domain-handler-configs.d.ts +18 -1
  349. package/dist/mcp/handlers/domain-handler-configs.js +42 -4
  350. package/dist/mcp/handlers/handler-factory.js +3 -0
  351. package/dist/mcp/handlers/wrapped-domain-handlers.js +7 -1
  352. package/dist/mcp/protocol-server.js +3 -2
  353. package/dist/mcp/tools/coverage-analysis/index.js +1 -0
  354. package/dist/mcp/types.d.ts +6 -0
  355. package/dist/shared/events/domain-events.d.ts +2 -0
  356. package/package.json +1 -1
  357. package/dist/cli/chunks/adapter-T54G3TBK.js +0 -2
  358. package/dist/cli/chunks/aqe-learning-engine-HLCJJ3JM.js +0 -2
  359. package/dist/cli/chunks/base-VLD755UU.js +0 -2
  360. package/dist/cli/chunks/browser-workflow-6M6VLWRF.js +0 -2
  361. package/dist/cli/chunks/chunk-5XXLWJU7.js +0 -2
  362. package/dist/cli/chunks/chunk-HEDLZWPM.js +0 -96
  363. package/dist/cli/chunks/chunk-XKIFNKHV.js +0 -2
  364. package/dist/cli/chunks/chunk-ZF5JTQEE.js +0 -4
  365. package/dist/cli/chunks/ci-NGMGB43T.js +0 -82
  366. package/dist/cli/chunks/client-FUHNNK5O.js +0 -2
  367. package/dist/cli/chunks/core-handlers-F7TLX4LM.js +0 -2
  368. package/dist/cli/chunks/coverage-IVKDV236.js +0 -27
  369. package/dist/cli/chunks/cross-domain-router-EGWPY67O.js +0 -2
  370. package/dist/cli/chunks/dream-V4QN6OF5.js +0 -2
  371. package/dist/cli/chunks/heartbeat-scheduler-3N3L6CRM.js +0 -2
  372. package/dist/cli/chunks/hnsw-adapter-2BXJYDXI.js +0 -2
  373. package/dist/cli/chunks/hnsw-index-2DLI5OVO.js +0 -2
  374. package/dist/cli/chunks/hybrid-backend-OW4J65YH.js +0 -2
  375. package/dist/cli/chunks/impact-analyzer-ZH5I2RG6.js +0 -2
  376. package/dist/cli/chunks/init-wizard-NIKM3ZOK.js +0 -2
  377. package/dist/cli/chunks/kernel-2EOSVM6X.js +0 -2
  378. package/dist/cli/chunks/knowledge-graph-D3BPWNNO.js +0 -2
  379. package/dist/cli/chunks/llm-router-service-TJPPZFUN.js +0 -2
  380. package/dist/cli/chunks/load-test-RFT7IDOL.js +0 -2
  381. package/dist/cli/chunks/memory-backend-O2O4CFKZ.js +0 -2
  382. package/dist/cli/chunks/memory-handlers-DAEGUDBD.js +0 -2
  383. package/dist/cli/chunks/pattern-null-store-TUBNXS34.js +0 -2
  384. package/dist/cli/chunks/protocol-executor-6FQTTO3Y.js +0 -2
  385. package/dist/cli/chunks/qe-reasoning-bank-TKPND56C.js +0 -2
  386. package/dist/cli/chunks/quality-46AK277P.js +0 -7
  387. package/dist/cli/chunks/queen-coordinator-DWMXLPJG.js +0 -2
  388. package/dist/cli/chunks/router-QIMUYRCC.js +0 -2
  389. package/dist/cli/chunks/routing-feedback-4OH5ZNF4.js +0 -2
  390. package/dist/cli/chunks/rvf-dual-writer-S6JBZL54.js +0 -2
  391. package/dist/cli/chunks/rvf-migration-adapter-TSGMLMYR.js +0 -2
  392. package/dist/cli/chunks/rvf-native-adapter-Z7QOW47S.js +0 -2
  393. package/dist/cli/chunks/safe-db-JLOVBJNC.js +0 -2
  394. package/dist/cli/chunks/schedule-IM6XOBGR.js +0 -2
  395. package/dist/cli/chunks/scheduler-XL2SVVK5.js +0 -2
  396. package/dist/cli/chunks/sqlite-persistence-LWMIIC4U.js +0 -2
  397. package/dist/cli/chunks/unified-memory-F5IJY7W7.js +0 -2
  398. package/dist/cli/chunks/unified-memory-hnsw-C3WAMAQL.js +0 -2
  399. package/dist/cli/chunks/unified-persistence-EWXXOTCB.js +0 -2
  400. package/dist/cli/chunks/visual-security-CKLTAIAB.js +0 -2
  401. package/dist/cli/chunks/witness-chain-Z6LWFEZS.js +0 -2
  402. package/dist/cli/chunks/workflow-orchestrator-OFTAR2NA.js +0 -2
@@ -209,10 +209,14 @@ export function coverageToMarkdown(result) {
209
209
  let md = `# Coverage Report\n\n`;
210
210
  md += `| Metric | Value | Status |\n`;
211
211
  md += `|--------|-------|--------|\n`;
212
- md += `| Lines | ${result.summary.line}% | ${result.summary.line >= result.threshold ? '\u2705' : '\u274C'} |\n`;
213
- md += `| Branches | ${result.summary.branch}% | ${result.summary.branch >= result.threshold ? '\u2705' : '\u274C'} |\n`;
214
- md += `| Functions | ${result.summary.function}% | ${result.summary.function >= result.threshold ? '\u2705' : '\u274C'} |\n`;
215
- md += `| Statements | ${result.summary.statement}% | ${result.summary.statement >= result.threshold ? '\u2705' : '\u274C'} |\n`;
212
+ // #995: an uncollected metric is "n/a", with no pass/fail mark — it is not 0%.
213
+ const row = (label, v) => v === null
214
+ ? `| ${label} | n/a (not collected) | \u2014 |\n`
215
+ : `| ${label} | ${v}% | ${v >= result.threshold ? '\u2705' : '\u274C'} |\n`;
216
+ md += row('Lines', result.summary.line);
217
+ md += row('Branches', result.summary.branch);
218
+ md += row('Functions', result.summary.function);
219
+ md += row('Statements', result.summary.statement);
216
220
  md += `\n**Threshold:** ${result.threshold}% \u2014 ${result.meetsThreshold ? '\u2705 Met' : '\u274C Not met'}\n`;
217
221
  if (result.gaps && result.gaps.length > 0) {
218
222
  md += `\n## Coverage Gaps\n\n`;
@@ -232,6 +236,19 @@ export function coverageToMarkdown(result) {
232
236
  }
233
237
  export function qualityGateToMarkdown(result) {
234
238
  let md = `# Quality Gate Report\n\n`;
239
+ if (result.evidenceStatus === 'insufficient-evidence') {
240
+ // #996: a gate that could not run is neither a pass nor a measured failure.
241
+ md += `**Result:** \u26A0\uFE0F INSUFFICIENT EVIDENCE \u2014 quality gate not evaluated\n`;
242
+ if (result.unmeasured && result.unmeasured.length > 0) {
243
+ md += `**Unmeasured:** ${result.unmeasured.join(', ')}\n`;
244
+ }
245
+ if (result.error)
246
+ md += `**Reason:** ${result.error}\n`;
247
+ if (result.recommendations && result.recommendations.length > 0) {
248
+ md += `\n## Recommendations\n\n${result.recommendations.map(rec => `- ${rec}`).join('\n')}\n`;
249
+ }
250
+ return md;
251
+ }
235
252
  md += `**Result:** ${result.passed ? '\u2705 PASSED' : '\u274C FAILED'}\n`;
236
253
  md += `**Score:** ${result.score}\n\n`;
237
254
  md += `## Checks\n\n`;
@@ -41,12 +41,19 @@ export interface CoverageData {
41
41
  };
42
42
  /** True if data came from real instrumented coverage (V8/istanbul) */
43
43
  instrumented: boolean;
44
+ /**
45
+ * Source files the instrumented report has no entry for (#995). These are
46
+ * UNMEASURED — excluded from the figures rather than counted as 0%.
47
+ */
48
+ unmeasuredFiles?: string[];
44
49
  }
45
50
  /**
46
51
  * Try to read real coverage data from a coverage-final.json file.
47
52
  * Returns null if no instrumented coverage is available.
48
53
  */
49
54
  export declare function readInstrumentedCoverage(projectRoot: string): Map<string, FileCoverage> | null;
55
+ /** Aggregate per-file coverage into whole-number percentages. */
56
+ export declare function summarizeCoverage(files: FileCoverage[]): CoverageData['summary'];
50
57
  /**
51
58
  * Build coverage data for the given source files.
52
59
  *
@@ -161,6 +161,20 @@ function estimateFileCoverage(filePath, content) {
161
161
  // ============================================================================
162
162
  // Public API
163
163
  // ============================================================================
164
+ /** Aggregate per-file coverage into whole-number percentages. */
165
+ export function summarizeCoverage(files) {
166
+ const sum = (pick) => files.reduce((acc, f) => acc + pick(f), 0);
167
+ const safeDiv = (a, b) => b > 0 ? Math.round((a / b) * 100) : 0;
168
+ const totalLines = sum(f => f.lines.total);
169
+ const coveredLines = sum(f => f.lines.covered);
170
+ return {
171
+ line: safeDiv(coveredLines, totalLines),
172
+ branch: safeDiv(sum(f => f.branches.covered), sum(f => f.branches.total)),
173
+ function: safeDiv(sum(f => f.functions.covered), sum(f => f.functions.total)),
174
+ statement: safeDiv(coveredLines, totalLines),
175
+ files: files.length,
176
+ };
177
+ }
164
178
  /**
165
179
  * Build coverage data for the given source files.
166
180
  *
@@ -174,6 +188,7 @@ export function buildCoverageData(sourceFiles, projectRoot) {
174
188
  // Try instrumented coverage first
175
189
  const instrumentedData = readInstrumentedCoverage(root);
176
190
  const files = [];
191
+ const unmeasuredFiles = [];
177
192
  let isInstrumented = false;
178
193
  if (instrumentedData && instrumentedData.size > 0) {
179
194
  isInstrumented = true;
@@ -187,18 +202,10 @@ export function buildCoverageData(sourceFiles, projectRoot) {
187
202
  files.push({ ...match, path: filePath });
188
203
  }
189
204
  else {
190
- // File exists in source but has no coverage data — 0% coverage
191
- const content = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';
192
- const totalLines = content.split('\n').length;
193
- files.push({
194
- path: filePath,
195
- lines: { covered: 0, total: totalLines },
196
- branches: { covered: 0, total: 1 },
197
- functions: { covered: 0, total: 1 },
198
- statements: { covered: 0, total: totalLines },
199
- uncoveredLines: Array.from({ length: totalLines }, (_, i) => i + 1),
200
- uncoveredBranches: [],
201
- });
205
+ // #995: the report has no entry for this file — the coverage run did
206
+ // not measure it. That is "unmeasured", not 0%: counting it as fully
207
+ // uncovered invented a figure nobody measured.
208
+ unmeasuredFiles.push(filePath);
202
209
  }
203
210
  }
204
211
  }
@@ -209,24 +216,11 @@ export function buildCoverageData(sourceFiles, projectRoot) {
209
216
  files.push(estimateFileCoverage(filePath, content));
210
217
  }
211
218
  }
212
- // Compute summary
213
- const totalLines = files.reduce((sum, f) => sum + f.lines.total, 0);
214
- const coveredLines = files.reduce((sum, f) => sum + f.lines.covered, 0);
215
- const totalBranches = files.reduce((sum, f) => sum + f.branches.total, 0);
216
- const coveredBranches = files.reduce((sum, f) => sum + f.branches.covered, 0);
217
- const totalFunctions = files.reduce((sum, f) => sum + f.functions.total, 0);
218
- const coveredFunctions = files.reduce((sum, f) => sum + f.functions.covered, 0);
219
- const safeDiv = (a, b) => b > 0 ? Math.round((a / b) * 100) : 0;
220
219
  return {
221
220
  files,
222
- summary: {
223
- line: safeDiv(coveredLines, totalLines),
224
- branch: safeDiv(coveredBranches, totalBranches),
225
- function: safeDiv(coveredFunctions, totalFunctions),
226
- statement: safeDiv(coveredLines, totalLines),
227
- files: files.length,
228
- },
221
+ summary: summarizeCoverage(files),
229
222
  instrumented: isInstrumented,
223
+ ...(unmeasuredFiles.length > 0 ? { unmeasuredFiles } : {}),
230
224
  };
231
225
  }
232
226
  //# sourceMappingURL=coverage-data.js.map
@@ -58,14 +58,28 @@ export interface ValidationResult {
58
58
  export declare function validateRiskDecision(value: unknown): ValidationResult;
59
59
  export declare function validateFindingVerdict(value: unknown): ValidationResult;
60
60
  export declare function validateCoverageGap(value: unknown): ValidationResult;
61
+ /** #996: confidence cap for an approve that rests on static analysis alone (no gate). */
62
+ export declare const STATIC_APPROVAL_CONFIDENCE = 0.7;
61
63
  /**
62
64
  * Derive a RiskDecision from a quality-gate outcome (quality_assess boundary).
63
65
  * Indeterminate gates (passed undefined) escalate rather than guess.
66
+ *
67
+ * #996: when key metrics are unmeasured the decision escalates with lowered
68
+ * confidence, whatever `passed` says — never approve on missing data. When
69
+ * `gateEvaluated` is false the rationale says plainly that no gate ran.
64
70
  */
65
71
  export declare function buildRiskDecisionFromQualityGate(input: {
66
72
  passed?: boolean;
67
73
  qualityScore?: number;
68
74
  recommendations?: string[];
75
+ /** Requested metrics that had no measured data. */
76
+ unmeasured?: string[];
77
+ /** Share of requested metrics that were measured, in [0, 1]. */
78
+ evidenceConfidence?: number;
79
+ /** False for static analysis: no quality gate was evaluated. */
80
+ gateEvaluated?: boolean;
81
+ /** Measured metrics that fail their own limit — conclusive, so the decision blocks. */
82
+ failedMetrics?: string[];
69
83
  }): RiskDecision;
70
84
  export declare const RISK_DECISION_SCHEMA: {
71
85
  readonly $schema: "http://json-schema.org/draft-07/schema#";
@@ -176,21 +176,62 @@ export function validateCoverageGap(value) {
176
176
  // ============================================================================
177
177
  // Builders — derive contract envelopes at MCP boundaries
178
178
  // ============================================================================
179
+ /** #996: confidence cap for an approve that rests on static analysis alone (no gate). */
180
+ export const STATIC_APPROVAL_CONFIDENCE = 0.7;
179
181
  /**
180
182
  * Derive a RiskDecision from a quality-gate outcome (quality_assess boundary).
181
183
  * Indeterminate gates (passed undefined) escalate rather than guess.
184
+ *
185
+ * #996: when key metrics are unmeasured the decision escalates with lowered
186
+ * confidence, whatever `passed` says — never approve on missing data. When
187
+ * `gateEvaluated` is false the rationale says plainly that no gate ran.
182
188
  */
183
189
  export function buildRiskDecisionFromQualityGate(input) {
184
- const { passed, qualityScore, recommendations = [] } = input;
190
+ const { passed, qualityScore, recommendations = [], unmeasured = [], failedMetrics = [], gateEvaluated, } = input;
191
+ const noGate = gateEvaluated === false ? ' (no quality gate was evaluated)' : '';
192
+ const score = typeof qualityScore === 'number' ? ` with score ${qualityScore}` : '';
193
+ // A measured failure cannot be rescued by metrics that were never measured.
194
+ if (failedMetrics.length > 0) {
195
+ const missing = unmeasured.length > 0 ? `; also unmeasured: ${unmeasured.join(', ')}` : '';
196
+ return {
197
+ contract: 'risk-decision@1',
198
+ decision: 'block',
199
+ riskFactors: [
200
+ `failed: ${failedMetrics.join(', ')}`,
201
+ ...(unmeasured.length > 0 ? [`unmeasured: ${unmeasured.join(', ')}`] : []),
202
+ ...recommendations,
203
+ ].slice(0, 10),
204
+ confidence: 0.9,
205
+ rationale: `Measured ${failedMetrics.join(', ')} failed its threshold${missing}${noGate}`,
206
+ };
207
+ }
208
+ if (unmeasured.length > 0) {
209
+ const share = typeof input.evidenceConfidence === 'number' && Number.isFinite(input.evidenceConfidence)
210
+ ? Math.min(1, Math.max(0, input.evidenceConfidence))
211
+ : 0;
212
+ return {
213
+ contract: 'risk-decision@1',
214
+ decision: 'escalate',
215
+ riskFactors: [`unmeasured: ${unmeasured.join(', ')}`, ...recommendations].slice(0, 10),
216
+ confidence: Math.round(0.5 * share * 100) / 100,
217
+ rationale: `Insufficient evidence: ${unmeasured.join(', ')} not measured${noGate} — `
218
+ + 'measure the missing metrics or run the measured quality gate before deciding',
219
+ };
220
+ }
185
221
  const decision = passed === true ? 'approve' : passed === false ? 'block' : 'escalate';
222
+ const subject = gateEvaluated === false ? 'Static analysis' : 'Quality gate';
223
+ // #996 (N1): an approval no gate evaluated is weaker evidence than a gate pass.
224
+ const confidence = decision === 'escalate'
225
+ ? 0.5
226
+ : decision === 'approve' && gateEvaluated === false ? STATIC_APPROVAL_CONFIDENCE : 0.9;
186
227
  return {
187
228
  contract: 'risk-decision@1',
188
229
  decision,
189
230
  riskFactors: recommendations.slice(0, 10),
190
- confidence: decision === 'escalate' ? 0.5 : 0.9,
231
+ confidence,
191
232
  rationale: decision === 'escalate'
192
233
  ? 'Quality gate outcome indeterminate — manual review required'
193
- : `Quality gate ${passed ? 'passed' : 'failed'}${typeof qualityScore === 'number' ? ` with score ${qualityScore}` : ''}`,
234
+ : `${subject} ${passed ? 'passed' : 'failed'}${score}${noGate}`,
194
235
  };
195
236
  }
196
237
  // ============================================================================
@@ -4,143 +4,141 @@
4
4
  * Extracted from task-executor.ts registerHandlers().
5
5
  * Covers: analyze-coverage
6
6
  */
7
- import * as fs from 'fs/promises';
8
- import * as path from 'path';
9
7
  import { ok, err } from '../../shared/types/index.js';
10
8
  import { toError } from '../../shared/error-utils.js';
11
- import { safeJsonParse } from '../../shared/safe-json.js';
12
- import { loadCoverageData } from './handler-utils.js';
13
- import { buildEstimatedCoverage, collectRustCoverage, isCoverageExecDisabled, isRustProject, isTestPath, } from './coverage-collection.js';
9
+ import { buildEstimatedCoverage, collectRustCoverage, findCargoRoot, isCoverageExecDisabled, isRustProject, } from './coverage-collection.js';
10
+ import { describeNoData, evaluateCoverageForTarget, isUnmeasured, resolveExistingCoverage, scopeCoverageToTarget, reconcileRecommendations, thresholdVerdict, } from './coverage-target.js';
11
+ import { describeRunFailure, runJsCoverage } from './coverage-runner.js';
14
12
  /**
15
- * Try to produce a JS/TS coverage report by running the project's test runner.
16
- * Returns true if the runner completed (which does not guarantee a report was
17
- * written — the caller re-checks disk).
13
+ * How long the project's test runner may take before its process tree is
14
+ * killed. `AQE_COVERAGE_TIMEOUT_MS` overrides the 120s default.
18
15
  */
19
- async function tryRunJsCoverage(targetPath) {
20
- // Running the project's test suite executes code from the analyzed repository
21
- // (the tests themselves, plus npm lifecycle scripts via npx). Honor the
22
- // opt-out before doing that.
23
- if (isCoverageExecDisabled())
24
- return false;
25
- try {
26
- const { execSync } = await import('child_process');
27
- let coverageCmd = 'npx vitest run --coverage --reporter=json 2>/dev/null';
28
- try {
29
- const pkgContent = await fs.readFile(path.join(targetPath, 'package.json'), 'utf-8');
30
- const pkg = safeJsonParse(pkgContent);
31
- const deps = {
32
- ...(pkg.devDependencies || {}),
33
- ...(pkg.dependencies || {}),
34
- };
35
- if (deps['jest'] || deps['@jest/core']) {
36
- coverageCmd = 'npx jest --coverage --json 2>/dev/null';
37
- }
38
- else if (deps['mocha'] || deps['nyc']) {
39
- coverageCmd = 'npx nyc mocha 2>/dev/null';
40
- }
41
- // vitest is the default — covers vitest, @vitest/coverage-v8, etc.
42
- }
43
- catch {
44
- // No package.json — use default vitest
45
- }
46
- execSync(coverageCmd, {
47
- cwd: targetPath,
48
- timeout: 120000,
49
- encoding: 'utf-8',
50
- stdio: ['pipe', 'pipe', 'pipe'],
51
- });
52
- return true;
53
- }
54
- catch {
55
- // Test runner failed or not available.
56
- return false;
57
- }
16
+ function coverageTimeoutMs(env = process.env) {
17
+ const parsed = Number.parseInt(env.AQE_COVERAGE_TIMEOUT_MS ?? '', 10);
18
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : 120000;
58
19
  }
59
- /**
60
- * Narrow an instrumented report to production files only, recomputing the
61
- * aggregate from what survives.
62
- *
63
- * Returns `null` when the report contains no production files at all — the
64
- * caller must NOT fall back to the unfiltered set, because that reports the test
65
- * files' own coverage as the project's (#569's third contradiction).
66
- */
67
- function productionOnly(report, targetPath) {
68
- const files = report.files.filter(f => !isTestPath(f.path, targetPath));
69
- if (files.length === 0)
70
- return null;
71
- // Recompute rather than carrying the original summary: it was computed over
72
- // the unfiltered set, so keeping it would blend test-file coverage into the
73
- // production headline figure.
20
+ /** Aggregate a file list into whole-number percentages. */
21
+ function recomputeSummary(files) {
74
22
  const sum = (pick) => files.reduce((acc, f) => acc + pick(f), 0);
75
- const pct = (covered, total) => total > 0 ? Math.round((covered / total) * 100) : 0;
23
+ const pct = (covered, total) => (total > 0 ? Math.round((covered / total) * 100) : 0);
76
24
  const totalLines = sum(f => f.lines.total);
77
25
  const coveredLines = sum(f => f.lines.covered);
78
- const branchDataCollected = files.some(f => f.branches.total > 0);
79
- const functionDataCollected = files.some(f => f.functions.total > 0);
80
26
  return {
81
- data: {
82
- files,
83
- summary: {
84
- line: pct(coveredLines, totalLines),
85
- branch: pct(sum(f => f.branches.covered), sum(f => f.branches.total)),
86
- function: pct(sum(f => f.functions.covered), sum(f => f.functions.total)),
87
- statement: pct(coveredLines, totalLines),
88
- files: files.length,
89
- },
90
- },
27
+ line: pct(coveredLines, totalLines),
28
+ branch: pct(sum(f => f.branches.covered), sum(f => f.branches.total)),
29
+ function: pct(sum(f => f.functions.covered), sum(f => f.functions.total)),
30
+ statement: pct(coveredLines, totalLines),
31
+ files: files.length,
32
+ };
33
+ }
34
+ /** An instrumented verdict as a collected result with honest provenance. */
35
+ function instrumented(files) {
36
+ return {
37
+ data: { files, summary: recomputeSummary(files) },
91
38
  provenance: {
92
39
  method: 'instrumented-report',
93
40
  estimated: false,
94
- branchDataCollected,
95
- functionDataCollected,
41
+ branchDataCollected: files.some(f => f.branches.total > 0),
42
+ functionDataCollected: files.some(f => f.functions.total > 0),
96
43
  notes: [],
97
44
  },
98
45
  };
99
46
  }
47
+ function fromEvaluation(evaluation, ranTests, projectRoot) {
48
+ return evaluation.status === 'measured'
49
+ ? { collected: instrumented(evaluation.files), ranTests, projectRoot }
50
+ : { collected: null, ranTests, noData: evaluation.reason, projectRoot };
51
+ }
100
52
  /**
101
53
  * Collect coverage for a target, preferring real measurement over estimation
102
54
  * and always reporting which one happened (#569).
103
55
  *
56
+ * The target may be the project root, a subfolder or file inside it, or an
57
+ * existing coverage report (#995). Tooling always runs from the project root
58
+ * and the result is scoped back down to the target. The decision about what
59
+ * a report says for a target is shared with the CLI (`resolveExistingCoverage`
60
+ * / `evaluateCoverageForTarget`).
61
+ *
104
62
  * Cascade:
105
- * 1. an instrumented report already on disk (istanbul/lcov/cobertura)
63
+ * 0. the target IS a coverage report — parse it, run nothing
64
+ * 1. an instrumented report already on disk at the project root
106
65
  * 2. language-specific instrumentation we can run — `cargo llvm-cov` for Rust,
107
- * the project's test runner for JS/TS
66
+ * the project's test runner for JS/TS — writing to a temp dir
108
67
  * 3. static estimation, explicitly labelled `estimated: true`
109
68
  */
110
69
  async function collectCoverage(targetPath) {
111
- const existing = await loadCoverageData(targetPath);
112
- if (existing) {
113
- const fromReport = productionOnly(existing, targetPath);
114
- // A report whose only entries are test files yields NO production coverage.
115
- // Falling back to the unfiltered set here would report the test files'
116
- // own coverage as the project's — recreating #569's third contradiction
117
- // (`tests/verifier_matrix.rs` analysed at 79.2% as if it were a target).
118
- if (!fromReport)
119
- return { collected: null, ranTests: false, testOnlyReport: true };
120
- return { collected: fromReport, ranTests: false };
70
+ const existing = await resolveExistingCoverage(targetPath);
71
+ if (existing.status === 'target-missing') {
72
+ return { collected: null, ranTests: false, noData: 'target-missing', projectRoot: null };
73
+ }
74
+ if (existing.status === 'evaluated') {
75
+ return fromEvaluation(existing.evaluation, false, existing.projectRoot);
121
76
  }
77
+ const { target, projectRoot } = existing;
122
78
  // #569 work item 1: delegate to real instrumentation where available.
123
- const rustProject = isRustProject(targetPath);
124
- if (rustProject) {
125
- const rust = await collectRustCoverage(targetPath);
126
- if (rust)
127
- return { collected: rust, ranTests: true };
79
+ if (isRustProject(target)) {
80
+ const rust = await collectRustCoverage(target);
81
+ if (rust) {
82
+ // cargo ran from the crate root; scope its crate-wide output to the target.
83
+ const crateRoot = findCargoRoot(target) ?? projectRoot;
84
+ const scoped = scopeCoverageToTarget(rust.data, crateRoot, target);
85
+ if (scoped.length === 0) {
86
+ return { collected: null, ranTests: true, noData: 'target-not-covered', projectRoot: crateRoot };
87
+ }
88
+ const collected = scoped.length === rust.data.files.length
89
+ ? rust
90
+ : { data: { files: scoped, summary: recomputeSummary(scoped) }, provenance: rust.provenance };
91
+ return { collected, ranTests: true, projectRoot: crateRoot };
92
+ }
128
93
  // This target is a Rust crate. Do not continue into the JS/TS runner:
129
94
  // `npx vitest --coverage` can still be available through the parent
130
95
  // process PATH and will write an empty report even though the crate has no
131
96
  // JavaScript tests. Treating that artifact as coverage hides the intended
132
97
  // Rust static-estimation fallback and mislabels the result.
133
- return { collected: buildEstimatedCoverage(targetPath), ranTests: false };
98
+ return { collected: buildEstimatedCoverage(target), ranTests: false, projectRoot };
134
99
  }
135
- const ranTests = await tryRunJsCoverage(targetPath);
136
- const afterRun = await loadCoverageData(targetPath);
137
- if (afterRun) {
138
- const fromRun = productionOnly(afterRun, targetPath);
139
- if (!fromRun)
140
- return { collected: null, ranTests, testOnlyReport: true };
141
- return { collected: fromRun, ranTests };
100
+ // Running the project's test suite executes code from the analyzed repository
101
+ // (the tests themselves, plus npm lifecycle scripts via npx). Honor the
102
+ // opt-out before doing that.
103
+ if (isCoverageExecDisabled()) {
104
+ return { collected: buildEstimatedCoverage(target), ranTests: false, projectRoot };
142
105
  }
143
- return { collected: buildEstimatedCoverage(targetPath), ranTests };
106
+ const run = await runJsCoverage(projectRoot, { timeoutMs: coverageTimeoutMs() });
107
+ // An EMPTY report (`{}`) from a run that reported its tests means "the run
108
+ // instrumented nothing under the target" → unmeasured, never an estimate
109
+ // (#995 review BL2). An empty report from a run that crashed before
110
+ // reporting any tests is a crash, handled below.
111
+ const crashedEmpty = run.data !== null && run.data.files.length === 0 &&
112
+ run.numTotalTests === null && !run.exitedCleanly;
113
+ if ((run.data && !crashedEmpty) || run.numTotalTests === 0) {
114
+ const evaluation = run.data
115
+ ? evaluateCoverageForTarget(run.data, projectRoot, target, { numTotalTests: run.numTotalTests })
116
+ : { status: 'no-data', reason: 'no-tests-ran' };
117
+ return fromEvaluation(evaluation, run.exitedCleanly || run.data !== null, projectRoot);
118
+ }
119
+ // No report at all: say exactly what happened to the run (not installed,
120
+ // timed out, crashed with stderr), then fall back to a labelled estimate.
121
+ const estimate = buildEstimatedCoverage(target);
122
+ const runNote = describeRunFailure(run, projectRoot);
123
+ if (estimate) {
124
+ estimate.provenance.notes = [
125
+ `${runNote} These numbers are a STATIC ESTIMATE derived from source shape — ` +
126
+ 'not measured coverage, and must not be reported as such.',
127
+ ...estimate.provenance.notes.filter(n => !/^No instrumentation ran/.test(n)),
128
+ ];
129
+ }
130
+ return {
131
+ collected: estimate, ranTests: run.exitedCleanly, projectRoot,
132
+ notes: estimate ? [] : [runNote],
133
+ };
134
+ }
135
+ /** Caller-facing explanation for a result with no coverage figures. */
136
+ function noDataWarning(reason, ranTests, targetPath, projectRoot) {
137
+ if (reason)
138
+ return describeNoData(reason, targetPath, projectRoot);
139
+ return ranTests
140
+ ? 'Tests ran but no coverage output was generated. Ensure a coverage provider is configured (e.g., @vitest/coverage-v8, istanbul).'
141
+ : 'No coverage data found and could not run tests automatically. ' + estimationGuidance(targetPath);
144
142
  }
145
143
  /**
146
144
  * Build the guidance string shown when a result is an estimate rather than a
@@ -169,8 +167,13 @@ export function registerCoverageHandlers(ctx) {
169
167
  const analyzer = ctx.getCoverageAnalyzer();
170
168
  const targetPath = payload.target || process.cwd();
171
169
  const threshold = payload.threshold || 80;
172
- const { collected, ranTests, testOnlyReport } = await collectCoverage(targetPath);
170
+ const { collected, ranTests, noData, projectRoot, notes } = await collectCoverage(targetPath);
171
+ // #995 review N5: say so plainly instead of "no coverage data found".
172
+ if (noData === 'target-missing') {
173
+ return err(new Error(describeNoData('target-missing', targetPath, null)));
174
+ }
173
175
  if (!collected) {
176
+ const unmeasured = noData !== undefined && isUnmeasured(noData);
174
177
  return ok({
175
178
  lineCoverage: null,
176
179
  branchCoverage: null,
@@ -182,22 +185,21 @@ export function registerCoverageHandlers(ctx) {
182
185
  estimated: false,
183
186
  measured: false,
184
187
  coverageMethod: 'none',
188
+ // #995(d): "no measurement exists for this target", distinct from 0%.
189
+ ...(unmeasured ? { unmeasured: true } : {}),
185
190
  algorithm: 'sublinear-O(log n)',
186
- warning: testOnlyReport
187
- ? 'The coverage report contains only test files, so there is no production ' +
188
- 'coverage to report. Coverage OF a test file is not a meaningful metric. ' +
189
- 'Check that your coverage tool is instrumenting your source directory.'
190
- : ranTests
191
- ? 'Tests ran but no coverage output was generated. Ensure a coverage provider is configured (e.g., @vitest/coverage-v8, istanbul).'
192
- : 'No coverage data found and could not run tests automatically. ' + estimationGuidance(targetPath),
191
+ warning: notes?.length ? notes.join(' ') : noDataWarning(noData, ranTests, targetPath, projectRoot),
193
192
  });
194
193
  }
195
194
  const { data: coverageData, provenance } = collected;
196
195
  // Analyze coverage using the real CoverageAnalyzerService
196
+ // #1008: the analyzer persists only measured coverage; a static estimate
197
+ // is returned below, labelled, but never stored as evidence.
197
198
  const analysisResult = await analyzer.analyze({
198
199
  coverageData,
199
200
  threshold,
200
201
  includeFileDetails: payload.detectGaps,
202
+ measured: !provenance.estimated,
201
203
  });
202
204
  if (!analysisResult.success) {
203
205
  return analysisResult;
@@ -227,7 +229,7 @@ export function registerCoverageHandlers(ctx) {
227
229
  }
228
230
  const round = (n) => Math.round(n * 10) / 10;
229
231
  const pct = (covered, total) => total > 0 ? Math.round((covered / total) * 1000) / 10 : null;
230
- const warnings = [...provenance.notes];
232
+ const warnings = [...provenance.notes, ...(notes ?? [])];
231
233
  if (provenance.estimated)
232
234
  warnings.push(estimationGuidance(targetPath));
233
235
  // #569: the analyzer generates recommendations from the summary numbers.
@@ -236,13 +238,11 @@ export function registerCoverageHandlers(ctx) {
236
238
  // significantly lower than line coverage", "Function coverage is below
237
239
  // 70%") is a statement about a metric nobody measured. Drop it rather
238
240
  // than send a developer to fix a number that doesn't exist.
239
- const recommendations = report.recommendations.filter(rec => {
240
- if (!provenance.branchDataCollected && /branch coverage/i.test(rec))
241
- return false;
242
- if (!provenance.functionDataCollected && /function coverage/i.test(rec))
243
- return false;
244
- return true;
245
- });
241
+ // #995 NB5/NB-A: the verdict and the shortfall advice come from the same
242
+ // collected-metrics computation (shared with the CLI), so they can't disagree.
243
+ const metricsCollected = { branch: provenance.branchDataCollected, function: provenance.functionDataCollected };
244
+ const verdict = thresholdVerdict(report.summary, threshold, metricsCollected);
245
+ const recommendations = reconcileRecommendations(report.recommendations, verdict, threshold, metricsCollected);
246
246
  return ok({
247
247
  lineCoverage: round(report.summary.line),
248
248
  // #569 work item 4: never report a branch percentage — least of all
@@ -258,7 +258,8 @@ export function registerCoverageHandlers(ctx) {
258
258
  functionCoverage: provenance.functionDataCollected ? pct(f.functions.covered, f.functions.total) : null,
259
259
  })),
260
260
  gaps,
261
- meetsThreshold: report.meetsThreshold,
261
+ // NB5: judged only on the metrics that were collected (shared with the CLI).
262
+ meetsThreshold: verdict.meetsThreshold,
262
263
  delta: report.delta,
263
264
  recommendations,
264
265
  algorithm: 'sublinear-O(log n)',