Skip to main content
Glama
Cohexa-ai

Agent Coherence — Stale Write Guard (FS)

Official
by Cohexa-ai

agent-coherence

agent-coherence stops one agent from silently clobbering another's work on a shared plan.md, store key, or memory.json — a vendor-neutral MESI + optimistic-concurrency coordinator for agent state on a single host, with the safety invariants machine-checked in TLA+.

Two agents share an artifact — a plan.md, a store key, a memory.json. One reads it and works; meanwhile a peer commits a newer version; the first writes back anyway. Last write wins, the peer's work is silently gone, nothing errors, and every downstream decision builds on the wrong version.

Why it goes unnoticed. An agent system keeps two records of what happened: the one your infrastructure can verify — which version each agent held, what actually committed, what was refused — and the one the model narrates, "task complete", "the plan is updated". They agree until an agent acts on a version that moved underneath it, and the narrated record is the one you read. That is why a lost update looks like a clean run and gets debugged as a model problem. agent-coherence is the verified record for the state your agents share. (It does not verify what an agent did in the outside world — a sent email, a fired webhook — that stays your outbox and idempotency layer's job.) agent-coherence turns that silent clobber into a loud, typed refusal: MESI-style ownership and invalidation over shared artifacts, optimistic commit-CAS for concurrent writers, and a read-generation fence for crash-reclaimed ones — a stale write is denied or returned as a retryable conflict, never silently applied. Same library, same protocol, across LangGraph, CrewAI, AutoGen, the OpenAI Agents SDK, plain files shared across processes (CoherentVolume), any MCP client (the stale-write-guard-fs server, via the mcp extra), and any custom orchestrator. Same behavior regardless of which model provider (Anthropic, OpenAI, Google, Mistral, open-source) the agents talk to.

CI PyPI arXiv Discussions

mcp-name: io.github.cohexa-ai/stale-write-guard-fs

# Requires Python 3.11+
pip install "agent-coherence[langgraph]"        # LangGraph drop-in
pip install "agent-coherence[crewai]"           # CrewAI adapter
pip install "agent-coherence[openai-agents]"    # OpenAI Agents SDK adapter (experimental)
pip install "agent-coherence[diagnose]"         # ccs-diagnose CLI
pip install "agent-coherence[mcp]"              # stale-write-guard-fs MCP server
pip install "agent-coherence[conformance]"      # substrate conformance corpus (for foreign implementations)
pip install "agent-coherence[all]"              # everything
# Before
from langgraph.store.memory import InMemoryStore
store = InMemoryStore()

# After — one import change, no node code changes
from ccs.adapters import CCSStore
store = CCSStore(strategy="lazy")

store.get(), store.put(), store.search() keep working unchanged. CCSStore adds read-side coherence: a peer's commit invalidates your cached view, so your next read is a fresh miss. It does not deny a stale write-back — put is not version-CAS; for write-side lost-update prevention, route writes through CoherentVolume or write_cas (below).

The one-import swap assumes your store namespaces carry the agent identity in namespace[0] — a (user_id, "memories") shape would merge users onto one shared artifact. See the namespace convention.

# Plain files shared across processes / sessions — no framework required
from ccs.adapters.coherent_volume import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("plans/**",))
plan = vol.read("plans/plan.md")           # tracked read — your view is registered
vol.write("plans/plan.md", revised_plan)   # stale view? denied fail-closed → vol.reacquire() and re-derive

agent-coherence-replay — invariant-replay for any CoherenceAdapterCore-mediated agent system. LangGraph capture verified in v1 via CCSStore.record_to(path); CrewAI / AutoGen wired through the same seam but unverified — file an issue if it breaks.

What it guarantees

Each row is a safety invariant model-checked with TLA+/TLC. make tla-check runs all nine specs. CI runs them as three parallel jobs on every pull request that changes a TLA+ input (the specs and their configs, the Makefile, the workflows, and the scripts that decide and judge the run), and on every run outside a pull request. Every spec carries a documented mutant that must fail — the invariants are load-bearing, not decorative.

The silent failure

What happens instead

Mechanism

Invariant

Stale-read overwrite — an agent acts on an old snapshot and writes over a newer version (two sessions, one plan.md)

the write is denied fail-closed; the writer must reacquire() and read the current version

MESI single-writer ownership + invalidation

SingleWriter, MonotonicVersion

Concurrent lost update — two writers hit the same key and both "succeed"

exactly one wins; the loser gets a typed conflict + bounded retry, never a silent drop

optimistic commit-CAS (write_cas)

NoLostUpdate

Reclaim-zombie write — a stalled writer is reclaimed by crash recovery, wakes later, and lands its stale commit; the version never moved, so a version check passes

the commit is rejected with a typed stale_read_generation conflict

read-generation fence — reclamation bumps the artifact's ownership epoch, checked atomically at commit

NoStaleApply

Zombie revoke — an invalidation is minted, and by the time it is applied the agent it names has been reclaimed, re-acquired and re-read; applying it now revokes a grant issued after the signal, and the target's version never moved, so a version check sees nothing

the invalidation is dropped as obsolete — the target is not behind what it announces, so the grant survives

a peer-issued revoke is pinned to the target's last observed version, and every revoke-class ending of a write claim — a sweep reclaim or a release — moves the ownership epoch (a peer's write-acquire moves neither comparand; the effect gate fences that by grant state instead)

NoZombieRevoke, NoSilentRevoke

Reclaim-zombie effect — the same reclaimed writer's escaping effect (a webhook, a deploy, an opened PR) fires on a decision made under the revoked grant; the version never moved, so a version-only re-check passes

the effect is held (StaleView) at the effect boundary — versions equal, ownership generations apart

gate() re-validates a pair-atomic (version, ownership generation) snapshot before firing

NoStaleAdmit

Torn multi-artifact read (read-skew) — an agent reads several artifacts one by one while a peer commits in between; each read was individually current, but the combination never coexisted

session reads serve from a pinned consistent cut; commits validate against the pinned base; a lapsed session fails closed with a typed rejection, never a silent fall-through to live state

multi-artifact snapshot sessions

NoReadSkewWithinCut, PinAlwaysRetained

Dead owner blocks the fleet — a crashed agent holds EXCLUSIVE forever

the heartbeat/TTL sweep reclaims the grant (on by default; best-effort, rate-limited)

crash-recovery sweep; an operator can read from /status?detail=full when the sweep will reclaim each write grant (how) and which grants it reclaimed (how)

sweep invariants I3–I6

The table is also machine-checked against the test suite: the packaged claim ladder (ccs.testing.claim_ladder) pins every claim-phrase above to the tests that would fail without it — resolved at collection time and two-way drift-guarded by tests/conformance/test_claim_ladder.py, so a claim cannot outlive its proof (and a cross-host rung cannot appear without one).

