Skip to content

feat(serve): add workspace-scoped MCP management - #10679

Merged
ytahdn merged 10 commits into
mainfrom
codex/daemon-workspace-runtime-mcp
Sep 2, 2026
Merged

feat(serve): add workspace-scoped MCP management#10679
ytahdn merged 10 commits into
mainfrom
codex/daemon-workspace-runtime-mcp

Conversation

@ytahdn

@ytahdn ytahdn commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Adds workspace-scoped MCP configuration and runtime management on top of the shared workspace runtime. Configuration writes remain durable when a runtime is cold, while reload, reconnect, approval, authentication, tools, and resources are routed only to the selected live workspace runtime.

MCP readiness is projected through runtime capability status with monotonic revisions and runtime epochs, so stale responses from a replaced ACP child cannot overwrite the current state. Configuration changes also refresh MCP tools in already-running sessions without restarting those sessions.

Updates the Web Shell MCP manager to use the new API directly, select among trusted workspaces, keep the workspace visible but read-only on detail pages, and show approval or authentication state before connection state.

Why it's needed

The previous MCP management flow was tied to the primary workspace and mixed durable configuration with live runtime state. That made multi-workspace management ambiguous and allowed management refreshes to race ACP replacement or active sessions. This change makes workspace ownership explicit while preserving the existing MCP endpoints for existing consumers.

Reviewer Test Plan

How to verify

  1. Start the daemon without an existing session, open Plugins → MCP, and confirm the selected workspace becomes ready and its MCP servers load without creating a hidden session.
  2. Register two trusted workspaces, switch the workspace selector, and confirm each workspace shows only its own workspace-scoped configuration and live status. Enter a server detail page and confirm the workspace remains visible but cannot be changed there.
  3. Add, edit, enable, disable, and remove MCP servers. Confirm configuration persists, the selected runtime converges, and an already-running session remains usable and receives the updated MCP tools.
  4. Reconnect, approve, and authenticate a server. Confirm pending approval/authentication takes precedence over disconnected state and that only one process-wide OAuth flow is admitted at a time.
  5. Replace or reap an ACP runtime while status polling is active. Confirm old-epoch status is ignored and the new runtime must complete live discovery before MCP reports ready.

Local verification passed: full build, full typecheck, full lint, coordinator and workspace route tests, the complete ACP bridge test file, the complete daemon SDK client test file, and the Web Shell MCP/App regression set.

Evidence (Before & After)

N/A — no browser recording was captured; the UI and runtime flows are covered by component, route, coordinator, bridge, and SDK tests.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

Node.js 22 local workspace; daemon build and automated tests.

Risk & Scope

  • Main risk or tradeoff: explicit runtime preparation can add cold-start latency, and configuration can be durably saved before a cold or draining runtime applies it; responses distinguish deferred reconciliation from live convergence.
  • Not validated / out of scope: real third-party OAuth callbacks and interactive cross-platform browser verification were not run. Extensions, Skills, and Tools remain outside this MCP capability migration.
  • Breaking changes / migration notes: existing MCP daemon endpoints remain registered. The Web Shell MCP manager intentionally requires the new workspace-scoped endpoints and does not fall back to an older daemon.

Linked Issues

N/A

中文说明

本 PR 做了什么

在共享 Workspace Runtime 之上增加 workspace 级 MCP 配置与运行时管理。runtime 处于冷状态时配置写入仍会持久化;reload、重连、审批、认证、Tools 和 Resources 只路由到用户选择的实时 workspace runtime。

MCP 就绪状态通过带单调 revision 和 runtime epoch 的 capability status 对外投影,因此被替换 ACP 子进程的迟到响应无法覆盖当前状态。配置变化也会刷新已运行 session 中的 MCP tools,不需要重启这些 session。

Web Shell MCP 管理页改为直接使用新接口,可在多个受信任 workspace 之间选择;进入服务器详情后仍显示 workspace,但不可切换;列表中待审批或待认证状态优先于连接状态。

为什么需要

之前的 MCP 管理流程绑定 primary workspace,并混合了持久化配置与实时 runtime 状态,导致多 workspace 管理边界不清晰,管理刷新也可能与 ACP 替换或活跃 session 发生竞争。本改动明确了 workspace 所有权,同时保留现有 MCP 接口供原有消费者继续使用。

Reviewer 测试计划

如何验证

  1. 在没有现有 session 的情况下启动 daemon,打开“插件 → MCP”,确认所选 workspace 能变为 ready 并加载 MCP 服务器,且不会创建隐藏 session。
  2. 注册两个受信任 workspace,切换 workspace selector,确认每个 workspace 只显示自己的 workspace 配置和实时状态。进入服务器详情页后,确认 workspace 仍可见但不可切换。
  3. 添加、编辑、启用、禁用和删除 MCP 服务器,确认配置持久化、所选 runtime 完成收敛,已有运行中 session 仍可用并获得更新后的 MCP tools。
  4. 对服务器执行重连、审批和认证,确认待审批/待认证优先于断开状态展示,并且进程级 OAuth 同一时间只允许一个流程。
  5. 在状态轮询期间替换或回收 ACP runtime,确认旧 epoch 状态被忽略,新 runtime 必须完成 live discovery 后才会报告 MCP ready。

本地验证已通过:完整 build、完整 typecheck、完整 lint、Coordinator 与 workspace 路由测试、完整 ACP bridge 测试文件、完整 daemon SDK client 测试文件,以及 Web Shell MCP/App 回归测试集。

证据(前后对比)

N/A —— 未录制浏览器操作;UI 与 runtime 流程由组件、路由、Coordinator、Bridge 和 SDK 测试覆盖。

测试平台

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

Node.js 22 本地 workspace;daemon build 与自动化测试。

风险与范围

  • 主要风险或取舍:显式 runtime 准备会增加冷启动延迟;配置可能先持久化,再由冷状态或 draining 状态的 runtime 延后应用,响应会区分 deferred reconciliation 与实时收敛。
  • 未验证/范围外:未运行真实第三方 OAuth 回调和跨平台交互式浏览器验证。Extensions、Skills 和 Tools 不属于本次 MCP capability 迁移范围。
  • 破坏性变化/迁移说明:现有 MCP daemon 接口仍然注册。Web Shell MCP 管理页有意要求新的 workspace-scoped 接口,不再回退旧 daemon。

关联 Issue

N/A

钉萁 added 4 commits August 31, 2026 10:35
…e-runtime-mcp

# Conflicts:
#	docs/design/daemon-multi-workspace-hardening.md
#	docs/design/session-idle-reaper/README.md
#	docs/design/workspace-runtime-architecture.md
#	docs/developers/daemon/00-index.md
#	docs/developers/daemon/03-acp-bridge.md
#	docs/developers/daemon/05-mcp-transport-pool.md
#	docs/developers/daemon/08-session-lifecycle.md
#	docs/developers/daemon/17-configuration.md
#	docs/developers/daemon/20-quickstart-operations.md
#	docs/users/qwen-serve-deploy-local.md
#	packages/acp-bridge/src/bridge.test.ts
#	packages/acp-bridge/src/bridge.ts
#	packages/cli/src/commands/serve.ts
#	packages/cli/src/serve/routes/workspace-runtime.test.ts
#	packages/cli/src/serve/server/error-response.test.ts
#	packages/cli/src/serve/types.ts
#	packages/cli/src/serve/workspace-runtime-coordinator.test.ts
#	packages/cli/src/serve/workspace-runtime-coordinator.ts
#	packages/cli/src/serve/workspace-service/index.ts
#	packages/sdk-typescript/test/unit/DaemonClient.test.ts
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

⚠️ Qwen Triage ended earlyview run. It stopped before finishing; check the run log.

⚠️ Qwen Triage 提前结束 —— 查看运行。未跑完,请查看运行日志。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

  • Template: complete ✓
  • Problem: this is the MCP slice of the workspace-runtime migration that feat(serve): establish workspace runtime ownership #10593 established (merged yesterday) — docs/design/workspace-runtime-architecture.md and docs/design/mcp-management-runtime-model.md on main already define this target, so the motivation is grounded in the shipped design rather than a theoretical concern. No linked issue, but the design docs play that role here.
  • Direction: aligned. This is the Stage-2/3 MCP cut of the documented plan: capability revision + epoch projection in the coordinator, workspace-scoped config/runtime routes, the daemon-global OAuth lane, and the Web Shell MCP page migration.
  • Size: cross-package core surface (acp-bridge, cli/serve, sdk-typescript, web-shell) — 2,566 production lines across 25 files, 1,736 test lines across 11 files, 110 doc lines. feat type, so no size block — but this is well past both the 500-line maintainer-awareness bar and the 1000-line large-PR advisory: a maintainer should be in the loop, and if splitting is still feasible (daemon routes vs Web Shell migration would be the natural seam) it would make review easier.
  • Approach: scope matches the design's acceptance criteria — §14.2 makes page behavior the completion standard, so bundling the Web Shell migration is defensible rather than scope creep. Structure looks coherent on first pass: the coordinator stays the single writable owner of MCP capability state, mutations run through one serial lane, and cross-epoch cache reuse is gated.
  • Risk: high-risk path matched — packages/cli/src/acp-integration/acpAgent.ts (the revert-history signal). Full review depth and CI evidence before any approval call.

Moving on to code review. 🔍

中文说明

感谢贡献!

  • 模板:完整 ✓
  • 问题:这是 feat(serve): establish workspace runtime ownership #10593(昨天已合并)确立的 workspace-runtime 迁移的 MCP 部分——main 上的 docs/design/workspace-runtime-architecture.mddocs/design/mcp-management-runtime-model.md 已定义了该目标,动机来自已落地的设计而非理论担忧。没有关联 issue,但设计文档起到了同等作用。
  • 方向:对齐。属于文档化计划的阶段二/三 MCP 切片:coordinator 的 capability revision + epoch 投影、workspace 级 config/runtime 路由、daemon 全局 OAuth lane、Web Shell MCP 页面迁移。
  • 规模:跨包触及核心面(acp-bridgecli/servesdk-typescriptweb-shell)——2,566 生产行(25 文件)、1,736 测试行(11 文件)、110 文档行。feat 类型,不因规模阻塞——但已远超 500 行维护者关注线和 1000 行大 PR 建议线:需要维护者跟进;如果还能拆分(daemon 路由与 Web Shell 迁移是天然切缝),会让审查更容易。
  • 方案:范围与设计的验收条件一致——§14.2 以页面行为为完成标准,因此打包 Web Shell 迁移是合理的,不算范围蔓延。结构初看连贯:coordinator 仍是 MCP capability 状态的唯一可写所有者,mutation 走单一串行 lane,跨 epoch 缓存复用受防护。
  • 风险:高风险路径命中——packages/cli/src/acp-integration/acpAgent.ts(revert 历史信号)。在做任何批准决定前需要完整审查深度与 CI 证据。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🖼️ web-shell visual preview

Rendered against a mock daemon (no real backend): the PR base vs this PR head 3b936af. Only screenshots that changed are shown (flows below, if any, are head-only) — refreshes on every push.

Screenshots · before / after

terminal-turn-error-copy-narrow-dark before/after

terminal-turn-error-copy-narrow-light before/after

Full-resolution recordings (.webm) are attached to the workflow run.

Qwen Code · web-shell visuals

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

This reads like a careful implementation of the target design, not an improvisation. The coordinator stays the single writable owner of MCP capability state, every write is guarded by revision (rejects superseded operations) and runtimeEpoch (rejects replaced ACP children), all runtime mutations funnel through one serial lane, and the config/runtime split from §13.1 is respected: durable writes go through the existing mutation lock + strict gate, live operations go through the coordinator, and activation: applied|deferred|reconciling is reported honestly. The process-wide OAuth lane is a single closure shared by all bridge instances, with release paths covering synchronous rejection, non-pending responses, channel teardown, and the timeout-retire path (retire → channel exit → teardown releases, so the lane cannot wedge). The Web Shell side checks epoch equality before trusting any catalog read, which is exactly what §9 asks of pages. Test coverage is genuinely pinning: revision coalescing, cold deferral, drain-rollback replay, cross-workspace OAuth rejection, admission retention after a timed-out RPC, and untrusted-workspace read-vs-mutate asymmetry all have dedicated cases.

Things worth a maintainer's attention:

  • Bare-preheat preservation removed, docs not updated. The PR deletes preserveForFirstStatusRead and inverts the corresponding bridge test (reaps an idle-timeout-zero preheat after workspace status settles), so this is intentional. But the architecture doc still says the opposite in three places — §8.3 bullet 3 ("裸 preheat 自身不启动立即回收器,而是保留到首次使用"), §14.4, and §16.1 item 9 — and the PR's own doc edits don't touch those lines. Concretely: with the default channelIdleTimeoutMs=0, the child created by startup preheat (still a bare bridge.preheat() in startBridgePreheat) is now reaped as soon as the first workspace-control read settles, so legacy consumers that haven't migrated to ensure pay the cold start the preheat policy exists to avoid. Either the docs should be updated to record the deliberate change (and startup preheat reconsidered or given a keepalive), or the mechanism should stay until Stage 4 removes preheat outright.
  • Enable/disable scope logic now lives in three places. McpManagerPage.runAction resolves the scope via server origin (configScopeForServer), while actions.ts and useDaemonMcp.ts each carry their own copy of a config-map lookup (serverName in config.user && !(serverName in config.workspace)). Two subtly different heuristics for the same decision, and the enable/disable branch inside manageMcpServer/manageServer appears unreachable from the current UI (the page routes enable/disable through setConfigServerEnabled, the chat message only sends approve/authenticate/reconnect). Worth collapsing into one shared helper and deleting the dead branch if nothing else calls it.
  • Older-daemon fallbacks dropped (disclosed). loadMcpTools/loadMcpResources no longer degrade gracefully against daemons without these routes — the PR body states this is intentional ("does not fall back to an older daemon"). Fine as a direction, just confirming the deployment mix tolerates it.

Everything else checked out on read: the SDK keeps enable/disable out of runtime actions, qualified routes never fall back to primary, getLlmClient/setTools for the live-session tool refresh exist on Config (not a dead switch), the ACP-side authenticate handler returns once the auth URL exists (30 s start budget), so the SDK's ~5 min budget for manageRuntimeMcpServer is adequate, and the 409 mapping for concurrent auth is stable-coded.

sequenceDiagram
    participant P1 as Web Shell MCP page
    participant P2 as WorkspaceRuntimeCoordinator
    participant P3 as Bridge
    participant P4 as ACP child
    P1->>P2: POST runtime ensure
    P2->>P3: preheat with 10 minute keepalive
    P3->>P4: spawn or reuse channel
    P2->>P2: queue MCP prepare at current revision
    P2->>P4: MCP initialize when discovery not started
    loop poll every 250 ms until completed or budget
        P2->>P3: read MCP status tagged with epoch
        P3-->>P2: live status for current epoch
    end
    P2-->>P1: lifecycle plus capabilities.mcp ready or starting
Loading
Files changed (30 of 38 shown)
File What changed
packages/cli/src/serve/workspace-runtime-coordinator.ts MCP capability state machine: revision and epoch projection, serial physical lane, cold deferral, drain-rollback replay, management-operation counting
packages/cli/src/serve/routes/workspace-mcp-config.ts New durable MCP config routes for user and workspace scope with enable/disable exclusions and activation fan-out
packages/cli/src/serve/routes/workspace-runtime-mcp.ts New runtime MCP routes: status/tools/resources reads plus reload, restart, approve, authenticate, clear-auth through the coordinator
packages/acp-bridge/src/bridge.ts Global OAuth admission hook with full release lifecycle, epoch and source tagging of MCP status, cross-epoch cache-merge gating, preheat preservation removal
packages/acp-bridge/src/status.ts Capability status type and epoch/source fields on MCP status shapes
packages/acp-bridge/src/bridgeErrors.ts McpAuthenticationInProgressError
packages/acp-bridge/src/bridgeOptions.ts acquireMcpAuthentication admission option
packages/cli/src/serve/server.ts Registers the new config and runtime route sets
packages/cli/src/serve/run-qwen-serve.ts One process-wide OAuth admission closure wired into every bridge creation site
packages/cli/src/serve/server/error-response.ts 409 mapping for concurrent authentication
packages/cli/src/serve/routes/workspace-settings.ts Exports the mutation lock and write-prep helpers for reuse
packages/cli/src/serve/acp-session-bridge.ts Re-exports the new error
packages/cli/src/acp-integration/acpAgent.ts Refreshes LLM-client tools after config-driven MCP sync; /mcp status derives from pool entries
packages/sdk-typescript/src/daemon/DaemonClient.ts User-scope MCP config methods and workspace-scoped config/runtime MCP methods, REST transport
packages/sdk-typescript/src/daemon/types.ts Config status, mutation result, reload result, capability status types
packages/sdk-typescript/src/daemon/index.ts Type exports
packages/sdk-typescript/src/index.ts Type exports
packages/web-shell/client/daemon/workspace/actions.ts Actions migrated to workspace-scoped SDK methods, config mutation actions added, older-daemon fallbacks removed
packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.ts Optional workspace targeting and epoch-aware action wrappers
packages/web-shell/client/daemon/workspace/types.ts Action interface additions
packages/web-shell/client/components/mcp/McpManagerPage.tsx Ensure-on-entry, epoch-checked catalog reads, config CRUD via new routes, workspace control slot, auth labels before connection state
packages/web-shell/client/components/plugins/PluginManagerPage.tsx Workspace selector: trusted workspaces only, locked read-only on detail pages, remount on switch
packages/web-shell/client/components/messages/McpStatusMessage.tsx Auth-pending and auth-failed labels ahead of connection state, epoch-guarded reload loop
packages/web-shell/client/App.tsx Preloaded MCP message plumbing for the plugins page removed
packages/web-shell/client/i18n.tsx New auth status strings, unused keys removed (EN and ZH)
docs/design/mcp-management-runtime-model.md Daemon API ownership section describing this PR's contract
docs/design/workspace-runtime-architecture.md Progress markers updated for the MCP capability stage
packages/cli/src/serve/workspace-runtime-coordinator.test.ts 13 new state-machine cases: coalescing, deferral, epoch waits, drain replay
packages/cli/src/serve/workspace-qualified-rest.test.ts Route scope, trust gate, wildcard exclusion, user-scope fan-out cases
packages/acp-bridge/src/bridge.test.ts Epoch cache policy, OAuth lane serialization and admission retention cases
…and 8 more files Remaining unit tests: acpAgent, run-qwen-serve, error-response, DaemonClient, App, PluginManagerPage, DaemonWorkspaceProvider, actions

Testing

Unattended run — the PR's code is not executed here; the evidence below is the PR's own CI on the reviewed commit, fetched once via the API (no polling).

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

Check Conclusion
web-shell E2E Smoke (ubuntu-latest, Node 22.x) 🚫 cancelled
Capture web-shell visuals (ubuntu-latest, Node 22.x) ✅ success
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Live Host (macos-latest) ✅ success
macos-latest / Java 21 ✅ success
Post Coverage Comment (ubuntu-latest, 22.x) ✅ success
Real daemon E2E / Java 11 ✅ success
Secret scan (TruffleHog) ✅ success
Serve A/B (ubuntu-latest, Node 22.x) ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
windows-latest / Java 21 ✅ success

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

The main unit suite, Serve A/B, daemon E2E, and web-shell visual capture were still running at review time — three PR-CI workflow runs (Qwen Code CI, Serve A/B, SDK Java) remain in progress on this commit, so no verdict is drawn from CI yet; the table above is updated in place once they settle. macOS/Windows test jobs are skipped on this run. Not verified here: live ACP-child behavior (real discovery convergence, OAuth round-trip, runtime replacement under load) — the suite exercises those paths against mocked bridges and services. The author reports local verification on macOS (their claim, not independently re-run); Windows/Linux untested per the PR body.

Sandboxed verification would settle this: @qwen-code /verify — the epoch/revision guards, workspace-scoped routing, and the single-OAuth-lane semantics are proven here against mocks, so this PR's suite could pass even if the real runtime reconciliation path misbehaved; an A/B wire-level run against a real daemon and real ACP child would pin the behavioral claims this PR actually makes.

中文说明

代码审查

这是一次对目标设计的忠实实现,而不是自由发挥:coordinator 仍是 MCP capability 状态的唯一可写所有者,每次写入都受 revision(拒绝被取代的操作)与 runtimeEpoch(拒绝被替换的 ACP 子进程)双重防护;所有 runtime mutation 走单一串行 lane;§13.1 的 config/runtime 分离得到遵守——持久化写入走既有 mutation lock + strict gate,实时操作走 coordinator,并如实报告 activation: applied|deferred|reconciling。进程级 OAuth lane 是所有 bridge 实例共享的单一闭包,释放路径覆盖了同步拒绝、非 pending 响应、channel 销毁与超时退役(退役 → channel 退出 → 销毁时释放,不会卡死全局 lane)。Web Shell 侧在信任任何 catalog 读取前都校验 epoch 相等,正是 §9 对页面的要求。测试覆盖是真钉住的:revision 合并、冷延迟、drain 回滚重放、跨工作区 OAuth 拒绝、超时 RPC 后的准入保持、未信任工作区"可读不可改"都有专门用例。

需要维护者关注的点:

  • 裸 preheat 保留机制被移除,但文档未同步。 PR 删除了 preserveForFirstStatusRead 并反转了对应测试(reaps an idle-timeout-zero preheat after workspace status settles),说明是有意为之。但架构文档有三处仍在陈述相反结论——§8.3 第 3 条("裸 preheat 自身不启动立即回收器,而是保留到首次使用")、§14.4、§16.1 第 9 条——而本 PR 的文档改动没有触及这些行。具体影响:默认 channelIdleTimeoutMs=0 时,启动预热(startBridgePreheat 仍是不带 keepalive 的 bridge.preheat())创建的子进程会在第一次 workspace-control 读取结算后立刻被回收,尚未迁移到 ensure 的旧消费者将重新承担预热策略本要避免的冷启动。要么更新文档记录这一有意变更(并重新考虑启动预热或为其加 keepalive),要么把该机制保留到阶段四正式移除预热为止。
  • enable/disable 的 scope 判定逻辑出现在三处。 McpManagerPage.runAction 按服务器来源判定(configScopeForServer),actions.tsuseDaemonMcp.ts 各自携带一份 config map 查找(serverName in config.user && !(serverName in config.workspace))。同一决策用了两种微妙不同的启发式,且 manageMcpServer/manageServer 内的 enable/disable 分支从当前 UI 看不可达(页面走 setConfigServerEnabled,聊天消息只发 approve/authenticate/reconnect)。建议收敛为一个共享 helper,并删除无人调用的分支。
  • 旧版 daemon 的降级回退被移除(已披露)。 loadMcpTools/loadMcpResources 不再对缺少这些路由的旧 daemon 优雅降级——PR 正文声明这是有意为之("不再回退旧 daemon")。方向上没问题,但请确认部署组合能接受。

其余部分读下来没有问题:SDK 未把 enable/disable 混入 runtime 动作;qualified 路由不回退 primary;活跃会话工具刷新所用的 getLlmClient/setToolsConfig 上真实存在(不是死开关);ACP 侧 authenticate 处理器在拿到 auth URL(30 秒启动预算)后即返回,所以 manageRuntimeMcpServer 约 5 分钟的客户端预算足够;并发认证的 409 映射有稳定 code。

(时序图见正文:ensure → preheat/keepalive → 串行 MCP prepare → 轮询带 epoch 的状态直至 ready/starting。)

测试

无人值守运行——此处不执行 PR 代码;以下证据是 PR 自身 CI 在被审提交上的结果,通过 API 一次性获取(不轮询)。正文表格中:主单测套件、Serve A/B、daemon E2E、web-shell 视觉截图在审查时仍在运行——该提交上还有三个 PR CI 工作流(Qwen Code CIServe A/BSDK Java)未完成,因此暂不依据 CI 下结论;表格会在 CI 落定后就地更新。macOS/Windows 测试任务本次被跳过。此处未验证:真实 ACP 子进程行为(真实 discovery 收敛、OAuth 往返、负载下的 runtime 替换)——套件是在 mock 的 bridge/service 上演练这些路径的。作者自报在 macOS 本地验证通过(作者声明,未独立复跑);按 PR 正文,Windows/Linux 未测试。

沙箱验证可以钉住这些:@qwen-code /verify —— epoch/revision 防护、workspace 级路由、单一 OAuth lane 语义目前都是由 mock 证明的,即使真实 runtime 收敛路径有毛病,本 PR 的套件也可能全绿;对真实 daemon 与真实 ACP 子进程做 A/B 线级验证,才能钉住本 PR 真正声明的行为。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 3/5 — a disciplined, design-faithful implementation with tests that genuinely pin the state machine, but the Stage 0 core-size escalation caps this at policy, and one doc/behavior mismatch needs a maintainer to settle it.

