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

Cost Attribution for Antigravity Agents — A Showback Architecture That Maps Execution Cost Back to Tenants Across Multi-Product Operations

A multi-tenant Showback architecture for Antigravity agents running across multiple products, with the schema, propagation patterns, and seven months of production numbers from running 4 sites and 6 apps in parallel.

antigravity429agents125cost-attributionshowbackmulti-tenancyproduction71

Premium Article

After running Antigravity agents across multiple products in parallel, I started getting an uncomfortable feeling every time I opened the monthly invoices from Google Cloud and Anthropic — I could not confidently answer "which product caused this line item." I have been an indie developer since 2014 with roughly 50 million cumulative downloads across iOS and Android apps, and I also run Dolice Labs (Claude Lab, Gemini Lab, Antigravity Lab, Rork Lab) in parallel. The agents touch everything from AdMob revenue optimization for our Zen wallpaper app to article generation and Crashlytics triage for each Lab site. They share a common cache, a common Gemma4 inference node, and a common Claude Agent SDK job queue, so the invoice cannot tell me "the wallpaper app cost this much in isolation" or "Claude Lab's article generation cost that much."

For a while I split the cost loosely — "the app business is bigger, so 70% of this is theirs" — until one month an AdMob eCPM optimization agent went into an unexpected retry storm and a single tenant silently consumed 71% of the month's budget. With sloppy attribution, finding the culprit and prioritizing the fix took more than a day. To bring that down to under two hours, I rebuilt the cost-attribution layer into the Showback architecture this article walks through.

Showback is originally a cloud-industry term for a design that "does not chargeback to tenants but makes execution cost visible per tenant." It is one step lighter than chargeback (where the tenant actually pays internally), and the discipline is to make sure every cost-incurring path is recorded with a "who, when, what for" label and surfaced on a dashboard. Even at an indie scale, this discipline pays off the moment your agents start crossing product boundaries.

How to Carry the Tenant ID — Context Propagation

The starting point of Showback is the state where every cost-incurring event carries a label (tenant ID) saying which product it belongs to. If even one path leaks this label, you end up with an eerie "unattributed: ¥98,400" line on the monthly report, and the attribution loses its credibility.

I use TypeScript's AsyncLocalStorage for context propagation. The tenant is fixed at the agent's entry point (an HTTP request, a Cron trigger, or a queue consumer), and from there every downstream async call carries the tenant ID without needing to add it to function signatures. The version I call from a Next.js Route Handler looks like this:

// src/lib/tenant-context.ts
import { AsyncLocalStorage } from "node:async_hooks";
 
export type TenantContext = {
  tenantId: string;        // e.g. "wallpaper-zen-ios" / "claudelab-net" / "internal-cron"
  productGroup: "app" | "lab" | "internal";
  invocationId: string;    // 1 request = 1 ID
  budgetCapJpy?: number;   // optional monthly cap
};
 
const storage = new AsyncLocalStorage<TenantContext>();
 
export function runWithTenant<T>(ctx: TenantContext, fn: () => Promise<T>) {
  return storage.run(ctx, fn);
}
 
export function currentTenant(): TenantContext {
  const ctx = storage.getStore();
  if (!ctx) {
    // Throw to prevent "unattributed" — never silently default
    throw new Error("tenant context missing — wrap with runWithTenant()");
  }
  return ctx;
}

Three points matter in the design. First, do not allow a default value when the context is missing. If you let an "unknown" or "default" tenant exist, you will one day find that 40% of cost sits there. I tripped over this for a week, but the upside was that every uninstrumented code path got flushed out, so I now strongly recommend forbidding the default as a production prerequisite.

Second, always issue invocationId as a ULID or UUIDv7. The Showback Ledger uses it in a unique constraint to absorb duplicate writes from at-least-once delivery.

Third, classify productGroup at the agent layer. In my setup the three buckets are app / lab / internal, which makes it easy to switch axes when generating the monthly report.

The Three Axes of Cost Measurement — Tokens, Requests, Side Effects

Antigravity agent execution cost cannot be measured fairly with a single unit. I track it on three axes.

