Skip to content

fix(webui): Make cross-session switching transactional - #8882

Merged
doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:fix/transactional-cross-session-switching
Aug 11, 2026
Merged

doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:fix/transactional-cross-session-switching

Conversation

@doudouOUC

Copy link
Copy Markdown
Collaborator

What this PR does

This PR makes modern WebUI load/resume switches to a different logical session transactional. The current session remains the visible owner while the target restores and is replayed into an isolated, bounded staging store; only a fully staged target that still wins the lifecycle/deadline arbitration is committed. Failed, timed-out, superseded, malformed, or stale targets are discarded and detached best-effort without clearing the current transcript, stopping its event stream, or replacing its connection state.

The restore coordinator permits one ordinary restore RPC at a time, coalesces identical targets, retains only the latest queued target, and fences late results by exact attachment and environment identity. Commit installs the target transcript, session/client/workspace references, connection state, staged notices and side channels in one synchronous ownership handoff before the public load promise resolves, then starts the prepared target runner without issuing a second restore.

The main WebShell provider now stays mounted across modern controlled session/workspace changes. Navigation paths share transactional ownership fences and write gates, preserve source metadata while a target is pending or fails, hide stale metadata on the first committed target frame, and delay the scheduled-run catch-up timeout until restore has committed. Daemons that explicitly lack client_identity keep the existing keyed, detach-first compatibility behavior; unknown capabilities and malformed modern ownership fail closed.

Why it's needed

Before this change, selecting another session immediately detached the current session, aborted its event stream, cleared its transcript, and published the target as loading before the target restore had succeeded. A large-session timeout or other restore failure therefore left the user on an empty/error target and stopped the conversation that had been usable. PR #8691 made restore deadlines safe and observable, and PR #8833 fenced late attachment work; this PR adds the missing client-side transaction boundary for ordinary and controlled cross-session load/resume.

Reviewer Test Plan

How to verify

Start with session A connected and containing a visible transcript, then delay the completed response for loading session B. Confirm that A stays connected, retains its transcript, continues receiving live events, and still accepts existing control operations while B is pending. Release B and confirm that the first committed state consistently contains B's session, client, workspace, and replay, with A detached only after commit.

Repeat with B returning a structured HTTP 504 and with B being superseded by later targets. Confirm that A remains usable after failure, only one ordinary restore is in flight, an intermediate queued target is never sent, and a late result cannot replace the latest owner. For a controlled workspace target, confirm that unresolved/failed workspace resolution keeps A mounted and rolls the host back once without a retry loop.

The focused regression suites cover WebUI restore arbitration, pure staging, attachment cleanup, controlled transitions, WebShell owner fences, navigation, queued prompts, background tasks, and artifacts. The real-daemon integration test exercises delayed-success and structured-504 scenarios through qwen serve.

Evidence (Before & After)

Before: deterministic baseline runs on pre-change main showed action-driven and controlled switches detach A, abort A's stream, clear its transcript, and expose B/loading before B settled; a rejected B left an empty B/error state. A same-workspace controlled target also remounted the session provider.

After: the real-daemon integration test completed B's restore but withheld the response for about five seconds. During that gate A remained connected, accepted a control request, and received a new live event; releasing the response atomically installed B. A structured B restore 504 preserved A and surfaced session_restore_timeout with HTTP status 504. Focused verification passed 279 WebUI action/provider tests and 418 affected WebShell tests; repository build, typecheck, bundle, and focused ESLint also passed.

Tested on

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

Environment (optional)

macOS Darwin 25.4.0 arm64, Node.js v22.22.3, npm 10.9.8. Real-daemon integration used QWEN_SANDBOX=false with the built qwen serve bundle and a mock ACP child.

Risk & Scope

  • Main risk or tradeoff: This is a cross-package WebUI/WebShell ownership change at the repository's 1,000+ production-line advisory threshold (9 production files, raw net +1,500), so restore arbitration, React lifecycle handoff, and host navigation deserve maintainer review. Staging temporarily holds A's store and B's replay, and best-effort detach failure can retain an invisible client reference until the existing reaper runs.
  • Not validated / out of scope: Same-logical-session reload, clientId-only handoff, epoch/ring resync and live-journal repair transactions are reserved for PR3c; branch creation/adoption and its timeout contract are reserved for PR3d. JSONL streaming/selective restore, checkpointing, a global attachment scheduler, SDK self-heal, and an 80 MiB CI timing threshold are also out of scope. Windows and Linux were not tested locally.
  • Breaking changes / migration notes: No daemon, REST, ACP, or WebSocket protocol changes and no migration are required. The connection state and provider expose additive optional transition/owner hooks. Daemons known not to support client_identity retain the prior destructive switching behavior.

