cd /news/artificial-intelligence/ground-control-from-google-ai-studio… Β· home β€Ί topics β€Ί artificial-intelligence β€Ί article
[ARTICLE Β· art-54490] src=dev.to β†— pub= topic=artificial-intelligence verified=true sentiment=↑ positive

Ground Control: From Google AI Studio Prototype to Local Production with Antigravity 2.0

Antigravity 2.0, a multi-surface release including a desktop app, CLI, SDK, and enterprise cloud, bridges the gap between Google AI Studio prototypes and production deployment. The lifecycle export feature converts entire multi-agent prototypesβ€”including agent architecture, prompts, tool configurations, and handoff contractsβ€”into a declarative manifest that can be version-controlled and deployed locally. This enables teams to move from sandbox to production without losing fidelity or requiring manual reconstruction.

read19 min views1 publishedJul 10, 2026

This guide targets

Antigravity 2.0β€” the

four-surface release (desktop app,agy

CLI,google-antigravity

SDK, and

enterprise cloud) that shares one agent harness β€” together with theAI Studiothat shipped alongside it. The SDK is pre-v1.0; symbol

lifecycle export

names, bundle fields, and CLI flags below reflect the documented API as of

mid-2026. Treat thepatternsas stable and re-check exact signatures against

the current docs before you ship.

Every AI engineering team knows this moment. Someone spends an afternoon in

Google AI Studio and comes back with something genuinely impressive: a

multi-agent app that classifies incoming support tickets, investigates the

customer's history, checks refund policy, and drafts a resolution β€” with typed

handoffs between agents and a sandbox transcript to prove it works. The demo

kills. Leadership asks the only question leadership ever asks: "When can this be in production?"

And then the project stalls, because between the sandbox and production sits a

canyon. On one side: a cloud playground optimized for iteration speed β€” prompts

in text boxes, tools declared in a UI, model parameters behind a slider, test

runs you eyeball. On the other side: everything production actually demands β€”

version control, typed contracts, security policy, audit logs, CI, and tool

implementations that hit your databases, not a mock.

Historically, teams escaped the canyon in one of two bad ways:

Both failure modes share a root cause: the prototype was never an artifact.

It was a pile of UI state, and UI state doesn't survive contact with a

repository.

Antigravity 2.0 closes the canyon from both ends. AI Studio's lifecycle export turns the entire prototype β€” agent architecture, prompts, tool

agy

CLIgoogle-antigravity

This tutorial walks the full bridge, end to end, with a real workload:

TriageDesk, a four-agent support-ticket triage system. We'll prototype it

in AI Studio, export it in one click, scaffold a local workspace with exact

terminal commands, and then wire it into a production execution harness with

typed contracts, default-deny policy, and a parity gate that replays the

sandbox transcripts as regression tests.

The word doing the heavy lifting in "lifecycle export" is lifecycle. It is

not a chat log, and it is not "the prompt." An agent lifecycle is the complete,

declarative description of a multi-agent system β€” everything needed to

reproduce its behavior, and nothing tied to the surface it happens to run on:

