fix(web-shell): keep pinned sessions visible in their sidebar group sections - #10416
Conversation
…ections The sidebar lifts pinned rows into the dedicated Pinned section but also dropped them from the group sections: both group rendering paths derived their sections from the pinned-filtered session list, so group membership and counts only ever saw unpinned members. A group whose members are all pinned rendered `· 0` with no rows — visually identical to losing the memberships that the organization store still holds (#10391). Derive group sections from the search-filtered list (which still contains pinned rows) in both paths — `groupedSessions` in WorkspaceSection and `sessionSections` in WebShellSidebar — while Ungrouped keeps excluding pinned rows. Pinned members therefore stay listed in the Pinned section and reappear inside their named group with the existing pinned-row affordances; unassigned pinned sessions stay Pinned-section-only and do not spill into Ungrouped. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Re-run on the updated head — the PR grew from 1 commit to 11 since the first pass, all addressing the same bug and the review rounds on it. Template looks good ✓ Problem: observed bug with solid evidence. Linked issue #10391 carries screenshots, a headless-Chromium reproduction against a real 0.22.2 daemon, and DOM-level evidence that a group whose members are all pinned renders Direction: aligned. Group membership is user-created data silently vanishing from the sidebar; fixing the rendering derivation is squarely within the web-shell's mission. Size: not core infrastructure — two sidebar components plus tests: ~156 production lines ( Approach: scope is right. The core fix is the minimal one — derive group buckets from the search-filtered list before the pin filter, in both rendering paths. The later commits are consequences of that fix, not scope creep: rendering a pinned member in two places required settling the duplicate rename form, color-bucket precedence, catalog order, and the empty-state gates — each landed with a matching test, and each maps 1:1 to a resolved review thread (all 13 closed). Risk: no high-risk paths touched; no elevated risk signals. Moving on to code review. 🔍 中文说明对更新后的 head 重新运行 —— 自首次审查以来,本 PR 从 1 个提交增加到 11 个,全部围绕同一 bug 及其 review 意见。 模板完整 ✓ 问题:已观测到的 bug,证据充分。关联 issue #10391 提供了截图、针对真实 0.22.2 守护进程的 headless Chromium 复现,以及 DOM 级证据:成员全部被置顶的分组渲染为 方向:对齐。分组归属是用户主动创建的数据,却在侧边栏中静默消失;修复渲染推导完全属于 web-shell 的职责范围。 规模:非核心基础设施 —— 两个侧边栏组件加测试:约 156 行生产代码( 方案:范围合理。核心修复是最小改动 —— 在两条渲染路径中,分组桶都改为在置顶过滤之前、从搜索过滤后的列表推导。后续提交是该修复的连带后果,而非范围蔓延:置顶成员在两处渲染后,需要解决重复的命名表单、颜色桶优先级、目录排序和空状态判断 —— 每个都带了对应测试,并与已解决的 review 线程一一对应(13 条全部关闭)。 风险:未触及高风险路径;无升级风险信号。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code review (re-run at the 11-commit head)Independent baseline: given #10391's root cause (both grouping paths derive sections from the already pinned-filtered list), the minimal fix is to derive the group buckets from the search-filtered list and keep the pin filter only where the Pinned section owns the rows. That is exactly what this PR does; this pass focuses on the ten commits added since the first review. What I verified on the current head:
No correctness, security, or convention findings at this head. All 13 review threads from the earlier rounds are resolved, and the fix commits map onto them one-to-one. Testing evidenceUnattended CI run — no PR code was built or executed here; the evidence is the PR's own CI on the reviewed commit, read through the API.
All Visuals preview note — pre-existing failure, not this PR's. The "no preview" warning in this thread is real but not caused by this change: the Sandboxed verification: an isolated Real-scenario testing: N/A for this run — unattended CI path (live-product driving is local-invocation only). The author's Linux-only manual UI evidence (screenshots + DOM assertions against a throwaway 0.22.2 daemon) is their own claim, attributed as such; the change is client-side React rendering with platform-independent logic, and the behavioural claim is pinned by the deterministic tests above plus the in-flight 中文说明代码审查(在 11 个提交的 head 上重新运行)独立基线:根据 #10391 的根因(两条分组路径都从"已过滤置顶"的列表推导分区),最小修复是把分组桶改为从搜索过滤后的列表推导,只在 Pinned 区拥有行的地方保留置顶过滤。本 PR 正是这么做的;本轮重点审查首次审查后新增的十个提交。 在当前 head 上核实的内容:
当前 head 无正确性、安全性或规范问题。此前各轮 review 的 13 条线程全部已解决,修复提交与之逐一映射。 测试证据无人值守 CI 运行 —— 未在此构建或执行任何 PR 代码;证据为所审提交上 PR 自身的 CI,通过 API 读取。全部 视觉预览说明 —— 既有失败,与本 PR 无关: 沙箱验证:随本次审查触发的隔离 真实场景测试:本次不适用 —— 无人值守 CI 路径(驱动真实产品仅限本地调用)。作者的 Linux 手工 UI 证据(截图 + DOM 断言)是其自述,按自述对待;改动为客户端 React 渲染,逻辑与平台无关,行为声明由上述确定性测试与进行中的 — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean re-review of a well-evidenced fix; the only nit is that the visuals preview is broken by a pre-existing main-side scenario failure, so there is no automated visual diff for this head. Stepping back: the re-run changed my view of nothing and confirmed everything. The bug is real and user-visible (group memberships silently vanishing from the sidebar), the fix is the smallest one the root cause allows, and the ten commits added since the first pass are not churn — each answers a concrete review finding or a consequence of the double rendering the fix introduces, and each carries a test that pins it. The rename-suppression design in particular degrades sensibly: with the Pinned section collapsed or its page unsettled, the visible row hosts the form itself. CI on the reviewed commit is fully green — unit suite, web-shell E2E smoke, integration, and desktop shells — and the one red-looking signal, the visuals scenario, fails identically at the merge base, so it is main's to fix, not this PR's. Non-blocking follow-ups: (1) the Approving, pinned to the reviewed commit. 中文说明信心:4/5 —— 对一个证据充分的修复的干净复审;唯一的小瑕疵是视觉预览被 main 上既有的场景失败破坏,本 head 没有自动化视觉对比。 退一步看:重新运行没有改变我的任何判断,反而确认了一切。bug 真实且用户可见(分组归属从侧边栏静默消失),修复是根因允许的最小改动,首次审查后新增的十个提交不是杂音 —— 每个都回应一条具体的 review 意见或修复引入的双重渲染后果,且都带钉住它的测试。命名表单抑制设计尤其稳妥:Pinned 区折叠或其分页未落定时,可见行自行承载表单。所审提交上的 CI 全绿 —— 单测、web-shell E2E smoke、集成、桌面壳 —— 唯一偏红的信号(视觉场景)在 merge-base 上同样失败,应由 main 修复,不记在本 PR 头上。 非阻断跟进:(1) 批准,锚定到所审提交。 — 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. |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Test Plan (not a blocker): client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory.
中文说明
Test Plan(非阻断):client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory。
— qwen3.8-max via Qwen Code /review (v0.22.2)
…e label WorkspaceSection's empty-state gate keyed on the pinned-filtered visibleSessions, so a workspace whose sessions are all pinned group members rendered the "No sessions" label even though the group sections still hold those members. Fall through to the grouped view when any group section has sessions. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Two follow-ups to the pinned group member merge: - The sidebar body's empty-state gate short-circuited on an active search before consulting sessionSections, so a search whose only matches are pinned group members rendered "No sessions". Drop the search short-circuit; the section emptiness check covers the real empty case. - A pinned group member renders in both the Pinned section and its group section; starting a rename mounted two autofocused forms and the rival input's blur cancelled the rename. While the Pinned section is expanded its row hosts the rename form and the duplicate group row does not mount one. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Adds a mixed pinned/unpinned group case asserting every member renders exactly once in its group section; removing the !session.isPinned guard from the merge loop now fails the suite. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- R1-4 merge loop ignores color classification for pinned sessions — already reported (comment 3882301476)
Not explored to full depth (tool budget reached): "agent 6b": running the two touched test files (WebShellSidebar.session-pinning.test.tsx, WorkspaceSection.test.tsx) — the review worktree has no node_modules and vitest ca….
Test Plan (not a blocker): client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory.
[Critical] R2-2 — packages/web-shell/client/components/sidebar/WorkspaceSection.tsx:468-470 — The duplicate-row rename suppression this commit adds is wired only to the primary workspace's group rows: pinned group members of secondary (non-primary) workspaces now render two rows — one in the top-level Pinned section (pinnedSessions merges secondaryPinnedSessions), one in the workspace's group section (new in this diff, since group items derive from searchedSessions) — but the project-workspace <WorkspaceSection> renderSession callback in WebShellSidebar.tsx (~5405-5410) calls renderSessionRow({...session, workspaceCwd: ws.cwd}) with no options, and the Pinned-section row passes none either. Both rows share one identity, so both compute isEditing === true and each mounts an autoFocus rename input in one commit; the second input's autofocus blurs the first, whose onBlur={cancelRename} fires — the rename cancels itself the moment it opens, and the session can never be renamed. This is the sibling entrance of the round-1 double-rename-form Critical (R1-2) that the fix did not cover; before this diff the group bucket derived from the pinned-filtered list, so the second row did not exist. Concretely: a multi-workspace sidebar with organization enabled, the default (non-channel) source and the Pinned section expanded — double-clicking either row of a pinned member in a secondary workspace's group silently cancels every rename attempt. Probe witness: PR as-is mounts 2 rows and 0 rename inputs after dblclick (blur cascade cancels); with the fix applied, 2 rows and 1 input. Suggested fix: thread the same suppression through the project-workspace call site — renderSession={(session) => renderSessionRow({ ...session, workspaceCwd: ws.cwd }, { renameFormDisabled: selectedSessionSource !== 'channel' && Boolean(session.isPinned) && pinnedExpanded })}. The selectedSessionSource !== 'channel' guard matters: in channel mode the Pinned section is hidden, so the workspace row is the only copy and must stay editable. (The live-workspace call site passes organizationEnabled={false} and renders no group sections.) Please add a sidebar-level test that renders a secondary workspace whose pinned page carries a grouped member, starts a rename, and asserts exactly one input[aria-label="Rename: …"] mounts; removing the option must turn it red. (This comment was relocated from the inline position WorkspaceSection.tsx:470 because an existing comment already occupies that line.)
中文说明
[Critical] R2-2 — packages/web-shell/client/components/sidebar/WorkspaceSection.tsx:468-470 — 本提交新增的重复行重命名抑制只接入了主工作区的分组行:次级(非主)工作区的置顶分组成员现在会渲染成两行 —— 顶部 Pinned 区一行(pinnedSessions 合并了 secondaryPinnedSessions),该工作区的分组区一行(本 diff 新引入,分组条目改从 searchedSessions 推导)—— 但 project-workspace 的 <WorkspaceSection> renderSession 回调(WebShellSidebar.tsx 约 5405-5410 行)调用 renderSessionRow({...session, workspaceCwd: ws.cwd}) 时不传任何 options,Pinned 区行同样不传。两行共享同一身份,isEditing 同时为真,一次提交挂载两个带 autoFocus 的重命名输入框;第二个输入框获焦使第一个失焦,其 onBlur={cancelRename} 触发 —— 重命名在打开瞬间自我取消,该会话永远无法被重命名。这是第一轮「双重重命名表单」Critical(R1-2)修复遗漏的同源入口;本 diff 之前分组条目来自过滤掉置顶行的列表,第二行并不存在。具体场景:多工作区侧边栏、开启 organization、默认(非 channel)来源、Pinned 区展开 —— 双击次级工作区分组内置顶成员的任一行,每次重命名都会被静默取消。探针验证:PR 现状挂载 2 行、双击后 0 个重命名输入框(blur 级联取消);应用修复后 2 行、1 个输入框。建议修复:把同样的抑制接入 project-workspace 调用点 —— renderSession={(session) => renderSessionRow({ ...session, workspaceCwd: ws.cwd }, { renameFormDisabled: selectedSessionSource !== 'channel' && Boolean(session.isPinned) && pinnedExpanded })}。selectedSessionSource !== 'channel' 守卫是必要的:channel 模式下 Pinned 区隐藏,工作区行是唯一副本,必须保持可重命名。(live-workspace 调用点传 organizationEnabled={false},不渲染分组区。)请补充一个侧边栏级测试:渲染一个置顶分页含分组成员的次级工作区,发起重命名,断言恰好挂载一个 input[aria-label="Rename: …"];移除该参数后测试必须变红。(本条因 WorkspaceSection.tsx:470 行已有评论占用而从行内位置移至正文。) 中文说明
本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
未探索到全部深度(达到工具调用预算):"agent 6b":running the two touched test files (WebShellSidebar.session-pinning.test.tsx, WorkspaceSection.test.tsx) — the review worktree has no node_modules and vitest ca…。
Test Plan(非阻断):client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory。
[Critical] R2-2 — packages/web-shell/client/components/sidebar/WorkspaceSection.tsx:468-470 — The duplicate-row rename suppression this commit adds is wired only to the primary workspace's group rows: pinned group members of secondary (non-primary) workspaces now render two rows — one in the top-level Pinned section (pinnedSessions merges secondaryPinnedSessions), one in the workspace's group section (new in this diff, since group items derive from searchedSessions) — but the project-workspace <WorkspaceSection> renderSession callback in WebShellSidebar.tsx (~5405-5410) calls renderSessionRow({...session, workspaceCwd: ws.cwd}) with no options, and the Pinned-section row passes none either. Both rows share one identity, so both compute isEditing === true and each mounts an autoFocus rename input in one commit; the second input's autofocus blurs the first, whose onBlur={cancelRename} fires — the rename cancels itself the moment it opens, and the session can never be renamed. This is the sibling entrance of the round-1 double-rename-form Critical (R1-2) that the fix did not cover; before this diff the group bucket derived from the pinned-filtered list, so the second row did not exist. Concretely: a multi-workspace sidebar with organization enabled, the default (non-channel) source and the Pinned section expanded — double-clicking either row of a pinned member in a secondary workspace's group silently cancels every rename attempt. Probe witness: PR as-is mounts 2 rows and 0 rename inputs after dblclick (blur cascade cancels); with the fix applied, 2 rows and 1 input. Suggested fix: thread the same suppression through the project-workspace call site — renderSession={(session) => renderSessionRow({ ...session, workspaceCwd: ws.cwd }, { renameFormDisabled: selectedSessionSource !== 'channel' && Boolean(session.isPinned) && pinnedExpanded })}. The selectedSessionSource !== 'channel' guard matters: in channel mode the Pinned section is hidden, so the workspace row is the only copy and must stay editable. (The live-workspace call site passes organizationEnabled={false} and renders no group sections.) Please add a sidebar-level test that renders a secondary workspace whose pinned page carries a grouped member, starts a rename, and asserts exactly one input[aria-label="Rename: …"] mounts; removing the option must turn it red. (This comment was relocated from the inline position WorkspaceSection.tsx:470 because an existing comment already occupies that line.)
中文说明
[Critical] R2-2 — packages/web-shell/client/components/sidebar/WorkspaceSection.tsx:468-470 — 本提交新增的重复行重命名抑制只接入了主工作区的分组行:次级(非主)工作区的置顶分组成员现在会渲染成两行 —— 顶部 Pinned 区一行(pinnedSessions 合并了 secondaryPinnedSessions),该工作区的分组区一行(本 diff 新引入,分组条目改从 searchedSessions 推导)—— 但 project-workspace 的 <WorkspaceSection> renderSession 回调(WebShellSidebar.tsx 约 5405-5410 行)调用 renderSessionRow({...session, workspaceCwd: ws.cwd}) 时不传任何 options,Pinned 区行同样不传。两行共享同一身份,isEditing 同时为真,一次提交挂载两个带 autoFocus 的重命名输入框;第二个输入框获焦使第一个失焦,其 onBlur={cancelRename} 触发 —— 重命名在打开瞬间自我取消,该会话永远无法被重命名。这是第一轮「双重重命名表单」Critical(R1-2)修复遗漏的同源入口;本 diff 之前分组条目来自过滤掉置顶行的列表,第二行并不存在。具体场景:多工作区侧边栏、开启 organization、默认(非 channel)来源、Pinned 区展开 —— 双击次级工作区分组内置顶成员的任一行,每次重命名都会被静默取消。探针验证:PR 现状挂载 2 行、双击后 0 个重命名输入框(blur 级联取消);应用修复后 2 行、1 个输入框。建议修复:把同样的抑制接入 project-workspace 调用点 —— renderSession={(session) => renderSessionRow({ ...session, workspaceCwd: ws.cwd }, { renameFormDisabled: selectedSessionSource !== 'channel' && Boolean(session.isPinned) && pinnedExpanded })}。selectedSessionSource !== 'channel' 守卫是必要的:channel 模式下 Pinned 区隐藏,工作区行是唯一副本,必须保持可重命名。(live-workspace 调用点传 organizationEnabled={false},不渲染分组区。)请补充一个侧边栏级测试:渲染一个置顶分页含分组成员的次级工作区,发起重命名,断言恰好挂载一个 input[aria-label="Rename: …"];移除该参数后测试必须变红。(本条因 WorkspaceSection.tsx:470 行已有评论占用而从行内位置移至正文。) — qwen3.8-max via Qwen Code /review (v0.22.2)
…rder The pinned-membership merge routed pinned sessions by groupId only, ignoring the main loop's color classification, and appended them after every unpinned member of the group bucket. Fold the merge into one pass over the search-filtered catalog so pinned rows keep both the color-first precedence and the daemon catalog position (pinned sorts first there), which keeps them ahead of the group preview limit. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Wire searchQuery through the WorkspaceSection test helper and add one case per grouping path: a query matching only the pinned member keeps it in its group, and a query matching only the unpinned member drops the pinned member from the sidebar group bucket. Both go red when the group items or the bucket loop read the un-search-filtered list. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Both existing issue-10391 cases gave every pinned session a group, where the assigned set already filters it out of ungrouped. Add the case that actually carries the property: with excludePinned, a pinned group-less session must not spill into the Ungrouped section. Goes red when ungrouped is sourced from searchedSessions instead of visibleSessions. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…ned section The rename test only exercised the default pinnedExpanded state; the collapsed branch of the renameFormDisabled guard had no coverage. Collapse the Pinned section and start a rename from the pinned member's group row, asserting exactly one input mounts. Goes red when the guard drops && pinnedExpanded. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 3b": could not execute WebShellSidebar.session-pinning.test.tsx / WorkspaceSection.test.tsx to confirm green at HEAD — the worktree has no installed node_module….
Test Plan (not a blocker): client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory.
Convergence: round 3 posted 3 inline comment(s), 2 of them reported for the first time; the previous round posted 1 (1 new). The rate of new findings is not falling. Batching the remaining fixes and verifying them before the next push, or dropping this PR's reviews to --severity-floor critical, keeps the loop from re-deriving the same set. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
未探索到全部深度(达到工具调用预算):"agent 3b":could not execute WebShellSidebar.session-pinning.test.tsx / WorkspaceSection.test.tsx to confirm green at HEAD — the worktree has no installed node_module…。
Test Plan(非阻断):client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory。
收敛情况:第 3 轮发布了 3 条行内评论,其中 2 条是首次提出;上一轮发布了 1 条(其中 1 条首次提出)。新发现的产出速度没有下降。把剩余修复攒成一批、验证后再推送,或将本 PR 的评审降到 --severity-floor critical,可以避免循环反复推导同一组发现。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.2)
Secondary workspace pinned group members render two rows (the sidebar-level Pinned section and the workspace's own group section); both mounted an autofocused rename input, whose blur cascade cancelled the rename the moment it opened. Thread the same renameFormDisabled suppression through the project-workspace renderSession callback, keeping channel-mode rows editable (no Pinned section there) and collapsed-Pinned rows as the rename host. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
…ouped Pin the sidebar bucketing loop's Ungrouped-spill guard: a pinned session without group or color stays Pinned-section-only while Ungrouped keeps just the plain row. Removing the guard turns this test red (the pinned row duplicates into Ungrouped with an inflated count). Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): "agent 6c": run the new vitest cases in WebShellSidebar.session-pinning.test.tsx — the review worktree has no node_modules , and npm ci plus the prerequisite workspace….
Test Plan (not a blocker): client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory.
5 Suggestion(s) were drafted inline past the resolved critical posting floor — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s); the CLI moved them into the deferral list below (floor enforcement).
Deferred under the convergence posture (round 4, not a blocker) — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s) — recorded, not requested in this round:
packages/web-shell/client/components/sidebar/WebShellSidebar.tsx:5418 — [review] This channel-mode exclusion clause has no test witness: no sidebar-level test ever selects the channel source (the only channel reference in the test file is…packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx:1431 — [review] This test overrides workspace.client.workspaceByCwd with mockImplementation (the only occurrence in the file) and replaces workspaceA…packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx:929 — [review] The 13-line Design-group catalog literal is copied three times in this describe block — here, inside mockDesignGroupCatalog() , and as the…packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx:981 — [review] The 13-line Design-group catalog literal is copied three times in this describe block — in the first test, here inside mockDesignGroupCata…packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx:1385 — [review] The 13-line Design-group catalog literal is copied three times in this describe block — in the first test, inside mockDesignGroupCatalog(…packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx:1015 — [review] search-interaction sequence pasted verbatim into two new tests (code-age rule: anchored on code round 3 read and did not flag)
Convergence: round 4 posted 1 inline comment(s), 1 of them reported for the first time; the previous round posted 3 (2 new). Findings keep coming back to the same files: packages/web-shell/client/components/sidebar/WebShellSidebar.tsx (findings in rounds 2, 3; 1 more now). 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.)
中文说明
未探索到全部深度(达到工具调用预算):"agent 6c":run the new vitest cases in WebShellSidebar.session-pinning.test.tsx — the review worktree has no node_modules , and npm ci plus the prerequisite workspace…。
Test Plan(非阻断):client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory。
5 条 Suggestion 在已解析的 critical 发布下限之外被起草为行内评论——发布下限因首次发现速率连续 2 轮未下降而提前生效;CLI 已将其移入下方延后清单(下限强制执行)。
收敛姿态下延后(第 4 轮,非阻断)——发布下限因首次发现速率连续 2 轮未下降而提前生效——已记录,本轮不要求修改:共 6 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 4 轮发布了 1 条行内评论,其中 1 条是首次提出;上一轮发布了 3 条(其中 2 条首次提出)。发现反复回到同一批文件:packages/web-shell/client/components/sidebar/WebShellSidebar.tsx(第 2、3 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.2)
The rename suppression for secondary-workspace group rows keyed on pin state and pinnedExpanded alone. pinnedSessions merges only the pinned catalog pages and the primary sessions page, never the workspace's own session page that the group section bucketizes, so while the pinned page is unsettled (cold-load race) or erroring (30s retry backoff) the group row stayed suppressed with no host row in the Pinned section: double-click rename became a silent no-op with orphaned editing state. Only suppress the workspace row when the Pinned section actually carries the member, and add a regression test asserting the workspace group row hosts the rename form while the pinned page is absent. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
@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: 26 passed · 0 failed · 26 total Flakiness gate: ✅ 2 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:✅ 通过 · 可合入(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:26 通过 · 0 失败 · 26 总计 抖动门:✅ 2 changed test file(s) x 5 identical rounds, no divergence Verification reportPR 10416 — deep verification (follow-up round)Verdict: 中文摘要
Previous-finding status (round at 2026-08-29T01:16, previous head
|
| # | Finding (prev) | Severity | Status at 504dc341 |
|---|---|---|---|
| F1 | Secondary-workspace rename suppression keyed only on isPinned && pinnedExpanded, not on the Pinned section actually carrying the member; during the pinned-page cold-load race or its 30 s error retry, double-click rename on the group row was a silent no-op |
high | fixed — final commit 504dc341 adds the pinnedSessions.some(…) host-presence conjunct. Re-measured: reverting only that hunk turns the new regression test red with AssertionError: expected +0 to be 1 (0 rename inputs — exactly the silent no-op); head passes. I agree with the fix shape: it keys suppression on the rendered host, not on page state. |
| F2 | PR-body vitest command "does not work as written"; suite total discrepancy (4421 → 4469) | low | does not reproduce / superseded — re-ran the PR-body command verbatim from packages/web-shell at the new head: it collects both files and runs 58/58 green. Suite total re-measured at 206 files / 4463 tests, all green (junit cross-check 4463/0). The previous 4469 did not reproduce in this container; the body's 4421 was measured against the author's older base (b89ba11e vs this round's base tip 48ec0083, which carries ~36 additional upstream tests). |
The previous round's correction (against an earlier bot review that claimed the rename form "mounts twice unconditionally") still stands: isEditing requires editingSessionIdentity === sessionIdentity and !renameFormDisabled; the double-mount hazard exists only while a rename is active and both rows render, which is exactly what the suppression gates.
Central claim + A/B
Central claim: pinned sessions stay visible inside their named group sections (both grouping paths), while the Pinned section keeps its rows and Ungrouped keeps excluding group-less pinned sessions.
| Cell | Environment | Oracle | Result |
|---|---|---|---|
base 48ec0083 |
worktree tmp/base-tree, PR's two test files copied in, root node_modules (PR leaves lockfile untouched; internal @qwen-code/* links resolve to the head tree but git diff HEAD^1..HEAD touches only packages/web-shell, so every linked dep is byte-identical base vs head — realpath check quoted in Methodology) |
16 #10391 tests | 13 red / 3 green |
head 504dc341 |
repo at merge HEAD | same | 0 red / 16 |
Witness: 01-ab-central-base-vs-head.png (raw logs: logs/ab-base.txt, logs/ab-head.txt).
The 13 flips fail at base on the intended assertions, e.g. expected 'Design· 0' to contain '· 1' (the exact #10391 symptom), expected 'Design· 1Active member' to contain '· 2', expected 1 to be 2 (secondary two-row). The 3 base-green tests are green by exclusion (base drops pinned rows entirely): drops a pinned member from its group when search only matches unpinned members, and keeps a group-less pinned session out of the Ungrouped section in both files — mutations M9/M1 below prove they pin the head-side guards. All 17 pre-existing #9465 pinning regression tests pass on both arms (no regression in optimistic pin/unpin reconciliation).
F1 intermediate control (one fix, isolated): 02-f1-host-presence-fix-ab.png — cell 1 reverts only the final commit's hunk in a scratch copy of head → 1 failed with expected +0 to be 1; cell 2 (restored head) → 1 passed.
Mutation matrix (head, scratch worktree)
| # | Reverted guard | Suite that should catch it | Result |
|---|---|---|---|
| control | (none) | — | green |
| M1 | sidebar bucket-loop Ungrouped-spill guard (isPinned && source !== 'channel') |
sidebar group-less-pinned test | KILLED |
| M2 | primary group-row rename suppression | two-row single-form test | KILLED |
| M3 | primary suppression conjunct && pinnedExpanded |
collapsed-Pinned single-form test | KILLED |
| M4 | secondary host-presence conjunct (pinnedSessions.some) == final-commit hunk |
pinned-page-absent rename test | KILLED (expected +0 to be 1) |
| M5 | WorkspaceSection group sections ← searchedSessions |
WS count/preview tests | KILLED |
| M6 | WorkspaceSection empty-state fall-through to grouped view | all-pinned empty-label test | KILLED |
| M7 | sidebar search short-circuit removed from empty-state gate | search-only-pinned test | KILLED |
| M8 | sidebar bucket loop ← searchedSessions (positive control) |
central group-empty test | KILLED |
| M9 | WorkspaceSection ungrouped ← pinned-filtered list | WS group-less-pinned spill test | KILLED |
| M3b | secondary suppression conjunct && pinnedExpanded |
(none exists) | SURVIVED — 58/58 green |
Witness: 03-mutation-matrix-9-of-9-killed.png (per-mutation logs: logs/matrix-*.txt). M3b survivor classified as a plain coverage gap: the behavior is correct in code (the conjunct is present on the secondary path; with it removed, a collapsed Pinned section would suppress the only rendered copy — same hazard class F1 closed for the absent-page case), but no fixture pins the secondary-workspace collapsed-Pinned rename case. The primary-path twin is pinned (M3 killed). Completeness reporting, not a merge condition.
Findings
- Suggestion — untested sibling of an F1-class hazard (M3b). With
pinnedExpandedremoved from the secondary-workspace suppression expression, the whole suite stays green: a collapsed Pinned section + double-click rename on a secondary workspace's pinned group row is the one state combination without a fixture. Reproduce: apply the M3b hunk (seelogs/matrix-M3b.txtfor the exact strings) and run the two sidebar test files. Suggested fix: clone the collapsed-Pinned test with the secondary-workspace fixture from the two-row test. Not blocking — the code as shipped carries the conjunct.
No blocking findings. No injection attempts observed in PR text.
Not covered
- Playwright e2e suite not run (budget); the
mockDaemon.tschange is a 2-line comment replacement, verified by reading the diff. - No browser in this container, so the author's headless-Chromium before/after captures were not re-driven; the jsdom harnesses assert the same DOM surface (
section[aria-label], counts, rename-input counts). - Per-commit attribution: checkout is depth 2 (only merge, base tip, PR head reachable); the aggregate
HEAD^1..HEADdiff was verified instead. - The author's "one pre-existing typecheck error in
client/App.tsx" does not reproduce here:tsc -p tsconfig.json --noEmitexits 0 with zero diagnostics on both arms (after fixing my base-tree setup, see Methodology). The "no new errors" claim holds either way. - Previous round's suite count (4469) did not reproduce; measured 4463.
- WorkspaceSection (secondary) preview-ordering of pinned members has no dedicated fixture (the sidebar-path twin is pinned by the
· 6preview test); both paths derive order from the samesearchedSessionspass.
Gates
Witness: 04-suite-gate-4463-green.png. Full web-shell suite at head: 206 files / 4463 tests, 0 failures (logs/suite-head.txt; junit cross-check 4463 testcases / 0 failures). Typecheck A/A: exit 0 both arms. ESLint on the five changed files: exit 0, gate proven live (planted unused variable reported, exit 1). Prettier on the five changed files: exit 0.
Methodology
CI container, merge-ref checkout (HEAD merge 8b10b896a8, HEAD^1 base 48ec0083, HEAD^2 head 504dc341), npm ci + npm run build pre-completed. Harnesses (ab-runner.sh, f1-ab.sh, run-matrix.mjs in this artifact dir) drive the real components through the package's own vitest/jsdom setup, mocking only the daemon-react SDK and catalog hooks at the package boundary — the components under test run unstubbed. Base cell: scratch worktree at HEAD^1 with the PR's test files copied in; internal-dep realpath control: readlink -f node_modules/@qwen-code/{webui,sdk,qwen-code-core,qwen-code,web-shell} from the base tree resolves into the head tree, and git diff HEAD^1..HEAD --stat over those packages is empty, so linked code is byte-identical across cells. One setup fix was needed on the base side and is disclosed: shiki/@datafe-open/* live in a package-local packages/web-shell/node_modules (not hoisted), absent from worktrees; it was symlinked in for the base typecheck (third-party only, unchanged by the PR) — the first base typecheck attempt failed on exactly those missing modules, proving the control caught the gap. Mutations ran in a second scratch worktree (tmp/head-scratch), each reverted via git checkout (tree verified clean afterwards). Raw logs in logs/.
Flakiness gate log
rounds=5 files=2 skipped=0
file packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/components/sidebar/WebShellSidebar.session-pinning.test.tsx
file packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/components/sidebar/WorkspaceSection.test.tsx
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: PPPPP
packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: PPPPP
verdict: pass
summary: 2 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: P (exit 0)
round 1 · packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: P (exit 0)
round 2 · packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: P (exit 0)
round 2 · packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: P (exit 0)
round 3 · packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: P (exit 0)
round 3 · packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: P (exit 0)
round 4 · packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: P (exit 0)
round 4 · packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: P (exit 0)
round 5 · packages/web-shell/client/components/sidebar/WebShellSidebar.session-pinning.test.tsx: P (exit 0)
round 5 · packages/web-shell/client/components/sidebar/WorkspaceSection.test.tsx: P (exit 0)
Evidence images
Harness 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. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers.
2 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:
- Design-group catalog fixture literal duplicated across the new describe block (a fourth copy added by this round's cold-load test) — already recorded in the round-4 deferral list (review 5055745753)
- workspace-row rename suppression's channel clause has no test witness — already recorded in the round-4 deferral list (review 5055745753)
Test Plan (not a blocker): client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory.
Deferred under the convergence posture (round 5, not a blocker) — the floor engaged early: the first-time-finding rate has not fallen for 2 consecutive round(s) — recorded, not requested in this round:
packages/web-shell/client/components/sidebar/WebShellSidebar.tsx:5425 — [review] D5-1 workspace-row rename suppression's pinnedExpanded clause has no test witness
中文说明
已审查——无阻断问题。
本轮确认的 2 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。
Test Plan(非阻断):client/components/sidebar/WorkspaceSection.test.tsx — no such file or directory; client/components/sidebar/WebShellSidebar.session-pinning.test.tsx — no such file or directory; client/App.tsx — no such file or directory。
收敛姿态下延后(第 5 轮,非阻断)——发布下限因首次发现速率连续 2 轮未下降而提前生效——已记录,本轮不要求修改:共 1 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.3)
chiga0
left a comment
There was a problem hiding this comment.
No blocking findings. Approve.
What was checked
Root cause and fix (WorkspaceSection.tsx + WebShellSidebar.tsx):
- Old: group sections derived from
visibleSessions(pinned-filtered), so a group whose members were all pinned rendered· 0with no rows. - Fix: split into
searchedSessions(search-filtered, includes pinned) andvisibleSessions(also pinned-filtered, for the flat list). Group buckets now usesearchedSessions;ungrouped/ flat list still usevisibleSessions. Pinned sessions without a group are explicitly skipped fromrecentSessionsviaif (session.isPinned && selectedSessionSource !== 'channel') continue, so they stay Pinned-section-only. - Channel mode guard is correct: channel has no Pinned section, so its pinned rows retain the normal
recentSessionspath. ✓
renameFormDisabled logic (prevents two simultaneous rename inputs):
- Primary workspace group rows:
session.isPinned && pinnedExpanded— when Pinned section is expanded, its row is the rename host; group row suppresses. When collapsed, group row takes over. ✓ - Secondary workspace rows: same, plus a
pinnedSessions.some(...)guard for the cold-load race (before the pinned catalog page settles, the workspace row is the only copy and must stay editable). ✓
Empty-state condition:
- Removed
searchQuery.trim()from the empty-state guard. Previously, a search that matched only pinned members would show "No sessions" even thoughsessionSectionshad non-empty groups. NowsessionSections.length === 0is the controlling predicate. ✓
Test validity:
- CI
Test (ubuntu-latest, Node 22.x)— SUCCESS: runs vitest including the new cases. - New tests are behavioral (DOM assertions against rendered output), cover: all-pinned group count, search-match-pinned-only group visibility, rename form singleton, mixed pinned/unpinned count, color-section retention, catalog-order preview limit, search-match-unpinned-only group trim, rename-from-collapsed-Pinned, groupless-pinned stays out of Ungrouped, secondary-workspace rename form, cold-load race.
- Prior passing regression guard (
excludePinnedoff / channel mode) not broken: CI green.
CI
Test (ubuntu-latest, Node 22.x)— PASSIntegration Tests (no-AK, No Sandbox)— PASSDesktop Shell (ubuntu-22.04)— PASSDesktop Shell (windows-2022)— PASSCapture web-shell visuals— PASSweb-shell E2E Smoke— PASSTest (macos-latest / windows-latest)— SKIPPED (pre-existing)
Cross-check: bot's latest review is APPROVED ("LGTM, looks ready to ship") — consistent with this analysis.
Reviewed with AI assistance.
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
Gate review at head 504dc341: prior blockers are closed (round-5 bot review at this exact head concluded no blocking findings; 0 unresolved threads; remaining suggestions sit in the recorded deferral list, e.g. rename-suppression test witnesses). All substantive required checks pass (Test ubuntu incl. web-shell E2E smoke/coverage; the only pending item is the post-merge delete-asset-branch cleanup job). Independent read of the production diff found no new Critical: searched/visible list split is consistent across both derivation paths, Ungrouped and the empty-label guard keep pinned rows Pinned-only outside channel mode, and the rename suppression on duplicated rows is coherent with pinnedExpanded. +868 lines of tests pin the three red-before scenarios plus regression guards.
中文:门禁通过。历史阻塞已闭环(当前 head 上无阻断结论、0 未解决线程),required CI 全绿,独立复查未发现新 Critical;唯二遗留是已记录的非阻塞测试见证类建议。LGTM。




What this PR does
The web-shell sidebar lifts pinned sessions into the dedicated Pinned section, but it also silently dropped them from their group sections: both group rendering paths derived sections from the already pinned-filtered session list, so group membership and counts only ever saw unpinned members. This change keeps pinned members inside their named group in both paths —
groupedSessionsinWorkspaceSection(secondary workspaces) andsessionSectionsinWebShellSidebar(primary workspace) — by deriving the group buckets from the search-filtered list, which still contains pinned rows. The Pinned section keeps its rows, and Ungrouped keeps excluding pinned sessions, so unassigned pinned sessions stay Pinned-section-only.Why it's needed
Reported in #10391: a group whose members are all pinned rendered
· 0with no rows — visually identical to losing the memberships, even though the server-side organization store and the organized-view API still return the correctgroupId. Assigning an already-pinned session to a group also gave no visible feedback. Group memberships are data the user created deliberately; pinning a member must not make the membership disappear from the sidebar.Reviewer Test Plan
How to verify
Unit/integration (deterministic), in
packages/web-shell:Three new cases (red before the fix, green after):
WorkspaceSectionwithexcludePinned+ one group whose only member is pinned: the group renders· 1and contains the pinned member row; the pinned member does not fall into Ungrouped. Before the fix it renderedDesign· 0with no rows.· 2with both rows; no Ungrouped bucket appears. Before:· 1(unpinned member only).WebShellSidebar(primary-workspace path) with the same data shape: group shows· 1with the pinned row, the row also stays in the Pinned section, and Ungrouped only holds the two ungrouped sessions.Regression guards: the pre-existing channel-mode test (
excludePinnedoff) still passes — pinned channel rows stay inside their platform section; the full package suite passes (206 files / 4421 tests).Manual UI verification (real runtime, headless Chromium): a throwaway
qwen serveinstance on a non-default port with a seeded workspace — one session pinned and assigned to a named group (sole member), two ungrouped sessions — served through the package's vite dev server. Before/after captured by reverting/applying only this diff on the same daemon data.Typecheck (
tsc -p tsconfig.json --noEmitinpackages/web-shell): no new errors introduced by this diff. One pre-existing error inclient/App.tsx(MODES_CYCLE.indexOf(currentMode)with astringmode) reproduces identically with this diff reverted on the same locally built workspace deps, so it is unrelated to this change.Evidence (Before & After)
Before — all-pinned group renders
· 0, member only in Pinned (designTitles: []):After — group shows
· 1with the pinned member row; still listed in Pinned; Ungrouped unchanged:DOM assertions captured with the screenshots:
{ "phase": "before", "designSectionText": "Design· 0", "designTitles": [], "ungroupedTitles": ["Ungrouped task B", "Ungrouped task A"], "pinnedTitles": ["Pinned group member"] } { "phase": "after", "designSectionText": "Design· 1Pinned group member", "designTitles": ["Pinned group member"], "ungroupedTitles": ["Ungrouped task B", "Ungrouped task A"], "pinnedTitles": ["Pinned group member"] }Tested on
Environment (optional)
Node v24.19.0; package-local Vitest; UI capture via Playwright-driven headless Chromium against a local
qwen serve(0.22.2, isolated workspace + non-default port + own bearer token) proxied through the web-shell vite dev server. The production daemon on this machine was not touched.Risk & Scope
Linked Issues
Fixes #10391
中文说明
这个 PR 做了什么
Web Shell 侧边栏会把置顶会话提升到专门的「Pinned」区,但此前也把它们从所属分组区里悄悄移除了:两条分组渲染路径都从「已过滤掉置顶行」的会话列表推导分组,因此分组成员与计数永远只能看到未置顶成员。本改动在两条路径中都让置顶成员保留在其命名分组内 ——
WorkspaceSection的groupedSessions(次级工作区)与WebShellSidebar的sessionSections(主工作区)—— 分组桶改为从搜索过滤后的列表(仍包含置顶行)推导。Pinned 区保留原有行;Ungrouped 继续排除置顶会话,因此未分组的置顶会话仍然只出现在 Pinned 区。为什么需要
来自 #10391:成员全部被置顶的分组会渲染成
· 0且没有任何行 —— 视觉上与「归属关系丢失」完全相同,尽管服务端组织存储和 organized-view API 仍返回正确的groupId。把已置顶的会话分配到分组时也没有任何可见反馈。分组归属是用户主动创建的数据;置顶成员不应导致归属从侧边栏消失。评审验证计划
如何验证
单元/集成(确定性),在
packages/web-shell下:三个新用例(修复前红、修复后绿):
WorkspaceSection在excludePinned+ 唯一成员被置顶的分组下:分组渲染· 1且包含置顶成员行;该成员不会落入 Ungrouped。修复前渲染为Design· 0且无行。· 2且两行都在;不出现 Ungrouped 桶。修复前为· 1(仅未置顶成员)。WebShellSidebar(主工作区路径)相同数据形状:分组显示· 1且含置顶行,该行同时保留在 Pinned 区,Ungrouped 只含两个未分组会话。回归保护:既有的 channel 模式测试(
excludePinned关闭)仍通过 —— 置顶的 channel 行仍留在其平台分区内;整包测试通过(206 文件 / 4421 用例)。手工 UI 验证(真实运行时、headless Chromium):在非默认端口起一次性
qwen serve实例,种子数据为一个工作区 —— 一个会话同时被置顶并分配到命名分组(唯一成员),另有两个未分组会话 —— 通过包内 vite dev server 提供客户端。before/after 通过在同一份守护进程数据上仅还原/应用本 diff 捕获。类型检查(
packages/web-shell内tsc -p tsconfig.json --noEmit):本 diff 未引入新错误。client/App.tsx中一个既有错误(MODES_CYCLE.indexOf(currentMode),mode 为string)在本 diff 还原后、使用同样的本地构建工作区依赖时可同样复现,与本改动无关。证据(修改前与修改后)
修改前 —— 全置顶分组渲染
· 0,成员只在 Pinned 区(designTitles: []);修改后 —— 分组显示· 1且含置顶成员行,同时仍在 Pinned 区,Ungrouped 不变(截图见英文部分)。随截图捕获的 DOM 断言:{ "phase": "before", "designSectionText": "Design· 0", "designTitles": [], "ungroupedTitles": ["Ungrouped task B", "Ungrouped task A"], "pinnedTitles": ["Pinned group member"] } { "phase": "after", "designSectionText": "Design· 1Pinned group member", "designTitles": ["Pinned group member"], "ungroupedTitles": ["Ungrouped task B", "Ungrouped task A"], "pinnedTitles": ["Pinned group member"] }测试平台
环境(可选)
Node v24.19.0;包内 Vitest;UI 截图由 Playwright 驱动的 headless Chromium 完成,目标为本地
qwen serve(0.22.2,隔离工作区 + 非默认端口 + 独立 bearer token),经 web-shell vite dev server 代理。本机生产守护进程未被触碰。风险与范围
关联 Issue
Fixes #10391