fix(desktop): 审查面板合流与协同审查入口修复,生成文件卡去重 - #2048
Conversation
两项审查面板修正: 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>
There was a problem hiding this comment.
💡 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".
|
| 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 获取轮次变更
Reviews (7): Last reviewed commit: "test(device-link): pong 自动应答提前到建链前装配,修 W..." | Re-trigger Greptile
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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>
There was a problem hiding this comment.
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>
Review 反馈处理总结(第 2 轮)本轮处理 2 条未解决 conversation,两条指向同一个真实缺陷,合并为一次最小修复(commit f40a888):
修复方式新增
验证
两个 thread 均已回复并 resolve。未 merge,等待后续 review。 |
There was a problem hiding this comment.
💡 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".
There was a problem hiding this comment.
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>
Review 反馈处理总结(第 3 轮)
缺陷与修复b416d65 把
这些恰是用户需要看见的证据;字面在工作区外的临时文件目标在捕获时已静默跳过、不会记 reason。因此采纳 reviewer 建议的第一个方案:读侧分类回退,把 验证
未 merge,等待后续 review。 |
There was a problem hiding this comment.
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>
Windows CI 失败处理(run 31180575750)失败项:Windows unit tests (1/2) → 聚合门 Windows unit tests。唯一失败用例: 定位:存量时序 flake,与本 PR 改动无关(PR 未触碰 device-link;该用例此前已因同类问题修过一轮,见 #1616 的确定性 pong 改造)。根因:确定性 pong 应答器装配在 修复(a0fee6b):把 send 拦截(ping→同步回 pong)提前到 验证:
已 push,未 merge。 |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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.
|
顺利合并了——审查面板的合流逻辑梳理得清晰,turn-capture 对 workspace 外路径和 symlink 逃逸的区分处理也很扎实,测试覆盖到了边界情况。 |
这次改了什么
摘要
五项修正,均源自实机验收反馈:
source/selectedCommitOid上提到外层ReviewTabBody,进出轮次视图不再把来源重置回「未暂存」。切走自动清 turnTarget、不提供"切回轮次"常驻项,与 Codex 的 EP 语义一致(要再看本条消息从卡片重进)。SidebarHostSessionProvider把 review tab 开到 lead 的可见桶,turnTarget.targetSessionId指向 worker 会话取数;跨会话轮次审查不提供 git 来源切换(lead 的 git 视图对 worker 的 worktree 语义错误),静态标题 + 只读 diff,关 tab 即回协同(对齐 Codexgit-review-mode: last-turn-only的思路)。main 侧 RSB 命令 sanitizer 校验并透传hostSessionId,detached 右栏窗口路径同样生效。node -e的命令落库文本里路径带转义残留(C:\\Users\\...),与同轮正斜杠形态的同一文件产生两个同名 chip。dedupeKeyForPath对 Windows 形态折叠连续分隔符(UNC 头部\\语义保留),盘符路径的画布路径同步归一——tooltip、Explorer 定位与变更卡 exactPaths 抑制比对都拿到干净形态。用真实落库数据重放派生管线验证:2 条收敛为 1 条。outside-workspace,该 reason 被归入「有变更证据」,「文件变更未完整记录 +0 −0」死胡同卡因此重现。源头:captureKnownFileBefore对字面在工作区外的目标静默跳过(刻意的追踪范围排除,不是捕获损失);symlink 逃逸(字面在内、realpath 在外)保持记 reason。读侧自愈:hasReviewableTurnChanges的零文件判定集合加入outside-workspace,已落盘的存量条目同样不再渲染。fileCount=0但带真实变更证据(sensitive-file/diff-too-large/read-failed等)的部分变更卡,此前照常渲染+0 −0与「审查」按钮——数字会被误读成「没有变化」,审查面板必然为空。这类卡的职责只是警示「有文件被更改但内容未记录、无法在此撤销」:现在增删统计与「审查」按钮只在录到文件时显示,副标题换用明确文案partialNone(四语言)。有文件行的部分变更卡渲染不变。变更类型
fix缺陷修复范围
hostSessionId、废弃turn.currentWorkspace文案(4 语言)、generatedFiles chip 去重UI 变化
[本条消息的变更 ▾] ……………… +N −N,下拉菜单结构与 git 审查完全一致(轮次项置顶带勾选)SourceDropdown,w-[var(--radix-dropdown-menu-trigger-width)])、菜单行 8px 内圆角与--cmd-palette-item-hover选中/悬停填充均为既有组件行为,本 PR 未新增样式;双模式经语义 token 继承,无硬编码颜色怎么验证的
自动验证
新增测试: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 数据离线重放验证。
未执行的验证
风险
风险分类
影响与回滚
提交前检查
git commit -s)🤖 Generated with Claude Code