Skip to main content

CausalMesh

License: MIT Python 3.11+

A research-grade experimental platform for separating information from enforcement in distributed LLM coding agents.


Install

pip install causalmesh               # SDK, policy gate, CLI, offline verifier
pip install 'causalmesh[server]'     # adds `causalmesh serve`

causalmesh init                      # write a starter causalmesh.yaml (one role, agent_a)
causalmesh policy check --agent agent_a --kind path --name src/app.py

Put the gate in front of your own agent's tools:

from causalmesh import CausalMesh, Tool

mesh = CausalMesh.from_config("causalmesh.yaml")
mesh.register_tool(Tool(name="write_file", handler=write_file, mutating=True,
                        resource_args=["path"]))
result = mesh.execute(agent_id="agent_a", tool="write_file",
                      arguments={"path": "src/app.py", "content": "..."})

A refused call never reaches the handler, and the refusal is signed evidence that causalmesh verify checks offline. The full local journey, with no key and no model, is examples/cli_demo/.

The experiment harness (four arms, the task suite, experiment run) is not in the wheel: git clone the repository for it. Pre-alpha. Every run so far used a deterministic mock agent; nothing here has been run against a hosted model, so nothing here says how real coding agents behave. See docs/backlog.md.


The research question

How much of the coordination benefit in distributed LLM coding agents comes from information, and how much comes from enforcement?

When multiple coding agents work on a shared codebase, they interfere: they make stale reads, duplicate each other's work, and overwrite each other's changes. Systems that fix this usually do two things at once — they tell agents what peers are doing, and they prevent agents from conflicting. Because these arrive together, their individual contributions are unknown.

CausalMesh separates them experimentally. The contribution is measurement, not the invention of a new coordination mechanism.

Experimental design

Four arms, manipulating exactly two factors:

Arm Information Enforcement Description
C0 none none Fully isolated agents, no peer visibility
C1 unbounded transcript none Agents see a full conversational transcript
C2 bounded structured metadata none Agents see typed events describing what changed
C3 bounded structured metadata write claims Metadata plus enforced exclusive write claims

The contrasts that answer the question:

C0 → C2    information benefit
C1 → C2    information representation / coordination cost
C2 → C3    enforcement benefit
C1 → C3    complete coordinated system

Everything other than information and enforcement is held constant: task, model, decoding parameters, agent roles, repository fixture, tool semantics, success criteria, randomization protocol. Anything that cannot be held constant is recorded and treated as a potential confound.

Architecture

                         ┌───────────────────────────┐
                         │       Experiment DB       │
                         └─────────────┬─────────────┘
                                       │
                         ┌─────────────▼─────────────┐
                         │    Experiment Scheduler   │
                         └─────────────┬─────────────┘
              ┌────────────────────────┼────────────────────────┐
        ┌─────▼─────┐            ┌─────▼─────┐            ┌─────▼─────┐
        │  Agent A  │            │  Agent B  │            │  Agent N  │
        └─────┬─────┘            └─────┬─────┘            └─────┬─────┘
              └───────────────┬────────┴───────────────┬────────┘
                              │  Lifecycle Hooks
                     ┌────────▼────────┐
                     │  Coordination   │
                     │    Boundary     │
                     └────────┬────────┘
                        ┌─────▼─────┐
                        │   Relay   │
                        └─────┬─────┘
              ┌───────────────┼────────────────┐
        ┌─────▼─────┐   ┌─────▼─────┐    ┌─────▼─────┐
        │ Event Log │   │  Claims   │    │  Fan-out  │
        └─────┬─────┘   └─────┬─────┘    └─────┬─────┘
              └───────────────┼────────────────┘
                       ┌──────▼──────┐
                       │   Observer  │
                       └──────┬──────┘
                      ┌───────▼────────┐
                      │ Offline Verify │
                      └───────┬────────┘
                     ┌────────▼─────────┐
                     │ Research Dataset │
                     └────────┬─────────┘
                     ┌────────▼─────────┐
                     │ Analysis Engine  │
                     └────────┬─────────┘
               ┌──────────────┼──────────────┐
             plots          tables          report

Dependencies run one way only. Analysis never mutates runtime state, the verifier never depends on the relay being online, and the observer is strictly read-only.

Measurement integrity

Every coordination event is appended to a cryptographic chain:

h_i = SHA256( h_(i-1) || canonical(event_i) )

Each event is signed with a per-actor Ed25519 key, so attribution is verifiable rather than asserted. A standalone offline verifier validates the chain from nothing but the log, the public keys, and optional anchors — the relay can be completely offline.

The adversarial battery tests what the chain actually detects:

Attack Expected result
Event mutation detected
Event insertion detected
Event deletion detected
Event reordering detected
Signature substitution detected
Wrong-actor attribution detected
Duplicate replay detected
Suffix truncation undetected without an anchor, detected with one

That last row is a real limitation, documented rather than hidden.

Status

Substrate complete and exercised; statistics and reporting are not built. On 2026-09-30, pytest --collect-only found 1751 tests. On 2026-10-12, python scripts/check.py (lint, format, types on the enforced modules, the security scan and the whole suite) passed on one Windows 11, Python 3.13.5 machine: 2918 passed, 0 failed, 15 skipped. The skips need a POSIX file system, an optional package such as CrewAI, or an API key. That is one machine on one day, and there is no CI to corroborate it: the workflow was removed rather than paid for (see docs/backlog.md). Linux and macOS have not been run. Re-run python scripts/check.py rather than trust this line.

The build follows a vertical slice first: a single scenario driven end-to-end through every subsystem, executed under all four arms, before breadth is added. No subsystem is considered complete until it participates in a real executable path.

Subsystem State
Event protocol, canonicalization ✅ property-tested
Ed25519 identity, hash chain ✅
Offline verifier + adversarial battery ✅ 8 attack classes
Claim registry, leases ✅ exclusivity tested across threads and processes on one filesystem; no fencing, no automatic renewal, no multi-path acquire (see backlog)
Relay: sequence, sign, chain, fan-out ✅
Isolation, 11 tools, lifecycle hooks ✅ leakage-tested
Channels, relevance, information window ✅
Script-driven agent ✅
Task system + empirical §37 gate ✅ 4 authored tasks, one per §35 family; verdicts come from scripted mock agents
Scheduler, state machine, run index ✅ resume-tested
Model adapters (OpenAI, Anthropic, local) ✅ tested over real HTTP against a local stand-in server; no hosted model has been called
Policy plane (contract, gate, ledger), checkpoints, identity keys ✅ unit and integration tested
Developer SDK, tool gateway, causalmesh serve (FastAPI) ✅ tested; Docker path re-verified 2026-09-30
Framework adapters: Generic, OpenAI function calling, Anthropic tool_use, LangChain, LangGraph, CrewAI ✅ tested with scripted payloads and fakes; no hosted model has driven any of them, and CrewAI has not run against the real package
Metrics (cost accounting, four contrasts), provenance, manifests, CLI ✅ descriptive only
Multi-rater kappa (causalmesh agreement), CooperBench loader ✅ the loader imports all 652 real CooperBench pairs (the real §37 gate on 3 pairs: NOT SENSITIVE, mock agents); the kappa code is tested on synthetic fixtures only and no human has coded a run
Statistics (inference), analytical module, reporting ⬜ not built (src/causalmesh/reporting is empty; analysis/ holds only agreement.py)

