fix(serve): Canonicalize Live task bridge session IDs - #9819
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
E2E test reportScopeVerified that legacy uppercase transcript spelling remains the storage identity while Live task operations converge on one lowercase canonical bridge entry. Baseline and reproduction
Results
Platform coverage
|
|
Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with |
|
Thanks for the PR! (Re-run on head Template looks good ✓ Problem: observed and maintainer-reported. This is the queued Live/bridge identity residual from the #9341 review, tracked in #9490: a legacy uppercase transcript and its lowercase canonical bridge entry can coexist for one logical session, with shared Conversations directory but separate prompt and event state. The mechanism is documented in the #9490 thread, and the PR's tests are written to fail on the pre-fix behavior. Direction: aligned — unchanged from the previous pass. REST internal restore and ACP Size: not a Stage 0 core path (single package, Approach: still minimal, and the new delta is reviewer-driven end to end: validate persisted ownership before accepting a canonical live owner, reject case-only twins as ambiguous, carry the caller-visible spelling separately for the wire and for metadata reads in the disappearance race, and register voice-resumed sessions by the canonical id. Each edit maps to a round-3 finding; no scope creep. Risk: no elevated risk signals — none of the Stage 1e high-risk paths are touched. Moving on to code review. 🔍 中文说明感谢贡献! (在头 模板完整 ✓ 问题:已观测、由维护者报告。这是 #9341 评审排队在 #9490 的 Live/bridge 身份遗留项:历史大写 transcript 与其小写 canonical bridge entry 可能同时挂在同一逻辑会话上,共享同一个 Conversations 目录,却有各自独立的 prompt 与事件状态。机制记录在 #9490 线程中,PR 的测试按修复前会失败的方式编写。 方向:对齐——与上一轮一致。REST 内部恢复与 ACP 规模:不属于 Stage 0 核心路径(单包 方案:依然是最小化的,且新 delta 完全由评审意见驱动:先验证持久化归属再接受 canonical live owner、大小写孪生冲突按歧义拒绝、为协议返回与消失竞态中的 metadata 读取单独保留调用方拼写、语音恢复会话以规范 id 注册。每处改动都对应第 3 轮的一条发现,无范围蔓延。 风险:无升级风险信号——未触及任何 Stage 1e 高风险路径。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewThe review question this pass was narrower than "is the design right" — the previous pass settled that — it was: do the four round-3 Critical findings actually get fixed? Before reading the delta, my independent answers were: R3-1, canonicalize at the voice-resume producer ( Verified on head
The Test evidence — the PR's own CIAll
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Two pieces of evidence beyond the unit suite. The Sandboxed verification would settle the residual gap: author tested macOS only, and Windows/Linux local runs are not verified — a sponsored Real-scenario testing: N/A for this run — unattended CI, and the change is internal daemon identity handling with no user-visible surface. 中文说明代码审查:本轮的问题比"设计是否正确"更聚焦——上一轮已经解决设计问题——本轮要回答的是:第 3 轮的四条 Critical 发现是否真正被修复? 在读 delta 之前,我的独立方案是:R3-1 在语音恢复的生产端( 已在头
测试证据:本头所有 真实场景测试:本次不适用——无人值守 CI,且改动为内部 daemon 身份处理,无用户可见面。 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — the four round-3 Critical findings are each answered by a targeted fix plus a pinning regression test, verified line-by-line on this head, with CI green and the Serve A/B wire check clean on it; what keeps this from a 5 is that @wenshao's changes-requested round is formally still open — my read is that every finding is addressed, but dismissing that review is a human call. Stepping back: this is what a good review-response cycle looks like. Round 3 found four real regressions the original canonicalization introduced or exposed — the voice producer registering the persisted spelling, case-only twins cross-routing between workspaces, the canonical summary leaking onto the wire, and the disappearance race demoting a canonical id to a storage key — and the answer was four surgical fixes with one deterministic test each, not a rewrite. My independent proposal for every one of them matches what landed. Re-checking the final state: the ambiguity checks are symmetric, already-canonical lookups take exactly the same path as before this PR, there are no remaining What would keep me honest: the stored-owner scan adds a workspace walk for non-canonical ids (bounded and rare, noted in Stage 2), the exact-spelling list-scan residue remains open under #9490 as the PR states, and the author's macOS-only local testing is covered by the Ubuntu CI run rather than duplicated. None of that blocks. Verdict: approve, pinned to 中文说明置信度:4/5 —— 第 3 轮的四条 Critical 发现各自都有针对性修复加钉固回归测试作答,已在本头上逐行核实,CI 全绿、Serve A/B 协议检查干净;没给到 5 分是因为 @wenshao 的 changes-requested 轮次在形式上仍未关闭——我的判断是每条发现都已处理,但撤销该评审是人工决定。 整体来看:这是一次高质量的评审响应循环。第 3 轮找出了初版规范化引入或暴露的四个真实回归——语音生产端注册持久化拼写、大小写孪生跨 workspace 误路由、canonical summary 泄漏到协议、消失竞态把 canonical id 当存储键——而回应是四处外科手术式修复、各配一个确定性测试,不是重写。我对每一条的独立方案与最终落地的实现一致。复核最终状态:歧义检查是对称的,已规范化查询走的路径与本 PR 之前完全相同,不再有任何 如实说明保留意见:存储归属扫描为非规范 id 增加了一次 workspace 遍历(有界且罕见,Stage 2 已述);精确拼写列表扫描的残留按 PR 所述仍挂在 #9490 项下;作者仅在 macOS 本地测试,由 Ubuntu CI 运行覆盖而非重复验证。均不构成阻塞。 结论:批准,钉固在 — Qwen Code · qwen3.8-max Reviewed at |
|
⏸️ Deferring to @wenshao — the review itself is clean (the fix matches the queued #9490 scope and pins the right behavior), but this commit's unit suite never ran in CI: the Ubuntu job trips main's pre-existing workflow-size ratchet ( |
🩺 serve daemon A/BBuilt the PR base vs this PR head ✅ No response changes against the PR base across 12 scenario(s). — Qwen Code · serve A/B |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not explored to full depth (tool budget reached): "agent 6c": did not run the new vitest specs ( live-task-service.test.ts ) to confirm they pass..
Not reviewed: reverse audit — stopped before round 4 by the review time budget.
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未探索到全部深度(达到工具调用预算):"agent 6c":did not run the new vitest specs ( live-task-service.test.ts ) to confirm they pass.。
未审查:反向审计——评审时间预算不足,未能开始第 4 轮。
— qwen3.8-max via Qwen Code /review (v0.22.0)
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No blocking issues. LGTM! ✅
Not explored to full depth (tool budget reached): "agent 6c": did not trace the bridge's restoreSession cold-spawn path end-to-end to confirm which sessionId it passes into the spawned CLI child — Finding 1's mechanism d…; "agent 6a": none — no check was cut short..
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/live/live-task-service.ts:54 — [review] R1-1 two-spelling invariant (bridge canonical vs storage persisted) undocumented; summary.sessionId spelling is path-dependentpackages/cli/src/serve/live/live-task-service.ts:1079 — [review] R1-2 ensureResident missing-metadata SessionNotFoundError now carries the canonical id, unpinned by tests; inconsistent with locateTask failure pathspackages/cli/src/serve/live/live-task-service.ts:1143 — [review] R1-3 canonical reference to a legacy mixed-case session resolves only while resident; cold-path storage probes are exact-spellingpackages/cli/src/serve/live/live-task-service.ts:1145 — [review] R1-4 no test pins that resolveLiveSessionOwner receives the canonical id; revert mutation survives the suitepackages/cli/src/serve/live/live-task-service.ts:808 — [review] R1-5 wait cursors embed the caller's raw id spelling; a spelling flip between polls resets the cursor and misses wake events
中文说明
无阻断问题。LGTM!✅
未探索到全部深度(达到工具调用预算):"agent 6c":did not trace the bridge's restoreSession cold-spawn path end-to-end to confirm which sessionId it passes into the spawned CLI child — Finding 1's mechanism d…;"agent 6a":none — no check was cut short.。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 5 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
@qwen-code /triage |
|
Sandboxed verification: ❌ not passed — blocked (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: 113 passed · 0 failed · 113 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 阻塞(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:113 通过 · 0 失败 · 113 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification reportPR 9819 — fix(serve): Canonicalize Live task bridge session IDsVerdict: 中文摘要
Central claim + A/BCentral claim: addressing a mixed-case persisted task through the Live task surface must converge every bridge-facing operation (owner lookup, summary lookup, resume, prompt dispatch, Conversations directory materialization/relocation, event polling) on the canonical UUID, while storage reads and wire responses keep the persisted spelling. Harness:
Head arm: 34/34; base arm: 30/30 (base assertions encode the bug reproduction as the expectation, so its reds are the point). Reviewer Test Plan walkthrough
FindingsF1 (blocking) — Voice-path registration still uses the persisted spelling; canonical-only probing now duplicates those sessions
Reproduction (compiled code,
Blast radius: every Minimal suggested fix (measured for collateral only)Canonicalize at the voice registration site, mirroring the pattern this PR applies elsewhere: // live-session-coordinator.ts, createOrResumeCoordinator()
const resumed = await runtime.bridge.resumeSession({
sessionId: normalizeSessionIdForLookup(candidate.sessionId),
...
});Measured in a scratch copy: F2 (completeness) —
|
|
Triage re-run completed without a new review. The bot already has a review of its own on 机器人在 The stage comments above were updated with the latest result. View workflow run. 上方各阶段评论已更新为最新结果。查看工作流运行。 |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Handled the incremental review findings on current head.
Verification: |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
No issues found. LGTM! ✅
Not explored to full depth (tool budget reached): "agent 5": running the live-task-service.test.ts vitest suite against HEAD to confirm the traced tests pass (worktree has no node_modules ; npm ci + monorepo build ex….
中文说明
未发现问题。LGTM!✅
未探索到全部深度(达到工具调用预算):"agent 5":running the live-task-service.test.ts vitest suite against HEAD to confirm the traced tests pass (worktree has no node_modules ; npm ci + monorepo build ex…。
— qwen3.8-max via Qwen Code /review (v0.22.0)
wenshao
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — npm test --workspace="packages/cli" exceeded the 540-second review budget; the focused Live task suite passed 21/21.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
Not reviewed: test-efficacy — the probe harness returned inconclusive with harnessValidated=null and no green baseline.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/cli/src/serve/live/live-task-service.test.ts:987 — [review] resident 测试未固定 canonical 直接查询
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)
[Critical] R3-4: ensureResident 的消失竞态会把 canonical live-summary ID 当作持久化 transcript ID。大写 transcript 由小写 bridge entry 驻留后,若 entry 在 locateTask 与 ensureResident 的两次探测之间消失,metadata 读取会使用小写 task.summary.sessionId,在大小写敏感文件系统上找不到大写 transcript,并抛错而不是恢复。Witness: not run — 需要精确控制该并发窗口;代码路径确定地把 canonical summary ID 传给 exact-ID storage 读取。
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — npm test --workspace="packages/cli" exceeded the 540-second review budget; the focused Live task suite passed 21/21。
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
未审查:test-efficacy — the probe harness returned inconclusive with harnessValidated=null and no green baseline。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
[Critical] R3-4: ensureResident 的消失竞态会把 canonical live-summary ID 当作持久化 transcript ID。大写 transcript 由小写 bridge entry 驻留后,若 entry 在 locateTask 与 ensureResident 的两次探测之间消失,metadata 读取会使用小写 task.summary.sessionId,在大小写敏感文件系统上找不到大写 transcript,并抛错而不是恢复。Witness: not run — 需要精确控制该并发窗口;代码路径确定地把 canonical summary ID 传给 exact-ID storage 读取。
— gpt-5.6-sol via Qwen Code /review (v0.22.0)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Addressed review round 3 in
Validation: 49 focused Live tests passed; the canonical ACP and REST restore tests passed; |
|
@qwen-code /triage |
|
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: 221 passed · 0 failed · 221 total Flakiness gate: ✅ 2 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:221 通过 · 0 失败 · 221 总计 抖动门:✅ 2 changed test file(s) x 5 identical rounds, no divergence Verification reportPR 9819 — fix(serve): Canonicalize Live task bridge session IDs (round 2)Verdict: 中文 — 判定:✅ 通过(agent 判定)本轮为复审。上一轮的阻塞项 F1(语音 resume 路径以持久化拼写注册 bridge entry,导致 canonical-only 探测产生重复 entry)已被按上轮给出的最小修复建议原样修复(
Previous-finding status (round 1 → new head)
Central claim + A/BCentral claim (carried over, re-measured): addressing a mixed-case persisted task through the Live task surface must converge every bridge-facing operation on the canonical UUID, while storage reads and wire responses keep the persisted spelling. Harness:
S7 is the round-1 blocker, re-measured: at the previous head the same configuration produced Reviewer Test Plan walkthrough (re-measured)
Vacuity and mutationInversion ( Mutation matrix (
Adjudication of the survivor: FindingsN1 (low, non-blocking) — two pre-existing registration sites consume persisted legacy ids without canonicalizing
Bounds: no path in the post-PR state can produce the triggering records — every caller-facing id source is now canonicalized (REST Note (informational) — PR text test count driftThe description's "focused Live task suite passes 21 tests" predates this round's additions; the suite now holds 24 tests (24/24 at head). No action needed. Not covered
MethodologyEnvironment: CI verify container ( Flakiness gate logEvidence imagesHarness scripts and raw logs are in the workflow run artifacts (7-day retention). — Qwen Code · sandboxed verification |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. Verified the three Criticals from the last round are addressed at head e066ac0:
- R3-1 (voice resume producer divergence): live-session-coordinator now registers resumed sessions via
normalizeSessionIdForLookup(candidate.sessionId), so the voice path converges on the same canonical bridge entry the lookup probes — no duplicate resume for a legacy uppercase transcript. Pinned by the new mixed-case registration test. - R3-2 (cross-workspace case-twin routing): when the caller's spelling differs from the canonical bridge id, locateTask now scans stored runtimes for the exact persisted spelling and refuses as ambiguous when the stored owner and the live owner disagree (or multiple runtimes hold the exact spelling) — the A-uppercase-stored / B-lowercase-resident shape fails closed instead of selecting the live twin and skipping the ownership scan. Pinned by the storage-vs-live owner test.
- R3-3 (wire spelling leak): LocatedTask now carries the caller-visible
threadIdseparately from the bridge id, and both the latestTurn fallback and the metadata reads use it — the wire response keeps the persisted uppercase identity instead of leaking the canonical lowercase spelling. Pinned by the no-user-turn wire-id test.
CI at approval time: 20 checks passing, none failing; review-pr automated check still pending on this head. All threads resolved.
|
Released in v0.22.2. |










What this PR does
This PR gives each Live task two explicit identities: the persisted transcript keeps its authoritative on-disk spelling, while every in-memory bridge operation uses the existing canonical caller-visible UUID. Live owner lookup, summary lookup, resume, prompt dispatch, Conversations directory relocation, and event polling now converge on that canonical bridge ID without changing the task ID returned on the wire.
Why it's needed
A legacy transcript can retain an uppercase UUID while ordinary REST or ACP restore registers the same session under its lowercase canonical UUID. Live task lookup previously carried the persisted spelling into the bridge, so it could miss an already-running canonical entry and resume a second entry for the same logical transcript. The duplicate entries shared one canonical Conversations directory but had separate prompt and event state. This closes the residual Live/bridge identity issue tracked in #9490 without broadening the lock-key or storage lookup work that remains there.
Reviewer Test Plan
How to verify
git diff --checkpass.Evidence (Before & After)
N/A — internal daemon identity handling with no TUI change.
Tested on
Environment (optional)
macOS 26.4.1 arm64, Node.js v22.22.3, npm 10.9.8; unit tests and repository build/typecheck.
Risk & Scope
Linked Issues
Part of #9490.
中文说明
本 PR 做了什么
本 PR 为每个 Live task 明确区分两种身份:持久化 transcript 保留磁盘上的权威拼写,所有内存 bridge 操作则统一使用现有的调用方可见 UUID 规范形式。Live owner 查询、summary 查询、恢复、prompt 分发、Conversations 目录迁移和事件轮询现在都会收敛到同一个 canonical bridge ID,同时不改变协议返回的 task ID。
为什么需要
历史 transcript 可能保留大写 UUID,而普通 REST 或 ACP 恢复会把同一会话注册到小写 canonical UUID 下。此前 Live task 查询会把持久化拼写继续传给 bridge,因此可能漏掉已经运行的小写 entry,并为同一逻辑 transcript 再恢复一个 entry。两个 entry 共享同一个规范化 Conversations 目录,却拥有独立的 prompt 与事件状态。本 PR 收口 #9490 中遗留的 Live/bridge 身份问题,不扩展该 issue 中仍待处理的锁 key 或存储查找工作。
Reviewer 测试计划
如何验证
git diff --check均通过。证据(Before & After)
N/A —— 内部 daemon 身份处理,无 TUI 变化。
测试平台
环境(可选)
macOS 26.4.1 arm64、Node.js v22.22.3、npm 10.9.8;单元测试以及仓库 build/typecheck。
风险与范围
关联 Issue
#9490 的部分修复。