◉ANTIGRAVITY LABJP
Articles/Agents & Manager
◈ Agents & Manager/2026-05-26Advanced

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.

antigravity461agents144architecture20state-managementcloudflare-workers9

✦ Premium Article

One morning, an Antigravity Background Agent forgot the previous day's AdMob review and emailed me the same warning three times in a row. Following the run logs, the memory was technically present, but the restore key was off by a few characters and the agent could not read it back. I build iOS and Android apps solo, and I run most of the surrounding operations through editor-integrated AI agents like Antigravity. After a few rounds of "the agent forgot too much" and "the agent remembered too much," I landed on a single rule: agent state has to be split into three lifetimes, or it will eventually break. Looking back, what was broken was never the memory — it was my habit of putting information with different lifespans on the same shelf.

This piece is a practical write-up of those three lifetimes — ephemeral, journal, canonical — together with how I map them onto Cloudflare Workers stores (KV, Durable Objects, R2, D1), and how I gate the write-back boundary with an explicit approval step. If you are running more than one Antigravity agent in production, the boundaries below should give you a frame to sharpen your own design decisions.

The same Antigravity agent holds information with three very different lifespans

When you call everything "agent memory," it eventually breaks. The split I have settled on is:

TierLifetimeDamage if lostExamples
ephemeral1 runnear zero (recomputable)intermediate prompts, raw LLM output, file snapshots
journaldays to weeksmedium (slow to replay)decision logs, A/B intermediate scores, observation diffs
canonicalpermanentfatal (product-breaking)published slugs, purchase records, membership state

The deciding factor is not byte size but blast radius. A 200KB chain-of-thought can be ephemeral. A 60-byte "we already published this slug" record absolutely cannot be ephemeral.

When you internalize the split, agent code only needs to ask three questions:

  1. Can the next run recompute this? If yes — ephemeral.
  2. If not, can a human accept "we have to redo it"? If yes — journal.
  3. If losing it breaks the user-facing product — canonical.

That is the entire rubric. The tiers map to different physical stores, but agent code talks to all three through a single adapter shape.

Ephemeral — working memory that lives and dies inside one agent run

Ephemeral is everything that is born when an agent run starts and dies when it ends: scratch prompts, raw LLM responses, intermediate file listings, retrieval results, in-flight reasoning. Because the next run can rebuild it cheaply, there is almost no value in writing it to a shared store like KV.

In my Antigravity Background Agents this tier lives in plain JavaScript memory — a Map scoped to the run entry function.

// state/ephemeral.ts
export class EphemeralStore {
  private bucket = new Map<string, unknown>();
 
  get<T = unknown>(key: string): T | undefined {
    return this.bucket.get(key) as T | undefined;
  }
 
  put<T = unknown>(key: string, value: T): void {
    this.bucket.set(key, value);
  }
 
  // Always call this at the end of a run. Disposal is explicit.
  dispose(): void {
    this.bucket.clear();
  }
}

The explicit dispose() is intentional. I want ephemeral to be actively thrown away, not silently abandoned. If you let an intermediate LLM response leak into the journal or canonical tier by accident, your storage bill rises without bound. A single Background Agent in my fleet handles roughly 200–800 KB of ephemeral data per run; written naively into KV that adds up to gigabytes per month, far above the ¥800/month budget I keep per site.

One implementation gotcha worth flagging: Cloudflare Workers can reuse the same Isolate across requests, so a Map declared at the module scope will quietly persist between runs. I fell into that exact trap once. The fix was to make sure EphemeralStore is always new-ed inside the run entry function.

Because that reuse comes and goes under wrangler dev, I would rather reproduce it on purpose in a test than hope to notice it in production. Mine is deliberately small: hit the same Worker twice and check that nothing leaked from the first request into the second.

// test/ephemeral-leak.test.ts — @cloudflare/vitest-pool-workers
import { SELF } from "cloudflare:test";
import { expect, it } from "vitest";
 
it("ephemeral does not survive across runs", async () => {
  // /run?probe=1 writes one entry, then returns the Map size it can see
  const first = await SELF.fetch("https://example.com/run?probe=1");
  const second = await SELF.fetch("https://example.com/run?probe=1");
  const a = await first.json<{ size: number }>();
  const b = await second.json<{ size: number }>();
  // each run should only see the single entry it wrote itself
  expect(a.size).toBe(1);
  expect(b.size).toBe(1);
});

Both calls land in the same execution environment inside the test, so a Map that escaped to module scope shows up as size === 2 on the second request. The morning that test first went red was the morning I finally looked at where I was calling new. One failing test turned Isolate reuse from an occasional ghost into a reproducible fact.

✦

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
✦Split agent state into ephemeral / journal / canonical so a single Background Agent run can fail without taking your product down
✦Concrete Cloudflare mapping — KV / Durable Objects / R2 / D1 — with a small TypeScript adapter and a two-request vitest that catches ephemeral state leaking across Isolate reuse
✦A minimal TypeScript approval gate that holds canonical writes for review, plus a table for telling a held write apart from a headless exit code 3 or a --print-timeout
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 $15 for lifetime access
View Membership →

Related Articles

◈ Agents & Manager2026-09-04
Snapshot Your Agents' Effective Scope So You Notice the Day a Default Changes
Antigravity 1.1.25 made Markdown-defined custom agents inherit the surrounding skills, rules, and subagents by default. Here is the snapshot-and-diff routine I now use to check whether an agent I meant to keep narrow quietly got wider after an update.
◈ Agents & Manager2026-08-21
Why I Check Description Overlap Before Turning On inheritCustomizations
CLI 1.1.14 collapsed markdown agent inheritance into a single inheritCustomizations switch. Here is what happened when I actually inventoried the 47 skills in my workspace and decided the switch on description overlap rather than on context size.
◈ Agents & Manager2026-05-27
Record & Replay for Antigravity Agents
How to deterministically replay a failed Antigravity Agent run offline, drawn from a month of running it across four production sites. Covers boundary recording, R2 + KV storage costs, PII masking, and a working TypeScript harness.
📚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