#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
2
3
4
5
6
~/.claude/projects/
_shared/infra/memory/ ← canonical,真实文件,唯一被同步
MEMORY.md
reference-*.md / project-*.md / feedback-*.md
-Users-lin-infra/memory → _shared/infra/memory (Mac: symlink)
C--Users-milin-infra/memory → _shared/infra/memory (Win: junction)

只同步 _shared/**(真实文件),软链本身不同步。这样绕开了路径编码差异——两端各自的软链指向本地同一个 canonical 目录,内容天然一致。

验证是否落到 canonical:

1
2
readlink -f ~/.claude/projects/-Users-lin-infra/memory
# → /Users/lin/.claude/projects/_shared/infra/memory

三、.stignore 从黑名单改白名单

上一版是黑名单(逐条排除凭证/缓存),隐患是:将来任何新文件(含新凭据)默认会被同步出去。改成白名单(默认拒绝,! 显式放行)后,新文件默认不出本机,要共享才主动加行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// WHITELIST 模式:最后一行 * 之前,没被 ! 放行的一切都不同步
!/CLAUDE.md
!/settings.json
!/plans
!/plans/**
!/plugins/installed_plugins.json
!/plugins/known_marketplaces.json
// 跨设备记忆:只同步 canonical 共享目录
!/projects/_shared
!/projects/_shared/**
// 个人 skills:跨工作区共享
!/skills
!/skills/**
!/.stignore
// 忽略其它一切(含 .credentials.json / sessions / cache / *.sync-conflict-*)
*

安全姿态从"记得排除敏感文件"变成"默认什么都不外泄",更符合最小权限。


四、自检:回执标记验证双向同步

在 canonical 目录放一份 SYNC-CHECK.md,任一设备的 ~/infra 会话跑一段验证 prompt,核对软链落点并追加一行回执

1
2
## 回执标记
- MiLin-MacbookAir | 2026-07-23 | Mac | /Users/lin/.claude/projects/_shared/infra/memory

判定: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:

  1. 每个 ~/infra 新任务开始时,先按当前系统解析~/.claude/projects/_shared/infra/memory/

  2. 必读 MEMORY.md,把它作为共享索引;

  3. 再按任务读取具体条目,例如当前 homelab 待办读todo-homelab-current.md,动态状态读 project-homelab-infra.md

  4. 写回时只改 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.mdMEMORY.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%,needItemsneedBytes 都为 0。

Windows 还有一个独立现象:管理员 SSH 会话下,Codex 的只读 sandbox 启动 PowerShell 子进程时可能报 0xC0000142。这不影响 Syncthing 或记忆文件本身;远程验证需区分"文件未同步"与
“SSH 管理员令牌下 sandbox 初始化失败”,正常桌面会话应另行验证。


延伸阅读