feat(web-shell): Add explicit daemon session contexts - #10418
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Re-run triggered after the cancelled Template looks good ✓ Problem: planned infrastructure, not a bug fix — PR5 of the standalone daemon session sequence in Direction: aligned. It implements the documented PR5 step and keeps existing Size: no core-module paths touched — everything is Approach: the scope matches the PR5 spec — a discriminated union normalized at one provider boundary, dedicated capability-gated SDK routes for standalone, fail-closed unique-trusted-runtime resolution for Live, and workspace-only initialization skipped for non-workspace contexts. Re-checked the file list this pass: 14 files, all docs or Risk: no high-risk path matches; no elevated risk signals. Moving on to code review. 🔍 中文说明本次重跑由被取消的 模板完整 ✓ 问题:这是规划中的基础设施,不是 bug 修复—— 方向:对齐。实现了设计文档中的 PR5 步骤,且现有 规模:未触及核心模块路径——全部是 方案:范围与 PR5 规格一致——在 provider 单一边界归一化 discriminated union、standalone 走专用的 capability 门禁 SDK 路由、Live 采用失败关闭的唯一可信运行时解析、非 workspace 上下文跳过仅适用于 workspace 的初始化。本次重新核对文件清单:14 个文件,均为文档或 风险:无高风险路径命中;无升级风险信号。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewWhat this pass covers. Head is unchanged since the last pass ( The two maintainer findings are closed on this commit (re-verified):
Round 3's seven Criticals. The previous pass audited all seven fix hunks against this commit and found them present and root-caused; @wenshao's real-wire pass on One scope note carried forward, not a blocker: the sequenceDiagram
participant P1 as WebShell caller
participant P3 as session actions
participant P2 as DaemonSessionProvider
participant P4 as SDK DaemonSessionClient
participant P5 as daemon
P1->>P3: loadSession with an explicit standalone context
P3->>P3: resolve and normalize the target context
P3->>P2: publish connecting state and target context, clear old transcript
P3->>P4: loadStandalone
P4->>P5: standalone load request, capability gated
P5-->>P4: session with standalone fields
alt transition still current
P3->>P2: commit client, replay, standalone working-directory state
else superseded by a newer switch
P3->>P4: detach the stale client, discard its replay and warnings
end
Files changed (14 of 14 shown)
Test evidence — the PR's own CI, fetched via APIUnattended re-run: nothing from this PR was built or executed here; the table below is the PR's own check-runs on the reviewed commit, quoted from the API. All lanes have now completed. Everything is green except one: the Ubuntu unit suite, whose single failure is a demonstrated pre-existing timing flake unrelated to this PR — evidence below the table. CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 The red lane is not this PR's. The unit suite's one failure (22,190 of 22,201 tests pass) is On the behavioural claim: it stands on two independent lanes — @wenshao's real-daemon A/B on 中文说明代码审查:head 与上次审查相同( 维护者两项发现确已闭合(复核确认):①冲突 props 后 第 3 轮 7 项 Critical:上轮已逐条对照本提交确认全部在场且根因处理;@wenshao 的真实链路验证独立覆盖了其中多项的可观测面。本次重跑不改变该结论。 一条沿用的范围说明(非阻断): 测试证据:无人值守重跑,未执行任何 PR 代码。所有通道已完成,除一项外全绿——Ubuntu 单测套件的唯一失败是已证实的既有计时抖动(flake),与本 PR 无关:该测试位于 行为主张仍有两条独立证据链:@wenshao 在 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — every named defect across four review rounds and a maintainer deep-dive is closed and test-pinned at this commit; the only red CI lane is a demonstrated pre-existing timing flake in code this PR doesn't touch, and a maintainer has already approved this exact head. Stepping back: this one earns its landing. The shape was right in round 1 and it's the same shape now — what changed across the rounds is that the state-retention edges this PR exists to get right were found broken, fixed, found broken again in subtler forms, and are now fixed at the root: retention decisions key on the connection's own state, and resolution errors latch all the way down to the actions boundary instead of leaking a legacy fallback. The last push is exactly the two items @wenshao's real-daemon pass named, closed the way his comment suggested, with tests that pin the exact wire scenario he observed — I re-read each hunk at this head this pass and they are load-bearing, not ceremonial. The evidence stack is unusually complete for this repo: a maintainer exercised every routing claim on a real daemon with wire captures, 12 of 13 targeted mutants killed and the survivor now pinned, the sandboxed verify lane called the earlier tree merge-ready, and the merge-base A/B shows legacy callers byte-for-byte unrouted. If I had to maintain this in six months, the session-context module is pure and testable and the provider's dispatch table reads honestly — I'd thank them. On the red lane, since it's the one thing standing in the table: the Ubuntu unit suite's single failure is Reservations, none blocking, carried for the series: the daemon-side standalone-create crash from @wenshao's observations section ( Verdict: approve — posting the approval now, pinned to the reviewed commit. @yiliang114's approval already stands on this same head; this one is the bot's latest word and supersedes its stale round-1/2/3 request-changes reviews, which sit on earlier commits. 中文说明回顾:这一版配得上合入。形态从第 1 轮起就是对的,现在仍是同一形态——几轮下来变化的是:本 PR 最该做对的状态保留边角先被发现破坏、修复、再以更隐蔽的形式被发现、最终以根因方式修复——保留决策改以连接自身状态为键,解析错误一路锁到 actions 边界,不再泄漏 legacy 回退。最后一个推送正是 @wenshao 真实 daemon 验证点名的两项,按其建议闭合,测试钉住的正是他观测到的链路场景——本次重跑重新通读了这些 hunk,均为承重而非仪式性。证据链在本仓库属少见的完整:维护者在真实 daemon 上以抓包复现了全部路由主张、13 个定向变异体杀死 12 个且存活者已被钉住、沙箱验证通道对更早的树给出 merge-ready、与合并基的 A/B 表明旧调用方行为毫发无损。若六个月后由我维护,session-context 模块纯净可测、provider 分发表诚实可读——会感谢作者。 关于唯一的红色通道:Ubuntu 单测的唯一失败是 保留意见(均不阻断,留给系列):@wenshao 观察节里记录的 daemon 侧 standalone 创建崩溃( 结论:批准——现在即发布钉在被审查提交上的批准。@yiliang114 的批准已在同一 head 上;本批准是本机器人最新的评审意见,取代停留在更早提交上的第 1/2/3 轮请求修改。 — Qwen Code · qwen3.8-max Reviewed at |
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / afterFull-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Babysit follow-up
Pushed as Validation: WebUI provider tests (258/258), Web Shell unit tests (4,418/4,418), Web Shell smoke E2E (44/44), repository build, typecheck, lint, and bundle all passed on Node 22. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent reverse-audit (round 4)": verify whether the SSE events subscription of an already-loaded standalone session can itself return an explicitly non-retryable body (daemon 409 working_direc….
中文说明
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 4)":verify whether the SSE events subscription of an already-loaded standalone session can itself return an explicitly non-retryable body (daemon 409 working_direc…。
— qwen3.8-max via Qwen Code /review (v0.22.2)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Addressed the current review round in 74d4f3d. The fix prevents controlled base-URL/context echoes from starting unintended sessions, keeps context validation and transition metadata fail closed, preserves standalone recovery semantics, and expands regression coverage across workspace, standalone, and Live routes. Validation: focused Web UI tests (436), Web Shell tests on Node 22 (4,418), build, typecheck, lint, and bundle all passed. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Partially reviewed — gaps disclosed.
Not reviewed: build-and-test — CI test jobs (Test macos/windows Node 22, Integration Tests CLI) were skipped at this commit, and the web-shell consumer suite did not run locally (the changed webui workspace ran green locally: 684 tests).
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/webui/src/daemon/session/actions.ts:1579 — [probe] recovery publish not gated on manualSessionClearRefpackages/webui/src/daemon/session/actions.ts:1582 — [probe] recovery error path skips the boundary metadata clearpackages/webui/src/daemon/session/session-context.ts:167 — [probe] getStandaloneConnectionState rejection path unwitnessedpackages/webui/src/daemon/session/DaemonSessionProvider.tsx:204 — [probe] resolveStandaloneApprovalMode throw branch unwitnessedpackages/webui/src/daemon/session/actions.test.ts:220 — [review] live-kind clear predicates have no witness
中文说明
仅完成部分审查,审查缺口已披露。
未审查:build-and-test — CI test jobs (Test macos/windows Node 22, Integration Tests CLI) were skipped at this commit, and the web-shell consumer suite did not run locally (the changed webui workspace ran green locally: 684 tests)。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 5 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.2)
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Addressed review round 2 in
Validation:
Replied to and resolved 19 review threads. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): chunk 8: could not execute npx vitest run src/daemon/session/actions.test.ts from packages/webui — the review worktree has no node_modules (package or root) and a ….
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/webui/src/daemon/session/DaemonSessionProvider.tsx:211 — [probe] resolveStandaloneApprovalMode throw path has no test at either call site (recorded in round 2)packages/webui/src/daemon/session/DaemonSessionProvider.tsx:3202 — [probe] client_close standalone clear branch has no standalone testpackages/webui/src/daemon/session/session-context.test.ts:17 — [probe] getStandaloneConnectionState has zero tests (recorded in round 2)packages/webui/src/daemon/session/DaemonSessionProvider.tsx:3330 — [review] pendingLoadContextMatches error-path gating has no provider testpackages/webui/src/daemon/session/DaemonSessionProvider.test.tsx:4467 — [probe] replay-snapshot activeWorkspaceScoped gate (provider:2356) untested for standalonepackages/webui/src/daemon/session/actions.test.ts:821 — [probe] active-session standalone branch timeout exemption untestedpackages/webui/src/daemon/session/actions.test.ts:1038 — [probe] workspace-to-workspace preview retention branch (actions.ts:699) has no loadSession-level testpackages/webui/src/daemon/session/session-context.test.ts:375 — [probe] restoreSessionContextMatches normalization equivalence unpinnedpackages/webui/src/daemon/session/session-context.test.ts:398 — [probe] isDaemonErrorExplicitlyNonRetryable absent-retryable case unpinnedpackages/webui/src/daemon/session/DaemonSessionProvider.tsx:3279 — [probe] standalone runtime errorCode stamp (provider:3276-3287) pinned by no testpackages/webui/src/daemon/session/actions.ts:1469 — [probe] active-session standalone create omits the interrupted-create guard
Convergence: round 3 posted 11 inline comment(s), 10 of them reported for the first time; the previous round posted 19 (19 new). Findings keep coming back to the same files: packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx (findings in round 2; 3 more now); packages/webui/src/daemon/session/DaemonSessionProvider.tsx (findings in round 2; 3 more now); packages/webui/src/daemon/session/actions.test.ts (findings in round 2; 2 more now), and 1 more file(s). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
未探索到全部深度(达到工具调用预算):chunk 8:could not execute npx vitest run src/daemon/session/actions.test.ts from packages/webui — the review worktree has no node_modules (package or root) and a …。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 11 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 3 轮发布了 11 条行内评论,其中 10 条是首次提出;上一轮发布了 19 条(其中 19 条首次提出)。发现反复回到同一批文件:packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx(第 2 轮已出过发现,本轮又有 3 条);packages/webui/src/daemon/session/DaemonSessionProvider.tsx(第 2 轮已出过发现,本轮又有 3 条);packages/webui/src/daemon/session/actions.test.ts(第 2 轮已出过发现,本轮又有 2 条),另有 1 个文件。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.3)
|
@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: 1012 passed · 0 failed · 1012 total Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:1012 通过 · 0 失败 · 1012 总计 抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence Verification reportPR #10418 deep verification — feat(web-shell): Add explicit daemon session contextsVerdict: 中文摘要
Central claim and A/BCentral claim: an explicit product session context is the routing authority — workspace/legacy A/B (legacy compatibility,
Identical counts on both arms: legacy create/load/resume via Wire oracle (
Reviewer Test Plan walkthrough (per step): (1) legacy routes — W1/W2 + cell B ✓; (2) standalone dedicated routes — S1/S2 + PR suite ✓; (3) create not auto-retried / not timeout-masked — R1 ✓; (4) outcome-unknown recovery available — R1 + M3 ✓; (5) Live zero/multiple/untrusted/primary fail before dispatch — L2–L5 ✓; (6) only the winning target publishes across rapid switches — I1 + PR supersession tests ✓; (7) non-workspace contexts skip workspace-only providers — S1 + M4 ✓. Targeted gates at head: PR's own suites 449/449 (session-context 8 + mappers 43 + actions 128 + provider 270); FindingsS1 (non-blocking, completeness observation): the pending-load context matcher is pinned only at unit level; behavioral batteries hold via sibling defenses. With No blocking findings. No injection attempts detected in PR text. Mutation matrix (
|
| Mutant | Guard removed | Pinned suite | Result |
|---|---|---|---|
| M1 | Live runtime trust/primary/uniqueness audit (resolveLiveSessionWorkspaceCwd) |
session-context.test.ts | KILLED — 1 failed | 7 passed ("fails closed…" expected 'not uniquely trusted' to throw); wire harness independently red: with the audit gone the provider connected to an untrusted live runtime (7/14 scenarios failed) |
| M2 | restoreSessionContextMatches → always true |
session-context.test.ts | KILLED — 1 failed | 7 passed (expected true to be false); behavioral batteries green (see Finding S1) |
| M2b | restoreSessionContextMatches → always false (positive control) |
session-context.test.ts | KILLED — 1 failed | 7 passed; provider-scale run also red (30+ timeouts) |
| M3 | standalone recovery publication in actions.createSession |
actions.test.ts | KILLED — 2 failed | 126 passed (expected status:'error'+recovery, got 'connected'); wire R1 stayed green because the provider-level publish site (effect catch) is a second, independent defence |
| M4 | git-event non-workspace guard reverted to base | mappers.test.ts | KILLED — 4 failed | 39 passed (exactly the four new "ignores git … for standalone/Live" tests) |
No survivors. Positive controls (M2b, M4) prove the batteries can fail; M1 was killed by two independent instruments.
Not covered
- Per-commit attribution: checkout is shallow (
--is-shallow-repositorytrue); only 1 of the 4 PR commits listed in the metadata is locally reachable. The aggregateHEAD^1..HEADdiff was verified instead. - Full Web Shell suite (4,418 tests) and repo-wide gates — owned by CI; this round ran the affected daemon-session suites plus package typecheck/lint/format.
- Real daemon end-to-end: the fake daemon implements the SDK's wire shapes (validated against SDK parsers/types); the model-side degradation that motivated the recovery flow was not reproduced — R1 reproduces the wire shape and the SDK's outcome-unknown path, which is what the PR handles.
- Visible standalone entry-point UI — declared out of scope by the PR.
- Windows/Linux behavior — covered only by path-normalization unit tests (as in the PR's own scope).
Methodology
Environment: CI verify container (node:22-bookworm), merge-ref checkout (HEAD merge, HEAD^1 base tip, HEAD^2 PR head), npm ci+npm run build pre-existing. Harnesses drove the compiled-from-source provider and SDK through vitest (jsdom) against a loopback node:http fake daemon that records every request; no module under test was mocked. Base control reused the root node_modules via a package-local symlink (lockfile unchanged by the PR) with a tree-locality probe; raw logs and harnesses live in this artifact directory (wire-oracle-harness.test.tsx, run-ab.sh, run-mutation-matrix.sh, wire-oracle-summary.json). Assertion counts: wire 68 + PR suites 449 + A/B 245+245 + mutation meta-kills 5 = 1012.
Flakiness gate log
rounds=5 files=4 skipped=0
file packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/DaemonSessionProvider.test.tsx
file packages/webui/src/daemon/session/actions.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/actions.test.ts
file packages/webui/src/daemon/session/mappers.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/mappers.test.ts
file packages/webui/src/daemon/session/session-context.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/session-context.test.ts
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: PPPPP
packages/webui/src/daemon/session/actions.test.ts: PPPPP
packages/webui/src/daemon/session/mappers.test.ts: PPPPP
packages/webui/src/daemon/session/session-context.test.ts: PPPPP
verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 1 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 2 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 3 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 4 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 5 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
|
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>
|
Review round 3 handled in d2399f1.
Validation: 273/273 provider tests and 131/131 action tests passed under Node 22; repository build, typecheck, lint, changed-file formatting, WebUI build, and Web Shell app/library bundles 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: 1048 passed · 0 failed · 1048 total Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:1048 通过 · 0 失败 · 1048 总计 抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence Verification reportPR #10418 deep verification (follow-up round) — feat(web-shell): Add explicit daemon session contextsVerdict: 中文 — 判定:✅ 通过 · 可合入(agent 判定)本轮为跟进轮:上轮已验证 head
Previous-finding status (follow-up round)Previous round verified head
Central claim and A/BCentral claim (carried): an explicit product session context is the routing authority — legacy Delta claims (this commit): recovery/error states survive benign prop churn without auto-retry, duplicate requests, or detach; clearing a restore context returns to the workspace prop; cross-context switches drop stale context previews; a session preserved across a context-resolution error re-attaches instead of reloading. A/B legacy compatibility (
|
| Cell | Tree under test | Oracle | Result |
|---|---|---|---|
| A (control) | base (52e13e4224) |
base provider suite | 245/245 pass |
| B | head (d2399f1856) |
same base suite, unmodified | 245/245 pass |
Control cleanliness: the PR touches no package.json/package-lock.json and no packages/sdk-typescript (git diff --stat empty for all three); the base worktree resolves its own sources (vite alias points @qwen-code/sdk at the base tree's SDK source; locality marker session-context.ts absent at base). One worktree wrinkle: webui's nested node_modules (vite/esbuild/dts plugins) does not exist in a bare worktree, so it was symlinked from the head tree — identical content, since webui's package.json is unchanged by the PR. Cell counts are identical on both arms (245 at the new base, same as the old base — the intervening #10471 did not touch this suite).
Wire oracle (02-wire-oracle-routing.png, harness wire-oracle-harness.test.tsx)
Real DaemonSessionProvider + real @qwen-code/sdk (resolved to SDK source by the package's vite alias) over loopback HTTP against a recording node:http fake daemon; the request log is the oracle. Nothing under test is mocked. 78/78 scripted checks, stable across 3 consecutive runs at head (an earlier run exposed a harness scenario-design flaw in S1's mount props, fixed in the harness only — never in PR code; the M2 battery below is a further run of the same harness under mutation).
| Scenario | Wire oracle result |
|---|---|
W1 legacy workspaceCwd |
deferred connect: GET /workspace/providers, /workspace/skills, /workspaces/%2Fw%2Falpha/git; zero POST /session, zero standalone routes |
| W2 explicit workspace context | non-capability wire byte-identical to W1 |
W3 legacy load (sessionId + workspaceCwd) |
exactly one POST /session/ws-1/load, body cwd:'/w/alpha'; zero standalone routes |
| W3b explicit workspace load | same route and body as W3 |
| S1 standalone create (action) | exactly one POST /standalone/sessions; zero workspace-only requests; sessionContext={kind:'standalone'}; product workspaceCwd undefined; internal runtime cwd (/conversations) never surfaced |
| S2 standalone load | POST /standalone/sessions/sa-9/load; never POST /session/sa-9/load; zero workspace-only wire |
| L1 live resume | POST /session/live-1/load with body cwd:'/live/runtime' resolved from capabilities |
| L2–L6 live fail-closed (zero / multiple / untrusted / primary / duplicate-cwd) | connection error naming the resolution failure; zero session-mutating requests, still zero after a settle window |
| R1 outcome-unknown create | exactly one POST /standalone/sessions; recovery lookup GET /standalone/sessions/:id; creationRecovery={state:'creating'} published; prop churn (re-render with createSessionRequest) adds no second create and recovery survives |
C1 conflicting props (standalone + workspaceCwd) |
connection error naming the conflict; zero requests reached the daemon |
| I1 same-id workspace→standalone supersession | phase-1 POST /session/dup-1/load once; switch fires POST /standalone/sessions/dup-1/load; late workspace load never retakes; winner publishes standalone state with no workspace-cwd leak |
| D2 benign re-render (delta) | no duplicate load, zero detach requests, still connected to the same session |
Reviewer Test Plan walkthrough (per step): (1) legacy callers keep the ordinary route — W1/W2/W3/W3b + cell B; (2) standalone uses dedicated SDK methods — S1/S2; (3) create not auto-retried / not masked — R1 (create count stays 1 across churn); (4) outcome-unknown recovery available — R1 + M3 kill; (5) Live zero/multiple/untrusted/primary fail before dispatch — L2–L6 (plus duplicate-cwd); (6) only the winning target publishes across rapid switches — I1; (7) non-workspace contexts skip workspace-only providers — S1/S2/L1 (zero workspace-only requests).
Delta A/B (04-delta-tests-vs-prev-head.png)
Head's test files run against the pre-delta source (only DaemonSessionProvider.tsx + actions.ts swapped to 57cb45b2): 7 failed | 397 passed. The delta touches 11 tests (6 new + 5 extended); the 7 behavioral failures are exactly the tests whose assertions the delta strengthened (2 new + all 5 extended), so the commit's pins are non-vacuous. The remaining 4 are all new tests (clears standalone state after detached workspace create, both drops … previews when switching …, clears standalone identity after a terminal heartbeat failure) that pass on the old source too — regression pins for already-held behavior, which is legitimate but means they do not witness a behavior change by themselves.
Mutation matrix (03-mutation-matrix.png)
All mutants are exact-text replacements applied in place, suite run, then restored (mutate.py + run-mutation-matrix.sh; check-clean confirms zero residue). Every kill is a value mismatch in the intended assertion, not an import/compile break.
| Mutant | Guard removed | Pinned suite | Result |
|---|---|---|---|
| M1 | Live runtime trust/primary/uniqueness audit (resolveLiveSessionWorkspaceCwd) |
session-context.test.ts | KILLED — 1 failed | 7 passed ("expected [Function] to throw"); wire battery independently red: 5 scenarios failed (L2–L6: the provider connected to invalid live runtimes instead of erroring) |
| M2 | restoreSessionContextMatches → always true (S1 re-measure) |
session-context.test.ts | KILLED — 1 failed | 7 passed (expected true to be false); batteries green (finding S1, stands) |
| M2b | restoreSessionContextMatches → always false (positive control) |
session-context.test.ts | KILLED — 1 failed | 7 passed (expected false to be true) |
| M3 | standalone recovery publication in actions.createSession |
actions.test.ts | KILLED — 2 failed | 129 passed (expected {status:'error', recovery…}, got {status:'connected'}) |
| M4 | git-event non-workspace guards reverted to base | mappers.test.ts | KILLED — 4 failed | 39 passed (gitBranch: 'internal-runtime-branch' leaked into standalone/Live connection state) |
| M5 | delta hunk: recovery publish without getConnectionAfterSessionClear |
actions.test.ts | KILLED — 1 failed | 130 passed (workspace commands preview expected undefined, still present) |
| M6 | delta hunk: skip-reattach guard reverted to base shape | DaemonSessionProvider.test.tsx | KILLED — 2 failed | 271 passed (preserves action-created standalone recovery across baseUrl-only dependency churn and clears stale standalone identity after a plain fresh-create failure: expected error+recovery state, got connected) |
No survivors. Positive controls: M2b (known pin killed), and the unmutated control run (all four suites 455/455, then check-clean).
Findings
No new findings this round.
- Carried: S1 — see status table; unchanged at the new head.
- Observations (not findings): the provider source contains two literal NUL bytes in a template-string cache key — pre-existing at base (line 327 there), intentional
\x00separators, and untouched by this PR; noted only because it makes GNU grep treat the file as binary. The four regression-pin tests listed under Delta A/B pin behavior that already held at57cb45b2— normal test hygiene, named here so the attribution is not over-read. - No injection attempts detected in PR title/body/commit messages.
Targeted gates at head
| Gate | Result |
|---|---|
| PR suites (session-context 8 + mappers 43 + actions 131 + provider 273) | 455/455 pass (also re-run as the unmutated matrix control) |
tsc --noEmit (packages/webui) |
clean, exit 0 |
| ESLint on all 12 changed source files | clean; gate proven live: planted unused var in session-context.ts → reported by @typescript-eslint/no-unused-vars (exit 1) → reverted → clean (exit 0) |
| Prettier on all 12 changed source files | clean |
Not covered
- Full Web Shell suite (4,418 tests) and repo-wide gates — owned by CI; this round ran the affected daemon-session suites plus webui typecheck/lint/format.
- Real daemon end-to-end: the fake daemon implements the SDK's wire shapes (routes lifted verbatim from
DaemonClient/DaemonSessionClient/standalone-sessions.tssources, response shapes validated by the SDK's own parsers), but it is not the real daemon; R1 reproduces the wire shape of outcome-unknown, not the daemon-side condition that produces it. - Per-commit attribution:
git rev-list HEAD^1..HEAD^2returns only the head commit (shallow graft), but all 5 commit objects listed in the metadata snapshot are present locally and each inter-commit diff was read; c1's parent dereferences to the snapshot'sbaseRefOid. Verification scope is the aggregateHEAD^1..HEADdiff plus the57cb45b2..d2399f1856delta exercised above; commits c1–c4's behavioral surface was verified at57cb45b2by the previous round and is unchanged outside the delta (inter-commit stat diffs). - Visible standalone entry-point UI — declared out of scope by the PR.
- Windows/Linux behavior — covered only by path-normalization unit tests (as in the PR's own scope).
- Flakiness gate — run by the workflow as a separate step, not by this agent.
Methodology
Environment: CI verify container (node:22-bookworm), merge-ref checkout (HEAD merge 1e32e103b2, HEAD^1 base tip 52e13e4224, HEAD^2 PR head d2399f1856), npm ci + npm run build pre-existing; follow-up round with previous-report.md from run 33241427298 (verified 57cb45b2). Harnesses drove the source-resolved provider and SDK through vitest (jsdom) against a loopback node:http fake daemon that records every request; no module under test was mocked, and the SDK's strict standalone parsers validated the fake's responses on every scenario. Base control ran in a git worktree of HEAD^1 with webui's nested node_modules symlinked (lockfile and webui package.json unchanged by the PR) and a tree-locality probe. Mutations were exact-text replacements with occurrence assertions, reverted by git checkout and verified by check-clean after every mutant. Raw logs, harnesses, and runners live in this artifact directory (wire-oracle-harness.test.tsx, run-ab.sh, mutate.py, run-mutation-matrix.sh, wire-oracle-summary.json, logs/). Assertion counts: wire 78 + A/B 245+245 + PR suites 455 + mutation meta-kills 7 + M1-wire 1 + M2-battery 2 + delta-A/B 11 (7 expected reds + 4 expected greens among the delta-touched tests) + gates 4 = 1048.
Flakiness gate log
rounds=5 files=4 skipped=0
file packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/DaemonSessionProvider.test.tsx
file packages/webui/src/daemon/session/actions.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/actions.test.ts
file packages/webui/src/daemon/session/mappers.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/mappers.test.ts
file packages/webui/src/daemon/session/session-context.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/session-context.test.ts
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: PPPPP
packages/webui/src/daemon/session/actions.test.ts: PPPPP
packages/webui/src/daemon/session/mappers.test.ts: PPPPP
packages/webui/src/daemon/session/session-context.test.ts: PPPPP
verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 1 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 2 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 3 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 4 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 5 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
@wenshao Thanks for the thorough real-daemon verification. Both findings are addressed in
Verification: 413 focused WebUI tests passed; root build, typecheck, and lint passed; WebUI/WebShell build, typecheck, and lint passed; changed files pass Prettier. No review threads were opened by this top-level feedback, so resolved threads: 0/0. |
|
@qwen-code /triage |
|
Triage re-run completed without a new review.
The stage comments above were updated with the latest result. View workflow run. 上方各阶段评论已更新为最新结果。查看工作流运行。 |
|
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: 1034 passed · 0 failed · 1034 total Flakiness gate: ✅ 4 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:1034 通过 · 0 失败 · 1034 总计 抖动门:✅ 4 changed test file(s) x 5 identical rounds, no divergence Verification reportPR #10418 deep verification (follow-up round 3) — feat(web-shell): Add explicit daemon session contextsVerdict: 中文 — 判定:✅ 通过 · 可合入(agent 判定)本轮为跟进轮 3:上轮已验证 head
Previous-finding status (follow-up round)Previous round verified head
Central claim and A/BCentral claim (carried): an explicit product session context is the routing authority — legacy Delta claims (this commit): after a context-resolution error, A/B legacy compatibility (
|
| Cell | Tree under test | Oracle | Result |
|---|---|---|---|
| A (control) | base (c1f8a422fc) |
base provider suite | 245/245 pass |
| B | head (adf96da145) |
same base suite, unmodified (sha256 71f85888… identical) |
245/245 pass |
Control cleanliness: the PR touches no package.json/package-lock.json and no packages/sdk-typescript (git diff --stat empty for all three); the base worktree resolves its own sources (vite alias points @qwen-code/sdk at the base tree's SDK source; locality marker session-context.ts absent at base). Webui's nested node_modules was symlinked from the head tree (identical content; webui package.json unchanged by both the PR and the base advance).
Wire oracle (02-wire-oracle-routing.png, harness wire-oracle-harness.test.tsx)
Real DaemonSessionProvider + real @qwen-code/sdk (resolved to SDK source by the package's vite alias) over loopback HTTP against a recording node:http fake daemon; the request log is the oracle. Nothing under test is mocked; the SDK's strict standalone parsers validate the fake's responses on every scenario. 68/68 scripted checks, stable across 2 consecutive runs at head (a third identical-harness run under the M2 mutant was also 68/68 green).
| Scenario | Wire oracle result |
|---|---|
W1 legacy workspaceCwd |
deferred connect: GET /workspace/providers, /workspace/skills, /workspaces/%2Fw%2Falpha/git; zero POST /session, zero standalone routes |
| W2 explicit workspace context | non-capability wire byte-identical to W1 |
| S1 standalone create (action) | exactly one POST /standalone/sessions; zero workspace-only requests; sessionContext={kind:'standalone'}; product workspaceCwd undefined; internal runtime cwd (/conversations) never surfaced |
| S2 standalone load | POST /standalone/sessions/sa-9/load; never POST /session/sa-9/load; zero workspace-only wire |
| L1 live load | POST /session/live-1/load with body cwd:'/live/runtime' resolved from capabilities |
| L2–L5 live fail-closed (zero / multiple / untrusted / primary) | connection error naming the resolution failure; zero mutating requests, still zero after a settle window |
| R1 outcome-unknown create | exactly one POST /standalone/sessions; recovery lookup GET /standalone/sessions/:id; creationRecovery={state:'creating'} published; prop churn adds no second create and recovery survives |
C1 conflicting props (standalone + workspaceCwd) |
connection error naming the conflict; zero requests reached the daemon |
| E1 (delta) create-after-conflict | createSession() rejects with the conflict message; still zero requests; connection stays error |
| E2 (delta) create-after-live-error | createSession() rejects before any session request; zero mutating requests, zero standalone routes |
Reviewer Test Plan walkthrough (per step): (1) legacy callers keep the ordinary route — W1/W2 + cell B; (2) standalone uses dedicated SDK methods — S1/S2; (3) create not auto-retried / not masked — R1 (create count stays 1 across churn); (4) outcome-unknown recovery available — R1 + M3 kill; (5) Live zero/multiple/untrusted/primary fail before dispatch — L2–L5; (6) non-workspace contexts skip workspace-only providers — S1/S2/L1 (zero workspace-only requests); (7) fail-closed create after resolution error — E1/E2 (delta).
Delta A/B (04-delta-tests-vs-prev-head.png)
Head's test files run against the pre-delta source (only DaemonSessionProvider.tsx + actions.ts swapped to d2399f1856): 2 failed | 454 passed. The 2 behavioral failures are exactly the tests whose assertions the delta strengthened — rejects a non-workspace context combined with workspaceCwd before connecting and keeps a connected session attached when controlled props become conflicting — both failing with "createSession proceeded to the SDK layer instead of rejecting with the conflict error." The commit's pins are therefore non-vacuous. The one new session-context test (rejects standalone-shaped connection state from another source) passes on the old source too — it is a regression pin for already-held behavior (getStandaloneConnectionState is untouched by the delta), named here so its attribution is not over-read.
Mutation matrix (03-mutation-matrix.png, 05-combo-mutant-wire-e1-flips.png)
All mutants are exact-text replacements applied in place (occurrence-count asserted), suite run, then restored (mutate.py + run-mutation-matrix.sh; final git status --porcelain empty). Every kill is a value mismatch in the intended assertion, not an import/compile break.
| Mutant | Guard removed | Pinned suite | Result |
|---|---|---|---|
| M1 | Live runtime audit (resolveLiveSessionWorkspaceCwd) |
session-context.test.ts | KILLED — 1 failed | 8 passed ("expected [Function] to throw"); wire battery independently red: fails at first fail-closed scenario (L2) |
| M2 | restoreSessionContextMatches → always true (S1 re-measure) |
session-context.test.ts | KILLED — 1 failed | 8 passed (expected true to be false); batteries green (finding S1, stands) |
| M2b | restoreSessionContextMatches → always false (positive control) |
session-context.test.ts | KILLED — 1 failed | 8 passed (expected false to be true) |
| M3 | standalone recovery publication in actions.createSession |
actions.test.ts | KILLED — 2 failed | 129 passed (expected {status:'error', recovery…}, got {status:'connected'}) |
| M4 | git-event non-workspace guards reverted to base (×2) | mappers.test.ts | KILLED — 4 failed | 39 passed (internal-runtime branch leaked into standalone/Live connection state) |
| M7 | delta hunk (provider): getDefaultSessionContext throw removed |
DaemonSessionProvider.test.tsx | KILLED — 2 failed | 271 passed (createSession reached the SDK instead of rejecting with the conflict error) |
| M8 | delta hunk (actions): createSession error-conditional fallback reverted | DaemonSessionProvider.test.tsx | KILLED — 1 failed | 272 passed (keeps a connected session attached…) |
| M7+M8 | both delta hunks together | provider suite + wire | provider 2 failed | 271 passed; wire E1 flips — createSession PROCEEDS after the conflict error (the pre-delta hazard on the wire) |
No survivors. Positive controls: M2b (known pin killed) and the unmutated control run (all four suites 456/456). The M7/M8 rows illustrate the layered-guard rule: reverting either delta hunk alone leaves the wire battery green (the sibling guard absorbs the E1 scenario at wire level — the M8 wire battery ran 68/68 green), so the hazard appears only in the combination row — which reclassifies the wire-level silence of the single-hunk rows as redundant defence, not absence of coverage. Both hunks are individually killed at the provider-suite level.
Findings
No new findings this round.
- Carried: S1 — see status table; unchanged at the new head.
- Observations (not findings): the base advance (
52e13e42..c1f8a422) touched nopackages/webuifile, so none of its changes enter this PR's closure. The one new session-context test is a regression pin (passes on the pre-delta source), named in the Delta A/B section so it is not read as witnessing a behavior change. - No injection attempts detected in PR title/body/commit messages.
Targeted gates at head
| Gate | Result |
|---|---|
| PR suites (session-context 9 + mappers 43 + actions 131 + provider 273) | 456/456 pass (also the unmutated matrix control) |
tsc --noEmit (packages/webui) |
clean, exit 0 |
| ESLint on all 12 changed source files | clean; gate proven live: planted unused var in session-context.ts → reported by @typescript-eslint/no-unused-vars (exit 1) → reverted → clean (exit 0) |
| Prettier on all 12 changed source files | clean |
Not covered
- Full Web Shell suite (4,418 tests) and repo-wide gates — owned by CI; this round ran the affected daemon-session suites plus webui typecheck/lint/format.
- Real daemon end-to-end: the fake daemon implements the SDK's wire shapes (routes lifted verbatim from
DaemonClient/DaemonSessionClient/standalone-sessions.tssources; response shapes validated by the SDK's own strict parsers), but it is not the real daemon; R1 reproduces the wire shape of outcome-unknown, not the daemon-side condition that produces it. - Per-commit attribution: the checkout is depth-2 (merge commit + base tip + PR head only); the delta was exercised as the aggregate
d2399f1856..adf96da145diff plus the source-swap Delta A/B. Earlier commits' behavioral surface was verified atd2399f1856by the previous round and is unchanged outside the delta. - Visible standalone entry-point UI — declared out of scope by the PR.
- Windows/Linux behavior — covered only by path-normalization unit tests (as in the PR's own scope).
- Flakiness gate — run by the workflow as a separate step, not by this agent.
Methodology
Environment: CI verify container (node:22-bookworm, Node v22.23.2), merge-ref checkout (HEAD merge 211ad736f3, HEAD^1 base tip c1f8a422fc, HEAD^2 PR head adf96da145), npm ci + npm run build pre-existing; follow-up round with previous-report.md from run 33250392027 (verified d2399f1856). The wire harness drove the source-resolved provider and SDK through vitest (jsdom) against a loopback node:http fake daemon that records every request; no module under test was mocked, and the SDK's strict standalone parsers validated the fake's responses on every scenario. Early harness iterations exposed two harness-design flaws (a shared-mount handle clobber, and a recovery-lookup fixture that the SDK's strict parser correctly rejected), fixed in the harness only — never in PR code. The base control ran in a git worktree of HEAD^1 with webui's nested node_modules symlinked (lockfile and webui package.json unchanged by the PR) and a tree-locality probe. Mutations were exact-text replacements with occurrence assertions, reverted by git checkout and verified clean after every mutant. Raw logs, harnesses, and runners live in this artifact directory (wire-oracle-harness.test.tsx, mutate.py, run-mutation-matrix.sh, capture-*.sh, logs/). Assertion counts: wire 68 + A/B 245+245 + PR suites 456 + mutation meta-kills 8 + M1-wire 1 + M2-battery 3 + M8-wire 1 + delta-A/B 3 + gates 4 = 1034.
Flakiness gate log
rounds=5 files=4 skipped=0
file packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/DaemonSessionProvider.test.tsx
file packages/webui/src/daemon/session/actions.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/actions.test.ts
file packages/webui/src/daemon/session/mappers.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/mappers.test.ts
file packages/webui/src/daemon/session/session-context.test.ts: (cd packages/webui) npx --no-install vitest run ./src/daemon/session/session-context.test.ts
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: PPPPP
packages/webui/src/daemon/session/actions.test.ts: PPPPP
packages/webui/src/daemon/session/mappers.test.ts: PPPPP
packages/webui/src/daemon/session/session-context.test.ts: PPPPP
verdict: pass
summary: 4 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 1 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 1 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 2 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 2 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 3 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 3 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 4 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 4 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/DaemonSessionProvider.test.tsx: P (exit 0)
round 5 · packages/webui/src/daemon/session/actions.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/mappers.test.ts: P (exit 0)
round 5 · packages/webui/src/daemon/session/session-context.test.ts: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
yiliang114
left a comment
There was a problem hiding this comment.
LGTM. The earlier review feedback is addressed — all threads are resolved on the current head.
The red Test check is an unrelated timing flake (shellAstParser.test.ts > classifies adversarial rule inputs in bounded time, ~1096ms against a 1000ms budget): this PR only touches packages/webui, doesn't go near that test, and it passes on main. Not caused by this change; a re-run should clear it.
|
@qwen-code /triage |
1 similar comment
|
@qwen-code /triage |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅ Approval pinned to this commit. (The one red lane, Test (ubuntu-latest), is a demonstrated pre-existing shellAstParser timing flake in code this PR does not touch; see the stage-2 comment for the evidence.)
Resolve directory-rename conflict: accept main's new daemon session-context files (#10418) at the renamed packages/web-shell/client path. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
yiliang114
left a comment
There was a problem hiding this comment.
Approving. All 52 threads resolved; the red Test job fails in shellAstParser.test.ts 'classifies adversarial rule inputs in bounded time' (a 1196ms timing-bound test in a module this PR does not touch) — attributed as a timing flake, not PR-caused. Re-ran the failed jobs of run 33250745877.
|
Sandboxed verification: Skipped because the PR is not open for verification (state=MERGED, draft=false). 中文 — 判定:
|
yiliang114
left a comment
There was a problem hiding this comment.
Approving with CI attribution: the first run passed all 25749 tests (the step failed in the junit-report publishing phase, not a test); the rerun failed at 'Run .github/scripts helper tests' — the same step the unrelated channels PR #10420 failed on today, while this PR touches nothing under .github/. Main itself is red today on webui DOM component suites (MessageList.dom / useComposerCore.dom / DialogShell / GitDialog) that this PR does not modify. All 52 review threads resolved.






















What this PR does
This PR gives the daemon React provider an explicit product session context for workspace, standalone, and Live conversations. Workspace requests keep their existing route and compatibility behavior, standalone requests use the capability-gated SDK routes introduced by #10294, and Live restores resolve exactly one trusted non-primary runtime before dispatch. The provider retains standalone working-directory and create-recovery state while keeping the internal Conversations runtime cwd out of the product workspace model.
Non-workspace contexts no longer initialize workspace-only providers, skills, ACP preheat, Git status, or workspace invalidation. Existing WebShell entry points remain unchanged so the visible Global New Chat, Recents, lifecycle controls, and project-control gating can be added separately without depending on the long-running #9811 cutover.
Why it's needed
WebShell needs a typed routing boundary before it can expose sessions that do not belong to a user workspace. Inferring ownership from
workspaceCwdwould either expose the private Conversations runtime as a project or silently fall back to the primary workspace. An explicit context lets create, load, resume, reconnect, and repair remain fail-closed while preserving legacy workspace callers.Reviewer Test Plan
How to verify
Confirm that legacy callers using
workspaceCwdstill create, load, and resume through the ordinary workspace route. For an explicit standalone context, confirm that create/load/resume use the dedicated SDK methods, that create is not automatically retried or masked by the generic action timeout, and that outcome-unknown recovery information remains available. For a Live context, confirm that zero, multiple, untrusted, or primary runtime candidates fail before a session request is sent. Switch rapidly between contexts and verify that only the winning target may publish its transcript, warnings, working-directory state, or recovery state, and that non-workspace contexts do not invoke workspace-only providers.Local verification completed with the focused daemon provider/action/context test set (418 tests), the complete Web Shell suite (4,418 tests), WebUI and Web Shell builds, the repository build and browser bundle, package and repository typechecks/lint, changed-file formatting, public declaration checks, and clean diff audits.
Evidence (Before & After)
N/A — this PR adds provider routing, state, types, tests, and design documentation without adding a visible WebShell entry point.
Tested on
Environment (optional)
macOS with Node.js 22.22.3; dependencies installed with
npm ci. Tests and static checks ran without a sandbox.Risk & Scope
workspaceCwdcallers remain supported; explicit standalone and Live contexts reject a simultaneous legacy cwd instead of guessing ownership.Linked Issues
Related to #8908. Builds on the standalone TypeScript SDK merged in #10294. Independent of #9811.
中文说明
本 PR 做了什么
本 PR 为 daemon React provider 增加了显式的产品会话上下文,用于区分 workspace、standalone 和 Live 会话。Workspace 请求保持现有路由与兼容行为,standalone 请求使用 #10294 引入且受 capability 约束的 SDK 路由,Live 恢复则在发送请求前精确解析出唯一可信且非 primary 的运行时。Provider 会保留 standalone 工作目录和创建恢复状态,同时不会把内部 Conversations 运行时 cwd 暴露为产品 workspace。
非 workspace 上下文不再初始化仅适用于 workspace 的 providers、skills、ACP 预热、Git 状态或 workspace 失效监听。现有 WebShell 入口保持不变,因此可在后续独立加入全局新建会话、最近会话、生命周期控制和项目控制门禁,而不依赖长期进行中的 #9811 切换。
为什么需要
WebShell 在暴露不属于用户 workspace 的会话前,需要一个类型明确的路由边界。如果继续从
workspaceCwd推断所有权,要么会把私有 Conversations 运行时暴露成项目,要么会静默回退到 primary workspace。显式上下文可让创建、加载、恢复、重连和修复保持失败关闭,同时兼容原有 workspace 调用方。Reviewer 测试计划
如何验证
确认继续使用
workspaceCwd的旧调用方仍通过普通 workspace 路由创建、加载和恢复。对于显式 standalone 上下文,确认创建、加载和恢复使用专用 SDK 方法,创建不会自动重试,也不会被通用 action 超时提前遮蔽,并且结果未知时的恢复信息仍然可用。对于 Live 上下文,确认候选运行时为零、多个、不可信或 primary 时,会在发送会话请求前失败。快速切换不同上下文,确认只有最终胜出的目标能够发布 transcript、警告、工作目录状态或恢复状态,并确认非 workspace 上下文不会调用仅适用于 workspace 的 providers。本地已完成聚焦的 daemon provider/action/context 测试(418 项)、完整 Web Shell 测试(4,418 项)、WebUI 和 Web Shell 构建、仓库构建与浏览器 bundle、各 package 与仓库级 typecheck/lint、变更文件格式检查、公共声明检查以及干净 diff 审计。
证据(变更前后)
N/A — 本 PR 增加 provider 路由、状态、类型、测试和设计文档,不增加可见的 WebShell 入口。
测试平台
环境(可选)
macOS,Node.js 22.22.3;使用
npm ci安装依赖。测试和静态检查未使用 sandbox。风险与范围
workspaceCwd调用方继续受支持;显式 standalone 或 Live 上下文若同时提供旧 cwd 会直接拒绝,而不是猜测所有权。关联 Issue
关联 #8908。基于已在 #10294 合入的 standalone TypeScript SDK。独立于 #9811。