DEV Community

Jordan Huang
Jordan Huang

Posted on

The Agent Wrote utils.js. Git Kept Utils.js.

Did your last agent commit survive a fresh Linux clone?
Mine often did not, and the chat never said why.
The laptop folded paths. The box did not.

I keep hearing the same claims in review threads.
They show up after a model edits a working tree.
They also show up when that tree hits a Linux server.

This is a myth-busting FAQ, not a tour.
You can run every check with git and a shell.
Skip any vendor if you already have Linux.

The claims I keep hearing

Developers repeat these like they were kernel facts:

  1. Git names are case-insensitive, so collisions cannot exist.
  2. A repo symlink is a real file on every host.
  3. The agent made the script executable, so CI will run it.
  4. If ls looks clean on a Mac, the tree is unique.
  5. A free remote box is just a slower laptop.

Which of those did you ship last month?
I will take them one by one.
Then you get a canary to run twice.

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

I draft the canary with MonkeyCode's free model access.
I run the Linux half on MonkeyCode's free server option.
I still treat both reports as untrusted text.
Neither layer fixes core.ignorecase for you.

Myth 1: Git does not care about case

Claim

macOS and Windows fold case.
So a repo cannot hold Utils.js and utils.js.
The agent cannot create a collision. Right?

Evidence

Gather this in two minutes. Do not argue first.

git config --get core.ignorecase
uname -s
git ls-files | python3 -c '
import sys
from collections import defaultdict
buckets = defaultdict(list)
for line in sys.stdin:
    path = line.rstrip("\n")
    buckets[path.lower()].append(path)
for group in buckets.values():
    if len(set(group)) > 1:
        print("COLLISION", group)
'
Enter fullscreen mode Exit fullscreen mode

On a default Mac, core.ignorecase is true.
Git will not shout when an agent adds utils.js.
The index may keep one path and hide the other.
Finder then shows a single file. You feel safe.

Corrected mental model

Git stores bytes. Your laptop folds some bytes.
Linux does not fold them. CI is usually Linux.
The agent wrote a second path. Your Mac displayed one.

After any agent session, run the collision script.
If it prints a group, do not merge yet.
Pick one spelling. Remove the other. Commit the rename.

# Label: example on a dirty tree, not a scored test.
git ls-files | grep -i utils.js
git mv -f utils.js Utils.js 2>/dev/null || true
git status --short
Enter fullscreen mode Exit fullscreen mode

Did git record a rename, or only a working-tree mess?
git status is the answer. The chat log is not.

Myth 2: A symlink is just a shortcut

Claim

The agent linked config/current to config/dev.json.
Everyone can open it. We are fine.

Evidence

On APFS that link may look like a regular file.
On Linux it is a real symlink with a path string.
Broken targets fail only on the host that notices.

git ls-files -s | awk '$1 == "120000" {print}'
find . -type l -exec ls -l {} \;
find . -type l ! -exec test -e {} \; -print
Enter fullscreen mode Exit fullscreen mode

The last command lists dangling links.
An agent loves dangling links.
It cannot feel a 404 on disk.

Corrected mental model

A git symlink is mode 120000 plus a target string.
It is not the contents of that target.
If the target is gitignored, Linux gets a hole.

Ask one question before you merge:
Does the target exist in a fresh clone?
If not, stop linking and commit a real file.

Myth 3: chmod in chat means +x in git

Claim

The model said it made scripts/canary.sh executable.
CI will run it. Why would git disagree?

Evidence

Did git record mode 100755?
Or did you only see a green phrase in the transcript?

git ls-files -s -- scripts/canary.sh
git diff --summary HEAD
stat -f '%A %N' scripts/canary.sh 2>/dev/null || stat -c '%a %n' scripts/canary.sh
Enter fullscreen mode Exit fullscreen mode

On a Mac, core.filemode is often false.
Git ignores the executable bit you flipped locally.
A Linux clone then gets a non-executable file.
CI prints Permission denied. The chat printed success.

Corrected mental model

The transcript is not git update-index --chmod=+x.
If core.filemode is false, set the bit in the index.

git update-index --chmod=+x scripts/canary.sh
git ls-files -s -- scripts/canary.sh
Enter fullscreen mode Exit fullscreen mode

You want 100755, not 100644.
Ask the agent to print that line.
Do not trust adjectives like "done" or "fixed".

Myth 4: ls on a Mac proves uniqueness

Claim

I listed the directory. Every name looks unique.
So the agent did not collide with Utils.js.

Evidence

ls on a case-insensitive volume is a fold.
It is not a proof. Two entries can share a display name.
Finder shows one. Linux readdir shows two.

