Skip to content

test(integration): assert successful /compress token reduction - #11259

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
yu-xin-c:codex/compress-telemetry-success-gate
Sep 11, 2026
Merged

test(integration): assert successful /compress token reduction#11259
wenshao merged 1 commit into
QwenLM:mainfrom
yu-xin-c:codex/compress-telemetry-success-gate

Conversation

@yu-xin-c

@yu-xin-c yu-xin-c commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

What this PR does

  • Adds a telemetry reader for integration tests that returns the latest event matching an exact event name, together with focused coverage for latest-match and absent-event behavior.
  • Makes the /compress fake-server fixture deterministic by disabling managed auto-memory, so the side-query remains at the expected request index.
  • Strengthens both active /compress end-to-end cases to require an actual token reduction (tokens_before > tokens_after) and reports the compression output-token count and cache-sharing state when the assertion fails.

Why it's needed

The deterministic fake-server coverage added in #11175 currently proves that a chat_compression event landed, but event presence alone does not prove that compression succeeded. The same event contains the result needed to distinguish a genuine reduction from a failed or token-inflating path. Reading the latest exact-name match also prevents a trailing unrelated telemetry record from being mistaken for the target event. This completes the /compress follow-up deferred from #11094 and tracked in #11213.

Reviewer Test Plan

How to verify

  1. Run npx cross-env QWEN_SANDBOX=false vitest run --root ./integration-tests ./test-helper.test.ts; all 9 tests should pass.
  2. Run QWEN_CODE_LANG=en QWEN_SANDBOX=false npx vitest run --root ./integration-tests interactive/context-compress-interactive.test.ts; both active /compress cases should pass and the existing token-inflation case should remain skipped.
  3. Run npm run typecheck:integration, targeted ESLint, and targeted Prettier checks; each should pass.
  4. To validate the guard itself, temporarily remove the exact event-name predicate from the telemetry reader and rerun its focused test. The latest-match test should fail because the trailing api_request fixture has no compression attributes; restoring the predicate makes it pass.

Observed fake-server telemetry was deterministic: the basic case made 2 API requests and reduced tokens from 1825 to 71; the instruction-forwarding case made 2 API requests and reduced tokens from 1832 to 71. Neither case emitted a managed-memory event. Both reported compression_output_token_count=0 and cache_sharing_used=false. The exact tokens_before values vary with the environment (the tracked finding observed 3607), so the assertion intentionally pins the invariant reduction rather than literal counts.

Evidence (Before & After)

N/A (test-infrastructure-only change). Before, the suite accepted any landed chat_compression event. After, it accepts only a matching event whose numeric token counts demonstrate a reduction, with actionable diagnostics on failure.

Tested on

OS Status
macOS Tested
Windows Not tested locally
Linux Not tested locally

Environment (optional)

macOS, Node.js v25.9.0, QWEN_SANDBOX=false, local FakeOpenAIServer fixture.

Risk & Scope

  • Main risk or tradeoff: This intentionally disables managed auto-memory only in the /compress integration suite so background extraction cannot shift fake-server request indexes.
  • Not validated / out of scope: Production compression behavior and the existing skipped token-inflation scenario are unchanged; Windows and Linux were not run locally and remain covered by CI.
  • Breaking changes / migration notes: None. This changes test infrastructure and assertions only.

Linked Issues

Refs #11213

中文说明

本 PR 的改动

  • 为集成测试新增遥测事件读取方法,返回事件名精确匹配的最新事件,并通过聚焦测试覆盖“返回最新匹配项”和“事件不存在”两种行为。
  • /compress 的假服务器测试配置中禁用托管自动记忆,使压缩侧查询稳定地位于预期请求序号。
  • 强化两个启用中的 /compress 端到端用例,要求压缩后确实减少 token(tokens_before > tokens_after);断言失败时同时输出压缩结果 token 数和缓存共享状态。

为什么需要这项改动

#11175 新增的确定性假服务器测试目前只能证明 chat_compression 事件已经写入,但事件存在本身不能证明压缩成功。该事件已经包含区分真实压缩、压缩失败或 token 膨胀所需的结果数据。读取事件名精确匹配的最新记录,也可避免把尾部无关遥测记录误当成目标事件。本 PR 完成了 #11094 延后处理、并由 #11213 跟踪的 /compress 后续工作。

审阅者测试计划

验证方法

  1. 运行 npx cross-env QWEN_SANDBOX=false vitest run --root ./integration-tests ./test-helper.test.ts;9 个测试应全部通过。
  2. 运行 QWEN_CODE_LANG=en QWEN_SANDBOX=false npx vitest run --root ./integration-tests interactive/context-compress-interactive.test.ts;两个启用中的 /compress 用例应通过,现有 token 膨胀用例仍保持跳过。
  3. 运行 npm run typecheck:integration、针对改动文件的 ESLint 和 Prettier 检查;均应通过。
  4. 如需验证保护机制本身,可临时移除遥测读取方法中的精确事件名条件并重新运行其聚焦测试。由于末尾的 api_request 固定数据不含压缩属性,“最新匹配项”测试应失败;恢复条件后测试通过。

实际观察到的假服务器遥测结果保持确定性:基础用例发起 2 次 API 请求,token 从 1825 降至 71;指令转发用例发起 2 次 API 请求,token 从 1832 降至 71。两个用例都未产生托管记忆事件,并且都报告 compression_output_token_count=0cache_sharing_used=false。具体的 tokens_before 数值会随环境变化(跟踪项中观测到的是 3607),因此断言有意固定“压缩后减少”这一不变量,而不写死具体数值。

前后对比证据

不适用(仅改动测试基础设施)。改动前,测试接受任意已写入的 chat_compression 事件;改动后,只有事件名匹配且数值型 token 计数表明确实减少时才会通过,并在失败时提供可操作的诊断信息。

测试平台

操作系统 状态
macOS 已测试
Windows 未在本地测试
Linux 未在本地测试

环境(可选)

macOS、Node.js v25.9.0、QWEN_SANDBOX=false、本地 FakeOpenAIServer 固定测试环境。

风险与范围

  • 主要风险或权衡:仅在 /compress 集成测试套件中有意禁用托管自动记忆,避免后台记忆抽取改变假服务器请求序号。
  • 未验证或范围外事项:生产环境压缩行为及现有跳过的 token 膨胀场景均未改变;Windows 和 Linux 未在本地运行,仍由 CI 覆盖。
  • 破坏性变更或迁移说明:无。本 PR 只修改测试基础设施和断言。

关联 Issue

Refs #11213

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Qwen Triage finished — CI landed green on 083a94b and the deferred approval was posted. finalize run

Qwen Triage 已完成 —— 083a94b 的 CI 全绿,延迟审批已提交。查看 finalize 运行

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR — this one is easy to orient on, because it picks up a piece of work the maintainers explicitly asked someone to redo.

Template looks good ✓ — every required heading is present and filled in with real content rather than placeholders.

Problem: observed and documented, not theoretical. main asserts only that a chat_compression telemetry event landed; event presence does not distinguish a real compression from a failed or token-inflating one. That gap is tracked in #11213, which is itself a deferred review finding from #11094, and the deferral carries measurements rather than speculation. So the premise is verifiable before anyone reads a line of the diff.

