bug: 绑定 worktree 的会话「移动到项目」后进入半移动状态(分组换了,worktree 归属/回收义务仍在旧仓库)
- Dominant language
- TypeScript
- Stars
- 2.7k
- Forks
- 395
- Avg merge
- 21h 48m
- Merged PRs (30d)
- 776
Description
**提交人**: zyjisdog
---
## 现象
绑定 worktree 的任务(工作目录是 Cindy 托管的 `/.cindy-worktrees/`,侧栏带 worktree 徽标)通过会话菜单「移动到项目」选另一个项目后,会进入**半移动状态**:列表与分组立刻按新项目走,但 worktree 一侧的东西全部留在原仓库。
- 底部工作目录 chip 仍显示旧 worktree 路径(Pi / Codex 这类移动后不关 runtime 的会话尤其明显);
- worktree 徽标、worktree store 里的会话归属仍是旧 worktree;
- 之后归档 / 删除该会话,仍会按旧绑定去**回收那个 worktree**;
- 同一个会话上出现两套互相矛盾的工作目录来源:分组与 Git 上下文按新项目解析,路径 chip 与 worktree 归属按旧 worktree。
移动只写了 `working_dir`,没有任何提示说这次移动只成功了一半。
## 复现步骤
1. 在项目 A 的某个 repo 上新建一个 worktree 任务,确认底部 chip 是 `/.cindy-worktrees/`;
2. 等本轮结束(非 running),在侧栏该会话菜单里选「移动到项目」→ 项目 B,界面 toast「已移动到项目」;
3. 不关闭任务,直接再发一条,观察底部路径 chip / worktree 徽标 / Git 上下文;
4. 再把该会话归档(worktree 无未提交改动时不会弹确认),观察它回收的是哪个 worktree。
## 根因
`local-db:sessions:update` 只把 `working_dir` / `workspace_kind` 写库(`apps/desktop/src/main/localDb/ipc/sessions.ts`),**完全不动 worktree 绑定与资源**;而 UI 侧路径 chip、worktree 徽标、Git 上下文各自从 live runtime / worktree store / DB 读取,于是「DB 换了组,运行时与 worktree 归属没换」。
同一条路径上还有两个「旧 workingDir 快照」入口会放大它:lazy-create 沿用 caller 传入的 `workingDir` 快照、live runtime 的 cwd 与 DB 漂移时不重建(`apps/desktop/src/main/maker-ipc/makerSendTransaction.ts`)。它们让旧目录在移动后继续生效。
## 期望行为(想先听维护者意见)
**(a) 拒绝**:worktree 任务的 workingDir 是它所属 repo 的派生目录,跨项目移动会造出「这个 worktree 归谁」无法回答的状态;真正迁出应由 handoff 承担(#2190 / #2585)。最小改法是提示「worktree 任务不能跨项目移动」。
**(b) 迁移**:连 worktree 绑定 / 资源 / 回收义务一起搬到目标项目。但目前没有「一个 worktree 归属另一个 repo 项目」的表达方式,成本与风险都高。
我个人倾向 (a),把 (b) 留给 #2190 的 handoff。
## 与 #4389(及 #4388)的关系 ⚠️
- #4388 把 `local-db:sessions:update` 的业务体抽成 `updateSessionInDb`,供非 IPC 调用复用;
- #4389 叠在 #4388 上,新增 agent 侧 `move_sessions` 工具,在工具层复现了 GUI 的**操作性**守卫(远程 / 已删 / Bot / Orca worker / 运行中 / IM 接管 / 已归档 / review / 空草稿),但**没有 worktree 绑定这一条**;
- 所以 #4389 合入后,agent 用 `move_sessions` 把 worktree 任务移到别的项目,会**原样复现本缺陷** —— 新入口、同一个写路径;
- 建议这条守卫落在**共享写路径**(`updateSessionInDb`),而不是 GUI 层或工具层:一处守卫同时封住 GUI 与工具两个入口,#4389 的守卫清单不必再抄一份(它的 `mapIpcError` 会把 `PRECONDITION_FAILED` 原样带回,错误码语义已经对齐)。若 #4389 先合,把守卫从 handler 体挪进 `updateSessionInDb` 即可(机械 rebase)。
## 与 #2941 / #3237 的关系
#2941(已关,由 #3237 / #3026 修复)解决的是同一类缺陷在 **dialogue → 项目** 路径上的表现:移动后必须重建 runtime、关掉旧 handle。我按 `worktree` 关键字查过 #3237 的改动,它没有触及 worktree 绑定 —— worktree owner 这条路径至今没有守卫。
## 现有草案
本机已有一版未推送的修复草案(守卫落在共享写路径 + renderer 预检 toast + lazy-create / live runtime 以 DB `working_dir` 为准,含单测)。方向确认后我可以整理成 PR;若维护者倾向 (b),我按结论调整。
## 其它(不在本 issue 展开,供参考)
- 反向移动(普通任务被移进别人的 worktree 目录)同样没有守卫;
- 目标目录在不同盘符 / 大小写拼写不同时的等价判定(Windows)需要明确口径。
## 环境
- Cindy 版本或 commit:main `05a0d3188`(源码构建)
- 平台与版本:Windows 11 · Desktop
- 安装方式:源码运行 / 本地打包
Contributor guide
Research direction
Start with apps/desktop/src/main/localDb/ipc/sessions.ts and the shared updateSessionInDb path, then inspect apps/desktop/src/main/maker-ipc/makerSendTransaction.ts and the related move-session entry points. First confirm whether maintainers choose rejection or migration; done means the chosen behavior is consistent across GUI and agent moves, avoids stale worktree state, and is covered by the mentioned tests.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- desktop
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100