Best practices for file sandboxing and strict JSON routing in Antigravity

Hi everyone,

I have a working, production-ready frontend single-page dashboard with complex internal UI state (animating SVGs, calculation engines, and specific tab components).

I am building an ingestion pipeline for an external client webhook (an omnichannel contact center platform firing JSON payloads over HTTP POST) that needs to persist events to Firestore and update the dashboard in real time.

When using an autonomous coding agent / agentic workflow (like Google Antigravity / Gemini Workspace tools) to implement the ingestion and wire up the incoming JSON fields, I ran into two major issues:

  1. Scope Bleed / File Overwriting: The agent had broad workspace write permissions and attempted to refactor the 6,000+ line frontend file (index.html), introducing regressions, splitting files, and creating mock test state instead of isolating changes.

  2. Non-Deterministic Routing: Because the incoming webhook payload sometimes omitted optional sub-properties (e.g., missing product_category), internal fallback logic misrouted sales data into generic interaction cards rather than updating the dedicated Sales tab metrics and state.

My questions for the community:

  1. Agent Sandboxing / Tooling: Are there recommended models, permission modes, or tool configurations in Gemini / Antigravity (e.g., read-only file locks, scoped context windows, diff-only patches) that strictly prevent an autonomous agent from touching existing frontend files while generating the backend service?

  2. Clean Separation of Concerns: What is the recommended architectural pattern when prompting Gemini for this setup? Is the standard practice to keep the backend ingestion service (Cloud Function / Firestore) in an entirely isolated repository/workspace, and then manually import a strictly scoped adapter function into the client?

  3. Deterministic Schema Mapping: What tools or libraries (e.g., Zod, JSON Schema validators) work best with Gemini function calling / code generation to guarantee strict routing (e.g., ensuring sale_amount > 0 always routes deterministically to sales state without triggering fuzzy fallback handlers)?

Would love to hear how other developers enforce strict code immutability and contract-driven adapters when working with agentic coding assistants.

Thanks!

First thing you should do is go outside of these specific questions. The agent is disposable. Your project is not. You’re asking the agent platform to provide boundaries that should already exist in the project.

Scope Bleed / File Overwriting: The agent had broad workspace write permissions and attempted to refactor the 6,000+ line frontend file (index.html), introducing regressions, splitting files, and creating mock test state instead of isolating changes.

6000+ line index.html… That’s a massive blast radius. If the agent has broad write access and sees this enormous file containing UI, state, calcs, SVG animation, tabs, apparently everything… If that agent decides to improve it, well, that’s a file that is going to probably get wrecked without isolating the features and subbing it out. The agent(s) are disposable.

Ensure index.html is versioned, committed to repo. Preferably push it somewhere private as well. If an agent goes helter skelter, restructures everything, deletes something important, or you discover three hours later that what it “fixed” broke five unrelated things, your recovery mechanism should be git. Not the agent remembering what it did, not the conversation history, not IDE timeline archaeology.

This should probably itself be minor milestones, probably three steps. First deliverable milestone should be a finalized isolation plan derived from analysis of the file by a single agent, back and forth with you, to determine what should and should not be touched. And isolating the fixes that need to be applied as local states.

Don’t fly through it with a single agent and change tabs, UI, states, back to UI, back to tabs, back to states, routing, then back into UI again. Instead have the agent analyze this, and come up with relational isolation of each separate need that must be addressed.

What owns what state? What functions mutate that state? Which UI pieces depend on it? Where does the webhook data actually enter the application’s state? What is shared? What is local? What should absolutely not be touched?

That first agent should not be implementing anything. Its output is the map and the isolation plan. You go back and forth with it until you agree with that plan.

Then once this is done and you approve that, the next step is to have each isolated goal addressing the needs distributed to several agents, one per need/goal, and for them to, in isolation, create a second working environment from the same known-good committed state and perform the edits there, leaving the pristine master index.html untouched.

Branches/worktrees are better than just making random copies if the rest of the project matters, but the idea is the same. Each agent gets the same starting state and one job. Not “fix this and anything else you notice.” One job.

From this, they should deliver a diff for their edits. Really the deliverable from each agent should be:

