Skip to main content

Contract-change authorization for AI agents: CodeRifts — contract change gate.

Only a granted change can proceed.

You won't have to trust our database to prove which one.

npx @coderifts/conformance --assurance GUARDED_TOOL_TABLE

COVERED / LIVE · 6 vectors

In the guarded tool table, an ungranted mutation does not exist. Scope: the tool table CodeRifts hands the agent. A path the host registers separately is outside this claim.

curl -sS -X POST https://app.coderifts.com/api/v1/action-verdict \
  -d '{"action_type":"tool_call","tool":{"name":"get_customer","capabilities":["read"]}}' \
  | python3 -c 'import sys,json;print(json.load(sys.stdin)["chain_receipt"])' > receipt.txt

git clone -q --depth 1 https://github.com/coderifts/receipt-verifier
node receipt-verifier/cli.js "$(cat receipt.txt)"

valid: true · VERIFIED_CURRENT · kid 2026-07-k1

VERIFIED_CURRENT means the signature verifies against the verifier’s current pinned key snapshot. It does not mean currently_authorized: true.

A signed decision, and a verifier that reads it without asking us. No API key, no account. Measured 2026-09-15.

Enforces on configured paths. Every decision names its coverage and limits.

Every answer carries its ceiling.

The left side is what the receipt above establishes. The right side is the same size, because it is the same receipt.

valid: true

The signature holds against the pinned keyring, offline.

currently_authorized: null

Whether that authorization is current could not be established from a token alone.

What a verdict can say.

execution_action

CONTINUE CONTINUE_WITH_MONITORING REQUEST_APPROVAL STOP

evidence

LIVERECORDED

no evidence

NOT_RUN

not decided

null

Where authorization cannot be established, the answer is never yes.

If current authorization cannot be established, the answer is null — never an assumed true.

What happens when an agent changes a contract

Format coverage, read 2026-10-04, is the table on the documentation page. An incomplete analysis in authorize mode is ANALYSIS_FAILED and STOP, with the gap named.

The agent requests a change

In the guarded tool table, an ungranted mutation does not exist.

Not blocked. Not there.

The agent does not receive an error. There is nothing to call.

npx @coderifts/conformance --assurance GUARDED_TOOL_TABLE

COVERED / LIVE · 6 vectors

Scope: the tool table CodeRifts hands the agent. A path the host registers separately is outside this claim.

The gate decides

A verdict is a named state.

Branch on execution_action. The printed Risk Score is not the branch.

A separate binding state is not a fifth outcome. AUTHORIZED_AND_COMMITTED is not authorized. The output carries the difference, not the documentation.

coderifts/demo pull request 4
check_run 114149286705
head b3a4b9c309f161de73d9ca99964de67a34f45a91
conclusion failure
# the check names the contract change
# contract change: phone removed from POST /users, including the request
decision             : STOP
breaking             : 3
Risk Score           : 39/100
title                : STOP · merge · REQUEST_FIELD_REMOVED

This block quotes check_run 114149286705. It is not bytes from the public action-verdict sample. The summary prints Risk Score 39/100. The branch is the decision STOP, not that number.

The surface carries its limits

The boundaries are a field you can fetch.

Eight limits of the published surface, served as data at the address below — not a paragraph in a PDF. They are surface-wide: an individual decision response does not carry this field.

curl -sS https://coderifts.com/.well-known/coderifts.json \
  | python3 -c 'import sys,json;[print(s) for s in json.load(sys.stdin)["does_not_prove"][:2]]'

Evidence is RECORDED — a pinned capture, replayed — not a live provider run.
proof_scope TRUSTED_EXECUTOR, provider_witness NOT_APPLICABLE,
externally_witnessed false: CodeRifts did not witness or sign a provider state.

All eight limits

What the merge check says

This change. This head. This issuer.

Not "the pull request is green". This change set, bound to this commit, from a named issuing App.

MEASURED — coderifts/demo #4

