DEV Community

Emery Chen
Emery Chen

Posted on

An Agent That Needs Your Laptop Is Not Ready

An agent that only succeeds on your laptop is not ready. That run borrowed your credentials, caches, and leftover files. Treat local IDE success as a sketch, never as evidence.

Local success is a contaminated sample

Your interactive shell is not a clean room for agents. It already holds SSH keys, cloud tokens, and package caches. It already holds git author data and private registry logins.

You did not measure the agent in isolation at all. You measured the agent plus your entire machine. Those two results are not interchangeable for shipping.

Shipping the laptop result is a category error. The agent never proved it could work elsewhere. It proved it could lean on you.

Name the contaminants before you argue prompts

Stop tuning the prompt while the machine is lying. List what leaked into the last “green” run. Then decide whether the merge still deserves a yes.

  • SSH agent sockets the model never requested
  • ~/.netrc, ~/.aws, leftover gcloud application-default files
  • Global npm, pip, and cargo caches with private packages
  • Editor buffers that never reached git
  • Docker images you built last Tuesday
  • Environment variables from a forgotten direnv file
  • Previous agent patches still sitting unstaged
  • VPN routes, /etc/hosts hacks, and local DNS lies

If the task “worked,” which item did the heavy lifting? You cannot know from a chat pane. That ignorance is the actual defect.

The opinion, without a hedge

Fail the merge when the agent needs your laptop. A clean remote workspace is the first honest test. A disposable model budget is enough to run that test.

Paid frontier models can wait for later stages. Your identity isolation cannot wait at all. The first production-shaped question is blunt. Did this agent work without your credentials?

Vibe coding in a chat pane is fine. Calling that session engineering is not. Engineering starts when a stranger machine can finish the work.

What the laptop hides on purpose

Localhost hides clock, network, and permission shape. Your VPN makes private APIs look public. Your Docker socket makes “shell” feel like root.

Your git user.email is already configured. Your SSH key already signs commits. Your password manager already filled the .env.

None of that will exist on the on-call host. None of that will exist in CI. None of that will exist for the next hire.

A clean-room gate you can actually run

This section is a proposed workflow, not a vendor score. Swap in the remote box you already control. Keep the assertions even if the runner changes.

You need four pinned inputs, nothing fuzzy.

  1. The git SHA under test
  2. A task file the agent must complete
  3. An allowlist of environment variables
  4. A hard stop on wall clock and steps

If any input is “whatever is on my desk,” stop. You are not testing yet. You are narrating a lucky afternoon.

Provision a workspace that is not $HOME

Use a throwaway server. Clone only the SHA. Create a non-root user with no extra keys.

# proposal: clean-room bootstrap on a disposable host
set -euo pipefail
SHA="${SHA:?pin the commit}"
TASK="${TASK:?path to task.md}"
WORKDIR="/opt/agent-gate/${SHA}"

sudo useradd --create-home --shell /bin/bash agentgate || true
sudo mkdir -p "$WORKDIR"
sudo chown agentgate:agentgate "$WORKDIR"

sudo -u agentgate git clone --depth 1 "$REPO_URL" "$WORKDIR/src"
sudo -u agentgate git -C "$WORKDIR/src" fetch --depth 1 origin "$SHA"
sudo -u agentgate git -C "$WORKDIR/src" checkout --detach "$SHA"
sudo -u agentgate cp "$TASK" "$WORKDIR/task.md"
Enter fullscreen mode Exit fullscreen mode

Replace $REPO_URL with your repository. Do not copy your local .env. Do not forward your SSH agent socket.

If the job needs a secret, inject one scoped token. Delete that token in teardown. A long-lived key on a “test” box is still production access.

Strip the environment on purpose

Default-deny the process environment. Pass only what the task truly needs. env -i is the whole technique.

# proposal: default-deny env, then exec the agent runner
ALLOW="PATH,HOME,LANG,TASK_PATH,MODEL_BASE_URL,MODEL_API_KEY"
ENV_FILE="$(mktemp)"

for key in $(echo "$ALLOW" | tr ',' ' '); do
  eval "val=\${$key-}"
  [ -n "$val" ] && printf '%s=%s\n' "$key" "$val" >> "$ENV_FILE"
done

sudo -u agentgate env -i \
  HOME=/home/agentgate \
  PATH=/usr/local/bin:/usr/bin:/bin \
  TASK_PATH="$WORKDIR/task.md" \
  MODEL_BASE_URL="${MODEL_BASE_URL:?}" \
  MODEL_API_KEY="${MODEL_API_KEY:?}" \
  /usr/local/bin/run-agent \
    --workdir "$WORKDIR/src" \
    --task "$WORKDIR/task.md" \
    --trace "$WORKDIR/trace.jsonl"
Enter fullscreen mode Exit fullscreen mode

Label the runner binary as a proposal in your repo. The brand of runner is not the lesson. The empty environment is the lesson.

Assert identity leakage, not writing quality

Do not grade the README tone. Inspect the tree. Inspect the trace for credential tools.

# proposal: fail the gate on identity leakage and noisy traces
from pathlib import Path
import json
import subprocess
import sys

sha = sys.argv[1]
root = Path("/opt/agent-gate") / sha
src = root / "src"
trace = root / "trace.jsonl"

diff = subprocess.check_output(
    ["git", "-C", src, "status", "--porcelain"], text=True
)
if not diff.strip():
    raise SystemExit("agent produced no tree change; task likely skipped")

forbidden = ("id_rsa", ".aws/", ".git-credentials", ".netrc", ".env")
for line in diff.splitlines():
    path = line[3:]
    if any(token in path for token in forbidden):
        raise SystemExit(f"identity file appeared in diff: {path}")

steps = 0
for raw in trace.read_text().splitlines():
    event = json.loads(raw)
    steps += 1
    if event.get("tool") not in {"shell", "exec"}:
        continue
    cmd = str(event.get("args", {}).get("command", ""))
    banned = ("ssh-add", "aws sts", "gcloud auth", "pbcopy", "security dump")
    if any(token in cmd for token in banned):
        raise SystemExit(f"tool reached identity APIs: {cmd!r}")

if steps > 80:
    raise SystemExit(f"too many steps for a cheap gate: {steps}")

print("clean-room gate passed")
Enter fullscreen mode Exit fullscreen mode

Pin your own step budget in team docs. The 80 above is an example threshold. This article does not present it as a measured result.

Tear the box down every time

Leave no home directory behind. Leave no key in shell history. Optional teardown is how secrets rot.

sudo pkill -u agentgate || true
sudo userdel -r agentgate || true
sudo rm -rf "/opt/agent-gate/${SHA}"
unset MODEL_API_KEY
Enter fullscreen mode Exit fullscreen mode

If teardown feels optional in your head, the gate is already compromised. A leftover user is a leftover identity.

Decision table: when local is allowed

Situation Local IDE Clean remote workspace
Spike to learn an unfamiliar API Yes No
Commit you intend to merge No Yes
Stakeholder demo No Yes, then keep the trace
Debugging a flaky tool parser Yes, then promote Yes before merge
Anything that can touch credentials Never Yes, with a scoped token
“It works on my machine” as evidence Never Required

If your row is “merge” and your column is “laptop,” you are guessing. Guesses do not belong in the main branch.

Why a free model belongs in this gate

The gate must be cheap enough to fail. Expensive models make teams skip the remote run. Skipping the remote run puts the laptop back in the loop.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode is an open source project with free model access and a free server option. Those two facts matter here only because the gate needs a disposable workspace and a budget you can burn. This article does not claim quotas, hardware, latency, or model names. Point MODEL_BASE_URL at the endpoint you actually operate.

If you already run this gate on your own iron, keep doing that. The isolation rule does not require a brand. It requires a box that is not your laptop.

What this gate will not catch

It will not prove the generated code is correct. It will not prove the prompt is optimal. It will not replace unit tests, contract tests, or a human review.

It will not stop a determined agent from doing something stupid inside the sandbox. It only removes your laptop from the evidence. That is a smaller claim, and smaller claims are defensible.

Clock skew, rate limits, and cold starts still exist remotely. Good. Those are production-shaped problems. Hiding them on localhost is how stalled agents reach users.

A passed gate is not a benchmark. A passed gate is not a quality score. A passed gate only says the agent did not need your privileged machine.

Who should not use this approach

Skip this workflow if you do not ship agents. Skip it if the task is a private editor thought. Skip it if you cannot isolate secrets from the model.

Do not use a shared box for customer data. Do not paste production credentials into any remote agent runtime. Do not treat a passed gate as a performance study.

If your compliance model forbids third-party inference, host the model yourself. The script still works. Only the endpoint changes.

Solo spikes can stay local. Merges cannot. That split is the whole policy.

Operating rule you can paste on the wiki

Write four lines. Enforce them on pull requests. Ignore them in chat sketches.

  1. Local chat is a sketch.
  2. A clean remote workspace is the first test.
  3. Tool traces are the artifact you review.
  4. No trace, no merge.

You can delete every product sentence and keep the rule. Agents fail in the environment you refused to measure. Measure a box that is not yours.

Top comments (0)