◉ANTIGRAVITY LABJP
Articles/Agents & Manager
◈ Agents & Manager/2026-06-02Advanced

Rehearsing an Agent's Actions Before They Touch Production — Designing a Zero-Side-Effect Dry-Run Layer

Some accidents survive shadow mode and canaries: the very first time an agent touches an external API. This is the design and TypeScript implementation of a zero-side-effect dry-run layer you can bolt onto Antigravity's parallel agents, with the real numbers from running six sites autonomously.

antigravity464agents146dry-runside-effectsoperations33architecture20reliability12

✦ Premium Article

One day I was running an agent that rewrites production Remote Config values, and my blood ran cold. All I wanted was to flip one campaign flag to true. But the agent had decided to tidy up a few stale keys along the way, and its plan included deleting settings that were still live. I happened to be watching the log that time, and stopped it just before it applied. But if it had been an overnight job, the display logic across the wallpaper apps I run might have broken all at once.

As a solo developer, I hand most routine work to Antigravity's parallel agents, but I never quite shook the fear around one category of action: when an agent changes something external. Code changes can be reviewed and rolled back. But AdMob settings, App Store Connect metadata, production Remote Config values, Cloudflare DNS — once you hit those, they are hard to un-hit.

Woodworkers have a saying: "Measure as often as you like; cut only once." Check the dimension as many times as you need, but make the cut just once. Ever since I started delegating external actions to agents, I've wanted to rebuild that instinct as a mechanism. This note is the record of the zero-side-effect dry-run layer I built for exactly that. It includes a minimal, working TypeScript implementation, and I hope it helps anyone else operating agents that carry external side effects.

Shadow mode and canaries can't protect the "first time"

Shadow execution and canaries are well-known safety devices for agents, and at first I assumed they were enough. But in practice, I found neither one protects the very first time an agent touches production.

Shadow execution runs an agent alongside production while discarding its output. It's great for observation, but it doesn't apply to side-effecting actions. The shadow of "rewrite Remote Config" is a contradiction: if you don't write, there's nothing to observe; if you do write, production has already changed. It works for read-heavy agents, but it can't protect a write agent's first run.

Canaries apply a change to a small slice of users or targets first, then widen it if nothing breaks. That helps after application. But what scared me was when the application itself was wrong. In the Remote Config example, the plan to "delete a live setting" was the error — and applying that error to 1% or to 100% is still an error. Canaries narrow the blast radius of a correct change; they don't reject an incorrect change in advance.

What I needed was a way to see, completely but without any side effects, exactly what was about to happen — before it happened. That is a dry-run. In engineering terms, I wanted the relationship between terraform plan and terraform apply, available for any external action an agent might take.

A dry-run only works if side effects are funneled

When you try to add a dry-run after the fact, you usually hit the first wall immediately: side effects are scattered all over the code, and there's no single point where "stop here and you stop every change." If each agent tool calls fetch or hits an SDK on its own, adding a dry-run means sprinkling conditionals across every site — and you will miss one. The moment a single call slips through unguarded, the dry-run lies.

So the starting point of the design is not the dry-run mechanism itself. It is funneling every externally-mutating action through one place. I call this the side-effect boundary. Tools never execute side effects themselves; they declare to the boundary, "I want to make this change." In production mode the boundary executes it; in dry-run mode it records it without executing. Only once this is unified does a dry-run become trustworthy.

// effect.ts — the type for a "planned action" representing an external side effect
export type EffectKind =
  | "http"          // a write request to an external API
  | "kv-write"      // a write to KV / Remote Config, etc.
  | "file-write"    // a write to a repo or asset
  | "delete";       // a deletion of something (split out — it's the most dangerous)
 
export interface Effect {
  kind: EffectKind;
  // a one-line summary a human can read to understand what will happen
  summary: string;
  // structured detail used when actually executing
  target: string;                 // e.g. "remote-config/campaign_flag"
  before?: unknown;               // value before the change (only if obtainable)
  after?: unknown;                // value after the change
  // the executor, called on real application. Never called during a dry-run.
  commit: () => Promise<void>;
}

The key is that an Effect bundles together the declaration of intent and the function that actually does it (commit). The declaration is what you show a human during the dry-run; commit runs only on real application. Locking both into the same object makes it far harder to drift into "the plan said delete X, but execution deleted Y."

✦

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
✦Get the full TypeScript implementation of a SideEffectBoundary that funnels all external mutations through one point, plus the Effect recorder that captures the plan
✦Understand how a dry-run solves a different problem than shadow mode and canaries, and how to decide which one a given situation needs
✦Learn the real operating numbers and gotchas — how unintended production changes went from a few a month to zero after adding the dry-run layer
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

Antigravity 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

◈ Agents & Manager2026-05-26
Antigravity Multi-Agent State Tiers — A Three-Layer Design with Ephemeral, Journal, and Canonical
Before your Antigravity Background Agents and Sub-agents start mixing up their memory, split agent state into three lifetimes — ephemeral, journal, canonical — and map each to the right Cloudflare store. Includes a TypeScript approval gate for write-back, and what changed when headless runs stopped silently auto-approving.
◈ Agents & Manager2026-04-24
SRE for Antigravity Agents — Taming Probabilistic Systems with SLOs and Error Budgets
AI agents are probabilistic by nature, so running them in production without SRE thinking is risky. This guide applies SLIs, SLOs, and error budgets to Antigravity agents with working code, and shows why the textbook 14.4x burn-rate alert never fires on a loose SLO.
◈ Agents & Manager2026-10-13
Turning Gut-Feeling Judgments into Rules an Agent Can Actually Follow
A small, repeatable way to turn the judgment calls that live only in your head into written rules an Antigravity agent can apply, with a quick check against past cases.