agent-semaphore
A coordination layer for parallel coding agents that provides intent-carrying claims, conflict prediction, and a serialized landing queue to prevent merge conflicts.
README
agent-semaphore
A coordination layer for parallel coding agents. Worktrees do not remove merge conflicts — they postpone them to integration time. agent-semaphore closes the gap: intent-carrying claims on scopes, a warning at the moment of writing, conflict prediction before anything is committed, and a serialized landing queue with a mandatory test gate.
Local-first: no daemon, no cloud, no account. A single SQLite file in the git common dir
is the whole rendezvous point, so every worktree of the repo sees it by construction.
Cross-vendor by design — Claude Code hooks and MCP, Codex CLI over MCP, everyone else
over a git pre-commit hook.
The problem
Agent-written pull requests conflict at 27.7% — against 10–20% for human ones (AgenticFlict, 107K+ agent PRs). On co-active pairs the split is 19.8% intra-agent vs 41.7% cross-agent: agents have no horizontal awareness of each other, and every product that ships coordination coordinates only its own agents. A badly resolved conflict carries up to ~26x the bug density of ordinary code (EMSE 2020) — the expensive part is not the conflict, it is the quiet bad resolution.
Isolation is solved and commoditized (a worktree or a container per agent — everyone
ships it). Prediction and integration are not: nobody runs git merge-tree between live
worktrees, and standalone local merge queues effectively do not exist.
What it does
| Layer | Mechanism |
|---|---|
| Claims | Leases, never locks: TTL, renewal by activity, monotonic fencing epochs, a mandatory intent (reason). Atomic all-or-nothing acquisition of the whole scope set in canonical path order, so deadlocks are impossible by construction. exclusive / shared / intent modes. Stealing is legal only from a dead holder or a human, and it is audited. Releasing a scope wakes the waiters and tells them which branch to rebase onto. |
| Enforcement | A PreToolUse hook (read-only against the DB, p95 ≈ 16–47 ms) that sees every write: protected ("hot") classes are denied always, another agent's scope is denied once in warn mode and permanently in strict. The deny text is written for the model — it names the holder, its intent, its branch and the exact call to make next. A PostToolUse hook auto-claims whatever was written. A git pre-commit hook is the vendor-neutral floor for agents without hooks, and for humans. |
| Radar | Snapshots of dirty worktrees taken through a temporary index (never mutating the working tree), compared pairwise with git merge-tree --write-tree. The tree is built twice: if the two builds disagree the snapshot is reported UNSTABLE, never CLEAN. Statuses: CLEAN / TEXTUAL / STRUCTURAL / HEAVY, with ConE-style noise filters. |
| Queue | FIFO under flock, one entry in flight. Rebase in a scratch worktree, then a mandatory test gate, then fencing against the global high-water epoch, then a git update-ref CAS into a staging branch. A conflict bounces back to the author with instructions ("your context is freshest"), and after every landing everyone is told the target moved. |
Hard guarantees exist at exactly one place: the landing path. Hooks and pre-commit are
cooperative admission control and telemetry, not a security boundary — ASEM_HOOK_OFF=1
and ASEM_OVERRIDE=1 are documented, audited escape hatches. This is stated up front
because a coordination layer that pretends to be a sandbox is worse than none.
Quickstart
uv tool install git+https://github.com/alwh1te/agent-semaphore # asem on PATH
# or, from a clone: uv tool install -e .
cd <your repo>
curl -O https://raw.githubusercontent.com/alwh1te/agent-semaphore/main/.agent-semaphore.toml.example
mv .agent-semaphore.toml.example .agent-semaphore.toml # set the gate command, hot classes, target branch
asem init # state in .git/agent-semaphore/
asem install --git-hooks # Claude Code hooks + .mcp.json + git pre-commit
asem doctor # PASS checklist
asem claim src/api/ -i "refactor auth parsing" --ttl 30m # exit 3 = held by someone else
asem check src/api/routes.py # who holds it, and what for
asem radar # conflicts between worktrees, before any commit
asem land feature-branch # rebase -> gate -> CAS into the staging branch
asem notices # messages addressed to you
asem status | asem queue status | asem doctor
Exit codes are part of the contract: 0 ok/free, 3 held/conflict/bounced, 2
usage, 1 internal error — a script can tell "coordination said no" from "the tool
broke".
Measured
Nobody in this space had measured whether claims actually reduce conflicts, so the repo ships two benchmarks of its own.
Scripted (docs/benchmark.md, 60 runs, deterministic agents,
compliance = 1 by construction): integration conflicts 60% → 0%, human interventions
9 → 0.
Live agents (docs/bench-llm.md, 40 runs of two concurrent
claude -p agents, $17.95):
| mode | ICR | WME | did_work | caught-up | $/run | COR |
|---|---|---|---|---|---|---|
| no coordination | 40% | 2 | 100% | 0% | $0.34 | 1.00x |
| advisory claims | 20% | 1 | 100% | 40% | $0.50 | 1.72x |
| claims + radar | 10% | 2 | 80% | 40% | $0.46 | 1.93x |
| strict + queue | 0% | 0 | 100% | 40% | $0.50 | 1.98x |
Three findings the scripted harness structurally could not produce:
- Conflicts are removed by the catch-up, not by the claim. 10 out of 10 runs where an agent rebased onto its peer's branch merged clean; every conflicted coordinated run is one where both agents claimed politely and neither rebased. A claim serializes writing — it does not hand you the other agent's result. That finding is what produced the "release wakes the waiters and names the branch" feature.
- The hook never fired once in 40 runs. With the protocol in the prompt, agents claim before editing and never write into a held scope, so enforcement turned out to be insurance that was not needed — not the working layer.
- Coordination can convert a conflict into work that never happened. In two runs the
blocked agent quoted the holder, its intent and its branch, and abandoned its task.
Without the
did_workcolumn next to ICR, those runs read as a clean success — which is why the column is there.
Semantic drift (textually clean, semantically broken) survives every advisory layer in both benchmarks and is caught only by the queue's mandatory gate.
How it plugs in
- Claude Code —
asem installwrites project.claude/settings.json(PreToolUse + PostToolUse), addsBash(asem:*)andmcp__semaphore__*to the allow-list, and registers the MCP server in.mcp.json. Committed wiring is host-portable ($HOMEand a bareasem), so a repo shared across machines does not carry one host's paths. - MCP (
asem mcp, server keysemaphore) —claim,release,check,status,extend,report_intent,radar,enqueue_land,land_status. Every response drains pending notices, so agents learn about steals, bounces and moved targets without polling. - Codex CLI — same MCP server via
~/.codex/config.toml, plus a protocol snippet forAGENTS.md. Headless Codex silently cancels MCP calls unless the tools are pre-approved;docs/integration.mdhas the working config. - Anything else —
asem install --git-hooksputs apre-commitgate in the shared hooks dir (it chain-loads whatever hook was there before).
Documentation
docs/00-research.md— the research dossier this design is built on: the measured problem, the tool landscape, the classic prior art worth stealing from, the 2024–26 papers, and the three confirmed gaps in the market.docs/adr/ADR-001-architecture.md— the architecture, plus a risk register and ten adversarial attacks from a blind review (eight of them changed the design).docs/adr/ADR-002-stack-and-state.md— stack, naming, and where state lives.docs/benchmark.md/docs/bench-llm.md— both benchmarks: method, results, and the limitations spelled out.docs/integration.md— installation and what is wired where.
Status
v1 is implemented and dogfooded: the repository coordinates its own agents through it. 150 tests, a p95 hook-latency gate in CI, both benchmarks reproducible from the repo.
Known limits, stated plainly: promotion from the staging branch to main is still manual
and ungated (asem promote is the next feature); the queue never pushes; there is no
symbol-level scoping, no semantic conflict detection beyond the test gate, no LLM
auto-resolution (the published ceiling is ~55–60% correctness, which is not good enough
to run unattended); and multi-host is a v2 design, though the schema already carries the
host column.
Development
uv run pytest -q # 150 tests
uv run ruff check . && uv run ruff format --check .
uv run python bench/hook_latency.py 200 # hook latency gate (p95 < 100 ms)
uv run python bench/runner.py --seeds 3 && uv run python bench/report.py
The PreToolUse hook script is vendored outside the package and must stay stdlib-only —
it runs on every write of every agent, so it has a latency budget rather than
dependencies. See CONTRIBUTING.md.
License
MIT — see LICENSE.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。