diff --git a/docs/plans/plan-8-session-fork.md b/docs/plans/plan-8-session-fork.md index 517670d..46ccae2 100644 --- a/docs/plans/plan-8-session-fork.md +++ b/docs/plans/plan-8-session-fork.md @@ -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 或话题创建逻辑 @@ -99,14 +99,23 @@ fork 后: /root/dev/.workspaces/abc-foo-fork- (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` @@ -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` 手动恢复(避免清理后用户无法重做) | @@ -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 层格式 @@ -314,6 +323,7 @@ Fork 涉及多个外部状态变更(worktree、stash、JSONL 复制、DB 记 | 血缘关系存储 | anycode 数据库(`parent_topic_id`) | 飞书侧仅展示,逻辑自管 | | 失败回滚顺序 | 飞书话题创建放最后 | 避免孤儿话题 | | 未跟踪大产物 | 默认提示用户跳过 | 避免 stash 卡顿 | +| 场景 A 默认 WIP 继承策略 | 默认继承(`stash create -u` + apply),`--clean` 显式跳过 | fork 的本意是「在当前状态分两条路」,带 WIP 才符合直觉;父工作树完全不被动 | ## 相关文档