feat(serve): Complete legacy session workspace telemetry - #7003
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Daemon legacy session workspace telemetry E2EGoalVerify that legacy session request spans use the workspace selected by route Baseline dry run
Implementation verificationRepeat the scenario with the locally built CLI:
ResultPassed on 2026-07-16 (Asia/Shanghai).
|
🩺 serve daemon A/BBuilt the PR base vs this PR head ✅ No response changes against the PR base across 4 scenario(s). — Qwen Code · serve A/B |
|
Thanks for the PR! (Re-triage at @wenshao's request, post maintainer verification.) Template looks good ✓ Problem: Real observability gap, not theoretical. Legacy session routes had incomplete or incorrect workspace attribution — owner-routed requests fell back to the primary workspace hash, and rewind/shell resolved the live owner twice. Documented follow-up to #6833 and #6378, reproducible in any multi-workspace daemon. Direction: Aligned. Completing the telemetry catalog and fixing workspace attribution is a direct continuation of prior merged work in the same domain. No CHANGELOG reference, but the area is clearly within the project's observability goals. Size: ~612 production lines (4 files), ~968 test lines (4 files), ~143 docs lines (3 files). All within Approach: The declarative catalog is the right structure — 48 routes with a 41/7 attribution split, auditable at a glance, and guarded by a bidirectional drift test. Deferred attribution via Moving on to code review. 🔍 中文说明感谢贡献!(应 @wenshao 要求,在维护者验证后重新 triage。) 模板完整 ✓ 问题: 真实的可观测性缺口,非理论性关注。Legacy session 路由的 workspace 归因不完整或不正确——owner-routed 请求回退到 primary workspace hash,rewind/shell 重复解析 live owner。是 #6833 和 #6378 的已记录后续工作,在任何多 workspace daemon 中可复现。 方向: 对齐。补齐 telemetry catalog 并修复 workspace 归因是同领域已合并工作的直接延续。无 CHANGELOG 参考,但该领域明确属于项目的可观测性目标。 规模: 约 612 行生产代码(4 个文件),约 968 行测试(4 个文件),约 143 行文档(3 个文件)。全部在 方案: 声明式 catalog 是正确的结构——48 条路由,41/7 归因分类,一目了然,由双向漂移测试守护。通过 进入代码审查 🔍 — Qwen Code · qwen3.7-max Reviewed at |
Code ReviewIndependent proposal: Before reading the diff, I'd solve this with: (1) a data-driven route catalog replacing the regex chain in Findings: No critical blockers, no AGENTS.md violations.
Reuse check: The catalog matcher is purpose-built for Express route matching with attribution metadata. No existing utility or dependency covers this. No reuse concern. Real-Scenario TestingThis is an observability-only change with no user-visible behavior surface — no TUI output, no API contract changes, no session execution changes. tmux testing cannot meaningfully exercise internal telemetry middleware attribution. Verification relies on:
The unit tests and the maintainer's live E2E comprehensively cover the critical paths. This testing approach matches the change's nature. 中文说明代码审查独立方案: 在阅读 diff 之前,我会用以下方式解决:(1) 数据驱动的路由 catalog 替代 发现: 无关键阻塞项,无 AGENTS.md 违规。
复用检查: catalog 匹配器是为带归因元数据的 Express 路由匹配专门构建的。没有现有工具或依赖覆盖此功能。无复用问题。 真实场景测试这是仅涉及可观测性的改动,无用户可见行为面——无 TUI 输出、无 API 合约变更、无 session 执行变更。tmux 测试无法有意义地验证内部 telemetry middleware 归因。验证依赖:
单元测试和维护者的在线 E2E 全面覆盖了关键路径。此测试方式匹配改动的性质。 — Qwen Code · qwen3.7-max Reviewed at |
|
Confidence: 5/5 — clean review across all stages, maintainer has independently verified with live daemon E2E and signed off LGTM, all review feedback addressed, CI green at the reviewed commit. This is a well-executed observability PR that has matured through multiple review rounds into a solid, thoroughly-verified change. The declarative catalog replaces a fragile regex chain with an auditable, drift-guarded data structure. The deferred attribution mechanism is minimal and correct — first-selection-wins, Symbol-keyed context, proper cleanup on settlement. Tests grew to 65 assertions covering every critical path. The maintainer's live E2E with tamper proofs is the strongest verification this kind of change can get. The concerns that prompted the previous Stage 3 defer (telemetry infrastructure scope, 612 production lines, new handler contract) have been fully addressed by the maintainer's independent verification — @wenshao confirmed every publication seam is load-bearing, the fail-safe direction is correct (omit hash, never misattribute), and wire parity holds across all driven scenarios. Approving. ✅ 中文说明置信度:5/5 — 各阶段审查均通过,维护者已独立通过在线 daemon E2E 验证并签字 LGTM,所有评审反馈已处理,CI 在所审查的 commit 上全绿。 这是一个执行良好的可观测性 PR,经过多轮评审迭代已成熟为扎实、充分验证的改动。声明式 catalog 用可审计、有 drift guard 的数据结构替代了脆弱的正则链。延迟归因机制最小且正确——首次选择优先、Symbol 键控 context、结算时正确清理。测试增长至 65 个断言,覆盖每个关键路径。维护者的在线 E2E 及破坏性实验是此类改动能获得的最强验证。 之前 Stage 3 延迟的顾虑(telemetry 基础设施范围、612 行生产代码、新 handler 合约)已被维护者的独立验证完全解决——@wenshao 确认每个发布点是承重的,fail-safe 方向正确(省略 hash,绝不误归因),wire 一致性在所有驱动场景中成立。 批准。 ✅ — Qwen Code · qwen3.7-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
[codex] Review feedback addressed in
Validation: targeted telemetry/resolver suite 62/62, full workspace typecheck, ESLint, and Prettier passed. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline.
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
[codex] Review feedback addressed in
Validation: targeted tests 55/55, full workspace typecheck, ESLint, and Prettier passed. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
— qwen3.7-max via Qwen Code /review
|
[codex] Thanks — no code change is needed for this review result. The cancelled |
|
@qwen-code /resolve |
…y and deferred runtime
|
Qwen Code resolved the merge conflicts and pushed the branch update. Merge Conflict Resolution Summary — PR #7003Base branch: main Conflicted Files1.
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: verification — the review posts findings, but no verifier ran (Step 4 builds its prompt with agent-prompt --role verify; none was recorded, so the findings were not verified).
— qwen3.7-max via Qwen Code /review
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Verification: targeted Vitest 48/48, Prettier, |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
— qwen3.7-max via Qwen Code /review
|
[codex] Thanks — won’t take this one. The cancelled |
Maintainer verification — local real-build & live-daemon E2E at
|
| Scenario | Span result |
|---|---|
POST /session creating in secondary |
200 · secondary hash (late-bound creation attribution) |
GET /session/:id/status on the live secondary session |
200 · secondary hash (live-owner routing) |
GET /session/foo%2Fbar/status |
404 · canonical route, session.id decoded to foo/bar, no hash |
GET /session/missing/status, POST /session/missing/prompt |
404 · span present, no hash (no primary fallback) |
GET /session/:id/export (secondary session id) |
404 · primary hash — documented primary-bound exception |
POST /sessions/archive |
200 · primary hash (pre-resolved) |
GET /session/:id/events SSE held 1400 ms |
span settles on close, duration 1405 ms, secondary hash |
GET /session/missing/events failed handshake |
404 · counted normally |
Merge-base, same driver — 2 spans total, and both are wrong or lossy: POST /session carries the primary hash even though the session was created in the secondary workspace; POST /session/missing/prompt falls back to the primary hash; status/export/archive/events produced no span at all.
4. Wire parity & span hygiene
- Every one of the 10 driven requests returned the identical HTTP status on both builds — consistent with the observability-only claim (and with the serve A/B bot's result).
- Raw-path leak scan: 0 of 9 PR request spans contain a workspace cwd; only 16-hex sha256 hashes appear.
- SIGINT graceful shutdown flushed all 9 request spans to the outfile.
- Cross-process spans (
qwen-code.daemon.bridge,qwen-code.daemon.session_start) present and unchanged on both builds; thesession.idresource attribute on parameterless routes (daemon:<pid>) is pre-existing and identical on both.
Not covered locally
- The in-process HTTP metric sink isn't observable through the outfile exporter, so successful-SSE exclusion vs counted failed-handshake at the metrics level rests on the 48 middleware unit tests (which assert exactly that seam).
- Windows/Linux not exercised locally; remote CI is green at the same head (unit 31m48s, Serve A/B 14m43s, web-shell smoke 5m37s).
中文版本(Chinese version)
维护者验证 — 在 1eefbed 上的本地真实构建与在线 daemon E2E
结论:LGTM。 本 PR 的全部声明都在本地真实构建上得到复现:catalog 与 drift guard 确实承担守护职责;workspace 归因绑定到 handler 实际选择的 runtime;无法解析的请求省略 hash 而不再回退 primary;文档声明的 primary-bound 例外行为与文档一致;SSE 结算正确;所有驱动场景下 HTTP wire 与 merge-base 完全一致。
环境
- 在 head
1eefbed建立隔离 detached worktree,完整npm ci+ bundle;另在 merge-base10a39d15d以相同方式构建第二个 worktree 用于 A/B。 - 产物身份核验:PR 独有符号
daemonTelemetryResponseContext存在于 PR bundle 的 server chunk、不存在于基线 bundle,确保 A/B 比较的确实是 PR 与基线代码。 - macOS、Node v22.23.1、npm 10.9.8。
1. 专项测试、typecheck、lint,以及两个破坏性实验
对 4 个 telemetry/resolver 测试文件运行 vitest run:65/65 通过(48 + 7 + 1 + 9;比 PR 描述中的 59 多,来自评审反馈提交新增的用例)。workspace typecheck、eslint、prettier(改动文件)全部通过。
为确认这些守卫是承重的而非装饰性的,我从两个方向破坏代码并观察对应测试翻红:
- 实验 A — 删除一条 catalog 条目(
GET /session/:id/status)后 drift guard 立即失败:expected [ …(47) ] to deeply equal [ …(48) ]。catalog 无法在任一方向与 Express 注册静默漂移。 - 实验 B — 移除
requireSessionRuntime中 live-owner 的setDaemonTelemetryWorkspace()发布点,4 个测试失败并精确点名该 seam。
两个实验均已还原,worktree 干净。
2. 发布点静态审计
- 全部 41 条
handler_resolved路由都经过 4 个发布 resolver 之一(requireSessionRuntime、session 创建、session 恢复、transcript resolver)——内联注册的status、pending-prompts、DELETE /session/:id逐一抽查。 - 失败方向是 fail-safe:resolver 在选择前退出只会省略 hash;由于发布是首次选择生效、且发生在 runtime 选择时刻,不可能产生误归因。
- drift guard 的 4 个注册器就是 legacy 路由注册点的全集——
serve/下唯一的其他app.post('/session'…)文本命中是auth.ts中的文档注释。
3. 双 workspace 在线 daemon A/B(真实产物,同一驱动脚本)
node dist/cli.js serve --port 0 --hostname 127.0.0.1 --workspace <primary> --workspace <secondary> --token …,附 QWEN_TELEMETRY_ENABLED=true QWEN_TELEMETRY_TARGET=local QWEN_TELEMETRY_OUTFILE=… OTEL_BSP_SCHEDULE_DELAY=300、隔离 HOME、dummy OpenAI 环境变量。先在 secondary workspace 创建真实 live session(POST /session {cwd: secondary} → 200),然后对两个构建驱动 10 个场景,从 outfile 读取 qwen-code.daemon.request span。
PR head — 9/9 场景产生 span,归因全部正确:
| 场景 | Span 结果 |
|---|---|
POST /session 在 secondary 创建 |
200 · secondary hash(创建时晚绑定归因) |
对 live secondary session 的 GET /session/:id/status |
200 · secondary hash(live-owner 路由) |
GET /session/foo%2Fbar/status |
404 · canonical route,session.id 解码为 foo/bar,无 hash |
GET /session/missing/status、POST /session/missing/prompt |
404 · span 存在,无 hash(不再回退 primary) |
GET /session/:id/export(secondary session id) |
404 · primary hash——文档声明的 primary-bound 例外 |
POST /sessions/archive |
200 · primary hash(pre-resolved) |
GET /session/:id/events SSE 持流 1400 ms |
span 在连接关闭时结算,时长 1405 ms,secondary hash |
GET /session/missing/events 握手失败 |
404 · 正常计数 |
merge-base,同一驱动 — 总共只有 2 个 span,且均错误或有损:POST /session 虽在 secondary 创建却带 primary hash;POST /session/missing/prompt 回退 primary hash;status/export/archive/events 完全没有 span。
4. Wire 一致性与 span 卫生
- 10 个驱动请求在两个构建上返回完全相同的 HTTP 状态——与"仅可观测性改动"的声明一致(也与 serve A/B bot 的结果一致)。
- 原始路径泄漏扫描:9 个 PR request span 中 0 个包含 workspace cwd;只出现 16 位 sha256 hash。
- SIGINT 优雅关停将全部 9 个 request span 刷入 outfile。
- 跨进程 span(
qwen-code.daemon.bridge、qwen-code.daemon.session_start)在两个构建上均存在且不变;无参数路由上的session.id资源属性(daemon:<pid>)为既有行为,两侧一致。
本地未覆盖
- 进程内 HTTP metric sink 无法通过 outfile exporter 观测,因此 SSE 成功流排除计数 / 握手失败正常计数这一层依赖 48 个 middleware 单元测试(正是断言该 seam)。
- Windows/Linux 未在本地验证;远端 CI 在同一 head 全绿(unit 31m48s、Serve A/B 14m43s、web-shell smoke 5m37s)。
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅



What this PR does
This PR adds a declarative telemetry catalog for all 48 explicit legacy
/session,/sessions, and/permissionroutes, with canonical route matching and an audited 41 handler-resolved / 7 primary-bound attribution split.Handler-resolved requests now bind
qwen-code.workspace.hashto the runtime actually selected by session creation, restore, transcript, or live-owner routing. The middleware no longer performs a duplicate live-owner lookup, and unresolved or ambiguous requests omit the workspace hash instead of falling back to the primary workspace.Request-span and metric settlement now distinguishes the long-lived session event stream from bounded request-scoped generation SSE, while preserving the existing heartbeat and daemon-status dashboard exclusions. The design and daemon observability documentation describe the coverage boundary, failure semantics, and intentionally daemon-global metrics.
Why it's needed
PR #6833 completed owner routing for additional legacy session operations but intentionally left telemetry as a follow-up to #6378. Before this change, many explicit legacy routes had no canonical request span, owner-routed routes could be attributed to the primary workspace, and rewind/shell requests could resolve the live owner twice.
Completing the catalog and late-binding workspace attribution makes multi-workspace traces reliable without changing HTTP contracts, SDK behavior, session execution, or metric cardinality.
Reviewer Test Plan
How to verify
telemetry.outfile; confirm an owner-routed secondary request records the secondary hash, a primary-bound exception records the primary hash, and an unresolved multi-workspace request has noqwen-code.workspace.hash.npm run verify:pr; all deterministic checks should pass. Locally,verify:prpassed dependency installation, build, audit, lockfile, isolation, lint, formatting, i18n, schema, typecheck, fast-path bundle, and the complete 739-test serve regression before the long full-unit step hit two unrelated flaky cases: the multi-workspace archived-listing file passed 88/88 on immediate full-file rerun, and the core user-memory timeout passed 1/1 on immediate isolated rerun. Remote CI should confirm the clean-run result.Evidence (Before & After)
N/A — observability-only change with no UI surface.
Tested on
Environment (optional)
macOS, Node.js 22.22.3, npm 10.9.8, local dual-workspace daemon, JSONL telemetry outfile, sandbox disabled.
Risk & Scope
Linked Issues
Refs #6378
Follow-up to #6833
中文说明
本 PR 的改动
本 PR 为全部 48 条显式 legacy
/session、/sessions和/permission路由新增声明式 telemetry catalog,提供 canonical route 匹配,并固定经过审计的 41 条 handler-resolved / 7 条 primary-bound 归因分类。handler-resolved 请求现在会把
qwen-code.workspace.hash晚绑定到 session creation、restore、transcript 或 live-owner 路由实际选择的 runtime。middleware 不再重复执行 live-owner 查询,无法解析或存在歧义的请求会省略 workspace hash,而不是回退到 primary workspace。request span 与 metric 的结算现在会区分长连接 session event stream 和有界的 request-scoped generation SSE,同时保留 heartbeat 与 daemon status 现有的 dashboard 排除语义。设计文档和 daemon observability 文档记录了覆盖边界、失败语义以及刻意保持 daemon 全局聚合的 metrics。
为什么需要
PR #6833 为更多 legacy session 操作完成了 owner routing,但有意把 telemetry 留作 #6378 的后续工作。此改动之前,许多显式 legacy 路由没有 canonical request span,owner-routed 路由可能被归因到 primary workspace,并且 rewind/shell 请求可能重复解析 live owner。
补齐 catalog 和 workspace 晚绑定归因后,多 workspace trace 将更可靠,同时不改变 HTTP 合约、SDK 行为、session 执行或 metric cardinality。
Reviewer 测试计划
验证方式
telemetry.outfile启动 daemon;确认 owner-routed secondary 请求记录 secondary hash,primary-bound 例外记录 primary hash,无法解析的多 workspace 请求不包含qwen-code.workspace.hash。npm run verify:pr,所有确定性检查都应通过。本地verify:pr已通过依赖安装、build、audit、lockfile、isolation、lint、formatting、i18n、schema、typecheck、fast-path bundle 以及完整的 739 项 serve 回归,随后在超长的全量 unit 步骤中遇到两个无关 flaky:multi-workspace archived-listing 文件立即完整重跑为 88/88,通过;core user-memory 超时用例立即单独重跑为 1/1,通过。远端 CI 应确认干净 runner 的结果。证据(改动前后)
N/A — 仅 observability 改动,不涉及 UI。
测试平台
环境(可选)
macOS、Node.js 22.22.3、npm 10.9.8、本地双 workspace daemon、JSONL telemetry outfile、禁用 sandbox。
风险与范围
关联 Issue
Refs #6378
Follow-up to #6833