What goal it was given. What it changed. Why. The diff. And whatever test/evidence shows that its particular goal was actually accomplished.

Then the agent can disappear. It doesn’t matter. Again, disposable.

Once all the agents return you should have a stack of diffs, all produced against the same known-good state, rather than agent 4 now working on top of whatever agent 2 decided was a better architecture.

Now inspect each of those, and ask yourself, or use an external reviewing agent that is explicitly told not to edit anything: How do these diffs, once incorporated into the master index.html, each realize the goals they were assigned? Do those implementations of the goals properly address the needs together without breaking the operations of each? Do any of them touch things outside their assigned goal? Does one alter state assumptions another one depends on? Does diff B depend on behavior that diff A changes?

That reviewing agent also has the nice property that it didn’t write any of this. It has no implementation momentum and no reason to defend decisions it already made.

Then third milestone is integration. Go back to the pristine committed master. Apply one approved diff. Inspect it. Run it. Test that area. Commit it. Then move to the next one.

If B breaks because A changed some assumption it depended on, you find that at the boundary between two clean commits instead of after you’ve poured six agents worth of changes into the 6000 line and are now trying to figure out which one broke what.

Now, to actually answer your specific Q1 rather than only saying the platform controls are secondary, Antigravity does have controls you should use on top of this.

If you’re using Projects, use isolated worktrees/conversation environments for implementation work rather than letting every agent work directly against your active checkout. Use the project permissions to narrow the write surface. If an agent needs to understand the frontend but should not be modifying it, then letting it read the frontend and denying writes to index.html or the frontend directory is a much stronger boundary than writing “please do not modify index.html” in the prompt.

Same with terminal permissions. Only give the agent the command prefixes and network access it actually needs for that task.

Put the behavioral part in AGENTS.md as well, but don’t confuse the two. Do not modify index.html in AGENTS.md is an instruction. Actually denying write access to index.html is a boundary. Use both.

Strict/sandbox modes are useful too, particularly for stopping the agent wandering outside the workspace, but your problem here is that the crown jewels are unfortunately inside the workspace.

So yes, use the Antigravity controls. They’re useful. I just wouldn’t make them the architectural mechanism keeping the project alive.

Then your Q2, yes, I would physically separate the webhook ingestion service from the frontend. That would improve this quite a bit.

That does not necessarily mean it absolutely needs to be a different repository. A sane monorepo separation would already be a huge improvement, something like:

frontend/

functions/webhook-ingest/

shared/contracts/

Now the agent working on webhook ingestion does not need write access to frontend/ at all.

If you want an even harder boundary, then sure, make the ingestion service its own repo and let the frontend and backend communicate only through Firestore, Pub/Sub, API, whatever contract you decide on. That physically removes the frontend from the backend agent’s workspace, so it literally cannot decide your UI would look better after a spontaneous 1200 line refactor.

But I would not manually copy some adapter function back and forth between two repos. That’s just another place for things to drift. Define the contract once.

Your ingestion service should take the weird external vendor payload, validate it, normalize it, and produce your own canonical internal event. Something roughly relationally like:

external webhook payload
→ validate
→ normalize missing / optional values
→ create canonical internal event
→ persist / publish
→ frontend consumes canonical event/state

The frontend should not particularly care what weird combination of optional properties the external contact-center vendor decided to POST today.

Which also leads into Q3. If sale_amount > 0 means this belongs to sales state, Gemini should not have a vote on that at runtime. That’s deterministic application logic.

Zod, JSON Schema, etc. can validate the shape and help you normalize the incoming data, but the routing guarantee itself comes from your code. If product_category is absent, define what absent means. If sale_amount > 0 overrides some generic interaction classification, define that precedence explicitly. Don’t let “fuzzy fallback handlers” decide something that already has a deterministic business rule.

And, again, that improves the agent situation too. The agent implementing webhook ingestion only needs: the webhook service, the shared contract, the exact routing requirements, and its own tests.

It shouldn’t need to go around inside a 6000 line front end file figuring out what “sales” probably means from context.

Prosto, don’t ask the agent to be the architecture, the sandbox, the version control, the implementer, the contract, and the reviewer all at once. Harness the agent with those things.

The agent is disposable. The repo’s not.