Skip to content

feat(daemon): Trace cold first-session startup - #6907

Merged
wenshao merged 8 commits into
QwenLM:mainfrom
doudouOUC:perf/cold-session-profiling
Jul 16, 2026
Merged

feat(daemon): Trace cold first-session startup#6907
wenshao merged 8 commits into
QwenLM:mainfrom
doudouOUC:perf/cold-session-profiling

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

What this PR does

This PR adds an end-to-end cold first-session trace across deferred daemon runtime admission, ACP channel startup or reuse, ACP session/new, and the existing detailed startChat profile. A request that arrives before the runtime mounts now backdates its HTTP span to bootstrap arrival and records whether it started or joined runtime loading. The bridge records whether the Session spawned, joined, or reused an ACP channel, correlates independently-started preheat work with a daemon-generated channel UUID, and propagates W3C trace context into the ACP child. The child records bounded stage durations for settings, Config setup, authentication, file-system setup, Session registration, and response construction. The opt-in session-start JSONL record gains an optional opaque Session ID for trace correlation.

The implementation preserves /health readiness, Session response shapes, ACP lifecycle behavior, and telemetry-disabled behavior. It does not add a new protocol or profiling format.

Why it's needed

Issue #4748 still has a post-health cold first-session gap that browser timing cannot attribute. A first Session may wait for deferred runtime loading and an in-flight ACP preheat, then perform per-session child initialization. Existing spans start too late to separate bootstrap wait from proxy or network time, do not identify the current request's channel-wait path, and do not connect session/new to the detailed startChat profile. This change closes that observability gap so follow-up optimization can target the measured dominant stage instead of adding speculative caching or changing readiness semantics.

Reviewer Test Plan

How to verify

Build and bundle the CLI, enable telemetry with a local outfile plus QWEN_CODE_PROFILE_SESSION_START=1, start qwen serve, and send POST /session immediately after the first successful /health. The Session should succeed with the unchanged response shape. Its request span should include runtime.path and a non-negative runtime.wait_ms when it crosses deferred runtime admission, followed by channel.wait, session.new, and a directly parented ACP-child session_start span. The completion event and JSONL record should contain the returned Session ID.

Repeat after POST /workspace/acp/preheat; the Session trace should report channel.wait.path=reused, and the preheat and Session traces should share the same ACP channel UUID. Then disable telemetry and JSONL profiling; Session creation should still succeed, no telemetry outfile should be created, and the profile file should remain unchanged.

Local verification completed with npm run lint, npm run build, npm run typecheck, npm run bundle, and focused Vitest runs covering 276 core tests, 443 CLI tests, and 394 ACP bridge tests. The final rebased bundled smoke recorded runtime.path=started_on_request, runtime.wait_ms=98.27, channel.wait.path=joined, and a child span directly parented to session.new in the same trace.

Evidence (Before & After)

N/A — telemetry-only change with no TUI or Web UI output change.

Tested on

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

Environment (optional)

macOS arm64, Node.js v22.22.3, npm 10.9.8, bundled CLI, loopback daemon, local OpenTelemetry file exporter, existing authenticated provider configuration.

Risk & Scope

  • Main risk or tradeoff: This is a cross-package telemetry change across daemon, bridge, ACP child, and core profiling. Session and channel identifiers are diagnostic span attributes only and are not added to metric labels or public responses.
  • Not validated / out of scope: Windows and Linux were not tested locally; CI remains responsible for those platforms. A representative deployment still needs at least 30 serialized cold starts before setting latency targets or choosing an optimization. ACP readiness capability work and startup optimization are intentionally out of scope.
  • Breaking changes / migration notes: None. ACP _meta remains optional, the JSONL sessionId is additive and opt-in, and telemetry-disabled behavior is unchanged.

Linked Issues

Part of #4748

中文说明

本 PR 做了什么

