feat(core): resolve model modalities from API metadata - #8529
feat(core): resolve model modalities from API metadata#8529DragonnZhang wants to merge 39 commits into
Conversation
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
E2E test report
Environment: macOS, Node.js 22, no sandbox. Windows and Linux runtime behavior were not manually tested. |
🩺 serve daemon A/BBuilt the PR base vs this PR head ✅ No response changes against the PR base across 4 scenario(s). — Qwen Code · serve A/B |
|
Re-run at the new head — gate re-validated, conclusions unchanged. Template looks good ✓ — all sections present, bilingual body, detailed reviewer test plan. Problem: observed, not theoretical. Concrete before/after on 0.20.1: a JPEG attached to Direction: aligned. #8558 sketches the staged approach this PR takes — modalities first via a models.dev snapshot, limits/reasoning/pricing later — and names this PR as the demonstration. The issue also requires the cold-start guarantees implemented here (bundled snapshot, non-blocking startup, disk cache + background refresh). Size: core paths are touched ( Approach: scope still feels right for the first step. Modalities only (no limits, pricing, reasoning flags, or model-list migration), precedence chain explicit config → catalog match → name heuristic → text-only, and every alias path (regional Alibaba endpoints, gateway family borrowing, OpenRouter variant suffixes, Idealab aliases) maps to a real configuration surface in this repo's presets with test coverage. Risk: no elevated risk signals — none of the changed files match the repo's revert-correlated high-risk paths. The PR does touch a sensitive area (model configuration and Moving on to code review. 🔍 中文说明在新 head 上重跑——门禁重新校验,结论不变。 模板完整 ✓ —— 各部分齐全、中英双语、审阅者测试计划详细。 问题:已观测到,非理论性问题。 0.20.1 中有具体 before/after:附加到 方向:对齐。 #8558 规划了本 PR 采用的分阶段方案——先经 models.dev 快照解决模态,limits/reasoning/价格后续——并点名本 PR 作为示范;issue 还要求了这里已实现的冷启动保障。 规模: 触及核心路径:当前 head 约 992 行生产逻辑(573 行 catalog 模块、modelsConfig.ts/modelRegistry.ts 148 行、69 行快照生成脚本、约 202 行分布在 provider-config.ts、useProviderUpdates.ts、workspace-providers-status.ts、config 入口与导出),测试约 2100 行,另有 1 行生成快照和 84 行设计文档。 方案:作为第一步范围仍然合理。 只做模态;优先级链为显式配置 → 目录命中 → 名称启发式 → 纯文本;每条 alias 路径都对应本仓库 preset 中真实存在的配置面且有测试覆盖。 风险: 无升级风险信号——改动文件均未命中与 revert 相关的高风险路径。但 PR 触及敏感区域(模型配置与 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code review (re-run at the new head)Since my last pass, this branch went through five Status of the flagged items:
The runtime flow, for orientation: sequenceDiagram
participant P1 as CLI config load
participant P2 as Catalog loader
participant P3 as Disk cache
participant P4 as models.dev
participant P5 as ModelsConfig and registry
P1->>P2: load catalog
P2->>P3: read cache
P3-->>P2: valid cache used immediately, even when stale
P2-->>P1: catalog resolved without waiting for network
P1->>P5: config captures the catalog reference
P2-)P4: background refresh, also when cache is missing (built-in snapshot covers it)
P4-->>P2: new catalog, strictly validated
P2->>P3: atomic cache write
Note over P2: refreshed data applies to configs created later - existing sessions keep theirs
Files changed (28)
Test evidenceUnattended CI run — no PR code built or executed here; the table quotes the PR's own CI on the reviewed commit:
All three Behavioural evidence: the central claim (unknown model gets catalog modalities, image goes on the wire as 中文说明代码审查(在新 head 上重跑)上次审查以来,该分支经历了五轮 /review 和六个新提交。我在当前 head 重读了代码,逐条核对 Critical 状态。核心结论不变:加载器卫生到位(发布/缓存前严格校验、32 MB 流式上限、10 秒超时、原子写、unref 刷新定时器、失败路径全部非致命);冷启动不等网络;会话内 catalog 引用稳定;显式配置永远优先;只读模态,目录数据不进入请求体/请求头/路由。 标记项状态:R4-7(Critical)已修复——最后一个提交正是该修复(stub OPENAI_API_KEY/OPENAI_BASE_URL),09bf0cf..444faf6 的差异仅此 2 行。R5-17(Critical)在当前 head 仍未解决,我静态复核了该场景而非仅引用 ledger:快照里 openrouter/kilo/merge-gateway 的 qwen/qwen3.7-plus 为 text,image(无 video),而同一文件里 crossmodel/zenmux 的同名模型有 video,Alibaba 各 endpoint 也有;modelRegistry.resolveModelConfig 用 catalogModalities ?? defaultModalities,目录直接胜出、不与固定启发式取并集;normalize() 会剥掉 qwen/ 前缀,所以 PR 前 defaultModalities('qwen/qwen3.7-plus') 命中固定规则得到 {image, video}(有测试钉住),PR 后经 OpenRouter preset(一等 preset、modelsEditable、spec 无模态)用户添加的同名模型解析为 {image: true},视频附件会被替换成"不支持"占位符——对 Qwen 自家旗舰 Plus 家族在受支持配置面上的静默能力回归。两个建议修复(重新生成快照条目 / 目录只增不减地叠加固定启发式)均未出现,也没有测试钉住新行为。这是合并前我希望解决的唯一问题,修复很小。其余 round-5 建议项仍开放、非阻断;我此前提的三个小问题中注释删除已解决,另两个仍是小问题。 测试证据无人值守 CI 运行,未构建或执行 PR 代码;表格引自该 commit 的 PR 自身 CI:三个 pull_request 事件工作流(Qwen Code CI、Serve A/B、SDK Java)全部绿色完成,无 pending;macOS/Windows 单元与 CLI 集成按工作流设计跳过;唯一在跑的 review-pr 是机器人编排任务。 行为证据:核心声明(未知模型获得目录模态、图片以 image_url 上线、冷启动不阻塞 models.dev)已由一位维护者对当前 head 的深度本地验证支撑——736/736 断言,含免 mock 回环抓包 A/B(base 出占位符、PR 发 image_url data URL)、stale-while-revalidate、失败路径、显式优先,以及让 55 个测试变红的变异测试。这是维护者本地运行(咨询性证据,已注明出处,非 CI),未覆盖 R5-17 场景、Windows/Linux 与 /auth 交互 UI;如需 CI 级证据,可用 @qwen-code /verify(作者有写权限,维护者触发即赞助运行)。 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 3/5 — clean review on the merits and a maintainer-verified behavioural proof, but one statically confirmed regression (the round-5 Critical R5-17) is still in this head, and I won't approve over an open Critical in our own review pipeline. Stepping back, this PR has earned a lot of credit: it is exactly the first step #8558 carved out, the core claim now has real wire-level A/B evidence (a maintainer's local deep verification on this exact head — base build emits the unsupported-image placeholder, PR build sends the JPEG as What I weighed hardest this round is narrower than last time — one question: what happens to
What I'm not doing: approving. A core maintainer has approved this head after deep verification, and I want to be explicit that my hold is not a second-guess of that judgment — his report substantiates the central claim thoroughly. It just didn't cover this scenario, and the round-5 ⏸️ Deferring to @wenshao (you approved this head — was R5-17 part of that call, or should @DragonnZhang land the additive-only fix first?) — that is the only open question; everything else in this PR is in good shape. 中文说明置信度:3/5 —— 就代码本身而言审查干净,行为声明也有维护者验证支撑,但当前 head 仍存在一处静态确认的回归(round-5 的 Critical R5-17),在我们自己的审查流程中还有未关闭的 Critical 时我不会批准。 退一步看,这个 PR 已经赢得了足够的信任:它正是 #8558 规划的第一步;核心声明现在有线级 A/B 证据(维护者对当前 head 的本地深度验证——base 构建出"不支持图片"占位符,PR 构建把 JPEG 以 image_url 发出);CI 全绿;五轮 review 明显加固了加载器。 这一轮我最慎重权衡的问题只有一个:qwen/qwen3.7-plus 经 OpenRouter 会怎样? PR 前固定启发式给该模型 {image, video}(有测试钉住);PR 后目录条目(内置快照里 openrouter/kilo/merge-gateway 下为 text,image)直接胜出,video 消失——经一等 preset 附加视频给 Qwen 自家旗舰 Plus 家族会被静默替换成"不支持"占位符。最后一个提交修复了另一个 Critical(测试 hermeticity),但没有修这个;round 5 以来的差异就是那 2 行测试修复。两个建议修复(重新生成快照条目 / 目录对固定启发式只增不减)均未出现。这既是代码问题也是产品判断:models.dev 的按网关数据是否权威(接受回归、改固定启发式),还是目录只应在仓库固定能力之上叠加(在应用点做个小并集)?上游数据本身不一致——同一模型 id 在同一快照的另外四个网关里有 video——倾向只增不减,但这应由维护者和作者决定。 我不做批准。一位核心维护者已深度验证后批准了当前 head,我要明确说明:我的保留不是对那个判断的质疑——他的报告充分证实了核心声明,只是没有覆盖这个场景;round-5 引用 R5-17 的 CHANGES_REQUESTED 审查仍然挂在 PR 上(我刻意不再叠加一个 request-changes,门禁已生效)。合并需要以下之一:只增不减修复或快照重新生成,或维护者明确关闭 R5-17 并接受该取舍。 ⏸️ 转交 @wenshao(您批准了当前 head——R5-17 是否在该判断之内,还是应由 @DragonnZhang 先落地只增不减修复?)这是唯一未决问题;PR 的其余部分状态良好。 — Qwen Code · qwen3.8-max Reviewed at |
|
Qwen Code review timed out. Qwen review timed out after 21600 seconds (of the 360-minute budget). This run already used the maximum 360 minute timeout. See workflow logs. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: chunk 2 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line not reviewable via line-paged reads; validated programmatically instead (JSON structure, model shapes, duplicate-api groups, spot-checked entries) — a line there exceeds the read limit. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally. Not reviewed: reverse audit — stopped before round 4 by the review time budget. Test Plan (not a blocker): 286 passing — this review observed 17102, 19149 passed.
中文说明
已审查。 建议见行内评论。 未审查:chunk 2 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line not reviewable via line-paged reads; validated programmatically instead (JSON structure, model shapes, duplicate-api groups, spot-checked entries)——其中有一行超出单次读取上限。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。 未审查:反向审计——评审时间预算不足,未能开始第 4 轮。 Test Plan(非阻断):286 passing — this review observed 17102, 19149 passed。
— qwen3.8-max via Qwen Code /review (v0.21.6)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, zero duplicate keys, shape/vocabulary audit, endpoint/env cross-check against every CLI preset, blob-hash equivalence with the diff line).
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: reverse audit — stopped at the 5-round cap: round 5 reported candidates (verified in the cap tail), so the two-consecutive-dry convergence certificate was not established.
Not explored to full depth (tool budget reached): "You are reviewing PR #8529 (QwenLM/qwen-code) — model…": none — the walk finished inside budget.; "You are reviewing PR #8529 (QwenLM/qwen-code) — model…": none — all checks above were completed within budget.; chunk 5: did not execute packages/core/src/models/model-metadata-catalog.test.ts — the review worktree and parent checkout have no node_modules , and a full monorepo …; "You are reviewing PR #8529 (QwenLM/qwen-code) — model…": none — all checks I intended completed within budget.; "You are reviewing PR #8529 (QwenLM/qwen-code) — model…": none — all planned checks completed within budget., and 18 more.
Test Plan (not a blocker): 286 passing — this review observed 19583, 20006, 1364, 1539, 494, 3502, 559 passed.
[Critical] R9-9 / R6-1 系列(重新核查结论:仍然成立):内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态——例如 openrouter/kilo/merge-gateway 下 qwen/qwen3.7-plus 缺少 video,alibaba/alibaba-cn 下 qwen-vl/qwen3-vl 系列缺少 video——ModelRegistry.resolveModelConfig 的 catalog 优先于启发式的优先级(modelRegistry.ts:354 附近)会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit 上复验:快照内容与既有条目一致(qwen/qwen3.7-plus 在 openrouter 下仍为 ["text","image"])。作者已把设计决策(全局加法并集 vs 文档化的精确 catalog 优先级)升级给维护者,维护者深度验证(issue comment 5219981685)给出了 merge-ready 的建议性结论但未对该并集问题明确裁决;第 16 轮审查也以此状态留待确认。该缺陷的实质在被审 commit 上依然存在,等待维护者对设计方向的明确裁决。已在 modelRegistry.ts:354 的既有线程(评论 3747085928 / 3747085939)中跟踪,不重复锚定。
English: R9-9 / R6-1 family (re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit: the snapshot still carries the thin entries. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep-verification (issue comment 5219981685) posted an advisory merge-ready verdict without an explicit ruling on the union question, and round 16 left this unresolved. The mechanism is verified present at this commit, awaiting an explicit maintainer design ruling. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, zero duplicate keys, shape/vocabulary audit, endpoint/env cross-check against every CLI preset, blob-hash equivalence with the diff line)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:reverse audit — stopped at the 5-round cap: round 5 reported candidates (verified in the cap tail), so the two-consecutive-dry convergence certificate was not established。
未探索到全部深度(达到工具调用预算):"You are reviewing PR #8529 (QwenLM/qwen-code) — model…":none — the walk finished inside budget.;"You are reviewing PR #8529 (QwenLM/qwen-code) — model…":none — all checks above were completed within budget.;chunk 5:did not execute packages/core/src/models/model-metadata-catalog.test.ts — the review worktree and parent checkout have no node_modules , and a full monorepo …;"You are reviewing PR #8529 (QwenLM/qwen-code) — model…":none — all checks I intended completed within budget.;"You are reviewing PR #8529 (QwenLM/qwen-code) — model…":none — all planned checks completed within budget.,另有 18 条。
Test Plan(非阻断):286 passing — this review observed 19583, 20006, 1364, 1539, 494, 3502, 559 passed。
[Critical] R9-9 / R6-1 系列(重新核查结论:仍然成立):内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态——例如 openrouter/kilo/merge-gateway 下 qwen/qwen3.7-plus 缺少 video,alibaba/alibaba-cn 下 qwen-vl/qwen3-vl 系列缺少 video——ModelRegistry.resolveModelConfig 的 catalog 优先于启发式的优先级(modelRegistry.ts:354 附近)会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit 上复验:快照内容与既有条目一致(qwen/qwen3.7-plus 在 openrouter 下仍为 ["text","image"])。作者已把设计决策(全局加法并集 vs 文档化的精确 catalog 优先级)升级给维护者,维护者深度验证(issue comment 5219981685)给出了 merge-ready 的建议性结论但未对该并集问题明确裁决;第 16 轮审查也以此状态留待确认。该缺陷的实质在被审 commit 上依然存在,等待维护者对设计方向的明确裁决。已在 modelRegistry.ts:354 的既有线程(评论 3747085928 / 3747085939)中跟踪,不重复锚定。
English: R9-9 / R6-1 family (re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit: the snapshot still carries the thin entries. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep-verification (issue comment 5219981685) posted an advisory merge-ready verdict without an explicit ruling on the union question, and round 16 left this unresolved. The mechanism is verified present at this commit, awaiting an explicit maintainer design ruling. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored.
— qwen3.8-max via Qwen Code /review (v0.21.11)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, 180 providers / 6,056 models, zero duplicate keys, shape audit against parseCatalog rules, generator byte-parity with the diff line, upstream-fidelity comparison) — a line there exceeds the read limit.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Test Plan (not a blocker): 286 passing — this review observed 19583, 20007, 1364, 1539, 494, 3502, 559 passed.
[Critical] R17-4 [Critical] R9-9 / R6-1 family (round-18 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 614d330: the committed snapshot still carries the thin entries (alibaba/alibaba-cn qwen-vl-max = ["text","image"] vs the pinned /^qwen-vl-/ → {image, video}; openrouter qwen/qwen3.7-plus = ["text","image"] vs the pinned /^qwen3.7-plus/ → {image, video}) and the precedence line is unchanged. New round-18 evidence folded into this class (independently found by two reverse auditors): the coding-plan CN snapshot/template contradiction — the template pins {image, video} for qwen3.5-plus/kimi-k2.5 and the new coding-plan test asserts it, but the bundled alibaba-coding-plan-cn entry lists both as ["text","image"], and omitModelSpecModalities strips the template copy from persisted installs, so the CN catalog entry becomes the runtime decider where pre-PR the persisted template value won — video is silently lost for China-region installs. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep-verification posted an advisory merge-ready verdict without an explicit ruling on the union question, and rounds 16-17 left this unresolved. The mechanism is verified present at this commit, awaiting an explicit maintainer design ruling. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored. | 中文:R9-9 / R6-1 系列(第 18 轮复核结论:仍然成立)。内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态,ModelRegistry.resolveModelConfig 的 catalog 优先于启发式的优先级会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit 614d330 上复验。第 18 轮新证据(两位反向审计独立发现)并入本类:coding-plan 中国区快照/模板矛盾——模板固定 {image, video},而内置 alibaba-coding-plan-cn 条目为 ["text","image"],且 omitModelSpecModalities 从持久化安装中剥离模板副本,使中国区 catalog 条目成为运行时裁决者,中国区安装静默丢失 video。作者已把设计决策升级给维护者;维护者深度验证给出了 merge-ready 的建议性结论但未对并集问题明确裁决。等待维护者的明确设计裁决。
中文说明
仅完成部分审查,审查缺口已披露。
未审查:chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, 180 providers / 6,056 models, zero duplicate keys, shape audit against parseCatalog rules, generator byte-parity with the diff line, upstream-fidelity comparison)——其中有一行超出单次读取上限。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
Test Plan(非阻断):286 passing — this review observed 19583, 20007, 1364, 1539, 494, 3502, 559 passed。
[Critical] R17-4 [Critical] R9-9 / R6-1 family (round-18 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 614d330: the committed snapshot still carries the thin entries (alibaba/alibaba-cn qwen-vl-max = ["text","image"] vs the pinned /^qwen-vl-/ → {image, video}; openrouter qwen/qwen3.7-plus = ["text","image"] vs the pinned /^qwen3.7-plus/ → {image, video}) and the precedence line is unchanged. New round-18 evidence folded into this class (independently found by two reverse auditors): the coding-plan CN snapshot/template contradiction — the template pins {image, video} for qwen3.5-plus/kimi-k2.5 and the new coding-plan test asserts it, but the bundled alibaba-coding-plan-cn entry lists both as ["text","image"], and omitModelSpecModalities strips the template copy from persisted installs, so the CN catalog entry becomes the runtime decider where pre-PR the persisted template value won — video is silently lost for China-region installs. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep-verification posted an advisory merge-ready verdict without an explicit ruling on the union question, and rounds 16-17 left this unresolved. The mechanism is verified present at this commit, awaiting an explicit maintainer design ruling. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored. | 中文:R9-9 / R6-1 系列(第 18 轮复核结论:仍然成立)。内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态,ModelRegistry.resolveModelConfig 的 catalog 优先于启发式的优先级会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit 614d330 上复验。第 18 轮新证据(两位反向审计独立发现)并入本类:coding-plan 中国区快照/模板矛盾——模板固定 {image, video},而内置 alibaba-coding-plan-cn 条目为 ["text","image"],且 omitModelSpecModalities 从持久化安装中剥离模板副本,使中国区 catalog 条目成为运行时裁决者,中国区安装静默丢失 video。作者已把设计决策升级给维护者;维护者深度验证给出了 merge-ready 的建议性结论但未对并集问题明确裁决。等待维护者的明确设计裁决。
— qwen3.8-max via Qwen Code /review (v0.21.12)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, 180 providers / 6,056 models, zero duplicate keys, shape audit, heuristic cross-check) — a line there exceeds the read limit.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (workspace unit builds/tests ran; all test failures measured pre-existing via base-tree delta).
Not explored to full depth (tool budget reached): "agent reverse-audit (round 4)": none — no check was cut short by the tool ceiling.; "agent reverse-audit (round 2)": none — no check was cut short..
Test Plan (not a blocker): 286 passing — this review observed 19583, 1364, 1539, 494, 3502, 559 passed.
[Critical] R17-4 (R9-9 / R6-1 family; round-19 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit aeb4c42 (programmatic sweep of the committed snapshot; the precedence line is unchanged; live models.dev carries the same gaps, so the hourly background refresh never corrects them). Round 18 folded the coding-plan CN snapshot/template contradiction into this class (template pins {image, video} for qwen3.5-plus/kimi-k2.5 while the bundled alibaba-coding-plan-cn entry is ["text","image"] and omitModelSpecModalities strips the template copy from persisted installs); that contradiction also remains. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification (issue comment 5219981685) posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored inline. 中文说明:R9-9 / R6-1 系列(第 19 轮复核结论:仍然成立)。内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态,catalog 优先于启发式的优先级会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit aeb4c42 上复验。作者已把设计决策(全局加法并集 vs 文档化的精确 catalog 优先级)升级给维护者;维护者深度验证给出了 merge-ready 的建议性结论但未对并集问题明确裁决。等待维护者的明确设计裁决;若接受文档化的精确 catalog 优先级,请明确说明,本类即可关闭。
中文说明
仅完成部分审查,审查缺口已披露。
未审查:chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — single 255,602-char line exceeds the read limit; audited programmatically instead (JSON validity, 180 providers / 6,056 models, zero duplicate keys, shape audit, heuristic cross-check)——其中有一行超出单次读取上限。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (workspace unit builds/tests ran; all test failures measured pre-existing via base-tree delta)。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 4)":none — no check was cut short by the tool ceiling.;"agent reverse-audit (round 2)":none — no check was cut short.。
Test Plan(非阻断):286 passing — this review observed 19583, 1364, 1539, 494, 3502, 559 passed。
[Critical] R17-4 (R9-9 / R6-1 family; round-19 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (near modelRegistry.ts:354) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit aeb4c42 (programmatic sweep of the committed snapshot; the precedence line is unchanged; live models.dev carries the same gaps, so the hourly background refresh never corrects them). Round 18 folded the coding-plan CN snapshot/template contradiction into this class (template pins {image, video} for qwen3.5-plus/kimi-k2.5 while the bundled alibaba-coding-plan-cn entry is ["text","image"] and omitModelSpecModalities strips the template copy from persisted installs); that contradiction also remains. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification (issue comment 5219981685) posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354 (comments 3747085928 / 3747085939); not re-anchored inline. 中文说明:R9-9 / R6-1 系列(第 19 轮复核结论:仍然成立)。内置快照仍有约 671 个条目(约 93 个 provider)所列模态少于仓库固定启发式授予的模态,catalog 优先于启发式的优先级会应用这些变薄的条目,附件在 PR 之前可以正常发送的位置被替换为占位文本。已在被审 commit aeb4c42 上复验。作者已把设计决策(全局加法并集 vs 文档化的精确 catalog 优先级)升级给维护者;维护者深度验证给出了 merge-ready 的建议性结论但未对并集问题明确裁决。等待维护者的明确设计裁决;若接受文档化的精确 catalog 优先级,请明确说明,本类即可关闭。
— qwen3.8-max via Qwen Code /review (v0.21.12)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: chunk 3 (packages/core/src/models/generated/models-dev-modalities.json) — a line there exceeds the read limit.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (workspace unit builds/tests ran green; all observed unit failures measured pre-existing via base-tree + test-delta).
Not explored to full depth (tool budget reached): chunk 5: none.All 79 tests pass. I've completed my review of this chunk. Here is my report..
Test Plan (not a blocker): 286 passing — this review observed 19583, 20010, 1364, 1539, 494, 3502, 559 passed.
[Critical] R19-3 (R17-4 / R9-9 / R6-1 family; round-20 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway while sibling qwen/qwen3.7-flash and first-party alibaba qwen3.7-plus carry it; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn loses video; abacus/claude-opus-4-8 loses pdf vs the pinned /^claude-/ -> {image, pdf}), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 7c3860d by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671/93 census reproduced) and by a real ModelRegistry probe (qwen-vl-max resolves {image} source 'catalog' where the pre-PR heuristic gave {image, video}; openrouter qwen/qwen3.7-plus likewise); live models.dev (fetched 2026-08-15) carries the same gaps, so the hourly background refresh never corrects them. The coding-plan CN contradiction folded into this class in round 18 also remains: omitModelSpecModalities strips the curated {image, video} from persisted installs while the bundled alibaba-coding-plan-cn entries for qwen3.5-plus/kimi-k2.5 are ['text','image'], so China-region fresh installs lose video via the same precedence. Round-19 ledger rulings: R19-1 (prototype-backed bundled snapshot) and R19-2 (settings-sourced endpoints excluded from the seeding gate) are fixed at this head — null-prototype top-level copy with a real-snapshot regression, and provider identity carried out-of-band with catalog re-lookup on auth sync, each with regressions that fail on the previous head. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:359 (comments 3747085928 / 3747085939); not re-anchored inline.
中文说明
仅完成部分审查,审查缺口已披露。
未审查:chunk 3 (packages/core/src/models/generated/models-dev-modalities.json)——其中有一行超出单次读取上限。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (workspace unit builds/tests ran green; all observed unit failures measured pre-existing via base-tree + test-delta)。
未探索到全部深度(达到工具调用预算):chunk 5:none.All 79 tests pass. I've completed my review of this chunk. Here is my report.。
Test Plan(非阻断):286 passing — this review observed 19583, 20010, 1364, 1539, 494, 3502, 559 passed。
[Critical] R19-3 (R17-4 / R9-9 / R6-1 family; round-20 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics for ~671 entries across ~93 providers (e.g. qwen/qwen3.7-plus loses video under openrouter/kilo/merge-gateway while sibling qwen/qwen3.7-flash and first-party alibaba qwen3.7-plus carry it; the qwen-vl/qwen3-vl family under alibaba/alibaba-cn loses video; abacus/claude-opus-4-8 loses pdf vs the pinned /^claude-/ -> {image, pdf}), and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 7c3860d by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671/93 census reproduced) and by a real ModelRegistry probe (qwen-vl-max resolves {image} source 'catalog' where the pre-PR heuristic gave {image, video}; openrouter qwen/qwen3.7-plus likewise); live models.dev (fetched 2026-08-15) carries the same gaps, so the hourly background refresh never corrects them. The coding-plan CN contradiction folded into this class in round 18 also remains: omitModelSpecModalities strips the curated {image, video} from persisted installs while the bundled alibaba-coding-plan-cn entries for qwen3.5-plus/kimi-k2.5 are ['text','image'], so China-region fresh installs lose video via the same precedence. Round-19 ledger rulings: R19-1 (prototype-backed bundled snapshot) and R19-2 (settings-sourced endpoints excluded from the seeding gate) are fixed at this head — null-prototype top-level copy with a real-snapshot regression, and provider identity carried out-of-band with catalog re-lookup on auth sync, each with regressions that fail on the previous head. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:359 (comments 3747085928 / 3747085939); not re-anchored inline.
— qwen3.8-max via Qwen Code /review (v0.21.12)
|
🔓 Takeover auto-released: the autofix loop paused on this PR 4 day(s) ago (🤖 AutoFix stopped: this counting window now contains 3 time-budget exhaustions (pushed rounds in between included; this ) and no re-arm followed, so the 中文说明🔓 已自动释放接管:autofix 循环在 4 天前暂停于此 PR(🤖 AutoFix stopped: this counting window now contains 3 time-budget exhaustions (pushed rounds in between included; this ),此后无人重新武装,现移除 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R21-11 residue heuristic re-persisting old-spec modalities on every update — already reported (threads 3729250165 / 3735217288, author acknowledged and deferred under late-round policy)
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not explored to full depth (tool budget reached): chunk 6: none — no checks were cut short.; chunk 5: executing model-metadata-catalog.test.ts to confirm the traced paths (worktree lacks packages/core/dist ; the vitest guard requires a full repo build first) ….
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 3.
Deferred under the convergence posture (round 21, not a blocker) — recorded, not requested in this round:
packages/core/src/models/model-metadata-catalog.test.ts:919 — [probe] no test replays the motivating thinkingmachines/inkling failure shape against the bundled snapshotdocs/design/model-modality-catalog.md:38 — [review] design doc contradicts shipped template behavior (templates keep modalities; only new installs omit them)packages/core/src/models/modelsConfig.ts:170 — [probe] the initialDefaultBaseUrl seeding gate's env disjunct is mutation-invisiblepackages/core/src/models/modelsConfig.ts:839 — [probe] updateCredentials with a model but no baseUrl clears endpoint evidence before catalog re-derivationpackages/cli/src/serve/workspace-providers-status.ts:91 — [review] the four env-proxy disjuncts feeding proxyUrl are mutation-invisiblepackages/core/src/models/model-metadata-catalog.ts:82 — [probe] builtInCatalog bypasses parseCatalog validation while the generator copies upstream arrays verbatimpackages/core/src/models/model-metadata-catalog.ts:145 — [probe] empty modality arrays pass validation and {} suppresses the heuristic as authoritativepackages/core/src/models/model-metadata-catalog.test.ts:355 — [probe] normalizeUrl's case-folding is mutation-invisible in the endpoint tablepackages/core/src/models/model-metadata-catalog.ts:536 — [review] R20-1 still stands — Idealab alias rewrites are mutually exclusive (if/else-if), a combined bailian/-dogfooding id misses the catalogpackages/core/src/models/model-metadata-catalog.test.ts:1066 — [review] R20-2 still stands — streamed-body size-cap test enqueues the body as a single chunkscripts/tests/generate-model-modality-snapshot.test.js:11 — [review] R20-3 still stands — no test pins buildSnapshot's deterministic sorted outputscripts/tests/generate-model-modality-snapshot.test.js:22 — [review] R20-5 still stands — no test pins buildSnapshot's filtering of malformed api/env fields
[Critical] R20-4 (R19-3 / R17-4 / R9-9 / R6-1 family; round-21 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 0682ac3 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671/93 census reproduced) and spot checks (openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ → {image, video}; alibaba qwen-vl-max = ['text','image'] vs the pinned /^qwen-vl-/ → {image, video}; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec and the intl sibling carry video); live models.dev carries the same gaps, so the hourly background refresh never corrects them. Round-21 evidence folded into this class (independently rediscovered by three reverse auditors): the coding-plan CN contradiction — omitModelSpecModalities strips the curated {image, video} from persisted installs while the bundled alibaba-coding-plan-cn entries for qwen3.5-plus/kimi-k2.5 are ['text','image'], so China-region fresh installs lose video via the same precedence. Round-21 ledger rulings: R20-1 (Idealab else-if alias) still stands; R20-2/R20-3/R20-5 (test-hardening) still stand and are deferred under the late-round posture. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:359 (comments 3747085928 / 3747085939); not re-anchored inline.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未探索到全部深度(达到工具调用预算):chunk 6:none — no checks were cut short.;chunk 5:executing model-metadata-catalog.test.ts to confirm the traced paths (worktree lacks packages/core/dist ; the vitest guard requires a full repo build first) …。
未审查:反向审计——在 3 轮的反审轮数上限内未收敛。
收敛姿态下延后(第 21 轮,非阻断)——已记录,本轮不要求修改:共 12 条(原文未翻译,列表见上方英文部分)。
[Critical] R20-4 (R19-3 / R17-4 / R9-9 / R6-1 family; round-21 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 0682ac3 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671/93 census reproduced) and spot checks (openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ → {image, video}; alibaba qwen-vl-max = ['text','image'] vs the pinned /^qwen-vl-/ → {image, video}; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec and the intl sibling carry video); live models.dev carries the same gaps, so the hourly background refresh never corrects them. Round-21 evidence folded into this class (independently rediscovered by three reverse auditors): the coding-plan CN contradiction — omitModelSpecModalities strips the curated {image, video} from persisted installs while the bundled alibaba-coding-plan-cn entries for qwen3.5-plus/kimi-k2.5 are ['text','image'], so China-region fresh installs lose video via the same precedence. Round-21 ledger rulings: R20-1 (Idealab else-if alias) still stands; R20-2/R20-3/R20-5 (test-hardening) still stand and are deferred under the late-round posture. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification posted an advisory merge-ready verdict without an explicit ruling on the union question. Disposition requires an explicit maintainer design ruling; if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:359 (comments 3747085928 / 3747085939); not re-anchored inline.
— qwen3.8-max via Qwen Code /review (v0.21.13)
- Ignore the Qwen OAuth placeholder baseUrl as provider evidence in the catalog lookup so it no longer suppresses the 'qwen-oauth' → 'alibaba' protocol fallback; catalog modalities now resolve for real Qwen OAuth sessions on both the registry and raw setModel paths. - Source the serve workspace-status test-runner gate from the daemon's own process env, mirroring the CLI path, so a project .env carrying NODE_ENV=test no longer silently disables the catalog for production serve status while live sessions keep it. - Align raw setModel with registry switchModel: a modalities value without a recorded source is caller-declared, not model-derived, so both switch paths preserve it.
|
The three round-21 Criticals are fixed in R21-6 — the Qwen OAuth placeholder no longer counts as provider evidence
R21-10 — test-runner detection now reads the daemon's own process envThe serve-status gate reads R21-15 — raw
|
# Conflicts: # packages/core/src/providers/__tests__/presets/alibaba-token-plan.test.ts
The workspace providers status builder gates the model metadata catalog fetch on the daemon's own process env, mirroring the CLI path, so a project .env carrying NODE_ENV=test cannot disable the catalog for one workspace while live sessions keep it. Register that process-scoped access in the serve process.env guard allowlist with its reason.
Review round 21 wrap-up: 3 Criticals closed, 37 Suggestions deferredPer Criticals — all 3 closed and resolvedRe-counted with pagination: 148 review threads, 40 unresolved → 3 Critical + 37 Suggestion. All 3 Criticals were already addressed at HEAD (
Verification at Suggestions — 37 deferredGrouped by file, with round tags (
Two worth naming explicitly, because both still stand at
Both are genuine tightenings of test strictness and are good follow-up material; neither is a correctness defect in shipped code, so both are deferred rather than fixed here. The bulk of the remaining 37 are docs/naming polish, extra test pins for already-covered behavior, and refactor suggestions on the catalog lookup helper. They are left unresolved on purpose so they stay visible for a follow-up PR.
中文说明依据 分页重新统计:148 条 review thread,40 条未解决 → 3 条 Critical + 37 条 Suggestion。3 条 Critical 在 HEAD ( 37 条 Suggestion 按文件分组如上表,全部延后到后续 PR,并特意保持未解决状态以便追踪。其中两条点名说明: |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
3 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- useProviderUpdates residue heuristic re-persisting future-spec-changed modalities — already reported as R21-11 (threads 3729250165 / 3735217288), author acknowledged and deferred under the late-round policy
- initialDefaultBaseUrl env-disjunct mutation-invisible test gap — already reported and deferred in round 21 (modelsConfig.ts:170)
- updateCredentials model re-derivation path lacks a discriminating test — already reported as R4-5 (comments 3735217274 / 3736862040), author acknowledged
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings).
Test Plan (not a blocker): 286 passing — this review observed 21953, 20676, 1590, 1598, 494, 3784, 540 passed.
Deferred under the convergence posture (round 22, not a blocker) — recorded, not requested in this round:
packages/core/src/models/model-metadata-catalog.ts:33 — [review] Qwen OAuth placeholder sentinel triplicated as bare literals (catalog constant, modelRegistry.ts:105 stamping side, test pin at model-metadata-catalog.test.ts:286) — consolida…scripts/generate-model-modality-snapshot.js:33 — [probe] 6,028 dead 'text' tokens ≈ 24.6% of the committed snapshot / ~39KB of the shipped bundle — filter at generation, keep empty arrays as text-only evidencescripts/generate-model-modality-snapshot.js:57 — [review] snapshot generator fetch has no timeout while the runtime fetch of the identical URL caps at 10s — add AbortSignal.timeoutscripts/generate-model-modality-snapshot.js:67 — [probe] main() guard string-compares a symlinked argv[1] against a realpath — silent no-op with exit 0 under symlinked invocation paths; compare realpaths
[Critical] R21-1 [Critical] R20-4 (R19-3 / R17-4 / R9-9 / R6-1 family; round-22 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit bba7c72 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671 entries / 93 providers reproduced) and spot checks (openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ → {image, video}; alibaba qwen-vl-max = ['text','image'] vs the pinned /^qwen-vl-/ → {image, video}; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec and the intl sibling carry video); live models.dev carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 3 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings)。
Test Plan(非阻断):286 passing — this review observed 21953, 20676, 1590, 1598, 494, 3784, 540 passed。
收敛姿态下延后(第 22 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
[Critical] R21-1 [Critical] R20-4 (R19-3 / R17-4 / R9-9 / R6-1 family; round-22 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit bba7c72 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671 entries / 93 providers reproduced) and spot checks (openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ → {image, video}; alibaba qwen-vl-max = ['text','image'] vs the pinned /^qwen-vl-/ → {image, video}; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec and the intl sibling carry video); live models.dev carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline.
— qwen3.8-max via Qwen Code /review (v0.21.14)
…st-runner gate - R22-1: replace the hand-maintained OPENROUTER_VARIANT_SUFFIX enumeration with a generic base-id fallback after the last ':'. The list missed :batch — the most common OpenRouter variant — so those models resolved text-only and image/pdf attachments were placeholder-replaced. Exact match stays first so explicit variant entries still win. - R22-4: evaluate the daemon-side test-runner gate against a boot-time env snapshot instead of live process.env. Sibling loadSettings -> loadEnvironment calls merge a trusted workspace's .env into the daemon process.env, so a workspace carrying NODE_ENV=test would otherwise disable the catalog daemon-wide for the process's lifetime. createServeApp passes the frozen daemonEnvAtBoot; a processEnv option allows injection.
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- useProviderUpdates residue heuristic re-persisting future-spec-changed modalities (useProviderUpdates.ts:292) — already reported as R21-11 (threads 3729250165 / 3735217288), author acknowledged and deferred under the late-round policy
- snapshot generator fetch has no proxy handling and no timeout (scripts/generate-model-modality-snapshot.js:57) — already reported (comment 3757556422) and deferred in round 22
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings).
Test Plan (not a blocker): 286 passing — this review observed 20677, 1590, 1598, 494, 3784, 540 passed.
Deferred under the convergence posture (round 23, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/workspace-providers-status.ts:116 — [review] proxy || chain treats an explicitly-empty HTTPS_PROXY="" as absent, contradicting the readProxyVar ?? convention in the same directorypackages/cli/src/serve/server.ts:1139 — [review] the only production wiring of the boot-time gate (processEnv: daemonEnvAtBoot) has no test gating — deleting it keeps every test greenpackages/cli/src/serve/workspace-providers-status.ts:119 — [review] of the five proxy sources in the new chain only settings.proxy is tested; the four env-var fallbacks and their precedence are untesteddocs/design/model-modality-catalog.md:67 (+2 locations) — [review] design doc contradicts shipped behavior: unknown OpenRouter suffixes 'remain unmatched' vs any-suffix base fallback; idealab family enumeration omits GLM while the code rout…
[Critical] R21-1 (R20-4 / R19-3 / R17-4 / R9-9 / R7-1 / R6-1 family; round-23 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit dce920e by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (~671 entries / ~93 providers reproduced) and spot checks: openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ -> {image, video}; the qwen-vl family (12 entries across alibaba/alibaba-cn) loses video vs pinned /^qwen-vl-/ and /^qwen3-vl-/; abacus claude-opus-4-8 loses pdf vs the pinned {image, pdf}; 302ai gemini-2.5-flash is thin vs pinned FULL_MULTIMODAL; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec, the intl sibling entry and the heuristics all carry video (reverse-audit rounds 1-2 independently re-executed this instance through getCatalogModalities: CN endpoint -> {"image":true}, intl endpoint -> {"image":true,"video":true}; fresh Coding Plan installs on the default China endpoint therefore lose video input while the template still advertises it). No additive-union logic exists anywhere in packages/core/src/models, and live models.dev carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline. Witness: sweep of committed snapshot at dce920e, real defaultModalities as oracle — ~671 thin entries / ~93 providers; probes quoted above.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings)。
Test Plan(非阻断):286 passing — this review observed 20677, 1590, 1598, 494, 3784, 540 passed。
收敛姿态下延后(第 23 轮,非阻断)——已记录,本轮不要求修改:共 4 条(原文未翻译,列表见上方英文部分)。
[Critical] R21-1 (R20-4 / R19-3 / R17-4 / R9-9 / R7-1 / R6-1 family; round-23 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit dce920e by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (~671 entries / ~93 providers reproduced) and spot checks: openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ -> {image, video}; the qwen-vl family (12 entries across alibaba/alibaba-cn) loses video vs pinned /^qwen-vl-/ and /^qwen3-vl-/; abacus claude-opus-4-8 loses pdf vs the pinned {image, pdf}; 302ai gemini-2.5-flash is thin vs pinned FULL_MULTIMODAL; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while the preset spec, the intl sibling entry and the heuristics all carry video (reverse-audit rounds 1-2 independently re-executed this instance through getCatalogModalities: CN endpoint -> {"image":true}, intl endpoint -> {"image":true,"video":true}; fresh Coding Plan installs on the default China endpoint therefore lose video input while the template still advertises it). No additive-union logic exists anywhere in packages/core/src/models, and live models.dev carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline. Witness: sweep of committed snapshot at dce920e, real defaultModalities as oracle — ~671 thin entries / ~93 providers; probes quoted above.
— qwen3.8-max via Qwen Code /review (v0.21.14)
A trusted workspace .env with NODE_ENV=test merged by loadSettings → loadEnvironment flipped the session-side catalog gate after launch, degrading modality resolution for live sessions while the daemon workspace-status path (gated on the boot env) kept the catalog — the status/session mismatch the design doc forbids. Evaluate the config.ts gate against a frozen process-launch snapshot (environment.ts) instead of live process.env, pass the daemon boot env to the runtime-scoped workspace-providers-status providers in run-qwen-serve.ts (mirroring server.ts daemonEnvAtBoot), and drop the process-env-guard allowance entry for workspace-providers-status.ts, whose reads moved onto the injected boot snapshot (the stale entry made the guard suite red).
…me-provider switchModel Catalog modalities resolved only with session-endpoint evidence (a cli/env/settings-sourced baseUrl) were clobbered on any registry switchModel: the lookup resolves against the registry default endpoint, and applyResolvedModelDefaults stamped over both the endpoint and the modalities with no re-apply (unlike syncAfterAuthRefresh). Carry the session endpoint across the switch only when the previous model is a same-provider registry model and the target model has no explicit baseUrl of its own, then re-apply catalog modalities against the carried endpoint — mirroring syncAfterAuthRefresh's savedBaseUrl block. Cross-provider switches and explicit-baseUrl targets keep the R10-12 boundary (a settings endpoint never leaks into a base-url-less registry model of another provider).
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
10 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- residue heuristic re-persisting future-spec-changed modalities (useProviderUpdates.ts:292) — already reported as R21-11 (threads 3729250165 / 3735217288), author acknowledged and deferred under the late-round policy (rediscovered three time…
- proxy || chain treating an explicitly-empty HTTPS_PROXY="" as absent (workspace-providers-status.ts:116) — already deferred in round 23
- test-runner gate predicate duplicated across config.ts and workspace-providers-status.ts — already reported as R8-2 (comment 3749264574), author acknowledged and deferred
- toMatchObject subset-based auth-sync modality assertions (modelsConfig.test.ts) — already reported (comment 3781281363), author deferred
- initialDefaultBaseUrl env-disjunct mutation-invisible test gap (modelsConfig.ts:170) — already deferred in round 21
- design doc contradicts shipped OpenRouter suffix fallback behavior (docs/design/model-modality-catalog.md:67) — already deferred in round 23
- boot-time gate production wiring untested (server.ts:1139) — already deferred in round 23
- updateCredentials model re-derivation path lacks a discriminating test (modelsConfig.ts:898) — already reported as R4-5 (comments 3735217274 / 3736862040), author acknowledged
- clearModelDerived !== undefined vs truthiness guard mismatch (modelsConfig.ts:861) — already reported as R15-7 (comment 3773979887), author acknowledged
- generator guard accepts a degraded-but-non-empty snapshot (scripts/generate-model-modality-snapshot.js:50) — adjacent to already-reported R2-15 (comment 3727157090)
Not reviewed: reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings).
Not explored to full depth (tool budget reached): chunk 2: none — all checks I started were completed (~17 of ~36 tool calls).; chunk 15: none — the cross-chunk consumer ( packages/core/src/models/model-metadata-catalog.ts ) is outside my territory by construction, not a cut short..
Test Plan (not a blocker): 286 passing — this review observed 22287, 20739, 1614, 1616, 494, 3849, 553 passed.
Deferred under the convergence posture (round 24, not a blocker) — recorded, not requested in this round:
packages/core/src/models/model-metadata-catalog.ts:82 — [probe] shallow null-prototype copy of the bundled snapshot leaves nested model maps on Object.prototypepackages/core/src/models/modelsConfig.ts:694 — [review] session-endpoint modalities reconciliation policy duplicated verbatim in switchModel and syncAfterAuthRefreshpackages/core/src/models/model-metadata-catalog.ts:443 — [probe] bundled google-vertex catalog providers are unreachable by resolutionpackages/core/src/models/modelsConfig.test.ts:1956 — [probe] the model.registryBaseUrl === undefined carry-guard clause has no testpackages/cli/src/serve/workspace-providers-status.ts:113 — [review] workspace-scoped proxy fed into the process-global catalog singleton is last-writer-winspackages/core/src/models/model-metadata-catalog.test.ts:110 — [probe] endpoint-match vs unique-envKey precedence is unpinned by any testpackages/core/src/models/modelsConfig.test.ts:1904 — [probe] the !options?.baseUrl clause of shouldCarrySessionEndpoint has no test
[Critical] R21-1 (R20-4 / R19-3 / R17-4 / R9-9 / R7-1 / R6-1 family; round-24 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 07b7ef0 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671 entries / 93 providers reproduced) and spot checks: openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ -> {image, video}; the qwen-vl family loses video vs pinned /^qwen-vl-/; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while this PR's own template (alibaba-coding-plan.ts:25) pins {image, video} and omitModelSpecModalities strips the template copy from new installs — fresh China-region Coding Plan installs lose video input while the /auth template still advertises it. Live models.dev (fetched 2026-08-20) carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification (issue comment 5219981685) was explicitly advisory and did not rule on the union question; triage stage-3 (comment 5188913533) withheld approval citing exactly this regression. Disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline. Witness: sweep of committed snapshot at 07b7ef0, real defaultModalities as oracle — total 6056 | thinner: 671 | providers: 93; live models.dev re-fetch carries the same gaps.
中文说明
仅完成部分审查,审查缺口已披露。
本轮确认的 10 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未审查:reverse audit — reached the round cap of 3 without converging (round 3 still reported new findings)。
未探索到全部深度(达到工具调用预算):chunk 2:none — all checks I started were completed (~17 of ~36 tool calls).;chunk 15:none — the cross-chunk consumer ( packages/core/src/models/model-metadata-catalog.ts ) is outside my territory by construction, not a cut short.。
Test Plan(非阻断):286 passing — this review observed 22287, 20739, 1614, 1616, 494, 3849, 553 passed。
收敛姿态下延后(第 24 轮,非阻断)——已记录,本轮不要求修改:共 7 条(原文未翻译,列表见上方英文部分)。
[Critical] R21-1 (R20-4 / R19-3 / R17-4 / R9-9 / R7-1 / R6-1 family; round-24 re-check verdict: still stands). The bundled snapshot still lists thinner modalities than the repo-pinned heuristics and preset specs for ~671 entries across ~93 providers, and the catalog-over-heuristic precedence in ModelRegistry.resolveModelConfig (modelRegistry.ts:359, catalogModalities ?? defaultModalities(config.id)) applies the thin entries, so attachments are placeholder-replaced where pre-PR they were sent. Re-verified at the reviewed commit 07b7ef0 by a programmatic sweep of the committed snapshot with the real defaultModalities as oracle (671 entries / 93 providers reproduced) and spot checks: openrouter qwen/qwen3.7-plus = ['text','image'] vs the pinned /^qwen3.7-plus/ -> {image, video}; the qwen-vl family loses video vs pinned /^qwen-vl-/; alibaba-coding-plan-cn qwen3.5-plus/kimi-k2.5 = ['text','image'] while this PR's own template (alibaba-coding-plan.ts:25) pins {image, video} and omitModelSpecModalities strips the template copy from new installs — fresh China-region Coding Plan installs lose video input while the /auth template still advertises it. Live models.dev (fetched 2026-08-20) carries the same gaps, so the hourly background refresh never corrects them. The author escalated the design decision (global additive union vs documented exact-catalog precedence) to maintainers; the maintainer deep verification (issue comment 5219981685) was explicitly advisory and did not rule on the union question; triage stage-3 (comment 5188913533) withheld approval citing exactly this regression. Disposition requires an explicit maintainer design ruling — if the documented exact-catalog precedence is accepted as-is, say so explicitly and this class can be closed. Tracked in the existing threads at modelRegistry.ts:354/359 (comments 3747085928 / 3747085939); not re-anchored inline. Witness: sweep of committed snapshot at 07b7ef0, real defaultModalities as oracle — total 6056 | thinner: 671 | providers: 93; live models.dev re-fetch carries the same gaps.
— qwen3.8-max via Qwen Code /review (v0.21.14)
Pull request was converted to draft
What this PR does
This PR resolves missing model input modalities from models.dev for configured models and runtime model switches. It ships a compact modality-only snapshot, uses a valid disk cache immediately, and refreshes remote metadata in the background without making cold startup wait for the network.
Successful refreshes become available to configurations created later in the same long-running process, while existing sessions keep stable capabilities. Explicit user configuration remains authoritative, catalog metadata is used after an exact provider/model match or a narrow documented provider-family alias, and existing model-name heuristics remain the final fallback.
Why it's needed
Model modality support is currently inferred mainly from model names. OpenAI-compatible providers can expose models whose identifiers are unknown to Qwen Code, and their model-list APIs usually do not include normalized input-modality metadata, so valid image attachments can be replaced with an unsupported-image text placeholder.
Using normalized metadata recognizes new models without adding a model-name rule for every provider release. The built-in snapshot and stale-while-revalidate cache also keep first use, offline use, and long-running daemon sessions deterministic when models.dev is slow or unavailable.
Reviewer Test Plan
How to verify
/authprovider-install flow. Confirm both paths apply catalog modalities when no explicit value is present.bailian/DeepSeek, Kimi, MiniMax, and Qwen model families through their official-provider counterparts while unknown families remain unmatched./authtemplate, then refresh auth and switch between registry models. Confirm the explicit value remains unchanged. Confirm newly generated/authentries omit modalities and use catalog metadata instead.Evidence (Before & After)
Before: Qwen Code 0.20.1 treated a JPEG attached to
thinkingmachines/inklingas unsupported and sent only the text placeholder because the model identifier was unknown to the built-in heuristic.After: the source build sent the same JPEG as an
image_url. The focused metadata, registry, configuration, resolver, and workspace-status suite reports 286 passing tests, including a cold-cache request that never resolves, stale refresh success and failure, later same-process loads, manual configuration, runtime provider installation, explicit overrides, and unknown-provider isolation. Repository build and typecheck also complete successfully.Tested on
Environment (optional)
Local Node.js 22 TypeScript runtime without a sandbox, using injected catalog requests and temporary caches for deterministic failure and refresh scenarios, plus a local OpenAI-compatible endpoint for attachment verification.
Risk & Scope
Linked Issues
Related to #8558.
中文说明
这个 PR 做了什么
这个 PR 为已配置模型和运行时模型切换从 models.dev 补全缺失的输入模态。它内置一份仅包含模态信息的精简快照,立即使用有效磁盘缓存,并在后台刷新远端元数据,不让冷启动等待网络。
刷新成功后,同一长生命周期进程里之后创建的配置会使用新数据,而已有会话保持稳定能力。用户显式配置始终具有最高优先级;目录元数据只在精确 provider/model 命中或已登记的窄范围模型家族别名命中后使用;现有基于模型名称的启发式规则继续作为最终兜底。
为什么需要
当前模型模态支持主要通过模型名称推断。OpenAI 兼容提供商可能暴露 Qwen Code 未知标识符的模型,而其模型列表 API 通常不包含统一的输入模态元数据,因此有效图片附件可能被替换为“不支持图片”的文本占位符。
使用统一元数据可以识别新模型,无需为每次 provider 发布都添加一条模型名称规则。内置快照和 stale-while-revalidate 缓存也能在 models.dev 缓慢或不可用时,让首次使用、离线使用和长生命周期 daemon 会话保持确定性。
审阅者测试计划
如何验证
/authprovider 安装流程配置同一个目录模型。确认没有显式值时,两条路径都会应用目录模态。bailian/下的 DeepSeek、Kimi、MiniMax、Qwen 模型家族使用对应官方 provider 的同型号元数据,未知家族保持不匹配。/auth已持久化的模态,再刷新 auth 并切换 registry 模型,确认显式值保持不变;同时确认新版/auth不再写入默认模态,而是交给 catalog 解析。证据(修改前与修改后)
修改前:Qwen Code 0.20.1 会将附加到
thinkingmachines/inkling的 JPEG 视为不支持;由于内置启发式规则不识别该模型标识符,因此只发送文本占位符。修改后:源码构建会将同一张 JPEG 作为
image_url发送。聚焦于元数据、注册表、配置解析和 workspace status 的测试套件共 286 个测试通过,其中包括永不完成请求的冷缓存场景、陈旧缓存刷新成功与失败、同进程后续加载、手动配置、运行时 provider 安装、显式覆盖和未知 provider 隔离。仓库构建和类型检查也都成功完成。测试平台
环境(可选)
本地 Node.js 22 TypeScript 运行时,未启用沙箱;通过注入目录请求和临时缓存稳定复现失败与刷新场景,并通过本地 OpenAI 兼容 endpoint 验证附件行为。
风险与范围
关联 Issue
关联 #8558。