Stepping back: before reading the diff I sketched what this PR would have to look like from the target design alone — coordinator as the single writable owner of epoch-bound MCP status, durable config routes split from runtime routes, one process-wide OAuth lane, and an ensure-on-entry Web Shell migration. The PR matches that shape almost exactly, and goes further in the right directions (drain-rollback replay, management-operation counting toward removal admission, revision coalescing). I did not find a materially simpler path — the architecture doc mandates this structure, and the predecessor (#10593) already landed the foundation it builds on. If I had to maintain this in six months, the revision/epoch discipline and the test suite would read well, not feel imposed.

Why not higher:

  • Policy cap, first. 2,566 production lines across four packages is far past the 500-line maintainer-awareness bar for core-touching work. The review above is as thorough as a static pass can be, but a change of this size gets a human sign-off in this repo's workflow, and that is this deferral, not a doubt about the code.
  • The bare-preheat question is real. Removing preserveForFirstStatusRead is intentional (the test inversion proves it), but the architecture doc still asserts the old behavior in three places, and in the default zero-idle-timeout deployment startup preheat now produces a child that dies with the first status read. A maintainer should decide whether that is the intended transitional behavior or whether the docs/startup preheat need one more edit.
  • CI has not landed. The unit suite, Serve A/B, and daemon E2E were still running at review time; the Stage 2 table updates in place when they settle. No approve-on-green marker is emitted because the Stage 0 escalation rules that out regardless of CI.
  • Small follow-ups if this moves forward: collapse the three copies of the enable/disable scope logic into one helper (the hook's enable/disable branch looks unreachable from the current UI), and confirm the dropped older-daemon fallbacks are acceptable for the deployment mix.

No approval submitted. Deferring to the maintainers — this comment is the explicit escalation; no owner could be resolved deterministically (no labels on the PR, no prior human review, no configured handle), so whoever picks it up: the two questions above are the ones I could not settle from the diff alone.

Qwen Code · qwen3.8-max

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

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 84.97% 84.97% 90.59% 84.71%
Core N/A% N/A% N/A% N/A%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   84.97 |    84.71 |   90.59 |   84.97 |                   
 src               |   86.57 |    82.86 |   88.88 |   86.57 |                   
  cli.ts           |   95.92 |    88.23 |     100 |   95.92 | ...00-701,705-706 
  llm.tsx          |   73.59 |    77.73 |   80.76 |   73.59 | ...1367-1371,1498 
  ...ractiveCli.ts |   89.27 |    83.13 |   89.06 |   89.27 | ...3157,3163,3229 
  ...liCommands.ts |   89.71 |    84.17 |   81.81 |   89.71 | ...31-633,650,757 
  ...ActiveAuth.ts |     100 |     87.5 |     100 |     100 | 66-80             
 ...cp-integration |   76.17 |    79.09 |   94.03 |   76.17 |                   
  acpAgent.ts      |   75.23 |    78.71 |    93.3 |   75.23 | ...32,14255-14256 
  ...k-reporter.ts |     100 |       80 |     100 |     100 | 81,84,119,141     
  authMethods.ts   |      92 |       60 |     100 |      92 | 33-34             
  ...heap-probe.ts |   97.39 |    96.66 |     100 |   97.39 | 243,264-265       
  errorCodes.ts    |     100 |      100 |     100 |     100 |                   
  ...ion-skills.ts |     100 |     87.5 |     100 |     100 | 17,28             
  generation.ts    |    97.1 |    81.25 |     100 |    97.1 | 109,112           
  ...figuration.ts |     100 |     95.6 |     100 |     100 | 121,196,242,259   
  ...DirContext.ts |     100 |      100 |     100 |     100 |                   
  ...ersistence.ts |   94.95 |    92.24 |     100 |   94.95 | ...13-118,227-228 
  ...management.ts |   76.99 |    71.56 |     100 |   76.99 | ...20-524,533-537 
  ...e-download.ts |    64.7 |    62.24 |    87.5 |    64.7 | ...08-609,615-619 
 ...tegration/live |   97.53 |    88.23 |   92.85 |   97.53 |                   
  ...en-context.ts |   95.89 |    82.85 |     100 |   95.89 | ...,72-73,105-106 
  ...structions.ts |     100 |      100 |     100 |     100 |                   
  ...ak-to-user.ts |   96.66 |      100 |    87.5 |   96.66 | 37-38             
  ...task-tools.ts |   98.97 |      100 |   88.88 |   98.97 | 201-202           
 ...ration/service |    97.1 |    95.89 |   93.75 |    97.1 |                   
  filesystem.ts    |    97.1 |    95.89 |   93.75 |    97.1 | ...22-123,246-247 
 ...ration/session |   91.09 |    86.58 |    95.7 |   91.09 |                   
  Session.ts       |   90.47 |    85.65 |   95.09 |   90.47 | ...84,14211-14215 
  ...entTracker.ts |   96.88 |    89.36 |      90 |   96.88 | 139-145,224       
  ...projection.ts |   98.85 |    91.59 |     100 |   98.85 | 234,250,262       
  ...stop-guard.ts |     100 |    98.07 |     100 |     100 | 37,127            
  ...eplay-page.ts |   94.19 |    86.53 |     100 |   94.19 | ...53,357,437,441 
  ...y-replayer.ts |   83.41 |    93.33 |   94.11 |   83.41 | ...30-148,266-268 
  index.ts         |       0 |        0 |       0 |       0 | 1-40              
  ...ssionUtils.ts |   89.19 |     87.8 |     100 |   89.19 | ...85-304,363-365 
  ...oal-update.ts |   98.61 |    97.29 |     100 |   98.61 | 64                
  ...lure-guard.ts |   98.32 |    97.72 |     100 |   98.32 | 294-295,340-341   
  tasksSnapshot.ts |   95.85 |    71.11 |     100 |   95.85 | 68-74,190-191     
  ...on-tracker.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...ssion/emitters |   95.68 |    91.97 |   97.14 |   95.68 |                   
  ...ageEmitter.ts |   95.36 |    92.42 |     100 |   95.36 | ...16,129-130,223 
  PlanEmitter.ts   |     100 |    85.71 |     100 |     100 | 68,70             
  base-emitter.ts  |   78.26 |    77.77 |     100 |   78.26 | 23-24,26-28       
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
  ...ll-emitter.ts |   98.57 |    94.84 |     100 |   98.57 | 75-76,394-395     
 ...ession/rewrite |   96.03 |    89.79 |   94.44 |   96.03 |                   
  LlmRewriter.ts   |   94.01 |    88.23 |     100 |   94.01 | 101-102,179-183   
  ...Middleware.ts |   96.99 |    88.37 |     100 |   96.99 | 145,153-155       
  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    |   86.65 |    80.82 |   94.01 |   86.65 |                   
  attach-lease.ts  |     100 |    97.05 |     100 |     100 | 173               
  ...t-cli-argv.ts |     100 |     92.3 |     100 |     100 | 15                
  ...ged-detach.ts |     100 |     90.9 |     100 |     100 | 40,64             
  presentation.ts  |   94.13 |    88.72 |   94.73 |   94.13 | ...57-358,382-384 
  protocol.ts      |     100 |      100 |     100 |     100 |                   
  pty-host-env.ts  |     100 |      100 |     100 |     100 |                   
  ...st-process.ts |   88.52 |    78.91 |   94.44 |   88.52 | ...1305,1395-1397 
  pty-host.ts      |   85.25 |    87.03 |   90.69 |   85.25 | ...22-524,539-540 
  ...sor-client.ts |   80.38 |    72.81 |   77.41 |   80.38 | ...22-626,652-656 
  ...r-dispatch.ts |      98 |    85.18 |     100 |      98 | 117,173,190       
  ...or-process.ts |    83.5 |     77.3 |   98.72 |    83.5 | ...4479-4482,4485 
  ...sor-runner.ts |   82.43 |    76.82 |   80.95 |   82.43 | ...69,493,496-506 
  ...sor-server.ts |   84.39 |    83.56 |    93.1 |   84.39 | ...67-568,571-588 
  ...isor-store.ts |   94.76 |    85.02 |     100 |   94.76 | ...,966,1008,1023 
  ...nal-bridge.ts |   93.98 |    91.54 |   83.33 |   93.98 | 228-238           
  ...r-sideband.ts |   94.91 |    89.36 |     100 |   94.91 | ...75-276,299-304 
 src/commands      |   90.45 |    78.26 |   65.62 |   90.45 |                   
  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.94 |      100 |      50 |   98.94 | 106               
  serve.ts         |   89.06 |    75.72 |     100 |   89.06 | ...27-930,942,953 
  sessions.ts      |     100 |      100 |      50 |     100 |                   
  update.ts        |   98.13 |    94.44 |   66.66 |   98.13 | 82-83             
 ...mmands/channel |   89.49 |    88.74 |   90.73 |   89.49 |                   
  channel-cwd.ts   |     100 |      100 |     100 |     100 |                   
  ...l-registry.ts |   94.78 |    94.59 |      90 |   94.78 | ...32-335,380-383 
  ...entry-path.ts |      75 |       50 |     100 |      75 | 8-9               
  config-utils.ts  |   96.91 |    96.27 |     100 |   96.91 | ...60-265,323-326 
  configure.ts     |    14.7 |      100 |       0 |    14.7 | 18-21,23-84       
  daemon-worker.ts |   93.72 |    85.81 |   94.33 |   93.72 | ...1305,1312-1313 
  loop-runtime.ts  |   91.66 |      100 |      50 |   91.66 | 15,22             
  ...classifier.ts |   98.53 |    96.66 |     100 |   98.53 | 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         |    87.7 |    83.63 |      88 |    87.7 | ...95,601-604,616 
  ...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  |   91.19 |    88.76 |   85.71 |   91.19 |                   
  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          |    92.9 |    84.84 |      80 |    92.9 | ...79-181,199-200 
  reconnect.ts     |   85.54 |    86.76 |    90.9 |   85.54 | 45-58,337-359     
  remove.ts        |     100 |       80 |     100 |     100 | 21-25             
 ...ommands/review |   92.08 |    90.61 |   93.59 |   92.08 |                   
  ab-drive.ts      |   85.22 |    90.47 |   94.11 |   85.22 | ...50-926,969-972 
  agent-prompt.ts  |   94.89 |    93.01 |   97.95 |   94.89 | ...3296,3631-3711 
  base-tree.ts     |   77.02 |    80.76 |   77.77 |   77.02 | ...63-384,386-399 
  capture-local.ts |   94.79 |    96.81 |   94.11 |   94.79 | ...1192,1412-1450 
  ...k-coverage.ts |   50.71 |       35 |   66.66 |   50.71 | ...40-245,279-289 
  cleanup.ts       |   92.34 |     89.5 |    90.9 |   92.34 | ...1107,1109-1110 
  comment-body.ts  |   67.85 |    87.09 |   66.66 |   67.85 | ...30,157,159-164 
  ...ent-status.ts |   94.22 |    87.32 |    90.9 |   94.22 | ...96,462,738-758 
  ...ose-review.ts |   97.38 |    94.08 |    98.8 |   97.38 | ...7376-7420,7691 
  cost-ledger.ts   |   94.58 |     94.4 |   81.25 |   94.58 | ...53-654,694-704 
  ...candidates.ts |   93.12 |    93.95 |   84.61 |   93.12 | ...49-660,662-674 
  drive.ts         |   97.12 |    89.85 |     100 |   97.12 | ...83-985,990-992 
  emit-workflow.ts |   90.57 |     93.1 |   83.33 |   90.57 | 154,176,285-295   
  extract-step.ts  |   91.36 |    90.62 |   88.88 |   91.36 | ...90-707,714-729 
  fetch-diff.ts    |   73.75 |      100 |   66.66 |   73.75 | 77-97             
  fetch-pr.ts      |   97.37 |    92.15 |     100 |   97.37 | ...1592,1806-1811 
  findings.ts      |    96.3 |    93.68 |     100 |    96.3 | ...1418,1427-1428 
  issue-context.ts |   88.15 |     93.1 |   85.71 |   88.15 | 249-276           
  load-rules.ts    |   26.41 |      100 |   16.66 |   26.41 | ...41-153,155-156 
  match-remote.ts  |   85.55 |     92.3 |   66.66 |   85.55 | 74-79,144-150     
  meta.ts          |   79.43 |    93.75 |   66.66 |   79.43 | 123-128,147-162   
  mock-provider.ts |   95.44 |    90.25 |   89.47 |   95.44 | 145,690-709       
  parse-args.ts    |   99.48 |    95.74 |     100 |   99.48 | 665,990,1046,1082 
  plan-diff.ts     |   71.42 |      100 |   66.66 |   71.42 | 162-197           
  pr-context.ts    |   96.22 |    88.86 |     100 |   96.22 | ...2580,2681-2697 
  presubmit.ts     |   94.42 |    90.38 |   94.11 |   94.42 | ...1240,1275-1306 
  ...ish-assets.ts |    81.3 |    82.22 |   85.71 |    81.3 | ...75-479,506-552 
  ...r-findings.ts |   90.74 |    83.75 |     100 |   90.74 | ...17-422,429-430 
  repo-context.ts  |   94.62 |    90.75 |     100 |   94.62 | ...66-467,482-487 
  ...ve-anchors.ts |   78.34 |    89.28 |      75 |   78.34 | ...83-188,200-217 
  revert-hunk.ts   |   91.48 |    87.94 |     100 |   91.48 | ...1189,1236-1239 
  run.ts           |   84.65 |    87.34 |   95.45 |   84.65 | ...43,859-913,927 
  save-artifact.ts |    94.2 |    92.46 |   94.11 |    94.2 | ...14-617,710-713 
  scratch-tree.ts  |   95.93 |       86 |     100 |   95.93 | ...91-392,461-464 
  script-lint.ts   |   81.23 |    80.45 |   88.88 |   81.23 | ...82-796,798-820 
  submit.ts        |   94.21 |       89 |   94.44 |   94.21 | ...1710,1738-1775 
  test-delta.ts    |   95.75 |     92.3 |      75 |   95.75 | 470-478           
  test-efficacy.ts |   84.03 |    80.48 |   96.07 |   84.03 | ...3249,3257-3277 
  test-plan.ts     |   94.61 |    91.79 |      95 |   94.61 | ...29-832,873-874 
  ...low-script.ts |     100 |      100 |     100 |     100 |                   
 ...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.37 |    94.75 |   98.71 |   97.37 |                   
  agent-briefs.ts  |   99.08 |      100 |      50 |   99.08 | 841-842           
  ...t-identity.ts |     100 |      100 |     100 |     100 |                   
  anchors.ts       |     100 |    97.04 |     100 |     100 | ...39,175,184,231 
  assets.ts        |     100 |      100 |     100 |     100 |                   
  audit-layers.ts  |   98.67 |    96.15 |     100 |   98.67 | 288-290           
  authorization.ts |    96.5 |    95.61 |     100 |    96.5 | ...54-255,629-630 
  budget.ts        |     100 |    97.95 |     100 |     100 | 887,940           
  build-budget.ts  |     100 |      100 |     100 |     100 |                   
  certification.ts |     100 |      100 |     100 |     100 |                   
  convergence.ts   |     100 |    97.94 |    92.3 |     100 | 52,515,620,716    
  coverage.ts      |   98.97 |    95.12 |     100 |   98.97 | ...1103,1648-1649 
  deadline.ts      |   98.03 |    91.66 |     100 |   98.03 | ...20,752,820,837 
  diff-flags.ts    |     100 |        0 |     100 |     100 | 75                
  diff-plan.ts     |   99.29 |    95.77 |     100 |   99.29 | 295-296,319       
  disk.ts          |     100 |      100 |     100 |     100 |                   
  effort.ts        |     100 |      100 |     100 |     100 |                   
  failing-files.ts |     100 |    93.33 |     100 |     100 | 41                
  gh.ts            |   89.53 |    95.52 |   78.94 |   89.53 | ...47,384-385,412 
  git.ts           |   96.92 |    94.11 |     100 |   96.92 | 264-265,302-303   
  heavy.ts         |     100 |      100 |     100 |     100 |                   
  import-graph.ts  |   96.68 |     95.6 |     100 |   96.68 | 180-182,211-212   
  ...ntal-scope.ts |     100 |      100 |     100 |     100 |                   
  inline-counts.ts |     100 |      100 |     100 |     100 |                   
  ...audit-gate.ts |     100 |     97.5 |     100 |     100 | 135               
  ledger.ts        |     100 |    99.47 |     100 |     100 | 884               
  local-anchor.ts  |   94.53 |    89.41 |     100 |   94.53 | ...36,669-670,837 
  local-diff.ts    |   86.77 |    94.28 |     100 |   86.77 | ...54-564,566-574 
  ...ry-context.ts |   96.61 |    95.48 |     100 |   96.61 | ...47-450,496-499 
  md-field.ts      |     100 |      100 |     100 |     100 |                   
  merge-base.ts    |     100 |      100 |     100 |     100 |                   
  narrow-diff.ts   |     100 |      100 |     100 |     100 |                   
  npm-toolchain.ts |   98.24 |    95.31 |     100 |   98.24 | ...,832,1213,1230 
  path-rules.ts    |     100 |      100 |     100 |     100 |                   
  paths.ts         |    95.6 |    88.67 |     100 |    95.6 | 40-41,168-173     
  prebuild.ts      |     100 |    96.15 |     100 |     100 | 248               
  prompt-record.ts |   98.03 |    94.23 |     100 |   98.03 | 293-294,300       
  receipt.ts       |     100 |      100 |     100 |     100 |                   
  remote-match.ts  |   98.03 |    94.73 |     100 |   98.03 | 109-110           
  report.ts        |   92.92 |    86.66 |     100 |   92.92 | 213-214,216-220   
  ...ry-context.ts |     100 |    98.66 |     100 |     100 | 187               
  resume.ts        |     100 |      100 |     100 |     100 |                   
  retirement.ts    |     100 |    94.36 |     100 |     100 | ...58-559,760,917 
  review-footer.ts |   99.55 |    98.09 |     100 |   99.55 | 548-549           
  ...w-settings.ts |     100 |    96.42 |     100 |     100 | 99                
  roster.ts        |     100 |    97.14 |     100 |     100 | 177,222           
  round-model.ts   |     100 |      100 |     100 |     100 |                   
  run-ledger.ts    |    98.2 |    93.87 |     100 |    98.2 | ...23,541,647,670 
  same-file.ts     |     100 |       95 |     100 |     100 | 46                
  ...boxed-exec.ts |   94.26 |    89.32 |   95.65 |   94.26 | ...49-550,728-729 
  shell-quote.ts   |     100 |      100 |     100 |     100 |                   
  stale-bundle.ts  |   98.18 |    94.38 |     100 |   98.18 | 431,472,512-513   
  test-utils.ts    |   99.04 |    91.66 |     100 |   99.04 | 75                
  toolchain.ts     |     100 |      100 |     100 |     100 |                   
  transcripts.ts   |   98.09 |    95.07 |     100 |   98.09 | ...92,438,707-708 
  ...pace-scope.ts |     100 |    96.96 |     100 |     100 | 186               
  workspaces.ts    |     100 |    96.85 |     100 |     100 | 222,452,499,512   
  ...ree-reader.ts |     100 |      100 |     100 |     100 |                   
  worktree.ts      |   89.39 |    81.78 |     100 |   89.39 | ...1813-1814,1827 
 ...w/lib/platform |   94.71 |    87.89 |   97.05 |   94.71 |                   
  aone-client.ts   |   94.94 |     87.3 |     100 |   94.94 | ...92-293,299-302 
  aone.ts          |   93.06 |    89.86 |   94.73 |   93.06 | ...34,598-603,655 
  github.ts        |   99.08 |     75.8 |     100 |   99.08 | 249-250           
  registry.ts      |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...mands/sessions |   94.11 |    89.06 |   89.47 |   94.11 |                   
  common.ts        |     100 |      100 |     100 |     100 |                   
  list.ts          |   90.96 |    86.66 |   81.81 |   90.96 | 208-219,221-222   
  ps.ts            |     100 |    94.44 |     100 |     100 | 58                
 src/config        |   94.53 |    90.57 |   95.73 |   94.53 |                   
  ...l-fallback.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   93.36 |    88.37 |     100 |   93.36 | ...06-307,330-331 
  ...eMcpImport.ts |   87.91 |    81.52 |     100 |   87.91 | ...63-371,453-454 
  compile-cache.ts |     100 |      100 |     100 |     100 |                   
  config.ts        |   88.92 |    91.37 |   88.63 |   88.92 | ...2485,2487-2495 
  ...cy-monitor.ts |      90 |    77.27 |     100 |      90 | ...72-73,90-92,98 
  ...ust-policy.ts |   83.02 |    88.88 |     100 |   83.02 | ...02-209,232-240 
  ...heme-names.ts |     100 |      100 |     100 |     100 |                   
  ...ScopeUtils.ts |   97.56 |    88.88 |     100 |   97.56 | 67                
  environment.ts   |   94.63 |    92.42 |   95.23 |   94.63 | ...24-625,693-694 
  ...le-watcher.ts |   90.86 |    83.65 |   95.83 |   90.86 | ...23-325,370,418 
  ...resh-state.ts |   95.65 |    97.36 |     100 |   95.65 | 137-142           
  ...ime-reload.ts |     100 |    69.69 |     100 |     100 | ...12-113,122-123 
  hot-reload.ts    |   98.91 |    84.61 |     100 |   98.91 | 332-333           
  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.96 |     100 |   98.97 | 85                
  mcpApprovals.ts  |   78.57 |       92 |   86.66 |   78.57 | ...18-319,324-326 
  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.93 |     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.93 |    93.16 |   91.17 |   91.93 | ...1134,1136-1137 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  settingsUtils.ts |   81.12 |     89.2 |   85.18 |   81.12 | ...03-621,628-636 
  ...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 
  ...el-options.ts |     100 |      100 |     100 |     100 |                   
  ...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 |    78.94 |   85.71 |   95.23 |                   
  index.ts         |   95.65 |     87.5 |     100 |   95.65 | 117-118           
  scheduler.ts     |   96.55 |       80 |     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    |   75.08 |    67.64 |   71.42 |   75.08 |                   
  ...tputBridge.ts |   75.33 |    68.18 |   73.68 |   75.33 | ...09-410,418-421 
  ...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          |   89.68 |    88.66 |   93.02 |   89.68 |                   
  index.ts         |   73.45 |    77.77 |      90 |   73.45 | ...70-271,294-299 
  languageUtils.ts |   98.88 |    97.01 |     100 |   98.88 | 184-185           
  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 |   87.37 |    83.73 |   89.32 |   87.37 |                   
  ...ng-failure.ts |     100 |      100 |     100 |     100 |                   
  ...iveHelpers.ts |   94.95 |    91.05 |     100 |   94.95 | ...30-431,529,542 
  ...uggestions.ts |   84.29 |    70.83 |     100 |   84.29 | 70-76,92-103      
  session.ts       |   84.97 |    76.31 |   96.07 |   84.97 | ...1048,1057-1067 
  ...iagnostics.ts |    95.8 |     87.5 |   93.75 |    95.8 | ...03,277-278,289 
  types.ts         |    42.5 |      100 |   33.33 |    42.5 | ...33-634,637-638 
 ...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 |   57.57 |    66.48 |   73.68 |   57.57 |                   
  ...Controller.ts |    42.4 |      100 |   83.33 |    42.4 | 101-105,140-223   
  ...Controller.ts |       0 |        0 |       0 |       0 | 1-56              
  ...Controller.ts |   70.23 |    63.33 |   91.66 |   70.23 | ...19-628,643-648 
  ...Controller.ts |   49.23 |       60 |      50 |   49.23 | ...07-108,111-121 
  ...Controller.ts |   53.96 |    67.08 |   66.66 |   53.96 | ...78-690,699-728 
 .../control/types |       0 |        0 |       0 |       0 |                   
  serviceAPIs.ts   |       0 |        0 |       0 |       0 | 1                 
 ...Interactive/io |   98.18 |    94.11 |   95.34 |   98.18 |                   
  ...putAdapter.ts |   98.07 |    93.21 |   98.11 |   98.07 | ...1448,1464-1465 
  ...putAdapter.ts |   96.22 |    91.66 |   85.71 |   96.22 | 52-53             
  ...nputReader.ts |     100 |    94.73 |     100 |     100 | 67                
  ...putAdapter.ts |   98.51 |      100 |   90.47 |   98.51 | 90-91,131-132     
  ...projection.ts |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/peerMessaging |   91.89 |    88.29 |   96.42 |   91.89 |                   
  ...ngContext.tsx |     100 |      100 |     100 |     100 |                   
  ...-messaging.ts |   91.78 |    88.17 |   96.29 |   91.78 | ...31-436,507-512 
 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.72 |    95.17 |     100 |   99.72 |                   
  ...livery-ipc.ts |     100 |    91.17 |     100 |     100 | 94,106,134        
  ...l-delivery.ts |     100 |      100 |     100 |     100 |                   
  cpu-percent.ts   |     100 |      100 |     100 |     100 |                   
  ...ion-source.ts |     100 |      100 |     100 |     100 |                   
  ...d-task-run.ts |     100 |       70 |     100 |     100 | 57,71             
  ...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.57 |     100 |     100 | 53,93-94,172,192  
  ...ls-mapping.ts |     100 |    96.15 |     100 |     100 | 26                
 src/serve         |    87.8 |     85.5 |   91.55 |    87.8 |                   
  ...extra-args.ts |     100 |      100 |     100 |     100 |                   
  ...tp-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...ion-bridge.ts |     100 |      100 |     100 |     100 |                   
  auth.ts          |   96.19 |    93.44 |     100 |   96.19 | ...47-448,451-453 
  ...em-adapter.ts |     100 |      100 |     100 |     100 |                   
  capabilities.ts  |     100 |    98.33 |     100 |     100 | 774               
  ...cp-command.ts |     100 |      100 |     100 |     100 |                   
  ...horization.ts |   98.19 |    93.75 |     100 |   98.19 | 135-136           
  ...op-mcp-ipc.ts |   81.06 |    73.68 |   94.11 |   81.06 | ...37-242,267,289 
  ...nt-service.ts |    94.1 |    86.98 |     100 |    94.1 | ...75-477,484,486 
  ...-selection.ts |     100 |      100 |     100 |     100 |                   
  ...ings-store.ts |   89.61 |    94.37 |   96.55 |   89.61 | ...64-276,528-531 
  ...ebhook-ipc.ts |    98.5 |     87.5 |     100 |    98.5 | 47                
  ...iagnostics.ts |     100 |      100 |     100 |     100 |                   
  ...worker-env.ts |     100 |      100 |     100 |     100 |                   
  ...rker-group.ts |   87.32 |    85.33 |     100 |   87.32 | ...14,820-824,842 
  ...er-manager.ts |   89.39 |    83.88 |   93.33 |   89.39 | ...98,711,722-724 
  ...horization.ts |     100 |      100 |     100 |     100 |                   
  ...tartup-ipc.ts |   97.72 |    96.66 |     100 |   97.72 | 88-89             
  ...supervisor.ts |   93.24 |    85.42 |    97.4 |   93.24 | ...1765,1819-1823 
  ...e-grouping.ts |     100 |    94.28 |     100 |     100 | 71,137            
  core-runtime.ts  |     100 |      100 |     100 |     100 |                   
  ...ub-session.ts |   91.01 |    81.25 |   94.73 |   91.01 | ...1120,1141-1146 
  ...tree-guard.ts |   93.87 |    89.81 |     100 |   93.87 | ...3227,3297-3301 
  daemon-logger.ts |   82.82 |    78.68 |   92.04 |   82.82 | ...1775,1802-1808 
  ...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.7 |    91.96 |     100 |    98.7 | ...1593,1595-1596 
  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 
  ...h-settings.ts |   94.94 |    90.45 |     100 |   94.94 | ...30,708,724,734 
  fast-path.ts     |    91.4 |       82 |   95.45 |    91.4 | ...47-556,634-635 
  ...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-149             
  ...e-observer.ts |   89.89 |    83.24 |      96 |   89.89 | ...11-512,541-543 
  ...-addresses.ts |     100 |     91.3 |     100 |     100 | 52,72             
  ...-path-open.ts |   96.12 |       97 |   93.33 |   96.12 | 114-123           
  ...back-binds.ts |     100 |      100 |     100 |     100 |                   
  ...-workspace.ts |   91.58 |    86.48 |     100 |   91.58 | ...44-145,156-157 
  ...pp-sandbox.ts |   96.72 |    95.23 |     100 |   96.72 | 41-42             
  ...iders-edit.ts |     100 |    83.33 |     100 |     100 | 58-60,65,81       
  ...ory-picker.ts |    90.9 |    91.66 |      75 |    90.9 | 32,55-64          
  ...-with-auth.ts |     100 |      100 |     100 |     100 |                   
  ...ate-blocks.ts |   99.03 |    94.73 |     100 |   99.03 | 133               
  ...sion-audit.ts |     100 |      100 |   93.33 |     100 |                   
  ...nal-ledger.ts |    94.9 |    84.94 |     100 |    94.9 | ...81,302,361-362 
  rate-limit.ts    |   92.68 |    88.29 |     100 |   92.68 | ...89-291,303-305 
  ...qwen-serve.ts |   85.12 |    82.05 |   78.86 |   85.12 | ...9633,9651-9655 
  ...tup-errors.ts |     100 |      100 |     100 |     100 |                   
  sandbox.ts       |   47.11 |    63.01 |   76.92 |   47.11 | ...1061,1073-1096 
  ...-keepalive.ts |   94.31 |    89.28 |     100 |   94.31 | ...37,541-542,581 
  ...-lifecycle.ts |     100 |      100 |     100 |     100 |                   
  ...-lifecycle.ts |   89.16 |    90.29 |   86.95 |   89.16 | ...24-325,330-334 
  serve-token.ts   |     100 |      100 |     100 |     100 |                   
  server.ts        |   89.76 |    91.53 |   74.45 |   89.76 | ...3387,3418-3419 
  ...ments-root.ts |     100 |      100 |     100 |     100 |                   
  ...-admission.ts |   99.13 |    95.94 |     100 |   99.13 | 308-309           
  ...on-helpers.ts |     100 |      100 |     100 |     100 |                   
  ...-redaction.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.72 |    77.93 |     100 |   93.72 | ...51,854,867-869 
  ...l-resolver.ts |   90.32 |    66.66 |     100 |   90.32 | 16,45-46          
  ...ell-static.ts |   93.33 |    86.15 |     100 |   93.33 | ...90-293,336-339 
  ...ssion-gate.ts |   98.48 |    94.44 |     100 |   98.48 | 70                
  ...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.65 |    80.18 |     100 |   98.65 | 111,139,193,196   
  ...tion-store.ts |    89.9 |    88.88 |   92.59 |    89.9 | ...03-412,423-426 
  ...e-registry.ts |   94.09 |    90.57 |     100 |   94.09 | ...93-594,601-602 
  ...e-remember.ts |   98.31 |    93.33 |     100 |   98.31 | ...47,351-356,397 
  ...te-runtime.ts |   89.85 |    90.69 |     100 |   89.85 | ...06-207,275-296 
  ...oordinator.ts |   91.15 |    86.86 |     100 |   91.15 | ...95,424,455-456 
  ...me-storage.ts |     100 |      100 |     100 |     100 |                   
  ...visibility.ts |     100 |      100 |     100 |     100 |                   
  ...management.ts |   72.91 |    73.04 |   96.29 |   72.91 | ...98-899,906-910 
  ...lls-status.ts |     100 |    95.45 |     100 |     100 | 152               
  ...reconciler.ts |   91.63 |    84.09 |     100 |   91.63 | ...71-273,306-307 
 ...serve/acp-http |   80.67 |    80.23 |   94.53 |   80.67 |                   
  ...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 |   93.03 |    84.18 |   98.52 |   93.03 | ...1624,1671-1682 
  dispatch.ts      |      76 |    76.98 |   93.44 |      76 | ...5819,5876-5882 
  index.ts         |   83.61 |     80.6 |   91.22 |   83.61 | ...2465,2551-2552 
  json-rpc.ts      |     100 |    96.96 |     100 |     100 | 92                
  ...ach-budget.ts |     100 |      100 |     100 |     100 |                   
  safe-ws-send.ts  |   52.94 |    71.42 |     100 |   52.94 | 33-42,47-55       
  sse-stream.ts    |   98.26 |    88.75 |     100 |   98.26 | 87-88,117         
  ...ort-stream.ts |       0 |        0 |       0 |       0 | 1                 
  ws-stream.ts     |   94.06 |    89.09 |     100 |   94.06 | 50,55,134,138-141 
 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             
 .../conversations |   86.63 |     79.1 |   92.96 |   86.63 |                   
  ...e-activity.ts |     100 |      100 |     100 |     100 |                   
  ...ime-errors.ts |     100 |      100 |     100 |     100 |                   
  ...me-manager.ts |   97.88 |    94.91 |     100 |   97.88 | 64-65,92          
  ...-ownership.ts |   87.33 |    83.75 |   88.46 |   87.33 | ...57-558,601-602 
  ...-workspace.ts |   88.17 |    76.15 |     100 |   88.17 | ...52-554,568-572 
  ...on-journal.ts |   91.65 |    80.76 |     100 |   91.65 | ...44-745,751-753 
  ...on-service.ts |   84.02 |    75.91 |   88.54 |   84.02 | ...3082,3091-3093 
 src/serve/fs      |   87.77 |    82.37 |     100 |   87.77 |                   
  audit.ts         |     100 |    96.29 |     100 |     100 | 211               
  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.52 |    89.18 |     100 |   90.52 | 172-180           
  text-cursor.ts   |   88.23 |       90 |     100 |   88.23 | 74-77,92-95       
  ...ile-system.ts |   88.02 |    81.88 |     100 |   88.02 | ...3027,3037-3038 
 src/serve/live    |   76.57 |    70.53 |    90.2 |   76.57 |                   
  discovery.ts     |   85.89 |    82.05 |    91.3 |   85.89 | ...73-579,592-593 
  ...oordinator.ts |   82.67 |    76.63 |   97.01 |   82.67 | ...1319,1351-1353 
  ...-installer.ts |   63.83 |    82.35 |   80.76 |   63.83 | ...45-446,460-475 
  ...oordinator.ts |    76.7 |    67.47 |   85.71 |    76.7 | ...1885,1976-1977 
  ...controller.ts |   67.82 |    79.66 |      75 |   67.82 | ...66-278,287-295 
  ...sk-service.ts |   82.71 |    66.15 |   93.61 |   82.71 | ...1270,1283,1290 
  ...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.85 |    77.39 |     100 |   94.85 | ...18,327-330,350 
  types.ts         |     100 |      100 |     100 |     100 |                   
 .../local-control |   82.89 |    90.09 |      90 |   82.89 |                   
  credentials.ts   |   96.42 |    95.45 |     100 |   96.42 | 109-110           
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...interfaces.ts |   43.58 |    82.75 |   42.85 |   43.58 | ...09-117,130-142 
  ...r-identity.ts |     100 |      100 |     100 |     100 |                   
  service.ts       |    93.4 |       90 |     100 |    93.4 | ...20-222,313-315 
 src/serve/routes  |   86.14 |    81.82 |   95.74 |   86.14 |                   
  a2ui-action.ts   |   96.84 |     88.5 |    87.5 |   96.84 | ...70-272,309-311 
  capabilities.ts  |   98.96 |    95.12 |     100 |   98.96 | 102               
  ...nel-notify.ts |   79.16 |    85.18 |     100 |   79.16 | ...03-104,120-126 
  ...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.94 |    91.17 |     100 |   98.94 | 143               
  health.ts        |   99.09 |    91.42 |     100 |   99.09 | 147               
  live-setup.ts    |   33.33 |     37.5 |      50 |   33.33 | ...18-123,130-135 
  live.ts          |   84.61 |    76.47 |     100 |   84.61 | ...04,106-111,131 
  permission.ts    |   96.03 |    87.87 |     100 |   96.03 | 81-84             
  ...uled-tasks.ts |   87.52 |    83.61 |   95.12 |   87.52 | ...2016,2061-2062 
  ...r-backfill.ts |   98.49 |    93.56 |     100 |   98.49 | ...99,601,821-822 
  ...on-runtime.ts |   91.42 |       90 |     100 |   91.42 | 56-64             
  session.ts       |   86.76 |     83.3 |   94.61 |   86.76 | ...7436,7438-7439 
  sse-events.ts    |   87.15 |    85.09 |   94.44 |   87.15 | ...48-959,962,969 
  ...e-sessions.ts |   87.13 |    80.79 |     100 |   87.13 | ...90-492,495-500 
  terminal.ts      |   92.81 |    90.35 |     100 |   92.81 | ...10-313,332-335 
  usage-stats.ts   |     100 |    95.45 |     100 |     100 | 118               
  user-language.ts |   99.24 |    87.87 |     100 |   99.24 | 167               
  ...space-auth.ts |   84.74 |    75.29 |     100 |   84.74 | ...35,349,357-361 
  ...el-control.ts |   86.26 |    78.94 |     100 |   86.26 | ...17-318,339-347 
  ...management.ts |   90.35 |    78.94 |     100 |   90.35 | ...52-553,576-577 
  ...d-contacts.ts |   83.62 |    94.59 |     100 |   83.62 | 123,125-142       
  ...controller.ts |   83.27 |    80.75 |      90 |   83.27 | ...1071,1076,1083 
  ...extensions.ts |   89.92 |    79.77 |   94.36 |   89.92 | ...2588,2633-2634 
  ...-file-read.ts |      91 |    80.91 |     100 |      91 | ...20-621,624-625 
  ...file-write.ts |   89.72 |    79.35 |     100 |   89.72 | ...05,719-726,807 
  ...t-branches.ts |   77.16 |    72.02 |     100 |   77.16 | ...42-647,656-663 
  ...e-git-diff.ts |   97.19 |    89.58 |     100 |   97.19 | 157-158,185-187   
  ...ce-git-log.ts |     100 |       95 |     100 |     100 | 48,73             
  workspace-git.ts |   74.71 |     87.5 |     100 |   74.71 | 83-104            
  ...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 
  ...al-control.ts |   73.61 |       70 |     100 |   73.61 | ...28,230-236,241 
  ...local-open.ts |     100 |    93.75 |     100 |     100 | 54                
  ...management.ts |   87.22 |    84.17 |     100 |   87.22 | ...1823,1833-1838 
  ...mcp-config.ts |   71.02 |    73.41 |   88.88 |   71.02 | ...01,410-413,442 
  ...cp-control.ts |    73.2 |    67.54 |   85.71 |    73.2 | ...27-633,644-645 
  ...ace-models.ts |   89.84 |    87.35 |     100 |   89.84 | ...27-332,336-338 
  ...ermissions.ts |    77.9 |    72.41 |     100 |    77.9 | ...69-277,298-316 
  ...untime-mcp.ts |   59.52 |    54.54 |   83.33 |   59.52 | ...50-251,265-273 
  ...ce-runtime.ts |     100 |    96.55 |     100 |     100 | 117               
  ...e-settings.ts |      79 |    78.72 |     100 |      79 | ...92-893,919-922 
  ...tup-github.ts |   77.97 |    70.58 |   84.21 |   77.97 | ...46-352,397-398 
  ...ace-skills.ts |   76.41 |    86.11 |     100 |   76.41 | ...29-354,360-394 
  ...ace-status.ts |   82.57 |    74.48 |     100 |   82.57 | ...71-473,477-478 
  ...pace-tools.ts |   75.94 |    69.69 |   66.66 |   75.94 | ...59-164,193-194 
  ...pace-trust.ts |   76.92 |     67.1 |      80 |   76.92 | ...38-343,351-352 
  ...pace-voice.ts |   91.33 |    81.02 |     100 |   91.33 | ...70-673,676-678 
 src/serve/server  |   93.74 |    91.64 |   96.24 |   93.74 |                   
  access-log.ts    |   98.73 |    97.26 |     100 |   98.73 | 119,196           
  ...-timestamp.ts |     100 |      100 |     100 |     100 |                   
  aone-mrs.ts      |   91.48 |    91.35 |   81.25 |   91.48 | ...53,299-300,466 
  ...er-helpers.ts |   63.82 |    78.15 |   81.81 |   63.82 | ...16,330,332-347 
  ...w-registry.ts |    98.8 |    81.81 |     100 |    98.8 | 107               
  ...r-handlers.ts |   97.87 |       80 |     100 |   97.87 | 27                
  ...r-response.ts |   89.76 |    82.82 |     100 |   89.76 | ...1015,1042-1051 
  fs-factory.ts    |     100 |    95.52 |     100 |     100 | 77,144,200        
  ...branch-ops.ts |     100 |      100 |     100 |     100 |                   
  ...list-cache.ts |   99.01 |    95.52 |     100 |   99.01 | 184-185           
  ...t-deadline.ts |     100 |      100 |     100 |     100 |                   
  ...iter-setup.ts |      65 |       80 |   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   |     100 |      100 |     100 |     100 |                   
  ...e-features.ts |   95.39 |     87.5 |     100 |   95.39 | 200-206           
  ...on-archive.ts |   92.46 |    90.49 |   97.61 |   92.46 | ...1150,1191-1192 
  ...ion-export.ts |   98.57 |    90.47 |     100 |   98.57 | 85                
  session-list.ts  |    97.4 |    93.75 |     100 |    97.4 | ...1192,1401-1405 
  ...pr-refresh.ts |   99.37 |    97.01 |     100 |   99.37 | 69-70             
  ...ry-context.ts |    87.5 |       50 |     100 |    87.5 | 49-50             
  telemetry.ts     |   99.18 |    97.65 |     100 |   99.18 | ...95,882,961-963 
 src/serve/voice   |    92.7 |    91.53 |   97.72 |    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.24 |     100 |     100 | 176               
 ...kspace-service |      90 |    87.33 |   91.66 |      90 |                   
  index.ts         |   89.65 |    86.98 |   90.47 |   89.65 | ...1393,1407,1421 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/services      |   92.72 |    89.81 |   98.13 |   92.72 |                   
  ...mandLoader.ts |     100 |       95 |     100 |     100 | 107               
  ...killLoader.ts |   97.19 |    86.48 |     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.65 |   85.71 |   79.55 | ...48,178,245-246 
  ...mandLoader.ts |   97.95 |    93.44 |     100 |   97.95 | 186,193-194       
  ...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.77 |     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 |   92.14 |    92.42 |     100 |   92.14 | ...91-296,329-330 
  ...low-loader.ts |     100 |    96.29 |     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.89 |     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 | ...96-898,901-903 
 ...s/housekeeping |   93.03 |    88.57 |      95 |   93.03 |                   
  scheduler.ts     |   93.03 |    88.57 |      95 |   93.03 | ...70-372,424-428 
 ...rvices/insight |     100 |      100 |     100 |     100 |                   
  dates.ts         |     100 |      100 |     100 |     100 |                   
 ...ght/generators |   88.94 |    86.86 |   96.29 |   88.94 |                   
  DataProcessor.ts |   88.31 |    86.84 |      95 |   88.31 | ...1368,1372-1379 
  ...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.25 |     100 |   97.27 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...eProcessor.ts |   94.52 |       85 |     100 |   94.52 | 46-47,93-94       
  ...tionParser.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.41 |    95.83 |     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.6 |    76.66 |      80 |    94.6 |                   
  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 |                   
  ...lot-client.ts |     100 |    66.66 |     100 |     100 | 31,39             
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |     100 |      100 |     100 |     100 |                   
 src/ui            |   71.68 |    78.68 |   72.18 |   71.68 |                   
  App.tsx          |   33.33 |       75 |   33.33 |   33.33 | 32-86             
  AppContainer.tsx |    77.5 |    74.24 |   76.31 |    77.5 | ...4520,4636-4642 
  ...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        |   63.63 |      100 |   41.17 |   63.63 | ...52,54-55,60-61 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...AutoUpdate.ts |   93.54 |    94.64 |      90 |   93.54 | 126,131,202-213   
  keyMatchers.ts   |   95.91 |    97.14 |     100 |   95.91 | 25-26             
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  ...one-update.ts |   39.81 |    77.44 |   62.16 |   39.81 | ...1193,1196-1215 
  ...ractiveUI.tsx |   68.53 |    78.26 |      50 |   68.53 | ...65-467,497-502 
  ...inePresets.ts |   96.27 |    83.87 |     100 |   96.27 | ...97,402,410-412 
  systemInfo.ts    |   95.09 |    90.27 |     100 |   95.09 | ...54-255,260-264 
  ...InfoFields.ts |   89.28 |    69.04 |     100 |   89.28 | ...09-110,124-125 
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...e-relaunch.ts |   89.61 |    86.66 |      50 |   89.61 | 56-61,83-84       
 src/ui/auth       |   73.75 |    70.24 |   61.22 |   73.75 |                   
  AuthDialog.tsx   |   59.01 |     42.1 |   16.66 |   59.01 | ...25,332-354,358 
  ...nProgress.tsx |       0 |        0 |       0 |       0 | 1-64              
  ...etupSteps.tsx |   74.93 |    78.62 |   71.42 |   74.93 | ...92-902,918,921 
  useAuth.ts       |   94.83 |    75.67 |     100 |   94.83 | ...33-234,253-259 
  ...rSetupFlow.ts |   79.13 |     57.4 |     100 |   79.13 | ...01,424,431-437 
 src/ui/commands   |   84.71 |    84.54 |   91.66 |   84.71 |                   
  aboutCommand.ts  |     100 |      100 |     100 |     100 |                   
  ...or-command.ts |     100 |    95.65 |     100 |     100 | 104,182           
  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 | 28,62             
  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 
  commands.ts      |   97.45 |    96.72 |     100 |   97.45 | 153-155           
  ...essCommand.ts |   86.91 |    66.66 |     100 |   86.91 | ...22-223,237-240 
  ...astCommand.ts |   84.75 |    76.47 |     100 |   84.75 | ...96-102,130-135 
  ...ig-command.ts |   93.12 |    88.42 |     100 |   93.12 | ...07-315,321-323 
  ...extCommand.ts |   73.96 |    74.68 |   83.33 |   73.96 | ...76-609,620-621 
  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 |   90.56 |    87.83 |    90.9 |   90.56 | ...75-280,327-334 
  docsCommand.ts   |     100 |     90.9 |     100 |     100 | 26                
  doctorChecks.ts  |   70.31 |    74.57 |     100 |   70.31 | ...95-301,325-341 
  doctorCommand.ts |   70.16 |    84.61 |      95 |   70.16 | ...29-679,682-816 
  dreamCommand.ts  |   85.45 |    88.88 |     100 |   85.45 | 58-65             
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  ...rt-command.ts |   80.95 |       80 |     100 |   80.95 | 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 | 95,146            
  goalCommand.ts   |     100 |    96.49 |     100 |     100 | 139,192           
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  ...oryCommand.ts |     100 |      100 |     100 |     100 |                   
  hooksCommand.ts  |   81.25 |    65.71 |   85.71 |   81.25 | ...,86-93,131-132 
  ideCommand.ts    |   60.75 |    64.28 |   41.17 |   60.75 | ...05-306,310-324 
  ...figCommand.ts |    58.5 |    74.07 |      80 |    58.5 | ...21-331,334-343 
  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.63 |    90.66 |     100 |   94.63 | ...25-226,253-263 
  learn-command.ts |     100 |      100 |     100 |     100 |                   
  lspCommand.ts    |     100 |    86.95 |     100 |     100 | 31,102-103        
  mcpCommand.ts    |     100 |      100 |     100 |     100 |                   
  memoryCommand.ts |     100 |      100 |     100 |     100 |                   
  modelCommand.ts  |   86.28 |    86.29 |     100 |   86.28 | ...1112,1146-1151 
  peers-command.ts |     100 |    94.36 |     100 |     100 | 59,70,223,228     
  ...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.6 |       90 |     100 |    89.6 | ...72-176,212-219 
  ...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.31 |    81.81 |     100 |   78.31 | 37-52,73,92       
  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.33 |    72.13 |     100 |   77.33 | ...46-150,173-178 
  ...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    |     100 |      100 |     100 |     100 |                   
  voice-command.ts |   93.63 |       88 |     100 |   93.63 | 36,98-103         
  ...owsCommand.ts |   94.38 |    85.29 |     100 |   94.38 | ...78-183,282-287 
 src/ui/components |   74.09 |    80.37 |   78.96 |   74.09 |                   
  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 |    89.2 |    91.13 |     100 |    89.2 | ...92-294,308-310 
  ...ontroller.tsx |     100 |      100 |     100 |     100 |                   
  Composer.tsx     |   94.54 |    66.66 |     100 |   94.54 | ...-76,88,143,158 
  ...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 |   11.28 |      100 |       0 |   11.28 | 71-598            
  DiffDialog.tsx   |    53.5 |     37.5 |   69.23 |    53.5 | ...32-737,747-760 
  ...ngsDialog.tsx |    8.44 |      100 |       0 |    8.44 | 37-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          
  ...gsDisplay.tsx |     100 |    96.87 |   83.33 |     100 | 69                
  ...ustDialog.tsx |     100 |      100 |     100 |     100 |                   
  Footer.tsx       |   81.27 |    69.23 |      50 |   81.27 | ...06,245,267-272 
  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.69 |    67.61 |     100 |   79.69 | ...17,520,523-529 
  ...ngeDialog.tsx |     100 |      100 |     100 |     100 |                   
  InputPrompt.tsx  |   86.36 |    83.41 |      80 |   86.36 | ...2242,2263,2366 
  ...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  |   95.88 |    96.03 |   46.15 |   95.88 | ...20,523-527,530 
  MemoryDialog.tsx |   86.59 |    80.15 |     100 |   86.59 | ...34-435,485,553 
  ModelDialog.tsx  |   85.22 |    74.17 |     100 |   85.22 | ...1042,1098,1100 
  ...tsDisplay.tsx |     100 |    97.22 |     100 |     100 | 270               
  ...fications.tsx |   16.66 |      100 |       0 |   16.66 | 14-56             
  ...onsDialog.tsx |    2.13 |      100 |       0 |    2.13 | 62-133,148-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 |   91.34 |       70 |     100 |   91.34 | 48-51,63-66,78    
  ...geDisplay.tsx |     100 |      100 |     100 |     100 |                   
  ...ngDisplay.tsx |   21.42 |      100 |       0 |   21.42 | 13-39             
  ...hProgress.tsx |   85.25 |    88.46 |     100 |   85.25 | 121-147           
  ...ngSpinner.tsx |   67.85 |    85.71 |      50 |   67.85 | 33-50,71,78-79    
  ...dSelector.tsx |   92.79 |    82.65 |     100 |   92.79 | ...19-323,354-370 
  ...ionPicker.tsx |   84.02 |    74.19 |     100 |   84.02 | ...04,410,452-474 
  ...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.55 |    73.89 |   69.23 |   71.55 | ...1252,1258-1259 
  ...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 |      28 |      100 |       0 |      28 | 18-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-171             
  ...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.9 |    86.88 |     100 |    93.9 | ...20,282,302-304 
  ...yTodoList.tsx |   96.36 |    88.23 |     100 |   96.36 | 138-141           
  ...nsDisplay.tsx |   96.01 |    88.05 |     100 |   96.01 | ...29-130,295-297 
  ...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 |    7.84 |      100 |       0 |    7.84 | 24-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 |    61.5 |    75.57 |    62.5 |    61.5 |                   
  ...atContent.tsx |    9.09 |      100 |       0 |    9.09 | 54-275,281-283    
  ...tChatView.tsx |     100 |    81.81 |     100 |     100 | 82                
  ...tComposer.tsx |   78.35 |     64.7 |   66.66 |   78.35 | ...64,277,303-305 
  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 |   45.51 |    70.53 |   60.86 |   45.51 |                   
  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 |    9.77 |      100 |       0 |    9.77 | 27-166            
  ...tusDialog.tsx |    5.63 |      100 |       0 |    5.63 | 33-75,80-288      
  ...topDialog.tsx |    6.17 |      100 |       0 |    6.17 | 33-213            
 ...ackground-view |   85.86 |     85.1 |   92.98 |   85.86 |                   
  ...sksDialog.tsx |   82.66 |    83.09 |   85.71 |   82.66 | ...1854,1977-1983 
  ...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.73 |    87.78 |   86.53 |   90.73 |                   
  ...orMessage.tsx |     100 |      100 |     100 |     100 |                   
  ...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 |     100 |      100 |     100 |     100 |                   
  ...nMessages.tsx |   92.35 |    96.07 |   76.92 |   92.35 | ...59-361,364-367 
  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.51 |     100 |   91.87 | ...49-651,658-660 
  ...upMessage.tsx |   98.38 |    95.38 |     100 |   98.38 | 188-191,422       
  ToolMessage.tsx  |   95.17 |    89.55 |     100 |   95.17 | ...1075,1120-1122 
 ...ponents/shared |   86.36 |    82.21 |    86.6 |   86.36 |                   
  ...ctionList.tsx |     100 |      100 |      75 |     100 |                   
  ...tonSelect.tsx |     100 |      100 |     100 |     100 |                   
  ...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 |      100 |     100 |     100 |                   
  ...eSelector.tsx |     100 |       60 |     100 |     100 | 40-45             
  ...lableList.tsx |   90.37 |    82.85 |   18.18 |   90.37 | ...60-63,65,73-76 
  StaticRender.tsx |     100 |      100 |     100 |     100 |                   
  TextInput.tsx    |   81.31 |    68.85 |      80 |   81.31 | ...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.78 |   97.91 |   85.98 | ...2664,2762-2763 
  ...er-actions.ts |   73.93 |    67.22 |     100 |   73.93 | ...32-733,934-936 
 ...ponents/skills |    4.07 |      100 |       0 |    4.07 |                   
  ...gerDialog.tsx |    4.07 |      100 |       0 |    4.07 | 78-136,139-667    
 ...ents/subagents |   30.87 |        0 |       0 |   30.87 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-11              
  reducers.tsx     |    12.1 |      100 |       0 |    12.1 | 33-190            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   10.95 |      100 |       0 |   10.95 | ...1,56-57,60-102 
 ...bagents/create |   18.06 |     62.5 |    8.33 |   18.06 |                   
  ...ionWizard.tsx |    7.28 |      100 |       0 |    7.28 | 34-299            
  ...rSelector.tsx |   14.75 |      100 |       0 |   14.75 | 26-85             
  ...onSummary.tsx |    4.26 |      100 |       0 |    4.26 | 27-331            
  ...tionInput.tsx |   72.41 |     62.5 |     100 |   72.41 | ...32-139,163-170 
  ...dSelector.tsx |   33.33 |      100 |       0 |   33.33 | 20-21,26-27,36-63 
  ...nSelector.tsx |    37.5 |      100 |       0 |    37.5 | 20-21,26-27,36-58 
  ...EntryStep.tsx |   12.76 |      100 |       0 |   12.76 | 34-78             
  ToolSelector.tsx |    4.16 |      100 |       0 |    4.16 | 31-253            
 ...bagents/manage |    21.6 |    59.52 |   27.27 |    21.6 |                   
  ...ctionStep.tsx |   10.25 |      100 |       0 |   10.25 | 21-103            
  ...eleteStep.tsx |   20.93 |      100 |       0 |   20.93 | 23-62             
  ...tEditStep.tsx |   25.53 |      100 |       0 |   25.53 | ...2,37-38,51-124 
  ...ctionStep.tsx |   35.61 |    59.52 |     100 |   35.61 | ...21-433,438-440 
  ...iewerStep.tsx |   13.72 |      100 |       0 |   13.72 | 18-73             
  ...gerDialog.tsx |    6.74 |      100 |       0 |    6.74 | 35-341            
 ...mponents/views |   69.22 |    71.81 |   61.11 |   69.22 |                   
  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    |      75 |    81.81 |     100 |      75 | 39-42,59-67       
 src/ui/contexts   |   86.47 |    82.27 |   86.48 |   86.47 |                   
  ...ewContext.tsx |   91.66 |       90 |      75 |   91.66 | ...89-193,279-289 
  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 |    79.56 |    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 | 237-238           
  ...deContext.tsx |      80 |     87.5 |      75 |      80 | ...11-112,118-120 
  ...rtContext.tsx |     100 |      100 |     100 |     100 |                   
 src/ui/daemon     |   89.51 |    76.92 |   95.65 |   89.51 |                   
  ...ui-adapter.ts |   89.51 |    76.92 |   95.65 |   89.51 | ...59,877-878,964 
 src/ui/editors    |   93.33 |    85.71 |   66.66 |   93.33 |                   
  ...ngsManager.ts |   93.33 |    85.71 |   66.66 |   93.33 | 49,63-64          
 src/ui/hooks      |   86.52 |    84.43 |   88.88 |   86.52 |                   
  ...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.51 |    73.58 |     100 |   94.51 | ...97-298,303-304 
  ...dProcessor.ts |   86.83 |    71.86 |   83.33 |   86.83 | ...1536,1565-1569 
  ...rt-command.ts |     100 |      100 |     100 |     100 |                   
  ...sced-flush.ts |     100 |      100 |     100 |     100 |                   
  ...llm-stream.ts |   88.87 |    85.13 |   85.18 |   88.87 | ...6265,6267,6372 
  ...ng-enabled.ts |     100 |      100 |     100 |     100 |                   
  ...oice-input.ts |   92.41 |    82.08 |   66.66 |   92.41 | ...12,514-515,670 
  ...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 |   96.03 |    88.75 |     100 |   96.03 | ...04-205,362-365 
  ...ompletion.tsx |    97.1 |    87.23 |     100 |    97.1 | ...26-327,337-338 
  ...dMigration.ts |    92.1 |    88.88 |     100 |    92.1 | 42-44             
  useCompletion.ts |   96.64 |    91.37 |     100 |   96.64 | ...37-238,242-243 
  ...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   |   11.62 |      100 |       0 |   11.62 | 44-87             
  ...oublePress.ts |   53.12 |       75 |     100 |   53.12 | 33-35,41-54       
  ...orSettings.ts |     100 |      100 |     100 |     100 |                   
  ...Completion.ts |   99.12 |    97.64 |     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       
  ...BranchName.ts |     100 |    94.44 |     100 |     100 | 54                
  ...oryManager.ts |   98.44 |     98.9 |     100 |   98.44 | 157-160           
  ...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    
  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 |    95.19 |     100 |     100 | ...53,289,360,375 
  ...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 |   89.16 |     82.6 |     100 |   89.16 | ...77,329-339,419 
  useQwenAuth.ts   |     100 |      100 |     100 |     100 |                   
  ...lScheduler.ts |   89.13 |     86.9 |     100 |   89.13 | ...61-463,496-506 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  ...umeCommand.ts |   96.51 |    90.19 |     100 |   96.51 | 279,306-311       
  ...ompletion.tsx |   90.67 |    83.33 |     100 |   90.67 | ...02,105,138-141 
  ...ectionList.ts |   97.12 |    96.19 |     100 |   97.12 | ...92-193,247-250 
  ...sionPicker.ts |   92.87 |    90.26 |     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.79 |    85.33 |   94.73 |   82.79 | ...86-688,696-732 
  ...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 |    79.2 |    35.29 |     100 |    79.2 | ...15-116,120-121 
  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 |    72.72 |     100 |   93.75 | 47-48,72          
  vim.ts           |      74 |    67.56 |   69.23 |      74 | ...1854-1861,1869 
 src/ui/layouts    |   91.25 |    89.47 |     100 |   91.25 |                   
  ...AppLayout.tsx |   90.99 |     87.5 |     100 |   90.99 | 61-63,111-116,152 
  ...AppLayout.tsx |   91.66 |    92.85 |     100 |   91.66 | 75-80             
 src/ui/model      |   97.91 |    98.36 |     100 |   97.91 |                   
  ...ggregation.ts |     100 |      100 |     100 |     100 |                   
  ...ming-model.ts |   97.43 |    97.72 |     100 |   97.43 | 261-265           
 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/opentui    |   64.46 |     80.8 |   76.82 |   64.46 |                   
  ...plain-text.ts |     100 |    89.47 |     100 |     100 | 73,134            
  ...een-reader.ts |     100 |    88.57 |     100 |     100 | 79-83,198,211     
  ...t-tool-run.ts |   97.26 |     62.5 |   66.66 |   97.26 | 116,130           
  clipboard.ts     |     100 |    88.88 |     100 |     100 | 47                
  ...ds-context.ts |   96.66 |      100 |   38.88 |   96.66 | 159,161           
  ...s-dispatch.ts |   80.59 |    84.02 |   73.07 |   80.59 | ...6-970,993-1004 
  ...nds-output.ts |     100 |      100 |     100 |     100 |                   
  ...s-registry.ts |    98.8 |    89.36 |     100 |    98.8 | 177-179           
  dialog-data.ts   |   82.48 |    72.08 |   89.65 |   82.48 | ...1291,1295-1325 
  ...ogs-arena.tsx |       0 |      100 |     100 |       0 | 3-818             
  dialogs-auth.tsx |   76.17 |    66.85 |      84 |   76.17 | ...41,957,966-982 
  dialogs-core.ts  |     100 |    94.44 |     100 |     100 | 179,190           
  ...xtensions.tsx |   89.06 |     82.7 |   78.57 |   89.06 | ...33-636,659-661 
  dialogs-mcp.tsx  |   29.85 |    98.71 |      90 |   29.85 | 295,320-872       
  ...ry-status.tsx |   22.58 |       80 |   28.57 |   22.58 | ...11-154,159-175 
  dialogs-misc.tsx |   12.02 |      100 |      20 |   12.02 | ...47-655,658-712 
  ...ogs-model.tsx |   45.95 |    96.77 |   76.92 |   45.95 | ...72-173,243-408 
  ...ogs-modes.tsx |       0 |      100 |     100 |       0 | 3-222             
  ...rmissions.tsx |   18.65 |    89.28 |   83.33 |   18.65 | ...58-159,201-734 
  ...-settings.tsx |   20.25 |    84.61 |    90.9 |   20.25 | ...71,239,254-870 
  ...gs-shared.tsx |   82.86 |    80.28 |   46.15 |   82.86 | ...51,453-456,472 
  ...ts-skills.tsx |     5.8 |      100 |       0 |     5.8 | ...04-381,391-458 
  ...ogs-theme.tsx |    30.8 |    88.88 |      75 |    30.8 | 148-326           
  diff-render.ts   |   97.87 |    95.23 |     100 |   97.87 | 89-90             
  early-input.ts   |   94.23 |    73.68 |   71.42 |   94.23 | 85,88-89          
  event-adapter.ts |      91 |    74.54 |   88.88 |      91 | ...36,721,740-748 
  exit-guard.ts    |     100 |      100 |     100 |     100 |                   
  ...-lifecycle.ts |   95.45 |     87.5 |     100 |   95.45 | 56                
  ...rust-gate.tsx |   98.55 |    96.55 |      75 |   98.55 | 190-191           
  help-content.ts  |   98.11 |    85.41 |     100 |   98.11 | 226-227,316,318   
  help-overlay.tsx |       0 |      100 |     100 |       0 | 3-281             
  input-history.ts |     100 |    84.21 |     100 |     100 | 43-45,58          
  ...prompt-key.ts |     100 |      100 |     100 |     100 |                   
  ...ompt-model.ts |   85.41 |    87.81 |   84.09 |   85.41 | ...1127,1130-1140 
  input-prompt.tsx |   81.95 |    68.01 |      28 |   81.95 | ...1071,1085-1087 
  ...projection.ts |   81.16 |    62.66 |   86.66 |   81.16 | ...1077-1082,1084 
  key-map.ts       |     100 |      100 |     100 |     100 |                   
  ...egotiation.ts |   94.82 |    73.68 |     100 |   94.82 | 142-144           
  link-click.ts    |     100 |    82.97 |     100 |     100 | ...49,152,185-189 
  ...sion-model.ts |   83.77 |       85 |   85.71 |   83.77 | ...40-550,623,679 
  live-session.ts  |   87.37 |    83.17 |   73.68 |   87.37 | ...60,462,479-489 
  markdown-heal.ts |     100 |      100 |     100 |     100 |                   
  ...rogressive.ts |   85.41 |    83.33 |   71.42 |   85.41 | 53,60-62,89-91    
  messages.tsx     |   58.82 |     79.1 |   73.68 |   58.82 | ...93-409,417-474 
  mouse-caret.ts   |     100 |      100 |     100 |     100 |                   
  mouse-hit.ts     |     100 |      100 |     100 |     100 |                   
  mouse-rows.ts    |     100 |      100 |     100 |     100 |                   
  ...-scrollbar.ts |     100 |      100 |     100 |     100 |                   
  ...app-shell.tsx |   94.94 |    90.19 |   63.63 |   94.94 | ...45-246,251-253 
  ...log-mount.tsx |   71.92 |    90.62 |   28.57 |   71.92 | ...53,473,548-564 
  ...-boundary.tsx |   95.91 |      100 |   85.71 |   95.91 | 82-83             
  opentui-host.ts  |   97.53 |    94.73 |   97.43 |   97.53 | ...04,216,236-237 
  ...ui-runtime.ts |   93.57 |    80.95 |     100 |   93.57 | 97-101,152,173    
  osc8-parity.ts   |     100 |      100 |     100 |     100 |                   
  ...me-session.ts |   72.34 |    77.77 |   57.14 |   72.34 | 40,44,59-63,76-81 
  ...ompaction.tsx |   76.19 |      100 |   66.66 |   76.19 | 106-130           
  ...wind-model.ts |    94.7 |    87.23 |     100 |    94.7 | 245-252,254       
  ...on-rewind.tsx |       0 |      100 |     100 |       0 | 3-391             
  ...ion-switch.ts |   74.74 |       50 |     100 |   74.74 | ...92-401,411-414 
  ...h-dispatch.ts |   56.86 |    38.88 |      50 |   56.86 | ...13-126,130-137 
  slash-gateway.ts |   96.82 |       90 |   81.81 |   96.82 | 70-71             
  sticky-todos.ts  |     100 |      100 |     100 |     100 |                   
  text-batcher.ts  |     100 |      100 |     100 |     100 |                   
  theme-auto.ts    |     100 |      100 |     100 |     100 |                   
  theme-parity.ts  |   98.68 |    82.35 |     100 |   98.68 | 87                
  theme.ts         |    97.7 |    96.55 |     100 |    97.7 | 202-204           
  ...pt-adapter.ts |   89.56 |       75 |   33.33 |   89.56 | ...50-152,172-173 
 src/ui/selection  |   93.56 |    86.19 |     100 |   93.56 |                   
  screen-buffer.ts |   94.73 |    66.66 |     100 |   94.73 | 51-52             
  ...ion-coords.ts |     100 |      100 |     100 |     100 |                   
  ...ction-span.ts |   93.81 |     92.1 |     100 |   93.81 | ...1,45-46,99-100 
  ...tion-state.ts |     100 |      100 |     100 |     100 |                   
  ...ction-text.ts |   93.85 |    93.44 |     100 |   93.85 | 30-34,130-131     
  ...selection.tsx |   91.88 |    78.57 |     100 |   91.88 | ...16-417,446-447 
 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.14 |     100 |   99.23 | 277-278           
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  ...inal-theme.ts |   88.59 |     86.2 |     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.14 |     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      |   88.07 |    86.06 |   96.15 |   88.07 |                   
  ...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.5 |     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.61 |    93.27 |     100 |   98.61 | 189,217-218,424   
  ...ssion-text.ts |   90.54 |    71.42 |     100 |   90.54 | 66-68,80,82,90-91 
  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 
  ...coalescing.ts |     100 |      100 |     100 |     100 |                   
  formatters.ts    |   94.87 |    98.24 |     100 |   94.87 | 116-119           
  goal-runtime.ts  |   94.44 |    96.29 |     100 |   94.44 | 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.65 |     100 |     100 | 45,151            
  historyUtils.ts  |   96.07 |     97.1 |     100 |   96.07 | 104-107           
  ...mage-parts.ts |   97.75 |    94.73 |     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.45 |     100 |     100 | 84                
  mouse-hit.ts     |     100 |     90.9 |     100 |     100 | 62-64             
  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 |   84.37 |    81.09 |     100 |   84.37 | ...03-625,759-760 
  ...ickerUtils.ts |     100 |      100 |     100 |     100 |                   
  ...evel-label.ts |   77.77 |    66.66 |     100 |   77.77 | 18,22-24          
  ...are-cursor.ts |      90 |     87.5 |     100 |      90 | 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.24 |    82.66 |     100 |   90.24 | ...04,506-508,631 
  ...ize-reflow.ts |     100 |     92.3 |     100 |     100 | 57,62,209,217,347 
  ...wOptimizer.ts |     100 |    94.73 |     100 |     100 | 35,78             
  terminalSetup.ts |    4.37 |      100 |       0 |    4.37 | 44-393            
  textUtils.ts     |   98.75 |    95.96 |     100 |   98.75 | 292-293,488-489   
  ...background.ts |     100 |      100 |     100 |     100 |                   
  todoSnapshot.ts  |   95.81 |     92.3 |     100 |   95.81 | ...09-210,243-244 
  ...isplay-map.ts |     100 |      100 |     100 |     100 |                   
  updateCheck.ts   |     100 |    92.75 |     100 |     100 | 227-239,331       
  windowTitle.ts   |   96.55 |    94.73 |     100 |   96.55 | 56-57             
  ...ow-keyword.ts |     100 |      100 |     100 |     100 |                   
 ...i/utils/export |   75.03 |     60.1 |   94.59 |   75.03 |                   
  collect.ts       |   71.27 |    65.81 |      96 |   71.27 | ...90-633,655-656 
  index.ts         |     100 |      100 |     100 |     100 |                   
  normalize.ts     |   80.42 |    51.35 |     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.24 |    79.78 |   81.69 |   81.24 |                   
  ...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         |   92.47 |    90.02 |   96.11 |   92.47 |                   
  ...p-profiler.ts |   98.39 |    92.59 |     100 |   98.39 | 141,185,235       
  acpModelUtils.ts |   97.36 |    95.14 |     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       
  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        
  ...y-identity.ts |   89.38 |    85.32 |     100 |   89.38 | ...48-449,456-457 
  ...Calculator.ts |     100 |      100 |     100 |     100 |                   
  cpuProfiler.ts   |   70.73 |    73.23 |   88.88 |   70.73 | ...27,430-431,438 
  deepMerge.ts     |     100 |       90 |     100 |     100 | 50-52,58          
  ...re-runtime.ts |     100 |      100 |     100 |     100 |                   
  ...putCapture.ts |   90.65 |    86.31 |     100 |   90.65 | ...73,371,373-374 
  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 
  ...tyWarnings.ts |     100 |      100 |     100 |     100 |                   
  ...lationInfo.ts |   97.81 |    94.69 |     100 |   97.81 | ...03,420-421,466 
  ...projection.ts |   95.27 |    95.58 |     100 |   95.27 | 140-145           
  jsonc-editor.ts  |   93.18 |    92.66 |     100 |   93.18 | ...80-381,384-385 
  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 |   96.05 |    93.79 |     100 |   96.05 | ...,85-86,334,443 
  ...-part-list.ts |     100 |      100 |     100 |     100 |                   
  osc.ts           |   97.18 |      100 |    87.5 |   97.18 | 182-183           
  package.ts       |   88.88 |    85.71 |     100 |   88.88 | 31-32             
  paths.ts         |     100 |      100 |     100 |     100 |                   
  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.44 |    97.36 |     100 |   99.44 | 121               
  sandbox-path.ts  |     100 |      100 |     100 |     100 |                   
  ...xImageName.ts |     100 |    77.77 |     100 |     100 | 10,18             
  sandboxMounts.ts |     100 |      100 |     100 |     100 |                   
  ...-path-argv.ts |     100 |      100 |     100 |     100 |                   
  sessionPaths.ts  |   90.84 |    90.56 |     100 |   90.84 | ...81-182,185-186 
  shell-args.ts    |     100 |      100 |     100 |     100 |                   
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...ate-verify.ts |     100 |      100 |     100 |     100 |                   
  ...upProfiler.ts |   98.47 |    94.66 |     100 |   98.47 | 132-133,308       
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdioHelpers.ts  |   76.66 |       90 |   83.33 |   76.66 | 93-99             
  ...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 
  ...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 |                   
  ...WithBackup.ts |   65.04 |    77.77 |     100 |   65.04 | 97,112,133-172    
 ...s/housekeeping |   94.35 |    94.11 |     100 |   94.35 |                   
  cleanup.ts       |   92.59 |    93.75 |     100 |   92.59 | ...02-205,209-211 
  ...eractionAt.ts |     100 |      100 |     100 |     100 |                   
  throttledOnce.ts |   95.95 |    93.93 |     100 |   95.95 | 77-78,153-154     
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
Core full-text-summary.txt not found at: coverage_artifact/core/coverage/full-text-summary.txt

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

Built the PR base vs this PR head 3b936af, 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 12 scenario(s).

Qwen Code · serve A/B

@ytahdn ytahdn left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

What this PR does / PR 主旨

This PR lands the workspace-scoped MCP management stage of the runtime architecture: the workspace runtime coordinator becomes the single writable owner of MCP capability state (every write guarded by revision + runtimeEpoch, all runtime mutations through one serial lane), durable MCP config routes are split from live runtime routes (/workspace(s)/.../mcp-config vs /runtime/mcp), a single process-wide OAuth admission lane is shared by all bridge instances, the SDK gains user-scope and workspace-scoped MCP config/runtime methods, and the Web Shell migrates to ensure-on-entry with epoch-checked catalog reads. It explicitly does not fall back to older daemons lacking the new routes (disclosed in the PR body), building on the foundation landed by PR 10593.

本 PR 落地 runtime 架构的 workspace 级 MCP 管理阶段:workspace runtime coordinator 成为 MCP capability 状态的唯一可写所有者(每次写入受 revision + runtimeEpoch 双守卫,所有 runtime mutation 走单一串行 lane);持久化 MCP 配置路由与实时运行时路由分离(/workspace(s)/.../mcp-config vs /runtime/mcp);进程级单一 OAuth 准入 lane 供所有 bridge 实例共享;SDK 增加用户级与 workspace 级的 MCP 配置/运行时方法;Web Shell 迁移为进入页面即 ensure、读取 catalog 前校验 epoch。明确不再回退缺少新路由的旧版 daemon(PR 正文已披露),建立在 PR 10593 打下的基础之上。


Review basis / 审查基线: head e11f8125ec913f6bf41d5d95aaf61c46ed5ecfe8, full static review (38 files, +3849/−563), all findings below cross-verified against a head tree at that SHA; no tests/builds were run. CI at publish time: all listed checks green (Test ubuntu, Serve A/B, web-shell visuals, Real daemon E2E, Desktop Shell, Integration Tests, Live Host, Java matrix, secret scan, CVE audit, etc.) except web-shell E2E Smoke = cancelled (the rollup renders it as fail, but it is a cancellation, not a test failure); the review-pr bot check was still pending. Not polled or waited on.

What reads well / 做得好的地方

  • The revision/epoch discipline is consistent end to end: the coordinator rejects superseded operations and replaced ACP children, the serial physical lane keeps mutations ordered, and activation: applied|deferred|reconciling is reported honestly instead of optimistically.
  • The process-wide OAuth lane is one closure wired into every bridge creation site, with release paths covering synchronous rejection, non-pending responses, channel teardown, and the timeout-retire path, so the global lane cannot wedge.
  • Web Shell pages verify epoch equality before trusting any catalog read — exactly what the architecture doc asks of pages.
  • Test coverage genuinely pins the state machine: revision coalescing, cold deferral, drain-rollback replay, cross-workspace OAuth rejection, admission retention after a timed-out RPC, and untrusted-workspace read-vs-mutate asymmetry all have dedicated cases.
  • The 13 endpoint contracts (path/method/fields/encoding) between routes, coordinator, bridge, and the SDK line up one by one — no dead fields found in the SDK sweep.

Findings / 发现

🔴 1. Critical — floating prepareMcp() promise in ensure() can take down the whole daemon

packages/cli/src/serve/workspace-runtime-coordinator.ts:159-168: ensure() calls const preparation = this.prepareMcp() and only attaches it to withTimeout(...) when remainingMs > 0. When the deadline is already exhausted, preparation floats with no handler at all (the > 0 path is safe because withTimeout's race always attaches handlers).

Two verified rejection paths feed it:

  1. queueMcpWork (:278-294) swallows the queue-tail copy of the rejection but the operation promise returned to the caller is unprotected; the queued body's assertAcceptingWork (:283) throws WorkspaceDrainingError once beginDrain() has run (called from the workspace-removal flow, workspace-management.ts:1586). Because prepareMcpRevision polls on a 2-minute budget (:296-351), a queued prepare can resolve minutes after ensure() gave up — so "remove this workspace within ~2 minutes after its ensure timed out" lands the rejection.
  2. The out-of-try await this.bridge.preheat(...) at :296-302 can reject on spawn failure.

The consequence chain is not hypothetical: the serve fast path installs no process-level unhandledRejection handler — a hazard you documented yourself at run-qwen-serve.ts:6527-6533 ("Node's default for one is to exit"), with a test asserting that behavior. One unhandled rejection here exits the daemon process, taking down every runtime, session, and connection it serves. Every sibling fire-and-forget in this same file attaches .catch (:206-213 scheduleMcpReconciliation, :251-259 runMcpRuntimeMutation); this one is the gap, and it is diff-new code.

Suggested fix — mirror :210-213 unconditionally:

const preparation = this.prepareMcp();
preparation.catch((error) =>
  this.recordMcpError(
    this.mcpRevision,
    this.bridge.getWorkspaceRuntimeLifecycleSnapshot().runtimeEpoch,
    error,
  ),
);
const remainingMs = deadline - Date.now();
if (remainingMs > 0) { ... }

触发链:ensure() 超时后约 2 分钟内删除该 workspace(或预热阶段 spawn 失败)→ 漂浮的 prepareMcp() 拒绝 → serve 无进程级 unhandledRejection 兜底 → Node 默认退出进程 → 整个 daemon 连同所有 runtime/session/连接一起退出。同文件其他 fire-and-forget 均有 .catch,此处为 diff 新增代码中的遗漏。修复:无条件挂 preparation.catch(...) 记录错误。

🟡 2. Important — manageServer inferred return union breaks property access at 5 consumer sites (needs a local typecheck to settle)

packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.ts:75-111: manageServer has no return annotation. The async arrow returns three differently-shaped objects, so TypeScript infers a union — and the enable/disable literal ({serverName, action, ok: true as const, ...(changed ... ? {} : {changed})}) does not carry messages, authUrl, or pending. Consumers then access those properties on the unnarrowed union:

  • McpManagerPage.tsx:946/947/949result.authUrl, result.messages, result.pending
  • McpStatusMessage.tsx:524/525result.messages, result.authUrl

Call-site narrowing of the action argument does not narrow the returned union, and nothing in the consumption chain constrains it: daemon-react-sdk.ts:98 re-exports the hook plainly, and both pages call it directly (the DaemonWorkspaceActions interface only types actions.manageMcpServer, not the hook's manageServer).

Why CI green does not settle it either way: this PR's CI gate never runs a package-level typecheck for web-shell — ci.yml only runs typecheck:integration; the root npm run typecheck lives in preflight/release flows, not the PR gate. A static read says these five accesses are TS2339 errors; that conflicts with the PR body's "full typecheck passed" claim, and I cannot run tsc in this review. Please run npm -w packages/web-shell run typecheck at e11f812 — if something saves it (a narrowing mechanism I'm missing), a reply noting it would close this out.

Suggested fix regardless — one annotation makes the contract explicit and removes the union:

manageServer: async (
  serverName: string,
  action: Parameters<typeof actions.manageMcpServer>[1],
): Promise<DaemonMcpManageResult> => { ... }

(与本 PR 正文"全量 typecheck 通过"的声明冲突,静态阅读无法调和;请在该 SHA 上跑一次 npm -w packages/web-shell run typecheck 确认——若有隐式机制救场,回复说明即可关闭本条。)

🟡 3. Important — MCP status is stamped with the epoch at response arrival, not request send

packages/acp-bridge/src/bridge.ts:6100-6214 (requestWorkspaceStatus): the stamp at :6135-6143 reads the closure's current runtimeEpoch when the response arrives — there is no epochAtRequest capture before the await and no liveChannelInfo() recheck after it. Deferred channel retirement (:3559) keeps the replaced channel OPEN while sessions drain, so a late completed response from the old ACP child can win the race against channel.exited and get stamped with the new epoch + source:'live', then written into workspaceMcpStatusCache (:6209). The coordinator (:324-325) and McpManagerPage (:496-497) would then treat a stale child's completed state as the current runtime's live ready — violating exactly the epoch-trust invariant this PR states for pages.

The same file already contains the correct pattern: expireWorkspaceMcpAuthentication (:6217-6225) checks liveChannelInfo() !== info before trusting the captured channel. Suggested fix, mirroring it:

const info = liveChannelInfo();
const epochAtRequest = this.runtimeEpoch;
// ... await extMethod ...
if (liveChannelInfo() !== info || epochAtRequest !== this.runtimeEpoch) {
  throw new BridgeChannelClosedError(...);
}

(epoch 在响应到达时才读取;延迟退役通道保旧通道 OPEN,旧子进程的迟到 completed 可能被打上新 epoch + live 写入缓存,违反本 PR 自述的 epoch 信任不变量。同文件 :6217-6225 已有正确先例可参照。)

🟡 4. Important — tools/resources cache writes are unstamped, so cache hits can read as false discovery timeouts

packages/acp-bridge/src/bridge.ts: cacheWorkspaceMcpDetails (:6000-6053) writes the raw extMethod result into the tools/resources caches without runtimeEpoch, and the idle fallbacks in getWorkspaceMcpToolsStatus/getWorkspaceMcpResourcesStatus (:11810-11868) return {...cached, acpChannelLive: false} — again no runtimeEpoch. But the clients do hard epoch comparison (McpManagerPage.tsx:633/657, McpStatusMessage.tsx:469: result.runtimeEpoch !== runtimeStatus.runtimeEpoch), so a cache-served read fails the comparison (undefined !== N) and surfaces as a "discovery timeout" instead of the cache display the design intends. Reachability is narrowed in practice (loadReadyRuntime requires live+ready+epoch match, and ensure's keepalive is 10 minutes), but the stamp asymmetry is a latent trap with a one-line fix: stamp runtimeEpoch when writing the cache entries, same as the live writers at :11835/:11866 already do.

(tools/resources 缓存写入未盖 runtimeEpoch,而客户端是硬比较 → 缓存命中被误判为 discovery timeout;可达性虽被 ensure 的 10 分钟 keepalive 收窄,但修复只需缓存写入时盖章。)

🟡 5. Important — enable/disable from the chat MCP card drops activation, so a cold runtime burns ~121s before a misleading timeout

McpStatusMessage.tsx:380-425 includes enable/disable in serverActions and routes them through manageServer (:521-526). Neither the hook's enable/disable literal nor DaemonMcpManageResult (sdk types.ts:4001-4009) carries activation, so this path has no activation === 'deferred' short-circuit — unlike McpManagerPage.runAction (:909-940), which tracks activation and short-circuits deferred writes. Concrete scenario: cold runtime + a cached server list → the card's reloadSelectedServer loop polls 81 × 1.5s ≈ 121s and then reports a misleading mcp.discovery.timeout, where the manager page would have shown "applied on next start" immediately. Secondary: the changed: false double-scope write against a cold runtime lands the same page in the same trap. Suggested fix: thread activation through the manage-result type and short-circuit deferred in both consumers.

(聊天卡片里的 enable/disable 丢失 activation 字段,无 deferred 短路;冷 runtime + 缓存列表场景下空转约 121 秒后报误导性超时。建议结果类型穿透 activation 并在两个消费者都短路 deferred。)

🟢 Nits

  • manageRuntimeMcpServer lacks the opts?.timeoutMs override its sibling has. DaemonClient.ts:6036-6051 hardcodes 330s (MCP_RESTART_DEFAULT_TIMEOUT_MS) while sibling manageMcpServer (:4907-4933) accepts an override; the 10-minute authenticate budget in actions.ts:400 is dead code because the inner SDK fetch aborts at 330s first. In practice the budget is adequate — the ACP-side authenticate handler returns as soon as the auth URL exists (30s start budget, acpAgent.ts:474) — so this is symmetry cleanup, not a timeout bug.
  • parseReloadOptions duplication with wording drift. workspace-runtime-mcp.ts:33-72 re-implements parseMcpReloadOptions (workspace-mcp-control.ts:46-88) with slightly different error messages; worth collapsing into one shared helper before they drift further.
  • reloadSelectedServer polling loop has no mounted check. McpStatusMessage.tsx:430-458 fires up to 81 × 1.5s requests without consulting mountedRef/waitForPoll, leaving ~160 requests in flight after unmount.

💬 Low confidence / human check

  • useDaemonMcp.ts:34-39 binds event-driven reloads to the primary workspace's signals (useDaemonWorkspaceEventSignals) even when browsing a secondary workspace via workspaceCwd. The fan-out semantics of those signals across workspaces were not verified in this pass — worth a maintainer's glance.

Notes on the bot triage (same SHA) / 与机器人审查的分歧

  • The bot's claim that manageServer's enable/disable branch is "unreachable from the current UI" does not hold: McpStatusMessage.tsx:405-411 renders enable/disable actions and routes them through manageServer (see finding 5). 机器人"enable/disable 分支不可达"的论断已证伪。
  • The bot's bare-preheat/documentation mismatch question (§8.3 / §14.4 / §16.1 of the architecture doc vs the preserveForFirstStatusRead removal) was not independently re-verified in this pass — carried forward as an open maintainer question, not counted among this review's findings.
  • The dropped older-daemon fallbacks are disclosed in the PR body; treated as a confirmed design decision.

Conclusion / 结论

COMMENT. One verified Critical (floating promise → daemon-exit path) and four Important items above. The state machine, OAuth lane, and test coverage are in good shape — the Critical is a narrow-window but high-blast-radius omission with a one-line fix, and the type-union item needs a local typecheck to settle. Self-authored PR, so no approval is possible here regardless; requesting the fixes or a reply addressing each point.

结论:COMMENT。1 条已验证 Critical(触发窗口窄但爆炸半径大,一行可修)+ 4 条 Important。状态机、OAuth lane 与测试覆盖整体扎实;类型联合那条需本地 typecheck 定论。本 PR 为作者本人提交,无法 approve,请逐条处理或回复。

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Independent review of head e11f8125ec913f6bf41d5d95aaf61c46ed5ecfe8. Findings frozen before reading the existing review; cross-check outcomes recorded below.

Scope

Static review: coordinator, bridge, config routes, runtime-MCP routes, SDK. Not read: McpStatusMessage full body, requestWorkspaceStatus pre-existing body. No local toolchain (rungs not run).

CI

ubuntu Test green, Desktop Shell green, Integration no-AK green. web-shell E2E cancelled. Test win/mac + Integration CLI: SKIPPED.

Own finding

F1 (Suggestion) - publishConfiguration (workspace-mcp-config.ts:~435) has 3 empty catch{} blocks for broadcast notifications. Intentional best-effort; observability concern only, not blocking.

Cross-check outcomes

Confirmed Critical — R1: floating prepareMcp() promise in ensure()

workspace-runtime-coordinator.ts:159-168: when deadline - Date.now() <= 0, const preparation = this.prepareMcp() has no .catch() handler. queueMcpWork returns the operation promise directly — mcpPhysicalTail absorbs the tail copy but does NOT suppress the caller-facing rejection. prepareMcpRevision can throw (preheat failure at :296, assertAcceptingWork drain-while-queued at :283, poll-loop errors at :296-351), arriving up to 120 s after ensure() returned. No process-level unhandledRejection exists (run-qwen-serve.ts:6527-6533) — Node's default exit fires. Every other fire-and-forget in the coordinator attaches .catch (lines 206-213, 251-259); this one does not.

Fix: ```ts
preparation.catch((error) => this.recordMcpError(
this.mcpRevision,
this.bridge.getWorkspaceRuntimeLifecycleSnapshot().runtimeEpoch, error));


### Cross-check misses (could not rule out)

- **R2** (TypeScript union in manageServer): McpManagerPage routes enable/disable via setConfigServerEnabled — that call site is clean. McpStatusMessage full body not read; typecheck not run. Cannot confirm or refute.
- **R3** (epoch at response arrival): Did not read pre-existing requestWorkspaceStatus body. Consistent with the diff evidence but unconfirmed.
- **R4** (tools/resources cache unstamped): cacheWorkspaceMcpDetails not read. Unverified.
- **R5** (enable/disable drops activation in McpStatusMessage): McpManagerPage confirmed clean. McpStatusMessage not fully read.

## Conclusion

COMMENT. R1 confirmed Critical — one-line fix. R2-R5 need maintainer verification. The state machine, OAuth lane, and MCP serial mutation queue are well-designed.

_Reviewed with AI assistance._
<!-- pr-review head=e11f8125ec913f6bf41d5d95aaf61c46ed5ecfe8 round=1 -->

Comment thread packages/cli/src/serve/workspace-runtime-coordinator.ts
@qqqys

qqqys commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

Verification report — head e9ffb1cbc9 (recurring independent review)

Scope: full diff (38 files, +3849/−563) — coordinator, config/runtime route modules, acp-bridge, SDK client, web-shell MCP manager, design docs.

Critical-focused review — no Critical found

Route ownership (per the workspace-route rules). workspace-mcp-config.ts registers two surfaces: legacy /workspace/config/mcp/* pinned to the primary entry with user-scope writes (compat surface, no trust gate — matches the pre-existing settings routes), and /workspaces/:workspace/config/mcp/* where reads resolve the selected entry and mutations additionally require requireTrustedWorkspaceRuntime. workspace-runtime-mcp.ts resolves the selected live runtime for both surfaces and never falls back to the primary (resolveWorkspaceRuntimeFromParam → 400/404 class responses). All mutations ride mutate({strict:true}), per-(workspace,scope) withMcpServerMutationLock shared with the pre-existing settings route, and persistSetting carries generationGuard.assertOpen.

Coordinator state machine. Every MCP status write is guarded by the monotonic mcpRevision and re-validated against the live runtimeEpoch at each transition (poll loop, completion, error recording); a replaced ACP child projects stale and can never overwrite newer state. Drain defers reconciliation and cancelDrain replays it. hasActiveWork now counts management operations and queued MCP work, so removal admission can't race them.

Cross-check of the standing R1 (floating prepareMcp() promise in ensure()): not reproducible — refuted. queueMcpWork assigns mcpPhysicalTail = operation.then(() => undefined, () => undefined), which subscribes a rejection handler to the very promise it returns, synchronously, at queue time. A caller that never awaits the result therefore cannot produce an unhandled rejection. Verified two ways: (a) a minimal Node repro of the exact pattern (queue(async () => { throw … }), result unhandled by the caller) exits clean — no unhandledRejection; (b) live: during my first e2e pass a deliberately mis-framed mock MCP server held discovery open past the 120s MCP_PREPARE_TIMEOUT_MS — the capability recorded error/mcp_prepare_timed_out, the daemon stayed up and healthy, and subsequent reloads converged normally. The R1 premise ("mcpPhysicalTail absorbs the tail copy but does not suppress the caller-facing rejection") misreads .then(a, b) semantics: it handles the receiver promise itself.

OAuth admission. acquireMcpAuthentication is a real daemon-global closure wired at all three bridge-creation sites in run-qwen-serve.ts (primary, registered, dynamic). Release paths cover: acquisition failure → 409 mcp_authentication_in_progress; ext-method failure (non-timeout) → release; non-pending success → release; observed completion → release; channel teardown → release-all; timeout → conservative hold until channel retirement (correct — child OAuth work is uncancellable).

Secrets round-trip. prepareSettingWrite restores __redacted__ placeholders from stored values before persisting and redacts on the way out (verified live below).

Removals. preserveForFirstStatusRead is fully excised (0 references at head); mergeManagedWorkspaceMcpStatus is now epoch-guarded; the removed web-shell preloading path (mcpDialogMessage preload for the Plugins tab) leaves the /mcp chat-dialog surface intact (still fed at App.tsx:10441).

Local verification at head (codeload tarball + npm ci)

  • cli: workspace-runtime-coordinator + workspace-qualified-rest + acpAgent — 641/641
  • acp-bridge: bridge.test.ts — 883/883
  • sdk-typescript: DaemonClient — 402/402
  • web-shell: App + PluginManagerPage + DaemonWorkspaceProvider + actions — 647/647
  • cli: run-qwen-serve (incl. OAuth admission pins) — 373/373

Live e2e (bundled head, tmux, loopback daemon, hermetic HOME/QWEN_RUNTIME_DIR, two registered workspaces, NDJSON mock MCP stdio server)

  1. Cold config write: PUT /workspace/config/mcp/servers/mock {scope:user, env:{API_TOKEN:secret}} → 200; secret verbatim in ~/.qwen/settings.json; GET redacts to __redacted__; re-PUT with the placeholder preserves the stored secret and applies an unrelated edit. ✅
  2. Workspace scoping: PUT /workspaces/<ws-b>/config/mcp/servers/mockb {scope:workspace} → lands only in ws-b/.qwen/settings.json, activation:"deferred" while cold; ws-a untouched. ✅
  3. Validation: scope mismatch 400, unknown workspace 400, illegal server name 400, malformed reload body 400 (invalid_force_reconnect_all_flag). ✅
  4. Reload on primary → {"accepted":true}; capability converges to {state:ready, revision:6, runtimeEpoch:2} after an epoch-advancing child replacement; live status discoveryState:completed, source:live, mock connected; tools endpoint lists mcp__mock__mock_echo with the matching epoch; restart → 200 restarted:true. ✅
  5. Workspace isolation: ws-b sees user-scope mock + workspace-scope mockb; ws-a sees only mock. ws-b's mockb arrived pending approval → POST …/runtime/mcp/mockb/approve → connected. ✅
  6. Exclusions: enable blocked by a mock* wildcard → 409 mcp_excluded_by_pattern with the pattern list; route-driven disable of mockb propagated live (both mock*-matching servers reported disabled:true on ws-b) and did not affect ws-a. ✅
  7. Compat: legacy GET /workspace/mcp response shape unchanged. ✅
  8. Fail-closed prepare: with a broken MCP server, the 120s prepare deadline records mcp_prepare_timed_out and the daemon keeps serving (see R1 note). ✅

Not covered live: the OAuth browser flow (needs a real OAuth server fixture) — its admission lifecycle is covered by the bridge/serve unit pins above.

Not approving under my standing rule: triage escalated this PR to the maintainer at Stage 0 (cross-package core surface) and no bot/maintainer APPROVED review stands at this head. Non-blocking observations: (a) POST …/runtime/mcp/:server/approve on a server with nothing pending returns a 500 rather than a 4xx — cosmetic, same as the legacy route; (b) out-of-band edits to a workspace settings file don't trigger live MCP reconciliation until a route-driven write occurs (pre-existing settings-watcher behavior, not introduced here).

CI at head: all completed lanes green (Test ubuntu, Integration no-AK, web-shell E2E smoke, Desktop Shell ×2, Serve A/B — 12/12 scenarios unchanged, Real daemon E2E, Post Coverage); only review-pr still running at time of writing.

— qwen3.8-max via Qwen Code (recurring independent review)

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

Not explored to full depth (tool budget reached): chunk 1: could not execute the modified test (review worktree has no node_modules; npx vitest failed to resolve vitest/config ) — verified it statically against the i…; "agent 1c": none — no check was cut short..

中文说明

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

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

未探索到全部深度(达到工具调用预算):chunk 1:could not execute the modified test (review worktree has no node_modules; npx vitest failed to resolve vitest/config ) — verified it statically against the i…"agent 1c"none — no check was cut short.

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

Comment thread packages/cli/src/serve/routes/workspace-runtime-mcp.ts
Comment thread packages/cli/src/serve/routes/workspace-runtime-mcp.ts Outdated
Comment thread packages/cli/src/serve/workspace-runtime-coordinator.ts
Comment thread packages/cli/src/serve/workspace-runtime-coordinator.ts
Comment thread packages/web-shell/client/components/messages/McpStatusMessage.tsx
Comment on lines +456 to +458
await new Promise((resolve) => window.setTimeout(resolve, 1_500));
}
if (!nextStatus || runtimeEpoch === undefined) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-13: McpStatusMessage poll loop has no unmount guard

The reloadSelectedServer poll loop runs up to 81 × 1.5s of daemon calls with no mounted/cancellation check — the component has no mountedRef at all, and the action is fire-and-forget from the keydown handler. Triggering a server action then switching conversation unmounts McpStatusMessage mid-poll: the detached loop keeps issuing runtimeStatus (and possibly reload/loadTools) daemon calls for up to ~2 minutes (11 calls measured in 15s of fake time after unmount), then calls setLocalStatus/setLocalToolsByServer on the unmounted component — wasted daemon traffic from a dead component for every abandoned action. Track mounted state in a ref and break the loop — and guard each setLocal* call — when unmounted, as McpManagerPage's waitForPoll does.

Witness:

Probe: render McpStatusMessage, trigger enable via keyboard, unmount mid-poll, advance 15s fake time — intact PR: calls before unmount 1, calls after +15s of polling post-unmount 11; with a mountedRef guard: 1.

Fix constraint: McpManagerPage's waitForPoll returns mountedRef.current and observeDiscovery bails with if (!mountedRef.current) return false; — the new guard should not change the 81-attempt budget the loops share.

Fix witness: New McpStatusMessage.test.tsx: with fake timers and a runtimeStatus mock that never becomes ready, start an action, unmount, advance timers past several 1.5s ticks, assert runtimeStatus is not called after unmount; removing the guard reds it.

中文说明

reloadSelectedServer 轮询循环最多会进行 81 × 1.5 秒的 daemon 调用,却没有任何 mounted/取消检查——组件根本没有 mountedRef,且操作是从 keydown 处理器以 fire-and-forget 方式发起的。触发服务器操作后切换会话会在轮询中途卸载 McpStatusMessage:脱离的循环会继续发出最多约 2 分钟的 runtimeStatus(以及可能的 reload/loadTools)daemon 调用(实测卸载后 15 秒虚拟时间内有 11 次调用),然后对已卸载组件调用 setLocalStatus/setLocalToolsByServer——每次被放弃的操作都会让一个已死组件浪费 daemon 流量。建议用 ref 跟踪 mounted 状态,在卸载时跳出循环并守卫每个 setLocal* 调用,与 McpManagerPage 的 waitForPoll 一致。

(Witness 证据、Fix constraint 修复约束与 Fix witness 修复见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Deferring this non-Critical suggestion to follow-up: this PR has already had more than five review rounds, and this pass is intentionally limited to correctness and regression fixes per project policy. No behavior change was made for this suggestion.

Comment on lines +34 to +37
const workspaces = useMemo(() => {
const listed = (workspace.capabilities?.workspaces ?? []).filter(
(entry) => entry.kind !== 'live',
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-23: kind !== 'live' selector filter has no test witness

The kind !== 'live' filter — the only guard keeping daemon-owned live-conversation workspaces out of the MCP workspace selector — has no test witness; neither fixture in the new PluginManagerPage.test.tsx sets kind. /capabilities lists active internal entries (capabilities.ts:64-68) and stamps kind: 'live' for provenance 'live-conversation' (:130-133); resolveWorkspaceRuntimeFromParam does NOT filter internal runtimes (only resolveManagedWorkspaceRuntimeByPathSelector applies isInternalWorkspaceRuntime). Replacing the filter with () => true keeps the shipped suite green (2/2); a purpose-built probe test adding a kind:'live' entry flips pass→fail. Add a third workspace entry with kind: 'live' to the fixture and assert its label never appears among the selector's [role='option'] elements.

Witness:

Probe: mutant (filter → () => true) — shipped PluginManagerPage.test.tsx still passes 2/2; purpose-built probe test adding a kind:'live' fixture entry passes on intact code and fails on the mutant ('expected [ Primary, LiveConversation, …(1) ] to not include LiveConversation').

Fix constraint: The stamp value is exactly 'live' — routes/capabilities.ts:132 ? { kind: 'live' as const } (sdk-typescript types.ts:119 kind?: 'live';); the fixture must use that literal.

Fix witness: The new case asserting the live-kind workspace is absent from the option list; removing the entry.kind !== 'live' filter must make it fail.

中文说明

kind !== 'live' 过滤器——阻止 daemon 自有的会话级(live)workspace 进入 MCP workspace 选择器的唯一守卫——没有测试见证;新的 PluginManagerPage.test.tsx 两个 fixture 都没有设置 kind。/capabilities 会列出 active 的内部条目(capabilities.ts:64-68),并为 provenance 为 'live-conversation' 的条目盖上 kind: 'live'(:130-133);resolveWorkspaceRuntimeFromParam 并不过滤内部 runtime(只有 resolveManagedWorkspaceRuntimeByPathSelector 应用 isInternalWorkspaceRuntime)。把过滤器替换为 () => true,现有套件仍然全绿(2/2);专门构造的探针测试加入 kind:'live' 条目后由过转变失败。建议在 fixture 中加入第三个 kind: 'live' 的 workspace 条目,并断言其标签永远不出现在选择器的 [role='option'] 元素中。

(Witness 证据、Fix constraint 修复约束与 Fix witness 修复见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Deferring this non-Critical suggestion to follow-up: this PR has already had more than five review rounds, and this pass is intentionally limited to correctness and regression fixes per project policy. No behavior change was made for this suggestion.

Comment on lines 336 to 338
return withActionTimeout(
client.initializeWorkspaceMcp(),
workspace.ensureRuntime().then(() => ({ accepted: true })),
'Initialize MCP timed out',

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-24: initializeMcp now always resolves accepted:true; consumer…

initializeMcp (and useDaemonMcp.initialize's workspaceClient branch, useDaemonMcp.ts:56-58) can now only ever resolve {accepted: true}, yet the sole consumer AgentCreatePage.tsx:227 still branches if (!initialization.accepted) await reloadMcpConfig() — permanently unreachable. The legacy contract genuinely returned accepted:false ('True only when this request started a new background discovery task'), so the type/comment now falsify the implementation; the dead branch also keeps reloadMcpConfig in the mount-effect dependency array (:274) — the very effect whose dependency instability the hook-identity Critical addresses. No runtime failure — the cost is a falsified contract and a stale dep on the effect under audit. Delete the dead branch and drop reloadMcpConfig from the effect deps (and the now-unused destructured binding), or change initializeMcp()'s result type so it no longer advertises an accepted flag nothing can make false.

Witness:

not run — dead-code fact settled by reading the construction site: the only value initializeMcp can resolve is the literal {accepted: true} (workspace.ensureRuntime().then(() => ({accepted: true}))), so no run could discriminate.

Fix witness: N/A (dead-code removal adds no guard a test can pin).

中文说明

initializeMcp(以及 useDaemonMcp.initialize 的 workspaceClient 分支,useDaemonMcp.ts:56-58)现在只可能 resolve {accepted: true},但唯一消费者 AgentCreatePage.tsx:227 仍然保留 if (!initialization.accepted) await reloadMcpConfig() 分支——永远不可达。旧契约确实会返回 accepted:false(「仅当本次请求启动了新的后台 discovery 任务时为 true」),因此类型/注释现在与实现相悖;这个死分支还让 reloadMcpConfig 留在挂载 effect 的依赖数组中(:274)——正是 hook 身份 Critical 所处理的依赖不稳定的那个 effect。无运行时故障——代价是被伪造的契约和留在被审查 effect 上的陈旧依赖。建议删除死分支并从 effect 依赖中移除 reloadMcpConfig(连同不再使用的解构绑定),或修改 initializeMcp() 的结果类型,不再暴露一个不可能为 false 的 accepted 标志。

(Witness 证据、Fix constraint 修复约束与 Fix witness 修复见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Deferring this non-Critical suggestion to follow-up: this PR has already had more than five review rounds, and this pass is intentionally limited to correctness and regression fixes per project policy. No behavior change was made for this suggestion.

const timeoutMs = action === 'authenticate' ? 10 * 60_000 : 5 * 60_000;
if (action === 'enable' || action === 'disable') {
const config =
action === 'disable' ? await workspace.mcpConfig() : undefined;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-15: Disable scope diverges for .mcp.json-shadowed servers

For a server whose effective origin is project .mcp.json — which outranks user settings (config.ts:2071) — and whose name is ALSO defined in user settings, the disable-scope probe picks 'user' (serverName in config!.user is true from the raw user file; mcpConfig() returns the raw per-scope maps, .mcp.json in neither), while the legacy handler this replaces special-cased server.scope === 'project' → SettingScope.Workspace (acpAgent.ts:9484-9500), matching the design doc ('Disabling project or workspace servers writes the exclusion to workspace-local settings'). The chat card is the only UI that can disable project servers (isManagedServerVisible hides project_mcp_json from the manager page), so this is a regression on a live path: disabling such a server from the chat card writes mcp.excluded to USER settings — global across the user's workspaces — instead of workspace-local. Align the probe with origin semantics — route project-origin disables to workspace scope per the design doc — in both copies (actions.ts and useDaemonMcp.ts).

Witness:

not run — scope selection is a ternary over the quoted raw maps and the authority facts are quoted code/doc: precedence config.ts:2071 ('settings < project .mcp.json < workspace/system settings'), configOrigin 'project_mcp_json' derivation (acpAgent.ts:6510-6527), legacy special case (acpAgent.ts:9484-9500), design-doc rule.

Fix witness: actions.test.ts case: mcpConfig resolves a user-map entry for a server whose effective origin is project .mcp.json; manageMcpServer(name,'disable') must call the workspace-scope setMcpServerEnabled, not setUserMcpServerEnabled; red if the probe falls back to the raw user-map membership test.

中文说明

对于有效 origin 为项目 .mcp.json 的服务器(.mcp.json 优先于 user 配置,config.ts:2071),且其名称同时定义在 user 配置中时,disable 的 scope 探测会选择 'user'(serverName in config!.user 为 true,来自原始 user 文件;mcpConfig() 返回的是按 scope 的原始映射,其中不含 .mcp.json);而它所替换的旧处理器对 server.scope === 'project' 有特殊分支 → SettingScope.Workspace(acpAgent.ts:9484-9500),与设计文档一致(「禁用项目或 workspace 服务器时排除项写入 workspace 本地配置」)。聊天卡片是唯一能禁用项目服务器的 UI(isManagedServerVisible 在管理页隐藏 project_mcp_json),因此这是一条实际可用路径上的回归:从聊天卡片禁用此类服务器会把 mcp.excluded 写入 USER 配置——对该用户的所有 workspace 全局生效——而不是 workspace 本地配置。建议在两个副本(actions.ts 与 useDaemonMcp.ts)中让探测与 origin 语义对齐——按设计文档把项目 origin 的 disable 路由到 workspace scope。

(Witness 证据、Fix constraint 修复约束与 Fix witness 修复见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Deferring this non-Critical suggestion to follow-up: this PR has already had more than five review rounds, and this pass is intentionally limited to correctness and regression fixes per project policy. No behavior change was made for this suggestion.

Comment on lines +29 to +31
() =>
workspaceClient ? workspaceClient.runtimeMcp() : actions.loadMcpStatus(),
[actions, workspaceClient],

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R1-16: Cross-workspace auto-reload is keyed to the host session's…

With workspaceCwd set, load/mutations route to the requested workspace, but useWorkspaceEventReload is still keyed to the HOST session's signals?.mcpVersion. mcpVersion bumps only on workspace.mcp.* events arriving in the ACTIVE session's stream (DaemonSessionProvider.tsx:4961-4966; both bump call sites are gated on activeWorkspaceScoped), and daemon-side MCP events publish only on the affected workspace's bridge (server.ts:1283-1290). McpManagerPage — the only workspaceCwd consumer — has no continuous idle polling (only bounded 1.5s loops), so a foreign workspace's externally-driven MCP change (a CLI session on B restarting/approving/authenticating a server, another client editing B's workspace config) never refreshes the view until manual Refresh, while any MCP event on host workspace A spuriously re-fetches B's status. Key the reload signal to the requested workspace — a per-workspace MCP signal analogous to skillMutationsByCwd — or at minimum skip the host-signal-driven reload when workspaceClient is active.

Witness:

not run — the wiring gap is quoted through four layers: useDaemonMcp.ts:36-38 keys useWorkspaceEventReload to signals?.mcpVersion; bumps only on active-session workspace.mcp.* events (DaemonSessionProvider.tsx:4961-4966); events publish only to the affected workspace's bridge (server.ts:1283-1290 → workspace-service/index.ts:1297-1330); no per-cwd MCP signal exists to subscribe to.

Fix constraint: useWorkspaceEventReload.ts:16-23 skips the first observed version via hasMountedRef — the fix must preserve no-reload-on-mount semantics so the initial explicit load is not duplicated.

Fix witness: Render-hook test: with workspaceCwd set, a host-signal mcpVersion bump must NOT call workspaceClient.runtimeMcp() again (and a target-workspace signal source, once added, must); red on inversion.

中文说明

设置了 workspaceCwd 时,加载/变更会路由到所请求的 workspace,但 useWorkspaceEventReload 仍然以宿主会话的 signals?.mcpVersion 为键。mcpVersion 只在到达活跃会话事件流的 workspace.mcp.* 事件时递增(DaemonSessionProvider.tsx:4961-4966;两处递增调用点都以 activeWorkspaceScoped 为条件),而 daemon 侧 MCP 事件只发布到受影响 workspace 的 bridge(server.ts:1283-1290)。McpManagerPage——唯一传 workspaceCwd 的消费者——没有持续的空闲轮询(只有有界的 1.5 秒循环),因此其他 workspace 的外部驱动 MCP 变化(B 上的 CLI 会话重启/审批/认证服务器,或其他客户端编辑 B 的 workspace 配置)永远不会刷新视图,直到手动 Refresh;而宿主 workspace A 上的任何 MCP 事件都会无谓地重新拉取 B 的状态。建议把刷新信号改为以所请求的 workspace 为键——类似 skillMutationsByCwd 的按 workspace MCP 信号——至少应在 workspaceClient 激活时跳过宿主信号驱动的 reload。

(Witness 证据、Fix constraint 修复约束与 Fix witness 修复见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Thanks. Deferring this non-Critical suggestion to follow-up: this PR has already had more than five review rounds, and this pass is intentionally limited to correctness and regression fixes per project policy. No behavior change was made for this suggestion.

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

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

  • R1-16 self-satisfying labelFor assertions in the priority test (App.test.tsx:18826) — already reported (comment 3905016952)
  • R1-21 dead-string negative assertions in 'does not show MCP discovery progress' (App.test.tsx:19278) — already reported (comment 3905016959)
  • R1-30 McpStatusMessage poll loop has no unmount guard (McpStatusMessage.tsx:430-477) — already reported (comment 3905017012)
  • R1-26 multi-scope enable commits user scope before workspace scope without rollback (actions.ts:414-417) — already reported (comment 3905016986)
  • R1-32 initializeMcp always resolves accepted:true, leaving AgentCreatePage's reloadMcpConfig branch dead (actions.ts:337) — already reported (comment 3905017026)
  • R1-19 server-side coverage gaps on the runtime-MCP route family (workspace-runtime-mcp.ts) — already reported (comment 3905016903)

Not reviewed: build-and-test — the test phase did not run (the harness budget expired before the test phase after 16/17 workspaces built cleanly; the coordinator suite 26/26 and useDaemonMcp 2/2 were run green by review agents).

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

Not explored to full depth (tool budget reached): chunk 5: executing packages/cli/src/serve/workspace-runtime-coordinator.test.ts under vitest to confirm the manual traces — the review worktree has no node_modules , ….

Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.

Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:

  • packages/cli/src/serve/routes/workspace-runtime-mcp.ts:229 — [review] runtime mutation routes drop the originator client id (parity with legacy X-Qwen-Client-Id validation)
  • packages/cli/src/serve/workspace-runtime-coordinator.ts:343 — [review] three prepareMcpRevision branches unpinned (not_started kickoff / discovery errors / prepare deadline)
  • packages/cli/src/serve/workspace-runtime-coordinator.test.ts:121 — [review] live epoch-mismatch stale projection unpinned
  • packages/cli/src/serve/workspace-runtime-coordinator.test.ts:191 — [review] 'observes an MCP reload already in progress' cannot discriminate observe-vs-retry
  • packages/web-shell/client/App.test.tsx:18858 — [review] priority test's message-side disabled-first / succeeded-passthrough edges unpinned
  • packages/web-shell/client/App.test.tsx:18936 — [review] 'observes MCP config reconciliation and reports a timeout' cannot discriminate its two paths
  • packages/web-shell/client/App.test.tsx:19148 — [review] message-side reload live/epoch gate unpinned
  • packages/web-shell/client/daemon/workspace/actions.ts:356 — [review] loadMcpTools rejection blanks AgentCreatePage's catalog (Promise.all)
  • packages/cli/src/serve/workspace-runtime-coordinator.test.ts:392 — [review] 'abandons stale MCP preparation' test cannot discriminate the abandonment
  • packages/web-shell/client/App.test.tsx:19352 — [review] manager-side freshness-gate conjuncts both violated by one fixture
  • packages/cli/src/serve/workspace-qualified-rest.test.ts:1264 — [probe] config/mcp toggle changed:true branch never exercised (persist / publish / exact-name exemption)
  • packages/web-shell/client/App.test.tsx:18582 — [probe] refreshAll's observeDiscovery half unpinned
  • packages/web-shell/client/App.test.tsx:19150 — [probe] rewritten poll loop bound pinned only at >=2
  • packages/web-shell/client/App.test.tsx:19300 — [probe] init-effect epoch-mismatch fall-through untested
中文说明

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

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

未审查:build-and-test — the test phase did not run (the harness budget expired before the test phase after 16/17 workspaces built cleanly; the coordinator suite 26/26 and useDaemonMcp 2/2 were run green by review agents)。

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

未探索到全部深度(达到工具调用预算):chunk 5:executing packages/cli/src/serve/workspace-runtime-coordinator.test.ts under vitest to confirm the manual traces — the review worktree has no node_modules , …

未审查:反向审计——在 5 轮的反审轮数上限内未收敛。

收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 14 条(原文未翻译,列表见上方英文部分)。

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

Comment thread packages/cli/src/serve/routes/workspace-runtime-mcp.ts
Comment thread packages/cli/src/serve/routes/workspace-runtime-mcp.ts Outdated
Comment thread packages/cli/src/serve/workspace-runtime-coordinator.ts
Comment thread packages/cli/src/serve/workspace-runtime-coordinator.ts Outdated
Comment thread packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.test.tsx Outdated
}
});

it('rejects an MCP mutation completed by a closed generation', 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] R2-10: The post-completion generation-close rejection added by the R1-5 fix (503 workspace_runtime_unavailable) is tested only for /runtime/mcp/:server/restart; the identical runtime.generationGuard?.assertOpen() guard in the sibling reload, approve, authenticate, and clear-auth handlers of routes/workspace-runtime-mcp.ts is exercised by no test. A refactor dropping assertOpen from (say) the reload handler ships green: a reload that finishes after the runtime generation was replaced — a policy change closing the guard mid-request, exactly as this test models for restart — returns 200 with the superseded generation's result instead of the retryable 503, and the client commits MCP state from a runtime that is no longer the active generation.

Witness:

Scratch-tree probe: mutant removing `runtime.generationGuard?.assertOpen();` from the reload handler
  workspace-qualified-rest.test.ts -> Test Files 1 passed, Tests 45 passed (45)   (baseline also 45/45)

Parametrize 'rejects an MCP mutation completed by a closed generation' over the remaining runtime mutation routes (POST /runtime/mcp/reload, /runtime/mcp/:server/approve, /runtime/mcp/:server/authenticate, /runtime/mcp/:server/clear-auth), closing the guard inside each mocked mutation function (bridge.reloadWorkspaceMcp / bridge.manageMcpServer). Each added case must go red (503 → 200) when the assertOpen line is removed from the corresponding handler; the existing restart case pins the expected 503/code shape they should share.

中文说明

R1-5 修复新增的「完成后 generation 关闭 → 拒绝」行为(503 workspace_runtime_unavailable)只对 /runtime/mcp/:server/restart 有测试;routes/workspace-runtime-mcp.ts 中同族的 reload、approve、authenticate、clear-auth 处理器里相同的 runtime.generationGuard?.assertOpen() 守卫没有任何测试覆盖。若删除(例如)reload 处理器中的 assertOpen,测试仍全绿:在 runtime generation 被替换后才完成的 reload(策略变更在请求中途关闭 guard,正如本测试为 restart 建模的场景)会带着已被取代 generation 的结果返回 200 而非可重试的 503,客户端会提交来自非活跃 generation 的 MCP 状态。(修复建议与见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Verified the current production path and found no present behavior defect. This item requires broader coverage or a cross-path refactor rather than a small correctness fix, so I am intentionally leaving it unresolved to avoid expanding this already mature PR; it is suitable for focused follow-up work.

Comment on lines +1324 to +1327
h.workspaceRegistry.beginReplacement(
h.workspaceRegistry.primaryEntry,
'policy-2',
);

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] R2-11: The user-scope reconcile fan-out's trust filter — registry.listManaged().filter((runtime) => runtime.trusted) in affectedRuntimes (workspace-mcp-config.ts:87) — is pinned by no test. 'reconciles user MCP configuration across managed workspaces' uses two trusted runtimes, and the untrusted-primary test closes that primary's generation guard via beginReplacement, which masks the mutant: the queued reload dies at assertAcceptingWork on the closed guard before ever reaching reloadWorkspaceMcp. Deleting the filter keeps the whole suite green, yet a user-scope MCP config write would then schedule reconcileMcpConfiguration() on live untrusted managed runtimes (workspace-trust-reconciler.ts:150 builds trusted:false runtimes that remain in listManaged()), calling reloadWorkspaceMcp so an ungranted workspace applies and starts configured MCP servers.

Witness:

Scratch-tree probe: mutant deleting `.filter((runtime) => runtime.trusted)`
  workspace-qualified-rest.test.ts -> Test Files 1 passed, Tests 45 passed (45)   <- same as baseline

Add a user-scope write case with a live untrusted runtime whose generation guard is still open (e.g. makeHarness({ secondaryTrusted: false }) without beginReplacement) and assert that runtime's bridge.reloadWorkspaceMcp is not called while trusted runtimes still get settings_changed/reconcile. The untrusted runtime used for the assertion must have an open guard: queued MCP work calls assertAcceptingWork before the body (workspace-runtime-coordinator.ts:410), and a closed guard — as beginReplacement produces (workspace-registry.ts:419, entry.current.guard.close()) — rejects the reload regardless of the filter, which is exactly what masks the mutant today. The new assertion must go red when the filter is removed from affectedRuntimes.

中文说明

user-scope reconcile 扇出的信任过滤——affectedRuntimes 中的 registry.listManaged().filter((runtime) => runtime.trusted)(workspace-mcp-config.ts:87)——没有任何测试钉住。'reconciles user MCP configuration across managed workspaces' 使用两个受信任 runtime,而 untrusted-primary 测试通过 beginReplacement 关闭了主 runtime 的 generation guard,恰好掩盖了突变体:排队的 reload 会在关闭的 guard 上经 assertAcceptingWork 被拒绝,根本到不了 reloadWorkspaceMcp。删除该过滤整个套件仍绿,但 user-scope MCP 配置写入将会对存活的、未受信任的受管 runtime 调度 reconcileMcpConfiguration()(workspace-trust-reconciler.ts:150 会构建仍留在 listManaged() 中的 trusted:false runtime),调用 reloadWorkspaceMcp,使未授权的 workspace 应用并启动已配置的 MCP 服务器。(修复建议、约束与见证见英文部分。)

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Verified the current production path and found no present behavior defect. This item requires broader coverage or a cross-path refactor rather than a small correctness fix, so I am intentionally leaving it unresolved to avoid expanding this already mature PR; it is suitable for focused follow-up work.

Comment thread packages/cli/src/serve/workspace-runtime-coordinator.test.ts
Comment thread packages/cli/src/serve/workspace-runtime-coordinator.test.ts Outdated
Comment thread packages/web-shell/client/components/messages/McpStatusMessage.tsx
@wenshao

wenshao commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Local verification report — head 3b936af2d7

The PR description says browser evidence is N/A, so I built the missing half: a real qwen serve daemon from this branch driven through a real Chrome, plus wire-level probes against the new endpoints. Everything below was reproduced on macOS from source at 3b936af2d7.

Rig (click to expand)
  • npm run build at 3b936af2d7; daemon started as
    node packages/cli/dist/index.js serve --port 4171 --hostname 127.0.0.1 --workspace <ws-a> --workspace <ws-b>
    (banner confirms Web Shell UI served from …/packages/web-shell/dist).
  • Isolated HOME; trustedFolders.json trusts both workspaces; ws-a/ws-b are separate git repos.
  • MCP servers: user-echo (user scope, stdio), ws-a-only / ws-b-only (workspace scope, stdio) — ~100-line JSON-RPC echo servers; oauth-demo (httpUrl + oauth.enabled) added later through the UI.
  • Model: local fake OpenAI-compatible server that logs the exact tools[] array of every request, so "which tools does this session advertise" is observed on the wire, not inferred.
  • tools.toolSearch.enabled=false so MCP tools are declared eagerly and visible in that array.
  • Every claim below is cross-checked against the daemon's own request log.

Reviewer Test Plan — all five items reproduced

1. Cold start, no hidden session — confirmed. With GET /workspaces/{ws-a,ws-b}/sessions both [], opening Plugins → MCP produced exactly:

02:07:15  POST   /workspaces/ws-a/runtime/ensure  -> 200 (2218ms)
02:07:15  GET    /workspaces/ws-a/runtime/status  -> 200 (0ms)
02:07:15  GET    /workspaces/ws-a/runtime/mcp     -> 200 (2ms)

No POST /session anywhere in the window, and both workspaces still reported {"sessions":[]} afterwards. The 2.2 s is the cold-start cost called out in Risk & Scope — real, and paid once.

2. Two workspaces, isolation, locked detail page — confirmed. The selector lists both trusted workspaces; switching to ws-b issued its own runtime/ensure (1201 ms) and never touched a /workspace/... primary-pinned route. The two runtimes carried distinct epochs (ws-a epoch 2, ws-b epoch 1), and approving ws-b-only left ws-a-only untouched:

ws-a  epoch=2 source=live   user-echo connected | ws-a-only disconnected approval=pending
ws-b  epoch=1 source=live   user-echo connected | ws-b-only connected

On the server detail page the control is still rendered with the workspace name and disabled === true (DOM-checked, not just visually).

3. Config CRUD + live tool refresh in a running session — confirmed, and this is the strongest result.
Scope routing is exact: PUT /workspaces/ws-a/config/mcp/servers/oauth-demo changed only ws-a/.qwen/settings.json; DELETE /workspaces/ws-b/config/mcp/servers/cold-write changed only ws-b/.qwen/settings.json; the user-scope file was never rewritten by either.

For the "already-running session receives updated MCP tools" claim I watched the actual tools[] the model receives, in one session that was never recreated (one session spawned line in the daemon log, same sessionId on both prompts):

turn 1              total=35  mcp=[user-echo__user_echo, user-echo__user_ping,
                                   ws-a-only__wsaonly_echo, ws-a-only__wsaonly_ping]
POST /workspaces/ws-a/config/mcp/user-echo/disable -> {"activation":"reconciling"}
turn 2 (same sid)   total=33  mcp=[ws-a-only__wsaonly_echo, ws-a-only__wsaonly_ping]

The reverse direction works too: approving ws-a-only mid-session took the next turn from 33 → 35 tools. Writing config against a cold runtime is durable and correctly deferred — PUT …/ws-b/config/mcp/servers/cold-write returned {"activation":"deferred"}, landed on disk, left state=cold, runtimeLive=false, and reconciled once ws-b came up.

4. Approval / authentication precedence — confirmed. Two servers in the same list, both mcpStatus=disconnected, render as Needs approval (approvalState=pending) and Needs authentication (requiresAuth=true) rather than "Disconnected". See E1 below for the A/B.

5. Runtime replacement / epoch guard — confirmed on a real killed ACP child. Polling runtime/status at 200 ms while kill -9-ing the child:

ready@e4  ->  (child killed)  stale@e4  ->  (ensure)  starting@e5  ->  ready@e5

MCP never reports ready on the old epoch, and the new runtime goes through live discovery (starting) before it does.

Standing CHANGES_REQUESTED findings, re-checked at this head

finding status at 3b936af
R1-1 unstable hook identity loops AgentCreatePage not reproducible. Opening Agents → Create issued 6 daemon requests in 20 s; its MCP tab then issued 0 in the following 25 s. useDaemonMcp now returns a useMemo'd api object.
R1-5 mutation routes never re-assert the generation guard fixed. runtime.generationGuard?.assertOpen() after every await (3 sites); removing all three fails a test.
R1-6 (fix-induced) reserved JS property names reach the handlers fixed. __proto__ / constructor / prototype400 invalid_server_name; dotted names like foo-bar.io are admitted and reach the handler.
R2-1 generic 500 instead of 501 workspace_runtime_not_supported fixed in code (requireRuntimeCoordinator → 501). Not reachable at runtime here — every bridge in a live daemon implements the lifecycle snapshot — so this one is code-read + unit pins only.
R2-2 preheat failures escape as an uncoded 500 fixed, verified live. Cold ws-b with its cwd made unavailable → POST /workspaces/ws-b/runtime/mcp/reload returns 503 + Retry-After: 5 + {"code":"runtime_initialization_failed"}.

Validation matrix on the new routes is clean: unknown workspace 400 workspace_mismatch, forceReconnectAll:"yes" 400, forceReconnectAll+forceReconnectWhich 400 conflicting_force_reconnect_options, entryIndex=-1 400. Legacy surfaces (GET /workspace/mcp, GET /workspace/tools, POST /workspace/mcp/initialize, POST /workspace/runtime/mcp/reload) all still answer.

Mutation matrix — 8/11 killed, and the 3 survivors are all the same guard

Baseline at head: cli 1087/1087, web-shell 688/688.

# mutation result
M1 drop the setTools() refresh after reinitializeMcpServers killedacpAgent.test.ts › T2.8 › initializes discovery without reloading sessions…
M2 drop all three post-await generationGuard.assertOpen() killed
M3 drop the reserved-JS-name rejection killedworkspace-qualified-rest.test.ts
M4 drop current.runtimeEpoch !== epoch from the poll-loop stale guard (coordinator ~L349) survived
M5 drop the re-queued prepare after a rejected mutation killedrechecks MCP readiness after a rejected runtime mutation
M6 drop mcpReconcileDeferred = true on the drain-interrupted path killedreplays MCP reconciliation interrupted while draining
M7 render the detail-page workspace control enabled killedshows the multi-workspace selector and locks it in MCP detail
M8 drop the requiresAuth branch from statusLabel killedprioritizes MCP approval and authentication labels over connection labels
M9 drop current.runtimeEpoch !== epoch from the post-loop guard (~L392) survived
M10 drop this.mcpStatus.runtimeEpoch !== snapshot.runtimeEpoch from status() (~L120) survived

The one thing I'd like fixed before merge (Suggestion, not a blocker): the three survivors are exactly the runtime-epoch half of the guard the PR headlines — "stale responses from a replaced ACP child cannot overwrite the current state". Every existing epoch test either holds the epoch constant while flipping runtimeLive (marks MCP stale when its runtime stops **without changing epoch**) or varies the reported MCP status epoch, which is a different, well-covered guard.

M10 is load-bearing, not an equivalent mutant — I built it and drove it against the real daemon:

                                                      HEAD                    M10 mutant
1) ensure                       live=T e1   mcp=ready@e1            mcp=ready@e1
2) kill ACP child               live=F e1   mcp=stale@e1            mcp=stale@e1
3) POST /session (new runtime, no MCP discovery)
4) status                       live=T e2   mcp=stale@e1   <-- ok   mcp=ready@e1   <-- false ready

A runtime that never ran MCP discovery is reported ready, and nothing in 1087 tests notices. One test per guard — snapshot live at a bumped epoch, assert stale — would close it.

Pre-existing, out of scope, but adjacent

POST /workspace/mcp/__proto__/clear-auth on the legacy route returns 200 {"ok":true,"changed":true} for a server that does not exist — the prototype-chain bypass R1-6 described. workspace-mcp-control.ts is untouched by this PR and the new routes reject it correctly, so this is not a regression; it's a one-line follow-up now that validateRuntimeServerName exists.

Not covered here

  • Real third-party OAuth callback, and therefore the "one process-wide OAuth flow at a time" admission (item 4, second half) — the release paths are only unit-pinned.
  • R2-1's 501 branch (not reachable against a live daemon).
  • Windows / Linux.

Verdict

Every claim in the Reviewer Test Plan reproduces on a real stack, all five standing Criticals are either fixed or not reproducible at this head, and the UI change is a clean improvement over the merge-base bundle. From my side this is good to merge; the epoch-guard test gap is worth a follow-up commit here or a follow-up PR, your call.

Evidence

E1 — same rig, same daemon state, two bundles. The merge-base bundle has no workspace selector and labels the requiresAuth server "Disconnected"; the PR-head bundle adds the selector and reads "Needs authentication". Bundle hashes recorded per arm (index-C6Hdj8xg.js vs index-VCf3Oqfj.js) so the comparison can't be a stale-cache artifact.

E1 before/after

E2 — workspace-scoped configuration and live status.

E2 workspace isolation

E3 — selector, and the detail page where the workspace is visible but locked.

E3 selector and detail lock

E4 — cold start: servers loaded, both workspaces still at zero sessions.

E4 cold start

中文说明

本地验证报告 —— head 3b936af2d7

PR 描述里「证据(前后对比)」写的是 N/A,所以我把缺的那一半补上了:用本分支源码构建的真实 qwen serve daemon + 真实 Chrome,并对新接口做了链路级探针。以下结论全部在 macOS 上于 3b936af2d7 复现。

验证台npm run build 后用 node packages/cli/dist/index.js serve --port 4171 --hostname 127.0.0.1 --workspace <ws-a> --workspace <ws-b> 启动(启动横幅确认 Web Shell 来自 packages/web-shell/dist);隔离 HOMEtrustedFolders.json 信任两个 workspace;MCP 服务器为自写 stdio echo server(user-echo 用户级、ws-a-only/ws-b-only workspace 级),oauth-demohttpUrl + oauth.enabled)是从 UI 里加的;模型是本地假 OpenAI 服务,会把每次请求的 tools[] 原样落盘,所以「这个会话到底带了哪些工具」是抓出来的而不是推出来的;设 tools.toolSearch.enabled=false 让 MCP 工具前置声明。所有结论都与 daemon 自己的请求日志交叉核对。

Reviewer 测试计划 —— 五项全部复现

1. 冷启动不产生隐藏 session —— 成立。 两个 workspace 的 sessions 都是 [] 时打开「插件 → MCP」,链路上只有 POST /workspaces/ws-a/runtime/ensure(2218ms)+ runtime/status + runtime/mcp,整个窗口没有任何 POST /session,事后两个 workspace 仍是 {"sessions":[]}。2.2 秒就是「风险与范围」里说的冷启动开销,真实存在,且只付一次。

2. 双 workspace 隔离 + 详情页只读 —— 成立。 选择器列出两个受信任 workspace;切到 ws-b 会自己发 runtime/ensure(1201ms),完全不碰 /workspace/... 这类绑定 primary 的路由。两个 runtime 的 epoch 各自独立(ws-a=2、ws-b=1);在 ws-b 批准 ws-b-only 后,ws-a 的 ws-a-only 仍是 approval=pending。详情页里 workspace 控件仍显示(值为 ws-b)但 disabled === true(读 DOM 确认,不只是看着灰)。

3. 配置 CRUD + 运行中 session 的工具热刷新 —— 成立,且这是最强的一条。 作用域路由精确:PUT /workspaces/ws-a/config/mcp/servers/oauth-demo 只改了 ws-a/.qwen/settings.jsonDELETE /workspaces/ws-b/config/mcp/servers/cold-write 只改了 ws-b/.qwen/settings.json;用户级文件两次都没被重写。

「已运行 session 获得更新后的 MCP tools」这条,我盯的是模型真正收到的 tools[],而且全程同一个 session(daemon 日志里只有一条 session spawned,两轮 prompt 的 sessionId 相同):第 1 轮 35 个工具(user-echo ×2 + ws-a-only ×2)→ POST …/config/mcp/user-echo/disable(返回 activation: reconciling)→ 同一 session 第 2 轮 33 个工具,user-echo 的两个消失。反方向也成立:会话中途批准 ws-a-only,下一轮从 33 涨到 35。冷 runtime 下写配置是持久且延后的:PUT …/ws-b/config/mcp/servers/cold-write 返回 {"activation":"deferred"},落盘成功,runtime 仍是 cold / runtimeLive=false,等 ws-b 起来后自动收敛。

4. 审批/认证状态优先于连接状态 —— 成立。 同一列表里两个 mcpStatus=disconnected 的服务器分别显示 Needs approvalapprovalState=pending)和 Needs authenticationrequiresAuth=true),而不是「已断开」。对比见下方 E1。

5. runtime 替换 / epoch 守卫 —— 用真实 kill -9 ACP 子进程验证成立。 200ms 间隔轮询 runtime/statusready@e4 →(杀子进程)stale@e4 →(ensure)starting@e5ready@e5。旧 epoch 上永远不会报 ready,新 runtime 必须走完 live discovery(经过 starting)才 ready。

现存 CHANGES_REQUESTED 条目在本 head 的复核

  • R1-1(hook 方法引用不稳定导致 AgentCreatePage 循环):复现不出来。打开「Agents → Create」20 秒内共 6 个请求,再进它的 MCP 页后续 25 秒 0 个请求;useDaemonMcp 现在返回 useMemo 过的 api 对象。
  • R1-5(mutation 路由 await 后不复检 generation guard):已修,三处 await 后都有 runtime.generationGuard?.assertOpen(),三处一起删会挂测试。
  • R1-6(修复引入)(保留 JS 属性名进到 handler):已修__proto__/constructor/prototype 一律 400 invalid_server_name,而 foo-bar.io 这类带点的名字正常放行进 handler。
  • R2-1(该 501 的地方给了泛化 500):代码层面已修requireRuntimeCoordinator 返回 501)。活体 daemon 里所有 bridge 都实现了生命周期快照,这条走不到,只能靠读码 + 单测钉住。
  • R2-2(preheat 失败逃逸成无 code 的 500):已修,且活体验证。把冷状态 ws-b 的 cwd 弄没,POST /workspaces/ws-b/runtime/mcp/reload 返回 503 + Retry-After: 5 + {"code":"runtime_initialization_failed"}

新路由的参数校验矩阵干净:未知 workspace 400 workspace_mismatchforceReconnectAll:"yes" 400、两个 reconnect 选项同时给 400 conflicting_force_reconnect_optionsentryIndex=-1 400。旧接口(GET /workspace/mcpGET /workspace/toolsPOST /workspace/mcp/initializePOST /workspace/runtime/mcp/reload)全部仍然可用。

变异矩阵 —— 11 个变异体杀掉 8 个,存活的 3 个是同一个守卫

基线:cli 1087/1087,web-shell 688/688。杀掉的:M1(删掉 reinitializeMcpServers 后的 setTools() 刷新)、M2(删掉三处 await 后的 assertOpen())、M3(删掉保留属性名拒绝)、M5(删掉 mutation 被拒后的重排 prepare)、M6(删掉 drain 中断路径的 mcpReconcileDeferred = true)、M7(详情页 workspace 控件改成可用)、M8(删掉 statusLabelrequiresAuth 分支)。

存活的 3 个(M4/M9/M10)全部是 runtime-epoch 守卫:poll 循环内(~L349)、循环后(~L392)、以及 status() 投影(~L120)各删掉 runtimeEpoch 比较,1087 个测试全绿。这正是 PR 主打的那句「被替换 ACP 子进程的迟到响应无法覆盖当前状态」。现有的 epoch 测试要么保持 epoch 不变只翻 runtimeLive(用例名就叫 marks MCP stale when its runtime stops **without changing epoch**),要么变的是 上报的 MCP status 的 epoch —— 那是另一个守卫,且已被覆盖。

M10 不是等价变异体,我把它打包成真 daemon 跑过:ensure 得到 ready@e1 → 杀 ACP 子进程得到 stale@e1POST /session 起一个没有做过任何 MCP discovery 的新 runtime → 此时 HEAD 报 live=T e2, mcp=stale@e1(正确),M10 报 live=T e2, mcp=ready@e1假 ready)。每个守卫补一条「快照 live 但 epoch 已推进 → 断言 stale」的用例即可闭合。这是 Suggestion,不是阻塞项。

既有问题(不在本 PR 范围,但相邻)

旧路由 POST /workspace/mcp/__proto__/clear-auth 对一个根本不存在的服务器返回 200 {"ok":true,"changed":true} —— 就是 R1-6 描述的原型链绕过。workspace-mcp-control.ts 本 PR 没动,新路由也拒绝得很正确,所以这不是回归;但既然 validateRuntimeServerName 已经有了,这是个一行的后续。

未覆盖

真实第三方 OAuth 回调,以及随之而来的「进程级 OAuth 同一时间只允许一个」(测试计划第 4 项的后半,释放路径只有单测钉住);R2-1 的 501 分支(活体 daemon 走不到);Windows / Linux。

结论

Reviewer 测试计划的五项在真实栈上全部复现,五条现存 Critical 在本 head 上要么已修要么复现不出来,UI 相对 merge-base 是干净的改进。我这边认为可以合入;epoch 守卫的测试缺口值得补一个 commit 或另开 PR,由你定。


Verified locally by the maintainer against a real daemon + real browser at head 3b936af2d7. Evidence images: assets-pr10679.

@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. Suggestions are inline.

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

  • DELETE no-op config mutations churn — already reported as R1-8 (comment 3905016888)
  • User-scope reconcile fan-out unpinned — already reported as R2-11 (comment 3908958546)
  • Vacuous label-priority assertions — already reported as R1-16 (comment 3905016952)
  • Enable activation aggregation masks 'deferred' — already reported as R1-10 (comment 3905016995)
  • Multi-scope enable commits without rollback — already reported as R1-9 (comment 3905016986)
  • Poll loop has no unmount guard — already reported as R1-13 (comment 3905017012)
  • OAuth error-path release unwitnessed — already reported as R1-18 (comment 3905016869)
  • prepareMcpRevision not_started/errors branches unwitnessed — already recorded in the round-2 deferred list (workspace-runtime-coordinator.ts:343, review 5083664753)
  • Epoch-mismatch stale projection unwitnessed — already recorded in the round-2 deferred list (workspace-runtime-coordinator.test.ts:121, review 5083664753)
  • Dead-string 'first startup' negative assertion — already reported as R1-21 (comment 3905016968)
  • enable/disable branch lacks the observeDiscovery .catch — already reported as R1-14 (comment 3905017000)
  • Epoch stamp applied at response arrival — already discussed in round-1 review 5074892799 finding 3 and @chiga0's cross-check R3

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

Not reviewed: reverse audit — stopped at the 3-round huge-diff cap without two consecutive dry rounds; round 3 re-derived mostly already-reported items.

Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:

  • packages/cli/src/serve/routes/workspace-mcp-config.ts:156 — [probe] config routes reject dotted server names the runtime routes accept
  • packages/acp-bridge/src/bridge.ts:6062 — [probe] same-epoch merge branch of mergeManagedWorkspaceMcpStatus unpinned
  • packages/web-shell/client/components/mcp/McpManagerPage.tsx:741 — [review] add/edit/remove dialog flows have zero test coverage
  • packages/web-shell/client/daemon/workspace/actions.ts:401 — [review] enable/disable no longer bumps mcpVersion (concurrent windows go stale)
  • packages/cli/src/serve/workspace-runtime-coordinator.ts:267 — [probe] deferred-flag asymmetry on repair-prepare and ensure drain-rejection paths
  • packages/cli/src/serve/routes/workspace-mcp-config.ts:355 — [probe] disable appends exact name ignoring covering glob pattern
  • packages/cli/src/serve/workspace-qualified-rest.test.ts:1362 — [probe] persistSetting scope asserted as expect.any(String)
  • packages/web-shell/client/components/mcp/McpManagerPage.tsx:633 — [review] stale tools/resources cache across runtime epoch bumps
  • packages/web-shell/client/components/mcp/McpManagerPage.tsx:623 — [review] mcp.discovery.timeout reused as generic sentinel
  • packages/cli/src/serve/workspace-qualified-rest.test.ts:1385 — [probe] unchanged-enable test omits the persist-not-called pin
  • packages/cli/src/serve/workspace-runtime-coordinator.ts:104 — [probe] background MCP prep holds the removal busy gate up to 2 minutes
  • packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.ts:70 — [probe] named action-timeout messages lost on the workspaceClient branch

Convergence: round 3 posted 2 inline comment(s), 2 of them reported for the first time; the previous round posted 15 (15 new). Findings keep coming back to the same files: packages/cli/src/serve/workspace-runtime-coordinator.test.ts (findings in round 2; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)

中文说明

仅完成部分审查,审查缺口已披露。 建议见行内评论。

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

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

未审查:reverse audit — stopped at the 3-round huge-diff cap without two consecutive dry rounds; round 3 re-derived mostly already-reported items。

收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 12 条(原文未翻译,列表见上方英文部分)。

收敛情况:第 3 轮发布了 2 条行内评论,其中 2 条是首次提出;上一轮发布了 15 条(其中 15 条首次提出)。发现反复回到同一批文件:packages/cli/src/serve/workspace-runtime-coordinator.test.ts(第 2 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)

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

Comment on lines +457 to +459
await Promise.resolve();
await Promise.resolve();
expect(mutationStarted).toHaveBeenCalledOnce();

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] R3-1: The drain-race repair test ('repairs a queued MCP mutation rejected by a drain race') synchronizes by counting raw microtask yields instead of polling for the intended state, coupling the test to the implementation's exact await schedule. The mutation body starts precisely two microtask hops after runMcpRuntimeMutation (the .catch reaction on the resolved physical tail, then the .then body), and the post-cancelDrain replay sets ready on the last microtask before the test's fourth bare yield — so any behavior-preserving change that adds a single await hop in queueMcpWork, runMcpRuntimeMutation, or prepareMcpRevision makes this test fail spuriously: a red CI run and debugging effort over an innocent refactor. The final commit 3b936af introduced this shape by replacing the prior vi.waitFor synchronization.

Witness:

Mutation probe (scratch tree): adding one behavior-preserving hop (await Promise.resolve(); as the first line of queueMcpWork's .then body) fails the test at line 459 — AssertionError: expected "spy" to be called once, but got 0 times. Pristine baseline for the same file: Tests 29 passed (29).

Replace the bare yields with the polling pattern the neighboring tests already use, at both synchronization sites:

await vi.waitFor(() => expect(mutationStarted).toHaveBeenCalledOnce());
// ... beginDrain / releaseMutation / cancelDrain ...
await vi.waitFor(() => {
  expect(harness.reloadWorkspaceMcp).toHaveBeenCalledOnce();
  expect(coordinator.status().capabilities?.mcp?.state).toBe('ready');
});

The rewrite must keep discriminating the drain-race repair itself: queued mutation rejected with WorkspaceDrainingError, capabilities.mcp 'stale', then cancelDrain() replays — reloadWorkspaceMcp called once and state 'ready' (packages/cli/src/serve/workspace-runtime-coordinator.test.ts:443-481). The rewritten test must stay green when a behavior-preserving await Promise.resolve() hop is added inside queueMcpWork's chain, which the current bare-yield version fails — please confirm the new shape passes that mutation.

中文说明

drain 竞态修复测试('repairs a queued MCP mutation rejected by a drain race')通过计数裸微任务让出来同步,而不是轮询目标状态,使测试与实现的精确 await 调度耦合。mutation body 恰好在 runMcpRuntimeMutation 之后两个微任务跳开始(已解析物理尾上的 .catch 反应,然后是 .then body),cancelDrain 后的重放在测试第四次裸让出的前一个微任务上置位 ready——因此,任何在 queueMcpWork、runMcpRuntimeMutation 或 prepareMcpRevision 中增加单个 await 跳的行为保持型改动都会让该测试虚假失败:一次无辜的重构带来红色 CI 与排查成本。最终提交 3b936af 用这种形状替换了原先的 vi.waitFor 同步。(Witness 证据见英文部分。)建议将两处同步点的裸让出替换为相邻测试已在使用的 vi.waitFor 轮询。改写必须继续区分 drain 竞态修复本身:排队的 mutation 以 WorkspaceDrainingError 被拒绝、capabilities.mcp 为 'stale',随后 cancelDrain() 重放——reloadWorkspaceMcp 被调用一次且状态回到 'ready'(packages/cli/src/serve/workspace-runtime-coordinator.test.ts:443-481)。改写后的测试在 queueMcpWork 链中加入行为保持的 await 跳时必须保持绿色(当前裸让出版本在同一变异下会失败)——请确认新形状能通过该变异。

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

Comment on lines +459 to +461
await actions.manageMcpServer('docs', 'disable');

expect(setUserMcpServerEnabled).toHaveBeenCalledWith('docs', 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.

[Suggestion] R2-5: (fix-induced) The disable-scope regression test added for R2-5 ('disables a user MCP server in user scope') pins only the user-only cell of the scope-selection matrix; the workspace-only and both-scopes cells remain unpinned in actions.test.ts, useDaemonMcp.test.tsx (which pins only the user-only 'constructor' prototype case), and App.test.tsx (manageServer mocked). Dropping the !Object.hasOwn(config!.workspace, serverName) conjunct from the disable condition in actions.ts and useDaemonMcp.ts keeps the whole suite green, and disabling a workspace-defined server — or one defined in both scopes — then routes the exclusion to USER scope via setUserMcpServerEnabled, whose publishConfiguration broadcasts to every managed runtime: the server is disabled in all workspaces instead of only the selected one, contradicting the design doc's 'Disabling project or workspace servers writes the exclusion to workspace-local settings'.

Witness:

Scratch-tree mutant: dropping the !Object.hasOwn(config!.workspace, ...) conjunct in both files keeps the existing suite green — Test Files 2 passed (2), Tests 24 passed (24). Both-scopes probe test (mcpConfig() resolving {user: {docs}, workspace: {docs}}, disable) FAILS under the mutant — setMcpServerEnabled (workspace) called 0 times — and passes on intact code: Tests 25 passed (25).

Add disable tests for the remaining matrix cells beside the current one: one with mcpConfig() resolving { user: {}, workspace: { docs: { command: 'docs' } } } asserting setMcpServerEnabled('docs', false) is called and setUserMcpServerEnabled is not, and one with both scopes defined asserting workspace scope wins.

The enable-side ordering is already pinned via invocationCallOrder by actions.test.ts 'enables an MCP server in every disabled scope' (packages/web-shell/client/daemon/workspace/actions.test.ts:400-435) — the new tests must not disturb that user-before-workspace expectation. The new workspace-only and both-scopes disable tests must go red when the !Object.hasOwn(config!.workspace, serverName) conjunct is dropped from the disable condition — please remove the conjunct and run the new tests to confirm.

中文说明

(fix-induced)为 R2-5 新增的 disable 作用域回归测试('disables a user MCP server in user scope')只钉住了作用域选择矩阵中「仅 user」一格;actions.test.ts、useDaemonMcp.test.tsx(只钉住 user-only 的 'constructor' 原型用例)与 App.test.tsx(manageServer 被 mock)中「仅 workspace」与「双作用域」两格均无测试。若删除 actions.ts 与 useDaemonMcp.ts 中 disable 条件里的 !Object.hasOwn(config!.workspace, serverName) 合取项,整个测试套件仍为绿色;此时禁用一个在 workspace 作用域定义(或在两个作用域都定义)的服务器会把排除项写入 USER 作用域(setUserMcpServerEnabled),其 publishConfiguration 会广播到所有受管 runtime——服务器将在所有 workspace 被禁用,而不是只在所选 workspace,与设计文档「禁用项目或 workspace 服务器时将排除项写入 workspace 本地设置」相悖。(Witness 证据见英文部分。)建议在当前用例旁补齐其余矩阵格的 disable 测试:mcpConfig() 解析为 { user: {}, workspace: { docs: { command: 'docs' } } } 时断言调用 setMcpServerEnabled('docs', false) 且未调用 setUserMcpServerEnabled;再补一个双作用域定义时断言选择 workspace 作用域的用例。enable 侧顺序已由 actions.test.ts 'enables an MCP server in every disabled scope' 通过 invocationCallOrder 钉住(packages/web-shell/client/daemon/workspace/actions.test.ts:400-435),新测试不得破坏该 user-before-workspace 预期。删除上述合取项时新增的两个用例必须变红——请移除合取项并运行新测试确认。

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

@wenshao

wenshao commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao
wenshao enabled auto-merge September 2, 2026 11:56
@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 2, 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: 64 passed · 0 failed · 64 total

Flakiness gate: ⚠️ timeout — the 15-minute budget elapsed before two full rounds completed (1 done) — no flakiness signal either way

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

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

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

抖动门:⚠️ timeout — the 15-minute budget elapsed before two full rounds completed (1 done) — no flakiness signal either way

Verification report

PR 10679 Deep Verification — feat(serve): add workspace-scoped MCP management

Verdict: merge-ready — 64/64 scripted assertions passed (wire harness 49, A/B arms 4+4, mutation matrix 4, restore checks 3). No blocking findings.
Verified head: 3b936af2d75be78762578f5ab0f83d8ef3b3a352 (merge commit 96465a3, base tip 3e8d92a).

中文摘要

结论:merge-ready 64/64 脚本化断言通过,未发现阻塞性问题。

  • A/B 结论:同一探针(epoch 替换后 capability 必须投影为 stale、冷启动写入必须 deferred)在 HEAD 上 4/4 绿、在 base(3e8d92a)上 4/4 红——epoch/revision 就绪投影机制是本 PR 引入的、且为承载性(load-bearing)改动(见 01-ab-coordinator-base-vs-head.png)。
  • Wire 级验证:对真实 express app(真实路由 + 真实 coordinator + 真实 settings 落盘,仅 ACP 子进程一侧为脚本化对端)驱动 49 个 REST 断言全部通过:冷配置写入持久化且 activation:'deferred'ensure 后 ready、runtime 被替换(epoch+1)后 capability 立即投影 stale、reload 后在新 epoch 重新 ready、校验矩阵(保留名/超长名/错误 scope/冲突的 reconnect 选项)全部按 400 拒绝(见 02-wire-harness-head-49-cells.png)。
  • 变异矩阵:4 个核心守卫变异体(coordinator stale 投影、bridge merge 的 epoch 相等条件、OAuth 进程级准入、live epoch/source 打标)全部被既有测试以行为断言杀死;还原后全量测试复绿、工作树零脏文件(见 03-mutation-matrix-4-mutants-killed.png)。
  • 门禁:受影响套件全绿——acp-bridge bridge.test.ts 890/890、cli serve 三文件 116/116、cli run-qwen-serve+acpAgent 971/971、SDK DaemonClient 407/407、web-shell 715/715。
  • Findings:无阻塞项。一条观察:epoch-变更场景由两层守卫共同保护(打标缺失时 epoch-change 测试失败,而 merge 条件缺失时是三个 fresh-child 测试失败),两层均为有效防御、非冗余。
  • 未覆盖:真实 ACP 子进程/模型的 E2E、真实第三方 OAuth 回调、逐提交归因(浅克隆仅 3 个提交可达)、repo 级门禁、integration-tests 车道、Windows 路径。

Scope selection

Central claim (C1): MCP readiness is projected through runtime capability
status with monotonic revisions and runtime epochs, so a stale response from a
replaced ACP child cannot overwrite the current state. C2: durable config
writes while cold (deferred reconciliation), live operations routed to the
selected trusted runtime only. S1: process-wide single-flight OAuth
admission. S2: existing sessions receive refreshed MCP tools without
restart.

1. A/B load-bearing proof of the central claim

An identical self-contained probe (pr10679-ab-probe.test.ts) asserting the
head behavior was run on both arms — 01-ab-coordinator-base-vs-head.png is
the witness of that run:

Arm Tree Observable oracle Result
HEAD (96465a3, PR applied) main checkout probe: 4 scenarios (ready projection, epoch-replacement → stale, runtime-stop → stale, cold write → deferred) 4/4 green (vitest exit 0)
BASE (3e8d92a = HEAD^1) tmp/base-tree worktree identical probe 0/4 — 4 failed on the behavioral assertions (expected undefined to be 'ready'/'stale'/'deferred', vitest exit 1)

The base coordinator returns status with no capabilities at all and has no
reconcileMcpConfiguration; the base failures are assertion mismatches, not
import/setup breakage. Static base cells corroborate: 0 occurrences of
capabilities/reconcileMcpConfiguration in the base coordinator, 0
registrations of the new route modules in base server.ts, and neither route
file exists in base — while head registers 4 route sets and the wire harness
(§2) gets 200s from them.

Realpath hygiene for the base arm: the base worktree resolves
@qwen-code/acp-bridge into the head tree's dist (worktrees share root
node_modules); the only value the base probe consumes across that boundary
is STATUS_SCHEMA_VERSION (= 1 in both base source and head dist), and the
cli vitest config aliases core/acp-bridge modules to each tree's own source,
so the control compares base code against base code. The PR touches no
package.json/package-lock.json (verified in the diff), so dependency
identity holds by construction.

2. Wire-oracle harness (mock-free REST drive of the head build)

wire-harness.mjs boots the real head-build express app
(packages/cli/dist) on loopback with a real WorkspaceRegistry, the real
WorkspaceRuntimeCoordinator, real loadSettings reads, and real on-disk
settings files (workspace-scope <cwd>/.qwen/settings.json, user-scope a
redirected $QWEN_HOME). The only scripted peer is the ACP-child side of the
contract (lifecycle snapshot + MCP discovery view), which the scenario
controls. Witness: 02-wire-harness-head-49-cells.png. 49/49 passed.

Key cells:

# Cell Oracle Result
1 cold runtime status 200, capabilities.mcp.state='not_started', runtimeLive=false PASS
2 PUT config while cold 200 activation:'deferred'; settings.json on disk carries mcpServers.docs; 0 reload calls to the cold runtime PASS (7 asserts)
3 POST runtime/ensure 200, runtimeLive=true, epoch 1, capability ready, initialize sent exactly once PASS (6)
4 runtime replaced (epoch 1→2, fresh child not yet discovered) GET runtime/status → capability stale keeping old epoch 1 while runtime reports epoch 2 PASS (3)
5 POST runtime/mcp/reload after replacement 200; capability ready on the new epoch 2 PASS (3)
6 runtime MCP/tools/resources passthrough 200 + shapes PASS (5)
7 validation matrix wrong scope, __proto__/300-char names, missing config, DELETE w/o scope, non-boolean forceReconnectAll, combined reconnect options → all 400 with stable codes PASS (10)
8 user-scope config write 200, durable in user settings file, read-back via effective merge PASS (4)
9 disable/enable exclusion persisted to / removed from disk PASS (5)
10 DELETE config server removed from disk and read-back PASS (3)

Harness environment notes (harness-side, not PR defects): two early red runs
were my own harness bugs — the SettingScope enum values are
'User'/'Workspace' (not lowercase), and os.homedir()/QWEN_HOME must be
redirected before the Node process boots (this container exports QWEN_HOME
pointing at a live .qwen store; the harness re-execs with both redirected
into scratch so it never touches it). Also: the serve app validates the
Host header against opts.hostname:opts.port, so the port must be fixed
before createServeApp. After those fixes: 49/49 twice in a row.

3. Mutation matrix (vacuity + load-bearing proof of the guards)

Driver: matrix-driver.sh, patches in patches/. Each mutant reverts exactly
one guard hunk; the suite that should catch it runs; then the tree is restored.
Witness: 03-mutation-matrix-4-mutants-killed.png.

Mutant Guard reverted Suite Outcome (failing tests / behavioral assertion)
M1 coordinator status() stale projection → always project raw mcpStatus workspace-runtime-coordinator.test.ts KILLEDmarks MCP stale when its runtime stops without changing epoch (1F/28P)
M2 mergeManagedWorkspaceMcpStatus epoch-equality condition removed bridge.test.ts -t "fresh child" KILLED — 3 tests: does not reuse other MCP states {after disabling, while polling, after restarting} in a fresh child; failing assertion: expected { discoveryState: 'completed' … } to match { discoveryState: 'not_started' … } — the old child's completed/authenticated state is re-served into a fresh child's not_started query
M3 manageMcpServer process-wide OAuth admission gate removed bridge.test.ts -t "MCP authentication" KILLEDserializes MCP authentication across workspace bridges, keeps MCP authentication admission after a timed-out RPC until its channel exits; assertion promise resolved "{ serverName: 'aone' … }" instead of rejecting
M4 live runtimeEpoch/source stamping of workspace-MCP status responses removed bridge.test.ts -t "runtime epoch changes" KILLEDdoes not reuse completed MCP status after the runtime epoch changes
restore unmutated HEAD both full suites bridge 890/890 green, coordinator 29/29 green, git status 0 dirty

Observation (not a defect — layered defense): the epoch-change scenario is
pinned by the stamping hunk (M4), while the merge-condition hunk (M2) is
pinned by the three fresh-child reuse shapes. Each single-hunk revert is
caught by a different test set; neither is redundant.

Harness-capability control: every mutant above was caught by its own suite,
and the same suites are green on unmutated HEAD — so "suite collects the file
and can go red" is proven in both directions; no survivor needed a separate
positive control.

4. Targeted gates (exact counts)

Gate Files Result
acp-bridge bridge.test.ts (full) 890/890 (49s)
cli serve workspace-runtime-coordinator.test.ts (29), workspace-qualified-rest.test.ts + server/error-response.test.ts (87) 116/116
cli (rest) run-qwen-serve.test.ts + acp-integration/acpAgent.test.ts 971/971
sdk-typescript test/unit/DaemonClient.test.ts 407/407
web-shell App.test.tsx, PluginManagerPage.test.tsx, useDaemonMcp.test.tsx, actions.test.ts 696/696
web-shell daemon/workspace/DaemonWorkspaceProvider.test.tsx 19/19
Total 3099/3099

5. Reviewer Test Plan walkthrough (per step)

  1. Daemon without existing session, open MCP, no hidden session — covered: coordinator unit test starts one workspace runtime without creating a session (green at gate), probe test 1, wire cell 3 (ensure spawns no session; sessionCount stays 0). ✔ at route/coordinator level; not exercised against a real ACP child.
  2. Two workspaces, per-workspace config/status, workspace read-only on detail — covered at route level: PR REST tests (persists workspace MCP configuration through the selected workspace route, untrusted-rejection) green; wire harness drives the secondary workspace exclusively; Web Shell read-only workspace selector is component-level (web-shell gate green). ✔
  3. Add/edit/enable/disable/remove persists; running session gets new tools — config half proven end-to-end on disk (wire cells 2/8/9/10). "Session receives updated tools" is the acpAgent.ts llmClient.setTools?.() hunk — pinned by the green acpAgent.test.ts (+85 lines) but not E2E-verified with a real model session. ◐
  4. Reconnect/approve/authenticate; pending beats disconnected; one OAuth flow — single-flight proven by M3 mutation kill + 2 pinned tests; UI precedence is web-shell component coverage. ✔ (OAuth callback itself out of scope, see below)
  5. Replace/reap runtime while polling; old epoch ignored; new runtime must rediscover — exactly A/B probe test 2 and wire cells 4–5 (stale on replacement, ready only after new-epoch discovery). ✔ with scripted peer; real child replacement not driven.

6. Findings

None blocking. No executed assertion failed; no defect survived the
matrix. One informational note (also in the matrix section): the two
bridge-side epoch hunks defend different observable shapes and are both
load-bearing — a future refactor touching either must keep the tests named in
the M2/M4 rows green.

7. Not covered

  • Per-commit attribution: checkout is depth 2 (git rev-parse --is-shallow-repository = true); git rev-list --count HEAD^1..HEAD^2 returns 1 while $QWEN_VERIFY_CONTEXT lists 10 commits, so individual commits are unreachable. The aggregate HEAD^1..HEAD diff is what was verified.
  • Real ACP child / real model E2E: the wire harness scripts the child side of the contract (the wire shape, not the producer). No live qwen serve with a real child process was booted; session-level tool refresh (S2) rests on unit gates only.
  • Real third-party OAuth callbacks (the PR also declares these out of scope): only the admission gate was exercised.
  • Integration-tests lanes, repo-wide npm run lint/typecheck/full suite (CI already covers; targeted suites were run instead).
  • Windows path handling (workspace-runtime-mcp.ts shares the POSIX path semantics with existing routes).
  • SDK/web-shell behavior on the BASE arm (their A/B is subsumed by the route-absence cells: the new SDK methods/Web Shell flows target endpoints that do not exist on base).
  • A replay of the previous-report.md kind does not apply: this is round 1 (no previous report in the context dir).

8. Methodology

Environment: CI node:22-bookworm container, refs/pull/10679/merge at
depth 2 (HEAD^1 = base tip 3e8d92a, HEAD^2 = PR head 3b936af),
npm ci + npm run build pre-run at HEAD. Base arm: git worktree add tmp/base-tree HEAD^1 with dist/node_modules symlinks satisfying the vitest
build-prereq guard (realpath check and the single cross-tree constant quoted
in §1). Harnesses (wire-harness.mjs, ab-driver.sh, matrix-driver.sh,
probe, patches) live in tmp/pr10679-verify-20260902-123846/ alongside raw
gate logs (gate-*.log); evidence images under evidence/. The wire harness
drove the compiled head build over loopback HTTP with fetch; user-scope
settings I/O was sandboxed by redirecting HOME/QWEN_HOME before
interpreter boot. Mutations were applied via saved patches, each reverted and
verified with a full green re-run plus a clean git status.

Flakiness gate log

rounds=5 files=12 skipped=0
file packages/acp-bridge/src/bridge.test.ts: (cd packages/acp-bridge) npx --no-install vitest run ./src/bridge.test.ts
file packages/cli/src/acp-integration/acpAgent.test.ts: (cd packages/cli) npx --no-install vitest run ./src/acp-integration/acpAgent.test.ts
file packages/cli/src/serve/run-qwen-serve.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/run-qwen-serve.test.ts
file packages/cli/src/serve/server/error-response.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/server/error-response.test.ts
file packages/cli/src/serve/workspace-qualified-rest.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/workspace-qualified-rest.test.ts
file packages/cli/src/serve/workspace-runtime-coordinator.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/workspace-runtime-coordinator.test.ts
file packages/sdk-typescript/test/unit/DaemonClient.test.ts: (cd packages/sdk-typescript) npx --no-install vitest run ./test/unit/DaemonClient.test.ts
file packages/web-shell/client/App.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/App.test.tsx
file packages/web-shell/client/components/plugins/PluginManagerPage.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/components/plugins/PluginManagerPage.test.tsx
file packages/web-shell/client/daemon/workspace/DaemonWorkspaceProvider.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/daemon/workspace/DaemonWorkspaceProvider.test.tsx
file packages/web-shell/client/daemon/workspace/actions.test.ts: (cd packages/web-shell) npx --no-install vitest run ./client/daemon/workspace/actions.test.ts
file packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/daemon/workspace/hooks/useDaemonMcp.test.tsx


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  packages/acp-bridge/src/bridge.test.ts: PP
  packages/cli/src/acp-integration/acpAgent.test.ts: PP
  packages/cli/src/serve/run-qwen-serve.test.ts: PP
  packages/cli/src/serve/server/error-response.test.ts: PP
  packages/cli/src/serve/workspace-qualified-rest.test.ts: PP
  packages/cli/src/serve/workspace-runtime-coordinator.test.ts: PP
  packages/sdk-typescript/test/unit/DaemonClient.test.ts: PP
  packages/web-shell/client/App.test.tsx: PP
  packages/web-shell/client/components/plugins/PluginManagerPage.test.tsx: P
  packages/web-shell/client/daemon/workspace/DaemonWorkspaceProvider.test.tsx: P
  packages/web-shell/client/daemon/workspace/actions.test.ts: P
  packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.test.tsx: P

verdict: timeout
summary: the 15-minute budget elapsed before two full rounds completed (1 done) — no flakiness signal either way

--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/acp-bridge/src/bridge.test.ts: P (exit 0)
round 1 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/run-qwen-serve.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/server/error-response.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/workspace-qualified-rest.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/workspace-runtime-coordinator.test.ts: P (exit 0)
round 1 · packages/sdk-typescript/test/unit/DaemonClient.test.ts: P (exit 0)
round 1 · packages/web-shell/client/App.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/components/plugins/PluginManagerPage.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/daemon/workspace/DaemonWorkspaceProvider.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/daemon/workspace/actions.test.ts: P (exit 0)
round 1 · packages/web-shell/client/daemon/workspace/hooks/useDaemonMcp.test.tsx: P (exit 0)
round 2 · packages/acp-bridge/src/bridge.test.ts: P (exit 0)
round 2 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/run-qwen-serve.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/server/error-response.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/workspace-qualified-rest.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/workspace-runtime-coordinator.test.ts: P (exit 0)
round 2 · packages/sdk-typescript/test/unit/DaemonClient.test.ts: P (exit 0)
round 2 · packages/web-shell/client/App.test.tsx: P (exit 0)

Evidence images

01-ab-coordinator-base-vs-head

02-wire-harness-head-49-cells

03-mutation-matrix-4-mutants-killed

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

Qwen Code · sandboxed verification

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review of head 3b936af2d75be78762578f5ab0f83d8ef3b3a352 (new commits since prior reviews at e11f8125 / bot head).

Critical findings from prior reviews — status at current head

chiga0 R1 (floating prepareMcp() promise)void preparation.catch(…) is present in the diff; the rejection is handled regardless of whether remainingMs <= 0. Closed.

qwen-code-ci-bot Critical R1-1 through R1-7 — all closed at current head:

  • R1-1 (unstable hook identity): useDaemonMcp wraps api in useMemo. Closed.
  • R1-2 (cold runtime not started): await mcp.ensureRuntime() is called before the poll loop. Closed.
  • R1-3 (drain-interrupted stale): prepareMcpRevision sets mcpReconcileDeferred = true inside its drain-exit branch; test added. Closed.
  • R1-4 (mutation silently drops queued reconciliation): The serial mcpPhysicalTail queue serializes all MCP work; configRevision guard is independent of mcpRevision, so reloadWorkspaceMcp() fires regardless of intervening mutations; the last queued item polls post-reload state. Closed.
  • R1-5 (generation guard not re-asserted): runtime.generationGuard?.assertOpen() follows every mutation await; test added. Closed.
  • R1-6 (dotted server names rejected): validateRuntimeServerName uses no charset restriction; foo-bar.io test passes at 200. Closed.
  • R1-7 (in operator on config maps): Object.hasOwn used throughout. Closed.

Remaining non-blocking suggestions (inherited from bot review, still open)

Minor (R1-9) — multi-scope enable commits user scope before workspace-scope check. See inline comment on actions.ts.

Minor (R1-12) — observation budget (81 x 1.5 s ≈ 121 s) can be exhausted before a queued restart completes (MCP_RESTART_TIMEOUT_MS = 300 s). See inline comment on McpStatusMessage.

Coverage

Read: coordinator, bridge, config routes, runtime-MCP routes, SDK types, actions, useDaemonMcp, McpStatusMessage, error-response, run-qwen-serve admission. Not read to full depth: McpManagerPage complete body, App.test.tsx fixture types, pre-existing requestWorkspaceStatus body.

No local execution.

Reviewed with AI assistance.

action === 'disable' ? await workspace.mcpConfig() : undefined;
const scopes =
action === 'enable'
? (['user', 'workspace'] as const)

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.

Minor (R1-9 inherited)enable iterates ["user", "workspace"] in that order. If the user-scope call succeeds but the workspace-scope call returns 409 mcp_excluded_by_pattern, the user-scope exclusion-removal is already committed with no rollback. The server remains visibly blocked (the wildcard still wins), so this does not cause an observable enable, but the user-scope exclusion list becomes inconsistent. Low-frequency; worth a comment noting partial-failure semantics or a pre-flight pattern check before writing user scope.

let nextStatus: DaemonWorkspaceMcpStatus | undefined;
let runtimeEpoch: number | undefined;
await mcp.ensureRuntime();
for (let attempt = 0; attempt < 81; attempt += 1) {

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.

Minor (R1-12 inherited) — 81 x 1.5 s ≈ 121 s observation budget. The coordinator serial queue can be held by a restart/approve/authenticate mutation for up to MCP_RESTART_TIMEOUT_MS (300 s) + MCP_PREPARE_TIMEOUT_MS (120 s). Two concurrent operations can exhaust this budget and surface a spurious timeout. Not blocking — the retry pattern is in place — but if this budget is ever revisited it should account for the full queued tail.

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review of head 3b936af2d75be78762578f5ab0f83d8ef3b3a352 (round 2 — prior review was at e11f8125). Findings frozen before reading the updated cross-check below.

Prior round finding status

Id Summary Status at current head
R1-1 Floating prepareMcp() promise — daemon crash ✅ Fixed — void preparation.catch(...) at coordinator.ts:163
R2 TypeScript union in manageServer enable/disable Checked — route goes through mcp.manageServer with correct dispatch. Not a blocker.
R3–R5 Epoch at response / cache unstamped / activation drop Addressed — runtimeEpoch stamping in bridge + mcp.ensureRuntime() in reloadSelectedServer

New finding

F2-1 (Major, supported)useDaemonMcp auto-reload is keyed to the primary workspace's mcpVersion signal regardless of the workspaceCwd option.

useDaemonWorkspaceEventSignals() returns workspace-event signals for the active session's (primary) workspace. When McpManagerPage selects a secondary workspace and calls useDaemonMcp({ workspaceCwd: secondaryCwd }), the auto-reload hook (useWorkspaceEventReload) still fires on primary-workspace MCP events — not the requested workspace's events. Result: MCP status for a secondary workspace will not auto-refresh when that workspace's MCP changes; it may reload spuriously when the primary changes.

Named chain: useDaemonMcp.ts:36useWorkspaceEventReload(signals?.mcpVersion, …) where signals = useDaemonWorkspaceEventSignals() (session-scoped, not workspace-scoped by workspaceCwd). Confirmed via the test's mock: useDaemonWorkspaceEventSignals: () => undefined is workspace-agnostic.

This is a direct gap in the multi-workspace MCP management feature this PR introduces. The feature remains operable (initial load is correct; users can manually trigger actions), but live auto-refresh is broken for non-primary workspaces.

Cross-check against qwen-code-ci-bot CHANGES_REQUESTED (SHA e9ffb1cb)

ci-bot Id Title Status at 3b936af2
R1-5 Mutation routes never re-assert generation guard ✅ Fixed — runtime.generationGuard?.assertOpen() at workspace-runtime-mcp.ts:194, 223, 247
R1-7 in on config maps lets prototype keys flip scope ✅ Fixed — Object.hasOwn() in actions.ts
R1-2 Chat-card enable/disable never starts cold runtime ✅ Fixed — mcp.ensureRuntime() in reloadSelectedServer
R1-3 Drain-interrupted prepare latches stale ✅ Addressed — mcpReconcileDeferred + cancelDrain() reschedules
R1-34 Cross-workspace auto-reload keyed to host session ❌ Still present — confirmed as F2-1
R1-30 McpStatusMessage poll loop no unmount guard Still present (81 × 1.5 s loop); React 18 tolerates it, no data corruption. Minor.

Could not rule on: R1-4 (mutation drops queued prepare — configRevision guard limits impact, not run-verified), R1-8/R1-9/R1-18/R1-22 (OAuth admission lane / tools-resources epoch — diff evidence looks correct, not fully verified).

Scope

Static review: coordinator, bridge patch, config routes, runtime-MCP routes, SDK, useDaemonMcp, McpStatusMessage, McpManagerPage. No local toolchain.

Verdict

No confirmed blocking finding. F2-1 is a functional gap in multi-workspace auto-refresh — bounded, non-corrupting, operable. Approving.

Reviewed with AI assistance.

const result = useDaemonResource(load, resourceOptions);
const signals = useDaemonWorkspaceEventSignals();
useWorkspaceEventReload(
signals?.mcpVersion,

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.

Majorsignals?.mcpVersion comes from useDaemonWorkspaceEventSignals(), which is keyed to the active session's (primary) workspace, not to the workspaceCwd option passed into this hook. When McpManagerPage calls useDaemonMcp({ workspaceCwd: secondary }), MCP status for that workspace will not auto-refresh when that workspace's MCP state changes; it reloads spuriously when the primary workspace's MCP changes instead.

Suggestion: drive the auto-reload signal from a per-workspace subscription (e.g. pass workspaceCwd to a workspace-aware signal hook), or fall back to polling when workspaceCwd targets a non-primary workspace.

@ytahdn
ytahdn disabled auto-merge September 2, 2026 13:33
@ytahdn
ytahdn added this pull request to the merge queue Sep 2, 2026
Merged via the queue into main with commit 38d532c Sep 2, 2026
600 of 615 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.23.0.

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.

5 participants