Cohesion

Health check proving an AI agent's beliefs are self-contradictory — before it moves your money.

Cohesion

Created At

ETHGlobal Lisbon 2026

Project Description

Autonomous agents are starting to move real money. Before one does, you want to know something basic: are its beliefs even self-consistent?

Hardware attestation — like 0G's TEE-sealed inference — proves who spoke: that this exact model produced this exact output, unaltered. It says nothing about whether what was said hangs together. An agent can be perfectly honest, running unmodified, and still hold beliefs that describe a world which cannot exist.

Cohesion measures that gap.

When an agent is about to execute a swap, Cohesion builds a probe triangle around the pair — e.g., for a WETH→USDC swap, it auto-selects a third asset (say WBTC) to close the triangular cycle WETH/USDC → USDC/WBTC → WBTC/WETH.

Every of such pair - can move up or down. But, because these three ratios multiply to exactly 1, arithmetic forces a constraint: all three of these can't move the same direction (up or down). That is why the count of disagreeing pairs, in terms of their direction, equals exactly 2 all the time, any coherent forecaster's expected number of disagreeing pairs — the sum P(A≠B) + P(B≠C) + P(A≠C) — must be exactly 2.0, regardless of how uncertain they .

Cohesion then asks the agent to forecast the three ratios in three separate, isolated conversations — so it never sees what it said elsewhere, exactly how multi-agent pipelines actually fragment reasoning. If those three answers cannot be combined nicely into any single joint probability distribution, the agent's beliefs are provably incoherent. The headline metric is incoherence measured in % — how much do we fail to make a joint probability be consistent. The verdict gates the swap: incoherent too much, and the trade is blocked before capital moves.

What's honest about this. Coherence is necessary, not sufficient. A coherent agent can still be wrong — we detect self-contradiction, not incorrectness. Like a compiler type-check: passing doesn't prove your program correct, but failing proves it's broken. We validated the core phenomenon across repeated runs (models reliably place 3–9% of belief mass on impossible worlds when contexts are separated, yielding $6–17 arbirtrage per $100).

We have not established that incoherence predicts profit or loss, and an early test of crude oracle spoofing moved the metric the wrong direction — consistent lies get coherently believed. Those limits are documented rather than papered over.

How it could be used in practice?

  • Silent-degradation canary: detect if a privately-hosted model "quietly" gets worse — no ground truth needed.
  • Healthcheck: alert when a deployed agent's incoherence drifts after a model or real-world data change.
  • Pre-execution hook: block the trade before it fires if the agent's beliefs contradict.
  • Coherence certificate: vaults and DAOs require a signed, fresh proof before accepting agent transactions.

The mathematical basis is de Finetti's 1931 Dutch book theorem and the sheaf-theoretic contextuality framework of Abramsky–Brandenburger — the same machinery used to analyze Bell inequalities in quantum foundations, applied to AI agent beliefs.

How it's Made

Stack: Python throughout — FastAPI with Server-Sent Events for the streaming dashboard, scipy.optimize.linprog for the coherence LP, scipy.stats.t for confidence intervals, web3.py for contract calls, and a stdio MCP server exposing the same engine as a callable tool. Frontend is plain HTML/CSS/JS with no build step, consuming the SSE stream directly.

The core: a linear program over possible worlds. Three binary propositions give 8 combinations, but the price identity forbids two of them (all-rise and all-fall are arithmetically impossible), leaving 6 possible worlds. The LP asks: does there exist a probability distribution over those 6 worlds whose pairwise marginals match what the agent actually said? We maximize total assignable mass subject to marginal constraints; if the optimum is below 1.0, the shortfall is the incoherence — belief mass that cannot live in any consistent world. The Dutch book falls directly out of the LP's infeasibility.

The Graph supplies the live data the entire constraint depends on. We query the Uniswap v3 subgraph via the decentralized gateway for token0Price/token1Price, feeTier, liquidity, totalValueLockedUSD, and poolDayDatas across the three probe pools. Crucially, this isn't decoration: without live pool prices there are no propositions to elicit beliefs about. We also use TVL to auto-select the third leg of the probe triangle. A guard rejects the run if the three ratios don't multiply to within 1% of 1.0 — that check certifies the triangle is currently arbitrage-tight, which is what makes the constraint valid on independently-priced AMM pools rather than clean cross-rates.

Uniswap turns an abstract check into a gated trade. We quote each leg through QuoterV2 via staticCall — mandatory, since the Quoter is non-view by design and reverts to return its data, which is the single most common integration mistake. The quote is what makes the veto concrete: a real, executable route with real amounts and fees, blocked at the Execute button.

0G Compute runs every belief elicitation. Rather than pulling in the TypeScript SDK, we point the standard openai Python SDK at 0G's OpenAI-compatible Compute Router (base_url), which let us keep the entire stack in Python and reuse our existing elicitation code unchanged. Each response's attestation metadata is captured and surfaced per-context in the UI. This is what makes the incoherence proof unforgeable — without it, a skeptic could reasonably say we fabricated the agent's answers.

Hacky bits worth mentioning:

Subprocess-per-context isolation. The measurement only means anything if the agent genuinely cannot see its other answers. Rather than trusting conversation-history management, we spawn each elicitation as a fully independent inference call — fresh process, fresh context window, provably no leakage. Nine of them run concurrently via ThreadPoolExecutor, which is what keeps a full run under ~45 seconds.

A leak bug that became a feature. Our first grounded run showed incoherence decreasing (0.68×). Tracing it, the market-data block was showing all three ratios to every context — letting the model infer the triangle constraint and enforce consistency it wouldn't otherwise have. We now slice the data block per-context so each conversation sees only its own two ratios. That bug is also the cleanest proof the phenomenon is real: give the model the whole picture and it becomes coherent; fragment it and it doesn't.

t-intervals, not 2×SE. With only 3 reps per context, using a flat 2-standard-error threshold would overclaim confidence — exactly the sin the interval exists to prevent. We use t_{0.975, df} (4.30 at reps=3, 2.31 at reps=9), which makes low-rep runs appropriately conservative. The system reports "no significant violation at this confidence" rather than forcing a verdict, and refuses to issue a veto at all below 3 reps where no variance estimate exists.

No mocked-data fallback, anywhere. If The Graph, 0G, or the quote is unavailable, the run fails visibly. A cached-price fallback would have been trivial and would have quietly invalidated every claim the demo makes.

Same engine, two surfaces. The dashboard and the MCP server call identical code paths, so coherence_check(pair, amount, reps, model) returns the same verdict object a human sees — meaning any MCP-capable agent can gate its own trades on it. In the demo, Claude Code runs with both Uniswap's MCP tooling and ours registered, and vetoes its own swap mid-conversation.

Two things to fix after building: the […]-style specifics (which 0G model we actually probed, real measured numbers from the final runs), and whether the Uniswap Trading API key arrived in time — if it did, add a line about POST /v1/quote alongside QuoterV2.

background image mobile

Join the mailing list

Get the latest news and updates