Direction: aligned. #11213's first bullet names exactly three things worth keeping from the abandoned half of #11094memory.enableManagedAutoMemory: false re-justified as request-index determinism, readTelemetryEvent plus a token-reduction gate, and a shared settings constant — and this PR is those three things and nothing else. It also correctly does not carry over #11094's two widened timeout constants, which the deferral called inert now that #11175 drives a local fake server instead of a live model. One note on scope: telemetry is normally a direction-escalation area for us, but that applies to changing what gets emitted. This only reads events that already exist from test code, so there is no production telemetry or public-contract surface here to escalate.

Size: not applicable — no core paths are touched. For the record, the split is 12 lines of test-infrastructure code (test-helper.ts) against 116 lines of tests.

Approach: the scope feels right, and I could not find a materially simpler path. Two observations worth putting on the table rather than blocking on:

  • The deferral suggests keeping the reduction gate as a deterministic oracle and quotes 3607 -> 71 as what reproduces on main. Your own runs report 1825 -> 71 and 1832 -> 71. Since tokens_before clearly moves with the environment, the relational tokens_before > tokens_after you chose is the better reading of that instruction — pinning the literal numbers would have been a cross-platform flake source. Worth a one-line mention in the PR body so a future reader does not "fix" it into an exact-match assertion.
  • expectSuccessfulCompression() runs right after the existing foundEvent assertion, so the two overlap when the event is missing entirely. Keeping both is defensible — the first gives the sharper "event never landed" message — but if you want one assertion, the new one alone would carry it.

Risk: no elevated risk signals. None of the changed files match the revert-correlated paths we track, and the only settings change is scoped to this suite's fixture, not to any product default.

Moving on to code review. 🔍

中文说明

感谢贡献 —— 这个 PR 很好定位,因为它接手的正是维护者明确要求重做的那部分工作。

模板完整 ✓ —— 所有必需小节都在,且填写的是真实内容而非占位符。

问题:是已观测且有记录的问题,不是理论性加固。main 目前只断言 chat_compression 遥测事件已写入;而事件存在本身无法区分"真的压缩成功"和"压缩失败或 token 膨胀"。这个缺口由 #11213 跟踪,它本身是 #11094 遗留下来的 review 发现,并且该遗留项附带了实测数据而非猜测。所以在读 diff 之前,前提就是可验证的。

方向:一致。#11213 的第一条明确列出了 #11094 被放弃的那一半中值得保留的三样东西 —— 以"请求序号确定性"重新论证的 memory.enableManagedAutoMemory: falsereadTelemetryEvent 加 token 缩减断言、以及共享的 settings 常量 —— 本 PR 正好是这三样,没有多余内容。它也正确地没有带上 #11094 那两个被放大的超时常量;自 #11175 改用本地假服务器而非真实模型后,遗留项已判定这两个常量失效。关于范围补充一点:遥测通常属于需要我们升级给维护者判断方向的领域,但那条规则针对的是"改变发出了什么"。本 PR 只是从测试代码里读取已存在的事件,因此这里没有需要升级的生产遥测或对外契约面。

规模:不适用 —— 未触及核心路径。作为记录,行数构成是 12 行测试基础设施代码(test-helper.ts)对 116 行测试代码。

方案:范围合理,我没有找到明显更简单的路径。有两点想摆出来讨论,但不构成阻塞:

  • 遗留项建议把缩减断言保留为确定性 oracle,并以 3607 -> 71 作为 main 上可复现的数值。而你的实测是 1825 -> 711832 -> 71。既然 tokens_before 明显随环境变化,你选择的关系型断言 tokens_before > tokens_after 其实是对那条指示更好的解读 —— 写死具体数值会成为跨平台抖动源。建议在 PR 描述里加一句说明,免得后来者把它"修正"成精确值断言。
  • expectSuccessfulCompression() 紧跟在已有的 foundEvent 断言之后,因此在"事件完全缺失"这种情况下两者有重叠。两个都留也说得通 —— 前者能给出更准确的"事件未写入"提示 —— 但如果想只保留一条,新断言本身就足够。

风险:无升级风险信号。改动文件均未命中我们跟踪的与 revert 相关的路径,唯一的 settings 变更也只作用于本套件的测试配置,不影响任何产品默认值。

进入代码审查 🔍

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Code review

No critical blockers and no AGENTS.md violations. The diff does one thing, does it in the idiom the file already uses, and carries no drive-by edits.

Before reading the diff I wrote down what I would do from the title and the "why" alone: add a synchronous telemetry accessor next to the existing last-api-request reader, assert tokens_before > tokens_after after the existing wait so the record has definitely landed, put the distinguishing attributes in the failure message, silence the background memory extractor so the fake server's request indexes stop depending on it, and unit-test the accessor with a fixture whose last record does not match so deleting the name predicate reddens the test. That is what this PR is, item for item — which is the expected result when the tracking issue prescribes the shape, but it does mean I have no simpler alternative to argue for.

