Designing a 4-Tier Fallback Architecture for Antigravity Agents — Catching Model Degradation, API Outages, and Cost Overruns Across Layers
How to design a 4-tier fallback hierarchy for production AI agents on Antigravity, drawn from 24 months of running 11 agents across 6 indie apps. Includes the decision logic, code, and real demotion statistics.
I have been shipping iOS and Android apps as an indie developer since 2014, and over the past two years I have been running Antigravity-based AI agents across 6 of them — a wallpaper app series, a relaxation series, and a manifestation series that together account for the bulk of the 50 million cumulative downloads I have shipped. After a while, one pattern became unavoidable: a single fallback layer is not enough to keep production agents alive.
The Gemini 3 Pro Thinking rate limits jump unpredictably on some days. The Crashlytics ingestion API jams for half an hour at a time. And during fiscal-close months, the AdMob optimization agent gets dangerously close to the monthly cost ceiling. Each failure mode has a different shape, but if you handle them all with the same "retry, then switch to a secondary model" pattern, you will eventually get stuck somewhere.
Downtime in any of these agents directly hits revenue. As the person responsible for keeping 50M+ downloads' worth of users on a stable experience, I could not keep running on a fragile single fallback. So I rebuilt the layer from scratch, and what follows is the 4-tier fallback architecture I now run in production — Tier 0 through Tier 3, governed by three orthogonal signals (Burn-Rate, cost cap, model health).
Why a single fallback layer breaks down in production
For the first six months I ran a simple try { primary() } catch { secondary() } pattern. In practice, this capped my monthly availability at around 99.0% — not enough for an indie operation depending on AdMob and IAP revenue.
The failure modes split into three distinct types. The first is gradual model quality degradation: no explicit error, just subtle drift and wasted reasoning loops. The second is localized API outages, where certain endpoints jam for 10 to 30 minutes. The third is cost overrun, where usage curves out of plan and threatens the monthly budget. A single fallback layer applies the same treatment ("switch to secondary") to all three, which actively makes the cost case worse — secondaries are usually equal-or-more expensive.
The lesson I took from this is that fallback design needs to separate the direction of demotion from the reason for demotion. The reason — Burn-Rate vs. health degradation vs. cost cap — should determine which tier you move to, not just whether you move at all.
The 4-tier hierarchy at a glance
The architecture I now run in production has four layers. Tier 0 is normal operation; Tier 3 is the last line of defense.
Tier 0 — Primary: Gemini 3 Pro Thinking on Antigravity. Full context, full tools, full reasoning.
Tier 1 — Degraded: Gemini 3 Flash with prompt compression and reduced tool surface. For rate limits and mild health degradation.
Tier 2 — Local Fallback: Locally-hosted Gemma 4 (12B quantized) integrated into Antigravity. For complete external API outages or severe degradation.
Tier 3 — Cached/Static: Semantic cache lookups plus pre-defined static responses. The final defense.
Each step down trades fewer external dependencies for lower quality and lower cost. Tier 2 still performs AI inference; Tier 3 abandons inference entirely and returns known-good responses. The principle is "any answer the user can act on beats no answer" — a value especially important for tasks like the Crashlytics morning triage agent where a 95%-accurate answer beats radio silence.
✦
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 implementation of the Tier 0–3 transition logic, including hysteresis to prevent flapping
✦Threshold design that combines Burn-Rate SLOs, cost caps, and model health scoring for demotion decisions
✦24 months of demotion statistics from 6 production apps — how often each tier kicked in and why
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.
Tier 0 — Primary (Gemini 3 Pro Thinking, normal operation)
Tier 0 handles 92 to 95% of all requests in steady state. The non-obvious operational rule here: you must collect the signals that drive demotion decisions inside Tier 0 itself. If you wait until things break to start measuring, you are already too late.
healthBuffer is a rolling window of the last 100 health scores, used by the Tier 1 demotion logic. In my production data, Tier 0 latency sits in the 3.8 to 5.2 second range with health scores between 0.93 and 0.98. When the live values drift outside that baseline, the demotion logic engages.
Tier 1 — Degraded (model swap and prompt compression)
Tier 1 catches the case where the external API is still reachable, but continuing on Tier 0 is risky. There are three demotion triggers:
More than three rate-limit errors in the last 10 minutes
Average health score over the last 20 requests dropped below 0.80
Cost Burn-Rate is tracking above 110% of the monthly budget
When demoted, the model swaps to Gemini 3 Flash, the prompt is compressed by 30 to 40%, and the tool call ceiling drops.
The gotcha I hit here is making sure compressPrompt is idempotent. When you return from Tier 1 back to Tier 0, you do not want the compressed remnants to leak into Tier 0 prompts. I learned this the hard way — I once shipped a build where the compressed prompt persisted in shared state, and Tier 0 kept running the truncated version for six hours before I caught it.
In production, Tier 1 costs come in at 22 to 28% of Tier 0 and latency drops to 1.4 to 2.1 seconds.
Tier 2 — Local Fallback (Gemma 4 local inference)
Tier 2 disconnects from external APIs entirely. I run a Gemma 4 12B quantized model bundled with the Antigravity runtime. Demotion triggers:
Tier 1 error rate exceeds 25% over the last 5 minutes
Gemini API has been returning 5xx for 30 seconds continuously
Health score drops below 0.60 (severe degradation)
Tier 2 quality varies sharply by task type. For complex code generation or open-ended reasoning, I see 60 to 75% of Tier 0 quality. But for shape-constrained tasks like the morning Crashlytics triage — where the agent only needs to fit known output structures — Tier 2 reaches 88 to 92% of Tier 0. The implication is that each agent should pre-declare which task types it accepts running on Tier 2.
For the wallpaper and relaxation app series, my AdMob placement optimizer runs eCPM calculations just fine on Tier 2, but the dynamic IAP offer adjustment is too quality-sensitive. On Tier 2, the IAP agent therefore switches to a different policy: "hold the most recent Tier 0 recommendation for 24 hours." This kind of agent-specific Tier 2 policy is crucial.
Tier 3 — Cached/Static (the last line of defense)
Tier 3 gives up on inference. Two mechanisms keep responses flowing.
First, I keep a semantic cache of the last 30 days of Tier 0 responses; if the current request has cosine similarity ≥ 0.92 to a cached response, I return the cached one. Second, each agent defines a static fallback response for when even the cache misses.
Any drop to Tier 3 triggers an alert (Slack plus PagerDuty). Tier 3 is the floor of automated recovery — staying there for long means real service degradation, so a human must investigate root cause and walk the system back up to Tier 0.
The transition decision logic — three signals, one controller
Tier transitions combine three orthogonal signals. Here is the controller I run:
// tier-controller.ts — Tier transition decisiontype TierLevel = 0 | 1 | 2 | 3;interface TierSignals { burnRate: number; // monthly budget consumption pace (1.0 = on track) errorRate10min: number; // error rate over last 10 minutes (0-1) healthScore20: number; // avg health over last 20 requests (0-1) apiAvailable: boolean; // external API reachability costCapExceeded: boolean; // monthly budget ceiling hit}export function decideTier(signals: TierSignals, current: TierLevel): TierLevel { // Forced Tier 3 — full degradation if (signals.costCapExceeded) return 3; // Forced Tier 2 — external API down if (!signals.apiAvailable) return 2; // Severe health degradation if (signals.healthScore20 < 0.60) return 2; // High error rate — demote to Tier 1 (already in Tier 1/2 stays put) if (signals.errorRate10min > 0.25 && current === 0) return 1; // High Burn-Rate — demote to Tier 1 if (signals.burnRate > 1.10 && current === 0) return 1; // Mild health degradation if (signals.healthScore20 < 0.80 && current === 0) return 1; // Promotion logic — only when all 3 signals stabilize if (current > 0 && signals.healthScore20 > 0.90 && signals.errorRate10min < 0.05 && signals.burnRate < 0.95) { return (current - 1) as TierLevel; } return current;}
The asymmetry matters: demotions are instant, promotions require hysteresis. A single condition triggers demotion, but promotion only fires when all three signals are simultaneously stable. In production I wrap the promotion check in an additional 5-minute window of continuous stability to prevent flapping between tiers.
The controller runs every 30 seconds, independently per agent. Across my 6 apps and 11 agents, each agent maintains its own tier — so it is entirely normal for the AdMob optimizer to be on Tier 1 while the IAP agent stays on Tier 0.
24 months of production data — demotion statistics
After running this architecture continuously for 24 months across 11 agents in 6 apps, the request distribution sits at roughly 1.84 million total executions:
Tier 0 (completed normally): 94.3% (about 1.735M)
Tier 1 (received fallback): 4.8% (about 88K)
Tier 2 (local inference): 0.7% (about 13K)
Tier 3 (cache or static): 0.21% (about 3,800)
The distribution of demotion reasons is more informative than the totals. Of all Tier 1 demotions, 67% were Burn-Rate driven (cost-trajectory corrections), 22% were rate limit, and 11% were health degradation. Of the 13K Tier 2 demotions, 85% were Gemini API localized outages, 12% severe health degradation, and 3% network path issues. The 3,800 Tier 3 events were almost all end-of-month cost-cap enforcement; true complete API outages happen only 4 to 6 times a year.
In day-to-day operation, Tier 1 is invisible to end users — output quality differences are barely perceptible. Tier 2 handles shape-constrained tasks like Crashlytics triage cleanly, and Tier 3 only kicks in during end-of-month budget enforcement or genuine emergencies.
Operational pitfalls when adopting this architecture
If you are bringing tier-based fallback into your own production system, here are the pitfalls I have personally hit.
First, do not defer the health score design. The demotion decisions lean heavily on health scoring, so a coarse score means an incoherent architecture. I started with latency as my only health signal; demotion accuracy improved dramatically once I extended the score to four axes (latency, token usage, finish_reason, validator pass).
Second, pre-warm the Tier 2 local model. Gemma 4 has a 15 to 20 second cold load, which is unacceptable when you need it most. I run a dummy inference once an hour as a health check that doubles as a warmup.
Third, every Tier 3 static response must guide the next human action. "Unknown" or "undetermined" responses are useless during degradation. Crashlytics triage uses manual_review_required; the AdMob optimizer says "hold the last 24-hour recommendation." Both tell the operator exactly what to do.
Fourth, save every demotion log and review them monthly. Aggregating "why did we drop to Tier 1?" over a month reveals structural issues. In my case, I noticed Tier 1 demotions clustering in specific hours, added an additional prompt-compression step during those hours, and reduced Tier 1 demotion frequency by 30%.
Fifth, do not rush promotions. Demote fast; promote slow. My controller uses a 5-minute continuous-stability requirement, but some agents I run extend that to 10 or 15 minutes.
Since shipping this architecture, my AI-agent-related on-call pages have dropped from 11 per month to 2.3 per month. As an artist who has been honored with 17 international art awards, I have come to believe that what truly defines completeness — whether in artwork or in production systems — is how something behaves when broken. Tier-based fallback takes about 3x the initial engineering cost of a single-fallback design, but the long-term operational stability pays it back many times over. If you are also running multiple production agents as an indie developer, I would encourage you to seriously evaluate the 4-tier model.
Thanks for reading.
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.