ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-05-30Advanced

Build for the Day the Agent Breaks Something: Keeping Blast Radius Small

Once you let an Antigravity agent touch production, the problem stops being how smart it is and becomes how much it wrecks when it slips. Here is a four-layer containment design that shrinks blast radius, with TypeScript and real numbers.

Antigravity338AI agents24production operations5reliability design4blast radius

Premium Article

When you run a 50-million-download app business by yourself, the chores you'd love to hand off pile up fast: bulk dependency upgrades, keeping localized resource files in sync, batch-fixing the same crash class that Crashlytics keeps surfacing. The work I've been grinding by hand since 2014 is exactly the work I most want to automate.

So the first time I let an Antigravity agent do a bulk rewrite across a production repo, I froze in front of a diff that had broken 38 files at once. The agent's reasoning was wrong in exactly one place — but the design fanned that one decision out to every file, so the damage spread instantly. Tests passed. What broke was an initialization order the tests never looked at.

That day taught me that in agent operations, the thing that bites you first isn't intelligence — it's blast radius: how far a single failure spreads. Even a smart agent will be wrong sometimes. The real question is how much it drags down with it when it is. Below is a containment design for keeping that radius structurally small, with the implementation alongside.

Measure Blast Radius on Three Axes

"That feels risky" is a feeling, not a design. Before talking about containment, you need a ruler for damage. I use three axes.

  • Spread: how many files, records, or external calls a single run changes
  • Cost: the token billing that run consumes, plus the money wasted when it fails
  • MTTR: wall-clock time to roll back from broken to working

Reviewing the 38-file incident on these axes: spread was 38 (excessive), cost was about ¥120 per run (trivial), and recovery took 27 minutes of git revert plus build verification (heavy). So the real risk of that task was never money — it was excessive spread and long recovery time. Splitting the axes shows you which knob to tighten.

Start with a small helper that estimates damage before the run.

// risk-estimate.ts — estimate blast radius before an agent run
export interface RiskEstimate {
  spread: number;       // number of targets changed (files/records/API calls)
  costYen: number;      // expected token billing (yen)
  mttrMinutes: number;  // expected recovery time on failure (minutes)
}
 
export interface RiskPolicy {
  maxSpread: number;
  maxCostYen: number;
  maxMttrMinutes: number;
}
 
export function classifyRisk(est: RiskEstimate, policy: RiskPolicy) {
  const reasons: string[] = [];
  if (est.spread > policy.maxSpread) reasons.push(`spread ${est.spread} > ${policy.maxSpread}`);
  if (est.costYen > policy.maxCostYen) reasons.push(`cost ¥${est.costYen} > ¥${policy.maxCostYen}`);
  if (est.mttrMinutes > policy.maxMttrMinutes) reasons.push(`mttr ${est.mttrMinutes}m > ${policy.maxMttrMinutes}m`);
  // empty reasons → auto-approve; otherwise route to a human approval gate
  return { autoApprove: reasons.length === 0, reasons };
}

The key is keeping the logic dumb: if any one of the three axes exceeds its threshold, stop auto-execution and route to approval. Fancy scoring makes it impossible to explain later why a run was blocked. My rule is simple — I don't put unexplainable automated decisions in production.

Layer 1: See the Diff Before You Apply It

The outermost layer is a dry-run that pins down what will change before anything changes. Antigravity agents cause side effects through tool calls, so the leverage is splitting each tool into a "plan" mode and an "apply" mode.

// dry-run-wrapper.ts — split side-effecting tools into plan/apply modes
type Plan = { op: "write" | "delete"; path: string; preview: string }[];
 
interface ToolContext {
  mode: "plan" | "apply";
  plan: Plan;
}
 
export async function writeFile(ctx: ToolContext, path: string, content: string) {
  if (ctx.mode === "plan") {
    // don't write — just stage the change in the plan
    ctx.plan.push({ op: "write", path, preview: content.slice(0, 200) });
    return { staged: true };
  }
  await fs.writeFile(path, content, "utf-8");
  return { staged: false, written: true };
}
 
// run one agent turn in plan mode, measure spread, then apply
export async function runWithDryRun(agentTurn: (ctx: ToolContext) => Promise<void>, policy: RiskPolicy) {
  const ctx: ToolContext = { mode: "plan", plan: [] };
  await agentTurn(ctx);
  const spread = ctx.plan.length;
  if (spread > policy.maxSpread) {
    return { applied: false, plan: ctx.plan, blockedBy: `spread ${spread}` };
  }
  await agentTurn({ mode: "apply", plan: ctx.plan });
  return { applied: true, plan: ctx.plan };
}

The official tool-design guidance doesn't stress this, but what works in production is running the same turn twice: plan first, count spread, then apply. With a plan phase in place, the 38-file incident would have shown me "about to change 38 files" and I'd have stopped it. It costs a bit under 2x the tokens, but trading ¥120 of a run to erase a 27-minute recovery risk is cheap insurance.

One caveat: if a tool is non-deterministic (depends on time, randomness, or external state), running it twice breaks down. I snapshot tool inputs at the start of the run and feed the same values to both phases. Get sloppy here and you hit the worst betrayal — "the plan said 3, the apply changed 12."

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
Wrap autonomous agent runs in four layers — dry-run, scoped permissions, circuit breaker, dead-letter queue — so a single runaway action can't cascade across production, with TypeScript you can drop in as-is
Learn from a real incident running a 50M-download app where one bulk rewrite corrupted 38 files, and how to measure blast radius along three concrete axes: file count, cost, and rollback time
Set circuit-breaker thresholds, dead-letter re-injection rules, and the human-approval boundary using real numbers that contain damage without strangling your own productivity
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-06-30
When the Android CLI Got 3x Faster and Cut Tokens by ~70%, the Right Move Was More Verification Per Change — Not More Parallelism
Reading that the Android CLI agent runs ~3x faster while using ~70% fewer tokens, my first instinct was to ask how many runs to parallelize. But a faster agent doesn't change how much work ships — it changes where the queue forms. This walks through why, sizes the new bottleneck (review and verification gates) with Little's Law, enforces a WIP cap with a working Python admission controller, and reinvests the freed budget into depth per change — with measured results.
Agents & Manager2026-06-01
Rolling Back a Half-Finished Agent: Compensating Transactions for Partial Failure
When you let an Antigravity agent run work that spans several external systems, a failure in the middle leaves the world half-rewritten. Retrying doesn't fix that. Here is how to fold it back safely with compensating transactions (the Saga pattern), with TypeScript and real operational numbers.
Agents & Manager2026-05-31
Flow Control for Autonomous Agents: Backpressure and Queues That Keep Production Alive
Run several Antigravity agents at once and the problem stops being how smart they are and becomes how little your downstream can absorb. Here is a flow-control design — bounded queue, semaphore, token bucket, backpressure, dead-letter — with TypeScript and real numbers.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →