Skip to content

fix(desktop): 审查面板合流与协同审查入口修复,生成文件卡去重 - #2048

Merged
MagicLizi merged 7 commits into
mainfrom
cindy/auto-io05g7
Aug 7, 2026
Merged

fix(desktop): 审查面板合流与协同审查入口修复,生成文件卡去重#2048
MagicLizi merged 7 commits into
mainfrom
cindy/auto-io05g7

Conversation

@DavidShenXD

@DavidShenXD DavidShenXD commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

这次改了什么

摘要

五项修正,均源自实机验收反馈:

  1. 轮次审查与 git 审查合流同一来源状态机(对齐 Codex 的单 source + turnSelection 模型):从聊天流变更卡进入的轮次审查视图,header 由「静态标题 + 当前工作区小按钮」换成与 git 审查同一个来源下拉,选中态显示「本条消息的变更」;在下拉里选任一 git 来源(未暂存/已暂存/提交/分支/上一轮)即一步切走并退出轮次审查——修复「从轮次视图切回 git 审查只能关掉 tab 重开」的死胡同。source/selectedCommitOid 上提到外层 ReviewTabBody,进出轮次视图不再把来源重置回「未暂存」。切走自动清 turnTarget、不提供"切回轮次"常驻项,与 Codex 的 EP 语义一致(要再看本条消息从卡片重进)。
  2. 协同(Orca)worker 审查入口打通:协同面板里点 worker 变更卡的「审查」/文件行原先毫无反应——review tab 被开进 worker 自己的 RSB 桶,而协同视图只显示 lead 的桶,跨会话可见性请求又被 MainLayout 有意忽略。现在 worker 流经 SidebarHostSessionProvider 把 review tab 开到 lead 的可见桶,turnTarget.targetSessionId 指向 worker 会话取数;跨会话轮次审查不提供 git 来源切换(lead 的 git 视图对 worker 的 worktree 语义错误),静态标题 + 只读 diff,关 tab 即回协同(对齐 Codex git-review-mode: last-turn-only 的思路)。main 侧 RSB 命令 sanitizer 校验并透传 hostSessionId,detached 右栏窗口路径同样生效。
  3. 「本条消息生成的文件」卡对转义残留路径去重:powershell 包一层 node -e 的命令落库文本里路径带转义残留(C:\\Users\\...),与同轮正斜杠形态的同一文件产生两个同名 chip。dedupeKeyForPath 对 Windows 形态折叠连续分隔符(UNC 头部 \\ 语义保留),盘符路径的画布路径同步归一——tooltip、Explorer 定位与变更卡 exactPaths 抑制比对都拿到干净形态。用真实落库数据重放派生管线验证:2 条收敛为 1 条。
  4. 工作区外临时文件写入不再催生空的部分变更卡:f101b90c3 按「有无变更证据」隐藏了零文件 opaque-tool 卡,但 agent 写工作区外临时文件(scratchpad、系统 Temp)会记 outside-workspace,该 reason 被归入「有变更证据」,「文件变更未完整记录 +0 −0」死胡同卡因此重现。源头:captureKnownFileBefore 对字面在工作区外的目标静默跳过(刻意的追踪范围排除,不是捕获损失);symlink 逃逸(字面在内、realpath 在外)保持记 reason。读侧自愈:hasReviewableTurnChanges 的零文件判定集合加入 outside-workspace,已落盘的存量条目同样不再渲染。
  5. 零文件变更卡不再显示误导性 +0 −0 与空审查入口:fileCount=0 但带真实变更证据(sensitive-file / diff-too-large / read-failed 等)的部分变更卡,此前照常渲染 +0 −0 与「审查」按钮——数字会被误读成「没有变化」,审查面板必然为空。这类卡的职责只是警示「有文件被更改但内容未记录、无法在此撤销」:现在增删统计与「审查」按钮只在录到文件时显示,副标题换用明确文案 partialNone(四语言)。有文件行的部分变更卡渲染不变。

变更类型

  • fix 缺陷修复