Linked Issues

Refs #8678

中文说明

本 PR 做了什么

本 PR 将现代 WebUI 中切换到不同逻辑会话的 load/resume 改为事务化。目标会话恢复并在隔离、有界的 staging store 中重放期间,当前会话仍然是可见 owner;只有完成全部 staging 且仍通过生命周期/截止时间仲裁的目标才会提交。失败、超时、被替换、格式错误或过期的目标会被丢弃并尽力 detach,不会清空当前 transcript、停止其事件流或替换其连接状态。

restore coordinator 同时只允许一个普通 restore RPC,合并相同目标,只保留最新排队目标,并使用精确 attachment 与环境 identity 隔离迟到结果。commit 在公开 load promise resolve 前,通过一次同步 ownership handoff 安装目标 transcript、session/client/workspace 引用、connection state、已 staging 的 notices 和 side channels,然后直接启动准备好的目标 runner,不会再次发起 restore。

现代受控 session/workspace 变化期间,主 WebShell provider 现在保持挂载。各导航入口共享事务化 ownership fence 和写入 gate,目标 pending 或失败时保留源会话 metadata,目标提交的第一帧隐藏过期 metadata,并将 scheduled-run 的 catch-up timeout 延后到 restore commit 之后。明确缺少 client_identity 的 daemon 继续使用现有 keyed、detach-first 兼容行为;capability 未知和现代 ownership 格式错误则 fail-closed。

为什么需要

变更前,选择另一个会话会在目标 restore 成功之前立即 detach 当前会话、终止其事件流、清空 transcript,并把目标发布为 loading。因此大型会话超时或其他 restore 失败会让用户停留在空白/错误目标,同时停止原本可用的会话。PR #8691 已使 restore deadline 安全且可观测,PR #8833 已隔离迟到 attachment 工作;本 PR 补上普通及受控跨会话 load/resume 所缺少的客户端事务边界。

Reviewer 测试计划

如何验证

先连接 session A 并确保存在可见 transcript,然后延迟 session B 已完成 load 的响应。确认 B pending 期间 A 仍保持 connected、保留 transcript、继续接收实时事件,并且现有控制操作仍能使用。释放 B 后,确认第一个 committed 状态中的 session、client、workspace 和 replay 全部一致属于 B,并且 A 只在 commit 后才 detach。

再让 B 返回结构化 HTTP 504,并让 B 被后续目标替换。确认失败后 A 仍可用、同时只有一个普通 restore 在运行、中间排队目标不会发送、迟到结果不能替换最新 owner。对于受控 workspace 目标,确认 workspace 解析未完成或失败时 A 保持挂载,host 只回滚一次且不会形成重试循环。

focused regression suites 覆盖 WebUI restore 仲裁、pure staging、attachment 清理、受控切换,以及 WebShell owner fence、导航、queued prompts、background tasks 和 artifacts。真实 daemon integration test 通过 qwen serve 覆盖 delayed-success 与结构化 504 场景。

证据(变更前后)

变更前:在变更前 main 上的确定性 baseline 运行表明,action-driven 和 controlled 切换会在 B settle 前 detach A、终止 A 的 stream、清空 transcript 并显示 B/loading;B reject 后停留在空白 B/error 状态。同 workspace 的受控目标还会 remount session provider。

变更后:真实 daemon integration test 完成了 B restore,但将 response 暂停约五秒。在 gate 期间 A 保持 connected、接受 control request 并收到新的 live event;释放 response 后原子安装 B。B 的结构化 restore 504 保留 A,并显示 session_restore_timeout 与 HTTP 504。focused verification 通过 279 个 WebUI action/provider 测试和 418 个受影响 WebShell 测试;仓库 build、typecheck、bundle 与 focused ESLint 也全部通过。

测试平台

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

环境(可选)

macOS Darwin 25.4.0 arm64,Node.js v22.22.3,npm 10.9.8。真实 daemon integration 使用 QWEN_SANDBOX=false、已构建的 qwen serve bundle 和 mock ACP child。

