◉ANTIGRAVITY LABJP
Articles/Tips & Best Practices
✦ Tips & Best Practices/2026-09-02Intermediate

The work-root guard I run before letting an agent near several repositories

When several repositories share one machine, an agent's working root is remarkably easy to mix up. Here is a small guard that compares resolved paths, tested against seven inputs.

antigravity466agents146bashworkflow57safety

✦ Premium Article

Disk space was running low, so I wrote a small routine to clear out stale checkouts. I am working in this one repository, so keep it and remove the siblings. That was the reasoning behind this line:

for OTHER in /tmp/repos/*; do
  [ "$OTHER" = "$WORK" ] && continue
  rm -rf "$OTHER"
done

As an indie developer maintaining iOS and Android apps alongside several site repositories, I usually have four or five checkouts sitting next to each other. This line was meant to protect exactly one of them.

It protected none. A single trailing slash on $WORK is enough for that comparison to never match.

Three ways a mix-up gets in

Working-root mix-ups do not announce themselves. They arrive through quiet mismatches: strings that differ while the underlying directory is the same, or the reverse. I walked into three of them.

Notation drift

/tmp/repos/siteA and /tmp/repos/siteA/ are different strings to the shell. Once two places in your code build that path, a trailing slash survives in one of them.

Symlink aliases

A convenience alias pointing at a checkout will never string-match the directory it points to.

Inherited depth

You may intend to hand the agent a repository root, but if the previous task descended into src/, that position quietly becomes the working root for the next one.

Watching it happen in a sandbox

Guessing at a fix usually shifts the problem somewhere else, so I reproduced the behaviour in a throwaway directory: siteA, siteB, and a symlink pointing at siteA.

T=$(mktemp -d); cd "$T"
mkdir -p repos/siteA repos/siteB
ln -s "$T/repos/siteA" repos/siteA-link
 
WORK="$T/repos/siteA/"          # trailing slash
for OTHER in "$T"/repos/*; do
  if [ "$OTHER" = "$WORK" ]; then echo "  skip(self): $OTHER"
  else echo "  DELETE: $OTHER"; fi
done

The output:

  DELETE: /tmp/tmp.vFHcRXrU5D/repos/siteA
  DELETE: /tmp/tmp.vFHcRXrU5D/repos/siteA-link
  DELETE: /tmp/tmp.vFHcRXrU5D/repos/siteB

The checkout I meant to keep is first in the delete list. skip(self) never printed once.

Running the same loop after normalising both sides through realpath changes the outcome:

WORK_R=$(realpath "$WORK")
for OTHER in "$T"/repos/*; do
  OR=$(realpath "$OTHER")
  if [ "$OR" = "$WORK_R" ]; then echo "  skip(self): $OTHER -> $OR"
  else echo "  DELETE: $OTHER -> $OR"; fi
done
  skip(self): .../repos/siteA      -> .../repos/siteA
  skip(self): .../repos/siteA-link -> .../repos/siteA
  DELETE:     .../repos/siteB      -> .../repos/siteB

The trailing slash is gone, the symlink is resolved, and both entry points to siteA land on the keep side. Resolve once before you compare — that is the whole idea.

✦

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
✦You will be able to spot, on your own machine, the conditions under which an agent's working root gets mixed up with a sibling checkout
✦You will be able to add resolved-path matching to your cleanup routine before it deletes a checkout you meant to keep
✦You will be able to adapt a guard that returns the intended exit code for seven different inputs to your own repository layout
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

✦ Tips2026-05-01
Drafting Bilingual App Release Notes from Your Commit History with Antigravity
A practical way to draft App Store and Google Play release notes in Japanese and English from your Git history with Antigravity: the prompt shapes, how to think about character limits, and how to spin off social copy.
◈ Agents & Manager2026-09-04
Snapshot Your Agents' Effective Scope So You Notice the Day a Default Changes
Antigravity 1.1.25 made Markdown-defined custom agents inherit the surrounding skills, rules, and subagents by default. Here is the snapshot-and-diff routine I now use to check whether an agent I meant to keep narrow quietly got wider after an update.
◈ Agents & Manager2026-05-10
Defining 'Done' with Antigravity Agents: Writing Acceptance Criteria into Your Prompts
When Antigravity returns code that is only halfway working, the usual cause is a missing Definition of Done. Here is the three-layer fix.