本 PR 为冷启动首个 Session 增加了端到端链路追踪,覆盖 daemon 延迟 runtime 准入、ACP channel 启动或复用、ACP session/new,以及现有的 startChat 详细分析。若请求在 runtime 挂载前到达,HTTP span 现在会回溯到 bootstrap 收到请求的时刻,并记录该请求是发起还是加入了 runtime 加载。Bridge 会记录 Session 是触发、加入还是复用了 ACP channel,用 daemon 生成的 channel UUID 关联独立启动的预热工作,并把 W3C trace context 传播到 ACP 子进程。子进程会记录设置加载、Config 初始化、认证、文件系统初始化、Session 注册和响应构建等有界阶段耗时。已有的可选 session-start JSONL 记录新增可选的不透明 Session ID,用于与 trace 关联。

实现保持 /health 就绪语义、Session 响应结构、ACP 生命周期行为以及关闭 telemetry 时的行为不变;没有增加新的协议或分析格式。

为什么需要它

#4748 仍存在健康检查完成后的冷启动首 Session 延迟,而浏览器计时无法归因。首个 Session 可能需要等待延迟 runtime 加载和正在进行的 ACP 预热,之后还要执行子进程内的逐 Session 初始化。现有 span 启动过晚,无法把 bootstrap 等待与代理或网络时间分开,也无法标识当前请求的 channel 等待路径,或将 session/new 与详细 startChat 分析关联起来。本改动补齐了该可观测性缺口,使后续优化可以针对实测的主要阶段,而不是引入推测性的缓存或改变就绪语义。

Reviewer 测试计划

如何验证

构建并 bundle CLI,启用指向本地文件的 telemetry 和 QWEN_CODE_PROFILE_SESSION_START=1,启动 qwen serve,并在第一次 /health 成功后立即发送 POST /session。Session 应成功并保持原有响应结构。若请求经过延迟 runtime 准入,其 request span 应包含 runtime.path 和非负的 runtime.wait_ms,之后依次出现 channel.waitsession.new,以及直接以其为父 span 的 ACP 子进程 session_start span。完成事件和 JSONL 记录应包含返回的 Session ID。

在调用 POST /workspace/acp/preheat 后重复测试;Session trace 应记录 channel.wait.path=reused,预热 trace 与 Session trace 应共享同一个 ACP channel UUID。随后关闭 telemetry 和 JSONL 分析;Session 仍应成功,不应创建 telemetry outfile,profile 文件也应保持不变。

本地已完成 npm run lintnpm run buildnpm run typechecknpm run bundle,以及覆盖 276 个 core 测试、443 个 CLI 测试和 394 个 ACP bridge 测试的聚焦 Vitest。最终在 rebase 后生成的 bundle smoke 中记录到 runtime.path=started_on_requestruntime.wait_ms=98.27channel.wait.path=joined,并确认 child span 在同一 trace 中直接以 session.new 为父 span。

证据(修改前后)

N/A — 仅 telemetry 变更,没有 TUI 或 Web UI 输出变化。

测试平台

OS 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

macOS arm64、Node.js v22.22.3、npm 10.9.8、bundled CLI、loopback daemon、本地 OpenTelemetry 文件 exporter、已有的认证 provider 配置。

风险与范围

  • 主要风险或取舍:这是跨 daemon、bridge、ACP 子进程和 core profiler 的跨包 telemetry 改动。Session 和 channel 标识只作为诊断 span 属性,不会加入指标标签或公共响应。
  • 未验证或不在范围内:未在本地测试 Windows 和 Linux,这些平台由 CI 验证。在设定延迟目标或选择优化方案前,代表性部署环境仍需执行至少 30 次串行冷启动。ACP readiness capability 和启动优化有意不包含在本 PR 中。
  • 破坏性变更或迁移说明:无。ACP _meta 仍为可选,JSONL sessionId 是可选增量字段,关闭 telemetry 时的行为不变。

关联 Issue

#4748 的一部分

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

Copy link
Copy Markdown
Collaborator Author

E2E Test Report

Tested the bundled CLI on macOS arm64 with Node.js v22.22.3, a loopback daemon, the local OpenTelemetry file exporter, and the existing authenticated provider configuration.

