Metadata-Version: 2.4
Name: causalmesh
Version: 0.1.0
Summary: An experimental platform separating information from enforcement in distributed LLM coding agents
Author: Sagexd08
License-Expression: MIT
Project-URL: Homepage, https://github.com/Sagexd08/CausalMesh
Project-URL: Repository, https://github.com/Sagexd08/CausalMesh
Project-URL: Source, https://github.com/Sagexd08/CausalMesh
Project-URL: Issues, https://github.com/Sagexd08/CausalMesh/issues
Project-URL: Changelog, https://github.com/Sagexd08/CausalMesh/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/Sagexd08/CausalMesh/tree/main/docs
Keywords: multi-agent,coordination,llm-agents,policy,audit,evidence,reproducible-research,experimental-platform
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=50.0
Requires-Dist: PyYAML>=6.0.2
Requires-Dist: typer>=0.16
Requires-Dist: click>=8.3.3
Requires-Dist: rich>=13.0
Provides-Extra: analysis
Requires-Dist: numpy>=1.26; python_version < "3.13" and extra == "analysis"
Requires-Dist: numpy>=2.1; python_version >= "3.13" and extra == "analysis"
Requires-Dist: scipy>=1.11.2; python_version < "3.13" and extra == "analysis"
Requires-Dist: scipy>=1.14.1; python_version >= "3.13" and extra == "analysis"
Requires-Dist: pandas>=2.1.1; python_version < "3.13" and extra == "analysis"
Requires-Dist: pandas>=2.2.3; python_version >= "3.13" and extra == "analysis"
Requires-Dist: statsmodels>=0.14; python_version < "3.13" and extra == "analysis"
Requires-Dist: statsmodels>=0.14.4; python_version >= "3.13" and extra == "analysis"
Requires-Dist: matplotlib>=3.8; python_version < "3.13" and extra == "analysis"
Requires-Dist: matplotlib>=3.9.2; python_version >= "3.13" and extra == "analysis"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: pytest-timeout>=2.2; extra == "dev"
Requires-Dist: pytest-xdist>=3.5; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: hypothesis>=6.100; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: httpx2>=2.0; extra == "dev"
Requires-Dist: ruff==0.9.6; extra == "dev"
Requires-Dist: mypy==1.14.1; extra == "dev"
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
Requires-Dist: bandit[toml]>=1.7; extra == "dev"
Provides-Extra: server
Requires-Dist: fastapi>=0.133; extra == "server"
Requires-Dist: starlette>=1.3.1; extra == "server"
Requires-Dist: pydantic>=2.4; extra == "server"
Requires-Dist: uvicorn>=0.29; extra == "server"
Provides-Extra: langchain
Requires-Dist: langchain-core>=1.3.3; extra == "langchain"
Dynamic: license-file

# CausalMesh

[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)

**A research-grade experimental platform for separating _information_ from _enforcement_ in distributed LLM coding agents.**

---

## Install

```bash
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:

```python
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/`](examples/cli_demo/README.md).

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`](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`](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`](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`](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.

```python
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.

```python
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`](examples/phase1_basic_agent.py) and [`examples/phase1_async_agent.py`](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/`](examples/cli_demo/README.md). 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:

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

## Vercel deployment

The website is a single Next.js application in [`frontend/`](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`](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.

```bash
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](LICENSE).
