Skip to content

agy/Antigravity quota policy (FLT-48)

Council-approved 2026-07-14 (report: llm-council/reports/2026-07-14_222458_fleet-routing-policy-question-context-researched.md; spec memory: agy-multimodel-fallback-quotas). Plan: Google AI Ultra Max $200/mo, 20x Pro tokens, 5-hour refresh + weekly cap, credits = paid overage only.

Pools

agy models split into three separately-capped pools:

Pool Models Nature Role
gemini Gemini 3.5 Flash (Low/Med/High), 3.1 Pro (Low/High) One unified pool, 20x multiplier Primary offload. Flash default; Pro High escalate-only
claude Claude Sonnet/Opus 4.6 (Thinking) Small fixed cap (~60% of a 5h window per conversation) Out-of-band interrupt budget — never a ladder rung
gptoss GPT-OSS 120B (Medium) Own small fixed cap Burst overflow behind Grok/local only; never arbiter

Corrected ladder: Fable → Opus/Sonnet → Codex → agy-Gemini → Grok → agy-GPT-OSS → local ollama, with agy-Claude alongside (not inside) the ladder.

Fleet-global single-writer ledger

agy-ledger.jsonl — append-only JSONL. All four PCs share ONE Google account, so all four must share ONE ledger: set config.json → agy.ledgerPath to the same UNC path on every machine (share the primary PC's ~/.claude/usage-governor dir). Empty = local file (single-PC fallback).

Atomic admit is the only sanctioned spend path. Appending under an exclusive lock serializes bytes, not decisions — a separate "gate" then "spend" lets four PCs all read the same snapshot, all decide "allowed", and all spend. That is precisely the fleet double-spend this ledger exists to prevent, so AgyLedger.Admit holds ONE exclusive handle across read → evaluate → append: the reservation IS the spend. It fails closed — if the lock can't be acquired (share offline, heavy contention), the caller is denied rather than admitted unmetered. Verified end-to-end: 8 concurrent agy-claude-gate processes against the 1-per-5h cap admit exactly one; 12 concurrent agy-admit calls against a 3-call budget admit exactly three.

Covers every agy consumer — offload subagents, /gemini-review, the council Gemini seat, the fleet-dispatch agy lane, and the weekly probe — because they all go through agy-wrap.ps1 / agy-claude-wrap.ps1, which admit via the CLI. Never call agy.exe directly from a skill: the policy depends on the wrappers being the only door.

Both wrappers launch agy.exe through ProcessStartInfo.ArgumentList (agy-run.ps1), never a cmd.exe /c string. Prompts are untrusted content — diffs, logs, agent text — and under cmd the characters % ! & | < > ^ still bite inside quotes.

Spend entries are weighted estimated tokens × model/thinking-mode weight (AgyModels), not call counts — short high-token prompts look cheap by count. Weights are coarse by design; budgets are the recalibrated quantity.

agy-Claude interrupt-budget gate

UsageGovernor agy-claude-gate (wrapped by agy-claude-wrap.ps1, which fails CLOSED). Triple gate:

  1. Native Claude tier is RED or BLACKOUT (from state.json).
  2. Reason code on the ceiling skip-list: native_red, review_gate, council_synth, incident.
  3. Non-empty --no-substitute justification (why Codex / Gemini 3.1 Pro High can't chair).

Then: pre-route token estimate (default cap 60k tokens — oversized one-shots force Gemini Pro High failover), Sonnet-first (Opus needs --opus-justification), and unit caps — 1 call per 5h window, ≤2/day, ≤5/week (all in agy config). Grants are ONE-SHOT: no follow-ups; incomplete results drop to Gemini 3.1 Pro (High), never a second Claude call.

The gate books the spend as the reservation, under the admit lock. An audit-only "grant" that left the spend for the wrapper to log afterwards let two concurrent gates both see zero calls and both burn the 1-per-5h budget. Consequences that follow from this and are deliberate: a failed call keeps its spend (retry-erosion guard), and a post-grant launch failure does not refund — otherwise a broken install could retry the reserve indefinitely.

No classifier can reach this lane. EvaluatePoolGate hard-rejects any Claude label with a pointer to this gate, so there is no "hard task → agy-Claude" path: the only door is an explicit agy-claude-gate invocation carrying a reason code. That is a constraint on the API, not just a policy note (FLT-15 council).

Reserves and floors

  • Unit reserve (the real emergency guarantee): the last claudeReserveWeeklyCalls (default 1) weekly agy-Claude call(s) are spendable only with --reason incident.
  • Gemini weekly floor: tapers linearly with remaining calendar exposure, ~40% at the weekly reset (Mon 00:00 UTC) down to ~15% just before the next reset. Remaining-below-floor ⇒ RED, and a call whose weight would breach the floor is denied up front.
  • GPT-OSS: soft floor only (warn, never deny on floor).
  • Budget projection: every admit denies if spent + thisCall would cross the 5h or weekly budget, not merely if the pool is already RED. Gating on the current tier rewards the proxy ("spend is under RED right now") while the goal is "don't overspend the pool" — one large Pro-High payload could otherwise walk a GREEN pool straight past 100% in a single admitted call.

Weekly-exhausted hard state

A quota-classified limit hit with weekly scope flips the pool to EXHAUSTED: hard-denied until the weekly reset, independent of the 5h refresh (no 5h thrash burning the remainder). When native Claude AND agy-Claude are both locked, fallback = prompt-hardened Gemini 3.1 Pro (High) + flag the work for retroactive Claude re-review.

Scope is inferred from the error text (AgyLimitClassifier.ClassifyScope), defaulting to 5h. The wrappers used to hardcode --scope 5h, which meant nothing automated could ever arm this state — a real weekly 429 silently degraded into a 5h ratchet, i.e. exactly the thrash the policy forbids. The default stays 5h because it is the conservative read: a weekly hard-deny inferred from a misread 5h error would lock the pool for days.

429-driven recalibration

429s are ground truth. UsageGovernor agy-limit (called automatically by the wrappers on failure) classifies stderr via AgyLimitClassifier:

  • quota + 5h → ratchet that pool's 5h budget to observed-spend × 0.8 (RecordLimitHit pattern), as a locked read-modify-write: concurrent 429s must not last-writer-win a looser budget back into place, because a ratchet that can be undone isn't a ratchet.
  • quota + weekly → weekly-exhausted hard state.
  • transport (timeouts, connection, 5xx) → logged for audit, budgets untouched.
  • unknown → logged, budgets untouched, classify manually. Guessing corrupts the ledger; misread lockout-as-transient is a retry storm.

Markers are word-boundary regexes, not substrings: a bare 429 matches ticket numbers and byte counts, and a bare capacity matches "disk capacity" — either would ratchet a real budget off an unrelated error.

agy.allowPaidOverage defaults to false. Flipping it to true is an explicit operator decision. Precisely what it softens:

State allowPaidOverage=false (default) =true
Gemini weekly floor breach deny allow
Projected 5h / weekly budget overshoot deny allow
Pool RED deny deny
Weekly EXHAUSTED (429 ground truth) deny deny

It is a floor and projection switch, not a master override: a pool that has actually been 429'd stays denied regardless, because that state is measured, not estimated.

Weekly probe

UsageGovernor agy-probe (Scheduled Task UsageGovernorAgyProbe, Mon 08:00, registered by install.ps1): runs agy models, diffs labels against the last snapshot in agy-probe-history.jsonl (Antigravity renames/requotas silently; metering regressed once in Mar 2026), then a tiny Flash-Low dry-run whose spend/failure feeds the ledger. Drift warns loudly; nothing auto-adjusts.

Machine-readable status (--json, FLT-15)

agy-status --json is the seam the fleet-harness escalation ladder (FLT-1) reads before it assumes the next rung is fresh. It is read-only: it books no spend and fires no probe, so a caller may poll it per routing decision.

UsageGovernor agy-status --json
{
  "schema": "agy-status/v1",
  "ledgerPresent": true,
  "pools": {
    "gemini": { "tier": "GREEN", "locked": false, "capProximity": 0.0014, "weeklyExhausted": false, ... }
  }
}

Consumers must honour three rules:

  • capProximity: null means UNKNOWN, not fine. Reading it as 0.0 re-creates the bug the ledger exists to prevent — advertising capacity nobody measured.
  • ledgerPresent: false forces every capProximity to null. An absent or unreachable ledger sums to zero spend, which is indistinguishable from a genuinely fresh pool. "Fresh" is the one answer a blind ledger must never give.
  • Only the three agy pools are metered. Codex, Grok and native Claude are other vendors' quotas the governor cannot see; a consumer must answer "unknown" for them rather than infer.

Proximity is distance to the budget we enforce, never to Google's real cap (there is no quota API): a 429 can still arrive at 0.2, and weeklyExhausted — a recorded 429 — is the only ground truth here. Denominators differ by pool kind: gemini/gptoss meter weighted spend, while the Claude reserve meters calls (its weighted budget is 0, so dividing spend by it is a divide-by-zero, not an answer).

The schema is versioned because it is cross-language. A consumer that does not recognise schema must treat the payload as unreadable rather than guess at renamed fields.

CLI quick reference

# Human-readable pool status; add --json for the machine contract above.
UsageGovernor agy-status

# Atomic check-then-charge. Exit 0 = admitted AND charged; exit 2 = denied (or gate broken).
UsageGovernor agy-admit --model "Gemini 3.1 Pro (High)" --est-tokens 50000 --consumer offload

# The agy-Claude interrupt budget. Exit 0 = granted AND charged; exit 2 = denied.
UsageGovernor agy-claude-gate --reason review_gate --model "Claude Sonnet 4.6 (Thinking)" `
    --est-tokens 20000 --no-substitute "chair seat needs Claude-family judgement"

# Unconditional record, for callers that cannot admit up front. Prefer agy-admit.
UsageGovernor agy-spend --model "Gemini 3.5 Flash (High)" --est-tokens 12000 --consumer offload

# --kind and --scope are inferred from --stderr when omitted.
UsageGovernor agy-limit --model "Gemini 3.1 Pro (High)" --stderr "<captured>"
UsageGovernor agy-probe

Option parsing is strict: unknown flags, missing values, and unparsable numbers exit 2 (deny) rather than silently defaulting — --model --est-tokens 5000 used to book an unmetered default-pool spend. Callers must treat any nonzero exit from a gate as a denial, not just 2; a crashed gate is not an admission. An unknown agy-* verb also exits 2, so a caller pinned to a removed command (the pre-FLT-48 agy-gate) fails closed and loudly instead of reading a vanished gate as permission.

Migrating from the split agy-gate + agy-spend

agy-gate is gone. It was the non-atomic half of the check-then-act the council flagged Critical: gate and spend as separate processes let concurrent callers all pass on one snapshot. Replace both calls with a single agy-admit — it evaluates and charges under one lock:

# BEFORE (racy, and agy-gate no longer exists - now exits 2 = deny)
& $gov agy-gate  --model $m --est-tokens $est; if ($LASTEXITCODE -eq 2) { <deny> }
& $gov agy-spend --model $m --est-tokens $est --consumer fleet-dispatch

# AFTER - one atomic call. Exit 0 = admitted AND charged; do NOT also call agy-spend (double-book).
& $gov agy-admit --model $m --est-tokens $est --consumer fleet-dispatch
if ($LASTEXITCODE -ne 0) { <deny - route to next ladder tier> }

Note the guard is -ne 0, not -eq 2: a crashed or missing gate must deny too.

Council family-diversity note: agy-Sonnet/Opus counts as a Claude seat in the ≥3-frontier-seats rule — same family judging native-Claude work hollows the council.

Coordination (FLT-6 / FLT-15, fleet-harness board)

fleet-harness/routing.yaml is the single source of truth for lane routing (roles → vendors, escalation ladder, review ladder). This repo deliberately does NOT define lanes: the AgyModels label→pool mapping is quota metering (which agy cap a call draws from), and the "route to Grok/local" strings in deny messages are advisory pointers, not a routing table. When routing.yaml grows the agy lanes (gemini-flash / gemini-pro-high / agy-gpt-oss / agy-claude-reserve, reordered ladder, family-distinct review seats — FLT-6 outstanding), the governor's deny messages should reference it rather than restating the ladder. agy-Claude must stay a governor-flag-only lane there: no keyword classifier may reach it (the only path is the explicit agy-claude-gate CLI with reason codes).

Known unmetered consumer: fleet-dispatch's gemini-via-agy adapter (fleet-dispatch/src/FleetDispatch.Core/AgentRun/) calls agy directly. It must be pointed at agy-wrap.ps1 (or call agy-spend/agy-gate itself) — tracked as follow-up work.