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
@@ -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: 0,
51
- complexity: 0,
52
- maintainability: 0,
53
- testability: 0,
108
+ coverage: UNMEASURED,
109
+ complexity: UNMEASURED,
110
+ maintainability: UNMEASURED,
111
+ testability: UNMEASURED,
54
112
  },
55
- recommendations: ['No source files found for quality assessment'],
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: payload.metrics || ['coverage', 'complexity', 'maintainability', 'testability'],
129
+ includeMetrics: requestedMetrics,
63
130
  });
64
131
  if (!result.success) {
65
132
  return result;
66
133
  }
67
134
  const report = result.value;
68
- const passed = report.score.overall >= threshold;
69
- // Convert metrics to the expected format
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: report.score.coverage,
80
- complexity: report.score.complexity,
81
- maintainability: report.score.maintainability,
82
- security: report.score.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: report.recommendations.map(r => `[${r.type}] ${r.title}: ${r.description}`),
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:** ${data.passed ? 'PASSED' : 'FAILED'}
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 ? 'No recommendations - all quality gates passed.' : data.recommendations.map((r, i) => `${i + 1}. ${r}`).join('\n')}
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
  /**