DEV Community

Jordan Huang
Jordan Huang

Posted on

Did You Clone or Continue? A Dirty-Workspace FAQ

Did your last free run inherit yesterday's cache?

I keep hearing one confident claim during reviews. "The agent passed, so the repo is fine." Is that claim about git, or about a dirty disk?

This FAQ is not about latency or chat logs. It is about the workspace your agent actually touched.

What I am actually arguing

A free model can still be useful here. A free server can still be useful here. Neither one proves the working tree was clean.

You still need a fingerprint of the files. You still need a fingerprint of the caches. You still need to know clone versus continue.

Scratch capacity is not a cleanroom

When I need a scratch model and a scratch server, I use MonkeyCode. Disclosure: This article was prepared as part of MonkeyCode's product outreach.

MonkeyCode provides free model access and a free server option. I treat that pair as a scratch pad only. I do not treat the pair as a CI system.

The model can still invent a passing test. The server can still keep last night's dependencies. Why would a green chat fix either problem?

The artifact: a workspace fingerprint

Do not argue from memory about the disk. Print a fingerprint before and after the session.

I use a small bash script on purpose. It is boring, and that is the point. It writes a text file next to the repository.

Label this as a proposed local check only. Run it on your own machine first. Do not paste secrets into the output file.

#!/usr/bin/env bash
# workspace-fingerprint.sh
# Proposed check. Read it before you run it.
set -euo pipefail

OUT="${1:-workspace-fingerprint.txt}"
ROOT="$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
cd "$ROOT"

{
  echo "cwd=$PWD"
  echo "date_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
  echo "git_head=$(git rev-parse HEAD 2>/dev/null || echo NO_GIT)"
  echo "git_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo NO_GIT)"
  echo "--- porcelain ---"
  git status --porcelain=v1 -uall 2>/dev/null || echo "NO_GIT"
  echo "--- lockfile hashes ---"
  for f in package-lock.json pnpm-lock.yaml yarn.lock Cargo.lock go.sum poetry.lock Gemfile.lock requirements.txt uv.lock; do
    if [[ -f "$f" ]]; then
      if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$f"
      else
        shasum -a 256 "$f"
      fi
    fi
  done
  echo "--- maybe-dirty dirs ---"
  for d in node_modules .venv venv target dist build .next .tox .pytest_cache __pycache__; do
    if [[ -e "$d" ]]; then
      echo "PRESENT $d"
    else
      echo "ABSENT  $d"
    fi
  done
  echo "--- top level ---"
  ls -1
} > "$OUT"

echo "wrote $OUT"
Enter fullscreen mode Exit fullscreen mode

Run the script before the agent starts work. Run the script after the agent stops work. Diff the two files with a calm head.

chmod +x workspace-fingerprint.sh
./workspace-fingerprint.sh before.txt
# start the agent session on this tree
./workspace-fingerprint.sh after.txt
diff -u before.txt after.txt
Enter fullscreen mode Exit fullscreen mode

What changed that git cannot even see? If the diff is empty, stay skeptical anyway. You only proved that the fingerprint stayed stable.

That is already more than a chat transcript. A transcript is prose. A fingerprint is evidence.

Myth 1: A new chat is a new machine

Claim: I opened a fresh thread, so the disk is fresh.

How do you know that without listing files? Look for leftover node_modules you never installed. Look for a branch you never checked out.

A chat session is only a conversation. A workspace is only a directory. They do not reset on the same clock.

Ask the boring questions before you trust the run.

git rev-parse --is-inside-work-tree
git remote -v
git log -1 --oneline
Enter fullscreen mode Exit fullscreen mode

If you cannot answer those three commands, you are continuing, not cloning. Why would the model know the difference?

Myth 2: A passing test proves the lockfile

Claim: The test command was green, so the lockfile is honest.

Hash the lockfile yourself before you celebrate. Check whether node_modules predates this supposed clone. A pre-existing tree can hide a broken lockfile.

# Proposed npm example. Adapt to your package manager.
test -f package-lock.json && sha256sum package-lock.json
test -d node_modules && echo "node_modules already exists"
Enter fullscreen mode Exit fullscreen mode

The agent may run tests against leftover packages. Those leftovers may be kinder than your CI. Have you ever seen a laptop pass and CI fail?

Corrected model: a green test on a dirty disk is a maybe. A green test on a fresh clone is a start. CI remains the record you merge against.

Myth 3: The agent cleaned up after itself

Claim: It created a venv, so it removed the venv.

Read the fingerprint section called maybe-dirty dirs. Then read untracked files in the porcelain output. Temp file names will look like leftover experiments.

