工作空间跨盘迁移
这个技能解决什么
工作空间不是「一个文件夹」那么简单。只复制文件夹,一定会丢东西:
历史对话看不见、记忆空白、项目级技能消失、自动化任务报路径不存在、侧边栏多出一个幽灵条目。
真正要搬的是下面五处,缺一处就不算无缝:
| # | 位置 | 丢了会怎样 |
|---|---|---|
| 1 | 工作空间目录本身(含 .workbuddy/:memory、skills、todos) |
记忆、项目技能全丢 |
| 2 | ~/.workbuddy/projects/<slug>/(会话历史 jsonl) |
历史对话在侧边栏消失 |
| 3 | 会话 jsonl 内的绝对路径 | 打开会话提示目录不存在、@文件路径失效 |
| 4 | ~/.workbuddy/workbuddy.db 的 workspaces / sessions / automations / workspace_trust_decisions |
自动化失效、会话拉不起来、要重新授权信任 |
| 5 | ~/.workbuddy/workspace-display-names.json |
侧边栏名字变了或出现幽灵工作空间 |
其中 <slug> 由工作空间绝对路径派生,这是最容易踩的坑:
盘符小写 + 各级目录用 - 连接,空格/中文/下划线/大小写原样保留。
D:\WorkBuddyWork\2026-10-21-09-32-06 -> d-WorkBuddyWork-2026-10-21-09-32-06
G:\2026\职称申报-20261024 -> g-2026-职称申报-20261024
细节与实测样例见 references/storage-map.md,不要凭记忆猜。
开工前必须问清的三件事
- 源:要搬哪个工作空间(绝对路径)。如果用户只说"这个项目",用当前会话的工作目录。
- 目标:搬到哪(绝对路径,含新名字)。提醒:移动硬盘建议用固定盘符,否则下次插拔换盘符,路径就又失效了。
- 是否有自动化:体检会列出来。有自动化绑定就必须改数据库,不能只复制文件。
标准流程(四步,别跳)
所有脚本都在本技能的 scripts/ 下,需要 Python 3.8+,Windows 自带即可。
命令里的路径一律加引号(中文路径和空格很常见)。
第 1 步:体检(只读,随便跑)
python scripts/plan_migration.py --source "D:\Work\项目A" --target "G:\迁移\项目A"
输出会给出:体积、目标盘空间与文件系统、slug 新旧对照、会话历史条数、数据库待改行数、
自动化绑定数、长路径风险、嵌套工作空间、目录联接。最后一屏是结论。
有 [STOP] 就别往下走,先解决阻断项(空间不足、目标已存在、FAT32、目标在源目录内部等)。
第 2 步:演练,给用户看
python scripts/apply_migration.py --source "D:\Work\项目A" --target "G:\迁移\项目A"
不加 --apply 只打印 8 个步骤,一个字节都不改。
把这份步骤和体检结论一起念给用户,等他明确同意再进第 3 步。
第 3 步:执行
python scripts/apply_migration.py --source "D:\Work\项目A" --target "G:\迁移\项目A" --apply
八个步骤:备份元数据 → 复制 → 校验体量 → 会话目录改名 → 改写 jsonl →
改写 .workbuddy 文本 → 显示名换键 → 数据库改路径(可选隔离源目录)。
- 不加
--isolate-source时源目录原样保留,这是默认且推荐的:先确认新的能用,再自己处理旧的。 - 加
--isolate-source也只是把源目录改名成项目A.migrated-20261029-101500,不删除,
同盘改名是瞬时的,随时能改回来。 - 数据库若被占用(应用正在跑),会打印
--db-only --apply补改命令,不算失败,别慌(补改前会先打印将要改的行数,不加--apply只预览)。
第 4 步:核对
python scripts/verify_migration.py --state "<上一步打印的状态目录>"
全绿才算成功。要更彻底加 --deep 做全量 MD5(文件多时很慢,先跟用户说)。
然后请用户:完全退出 WorkBuddy(含托盘)→ 重启 → 打开新路径,
亲眼确认三样东西:历史会话、.workbuddy/memory 里的记忆、项目级 skills。
第 5 步(可选):回滚
python scripts/rollback_migration.py --state "<状态目录>" # 演练
python scripts/rollback_migration.py --state "<状态目录>" --apply # 执行
回滚只做改名/改回路径/还原备份,不删任何文件;新位置目录会被改名成 .rolledback-<时间戳>。
关键判断
目标路径已存在且非空 → 不覆盖。换一个目标名,或让用户自己确认后清空。脚本会直接 STOP。
源目录里嵌着别的工作空间(子目录里有 .workbuddy)→ 它们会跟着一起复制,但各自的
projects/<slug> 与数据库记录不会自动跟着改。要么分开迁移,要么先问用户这些子工作空间还要不要。
搬到移动硬盘/网络盘 → 额外三条提醒:
- 盘符必须固定(磁盘管理里改盘符),否则换 USB 口就失效;
- 机械移动硬盘休眠会卡 I/O,大项目建议复制时别做别的事;
- 拔盘前一定「安全删除硬件」,WorkBuddy 还开着时更不能直接拔。
FAT32 目标 → 直接拒绝(4GB 单文件上限 + 长路径问题),让用户先格式化成 NTFS 或 exFAT。
数据库改不动(database is locked) → 正常现象。让用户完全退出应用(任务管理器里确认没有
WorkBuddy 进程),再跑 apply_migration.py --state "<状态目录>" --db-only --apply(不加 --apply 只预览行数)。
用户其实只想省空间,不想换位置 → 先提醒:迁移本身不省空间,省空间靠删掉旧目录。
所以要讲清"先搬、验证、再删",别一上来就删。
影响范围与安全边界(审计用)
本技能只写下列位置,别处一律不碰:
| 写入对象 | 时机 | 保护措施 |
|---|---|---|
| 目标目录(用户指定的新路径) | 第 2 步复制 | 只新建;目标已存在且非空直接中止,从不覆盖 |
projects/<旧slug> → <新slug> |
第 4 步 | 先把整个目录复制进备份区,再 rename |
| jsonl 文件内容 | 第 5 步 | 每个文件改前单独复制备份;逐行 json.loads 校验,任何一行失败则该文件整体放弃 |
目标目录内 .workbuddy/** 的 md/json/txt |
第 6 步 | 只做路径字符串替换;JSON 改后重新 parse 校验,失败则不写 |
workspace-display-names.json |
第 7 步 | 改前复制 .bak |
workbuddy.db 的四张表 |
第 8 步 | 改前复制 db 及 -wal/-shm;单事务提交,异常整体回滚 |
| 源目录 | 仅 --isolate-source |
只改名为 <名字>.migrated-<时间戳>,绝不删除 |
硬约束:
- 不加
--apply时不写任何文件,只打印步骤。 - 脚本内没有删除文件或目录的调用(不使用
rmtree/remove/rmdir/del),也不提供删除源目录的功能;旧数据怎么处理由用户自己决定。唯一的"替换"是保存 JSON 时用临时文件原子替换同名文件(写入前已校验)。 - 不读写任何账号凭据、不访问网络、不启动后台服务;只调用系统自带的
robocopy与tasklist(后者仅用于提示"应用正在运行")。 - 所有改动都可以用
rollback_migration.py逆向还原,回滚同样只改名/复原、不删除。
红线
- 绝不
rm -rf/del /S /Q源目录,也绝不替用户删。只改名隔离,删除交给用户。 - 绝不用
robocopy /MIR做备份或迁移(会删掉目标端多余文件);复制用/E。 - 绝不在没跑体检的情况下直接
--apply。 - 绝不在用户没确认前动真实工作空间;先用
--skip-db/ 演练模式说话。 - 绝不手改
workbuddy.db之外的方式绕锁(比如杀掉用户进程)。
脚本清单
| 脚本 | 作用 | 是否写盘 |
|---|---|---|
scripts/ws_migrate_common.py |
公共库:slug 推导、路径改写、体积统计、数据库改名 | 被调用时才写 |
scripts/plan_migration.py |
迁移前体检 | 只读(--json 除外) |
scripts/apply_migration.py |
执行迁移,默认演练 | 带 --apply 才写 |
scripts/verify_migration.py |
迁移后核对 | 只读 |
scripts/rollback_migration.py |
回滚,默认演练 | 带 --apply 才写 |
已知限制(别过度承诺)
- 源目录全程只读:迁移是「复制」不是「移动」,源目录的内容在任何阶段都不被修改,
所以永远保有一份原始数据;有它兜底,最坏情况也能重来一遍。
--isolate-source只是改名隔离,内容不动。 - 回滚的边界:
rollback_migration.py能还原路径、目录名、数据库记录、显示名与已备份的
jsonl/文档。它不能还原用户自己删掉或改过的业务文件——脚本从不删文件,也从不改源数据。 - 自动化 prompt 正文里手写的绝对路径不会被改(那是用户的文字),需要在自动化界面里自己改。
file-history/file-tree-manifests/artifact-index/changes-index等索引仍含旧路径,
属可重建缓存,默认不动;用过之后会按新路径重建。- 移动硬盘盘符一变,整套对应关系(slug、数据库、显示名)全部失效,必须先固定盘符。
- 不碰用户级技能(
~/.workbuddy/skills)、不碰任何账号凭据、不联网。 - 目标文件系统是 FAT32 时拒绝执行(4GB 单文件与长路径限制)。
参考资料
references/storage-map.md— 五处存储的具体路径、格式、哪些能重建、实测样例references/playbook.md— 不依赖脚本的手工迁移全流程(含 Windows 命令),以及给用户的话术references/troubleshooting.md— 常见报错、盘符漂移、权限、长路径、数据库锁