Cold Session immediately after health

  • Result: POST /session returned 200 with the unchanged response shape.
  • Final rebased smoke: daemon route duration 1.520s.
  • Request trace: runtime.path=started_on_request, runtime.wait_ms=98.27, channel.wait.path=joined.
  • Trace continuity: POST /session → session.new → ACP child session_start; the child span was directly parented to session.new in trace 9246bdffd18b2cfc14e7d4ae0a46039e.
  • Correlation: the Session completion event and opt-in JSONL record contained the returned Session ID.

Explicitly preheated Session

  • POST /workspace/acp/preheat?timeoutMs=10000 returned 200 with ready: true in 1.064s.
  • The following Session returned 200 in 383ms with channel.wait.path=reused.
  • The preheat and Session traces shared ACP channel ID adde4a73-04a8-4247-9cd9-cb09c4af46da.

Telemetry disabled

  • A cold Session returned 200 in 1.061s.
  • No telemetry outfile was created.
  • The session-start profile JSONL line count remained unchanged at 8.

Automated verification

  • npm run lint
  • npm run build
  • npm run typecheck
  • npm run bundle
  • Core focused tests: 276 passed.
  • CLI focused tests: 443 passed.
  • ACP bridge focused tests: 394 passed.

The ACP child printed a pipe AbortError only during immediate daemon teardown after successful Session responses. The daemon exited successfully, and this shutdown-only message did not affect the request or trace assertions.

@doudouOUC
doudouOUC marked this pull request as ready for review July 15, 2026 02:02
@doudouOUC doudouOUC self-assigned this Jul 15, 2026
@doudouOUC
doudouOUC requested a review from wenshao July 15, 2026 02:02
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: This is an observed gap, not a theoretical concern. The PR links to #4748 and includes concrete timing data from a downstream deployment (2,534ms P50 cold vs 222ms preheated) and a local dry-run showing 700–800ms unattributed latency. Browser timing cannot separate daemon, channel, and child work — that's a real observability blind spot.

Direction: Cold-start observability is squarely within the daemon performance story. Standard W3C trace context propagation and extending the existing session-start-profiler and daemon telemetry seams is the right approach. No CHANGELOG reference for cold-session tracing specifically, but the area (daemon telemetry, session profiling) is clearly relevant — the existing QWEN_CODE_PROFILE_SESSION_START profiler is already in the codebase.

Size: Cross-package change (cli, core, acp-bridge) touching core paths (packages/core/src/core/client.ts, packages/core/src/telemetry/daemon-tracing.ts, packages/core/src/core/session-start-profiler.ts). Production logic: ~340 lines (well under 500). Tests: ~348 lines. Design docs: ~194 lines. Total additions ~803 — a large PR in aggregate, though most of the bulk is tests and documentation. Flagging for maintainer awareness given the cross-package + telemetry scope.

Approach: The scope feels appropriate for the stated goal. Each addition maps to a specific gap in the trace tree: deferred-runtime request timing, channel-wait classification (spawned/joined/reused), W3C trace context into the ACP child, bounded child stage profiling, and session ID correlation. The startRuntime()boolean return type change is a minimal, well-contained signal. The DeferredRuntimeRequestTiming Symbol-keyed request property is a clean pattern that avoids coupling middleware ordering. The design doc is thorough and the existing test infrastructure is extended rather than duplicated.

One note: the createAcpSessionStartProfiler helper in acpAgent.ts (~70 lines) is a self-contained micro-profiler that mirrors the existing session-start-profiler.ts pattern. Is there a reason the existing profiler couldn't be reused or extended, rather than a parallel implementation? Not blocking — just worth considering whether the two profilers should converge over time.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题:这是一个已观测到的缺口,不是理论性担忧。PR 关联了 #4748 并包含来自下游部署的具体计时数据(冷启动 P50 2,534ms vs 预热后 222ms)以及本地 dry-run 显示的 700–800ms 无法归因的延迟。浏览器计时无法区分 daemon、channel 和子进程的工作——这是一个真实的可观测性盲区。