check-run re-read 2026-10-03
check_run 111270526393 · head a81e6092fea0ca82696a6d252441afde4117f2fa · GitHub completed_at 2026-10-03T18:58:29Z · conclusion failure
issuer title: STOP · merge · sha256:871f500…92c021 · 2026-10-03T22:58:22Z · REQUEST_FIELD_REMOVED, FIELD_REMOVAL, TOOL_RESULT_SHAPE_DRIFT, TOOL_INPUT_SCHEMA_DRIFT
Found 3 breaking, 4 safe, and 0 unclassified change(s) across 1 schema file(s). · Risk Score: 59/100 · 1 policy violation(s) (blocking) · earlier check reported 6

capture 2026-09-21
check: CodeRifts / contract-gate · conclusion: failure
issuer: CodeRifts App (app_id 2860592)
MERGE ENFORCEMENT : VERIFIED — re-read 2026-10-11: ruleset 22074842 alone, bypass_actors empty; GET /repos/coderifts/demo/branches/main/protection answers 404 Branch not protected; admin override refused with “2 of 2 required status checks are failing.”
ISSUER BINDING    : APP-BOUND — the required check carries app_id 2860592
WORKFLOW PIN      : ACTION PINNED BY SHA (96d0b4d) — the branch requires the CONTEXT
                    NAME, not a workflow path, so the pin is not what closes it
REQUIRED CONTEXTS : 2 — the app-bound check plus contract-gate (required), a
                    skip-immune aggregator from the shared github-actions issuer;
                    it closes the skipped-is-green hole, not the issuer question

re-read 2026-10-10, the latest CodeRifts / issuer check-run on coderifts/demo pull request 4 is check_run 114149286705, head b3a4b9c309f161de73d9ca99964de67a34f45a91, GitHub completed_at 2026-10-10T06:13:35Z, conclusion failure. The output title is STOP · merge · sha256:871f500…92c021 · 2026-10-10T10:13:29Z · REQUEST_FIELD_REMOVED, FIELD_REMOVAL, TOOL_RESULT_SHAPE_DRIFT, TOOL_INPUT_SCHEMA_DRIFT. The summary reports Found 3 breaking, 4 safe, and 0 unclassified change(s) across 1 schema file(s). · Risk Score: 39/100 · 1 policy violation(s) (blocking). An earlier issuer check on head df3de0d3ac4bdd1da82ed6449c7780b27ac13c7d, completed 2026-09-28T20:13:03Z, reported 6 breaking changes.

The gate judges an enum change by direction (ruleset enum-direction/2026-10-03): a request value removed is breaking, a request value added is not breaking, a response value removed is INFO, and a response value added is breaking and the decision is REQUIRE_APPROVAL, not BLOCK.

The committed note on the direction rule says the 3.0 engine adds a schema-level enum entry with an assumed request direction, and a response-side removal stays an INFO change. On this pull request Order.status drops delivered, and Order is a response body on GET /orders, POST /orders, and GET /orders/{id}, so that narrowing is an INFO change and sits outside the 3 breaking changes in check_run 111270526393.

The breaking changes behind the STOP line are the phone field removals, including the removal from the request: a response property on POST /users, a response property on GET /users/{id}, and a request property on POST /users. The title cites REQUEST_FIELD_REMOVED, FIELD_REMOVAL, TOOL_RESULT_SHAPE_DRIFT, and TOOL_INPUT_SCHEMA_DRIFT.

Pull request 4 is still open and its merge state is BLOCKED, so it has not merged. Check_run 111270526393 concluded failure, and its summary records 1 policy violation(s) (blocking).

Read the last line first. The check ran, failed, and named its issuer, and on 2026-09-19 that branch began requiring it, so a red check now stops the merge. Until that date an earlier capture ended “NOT VERIFIED” because nothing required the check. The sentence “Enforcement status: not verified” is what that earlier summary said. Check_run 111270526393 does not say it: the summary reports 3 breaking changes, Risk Score 59/100, and 1 policy violation(s) (blocking), and the conclusion is failure. On pull request 4 and pull request 26 both required contexts concluded failure: CodeRifts / issuer (App 2860592) and CodeRifts / contract-gate (Actions 15368). contract-gate (Action) is a third run, not a required context. The Actions app id on those runs is 15368, an issuer every workflow in a repository shares. Requiring a context by name is not enough, and binding it to an app id narrows who can forge it without closing it; pinning the required workflow by immutable SHA is what closes it.

