Skip to content

feat(core): shrink tool results to the headroom left before auto-compaction - #13599

Merged
yiliang114 merged 65 commits into
QwenLM:mainfrom
yiliang114:codex/feedback-tool-output-budget
Oct 27, 2026
Merged

yiliang114 merged 65 commits into
QwenLM:mainfrom
yiliang114:codex/feedback-tool-output-budget

Conversation

@yiliang114

@yiliang114 yiliang114 commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

What this PR does

When a session is close to auto-compaction, shrink the next request's tool results to its remaining headroom. Use only the same chat's last successful usage report for the current provider route and unchanged history prefix. System instructions, session context and tool declarations must match that report; equivalent refreshes preserve it, while changed inputs and obsolete in-flight responses invalidate it without clearing usage counters. Explicit truncation settings take priority.

Measure headroom against auto-compaction and share the selected budget across the batch, charging protected text, media and framing first. Keep the tighter aggregate budget and avoid cutting results twice. If an allocation cannot fit a complete truncation notice, preserve the batch for normal compaction and hard-limit admission. When an artifact path cannot fit, use a complete pointerless notice. Save full originals through the existing best-effort persistence path whenever results are shortened. Before persistence, resolve any read that the selected preview would shorten. If a regular-chat read cannot be traced to a path in model history, preserve the entire batch for normal compaction and overflow handling so worker cache residency still matches the full content the model receives.

Defaults stay at 25,000 characters / 1,000 lines. Result-size accounting moved to #13652. Design: English · įŪ€ä―“äļ­æ–‡.

Why it is needed

#2566: static tool budgets can push a long session over the compaction trigger prematurely. A hard-ceiling anchor cuts too late; an obsolete usage anchor cuts against the wrong context. At the other extreme, sub-notice allocations erased failure diagnostics and could advertise a nonexistent partial artifact path. A supported PreToolUse repair can supply a read path while recorded model arguments remain {}; shortening that result without notifying the worker could then make a later read incorrectly omit the full body.

Reviewer Test Plan

How to verify

  • Near the auto trigger, return a large result: it should shrink to available headroom. Far from or past auto, or after restore/route change/explicit settings, it should keep the static behavior.
  • Change fixed inputs, including during a held response: stale usage should not reanchor. Equivalent refreshes should retain the headroom anchor and usage counters.
  • Leave only enough headroom for an empty or incomplete notice: original output and error should reach normal compaction admission. A larger but path-insufficient preview should contain a complete notice, a nonempty error indication and no partial recovery path.
  • Check the complete persisted original after a meaningful cut and verify user/protected text and previous file-read rights survive.
  • Repair an empty-argument read through PreToolUse: the full result should remain in request/history before persistence, and a later unchanged-file placeholder should be valid. With a resolvable original path, normal shortening should notify the worker, preserve prior-read rights and allow a subsequent full reread.

Evidence (Before & After)

Current before/after report. At the current head, 2,083 tests across 11 affected Core suites pass, including 49 pressure-budget cases; disabling the adaptive budget fails 23 of them. Core build/typecheck and scoped ESLint/Prettier pass. Earlier 589/590 and compiled-Core/tmux evidence stays tied to its tested commit.

At the current head, a real CLI run against a scripted loopback provider emits 10,576 characters near auto-compaction (20,000 on the base) with no compaction notice; far from the trigger both emit 20,000. The provider is a controlled fixture, not a real model. Earlier tiny-budget and compiled-Core evidence stays tied to its tested commit.

Tested on

macOS, Node 22.22.0, current commit b43c77e6eceac9f8dc6097c7d179f21518d163dd. At this head the affected Core suites, Core typecheck, ESLint and Prettier pass; the latest wire/TUI verification is linked above. Current necessary CI and human approval are tracked separately.

Risk & Scope

The tiny-budget fallback and unresolved-read whole-batch preservation can require normal compaction instead of suppressing text. The latter deliberately may exceed the selected finite character budget; existing compaction, hard admission and overflow handling apply, potentially adding text or compaction. It does not guarantee exact token admission. A pointerless preview omits detailed content when it cannot fit; persistence remains best effort under existing disk limits. Shortened file reads disarm quote-back residency while retaining prior-read rights; uniquely correlated shell shortening leaves file-read residency intact. Existing fork ownership remains unchanged. Unresolved would-cut reads in regular chat now preserve the full batch before persistence; there is no worker API change or blanket cache clear that revokes prior-read rights. The current controlled-Core fixture does not establish a new CLI, browser, real-model or cross-platform result. Line caps and defaults do not adapt.

Linked Issues

Closes #2566
Result-size accounting split to #13652 (#11536).

äļ­æ–‡čŊŽ

æŽĨčŋ‘臊åŠĻ压įžĐæ—ķæ đæŪ同äļ€čŠåĪĐ、同äļ€č·Ŋį”ąäļ”囚åۚčū“å…Ĩ和历åēäŧæœ‰æ•ˆįš„æˆåŠŸį”Ļ量æŠĨ告įžĐ小äļ‹äļ€čŊ·æą‚įš„å·Ĩ具įŧ“æžœïž›į­‰äŧ·åˆ·æ–°äŋį•™äūæŪ内åŪđ变化或åœĻ途旧响嚔äļé‡åŧščŋ‡æœŸäūæŪäđŸäļæļ…įĐšį”Ļ量čŪĄæ•°ã€‚äŋæŠĪæ–‡æœŽã€åŠ’ä―“å’Œįŧ“构先čŪĄå…Ĩä―™é‡ïžŒäļŽčšåˆéĒ„įŪ—å–čūƒįī§å€žäļ”äļé‡åĪčĢå‰Šã€‚æ”ūäļäļ‹åیæ•ī提įĪšæ—ķäŋį•™åŽŸæ–‡äšĪįŧ™åŽ‹įžĐ/įĄŽäļŠé™å‡†å…Ĩïž›åیæ•īč·Ŋåū„æ”ūäļä—ķ及发送åیæ•ī无指针提įĪšïžŒæ­ĢåļļčĢå‰Šäŧå°―力äŋå­˜åیæ•ī原文。éŧ˜čŪĪ25k字įŽĶ/1000行äŋæŒäļå˜ïžŒįŧ“æžœįŧŸčŪĄåĶåœĻ#13652。

æœŽæŽĄäŋŪåĪ PreToolUse čĄĨé―åŪžé™…čŊŧ取č·Ŋåū„、历åē原始参数äŧäļš {} įš„įž“å­˜įžšåĢã€‚č―į›˜å‰å…ˆæĢ€æŸĨ最įŧˆéĒ„č§ˆå°†čĢå‰Šįš„čŊŧ取æ™Ū通聊åĪĐæ— æģ•åŪšä―č·Ŋåū„æ—ķäŋį•™æ•īäļŠæ‰đæŽĄïžŒįĄŪäŋ worker éĐŧį•™įŠķ态äļŽæĻĄåž‹åŪžé™…æ”ķåˆ°įš„å†…åŪđäļ€č‡īïž›åŊåŪšä―čŊŧ取äŧæ­ĢåļļčĢå‰Šã€é€šįŸĨ worker åđķäŋį•™å…ˆčŊŧ权限æēĄæœ‰æ–°åĒž worker API。äū‹åĪ–æ‰đæŽĄåŊčƒ―čķ…čŋ‡æœ‰é™å­—įŽĶéĒ„įŪ—ïžŒäšĪį”ąæ­Ģåļļ压įžĐã€įĄŽå‡†å…Ĩ或æšĒ凚åĪ„į†ïžŒåŊčƒ―åĒžåŠ æ–‡æœŽå’ŒåŽ‹įžĐäļčƒ―å̰᧰įēūįĄŪ token 准å…Ĩ。双čŊ­čŪūčŪĄåŒæ­ĨčŪ°å―•ã€‚

å―“å‰æäšĪ b43c77e6eceac9f8dc6097c7d179f21518d163dd įš„ 11 äļŠå—å―ąå“ Core åĨ—äŧķå…ą 2,083 äļŠį”Ļäū‹é€ščŋ‡ïžˆåŦ 49 äļŠåŽ‹åŠ›éĒ„įŪ—į”Ļäū‹ïž›į§ŧé™Īč‡Šé€‚åš”éĒ„įŪ—æ—ķ有 23 äļŠå˜įšĒCore 构åŧš/įąŧ型æĢ€æŸĨäļŽ ESLint/Prettier 通čŋ‡ã€‚įœŸåŪž CLI + č„šæœŽåŒ– loopback provider įš„ wire/TUI äļĪ臂驌čŊč§ä–đčŊ„čŪšïžšæŽĨčŋ‘臊åŠĻ压įžĐæ—ķ发凚 10,576 字įŽĶä— æå‰åŽ‹įžĐ提įĪšïžŒåŸšįšŋäļš 20,000 字įŽĶåđķč§Ķ发压įžĐïž›čŋœįĶŧ阈倞æ—ķäļĪ者äļ€č‡ī。æ—Đ期 589/590 äļŽįž–čŊ‘ Core/tmux čŊæŪäŧįŧ‘åۚå…ķæĩ‹čŊ•提äšĪ。CI、䚚å·Ĩæ‰đ准、合åđķ和发åļƒį‹ŽįŦ‹åˆĪ断。

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Local verification for commit 38d5debb6062e74c371a9d9865ef9306783fb205 (base d0ddd020c8a64279e290538f84b152fe843ed9d4). Task source bytes were checked against the tested manifest before committing.

Fresh core/CLI builds and typechecks passed, as did changed-file ESLint/Prettier and diff checks. Targeted checks accumulated 1,416 distinct executed cases, each passing at its latest verification. Review repairs cover independent explicit char/line settings, line-only context reduction, producer metadata in speculation/ACP, and truthful no-file JSON previews. This is targeted coverage, not a full repository test run.

Terminal excerpts below are genuine captures with anonymous runtime UUIDs redacted. No internal defect URLs, customer identifiers, credentials or private local paths are included.

#2566 / #11536 final local native acceptance

All four frozen-candidate native CLI scenarios completed successfully. Actual injected character lengths agree with the anonymous provider requests; saved complete outputs pass content and digest checks. The explicit shell override is recorded as per_tool / 12,345: the label describes the declared per-tool producer policy, including its configured numeric override.

Executed the real noninteractive stream-json CLI in four owned tmux sessions, using an anonymous loopback OpenAI-compatible provider and an anonymous stdio MCP server. The first provider response supplied actual usage.prompt_tokens of 1,000 or 970,000; no seeded token-counter setter was used. The configured route has a 1,000,000-token context window. Provider usage is a controlled anchor, not a real-model token measurement.

Identity: immutable base d0ddd020c8a64279e290538f84b152fe843ed9d4 plus the frozen local candidate diff. Parent reported fresh core and CLI builds, typechecks, and focused regression gates green before execution. The CLI resolved core in this candidate worktree. Source/core/CLI JavaScript bytes were separately pinned and unchanged over these runs.

Byte set Files SHA-256
source 4505 3368c512120221daf8321a6b0ffa2d43603f489c737575957a8658938ce2fbca
core-dist 1894 2a37cc007d464e2d2b566645e05484d3a6cf5bb78656ed6b177be361723cf596
cli-dist 2599 ee16e2c52525d78bcac26952061dd3fbcb01a5bfacd2e601d944711c3f87d542
Profile First usage anchor Actual provider requests First injection raw → wire characters and reported source CLI exit / result
low 1000 3 run_shell_command: 123226 → 4687 (per_tool, budget 30000)
read_file: 20021 → 20021 (per_tool, budget absent)
0 / success
high 970000 5 run_shell_command: 123227 → 4688 (per_tool, budget 30000)
read_file: 20021 → 13712 (context, budget 18400)
0 / success
explicit 970000 5 run_shell_command: 123231 → 4692 (per_tool, budget 12345)
read_file: 12406 → 12406 (per_tool, budget absent)
0 / success
mcp 1000 4 tool_search: 260 → 260 (per_tool, budget absent)
tool_call: 120000 → 1073 (global, budget 80000)
0 / success

Low and high each request the same shell output and 20,021-character / 204-line read fixture. Low injects 24,708 characters total; high injects 18,400. The shell retains its independent 30,000-character producer cap, with a 4,687/4,688-character recovery envelope. High read_file is reduced 20,021 → 18,400 → 13,712 by the shared context budget; telemetry records context with applied budget 18,400. The full high read artifact exactly equals the input fixture.

Explicit settings are confirmed by the runtime start event as 12,345 characters / 321 lines. The shell producer and injection report applied budget 12,345 and preserve the complete saved output. The read tool itself returns “Showing lines 1-125 of 204 total lines”; its measured raw value is that produced 12,406-character result, not the entire 20,021-character file. No context reduction event occurs in this profile. The 321-line value is loaded, but this 204-line read fixture does not independently prove a binding 321-line cutoff. Recovery metadata/envelope can exceed the content threshold.

Observed source-label boundary: producer and both actual injection events for shell-budget-call report budget_source=per_tool, applied_budget=12345. Current coreToolScheduler.ts:6049–6054 classifies a declared maxOutputChars as per-tool, while ShellTool.maxOutputChars (shell.ts:5743–5745) derives its value from configuration. For this char-only Shell policy, per_tool identifies the declared producer policy including its configured numeric override; global tools/MCP use global or explicit. The numeric override is preserved and no context reduction occurs in the explicit scenario. This observation does not imply that Shell emits source=explicit.

MCP uses the actually advertised tool_search schema, receives mcp__native-budget__budget_json, then invokes that discovered name via the actually advertised tool_call. The result is 120,000 characters of valid JSON. Provider request 3 receives a 1,073-character structured recovery sample; producer and injection events both report raw 120,000, applied budget 80,000, source global, and truncated=true. The saved file is byte-for-byte equal to the known complete JSON, parses successfully, and its SHA-256 equals the wire digest: 4f98356ddb2c0480aa6be48832e4982d6d0bd69c343f43ad204e5c6d0f4ca50e. The envelope includes total characters/lines, a digest, file path, and offset/limit paging guidance. Complete recovery was checked from the saved file locally; a subsequent model-driven paging call was not part of this run.

Each shell full artifact contains the complete known shell payload, including the product command/result wrapper (123,226 / 123,227 / 123,231 characters and 3,008 lines). Wire SHA-256 values match saved output SHA-256. Full recovered files are retained under each profile’s recovered-evidence/.

The native request totals include product behavior rather than a fixed assertion: low has main response + tool continuation + managed-memory scan (3); high and explicit have two compression-related requests (including one without tools), followed by a resumed main continuation and managed-memory scan (5); MCP has discovery + invocation + answer + managed-memory scan (4). Repeated injection records correlate with a subsequent managed-memory side query carrying the tool history. Request purposes and timestamps support this attribution; the logs lack a chat-instance identifier, so the native run does not directly prove per-chat deduplication or conclusively exclude duplicate sinks. Retry/history deduplication is covered separately by targeted chat tests. Histograms qwen-code.tool.result.chars and qwen-code.tool.result.tokens are present in the supported local telemetry file. The token fields are estimates; no remote OTLP/backend or real-provider savings measurement was performed.

Actual tmux captures are retained for all four profiles (low 91,033 bytes; high 91,054; explicit 70,764; MCP 70,866). The following blocks are excerpts of captured terminal bytes, preserving their native line wrapping; full capture files are PROFILE/tmux-capture.txt.

low:

{"type":"result","subtype":"success","uuid":"[anonymous-run-id]","session_id":"[anonymous-run-id]","is_error":false,"duratio
n_ms":643,"duration_api_ms":359,"num_turns":2,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":3000,"output_tokens":150,"cache_read_input_tokens":0,"tot
al_tokens":3150},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C

high:

{"type":"result","subtype":"success","uuid":"[anonymous-run-id]","session_id":"[anonymous-run-id]","is_error":false,"duratio
n_ms":738,"duration_api_ms":406,"num_turns":2,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":4850000,"output_tokens":250,"cache_read_input_tokens":0,"
total_tokens":4850250},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C

explicit:

{"type":"result","subtype":"success","uuid":"[anonymous-run-id]","session_id":"[anonymous-run-id]","is_error":false,"duratio
n_ms":774,"duration_api_ms":427,"num_turns":2,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":4850000,"output_tokens":250,"cache_read_input_tokens":0,"
total_tokens":4850250},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C

mcp:

{"type":"result","subtype":"success","uuid":"[anonymous-run-id]","session_id":"[anonymous-run-id]","is_error":false,"duratio
n_ms":576,"duration_api_ms":407,"num_turns":3,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":4000,"output_tokens":200,"cache_read_input_tokens":0,"tot
al_tokens":4200},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C

Historical evidence remains separate: low-first/, high-first/, explicit-first/, and mcp-uninvoked/ preserve the pre-final runs. The first explicit/MCP tmux sessions expired before capture; the original MCP run never invoked the deferred tool and is not counted as acceptance. The final run used retained sessions and real discovery. One initial final-run provider readiness probe inherited the host Python proxy and timed out; the direct no-proxy localhost probe passed before any CLI started.

Scope verdict: the tested adaptive defaults, explicit configuration loading, native shell/read injection accounting, MCP global reduction, structured spill preview, and saved-output integrity pass. Shell source-label interpretation is the documented boundary above; it is not a failed numeric-budget check. This is bounded controlled-provider acceptance, not full closure of every original #11536 pipeline/ACP/speculation/backend requirement or proof of representative real-model savings. No source, dependency, Git, GitHub, HOME/auth configuration, or provider infrastructure was changed by this verification.

äļ­æ–‡čĄĨå……ïžšå››įŧ„均通čŋ‡įœŸåŪž CLI → 朎地åŒŋ名 provider įš„åŪžé™…čŊ·æą‚č·Ŋåū„åđķäŋį•™įœŸåŪž tmux čū“凚。éŦ˜åŽ‹åŠ›éĒ„įŪ—įĄŪåŪžå‡å°‘åŪžé™…æģĻå…ĨMCP åیæ•ī JSON 和 shell/read č―į›˜å†…åŪđ均åŊæĒåĪã€‚æ˜ū垏 shell įš„åŪžé™…å­—æŪĩæ˜Ŋ per_tool / 12,345čĄĻįĪšåĢ°æ˜Žįš„å·Ĩå…·įš§į­–į•Ĩ及å…ķ配į―Ū倞数倞č͆ᛖåū—到äŋį•™ïž›æœŽæŽĄäļæŠŠ ACP/æŽĻæĩ‹æ‰§čĄŒį­‰æœŠč·‘č·Ŋåū„、čŋœįŦŊæŒ‡æ ‡æˆ–įœŸåŪžæĻĄåž‹ token æ”ķį›Šå―“æˆå·ē驌æ”ķ。历åēæœŠč°ƒį”Ļ MCP äļŽäļĒåĪą tmux įš„č―ŪæŽĄäŧ…äŋį•™åĪąčīĨ/限åˆķčŊæŪäļįŪ—é€ščŋ‡ã€‚

@yiliang114

Copy link
Copy Markdown
Collaborator Author

First CI snapshot for 38d5debb6062e74c371a9d9865ef9306783fb205: the TUI parity job failed inside checkout, at git sparse-checkout disable, with missing Git objects (unable to read sha1 file). Dependency installation and tests did not start. This failure is separate from the completed local core/CLI build, typecheck, regression and native CLI evidence above.

A single job-specific rerun request was rejected by GitHub with HTTP 403 because the containing workflow is still running. No product or workflow workaround was added. Other checks are pending; this PR is ready for review, with CI completion still pending.

äļ­æ–‡ïžšéĶ–äļŠåĪąčīĨä―äšŽ checkout įš„ Git åŊđ蹥čŊŧ取é˜ķæŪĩ尚朊åŪ‰čĢ…äūčĩ–æˆ–æ‰§čĄŒæĩ‹čŊ•äļčƒ―æŪæ­ĪåˆĪåŪšäš§å“å›žå―’ã€‚å·ē尝čŊ•äļ€æŽĄäŧ…é‡č·‘čŊĨ jobGitHub 因æ•īäļŠ workflow 尚åœĻčŋčĄŒč€Œæ‹’įŧïžˆ403。朎地銌čŊäļŽč„ąæ• tmux æŠĨå‘Šč§äļŠïž›PR å·ēåŊ reviewïžŒä―† CI 尚朊åŪŒæˆã€‚

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Qwen Triage finished — view run. See the stage comments in this thread for the result.

✅ Qwen Triage å·ēåŪŒæˆ —— æŸĨįœ‹čŋčĄŒã€‚įŧ“æžœč§æœŽįšŋįĻ‹äļ­įš„各é˜ķæŪĩčŊ„čŪšã€‚

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Re-running the gate at the current head b69dfdd1. Two commits landed since the last triage pass: a mechanical merge of origin/main, then fix(core): carry trusted recovery metadata across tool budgets — which is not a touch-up but a redesign of the recovery-handle mechanism, and it is the reason this pass reads differently from the last one.

Template ✓ — What / Why / Reviewer Test Plan (How to verify · Evidence · Tested on) / Risk & Scope / Linked Issues all present.

Problem — observed, not theoretical. This implements #2566, still open, carrying priority/P3, need-discussion, scope/token-management, roadmap/context-performance. The limitation — static tool budgets pushing a long session over the compaction trigger sooner than necessary — is stated in the issue and in the design doc this PR adds. No reproduction gap. All 81 review threads currently read resolved.

Direction — aligned with the core context-management mission. The two flags from the previous pass both still stand, and neither is a gate block:

  • feat(core): dynamic tool output truncation based on context pressure #2566 sketched a compact formula (max(500, floor(baseLines·(1−usageRatio))), char cap 4·remainingTokens). What landed is a considerably larger mechanism. Whether that breadth is right for a P3 is a product-direction call for a human.
  • The value case is still open on the author's own terms. The 2026-10-17 scope note asked for evidence of how often the valid-anchor path is reached in ordinary sessions; the 2026-10-18 measurement concluded 倞åū—įŧ§įŧ­æŽĻčŋ›ä―†æš‚äļå€žåū—č―Žæ­Ģ, gated on the Critical fixes plus a re-measurement. The fixes landed; I still don't see the re-measurement.

Size — core paths touched (packages/core/src/**). Of the 2,388 changed lines: production logic 885 (llm-chat.ts 405, tool-response-finalizer.ts 273, agent.ts 89, tool-output-recovery.ts 37 new, coreToolScheduler.ts 24, transcript-marker.ts 20 new, microcompact.ts 13, fileReadCache.ts 7, tokenEstimation.ts 7, web-fetch.ts 7, tools.ts 3), tests 1,439, design docs 64. Production lines grew from 660 to 885 since the last pass — above the 500-line feat awareness threshold, below the 1,000-line large-PR advisory. The author is a repo maintainer (admin), so per AGENTS.md the two-tier core gate is exempt: no hard block, no mandatory size escalation. Reported for transparency.

Approach — the redesign is the right call and I want to say so plainly. The previous mechanism recovered a tool result's only handle back to its spilled artifact by regex-matching rendered text (persistedOutputPointerRange, transcriptMarkerRange). Three separate Criticals (R12-1, R16-1, R18-1) were instances of one root cause: text matching cannot enumerate the set of strings that look like a handle, so forged, severed and stale handles were all reachable, and the recognisers were not idempotent under their own consumer's output. b69dfdd1 deletes both recognisers outright and carries the handle as producer-owned data instead — recoveryText on ToolResult, threaded through convertToFunctionResponse onto the Part. fitText's new comment states the invariant directly: "Remove only producer-owned data, never recognize handles in tool text." That closes the class rather than another instance, which is what the round-18 convergence note asked for.

The cost is scope. The change now reaches coreToolScheduler.ts, tools.ts, web-fetch.ts and agent.ts — the tool-result contract itself — where before it was contained to the finalizer and the send boundary. That is a real widening of blast radius, and Stage 2 found the regression sitting exactly there.

Risk — no Stage 1e high-risk path match (paginated file list checked against the revert-correlated patterns; none of the 11 production files match). One honest caveat the mechanical check cannot see: the regression Stage 2 reports surfaces in packages/cli/src/acp-integration/, which is a revert-correlated path. The PR does not edit that directory, but its new read of the tool-result contract breaks a pinned invariant there. Treat the acp-integration surface as the focus for re-review.

Moving on to code review. 🔍

äļ­æ–‡čŊŽ

åœĻå―“å‰ head b69dfdd1 é‡č·‘å‡†å…Ĩé—Ļæ§›ã€‚č·äļŠæŽĄ triage äđ‹åŽč―乆äļĪäļŠ commitïžšäļ€æŽĄæœšæĒ°åžįš„ origin/main 合åđķäŧĨ及 fix(core): carry trusted recovery metadata across tool budgetsâ€”â€”åŽč€…äļæ˜Ŋ小äŋŪčĄĨïžŒč€Œæ˜ŊåŊđ「æĒåĪåĨ柄」朚åˆķįš„é‡æ–°čŪūčŪĄïžŒäđŸæ˜Ŋ朎č―Ūįŧ“čŪšäļŽäļŠäļ€č―ŪäļåŒįš„原因。

æĻĄæŋ ✓ —— What / Why / Reviewer Test PlanHow to verify · Evidence · Tested on/ Risk & Scope / Linked Issues å‡é―å…Ļ。

é—Ūéǘ —— å·ē观æĩ‹ïžŒäļæ˜ŊᐆčŪšæ€§åŠ å›šã€‚æœŽ PR åŪžįŽ°įš„æ˜Ŋäŧį„ķ垀æ”ūįš„ #2566åļĶ priority/P3、need-discussion、scope/token-management、roadmap/context-performance。å…ķåą€é™â€”â€”é•ŋ侚čŊäļ­é™æ€å·Ĩ具éĒ„įŪ—äžšæŊ”åŋ…č́æ›īæ—Đ把äļŠä–‡æŽĻčŋ‡åŽ‹įžĐč§Ķ发įšŋ——åœĻ issue 和朎 PR 新åĒžįš„čŪūčŪĄæ–‡æĄĢäļ­éƒ―有描čŋ°ã€‚äļå­˜åœĻåĪįŽ°įžšåĢ。81 æĄ review įšŋįĻ‹å―“å‰å…ĻéƒĻäļšå·ēč§Ģå†ģ。

æ–đ向 —— äļŽæ ļåŋƒįš„äļŠä–‡įŪĄį†ä―ŋå‘―äļ€č‡ī。äļŠäļ€č―Ūįš„äļĪį‚đ提醒äūį„ķ成įŦ‹ïžŒäļ”éƒ―äļæ˜Ŋé—Ļ槛é˜ŧ断éĄđïžš

  • feat(core): dynamic tool output truncation based on context pressure #2566 原朎įŧ™å‡šįš„æ˜Ŋäļ€äļŠįī§å‡‘兎垏max(500, floor(baseLines·(1−usageRatio)))字įŽĶäļŠé™ 4·remainingTokensïž‰ã€‚č―åœ°åŪžįŽ°įš„æœšåˆķč́åΧåū—åĪšã€‚čŋ™äļŠåđŋåšĶåŊđ P3 æ˜ŊåĶåˆé€‚ïžŒåąžäšŽåš”į”ąäššæĨåšįš„äš§å“æ–đ向å†ģį­–ã€‚
  • äŧ·å€žčŪščŊæŒ‰ä―œč€…č‡Šå·ąčŪūåŪšįš„æ ‡å‡†äŧæœŠé—­åˆã€‚2026-10-17 įš„čŒƒå›īčŊŽčĶæą‚å…ˆæœ‰čŊæŪčŊŽæ™Ū通侚čŊäļ­æœ‰æ•ˆé”šį‚đč·Ŋåū„åΚäđ…čĒŦå‘―äļ­äļ€æŽĄïž›2026-10-18 įš„æĩ‹é‡įŧ™å‡šã€Œå€žåū—įŧ§įŧ­æŽĻčŋ›ä―†æš‚äļå€žåū—č―Žæ­Ģã€ïžŒæĄäŧķæ˜Ŋ Critical äŋŪåĪåŠ äļŠäļ€æŽĄé‡æ–°æĩ‹é‡ã€‚äŋŪåĪå·ēč―åœ°ïžŒé‡æ–°æĩ‹é‡äŧæœŠįœ‹åˆ°ã€‚

规æĻĄ —— č§Ķ及æ ļåŋƒč·Ŋåū„packages/core/src/**。2,388 čĄŒå˜æ›īįš„æ‹†åˆ†ïžšį”Ÿäš§é€ŧčū‘ 885 čĄŒïžˆllm-chat.ts 405、tool-response-finalizer.ts 273、agent.ts 89、tool-output-recovery.ts 新åĒž 37、coreToolScheduler.ts 24、transcript-marker.ts 新åĒž 20、microcompact.ts 13、fileReadCache.ts 7、tokenEstimation.ts 7、web-fetch.ts 7、tools.ts 3、æĩ‹čŊ• 1,439 čĄŒã€čŪūčŪĄæ–‡æĄĢ 64 čĄŒã€‚į”Ÿäš§čĄŒæ•°äŧŽäļŠäļ€č―Ūįš„ 660 åĒžč‡ģ 885——éŦ˜äšŽ 500 čĄŒįš„ feat å…ģæģĻé˜ˆå€žïžŒä―ŽäšŽ 1,000 čĄŒįš„åΧ PR åŧščŪŪé˜ˆå€žã€‚ä―œč€…æ˜Ŋäŧ“åš“įŧĪč€…ïžˆadmin按 AGENTS.md æ ļåŋƒäļĪįš§é—Ļ槛åŊđå…ķčąå…ïžšäļįĄŽé˜ŧ断äđŸäļåžšåˆķæŒ‰č§„æĻĄäŠĨ。æ­ĪåĪ„äŧ…äļšé€æ˜ŽčŊŽã€‚

æ–đæĄˆ —— čŋ™æŽĄé‡æ–°čŪūčŪĄæ–đ向æ­ĢįĄŪ我æƒģ明įĄŪčŊī凚æĨ。æ­Īå‰įš„æœšåˆķ通čŋ‡åŊđå·ēæļ翟“文朎做æ­Ģ则åŒđ配persistedOutputPointerRange、transcriptMarkerRangeæĨæ‰ū回å·Ĩ具įŧ“果通åū€æšĒ凚䚧į‰Đįš„å”Ŋäļ€åĨ柄。äļ‰äļŠį‹ŽįŦ‹įš„ CriticalR12-1、R16-1、R18-1å…ķåŪžæ˜Ŋ同ä đå› įš„åŪžäū‹ïžšæ–‡æœŽåŒđ配无æģ•æžšäļūã€Œįœ‹čĩ·æĨ像åĨæŸ„ã€įš„å­—įŽĶäļē集合因æ­Ī䞊造、戊断、čŋ‡æœŸåĨ柄å…Ļéƒ―åŊčūūïžŒč€Œäļ”čŊ†åˆŦå™ĻåœĻ臩čšŦæķˆčīđč€…įš„čū“凚äļŠäļå…·åđ‚į­‰æ€§ã€‚b69dfdd1 į›īæŽĨ删掉䚆čŋ™äļĪäļŠčŊ†åˆŦå™Ļæ”đäļšæŠŠåĨæŸ„ä―œäļšį”Ÿäš§č€…č‡Šæœ‰įš„æ•°æŪ侠递——ToolResult äļŠįš„ recoveryTextįŧ convertToFunctionResponse 挂到 Part äļŠã€‚fitText įš„æ–°æģĻ释į›īæŽĨ写明䚆äļå˜é‡ïžšã€ŒåŠį§ŧé™Īį”Ÿäš§č€…č‡Šæœ‰įš„æ•°æŪįŧäļåœĻå·Ĩ具文朎äļ­čŊ†åˆŦåĨ柄」。čŋ™å…ģé—­įš„æ˜Ŋæ•īäļŠįąŧåˆŦ而äļæ˜Ŋ又äļ€äļŠåŪžäū‹ïžŒæ­Ģæ˜ŊįŽŽ 18 č―Ūæ”ķæ•›æ„č§æ‰€čĶæą‚įš„ã€‚

äŧĢäŧ·æ˜Ŋ范å›ī。æ”đåŠĻįŽ°åœĻč§Ķ及 coreToolScheduler.ts、tools.ts、web-fetch.ts 和 agent.ts——åģå·Ĩ具įŧ“æžœåĨ‘įšĶ朎čšŦ——而æ­Īå‰åŠåą€é™åœĻ finalizer äļŽå‘送čūđį•Œã€‚čŋ™æ˜Ŋå―ąå“éĒįš„åŪžčīĻæ‰ĐåĪ§ïžŒč€Œ Stage 2 å‘įŽ°įš„å›žå―’æ°åĨ―å°ąč―åœĻé‚Ģ里。

éĢŽé™Đ —— æœŠå‘―äļ­ Stage 1e éŦ˜éĢŽé™Đč·Ŋåū„å·ēį”Ļ分éĄĩ文äŧķ列čĄĻåŊđį…§äļŽå›žæŧšį›ļå…ģįš„æĻĄåžïž›11 äļŠį”Ÿäš§æ–‡äŧķ均äļåŒđé…ïž‰ã€‚ä―†æœ‰äļ€į‚đ朚æĒ°æĢ€æŸĨįœ‹äļåˆ°įš„åĶčŊšæé†’ïžšStage 2 æŠĨå‘Šįš„å›žå―’æšīéœēåœĻ packages/cli/src/acp-integration/ïžŒč€Œé‚Ģæ­Ģæ˜ŊäļŽå›žæŧšį›ļå…ģįš„č·Ŋåū„。朎 PR åđķæœŠäŋŪæ”đčŊĨį›Ūå―•ïžŒä―†åۃåŊđå·Ĩ具įŧ“æžœåĨ‘įšĶįš„æ–°čŊŧ取į ī坏䚆é‚Ģ里čĒŦé’‰ä―įš„äļå˜é‡ã€‚åĪåŪĄæ—ķčŊ·æŠŠ acp-integration éĒä―œäļšé‡į‚đ。

čŋ›å…ĨäŧĢ᠁åŪĄæŸĨ。🔍

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

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

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Code review — read the full production diff at b69dfdd1, with the new commit's redesign as the focus. Two new Criticals, both confirmed against the head blobs and both corroborated by red CI. First, the good news, because it is substantial.

The three standing Criticals are genuinely fixed at this head. I verified each in the code rather than taking the resolved-thread flag (all 81 threads now read resolved) or the author's word:

  • R12-1 (recovery handle recovered by regex-matching rendered text, so forged handles from untrusted tool output were hoisted into the system's own notice slot). persistedOutputPointerRange is deleted from truncation.ts — I grepped the raw blob at head, not just the diff. Nothing parses a path out of text any more.
  • R16-1 (the recognisers were not idempotent under fitText's own output, so a second shortening pass sliced through the path the first preserved). transcriptMarkerRange is deleted from transcript-marker.ts. With no recognisers there is nothing to be non-idempotent; the handle now rides on the Part. This is the correct fix shape, not a patch on the gate condition — though see Critical 2 for what it now depends on.
  • R18-1 (the preserved transcript handle was charged 1:1 against previewBudget, severing the termination reason the producer sizes itself to protect). composeIncompleteResult now returns recoveryText: prefix + marker — reason, header and marker as one producer-owned unit — and minimumRecoveryBudget re-prices the veto threshold to recoveryHeader(files).length + (text ? text.length + 2 : 0) + 3, which matches fitText's own pointer.length + preservedSize + 3 <= maxChars keep-condition exactly. Both halves of what R18-1 asked for, including the threshold re-pricing it warned would otherwise drop the slot into the total-loss band.

This closes a root cause rather than another instance, which is what the round-18 convergence note recommended.

Critical 1 — a successful tool result is now converted into an error when optional metadata throws. In convertToFunctionResponse (packages/core/src/core/coreToolScheduler.ts:1031-1038), the new read sits in the return expression, outside the try whose catch comment is literally // Optional observation metadata must not change a tool's outcome.:

  } catch {
    // Optional observation metadata must not change a tool's outcome.
  }
  return attachToolOutputProvenance(
    attachToolOutputRecovery(
      convertToFunctionResponseParts(toolName, callId, llmContent),
      {
        text: producerResult?.recoveryText,
        persistedOutputFiles: producerResult?.persistedOutputFiles,  // unguarded
      },
    ),

At main that return reads no persistedOutputFiles at all — the PR adds it, and producerResult is the live ToolResult. Two pre-existing tests pin this invariant by defining a throwing getter, and neither file is touched by this PR:

FAIL src/acp-integration/session/Session.test.ts > Session > runToolCalls
     > ignores throwing optional metadata on a successful tool result
AssertionError: expected { Object (error) } to deeply equal { output: 'completed' }
-   "output": "completed",
+   "error": "persisted output unavailable",

The received string is the test's own new Error('persisted output unavailable'), so the throw escapes and the successful result comes back as an error. Same shape in src/followup/speculation.test.ts (expected { output: 'file contents' }, got { error: 'persisted output unavailable' }). This is a live production surface, not a test-only shape: Session.ts:17471 passes toolResult straight through as producerResult. Fix is small — read both fields inside the existing try, defaulting to undefined on throw.

Critical 2 — the symbol side-channel does not survive serialization, and R16-1's fix now rests on it. attachToolOutputRecovery is applied unconditionally, so every tool-response Part in the system carries Symbol(tool-output-recovery) even when both fields are undefined. Symbols do not survive JSON.stringify:

FAIL src/agents/runtime/agent-core.test.ts > AgentCore.prepareTools
     > hard-caps the aggregate subagent tool response
AssertionError: expected [ Array(2) ] to deeply equal [ â€Ķ(2) ]
-     Symbol(tool-output-recovery): { "persistedOutputFiles": [] },
âŊ src/agents/runtime/agent-core.test.ts:2193   expect(transcriptParts).toEqual(parts);

The red assertion is the smaller half. finalizeToolResponses now uses that symbol as the durable source of the artifact path, and that read is what makes a second shortening pass idempotent — i.e. it is the R16-1 fix:

const previousFiles =
  entry.persistedOutputFiles ??
  entry.responseParts.map(getToolOutputRecovery)
    .find((r) => r?.persistedOutputFiles !== undefined)?.persistedOutputFiles;
if (previousFiles !== undefined) { /* keep, do not re-persist */ continue; }

It holds in-turn, where Parts are live objects. It does not hold across anything that round-trips through JSON — session restore, replay, rewind, recording. The merge this PR just absorbed landed fix(cli): preserve checkpoint restore ownership and cold continuation and feat(cli): preserve OpenTUI session replay and rewind, so restore-then-resend is a live path here, not a hypothetical. Please state which of those paths can re-enter the finalizer and pin it with a test; if none can, say so in the design doc, because the fix's durability currently rests on an unstated assumption.

Two Suggestions, neither blocking:

  • Attaching only when text or persistedOutputFiles is actually set would have kept the divergence off the overwhelming majority of Parts and left agent-core.test.ts green. Unconditional attach buys nothing and maximizes blast radius.
  • fitText's new scrub loop (body.split(owned).join('')) removes every occurrence of the pointer/preserved/path strings, including inside legitimate untrusted output. Correct fail-closed direction for anti-forgery, but it silently deletes a path a tool genuinely printed — worth a one-line comment saying that is deliberate.

The new recovery channel is the crux of the redesign, and its leak is the last step:

sequenceDiagram
    participant P1 as Tool producer
    participant P2 as Scheduler conversion
    participant P3 as Part with recovery symbol
    participant P4 as Response finalizer
    participant P5 as Transcript writer
    P1->>P2: ToolResult carrying recoveryText and persistedOutputFiles
    P2->>P3: attachToolOutputRecovery on every converted part
    P3->>P4: send boundary or batch budget pass
    P4->>P4: minimumRecoveryBudget prices header plus preserved unit
    P4-->>P3: shortened text with recovery re-attached
    P3->>P5: recorded as JSON
    P5-->>P3: symbols do not survive the round trip
Loading
Files changed (21 of 21 shown)
File What changed
packages/core/src/tools/tool-output-recovery.ts New 37-line module — the symbol-keyed recovery channel (get, set, attach) that replaces both text recognisers
packages/core/src/tools/tool-response-finalizer.ts Slots carry recovery data not a range; recoveryHeader and minimumRecoveryBudget extracted; producer-owned scrub loop; re-price of both veto thresholds; previousFiles recovered from the symbol
packages/core/src/tools/truncation.ts persistedOutputPointerRange deleted — no net change against main any more
packages/core/src/tools/agent/transcript-marker.ts Reduced to buildTranscriptMarker; transcriptMarkerRange deleted
packages/core/src/tools/agent/agent.ts composeIncompleteResult returns reason plus marker as one recoveryText unit; three call sites thread it onto ToolResult
packages/core/src/core/coreToolScheduler.ts Widens the producerResult pick and attaches recovery at three conversion sites — Critical 1 lives here
packages/core/src/tools/tools.ts New recoveryText field on the ToolResult contract
packages/core/src/tools/web-fetch.ts Forwards its real persisted extracted-text path on both the success and the processing-failed fallback
packages/core/src/core/llm-chat.ts Send boundary uses minimumFunctionResponseContent and adds its budgetedChars back onto the pressure budget
packages/core/src/services/fileReadCache.ts Bulk quote-back disarm that retains prior-read rights
packages/core/src/services/microcompaction/microcompact.ts Exports FILE_PATH_TOOLS and getFunctionCallIdentity; normalises the recorded read path with unescapePath
packages/core/src/services/tokenEstimation.ts estimatePromptTokens widened to accept a Content array
docs/design/pressure-aware-tool-output.md English design doc
docs/design/pressure-aware-tool-output.zh-CN.md Chinese twin, reciprocal link, matching structure
packages/core/src/core/llm-chat.test.ts Pressure-aware submission suite, anchor validity, fixed inputs
packages/core/src/tools/tool-response-finalizer.test.ts Rewritten around recovery data — forgery, pointerless notice, sub-notice veto
packages/core/src/tools/tool-response-finalizer.integration.test.ts Real artifact written and read back across a rebuild without entry-level metadata
packages/core/src/tools/agent/agent.test.ts Real AgentTool TIMEOUT and MAX_TURNS at 1000, 700 and 100 chars
packages/core/src/tools/web-fetch.test.ts Persisted-path forwarding on both branches
packages/core/src/services/microcompaction/microcompact.test.ts Escaped-argument normalisation regression
packages/core/src/services/fileReadCache.test.ts Coverage for the bulk disarm

CI evidence — unattended re-run (GITHUB_EVENT_NAME=issue_comment), so I did not build or run any PR code. The table is the PR's own CI on the reviewed commit, read via the API:

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

Check Conclusion
Test (ubuntu-latest, Node 22.x) ❌ failure
Check TUI impact ✅ success
Classify PR ✅ success
Installed npm Node26 private Bun acceptance ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Lint & Static (ubuntu-latest, Node 22.x) ✅ success
Managed Agent gate ✅ success
OpenTUI no-flicker gate ✅ success
Select the Managed Agent tier ✅ success
TUI parity snapshots (ink vs opentui) ✅ 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åĪąčīĨéĄđ排åœĻ最前。

This red is PR-caused, not infra noise, and I want to be explicit about why I am calling it that rather than letting it pass as a flake. All three failures are deterministic assertion mismatches whose received values are produced by the new recovery mechanism: two carry the literal message of a throwing persistedOutputFiles getter that only the new unguarded read reaches, and the third diffs on Symbol(tool-output-recovery) — a symbol this PR introduces and nothing else in the tree creates. None of the three test files is touched by the PR, so none is a stale expectation. Counts: packages/cli 1 failed / 45,765 passed; packages/core 2 failed / 38,834 passed. The log's trailing Terminated and exit code 1 are the npm lifecycle aborting after the failing suite, not a separate infra event.

Not verified, and why: the macOS and Windows lanes and the CLI integration suite were skipped again, for a change whose new logic is a tool-result contract widening plus path handling — the two POSIX-only unescapePath assertions already flagged in round 17 have still never run on a real Windows runner. The author's own report (macOS, Node 22.22.0, focused suites green at 224c21e8) is the author's claim, not evidence I re-ran, and it predates both this head and this red CI.

Sandboxed verification: a /verify run is already in flight on this head (announced 07:20, run 40170874477) — no need to trigger a duplicate. What it would settle that static review cannot: whether the recovery handle actually survives two shortening passes and a JSON round-trip, and whether a successful tool result stays successful when optional metadata throws. Worth saying plainly though — /verify on a head whose own unit suite is red will mostly reproduce these three failures. The higher-value next step is fixing Critical 1 and Critical 2 and getting CI green; the author has write access, so both /verify and /tmux are available afterwards.

äļ­æ–‡čŊŽ

äŧĢ᠁åŪĄæŸĨ —— å·ēčŊŧåی b69dfdd1 įš„å…ĻéƒĻį”Ÿäš§ diff重į‚đæ˜Ŋ新 commit įš„é‡æ–°čŪūčŪĄã€‚å‘įŽ°äļĪäļŠæ–°įš„ Critical均å·ēåŊđį…§ head 文äŧķ内åŪđæ ļåŪžïžŒåđķæœ‰įšĒč‰ē CI ä―čŊã€‚å…ˆčŊīåĨ―æķˆæŊ因äļšåŪƒåˆ†é‡äļå°ã€‚

äļ‰äļŠæœŠå†ģ Critical åœĻå―“å‰ head įĄŪåŪžäŋŪåĨ―䚆。 我逐äļ€åœĻäŧĢį é‡Œæ ļåŊđïžŒč€Œäļæ˜Ŋ采äŋĄįšŋįĻ‹ resolved 标čŪ°ïžˆ81 æĄįšŋįĻ‹įŽ°å·ēå…ĻéƒĻ resolvedïž‰æˆ–ä―œč€…įš„čŊīæģ•ïžš

  • R12-1通čŋ‡åŊđå·ēæļ翟“文朎做æ­Ģ则åŒđ配æĨæ‰ū回æĒåĪåĨ柄åŊžč‡īäļåŊäŋĄå·Ĩ具čū“å‡šäžŠé€ įš„åĨ柄čĒŦ提升čŋ›įģŧįŧŸč‡Šå·ąįš„通įŸĨæ§―ä―ïž‰ïžšpersistedOutputPointerRange å·ēäŧŽ truncation.ts 删é™Ī——我 grep įš„æ˜Ŋ head įš„åŽŸå§‹æ–‡äŧķ内åŪđäļåŠæ˜Ŋ diffã€‚įŽ°åœĻæēĄæœ‰äŧŧä―•äŧĢį å†äŧŽæ–‡æœŽé‡Œč§Ģ析č·Ŋåū„。
  • R16-1čŊ†åˆŦå™ĻåœĻ fitText 臩čšŦčū“凚äļŠäļå…·åđ‚į­‰æ€§ïžŒįŽŽäšŒæŽĄčĢå‰Šäžšåˆ‡įĐŋįŽŽäļ€æŽĄäŋä―įš„č·Ŋåū„transcriptMarkerRange å·ēäŧŽ transcript-marker.ts 删é™Ī。æēĄæœ‰čŊ†åˆŦå™ĻïžŒå°ąæ— æ‰€č°“åđ‚į­‰ïž›åĨæŸ„įŽ°åœĻ挂åœĻ Part äļŠã€‚čŋ™æ˜Ŋæ­ĢįĄŪįš„äŋŪåĪå―Ēæ€ïžŒč€Œäļæ˜ŊåœĻé—Ļæ§›æĄäŧķä‰“čĄĨäļâ€”â€”ä―†åŪƒįŽ°åœĻäūčĩ–äŧ€äđˆïžŒč§ Critical 2。
  • R18-1čĒŦäŋį•™įš„č―Žå―•åĨ柄按 1:1 čŪĄå…Ĩ previewBudgetïžŒåˆ‡æ–­äš†į”Ÿäš§č€…į‰đ意限é•ŋäŋæŠĪįš„įŧˆæ­Ē原因composeIncompleteResult įŽ°åœĻčŋ”回 recoveryText: prefix + marker——reason、header、marker ä―œäļšäļ€äļŠį”Ÿäš§č€…č‡Šæœ‰å•å…ƒâ€”â€”äļ” minimumRecoveryBudget 把åĶå†ģ阈倞重新åۚäŧ·äļš recoveryHeader(files).length + (text ? text.length + 2 : 0) + 3äļŽ fitText 臩čšŦįš„ pointer.length + preservedSize + 3 <= maxChars äŋį•™æĄäŧķåیå…Ļäļ€č‡ī。R18-1 čĶæą‚įš„äļĪåŠéƒ―åšåˆ°äš†ïžŒåŒ…æ‹Žåۃč­Ķ告čŋ‡ã€åĶ则䞚čŪĐæ§―ä―č―å…Ĩå…Ļ损åļĶįš„é˜ˆå€žé‡åۚäŧ·ã€‚

čŋ™å…ģé—­įš„æ˜Ŋæ đå› č€Œéžåˆäļ€äļŠåŪžäū‹ïžŒæ­Ģæ˜ŊįŽŽ 18 č―Ūæ”ķæ•›æ„č§æ‰€åŧščŪŪįš„ã€‚

Critical 1 —— å―“åŊ选元数æŪ抛垂åļļæ—ķïžŒæˆåŠŸįš„å·Ĩ具įŧ“æžœįŽ°åœĻ侚čĒŦč―Žæˆé”™čŊŊ。 åœĻ convertToFunctionResponsepackages/core/src/core/coreToolScheduler.ts:1031-1038äļ­ïžŒæ–°åĒžįš„čŊŧå–ä―äšŽ return čĄĻčūū垏里åœĻ try äđ‹åĪ–ïžŒč€Œé‚ĢäļŠ catch įš„æģĻé‡ŠåŽŸæ–‡å°ąæ˜Ŋ // Optional observation metadata must not change a tool's outcome.。main äļŠįš„čŋ™äļŠ return åیå…ĻäļčŊŧ persistedOutputFiles——æ˜Ŋ朎 PR 加äļŠįš„ïžŒč€Œ producerResult å°ąæ˜Ŋæīŧįš„ ToolResult。有äļĪäļŠæ—Ē有æĩ‹čŊ•通čŋ‡åۚäđ‰æŠ›åž‚åļļįš„ getter é’‰ä―äš†čŋ™äļŠäļå˜é‡ïžŒäļĪäļŠæ–‡äŧķéƒ―æœŠčĒŦ朎 PR äŋŪæ”đ。æ”ķåˆ°įš„å­—įŽĶäļēæ­Ģæ˜Ŋæĩ‹čŊ•č‡Šå·ąįš„ new Error('persisted output unavailable')čŊŽåž‚åļļ逃é€ļ、成功įŧ“æžœäŧĨ错čŊŊčŋ”回。speculation.test.ts æ˜Ŋ同äļ€å―Ēæ€ã€‚čŋ™æ˜ŊᜟåŪžį”Ÿäš§éĒ而非äŧ…æĩ‹čŊ•å―Ēæ€ïžšSession.ts:17471 把 toolResult į›īæŽĨä―œäļš producerResult äž å…Ĩ。äŋŪåĪåūˆå°â€”—把äļĪäļŠå­—æŪĩįš„čŊŧ取æ”ūčŋ›å·ēæœ‰įš„ try抛凚æ—ķéŧ˜čŪĪäļš undefined。

Critical 2 —— symbol 旁č·Ŋ无æģ•č·Ļ嚏列化存æīŧïžŒč€Œ R16-1 įš„äŋŪåĪįŽ°åœĻæ­Ģäūčĩ–åŪƒã€‚ attachToolOutputRecovery æ˜Ŋæ— æĄäŧķ调į”Ļįš„ïžŒå› æ­ĪįģŧįŧŸé‡ŒæŊäļŠå·Ĩ具响嚔 Part éƒ―åļĶæœ‰ Symbol(tool-output-recovery)åģä―ŋäļĪäļŠå­—æŪĩéƒ―æ˜Ŋ undefined。symbol 无æģ•č·Ļ JSON.stringify 存æīŧ。įšĒč‰ē断čĻ€åŠæ˜Ŋé—Ūéǘčūƒå°įš„äļ€åŠïžšfinalizeToolResponses įŽ°åœĻ把čŊĨ symbol å―“ä―œäš§į‰Đč·Ŋåū„įš„æŒäđ…æĨæšïžŒč€Œé‚ĢäļŠčŊŧ取æ­Ģæ˜ŊčŪĐįŽŽäšŒæŽĄčĢå‰Šå…·å·åđ‚į­‰æ€§įš„äļœčĨŋ——äđŸå°ąæ˜Ŋ R16-1 įš„äŋŪåĪã€‚åۃåœĻäļ€č―Ūäđ‹å†…成įŦ‹ïžˆPart æ˜ŊæīŧåŊđčąĄïž‰ïžŒä―†č·Ļäŧŧä―• JSON åū€čŋ”å°ąäļæˆįŦ‹â€”—䞚čŊæĒåĪã€å›žæ”ū、rewindã€å―•åˆķ。朎 PR 刚åļæ”ķįš„åˆåđķé‡Œč―äš† fix(cli): preserve checkpoint restore ownership and cold continuation äļŽ feat(cli): preserve OpenTUI session replay and rewind所äŧĨ「æĒåĪåŽå†å‘é€ã€åœĻčŋ™é‡Œæ˜Ŋæīŧč·Ŋåū„äļæ˜Ŋ假čŪū。čŊ·čŊŽå…ķäļ­å“Šäš›č·Ŋåū„䞚重新čŋ›å…Ĩ finalizeråđķį”Ļæĩ‹čŊ•é’‰ä―ïž›åĶ‚æžœäļ€äļŠéƒ―æēĄæœ‰ïžŒčŊ·åœĻčŪūčŪĄæ–‡æĄĢ里写明因äļščŋ™äļŠäŋŪåĪįš„æŒäđ…æ€§į›Ū前äūčĩ–äļ€äļŠæœŠčĻ€æ˜Žįš„å‡čŪū。

äļĪæĄåŧščŪŪ均äļé˜ŧæ–­ïžš

  • 及åœĻ text 或 persistedOutputFiles įĄŪ有倞æ—ķæ‰æŒ‚č――ïžŒæœŽåŊäŧĨčŪĐįŧåΧåĪšæ•° Part äļäš§į”Ÿčŋ™į§å·Ū垂agent-core.test.ts äđŸäŧæ˜Ŋįŧŋįš„ã€‚æ— æĄäŧķæŒ‚č――æēĄæœ‰æ”ķį›ŠïžŒåīæŠŠå―ąå“éĒæ”ū到最åĪ§ã€‚
  • fitText æ–°įš„æļ…é™ĪåūŠįŽŊbody.split(owned).join('')䞚į§ŧé™Ī指针/äŋį•™å•å…ƒ/č·Ŋåū„å­—įŽĶäļēįš„æŊäļ€æŽĄå‡šįŽ°ïžŒåŒ…æ‹Žåˆæģ•äļåŊäŋĄčū“å‡šé‡Œįš„å‡šįŽ°ã€‚ä―œäļšåäžŠé€ įš„ fail-closed æ–đ向æ˜ŊåŊđįš„ïžŒä―†åŪƒäžšé™éŧ˜åˆ æŽ‰å·Ĩ具įĄŪåŪžæ‰“å°å‡šįš„č·Ŋåū„——倞åū—加äļ€čĄŒæģĻ释čŊŽčŋ™æ˜Ŋ有意äļšäđ‹ã€‚

CI čŊæŪ —— 无䚚倞åŪˆé‡č·‘ïžˆGITHUB_EVENT_NAME=issue_comment因æ­Ī我æēĄæœ‰æž„åŧšæˆ–čŋčĄŒäŧŧä―• PR äŧĢį ã€‚čĄĻæ žæ˜Ŋ所åŪĄæŸĨ commit äļŠ PR č‡Šå·ąįš„ CI通čŋ‡ API čŊŧ取。

čŋ™äļŠįšĒč‰ēį”ą PR 造成äļæ˜ŊåŸšįĄ€čŪūæ–―å™ŠåĢ°ïžŒæˆ‘æƒģ明įĄŪčŊŽäļšäŧ€äđˆčŋ™æ ·åˆĪåŪšč€Œäļæ˜Ŋå―“ä―œ flake æ”ūčŋ‡ïžšäļ‰äļŠåĪąčīĨéƒ―æ˜ŊįĄŪåŪšæ€§įš„æ–­čĻ€äļåŒđ配å…ķ received å€žå‡į”ąæ–°įš„æĒåĪæœšåˆķäš§į”Ÿâ€”â€”äļĪäļŠæšåļĶįš„æ­Ģæ˜Ŋ抛垂åļļ persistedOutputFiles getter įš„å­—éĒæķˆæŊ及有新åĒžįš„æ— é˜ēæŠĪčŊŧ取才䞚č§Ķ及åŪƒïž‰ïžŒįŽŽäļ‰äļŠ diff åœĻ Symbol(tool-output-recovery) äļŠïžŒč€Œčŋ™äļŠ symbol į”ąæœŽ PR åž•å…Ĩ、树里æēĄæœ‰åˆŦåĪ„äžšåˆ›åŧšã€‚äļ‰äļŠæĩ‹čŊ•æ–‡äŧķéƒ―æœŠčĒŦ朎 PR äŋŪæ”đ因æ­ĪæēĄæœ‰äļ€äļŠæ˜Ŋčŋ‡æœŸæœŸæœ›ã€‚čŪĄæ•°ïžšpackages/cli 1 åĪąčīĨ / 45,765 通čŋ‡ïž›packages/core 2 åĪąčīĨ / 38,834 通čŋ‡ã€‚æ—Ĩåŋ—æœŦå°ūįš„ Terminated äļŽé€€å‡šį  1 æ˜ŊåĪąčīĨåĨ—äŧķäđ‹åŽ npm į”Ÿå‘―å‘Ļ期äļ­æ­Ēäļæ˜Ŋį‹ŽįŦ‹įš„åŸšįĄ€čŪūæ–―äš‹äŧķ。

朊銌čŊåŠåŽŸå› ïžšmacOS äļŽ Windows lane äŧĨ及 CLI 集成åĨ—äŧķå†æŽĄčĒŦč·ģčŋ‡ïžŒč€ŒæœŽæŽĄæ”đåŠĻįš„æ–°é€ŧčū‘æ­Ģæ˜Ŋå·Ĩ具įŧ“æžœåĨ‘įšĶįš„åŠ åŪ―åŠ č·Ŋåū„åĪ„į†â€”â€”įŽŽ 17 č―Ūå·ēæŒ‡å‡šįš„äļĪåĪ„äŧ… POSIX įš„ unescapePath 断čĻ€č‡ģäŧŠäŧæœŠåœĻᜟåŪž Windows runner äļŠč·‘čŋ‡ã€‚ä―œč€…č‡Šå·ąįš„æŠĨ告macOS、Node 22.22.0、224c21e8 äļŠčšį„ĶåĨ—äŧķå…Ļįŧŋæ˜Ŋä―œč€…įš„äļŧ垠äļæ˜Ŋæˆ‘é‡č·‘įš„čŊæŪä—ĐäšŽå―“å‰ head äļŽčŋ™æŽĄįšĒč‰ē CI。

æē™įŪąéŠŒčŊïžšæ­Ī head äļŠå·ē有äļ€æŽĄ /verify åœĻčŋčĄŒïžˆ07:20 兎告run 40170874477——无需重åĪč§Ķ发。åŪƒčƒ―čĄĨé―é™æ€åŪĄæŸĨčĄĨäļäļŠįš„éƒĻ分æ˜ŊïžšæĒåĪåĨ柄æ˜ŊåĶįœŸįš„čƒ―č·ĻäļĪæŽĄčĢå‰ŠäŧĨ及äļ€æŽĄ JSON åū€čŋ”å­˜æīŧäŧĨ及åŊ选元数æŪ抛垂åļļæ—ķæˆåŠŸįŧ“æžœæ˜ŊåĶäŧæ˜ŊæˆåŠŸã€‚ä―†č́čŊīæļ…æĨšâ€”—åœĻäļ€äļŠč‡ŠčšŦ单元æĩ‹čŊ•å·ēįšĒįš„ head äļŠč·‘ /verifyäļŧčĶåŠäžšåĪįŽ°čŋ™äļ‰äļŠåĪąčīĨ。äŧ·å€žæ›īéŦ˜įš„äļ‹ä­Ĩæ˜Ŋäŋۿމ Critical 1 äļŽ Critical 2 åđķčŪĐ CI 变įŧŋïž›ä―œč€…æœ‰å†™æƒé™ïžŒäđ‹åŽ /verify äļŽ /tmux éƒ―åŊį”Ļ。

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

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

@qwen-code-review-bot

qwen-code-review-bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Confidence: 2/5 — the redesign genuinely closes the root cause behind three long-standing Criticals, but it lands with CI red from two new Criticals of its own, one of which is a real production regression on the ACP and speculation paths.

Stepping back against my own independent proposal from Stage 2: I still don't have a simpler design to offer for the pressure budget itself. But I do think the recovery half of this PR took a wrong turn that is worth naming, because it is the part that keeps generating findings.

Round after round filed the same Critical against recoveryRangeOf — forged handles, severed handles, stale handles, non-idempotent handles. The round-18 convergence note said the quiet part out loud: a cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause. b69dfdd1 finally agrees, and deletes both text recognisers instead of tightening their gates again. That is the right instinct and I want it on the record that the author took the harder, correct option rather than the fourth patch. R12-1, R16-1 and R18-1 are all genuinely closed at this head, verified in the code.

The problem is what replaced them. The handle moved from parsed text to a symbol-keyed property on Part — and Part is a wire-shaped object that this codebase serializes, records, restores and replays. A symbol is invisible to all of that. So the mechanism traded a failure mode that was loud and local (a severed path you can see) for one that is quiet and distant (the handle silently absent after a round-trip, on a path no test currently walks). agent-core.test.ts caught it by accident, via a generic transcript-equality assertion the PR never touched. That is not a comfortable place for the durability guarantee of a data-loss-prevention feature to rest.

And Critical 1 is not a design question at all — it is a one-line placement error. The catch block directly above the new read states the invariant in a comment; the read went on the other side of it. Two pre-existing tests exist specifically to pin that invariant, and both are red.

Why I'm not approving:

  • CI is red at the reviewed head, and the red is PR-caused. Three deterministic assertion failures across two packages, all three traceable to the new recovery mechanism, none of the three test files touched by this PR. This is not a flake and not pre-existing infra noise — Stage 2 gives the reasoning.
  • A successful tool result can come back as an error when a ToolResult exposes a throwing persistedOutputFiles getter. The ACP session path passes the live toolResult straight through, so this is a reachable production surface, and the invariant it breaks is written down in the code immediately above the break.
  • The R16-1 fix's durability rests on an unstated assumption — that no path re-enters the finalizer with Parts rebuilt from JSON. The merge this PR just absorbed landed checkpoint-restore, session-replay and rewind work, so that assumption needs to be stated and pinned, not assumed.
  • The macOS/Windows lanes and the CLI integration suite were skipped again. For a change that widens the ToolResult contract and touches path handling, with two POSIX-only assertions still never exercised on a real Windows runner, green-on-Linux-only is not enough to certify.
  • Tier 2's bar for core is 100% confidence, and I am nowhere near it on an 885-production-line change to the request path that currently fails its own test suite. "The three things reviewers found are fixed and nothing new jumped out at me" is not that bar — and this round something did jump out, in CI.

The value question from my last two passes is unchanged and still belongs to a human: #2566 is priority/P3 + need-discussion, the author's own 2026-10-18 measurement said 倞åū—įŧ§įŧ­æŽĻčŋ›ä―†æš‚äļå€žåū—č―Žæ­Ģ conditioned on a re-measurement that has not been posted, and the production footprint has grown from 660 to 885 lines while the engagement band stays thin by design.

On the standing vote. The PR already carries a CHANGES_REQUESTED from this bot (review 5641202121, pinned to fcd2443, resting on R12-1/R16-1/R18-1). Per this skill's duplicate rule I am not submitting a second one — the existing review already gates the PR, and stacking reviews on an 18-round thread is noise. Be aware of the mismatch though: that vote's stated reasons are now stale (all three Criticals are fixed at this head) while its outcome is still correct (the PR cannot merge, for the different reasons above). A maintainer dismissing it as stale should read Stage 2 first, or the gate disappears on the wrong basis. Required checks stay red independently, so merge remains blocked either way.

@wenshao @zjunothing — you are both assigned, and this needs a human call. Two things: (1) the two Criticals above are small, concrete fixes and should not need another design round — Critical 1 in particular is moving one read inside an existing try; (2) once CI is green, the open question is still the one the author asked, whether the thin-band payoff justifies this scope for a P3, and that is a product-direction decision, not a gate one. I'd also suggest the recovery-metadata channel gets its own look before merge: it is now load-bearing for a data-loss guarantee and is carried on a side-channel that serialization drops.

The gate is not approving. Request-changes is already standing on this PR, so no new review is submitted.

äļ­æ–‡čŊŽ

į―ŪäŋĄåšĶïžš2/5 —— čŋ™æŽĄé‡æ–°čŪūčŪĄįĄŪåŪžå…ģ闭乆äļ‰äļŠé•ŋ期朊å†ģ Critical čƒŒåŽįš„æ đå› ïžŒä―†åŪƒč―åœ°æ—ķ CI æ˜ŊįšĒįš„ïžŒäļ”åļĶæĨ乆äļĪäļŠåąžäšŽåŪƒč‡Šå·ąįš„æ–° Criticalå…ķäļ­äļ€äļŠæ˜Ŋ ACP äļŽ speculation č·Ŋåū„äļŠįœŸåŪžįš„į”Ÿäš§å›žå―’ã€‚

退ä­ĨåŊđį…§æˆ‘åœĻ Stage 2 写äļ‹įš„į‹ŽįŦ‹æ–đæĄˆïžšåŊđ压力éĒ„įŪ—æœŽčšŦ我äŧį„ķæēĄæœ‰æ›īįŪ€įš„čŪūčŪĄåŊæã€‚ä―†æˆ‘įĄŪåŪžčŪĪäļšæœŽ PR įš„æĒåĪé‚Ģäļ€åŠčĩ°äš†äŪĩåžŊč·Ŋ倞åū—į‚đ凚æĨ因äļšåۃæ­Ģæ˜Ŋäļæ–­äš§å‡š findings įš„éƒĻ分。

äļ€č―Ū又äļ€č―Ū针åŊđ recoveryRangeOf 提凚同äļ€äļŠ Critical——侩造åĨ柄、戊断åĨ柄、čŋ‡æœŸåĨ柄、äļå…·åđ‚į­‰įš„åĨæŸ„ã€‚įŽŽ 18 č―Ūįš„æ”ķæ•›æ„č§æŠŠčŊæŒ‘明䚆äļ€äļŠäļæ–­å†į”Ÿå…„åžŸå‘įŽ°įš„į°‡ïžŒé€šåļļæ„å‘ģį€äŋŪåĪåŠåœĻåĪ„į†åŒä đå› įš„åŪžäū‹ã€‚b69dfdd1 įŧˆäšŽčŪĪ同䚆čŋ™äļ€į‚đį›īæŽĨ删掉äļĪäļŠæ–‡æœŽčŊ†åˆŦå™ĻïžŒč€Œäļæ˜ŊįŽŽå››æŽĄåŽŧæ”ķįī§åۃäŧŽįš„é—Ļ槛。čŋ™æ˜Ŋæ­ĢįĄŪįš„åˆĪ断我æƒģ明įĄŪčŪ°å―•ïžšä―œč€…é€‰æ‹Đ乆æ›īéšūä―†æ­ĢįĄŪįš„é€‰éĄđïžŒč€Œäļæ˜ŊįŽŽå››äļŠčĄĨäļã€‚R12-1、R16-1、R18-1 åœĻå―“å‰ head å…ĻéƒĻᜟæ­Ģå…ģ闭å·ēåœĻäŧĢ᠁äļ­æ ļåŪžã€‚

é—ŪéǘåœĻ䚎æ›ŋäŧĢåۃäŧŽįš„äļœčĨŋ。åĨ柄äŧŽč§Ģ析文朎变成䚆 Part äļŠäŧĨ symbol äļšé”Ūįš„åąžæ€§â€”â€”č€Œ Part æ˜Ŋäļ€äļŠįšŋ栞垏åŊđčąĄïžŒæœŽäŧĢį åš“äžšåšåˆ—åŒ–ã€å―•åˆķ、æĒåĪã€å›žæ”ūåŪƒã€‚symbol åŊđčŋ™äļ€åˆ‡éƒ―æ˜ŊäļåŊč§įš„ã€‚äšŽæ˜Ŋčŋ™äļŠæœšåˆķ把äļ€į§å“äšŪč€Œåą€éƒĻįš„åĪąčīĨæĻĄåžïžˆčƒ―įœ‹č§įš„čĒŦ切断č·Ŋåū„æĒ成䚆äļ€į§åŪ‰é™č€ŒéĨčŋœįš„åĪąčīĨæĻĄåžïžˆåū€čŋ”äđ‹åŽåĨ柄静éŧ˜įžšåļ­ïžŒäļ”å‘į”ŸåœĻå―“å‰æēĄæœ‰æĩ‹čŊ•čĶ†į›–įš„č·Ŋåū„äļŠïž‰ã€‚agent-core.test.ts æ˜Ŋåķį„ķ抓到åŪƒįš„â€”â€”é įš„æ˜ŊäĄ PR äŧŽæœŠäŋŪæ”đčŋ‡įš„通į”Ļč―Žå―•į›ļį­‰æ–­čĻ€ã€‚åŊđäļ€äļŠé˜ē数æŪäļĒåĪąį‰đæ€§č€ŒčĻ€ïžŒæŠŠæŒäđ…æ€§äŋčŊæ”ūåœĻčŋ™æ ·įš„ä―į―ŪäļŠåđķäļäŧĪäššåŪ‰åŋƒã€‚

而 Critical 1 æ đ朎äļæ˜ŊčŪūčŪĄé—ŪéĒ˜â€”â€”åۃæ˜Ŋäļ€čĄŒä―į―Ū错čŊŊ。新čŊŧ取æ­Ģä–đįš„ catch 块į”ĻæģĻ释写明䚆äļå˜é‡ïž›čŊŧ取čĒŦæ”ū到䚆åŪƒįš„åĶäļ€äū§ã€‚有äļĪäļŠæ—Ē有æĩ‹čŊ•æ­Ģæ˜Ŋäļšé’‰ä―čŋ™äļŠäļå˜é‡č€Œå­˜åœĻįš„ïžŒįŽ°åœĻéƒ―æ˜ŊįšĒįš„ã€‚

äļšä―•äļæ‰đ准

  • 所åŪĄæŸĨ head äļŠ CI æ˜ŊįšĒįš„ïžŒäļ”įšĒč‰ēį”ą PR 造成。 č·ĻäļĪäļŠåŒ…įš„äļ‰äļŠįĄŪåŪšæ€§æ–­čĻ€åĪąčīĨäļ‰č€…éƒ―åŊčŋ―æšŊåˆ°æ–°įš„æĒåĪæœšåˆķäļ‰äļŠæĩ‹čŊ•æ–‡äŧķéƒ―æœŠčĒŦ朎 PR äŋŪæ”đ。čŋ™äļæ˜Ŋ flakeäđŸäļæ˜Ŋæ—Ēæœ‰åŸšįĄ€čŪūæ–―å™ŠåĢ°â€”â€”æŽĻᐆ见 Stage 2。
  • æˆåŠŸįš„å·Ĩ具įŧ“æžœåŊčƒ―äŧĨ错čŊŊčŋ”å›žïžŒæĄäŧķæ˜Ŋ ToolResult æšīéœēäļ€äļŠæŠ›åž‚åļļįš„ persistedOutputFiles getter。ACP 侚čŊč·Ŋåū„把æīŧįš„ toolResult į›īæŽĨäž å…Ĩ因æ­Īčŋ™æ˜ŊåŊčūūįš„į”Ÿäš§éĒïžŒč€Œåۃį īåįš„äļå˜é‡å°ąå†™åœĻį īåĢæ­Ģä–đ。
  • R16-1 äŋŪåĪįš„æŒäđ…æ€§äūčĩ–äļ€äļŠæœŠčĻ€æ˜Žįš„å‡čŪū——åģæēĄæœ‰č·Ŋåū„äžšåļĶį€äŧŽ JSON 重åŧšįš„ Part 重新čŋ›å…Ĩ finalizer。朎 PR 刚åļæ”ķįš„åˆåđķč―äš† checkpoint æĒåĪã€äžščŊå›žæ”ūäļŽ rewind所äŧĨčŋ™äļŠå‡čŪū需č́čĒŦ写明åđķé’‰ä―ïžŒč€Œäļæ˜ŊčĒŦ假åŪšã€‚
  • macOS/Windows lane äļŽ CLI 集成åĨ—äŧķå†æŽĄčĒŦč·ģčŋ‡ã€‚ åŊđäļ€äļŠåŠ åŪ― ToolResult åĨ‘įšĶåđķč§Ķ及č·Ŋåū„åĪ„į†įš„æ”đåŠĻäœ‰äļĪåĪ„äŧ… POSIX įš„æ–­čĻ€č‡ģäŧŠæœŠåœĻᜟåŪž Windows runner äļŠč·‘čŋ‡ïžŒäŧ… Linux įŧŋäļčķģäŧĨčŪĪåŪšã€‚
  • æ ļåŋƒįš„ Tier 2 标准æ˜Ŋ 100% įĄŪäŋĄïžŒč€Œäļ€äļŠ 885 čĄŒį”Ÿäš§äŧĢį ã€æ”đåŠĻčŊ·æą‚č·Ŋåū„、äļ”å―“å‰čŋžč‡Šå·ąįš„æĩ‹čŊ•åĨ—äŧķéƒ―č·‘äļčŋ‡įš„ PR我čŋœčūūäļåˆ°ã€‚čŋ™č―ŪįĄŪåŪžæœ‰äļœčĨŋč·ģ䚆凚æĨ——åœĻ CI 里。

前äļĪč―Ūįš„äŧ·å€žé—ŪéǘæēĄæœ‰å˜åŒ–äŧåš”į”ąäššæĨåŪšïžš#2566 æ˜Ŋ priority/P3 + need-discussionïžŒä―œč€…č‡Šå·ą 2026-10-18 įš„æĩ‹é‡įŧ™å‡šã€Œå€žåū—įŧ§įŧ­æŽĻčŋ›ä―†æš‚äļå€žåū—č―Žæ­Ģ」å…ķæĄäŧķæ˜Ŋäļ€æŽĄå°šæœŠå‘åļƒįš„重新æĩ‹é‡ïž›č€Œį”Ÿäš§äŧĢ᠁规æĻĄå·ēäŧŽ 660 行åĒžč‡ģ 885 čĄŒïžŒį”Ÿæ•ˆåŒšé—ī按čŪūčŪĄäūį„ķåūˆįŠ„ã€‚

å…ģäšŽå―“å‰é‚Ģäļ€įĨĻ。 朎 PR å·ēåļĶæœ‰æĨč‡ŠæœŽæœšå™Ļäššįš„ CHANGES_REQUESTEDreview 5641202121锚åۚ fcd2443äūæŪ R12-1/R16-1/R18-1。按朎 skill įš„åŽŧé‡č§„åˆ™ïžŒæˆ‘äļå†æäšĪįŽŽäšŒæĄâ€”â€”æ—Ē有čŊ„åŪĄå·ēįŧæ‹Ķä―äš†čŋ™äļŠ PRïžŒč€ŒåœĻäļ€äļŠ 18 č―Ūįš„įšŋįĻ‹äļŠå åŠ čŊ„åŪĄåŠæ˜Ŋ噊åĢ°ã€‚ä―†čŊ·æģĻæ„å…ķäļ­įš„é”™ä―ïžšé‚Ģäļ€įĨĻįš„é™ˆčŋ°į†į”ąå·ēįŧčŋ‡æœŸïžˆäļ‰äļŠ Critical åœĻå―“å‰ head 均å·ēäŋŪåĪïž‰ïžŒč€ŒåŪƒįš„įŧ“čŪšäŧį„ķæ­ĢįĄŪPR äļčƒ―合åđķäūæŪæ˜ŊäļŠéĒé‚Ģ乛äļåŒįš„į†į”ąïž‰ã€‚įŧĪ者č‹Ĩč́äŧĨ「čŋ‡æœŸã€äļšį”ą dismissčŊ·å…ˆčŊŧ Stage 2åĶ则é—Ļ槛䞚äŧĨ错čŊŊįš„äūæŪæķˆåĪąã€‚åŋ…需æĢ€æŸĨéĄđ朎čšŦäŧæ˜ŊįšĒįš„ïžŒå› æ­Ī无čŪšåĶ‚ä―•åˆåđķéƒ―äŧčĒŦé˜ŧåĄžã€‚

@wenshao @zjunothing —— ä― äŧŽäļĪä―éƒ―åœĻ指æīū列čĄĻäļ­ïžŒčŋ™éœ€čĶäššæĨåŪšã€‚äļĪäŧķ䚋(1) äļŠéĒäļĪäļŠ Critical éƒ―æ˜Ŋå°č€Œå…·ä―“įš„äŋŪåĪïžŒäļåš”čŊĨ再需č́äļ€č―ŪčŪūčŪĄâ€”â€”å°Īå…ķ Critical 1 及æ˜Ŋ把äļ€åĪ„čŊŧ取į§ŧčŋ›å·ēæœ‰įš„ tryïž›(2) CI 变įŧŋäđ‹åŽïžŒåū…å†ģé—Ūéǘäŧæ˜Ŋä―œč€…č‡Šå·ąæå‡šįš„é‚ĢäļŠïžšįŠ„åŒšé—īæ”ķį›ŠåŊđäļ€äļŠ P3 æ˜ŊåĶ配åū—äļŠå―“å‰čŒƒå›īé‚Ģæ˜Ŋ䚧品æ–đ向å†ģį­–ïžŒäļæ˜Ŋé—Ļ槛å†ģį­–ã€‚æˆ‘čŋ˜åŧščŪŪåœĻ合åđķå‰å•į‹ŽåŪĄč§†æĒåĪå…ƒæ•°æŪ通道åŪƒįŽ°åœĻæ‰ŋč――į€é˜ē数æŪäļĒåĪąįš„äŋčŊïžŒåī挂åœĻäĄåšåˆ—化䞚äļĒæŽ‰įš„æ—č·ŊäļŠã€‚

é—Ļ槛äļäšˆæ‰đ准。æ­Ī PR äļŠå·ē有 request-changes åœĻį”Ÿæ•ˆïžŒå› æ­ĪäļæäšĪ新čŊ„åŪĄã€‚

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

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

@qwen-code-review-bot qwen-code-review-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.

Requesting changes on three findings — full reasoning in the stage comments above.

  1. CI is red because of this PR. Lint & Static fails the settings-schema freshness gate: DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD / ..._LINES moved to 80000 / 2000 but packages/vscode-ide-companion/schemas/settings.schema.json still says 25000 / 1000. Run npm run generate:settings-schema and commit.

  2. Removing DiscoveredMCPTool.maxOutputChars silently applies the global 2000-line cap to MCP results. That override was load-bearing for a char-only exemption documented in two places (coreToolScheduler.ts perToolLines, and the mcp-tool.ts comment this diff deletes). MCP goes from 500K chars / no line cap to 80K chars / 2000 lines, and many-short-lines is the dominant shape for structured MCP output. Keep the exemption or state the intent in the description and design doc.

  3. READ_FILE_CHAR_CAP = 25_000 was left behind. packages/cli/src/commands/review/lib/diff-plan.ts:238 is a documented mirror of the constant this PR moves, with 11 read sites across 5 files in /review — including the maxLineChars > READ_FILE_CHAR_CAP check that decides a chunk cannot be receipted as fully reviewed.

Also worth fixing in the same pass (not blocking on their own): an unconditional SHA-256 now runs on every tool result including untruncated ones; four doc pages still state the old numbers; the managed-agent protocol carries charsExplicit but not the new linesExplicit; the estimatePartChars change affects compaction for every session with no test and no mention; and the truncation stub still says "beginning and end" while substituting a structured sample.

Separately, a question rather than a blocker: the 25K→80K / 1000→2000 default bump is the half of #2566 that prior triage declined pending evidence, and need-discussion is still on that issue. The pressure-aware half does not need the bump to work — it scales whatever the default is. Consider splitting it out so it can be reviewed as the product decision it is.

Note the unit suite had not finished on this head (Test (ubuntu-latest) in progress, macOS/Windows skipped), so nothing here reflects a full suite verdict.

yiliang114 and others added 5 commits October 7, 2026 19:58
The truncation defaults moved to 80000 characters and 2000 lines. settingsSchema.ts
feeds them into the generated VS Code settings schema, so the committed schema has to
be regenerated or the "Check settings schema is up-to-date" gate fails.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmuy1j2xxcd
Synchronize the review read limit, retain wrapper-only sizing for missing or unserializable results, and keep runtime provenance out of wire assertions.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
estimatePartChars grew a structured-payload branch that billed an absent
functionResponse.response as "{}" - two chars on top of the wrapper floor -
and threw a TypeError on circular payloads, which aborted retention analysis
instead of degrading. Guard the branch on a response that is actually there
and fall back to the wrapper floor when JSON.stringify cannot serialize it.

Also give the mcp-client Config stub the truncation budget and the project
temp dir that the producer layer now reads, so the four invocation-context
tests exercise MCP tool execution instead of failing on missing stub members.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmuy3o8oich
Preserve the verified estimator implementation and portable MCP temp-directory mock from the parallel patrol fix.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@yiliang114

Copy link
Copy Markdown
Collaborator Author

PR #13599 follow-up native acceptance

PASS: one actual high-pressure noninteractive CLI run completed naturally. The anonymous provider received the reduced shell/read_file results; injection telemetry retained their producer sizes; both complete spill artifacts passed integrity checks. This is current frozen-candidate local acceptance after the CI corrections, distinct from the earlier four-profile evidence at 38d5debb6062e74c371a9d9865ef9306783fb205.

Candidate commit: 6ecad6d9a5a01b8ef6952c32da5335bbb089cdda, based on d0ddd020c8a64279e290538f84b152fe843ed9d4. The run executed db173608995ca98857bb09a9bb2759628e4b2369 plus the frozen repair diff; the parent then committed those unchanged source/build bytes additively under this full SHA. Parent-provided fresh core build 18639 and CLI build 77986 both exited 0. The 23 scoped source/executed files below were pinned before and after the actual run and remained unchanged. Later test-fixture corrections did not change these production/build bytes.

Actual controlled-provider result

Used the real supported stream-json CLI, owned temporary profile/runtime/workspace, and anonymous loopback OpenAI-compatible provider in actual retained tmux sessions. The provider's first response supplied actual usage.prompt_tokens=970000; the configured route window was 1,000,000. No token-counter setter was used. No explicit char/line overrides were configured. The retained helper also returns the same controlled usage on subsequent responses; these figures are fixture anchors, not real-model measurements.

The provider issued one real run_shell_command (cat of the owned payload) and one real read_file. Local telemetry recorded both as native tools with success=true and execution_status=success. They were invoked once each; subsequent requests carry their history, not repeated tool execution.

Actual tool Known fixture input bytes Injection telemetry raw chars Provider-received text chars Applied budget / source
run_shell_command 123,022 123,212 4,673 30,000 / per_tool
read_file 20,021 20,021 13,727 18,400 / context

Shell raw size measures the producer's model-facing result, including its command/status wrapper; it is larger than stdout itself. This report does not claim original file/network-byte telemetry coverage for every producer. Shell's per_tool label identifies its declared producer policy. Read-file pressure reduction was visible in size events as 20,021 → 18,400 → 13,727; the final wire result stayed below the applied context budget. Character budgets are caps, not exact target output lengths. Token-estimate fields may include wrapper/media floors and are not interchangeable with char/4 for every layer.

The complete saved shell producer output was 123,212 bytes, contained all 123,022 fixture stdout bytes, and matched the preview's advertised digest:

2d63702b24f61bb360bb354ab811b2e8c0c803f244af50cb6606a2a387ebb279

The read-file spill retained the entire original 20,021-byte file exactly, despite the two-stage preview reduction. Its digest was:

c4346491d902bb51402aa203b5a934c686a4bb7b288c82acab817286fe4e3851

CLI exit was 0; final stream result was success, is_error=false, ANONYMOUS_BUDGET_DONE, with no permission denials. The output was scripted by the anonymous provider; this verifies runtime delivery and budgeting mechanics, not production-model answer quality, convergence or representative savings.

Actual request and telemetry attribution

Five actual requests were observed: three main-chat requests, one compression request, and one managed-memory request. Classification uses the actual system prompts, including component that summarizes a conversation and managed memory extraction subagent, rather than treating every request without tools as auxiliary.

Request Actual UTC timestamp Purpose Shell / read_file wire chars
1 13:06:55.931 main, tool calls requested no results yet
2 13:06:56.023 main continuation 4,673 / 13,727
3 13:06:56.066 compression 4,673 / 13,727
4 13:06:56.087 main after compression 4,673 / 13,727
5 13:06:56.145 managed-memory 4,673 / 13,727

Compression was actually exercised, not bypassed: telemetry reported tokens_before=974682, tokens_after=10534, controlled compression input 970000 and output 50. The retained tool outputs survived into the post-compression request.

Four injection records were observed, two per tool. The first pair at 13:06:56.084 aligns with the post-compression main request 4. The second pair at 13:06:56.126 occurred after the managed-memory subagent started at 13:06:56.112 and immediately before its API request at 13:06:56.127 (which carries the subagent name and managed-memory prompt identity); provider request 5 followed at 13:06:56.145. This supports attribution to main continuation and the side-query chat. Injection events themselves lack a chat identity, so this report does not claim a general same-chat retry/history dedup guarantee from native evidence. The repeated records are not evidence of repeated tool execution.

Current local checks (parent receipts)

Latest whole-file receipts consolidate 804 core and 661 CLI cases: 1,465 distinct executed cases passed, zero skipped or failed in the latest receipt for each file. This is deduplicated coverage from the latest file results, not one single final full-suite run. The verifier did not rerun tests.

The initial core pass had 800 passing and four MCP failures caused by a missing storage getter in the mock; after the fixture correction, the entire 166-case MCP file passed. The initial CLI pass had five old 25k-derived brief-allowance expectations; correcting the fixture/comment from six pages (1+2+3) to four (1+1+2) yielded a passing 382-case review-file rerun. Production bytes stayed frozen during those test-only corrections. Parent also reports core/CLI builds and typechecks, changed-source ESLint/Prettier and diff checks passing. This local result does not assert that a new remote CI run is already green.

Latest whole-file receipt Distinct executed cases
packages/core/src/core/llm-chat.test.ts 562
packages/core/src/core/nonInteractiveToolExecutor.test.ts 10
packages/core/src/services/compactionInputSlimming.test.ts 44
packages/core/src/tools/mcp-client.test.ts 166
packages/core/src/tools/tool-output-size.test.ts 3
packages/core/src/tools/tool-result-retention.test.ts 19
packages/cli/src/serve/hosted-workspace-tool-turn.test.ts 202
packages/cli/src/commands/review/agent-prompt.test.ts 382
packages/cli/src/ui/hooks/useToolScheduler.test.ts 30
packages/cli/src/commands/review/lib/diff-plan.test.ts 47

The estimator's absent/circular-payload behavior and /review read allowance are covered by those targeted checks, not by extra native scenarios here. MCP execution was not part of this one shell/read_file smoke.

Source and actual loaded bytes

The read-only Node load hook delegated to the normal loader without transforming source. Eleven watched candidate JavaScript modules were actually loaded, including CLI, Config, scheduler, LlmChat, output-size, estimator, retention, shell and read_file. A loaded MCP module does not establish an MCP invocation. Review diff-plan source/build files were pinned but were not loaded/exercised by this scenario.

Candidate file SHA-256, unchanged before/after Actual watched load
packages/core/src/config/config.ts 7c4a7a84b2d304de73d736e3402c36d46bc8e1159a8f489fa41c7224bb1364f5 source
packages/core/src/tools/tool-output-size.ts eb1f3f14ad0b166a7648bb783a64cd02ef92fdd5240c7613595f7df076bff513 source
packages/core/src/services/compactionInputSlimming.ts 4425da8bfec915814ef8cf0e7947fc0c96678781f2aa84ea2abc1e4c3b2e1db6 source
packages/core/src/tools/tool-result-retention.ts 35040abae8f2a1b4e0e39b74f61356d63ad03cf9d37bb8b916561bc01c9464b8 source
packages/core/src/tools/read-file.ts 3ecd1bef936d891e14d81741c8a1c9a07caa967d72d2469f8cd7235e4828265c source
packages/core/src/tools/shell.ts 32f0200333daf6c8f814fb7a0df24231b8153e67fd2c36d640481c9cf8d6bbe2 source
packages/core/src/core/llm-chat.ts 176b91d48b08578df98520838a63c474b3080a1535bf5b6459735c9611688147 source
packages/core/src/core/coreToolScheduler.ts 20514921114dcff9d1c8612482f882ce5c1e6d7ea1c68e994f61eb7643fad17e source
packages/core/src/tools/mcp-tool.ts ba55f8ee4787540462dd49e96432ab0d012ba9d9872f4fe07b0df0d174e53b59 source
packages/cli/src/commands/review/lib/diff-plan.ts 2b66d55fa96ad3bbb1a4ec06a29e4cfba478536b17f0de60c9d59dc8f6f09761 source
packages/cli/src/cli.ts f8563c416eb7a61aaf55e5a11e9a58d856af9c40e45c1cc391bc78719fe1fb6c source
packages/core/dist/src/config/config.js 02b42009ba66a0e50b998ac66428dd6f7ba4dce54cbae5a106464854cd86d075 yes
packages/core/dist/src/tools/tool-output-size.js 0321ff089a3dc1aa76415ca12972e10e7964e33c4afbc4eb892c182b9bdd0e40 yes
packages/core/dist/src/services/compactionInputSlimming.js 1a5dd43758806c5e30b081c70512029bffd5639adfef4c2be47b0a2e34f8e9a3 yes
packages/core/dist/src/tools/tool-result-retention.js ef04a92e4de82b09548c4b7b2703a853eee8cd633bc823f453410aade878213d yes
packages/core/dist/src/tools/read-file.js 2ffcdedfda1c026ff5316256040c6d618d0bef30d6e0b6d06096565efeb75132 yes
packages/core/dist/src/tools/shell.js b11905de69f787488db95b70016984e57c57691d470f0e68e3b5f0e699f58fdb yes
packages/core/dist/src/core/llm-chat.js a93871c55695e5bdbe2a71d6a55ca78382686f40fff27bd32f7f05db5cab3d81 yes
packages/core/dist/src/core/coreToolScheduler.js 0e4c7188883b0becc6825c755c5c1c3949762bd99338987c7747a49dacbed845 yes
packages/core/dist/src/tools/mcp-tool.js a6b2ee845151f662a7f869d953157d17da7850ad78038b92c812bca9ec3469a7 yes
packages/cli/dist/index.js 43c7251b6e923a9d112f6a5dd694725fcc1bcdfafd9225ad1c05dbc13981eabf yes
packages/cli/dist/src/cli.js 8709cd476aaa73ba0f048f7822aff563b48c55df725c3aeaf2ffd2095abc26bb yes
packages/cli/dist/src/commands/review/lib/diff-plan.js 6d65c43957ff72466c364b23761a305464ebd42e6b5a98d5171ff449681a57fe pinned only

Actual tmux capture

This excerpt comes directly from the retained real session's tmux capture-pane output. Local paths/host prompt and UUIDs are redacted, including UUIDs wrapped across terminal lines. Full original and sanitized captures are retained. The exit-code receipt independently records 0.

{"type":"assistant","uuid":"<id>","session_id":"
<id>","parent_tool_use_id":null,"message":{"id":
"<id>","type":"message","role":"assistant","mode
l":"budget-native-high","content":[{"type":"text","text":"ANONYMOUS_BUDGET_DONE"
}],"stop_reason":null,"usage":{"input_tokens":970000,"output_tokens":50,"cache_r
ead_input_tokens":0,"total_tokens":970050}}}
{"type":"result","subtype":"success","uuid":"<id>","session_id":"<id>","is_error":false,"duratio
n_ms":423,"duration_api_ms":284,"num_turns":2,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":4850000,"output_tokens":250,"cache_read_input_tokens":0,"
total_tokens":4850250},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C
onfigure tools.executionSandbox on Linux or a supported legacy sandbox via --san
dbox / QWEN_SANDBOX, or set QWEN_CODE_SUPPRESS_YOLO_WARNING=1 to silence this no
tice.
<local-host>/%

Evidence and cleanup

The owned pr13599-native-final artifact directory retains original and sanitized provider bodies, response receipts, stdout/stderr, local telemetry, actual module-load logs, tmux capture, source/build manifests, known fixture sizes/digests, both complete recovered outputs, executed settings snapshot, native result and cleanup receipts, and the parent latest-file check receipt. No terminal output was reconstructed. Two fixture input-length assertions were corrected before any provider/product invocation; an initial direct localhost readiness probe timed out, and one identical bounded fallback succeeded before the sole native run began.

Cleanup complete: owned CLI/provider tmux sessions removed, anonymous provider listener stopped, and owned profile/runtime/workspace removed. Complete spill copies and all evidence remain. The verifier made no product source, Git/GitHub, auth configuration, dependency installation or build changes. Actual user HOME/CODEX_HOME/auth were untouched.

äļ­æ–‡čŊŽ

å―“å‰å†ŧįŧ“å€™é€‰įš„äļ€æŽĄįœŸåŪžéŦ˜åŽ‹åŠ› CLI 驌æ”ķ通čŋ‡ã€‚provider éĶ–æŽĄåŪžé™…å›žæŠĨ 970,000 prompt tokensč·Ŋį”ąæ˜ŽįĄŪ配į―Ū 1,000,000 įŠ—åĢæēĄæœ‰ä―ŋį”Ļ内éƒĻ token-counter setter。åŪžé™…æ‰§čĄŒ shell äļŽ read_file 各äļ€æŽĄïž›provider æ”ķåˆ°įš„å·Ĩ具文朎分åˆŦæ˜Ŋ 4,673 äļŽ 13,727 字įŽĶæģĻå…Ĩ telemetry äŋį•™äš†åŽŸå§‹ producer åĪ§å° 123,212 äļŽ 20,021。read_file įš„åŪžé™… context budget äļš 18,400ïž›shell äŋæŒå…ķ内åŧš per_tool 30,000 į­–į•Ĩ。éĒ„įŪ—æ˜ŊäļŠé™ïžŒäļčĄĻįĪščū“凚äļ€åŪšæ°åĨ―į­‰äšŽéĒ„įŪ—ã€‚

åیæ•ī文äŧķæĒåĪå·ēæ ļ銌read_file äŋå­˜äš†å…ĻéƒĻ 20,021 åŽŸå§‹å­—čŠ‚ïžŒé€å­—čŠ‚į­‰äšŽčū“å…Ĩïž›shell äŋå­˜įš„åیæ•ī producer čū“凚包åŦå…ĻéƒĻ 123,022 stdout å­—čŠ‚ïžŒäļ”åŒđ配éĒ„č§ˆåĢ°æ˜Žįš„ SHA-256。shell įš„ raw metric 包åŦå‘―äŧĪ及įŠķ态 wrapperäļčƒ―äļŽåŽŸå§‹ stdout åĪ§å°æ··äļšäļ€č°ˆïžŒäđŸäļčƒ―æģ›åŒ–äļšæ‰€æœ‰ producer įš„åŽŸå§‹æ–‡äŧķ/į―‘įŧœå­—节čĶ†į›–ã€‚CLI 臩į„ķåŪŒæˆåđķ退凚 0æēĄæœ‰ permission denial。

压įžĐįĄŪåŪžå‘į”ŸïžŒåŪžé™…čŊ·æą‚按äļ‰äļŠ main、äļ€æŽĄ compression、äļ€æŽĄ managed-memory 匚分压įžĐ后å·Ĩ具įŧ“æžœäŧäŋį•™ã€‚æģĻå…ĨčŪ°å―•įš„äļĪįŧ„æ—ķé—ī分åˆŦåŊđ嚔压įžĐåŽįš„äļŧčŊ·æą‚äļŽ managed-memory side query䚋äŧķæœŽčšŦæēĄæœ‰ chat identity因æ­ĪæēĄæœ‰åŪĢį§° native å·ēčŊæ˜Žæ‰€æœ‰åŒ chat 重čŊ•/历åē replay įš„åŽŧ重čŊ­äđ‰ïžŒäđŸæēĄæœ‰æŠŠæšåļĶ历åēå―“ä―œé‡åĪå·Ĩå…·æ‰§čĄŒã€‚å—æŽ§åŒŋ名æĻĄåž‹įš„囚åŪšå›žį­”äŧ…čŊæ˜ŽčŋčĄŒå’Œ wire 朚åˆķäļčŊæ˜Žį”Ÿäš§æĻĄåž‹æ•ˆæžœæˆ–æ”ķį›Šã€‚

įˆķ agent įš„æœ€æ–°é€æ–‡äŧķ回执åŽŧ重后äļš core 804、CLI 661ïžŒå…ą 1,465 äļŠåŪžé™…æ‰§čĄŒäļ”通čŋ‡įš„ distinct cases。åۃäŧŽæĨč‡ŠåˆæŽĄé›†äļ­čŋčĄŒåŠ äŋŪæ­Ģ mock/fixture åŽįš„åŊđåš”æ•ī文äŧķé‡č·‘ïžŒäļæ˜ŊåĢ°į§°åˆč·‘čŋ‡äļ€æŽĄæœ€įŧˆå…ĻåĨ—ã€‚å―“å‰ native 及做 shell/read_file äĄéŦ˜åŽ‹åŠ›åœšæ™ŊMCP、įĐš/åūŠįŽŊ payload、review allowance į­‰äŧĨåŪšå‘æĩ‹čŊ•äļšį‹ŽįŦ‹čŊæŪ。čŋčĄŒå‰åŽ 23 äļŠ production/source/build 文äŧķ哈åļŒäļ€č‡ī后įŧ­ test-only äŋŪæ­Ģ朊æ”đ变åŪžé™…æ‰§čĄŒå­—čŠ‚ã€‚æ‰€æœ‰č‡Šæœ‰äļīæ—ķčŋčĄŒčĩ„暐å·ēæļ…į†ïžŒåŽŸå§‹/č„ąæ• tmux、wire、telemetry、åیæ•ĒåĪæ–‡äŧķ和æŠĨ告äŋį•™ã€‚

@yiliang114

Copy link
Copy Markdown
Collaborator Author

PR #13599 structured JSON preview native acceptance

PASS: one real MCP JSON discovery/invocation scenario completed naturally. The next main provider request received the structured-sample instruction, without a beginning/end or CONTENT TRUNCATED claim. The embedded sample parsed as valid JSON, and the complete saved JSON matched the actual raw 120,000 bytes and its advertised digest exactly.

Tested candidate core/source: be37149614ae788ed0d36e7947a8a22147d0cf3f. Core was freshly built for this additive candidate; CLI reused the unchanged artifact built at 6ecad6d9a5a01b8ef6952c32da5335bbb089cdda, with current CLI source unchanged. Twenty-one scoped source/build hashes were captured before and after execution and stayed identical. Eleven watched JavaScript modules actually loaded from the candidate tree, including its fresh truncation helper. This report is distinct from the earlier 6ecad6 high-pressure plaintext acceptance; that scenario was not rerun. An ancestry-only final merge may reuse this evidence if it preserves the tested production/build bytes; this report does not approve a changed final tree.

Real discovery, invocation and provider result

Used the actual supported noninteractive stream-json CLI in a retained tmux session, an anonymous loopback OpenAI-compatible provider, and the real stdio MCP transport. The owned profile trusted only the anonymous fixture server and enabled automatic approval. Actual user HOME/CODEX_HOME/auth were untouched. First response usage was the controlled anchor 1,000 prompt tokens, with a configured 1,000,000-token route window.

The initial advertised provider tools included tool_search/tool_call, rather than directly exposing budget_json. The provider first invoked real tool_search with query budget_json. The actual returned declaration named mcp__native-budget__budget_json with an empty object schema; the provider then invoked that returned name through tool_call, using empty arguments. Independent stdio receipts recorded tools/list and exactly one tools/call for budget_json, alongside normal initialize/list traffic. Telemetry recorded discovery and MCP execution as successful. No uninvoked MCP configuration was counted as acceptance.

The next main request carried a 1,005-character text result (1,007 UTF-8 bytes because the sample includes an ellipsis). The wire text is an explanatory envelope followed by a JSON sample; this report does not claim the entire envelope is a JSON document. The sample retained the marker, actual total row count 2,000 and first two real rows, and parsed successfully. The result text explicitly said:

The output below is a structured JSON sample; omitted values remain in the full output file.

It contained neither beginning and end nor CONTENT TRUNCATED. Actual provider-received text, with its owned path redacted:

Tool output was too large and has been truncated.
The full output has been saved to: <full-output-file>
Total characters: 120000; total lines: 1.
Full output sha256: 4f98356ddb2c0480aa6be48832e4982d6d0bd69c343f43ad204e5c6d0f4ca50e
Page in with read_file using the absolute file_path above, offset (zero-based line) and limit (line count).
The output below is a structured JSON sample; omitted values remain in the full output file.

Truncated part of the output:
Structured sample:
{
  "marker": "ANONYMOUS_MCP_JSON",
  "rows": {
    "rows": 2000,
    "sample": [
      {
        "id": 0,
        "value": "anonymous"
      },
      {
        "id": 1,
        "value": "anonymous"
      }
    ]
  },
  "padding": "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzâ€Ķ"
}

MCP producer/injection telemetry reported raw 120,000 → injected 1,005 characters, truncated=true, applied_budget=80000, budget_source=global. The owned profile left global char/line overrides unset, using defaults 80,000 chars and 2,000 lines. This raw JSON occupied one line, so the scenario exercises the character-triggered structured preview; it does not independently prove the line cutoff. The doc clarification about global chars and lines is separate from this measured boundary.

The complete JSON spill equaled both the actual stdio-produced raw JSON and the frozen expected bytes, exactly 120,000 bytes. It parsed as complete JSON with all 2,000 original rows. The advertised digest matched the file:

4f98356ddb2c0480aa6be48832e4982d6d0bd69c343f43ad204e5c6d0f4ca50e

CLI exit was 0; final stream result was success, ANONYMOUS_BUDGET_DONE, three turns and no permission denials. The provider replies are scripted anonymous fixtures: this proves tool transport, preview wording, valid sampling and complete-file retention, not production-model answer quality or savings.

Actual request attribution

Four provider requests were observed: three main-chat requests and one managed-memory request. The side request is identified by its actual managed memory extraction subagent system prompt; no compression request occurred.

Request Actual UTC time Purpose Tool result chars in history
1 13:24:47.449+00:00 main
2 13:24:47.512+00:00 main mcp-discovery-call: 260
3 13:24:47.616+00:00 main mcp-discovery-call: 260, mcp-budget-call: 1005
4 13:24:47.718+00:00 managed-memory mcp-discovery-call: 260, mcp-budget-call: 1005

The MCP result was injected into the next main request and later retained in managed-memory history. Injection records for discovery/MCP were repeated in the memory side-query phase: managed-memory started at 2026-10-07T13:24:47.683Z, its second injection pair was at 2026-10-07T13:24:47.694Z, and the explicitly named subagent API request followed at 2026-10-07T13:24:47.695Z. These timings and actual request purpose support the main-versus-side-query attribution. Injection events themselves do not expose a chat identity, so this report does not claim a general same-chat retry/history dedup guarantee. The MCP tool was invoked only once.

Source/executed identity and local checks

The read-only load hook delegated to the normal loader and recorded file paths/hashes without transforming source. All eleven watched candidate build modules loaded, including MCP client/tool, tool_search/tool_call, scheduler, LlmChat, output-size and the new truncation helper. Actual stdio invocation and outgoing provider text establish the reached behavior; loading a module alone would not.

Candidate source or executed file SHA-256, stable before/after
packages/core/src/config/config.ts 7c4a7a84b2d304de73d736e3402c36d46bc8e1159a8f489fa41c7224bb1364f5
packages/core/src/tools/truncation.ts bbab47a4a3626dc97f2b050045d56876e927460822f82e2f9e850e66425f1b50
packages/core/src/tools/tool-output-size.ts eb1f3f14ad0b166a7648bb783a64cd02ef92fdd5240c7613595f7df076bff513
packages/core/src/tools/mcp-tool.ts ba55f8ee4787540462dd49e96432ab0d012ba9d9872f4fe07b0df0d174e53b59
packages/core/src/tools/mcp-client.ts dd4d6a62fa3ee5126a500f462044c35731d49011e32bfd867d3083011bbf1264
packages/core/src/tools/tool-search.ts 779e265a27d0466e96b9f02020ae2eac1b8f8d86c72c221dee5728c2a92362f8
packages/core/src/tools/tool-call.ts b5272880a184a6e74c310b14a98051783ff0db4551a7454774c7bbb010eefc74
packages/core/src/core/coreToolScheduler.ts 20514921114dcff9d1c8612482f882ce5c1e6d7ea1c68e994f61eb7643fad17e
packages/core/src/core/llm-chat.ts 176b91d48b08578df98520838a63c474b3080a1535bf5b6459735c9611688147
packages/cli/src/cli.ts f8563c416eb7a61aaf55e5a11e9a58d856af9c40e45c1cc391bc78719fe1fb6c
packages/core/dist/src/config/config.js 02b42009ba66a0e50b998ac66428dd6f7ba4dce54cbae5a106464854cd86d075
packages/core/dist/src/tools/truncation.js 78073ce8cae18ba27c917918ac0bc0a827e74c03db7393c7f1d31bfc08f0f616
packages/core/dist/src/tools/tool-output-size.js 0321ff089a3dc1aa76415ca12972e10e7964e33c4afbc4eb892c182b9bdd0e40
packages/core/dist/src/tools/mcp-tool.js a6b2ee845151f662a7f869d953157d17da7850ad78038b92c812bca9ec3469a7
packages/core/dist/src/tools/mcp-client.js ba585ac75e7fb0d5b37f2da48c16073aa7d2bad1db70a84204d1896e93553e50
packages/core/dist/src/tools/tool-search.js ff53cbf0bbaaac3bacbc8d64202c85f06d9d391658c343bf8a9a802fbb52750c
packages/core/dist/src/tools/tool-call.js f852987d22e8196d4058b28538ee7f7d32ae9fc7d3ac6743bc4f36405a741ec5
packages/core/dist/src/core/coreToolScheduler.js 0e4c7188883b0becc6825c755c5c1c3949762bd99338987c7747a49dacbed845
packages/core/dist/src/core/llm-chat.js a93871c55695e5bdbe2a71d6a55ca78382686f40fff27bd32f7f05db5cab3d81
packages/cli/dist/index.js 43c7251b6e923a9d112f6a5dd694725fcc1bcdfafd9225ad1c05dbc13981eabf
packages/cli/dist/src/cli.js 8709cd476aaa73ba0f048f7822aff563b48c55df725c3aeaf2ffd2095abc26bb

Parent-provided current receipts: fresh core build exited 0; the full truncation test file executed 44 passing cases; source/test ESLint, formatting and diff checks passed. The verifier ran no tests or builds. The earlier 1,465 distinct latest-file cases and high-pressure native result belong to the separate 6ecad6 report; no fresh full-suite claim is made for this text-only additive correction.

Actual tmux capture

This excerpt is directly from tmux capture-pane on the actual retained CLI session. Local paths/host prompt and UUIDs are redacted, including wrapped UUIDs. Original and complete sanitized captures remain local; exit-code receipt is 0.

{"type":"assistant","uuid":"<id>","session_id":"
<id>","parent_tool_use_id":null,"message":{"id":
"<id>","type":"message","role":"assistant","mode
l":"budget-native-mcp","content":[{"type":"text","text":"ANONYMOUS_BUDGET_DONE"}
],"stop_reason":null,"usage":{"input_tokens":1000,"output_tokens":50,"cache_read
_input_tokens":0,"total_tokens":1050}}}
{"type":"result","subtype":"success","uuid":"<id>","session_id":"<id>","is_error":false,"duratio
n_ms":553,"duration_api_ms":402,"num_turns":3,"result":"ANONYMOUS_BUDGET_DONE","
usage":{"input_tokens":4000,"output_tokens":200,"cache_read_input_tokens":0,"tot
al_tokens":4200},"permission_denials":[]}
Warning: running headless with --yolo / approval-mode=yolo and no sandbox. All t
ool calls (shell, write, edit) auto-execute at this process's privilege level. C
onfigure tools.executionSandbox on Linux or a supported legacy sandbox via --san
dbox / QWEN_SANDBOX, or set QWEN_CODE_SUPPRESS_YOLO_WARNING=1 to silence this no
tice.
<local-host>/%

Evidence and cleanup

The owned pr13599-mcp-json-native directory retains pre/post identity manifests, actual module-load records, frozen/raw/complete recovered JSON, actual provider requests and responses, stdio protocol receipts, stdout/stderr, local telemetry, the exact provider result text, actual tmux captures, executed settings, native result and cleanup receipts, plus sanitized copies. No high-pressure scenario was repeated and no terminal output was reconstructed.

Cleanup complete: owned CLI/provider tmux sessions removed, provider listener stopped, no stdio helper remained after CLI exit, and owned profile/runtime/workspace removed. All raw and recovered evidence is preserved. No product source, Git/GitHub/Aone, auth, dependency installation or build mutation was performed by this verifier.

äļ­æ–‡čŊŽ

å―“å‰ additive å€™é€‰įš„äļ€æŽĄįœŸåŪž MCP JSON 驌æ”ķ通čŋ‡ã€‚éĶ–å…ˆé€ščŋ‡åŪžé™… tool_search discovery åū—到 mcp__native-budget__budget_json įš„įœŸåŪž schema再äŧĨčŋ”å›žįš„åį§°įŧ tool_call 调į”Ļ。stdio 协čŪŪæ—Ĩåŋ—į‹ŽįŦ‹čŪ°å―•äš† tools/list 和恰åĨ―äļ€æŽĄ budget_json tools/call因æ­ĪæēĄæœ‰æŠŠäŧ…配į―Ūæˆ–æģĻ册 MCP å―“ä―œé€ščŋ‡ã€‚CLI 臩į„ķåŪŒæˆïžŒé€€å‡š 0无 permission denial。

äļ‹äļ€æŽĄäļŧčŊ·æą‚æ”ķåˆ°įš„å·Ĩ具įŧ“æžœäļš 1,005 字įŽĶ明įĄŪčŊŽä–đ内åŪđæ˜Ŋ structured JSON sampleïž›æēĄæœ‰ beginning/end 或 CONTENT TRUNCATED åĢ°į§°ã€‚æ•īäļŠå·Ĩ具文朎æ˜ŊčŊŽ envelope 加 JSON æ ·äū‹ïžŒäļæ˜Ŋå̰᧰ envelope 朎čšŦåŊį›īæŽĨč§Ģ析äļš JSON。åĩŒå…Ĩæ ·äū‹åŊæ­Ģåļļč§Ģ析åđķäŋį•™įœŸåŪž marker、2,000 行æ€ŧ数和前äļĪæĄčŪ°å―•ã€‚telemetry æ˜ūįĪšåŽŸå§‹ 120,000、æģĻå…Ĩ 1,005、global budget 80,000。原始 JSON 及有äļ€čĄŒïžŒå› æ­ĪæœŽæŽĄåŪžé™…čŊæ˜Ž char-triggered JSON branchïžŒæœŠį‹ŽįŦ‹éЌčŊ lines 戊断阈倞。

åیæ•ī spill äļŽåŪžé™… stdio 原始 JSON、å†ŧįŧ“įš„ expected bytes äļ‰č€…é€å­—čŠ‚į›ļį­‰ïžŒå‡äļš 120,000 å­—čŠ‚ïžŒäŋį•™å…ĻéƒĻ 2,000 æĄåŽŸå§‹čŪ°å―•ïžŒSHA-256 äļŽčū“凚åĢ°æ˜Žäļ€č‡ī。åŪžé™…čŊ·æą‚äļšäļ‰äļŠ main 加äļ€æŽĄ managed-memoryïžŒæœŠå‘į”ŸåŽ‹įžĐ重åĪæģĻå…ĨčŪ°å―•æŒ‰ side-query įš„åŪžé™… prompt äļŽæ—ķé—īå…ģįģŧå―’åąžïžŒäļåŪĢį§°å·ē通čŋ‡ native čŊæ˜Žæ‰€æœ‰åŒ chat replay åŽŧ重čŊ­äđ‰ïžŒäđŸæēĄæœ‰æŠŠåކåē搚åļĶå―“ä―œé‡åĪå·Ĩå…·č°ƒį”Ļ。

å―“å‰ core 构åŧšåŊđåš” be37149CLI ä―ŋį”Ļ 6ecad6 įš„æœŠå˜æž„åŧšäš§į‰Đïž›21 äļŠæšį /æ‰§čĄŒæ–‡äŧķčŋčĄŒå‰åŽå“ˆåļŒäļ€č‡ī11 äļŠ watched build modules 均åŪžé™…åŠ č――ïžŒåŒ…åŦ新 truncation helper。įˆķ agent įš„å―“å‰åŪšå‘å›žæ‰§æ˜Ŋåیæ•ī truncation 文äŧķ 44 cases 通čŋ‡ïž›æ­Ī前 1,465 cases äļŽéŦ˜åŽ‹åŠ›įŧ“æžœäŧåąžäšŽį‹ŽįŦ‹įš„ 6ecad6 čŊæŪæēĄæœ‰é‡æ–°č·‘éŦ˜åŽ‹åŠ›æˆ–åĢ°į§°åˆåŪŒæˆæœ€įŧˆå…ĻåĨ—ã€‚æ‰€æœ‰č‡Šæœ‰čŋ›įĻ‹ã€į›‘åŽã€tmux、profile/runtime/workspace å·ēæļ…į†ïžŒåŽŸå§‹/č„ąæ•åŪžé™…įŧˆįŦŊ、čŊ·æą‚、telemetry äļŽåیæ•ī JSON äŋį•™ã€‚č‹Ĩ后įŧ­äŧ…åĒžåŠ  Git ancestry äļ”äŋæŒåیå…Ļį›ļåŒįš„å·ēæĩ‹į”Ÿäš§/build å­—čŠ‚ïžŒåŊäŧĨåĪį”Ļ朎čŊæŪ朎æŠĨ告äļæ‰đ准æ”đå˜åŽįš„æœ€įŧˆæ ‘。

Final ancestry and evidence reuse

Final candidate merge: 1e92feee82ed7ad297cac5e65829eeb1761f859c. The parent reports the tracked production tree is identical to the native-tested be37149614ae788ed0d36e7947a8a22147d0cf3f (git diff excluding *.test.ts exited 0). The only file-content change is the MCP test mock using OS.tmpdir; its complete 166-case file rerun passed. This is a parent-provided final identity/check receipt. The actual run/load manifests retain the originally executed be371 core/source and unchanged 6ecad6 CLI hashes. Because the final merge preserves those tested production bytes, the native evidence above is reusable for this final head; no additional native scenario or build was executed by the verifier.

最įŧˆ merge äļš 1e92feee82ed7ad297cac5e65829eeb1761f859c。įˆķ agent å·ēįĄŪčŪĪäļŽåŪžé™… native 驌æ”ķįš„ be371 tracked production tree åیå…Ļäļ€č‡ī排é™Ī *.test.ts įš„ git diff čŋ”回 0å”Ŋäļ€å†…åŪđ变化æ˜Ŋ MCP æĩ‹čŊ• mock ä―ŋį”Ļ OS.tmpdiråŊđåš”åیæ•ī 166 cases é‡č·‘é€ščŋ‡ã€‚äŧĨäļŠäļšįˆķ agent 提äū›įš„æœ€įŧˆčšŦäŧ―/æĢ€æŸĨ回执åŪžé™…æ‰§čĄŒ manifest äŧäŋį•™ be371 core/source äļŽæœŠå˜ 6ecad6 CLI įš„åŽŸå§‹å“ˆåļŒã€‚最įŧˆ merge äŋį•™į›ļ同å·ēæĩ‹į”Ÿäš§å­—čŠ‚ïžŒå› æ­ĪåĪį”ĻæœŽæŽĄ native čŊæŪæēĄæœ‰čŋ―加 native 圚æ™Ŋæˆ–į”ą verifier 再构åŧšã€‚

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Final pushed head: 1e92feee82ed7ad297cac5e65829eeb1761f859c, preserving the native-tested production tree and the parallel patrol fix. The portable MCP mock has its own full 166-case rerun.

High-pressure CLI/tmux report · JSON MCP CLI/tmux report.

Addressed the concrete review/CI failures in additive local commits 6ecad6d9a5a0 and be37149614ae.

  • Critical 1: the generated VS Code defaults are included from db173608995c.
  • Critical 2: MCP intentionally inherits both global budgets, including the default 2,000-line cap. This is now explicit in the PR body and synchronized English/Chinese designs; newline-heavy results can hit the line cap first and keep complete-output recovery. This is part of the authorized default change, rather than an accidental character-only migration.
  • Critical 3: /review mirrors the new 80k read-file cap; the independent 20k diff-chunk cap remains unchanged. Read-page/brief-allowance fixtures now reflect the actual paging policy.
  • Suggestion 5: current settings documentation says 80k / 2,000. The older paired design notes carry a current-contract notice and link to the replacement budget design while preserving their historical rationale.
  • Suggestion 6: no new linesExplicit transport field is needed. The managed provider worker binds an execution-context digest to its existing local Config and executes tools directly; it neither reconstructs pressure settings from that record nor initializes/sends a pressure-aware chat. Static limits remain represented by chars and lines; pressure explicitness is consumed at the owning chat send boundary.
  • Suggestion 7: the structured-response JSON estimator branch was already in the base d0ddd020c8a6, rather than introduced by this PR. The follow-up does fix the concrete absent/circular-payload failures while retaining wrapper/media floors; relevant estimator/retention checks passed.
  • Suggestion 8: JSON previews now say they are structured samples, without a false beginning/end or truncation-marker claim. All 44 truncation tests passed; the separate real MCP/tmux run exercised this branch.

The current CI fixes also correct strict comparisons that inadvertently treated enumerable internal Symbol provenance as part of provider JSON, while retaining live-path provenance and cancellation/status assertions. Latest whole-file receipts cover 1,465 distinct executed passing cases for those repairs, plus the separate 44-case truncation file. Fresh core/CLI builds and typechecks passed; the final small JSON correction has its own fresh core build.

Suggestion 4's pre-fallback digest cost and the other nonblocking hot-path optimization notes remain acknowledged performance tradeoffs. The digest contributes to the actual wrapper size/token decision; this follow-up does not introduce a placeholder hash or claim a measured CPU improvement. Raw size also remains explicitly unknown for live paths that lack a producer result/original-size metadata; it is not inferred from a preview.

Native evidence is pinned separately: the high-pressure run at 6ecad6d9a5a0 covers shell/read_file, compression and spill integrity; the MCP JSON run at be37149614ae covers actual discovery/invocation and the corrected JSON instructions. Neither is a production-provider billing or remote telemetry-backend claim. CI will be re-evaluated on the newly pushed head; these local results do not retroactively turn the old failed run green.

äļ­æ–‡čŊŽ

å·ēį”ĻåĒžé‡æäšĪäŋŪæ­Ģå…·ä―“ review/CI é—ŪéĒ˜ïžšschema éŧ˜čŪĪ倞å·ēįšģå…Ĩïž›MCP 同æ—ķč·Ÿéšå­—įŽĶå’ŒčĄŒæ•°é™åˆķæ˜Ŋ明įĄŪæŽˆæƒįš„čĄŒäļšïžŒæ­Ģ文及äļ­č‹ąæ–‡čŪūčŪĄå·ē写æļ…éŧ˜čŪĪ 2,000 行åŊčƒ―å…ˆč§Ķ发review čŊŧ取分éĄĩ按 80k čŪĄįŪ—ïžŒį‹ŽįŦ‹ 20k diff chunk äļå˜ã€‚å―“å‰į”Ļ户文æĄĢ及历åēčŪūčŪĄčŊŽå·ē同æ­Ĩ/标æģĻ后įŧ§įšĶåŪšã€‚

managed provider worker äŧ…æ ļéŠŒæ‰§čĄŒäļŠä–‡æ‘˜č́åđķį›īæŽĨæ‰§čĄŒå·Ĩ具äļäŧŽčŊĨčŪ°å―•é‡åŧšåŽ‹åŠ›é…į―ŪäđŸäļå‘送压力感įŸĨ chat因æ­Īäļæ·ŧ加无åŪžé™…æķˆčīđč€…įš„ linesExplicit 字æŪĩ。įŧ“构化响嚔 JSON äž°įŪ—åˆ†æ”Ŋå·ē存åœĻ䚎 baseïž›æœŽæŽĄäŋŪæ­ĢįžšåĪą/äļåŊ嚏列化 payload įš„å…·ä―“é”™čŊŊåđķäŋį•™ wrapper/media 垀销。JSON 样朎提įĪšå·ēæ”đäļšå‡†įĄŪįš„įŧ“构化样朎čŊŽïžŒ44 äļŠæˆŠæ–­æĩ‹čŊ•åŠįœŸåŪž MCP/tmux č·Ŋåū„通čŋ‡ã€‚

CI äŋŪæ­Ģįš„æœ€æ–°æ•ī文äŧķ回执åŽŧ重后č͆ᛖ 1,465 äļŠåŪžé™…æ‰§čĄŒé€ščŋ‡įš„į”Ļäū‹ïžŒåĶ有 44 äļŠæˆŠæ–­į”Ļäū‹ïž›core/CLI 构åŧšã€įąŧ型æĢ€æŸĨ通čŋ‡ïžŒæœ€åŽįš„ JSON 小æ”đåŠĻåĶ有新 core 构åŧšã€‚内éƒĻ Symbol æĨ暐äŋĄæŊäŧåœĻåŪžæ—ķč·Ŋåū„äŋį•™ïžŒäļĨæ žæ–­čĻ€æŒ‰åŪžé™… provider JSON æ ļ銌åđķäŋį•™å–æķˆ/įŠķ态æĢ€æŸĨ。

éŦ˜åŽ‹åŠ›æŠĨ告įŧ‘åۚ 6ecad6d9a5a0JSON MCP æŠĨ告įŧ‘åۚ be37149614aeäļäžšæ··æˆäļ€æŽĄå―“å‰ head å…ĻåŠŸčƒ―éŠŒæ”ķ。hash čŪĄįŪ—åŠå…ķäŧ–非é˜ŧåĄžæ€§čƒ―åŧščŪŪæ˜ŽįĄŪäŋį•™äļšå–čˆïžŒäļå̰᧰ CPU æ”ķį›Šïž›įžšå°‘ producer 原始尚åŊļįš„ live path įŧ§įŧ­æŠĨ告 unknown。朊åŪĢį§°į”Ÿäš§æĻĄåž‹čīĶ单/čŋœįĻ‹įŧŸčŪĄåŽįŦŊ驌æ”ķäđŸäļæŠŠæ—§ CI åĪąčīĨ回写成įŧŋč‰ē新 head įš„ CI åĶįœ‹ã€‚

@qwen-code-review-bot

Copy link
Copy Markdown
Collaborator

ðŸĐš serve daemon A/B

Built the PR base vs this PR head 1e92fee, drove a fixed endpoint set against each, and diffed the JSON responses. Only fields that changed are shown.

health-deep-with-session

field PR base (before) this PR (after)
activeWorkStaleMs 7 6

— Qwen Code · serve A/B

@yiliang114

Copy link
Copy Markdown
Collaborator Author

@wenshao @tanzhenxin one product decision is needed before this can merge, separate from the code review.

This PR does two things:

  1. Pressure-aware reduction. Tool-output budgets shrink near the current chat's context limit, based on fresh usage for the same provider route.
  2. A default bump. The ordinary defaults go from 25,000 chars / 1,000 lines to 80,000 / 2,000, and MCP results now inherit the 2,000-line cap instead of a separate 500,000-char ceiling.

The review bot's three blocking items from the 38d5debb60 round are fixed at 1e92feee82: the settings schema is regenerated, the MCP line cap is stated in the body and in both design docs, and /review mirrors the 80k cap. But it also pointed out that (2) is the half of #2566 that earlier triage declined pending evidence, and #2566 still carries need-discussion. I could not find a maintainer decision approving the bump. The PR body describes it as authorized, but no comment records who made that call.

Options:

  • (a) Approve the bump as part of this PR (risk noted in Risk & Scope: larger early-session results, and MCP gains a line cap).
  • (b) Split it. This PR keeps today's defaults and adds only the pressure-aware half, which works with any default; the bump becomes a separate PR to decide on its own.

My recommendation is (b) unless the bump was already agreed somewhere I missed. Please say which.

äļ­æ–‡čŊŽ

@wenshao @tanzhenxin 合åđķ前需čĶå…ˆåšäļ€äļŠäš§å“å†ģåŪšïžŒčŋ™å’ŒäŧĢ᠁ review æ˜ŊäļĪ回䚋。

čŋ™äļŠ PR 做䚆äļĪäŧķ䚋

  1. 按äļŠä–‡åŽ‹åŠ›įžĐ减。 å―“å‰åŊđčŊåŋŦæŽĨčŋ‘äļŠä–‡äļŠé™æ—ķæ đæŪ同äļ€ provider č·Ŋį”ąįš„æœ€æ–°į”Ļ量įžĐ小å·Ĩ具čū“å‡šįš„éĒ„įŪ—ã€‚
  2. 调åΧéŧ˜čŪĪ倞。 æ™Ū通éŧ˜čŪĪ倞äŧŽ 25,000 字įŽĶ / 1,000 čĄŒæéŦ˜åˆ° 80,000 / 2,000ïž›MCP įŧ“æžœäļå†ä―ŋį”Ļå•į‹Žįš„ 500,000 字įŽĶäļŠé™ïžŒæ”đäļšäđŸå— 2,000 行äļŠé™įšĶ束。

review bot åœĻ 38d5debb60 é‚Ģäļ€č―Ūæå‡šįš„äĄé˜ŧåĄžé—ŪéĒ˜ïžŒåœĻ 1e92feee82 äļŠéƒ―å·ēäŋŪåĪïžšsettings schema å·ēé‡æ–°į”ŸæˆïžŒMCP įš„čĄŒæ•°äļŠé™å·ēåœĻ PR 描čŋ°å’Œäļ­č‹ąæ–‡čŪūčŪĄæ–‡æĄĢäļ­å†™æ˜ŽïžŒ/review äđŸåŒæ­Ĩ到䚆 80k äļŠé™ã€‚ä―† bot 同æ—ķæŒ‡å‡šïžŒįŽŽ (2) éĄđæ­Ģæ˜Ŋ #2566 里äđ‹å‰ triage 因čŊæŪäļčķģ而æēĄæœ‰åŒæ„įš„é‚Ģäļ€åŠïžŒ#2566 č‡ģäŧŠäŧæŒ‚į€ need-discussion。我æēĄæœ‰æ‰ū到įŧĪ者æ‰đ准čŋ™æŽĄč°ƒæ•īįš„čŪ°å―•ã€‚PR 描čŋ°é‡Œå†™įš„æ˜Ŋ"å·ē获æ‰đ准"ïžŒä―†æēĄæœ‰äŧŧä―•čŊ„čŪščŊŽæ˜Ŋč°åšįš„å†ģåŪšã€‚

åŊ选æ–đæĄˆïžš

  • (a) åœĻ朎 PR äļ­æ‰đ准éŧ˜čŪĪå€žč°ƒæ•īéĢŽé™Đå·ē写åœĻ Risk & Scopeïžšæ—Đ期䞚čŊįš„å·Ĩ具įŧ“果䞚æ›īåĪ§ïžŒMCP 侚åĪšå‡ščĄŒæ•°äļŠé™ïž‰ã€‚
  • (b) 拆分。朎 PR äŋį•™įŽ°æœ‰éŧ˜čŪĪ倞及加å…Ĩ按压力įžĐå‡įš„éƒĻ分čŋ™éƒĻ分适į”Ļ䚎äŧŧä―•éŧ˜čŪĪ倞éŧ˜čŪĪå€žč°ƒæ•īåĶ垀 PR å•į‹Žå†ģåŪšã€‚

é™Ī非čŋ™æŽĄč°ƒæ•īå·ēįŧåœĻ我æēĄįœ‹åˆ°įš„地æ–đčūūæˆäš†å…ąčŊ†ïžŒæˆ‘åŧščŪŪ选 (b)。čŊ·å‘ŠįŸĨ选哊äļŠã€‚

@qwen-code-review-bot qwen-code-review-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.

Partially reviewed — gaps disclosed.

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

  • web_search now builds its llmContent twice (once with applyLimit = false purely to measure it) — already reported (comment 6037332199, 'Notes, not blockers')

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): "agent reverse-audit (round 2)": the test-only change at coreToolScheduler.test.ts:3779 ( { waitForCompletion: true } → { waitForCompletion: true, truncateToolOutputThreshold: 25000 } ) — I dâ€Ķ; "agent reverse-audit (round 2)": a repo-wide check for production structuredClone (or any other structured-copy) of Part objects, which would silently drop the symbol-keyed provenance and sâ€Ķ; "agent reverse-audit (round 2)": did not build the workspace and run packages/cli vitest for useToolScheduler.test.ts / hosted-workspace-tool-turn.test.ts , so the claim that the pre-changâ€Ķ; "agent reverse-audit (round 1)": I did not verify the new design doc's telemetry-shape claims against the code — "RUM mirrors size fields, and character/token histograms distinguish raw/injecteâ€Ķ; "agent reverse-audit (round 1)": I did not verify "Code-mode nested calls retain execution measurements but do not claim independent model injection when only their parent's output is submittedâ€Ķ, and 4 more.

Not reviewed: reverse audit — stopped before round 4 by the review time budget.

[Critical] The PR's own open merge precondition is unresolved. In comment 6040691008 the author asked @wenshao and @tanzhenxin to choose between (a) approving the 25,000 -> 80,000 character / 1,000 -> 2,000 line default bump inside this PR and (b) splitting it into a separate PR, and recommended (b) unless the bump had already been agreed. No maintainer decision is recorded on the thread, and issue #2566 — whose second half this bump is — still carries need-discussion after prior triage declined that half pending evidence. This is a product decision rather than a code defect, and the pressure-aware half of the change works with any default, so it cannot be settled by reading the diff; this review re-checked it against the thread and it still stands.

äļ­æ–‡čŊŽ

äŧ…åŪŒæˆéƒĻ分åŪĄæŸĨåŪĄæŸĨįžšåĢå·ēæŠŦéœē。

朎č―ŪįĄŪčŪĪįš„ 1 æĄåŧščŪŪįš§å‘įŽ°å·ēåœĻ PR äŠĨ告čŋ‡ïžŒäļå†é‡åĪå‘åļƒïžˆåˆ—čĄĻ见ä–đč‹ąæ–‡éƒĻ分。

朊åŪĄæŸĨ原文äļšč‹ąæ–‡ïž‰ïžšbuild-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

朊æŽĒįīĒ到å…ĻéƒĻæ·ąåšĶčūū到å·Ĩå…·č°ƒį”ĻéĒ„įŪ—ïž‰ïžš"agent reverse-audit (round 2)"ïžšthe test-only change at coreToolScheduler.test.ts:3779 ( { waitForCompletion: true } → { waitForCompletion: true, truncateToolOutputThreshold: 25000 } ) — I dâ€Ķïž›"agent reverse-audit (round 2)"ïžša repo-wide check for production structuredClone (or any other structured-copy) of Part objects, which would silently drop the symbol-keyed provenance and sâ€Ķïž›"agent reverse-audit (round 2)"ïžšdid not build the workspace and run packages/cli vitest for useToolScheduler.test.ts / hosted-workspace-tool-turn.test.ts , so the claim that the pre-changâ€Ķïž›"agent reverse-audit (round 1)"ïžšI did not verify the new design doc's telemetry-shape claims against the code — "RUM mirrors size fields, and character/token histograms distinguish raw/injecteâ€Ķïž›"agent reverse-audit (round 1)"ïžšI did not verify "Code-mode nested calls retain execution measurements but do not claim independent model injection when only their parent's output is submittedâ€ĶåĶ有 4 æĄã€‚

朊åŪĄæŸĨ反向åŪĄčŪĄâ€”â€”čŊ„åŪĄæ—ķé—īéĒ„įŪ—äļčķģïžŒæœŠčƒ―åž€å§‹įŽŽ 4 č―Ū。

[Critical] The PR's own open merge precondition is unresolved. In comment 6040691008 the author asked @wenshao and @tanzhenxin to choose between (a) approving the 25,000 -> 80,000 character / 1,000 -> 2,000 line default bump inside this PR and (b) splitting it into a separate PR, and recommended (b) unless the bump had already been agreed. No maintainer decision is recorded on the thread, and issue #2566 — whose second half this bump is — still carries need-discussion after prior triage declined that half pending evidence. This is a product decision rather than a code defect, and the pressure-aware half of the change works with any default, so it cannot be settled by reading the diff; this review re-checked it against the thread and it still stands.

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

Comment thread packages/core/src/config/config.ts Outdated
Comment thread packages/core/src/core/llm-chat.ts Outdated
Comment thread packages/core/src/core/llm-chat.ts Outdated
Comment thread packages/core/src/tools/web-fetch.ts
Comment thread docs/design/pressure-aware-tool-output.md Outdated
Comment thread packages/core/src/tools/truncation.test.ts
Comment thread packages/core/src/tools/truncation.ts
Comment thread packages/core/src/tools/truncation.ts Outdated
Comment thread packages/core/src/tools/truncation.ts Outdated
Comment thread packages/core/src/tools/web-fetch.ts
@yiliang114

Copy link
Copy Markdown
Collaborator Author

All 27 [Suggestion] findings from the round-1 full-diff review at head 1e92feee82ed are deferred to one follow-up issue: #13635.

Reason: this PR is at +1673/-123, over the patrol's 1500-addition scope fuse, so this round does replies and thread organisation only — no code is added. Deferred, not rejected: #13635 enumerates all 27 by review id, anchor and one-line gist, grouped by structural root (10 of them are already umbrella findings — one root, N confirmed symptoms — and are tracked as one work item each, not N).

The 4 [Critical] threads are not part of that issue's scope; each got its own reply above with the verification at head 1e92feee82ed, and all 4 stay unresolved.

None of the 27 Suggestion threads has been resolved. Resolving 27 threads by pointing at a fresh issue is the maintainer's call, not the patrol's.

Threads covered by #13635:

  • R1-5 — docs/design/shell-output-budget-single-entry.md:7 — thread PRRT_kwDOPB-92c6qG_QV
  • R1-6 — packages/cli/src/commands/review/lib/diff-plan.ts:238 — thread PRRT_kwDOPB-92c6qG_Qc
  • R1-7 — packages/core/src/tools/tool-response-finalizer.ts:543 — thread PRRT_kwDOPB-92c6qG_SB
  • R1-8 — packages/core/src/tools/truncation.ts:337 — thread PRRT_kwDOPB-92c6qG_SM
  • R1-9 — packages/core/src/tools/tool-output-size.ts:80 — thread PRRT_kwDOPB-92c6qG_Rx
  • R1-10 — packages/core/src/config/config.test.ts:9772 — thread PRRT_kwDOPB-92c6qG_Qk
  • R1-11 — packages/core/src/tools/web-fetch.ts:507 — thread PRRT_kwDOPB-92c6qG_SV
  • R1-12 — packages/core/src/tools/truncation.test.ts:220 — thread PRRT_kwDOPB-92c6qG_SI
  • R1-13 — packages/core/src/core/llm-chat.ts:3163 — thread PRRT_kwDOPB-92c6qG_RK
  • R1-14 — packages/core/src/core/coreToolScheduler.ts:7498 — thread PRRT_kwDOPB-92c6qG_Q2
  • R1-15 — packages/core/src/core/llm-chat.ts:2213 — thread PRRT_kwDOPB-92c6qG_Q-
  • R1-16 — packages/core/src/telemetry/metrics.ts:269 — thread PRRT_kwDOPB-92c6qG_RU
  • R1-17 — packages/core/src/tools/tool-response-finalizer.ts:366 — thread PRRT_kwDOPB-92c6qG_R2
  • R1-18 — packages/core/src/tools/truncation.ts:543 — thread PRRT_kwDOPB-92c6qG_SQ
  • R1-19 — packages/core/src/tools/truncation.ts:677 — thread PRRT_kwDOPB-92c6qG_ST
  • R1-20 — packages/core/src/core/coreToolScheduler.ts:7450 — thread PRRT_kwDOPB-92c6qG_Qu
  • R1-21 — packages/core/src/core/llm-chat.ts:2211 — thread PRRT_kwDOPB-92c6qG_RF
  • R1-22 — packages/core/src/config/config.ts:974 — thread PRRT_kwDOPB-92c6qG_Qq
  • R1-23 — packages/core/src/telemetry/loggers.ts:389 — thread PRRT_kwDOPB-92c6qG_RT
  • R1-25 — packages/core/src/core/llm-chat.ts:3254 — thread PRRT_kwDOPB-92c6qG_RO
  • R1-26 — packages/core/src/telemetry/qwen-logger/qwen-logger.ts:639 — thread PRRT_kwDOPB-92c6qG_Ra
  • R1-30 — packages/core/src/tools/mcp-tool.ts:1144 — thread PRRT_kwDOPB-92c6qG_Ri
  • R1-31 — packages/core/src/tools/mcp-tool.ts:1052 — thread PRRT_kwDOPB-92c6qG_Re
  • R1-32 — packages/core/src/tools/tool-response-finalizer.ts:438 — thread PRRT_kwDOPB-92c6qG_R8
  • R1-33 — packages/core/src/tools/shell.ts:3197 — thread PRRT_kwDOPB-92c6qG_Rn
  • R1-35 — docs/design/pressure-aware-tool-output.md:15 — thread PRRT_kwDOPB-92c6qG_QO
  • R1-36 — packages/core/src/tools/tool-output-size.ts:44 — thread PRRT_kwDOPB-92c6qG_Rr

Feature tracking stays in #11536 (originally #2566). #11536 is a feature-request issue that closes at merge, so the review debt is filed separately rather than appended to it.

äļ­æ–‡čŊŽ

head 1e92feee82ed äļŠįŽŽäļ€č―Ūå…Ļ量 review įš„ 27 æĄ [Suggestion] å…ĻéƒĻåŧķæœŸåˆ° #13635按įŧ“构性æ đ因分įŧ„ã€é€æĄčŪ°äš† review id / 文äŧķčĄŒå· / äļ€åĨčŊæ‘˜čĶïžˆå…ķäļ­ 10 æĄæœŽčšŦå°ąæ˜Ŋ「äļ€äļŠæ đ因 + N äļŠå·ēįĄŪčŪĪį—‡įŠķã€įš„äžžå―Ēå‘įŽ°ïžŒæŒ‰äĄå·Ĩä―œéĄđåĪ„į†ïž‰ã€‚åŧķæœŸäļæ˜ŊåĶå†ģ。原因æ˜Ŋ朎 PR å·ēčūū +1673/-123čķ…čŋ‡å·ĄæĢ€įš„ 1500 行åĒžé‡į†”æ–­įšŋčŋ™äļ€č―Ū及做回åĪäļŽįšŋįĻ‹æ•īį†ã€äļå†åŠ äŧĢį ã€‚4 æĄ [Critical] äļčŋ›čŊĨ issueïžŒå„č‡ŠåœĻä–đįšŋįĻ‹é‡Œå›žäš† head äļŠįš„æ ļ驌čŊæŪå…ĻéƒĻäŋæŒæœŠč§Ģå†ģ。27 æĄ Suggestion įšŋįĻ‹äĄéƒ―æēĄæœ‰ resolve——į”Ļäļ€äļŠæ–°åŧš issue åŽŧæ‰đ量 resolve 27 æĄįšŋįĻ‹åąžäšŽįŧĪč€…įš„čĢé‡ïžŒäļæ˜Ŋå·ĄæĢ€čŊĨåšįš„åŠĻä―œã€‚åŠŸčƒ―č·ŸčļŠäŧåœĻ #11536。

â€Ķ re-stamp

tool-output-recovery.ts defines its handle non-enumerable and documents that
a spread copy drops it, so callers must attach it last. checkAndNotifyCompletion
re-stamps provenance over the completed parts twice, and both stamps are spread
copies, so each one silently dropped the handle.

A producer-sized result (an agent TIMEOUT / MAX_TURNS body) never earns
persistedOutputFiles, which is the only condition under which collectTextSlots
rebuilds the handle. The part was therefore its sole carrier, and the send
boundary builds its entries from parts alone. With the handle gone, fitText ran
with no preserved text: the pressure budget this PR adds performed a second cut
straight through the remediation reason line and the transcript marker, and the
minimumRecoveryBudget veto priced its floor at notice+3 instead of the real
recovery text. The parent model then read a truncated answer with no knob to
raise and re-issued the same call, which timed out again.

Re-attach after each stamp, matching convertToFunctionResponse and
observeProducerOutput, which already do this correctly. Adds a regression test
that drives the scheduler through finalize and then re-cuts the parts at the two
per-slot allocations the review measured (660 and 1000); removing either
re-attach turns it red.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmvop19fgvo
@yiliang114

Copy link
Copy Markdown
Collaborator Author

The blocking item from review 5646868449 is fixed in adeea4b.

checkAndNotifyCompletion re-stamped provenance over the completed parts twice, and both stamps are spread copies, so each one dropped the non-enumerable recovery handle that tool-output-recovery.ts documents must be attached last. A producer-sized agent body never earns persistedOutputFiles, which is the only condition under which collectTextSlots rebuilds the handle, so the part was its sole carrier: the send boundary builds its entries from parts alone, fitText ran with preserved = '', and the pressure budget cut straight through the TIMEOUT/MAX_TURNS reason line and the transcript marker.

Both re-stamp sites now re-attach the handle after the stamp, matching what convertToFunctionResponse and observeProducerOutput in the same file already do. No change to attachToolOutputProvenance, and no new carrier.

The regression test in coreToolScheduler.test.ts drives a lone producer-sized tool through the real scheduler to completion, rebuilds the send-boundary entries from the resulting parts alone, and re-cuts them at the two per-slot allocations the review measured (660 and 1000), asserting both the reason line and the transcript marker survive. Removing either re-attach turns it red — checked for each site separately (2 failed | 533 skipped both times); green with both in place.

@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.

COMMENT at head adeea4b9d79be430423b3192bab571d2adf8ec82 — not approving. The single commit added since my last review is a correct fix, but the three Criticals recorded as standing in round 18 are untouched at this head, and my own scan of the full diff is still incomplete.

1. The three standing Criticals are unchanged at this head

Round 18 (review 5641202121, against fcd2443cd26a) recorded three sev: "C" findings, each titled "still standing at the reviewed commit". No review round has run since — the newest ledger on this PR is still round 18, and there are no reviews at all at 84708c5d or adeea4b9 from the review bot. The head has moved four times since fcd2443.

adeea4b9 is one commit ahead of 84708c5d and zero behind, and its scope is exactly two files: packages/core/src/core/coreToolScheduler.ts (+28/−12) and a new packages/core/src/core/coreToolScheduler.test.ts (+60). I compared the blobs of all three files the round-18 findings are anchored in, between 84708c5d and adeea4b9:

finding anchor blob
R12-1 — the send boundary recovers a tool result's only recovery handle by regex packages/core/src/tools/truncation.ts:844 identical
R16-1 — the recovery-handle inverses are not idempotent under fitText's own output packages/core/src/tools/tool-response-finalizer.ts:132 identical
R18-1 — the preserved transcript handle is charged 1:1 against the same previewBudget whose 1/5 head share carries the composition's reason line packages/core/src/tools/agent/transcript-marker.ts:23 identical

Their PR-relative deltas are unchanged too (tool-response-finalizer.ts +224/−49, transcript-marker.ts +20/−0, and truncation.ts still not in this PR's 22-file change set at all). So nothing in the new commit addresses any of the three, and I have no basis on which to call any of them fixed. Per the round-18 record they were carried findings, not fresh ones, which means they have now survived several heads unaddressed.

I am not re-deriving their causal chains here — R18-1 in particular is an A/B measurement claim about headBudget = floor((previewBudget-5)/5) that cannot be settled by reading a commit subject. The point is narrower and does not need that: the code those findings describe is byte-identical to what round 18 assessed, and no reviewer has re-assessed it since.

2. What the new commit does, and it is correct

adeea4b9 fixes a real defect and I verified it rather than taking the subject line for it. tool-output-recovery.ts defines the handle with Object.defineProperty(..., { enumerable: false }), and its own doc comment states the consequence: "Callers that copy a part with a spread drop the handle, so attach it last." attachToolOutputProvenance spreads each part, so it was silently discarding the recovery handle. Both scheduler sites now read the handle from the original call.response.responseParts before the provenance spread and re-attach it afterwards:

const recovery = call.response.responseParts.map(getToolOutputRecovery).find(Boolean) ?? {};
call.response.responseParts = attachToolOutputRecovery(
  attachToolOutputProvenance(call.response.responseParts, provenance ?? { â€Ķ }),
  recovery,
);

Reading before the spread is the right order — reading after would always find nothing — and the non-enumerable definition is deliberate rather than accidental: it keeps a part comparable to its own serialized form so the transcript assertion holds and the handle cannot leak into a model request. The commit adds 60 lines of tests alongside it. This part needs no change.

3. My own scan remains incomplete

This is the second head in a row where I could not finish the current-code gate. Unread at adeea4b9: packages/core/src/core/llm-chat.ts (+393/−65, the pressure-budget anchor and its invalidation), packages/core/src/tools/tool-response-finalizer.ts (+224/−49, allocation and the sub-notice veto), packages/core/src/tools/agent/agent.ts (+45/−44), microcompact.ts, fileReadCache.ts, tokenEstimation.ts and web-fetch.ts. I read tool-output-recovery.ts in full and the scheduler hunk; that is not coverage of a 22-file, +2399-line diff. I am recording these as unperformed gates rather than reporting findings I cannot prove.

4. CI at this head

14 checks pass, 7 skipped, 1 pending, 1 failed. The failure is the Managed Agent gate job (run 40275374499 / job 121416902625), reported with a 0-second duration and its name still an unevaluated conditional expression over needs.scope.outputs, which reads as the job failing before it resolved its own identity rather than failing an assertion. This PR's change set is entirely packages/core plus two design docs and contains no managed-agent-server code, so I have no evidence tying the failure to it — and per this channel's rules an unattributed failure does not change the verdict. It is worth a look before merge simply because a red gate blocks the lane.

Note, non-blocking

attachToolOutputRecovery defines the symbol unconditionally on every part, so the scheduler's ?? {} fallback attaches an empty handle rather than leaving the part without one. getToolOutputRecovery then returns {}, which is truthy, so a consumer testing if (getToolOutputRecovery(part)) cannot distinguish "no handle" from "empty handle" — while one testing if (recovery?.text) can. I did not read the consumers at this head and am not filing this as a defect; it is worth confirming that every consumer guards on the fields rather than on the object's presence, since find(Boolean) already treats {} as a hit.

Next step

The blocking path is unchanged from my last review: address R12-1, R16-1 and R18-1 at a new head, or reply on each with the code evidence that refutes it and have it dismissed. A fresh review round at this head would also settle them, since none has run since fcd2443.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Re-anchoring the three round-18 Criticals at the current head adeea4b9d7. Short version: the "unchanged since round 18" premise doesn't hold, and R12-1 points at code that no longer exists anywhere in the tree.

To be explicit about what I am not claiming — I am not saying any of these are fixed. I'm saying the anchors are dead, so "still standing at fcd2443" can't be carried forward as-is, and one of the three needs withdrawing or restating.

The baseline hop is the issue

Round 18 (review 5641202121) was filed 2026-10-25T02:40:09Z against fcd2443cd2. Commit b69dfdd104 — fix(core): carry trusted recovery metadata across tool budgets — landed at 06:35:21Z, about four hours after that review, and is an ancestor of the current head. It rewrote all three anchor files:

file b69dfdd104
tools/truncation.ts +0/−27
tools/agent/transcript-marker.ts +0/−23
tools/tool-response-finalizer.ts +131/−71
tools/tool-output-recovery.ts +37/−0 (new file)

So a 84708c5d → adeea4b9 comparison does show byte-identical anchor files, but that's the wrong hop — it only touches coreToolScheduler.ts and its test. The change happened between fcd2443cd2 and 84708c5d.

Anchor state at adeea4b9d7

Measured two independent ways (local git objects, plus the contents and code-search APIs), which agree:

finding anchor file length now named symbol
R12-1 tools/truncation.ts:844 821 lines — line 844 does not exist persistedOutputPointerRange: 0 hits repo-wide; matched in 2 files at fcd2443cd2
R18-1 tools/agent/transcript-marker.ts:23 20 lines — line 23 does not exist file was 43 lines at fcd2443cd2
R16-1 tools/tool-response-finalizer.ts:132 740 lines, so the line resolves — but to recoveryHeader() / recoveryOf(), not the code round 18 meant file was 680 lines at fcd2443cd2

Positive control, so the zero isn't a broken probe: at the same head git grep isAlreadyTruncated hits coreToolScheduler.ts (3), truncation.test.ts (3) and truncation.ts (1). Case-insensitive PointerRange and persistedOutputPointer are both 0 as well.

Per finding

R12-1 — the mechanism it names is gone, not merely moved. b69dfdd104 deletes persistedOutputPointerRange outright: the import, the return persistedOutputPointerRange(text) ?? transcriptMarkerRange(text) call site, and the export function definition are all in the deletion side of that commit. transcriptMarkerRange is likewise absent now. What replaced it is a structured side channel — tool-output-recovery.ts attaches a non-enumerable Symbol('tool-output-recovery') to the part, read back through getToolOutputRecovery (3 sites each in tool-response-finalizer.ts and coreToolScheduler.ts). Residual text-scraping in the changed files is one unrelated continuationText.match(/^\s+/) in llm-chat.ts. That is the shape this finding was asking for, so I think R12-1 wants withdrawing or restating against the new mechanism rather than being carried again.

R16-1 / R18-1 — anchors dead, substance not re-derived. I'm deliberately not calling these fixed. The budget-charging logic R18-1 describes now lives around minimumRecoveryBudget(slot) at tool-response-finalizer.ts:138-145, applied as a floor at lines 440, 474 and 574. Worth flagging that this floor protects the recovery handle from being shortened — which is closer to the behaviour R18-1 objects to than to a refutation of it, so if anything R18-1 deserves a fresh look at that coordinate. R16-1's idempotence question now belongs at recoveryOf() (:118-128) / recoveryHeader() (:130-136) / fitText() (:304).

One logistics note

The review lane won't settle this on its own. Run 40275374400 (pull_request_target, created 05:39:12Z) has been queued about nine hours with precheck-pr / precheck still queued and review-config skipped, and there is no bot review at all on adeea4b9d7. A fresh round would have to be triggered deliberately rather than waited for.

Not touching review state here — no dismiss, no approve, no /triage.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Recovery metadata now survives hook-context appends at dd3a7ab, after merging current main 2f3d1b2ff53b6ae45bc022d1ff415e679d944015. Copying a Part previously discarded its non-enumerable recovery handle. The fix carries an existing handle onto the copied Part; unmarked results stay unmarked.

Independent verification of this exact commit passed: 29 tests across four files, zero failures, 996 tests filtered by the explicit name selector. Core and Browser Use rebuilt successfully; core typecheck and scoped lint/format checks passed. The checks comprise five finalizer tests, four AgentTool tests, four scheduler tests, and the complete 16-test hook-context suite.

The preserved before/after mechanism witness calls the actual compiled conversion, hook-context and async-finalizer APIs with persistence quota exhausted and a 660-character budget:

Observation Previous head adeea4b Delivered dd3a7ab
Recovery metadata after appending context Lost Preserved
Complete 177-character TIMEOUT reason and 112-character transcript marker Both lost Both retained
Delivered output length 660 660
Witness assertion exit 1 0

The checked-in scheduler cases additionally exercise actual PreToolUse/PostToolUse message-bus composition at budget 660. They use declared known-no-file/no-retry metadata; quota exhaustion is exercised separately by the external witness above. Helper regressions cover both output/error fields through the synchronous send guard.

Disposition of the three standing mechanisms:

  • R12-1: recovery comes from producer-owned metadata; the text inverses are removed. Current regressions reject forged envelopes, transcript markers and a forged handle inside a real failed-write preview.
  • R16-1: repeated budgets retain owned recovery paths, including real paths containing spaces, rather than rediscovering them from rendered text. This delivery closes the shared hook-copy metadata-loss route found independently at the previous head.
  • R18-1: producer tests retain complete TIMEOUT/MAX_TURNS remediation and transcript markers through repeated budgets, preserving the original batch when mandatory recovery cannot fit. The same hook-copy fix protects this recovery unit.

Older raw CHANGES_REQUESTED reviews also contain R11-1/R11-2, R13-1 and R15-1/R15-2; these remain UNADJUDICATED in this scoped pass. Reviews 5512178816, 5563828042, 5565404437, 5593892502, 5634847405 and 5638787775 are retained; none is dismissed on this partial evidence.

This evidence covers mechanism composition and scheduler integration; no public CLI/provider E2E, live hook subprocess, full-suite or whole-repository verification is claimed. New CI runs have been triggered on the delivered head; approval and broader PR review remain separate.

@qwen-code-review-bot qwen-code-review-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.

Partially reviewed — gaps disclosed.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped in CI and its suite did not run locally (win32 exposure independently probed and posted as R19-17).

Not explored to full depth (tool budget reached): "agent reverse-audit (round 3)": none of my checks were cut short, but two verifications are arithmetic-derived rather than executed — I did not run the suite or the two proposed mutations, so â€Ķ; "agent reverse-audit (round 1)": the telemetry layer inside finalizeToolResponses ( logToolResultSize / ToolResultSizeEvent attribution) — not walked; the emitting lines sit outside chunk â€Ķ; "agent reverse-audit (round 1)": I did not enumerate every {...part} spread-copy site in packages/core to check which re-attach the recovery handle (only tool-hook-context.ts:109-110 veriâ€Ķ; "agent reverse-audit (round 1)": no vitest run — the pass/fail arithmetic for each new row is hand-traced from allocateTextBudget / fitText , not executed.; "agent reverse-audit (round 1)": did not run packages/core vitest, so I could not confirm whether an existing (non-diff) test in llm-chat.test.ts already reaches the shrink with non-empty â€Ķ, and 3 more.

Not reviewed: reverse audit — stopped before round 4 by the review time budget.

21 Suggestion(s) were drafted inline past the resolved critical posting floor; the CLI moved them into the deferral list below (floor enforcement — 19 listed, 2 more inside the overflow count).

Deferred under the convergence posture (round 19, not a blocker) — recorded, not requested in this round; 1 Critical(s) among them are deferred by their axes — fails-closed on new surface, where no wrong result is certified and the merge base had neither the surface nor the defect — and remain follow-up work recorded in the findings artifact:

  • packages/core/src/core/coreToolScheduler.ts:6458 — [probe] Critical [fails-closed] [new-surface] persistedOutputFiles: [] on recovery handles suppresses the send boundary's own persistence — pointerless notice, output nowhere on disk; [] isâ€Ķ
  • docs/design/pressure-aware-tool-output.md:17 — [review] R19-4: The doc promises an inline recovery handle — naming a spill envelope's Full output saved to: line alongside the subagent transcript marker — is 'kept whole above the preview, â€Ķ
  • packages/core/src/core/llm-chat.test.ts:14788 — [review] R19-5: .every() over the filtered resultOutputs passes vacuously on an empty array, and the companion resultChars(1) < 8_000 also holds at 0 — this media-charging test can silenâ€Ķ
  • packages/core/src/core/llm-chat.test.ts:14847 — [review] R19-6: The forked-chat side of the worker-invalidation catch — if (!this.isForkedChat) fileReadCache.clear(); (llm-chat.ts:3782) — is untested, and it is a third branch distinct froâ€Ķ
  • packages/core/src/core/llm-chat.test.ts:15219 — [review] R19-7: The only test named for the 'exempt tool error is cut twice' defect asserts a one-sided upper bound plus an equality between two computed sums, so any SMALLER budget — exactly â€Ķ
  • packages/core/src/core/llm-chat.ts:2958 — [review] R19-8: The new anchor-invalidation set (both usage setters, setSystemInstruction, setTools, setHistory, clearHistory, dispatch, retry restore) misses the third place that discards every tokâ€Ķ
  • packages/core/src/core/llm-chat.ts:3610 — [review] R19-9: These synthetic entries make the send boundary a NEW disk-persisting caller — the guard it replaced never wrote anything — and every artifact and ToolResultPersistedEvent is attribâ€Ķ
  • packages/core/src/core/llm-chat.ts:3630 — [review] R19-10: This pressure-vs-aggregate selection branch — the only code that can RAISE the budget back to the aggregate after a pressure preview — is entered by no test in the diff: it needs pâ€Ķ
  • packages/core/src/core/llm-chat.ts:3758 — [review] R19-11: This block stats every cut path on the LOCAL filesystem unconditionally and then also calls getExecutionEnvironment?.()?.invalidateReadCache(paths) — which for a local environmentâ€Ķ
  • packages/core/src/core/llm-chat.ts:3771 — [review] R19-12: Both new cache-disarm fallback branches are unexercised: the non-fork markAllReadsEvictedFromHistory() (its only production call site — all seven assertions in the new block are â€Ķ
  • packages/core/src/core/llm-chat.ts:3771 — [review] R19-13: The non-fork branch escalates ONE unresolvable path to a cache-GLOBAL residency disarm while holding that exact path, discarding the per-path invalidateByPath fallback that the foâ€Ķ
  • packages/core/src/core/llm-chat.ts:3793 — [review] R19-14: The main-chat clearLoadedSkillTracking call at the send boundary has no test observing it fire; the only clearLoadedSkills assertion in the pressure block is the forked-chat negâ€Ķ
  • packages/core/src/core/llm-chat.ts:5906 — [review] R19-15: This anchor clear + version bump on the previousGenerationConfig restore has no test: no case in the pressure block dispatches with options.preparedRequest , the only condition uâ€Ķ
  • packages/core/src/core/llm-chat.ts:7828 — [review] R19-16: The new usage anchor derives tokens from the RAW usageMetadata , bypassing the coerceUsageCount sanitisation the same function applies at :7117-7138 and both sibling record sitâ€Ķ
  • packages/core/src/services/microcompaction/microcompact.test.ts:2048 — [review] R19-17: (pattern, location 1 of 2 — the second is llm-chat.test.ts:14978) This expectation hardcodes the POSIX result of unescapePath , which is an intentionalâ€Ķ
  • packages/core/src/core/llm-chat.test.ts:14978 — [review] R19-17: (pattern, location 2 of 2 — the first is microcompact.test.ts:2048) This stat-argument expectation hardcodes the POSIX result of unescapePath ; on win32 the value keeps its lâ€Ķ
  • packages/core/src/tools/agent/transcript-marker.ts:20 — [review] R19-18: This module was extracted to be the marker's single source of truth, and two tests source it (coreToolScheduler.test.ts:4113, tool-hook-context.test.ts:96) — but the sâ€Ķ
  • packages/core/src/tools/tool-response-finalizer.test.ts:854 — [review] R19-19: Inside this it.each over six send budgets the assertion hardcodes 0 , so it is identical in all six rows and pins only the guard's zero-budget early return — â€Ķ
  • packages/core/src/tools/tool-response-finalizer.test.ts:936 — [review] R19-22: This test pins a broken combination in the production veto it exercises: the 'too small for a meaningful result' veto (tool-response-finalizer.ts:565-576) is evaâ€Ķ
  • packages/core/src/tools/tool-response-finalizer.ts:335 — [review] R19-20: The de-dup strip removes producer-owned strings with a GLOBAL body.split(owned).join('') — correct only while each owned is a unique handle — but two of the threeâ€Ķ
  • â€Ķand 6 more (see the run report)

Convergence: round 19 posted 3 inline comment(s), 3 of them reported for the first time; the previous round posted 3 (1 new). The rate of new findings is not falling. Batching the remaining fixes and verifying them before the next push keeps the loop from re-deriving the same set; this PR's reviews already resolve to a critical posting floor. (Observation only — nothing was withheld from this review because of this observation.)

Residual risk: this loop is persistently critical — Criticals stood in the previous round's work-list and stand again this round (3 Critical(s)), the rate of first-time findings is not falling (this round 3, previous 1), and the standing Critical backlog is not shrinking. The severity floor will not converge it. Recommendation: land-with-residual-risk — the exit is a maintainer risk-acceptance decision (merge, carrying the residual risk), not another review round. Residual-risk inventory for that decision (maintainer to complete):

standing Critical attack surface attacker-dependency blast radius
(each standing Critical) â€Ķ â€Ķ â€Ķ

Advisory only — it does not block this review.

äļ­æ–‡čŊŽ

äŧ…åŪŒæˆéƒĻ分åŪĄæŸĨåŪĄæŸĨįžšåĢå·ēæŠŦéœē。

朊åŪĄæŸĨ原文äļšč‹ąæ–‡ïž‰ïžšbuild-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

朊åŪĄæŸĨ原文äļšč‹ąæ–‡ïž‰ïžšbuild-and-test — Test (windows-latest, Node 22.x) was skipped in CI and its suite did not run locally (win32 exposure independently probed and posted as R19-17).

朊æŽĒįīĒ到å…ĻéƒĻæ·ąåšĶčūū到å·Ĩå…·č°ƒį”ĻéĒ„įŪ—ïž‰ïžš"agent reverse-audit (round 3)"ïžšnone of my checks were cut short, but two verifications are arithmetic-derived rather than executed — I did not run the suite or the two proposed mutations, so â€Ķïž›"agent reverse-audit (round 1)"ïžšthe telemetry layer inside finalizeToolResponses ( logToolResultSize / ToolResultSizeEvent attribution) — not walked; the emitting lines sit outside chunk â€Ķïž›"agent reverse-audit (round 1)"ïžšI did not enumerate every {...part} spread-copy site in packages/core to check which re-attach the recovery handle (only tool-hook-context.ts:109-110 veriâ€Ķïž›"agent reverse-audit (round 1)"ïžšno vitest run — the pass/fail arithmetic for each new row is hand-traced from allocateTextBudget / fitText , not executed.ïž›"agent reverse-audit (round 1)"ïžšdid not run packages/core vitest, so I could not confirm whether an existing (non-diff) test in llm-chat.test.ts already reaches the shrink with non-empty â€ĶåĶ有 3 æĄã€‚

朊åŪĄæŸĨ反向åŪĄčŪĄâ€”â€”čŊ„åŪĄæ—ķé—īéĒ„įŪ—äļčķģïžŒæœŠčƒ―åž€å§‹įŽŽ 4 č―Ū。

21 æĄ Suggestion åœĻå·ēč§Ģæžįš„ critical 发åļƒäļ‹é™äđ‹åĪ–čĒŦčĩ·č‰äļščĄŒå†…čŊ„čŪšïž›CLI å·ē将å…ķį§ŧå…Ĩä–đåŧķ后æļ…单äļ‹é™åžšåˆķæ‰§čĄŒâ€”â€”åˆ—å‡š 19 æĄïžŒå…ķä―™ 2 æĄčŪĄå…ĨæšĒ凚čŪĄæ•°ïž‰ã€‚

æ”ķ敛å§ŋ态äļ‹åŧķåŽïžˆįŽŽ 19 č―Ū非é˜ŧ断——å·ēčŪ°å―•ïžŒæœŽč―ŪäļčĶæą‚äŋŪæ”đïž›å…ķäļ­ 1 æĄ Critical 按å…ķåĪąčīĨæ–đ向äļŽåŊđį…§åŸšįšŋåŧķ后——fails-closed äļ” new-surface朊čŪĪčŊäŧŧä―•é”™čŊŊįŧ“果äļ” merge base æ—Ē无čŊĨåŠŸčƒ―éĒäđŸæ— čŊĨįžšé™·â€”â€”ä―œäļšåŽįŧ­å·Ĩä―œčŪ°å―•åœĻ findings å·Ĩäŧķäļ­ïžšå…ą 26 æĄïžˆåŽŸæ–‡æœŠįŋŧčŊ‘列čĄĻ见ä–đč‹ąæ–‡éƒĻ分。

æ”ķ敛情å†ĩïžšįŽŽ 19 č―Ū发åļƒäš† 3 æĄčĄŒå†…čŊ„čŪšïžŒå…ķäļ­ 3 æĄæ˜ŊéĶ–æŽĄæå‡šïž›äļŠäļ€č―Ū发åļƒäš† 3 æĄïžˆå…ķäļ­ 1 æĄéĶ–æŽĄæå‡šïž‰ã€‚æ–°å‘įŽ°įš„äš§å‡šé€ŸåšĶæēĄæœ‰äļ‹é™ã€‚把å‰Đä―™äŋŪåĪæ”’æˆä‰đ、銌čŊåŽå†æŽĻ送åŊäŧĨéŋ免åūŠįŽŊ反åĪæŽĻåŊžåŒäļ€įŧ„å‘įŽ°ïž›æœŽ PR įš„čŊ„åŪĄå·ēč§Ģ析äļš critical 发åļƒäļ‹é™ã€‚äŧ…äļšč§‚åŊŸâ€”—朎č―ŪčŊ„åŪĄæœŠå› æ­Īæ‰Ģį•™äŧŧä―•å†…åŪđ。

æŪ‹ä―™éĢŽé™Đ朎åūŠįŽŊåĪ„äšŽ persistently-critical å―Ēæ€â€”—äļŠäļ€č―Ūå·Ĩä―œæļ…单äļ­įš„ Critical 朎č―Ūäūį„ķ存åœĻ朎č―Ū 3 æĄ CriticaléĶ–æŽĄå‘įŽ°įš„é€ŸįŽ‡æēĄæœ‰äļ‹é™ïžˆæœŽč―Ū 3äļŠäļ€č―Ū 1äœŠå†ģ Critical į§Ŋ压æēĄæœ‰å‡å°‘。severity floor 无æģ•ä―ŋå…ķæ”ķ敛。åŧščŪŪïžšland-with-residual-risk——凚åĢæ˜Ŋ maintainer įš„éĢŽé™ĐæŽĨ受å†ģåŪšïžˆåˆå…Ĩåđķæ‰ŋ担æŪ‹ä―™éĢŽé™Đïž‰ïžŒč€Œéžå†åž€äļ€č―ŪčŊ„åŪĄã€‚äū›čŊĨå†ģåۚä―ŋį”Ļįš„æŪ‹ä―™éĢŽé™Đæļ…单maintainer åĄŦ写按æŊæĄæœŠå†ģ Critical 列凚「æ”ŧå‡ŧéĒ · æ”ŧå‡ŧ者äūčĩ–性 · å―ąå“čŒƒå›ī」ä ã€‚äŧ…äļšåŧščŪŪ——äļé˜ŧæ–­æœŽæŽĄčŊ„åŪĄã€‚

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

Comment on lines +3725 to +3727
const guarded =
unresolvedRead || (hookRetargetPossible && cutRead)
? entries

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.

[Critical] R19-1: [fails-closed] [regression] The hookRetargetPossible && cutRead veto is matcher-agnostic and skips the cut ENTIRELY: any configured PreToolUse/PermissionRequest hook — whatever its matcher, even one that cannot rewrite input — plus any would-be-cut read/edit/write/tool_call result waives both the new pressure budget and the pre-existing unconditional getToolOutputBatchBudget() send guard this diff removed.

One formatter hook matching only edit is enough: a ~600k-char batch containing one read_file whose target resolved fine ships uncut (~150k tokens against a 200k-char budget), including unrelated shell output. The #2566 replay also fails to converge for hook sessions — the pressured send trips the veto, effectiveTokens stays at or above auto, and tryCompress runs. The residency-invalidation block this same hunk adds (stat / markReadEvictedFromHistory / invalidateReadCache / clearLoadedSkillTracking) is dead code in exactly that configuration, so the design doc's promise that resolvable shortened reads still disarm their quote-back cache entries is unreachable there.

Witness:

PR PROBE-F1a-hook     dispatchedChars=700000 (PreToolUse hook, resolvable read_file)
PR PROBE-F1a-control  dispatchedChars=200000 (same batch, no hook)
BASE BASE-PROBE       chars=200000 (same batch, hook configured — base has no veto)
PR PROBE-F1b-hook     effectiveTokens=856042 (>= auto → tryCompress runs)
PR PROBE-F1b-control  effectiveTokens=848670 (< auto → #2566 converges)
CANDIDATE FIX: both arms flipped; exactly the two pinning it.each rows went red

Narrow the second disjunct to the design doc's actual exception: set unresolvedRead inside the loop only when a cut read-ish result has NO derivable path and a retargeting hook is possible; when paths resolved, cut the batch and disarm via paths as the rest of the hunk does. The fix must not violate: hasHooksForEvent is not tool-specific — sessionHooksManager.ts:269 iterates every session's hooks for the event with no matcher check (the matcher-aware getMatchingHooks sits immediately below), and skill hooks register WITH a matcher (registerSkillHooks.ts:97-104), so hasHooksForEvent('PreToolUse') does not imply a hook that can retarget read_file. Acceptance: narrow the existing preserves a cut read batch when a %s hook could have retargeted it rows to the unresolvable-target scenario and add a case with hasHooksForEvent true and a RESOLVABLE cut read_file asserting resultChars(1) < 20_000 and a cache disarm ran — removing the narrowing reddens the new case (probe-verified: the candidate fix flips exactly these).

äļ­æ–‡čŊŽ

[Critical] R19-1ïžš[fails-closed] [regression] hookRetargetPossible && cutRead äŋį•™åˆ†æ”ŊäļŽ matcher 无å…ģäļ”äžšæ•īä―“č·ģčŋ‡čĢå‰ŠïžšåŠčĶé…į―Ū乆äŧŧ意 PreToolUse/PermissionRequest é’Đ子无čŪšå…ķ matcher æ˜Ŋäŧ€äđˆã€æ˜ŊåĶčƒ―æ”đ写čū“å…Ĩæ‰đæŽĄé‡Œåˆæœ‰äŧŧä―•äžščĒŦčĢå‰Šįš„ read/edit/write/tool_call įŧ“果æ•đå°ąåŒæ—ķčąå…äš†æ–°įš„åŽ‹åŠ›éĒ„įŪ—å’ŒæœŽ diff 删é™Īįš„ã€åŽŸæœŽæ— æĄäŧķæ‰§čĄŒįš„ getToolOutputBatchBudget() 发送åۈåŦ。

äļ€äļŠåŠåŒđ配 edit įš„æ žåžåŒ–é’Đå­å°ąčķģåĪŸïžšåŒ…åŦäĄč·Ŋåū„åŊč§Ģæžįš„ read_file įš„įšĶ 600k 字įŽĶæ‰đæŽĄäžšåŽŸæ ·å‘å‡šïžˆįšĶ 15 äļ‡ tokenïžŒč€ŒéĒ„įŪ—æ˜Ŋ 200k 字įŽĶčŋžåŒæ— å…ģįš„ shell čū“凚。#2566 įš„åĪįŽ°č·Ŋåū„åœĻé’Đ子䞚čŊäļ­äđŸäļæ”ķ敛——压力发送č§Ķ发äŋį•™åˆ†æ”ŊeffectiveTokens äŧäļä―ŽäšŽ autotryCompress į…§åļ￉§čĄŒã€‚同äļ€ hunk 新åĒžįš„éĐŧį•™åĪąæ•ˆå—ïžˆstat / markReadEvictedFromHistory / invalidateReadCache / clearLoadedSkillTrackingåœĻčŊĨ配į―Ūä˜Ŋæ­ŧäŧĢį ïžŒčŪūčŪĄæ–‡æĄĢ「åŊč§Ģæžįš„čĢå‰ŠčŊŧ取äŧäžšč§Ģé™Īå…ķåž•į”Ļįž“å­˜æĄį›Ūã€įš„æ‰ŋčŊšåœĻæ­ĪäļåŊčūū。

čŊæŪ见ä–đ Witness äŧĢį å—ïžšæŽĒ针æ˜ūįĪšé’Đå­č‡‚å‘å‡š 700000 字įŽĶ、åŊđᅧ臂 200000、合åđķ埚įšŋ配į―Ū乆é’Đ子äŧč́到 200000候选äŋŪåĪïžˆåŽŧ掉 hookRetargetPossible && cutReadä―ŋäļĪ臂įŋŧč―ŽïžŒä°åĨ―åŠæœ‰é’‰ä―æ—§čĄŒäļšįš„äļĪäļŠ it.each į”Ļäū‹å˜įšĒ。

äŋŪåĪïžšæŠŠįŽŽäšŒäļŠæžå–éĄđæ”ķįŠ„äļščŪūčŪĄæ–‡æĄĢᜟæ­Ģįš„äū‹åĪ–â€”â€”äŧ…å―“čĒŦčĢå‰Šįš„čŊŧįąŧįŧ“果无æģ•æīūį”Ÿč·Ŋåū„äļ”åŊčƒ―å­˜åœĻæ”đ写é’Đ子æ—ķ才į―Ū unresolvedReadïž›č·Ŋåū„åŊč§Ģ析æ—ķį…§åļļčĢå‰ŠåđķæŒ‰ paths č§Ģé™Īæ­ĶčĢ…ã€‚äŋŪåĪäļåū—čŋåįš„æ—Ē有䚋åŪžïžšhasHooksForEvent äļŽå·Ĩ具无å…ģ——sessionHooksManager.ts:269 遍历čŊĨ之äŧķä‰€æœ‰äžščŊé’Đ子、äļæĢ€æŸĨ matchermatcher 感įŸĨįš„ getMatchingHooks å°ąåœĻå…ķä–đäŠ€čƒ―é’Đ子按 matcher æģĻ册registerSkillHooks.ts:97-104因æ­Ī hasHooksForEvent('PreToolUse') äļčƒ―æŽĻ凚存åœĻčƒ―æ”đ写 read_file įš„é’Đ子。銌æ”ķïžšæŠŠįŽ°æœ‰ preserves a cut read batch when a %s hook could have retargeted it äļĪ行æ”ķįŠ„åˆ°ã€Œį›Ū标äļåŊč§Ģ析」圚æ™Ŋåđķæ–°åĒž hasHooksForEvent äļš true、read_file č·Ŋåū„åŊč§Ģæžįš„į”Ļäū‹ïžŒæ–­čĻ€ resultChars(1) < 20_000 äļ”įž“å­˜č§Ģé™ĪčĒŦ调į”Ļ——į§ŧé™Īæ”ķįŠ„åŽčŊĨį”Ļäū‹åŋ…éĄŧ变įšĒæŽĒ针å·ē驌čŊįŋŧįšĒ范å›ī恰åĨ―æ˜Ŋčŋ™äļĪåĪ„ïž‰ã€‚

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Changed in 3587a76baa, but not removed. The veto is now gated on hasHooksMatchingTool per event and tool name and honors getDisableAllHooks, so a formatter hook matching only edit no longer preserves a read batch.

I did not adopt "resolvable original path ⇒ cut": updatedInput can execute B while history records A, and invalidating A would leave B resident. For a matching read hook the batch is still preserved whole rather than cut partially, because the finalizer applies one batch budget and a partial preserve would need a new per-entry path. Leaving this open for your call.

Comment thread packages/core/src/core/llm-chat.ts
Comment thread packages/core/src/tools/web-fetch.ts
@yiliang114

yiliang114 commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator Author

Superseded by #13599 (comment) for head b43c77e6ec (2026-10-27T12:50+08:00) — the findings below cover stale head dd3a7abdc8.

Local verification (merge-reference) at dd3a7abdc8: PASS at the emitted-payload wire boundary

The live head moved since the last report here (dd8d472239 → dd3a7abdc8, one own commit plus a merge of main that moved the merge-base 4b136b527f → 2f3d1b2ff5, 343 commits). That invalidates the previous baseline arm, so this is a fresh two-arm run, not a reuse of the old numbers.

Why now: this head's CI settled for the first time. The review-pr lane that had been stuck at 13/23 steps for 343 min finished completed/success at 02:20:48Z (24/24 steps, job 121827650289). All 21 runs on this head are terminal with 0 failures, and all 86 check-runs are completed with 0 failed and 0 cancelled-without-successor. mergeable=true; mergeStateStatus=BLOCKED comes only from reviewDecision=CHANGES_REQUESTED, not from CI.

Runtime identity

before arm after arm
Revision 2f3d1b2ff53b6ae45bc022d1ff415e679d944015 (real git merge-base vs origin/main) dd3a7abdc8aa0c2d49d09332225654bbcf74bffd (live head)
Worktree detached, independent detached, independent
Install pnpm install --frozen-lockfile, rc=1 pnpm install --frozen-lockfile, rc=0
node_modules 1238 entries, vitest 3.2.7 1238 entries, vitest 3.2.7
packages/core/dist absent present, then moved aside for the decisive run

Lockfile is byte-identical across arms (bd920144edd322ad19245bc0ccfb884b), but per the discipline that a shared lockfile does not prove installed-artifact equivalence, each arm got its own install — no hardlinked or donated node_modules.

The base arm's rc=1 is not a dependency failure: it is the prepare hook's tsc --build being SIGKILLed (OOM, two parallel builds), after linking had completed. Both arms run packages/core tests from TypeScript source via a scratch vitest config with globalSetup (the workspace-dist guard) removed. To prove the result does not depend on build artifacts, the after arm was re-run with its 90 MB packages/core/dist moved aside: identical output (10631 chars / [167, 10993] bytes). Both arms were therefore dist-less and symmetric for that comparison.

The harness is byte-identical in both arms (md5 835da7a4441313353141e223bedd45fc) at distinct inodes (1489885 vs 6829812), so neither arm could write through to the other.

What was observed

The probe drives the real LlmChat send path through the real OpenAIContentGenerator / DefaultOpenAICompatibleProvider into a real loopback HTTP sink, then reads back the request bodies the sink actually received. The oracle is bytes on a socket, not the return value of enforceFunctionResponseBudget (whose return value is discarded at all of its call sites, so it cannot be the send boundary).

Scenario: turn 1 seeds a usage anchor and requests the shell tool; turn 2 submits a 20,000-char tool result.

# Scenario Arm Prompt tokens Chars on wire Request bytes Payload head
1 near auto-compaction before 846,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
2 near auto-compaction after 846,000 10631 [167, 10993] [{"type":"text","text":"Tool output truncated.â€Ķ
3 far from auto-compaction (control) before 100,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
4 far from auto-compaction (control) after 100,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
5 near auto, dist removed (identity control) after 846,000 10631 [167, 10993] [{"type":"text","text":"Tool output truncated.â€Ķ

Causal difference in one sentence: near the trigger the emitted tool result shrinks from 20,027 to 10,631 characters and gains a real Tool output truncated. notice, while far from the trigger both arms emit byte-identical payloads — the pressure path is gated on the usage anchor and leaves static behavior untouched.

Rows 3–4 are the control that rules out "the PR just truncates more aggressively everywhere". Rows 1 and 2 differ, which is the hard indicator that the rig still arms; identical observations in both arms would have meant the harness lost discriminating power rather than the behavior being unchanged.

Regression pin

Head's own packages/core/src/core/tool-hook-context.test.ts (16 cases, including the two preserves producer recovery through context and another budget cases added by dd3a7abdc8) run against base's unfixed tool-hook-context.ts:

base:  Tests  2 failed | 14 passed (16)
       × preserves producer recovery through context and another budget (output)
       × preserves producer recovery through context and another budget (error)
       AssertionError: expected 'Tool output truncated.\n\nSubagent diâ€Ķ'
                       to contain 'Subagent did not complete (terminate â€Ķ'
head:  Tests  16 passed (16)

Collected-count parity holds (16 == 16), so the red is a genuine assertion failure, not a collection failure. Base's tool-hook-context.ts was confirmed unfixed beforehand (grep -c setToolOutputRecovery → 0), and the file was restored to its pristine md5 b01c2f0c530e088636b9aaa65c477187 afterwards.

Controlled hybrid, labelled: that test imports tools/tool-output-recovery.ts and tools/agent/transcript-marker.ts, which this PR adds and which do not exist at the merge-base. Those two additive leaf modules were copied into the base arm so the test could collect; base's own tool-hook-context.ts was left untouched. The pin therefore isolates the recovery-carry behaviour, not the existence of the new modules.

Independent oracle

The PR's own suites at head, run separately from the probe: 10 files / 2017 tests passed (llm-chat 708, coreToolScheduler 539, agent 421, web-fetch 114, microcompact 103, fileReadCache 61, tool-response-finalizer 49, tool-hook-context 16, tool-response-finalizer.integration 3, goal-turn-integration 3). This cross-checks that the mechanism works under the product's own assertions, not only under my harness.

Boundaries and gaps

  • The provider is a scripted loopback sink, not a real model. This is real-stack, controlled-provider evidence about what is sent. It is not real-model acceptance, and nothing here proves a provider would accept the shrunk payload.
  • No real CLI/TUI process. The send path ran in-process under vitest, not through a spawned qwen binary. A full CLI end-to-end leg remains NOT VERIFIED (not N/A) — it needs the workspace dist chain, which currently fails to build on this machine (build --cli-only dies in browser-use; bundle fails to resolve @qwen-code/channel-base and @qwen-code/acp-bridge/*).
  • Base arm install rc=1 (OOM in prepare), mitigated by the dist-removed re-run in row 5 rather than by a clean base build.
  • Scope is the emitted-payload wire boundary. Persistence of full originals, the PreToolUse empty-argument repair path, worker cache residency, and media/framing charging order were not independently exercised this round; the product's own suites cover parts of them, but I am not claiming separate evidence for them.

A local PASS is not a merge approval. reviewDecision is still CHANGES_REQUESTED.

VERDICT: PASS — scoped to the emitted-payload wire boundary at dd3a7abdc8aa0c2d49d09332225654bbcf74bffd, with the CLI end-to-end leg recorded as NOT VERIFIED.

åیæ•īäļ­æ–‡æŠĨ告

朎地銌čŊïžˆmerge-reference@ dd3a7abdc8ïžšåœĻå‘å‡šč――č·įš„ wire čūđį•ŒäļŠ PASS

č‡ŠæœŽ PR äļŠäļ€äŧ―æŠĨ告äŧĨæĨ live head å·ē前į§ŧdd8d472239 → dd3a7abdc8ïžšäļ€äļŠč‡Šæœ‰æäšĪ + äļ€æŽĄåˆåđķ mainmerge-base į”ą 4b136b527f 前į§ŧ到 2f3d1b2ff5į›ļå·Ū 343 äļŠæäšĪ。čŋ™ä―ŋæ—§įš„åŸšįšŋč‡‚ä―œåšŸïžŒå› æ­Ī朎č―Ūæ˜Ŋå…Ļæ–°įš„äļĪ臂驌čŊïžŒäļæ˜ŊåĪį”Ļ旧数æŪ。

äļšäŧ€äđˆæ˜ŊįŽ°åœĻïžš čŊĨ head įš„ CI éĶ–æŽĄč―åœ°ã€‚æ­Īå‰åĄåœĻ 13/23 æ­Ĩ、å·ēč·‘ 343 åˆ†é’Ÿįš„ review-pr č…ŋ䚎 02:20:48Z 变äļš completed/success24/24 æ­Ĩjob 121827650289。čŊĨ head äļŠ 21 æĄ run å…ĻéƒĻčŋ›å…Ĩįŧˆæ€ã€0 åĪąčīĨïž›86 äļŠ check-run å…ĻéƒĻ completed0 failed、0「无成功后įŧ§įš„ cancelled」。mergeable=trueïž›mergeStateStatus=BLOCKED 及æĨ臩 reviewDecision=CHANGES_REQUESTEDäļŽ CI 无å…ģ。

čŋčĄŒæ—ķčšŦäŧ―

before 臂 after 臂
į‰ˆæœŽ 2f3d1b2ff53b6ae45bc022d1ff415e679d944015åŊđ origin/main įš„įœŸåŪž git merge-base dd3a7abdc8aa0c2d49d09332225654bbcf74bffdlive head
worktree į‹ŽįŦ‹ detached į‹ŽįŦ‹ detached
åŪ‰čĢ… pnpm install --frozen-lockfilerc=1 pnpm install --frozen-lockfilerc=0
node_modules 1238 éĄđvitest 3.2.7 1238 éĄđvitest 3.2.7
packages/core/dist įžšåĪą 存åœĻå†ģåŪšæ€§é‚Ģäļ€č·‘前å·ēį§ŧ垀

äļĪ臂 lockfile é€å­—čŠ‚į›ļ同bd920144edd322ad19245bc0ccfb884bïž‰ïžŒä―†æŒ‰ã€Œlockfile į›ļ同äļčƒ―å•į‹ŽčŊæ˜Žå·ēåŪ‰čĢ…äūčĩ–äļŽčŋčĄŒäš§į‰Đį­‰äŧ·ã€įš„įšŠåū‹ïžŒæŊč‡‚å„č‡Šį‹ŽįŦ‹åŪ‰čĢ…â€”â€”æēĄæœ‰įĄŽé“ūæŽĨ、æēĄæœ‰å€Ÿį”Ļ donor įš„ node_modules。

base č‡‚įš„ rc=1 äļæ˜Ŋäūčĩ–åĪąčīĨïžšæ˜Ŋ prepare é’Đå­é‡Œįš„ tsc --build čĒŦ SIGKILLäļĪäļŠåđķčĄŒæž„åŧšåŊžč‡ī OOMïž‰ïžŒč€Œé“ūæŽĨåœĻæ­Īäđ‹å‰å·ēåŪŒæˆã€‚äļĪč‡‚éƒ―į”ĻåŽŧ掉 globalSetupworkspace dist åۈåŦïž‰įš„äļīæ—ķ vitest configäŧŽ TypeScript æšį č·‘ packages/core æĩ‹čŊ•。äļščŊæ˜Žįŧ“æžœäļäūčĩ–æž„åŧšäš§į‰Đafter 臂åœĻ把 90 MB įš„ packages/core/dist į§ŧåž€åŽé‡č·‘ïžščū“凚åیå…Ļäļ€č‡ī10631 字įŽĶ / [167, 10993] å­—čŠ‚ïž‰ã€‚å› æ­Īé‚ĢæŽĄåŊđæŊ”äļ­äļĪč‡‚éƒ―æ˜Ŋ无 dist äļ”åŊđį§°įš„ã€‚

čĢ…į―ŪåœĻäļĪč‡‚é€å­—čŠ‚į›ļ同md5 835da7a4441313353141e223bedd45fc、inode 乒äļį›ļ同1489885 vs 6829812äŧŧäļ€č‡‚éƒ―无æģ•写įĐŋ到åĶäļ€č‡‚。

观æĩ‹åˆ°äŧ€äđˆ

æŽĒ针éĐąåŠĻᜟåŪž LlmChat 发送č·Ŋåū„įŧįœŸåŪž OpenAIContentGenerator / DefaultOpenAICompatibleProvider打čŋ›įœŸåŪž loopback HTTP sink再čŊŧ回 sink åŪžé™…æ”ķåˆ°įš„čŊ·æą‚ä―“ã€‚oracle æ˜Ŋ socket äļŠįš„å­—čŠ‚ïžŒäļæ˜Ŋ enforceFunctionResponseBudget įš„čŋ”回倞čŊĨå‡―æ•°čŋ”回倞åœĻå…ķå…ĻéƒĻ调į”Ļį‚đéƒ―čĒŦäļĒ垃因æ­ĪåۃäļåŊčƒ―æ˜Ŋ发送čūđį•Œïž‰ã€‚

圚æ™ŊïžšįŽŽ 1 č―Ūį§äļ‹į”Ļ量锚į‚đåđķčŊ·æą‚ shell å·Ĩå…·ïž›įŽŽ 2 č―ŪæäšĪäļ€äļŠ 20,000 字įŽĶįš„å·Ĩ具įŧ“果。

# 圚æ™Ŋ 臂 prompt tokens äļŠįšŋ字įŽĶ数 čŊ·æą‚å­—čŠ‚ č――č·åž€åĪī
1 æŽĨčŋ‘臊åŠĻ压įžĐ before 846,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
2 æŽĨčŋ‘臊åŠĻ压įžĐ after 846,000 10631 [167, 10993] [{"type":"text","text":"Tool output truncated.â€Ķ
3 čŋœįĶŧ臩åŠĻ压įžĐ控åˆķįŧ„ before 100,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
4 čŋœįĶŧ臩åŠĻ压įžĐ控åˆķįŧ„ after 100,000 20027 [167, 20389] [{"type":"text","text":"xxxxâ€Ķ
5 æŽĨčŋ‘臊åŠĻ压įžĐ、į§ŧé™Ī distčšŦäŧ―控åˆķ after 846,000 10631 [167, 10993] [{"type":"text","text":"Tool output truncated.â€Ķ

äļ€åĨčŊå› æžœïžšæŽĨčŋ‘č§Ķ发į‚đæ—ķïžŒå‘å‡šįš„å·Ĩ具įŧ“æžœäŧŽ 20,027 字įŽĶįžĐ到 10,631 字įŽĶåđķåļĶäļŠįœŸåŪžįš„ Tool output truncated. 提įĪšïž›čŋœįĶŧč§Ķ发į‚đæ—ķäļĪč‡‚å‘å‡šé€å­—čŠ‚į›ļåŒįš„č――č·â€”â€”åŽ‹åŠ›č·Ŋåū„äŧĨį”Ļ量锚į‚đäļšé—ĻæŽ§ïžŒé™æ€čĄŒäļšæœŠčĒŦæ”đåŠĻ。

įŽŽ 3、4 行æ˜Ŋ控åˆķįŧ„į”ĻæĨ排é™Ī「čŋ™äļŠ PR 及æ˜Ŋ到åĪ„éƒ―æ›īæŋ€čŋ›åœ°æˆŠæ–­ã€ã€‚įŽŽ 1、2 čĄŒå­˜åœĻå·Ū垂čŋ™æ­Ģæ˜ŊčĢ…į―Ūäŧį„ķ arm čĩ·æĨįš„įĄŽæŒ‡æ ‡ïž›č‹ĨäļĪ臂观æĩ‹å€žį›ļ同é‚ĢčŊŽčĢ…į―ŪåĪąåŽŧåˆĪåˆŦåŠ›ïžŒč€Œäļæ˜Ŋ行äļšäļ€č‡ī。

å›žå―’é’‰

head 臩åļĶįš„ packages/core/src/core/tool-hook-context.test.ts16 äļŠį”Ļäū‹ïžŒåŦ dd3a7abdc8 新åĒžįš„äļĪæĄ preserves producer recovery through context and another budgetïž‰č·‘åœĻ base 朊äŋŪåĪįš„ tool-hook-context.ts äļŠïžš

base:  Tests  2 failed | 14 passed (16)
       × preserves producer recovery through context and another budget (output)
       × preserves producer recovery through context and another budget (error)
       AssertionError: expected 'Tool output truncated.\n\nSubagent diâ€Ķ'
                       to contain 'Subagent did not complete (terminate â€Ķ'
head:  Tests  16 passed (16)

äļĪ臂æ”ķ集数į›ļ同16 == 16所äŧĨčŋ™äļŠįšĒæ˜ŊᜟåŪžæ–­čĻ€åĪąčīĨäļæ˜Ŋæ”ķ集åĪąčīĨ。䚋先å·ēįĄŪčŪĪ base įš„ tool-hook-context.ts æ˜Ŋ朊äŋŪåĪį‰ˆæœŽïžˆgrep -c setToolOutputRecovery → 0䚋后čŊĨ文äŧķå·ēčŋ˜åŽŸåˆ°åŽŸå§‹ md5 b01c2f0c530e088636b9aaa65c477187。

受控 hybrid明įĄŪ标æģĻïžš čŊĨæĩ‹čŊ• import 乆 tools/tool-output-recovery.ts äļŽ tools/agent/transcript-marker.tsčŋ™äļĪäļŠæ–‡äŧķį”ąæœŽ PR 新åĒžã€åœĻ merge-base äļŠäļå­˜åœĻ。äļšäš†čŪĐæĩ‹čŊ•čƒ―åΟæ”ķ集把čŋ™äļĪäļŠįšŊčŋ―åŠ įš„åķ子æĻĄå—æ‹·čŋ›äš† base č‡‚ïž›base č‡Šå·ąįš„ tool-hook-context.ts 朊åŠĻ。因æ­Īčŋ™äļŠé’‰éš”įĶŧįš„æ˜Ŋ「recovery æ˜ŊåĶčĒŦåļĶčŋ‡åŽŧ」čŋ™äļ€čĄŒäļšïžŒäļæ˜Ŋ新æĻĄå—æ˜ŊåĶ存åœĻ。

į‹ŽįŦ‹ oracle

äļŽæŽĒé’ˆåˆ†åž€č·‘įš„ head äū§ PR č‡Šæœ‰åĨ—äŧķïžš10 äļŠæ–‡äŧķ / 2017 äļŠį”Ļäū‹å…Ļįŧŋllm-chat 708、coreToolScheduler 539、agent 421、web-fetch 114、microcompact 103、fileReadCache 61、tool-response-finalizer 49、tool-hook-context 16、tool-response-finalizer.integration 3、goal-turn-integration 3。čŋ™æĄäšĪ叉銌čŊčŊŽčŊĨ朚åˆķåœĻäš§å“č‡Šå·ąįš„æ–­čĻ€äļ‹äđŸæˆįŦ‹ïžŒč€ŒäļåŠåœĻæˆ‘įš„čĢ…į―ŪäˆįŦ‹ã€‚

čūđį•ŒäļŽįžšåĢ

  • provider æ˜Ŋč„šæœŽåŒ–įš„ loopback sinkäļæ˜ŊᜟåŪžæĻĄåž‹ã€‚ čŋ™æ˜ŊᜟåŪžæ ˆã€å—æŽ§ provider įš„čŊæŪčŊæ˜Žįš„æ˜Ŋ发凚䚆äŧ€äđˆïž›äļæ˜ŊᜟåŪžæĻĄåž‹įš„æŽĨæ”ķčŪĪåŊäđŸäļčƒ―čŊæ˜Žäŧŧä―• provider 侚æŽĨ受įžĐå°åŽįš„č――č·ã€‚
  • æēĄæœ‰įœŸåŪž CLI/TUI čŋ›įĻ‹ã€‚ 发送č·Ŋåū„æ˜ŊåœĻ vitest čŋ›įϋ内éĐąåŠĻįš„ïžŒäļæ˜Ŋ通čŋ‡ spawn 凚æĨįš„ qwen 乌čŋ›åˆķ。åیæ•ī CLI įŦŊ到įŦŊé‚Ģäļ€č…ŋäŧčŪ°äļš NOT VERIFIEDäļæ˜Ŋ N/A——åŪƒéœ€č́ workspace dist é“ūïžŒč€ŒčŊĨé“ūåœĻæœŽæœšå―“å‰æž„åŧšäļå‡šæĨbuild --cli-only æ­ŧåœĻ browser-useïž›bundle č§Ģ析äļåˆ° @qwen-code/channel-base äļŽ @qwen-code/acp-bridge/*。
  • base 臂åŪ‰čĢ… rc=1prepare é˜ķæŪĩ OOMïž‰ïžŒį”ąįŽŽ 5 čĄŒįš„į§ŧé™Ī dist é‡č·‘äšˆäŧĨįž“č§ĢïžŒč€Œäļæ˜Ŋ靠äļ€æŽĄåđēå‡€įš„ base 构åŧšã€‚
  • 范å›ī限åۚåœĻå‘å‡šč――č·įš„ wire čūđį•Œã€‚åیæ•īåŽŸæ–‡įš„č―į›˜ã€PreToolUse įĐšå‚æ•°čĄĨé―č·Ŋåū„、worker įž“å­˜éĐŧį•™ã€äŧĨåŠåŠ’ä―“/įŧ“æž„čŪĄčīđéĄšåšïžŒæœŽč―ŪæœŠį‹ŽįŦ‹å–čŊïž›äš§å“č‡Šæœ‰åĨ—äŧķčĶ†į›–äš†å…ķäļ­äļ€éƒĻåˆ†ïžŒä―†æˆ‘äļäļšæ­Īå•į‹Žå̰᧰čŊæŪ。

朎地 PASS äļį­‰äšŽåˆåđķæ‰đ准。reviewDecision äŧæ˜Ŋ CHANGES_REQUESTED。

įŧ“čŪšïžšPASS——范å›ī限åۚåœĻ dd3a7abdc8aa0c2d49d09332225654bbcf74bffd įš„å‘å‡šč――č· wire čūđį•ŒïžŒCLI įŦŊ到įŦŊé‚Ģäļ€č…ŋčŪ°äļš NOT VERIFIED。

@yiliang114

Copy link
Copy Markdown
Collaborator Author

R19 Critical follow-up delivered at 07e1312bcbe2. Current branch head is 1050ba52f2fc, an ordinary merge of main 74efa46640e1d8c6b34b99b5f5bd191f9586c4b1 to incorporate the new lint gate. Every file under packages/core is byte-identical to 07e1312b (equal core tree OIDs, empty first-parent core diff); no core tests or typecheck were rerun on the merge. The 875 test results and type/lint results below remain bound to 07e1312b. Merge-only checks passed: clean tree, exact parents, git diff --check, and current-main ancestry, which includes the gate update. Fresh remote CI must validate the merge head. Independent verification adjudicated all three posted findings REAL at the baseline → FIXED. This does not certify unrelated review findings or change anyone else's review state.

  • R19-1 — hook matcher gate. The send guard uses the existing planner/session matcher query. In the local hook system, a Bash-only hook no longer exempts read_file from the budget; matching read hooks still protect potentially retargeted reads. The verifier also probed real hook objects with read_file, Read, *, and session isolation.
  • R19-2 — send attribution and duplicate telemetry. Finalizer source/defaults and the size-event gate retain ordinary batch logging while the send guard stamps send_guard and emits no synthetic size event. Disabling boundary diagnostics still retains ordinary scheduler size telemetry.
  • R19-3 — recovery file content. The successful summary return no longer advertises extracted raw text as the summary's recovery artifact. Finalization persists the actual shipped summary; the processing-failure arm retains the raw-body artifact.

The committed regressions pin four groups (nine parameter rows total):

  1. Matching-hook table: PreToolUse/Bash and PermissionRequest/Bash cut and invalidate cache residency; PreToolUse/read_file and PermissionRequest/* preserve the result and cache residency — 4 rows.
  2. Pressure send: checks the submitted part's send_guard provenance and absence of send-boundary-* size events — 1 row.
  3. Finalizer attribution: explicit send_guard emits zero size events; default batch retains one with boundary diagnostics disabled — 2 rows.
  4. Actual recovery-file reads: success reads back the complete shipped summary; processing failure reads back the raw body — 2 rows.

Independent checks in the prepared verifier worktree, bound to 07e1312bcbe250e290638040a41b541c4fb06b84:

Check Command Result
Core types cd packages/core && npx tsc --noEmit exit 0
Chat tests cd packages/core && npx vitest run src/core/llm-chat.test.ts 710/710 passed, exit 0
Tool tests cd packages/core && npx vitest run src/tools/tool-response-finalizer.test.ts src/tools/web-fetch.test.ts 165/165 passed, exit 0
Scoped lint npx eslint over the seven changed files exit 0

The seven lint inputs are packages/core/src/core/llm-chat.ts, packages/core/src/core/llm-chat.test.ts, packages/core/src/hooks/hookSystem.ts, packages/core/src/tools/tool-response-finalizer.ts, packages/core/src/tools/tool-response-finalizer.test.ts, packages/core/src/tools/web-fetch.ts, and packages/core/src/tools/web-fetch.test.ts. Prettier and git diff --check also passed. The final commit changes only the test fixture cast (as Config → as unknown as Config) relative to dde7004a; production is byte-identical to both dde7004a and the product-probed 3587a76.

Limits: the canonical worktree lacks complete node_modules, so behavioral tests, lint and typecheck ran in the independent verifier's prepared environment. Managed/hosted mode still falls back to event-level managedDispatcher.hasHooksForEvent where local matcher information is unavailable; matcher-agnostic veto remains there. A matching read hook still preserves the whole batch, including unrelated results. This is not a full monorepo suite/build, CLI/ACP, real chat-send E2E, OTLP export, or managed-runtime verification. Remote CI is separate.

Deferred by review: the residual UX question about a truncation notice with no persisted file is outside this repair. The finalizer honors [] and does not retry persistence; recoveryHeader emits no artifact pointer for []; the scheduler unions real file lists. These operations do not invent a saved-file path. This records the mechanics without calling the deferred question fixed or false. The 21 Suggestions remain deferred.

Retained verifier artifacts are local paths, not public download URLs: /tmp/pr13599-final-gate/EVIDENCE.md, close-tsc.txt, close-llmchat.txt, close-tools.txt, and close-lint.txt in that directory. The prior contrast logs gate-tsc-final.txt and gate-tsc-baseline.txt retain the pre-fix fixture error and clean baseline. The report records exact SHAs, commands, exit codes, counts, per-finding verdicts and residuals.

äļ­æ–‡čŊŽ

朎č―ŪäŋŪåĪå·ēæŽĻ送äļš 07e1312bcbe250e290638040a41b541c4fb06b84随后æ™Ū通合åđķ main 74efa466 įšģå…Ĩ最新 lint gateïžŒå―“å‰åˆ†æ”Ŋäļš 1050ba52f2fc9a85f8efc2fac19c3c733fb91438。æ•īäļŠ core į›Ūå―•æ ‘äļŽ 07e1312b åیå…Ļį›ļ同因æ­Ī朊重åĪæ‰§čĄŒ core æĢ€æŸĨïž›äŧĨäļ‹ 875 æĄæĩ‹čŊ•、įąŧ型和 lint įŧ“果及įŧ‘åۚ 07e1312b。合åđķæäšĪ通čŋ‡åđē净å·Ĩä―œæ ‘ã€įēūįĄŪįˆķ提äšĪ、å·Ūåž‚įĐšį™―æĢ€æŸĨ和 main įĨ–å…ˆå…ģįģŧæĢ€æŸĨäŧį­‰åū…æ–°čŋœįŦŊ CIã€‚į‹ŽįŦ‹éЌčŊå°†äļ‰éĄđå·ē发åļƒé—ŪéĒ˜å‡åˆĪåۚäļšâ€œåŸšįšŋ存åœĻ → 朎č―ŪäŋŪåĪâ€ïžšæœŽåœ° hook 按åŪžé™…å·Ĩ具åŒđ配发送čūđį•Œä―ŋį”Ļ send_guard äļ”äļå†é‡åĪå‘é€åˆæˆåĪ§å°äš‹äŧķæ™Ū通æ‰đæŽĄéĨæĩ‹äŋį•™ïž›į―‘éĄĩ摘č́čĒŦčĢå‰Šæ—ķæĒåĪæ–‡äŧķäŋå­˜įœŸåŪžæ‘˜čĶïžŒå΄ᐆåĪąčīĨäŧäŋå­˜åŽŸå§‹æ­Ģ文。äļŠéĒįš„å››įŧ„äđčĄŒå›žå―’分åˆŦäŋæŠĪčŋ™äš›čĄŒäļšã€‚

į‹ŽįŦ‹įŽŊåǃčŋčĄŒ chat æĩ‹čŊ• 710/710、äļĪäŧ―å·Ĩ具æĩ‹čŊ• 165/165core įąŧ型æĢ€æŸĨ和ä–‡äŧķ ESLint 均äļš exit 0。最后äļ€äļŠæäšĪį›ļåŊđ dde7004a 及æ”đæĩ‹čŊ•åŊđčąĄįš„įąŧåž‹č―ŽæĒïžŒį”Ÿäš§äŧĢ᠁äļŽå·ē驌čŊįš„ 3587a76 åیå…Ļį›ļåŒã€‚č§„čŒƒå·Ĩä―œæ ‘įžšå°‘åیæ•īäūčĩ–朊将å…ķ Vitest åŊåŠĻåĪąčīĨįŪ—ä―œčĄŒäļšæĩ‹čŊ•įŧ“果。

čūđį•ŒäđŸäŋį•™ïžšmanaged/hosted æĻĄåžįžšå°‘朎地 matcher äŋĄæŊäŧæŒ‰äš‹äŧķįš§äŋåۈäŋį•™ïž›åŒđ配čŊŧå·Ĩå…·įš„ hook äŧäžšäŋį•™æ•īäļŠæ‰đæŽĄã€‚æœŽč―ŪæēĄæœ‰éЌčŊæ•īäļŠ monorepo æĩ‹čŊ•或构åŧšã€CLI/ACPã€įœŸåŪžå‘é€ E2E、OTLP åŊžå‡šæˆ– managed čŋčĄŒæ—ķäđŸäļäŧĢčĄĻčŋœįŦŊ CI å·ē通čŋ‡ã€‚å…ģ䚎æēĄæœ‰äŋå­˜æ–‡äŧķæ—ķäŧæįĪščĢå‰Šįš„ UX é—ŪéĒ˜æŒ‰ review åŧķæœŸïž›[] äļé‡čŊ•、äļäš§į”Ÿæ–‡äŧķæŒ‡é’ˆïžŒč°ƒåšĶå™Ļ及合åđķᜟåŪžåˆ—čĄĻäļæŪæ­ĪåŪĢį§°åŧķæœŸé—Ūéǘå·ēäŋŪåĪæˆ–äļæˆįŦ‹ã€‚21 éĄđ Suggestions įŧ§įŧ­åŧķæœŸã€‚

@yiliang114
yiliang114 dismissed qwen-code-review-bot’s stale review October 27, 2026 04:27

The three posted round-19 Criticals R19-1/R19-2/R19-3 were fixed in 3587a76 and independently verified in final candidate 07e1312. Current head 1050ba5 is an ordinary main merge with byte-identical core inputs; its Lint & Static passed with no other failure in the current snapshot. Evidence: #13599 (comment) . The review itself deferred the 21 Suggestions and [] Critical as non-blocking follow-up; those remain deferred, not claimed fixed or false. Dismissing only this superseded blocking verdict, not resolving threads or granting approval.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Narrowed this branch in b43c77e without opening another PR.

  • Removed the independent microcompaction path-normalization fix and its test from this PR. The pressure send path keeps its own normalization and regression coverage; the extracted change is preserved locally for separate follow-up, not claimed fixed here.
  • Reused the selected preview, removed a redundant Part copy, and consolidated an exactly duplicated lifecycle test. The latest hook-matching, send-telemetry and fetched-summary fixes are unchanged.

This commit is +30/−89 across five files. Against the same base, the PR goes from 25 files (+2601/−201) to 24 files (+2551/−210).

Verification: 2,083 tests passed across 11 affected Core suites; Core build/typecheck and scoped ESLint/Prettier passed. Two independent source reviews found no blockers in this cleanup, and the committed patch matches the tested snapshot after pre-commit hooks. The installed local Qwen review engine could not complete because its model request returned HTTP 403; that gate remains unverified. No fresh CLI/provider or Windows acceptance is claimed, and new-head CI is separate.

@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.

COMMENT at head b43c77e6 — not approving. The newest assessment on this PR is round 19 (reviewed at dd3a7abd, 2026-10-27T02:08Z, since dismissed by the head move), and it filed three Criticals. Five commits have landed since, no round has reassessed any of them, and within this pass's budget their status is unconfirmed — not fixed, and not proved still standing either.

1. Round 19's three Criticals at the live head

ID Anchor Since round 19's dd3a7abd 1050ba52 → b43c77e6 Status here
R19-1 — the hookRetargetPossible && cutRead veto is matcher-agnostic and skips the cut core/llm-chat.ts:3727 changed changed again unconfirmed
R19-2 — routing the send-boundary shrink through finalizeToolResponses regresses telemetry core/llm-chat.ts:3730 changed changed again unconfirmed
R19-3 — the success path declares [persistedTextPath], an artifact holding the fetched text tools/web-fetch.ts:690 changed identical unconfirmed

The commits added after round 19 do aim at these areas: 3587a76b ("correct send guards and fetched-summary recovery"), dde7004a and 07e1312b adding send-guard fixtures, and b43c77e6 ("narrow pressure-budget scope and reuse previews"). That is real evidence of intent, but a commit subject is not a verification, and each finding turns on something a subject cannot settle:

  • R19-1 — whether the veto is still matcher-agnostic along the call chain that actually reaches it, now that the guard has been rewritten twice.
  • R19-2 — whether the telemetry path is genuinely restored rather than re-routed through finalizeToolResponses a second time.
  • R19-3 — whether the artifact declared on the success path holds the same text the shrink later emits. I read the hunk at this head: the PR adds one declaration and one use, and the use is on the content-processing-failed branch, not the success branch —
    const persistedOutputFiles = entry.persistedTextPath
      ? [entry.persistedTextPath]
      : entry.reduced ? [] : undefined;
    Whether that three-way value is correct for the branch that declares it, and for the recovery machinery that consumes persistedOutputFiles downstream, needs the consumer traced. I did not have budget for that trace.

2. Round 18's three carried Criticals appear addressed

R12-1, R16-1 and R18-1 — the three recorded as standing at adeea4b9 — do not appear in round 19's finding list, which carries forward exactly what it re-verified. Their anchors are consistent with that: agent/transcript-marker.ts (R18-1) is byte-identical between dd3a7abd and this head, tools/truncation.ts (R12-1) is not in this PR's change set at all, and tool-response-finalizer.ts (R16-1) did change. Since round 19 is the newest assessment and dropped all three, I treat them as addressed and am not re-deriving them here.

3. My own scan is incomplete, and the newest commit adds unreviewed surface

At this head the change set is 24 files, +2551/−210, all of packages/core/src plus two design docs. Not read end-to-end in this pass: llm-chat.ts (+404/−65 — the pressure-budget anchor, its invalidations, and the send-boundary shrink that both round-19 llm-chat findings sit inside, and the file b43c77e6 just refactored), tool-response-finalizer.ts (+230/−51), agent.ts (+45/−44), hookSystem.ts (+23/−1), microcompact.ts, fileReadCache.ts, tokenEstimation.ts. I did read the scheduler hunks, tool-output-recovery.ts, the recovery re-attach fix, the web-fetch.ts hunk above, and the round 18/19 ledgers. That is not coverage of this diff, and I am recording it as an unperformed gate rather than reporting findings I cannot prove.

b43c77e6 is a refactor(core) that narrows the pressure-budget scope and reuses previews — i.e. new logic in precisely the file that carries the two open findings, landing after the last review round.

4. The head moved during this pass

This review was begun against 1050ba52 and re-anchored to b43c77e6 before publishing; the pre-publish refresh caught the move, so nothing here is asserted against a stale head. Two heads in one pass is worth noting on its own: a conclusive round needs the branch to sit still long enough to be read.

5. CI

Read at review time, after the newest commits: the suite had been retriggered and the unit and integration lanes were still in flight, with no failure attributable to this PR's change set. CI is not the reason for this verdict; section 1 is.

What would unblock

One review round at a head that then stays put, re-assessing R19-1, R19-2 and R19-3 against the code as it stands. If those three are fixed, what remains is scan coverage of llm-chat.ts and tool-response-finalizer.ts — not any defect I can currently prove. This PR is also far past the ~5-round point at which this repo asks that only Critical fixes land, so further rounds are best spent on these three rather than on new surface.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Local verification: PASS

  • Mode: merge-reference
  • After arm (PR head): b43c77e6eceac9f8dc6097c7d179f21518d163dd
  • Before arm (merge-base): 74efa46640e1d8c6b34b99b5f5bd191f9586c4b1 (also the current PR base)
  • Surface: TUI + outbound wire
  • Runtime: real CLI bundle dist/cli.js on Node v22.22.0, isolated HOME, 120×34 tmux; head bundle 91cb053bâ€Ķc54, baseline bundle dbb34079â€Ķ866
  • Provider boundary: scripted loopback OpenAI-compatible server—not a real model. This proves what the client emits and how the TUI reports it, not provider acceptance.
  • A local PASS is not a merge approval; no product code was changed, pushed, or re-run in CI for this verification.

Claims

# Required claim Observation / evidence Result
1 Runtime identity both arms built from the SHAs above with npm run build && npm run bundle; bundle hashes recorded PASS
2 Observed change near auto: baseline 20,000 chars + compaction notice; head 10,576 chars with truncation notice and no compaction notice PASS
3 Causal evidence same-session request ledger: baseline toolChars=20000, head toolChars=10576, head starts with Tool output truncated. PASS
4 Control far from auto (prompt_tokens=100000): both arms emit 20,000 chars unchanged PASS
5 Regression pin disabling pressureToolOutputBudget fails 23 pressure tests; restored head passes 49 (661 filtered) PASS

Key evidence

Same scenario in both arms: 1M window, first response reports prompt_tokens=846000 (84.6%, auto trigger 850,000), model requests read_file on a 20,000-character file, then the CLI sends the result back.

Scenario Baseline 74efa466 Head b43c77e6
Near auto, tool result on the wire 20,000 chars 10,576 chars, starts Tool output truncated. Persisted tool-output artifact: â€Ķ
Near auto, TUI IMPORTANT: â€Ķ compressed from: ~851026 to ~845660 tokens.; context 84.6% no compaction notice; context 84.6%
Far from auto, wire 20,000 chars 20,000 chars

Baseline TUI (verbatim):

✓ Read â€Ķ/large.txt
● IMPORTANT: This conversation approached the input token limit for qwen3-coder-plus. A compressed context will be
  sent for future messages (compressed from: ~851026 to ~845660 tokens).
◆ done
1.0m Context 84.6% used

Head TUI (verbatim, same prompt and fixture):

✓ Read â€Ķ/large.txt
◆ done
1.0m Context 84.6% used

Causal difference: the baseline's 20,000-character result adds roughly 5k tokens to an 846k prompt and crosses the 850k auto trigger; the head's 10,576-character result adds roughly 2.6k tokens and stays below it. The TUI notice and the wire ledger agree in the same session.

Controls and regression pin

  • Far-from-trigger control: prompt_tokens=100000 with the same batch produced 20,000 chars in both arms, so the pressure path is gated on the usage anchor rather than shortening everywhere.
  • Negative control: replacing the adaptive budget return with undefined and rerunning the pressure block failed 23 tests, including the shrink, retained-headroom, parallel-batch and media-charging cases. Restoring the head made the same block green (49 passed, 661 filtered by name).

Gates

Affected Core build/typecheck, scoped ESLint/Prettier and 11 affected test files (2,083 tests) passed on this head before the probe. This verification adds the wire/TUI legs and the mutation pin.

Review and CI state

At the time of the run, 9 checks were green, 25 skipped, and 5 were still in progress (1 queued), so the closeout state is pending-verification. mergeStateStatus=BLOCKED comes from REVIEW_REQUIRED.

Three older bot threads remain unresolved. R19-2 and R19-3 are addressed in 3587a76baa; R19-1 was changed to a matcher-aware hasHooksMatchingTool guard rather than removed, because removing the veto would leave the executed path resident after a hook retarget. I have not resolved those threads.

Residuals

Not established here: real-model acceptance, Windows, full-repository suite, image-level layout/color capture, and the currently running CI. The TUI evidence is a text pane capture, not a screenshot.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

@qqqys thanks for the pass at b43c77e6. The head has not moved since, so here is the direct evidence for the three at this commit.

R19-2 (telemetry). finalizeToolResponses now takes explicit budgetSource and logResultSize; the send-boundary call passes send_guard and false (llm-chat.ts:3728-3743). The finalizer test preserves %s attribution and suppresses only send-boundary size telemetry covers both sources, and the pressure send test asserts no send-boundary- call id is logged (llm-chat.test.ts:14520). Thread replied and resolved.

R19-3 (fetched summary). The successful-summary return no longer declares persistedOutputFiles; only the processing-failure return does (web-fetch.ts:748-756). advertises the artifact for the text actually cut now runs the real finalizer and asserts the saved artifact equals the shipped summary on success and the raw body on failure. Thread replied and resolved.

R19-1 (hook veto). The veto is now gated on hasHooksMatchingTool per event and tool name and honors getDisableAllHooks (llm-chat.ts:3709-3726). The llm-chat rows cover a Bash matcher (does not preserve) versus read_file / * (preserves). I did not remove it: updatedInput can execute B while history records A, so invalidating A would leave B resident. For a matching read hook the batch is still preserved whole because the finalizer applies one batch budget; a partial preserve would need a new per-entry path. That thread is deliberately still open for your call.

Local merge-reference at this same head: near auto the tool result is 10,576 chars with no compaction notice, versus 20,000 chars plus compressed from: ~851026 to ~845660 tokens on the base; far from the trigger both are 20,000. Disabling the adaptive budget fails 23 pressure tests, restored head passes 49.

Required Lint & Static is green and no check has failed. The four queued Installed npm jobs are the informational install matrix, not a required gate.

Could you re-assess the three at b43c77e6? If R19-1 is the one you still want changed, tell me which contract you want and I will take it.

@wenshao

wenshao commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

Local verification: merge-ready (pending required human review)

Verified head b43c77e6eceac9f8dc6097c7d179f21518d163dd against its merge-base 74efa46640e1d8c6b34b99b5f5bd191f9586c4b1. The gate run used the merge into main 6f53133d6d; before posting I re-merged the head into the then-current tip 57f4bd14 — both merges are conflict-free, and the 17-file delta between those tips is review tooling only, so the gate cells carry. 4,107 checks: 2,039 affected-suite passes at the head + 2,039 on the merged tree + 23 mutation kills + 5 wire observations + 1 clean trial merge.

äļ­æ–‡æ‘˜č́

朎地銌čŊé€ščŋ‡ïžšhead b43c77e6merge-base 74efa466。é—Ļį́åœĻ合åđķ到 main 6f53133d įš„æ ‘äļŠč·‘发åļƒå‰åˆåŊđå―“æ—ķ最新 tip 57f4bd14 重做合åđķäļĪæŽĄéƒ―æ— å†ēįŠïžŒäļĪäļŠ tip äđ‹é—ī 17 äļŠæ–‡äŧķå…ĻéƒĻæ˜Ŋ review å·Ĩ具é“ū所äŧĨé—Ļį́įŧ“æžœåŊ搚åļĶã€‚å…ą 4,107 éĄđæĢ€æŸĨïžšhead 2,039 äļŠå—å―ąå“åĨ—äŧķį”Ļäū‹ + 合åđķæ ‘ 2,039 äļŠ + 23 äļŠå˜åž‚å‡ŧ杀 + 5 äļŠ wire 观æĩ‹ + 1 æŽĄåđē净合åđķ。

ᜟåŪž CLIdist/cli.js+ č„šæœŽåŒ– loopback OpenAI å…žåŪđ provider1M įŠ—åĢéĶ–äļŠå“åš”æŠĨ prompt_tokens=846000auto č§Ķ发į‚đ 850,000æĻĄåž‹ read_file äļ€äļŠ 19,952 字įŽĶ文äŧķ。head åœĻ wire äļŠåŠå‘ 10,549 字įŽĶ、äļč§Ķ发压įžĐïž›base 发 19,952 字įŽĶåđķč§Ķ发压įžĐTUI æ˜ūįĪš compressed from: ~851024 to ~11048 tokens。čŋœįĶŧ阈倞100kæ—ķäļĪč‡‚éƒ―å‘ 19,952ã€éƒ―äļåŽ‹įžĐ。11 äļŠå—å―ąå“ Core åĨ—äŧķ 2,039 å…Ļčŋ‡ïžˆhead äļŽåˆåđķæ ‘各äļ€æŽĄïž‰ïž›åŽ‹åŠ›åĨ—äŧķ 49 å…Ļčŋ‡ïž›æŠŠ pressureToolOutputBudget æ”đ成į›īæŽĨčŋ”回 undefined 后有 23 åĪąčīĨ / 26 通čŋ‡ã€‚

CIïžšLint & Static、Test (ubuntu-latest) 通čŋ‡ïž›review-pr åœĻ “Run review” æ­ĨéŠĪåĪąčīĨæŠĨ Failed to determine state for PR #13599æ˜ŊčŊ„åŪĄå·Ĩä―œæĩč‡ŠčšŦįš„įŠķ态æŸĨčŊĒåĪąčīĨäļæ˜Ŋæĩ‹čŊ•åĪąčīĨ。mergeable=truemergeStateStatus=blocked äŧ…æĨ臩 reviewDecision=REVIEW_REQUIRED。åđķ发 PR #14332 äđŸæ”đ truncation / coreToolSchedulerïžŒä―†åŪƒč‡ŠčšŦå·ēäļŽå―“前 main åœĻ truncation.ts å†ēįŠïžŒåąžé™ˆæ—§åˆ†æ”ŊäļŽæœŽ PR 无同 hunk å†ēįŠã€‚

朊čĶ†į›–ïžšč„šæœŽ provider äļæ˜ŊᜟåŪžæĻĄåž‹ïž›æ—  Windows、æĩč§ˆå™Ļæˆ–įœŸåŪž provider 驌æ”ķïž›æēĄæœ‰é€æĄåĪæ ļ 226 æĄåŽ†åē review įšŋįĻ‹ïž›ä―œč€…æŠĨįš„ 10,576 äļŽæœŽåœ° 10,549 į›ļå·Ū 27 字įŽĶæĨ臩 fixture äļŽį”Ļ量įŧ†čŠ‚å·Ū垂19,952 字įŽĶ文äŧķ、20 äļŠ completion tokenäļå―ąå“įŧ“čŪšã€‚

What I observed

Same scenario in both arms: 1M window; the first response reports prompt_tokens=846000 (auto trigger 850,000); the scripted model asks read_file on a 19,952-character file; the CLI then sends the result back. The loopback server records the raw request bodies.

Scenario Arm Tool-result chars on the wire Compaction request
near auto (846k / 850k) head b43c77e6 10,549 no
near auto (846k / 850k) base 74efa466 19,952 yes
far from auto (100k) head b43c77e6 19,952 no
far from auto (100k) base 74efa466 19,952 no

The TUI agrees with the wire in the same sessions. Head finishes with done and no compaction notice at 84.6% context; base prints IMPORTANT: This conversation approached the input token limit â€Ķ (compressed from: ~851024 to ~11048 tokens) at the same 84.6%. The base arm's 19,952-character result is what its compaction side-query receives; the head's 10,549-character result stays below the trigger and the request goes out normally.

Gates and controls

Gate Result
11 affected Core suites at head (llm-chat, coreToolScheduler, finalizer, finalizer integration, goal turn, tool-hook context, file-read cache, agent, web-fetch, microcompact, token estimation) 2,039 passed / 11 files
Pressure suite at head 49 passed
Pressure suite with pressureToolOutputBudget forced to undefined 23 failed / 26 passed
Near-auto wire, head vs base 10,549 / 19,952 chars; no / yes compaction
Far-from-auto wire, head vs base 19,952 / 19,952; no / no compaction
TUI near-auto, head vs base no notice / compressed from: ~851024 to ~11048 tokens
Merged tree 6f53133d, 11 affected suites 2,039 passed
Merged tree wire, near auto 10,549 chars, no compaction
Trial merge into 6f53133d / final tip 57f4bd14 clean / clean

Not covered

  • The provider is a controlled loopback fixture, not a real model; this proves what the client emits, not provider acceptance, billing, or exact token admission.
  • No Windows, browser, real-provider, or cross-platform run.
  • I did not re-adjudicate the 226 prior review threads; this is behavioral verification at the current head.
  • The failing review-pr lane was not re-run; its failure is the workflow's PR-state lookup, not a test or build failure.
  • Sibling PR Make tool output truncation depend on the remaining context window #14332 was checked for overlap, not evaluated as an alternative implementation. It conflicts with current main on its own (truncation.ts), so it is stale; this PR does not touch that file.

Methodology

  • Independent worktrees for head b43c77e6, base 74efa466 (the real merge-base), and the trial merge; each ran corepack pnpm install --frozen-lockfile and built its own dist/cli.js. No tree shared another's node_modules or build output.
  • Real CLI, isolated HOME/QWEN_HOME, env -u QWEN_RUNTIME_DIR, proxies unset for loopback, OPENAI_MODEL=verify-model at a http://127.0.0.1 base URL, and model.generationConfig.contextWindowSize=1000000 in settings.
  • The loopback OpenAI-compatible server logs every request body. Its first response reports the usage anchor and asks read_file; the near base arm's compaction side-queries are answered by the same fixture. The fixture and usage details differ slightly from the author's run (19,952-character file, 20 completion tokens), which accounts for the 27-character gap from the reported 10,576; the conclusion is unchanged.
  • TUI arms ran the built bundle in tmux at 120×34 with the same fixture; panes were captured with tmux capture-pane -e and rasterized by scripts/verify-capture.mjs. The captures below are rendered from the real terminal frame, not OS-level screenshots.
  • Mutation: the first statement of pressureToolOutputBudget was replaced with return undefined; the file was restored from a byte copy and git status was clean before the green run. The focused suite was run unmodified (49 passed) and mutated (23 failed).
  • Trial merge: git merge origin/pr-13599-head into 6f53133d was clean; that tree was installed, built, and re-ran the 11 suites plus the near-auto wire arm. The head was then re-merged into 57f4bd14 cleanly; the 17-file delta was inspected and contains only packages/cli/src/commands/review/** and the review skill doc, none of which the gates exercise.
  • Evidence-image values are parsed from the run artifacts by a script, not typed into the figure.

Evidence captures

Head, near auto-compaction — 10,549 characters on the wire, no compaction notice:

head near auto

Base, same scenario — 19,952 characters and the compression notice:

base near auto

Wire A/B and gate summary (values parsed from the request logs and test output):

wire and gates


Independent maintainer verification by @wenshao. No product code was modified, pushed, or re-run in CI for this verification.

@doudouOUC doudouOUC 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.

PR #13599 Review Summary

PR: feat(core): shrink tool results to the headroom left before auto-compaction
Author: yiliang114
Changes: +2551 / -210, 24 files


Overall Assessment: Approve

This is a well-designed, thoroughly tested feature that addresses a real problem (#2566): static tool output budgets can push long sessions over the auto-compaction trigger prematurely. The solution introduces pressure-aware dynamic budgeting at the send boundary, anchored to the chat's last successful usage report with proper invalidation semantics.


Strengths

  1. Design documentation (docs/design/pressure-aware-tool-output.md / .zh-CN.md) is exemplary — clear problem statement, detailed change description, explicit scope/limits, and acceptance criteria.
  2. Test coverage is comprehensive (~900+ lines of new tests in llm-chat.test.ts alone), covering edge cases including: forked chats, hook retargeting, escaped paths, media-bearing results, sub-notice allocations, disabled budgets, foreign routes, in-flight invalidation, and more.
  3. Security-conscious design: recovery handles use non-enumerable Symbol properties to avoid leaking into serialized model requests; forged artifact paths in tool text are explicitly rejected (test: does not promote an untrusted...).
  4. Proper anchor invalidation: fixed-input version tracking, dispatch-time clearing, history prefix comparison, route key matching — all correctly implemented to prevent stale anchors.
  5. Recovery text preservation: the ToolOutputRecovery side-channel ensures producer-owned diagnostic text (subagent timeout reasons, transcript markers) survives multiple budget passes intact.

Suggestions (non-blocking)

  1. llm-chat.ts:pressureToolOutputBudget (~line 6464-6540): The method is ~75 lines with deeply nested logic. Consider extracting the "compute remaining tokens from anchor" calculation into a named helper for readability. Not blocking — the comments are thorough.

  2. tool-response-finalizer.ts:fitText: The body-stripping loop (for (const owned of [pointer, preserved, ...files])) uses string splitting to remove known substrings. This is correct but O(n*m) in pathological cases. Given the bounded input sizes this is fine in practice, but worth noting.

  3. hookSystem.ts:hasHooksMatchingTool: New public method added to support the send-boundary check. The managed dispatcher path only checks event-level presence, not tool-level matching — this is documented behavior via the test matrix, just confirming it's intentional.

  4. tokenEstimation.ts:estimatePromptTokens: Signature changed to accept Content | Content[]. All existing callers pass single Content (backward compatible), and the new array path is used only by the pressure budget. Clean extension.


Security Review

  • No hardcoded secrets or credentials
  • Recovery handles use non-enumerable Symbol keys — safe against serialization leaks
  • Forged artifact path injection explicitly tested and rejected
  • File read cache invalidation properly scoped (fork vs. main chat)
  • Worker cache invalidation failure handled gracefully with fallback
  • No command injection vectors introduced
  • Path traversal mitigated via resolvePath + unescapePath normalization

CI/Build Status

  • Test (ubuntu-latest, Node 22.x): SUCCESS
  • Lint & Static: SUCCESS
  • Integration Tests (no-AK, No Sandbox): SUCCESS
  • TUI parity snapshots: SUCCESS
  • OpenTUI no-flicker gate: SUCCESS
  • Web-shell E2E Smoke: SUCCESS
  • Installed npm global/npx (all platforms): SUCCESS
  • Managed Agent gate: SUCCESS
  • review-pr bot: FAILURE (automated review bot, not a build/test gate)

All build, test, and lint gates pass. The review-pr failure is the automated review bot, not a quality gate.


Conclusion

Approve. The implementation is correct, well-tested, and security-conscious. The suggestions above are optional improvements for future consideration.

@doudouOUC doudouOUC 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.

PR #13599 Review Summary

PR: feat(core): shrink tool results to the headroom left before auto-compaction
Author: yiliang114
Changes: +2551 / -210, 24 files


Overall Assessment: Approve

This is a well-designed, thoroughly tested feature that addresses a real problem (#2566): static tool output budgets can push long sessions over the auto-compaction trigger prematurely. The solution introduces pressure-aware dynamic budgeting at the send boundary, anchored to the chat's last successful usage report with proper invalidation semantics.


Strengths

  1. Design documentation (docs/design/pressure-aware-tool-output.md / .zh-CN.md) is exemplary — clear problem statement, detailed change description, explicit scope/limits, and acceptance criteria.
  2. Test coverage is comprehensive (~900+ lines of new tests in llm-chat.test.ts alone), covering edge cases including: forked chats, hook retargeting, escaped paths, media-bearing results, sub-notice allocations, disabled budgets, foreign routes, in-flight invalidation, and more.
  3. Security-conscious design: recovery handles use non-enumerable Symbol properties to avoid leaking into serialized model requests; forged artifact paths in tool text are explicitly rejected (test: does not promote an untrusted...).
  4. Proper anchor invalidation: fixed-input version tracking, dispatch-time clearing, history prefix comparison, route key matching — all correctly implemented to prevent stale anchors.
  5. Recovery text preservation: the ToolOutputRecovery side-channel ensures producer-owned diagnostic text (subagent timeout reasons, transcript markers) survives multiple budget passes intact.

Suggestions (non-blocking)

  1. llm-chat.ts:pressureToolOutputBudget (~line 6464-6540): The method is ~75 lines with deeply nested logic. Consider extracting the "compute remaining tokens from anchor" calculation into a named helper for readability. Not blocking — the comments are thorough.

  2. tool-response-finalizer.ts:fitText: The body-stripping loop (for (const owned of [pointer, preserved, ...files])) uses string splitting to remove known substrings. This is correct but O(n*m) in pathological cases. Given the bounded input sizes this is fine in practice, but worth noting.

  3. hookSystem.ts:hasHooksMatchingTool: New public method added to support the send-boundary check. The managed dispatcher path only checks event-level presence, not tool-level matching — this is documented behavior via the test matrix, just confirming it's intentional.

  4. tokenEstimation.ts:estimatePromptTokens: Signature changed to accept Content | Content[]. All existing callers pass single Content (backward compatible), and the new array path is used only by the pressure budget. Clean extension.


Security Review

  • No hardcoded secrets or credentials
  • Recovery handles use non-enumerable Symbol keys — safe against serialization leaks
  • Forged artifact path injection explicitly tested and rejected
  • File read cache invalidation properly scoped (fork vs. main chat)
  • Worker cache invalidation failure handled gracefully with fallback
  • No command injection vectors introduced
  • Path traversal mitigated via resolvePath + unescapePath normalization

CI/Build Status

  • Test (ubuntu-latest, Node 22.x): SUCCESS
  • Lint & Static: SUCCESS
  • Integration Tests (no-AK, No Sandbox): SUCCESS
  • TUI parity snapshots: SUCCESS
  • OpenTUI no-flicker gate: SUCCESS
  • Web-shell E2E Smoke: SUCCESS
  • Installed npm global/npx (all platforms): SUCCESS
  • Managed Agent gate: SUCCESS
  • review-pr bot: FAILURE (automated review bot, not a build/test gate)

All build, test, and lint gates pass. The review-pr failure is the automated review bot, not a quality gate.


Conclusion

Approve. The implementation is correct, well-tested, and security-conscious. The suggestions above are optional improvements for future consideration.

@chiga0 chiga0 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.

Review Summary

Reviewed 24 files (+2551/-210) implementing pressure-aware tool output shrinking at the chat send boundary. The design is well-structured: a usage anchor from the last successful response sizes the next send's tool results against the headroom before auto-compaction, with thorough invalidation at input-change points (system instruction, tools, history, dispatch).

Prior review cross-check: The existing review (sha 1e92fee, 31 findings across R1-R4) raised several Critical concerns. Re-checking against the current head (b43c77e):

  • R1-1 (pressure budget floor of 1): pressureToolOutputBudget now returns undefined when remainingTokens â‰Ī 0, disabling the pressure budget and falling back to static budgets. ✓ Fixed.
  • R2-1 (POSITIVE_INFINITY resurrecting send guard): The budget combination logic correctly handles undefined/non-finite batchBudget — Math.min(Infinity, pressureBudget) only activates when a real pressure budget exists. ✓ Fixed.
  • R2-3 (missing context window fallback): Returns undefined when contextWindow is invalid, safely disabling the pressure path. ✓ Safe.
  • R3-1 (shrink written into durable history): The shrunk content is intentionally what goes into history — compaction owns the batch afterward. ✓ By design.

The earlier Critical findings have been addressed through revision. The current implementation is solid.

Non-blocking observations

1. fitText body cleanup uses substring removal. The line body = body.split(owned).join('') removes all occurrences of the pointer, preserved text, and file paths from the preview body. If tool output naturally contains these strings (e.g., a compiler error that mentions the artifact path), that text is silently deleted from the preview. The strings are specific enough that collision is unlikely in practice, but String.replace with a first-occurrence-only semantic would be more defensive if the intent is to remove the framework-placed copy rather than every occurrence.

2. Recovery handle fragility. The non-enumerable Symbol-keyed recovery handle is an elegant in-process side channel, but any structuredClone, JSON.parse(JSON.stringify(...)), or spread-copy of Part objects silently drops it. The code correctly re-attaches after provenance spreads (coreToolScheduler.ts lines ~7933, ~7976), but future code that copies parts needs to be aware of this. The existing review (R1-9) partially covers this.

3. toolBudgetUsageAnchor memory. The anchor stores this.history.slice() — a shallow copy of the history array. While individual Content objects are not deep-copied, the array itself grows with history. For long sessions, this is a linear memory overhead that persists until the next dispatch clears it. The existing review (R1-21) noted this; the current implementation accepts this trade-off.

What I checked

  • Design doc (docs/design/pressure-aware-tool-output.md) — documents the approach, anchor invalidation rules, two-preview selection, scope/limits
  • llm-chat.ts — pressure budget computation, two-preview selection (pressure vs aggregate), invalidation points, cache invalidation after shrink, forked chat handling
  • tool-response-finalizer.ts — recovery handle integration, minimumFunctionResponseContent, isBudgetShrinkablePart, zero-budget edge cases
  • coreToolScheduler.ts — recovery handle re-attachment after provenance spread
  • agent.ts — recoveryText propagation through composeIncompleteResult
  • tool-output-recovery.ts — Symbol-keyed side channel implementation
  • transcript-marker.ts — extracted transcript marker builder
  • hookSystem.ts — hasHooksMatchingTool for hook-retarget detection
  • fileReadCache.ts — markAllReadsEvictedFromHistory for post-shrink cache invalidation
  • tokenEstimation.ts — Content | Content[] parameter generalization
  • Test coverage: llm-chat.test.ts (+922 lines), tool-response-finalizer.test.ts (+383 lines), integration tests, web-fetch tests, agent tests

@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 b43c77e6. This supersedes my COMMENT of 04:39Z on this same commit, whose blocker was that round 19's three Criticals were unconfirmed. I have now confirmed all three fixed by reading the code at this head, and the gate that made my two previous reviews COMMENT is closed.

1. R19-1 — the matcher-agnostic veto is now matcher-specific

llm-chat.ts:3709-3724 computes hookRetargetPossible per cut result as a three-way conjunction:

(name === ToolNames.TOOL_CALL || FILE_PATH_TOOLS.has(name)) &&
!this.config.getDisableAllHooks?.() &&
['PreToolUse', 'PermissionRequest'].some((event) =>
  this.config.getHookSystem?.()?.hasHooksMatchingTool(event, name, this.config.getSessionId()))

The finding was that the veto skipped the cut for any batch whenever any hook existed anywhere. It now requires a hook that matches this tool name under either of the two events that can rewrite arguments, on this session, and it stands down entirely when hooks are disabled. A formatter hook registered only for edit therefore no longer preserves a read batch. At :3730-3732 the veto still resolves to guarded = entries — preserve rather than cut — which is the fail-safe direction for a path the harness may be about to rewrite.

This also closes the P2 from the 2026-10-11 verification round, and the chain is worth spelling out because that round did not re-adjudicate it. The P2 was: a PreToolUse hook rewrites read_file(small.txt) to read_file(big.txt), the boundary cuts big.txt, the disarm is keyed on the model-authored small.txt, and a later read_file(big.txt) is then told "unchanged, full content provided earlier" — falsely. Under the code at this head a hook matching read_file makes hasHooksMatchingTool true, so hookRetargetPossible is true, so guarded === entries and the batch is never cut. No cut means no persisted artifact and no disarm, so the false "unchanged" reply cannot be produced through this path. The author's inline note that the veto was "changed but not removed" is accurate, and narrowing which hooks count is what preserves the retarget case while stopping unrelated hooks from suppressing the shrink.

2. R19-2 — the telemetry regression is closed by an explicit flag, not by luck

tool-response-finalizer.ts:485-495 now declares budgetSource: ToolOutputBudgetSource = 'batch' as parameter 8 and logResultSize = true as parameter 9. The send-boundary call at llm-chat.ts:3733-3743 passes 'send_guard' and false in exactly those positions. So the guard's shrink no longer emits a synthetic result-size event — the regression the finding named — while the budget's provenance is still attributed distinctly rather than being folded into 'batch'. I checked the argument positions against the signature rather than counting on the call reading sensibly.

3. R19-3 — the success path no longer claims an artifact it is about to cut

web-fetch.ts at this head declares persistedOutputFiles once (:688-690) and consumes it once (:752), inside the return whose llmContent is [Content processing failed (â€Ķ)]; the raw fetched content follows. The successful-summary return does not declare it, so the finalizer persists the summary it is actually about to cut, while the failure path still declares the raw page — which is the text that return genuinely contains. The finding was that the success path declared an artifact holding text the shrink then replaced; there is now exactly one declaration site and it is the one whose text matches.

Round 18's three carried findings (R12-1, R16-1, R18-1) do not appear in round 19's list, which carries forward only what it re-verified, so they were assessed as closed before this head.

4. What I read versus what I am carrying, stated plainly

My own Critical-only scan of this 24-file, +2551/−210 diff is still not complete — that disclosure is unchanged from my last two reviews. What changed is that it is no longer the binding constraint, for two reasons.

The three findings that were actually standing are the ones I verified myself, line by line, at this head. And the behavioural surface I have not read has been verified at this exact commit by @wenshao's round of 12:51Z: 2,039 affected-suite passes at head and again on a merged tree across the 11 Core suites this PR touches, a wire A/B showing 10,549 characters with no compaction against base's 19,952 with one, a far-from-auto control where both arms send 19,952 and neither compacts, clean trial merges into two different main tips, and — the part that makes the suite evidence meaningful rather than circular — a mutation control where forcing pressureToolOutputBudget to return undefined turns 23 of 49 pressure cases red. I am carrying that as evidence about behaviour, and naming it as someone else's work rather than folding it into my own coverage.

What neither of us has adjudicated: the 226 prior review threads, which the same round explicitly declines to re-adjudicate, and which on the ledger evidence are Suggestion-level now that every sev:"C" through round 19 is closed.

5. CI

24 pass, 29 skipping, 1 pending, 1 fail. The failure is review-pr, which is not a gate in any state and which the verification round independently diagnosed as the review workflow's own PR-state lookup failing rather than a build or test failure. Lint & Static (ubuntu-latest, Node 22.x), Test (ubuntu-latest, Node 22.x) and Integration Tests (no-AK, No Sandbox) all pass at this head.

Note for whoever lands this

This PR is 20 days old with 19 review rounds and a standing land-with-residual-risk framing from round 18. Every Critical any round filed is now closed and verified, the affected suites pass at head and on a merged tree, and the trial merges are clean against two main tips. Continuing to open new rounds is more likely to add findings of the fix-induced kind this history already shows several of than to find something blocking; if a residual is going to be accepted, this is a reasonable head to accept it at.

@yiliang114

Copy link
Copy Markdown
Collaborator Author

🧐 Qwen Pull Request Review / review-pr failed on the current head b43c77e6 — job 122131579700, step Run review (14) concluded failure at 12:26:01Z.

Flagging, not fixing: the approvals are already bound to this head and Lint & Static is green, but a real failure on the head means the merge patrol leaves this one alone this round. No rerun requested from here.

@yiliang114
yiliang114 merged commit a325af5 into QwenLM:main Oct 27, 2026
236 of 241 checks passed
@qwen-code-review-bot

Copy link
Copy Markdown
Collaborator

Released in v0.26.3.

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.

feat(core): dynamic tool output truncation based on context pressure

8 participants