方向:冷启动可观测性是 daemon 性能故事的核心部分。使用标准 W3C trace context 传播并扩展现有的 session-start-profiler 和 daemon telemetry 接缝是正确的做法。CHANGELOG 中没有冷启动 session 追踪的直接参考,但该领域(daemon telemetry、session profiling)明确相关。

规模:跨包改动(cli、core、acp-bridge),触及核心路径。生产代码约 340 行(远低于 500 阈值),测试约 348 行,设计文档约 194 行。总增加约 803 行——整体是一个大 PR,但大部分体积来自测试和文档。鉴于跨包 + telemetry 范围,标记供 maintainer 关注。

方案:范围与目标匹配。每个新增都对应 trace 树中的特定缺口:延迟 runtime 请求计时、channel 等待分类(spawned/joined/reused)、W3C trace context 传播到 ACP 子进程、有界子进程阶段分析、session ID 关联。createAcpSessionStartProfiler(约 70 行)是一个与现有 session-start-profiler.ts 模式平行的独立微分析器——是否考虑过复用或扩展现有分析器?不阻塞,但值得思考两者是否应该在未来合并。

进入代码审查 🔍

Qwen Code · qwen3.7-max

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

The implementation is clean and well-structured. Each change maps to a specific gap in the cold-start trace tree, and the telemetry paths are defensively guarded so they can never affect session creation.

Correctness: The withDaemonSpan parent-context propagation uses the OpenTelemetry 4-arg startActiveSpan overload correctly. The startRuntime()boolean return type change is minimal and backward-compatible (the wrapper startRuntimeForRequest?.() ?? false handles undefined). Channel path classification (reusedjoinedspawned_on_request) is correct — a dying channel can't be reused, an in-flight spawn can be joined, otherwise spawn fresh.

Security: No issues. Session IDs and channel UUIDs are diagnostic span attributes only — not added to metric labels or public API responses.

Reuse note: createAcpSessionStartProfiler in acpAgent.ts (~70 lines) mirrors the existing session-start-profiler.ts pattern (time/timeSync/stage recording/failure tracking). The two serve different sinks — OTel span attributes vs. JSONL file — so a shared abstraction isn't trivially applicable. Not blocking, but if both profilers grow over time, convergence would reduce drift.

No critical blockers found.

Verification

This is a telemetry-only change (no TUI or Web UI output change), so tmux before/after is not applicable. Verification focused on build integrity and unit tests:

$ npm run build
✅ All packages compiled successfully

$ npm run typecheck
✅ All packages type-checked successfully

$ npx eslint <changed files>
✅ No lint errors

$ cd packages/core && npx vitest run src/telemetry/daemon-tracing.test.ts src/core/session-start-profiler.test.ts src/core/client.test.ts
 Test Files  3 passed (3)
      Tests  276 passed (276)

$ cd packages/acp-bridge && npx vitest run src/bridge.test.ts
 Test Files  1 passed (1)
      Tests  394 passed (394)

$ cd packages/cli && npx vitest run src/acp-integration/acpAgent.test.ts src/serve/server/telemetry.test.ts src/serve/run-qwen-serve.test.ts
 Test Files  3 passed (3)
      Tests  443 passed (443)

All 1,113 tests pass across the three affected packages. The new tests cover: deferred-runtime request timing propagation into spans, channel-wait path classification (spawned/joined/reused), ACP channel UUID correlation, W3C trace context propagation into the ACP child, bounded stage profiling with failure recording, and session ID in JSONL records.

中文说明

代码审查

实现干净且结构良好。每个改动都对应冷启动 trace 树中的特定缺口,telemetry 路径都有防御性保护,不会影响 session 创建。

正确性: withDaemonSpan 的 parent-context 传播正确使用了 OpenTelemetry 的 4 参数 startActiveSpan 重载。startRuntime()boolean 返回类型变更最小化且向后兼容。Channel path 分类(reusedjoinedspawned_on_request)逻辑正确。

安全性: 无问题。Session ID 和 channel UUID 仅作为诊断 span 属性,不会添加到指标标签或公共 API 响应中。