The first axis is token cost — the LLM call itself for models like Gemini 3 Pro Thinking. Pricing differs per input/output and per provider, so I keep a unit-price table and record "current unit price × tokens" at call time. This includes retries and Tier 1 fallbacks, so the redundant cost during a resilience event is captured too.

The second axis is request cost — per-call fees from tool-side APIs (Apple Search Ads API, Crashlytics Reporting API, Stripe API) and Cloudflare Workers invocation fees. These stack up in tiny units like ¥0.001 per request and "who called this" disappears quickly if you do not attribute.

The third axis is side effects — the production-write outcomes the agent produced (ad spend, push notification fees, SMS/email fees). This is usually 3x to 10x larger than the LLM cost and is therefore the most important axis for Showback. For example, the keyword-bidding agent I run for AdMob mediation A/B tests costs ¥4,200/month in tokens but moves ¥180,000/month of actual ad spend. If you only track the former, you will conclude "LLM is cheap, no problem" and miss the real story.

The implementation can be a thin wrapper. A small layer that calls the Anthropic SDK and Google AI SDK "with tenant" leaves most of your code untouched.

// src/lib/cost-meter.ts
import { currentTenant } from "./tenant-context";
import { writeLedgerRow } from "./ledger";
 
type Pricing = { inputPer1k: number; outputPer1k: number; currency: "JPY" };
 
const PRICING: Record<string, Pricing> = {
  "gemini-3-pro-thinking":      { inputPer1k: 0.42, outputPer1k: 2.10, currency: "JPY" },
  "claude-opus-4-6":            { inputPer1k: 2.25, outputPer1k: 11.25, currency: "JPY" },
  "gemma4-27b-onprem":          { inputPer1k: 0.00, outputPer1k: 0.00, currency: "JPY" }, // electricity tracked elsewhere
};
 
export async function meterLlmCall<T>(opts: {
  model: keyof typeof PRICING;
  inputTokens: number;
  outputTokens: number;
  result: T;
}): Promise<T> {
  const tenant = currentTenant();
  const p = PRICING[opts.model];
  const costJpy =
    (opts.inputTokens / 1000) * p.inputPer1k +
    (opts.outputTokens / 1000) * p.outputPer1k;
 
  await writeLedgerRow({
    tenantId: tenant.tenantId,
    productGroup: tenant.productGroup,
    invocationId: tenant.invocationId,
    axis: "tokens",
    model: opts.model,
    units: opts.inputTokens + opts.outputTokens,
    costJpy,
    occurredAt: new Date().toISOString(),
  });
 
  return opts.result;
}

The side-effect axis uses the same wrapper pattern. A separate row with axis: "side_effect" writes to the same ledger. Keeping all three axes in one table lets the monthly report use a plain SUM(cost_jpy) GROUP BY tenant, axis, which is much easier to operate over time.

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
TypeScript middleware using AsyncLocalStorage to propagate tenant context safely through Antigravity's three execution layers (prompts, tool calls, side effects), with no leakage to the 'unattributed' bucket
Schema for a Cloudflare D1 Showback Ledger that attributes ~¥320,000 of monthly agent execution cost across 17 tenants (4 sites + 6 apps + 7 internal cron jobs), with Workers Cron generating the monthly attribution report
How a single tenant silently consumed 71% of the month's budget in the first 7 days of March 2026, how the Showback dashboard surfaced it, and how the fix cut token consumption by 42% within 48 hours
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-07-05
Protecting Your Agent Stack's Known-Good State with a Single Lockfile — Change-Budget Design for an Era of Simultaneously Moving Parts
When the IDE build, CLI, model, and dependencies all move at once, you can no longer tell which one caused a regression. Here is a change-budget design that pins your known-good state to one lockfile, with working code and operational logs.
Agents & Manager2026-05-29
Supervising Long-Running Antigravity Agents — Watchdog and Tiered Recovery
Eight weeks of running AdMob revenue optimization on Antigravity background agents revealed three quiet failure modes. Here is the watchdog plus tiered recovery design I landed on.
Agents & Manager2026-05-27
Record & Replay for Antigravity Agents — A Production Pattern to Reproduce Failures in 3 Minutes
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
See all →