The sandbox-to-Vercel ship loop — deploying when your AI agent can't push
AI coding sandboxes can edit your repo but usually can't push to GitHub. The reliable workflow — including the git-wall errors that look like bugs but aren't — for getting agent work live on Vercel.
You've been coworking with an AI agent. It edited twelve files, the tests pass, and you say "ship it." Then one of these appears:
error: could not lock ref ... .git/index.lock: Operation not permitted
fatal: could not read Username for 'https://github.com'
Permission denied (publickey)
These look like bugs. They're not — they're the git wall: most agent sandboxes (Cowork, cloud IDEs, containerized agents) mount your repo with file access but deliberately without your GitHub credentials. The agent can edit; it cannot push. This is a security feature. An agent with push rights to your production repo is one prompt injection away from being someone else's agent.
The wrong response — for you or the agent — is retrying the push fifteen ways or, worse, "fixing" things inside .git/. The right response is a clean handoff. Here's the loop we use to ship VibeShare.
The loop
1. Agent finishes and verifies in the sandbox. Type-check, lint, tests — whatever can run there. Note explicitly what couldn't run (some sandboxes can't execute the full build — missing platform binaries, blocked registries). Unverified steps move to post-deploy checks, they don't disappear.
2. Agent stages a handoff block, not a push. The agent's deliverable is the exact commands for you to run in your own terminal, where your credentials live:
cd ~/your-repo
git status # eyeball what the agent touched
git diff --stat # sanity-check the blast radius
git add -A
git commit -m "feat: <what the agent did>"
git push origin main
The two read-only commands at the top are the point. You are the review gate the sandbox can't be. If git status shows files you don't recognize, stop and ask the agent why before committing.
3. Vercel auto-deploys on push. If the repo is connected to Vercel, the push is the deploy. No CLI, no tokens in the sandbox, no extra infra.
4. Verify the deploy actually went live. "I pushed" is not "it deployed." Check three things:
# did the build succeed? (or check the Vercel dashboard)
npx vercel ls --yes 2>/dev/null | head -5
# is the new behavior live? curl something the change affects:
curl -s https://yoursite.com/sitemap.xml | head -20
# did the deploy break anything obvious?
curl -s -o /dev/null -w "%{http_code}" https://yoursite.com/
Pick a URL that only works if the new code is live — a new route, changed metadata, a new sitemap entry — not the homepage, which would have worked anyway.
Gotchas that waste afternoons
- Stale
index.lock. If a sandbox git command died mid-write, anindex.lockmay linger that the sandbox can't delete. Remove it from your own terminal:rm .git/index.lock. Never let the agent script destructive operations inside.git/. - The build passed locally but not on Vercel. Sandboxes often skip the production build (platform binaries, env vars). Pre-existing type errors that your dev loop tolerates will fail
next buildon Vercel. Runnpx tsc --noEmitin the sandbox at minimum, and treat any error — even "pre-existing" — as a deploy risk to flag. - Env-var drift. The agent tested against
.env.local; Vercel has its own env settings. Any change that reads a new env var needs that var added in the Vercel dashboard before the push. git add -Ais a trust decision. It commits everything the agent touched, including files you didn't discuss. Thegit statusreview step exists precisely because of this. If the diff is big, commit in slices.
Why not just give the agent a deploy token?
You can — a scoped token with deploy-only rights is a defensible setup for low-stakes projects. But the handoff loop has a property tokens can't give you: a human reads the diff between "agent done" and "code live." For a solo builder, that thirty-second git diff --stat glance is your entire change-management process. Keep it.