@qwen-code/qwen-code 0.22.1 → 0.22.2-nightly.20260828.7357136dd1

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 (418) hide show
  1. package/bundled/goal-draft/SKILL.md +109 -0
  2. package/bundled/qc-helper/docs/configuration/settings.md +13 -13
  3. package/bundled/qc-helper/docs/features/_meta.ts +1 -0
  4. package/bundled/qc-helper/docs/features/channels/overview.md +34 -3
  5. package/bundled/qc-helper/docs/features/code-review.md +3 -3
  6. package/bundled/qc-helper/docs/features/commands.md +2 -1
  7. package/bundled/qc-helper/docs/features/goals.md +58 -0
  8. package/bundled/qc-helper/docs/features/headless.md +13 -11
  9. package/bundled/qc-helper/docs/features/status-line.md +3 -3
  10. package/bundled/review/SKILL.md +70 -22
  11. package/bundled/review/references/persistence.md +7 -3
  12. package/bundled/review/references/posting.md +2 -0
  13. package/chunks/MaxSizedBox-LMWXCLDV.js +110 -0
  14. package/chunks/{StandaloneSessionPicker-XF2TVJFK.js → StandaloneSessionPicker-W3F7U3JL.js} +79 -81
  15. package/chunks/{acpAgent-GY53MJIW.js → acpAgent-EYZ6EEHD.js} +1132 -339
  16. package/chunks/{agent-7U6CC5KF.js → agent-RBLGBY65.js} +44 -46
  17. package/chunks/agent-headless-L5T4Y4OZ.js +85 -0
  18. package/chunks/{anthropicContentGenerator-U76GTVB6.js → anthropicContentGenerator-GMHML35Q.js} +10 -11
  19. package/chunks/{artifact-tool-XDJCZZCF.js → artifact-tool-XSMPHZBT.js} +11 -3
  20. package/chunks/{askUserQuestion-ZXXERLEV.js → askUserQuestion-4SWJY65V.js} +2 -2
  21. package/chunks/bridge-5LR2F2ND.js +119 -0
  22. package/chunks/{channel-management-service-HXXS3HOZ.js → channel-management-service-KHR3N6CH.js} +6 -6
  23. package/chunks/channel-settings-store-4FOAZW72.js +120 -0
  24. package/chunks/{channel-worker-group-EVDC2P4K.js → channel-worker-group-6NA6BBZV.js} +8 -7
  25. package/chunks/{channel-worker-manager-IJJZHGEI.js → channel-worker-manager-NEZ2J6RK.js} +10 -8
  26. package/chunks/{channel-worker-supervisor-YSKB5GVB.js → channel-worker-supervisor-FKU7DIHJ.js} +6 -5
  27. package/chunks/{chunk-7QGEJI7M.js → chunk-23RFD54N.js} +1 -1
  28. package/chunks/{chunk-LJP2BBMK.js → chunk-2SXSLSQE.js} +4 -4
  29. package/chunks/chunk-2TNL5KPM.js +49 -0
  30. package/chunks/chunk-335CJSNF.js +2320 -0
  31. package/chunks/chunk-36LGBEM6.js +130 -0
  32. package/chunks/{chunk-ZWKGZB2K.js → chunk-3JXM2CVW.js} +3 -3
  33. package/chunks/{chunk-ICWGPK23.js → chunk-3K6TUZHS.js} +7 -7
  34. package/chunks/{chunk-43C2II2G.js → chunk-3OOXT6CR.js} +5 -5
  35. package/chunks/{chunk-ILPXSKGQ.js → chunk-3WK3QDNV.js} +2 -2
  36. package/chunks/{chunk-2YDYDDE2.js → chunk-3XOKEVII.js} +3 -3
  37. package/chunks/chunk-3Y34Q3JV.js +1584 -0
  38. package/chunks/{chunk-ZLTEQYNC.js → chunk-42IDLQWS.js} +26 -20
  39. package/chunks/{chunk-5MK32KJV.js → chunk-454XFGH4.js} +3 -3
  40. package/chunks/chunk-4CMJKUND.js +95 -0
  41. package/chunks/{chunk-77TOMRBG.js → chunk-4K7KNVWH.js} +3 -3
  42. package/chunks/{chunk-V3GINY6K.js → chunk-4QHPPXK2.js} +16 -8
  43. package/chunks/{chunk-UCSV2IZW.js → chunk-62GQFYID.js} +3 -3
  44. package/chunks/{chunk-SLEWBQWM.js → chunk-6DIGWMGT.js} +2 -2
  45. package/chunks/{chunk-I6YW3T3W.js → chunk-6JCGO55P.js} +723 -3399
  46. package/chunks/{chunk-PVBT5QPD.js → chunk-6PJOTWAN.js} +1 -1
  47. package/chunks/{chunk-5Y2JCJAH.js → chunk-6W2IK5KC.js} +956 -17
  48. package/chunks/{chunk-A7X32SCT.js → chunk-6ZZBRRKQ.js} +5 -5
  49. package/chunks/{chunk-ELY4QVIB.js → chunk-7EQQR46F.js} +1 -1
  50. package/chunks/{chunk-4DCP3362.js → chunk-7GWQW5ID.js} +4 -4
  51. package/chunks/chunk-7J6OTNGO.js +424 -0
  52. package/chunks/{chunk-CITGRGCM.js → chunk-7TUBJBBJ.js} +4 -1
  53. package/chunks/{chunk-L436XFLJ.js → chunk-7VD6IPQM.js} +1 -1
  54. package/chunks/{chunk-E4I5MVBR.js → chunk-ABS25QER.js} +1 -1
  55. package/chunks/{chunk-2ZV3CZKI.js → chunk-AJGR6E6Z.js} +49 -32
  56. package/chunks/{chunk-HJOWQUE2.js → chunk-AU3MMVJF.js} +31 -14
  57. package/chunks/{chunk-I32QCIK7.js → chunk-AXC26UCQ.js} +1 -1
  58. package/chunks/{chunk-7AS7QX72.js → chunk-AY3BNYEQ.js} +1 -1
  59. package/chunks/{chunk-FVDQ5E67.js → chunk-B466ZSHZ.js} +3 -3
  60. package/chunks/{chunk-OI2DR6QB.js → chunk-B5OUL5EL.js} +2 -2
  61. package/chunks/{chunk-6US7BF3G.js → chunk-B5PHIJGI.js} +183 -5
  62. package/chunks/{chunk-5T4LWWNX.js → chunk-B7CDU2SL.js} +2 -2
  63. package/chunks/{chunk-4TPAG3O7.js → chunk-BFPESSTR.js} +1 -1
  64. package/chunks/{chunk-WBT3STGU.js → chunk-BKWNKLZB.js} +20 -20
  65. package/chunks/{chunk-73WFGVIZ.js → chunk-BOWFGCEU.js} +3 -3
  66. package/chunks/{chunk-EMGAVCBZ.js → chunk-BPAKYXQ3.js} +2 -2
  67. package/chunks/{chunk-IPQA4SR5.js → chunk-BWORX6FA.js} +1 -1
  68. package/chunks/{chunk-BKDAF7ES.js → chunk-BXDAPVQU.js} +2 -2
  69. package/chunks/{chunk-ZEADBZLO.js → chunk-C2X7KY45.js} +3 -3
  70. package/chunks/{chunk-T2HMXHYA.js → chunk-C3KURPP7.js} +6 -6
  71. package/chunks/{chunk-JUIJ3FXC.js → chunk-CAJTKR6W.js} +5 -5
  72. package/chunks/{chunk-2FN2HNTI.js → chunk-CFJY4DGX.js} +2 -2
  73. package/chunks/{chunk-DP7LABIB.js → chunk-CMHFCLBU.js} +1 -1
  74. package/chunks/{chunk-LRKMQPMD.js → chunk-CQH5KTKC.js} +1 -1
  75. package/chunks/{chunk-J7MLXFC5.js → chunk-CRKKHXTI.js} +185 -174
  76. package/chunks/{chunk-XNXWEGKB.js → chunk-CYAUBK46.js} +3 -3
  77. package/chunks/{chunk-JECO75XD.js → chunk-D5LUXLUH.js} +2 -2
  78. package/chunks/{chunk-SRGENTPF.js → chunk-DFDWJE6Z.js} +48 -31
  79. package/chunks/chunk-DJBQCO2E.js +842 -0
  80. package/chunks/{chunk-2CRNTW2B.js → chunk-DP4MWIM3.js} +2 -2
  81. package/chunks/{chunk-EB4VDXNI.js → chunk-DYXFD5RO.js} +182 -3
  82. package/chunks/{chunk-ODXCSIH5.js → chunk-E3XVSVKK.js} +26 -4
  83. package/chunks/{chunk-ZKAZP2ZG.js → chunk-EB6QRGHM.js} +3 -3
  84. package/chunks/{chunk-GNRVKN76.js → chunk-EKNBU2X7.js} +4 -4
  85. package/chunks/{chunk-3UZCLCXR.js → chunk-EKSCLBBF.js} +3 -3
  86. package/chunks/{chunk-624NHDGB.js → chunk-ERFDKH32.js} +11 -0
  87. package/chunks/chunk-EW5VVLJT.js +3525 -0
  88. package/chunks/{chunk-XYX474JQ.js → chunk-EWIQ3NAE.js} +2522 -2030
  89. package/chunks/{chunk-O5YGAEQS.js → chunk-FALBWZF4.js} +6 -6
  90. package/chunks/{chunk-GZJUCMH4.js → chunk-FEHMA4RO.js} +542 -654
  91. package/chunks/{chunk-2YEZNBSD.js → chunk-FMFBLTLY.js} +320 -86
  92. package/chunks/{chunk-MZS7GEC7.js → chunk-FTF2YIZ5.js} +1 -1
  93. package/chunks/{chunk-ZJG323IA.js → chunk-G6FECKTJ.js} +7 -5
  94. package/chunks/{chunk-6KS24S3X.js → chunk-GGNE3JAW.js} +1 -1
  95. package/chunks/{chunk-BQBUZDDH.js → chunk-H33EQVMG.js} +4 -4
  96. package/chunks/chunk-H72BQNM3.js +645 -0
  97. package/chunks/{chunk-5ZYZPDDF.js → chunk-HGIMWQVT.js} +30 -25
  98. package/chunks/{chunk-DCSAVGDJ.js → chunk-HOCJDUFY.js} +2 -2
  99. package/chunks/{chunk-MDZO5V2O.js → chunk-HPXV655A.js} +9 -7
  100. package/chunks/{chunk-7JPW6IYH.js → chunk-HVC3PNVC.js} +1 -1
  101. package/chunks/{chunk-2MHGD7EE.js → chunk-HYDJDQGJ.js} +1 -1
  102. package/chunks/{chunk-BDHLBR46.js → chunk-IB7FQJRY.js} +34 -11
  103. package/chunks/{chunk-E55H6BZ7.js → chunk-ICURCKIU.js} +1335 -119
  104. package/chunks/{chunk-PAJGKZAA.js → chunk-ISKH4QX3.js} +14 -12
  105. package/chunks/{chunk-3B5DCAQU.js → chunk-J6S24ZJE.js} +34 -34
  106. package/chunks/{chunk-AYVIGUUK.js → chunk-JB4JIVTJ.js} +1 -1
  107. package/chunks/{chunk-C4WBKZJT.js → chunk-JN4IWWMN.js} +4 -4
  108. package/chunks/{chunk-6HTMIKXZ.js → chunk-JUUBLSZU.js} +4 -4
  109. package/chunks/{chunk-T6OODF2U.js → chunk-JWALNCLT.js} +1 -1
  110. package/chunks/{chunk-SKNVVYIJ.js → chunk-K5KXUXWC.js} +3 -3
  111. package/chunks/{chunk-IRIX323G.js → chunk-KTJFDCX4.js} +2 -2
  112. package/chunks/{chunk-FK4H3V6S.js → chunk-LEFCFTEX.js} +6 -6
  113. package/chunks/{chunk-OQ6LTSX3.js → chunk-LEGTMYAE.js} +1 -1
  114. package/chunks/{chunk-NY3IJYAV.js → chunk-LLZA2IK3.js} +15 -9
  115. package/chunks/{chunk-5CI4AXDD.js → chunk-LNETQKBE.js} +4 -4
  116. package/chunks/{chunk-TOF4UZHH.js → chunk-LUEHOUHM.js} +3 -3
  117. package/chunks/{chunk-62PXFOK4.js → chunk-LVPZO674.js} +4 -4
  118. package/chunks/{chunk-EEQYTWGU.js → chunk-M4ORUPYI.js} +5 -5
  119. package/chunks/{chunk-GNMJNDZE.js → chunk-M6FOJXW3.js} +1 -1
  120. package/chunks/{chunk-MYLXL7F6.js → chunk-MAYHGRP4.js} +9 -9
  121. package/chunks/{chunk-K3LPAED5.js → chunk-MQM5PJML.js} +24 -4
  122. package/chunks/{chunk-SM7ZVKTF.js → chunk-MTJN7FOT.js} +7 -7
  123. package/chunks/{chunk-BGXZBI5B.js → chunk-N4X7G4J2.js} +2 -1
  124. package/chunks/{chunk-27ONXAFY.js → chunk-NIFYWDYN.js} +1 -1
  125. package/chunks/{chunk-IJVDHPKD.js → chunk-NU7RKEZR.js} +2 -2
  126. package/chunks/{chunk-R2ZJH3FV.js → chunk-NXMBRVMQ.js} +3 -3
  127. package/chunks/{chunk-JLGFGPZJ.js → chunk-OI4NY7RA.js} +2 -2
  128. package/chunks/{chunk-RRKNX553.js → chunk-PDQGMSZK.js} +1 -1
  129. package/chunks/{chunk-GTUKW3I6.js → chunk-PPKZ7JOE.js} +1 -1
  130. package/chunks/{chunk-SAH4BD2J.js → chunk-PQEISIKS.js} +67 -0
  131. package/chunks/{chunk-MW5AHWOZ.js → chunk-PWIGAMA4.js} +93 -14
  132. package/chunks/{chunk-WEGXPP5E.js → chunk-QFCDF5PL.js} +1 -1
  133. package/chunks/{chunk-EAV3TENS.js → chunk-QPTRRPZU.js} +44 -7
  134. package/chunks/{chunk-SQPOJ4ZI.js → chunk-RFI4YGMR.js} +21 -266
  135. package/chunks/{chunk-JVPFTDBK.js → chunk-RFIDDFAL.js} +1 -1
  136. package/chunks/chunk-RKFGE7JH.js +93 -0
  137. package/chunks/chunk-RNVFRIOO.js +275 -0
  138. package/chunks/{chunk-CPPYZHBN.js → chunk-RU7BK6B3.js} +1 -1
  139. package/chunks/{chunk-OT26ITEY.js → chunk-RVXDXY6I.js} +3 -3
  140. package/chunks/{chunk-W2WRNJKX.js → chunk-RWZB422L.js} +5 -5
  141. package/chunks/{chunk-6YAR6ZU3.js → chunk-RYNZQGXN.js} +19 -2
  142. package/chunks/{chunk-EKP3M4RF.js → chunk-SJ5ERAW3.js} +1 -1
  143. package/chunks/{chunk-DEYPMJPD.js → chunk-SMPR7SPO.js} +1 -1
  144. package/chunks/{chunk-3BV7TZNQ.js → chunk-SRZZY4IY.js} +3 -3
  145. package/chunks/{chunk-7CWEZQ6T.js → chunk-STUJ7IMZ.js} +0 -77
  146. package/chunks/{chunk-TKO4TLT6.js → chunk-SUX7HDNA.js} +3 -3
  147. package/chunks/{chunk-76WGY6LW.js → chunk-SVNO44PF.js} +3 -3
  148. package/chunks/{chunk-UX3IFLJM.js → chunk-SZLBBZH4.js} +150 -23
  149. package/chunks/{chunk-K63NK3KL.js → chunk-T2NKRHZZ.js} +13 -13
  150. package/chunks/{chunk-4K7NQDCG.js → chunk-T4EFG2P5.js} +18 -9
  151. package/chunks/{chunk-2NCVUT3C.js → chunk-TLHUNMO7.js} +4 -4
  152. package/chunks/{chunk-EC5BONC3.js → chunk-TPI7O7HR.js} +1 -1
  153. package/chunks/{chunk-ZXDM7DYV.js → chunk-TQHGJYR3.js} +2029 -616
  154. package/chunks/{chunk-T2CRBIDJ.js → chunk-TTX2JUE6.js} +1 -1
  155. package/chunks/{chunk-OX44MOAN.js → chunk-TW3E6YNO.js} +3 -3
  156. package/chunks/chunk-U54AYGEC.js +337 -0
  157. package/chunks/{chunk-2NNTE2ED.js → chunk-UHQFIS7N.js} +15 -0
  158. package/chunks/{chunk-KOJ6MHD5.js → chunk-UTLCH2FK.js} +1 -1
  159. package/chunks/{chunk-KCOXDRQL.js → chunk-UY3PBNY3.js} +17 -29
  160. package/chunks/{chunk-7DPKPBOQ.js → chunk-V2NDP3I4.js} +2 -2
  161. package/chunks/{chunk-47SIHHPL.js → chunk-V2QDDC5J.js} +19 -9
  162. package/chunks/{chunk-BVE5EMIG.js → chunk-V5J4J5TP.js} +2 -2
  163. package/chunks/{chunk-X2UOOG5V.js → chunk-VQUGEVGR.js} +1 -1
  164. package/chunks/{chunk-XJS54K4A.js → chunk-VTHREBQM.js} +2 -2
  165. package/chunks/{chunk-VMDURMFY.js → chunk-W6DUQ5GA.js} +11307 -10424
  166. package/chunks/{chunk-BX2BZPFJ.js → chunk-WALC5VJH.js} +4 -4
  167. package/chunks/{chunk-FPH6TXD3.js → chunk-WAOIHTJS.js} +1 -1
  168. package/chunks/{chunk-LI2HEMKO.js → chunk-WBHKK3CU.js} +4 -4
  169. package/chunks/{chunk-O3BUXIPM.js → chunk-WXEA74YB.js} +3 -3
  170. package/chunks/{chunk-NER3H7PI.js → chunk-X3ZRTCJD.js} +1 -1
  171. package/chunks/{chunk-YRLW2MSX.js → chunk-XBVNNDPK.js} +11 -4
  172. package/chunks/{chunk-VL4YX3FH.js → chunk-XF63PKEN.js} +1 -1
  173. package/chunks/{chunk-FN5C6C7G.js → chunk-XLLQJRAH.js} +190 -478
  174. package/chunks/{chunk-3FSBMAYD.js → chunk-XQFT3QUF.js} +1 -1
  175. package/chunks/{chunk-FJWICLG7.js → chunk-Y3QL45LS.js} +1 -1
  176. package/chunks/{chunk-IKN3IUJP.js → chunk-Y4TPOUEV.js} +17 -9
  177. package/chunks/{chunk-NVK4HJYQ.js → chunk-YRZX2PHA.js} +1 -1
  178. package/chunks/{chunk-2SERYG4T.js → chunk-YTDVNSUQ.js} +1 -1
  179. package/chunks/{chunk-KWFP3WV6.js → chunk-YWQ237GX.js} +4 -193
  180. package/chunks/{chunk-6E4WHQJV.js → chunk-ZTP35P4B.js} +1 -1
  181. package/chunks/{chunk-E6FGMDFQ.js → chunk-ZU4UDIWX.js} +19 -1
  182. package/chunks/config-utils-JVGAP34H.js +113 -0
  183. package/chunks/contextCommand-XDB7OSO7.js +106 -0
  184. package/chunks/{core-runtime-PKNQ63VL.js → core-runtime-XHYA2RH7.js} +59 -61
  185. package/chunks/{create-sub-session-RVHSWVAP.js → create-sub-session-23ITIELI.js} +3 -3
  186. package/chunks/{create-sub-session-PLGPY6TV.js → create-sub-session-MHYCFNCM.js} +163 -84
  187. package/chunks/{cron-create-IJGVR5R2.js → cron-create-KJ4IY6PP.js} +57 -10
  188. package/chunks/{cron-delete-4N5BI6GL.js → cron-delete-MBLQCAYY.js} +4 -4
  189. package/chunks/{cron-list-2CATIPUX.js → cron-list-DWH2PQ47.js} +4 -4
  190. package/chunks/{daemon-NAYK4XXU.js → daemon-K5XS42ID.js} +516 -3
  191. package/chunks/{daemon-git-worktree-guard-LDBJXH2U.js → daemon-git-worktree-guard-V3P5XLHB.js} +56 -58
  192. package/chunks/daemon-status-provider-KKU7ZTLM.js +117 -0
  193. package/chunks/daemon-trust-policy-TZGK34DW.js +112 -0
  194. package/chunks/{daemon-trust-policy-monitor-IGEJPHQ4.js → daemon-trust-policy-monitor-F23DD2M4.js} +62 -64
  195. package/chunks/deferred-core-runtime-LKLGBCGZ.js +114 -0
  196. package/chunks/{display-image-DR55LL6M.js → display-image-BE7I6ZB6.js} +5 -5
  197. package/chunks/{dist-DJBNNPLN.js → dist-4WXLCBDR.js} +1 -1
  198. package/chunks/{dist-TJUDJTCB.js → dist-5VXFRTWU.js} +280 -48
  199. package/chunks/{dist-6H6RZ5H6.js → dist-6BPYWHVD.js} +89 -65
  200. package/chunks/{dist-ZBVOXSKF.js → dist-GFN5DCBX.js} +74 -65
  201. package/chunks/{dist-SATKXKUR.js → dist-HZNT6GQS.js} +1 -1
  202. package/chunks/{dist-FVJZSIO4.js → dist-LVZCXVPF.js} +72 -20
  203. package/chunks/{dist-7CKN54NL.js → dist-N6W6E753.js} +71 -64
  204. package/chunks/{dist-4Q7NTUNB.js → dist-QDLFKWOL.js} +2 -2
  205. package/chunks/{dist-UQUFZDIY.js → dist-SWQMYP2Q.js} +18 -16
  206. package/chunks/earlyInputCapture-NERYFYTR.js +107 -0
  207. package/chunks/{edit-KSPVUEAS.js → edit-ACCATIXB.js} +45 -47
  208. package/chunks/{en-6MDBWMTG.js → en-YLXBXM4Z.js} +2 -1
  209. package/chunks/{enter-worktree-UOZ6ZTP7.js → enter-worktree-35QCAAFP.js} +7 -7
  210. package/chunks/{enterPlanMode-DZTABSKN.js → enterPlanMode-NEN6EPVM.js} +44 -46
  211. package/chunks/{environment-JXOSW25V.js → environment-CKWHBC5T.js} +58 -60
  212. package/chunks/errors-XTZPNQ77.js +113 -0
  213. package/chunks/{exit-worktree-G2QATUCV.js → exit-worktree-XQD6D543.js} +7 -7
  214. package/chunks/exitPlanMode-CY344X5J.js +83 -0
  215. package/chunks/{fast-path-EBS7NAVL.js → fast-path-YSOX4PAM.js} +5 -5
  216. package/chunks/{gemini-JELEKOVS.js → gemini-VQY2YCPH.js} +110 -108
  217. package/chunks/{geminiContentGenerator-I2CKP7XI.js → geminiContentGenerator-PAPXNIGU.js} +8 -8
  218. package/chunks/{glob-TDVEVPQZ.js → glob-GVCUSBBF.js} +44 -46
  219. package/chunks/{goal-tools-CNVXMSZD.js → goal-tools-5STK6OXW.js} +10 -8
  220. package/chunks/{grep-3NLSS32D.js → grep-5GPICQBU.js} +6 -6
  221. package/chunks/handleAutoUpdate-ZJPJN42W.js +109 -0
  222. package/chunks/i18n-BZYKOST7.js +124 -0
  223. package/chunks/{image-gen-63NN2H5N.js → image-gen-J6FDGUZP.js} +8 -10
  224. package/chunks/initializer-GO2C5HE3.js +113 -0
  225. package/chunks/installationInfo-WM2GXQZ6.js +107 -0
  226. package/chunks/{keychain-token-storage-3MGFRYGM.js → keychain-token-storage-37XPR34V.js} +2 -2
  227. package/chunks/list-ERDJB7PQ.js +116 -0
  228. package/chunks/{list-agents-EXRSHIDJ.js → list-agents-DI3YFTDT.js} +2 -2
  229. package/chunks/loadedSettingsAdapter-7JOCVOKV.js +110 -0
  230. package/chunks/{loggingContentGenerator-CZWUAHAI.js → loggingContentGenerator-3ONLU3BS.js} +321 -30
  231. package/chunks/{loop-wakeup-XBPB2BRO.js → loop-wakeup-XJFONSQ7.js} +5 -5
  232. package/chunks/{ls-WFONAND2.js → ls-3VAT2J7A.js} +4 -4
  233. package/chunks/{lsp-DUKPDUXN.js → lsp-5W7IRUBH.js} +2 -2
  234. package/chunks/{managed-npm-update-54W23SP5.js → managed-npm-update-TLRCC2GE.js} +57 -59
  235. package/chunks/mcp-2LH3QXR2.js +110 -0
  236. package/chunks/{monitor-A4J5QRGJ.js → monitor-P7LDAL4W.js} +44 -46
  237. package/chunks/nonInteractiveCli-E5T7PZ27.js +184 -0
  238. package/chunks/{notebook-edit-4ANFZON5.js → notebook-edit-F7KSG4WQ.js} +45 -47
  239. package/chunks/{openaiContentGenerator-JCCJJQZC.js → openaiContentGenerator-2EXCZUGN.js} +26 -28
  240. package/chunks/pidfile-P25DWA5Q.js +111 -0
  241. package/chunks/process-registry-OAEG6WGC.js +662 -0
  242. package/chunks/{processUtils-JJAR56SO.js → processUtils-PVDKBL35.js} +2 -2
  243. package/chunks/prompt-terminal-ledger-AJ7T7OIV.js +105 -0
  244. package/chunks/{qwenContentGenerator-5JM44ZSA.js → qwenContentGenerator-TBBRLWIT.js} +49 -51
  245. package/chunks/{qwenOAuth2-JIYAWXTW.js → qwenOAuth2-OF5WRBQT.js} +7 -9
  246. package/chunks/{read-file-N3E5DY7N.js → read-file-6IIY4RLJ.js} +13 -13
  247. package/chunks/{read-mcp-resource-7A7NFSFL.js → read-mcp-resource-XD257PGJ.js} +2 -2
  248. package/chunks/{record-artifact-CPJ4IJSD.js → record-artifact-VW23GS4F.js} +4 -4
  249. package/chunks/{report-findings-VFM5DENC.js → report-findings-XA3FJG3N.js} +5 -5
  250. package/chunks/{request-shutdown-AXVOFN5C.js → request-shutdown-BCLA3JV2.js} +7 -7
  251. package/chunks/resumeHistoryUtils-44IETXDY.js +118 -0
  252. package/chunks/ripGrep-E7ESZ7SS.js +39 -0
  253. package/chunks/{run-qwen-serve-IWGPZADI.js → run-qwen-serve-DXBCYW7V.js} +356 -331
  254. package/chunks/{runtime-I76HPFUE.js → runtime-RUSPD35D.js} +67 -69
  255. package/chunks/scheduled-tasks-DVRJZO6I.js +127 -0
  256. package/chunks/{scheduler-M6YL4K4I.js → scheduler-TDRB2NSY.js} +58 -60
  257. package/chunks/{sdk-exporters-http-XXSKBD2C.js → sdk-exporters-http-TTU2CHBU.js} +2 -2
  258. package/chunks/{sdk-impl-VPNBLV7I.js → sdk-impl-UUMVYMAL.js} +11 -5
  259. package/chunks/{send-message-A2GXDX3B.js → send-message-AJ4OCUBV.js} +25 -9
  260. package/chunks/serve-KITOM7OR.js +118 -0
  261. package/chunks/{server-TWXRRREO.js → server-THCOMUC2.js} +5476 -4771
  262. package/chunks/{session-IT5ZXU7A.js → session-XKR5MPXE.js} +105 -106
  263. package/chunks/session-pr-refresh-KRBH7KDA.js +278 -0
  264. package/chunks/{settings-IX5HOOCG.js → settings-YXZ4XBKK.js} +61 -63
  265. package/chunks/{shell-LZ55AAJB.js → shell-5FJ2DZ3W.js} +44 -46
  266. package/chunks/{skill-WSZ53D2R.js → skill-VMMJZ6CJ.js} +71 -26
  267. package/chunks/skill-settings-7SG4YA2W.js +118 -0
  268. package/chunks/spawnChannel-BPMIXRBI.js +112 -0
  269. package/chunks/standalone-update-QN5HFRIC.js +118 -0
  270. package/chunks/{startInteractiveUI-O6BI2OSA.js → startInteractiveUI-5A7RQFRP.js} +2902 -2289
  271. package/chunks/{syntheticOutput-5AJ444QT.js → syntheticOutput-ML43GEFN.js} +3 -3
  272. package/chunks/{task-create-CDJSOEQD.js → task-create-QN4LYRSE.js} +11 -11
  273. package/chunks/{task-list-BELMPA75.js → task-list-JPLGK45U.js} +5 -5
  274. package/chunks/{task-stop-U3S3KZG2.js → task-stop-CXEH5L2D.js} +2 -2
  275. package/chunks/{task-update-77VCFDDZ.js → task-update-IWEOJFV2.js} +11 -11
  276. package/chunks/{team-create-UF4TMQEH.js → team-create-HVGVUL2L.js} +44 -46
  277. package/chunks/{team-delete-YWFXX77L.js → team-delete-RZVVSGCY.js} +5 -5
  278. package/chunks/{team-plan-approval-U57NSR6C.js → team-plan-approval-S5A5FQWU.js} +44 -46
  279. package/chunks/terminal-image-renderer-SIVPPXDI.js +114 -0
  280. package/chunks/theme-manager-TA33UZZX.js +102 -0
  281. package/chunks/{todoWrite-435EO2CS.js → todoWrite-WDJNQEMW.js} +43 -25
  282. package/chunks/{tool-search-SV35UV72.js → tool-search-NX7IPDKP.js} +18 -18
  283. package/chunks/total-session-admission-PWXZBOVX.js +114 -0
  284. package/chunks/{trustedFolders-YKK67IP3.js → trustedFolders-V6Y4CU3F.js} +57 -59
  285. package/chunks/{update-relaunch-DX7LVA4P.js → update-relaunch-CGSX3R37.js} +5 -5
  286. package/chunks/updateCheck-T5O2WGIB.js +118 -0
  287. package/chunks/useAutoAcceptIndicator-ULBJJWPY.js +120 -0
  288. package/chunks/{validateNonInterActiveAuth-B26O4YJP.js → validateNonInterActiveAuth-E5S2JSRC.js} +101 -102
  289. package/chunks/{version-K5D7VJ4B.js → version-LS5JPU36.js} +1 -1
  290. package/chunks/{web-fetch-VFHZLLNZ.js → web-fetch-RLMFZTS3.js} +16 -18
  291. package/chunks/{web-search-AQE5A4J5.js → web-search-LMCIC4N5.js} +9 -11
  292. package/chunks/{workflow-XL6AEUZ7.js → workflow-JBXU3IGA.js} +128 -94
  293. package/chunks/workspace-providers-status-525ORQBH.js +114 -0
  294. package/chunks/{workspace-registration-store-I4U3V5LL.js → workspace-registration-store-7Y44R4LV.js} +1 -1
  295. package/chunks/workspace-registry-HS4RWUSQ.js +123 -0
  296. package/chunks/workspace-service-7PXDM6EB.js +129 -0
  297. package/chunks/workspace-skills-status-FMJGC2XI.js +113 -0
  298. package/chunks/{workspace-trust-reconciler-GJ466KCE.js → workspace-trust-reconciler-3YTWSQBY.js} +70 -70
  299. package/chunks/write-file-BVQRYQQO.js +88 -0
  300. package/chunks/{zh-TW-MV4FWFB6.js → zh-TW-RPTMRTHG.js} +2 -1
  301. package/chunks/{zh-X3WGU6SI.js → zh-UNY63FR3.js} +2 -1
  302. package/chunks/{zoom-image-NYJPC3NI.js → zoom-image-FGCELNS4.js} +13 -13
  303. package/cli.js +14 -14
  304. package/locales/en.js +2 -0
  305. package/locales/zh-TW.js +2 -0
  306. package/locales/zh.js +2 -0
  307. package/package.json +3 -3
  308. package/web-shell/assets/{abnfDiagram-VCTEODGH-yq7z7Anb.js → abnfDiagram-VCTEODGH-2XZDeWYO.js} +1 -1
  309. package/web-shell/assets/{arc-CbJO0xnT.js → arc-B6UgMh90.js} +1 -1
  310. package/web-shell/assets/{architectureDiagram-5GKGNRK7-CRh89421.js → architectureDiagram-5GKGNRK7-DZgNFeN1.js} +1 -1
  311. package/web-shell/assets/{blockDiagram-NRAW4CY4-C75KAhKv.js → blockDiagram-NRAW4CY4-DQ2wvBFN.js} +1 -1
  312. package/web-shell/assets/{c4Diagram-UCG6FXSJ-BzlMveFB.js → c4Diagram-UCG6FXSJ-DMmQMrc4.js} +1 -1
  313. package/web-shell/assets/channel-D_TyfiTx.js +1 -0
  314. package/web-shell/assets/{chunk-2Q5K7J3B-DA8QDX3a.js → chunk-2Q5K7J3B-C8Zh2e9y.js} +1 -1
  315. package/web-shell/assets/{chunk-5VM5RSS4-Bgu7gQ7Y.js → chunk-5VM5RSS4-C3LTenP8.js} +1 -1
  316. package/web-shell/assets/{chunk-F27PBJKO-BOew7pIK.js → chunk-F27PBJKO-CxABOc8n.js} +1 -1
  317. package/web-shell/assets/{chunk-G27WJ6UU-DHx1I-hO.js → chunk-G27WJ6UU-CD_JxHDC.js} +1 -1
  318. package/web-shell/assets/{chunk-JWPE2WC7-SarFW0WU.js → chunk-JWPE2WC7-BdmZPmce.js} +1 -1
  319. package/web-shell/assets/{chunk-LCL6LL3I-BLzP083Z.js → chunk-LCL6LL3I-80bcHmfK.js} +1 -1
  320. package/web-shell/assets/{chunk-POPQ4Y6H-Bmkv_iKm.js → chunk-POPQ4Y6H-CNpLZyQF.js} +1 -1
  321. package/web-shell/assets/{chunk-SVP7TREG-MPx9r1N2.js → chunk-SVP7TREG-CXlKgMv3.js} +1 -1
  322. package/web-shell/assets/{chunk-XXDRQBXY-Ck-09rKG.js → chunk-XXDRQBXY-CivyYyvL.js} +1 -1
  323. package/web-shell/assets/classDiagram-DTDB5LWJ-Bw_H3xMd.js +1 -0
  324. package/web-shell/assets/classDiagram-v2-JRS7N3AN-Bw_H3xMd.js +1 -0
  325. package/web-shell/assets/{cose-bilkent-JH36ORCC-DWm6lfOG.js → cose-bilkent-JH36ORCC-COAjRH3w.js} +1 -1
  326. package/web-shell/assets/{cynefin-OW5HDTMX-DcBUyr5R.js → cynefin-OW5HDTMX-DqU1JHO6.js} +1 -1
  327. package/web-shell/assets/{cynefinDiagram-5FMLGOSQ-Df8qmd_p.js → cynefinDiagram-5FMLGOSQ-BCLI1Bvj.js} +1 -1
  328. package/web-shell/assets/{dagre-3AP2YEHR-DDmGspD7.js → dagre-3AP2YEHR-DmAz8nqF.js} +1 -1
  329. package/web-shell/assets/{diagram-S7CK7UJ4-BK_qFvbi.js → diagram-S7CK7UJ4-Ck-5QLT5.js} +1 -1
  330. package/web-shell/assets/{diagram-UQ7AKVKN-Uw3AP_bi.js → diagram-UQ7AKVKN-C0ljX0mW.js} +1 -1
  331. package/web-shell/assets/{diagram-VSXAHHWV-CJOQKxY7.js → diagram-VSXAHHWV-DPgvT24v.js} +1 -1
  332. package/web-shell/assets/{diagram-VX7I27RA-DhdvZna5.js → diagram-VX7I27RA-D7hWr1LR.js} +1 -1
  333. package/web-shell/assets/{diagram-Z3DM3KII-edQU0nlP.js → diagram-Z3DM3KII-D59tlOta.js} +1 -1
  334. package/web-shell/assets/{ebnfDiagram-PWID7BFC-4kugBEsO.js → ebnfDiagram-PWID7BFC-CY2DDx00.js} +1 -1
  335. package/web-shell/assets/{erDiagram-SSCWMZ5O-BMkFivWO.js → erDiagram-SSCWMZ5O-AR4h8Q8k.js} +1 -1
  336. package/web-shell/assets/{flowDiagram-A5DVABFB-BM1ea8g-.js → flowDiagram-A5DVABFB-DbF0HqBz.js} +1 -1
  337. package/web-shell/assets/{ganttDiagram-EL5Y4UJY-CXj_cKRf.js → ganttDiagram-EL5Y4UJY-J52mrIhc.js} +1 -1
  338. package/web-shell/assets/{gitGraphDiagram-WWUBYQGX-D0nEqwqu.js → gitGraphDiagram-WWUBYQGX-CcgMY5EV.js} +1 -1
  339. package/web-shell/assets/index-BjUg3vPr.css +5 -0
  340. package/web-shell/assets/index-CFwYYVFU.js +1873 -0
  341. package/web-shell/assets/{index-B6JN54Pd.js → index-MQs7l15A.js} +1 -1
  342. package/web-shell/assets/{infoDiagram-RXCK75RN-DF7aGGbQ.js → infoDiagram-RXCK75RN-Buu3ibp2.js} +1 -1
  343. package/web-shell/assets/{ishikawaDiagram-5VMMS53U-CZYNX1lL.js → ishikawaDiagram-5VMMS53U-CoKQxnF3.js} +1 -1
  344. package/web-shell/assets/{journeyDiagram-EYS64GPL-CvrqMUz1.js → journeyDiagram-EYS64GPL-CxK35mTC.js} +1 -1
  345. package/web-shell/assets/{kanban-definition-3QL26DDD-BZNJQZBj.js → kanban-definition-3QL26DDD-tk2CHTQ1.js} +1 -1
  346. package/web-shell/assets/{layout-Bf83x7D6.js → layout-DEpMwb4u.js} +1 -1
  347. package/web-shell/assets/{linear-Bkc3ES2f.js → linear-DSQsLh53.js} +1 -1
  348. package/web-shell/assets/{mermaid.core-C6xuovwz.js → mermaid.core-DIZuPBQT.js} +6 -6
  349. package/web-shell/assets/{mindmap-definition-FBJOCRG2-DzH6gVRg.js → mindmap-definition-FBJOCRG2-CCAvk8r_.js} +1 -1
  350. package/web-shell/assets/{pegDiagram-XKGWAZYB-DCvLh2fa.js → pegDiagram-XKGWAZYB-CpJ28jxz.js} +1 -1
  351. package/web-shell/assets/{pieDiagram-E7YTZNPT-BtjVlx_6.js → pieDiagram-E7YTZNPT-ziJR2R32.js} +1 -1
  352. package/web-shell/assets/{quadrantDiagram-AXDQQJYC-IbroqxFe.js → quadrantDiagram-AXDQQJYC-B60zE3Mh.js} +1 -1
  353. package/web-shell/assets/{railroadDiagram-O6MQD6OU-BvMykiWW.js → railroadDiagram-O6MQD6OU-M3GqQ_g1.js} +1 -1
  354. package/web-shell/assets/{requirementDiagram-EFPCY7ZU-D-HHMyrh.js → requirementDiagram-EFPCY7ZU-ntCtYqTk.js} +1 -1
  355. package/web-shell/assets/{sankeyDiagram-P5KCCOFB-C0XXhB6d.js → sankeyDiagram-P5KCCOFB-AYLoItcZ.js} +1 -1
  356. package/web-shell/assets/{sequenceDiagram-WJ2MYXX4-DseAXj5d.js → sequenceDiagram-WJ2MYXX4-DEWk4Mlj.js} +1 -1
  357. package/web-shell/assets/{sizeCapture-X5ZJPWSS-Cek2EweK.js → sizeCapture-X5ZJPWSS-OOWvzPay.js} +1 -1
  358. package/web-shell/assets/{stateDiagram-HBIQ2CUA-yLv8wafW.js → stateDiagram-HBIQ2CUA-BcMmvxem.js} +1 -1
  359. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DRLLqPWO.js +1 -0
  360. package/web-shell/assets/{swimlanes-XN3QIQJK-Cwz-opvE.js → swimlanes-XN3QIQJK-DuZTWWvh.js} +1 -1
  361. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-DOfLQQYt.js +8 -0
  362. package/web-shell/assets/{timeline-definition-24CTP7MA-BJuFD7OG.js → timeline-definition-24CTP7MA-CjqtZXEd.js} +1 -1
  363. package/web-shell/assets/{vennDiagram-4TSXK5OY-CNAavdoV.js → vennDiagram-4TSXK5OY-knVCVkQ4.js} +1 -1
  364. package/web-shell/assets/{wardleyDiagram-VM6X3IG4-DaIjAidq.js → wardleyDiagram-VM6X3IG4-yoWhw0a2.js} +1 -1
  365. package/web-shell/assets/{xychartDiagram-S5SC5T6Z-DkcQXjTy.js → xychartDiagram-S5SC5T6Z-ue14E_gL.js} +1 -1
  366. package/web-shell/index.html +3 -3
  367. package/chunks/MaxSizedBox-UUTUE42J.js +0 -112
  368. package/chunks/agent-headless-32O4L2CT.js +0 -87
  369. package/chunks/bridge-DFX2FFH4.js +0 -119
  370. package/chunks/channel-settings-store-ELLH2M4M.js +0 -117
  371. package/chunks/chunk-I6VVOZDK.js +0 -202
  372. package/chunks/chunk-JS73NNWF.js +0 -30
  373. package/chunks/chunk-K2OJUPOE.js +0 -78
  374. package/chunks/chunk-P3QQPMQA.js +0 -19
  375. package/chunks/chunk-SDVF6A42.js +0 -945
  376. package/chunks/chunk-VGHZT6JR.js +0 -51
  377. package/chunks/config-utils-7M6PCTLT.js +0 -113
  378. package/chunks/contextCommand-G4QG3TW6.js +0 -108
  379. package/chunks/daemon-status-provider-F5FEHVVG.js +0 -118
  380. package/chunks/daemon-trust-policy-C6YTU5CQ.js +0 -114
  381. package/chunks/deferred-core-runtime-GHT3GDOR.js +0 -116
  382. package/chunks/earlyInputCapture-HOSJTLWP.js +0 -109
  383. package/chunks/errors-IOXFDE4J.js +0 -115
  384. package/chunks/exitPlanMode-PZG4FXRB.js +0 -85
  385. package/chunks/handleAutoUpdate-AWAGHKOX.js +0 -111
  386. package/chunks/i18n-CYA25C5F.js +0 -126
  387. package/chunks/initializer-JD2FLNLO.js +0 -115
  388. package/chunks/installationInfo-N3DCWVWR.js +0 -109
  389. package/chunks/list-XDQAUET5.js +0 -118
  390. package/chunks/loadedSettingsAdapter-IK7H5JRK.js +0 -112
  391. package/chunks/mcp-2E7ZLMEE.js +0 -112
  392. package/chunks/nonInteractiveCli-ZNDXB62Y.js +0 -185
  393. package/chunks/pidfile-DHV2Y7UD.js +0 -113
  394. package/chunks/process-registry-PMJOA5CO.js +0 -172
  395. package/chunks/prompt-terminal-ledger-LDZ6QVYA.js +0 -107
  396. package/chunks/resumeHistoryUtils-YQRITQAG.js +0 -120
  397. package/chunks/ripGrep-NEIHKBKB.js +0 -39
  398. package/chunks/serve-XY5S63GN.js +0 -120
  399. package/chunks/skill-settings-5U7FTJIK.js +0 -120
  400. package/chunks/spawnChannel-TMC6VXGN.js +0 -113
  401. package/chunks/standalone-update-CUYGWTR4.js +0 -120
  402. package/chunks/terminal-image-renderer-DRL3BROJ.js +0 -116
  403. package/chunks/theme-manager-HHJPUDRN.js +0 -104
  404. package/chunks/total-session-admission-TQI7TCTL.js +0 -114
  405. package/chunks/updateCheck-6JRTKTUF.js +0 -120
  406. package/chunks/useAutoAcceptIndicator-MYXYSKSL.js +0 -122
  407. package/chunks/workspace-providers-status-H6TG34YV.js +0 -116
  408. package/chunks/workspace-registry-2PWNIYNI.js +0 -123
  409. package/chunks/workspace-service-4FGTUJLA.js +0 -131
  410. package/chunks/workspace-skills-status-7VOLUDT6.js +0 -115
  411. package/chunks/write-file-PWOYCT2K.js +0 -90
  412. package/web-shell/assets/channel-B3sGR79J.js +0 -1
  413. package/web-shell/assets/classDiagram-DTDB5LWJ-sTGo0K3v.js +0 -1
  414. package/web-shell/assets/classDiagram-v2-JRS7N3AN-sTGo0K3v.js +0 -1
  415. package/web-shell/assets/index-C-XlQn24.css +0 -5
  416. package/web-shell/assets/index-DLjXM0dn.js +0 -1860
  417. package/web-shell/assets/stateDiagram-v2-4QOOHH4V-DeThXnol.js +0 -1
  418. package/web-shell/assets/swimlanesDiagram-VK2B7HYN-GW0R1ZSv.js +0 -8