Agents are sloppy houseguests on a good day. They install extra packages and then patch local files. Who told them to delete the leftover caches?

git clean -nd
git status --ignored
Enter fullscreen mode Exit fullscreen mode

Remember that git clean -nd is a dry run. Read the list slowly before you panic. Do not run git clean -fd until you mean every line.

Corrected model: cleanup is a command you actually ran. It is not a vibe the model implied.

Myth 4: Free servers reset like CI runners

Claim: This server is free, so it must be ephemeral.

Is it ephemeral in any written note? Did you check the directory after a disconnect? Billing and lifecycle are different facts.

CI runners usually start from a clean image. Then they clone the SHA and die on purpose. A reused free server is closer to a laptop.

Laptops accumulate junk without asking anyone. Free describes billing, not lifecycle. Prove the lifecycle with a clone step you control.

I want this sequence in the notes, every time.

  1. Create an empty directory.
  2. Run git clone <url> . inside it.
  3. Run workspace-fingerprint.sh and save before.txt.
  4. Run the agent against that clone only.
  5. Run workspace-fingerprint.sh again as after.txt.
  6. Run the project's real test command yourself.
  7. Attach both fingerprint files to the PR.

If you skip the empty directory, you are not doing CI. You are doing conversation-driven work on a used plate. Does that workflow still sound mergeable to you?

Myth 5: git status is enough by itself

Claim: porcelain is empty, so the workspace is clean.

Porcelain ignores a lot of runtime junk files. node_modules is often gitignored on purpose. .venv and build caches are often gitignored too.

Those ignored paths are exactly what change tests. Empty porcelain plus PRESENT .venv is not clean. It means the repo is clean and the machine is not.

git status --porcelain=v1 -uall
git status --ignored
Enter fullscreen mode Exit fullscreen mode

Corrected model: git answers questions about tracked files. Tests answer questions about runtime files. You need both fingerprints before you shrug.

Decision table: what that free run proved

Print the table in the pull request body. Argue from the row you actually occupy. Which row are you in right now?

You saw You proved You did not prove
Chat said tests passed The model emitted that sentence The test runner actually exited 0
Fingerprint shows ABSENT caches, then tests You probably started from source Anything about production parity
Fingerprint shows PRESENT node_modules Something was installed earlier That the tree matches the lockfile
Porcelain empty after the session Tracked files still match HEAD Ignored caches are honest
Two fingerprints match The fingerprint was stable The fingerprint was the right one
Fresh clone + real test command + exit 0 This SHA passed this command here Tomorrow's box will match today

Two extra checks worth running

These commands are examples, so label them as unproven sketches. Adapt them to your stack before you copy.

# Node: pick a fight with the lockfile, do not "fix" it yet
if [[ -f package-lock.json ]]; then
  npm ci --ignore-scripts --dry-run
fi
Enter fullscreen mode Exit fullscreen mode
# Python: are we sitting inside a leftover prefix?
python -c "import sys; print(sys.prefix)"
test -d .venv && echo "local .venv present"
Enter fullscreen mode Exit fullscreen mode

npm ci --dry-run will not repair the tree. It will argue with the tree in public. That public argument is the entire point here.

For compiled stacks I also glance at target or dist. A warm cache can hide a broken toolchain. A cold clone cannot hide it as easily.

Limitations

This fingerprint does not hash every file on disk. It does not detect hostile processes on the box. It does not prove network isolation either way.

It does not name a model or any hardware. It does not promise a free server will exist tomorrow. Treat those absences as features of the check.

It also cannot see secrets, and that is good. Do not teach the script to print .env. Do not dump credentials into the PR for completeness.

If your build needs private packages, this sketch is incomplete. If your tests need extra services, this sketch is incomplete. If you need a contractual SLA, this is the wrong layer.

Who should not use this approach

Do not use a shared free server for customer data. Do not use it for production deploys either. Do not use it as your only CI system.

Skip this folklore if you already have ephemeral runners. Those runners already clone a SHA into a throwaway disk. Your job is then the test command, not the workspace myth.

Skip this if you cannot read git status output. The script will not save a review you will not do.

What I want reviewers to ask

Was this run a clone or a continue? Where is the before fingerprint, and the after fingerprint? Which row of the table are we occupying?

If the answers are shrugs, the green chat is a story. Stories do not belong on main branch. Cheap tools still need expensive honesty about the disk.

A free model is a cheap way to draft a change. A free server is a cheap place to try that draft. The workspace fingerprint keeps those tools honest about files.

If you try the script on a scratch box, attach the diff. Do not attach the chat log as evidence.

Top comments (0)