风险与范围

  • 主要风险或取舍:这是跨 WebUI/WebShell package 的 ownership 变更,达到仓库 1,000+ 生产行 advisory 阈值(9 个生产文件,原始净增 +1,500),因此 restore 仲裁、React lifecycle handoff 与 host navigation 需要 maintainer review。staging 会短暂同时持有 A store 与 B replay;best-effort detach 失败可能使不可见 client reference 保留到现有 reaper 执行。
  • 未验证/范围外:same-logical-session reload、clientId-only handoff、epoch/ring resync 与 live-journal repair transaction 留给 PR3c;branch creation/adoption 及其 timeout contract 留给 PR3d。JSONL streaming/selective restore、checkpoint、全局 attachment scheduler、SDK self-heal 和 80 MiB CI 时延阈值也不在本 PR 范围内。Windows 与 Linux 未在本地测试。
  • Breaking change / 迁移说明:不修改 daemon、REST、ACP 或 WebSocket 协议,不需要迁移。connection state 与 provider 仅增加可选 transition/owner hooks。明确不支持 client_identity 的 daemon 保留原有 destructive switching 行为。

关联 Issue

Refs #8678

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

E2E verification report

Validated the final PR head 175640d351e166ea801b174442e90aa49e64389f against origin/main@95e17691a97e090f28f3542a013db6319c2a8895.

Baseline

On pre-change main, both action-driven and controlled A→B switches detached A, aborted its SSE, cleared its transcript, and exposed B/loading before B settled. Rejecting B left an empty B/error state, and a same-workspace controlled target remounted the session provider.

Real-daemon scenarios

QWEN_SANDBOX=false npx vitest run --root ./integration-tests --poolOptions.forks.minForks 2 --poolOptions.forks.maxForks 2 cli/qwen-serve-webui-session-switching.test.ts --reporter=verbose

  • Delayed completed response: the daemon completed B restore while the SDK response was gated. A stayed connected, accepted a control request, and received a new live event. Releasing the response atomically installed B's owner and transcript. Passed in 6.4 s.
  • Structured restore timeout: B returned HTTP 504 with code=session_restore_timeout and retryable=true. The public switch rejected with the structured status while A remained connected and retained its transcript. Passed in 6.2 s.

Result: 2/2 passed.

Additional verification

  • WebUI provider/actions: 224/224 + 55/55 passed.
  • Affected WebShell suites: 418/418 passed.
  • WebUI and WebShell typecheck, focused ESLint, repository build, repository typecheck, and bundle: passed.
  • Production diff budget: 9 files, +1848/-348, raw net +1500.

The test environment emitted existing React act(...) warnings; assertions and daemon process outcomes passed. The approximately 80 MiB manual memory/trace run remains intentionally outside CI and was not repeated because this PR does not optimize JSONL or replay cost.

@github-actions

Copy link
Copy Markdown
Contributor

Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration.

中文

请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。

doudouOUC added a commit to doudouOUC/qwen-code that referenced this pull request Aug 11, 2026
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review follow-up

Review item Outcome
R1-3 passive assistant timer Fixed in 815f838a7c; commit clears the source timer before target prompt state is published.
R1-16 retry settlement across reattach Fixed in 815f838a7c; logical session/workspace ownership now settles retry state.
R1-17 recap/BTW placeholder Fixed in 815f838a7c; settlement survives same-session reattachment without crossing workspace boundaries.
R1-22 prompt/cancel/shell completion Fixed in 815f838a7c; logical-owner completion is preserved and an active replacement prompt is not finalized by an old abort.
R1-28 queued edit data loss Fixed in 815f838a7c; confirmed removal restores payload exactly once, including same-id and cross-session owner changes.
R1-30 concurrent branch Fixed in 815f838a7c; concurrent source-bound branch admission is rejected and stale candidates are detached without handoff.
R1-39 deferred mid-turn input Fixed in 815f838a7c; unaccepted input is recovered before owner reset and confirmed edit removal avoids loss or duplication.
R1-9 real-daemon integration Verified locally against the built CLI: 2/2 focused tests pass.
R1-40 branch commit assertion Addressed with the Critical branch regression coverage; the surviving handoff must reach the connected target.
Remaining Suggestions R1-7, R1-8, R1-10, R1-11, R1-12, R1-23, R1-24, R1-25, R1-27, R1-31, R1-32, R1-37, R1-45 Deferred to follow-up under the repository rule for mature PRs after roughly five review rounds; this round is limited to Critical correctness, data-loss, and regression fixes. Each inline thread records the deferral.

