Claude Code 记忆同步进阶——canonical 软链、白名单 .stignore 与跨工作区 skill
#ClaudeCode #Syncthing #开发环境 #服务器运维
上一篇用 Syncthing 打通了三端
~/.claude/,但直接同步各工作区的memory/目录会遇到一个坑:不同设备的工作区路径编码不一样。本文记录把记忆层重构成 canonical_shared/+ 软链、把.stignore从黑名单改成白名单,并新增两个跨工作区 skill 与记忆治理约定的过程——一次完整的"开发环境梳理"。
一、上一版留下的坑:per-encoding 路径错位
Claude Code 把每个工作区的记忆存在 projects/<路径编码>/memory/ 下,而路径编码含绝对路径:
-
Mac:
projects/-Users-lin-infra/memory/ -
Windows:
projects/C--Users-milin-infra/memory/
同一个 ~/infra 工作区,两台机器编码不同。若直接同步 projects/*/memory,两端各写各的目录,记忆根本对不上,同步等于白做。
二、方案:canonical _shared/ + 软链/junction
把真实记忆文件收敛到一个与设备无关的规范目录,各工作区的 memory 只做符号链接指过去:
1 | ~/.claude/projects/ |
只同步 _shared/**(真实文件),软链本身不同步。这样绕开了路径编码差异——两端各自的软链指向本地同一个 canonical 目录,内容天然一致。
验证是否落到 canonical:
1 | readlink -f ~/.claude/projects/-Users-lin-infra/memory |
三、.stignore 从黑名单改白名单
上一版是黑名单(逐条排除凭证/缓存),隐患是:将来任何新文件(含新凭据)默认会被同步出去。改成白名单(默认拒绝,! 显式放行)后,新文件默认不出本机,要共享才主动加行:
1 | // WHITELIST 模式:最后一行 * 之前,没被 ! 放行的一切都不同步 |
安全姿态从"记得排除敏感文件"变成"默认什么都不外泄",更符合最小权限。
四、自检:回执标记验证双向同步
在 canonical 目录放一份 SYNC-CHECK.md,任一设备的 ~/infra 会话跑一段验证 prompt,核对软链落点并追加一行回执:
1 | ## 回执标记 |
判定:memory 解析到 _shared 且能看到另一台设备之前追加的回执行 = 双向同步成功。看不到 → 查 .stignore md5 是否一致、junction 是否建、Syncthing 是否在跑。
五、新增同步产物:跨工作区 skill
个人 skill(~/.claude/skills/)在任意工作区的会话都可见,很适合放"跨项目的做事规范"。把 skills/ 也纳入白名单后,两个 skill 随记忆一起跨设备:
blog-post——解决"大多数写博客的会话不在博客工作区里"的问题。博客的写作流程与脱敏规则原本写在博客仓库 CLAUDE.md,只有在博客工作区才自动加载。该 skill 让任意会话在落盘博文前,先读当前设备博客 CLAUDE.md 的权威规则,并内联硬性脱敏底线(Key/Token/账号绝不写),读不到就停手。
memory-audit——记忆多了会有错的/过期的/重复的。该 skill 按四类信号打分:可证伪(point 到的文件/服务/端口去实时核对)、重复(与 CLAUDE.md 或其它记忆重叠)、出处/置信(是否单次推断)、过期(lastConfirmed 超阈值),输出 KEEP/FIX/MERGE/DELETE 清单,确认后清理。注意:本系统没有记忆调用计数,所以不假装能测"使用频率",用置信+重复+可证伪当代理指标。
六、记忆治理约定
-
frontmatter 加两个字段:
source(出处,排信任度用)、lastConfirmed(上次真正复核日期)。 -
诚实红线:不给没核对过的记忆乱盖当天
lastConfirmed——那等于谎报已验证。 -
清理示例:一条"运维只给内联命令、不建 .sh"的偏好,因语义已被工作区
CLAUDE.md的"不新建抽象"覆盖(记忆准则:不记 repo/CLAUDE.md 已有的),判为重复直接删。
七、让 Codex 复用同一套 canonical 记忆
Codex 自带的 memories_1.sqlite 与 Claude Code 的 Markdown 记忆不是同一套存储。即使
~/.claude/projects/_shared/infra/memory/ 已经同步正常,Codex 的 SQLite 仍可能是空的;直接查
SQLite 会误判成"没有记忆"。
接入方式不是修改 Codex 内部数据库,而是用工作区 AGENTS.md 做 bootstrap:
-
每个
~/infra新任务开始时,先按当前系统解析~/.claude/projects/_shared/infra/memory/; -
必读
MEMORY.md,把它作为共享索引; -
再按任务读取具体条目,例如当前 homelab 待办读
todo-homelab-current.md,动态状态读project-homelab-infra.md; -
写回时只改 canonical Markdown,并同步更新索引。
Mac 与 Windows 的 AGENTS.md 使用相同规则,但 canonical 绝对路径分别按各自的用户目录解析。这样 Claude Code 和 Codex 共用同一事实源,同时不依赖某个产品的内部数据库格式。
三字段加载测试
canonical 目录增加一份 CODEX-LOAD-TEST.md,其中保存一个只属于该文件的测试标记。全新
Codex 任务收到测试指令后,必须返回:
-
测试文件中的完整标记;
-
当前设备的 canonical 绝对路径;
-
MEMORY.md中"家庭实验室当前待办"对应的文件名。
三项分别证明 Codex 实际读取了测试文件、解析了当前设备路径、读取了索引。测试标记不能复制到
AGENTS.md 或博文中,否则只加载 bootstrap 也能答对,测试就失去意义。
2026-07-26 的实测中,Mac 与 milin_desktop 都返回了正确的三项结果;两端AGENTS.md、MEMORY.md 和测试文件的 SHA-256 也一致。
八、公网环境下同步不动:静态 LAN 地址的陷阱
这次复核恰好发生在 Mac 离开家庭局域网后。三端文件内容此前一致,但新增测试文件迟迟不到
Windows。Syncthing 进程和 N100 容器都健康,真正的问题是每个远端设备只配置了:
1 | tcp://192.168.2.x:22000 |
离开 LAN 后这些地址不可达,即使已经开启全局发现和 relay,也不会自动替代手工写死的地址。修复时保留原 LAN 地址,并为每个对端追加:
1 | dynamic |
回家后仍可走局域网直连;在公网则通过发现或 relay 建链。本次公网实测中,Mac 到 N100 和
milin_desktop 都建立了 relay-client 连接,claude-config 文件夹状态回到 idle,两端
completion 均为 100%,needItems 与 needBytes 都为 0。
Windows 还有一个独立现象:管理员 SSH 会话下,Codex 的只读 sandbox 启动 PowerShell 子进程时可能报 0xC0000142。这不影响 Syncthing 或记忆文件本身;远程验证需区分"文件未同步"与
“SSH 管理员令牌下 sandbox 初始化失败”,正常桌面会话应另行验证。
延伸阅读
-
《Claude Code 多设备记忆同步——Syncthing 方案实录》(本文的上一篇,三端 Syncthing 打通)
-
《milin_desktop v2rayA 点启动弹错——孤儿 core 占端口排查》(同一轮开发环境梳理里的代理维护)
