@qwen-code/qwen-code 0.21.13 → 0.21.14-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundled/qc-helper/docs/configuration/settings.md +13 -7
- package/bundled/qc-helper/docs/features/code-review.md +5 -1
- package/bundled/qc-helper/docs/features/commands.md +120 -8
- package/bundled/qc-helper/docs/features/sub-agents.md +2 -2
- package/bundled/qc-helper/docs/qwen-serve-deploy-local.md +1 -1
- package/bundled/qc-helper/docs/qwen-serve.md +20 -3
- package/bundled/review/SKILL.md +50 -30
- package/chunks/{MaxSizedBox-UQALOVVT.js → MaxSizedBox-3PZITE2Y.js} +24 -23
- package/chunks/{StandaloneSessionPicker-KEGXNUG6.js → StandaloneSessionPicker-2C34PNGO.js} +41 -40
- package/chunks/{acp-startup-profiler-NMDXYGDN.js → acp-startup-profiler-HUYGO57H.js} +2 -2
- package/chunks/{acpAgent-XPES32S7.js → acpAgent-3J4NHAYT.js} +626 -141
- package/chunks/{agent-DPTOZFEM.js → agent-VFX4RPP5.js} +22 -21
- package/chunks/{agent-headless-GJCQMURW.js → agent-headless-6WCF4J6N.js} +22 -21
- package/chunks/{anthropicContentGenerator-G7BVXTKN.js → anthropicContentGenerator-M7XRJMSK.js} +3 -3
- package/chunks/{bridge-YNQBAA4Z.js → bridge-XR5ZSR7U.js} +26 -25
- package/chunks/{ca-4CQ6MYNQ.js → ca-SLZJD6QH.js} +8 -0
- package/chunks/{channel-management-service-GWY2GAIC.js → channel-management-service-X6LCQUAD.js} +1 -1
- package/chunks/{channel-settings-store-WNWYFHG7.js → channel-settings-store-Z7BUEUJE.js} +30 -29
- package/chunks/{channel-worker-group-7LXCMZ77.js → channel-worker-group-TJ35JDPE.js} +2 -2
- package/chunks/{channel-worker-manager-RYEME4ZZ.js → channel-worker-manager-G5LKWGOG.js} +2 -2
- package/chunks/{chunk-MLPXSYXR.js → chunk-25GHLMQQ.js} +25 -16
- package/chunks/chunk-2C6HJMG3.js +757 -0
- package/chunks/{chunk-TUGSVUJB.js → chunk-2JXDFSVE.js} +5 -5
- package/chunks/{chunk-AS5IN4GK.js → chunk-2OA3JMRD.js} +3 -3
- package/chunks/chunk-3PWCMXIR.js +9169 -0
- package/chunks/{chunk-U3VNBPUO.js → chunk-4KQ2TI2K.js} +2 -2
- package/chunks/{chunk-NEFOOD54.js → chunk-4V4DFJBS.js} +2 -2
- package/chunks/{chunk-OMLJNPBE.js → chunk-4ZMS3JCR.js} +1 -0
- package/chunks/{chunk-PP4FUZNV.js → chunk-55PIFSP2.js} +2 -2
- package/chunks/{chunk-3U3VAL47.js → chunk-57KOJ2KZ.js} +1 -1
- package/chunks/{chunk-A4QRWUDE.js → chunk-5IYS4WWR.js} +1 -1
- package/chunks/{chunk-TS6625XH.js → chunk-5MK45KZ3.js} +9 -5
- package/chunks/{chunk-CU6GJD55.js → chunk-6DFPR7CS.js} +1 -1
- package/chunks/{chunk-V57QC6WW.js → chunk-6LAPGPVA.js} +5 -3
- package/chunks/{chunk-OOQUVWIG.js → chunk-6PDOHRN6.js} +4 -4
- package/chunks/{chunk-UZPM4XHR.js → chunk-6VDPDHIP.js} +29 -8
- package/chunks/{chunk-O4D7SPJN.js → chunk-73G3Z7QS.js} +3 -3
- package/chunks/{chunk-QIH2HAEN.js → chunk-7OCSX7FC.js} +1 -1
- package/chunks/{chunk-OVZPVYZB.js → chunk-AKBOLMUF.js} +95 -17
- package/chunks/{chunk-DGK6P3BQ.js → chunk-AQBHJ2IG.js} +10 -10
- package/chunks/{chunk-JNDY2MDR.js → chunk-BOJNAPUE.js} +1 -1
- package/chunks/{chunk-NHDR76JX.js → chunk-BQWNIPTE.js} +1 -1
- package/chunks/{chunk-AA2BEKV3.js → chunk-CH5OFRV7.js} +1 -1
- package/chunks/{chunk-MS7GNABG.js → chunk-CXJHQVEK.js} +8 -0
- package/chunks/{chunk-K5IDV3PC.js → chunk-D34RDOLP.js} +12 -2
- package/chunks/{chunk-OXCMMLED.js → chunk-DGJ7O55J.js} +1 -1
- package/chunks/{chunk-BKPPWD33.js → chunk-DRKYT2TR.js} +1 -1
- package/chunks/{chunk-P7MPQI75.js → chunk-DVR7XKO5.js} +1 -1
- package/chunks/{chunk-MRMJCLIY.js → chunk-DZCV3ZYU.js} +2 -2
- package/chunks/{chunk-EZ5RP345.js → chunk-E5RS7LF4.js} +40 -9
- package/chunks/{chunk-4NFY2S7N.js → chunk-E7REAOTN.js} +15 -0
- package/chunks/chunk-EOCQCNV4.js +3887 -0
- package/chunks/{chunk-BVJS5BSP.js → chunk-FCTLOVYW.js} +1 -1
- package/chunks/{chunk-DGZEMW5R.js → chunk-FS5NGKWC.js} +1 -1
- package/chunks/{chunk-XHRY7BXD.js → chunk-G6FFFICU.js} +3 -3
- package/chunks/{chunk-NM5V5OVW.js → chunk-GV3OIADZ.js} +2 -2
- package/chunks/{chunk-QV5YN5YD.js → chunk-HGPMOXLZ.js} +5 -5
- package/chunks/{chunk-GMIEIY3Y.js → chunk-I3N2Z25H.js} +1 -1
- package/chunks/{chunk-RIAABG2K.js → chunk-I7NTHW4C.js} +1 -1
- package/chunks/{chunk-HUTNYWBG.js → chunk-IOTPI425.js} +18 -10
- package/chunks/{chunk-ZYYTZUI2.js → chunk-ISRHAMNL.js} +3 -3
- package/chunks/{chunk-BHILXOC3.js → chunk-J63PKMYA.js} +1 -1
- package/chunks/{chunk-KD5BOPLP.js → chunk-J6YS7Z25.js} +98 -4
- package/chunks/{chunk-GO7STP6N.js → chunk-K3CECR5C.js} +4 -4
- package/chunks/{chunk-RVOJ2FBP.js → chunk-KC4WMLZO.js} +1486 -10574
- package/chunks/{chunk-IGGGG5CX.js → chunk-KSGWAYNE.js} +4580 -1652
- package/chunks/{chunk-X26JYGTD.js → chunk-L6CCLB3G.js} +1 -1
- package/chunks/{chunk-M6NDLY2T.js → chunk-LD5VIJ7S.js} +1 -1
- package/chunks/{chunk-ZYBBISBY.js → chunk-LLR424CQ.js} +15 -12
- package/chunks/{chunk-JUEPY766.js → chunk-LRDB2OLB.js} +2 -2
- package/chunks/{chunk-PQ36L4F5.js → chunk-MCG53LWC.js} +1 -1
- package/chunks/{chunk-OD2WU5QY.js → chunk-NRPXRNN4.js} +1 -1
- package/chunks/{chunk-76J3IJ3M.js → chunk-NV6LW6FT.js} +3 -3
- package/chunks/{chunk-CKSJECTU.js → chunk-NVRMCWTP.js} +8 -4
- package/chunks/{chunk-Y3YCSWG5.js → chunk-OBJYN4MX.js} +1 -1
- package/chunks/{chunk-WFSTRPRO.js → chunk-OJOP26ET.js} +1 -1
- package/chunks/{chunk-A267JX7H.js → chunk-OLZSWUZR.js} +6 -6
- package/chunks/{chunk-TIGA2IVL.js → chunk-OO4FKHLO.js} +2 -2
- package/chunks/{chunk-OY5RNSV4.js → chunk-OSAFWBNR.js} +3 -3
- package/chunks/{chunk-BZ263IKX.js → chunk-OVY67CYC.js} +1 -1
- package/chunks/{chunk-YEYY7TEP.js → chunk-P425IVUK.js} +5 -5
- package/chunks/{chunk-AFSG32D5.js → chunk-PMS7O6SA.js} +1 -1
- package/chunks/{chunk-6ORZ2EDC.js → chunk-PPTM4O4R.js} +1 -1
- package/chunks/{chunk-JOOFMXGF.js → chunk-QECSDJET.js} +5 -5
- package/chunks/{chunk-CZBYX5MY.js → chunk-QEJZUXSK.js} +1 -1
- package/chunks/{chunk-4PKCUT7F.js → chunk-QIHHJKXT.js} +6 -6
- package/chunks/{chunk-QRCNA3SR.js → chunk-RAQBBGS6.js} +49 -69
- package/chunks/{chunk-M732YJPW.js → chunk-RXHBTKMG.js} +2 -2
- package/chunks/{chunk-XIT3GURH.js → chunk-S6CXQKDD.js} +3 -3
- package/chunks/{chunk-GGDBMYGZ.js → chunk-SA7GBPEA.js} +3 -3
- package/chunks/{chunk-7UXUJ5T3.js → chunk-SBRIBTYN.js} +1 -1
- package/chunks/{chunk-MC4TLKEI.js → chunk-SGLPDKRT.js} +2 -2
- package/chunks/{chunk-WSH6GRKG.js → chunk-SLILKYV2.js} +2 -2
- package/chunks/{chunk-DXIBNLBZ.js → chunk-SNQZZ765.js} +1 -1
- package/chunks/{chunk-5EYKMUUY.js → chunk-SR2LYSMT.js} +1 -1
- package/chunks/{chunk-AW4TRXOC.js → chunk-TNPWODIZ.js} +1 -1
- package/chunks/{chunk-QF2NHSPB.js → chunk-U6RZRKNM.js} +233 -184
- package/chunks/{chunk-QNQAPSN2.js → chunk-UC67T4SF.js} +3 -1
- package/chunks/{chunk-O565M6A4.js → chunk-UYDGFWIJ.js} +199 -14
- package/chunks/{chunk-LJQAUZMR.js → chunk-VGAAAMRD.js} +925 -92
- package/chunks/{chunk-UKMR3CKV.js → chunk-VHPMDDPT.js} +3 -3
- package/chunks/{chunk-S5DWMTHO.js → chunk-WP4WIUOJ.js} +2 -0
- package/chunks/{chunk-25EYDK42.js → chunk-WPVUOVYX.js} +1 -1
- package/chunks/{chunk-AXN7UPYQ.js → chunk-WSSH2BNO.js} +1 -1
- package/chunks/{chunk-6YKYPVDV.js → chunk-WVIABDXV.js} +3 -3
- package/chunks/chunk-WZAD4ZNJ.js +59 -0
- package/chunks/{chunk-N5YPT6PH.js → chunk-X3YA3YGG.js} +7 -4
- package/chunks/{chunk-4S5337WV.js → chunk-XC377TLN.js} +4312 -2704
- package/chunks/{chunk-NNHNLJ2X.js → chunk-XO24LIZC.js} +240 -42
- package/chunks/{chunk-EL5SXY3T.js → chunk-XT4RKHVE.js} +2 -2
- package/chunks/{chunk-FDGGZIYW.js → chunk-YECDICIO.js} +2 -2
- package/chunks/{chunk-E23IPNGS.js → chunk-YFPZ3NNC.js} +12 -3
- package/chunks/{chunk-46UV252V.js → chunk-YHC2EYYG.js} +35 -38
- package/chunks/{chunk-V26NVGAU.js → chunk-YIWH3MKK.js} +8 -3
- package/chunks/{chunk-IWPYVAO2.js → chunk-YXC7JYUI.js} +2 -2
- package/chunks/{chunk-A4MFR4C5.js → chunk-ZIGC3MUS.js} +2 -4
- package/chunks/{chunk-ZZO74WKW.js → chunk-ZXEAEZFN.js} +24 -18
- package/chunks/{computer-use-77C5FHMW.js → computer-use-ABECHPS5.js} +22 -21
- package/chunks/{config-utils-IVHRSXWI.js → config-utils-NJQA5YLJ.js} +2 -2
- package/chunks/{contextCommand-PJ77GBWL.js → contextCommand-4CD5HZQQ.js} +24 -23
- package/chunks/{core-runtime-7VMIKUTG.js → core-runtime-2MOVQ4VX.js} +22 -21
- package/chunks/{create-sub-session-2L5MQRN4.js → create-sub-session-L6NNHG5N.js} +26 -24
- package/chunks/{cron-create-H7UEPDSN.js → cron-create-NFRZEOQX.js} +2 -2
- package/chunks/{cron-delete-6SZK7YFM.js → cron-delete-GFVSVO7C.js} +2 -2
- package/chunks/{cron-list-H25JIHEZ.js → cron-list-NRW34CLU.js} +2 -2
- package/chunks/{daemon-2KTG7ZBN.js → daemon-XH76IXOC.js} +356 -21
- package/chunks/{daemon-git-worktree-guard-DXYIMWFT.js → daemon-git-worktree-guard-BLXFEG4Q.js} +24 -23
- package/chunks/{daemon-status-provider-ZDOYJCU6.js → daemon-status-provider-XGD3TPTO.js} +31 -30
- package/chunks/{daemon-trust-policy-E67OYUK2.js → daemon-trust-policy-THEGCWPO.js} +29 -28
- package/chunks/{daemon-trust-policy-monitor-RYYGCQ3J.js → daemon-trust-policy-monitor-L7L4ACWQ.js} +29 -28
- package/chunks/{de-UBEBKG6V.js → de-AH4LGCF5.js} +8 -0
- package/chunks/{deferred-core-runtime-3SGXFXEQ.js → deferred-core-runtime-YQLHDUGE.js} +22 -21
- package/chunks/{discovery-ORA5J7YP.js → discovery-X2NV535U.js} +1 -1
- package/chunks/{dist-WJ4BHIEW.js → dist-ESS34DHC.js} +72 -0
- package/chunks/{earlyInputCapture-KJMLFVQV.js → earlyInputCapture-PCTXNW7N.js} +22 -21
- package/chunks/{edit-I4LQFAEC.js → edit-QX2EGWYW.js} +25 -23
- package/chunks/{en-X6YV7GME.js → en-MD3GP5YL.js} +8 -0
- package/chunks/{enter-worktree-YNPOJSRH.js → enter-worktree-EM3ZZ2SB.js} +22 -21
- package/chunks/{enterPlanMode-XCPK62ER.js → enterPlanMode-ESCMVAKB.js} +22 -21
- package/chunks/{environment-S7CTIHMX.js → environment-TOJI56YV.js} +25 -24
- package/chunks/{errors-7HB2AIDS.js → errors-DZ5FWWEM.js} +24 -23
- package/chunks/{exit-worktree-AIIY7SR6.js → exit-worktree-KUIJ36XA.js} +22 -21
- package/chunks/{exitPlanMode-R4JY7V2O.js → exitPlanMode-NFVY54FV.js} +22 -21
- package/chunks/{fast-path-NDJV3ZFJ.js → fast-path-JLQXJSH6.js} +3 -3
- package/chunks/{fast-path-settings-LNOFM2YP.js → fast-path-settings-WHX3J3ZI.js} +2 -2
- package/chunks/{fr-VENTC2BS.js → fr-ZEML2JWS.js} +8 -0
- package/chunks/{gemini-RSUFP5OE.js → gemini-I7OT4FWL.js} +62 -61
- package/chunks/{geminiContentGenerator-SQ5XBPZ7.js → geminiContentGenerator-GRIS77NJ.js} +3 -3
- package/chunks/{glob-J4RUJAHX.js → glob-7TBUUV6R.js} +22 -21
- package/chunks/{goal-tools-VHTNVOKK.js → goal-tools-K5OHOJ6B.js} +1 -1
- package/chunks/{grep-VJWPLFOT.js → grep-743SYWCZ.js} +22 -21
- package/chunks/{handleAutoUpdate-POCX2MNB.js → handleAutoUpdate-GAOYUYOD.js} +26 -25
- package/chunks/{i18n-RM5YOICI.js → i18n-2ITURYX6.js} +23 -22
- package/chunks/{image-gen-EXECB5XM.js → image-gen-4OIWDZD6.js} +2 -2
- package/chunks/{initializer-Z2AFGBNR.js → initializer-OC7LWA6X.js} +29 -28
- package/chunks/{installationInfo-4I5FXNMH.js → installationInfo-CO47QX3Q.js} +23 -22
- package/chunks/{ja-QHT4EQTO.js → ja-UM5B2HMD.js} +8 -0
- package/chunks/{list-ZMBFIGZA.js → list-ATR2SFWT.js} +32 -31
- package/chunks/{loadedSettingsAdapter-T6TV4YJ7.js → loadedSettingsAdapter-NK4B7AGK.js} +29 -28
- package/chunks/{loggingContentGenerator-WF4ZOJEZ.js → loggingContentGenerator-3WXBU5TQ.js} +12 -10
- package/chunks/{loop-wakeup-WQS6JYAG.js → loop-wakeup-HWVUU5CF.js} +3 -3
- package/chunks/{managed-npm-update-E7IW6N7V.js → managed-npm-update-4SSBWI2D.js} +23 -22
- package/chunks/{mcp-6UNYX4HG.js → mcp-C62AE2QI.js} +29 -28
- package/chunks/{monitor-EE2UYMGE.js → monitor-FEYEGASA.js} +22 -21
- package/chunks/{nonInteractiveCli-WAY237O4.js → nonInteractiveCli-AJEV5ZWO.js} +59 -57
- package/chunks/{notebook-edit-NTDY4QDU.js → notebook-edit-SLLTEY4O.js} +26 -24
- package/chunks/{openaiContentGenerator-YNFVJMRJ.js → openaiContentGenerator-WLUGIAPX.js} +12 -11
- package/chunks/{pidfile-4XSFLYBZ.js → pidfile-TV2VWUMB.js} +22 -21
- package/chunks/{processUtils-WDA2MJFD.js → processUtils-3IN2ZR55.js} +2 -2
- package/chunks/{pt-VL3I5RNY.js → pt-5BMJ3XYH.js} +8 -0
- package/chunks/{qwenContentGenerator-434M7QKY.js → qwenContentGenerator-GSGRFPJE.js} +25 -24
- package/chunks/{qwenOAuth2-GMYZOO6A.js → qwenOAuth2-JLSTIS4Z.js} +3 -3
- package/chunks/{read-file-TAFBKMNI.js → read-file-6PZZRGZZ.js} +9 -7
- package/chunks/{record-artifact-SEYFJEJF.js → record-artifact-GLU3UWBV.js} +3 -1
- package/chunks/{resumeHistoryUtils-EVG3WYK2.js → resumeHistoryUtils-5GEWKOOO.js} +25 -24
- package/chunks/{ripGrep-WOEK2PXS.js → ripGrep-FX7GBGFN.js} +22 -21
- package/chunks/{ru-4RCKL4PY.js → ru-6GL66ORV.js} +8 -0
- package/chunks/{run-qwen-serve-3QSGRXAN.js → run-qwen-serve-7RHU3NSV.js} +49 -40
- package/chunks/{runtime-5MLLZ7DC.js → runtime-EPOXAMLP.js} +32 -31
- package/chunks/{scheduler-RPBJ63CH.js → scheduler-F4Y7ENVV.js} +24 -23
- package/chunks/{sdk-exporters-http-XUVP63H6.js → sdk-exporters-http-NC746HLT.js} +2 -2
- package/chunks/{sdk-impl-YXUEN2Z2.js → sdk-impl-YVL7BP6Y.js} +2 -2
- package/chunks/{send-message-VZENRWRH.js → send-message-TNBG5EVA.js} +1 -1
- package/chunks/{serve-HS3XLPXU.js → serve-LQV4TN5A.js} +29 -30
- package/chunks/{server-TC4A7AAZ.js → server-IZCKLIYQ.js} +1267 -165
- package/chunks/{session-UBOFXVMN.js → session-52ZGTDVB.js} +60 -58
- package/chunks/{settings-D5644OM5.js → settings-WCVB34HW.js} +28 -27
- package/chunks/{shell-3PZULQQV.js → shell-33BIUBLH.js} +22 -21
- package/chunks/{skill-PEVVXQ3L.js → skill-IYVQ2QNK.js} +11 -9
- package/chunks/{skill-settings-J65JKKES.js → skill-settings-YG4ZS4OZ.js} +28 -27
- package/chunks/{spawnChannel-IYGXRHSQ.js → spawnChannel-BLA4YT4W.js} +24 -23
- package/chunks/{standalone-update-IFM53PE7.js → standalone-update-ALRIPWSM.js} +24 -23
- package/chunks/{startInteractiveUI-3AVH7L6H.js → startInteractiveUI-PISBXWKA.js} +262 -191
- package/chunks/{task-create-FCBDX7UH.js → task-create-EILHJJB7.js} +4 -4
- package/chunks/{task-list-YIYIUFUA.js → task-list-WGJR6YL5.js} +18 -4
- package/chunks/{task-update-MYPFVOVM.js → task-update-XZAHVXJ5.js} +79 -8
- package/chunks/{team-create-ZAIA7KAV.js → team-create-CIQDCC6A.js} +23 -23
- package/chunks/{team-delete-6UMU4XLS.js → team-delete-UEP4QOOT.js} +3 -3
- package/chunks/{team-plan-approval-UNIUS7OD.js → team-plan-approval-664XCRB4.js} +22 -21
- package/chunks/{terminal-image-renderer-G343P2RV.js → terminal-image-renderer-TEBO6ONY.js} +22 -21
- package/chunks/{theme-manager-Z3RIT7DB.js → theme-manager-3CIFH2JV.js} +22 -21
- package/chunks/{todoWrite-U2R4NVF4.js → todoWrite-ATS62CAP.js} +1 -1
- package/chunks/{tool-search-5R2OE4C7.js → tool-search-IJX5JHJB.js} +9 -7
- package/chunks/{total-session-admission-4KBHMQQ2.js → total-session-admission-XBK2NVRV.js} +26 -25
- package/chunks/{trustedFolders-LK6T4XC7.js → trustedFolders-OOFNLRWK.js} +23 -22
- package/chunks/{update-relaunch-UBKROIC6.js → update-relaunch-EIG7EYAQ.js} +5 -5
- package/chunks/{updateCheck-W4Y2GFSD.js → updateCheck-KYDCBLSF.js} +25 -24
- package/chunks/{useAutoAcceptIndicator-C7BEICBF.js → useAutoAcceptIndicator-RFX3KNBN.js} +31 -30
- package/chunks/{validateNonInterActiveAuth-LTEI2TB6.js → validateNonInterActiveAuth-MZXSAITU.js} +56 -54
- package/chunks/{version-G5A5DGVS.js → version-PJC6ZNXV.js} +1 -1
- package/chunks/{web-fetch-RTEQTR5Z.js → web-fetch-XXICVGKQ.js} +11 -8
- package/chunks/{web-search-5OXHEZPV.js → web-search-LHPM6QWC.js} +2 -2
- package/chunks/{workflow-ISQJHPG2.js → workflow-4CUO2BV7.js} +139 -37
- package/chunks/{workspace-providers-status-2ZPZPUPZ.js → workspace-providers-status-G4MUGH3X.js} +32 -31
- package/chunks/{workspace-registration-store-CYNY6IP6.js → workspace-registration-store-QMDMFWBE.js} +1 -1
- package/chunks/{workspace-registry-34GTUUHB.js → workspace-registry-FKBB4MZK.js} +26 -25
- package/chunks/{workspace-service-XCDQLE2Y.js → workspace-service-TIDTTA65.js} +33 -32
- package/chunks/{workspace-skills-status-RS4O7AIJ.js → workspace-skills-status-LBC2TZKM.js} +30 -29
- package/chunks/{workspace-trust-reconciler-AYGXC7A3.js → workspace-trust-reconciler-4KJ567XP.js} +33 -32
- package/chunks/{write-file-EWXKVVXF.js → write-file-R6LAEER5.js} +22 -21
- package/chunks/{zh-TW-SQ4HTSCW.js → zh-TW-LVLBK4KF.js} +8 -0
- package/chunks/{zh-LMSTK4WD.js → zh-UBMWS2BJ.js} +8 -0
- package/chunks/{zoom-image-4BFYJ3HG.js → zoom-image-XLCK6DDK.js} +14 -11
- package/cli.js +13 -13
- package/locales/ca.js +13 -0
- package/locales/de.js +12 -0
- package/locales/en.js +11 -0
- package/locales/fr.js +13 -0
- package/locales/ja.js +13 -0
- package/locales/pt.js +12 -0
- package/locales/ru.js +12 -0
- package/locales/zh-TW.js +11 -0
- package/locales/zh.js +11 -0
- package/package.json +3 -3
- package/web-shell/assets/{arc-CgOpDKvm.js → arc-DItbX0DI.js} +1 -1
- package/web-shell/assets/{architectureDiagram-3BPJPVTR-0Ag_m34F.js → architectureDiagram-3BPJPVTR-CmnIlH8y.js} +1 -1
- package/web-shell/assets/{blockDiagram-GPEHLZMM-IwTwFau0.js → blockDiagram-GPEHLZMM-pObnB9kw.js} +1 -1
- package/web-shell/assets/{c4Diagram-AAUBKEIU-DLa5GUv8.js → c4Diagram-AAUBKEIU-BNBmMS0Y.js} +1 -1
- package/web-shell/assets/channel-hZnMfWA9.js +1 -0
- package/web-shell/assets/{chunk-2J33WTMH-DcYP-WFG.js → chunk-2J33WTMH-Dz4JBOO6.js} +1 -1
- package/web-shell/assets/{chunk-4BX2VUAB-C8MVPMiQ.js → chunk-4BX2VUAB-Cz2GnvAR.js} +1 -1
- package/web-shell/assets/{chunk-55IACEB6-BUi4Yfv3.js → chunk-55IACEB6-CC6kOQYx.js} +1 -1
- package/web-shell/assets/{chunk-727SXJPM-Cy3Su2YY.js → chunk-727SXJPM-Z4lY4gbh.js} +1 -1
- package/web-shell/assets/{chunk-AQP2D5EJ-Dp8Teqv6.js → chunk-AQP2D5EJ-Yz0hZr3D.js} +1 -1
- package/web-shell/assets/{chunk-FMBD7UC4-BDTDM-4j.js → chunk-FMBD7UC4-nKmTWTv9.js} +1 -1
- package/web-shell/assets/{chunk-ND2GUHAM-7HfDt0r2.js → chunk-ND2GUHAM-CjZz0f6Y.js} +1 -1
- package/web-shell/assets/{chunk-QZHKN3VN-BIBU0uCF.js → chunk-QZHKN3VN-Bab3FpuS.js} +1 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-BRnxm8DX.js +1 -0
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-BRnxm8DX.js +1 -0
- package/web-shell/assets/{cose-bilkent-S5V4N54A-DRLdcg9p.js → cose-bilkent-S5V4N54A-BuSAGFox.js} +1 -1
- package/web-shell/assets/{dagre-BM42HDAG-DCCgDwqW.js → dagre-BM42HDAG-CRZXTg3g.js} +1 -1
- package/web-shell/assets/{diagram-2AECGRRQ-CsBTBmk3.js → diagram-2AECGRRQ-BFx1Ayrz.js} +1 -1
- package/web-shell/assets/{diagram-5GNKFQAL-C9qjH8Lt.js → diagram-5GNKFQAL-B2hMZ3gS.js} +1 -1
- package/web-shell/assets/{diagram-KO2AKTUF-Brjs_lj2.js → diagram-KO2AKTUF-DKxAQLmc.js} +1 -1
- package/web-shell/assets/{diagram-LMA3HP47-DtiWCl_F.js → diagram-LMA3HP47-Bo1xGqVY.js} +1 -1
- package/web-shell/assets/{diagram-OG6HWLK6-DmfKLkBT.js → diagram-OG6HWLK6-B9NG78d-.js} +1 -1
- package/web-shell/assets/{erDiagram-TEJ5UH35-Bve_4deH.js → erDiagram-TEJ5UH35-DltQKiqQ.js} +1 -1
- package/web-shell/assets/{flowDiagram-I6XJVG4X-C2_kXCcV.js → flowDiagram-I6XJVG4X-5xXGmS4I.js} +1 -1
- package/web-shell/assets/{ganttDiagram-6RSMTGT7-BSBIjiTF.js → ganttDiagram-6RSMTGT7-bhGL5n0l.js} +1 -1
- package/web-shell/assets/{gitGraphDiagram-PVQCEYII-CyebwI55.js → gitGraphDiagram-PVQCEYII-ZYnz9hgh.js} +1 -1
- package/web-shell/assets/{index-d0yfnV1M.js → index-CBBeUArD.js} +1 -1
- package/web-shell/assets/index-CbgU-jUo.css +5 -0
- package/web-shell/assets/index-_nhzXqab.js +1750 -0
- package/web-shell/assets/{infoDiagram-5YYISTIA-CSDGnWxU.js → infoDiagram-5YYISTIA-Cg-jamQf.js} +1 -1
- package/web-shell/assets/{ishikawaDiagram-YF4QCWOH-BK4jU13p.js → ishikawaDiagram-YF4QCWOH-CcmvKlyq.js} +1 -1
- package/web-shell/assets/{journeyDiagram-JHISSGLW-D_W6j-oF.js → journeyDiagram-JHISSGLW-BRsOQZp1.js} +1 -1
- package/web-shell/assets/{kanban-definition-UN3LZRKU-C7xf-SdC.js → kanban-definition-UN3LZRKU-xW-dmEHK.js} +1 -1
- package/web-shell/assets/{linear-DQmzxpxd.js → linear-ClBfEZH2.js} +1 -1
- package/web-shell/assets/{mermaid.core-9P7AGOLG.js → mermaid.core-D65NDkzc.js} +5 -5
- package/web-shell/assets/{mindmap-definition-RKZ34NQL-DgcgMyPk.js → mindmap-definition-RKZ34NQL-CyvFslYa.js} +1 -1
- package/web-shell/assets/{pieDiagram-4H26LBE5-3wWGb9eG.js → pieDiagram-4H26LBE5-BwSefMyG.js} +1 -1
- package/web-shell/assets/{quadrantDiagram-W4KKPZXB-BeCXnQw9.js → quadrantDiagram-W4KKPZXB-GqBJspFS.js} +1 -1
- package/web-shell/assets/{requirementDiagram-4Y6WPE33-Bi1UOHDz.js → requirementDiagram-4Y6WPE33-B137PlTJ.js} +1 -1
- package/web-shell/assets/{sankeyDiagram-5OEKKPKP-lrLUjjbb.js → sankeyDiagram-5OEKKPKP-maHkkt8i.js} +1 -1
- package/web-shell/assets/{sequenceDiagram-3UESZ5HK-BFuqKz6Q.js → sequenceDiagram-3UESZ5HK-DJ1mRaG3.js} +1 -1
- package/web-shell/assets/{stateDiagram-AJRCARHV-8DemQLhu.js → stateDiagram-AJRCARHV-aRmP7Q4i.js} +1 -1
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-Db39gvZ_.js +1 -0
- package/web-shell/assets/{timeline-definition-PNZ67QCA-uzEuVQXg.js → timeline-definition-PNZ67QCA-CIQ3BRPA.js} +1 -1
- package/web-shell/assets/{vennDiagram-CIIHVFJN-BQTeZ_-o.js → vennDiagram-CIIHVFJN-DR9VPd5h.js} +1 -1
- package/web-shell/assets/{wardley-L42UT6IY-B6yM5Im7.js → wardley-L42UT6IY-ZcfIbT4D.js} +1 -1
- package/web-shell/assets/{wardleyDiagram-YWT4CUSO-ChM8bmgn.js → wardleyDiagram-YWT4CUSO-D0jgPi4T.js} +1 -1
- package/web-shell/assets/{xychartDiagram-2RQKCTM6-oNG6HyB1.js → xychartDiagram-2RQKCTM6-BGWU4s5o.js} +1 -1
- package/web-shell/index.html +2 -2
- package/chunks/chunk-MPHPFVKK.js +0 -48
- package/chunks/chunk-RLKC46YT.js +0 -3582
- package/chunks/chunk-UPDOZBWO.js +0 -358
- package/web-shell/assets/channel-BrGVBa2L.js +0 -1
- package/web-shell/assets/classDiagram-4FO5ZUOK-B5TUzJzI.js +0 -1
- package/web-shell/assets/classDiagram-v2-Q7XG4LA2-B5TUzJzI.js +0 -1
- package/web-shell/assets/index-CIDsgxTe.js +0 -1792
- package/web-shell/assets/index-DiQnWi_E.css +0 -5
- package/web-shell/assets/stateDiagram-v2-BHNVJYJU-DhM5ixYd.js +0 -1
- /package/chunks/{chunk-NTTBXH7U.js → chunk-UGK4K2N2.js} +0 -0
package/bundled/review/SKILL.md
CHANGED
|
@@ -88,13 +88,25 @@ The parser already classified the target, so there is nothing to disambiguate by
|
|
|
88
88
|
--owner <the verdict's owner> --repo <the verdict's repo> --host <the verdict's host>
|
|
89
89
|
```
|
|
90
90
|
|
|
91
|
+
For an **Aone nested-group target** (`…/<group>/<subgroup…>/<project>/codereview/<id>`), also pass `--group-path <group>/<subgroup…>/<project>` (the URL's full path before `/codereview/`) — owner/repo collapse to the last two segments, and without the full path the matcher could pick a different group's same-named repo.
|
|
92
|
+
|
|
91
93
|
Exit 0 prints the matching remote's name — forks included: a clone whose `upstream` points to the target repository matches that repository's PRs exactly. Exit 6 means no remote matches — go to item 3. Exit 7 means several match; tell the user and stop rather than picking one. Any other exit is fail-closed like the other gates: report it and stop.
|
|
92
94
|
|
|
93
95
|
2. If a matching remote is found, proceed with the **normal worktree flow** — use that remote name (instead of hardcoded `origin`) for `git fetch <remote> pull/<number>/head:qwen-review/pr-<number>`. In Step 7, use the owner/repo from the URL for posting comments.
|
|
94
96
|
|
|
95
|
-
For
|
|
97
|
+
For **every** `pr-url` target — **`github.com` included** — **pass `--host <host>` to every review subcommand that talks to the platform — `meta`, `fetch-pr`, `pr-context`, `comment-status`, `issue-context`, `fetch-diff`, `comment-body`, `plan-diff`, `test-plan`, `presubmit`, `compose-review`, `submit`, and `publish-assets`**. This routes all of their API calls at the right host in code (a forgotten host silently retargets them at github.com's same-named `owner/repo`), and it pins platform detection to the URL's host: without the hint, detection falls back to the cwd clone's origin, so a `github.com` PR reviewed from inside an Aone-origin clone (or the reverse) is hijacked to the other platform's backend. Every fetch this skill needs rides a subcommand — the one exception is Step 4's render-adjudication carve-out (a direct `gh api` against `QWEN_REVIEW_SCRATCH_REPO`, GitHub-only by nature). That call runs in a **verifier subagent's** shell, so a `--host` note here cannot reach it: it routes at the Enterprise host only when GH_HOST is **exported in the environment** (subagent shells inherit the process env). On an Enterprise run without an exported GH_HOST, render adjudication is unavailable — the verifier rules from the raw markdown and says so.
|
|
98
|
+
|
|
99
|
+
For an **Aone Code** target, run `/review` **from inside a clone of that repo** (origin on `gitlab.alibaba-inc.com`). The platform is detected from the clone's remote — the read subcommands (`meta`, `fetch-pr`, `issue-context`, `fetch-diff`) work unchanged, backed by the `a1` CLI instead of `gh`; the target number is the global MR id. `fetch-pr` fetches `refs/merge-requests/<id>/head` and builds the worktree + diff as usual, so agents still review the worktree. A `…/codereview/<id>` URL pasted from OUTSIDE a clone of that repo cannot be resolved — the URL's host does pin detection (passed as `--host`), but there is then no clone to fetch the MR ref into and build the worktree/diff from — stop and tell the user to run inside the clone. Pass `--host gitlab.alibaba-inc.com` on the subcommands for Aone targets: it is harmless for the a1-backed readers and makes both detection and the `--comment` refusal fire regardless of cwd.
|
|
100
|
+
|
|
101
|
+
Every Aone run is **context-unavailable** this phase, and several flows must be skipped rather than allowed to hit github.com's same-named repo:
|
|
96
102
|
|
|
97
|
-
|
|
103
|
+
- `pr-context`, `comment-status`, `presubmit` have no Aone backing — skip them. Step 7 caps the verdict at `COMMENT`; findings are still generated.
|
|
104
|
+
- `test-plan` fetches the PR body via `gh pr view` (GitHub-direct) — unbacked on Aone; treat the Test Plan as unchecked.
|
|
105
|
+
- Agent 0 (issue fidelity) is gated on `pr-context` success, so it is **skipped** on Aone — do not claim issue fidelity ran. (`issue-context` works standalone for the workitem evidence, but it is not wired to Agent 0.)
|
|
106
|
+
- Step 9's bypass audit queries GitHub by host; on an Aone report (host null) skip it instead of querying github.com.
|
|
107
|
+
- The run **is** read-only toward the platform in this phase: `publish-assets` (Step 7) is a Contents-API write that is not Aone-backed — skip it on Aone — and **`--comment` is refused** — report the findings in the terminal and saved report only, and tell the user posting to Aone is not supported yet.
|
|
108
|
+
|
|
109
|
+
3. If **no remote matches**, use **lightweight mode**: fetch the diff directly with `"${QWEN_CODE_CLI:-qwen}" review fetch-diff <number> --repo <owner>/<repo> --host <host> --out .qwen/tmp/qwen-review-pr-<number>-diff.txt` (the URL's host — `github.com` included, per the host rule above: without it the cwd clone's origin picks the platform). If `fetch-diff` fails here (auth, network), inform the user and stop — lightweight mode has no diff to review and no later step refetches it. Skip Step 2 (no local rules) and Step 8 (no local reports or cache). In Step 9, skip worktree removal (none was created) but still clean up temp files (`.qwen/tmp/qwen-review-{target}-*`). Also run `"${QWEN_CODE_CLI:-qwen}" review pr-context <number> <owner>/<repo> --host <host> --out .qwen/tmp/qwen-review-pr-<number>-context.md` — it is pure platform API and works cross-repo. Agent 0 and Step 6's open-Critical re-check depend on it: a `Refs #123`-style target issue is only discoverable from the PR body, and open Critical threads only from the context file, so skipping it lets a wrong-root fix sail through blocker-free. If `pr-context` fails here (auth, network), warn and continue with the diff alone — but skip Agent 0 (it has nothing to work from) and treat every open-Critical re-check verdict as "cannot tell", which forbids an Approve. Carry this forward as the **context-unavailable** state: Step 7's invariant caps **every** `C=0` outcome of such a run at `COMMENT` with a diff-only body (both the would-be APPROVE and the Suggestion-only "no blockers" sentence), so a run that could not see the PR's existing discussion can post findings but never certify the absence of blockers. In Step 7, use the owner/repo from the URL. Inform the user: "Cross-repo review: running in lightweight mode (no build/test)."
|
|
98
110
|
|
|
99
111
|
Based on the parsed `target.type`:
|
|
100
112
|
|
|
@@ -145,14 +157,14 @@ Based on the parsed `target.type`:
|
|
|
145
157
|
|
|
146
158
|
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.
|
|
147
159
|
|
|
148
|
-
- **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
|
|
149
|
-
- `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. **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.
|
|
150
|
-
- `upToDate: true` **and**
|
|
151
|
-
- `upToDate: true` **
|
|
152
|
-
- `
|
|
160
|
+
- **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}}`):
|
|
161
|
+
- `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. **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.)
|
|
162
|
+
- `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.
|
|
163
|
+
- `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."
|
|
164
|
+
- `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.
|
|
153
165
|
- `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); `hunks-outside-pr-diff` (the delta carries hunks the PR's own diff does not contain, which an ordinary "undo per feedback" revert produces from a perfectly valid anchor); `containment-unverified` (that check could not be RULED — a path shape its parser cannot name — which is an unavailable oracle rather than a disproved delta); `base-untrusted` (the base could not be fetched, so the clamp that prevents those could not be ruled); `capture-failed` (a capture threw); `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.
|
|
154
166
|
|
|
155
|
-
- **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, **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 capture — is re-run by the re-run. Every other reason is deterministic for the same sha and must NOT be retried: a validity refusal re-refuses, `partition-failed` re-fails the partitioner on identical bytes — with ONE exception, and `mergeBaseSha` is the field that names it. A `partition-failed` round that came back PLANLESS (`diffPath: null`) **with a null `mergeBaseSha` AND `baseFetchFailed: true`** never ran the full-range rescue at all: there was no base to rescue from, and the component that failed — the base fetch — is one the re-run repeats, so the same bytes can tile as a full review. Retry that one, once. A null `mergeBaseSha` with `baseFetchFailed: false` is the other cause and 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. A planless `partition-failed` that DID carry a `mergeBaseSha` means both ranges were in hand and both refused to tile, which the re-run reproduces exactly — do not retry it. The partitioner is deterministic either way; what varies is whether the round ever had a full range to offer it. The containment reasons re-rule identically) —, **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 model
|
|
167
|
+
- **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 capture — is re-run by the re-run. Every other reason is deterministic for the same sha and must NOT be retried: a validity refusal re-refuses, `partition-failed` re-fails the partitioner on identical bytes — with ONE exception, and `mergeBaseSha` is the field that names it. A `partition-failed` round that came back PLANLESS (`diffPath: null`) **with a null `mergeBaseSha` AND `baseFetchFailed: true`** never ran the full-range rescue at all: there was no base to rescue from, and the component that failed — the base fetch — is one the re-run repeats, so the same bytes can tile as a full review. Retry that one, once. A null `mergeBaseSha` with `baseFetchFailed: false` is the other cause and 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. A planless `partition-failed` that DID carry a `mergeBaseSha` means both ranges were in hand and both refused to tile, which the re-run reproduces exactly — do not retry it. The partitioner is deterministic either way; what varies is whether the round ever had a full range to offer it. The containment reasons re-rule identically) —, **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 reviewed-at sha 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). 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 the last posted round was fail-closed (`compose-review` withholds the anchor then — Step 8 names the conditions), had its ledger truncated by the marker's size caps (a partial work list must not certify a range — the dropped entries would fall outside the next round's scope and retire silently), or predates the field — in every case there is no anchor to recover, 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.)
|
|
156
168
|
|
|
157
169
|
- **The setup calls that do not feed each other go out in ONE response — as separate tool calls, never joined with `&&`/`;` into one Shell command** (high and medium effort — at low, Step 2's rules load is skipped and nothing consumes the comment index, so the batch is whatever calls remain). A joined chain changes the failure semantics — a `pr-context` failure must warn-and-continue, not skip the other two — and merges the `warning:` size lines the paging decisions below read. Once `fetch-pr` has returned (and the incremental check, which reads its report, is decided — except on the side-file anchor path, where the decision deliberately waits for `pr-context`'s side file), the next three commands are mutually independent — `pr-context` (below), `comment-status` (below), and Step 2's rules load — every one a read with no side effect the others observe. Issue all three tool calls in a single response, exactly as Step 3 already requires for the agent fan-out, then read their outputs (paging where a file exceeds one read, and those reads can share a response too). The rules load takes `<remote>/<baseRefName>` — the ref `fetch-pr` just updated; no local-existence probe — **except when the fetch report recorded `baseFetchFailed: true`: drop it from the batch and `git fetch <remote> <baseRefName>` first** (on an unresolvable ref `load-rules` reports "no rules found", indistinguishable from a repo that has none, and the review silently enforces nothing). Measured on a real small-PR run: the stretch from `parse-args` to the first agent launch took **7 minutes of wall clock**, one round-trip at a time, on calls that never needed an order. The only orderings that matter: `fetch-pr` before all of them (it creates the worktree and the plan), **any side-file `fetch-pr --since` re-run before `repo-context`** (the re-run rewrites the fetch report from scratch, and `repo-context` enriches that same file in place — an enrichment written first is silently discarded, and the roster then builds without the manifest's required agents), `repo-context` before `agent-prompt --roster` (the roster and every brief bake the manifest's required agents and context blocks, so building them first silently drops the context), and `agent-prompt --roster` after the rules load (the roster bakes the rules into every brief).
|
|
158
170
|
|
|
@@ -174,8 +186,9 @@ Based on the parsed `target.type`:
|
|
|
174
186
|
```bash
|
|
175
187
|
"${QWEN_CODE_CLI:-qwen}" review comment-status <pr_number> <owner>/<repo> \
|
|
176
188
|
--out .qwen/tmp/qwen-review-pr-<pr_number>-comment-status.json
|
|
177
|
-
#
|
|
178
|
-
# each subcommand is its own process, so a host set elsewhere
|
|
189
|
+
# add --host <host> (every PR target, including github.com — see Step 1's
|
|
190
|
+
# host rule); each subcommand is its own process, so a host set elsewhere
|
|
191
|
+
# does not carry over.
|
|
179
192
|
```
|
|
180
193
|
|
|
181
194
|
One call answers, per existing thread, every status question the re-check and the finder agents otherwise re-derive one API fetch at a time: is the anchor **outdated** at the live head (`line: null`), did the anchored **file change in the worktree since the comment's commit** and which commits touched it (`code.touchedBy` — the candidate "fixed by" commits), who replied and **did the PR author answer**, and whether the body **asserts a blocker** (same `carriesBlockerSignal` the context file's promotion uses). It also compares the worktree HEAD against the live PR head and warns on drift. **The report can exceed one `read_file`** — `threads` is path-sorted, so a truncated read drops the alphabetically-later files wholesale while the cut JSON does not even parse (measured; DESIGN.md — The 71-thread comment-status report). The command prints a `warning:` line naming the size when this happens; when it does, query the file with `jq` (it is machine-shaped) or page with `offset`/`limit` until `isTruncated` is false — same rule as the context file above. **Do not fetch per-comment status metadata yourself** — no raw API calls to read `line`/`outdated`/`commit_id`, and no hand-run `git log` per comment (measured; DESIGN.md — The 20-turn status re-derivation). Comment **bodies** are a different matter and stay where they were: the context file renders them (in full for blockers and review summaries), and only a body the renderer truncated is fetched, by running the exact `review comment-body` command its `_(truncated — run …)_` note names. If `comment-status` itself fails (auth, network), warn and continue — it is an index, not the evidence: statuses become "re-derive if needed", and nothing here sets the context-unavailable state.
|
|
@@ -252,9 +265,9 @@ For **cross-repo lightweight reviews**, do the same with the diff the platform h
|
|
|
252
265
|
--pr <pr_number> --repo <owner>/<repo> \
|
|
253
266
|
--effort <effort> \
|
|
254
267
|
--out .qwen/tmp/qwen-review-pr-<n>-plan.json
|
|
255
|
-
#
|
|
256
|
-
# welded issue-context command routes at it; a
|
|
257
|
-
# fetch-pr to carry the host otherwise.
|
|
268
|
+
# add --host <host> (every PR target, including github.com) — plan-diff
|
|
269
|
+
# records it and Agent 0's welded issue-context command routes at it; a
|
|
270
|
+
# lightweight run has no fetch-pr to carry the host otherwise.
|
|
258
271
|
```
|
|
259
272
|
|
|
260
273
|
**Pass `--pr`/`--repo` only when the `pr-context` fetch above succeeded** — they put the PR identity into the plan, which makes the roster REQUIRE Agent 0 (`check-coverage` will name it if it never runs, exactly as in worktree mode). If `pr-context` failed, omit them: the run is in the context-unavailable state, Agent 0 has nothing to work from, and a roster demanding an agent nobody can brief would wedge the review.
|
|
@@ -635,7 +648,7 @@ After deduplication, run reverse audit **iteratively** — the first launch ride
|
|
|
635
648
|
|
|
636
649
|
- **Small diffs (Step 3A path):** one reverse audit agent per round, reading the whole diff — except rounds 1 and 2, which are **the convergence pair** and launch together (below).
|
|
637
650
|
- **Large diffs (Step 3B path):** one reverse audit agent **per chunk** per round, launched together in a single response — and rounds 1 and 2 are **the convergence pair** here too, their per-chunk auditors launched together (below). A single agent asked to re-read a 5 800-line diff with a growing finding list appended is the most context-starved agent in the pipeline — precisely on the PRs where the reverse audit matters most. Each per-chunk auditor gets the same territory as its Step 3B counterpart, plus the cumulative finding list for the **whole** diff (so it knows what is already covered elsewhere).
|
|
638
|
-
- **The builder schedules the 3B fan-out; you do not.** Rounds 1 and 2 audit every chunk — they are what establishes each territory's record. From round 3 on, `--all-chunks` reads the harness transcripts and **retires** any chunk whose own last two audits were substantively dry (the receipt named what it examined AND the transcript shows the diff was opened): a retired chunk is cold-checked on alternating rounds instead of every round, and a cold check that yields anything returns it to every-round auditing. The savings land on the odd rounds — every retired chunk cold-checks together on the even ones, so an even round's fan-out is unchanged; expect the odd rounds to shrink, not the even ones (under the 3-round huge-diff cap only round 3 can shrink
|
|
651
|
+
- **The builder schedules the 3B fan-out; you do not.** Rounds 1 and 2 audit every chunk — they are what establishes each territory's record. From round 3 on, `--all-chunks` reads the harness transcripts and **retires** any chunk whose own last two audits were substantively dry (the receipt named what it examined AND the transcript shows the diff was opened): a retired chunk is cold-checked on alternating rounds instead of every round, and a cold check that yields anything returns it to every-round auditing. The savings land on the odd rounds — every retired chunk cold-checks together on the even ones, so an even round's fan-out is unchanged; expect the odd rounds to shrink, not the even ones (under the 3-round huge-diff cap — the reduction a run earns only when it has a deadline — only round 3 can shrink, because the cap ends the loop before round 5). The blocks it prints are the round; the `retirement:` note after the `end of round` line names each skipped chunk and its certificate — relay that note in your narration, and do not hand-build an auditor for a chunk the builder skipped. Why, measured: on a real 6-chunk run, two chunks were dry in **all five rounds** — a third of the loop's auditors re-certifying territories that had already converged, while the three hot chunks were where every finding came from. Attention follows evidence; the certificate a retired chunk holds (two consecutive substantive dry audits) is exactly the one the whole loop used to end on.
|
|
639
652
|
|
|
640
653
|
One anomaly the builder flags but does not refuse (#9242): a per-chunk build on a plan whose own `srcDiffLines`/`diffLines` say Step 3A prints a stderr note — the plan's numbers price one whole-diff auditor per round (the reverse-audit round cap reads them), yet per-chunk auditors were built. It fires on `--all-chunks` and on a `--chunk` build of a round that has no admission stamp yet; a stamped round's `--chunk` rebuilds are exempt — their fan-out was ruled on at admission. If the note fires and the fan-out is deliberate — you decided against the plan's numbers (the routing is yours, as Step 1 says), or this is a whole-round `--all-chunks` rebuild of an already-admitted round on a hand-maintained plan — say so in the round; if it was not deliberate, stop and re-derive the topology from Step 1 instead of spending a fan-out the plan never owed.
|
|
641
654
|
|
|
@@ -690,7 +703,7 @@ The brief holds what the auditor is for: hunt only the **gaps** no prior agent c
|
|
|
690
703
|
- **On the 3B path the builder is also the convergence ledger**: when every chunk holds two consecutive substantive dry audits and none is due a cold check, `--all-chunks` builds nothing, prints a `CONVERGED` explanation to stderr and exits **5**. Stop the loop and proceed to Step 6 — this is a **clean** convergence, not a gap: no `unreviewedDimensions` entry is owed, because each chunk holds the two-dry rule's evidence chunk by chunk — two consecutive dry **audits**, though not necessarily in consecutive rounds (a chunk dry in rounds 1 and 2 skips round 3 and cold-checks dry in round 4, holding rounds 2 and 4). If an earlier round-cap or budget refusal told you to add its stop entry to `unreviewedDimensions`, remove it now — this convergence supersedes that stop (the marker on disk is cleared the same way). Exit 5 is mainly the CLI enforcing the stop the two-dry-rounds rule above used to leave to orchestrator discretion; the new savings are the odd-round skips and a convergence at the cap round (round 5 on a 3B diff, round 3 under the huge-diff cap when the run has a deadline and round 5 when it does not — this ledger is 3B's, so the 3A tier's ten never applies here). (It cannot owe a verification launch: a reporting round makes its chunk hot, so every verifier launched with a later round that did run.)
|
|
691
704
|
- Stop at the plan's **`reverseAuditRounds` cap** — 10 on a 3A diff, 5 on a 3B one, and 3 for a huge diff (effective ≥ 3000 lines) **when the run has a deadline**, 5 when it does not (the huge reduction answers a six-hour ceiling, so it applies only where there is one) — and say so in the output rather than implying convergence. The cap is per topology because it prices a round, and a 3A round is one auditor where a huge-diff round is ~90 minutes; you never work this out yourself, the builder reads the plan's tier. The builder enforces this itself: a round past the cap gets a `ROUND CAP:` refusal on stderr and exit **4**, and — like the time-budget gate — writes a marker `compose-review` caps the verdict on whether or not you relay anything; still add the entry the message names to `unreviewedDimensions` so the terminal report agrees. If the cap round reported findings, its verifiers have NOT launched — that launch rides the next round's build, which the cap forbids — so verify them before Step 6 through `agent-prompt --role verify` **only** (never a hand-rolled agent), under the same bounded tail as the budget stop below: that builder is gated on the compose floor and refuses once too little time remains, and when the deadline is within the floor you stop waiting on any verifier batch still out and compose with the tags in hand — no fresh re-verification pass, and nothing already confirmed re-verified. This matters most on exactly the huge diffs the cap targets: a time-budgeted CI run that stops at the cap with ~30-90 minutes left must not spend it on an unbounded tail and die before compose. The tag backstop below (and `compose-review`'s machine-read of it) is what catches a miss.
|
|
692
705
|
- Findings **reported** by each round are merged into the cumulative list **before** the next round begins, so each round sees an updated baseline. **The merge runs unconditionally — before every round build and before Step 6, whether or not the previous round reported findings**: under the pipelined loop below, round _k_'s verdicts land during round _k+1_, and every termination mode (two dry rounds, CONVERGED, budget stop, the round cap) can arrive with the final rounds dry — a merge keyed to "some round reported something" would never apply the last verdicts that landed. Each merge applies every Step 4 verdict that has landed: confirmed removes the tag, rejected removes the entry. Verification status does not gate the merge — the list exists so auditors do not re-report what is already filed, and an unverified entry serves that purpose exactly as well as a confirmed one. The trade, named: an entry a verifier later rejects will have suppressed one round of rediscovery in its neighbourhood — the window is one round in one location, and the plan's round cap still bounds the loop. The tag is what keeps this mechanical rather than remembered: an entry enters the list tagged `— [unverified]`; the merge after its Step 4 verdict removes the tag (confirmed) or the entry (rejected). Step 6's confirmed-only read then has something to key on — anything still tagged is left out of the confirmed set — instead of a memory of which round each entry arrived in. The tag rides inside the findings file, which is hashed into the record key and copied to the digest-named list file each block points at — so a launch that drops the pointer matches no record, and the delivery floor counts the agent's read of that file exactly as it counts the brief's.
|
|
693
|
-
- **A reporting round whose every finding the verifier rejected is retroactively dry.** The merge already removes a rejected entry from the cumulative list; from the merge that applies the last of a round's rejections, the round also stops counting as a reporting round, and the two-consecutive-dry rule reads rounds' **effective** status. Rejected means rejected — an entry confirmed at low confidence keeps its round a reporting round. Under the pipelined loop a round's verdicts land while the next round runs, so the upgrade usually arrives one round late, and that is still one round saved: a measured run held round 2 dry, watched round 3's sole finding be rejected, and then ran rounds 4 **and 5** — round 4's dry return plus the rejection already in hand was the two-dry evidence, and the fifth round audited nothing the loop had not already answered (measured; DESIGN.md — The rounds a rejected finding bought (PR #8353)). The rule leans on the rejection bar the verifier's brief already enforces — a rejection claims direct counter-evidence, never mere unverifiability — so a round retired by rejections is retired on evidence, not on doubt. **It pairs forward only, and is consulted when a round returns**: on round _k_'s dry return, first apply every verdict that has landed (the unconditional merge — the retirement takes effect at this application, not at some earlier moment), then end the loop if round _k−1_ was dry or is now retired. Round _k−1_ counts **launches, not labels**: the convergence pair is one round here — a pair member is never round _k−1_ on its own (the pair bullet's not-carried-forward rule stands), and a reporting pair retires only when every finding from **both** members is rejected. The upgrade never ends the loop by itself — a preceding dry round plus a freshly-retired round stops nothing while the next round is already in flight: that round was launched, and its return is taken whatever it says, because a launched auditor can be carrying a real Critical. This is the measured shape (round 4's return is where the loop closes under this rule — the measured run, which predates it, ran a fifth round; a cap-5 shape — under the 3-round huge-diff tier the upgrade can only ever retire rounds 1–2, since the cap round's verdicts land during its solo verification, after the loop has already ended) and the only pairing licensed here. It softens nothing else: a whiffed scope stays not-audited whatever the verdicts say, and on 3B the retirement ledger's per-chunk certificates are untouched — this rule reads at the level the round counter reads.
|
|
706
|
+
- **A reporting round whose every finding the verifier rejected is retroactively dry.** The merge already removes a rejected entry from the cumulative list; from the merge that applies the last of a round's rejections, the round also stops counting as a reporting round, and the two-consecutive-dry rule reads rounds' **effective** status. Rejected means rejected — an entry confirmed at low confidence keeps its round a reporting round. Under the pipelined loop a round's verdicts land while the next round runs, so the upgrade usually arrives one round late, and that is still one round saved: a measured run held round 2 dry, watched round 3's sole finding be rejected, and then ran rounds 4 **and 5** — round 4's dry return plus the rejection already in hand was the two-dry evidence, and the fifth round audited nothing the loop had not already answered (measured; DESIGN.md — The rounds a rejected finding bought (PR #8353)). The rule leans on the rejection bar the verifier's brief already enforces — a rejection claims direct counter-evidence, never mere unverifiability — so a round retired by rejections is retired on evidence, not on doubt. **It pairs forward only, and is consulted when a round returns**: on round _k_'s dry return, first apply every verdict that has landed (the unconditional merge — the retirement takes effect at this application, not at some earlier moment), then end the loop if round _k−1_ was dry or is now retired. Round _k−1_ counts **launches, not labels**: the convergence pair is one round here — a pair member is never round _k−1_ on its own (the pair bullet's not-carried-forward rule stands), and a reporting pair retires only when every finding from **both** members is rejected. The upgrade never ends the loop by itself — a preceding dry round plus a freshly-retired round stops nothing while the next round is already in flight: that round was launched, and its return is taken whatever it says, because a launched auditor can be carrying a real Critical. This is the measured shape (round 4's return is where the loop closes under this rule — the measured run, which predates it, ran a fifth round; a cap-5 shape — under the 3-round huge-diff tier, which a run only gets when it has a deadline, the upgrade can only ever retire rounds 1–2, since the cap round's verdicts land during its solo verification, after the loop has already ended) and the only pairing licensed here. It softens nothing else: a whiffed scope stays not-audited whatever the verdicts say, and on 3B the retirement ledger's per-chunk certificates are untouched — this rule reads at the level the round counter reads.
|
|
694
707
|
- **Verification rides alongside the next round, not ahead of it.** When round _k_ returns with new findings, one response launches BOTH round _k_'s verifiers (Step 4, `--role verify --round k` with that round's new findings) AND round _k+1_'s auditors — build the two prompt sets first, then fire every agent together, exactly as Step 3 fans out. (Step 4's initial verification is the k=0 case of the same rule: its shards ride with the first reverse-audit launch — the convergence pair, whole-diff on 3A and per-chunk rounds 1 and 2 on 3B. The convergence pair is the one exception on the LAUNCH side: a pair member's return never triggers this rule per member — round 2's auditors are already in flight — and the pair bullets above define the one transition; the pair's findings still verify as the k=2 case, riding round 3.) The serial shape (audit → wait for verification → next round) spent 5-8 minutes per round waiting for verifiers whose results the next round's auditors never needed. Two orderings still hold: the **last** round's verification must complete before Step 6 (that ordering is what keeps unverified entries out of the report and the PR, backed by the tag backstop at the end of this step — which `compose-review` machine-checks from `findingsPath`, Step 6), and a rejected finding leaves the cumulative list at the next merge.
|
|
695
708
|
- **The round builder is also the loop's clock.** In a time-budgeted run (CI exports `QWEN_REVIEW_DEADLINE_EPOCH`; a local run normally has no deadline and is untouched), `agent-prompt --role reverse-audit` refuses to build a round that no longer fits: the remaining time must cover **the round itself** (estimated from the costliest round's measured cost so far — a repair relaunch can make one round the expensive one, and the gate prices the worst case the run has proved, not the newest dip — or a conservative constant for round 1) **plus** the reserve kept for its verification, compose-review and submission. On refusal it prints a `BUDGET:` line to stderr and exits **4**. That refusal is a termination rule, not an error — do not rebuild the round, do not relaunch auditors, and do not retry the command. The builder also records a budget-stop marker that `compose-review` reads directly, so the verdict is capped whether or not you relay anything; still add the exact entry the message names (`reverse audit — stopped before round <k> by the review time budget`) to `unreviewedDimensions` so the terminal report and the body agree, and proceed to Step 6. **The tail after a budget stop is bounded, and its order is load-bearing.** Verify the last round's findings — the ones whose verifiers would have ridden the round the gate just refused — **only through `agent-prompt --role verify`, never a hand-rolled `agent`**: that builder is gated on a **compose floor** and prints a `VERIFY BUDGET:` refusal (exit 4) once too little time remains, at which point you stop verifying and compose **immediately** — findings still carrying `— [unverified]` keep the tag, and `compose-review` caps the verdict on it and never treats an unverified finding as a confirmed blocker; everything earlier rounds confirmed still posts. **Bound the wait, not just the launch:** the builder gate stops a verifier from being _built_ below the floor, but a verifier admitted _above_ it can still run a real filesystem/git E2E workload past the floor while you wait on its batch — and `agent-prompt` builds prompts, it cannot cancel a running agent. So when the deadline is within the compose floor and a verifier batch has not returned, **stop waiting on it yourself**: take the findings in hand at their current tag and compose. A verifier you stopped waiting on leaves its findings `— [unverified]`, which caps the verdict exactly as a refused build would. Do **not** re-verify findings already confirmed in earlier rounds, and do **not** invent a fresh re-verification pass — that is the unbounded work a wall runs into. Compose and submit are non-negotiable; they always run. Why this exists, measured twice: a +1699-line PR's CI review ran the audit loop to the 5-round cap and was killed while round 5's findings were still being verified (#8368); and a 4,269-line cross-worktree git guard stopped the audit correctly with ~110 minutes left, then a single hand-rolled agent re-running a 15-family shell/git bypass battery with real filesystem E2E consumed all of it — the wall hit mid-verification, compose never ran, and ~20 E2E-confirmed Critical bypasses were never posted (measured; DESIGN.md — The killed-before-compose tail (PR #8687)). A review that stops on the budget still reports everything it proved; one that runs past it reports nothing.
|
|
696
709
|
|
|
@@ -762,7 +775,7 @@ Render the rulings as a short table at the top of the Findings section — id, o
|
|
|
762
775
|
|
|
763
776
|
**A re-review that keeps posting new non-Critical findings is the motor of a feedback loop this pipeline has measured from the outside**: every push triggers a fresh review, the review files findings on code the previous round just added, the next push implements them, and the diff widens — which allocates more agents, which file more findings. One managed PR rode that loop to +13k lines across 8 rounds with its per-round Critical count flat, and was closed unmerged; the growth was 78–86% test lines. Bug-finding never converges a loop — only the **posting bar** can, and it must rise as rounds accumulate, exactly the discipline a senior reviewer applies by hand ("after ~5 rounds, only blockers; defer the rest, on the record"). This posture is that discipline, made the default. It governs **what posts to the PR**, never what is found, verified, or reported in the terminal: `RECALL` still binds every finder, Step 4 still verifies, the artifact and the terminal report still carry everything.
|
|
764
777
|
|
|
765
|
-
**Resolve the floor first.** The Step 1 verdict's `severityFloor` is `critical`, `suggestion`, or `auto`. Explicit values are the operator's call: `critical` applies the Critical-only posture from round 1; `suggestion` turns the posture **off** — every round posts Suggestions, and the code-age rule below does not run. `auto` — the default — resolves here, where the round is known: **this review is round `prev ledger round + 1`**, and the round that decides the posture is the SIDE FILE's — the same read `compose-review` stamps into the marker and the deferral clause; the local cache's round scopes the diff but never decides the posture, or the body and the marker would disagree about which round ran (no recovered ledger → round 1 → no posture). Through round 5 the floor is `suggestion`; **from round 6 it is `critical`**. In the **context-unavailable** state the round is unknowable — the ledger this rule counts from could not be recovered by a run that could not read the PR — so treat `auto` as round 1: no posture, full posting, and say so in the terminal report (the deterministic marker still stamps its own count from the side file; a posting bar in doubt fails open, bookkeeping does not). Carry the **verdict's `severityFloor` into the compose state UNRESOLVED** — explicit values as they are, and `auto` as the literal string `auto`, never as the level it resolved to this round: the module licenses `auto` by the round it derives itself, and a round-resolved `suggestion` is indistinguishable from the operator's explicit posture-off override — passing it would turn every legal rounds-2–5 age-rule deferral into an unlicensed one. The resolution in this paragraph decides what YOU post; the state field carries the policy.
|
|
778
|
+
**Resolve the floor first.** The Step 1 verdict's `severityFloor` is `critical`, `suggestion`, or `auto`. Explicit values are the operator's call: `critical` applies the Critical-only posture from round 1; `suggestion` turns the posture **off** — every round posts Suggestions, and the code-age rule below does not run. `auto` — the default — resolves here, where the round is known: **this review is round `prev ledger round + 1`**, and the round that decides the posture is the SIDE FILE's — the same read `compose-review` stamps into the marker and the deferral clause; the local cache's round scopes the diff but never decides the posture, or the body and the marker would disagree about which round ran (no recovered ledger → round 1 → no posture). Through round 5 the floor is `suggestion`; **from round 6 it is `critical`**. In the **context-unavailable** state the round is unknowable — the ledger this rule counts from could not be recovered by a run that could not read the PR — so treat `auto` as round 1: no posture, full posting, and say so in the terminal report (the deterministic marker still stamps its own count from the side file; a posting bar in doubt fails open, bookkeeping does not). Carry the **verdict's `severityFloor` into the compose state UNRESOLVED** — explicit values as they are, and `auto` as the literal string `auto`, never as the level it resolved to this round: the module licenses `auto` by the round it derives itself, and a round-resolved `suggestion` is indistinguishable from the operator's explicit posture-off override — passing it would turn every legal rounds-2–5 age-rule deferral into an unlicensed one. The resolution in this paragraph decides what YOU post; the state field carries the policy. **The module also enforces the floor itself**: a Suggestion still drafted inline past a resolved `critical` floor is moved into the deferral list mechanically by `compose-review`/`submit` (the composed result's `floorEnforced` names the moved indices, the posted body discloses the move, and `submit` drops those comments from the write). Your Step 6 routing stays the primary path — the enforcement is the backstop that keeps the posted set lawful when the routing drifts, so a submit report showing fewer inline comments than you drafted under a critical floor is the floor working, not a lost finding. Three consequences of it being mechanical: the backstop classifies by the drafted severity MARKER alone — it cannot re-derive confidence or a Nice-to-have, so keeping low-confidence and Nice-to-have findings OUT of the drafted comments (as this step already mandates) is what keeps them out of the published deferral list too; **leave moved comments IN the comments file and the submit payload** — the CLI removes them from the write itself, and hand-removing them "to match" makes both boundaries recompute over the reduced set and erases the deferral record the move exists to keep; and the floor it enforces is the RESOLVED one (an explicit `critical`, or `auto` from round 6), recovered where possible from the CLI's own record of the invocation rather than the state field alone.
|
|
766
779
|
|
|
767
780
|
**At floor `critical`, a non-Critical finding that would otherwise post is recorded, not requested.** The deferrable set is exactly the set the floor takes away: **high-confidence Suggestions** — the findings a `suggestion`-floor round would have drafted inline. Low-confidence findings and Nice-to-haves were never posted at any floor and **stay terminal-only exactly as before**: routing them through the deferral list would _publish_ to the PR what the review contract keeps out of it, and inflate the list the posture exists to keep small. A deferred finding has been through Step 4 like any posted one — the deferral list publishes its one-line claims in the body, so `compose-review`'s verifier-delivery floor counts deferred findings exactly as posted ones; an unverified claim does not become publishable by being deferred. (Deterministic findings are the exception on both sides at once: a `[build]`/`[test]`/`[probe]` finding is pre-confirmed, Step 4 launches no verifier for it, and the floor excludes it — by its `source` field.) Each deferred finding stays in the findings artifact and the terminal report under its own grouping — "Deferred (convergence posture)" — and enters the compose state's `deferredSuggestions` as a **TYPED entry, one object per finding, copied from the artifact's own fields**: `{"file": "src/a.ts", "line": 42, "source": "test", "severity": "Suggestion", "title": "mutation survivor on the retry guard"}` (`line` optional; a pattern aggregate adds `"locations": N` for its further locations). This is a data field, not a sentence: `compose-review` derives deterministic from `source`, relocates a `severity: "Critical"` entry into the body Criticals (a Critical is never deferred), refuses a `"Nice to have"` (terminal-only) or any malformed entry, and RENDERS the human line `file:line — [source] title` itself — never write that line into the state, and never re-type the fields: read them out of the findings artifact you just wrote. It is **not** drafted into the `comments` array, **not** counted toward `S`, and casts no vote on the event: `compose-review` renders the list as a disclosed, non-capping paragraph — up to 20 entries, each capped at 240 characters, with an overflow count pointing at the run report — so the deferral is on the PR record without opening a thread that regenerates a round, and anything past the rendered cap survives in full in the findings artifact and the terminal report (say so there when the cap trims the list). A previous-round **non-Critical** ledger entry that still stands is ruled in the status table as `still stands — deferred (convergence posture)` and is likewise not re-posted; it leaves the machine ledger (`buildLedger` ingests only posted findings), and the deferral list plus the original round's thread remain its record. **A Critical is never deferred — any round, any floor**: new Criticals post, still-standing ledger Criticals re-post under their original ids, and every Critical ruling above runs unchanged. An APPROVE composed over a non-empty deferral list opens "No blocking issues" instead of "No issues found" — `compose-review` owns that wording.
|
|
768
781
|
|
|
@@ -815,7 +828,8 @@ Two failure modes this closes, both observed in this repo's own dogfood: reporti
|
|
|
815
828
|
--worktree <worktreePath> \
|
|
816
829
|
--build-test <Agent 7's build-test report, when this review produced one> \
|
|
817
830
|
--out <the plan report's directory>/qwen-review-pr-<n>-test-plan.json
|
|
818
|
-
#
|
|
831
|
+
# add --host <host> (every PR target, including github.com) — it fetches
|
|
832
|
+
# the PR description.
|
|
819
833
|
```
|
|
820
834
|
|
|
821
835
|
Run it on a same-repo **PR** review only. A **local** or **file** review has no PR body, and a cross-repo **lightweight** review has no worktree to resolve paths against; the command is skipped in both, and `compose-review` expects nothing from it there.
|
|
@@ -862,8 +876,12 @@ Each entry carries `id` (unique — outcomes and resolved anchors both join on i
|
|
|
862
876
|
"${QWEN_CODE_CLI:-qwen}" review compose-review --input .qwen/tmp/qwen-review-{target}-compose.json \
|
|
863
877
|
--comments .qwen/tmp/qwen-review-{target}-comments.json \
|
|
864
878
|
--out .qwen/tmp/qwen-review-{target}-composed.json
|
|
865
|
-
#
|
|
866
|
-
#
|
|
879
|
+
# PR reviews: add --pr <n> --repo <owner/repo> — the recorded-floor
|
|
880
|
+
# recovery's first identity, mirroring submit's own --pr/--repo so the
|
|
881
|
+
# archived compose and the post resolve one floor whatever the plan does.
|
|
882
|
+
# add --host <host> (every PR target, including github.com) — compose-review
|
|
883
|
+
# may fetch the PR description to pick the body language, that gh call must
|
|
884
|
+
# hit the PR's host, and the host is the recovery's own identity axis too.
|
|
867
885
|
```
|
|
868
886
|
|
|
869
887
|
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 (see there for every field): 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.
|
|
@@ -877,6 +895,8 @@ The rules it applies — so you can read the line it gives you, not so you can a
|
|
|
877
895
|
- **Request changes** — one or more high-confidence Criticals, anchored or in the body, **whose verification is on record** (a deterministic `[build]`/`[test]` finding is pre-confirmed and needs none).
|
|
878
896
|
- **Comment** — suggestions but no blockers, **or** an Approve that a cap took away: an uncoverable chunk, a chunk nobody read, a dimension nobody reviewed, a **reverse audit that never ran**, an existing blocker you could not rule on, a PR whose discussion you could not read. A review that did not read part of the diff — or never looked for what it missed — cannot certify it. **Or a Request changes whose blockers were never verified**: the findings still post, disclosed as unverified, but an unverified finding must not become a public blocker — a run whose verifier never launched posted a CHANGES_REQUESTED onto an external contributor's PR over a Critical its own body disclosed as unverified, and this row is what stops the next one.
|
|
879
897
|
|
|
898
|
+
**The body it returns already fits GitHub's limit.** A review body over 65,536 characters is rejected by the API **whole** — every blocker it carries with it — so `compose-review` measures the composed body (holding room for the ledger marker it appends) and, when it would overflow, trims in a fixed order: **the Chinese fold first** — it is a translation of the English above it, so dropping it costs no content at all — then the deferral display, then the not-reviewed disclosures, and **the blockers, the undecided-blocker list and the sentences that qualify the verdict never**. Every trim is disclosed at the top of the body — naming which kinds went, above the sentences that refer to them — and repeated on stderr; if the un-trimmable remainder still overflows, the body is truncated with a loud notice rather than posted as a rejection — and **that notice rides above the cut, with the others**, so nothing the cut left open can swallow it and no part of this has to model how the page renders. That last cut has an order of its own: it spends the sentences the author already received in an earlier round — the undecided-blocker list — before this round's body Criticals, which exist in no other place the author can reach. You do not shorten anything yourself to help it — a finding you drop is a finding lost, while **a finding it trims stays whole in the findings artifact** (each deferral is its own `D<round>-<n>` entry there). **A trimmed disclosure section is not a finding and has no other durable copy** — the artifact persists findings, counts and the trimmed body, so the not-reviewed, deferred-checker, Test-Plan and repository-context text exists nowhere else once the body drops it. The stderr line names which kinds went: **say in your Step 6 terminal summary what was trimmed and what it said.** That summary is the copy.
|
|
899
|
+
|
|
880
900
|
**Why this is a command and not a paragraph.** It was a paragraph, and the paragraph was skipped. A run once printed an Approve it had composed itself, from prose, on a review whose gate had just refused (measured; DESIGN.md — The paraphrased roster prompt). There is now one place a verdict exists. Skipping the command does not get you a different one; it gets you none.
|
|
881
901
|
|
|
882
902
|
**And you may not overrule the line it gives you.** The failure came back subtler: a run read the capped verdict, narrated the gap away as a "transcript visibility issue", and reported Approve — wrongly, and by its own doing (measured; DESIGN.md — The narrated-away cap). **A cap you can explain is still a cap.** If you believe a gap is wrong, the answer is to make the step verifiable — relaunch it with the prompt `agent-prompt` printed, verbatim — and run `compose-review` again. It is never to keep the verdict you preferred and narrate the gap away. The verdict you print, and the verdict in the report you save, are the one this command computed; when they differ from it, the review is lying to the person who trusted it.
|
|
@@ -978,9 +998,9 @@ Report `stats.drifted` in the terminal: it is the number of findings whose agent
|
|
|
978
998
|
|
|
979
999
|
Do **not** submit a review — with a placeholder body, a one-character body, or any body at all — merely to discover whether an anchor sticks. Each such attempt is a permanent, public review on someone's pull request. This has happened, five times in one run (measured; DESIGN.md — The five test reviews). One Create Review call, after the lookup, is the only write this step makes.
|
|
980
1000
|
|
|
981
|
-
First, determine the repository owner/repo. For **same-repo** reviews, run `"${QWEN_CODE_CLI:-qwen}" review meta` (
|
|
1001
|
+
First, determine the repository owner/repo. For **same-repo** reviews, run `"${QWEN_CODE_CLI:-qwen}" review meta` (with `--host <host>` for every PR target — see Step 1's host rule) and read its `ownerRepo`. For **cross-repo** reviews, use the owner/repo from the PR URL in Step 1.
|
|
982
1002
|
|
|
983
|
-
Use the **HEAD commit SHA** captured in Step 1. If not captured, fall back to `"${QWEN_CODE_CLI:-qwen}" review meta {pr_number} --repo {owner}/{repo}` (
|
|
1003
|
+
Use the **HEAD commit SHA** captured in Step 1. If not captured, fall back to `"${QWEN_CODE_CLI:-qwen}" review meta {pr_number} --repo {owner}/{repo}` (with `--host <host>` for every PR target — see Step 1's host rule) and read its `headSha`.
|
|
984
1004
|
|
|
985
1005
|
**Run pre-submission checks**: the bundled `qwen review presubmit` subcommand performs self-PR detection, CI / build status classification, and existing-Qwen-comment classification in one pass — three deterministic gh-API queries collapsed into a single JSON report. Read the report to drive the rest of Step 7.
|
|
986
1006
|
|
|
@@ -1128,12 +1148,12 @@ Then reference each finding's `assets` URLs in its inline comment body as `![evi
|
|
|
1128
1148
|
{
|
|
1129
1149
|
"path": "src/file.ts",
|
|
1130
1150
|
"line": 42,
|
|
1131
|
-
"body": "**[Critical]** issue description
|
|
1151
|
+
"body": "**[Critical]** issue description as plain sentences carrying the concrete trigger and the wrong outcome\n\n```suggestion\nfix code\n```\n\n_— YOUR_MODEL_ID via Qwen Code /review (v{{cliVersion}})_",
|
|
1132
1152
|
},
|
|
1133
1153
|
{
|
|
1134
1154
|
"path": "src/other.ts",
|
|
1135
1155
|
"line": 88,
|
|
1136
|
-
"body": "**[Suggestion]** recommended improvement
|
|
1156
|
+
"body": "**[Suggestion]** recommended improvement as plain sentences carrying the concrete cost (what is duplicated, wasted, or fragile)\n\n```suggestion\nimproved code\n```\n\n_— YOUR_MODEL_ID via Qwen Code /review (v{{cliVersion}})_",
|
|
1137
1157
|
},
|
|
1138
1158
|
],
|
|
1139
1159
|
"state": {
|
|
@@ -1176,7 +1196,7 @@ The verdict is a computed fact and this is the second place it must not be re-de
|
|
|
1176
1196
|
|
|
1177
1197
|
When `startLine === line`, emit only `"line"` — a single-line comment needs no side (it defaults to `RIGHT`, which is what every comment here is). Do **not** send `start_line` on its own: the multi-line form that omits `start_side` is the one shape of this feature that fails, and it fails by discarding every inline blocker in the review.
|
|
1178
1198
|
|
|
1179
|
-
- Comment body format: `**[Critical]** issue description
|
|
1199
|
+
- Comment body format: `**[Critical]** issue description\n\n```suggestion\nfix\n```\n\n_— YOUR_MODEL_ID via Qwen Code /review (v{{cliVersion}})_` — use the `**[Suggestion]**` prefix for Suggestion-level findings so the author can tell blockers from recommendations at a glance. Write the description as plain reviewer prose: state the problem, when it bites, and what to do about it, in ordinary sentences — no `— Failure scenario:` label, no `<trigger> → <wrong outcome>` arrow notation, no section-header voice. The description MUST still carry the finding's concrete failure scenario (the trigger and the wrong outcome, or the concrete cost) — a posted comment that says only what to change, without why it fails, has lost the evidence the finder was required to produce; the scaffolding is gone, the evidence is not. The prefix must be the **first thing in the body** and the footer must be present: the CLI's counting, its unmarked-draft gates, and the attribution-off strip machinery key off them. The autofix coupling is narrower — `.github/workflows/qwen-autofix.yml` recognizes Critical findings by the `**[Critical]**` substring in comment bodies (position-independent) and keeps Suggestion findings out of the autofix loop by its absence; it never reads the footer. Changing the prefix silently makes the autofix bot start applying non-blocking suggestions. (When the operator turned `review.attribution` off, `submit` strips the prefix and the footer from what GitHub receives — you write them regardless; they are the pipeline's counting and filtering signals.)
|
|
1180
1200
|
- The model name is declared at the top of this prompt. You MUST include it in every footer. Do NOT omit the model name.
|
|
1181
1201
|
- Use ` ```suggestion ` for one-click fixes; regular code blocks if fix spans multiple locations.
|
|
1182
1202
|
- Only ONE comment per unique issue.
|
|
@@ -1187,10 +1207,10 @@ Then submit it — through `submit`, which checks the authorisation and the payl
|
|
|
1187
1207
|
"${QWEN_CODE_CLI:-qwen}" review submit \
|
|
1188
1208
|
--pr {pr_number} --repo {owner}/{repo} \
|
|
1189
1209
|
--review .qwen/tmp/qwen-review-{target}-review.json \
|
|
1190
|
-
[--host <host>] #
|
|
1210
|
+
[--host <host>] # the PR's host — pass for every PR target, including github.com (pins the platform)
|
|
1191
1211
|
```
|
|
1192
1212
|
|
|
1193
|
-
**If the call fails with HTTP 422**, the review is created all-or-nothing — nothing was posted, including the Critical findings. This should now be unreachable for anchor arithmetic: every `line` you posted came out of `resolve-anchors`, which only ever considers lines it collected from **inside a hunk** of the very diff you are reviewing. So before working the recovery below, check the likelier remaining causes: **the diff you resolved against is not the commit you are posting to** — re-run `"${QWEN_CODE_CLI:-qwen}" review meta <n> --repo <owner>/<repo>` (
|
|
1213
|
+
**If the call fails with HTTP 422**, the review is created all-or-nothing — nothing was posted, including the Critical findings. This should now be unreachable for anchor arithmetic: every `line` you posted came out of `resolve-anchors`, which only ever considers lines it collected from **inside a hunk** of the very diff you are reviewing. So before working the recovery below, check the likelier remaining causes: **the diff you resolved against is not the commit you are posting to** — re-run `"${QWEN_CODE_CLI:-qwen}" review meta <n> --repo <owner>/<repo>` (with `--host <host>` for every PR target — see Step 1's host rule) and compare its `headSha` to the `commit_id` in your review JSON (which is the `fetchedSha` Step 1 captured; `fetchedSha` is a field of the _fetch report_, not of the review JSON). If they differ, the head advanced mid-review and **this review is of a commit that is no longer the pull request.** Do not re-resolve the old findings against the new diff and submit those: re-resolving relocates the _anchors_, it does not review the new code, re-verify the old conclusions, re-check the open Criticals, or re-run presubmit. You would be approving lines nobody read, or filing a blocker the new commit already fixed. **Abandon this submission and start the review again at the new SHA** — say so in your output, and go back to Step 1's `fetch-pr` — **unless this review has already restarted once for head movement** (the shared per-review bound the drift rule states above): in that case do NOT restart again, submit at the current reviewed SHA with the drift named, and let the Approve cap stand. Step 8 writes no cache for an abandoned run. The other cause is a `line` hand-edited after the resolver returned it. GitHub's error names the failing field (`pull_request_review_thread.line must be part of the diff`) but **does not tell you which entry is at fault**, so do not try to read the offender out of the error text.
|
|
1194
1214
|
|
|
1195
1215
|
Recovery, if it is genuinely an anchor: recheck them against `files[].hunks[]` from the fetch report — a pure lookup, no API calls (in lightweight mode, against the `fetch-diff` output you already have): an entry is valid if its `line` appears **anywhere inside a diff hunk** for `path` — an added or modified line, or an unchanged context line rendered within the hunk (every comment is on the `RIGHT` side: a single-line one by default, a multi-line one because it says so explicitly). For a multi-line entry, **one hunk must contain the whole range**: `newStart <= start_line <= line <= newEnd` for the _same_ hunk. Checking the two ends independently passes a range whose endpoints sit in different hunks, and a reversed range (`start_line > line`) passes both checks and 422s anyway — a second rejection you paid a round trip to discover. Check that it carries `side` and `start_side` too, whose absence is itself a 422. What GitHub rejects is a line in **no hunk at all**, or a file the PR does not touch. Drop every entry that fails that test, then resubmit once: move each failing **Critical** into the `body` as a whole-PR observation, and discard each failing **Suggestion** (it stays in the terminal output and the Step 8 report — Suggestion text must not enter `body`, see above). **You recompute nothing.** Update the payload and resubmit: each relocated Critical moves into `state.bodyCriticals`, each discarded Suggestion increments `state.suggestionsDiscarded`, and the failing entries come out of `comments`. `submit` recomposes the event and body from what you hand it, so the guarantees the recovery used to hand-derive are structural: a discarded Suggestion still counts toward `S`, so the verdict never upgrades to `APPROVE` on the resubmit; a context-unavailable run keeps its diff-only wording; a relocated blocker keeps `REQUEST_CHANGES` (body Criticals count toward `C` exactly like anchored ones). If the resubmit still 422s, submit once more with `"comments": []` — every remaining Critical in `state.bodyCriticals`, every Suggestion counted in `state.suggestionsDiscarded`: a review with the blockers in prose beats no review at all, and the truth table produces a non-empty `COMMENT` body when no Critical remains, so the one combination GitHub is documented to reject (no body, no comments) cannot be constructed. Never let a single mis-anchored Suggestion suppress a Critical blocker. Log which entries were relocated and which were discarded.
|
|
1196
1216
|
|
|
@@ -1247,14 +1267,14 @@ After the Markdown report exists, create and register the structured review arti
|
|
|
1247
1267
|
|
|
1248
1268
|
`save-artifact` resolves relative paths and its containment root against `--workspace-root` — **pass the main project directory explicitly, as the block above does**; without the flag it falls back to its own working directory. The flag is not decoration: the root anchors the containment checks (`isWithin` and the symlink walk), and an ambient-cwd root is only as trustworthy as wherever the command happened to run — from inside the untrusted PR worktree it would be the PR's own tree, the exact threat `comment-status`'s run-from-the-main-checkout rule exists to prevent. It used to prefer `QWEN_CODE_PROJECT_DIR`, which does not name the main checkout in any environment — the harness exports it as the session-storage directory under the runtime base — and every measured CI run burned minutes rediscovering that before improvising a workaround (measured; DESIGN.md — The artifact root that pointed at qwen-home).
|
|
1249
1269
|
|
|
1250
|
-
For PR worktree mode, the findings and composed inputs were created inside `worktreePath`, while the durable report and output belong to the main project directory. Pass absolute paths for all four: resolve `--findings` and `--composed` against `worktreePath`, and resolve `--report` and `--out` against the main project directory. The worktree lives under the main project's `.qwen/tmp/`, so all four remain inside the session workspace accepted by the helper. `save-artifact` prints one JSON object on stdout — `{"path": "<absolute path>", "workspacePath": "<path relative to the main project directory>"}`. Then call `record_artifact` in the current session with exactly this registration shape, copying `
|
|
1270
|
+
For PR worktree mode, the findings and composed inputs were created inside `worktreePath`, while the durable report and output belong to the main project directory. Pass absolute paths for all four: resolve `--findings` and `--composed` against `worktreePath`, and resolve `--report` and `--out` against the main project directory. The worktree lives under the main project's `.qwen/tmp/`, so all four remain inside the session workspace accepted by the helper. `save-artifact` prints one JSON object on stdout — `{"path": "<absolute path>", "workspacePath": "<path relative to the main project directory>"}`. Then call `record_artifact` in the current session with exactly this registration shape, copying the absolute `path` into `workspacePath`. The tool verifies the file and stores the canonical workspace-root-relative form. Do not invent a different relative path, and do not use the old `path` tool parameter:
|
|
1251
1271
|
|
|
1252
1272
|
```json
|
|
1253
1273
|
{
|
|
1254
1274
|
"title": "Code review result",
|
|
1255
1275
|
"kind": "other",
|
|
1256
1276
|
"storage": "workspace",
|
|
1257
|
-
"workspacePath": ".
|
|
1277
|
+
"workspacePath": "<absolute path from save-artifact.path>",
|
|
1258
1278
|
"mimeType": "application/vnd.qwen.code-review+json",
|
|
1259
1279
|
"metadata": {
|
|
1260
1280
|
"artifactType": "code_review",
|
|
@@ -1269,7 +1289,7 @@ The JSON helper is fail-closed because it carries the authoritative review resul
|
|
|
1269
1289
|
|
|
1270
1290
|
If reviewing a PR **at high effort**, update the review cache for incremental review support. Low and medium reviews must NOT write it — 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.
|
|
1271
1291
|
|
|
1272
|
-
**
|
|
1292
|
+
**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:
|
|
1273
1293
|
|
|
1274
1294
|
1. Create `.qwen/review-cache/` directory if it doesn't exist
|
|
1275
1295
|
2. Write `.qwen/review-cache/pr-<number>.json` with:
|
|
@@ -1295,7 +1315,7 @@ If reviewing a PR **at high effort**, update the review cache for incremental re
|
|
|
1295
1315
|
}
|
|
1296
1316
|
```
|
|
1297
1317
|
|
|
1298
|
-
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
|
|
1318
|
+
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.
|
|
1299
1319
|
|
|
1300
1320
|
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.
|
|
1301
1321
|
|
|
@@ -4,44 +4,44 @@ import {
|
|
|
4
4
|
MINIMUM_MAX_HEIGHT,
|
|
5
5
|
MaxSizedBox,
|
|
6
6
|
setMaxSizedBoxDebugging
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-J63PKMYA.js";
|
|
8
8
|
import "./chunk-6QFDQMQX.js";
|
|
9
9
|
import "./chunk-SV5PQVQE.js";
|
|
10
10
|
import "./chunk-2IIJTXYF.js";
|
|
11
11
|
import "./chunk-2M55OBEB.js";
|
|
12
12
|
import "./chunk-5QWWOFGG.js";
|
|
13
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-E7REAOTN.js";
|
|
14
14
|
import "./chunk-2MIN6GRR.js";
|
|
15
15
|
import "./chunk-QHTIBUWB.js";
|
|
16
16
|
import "./chunk-RKUWKYED.js";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-XC377TLN.js";
|
|
18
18
|
import "./chunk-GOFAQQZA.js";
|
|
19
19
|
import "./chunk-5M6IDOMF.js";
|
|
20
20
|
import "./chunk-TWPJO254.js";
|
|
21
21
|
import "./chunk-CQ35AJ4Z.js";
|
|
22
|
-
import "./chunk-
|
|
23
|
-
import "./chunk-
|
|
24
|
-
import "./chunk-
|
|
25
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-FS5NGKWC.js";
|
|
23
|
+
import "./chunk-SLILKYV2.js";
|
|
24
|
+
import "./chunk-NVRMCWTP.js";
|
|
25
|
+
import "./chunk-AKBOLMUF.js";
|
|
26
26
|
import "./chunk-6PVPNMXU.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-SR2LYSMT.js";
|
|
28
28
|
import "./chunk-IRH27ZC2.js";
|
|
29
29
|
import "./chunk-QHWCP53L.js";
|
|
30
|
-
import "./chunk-
|
|
31
|
-
import "./chunk-
|
|
30
|
+
import "./chunk-OBJYN4MX.js";
|
|
31
|
+
import "./chunk-2C6HJMG3.js";
|
|
32
32
|
import "./chunk-O6GEWCJA.js";
|
|
33
33
|
import "./chunk-T26EAKDL.js";
|
|
34
34
|
import "./chunk-ZPJWUGCS.js";
|
|
35
35
|
import "./chunk-CPBF7KYF.js";
|
|
36
36
|
import "./chunk-RWDNJBWN.js";
|
|
37
|
-
import "./chunk-
|
|
37
|
+
import "./chunk-WPVUOVYX.js";
|
|
38
38
|
import "./chunk-MIPFDQAF.js";
|
|
39
39
|
import "./chunk-MLXTMF7H.js";
|
|
40
40
|
import "./chunk-KV27IEHM.js";
|
|
41
41
|
import "./chunk-E3DYKPYZ.js";
|
|
42
|
-
import "./chunk-
|
|
43
|
-
import "./chunk-
|
|
44
|
-
import "./chunk-
|
|
42
|
+
import "./chunk-4ZMS3JCR.js";
|
|
43
|
+
import "./chunk-DGJ7O55J.js";
|
|
44
|
+
import "./chunk-OO4FKHLO.js";
|
|
45
45
|
import "./chunk-SAH4BD2J.js";
|
|
46
46
|
import "./chunk-XLLKYULU.js";
|
|
47
47
|
import "./chunk-PT4I7NBA.js";
|
|
@@ -51,7 +51,7 @@ import "./chunk-SG7ZP5PG.js";
|
|
|
51
51
|
import "./chunk-K623ENWT.js";
|
|
52
52
|
import "./chunk-AQ37AY7B.js";
|
|
53
53
|
import "./chunk-KBXKR5R2.js";
|
|
54
|
-
import "./chunk-
|
|
54
|
+
import "./chunk-I3N2Z25H.js";
|
|
55
55
|
import "./chunk-NAVJD2PQ.js";
|
|
56
56
|
import "./chunk-ROIYNTHJ.js";
|
|
57
57
|
import "./chunk-2B2BF7P7.js";
|
|
@@ -59,28 +59,29 @@ import "./chunk-P3QQPMQA.js";
|
|
|
59
59
|
import "./chunk-YZHRN3CF.js";
|
|
60
60
|
import "./chunk-FAAUPSY2.js";
|
|
61
61
|
import "./chunk-KM73TBQ4.js";
|
|
62
|
-
import "./chunk-MPHPFVKK.js";
|
|
63
62
|
import "./chunk-AXMWHKXA.js";
|
|
64
63
|
import "./chunk-GLCZKT5V.js";
|
|
65
64
|
import "./chunk-DJ2GSRLV.js";
|
|
66
65
|
import "./chunk-QFJ5JHQR.js";
|
|
67
|
-
import "./chunk-
|
|
66
|
+
import "./chunk-KC4WMLZO.js";
|
|
68
67
|
import "./chunk-YRLW2MSX.js";
|
|
69
68
|
import "./chunk-VGC4I5JJ.js";
|
|
69
|
+
import "./chunk-3PWCMXIR.js";
|
|
70
70
|
import "./chunk-CHHABXPE.js";
|
|
71
71
|
import "./chunk-MA2HEDVP.js";
|
|
72
72
|
import "./chunk-XUJNK7Y6.js";
|
|
73
73
|
import "./chunk-6HY6IF3Z.js";
|
|
74
|
-
import "./chunk-
|
|
74
|
+
import "./chunk-QIHHJKXT.js";
|
|
75
75
|
import "./chunk-F6WFNA7U.js";
|
|
76
|
-
import "./chunk-
|
|
76
|
+
import "./chunk-WZAD4ZNJ.js";
|
|
77
77
|
import "./chunk-FPGTNKCP.js";
|
|
78
|
+
import "./chunk-YHC2EYYG.js";
|
|
78
79
|
import "./chunk-CPHEPGAO.js";
|
|
79
|
-
import "./chunk-
|
|
80
|
-
import "./chunk-
|
|
81
|
-
import "./chunk-
|
|
80
|
+
import "./chunk-DVR7XKO5.js";
|
|
81
|
+
import "./chunk-YECDICIO.js";
|
|
82
|
+
import "./chunk-XT4RKHVE.js";
|
|
82
83
|
import "./chunk-HHJLM3WQ.js";
|
|
83
|
-
import "./chunk-
|
|
84
|
+
import "./chunk-D34RDOLP.js";
|
|
84
85
|
import "./chunk-BUQDLC2G.js";
|
|
85
86
|
import "./chunk-VOQXFAY5.js";
|
|
86
87
|
import "./chunk-75DOP5OR.js";
|