Verification on 815f838a7c:

  • WebUI session actions/provider: 290/290 passed.
  • WebShell App/queued prompts: 407/407 passed.
  • WebUI and WebShell package typechecks passed.
  • WebUI production build passed.
  • Focused real-daemon transactional switching E2E: 2/2 passed.
  • Affected ESLint checks and git diff --check passed.
  • Two consecutive open-ended diff/test audits found no remaining actionable Critical correctness issue in the updated scope.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Handled the second review round on head e3e0825.\n\n| Item | Disposition |\n| --- | --- |\n| R2-1 | Fixed: plan preparation uses an operation token; same-ID and A→B→A regressions covered. |\n| R2-2 | Fixed: model select/delete share an operation token; same-ID and ABA regressions covered. |\n| R2-3 | Fixed: blocked turn-error retry no longer consumes its latch and remains usable after unblock. |\n| R2-4 | Fixed: same-logical legacy loads cannot restart the runner while a transactional target is pending. |\n| R2-7, R2-8, R2-10, R2-11, R2-12, R2-13 | Deferred as Suggestions under the repository policy for mature PRs after roughly five review rounds; each thread records the focused follow-up scope. |\n\nVerification:\n- WebUI actions + Provider: 291 tests passed.\n- WebShell App + WorkspaceSessionProvider: 380 tests passed.\n- Six focused same-ID/ABA/write-block regressions passed.\n- Independent Provider probe confirmed A stays connected, B stays preparing, and only B restore runs.\n- WebUI and WebShell typecheck passed.\n- WebUI build, affected ESLint, formatting, and diff checks passed.\n- Two consecutive clean audits found no remaining actionable correctness issue.

