Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 18 additions & 8 deletions docs/plans/plan-8-session-fork.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
summary: "Session Fork:从已有话题分叉出新会话,继承对话历史、工作区状态与隐性共识"
status: draft
owner: lishuceo
last_updated: "2026-05-24"
last_updated: "2026-05-27"
read_when:
- 实现或修改 session fork 流程
- 修改 ThreadSession schema 或话题创建逻辑
Expand Down Expand Up @@ -99,14 +99,23 @@ fork 后: /root/dev/.workspaces/abc-foo-fork-<id> (branch: feat/foo-fork-
```
git worktree 是硬链接 + 独立 checkout,磁盘开销极小、创建毫秒级。

2. **继承未提交修改**(staged / unstaged / untracked 全要):
2. **继承未提交修改**(**默认行为**,staged / unstaged / untracked 全要):
```bash
cd <原工作区>
STASH_REF=$(git stash create) # 不污染 stash 栈
cd <新工作区>
git stash apply $STASH_REF # 失败则 abort 整个 fork
STASH_REF=$(git stash create -u) # 含 untracked,不入 stash 栈、不动工作树
if [ -n "$STASH_REF" ]; then
cd <新工作区>
git stash apply $STASH_REF # 失败则 abort 整个 fork
fi
```
备选:`rsync` 差异文件(git stash 漏 untracked 时兜底)。
**关键性质**:
- `git stash create` 仅生成 stash commit object,不 push 到栈、**完全不修改父工作树** — 父话题继续工作零干扰
- 父无任何 WIP 时 `STASH_REF` 为空字符串,跳过 apply,新 worktree 就是干净 HEAD
- 子 worktree apply 后所有改动都是 unstaged(staged/unstaged 区分会丢失);如果业务上一定要保留 index 状态,需要额外 `git diff --cached` → `git apply --cached`,P0b 暂不做
- `-u` 含 untracked 文件(node_modules 等大产物的取舍见下文「未跟踪产物处理」)
- 备选:`rsync` 差异文件(git stash 在 submodule/特殊文件场景的兜底)

**可选 `--clean` 标志**:`/fork --clean` 时跳过本步骤,新 worktree 从纯 HEAD 起步。适用于「我想用同样的对话历史但从干净代码起点重新试」。默认不带 `--clean`,因为绝大多数 fork 场景都是「在当前进行中的工作上分两条路试」,带 WIP 才符合直觉。

3. **复制 SDK session JSONL**(⚠️ 待调研):
- 找到原 `conversationId` 对应的 JSONL 文件,复制到新位置生成新 `conversationId`
Expand Down Expand Up @@ -236,7 +245,7 @@ Fork 涉及多个外部状态变更(worktree、stash、JSONL 复制、DB 记
| 步骤 | 失败时回滚动作 |
|------|----------------|
| 1. 新 worktree | 无需回滚 |
| 2. Stash apply(含 `-u` 含 untracked) | `git worktree remove --force <新路径>` + `git branch -D <新分支>` |
| 2. Stash apply(含 `-u` 含 untracked,可被 `--clean` 跳过) | `git worktree remove --force <新路径>` + `git branch -D <新分支>`。⚠️ 父工作树未被动过,无需回滚 |
| 3. JSONL 复制(写临时文件后 atomic rename) | 上述全部 + `rm <新 JSONL>` |
| 4. ThreadSession 写入 | 上述全部 + `DELETE FROM thread_sessions WHERE thread_id=...` |
| 5. 飞书话题创建 | **保留现场不清理**,在 DB 标记 `status='fork_pending'`,提供 `/fork-retry` 手动恢复(避免清理后用户无法重做) |
Expand All @@ -253,7 +262,7 @@ Fork 涉及多个外部状态变更(worktree、stash、JSONL 复制、DB 记

## UI/UX

- **触发命令**:`/fork [可选描述]`
- **触发命令**:`/fork [可选描述]` 或 `/fork --clean [可选描述]`(场景 A 下跳过 WIP 复制,从干净 HEAD 起步)
- **进度提示**:fork 涉及 worktree + stash + session 复制,可能需要 1-3 秒,期间在原话题显示「正在创建分叉...」
- **完成回复**:在原话题贴出新话题链接 + 简短摘要
- **新话题首条消息**:见上文「血缘关系」UI 层格式
Expand Down Expand Up @@ -314,6 +323,7 @@ Fork 涉及多个外部状态变更(worktree、stash、JSONL 复制、DB 记
| 血缘关系存储 | anycode 数据库(`parent_topic_id`) | 飞书侧仅展示,逻辑自管 |
| 失败回滚顺序 | 飞书话题创建放最后 | 避免孤儿话题 |
| 未跟踪大产物 | 默认提示用户跳过 | 避免 stash 卡顿 |
| 场景 A 默认 WIP 继承策略 | 默认继承(`stash create -u` + apply),`--clean` 显式跳过 | fork 的本意是「在当前状态分两条路」,带 WIP 才符合直觉;父工作树完全不被动 |

## 相关文档

Expand Down
Loading