复用说明: acpAgent.ts 中的 createAcpSessionStartProfiler(约 70 行)与现有 session-start-profiler.ts 模式平行。两者服务于不同的输出目标(OTel span 属性 vs. JSONL 文件),共享抽象并不容易。不阻塞,但如果两个 profiler 随时间增长,建议考虑合并以减少漂移。

未发现关键阻塞问题。

验证

这是仅 telemetry 的变更(无 TUI 或 Web UI 输出变化),因此 tmux before/after 不适用。验证集中在构建完整性和单元测试:构建 ✓、类型检查 ✓、Lint ✓、1,113 个测试全部通过(core 276 + acp-bridge 394 + cli 443)。

Qwen Code · qwen3.7-max

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — Clean across every stage; the 4/5 reflects the cross-package + telemetry scope needing a maintainer's sign-off, not quality concerns.

This PR closes a real and measured observability gap (#4748) — cold first-session latency of 2,534ms P50 that browser timing cannot attribute. The implementation adds tracing spans at exactly the right boundaries: deferred-runtime request timing, channel-wait classification, W3C trace context into the ACP child, and bounded child stage profiling. Every addition maps to a specific gap in the trace tree.

The code is clean. Telemetry paths are defensively guarded (try/catch on every setAttribute, span?. optional chaining), the startRuntime() boolean return is a minimal signal change, and the channel path enum is readable. 1,113 unit tests pass across three packages, build and typecheck are clean.

The design doc is genuinely useful — it documents the architecture, downstream consumers reviewed, and the verification plan. The createAcpSessionStartProfiler parallel to session-start-profiler.ts is noted but acceptable given the different sinks (OTel spans vs. JSONL).

My only reservation is procedural: this crosses core/bridge/CLI package boundaries and touches telemetry infrastructure, which warrants a maintainer's eye on the architectural decision even though the production logic change is ~340 lines and well-contained.

Deferring to the maintainer for final approval.

中文说明

置信度:4/5 — 各阶段均通过;4/5 反映的是跨包 + telemetry 范围需要 maintainer 签字,而非质量顾虑。

本 PR 填补了一个已测量的可观测性缺口(#4748)——冷启动首 Session P50 延迟 2,534ms,浏览器计时无法归因。实现在恰当的边界添加了追踪 span:延迟 runtime 请求计时、channel 等待分类、W3C trace context 传播到 ACP 子进程、有界子进程阶段分析。每个新增都对应 trace 树中的特定缺口。

代码干净。Telemetry 路径有防御性保护,startRuntime() 布尔返回值是最小化的信号变更。1,113 个单元测试跨三个包全部通过,构建和类型检查均正常。

设计文档有实际价值。唯一保留是程序性的:此改动跨越 core/bridge/CLI 包边界并触及 telemetry 基础设施,即使生产逻辑变更仅约 340 行且范围明确,仍需 maintainer 对架构决策的最终确认。

转交 maintainer 做最终审批。

Qwen Code · qwen3.7-max

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

⏸️ Deferring to @chinesepowered — this is a clean feat PR (cross-package telemetry across cli/core/acp-bridge, ~340 production lines, 1,113 tests passing, no code review blockers), but it touches core paths and telemetry infrastructure across package boundaries, which needs a maintainer's sign-off per our triage policy. Needs a human call on this one.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/acp-bridge/src/bridge.ts
Comment thread packages/cli/src/serve/server/request-helpers.ts
doudouOUC and others added 2 commits July 15, 2026 11:11
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/serve/server/telemetry.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/telemetry/daemon-tracing.test.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

Reviewed. Suggestions are inline. Not reviewed: removed-behavior audit — Agent 1b failed twice.

— qwen3.7-max via Qwen Code /review

Comment thread packages/acp-bridge/src/bridge.test.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed — no blockers. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to the diff; see the terminal output.

— qwen3.7-max via Qwen Code /review

Comment thread packages/core/src/telemetry/daemon-metrics.ts
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>

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

No issues found. LGTM! ✅

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Independent maintainer verification — build + live E2E ✅

I reproduced this PR end-to-end on a maintainer machine against the real built binary (no mocks, no stubs). I booted a live qwen serve daemon with the OpenTelemetry file exporter, drove it over HTTP, and read the emitted spans back off disk to assert every claim programmatically. This corroborates @doudouOUC's E2E report and adds a load-bearing controlled experiment.

Setup: isolated worktree at PR head a4e194a13 with its own npm ci build; isolated HOME with security.folderTrust.enabled=false, security.auth.selectedType=openai + dummy OpenAI creds (so session/new authenticates without contacting a model — the model is only hit on prompt); env QWEN_TELEMETRY_ENABLED=1 QWEN_TELEMETRY_TARGET=local QWEN_TELEMETRY_OUTFILE=… QWEN_CODE_PROFILE_SESSION_START=1 OTEL_BSP_SCHEDULE_DELAY=300; qwen serve --no-web (fast-path, runtime deferred until the first non-/health request).

1 · One cold POST /session → the full cross-process trace

A single cold session emits one distributed trace spanning two OS processes — the daemon (qwen serve) and the per-session ACP child (qwen --acp) — correlated by W3C traceparent. Every id/attribute below was read back from the exporter file:

cross-process trace tree

The child session_start span lives in a separate process, yet shares the daemon's trace id (cb5455f8…) and lists session.new as its parent span — the end-to-end propagation works exactly as described.

2 · Live assertions — 31/31 green (Scenarios A, C, D)

live E2E assertions

  • A — cold first session (22/22): request span carries runtime.path=started_on_request + non-negative runtime.wait_ms; span start is backdated over the deferred wait (duration ≥ wait_ms); channel.wait and session.new parent to the request span; session.new.completed event carries the returned Session ID + acp_channel.id; the ACP-child span is directly parented to session.new, all six bounded stage durations present; the opt-in JSONL profile record carries the same Session ID.
  • C — explicit preheat (5/5): the following Session records channel.wait.path=reused, and the preheat-spawned channel + session.new share the same acp_channel.id.
  • D — negative control (4/4): with telemetry and JSONL profiling off, the Session still returns 200, and no telemetry outfile and no profile file are created.

3 · Load-bearing — the trace injection is doing real work

Reverting only telemetry.injectPromptContext(...) on the ACP session/new call (bridge.ts), rebuilding, and re-running the same cold session de-parents the child span into a separate root trace — while the Session still returns 200 (telemetry never affects behavior). Restoring it brings parenting back.

load-bearing revert

4 · The PR's own test suites pass at HEAD

test suites

1137 tests pass across the 8 test files this PR touches, including run-qwen-serve.test.ts“starts deferred runtime for the first runtime route and serves that request”, which asserts both runtime.path='started_on_request' and runtime.path='joined' deterministically.

Notes (honest scope)

  • runtime.path='joined' is inherently racy in a live run: the runtime mount does ~110 ms of synchronous work that blocks the event loop, so a concurrent request can't reliably interleave into the deferred-admission window. It reproduces deterministically only under the mocked mount in run-qwen-serve.test.ts:3253. The realistic cold path (started_on_request) reproduces every time. channel.wait.path’s joined/reused states are both observed live (Scenarios A/C).
  • POST /workspace/acp/preheat isn't in the telemetry route-classifier allowlist, so it gets no HTTP request span — pre-existing behavior, not introduced by this PR. The preheat→session correlation is instead proven via the matching bridge channel.spawn / session.new acp_channel.id.

Verdict: the observability this PR adds is real and correct against the built binary; /health readiness, the Session response shape, and telemetry-disabled behavior are all unchanged. LGTM from a verification standpoint. 👍

中文版本

维护者独立验证 —— 构建 + 真实 E2E ✅

我在维护者机器上针对真实构建产物(无 mock、无 stub)对本 PR 做了端到端复现:启动一个真实的 qwen serve daemon,启用 OpenTelemetry 文件 exporter,通过 HTTP 驱动它,再把落盘的 span 读回来对每一项声明做程序化断言。本报告印证了 @doudouOUC 的 E2E 报告,并补充了一个「可承重」(load-bearing)对照实验。

环境: 隔离 worktree 位于 PR head a4e194a13,独立 npm ci 构建;隔离 HOME,设置 security.folderTrust.enabled=falsesecurity.auth.selectedType=openai + 假 OpenAI 凭据(使 session/new 无需联系真实模型即可认证,模型仅在 prompt 时才被调用);环境变量 QWEN_TELEMETRY_ENABLED=1 QWEN_TELEMETRY_TARGET=local QWEN_TELEMETRY_OUTFILE=… QWEN_CODE_PROFILE_SESSION_START=1 OTEL_BSP_SCHEDULE_DELAY=300qwen serve --no-web(fast-path,runtime 延迟到第一个非 /health 请求才挂载)。

1 · 一次冷启动 POST /session → 完整的跨进程 trace

单次冷启动 Session 产生一条横跨两个操作系统进程的分布式 trace——daemon(qwen serve)与每 Session 的 ACP 子进程(qwen --acp)——通过 W3C traceparent 关联。上图中每个 id/属性都是从 exporter 文件读回的:子进程的 session_start span 位于独立进程,却共享 daemon 的 trace id(cb5455f8…)并将 session.new 记为其父 span,跨进程传播完全符合描述。

2 · 真实断言 —— 31/31 全绿(场景 A、C、D)

  • A —— 冷启动首个 Session(22/22): request span 带 runtime.path=started_on_request 和非负 runtime.wait_ms;span 起始时间回溯覆盖延迟等待(duration ≥ wait_ms);channel.waitsession.new 以 request span 为父;session.new.completed 事件带返回的 Session ID + acp_channel.id;ACP 子进程 span 直接以 session.new 为父,六个有界阶段耗时齐全;可选 JSONL profile 记录带相同的 Session ID。
  • C —— 显式预热(5/5): 随后的 Session 记录 channel.wait.path=reused,且预热所生成的 channel 与 session.new 共享同一个 acp_channel.id
  • D —— 负向对照(4/4): 关闭 telemetry 与 JSONL profiling 后,Session 仍返回 200,且创建 telemetry 输出文件、创建 profile 文件。

3 · 可承重验证 —— trace 注入确实在起作用

回退 ACP session/new 调用上的 telemetry.injectPromptContext(...)(bridge.ts),重新构建并重跑同样的冷启动 Session,会让子进程 span 脱离为独立的根 trace——而 Session 仍返回 200(telemetry 从不影响行为)。恢复该行后,父子关联重新出现。

4 · PR 自带测试在 HEAD 全通过

本 PR 改动的 8 个测试文件共 1137 个用例全部通过,包含 run-qwen-serve.test.ts“starts deferred runtime for the first runtime route and serves that request”,该用例确定性地同时断言 runtime.path='started_on_request'runtime.path='joined'

说明(如实标注范围)

  • runtime.path='joined' 在真实运行中天然存在竞态:runtime 挂载有约 110ms 的同步工作会阻塞事件循环,因此并发请求难以稳定地插入到延迟准入窗口内。它只在 run-qwen-serve.test.ts:3253 的 mock 挂载下确定性复现。真实冷启动路径(started_on_request)每次都能复现。channel.wait.pathjoined/reused 两种状态在真实运行中均已观测到(场景 A/C)。
  • POST /workspace/acp/preheat 不在 telemetry 路由分类白名单中,因此没有 HTTP request span——这是既有行为,非本 PR 引入。预热→Session 的关联改由匹配的 bridge channel.spawn / session.newacp_channel.id 证明。

结论: 本 PR 增加的可观测性在构建产物上真实且正确;/health 就绪语义、Session 响应结构、以及关闭 telemetry 时的行为均保持不变。从验证角度 LGTM。👍

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

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

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

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

Qwen Code · serve A/B

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

No issues found. LGTM! ✅

— qwen3.7-max via Qwen Code /review

@wenshao
wenshao added this pull request to the merge queue Jul 16, 2026
Merged via the queue into QwenLM:main with commit 10a39d1 Jul 16, 2026
40 of 41 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants