关于 agent-sync
agent-sync 是用于协调多个 Agent 在同一仓库并行工作的 Skill。它结合 Git 持久记录与可过期的协调状态,提供租约、编辑保护和项目配置检查。
主要功能
- 01通过可过期租约协调 Agent
- 02保护共享注册表的编辑
- 03跟踪跨仓库依赖
- 04设置并验证项目配置
- 05协调 Git 记录与云端状态
适用场景
- 01避免多个 Agent 覆盖共享文件
- 02在 Agent 会话间领取和释放任务
- 03检查离开期间的依赖变更
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ssheleg/ agent-sync --skill agent-sync安装后在 Claude Code 中输入/agent-sync确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/adapter-contract.mdbackend-fs.mdbackend-notion.mdbackend-outline.mdbranching.mdearned-rules.mdfabric-coordination.mdhooks.mdlease-protocol.mdpipeline-binding.mdroadmap.mdtwo-sources.mdscripts/agent_sync.pybash_writes.py详细介绍
agent-sync——一个项目,多个 Agent,互不冲突
两个平面,以及它们之间的一条规则:
Git 是记录平面,云端是协调平面。 必须留存的事实先写入 Git,再在云端引用。 谁此刻在做什么 这样的事实存于云端,并会过期。
云端对象绝不会是持久事实的唯一存放位置。下面的所有内容都是为了确保 多个 Agent 同时写入时仍能做到这一点。
**即使无人要求,也要在以下情况下使用:**在包含 .claude/agent-sync.json 的项目中,编辑共享注册表(决策、待解决问题、路线图、工作流、依赖项)之前;未认领的编辑会让两个 Agent 相互覆盖。也可使用 /agent-sync。
四个陷阱——先读这些
**1. 知识库永远不会决定租约。**它做不到:对同一篇 Outline 文档并发追加 12 次,返回了 12 次成功,却只留下了 3 行。互斥必须依靠真正的比较并交换机制。该平面只承载记录,不做其他事。
2. 弄清楚自己持有哪种租约,并明确说明。leaseBackend: "local" 是原子文件创建——在同一文件系统的进程之间互斥,跨机器时仅具建议性。leaseBackend: "git" 会推送一个引用,而远程端拒绝非快进更新,就是比较并交换机制——跨机器互斥。acquire 会打印所用的租约类型。假装持有租约比没有租约更糟:其他 Agent 会停止检查。
acquire 还会将认领信息写入路线图,而 release 会准确还原原状——只改一行、一个单元格;遇到歧义则拒绝操作;往返后 git diff 为空。配置 claimTags 或关闭任务前,请先阅读 references/roadmap.md;关闭任务是对工作成果的说明,应由你负责。
在分支上工作;集成分支是他人维护的稳定基线。acquire
只会在集成分支上写入认领信息——在其他任何分支上,持有者都会留在协调平面中,所有 Agent 都能通过 status 看到它。认领信息一旦提交到某个分支,在合并之前就不可见,而且会让共享路线图变成两个分支都会编辑的文件。使用 merge 落地工作:先计算冲突,不触及任何内容;将合并记录写入 docs/MERGES.md,并释放 --key 租约;merges 会说明你离开期间落地了哪些内容。合并前请先阅读 references/branching.md。
**3. 验证 Claude hook 适配器。**在强制执行未经验证时,请明确运行 guard。将 hook 证据与 gated 租约仲裁情况分别报告。某个模式的五个能力字段只在一处定义——references/adapter-contract.md → 状态能力契约,此处不再重复说明。项目未受保护时,不要声称它已受保护。
**4. 日志无法读取不等于竞争失败。**输出 -,接受 -/*/+,过滤之前统计所有格式错误的类条目行,并拒绝无法解析比例超过 2% 的情况。解析契约和观察到的故障详见 references/lease-protocol.md。
将其引入任何项目——完整流程
scaffold → create the documentation architecture, only where it is absent
adopt → read the repository, propose a config, write nothing
init → write the approved config + the gitignored env file
(operator places the token — never you)
reconcile --set-baseline → make history a counted backlog, once
setup → generate the snapshot that describes this project's wiring
check → validate the whole thing; non-zero if it is not healthy
check 会拒绝缺失的规则目标、凭据或注册表基线、过期快照,以及由 Git 跟踪的环境文件。在引入后、配置变更后以及 CI 中运行它;它会逐一列出所有失败条件。
**scaffold 从不覆盖文件。**它会初始化一个决策注册表和一个指向快照的 AGENTS.md,并保留所有现有文件不变。
现有项目:从 adopt 开始
在 init 之前运行 adopt。它会读取仓库并打印发现的内容——ID 注册表、注册表文件、门控项——以及它拒绝替你做出的决定,然后提出配置建议。
python3 "$SKILL_DIR/scripts/agent_sync.py" adopt
先与操作员确认注册表和受保护文件:如果注册表指向错误的文件,之后的每次检查都会自信地给出错误结果;如果受保护文件列表漏掉了某个共享文件,唯一会发生冲突的位置就得不到保护。在子模块中,它不会声明任何注册表——决策属于父项目。然后从上面的 init 开始完整流程。
项目中的首个命令:init
切勿对未初始化的项目运行任何其他操作。 init 会一次性询问并确定存储方式,然后将其记录下来。
在聊天中向操作人员询问以下两项——不要猜测,也不要选择默认值:
-
协调状态应存放在哪里?
- 知识云——
outline(托管或可自托管)或notion——供多台机器共享的记录、状态感知和看板——两者都不负责决定租约(陷阱 1); - 或本地文件(
fs)——无需凭据,但其他机器上的 Agent 无法看到——没有共享状态感知、跨仓库信号或看板。
租约由
leaseBackend单独决定(陷阱 2),而且gated取决于该选择,绝不取决于记录层面——报告你实际拥有的保障。ID 也是如此:在leaseBackend: "git"下,即使使用fs,注册项也会从 git refs 中预留。 - 知识云——
-
如果选择云端:具体位置。 Outline 需要其实例 URL;Notion 需要容器所属页面的 ID。这些都属于配置,不是秘密,因此可以记录。令牌不可以——绝不在聊天中索要令牌、绝不读回令牌,也绝不替对方放置令牌。
然后使用对方提供的信息运行:
python3 "$SKILL_DIR/scripts/agent_sync.py" init --backend outline --url https://<their-instance>
python3 "$SKILL_DIR/scripts/agent_sync.py" init --backend notion
python3 "$SKILL_DIR/scripts/agent_sync.py" init --backend fs
init 会写入 .claude/agent-sync.json(结构,可提交)和 .env.agent-sync(身份,权限模式为 600),其中令牌一栏为空;它还会将这两个文件加入 gitignore,并打印唯一需要操作人员执行的步骤——创建令牌并放置令牌(notion:放入保管库)。除非使用 --force,否则绝不覆盖任一文件。原样转达最后的说明;此设计依赖令牌始终由对方掌管。
然后,在每次会话开始前
python3 "$SKILL_DIR/scripts/agent_sync.py" status
幂等:检查、修复、报告,并根据 check 的判定指出一个后续操作。
阅读两个状态感知部分:
- 当前正在处理此项目的其他运行实例。 此刻谁持有哪些内容。不要接手这些任务,也不要“只是看一眼”它们所覆盖的文件。
- 你上次查看后新增的内容。 你离开期间落地的跨仓库依赖变更——每个运行实例都带有水印,因此只有发生变化时才会打扰你。依赖变更可能解除你原计划的阻碍,也可能使计划失效。
status 还会作出以下判断:没有凭据 → 降级模式;缺少 task-pipeline → 打印安装命令并停止。检查会覆盖所有主机布局,或明确指定的 pipelinePath,因此不会被任何主机上的副本遮蔽或遗漏。租约核心(acquire/renew/release)需要后端和租约,不需要绑定。
npx sshlg-skills install
命令
| 命令 | 作用 |
|---|---|
init | **首先运行。**询问状态存放位置,写入配置和被 git 忽略的环境文件,打印操作员的步骤 |
status | 检查、修复、报告,并指出一个下一步操作 |
bootstrap | 创建云容器,并打印要粘贴到环境文件中的 ID |
acquire <KEY> | 获取任务 ID 的租约。打印 won 或 lost <holder> |
renew <KEY> | 显式保活。钩子使用 --active-only;无关调用不会延长认领 |
release <KEY> | 在所有路径上归还租约;released 表示远程引用已消失 |
release --held | 在一个进程中归还本次运行持有的所有租约——这是 SessionEnd 执行的操作 |
reserve <REG> [--key K] [--offline] | 在登记簿中预留下一个 ID(DEC、OQ、DEP 等);打印该 ID。--key 可确保重试幂等(一个键对应一个编号);--offline 会生成带命名空间的 REG-o-… ID,不具备全局权威性 |
map-offline <REG> <ID> <N> | 将离线 ID 绑定到正式预留的编号——只追加,绝不重新绑定 |
release-id <REG> <ID> | 归还最终未写入 git 的 ID |
journal <text> | 在本次运行的日志中追加一行 |
record <text> | 追加记录实际构建的内容——--decision DEC-…、--files a,b |
reconcile | 对比意图(git)与实际构建状态(云端)。每个项目运行一次 --set-baseline |
signal <DEP-ID> <state> | 记录依赖状态,绝不发起请求。使用 Fabric 时,通过 com.submit 发起请求;DEP 会引用其看板消息 |
guard <path> | 回答本次运行是否可以写入该路径。退出码 0 = 可以,2 = 不可以 |
board | 重新生成共享看板和本仓库的页面。--mirror 还会将配置的 git 文档渲染到 plane 中 |
whoami | 打印本次运行的 ID 及其持有的租约 |
residue | 查看本次运行在两个 plane 中留下的内容:磁盘上的过期锁,以及没有有效租约对应的认领标签 |
reap [KEY…] | 仅清理本次运行能够证明归其所有且已用完的内容,然后重新读取目录以确认其已清除 |
reap --long-expired | 列出两个 plane 上过期 7 天以上的锁,并逐一给出判断。--apply 仅按过期时间清理——由操作员决定,绝非由你决定 |
setup | 写入生成的快照,说明本项目的配置方式,供 Agent 阅读 |
adopt | 检查现有项目并提出配置方案——不会写入任何内容 |
merge | 合并此分支:在触碰任何内容之前检查冲突,写入合并日志,并在推送后释放 --key 租约。支持 --summary、--dry-run、--push |
merges | 查看你在此分支期间合并的内容。--all 会包含压缩后的历史记录 |
check | 从头到尾验证整个配置。配置不健康时返回非零值 |
scaffold [--full] | 仅创建缺失内容,绝不改动现有行。--full 还会初始化问题登记簿、索引、依赖看板、数据模型和文档门禁 |
finish [--gates] | 工作是否已完成——每个仓库都干净、已推送并已指向目标,且没有遗留的持有项。check 判断项目配置是否正确;此命令判断你是否已完成 |
根据当前 SKILL.md 的位置解析 $SKILL_DIR,并复用绝对路径;
${CLAUDE_PLUGIN_ROOT}/skills/agent-sync 是 Claude 的文本替换,不保证是 Bash
环境变量。
每个会话使用一个身份,以及身份的判定方式
身份优先级(references/earned-rules.md):AGENT_SYNC_RUN_ID · CLAUDE_SESSION_ID ·
CLAUDE_CODE_SESSION_ID(每次 Claude Code Bash 调用都会携带它)· 启动此 shell 的
会话(SessionStart 会将其写入 .agent-sync/sessions/<CLI pid>;命令会沿着
进程祖先链查找它)· 共享——共享身份会明确说明,绝不会被描述成相互独立。
认领——关键的形态
acquire → do the work → release
即使失败,也要释放;被遗弃的租约会阻塞任务,直到 TTL 到期。
对于 PR:先推送持久交接,再释放;参见 references/branching.md。
在另一个仓库中获取的租约会列在此会话下,renew 和
SessionEnd 钩子也会在该仓库中执行(AS-07)。
租约不等于认领。 租约说明当前由谁持有,并会到期;持久认领是 git 中的标签,
由 acquire 写入,并由 release 清除。一项事实,一个归处——不要另创第三处来记录所有权。
两个平面都采用同一种持有状态。 TTL 结束时租约也结束,因此,指向租约已结束的
run 的标签属于残留:status、residue 和 reconcile 会报告它,并由 release <KEY> 清除,
以说明它原本属于谁。租约仍有效的标签绝不会被另一个 run 触碰。
更改获取、到期、抢占或 ID 分配前,请阅读 references/lease-protocol.md。
受保护文件
配置列出了多个 Agent 都会写入的注册表文件。编辑其中一个之前:
python3 "$SKILL_DIR/scripts/agent_sync.py" guard docs/DECISIONS.md
退出码 2 说的是此 run:它没有持有租约——而不是说其他人持有该文件。 拒绝信息会指出另一个 run 及其 key,因为路径旁边的“r-x 持有租约” 很容易被转述成“r-x 持有此文件”。不要因此仍去编辑,也不要“只修一行”—— 被覆盖的决策看起来和原决策一模一样。
两种写入模式。 任务租约授权的是 TASK,而不是文件,因此受保护的写入还要获取
该文件的资源认领(res--<repo>--<canonical path>,SY-04)——即
短事务锁:同一个注册表串行处理,不同文件互不影响;
只有在 leaseBackend: "git" 时才支持跨机器。重度冲突:隔离的 worktree + 合并。
保护机制不能保证由单个任务所有者来执行强制管控。
随附的 PreToolUse 适配器会在所属仓库中针对 Edit/Write、git commit(包括同一命令暂存的内容)
以及由 scripts/bash_writes.py 解析的 Bash 写入运行此检查——它是解析器,不是沙箱。没有经过验证的适配器时,请自行运行。
预留 ID
读取“下一个可用 ID”这一行不等于预留它——两个 Agent 读到同一个数字,就都会使用它。
python3 "$SKILL_DIR/scripts/agent_sync.py" reserve DEC # → DEC-0216
分配按合并后的日志进行定位——包括所有分片,绝不只看此 run 的日志——因此每个 Agent 都会计算出相同的答案。已预留但未写入 git?请执行 release-id,否则该数字会成为一个缺口,并被看板报告为泄漏。
日志中的内容永远不会被编辑或删除
日志会按顺序重放,因此编辑会悄无声息地改写其他 Agent 已据此采取行动的结论。应通过追加来更正:释放租约,为未使用的 ID 执行 release-id,并用较晚的记录取代错误的竣工记录。生成的页面是唯一例外。生命周期和生成对象约定:references/two-sources.md。
两种文档来源,以及协调它们的责任
Git 文档回答应当如何;竣工记录回答实际情况如何。
两者都不高于另一方,两者之间的差距就是发现的问题,而不是缺陷。这项责任贯穿任务的开始和结束:开始前执行 reconcile,完成后执行 record 和 reconcile——而 reconcile 是机械操作,因此把它显示为绿色视为双方一致,正是差异在任务两端都得以留存的原因。
首次执行 reconcile 前,请阅读 references/two-sources.md:其中包含完整责任说明、reconcile 不会判断的内容、生成的 setup 快照、文档应归属的位置,以及为何没有内容会被删除。
绑定到 task-pipeline
此技能提供各个阶段;阶段名称由 task-pipeline 自行定义。
11 个阶段中,有 5 个带有顺序规则:0 在 brief 提交前执行 acquire;1 在编写代码前执行 reconcile;3 在提交 git 前预留 ID;9 执行主要写入;10 对每个租约执行 merge/release。
配置 pipeline.json 时,请阅读 references/pipeline-binding.md——其中说明了各阶段的理由、skills[] 条目、需要保护的内容以及门控表达式。
配置
两个文件,它们之间的划分就是整个安全模型。
.claude/agent-sync.json — 结构,提交到仓库:使用哪个后端、TTL、保护哪些文件、
有哪些寄存器、运行哪些门禁。
.env.agent-sync — 身份信息,由 init 写入,权限模式为 600,并被 Git 忽略;可在此处、
父项目中或 AGENT_SYNC_ENV 指定的位置找到 — check 会打印具体位置。工具会自行读取它:
不要对它执行 source。notion 令牌应存放在 Observatory 保险库中(1.22.0)— init
会打印 vault.py put 命令;记录平面的命令会在 use_secret.py run --vault-only 下重新运行,而
保护钩子不会请求保险库。outline 会将令牌保留在文件中。scheduledIntervalSeconds(≥ 60):
租约持有时间超过此值时,会发出一次警告。
绝不要将主机名或令牌写入配置、测试、示例或提交;绝不要处理、回显令牌值,也不要通过 argv
传递令牌值。如果有人在聊天中提供令牌,请让对方改用该文件。
子模块的配置只声明它自己的寄存器。 跨仓库事实属于父项目;服务仓库列出父项目的决策寄存器 属于配置缺陷。
不可妥协的原则
- 先追加、读回,然后再操作。绝不重写协作文档。
- 没有
agent-sync:generated标记的生成对象会被拒绝,而不是覆盖 — 说明有人接管了它。约定:references/two-sources.md。 - 无论成功还是失败,所有路径上都要释放已获取的资源。
- 运行报告必须说明留下了什么:过期只会结束租约,不会删除文件 —
residue。 - 凭据绝不能进入
argv、日志行或仓库。 - 降级时要明确说明。
ungated是可接受的状态;谎称已执行强制检查则不可接受。 - 同一检出目录中的两个 Agent 必须使用两个身份,否则租约只是装饰。
- 子模块提交只有在父项目指向该提交后才算完成 — 运行
finish。 - 云端持有的所有持久事实都必须是指向 Git 的链接,绝不能取代 Git 中的事实。
- 边界:
.claude/agent-sync.json。未配置,或只有一个 Agent 独自工作 — 跳过协调。操作者可豁免: “no coordination” / «без координации»。
触发条件
除描述中的内容外,还包括:“认领此任务”、“谁在处理这个任务”、“设置 Agent 协调” / «настрой координацию агентов»、“子模块已推送” / «сабмодуль запушен»、“父项目仍指向旧提交” / «родитель на старом коммите»。
参考资料
每个文件只在其对应触发条件下加载,而非默认加载。
| 文件 | 阅读时机 |
|---|---|
references/adapter-contract.md | 添加或审查知识后端时 — 六个基本操作、能力标志、如实说明降级的路径 |
references/lease-protocol.md | 更改获取、过期、抢占或 ID 分配逻辑时 |
references/backend-outline.md | 调用 Outline API 或调试相关调用时 |
references/backend-notion.md | 调用 Notion API 或调试相关调用时 |
references/backend-fs.md | 不使用云端后端运行,或说明降级模式时 |
references/pipeline-binding.md | 配置 pipeline.json 或添加阶段钩子时 |
references/hooks.md | 安装、调试或移除 Claude Code 钩子时 |
references/two-sources.md | 首次协调前、决定文档归属时,或编写生成对象时 |
references/roadmap.md | 配置 claimTags、认领或关闭任务,或重新规划看板时 |
references/branching.md | 开始会产出提交的工作、合并分支,或询问离开期间有哪些工作已合并时 |
references/earned-rules.md | 询问身份为何如此解析,或询问为何需要 finish 时 |
references/fabric-coordination.md | Fabric 协调约定,或使用 reap --long-expired 时 |
缺少 references/?请从 https://raw.githubusercontent.com/ssheleg/agent-sync/main/plugins/agent-sync/skills/agent-sync/references/<file> 获取。