On 2026-10-04 coderifts/demo pull request 5, head d9065ad60ededa50f8cee0f2417462bfe3824b0b, concluded success on CodeRifts / issuer (check_run 111474776915, completed 2026-10-04T16:04:39Z) and on CodeRifts / contract-gate (check_run 111474814520, completed 2026-10-04T16:04:47Z).

Open the live check   Reproduce it yourself

The grant is spent

One grant. One use.

No adapter can claim more than it can prove.

An adapter that cannot demonstrate single-use atomic consumption cannot report the atomic success state.

npx @coderifts/conformance --assurance ATOMIC_COMMIT

COVERED / RECORDED · 6 vectors

Anyone can verify it later

Prove that what executed matched what was authorized.

Offline. No API key. No CodeRifts server.

This is the command under the headline. The verifier reads a vendored key snapshot: it does not contact us, and it reports what the evidence does not establish.

currently_authorized: null

The hosted verify_receipt route, given a token alone, answers currently_authorized null. The open offline verifier prints VERIFIED_CURRENT and has no currently_authorized field.

Run it again from the top   What the receipt does not prove

One screen: the definition moved, and the call stopped

An agent was authorized to call a tool. Upstream, the tool’s description changed. Nothing else did. Run the description-drift preflight on /verify/ — it is block ten there, with the command, the output and what it does not prove. Six of the ten blocks need nothing but a terminal.

The one line the claims registry quotes from this page stays here, because /verify/ prints it in JSON and this is the field-and-value form:

# the clean tool, and the same tool with one sentence added to its description
curl -sS https://coderifts.com/examples/description-drift.json \
  | curl -sS -X POST https://app.coderifts.com/api/v1/preflight \
      -H 'Content-Type: application/json' -d @-

analysis_outcome : BREAKS_DETECTED
patterns         : ["description_only_change", "TOOL_INSTRUCTION_DRIFT"]
may_execute      : false
authorization_effect : NONE
verdict_fingerprint  : sha256:5096f53d…006209c

The gate runs where your agent already runs

ENFORCING AT THE MERGE · ADVISORY EVERYWHERE ELSE

OpenClaw, Claude Code and the Docker MCP Gateway each have their own wiring, and each says which of the two it is. See which path enforces and which reports.

The part most vendors leave out

The published surface carries its own limits, as data. Here are three of the eight, served live at the address below.

Evidence is RECORDED — a pinned capture, replayed — not a live provider run.

The grant does not bind the canonical tool-call bytes, so what-was-called is not proven equal to what-was-authorized (planned, not available).

No DSSE or in-toto envelope is emitted by any published artifact.

curl -sS https://coderifts.com/.well-known/coderifts.json \
  | python3 -c 'import sys,json;[print(s) for s in json.load(sys.stdin)["does_not_prove"]]'

A limit we publish is one you can hold us to. A limit we omit is one you discover in an incident.

All eight limits · What each proof proves

Written for the person who has to answer for it

You run agents in production. Something changed a contract last week and the answer to "who allowed that" is a log line you also control.

You are adding a gate to an agent runtime. Branch on execution_action. The issuer summary also prints Risk Score 59/100, and that number is not the branch.

You are the one an auditor will ask. You need evidence that survives us: no callback, no account, no vendor.

If none of these is you yet, the honest answer is that you do not need this today.

Start with the part that asks nothing of you

Signup at app.coderifts.com/api/signup is a key, and it is self-serve. The mail link is a conversation, not a form: partnership, one consequential change, and what we can and cannot prove about it.

During the public beta every organization gets Team features at $0 with a self-serve key, public and private repositories alike, no separate Team key to request, and each key allows 1,000 requests a month: after that, /preflight authorize returns BETA_LIMIT_REACHED while its analysis keeps working; other API routes return 429.