The parts I checked rather than assumed:

  • The fixture survives the telemetry parser. _readAndParseTelemetryLog() does not split on newlines — it splits on }\n{ and re-adds the braces by position. Compact single-line JSON.stringify records joined with \n are exactly the shape that reconstruction expects, so all four fixture records parse. I traced the mutant too: drop the event.name predicate and pop() returns the trailing api_request record, so toBe(28891) fails. The fixture genuinely bites, which is what the deferred finding asked for.
  • The shared settings constant is safe to share. setup() builds a fresh object ({telemetry, sandbox, ...options.settings}) and only ever JSON.stringifys it — the caller's object is never mutated, so hoisting one literal across three cases cannot leak state between them. setup() also rm -rfs the test directory first, so neither new helper test can inherit a stale telemetry.log.
  • The memory setting really takes effect. memory.enableManagedAutoMemory is a live schema key (default true) and packages/cli/src/config/config.ts honors it unless bare or safe mode applies, neither of which this rig uses. So the request-index determinism in the new comment is a property of the code, not a hope — and it matches the measurement recorded in Deferred review findings from PR #11094 #11213 (side-query at index 2 with the extractor on, 1 with it off).
  • The assertion typechecks honestly. The attributes are unknown, and the typeof guards narrow before the comparison, so tokensBefore > tokensAfter is a real numeric comparison rather than a coerced one. A missing event yields undefined and fails with the diagnostics printed, and there is no torn-read race with the preceding wait: a partially flushed line would not parse, so it could not have satisfied the wait either.

One non-blocking note: readLastApiRequest() is now expressible as readTelemetryEvent('api_request'), and you correctly left it alone — folding an untouched method into the new one would have widened the diff for no gain. Same reasoning applies to the duplicated foundEvent assertion I mentioned at the gate; keeping it buys a sharper failure message.

Test evidence

This is an unattended CI run, so I did not build or execute anything from this PR — the evidence below is the PR's own CI, read through the API for commit 083a94bd03d583e6d0290595d07a93b0ff5dc0c3. Nothing has failed. The three checks that matter for this diff were still running when I fetched, and I did not poll or guess their outcome.

The leg that would actually exercise these tests is Integration Tests (no-AK, No Sandbox), still in progress; the Integration Tests (CLI, No Sandbox) and the macOS/Windows Test matrix legs are skipped for this PR, and the two active /compress cases are skipIf(win32) in any case, so Windows never covers them. triage and review-pr in the table are this bot's own orchestration jobs, not the PR's CI.

Final CI results for 083a94b (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Lint & Static (ubuntu-latest, Node 22.x) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

Not verified, and why:

  • The new gate's bite on a real inflation path is unproven. should handle compression failure on token inflation is still it.skip, so nothing in the suite demonstrates that expectSuccessfulCompression() reddens when compression genuinely inflates tokens. The helper test proves the name predicate is load-bearing; it does not prove the reduction assertion is.
  • The integration suite has not reported on this commit yet — in progress at fetch time.
  • Author's claim, not independently re-run: macOS-only runs reporting 1825 -> 71 and 1832 -> 71, 2 API requests per case, no managed-memory events, compression_output_token_count=0 and cache_sharing_used=false. Windows and Linux were not run locally by the author.

Sandboxed verification would settle the first gap: @qwen-code /verify — whether the token-reduction assertion actually fails when the summary inflates (point the fake server's non-zero index at a payload longer than the seeded history and confirm the case reddens) is not observable from the diff, and the one case that would exercise it is skipped. The author has read-only access, so this is a sponsored run: a maintainer's @qwen-code /verify comment approves the head it was written against, and that run carries a pre-execution risk screen plus a full workspace wipe before any PR code runs. Please still read the resulting report with the same skepticism as this fork's own CI logs — the code under verification is adversarial input, and a crafted PR can shape what a report says even though the sandbox bounds what it can do.

No real-scenario tmux capture on this run: that path is local-invocation only, and this change has no user-visible product surface to drive.

中文说明

代码审查

没有阻塞性问题,也没有违反 AGENTS.md 的地方。这个 diff 只做一件事,用的是文件里已有的写法,没有夹带顺手改动。

在读 diff 之前,我先只根据标题和"为什么需要"写下了自己的方案:在已有的"读取最后一条 api_request"方法旁边加一个同步的遥测读取方法;在已有的等待之后断言 tokens_before > tokens_after,确保记录确实已落盘;把可区分的属性放进失败信息;关掉后台记忆抽取,使假服务器的请求序号不再依赖它;并为读取方法写单测,且固定数据的最后一条记录故意不匹配,这样删掉事件名条件就会让测试变红。这个 PR 逐条就是这样做的 —— 在跟踪 issue 已经规定了形态的情况下这是预期结果,但也意味着我没有更简单的替代方案可提。

以下是我实际核对过、而不是想当然的部分:

  • 固定数据能通过遥测解析器。 _readAndParseTelemetryLog() 不是按换行切分的 —— 它按 }\n{ 切分,再按位置把花括号补回去。紧凑单行的 JSON.stringify 记录用 \n 连接,正好是这套补括号逻辑所期望的形状,因此四条记录都能解析成功。变异体我也推演过:去掉 event.name 条件后,pop() 会返回末尾的 api_request 记录,于是 toBe(28891) 失败。这个固定数据是真正"咬得住"的,正是遗留项所要求的。
  • 共享的 settings 常量可以安全共享。 setup() 会构造一个新对象({telemetry, sandbox, ...options.settings}),并且只做 JSON.stringify,从不修改调用方传入的对象,因此把一个常量提到三个用例之上不会造成用例间状态泄漏。setup() 还会先 rm -rf 测试目录,所以两个新的辅助测试也不可能继承到残留的 telemetry.log
  • 记忆开关确实生效。 memory.enableManagedAutoMemory 是有效的 schema 键(默认 true),packages/cli/src/config/config.ts 会尊重它,除非处于 bare 或 safe 模式 —— 而本测试装置两者都不是。所以新注释里说的请求序号确定性是代码本身的性质,不是一种期望;它也与 Deferred review findings from PR #11094 #11213 记录的实测一致(抽取器开启时侧查询在序号 2,关闭时在序号 1)。
  • 断言在类型上是诚实的。 这些属性的类型是 unknown,而 typeof 守卫会在比较之前完成收窄,所以 tokensBefore > tokensAfter 是真正的数值比较,而不是隐式转换后的比较。事件缺失时得到 undefined 并带着诊断信息失败;与前面的等待之间也不存在读到半条记录的竞态:未写完的行不会解析成功,因此也不可能让等待先通过。

一条非阻塞的观察:readLastApiRequest() 现在可以写成 readTelemetryEvent('api_request'),而你正确地没有去动它 —— 把一个未被触及的方法折进新方法里,只会为了没有收益的事情扩大 diff。同样的理由也适用于我在 gate 阶段提到的 foundEvent 重复断言:保留它能换来更清晰的失败信息。

测试证据

这是一次无人值守的 CI 运行,因此我没有构建或执行本 PR 的任何代码 —— 下面的证据来自 PR 自己的 CI,通过 API 读取提交 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 的结果。目前没有失败项。对这个 diff 真正重要的三项检查在我抓取时仍在运行,我没有轮询,也没有猜测结果。

真正会跑到这些测试的是 Integration Tests (no-AK, No Sandbox),仍在进行中;Integration Tests (CLI, No Sandbox) 以及 macOS/Windows 的 Test 矩阵对本 PR 是跳过的,而两个启用中的 /compress 用例本身就是 skipIf(win32),所以 Windows 无论如何都覆盖不到。表格里的 triagereview-pr 是本机器人自己的编排任务,不属于 PR 的 CI。

未验证项及原因:

  • 新断言在真实 token 膨胀路径上是否"咬得住"尚未被证明。 should handle compression failure on token inflation 仍是 it.skip,所以套件中没有任何用例能证明当压缩真的膨胀 token 时 expectSuccessfulCompression() 会变红。辅助测试证明的是事件名条件是起作用的,并没有证明缩减断言是起作用的。
  • 集成测试套件在该提交上还没有出结果 —— 抓取时仍在进行中。
  • 作者自述、未经独立复跑: 仅在 macOS 上运行,报告 1825 -> 711832 -> 71、每个用例 2 次 API 请求、无托管记忆事件、compression_output_token_count=0cache_sharing_used=false。作者未在本地运行 Windows 和 Linux。

沙箱化验证可以补上第一个缺口:@qwen-code /verify —— 当摘要发生膨胀时,token 缩减断言是否真的会失败(把假服务器的非零序号指向一个比种子历史更长的响应,确认用例变红),这一点无法从 diff 看出来,而唯一能触发它的用例是跳过状态。作者只有只读权限,因此这是一次由维护者担保的运行:维护者评论 @qwen-code /verify 即批准其所针对的 head,该运行会在执行任何 PR 代码之前先做执行前风险筛查并完整清空工作区。即便如此,阅读产出的报告时请保持与阅读该 fork 自身 CI 日志同样的怀疑态度 —— 被验证的代码是对抗性输入,精心构造的 PR 可以影响报告说什么,尽管沙箱限制了它能做什么

本次运行没有真实场景的 tmux 抓取:该路径仅适用于本地调用,而且这个改动没有可供驱动的用户可见产品界面。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — the change is right and I found nothing to object to; the missing point is CI on this commit, which had not landed when I reviewed.

This is the unusual follow-up PR that arrives carrying its own mandate. #11213 is a maintainer-verified deferral out of #11094 that named exactly three survivors from the abandoned half — the memory setting re-justified as index determinism, a telemetry reader plus a real reduction gate, and a shared settings constant — and this diff is those three and nothing else. I went looking for scope creep, drive-by refactors, and the two inert timeout constants the deferral said to leave behind, and found none of them. The one place the author could have quietly widened the diff, folding the existing last-api-request reader into the new generic one, they correctly left alone.

What I would normally be suspicious of in a PR like this is an assertion on numbers that move. That is handled the right way: the gate is relational rather than exact, and the volatile values go into the failure message. The gap between the author's observed 1825 -> 71 and the 3607 -> 71 measured on main in the deferral is not a discrepancy to resolve — it is the argument for that choice, and it is worth a line in the PR body so nobody later "tightens" it into a pinned oracle.

My one real reservation is that nobody has yet seen the new gate fail on a genuine inflation, because the single case that would exercise it is still skipped — and this PR correctly declines to un-skip it, since that case predates the diff and needs its live-model path rethought. So the reservation is a follow-up, not a defect here: against main's presence-only assertion this is a strict improvement either way, and I named the sponsored /verify lane in the review above for whoever wants the bite proven.

On maintenance: readTelemetryEvent reads like the method sitting next to it, the helper test's trailing non-matching record means a future deletion of the name predicate fails loudly instead of silently passing, and a broken /compress case will now print the numbers needed to diagnose it rather than a bare boolean. In six months I would thank the author, not curse them.

I also checked whether I was being worn down by volume rather than judging merit — the author has two other open PRs, on unrelated surfaces, roughly a week apart. Nothing that reads as churn, and this one stands on its own evidence.

Verdict: approve. CI on this commit was still running at review time (Qwen Code CI — ubuntu unit, lint/static, and the no-AK integration leg that actually exercises these tests), so approval is deferred until CI lands green on 083a94bd03d583e6d0290595d07a93b0ff5dc0c3; the finalize job posts the commit-pinned approval then, and withholds it if anything lands red or the head moves. I am not approving in this run, because approving against a suite that has not reported would attest to a result that does not exist yet.

中文说明

信心度:4/5 —— 改动是对的,我没有找到可反对之处;差的那一分是本次审查时该提交上的 CI 还没出结果。

这是一个少见的、自带授权的后续 PR。#11213 是从 #11094 遗留下来、经维护者核实过的遗留项,明确点名了被放弃的那一半中应当保留的三样东西 —— 以序号确定性重新论证的记忆开关、一个遥测读取方法加一条真实的缩减断言、以及一个共享的 settings 常量 —— 而这个 diff 正好是这三样,不多不少。我特意去找了范围蔓延、顺手重构,以及遗留项要求丢弃的那两个已失效的超时常量,都没有找到。作者本来可以悄悄扩大 diff 的那个地方(把已有的"读取最后一条 api_request"方法折进新的通用方法里),也正确地没有去动。

这类 PR 我通常会怀疑的一点,是对会变动的数值下断言。这里处理方式是对的:断言是关系型的而非精确值,且把易变的数值放进了失败信息。作者实测的 1825 -> 71 与遗留项在 main 上测到的 3607 -> 71 之间的差异,并不是一个需要消除的矛盾 —— 它恰恰是选择关系型断言的理由;建议在 PR 描述里加一句,以免后来者把它"收紧"成写死的 oracle。

我唯一真正的保留意见是:还没有人见过这个新断言在真实 token 膨胀时失败,因为唯一能触发它的用例仍然是跳过状态 —— 而本 PR 正确地没有去取消跳过,因为那个用例早于本 diff,且需要重新设计其真实模型路径。所以这条保留意见属于后续工作,而不是本 diff 的缺陷:相比 main 上"只断言事件存在"的写法,无论如何这都是严格改进。我在上面的审查中也点名了由维护者担保的 /verify 通道,供想证明它"咬得住"的人使用。

关于后续维护:readTelemetryEvent 读起来与它旁边的方法一致;辅助测试末尾那条不匹配的记录意味着,将来若有人删掉事件名条件,测试会明确失败而不是静默通过;而一旦 /compress 用例出问题,现在会打印出诊断所需的数值,而不是一个光秃秃的布尔值。六个月后我会感谢作者,而不是埋怨。

我也检查了自己是否是被数量磨软了判断、而非基于实质:该作者另有两个开放 PR,主题互不相关,间隔约一周。看不出刷量迹象,本 PR 也是凭自身证据独立判断的。

结论:批准。审查时该提交上的 CI 仍在运行(Qwen Code CI —— ubuntu 单测、lint/静态检查,以及真正会跑到这些测试的 no-AK 集成测试环节),因此批准动作推迟到 CI 在 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 上全绿之后;届时由收尾任务提交绑定到该提交的批准,若有任何一项变红或 head 发生变动则不予批准。本次运行不提交批准,因为在测试套件尚未出结果时批准,等于为一个还不存在的结论背书。

Qwen Code · qwen3.8-max-2026-09-02

Reviewed at 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 · re-run with @qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship — CI landed green after the review. ✅

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 10800 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: @qwen-code /review --timeout=360. See workflow logs.

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Suggestions are inline.

1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:

  • R1-3 readLastApiRequest / readTelemetryEvent duplication — already reported (comment 5565290448)

Not explored to full depth (tool budget reached): "agent 6a": did not execute the two /compress integration cases or the new test-helper.test.ts cases (needs npm run build && npm run bundle in this shared worktree); …; "agent 3b": none — every check above completed inside the tool budget (~16 of 33 calls).; "agent 1c": did not verify whether the managed-auto-dream background task ( getManagedAutoDreamEnabled() , packages/core/src/config/config.ts:8194-8198 , left enabled by ….

中文说明

已审查。 建议见行内评论。

本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。

未探索到全部深度(达到工具调用预算):"agent 6a"did not execute the two /compress integration cases or the new test-helper.test.ts cases (needs npm run build && npm run bundle in this shared worktree); …"agent 3b"none — every check above completed inside the tool budget (~16 of 33 calls)."agent 1c"did not verify whether the managed-auto-dream background task ( getManagedAutoDreamEnabled() , packages/core/src/config/config.ts:8194-8198 , left enabled by …

— qwen3.8-max via Qwen Code /review (v0.23.0)

Comment thread integration-tests/interactive/context-compress-interactive.test.ts
expect(
typeof tokensBefore === 'number' &&
typeof tokensAfter === 'number' &&
tokensBefore > tokensAfter,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-7: This oracle means what it says only because the fake server reports zero usage, and nothing records that dependency. The /compress handler returns just { content }, so FakeOpenAIServer falls back to DEFAULT_USAGE (all zeros) and ChatCompressionService takes its local-estimator branch — which is the only reason tokens_before is an estimate of the seeded history at all. That makes the assertion fragile in both directions at once. Below one boundary, a maintainer who makes the fixture "realistic" by adding a usage block reddens two healthy cases with a message that names product token accounting while the real cause is a fixture field two files away. Above it, the oracle goes vacuous: tokens_before becomes the fixture's own declared number and tokens_after the estimator's count for the fixed 66-character summary, so shrinking the seeded model response a hundredfold moves neither value and the assertion reduces to comparing two fixture constants. That edit idiom is common in this tree — four of the five distinct prompt_tokens literals under integration-tests/ (20, 24, 32, 50) sit below the measured boundary of 71, so copying the nearest sibling breaks both cases. To be clear about the present state: the committed oracle is sound today. It is genuinely seed-sensitive, and it does catch a real inflated summary, so this is a latent fragility rather than a live defect — but it is one field-edit away, and the failure message would point the next debugger at the wrong subsystem.

Witness:

QWEN_SANDBOX=false npx vitest run --root ./integration-tests \
  ./interactive/context-compress-interactive.test.ts

CONTROL (as committed):                       Tests 2 passed | 1 skipped
  tokens_before=1864  tokens_after=71  input=0  output=0  cache_sharing=False

BELOW the boundary -- fixture usage {prompt_tokens: 20, completion_tokens: 5}:
                                              Tests 2 failed | 1 skipped
  AssertionError: chat_compression event recorded no token reduction:
  tokens_before=20, tokens_after=71, output=5, cache_sharing=true: expected false to be true
  boundary sweep: 20 RED   50 RED   100 GREEN   1500/200 GREEN

ABOVE the boundary -- the oracle stops measuring the conversation:
  A  usage 100/20, seed INTACT                -> 2 passed, tokens_before=100 tokens_after=71
  B  usage 100/20, seed shortened 100x        -> 2 passed, tokens_before=100 tokens_after=71
                                                ^ bit-identical to A: the seed no longer matters
  D  usage 100/20, seed turn DELETED          -> 1 failed, 'telemetry event was not found'
                                                ^ so the vacuity is the seed's magnitude, not its existence
  F  committed zero-usage + seed shortened    -> 2 passed, tokens_before=1663 (was 1864)
                                                ^ the committed oracle IS seed-sensitive

Bounding arm -- does the gate bite on a genuine inflation?
  E1 '<state_snapshot>' padded with 'padded '.repeat(2000):
                                              Tests 2 failed | 1 skipped
  AssertionError: chat_compression event recorded no token reduction:
  tokens_before=1864, tokens_after=3571, output=0, cache_sharing=false: expected false to be true

The cheapest half of the fix is to record the dependency where the oracle lives, and to make the zeros deliberate rather than inherited:

// The reduction below is only meaningful because the fake server reports zero
// usage (DEFAULT_USAGE), which sends ChatCompressionService down its
// local-estimator branch so tokens_before estimates the seeded history.
// Any non-zero usage changes what tokens_before means: below ~71 prompt tokens
// both cases fail as INFLATED, and above it tokens_before is just the fixture's
// own number and the assertion stops measuring the conversation.

plus usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 } in the handler, and optionally an assertion on the two attributes that actually identify the branch (compression_input_token_count === 0, cache_sharing_used === false).

The boundary to respect is originalTokenCount >= 71 — the estimator's count for the fixture's fixed 66-character <state_snapshot> string — not the cold-path compressionInputTokenCount - 1000 arithmetic at chatCompressionService.ts:1205-1208, which is never reached here because a non-zero provider count also flips hasProviderTokenCount true (:800-802), takes the cache-sharing path (:808, :889) and routes into the estimator at :1243; note too that usage: message.usage ?? DEFAULT_USAGE (fake-openai-server.ts:420-424, consumed at :392) makes explicit zeros byte-identical to today's wire bytes.

If you add the branch-identifying assertion rather than only the comment, it must go red on the vacuity arm and stay green as committed — measured both ways: green on the committed fixture, and oracle no longer measures the seeded history ... tokens_before=100 on the prompt_tokens: 100 arm with a shortened seed.

中文说明

这条断言之所以成立,完全是因为假服务器返回的 usage 全为零,而这个依赖没有任何地方记录下来。/compress 的处理函数只返回 { content },于是 FakeOpenAIServer 回退到 DEFAULT_USAGE(全零),ChatCompressionService 走进本地估算分支 —— 这也正是 tokens_before 会成为种子历史估算值的唯一原因。这让断言在两个方向上同时变脆。在某个边界之下,维护者若为了让 fixture「更真实」而加上 usage,两个本来健康的用例就会变红,而报错信息指向的是产品的 token 计算,真正的原因却是两个文件之外的一个 fixture 字段。在边界之上,断言会变成空转:tokens_before 变成 fixture 自己声明的数字,tokens_after 变成估算器对固定 66 字符摘要的计数,于是把种子模型响应缩短一百倍两个数字都不变,断言退化为比较两个 fixture 常量。这种改法在本仓库里很常见 —— integration-tests/ 下五个不同的 prompt_tokens 字面量中有四个(20、24、32、50)都低于实测的 71 边界,所以照抄最近的同类文件就会让两个用例都失败。需要说明当前状态:已提交的断言今天是可靠的。它确实对种子敏感,也确实能抓到真实的摘要膨胀,所以这是一个潜在脆弱性而不是现存缺陷 —— 但距离触发只差一次字段修改,而且失败信息会把下一个排查的人指向错误的子系统。

最省事的修法是把依赖记录在断言所在处,并把「零」变成显式选择而不是继承来的默认值(见上方英文代码块中的注释与显式 usage 写法),也可以再加上真正标识该分支的两个属性断言(compression_input_token_count === 0cache_sharing_used === false)。

需要尊重的边界是 originalTokenCount >= 71 —— 即估算器对 fixture 固定 66 字符 <state_snapshot> 字符串的计数 —— 而不是 chatCompressionService.ts:1205-1208 中冷路径的 compressionInputTokenCount - 1000 算式;后者在这里根本不会走到,因为非零的 provider 计数同时会把 hasProviderTokenCount 置为真(:800-802),走缓存共享路径(:808:889),并在 :1243 转入估算分支。另外,usage: message.usage ?? DEFAULT_USAGEfake-openai-server.ts:420-424,在 :392 被消费)意味着显式写零与今天的实际字节完全一致。

如果选择加分支标识断言而不只是加注释,那么它必须在空转那一侧变红、在已提交状态下保持绿色 —— 两个方向都已实测:committed fixture 下绿色,prompt_tokens: 100 且种子缩短时输出 oracle no longer measures the seeded history ... tokens_before=100

— qwen3.8-max via Qwen Code /review (v0.23.0)

Comment on lines +208 to +209
// Earlier unit tests cover prompt composition; this pins the end-to-end
// argument path and a successful compression result.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-1: This rewrite strengthens the comment to say the test "pins the end-to-end argument path", but nothing in the test observes the argument. The fake-server handler dispatches on requestIndex alone and never reads the request body, and the case's only assertions are telemetry-event presence plus the new token reduction — both satisfied by any successful compression. So if a future change stops forwarding the trailing argument into the compression system prompt, this case stays green while the behaviour its own name advertises (should forward /compress instructions through to the side-query) is broken, and the comment now tells the next reader the path is covered, which is what stops someone adding the assertion. One correction that bounds the cost, because it matters: this would not be an undetected regression. Both hops are pinned elsewhere — compressCommand.test.ts:346-367 asserts 'focus on auth bug' reaches tryCompressChat, and chatCompressionService.test.ts:4384-4406 asserts the Additional Instructions: block is built. What is actually wrong is narrower: a comment in this file records coverage this file does not have. Either add the assertion or narrow the comment.

Witness:

grep -n "focus on the scientist\|requests\|body" \
  integration-tests/interactive/context-compress-interactive.test.ts
-> exactly one hit, line 210, the keystroke itself:
     await type(ptyProcess, '/compress focus on the scientist mentioned');
-> zero of the file's 10 expect() calls reference fakeServer.requests or a request body

handler discards the body:
  ({ requestIndex }) => ({ content: requestIndex === 0 ? ... : '<state_snapshot>...' })

witness (mutation arm): not run — the mutation lives in production source under
  packages/cli and packages/core; the scratch tree's unit harness stops at its own
  build guard (missing packages/acp-bridge/dist/index.js and 12 more) and the
  interactive case spawns dist/cli.js, which the scratch tree does not have.
  Survival rests on the sweep above plus the reply being index-keyed and body-independent.

The fake server already records what it received, and siblings assert on it (file-system-interactive.test.ts:137, mid-turn-submit-interactive.test.ts:270). Searching rather than indexing keeps the assertion from inheriting the request-index premise:

expect(
  fakeServer!.requests.some((r) =>
    JSON.stringify(r.body).includes('focus on the scientist mentioned'),
  ),
  'the /compress instruction never reached the side-query',
).toBe(true);

Assert on the instruction substring rather than the assembled prompt shape or a byte offset: the instruction travels in the side-query's system prompt as an Additional Instructions: block (chatCompressionService.ts:377) and the CLI clips it with rawArgs.slice(0, MAX_COMPRESS_INSTRUCTIONS_CHARS), cap 2000 (compressCommand.ts:71-72, itself pinned by compressCommand.test.ts:425-437).

Whichever way you resolve it, the new body assertion must go red when the instruction stops reaching the side-query — drop customInstructions at compressCommand.ts:89 or parts.push(userInstructions.trim()) at chatCompressionService.ts:371 and confirm it fails; neither mutation reddens any assertion in this file today.

中文说明

这次改写把注释强化为「pins the end-to-end argument path」(钉住端到端参数路径),但测试里没有任何地方观察这个参数。假服务器的处理函数只按 requestIndex 分派,从不读取请求体;而该用例仅有的断言是遥测事件存在,加上新增的 token 缩减 —— 任何一次成功的压缩都能同时满足这两条。因此如果将来有改动不再把尾随参数转发进压缩系统提示词,这个用例仍然会是绿色,而它自己名字所声称的行为(should forward /compress instructions through to the side-query)已经坏了;更糟的是注释现在会告诉后来的读者这条路径已被覆盖,从而阻止别人补上断言。有一个限定代价的更正很重要:这不会是一个无人察觉的回归。两处环节都已被别处钉住 —— compressCommand.test.ts:346-367 断言 'focus on auth bug' 到达了 tryCompressChatchatCompressionService.test.ts:4384-4406 断言 Additional Instructions: 块被构造出来。真正有问题的地方更窄:本文件里的注释记录了本文件并不具备的覆盖。要么补上断言,要么把注释收窄。

假服务器已经记录了它收到的内容,同类测试也是这样断言的(file-system-interactive.test.ts:137mid-turn-submit-interactive.test.ts:270)。用查找而不是固定序号,可以避免断言继承请求序号这个前提(见上方英文代码块)。

请断言指令子串,而不是拼装后的提示词形状或字节偏移:指令是以 Additional Instructions: 块的形式进入侧查询的系统提示词(chatCompressionService.ts:377),并且 CLI 会用 rawArgs.slice(0, MAX_COMPRESS_INSTRUCTIONS_CHARS) 截断,上限 2000(compressCommand.ts:71-72,该上限本身由 compressCommand.test.ts:425-437 钉住)。

无论选择哪种处理方式,新增的请求体断言都必须在指令不再到达侧查询时变红 —— 去掉 compressCommand.ts:89customInstructions,或去掉 chatCompressionService.ts:371parts.push(userInstructions.trim()),确认它失败;今天这两个变异都不会让本文件任何断言变红。

— qwen3.8-max via Qwen Code /review (v0.23.0)

Comment on lines +118 to +119
rig.createFile(
'telemetry.log',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-6: This fixture writes compact one-record-per-line JSON, but the exporter that produces a real telemetry.log writes indent-2 pretty-printed records — so the rig's only fast unit coverage of the record-splitting path is keyed to a format production never emits. FileExporter.serialize is safeJsonStringify(data, 2) + '\n', and a real log from this very suite measured 1870 lines across 20 records. _readAndParseTelemetryLog's split(/}\n{/) brace reassembly exists precisely for that multi-line shape. Both shapes parse today, so the tests are green — but that is the problem: replacing the reassembly with line-based JSONL survives this fixture while returning null for every record of a real log. The fixture therefore blesses exactly the simplification that would break every consumer of the parser (waitForTelemetryEvent, readLastApiRequest, readToolLogs, readMetric), and defers the catch to the slow E2E suite. To be fair about severity, the mutation is not silent at suite level — those consumers read real logs and the slow cases would fail — and the parser itself is pre-existing unchanged code. The gap is specifically that a reviewer simplifying the parser would see this fast fixture stay green, and grep confirms it is the only createFile('telemetry.log', ...) fixture in integration-tests/.

Witness:

production shape, read:
  packages/core/src/telemetry/file-exporters.ts:45-47
    protected serialize(data: unknown): string {
      return safeJsonStringify(data, 2) + '\n';
    }

real log from this suite (kept run, interactive-compress-test/telemetry.log):
  {"firstLineOfFile":"{","fileLineCount":1870,"recordCount":20}

INTACT parser, real log                -> {"eventName":"qwen-code.chat_compression",
                                           "tokens_before":1845,"tokens_after":71}
MUTANT line-based JSONL, PR fixture    -> Tests 9 passed (9)        <- mutation SURVIVES
MUTANT line-based JSONL, same real log -> {"recordCount":120,"recordsWithoutAttributes":120}
  readTelemetryEvent('chat_compression') -> {"returnedNull":true}   <- breaks every consumer

fixture emitted as JSON.stringify(obj, null, 2):
  INTACT -> Tests 9 passed (9)                        <- fix is safe
  MUTANT -> FAILED expected undefined to be 28891     <- fix kills the mutant

Emitting each record with the exporter's own shape is a fixture-only change — build them as JSON.stringify(obj, null, 2) and keep the .join('\n'). The indent must match serialize's safeJsonStringify(data, 2) (packages/core/src/telemetry/file-exporters.ts:45-47), and the parser's content.split(/}\n{/) at test-helper.ts:788 must keep working for both shapes.

With the pretty-printed fixture, replacing the split(/}\n{/) reassembly in _readAndParseTelemetryLogwith per-lineJSON.parsemust turn the latest-match case red — measured:expected undefined to be 28891`. Against today's compact fixture that same mutation stays green.

中文说明

这个固定数据写的是紧凑的「一行一条记录」JSON,而真正生成 telemetry.log 的导出器写的是缩进 2 的美化多行记录 —— 因此测试夹具中唯一快速覆盖记录切分路径的单测,绑定的是一个生产环境永远不会输出的格式。FileExporter.serializesafeJsonStringify(data, 2) + '\n',而本套件产生的一份真实日志实测为 1870 行、20 条记录。_readAndParseTelemetryLog 里的 split(/}\n{/) 补括号逻辑正是为这种多行形状而存在。两种形状今天都能解析,所以测试是绿色的 —— 但问题恰恰在这里:把补括号逻辑换成按行 JSONL 解析,在这个固定数据上依然通过,而对真实日志的每一条记录都返回 null。于是这份固定数据恰好为那个会破坏解析器所有消费者(waitForTelemetryEventreadLastApiRequestreadToolLogsreadMetric)的「简化」背书,并把发现时机推迟到慢速 E2E 套件。为公允地判断严重程度:这个变异在套件层面并不是无声的 —— 那些消费者读取真实日志,慢速用例会失败 —— 而且解析器本身是本次未改动的既有代码。缺口具体在于:一个想简化解析器的审查者会看到这个快速 fixture 仍然绿色,而 grep 确认它是 integration-tests/ 中唯一一处 createFile('telemetry.log', ...) 固定数据。

按导出器自身的形状生成每条记录只涉及固定数据本身的改动 —— 用 JSON.stringify(obj, null, 2) 构造,并保留 .join('\n')。缩进必须与 serializesafeJsonStringify(data, 2) 一致(packages/core/src/telemetry/file-exporters.ts:45-47),并且 test-helper.ts:788content.split(/}\n{/) 必须对两种形状都继续有效。

改用美化格式后,把 _readAndParseTelemetryLog 中的 split(/}\n{/) 补括号逻辑换成逐行 JSON.parse,必须让「最新匹配」用例变红 —— 实测输出 expected undefined to be 28891。而在今天的紧凑固定数据下,同一个变异仍然绿色。

— qwen3.8-max via Qwen Code /review (v0.23.0)

Comment on lines +146 to +147
expect(event?.attributes?.['tokens_before']).toBe(28891);
expect(event?.attributes?.['tokens_after']).toBe(27128);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] R1-4: Neither new test pins the reader's exact event-name match. The interleaved api_request records prove that non-matching names are skipped, but no record's name extends the queried one — so the exactness the method's contract depends on has no witness. That matters because the relaxation is a natural future edit: letting callers pass a fully-qualified name means changing === to startsWith, and that mutant survives both tests and typechecks clean, so it could ship past the gate CI runs. The telemetry namespace already contains prefix-colliding pairs, so the consequence is concrete rather than hypothetical — readTelemetryEvent('slash_command') would silently start returning slash_command.model events, and a caller would assert on the wrong event's attributes with nothing failing anywhere. Note that neither the method name nor its comment says "exact"; only the PR description does. The prior review on this PR traced a different mutant — deleting the name predicate, which this fixture does kill — so this gap is a distinct one.

Witness:

vitest run --root ./integration-tests ./test-helper.test.ts   (baseline: 9 passed)

arm                                    | new unit tests                | probe: readTelemetryEvent('slash_command')
INTACT ===, PR fixture                 | Tests 9 passed (9)            | {"returnedNull":true}
MUTANT ?.startsWith, PR fixture        | Tests 9 passed (9)  SURVIVES  | {"returnedNull":false,
                                                                        "eventName":"qwen-code.slash_command.model"}
MUTANT + proposed .details record      | FAILED expected 1 to be 28891 | -
INTACT + proposed .details record      | Tests 9 passed (9)            | -

npm run typecheck:integration under the mutant: no test-helper.ts error
  -> the relaxation is type-admissible, so no gate catches it

colliding pairs verified at HEAD:
  packages/core/src/telemetry/constants.ts:31  EVENT_SLASH_COMMAND       = 'qwen-code.slash_command'
  packages/core/src/telemetry/constants.ts:49  EVENT_MODEL_SLASH_COMMAND = 'qwen-code.slash_command.model'
  packages/core/src/telemetry/constants.ts:89  'qwen-code.memory.recall'
  packages/core/src/telemetry/constants.ts:90  'qwen-code.memory.recall.delivery'

Appending one record as the last line whose name extends the queried one gives exactness a witness at no cost to correct code:

JSON.stringify({
  attributes: {
    'event.name': 'qwen-code.chat_compression.details',
    tokens_before: 1,
    tokens_after: 0,
  },
}),

The new record must carry the full qwen-code. prefix, because the filter builds the name as `qwen-code.${eventName}` (test-helper.ts:900), matching waitForTelemetryEvent (:559) and readMetric (:912).

The latest-match case must then go red when === at test-helper.ts:900 becomes .startsWith(...); today that mutation is invisible (measured: 9 passed either way).

中文说明

两个新测试都没有钉住读取方法的精确事件名匹配。中间插入的 api_request 记录只能证明不匹配的名字会被跳过,但没有任何一条记录的名字延伸了被查询的名字 —— 于是该方法契约所依赖的「精确性」没有任何证据。这一点之所以重要,是因为放宽匹配是一个很自然的后续改动:想让调用方可以传入完整限定名,就要把 === 改成 startsWith,而这个变异体在两个测试中都能存活,并且类型检查也干净通过,因此可能绕过 CI 所运行的关卡直接进入代码库。遥测命名空间里本来就存在前缀冲突的事件对,所以后果是具体的而不是假设性的 —— readTelemetryEvent('slash_command') 会静默开始返回 slash_command.model 事件,调用方于是对错误事件的属性下断言,而任何地方都不会报错。需要注意的是,方法名和它的注释都没有写明「精确」,只有 PR 描述写了。本 PR 之前的审查推演的是另一个变异体 —— 删除事件名条件(这个固定数据确实能抓到),所以这里的缺口是另一个不同的问题。

在数组最后追加一条名字延伸了被查询名字的记录,就能为精确性提供证据,且对正确代码没有任何代价(见上方英文代码块)。

新记录必须带上完整的 qwen-code. 前缀,因为过滤条件是按 `qwen-code.${eventName}` 构造名字的(test-helper.ts:900),与 waitForTelemetryEvent:559)和 readMetric:912)一致。

加上之后,把 test-helper.ts:900=== 改成 .startsWith(...) 必须让「最新匹配」用例变红;今天这个变异是不可见的(实测两种情况都是 9 passed)。

— qwen3.8-max via Qwen Code /review (v0.23.0)

@qqqys

qqqys commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Verification report at head 083a94bd03d583e6d0290595d07a93b0ff5dc0c3 — conclusion: mergeable.

No tmux e2e run, deliberately. This PR is zero production code: all three changed files sit under integration-tests/ (interactive/context-compress-interactive.test.ts +42/−26, test-helper.test.ts +48/−0, test-helper.ts +12/−0), so there is no product behaviour for a TUI session to exercise — a tmux run would be a strictly weaker duplicate of what the harness itself asserts. The instrument of record is the harness's own assertions, which I read at the head blobs rather than at the diff.

What the new assertions actually pin

  • context-compress-interactive.test.ts:74-93 (blob 9473affc9602) — expectSuccessfulCompression() requires tokens_before and tokens_after to both be typeof 'number' and tokensBefore > tokensAfter, with a diagnostic naming both values. That is precisely what the PR title claims: a /compress that records no token reduction now fails instead of passing silently.
  • test-helper.test.ts:115-149 (blob 8b6cff3a2203) — the recency-plus-filter test. The fixture holds two qwen-code.chat_compression records (1000/900, then 28891/27128) interleaved with two qwen-code.api_request records, and asserts the reader returns 28891/27128. The interleaving is what makes it a filter test as well as a recency test: it pins readTelemetryEvent's attributes['event.name'] === 'qwen-code.' + name match (test-helper.ts:898-902) and its events.pop() newest-wins return (:903) at the same time, so the oracle cannot be satisfied by a stale or differently-named event.
  • test-helper.test.ts:151-158expect(rig.readTelemetryEvent('chat_compression')).toBeNull(), pinning the absent-event case so a telemetry log that never landed cannot read as success.

CI, verified by lane name rather than by rollup state

Complete census at this head: 121/121 check-runs fetched (sum(returned) == total_count asserted), 0 non-green live lanes after deduplicating by lane name and taking the newest completed_at per lane; raw census 103 skipped / 18 success / 0 failure. review-pr is now success — it was still in_progress the last time this PR was examined, which is the condition that had held the report branch closed. Product lanes green: Test (ubuntu-latest, Node 22.x), Lint & Static (ubuntu-latest, Node 22.x), Integration Tests (no-AK, No Sandbox), web-shell E2E Smoke (ubuntu-latest, Node 22.x), Desktop Shell (ubuntu-22.04), Desktop Shell (windows-2022).

Approval leg: qwen-code-ci-bot APPROVED (row 5128395088) is anchored at this head, and its submitted_at of 2026-09-07T05:38:48Z is +3102 s after the head commit's own committer.date of 2026-09-07T04:47:06Z — a positive delta, so it is a genuine verdict on this tree and not a stale approval that GitHub re-anchored onto a server-side base merge.

One coverage fact worth recording — not a blocker

The green Integration Tests (no-AK, No Sandbox) lane runs an explicit file list (package.json:60) that includes ./test-helper.test.ts but not ./interactive/context-compress-interactive.test.ts; the interactive suite sits behind a separate script (package.json:66), and Integration Tests (CLI, No Sandbox) was skipped on this PR. So of the three instruments above, the two in test-helper.test.ts are executed by CI on a head-verified checkout (the lane runs Verify checkout includes expected head commit, and also npm run typecheck:integration), while the headline expectSuccessfulCompression() is typechecked but never executed on this PR.

That is a gap in lane coverage, not a defect in the change: the assertion reads correctly, it is the strengthening the PR set out to make, and nothing here can affect shipped behaviour. It does mean the new /compress oracle is only exercised when the interactive suite runs, so it will first get real airtime after merge rather than in this PR's own CI.

Findings

No Critical from me. qwen-code-ci-bot's at-head review (5135137376) carries 5 findings, all Suggestion-level by its own machine-readable ledger ("sev":"S" on R1-1, R1-2, R1-4, R1-6, R1-7), and its stage-2 triage (5565290448) reports "No critical blockers and no AGENTS.md violations". Approving on that basis.

中文说明

在 head 083a94bd03 上给出验证报告,结论:可以合入

未做 tmux e2e,是有意为之。 本 PR 零生产代码:三个改动文件全部位于 integration-tests/ 下,没有任何产品行为可供 TUI 会话触发,跑 tmux 只会是测试框架自身断言的更弱重复。因此以框架自身的断言为准,并且是在 head blob 上读取而非看 diff。

**新断言实际钉住了什么:**① context-compress-interactive.test.ts:74-93expectSuccessfulCompression() 要求 tokens_beforetokens_after 同为 numbertokensBefore > tokensAfter,并在失败信息里带上两个值——正是标题所声称的:没有产生 token 下降的 /compress 从此会失败而不是静默通过。② test-helper.test.ts:115-149 的「时近性 + 过滤」用例:夹具里两条 chat_compression1000/90028891/27128与两条 api_request 交错,断言取到 28891/27128;正是这个交错使它同时成为过滤用例,一次钉住 readTelemetryEventevent.name 精确匹配(test-helper.ts:898-902)与 events.pop() 的最新优先返回(:903),因此该 oracle 不可能被过期或异名事件满足。③ test-helper.test.ts:151-158toBeNull() 钉住「事件从未落盘」的情形,避免缺事件被读成成功。

CI 按 lane 名核验,而非按 rollup 状态。 本 head 完整普查 121/121 条 check-run(已断言 sum(returned) == total_count),按 lane 名去重取最新 completed_at非绿灯 lane = 0;原始普查 103 skipped / 18 success / 0 failure。review-pr 现已 success——上次查看本 PR 时它仍为 in_progress,而那正是当时报告分支关闭的原因。产品 lane 全绿(ubuntu 单测、Lint & Static、no-AK 集成、web-shell E2E Smoke、两个 Desktop Shell)。批准腿:ci-bot 的 APPROVED5128395088)锚定在本 head,其 submitted_at05:38:48Z)比 head 提交自身的 committer.date04:47:06Z晚 3102 秒,为正差值,说明它是对本棵树的真实裁定,而不是被 GitHub 重新锚定到服务端 base 合并上的过期批准。

一条值得记录、但不构成阻塞的覆盖事实。 绿灯的 Integration Tests (no-AK, No Sandbox) 跑的是显式文件清单package.json:60),其中包含 ./test-helper.test.ts,但不包含 ./interactive/context-compress-interactive.test.ts;交互式套件在另一个脚本里(package.json:66),而 Integration Tests (CLI, No Sandbox) 在本 PR 上是 skipped。因此上述三个手段中,test-helper.test.ts 的两个已由 CI 在经 head 校验的检出上实际执行(该 lane 会跑 Verify checkout includes expected head commitnpm run typecheck:integration),而最核心的 expectSuccessfulCompression() 只被类型检查、在本 PR 上从未执行。这是 lane 覆盖的缺口而非改动本身的缺陷:断言写法正确、正是本 PR 想要加强的东西,且不可能影响已发布行为;但新的 /compress oracle 要等交互式套件运行时才会被真正执行,也就是说它首次获得真实运行是在合入之后,而不在本 PR 自己的 CI 里。

结论: 我没有发现 Critical。ci-bot 在本 head 的评审(5135137376)共 5 条发现,按其自带的机器可读 ledger 全部为 Suggestion 级(R1-1、R1-2、R1-4、R1-6、R1-7 均为 "sev":"S"),其 stage-2 triage(5565290448)也写明「No critical blockers」。据此批准。

@qqqys qqqys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve at head 083a94bd03d583e6d0290595d07a93b0ff5dc0c3.

Verification report above; its conclusion is mergeable. Zero production code (all three files under integration-tests/), so the report cites the harness's own executed assertions rather than a tmux run. Complete CI census 121/121 with 0 non-green live lanes by lane name; qwen-code-ci-bot APPROVED anchored at this head with a +3102 s re-anchor delta. No Critical from me, and ci-bot's 5 at-head findings are all Suggestion-level by its own ledger.

One non-blocking coverage note is recorded in the report: the headline expectSuccessfulCompression() in interactive/context-compress-interactive.test.ts is not in the no-AK lane's explicit file list, so it is typechecked but not executed by this PR's CI.

@wenshao
wenshao added this pull request to the merge queue Sep 11, 2026
Merged via the queue into QwenLM:main with commit 6a43348 Sep 11, 2026
179 of 180 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants