the intent plane — fail-closed authorization

Agents propose.
The gate disposes.
The record proves it — no trust required.

Before an AI agent submits a regulatory report, moves money, or triggers anything irreversible, it must declare the intent. A deterministic gate authorizes it against a policy a human signed — or refuses. Every decision leaves exactly one durable record that a third party can re-check. The worst case is an action that wrongly waits, never one that wrongly executes.

with the plane

Authorization sits outside the agent: the rules arrive only in a spec a human signed, anything the gate cannot evaluate is a no, and the record can be re-checked by someone who trusts nothing.

What it refuses: anything it cannot evaluate. Missing data, an unsigned or revoked spec, an empty rule set, a duplicate action — all deny.

It attaches at the tool-call seam, not in the prompt — a regulatory-report submission the plane never reads, a LangChain tool wrapped in one call, a FastMCP server you own or one you don't, fronted unchanged.

cdmreporting · stdlib langchainlangchain-core fastmcpmcp · 3.4.7
where it lands first — finos cdm

A WorkflowStep in.
The decision back, as a WorkflowStep.

Agents are entering post-trade workflows, and the first irreversible thing they will do is submit. gate_cdm_event takes one CDM WorkflowStep — a plain dict in 7.1.0's JSON shape — declares the submission to the gate, and returns the outcome as a step your stack already stores: ACHIEVED with its business event, or the refusal and its reason. The plane never reads the report itself: a submission is keyed by what it is — entity, trade, action, rule set — never by what it says.

01Same report, new numbers — refused

A resubmission with different bytes is still the same report, refused before it fires. That is the duplicate a trade repository will not reject for you — and exactly what a retry after a timeout produces.

02Says one thing, proposes another — refused

Pass your own qualifier and a step whose declared intent is not what its instruction qualifies as declares nothing at all. The check runs before anything is declared — an agent cannot label its way past the gate.

03A doorway, not a dependency

The bridge is one edge module that imports no CDM package. The kernel underneath carries no financial vocabulary — pinned by tests — and the same gate fronts payments, LangChain tools, and MCP servers below.

Honestly scoped. The action-type table is EMIR-Refit-shaped and stated as the author's reading — verify it against your current validation rules before any production claim. Identity fields are keyed verbatim; case normalization is the operator's, upstream.

the reference application — proof on payments

The same gate, pointed at a real consequence: treasury payments.

The kernel knows no domain — criteria, thresholds, and what counts as a duplicate arrive only through the signed spec. Pointed at treasury payments in the testing monorepo, the loop closes: every settlement carries three hashes joining the runtime trace back to the exact signed policy that authorized it.

A duplicate of an irreversible payment isn't caught.
It's impossible — refused at the dispatch edge, by construction.

The worked example the monorepo runs end to end: the idempotency key is a declared, first-class gate criterion — required, and reserved at the dispatch edge. The same declared key then keys the settlement ledger, so at-most-once holds end to end.

No production claim. A working authorization plane with proven invariants, not a deployed payment system. Everything else on this page is a fact of the intent-plane repo, reproducible from its test suites; the treasury facts reproduce from the monorepo's quickstart. No metric on this page is invented.

the attach point — langchain · mcp · reporting

Gate the call, four ways.
The tool body runs only on a fresh Proceed.

The gate sits at the tool-call seam, not in the prompt — it is not an instruction an agent can be talked out of. Embed it once at the framework layer and every agent inherits it. Three adapters ship pre-wired: a LangChain tool wrapper (langchain-core), an MCP middleware with a proxy front end (fastmcp, probed on 3.4.7), and a stdlib regulatory-reporting adapter with a dict-shaped CDM WorkflowStep bridge.

on anything that isn't a grant

Everything that is not a fresh Proceed is a classified refusal — the body is never called. LangChain and the reporting adapter raise IntentRefused; MCP carries the same classification in the ToolError message as literal class= / terminal= / reason= / retry_safe= substrings. And the key derivation is engineered against forks — one action spelled two ways — with the four spellings that once double-executed against a live fastmcp 3.4.7 server now pinned by tests.

Optional by construction. Only the LangChain and MCP modules leave pydeclarant's stdlib-only rule, and each adapter's tests skip visibly without its framework. There is no PyPI package — vendor the tree, or skip the adapters and speak the wire directly.

two sides, one record

Bought by one function. Installed by another.

One function buys this — audit, compliance, model risk. Another installs it — the platform team that owns how agents call tools. What connects them is not a report either side writes; it is the record itself, examinable by construction. The repo ships one package per side.

The two amber packages are what the repo ships — one per side of the sale; the blue feed is the record they meet at. Plane internals mature in a private testing monorepo and are ported here once they settle.

three commitments

One signed object. Authority is key possession. Fail-closed, twice.

Not aspirations — load-bearing claims, each with a named mechanism and a test built to refute it. The claim-by-claim standing lives in docs/assurance.md.

01One signed object

The bytes the attester signs are the bytes the gate executes — a hash equality, not an alignment of documents. Criteria cannot ride the wire at all; they reach the gate only through signature verification plus content-address equality.

02Authority is key possession

The AI-facing drafting side — the author — holds no keys and structurally cannot sign. Nothing is enforceable until a human attester signs it, and a signed tombstone revokes it: maker-checker, applied to what agents act on. No signing seat exists anywhere in this repo.

03Fail-closed, twice, with one record

Verdicts are pass / fail / unevaluable — which never passes. Volatile facts and the authority itself are re-verified at the last moment before the consequence fires, and the decision and its audit record are one byte-exact event.

⤷ a signed tombstone revokes — checked at declaration, re-checked at the dispatch edge; the withdrawal is itself part of the record.

the decision flow

Four moves, four terminals — and every branch fails closed.

Every declared intent walks toward exactly one terminal — and a refused or duplicate intent leaves no ACHIEVED record: no value moved. The gate's job ends at the durable record — emit-and-observe: it settles nothing and never calls out; consumers pull the feed and recompute.

invariants

Eight properties, enforced by construction and pinned by tests.

None of these is a convention the code tries to follow. Each is structural — violating it fails an acceptance test, not a guideline. Amendments run CONTRACT.md first, then the pinned tables — never the reverse.

01Sole emitter

The gate alone emits the single ACHIEVED record, fsynced to the durable feed before success is reported. Consumers act only after observing it.

02Tri-state, fail-closed

Pass / Fail / Unevaluable — any Fail or Unevaluable ⟹ not authorized. Absence of valid proof is a denial, never a default-pass.

03Stable vs volatile

Stable criteria score once, at declaration. Volatile facts score again at the dispatch edge — same authority, immediately before authorizing.

04Idempotency by construction

The key is required and reserved at the edge. Same key, different intent hash — a near-duplicate — collides and is refused. At-most-once holds structurally.

05Determinism & replay

Per-intent logical clock, IDs from the episode seed, no wallclock. Replay drives a recompute, byte-identical — and the global cursor never enters the trajectory hash.

06Durability

The feed and the key reservations survive restart over the same INTENT_DATA_DIR — kill/restart proven: byte-identical events, same-key re-dispatch still refused.

07Thin-spec defense

Zero criteria or unknown volatility refuse before any scoring. "No criterion failed" is never satisfied by "no criterion existed" — attestation does not launder vacuity.

08Core neutrality

core/ carries no domain vocabulary, pinned mechanically. The kernel knows no domain — the treasury application below is one instantiation, not the design.

the record, re-verified

Hand the feed to someone who doesn't trust you.
They can re-derive all of it.

Everything expensive about the plane — logical clocks instead of wallclock, byte-frozen cross-language fixtures, an fsync before every success — buys nothing at decision time. It pays off afterward, when someone who does not trust the gate recomputes the record.

Status, honestly 4 stated
  • Key authority is test-grade until ADR-0009 — and every signature says so (key_authority: "test").
  • Record signing (R1) and workload identity (R2) are staged, not built — so today the verifier proves the record self-consistent, not never-rewritten.
  • force_scores is guarded and witnessed: refused unless the server booted with INTENT_UNSAFE_FORCE_SCORES=1, and every scored record carries a scorer_id witness — a forced decision is never byte-indistinguishable from a live one. Wherever the flag is set the bypass is total; it must never reach production.
  • The feed read surface is unauthenticated by design — network isolation is a deployment decision the platform owns.