范围

  • 关联 Issue / 需求:实机验收反馈(fix(desktop): 变更捕获上线首日三修——空审查卡、同目录任务串行、零信息卡收敛 #2026 变更捕获系列的后续)
  • 本 PR 包含:review 插件来源状态机合流、SourceDropdown 轮次伪选项、协同 worker 审查入口跨桶路由、RSB 命令 wire 字段 hostSessionId、废弃 turn.currentWorkspace 文案(4 语言)、generatedFiles chip 去重
  • 明确不包含:SplitGroup 非活动 pane 的同类入口问题(未验证是否存在,未动);git 视图 source 的持久化策略(维持不持久化)
  • 用户可见变化:轮次审查 header 变为来源下拉;「当前工作区」文字按钮移除(功能并入下拉);协同面板 worker 变更卡的「审查」从无反应变为打开只读轮次审查;转义残留路径不再产生重复文件 chip;零文件警示卡不再显示 +0 −0 和「审查」按钮,副标题改为明确说明
  • 是否存在 breaking change:无(turnTarget 旧持久化数据 hydrate 兼容,targetSessionId 缺省 null = 原语义)

UI 变化

  • 轮次审查 header:[本条消息的变更 ▾] ……………… +N −N,下拉菜单结构与 git 审查完全一致(轮次项置顶带勾选)
  • 跨会话(协同 worker)轮次审查 header:图标 + 静态标题「本条消息的变更」,无下拉
  • 引用的设计规范:DESIGN.md §Select & Dropdown——面板宽度绑定触发器宽度(复用现有 SourceDropdown,w-[var(--radix-dropdown-menu-trigger-width)])、菜单行 8px 内圆角与 --cmd-palette-item-hover 选中/悬停填充均为既有组件行为,本 PR 未新增样式;双模式经语义 token 继承,无硬编码颜色

怎么验证的

自动验证

pnpm test:unit                             全仓 PASS(合并后复跑,desktop ~150s)
pnpm --filter desktop typecheck            PASS(合并后复跑)
pnpm check:i18n / check:i18n-glossary      PASS(删 key 无孤儿)
pnpm check:dco                             PASS(4 commits)

新增测试:SourceDropdown 轮次伪选项 4 例(helpers)、TurnChangesCard 宿主桶路由 2 例、main sanitizer hostSessionId 透传/拒绝 1 例、generatedFiles 去重回归 2 例、turn-change-set 工作区外写入 3 例(store 集成:外部写入不降级/纯外部整轮不落卡/symlink 逃逸仍标 partial)+ 渲染过滤用例扩入 outside-workspace、零文件警示卡渲染 1 例(无 +0 −0/无审查与撤销入口/partialNone 文案)。

手工验证

Windows dev 实例(--isolated 沙箱,region=cn)实机验收通过:轮次视图下拉切「未暂存」即回 git 视图、卡片可重进轮次视图、协同 worker 卡「审查」正常打开只读审查(验收者:用户本人;计算机使用工具辅助复核了下拉菜单结构与切换行为)。chip 去重用产生该 bug 的 session 真实 DB 数据离线重放验证。

未执行的验证

  • macOS 端未实机验证(改动为跨平台 renderer/main 通用逻辑,无平台分支)
  • Light/Dark 双模式未逐一实机目检(全部复用既有 themed 组件与语义 token,无新增颜色)
  • detached 右栏窗口下的协同审查路径未实机走查(有 sanitizer 单测钉住字段透传)

风险

风险分类

  • 无已知风险

影响与回滚

  • 影响范围:右栏 review 插件、协同面板审查入口、RSB 窗口命令 wire(仅新增可选字段,双向兼容:旧 renderer + 新 main / 新 renderer + 旧 main 均按原语义降级)、generatedFiles 派生管线(纯函数,仅影响 chip 展示归并)、turn-change-set 捕获与聊天流卡片过滤(落盘 schema 不变,读侧过滤对存量 sidecar 自愈)
  • 回滚 / 降级方式:四个 commit 各自独立可 revert,turnTarget 持久化数据向前向后兼容

提交前检查

  • 已 review 完整 diff
  • 每个 commit 都带 DCO 签名(git commit -s)
  • UI 改动已在「UI 变化」注明引用的设计规范章节
  • 未提交凭证、令牌或授权文件
  • 已补充必要文档(代码内注释;无需独立文档)
  • 已确认测试结果或说明未执行原因

注:chip 去重原为独立 PR #2049,按用户要求合并至本 PR(cherry-pick f2d9490),#2049 已关闭。

🤖 Generated with Claude Code

两项审查面板修正:

1. 来源合流(对齐 Codex 的单 source + turnSelection 模型):轮次审查视图
   header 换成与 git 审查同一个来源下拉,选中态为「本条消息的变更」;切到任一
   git 来源即退出轮次审查(清 turnTarget),不再需要关掉 tab 重开。source /
   selectedCommitOid 上提到 ReviewTabBody,进出轮次视图不再丢来源选择。
   废弃「当前工作区」文字按钮及其 4 语言文案。

2. 协同审查入口(orca-workers):worker 流里点变更卡「审查」原先把 review tab
   开进 worker 自己的桶——协同视图下该桶不可见,点击无任何反应。现经
   SidebarHostSessionProvider 把 tab 开到 lead 的可见桶,turnTarget 携带
   targetSessionId 按 worker 会话取数;跨会话轮次审查不提供 git 来源切换
   (git 视图跟随桶会话 workdir,对 worker 的 worktree 语义错误)。main 侧
   RSB 命令 sanitizer 校验并透传 hostSessionId(detached 窗口路径)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI lite review requested due to automatic review settings August 7, 2026 10:38
@DavidShenXD
DavidShenXD requested a review from a team as a code owner August 7, 2026 10:38

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a4ee290551

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@greptile-apps

greptile-apps Bot commented Aug 7, 2026

Copy link
Copy Markdown

Greptile Summary

此 PR 修复右栏审查来源切换与协同 worker 审查路由,同时完善轮次变更卡和生成文件路径处理。

  • 将轮次审查与 Git 审查整合到同一来源状态机。
  • 通过 hostSessionId 将 worker 审查标签页路由到 lead 的可见桶,并保留 worker 作为取数目标。
  • 过滤工作区外临时写入产生的空变更卡,改善零文件警示卡展示。
  • 归一化 Windows 路径分隔符,避免生成文件卡重复。

Confidence Score: 5/5

当前代码已修复先前报告的 detached 跨会话路由问题,未发现仍需阻止合并的故障。

没有阻塞性故障遗留。

Important Files Changed

Filename Overview
apps/desktop/src/main/right-sidebar-window/controller.ts detached 命令的上下文校验、延迟排队与刷新已统一使用宿主会话桶,先前的跨会话 stale-context 问题已修复。
apps/desktop/src/renderer/features/right-sidebar/lib/openTurnReview.ts 审查标签页使用 hostSessionId 决定可见桶,并以原 sessionId 记录跨会话取数目标。
apps/desktop/src/renderer/features/right-sidebar/plugins/review/ReviewTabBody.tsx 轮次审查与 Git 审查共享来源状态,并在跨会话模式下禁用语义错误的 Git 来源切换。
apps/desktop/src/main/turn-change-set/store.ts 字面位于工作区外的已知写入被视为范围排除,而工作区内路径经符号链接逃逸仍保留不完整标记。
apps/desktop/src/renderer/lib/generatedFiles.ts Windows 路径分隔符归一化避免转义残留导致重复文件卡,同时保留 UNC 前缀语义。

Sequence Diagram

sequenceDiagram
  participant WorkerCard as Worker 变更卡
  participant MainRenderer as 主 Renderer
  participant Controller as Detached Controller
  participant Sidebar as Detached 右栏
  participant Review as Review Tab
  WorkerCard->>MainRenderer: "openTurnReview(workerId, hostSessionId=leadId)"
  MainRenderer->>Controller: open-turn-review 命令
  Controller->>Controller: 按 leadId 校验或入队
  Controller->>Sidebar: 派发命令
  Sidebar->>Review: 在 leadId 桶创建或聚焦标签页
  Review->>Review: 按 workerId 获取轮次变更
Loading

Reviews (7): Last reviewed commit: "test(device-link): pong 自动应答提前到建链前装配,修 W..." | Re-trigger Greptile

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

本 PR 修复 Desktop 右侧栏审查插件在「轮次审查」与「Git 审查」间切换的死胡同,并打通协同(Orca)worker 流内嵌场景下的审查入口,使其能在 lead 会话可见的 RSB 桶中正确打开审查面板(跨会话时以只读轮次审查呈现)。

Changes:

  • 将轮次审查与 Git 审查合流为同一来源状态机:轮次态 header 使用同一来源下拉(含轮次伪选项),切换任一 Git 来源会退出轮次审查并直接进入 Git 视图。
  • 新增 SidebarHostSession 上下文与 hostSessionId 路由字段:协同 worker 流触发审查时,将 review tab 打开到 lead 的可见桶,并通过 targetSessionId 指向 worker 会话取数。
  • 清理轮次审查 header 中废弃的“当前工作区”文案及对应四语言 i18n key,并补充覆盖相关路径的单测(来源下拉/跨桶路由/IPC sanitizer)。

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated no comments.

Show a summary per file
File Description
apps/desktop/src/shared/rightSidebarWindow.ts 为 open-turn-review 命令新增可选 hostSessionId 字段,承载跨桶路由能力
apps/desktop/src/renderer/i18n/locales/zh-CN/common.json 移除轮次审查 header 废弃文案 key
apps/desktop/src/renderer/i18n/locales/ko/common.json 同上(韩语)
apps/desktop/src/renderer/i18n/locales/ja/common.json 同上(日语)
apps/desktop/src/renderer/i18n/locales/en/common.json 同上(英语)
apps/desktop/src/renderer/features/right-sidebar/plugins/review/ReviewTabBody.tsx 合流来源状态机、轮次伪选项下拉、跨会话轮次审查禁用 git 来源切换并按 targetSessionId 取数
apps/desktop/src/renderer/features/right-sidebar/plugins/review/index.tsx 为 turnTarget hydrate 增加 targetSessionId(旧数据默认 null 保持兼容)
apps/desktop/src/renderer/features/right-sidebar/plugins/review/tests/ReviewTabBody.helpers.test.ts 新增轮次伪选项下拉行为回归测试
apps/desktop/src/renderer/features/right-sidebar/plugins/orca-workers/index.tsx 在协同面板提供 SidebarHostSessionProvider,标记宿主(lead)桶
apps/desktop/src/renderer/features/right-sidebar/lib/sidebarHostSession.tsx 新增宿主桶 sessionId 的 React Context(供内嵌消息流入口路由使用)
apps/desktop/src/renderer/features/right-sidebar/lib/openTurnReview.ts 支持 hostSessionId 开 tab 到宿主桶,并写入 targetSessionId 供跨会话取数
apps/desktop/src/renderer/features/right-sidebar/lib/executeSidebarCommand.ts 转发 open-turn-review 的 hostSessionId 到 openTurnReview
apps/desktop/src/renderer/components/chat/TurnChangesCard.tsx 读取宿主桶 sessionId 并在打开审查时传入 hostSessionId
apps/desktop/src/renderer/components/chat/tests/TurnChangesCard.test.tsx 覆盖主实例与协同内嵌两种路由行为的单测
apps/desktop/src/main/right-sidebar-window/ipc.ts sanitizer 校验并透传 hostSessionId(可选字段)
apps/desktop/src/main/right-sidebar-window/tests/ipc.test.ts 覆盖 hostSessionId 的透传与非法值拒绝

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

实测场景(pr-watch session):powershell 包一层 node -e 的命令落库文本里,
路径带转义残留(C:\Users\...);同轮另一条命令用正斜杠形态。二者在
fs 层是同一文件(Windows 归并重复分隔符),但去重 key 未折叠连续反斜杠,
产生两个同名 chip。

- dedupeKeyForPath:Windows 形态折叠连续分隔符(UNC 头部 \ 保留);
- canonicalizeWindowsShape:盘符路径的画布路径本身同步折叠——chip tooltip、
  Explorer 定位与打开拿到干净的单反斜杠形态,MessageStream 与变更卡
  exactPaths 的抑制比对也能对上。

用该 session 真实落库数据重放派生管线验证:registry.json 从 2 条收敛为 1 条。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 11:20
@DavidShenXD DavidShenXD changed the title fix(desktop): 轮次审查合流 git 审查来源下拉,协同 worker 审查入口不再无响应 fix(desktop): 审查面板合流与协同审查入口修复,生成文件卡去重 Aug 7, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 18 out of 18 changed files in this pull request and generated no new comments.

Suppressed comments (2)

apps/desktop/src/renderer/features/right-sidebar/plugins/review/ReviewTabBody.tsx:459

  • TurnChangeSetReviewBody 的 localOnly 守卫仍然用 ctx.remoteHostId / ctx.deviceLinkDeviceId(宿主桶 session)判断;当 turnTarget.targetSessionId 指向另一个会话时,这个判断会基于“错误的会话”做决策:例如 lead 为 remote 而 worker 为 local 时会被误判为不支持;反之亦然。建议至少在 crossSession 时不要复用 ctx 的判断,避免错误拦截/提示。
    if (ctx.remoteHostId !== null || ctx.deviceLinkDeviceId !== null) {
      setChangeSets([]);
      setError(t('rightSidebar.review.turn.localOnly'));
      setLoading(false);
      return;

apps/desktop/src/renderer/features/right-sidebar/plugins/review/index.tsx:136

  • hydrateState 对 turnTarget.targetSessionId 只做了 typeof === 'string' 检查,空字符串也会被接受:此时 TurnChangeSetReviewBody 会把 reviewSessionId 变成 "" 并发起 getTurnChangeSets("", ...),导致必然失败且难以定位。建议按 sessionId 的常规约束校验非空并限制长度。
          targetSessionId: typeof (rawTurnTarget as { targetSessionId?: unknown }).targetSessionId === 'string'
            ? (rawTurnTarget as { targetSessionId: string }).targetSessionId
            : null,

上一轮修复(f101b90)按「有无变更证据」隐藏了零文件 opaque-tool 卡,但
agent 写工作区外的临时文件(PR 描述、scratchpad、系统 Temp)会记一条
outside-workspace,该 reason 被归入「有变更证据」,零文件卡因此重现:
「文件变更未完整记录 +0 -0」,点审查仍是空面板。

两处修正:

- 源头(captureKnownFileBefore):目标路径字面在工作区外是刻意的追踪范围
  排除,不是捕获损失——静默跳过,不再记 outside-workspace,也不再把
  纯工作区内的捕获降级为 partial。字面在内、realpath 逃逸(symlink)
  的检查保持记 reason:那种情况工作区树表面被碰过,值得标注。
- 读侧自愈(hasReviewableTurnChanges):零文件判定集合加入
  outside-workspace,已落盘的存量条目(以及 codex 外来 diff 块 fail-closed
  产生的零文件条目)同样不再渲染死胡同卡。非零文件条目不受影响。

新增测试:工作区外已知写入不降级捕获/纯外部写入整轮不落卡/symlink 逃逸
仍标 partial(store 集成 3 例);渲染过滤用例扩入 outside-workspace。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 11:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (2)

apps/desktop/src/renderer/lib/generatedFiles.ts:77

  • canonicalizeWindowsShape 目前只处理盘符路径,UNC 路径(\\server\share...)即使包含转义残留的连续分隔符也会原样透传。由于 collectGeneratedFiles 会把首次出现的 abs 存为 chip 的展示/打开路径,这会让 UNC 场景下仍可能保留脏路径(例如 \\\\server\\share...)并影响 Explorer 定位/tooltip 展示。建议对 UNC 绝对路径也做“保留前缀 \\,其余连续分隔符折叠”的归一化。
function canonicalizeWindowsShape(abs: string): string {
  // 连续分隔符折叠进画布路径本身(不只 dedupe key):stat 虽能容忍 `C:\\x`,但
  // Explorer `/select` 与 chip 展示不该带转义残留。盘符形态不存在 UNC 头,可整段折叠。
  return /^[a-zA-Z]:[\\/]/.test(abs)
    ? abs.replace(/\//g, '\\').replace(/\\{2,}/g, '\\')
    : abs;

apps/desktop/src/renderer/lib/generatedFiles.ts:63

  • dedupeKeyForPath 里用 /(?<!^)\\{2,}/ 只会折叠“非行首”的连续反斜杠;如果命令文本里 UNC 路径出现双重转义(例如 \\\\server\\share...,开头是 4 个 \),当前逻辑会把这 4 个 \ 原样保留,导致与正常的 \\server\\share... 生成不同的 dedupe key,从而仍可能出现重复 chip。

This issue also appears on line 72 of the same file.

  return abs
    .replace(/\//g, '\\')
    .replace(/(?<!^)\\{2,}/g, '\\')
    .toLowerCase();

fileCount=0 但带变更证据(sensitive-file / diff-too-large / read-failed 等)
的部分变更卡,此前照常渲染 +0 -0 与「审查」按钮:数字会被误读成「没有
变化」,审查面板必然为空。这类卡的职责只是警示「有文件被更改但内容未
记录、无法在此撤销」,现在:

- 增删统计只在录到文件时显示;
- 「审查」按钮只在有可展示 diff 时显示(撤销按钮本就受 isReversible 约束,
  零文件时不出现);
- 副标题换用明确文案 partialNone(四语言),说明内容未记录、无法在此
  审查或撤销。

有文件行的部分变更卡(partial + files>0)渲染不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 12:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/desktop/src/renderer/features/right-sidebar/lib/openTurnReview.ts:40

  • openTurnReview 里给 command 增加了 hostSessionId,并在 attached 路径用它来 ensureHydrated/addTab/patchState,这能修复“开进不可见桶”的问题;但 detached 路径仍然会失败:main 侧 RightSidebarWindowController.canDispatchCommand 只用 command.sessionId 与 lastContext.sessionId 比较(apps/desktop/src/main/right-sidebar-window/controller.ts:208-214),完全不看 hostSessionId。协同面板里 worker 的入口发出的 command.sessionId=worker、hostSessionId=lead 时,在 detached 模式会被判定为 stale-context,routeSidebarCommand 返回后这里直接 return,表现仍是“点击无反应”。建议 main 路由裁决与 deferredCommands 的 key 统一改用 (command.hostSessionId ?? command.sessionId) 来决定可见桶,并保持 command.sessionId 继续表示取数的目标会话。
  const requestNonce = opts.requestNonce ?? ++nextRequestNonce;
  const hostSessionId = opts.hostSessionId ?? sessionId;
  const command = {
    type: 'open-turn-review' as const,
    sessionId,
    changeSetIds,
    selectedDiffId: opts.selectedDiffId ?? null,
    selectedPath: opts.selectedPath ?? null,
    requestNonce,
    hostSessionId,
  };
  const routeResult = await routeSidebarCommand(command);
  if (routeResult !== 'attached') {
    if (routeResult === 'routed') requestRightSidebarVisibility('open', { sessionId: hostSessionId });
    return;

跨会话 open-turn-review(协同面板审查 worker 轮次)携带 worker sessionId
(取数目标)与 lead hostSessionId(可见桶),但 RsbWindowController 的
canDispatchCommand 仍按 sessionId 与当前 context 比对——detached 形态下
context 停在 lead,worker 审查命令被误判 stale-context 拒发,协同审查
入口在 detached 右栏下点了没反应;延迟命令同样按 worker 会话入队,
context 不会切到 worker,永远刷不出来。

新增 commandHostSessionId:open-turn-review 取 hostSessionId ?? sessionId,
其余命令保持自身 sessionId。裁决(canDispatchCommand)与 deferred 排队
(enqueueDeferredCommand)统一以宿主桶为键;hostSessionId 与当前 context
不符仍拒发,可见性边界不放宽。

新增 controller 测试 2 例:跨会话命令按 lead 桶 routed(错桶仍
stale-context)、allowOpen=false 时按 lead 桶入队并在 lead ready 后派发。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 12:30
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

Review 反馈处理总结(第 2 轮)

本轮处理 2 条未解决 conversation,两条指向同一个真实缺陷,合并为一次最小修复(commit f40a888):

Thread 结论 处理
chatgpt-codex-connector(openTurnReview.ts:35,detached 下 worker 审查命令被过早拒绝) 属实 RsbWindowController.canDispatchCommand 改按宿主桶裁决
greptile-apps(openTurnReview.ts:37,裁决与延迟队列都用错桶) 属实 同上,且 enqueueDeferredCommand 同步改按宿主桶入队

修复方式

新增 commandHostSessionId(cmd):open-turn-reviewhostSessionId ?? sessionId(跨会话审查 worker 轮次时 sessionId 是取数目标,可见桶在 lead),其余命令保持自身 sessionId。裁决与 deferred 排队统一以宿主桶为键:

  • detached context 停在 lead 时,携带 worker sessionId + lead hostSessionId 的命令按 lead 桶放行,不再误判 stale-context;
  • allowOpen=false 的延迟命令按 lead 桶入队,lead context ready 后正常 flush(原按 worker 会话入队永远刷不出来);
  • 边界不放宽:hostSessionId 与当前 context 不符仍拒发。

验证

  • 新增 controller 单测 2 例:跨会话命令按 lead 桶 routed(错桶仍 stale-context);延迟命令按 lead 桶入队并在 lead ready 后派发
  • RSB controller + ipc 测试 47/47 通过;全仓 pnpm test:unit exit 0;pnpm --filter desktop typecheck 通过;pnpm check:dco 5 commits 全签

两个 thread 均已回复并 resolve。未 merge,等待后续 review。

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f40a888d67

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread apps/desktop/src/shared/turnChangeSet.ts Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

Suppressed comments (2)

apps/desktop/src/renderer/lib/generatedFiles.ts:77

  • canonicalizeWindowsShape 目前只在盘符路径上折叠连续分隔符;对 UNC 路径(以 \ 开头)不会清理中间的重复 \。因为 collectGeneratedFiles 会把首次命中的 abs 直接作为 chip 展示/tooltip 路径保存,这会导致 UNC 形态仍可能带转义残留(例如 "\server\share\\dir\file" 里的 "\\"),与上方注释/测试用例描述的“折叠 inner separator runs”不一致。建议在保留 UNC 头部 \ 的前提下,同样折叠其余部分的重复分隔符。
function canonicalizeWindowsShape(abs: string): string {
  // 连续分隔符折叠进画布路径本身(不只 dedupe key):stat 虽能容忍 `C:\\x`,但
  // Explorer `/select` 与 chip 展示不该带转义残留。盘符形态不存在 UNC 头,可整段折叠。
  return /^[a-zA-Z]:[\\/]/.test(abs)
    ? abs.replace(/\//g, '\\').replace(/\\{2,}/g, '\\')
    : abs;

apps/desktop/src/main/right-sidebar-window/ipc.ts:135

  • 这里的校验允许 hostSessionId 为 undefined(字段缺省)或 null,但报错信息写成了 “must be string | null”,会误导调用方以为 undefined 也非法。建议把文案改成“when provided, must be a non-empty string or null”以匹配实际约束。
      throwIpcError('INVALID_PARAMS', 'command.hostSessionId must be string | null');

review 反馈(#2048 r3735770063)属实:b416d6599 把 outside-workspace 归入
「无变更证据」集合后,当零文件条目仅有该 reason 时整卡被隐藏。但源头修复
之后,store 只会在可疑场景记录 outside-workspace——工作区内路径 realpath
经 symlink 逃逸、provider diff 块 fail-closed 拒收——这些恰恰是需要用户
看见的证据;字面在工作区外的目标在捕获时已静默跳过,不会再记 reason。

读侧分类回退:outside-workspace 移出 NO_CHANGE_EVIDENCE_REASONS,零文件
outside-workspace 条目重新渲染(经 b716ed8 已是纯警示条,无 +0 -0、无
死胡同按钮)。源头静默跳过保持不变,原始 bug(纯外部临时文件写入催生空卡)
不复发。渲染过滤测试拆分用例:cs-escape 仅含 outside-workspace,断言保留。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 12:59
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

Review 反馈处理总结(第 3 轮)

Conversation 位置 评估 处置
chatgpt-codex-connector(P1):outside-workspace 归入无证据集合后,symlink 越界问题不再可见 shared/turnChangeSet.ts:92 属实 已修复(27e7eba),已回复并 resolve

缺陷与修复

b416d65outside-workspace 加进 NO_CHANGE_EVIDENCE_REASONS,导致零文件条目仅含该 reason 时整卡被隐藏。但源头修复之后,store 只会在可疑场景记录 outside-workspace:

  • 工作区内路径 realpath 经 symlink 逃逸(captureKnownFileBefore 的 realpath 检查);
  • provider diff 块因不安全路径 / 格式非法被 fail-closed 拒收(filterSensitiveDiffBlocks)。

这些恰是用户需要看见的证据;字面在工作区外的临时文件目标在捕获时已静默跳过、不会记 reason。因此采纳 reviewer 建议的第一个方案:读侧分类回退,把 outside-workspace 移出无证据集合,零文件 outside-workspace 卡恢复渲染。源头静默跳过不动,原始 bug(纯外部临时文件写入催生空卡)不复发;且该卡经 b716ed8 已是纯警示条(无 +0 -0、无死胡同审查按钮)。

验证

  • 渲染过滤测试拆分用例:cs-escape(仅 outside-workspace)断言保留,cs-noise(opaque-tool / turn-failed / concurrent-workspace)断言隐藏
  • symlink 逃逸、fail-closed diff 块的 store 集成测试(4 例)未改动、仍通过;涉及的 3 个测试文件 113/113
  • 全仓 pnpm test:unit 通过;desktop typecheck 通过;check:dco 6 commits 全签

未 merge,等待后续 review。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.

Suppressed comments (2)

apps/desktop/src/renderer/lib/generatedFiles.ts:77

  • canonicalizeWindowsShape 目前只对盘符路径折叠连续分隔符,但命令文本提取出来的 UNC 路径同样可能带重复反斜杠(例如二次转义导致的 \\\\server... 或内部 share\\dir)。这里不归一会让 chip 展示/Explorer 定位继续携带转义残留。建议对 UNC 也做“保留 2 个前导反斜杠 + 折叠其余分隔符”的归一化。
function canonicalizeWindowsShape(abs: string): string {
  // 连续分隔符折叠进画布路径本身(不只 dedupe key):stat 虽能容忍 `C:\\x`,但
  // Explorer `/select` 与 chip 展示不该带转义残留。盘符形态不存在 UNC 头,可整段折叠。
  return /^[a-zA-Z]:[\\/]/.test(abs)
    ? abs.replace(/\//g, '\\').replace(/\\{2,}/g, '\\')
    : abs;

apps/desktop/src/renderer/lib/generatedFiles.ts:63

  • dedupeKeyForPath 目前通过 /(?<!^)\\{2,}/g 来“保留 UNC 头部的 \\”。但如果命令文本里 UNC 路径出现二次转义(例如以 3~4 个反斜杠开头的 \\\\server...),该实现会把整段开头连续反斜杠都保留,导致同一 UNC 路径不同转义形态仍可能产生不同 dedupe key(重复 chip)。建议显式把 UNC/extended-length 前缀归一为恰好两个开头反斜杠,并折叠其余分隔符,同时避免 lookbehind。

This issue also appears on line 72 of the same file.

  return abs
    .replace(/\//g, '\\')
    .replace(/(?<!^)\\{2,}/g, '\\')
    .toLowerCase();

CI 失败(run 31180575750,Windows 分片 1/2)定位:「慢可靠业务 handler 不
阻塞 pong」用例把确定性 pong 应答器装在 ack() + establishInboundReliableLink
之后,但心跳 interval 在 hello-ack 时就已启动(pingIntervalMs=8,
pongMissLimit=1)。慢 runner 上建链期间的几次 await tick() 真实耗时可超两个
心跳周期,期间发出的 ping 无人应答,pongMiss 越限触发误断网,断言
terminated=false 失败。本地快机器建链耗时 <8ms 抓不到,与本 PR 改动无关,
是存量时序 flake。

修法:把 send 拦截(ping→同步回 pong)提前到 ack() 之前,建链全程心跳都有
确定性应答,彻底消除对 runner 速度的依赖。断言语义不变:慢业务 handler 若
真堵住帧处理,push 进来的 pong 不会被消费,pongMiss 照样断网,回归仍能抓住。

验证:单测全文件 103/103;目标用例单跑 3 次稳定通过;tsc --noEmit 通过;
全仓 test:unit 56 package PASS。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: DavidShen <david@xd.com>
Copilot AI review requested due to automatic review settings August 7, 2026 13:40
@DavidShenXD

Copy link
Copy Markdown
Collaborator Author

Windows CI 失败处理(run 31180575750)

失败项:Windows unit tests (1/2) → 聚合门 Windows unit tests。唯一失败用例:
packages/device-link DeviceLinkClient > 慢可靠业务 handler 不阻塞 pong,避免把本地处理拥塞误判成断网(expected true to be false,即 fake socket 被 terminate、误判断网)。

定位:存量时序 flake,与本 PR 改动无关(PR 未触碰 device-link;该用例此前已因同类问题修过一轮,见 #1616 的确定性 pong 改造)。根因:确定性 pong 应答器装配在 ack()(hello-ack 即启动心跳,pingIntervalMs=8 / pongMissLimit=1)与建链流程之后。慢 runner 上建链期间的几次 await tick() 真实耗时可超两个心跳周期,期间发出的 ping 无人应答,pongMiss 越限触发误断网。

修复(a0fee6b):把 send 拦截(ping→同步回 pong)提前到 ack() 之前,建链全程心跳都有确定性应答,消除对 runner 速度的依赖。断言语义不变:慢业务 handler 若真堵住帧处理,push 进来的 pong 不会被消费,pongMiss 照样断网,回归仍能抓住。

验证:

  • client.test.ts 全文件 103/103;目标用例单跑 3 次(--pool=threads --maxWorkers=1)稳定通过
  • @cindy/device-link tsc --noEmit 通过
  • 全仓 pnpm test:unit 56 package PASS、无 FAIL;check:dco 7 commits 全签

已 push,未 merge。

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated no new comments.

Suppressed comments (1)

apps/desktop/src/renderer/lib/generatedFiles.ts:63

  • dedupeKeyForPath() 目前用 /(?<!^)\\{2,}/g 折叠连续反斜杠以保留 UNC 前缀,但这会把“开头超过 2 个反斜杠”的情况(例如 node -e / powershell 包装串里常见的 \\\\server\\share...)整段保留,导致与正常 \\server\\share... 生成不同的去重 key,仍可能重复出 chip。建议显式把“前导反斜杠 run”折叠为恰好 \\,再对剩余部分折叠连续分隔符。
  // UNC 头部的 `\\` 是路径语义的一部分,保留。
  return abs
    .replace(/\//g, '\\')
    .replace(/(?<!^)\\{2,}/g, '\\')
    .toLowerCase();

@MagicLizi MagicLizi added status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示) and removed status:awaiting-bot-review 等外部审查机器人表态(review-pr 自动维护,仅展示) labels Aug 7, 2026

@MagicLizi MagicLizi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review complete: 0 P0/P1 issues. Two minor P2 advisories (UNC path collapsing asymmetry, one untested hydrateState branch) — neither blocking. All 5 security soft hits confirmed as test fixture writes (self-cleaning tmpdir). Clean malicious code check.

@MagicLizi
MagicLizi merged commit 0aad640 into main Aug 7, 2026
16 checks passed
@MagicLizi
MagicLizi deleted the cindy/auto-io05g7 branch August 7, 2026 14:51
@MagicLizi

Copy link
Copy Markdown
Contributor

顺利合并了——审查面板的合流逻辑梳理得清晰,turn-capture 对 workspace 外路径和 symlink 逃逸的区分处理也很扎实,测试覆盖到了边界情况。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

touches:core 改动碰到架构核心路径(review-pr 自动维护,仅展示) touches:product-ui 改动碰到产品 / UI 面(review-pr 自动维护,仅展示)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants