Why Antigravity Agents Build Your Xcode Project Fine One Day and Break It the Next on a Synced Folder
Put an Xcode project inside a Dropbox-synced folder, hand it to an Antigravity agent, and builds start passing and failing at random. Here is the real cause — sync daemon versus build — and how to fix it with xattr exclusion and a conflict-copy preflight.
One week, an Antigravity agent I run overnight started failing on roughly one run in three — same task every time. The logs showed a corrupted lockfile partway through pod install, or xcodebuild halting because a file that should exist could not be found. Running the exact same commands by hand worked every time, and the failure landed in a slightly different place on each bad run. When a failure refuses to reproduce and never lands twice in the same spot, the cause is almost always the environment, not the code.
I work as an indie developer with iOS apps on the App Store and companion Android builds on Google Play, and my project folder has lived in a Dropbox-synced setup across two Macs for years. Convenient — but it took me a while to notice that this sync is a poor match for unattended agent runs. The culprit was the sync daemon and the build touching the same directories at the same time.
The Same Command Passes and Fails on Alternating Days
The last thing to suspect is your own code or the agent itself. When the failure point moves around, never reproduces by hand, and clusters at certain times of day, suspect environmental non-determinism first.
On a cloud-synced folder, two processes fight over the same files:
Process
What it does
Directories it touches
Build (xcodebuild / pod install)
Creates and deletes intermediates by the second
build/, Pods/, DerivedData/, .swiftpm/
Sync daemon (Dropbox, etc.)
Detects changes and uploads / re-downloads them
The entire synced folder
The daemon starts uploading a file the build just wrote, the build overwrites that same file mid-upload, and the sync layer decides to keep both — producing a conflicted copy like Podfile.lock (Your Mac's conflicted copy 2026-06-27).lock. The next build grabs it and consistency falls apart. When you work interactively you rarely hit that exact instant, so it stays invisible. Run an agent many times a day, unattended, and the odds of landing on a conflict stop being negligible.
The Sync Daemon and the Build Are Fighting Over the Same Directory
The important point: this is not an Antigravity bug or a CocoaPods bug. Whatever agent and whatever build tool you use, the same race happens as long as you keep high-churn directories inside a file-sync scope.
The most fragile spots are metadata Xcode rewrites constantly such as .swiftpm/xcuserdata, dependency trees that get regenerated wholesale like Pods/, and intermediates like build/ or DerivedData/. All of these are "fine to disappear and be regenerated anytime," and there is no value in sharing them with another Mac. What you want to share is source code, not build debris.
✦
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
✦Learn to diagnose 'builds that fail at random' as a race between the sync daemon and the build, instead of chasing your own code
✦Take away a concrete recipe for excluding volatile directories (build / Pods / .swiftpm) from sync with xattr, including the Run Script that re-applies the mark after a clean build
✦Add a preflight that refuses to run the agent when conflicted copies are present, so unattended runs never grab a corrupted repository
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.
.gitignore Will Not Save You (Git and Sync Are Different Layers)
Here is where many people get caught: "it's in .gitignore, so it's fine." .gitignore only controls what Git commits. The Dropbox or iCloud sync daemon never reads .gitignore at all. A directory Git ignores is still dutifully uploaded by the daemon.
So the fix has to happen on a different layer — telling the sync daemon itself "do not sync this folder." On macOS, Dropbox offers a per-item extended attribute for exactly this.
Exclude Volatile Directories From Sync — Mark Them With xattr
Dropbox for macOS excludes any file or directory whose extended attribute com.dropbox.ignored is set to 1. Run this script once at the project root to exclude the volatile directories in one pass.
#!/bin/bash# mark-volatile-ignored.sh — exclude Xcode volatile dirs from Dropbox syncset -euo pipefail# run from the project rootTARGETS=( "Pods" "build" "DerivedData" ".swiftpm")# also include user-specific metadata inside *.xcodeproj / *.xcworkspacewhile IFS= read -r -d '' p; do TARGETS+=("$p")done < <(find . -type d \( -name "xcuserdata" -o -name "*.xcuserdatad" \) -print0)for t in "${TARGETS[@]}"; do if [ -e "$t" ]; then xattr -w com.dropbox.ignored 1 "$t" echo "ignored: $t" fidone
Confirm it took effect with the command below. A return value of 1 means the directory is out of sync scope.
xattr -p com.dropbox.ignored Pods# => 1
If you use iCloud Drive the mechanism differs: append .nosync to the folder name, or move build output outside the synced area entirely (for example ~/Library/Developer/Xcode/DerivedData). I keep build output outside the synced folder from the start, but for things that must sit at the project root — like CocoaPods' Pods/ — I rely on the xattr exclusion above.
A Clean Build Erases the Mark — Plug It With a Run Script
There is one more trap. The xattr mark lives on the directory, so a clean build that recreates the directory wipes the mark along with it. Set it once and relax, and conflicted copies quietly return after the next clean build.
The fix is to re-apply the mark on every build. Add a Run Script phase to your target that re-applies the exclusion early in the build.
# Build Phases > Run Script (uncheck "Based on dependency analysis")# re-stamp the ignored attribute onto freshly created directoriesfor d in "${PODS_ROOT:-$SRCROOT/Pods}" "$SRCROOT/.swiftpm" "$SRCROOT/build"; do [ -e "$d" ] && /usr/bin/xattr -w com.dropbox.ignored 1 "$d" 2>/dev/null || truedone
If you use CocoaPods, put the same logic in the post_install hook of your Podfile so the mark is applied right after pod install.
# Podfilepost_install do |installer| system("xattr -w com.dropbox.ignored 1 \"#{installer.sandbox.root}\"")end
With this in place, Pods/ is regenerated already outside sync scope every time you reinstall dependencies, so a lockfile turning into a conflicted copy mid-pod install becomes far less likely.
Inspect for Conflicted Copies Before Running the Agent
Even with exclusion in place, an old conflicted copy can linger in a corner of the folder. If the agent grabs it, you hit the same failure again. So before handing a task to the Antigravity agent, add a preflight that confirms there are no conflicted copies. The very first step of a scheduled run is the right place for it.
#!/bin/bash# preflight-no-conflicts.sh — refuse to run the agent if conflicted copies remainset -euo pipefail# catch Dropbox conflicted copies and iCloud-style duplicatesHITS=$(find . \ \( -iname "*conflicted copy*" \ -o -iname "* 2.lock" \ -o -iname "*.lock.sb-*" \) \ -not -path "*/.git/*" 2>/dev/null)if [ -n "$HITS" ]; then echo "🛑 Conflicted copies detected. Aborting the agent:" echo "$HITS" exit 1fiecho "✅ Clean — agent run permitted"
The key is to stop with exit 1. For unattended runs the default has to be "stop when in doubt," not "proceed when in doubt" — otherwise a corrupted state rides all the way through to a commit and a push. In automation like this I make a point of never continuing past a situation I am unsure about; I fail early on purpose. Surfacing the failure sooner makes the cleanup far lighter.
What to Keep in Sync, What to Exclude
Finally, the dividing line. When unsure, judge by two questions: "is it worth sharing with another Mac?" and "can it be regenerated if it disappears?"
Path
Sync
Reason
Source code (.swift, .kt, the .xcodeproj settings themselves)
Keep
Worth sharing; hand-written work
Podfile / Package.swift / *.lock
Keep
Pinned dependency info — but watch for mid-build overwrites
Pods/ / .build/ / build/
Exclude
Regenerable; high churn, a breeding ground for conflicts
DerivedData/
Exclude (ideally relocate outside sync)
Intermediates; no value in sharing
xcuserdata / *.xcuserdatad
Exclude
Personal editor state; expected to differ per Mac
Putting *.lock in the "keep" column is the awkward call. It pins dependency versions, so you want to share it — yet it is also the file most likely to conflict during a build. I settled on excluding Pods/ entirely and keeping Podfile.lock at the repository root (outside the volatile directories), shared via Git. Physically separating the responsibilities of the sync daemon and the build is the least breakable arrangement I have found.
If You Want to Try It on a Single App First
Fixing several apps at once makes it impossible to tell which change helped. Start with one project: mark Pods/ and .swiftpm with xattr -w com.dropbox.ignored 1, confirm xattr -p returns 1, then watch a few nights of agent runs. The failure rate should drop noticeably. From there, add the Run Script and the preflight to build a foundation that holds up even when it runs unattended.
Cloud sync is a strong ally for an indie developer, but it needs its turf kept separate from a build, where state changes by the second. Draw the boundary between what you trust to sync and what you trust to the build once, properly, and you can let an agent take the night shift with some peace of mind.
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.