Skip to content

feat(daemon): guard cross-worktree Git mutations - #8687

Merged
wenshao merged 51 commits into
mainfrom
feat/daemon-git-worktree-guard
Aug 14, 2026
Merged

feat(daemon): guard cross-worktree Git mutations#8687
wenshao merged 51 commits into
mainfrom
feat/daemon-git-worktree-guard

Conversation

@wenshao

@wenshao wenshao commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds a built-in host-side guard for model-issued run_shell_command calls in qwen serve. The guard recognizes Git repository relocation through -C, --work-tree, and --git-dir, then blocks mutating or unclassified commands when the resolved target escapes the session's current effective working directory. It uses daemon-owned session state, composes before an optional external guard, handles compound shell forms, dynamic targets, symlink ancestors, chained -C, and Git global options with values.

Worktree sessions use their live effectiveCwd as the boundary, so mutations inside the session's own worktree remain allowed while mutations targeting another checkout or worktree are denied. Read-only relocated Git commands remain allowed.

The managed guard is installed only for daemon ACP children. CLI/TUI shell validation, permission classification, confirmation, YOLO behavior, and shared core tool execution are unchanged.

Why it's needed

A daemon model can start a shell inside its assigned workspace but relocate Git to another checkout with git -C, --work-tree, or --git-dir. That bypasses the existing shell working-directory boundary and can mutate a sibling worktree or unrelated checkout. Enforcing the boundary in the daemon host prevents the ACP child from forging workspace context and avoids changing normal CLI behavior.

Reviewer Test Plan

How to verify

  1. Start a managed daemon session bound to one checkout or worktree.
  2. Confirm a model-issued mutating command such as git -C <other-worktree> reset --hard is denied before execution.
  3. Repeat with --work-tree, --git-dir, a dynamic relocation target, a symlinked path, a compound command, and a value-taking Git global option such as --namespace; each mutating or unclassified external relocation should be denied.
  4. Confirm git -C <other-worktree> rev-parse HEAD remains allowed, and that status/grep do not (they refresh the target index and run its core.fsmonitor/textconv drivers), nor does cat-file --textconv.
  5. Confirm a mutating Git command targeting a directory inside the session's effectiveCwd remains subject only to the existing permission flow.
  6. Run the normal CLI/TUI and confirm its shell behavior is unchanged because no shared core shell or permission path is modified.

Evidence (Before & After)

N/A — daemon policy change with unit and integration coverage; no UI changes.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

macOS, Node.js 22-compatible workspace. Passed: 25 daemon guard tests, 5 managed ACP guard tests, 97 ACP bridge tests, ACP bridge typecheck, targeted ESLint, Prettier, pre-commit hooks, normal CLI Git regression tests (36/36 in independent verification), npm run dev -- --version, and daemon startup. The targeted run-qwen-serve.test.ts could not be collected in this checkout because the workspace is missing @qwen-code/channel-github; the root build/typecheck is blocked by the same missing channel packages and existing unrelated type errors.

Risk & Scope

  • Main risk or tradeoff: This is intentionally a narrow shell-token policy rather than a complete shell interpreter. Dynamic Git relocation is fail-closed for mutating or unknown subcommands, while commands without recognized relocation retain existing behavior.
  • Not validated / out of scope: Windows/Linux runtime verification, direct user-entered daemon shell commands, linked-worktree exceptions, confirmation UX, and general environment-variable evaluation.
  • Breaking changes / migration notes: None for CLI/TUI users. Daemon model calls that attempt cross-worktree Git mutation are now denied.

Linked Issues

N/A

中文说明

本 PR 做了什么

qwen serve 中模型发出的 run_shell_command 调用增加 Daemon host 侧内置 guard。该 guard 识别通过 -C--work-tree--git-dir 进行的 Git 仓库重定位;当解析后的目标逃出 session 当前有效工作目录时,阻止可变更或无法分类的命令。策略使用 Daemon 持有的可信 session 状态,在可选 external guard 之前执行,并覆盖 compound shell、动态目标、symlink ancestor、连续 -C 以及带值的 Git 全局选项。

Worktree session 使用实时 effectiveCwd 作为边界,因此 session 自己 worktree 内的 mutation 仍然允许,而指向其他 checkout 或 worktree 的 mutation 会被拒绝。重定位后的只读 Git 命令仍然允许。

Managed guard 只安装到 Daemon ACP child。CLI/TUI 的 shell 校验、权限分类、确认、YOLO 行为以及共享 core tool 执行均保持不变。

为什么需要

Daemon 模型可以让 shell 从分配的 workspace 内启动,但通过 git -C--work-tree--git-dir 将 Git 重定位到另一个 checkout。这会绕过现有 shell 工作目录边界,并可能修改 sibling worktree 或无关 checkout。把边界放在 Daemon host 强制执行,可以防止 ACP child 伪造 workspace context,同时避免改变普通 CLI 行为。

Reviewer Test Plan

如何验证

  1. 启动一个绑定到某个 checkout 或 worktree 的 managed daemon session。
  2. 确认模型发出的 git -C <other-worktree> reset --hard 等 mutation 命令在执行前被拒绝。
  3. 分别使用 --work-tree--git-dir、动态 relocation target、symlink path、compound command,以及 --namespace 这类带值的 Git 全局选项;所有指向外部的 mutation 或无法分类命令都应被拒绝。
  4. 确认 git -C <other-worktree> rev-parse HEAD 仍然允许;而 status/grep 不再允许(它们会刷新目标 index 并运行其 core.fsmonitor/textconv 驱动),cat-file --textconv 同样不允许。
  5. 确认指向 session effectiveCwd 内目录的 Git mutation 仍只受现有 permission flow 约束。
  6. 运行普通 CLI/TUI,确认其 shell 行为不变,因为没有修改共享 core shell 或 permission 路径。

Evidence(Before & After)

N/A——这是带单元和集成覆盖的 Daemon 策略改动,没有 UI 变化。

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment(可选)

macOS,Node.js 22 兼容 workspace。已通过:25 个 Daemon guard 测试、5 个 managed ACP guard 测试、97 个 ACP bridge 测试、ACP bridge typecheck、定向 ESLint、Prettier、pre-commit hooks、普通 CLI Git 回归测试(独立验证 36/36)、npm run dev -- --version 和 Daemon 启动。当前 checkout 缺少 @qwen-code/channel-github,因此定向 run-qwen-serve.test.ts 无法完成测试收集;root build/typecheck 也被相同的 channel package 缺失和已有无关类型错误阻塞。

风险与范围

  • 主要风险或取舍:这是刻意保持最小范围的 shell token 策略,而不是完整 shell interpreter。动态 Git relocation 对 mutation 或 unknown subcommand fail-closed;没有已识别 relocation 的命令保持原行为。
  • 未验证 / 不在范围内:Windows/Linux runtime 验证、用户直接输入的 Daemon shell 命令、linked-worktree exception、confirmation UX 和通用环境变量求值。
  • Breaking changes / migration notes:CLI/TUI 用户无变化。尝试跨 worktree 执行 Git mutation 的 Daemon 模型调用现在会被拒绝。

Linked Issues

N/A

wenshao and others added 2 commits August 7, 2026 19:40
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Re-run on head a0427ae032a — five commits since my last full pass at 5fc219db, all inside the guard and its tests: fixes for the two Criticals from that round, three more reported forms after them, and the trailer/man/sendemail command-executing config keys. Nothing outside daemon-git-worktree-guard.ts (+65/−11) and its test file (+31) changed. Gate picture below; the review verdict lives in the Stage 2/3 comments.

Template looks good ✓ — complete and bilingual, Evidence honestly marked N/A for a daemon policy change.

Problem: demonstrated, not argued — unchanged from earlier rounds. Every escape this guard's own rules introduced has been reproduced against a real shell and real git, including the two I traced in my last pass; this is not hypothetical hardening.

Direction: aligned — unchanged. Host-side containment for daemon-model shells, with CLI/TUI validation, the permission flow, and shared core execution untouched. Re-verified at this head: the wiring (acp-bridge, acp-integration, core, docs) is byte-identical to the head I reviewed in full.

Size: ~3,200 production lines (guard 2,910 + ~290 wiring across acp-bridge/cli/core) vs ~2,900 test vs ~410 docs. Author is a maintainer and the type is feat, so per the maintainer exemption no gate applies; the 1,000+ line advisory stands on the record, and at this point in the PR's life splitting is no longer a realistic ask.

Approach: the delta is what it should be at this stage — each commit answers one reported escape, no scope creep, and the removal/shadow logic was collapsed toward a single fail-closed rule rather than grown.

Risk: acp-integration still matches the high-revert-correlation path list → full review depth, nothing skipped. For context: human review threads on this PR are at zero (verified via the API — no unresolved, non-outdated threads), and two approvals landed this morning.

Moving on to the delta review. 🔍

中文说明

在 head a0427ae032a 上重跑 —— 距上次在 5fc219db 的完整审查有五个提交,全部落在 guard 及其测试内:修复上一轮两个 Critical、其后三个新报告的形式,以及 trailer/man/sendemail 命令执行类 config key。除 daemon-git-worktree-guard.ts(+65/−11)与其测试文件(+31)外没有任何改动。Gate 结论如下;审查结论见 Stage 2/3 评论。

模板完整 ✓ —— 各项齐全、中英双语,Evidence 如实标注 N/A(daemon 策略改动)。

问题:被证明而非推测 —— 与此前各轮一致。这个 guard 自身规则引入的每一次逃逸都在真实 shell + 真实 git 上复现过,包括我上一轮推演出的两个;不属于理论性加固。

方向:对齐 —— 不变。host 侧约束 daemon 模型的 shell,CLI/TUI 校验、权限流程与共享 core 执行均未改动。已在当前 head 复核:接线部分(acp-bridge、acp-integration、core、docs)与我完整审查过的 head 逐字节一致。

规模:约 3,200 行生产代码(guard 2,910 行 + acp-bridge/cli/core 约 290 行接线),测试约 2,900 行,文档约 410 行。作者是 maintainer 且类型为 feat,按 maintainer 豁免不触发任何门槛;1,000+ 行建议线记录在案,且以本 PR 目前的阶段,拆分已不现实。

方案:delta 在这个阶段该有的样子 —— 每个提交回应一个已报告的逃逸,无范围蔓延,removal/shadow 逻辑向单一 fail-closed 规则收敛而不是继续膨胀。

风险:acp-integration 仍命中高回滚相关路径列表 → 全深度审查,一项不跳。背景:本 PR 的人工评审线程已归零(经 API 核实 —— 无未解决且未过期的线程),今晨已有两个批准。

进入 delta 审查 🔍

Qwen Code · qwen3.8-max

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

@wenshao

wenshao commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 7, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. Remove the autofix/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review — delta 5fc219dba0427ae0

Five commits landed since my last pass, touching only the guard (+65/−11) and its tests (+31). I read the full delta and re-verified each hunk against the surrounding machinery in the complete file at this head. One Critical below — a new bypass in the freshly reworked removal logic. Everything else in the delta holds up, and the two Criticals from my last round are genuinely fixed.

Last round's two Criticals: fixed and pinned

  • Critical 1 (shadowed removal builtin) — closed by shadowedBuiltin = !hasCommandPrefix && definedBodies.has(removalProgram): a function shadowing unset/unalias/export now falls through to shadow dispatch instead of being treated as a builtin removal, so unset() { git -C <outside> reset --hard; }; unset other replays the relocating body and is denied. The deny suite pins my exact payload, plus the command()/builtin() shadowed-prefix variants, the enable -n unset edge, and — nice addition — A=nested; unset A; cd $A; git reset --hard, which pins the new shellLocals drop so a stale in-bounds value can't survive its own unset.
  • Critical 2 (leading redirection defeats the prefix strip) — closed by skipRedirectOperands() before and inside the command/builtin loop, matching how readProgramWord normalizes. Pinned by git() { :; }; 2>/dev/null command unset -f git; git -C <outside> reset --hard.
  • The three follow-up commits check out, all deny-direction, all with tests: PATH=/GIT_EXEC_PATH= added to the wrapper relocation markers and the program-env set (consistent with the existing direct-path denial), delimiter-glued GIT_DIR=/GIT_WORK_TREE= now register inside quoted payloads (su -c 'true;GIT_DIR=…'), the redirect-decoy fix in the body-replay programIndex scan (> g GIT_DIR=… g), and the trailer.<token>.command / man.<tool>.cmd / sendemail.{sendmailcmd,tocmd,cccmd} config keys joining the command-executing pattern list.

Critical — a chained command/builtin prefix after a real prefix still defeats the removal scan

The new break on a shadowed prefix word (line ~2273) is unconditional, but it only matches bash semantics for the first prefix word. Once a real command/builtin prefix has been consumed, bash disables function lookup for the rest of the line, so a later shadowed prefix word runs as the real builtin:

git() { :; }; builtin() { :; }; command builtin unset -f git; git -C <outside> reset --hard

Trace at this head: prefix-strip iteration 1 consumes command (not shadowed → hasCommandPrefix = true); iteration 2 sees builtin in definedBodiesbreak. removalTokens therefore starts at builtin, and readProgramWord returns builtin — it skips redirects, assignments, and LEADING_SHELL_KEYWORDS, which does not include command/builtin — so isRemoval is false. shadowedBuiltin is also false (hasCommandPrefix is true). The run falls to shadow dispatch, where invoked is command — not in definedBodies — then to classification: unrecognized program, no relocation marker, allowed, and the removal is never recorded. The final git -C <outside> reset --hard is still masked by the stale harmless git() { :; } shadow, replays :, and is allowed. In bash, command bypasses the builtin function, the real builtin builtin bypasses the unset function, the real unset -f git drops the shadow, and the real git mutates the sibling checkout. The symmetric form works identically: command() { :; }; builtin command unset -f git; … (stripped builtin, break on shadowed command).

Fix direction: the break needs the same condition the final-program check already has — if (!hasCommandPrefix && definedBodies.has(…)) break;. After a real prefix is consumed, no later word can run a shadow body, so stripping must continue. Worth regressing both payloads above, with the existing position-1 shadowed-prefix deny cases kept as the allow-side control that the fix must not break.

This is static trace, not execution — I don't run PR code — but it is the same shape of evidence as my last round's two Criticals, which reproduced exactly as traced.

Carried over from the full pass at 5fc219db (files unchanged at this head)

Wiring soundness (daemon-owned effectiveCwd, validated invocationCwd, prompt-less fail-closed, built-in denial final), the minimal read-only allowlist with --textconv/--filters/--output demotion, and the additive-only core changes (sessionId/cwd context) were verified at 5fc219db and are byte-identical here. @qqqys's bare-unset closure, the R6 items, and the symlink/relocation-resolution checks from earlier rounds remain in place.

sequenceDiagram
    participant P1 as Model
    participant P2 as ACP child managed guard
    participant P3 as Daemon built-in guard
    participant P4 as External provider
    participant P5 as Shell
    P1->>P2: run_shell_command
    P2->>P3: sessionId, effectiveCwd, invocationCwd
    P3->>P3: tokenize, unwrap, classify, contain
    alt denied
        P3-->>P2: final denial, never forwarded
    else allowed
        P3->>P4: same request (only when attached, prompt-bound)
        P4-->>P2: decision
        P2->>P5: execute
    end
Loading
Files changed (23 of 23 shown)
File What changed
packages/cli/src/serve/daemon-git-worktree-guard.ts the guard itself: tokenizer, wrapper unwrapping, git relocation extraction, containment, shadow/relink/removal state model
packages/cli/src/serve/daemon-git-worktree-guard.test.ts guard tests: deny payloads, read-only allowance, shadow and removal modelling, in-boundary controls
packages/cli/src/acp-integration/acpAgent.test.ts provider-attached vs built-in-only guard behavior, prompt-less fallback identity
packages/cli/src/acp-integration/acpAgent.ts managed guard resolves non-shell tools locally without a provider; hidden-agent restrictions key on provider-attached; session-id fallback
packages/cli/src/acp-integration/session/Session.ts passes scheduler-owned sessionId and cwd into the guard context
packages/cli/src/acp-integration/session/Session.test.ts asserts the new context fields
packages/cli/src/serve/run-qwen-serve.ts installs createDaemonToolGuard on every runtime bridge, always sets the plumbing marker, provider marker only when attached
packages/cli/src/serve/run-qwen-serve.test.ts end-to-end: guard denies an outside relocation through runQwenServe; provider marker handshake test
packages/cli/src/gemini.tsx consumes and re-exposes the provider-attached marker for the ACP child
packages/acp-bridge/src/bridgeClient.ts promptId optional for context-less checks, forwards effectiveCwd and untrusted invocationCwd, re-validates after the call
packages/acp-bridge/src/bridgeClient.test.ts prompt-less validation and cwd forwarding cases
packages/acp-bridge/src/bridgeOptions.ts request type gains optional promptId, effectiveCwd, invocationCwd with trust documentation
packages/acp-bridge/src/externalToolGuard.ts provider-attached marker constants and SHELL_EXECUTING_TOOL_NAMES set
packages/core/src/utils/paths.ts realpathNearestExistingAsync for the shared event loop
packages/core/src/utils/paths.test.ts parity with the sync variant, dangling-symlink case
packages/core/src/core/tool-invocation-guard.ts guard context gains sessionId and cwd
packages/core/src/core/coreToolScheduler.ts supplies sessionId and cwd to the guard
packages/core/src/core/coreToolScheduler.test.ts asserts the new fields
packages/core/src/followup/speculation.ts same additive context for speculative tool calls
packages/core/src/followup/speculation.test.ts asserts the new fields
docs/design/daemon-git-worktree-guard.md design doc: policy, failure semantics, bounded promise, non-goals
docs/users/qwen-serve.md user-facing description of the guard
docs/developers/qwen-serve-protocol.md protocol doc updates for the guard request fields

Testing — the PR's own CI on a0427ae0, via the API

All pull_request-event workflow runs on this head completed successfully (Security Checks, SDK Java, Serve A/B, Qwen Code CI). Quoted from the check-runs API for this commit:

Check Conclusion
Test (ubuntu-latest, Node 22.x) success
Post Coverage Comment (ubuntu-latest, 22.x) success
Serve A/B (ubuntu-latest, Node 22.x) success
Real daemon E2E / Java 11 success
Desktop Shell (ubuntu-22.04) success
Desktop Shell (windows-2022) success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) success
Classify PR success
Dependency CVE audit success
Secret scan (TruffleHog) success
Integration Tests (CLI, No Sandbox) skipped
Test (macos-latest, Node 22.x) skipped
Test (windows-latest, Node 22.x) skipped

(The review-pr check still showing in flight on this head is bot orchestration, not the PR's CI.) The two standing coverage gaps remain: the CLI integration suite and the macOS/Windows unit runs are skipped in CI, so the guard is exercised on Linux CI only — the no-sandbox daemon path this PR targets never runs in CI at all, worth a follow-up independent of this PR.

Sandboxed verification would settle what static tracing cannot: @qwen-code /verify — the chained-prefix bypass above is a code trace, and an A/B runtime probe at this head would confirm it executes end-to-end and test whether the suite actually pins the removal machinery (this one is invisible to the current suite, exactly like its two predecessors).

中文说明

代码审查 —— 5fc219dba0427ae0 增量

上次审查后新增五个提交,只改 guard(+65/−11)与其测试(+31)。我通读了完整增量,并在当前 head 的完整文件中逐一对照了周边机制。下面一个 Critical —— 刚重构的 removal 逻辑里有一个新绕过。增量其余部分成立,上一轮两个 Critical 已被真正修复。

上一轮两个 Critical:已修复并被钉住。 Critical 1(被影子化的删除 builtin)由 shadowedBuiltin = !hasCommandPrefix && definedBodies.has(removalProgram) 关闭,unset() { git -C <outside> reset --hard; }; unset other 现在走影子分发并被拒绝;deny 套件钉住了我的原始 payload、command()/builtin() 影子前缀变体、enable -n unset 边界,以及 A=nested; unset A; cd $A; …(钉住新的 shellLocals 删除)。Critical 2(前导重定向使前缀剥离失效)由 skipRedirectOperands() 关闭并被对应 payload 钉住。三个后续提交均为 deny 方向且带测试:PATH=/GIT_EXEC_PATH= 进入 wrapper 标记与程序环境集、分隔符粘连的 GIT_DIR=、body 重放中 redirect 诱饵的 programIndex 修正、trailer/man/sendemail 命令执行 config key。

Critical —— 真实前缀之后的链式 command/builtin 前缀仍能绕过 removal 扫描。 新增的"影子前缀词即 break"(约 2273 行)是无条件的,但只对第一个前缀词符合 bash 语义:git() { :; }; builtin() { :; }; command builtin unset -f git; git -C <outside> reset --hard 中,第 1 轮剥离真实 command(hasCommandPrefix=true),第 2 轮见影子 builtin 即 break;readProgramWord 不跳过 command/builtin(不在 LEADING_SHELL_KEYWORDS 中),返回 builtin,isRemoval 与 shadowedBuiltin 均为 false;影子分发看到的程序词是未影子的 command,最终按"未识别程序、无重定位标记"放行,删除从未被记录;最后的 git -C <outside> reset --hard 仍被过期无害影子掩盖,重放 : 后放行。而 bash 中 command 绕过函数查找,真实 builtin 链删除影子,真实 git 变更 sibling checkout。对称形式 command() { :; }; builtin command unset -f git; … 同理。修复方向:break 加上与终程序检查相同的条件 if (!hasCommandPrefix && …)——消费过真实前缀之后,后续任何词都不可能执行影子体。建议回归上述两个 payload,并保留现有"位置 1 影子前缀"deny 用例作为允许侧对照。以上为纯静态推演(不执行 PR 代码),与上一轮两个 Critical 的证据形态相同,且它们都按推演精确复现。

5fc219db 完整审查继承的结论(这些文件在当前 head 逐字节未变): 接线可信性、最小只读白名单、core 侧纯附加改动均已在上个 head 验证。

测试 —— 该 PR 自己在 a0427ae0 上的 CI(经 API 读取)

该 head 上所有 pull_request 事件的工作流运行均已成功完成(表见英文部分)。review-pr 仍显示进行中属于 bot 编排,不是 PR 的 CI。两个长期覆盖缺口依旧:CLI 集成套件与 macOS/Windows 单测在 CI 中被跳过,guard 只在 Linux CI 上运行;本 PR 针对的无沙箱 daemon 路径在 CI 中根本不执行,值得独立跟进。

沙箱验证能定论静态推演定论不了的事:@qwen-code /verify —— 上述链式前缀绕过是代码推演,在此 head 上的 A/B 运行时探测能确认它端到端可执行,并检验测试套件是否真的钉住 removal 机制(这一条对当前套件不可见,与前两条完全相同)。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 2/5 — last round's two Criticals are genuinely fixed and pinned, and the delta is disciplined; but I traced one live bypass of the new removal logic at this head, in the exact class of escape this guard exists to close.

Stepping back: my independent proposal for this problem was always the shape this PR ended up at — host-side enforcement, deny relocated mutations, pin a minimal read-only set, fail closed on the unknown — so I'm not reviewing against a simpler alternative I think was missed. The two fixes I asked for last round landed the way the good fixes on this PR always land: exact payload pinned, allow-side control, the adjacent variants picked up too (the unset A; cd $A stale-local drop is a better fix than the one I sketched). The convergence note posted before this run is honest, and I agree with its structural diagnosis: a static reader of shell text has an adversarial tail that no patch sequence eliminates, and the design doc says so in exactly those words.

Where I part ways with "merge on that basis" is the location of this round's hole. It is not in the adversarial tail the doc disclaims — no variable indirection, no generated payload, no exotic quoting; just a function definition, a command prefix, and unset -f, all literal and all machinery this PR models. It sits in the interaction the last two commits rewrote, it reproduces under a plain code trace, and it closes with a one-condition change plus two regression tests. That is the same shape as the two Criticals fixed within the hour last round, and the standard this PR has been held to — and has held itself to — is "no known open hole in the machinery we added".

The pattern deserves naming, because it is the real argument in the convergence note: this is the third consecutive round where the layer added to close the last hole is where the next one appears. That is normal for a parser of this size, but it is also evidence. If this fix lands and pins the way its predecessors did, the PR is in strong shape to merge. If the next round produces another variant of the same interaction, that is the signal to stop patching and move enforcement off the text — and the follow-up issue for that design should exist before merge either way, so it doesn't evaporate.

Verdict: requesting changes on the one traced finding (full trace and fix direction in the Stage 2 comment). The two human approvals stand on the record and the author's bounded-promise case is coherent; this is the bot's vote, applied at the bar it has applied every round.

中文说明

置信度:2/5 —— 上一轮两个 Critical 已被真正修复并钉住,delta 也很克制;但在当前 head 上我推演出新 removal 逻辑的一个活绕过,正是这个 guard 存在所要关闭的那一类逃逸。

退一步看:我对这个问题的独立提案最终正是这个 PR 落成的形态 —— host 侧强制、拒绝重定位变更、钉死最小只读集合、未知即 fail closed —— 所以我并不是拿一个更简单的被忽略方案来对照审查。我上轮要求的两个修复以这个 PR 一贯的高质量方式落地:精确 payload 钉住、允许侧对照、相邻变体一并处理(unset A; cd $A 的过期局部变量删除比我草拟的修复更好)。本轮运行前发布的收敛说明是诚实的,我认同其结构性诊断:静态阅读 shell 文本存在任何补丁序列都无法消除的对抗性长尾,设计文档原话正是如此。

我与"据此合入"的分歧在于本轮这个洞的位置。它不在文档声明放弃的对抗性长尾里 —— 没有变量间接、没有生成式 payload、没有奇异引号;只有一个函数定义、一个 command 前缀和 unset -f,全是字面形式,全是这个 PR 自己建模的机制。它位于最近两个提交重写的交互里,纯代码推演即可复现,且只需一个条件修改加两个回归测试即可关闭。这与上一轮一小时内修复的两个 Critical 形态完全相同,而本 PR 一直被坚持(也一直在坚持)的标准就是"我们新增的机制中没有已知开口"。

这个模式值得点名,因为它正是收敛说明中真正有力的论据:连续第三轮,修补上一个洞的那一层出现了下一个洞。对这种规模的解析器这是常态,但同时也是证据。如果这次修复像前几次一样落地并钉住,这个 PR 处于可以合入的强势状态;如果下一轮又出现同一交互的新变体,那就是停止打补丁、把强制点从文本挪走的信号 —— 无论哪种结果,后续设计的 issue 都应在合入前存在,以免不了了之。

结论:就一个已推演的发现请求修改(完整推演与修复方向见 Stage 2 评论)。两位人工评审的批准记录在案,作者的有界承诺论述也自洽;这是 bot 的一票,按每一轮相同的标准给出。

Qwen Code · qwen3.8-max

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes on the tokenizer gaps: the #-comment and glob fail-open paths each defeat the guard for a whole command, and the GIT_DIR / env-wrapper relocation forms are uncovered. Details and suggested fixes are in my review comment above. The architecture is right and this is close — happy to re-run triage on the fix. 🙏

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

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

No response changes against the PR base across 4 scenario(s).

Qwen Code · serve A/B

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 84.07% 84.07% 89.96% 83.3%
Core 87.88% 87.88% 89.45% 86.37%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   84.07 |     83.3 |   89.96 |   84.07 |                   
 src               |   84.98 |    81.38 |   88.49 |   84.98 |                   
  cli.ts           |   95.68 |    84.11 |     100 |   95.68 | ...60-561,565-566 
  gemini.tsx       |    73.4 |    78.04 |   80.76 |    73.4 | ...1338-1342,1469 
  ...ractiveCli.ts |   86.74 |    81.15 |   88.13 |   86.74 | ...2955,2961,3026 
  ...liCommands.ts |   89.33 |     85.6 |      90 |   89.33 | ...01,518,552,674 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   71.54 |    73.87 |   91.19 |   71.54 |                   
  acpAgent.ts      |   70.95 |    73.69 |   90.74 |   70.95 | ...96,12301-12303 
  ...k-reporter.ts |     100 |    80.95 |     100 |     100 | 77,80,115,135     
  authMethods.ts   |      92 |       60 |     100 |      92 | 33-34             
  errorCodes.ts    |     100 |      100 |     100 |     100 |                   
  ...ion-skills.ts |     100 |    88.23 |     100 |     100 | 17,32             
  generation.ts    |    97.1 |    81.25 |     100 |    97.1 | 109,112           
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
 ...ration/service |    97.1 |    95.83 |   93.33 |    97.1 |                   
  filesystem.ts    |    97.1 |    95.83 |   93.33 |    97.1 | ...22-123,246-247 
 ...ration/session |   91.49 |    86.99 |   96.71 |   91.49 |                   
  Session.ts       |   90.51 |    85.18 |   95.94 |   90.51 | ...97,10724-10728 
  ...entTracker.ts |    96.8 |    89.36 |      90 |    96.8 | 137-143,221       
  ...projection.ts |   98.57 |    93.29 |     100 |   98.57 | ...76,333,344,356 
  ...stop-guard.ts |     100 |    98.07 |     100 |     100 | 37,127            
  ...eplay-page.ts |   93.44 |    91.74 |     100 |   93.44 | 74,85-88,115-125  
  ...y-replayer.ts |   98.54 |    95.65 |     100 |   98.54 | 241-243           
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   89.76 |    87.32 |     100 |   89.76 | ...54-270,326-328 
  ...lure-guard.ts |   98.32 |    97.75 |     100 |   98.32 | 294-295,340-341   
  tasksSnapshot.ts |    94.3 |     87.5 |     100 |    94.3 | 65-71             
  ...on-tracker.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ssion/emitters |   96.01 |    94.15 |   96.66 |   96.01 |                   
  ...ageEmitter.ts |   95.95 |       96 |     100 |   95.95 | 52-59             
  PlanEmitter.ts   |     100 |       90 |     100 |     100 | 66                
  base-emitter.ts  |   78.26 |       75 |     100 |   78.26 | 23-24,26-28       
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
  ...ll-emitter.ts |   99.18 |    96.47 |     100 |   99.18 | 355-356           
 ...ession/rewrite |    91.8 |    89.13 |   94.44 |    91.8 |                   
  LlmRewriter.ts   |    82.4 |     86.2 |     100 |    82.4 | ...,88-89,166-170 
  ...Middleware.ts |   96.96 |    88.09 |     100 |   96.96 | 144,152-154       
  TurnBuffer.ts    |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/agent-view    |   89.03 |    81.37 |   89.09 |   89.03 |                   
  ...t-cli-argv.ts |     100 |      100 |     100 |     100 |                   
  protocol.ts      |     100 |      100 |     100 |     100 |                   
  ...sor-client.ts |   80.38 |    72.54 |   76.66 |   80.38 | ...22-626,652-656 
  ...or-process.ts |   96.61 |    89.47 |   84.61 |   96.61 | 129-130,150-151   
  ...sor-runner.ts |    84.9 |     75.6 |      85 |    84.9 | ...44,468,471-481 
  ...sor-server.ts |   85.71 |    83.06 |   95.45 |   85.71 | ...67-468,471-488 
  ...isor-store.ts |   97.73 |    81.16 |     100 |   97.73 | ...92,594,607,643 
  ...nal-bridge.ts |   93.98 |     91.3 |   83.33 |   93.98 | 228-238           
 src/commands      |   90.81 |    79.61 |   66.66 |   90.81 |                   
  auth.ts          |     100 |    83.33 |     100 |     100 | 11,14             
  channel.ts       |   55.55 |      100 |       0 |   55.55 | 18-22,30-40       
  extensions.tsx   |   96.77 |      100 |      50 |   96.77 | 39                
  hooks.tsx        |   66.66 |      100 |       0 |   66.66 | 20-24             
  mcp.ts           |   95.45 |      100 |      50 |   95.45 | 31                
  review.ts        |   98.66 |      100 |      50 |   98.66 | 86                
  serve.ts         |   89.84 |    77.32 |     100 |   89.84 | ...52,855-858,870 
  sessions.ts      |     100 |      100 |      50 |     100 |                   
  update.ts        |   98.13 |    94.44 |   66.66 |   98.13 | 82-83             
 ...mmands/channel |   88.91 |     88.5 |   90.54 |   88.91 |                   
  channel-cwd.ts   |     100 |      100 |     100 |     100 |                   
  ...l-registry.ts |   95.21 |    96.73 |   88.88 |   95.21 | ...18-221,266-269 
  ...entry-path.ts |      75 |       50 |     100 |      75 | 8-9               
  config-utils.ts  |   95.87 |    96.35 |     100 |   95.87 | ...08-213,271-274 
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  daemon-worker.ts |   93.96 |    85.44 |   94.23 |   93.96 | ...1229,1236-1237 
  loop-runtime.ts  |   91.66 |      100 |      50 |   91.66 | 15,22             
  ...classifier.ts |   98.49 |    96.51 |     100 |   98.49 | 115-116,161       
  ...tact-store.ts |   93.51 |    87.65 |     100 |   93.51 | ...71,288-289,337 
  pairing.ts       |      75 |      100 |      50 |      75 | 22-28,59-70       
  pidfile.ts       |   95.55 |       90 |     100 |   95.55 | ...50-251,315-316 
  proxy.ts         |     100 |      100 |     100 |     100 |                   
  reload.ts        |    77.5 |    86.95 |      75 |    77.5 | 72-84,93-97       
  runtime.ts       |   82.43 |    86.44 |     100 |   82.43 | ...87-191,251-253 
  set.ts           |   75.72 |    85.71 |      50 |   75.72 | 65-83,111-116     
  start.ts         |    85.8 |    82.17 |      88 |    85.8 | ...85,591-594,606 
  ...ure-format.ts |   93.65 |    82.45 |     100 |   93.65 | ...42,48-49,74-75 
  status.ts        |   78.57 |    59.25 |   66.66 |   78.57 | ...36-137,150-161 
  stop.ts          |   57.83 |    82.35 |      50 |   57.83 | ...3,74-76,85-111 
 ...nds/extensions |   88.85 |    87.91 |   87.09 |   88.85 |                   
  consent.ts       |   72.53 |    90.32 |   42.85 |   72.53 | ...86-142,157-163 
  disable.ts       |     100 |       90 |     100 |     100 | 30                
  enable.ts        |     100 |    91.66 |     100 |     100 | 38                
  install.ts       |   82.95 |    81.57 |      75 |   82.95 | ...96-199,202-211 
  link.ts          |     100 |      100 |     100 |     100 |                   
  list.ts          |     100 |     90.9 |     100 |     100 | 18                
  new.ts           |     100 |      100 |     100 |     100 |                   
  settings.ts      |   99.15 |      100 |   83.33 |   99.15 | 151               
  sources.ts       |   93.42 |    87.09 |   92.85 |   93.42 | ...4-66,96-98,167 
  uninstall.ts     |   74.57 |       40 |   66.66 |   74.57 | 45-47,60-67,70-73 
  update.ts        |   96.71 |    97.05 |     100 |   96.71 | 114-118           
  utils.ts         |   75.63 |    57.14 |     100 |   75.63 | ...30-134,136-140 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 ...amples/starter |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-64              
 src/commands/mcp  |   90.31 |    84.61 |   83.33 |   90.31 |                   
  add.ts           |    99.3 |    96.07 |     100 |    99.3 | 154-155           
  approve.ts       |   76.19 |     87.5 |   66.66 |   76.19 | ...,89-99,114-124 
  list.ts          |   93.15 |    84.84 |      80 |   93.15 | ...78-180,198-199 
  reconnect.ts     |   78.85 |    66.66 |   85.71 |   78.85 | 42-55,169-191     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   87.66 |    88.04 |   88.42 |   87.66 |                   
  agent-prompt.ts  |   93.67 |    91.71 |   97.22 |   93.67 | ...2498,2623-2703 
  base-tree.ts     |   76.16 |    80.76 |   77.77 |   76.16 | ...50-371,373-386 
  capture-local.ts |   68.57 |     90.9 |      75 |   68.57 | 107-111,158-189   
  ...k-coverage.ts |   50.71 |       35 |   66.66 |   50.71 | ...40-245,279-289 
  cleanup.ts       |   89.12 |    82.22 |   83.33 |   89.12 | ...99-504,506-507 
  ...ent-status.ts |   93.03 |    83.87 |   83.33 |   93.03 | 291,531-551       
  ...ose-review.ts |   96.87 |    92.53 |   96.55 |   96.87 | ...2126,2154-2176 
  cost-ledger.ts   |   94.67 |    95.86 |   78.57 |   94.67 | ...04-505,545-555 
  drive.ts         |   76.07 |    85.71 |   81.81 |   76.07 | ...90-492,497-499 
  extract-step.ts  |   91.36 |    90.62 |   88.88 |   91.36 | ...90-707,714-729 
  fetch-pr.ts      |    76.7 |    68.75 |   63.63 |    76.7 | ...95,417,450-455 
  findings.ts      |   89.35 |    89.13 |   95.45 |   89.35 | ...15-918,927-928 
  load-rules.ts    |   26.41 |      100 |   16.66 |   26.41 | ...41-153,155-156 
  match-remote.ts  |   85.54 |     92.3 |   66.66 |   85.54 | 67-72,131-136     
  mock-provider.ts |   95.44 |    90.25 |   89.47 |   95.44 | 145,690-709       
  parse-args.ts    |   99.66 |    96.55 |     100 |   99.66 | 404               
  plan-diff.ts     |   64.04 |      100 |   66.66 |   64.04 | 127-163           
  pr-context.ts    |   81.77 |    80.86 |   92.85 |   81.77 | ...1043,1072-1074 
  presubmit.ts     |   83.75 |    92.72 |   88.88 |   83.75 | ...77-578,655-685 
  ...ish-assets.ts |   77.18 |    82.14 |   71.42 |   77.18 | ...85-531,533-544 
  repo-context.ts  |   94.92 |    90.82 |     100 |   94.92 | ...67-368,376-377 
  ...ve-anchors.ts |   77.77 |    88.88 |      75 |   77.77 | ...77-182,194-211 
  run.ts           |   82.16 |    87.12 |   91.66 |   82.16 | ...52,468-516,529 
  save-artifact.ts |    89.9 |    81.81 |   94.11 |    89.9 | ...08-311,404-407 
  script-lint.ts   |   83.67 |    78.41 |   88.88 |   83.67 | ...59-773,775-797 
  submit.ts        |   83.77 |    83.95 |      90 |   83.77 | ...55,544,571-607 
  test-delta.ts    |   87.13 |    91.46 |      75 |   87.13 | 206-237,477-485   
  test-efficacy.ts |   88.04 |    84.12 |   95.45 |   88.04 | ...2602,2610-2630 
  test-plan.ts     |   91.44 |    91.39 |   89.47 |   91.44 | ...38-839,903-920 
 ...w/__fixtures__ |     100 |      100 |     100 |     100 |                   
  ...r-default.mjs |     100 |      100 |     100 |     100 |                   
  ...der-empty.mjs |     100 |      100 |     100 |     100 |                   
  ...der-named.mjs |     100 |      100 |     100 |     100 |                   
 ...nds/review/lib |   97.09 |    95.05 |   97.91 |   97.09 |                   
  agent-briefs.ts  |   98.96 |      100 |      50 |   98.96 | 719-720           
  anchors.ts       |     100 |    94.79 |     100 |     100 | ...33,169,178,225 
  assets.ts        |     100 |      100 |     100 |     100 |                   
  authorization.ts |    92.4 |    92.59 |     100 |    92.4 | 127-133           
  budget.ts        |     100 |    97.14 |     100 |     100 | 513,553           
  coverage.ts      |   94.49 |    94.61 |      96 |   94.49 | ...72-489,526-537 
  deadline.ts      |   98.33 |    93.61 |     100 |   98.33 | ...88,237,629,661 
  diff-flags.ts    |     100 |        0 |     100 |     100 | 63                
  diff-plan.ts     |   98.73 |    93.01 |     100 |   98.73 | ...41,264,290-291 
  disk.ts          |     100 |      100 |     100 |     100 |                   
  effort.ts        |     100 |      100 |     100 |     100 |                   
  gh.ts            |   87.07 |    92.45 |   76.47 |   87.07 | ...72,309-310,337 
  git.ts           |   97.64 |    95.65 |     100 |   97.64 | 180-181           
  heavy.ts         |     100 |      100 |     100 |     100 |                   
  inline-counts.ts |     100 |      100 |     100 |     100 |                   
  ledger.ts        |     100 |      100 |     100 |     100 |                   
  local-diff.ts    |   84.86 |    90.38 |     100 |   84.86 | ...63-473,475-483 
  ...ry-context.ts |   96.19 |    94.93 |     100 |   96.19 | ...90-491,496-499 
  merge-base.ts    |     100 |      100 |     100 |     100 |                   
  npm-toolchain.ts |   97.36 |    95.37 |     100 |   97.36 | ...86,409,770,787 
  path-rules.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |     100 |     87.5 |     100 |     100 | 92                
  prompt-record.ts |   97.88 |    93.87 |     100 |   97.88 | 260-261,267       
  receipt.ts       |     100 |      100 |     100 |     100 |                   
  remote-match.ts  |   97.26 |    91.42 |     100 |   97.26 | 49-50             
  report.ts        |   94.68 |    93.75 |     100 |   94.68 | 189-193           
  ...ry-context.ts |     100 |    98.66 |     100 |     100 | 184               
  retirement.ts    |     100 |    92.39 |     100 |     100 | ...28,308-309,449 
  review-footer.ts |     100 |      100 |     100 |     100 |                   
  roster.ts        |     100 |    95.71 |     100 |     100 | 145,163,208       
  shell-quote.ts   |     100 |      100 |     100 |     100 |                   
  stale-bundle.ts  |   98.11 |    94.04 |     100 |   98.11 | 416,457,497-498   
  test-utils.ts    |     100 |      100 |     100 |     100 |                   
  toolchain.ts     |     100 |      100 |     100 |     100 |                   
  transcripts.ts   |   96.59 |    94.56 |     100 |   96.59 | ...08,297-298,323 
  ...pace-scope.ts |     100 |    96.96 |     100 |     100 | 172               
  workspaces.ts    |     100 |    96.77 |     100 |     100 | 222,452,499,512   
  worktree.ts      |     100 |      100 |     100 |     100 |                   
 ...mands/sessions |   91.56 |    86.95 |   83.33 |   91.56 |                   
  common.ts        |     100 |      100 |     100 |     100 |                   
  list.ts          |   90.96 |    86.66 |   81.81 |   90.96 | 208-219,221-222   
 src/config        |    94.9 |    89.85 |   96.27 |    94.9 |                   
  ...l-fallback.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   89.35 |    83.56 |     100 |   89.35 | ...97-298,314-315 
  ...eMcpImport.ts |   87.91 |    81.52 |     100 |   87.91 | ...63-371,453-454 
  compile-cache.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |   88.93 |    88.59 |   83.33 |   88.93 | ...2451,2453-2461 
  ...cy-monitor.ts |   88.75 |    76.19 |     100 |   88.75 | ...3,90-92,98,101 
  ...ust-policy.ts |   83.02 |    88.88 |     100 |   83.02 | ...02-209,232-240 
  ...heme-names.ts |     100 |      100 |     100 |     100 |                   
  environment.ts   |    96.5 |    93.58 |      95 |    96.5 | ...85-586,640-641 
  ...le-watcher.ts |   90.86 |    83.65 |   95.83 |   90.86 | ...23-325,370,418 
  ...resh-state.ts |   90.57 |    97.29 |   93.75 |   90.57 | 137-142,146-152   
  ...ime-reload.ts |     100 |    69.69 |     100 |     100 | ...12-113,122-123 
  hot-reload.ts    |     100 |    89.13 |     100 |     100 | 47,172-178,238    
  keyBindings.ts   |    97.4 |       50 |     100 |    97.4 | 240-243           
  ...ngsAdapter.ts |     100 |    94.11 |     100 |     100 | 64                
  ...ig-watcher.ts |   95.17 |    83.05 |     100 |   95.17 | ...78,200,292-293 
  ...er-secrets.ts |   98.97 |    96.87 |     100 |   98.97 | 85                
  mcpApprovals.ts  |   96.55 |    95.55 |     100 |   96.55 | 223-224,229-231   
  mcpJson.ts       |     100 |      100 |     100 |     100 |                   
  mcpServers.ts    |   92.85 |     87.5 |     100 |   92.85 | 46-47             
  ...idersScope.ts |      95 |    94.73 |     100 |      95 | 11-12             
  ...abledTools.ts |     100 |      100 |     100 |     100 |                   
  ...comparison.ts |     100 |      100 |     100 |     100 |                   
  ...n-settings.ts |   99.15 |    93.75 |     100 |   99.15 | 63                
  sandboxConfig.ts |   93.33 |    93.33 |     100 |   93.33 | ...42-147,216-217 
  session-id.ts    |     100 |      100 |     100 |     100 |                   
  ...ings-cache.ts |   96.52 |    93.93 |     100 |   96.52 | 90-91,201-202     
  settings.ts      |   91.27 |    92.64 |      90 |   91.27 | ...1030,1032-1033 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...ngsWatcher.ts |   95.54 |    88.34 |     100 |   95.54 | ...28,277-278,293 
  ...d-env-keys.ts |     100 |      100 |     100 |     100 |                   
  ...l-settings.ts |     100 |      100 |     100 |     100 |                   
  ...paths-lite.ts |   89.47 |       88 |     100 |   89.47 | 43-44,53-54,56-57 
  ...precedence.ts |   98.79 |     92.3 |     100 |   98.79 | 62                
  ...tedFolders.ts |   92.53 |    93.47 |     100 |   92.53 | ...36-337,373-384 
 ...nfig/migration |   95.23 |    77.77 |   83.33 |   95.23 |                   
  index.ts         |   95.65 |     87.5 |     100 |   95.65 | 117-118           
  scheduler.ts     |   96.55 |    77.77 |     100 |   96.55 | 19-20             
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ation/versions |   94.91 |      100 |     100 |   94.91 |                   
  ...-v2-shared.ts |     100 |      100 |     100 |     100 |                   
  v1-to-v2.ts      |   81.75 |      100 |     100 |   81.75 | ...28-229,231-247 
  v2-to-v3.ts      |     100 |      100 |     100 |     100 |                   
  v3-to-v4.ts      |     100 |      100 |     100 |     100 |                   
  v5-to-v4.ts      |      96 |      100 |     100 |      96 | 94-95,99          
 src/core          |     100 |      100 |     100 |     100 |                   
  auth.ts          |     100 |      100 |     100 |     100 |                   
  initializer.ts   |     100 |      100 |     100 |     100 |                   
  theme.ts         |     100 |      100 |     100 |     100 |                   
 src/dualOutput    |    71.8 |    70.31 |   66.66 |    71.8 |                   
  ...tputBridge.ts |   71.95 |    70.96 |   68.42 |   71.95 | ...08-409,417-420 
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/export        |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-7               
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 src/hooks         |     100 |      100 |     100 |     100 |                   
  ...elete-hook.ts |     100 |      100 |     100 |     100 |                   
 src/i18n          |   85.98 |    81.92 |   89.65 |   85.98 |                   
  index.ts         |   73.45 |    77.77 |      90 |   73.45 | ...70-271,294-299 
  languages.ts     |   93.07 |     92.3 |   85.71 |   93.07 | ...35,164-169,184 
  ...nslateKeys.ts |     100 |      100 |     100 |     100 |                   
  ...lationDict.ts |   93.33 |    66.66 |     100 |   93.33 | 15                
 src/i18n/locales  |     100 |      100 |     100 |     100 |                   
  ca.js            |     100 |      100 |     100 |     100 |                   
  de.js            |     100 |      100 |     100 |     100 |                   
  en.js            |     100 |      100 |     100 |     100 |                   
  fr.js            |     100 |      100 |     100 |     100 |                   
  ja.js            |     100 |      100 |     100 |     100 |                   
  pt.js            |     100 |      100 |     100 |     100 |                   
  ru.js            |     100 |      100 |     100 |     100 |                   
  zh-TW.js         |     100 |      100 |     100 |     100 |                   
  zh.js            |     100 |      100 |     100 |     100 |                   
 ...nonInteractive |   80.98 |    77.27 |   84.12 |   80.98 |                   
  session.ts       |   84.97 |    76.31 |   96.07 |   84.97 | ...1048,1057-1067 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...31-632,635-636 
 ...active/control |   75.54 |    89.83 |      80 |   75.54 |                   
  ...rolContext.ts |    6.06 |        0 |       0 |    6.06 | 57-99             
  ...Dispatcher.ts |   91.95 |    92.98 |   88.88 |   91.95 | ...54-372,392,395 
  ...rolService.ts |    6.89 |        0 |       0 |    6.89 | 46-188            
 ...ol/controllers |   45.95 |    69.03 |   55.26 |   45.95 |                   
  ...Controller.ts |    42.4 |      100 |   83.33 |    42.4 | 101-105,140-223   
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |   55.01 |    67.14 |   58.33 |   55.01 | ...15-624,639-644 
  ...Controller.ts |   49.23 |       60 |      50 |   49.23 | ...07-108,111-121 
  ...Controller.ts |   40.64 |    68.11 |   46.66 |   40.64 | ...72-684,693-722 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |    98.1 |    94.16 |   95.23 |    98.1 |                   
  ...putAdapter.ts |   97.98 |    93.23 |   98.07 |   97.98 | ...1415,1431-1432 
  ...putAdapter.ts |      96 |    91.66 |   85.71 |      96 | 51-52             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.49 |      100 |   90.47 |   98.49 | 85-86,126-127     
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/remoteInput   |   87.31 |    75.32 |   88.23 |   87.31 |                   
  ...utContext.tsx |     100 |      100 |     100 |     100 |                   
  ...putWatcher.ts |   88.01 |       76 |   93.33 |   88.01 | ...49-350,361-364 
  index.ts         |       0 |        0 |       0 |       0 | 1-8               
 src/runtime       |   99.61 |    95.04 |     100 |   99.61 |                   
  ...livery-ipc.ts |     100 |     90.9 |     100 |     100 | 94,106,134        
  ...l-delivery.ts |     100 |      100 |     100 |     100 |                   
  cpu-percent.ts   |     100 |      100 |     100 |     100 |                   
  ...erver-name.ts |     100 |      100 |     100 |     100 |                   
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  ...-summaries.ts |   86.66 |       50 |     100 |   86.66 | 11,19             
  ...ber-errors.ts |     100 |    95.32 |     100 |     100 | 53,93-94,172,192  
  ...ls-mapping.ts |     100 |      100 |     100 |     100 |                   
 src/serve         |   87.96 |    84.25 |   90.85 |   87.96 |                   
  ...tp-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...ion-bridge.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   93.43 |    93.05 |     100 |   93.43 | ...20-321,324-326 
  ...em-adapter.ts |     100 |      100 |     100 |     100 |                   
  capabilities.ts  |     100 |    98.07 |     100 |     100 | 679               
  ...cp-command.ts |     100 |      100 |     100 |     100 |                   
  ...horization.ts |   92.79 |    93.33 |    87.5 |   92.79 | 75-80,135-136     
  ...op-mcp-ipc.ts |   81.06 |    73.68 |   94.11 |   81.06 | ...37-242,267,289 
  ...nt-service.ts |    94.1 |    86.89 |     100 |    94.1 | ...75-477,484,486 
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...ings-store.ts |   88.59 |    93.68 |   96.29 |   88.59 | ...95-207,451-454 
  ...ebhook-ipc.ts |    98.5 |    86.66 |     100 |    98.5 | 47                
  ...iagnostics.ts |     100 |      100 |     100 |     100 |                   
  ...worker-env.ts |     100 |      100 |     100 |     100 |                   
  ...rker-group.ts |   87.27 |     85.2 |     100 |   87.27 | ...10,816-820,838 
  ...er-manager.ts |   89.39 |    83.88 |   93.33 |   89.39 | ...98,711,722-724 
  ...tartup-ipc.ts |   97.72 |    96.66 |     100 |   97.72 | 88-89             
  ...supervisor.ts |   92.42 |    84.44 |    97.1 |   92.42 | ...1466,1520-1524 
  ...e-grouping.ts |     100 |    94.28 |     100 |     100 | 71,137            
  core-runtime.ts  |     100 |      100 |     100 |     100 |                   
  ...ub-session.ts |    90.1 |    77.83 |   94.73 |    90.1 | ...1014,1021-1026 
  ...tree-guard.ts |   92.89 |    87.55 |     100 |   92.89 | ...2766,2836-2840 
  daemon-logger.ts |    82.2 |    77.42 |   91.76 |    82.2 | ...1720,1747-1753 
  ...y-pressure.ts |     100 |    96.96 |     100 |     100 | 135               
  ...trics-ring.ts |     100 |      100 |     100 |     100 |                   
  ...s-provider.ts |   68.04 |    52.77 |     100 |   68.04 | ...44-249,282-290 
  daemon-status.ts |   98.57 |     90.8 |     100 |   98.57 | ...1411,1413-1414 
  debug-mode.ts    |     100 |      100 |     100 |     100 |                   
  env-snapshot.ts  |   93.37 |    85.18 |     100 |   93.37 | 114-117,195-202   
  ...-scheduler.ts |   87.34 |    83.87 |     100 |   87.34 | 33-36,48-50,79-81 
  ...d-provider.ts |   92.06 |    87.09 |     100 |   92.06 | ...72,287-293,316 
  ...-path-argv.ts |     100 |      100 |     100 |     100 |                   
  ...h-settings.ts |   94.94 |    90.41 |     100 |   94.94 | ...30,708,724,734 
  fast-path.ts     |   90.99 |    81.38 |   95.45 |   90.99 | ...33-542,608-609 
  ...ration-sse.ts |   42.55 |    33.33 |     100 |   42.55 | 23-24,30,33-56    
  health-query.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-144             
  ...e-observer.ts |   89.89 |    83.24 |      96 |   89.89 | ...11-512,541-543 
  ...back-binds.ts |     100 |    88.88 |     100 |     100 | 32                
  ...-workspace.ts |    90.9 |    85.71 |     100 |    90.9 | ...30-131,142-143 
  ...iders-edit.ts |     100 |    82.14 |     100 |     100 | 58-60,65,81       
  ...ory-picker.ts |     100 |    86.95 |     100 |     100 | 36,66,92          
  ...sion-audit.ts |     100 |      100 |   93.33 |     100 |                   
  rate-limit.ts    |   92.68 |    88.29 |     100 |   92.68 | ...89-291,303-305 
  ...qwen-serve.ts |    84.2 |    80.42 |   75.43 |    84.2 | ...7485,7491-7492 
  ...tup-errors.ts |     100 |      100 |     100 |     100 |                   
  ...-keepalive.ts |   94.22 |    87.96 |     100 |   94.22 | ...27,531-532,572 
  ...-lifecycle.ts |     100 |      100 |     100 |     100 |                   
  server.ts        |   90.59 |    91.18 |   71.81 |   90.59 | ...2720,2734-2738 
  ...-admission.ts |   98.24 |    94.73 |     100 |   98.24 | 79-80,303-304     
  ...on-helpers.ts |     100 |      100 |     100 |     100 |                   
  ...t-event-id.ts |     100 |    95.23 |     100 |     100 | 12                
  ...-admission.ts |   98.71 |    89.65 |     100 |   98.71 | 68                
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ion-limits.ts |     100 |      100 |     100 |     100 |                   
  ...t-sessions.ts |    93.3 |    76.83 |     100 |    93.3 | ...20,823,836-838 
  ...l-resolver.ts |   90.32 |    66.66 |     100 |   90.32 | 16,45-46          
  ...ell-static.ts |   92.18 |    88.37 |     100 |   92.18 | ...21-224,267-270 
  ...ace-agents.ts |   66.13 |    70.57 |   92.68 |   66.13 | ...2246,2256-2266 
  ...generation.ts |    95.4 |    82.35 |   66.66 |    95.4 | 55-56,78,92       
  ...-git-state.ts |     100 |    91.93 |    90.9 |     100 | 161,172,202,265   
  ...ace-inputs.ts |     100 |      100 |     100 |     100 |                   
  ...ace-memory.ts |      83 |    74.54 |     100 |      83 | ...30-537,597-604 
  ...ers-status.ts |   98.58 |       79 |     100 |   98.58 | 106,134,174,177   
  ...tion-store.ts |   89.67 |    88.27 |   92.59 |   89.67 | ...91-400,411-414 
  ...e-registry.ts |   93.89 |     87.5 |     100 |   93.89 | ...18-519,525-526 
  ...e-remember.ts |   98.23 |    92.51 |     100 |   98.23 | ...36,340-345,386 
  ...te-runtime.ts |   83.98 |    90.29 |     100 |   83.98 | ...48-156,216-237 
  ...me-storage.ts |     100 |      100 |     100 |     100 |                   
  ...management.ts |   72.63 |    72.72 |      96 |   72.63 | ...88-889,896-900 
  ...lls-status.ts |     100 |    95.45 |     100 |     100 | 152               
  ...reconciler.ts |   91.63 |    84.26 |     100 |   91.63 | ...71-273,306-307 
 ...serve/acp-http |   78.25 |    80.02 |    93.1 |   78.25 |                   
  ...r-registry.ts |   96.92 |    94.87 |     100 |   96.92 | 184-187           
  client-mcp-ws.ts |   54.85 |    58.62 |   72.72 |   54.85 | ...99-300,304-305 
  ...n-registry.ts |    98.2 |    88.55 |     100 |    98.2 | 1015,1041-1052    
  dispatch.ts      |   73.85 |     77.7 |   94.23 |   73.85 | ...5227,5275-5281 
  index.ts         |   82.23 |    80.11 |   91.07 |   82.23 | ...2341,2425-2426 
  json-rpc.ts      |     100 |    96.96 |     100 |     100 | 92                
  safe-ws-send.ts  |   52.94 |    71.42 |     100 |   52.94 | 33-42,47-55       
  sse-stream.ts    |   93.96 |    88.57 |   84.61 |   93.96 | ...57-159,161-163 
  ...ort-stream.ts |       0 |        0 |       0 |       0 | 1                 
  ws-stream.ts     |   91.86 |       80 |     100 |   91.86 | 45,50,96,100-103  
 src/serve/auth    |   86.86 |     79.7 |   93.87 |   86.86 |                   
  device-flow.ts   |   96.35 |    80.57 |   97.61 |   96.35 | ...1358,1453,1519 
  ...w-provider.ts |   44.24 |    74.07 |   71.42 |   44.24 | ...23-284,297,301 
 ...rve/cdp-tunnel |   87.73 |    76.21 |    97.5 |   87.73 |                   
  ...r-emulator.ts |   93.27 |    77.77 |     100 |   93.27 | ...53-256,282-283 
  ...verse-link.ts |      88 |    76.19 |     100 |      88 | ...28-329,420-423 
  ...l-registry.ts |     100 |      100 |     100 |     100 |                   
  cdp-ws.ts        |   76.28 |    61.29 |    87.5 |   76.28 | ...13-217,223-228 
 ...nel/acceptance |    6.12 |    57.89 |   46.15 |    6.12 |                   
  ...helpers.d.mts |       0 |        0 |       0 |       0 | 1                 
  ...e-helpers.mjs |   97.64 |    70.96 |     100 |   97.64 | 22-23             
  ...mcp-smoke.mjs |       0 |        0 |       0 |       0 | 1-124             
  ...cceptance.mjs |       0 |        0 |       0 |       0 | 1-473             
  ...re-server.mjs |       0 |        0 |       0 |       0 | 1-59              
  ...ols-smoke.mjs |       0 |        0 |       0 |       0 | 1-268             
  real-tab.mjs     |       0 |        0 |       0 |       0 | 1-218             
  ...al-chrome.mjs |       0 |        0 |       0 |       0 | 1-223             
 src/serve/fs      |   86.46 |    81.42 |     100 |   86.46 |                   
  audit.ts         |     100 |    96.15 |     100 |     100 | 204               
  errors.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...x-registry.ts |     100 |      100 |     100 |     100 |                   
  paths.ts         |   77.64 |    74.21 |     100 |   77.64 | ...65,594-598,611 
  policy.ts        |   90.42 |    89.18 |     100 |   90.42 | 161-169           
  text-cursor.ts   |   88.23 |       90 |     100 |   88.23 | 74-77,92-95       
  ...ile-system.ts |   86.27 |    80.56 |     100 |   86.27 | ...2699,2709-2710 
 src/serve/live    |   78.01 |    69.54 |   90.61 |   78.01 |                   
  ...en-context.ts |   95.74 |    81.25 |     100 |   95.74 | ...0,66-67,99-100 
  ...-workspace.ts |   88.63 |    82.53 |     100 |   88.63 | ...40-241,253-254 
  discovery.ts     |   85.77 |    76.92 |      90 |   85.77 | ...49-250,255-256 
  ...structions.ts |     100 |      100 |     100 |     100 |                   
  ...oordinator.ts |   82.67 |     76.8 |   97.01 |   82.67 | ...1319,1351-1353 
  ...-installer.ts |    64.3 |    82.35 |   80.76 |    64.3 | ...45-446,460-472 
  ...oordinator.ts |   75.99 |    65.18 |   85.71 |   75.99 | ...1883,1974-1975 
  ...controller.ts |   67.82 |    79.31 |   72.72 |   67.82 | ...66-278,287-295 
  ...ak-to-user.ts |   96.66 |      100 |   83.33 |   96.66 | 37-38             
  ...sk-service.ts |    86.3 |    59.78 |   93.33 |    86.3 | ...1160,1184-1191 
  ...task-tools.ts |      99 |      100 |   85.71 |      99 | 205-206           
  ...redentials.ts |   96.26 |    93.47 |     100 |   96.26 | 91-94             
  ...me-session.ts |   65.63 |    57.24 |   88.88 |   65.63 | ...2270,2275-2282 
  ...up-context.ts |   94.83 |    77.58 |     100 |   94.83 | ...18,327-330,350 
  ...ion-source.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/serve/routes  |    85.6 |    80.12 |   94.73 |    85.6 |                   
  a2ui-action.ts   |   96.84 |     88.5 |    87.5 |   96.84 | ...70-272,309-311 
  capabilities.ts  |     100 |      100 |     100 |     100 |                   
  ...nel-notify.ts |   86.45 |       88 |     100 |   86.45 | ...,83-87,103-104 
  ...l-webhooks.ts |   93.56 |    84.09 |     100 |   93.56 | ...42,292,332,334 
  daemon-status.ts |   85.71 |    83.33 |     100 |   85.71 | 101-108           
  goals.ts         |   98.92 |     90.9 |     100 |   98.92 | 146               
  health.ts        |   99.09 |    91.17 |     100 |   99.09 | 147               
  live-setup.ts    |   33.33 |     37.5 |      50 |   33.33 | ...18-123,130-135 
  live.ts          |    82.4 |    71.42 |     100 |    82.4 | ...-94,96-101,121 
  permission.ts    |     100 |     92.3 |     100 |     100 | 50,98             
  ...uled-tasks.ts |   87.29 |    82.94 |   92.59 |   87.29 | ...1275,1318-1319 
  ...on-runtime.ts |     100 |    90.47 |     100 |     100 | 58,94             
  session.ts       |   85.74 |    82.18 |    91.3 |   85.74 | ...4831,4833-4834 
  sse-events.ts    |   86.82 |    85.71 |   94.11 |   86.82 | ...16-927,930,937 
  usage-stats.ts   |     100 |    95.45 |     100 |     100 | 118               
  ...space-auth.ts |   85.55 |    75.64 |     100 |   85.55 | ...21-326,331,345 
  ...el-control.ts |   86.26 |    78.94 |     100 |   86.26 | ...17-318,339-347 
  ...management.ts |   90.92 |    79.69 |     100 |   90.92 | ...81-482,501-502 
  ...d-contacts.ts |     100 |      100 |     100 |     100 |                   
  ...controller.ts |   83.11 |    79.31 |      90 |   83.11 | ...1033,1039,1042 
  ...extensions.ts |   88.15 |    75.19 |   92.98 |   88.15 | ...2027,2072-2073 
  ...-file-read.ts |      91 |    80.91 |     100 |      91 | ...20-621,624-625 
  ...file-write.ts |   84.44 |    64.51 |     100 |   84.44 | ...73-275,355-357 
  ...t-branches.ts |   75.43 |    66.66 |     100 |   75.43 | ...13-618,627-634 
  ...e-git-diff.ts |   97.32 |    90.56 |     100 |   97.32 | 161-162,189-191   
  ...ce-git-log.ts |     100 |    93.18 |     100 |     100 | 52,77,188         
  workspace-git.ts |   77.08 |    89.65 |     100 |   77.08 | 97-118            
  ...github-prs.ts |   88.26 |    63.46 |     100 |   88.26 | ...38-239,264-265 
  ...-lifecycle.ts |   95.23 |    75.75 |     100 |   95.23 | ...50-151,186-187 
  ...management.ts |   87.41 |    84.13 |     100 |   87.41 | ...1660,1680-1685 
  ...cp-control.ts |    73.2 |    67.54 |   85.71 |    73.2 | ...27-633,644-645 
  ...ace-models.ts |   95.53 |    89.74 |     100 |   95.53 | ...52-157,296-297 
  ...ermissions.ts |    77.9 |    72.41 |     100 |    77.9 | ...69-277,298-316 
  ...e-settings.ts |   75.04 |    72.99 |     100 |   75.04 | ...79-690,696-697 
  ...tup-github.ts |   77.97 |    70.58 |   84.21 |   77.97 | ...46-352,397-398 
  ...ace-skills.ts |    76.9 |    87.15 |     100 |    76.9 | ...29-354,360-394 
  ...ace-status.ts |   82.94 |     74.5 |     100 |   82.94 | ...84-486,490-491 
  ...pace-tools.ts |   75.94 |    69.69 |   66.66 |   75.94 | ...59-164,193-194 
  ...pace-trust.ts |   78.92 |    66.21 |      80 |   78.92 | ...38-343,351-352 
  ...pace-voice.ts |   91.33 |    80.92 |     100 |   91.33 | ...70-673,676-678 
 src/serve/server  |    91.8 |    88.97 |   96.85 |    91.8 |                   
  access-log.ts    |   98.68 |     97.1 |     100 |   98.68 | 115,186           
  ...er-helpers.ts |   63.82 |    77.96 |   81.81 |   63.82 | ...16,330,332-347 
  ...w-registry.ts |    98.8 |    81.81 |     100 |    98.8 | 107               
  ...r-handlers.ts |   97.29 |       75 |     100 |   97.29 | 17                
  ...r-response.ts |   86.54 |    72.48 |     100 |   86.54 | ...49,766,829-838 
  fs-factory.ts    |     100 |    94.54 |     100 |     100 | 42,103,159        
  ...branch-ops.ts |     100 |      100 |     100 |     100 |                   
  ...list-cache.ts |   98.58 |    94.87 |     100 |   98.58 | 156-157           
  ...t-deadline.ts |     100 |      100 |     100 |     100 |                   
  ...iter-setup.ts |      65 |    73.33 |   33.33 |      65 | 30-35,38-43,47-48 
  ...st-helpers.ts |   95.13 |    95.09 |     100 |   95.13 | ...66-168,423-428 
  self-origin.ts   |   76.19 |       80 |     100 |   76.19 | 45-54             
  ...e-features.ts |      95 |     87.5 |     100 |      95 | 182-188           
  ...on-archive.ts |   89.55 |    87.72 |   97.14 |   89.55 | ...32-836,888-889 
  ...ion-export.ts |     100 |    94.44 |     100 |     100 | 64                
  session-list.ts  |    97.1 |     92.5 |     100 |    97.1 | ...95,798-804,964 
  telemetry.ts     |   99.03 |    97.44 |     100 |   99.03 | ...31,645,787-789 
 src/serve/voice   |    92.7 |    91.48 |   97.67 |    92.7 |                   
  ...ice-config.ts |   84.81 |       30 |     100 |   84.81 | 91-100,104-105    
  voice-ws.ts      |   91.58 |    93.44 |      96 |   91.58 | ...68,483,521-523 
  ...oordinator.ts |     100 |    98.21 |     100 |     100 | 176               
 ...kspace-service |   90.65 |    87.73 |   91.11 |   90.65 |                   
  index.ts         |   90.13 |    87.04 |   89.74 |   90.13 | ...1464-1468,1471 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services      |   92.49 |    89.25 |      98 |   92.49 |                   
  ...mandLoader.ts |     100 |    88.88 |     100 |     100 | 105-118           
  ...killLoader.ts |   97.19 |    85.29 |     100 |   97.19 | 142,153-154       
  ...andService.ts |   98.73 |      100 |     100 |   98.73 | 107               
  ...mandLoader.ts |   87.09 |    83.07 |     100 |   87.09 | ...35-340,345-350 
  ...omptLoader.ts |   79.55 |    88.29 |   83.33 |   79.55 | ...48,178,245-246 
  ...mandLoader.ts |   97.77 |    92.15 |     100 |   97.77 | 176,183-184       
  ...nd-factory.ts |   91.42 |    91.66 |     100 |   91.42 | 128,137-144       
  ...ation-tool.ts |     100 |    95.45 |     100 |     100 | 125               
  ...ndMetadata.ts |   98.23 |    96.72 |     100 |   98.23 | 83,87             
  commandUtils.ts  |      96 |     90.9 |     100 |      96 | 48                
  ...and-parser.ts |   90.69 |    85.71 |     100 |   90.69 | 63-66             
  ...ionService.ts |     100 |      100 |     100 |     100 |                   
  prompt-stash.ts  |   96.66 |    92.85 |     100 |   96.66 | 34-35             
  ...tree-lease.ts |   88.23 |    86.48 |     100 |   88.23 | ...94-199,232-233 
  ...low-loader.ts |     100 |    96.15 |     100 |     100 | 88                
  setup-github.ts  |    90.8 |    80.95 |     100 |    90.8 | ...49-450,457-458 
  ...-args-file.ts |   93.93 |    91.66 |    87.5 |   93.93 | 208-210,224-230   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...e-keyterms.ts |   98.64 |    95.77 |     100 |   98.64 | 116,142-143       
  voice-model.ts   |     100 |      100 |     100 |     100 |                   
  voice-service.ts |    90.4 |    87.87 |     100 |    90.4 | ...81,288,353-358 
  ...e-settings.ts |     100 |    95.23 |     100 |     100 | 19                
  ...ranscriber.ts |   91.77 |    87.11 |   97.22 |   91.77 | ...99-901,904-906 
 ...rvices/insight |     100 |      100 |     100 |     100 |                   
  dates.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |   88.91 |     86.8 |   96.15 |   88.91 |                   
  DataProcessor.ts |   88.28 |    86.77 |   94.73 |   88.28 | ...1362,1366-1373 
  ...tGenerator.ts |   98.24 |    85.71 |     100 |   98.24 | 47                
  ...teRenderer.ts |     100 |      100 |     100 |     100 |                   
 .../insight/types |       0 |       50 |      50 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 |                   
  ...sightTypes.ts |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.27 |    94.04 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |    84.21 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.65 |     100 |   97.41 | 96-99             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services/tips |   97.27 |    84.61 |     100 |   97.27 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  tipHistory.ts    |   92.59 |       70 |     100 |   92.59 | ...24,146,153,162 
  tipRegistry.ts   |     100 |      100 |     100 |     100 |                   
  tipScheduler.ts  |     100 |    91.66 |     100 |     100 | 55                
 src/startup       |   88.99 |    83.47 |    90.9 |   88.99 |                   
  ...p-prefetch.ts |   98.09 |    94.23 |    87.5 |   98.09 | 50,209,225-226    
  ...reeStartup.ts |   80.53 |     74.6 |     100 |   80.53 | ...94,403,409-412 
 src/test-utils    |   94.09 |    79.16 |   77.77 |   94.09 |                   
  ci-env.ts        |      88 |     62.5 |     100 |      88 | 22-23,28          
  ...omMatchers.ts |   69.69 |       50 |      50 |   69.69 | 32-35,37-39,45-47 
  ...mised-lock.ts |     100 |      100 |   66.66 |     100 |                   
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   73.15 |    75.52 |   67.03 |   73.15 |                   
  App.tsx          |   33.33 |       75 |   33.33 |   33.33 | 32-86             
  AppContainer.tsx |   74.35 |    72.04 |   68.57 |   74.35 | ...4181,4297-4303 
  ...tionNudge.tsx |    9.58 |      100 |       0 |    9.58 | 24-94             
  ...ackDialog.tsx |    30.3 |      100 |       0 |    30.3 | 26-76             
  ...tionNudge.tsx |    7.69 |      100 |       0 |    7.69 | 25-103            
  colors.ts        |      60 |      100 |   35.29 |      60 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.91 |    97.14 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...ractiveUI.tsx |   70.51 |       74 |    62.5 |   70.51 | ...12,339,392-397 
  ...inePresets.ts |   96.27 |    83.87 |     100 |   96.27 | ...97,402,410-412 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/auth       |   58.53 |    66.18 |   51.06 |   58.53 |                   
  AuthDialog.tsx   |   59.01 |     42.1 |   16.66 |   59.01 | ...25,332-354,358 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |   60.21 |    70.73 |   57.69 |   60.21 | ...90,794,803,806 
  useAuth.ts       |    94.6 |    73.52 |     100 |    94.6 | ...21-222,241-247 
  ...rSetupFlow.ts |   43.18 |    33.33 |      50 |   43.18 | ...78-399,416-459 
 src/ui/commands   |   82.69 |     83.2 |   89.17 |   82.69 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  agentsCommand.ts |   83.78 |      100 |      60 |   83.78 | 30-32,42-44       
  ...odeCommand.ts |    93.1 |    95.23 |     100 |    93.1 | 77-82             
  arenaCommand.ts  |   63.89 |    65.71 |   65.21 |   63.89 | ...01-606,691-699 
  authCommand.ts   |     100 |      100 |     100 |     100 |                   
  branchCommand.ts |     100 |      100 |     100 |     100 |                   
  btwCommand.ts    |   94.32 |    81.25 |     100 |   94.32 | 35-36,114-119     
  bugCommand.ts    |     100 |    77.77 |     100 |     100 | 27,61             
  cdCommand.ts     |    92.3 |    82.75 |     100 |    92.3 | ...,94-99,178,187 
  clearCommand.ts  |    80.9 |    70.83 |     100 |    80.9 | ...28-129,137-146 
  ...essCommand.ts |   68.06 |    54.05 |      75 |   68.06 | ...96-197,211-214 
  ...astCommand.ts |   84.17 |       75 |     100 |   84.17 | ...,91-97,125-130 
  ...ig-command.ts |   93.12 |    88.42 |     100 |   93.12 | ...07-315,321-323 
  ...extCommand.ts |   69.07 |     72.6 |   84.61 |   69.07 | ...78-611,622-623 
  copyCommand.ts   |    98.7 |    96.29 |     100 |    98.7 | 66-67,172,272,323 
  ...or-command.ts |   85.95 |    80.55 |   88.88 |   85.95 | ...68-274,298-309 
  deleteCommand.ts |     100 |      100 |     100 |     100 |                   
  diffCommand.ts   |     100 |    87.87 |     100 |     100 | ...63,231-232,245 
  ...ryCommand.tsx |   81.64 |    87.67 |    90.9 |   81.64 | ...73-278,325-332 
  docsCommand.ts   |     100 |     90.9 |     100 |     100 | 25                
  doctorCommand.ts |   65.37 |    81.88 |   94.11 |   65.37 | ...85-535,538-672 
  dreamCommand.ts  |   85.45 |    88.88 |     100 |   85.45 | 58-65             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  ...rt-command.ts |   80.48 |       75 |     100 |   80.48 | 49-54,69-72,93-98 
  effort-utils.ts  |     100 |      100 |     100 |     100 |                   
  exportCommand.ts |   98.25 |    91.02 |     100 |   98.25 | ...81,198-199,364 
  ...onsCommand.ts |   52.31 |    56.25 |   69.23 |   52.31 | ...09,277-329,390 
  forgetCommand.ts |     100 |       90 |     100 |     100 | 59                
  forkCommand.ts   |     100 |    94.11 |     100 |     100 | 96,147            
  goalCommand.ts   |   72.81 |    86.84 |   66.66 |   72.81 | ...63-168,277-280 
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  ...oryCommand.ts |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   81.13 |    65.71 |   85.71 |   81.13 | ...,86-93,131-132 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  ...figCommand.ts |   52.83 |    81.25 |      70 |   52.83 | ...74-319,321-330 
  initCommand.ts   |   91.86 |       80 |     100 |   91.86 | 48,83-88          
  ...ghtCommand.ts |   77.87 |    71.42 |     100 |   77.87 | ...44-245,250-272 
  ...ageCommand.ts |   94.44 |    90.14 |     100 |   94.44 | ...13-214,241-251 
  learn-command.ts |     100 |      100 |     100 |     100 |                   
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,101-102        
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   84.78 |    82.47 |     100 |   84.78 | ...1071,1105-1110 
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  planCommand.ts   |   78.82 |    76.92 |     100 |   78.82 | 30-35,51-56,68-73 
  quitCommand.ts   |     100 |      100 |     100 |     100 |                   
  recapCommand.ts  |   21.81 |      100 |      50 |   21.81 | 24-73             
  ...ns-command.ts |   98.83 |    81.81 |     100 |   98.83 | 100               
  ...berCommand.ts |     100 |     87.5 |     100 |     100 | 46                
  renameCommand.ts |   89.06 |    88.37 |     100 |   89.06 | ...72-176,202-209 
  ...oreCommand.ts |   90.96 |    86.04 |     100 |   90.96 | ...41-146,177-178 
  resumeCommand.ts |     100 |      100 |     100 |     100 |                   
  rewindCommand.ts |   81.25 |      100 |      50 |   81.25 | 20-22             
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |   89.47 |       75 |      80 |   89.47 | 54-59             
  skillsCommand.ts |   78.82 |    81.81 |     100 |   78.82 | 37-52,78,97       
  statsCommand.ts  |   90.65 |    76.73 |     100 |   90.65 | ...30-733,825-832 
  ...ineCommand.ts |     100 |      100 |     100 |     100 |                   
  ...aryCommand.ts |   73.04 |     82.3 |      90 |   73.04 | ...20-547,561-565 
  tasksCommand.ts  |   77.22 |    72.13 |     100 |   77.22 | ...46-150,172-177 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  toolsCommand.ts  |     100 |      100 |     100 |     100 |                   
  trustCommand.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...te-command.ts |     100 |    94.11 |     100 |     100 | 74,148            
  vimCommand.ts    |   54.54 |      100 |      50 |   54.54 | 19-29             
  voice-command.ts |   93.57 |       88 |     100 |   93.57 | 35,97-102         
  ...owsCommand.ts |   92.92 |       85 |   66.66 |   92.92 | ...72-177,276-281 
 src/ui/components |   71.73 |    79.32 |   79.85 |   71.73 |                   
  AboutBox.tsx     |     100 |      100 |     100 |     100 |                   
  AnsiOutput.tsx   |   65.57 |      100 |      50 |   65.57 | 69-90             
  ApiKeyInput.tsx  |       0 |        0 |       0 |       0 | 1-97              
  AppHeader.tsx    |    88.7 |       75 |     100 |    88.7 | 36,38-43,45       
  ...odeDialog.tsx |   87.24 |    72.22 |   33.33 |   87.24 | ...85,233-238,245 
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |   95.65 |    66.66 |     100 |   95.65 | 27,52             
  ...TextInput.tsx |   88.65 |    90.41 |     100 |   88.65 | ...84-286,300-302 
  Composer.tsx     |   94.49 |    66.66 |     100 |   94.49 | ...-72,84,139,153 
  ...entPrompt.tsx |     100 |      100 |     100 |     100 |                   
  ...ryDisplay.tsx |   75.89 |    62.06 |     100 |   75.89 | ...,88,93-108,113 
  ...geDisplay.tsx |   68.42 |    57.14 |     100 |   68.42 | 16-17,31-32,42-50 
  CronPill.tsx     |     100 |    93.75 |     100 |     100 | 19                
  ...ification.tsx |      84 |       60 |     100 |      84 | 23-24,40-42       
  ...gProfiler.tsx |       0 |        0 |       0 |       0 | 1-36              
  ...ogManager.tsx |       0 |        0 |       0 |       0 | 1-598             
  DiffDialog.tsx   |    53.5 |     37.5 |   69.23 |    53.5 | ...32-737,747-760 
  ...ngsDialog.tsx |       0 |        0 |       0 |       0 | 1-195             
  EffortDialog.tsx |   97.36 |      100 |     100 |   97.36 | 55-56             
  ExitWarning.tsx  |     100 |      100 |     100 |     100 |                   
  ...hProgress.tsx |    87.8 |    33.33 |     100 |    87.8 | 28-31,56          
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   81.19 |    69.23 |      50 |   81.19 | ...02,240,262-267 
  ...ngSpinner.tsx |   68.42 |    85.71 |      50 |   68.42 | 35-52,73,80-81    
  GoalPill.tsx     |   93.51 |    81.81 |     100 |   93.51 | 37-38,106-109,123 
  Header.tsx       |   98.65 |    94.73 |     100 |   98.65 | 173,175           
  Help.tsx         |   98.33 |       90 |     100 |   98.33 | ...25,382,448-449 
  ...emDisplay.tsx |   79.28 |    66.99 |     100 |   79.28 | ...08,511,514-520 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   84.26 |    82.94 |      80 |   84.26 | ...2215,2236,2332 
  ...Shortcuts.tsx |     100 |       88 |     100 |     100 | 98,119            
  ...Indicator.tsx |   98.18 |    97.82 |     100 |   98.18 | 161-162           
  ...firmation.tsx |   91.42 |      100 |      50 |   91.42 | 26-31             
  MainContent.tsx  |   96.31 |    95.06 |      50 |   96.31 | ...01,464-468,471 
  MemoryDialog.tsx |   86.59 |    80.15 |     100 |   86.59 | ...34-435,485,553 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-41              
  ModelDialog.tsx  |   81.95 |    71.27 |     100 |   81.95 | ...1045,1050-1066 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |       0 |        0 |       0 |       0 | 1-56              
  ...onsDialog.tsx |       0 |        0 |       0 |       0 | 1-1004            
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...icePrompt.tsx |   92.64 |    85.71 |     100 |   92.64 | 102-106,134-139   
  PrepareLabel.tsx |   91.66 |    77.27 |     100 |   91.66 | 73-75,77-79,110   
  ...atePrompt.tsx |       0 |        0 |       0 |       0 | 1-134             
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |       0 |        0 |       0 |       0 | 1-39              
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...dSelector.tsx |   92.79 |    82.65 |     100 |   92.79 | ...19-323,354-370 
  ...ionPicker.tsx |   83.66 |    72.13 |     100 |   83.66 | ...96,402,444-466 
  ...onPreview.tsx |   93.58 |    83.78 |     100 |   93.58 | ...,70-71,195-197 
  ...ryDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...putPrompt.tsx |   92.06 |    86.36 |   83.33 |   92.06 | ...,70-72,120-123 
  ...tedDialog.tsx |     100 |      100 |     100 |     100 |                   
  ...ngsDialog.tsx |   71.49 |    73.89 |   69.23 |   71.49 | ...1244,1250-1251 
  ...ionDialog.tsx |    92.3 |    96.15 |   33.33 |    92.3 | 60-63,68-75,164   
  ...putPrompt.tsx |    15.9 |      100 |       0 |    15.9 | 20-63             
  ...Indicator.tsx |   57.14 |      100 |       0 |   57.14 | 12-15             
  ...MoreLines.tsx |       0 |        0 |       0 |       0 | 1-40              
  ...iewDialog.tsx |   97.77 |    87.67 |     100 |   97.77 | ...97,305-307,324 
  ...tsDisplay.tsx |   95.86 |       75 |     100 |   95.86 | 67-71             
  ...ionPicker.tsx |       0 |        0 |       0 |       0 | 1-172             
  ...tivityTab.tsx |    3.94 |      100 |       0 |    3.94 | 27-275            
  StatsDialog.tsx  |    8.64 |      100 |       0 |    8.64 | ...76-111,130-322 
  StatsDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ciencyTab.tsx |    78.9 |    56.52 |     100 |    78.9 | ...26,213,262-288 
  ...atmapView.tsx |    8.98 |      100 |       0 |    8.98 | 20-107            
  ...essionTab.tsx |      80 |    66.66 |     100 |      80 | ...70-277,283-300 
  ...ineDialog.tsx |   93.82 |    86.66 |     100 |   93.82 | ...17,279,299-301 
  ...yTodoList.tsx |   96.36 |    88.23 |     100 |   96.36 | 138-141           
  ...nsDisplay.tsx |   95.62 |    87.09 |     100 |   95.62 | ...24-125,273-275 
  ...inalImage.tsx |     100 |    93.93 |     100 |     100 | 75,129            
  ThemeDialog.tsx  |   89.95 |    46.15 |      75 |   89.95 | ...71-173,243-245 
  Tips.tsx         |   93.54 |       75 |     100 |   93.54 | 39-40             
  TodoDisplay.tsx  |     100 |      100 |     100 |     100 |                   
  ...tsDisplay.tsx |     100 |     87.5 |     100 |     100 | 31-32             
  TrustDialog.tsx  |     100 |    83.33 |     100 |     100 | 72-87             
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...Indicator.tsx |    92.5 |     87.5 |     100 |    92.5 | 50-53             
  ...ackDialog.tsx |       0 |        0 |       0 |       0 | 1-134             
  ...xitDialog.tsx |   80.36 |    43.47 |      60 |   80.36 | ...24-238,248-251 
  ...odeVisuals.ts |   97.22 |    85.71 |     100 |   97.22 | 25                
  ...s-helpers.tsx |   66.25 |    81.25 |      50 |   66.25 | 25-32,46-53,62-72 
 ...nts/agent-view |   55.05 |    69.09 |      50 |   55.05 |                   
  ...atContent.tsx |    9.09 |      100 |       0 |    9.09 | 54-275,281-283    
  ...tChatView.tsx |   21.05 |      100 |       0 |   21.05 | 21-39             
  ...tComposer.tsx |   69.48 |    33.33 |   66.66 |   69.48 | ...51,269,277-279 
  AgentFooter.tsx  |   15.38 |      100 |       0 |   15.38 | 28-65             
  AgentHeader.tsx  |   15.38 |      100 |       0 |   15.38 | 27-64             
  AgentTabBar.tsx  |    87.9 |    63.88 |     100 |    87.9 | ...88,110-118,136 
  ...oryAdapter.ts |     100 |    91.83 |     100 |     100 | 103,109-110,138   
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
 ...mponents/arena |    42.3 |    68.69 |   73.68 |    42.3 |                   
  ArenaCards.tsx   |   73.06 |    71.79 |   85.71 |   73.06 | ...83-185,321-326 
  ...ectDialog.tsx |   83.48 |    69.86 |   88.88 |   83.48 | ...88-392,409-410 
  ...artDialog.tsx |       0 |        0 |       0 |       0 | 1-166             
  ...tusDialog.tsx |       0 |        0 |       0 |       0 | 1-288             
  ...topDialog.tsx |       0 |        0 |       0 |       0 | 1-213             
 ...ackground-view |   85.34 |    84.91 |   92.98 |   85.34 |                   
  ...sksDialog.tsx |   81.87 |    82.77 |   85.71 |   81.87 | ...1853,1965-1971 
  ...TasksPill.tsx |   78.84 |    94.28 |     100 |   78.84 | 64,109-129        
  ...gentPanel.tsx |   97.08 |    86.31 |     100 |   97.08 | 132,442-446,520   
  agent-forest.ts  |    99.2 |    93.93 |     100 |    99.2 | 258               
  ...Visibility.ts |     100 |      100 |     100 |     100 |                   
  ...e-overlay.tsx |    88.2 |    76.47 |     100 |    88.2 | ...36-138,140-142 
 ...nts/extensions |   84.32 |    76.78 |   83.33 |   84.32 |                   
  ...gerDialog.tsx |   82.15 |    76.08 |     100 |   82.15 | ...91-198,258,260 
  TabBar.tsx       |   97.29 |    88.88 |     100 |   97.29 | 33                
  index.ts         |       0 |        0 |       0 |       0 | 1-12              
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...tensions/steps |   46.26 |       85 |   58.82 |   46.26 |                   
  ...ctionStep.tsx |   95.12 |    92.85 |   85.71 |   95.12 | 84-86,89          
  ...etailStep.tsx |       0 |        0 |       0 |       0 | 1-145             
  ...nListStep.tsx |   75.26 |    88.37 |   66.66 |   75.26 | ...53,174,203-209 
  ...electStep.tsx |       0 |        0 |       0 |       0 | 1-83              
  ...nfirmStep.tsx |   16.32 |      100 |       0 |   16.32 | 28-74             
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
 ...xtensions/tabs |   71.92 |    68.21 |   70.83 |   71.92 |                   
  DiscoverTab.tsx  |   68.22 |    67.66 |   55.55 |   68.22 | ...93,656-660,664 
  InstalledTab.tsx |   75.49 |    67.44 |   83.33 |   75.49 | ...77,782-783,820 
  SourcesTab.tsx   |   71.67 |    70.47 |   77.77 |   71.67 | ...28,547,621-633 
 ...tensions/views |    50.7 |    52.38 |   20.83 |    50.7 |                   
  ...tionsView.tsx |   73.75 |    56.36 |   66.66 |   73.75 | ...30,353,369-374 
  ...tionsView.tsx |   43.45 |    44.82 |    6.66 |   43.45 | ...98-405,408-420 
  ...etailView.tsx |    9.24 |      100 |       0 |    9.24 | 40-67,70-163      
 ...mponents/hooks |   87.11 |    81.37 |   91.89 |   87.11 |                   
  ...rListBody.tsx |   95.29 |    85.18 |     100 |   95.29 | 95-98             
  ...etailStep.tsx |   75.32 |    71.42 |      60 |   75.32 | ...56-169,173-186 
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entHeader.tsx |     100 |    85.71 |     100 |     100 | 47                
  ...rListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...abledStep.tsx |     100 |      100 |     100 |     100 |                   
  ...sListStep.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   72.29 |    70.49 |     100 |   72.29 | ...51,563-568,572 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
  ...erGrouping.ts |     100 |      100 |     100 |     100 |                   
  sourceLabels.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...components/mcp |   40.91 |    63.44 |   70.58 |   40.91 |                   
  ...ealthPill.tsx |     100 |      100 |     100 |     100 |                   
  ...entDialog.tsx |   32.09 |    26.19 |      40 |   32.09 | ...12,914,927-933 
  ...valDialog.tsx |   15.06 |      100 |       0 |   15.06 | 40-109            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-35              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |      97 |       95 |     100 |      97 | 24,113-114        
 ...ents/mcp/steps |   53.94 |    73.51 |   57.14 |   53.94 |                   
  ...icateStep.tsx |    5.65 |      100 |       0 |    5.65 | 40-66,69-308      
  ...electStep.tsx |   10.95 |      100 |       0 |   10.95 | 16-88             
  ...etailStep.tsx |     100 |      100 |     100 |     100 |                   
  ...eListStep.tsx |   99.09 |    97.36 |     100 |   99.09 | 71                
  ...etailStep.tsx |   62.83 |       60 |   33.33 |   62.83 | ...87-296,307-332 
  ...rListStep.tsx |   88.53 |    81.25 |     100 |   88.53 | ...64,170,175-180 
  ...etailStep.tsx |    10.3 |      100 |       0 |    10.3 | ...1,67-79,82-140 
  ToolListStep.tsx |   69.29 |       50 |     100 |   69.29 | ...23,126,135-144 
 ...nents/messages |   90.26 |    86.89 |   85.57 |   90.26 |                   
  ...ionDialog.tsx |   89.23 |     84.9 |   81.81 |   89.23 | ...75,593,611-613 
  BtwMessage.tsx   |     100 |      100 |     100 |     100 |                   
  ...upDisplay.tsx |     100 |    94.73 |     100 |     100 | ...43,289,402,432 
  ...onMessage.tsx |   92.06 |    82.35 |     100 |   92.06 | 58-60,62,64       
  ...nMessages.tsx |   94.11 |    95.91 |   76.92 |   94.11 | ...47-349,352-355 
  DiffRenderer.tsx |   93.17 |    86.02 |     100 |   93.17 | ...07,235-236,302 
  ...tsDisplay.tsx |   97.08 |    77.77 |     100 |   97.08 | 95,97,106         
  ...usMessage.tsx |   81.73 |     65.9 |      75 |   81.73 | ...10-214,222,245 
  ...tsDisplay.tsx |   95.52 |    88.31 |     100 |   95.52 | ...40,142,175-180 
  ...ssMessage.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...edMessage.tsx |   21.05 |      100 |       0 |   21.05 | 23-39             
  ...sMessages.tsx |   59.04 |       50 |    37.5 |   59.04 | ...21-126,147-159 
  ...ryMessage.tsx |   13.63 |      100 |       0 |   13.63 | 23-64             
  ...onMessage.tsx |   91.87 |    82.63 |     100 |   91.87 | ...49-651,658-660 
  ...upMessage.tsx |   98.38 |    95.38 |     100 |   98.38 | 188-191,422       
  ToolMessage.tsx  |   93.06 |    86.32 |   93.75 |   93.06 | ...1037,1082-1084 
 ...ponents/shared |   86.29 |    82.41 |   94.17 |   86.29 |                   
  ...ctionList.tsx |     100 |      100 |      75 |     100 |                   
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  EnumSelector.tsx |     100 |    96.42 |     100 |     100 | 58                
  ...rBoundary.tsx |     100 |      100 |     100 |     100 |                   
  MaxSizedBox.tsx  |   84.71 |    86.95 |      90 |   84.71 | ...67-568,685-686 
  MultiSelect.tsx  |   93.58 |       75 |     100 |   93.58 | ...43,199-201,211 
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...ontroller.tsx |     100 |    83.33 |     100 |     100 | 73,93-95          
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  ...lableList.tsx |   81.48 |    84.84 |     100 |   81.48 | 46-66,73-76       
  StaticRender.tsx |     100 |      100 |     100 |     100 |                   
  TextInput.tsx    |    80.8 |    67.24 |      80 |    80.8 | ...36-240,252-258 
  ...ontroller.tsx |     100 |    81.81 |     100 |     100 | 59-62             
  ...apsedTime.tsx |     100 |      100 |     100 |     100 |                   
  ...Indicator.tsx |     100 |      100 |     100 |     100 |                   
  ...lizedList.tsx |   91.49 |    86.66 |   83.33 |   91.49 | ...18-846,859,959 
  text-buffer.ts   |   85.98 |    81.81 |   97.91 |   85.98 | ...2664,2762-2763 
  ...er-actions.ts |   73.93 |    67.22 |     100 |   73.93 | ...32-733,934-936 
 ...ponents/skills |       0 |        0 |       0 |       0 |                   
  ...gerDialog.tsx |       0 |        0 |       0 |       0 | 1-681             
 ...ents/subagents |       0 |        0 |       0 |       0 |                   
  constants.ts     |       0 |        0 |       0 |       0 | 1-71              
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |       0 |        0 |       0 |       0 | 1-190             
  types.ts         |       0 |        0 |       0 |       0 | 1-125             
  utils.ts         |       0 |        0 |       0 |       0 | 1-102             
 ...bagents/create |       0 |        0 |       0 |       0 |                   
  ...ionWizard.tsx |       0 |        0 |       0 |       0 | 1-299             
  ...rSelector.tsx |       0 |        0 |       0 |       0 | 1-85              
  ...onSummary.tsx |       0 |        0 |       0 |       0 | 1-331             
  ...tionInput.tsx |       0 |        0 |       0 |       0 | 1-177             
  ...dSelector.tsx |       0 |        0 |       0 |       0 | 1-63              
  ...nSelector.tsx |       0 |        0 |       0 |       0 | 1-58              
  ...EntryStep.tsx |       0 |        0 |       0 |       0 | 1-78              
  ToolSelector.tsx |       0 |        0 |       0 |       0 | 1-253             
 ...bagents/manage |   14.14 |    53.19 |    37.5 |   14.14 |                   
  ...ctionStep.tsx |       0 |        0 |       0 |       0 | 1-103             
  ...eleteStep.tsx |       0 |        0 |       0 |       0 | 1-62              
  ...tEditStep.tsx |       0 |        0 |       0 |       0 | 1-124             
  ...ctionStep.tsx |   35.61 |    59.52 |     100 |   35.61 | ...21-433,438-440 
  ...iewerStep.tsx |       0 |        0 |       0 |       0 | 1-73              
  ...gerDialog.tsx |       0 |        0 |       0 |       0 | 1-341             
 ...mponents/views |    70.1 |    72.89 |   61.11 |    70.1 |                   
  ContextUsage.tsx |   71.49 |    64.86 |      80 |   71.49 | ...30-436,473-567 
  DoctorReport.tsx |     9.8 |      100 |       0 |     9.8 | 25-54,57-131      
  ...sionsList.tsx |   88.05 |       75 |     100 |   88.05 | 70-77             
  McpStatus.tsx    |   92.01 |     73.8 |     100 |   92.01 | ...36,175-177,262 
  SkillsList.tsx   |   20.51 |      100 |       0 |   20.51 | 17-20,27-57       
  ToolsList.tsx    |     100 |      100 |     100 |     100 |                   
 src/ui/contexts   |   84.16 |    81.83 |   85.13 |   84.16 |                   
  ...ewContext.tsx |   64.83 |    88.88 |      50 |   64.83 | ...16-219,225-235 
  AppContext.tsx   |      80 |       50 |     100 |      80 | 19-20             
  ...ewContext.tsx |   93.83 |    68.51 |   42.85 |   93.83 | ...44,281-285,317 
  ...igContext.tsx |   81.81 |       50 |     100 |   81.81 | 15-16             
  ...ssContext.tsx |   85.65 |    84.85 |     100 |   85.65 | ...1612-1614,1620 
  ...owContext.tsx |   91.07 |    81.81 |     100 |   91.07 | 47-48,60-62       
  ...deContext.tsx |     100 |      100 |      50 |     100 |                   
  ...onContext.tsx |   80.77 |       80 |    92.3 |   80.77 | ...31-434,443-446 
  ...gsContext.tsx |     100 |      100 |     100 |     100 |                   
  ...usContext.tsx |     100 |      100 |     100 |     100 |                   
  ...ngContext.tsx |   71.42 |       50 |     100 |   71.42 | 17-20             
  ...utContext.tsx |   85.71 |      100 |   66.66 |   85.71 | 13-14             
  ...edContext.tsx |     100 |      100 |      50 |     100 |                   
  ...nsContext.tsx |   88.88 |       50 |     100 |   88.88 | 156-157           
  ...teContext.tsx |   86.66 |       50 |     100 |   86.66 | 235-236           
  ...deContext.tsx |      80 |     87.5 |      75 |      80 | ...11-112,118-120 
  ...rtContext.tsx |     100 |      100 |     100 |     100 |                   
 src/ui/daemon     |   88.35 |    73.51 |   95.45 |   88.35 |                   
  ...ui-adapter.ts |   88.35 |    73.51 |   95.45 |   88.35 | ...74,792-793,879 
 src/ui/editors    |       0 |        0 |       0 |       0 |                   
  ...ngsManager.ts |       0 |        0 |       0 |       0 | 1-67              
 src/ui/hooks      |   85.48 |    83.02 |   87.79 |   85.48 |                   
  ...dProcessor.ts |   85.53 |    85.13 |     100 |   85.53 | ...-970,1017-1018 
  ...ention-ref.ts |   97.72 |       84 |     100 |   97.72 | 65                
  keyToAnsi.ts     |    3.92 |      100 |       0 |    3.92 | 19-77             
  ...esourceRef.ts |     100 |      100 |     100 |     100 |                   
  ...completion.ts |     100 |    95.45 |     100 |     100 | 95                
  ...ention-ref.ts |     100 |      100 |     100 |     100 |                   
  ...dProcessor.ts |   94.62 |    73.58 |     100 |   94.62 | ...87-288,293-294 
  ...dProcessor.ts |   85.75 |     68.4 |   81.81 |   85.75 | ...1464,1485-1489 
  ...rt-command.ts |     100 |      100 |     100 |     100 |                   
  ...sced-flush.ts |     100 |      100 |     100 |     100 |                   
  ...ng-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...oice-input.ts |   92.36 |    81.95 |   66.66 |   92.36 | ...00,502-503,658 
  ...ke-repaint.ts |     100 |      100 |     100 |     100 |                   
  ...amingState.ts |   12.22 |      100 |       0 |   12.22 | 54-157            
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...dScrollbar.ts |     100 |      100 |     100 |     100 |                   
  ...ationFrame.ts |      52 |    63.63 |     100 |      52 | ...59,67-70,76-87 
  ...odeCommand.ts |   58.82 |      100 |     100 |   58.82 | 28,33-48          
  ...enaCommand.ts |      85 |      100 |     100 |      85 | 23-24,29          
  ...aInProcess.ts |   27.92 |       80 |      25 |   27.92 | ...69-170,173-175 
  ...Completion.ts |   86.44 |    88.48 |     100 |   86.44 | ...14-515,525-541 
  ...ifications.ts |   87.82 |    96.77 |     100 |   87.82 | 138-152           
  ...tIndicator.ts |   88.28 |    81.57 |     100 |   88.28 | ...66,175,179-187 
  ...waySummary.ts |   96.26 |       75 |     100 |   96.26 | 126-128,170       
  ...ndTaskView.ts |   94.89 |    77.55 |     100 |   94.89 | 164-168,257,263   
  ...chedScroll.ts |     100 |      100 |     100 |     100 |                   
  ...ketedPaste.ts |    23.8 |      100 |       0 |    23.8 | 19-37             
  ...nchCommand.ts |   95.53 |    83.01 |     100 |   95.53 | ...64-165,289-292 
  ...ompletion.tsx |   97.09 |    87.09 |     100 |   97.09 | ...23-324,334-335 
  ...dMigration.ts |    92.1 |    88.88 |     100 |    92.1 | 42-44             
  useCompletion.ts |   96.29 |    90.56 |     100 |   96.29 | ...17-218,222-223 
  ...nitMessage.ts |     100 |      100 |     100 |     100 |                   
  ...extualTips.ts |   78.26 |       50 |     100 |   78.26 | ...2,75-79,96-104 
  ...eteCommand.ts |   89.52 |    90.69 |     100 |   89.52 | ...98-106,114-115 
  ...ialogClose.ts |   36.11 |       10 |     100 |   36.11 | ...89-195,202-207 
  useDiffData.ts   |       0 |        0 |       0 |       0 | 1-87              
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |    97.67 |     100 |   99.12 | 182-183           
  ...ionUpdates.ts |   93.72 |    92.98 |     100 |   93.72 | ...87-291,314-320 
  ...agerDialog.ts |   88.88 |      100 |     100 |   88.88 | 21,25             
  ...backDialog.ts |    63.9 |    76.47 |   66.66 |    63.9 | ...66-168,190-191 
  useFocus.ts      |     100 |      100 |     100 |     100 |                   
  ...olderTrust.ts |     100 |    93.33 |     100 |     100 | 62                
  ...ggestions.tsx |   96.47 |    78.94 |     100 |   96.47 | 121,155-156       
  ...miniStream.ts |   86.08 |    81.23 |   76.92 |   86.08 | ...5198-5200,5202 
  ...BranchName.ts |     100 |    94.44 |     100 |     100 | 54                
  ...oryManager.ts |   98.38 |    98.85 |     100 |   98.38 | 141-144           
  ...ooksDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...stListener.ts |     100 |      100 |     100 |     100 |                   
  ...nAuthError.ts |   76.19 |       50 |     100 |   76.19 | 39-40,43-45       
  ...putHistory.ts |   92.59 |    85.71 |     100 |   92.59 | 63-64,72,94-96    
  ...storyStore.ts |     100 |    94.11 |     100 |     100 | 69                
  useKeypress.ts   |     100 |      100 |     100 |     100 |                   
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...unchEditor.ts |   22.58 |      100 |      50 |   22.58 | 11-32,44-85       
  ...gIndicator.ts |     100 |    96.66 |     100 |     100 | 109               
  useLogger.ts     |      16 |      100 |       0 |      16 | 15-45             
  useMCPHealth.ts  |   10.52 |      100 |       0 |   10.52 | 36-75             
  ...cpApproval.ts |   93.12 |    86.11 |     100 |   93.12 | ...24-127,139-140 
  useMcpDialog.ts  |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...moryDialog.ts |    87.5 |      100 |     100 |    87.5 | 19,23             
  ...oryMonitor.ts |   83.14 |    78.57 |     100 |   83.14 | 54-63,74-79       
  ...ssageQueue.ts |     100 |     97.4 |     100 |     100 | 175,262           
  ...delCommand.ts |     100 |       96 |     100 |     100 | 61                
  ...ouseEvents.ts |   94.89 |       95 |   83.33 |   94.89 | 78-82             
  ...raseCycler.ts |   84.74 |    76.47 |     100 |   84.74 | ...49,52-53,69-71 
  ...rredEditor.ts |   58.33 |    22.22 |     100 |   58.33 | 23-27,29-33       
  ...derUpdates.ts |   85.29 |    80.28 |    92.3 |   85.29 | ...36,351-361,441 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |   89.48 |    88.88 |     100 |   89.48 | ...54-456,489-499 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   95.34 |    77.14 |     100 |   95.34 | 124-125,227-232   
  ...ompletion.tsx |   90.67 |    83.33 |     100 |   90.67 | ...02,105,138-141 
  ...ectionList.ts |   97.12 |    96.26 |     100 |   97.12 | ...92-193,247-250 
  ...sionPicker.ts |   92.87 |    90.35 |     100 |   92.87 | ...99-501,503-505 
  ...earchInput.ts |     100 |    97.29 |     100 |     100 | 82                
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |   93.28 |    80.95 |     100 |   93.28 | ...96,153-154,164 
  ...oryCommand.ts |   85.48 |    58.33 |     100 |   85.48 | 22-28,40,71       
  ...agerDialog.ts |   88.23 |      100 |     100 |   88.23 | 20,24             
  ...Completion.ts |   82.85 |    85.13 |   94.73 |   82.85 | ...78-680,688-724 
  ...tateAndRef.ts |     100 |      100 |     100 |     100 |                   
  ...tatsDialog.ts |     100 |      100 |     100 |     100 |                   
  useStatusLine.ts |   97.32 |    93.93 |     100 |   97.32 | ...18-422,518-525 
  ...eateDialog.ts |   88.23 |      100 |     100 |   88.23 | 14,18             
  ...mInProcess.ts |   27.35 |       80 |      25 |   27.35 | ...82-183,186-188 
  ...tification.ts |     100 |     87.5 |     100 |     100 | 50                
  ...alProgress.ts |   67.34 |    58.82 |   66.66 |   67.34 | 52-53,61-68,79-85 
  ...rminalSize.ts |     100 |      100 |     100 |     100 |                   
  ...emeCommand.ts |   67.01 |    29.41 |     100 |   67.01 | ...10-111,115-116 
  useTimer.ts      |   97.59 |    94.73 |     100 |   97.59 | 17-18             
  ...lMigration.ts |       0 |        0 |       0 |       0 |                   
  ...rustModify.ts |     100 |    90.47 |     100 |     100 | 112,134           
  useTurnDiffs.ts  |   95.12 |    78.57 |     100 |   95.12 | 133-134,156-157   
  ...elcomeBack.ts |   87.36 |     90.9 |     100 |   87.36 | ...,94-96,114-115 
  ...reeSession.ts |   93.75 |       70 |     100 |   93.75 | 47-48,72          
  vim.ts           |      74 |    67.56 |   69.23 |      74 | ...1854-1861,1869 
 src/ui/layouts    |    91.2 |    89.47 |     100 |    91.2 |                   
  ...AppLayout.tsx |    90.9 |     87.5 |     100 |    90.9 | 60-62,110-115,151 
  ...AppLayout.tsx |   91.66 |    92.85 |     100 |   91.66 | 75-80             
 src/ui/models     |   80.72 |       80 |   71.42 |   80.72 |                   
  ...ableModels.ts |   80.72 |       80 |   71.42 |   80.72 | ...,61-71,125-127 
 ...noninteractive |     100 |      100 |    6.66 |     100 |                   
  ...eractiveUi.ts |     100 |      100 |    6.66 |     100 |                   
 src/ui/selection  |   92.65 |    84.69 |     100 |   92.65 |                   
  screen-buffer.ts |   94.73 |    64.28 |     100 |   94.73 | 51-52             
  ...ion-coords.ts |     100 |      100 |     100 |     100 |                   
  ...ction-span.ts |   93.75 |    91.66 |     100 |   93.75 | 41-42,71-72       
  ...tion-state.ts |     100 |      100 |     100 |     100 |                   
  ...ction-text.ts |   92.85 |    92.59 |     100 |   92.85 | 30-34,114-115     
  ...selection.tsx |   90.26 |    75.32 |     100 |   90.26 | ...75-376,392-393 
 src/ui/state      |      95 |    81.81 |     100 |      95 |                   
  extensions.ts    |      95 |    81.81 |     100 |      95 | 69-70,89          
 src/ui/themes     |    98.5 |    73.17 |     100 |    98.5 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |   99.23 |    97.05 |     100 |   99.23 | 277-278           
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |    85.96 |     100 |   88.59 | ...57-261,266-270 
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  qwen-dark.ts     |     100 |      100 |     100 |     100 |                   
  qwen-light.ts    |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-manager.ts |   88.68 |    84.52 |     100 |   88.68 | ...83-392,397-398 
  theme.ts         |     100 |    38.02 |     100 |     100 | ...34-449,457-461 
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   87.56 |    85.64 |   95.76 |   87.56 |                   
  ...Colorizer.tsx |   80.31 |    85.41 |     100 |   80.31 | ...00-201,313-339 
  ...nRenderer.tsx |   80.07 |     75.6 |     100 |   80.07 | ...70,274,332-333 
  ...wnDisplay.tsx |   92.87 |    93.46 |     100 |   92.87 | ...,955,1002-1020 
  ...idDiagram.tsx |   87.79 |    95.34 |     100 |   87.79 | 156-179           
  ...eRenderer.tsx |   93.63 |    81.77 |   95.23 |   93.63 | ...47-750,803-808 
  ...odeDisplay.ts |   94.28 |    85.71 |     100 |   94.28 | 23,40             
  asciiCharts.ts   |    96.7 |     87.5 |     100 |    96.7 | 170-177,278       
  ...dWorkUtils.ts |     100 |      100 |     100 |     100 |                   
  ...boardUtils.ts |    52.9 |    74.15 |    92.3 |    52.9 | ...29,632-641,644 
  commandUtils.ts  |   98.38 |    92.38 |     100 |   98.38 | 108,136-137,343   
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  customBanner.ts  |   90.68 |    91.22 |     100 |   90.68 | ...13,324-327,334 
  displayUtils.ts  |   73.84 |    73.91 |     100 |   73.84 | ...34,36-40,42-46 
  formatters.ts    |   94.87 |    98.24 |     100 |   94.87 | 116-119           
  goal-runtime.ts  |   91.42 |       95 |     100 |   91.42 | 32-34             
  gradientUtils.ts |     100 |      100 |     100 |     100 |                   
  highlight.ts     |     100 |      100 |     100 |     100 |                   
  ...gap-notice.ts |     100 |      100 |     100 |     100 |                   
  ...oryMapping.ts |     100 |       95 |     100 |     100 | 44,103            
  historyUtils.ts  |   96.03 |     97.1 |     100 |   96.03 | 103-106           
  ...mage-parts.ts |   97.75 |    94.87 |     100 |   97.75 | 82-83             
  inline-math.ts   |   98.48 |    95.23 |     100 |   98.48 | 129-130           
  input-mouse.ts   |     100 |    85.71 |     100 |     100 | 48,93             
  isNarrowWidth.ts |     100 |      100 |     100 |     100 |                   
  ...olDetector.ts |   68.81 |       75 |   66.66 |   68.81 | ...27-132,160-161 
  latexRenderer.ts |   94.95 |     73.8 |     100 |   94.95 | ...76-178,184-187 
  layoutUtils.ts   |     100 |      100 |     100 |     100 |                   
  list-mouse.ts    |     100 |      100 |     100 |     100 |                   
  ...ightLoader.ts |     100 |       95 |     100 |     100 | 81                
  ...nUtilities.ts |   98.72 |    94.36 |     100 |   98.72 | 145-146           
  ...t-position.ts |     100 |     87.5 |     100 |     100 | 85                
  ...geRenderer.ts |   86.51 |    70.16 |   95.12 |   86.51 | ...1286,1326-1332 
  ...alRenderer.ts |   86.69 |     71.9 |     100 |   86.69 | ...1476,1513-1519 
  ...lsBySource.ts |     100 |    95.23 |     100 |     100 | 84                
  mouse.ts         |   92.85 |    74.19 |     100 |   92.85 | ...38,145,149-152 
  osc8.ts          |   91.33 |    79.03 |     100 |   91.33 | ...73,273,277-278 
  ...red-height.ts |   98.38 |    97.14 |     100 |   98.38 | 195-197           
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  restoreGoal.ts   |     100 |      100 |     100 |     100 |                   
  ...storyUtils.ts |   82.73 |    79.48 |     100 |   82.73 | ...84-606,737-738 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...evel-label.ts |   77.77 |    66.66 |     100 |   77.77 | 18,22-24          
  ...are-cursor.ts |   89.47 |    85.71 |     100 |   89.47 | 39-44             
  ...ataService.ts |   93.17 |     79.1 |     100 |   93.17 | ...14,227,254-256 
  suggestions.ts   |     100 |      100 |     100 |     100 |                   
  ...izedOutput.ts |   95.19 |      100 |   88.88 |   95.19 | 121-126           
  ...nal-buffer.ts |     100 |      100 |     100 |     100 |                   
  ...e-renderer.ts |   90.61 |    83.44 |     100 |   90.61 | ...80,482-484,607 
  ...ize-reflow.ts |     100 |     92.3 |     100 |     100 | 57,62,209,217,347 
  ...wOptimizer.ts |     100 |    94.11 |     100 |     100 | 33,76             
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   97.94 |    95.49 |   94.11 |   97.94 | ...82-283,443-444 
  ...background.ts |     100 |      100 |     100 |     100 |                   
  todoSnapshot.ts  |   90.42 |    92.85 |     100 |   90.42 | ...06-207,240-241 
  ...isplay-map.ts |     100 |      100 |     100 |     100 |                   
  updateCheck.ts   |     100 |    92.75 |     100 |     100 | 227-239,331       
  ...ow-keyword.ts |     100 |      100 |     100 |     100 |                   
 ...i/utils/export |   75.03 |     60.3 |   94.59 |   75.03 |                   
  collect.ts       |   71.27 |    66.38 |      96 |   71.27 | ...90-633,655-656 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   80.42 |    50.68 |     100 |   80.42 | ...59-364,376-378 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
  utils.ts         |     100 |      100 |     100 |     100 |                   
 ...ort/formatters |   52.92 |    47.22 |   71.42 |   52.92 |                   
  html.ts          |   84.61 |       50 |     100 |   84.61 | ...53,57-58,62-63 
  json.ts          |     100 |      100 |     100 |     100 |                   
  jsonl.ts         |   82.45 |     37.5 |     100 |   82.45 | ...48,50-51,65-66 
  markdown.ts      |   36.32 |    47.05 |      50 |   36.32 | ...16-219,233-295 
 src/ui/voice      |   81.27 |    79.92 |   81.94 |   81.27 |                   
  ...d-recorder.ts |     6.2 |      100 |       0 |     6.2 | ...33-159,162-163 
  ...o-recorder.ts |   84.61 |    93.33 |   57.14 |   84.61 | ...16-117,131-136 
  ...me-session.ts |   91.09 |     92.1 |     100 |   91.09 | ...99,305,316-319 
  sox-recorder.ts  |    92.7 |    71.87 |     100 |    92.7 | ...34-135,153-154 
  ...ailability.ts |     100 |      100 |     100 |     100 |                   
  ...e-keyterms.ts |     100 |      100 |     100 |     100 |                   
  voice-model.ts   |     100 |      100 |     100 |     100 |                   
  ...e-recorder.ts |   88.29 |    67.74 |   81.81 |   88.29 | ...,98-99,112,115 
  voice-refine.ts  |     100 |    93.33 |     100 |     100 | 92                
  ...ream-retry.ts |   86.79 |       70 |     100 |   86.79 | 16-18,48-49,59-60 
  ...am-session.ts |   88.02 |    66.66 |   84.61 |   88.02 | ...26,343-345,363 
  ...ranscriber.ts |     100 |      100 |     100 |     100 |                   
 src/utils         |   81.51 |    87.07 |   92.57 |   81.51 |                   
  ...p-profiler.ts |   98.39 |    92.59 |     100 |   98.39 | 141,185,235       
  acpModelUtils.ts |   97.36 |    95.19 |     100 |   97.36 | ...09-210,214-215 
  apiPreconnect.ts |   96.74 |    94.59 |     100 |   96.74 | 167-170           
  ...ol-call-id.ts |   84.61 |       60 |     100 |   84.61 | 26-27,37-38       
  ...ng-failure.ts |     100 |       95 |     100 |     100 | 72                
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  ...-api-error.ts |     100 |    96.42 |     100 |     100 | 14                
  cleanup.ts       |   84.05 |    94.11 |      80 |   84.05 | 80,111-121        
  commands.ts      |   97.45 |    96.66 |     100 |   97.45 | 153-155           
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  cpuProfiler.ts   |   70.73 |    73.23 |   88.88 |   70.73 | ...27,430-431,438 
  deepMerge.ts     |     100 |    89.65 |     100 |     100 | 41-43,49          
  ...re-runtime.ts |     100 |      100 |     100 |     100 |                   
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  doctorChecks.ts  |   70.31 |    74.57 |     100 |   70.31 | ...95-301,325-341 
  ...putCapture.ts |   90.65 |    86.31 |     100 |   90.65 | ...73,371,373-374 
  ...arResolver.ts |   97.14 |    96.55 |     100 |   97.14 | 125-126           
  errors.ts        |   97.56 |    94.64 |     100 |   97.56 | 69-70,304-305     
  events.ts        |     100 |      100 |     100 |     100 |                   
  ...on-mention.ts |   88.48 |     82.6 |     100 |   88.48 | ...56-160,164-168 
  gitUtils.ts      |   92.85 |    86.66 |     100 |   92.85 | ...13-116,164-167 
  ...AutoUpdate.ts |    93.1 |       94 |      90 |    93.1 | 103,108,179-190   
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |   97.68 |    94.28 |     100 |   97.68 | ...64,381-382,427 
  jsonc-editor.ts  |   93.18 |    92.66 |     100 |   93.18 | ...80-381,384-385 
  languageUtils.ts |   98.88 |    97.05 |     100 |   98.88 | 184-185           
  load-undici.ts   |     100 |      100 |     100 |     100 |                   
  ...npm-update.ts |   89.31 |    77.33 |     100 |   89.31 | ...87,303-304,344 
  math.ts          |       0 |        0 |       0 |       0 | 1-15              
  ...er-mention.ts |     100 |    66.66 |     100 |     100 | 14,30,44-46       
  ...iagnostics.ts |   94.57 |    83.01 |   88.88 |   94.57 | ...05,311,315-317 
  ...serMessage.ts |     100 |      100 |     100 |     100 |                   
  ...onfigUtils.ts |   94.25 |    91.17 |     100 |   94.25 | ...30,436,439-443 
  ...iveHelpers.ts |   95.13 |    91.79 |     100 |   95.13 | ...53-454,552,565 
  osc.ts           |   97.18 |      100 |    87.5 |   97.18 | 182-183           
  package.ts       |   88.88 |    85.71 |     100 |   88.88 | 31-32             
  ...uggestions.ts |   84.29 |    70.83 |     100 |   84.29 | 70-76,92-103      
  processUtils.ts  |    92.3 |       80 |     100 |    92.3 | 45-46             
  readStdin.ts     |   93.67 |    94.11 |   85.71 |   93.67 | 79-83             
  relaunch.ts      |   95.87 |    89.28 |     100 |   95.87 | 103-105,131       
  resolvePath.ts   |     100 |      100 |     100 |     100 |                   
  runBudget.ts     |   99.35 |    96.77 |     100 |   99.35 | 119               
  sandbox-path.ts  |     100 |      100 |     100 |     100 |                   
  sandbox.ts       |   45.52 |    57.35 |   76.92 |   45.52 | ...1040,1052-1075 
  ...xImageName.ts |     100 |    77.77 |     100 |     100 | 10,18             
  sandboxMounts.ts |     100 |      100 |     100 |     100 |                   
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  settingsUtils.ts |   82.35 |    89.57 |      90 |   82.35 | ...25-743,750-758 
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...ate-verify.ts |     100 |      100 |     100 |     100 |                   
  ...one-update.ts |   39.81 |    77.44 |   62.16 |   39.81 | ...1193,1196-1215 
  ...upProfiler.ts |   98.47 |    94.66 |     100 |   98.47 | 132-133,308       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |     100 |       90 |     100 |     100 | 23                
  systemInfo.ts    |   95.12 |    90.27 |     100 |   95.12 | ...54-255,260-264 
  ...InfoFields.ts |    87.5 |    65.85 |     100 |    87.5 | ...24-125,146-147 
  ...alSequence.ts |     100 |    97.61 |     100 |     100 | 60                
  ...iffPreview.ts |   76.47 |       25 |     100 |   76.47 | 13,17,23-24       
  ...on-handler.ts |    73.8 |       75 |     100 |    73.8 | 17-18,25-26,67-73 
  ...e-relaunch.ts |   89.61 |    86.66 |      50 |   89.61 | 56-61,83-84       
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...ansionHook.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |   87.75 |       75 |     100 |   87.75 | 47-48,53-54,57-58 
  version.ts       |     100 |    66.66 |     100 |     100 | 11                
  ...ingHandler.ts |     100 |      100 |     100 |     100 |                   
  windowTitle.ts   |   95.45 |    93.33 |     100 |   95.45 | 54-55             
  ...WithBackup.ts |   65.04 |    77.77 |     100 |   65.04 | 97,112,133-172    
 ...s/housekeeping |   93.51 |    90.95 |   96.96 |   93.51 |                   
  cleanup.ts       |   92.59 |    93.75 |     100 |   92.59 | ...02-205,209-211 
  ...eractionAt.ts |     100 |      100 |     100 |     100 |                   
  scheduler.ts     |      93 |    88.34 |      95 |      93 | ...57-359,411-415 
  throttledOnce.ts |   95.95 |    93.93 |     100 |   95.95 | 77-78,153-154     
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   87.88 |    86.37 |   89.45 |   87.88 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/agents        |   90.38 |    84.54 |   94.85 |   90.38 |                   
  ...transcript.ts |   87.63 |    83.52 |     100 |   87.63 | ...80,588,594-598 
  ...ent-resume.ts |   85.59 |    77.55 |   83.33 |   85.59 | ...1793-1797,1800 
  ...ound-tasks.ts |   94.63 |    90.13 |   96.38 |   94.63 | ...1773,1793-1796 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...ent-result.ts |    96.8 |    92.68 |     100 |    96.8 | 106,129-131       
  ...n-registry.ts |   94.79 |     87.7 |     100 |   94.79 | ...1067,1081-1083 
  ...w-snapshot.ts |   92.12 |    77.14 |     100 |   92.12 | ...65,189,196-198 
 src/agents/arena  |   76.94 |    68.22 |   78.94 |   76.94 |                   
  ...gentClient.ts |   79.47 |    88.88 |   81.81 |   79.47 | ...68-183,189-204 
  ArenaManager.ts  |   75.89 |     65.2 |   78.57 |   75.89 | ...1887,1893-1894 
  arena-events.ts  |   64.44 |      100 |      50 |   64.44 | ...71-175,178-183 
  diff-summary.ts  |    87.5 |    72.34 |     100 |    87.5 | ...32-133,137-138 
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...gents/backends |   78.09 |    85.23 |   76.28 |   78.09 |                   
  ITermBackend.ts  |   97.97 |    93.93 |     100 |   97.97 | ...78-180,255,307 
  ...essBackend.ts |    90.9 |    85.36 |   93.33 |    90.9 | ...70,672,674-675 
  TmuxBackend.ts   |    90.7 |    76.55 |   97.36 |    90.7 | ...87,697,743-747 
  detect.ts        |   31.25 |      100 |       0 |   31.25 | 34-88             
  index.ts         |     100 |      100 |     100 |     100 |                   
  iterm-it2.ts     |     100 |     92.1 |     100 |     100 | 37-38,106         
  tmux-commands.ts |    6.64 |      100 |    3.03 |    6.64 | ...93-363,386-503 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...agents/runtime |    91.1 |    86.68 |   89.23 |    91.1 |                   
  agent-context.ts |     100 |      100 |     100 |     100 |                   
  agent-core.ts    |   85.07 |     76.8 |   77.77 |   85.07 | ...2291,2337-2339 
  agent-events.ts  |     100 |      100 |     100 |     100 |                   
  ...t-headless.ts |   93.49 |    89.41 |   83.33 |   93.49 | ...96-497,500-501 
  ...nteractive.ts |   81.01 |    82.35 |   76.66 |   81.01 | ...33,535-538,541 
  ...statistics.ts |   98.29 |    82.55 |     100 |   98.29 | 141,165,206,239   
  agent-types.ts   |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...ool-policy.ts |   98.34 |      100 |    92.3 |   98.34 | 81-82             
  ...low-budget.ts |     100 |      100 |     100 |     100 |                   
  ...-scheduler.ts |   97.43 |    96.36 |     100 |   97.43 | 128-130           
  ...ow-journal.ts |   91.76 |    75.86 |     100 |   91.76 | ...38-139,179-181 
  ...chestrator.ts |    92.4 |       90 |   83.78 |    92.4 | ...1862,1911-1914 
  ...ow-prompts.ts |     100 |      100 |     100 |     100 |                   
  ...low-runner.ts |   94.85 |     87.5 |   92.85 |   94.85 | ...93,260,280-283 
  ...ow-sandbox.ts |   96.85 |    91.28 |     100 |   96.85 | ...1705,1711-1712 
  ...flow-saved.ts |   96.51 |    94.36 |     100 |   96.51 | 134-135,234-237   
  ...flow-stall.ts |    97.9 |    83.33 |     100 |    97.9 | 138-139,236       
 src/agents/tasks  |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/agents/team   |   82.04 |    84.17 |   88.97 |   82.04 |                   
  TeamManager.ts   |   72.02 |    79.41 |   79.24 |   72.02 | ...1632,1655-1656 
  identity.ts      |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...sionBridge.ts |     100 |      100 |     100 |     100 |                   
  mailbox.ts       |   96.02 |    87.23 |     100 |   96.02 | 352-358           
  ...ptAddendum.ts |     100 |      100 |     100 |     100 |                   
  tasks.ts         |   89.24 |    82.82 |     100 |   89.24 | ...-994,1038-1039 
  team-events.ts   |   60.52 |      100 |      50 |   60.52 | ...40-144,151-155 
  teamHelpers.ts   |   92.02 |    94.91 |   95.23 |   92.02 | ...31-332,368-378 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...eam/test-utils |   94.39 |    94.26 |   98.21 |   94.39 |                   
  ...on-harness.ts |   96.49 |    84.21 |     100 |   96.49 | 128-129,141-142   
  fake-agent.ts    |   98.49 |    95.08 |     100 |   98.49 | 201-203           
  fake-backend.ts  |   86.46 |    97.61 |   95.83 |   86.46 | 124-146           
 src/config        |   84.78 |    87.15 |   75.42 |   84.78 |                   
  approval-mode.ts |     100 |      100 |     100 |     100 |                   
  ...xtDefaults.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |   84.08 |    86.88 |   73.86 |   84.08 | ...8427,8431-8432 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  storage.ts       |   94.39 |    91.57 |   88.23 |   94.39 | ...45-446,449-450 
 ...nfirmation-bus |   98.27 |    97.14 |     100 |   98.27 |                   
  message-bus.ts   |   98.14 |    97.05 |     100 |   98.14 | 42-43             
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |   92.37 |    88.09 |   93.29 |   92.37 |                   
  baseLlmClient.ts |    88.4 |     83.8 |   81.81 |    88.4 | ...59,672,678-680 
  client.ts        |   92.05 |     87.4 |   91.66 |   92.05 | ...3987,4085-4086 
  ...tGenerator.ts |   86.34 |    87.34 |   84.61 |   86.34 | ...96-497,542-548 
  ...lScheduler.ts |   90.05 |    84.67 |   96.15 |   90.05 | ...6217,6245-6261 
  geminiChat.ts    |    94.7 |    90.12 |   95.53 |    94.7 | ...5052,5100-5101 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  genai-compat.ts  |     100 |      100 |     100 |     100 |                   
  ...MediaLimit.ts |     100 |       96 |     100 |     100 | 96                
  ...htProtocol.ts |    9.09 |      100 |       0 |    9.09 | ...9,62-66,69-110 
  ...ream-error.ts |     100 |      100 |     100 |     100 |                   
  logger.ts        |   87.41 |    87.02 |     100 |   87.41 | ...64-568,614-628 
  ...lay-buffer.ts |     100 |      100 |     100 |     100 |                   
  ...dispatcher.ts |     100 |      100 |     100 |     100 |                   
  ...tyDefaults.ts |     100 |      100 |     100 |     100 |                   
  ...olExecutor.ts |   93.54 |    83.33 |      50 |   93.54 | 49-50             
  ...on-helpers.ts |   93.49 |    78.57 |     100 |   93.49 | ...10-211,228-229 
  ...issionFlow.ts |   98.97 |    96.96 |     100 |   98.97 | 107               
  ...try-policy.ts |     100 |      100 |     100 |     100 |                   
  ...ell-policy.ts |   94.89 |    88.54 |     100 |   94.89 | ...51-252,297-298 
  prompts.ts       |   93.64 |    91.42 |   83.33 |   93.64 | ...1209,1412-1413 
  ...ing-effort.ts |     100 |      100 |     100 |     100 |                   
  ...n-recovery.ts |   95.13 |       80 |     100 |   95.13 | ...06-107,142-144 
  ...t-profiler.ts |    97.9 |    81.15 |   88.23 |    97.9 | 117,124-125,130   
  ...port-retry.ts |     100 |      100 |     100 |     100 |                   
  tokenLimits.ts   |     100 |     92.1 |     100 |     100 | 87,122-139        
  ...reparation.ts |     100 |      100 |     100 |     100 |                   
  ...tion-guard.ts |   90.38 |    94.73 |     100 |   90.38 | 83-87             
  ...allIdUtils.ts |   98.41 |    93.47 |     100 |   98.41 | 36,45             
  ...okTriggers.ts |   99.45 |    92.43 |     100 |   99.45 | 182,193           
  ...terruption.ts |     100 |     92.3 |     100 |     100 | 86,104            
  turn.ts          |   98.67 |    93.12 |     100 |   98.67 | ...79,707-708,755 
  ...l-fallback.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   96.33 |    88.12 |   96.15 |   96.33 |                   
  ...tGenerator.ts |   97.24 |    86.72 |   94.87 |   97.24 | ...1429,1458,1469 
  converter.ts     |   96.19 |    89.25 |     100 |   96.19 | ...1329,1550-1552 
  index.ts         |       0 |        0 |       0 |       0 | 1-21              
  usage.ts         |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   88.78 |    72.36 |   89.47 |   88.78 |                   
  ...tGenerator.ts |   87.18 |    71.83 |   88.88 |   87.18 | ...58-364,382-383 
  index.ts         |     100 |       80 |     100 |     100 | 50                
 ...ntentGenerator |    95.6 |    88.74 |    92.3 |    95.6 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   95.52 |    87.88 |   91.89 |   95.52 | ...1195-1196,1224 
  ...tDetection.ts |     100 |      100 |     100 |     100 |                   
 ...ntentGenerator |   91.64 |    90.46 |   95.61 |   91.64 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  converter.ts     |   91.15 |    89.32 |   96.87 |   91.15 | ...1914,2083-2098 
  errorHandler.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |   60.31 |       75 |      50 |   60.31 | ...71,74-78,90-94 
  ...tGenerator.ts |    66.4 |    70.58 |   88.88 |    66.4 | ...51-157,168-169 
  pipeline.ts      |   95.48 |    91.27 |     100 |   95.48 | ...1309,1317,1416 
  ...ix-caching.ts |   95.23 |    92.85 |     100 |   95.23 | 45-46,69-70       
  ...ureContext.ts |     100 |      100 |     100 |     100 |                   
  ...ingOptions.ts |       0 |        0 |       0 |       0 | 1                 
  ...CallParser.ts |   92.24 |     92.4 |     100 |   92.24 | ...28-529,549-552 
  ...kingParser.ts |     100 |    96.87 |     100 |     100 | 42                
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...rator/provider |   97.36 |    92.19 |    98.5 |   97.36 |                   
  dashscope.ts     |   98.33 |       95 |   96.55 |   98.33 | ...97-698,840-841 
  deepseek.ts      |   94.91 |    89.36 |     100 |   94.91 | ...31-132,145-146 
  default.ts       |   99.16 |    96.96 |     100 |   99.16 | 198               
  index.ts         |     100 |      100 |     100 |     100 |                   
  mimo.ts          |   94.11 |    66.66 |     100 |   94.11 | 29,52-53          
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  mistral.ts       |   96.07 |    73.33 |     100 |   96.07 | 32-33             
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
  utils.ts         |     100 |      100 |     100 |     100 |                   
  zai.ts           |   92.13 |    82.14 |     100 |   92.13 | ...,39-40,135-137 
 src/extension     |   87.71 |    84.62 |   92.57 |   87.71 |                   
  ...ive-safety.ts |     100 |      100 |     100 |     100 |                   
  ...-converter.ts |   80.55 |    73.66 |     100 |   80.55 | ...1133,1179-1180 
  corruptFile.ts   |     100 |       50 |     100 |     100 | 40-45             
  ...-converter.ts |     100 |      100 |     100 |     100 |                   
  ...me-refresh.ts |     100 |      100 |     100 |     100 |                   
  ...sion-store.ts |   90.94 |    86.26 |   97.91 |   90.94 | ...1230-1236,1280 
  ...ionManager.ts |   83.89 |    82.86 |   81.72 |   83.89 | ...2832,2861-2862 
  ...references.ts |     100 |     90.9 |     100 |     100 | ...05,129,197,200 
  ...onSettings.ts |    92.3 |     94.4 |     100 |    92.3 | ...98-501,570-571 
  ...-converter.ts |    75.9 |    85.71 |   85.71 |    75.9 | ...98,202,214-248 
  github.ts        |   90.48 |    82.71 |     100 |   90.48 | ...4,994-995,1005 
  http-client.ts   |   84.61 |       80 |     100 |   84.61 | 20-21             
  i18n.ts          |   78.26 |       96 |      50 |   78.26 | 104-110,116-123   
  index.ts         |     100 |      100 |     100 |     100 |                   
  marketplace.ts   |   88.39 |    83.11 |     100 |   88.39 | ...08,494,507-508 
  ...ork-policy.ts |   89.72 |       90 |     100 |   89.72 | ...36,148-154,156 
  npm.ts           |   89.02 |    81.81 |     100 |   89.02 | ...86-688,695-700 
  override.ts      |   94.11 |    93.33 |     100 |   94.11 | 63-64,81-82       
  ...-converter.ts |   94.89 |    90.41 |     100 |   94.89 | ...50-151,222-224 
  redaction.ts     |     100 |      100 |     100 |     100 |                   
  settings.ts      |   66.26 |      100 |      50 |   66.26 | 81-107,141-146    
  ...ceRegistry.ts |   94.01 |    83.14 |     100 |   94.01 | ...38-344,365-366 
  storage.ts       |     100 |      100 |     100 |     100 |                   
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   88.95 |    84.21 |     100 |   88.95 | ...32-235,238-241 
  ...extraction.ts |   85.77 |       81 |   89.47 |   85.77 | ...02-205,260-261 
 ...ent-plugins-v1 |   84.94 |    79.51 |     100 |   84.94 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  manifest.ts      |   81.87 |    84.48 |     100 |   81.87 | ...55-156,161-174 
  mcp.ts           |   84.98 |    79.56 |     100 |   84.98 | ...88-389,419-420 
  paths.ts         |     100 |    94.44 |     100 |     100 | 59                
  skills.ts        |   82.31 |    63.88 |     100 |   82.31 | ...38-141,150-151 
 src/followup      |   79.94 |    79.28 |    90.9 |   79.94 |                   
  followupState.ts |   98.44 |    95.74 |     100 |   98.44 | 236-237           
  index.ts         |     100 |      100 |     100 |     100 |                   
  overlayFs.ts     |   96.29 |    88.88 |     100 |   96.29 | 78,108,122        
  speculation.ts   |   71.88 |    65.71 |   71.42 |   71.88 | ...55-656,663-664 
  ...onToolGate.ts |   97.97 |     87.5 |     100 |   97.97 | 105,110           
  ...nGenerator.ts |   72.03 |    81.15 |   83.33 |   72.03 | ...68-219,331-333 
 src/generated     |       0 |        0 |       0 |       0 |                   
  git-commit.ts    |       0 |        0 |       0 |       0 | 1-10              
 src/goals         |    93.3 |    89.05 |    94.6 |    93.3 |                   
  ...eGoalStore.ts |   87.61 |    88.88 |   86.66 |   87.61 | ...85-188,196-204 
  ...t-verifier.ts |   96.27 |     90.9 |     100 |   96.27 | ...20,143-146,163 
  ...checkpoint.ts |   81.48 |    76.19 |     100 |   81.48 | ...02-105,115-118 
  goal-evidence.ts |   88.79 |     88.5 |   96.42 |   88.79 | ...04-805,828-831 
  ...projection.ts |   66.66 |    72.97 |   33.33 |   66.66 | ...83,186,190-192 
  ...ersistence.ts |   87.73 |    84.84 |      80 |   87.73 | ...-94,97,101-106 
  goal-protocol.ts |   95.74 |    93.33 |     100 |   95.74 | 154-155           
  goal-reducer.ts  |    93.4 |    90.65 |   96.96 |    93.4 | ...27,501,519-520 
  goal-runtime.ts  |   97.62 |     89.9 |     100 |   97.62 | ...1049,1169-1170 
  goal-tools.ts    |   98.22 |    93.02 |      95 |   98.22 | ...46-147,248-249 
  ...rn-context.ts |     100 |      100 |     100 |     100 |                   
  goal-verifier.ts |   92.46 |    92.85 |     100 |   92.46 | ...69-172,185-187 
  goal-wire.ts     |       0 |        0 |       0 |       0 | 1-28              
  goalHook.ts      |   96.91 |    92.42 |     100 |   96.91 | 115-120,221-222   
  goalJudge.ts     |   95.84 |    87.09 |     100 |   95.84 | ...55-356,448-449 
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/hooks         |   88.07 |    86.36 |   88.54 |   88.07 |                   
  ...okRegistry.ts |   86.48 |    77.08 |     100 |   86.48 | ...41-344,362-369 
  ...bortSignal.ts |     100 |      100 |     100 |     100 |                   
  context-usage.ts |     100 |      100 |     100 |     100 |                   
  ...terpolator.ts |   96.66 |    93.33 |     100 |   96.66 | 66-67             
  ...HookRunner.ts |   96.68 |    87.23 |     100 |   96.68 | 110-112,231-233   
  ...Aggregator.ts |   96.57 |    91.48 |     100 |   96.57 | ...20-321,402,404 
  ...entHandler.ts |   95.57 |    84.76 |   94.73 |   95.57 | ...1040-1041,1051 
  hookPlanner.ts   |   87.55 |    85.54 |   86.66 |   87.55 | ...22-226,233-244 
  hookRegistry.ts  |   92.53 |    85.43 |     100 |   92.53 | ...39,458,462,466 
  hookRunner.ts    |   62.65 |    72.34 |   66.66 |   62.65 | ...70-771,780-781 
  hookSystem.ts    |   87.64 |     98.5 |   70.83 |   87.64 | ...58-759,765-766 
  ...HookRunner.ts |   79.06 |    66.66 |      80 |   79.06 | ...33-434,452-456 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...edCallback.ts |     100 |      100 |     100 |     100 |                   
  ...HookRunner.ts |   94.19 |    84.37 |   81.81 |   94.19 | ...76-384,458-459 
  ...SkillHooks.ts |   78.75 |       75 |   66.66 |   78.75 | 62-66,137-152     
  ...oksManager.ts |   94.87 |    88.88 |     100 |   94.87 | ...84,325,327-329 
  ssrfGuard.ts     |   86.45 |    89.13 |     100 |   86.45 | ...85,289-295,301 
  stopHookCap.ts   |     100 |      100 |     100 |     100 |                   
  trustedHooks.ts  |      90 |    52.63 |     100 |      90 | ...53,66-67,97-98 
  types.ts         |   94.25 |    96.12 |   88.88 |   94.25 | ...46-547,632-636 
  urlValidator.ts  |     100 |      100 |     100 |     100 |                   
  ...it-context.ts |     100 |      100 |     100 |     100 |                   
 src/ide           |   76.98 |    85.03 |   79.03 |   76.98 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |   69.16 |    84.65 |   68.29 |   69.16 | ...1068,1097-1105 
  ide-installer.ts |   89.06 |    79.31 |     100 |   89.06 | ...36,143-147,160 
  ideContext.ts    |     100 |      100 |     100 |     100 |                   
  process-utils.ts |   84.84 |    71.79 |     100 |   84.84 | ...37,151,193-194 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/lsp           |   58.96 |    70.57 |   66.14 |   58.96 |                   
  ...nfigLoader.ts |   80.55 |       72 |   95.45 |   80.55 | ...02-504,508-514 
  ...ionFactory.ts |   42.81 |    73.07 |      50 |   42.81 | ...76-427,433-450 
  ...Normalizer.ts |   23.09 |    13.72 |   30.43 |   23.09 | ...04-905,909-924 
  ...verManager.ts |   75.73 |     80.1 |   79.66 |   75.73 | ...1346,1352-1382 
  ...eLspClient.ts |   32.78 |       80 |   16.66 |   32.78 | ...89-293,299-300 
  ...LspService.ts |      60 |    73.36 |   78.26 |      60 | ...1575,1635-1645 
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/mcp           |    82.3 |    77.81 |   78.33 |    82.3 |                   
  configHash.ts    |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   86.95 |      100 |   33.33 |   86.95 | ...,93,97,101-102 
  ...h-provider.ts |   79.31 |    58.06 |     100 |   79.31 | ...26-933,940-942 
  ...en-storage.ts |   98.78 |    97.95 |     100 |   98.78 | 106-107           
  oauth-utils.ts   |   73.61 |    85.48 |    92.3 |   73.61 | ...46-366,392-421 
  ...n-provider.ts |   89.83 |       96 |   45.45 |   89.83 | ...43,147,151-152 
 .../token-storage |   82.12 |    88.19 |   89.28 |   82.12 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   87.08 |    87.03 |   95.23 |   87.08 | ...00-201,214-215 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   68.14 |    82.35 |   64.28 |   68.14 | ...81-295,298-314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/memory        |   87.83 |    83.81 |   90.47 |   87.83 |                   
  ...y-document.ts |   89.52 |    84.61 |     100 |   89.52 | ...24-325,329-330 
  ...nel-memory.ts |   97.36 |    96.63 |   96.42 |   97.36 | ...91-293,367-368 
  const.ts         |   94.28 |     92.3 |     100 |   94.28 | 66-67             
  dream.ts         |    64.6 |    72.22 |      50 |    64.6 | ...04-109,124-165 
  ...entPlanner.ts |     100 |    83.33 |     100 |     100 | 136,146           
  entries.ts       |   75.59 |    84.84 |   83.33 |   75.59 | ...56-157,172-180 
  extract.ts       |   92.41 |    79.41 |     100 |   92.41 | 56-61,100,119-122 
  ...entPlanner.ts |   91.59 |    76.74 |     100 |   91.59 | ...05,114-117,293 
  ...ionPlanner.ts |       0 |        0 |       0 |       0 | 1                 
  forget.ts        |   81.83 |       75 |   83.33 |   81.83 | ...51,474,478-507 
  indexer.ts       |   94.14 |       84 |     100 |   94.14 | ...32-233,334,337 
  ...kill-agent.ts |   97.94 |    89.36 |     100 |   97.94 | 82-83,179-180     
  manager.ts       |    78.4 |    82.29 |   77.77 |    78.4 | ...1482,1495-1497 
  ...ent-config.ts |   86.99 |    82.69 |   86.36 |   86.99 | ...69,389,396-402 
  memoryAge.ts     |   90.47 |       80 |     100 |   90.47 | 50-51             
  paths.ts         |     100 |      100 |     100 |     100 |                   
  ...ing-skills.ts |     100 |       72 |     100 |     100 | 31-35,73-78,97    
  prompt.ts        |   97.26 |    87.03 |     100 |   97.26 | ...10-218,222,225 
  recall.ts        |   82.06 |       75 |    90.9 |   82.06 | ...59-364,395-406 
  refresh.ts       |   93.58 |    89.58 |     100 |   93.58 | ...75-176,183-184 
  ...ceSelector.ts |    93.1 |    81.81 |     100 |    93.1 | ...25,127-128,136 
  remember.ts      |   98.89 |    90.19 |     100 |   98.89 | 50,70             
  scan.ts          |   93.12 |    77.41 |     100 |   93.12 | ...08-109,154,157 
  scopes.ts        |     100 |      100 |     100 |     100 |                   
  ...et-scanner.ts |     100 |      100 |     100 |     100 |                   
  ...entPlanner.ts |   77.24 |    74.07 |   72.22 |   77.24 | ...52-456,459,465 
  status.ts        |   10.52 |      100 |       0 |   10.52 | 41-98             
  store.ts         |   92.92 |    81.81 |     100 |   92.92 | ...16-117,147-148 
  ...git-status.ts |     100 |     87.5 |     100 |     100 | 30                
  ...cret-guard.ts |     100 |      100 |     100 |     100 |                   
  ...emory-sync.ts |   94.24 |    82.85 |     100 |   94.24 | ...34-236,246-247 
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ontextFile.ts |   81.21 |     79.1 |   81.81 |   81.21 | ...63-277,291-296 
 src/mocks         |       0 |        0 |       0 |       0 |                   
  msw.ts           |       0 |        0 |       0 |       0 | 1-9               
 src/models        |   92.55 |    88.62 |   91.13 |   92.55 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...tor-config.ts |   97.77 |    91.83 |     100 |   97.77 | 155,161,171       
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nfigErrors.ts |   74.22 |       44 |   84.61 |   74.22 | ...,67-74,106-117 
  ...igResolver.ts |   98.71 |    93.33 |     100 |   98.71 | 166,328,334       
  modelRegistry.ts |     100 |    98.11 |     100 |     100 | 177,261           
  modelsConfig.ts  |   89.36 |    86.93 |   88.09 |   89.36 | ...1404,1433-1434 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/output        |     100 |      100 |     100 |     100 |                   
  ...-formatter.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/permissions   |   83.79 |    91.17 |   71.07 |   83.79 |                   
  autoMode.ts      |   97.66 |    93.13 |     100 |   97.66 | ...82-589,635,712 
  ...transcript.ts |      98 |       84 |     100 |      98 | 200-201           
  classifier.ts    |      94 |    94.54 |     100 |      94 | 158-165,389-393   
  ...erousRules.ts |     100 |    89.36 |     100 |     100 | 110,133,147,175   
  ...alTracking.ts |     100 |      100 |     100 |     100 |                   
  ...e-commands.ts |   86.77 |     73.8 |     100 |   86.77 | 131-141,210-214   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...on-manager.ts |   86.63 |    89.01 |      80 |   86.63 | ...1111,1217-1221 
  rule-parser.ts   |   94.49 |     92.7 |     100 |   94.49 | ...1447,1481-1483 
  ...-semantics.ts |   70.44 |    91.09 |   46.66 |   70.44 | ...2237,2311-2314 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...sifier-prompts |   99.04 |    95.23 |     100 |   99.04 |                   
  system-prompt.ts |   99.04 |    95.23 |     100 |   99.04 | 220               
 src/prompts       |   83.63 |      100 |    87.5 |   83.63 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |     100 |      100 |     100 |     100 |                   
 src/providers     |   83.71 |     78.6 |   81.25 |   83.71 |                   
  all-providers.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  install.ts       |   93.11 |     84.5 |     100 |   93.11 | ...56-257,330-331 
  ...der-config.ts |   75.85 |    74.04 |   78.26 |   75.85 | ...73-474,502-503 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...viders/presets |   97.82 |    91.66 |   63.63 |   97.82 |                   
  ...oding-plan.ts |   87.34 |      100 |       0 |   87.34 | 81-83,86-88,90-93 
  ...a-standard.ts |     100 |      100 |     100 |     100 |                   
  ...token-plan.ts |     100 |      100 |     100 |     100 |                   
  ...m-provider.ts |   97.05 |    81.25 |      75 |   97.05 | 118-119           
  deepseek.ts      |     100 |      100 |     100 |     100 |                   
  grok.ts          |     100 |      100 |     100 |     100 |                   
  idealab.ts       |     100 |      100 |     100 |     100 |                   
  minimax.ts       |     100 |      100 |     100 |     100 |                   
  modelscope.ts    |     100 |      100 |     100 |     100 |                   
  openrouter.ts    |     100 |      100 |     100 |     100 |                   
  requesty.ts      |     100 |      100 |     100 |     100 |                   
  zai.ts           |     100 |      100 |     100 |     100 |                   
 src/qwen          |   85.41 |    78.76 |   95.89 |   85.41 |                   
  ...tGenerator.ts |   98.64 |    98.18 |     100 |   98.64 | 105-106           
  qwenOAuth2.ts    |   82.79 |    73.75 |   90.62 |   82.79 | ...1205-1221,1251 
  ...kenManager.ts |   85.36 |    76.61 |     100 |   85.36 | ...52-757,778-783 
 src/resources     |     100 |      100 |     100 |     100 |                   
  ...e-registry.ts |     100 |      100 |     100 |     100 |                   
 src/services      |   89.76 |    84.62 |   96.92 |   89.76 |                   
  ...ionTrailer.ts |     100 |      100 |     100 |     100 |                   
  ...llRegistry.ts |    98.5 |     87.5 |     100 |    98.5 | 81-82,105,476-477 
  ...ionService.ts |   97.51 |    96.15 |     100 |   97.51 | ...,929,1072-1080 
  ...ingService.ts |   91.41 |    85.15 |   95.65 |   91.41 | ...2116,2143-2144 
  ...ttribution.ts |   91.73 |    87.71 |      90 |   91.73 | ...80-685,826-827 
  ...utSlimming.ts |    97.2 |    93.93 |     100 |    97.2 | ...39-340,378-381 
  cronScheduler.ts |   94.17 |    90.45 |      98 |   94.17 | ...1333,1736-1737 
  cronTasksFile.ts |   95.49 |    90.82 |     100 |   95.49 | ...37,346-347,483 
  cronTasksLock.ts |   94.44 |    89.47 |     100 |   94.44 | ...02-103,132-133 
  ...eryService.ts |   96.22 |    93.54 |      90 |   96.22 | 121,155-156,161   
  ...oryService.ts |   88.17 |    79.02 |    92.3 |   88.17 | ...1303,1344-1347 
  fileReadCache.ts |    97.5 |    96.07 |     100 |    97.5 | 349-350,363-364   
  ...temService.ts |    92.8 |    84.68 |   94.11 |    92.8 | ...53,479-486,531 
  ...ratedFiles.ts |      96 |    88.23 |     100 |      96 | 119-120,146-147   
  gitInit.ts       |     100 |      100 |     100 |     100 |                   
  ...reeService.ts |    73.7 |    68.49 |   95.83 |    73.7 | ...2196,2225-2226 
  ...on-service.ts |   87.38 |       72 |     100 |   87.38 | ...01-305,343-344 
  ...references.ts |   98.39 |    88.76 |     100 |   98.39 | 154-155,215-216   
  ...ionService.ts |   98.26 |    97.35 |     100 |   98.26 | ...13-714,761-762 
  ...ticsDumper.ts |   98.37 |    95.23 |     100 |   98.37 | 185-186           
  ...ureMonitor.ts |   95.82 |    90.52 |   97.05 |   95.82 | ...60,861,875-877 
  ...orRegistry.ts |    97.3 |    91.22 |     100 |    97.3 | ...53-454,611-612 
  ...ttachments.ts |   97.74 |    90.85 |     100 |   97.74 | 298-308,646       
  ...ersistence.ts |   90.95 |    78.75 |     100 |   90.95 | ...78,963-964,992 
  ...on-service.ts |   94.49 |    92.26 |   97.14 |   94.49 | ...98-600,656-664 
  ...ce-service.ts |    98.5 |    94.11 |    90.9 |    98.5 | 64-65             
  ...ipt-reader.ts |   94.55 |    89.78 |   96.66 |   94.55 | ...1353-1354,1422 
  ...est-helper.ts |       0 |        0 |       0 |       0 | 1-65              
  ...iter-lease.ts |   83.14 |    74.47 |   97.61 |   83.14 | ...2433,2445-2448 
  sessionRecap.ts  |   67.56 |    43.47 |     100 |   67.56 | ...60,178,180-183 
  ...ionService.ts |   88.79 |    83.72 |   97.18 |   88.79 | ...2477,2553-2573 
  sessionTitle.ts  |   94.26 |    73.21 |     100 |   94.26 | ...45-248,279-280 
  ...ionService.ts |    84.4 |    78.45 |   97.18 |    84.4 | ...2493,2499-2504 
  ...pInhibitor.ts |   97.42 |    92.77 |     100 |   97.42 | ...30,169,369-370 
  ...Estimation.ts |     100 |    88.23 |     100 |     100 | 118-119           
  ...ageService.ts |   97.76 |    91.59 |   93.75 |   97.76 | ...61-262,366,567 
  ...ite-origin.ts |     100 |    93.33 |     100 |     100 | 32                
  ...UseSummary.ts |   94.63 |    88.46 |     100 |   94.63 | ...62-164,214-215 
  ...rd-service.ts |     100 |    88.37 |     100 |     100 | ...29,145-146,241 
  ...oryService.ts |   90.72 |    84.07 |     100 |   90.72 | ...06-509,561-562 
  ...reeCleanup.ts |   14.42 |      100 |   33.33 |   14.42 | 58-186            
  ...ionService.ts |   87.98 |    86.95 |     100 |   87.98 | ...38-439,455-456 
 ...icrocompaction |    98.9 |    95.08 |     100 |    98.9 |                   
  microcompact.ts  |    98.9 |    95.08 |     100 |    98.9 | ...40,749,758-759 
 ...s/visionBridge |   98.81 |    92.12 |     100 |   98.81 |                   
  ...capability.ts |     100 |      100 |     100 |     100 |                   
  ...part-utils.ts |     100 |      100 |     100 |     100 |                   
  ...ion-bridge.ts |   98.72 |    82.35 |     100 |   98.72 | 65,71             
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  ...ge-service.ts |   98.61 |     94.7 |     100 |   98.61 | ...06,666,679-680 
 src/skills        |   89.29 |    85.89 |   93.61 |   89.29 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...activation.ts |     100 |    93.33 |     100 |     100 | 93,112            
  skill-curator.ts |   89.71 |    81.54 |     100 |   89.71 | ...01-902,904-907 
  skill-load.ts    |   94.84 |     87.5 |     100 |   94.84 | ...03,223,235-237 
  skill-manager.ts |   84.82 |    85.29 |   83.33 |   84.82 | ...1243,1250-1254 
  skill-paths.ts   |   90.42 |     87.5 |     100 |   90.42 | ...19-120,125-126 
  symlinkScope.ts  |     100 |      100 |     100 |     100 |                   
  types.ts         |   97.91 |    98.03 |     100 |   97.91 | 277-278           
 ...ataviz/scripts |   80.06 |    95.23 |   88.23 |   80.06 |                   
  ...te_palette.js |   80.06 |    95.23 |   88.23 |   80.06 | 261-296,306-328   
 ...s/bundled/loop |   97.48 |    95.77 |     100 |   97.48 |                   
  ...omous-loop.ts |     100 |      100 |     100 |     100 |                   
  ...-task-file.ts |   94.85 |     92.4 |     100 |   94.85 | ...56,367,375-376 
  ...k-resolver.ts |     100 |      100 |     100 |     100 |                   
 src/subagents     |   87.72 |    89.01 |   96.55 |   87.72 |                   
  ...ter-schema.ts |     100 |    98.07 |     100 |     100 | 99                
  ...tin-agents.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...nt-manager.ts |   84.48 |    85.91 |   94.87 |   84.48 | ...1582,1659-1660 
  types.ts         |     100 |      100 |     100 |     100 |                   
  validation.ts    |   92.46 |    95.18 |     100 |   92.46 | 47-52,63-68,71-76 
 src/telemetry     |   81.73 |    83.57 |   84.83 |   81.73 |                   
  ...ty-tracker.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...on-metrics.ts |   99.08 |    80.95 |     100 |   99.08 | 185,199           
  ...on-tracing.ts |   76.31 |    74.62 |   73.68 |   76.31 | ...80,387-389,405 
  ...attributes.ts |   95.15 |    87.27 |     100 |   95.15 | ...97-198,216-217 
  ...ag-metrics.ts |     100 |    77.77 |     100 |     100 | 21,40             
  ...t-loop-lag.ts |   96.85 |    85.71 |     100 |   96.85 | 170-173           
  ...-exporters.ts |   65.78 |    83.33 |   55.55 |   65.78 | ...04-105,108-109 
  ...ai-content.ts |    74.5 |    66.41 |   91.66 |    74.5 | ...1480,1493-1502 
  ...i-provider.ts |     100 |       99 |     100 |     100 | 99                
  ...ai-request.ts |   87.52 |    92.79 |   83.78 |   87.52 | ...55-561,564-570 
  gen-ai-usage.ts  |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-111             
  ...-processor.ts |    99.1 |    95.72 |      95 |    99.1 | 145,369-370       
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-128             
  loggers.ts       |   60.03 |    76.51 |   66.07 |   60.03 | ...1484,1501-1521 
  metrics.ts       |   80.37 |    82.35 |   80.95 |   80.37 | ...1150,1153-1164 
  otlp-urls.ts     |     100 |      100 |     100 |     100 |                   
  ...attributes.ts |     100 |      100 |     100 |     100 |                   
  ...ime-config.ts |       0 |        0 |       0 |       0 | 1                 
  sanitize.ts      |      80 |    83.33 |     100 |      80 | 35-36,41-42       
  ...rters-grpc.ts |     100 |      100 |     100 |     100 |                   
  ...rters-http.ts |     100 |      100 |     100 |     100 |                   
  sdk-impl.ts      |   91.06 |    87.15 |   68.75 |   91.06 | ...32,482-483,499 
  sdk.ts           |   82.12 |    90.47 |   66.66 |   82.12 | ...90-194,232-254 
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...on-tracing.ts |    91.1 |    88.68 |   96.77 |    91.1 | ...1737,1768-1771 
  ...etry-utils.ts |     100 |      100 |     100 |     100 |                   
  ...l-decision.ts |     100 |      100 |     100 |     100 |                   
  trace-context.ts |     100 |      100 |     100 |     100 |                   
  ...e-id-utils.ts |     100 |      100 |     100 |     100 |                   
  tracer.ts        |   98.56 |    88.63 |     100 |   98.56 | 52,101            
  types.ts         |      83 |     87.5 |   86.36 |      83 | ...1467,1471-1478 
  uiTelemetry.ts   |   97.18 |    93.93 |      88 |   97.18 | ...70,314,461-462 
 ...ry/qwen-logger |   74.23 |    80.53 |      70 |   74.23 |                   
  event-types.ts   |       0 |        0 |       0 |       0 |                   
  qwen-logger.ts   |   74.23 |    80.35 |   69.49 |   74.23 | ...1122,1160-1161 
 src/test-utils    |   96.02 |    98.41 |   82.92 |   96.02 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  ...st-helpers.ts |   94.11 |       90 |     100 |   94.11 | 69-70             
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...mised-lock.ts |     100 |      100 |     100 |     100 |                   
  mock-tool.ts     |   94.85 |      100 |   78.78 |   94.85 | ...53,227-228,241 
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
 src/tools         |   86.25 |    85.07 |   88.72 |   86.25 |                   
  ...erQuestion.ts |   89.71 |    80.76 |   91.66 |   89.71 | ...66-367,374-375 
  ...-registrar.ts |    77.7 |    66.66 |   66.66 |    77.7 | ...72-277,292-294 
  ...ub-session.ts |   89.67 |     91.3 |   81.81 |   89.67 | ...03-304,315-322 
  cron-create.ts   |   90.64 |    92.85 |   72.72 |   90.64 | ...,73-74,223-231 
  cron-delete.ts   |   97.56 |      100 |   83.33 |   97.56 | 31-32             
  cron-list.ts     |   98.23 |    95.34 |    87.5 |   98.23 | 57-58             
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  display-image.ts |   87.42 |    84.84 |   88.88 |   87.42 | ...29-134,194-195 
  edit.ts          |   82.76 |    86.77 |   81.25 |   82.76 | ...45-746,865-915 
  ...r-worktree.ts |   83.14 |    67.56 |    87.5 |   83.14 | ...84-187,278-279 
  enterPlanMode.ts |      85 |     82.6 |    87.5 |      85 | ...28-133,161-175 
  exit-worktree.ts |   83.29 |    83.65 |   94.44 |   83.29 | ...14-515,537-538 
  exitPlanMode.ts  |      95 |    85.29 |     100 |      95 | ...21-325,344,378 
  ...permission.ts |     100 |      100 |     100 |     100 |                   
  glob.ts          |   96.33 |     88.5 |     100 |   96.33 | ...24-225,373,376 
  grep.ts          |   90.73 |    86.61 |   85.71 |   90.73 | ...76-677,727-728 
  ...adTracking.ts |     100 |      100 |     100 |     100 |                   
  image-gen.ts     |   91.66 |    77.41 |    90.9 |   91.66 | ...13-214,221-222 
  list-agents.ts   |   94.02 |    82.35 |   83.33 |   94.02 | 31-32,47-48       
  loop-wakeup.ts   |   99.27 |    92.85 |     100 |   99.27 | 45                
  ls.ts            |   96.74 |    90.27 |     100 |   96.74 | 176-181,212,216   
  lsp.ts           |   72.71 |     59.5 |   90.32 |   72.71 | ...1212,1214-1215 
  ...nt-manager.ts |   82.13 |    80.47 |   85.71 |   82.13 | ...3234,3236-3237 
  mcp-client.ts    |   80.03 |    86.58 |   89.47 |   80.03 | ...2272,2276-2279 
  ...ry-timeout.ts |     100 |      100 |     100 |     100 |                   
  mcp-errors.ts    |     100 |      100 |     100 |     100 |                   
  ...pool-entry.ts |   79.21 |    85.71 |   81.57 |   79.21 | ...1341,1349-1350 
  ...ool-events.ts |       8 |      100 |       0 |       8 | 132-158           
  mcp-pool-key.ts  |   97.46 |    93.93 |     100 |   97.46 | 176-177           
  ...ce-content.ts |   96.55 |    91.17 |     100 |   96.55 | 80-82             
  mcp-retry.ts     |   97.67 |    95.65 |     100 |   97.67 | 131-132           
  ...ion-config.ts |     100 |      100 |     100 |     100 |                   
  mcp-status.ts    |     100 |      100 |     100 |     100 |                   
  mcp-tool.ts      |   98.35 |    93.71 |     100 |   98.35 | ...-990,1045-1046 
  ...sport-pool.ts |   83.98 |     80.3 |   88.46 |   83.98 | ...1409,1416-1420 
  ...ace-budget.ts |   87.27 |     82.6 |     100 |   87.27 | ...00-305,340-345 
  memory-config.ts |     100 |      100 |     100 |     100 |                   
  ...iable-tool.ts |     100 |    84.61 |     100 |     100 | 101,108           
  monitor.ts       |   91.82 |    83.09 |   88.46 |   91.82 | ...99,612,810-815 
  notebook-edit.ts |   85.71 |    77.08 |   81.25 |   85.71 | ...96-912,958-959 
  ...escendants.ts |   36.17 |    64.51 |   55.55 |   36.17 | ...46-310,385-390 
  ...nforcement.ts |   83.21 |    90.69 |     100 |   83.21 | 147-158,207-220   
  read-file.ts     |   95.49 |    88.52 |   86.66 |   95.49 | ...49,464,536-537 
  ...p-resource.ts |   96.85 |      100 |   91.66 |   96.85 | 92-96             
  ...d-artifact.ts |   91.18 |    86.71 |    87.5 |   91.18 | ...26-427,441-453 
  ripGrep.ts       |    94.6 |    87.26 |   95.23 |    94.6 | ...33-734,740-741 
  ...-transport.ts |   71.42 |    55.55 |   71.42 |   71.42 | ...36-137,143-144 
  send-message.ts  |   81.13 |    89.74 |    62.5 |   81.13 | ...80-286,363-371 
  ...n-mcp-view.ts |   94.07 |    91.89 |    90.9 |   94.07 | 131-139           
  shell.ts         |   78.81 |    84.22 |   91.91 |   78.81 | ...5036,5099-5100 
  skill-utils.ts   |     100 |      100 |     100 |     100 |                   
  skill.ts         |   91.39 |    92.55 |      90 |   91.39 | ...84,488,534-556 
  ...eticOutput.ts |   95.12 |      100 |      80 |   95.12 | 87-88             
  task-create.ts   |    94.4 |    93.33 |   81.81 |    94.4 | 45-49,63-64,95    
  task-list.ts     |   73.38 |    77.77 |   83.33 |   73.38 | ...02,105,109-116 
  task-stop.ts     |   93.14 |    96.15 |   85.71 |   93.14 | 39-40,54-64       
  task-update.ts   |   82.89 |    83.92 |    92.3 |   82.89 | ...14-422,454-465 
  team-create.ts   |   97.22 |    85.71 |   83.33 |   97.22 | 48-49,129-130     
  team-delete.ts   |   86.74 |    83.33 |   83.33 |   86.74 | 37-38,42-48,72-73 
  ...n-approval.ts |   92.14 |    96.77 |   77.77 |   92.14 | 38-39,42-43,93-99 
  todoWrite.ts     |   95.13 |    87.85 |   93.33 |   95.13 | ...23-527,540-545 
  tool-error.ts    |     100 |      100 |     100 |     100 |                   
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   78.57 |    79.59 |    82.6 |   78.57 | ...89-990,998-999 
  tool-search.ts   |   96.19 |    89.72 |   93.33 |   96.19 | ...09,259-264,426 
  tools.ts         |   93.11 |    92.53 |   91.66 |   93.11 | ...69-570,586-592 
  ...reapproved.ts |   99.27 |    94.11 |     100 |   99.27 | 170               
  web-fetch.ts     |   96.05 |    90.54 |   96.77 |   96.05 | ...85-786,800-801 
  web-search.ts    |   90.58 |    83.57 |      80 |   90.58 | ...1025,1083-1086 
  write-file.ts    |   86.72 |    84.92 |   88.88 |   86.72 | ...25-828,865-900 
  zoom-image.ts    |   95.76 |    93.75 |      90 |   95.76 | 54-59,203-204     
 src/tools/agent   |   87.22 |    87.68 |   88.69 |   87.22 |                   
  agent.ts         |   85.84 |    86.59 |   86.31 |   85.84 | ...4315,4337-4347 
  fork-profile.ts  |   93.65 |       90 |     100 |   93.65 | ...33-134,171-174 
  fork-subagent.ts |   98.73 |       95 |     100 |   98.73 | 101-102,173       
 ...tools/artifact |   95.78 |    92.51 |   88.63 |   95.78 |                   
  artifact-tool.ts |   91.46 |    88.46 |   71.42 |   91.46 | ...13-314,322-325 
  ...-publisher.ts |     100 |    85.71 |     100 |     100 | 32                
  ...-publisher.ts |   96.74 |    97.72 |    87.5 |   96.74 | 29-30,156-157     
  html.ts          |     100 |    96.77 |     100 |     100 | 122               
  ...-publisher.ts |     100 |       80 |     100 |     100 | 30                
  oss-publisher.ts |    98.1 |    91.48 |     100 |    98.1 | 43-45             
  publisher.ts     |     100 |      100 |     100 |     100 |                   
 ...s/computer-use |   90.21 |    82.17 |   78.08 |   90.21 |                   
  bootstrap.ts     |   59.42 |    80.95 |   41.66 |   59.42 | ...35-339,341-345 
  client.ts        |   80.11 |       90 |   77.77 |   80.11 | ...97,242-243,274 
  constants.ts     |     100 |    94.73 |     100 |     100 | 129,256           
  downloader.ts    |   65.29 |    52.77 |   58.33 |   65.29 | ...99-300,316-355 
  index.ts         |     100 |      100 |     100 |     100 |                   
  install-state.ts |   94.44 |    72.72 |     100 |   94.44 | 44-45             
  ...n-detector.ts |     100 |     87.5 |     100 |     100 | 50                
  schemas.ts       |     100 |      100 |     100 |     100 |                   
  tool.ts          |    96.3 |    85.71 |     100 |    96.3 | 75-76,184,252-258 
 ...tools/workflow |   86.51 |    84.81 |      75 |   86.51 |                   
  workflow.ts      |   86.51 |    84.81 |      75 |   86.51 | ...67,512,514-515 
 src/utils         |   92.85 |    89.59 |   96.88 |   92.85 |                   
  LruCache.ts      |     100 |      100 |     100 |     100 |                   
  ...Controller.ts |     100 |      100 |     100 |     100 |                   
  ...ssageQueue.ts |     100 |      100 |     100 |     100 |                   
  ...cFileWrite.ts |   94.94 |    92.47 |     100 |   94.94 | ...43-544,651-655 
  bareMode.ts      |   81.81 |      100 |      50 |   81.81 | 18-19             
  ...ry-content.ts |   98.45 |    95.45 |     100 |   98.45 | 132-133,159-160   
  browser.ts       |   86.84 |    78.94 |     100 |   86.84 | 34,36-37,65-66    
  btwUtils.ts      |   13.95 |      100 |       0 |   13.95 | 17-31,34-55       
  bundlePaths.ts   |     100 |      100 |     100 |     100 |                   
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  ...igResolver.ts |     100 |      100 |     100 |     100 |                   
  ...engthError.ts |   91.06 |    89.47 |     100 |   91.06 | ...46-147,154-155 
  ...n-branches.ts |   95.88 |    94.11 |      95 |   95.88 | ...98-499,511-524 
  ...tion-chain.ts |     100 |      100 |     100 |     100 |                   
  cronDisplay.ts   |     100 |    97.61 |     100 |     100 | 46                
  cronParser.ts    |   95.34 |    93.33 |     100 |   95.34 | 41-42,47-48,70-71 
  debugLogger.ts   |   96.66 |    96.61 |   88.88 |   96.66 | 192-196           
  editHelper.ts    |   93.63 |     83.9 |     100 |   93.63 | ...27-428,462-463 
  editor.ts        |   97.65 |    95.45 |     100 |   97.65 | ...35-336,338-339 
  encoding.ts      |     100 |      100 |     100 |     100 |                   
  env.ts           |     100 |      100 |     100 |     100 |                   
  ...arResolver.ts |   94.28 |    88.88 |     100 |   94.28 | 28-29,125-126     
  ...entContext.ts |   96.63 |    90.13 |   96.66 |   96.63 | ...42,444-445,512 
  errorParsing.ts  |     100 |      100 |     100 |     100 |                   
  ...rReporting.ts |   95.65 |    93.33 |     100 |   95.65 | 37-38             
  errors.ts        |   83.01 |    95.03 |    61.9 |   83.01 | ...62-378,382-388 
  fetch.ts         |   90.68 |    82.51 |     100 |   90.68 | ...72,483-484,503 
  file-identity.ts |     100 |      100 |     100 |     100 |                   
  fileUtils.ts     |   94.87 |    92.95 |   96.15 |   94.87 | ...1907,1915-1916 
  forkedAgent.ts   |   92.45 |    82.35 |   93.75 |   92.45 | ...34,642,647-654 
  formatters.ts    |     100 |      100 |     100 |     100 |                   
  ...eUtilities.ts |    92.4 |    86.95 |     100 |    92.4 | ...52-158,168-169 
  ...rStructure.ts |   94.39 |    94.28 |     100 |   94.39 | ...29-132,343-348 
  getPty.ts        |   31.57 |       50 |     100 |   31.57 | 26-38             
  git-branches.ts  |    91.6 |    84.21 |    92.3 |    91.6 | ...90,405-410,570 
  ...fig-safety.ts |   97.01 |       80 |     100 |   97.01 | 53-54             
  gitDiff.ts       |   95.19 |    81.36 |     100 |   95.19 | ...1073,1419-1420 
  gitDirect.ts     |   98.84 |    94.28 |     100 |   98.84 | 234,318           
  ...noreParser.ts |   94.48 |    93.22 |     100 |   94.48 | ...23-124,158-159 
  gitUtils.ts      |   78.02 |    81.25 |   85.71 |   78.02 | ...22-123,147-198 
  github-prs.ts    |   95.74 |    82.27 |     100 |   95.74 | 216,314-322       
  iconvHelper.ts   |     100 |      100 |     100 |     100 |                   
  ...rePatterns.ts |     100 |      100 |     100 |     100 |                   
  image-view.ts    |   95.12 |    93.33 |     100 |   95.12 | ...68-172,240-244 
  ...ionManager.ts |     100 |     90.9 |     100 |     100 | 27                
  ...lPromptIds.ts |     100 |      100 |     100 |     100 |                   
  ...on-context.ts |     100 |      100 |     100 |     100 |                   
  jsonl-utils.ts   |   95.27 |     93.1 |     100 |   95.27 | ...16-317,359-362 
  ...-detection.ts |     100 |      100 |     100 |     100 |                   
  ...iconv-lite.ts |     100 |      100 |     100 |     100 |                   
  ...simple-git.ts |   96.77 |    91.66 |     100 |   96.77 | 38                
  ...m-headless.ts |      96 |    88.88 |     100 |      96 | 34                
  ...iagnostics.ts |    96.4 |     94.2 |     100 |    96.4 | ...66,293-294,376 
  ...yDiscovery.ts |    92.4 |    89.13 |     100 |    92.4 | ...28,331,522-525 
  ...tProcessor.ts |   94.01 |       90 |     100 |   94.01 | ...47-353,445-446 
  ...Inspectors.ts |     100 |      100 |     100 |     100 |                   
  modelId.ts       |   98.96 |    98.21 |     100 |   98.96 | 153               
  ...kerChecker.ts |    90.9 |    91.66 |     100 |    90.9 | 73-79             
  notebook.ts      |   94.57 |    89.91 |   95.83 |   94.57 | ...21,333,385-387 
  openaiLogger.ts  |   91.66 |    89.74 |     100 |   91.66 | ...26-228,251-256 
  osc8.ts          |   54.26 |    64.86 |   83.33 |   54.26 | ...72-195,197-257 
  partUtils.ts     |     100 |    98.64 |     100 |     100 | 211               
  pathReader.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |   90.88 |     90.6 |     100 |   90.88 | ...25-626,628-630 
  pdf.ts           |   92.17 |    85.81 |     100 |   92.17 | ...64-565,606-611 
  projectPath.ts   |     100 |      100 |     100 |     100 |                   
  projectRoot.ts   |   71.73 |    78.57 |     100 |   71.73 | 54-66             
  ...ectSummary.ts |   89.62 |    72.41 |     100 |   89.62 | ...40-145,196-199 
  ...tIdContext.ts |     100 |      100 |     100 |     100 |                   
  proxyUtils.ts    |     100 |      100 |     100 |     100 |                   
  ...rDetection.ts |   71.15 |       86 |     100 |   71.15 | ...-90,96-101,147 
  ...noreParser.ts |   92.63 |    91.66 |     100 |   92.63 | ...77-178,197-198 
  rateLimit.ts     |   93.75 |    89.62 |     100 |   93.75 | ...13,218-219,262 
  ...text-range.ts |   96.98 |    87.15 |     100 |   96.98 | ...87-688,763-764 
  readManyFiles.ts |   95.75 |    80.86 |     100 |   95.75 | ...05,558,568-572 
  retry.ts         |   96.09 |    92.52 |     100 |   96.09 | ...67,558-559,577 
  retryContext.ts  |     100 |      100 |     100 |     100 |                   
  ...sification.ts |   97.63 |    97.08 |     100 |   97.63 | ...17,251-252,278 
  retryPolicy.ts   |   97.72 |    90.56 |     100 |   97.72 | 130-131           
  ripgrepUtils.ts  |   90.04 |    93.43 |   95.45 |   90.04 | ...55-565,598-599 
  ...sDiscovery.ts |   97.46 |    93.05 |     100 |   97.46 | ...04,182-183,202 
  ...iagnostics.ts |   83.08 |     67.5 |   92.59 |   83.08 | ...23,543-544,550 
  ...tchOptions.ts |   84.87 |    86.71 |   96.29 |   84.87 | ...71,696,725-734 
  ...odelPrefix.ts |     100 |      100 |     100 |     100 |                   
  runtimeStatus.ts |    97.5 |    89.74 |     100 |    97.5 | 162-163           
  safe-mode.ts     |     100 |      100 |     100 |     100 |                   
  safeJsonParse.ts |     100 |      100 |     100 |     100 |                   
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  ...-child-env.ts |     100 |      100 |     100 |     100 |                   
  ...aConverter.ts |   98.03 |    97.75 |     100 |   98.03 | 100,102-103       
  ...aValidator.ts |   92.09 |    83.65 |   90.47 |   92.09 | ...60,882-883,896 
  ...r-launcher.ts |   96.35 |    93.97 |   85.71 |   96.35 | ...35-336,347-348 
  sedEditParser.ts |   91.78 |    92.18 |     100 |   91.78 | ...66-569,645-646 
  ...nIdContext.ts |     100 |      100 |     100 |     100 |                   
  ...orageUtils.ts |   95.98 |    83.96 |     100 |   95.98 | ...70,386,466,485 
  ...-pager-env.ts |     100 |      100 |     100 |     100 |                   
  ...fety-rules.ts |     100 |     89.7 |     100 |     100 | ...01,304,309-311 
  shell-utils.ts   |   86.07 |    88.34 |     100 |   86.07 | ...2269,2276-2280 
  ...lAstParser.ts |   98.27 |    91.38 |     100 |   98.27 | ...1321-1323,1333 
  ...ContextEnv.ts |     100 |       92 |     100 |     100 | 50-52             
  ...nlyChecker.ts |   96.33 |    96.57 |     100 |   96.33 | ...83-284,292-293 
  sideQuery.ts     |   86.82 |    86.66 |     100 |   86.82 | ...79-185,187-193 
  ...pEventSink.ts |     100 |       80 |     100 |     100 | 61                
  ...tGenerator.ts |     100 |      100 |     100 |     100 |                   
  ...ameContext.ts |     100 |      100 |     100 |     100 |                   
  symlink.ts       |   77.77 |       50 |     100 |   77.77 | 44,54-59          
  ...e-encoding.ts |   85.96 |    76.47 |     100 |   85.96 | 58-61,64-65,78-79 
  ...emEncoding.ts |   96.36 |    91.17 |     100 |   96.36 | 59-60,124-125     
  terminalSafe.ts  |     100 |      100 |     100 |     100 |                   
  ...Serializer.ts |   98.72 |       90 |     100 |   98.72 | 42-43,134,201-203 
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  ...-constants.ts |     100 |      100 |     100 |     100 |                   
  textUtils.ts     |      65 |      100 |      75 |      65 | 56-75             
  thoughtUtils.ts  |     100 |    95.65 |     100 |     100 | 99                
  ...-converter.ts |   95.23 |    85.71 |     100 |   95.23 | 36-37             
  ...name-utils.ts |     100 |      100 |     100 |     100 |                   
  ...-finalizer.ts |   97.66 |     90.9 |     100 |   97.66 | 165-166,168-172   
  tool-utils.ts    |    95.2 |    93.61 |     100 |    95.2 | ...58-159,162-163 
  ...ultCleanup.ts |   54.62 |       64 |      75 |   54.62 | ...03-105,108-134 
  ...Compaction.ts |   96.13 |    96.42 |     100 |   96.13 | ...34-339,341-346 
  ...pt-records.ts |    87.5 |    86.13 |     100 |    87.5 | ...76-480,510-525 
  truncation.ts    |   90.56 |    90.43 |     100 |   90.56 | ...35-443,480-486 
  windowsPath.ts   |   89.47 |    79.31 |     100 |   89.47 | ...57-58,62,90-91 
  ...aceContext.ts |   96.74 |    91.04 |     100 |   96.74 | ...69,196,299-301 
  xml.ts           |    97.8 |    87.69 |     100 |    97.8 | 98-99             
  yaml-parser.ts   |   83.87 |    77.27 |     100 |   83.87 | ...31-234,239-240 
 ...ils/filesearch |   83.94 |    80.72 |   94.73 |   83.94 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |    82.9 |    76.81 |   95.08 |    82.9 | ...1563,1597-1598 
  fileSearch.ts    |   93.78 |    87.67 |     100 |   93.78 | ...71-272,274-275 
  fzfWorker.ts     |       0 |        0 |       0 |       0 | 1-109             
  ...rkerHandle.ts |   84.05 |    75.43 |   89.47 |   84.05 | ...30-334,340-341 
  ignore.ts        |     100 |    97.36 |     100 |     100 | 187               
  result-cache.ts  |     100 |    93.75 |     100 |     100 | 49                
 ...uest-tokenizer |   69.76 |    75.47 |   85.29 |   69.76 |                   
  ...eTokenizer.ts |   65.72 |    74.02 |    92.3 |   65.72 | ...65-466,479-533 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...tTokenizer.ts |   68.39 |    69.49 |    90.9 |   68.39 | ...24-325,327-328 
  ...ageFormats.ts |   76.92 |      100 |   33.33 |   76.92 | 46-49,56-57       
  textTokenizer.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run.

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 2 finishedview run. See this round's report below.

中文说明

AutoFix 第 2 轮已完成 —— 查看运行。本轮报告见下方。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Could not produce a passing fix for this feedback (round 1/100). This item now needs a human; the loop stays engaged and still picks up new feedback and base conflicts, but will not retry this item on its own.

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:
Qwen failed during address-review: status 125.

See the Qwen Autofix agent step logs for model/tool output.

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31179492621


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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

中文说明

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。

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

Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/run-qwen-serve.ts
Comment thread packages/cli/src/serve/run-qwen-serve.ts
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.test.ts
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Comment thread packages/cli/src/serve/daemon-git-worktree-guard.ts Outdated
Rebuild the daemon-side Git relocation guard parser so the runtime-verified
bypasses from review are closed: comment/glob tokens, backslash
continuations, shell wrapper and path-qualified invocations, cwd-shifting
builtins, env-var relocations, gitfile/symlink/worktree-admin indirection,
-C vs relative git-dir ordering, --output and textconv-capable read-only
subcommands, command-valued -c config, and dynamic expansion forms all fail
closed for mutations outside the session working directory. Command
splitting, canonicalization, and containment now reuse the core helpers.

Key the child-side v1 restrictions (/fork, agent-backed workspace memory)
and per-call daemon round trips on a real external provider being attached
instead of on guard plumbing presence: under the built-in guard alone,
hidden-agent tool calls traverse the same daemon-side policy, so those
features stay available and non-shell tools resolve locally.

Denial reasons are length-clamped and control-character-stripped so they
always satisfy the guard result validation.
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下:

Autofix review round summary — PR #8687

All 11 Critical findings and all 7 Suggestions are addressed. One commit: b175cd72fb (fix(serve): close daemon Git guard parser bypasses).

Critical findings (R1-1 … R1-11) — all fixed

The daemon-side guard parser in daemon-git-worktree-guard.ts was rebuilt. Every runtime-verified bypass is now closed and covered by a unit test:

  • R1-1 unrecognized tokens / fail-open: {comment} tokens terminate the line instead of severing the command; glob tokens are treated as opaque dynamic arguments; unparseable commands fail closed; an empty -C value is a no-op chdir (scanning continues to the next relocation); a dangling relocation option fails closed.
  • R1-2 dynamic relocation forms: backtick, leading-~, ANSI-C $'…' (both flag and program position), and command-substitution forms are unresolved and denied for mutating/unclassifiable subcommands. $(…) bodies are consumed as one opaque dynamic token so GIT_DIR=$(…) git … keeps its assignment attached to the git invocation. The pre-existing quoted-substitution denial is preserved.
  • R1-3 git detection: git is matched by basename of path-qualified invocations (/usr/bin/git, ./bin/git); leading {/! shell syntax is stripped; sh|bash|dash|zsh|ksh -c and eval payloads are analyzed recursively (including bundled -c such as bash -lc, with -o/-O value semantics respected); sudo, nohup, timeout <duration>, exec, command, and env wrappers are unwrapped. A segment whose program token is unclassifiable fails closed when it also carries a relocation marker or a recorded relocation.
  • R1-4 cwd-shifting builtins: cd/pushd/popd are tracked across segments (including inside (…) subshell boundaries); a shifted cwd becomes the containment basis for later git invocations; popd, bare cd ($HOME), cd -, and dynamic targets mark the cwd unknown and mutations fail closed. eval payloads propagate cwd changes; sh -c payloads do not (subshell).
  • R1-5 backslash continuations: command splitting now reuses core splitCommands, which joins \<newline> before parsing (also fixes the comment/quote-state divergences).
  • R1-6 env-var relocations: GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE assignments are recorded as relocations (denied for mutations when outside, unresolved when dynamic); env flags that take values are consumed (-u, -S, --split-string, --unset), env -C/--chdir records a cwd relocation, env -S payloads are analyzed recursively, and unrecognized env options fail closed; sudo -D/--chdir records a cwd relocation.
  • R1-7 --git-dir indirection: targets are canonicalized (core realpathNearestExisting) before any .git basename handling; .git gitfiles are followed through their gitdir: redirect; per-worktree admin dirs (<repo>/.git/worktrees/<name>) resolve through their gitdir file to the linked worktree checkout; unresolvable indirections fail closed. Relocated mutating targets must fully exist at decision time (closes the ln -s /outside link && git -C link reset TOCTOU shape).
  • R1-8 relative target basis: relative --git-dir/--work-tree resolve against the final cwd of the -C chain regardless of argv order (matching measured git behavior).
  • R1-9 read-only allowlist: relocated read-only fast path shrunk to a verified side-effect-free set (status, rev-parse, ls-files, grep, describe, cat-file); diff/log/show/blame removed (textconv executes target-repo config); any --output flag demotes an invocation to mutating.
  • R1-10 command-valued config: -c/--config-env assignments whose key executes commands (alias.*, core.editor, core.pager, core.fsmonitor, credential.helper, filter.*, difftool.*, mergetool.*) or whose value starts with ! mark mutating/unclassifiable invocations unresolved. The two-step variant is covered because git config itself is not read-only.
  • R1-11 /fork and workspace memory regression: root-caused to the child gates keying on guard-plumbing presence, which this PR made unconditional. Verified that hidden-agent tool calls (forked agents via createApprovalModeOverride, memory agents via createMemoryScopedAgentConfig — both Object.create(sessionConfig) delegators) traverse the same managed guard and therefore the same daemon-side built-in policy. The gates now key on a new private marker set only when a real external provider is attached: under the built-in guard alone, /fork and agent-backed remember/dream are available again; with a provider the v1 restrictions remain. Both directions are unit-tested.

Suggestions (R1-12 … R1-18) — all addressed

  • R1-12 core helper reuse: the guard now imports splitCommands, realpathNearestExisting, and isWithinRoot from core (fixing the continuation/comment/canonicalization divergences). parseGitInvocation/tokeniseSegment are not exported from core and carry attribution semantics (skip-when-unattributable) rather than security semantics (deny-when-unprovable); exporting them would require core changes the PR design explicitly forbids, so the guard-specific parser stays local but aligns its flag tables with core's.
  • R1-13 ToolNames literals: the serve fast-path constraint from commit 2b0e8cdcf7 still applies, so the literals stay; a pinning comment ties them to the ToolNames constants and a unit test asserts set equality so a rename fails the build.
  • R1-14 documentation: added a "Built-in daemon Git relocation guard" section to docs/users/qwen-serve.md (policy, recognized relocation forms, read-only set, limitations, and the /fork/memory behavior split); updated the external_tool_guard tag description in docs/developers/qwen-serve-protocol.md to state the tag reflects only the external provider and that the built-in guard runs regardless; refreshed the design doc's policy/failure-semantics/non-goals sections.
  • R1-15 per-call overhead: with no external provider attached, the child resolves every non-run_shell_command tool call locally (the built-in policy allows them structurally) instead of the serialized child→daemon→child round trip; shell commands always round-trip. Covered by unit tests.
  • R1-16 mutation-killer tests: all five added — (1) a provider-denies-an-otherwise-allowed-call case asserting the denial verbatim; (2) externalToolGuard asserted on every createBridge.mock.calls entry plus one captured handler invoked with a relocated mutation; (3) a forged workspaceCwd/effectiveCwd child-payload case in bridgeClient.test.ts asserting the entry's trusted values win; (4) fail-closed parametrized over both trusted fields; (5) nested executors parametrized over all four ToolNames for the provider/no-provider split.
  • R1-17 unbounded denial reason: denial paths are control-character-stripped and clamped so the full reason stays ≤ 500 UTF-16 units (validated by a test with a > 400-char path and a tab-containing path).
  • R1-18 heredoc false positives: documented as an accepted limitation in the design doc non-goals and the user guide (fail-closed false positive, not a bypass); heredoc-body tracking in the duplicated parser was declined because an incorrect tracker could re-open bypasses.

Notes

  • Behavior change worth reviewer attention: relocated diff/log/show/blame are now denied (they were in the read-only set), and relocated mutating commands whose target does not exist at decision time are denied (git -C new-dir init style flows need the directory to exist first, e.g. mkdir -p in a prior command).
  • No conflicts; --conflict false and no merge was performed.

Verification

Commands actually run (all passed):

  • npm run build — passed (exit 0)
  • npm run typecheck — passed (exit 0, no TS errors)
  • npm run lint — passed (0 errors)
  • npx prettier --check on the three changed docs — passed
  • vitest run src/serve/daemon-git-worktree-guard.test.ts (packages/cli) — 92 passed
  • vitest run src/serve (packages/cli, all serve suites) — 146 files, 4217 passed / 1 skipped
  • vitest run src/acp-integration/acpAgent.test.ts (packages/cli) — 386 passed
  • vitest run src/gemini.test.tsx (packages/cli) — 59 passed
  • vitest run src/serve/run-qwen-serve.test.ts (packages/cli) — 231 passed
  • vitest run src/serve/process-env-guard.test.ts (packages/cli) — 3 passed
  • vitest run (packages/acp-bridge, full suite incl. new forged-payload test) — 25 files, 1093 passed
  • npm run bundle — passed (exit 0)
  • Integration (after bundle, QWEN_SANDBOX=false): cli/daemon-invocation-context.test.ts + cli/qwen-serve-routes.test.ts — 36 passed; cli/qwen-serve-baseline.test.ts + cli/qwen-serve-streaming.test.ts + cli/acp-integration.test.ts + cli/mock-acp-typecheck.test.ts + cli/run_shell_command.test.ts — 28 passed
  • Runtime probe scripts executing the reviewer's exact attack commands against the built guard (real temp-dir fixtures, gitfile/symlink/worktree-admin layouts) — all 11 Critical bypass shapes now denied, inside-boundary controls allowed
中文说明

Autofix 审查轮次总结 — PR #8687

全部 11 个 Critical 发现和全部 7 个 Suggestion 均已处理。单次提交:b175cd72fbfix(serve): close daemon Git guard parser bypasses)。

Critical 发现(R1-1 … R1-11)— 全部修复

重建了 daemon-git-worktree-guard.ts 中的守护进程侧守卫解析器。每一个经过运行时验证的绕过都已被封堵,并有单元测试覆盖:

  • R1-1 未识别 token / 放行漏洞{comment} token 现在终止整行而不是切断命令;glob token 按不透明的动态参数处理;无法解析的命令整体拒绝;空 -C 值视为无操作 chdir(继续扫描下一个重定位);悬空的重定位选项一律拒绝。
  • R1-2 动态重定位形式:反引号、前导 ~、ANSI-C $'…'(flag 位和程序位均覆盖)、命令替换形式对变更类/无法分类的子命令判定为未解析并拒绝。$(…) 主体作为单个不透明动态 token 消费,保证 GIT_DIR=$(…) git … 的赋值不会与 git 调用被切断。原有的带引号替换拒绝行为保持不变。
  • R1-3 git 识别:按 basename 匹配路径限定的 git(/usr/bin/git./bin/git);剥离前导 {/! shell 语法;递归分析 sh|bash|dash|zsh|ksh -ceval 载荷(包括 bash -lc 这类捆绑 -c,并正确处理 -o/-O 的取值语义);解开 sudonohuptimeout <时长>execcommandenv 包装。程序 token 无法分类的段在同时携带重定位标记或已记录重定位时拒绝。
  • R1-4 切换目录的内建命令:跨段跟踪 cd/pushd/popd(包括 (…) 子 shell 边界);被移动的 cwd 成为后续 git 调用的包含性基准;popd、裸 cd($HOME)、cd - 和动态目标将 cwd 标记为未知,变更类命令拒绝。eval 载荷传播 cwd 变化;sh -c 载荷不传播(子 shell)。
  • R1-5 反斜杠续行:命令切分改为复用 core 的 splitCommands,在解析前先拼接 \<newline>(同时修复注释/引号状态的分歧)。
  • R1-6 环境变量重定位GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE 赋值被记录为重定位(在边界外时对变更类拒绝,动态时判未解析);正确消费 env 的取值 flag(-u-S--split-string--unset),env -C/--chdir 记录 cwd 重定位,env -S 载荷递归分析,未知 env 选项拒绝;sudo -D/--chdir 记录 cwd 重定位。
  • R1-7 --git-dir 间接层:目标先做规范化(core realpathNearestExisting.git basename 处理;.git gitfile 沿 gitdir: 重定向跟踪;per-worktree 管理目录(<repo>/.git/worktrees/<name>)通过其 gitdir 文件解析到链接的 worktree 检出目录;无法解析的间接层拒绝。变更类的重定位目标必须在决策时刻真实存在(封堵 ln -s /outside link && git -C link reset 的 TOCTOU 形态)。
  • R1-8 相对目标基准:相对 --git-dir/--work-tree 一律按 -C 链的最终 cwd 解析,与 argv 顺序无关(与实测 git 行为一致)。
  • R1-9 只读白名单:重定位只读快速通道收缩为经过验证的无副作用集合(statusrev-parsels-filesgrepdescribecat-file);移除 diff/log/show/blame(textconv 会执行目标仓库配置的命令);任何 --output flag 都会把调用降级为变更类。
  • R1-10 命令值配置:键会执行命令的 -c/--config-env 赋值(alias.*core.editorcore.pagercore.fsmonitorcredential.helperfilter.*difftool.*mergetool.*)或值以 ! 开头的赋值,会把变更类/无法分类的调用标记为未解析。两步变体已被覆盖,因为 git config 本身不在只读集合。
  • R1-11 /fork 与 workspace memory 回归:根因是子进程门禁以"守卫管道存在"为条件,而本 PR 让该管道无条件存在。已验证隐藏 agent 的工具调用(经 createApprovalModeOverride 的 fork agent、经 createMemoryScopedAgentConfig 的 memory agent —— 两者都是 Object.create(sessionConfig) 委托)会穿过同一套受管守卫,从而穿过同一套守护进程侧内建策略。门禁现在改为以"真实外部 provider 已挂载"为条件(新增私有标记,仅在 provider 挂载时下发):仅内建守卫时 /fork 与 agent 版 remember/dream 恢复可用;挂载 provider 时保留 v1 限制。两个方向均有单元测试。

Suggestion(R1-12 … R1-18)— 全部处理

  • R1-12 复用 core 助手:守卫现在从 core 导入 splitCommandsrealpathNearestExistingisWithinRoot(修复续行/注释/规范化分歧)。parseGitInvocation/tokeniseSegment 未从 core 导出,且其语义是归因语义(无法归因则跳过)而非安全语义(无法证明则拒绝);导出它们需要修改 core,而 PR 设计明确禁止,因此守卫专用解析器保留在本地,但 flag 表与 core 对齐。
  • R1-13 ToolNames 字面量:提交 2b0e8cdcf7 的 serve 快速路径约束仍然成立,故保留字面量;用注释钉住其与 ToolNames 常量的对应关系,并增加单元测试断言集合一致,重命名会直接挂测试。
  • R1-14 文档:在 docs/users/qwen-serve.md 新增 "Built-in daemon Git relocation guard" 小节(策略、识别的重定位形式、只读集合、限制、/fork/memory 的行为区分);更新 docs/developers/qwen-serve-protocol.mdexternal_tool_guard 标签描述,说明该标签仅反映外部 provider,内建守卫无论标签是否存在都会生效;同步刷新设计文档的 policy/failure-semantics/non-goals 部分。
  • R1-15 每次调用开销:未挂载外部 provider 时,子进程对除 run_shell_command 外的所有工具调用本地直接放行(内建策略在结构上必然放行),免去串行的 child→daemon→child 往返;shell 命令始终走往返。有单元测试覆盖。
  • R1-16 mutation-killer 测试:五项全部补齐 ——(1)provider 拒绝一个本来会被放行的调用,逐字断言拒绝结果;(2)对 createBridge.mock.calls每一个条目断言 externalToolGuard,并用捕获的 handler 实际调用一次重定位变更命令;(3)在 bridgeClient.test.ts 中伪造子进程载荷的 workspaceCwd/effectiveCwd,断言以 entry 的可信值为准;(4)fail-closed 测试对两个可信字段做参数化;(5)嵌套执行器在 provider 有/无两种情况下对全部四个 ToolNames 参数化。
  • R1-17 无界拒绝原因:拒绝原因中的路径会剥除控制字符并截断,保证完整原因 ≤ 500 个 UTF-16 单元(用 400+ 字符路径和含制表符路径的测试验证)。
  • R1-18 heredoc 误报:在设计文档 non-goals 与用户文档中记录为已知限制(fail-closed 的误报,不是绕过);拒绝在复制的解析器中实现 heredoc 主体跟踪,因为实现不当可能重新打开绕过。

说明

  • 需要评审者留意的行为变化:重定位的 diff/log/show/blame 现在会被拒绝(它们原来在只读集合里);决策时刻目标不存在的重定位变更命令会被拒绝(git -C new-dir init 这类流程需要先让目录存在,例如先用 mkdir -p)。
  • 无冲突;--conflict false,未执行任何 merge。

验证

实际执行的命令(全部通过):

  • npm run build — 通过(exit 0)
  • npm run typecheck — 通过(exit 0,无 TS 错误)
  • npm run lint — 通过(0 错误)
  • 对三个改动文档执行 npx prettier --check — 通过
  • vitest run src/serve/daemon-git-worktree-guard.test.ts(packages/cli)— 92 通过
  • vitest run src/serve(packages/cli 全部 serve 套件)— 146 个文件,4217 通过 / 1 跳过
  • vitest run src/acp-integration/acpAgent.test.ts(packages/cli)— 386 通过
  • vitest run src/gemini.test.tsx(packages/cli)— 59 通过
  • vitest run src/serve/run-qwen-serve.test.ts(packages/cli)— 231 通过
  • vitest run src/serve/process-env-guard.test.ts(packages/cli)— 3 通过
  • vitest run(packages/acp-bridge 全套件,含新增的伪造载荷测试)— 25 个文件,1093 通过
  • npm run bundle — 通过(exit 0)
  • 集成测试(bundle 之后,QWEN_SANDBOX=false):cli/daemon-invocation-context.test.ts + cli/qwen-serve-routes.test.ts — 36 通过;cli/qwen-serve-baseline.test.ts + cli/qwen-serve-streaming.test.ts + cli/acp-integration.test.ts + cli/mock-acp-typecheck.test.ts + cli/run_shell_command.test.ts — 28 通过
  • 针对构建产物守卫的运行时探测脚本(真实临时目录 fixture、gitfile/符号链接/worktree 管理目录布局),逐一执行评审给出的攻击命令 — 全部 11 种 Critical 绕过形态现在均被拒绝,边界内的对照命令均放行

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x), Post Coverage Comment (ubuntu-latest, 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x), Post Coverage Comment (ubuntu-latest, 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round — no action needed (PR #8687)

No new actionable feedback arrived since the last evaluation (2026-08-07T14:57:42Z), so this round makes no changes and commits nothing.

What was evaluated

  • Reviews: no reviews newer than the cutoff. The two existing automated-reviewer CHANGES_REQUESTED reviews (submitted 2026-08-07T12:32:13Z and 2026-08-07T14:57:42Z, both against commit 107b5f64) were already evaluated in earlier rounds; their parser-bypass findings were addressed in commit b175cd72fb ("fix(serve): close daemon Git guard parser bypasses"), as recorded in the round-2 summary comment.
  • Inline / issue comments: the only comments newer than the cutoff are this bot's own round-2 summary and base-update notices, which carry no actionable feedback.
  • Failed checks: none. All completed checks are SUCCESS (or intentionally SKIPPED). Test (ubuntu-latest, Node 22.x) was still IN_PROGRESS when this round started — that is a re-run triggered by the base update, not a failure.
  • Conflicts: none reported (--conflict false); no merge performed.

Outcome

No code changes. Branch feat/daemon-git-worktree-guard stays at 2af741937b (merge of current main into the branch).

中文说明

Autofix 评审轮次 — 无需处理(PR #8687

自上次评估(2026-08-07T14:57:42Z)以来没有新的可处理反馈,因此本轮不做任何修改、不提交任何代码。

评估内容

  • 评审(Reviews): 截止时间之后没有新的评审。已有的两条自动化评审器 CHANGES_REQUESTED 评审(分别提交于 2026-08-07T12:32:13Z 和 2026-08-07T14:57:42Z,均针对提交 107b5f64)已在之前的轮次中评估过;其指出的解析器绕过问题已在提交 b175cd72fb("fix(serve): close daemon Git guard parser bypasses")中修复,详见第 2 轮总结评论。
  • 行内评论 / Issue 评论: 截止时间之后仅有的评论都是本机器人自己的第 2 轮总结和 base 更新通知,不包含可处理的反馈。
  • 失败的检查: 无。所有已完成的检查均为 SUCCESS(或按预期 SKIPPED)。Test (ubuntu-latest, Node 22.x) 在本轮开始时仍在运行中(IN_PROGRESS)—— 这是 base 更新触发的重新运行,并非失败。
  • 冲突: 无冲突报告(--conflict false);未执行任何合并。

结果

无代码改动。分支 feat/daemon-git-worktree-guard 保持在 2af741937b(将当前 main 合入该分支的合并提交)。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下:

Autofix round summary — PR #8687

Feedback triaged

This round contained no review, inline, or issue-level comments — only two failing checks:

Feedback point Classification Disposition
Test (ubuntu-latest Node 22.x): FAILURE Required — reproduced locally Fixed (root cause below)
Post Coverage Comment (ubuntu-latest 22.x): FAILURE Downstream of the Test job (needs: test, consumes its coverage-reports artifact) Expected to recover once the Test job passes

Diagnosis

Reproduced the Test job failure locally: npm run check:serve-fast-path-bundle ("Check serve fast-path bundle closure" step) failed on the PR head. The esbuild static closure of the serve pre-listen root run-qwen-serve.ts contained the entire @qwen-code/qwen-code-core barrel — 360 core source modules, including the forbidden Core shell tool runtime (packages/core/src/tools/shell.ts) and the forbidden vendor packages glob, chokidar, fzf, and @iarna/toml — bundled into a 5.4 MB chunk statically imported by the run-qwen-serve chunk.

Root cause: the parser rewrite in fix(serve): close daemon Git guard parser bypasses re-introduced a static value import from the core barrel into the new daemon-git-worktree-guard.ts (isWithinRoot, realpathNearestExisting, splitCommands), and run-qwen-serve.ts imported that module at module scope. At the merge base every core import in the serve entry was import type (erased) or a deep subpath import, keeping the barrel out of the pre-listen closure; core's index uses export * without sideEffects: false, so a single value import resolves to the whole barrel graph. This undid the earlier fix(daemon): keep Git guard off serve fast path commit, and the guard module's own comment documents its intent to stay free of core barrel imports.

Changes (1 file, +5/−1)

  • packages/cli/src/serve/run-qwen-serve.ts — removed the module-scope import { createDaemonToolGuard } from './daemon-git-worktree-guard.js' and dynamic-imported the module at the single handler-creation site inside runQwenServeImpl. This is the exact pattern the same function already uses for ./external-tool-guard-provider.js immediately above (and for bridge/spawnChannel below). Guard behavior is unchanged; the module (and its core helper imports) is simply no longer part of the static pre-listen bundle.

Conflict notes

None (--conflict false; origin/main is already merged into the branch head).

Verification

Commands actually run and their results:

  • npm run check:serve-fast-path-bundlefailed before the fix (glob / chokidar / fzf / @iarna/toml vendor packages and Core shell tool runtime in the static closure of run-qwen-serve); passed after the fix ("Startup bundle closure checks passed", including the ACP boundary, sdk-impl protocol, and entry-bootstrap checks)
  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx prettier --check on the 7 PR-touched source files — passed
  • vitest focused, packages/acp-bridge src/bridgeClient.test.ts — 98 passed
  • vitest focused, packages/cli src/serve/daemon-git-worktree-guard.test.ts + src/serve/run-qwen-serve.test.ts — 323 passed
  • vitest focused, packages/cli src/acp-integration/acpAgent.test.ts — 386 passed
  • npm run test:ci under a CI-equivalent clean environment (fresh HOME, empty API keys, host QWEN_* session variables unset) — all packages green. The only residual local failure was AuthDialog.test.tsx TUI-input tests, which the test file itself skips under CI=true (isUnreliableTuiInputEnvironment gate); verified the file passes with CI=true (7 passed, 18 skipped), so it cannot fire on GitHub Actions
  • npm run test:integration:no-ak:sandbox:none — 11 test files / 130 tests passed (includes qwen-serve-routes, qwen-serve-streaming, and daemon-invocation-context, which exercise the changed daemon spawn path through the bundled CLI)
  • npm run check-i18n — passed
  • npm run check:lockfile — passed
  • Settings schema — the PR touches no settings source; packages/vscode-ide-companion/schemas/settings.schema.json is clean

Commit: b1b7606052 fix(serve): keep daemon Git guard out of serve fast-path closure

中文说明

Autofix 轮次总结 — PR #8687

处理的反馈

本轮没有任何 review、行内或 issue 级评论——只有两个失败的检查:

反馈点 分类 处理结果
Test (ubuntu-latest Node 22.x):失败 必需项——已在本地复现 已修复(根因见下)
Post Coverage Comment (ubuntu-latest 22.x):失败 Test 作业的下游依赖(needs: test,消费其 coverage-reports 产物) Test 作业通过后预期自动恢复

诊断

在本地复现了 Test 作业的失败:npm run check:serve-fast-path-bundle("Check serve fast-path bundle closure" 步骤)在 PR 头上失败。serve 预监听根模块 run-qwen-serve.ts 的 esbuild 静态闭包中包含了整个 @qwen-code/qwen-code-core barrel——共 360 个 core 源模块,其中包括被禁止的 Core shell tool runtimepackages/core/src/tools/shell.ts)以及被禁止的 vendor 包 globchokidarfzf@iarna/toml——它们被打进一个 5.4 MB 的 chunk,并被 run-qwen-serve chunk 静态导入。

根因:fix(serve): close daemon Git guard parser bypasses 中的解析器重写,在新文件 daemon-git-worktree-guard.ts 中重新引入了来自 core barrel 的静态导入(isWithinRootrealpathNearestExistingsplitCommands),而 run-qwen-serve.ts 又在模块顶层静态导入了该模块。在合并基点上,serve 入口中的所有 core 导入要么是 import type(编译时擦除),要么是深子路径导入,因此 barrel 一直不在预监听闭包中;core 的 index 使用 export * 且未标记 sideEffects: false,所以单个值导入会解析到整个 barrel 依赖图。这抵消了更早的 fix(daemon): keep Git guard off serve fast path 提交,而且该 guard 模块自身的注释也明确记录了它有意避免 core barrel 导入。

变更(1 个文件,+5/−1)

  • packages/cli/src/serve/run-qwen-serve.ts —— 移除模块顶层的 import { createDaemonToolGuard } from './daemon-git-worktree-guard.js',改为在 runQwenServeImpl 内唯一的 handler 创建处动态导入该模块。这与同一函数中紧邻上方对 ./external-tool-guard-provider.js(以及下方对 bridge/spawnChannel)使用的模式完全一致。Guard 的行为不变;只是该模块(及其 core 辅助导入)不再属于静态预监听 bundle 的一部分。

冲突说明

无(--conflict falseorigin/main 已合并进当前分支头)。

验证

实际执行的命令及结果:

  • npm run check:serve-fast-path-bundle —— 修复前失败(glob / chokidar / fzf / @iarna/toml vendor 包以及 Core shell tool runtime 出现在 run-qwen-serve 的静态闭包中);修复后通过("Startup bundle closure checks passed",包括 ACP 边界、sdk-impl 协议链和入口引导完整性检查)
  • npm run build —— 通过
  • npm run typecheck —— 通过
  • npm run lint —— 通过
  • 对 PR 改动的 7 个源文件执行 npx prettier --check —— 通过
  • vitest 定向运行,packages/acp-bridgesrc/bridgeClient.test.ts —— 98 个通过
  • vitest 定向运行,packages/clisrc/serve/daemon-git-worktree-guard.test.ts + src/serve/run-qwen-serve.test.ts —— 323 个通过
  • vitest 定向运行,packages/clisrc/acp-integration/acpAgent.test.ts —— 386 个通过
  • 在 CI 等效的洁净环境下运行 npm run test:ci(全新 HOME、清空 API key、移除宿主机的 QWEN_* 会话变量)—— 所有包均为绿色。唯一的本地残留失败是 AuthDialog.test.tsx 的 TUI 输入测试,而该测试文件自身会在 CI=true 时跳过这些用例(isUnreliableTuiInputEnvironment 门控);已验证在 CI=true 下该文件通过(7 个通过、18 个跳过),因此不会在 GitHub Actions 上触发
  • npm run test:integration:no-ak:sandbox:none —— 11 个测试文件 / 130 个测试全部通过(包括 qwen-serve-routesqwen-serve-streamingdaemon-invocation-context,它们通过打包后的 CLI 验证了本次改动的 daemon 启动路径)
  • npm run check-i18n —— 通过
  • npm run check:lockfile —— 通过
  • Settings schema —— 本 PR 未改动任何 settings 源;packages/vscode-ide-companion/schemas/settings.schema.json 保持干净

提交:b1b7606052 fix(serve): keep daemon Git guard out of serve fast-path closure

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。

Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ❌ not passed — findings reported (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 1842 passed · 13 failed · 1855 total

中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:1842 通过 · 13 失败 · 1855 总计

Verification report

PR 8687 Deep Verification — feat(daemon): guard cross-worktree Git mutations

Verdict: findings — 1855 scripted assertions: 1842 pass / 13 fail. Verified head b1b76060528f81243cd768091e073aaaca3fa3cf merged onto base 4ec0371e616decbe723cec250e19943b226d31e1. The central claim is proven load-bearing by an A/B against the base build, and every targeted gate is green — but a sibling-sweep battery reproduced 12 allow-where-deny-expected cells (11 reachable evasions — argv-prefix wrapper / command-executor forms plus the monitor tool — and 1 mitigated by the shell tool's own directory validation) and one defensive clause is unpinned by any test, which is why the verdict is findings rather than merge-ready.

中文摘要

结论:findings(中央机制经 A/B 证实有效且载荷真实,但存在可复现的绕过)。

  • A/B 结论:在 head 上,真实 createAcpSessionBridge + 真实 guard(按 run-qwen-serve.ts 的方式接线)对 10 个判定单元全部给出正确决策(5 拒 5 放);伪造的 workspaceCwd/effectiveCwd 字段被忽略。在 base 上不存在任何 daemon 决策点(10 次咨询全部 -32601 Method not found),且同一条跨 worktree 变更命令会真实落地(见 01-ab-head-guard-decisions.png / 02-ab-base-no-decision-point.png)。中央改动是载荷真实的。
  • findings:旁路电池发现 11 个可达绕过——nice/setsid/stdbuf/time/ionice/watch/script 前缀包裹、find -execxargs,以及 monitor 工具(guard 只钩住 run_shell_commandmonitor 同样执行 shell 命令却完全不被检查)。另有一个未受测试约束的防御性子句(--output,变异 M2 存活)。详见 Findings 表。
  • 未覆盖:逐 commit 归因(浅克隆仅 3 个 commit 可达)、Windows 路径、真实模型 E2E(用接线缝隙的 fake ACP child 驱动,而非真实模型回环)、仓库全量测试(只跑受影响 workspace)。

Scope

  • Central claim: under qwen serve, a model-issued run_shell_command that relocates Git (-C / --work-tree / --git-dir / env / wrapper) outside the session's live effectiveCwd is denied by the daemon before execution when mutating or unclassifiable, while relocated read-only Git and in-boundary mutations remain allowed.
  • Secondary claims: (1) the guard is always installed for daemon ACP children even with no external provider, child-forged workspace fields are ignored, and child features (/fork, workspace memory) stay available without a provider; (2) the guard stays out of the serve fast-path closure via dynamic import.

Central claim — A/B load-bearing proof

Harness harness-ab.mjs boots the real createAcpSessionBridge (compiled dist) with a FakeAgent ACP child over an in-memory NDJSON channel. The fake child issues the same qwen/control/external_tool_guard/prepare ext method a managed qwen --acp child issues before executing a tool call. Wiring mirrors run-qwen-serve.ts exactly: head passes externalToolGuard = createDaemonToolGuard(undefined) and the child acknowledges enforcement; base passes no externalToolGuard and no acknowledgement (matching base run-qwen-serve.ts with no provider). Scripted expectations re-verified by harness-ab-assert.mjs (14/14 pass).

# cell (child → daemon) HEAD decision BASE outcome
H1 git -C <outside> reset --hard deny (reason names outside path) -32601 no decision point
H2 git -C <outside> status --short allow -32601
H3 git -C nested reset --hard (inside) allow -32601
H4 H1 + forged workspaceCwd/effectiveCwd=<outside> deny (forgery ignored) -32601
H5 write_file (non-shell tool) allow -32601
H6 git --work-tree=<outside> --git-dir=<outside>/.git clean -fd deny -32601
H7 pwd && git -C <outside> reset --hard deny -32601
H8 git -C "$OTHER_WORKTREE" reset --hard (dynamic) deny (fail-closed) -32601
H9 pwd allow -32601
H10 agent tool, no provider allow -32601
B-exec relocated mutation executed as base child would file tampered, reset --hard restores it (restored=true)

Base control: base BridgeClient.handleExternalToolGuardPrepare throws RequestError.methodNotFound when no handler is configured (verified against HEAD^1 source and live), and base run-qwen-serve.ts only sets the child marker env / passes externalToolGuard when a provider exists — so a base child never consults and the command runs. The B-exec cell materializes exactly the cross-checkout mutation the PR exists to block. Control hygiene: packages/core is byte-identical between the arms (git diff HEAD^1..HEAD -- packages/core empty, same for all package.json/package-lock.json), so the shared root node_modules is a clean control; readlink -f node_modules/@qwen-code/qwen-code-core → head-tree core, which is provably unchanged. Base acp-bridge was rebuilt in a scratch worktree (tsc -b --noCheck, emit-only); its dist carries 0 PROVIDER markers vs 2 at head, confirming the arms differ only by the PR.

Witnesses: 01-ab-head-guard-decisions.png, 02-ab-base-no-decision-point.png.

Secondary claim 2 (fast path): verified statically — run-qwen-serve.ts has no module-scope import of the guard; the only reference is await import('./daemon-git-worktree-guard.js') inside runQwenServeImpl (line 2804). Bundle-size measurement was not run (see Not covered).

Findings

Severity: the guard is a defense-in-depth policy filter and the PR explicitly scopes it as "a narrow shell-token policy rather than a complete shell interpreter" — the findings below are residual evasions of that stated goal, not a collapse of the mechanism (which the A/B proves works for everything it covers). They are worth a reviewer's attention because several sit in the same class the PR already handles.

F1 — Medium: relocation evades detection behind unrecognized argv-prefix wrappers and command executors

The wrapper set (env, sudo, timeout, eval, nohup, exec, command, sh/bash/…, cd/pushd/popd) is incomplete. analyzeRun returns other for any unrecognized program and stops, so a relocation carried behind it is never inspected. Measured allow on the head build for every row (all mutating, all targeting the outside repo):

nice git -C <outside> reset --hard            nice -n 10 git -C <outside> reset --hard
setsid git -C <outside> reset --hard          stdbuf -oL git -C <outside> reset --hard
time git -C <outside> reset --hard            ionice -c2 -n7 git -C <outside> reset --hard
watch -n 1 git -C <outside> reset --hard      script -q /dev/null git -C <outside> reset --hard
find . -name '*.txt' -exec git -C <outside> reset --hard \;
echo x | xargs git -C <outside> reset --hard

Reproduce: node tmp/pr8687-verify-20260808-002426/harness-bypass.mjs (rows marked FAIL are allow-where-deny-expected; witness 03-bypass-battery-head.png). Notes: time/watch carry runtime caveats (time is a shell keyword, watch wants a tty) but the guard's pre-execution decision is allow regardless; nice/setsid/stdbuf/ionice/script/find -exec/xargs execute the wrapped git unchanged. The PR's commit message claims "shell wrapper … invocations … fail closed"; these siblings of nohup/timeout do not. A safe extension needs per-wrapper flag tables with fail-closed fallback (the consumeEnvWrapper pattern), which is why no patch is suggested here — a naive "skip one token" fix would misparse nice -n 10 into a false allow, worse than the gap. find -exec/xargs need argument-level inspection beyond a prefix scan; if that stays out of scope, naming them as known limitations in the design doc would keep the contract honest.

F2 — Medium: the monitor tool executes model-supplied shell commands but is entirely outside the guard

evaluateBuiltInGuard only inspects toolName === 'run_shell_command'; with no external provider the child's managed guard additionally resolves every non-shell tool locally (acpAgent.ts createManagedExternalToolGuard), so a monitor call never reaches the daemon decision point at all. Measured: guard returns allowed=true for {toolName:'monitor', arguments:{command:'git -C <outside> reset --hard'}} (harness-bypass row monitor tool). MonitorTool (registered unconditionally, core/src/config/config.ts:8255) validates only directory (workspace-bounded) and background operators — never the command's relocation — and spawns with cwd: params.directory || targetDir (core/src/tools/monitor.ts:364). The threat statement ("a daemon model can start a shell … and relocate Git") is reachable through this door without any wrapper trick. Cheapest closure: treat monitor like run_shell_command in evaluateBuiltInGuard (same command argument shape) and route it to the daemon in the child-side local-resolution shortcut.

F3 — Low: the guard trusts model-controlled arguments.directory as basis cwd and does not itself enforce it lies inside effectiveCwd

Measured cell: run_shell_command with arguments.directory = <outside> and bare git reset --hardallowed=true. The guard's relocated predicate compares basis-vs-entry cwd, and since both are the forged directory, it sees "no relocation" and skips containment. Unreachable in production — the shell tool independently rejects an out-of-workspace directory (core/src/tools/shell.ts:5127, same check in monitor.ts:783) — so this is a defense-in-depth gap, not a live bypass: the guard's soundness depends on an invariant enforced elsewhere. Making the guard self-contained (deny when startDirectory canonicalizes outside effectiveCwd) would remove the dependency.

F4 — Low: the --output clause is unpinned and currently has no behavioral effect (mutation survivor)

Mutation M2 (drop !invocation.hasOutputFlag from the read-only early-return) survived 92/92. None of the six allowlisted subcommands (cat-file, describe, grep, ls-files, rev-parse, status) accepts a file-writing --output flag — the flag matters only for diff/log/show, which are excluded from the allowlist entirely. Classification: defensive clause, dead for the current allowlist (not dead code in intent — it becomes live if a --output-capable subcommand is ever allowlisted). The suite pins nothing along this axis today; if the clause stays, the pinning fixture is git -C <outside> status --output=x (which would go red only once such a subcommand enters the set).

F5 — Nit: two allowlist observations, both measured safe on this box

  • git -C <outside> status is allowlisted, and git status can refresh the target repo's index (a metadata-only write into <outside>/.git/index). Harmless here and presumably weighed by the author ("verified to neither write files … on the managed output path"), noted for completeness.
  • The textconv risk that excluded diff/log/show/blame was probed for grep: a target repo rigged with diff.evil.textconv='touch <marker>' + *.txt diff=evil did not fire on git grep in this container (git 2.39.5) — the allowlist entry is safe on this git version; newer gits may differ.

Not covered

  • Per-commit attribution: the checkout is depth-2 (merge commit, base tip, PR head only). git rev-list HEAD^1..HEAD^2 returns 1 commit while the metadata snapshot lists 6 — a shallow-boundary artifact — so the aggregate HEAD^1..HEAD diff was verified and per-commit claims (e.g. which commit closed which review bypass) were not individually exercised.
  • Real-model E2E: the A/B drives the identical wire seam the real child uses (the externalToolGuardPrepare ext method through the real Bridge + real guard), but no live model round-trip was run; model-side elicitation of the tool call is out of scope for the guard itself.
  • Windows/Linux path-shape behavior (PR marks Windows/Linux ⚠️; this round ran Linux-only), linked-worktree exceptions, confirmation UX, and direct user-entered daemon shell commands — all declared out of scope by the PR.
  • Repo-wide gates: only the affected workspaces ran (cli targeted files + full acp-bridge, 1094 tests). A repo-root typecheck/test was not run, so the PR body's claim that the root build is blocked by missing channel packages is neither confirmed nor refuted here; what I can attest is that this checkout resolves @qwen-code/channel-github (the PR author's collection failure does not reproduce) and that both affected workspaces typecheck clean.
  • Bundle-size proof of the fast-path claim: verified by import-graph inspection only (single dynamic import), not by measuring the esbuild closure.
  • run-qwen-serve.test.ts collected and passed here (231/231); the PR's note that it could not be collected was specific to the author's checkout missing @qwen-code/channel-github.

Methodology

Environment: CI merge-ref checkout (HEAD = merge commit) in a node:22-bookworm container, npm ci + npm run build pre-run at head; base arm rebuilt in scratch worktree tmp/base-tree (emit-only tsc -b --noCheck, control purity asserted above). The A/B harness instantiated the real compiled createAcpSessionBridge with an in-memory NDJSON channel and a FakeAgent child speaking the production ext-method protocol; the bypass battery and perf ladder drove the head-built guard module directly. Raw logs in logs/ (ab-head.log, ab-base.log, ab-assert.log, bypass-head.log, perf-ladder.log), harnesses kept in the artifact dir for rerun. Mutation runs applied single-hunk edits, ran the affected suite, and restored byte-identically (git status clean at the end). Perf ladder: 5 rung sets up to 100 KB, linear throughout (worst 18.6 ms at 56 KB), recursion fail-closed at depth > 3.

Evidence images

01-ab-head-guard-decisions

02-ab-base-no-decision-point

03-bypass-battery-head

04-mutation-matrix

05-perf-ladder-linear

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round — no action needed (PR #8687)

This round found no actionable feedback, so no code changes were made and nothing was committed.

What was triaged

  • Reviews: no new review findings. The most recent automated reviewer review (2026-08-08T00:21:41Z) is an APPROVE — "LGTM, looks ready to ship."
  • Inline comments: none newer than the last evaluation (2026-08-07T18:28:17Z). All earlier inline findings (the Critical tokenizer/parser fail-open items) were already addressed in prior rounds by fix(serve): close daemon Git guard parser bypasses and fix(serve): keep daemon Git guard out of serve fast-path closure.
  • Issue-level comments: only CI-bot status notes (sandboxed verification running) and a maintainer /triage command trigger — neither carries feedback to address.
  • Failed checks: none. All reported checks are green (16 SUCCESS, 12 SKIPPED).
  • Base conflicts: none reported (--conflict false), so origin/main was not merged.

Result

The branch stays at b1b7606052 (fix(serve): keep daemon Git guard out of serve fast-path closure), already pushed and matching the live PR head. No verification commands were run this round because no code was changed.

中文说明

Autofix 评审轮次 — 无需操作(PR #8687

本轮没有发现需要处理的反馈,因此未做任何代码改动,也没有提交任何内容。

分诊内容

  • 评审(Reviews): 没有新的评审意见。最近一次自动评审(2026-08-08T00:21:41Z)为 APPROVE —— “LGTM, looks ready to ship.”
  • 行内评论(Inline comments): 自上次评估(2026-08-07T18:28:17Z)之后没有新的行内评论。此前所有行内问题(Critical 级别的 tokenizer/parser fail-open 项)已在之前的轮次中由 fix(serve): close daemon Git guard parser bypassesfix(serve): keep daemon Git guard out of serve fast-path closure 处理完毕。
  • Issue 级评论: 仅有 CI 机器人的状态通知(沙箱验证进行中)以及维护者的 /triage 命令触发,均不包含需要处理的反馈。
  • 失败的检查: 无。所有报告的检查均为绿色(16 个 SUCCESS,12 个 SKIPPED)。
  • 与基线分支的冲突: 无(--conflict false),因此未合并 origin/main

结果

分支保持在 b1b7606052fix(serve): keep daemon Git guard out of serve fast-path closure),该提交已推送且与 PR 的实时 head 一致。本轮未运行任何验证命令,因为没有改动任何代码。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

Two more escapes doudouOUC reproduced in the fail-closed removal rule.

- The rule's early `continue` fired even when `unset`/`unalias`/`export` was
  itself a recorded function and the operands named only untracked state, so a
  shadowing `unset(){ git -C <outside> …; }; unset other` was classified as a
  harmless builtin removal and never reached the shadow dispatch that replays
  the relocating body. The branch now runs only when the program is not a
  shadowed function (a `command`/`builtin` prefix still forces the builtin).
- The removal never dropped tracked variables, so `A=nested; unset A; cd $A`
  kept expanding the stale in-bounds value while bash's `unset A` leaves `$A`
  empty and `cd $A` lands at $HOME. `unset NAME`/`unset -v NAME` now deletes
  the shell-local, turning the later `$A` into an unresolved reference the cd
  fails closed on. `unset -f` is functions-only and leaves variables intact.

Adds regressions for both.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

Not reviewed: reverse audit — stopped at the 3-round huge-diff cap without converging (rounds 1, 2 and 3 each reported new verified findings; chunk 5 retired after two substantive dry audits).

Not reviewed: build-and-test — CI's 'Integration Tests (CLI, No Sandbox)' job was skipped and the integration suite was not run locally (workspace unit suites covering the changed packages passed).

Not explored to full depth (tool budget reached): "PR #8687 adds a daemon host-side guard for qwen serve that…": none — all checks I started were completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": none — all three candidates were verified to ground truth; consumeSudoWrapper / consumeTimeoutWrapper / consumeShellWrapper internals were only examined at th…; chunk 15: none — all checks I identified were completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": none — all six layers were walked at the doc-claim level my chunk defines.; "You are review agent reverse-audit — Reverse audit agent…": end-to-end session-identity trace of resumed background agents' context-less shell calls (background-agent-resume.ts → new Config's getSessionId() → bridge o…, and 3 more.

[Critical] R9-3 consumeTimeoutWrapper never checks token.dynamic; a blind index++ eats a dynamic duration operand and the wrapped command falls to the marker net unanalyzed

[Critical] R9-26 consumeEnvWrapper -u/--unset skips a dynamic value token with index += 2 without ever setting state.unresolved

[Critical] R9-59 paren-form function definitions that do not START a segment are never recorded (functionHeader is segment-start anchored; readDefinition/readFunctionName fallbacks match shapes shell-quote cannot emit; function is absent from LEADING_SHELL_KEYWORDS)

[Critical] R8-10 prompt-less guard requests are gated only by channel-level ownsSession, so any sibling sessionId owned by the same multiplexed channel passes and receives the nominated session's trusted effectiveCwd (constructor default is () => true)

[Critical] R9-23 git config core.worktree is unmodeled — subcommand config stops option scanning, nothing records the redirect, and a later mutation honors the planted redirect (core.hooksPath gives code execution)

[Critical] R9-22 the env-key sets miss dynamic-loader and shell-startup keys (LD_PRELOAD, LD_LIBRARY_PATH, DYLD_*, PYTHONPATH, GITPERLLIB, PERL5LIB, BASH_ENV, ZDOTDIR) — prefix assignments execute outside-boundary code unflagged

[Critical] R9-2 process substitutions <( )/>( ) split runs — git > >(true) -C /outside reset --hard leaves [-C /outside reset --hard] as an other run with no git word that evaluateUnrecognizedRun clears; sh -c > >(true) "$(cat evil)" severs the wrapper payload the same way

[Critical] R9-25 redirectOperand is cleared only on string tokens; a >(pwn) body's first word gets redirect:true and is skipped by both replay gates (readProgramWord/runFromProgramWord) and the marker gate

[Critical] R9-27 consumeSudoWrapper blind-skips SUDO_VALUE_FLAGS values — sudo -u $U with U='root --chdir=' never marks unresolved and never sees the injected chdir

[Critical] R9-17 CHDIR_OPTION_PATTERN is a cross-shell union (-q, -s, -@ are zsh/ksh-only) applied to bash cd — the guard tracks a phantom cwd that bash rejects (bash usage offers only [-L|[-P [-e]] [-@]])

[Critical] R9-56 the command/builtin branch skips all - flags and re-classifies the next word — command -v cd deep/dir classifies as kind cd with target deep/dir (a phantom cd; command -v only prints)

[Critical] R7-9 declare -f / readonly -f phantom export — the export analysis adds every non-dash operand to exportedFunctions whenever any operand is -f; the payload-scope filter then keeps the harmless body and invokeDefinedBody replays it

[Critical] R8-51 after consumeEnvWrapper/consumeSudoWrapper/consumeTimeoutWrapper the loop re-classifies with no child-process marker — cd ; env cd ; git reset --hard updates trackedCwd inside the wrapper and is judged in-bounds

[Critical] R9-29 the env -S/--split-string payload branch returns the payload with propagatesCwd:false and no importsExportedFunctions (scope {}) while the plain env bash -c path sets importsExportedFunctions — exported shadows are invisible only through the -S form

[Critical] R9-31 any unrecognized prefix program (nice, setsid, strace, stdbuf, …) hides a wrapped bash -c pwn payload — evaluateUnrecognizedRun consults only token text, never definedBodies/exportedFunctions/gitShapedNames

[Critical] R8-15 on Windows resolvePhysicalPath starts at path.parse(target).root but still splits the FULL target — C:\work\subdir resolves to C:\C:\work\subdir, yielding UNRESOLVED denials or a poisoned trackedCwd on every relocated run

[Critical] R9-44 the discovery walk's 64-iteration cap fails OPEN — resolveDiscoveredRepository returns undefined on loop exhaustion and denyOutsideDiscoveredRepository treats undefined as no denial

[Critical] R9-61 a nested function-header line inside an in-progress definition unconditionally overwrites insideDefinition and resets definitionBody — the outer body is discarded unrecorded

[Critical] R8-8 function-definition end detection is raw segment.includes('}') + definitionBody.lastIndexOf('}') with no quote awareness — a quoted } closes the definition early and the remainder evaluates at its original position

[Critical] R7-6 cd $VAR where VAR is a known-but-empty local expands to {text:'', dynamic:false} — trackedCwd stays unchanged via path.resolve(trackedCwd, '') while bash drops the empty word and cds to $HOME

[Critical] R9-57 a cd whose target does not exist fails silently in bash (cwd unchanged) but the guard records the lexical phantom; realpathNearestExistingAsync preserves phantom tails, so a depth-matched relative -C folds back onto an in-bounds decoy

[Critical] R2-23 realpathNearestExistingAsync contract gap — a dangling intermediate symlink returns the input unchanged; the access() walk-up re-appends missing segments lexically, and the reported-cwd acceptance path consumes the result with no full-existence requirement

[Critical] R8-11 a child-reported invocationCwd is accepted with no binding to the invoking agent's assigned worktree — the acceptance key is the session-level worktrees dir, so an agent assigned worktree A can nominate and mutate sibling worktree B

[Critical] R8-36 reported-cwd acceptance cannot place AgentTool-provisioned worktrees when the session is bound to a monorepo subdirectory — the else branch checks the global storage dir while AgentTool provisions under /.qwen/worktrees, so both isWithinRoot checks fail and the call is over-denied

[Critical] R9-21 the guard replays alias bodies as if aliases expanded, but the daemon executes via non-interactive bash -c where aliases never expand (no expand_aliases anywhere) — the pinned allowed:true test blesses fictitious modeling

[Critical] R9-1 GIT_UNRESOLVABLE_ENV_KEYS omits HOME and XDG_CONFIG_HOME (global config ⇒ core.hooksPath ⇒ code execution on hook firing) and GIT_PROXY_COMMAND (its config twin core.gitProxy IS in GIT_COMMAND_CONFIG_KEY_PATTERNS; PATH/GIT_EXEC_PATH are already unresolved keys)

[Critical] R9-38 GIT_TRACE/GIT_TRACE2*/GIT_QUARANTINE_PATH env keys redirect git's trace/quarantine output to arbitrary file paths outside the boundary; recordEnvAssignment ignores them (git writes argv-embedding trace lines outside on any invocation)

[Critical] R8-64 worktree isolation is derived from live .git discovery with no monotonicity — rm .git is unmodeled and re-widens the boundary to the parent checkout on the next call; a corrupted gitfile fails closed but a deleted one fails open

中文说明

仅完成部分审查,审查缺口已披露。

未审查:reverse audit — stopped at the 3-round huge-diff cap without converging (rounds 1, 2 and 3 each reported new verified findings; chunk 5 retired after two substantive dry audits)。

未审查:build-and-test — CI's 'Integration Tests (CLI, No Sandbox)' job was skipped and the integration suite was not run locally (workspace unit suites covering the changed packages passed)。

未探索到全部深度(达到工具调用预算):"PR #8687 adds a daemon host-side guard for qwen serve that…"none — all checks I started were completed within budget."You are review agent reverse-audit — Reverse audit agent…"none — all three candidates were verified to ground truth; consumeSudoWrapper / consumeTimeoutWrapper / consumeShellWrapper internals were only examined at th…;chunk 15:none — all checks I identified were completed within budget."You are review agent reverse-audit — Reverse audit agent…"none — all six layers were walked at the doc-claim level my chunk defines."You are review agent reverse-audit — Reverse audit agent…"end-to-end session-identity trace of resumed background agents' context-less shell calls (background-agent-resume.ts → new Config's getSessionId() → bridge o…,另有 3 条。

[Critical] R9-3 consumeTimeoutWrapper never checks token.dynamic; a blind index++ eats a dynamic duration operand and the wrapped command falls to the marker net unanalyzed

[Critical] R9-26 consumeEnvWrapper -u/--unset skips a dynamic value token with index += 2 without ever setting state.unresolved

[Critical] R9-59 paren-form function definitions that do not START a segment are never recorded (functionHeader is segment-start anchored; readDefinition/readFunctionName fallbacks match shapes shell-quote cannot emit; function is absent from LEADING_SHELL_KEYWORDS)

[Critical] R8-10 prompt-less guard requests are gated only by channel-level ownsSession, so any sibling sessionId owned by the same multiplexed channel passes and receives the nominated session's trusted effectiveCwd (constructor default is () => true)

[Critical] R9-23 git config core.worktree is unmodeled — subcommand config stops option scanning, nothing records the redirect, and a later mutation honors the planted redirect (core.hooksPath gives code execution)

[Critical] R9-22 the env-key sets miss dynamic-loader and shell-startup keys (LD_PRELOAD, LD_LIBRARY_PATH, DYLD_*, PYTHONPATH, GITPERLLIB, PERL5LIB, BASH_ENV, ZDOTDIR) — prefix assignments execute outside-boundary code unflagged

[Critical] R9-2 process substitutions <( )/>( ) split runs — git > >(true) -C /outside reset --hard leaves [-C /outside reset --hard] as an other run with no git word that evaluateUnrecognizedRun clears; sh -c > >(true) "$(cat evil)" severs the wrapper payload the same way

[Critical] R9-25 redirectOperand is cleared only on string tokens; a >(pwn) body's first word gets redirect:true and is skipped by both replay gates (readProgramWord/runFromProgramWord) and the marker gate

[Critical] R9-27 consumeSudoWrapper blind-skips SUDO_VALUE_FLAGS values — sudo -u $U with U='root --chdir=' never marks unresolved and never sees the injected chdir

[Critical] R9-17 CHDIR_OPTION_PATTERN is a cross-shell union (-q, -s, -@ are zsh/ksh-only) applied to bash cd — the guard tracks a phantom cwd that bash rejects (bash usage offers only [-L|[-P [-e]] [-@]])

[Critical] R9-56 the command/builtin branch skips all - flags and re-classifies the next word — command -v cd deep/dir classifies as kind cd with target deep/dir (a phantom cd; command -v only prints)

[Critical] R7-9 declare -f / readonly -f phantom export — the export analysis adds every non-dash operand to exportedFunctions whenever any operand is -f; the payload-scope filter then keeps the harmless body and invokeDefinedBody replays it

[Critical] R8-51 after consumeEnvWrapper/consumeSudoWrapper/consumeTimeoutWrapper the loop re-classifies with no child-process marker — cd ; env cd ; git reset --hard updates trackedCwd inside the wrapper and is judged in-bounds

[Critical] R9-29 the env -S/--split-string payload branch returns the payload with propagatesCwd:false and no importsExportedFunctions (scope {}) while the plain env bash -c path sets importsExportedFunctions — exported shadows are invisible only through the -S form

[Critical] R9-31 any unrecognized prefix program (nice, setsid, strace, stdbuf, …) hides a wrapped bash -c pwn payload — evaluateUnrecognizedRun consults only token text, never definedBodies/exportedFunctions/gitShapedNames

[Critical] R8-15 on Windows resolvePhysicalPath starts at path.parse(target).root but still splits the FULL target — C:\work\subdir resolves to C:\C:\work\subdir, yielding UNRESOLVED denials or a poisoned trackedCwd on every relocated run

[Critical] R9-44 the discovery walk's 64-iteration cap fails OPEN — resolveDiscoveredRepository returns undefined on loop exhaustion and denyOutsideDiscoveredRepository treats undefined as no denial

[Critical] R9-61 a nested function-header line inside an in-progress definition unconditionally overwrites insideDefinition and resets definitionBody — the outer body is discarded unrecorded

[Critical] R8-8 function-definition end detection is raw segment.includes('}') + definitionBody.lastIndexOf('}') with no quote awareness — a quoted } closes the definition early and the remainder evaluates at its original position

[Critical] R7-6 cd $VAR where VAR is a known-but-empty local expands to {text:'', dynamic:false} — trackedCwd stays unchanged via path.resolve(trackedCwd, '') while bash drops the empty word and cds to $HOME

[Critical] R9-57 a cd whose target does not exist fails silently in bash (cwd unchanged) but the guard records the lexical phantom; realpathNearestExistingAsync preserves phantom tails, so a depth-matched relative -C folds back onto an in-bounds decoy

[Critical] R2-23 realpathNearestExistingAsync contract gap — a dangling intermediate symlink returns the input unchanged; the access() walk-up re-appends missing segments lexically, and the reported-cwd acceptance path consumes the result with no full-existence requirement

[Critical] R8-11 a child-reported invocationCwd is accepted with no binding to the invoking agent's assigned worktree — the acceptance key is the session-level worktrees dir, so an agent assigned worktree A can nominate and mutate sibling worktree B

[Critical] R8-36 reported-cwd acceptance cannot place AgentTool-provisioned worktrees when the session is bound to a monorepo subdirectory — the else branch checks the global storage dir while AgentTool provisions under /.qwen/worktrees, so both isWithinRoot checks fail and the call is over-denied

[Critical] R9-21 the guard replays alias bodies as if aliases expanded, but the daemon executes via non-interactive bash -c where aliases never expand (no expand_aliases anywhere) — the pinned allowed:true test blesses fictitious modeling

[Critical] R9-1 GIT_UNRESOLVABLE_ENV_KEYS omits HOME and XDG_CONFIG_HOME (global config ⇒ core.hooksPath ⇒ code execution on hook firing) and GIT_PROXY_COMMAND (its config twin core.gitProxy IS in GIT_COMMAND_CONFIG_KEY_PATTERNS; PATH/GIT_EXEC_PATH are already unresolved keys)

[Critical] R9-38 GIT_TRACE/GIT_TRACE2*/GIT_QUARANTINE_PATH env keys redirect git's trace/quarantine output to arbitrary file paths outside the boundary; recordEnvAssignment ignores them (git writes argv-embedding trace lines outside on any invocation)

[Critical] R8-64 worktree isolation is derived from live .git discovery with no monotonicity — rm .git is unmodeled and re-widens the boundary to the parent checkout on the next call; a corrupted gitfile fails closed but a deleted one fails open

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

Comment on lines +1857 to +1858
* best-effort: only the first heredoc on a line is handled, which is the
* shape a model emits, and anything unrecognised is left untouched.

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] R8-58 (round-8 ledger, still stands): stripHeredocBodies strips only the FIRST heredoc per line and does not recognize backslash-quoted delimiters (<<\EOF); unrecognized heredoc forms leave their body lines to be parsed as real commands. — Failure scenario: cd /tmp/outside + cat <<\\EOF + body cd <session-root> + git reset --hard — the regex fails on the delimiter, the body cd launders trackedCwd back inside, and git reset --hard is ALLOWED while bash executes it outside (probe-verified). cat <<A <<B launders identically (only A's body stripped). Suggested fix: iterate all << on a line, handle backslash delimiters, and deny when a heredoc cannot be delimited.

中文说明

R8-58(第 8 轮台账,仍然成立):stripHeredocBodies 每行只剥离第一个 heredoc,且不识别反斜杠引用的定界符(<<\EOF);未识别形式的 heredoc 正文会被当作真实命令解析。cd /tmp/outside + cat <<\\EOF + 正文 cd <session-root> + git reset --hard:正则无法匹配定界符,正文里的 cd 把 trackedCwd 洗回边界内,git reset --hard 被放行,而 bash 在边界外执行(已探针验证)。cat <<A <<B 同样可洗(只剥 A 的正文)。修复:遍历一行内所有 <<、处理反斜杠定界符、无法界定时报错拒绝。

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

Comment on lines +811 to +817
if (SUDO_VALUE_FLAGS.has(token.text)) {
index += 2;
continue;
}
index++;
}
return { next: index };

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] consumeSudoWrapper treats sudo -i/--login as a neutral flag, but sudo changes the working directory to the target user's home before running the command — an unmodeled relocation of the same class the code applies to -R/--chroot. — Failure scenario: sudo -i git commit -m x is analyzed as an ordinary git commit against the session cwd and ALLOWED; sudo executes with cwd = the target user's home, so a repository discoverable there (e.g. a dotfiles repo at $HOME) is mutated outside the boundary (probe-verified against sudo docs). Suggested fix: set state.unresolved = true on -i/--login, like the chroot flags.

中文说明

consumeSudoWrappersudo -i/--login 当作中性标志,但 sudo 会在执行命令前把工作目录切到目标用户家目录——与代码对 -R/--chroot 同类的未建模重定位。sudo -i git commit -m x 会按会话 cwd 的普通 git commit 分析并被放行;sudo 实际以目标用户家目录为 cwd 执行,若那里可发现仓库(如 $HOME 的 dotfiles 仓库)就会在边界外被修改(已对照 sudo 文档探针验证)。修复:对 -i/--loginstate.unresolved = true,与 chroot 标志一致。

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

Comment on lines +1416 to +1419
current = path.resolve(path.dirname(current), match[1]!.trim());
continue;
}
if (path.basename(current) === '.git') {

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] resolveGitDirRepository canonicalizes only the initial target; gitdir: redirect targets and worktree gitdir pointers are resolved purely lexically, so a symlink named .git INSIDE the boundary whose realpath points outside defeats containment (stat follows the link, but basename/dirname are applied to the lexical path; the caller's final realpathNearestExistingAsync is a no-op because the directory exists inside). — Failure scenario: plant ln -s /outside/repo/.git /inside/link/.git + gitfile bait.git -> /inside/link/.git; git --git-dir=bait.git config … resolves to /inside/link → ALLOWED, while real git writes into /outside/repo (PoC'd for both the --git-dir and discovery variants). Suggested fix: re-canonicalize with realpath after every indirection hop (targets that don't exist already throw, so full realpath is safe).

中文说明

resolveGitDirRepository 只对初始目标做规范化;gitdir: 重定向目标与 worktree gitdir 指针完全按词法解析,因此边界内一个 realpath 指向外部的名为 .git 的符号链接可以绕过包含性检查(stat 跟随链接,但 basename/dirname 作用在词法路径上;调用方最后的 realpathNearestExistingAsync 因目录真实存在于边界内而无效)。布置 ln -s /outside/repo/.git /inside/link/.git 与 gitfile bait.git -> /inside/link/.git 后,git --git-dir=bait.git config … 解析到 /inside/link 被放行,真实 git 却写入 /outside/repo(--git-dir 与发现两条路径均已 PoC)。修复:每次间接跳转后重新用 realpath 规范化(不存在的目标本就会抛错,完整 realpath 是安全的)。

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

Comment on lines +2159 to +2162
const functionHeader =
/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*\(\s*\)/.exec(segment) ??
// The `function NAME` keyword form, with the `()` optional.
/^\s*function\s+([A-Za-z_][A-Za-z0-9_]*)\b/.exec(segment);

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] The function NAME keyword-form regex stops the name at the first non-word character (\b), so function git-x { … } records the body under the truncated name git (bash defines git-x; hyphenated names are legal), which later shadow dispatch substitutes for the real program. — Failure scenario: function git-x { echo ok; } + git -C /outside/repo push — the body is stored under definedBodies['git']; the git invocation hits shadow dispatch and replays the harmless body → ALLOWED while bash runs the real git (probe-verified). Suggested fix: consume the whole bash-legal name and require the header shape, e.g. /^\s*function\s+([A-Za-z_][A-Za-z0-9_-]*)(?=\s*(\(|\{))/.

中文说明

function NAME 关键字形式的正则在第一个非单词字符处截断名字(\b),function git-x { … } 的函数体会被记录在截断名 git 下(bash 定义的是 git-x,带连字符的名字合法),之后的影子分发会用它顶替真实程序。function git-x { echo ok; } + git -C /outside/repo push:函数体存入 definedBodies['git'],git 调用走影子分发重放无害函数体而被放行,bash 却执行真实 git(已探针验证)。修复:完整消费 bash 合法名字并要求头部形态,如 /^\s*function\s+([A-Za-z_][A-Za-z0-9_-]*)(?=\s*(\(|\{))/

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

Comment on lines +2583 to +2584
const pending = shellLocals.get(operand.text);
if (pending) recordEnvAssignment(pending, exported);

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] A name-only export of a GIT_* relocation key with no tracked pending assignment is assumed harmless, but the variable may already hold a value written by an unmodeled setter (read, printf -v), so the promotion exports an invisible relocation. — Failure scenario: echo /outside/repo > .g; read GIT_DIR < .g; export GIT_DIR; git push — shellLocals has no pending GIT_DIR, nothing is promoted, git push evaluates unrelocated against the session cwd → ALLOWED, while bash exports the value and pushes the outside repo (probe-verified). Suggested fix: when a name-only export targets a relocation key with no pending value, set exported.unresolved = true (fail closed).

中文说明

对没有待处理赋值记录的 GIT_* 重定位键做无名 export 被视为无害,但该变量可能已被未建模的写入者(readprintf -v)赋值,这次提升会把一个不可见的重定位导出。echo /outside/repo > .g; read GIT_DIR < .g; export GIT_DIR; git push:shellLocals 中没有待处理的 GIT_DIR,无任何提升,git push 按会话 cwd 无重定位求值被放行,bash 却导出该值并 push 外部仓库(已探针验证)。修复:无名 export 作用于重定位键且无待处理值时置 exported.unresolved = true(fail-closed)。

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

await agentPromise;
});

it('allows /fork past the guard gate under the built-in guard alone', async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The test that opens the /fork gate under the built-in guard alone stubs the entire agent tool (getTool → build → execute), so the diff's own justification for opening the gate — that the forked nested agent's shell calls remain subject to the daemon's containment policy — is pinned by no test. — Concrete cost: guard inheritance into forks holds today only because subagents reuse the parent Config via prototype delegation; if a future change gives subagents a fresh config or rebuilds the registry without the guard, forked run_shell_command/monitor calls never reach externalToolGuardPrepare and execute uncontained — while this allow-test (stubs the tool) and every daemon-side unit test stay green. The rejection twin asserts loadCliConfig({toolInvocationGuard}); the allow twin asserts nothing downstream of the gate. Suggested fix: assert the initialize response carries EXTERNAL_TOOL_GUARD_READY_META_KEY here (as the provider-attached memory test does), or drive a forked shell call through the guard.

中文说明

在仅内置守卫模式下打开 /fork 闸门的测试把整个 agent 工具 stub 掉(getTool → build → execute),因此 diff 打开闸门的理由——fork 出的嵌套 agent 的 shell 调用仍受 daemon 包含策略约束——没有任何测试钉住。代价:今天 fork 的守卫继承仅因子代理通过原型委托复用父 Config;若未来给子代理全新 config 或不带守卫重建注册表,fork 的 run_shell_command/monitor 调用将不再到达 externalToolGuardPrepare、不受包含地执行——而此允许测试(stub 了工具)与所有 daemon 侧单测仍绿。拒绝孪生体断言了 loadCliConfig({toolInvocationGuard});允许孪生体对闸门之后毫无断言。修复:在此断言 initialize 响应携带 EXTERNAL_TOOL_GUARD_READY_META_KEY(如提供方挂载的记忆测试那样),或真正驱动一个 fork 的 shell 调用过守卫。

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

Comment on lines +1119 to +1120
await expect(guard(call)).resolves.toEqual({ allowed: true });
await rm(linkedRoot, { recursive: true, force: true });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] 'keeps a linked-worktree session working when its own .git points outside' cleans up its scratch directory only on the success path: linkedRoot is created via mkdtempSync directly under os.tmpdir() (outside the afterAll's temporaryRoot), and await rm(linkedRoot, …) runs after the assertions with no try/finally. — Concrete cost: if either expect (or a setup writeFile) throws, a populated daemon-guard-wt-* tree (fake main repo, admin dir, checkout) leaks into the system temp dir on every failing run, on CI runners and dev machines alike. The sibling test 'contains a sub-agent to the worktree it reports' handles the identical hazard with try/finally. Suggested fix: wrap the guard assertions in try { … } finally { await rm(linkedRoot, …) }.

中文说明

'keeps a linked-worktree session working when its own .git points outside' 只在成功路径清理临时目录:linkedRoot 经 mkdtempSync 直接建在 os.tmpdir() 下(afterAll 的 temporaryRoot 之外),await rm(linkedRoot, …) 在断言之后且无 try/finally。代价:任一 expect(或 setup writeFile)抛出时,一份内容完整的 daemon-guard-wt-* 树(假主仓库、管理目录、checkout)会在每次失败运行时泄漏到系统临时目录,CI runner 与开发机皆然。孪生测试 'contains a sub-agent to the worktree it reports' 用 try/finally 处理了同样的隐患。修复:把守卫断言包进 try { … } finally { await rm(linkedRoot, …) }。

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

Comment on lines +256 to +257
importing it on a dash-backed `sh` would recreate the escape. It fails
closed, over-denying the bash-backed case (a false positive, not a bypass).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The non-goal bullet misdescribes the failure direction of not importing export -f bodies into sh -c payloads: the choice UNDER-analyzes (fail-open on bash-backed sh hosts) and cannot over-deny — not importing never adds analysis. The doc asserts this corner 'fails closed … (a false positive, not a bypass)', the opposite of the code's actual direction for the relocating-body variant (the R9-58 mechanism, probe-verified: f() { git -C /outside reset --hard; }; export -f f; sh -c 'f' → allowed; bash-backed hosts execute the body). — Concrete cost: the document whose stated purpose is accurate scoping ('Treating it as more than that would be the actual risk') states an unconditional direction that is wrong for half the state space; only the shadow variant (git() { :; }; export -f git; sh -c 'git -C …' → denied) matches the doc. Suggested fix: state the true direction — under-analysis on bash-backed hosts, accepted here.

中文说明

该 non-goal 条目误述了不把 export -f 函数体导入 sh -c payload 的失败方向:此选择是分析不足(在 sh 即 bash 的主机上 fail-open),不可能过度拒绝——不导入从不增加分析。文档断言此角落'fail-closed……(误报而非绕过)',与代码在重定位函数体变体上的实际方向相反(即 R9-58 机制,已探针验证:f() { git -C /outside reset --hard; }; export -f f; sh -c 'f' 被放行;bash 系主机执行函数体)。代价:以精确界定为目的的文档('把它当成更多才是真正的风险')对一半状态空间给出了方向性错误;只有影子变体(git() { :; }; export -f git; sh -c 'git -C …' 被拒绝)与文档相符。修复:写明真实方向——bash 系主机上分析不足,此处接受。

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

Comment thread docs/users/qwen-serve.md
Comment on lines +494 to +496
track environment variable values across commands, or analyze heredoc bodies
(Git-shaped text inside a heredoc can be denied even though the shell never
executes it). `/fork` and agent-backed workspace memory remember/dream remain

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The new user-facing docs describe pre-stripping heredoc behavior this same PR eliminates: the guard now strips heredoc bodies before analysis (probe: cat <<EOF + git payload + EOF → allowed), and this PR's own round-9 test requires a body cd NOT to be tracked — but the added prose still asserts 'Git-shaped text inside a heredoc can be denied even though the shell never executes it'. — Concrete cost: operators reading the new docs expect false-positive denials on inert heredoc text (or file bugs when they don't occur); the sentence contradicts the code and tests shipped in the same change. Separate new prose from the already-reported stale design-doc bullet. Suggested fix: rewrite to describe the shipped behavior — bodies are stripped, so heredoc text normally cannot trigger a denial (incomplete-stripping edge cases may still scan body lines).

中文说明

新增的用户文档描述了同一 PR 已消除的预剥离前行为:守卫现在在分析前剥离 heredoc 正文(探针:cat <<EOF + git payload + EOF 被允许),本 PR 自己的第 9 轮测试也要求正文中的 cd 不被跟踪——但新增文字仍断言'heredoc 内的 Git 形状文本可能被拒绝,即使 shell 从不执行它'。代价:读新文档的运维者会期待对惰性 heredoc 文本的误拒(或不发生时提 bug);该句与同一变更交付的代码和测试矛盾。与已报告的过期设计文档条目是不同文件的不同新文字。修复:改写为已交付行为——正文被剥离,heredoc 文本通常不会触发拒绝(剥离不完整的边缘情形仍可能扫描正文行)。

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

Comment on lines +968 to +974
const EXPORT_BUILTINS = new Set([
'declare',
'export',
'local',
'readonly',
'typeset',
]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] Bash namerefs (declare -n/typeset -n/local -n) create an indirect assignment channel into GIT_* relocation keys that the guard never models (verifier: variable indirection is a disclosed may-pass class, so this is a hardening suggestion; the channel is probe-verified and the fix is cheap). — Concrete cost: declare -n p=GIT_DIR; p=/outside/repo/.git; export GIT_DIR; git reset --hard → ALLOWED (probe): recordEnvAssignment ignores key p (not even unresolved), p=/outside lands in shellLocals under p, the name-only export finds no pending GIT_DIR to promote, git evaluates unrelocated; bash exports GIT_DIR=/outside/repo/.git and git resets the outside repo. Direct GIT_DIR= twins are denied. Distinct from R11-8 (there GIT_DIR itself is set by an unmodeled setter). Suggested fix: on a -n declaration whose value is a tracked GIT_* name, route later NAME=value assignments through the target key, or mark exported.unresolved.

中文说明

Bash 引用(declare -n/typeset -n/local -n)创建了通向 GIT_* 重定位键的间接赋值通道,守卫从未建模(验证者:变量间接引用属于已披露的可能放行类别,故按加固建议处理;通道已探针验证且修复便宜)。代价:declare -n p=GIT_DIR; p=/outside/repo/.git; export GIT_DIR; git reset --hard 被放行(探针):recordEnvAssignment 忽略键 p(甚至不置 unresolved),p=/outside 以 p 为名进入 shellLocals,无名 export 找不到待提升的 GIT_DIR,git 按无重定位求值;bash 导出 GIT_DIR=/outside/repo/.git,git 重置外部仓库。直接 GIT_DIR= 孪生体被拒绝。与 R11-8 不同(那里是 GIT_DIR 本身被未建模写入者赋值)。修复:-n 声明的值是被跟踪 GIT_* 名时,把后续 NAME=value 赋值路由到目标键,或置 exported.unresolved。

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

…elocation

Two escapes surfaced by the round-11 review, both reproduced against the guard.

- The shadow-removal prefix scan trusted a literal `command`/`builtin` word to
  force the real builtin, but bash resolves a function of that name first. A
  `command(){ git -C <outside> …; }; command unset other` therefore
  early-continued as a harmless builtin removal and never replayed the
  relocating body. The prefix loop now stops when the prefix word is itself a
  recorded shadow, leaving it for the normal shadow dispatch.
- The unrecognized-program marker scans covered GIT_DIR/GIT_WORK_TREE-family
  assignments but not GIT_PROGRAM_ENV_KEYS (`PATH`/`GIT_EXEC_PATH`), which
  decide which git binary runs. The direct `PATH=/evil git …` was denied while
  `find … -exec sh -c 'PATH=/evil git …'` slipped through. Both marker scans
  now include those keys, and they remain gated on a co-present git word so an
  ordinary `PATH=… make` is unaffected.

Adds regressions for the shadowed prefixes and the wrapped PATH/GIT_EXEC_PATH
forms.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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 (unit suites for all PR-touched workspaces passed); Test (macos-latest)/Test (windows-latest) legs also skipped in CI — no platform-specific tests exist in the diff (the R8-15 Windows claim was probed against node:path.win32).

Not explored to full depth (tool budget reached): "You are review agent reverse-audit — Reverse audit agent…": did not exhaustively enumerate every git config key against git's full config reference (checked the plausible executing keys — submodule.<name>.update consid…; "You are review agent reverse-audit — Reverse audit agent…": none — all checks above completed within budget.; "You are review agent reverse-audit — Reverse audit agent…": none — finished within budget.; "You are review agent reverse-audit — Reverse audit agent…": inheritance ( set -a /allexport into children, and its reset) not walked this round beyond noting env inheritance as the finding's attack source.; "You are review agent reverse-audit — Reverse audit agent…": toctou (planted .git , relink-then-use) not walked this round — observed only the relink precheck at lines 2469–2499 en passant; ledger items R8-64 et al. co…, and 4 more.

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

[Critical] R11-8 (daemon-git-worktree-guard.ts:2613): the all-export-off arm discards analysis.state.relocations while set is a POSIX special builtin — prefix assignments persist across set +a on dash/POSIX-sh, exporting the dropped relocation

[Critical] R11-7 (daemon-git-worktree-guard.ts:2814): a reported invocationCwd that is a SUBDIRECTORY of a checkout leaves the boundary at the session root (narrowing requires realpath(discovered) === canonicalReported), so sibling/parent mutations pass and own work mis-resolves

[Critical] R11-6 (daemon-git-worktree-guard.ts:2524): the dynamic-program case never fails closed when tokens remain dynamic (unexpanded) after expandShellLocals, so an unreadable positional argument smuggles a -C relocation past every marker scan

[Critical] R8-58 (daemon-git-worktree-guard.ts:1858): stripHeredocBodies strips only the FIRST heredoc per line and does not recognize backslash-quoted delimiters (<<\EOF); unrecognized/second heredoc bodies survive into splitCommands and a data-only cd launders trackedCwd

[Critical] R11-1 (daemon-git-worktree-guard.ts:817): consumeSudoWrapper treats sudo -i/--login as a neutral flag, but sudo changes the working directory to the target user's home before running the command — an unmodeled relocation of the same class as -R/--chroot

[Critical] R11-2 (daemon-git-worktree-guard.ts:1419): resolveGitDirRepository canonicalizes only the initial target; gitdir: redirect targets and worktree gitdir pointers are resolved purely lexically, so a symlink named .git INSIDE the boundary whose realpath points outside defeats containment

[Critical] R11-3 (daemon-git-worktree-guard.ts:2162): the function NAME keyword-form regex stops the name at the first non-word character (\b), so function git-x { ... } records the body under the truncated name git; later git -C push hits the shadow dispatch and replays the harmless body

[Critical] R11-4 (daemon-git-worktree-guard.ts:2584): a name-only export of a GIT_* relocation key with no tracked pending assignment is assumed harmless, but the variable may already hold a value written by an unmodeled setter (read, printf -v); the promotion exports an invisible relocation

[Critical] R11-5 (daemon-git-worktree-guard.ts:2647): the gitShapedNames branch evaluates a fabricated two-token run [programToken, 'git'] and drops the invocation's real argv, so relocation flags the fall-through evaluateUnrecognizedRun(run) would have caught are never scanned

[Critical] R7-8 (daemon-git-worktree-guard.ts:2167): a function definition with a non-brace compound body (( ... ), for, if, while, until) sets insideDefinition but the definition only ever closes on a segment containing } — it never closes, and every later segment (including a literal outside git command) is swallowed into definitionBody

[Critical] R9-30 (daemon-git-worktree-guard.ts:1217): the exec/nohup branch consumes only the program word; exec's own flags (-l, -a, --) fall through to kind 'other' and assignments AFTER them are dropped before recordEnvAssignment runs

[Critical] R9-3 (daemon-git-worktree-guard.ts:827): consumeTimeoutWrapper never checks token.dynamic; a blind index++ eats a dynamic duration operand and the wrapped command falls to the marker net unanalyzed

[Critical] R9-26 (daemon-git-worktree-guard.ts:730): consumeEnvWrapper -u/--unset skips a dynamic value token with index += 2 without ever setting state.unresolved — the one dynamic operand in the wrapper that does not fail closed

[Critical] R9-59 (daemon-git-worktree-guard.ts:2163): paren-form function definitions that do not START a segment are never recorded (functionHeader is segment-start anchored; readDefinition/readFunctionName fallbacks match shapes shell-quote cannot emit; function is absent from LEADING_SHELL_KEYWORDS)

[Critical] R8-10 (bridgeClient.ts:1320): prompt-less guard requests are gated only by channel-level ownsSession, so any sibling sessionId owned by the same multiplexed channel passes and receives the nominated session's trusted effectiveCwd (constructor default () => true)

[Critical] R9-23 (daemon-git-worktree-guard.ts:1382): git config core.worktree is unmodeled — subcommand config stops option scanning, nothing records the redirect, and a later mutation honors the planted redirect (core.hooksPath gives code execution)

[Critical] R9-22 (daemon-git-worktree-guard.ts:111): the env-key sets miss dynamic-loader and shell-startup keys (LD_PRELOAD, LD_LIBRARY_PATH, DYLD_*, PYTHONPATH, GITPERLLIB, PERL5LIB, BASH_ENV, ZDOTDIR) — prefix assignments execute outside-boundary code unflagged

[Critical] R9-2 (daemon-git-worktree-guard.ts:403): process substitutions <( )/>( ) split runs — tokenizeSegment starts a new run at the ( and never resumes; git > >(true) -C /outside reset --hard leaves [-C /outside reset --hard] as an other run with no git word

[Critical] R9-25 (daemon-git-worktree-guard.ts:447): redirectOperand is cleared only on string tokens; a >(pwn) body's first word gets redirect:true and is skipped by both replay gates (readProgramWord/runFromProgramWord) and the marker gate

[Critical] R9-27 (daemon-git-worktree-guard.ts:779): consumeSudoWrapper blind-skips SUDO_VALUE_FLAGS values — sudo -u $U with U='root --chdir=' never marks unresolved and never sees the injected chdir

[Critical] R9-17 (daemon-git-worktree-guard.ts:985): CHDIR_OPTION_PATTERN is a cross-shell union (-q, -s, -@ are zsh/ksh-only) applied to bash cd — the guard tracks a phantom cwd that bash rejects

[Critical] R9-56 (daemon-git-worktree-guard.ts:1163): the command/builtin branch skips all - flags and re-classifies the next word — command -v cd deep/dir classifies as kind cd with target deep/dir (a phantom cd; command -v only prints)

[Critical] R7-9 (daemon-git-worktree-guard.ts:2576): declare -f / readonly -f phantom export — the export analysis adds every non-dash operand to exportedFunctions whenever any operand is -f; the payload-scope filter then keeps the harmless body and invokeDefinedBody replays it

[Critical] R8-51 (daemon-git-worktree-guard.ts:1163): after consumeEnvWrapper/consumeSudoWrapper/consumeTimeoutWrapper the loop re-classifies with no child-process marker — a wrapped cd updates trackedCwd although a child process cannot move the parent shell

[Critical] R9-29 (daemon-git-worktree-guard.ts:1165): the env -S/--split-string payload branch returns the payload with propagatesCwd:false and no importsExportedFunctions (scope {}) while the plain env bash -c path sets importsExportedFunctions — exported shadows are invisible only through the -S form

[Critical] R9-31 (daemon-git-worktree-guard.ts:1775): any unrecognized prefix program (nice, setsid, strace, stdbuf, ...) hides a wrapped bash -c pwn payload — evaluateUnrecognizedRun consults only token text, never definedBodies/exportedFunctions/gitShapedNames

[Critical] R8-15 (daemon-git-worktree-guard.ts:1449): on Windows resolvePhysicalPath starts at path.parse(target).root but still splits the FULL target — C:\work\subdir resolves to C:\C:\work\subdir, yielding UNRESOLVED denials or a poisoned trackedCwd on every relocated run

[Critical] R9-44 (daemon-git-worktree-guard.ts:1477): the discovery walk's 64-iteration cap fails OPEN — resolveDiscoveredRepository returns undefined on loop exhaustion and denyOutsideDiscoveredRepository treats undefined as no denial

[Critical] R9-61 (daemon-git-worktree-guard.ts:2181): a nested function-header line inside an in-progress definition unconditionally overwrites insideDefinition and resets definitionBody — the outer body is discarded unrecorded

[Critical] R8-8 (daemon-git-worktree-guard.ts:2176): function-definition end detection is raw segment.includes(''}') + definitionBody.lastIndexOf('}') with no quote awareness — a quoted } closes the definition early and the remainder evaluates at its original position

[Critical] R7-6 (daemon-git-worktree-guard.ts:527): cd $VAR where VAR is a known-but-empty local expands to {text:'', dynamic:false} — trackedCwd stays unchanged via path.resolve(trackedCwd, '') while bash drops the empty word and cds to $HOME

[Critical] R9-57 (daemon-git-worktree-guard.ts:2376): a cd whose target does not exist fails silently in bash (cwd unchanged) but the guard records the lexical phantom; realpathNearestExistingAsync preserves phantom tails, so a depth-matched relative -C folds back onto an in-bounds decoy

[Critical] R2-23 (paths.ts:522): realpathNearestExistingAsync contract gap — a dangling intermediate symlink returns the input unchanged; the access() walk-up re-appends missing segments lexically, and the reported-cwd acceptance path consumes the result with no full-existence requirement

[Critical] R8-11 (daemon-git-worktree-guard.ts:2840): a child-reported invocationCwd is accepted with no binding to the invoking agent's assigned worktree — the acceptance key is the session-level worktrees dir, so an agent assigned worktree A can nominate and mutate sibling worktree B

[Critical] R8-36 (daemon-git-worktree-guard.ts:2811): reported-cwd acceptance cannot place AgentTool-provisioned worktrees when the session is bound to a monorepo subdirectory — branch 1 requires the worktree inside the session cwd (true only when projectRoot == sessionCwd); the else branch checks the global storage dir

[Critical] R9-21 (daemon-git-worktree-guard.ts:2337): the guard replays alias bodies as if aliases expanded, but the daemon executes via non-interactive bash -c where aliases never expand (no expand_aliases anywhere) — the pinned allowed:true test blesses fictitious modeling

[Critical] R9-1 (daemon-git-worktree-guard.ts:111): GIT_UNRESOLVABLE_ENV_KEYS omits HOME and XDG_CONFIG_HOME (global config => core.hooksPath => code execution on hook firing) and GIT_PROXY_COMMAND (config twin core.gitProxy IS in GIT_COMMAND_CONFIG_KEY_PATTERNS)

[Critical] R9-38 (daemon-git-worktree-guard.ts:111): GIT_TRACE/GIT_TRACE2*/GIT_QUARANTINE_PATH env keys redirect git's trace/quarantine output to arbitrary file paths outside the boundary; recordEnvAssignment ignores them

[Critical] R8-64 (daemon-git-worktree-guard.ts:2819): worktree isolation is derived from live .git discovery with no monotonicity — rm .git is unmodeled (not in PATH_RELINKING_PROGRAMS) and re-widens the boundary to the parent checkout on the next call; a corrupted gitfile fails closed but a deleted one fails open

[Critical] R8-48 (daemon-git-worktree-guard.ts:2228): leaving a ( ... ) subshell restores only cwd/relocations/unresolved/allExport/locals; definedBodies, gitShapedNames, exportedNames, exportedFunctions are shared by reference and survive the subshell

中文说明

仅完成部分审查,审查缺口已披露。

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (unit suites for all PR-touched workspaces passed); Test (macos-latest)/Test (windows-latest) legs also skipped in CI — no platform-specific tests exist in the diff (the R8-15 Windows claim was probed against node:path.win32)。

未探索到全部深度(达到工具调用预算):"You are review agent reverse-audit — Reverse audit agent…"did not exhaustively enumerate every git config key against git's full config reference (checked the plausible executing keys — submodule.<name>.update consid…"You are review agent reverse-audit — Reverse audit agent…"none — all checks above completed within budget."You are review agent reverse-audit — Reverse audit agent…"none — finished within budget."You are review agent reverse-audit — Reverse audit agent…"inheritance ( set -a /allexport into children, and its reset) not walked this round beyond noting env inheritance as the finding's attack source."You are review agent reverse-audit — Reverse audit agent…"toctou (planted .git , relink-then-use) not walked this round — observed only the relink precheck at lines 2469–2499 en passant; ledger items R8-64 et al. co…,另有 4 条。

未审查:反向审计——评审时间预算不足,未能开始第 3 轮。

[Critical] R11-8 (daemon-git-worktree-guard.ts:2613): the all-export-off arm discards analysis.state.relocations while set is a POSIX special builtin — prefix assignments persist across set +a on dash/POSIX-sh, exporting the dropped relocation

[Critical] R11-7 (daemon-git-worktree-guard.ts:2814): a reported invocationCwd that is a SUBDIRECTORY of a checkout leaves the boundary at the session root (narrowing requires realpath(discovered) === canonicalReported), so sibling/parent mutations pass and own work mis-resolves

[Critical] R11-6 (daemon-git-worktree-guard.ts:2524): the dynamic-program case never fails closed when tokens remain dynamic (unexpanded) after expandShellLocals, so an unreadable positional argument smuggles a -C relocation past every marker scan

[Critical] R8-58 (daemon-git-worktree-guard.ts:1858): stripHeredocBodies strips only the FIRST heredoc per line and does not recognize backslash-quoted delimiters (<<\EOF); unrecognized/second heredoc bodies survive into splitCommands and a data-only cd launders trackedCwd

[Critical] R11-1 (daemon-git-worktree-guard.ts:817): consumeSudoWrapper treats sudo -i/--login as a neutral flag, but sudo changes the working directory to the target user's home before running the command — an unmodeled relocation of the same class as -R/--chroot

[Critical] R11-2 (daemon-git-worktree-guard.ts:1419): resolveGitDirRepository canonicalizes only the initial target; gitdir: redirect targets and worktree gitdir pointers are resolved purely lexically, so a symlink named .git INSIDE the boundary whose realpath points outside defeats containment

[Critical] R11-3 (daemon-git-worktree-guard.ts:2162): the function NAME keyword-form regex stops the name at the first non-word character (\b), so function git-x { ... } records the body under the truncated name git; later git -C push hits the shadow dispatch and replays the harmless body

[Critical] R11-4 (daemon-git-worktree-guard.ts:2584): a name-only export of a GIT_* relocation key with no tracked pending assignment is assumed harmless, but the variable may already hold a value written by an unmodeled setter (read, printf -v); the promotion exports an invisible relocation

[Critical] R11-5 (daemon-git-worktree-guard.ts:2647): the gitShapedNames branch evaluates a fabricated two-token run [programToken, 'git'] and drops the invocation's real argv, so relocation flags the fall-through evaluateUnrecognizedRun(run) would have caught are never scanned

[Critical] R7-8 (daemon-git-worktree-guard.ts:2167): a function definition with a non-brace compound body (( ... ), for, if, while, until) sets insideDefinition but the definition only ever closes on a segment containing } — it never closes, and every later segment (including a literal outside git command) is swallowed into definitionBody

[Critical] R9-30 (daemon-git-worktree-guard.ts:1217): the exec/nohup branch consumes only the program word; exec's own flags (-l, -a, --) fall through to kind 'other' and assignments AFTER them are dropped before recordEnvAssignment runs

[Critical] R9-3 (daemon-git-worktree-guard.ts:827): consumeTimeoutWrapper never checks token.dynamic; a blind index++ eats a dynamic duration operand and the wrapped command falls to the marker net unanalyzed

[Critical] R9-26 (daemon-git-worktree-guard.ts:730): consumeEnvWrapper -u/--unset skips a dynamic value token with index += 2 without ever setting state.unresolved — the one dynamic operand in the wrapper that does not fail closed

[Critical] R9-59 (daemon-git-worktree-guard.ts:2163): paren-form function definitions that do not START a segment are never recorded (functionHeader is segment-start anchored; readDefinition/readFunctionName fallbacks match shapes shell-quote cannot emit; function is absent from LEADING_SHELL_KEYWORDS)

[Critical] R8-10 (bridgeClient.ts:1320): prompt-less guard requests are gated only by channel-level ownsSession, so any sibling sessionId owned by the same multiplexed channel passes and receives the nominated session's trusted effectiveCwd (constructor default () => true)

[Critical] R9-23 (daemon-git-worktree-guard.ts:1382): git config core.worktree is unmodeled — subcommand config stops option scanning, nothing records the redirect, and a later mutation honors the planted redirect (core.hooksPath gives code execution)

[Critical] R9-22 (daemon-git-worktree-guard.ts:111): the env-key sets miss dynamic-loader and shell-startup keys (LD_PRELOAD, LD_LIBRARY_PATH, DYLD_*, PYTHONPATH, GITPERLLIB, PERL5LIB, BASH_ENV, ZDOTDIR) — prefix assignments execute outside-boundary code unflagged

[Critical] R9-2 (daemon-git-worktree-guard.ts:403): process substitutions <( )/>( ) split runs — tokenizeSegment starts a new run at the ( and never resumes; git > >(true) -C /outside reset --hard leaves [-C /outside reset --hard] as an other run with no git word

[Critical] R9-25 (daemon-git-worktree-guard.ts:447): redirectOperand is cleared only on string tokens; a >(pwn) body's first word gets redirect:true and is skipped by both replay gates (readProgramWord/runFromProgramWord) and the marker gate

[Critical] R9-27 (daemon-git-worktree-guard.ts:779): consumeSudoWrapper blind-skips SUDO_VALUE_FLAGS values — sudo -u $U with U='root --chdir=' never marks unresolved and never sees the injected chdir

[Critical] R9-17 (daemon-git-worktree-guard.ts:985): CHDIR_OPTION_PATTERN is a cross-shell union (-q, -s, -@ are zsh/ksh-only) applied to bash cd — the guard tracks a phantom cwd that bash rejects

[Critical] R9-56 (daemon-git-worktree-guard.ts:1163): the command/builtin branch skips all - flags and re-classifies the next word — command -v cd deep/dir classifies as kind cd with target deep/dir (a phantom cd; command -v only prints)

[Critical] R7-9 (daemon-git-worktree-guard.ts:2576): declare -f / readonly -f phantom export — the export analysis adds every non-dash operand to exportedFunctions whenever any operand is -f; the payload-scope filter then keeps the harmless body and invokeDefinedBody replays it

[Critical] R8-51 (daemon-git-worktree-guard.ts:1163): after consumeEnvWrapper/consumeSudoWrapper/consumeTimeoutWrapper the loop re-classifies with no child-process marker — a wrapped cd updates trackedCwd although a child process cannot move the parent shell

[Critical] R9-29 (daemon-git-worktree-guard.ts:1165): the env -S/--split-string payload branch returns the payload with propagatesCwd:false and no importsExportedFunctions (scope {}) while the plain env bash -c path sets importsExportedFunctions — exported shadows are invisible only through the -S form

[Critical] R9-31 (daemon-git-worktree-guard.ts:1775): any unrecognized prefix program (nice, setsid, strace, stdbuf, ...) hides a wrapped bash -c pwn payload — evaluateUnrecognizedRun consults only token text, never definedBodies/exportedFunctions/gitShapedNames

[Critical] R8-15 (daemon-git-worktree-guard.ts:1449): on Windows resolvePhysicalPath starts at path.parse(target).root but still splits the FULL target — C:\work\subdir resolves to C:\C:\work\subdir, yielding UNRESOLVED denials or a poisoned trackedCwd on every relocated run

[Critical] R9-44 (daemon-git-worktree-guard.ts:1477): the discovery walk's 64-iteration cap fails OPEN — resolveDiscoveredRepository returns undefined on loop exhaustion and denyOutsideDiscoveredRepository treats undefined as no denial

[Critical] R9-61 (daemon-git-worktree-guard.ts:2181): a nested function-header line inside an in-progress definition unconditionally overwrites insideDefinition and resets definitionBody — the outer body is discarded unrecorded

[Critical] R8-8 (daemon-git-worktree-guard.ts:2176): function-definition end detection is raw segment.includes(''}') + definitionBody.lastIndexOf('}') with no quote awareness — a quoted } closes the definition early and the remainder evaluates at its original position

[Critical] R7-6 (daemon-git-worktree-guard.ts:527): cd $VAR where VAR is a known-but-empty local expands to {text:'', dynamic:false} — trackedCwd stays unchanged via path.resolve(trackedCwd, '') while bash drops the empty word and cds to $HOME

[Critical] R9-57 (daemon-git-worktree-guard.ts:2376): a cd whose target does not exist fails silently in bash (cwd unchanged) but the guard records the lexical phantom; realpathNearestExistingAsync preserves phantom tails, so a depth-matched relative -C folds back onto an in-bounds decoy

[Critical] R2-23 (paths.ts:522): realpathNearestExistingAsync contract gap — a dangling intermediate symlink returns the input unchanged; the access() walk-up re-appends missing segments lexically, and the reported-cwd acceptance path consumes the result with no full-existence requirement

[Critical] R8-11 (daemon-git-worktree-guard.ts:2840): a child-reported invocationCwd is accepted with no binding to the invoking agent's assigned worktree — the acceptance key is the session-level worktrees dir, so an agent assigned worktree A can nominate and mutate sibling worktree B

[Critical] R8-36 (daemon-git-worktree-guard.ts:2811): reported-cwd acceptance cannot place AgentTool-provisioned worktrees when the session is bound to a monorepo subdirectory — branch 1 requires the worktree inside the session cwd (true only when projectRoot == sessionCwd); the else branch checks the global storage dir

[Critical] R9-21 (daemon-git-worktree-guard.ts:2337): the guard replays alias bodies as if aliases expanded, but the daemon executes via non-interactive bash -c where aliases never expand (no expand_aliases anywhere) — the pinned allowed:true test blesses fictitious modeling

[Critical] R9-1 (daemon-git-worktree-guard.ts:111): GIT_UNRESOLVABLE_ENV_KEYS omits HOME and XDG_CONFIG_HOME (global config => core.hooksPath => code execution on hook firing) and GIT_PROXY_COMMAND (config twin core.gitProxy IS in GIT_COMMAND_CONFIG_KEY_PATTERNS)

[Critical] R9-38 (daemon-git-worktree-guard.ts:111): GIT_TRACE/GIT_TRACE2*/GIT_QUARANTINE_PATH env keys redirect git's trace/quarantine output to arbitrary file paths outside the boundary; recordEnvAssignment ignores them

[Critical] R8-64 (daemon-git-worktree-guard.ts:2819): worktree isolation is derived from live .git discovery with no monotonicity — rm .git is unmodeled (not in PATH_RELINKING_PROGRAMS) and re-widens the boundary to the parent checkout on the next call; a corrupted gitfile fails closed but a deleted one fails open

[Critical] R8-48 (daemon-git-worktree-guard.ts:2228): leaving a ( ... ) subshell restores only cwd/relocations/unresolved/allExport/locals; definedBodies, gitShapedNames, exportedNames, exportedFunctions are shared by reference and survive the subshell

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

Comment on lines +661 to +662
function consumeEnvWrapper(
run: GuardToken[],

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] A brace-expansion token in the env/sudo wrapper option position ({-C,<outside>} / {-D,<outside>}) is static text that matches no branch of consumeEnvWrapper (it does not start with -, so it never reaches the fail-closed unrecognized-option branch), and is equally invisible to hasGitRelocationMarker and TEXT_RELOCATION_MARKER_PATTERN — the relocation it expands into is never modelled. — Failure scenario: probe-verified env {-C,/outside/repo} git reset --hard -> ALLOWED (bash brace-expands to env -C /outside/repo git reset --hard, env --chdir is modelled by this very function via ENV_CHDIR_FLAGS); the token is treated as the program word, no relocation is recorded, and the in-boundary basis passes. sudo {-D,<outside>} git ... is the same class one function down. The suite already pins the git-position form (git {-C,...}); the wrapper position is the same expansion one token earlier, undefended.

中文说明

大括号展开 token 出现在 env/sudo 包装器选项位置({-C,})时是静态文本,不匹配 consumeEnvWrapper 的任何分支,两个 marker 扫描也都看不见它,其展开后的重定位完全不被建模。探针验证 env {-C,/outside/repo} git reset --hard 被放行;git 自身位置的同形式已有测试钉住,包装器位置是前移一个 token 的同一展开,未设防。

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

Comment on lines +560 to +561
function hasGitRelocationMarker(tokens: GuardToken[]): boolean {
return tokens.some((token) => {

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] The unrecognized-program backstop ('other' case / evaluateUnrecognizedRun) scans only literal token text for relocation markers, passes tokens unexpanded, and never fails closed on dynamic tokens — a relocation stored in a shell variable and referenced after a static unrecognized program word is invisible. The dynamic-program case expands shellLocals first; this sibling path does not. — Failure scenario: probe-verified X='-C /outside/repo'; nice git $X reset --hard -> ALLOWED (analyzeRun stops at static nice; marker scans see literal $X; state clean; in-boundary basis) while bash expands $X and runs git -C /outside/repo reset --hard. Same-chain locals are modelled everywhere else (the suite denies four same-chain variable relocations); the static-other path is the undefended sibling.

中文说明

未识别程序兜底('other' 分支 / evaluateUnrecognizedRun)只扫描字面 token 文本,不展开 shellLocals,也不对动态 token fail closed——藏在 shell 变量里、位于静态未识别程序词之后的重定位完全不可见。探针验证 X='-C /outside/repo'; nice git $X reset --hard 被放行,而 bash 展开 $X 后在界外执行 reset。dynamic-program 分支会先展开局部变量,这个孪生路径没有。

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

Comment on lines +906 to +908
if (token.text.startsWith('+')) {
index++;
continue;

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] consumeShellWrapper treats every +-option as valueless, but +o/+O take an argument; the argument is then misread as a script filename, the scan returns 'none', and the -c payload is never analyzed. Unmodelled value-taking long options (--rcfile, --init-file) fall through advancing only one token. — Failure scenario: reproduced bash +o nounset -c "$(cat /tmp/evil)" -> ALLOWED while the identical command without +o is denied; also bash +o nounset -c 'GIT_OBJECT_DIRECTORY=<outside>/.git/objects git add -A' and bash --rcfile /nonexistent -c "$(cat /tmp/evil)" -> ALLOWED. Real bash executes the payload (bash +o nounset -c 'cmd' arg verified). This defeats two documented rules at once — recursive -c analysis and 'an undecidable payload is denied' — with ordinary bash option syntax.

中文说明

consumeShellWrapper 把所有 + 选项当作无值选项,但 +o/+O 带参数;参数被误读为脚本文件名,扫描返回 'none',-c 载荷完全不被分析;--rcfile/--init-file 等带值长选项也只前进一格。复现:bash +o nounset -c "$(cat /tmp/evil)" 被放行,去掉 +o 的同命令被拒。普通 bash 选项语法即可同时击穿‘递归分析 -c’与‘不可判定载荷拒绝’两条文档承诺。

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

Comment on lines +460 to +462
if (token === null || typeof token !== 'object') return null;
if ('comment' in token) break;
if (!('op' in token)) return null;

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] A # comment line ending in a backslash lets the following command line bypass the guard entirely: splitCommands (core, unchanged by this PR) drops \\<newline> continuations without comment tracking, splicing the comment onto the next line into one segment, and the tokenizer then trusts shell-quote's comment model — which runs a comment to END-OF-STRING, unlike bash's end-of-line — so everything after # (including the spliced command) is dropped from analysis. — Failure scenario: reproduced end-to-end true # c \\ + newline + git -C <outside> reset --hard -> ALLOWED; live bash probe confirms bash treats the backslash inside a comment as inert, ends the comment at the newline, and executes the git line. Same trick inside $( ) via the recursion path also ALLOWED with bash executing the inner line. Controls denied; the backtick form is NOT a bypass (bash strips \\<newline> inside backquotes — verified). The guard already pins each half separately; the composition defeats the canonical literal git -C <path> form at the center of the doc's reliability promise.

中文说明

以反斜杠结尾的 # 注释行可让其后的命令行完全绕过守卫:splitCommands 无注释状态地丢弃反斜杠换行(把注释与下一行拼成一段),tokenizer 又信任 shell-quote 的注释模型——注释被当作直到字符串结尾,而非 bash 的行尾——# 之后的一切(含被拼接的命令)都退出分析。端到端复现:'true # c \' + 换行 + 'git -C reset --hard' 被放行,bash 探针确认隐藏行真实执行;$( ) 递归路径同样放行。两个对照被拒;反引号形态不是绕过(bash 会剥掉反引号内的反斜杠换行)。

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

Comment on lines +1026 to +1027
function disablesAllExport(run: GuardToken[], start: number): boolean {
for (let index = start; index < run.length; index++) {

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] disablesAllExport scans every token after set as if it were an option — past -- and past positional operands — so set -- +a / set foo +a, which only assign positional parameters and leave allexport untouched, are modeled as turning allexport off. The request side only over-approximates (fail-closed); the disable side under-approximates (fail-open). — Failure scenario: live bash verified set -a; set -- +a; X=1; env | grep X still exports (allexport stays ON); guard probe set -a; set -- +a; GIT_DIR=<outside>/meta; git reset --hard -> ALLOWED (the assignment is demoted to a shell-local instead of an exported relocation; the bare git reset --hard carries no marker) — likewise the set foo +a and GIT_WORK_TREE variants; controls set -a; GIT_DIR=... denied and set -a; set +a; GIT_DIR=... correctly allowed. At runtime git really honors the exported variable and mutates the outside repository.

中文说明

disablesAllExport 把 set 之后的每个 token 都当选项扫描——越过 -- 与位置参数——因此 set -- +a / set foo +a(bash 中只赋位置参数、不动 allexport)被建模为关闭 allexport。请求侧只会过近似(fail-closed),关闭侧却欠近似(fail-open)。实测 bash 中 allexport 保持开启;守卫探针 set -a; set -- +a; GIT_DIR=/meta; git reset --hard 被放行(赋值被降为 shell-local,裸 git 无标记),set foo +a 与 GIT_WORK_TREE 变体同样放行。

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

Comment on lines +157 to +158
Git global options that consume the next argv entry (`--namespace`,
`--super-prefix`, `--shallow-file`, `--attr-source`) are modelled as such:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] --super-prefix is listed as a git global option that consumes the next argv entry, but git rejects it in the pre-subcommand position — measured on git 2.43.0: git --super-prefix x version dies with unknown option: --super-prefix, and the current git-scm.com global-options list omits it. It exists only per-builtin (git read-tree --super-prefix=x accepts it). No security impact (real git rejects the option before any relocation matters); the cost is a fictional entry in the parser's authoritative maintenance doc. — Concrete cost: a maintainer auditing these four options against a real git finds one fictional entry and may distrust the remaining three — two of which (--shallow-file, --attr-source) are real and load-bearing for the parser.

中文说明

文档把 --super-prefix 列为消耗下一个 argv 项的 git 全局选项,但 git 在子命令前位置拒绝它(git 2.43.0 实测 unknown option;git-scm.com 全局选项列表也没有它),它只存在于个别内建命令(read-tree 接受)。无安全影响(真实 git 在任何重定位生效前就拒绝);代价是解析器权威维护文档中的一条虚构条目,可能让维护者连带怀疑真实的两条(--shallow-file、--attr-source)。

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

Comment on lines +144 to +145
`uploadpack.packObjectsHook`, `core.hooksPath` and `gpg.<format>.program`,
matched case-insensitively because Git config keys are; any value starting

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The design doc's enumeration of command-executing -c/--config-env keys presents itself as the complete list but omits 15 of the 28 families GIT_COMMAND_CONFIG_KEY_PATTERNS actually enforces: include.path, includeif.*.path, core.gitproxy, remote.<name>.proxy|receivepack|uploadpack, credential.<name>.helper, browser.<name>.cmd, diff.external, gc.recentObjectsHook, help.browser, interactive.diffFilter, ssh.variant, tar.<name>.command, web.browser, imap.tunnel, instaweb.httpd. — Concrete cost: the check is relocation-independent, so git -c include.path=./extra.config commit is denied even entirely inside the workspace; an operator tracing that denial against the doc finds include.path nowhere and concludes the guard misbehaved; the inverse audit (deriving the denial surface from the doc) silently misses 15 rules. Same drift class as the accepted env-key enumeration finding (R11-13), different list.

中文说明

设计文档对命令执行类 -c/--config-env 键的枚举以完整清单自居,却漏掉实现实际强制的 28 族中的 15 族(include.path、includeif..path、core.gitproxy、remote..proxy/receivepack/uploadpack、credential..helper、browser..cmd、diff.external、gc.recentObjectsHook、help.browser、interactive.diffFilter、ssh.variant、tar.*.command、web.browser、imap.tunnel、instaweb.httpd)。该检查与重定位无关,界内 git -c include.path=… commit 也会被拒;照文档排查的人会以为守卫出错;反向审计则会静默漏掉 15 条规则。与已接受的 R11-13 同属文档漂移,但是另一份清单。

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

Comment on lines +1762 to +1763
// Extract-then-commit is ordinary work, not a relocation.
'tar -xf a.tar && git commit -m x',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] An archive extraction can plant a .git file at the extraction root, but the extractor branch only pushes trackedCwd into relinkedTargets and never sets scope.relink.gitDir, so a path-less git after tar/unzip/cpio/rsync escapes via discovery — while the identical end state via ln is denied by the very next test family. — Concrete cost: probe-verified tar -xf a.tar && git commit -m x with a.tar carrying a top-level .git file gitdir: /outside/repo/.git -> ALLOWED; at runtime discovery follows the planted gitfile and the commit writes refs/objects and executes hooks from the outside repository. The branch comment documents this as an accepted TOCTOU trade-off (the archive is unpacked after the decision), and the verifier upholds that scoping — the actionable residue is that the module's invariant comment ('A relinked .git redirects discovery for every later command, relocated or not') and the design doc's relink discussion overstate coverage without acknowledging the extractor carve-out the allow assertions at :1762 and :1588 pin.

中文说明

归档解压可以在解压根植入 .git 文件,但提取器分支只把 trackedCwd 推入 relinkedTargets、从不设置 scope.relink.gitDir,tar/unzip/cpio/rsync 之后的无路径 git 会经 discovery 逃逸——而 ln 构造的同一终态被拒绝。探针验证:顶层含 'gitdir: /outside/repo/.git' 的 a.tar,tar -xf a.tar && git commit -m x 放行,运行时 commit 写入界外仓库并执行其 hooks。分支注释已把此形态记为接受的 TOCTOU 取舍(归档在决策后才解压),验证者认可该定界——可执行的遗留问题是:模块不变量注释(‘被重链的 .git 会重定向之后所有命令的 discovery’)与设计文档未承认这个由 :1762/:1588 放行断言钉住的提取器例外,覆盖了夸大的覆盖面。

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

Comment on lines +1385 to +1386
'grep -C 5 git CHANGELOG.md',
'tar -C nested -cf out.tar .',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The extractor branch fires on the mere presence of a PATH_EXTRACTING_PROGRAMS word with no mode inspection, so archive create/list invocations (tar -c, tar -t, unzip -l, cpio -t) mark the entire cwd as a relinked target even though they write nothing; any following path-resolving git under the cwd is then spuriously denied. — Concrete cost: probe-verified tar -czf backup.tgz nested && git -C nested commit -m x -> DENIED ('dynamic repository location'), tar -tzf likewise, while git -C nested commit alone and a path-less git after tar are allowed — the branch's justification ('An archive can place a symlink anywhere below the extraction directory') is false for create/list mode; this is over-denial of ordinary work commands.

中文说明

提取器分支仅凭 PATH_EXTRACTING_PROGRAMS 词出现就触发、不检查模式,归档创建/列举(tar -c、tar -t、unzip -l、cpio -t)并不写任何东西却把整个 cwd 标为重链目标,随后 cwd 之下带路径的 git 被误拒。探针验证:tar -czf backup.tgz nested && git -C nested commit -m x 被拒(dynamic repository location),tar -tzf 同样;单独 git -C nested commit 与 tar 后无路径 git 均放行。分支理由(归档可在解压目录下任意放置符号链接)对创建/列举模式不成立——这是对普通工作命令的过度拒绝。

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

Comment on lines +1911 to +1913
if (
!backtick &&
substitution > 0 &&

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The substitution counter in readTopLevelSeparators (and core splitCommands) closes $( at the first unquoted ) even when that ) only closes a nested bare subshell ( ... ); both parsers share the bug, so the cross-check stays green while splitCommands cuts inside a live command substitution, and the severed fragment then fails extractCommandSubstitutions (which IS paren-aware) — the guard hard-denies innocent, reasonably common shell constructs. — Concrete cost: probe-verified echo $( (sleep 1); date ) -> DENIED ('could not be parsed before execution'), likewise make $( (test -d build) || echo init ); bash executes both fine. Fail-closed, so no security harm — but unconditional denial of defensive $( (test -d x) || ... ) idioms and makefile recipes, with the disagreement safety net silent because both parsers misparse identically.

中文说明

readTopLevelSeparators(与 core splitCommands)的替换计数器在嵌套裸子shell的 ) 处就关闭 $(,两个解析器同病,交叉校验保持绿色而 splitCommands 在活的命令替换内部切断,残片在括号感知的 extractCommandSubstitutions 处失败——守卫对无辜且常见的 shell 构造硬拒绝。探针验证:echo $( (sleep 1); date ) 与 make $( (test -d build) || echo init ) 均被拒(could not be parsed),bash 执行正常。fail-closed 无安全危害,但对防御性 $( (test -d x) || ... ) 惯用法与 makefile 配方是无条件拒绝,且双方同样误解析使失配安全网沉默。

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

…fix drops

Two escapes surfaced by the round-12 review, both reproduced against the guard.

- The env-assignment arm of both text marker patterns required `(^|\s)` before
  the key, while the sibling `cd`/`pushd` arm already allowed `;&|(){}`
  boundaries. A relocation glued to a delimiter inside a quoted wrapper payload
  (`su -c 'true;GIT_DIR=<outside> git reset --hard'`) therefore evaded the
  unrecognized-program backstop. Both arms now share the same boundary class.
- `invokeDefinedBody` located the invoked name with a raw `findIndex` that
  also matched redirect operands, so a decoy `> g` whose target equals the
  function name truncated the prefix-assignment scan to empty and dropped the
  call's `GIT_DIR=` relocation. The lookup now skips redirect/fd operands like
  `readProgramWord` does.

Adds regressions for the delimiter-glued and redirect-decoy forms.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

Not reviewed: reverse audit — stopped at the 3-round huge-diff cap without converging (rounds 1, 2 and 3 each reported newly verified findings).

Not reviewed: build-and-test — CI's 'Integration Tests (CLI, No Sandbox)' job was skipped and its suite did not run locally (workspace unit suites covering all changed packages passed); Test (macos-latest)/Test (windows-latest) legs also skipped — no platform-specific tests exist in the diff (the R8-15 Windows claim was probed against node:path.win32).

Not explored to full depth (tool budget reached): "You are review agent reverse-audit — Reverse audit agent…": exhaustive enumeration of git-config(1) command-executing keys beyond the three families named (walked the layer, found and proved the gap; did not attempt a co…; "You are review agent reverse-audit — Reverse audit agent…": none — all checks above completed within budget.; "PR #8687 adds a daemon host-side guard for qwen serve that…": I did not run the unit tests for the new files; I did not trace channel-worker daemon spawn paths other than runQwenServe for marker env var leakage (channel wo…; "PR #8687 adds a daemon host-side guard for qwen serve that…": did not execute the new test files (relied on tsc + trace). Budget gap: did not exhaustively trace channel-worker spawn paths beyond confirming the markers are …; "You are review agent reverse-audit — Reverse audit agent…": macOS/BSD sudo -a acceptance could not be verified from this Linux runner (Finding 2's trigger platform)., and 13 more.

[Critical] R13-10: Hidden workspace-memory (remember/dream) agents run with NO tool-invocation guard: createHiddenWorkspaceMemoryConfig (acpAgent.ts ~859) proxies the guard-less agent-level config (only getChatRecordingService/getTranscriptPath overridden); Config.toolInvocationGuard has no setter and the only attach site is newSessionConfig (~11959); both planners grant shell (allowShell: true). Under default qwen serve (built-in guard, no provider), a remember/dream agent's run_shell_command reaches CoreToolScheduler with getToolInvocationGuard() === undefined — evaluateToolInvocationGuard never runs, no externalToolGuardPrepare is sent, and the daemon-side createDaemonToolGuard never sees the call: cross-worktree git mutations from the hidden chain are unguarded. The design doc's 'hidden-agent tool calls traverse the same managed guard' promise is contradicted by the code. Could not be anchored to a diff line (the attach-site lines are outside this PR's hunks). — R13-10:隐藏的 workspace-memory(remember/dream)agent 运行时没有任何 tool-invocation guard:createHiddenWorkspaceMemoryConfig 代理的是无 guard 的 agent 级配置;Config.toolInvocationGuard 无 setter,唯一挂载点是 newSessionConfig;两个 planner 都授予 shell。默认 qwen serve 下,隐藏链的 run_shell_command 到达 CoreToolScheduler 时 getToolInvocationGuard() === undefined——守卫求值永不运行、不发 externalToolGuardPrepare、daemon 侧永远看不到该调用:来自隐藏链的跨 worktree git 变更不受守卫。设计文档'隐藏 agent 工具调用经过同一 managed guard'的承诺与代码矛盾。

中文说明

仅完成部分审查,审查缺口已披露。

未审查:reverse audit — stopped at the 3-round huge-diff cap without converging (rounds 1, 2 and 3 each reported newly verified findings)。

未审查:build-and-test — CI's 'Integration Tests (CLI, No Sandbox)' job was skipped and its suite did not run locally (workspace unit suites covering all changed packages passed); Test (macos-latest)/Test (windows-latest) legs also skipped — no platform-specific tests exist in the diff (the R8-15 Windows claim was probed against node:path.win32)。

未探索到全部深度(达到工具调用预算):"You are review agent reverse-audit — Reverse audit agent…"exhaustive enumeration of git-config(1) command-executing keys beyond the three families named (walked the layer, found and proved the gap; did not attempt a co…"You are review agent reverse-audit — Reverse audit agent…"none — all checks above completed within budget."PR #8687 adds a daemon host-side guard for qwen serve that…"I did not run the unit tests for the new files; I did not trace channel-worker daemon spawn paths other than runQwenServe for marker env var leakage (channel wo…"PR #8687 adds a daemon host-side guard for qwen serve that…"did not execute the new test files (relied on tsc + trace). Budget gap: did not exhaustively trace channel-worker spawn paths beyond confirming the markers are …"You are review agent reverse-audit — Reverse audit agent…"macOS/BSD sudo -a acceptance could not be verified from this Linux runner (Finding 2's trigger platform).,另有 13 条。

[Critical] R13-10: Hidden workspace-memory (remember/dream) agents run with NO tool-invocation guard: createHiddenWorkspaceMemoryConfig (acpAgent.ts ~859) proxies the guard-less agent-level config (only getChatRecordingService/getTranscriptPath overridden); Config.toolInvocationGuard has no setter and the only attach site is newSessionConfig (~11959); both planners grant shell (allowShell: true). Under default qwen serve (built-in guard, no provider), a remember/dream agent's run_shell_command reaches CoreToolScheduler with getToolInvocationGuard() === undefined — evaluateToolInvocationGuard never runs, no externalToolGuardPrepare is sent, and the daemon-side createDaemonToolGuard never sees the call: cross-worktree git mutations from the hidden chain are unguarded. The design doc's 'hidden-agent tool calls traverse the same managed guard' promise is contradicted by the code. Could not be anchored to a diff line (the attach-site lines are outside this PR's hunks). — R13-10:隐藏的 workspace-memory(remember/dream)agent 运行时没有任何 tool-invocation guard:createHiddenWorkspaceMemoryConfig 代理的是无 guard 的 agent 级配置;Config.toolInvocationGuard 无 setter,唯一挂载点是 newSessionConfig;两个 planner 都授予 shell。默认 qwen serve 下,隐藏链的 run_shell_command 到达 CoreToolScheduler 时 getToolInvocationGuard() === undefined——守卫求值永不运行、不发 externalToolGuardPrepare、daemon 侧永远看不到该调用:来自隐藏链的跨 worktree git 变更不受守卫。设计文档'隐藏 agent 工具调用经过同一 managed guard'的承诺与代码矛盾。

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

const currentEntry = this.resolveEntry(sessionId);
if (
!this.ownsSession(sessionId) ||
currentEntry !== entry ||

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] R13-11: Prompt-less guard requests are not serialized against changeSessionCwd (probe-verified with a fix-and-flip): effectiveCwd — the containment basis forwarded at line 1344 — is mutated IN PLACE by changeSessionCwd (bridge.ts:7693) during the handler await; the post-call recheck is reference-identity only and prompt-less requests skip the prompt-state arm (changeSessionCwd throws CdWhilePromptActiveError only while promptActive). — Failure scenario: A context-less shell check (subagent/cron/background path — the exact flows this PR's prompt-less support creates) is in flight while a /cd completes on the same session: the decision validated against basis B0 is returned after the basis became B1, allowing e.g. git -C /workspace/sibling reset --hard that the narrowed effectiveCwd would deny. A naive reference recheck cannot see the in-place write; capturing the basis pre-await and comparing flips the probe to rejected.

中文说明

(见英文说明)

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


// Programs whose own `-C` means something else entirely (`grep -C 5`,
// `tar -C dir`), so it must not read as a git relocation marker.
const PROGRAMS_WITH_OWN_C_FLAG = new Set([

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] R13-25: PROGRAMS_WITH_OWN_C_FLAG includes make, whose -C is a REAL chdir applied before recipes run: the own-C carve-out skips hasGitRelocationMarker and the -C-free text marker matches nothing (probe-verified ALLOWED for -C and --directory; tar gets PATH_EXTRACTING_PROGRAMS treatment, make has none). — Failure scenario: make -C <outside> git-release → allowed: GIT_WORD gate passes on the git-bearing target, ownsCFlag suppresses the -C marker scan, state is empty, and make chdirs and runs the recipe's git against the outside repository (make chdir verified live).

中文说明

(见英文说明)

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

const token = parsed[index];
if (typeof token === 'string') {
const isRedirectOperand = redirectOperand;
redirectOperand = false;

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] R9-25 still stands: redirectOperand is cleared only on string tokens; a >(pwn) body's first word gets redirect:true and is skipped by both replay gates (readProgramWord/runFromProgramWord) and the marker gate. — Failure scenario: f() { git -C /outside reset --hard; }; true >(f) — the run [f(redirect:true)] gets programToken undefined, shadow dispatch and definition readers skip it, evaluateUnrecognizedRun finds no git word → allowed while bash runs f in the procsub.

中文说明

R9-25 仍然成立:redirectOperand 只在字符串 token 上清除;>(…) 体的第一个词继承 redirect:true,被重放闸门(readProgramWord/runFromProgramWord)和 marker 闸门同时跳过。失败场景:f() { git -C /outside reset --hard; }; true >(f)——run [f(redirect:true)] 的 programToken 为 undefined,影子分发与定义读取器都跳过它,backstop 无 git 词 → 放行,而 bash 在进程替换中执行 f。

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

Comment on lines +438 to +439
if (inner.op === '(') depth++;
else if (inner.op === ')') {

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] R13-S1: The $(...) body consumption loop counts only bare (/) ops, but shell-quote emits <(/>( as their own ops closed by a bare ) — the first ) inside the substitution satisfies the counter and a leftover ) leaks into the main tokenizer. UPGRADED from Suggestion: the verifier found the allow-flip the finding as filed denied. — Failure scenario: (cd <outside>; echo $(cat <(echo hi)); git reset --hard) → allowed while bash executes git with cwd=outside (verified): the early break leaks a ) that pops the enclosing subshell scope early, discarding the subshell's tracked outside-cwd before the git segment is evaluated. The round-5 fix covered the main loop's identical failure mode; the substitution-body loop was missed.

中文说明

(见英文说明)

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

runs.at(-1)!.tokens.push({ text: pattern, dynamic: true });
continue;
}
if (SUBSHELL_OPENING_OPERATORS.has(op)) {

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] R9-2 still stands: process substitutions split runs — op ( opens a new run and never resumes; git > >(true) -C /outside reset --hard strands [-C /outside reset --hard] as an other run with no git word that evaluateUnrecognizedRun clears. — Failure scenario: git > >(true) -C /outside reset --hard — run1 [git] is unrelocated in-bounds, run3 [-C /outside reset --hard] has no git word and passes; bash executes the full relocated command.

中文说明

R9-2 仍然成立:进程替换会切断 run——op ( 开启新 run 且永不恢复;git > >(true) -C /outside reset --hard 把 [-C /outside reset --hard] 留作无 git 词的 other run,被 evaluateUnrecognizedRun 放行。失败场景:run1 [git] 未重定位界内,run3 无 git 词通过;bash 执行完整的重定位命令。

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

Comment on lines +139 to +140
directory. The command-executing keys are `alias.*`, `core.askPass`,
`core.editor`, `core.fsmonitor`, `core.pager`, `core.sshCommand`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R12-17 still stands: the design doc's enumeration of command-executing -c/--config-env keys covers 13 of the 28 families enforced in GIT_COMMAND_CONFIG_KEY_PATTERNS (the 15 verified omissions include include.path, includeif.*.path, core.gitproxy, remote..proxy, credential.helper, browser..cmd, diff.external, gc.recentObjectsHook, and more). — Failure scenario: The committed spec undercounts the control's own deny surface by more than half; anyone porting or auditing the guard from the doc rebuilds a materially different policy.

中文说明

(见英文说明)

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


The guard is a containment control against mis-targeted Git invocations
expressed in the literal forms above. It is not a sandbox against a
prompt-injected agent: script-file contents are not read, variable values are

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R13-S10: The doc twice states variable values are 'not tracked across commands' (Limitations + Non-goals), but the guard tracks shell-local values chain-wide in shellLocals and expands them via expandShellLocals — whose own JSDoc exists precisely because X=git; Y='-C <outside> reset --hard'; $X $Y is a relocation the literal scan cannot see; the doc's Policy section itself relies on chain-wide state. — Failure scenario: A maintainer reasoning from the doc treats the shellLocals/expansion machinery as out-of-scope accretion (removable or not-to-be-extended) or mis-models the control — e.g. concluding X=/outside; git -C $X reset --hard slips past, when the implemented guard resolves $X and denies. For a security control whose doc claims to enumerate its own boundary, the stated boundary is wrong.

中文说明

(见英文说明)

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

Comment on lines +264 to +265
- No heredoc body analysis: `splitCommands` has no heredoc state, so a
heredoc body is scanned as ordinary command lines. Usually that only

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R13-S4: The heredoc Non-goal bullet is stale: it states heredoc bodies are 'scanned as ordinary command lines', but the implementation strips heredoc bodies BEFORE splitting (stripHeredocBodies applied at guard.ts:2117-2118) — probe-verified: the heredoc-wrapped relocation is allowed while the direct form denies. The user-facing qwen-serve.md heredoc sentence is stale the same way. — Failure scenario: A maintainer auditing or repairing the already-filed strip defects (R8-58/R12-6/R13-5/R13-18) reads the doc and concludes the stripping is out-of-spec dead code, or treats the documented over-denial behavior as the contract — risking a fix that restores the wrong semantics.

中文说明

(见英文说明)

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

});
});

it('keeps the round-9 in-boundary equivalents alone', async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R12-18 still stands: an archive extraction can plant a .git file at the extraction root, but the extractor branch pushes trackedCwd only and never sets scope.relink.gitDir (contrast the ln/cp/mv branch which does); the actionable residue is the unqualified invariant comment at guard.ts:2474-2477 and design-doc silence — the TOCTOU itself is the deliberately documented tradeoff. — Failure scenario: tar -xf a.tar && git commit -m x is allowed with the comment promising relinked-.git redirects are tracked for every later command; the deterministic planted-symlink forms are caught downstream by discovery realpath, leaving the race window the comment overstates as closed.

中文说明

(见英文说明)

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

Comment on lines +1386 to +1389
const guard = createDaemonToolGuard();

for (const command of [
'grep -C 5 git CHANGELOG.md',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R12-19 still stands: the extractor branch fires on the mere presence of a PATH_EXTRACTING_PROGRAMS word with no mode inspection (probe-verified over-denial). — Failure scenario: tar -czf backup.tgz nested && git -C nested commit and tar -tzf … are denied as 'dynamic repository location' although create/list modes cannot plant a redirect — the branch's own justification is false for these modes; over-denial of benign commands.

中文说明

(见英文说明)

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

The dangerous-config model already denies `git -c <key>=<command>` for the
command-executing config families, but omitted three documented ones:
`trailer.<token>.command`, `man.<tool>.cmd`, and
`sendemail.(sendmailcmd|tocmd|cccmd)`. `git -c trailer.sign.command='…'
interpret-trailers` (and the man/sendemail forms) ran the configured shell
command while the guard allowed it. Adds the three patterns and regressions.
@wenshao

wenshao commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

A note on the remaining /review findings — this is a bounded, documented scope

Human review on this PR has converged: doudouOUC, yiliang114, and ytahdn each have zero unresolved threads, and their latest reviews confirm the reported escapes are fixed. CI is green.

The Critical items that keep reappearing are from the automated /review runs, re-generated (and largely re-raised as "still stands") each round. That is expected, and it is worth stating explicitly why, so the standing count is read as a deliberate boundary decision rather than an oversight.

Why the automated count does not reach zero

This guard decides by reading command text before a shell interprets it. That gap is structural, not a backlog. Bash's surface — quoting modes, expansion order, subshell boundaries, deferred function/alias bodies, builtin-vs-function resolution, environment attributes, config channels — is larger than any static token scan of it. An adversarial reviewer probing a static parser will always find another construction that hides a relocation from a static reader. The design doc says so directly (see docs/design/daemon-git-worktree-guard.md → Limitations):

Best-effort, not a boundary, against shell text written to defeat it. Constructions that hide the relocation from a static reader — variable indirection, generated payloads, exotic quoting, program words the daemon cannot model — may pass. New ones will keep being found.

Treating it as more than that would be the actual risk: an operator who believes the daemon cannot mutate a sibling worktree will grant it broader trust than the mechanism earns.

So the promise is intentionally split: reliable against Git relocation in the literal forms the doc enumerates (the case the control exists for — a mis-targeted sibling checkout, a stale -C, a cd that outlived its purpose), and best-effort against shell text written specifically to defeat the parser.

What was addressed vs. what is out of scope

  • Fixed (reproduced against the real guard + real shell first, each with a regression): every escape that landed in the rules this PR added, plus genuine coverage gaps that fit the model the guard already enforces — most recently the trailer.<token>.command / man.<tool>.cmd / sendemail.*cmd command-executing config keys. The removal/shadow-modelling logic was also collapsed into a single fail-closed rule after precise per-case modelling proved to be a self-treadmill.
  • Out of scope by design: adversarially-constructed shell text — process substitution, heredoc bodies, source/. state, delimiter/quoting tricks, obscure wrapper flags, and similar constructions that hide the relocation from a static reader. These are exactly the class the Limitations section declares best-effort.

The only paths that actually converge

  1. Accept the documented bounded promise and merge — which is the intent here. Human review is at zero, CI is green, and the self-introduced escapes are structurally closed.
  2. Move enforcement off the text — a restricted working directory, a mount/namespace view, or interception at the Git invocation rather than the shell line. That is the real fix for the adversarial tail, and it is a separate change with its own design; this PR should not grow into it by accretion.

Continuing to patch individual static-parser findings does not converge and expands the parser (and its regression surface) without moving the boundary. This PR is being merged on basis (1).

中文说明

关于 /review 剩余发现的说明 —— 这是一个有意识的、文档化的边界

本 PR 的人工评审已收敛:doudouOUC、yiliang114、ytahdn 三位均 0 条未解决线程,且最新评审确认所报逃逸已修;CI 全绿。

仍在反复出现的 Critical 来自自动 /review,每轮重新生成(且大多标 "still stands" 重提)。这是预期之内的,有必要把原因写清楚,让这个"未解决计数"被理解成有意识的边界决定,而非遗漏

为什么自动计数不会归零

这个 guard 是在 shell 解释命令之前读命令文本来判断的。这个 gap 是结构性的,不是待办积压。bash 的表达面 —— 引号模式、展开顺序、子 shell 边界、延迟的函数/别名体、builtin-vs-函数解析、环境属性、config 通道 —— 比任何静态 token 扫描都大。对抗性评审探测静态解析器,总能再构造出一种骗过静态阅读器的写法。设计文档(docs/design/daemon-git-worktree-guard.md → Limitations)对此已直言:

Best-effort, not a boundary(尽力而为,不是边界),对抗刻意写来击败它的 shell 文本。把重定向对静态阅读器隐藏起来的构造 —— 变量间接、生成式 payload、奇异引号、daemon 无法建模的程序词 —— 可能通过。新的会不断被发现。

所以这个承诺是刻意二分的:对文档列举的字面形式的 git 重定向可靠(这正是该控件存在的目的 —— 打错的兄弟 checkout、过期的 -C、活过头的 cd);对专门写来击败解析器的 shell 文本尽力而为

修了什么 vs. 什么在范围外

  • 已修(每条都先对真 guard + 真 shell 复现,并配回归):所有落在本 PR 新增规则里的逃逸,以及符合 guard 既有模型的真覆盖缺口 —— 最近是 trailer.<token>.command / man.<tool>.cmd / sendemail.*cmd 命令执行类 config key。removal/shadow 建模逻辑在逐例精确建模被证明是"自造 treadmill"后,已塌缩为一条 fail-closed 规则。
  • 设计上范围外:对抗性 shell 文本 —— 进程替换、heredoc 体、source/. 状态、分隔符/引号技巧、冷门 wrapper flag 等把重定向对静态阅读器隐藏的构造。这些正是 Limitations 声明为 best-effort 的类别。

真正能收敛的只有两条路

  1. 接受文档化的 bounded promise 并合入 —— 这正是当前的意图。人工评审 0 未解决、CI 绿、自引入逃逸已结构性关闭。
  2. 把强制点从文本挪走 —— 受限工作目录、mount/namespace 视图,或在 git 调用点拦截,而非在 shell 行预测。这才是对抗性长尾的真正解法,是一个有独立设计的单独改动;本 PR 不应靠堆叠长成它。

继续逐条打补丁不会收敛,只会扩大解析器(及其回归面)而不移动边界。本 PR 基于路径 (1) 合入。

@wenshao

wenshao commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 14, 2026

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 2545 passed · 0 failed · 2545 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:2545 通过 · 0 失败 · 2545 总计

Verification report

PR #8687 deep verification — feat(daemon): guard cross-worktree Git mutations

Verdict: merge-ready — 2545 scripted assertions executed, 0 failed.
Verified head: a0427ae032aaaa12f2ed97ecf5a155262199c8d5 (git rev-parse HEAD^2), base tip 22bacfe249dd35cba7771571eaaf9d3af5d46973 (HEAD^1). The metadata snapshot's baseRefOid (de48637a) is older than the local base tip because the branch merged main afterwards; verification used the local merge checkout.

Scope chosen: (1) A/B load-bearing proof of the central claim — relocated mutating Git is denied, read-only and in-boundary commands are unaffected; (2) fail-closed sweep of novel hostile shapes against the documented promise; (3) vacuity proof of the new 373-test suite; (4) targeted gates on every touched workspace. The PR's design doc bounds its own promise: reliable for literal relocation forms, best-effort (not a boundary) against defeat-oriented shell text — sweep cells were classified against that boundary.

中文摘要
  • 结论: merge-ready。2545 条脚本化断言全部通过,0 失败。
  • A/B 结论: 基线侧(22bacfe2)不存在任何 host 侧策略面(9/9 检查通过),且在真实 git fixture 上实际执行了 git -C <sibling> reset --hard,兄弟 checkout 被真实改写(MUTATED → original);PR 侧编译产物对同一命令在执行前拒绝:111 个中心声明单元与 145 个新型对抗形状共 256 格全部符合文档承诺的期望(232 个迁移/不可分类形状全部拒绝,19 个合法形状全部放行,含 rev-parse/cat-file 只读迁移与边界内命令;见 01-ab-head-matrix-111-cells.png02-ab-base-control-no-policy.png04-bypass-sweep-145-cells.png)。
  • 测试非空洞: 粗突变(绕过整个策略)使 373 个测试中 330 个按 expected {allowed:false} / received {allowed:true} 变红;细突变(把 status 加入只读集)恰好使 4 个相关测试变红;恢复后 373/373 重新全绿(见 03-vacuity-m1-mutant-goes-red.png)。
  • findings: 仅 2 处文档层面问题(详见正文):新增设计文档声称 SHELLOPTS 标记 unresolved,但代码刻意放行且已有测试固定(文档与代码不一致,安全影响可忽略);PR 描述中"25 个 guard 测试 / run-qwen-serve.test.ts 无法收集 / root typecheck 被阻塞"与本 checkout 实测不符(373 个测试、256 通过收集、root typecheck 通过)。无阻塞项。
  • 未覆盖范围: 逐 commit 归因(浅克隆仅 3 个 commit 可达)、Windows/macOS 运行时、真实模型驱动的 serve 端到端、脚本文件内容与跨命令变量追踪(PR 明确非目标)、heredoc 体解析(PR 明确非目标)、完整仓库测试套件(仅定向门禁)。

Central claim and A/B

Claim: a model-issued run_shell_command/monitor call that relocates Git (-C, --work-tree, --git-dir, GIT_* env assignments, wrapper chdir flags, cd chains) outside the session's effective cwd is denied before execution when mutating or unclassifiable; relocated read-only rev-parse/cat-file and in-boundary commands keep existing behavior. The guard is fail-closed for dynamic/undecidable forms, including command-executing -c config keys even inside the boundary.

Fixture: real git init repos — session workspace fixture/workspace/worktree (with nested dir, inside/outside symlinks, a nested sub-repo) and sibling checkouts outside/repo (dirty: M tracked.txt), outside/wt-linked (linked worktree), elsewhere/checkout (no "git" in path).

arm environment oracle result
base control worktree at 22bacfe2 (HEAD^1) 8 scripted absence checks (no guard module, guard wiring conditional on provider only, no effectiveCwd/invocationCwd channel, no SHELL_EXECUTING_TOOL_NAMES, child guard requires provider marker) + executed consequence demo 9/9: no policy exists; the exact command run as the base daemon would ran it mutated the sibling repo (tracked.txt: MUTATED-BY-ATTACKER-PROBEoriginal)
head matrix compiled HEAD guard (packages/cli/dist), same fixture per-cell decision vs encoded expectation (92 deny cells, 14 allow cells, 5 structural cells: monitor tool, non-shell tool, prompt-less check, missing-effectiveCwd throw, outside directory arg) 111/111
bypass sweep same guard, 145 novel shapes fail-closed expectations tagged to the documented reliable set 145/145 (0 hard fails)

Witnesses: 01-ab-head-matrix-111-cells.png (head arm; top ~118 rows of the 317-row output dropped by the capture's --rows cap, summary retained), 02-ab-base-control-no-policy.png (base arm incl. the executed mutation), 04-bypass-sweep-145-cells.png (sweep + timing ladder).

Notable head-arm cells that all behaved as documented: symlink-ancestor escape (git -C <ws>/evil-link reset --hard denied), --git-dir through a per-worktree admin dir resolved via its gitdir file (denied), chained -C ending inside (allowed) vs outside (denied), ln -s relink poisoning later containment (denied), extractor poisoning (tar -xf … && git -C nested reset --hard, denied), sub-agent scope cells — reported invocationCwd in an owned worktree becomes the boundary (escape denied, in-boundary allowed), untrusted reported cwd outside owned state fails closed, a nested-checkout sub-agent is contained to its own repo, not the parent checkout.

Base cell honesty note: on base there is no guard to drive, so the control proves policy absence structurally plus the consequence by executing the command exactly as the unguarded daemon would. The head↔base flip is: 0 policy decisions produced on base (mutation lands) → 232/232 correct deny decisions on head (92 matrix + 140 sweep deny cells), with all 19 legitimate cells (14 matrix + 5 sweep) still allowed.

Corrections

  1. Design doc vs code on SHELLOPTS (this PR's own doc). docs/design/daemon-git-worktree-guard.md says SHELLOPTS "mark[s] the invocation unresolved", but the shipped code deliberately does not treat it as a marker — the suite pins the allow at daemon-git-worktree-guard.test.ts:1586 ("SHELLOPTS is bash's own options state, not a git redirection"), and my sweep confirmed SHELLOPTS=errexit git status → allow. Security impact is negligible (a prefix assignment lands in git's env, not the shell's; exported, it can only steer bash option state, never a repository). This is a label-the-description correction: the doc sentence should drop SHELLOPTS, not a code change.

  2. PR description environment statements. The description reports "25 daemon guard tests", that run-qwen-serve.test.ts could not be collected for a missing @qwen-code/channel-github, and that root build/typecheck was blocked. In this checkout the guard suite has 373 tests, run-qwen-serve.test.ts collects and passes 256/256, and npm run typecheck passes repo-wide. These are statements about the author's checkout, not defects — but the next reader should not treat the description's test inventory as current.

Findings (none blocking)

  1. (minor, doc) The SHELLOPTS drift above — one-line doc fix, no code change suggested; behavior is the safer of the two readings anyway.
  2. (informational, expected over-denial) echo git -C <outside> reset --hard is denied ("may run a relocated Git command through an unrecognized program"). This is the documented fail-closed rule for program words outside the unwrapped set; the over-denial direction is safe and the design doc's Limitations section owns this class. Recorded so a reviewer doesn't read it as accidental.
  3. (informational) Timing ladder over model-authored text is linear, no superlinear rung: long token 2 k/5 k/20 k/65 k chars → 1.4/2.8/4.9/6.8 ms; 200/500/1000/2000 chained segments → 2.2/5.4/8.5/16.1 ms; nested $(…) depth 50/100/200 → 0.5/0.8/1.5 ms; 24 k-char bash -c payload → 2.4 ms. The parser is on the model-command hot path, so this was probed as a scaling question — no issue found.

Vacuity / mutation matrix

Suite under test: daemon-git-worktree-guard.test.ts (373 tests), run in a scratch worktree at HEAD with node_modules symlinked in (PR touches no package.json/lockfile, so the shared tree is a clean control; @qwen-code/qwen-code-core realpath verified to resolve to the same-HEAD content).

mutation expectation observed verdict
unmutated control green 373/373 pass live gate ✓
M1 blunt: evaluateBuiltInGuard early-returns {allowed:true} suite goes red on the intended assertion 330/373 fail, failures of the form expected { allowed: true } to match object { allowed: false } — the exact allow/deny mismatch the tests exist to catch killed ✓
M2 fine: add 'status' to RELOCATED_READ_ONLY_GIT_SUBCOMMANDS only relocated-status cells go red 4/373 fail: denies relocated mutating Git command 7 plus 3 other cells that use relocated status as their subcommand — all with the same allowed:false mismatch; no unrelated test moved killed ✓
restore green again 373/373 pass restored ✓

Witness: 03-vacuity-m1-mutant-goes-red.png (live mutant run, tail retained).

Targeted gates

gate result
daemon-git-worktree-guard.test.ts (cli) 373/373 pass
run-qwen-serve.test.ts (cli) — incl. the provider-attached handshake tests 256/256 pass
acpAgent.test.ts (cli) — managed-guard round-trip shapes, prompt-less fallback, fail-closed identity 413/413 pass
session/Session.test.ts (cli) 615/615 pass
bridgeClient.test.ts (acp-bridge) 103/103 pass
coreToolScheduler.test.ts + speculation.test.ts + paths.test.ts (core) 516 pass, 1 skipped (pre-existing skip)
npm run typecheck (repo-wide) pass

CLI/TUI-unchanged claim verified by wiring trace, not just diff size: toolInvocationGuard reaches a Config only through hostPolicy, which packages/cli/src/config/config.ts documents as "deliberately not sourced from argv, settings, or the environment"; the only installer is the daemon ACP host (acpAgent.ts). CoreToolScheduler/speculation gained additive context fields (sessionId, cwd) with no behavior change for hosts that ignore them.

Not covered

  • Per-commit attribution: the checkout is depth 2 (shallow), only the merge commit and its two parents exist; the snapshot lists 51 commits. The aggregate HEAD^1..HEAD diff is what was verified.
  • Live qwen serve boot with a real model: no API credentials in this sandbox; the daemon↔child round trip is covered by the PR's handshake/round-trip tests (run above) plus my wiring trace, not by a model-driven E2E.
  • Windows/macOS behavior (flagged ⚠️ by the author): verified on Linux only.
  • Documented non-goals not probed as findings: script-file contents (bash script.sh), variable values tracked across separate commands, heredoc body analysis (direction not guaranteed per the doc), and defeat-oriented constructions generally. The sweep tagged 3 such cells best-effort (e.g. GIT=$(which git); "$GIT" -C <outside> reset --hard); all three denied anyway, and the only cell where my initial expectation diverged from the shipped behavior was the deliberately pinned SHELLOPTS allow handled under Corrections.
  • Repo-wide test suite (only the touched workspaces' targeted suites ran); interactive TUI behavior.

Methodology

One container (node:22-bookworm, node v22.23.2), working tree at pull/8687/merge with npm ci + npm run build pre-run; guard exercised through its compiled output packages/cli/dist/src/serve/daemon-git-worktree-guard.js. Harnesses (ab-head-matrix.mjs, base-control.sh, bypass-sweep.mjs, make-fixture.sh) drove createDaemonToolGuard() with ExternalToolGuardPrepareRequest objects against a real-git fixture (session workspace + dirty sibling repo + linked worktree + symlink escapes + nested sub-repo + QWEN_HOME-rooted owned-worktrees dir), asserting each decision against expectations encoded from the design doc; raw per-cell output in logs/. Base arm used a scratch worktree at HEAD^1 (structural absence checks + executed mutation consequence; no base build needed since the guard does not exist there), and mutation runs used a second scratch worktree at HEAD with node_modules symlinked (readlink -f confirmed the core link resolves to HEAD-identical content; the PR changes no dependency manifests). Both worktrees were removed after use. Evidence images were produced with scripts/verify-capture.mjs; mutation red/green states were additionally asserted through vitest exit codes. All counts above come from those executed runs (assertions.json: 2545 pass / 0 fail).

Evidence images

01-ab-head-matrix-111-cells

02-ab-base-control-no-policy

03-vacuity-m1-mutant-goes-red

04-bypass-sweep-145-cells

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed 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.

LGTM✅

@wenshao
wenshao dismissed stale reviews from qwen-code-ci-bot, ytahdn, and qqqys August 14, 2026 09:55

fixed

@wenshao
wenshao added this pull request to the merge queue Aug 14, 2026
Merged via the queue into main with commit 4257916 Aug 14, 2026
61 of 62 checks passed

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

One traced bypass of the new removal logic at this head — a chained prefix (command builtin unset -f git with builtin shadowed, after a real command prefix) removes the masking shadow in bash but the guard never records the removal, so the final relocated git call is still replayed as the stale harmless body. One-condition fix and regression payloads in my Stage 2 comment. 🙏

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

Reviewed at a0427ae (head unchanged during review). I verified every finding below on this checkout by driving createDaemonToolGuard() directly and, where the shell half mattered, by executing the same construct in real bash + real git against a two-repo lab (session repo + outside repo). The PR's own 373 guard tests pass here, so nothing below is a broken-build artifact.

Short version: I can't approve this. Not because the diff is careless — it is unusually careful — but because I reproduced 8 escapes in about an hour with plain one-liners, and measured that 7 of 30 ordinary in-bounds commands are denied, on a control that is now unconditionally on for every daemon session with no way to turn it off. I think the design doc's own "Why this cannot be made complete here" section is correct, and that it argues against merging this parser.


Critical — verified escapes at this head

Each row: guard verdict measured through createDaemonToolGuard(); "real effect" measured by running the identical construct in bash and checking whether the outside repository was actually written (payload git config --local probe.pwned <id> in the outside repo, so the proof is an artifact rather than an inference).

C1. A heredoc fed to bash is a complete one-line bypass

bash <<EOF
git -C <outside> reset --hard
EOF

Guard: ALLOWED. Real bash: executed — the outside repo was mutated (probe.pwned=HEREDOC_EXEC). Same for sh <<'EOF' … EOF and bash -s <<EOF … EOF.

Cause: stripHeredocBodies removes heredoc bodies before splitCommands, but when the heredoc is the script for bash/sh, the body is the command. This is also where both docs say the opposite of what the code does — design doc Non-goals: "a heredoc body is scanned as ordinary command lines"; user doc: "Git-shaped text inside a heredoc can be denied even though the shell never executes it". Measured behavior is the reverse in both directions: a heredoc-wrapped relocation is allowed, while echo "…git -C…" > f is denied (see M1).

C2 / C3. -- before a payload drops the payload from analysis

command guard control without -- real effect
eval -- "cd <outside>" && git reset --hard ALLOWED DENIED outside repo written
bash -c -- "git -C <outside> reset --hard" ALLOWED DENIED outside repo written

bash's eval/-c treat -- as end-of-options and run the rest; the re-parse classifies -- as an unrecognized program word and discards what follows.

C4. timeout eats a dynamic duration without failing closed

timeout $DUR -C <outside> reset --hard with DUR="5 git" → guard ALLOWED; bash runs timeout 5 git -C <outside> … and the outside repo was written. consumeTimeoutWrapper does a blind index++ without checking token.dynamic.

C5. The unrecognized-program backstop doesn't expand tracked locals

X='-C <outside>'; nice git $X reset --hard    → ALLOWED  (outside repo written)
X='-C <outside>';      git $X reset --hard    → DENIED

Same variable, same relocation; adding a wrapper the guard doesn't model turns a denial into an allow. The asymmetry is the tell — evaluateUnrecognizedRun scans literal token text only.

C6. A function defined inside a compound is never recorded

if true; then f() { git reset --hard; }; fi; cd <outside>; f → guard ALLOWED; outside repo written. functionHeader is segment-start anchored, so the definition inside then … records nothing and the later bare f carries no git word.

C7. set -- +a is modeled as turning allexport off

set -a; set -- +a; GIT_WORK_TREE=<outside>; git reset --hard → guard ALLOWED. Verified with real git: git rev-parse --show-toplevel reports the outside path, i.e. the mutation lands there. set -- +a only assigns positional parameters; disablesAllExport scans past --.

C8. git config core.worktree moves the boundary for every later command

git config core.worktree <outside> → guard ALLOWED (no relocation flag in the argv, and the write itself is to the in-bounds repo). Verified afterwards, in-bounds with no flags at all:

git rev-parse --show-toplevel   → <outside>
git status --short              → sees the outside tree

So a following plain git reset --hard — which the guard allows, because there is nothing to see in it — mutates the outside working tree. This one persists past the command chain, which puts it in a different class from the parsing misses: it defeats the boundary for all subsequent calls in the session.

C9. sudo -i is treated as a neutral flag

sudo -i git reset --hard → guard ALLOWED, while sudo -D <outside> git reset --hard is DENIED. -i/--login starts the command in the target user's home — the same class of relocation as -D/-R, which the guard does model. (Guard verdict measured; I did not execute the sudo half locally.)

C10. Brace expansion in the wrapper option position

env {-C,<outside>} git reset --hard → guard ALLOWED. Verified bash -c 'echo {-C,<outside>}' expands to -C <outside>, so GNU env receives the chdir. The token doesn't start with -, so it misses both the flag branches and the fail-closed unrecognized-option branch, and falls through as the program word. (GNU coreutils only; env -C doesn't exist on BSD/macOS.)

C11. Prompt-less guard requests are bound only by channel-level ownership

promptId became optional, and for a prompt-less request the only binding is ownsSession(sessionId) — wired in bridge.ts as sessionIds.has(sessionId) over the whole multiplexed channel, defaulting to () => true in the constructor. The nominated session's entry.effectiveCwd then becomes the containment basis, and nothing ties the requester to the session it nominated. A worktree-pinned session can name a sibling session on the same channel and inherit its broader effectiveCwd. The paths this PR creates (subagent loops, cron turns, resumed background agents) are exactly the prompt-less ones.


Major — the false-positive cost, now unconditional

PRIVATE_EXTERNAL_TOOL_GUARD_ENV is now set to REQUIRED unconditionally, and createDaemonToolGuard(...) is wired at all three bridge sites. There is no flag, setting, or env var to disable it, so every qwen serve session pays this policy plus a child→daemon→child round trip per run_shell_command.

I ran 30 ordinary in-bounds developer commands through the guard with the session at a normal checkout. 7 were denied. The classes that worry me:

Text that merely mentions git -C is denied. All three denied with "may run a relocated Git command through an unrecognized program":

echo "use git -C dir status" > README.md
printf "%s\n" "git -C x commit" > note.txt
gh pr comment 1 --body "try git -C repo status"

while git commit -m "explain git -C usage" is allowed. Agents in this repo write notes, design docs, and gh pr comment bodies constantly; this makes documenting git behavior a denied action, inconsistently.

eval "$(…)" is always denied — "payload could not be resolved":

eval "$(direnv export bash)"
eval "$(ssh-agent -s)" && git push
eval "$(fnm env)" && npm test

That is the standard bootstrap for direnv / ssh-agent / fnm / nvm / pyenv.

An in-bounds subdirectory reached through a variable is denied, including read-only:

D=nested; git -C $D commit -m x        → DENIED (dynamic repository location)
D=./nested; git -C $D status --short   → DENIED

Documented as fail-closed, so not a doc mismatch — but the value is sitting in shellLocals and resolves in-bounds, so the denial is avoidable rather than necessary.

In-bounds git -C <subdir> after an archive/copy program is denied:

tar -czf backup.tgz nested && git -C nested commit -m wip   → DENIED
cp -r nested copy && git -C copy commit -m x               → DENIED
rsync -a nested/ copy/ && git -C nested status --short     → DENIED

tar -czf is create mode — it cannot plant a .git redirect — so the branch's own justification doesn't hold for it (this is the already-filed R12-19, independently reproduced).

None of these classes are in docs/users/qwen-serve.md, so an operator hitting them has no way to recognize them as policy.

Major — hidden workspace-memory agents run with no guard at all

createHiddenWorkspaceMemoryConfig(this.config) proxies the agent-level config. toolInvocationGuard is only ever attached in newSessionConfig, and Config.toolInvocationGuard is a readonly constructor param with no setter, so on that path getToolInvocationGuard() is undefined and evaluateToolInvocationGuard never runs — no externalToolGuardPrepare is sent and the daemon never sees the call. The design doc states "hidden-agent tool calls traverse the same managed guard and are inspected by the same daemon-side policy"; the code contradicts it.

Partly mitigated: createMemoryScopedAgentConfig restricts shell to isShellCommandReadOnlyASTInDirectory. But that classifier's "read-only" is not this policy — it has no notion that a relocated status/grep refreshes the target index and runs its core.fsmonitor/textconv drivers, which is precisely why this PR excludes them from RELOCATED_READ_ONLY_GIT_SUBCOMMANDS. So the mitigation doesn't cover the case the guard was built for.

Suggestions — doc/code drift in a security document

  • Design doc Non-goals and user doc both say heredoc bodies are unanalyzed / "scanned as ordinary command lines"; the code strips them (C1).
  • Both docs say "variable values are not tracked across commands"; shellLocals + expandShellLocals track them chain-wide, and the Policy section depends on that.
  • The design doc enumerates ~13 of the 28 families actually enforced in GIT_COMMAND_CONFIG_KEY_PATTERNS, so anyone re-deriving the policy from the spec builds a materially different one.

CI coverage

Test (ubuntu-latest) passes, but Test (macos-latest), Test (windows-latest), and Integration Tests (CLI, No Sandbox) are all skipping on this head. For a path-containment control this is the coverage that matters most — Windows case-insensitivity and separator handling are load-bearing for isWithinRoot and realpathNearestExistingAsync. The PR description already marks Windows/Linux ⚠️.


What I'd actually suggest

I want to be clear that I'm not disputing the problem. git -C <sibling-worktree> reset --hard from a daemon agent is real, and the trusted-effectiveCwd plumbing this PR adds is the right foundation.

My objection is to the parser, and the design doc already makes my argument better than I can:

The parser is now several times the size of the policy it protects […] Best-effort, not a boundary […] New ones will keep being found. […] Closing the gap properly means moving the decision off the text. The enforcement point, not the parser, is what would converge […] this one should not grow into it by accretion.

I agree with all of it. But the conclusion is that this parser shouldn't ship — 2910 production lines of partial bash semantics (tokenizer, heredoc stripping, subshell snapshot/restore, alias/function shadow replay, allexport modeling, nameref, env/sudo/timeout/shell wrapper argv models) against AGENTS.md's Simplicity First, which is the principle this project weighs most heavily. Thirteen review rounds, 295 unresolved Critical threads (273 of them not outdated), and the review tooling reporting that it "stopped at the 3-round huge-diff cap without converging" are the empirical form of the same conclusion. AGENTS.md also caps this: past ~5 rounds, land only Critical fixes — and here the Criticals are structural, not a punch list.

Concretely, I'd split this into what converges and what doesn't:

  1. Land now (small, useful, no guessing): the trusted context plumbing — effectiveCwd/invocationCwd on ExternalToolGuardPrepareRequest, the sessionId/cwd fields on ToolInvocationGuardContext and their three call sites, and realpathNearestExistingAsync. That's roughly 130 lines and stands on its own. Please keep promptId required, or bind the prompt-less path to the requesting session rather than to channel-level ownership (C11).
  2. Replace the parser with a literal-form-only check (~150–250 lines): -C, --git-dir[=], --work-tree[=], leading GIT_DIR/GIT_WORK_TREE/GIT_COMMON_DIR/GIT_INDEX_FILE, and cd earlier in the same chain — resolve, compare, deny. Everything else allowed rather than guessed. That is exactly the case the doc calls the control's reason to exist ("a stale -C, a cd that outlived its purpose"), it drops nearly all of the FP surface above, and it makes no claim it can't keep. Add core.worktree writes to that list (C8) — it's a literal form and it's the one miss that survives the chain.
  3. Do the enforcement-point change as its own design — decide where a command may write when it runs (restricted cwd, mount/namespace view, or interception at the git invocation), per the doc's own closing paragraph.

If the parser is kept in any form, it should at minimum be opt-in or disable-able (M2), so an operator hitting the eval "$(direnv …)" or echo "…git -C…" denials has a way out.

Happy to re-review a reduced version quickly. Reproduction harness for everything above is a single vitest file driving createDaemonToolGuard() plus a bash lab script — I can post it if useful.

中文摘要

a0427ae 上复核。所有结论均在本机独立验证:直接调用 createDaemonToolGuard() 测 guard 判定,并用真实 bash + 真实 git 在「会话仓库 / 外部仓库」双仓实验里跑同样的构造,确认外部仓库是否真的被写入。PR 自带的 373 个 guard 测试在我这里全部通过。

结论:暂不能 approve。 不是因为改动粗糙——恰恰相反它非常细致——而是我用一小时、全部是普通单行命令复现了 8 个逃逸,并测出 30 条常规「界内」命令里有 7 条被误拦;而这个控制现在对每个 daemon 会话无条件开启且无法关闭。

已验证的逃逸(guard 放行 + 真实 bash/git 确认外部仓库被改)

  1. bash <<EOF … EOF 一行完全绕过:heredoc 体在 splitCommands 前被剥掉,但喂给 bash/sh 时 heredoc 体就是脚本本身。sh <<'EOF'bash -s <<EOF 同样。两份文档在这里的描述与代码行为完全相反。
  2. eval -- "cd <外部>" && git reset --hard 放行;去掉 -- 则拒绝。
  3. bash -c -- "git -C <外部> …" 放行;去掉 -- 则拒绝。
  4. timeout $DUR -C <外部> reset --hardDUR="5 git")放行——consumeTimeoutWrapper 未检查 token.dynamic
  5. X='-C <外部>'; nice git $X reset --hard 放行,而不加 nice 的同一命令拒绝——backstop 只扫字面文本。
  6. if true; then f() { git reset --hard; }; fi; cd <外部>; f 放行——函数定义识别锚定在段首。
  7. set -a; set -- +a; GIT_WORK_TREE=<外部>; git reset --hard 放行——set -- +a 只赋值位置参数,却被建模成关闭 allexport。
  8. git config core.worktree <外部> 放行,之后不带任何标志的界内 git 命令即作用于外部工作树(已验证 rev-parse --show-toplevelstatus)。这一条会跨越整个命令链持续生效,性质比其他解析漏洞更严重。
  9. sudo -i git reset --hard 放行,而 sudo -D 被拒——-i 会切到目标用户 home,属同类重定位。
  10. env {-C,<外部>} git reset --hard 放行——花括号展开后的 token 不以 - 开头,绕过了所有分支(仅 GNU env)。
  11. 无 prompt 的 guard 请求只靠 channel 级 ownsSession 校验,请求方与其声明的 sessionId 之间没有任何绑定,可借兄弟会话更宽的 effectiveCwd 作为边界。

误拦成本(现已对所有人无条件生效,且无关闭开关):30 条常规界内命令拒绝 7 条。最麻烦的几类:字符串里只要出现 git -C 就被拒(echo … > README.mdprintfgh pr comment --body,而 git commit -m "…git -C…" 却放行);eval "$(direnv export bash)" / ssh-agent / fnm 一律被拒;界内子目录经变量引用被拒(连只读 status 也拒);tar -czf(创建模式本身无法植入重定向)、cp -rrsync 之后的界内 git -C <子目录> 被拒。这些都没有写进用户文档。

隐藏 memory agent 完全没有 guardcreateHiddenWorkspaceMemoryConfig 代理的是 agent 级 config,而 toolInvocationGuard 只在 newSessionConfig 挂载且无 setter,因此该路径上 getToolInvocationGuard() 为 undefined。设计文档的承诺与代码矛盾。memory-scoped 权限层的「只读 shell」限制并不等价于本策略(它不知道重定位的 status/grep 会跑目标仓库的 fsmonitor/textconv——而这正是本 PR 拒绝它们的原因)。

CITest (macos-latest)Test (windows-latest)Integration Tests (CLI, No Sandbox) 在此 head 上均为 skipping。对一个路径包含性控制来说,这恰是最关键的覆盖。

建议:设计文档自己的「Why this cannot be made complete here」已经把结论写清楚了——「parser 已是它所保护策略的数倍大小」「best-effort, not a boundary」「真正收敛的是执行点而不是 parser,本改动不应以累加方式长成那个东西」。我完全同意,而这个结论指向的是这个 parser 不应合入。对照 AGENTS.md 最看重的 Simplicity First,2910 行 production 代码的偏 bash 语义模拟、13 轮评审、295 条未解决 Critical、以及评审工具「三轮未收敛即停」的记录,都是同一结论的经验形态。

拆分建议:(1) 先合入可信上下文管道(effectiveCwd/invocationCwdToolInvocationGuardContextsessionId/cwdrealpathNearestExistingAsync,约 130 行),但 promptId 保持必填或把无 prompt 路径绑定到请求方会话;(2) 用 150–250 行的纯字面形式检查替代 parser(-C--git-dir--work-tree、前置 GIT_*、同链 cd,外加 core.worktree 写入),其余一律放行而不猜测——这正好覆盖文档所说「本控制存在的理由」,且不做守不住的承诺;(3) 执行点方案(受限 cwd / mount-namespace 视图 / 在 git 调用处拦截)作为独立设计推进。若仍保留 parser,至少要做成可选或可关闭。

需要的话我可以把复现用的 vitest 文件和 bash 实验脚本贴出来。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.12.

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.

7 participants