yiliang114
yiliang114 previously approved these changes Aug 11, 2026

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. The transactional core holds up under an adversarial pass: commitCrossSession is a fully synchronous ownership swap (microtask-batched store notification, one React batch — no observer-visible intermediate state), the arbitration (intent identity, lifecycle, env generation, deadline, source logical identity) re-runs inside the commit with no await between staging and commit, discard paths only retire the candidate without touching the current session's store/refs/stream, and the coordinator serializes with latest-queued-only and identical-target coalescing. No sessionId-only ownership is reintroduced, double-commit and commit-after-discard are structurally impossible, and all prior rounds' Criticals (round-2's seven, round-3's four, plus round-1's 89 broken unit tests) are verifiably fixed at head with 96 new cases and a real-daemon E2E. CI green on head.

Two P2 hardening items for fast-follow, not blockers: (1) the pump accepts a candidate attachment whose clientId differs from the requested id (DaemonSessionProvider.tsx:3440-3448 never compares candidate.clientId to requestClientId, and persistStableClientId persists the echoed one) — that weakens the #8833 exact-attachment fencing to 'some attachment identity' if a daemon ever echoes a different id; reject or persist the requested id and pin with a test. (2) 'bounded staging' only bounds side queues: the staging store is created with maxBlocks MAX_SAFE_INTEGER and the replay is synchronous, so an incident-class 76 MiB target transiently holds ~3x block arrays and blocks the main thread; deliberate and pinned by tests, not a regression vs legacy, but cap staging at maxBlocks or chunk across macrotasks and document the peak cost. Plus the known-deferred P3s (silent pump stall when client_identity capability is missing at pump time until the watchdog fires; releaseSession only cancels a pending transition when the current session matches). Ship it.

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

doudouOUC added a commit to doudouOUC/qwen-code that referenced this pull request Aug 11, 2026
@doudouOUC
doudouOUC force-pushed the fix/transactional-cross-session-switching branch from e3e0825 to 824dfa5 Compare August 11, 2026 07:10
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased the PR onto current main at a863f6a and resolved the single semantic conflict in the session-actions test harness by preserving both upstream restore-session injection and this PR's prompt-status injection.\n\nPost-rebase verification on head 824dfa5:\n- WebUI actions + Provider: 292 tests passed.\n- WebShell App + WorkspaceSessionProvider + queued-prompts DOM: 423 tests passed.\n- WebUI and WebShell typecheck passed.\n- WebUI build, affected ESLint, conflict-marker scan, formatting, and diff checks passed.\n- Range-diff and two clean audits found no unintended semantic change.

doudouOUC added a commit to doudouOUC/qwen-code that referenced this pull request Aug 11, 2026
@doudouOUC
doudouOUC force-pushed the fix/transactional-cross-session-switching branch from 824dfa5 to 5651663 Compare August 11, 2026 08:20
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Rebased again onto current main (1a2c5026b2) and resolved the semantic overlap with #8798 while preserving both stable message-ID reconciliation and transactional owner fencing.

The integration audit found and fixed three merge-specific Critical races: a stable-ID mid-turn payload can no longer disappear during same-session reattachment; old-owner reconciliation cannot write into the replacement attachment or fall through after the transition write gate closes; and pending payloads remain scoped to their workspace across A→B→A while authoritative snapshots/events retire them so they cannot be resurrected.

Post-rebase verification on head 5651663d82:

  • Stable-ID/owner/write-gate regressions: 7/7 passed.
  • Queued-prompt hook suites: 83/83 passed.
  • WebShell App + ChatPane: 446/446 passed.
  • WebUI actions/provider (independent verification): 296/296 passed.
  • Repository build and repository typecheck passed.
  • WebUI/WebShell package typechecks, affected ESLint/Prettier, conflict-marker scan, and git diff --check passed.
  • Focused real-daemon transactional switching E2E remained 2/2 passed before the final conflict-only hook hardening.
  • Two consecutive open-ended audits of the final diff found no remaining actionable correctness issue.

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

Review — transactional cross-session switching

Reviewed range 1a2c5026b..5651663d8 (4 commits). The transactional-switch machinery is well-engineered and holds up under close inspection: the commit is a single synchronous ownership handoff (re-validating desired-intent identity / lifecycle / environment generation / absolute deadline immediately before install), A's buffered events cannot contaminate B's store, late results are fenced by exact attachment + environment identity, unmount fencing is careful, and the WebShell owner-guard sweep is consistent across navigation paths. No Critical issues found.

One Important and three Minor items are inline:

  1. actions.ts:1549 (Important) — branchSession's in-flight flag is cleared only when the raw request settles; withActionTimeout does not abort it, so a slow branch blocks every requireStableSession action (prompt / model / rename / recap …) for up to the SDK's 30 s fetch timeout. Clear the flag in a whole-operation finally.
  2. useQueuedPrompts.ts:625 — dropped mid-turn admissions leak in pendingMidTurnAdmissionsRef (one entry per interrupted mid-turn submission, app lifetime).
  3. ChatPane.tsx:483 — this useQueuedPrompts instance lacks writeBlocked; mid-switch submits fail at the actions layer and land in the queue as failed instead of being rejected at the composer.
  4. DaemonSessionProvider.tsx:3300 — a throwing onSessionTransitionCommit is swallowed after B is installed; the self-heal-on-next-render contract deserves a comment or a retry on failure.

Scope note: the acp-bridge / cli-serve / SDK daemon changes described in the PR body are not part of this diff (they landed in the base via #8691/#8833); this review covers the 23 files actually changed here.

中文说明

审查范围 1a2c5026b..5651663d8(4 个 commit)。事务化切换机制本身扎实:commit 是单次同步 ownership 交接(安装前重新校验 desired-intent 身份 / 生命周期 / 环境代数 / 绝对截止时间),A 的缓冲事件不会污染 B 的 store,迟到结果按精确 attachment + 环境身份隔离,卸载 fencing 严谨,WebShell owner-guard 扫描一致。无 Critical。

Important 1 项 + Minor 3 项见行内评论:branchSession in-flight 标志的无限期延迟耦合;pendingMidTurnAdmissionsRef 泄漏;ChatPane 未传 writeBlocked;抛异常的 onSessionTransitionCommit 被吞。另说明:PR 描述中提到的 acp-bridge / cli-serve / SDK daemon 改动不在本 diff 内(经 #8691/#8833 已合入 base),本审查只覆盖实际改动的 23 个文件。

Comment thread packages/webui/src/daemon/session/actions.ts
Comment thread packages/web-shell/client/hooks/useQueuedPrompts.ts
Comment thread packages/web-shell/client/components/ChatPane.tsx
Comment thread packages/webui/src/daemon/session/DaemonSessionProvider.tsx
yiliang114
yiliang114 previously approved these changes Aug 11, 2026

@yiliang114 yiliang114 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-approving at head 5651663 after the two rebases: commitCrossSession is byte-identical to the head I approved, the arbitration/discard/coordinator code is untouched, no tests were removed, and the genuine new work checks out — the O(B) single-dispatch replay (fixing my P2-2's multiplicative transient allocation, with a batch-size regression test), the pendingMidTurnAdmissionsRef ledger with workspace-scoped owner-token identity and fenced old-owner reconciles (14 new tests), and the #8883 adaptation clearing failed loads without auto-fresh attach.

Remaining, all bounded fast-follows: my P2-1 (candidate clientId still accepted on truthiness without equality vs requestClientId, and commit persists the candidate's id) is open but re-scoped — types.ts now documents clientId as not an ownership boundary for stable-id mid-turn queues and the design doc defers client-id replacement; still a cheap unperformed check. New P2 from ytahdn verified: branchSession's setSourceBoundOperationInFlight clears only when the raw branch request settles and withActionTimeout is a Promise.race, so a hung branch serializes every requireStableSession action for up to the 30s fetch timeout — a whole-operation finally fixes it. P3s: pendingMidTurnAdmissionsRef never deletes non-matching entries on post-switch rejection (one orphaned entry per interrupted submission), ChatPane's useQueuedPrompts lacks writeBlocked so mid-switch pane submits land as failed queue entries instead of composer rejection, and a throwing onSessionTransitionCommit after B install self-heals one frame late. CI green on head, identical skip profile to the old head. Ship it with the fast-follows.

ytahdn
ytahdn previously approved these changes Aug 11, 2026

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

✅ Approved

Approved after the review discussion resolved on the four inline threads: three suggestions were withdrawn (the branch in-flight interlock, the mid-turn admission retention, and the commit-observer boundary are all intentional and correctly reasoned), and the remaining ChatPane writeBlocked follow-up is accepted as a tracked deferral per the review-round policy. No blockers remain.

Note: Test (macos/windows) and Integration Tests (CLI, No Sandbox) are skipped on this head (the existing repo-wide pattern); the jobs that ran — Test (ubuntu), Desktop Shell (ubuntu/windows), web-shell E2E Smoke, visuals — are green.

中文说明

审查通过。4 条行内评论线程已闭环:3 条撤回(branch in-flight 互锁、mid-turn admission 保留、commit observer 边界均为有意设计且论证成立),剩余 ChatPane writeBlocked 一条作为延后项记录(符合审查轮次策略)。无遗留 blocker。说明:head 上 macos/windows 测试与 Integration Tests 被跳过(仓库既有模式),实际运行的 ubuntu Test、Desktop Shell、web-shell E2E 与 visuals 均绿。

@doudouOUC
doudouOUC dismissed stale reviews from ytahdn and yiliang114 via b700a4c August 11, 2026 09:32
@doudouOUC
doudouOUC force-pushed the fix/transactional-cross-session-switching branch from 5651663 to b700a4c Compare August 11, 2026 09:32
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Conflict resolution update:

  • Rebasing onto origin/main@096bb0e3b9 is complete and the branch was force-pushed at b700a4cef2.
  • The merge combines the new workspace-scoped session catalog with PR fix(webui): Make cross-session switching transactional #8882's attachment-owner fences. Worktree metadata, queued shell invalidation, turn completion, and rename reconciliation remain scoped to the captured source session/workspace.
  • Added a regression test proving a deferred rename still updates the source workspace catalog after navigation without writing stale UI state into the new session.
  • Verified focused WebShell/WebUI suites, package and repository typechecks, repository build, affected-file ESLint, diff checks, and the real-daemon session-switching E2E (2/2). Two open-ended post-resolution audits and an independent test-engineer audit found no remaining actionable correctness issue.

The PR is mergeable with no unresolved current review threads. Checks for the rebased SHA are still finishing; the zero-duration red entries belong to a superseded workflow run that GitHub marked cancelled, so they were not retried.

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

✅ Approved (re-approved after rebase)

Re-reviewed the updated head b700a4cef (rebase onto the newer main + one new commit):

  • The rebase is content-clean — every PR-specific file (DaemonSessionProvider, actions, types, useQueuedPrompts, WorkspaceSessionProvider, useBackgroundTasks, useSessionArtifacts, midTurnDedup, the daemon side channels, integration test, design doc) is byte-identical to the previously approved head; the diff on the shared App.tsx/ChatPane files is entirely the newer base's own progress (#8891/#8914), not conflict-resolution churn.
  • The new commit b700a4cef is sound — it moves the owner.isCurrent() gate in the rename flow to run after the catalog reconciliation and before the local status notice / error report, so a confirmed server-side rename still reconciles into the catalog after its source attachment was replaced, with a dedicated regression test (reconciles a confirmed rename after its source attachment is replaced). Matches its stated purpose.
  • Prior discussion conclusions are unchanged: three suggestions withdrawn (branch in-flight interlock, mid-turn admission retention, commit-observer boundary), one accepted as a tracked deferral (ChatPane writeBlocked).

CI on the new head: no failures (checks re-running after the rebase). The Test (macos/windows) / Integration Tests (CLI, No Sandbox) skip pattern remains the pre-existing repo-wide one; Test (ubuntu), Desktop Shell, web-shell E2E Smoke, and visuals were green.

中文说明

已针对更新后的 head b700a4cef(rebase 到新 main + 一个新提交)重新审查后再次通过:

  • rebase 内容干净——本 PR 专属文件(DaemonSessionProvider、actions、types、useQueuedPrompts、WorkspaceSessionProvider、useBackgroundTasks、useSessionArtifacts、midTurnDedup、daemon 侧通道、集成测试、设计文档)与此前 approved 的 head 逐字节一致;共享文件(App.tsx/ChatPane)的差异全部来自新 base 自身的进度(#8891/#8914),不是 rebase 冲突改动。
  • 新提交 b700a4c 合理——把 rename 流程中的 owner.isCurrent() 门控移到 catalog reconcile 之后、本地状态通知/错误上报之前:确认过的服务端 rename 在 source attachment 被替换后仍会 reconcile 进 catalog,并有专属回归测试。与提交目的一致。
  • 此前的讨论结论不变:3 条撤回(branch in-flight 互锁、mid-turn admission 保留、commit observer 边界),1 条接受为延后项(ChatPane writeBlocked)。

新 head 的 CI 无失败(rebase 后检查正在重跑);macos/windows 与 Integration Tests 的跳过仍是仓库既有模式,实际运行的 ubuntu Test、Desktop Shell、web-shell E2E 与 visuals 此前均绿。

@doudouOUC
doudouOUC enabled auto-merge August 11, 2026 09:37
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@doudouOUC
doudouOUC added this pull request to the merge queue Aug 11, 2026
Merged via the queue into QwenLM:main with commit 00248b1 Aug 11, 2026
58 of 70 checks passed
@doudouOUC
doudouOUC deleted the fix/transactional-cross-session-switching branch August 11, 2026 09:46
cursor Bot pushed a commit to samuelhsin/qwen-code that referenced this pull request Aug 14, 2026
…he loading-skeleton model (QwenLM#9129)

The transactional cross-session switching from QwenLM#8882 staged a handoff and
kept the old attachment live until the target load committed. It added a
large transition state machine (intent staging, same-session capture,
watchdog deadlines, controlled rebind) across the daemon session layer
and the web-shell provider, and left the UI pinned to the previous
session while a switch prepared.

Restore the loading-skeleton model: switching a session clears the
transcript, shows the loading skeleton, and waits for the load result.

- Remove sessionTransition state, onSessionTransitionCommit and the
  transactional target logic from WorkspaceSessionProvider.
- Strip the transition state machine from DaemonSessionProvider and
  restore single-session restores: restore_in_progress retries stay
  bounded by the existing watchdog, and the skeleton UI keys on
  loadingTranscript.
- Move useDaemonSessionOwnerGuard back under the daemon index export.
- Delete the transactional design docs and both daemon integration
  tests; the restored behavior is covered by unit tests.
- Drop the dead desiredSessionTargetPending prop (write gating now keys
  on loadingTranscript alone) and stop a failed switch's target
  workspace from leaking into the next workspace-less load.

Co-authored-by: 钉萁 <[email protected]>
Co-authored-by: Qwen-Coder <[email protected]>
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