git ls-files | wc -l
find . -iname 'utils.js'
python3 - <<'PY'
import os
for root, dirs, files in os.walk('.'):
    if '.git' in dirs:
        dirs.remove('.git')
    names = dirs + files
    folded = {}
    for n in names:
        folded.setdefault(n.lower(), []).append(n)
    for key, group in folded.items():
        if len(set(group)) > 1:
            print(os.path.join(root, key), group)
PY
Enter fullscreen mode Exit fullscreen mode

Corrected mental model

Uniqueness is a filesystem property, not an ls property.
Prove it on a case-sensitive volume.
That is what a throwaway Linux server is for.

Would you merge a tree you cannot list twice?
I would not. Not after an agent session.

Myth 5: A free Linux box is a slow laptop

Claim

Same repo. Same commands. Same results.
The remote box is only slower and poorer.

Evidence

No. The kernel is the test.
Case, filemode, symlink following, and path length change.
Your laptop is the special case.
Linux is the default for most CI.

I do not treat a free server as production.
I treat it as a second filesystem.
If the two canary reports disagree, the laptop lost.

The artifact: run this canary twice

Save this as scripts/repo-fs-canary.sh.
Run it on the laptop. Run it on Linux.
Diff the two reports. Do not eyeball them.

#!/usr/bin/env bash
# Label: local helper, not a published benchmark.
set -euo pipefail

echo "### host"
uname -a
git rev-parse --show-toplevel
git rev-parse HEAD
echo "ignorecase=$(git config --get core.ignorecase || echo unset)"
echo "filemode=$(git config --get core.filemode || echo unset)"
echo "symlinks=$(git config --get core.symlinks || echo unset)"

echo "### case collisions in the index"
git ls-files | python3 -c '
import sys
from collections import defaultdict
b = defaultdict(list)
for line in sys.stdin:
    p = line.rstrip("\n")
    b[p.lower()].append(p)
for g in b.values():
    if len(set(g)) > 1:
        print("COLLISION", g)
'

echo "### dangling symlinks"
git ls-files -s | awk '$1=="120000"{print $4}' | while read -r p; do
  if [[ ! -e "$p" ]]; then
    echo "DANGLING $p"
  fi
done

echo "### shell scripts without +x in the index"
git ls-files -s | awk '/\.sh$/ && $1=="100644" {print $4}'

echo "### path length outliers"
git ls-files | awk 'length($0)>180 {print length($0), $0}'
Enter fullscreen mode Exit fullscreen mode

Make it executable in the index, not only in your head.

chmod +x scripts/repo-fs-canary.sh
git update-index --chmod=+x scripts/repo-fs-canary.sh
bash scripts/repo-fs-canary.sh | tee /tmp/canary-laptop.txt
Enter fullscreen mode Exit fullscreen mode

On the Linux side, clone a clean copy.
Do not rsync node_modules.
Do not copy APFS metadata.

git clone --no-local . /tmp/repo-linux-check
cd /tmp/repo-linux-check
bash scripts/repo-fs-canary.sh | tee /tmp/canary-linux.txt
diff -u /tmp/canary-laptop.txt /tmp/canary-linux.txt
Enter fullscreen mode Exit fullscreen mode

If you lack a Linux laptop, use any throwaway Linux server.
That is the only reason a free server belongs in this workflow.
The model can draft the script. You still read the diff.

Decision table

Symptom on Linux Laptop lie Fix in git, not in chat
Two files, one name on Mac core.ignorecase=true Rename; keep one path
Permission denied on .sh core.filemode=false git update-index --chmod=+x
Missing config after clone Symlink to an ignored target Commit the target or stop linking
Agent "created" a file you had Case fold in Finder `git ls-files \
Canary differs after clone You copied local extra state Fresh clone, no extra files

Print the table in the pull request.
If a row fires, the model did not almost get it.
The filesystem rejected the tree.

What this does not prove

This canary does not prove tests pass.
It does not prove the model understood the patch.
It does not prove production matches Linux.

It also does not scan secrets.
Do not paste {% raw %}.env into a model prompt.
Do not leave credentials on a shared box.

APFS can be formatted case-sensitive.
Some USB disks are not.
Measure the host you actually run. Do not assume.

Windows has a third fold story.
This FAQ does not cover NTFS or WSL mounts.
If that is your world, add a third canary host.

Who should skip this workflow

Skip it if production is only macOS laptops.
Skip it if real CI already clones on Linux.
Skip it if you cannot read a twelve-line shell script.

A free model can draft the script.
It cannot own the rename.
You still pick Utils.js or utils.js.

Do not use a shared free server for private keys.
Do not use it as your only review.
Do not treat a green chat as git ls-files -s.

Closing

So which layer lied to you this week?
Was it Finder, core.ignorecase, or the transcript?

Run the canary on the laptop.
Run it again on a case-sensitive box.
If you already use MonkeyCode, the free server is one place for that second pass. Then read the diff yourself.

The agent can write files.
Only git and the kernel keep them.

Top comments (0)