@@ -65,13 +65,13 @@ You cannot fix this yourself: the skill you are reading comes from that same bun
65
65
  It prints a JSON verdict; use it **verbatim**:
66
66
 
67
67
  - `target` — `{type: "pr-number", number}` | `{type: "pr-url", url, host, owner, repo, number}` | `{type: "file", path}` | `{type: "local"}`. A `pr-url` arrives validated and canonicalized (scheme/host lowercased, query and fragment dropped, the number required to end its path segment — `/pull/42oops` is not PR 42) with host/owner/repo/number extracted; do not re-classify tokens by hand. A token that merely looks like a URL is refused with a warning and reported in `extraTokens`, never guessed into a target.
68
- - `effort` + `effortSource` — the resolved level after defaults (**high** for PR targets, **medium** for local/file) and the `--comment` override (an **effective** `--comment` forces `high`; an ignored one on a non-PR target changes nothing). Two `settings.json` keys feed the defaults: `review.effort` replaces the built-in default when `--effort` is absent (`effortSource: "configured"`), and `review.comment: true` makes every PR review behave as if `--comment` was passed — the forcings above still apply. Both resolve from operator scopes only (system/user); a repository's `.qwen/settings.json` cannot set them. Do not re-derive it.
68
+ - `effort` + `effortSource` — the resolved level after remembered/configured defaults (**high** for PR targets, **medium** for local/file) and the `--comment` override (an **effective** `--comment` forces `high`; an ignored one on a non-PR target changes nothing). `last_used` means the project reused the last level the user explicitly typed, and it outranks `review.effort`. Two `settings.json` keys feed the configured defaults: `review.effort` replaces the built-in target default when neither an explicit nor remembered level applies (`effortSource: "configured"`), and `review.comment: true` makes every PR review behave as if `--comment` was passed — the forcings above still apply. Both resolve from operator scopes only (system/user); a repository's `.qwen/settings.json` cannot set them. Do not re-derive it.
69
69
  - `comment.requested` / `comment.effective` — `effective` is what gates Step 7 (true also when only the `review.comment` setting is on); `requested && !effective` means the user asked on a non-PR target, and the warning for that is already in `warnings`.
70
70
  - `fix.requested` / `fix.effective` — `--fix` is `--comment` reflected, and gated on the opposite target. `--comment` writes to a **pull request**, so it needs one; `--fix` writes to a **working tree**, so it needs one that outlives the review. A PR review's tree is the ephemeral worktree `fetch-pr` creates and Step 9 deletes, so `--fix` on a PR target is ignored with a warning — edits there are discarded minutes later, and reporting findings as "fixed" into a directory that no longer exists is worse than not fixing them. `effective` is what gates Step 6B. An effective `--fix` also floors the effort at **medium**: it edits the user's files, and low runs no verification, so applying an unverified finding is the same mistake as posting one, aimed at their working tree instead of a pull request. It does not force **high** — medium's findings are verified, and the reverse audit high adds hunts for findings that are _missing_, which is not what deciding whether to apply one turns on.
71
71
  - `severityFloor` + `severityFloorSource` — the posting floor for a PR review: `critical` posts only Criticals (otherwise-postable high-confidence Suggestions are recorded and deferred — Step 6's convergence posture; low-confidence and Nice-to-have findings stay terminal-only as ever), `suggestion` posts Criticals and Suggestions at every round, and `auto` — the default — is the **round-adaptive rule you resolve in Step 6**, where the round is known: `suggestion` through round 5, `critical` from round 6 — **or `critical` from any round once the recovered ledger's `flatRounds` streak has reached its bar** (Step 6's signal-driven trigger: the first-time-finding rate has not fallen for that many consecutive rounds, so the loop is re-deriving the same set and the floor stems it early). The parser cannot resolve `auto` itself (the round comes from the previous posted round's ledger, not fetched yet), so carry the verdict's value forward and resolve it there. Explicit flag beats the `review.severityFloor` setting beats `auto`; a non-PR target has no rounds, so the flag warns and is ignored there. The floor governs what the review **posts**, never what it finds, verifies, or reports in the terminal.
72
72
  - `topology` + `topologySource` — the shape of the run. `auto` (the default) runs the standing effort-driven pipeline described below. `minimal` runs the single-pass A/B comparison arm (Step 3M) instead — and when it is set, it OVERRIDES the effort dispatch entirely. In this step you run `parse-args` and the **diff capture only** (`fetch-pr` for a same-repo PR, the lightweight `fetch-diff` for a cross-repo PR, or the local capture for a local/file target — exactly as below), then jump straight to **Step 3M**. You SKIP the rest of Step 1's setup — the rules load, `pr-context`, `comment-status`, and the incremental-cache check — and you skip the fan-out, verification, reverse audit, and posting. `minimal` is terminal-only; the parser has already forced `comment.effective`, `fix.effective`, and `resume.effective` to false, and its warnings for that are in `warnings`. There is no configured topology — it is only ever an explicit flag.
73
- - `resume.requested` / `resume.effective` — `--resume` continues an interrupted run of the same PR instead of starting over. `effective` is what gates the resume branch below, and it is a TARGET-SHAPE gate rather than a promise: a cross-repo `pr-url` with no matching remote is `effective: true` but routes to lightweight mode, which never calls `fetch-pr` — item 3 below owns telling the user the flag is inert there. `requested && !effective` means a local or file target, or `--topology minimal` (a fresh single pass neither continues nor consumes an interrupted run), already warned in `warnings`. It never changes the effort: a continuation is pinned to the interrupted run's recorded level, and an explicitly different `--effort` makes `fetch-pr` refuse the resume and run fresh at the requested one.
74
- - `warnings` — surface every entry to the user, word for word.
73
+ - `resume.requested` / `resume.effective` — `--resume` continues an interrupted run of the same PR instead of starting over. `effective` is what gates the resume branch below, and it is a TARGET-SHAPE gate rather than a promise: a cross-repo `pr-url` with no matching remote is `effective: true` but routes to lightweight mode, which never calls `fetch-pr` — item 3 below owns telling the user the flag is inert there. `requested && !effective` means a local or file target, or `--topology minimal` (a fresh single pass neither continues nor consumes an interrupted run), already warned in `warnings`. The resolved effort source controls continuity: a target default is omitted so the interrupted run stays pinned to its recorded level; an explicit, remembered, configured, or comment-forced level is passed through, and a mismatch makes `fetch-pr` refuse the resume and run fresh at that required level.
74
+ - `warnings` — surface every entry to the user, word for word. When a warning says the last explicitly typed effort was reused, relay it as the opening line before starting the review.
75
75
  - `extraTokens` / `unknownFlags` — leftover input the parser refused to guess about; mention them to the user rather than silently dropping them.
76
76
 
77
77
  **Reference files, gated by this verdict.** This skill's conditional territory lives in `references/` beside it, and the verdict above already decides which of them this run needs — read each applicable one with `read_file` from this skill's base directory before the step that owns it:
@@ -112,7 +112,13 @@ For an **Aone Code** target — a `…/codereview/<id>` URL, a `pr-url` whose ve
112
112
  Based on the parsed `target.type`:
113
113
 
114
114
  - **`local`**: Review local uncommitted changes — staged, unstaged, **and untracked**. Capture them with `qwen review capture-local` (below); do not run `git diff` yourself. A `git diff` of any form reports changes to files git already **tracks**, and a file the user created but has not `git add`ed is in neither the index nor HEAD — so it appears in no `git diff` output at all. Reviews have skipped brand-new files this way — not judged low-risk, simply unseen (measured; DESIGN.md — The unseen untracked file).
115
- - If the capture's plan is empty (`chunks: []` — nothing staged, nothing unstaged, nothing untracked), inform the user there are no changes to review and stop heredo not proceed to the review agents
115
+ - **At medium effort, the cache is a LEDGER, never an anchor**: read the `findings` of the cache the capture names in its plan (`cachePath`)**read that field, do not compute the name**: `target` is derived inside the command and `safeTarget` is not hand-reproducible (past 64 characters it suffixes a digest, and symlink canonicalisation diverges from any hand recipe), so a predicted name misses exactly the spellings the canonicalisation exists for and the round then rules on zero entries over a Critical that still stands. At this effort the capture runs without `--cache`, so run it first and read the field off the plan Step 6 owes each entry a ruling at medium too, and a medium round that cannot see the previous high round's open Critical presents zero blockers over a blocker that still stands. Do NOT pass `--cache` to the capture and do NOT write the cache: incremental scoping and the cache write stay high-only, for the PR cache's exact reasons.
116
+ - **Incremental local rounds** (high effort only — the same gate, and the same reasons, as the PR cache): append `--cache .qwen/review-cache` to the `capture-local` command — **the DIRECTORY, not a file name you compute**. For a plain local round the file is `local.json` and either form works; for a FILE review the name is namespaced by the source path (`file-<target>-<digest>.json`), and `target` is derived inside the command from `--file`, so it does not exist yet when this step runs. Predicting it is the same hand-derivation the capture block forbids, wrong by construction — the name carries a digest only the command computes — and wrong in exactly the spelling classes canonicalisation exists for: `ln -s src srclink` then a review of `srclink/foo.ts` predicts from `srclink/foo.ts` while the command canonicalises to `src/foo.ts`, so the prediction misses and the round silently loses BOTH incremental scoping and the findings ledger, with no refusal line printed. Given the directory, the command resolves the file from the target it derived, and a directory holding no cache for this target reads as no anchor. **Do not pass a model**: the command rules the same-model gate over the identity the runtime published, not over a token you carry. A hand-carried one was wrong every time it was written, because `{{model}}` interpolates the BARE model id while the identity the CLI records is provider-qualified — two provider configurations exposing one model name compared equal and passed each other's gate, which is the whole contract. The command enforces the gates itself — same identity, same HEAD, content actually unchanged — and on any refusal falls back to the full capture with the reason on stderr; **repeat that line to the user**, whichever way it went. When it does scope incrementally, the plan carries an `incremental` block (changed files + one-import-hop interaction files, the rest left out) and the chunk briefs direct each agent accordingly; the rest of the flow reads the same plan shape it always did. **Also read the cache's `findings` ledger**: those are the previous local round's findings with their ids, and Step 6 owes each of them a ruling this round, exactly as on the PR path.
117
+ - If the plan carries `nothingToReview: { reason: "unchanged-since-last-round" }` — the field, not the stderr sentence; the capture writes it and `qwen review run` reads it, so a decided stop no longer reaches the parent as "Review did not complete" — **first check the cache's `findings` for open entries.** The state is byte-identical to the round that recorded them, so every open finding still stands VERBATIM — render the still-open list with ids and titles (no re-ruling is needed; nothing they describe can have changed), keeping severities distinct: open Criticals remain the round's blockers, open Suggestions are re-listed as open suggestions and block nothing. Then stop. Only when the cached ledger has no open findings does the stop read as clean: inform the user nothing changed since the previous round's clean review — name that round's verdict — and stop here. This is NOT the clean-tree case below: the tree is dirty, but it is byte-identical to the state the previous round already reviewed.
118
+ - If the plan carries `nothingToReview: { reason: "scope-emptied" }`, the round is decided the same way, for a different reason: the incremental slice kept zero sections — each anchored path has since been REMOVED (a file deleted, or the change discarded) or sits BYTE-IDENTICAL to what the previous round reviewed, and the stop gate does not distinguish the two. So split the cache's still-open findings by their CITED PATHS against the plan's `incremental.scope.supersededPaths` — the capture publishes exactly the paths whose recorded change is gone, and file PRESENCE cannot answer this (a discarded change leaves the file present with the cited bytes gone): a finding whose cited file IS IN `supersededPaths` is SUPERSEDED — the bytes it cited no longer exist and there is nothing left for it to block; **Never render these findings as still-standing blockers** and do not re-rule them — a verdict that rendered them as standing would repeat that contradiction every round, until HEAD or the model changes. A finding whose cited file is NOT in the list sits byte-identical to what the previous round reviewed — render it as still-standing exactly as the `unchanged-since-last-round` bullet above does (open Criticals remain the round's blockers; open Suggestions are re-listed as open suggestions and block nothing). Then stop. (Without this bullet the shape had no branch at all: `chunks: []` with an `incremental` block, so neither stop fired, `agent-prompt --roster` threw on the first diff-reading role, and the parent reported "Review did not complete" over a decided round.)
119
+ - If the plan has `chunks: []` and a NON-EMPTY `skippedFiles` and NO `nothingToReview`, that is not a stop and must never be reported as one: the capture read nothing AND could not read what it skipped. Report every skipped entry under "Not reviewed" with its reason, tell the user the working tree was not reviewed, and end the round WITHOUT a clean verdict — the absent field is the capture refusing to call this decided, and the round owes the user that distinction.
120
+ - If the plan has `chunks: []` and an EMPTY `skippedFiles` and NO `nothingToReview` on a plain local round, the capture withheld the stop field — a stop is a DECIDED outcome, and none of the shapes that land here is decided. In one, the tree MOVED while the capture was hashing it (`WARNING: 0 chunks, but the working tree changed while the capture was being hashed`); in another, a cached path DROPPED OUT of the capture while still on disk and diverges from HEAD (`WARNING: 0 chunks, but a cached path dropped out of this capture while still on disk and diverges from HEAD`) — an edit git cannot see (`git update-index --assume-unchanged` is the live case), which the anchor refusal above already named; in the third, tracked paths carry an `--assume-unchanged`/`--skip-worktree` bit (or the bits could not be enumerated), and `git diff` is blind to any edit on them (`WARNING: 0 chunks, but … carry an --assume-unchanged/--skip-worktree bit`, or the same sentence on stderr from an incremental round whose stop it withheld); in the fourth, the round ran with `--no-untracked`, so the untracked half was never enumerated — the clean-tree stop's third clause, checked by nobody, which is exactly the shape the oversized-skip recovery re-run lands in (`the tracked tree is clean, but untracked files were not enumerated (--no-untracked)`), and the two INCREMENTAL stops carry the same exclusion and withhold under the same flag (`The incremental scope kept nothing to review, but untracked files were not enumerated (--no-untracked)`): their comparisons cover tracked content only, and the gate admits no narrower round than the cache, so the cached round ran narrow too and a brand-new file is invisible to both. Never report nothing-to-review on any of these shapes and never take the clean-tree branch: for the `--no-untracked` shape do NOT re-run — report the untracked scope under "Not reviewed" and end the round without a clean verdict; for the others re-run `capture-local` once, and if the warning repeats tell the user — for the moved tree, that their tree is being modified while the review captures it; for the dropped-out path, that a file diverges from HEAD invisibly to git (an `--assume-unchanged`/`--skip-worktree` bit, or an ignore rule) and needs their inspection; for the visibility bits, which paths carry them and that clearing them (`git update-index --no-assume-unchanged` / `--no-skip-worktree`) restores reviewability — and end the round without a verdict. (A FILE review reaching this shape takes the no-diff branch below instead: a whole-file review reads the current state either way.)
121
+ - If the plan carries `nothingToReview: { reason: "clean-tree" }` (`chunks: []` — nothing staged, nothing unstaged, nothing untracked), inform the user there are no changes to review and stop here — do not proceed to the review agents. Read the FIELD, not the chunk count: a capture that SKIPPED files also has no chunks, and that round could not read what it skipped, so the capture withholds the field there and the round owes a "Not reviewed" section instead of a stop. `qwen review run` reads the same field, so this stop no longer reaches the parent as "Review did not complete". **First, the same ledger carve-out the no-changes stop above carries**: read the cache at the plan's `cachePath` and, if it holds open findings, render them with ids and severities before stopping — open Criticals as the round's still-standing blockers, open Suggestions as still-open suggestions. A clean tree is not a resolution: the common shape is a user who COMMITS the change without fixing the blocker, leaving a permanently clean tree, and without this the finding is never surfaced on any later round
116
122
 
117
123
  - **`pr-number`, or `pr-url` with a matching remote** (cross-repo `pr-url`s are handled by the lightweight mode above):
118
124
 
@@ -129,10 +135,9 @@ Based on the parsed `target.type`:
129
135
  # compose-review's own coverage recomputation — reads it from there, so they
130
136
  # cannot disagree about which agents a medium review owed. Omit it only if
131
137
  # the parser resolved the default high. On a FRESH run passing it always
132
- # is harmless; on a RESUME it is not — the ruling cannot tell a passed-
133
- # through default from a user's explicit choice, so follow the resume
134
- # bullet below: pass --effort only when the user chose a level in THIS
135
- # invocation.
138
+ # is harmless; on a RESUME it is not — pass it for explicit, last_used,
139
+ # configured, or forced-by-comment and omit it only for default, as
140
+ # detailed in the resume bullet below.
136
141
  # High-effort re-review with a cached anchor: append --since <lastCommitSha>
137
142
  # (the incremental check below) — the CLI validates the anchor and scopes
138
143
  # the diff and plan; never run git against an anchor yourself.
@@ -158,20 +163,20 @@ Based on the parsed `target.type`:
158
163
 
159
164
  Guessing the owner/repo here is not a recoverable mistake — a guessed repo has already stopped a review before it read a line of code (measured; DESIGN.md — The guessed fork repo). If `meta` fails, or the matcher exits 6 (no remote matches) or 7 (several do), say so and stop rather than picking one.
160
165
 
161
- Read `.qwen/tmp/qwen-review-pr-<n>-fetch.json` for: `worktreePath`, `baseRefName`, `headRefName`, `fetchedSha` (use as the **HEAD commit SHA** for Step 7), `isCrossRepository`, `diffStat` (files / additions / deletions), `emptyDiff` (**stop here**: the branch tree is byte-identical to its merge base — the work already landed or was superseded; tell the user and recommend close-as-superseded instead of fanning out agents over zero hunks — but first run `"${QWEN_CODE_CLI:-qwen}" review cleanup pr-<n>` to release the lease and remove the worktree just created, same as the same-SHA stop below: this stop is clean, yet without the cleanup the lease survives process exit and every later review of this PR refuses until it is deleted by hand), `collapsedFromUpstream` (disclose in the summary: overlapping merged PRs have collapsed this one to a residual — the review scope is the recomputed diff, and body claims about the rest are description-of-history, which Agent 0 should read accordingly), `prDescriptionHasHan` (the PR description contains Chinese — every posted inline comment must then be bilingual; see Step 7), and — when `--since` was passed — `incremental` (the anchor ruling the incremental-review check below acts on: `effective`/`upToDate`/`reason`) If the command fails (auth, network, PR not found), inform the user and stop. One failure needs a specific relay: a **lease conflict** says another session is already reviewing this PR. Same-PR reviews share one worktree path, so `fetch-pr` refuses rather than destroy the other session's worktree mid-run (#9205). Tell the user the PR is under review by another session and stop — do NOT delete the lease file to force the fetch: that file is the only protection the other session's state has, and removing it re-opens exactly the destruction this refusal prevents.
166
+ Read `.qwen/tmp/qwen-review-pr-<n>-fetch.json` for: `worktreePath`, `baseRefName`, `headRefName`, `fetchedSha` (use as the **HEAD commit SHA** for Step 7), `isCrossRepository`, `diffStat` (files / additions / deletions), `emptyDiff` (**stop here**: the branch tree is byte-identical to its merge base — the work already landed or was superseded; tell the user and recommend close-as-superseded instead of fanning out agents over zero hunks — but first write the stop sidecar exactly as the up-to-date stop below does (reason `empty-diff`, runId from `QWEN_REVIEW_RUN_ID`, skipped without the variable) and run `"${QWEN_CODE_CLI:-qwen}" review cleanup pr-<n>` to release the lease and remove the worktree just created, same as the same-SHA stop below: this stop is clean, yet without the cleanup the lease survives process exit and every later review of this PR refuses until it is deleted by hand), `collapsedFromUpstream` (disclose in the summary: overlapping merged PRs have collapsed this one to a residual — the review scope is the recomputed diff, and body claims about the rest are description-of-history, which Agent 0 should read accordingly), `prDescriptionHasHan` (the PR description contains Chinese — every posted inline comment must then be bilingual; see Step 7), and — when `--since` was passed — `incremental` (the anchor ruling the incremental-review check below acts on: `effective`/`upToDate`/`reason`) If the command fails (auth, network, PR not found), inform the user and stop. One failure needs a specific relay: a **lease conflict** says another session is already reviewing this PR. Same-PR reviews share one worktree path, so `fetch-pr` refuses rather than destroy the other session's worktree mid-run (#9205). Tell the user the PR is under review by another session and stop — do NOT delete the lease file to force the fetch: that file is the only protection the other session's state has, and removing it re-opens exactly the destruction this refusal prevents.
162
167
 
163
168
  Worktree isolation: all subsequent steps (agents, build/test) operate inside `worktreePath`, not the user's working tree. Cache and reports (Step 8) are written to the **main project directory**, not the worktree.
164
169
 
165
170
  - **Incremental review check** (high effort only — neither low nor medium consults or updates the cache): read `.qwen/review-cache/pr-<n>.json` **before** `fetch-pr` (it is a local file; nothing about it needs the fetch) and, when it holds a `lastCommitSha`, pass BOTH fields to the fetch verbatim: `--since <lastCommitSha> --since-model <lastModelId>` (omit `--since-model` when the cache has no `lastModelId`; do not substitute anything for it). **Copy them; do not compare them to anything.** The same-model gate is ruled inside `fetch-pr`, over the identity the runtime published — "clean up to `lastCommitSha`" is the recorded identity's verdict, and the command validates an anchor against the HISTORY, never against who certified it, so an anchor from another identity is ancestrally perfect and would scope this round past code it never reviewed. A hand-applied version of that gate was wrong every time it was written, because `{{model}}` interpolates the BARE model id while every identity the CLI records is provider-qualified: two provider configurations exposing one model name compared equal and passed each other's gate. When the gate refuses, the report says `cross-model-anchor` and the round reviews the full diff. Read the cache's `findings` ledger either way (Step 6 owes each entry a ruling; the work list carries across models, only the anchor does not). **You never run `git` against an anchor yourself** — no `git diff <sha>..HEAD`, no `cat-file`, no `merge-base --is-ancestor`: the command validates the anchor against the fetched history and computes the scoped diff and chunk plan in one pass, because a hand-run check is one a run can skip, and the hand-computed delta was exactly the shape this skill forbids everywhere else (the diff is a file the CLI writes, never a command you run). The report's `incremental` field is the decision; act on it with `lastModelId` from the cache and the current model ID (`{{model}}`):
166
171
  - `effective: true` (no `upToDate`) → the report's diff and plan ARE the incremental scope (`since..head`); continue with them exactly as with a full plan. The file set is **widened by one import hop**: a still-clean source file that imports a changed one re-enters the scope with its own full-range hunks, because the round before cleared it against the callee's OLD shape. `incremental.scope` names each file's class — `deltaFiles` (touched since the anchor), `interaction[]` (widened back in, each with the edges that did it), `contextFileCount` (weighed and passed over) — and a chunk brief built for an interaction file points its agent at that seam instead of a from-scratch re-review. **Also read the cache's `findings` ledger** (older caches have none — then there is nothing to track): these are the previous round's findings with their ids, and Step 6 owes each of them a ruling this round. (Reachable only under a matching identity: the gate inside the command is what keeps a cross-model anchor from scoping anything.)
167
- - `upToDate: true` **and** `comment.effective` is false (no `--comment` flag, and `review.comment` not enabled in settings) → inform the user "No new changes since last review" (this branch consumes no plan, so it holds even when `diffPath` is null), run `"${QWEN_CODE_CLI:-qwen}" review cleanup pr-<n>` to remove the worktree just created, and stop. **This branch does not apply on a resumed run** (`resumed: true` from the resume branch below): a continuation's `incremental` field is the interrupted attempt's history, not this run's decision, and taking the stop/cleanup here would destroy the very state `--resume` reused.
172
+ - `upToDate: true` **and** `comment.effective` is false (no `--comment` flag, and `review.comment` not enabled in settings) → inform the user "No new changes since last review" (this branch consumes no plan, so it holds even when `diffPath` is null). **Before the cleanup, write the stop sidecar** so `qwen review run` reads the round as DECIDED instead of exiting 1 "Review did not complete" over it: when the environment carries `QWEN_REVIEW_RUN_ID`, write `.qwen/tmp/qwen-review-pr-<n>-stop.json` containing exactly `{"reason": "<up-to-date|empty-diff>", "runId": "<the QWEN_REVIEW_RUN_ID value>"}` — the same reason+runId contract `capture-local` writes for local stops, runId copied verbatim (the parent's reader is nonce-fenced and discards any other stamp); without that variable no parent is reading and the file is not written. `cleanup` deliberately KEEPS this run's sidecar (it spares a `stop.json` whose `runId` matches the environment) so the parent can still read the decision after the child exits — do not remove it by hand; the next run's cleanup collects it. Then run `"${QWEN_CODE_CLI:-qwen}" review cleanup pr-<n>` to remove the worktree just created, and stop. **This branch does not apply on a resumed run** (`resumed: true` from the resume branch below): a continuation's `incremental` field is the interrupted attempt's history, not this run's decision, and taking the stop/cleanup here would destroy the very state `--resume` reused.
168
173
  - `upToDate: true` **but** `comment.effective` is true (the `--comment` flag or the `review.comment` setting) → run the full review anyway — the report already holds the full-range diff and plan for exactly this flow, unless `diffPath` is null, which is the ordinary degraded state (partial coverage, disclosed) rather than a scoping fact. Inform the user: "No new code changes. Running review to post inline comments."
169
174
  - `reason: cross-model-anchor` → the cached anchor was certified by another identity, so it was not used. Continue on the full-range plan (or, when `diffPath` is null, on the degraded state its siblings name). The command already said which identity certified it and which is running; repeat that to the user rather than restating it from the cache.
170
175
  - `effective: false` → the anchor was refused and the report says why. **Every reason names a CAUSE** — `not-an-ancestor` (a rebase or force-push); `unknown-commit`; `behind-merge-base` (the base moved past the anchor, e.g. a partial merge landed, and scoping to it would review base history the PR does not contain); `nothing-to-narrow` (the narrowing found nothing it could publish — all deterministic and all safe, because the round keeps the full range: an ordinary "undo per feedback" revert that puts lines back the way the base had them, so the PR's own diff no longer displays the undone FILE at all (a file the PR still displays does not refuse — the join fails closed and publishes its section whole instead); a capture on either side whose bytes do not survive a UTF-8 round trip; a delta the parser cannot read; and a fail-closed refusal where the two captures key the same change differently — a path or a rename git resolves differently across the two ranges — so narrowing would drop a change the PR's diff displays); `base-untrusted` (the base could not be fetched, so the clamp that keeps an anchor from scoping wider than the PR's diff could not be ruled); `capture-failed` (a capture threw, or the base fetch or merge-base resolution failed); `partition-failed` (the diff would not tile). **Whether a PLAN exists is a separate field: `diffPath`.** Non-null → the diff and plan are the full range; continue as a full review. Null → no diff exists at all: that is the `diffPath: null` degraded state (partial coverage, disclosed), whatever the reason says. Do not read one field for both facts — a reason that meant "planless" as well as "why" is what put deterministic refusals into the retry class below. The previous round's ledger is still owed its rulings in every refusal.
171
176
 
172
177
  - **When the cache has no anchor, the PR itself carries one** (high effort only, same as the cache). The file being absent is the NORMAL state everywhere except the machine that ran the last review — CI, another clone, a colleague's checkout — and it used to mean the incremental range silently degraded to the full diff every time, which is precisely the cost incremental review exists to avoid. The anchor now rides the posted review: the machine ledger's marker carries `sha`, the head the last clean round reviewed, and `pr-context` writes it into the side file `qwen-review-pr-<n>-prev-ledger.json` with the rest of the ledger. So when the cache had no anchor to pass — including the case where it HELD one that the cache-path gate withheld, because `lastModelId` was another model's: the marker may carry an anchor THIS model certified, and a round that stops at the cache would never look — **or the anchor it passed was refused** (`incremental.effective: false` — a rebase or force-push retires a cached anchor exactly when another environment may have posted a newer round whose marker still holds a valid one): proceed with the setup batch as usual, and when the side file lands with a `sha` — **different from the one already refused, OR the same sha when the refusal was infrastructure** (`base-untrusted`, `capture-failed`: the anchor was never ruled invalid, and the component that failed — a base fetch, a merge-base resolution, a capture — is re-run by the re-run. One shape of `capture-failed` retries ONCE, not forever: a base-less refusal (a null `mergeBaseSha`) means the base fetch failed (`baseFetchFailed: true`) and no local base ref remained, or `git merge-base` itself failed on a non-answer exit. The failed component IS re-run by the re-run, but the exit status cannot split the members — git exits 128 identically for a transient fetch fault and for a deterministic refusal (the base branch deleted on the remote — the refspec fetch fails every time), and the merge-base probe folds its surface failures the same way — so a second refusal of the same shape on the same sha is the deterministic member. Retry that one, once. Every other reason is deterministic for the same sha and must NOT be retried: a validity refusal re-refuses; a planless `partition-failed` always carries a `mergeBaseSha` — with no base nothing is captured and an empty diff cannot fail to tile — so both ranges were in hand and both refused to tile, which the re-run reproduces exactly, do not retry it; `nothing-to-narrow` re-narrows identically: the same two captures select the same hunks, and a capture that failed a UTF-8 round trip fails it again — and its base-less shape (a null `mergeBaseSha` with `baseFetchFailed: false`) is NOT retryable: the fetch succeeded and `git merge-base` found no common ancestor at all (a cross-fork PR with unrelated history), which a re-run reproduces exactly) —, **re-run the `fetch-pr` command from above with `--since <sha>` — REPLACING any `--since` it already carries, never appending a second one** (a repeated flag is one flag with two values; the CLI takes the last, but a command that reads as two anchors is a command nobody can check) — the PR ref is already fetched so the re-run is cheap, and it rebuilds the worktree, diff and chunk plan scoped to the delta, with the validation the old flow asked you to hand-run (`cat-file`, `merge-base --is-ancestor`) inside the command where it cannot be skipped. Then act on the new report's `incremental` field exactly as the cache path above does (**the same-model gate on this path is RULED FOR YOU, not left to you to apply**: the marker carries `model` beside its `sha` — the identity that certified the range — and `pr-context`'s ledger section states the verdict outright, either "the same-model contract HOLDS" or "**Do NOT pass the anchor above as `--since`**". Obey that sentence and do not compare the two identities yourself: the marker's `model` is a PROVIDER-QUALIFIED identity (`<model>@<digest>`) while `{{model}}` above is the bare model id, so they are not the same kind of string — comparing them by hand either never matches, which throws away this whole recovery path, or matches loosely, which accepts another provider's same-named model and scopes past code it never reviewed. A ledger section that states no verdict — because the side file survived from an earlier round the recovery could not re-vouch — is a mismatch: review the full range. The ledger's round is used only for precedence, and an `upToDate` anchor from the side file stops only when `comment.effective` is false **and the side file carries no `anchorFromRound`** — a grafted anchor that resolves to the head means the round it was carried for closed at a head its source had already certified, so `sha..HEAD` re-covers nothing, and the stop would abandon that round's owed work list without a ruling, with every later round at the same head repeating the same stop: proceed instead as when `comment.effective` is true (the re-run report already holds the full-range diff and plan) and rule every ledger entry). The decision lands AFTER the setup batch but BEFORE any agent launches, which is where the money is (a same-SHA stop still runs `cleanup`; it just fires three cheap commands later than the cache's fast path would have). An anchor that fails validation falls back to the full diff with the reason in the report, exactly as a rebased cache sha does. Two edges, both decided for you: if the side file's `round` is **higher** than the cache's, prefer the side file's sha — the cache is stale by a round some other environment posted; and a side file with no `sha` field means no anchor is recoverable. When the last posted round was fail-closed (`compose-review` withholds the anchor then — Step 8 names the conditions) and its work list survived whole, `pr-context` grafts the anchor forward from the most recent EARLIER own marker that carries one — the withhold is about the fail-closed round's own range, while the earlier round's "clean up to `sha`" stays true, and scoping `sha..HEAD` re-covers the gap (the ledger section says "anchoring at", never "reviewed at", when the anchor was carried forward this way, and names the round it was carried from). So a missing `sha` means a shape the graft refuses or cannot reach — the winning work list was truncated by the marker's size caps (a partial work list must not certify a range — the dropped entries would fall outside the grafted scope and retire silently), the only anchored own marker is the winner's own round (one round cannot both certify and withhold), the winner ran at the same head the candidate sha certifies (grafting it would hand Step 1 a same-sha stop that abandons the work list the winner still owes), every own round on the PR closed without an anchor, the only markers are other accounts' (the sha never crosses accounts), or the markers predate the field — and the review is full-range. (The side file may also carry `commitId` — the previous review's own `commit_id`. That is Step 6's **age reference** for the convergence posture, present even on fail-closed rounds; it is never an anchor, and scoping the diff to it would skip exactly the range a fail-closed round could not certify.)
173
178
 
174
- - **Resuming an interrupted run (`--resume`)**: when `parse-args` reported `resume.effective: true`, append `--resume` to the `fetch-pr` command above, and decide `--effort` off `effortSource`, not off whether the word `--effort` was typed. Pass the resolved level whenever `effortSource` is `explicit` **or `forced-by-comment`** (the `--comment` flag or the `review.comment` setting forces high — parse-args announces "running at high effort"); omit it ONLY when `effortSource` is `default`. `fetch-pr` cannot tell a passed-through default from a chosen level: the interrupted run may have recorded a different one, and handing it the resolved default refuses the resume (`effort-mismatch`) whose fresh fall-through discards the very state `--resume` exists to save — blaming an effort nobody asked for. Omitted, the continuation pins to the recorded level. A level this invocation actually requires — a user's explicit `--effort`, or the high that `--comment` forces — that differs from the recorded one is NOT a passed-through default: pass it, so a recorded lower level refuses (`effort-mismatch`) and runs fresh at the level this invocation needs. That is right — different effort is different work, and posting authority raising the required depth is different work too, never a silent pin. Omitting a `forced-by-comment` high is the trap: `fetch-pr` has no `--comment` input and reads `requestedEffort` only from `--effort`, so the null would pin the continuation at the recorded sub-high level while `--comment` stays effective — the "effective comment at medium effort" state the medium-tier rules call impossible, posting nothing (medium skips posting) or posting from a pipeline missing the high-only passes the forcing exists to guarantee. `fetch-pr` rules on the interrupted attempt's on-disk state itself (worktree still at `fetchedSha` and clean, diff bytes unchanged, PR head unmoved, resume cap unspent — every probe is a fact it gathers, none is yours to assert) and prints one JSON line on stdout. Branch on it:
179
+ - **Resuming an interrupted run (`--resume`)**: when `parse-args` reported `resume.effective: true`, append `--resume` to the `fetch-pr` command above, and decide `--effort` off `effortSource`, not off whether the word `--effort` was typed. Pass the resolved level whenever `effortSource` is `explicit`, `last_used`, `configured`, or `forced-by-comment` (the `--comment` flag or the `review.comment` setting forces high — parse-args announces "running at high effort"); omit it ONLY when `effortSource` is `default`. `fetch-pr` cannot tell a passed-through default from a chosen level: the interrupted run may have recorded a different one, and handing it the resolved default refuses the resume (`effort-mismatch`) whose fresh fall-through discards the very state `--resume` exists to save — blaming an effort nobody asked for. Omitted, the continuation pins to the recorded level. A level this invocation actually requires — a user's explicit `--effort`, the project's remembered level, a configured `review.effort`, or the high that `--comment` forces — that differs from the recorded one is NOT a passed-through default: pass it, so a mismatch refuses the resume (`effort-mismatch`) and runs fresh at the level this invocation needs. That is right — different effort is different work, and posting authority raising the required depth is different work too, never a silent pin. Omitting a `forced-by-comment` high is the trap: `fetch-pr` has no `--comment` input and reads `requestedEffort` only from `--effort`, so the null would pin the continuation at the recorded sub-high level while `--comment` stays effective — the "effective comment at medium effort" state the medium-tier rules call impossible, posting nothing (medium skips posting) or posting from a pipeline missing the high-only passes the forcing exists to guarantee. `fetch-pr` rules on the interrupted attempt's on-disk state itself (worktree still at `fetchedSha` and clean, diff bytes unchanged, PR head unmoved, resume cap unspent — every probe is a fact it gathers, none is yours to assert) and prints one JSON line on stdout. Branch on it:
175
180
  - **`{"resumed": true, ...}`** — this run continues the interrupted one. The report at the `--out` path is the PREVIOUS attempt's, deliberately left untouched (its mtime is the run epoch every downstream fence keys on); read it for the worktree, plan and diff, which are all reused. The report's `incremental` field is now HISTORY, not a decision to re-take: a resumed run proceeds on the reused plan and does NOT re-enter the incremental check above — in particular it never takes the `upToDate: true` stop/cleanup branch, which runs `cleanup pr-<n>` and would destroy the exact worktree and lease `--resume` just saved (the interrupted attempt was a `--comment` full review of an up-to-date PR; resuming it without `--comment` effective in THIS invocation would otherwise route it straight into "No new changes since last review" and abandon it). Then rebuild your working state from disk before launching anything:
176
181
 
177
182
  ```bash
@@ -218,7 +223,7 @@ Based on the parsed `target.type`:
218
223
  - **Attach repository context** at medium or high effort, before `agent-prompt --roster` (and therefore before launching agents): run `qwen review repo-context` with absolute `--plan`, `--worktree`, and `--out` paths. See the repository-context step in the Diff capture section below; for same-repo PRs the manifest is read from the trusted merge base recorded by `fetch-pr`.
219
224
 
220
225
  - **`file`** (e.g., `src/foo.ts`):
221
- - Run `"${QWEN_CODE_CLI:-qwen}" review capture-local --file <file> --target <filename> --out .qwen/tmp/qwen-review-<filename>-plan.json` to get its changes (`--out` is required — see the capture block below for the full form). An **untracked** target file is captured whole (every line reads as added), which is the right frame for a file that does not exist upstream yet. The path is taken relative to **your** working directory and must be inside the repo.
226
+ - Run `"${QWEN_CODE_CLI:-qwen}" review capture-local --file <file> --out .qwen/tmp/file-review-<first 24 chars of the basename>-<HHMMSS>-plan.json` to get its changes (`--out` is required, and the 24-char truncation is not optional a POSIX basename may run to 255 bytes, the decoration adds 29, and the full spelling dies with ENAMETOOLONG before the capture runs; the capture block below carries the same form and the reason). **A file review carries the same ledger and incremental rules as `local` above — read those four bullets and apply them here**: append `--cache .qwen/review-cache` at high effort (the DIRECTORY; the command resolves this target's file from the target it derives, and that name is namespaced by source path so it is not yours to spell), read the cache's `findings` at medium and high alike, and branch on `nothingToReview` exactly as they say. Without this the file-path ledger was write-only: Step 8 wrote it and nothing ever read it back, so round 2 of a high-effort file review presented zero blockers over a Critical round 1 had recorded as open. **Do not pass `--target` for a file review and do not compute one**: the command derives it from `--file`, using the same repo-relative canonicalisation and flattening `qwen review run` uses to name the artifacts it waits for. Applying that recipe by hand is what made the two disagree — the hand version normalises characters but does not canonicalise, so `ln -s src srclink` then a review of `srclink/foo.ts` had the parent waiting on one name while every child artifact carried another, and a review that had already run reported no verdict. An **untracked** target file is captured whole (every line reads as added), which is the right frame for a file that does not exist upstream yet. The path is taken relative to **your** working directory and must be inside the repo.
222
227
  - If the plan is empty (the file is tracked and unmodified), read the file and review its current state — see the no-diff branch below
223
228
 
224
229
  ### Diff capture and the review topology
@@ -247,8 +252,49 @@ For **local-diff and file-path reviews**, capture and plan in one command:
247
252
  ```bash
248
253
  "${QWEN_CODE_CLI:-qwen}" review capture-local --effort <effort> --out .qwen/tmp/qwen-review-local-plan.json
249
254
  # for a file-path review:
250
- "${QWEN_CODE_CLI:-qwen}" review capture-local --file <file> --target <filename> --effort <effort> \
251
- --out .qwen/tmp/qwen-review-<filename>-plan.json
255
+ "${QWEN_CODE_CLI:-qwen}" review capture-local --file <file> --effort <effort> \
256
+ --out .qwen/tmp/file-review-<first 24 chars of the basename>-<HHMMSS>-plan.json
257
+ # The plan's own `--out` is the ONE name you may choose: you write it and you
258
+ # read it back, so it cannot diverge from anything. Make it UNIQUE to this
259
+ # run and keep it BOUNDED — at most the first 24 characters of the basename
260
+ # plus a time suffix. Bounded, not merely "short": the decoration around it
261
+ # is 29 characters, a basename is itself allowed up to 255, and the plan
262
+ # write dies with ENAMETOOLONG before the capture runs — every round, for
263
+ # that target. The family deliberately does NOT start with `qwen-review-`:
264
+ # Step 9's `cleanup` sweeps `.qwen/tmp/qwen-review-<target>-*`, and any
265
+ # `qwen-review-…` family is inside SOME target's sweep — a file literally
266
+ # named `file` (or `file-<X>`) cleaned up while another file review ran
267
+ # swept that review's live plan mid-round and killed it on its next plan
268
+ # read. `file-review-…` is outside every sweep prefix, which is what makes
269
+ # the "cleanup must never glob its family" contract in Step 9 true. Truncating cannot collide within a run (the time suffix
270
+ # separates), and across runs it does not matter: you write this name and
271
+ # you read it back. Never the full PATH flattened into one name, for the
272
+ # same ceiling one level worse. One fixed name is not safe here: a file
273
+ # review takes no lease (leases are PR-only) and the plan is re-read all
274
+ # round long (`repo-context --plan`, `agent-prompt --roster`,
275
+ # `check-coverage`, `compose-review`, and Step 8's
276
+ # `cachePath`/`cacheCandidatePath`), so two concurrent file reviews
277
+ # overwrite each other's central artifact mid-run — the second round then
278
+ # reviews the first's file and merges its findings into the wrong ledger.
279
+ # It does not have to match anything the CLI derives; it only has to differ
280
+ # from another run's.
281
+ #
282
+ # Every OTHER artifact of this round — the roster, coverage,
283
+ # compose-review's `--out`, Step 8's cache name, Step 9's
284
+ # `cleanup <target>` — must carry the token the CLI derived, and the report
285
+ # hands it to you as **`target`**. READ IT; do not recompute it. `qwen review
286
+ # run` pins the artifact name it waits for from the same canonicalisation, and
287
+ # a stem flattened by hand agrees with it only where the two happen to: put a
288
+ # symlink below the repo root (`ln -s src srclink`, then review
289
+ # `srclink/foo.ts`) and every artifact you name misses the poll, so a review
290
+ # that has already run — and with --comment, already posted — reports that no
291
+ # verdict was produced.
292
+ #
293
+ # Never the basename either: the target keys the tmp stems AND the review
294
+ # cache, and `src/index.ts` and `test/index.ts` sharing the target
295
+ # `index.ts` would overwrite each other's cache, the second review erasing
296
+ # the first file's still-open findings. The CLI's token never collides that
297
+ # way; a hand-picked one can.
252
298
  # <effort> is the resolved level (local defaults to medium). It is recorded in
253
299
  # the plan so the roster, check-coverage and compose-review all read one value.
254
300
  ```
@@ -621,7 +667,7 @@ Then skip Steps 4 and 5 entirely and go to Step 6 with these adjustments:
621
667
 
622
668
  ### Deduplication
623
669
 
624
- Before verification, merge findings that refer to the same issue (same file, same line range, same root cause) even if reported by different agents. Keep the most detailed description and note which agents flagged it. When severities differ across merged items, use the **highest severity** — never let deduplication downgrade severity. **If a merged finding includes any deterministic source** (`[build]`, `[test]`), treat the entire merged finding as pre-confirmed — retain all source tags for reporting, preserve deterministic severity as authoritative, and skip verification.
670
+ Before verification, merge findings that refer to the same issue (same file, same line range, same root cause) even if reported by different agents. Keep the most detailed description and note which agents flagged it. When severities differ across merged items, use the **highest severity** — never let deduplication downgrade severity. **Deduplication merges the fix side too: keep every `fixWitness` and every sourced `fixConstraint` the merged findings carry.** Combine consistent constraints into one; when two conflict, adjudicate explicitly — re-read the named sources and keep the constraint the code actually bears — instead of silently discarding one with the less-detailed report. The most-detailed-description pick is about the claim's wording and cannot see a fix-side sentence only another agent's copy recorded, and canonicalization receives only the deduplicated record: a witness or a constraint dropped here reads as absent at posting, leaving the unwitnessed guard or the unconstrained fix these fields exist to prevent. **If a merged finding includes any deterministic source** (`[build]`, `[test]`), treat the entire merged finding as pre-confirmed — retain all source tags for reporting, preserve deterministic severity as authoritative, and skip verification.
625
671
 
626
672
  ### Batch verification
627
673
 
@@ -685,6 +731,7 @@ For each pattern group:
685
731
  - **Witness:** <the representative instance's witness — often the one sweep or probe that confirmed the whole pattern — or the group's shared `not run — <reason>` line; the witness rule reads an aggregate exactly as it reads a standalone finding>
686
732
  - **Suggested fix:** <general fix approach>
687
733
  - **Fix witness:** <the group's shared acceptance criterion — the test that must go red if the general fix is removed, or N/A>
734
+ - **Fix constraint:** <the existing fact the general fix must not violate, with its source — omit the line when none was observed>
688
735
  - **Severity:** <highest severity among the group>
689
736
 
690
737
  **Aggregation must not drop the anchors.** Each merged finding arrived with its own `Anchor`, and Step 7 posts one comment per location — so it needs one anchor per location, not one for the group. An aggregated entry sent to `resolve-anchors` with no `anchor` is a hard failure: the subcommand validates every entry and **throws on the whole batch**, so a single anchorless aggregate takes down the resolution of every other finding in the review. Carry the anchors through into the aggregate's `locations[]` — one entry per location, each with its own `anchor` — and Step 6's `findings --to-anchors` performs the expansion mechanically: one resolver request per location, ids suffixed `<id>-1`, `<id>-2`, … (resolutions are joined back to findings by id, so these must be unique — a suffix that collides with another finding's id is refused at projection, and the subcommand rejects duplicates besides).
@@ -712,13 +759,13 @@ One anomaly the builder flags but does not refuse (#9242): a per-chunk build on
712
759
  **The convergence pair — 3A (whole-diff form).** Rounds 1 and 2 launch **in one response** — together with Step 4's verifier shards (Step 4 names this) — each built by its own `agent-prompt` call: `--round 1` and `--round 2`, the **same** `--findings` file. This is not a loosened criterion; it is the serial shape's own arithmetic made concurrent: a dry round leaves the cumulative list unchanged, so round 2's launch input was already substantively identical to round 1's — the same entries, at most with verification tags the merge had cleared in between — an independent rerun that the serial shape bought with a full round of wall clock, and that one budget-gated run could no longer afford at all, shipping a capped verdict for want of a second dry audit it had time to run in parallel but not in series (measured; DESIGN.md — The serial convergence pair). What the two-consecutive-dry criterion demands is unchanged: two independent, substantively-dry audits of the whole diff. The one delta the pair does introduce is the same one-round suppression window the pipelined loop already accepts (the merge bullet in the termination rules): the round-2 member audits with entries a verifier may be rejecting mid-flight still on its do-not-re-report list.
713
760
 
714
761
  - **Both members dry** (substantive receipts, per the termination rules): the audit has converged. Wait for the riding verifiers' verdicts, apply the final merge, and proceed to Step 6.
715
- - **Either member reports findings**: the pair is one reporting round. Its members could not see each other, so first dedup the pair against itself (same defect, same location, same root cause keeps one, at the highest severity), merge into the cumulative list, and continue serially: the pair's verifiers ride with round 3's auditor — verify builds over the **deduped union**, sharded per Step 4's `verifyShard` exactly as any reporting round's findings are, **every shard passed as `--round 2`** (the pair's later label; never one build per member — the dedup already merged cross-member findings, and a per-member split would put one entry in front of two verifiers) — and convergence now needs two consecutive dry rounds from round 3 on. A dry member of a reporting pair is **not** carried forward as half of that evidence — its dry predates the other member's findings entering the list. One exception, and it is the retroactively-dry rule below, not a third rule: if a later merge retires the pair in full — every finding from both members rejected — the pair counts as the dry predecessor, and round 3's dry return ends the loop.
762
+ - **Either member reports findings**: the pair is one reporting round. Its members could not see each other, so first dedup the pair against itself (same defect, same location, same root cause keeps one, at the highest severity; a `fixWitness`/sourced `fixConstraint` on either copy survives the merge — Step 4's rule), merge into the cumulative list, and continue serially: the pair's verifiers ride with round 3's auditor — verify builds over the **deduped union**, sharded per Step 4's `verifyShard` exactly as any reporting round's findings are, **every shard passed as `--round 2`** (the pair's later label; never one build per member — the dedup already merged cross-member findings, and a per-member split would put one entry in front of two verifiers) — and convergence now needs two consecutive dry rounds from round 3 on. A dry member of a reporting pair is **not** carried forward as half of that evidence — its dry predates the other member's findings entering the list. One exception, and it is the retroactively-dry rule below, not a third rule: if a later merge retires the pair in full — every finding from both members rejected — the pair counts as the dry predecessor, and round 3's dry return ends the loop.
716
763
  - The substantive-return check applies per member, relaunch-once included. A twice-whiffed member makes the pair not dry — silence is not convergence evidence — and its scope joins the outstanding-whiffed-scopes list exactly as for any round.
717
764
  - If the deadline gate refuses one of the pair's builds (exit 4) and admits the other, launch the admitted member alone and treat the refusal as the budget stop it is (the termination rules below). If it refuses BOTH builds, nothing launches: the remaining budget cannot cover even one round plus the reserve, the first refusal's stop marker is the stop, and the two refusals each name their own round's stop entry — proceed to Step 6 and relay the MARKER's entry only (it holds the first refusal, and it is the one `compose-review` renders). The single-refusal split is defensive only: while the runtime's tool-concurrency pool holds both whole-diff members at once, the gate prices the paired round 2 at one round's wall, so it admits no dearer than the round 1 just admitted and that split cannot currently fire — the rule exists so a future pricing change degrades to the serial shape instead of to a guess.
718
765
 
719
766
  **The convergence pair — 3B (per-chunk form).** On 3B the pair applies per chunk. Launch `--all-chunks --round 1` **and** `--all-chunks --round 2` **in the same response** — both fan out to every chunk (rounds 1 and 2 always do, and the retirement schedule only reads history from round 3, so round 2's build needs nothing round 1 has produced yet), so each chunk's two establishing audits run concurrently instead of a round-wall apart. This is the same arithmetic as 3A read per territory: a chunk dry in round 1 leaves its slice of the cumulative list unchanged, so that chunk's round-2 auditor re-runs substantively the same audit — one round's wall the serial shape paid on every chunked review (measured; DESIGN.md — The serial 3B convergence rounds). The convergence contract is unchanged and reads per chunk through the retirement ledger: a chunk dry in both members holds its two-consecutive-dry certificate, and a pair dry on **every** chunk converges at the round-3 `--all-chunks` build (`CONVERGED`, exit 5) exactly as an all-dry pair does on 3A. Same one-round suppression window, per chunk (a round-2 auditor audits with entries a verifier may be clearing mid-flight). The launch coupling holds too: both members ride with the Step 4 verifier shards (Step 4 names this).
720
767
 
721
- - **Any auditor in either member reports findings**: the pair is one reporting round, exactly as on 3A — wait for BOTH fan-outs to return in full before the dedup (every chunk has an auditor in each member, and members cannot see each other across rounds either), dedup the pair against itself across rounds **and** chunks (same defect, same location, same root cause keeps one, at the highest severity), and merge the union into the cumulative list once. The pair's verifiers ride round 3's `--all-chunks` build: one batch over the **deduped union**, sharded per Step 4's `verifyShard`, **every shard passed as `--round 2`** (the pair's later label — never one build per member). Round 2's auditors are already in flight when round 1's returns land, so the pipelined k/k+1 rule below does not launch them again; this bullet is the pair's only transition. Convergence then reads per chunk through the retirement ledger as above: a chunk that reported in either member holds no certificate and stays under every-round audit, and the pair counts as one reporting round for the retroactively-dry rule — retired only when every finding from **both** members is rejected.
768
+ - **Any auditor in either member reports findings**: the pair is one reporting round, exactly as on 3A — wait for BOTH fan-outs to return in full before the dedup (every chunk has an auditor in each member, and members cannot see each other across rounds either), dedup the pair against itself across rounds **and** chunks (same defect, same location, same root cause keeps one, at the highest severity; a `fixWitness`/sourced `fixConstraint` on any copy survives the merge — Step 4's rule), and merge the union into the cumulative list once. The pair's verifiers ride round 3's `--all-chunks` build: one batch over the **deduped union**, sharded per Step 4's `verifyShard`, **every shard passed as `--round 2`** (the pair's later label — never one build per member). Round 2's auditors are already in flight when round 1's returns land, so the pipelined k/k+1 rule below does not launch them again; this bullet is the pair's only transition. Convergence then reads per chunk through the retirement ledger as above: a chunk that reported in either member holds no certificate and stays under every-round audit, and the pair counts as one reporting round for the retroactively-dry rule — retired only when every finding from **both** members is rejected.
722
769
  - If the deadline gate refuses one member's `--all-chunks` build (exit 4) and admits the other's, launch the admitted member alone and take the stop. The gate prices the round-2 build as the pair's wall — both fan-outs in waves of the runtime's tool-concurrency pool — so this split fires exactly when the pair plus the reserve does not fit but one round still does, and the admitted round alone keeps the serial shape. If it refuses BOTH builds, nothing launches: the remaining budget cannot cover even one round plus the reserve, the first refusal's stop marker is the stop, and the two refusals each name their own round's stop entry — proceed to Step 6 and relay the MARKER's entry only (it holds the first refusal, and it is the one `compose-review` renders).
723
770
 
724
771
  **Do not write the reverse auditor's prompt. Ask for it — and hand it the findings so far so it prints the whole block:**
@@ -801,8 +848,9 @@ For each **individual** finding, include:
801
848
  5. **Witness** — for a Critical: the observed output that settled the verdict, trimmed to the deciding lines — the probe's two sides, the A/B's quote pair, the sweep count over the real population, the failing test text — or the verifier's `not run — <reason>` line (Step 4's witness rule). A Suggestion carries one when a run produced it; it is not owed one.
802
849
  6. **Suggested fix** — Concrete code suggestion when possible
803
850
  7. **Fix witness** — the test that must go RED if that fix is removed (file + the behaviour it pins), or `N/A` when the fix adds no guard, branch or behaviour a test can pin. This is the ACCEPTANCE CRITERION for whoever fixes it, not the reviewer's evidence — `Witness` above is the evidence, and the two never substitute for each other.
851
+ 8. **Fix constraint** — an existing fact the fix must not violate, with its source (the quoted constant or `file:line`): a configured limit a new bound must stay within, a second site that reads the field a shape change touches, a uniqueness a newly shared resource's key must keep. `Fix witness` pins the fix's claim; this pins its premises — the class that passes a witnessed test and is still wrong. Omit it when none was observed — never `N/A` — and never without a source: a caution with no quoted fact ("be careful about concurrency") is not a constraint, and a wrong one is misdirection the fixer will follow.
804
852
 
805
- For **pattern-aggregated** findings, use the aggregated format from Step 4 (Pattern, Occurrences, Example, Failure scenario, Witness, Suggested fix, Fix witness, Severity) with the source tag added.
853
+ For **pattern-aggregated** findings, use the aggregated format from Step 4 (Pattern, Occurrences, Example, Failure scenario, Witness, Suggested fix, Fix witness, Fix constraint, Severity) with the source tag added.
806
854
 
807
855
  Group high-confidence findings first. Then add a separate section:
808
856
 
@@ -820,7 +868,7 @@ If there are none of these, omit this section.
820
868
 
821
869
  ### Previous round's findings (incremental re-review only)
822
870
 
823
- The ledger has two sources, in priority order: **the PR itself** — `pr-context` recovers the machine ledger embedded in this account's last posted review and renders it as the "Previous /review round (machine ledger)" section (also written beside the context file as `qwen-review-pr-<n>-prev-ledger.json`) — and, as fallback for rounds that never posted, the local cache. The PR copy is authoritative because it survives what the cache cannot: CI, another machine, a fresh clone. **This ruling section runs at medium effort too** — recovering the ledger costs nothing (pr-context already fetched the reviews), and a re-review that ignores what it told the author last round is the amnesia this exists to end; medium still writes no cache and posts nothing, exactly as before. When either source loaded a ledger, this review is **round N+1 of the same PR**, and the single most useful thing it can tell the reader is what happened to round N's findings — a re-reviewer who only lists new findings leaves the author to diff two reports by hand. Rule on **every** ledger entry against the code at the reviewed commit, exactly the way the open-Criticals re-check below rules (trace the mechanism; the diff containing a fix is not the same claim as the defect no longer firing):
871
+ The ledger has two sources, in priority order: **the PR itself** — `pr-context` recovers the machine ledger embedded in this account's last posted review and renders it as the "Previous /review round (machine ledger)" section (also written beside the context file as `qwen-review-pr-<n>-prev-ledger.json`) — and, as fallback for rounds that never posted, the local cache. (A local or file-path review has no PR to post to, so its only source IS its cache — read at the plan's `cachePath`, never a name you compute (the capture derives `target` inside itself, and a file review's cache name carries a source-path digest besides); Step 1's incremental check already read it; the rulings below apply to its entries unchanged.) The PR copy is authoritative because it survives what the cache cannot: CI, another machine, a fresh clone. **This ruling section runs at medium effort too** — recovering the ledger costs nothing (pr-context already fetched the reviews), and a re-review that ignores what it told the author last round is the amnesia this exists to end; medium still writes no cache and posts nothing, exactly as before. When either source loaded a ledger, this review is **round N+1 of the same PR**, and the single most useful thing it can tell the reader is what happened to round N's findings — a re-reviewer who only lists new findings leaves the author to diff two reports by hand. Rule on **every** ledger entry against the code at the reviewed commit, exactly the way the open-Criticals re-check below rules (trace the mechanism; the diff containing a fix is not the same claim as the defect no longer firing):
824
872
 
825
873
  - **fixed** — the mechanism can no longer fire. Say so, by id, in one line: `R1-2 fixed by <what>`. Do not re-report it as a finding. The sibling-entrance rule from the re-check below applies here unchanged: for a divergence-class entry, `fixed` is a ruling about the family's entrances, checked one by one — for a **bounded** family a still-open sibling becomes a fresh `R<round>-<n>` entry (for an unbounded surface, apply the bounded/unbounded rule below instead of filing the sibling), never a reason to withhold the original's `fixed`.
826
874
  - **still stands** — re-report it **under its original id**, updating the location if the code moved. It keeps its severity; a still-standing Critical blocks exactly as a new one would. Write that id into the re-report itself, immediately after the severity marker — `**[Critical]** R1-2: <the claim>` — and into the body entry if it cannot be anchored (`R1-2 <the claim>`). That prefix is not decoration: `compose-review` reads it back out of the comment when it builds the marker, and it is the only way an id survives into the machine ledger the next round recovers. Omit it and the same claim comes back renumbered, which is exactly what carrying the id forward exists to prevent.
@@ -938,7 +986,7 @@ Write every confirmed finding — high and low confidence alike — as a JSON ar
938
986
 
939
987
  **One finding, one name.** A high-effort PR review also writes the incremental cache's cross-round `findings` ledger (Step 8), whose ids are `R<round>-<n>` — use those same ids here: a finding that will enter the ledger gets its `R<round>-<n>` as the artifact `id`, and a carried-forward finding keeps the id it already has. Two id schemes for one finding is how "R1-2" in next round's report and "f7" in this round's outcome ledger turn out to be the same defect that nobody can join. A finding the convergence posture deferred is still a confirmed finding and enters this artifact with all its fields — the deferral is a posting decision recorded in the compose state, never a severity change and never a reason to leave the artifact — but under its own id sequence, `D<round>-<n>`, **never consuming an `R<round>-<n>`**: the `R` counter must predict `buildLedger`, which numbers POSTED findings only, and a deferred finding holding `R6-2` would hand next round a ledger whose `R6-2` names a different defect than this round's artifact — the exact join "one finding, one name" exists to keep.
940
988
 
941
- Each entry carries `id` (unique — outcomes and resolved anchors both join on it), `severity`, `confidence`, `source`, `summary`, `failureScenario`, and either `file`/`line`/`anchor` or, for a pattern aggregate, a `locations[]` array with **one entry per location** (`suggestedFix`, `fixWitness`, `category`, `shortSummary` and `witness` are optional; `shortSummary` is derived from `summary` when absent; `witness` is the Step 4 witness — the executed evidence, or its `not run — <reason>` line — carried as data so the report and the comment bodies quote one recorded string instead of transcribing it twice more; `fixWitness` is the acceptance criterion the finding format asks for — the test that must go red if the suggested fix is removed, or `N/A` — carried for the same reason and read back by Step 7's comment body). The command validates the shape, refuses a duplicate id, refuses a finding with no failure scenario, sorts by severity → confidence → file → line → id, and writes counts nobody then recomputes by hand. Read the artifact for the numbers you quote in the Summary. This is a **canonicalization**, not a gate: it does not decide the verdict — `compose-review` does that, from the same findings — and it does not run at low effort, where the pass is unverified and emits no verdict.
989
+ Each entry carries `id` (unique — outcomes and resolved anchors both join on it), `severity`, `confidence`, `source`, `summary`, `failureScenario`, and either `file`/`line`/`anchor` or, for a pattern aggregate, a `locations[]` array with **one entry per location** (`suggestedFix`, `fixWitness`, `fixConstraint`, `category`, `shortSummary` and `witness` are optional; `shortSummary` is derived from `summary` when absent; `witness` is the Step 4 witness — the executed evidence, or its `not run — <reason>` line — carried as data so the report and the comment bodies quote one recorded string instead of transcribing it twice more; `fixWitness` is the acceptance criterion the finding format asks for — the test that must go red if the suggested fix is removed, or `N/A` — carried for the same reason and read back by Step 7's comment body; `fixConstraint` is the existing fact the fix must not violate, with its source — present only when the finder observed one, with no `N/A` form (the command drops the literal), and read back by the same comment body). The command validates the shape, refuses a duplicate id, refuses a finding with no failure scenario, sorts by severity → confidence → file → line → id, and writes counts nobody then recomputes by hand. Read the artifact for the numbers you quote in the Summary. This is a **canonicalization**, not a gate: it does not decide the verdict — `compose-review` does that, from the same findings — and it does not run at low effort, where the pass is unverified and emits no verdict.
942
990
 
943
991
  **Then speak the same list to the client, in-band — one `report_findings` tool call.** The artifact is the canonical record, but it is a file on disk registered after the fact (Step 8); every client rendering this session live — the TUI, the Web Shell transcript, an ACP host — otherwise sees only the prose restatement, which is the transcription surface the artifact exists to close. Immediately after the artifact is written, call the `report_findings` tool once (load it via `tool_search` if it is not in your tool list) — each call replaces the whole list, and Step 6B re-issues it with outcomes after a fix run — with `level` set to this review's effort and one entry per finding **copied from the artifact you just wrote** — `id`, `severity`, `confidence`, `source`, `file`/`line` (a pattern aggregate passes its first location; the artifact keeps the rest), `summary`, `shortSummary`, `failureScenario`, `category` — never re-typed from the terminal prose: the artifact is the oracle, and a re-derived severity here is the same drift the marker rule below closes. A finding the convergence posture deferred is still a finding — report it under its `D<round>-<n>` id like any other. **The tool's contract is harder-bounded than the artifact's, and a violation refuses the whole call**: at most 50 findings, with per-field length caps the schema states. When the artifact outgrows those bounds, do not let the call die on them — pass the first 50 findings in artifact order (the artifact is already sorted most-severe-first) and say in the terminal summary how many the cap cut, and shorten an over-cap `summary`/`failureScenario` — or `outcomeNote` on the Step 6B re-report — to fit rather than dropping the entry (the artifact keeps the full-length text, so nothing is lost by a delivery-only shortening). This is the one sanctioned departure from copy-verbatim, and it is a departure of length only, never of severity, confidence, or meaning — a bounded list delivered beats a complete list refused. This call is UI delivery, not bookkeeping: it persists nothing and decides nothing, and a failure (or an environment where the tool is not registered and `tool_search` cannot find it) is disclosed and moved past — never a reason to touch the artifact, the compose state, or the verdict, exactly the rule `record_artifact` follows in Step 8.
944
992
 
@@ -965,7 +1013,7 @@ Each entry carries `id` (unique — outcomes and resolved anchors both join on i
965
1013
  It prints a `Verdict:` line to stderr. **That line is the verdict — print it, and nothing else.** It writes nothing, posts nothing, and needs no authorisation, so run it on every verified review — **high and medium** — whether or not you are going to post. The state file is the same one Step 7 uses (every field is listed just below): your findings and the states you established — the body Criticals, the discarded suggestions, the `cannot tell` blockers, the unreviewed dimensions, the `planPath`, the `findingsPath` (high effort — the cumulative reverse-audit findings file, for the `— [unverified]` check), the presubmit flags, the model id. It does **not** take the coverage or the inline counts, and it **refuses** a state JSON carrying `criticalsInline`/`suggestionsInline`. It derives coverage from the harness's transcripts, and it **counts** the inline findings from `--comments`: write the drafted inline comments to that file first — the same `[{path, line, body, …}]` array the Step 7 payload will carry, each body opening with its `**[Critical]**`/`**[Suggestion]**` marker; a review with nothing anchored inline passes a file containing `[]`. A report-only run has read Approve over a blocker its own report listed (measured; DESIGN.md — The Approve over a relocated Critical); counted from the draft, that finding cannot fall out of the computation. **If the comment set changes after composing** — an anchor fails to resolve, a finding relocates to the body, a comment is dropped — update the comments file (and the state), and run `compose-review` again: the verdict must be computed from the set you actually post, and Step 7's `submit` recounts from the payload to hold you to it.
966
1014
 
967
1015
  - **Not `criticalsInline` / `suggestionsInline`.** `submit` counts those off the `**[Critical]**` / `**[Suggestion]**` prefixes of the comments you attached — a number beside a list is a number that can disagree with the list, and one did. A `state` that supplies either is refused.
968
- - `bodyCriticals` — descriptions of unmappable or 422-relocated Criticals (their only copy lives in the body; they count toward `C` like anchored ones); a `Critical` entry placed in `deferredSuggestions` is relocated here, never deferred.
1016
+ - `bodyCriticals` — descriptions of unmappable or 422-relocated Criticals (their only copy lives in the body; they count toward `C` like anchored ones); a `Critical` entry placed in `deferredSuggestions` is relocated here, never deferred; an entry whose finding carries a `fixWitness` or a `fixConstraint` appends the corresponding sentence, copied from the artifact — the only published copy of the finding must not post without the fix's witness or the premise it rests on.
969
1017
  - `suggestionsDiscarded` — how MANY Suggestions lost their anchors to offline validation or the 422 recovery: a count (non-negative integer). The list of discarded items itself is also accepted and counted by its length (`[]` is zero). They still count toward `S`: dropping every anchor must never upgrade the verdict.
970
1018
  - `suggestionsDroppedAsDuplicates` — one entry per **confirmed** Suggestion you did not re-post because it is already reported on the PR (a prior round, a concurrent reviewer, an overlap drop), each naming the finding and where it already lives — never the finding's own text: Suggestion text must never appear in the review `body`, because `.github/workflows/qwen-autofix.yml` does not filter review bodies, so a Suggestion copied into the body would be handed to the autofix bot (full rule in `references/posting.md`); the carve-out for this account is exactly that name + location, e.g. `R1-2 loose review-config pins — already reported (comment 3788857379)`. Use this INSTEAD of bumping `suggestionsDiscarded` for duplicate drops: the two render different sentences, and the discarded one asserts an anchor failure that never happened. They still count toward `S`.
971
1019
  - `cannotTellCriticals` — one line per existing PR Critical whose Step 6 re-check landed on `cannot tell` (location + what could not be determined).
@@ -1059,7 +1107,7 @@ Run the bundled cleanup subcommand:
1059
1107
  "${QWEN_CODE_CLI:-qwen}" review cleanup <target>
1060
1108
  ```
1061
1109
 
1062
- `<target>` is the same suffix used throughout (`pr-<n>`, `local`, or filename). The command removes the worktree at `.qwen/tmp/review-pr-<n>` (PR targets only), deletes the local branch ref `qwen-review/pr-<n>`, and clears any `.qwen/tmp/qwen-review-<target>-*` side files (review JSON, PR context, presubmit / findings reports). It is idempotent — missing files are silent OK. It is also lease-guarded: when another session still holds this PR's worktree lease, cleanup skips the target wholesale and prints a `note:` line saying so (#9205) — relay that note verbatim and leave the lease file alone; the holder's own cleanup releases it. For PR targets it first **audits the review window**: any issue comment the reviewing account posted — or edited — since `fetch-pr` opened the window (the boundary reaches back across drift restarts and a clock-skew allowance), and any **review** the account submitted that `submit`'s receipt does not vouch for, is flagged with `warning:` lines, because submit's one sanctioned write is receipt-recorded and never touches issue comments (Step 7's write ban) — so such a comment is most likely an external same-account write — something the user did by hand from another terminal, or **another workflow posting under the same account** (in CI the review shares the bot identity with precheck/triage; their marker-stamped comments are filtered out automatically, but this reading stays real for anything unmarked) — and is a write that bypassed the gate only if its content is this review's own output. On an **Aone target** the audit runs through the `a1` CLI and the ruling keys on comment ids instead of review ids, because there the sanctioned submit POSTS COMMENTS (the inline findings and the summary — Aone has no review object): any MR comment the authenticated account posted — or edited — inside the window whose id the submit receipt does not vouch for is flagged the same way (a marker-stamped comment is filtered as on GitHub; a submitted comment whose id was never read back is unvouchable and may draw a flag — over-flagging is the fail-safe direction). Because the default listing hides RESOLVED comments, the audit unions it with a `--resolved` query — a bypass posted-then-resolved inside the window is still flagged — but a resolved comment is judged by its CREATION only (a resolution bumps `updatedAt` exactly like an edit, so it is not edit evidence). Five disclosed residuals: an edit of a submit-posted (receipt-vouched) comment is outside the tripwire's sight; an edit of an UNVOUCHED pre-window comment is invisible once its discussion is resolved (a resolved comment is judged by its creation only — a resolution bump is not edit evidence); resolved replies have no a1 listing at all; the comment listing is unpaged (one `comment list` per query — if a1 caps a page, comments past the cap stay invisible); and `a1 repo mr approve` / `a1 repo mr edit` writes are banned in Step 7 but outside this tripwire's coverage (the recorded a1 surface exposes no listing an audit could query for them). **Relay those `warning:` lines verbatim in your terminal summary** — the user can dismiss their own comment; a bypass they were never told about, they cannot. The audit is best-effort: when it cannot run (offline, unauthenticated, no report) it says so once on stderr — `note: bypass audit skipped (…)` — so a skipped audit is never mistaken for a clean one. Also remove `.qwen/tmp/qwen-review-parse-args.json` and the session args directory `.qwen/tmp/s-<session>/` (the path from the `<skill-args>` note) — both are written before the target suffix is known, so the pattern above misses them. (Leave the args file in place if you had to fall back to writing it yourself and the run failed: it is the only record of what the review was actually asked to do.)
1110
+ `<target>` is the same suffix used throughout (`pr-<n>`, `local`, or filename). **A FILE review whose derived token collides with a RESERVED one — `local`, `pr`, or `pr-<n>` — must NOT run this command at all** (a repo-root directory or file literally named `local` — or `pr`, whose sweep prefix engulfs EVERY PR family and whose lease guard never runs on the bare token; the CLI refuses that one itself — derives exactly such a token): the sweep is a prefix match over a shared namespace, so `cleanup local` from a file review deletes a concurrent whole-tree round's live plan and its `-prompts` records mid-round, and neither is lease-guarded. Skip the command, remove only the artifacts you wrote (the plan `--out` and its `-prompts` directory, per the paragraph below), and say so in the terminal — leaking this target's other side files is the affordable side of that trade. (The general prefix-collision class, and the namespace fix that ends it, is tracked in issue #10057.) The command removes the worktree at `.qwen/tmp/review-pr-<n>` (PR targets only), deletes the local branch ref `qwen-review/pr-<n>`, and clears any `.qwen/tmp/qwen-review-<target>-*` side files (review JSON, PR context, presubmit / findings reports). It is idempotent — missing files are silent OK. It is also lease-guarded: when another session still holds this PR's worktree lease, cleanup skips the target wholesale and prints a `note:` line saying so (#9205) — relay that note verbatim and leave the lease file alone; the holder's own cleanup releases it. For PR targets it first **audits the review window**: any issue comment the reviewing account posted — or edited — since `fetch-pr` opened the window (the boundary reaches back across drift restarts and a clock-skew allowance), and any **review** the account submitted that `submit`'s receipt does not vouch for, is flagged with `warning:` lines, because submit's one sanctioned write is receipt-recorded and never touches issue comments (Step 7's write ban) — so such a comment is most likely an external same-account write — something the user did by hand from another terminal, or **another workflow posting under the same account** (in CI the review shares the bot identity with precheck/triage; their marker-stamped comments are filtered out automatically, but this reading stays real for anything unmarked) — and is a write that bypassed the gate only if its content is this review's own output. On an **Aone target** the audit runs through the `a1` CLI and the ruling keys on comment ids instead of review ids, because there the sanctioned submit POSTS COMMENTS (the inline findings and the summary — Aone has no review object): any MR comment the authenticated account posted — or edited — inside the window whose id the submit receipt does not vouch for is flagged the same way (a marker-stamped comment is filtered as on GitHub; a submitted comment whose id was never read back is unvouchable and may draw a flag — over-flagging is the fail-safe direction). Because the default listing hides RESOLVED comments, the audit unions it with a `--resolved` query — a bypass posted-then-resolved inside the window is still flagged — but a resolved comment is judged by its CREATION only (a resolution bumps `updatedAt` exactly like an edit, so it is not edit evidence). Five disclosed residuals: an edit of a submit-posted (receipt-vouched) comment is outside the tripwire's sight; an edit of an UNVOUCHED pre-window comment is invisible once its discussion is resolved (a resolved comment is judged by its creation only — a resolution bump is not edit evidence); resolved replies have no a1 listing at all; the comment listing is unpaged (one `comment list` per query — if a1 caps a page, comments past the cap stay invisible); and `a1 repo mr approve` / `a1 repo mr edit` writes are banned in Step 7 but outside this tripwire's coverage (the recorded a1 surface exposes no listing an audit could query for them). **Relay those `warning:` lines verbatim in your terminal summary** — the user can dismiss their own comment; a bypass they were never told about, they cannot. The audit is best-effort: when it cannot run (offline, unauthenticated, no report) it says so once on stderr — `note: bypass audit skipped (…)` — so a skipped audit is never mistaken for a clean one. Also remove `.qwen/tmp/qwen-review-parse-args.json` and the session args directory `.qwen/tmp/s-<session>/` (the path from the `<skill-args>` note) — both are written before the target suffix is known, so the pattern above misses them. (Leave the args file in place if you had to fall back to writing it yourself and the run failed: it is the only record of what the review was actually asked to do.) A FILE review's plan falls outside the pattern for the opposite reason: its `--out` is the one name you chose yourself — unique to your run, precisely because no lease guards a file review — so `cleanup` cannot know it and must never glob its family. The family therefore deliberately does NOT start with `qwen-review-` — the prefix every cleanup sweep matches — because a sweep could not tell a live concurrent plan from its own run's residue, and a target literally named `file` or `file-<X>` sweeping `qwen-review-file-*` deleted concurrent file reviews' live plans mid-round. Remove the plan `--out` you wrote, and the `-prompts` directory beside it — **unless the reverse-audit loop stopped without converging**: a `budget-stop.json` marker inside the `-prompts` directory is that stop's record, and the directory is then the only certification history there is — keep the plan AND the directory, and tell the user to remove them once diagnosed, exactly as cleanup's `Kept` line does for the swept families (#9206; this instruction is the file family's only remover, so the retention duty rides with it). On a converged run: `agent-prompt` records every launch prompt under the plan's own name with `.json` replaced by `-prompts`, so the record rides the one family cleanup must never glob, and nothing else removes it.
1063
1111
 
1064
1112
  This step runs **after** Step 7 and Step 8 to ensure all review outputs are saved before cleanup.
1065
1113
 
@@ -13,7 +13,7 @@ Save the review results to a Markdown file for future reference:
13
13
 
14
14
  - Local changes review → `.qwen/reviews/<YYYY-MM-DD>-<HHMMSS>-local.md`
15
15
  - PR review → `.qwen/reviews/<YYYY-MM-DD>-<HHMMSS>-pr-<number>.md`
16
- - File review → `.qwen/reviews/<YYYY-MM-DD>-<HHMMSS>-<filename>.md`
16
+ - File review → `.qwen/reviews/<YYYY-MM-DD>-<HHMMSS>-<target>.md` (`<target>` is the capture's published token — the plan's `target` field, never the raw filename: `qwen review run` pins the saved report against that same token, and a bare filename agrees with it only at the repo root, which is why the drift went unnoticed). When `<target>` itself ends in `.md`, the name ENDS at `<target>` — do not double the extension: `qwen review run`'s report pin for such targets matches `…-<target>` with no second `.md`, so a doubled `…<target>.md` name silently loses the run's `Report:` line
17
17
 
18
18
  Include hours/minutes/seconds in the filename to avoid overwriting on same-day re-reviews.
19
19
 
@@ -74,6 +74,8 @@ The JSON helper is fail-closed because it carries the authoritative review resul
74
74
 
75
75
  If reviewing a PR **at high effort**, update the review cache for incremental review support. Low and medium reviews must NOT write it, and neither must a `--topology minimal` run at any effort — a cache hit would make a later high-effort review of the same SHA report "No new changes since last review", silently converting a cheaper pass into a full-review verdict.
76
76
 
77
+ **A local or file-path review at high effort writes its cache the same way, from the capture's candidate.** **Before promoting, CHECK the candidate is this round's own:** read the file at the plan's `cacheCandidatePath` and compare its `stateId` to the plan's `cacheCandidateStateId` — the path is stable per target and local/file reviews take no lease, so a concurrent same-target run overwrites the file mid-round, and promoting the foreign candidate would anchor the next round's "no changes" to a tree this round never reviewed. A mismatch (or an absent `cacheCandidateStateId` field on a plan that published a path) is treated exactly like a withheld candidate: skip the cache write and say so. `capture-local` wrote this round's content anchor to the plan's `cacheCandidatePath` (`.qwen/tmp/qwen-review-<target>-cache-candidate.json`): the hashed per-file state and HEAD of exactly what was captured, deterministic, not yours to recompute. Read that file, add the ledger fields — `lastReviewDate`, `round`, `findingsCount`, `verdict`, and `findings[]` under the same id-carrying rules as the PR cache below — and write the merged object to the path the capture named in its plan, `cachePath` — **read that field, do not compute the name**, for the reason the incremental bullet in Step 1 gives: `target` is derived inside the command and `safeTarget` is not hand-reproducible. **The fail-closed conditions here are keyed to the PR cache's marker rule below — one definition, read twice.** Do NOT apply that rule's literal CHECK here: it keys on a `sha` inside the posted review's `qwen-review-ledger` marker, and a local round posts nothing and has no marker to read — a literal check would skip this write on every round and the feature would never persist. What this write takes from that rule is its WITHHOLDING CONDITIONS, applied as conditions on this round's own caps. For a local or file-path round the rule is a POSITIVE one, deliberately — and its fail-closed half is deliberately NOT a list: the list that lived here was "completed" three times, and each time a fourth shape walked through it (the last one an Uncoverable chunk and a whiffed lens, which withheld the PR marker's `sha` but never this write). **Write the cache only when every Critical this round raised carries a confirmed disposition: fixed, or confirmed-standing and recorded in `findings[]` under an id — and skip this write under any condition that would withhold the PR marker's `sha` in the rule below, plus the one local-only condition the marker cannot see: a non-empty `skippedFiles` in the capture (skipped content is in no diff and no hash).** At the time of writing those conditions are: `cannotTellCriticals` (a verifier that returned CANNOT-TELL, or a deadline stop or timed-out batch that never returned one), an Uncoverable chunk, the context-unavailable state, `scopeUnproven` (a chunk nobody read, an idle or blind agent), a finding still marked `— [unverified]`, the deterministic gates, and any `unreviewedDimensions` entry other than a depth-only build-and-test one (a whiffed lens withholds the marker's `sha`, so it withholds this write). The examples are the set as written, not the gate: the gate is the marker paragraph's withholding set — if it changes, this write changes with it, because a second hand-copied list here is how they drifted once already. Anything else withholds the candidate and says so. The reason is one reason, and it is why the rule is stated as a rule: the anchor's whole claim is "the next round may skip this", and a claim nobody ruled on is exactly what the next round must not skip — the round after it stops decided over a Critical that was never verified: skipped content is in no diff and no hash, so a candidate promoted over it would anchor the next round's "no changes" past work this round could not read. A run that ended with unreviewed or undecided scope skips this write and says so, because the candidate would anchor the next round's skip past scope nobody reviewed. (A capture that detected a mid-capture tree change — or a tracked path carrying a visibility bit, or an enumeration of them that failed — withholds the candidate itself and says so; then there is nothing to promote.) Low and medium local reviews must NOT write it, for the PR cache's exact reason.
78
+
77
79
  **The cache advances exactly when the marker anchored — read the marker, do not re-derive the net.** `compose-review` already computed whether this round may certify a range: its posted body's ledger marker carries a `sha` on a clean round and withholds it otherwise (unproven coverage, an undecided blocker, any cap other than a depth-only `unreviewed-dimension` — where depth-only means every entry names the build-and-test dimension or is the machine's own relayed stop entry; a whiffed LENS in that field withholds). The cache and the marker must never disagree about what a clean round is, and a hand-copied condition list here is how they drifted once already — the list in this paragraph aged out of sync with the module and told a whiffed-lens round to cache the sha the marker had refused. So the rule is mechanical: **write `lastCommitSha` into the cache only if the composed body's marker carries a `sha`** (check the composed JSON's body for `"sha"` inside the `qwen-review-ledger` comment); when it does not, **skip the cache write entirely and say so in the terminal output**. Caching this SHA would scope the next high-effort run to `lastCommitSha..HEAD` — or, worse, let the same-SHA shortcut report "No new changes since last review" and skip the run outright, Step 6 re-check included: a whiffed Security lens at SHA A followed by an incremental review at SHA B means no run ever reviews A's diff for security, and an existing blocker this run could only mark `cannot tell` would never be re-checked at the same SHA, while the cached verdict reads as full coverage. Leave the previous cache entry in place (or none), so the next high-effort run re-covers the whole range — re-detecting any uncoverable chunk and re-ruling on any undecided blocker, keeping both disclosures alive:
78
80
 
79
81
  1. Create `.qwen/review-cache/` directory if it doesn't exist
@@ -82,7 +84,7 @@ If reviewing a PR **at high effort**, update the review cache for incremental re
82
84
  ```json
83
85
  {
84
86
  "lastCommitSha": "<HEAD SHA captured in Step 1>",
85
- "lastModelId": "<your model id — the YOUR_MODEL_ID value declared at the top of the skill prompt>",
87
+ "lastModelId": "<reviewModelId from the fetch report copied verbatim>",
86
88
  "lastReviewDate": "<ISO timestamp>",
87
89
  "round": <N — 1 on a first review, previous round + 1 after>,
88
90
  "findingsCount": <number>,
@@ -100,6 +102,8 @@ If reviewing a PR **at high effort**, update the review cache for incremental re
100
102
  }
101
103
  ```
102
104
 
103
- The cache is the FALLBACK copy of the ledger — the authoritative one rides the posted review body itself: `compose-review` embeds a machine-readable marker (an HTML comment, invisible on the PR page) carrying this round's findings, round number, and — when the run ended clean — the reviewed head `sha`, and the next round's `pr-context` reads it back wherever it runs. The `sha` is what lets a fresh environment recover BOTH halves of incremental review, the work list and the anchor (Step 1's recovered-anchor check), where the cache could only ever serve the machine that wrote it. It is withheld under the fail-closed conditions that skip this cache write **and under every cap `compose-review` computes itself except `unreviewed-dimension`** — `cannotTellCriticals`, `uncoverableChunks`, the context-unavailable state, `scopeUnproven` (coverage the module could not prove — a chunk nobody read, an idle or blind agent), findings still `— [unverified]`, the deterministic gates because an anchor written past unread scope would let the next round's incremental range skip it forever: a fail-closed round still posts its findings; it just never certifies a range. The wider net is measured, not cautionary: gated on the input fields alone, a round the module itself stamped "could not certify that any of this diff was reviewed" still carried the anchor. **`unreviewedDimensions` is the deliberate exception, and it is measured too**: it is prose about DEPTH "the integration suite CI skipped did not run locally" is true of every round on a repo whose suites do not fit `build-test`'s whole-call budget — so gating on it closed a loop with no exit, where an untestable dimension capped the verdict, the cap withheld the anchor, and the missing anchor made the next round re-review the full diff of a PR that had not changed a line (measured: PR #9113 round 2, 119 minutes, 34M input tokens). A dimension nobody could run says nothing about WHICH LINES were read, and the anchor's only claim is about lines. A run that posts therefore persists its ledger even when this cache write is skipped; a run that does not post has only this cache, which is exactly why the cache remains. The `findings` ledger is what lets the **next** run open with "R1-2 is fixed" instead of a from-scratch list (see Step 6's previous-round section). Write every **newly confirmed high-confidence** finding under a fresh `R<round>-<n>` id, and carry a still-standing previous entry forward **under the id it already has** — the whole payoff is that `R1-2` names the same claim in every round, so a finding that survives is re-reported, never renumbered while a finding ruled `fixed` this round leaves the ledger (the report said so; the cache is for what the next round must check, not history). Low-confidence and terminal-only findings stay out: the ledger holds claims this review stands behind, because next round re-asserts each one by id. Findings the convergence posture deferred stay out the same way — carrying them as ledger work would hand the next round the very re-ruling the posture exists to end. Their durable record on the PR is the POSTED deferral list (up to 20 entries; the body's overflow count names how many more) — and it is **not guaranteed**: the list is the first section the body budget trims, so an overflowing body can carry none of it. The findings artifact carries each deferred finding's full content under its `D<round>-<n>` id but no structured deferred marker yet, and the run report is machine-local — so an entry past the rendered cap, or in a list the budget trimmed, has no cross-round record on the PR at all. Keep the deferral list within its cap by collapsing families first (the bounded/unbounded rule) rather than deferring twenty-plus point findings; when the budget trims it, the terminal summary is where the author's copy comes from.
105
+ `lastModelId` is COPIED, not typed: it is the fetch report's `reviewModelId` the provider-qualified identity (`<model>@<digest>`) the CLI sampled at the round's start, the same string the next round's same-model gate compares against. The skill prompt's own model token (`YOUR_MODEL_ID`, the value the loader declares) is the BARE model id, which is not the same kind of string: a cache written with it fails the gate as `cross-model-anchor` on every later round and never heals — every round rewrites the cache with the same bare id — so cache-path incremental scoping and the `upToDate` stop die for every PR, and a run that does not post loses the anchor entirely. When the fetch report carries NO `reviewModelId` (the runtime published no identity), omit the `lastModelId` field entirelydo not substitute anything; Step 1 then omits `--since-model` and the gate fails closed to a full review, the correct state for an unrecorded identity.
106
+
107
+ The cache is the FALLBACK copy of the ledger — the authoritative one rides the posted review body itself: `compose-review` embeds a machine-readable marker (an HTML comment, invisible on the PR page) carrying this round's findings, round number, and — when the run ended clean — the reviewed head `sha`, and the next round's `pr-context` reads it back wherever it runs. The `sha` is what lets a fresh environment recover BOTH halves of incremental review, the work list and the anchor (Step 1's recovered-anchor check), where the cache could only ever serve the machine that wrote it. It is withheld under the fail-closed conditions that skip this cache write **and under every cap `compose-review` computes itself except `unreviewed-dimension`** — `cannotTellCriticals`, `uncoverableChunks`, the context-unavailable state, `scopeUnproven` (coverage the module could not prove — a chunk nobody read, an idle or blind agent), findings still `— [unverified]`, the deterministic gates — because an anchor written past unread scope would let the next round's incremental range skip it forever: a fail-closed round still posts its findings; it just never certifies a range. The wider net is measured, not cautionary: gated on the input fields alone, a round the module itself stamped "could not certify that any of this diff was reviewed" still carried the anchor. **`unreviewedDimensions` is the deliberate exception, and it is measured too**: it is prose about DEPTH — "the integration suite CI skipped did not run locally" is true of every round on a repo whose suites do not fit `build-test`'s whole-call budget — so gating on it closed a loop with no exit, where an untestable dimension capped the verdict, the cap withheld the anchor, and the missing anchor made the next round re-review the full diff of a PR that had not changed a line (measured: PR #9113 round 2, 119 minutes, 34M input tokens). A dimension nobody could run says nothing about WHICH LINES were read, and the anchor's only claim is about lines. A run that posts therefore persists its ledger even when this cache write is skipped; a run that does not post has only this cache, which is exactly why the cache remains. The `findings` ledger is what lets the **next** run open with "R1-2 is fixed" instead of a from-scratch list (see Step 6's previous-round section). Write every **newly confirmed high-confidence** finding under a fresh `R<round>-<n>` id, and carry a still-standing previous entry forward **under the id it already has** — the whole payoff is that `R1-2` names the same claim in every round, so a finding that survives is re-reported, never renumbered — while a finding ruled `fixed` this round leaves the ledger (the report said so; the cache is for what the next round must check, not history). Low-confidence and terminal-only findings stay out: the ledger holds claims this review stands behind, because next round re-asserts each one by id. Findings the convergence posture deferred stay out the same way — carrying them as ledger work would hand the next round the very re-ruling the posture exists to end. Their durable record on the PR is the POSTED deferral list (up to 20 entries; the body's overflow count names how many more) — and it is **not guaranteed**: the list is the first section the body budget trims, so an overflowing body can carry none of it. The list opens with an invisible `<!-- qwen-review-deferred -->` marker so later tooling can locate the block across rounds, and the findings artifact carries each deferred finding's full content under its `D<round>-<n>` id — the CI review job uploads it and the report as run artifacts (what the overflow count's "see the run report" pointer resolves to — kept for the artifact's 90-day retention window, not forever), and a local run's saved artifact under `.qwen/reviews/` holds the same — so an entry past the rendered cap, or in a list the budget trimmed, keeps a recoverable record even though the PR page never shows it. Keep the deferral list within its cap by collapsing families first (the bounded/unbounded rule) rather than deferring twenty-plus point findings; when the budget trims it, the terminal summary is where the author's copy comes from.
104
108
 
105
109
  3. Ensure `.qwen/reviews/` and `.qwen/review-cache/` are ignored by `.gitignore` — a broader rule like `.qwen/*` also satisfies this. Only warn the user if those paths are not ignored at all.
@@ -40,6 +40,8 @@ Also skip this step (independently of the gate above) if the review target is no
40
40
 
41
41
  **And a comment whose fix adds a guard carries the test that must pin it.** When the finding's `fixWitness` is anything other than `N/A`, the posted body closes with it, in one sentence of ordinary prose: name the test that must fail if the fix is removed, and ask for the mutation that proves it (remove the guard, run that test, confirm it reds). One sentence, after the suggestion block — not a heading, not a checklist. This is the reviewer-side half of a measured loop: roughly a third of every post-first-round finding on six multi-round pull requests was introduced by the fix immediately before it, overwhelmingly as a guard or branch with no test of its own, and the deterministic gate re-runs only the tests that exist — so an unwitnessed guard passes every gate and returns as next round's finding. A fixer who is told the acceptance criterion closes it in THIS round; one who is not, does not (measured; DESIGN.md — The fix round that wrote the next round's findings (#9578)). The line reaches every fixer — a contributor, a maintainer, any bot — which is the point: the review cannot assume the fix comes from something it can configure. A finding whose `fixWitness` is `N/A` adds nothing (do not write "no test needed" — silence says it), and this sentence never changes what the comment reports or at what severity.
42
42
 
43
+ **And a comment whose fix rests on an existing fact carries that fact.** When the finding has a `fixConstraint`, the posted body carries it in one sentence of ordinary prose immediately before the fix-witness sentence, which still closes the body — and when the finding's `fixWitness` is `N/A`, where no fix-witness sentence stands, the constraint sentence takes its place after the suggestion block: state the fact the fix must not violate and its source — the quoted constant, the `file:line` — copied from the artifact, not re-derived. `fixWitness` pins the fix's claim; this pins its premises, the class that passed a witnessed test and was still wrong twice on one merged fix — a hand-picked bound below a configurable limit, and a newly shared registry that broke a `callId` uniqueness relied on elsewhere (measured; DESIGN.md — The fix whose premises were wrong (#10153)). The evidence bar is the `witness` bar, and it holds at posting as it did at finding: a constraint that names no constant and no `file:line` is not posted — a wrong `fixWitness` costs one unwritten test; a wrong constraint is misdirection the fixer will follow. A finding with no `fixConstraint` adds nothing — no `N/A`, no "no constraints observed"; the sentence is absent and the comment is that much shorter — and, like the fix-witness sentence, this one never changes what the comment reports or at what severity. And the rule follows the finding through the two moves the orchestrator performs: a Critical carrying either fix-side sentence — the fix-witness or the constraint sentence — that moves to `bodyCriticals` — unmappable, or relocated by the 422 recovery — appends the same sentence to that entry, copied from the artifact — the constraint before the witness when the finding carries both — because that entry is the finding's sole published copy, and a witness or a premise lost there is one no fixer ever sees. A third move exists and carries neither sentence: on an Aone target the platform performs no server-side anchor validation, so `submit` validates every anchor itself and relocates an unanchorable Critical into the body as a one-line entry rebuilt from the claim line alone — a channel structurally too narrow for either sentence. Closing that channel is the structured body-Critical projection, larger than this rule. Two more moves exist and carry neither sentence: a finding carried into `deferredSuggestions` renders as the typed one-line entry — a `DeferredEntry` holds no fix-side field, and the line is capped at 240 characters — and a Suggestion dropped as a duplicate posts a name-and-location account only, never the finding's own text — both structurally too narrow for either sentence; until a deferral entry grows the fields, the loss is a named acceptance, not a silent one.
44
+
43
45
  **Resolve every anchor before you submit — do not post the line numbers the agents reported.** GitHub rejects the whole review with a 422 if any comment's `(path, line)` falls outside every hunk of that file, and it does so all-or-nothing: one miscounted anchor takes every Critical in the review down with it. The line is therefore computed from the diff, not carried over from an agent. The resolver input already exists — Step 6's `findings --to-anchors` wrote it from the artifact, one entry per anchored location of every high-confidence Critical and Suggestion (do NOT hand-project it from the artifact's `locations[]`: the resolver wants `path` where the artifact stores `file`, and a hand projection once produced all-null anchors). Run the resolver:
44
46
 
45
47
  ```bash
@@ -0,0 +1,110 @@
1
+ // Force strict mode and setup for ESM
2
+ "use strict";
3
+ import {
4
+ MINIMUM_MAX_HEIGHT,
5
+ MaxSizedBox,
6
+ setMaxSizedBoxDebugging
7
+ } from "./chunk-NU7RKEZR.js";
8
+ import "./chunk-WAOIHTJS.js";
9
+ import "./chunk-SV5PQVQE.js";
10
+ import "./chunk-5Q372OFJ.js";
11
+ import "./chunk-5QWWOFGG.js";
12
+ import "./chunk-QFCDF5PL.js";
13
+ import "./chunk-E7REAOTN.js";
14
+ import "./chunk-2MIN6GRR.js";
15
+ import "./chunk-QHTIBUWB.js";
16
+ import "./chunk-RKUWKYED.js";
17
+ import "./chunk-W6DUQ5GA.js";
18
+ import "./chunk-CRKKHXTI.js";
19
+ import "./chunk-CQH5KTKC.js";
20
+ import "./chunk-LJZSMWOH.js";
21
+ import "./chunk-V2QDDC5J.js";
22
+ import "./chunk-OI4NY7RA.js";
23
+ import "./chunk-K5KXUXWC.js";
24
+ import "./chunk-62GQFYID.js";
25
+ import "./chunk-C3KURPP7.js";
26
+ import "./chunk-XQFT3QUF.js";
27
+ import "./chunk-VTHREBQM.js";
28
+ import "./chunk-ZPJWUGCS.js";
29
+ import "./chunk-3JXM2CVW.js";
30
+ import "./chunk-TW3E6YNO.js";
31
+ import "./chunk-EWIQ3NAE.js";
32
+ import "./chunk-GOFAQQZA.js";
33
+ import "./chunk-5M6IDOMF.js";
34
+ import "./chunk-TWPJO254.js";
35
+ import "./chunk-CQ35AJ4Z.js";
36
+ import "./chunk-EKSCLBBF.js";
37
+ import "./chunk-LUEHOUHM.js";
38
+ import "./chunk-WXEA74YB.js";
39
+ import "./chunk-42IDLQWS.js";
40
+ import "./chunk-6PVPNMXU.js";
41
+ import "./chunk-JB4JIVTJ.js";
42
+ import "./chunk-DJBQCO2E.js";
43
+ import "./chunk-IRH27ZC2.js";
44
+ import "./chunk-QHWCP53L.js";
45
+ import "./chunk-D5LUXLUH.js";
46
+ import "./chunk-O6GEWCJA.js";
47
+ import "./chunk-T26EAKDL.js";
48
+ import "./chunk-2SXSLSQE.js";
49
+ import "./chunk-CPBF7KYF.js";
50
+ import "./chunk-6W2IK5KC.js";
51
+ import "./chunk-TTX2JUE6.js";
52
+ import "./chunk-4K7KNVWH.js";
53
+ import "./chunk-SMPR7SPO.js";
54
+ import "./chunk-JWALNCLT.js";
55
+ import "./chunk-NIFYWDYN.js";
56
+ import "./chunk-C2X7KY45.js";
57
+ import "./chunk-XLLQJRAH.js";
58
+ import "./chunk-7J6OTNGO.js";
59
+ import "./chunk-V5J4J5TP.js";
60
+ import "./chunk-MLXTMF7H.js";
61
+ import "./chunk-NAVJD2PQ.js";
62
+ import "./chunk-B466ZSHZ.js";
63
+ import "./chunk-4QHPPXK2.js";
64
+ import "./chunk-QHMLYMMS.js";
65
+ import "./chunk-3WK3QDNV.js";
66
+ import "./chunk-VNOVK4I7.js";
67
+ import "./chunk-PQEISIKS.js";
68
+ import "./chunk-EB6QRGHM.js";
69
+ import "./chunk-CMHFCLBU.js";
70
+ import "./chunk-S6LOFUVP.js";
71
+ import "./chunk-CFKIH3D3.js";
72
+ import "./chunk-2LD5U7Q3.js";
73
+ import "./chunk-DJ2GSRLV.js";
74
+ import "./chunk-DYXFD5RO.js";
75
+ import "./chunk-Y3QL45LS.js";
76
+ import "./chunk-SZLBBZH4.js";
77
+ import "./chunk-XBVNNDPK.js";
78
+ import "./chunk-VGC4I5JJ.js";
79
+ import "./chunk-BKWNKLZB.js";
80
+ import "./chunk-6PJOTWAN.js";
81
+ import "./chunk-BWORX6FA.js";
82
+ import "./chunk-WZAD4ZNJ.js";
83
+ import "./chunk-FPGTNKCP.js";
84
+ import "./chunk-6DIGWMGT.js";
85
+ import "./chunk-3K6TUZHS.js";
86
+ import "./chunk-XZA32HII.js";
87
+ import "./chunk-23RFD54N.js";
88
+ import "./chunk-PPKZ7JOE.js";
89
+ import "./chunk-HHJLM3WQ.js";
90
+ import "./chunk-B5PHIJGI.js";
91
+ import "./chunk-74TONY4F.js";
92
+ import "./chunk-XF63PKEN.js";
93
+ import "./chunk-CAJTKR6W.js";
94
+ import "./chunk-ZU4UDIWX.js";
95
+ import "./chunk-AQ37AY7B.js";
96
+ import "./chunk-PDQGMSZK.js";
97
+ import "./chunk-UTLCH2FK.js";
98
+ import "./chunk-UHQFIS7N.js";
99
+ import "./chunk-75DOP5OR.js";
100
+ import "./chunk-DMTGGOSA.js";
101
+ import "./chunk-YQ3U5MUC.js";
102
+ import "./chunk-AMDSOFFV.js";
103
+ import "./chunk-TBWQLLFO.js";
104
+ import "./chunk-5O2XNYP6.js";
105
+ import "./chunk-J2S4EL5Y.js";
106
+ export {
107
+ MINIMUM_MAX_HEIGHT,
108
+ MaxSizedBox,
109
+ setMaxSizedBoxDebugging
110
+ };