Lifecycle component In the AI Studio sandbox In your local codebase
Agent architecture
Nodes on the app canvas
lifecycle.json manifest (agents + edges)
Prompts
System-instruction text boxes
prompts/*.md , one file per agent, checksummed
Tool configurations
Function declarations in the UI
tools/tools.json (interfaces) + your Python implementations
Handoff contracts
Structured-output schemas
schemas/handoffs.json β†’ Pydantic models
Generation parameters
Temperature / top-p sliders Pinned in the manifest, applied by the harness
Evidence
Sandbox test runs you eyeballed
eval/golden.jsonl β†’ replayable parity tests

Read that table column by column and you can see the whole thesis of this

article: every piece of sandbox state has a canonical home in a repository.

The export is a projection from one column to the other. Nothing is

reconstructed from memory; nothing is lossy.

   GOOGLE AI STUDIO (cloud sandbox)                LOCAL WORKSPACE (production)
  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
  β”‚  canvas: 4 agents, 2 edges   β”‚               β”‚  bridge/harness.py   policy  β”‚
  β”‚  prompts in text boxes       β”‚   1-click     β”‚  bridge/lifecycle.py   β”‚
  β”‚  tool declarations (UI)      β”‚   export      β”‚  bridge/tools.py     impls   β”‚
  β”‚  output schemas (UI)         β”‚ ───────────▢  β”‚  bridge/schemas.py   typed   β”‚
  β”‚  temp/top-p sliders          β”‚  .agy bundle  β”‚  bridge/orchestrator.py      β”‚
  β”‚  sandbox test transcripts    β”‚               β”‚  bridge/parity.py    gate    β”‚
  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                 β–²                                              β”‚
                 β”‚            agy studio pull / diff            β”‚
                 └──────────────── parity β—€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

The reason "rewrite from screenshots" fails isn't laziness β€” it's that agent

behavior is absurdly sensitive to details humans don't transcribe faithfully.

A prompt that lost a trailing instruction in copy-paste. A temperature of 0.7

where the sandbox ran 0.2. A tool schema where priority

silently became a

string instead of an enum. Each one changes behavior; none of them shows up in

a code review, because there's no "before" to diff against.

So we treat parity as a contract with four clauses, each mechanically

checkable:

Resolution

disagrees with the sandbox Resolution

on a golden ticket, the deploy gate fails.Everything we build in Steps 3 and 4 exists to enforce one of those four

clauses. Keep the table and the contract in your head; the rest is

implementation.

This is a tutorial about leaving the sandbox, so we'll keep the sandbox part

brief β€” but the shape of the prototype matters, because a well-structured

prototype exports cleanly and a mushy one doesn't.

In AI Studio, create a new multi-agent app and lay out four agents:

TicketClass

: category (billing

/ technical

/ account

/ other

), severity, and a one-line summary.TicketClass

; declares two function tools, search_tickets

(prior tickets from this customer) and read_account

(plan, billing history). Emits an Investigation

.read_policy

. Emits a PolicyVerdict

β€” what the refund/SLA policy permits for this category.Investigation

and PolicyVerdict

and emits the final Resolution

: an action, a customer-facing reply draft, and an escalate

flag with rationale.Wire the edges on the canvas: Classifier fans out to Investigator and Policy

Checker (parallel branch), which join into Resolver. A diamond.

Three habits at this stage pay for themselves at export time:

schemas/handoffs.json

in the bundle, and typed handoffs are what make the local orchestrator deterministic. An agent without an output schema exports as a prose-emitter, and you'll pay for it later with regex.eval/golden.jsonl

: your regression suite, for free.Tune the temperature down to 0.2 for the Classifier and Policy Checker (you

want determinism), leave the Resolver a little warmer for reply drafting, and

iterate until the diamond behaves. That's the prototype. Total UI time: an

afternoon. Now let's make it an artifact.

In the app's toolbar: Deploy β–Έ Export lifecycle. AI Studio bundles the

entire app definition into a single archive β€” triagedesk.agy.zip

β€” and offers

two delivery paths: a direct download, or a push to your project's export

registry that the CLI can pull by app ID (we'll use both in Step 3).

Unzip it and look around; this bundle is the contract between the two worlds:

triagedesk.agy.zip
β”œβ”€β”€ lifecycle.json            # the manifest β€” architecture, params, checksums
β”œβ”€β”€ prompts/
β”‚   β”œβ”€β”€ classifier.md         # byte-exact system instructions, one per agent
β”‚   β”œβ”€β”€ investigator.md
β”‚   β”œβ”€β”€ policy_checker.md
β”‚   └── resolver.md
β”œβ”€β”€ tools/
β”‚   └── tools.json            # function DECLARATIONS (JSON Schema) β€” no impls
β”œβ”€β”€ schemas/
β”‚   └── handoffs.json         # structured-output contracts between agents
└── eval/
    └── golden.jsonl          # your approved sandbox runs: input β†’ typed output

The manifest is the piece worth reading line by line:

{
  "format": "antigravity.lifecycle/v1",
  "name": "triagedesk",
  "exported_from": "aistudio://apps/triagedesk-8f3a21",
  "exported_at": "2026-07-08T14:32:00Z",
  "model_defaults": {
    "model": "gemini-3-pro",
    "generation": { "temperature": 0.2, "top_p": 0.95, "max_output_tokens": 2048 }
  },
  "agents": [
    { "id": "classifier",
      "prompt": "prompts/classifier.md",
      "tools": [],
      "output_schema": "schemas/handoffs.json#/TicketClass" },
    { "id": "investigator",
      "prompt": "prompts/investigator.md",
      "tools": ["search_tickets", "read_account"],
      "output_schema": "schemas/handoffs.json#/Investigation" },
    { "id": "policy_checker",
      "prompt": "prompts/policy_checker.md",
      "tools": ["read_policy"],
      "output_schema": "schemas/handoffs.json#/PolicyVerdict" },
    { "id": "resolver",
      "prompt": "prompts/resolver.md",
      "tools": [],
      "generation": { "temperature": 0.6 },
      "output_schema": "schemas/handoffs.json#/Resolution" }
  ],
  "edges": [
    { "from": "classifier", "to": ["investigator", "policy_checker"], "mode": "parallel" },
    { "from": ["investigator", "policy_checker"], "to": "resolver", "mode": "join" }
  ],
  "checksums": {
    "prompts/classifier.md":     "sha256:1f9c2e…",
    "prompts/investigator.md":   "sha256:aa07d1…",
    "prompts/policy_checker.md": "sha256:83b4f0…",
    "prompts/resolver.md":       "sha256:c95a72…"
  }
}

What to notice, because each field maps to a parity clause from Section 2:

model_defaults

  • per-agent generation

overrideschecksums

agents[].tools

  • tools/tools.json

read_account

with hand-typed JSON; your production read_account

will hit the real billing database with real credentials that never touch a bundle file.edges

eval/golden.jsonl

{agent_inputs, expected_output}

pairs.One click, and the prototype stopped being UI state. It's an artifact. Now we

bring it home.

Everything in this section is exact terminal commands. First, the two surfaces

you'll use locally β€” the CLI (agy

, the Go-based terminal harness) and the

SDK (google-antigravity

, the same harness as a Python library):

curl -fsSL https://antigravity.google/cli/install.sh | bash


agy models

export GEMINI_API_KEY="your_key_here"

agy init

with the studio-import

template creates a workspace pre-shaped for

an ingested lifecycle β€” an export/

directory the bundle lands in, a bridge/

package for the production wiring, and a .agy/

config that tells the harness

where the manifest lives:

agy init triagedesk --template studio-import
cd triagedesk

python -m venv .venv && source .venv/bin/activate
pip install google-antigravity pydantic python-dotenv

Two equivalent paths. If you downloaded the zip:

agy studio import ~/Downloads/triagedesk.agy.zip

Or skip the download entirely and pull from AI Studio's export registry by app

ID β€” this is the path you'll automate later, because it means "re-sync the

prototype" is one non-interactive command:

agy studio pull --app triagedesk-8f3a21

Either way, the CLI unpacks the bundle, records its provenance (exported_from

,

exported_at

) in .agy/lifecycle.lock

, and your workspace now looks like this:

triagedesk/
β”œβ”€β”€ .agy/
β”‚   └── lifecycle.lock        # provenance pin: which export this workspace tracks
β”œβ”€β”€ export/
β”‚   └── triagedesk/           # the ingested bundle, verbatim (treat as read-only)
β”‚       β”œβ”€β”€ lifecycle.json
β”‚       β”œβ”€β”€ prompts/  tools/  schemas/  eval/
β”œβ”€β”€ bridge/                   # YOUR code β€” the production wiring (Step 4)
β”œβ”€β”€ main.py
└── requirements.txt

The discipline embedded in that layout: ** export/ is read-only, regenerated by re-import; bridge/ is yours, written once.** Prompt changes happen in AI

export/

in place β€” thatThree commands before you write any Python:

agy lifecycle validate

agy lifecycle diff --against studio

agy run --lifecycle export/triagedesk/lifecycle.json \
    --mock-tools export/triagedesk/eval/golden.jsonl \
    -p "Ticket: 'I was charged twice for my Pro plan this month.'" \
    --output-format json

Unpacking the flags on that last command, because it's doing something subtle:

--lifecycle

--mock-tools

--output-format json

Resolution

on stdout, pipeable into jq

or CI.If step 9 prints the same Resolution

the sandbox produced for that ticket,

the bridge held: the prototype is now running on your laptop, byte-identical

prompts, pinned parameters, same harness. What it doesn't have yet is

production β€” real tools, real policy, real telemetry. That's Step 4.

The CLI proved the lifecycle is portable. The SDK is where it becomes

deployable: a Python package (bridge/

) that loads the exported manifest

into governed agent configs, binds real tool implementations against the

exported interfaces, drives the diamond with typed handoffs, and gates deploys

behind a golden-transcript replay.

Every agent in the lifecycle gets minted through one factory with one set of

guardrails. Prompts and parameters come from the bundle; policy and

observability come from here. That separation is the production boundary in

one sentence: AI Studio decides what the agents say; your harness decides what they're allowed to do.

import os
from typing import Callable, Sequence

from google.antigravity import LocalAgentConfig
from google.antigravity.hooks import hooks, policy

BASE_POLICIES = [
    policy.allow("search_tickets"),
    policy.allow("read_account"),
    policy.allow("read_policy"),
    policy.deny("run_command"),   # a triage agent never shells out
    policy.deny("write_file"),    # …or writes to disk
    policy.deny("*"),             # default-deny: anything undeclared is refused
]

class AuditTrailHook(hooks.PostToolCallHook):
    """One structured audit line per tool call, in every agent of the diamond."""
    async def run(self, context: hooks.HookContext, data) -> None:
        print(
            f"[audit] trajectory={getattr(context, 'trajectory_id', '?')} "
            f"tool={getattr(data, 'name', '?')} ok={getattr(data, 'ok', True)}"
        )

def build_agent_config(
    *,
    system_instructions: str,
    tools: Sequence[Callable] = (),
    model: str,
    generation: dict | None = None,
) -> LocalAgentConfig:
    kwargs = dict(
        model=model,
        system_instructions=system_instructions,
        tools=list(tools),
        policies=BASE_POLICIES,      # the production floor β€” non-negotiable
        hooks=[AuditTrailHook()],    # telemetry on every node
    )
    if generation:
        kwargs["generation"] = dict(generation)   # parameter parity, applied
    if os.getenv("VERTEX") == "1":
        kwargs["vertex"] = True
    elif os.getenv("GEMINI_API_KEY"):
        kwargs["api_key"] = os.environ["GEMINI_API_KEY"]
    return LocalAgentConfig(**kwargs)

The design decision that matters: BASE_POLICIES

is not part of the

export, on purpose. Policy is an attribute of the environment, not the

prototype β€” the same lifecycle might run with generous policies in staging

and airtight ones in production. The bundle can name tools; only the harness

can grant them.

The bundle shipped tools/tools.json

: three function declarations with full

JSON Schema parameters. Locally, you write the real implementations and

validate them against the exported declarations at import time. This is

interface parity as executable code β€” if AI Studio exported

read_account(customer_id: string)

and you wrote

read_account(account_id: str)

, you find out at startup, not in an incident

review.

import inspect
import json
from pathlib import Path

BUNDLE = Path(__file__).parent.parent / "export" / "triagedesk"

def search_tickets(customer_id: str, days: int = 90) -> str:
    """Return this customer's tickets from the last `days` days, as JSON."""
    from .backends import ticket_store            # real system, injected here
    return json.dumps(ticket_store.recent(customer_id, days=days))

def read_account(customer_id: str) -> str:
    """Return the customer's plan, billing history, and account flags."""
    from .backends import billing
    return json.dumps(billing.account_summary(customer_id))

def read_policy(category: str) -> str:
    """Return the support policy text applicable to the given ticket category."""
    from .backends import policy_docs
    return policy_docs.for_category(category)

TOOL_REGISTRY = {
    "search_tickets": search_tickets,
    "read_account": read_account,
    "read_policy": read_policy,
}

def validate_registry() -> None:
    """Interface parity: every exported declaration must have a local impl
    whose signature matches the exported JSON Schema. Fail at startup."""
    declared = json.loads((BUNDLE / "tools" / "tools.json").read_text())
    for decl in declared["functions"]:
        impl = TOOL_REGISTRY.get(decl["name"])
        if impl is None:
            raise RuntimeError(f"exported tool '{decl['name']}' has no local implementation")
        got = set(inspect.signature(impl).parameters)
        want = set(decl["parameters"]["properties"])
        if got != want:
            raise RuntimeError(
                f"signature drift on '{decl['name']}': bundle declares {sorted(want)}, "
                f"local implementation has {sorted(got)}"
            )

Line by line, the load-bearing parts:

ticket_store

, billing

, policy_docs

). This is the code the sandbox validate_registry()

compares Python signatures to exported JSON Schema properties.account_id

and the tool keeps erroring" three weeks after launch.Now the centerpiece: a that reads lifecycle.json

and mints one

governed LocalAgentConfig

per agent β€” verifying prompt checksums as it goes.

This function is the bridge.

import hashlib
import json
from pathlib import Path

from google.antigravity import LocalAgentConfig

from .harness import build_agent_config
from .tools import BUNDLE, TOOL_REGISTRY, validate_registry

class ParityError(RuntimeError):
    """The local bundle no longer matches what AI Studio exported."""

def _verify_checksum(root: Path, rel: str, expected: str) -> None:
    digest = hashlib.sha256((root / rel).read_bytes()).hexdigest()
    if f"sha256:{digest}" != expected:
        raise ParityError(
            f"prompt drift in {rel}: bundle expects {expected[:19]}…, "
            f"file hashes to sha256:{digest[:12]}…. Re-import the export; "
            f"never hand-edit export/."
        )

def load_lifecycle(bundle: Path = BUNDLE) -> dict[str, LocalAgentConfig]:
    manifest = json.loads((bundle / "lifecycle.json").read_text())
    validate_registry()                              # interface parity gate

    defaults = manifest["model_defaults"]
    configs: dict[str, LocalAgentConfig] = {}
    for spec in manifest["agents"]:
        _verify_checksum(bundle, spec["prompt"],     # prompt parity gate
                         manifest["checksums"][spec["prompt"]])
        configs[spec["id"]] = build_agent_config(
            system_instructions=(bundle / spec["prompt"]).read_text(),
            tools=[TOOL_REGISTRY[name] for name in spec["tools"]],
            model=spec.get("model", defaults["model"]),
            generation={**defaults.get("generation", {}),
                        **spec.get("generation", {})},   # parameter parity
        )
    return configs

Walk it:

validate_registry()

runs before any agent is built_verify_checksum

on every prompt.ParityError

message tells the operator the dict[str, LocalAgentConfig]

β€” means the rest of the system addresses agents by their classifier

, investigator

, …). The names on the AI Studio canvas are now the names in your stack traces. Small thing; enormous debugging dividend.The bundle's schemas/handoffs.json

defines what flows along each edge. In

Python, those become Pydantic models β€” the same contracts, now enforced by the

harness's structured-output machinery:

from enum import Enum
from pydantic import BaseModel, Field

class Category(str, Enum):
    BILLING = "billing"; TECHNICAL = "technical"; ACCOUNT = "account"; OTHER = "other"

class TicketClass(BaseModel):
    category: Category
    severity: int = Field(ge=1, le=4)          # 1 = critical … 4 = low
    summary: str

class Investigation(BaseModel):
    customer_id: str
    prior_tickets: int
    account_standing: str
    relevant_facts: list[str]

class PolicyVerdict(BaseModel):
    policy_section: str
    permitted_actions: list[str]
    requires_human_approval: bool

class Resolution(BaseModel):
    action: str
    reply_draft: str
    escalate: bool
    rationale: str

The manifest's edges

said: classify, then fan out in parallel, then join.

The production orchestrator encodes exactly that β€” and nothing else. All the

intelligence lives in the exported prompts; this file is just the topology,

made explicit and typed.

import asyncio
import os
from typing import Type, TypeVar

from google.antigravity import Agent
from pydantic import BaseModel

from .lifecycle import load_lifecycle
from .schemas import Investigation, PolicyVerdict, Resolution, TicketClass

T = TypeVar("T", bound=BaseModel)

AGENTS = load_lifecycle()          # parity gates run HERE, at import time

async def run_once(config, prompt: str, schema: Type[T]) -> T:
    """One isolated agent, one typed turn, then tear down."""
    async with Agent(config) as agent:
        resp = await agent.chat(prompt, response_schema=schema)
        return await resp.parsed()               # a validated Pydantic instance

async def triage(ticket_text: str, customer_id: str) -> Resolution:
    tclass = await run_once(
        AGENTS["classifier"],
        f"Classify this support ticket.\n\nTICKET:\n{ticket_text}",
        TicketClass,
    )

    shared = (
        f"TICKET:\n{ticket_text}\n\ncustomer_id: {customer_id}\n"
        f"classification: {tclass.model_dump_json()}"
    )
    investigation, verdict = await asyncio.gather(
        run_once(AGENTS["investigator"],
                 f"Investigate. Use your tools.\n\n{shared}", Investigation),
        run_once(AGENTS["policy_checker"],
                 f"Determine what policy permits.\n\n{shared}", PolicyVerdict),
    )

    return await run_once(
        AGENTS["resolver"],
        "Draft the resolution from these independent findings.\n\n"
        f"Investigation: {investigation.model_dump_json(indent=2)}\n"
        f"PolicyVerdict: {verdict.model_dump_json(indent=2)}",
        Resolution,
    )

async def triage_batch(tickets: list[tuple[str, str]]) -> list[Resolution]:
    limit = int(os.getenv("TRIAGE_CONCURRENCY", "8"))
    sem = asyncio.Semaphore(limit)               # bound live model sessions

    async def _guarded(t: tuple[str, str]) -> Resolution:
        async with sem:
            return await triage(*t)

    return await asyncio.gather(*(_guarded(t) for t in tickets))

The parts worth reading twice:

AGENTS = load_lifecycle()

at module import.ParityError

naming the exact file, instead of serving subtly-wrong triage for a week.run_once

opens a fresh Agent

per node.async with Agent(config)

is a full harness session with its own isolated context window. The Investigator's tool output never pollutes the Policy Checker's reasoning β€” the same state isolation the sandbox gave the diamond, reproduced locally.asyncio.gather

is the manifest's "mode": "parallel"

edge, verbatim.max(investigate, policy)

, not the sum β€” identical to how the AI Studio canvas ran it.response_schema=schema

on every turn.resp.parsed()

hands back a validated instance. Fan-in stays deterministic.triage_batch

Last clause of the contract: behavioral parity. The bundle's eval/golden.jsonl

holds every sandbox run you approved. Replay them through the local system β€”

real harness, real policy, mocked tools (so backends don't flake the gate) β€”

and compare the structured fields that matter:

import asyncio
import json

from .orchestrator import triage
from .tools import BUNDLE

async def replay_golden() -> tuple[int, int]:
    """Replay approved sandbox transcripts against the local lifecycle.
    Returns (passed, total). Wire this into CI as the deploy gate."""
    passed = total = 0
    for line in (BUNDLE / "eval" / "golden.jsonl").read_text().splitlines():
        case = json.loads(line)
        total += 1
        local = await triage(case["ticket"], case["customer_id"])
        expected = case["expected"]                     # sandbox Resolution
        ok = (local.action == expected["action"]
              and local.escalate == expected["escalate"])
        passed += ok
        print(f"[parity] {case['id']}: {'PASS' if ok else 'FAIL'}")
    return passed, total

if __name__ == "__main__":
    p, t = asyncio.run(replay_golden())
    raise SystemExit(0 if p == t else 1)                # CI-friendly exit code

Note what we compare: action

and escalate

β€” the decisions β€” not

reply_draft

, which is legitimately creative at temperature 0.6. A parity gate

that string-matches prose will cry wolf until someone deletes it; a gate that

checks decisions earns its place in CI. python -m bridge.parity

exits nonzero

on any regression, which makes the deploy pipeline one line:

agy lifecycle diff --against studio && python -m bridge.parity && ./deploy.sh

Read that line back slowly, because it's the whole article: the cloud hasn't drifted from the bundle, the local system still behaves like the approved sandbox runs, therefore ship. Three worlds β€” AI Studio, the repository, and

import asyncio, json, sys
from bridge.orchestrator import triage

async def main():
    ticket = sys.stdin.read().strip() or "I was charged twice for my Pro plan."
    resolution = await triage(ticket, customer_id="cus_4821")
    print(json.dumps(resolution.model_dump(), indent=2))

asyncio.run(main())
bash
$ echo "I was charged twice for my Pro plan this month." | python main.py
[audit] trajectory=tj_01 tool=search_tickets ok=True
[audit] trajectory=tj_01 tool=read_account ok=True
[audit] trajectory=tj_02 tool=read_policy ok=True
{
  "action": "refund_duplicate_charge",
  "reply_draft": "Hi β€” you're right, and I'm sorry: our records confirm a duplicate charge…",
  "escalate": false,
  "rationale": "Billing history confirms duplicate charge on 2026-07-02; policy Β§4.2 permits immediate refund of verified duplicates without approval."
}

Same prompts as the sandbox, byte-for-byte. Same parameters, applied by the

harness. Same diamond, now with real tools, default-deny policy, an audit line

per tool call, and a regression suite that was generated by using the prototype. The distance from demo to this was four files of wiring β€” and none

The canyon between "look what I built in AI Studio" and "it's in production"

was never really about code. It was about state that lived in a UI β€” and

every team that lost a week reconstructing a prototype from screenshots, or

shipped a playground behind a proxy and prayed, was paying interest on that

one architectural debt.

Antigravity 2.0's answer is to make the prototype an artifact with a defined

projection into a repository. The lifecycle export captures architecture,

prompts, tool interfaces, handoff schemas, parameters, and evidence in one

bundle. The ** agy CLI** makes ingesting, validating, and drift-checking that

The deeper shift is organizational. When export is one click and ingestion is

one command, the prototype stops being a throwaway and becomes the first commit β€” product-minded builders iterate in AI Studio, engineers govern the

Prototype where iteration is cheapest. Productionize where governance is

strongest. With a checksummed bridge between them, you no longer have to

choose β€” and the next time leadership asks "when can this be in production?",

the honest answer is: it already compiled.

Google Cloud Credits are provided for this project. #AgenticArchitectSprint #Antigravity

agy

):

── more in #artificial-intelligence 4 stories Β· sorted by recency
── more on @google ai studio 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/ground-control-from-…] indexed:0 read:19min 2026-07-10 Β· β€”