◉CLAUDE LABJP
●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.301 — Adds the vim % motion, LaTeX math rendering in the VS Code panel, and a retry-watchdog limit for usage-limit waits (10/15)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 45 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-06-02Advanced

Guard Your Agent's Destructive Operations with Pre- and Post-condition Contracts

A design for wrapping an autonomous agent's writes in deterministic pre- and post-condition checks. A contract gate stops the destructive operations that better prompts can never reliably prevent.

Claude Agent SDK13Autonomous AgentsArchitecture3Reliability5Design6

✦ Premium Article

The scariest thing about running an autonomous agent in production is not when it crashes. A crash, at least, is honest. The truly dangerous failure is when a language model like Claude fails plausibly: it produces wrong content, confidently, in a perfectly well-formed shape. The schema validates. The types line up. The logs say success. And yet what got written is broken. That is the failure mode that keeps me up at night.

I have been running several apps and sites solo since 2014, and for the past couple of years I have handed parts of my publishing and deployment to autonomous agents. My first naive setup let the agent push to git directly, and once or twice a week something broken would reach production: a link pointing at a page that did not exist, a mismatch between the number of Japanese and English articles, a config file whose internal consistency had quietly drifted. Every one of these is the kind of failure you think you can fix by writing a more careful prompt. But no matter how much I polished the instructions, the leak rate never reached zero.

Here is the conclusion up front: what worked was not better prompting. It was physically wrapping the agent's write operations in deterministic gates. This article generalizes that design as a "contract gate" and walks all the way to an implementation you can bolt onto Claude Agent SDK tool calls.

Why "Ask the AI to Be Careful" Designs Break Down

Most agent implementations delegate safety to the prompt: "Do not write broken data," "Verify consistency before you push." It seems to work, but it is fundamentally a probabilistic defense. The model decides slightly differently every time. Even at 99% reliability, if you run it dozens of times a day, that remaining 1% will surface, reliably.

This is where an old idea from software engineering helps: Design by Contract. You give a function a precondition (the states in which it may be called) and a postcondition (what it guarantees afterward), and if either is violated you refuse to run. An autonomous agent's tool call is exactly the place that needs such a contract. Whatever the model is "thinking," the machine checks the invariants before and after the write, and a state that violates an invariant never reaches production. You move the locus of judgment from the model to deterministic code.

Official docs often present permission hooks like canUseTool as a way to insert human approval. But in real operation, the most effective thing to interpose is not a human and not the model — it is a deterministic check with neither in the loop. Human approval does not work in a nightly batch, and the model's self-check is, as noted, probabilistic.

The Shape of a Contract Gate: tryGuard, apply, commitGuard, rollback

A contract gate wraps a single destructive operation in four phases.

  1. tryGuard (precondition) — Check whether the state permits the operation. If violated, refuse without executing.
  2. snapshot — Record the pre-operation state for reversal.
  3. apply — Perform the actual write.
  4. commitGuard (postcondition) — Check whether the post-write state satisfies the invariants. If violated, roll back to the snapshot.

The key is that the postcondition phase follows an "apply, then check, then revert if bad" flow. Preconditions alone cannot verify the result of applying. For example, the invariant "the Japanese and English article counts are equal" can only be confirmed after the files are written. That is precisely why apply, check, and reversal must be treated as one unit.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦A complete, dependency-free TypeScript implementation of a contract gate that wraps tool calls in tryGuard, apply, commitGuard, and rollback
✦Clear criteria for what belongs in pre- and post-conditions: only invariants that resolve deterministically against external state
✦Concrete patterns for safe rollback via snapshots and idempotent reversal, plus a real breakdown of what the gate actually caught in production
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-08-01
Swapping Tools Mid-Conversation Without Losing Your Prompt Cache
Tools can now be added and removed between turns, but the tool block sits at the very front of the cache prefix. I measured 12 mutation patterns with fingerprints and built a stable-core plus volatile-tail registry with a guard that refuses unsafe changes.
⬡ API & SDK2026-07-26
Rebuilding a Remote MCP Server That Never Needed Mcp-Session-Id
The MCP spec release candidate drops the session header. Here is how I audited my own remote server for session coupling and moved it to signed cursors, with measurements.
⬡ API & SDK2026-05-20
Resolving Tool Name Collisions When Bundling Multiple MCP Servers in the Claude Agent SDK
When the GitHub MCP and Linear MCP both expose create_issue, Sonnet 4.6 cannot tell them apart. This article covers the structure of MCP tool name collisions, a TypeScript reconciler implementation, and a measured correction: the schemaHash I originally published missed all five schema drifts I tested it against.