Scope, honestly: the guarantees hold for writers that go through the coordinator, under a single coordinator (one host). Both registries hold one lock across every check-and-act sequence — the durable SQLite registry and the in-memory one carry the same concurrency contract, so an embedder may drive either from several threads. Concurrent same-key writers on one host are covered, with one open gap (#248): a plain write() made after a peer's write_cas_at has committed but before the peer's bytes reach disk is lost when the writing volume holds no live view of the file — it never read it, it read it only inside that window (the coordinator refuses such a read, even where read() hands back the old bytes under the default on_stale_read="allow"), or a write_cas / write_cas_at it ran ended in ViewWedged. Both calls report success, disk keeps the peer's bytes while the coordinator records the lost write as the newest version, and every write_cas / write_cas_at on the file raises ViewWedged until a plain write() replaces it. A volume that read the file before the peer committed is refused instead. Cross-host fencing is on the roadmap, demand-gated — if you need it, open an issue. Edits that bypass the coordinator entirely (a human in an editor, a formatter, a regenerating script) are caught at the read()/write() boundary by content-hash checks — the foreign-edit guards below, enforced by tests rather than TLA+; the batch CAS path has a narrower boundary, spelled out in Atomic multi-file publish. Specs, the invariant ↔ implementation map, and the mutant recipes live in formal/tla/.

Correctness is the wedge; the token savings come with it. Writes publish ~12-token invalidation signals instead of rebroadcasting full artifacts, so read-heavy fleets stop re-paying for state they already hold:

Workload

Agents

Reads:Writes

Hit rate

Savings

Planning (read-heavy)

4

12:1

75%

69%

Code review (moderate)

3

8:3

60%

47%

High-churn (write-heavy)

4

8:4

50%

29%

Measured on real LangGraph graphs; see docs/reproduce.md and the user guide.

Those are the spatial savings (more agents sharing one artifact). The temporal dimension — a single agent whose source drifts between its turns — has its own pre-registered benchmark, TC-1 (#116): a reproducible savings-regime map of how many re-fetches coherence-gating avoids as the change-rate rises. The metric is re-fetches-avoided — a proxy, a regime map, not a token/dollar invoice. Reproduce with python tools/run_cost_sweep.py; the locked verdict + numbers (PASS at n=50, crossover r≈0.31) live in benchmarks/cost_preregistration.md. Shipped in v0.9.3.

Related MCP server: mcp-lock-manager

RAG & shared agent memory

RAG corpora and agent memory are shared mutable state, so the stale-read→write lost update lands there too — and a consistent store doesn't save you: the staleness is in the agent's cached view of a record, not the store. Two agents read a record at v1; one writes v2; the other, still on its v1, writes an edit computed from v1 and clobbers v2. agent-coherence keeps the readers current — CCSStore is a drop-in for langgraph.store (composing with Mem0, Letta, LlamaIndex, a vector store, or a plain file underneath whatever you already use; it stores no vectors and does no ranking), so a peer's commit invalidates the stale cached view (read-side coherence). Preventing the stale write-back itself is the write side — route those writes through CoherentVolume or write_cas.

  • Runnable, deterministic demo (offline, no keys): python -m examples.coherent_volume.main reproduces the documented lost update, then prevents it.

  • Honest scope: writes that go through the coordinator are caught. Auto-watching an unmanaged external source that changes with no coordinator write (a hand-edited file, an out-of-band re-index) is the source-watcher case — on the roadmap, demand-gated, not shipped today.

  • Positioning + FAQ: agent-coherence.dev/rag.


  • 📖 User guide — installation, namespace convention, strategies, observability, telemetry, examples, full API reference

  • 🔎 RAG & shared memory — coherence for retrieval corpora and agent memory stores, with the runnable lost-update demo

  • 🗂️ Coherent workspace — CoherentVolume, the data-plane appliance for plain files shared across processes

  • 🧱 BYO substrate — CoherentRow / CoherentObject, the same coherence over a Postgres row or an S3 object you already run

  • ⏪ Workspace versioning & restore — WorkspaceVersioner, checkpoint a mixed file + S3 workspace, name who may restore it, and bring it back with per-member honesty

  • 🛡️ Foreign-edit guards — catch out-of-band edits (a human, a formatter, a script) at the read/write boundary, and count the ones in between offline (foreign-write detection)

  • 🔌 MCP server — stale-write-guard-fs, the same guarantee for any MCP client, no Python integration required

  • 🚦 Effect-ordering gate — gate(), fire an agent's effect only on the input state — value and grant — it decided from; the same verdict over MCP (swg_gate) and over HTTP (POST /hooks/effect-fence)

  • 📸 Multi-artifact snapshot sessions — read several artifacts as one consistent cut; no torn reads

  • 📦 Atomic multi-file publish — atomic_publish, land a set of files all-or-nothing; never a torn pair

  • 🤝 Targeted grant handoff — hand a file to one named session; the giver's later write is refused as handed_off until the version moves, the successor declines, or the giver withdraws. From Claude Code, CoherentVolume, MCP or HTTP; served by the Python coordinator only

  • 🧮 Formal verification — the TLA+ specs, invariant ↔ implementation map, mutant recipes

  • 🩺 ccs-diagnose CLI — find divergent reads in your existing LangGraph graph without changing any code

  • 📊 Conflict-outcome counters — count how often each typed refusal actually fired, read offline from a closed state.db

  • 🧩 Claude Code plugin — cross-session coherence for the prose rules (CLAUDE.md, plan.md) parallel Claude Code sessions share

  • 🔍 Why coherence matters — the gap across LangGraph, CrewAI, AutoGen, and Claude Agent SDK

  • 🧭 The MESI-derived approach — how the protocol maps each documented gap to a shipped surface, with boundaries

  • 🪪 Caller principal — once a writer claims one, a request naming its session without it cannot release its grants or commit under its name; clients that never claim keep working

  • 🔐 Security & supply chain — kill switches, hash-pinned install, attestation verification, threat model

  • 📜 Changelog — version history

  • 📄 Paper on arXiv (2603.15183) — formal protocol, TLA+ verification, simulation results

How it works

Each shared artifact is cached locally per agent and reads serve from the local cache when that copy is fresh. Writes commit to a coordinator, which sends lightweight invalidation signals (~12 tokens) to peers so the next read fetches the new version instead of rebroadcasting the full artifact. Consistency is single-writer-multiple-reader per artifact with bounded staleness — peers re-fetch on next read.

Two write disciplines share the same guarantee. Pessimistic: acquire EXCLUSIVE, commit; a writer whose view went stale is denied and must reacquire(). Optimistic: write_cas — read, compute, commit-CAS; the loser of a race gets a typed conflict and bounded retry. Crash recovery composes with both: reclaiming a stalled grant bumps the artifact's ownership epoch, so a reclaimed writer that completes later is rejected at commit even when the version is unchanged (the read-generation fence). On the read side, a snapshot session pins a consistent cut across several artifacts, so a multi-artifact read never sees a torn mix of versions.

Five synchronization strategies ship out of the box: lazy (default), eager, lease (TTL-based), access_count, and broadcast. Pick the one that matches your workload's read/write ratio and how aggressively cached reads should refresh.

Architecture

  • Protocol (ccs.core, ccs.strategies) — coherence state machine and synchronization strategies; no framework dependencies.

  • Coordinator (ccs.coordinator) — authority service tracking directory state, publishing invalidations, arbitrating commit-CAS, and reclaiming stale grants (crash recovery + read-generation fence).

  • Adapters (ccs.adapters) — framework integrations for LangGraph, CrewAI, and AutoGen (~100 lines each), plus an experimental OpenAI Agents SDK adapter (Session-cache coherence + RunHooks).

  • Coherent workspace (ccs.adapters.coherent_volume) — the data-plane appliance: an out-of-process coordinator client that brings the same guarantee to plain files on disk, no framework required. See Coherent workspace.

  • MCP server (ccs.mcp) — the stale-write-guard-fs stdio server that exposes the coherent-workspace guarantee to any Model Context Protocol client over ten swg_* tools. See MCP server.

  • Simulation (ccs.simulation) — deterministic tick-driven engine for scenario benchmarks with failure injection.

  • Event bus (ccs.bus) — the transport for invalidation signals; in-memory / in-process today (InMemoryEventBus). Networked transports (Redis, Kafka, NATS, gRPC) for a multi-host deployment are on the roadmap, demand-gated.

Protocol safety properties — single-writer, monotonic versioning, the crash-recovery sweep invariants, the OCC no-lost-update, the reclamation fence's no-stale-apply, the effect gate's no-stale-admit, version retention's no-collected-read, the snapshot session's no-read-skew-within-cut, and workspace restore's no-partial-restore-registered — are model-checked with TLA+/TLC. CI runs all nine specs, split across three parallel jobs, on every pull request that changes a TLA+ input (the specs and their configs, the Makefile, the workflows, and the scripts that decide and judge the run) and on every run outside a pull request.

Coherent workspace: the data plane for shared files

The framework adapters wrap a store. CoherentVolume is the other half — the data-plane appliance, the building block that makes a shared workspace coherent for plain files on disk, with no framework in the loop. Architecturally it's an out-of-process coordinator client, not an in-process wrapper: it writes the policy, spawns (or attaches to) a local coordinator over SQLite-WAL, and routes reads and writes through it. Your content stays on the real filesystem; the coordinator holds only MESI state, a content hash, and a version per managed file. Point a sibling volume in another process at the same workspace and it attaches to the same coordinator, so a single-host fleet shares one coherent view. Every volume in that fleet must declare the same managed globs: an attaching volume adds nothing to a running coordinator's policy, so at attach each volume checks the globs it declared against the coordinator's strict, tracked and ignored sets and refuses, naming them, if any is not enforced. A mixed-globs fleet is unsupported; it fails closed rather than quietly.

from ccs.adapters.coherent_volume import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("plans/**", "memory/**"))
data = vol.read("plans/plan.md")              # bytes — registers a SHARED view
vol.write("plans/plan.md", revise(data))      # stale view? denied fail-closed
data = vol.reacquire("plans/plan.md")         # recover: clear the stale view + mandatory fresh read

The explicit read / write / reacquire / write_cas API is the supported primitive (write_cas(path, make_content) is the optimistic counterpart for same-key contention — the loser gets a typed conflict, never a silent drop). For code you'd rather not rewrite, an opt-in, demo-grade open() shim routes managed-path opens through the volume so existing open() / pathlib calls get coherence unchanged:

from ccs.adapters.coherent_volume import coherent_workspace

with coherent_workspace(workspace_root, managed=("plans/**",)):
    text = open("plans/plan.md").read()       # registers a SHARED view
    open("plans/plan.md", "w").write(edit)    # stale view? raises out of close()

Scope, honestly. Plain write() prevents the sequential stale-read→write lost update for a single-host fleet sharing one workspace (A reads v1, B reads v1, A commits v2, B's stale write is denied → B re-reads); concurrent same-key racers go through write_cas — one winner, the loser gets a typed conflict and re-derives, never a silent drop. Edits that bypass the volume entirely are caught at the boundary by the foreign-edit guards. It does not catch an agent that re-reads fresh bytes and then writes a buffer computed from older ones. The open() shim is convenience, not the contract: it covers open()/pathlib text+binary read/write, but not raw os.open, subprocess redirection, mmap, or append/update modes — those delegate to the original open() unchanged. Run it yourself: python -m examples.coherent_volume.main (offline, deterministic, no keys), or read the positioning + FAQ.

Foreign-edit guards: writes that bypass the coordinator

Coordination covers writers that opt in — but real workspaces also get edited from outside: a human fixes a file in an editor, a formatter rewrites it, a CI script regenerates it. Without a guard, the next agent write silently buries that edit, and the next agent read silently builds on bytes the coordinator never saw. CoherentVolume guards both boundaries with a content-hash check:

  • Write boundary — on by default. Before writing, the volume checks whether the managed file's on-disk bytes changed out-of-band since it last read or wrote them. If they did, the write raises StaleView instead of clobbering the foreign edit — recover with reacquire() (fresh read → re-derive → re-write). Opt out with CoherentVolume(on_stale_write="allow") to restore last-writer-wins.

  • Read boundary — opt-in. With CoherentVolume(on_stale_read="raise"), re-reading a managed file whose bytes changed out-of-band raises StaleView instead of returning bytes your other state wasn't computed from; in strict mode the coordinator enforces the same check server-side. A volume never denies its own just-written bytes — the benign commit→disk-write lag window is recognized and suppressed. on_stale_read governs read() only. Whatever it says, the versioned reads — read_with_version(), read_with_version_generation() and the MCP swg_read — refuse a managed file whose bytes are not the coordinator's content at the version it reports, outside that same window after the volume's own commit: they raise StaleView (swg_read answers a stale_view deny with no version) rather than hand out a version a compare-and-swap would then be checked against.

vol = CoherentVolume(workspace_root, managed=("plans/**",), on_stale_read="raise")
# a formatter rewrites plans/plan.md out-of-band …
vol.write("plans/plan.md", revised)   # StaleView — the foreign edit survives
fresh = vol.reacquire("plans/plan.md")  # recover: fresh read, re-derive, re-write

Scope, honestly. These are content-hash checks at the volume's read/write boundary — best-effort point-in-time detection, not filesystem interception. A write that never goes through the volume is caught at the next volume read/write of that file, not blocked as it happens; watching unmanaged external sources is on the roadmap, demand-gated. What happens before that next read or write is now observable: the Python coordinator's sweep asks git which coordinated files changed on disk and records each change it cannot account for as foreign — counted, never denied — and ccs.diagnose.foreign_writes.read_foreign_write_report reads the tally back offline from a closed state.db (foreign-write detection). It watches only files git tracks, and only when the coordinator's root is the top of its git work tree (#241). These guards are enforced by tests, not TLA+ — the model-checked invariants cover the protocol state machine, not disk bytes. They cover read()/write() (and the MCP tools that wrap them); the CAS paths check versions instead — write_cas/write_cas_at still fail closed on a foreign edit (the content-checked comparand read wedges rather than clobbering), but a multi-file atomic_publish does not check disk content at all — see its scope note.

MCP server: stale-write-guard-fs

The same guarantee for agents that speak Model Context Protocol — Claude Code, Cursor, or a custom runtime — with no Python integration at all. stale-write-guard-fs is a stdio MCP server that wraps CoherentVolume and exposes coordinated file access as ten tools:

pip install "agent-coherence[mcp]"
{
  "mcpServers": {
    "stale-write-guard-fs": {
      "command": "stale-write-guard-fs",
      "env": { "SWG_ROOT": "/path/to/shared/workspace" }
    }
  }
}

Tool

What it does

swg_read

Tracked read — registers the agent's view of the file. If the bytes on disk are not what the coordinator recorded, the read returns a stale_view deny with no version. Retry swg_reacquire + swg_read for a few seconds first, since a peer's commit may still be reaching disk; if it stays denied, the file was changed outside the coordinator (an out-of-band edit, or a commit whose disk write failed), so swg_write the reacquired content to record it

swg_write

Guarded write — a stale view or a foreign edit gets a typed stale_view deny with recover: reacquire, never a silent overwrite

swg_reacquire

Recovery — clears the stale view + mandatory fresh read after a deny

swg_write_cas

Single-shot version-checked write for concurrent same-key contention

swg_gate

Effect fence — re-checks the (version, owner_generation) pair from your swg_read right before an irreversible external action (a webhook, a deploy, an opened PR), and denies if the value moved OR the grant it was read under was reclaimed OR a peer's write-claim preempted it (which moves neither comparand — the fence also re-checks that the grant still stands)

swg_status

Three-state coordination health: on / off / unknown, plus this session's session_agent_id, the id another session names to hand it a path

swg_transfer

Hands this session's claim on one or more paths to the session whose session_agent_id it names; until the handoff ends, this session's swg_write / swg_write_cas on those paths gets a typed handed_off deny (Targeted grant handoff)

swg_accept

As the successor, accepts a handoff without writing the path

swg_decline

As the successor, declines a handoff; the giver may write the path again

swg_withdraw

As the giver, withdraws a handoff, on the user's or host's explicit instruction only

POST /hooks/effect-fence

Not a tool — the HTTP sibling of swg_gate. The coordinator answers the same fence verdict to any client that can make an HTTP request, with no MCP and no Python in the loop, and it is the only surface that can answer the no-content-claim leg. See Effect fence over HTTP

The server binds one workspace per session (SWG_ROOT, defaulting to its working directory; the whole workspace is guarded unless SWG_MANAGED — a comma-separated glob list — narrows it), rejects path traversal and any access to the coordinator's own state directory, and fails closed on IO errors. Denials come back as typed, machine-readable payloads — an agent can parse recover: reacquire and self-heal instead of retrying blindly. Run the red→green demo: python -m examples.mcp_stale_write_guard.main (offline, deterministic, no keys).

Scope, honestly. Same contract as the volume it wraps: single-host, managed paths, cooperative — it guards agents that route file access through the tools; it cannot see edits made around them (those are caught at the next tool call on that file by the foreign-edit guards).

Effect-ordering gate

Agents don't only overwrite files — they fire effects (a deploy, a PR, a shell command) computed from inputs they read earlier. If the input moved in between, the effect fires on stale state. gate() narrows that window: it captures the input's (version, ownership generation) pair at decision time, re-reads the pair at the effect boundary, and fires only if both are unchanged at that re-read — otherwise it holds the effect before it runs. The two comparands catch different failures: the version catches a peer that changed the input; the generation catches a coordinator sweep that reclaimed the grant the input was read under while the bytes never moved — a stalled agent whose lease lapsed mid-decision would otherwise fire its effect on revoked authority, with the version check none the wiser. One revocation moves neither comparand: a peer's pessimistic write-acquire takes the holder's grant with no commit behind it yet (version unmoved) and no epoch bump (that trigger is deliberately outside the bump set). The gate closes that leg by also requiring the re-read itself to be served under a standing grant — a preempted holder's re-read comes back stale, and the effect holds. That re-read is a verification read the coordinator does not re-grant, so the hold is level-triggered: a bare retry holds again until the caller reacquires. This third leg is enforced by tests (the claim ladder pins it), not TLA+ — the model-checked NoStaleAdmit covers the comparand pair.

from ccs.adapters import CoherentVolume, gate

vol = CoherentVolume(workspace_root, managed=("deploy/**",))

# fires run_deploy(plan) only if deploy/config.txt is unchanged since decide() read it;
# else raises StaleView before the deploy runs — reacquire() and re-decide.
gate(vol, "deploy/config.txt", decide=plan_deploy, effect=run_deploy)

It's plain Python, so the same call drops into a LangGraph node, a CrewAI task, or a raw script unchanged.

Three surfaces, one rule. gate() is the Python spelling of the fence. swg_gate is the MCP one, and POST /hooks/effect-fence answers the identical verdict to any client that speaks neither — same question, same classification, same hold reasons. Those reasons are a wire vocabulary, published with each one's establisher and its own recovery in the effect-fence contract. One leg is not the same everywhere: only the HTTP route can report content_claim_absent, the case where the coordinator records no content claim for the artifact at all — the Python and MCP surfaces read a hash comparison rather than the coordinator's own record, so "the claim matches" and "there is no claim" reach them as one value.

Scope, honestly. The gate orders effects, it does not roll them back: it fires pre-effect and never undoes one, so for an escaping effect there's a residual re-read→fire window it narrows but can't close. It's single-host and cooperative — the agent opts in. Both comparands are fail-closed: an unconfirmed version (degraded read) or an unconfirmed generation (a strict-mode deny, or a coordinator that reports no generation, such as one older than v0.14.0) HOLDs rather than firing. For a pure write effect, use vol.write_cas_at(path, expected_version, content) directly, which is the atomic, no-window path. Gating several mutually-consistent inputs at once is a snapshot-session operation on the coordinator, not this single-input wrapper. Run it: python -m examples.effect_gate.main (offline, deterministic, no keys), or add --baseline to see the stale fire it catches; python -m examples.gate_effect_ordering.main adds the reclaimed-lease act (version unchanged, authority revoked, deploy held).

Multi-artifact snapshot sessions

gate() protects one input. But an agent that reads several artifacts one by one — a plan, a config, a memory file — can see a torn combination: plan.md from before a peer's commit and config.json from after it. Every individual read was current; the set never coexisted (read-skew). A snapshot session closes that window: it pins a consistent cut of the artifacts you name, captured at a single point, and serves every session read from that cut while peers keep writing.

Against a running coordinator (the same one CoherentVolume spawns), over HTTP:

POST /session/begin      {session_id, read_set: ["plans/plan.md", "config/app.json"]}
                         → {session_token, cut: {path: version}, …}
POST /session/read       {session_id, session_token, path}
                         → the artifact at its PINNED version — never a newer one
POST /session/commit     {session_id, session_token, path, content}
                         → wins only if no peer moved the artifact since the cut
POST /session/heartbeat  {session_id, session_token} — keep the session's lease alive

Or in-process: CoordinatorService.begin_session(read_set=…, owner=…) → session_read(…) / session_commit(…). The cut is an inspectable {artifact: version} map, not an opaque handle — you can read exactly which versions your session is pinned to.

Fail-closed by construction: reading an artifact that was not in the pinned read-set is refused with a typed rejection — never silently served from live state. Sessions have a bounded lifetime backed by a heartbeat lease: a session whose heartbeat lapses, or that is lost to a coordinator restart, is invalidated — later reads get a typed "session invalidated" rejection telling the agent to re-establish, never a quiet fall-through to whatever is current. Model-checked: NoReadSkewWithinCut and PinAlwaysRetained (formal/tla/Snapshot.tla).

Scope, honestly. This prevents read-skew — torn reads across artifacts. It does not add write-skew prevention: commits validate per-artifact against the pinned base through the same optimistic CAS as write_cas, so two sessions that read one cut and write different artifacts can still interleave. Single coordinator, single host. When the coordinator retains version bodies it serves the pinned bytes directly; otherwise it returns the pinned version and content hash as a typed signal and the caller fetches the bytes from its own data plane.

Atomic multi-file publish

write_cas_at lands one file if it hasn't moved. But an agent often edits a set of files that must stay consistent — a plan and its manifest, a config split across files — and must land them together or not at all, never a torn pair where one file references another that already changed. atomic_publish is that all-or-nothing batch:

from ccs.adapters import CoherentVolume

vol = CoherentVolume(workspace_root, managed=("proj/**",))

# lands BOTH files iff each is still at the version the agent read; otherwise the
# WHOLE publish is held (StaleView / CasVersionConflict) with NO file written.
versions = vol.atomic_publish([
    ("proj/plan.md",     plan_version,     new_plan_bytes),
    ("proj/manifest.md", manifest_version, new_manifest_bytes),
])   # -> {"proj/plan.md": 2, "proj/manifest.md": 3}

Either every member's version advances or none does, and a moved member holds the whole batch — a torn commit is never a reachable state, formally specified as the NoPartialPublish invariant in formal/tla/AtomicPublish.tla. AtomicPublish.tla is specified and semantically validated but is not in the CI sweep — it is held out pending a bounded encoding that converges within the CI time budget, so NoPartialPublish is not among the nine invariants model-checked on every push. A single-file publish takes the direct CAS path; a multi-file publish opens a snapshot session so the versions it checks are captured at one point (no member read across a peer commit). Run it: python -m examples.atomic_publish.main (offline, deterministic, no keys), or add --baseline to see the file-by-file torn pair it prevents.

Scope, honestly. The all-or-nothing guarantee is at the coordinator commit — that is what NoPartialPublish covers. Disk materialization happens after the commit and is best-effort: every file is staged to a temp then renamed into place, so a disk fault fails before any rename (disk stays uniformly old) and a rename failing partway raises a typed PublishMaterializationError naming exactly which files landed — never a bare error implying nothing published. This shrinks, but a crash between renames can't fully eliminate, the multi-file disk window (there is no POSIX multi-file atomic rename); on that error the coordinator is ahead of disk: write each file that didn't land again with write(), using the bytes you published (a versioned read of such a file is refused until then). It is single-host and cooperative. The multi-file path also adds a small capture→commit window (the session open); a peer winning it holds the publish rather than tearing it. This is all-or-nothing publish of a file set — not rollback of effects that already escaped, and not write-skew prevention across sessions. And the staleness it detects is version drift: only writes routed through a volume advance versions, so an edit that bypasses the volume entirely (a human in an editor, a formatter, a script writing a member directly) is invisible to a multi-file publish — the batch commits and overwrites it. A single-file publish and plain write() fail closed on that same edit via the foreign-edit guards; use atomic_publish only for file sets whose every contending writer goes through a volume.

BYO substrate: coherence over the store you already run

CoherentVolume puts coherence over files on disk. But shared agent state often lives in a store you already run — a Postgres row, an S3 object. BYO-substrate bindings bring the same coherence over that store, with the coordinator holding only metadata (a version, per-agent MESI, a fixed-width content_hash, an opaque substrate token) — never the bytes:

from ccs.adapters.coherent_row import CoherentRow      # pip install "agent-coherence[coherent-row]"
from ccs.adapters.coherent_object import CoherentObject  # pip install "agent-coherence[coherent-object]"

# agent A reads a row it will edit over several steps
row = CoherentRow(dsn=..., table="workspaces", artifact_id="ws-42")
data, token = row.read("ws-42")
# ... meanwhile agent B commits a new version through the binding ...
# A's next binding-mediated read/act is DENIED before A writes:
row.commit("ws-42", expected_token=token, new_bytes=revised)  # -> StaleView; reacquire() and re-decide

The value over the substrate's own conditional write (UPDATE … WHERE version=?, S3 If-Match): a bare CAS rejects A's write at write time; the binding tells A its cached view went stale before it acts, in the same typed vocabulary a file (CoherentVolume) or a store key (CCSStore) uses — one coherence surface over a row, an object, or a file. A declarative Coherence Manifest wires each artifact to a substrate and an honest guarantee tier, with credentials as references (secret-file: / aws-default, never literals) and SSRF-constrained connection targets.

Scope, honestly. v1 ships two native-CAS bindings (Postgres + S3) and a forward-only tier for action backends (a Slack post, a Gmail send — decision-input freshness only, no CAS). The value is invalidation-before-act + cross-substrate uniformity; the read-generation fence over a substrate is a documented roadmap item, not claimed — v1 OCC writers ride admit-on-absent + the version-CAS. Single-host and cooperative; when the substrate is itself distributed (S3, managed Postgres) the no-lost-update guarantee is the substrate's and identical with or without this layer. Run it: python -m examples.coherent_row.main / examples.coherent_object.main (offline, deterministic, no keys — an in-memory substrate stand-in; production points the same binding at real Postgres / S3). Full API, per-binding least-privilege, and the honest tier table: BYO substrate bindings.

Workspace versioning & restore

Everything above keeps shared state safe while agents write it. Workspace versioning answers the after-the-fact question: an agent mutated a workspace spread across backends — files on disk, objects in S3 — the attempt failed, and you want it back, with per-member honesty about what can and cannot come back:

from ccs.adapters.workspace import WorkspaceVersioner

wv = WorkspaceVersioner(service=service, owner=owner, file_resolver=resolver)
wv.add_file_member(source, "ws/notes.md")
wv.add_object_member(binding, "reports/summary.txt")   # a CoherentObject (S3)
wv.add_forward_only_member("actions/deploy-step")      # named, not captured

checkpoint = wv.checkpoint("before-migration")         # pins on by default
report = wv.restore(checkpoint.record.checkpoint_id)   # per-member terminal truth

A checkpoint is a named manifest of native version pointers (an S3 versionId, a file's coordinator version) and content fingerprints — never a second copy of your bytes — captured as a skew-declared cut: the capture window is recorded rather than hidden, and a member that moved inside it is flagged, not silently torn. A member missing at capture is recorded as absent, so restore includes delete legs. Restore drives one conditional write per member under a termination contract, so it always concludes with a frozen per-member report — restored (the captured bytes landed over whatever was live, and the report says per member what that write discarded), converged (live state already matched), conflict (a live foreign writer won: bounded re-drive, never a livelock — that member's live state survives), target_lost (the captured version is gone — reported, never substituted), held_unconfirmed, or forward_only_skipped.

Every member carries an honest restore tier: restorable (versioned S3, backed by a legal hold in your bucket), restorable-unpinned (history exists now and may expire), or forward_only (described, not restorable — stated at capture, not discovered at restore). The durable truth is the pair (restore_tier, pin_state): (restorable, unpinned) is rendered claimed-but-not-yet-backed, never as a plain promise. Scriptable from the shell, where exit code 3 means the restore concluded — read the report:

agent-coherence-workspace checkpoint before-migration --file notes.md --file plan.md
agent-coherence-workspace status <checkpoint-id>     # (restore_tier, pin_state) per member
agent-coherence-workspace restore <checkpoint-id>    # 0 no absorbed outcome · 3 concluded with absorbed outcomes

Who may restore a checkpoint, and releasing its pins. A checkpoint records who took it, and nothing checks a restore against that: often the point is that someone else restores it. To restrict who may, name a receiver when you take it — wv.checkpoint(name, receiver=<their owner id>), or receiver_session_id on POST /workspace/checkpoint — and any other controller is refused not_the_receiver. With or without a receiver, the first controller to register a restore claims the checkpoint: any other controller is then refused already_registered, the claimant's own retries stay idempotent, and the claim is never released, so if that controller stops, resume with it or take a new checkpoint (which captures the workspace as it is now). A registration is also checked against the checkpoint itself: a path it does not describe (not_a_checkpoint_member) or bytes other than the captured ones (fingerprint_mismatch) are refused before anything changes. In-process, restore() checks the receiver and the claim before it writes anything and raises CheckpointRegistrationRefused; the CLI exits 2. wv.release_checkpoint(checkpoint_id) drops the pins a checkpoint holds — keeping any S3 hold another checkpoint in the same registry still relies on — and leaves its manifest in place. It is one-way and Python-only, and each S3 member must first be re-declared on that versioner through the binding and key that placed its hold. More in the guide: who may restore a checkpoint and dropping a pin.

Scope, honestly. Single-host coordinator — checkpoints, pins, and restore progress live in local coordinator state and make no cross-host claims. Restore is over artifacts, never effects: a file or an object comes back, a sent message does not. File members are detection-only (no-arbiter) — only a backend with a native conditional write arbitrates a racing foreign writer — and they ride coordinator retention, which offers no per-version hold: their tier stays restorable-unpinned, and a version that ages out surfaces as target_lost rather than restoring different content. Restore is a forward commit carrying old bytes: versions strictly increase, history is never rewritten. And a restore is not a merge: a member whose content moved after the capture is put back over, the later content is gone, and nothing on this path refuses that write. What you get is the record of it — per member, the version and the digest discarded, where the leg could read them — plus restore --exit-nonzero-on-discarded-content to make such a run exit non-zero after the fact. Exit 0 means no member ended in an absorbed outcome; it never meant nothing was overwritten. S3 members are captured and restored through the Python API, because their bindings carry credentials the CLI never holds. Run it: python -m examples.workspace_versioning.main (offline, deterministic, no keys), or add --baseline to see the loss first; python -m examples.session_handoff.main runs two sessions as separate processes that hand work over through a checkpoint and a restore, including the bounded conflict when the first is still writing. Full honesty model — tiers, pin states, the retention caveat, the exit-code contract: guide § Workspace versioning & restore.

Status

v0.15.0 released — targeted grant handoff, a caller principal, the effect fence over HTTP, and checkpoints that say who may restore them. A session can now hand a file to one named session: until the version moves, the successor declines or the giver withdraws, the giver's own writes to it are refused as handed_off. Handoff works from Claude Code, CoherentVolume, the MCP server (four new tools, ten in all) and HTTP. The Claude Code hook client, CoherentVolume, the MCP server and the coordinator session behind the BYO-substrate bindings now claim a caller principal for their session automatically; once one is claimed, the coordinator refuses with HTTP 400 a request that names that session without it on the routes that can change another writer's work. That keeps protocol-following clients apart; it is not a security boundary. POST /hooks/effect-fence answers the effect-fence verdict to any HTTP client, with an eighth hold reason, content_claim_absent, for an artifact whose content the coordinator never recorded. A restore registration is now checked against the checkpoint's members and captured fingerprints; a checkpoint can name the one controller allowed to restore it, and the first controller to register a restore claims it for good; a restore reports what it overwrote; and WorkspaceVersioner.release_checkpoint drops a checkpoint's pins. Operators can read from /status?detail=full when the sweep will reclaim each write grant, which grants it reclaimed and who last wrote each artifact, and every /status view now reports registry_schema_version. The Claude Code plugin's Node coordinator serves none of the new surfaces: it answers the handoff, caller-principal and effect-fence routes 404 and /status?detail=full 501, and its /status carries no registry_schema_version.

Upgrading from 0.14.1. The registry schema moves from 7 to 10 with no down-migration: once this release opens a workspace's state.db, or the workspace CLI's workspace.db, 0.14.1 refuses it. Some answers and return types change too — for example, /status reports agent_name as null below the operator tier, and write_cas() / write_cas_at() return a CasCommitResult. Upgrade the coordinator, the clients and the Claude Code plugin together, and pair this release with plugin 0.6.0 or later. The guide's Upgrading from 0.14.1 lists the breaking changes.

See CHANGELOG.md for the full version history and releases for tagged artifacts. Alpha — APIs may change before v1.0.

Paper

Token Coherence: Adapting MESI Cache Protocols to Minimize Synchronization Overhead in Multi-Agent LLM Systems arXiv:2603.15183

@article{parakhin2026token,
  title   = {Token Coherence: Adapting MESI Cache Protocols to Minimize
             Synchronization Overhead in Multi-Agent LLM Systems},
  author  = {Parakhin, Vladyslav},
  journal = {arXiv preprint arXiv:2603.15183},
  year    = {2026}
}

Community

Questions, war stories, and ideas welcome in Discussions. If you've hit a stale-read bug in a multi-agent workflow, open an issue — I'd like to hear about it.

License

Apache-2.0. See LICENSE.

Available Tools

10 tools
swg_acceptA

As the successor, accept the live handoff of a path without writing it: a pending handoff becomes completed (a write of the path completes it too). The giver stays fenced until a write moves the version. If another session has acquired the path and nothing has written it since, the handoff is overtaken but live: the accept is taken but changes nothing, answers status=overtaken and names that session as counterparty. Once a write has moved the version the handoff has ended, and the accept is refused handoff_not_live with the status saying how. Refused with handoff_not_successor or handoff_not_live, as an error that changes nothing (recover=stop_and_report, retryable=false, and a fixed next_step). A transfer fences the giver and does not reserve the path: other sessions keep reading and writing it by the ordinary rules, and the handoff only labels what they do. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only readOnlyHint=false from annotations, the description carries the behavioral load and does so richly: it explains the state transition (pending handoff becomes completed), the overtaken-but-live no-op case, the refusal codes, and the error shape (recover=stop_and_report, retryable=false, fixed next_step). It also discloses the single-host limitation and enumerates what is explicitly out of guarantee, which an agent cannot get from the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The primary action is front-loaded, which is good, but the body is a dense run-on chain of clauses joined by colons, dashes, and parentheticals that is hard to parse on first read. The closing laundry list of out-of-guarantee items is genuinely informative yet bloats an already heavy paragraph.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter mutation with no output schema, the description covers the outcomes an agent needs: success, overtaken no-op, and the two refusal codes with their error semantics and non-retryable status. The return payload structure is only gestured at via status names, but the behavioral picture is otherwise complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% and the sole parameter 'path' has no schema description, so the description must compensate. It implies the path is the one whose live handoff is being accepted, but adds no syntax, format, or resolution detail (relative vs absolute, mount scoping) beyond that implication.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: 'accept the live handoff of a path without writing it,' which pins down both the action and its non-obvious distinction from completing the handoff via a write. The core purpose is clear once parsed, but the surrounding jargon ('giver stays fenced', 'moves the version') assumes familiarity with the swg vocabulary and does not name the sibling it substitutes for (swg_write) explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete when-it-applies and when-not conditions: succeeds when a pending handoff is live, answers status=overtaken when another session has acquired but not written, and is refused handoff_not_live once a write has moved the version. It stops short of naming swg_write/swg_decline as explicit alternatives, so the routing is implied rather than spelled out.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_declineA

As the successor, decline the live handoff of a path: the handoff ends and its giver's fence lifts. Refused with handoff_not_successor or handoff_not_live, as an error that changes nothing (recover=stop_and_report, retryable=false, and a fixed next_step). A transfer fences the giver and does not reserve the path: other sessions keep reading and writing it by the ordinary rules, and the handoff only labels what they do. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

A3.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Despite only a readOnlyHint=false annotation, the description discloses the state change (handoff ends, giver's fence lifts), the two failure codes, and their semantics (recover=stop_and_report, retryable=false, fixed next_step). It also scopes guarantees (SINGLE-HOST, no detection of multi-host/mount writers, no auto-merge), which is exactly the behavioral context annotations do not supply.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core action and effect are front-loaded well, but the passage is dense and includes a long trailing guarantee-scope sentence spanning multiple caveats. The information is useful but not tightly trimmed.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and a single parameter, the description adequately covers the mutation's effect, error modes, retry semantics, and applicability limits. Only finer param semantics are missing, which is a minor gap for this tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With one parameter at 0% schema description coverage, the description carries the burden. It implies 'path' identifies the handoff being declined, giving minimal semantic grounding, but adds no format, origin, or validation detail beyond that implication.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'decline the live handoff of a path,' and clarifies the role ('as the successor') and effect ('the handoff ends and its giver's fence lifts'). It is clearly distinct from acceptance/transfer siblings, though it never names them explicitly.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the 'as the successor' framing and the refusal conditions (handoff_not_successor, handoff_not_live), which tell an agent when the call is valid. However, there is no explicit when-to-use-vs-alternative guidance, e.g. decline vs withdraw or reacquire.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_gateA

Verify a file is STILL unchanged and still under the same grant. Pass BOTH comparands from your earlier swg_read — expected_version AND expected_generation (its owner_generation). Call this immediately BEFORE any irreversible external action you decided from that read (sending a webhook, opening a PR, running a deploy, posting a message). Returns decision=proceed, or DENIES with reason=stale_view if the file moved OR the grant you read it under was reclaimed (the version alone cannot see a reclaim) OR a peer write-claim preempted your grant (which moves neither comparand — the gate re-checks that the grant still stands) OR either comparand is unconfirmed. On a deny: do NOT take the action — swg_reacquire, re-read, re-decide. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
expected_versionYes
expected_generationYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The only annotation is readOnlyHint=false, which carries almost no actionable information, so the description bears the full burden and delivers: it enumerates the distinct deny causes (stale version, reclaimed grant, peer preemption, unconfirmed comparand), states that version alone cannot detect a reclaim, and lists out-of-guarantee scenarios (multi-host, network mounts, divergent history, semantic correctness, auto-merge).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core purpose and the 'call it immediately before' instruction, and every clause carries decision-relevant content. It is dense and somewhat run-on, particularly the long parenthetical listing deny reasons, but nothing is padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, yet the description documents the return contract (decision=proceed, or DENIES with reason=stale_view), the failure-recovery path, and the tool's guarantee boundaries. An agent has everything needed to call it, interpret the result, and act correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does so for the two non-obvious integers — both comparands must come from the earlier swg_read, and expected_generation is identified as the owner_generation — which is meaning the schema cannot convey. It does not explain `path`, though that parameter is largely self-evident.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb+resource+scope: verify a file is STILL unchanged and under the same grant, using both comparands. An agent can distinguish it from siblings like swg_read (initial read) and swg_reacquire (recovery after deny) without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit when: 'Call this immediately BEFORE any irreversible external action you decided from that read', with concrete examples (webhook, PR, deploy, message). Explicit what-to-do-on-failure: 'do NOT take the action — swg_reacquire, re-read, re-decide', naming the alternative sibling.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_reacquireA

Recover from a stale_view deny: re-mint identity and return the CURRENT bytes. You MUST write FROM these exact bytes — the server enforces version lineage, NOT that your content was derived from what you read. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

A4.3/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint=false annotation, the description explains re-minting identity, version lineage enforcement, and lists out-of-guarantee scenarios. This adds significant behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense sentence with additional clarifications. It conveys essentials without excess, though it could be slightly more structured for readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's niche purpose, the description covers recovery action, behavioral rules, and reliability caveats. It is sufficient for an AI agent to use correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters1/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema has one required parameter 'path' with no description. The tool description does not explain what 'path' represents, leaving ambiguity. With 0% schema coverage, the description should compensate but fails to.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool recovers from stale_view deny by re-minting identity and returning current bytes. It distinguishes itself from siblings like swg_read and swg_write by specifying the recovery context.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit when-to-use ('recover from stale_view deny') and when-not-to-use (SINGLE-HOST only, not for divergent history). Also gives instructions to write FROM exact bytes returned, with warnings about limitations.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_readA

Read a workspace text file under coherence tracking. Returns {content, version, owner_generation}. The version is the comparand you pass to swg_write_cas; KEEP BOTH version and owner_generation and pass them to swg_gate before any irreversible external action you decide from this read (owner_generation=null means this coordinator does not report generations, so swg_gate will hold). When the path has a handoff record the result also carries handoff: the record as it concerns this session (role giver, successor or bystander, the two session_agent_ids, version_at_transfer, hold_shape, status, live); handoff_unknown=true instead means the read was denied and the record could not be fetched. The coordinator attaches the record best-effort, so a result with neither does not prove there is no record; swg_status lists every record. A sticky-INVALID view returns fresh bytes but stays INVALID — use swg_reacquire to recover before writing. If the bytes on disk are not the content at the current version, the read is DENIED with reason=stale_view and no version. A peer's commit still reaching disk clears on its own: swg_reacquire, then swg_read again, retrying for a few seconds. Only if it stays denied past that was the file changed outside the coordinator (an out-of-band edit, or a commit whose disk write failed): swg_write the content swg_reacquire returned (or your merge of it), then swg_read. A write made sooner can be overwritten by a peer commit still reaching disk. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

A4.5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations supply only readOnlyHint=false, so the description carries the load and does: DENIED with reason=stale_view and no version, sticky-INVALID views returning fresh bytes while staying invalid, handoff/ handoff_unknown semantics, the best-effort attach caveat (absence of a record does not prove none exists), and the explicit single-host scope plus a list of guarantees NOT detected in v1.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first two sentences are crisply front-loaded (purpose, return shape, next-tool routing), but the remainder is a dense multi-topic run-on covering denial handling, handoff internals, and scope caveats that an agent must parse in one block. Much of it earns its place, yet the wall-of-text structure hurts scannability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, and the description compensates fully by naming every returned field, the conditional handoff sub-record, the denial reason, and the boundary conditions of the guarantee. For a coherence-protocol tool, an agent has everything needed to interpret a result and decide the next call.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0% for the single 'path' parameter, so the description must compensate. It implies path addresses a workspace file that can be keyed to a handoff record ('when the path has a handoff record'), but gives no format, root, or resolution rules for the value, so it only partially fills the gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a concrete verb+resource+scope: 'Read a workspace text file under coherence tracking', then names the exact return shape {content, version, owner_generation}. It also positions the tool against siblings (swg_write_cas, swg_gate, swg_status, swg_reacquire), so an agent can distinguish this read from the write/CAS/gate tools without opening schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly instructs what to do with the outputs (keep version and owner_generation and pass them to swg_write_cas/swg_gate before any irreversible external action) and gives a concrete escalation path for stale_view (swg_reacquire, re-read, retry for a few seconds, and only then swg_write). It even flags the ordering hazard that an early write can be overwritten by a peer commit still reaching disk.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_statusA
Read-only

Report coherence state: coordinator on|off|unknown (unknown is NOT off), per-path enforced|not_registered (with handoff: the path's handoff record -- giver, successor, version_at_transfer, hold_shape, status, live -- when it has one; per_path is null, not {}, when the coordinator's /status answer carries no list of tracked paths, as a degraded answer does when its registry was busy: retry shortly and do not treat it as nothing tracked; the text result then says per_path=unavailable. With coordinator=unknown, per_path is {} and says nothing about what is tracked), is_attached/is_degraded/session_id, session_agent_id (this session's id as a handoff successor: the value another session passes to swg_transfer to hand this session a path; it does not change when this session reacquires, and it names this session as a successor only while principal_claim is bound), principal_claim (this session's caller-principal state: bound; unsupported = the coordinator issues none; unconfirmed = the last claim's answer was lost and the next call claims again by itself; refused = the session is bound under another nonce, so every later swg_read/swg_write/swg_gate answers the typed caller_principal_* deny with recover=restart_session while the coordinator stays on; not_attempted = nothing claimed yet), caller_principal_absent_total and caller_principal_refused_total (the coordinator's counters; null, never 0, when it is unreachable or does not report them), registry_schema_version (the schema version of the coordinator's store, an integer; an older release refuses a store at a newer version; null when the coordinator is unreachable (coordinator=unknown) or its answer carries no such field, as a coordinator built before the field does; report it to the user rather than act on it), and heterogeneous_scope_detectable=false (a multi-host or differently-scoped setup is NOT distinguishable in v1). SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint, yet the description discloses deep behavioral context far beyond them: unknown is NOT off, per_path is null rather than {} on a degraded answer, the text result becomes per_path=unavailable, coordinator=unknown yields per_path={}, null vs 0 counters, and the metadata-alias operations are modeled. This is exceptionally rich disclosure of edge cases and degraded states.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness2/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded, but the body is one enormous run-on sentence overloaded with nested parentheses and reversed clauses, making it hard to parse. Much of the detail is valuable, but the structure sacrifices readability and every clause is not weighted for importance.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description carries the full burden of explaining return values and does so thoroughly — each field, its null/absent meanings, degraded-answer behavior, and the v1 single-host limitation. An agent has enough to interpret the result correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so there is no parameter semantics to clarify; the baseline for a no-parameter tool applies. The description instead spends its detail on return-value semantics, which is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with a specific verb+resource: 'Report coherence state,' and enumerates the concrete data returned (coordinator state, per-path enforcement, session ids, principal claim, counters, registry schema version). It is clearly a read-only diagnostic, distinct from the mutation/read siblings, but it never names or contrasts with any sibling tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is embedded reaction guidance ('retry shortly and do not treat it as nothing tracked,' 'report it to the user rather than act on it'), which implies how to act on results. However, there is no explicit statement of when to call this tool versus swg_read/swg_gate/etc., or what prerequisite conditions select it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_transferA

Hand this session's claim on one or more paths -- the write grant from swg_write, or the standing read from swg_read -- to another session, the successor. Name the successor by the session_agent_id that the successor's OWN swg_status reports. Answers one grant per path, in order: transferred=true with the record (giver, successor, version_at_transfer, hold_shape, status=pending), or transferred=false with a typed reason and nothing changed (handoff_not_held: this session holds no claim on the path; handoff_successor_unknown: the coordinator does not know that id; handoff_in_flight: another session's handoff of the path is live). The result is an error unless every grant transferred, and each refused grant carries recover and retryable=false and a fixed next_step: fix_successor for a successor id that names no other session, check_handoff for handoff_not_held (a transfer that already landed answers it too), stop_and_report otherwise; the top level speaks for the most restrictive refused grant. While a handoff is live this session's swg_write and swg_write_cas on its path are denied with reason=handed_off. The fence covers this MCP session only: a write you make through any other route (your own file tools, a shell) is another session's and is not refused, so write a path you handed off by no route. If the answer is commit_unconfirmed (recover=check_handoff) the transfer may have landed: look at the path's handoff (swg_read or swg_status) first. A handoff from you to that successor made at the version you held (its version_at_transfer), live or ended, means your transfer landed: do not transfer again, and do not withdraw to start over. Transfer again only if no such handoff shows; if the path was written since or shows another session's handoff, ask your user. Once the successor has written the path, a second transfer is a new handoff. A transfer fences the giver and does not reserve the path: other sessions keep reading and writing it by the ordinary rules, and the handoff only labels what they do. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathsYes
successorYes

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only readOnlyHint=false in annotations, the description carries the behavioral burden and does so: it discloses per-path success/failure answer shapes, typed refusal reasons (handoff_not_held, handoff_successor_unknown, handoff_in_flight), retryable=false and recover values, the fencing effect on swg_write/swg_write_cas during a live handoff, and the explicit non-guarantees (SINGLE-HOST only; cross-host, network mount, divergent history, auto-merge not detected in v1).

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The purpose is front-loaded correctly, and most clauses carry real routing or recovery value, but the body reads as a dense run-on that packs multiple obligations into single sentences. Splitting the error/recovery matrix into the same structured form as the answer list would make it scannable without losing content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no output schema, the description fully specifies the return contract (transferred=true/false, record fields, typed reasons, error-on-any-refusal, top-level aggregation) plus recovery and retry semantics and scope limits. An agent has everything needed to call it and to interpret every outcome.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, so the description must compensate. It precisely defines `successor` ('the session_agent_id that the successor's OWN swg_status reports') and describes `paths` behaviorally (one grant per path, answered in order). It does not specify path format (absolute vs relative vs glob), which is the remaining gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The first sentence names a precise verb and object ('hand this session's claim on one or more paths ... to another session, the successor') and immediately identifies what is being transferred (the write grant from swg_write or the standing read from swg_read). It is unmistakably distinct from siblings like swg_withdraw and swg_accept, and the direction of the transfer is explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states when to use it, when NOT to re-use it ('do not transfer again, and do not withdraw to start over'), and how to disambiguate commit_unconfirmed by inspecting the path's handoff. It also names the specific conditions for retrying and the fallback ('ask your user'), and warns against writing a handed-off path by any route other than MCP.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_withdrawA

As the giver, withdraw this session's live handoff of a path: the handoff ends and this session's fence on the path lifts. Take it only on the user's or host's explicit instruction. It is never the recovery for the handed_off refusal: when a write of a path you handed off is refused, stop and report to your user or host. Refused with handoff_not_giver or handoff_not_live, as an error that changes nothing, whose next_step says to stop and report. A transfer fences the giver and does not reserve the path: other sessions keep reading and writing it by the ordinary rules, and the handoff only labels what they do. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With only readOnlyHint=false from annotations, the description carries the burden and delivers: it states the effect (handoff ends, fence lifts), the two refusal errors and that they 'change nothing,' that next_step says to stop and report, and the SINGLE-HOST scope plus the out-of-guarantee caveats. This is exactly the mutation-consequence and failure-mode context annotations cannot provide.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core purpose is front-loaded, which is good, but the prose is dense with internal jargon ('handed_off refusal', 'handoff_not_giver', 'fences the giver') and the trailing guarantee-disclaimer list reads as a dump. It is informative rather than concise, and parsing cost is high.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a single-parameter mutation tool with no output schema and minimal annotations, the description covers purpose, effect, error behavior, scope limits, and untested cases. The main remaining gap is that parameter format and any return/next_step schema are left implicit, but overall it is complete enough to invoke correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% for the single 'path' parameter, so the description is the only source; it contextualizes the parameter as a path previously handed off by this session but gives no format or example. For a self-evident single path argument this is adequate but does not fully compensate for the coverage gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a precise verb and resource: withdrawing 'this session's live handoff of a path,' with the concrete effect that the handoff ends and the fence lifts. It also implicitly contrasts with the sibling swg_transfer ('A transfer fences the giver and does not reserve the path'), so an agent can distinguish the two operations. It stops short of an explicit 'to hand off, use swg_transfer' routing statement.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives an explicit precondition ('Take it only on the user's or host's explicit instruction') and a clear when-not ('It is never the recovery for the handed_off refusal'), directing the agent to stop and report instead. That is strong contextual guidance, though it never names a sibling tool as the alternative action.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_writeA

Write a workspace text file (acquire -> write -> commit). DENIED with reason=stale_view if the file changed since you read it — a peer commit OR an out-of-band edit (another tool/editor): recover with swg_reacquire, then write FROM its bytes. A mid-write preempt returns reason=commit_preempted (disk may hold un-versioned bytes; reacquire_and_reconcile). SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
contentYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations indicate readOnlyHint=false, so no contradiction. Description details stale view detection, preemption, single-host limitation, and v1 limitations. Adds significant behavioral context beyond annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Lengthy but efficient; every sentence adds value. Front-loaded with core action, then details. Not overly verbose given complexity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Fully explains error conditions, recovery procedures, limitations (single-host, no auto-merge). No output schema, but return values are implicitly clear from context. No gaps for agent invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, requiring description to compensate. Description implies 'path' and 'content' via 'file' and 'bytes', but does not explicitly describe them. Since parameters are simple and purpose is clear, minimal but adequate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly states 'Write a workspace text file' with specific workflow (acquire->write->commit). Distinguishes from siblings like swg_write_cas and mentions recovery tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly says when to use (after acquire, before commit), when not (stale view, preempted), and recovery steps with alternative tools (swg_reacquire, reacquire_and_reconcile).

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

swg_write_casA

Concurrent same-key write via compare-and-set. You read (swg_read returns the version comparand; swg_reacquire does NOT — it is for swg_write recovery), MERGE, then call swg_write_cas(path, expected_version, new_content). Stale-write-rejected: if a peer committed since your read, the CAS is a TYPED CONFLICT (reason=version_mismatch, current_version returned) — NOT an auto-merge; re-read at current_version, re-merge, and retry. A win on a path with a live handoff carries handoff: outcome=completed when this session is its successor, overtaken (with counterparty) otherwise. The per-session conflict counter bounds only a COOPERATING agent (one session, stops on retryable=false); it is NOT livelock-proof against a fresh session or one that ignores retryable=false. SINGLE-HOST only. Out of guarantee and NOT detected in v1: writers on different hosts or across a synced/network mount, divergent-history reconciliation, semantic correctness, server-enforced auto-merge.

ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
new_contentYes
expected_versionYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations only declare readOnlyHint=false, so the description carries the behavioral burden, and it does so richly: typed conflict semantics (reason=version_mismatch, current_version returned) instead of auto-merge, handoff outcome behavior, single-host scoping, and an explicit out-of-guarantee list (cross-host writers, divergent history, semantic correctness). These are exactly the failure modes an agent needs before invoking a CAS write.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Content is front-loaded: protocol first, then failure semantics, then scope limits. Given the genuine complexity, nearly every clause earns its place, but the dense capitalization and stacked parentheticals make it harder to parse than necessary.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and minimal annotations, the description still covers the protocol, conflict outcomes, concurrency guarantees, and explicit non-guarantees. An agent has everything required to call it correctly and interpret a rejection.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0% with all three parameters undocumented, so the description must compensate. It explains expected_version as the version comparand returned by swg_read and new_content as the merged value, plus the conflict path that returns current_version; only 'path' is left implicit, hence not a full 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The opening sentence gives a precise verb and mechanism: 'Concurrent same-key write via compare-and-set.' It then explicitly names the sibling tools it is and is not (swg_read provides the comparand; swg_reacquire does not), letting an agent distinguish it from swg_write and swg_reacquire without opening a schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description prescribes the exact workflow ('You read..., MERGE, then call swg_write_cas(path, expected_version, new_content)') and names the alternatives with their differing semantics. It also states the recovery path on failure (stale-write -> re-read, re-merge, retry) rather than leaving the agent to infer it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv0.15.0
    • Addedswg_accept
    • Addedswg_decline
    • Addedswg_transfer
    • Addedswg_withdraw
  2. 1 tool updatev0.14.0
    • Addedswg_gate
  3. 5 tool updatesv0.12.0
    • First observedswg_reacquire
    • First observedswg_read
    • First observedswg_status
    • First observedswg_write
    • First observedswg_write_cas

TDQS

A4.2/5.0

Scored across 10 tools

Disambiguation4/5

Each tool targets a distinct role/action: read, write, CAS-write, gate, reacquire, status, and the handoff lifecycle (transfer/accept/decline/withdraw). The two write tools (swg_write vs swg_write_cas) are the most likely to be confused, and the densely overlapping vocabulary around handoffs adds some cognitive load, but the descriptions do draw clear boundaries.

Naming Consistency5/5

Every tool uses the same swg_ prefix followed by a consistent snake_case verb or verb_noun form (swg_read, swg_write, swg_write_cas, swg_transfer, swg_accept, swg_decline, swg_withdraw, swg_reacquire, swg_gate, swg_status). No mixing of conventions or casing styles.

Tool Count5/5

Ten tools is well-scoped for a stale-write-guard/coherence domain. Each tool covers a necessary operation (core read/write/CAS/gate/reacquire plus the full handoff lifecycle and status) without redundancy.

Completeness4/5

The surface covers the core lifecycle: read, write, CAS, pre-action gate, stale-view recovery, status, and the complete transfer/accept/decline/withdraw handoff flow. There is no explicit delete or path-enumeration tool, but swg_status partially surfaces tracked paths, so gaps are minor.

Maintenance

ActivityActive
ResponsivenessWithin a week

Related MCP Connectors

Related MCP Servers