Not yet done, and stated plainly: no hosted model has been run (no key was available, and spending money is the operator's decision), no human rater has coded a run, no real CooperBench task has been run with anything but a mock agent replaying gold patches, and there is no inferential statistics or reporting layer. See docs/backlog.md, which records every gap as a tracked item with its reason.

What is and is not enforced today (2026-10-12; per-claim evidence in docs/claims-ledger.md):

  • Ownership refusals: the example Claude Code hook refuses Write and Edit on a path another role owns. It has been run only against synthesized input, never a real claude session.
  • Lease (claim) refusals: SDK sessions given claims=, and experiment runs without a causalmesh.yaml (in contract-gated runs the lease key does not match; see backlog), on one filesystem. The example hook does not consult leases.
  • Not covered: Bash, MultiEdit, NotebookEdit; agents on different machines; fencing tokens and automatic lease renewal (a lease that lapses mid-write is not stopped).
  • Evidence: a deterministic mock agent only, on Windows only, with no CI.

Measured results

A real pilot collection — 2 tasks × 4 arms × 3 repetitions = 24 runs, executed on this machine, 150 s wall clock, by a deterministic mock agent (no language model was involved). Nothing below is illustrative. The raw runs are not tracked in git; data/manifests/pilot_landing.manifest.json records 24 artifact hashes, but causalmesh audit cannot re-check them from a fresh checkout, and 35 tests skip for that reason.

Arm Success Mean coordination bytes Denials Divergences Chains verified
C0 0/6 0 0 6 6/6
C1 6/6 361 0 0 6/6
C2 6/6 196 0 0 6/6
C3 3/6 196 3 0 6/6

Reading it:

  • C0 fails every run. Isolated agents make exactly the stale-read and clobber errors the tasks induce. Zero coordination bytes, and a single unique context digest across all turns — proof that nothing entered, not an assertion that nothing should have.
  • Information alone recovers everything on both tasks. C1 and C2 are 6/6.
  • C1 costs 84% more than C2 for identical outcomes. That is the representational overhead, measured.
  • C2 and C3 spend identically (196 bytes), as they must: they differ by enforcement alone, and any information difference would confound the contrast.
  • C3 splits 3/3 and 0/3. Where ownership is disjoint it matches C2. Where the path is genuinely contested, enforcement prevents the clobber and blocks legitimate work — the file needs two writers. That is §74's trade-off measured rather than assumed to be an improvement.
  • All 24 chains verify offline, rebuilt from each run's derived seed.

One pilot estimates a direction, not an effect size. The confirmatory design (1920 runs) is built and gated: it commits to a preregistered analysis this project has not written.

📋 Implementation plan

→ docs/PLAN.md — the full implementation plan.

It covers the architecture, the honest security model, the 17-step build sequence, the verification procedure, and the explicit non-goals of the current slice.

Usage

To integrate CausalMesh into an existing AI coding agent, use the SDK to register your tools and route executions through the policy gateway.

Security-sensitive resources should be explicitly declared rather than inferred from arbitrary tool arguments. CausalMesh extracts them deterministically.

from causalmesh import CausalMesh, Tool

# 1. Load the policy contract and initialize the mesh
mesh = CausalMesh.from_config("causalmesh.yaml")

# 2. Register your agent's tools
mesh.register_tool(
    Tool(
        name="write_file",
        handler=my_agent.write_file_handler,
        kind="filesystem",
        mutating=True,
        resource_args=["path"],  # Explicitly declare protected resources
    )
)

# 3. Route actions through the Mesh
result = mesh.execute(
    agent_id="coding-agent",
    tool="write_file",
    arguments={"path": "src/main.py", "content": "print('hello')"}
)

if result.allowed:
    print(f"Action succeeded! Return value: {result.value}")
else:
    print(f"Action denied: {result.describe()}")

Tools are gated unless registered with mutating=False. Only agents declared under roles (or passed as agents=) can call execute; any other agent raises UnknownAgentError before anything runs, because its calls could not be signed. Only arm C3 refuses. On C0-C2 a call C3 would refuse runs and reports result.would_refuse. Pass seed= or declare identity.keys for a reproducible signed chain. docs/backlog.md lists what the SDK does not do yet.

Adapters put the same gate in front of a framework's tool loop: OpenAI function calling, Anthropic tool_use, LangChain, LangGraph and CrewAI, all through CausalMesh.execute. They are tested with scripted payloads and fakes; no hosted model has driven any of them, and CrewAI has not run against the real package.

from causalmesh.integrations import AnthropicAdapter

adapter = AnthropicAdapter(mesh, agent_id="coding-agent")
# pass adapter.get_tools() as `tools=` to the Messages API, then answer the
# model's tool_use blocks:
reply = adapter.handle_message(assistant_message)   # a user message, or None

mesh.export("evidence/") writes run.json and keyring.json so that causalmesh verify --run evidence/ can check the session; causalmesh serve --tools loads your tool registrations into the HTTP server, and --ssl-certfile/--ssl-keyfile serve it over TLS (see docs/security.md for what each does and does not protect).

For async agents, use await mesh.execute_async(...) to ensure non-blocking execution while maintaining policy enforcement boundaries. A denied async action is never awaited.

See examples/phase1_basic_agent.py and examples/phase1_async_agent.py for complete working examples.

To see the whole path locally (contract, gateway, an allowed call, a denied call whose handler never runs, and evidence you can verify), follow examples/cli_demo/. It needs no API key and no model, and python examples/cli_demo/run_demo.py runs it unattended. It is a demonstration of a pre-alpha SDK. It was written before causalmesh serve --tools, TLS and CausalMesh.export existed, so it serves its tool from a short script and holds its evidence in memory.

Run the suite with pytest. The slow integration tests execute real subprocesses; pytest -m "not slow" skips them.

The CLI

Install the package, then run a collection and verify what it produced:

pip install -e .

causalmesh doctor                           # environment health check
causalmesh task list                        # the task suite
causalmesh task validate                    # structural validation (§36)
causalmesh experiment plan --profile smoke  # a run schedule, executed nothing
causalmesh experiment run  --profile smoke  # execute; resumable and idempotent
causalmesh experiment status --latest       # per-arm outcomes
causalmesh verify --all                     # offline chain verification
causalmesh analyze --latest                 # costs and the four contrasts
causalmesh manifest write --latest          # SHA-256 over every artifact
causalmesh audit --all                      # evidence present, unchanged, valid
causalmesh watch --latest                   # live view of a collection
causalmesh connectors list                  # what this machine actually has

causalmesh policy init                      # write a starter causalmesh.yaml
causalmesh policy show                      # the contract, as the gate reads it
causalmesh policy check --agent buyer \
    --kind spend --name evil.io --amount 2.40

The buyer example refuses only once the buyer role in the starter contract is uncommented; the contract states exceptions, so an agent it does not name is unrestricted and the check answers ALLOW.

policy check asks the gate whether an action would be allowed, without running an agent or spending anything, and exits 0 for allowed and 1 for refused. It records nothing: a denial count that included questions would measure how often the gate was consulted rather than how much work was blocked. See docs/policy.md.

watch is read-only: it opens the run index mode=ro, so attaching it to a collection executing in another terminal cannot interfere with it. That is possible because progress lives in the index rather than in the executing process — the same property that makes experiment run resumable.

Every command takes --json for scripting, and exits 0 on success, 1 on a negative result, 2 on a bad invocation and 3 when it could not run — so a caller can branch without parsing output.

causalmesh verify --run <directory> verifies a single exported run with no repository and no relay present, which is the §82 clean-room check.

analyze is descriptive: it reports what the runs did, per arm and per task, and makes no claim that a difference is distinguishable from noise. The statistics layer that would support such a claim is not built.

audit is what makes the traceability commitment checkable rather than asserted: it confirms every recorded artifact is present and unchanged, and re-verifies every chain rather than trusting the manifest — a manifest proves the bytes have not changed, not that they were ever valid.

Manifests are tracked in git while data/raw/ is not, so a published figure stays traceable to its run after the raw directory is gone.

Connecting real coding agents

causalmesh connectors list probes this machine for installed agents and infrastructure. Every probe invokes the tool rather than trusting PATH, and reports one of three outcomes — available, degraded (installed, executable, not answering) or absent.

This build includes headless drivers for claude and codex; neither has been run against a real hosted model yet (see Status). A run driven by a real agent is stamped reproducible: false in its artifact: hosted models are nondeterministic and the agent's own version moves under it. The chain still signs and verifies — it records what happened, which is a different property from being repeatable. Such a run also costs money, so nothing runs an agent unless you name it.

Not yet built: report. The analysis and reporting subsystems are empty — causalmesh doctor says so, and docs/backlog.md records why.

Design commitments

  • No fabricated results. Ever. Not results, not benchmarks, not model outputs.
  • Raw data is immutable. Once written, data/raw/ is never modified by analysis.
  • Failed runs are evidence. They are recorded with full context, never silently discarded.
  • No overclaiming. Security properties are stated only where a passing test establishes them; the threat model documents what is not protected.
  • Full traceability. Every reported number traces from the claim, through the analysis, through processed data, through the run, through the signed event chain, back to the exact task, configuration, model and commit that produced it.

Repository layout

configs/      experiment, agent, model, relay, claim and task configuration
src/          the causalmesh package
tasks/        declarative task definitions
fixtures/     repository fixtures used by tasks
data/         raw (immutable), processed, manifests, exports
artifacts/    chains, metrics, plots, tables, reports, certificates
tests/        unit, integration, property, adversarial, failure, reproducibility
docs/         architecture, protocol, events, claims, security, analysis
frontend/     the website: landing page and documentation (Next.js)

Documentation

Document What it covers
docs/IDEA.md What this is and why, for a deployment reader
docs/PLAN.md The full build sequence
docs/security.md What the hash chain and signatures establish, and what they do not
docs/isolation.md The three-layer isolation model and its limits
docs/adapters.md Model adapters, and what has not been executed
docs/scheduler.md The run loop, state machine and randomization
docs/claims.md Write leases and enforcement
docs/policy.md The contract, the gate, and resources of every kind
docs/FRAMEWORK-PLAN.md Where this is going, and the research behind it
docs/tasks.md Task definitions and the §37 sensitivity gate
docs/backlog.md Every known gap, with its reason

Vercel deployment

The website is a single Next.js application in frontend/: the landing page is served at / and the documentation at /docs. Import the repository with the root directory left as .; the root vercel.json points Vercel at that app.

Vercel detects the framework by looking for next in the root package.json, which in a workspace layout is not where the dependency actually lives. It is declared in both: the real one in frontend/, and a matching entry at the root so detection succeeds. Keep the two versions in step — a stale root entry makes Vercel resolve a version the app does not use.

npm install
npm run dev      # http://localhost:3000 — landing at /, docs at /docs
npm run build

The landing page and the docs were previously two separate applications stitched together by Vercel routing rules. They are now one build, which is why no route configuration is needed to move between them. Use Node.js 20 or newer.

Requirements

Python 3.11+ and Git. The analysis stack uses numpy, scipy, pandas, statsmodels and matplotlib; the runtime core deliberately stays lighter, leaning on the standard library and cryptography for Ed25519. No Kafka, Redis, Kubernetes, Prometheus or Grafana.

License

MIT — see LICENSE.

Metadata

Release files for causalmesh 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for causalmesh 0.1.0
File Size Uploaded
causalmesh-0.1.0.tar.gz 934.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for causalmesh 0.1.0
File Interpreter ABI Platform
causalmesh-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 1.4 MB

Release files / causalmesh-0.1.0.tar.gz

Download URL causalmesh-0.1.0.tar.gz
Size 934.8 kB
Tags Source
SHA-256 checksum
How to use checksums
f86a5b94da26523e4673184696e17d21dbce764144a7a9e2e5c9c54b58deb2ee
BLAKE2b-256 checksum
How to use checksums
8abc743d10a37b8897b3b3d0e48e86b5fa251d4aea1908290e5c172646776ecb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / causalmesh-0.1.0-py3-none-any.whl

Download URL causalmesh-0.1.0-py3-none-any.whl
Size 443.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
d7cad4bec24113580871f744c65e8baa8d65cda3dd6d1e1e026f221edfeb622e
BLAKE2b-256 checksum
How to use checksums
0054aebac4aa99aae37d1443f7cc267462993d42de6f6ee174665d3f4a8dbcb2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page