CLAUDE LABJP
BILLING — 5 days to the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly creditDREAMING — Claude Managed Agents' "Dreaming" (research preview) reviews past sessions to curate memory and self-improve; Harvey reports ~6x task completionOUTCOMES — With outcomes, a separate grader scores agent output against your rubric in its own context; Wisedocs cut review time by 50%OPUS4.8-FAST — Opus 4.8 fast mode is now 3x cheaper and 2.5x faster, and claude.ai lets you dial the effort Claude spends on a taskDYNAMIC-WORKFLOWS — Dynamic workflows handle codebase-wide bug hunts, optimization audits, and parallel search with independent verificationORCHESTRATION — Multi-agent orchestration lets a lead agent delegate to specialists; Netflix now processes logs from hundreds of builds at onceBILLING — 5 days to the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly creditDREAMING — Claude Managed Agents' "Dreaming" (research preview) reviews past sessions to curate memory and self-improve; Harvey reports ~6x task completionOUTCOMES — With outcomes, a separate grader scores agent output against your rubric in its own context; Wisedocs cut review time by 50%OPUS4.8-FAST — Opus 4.8 fast mode is now 3x cheaper and 2.5x faster, and claude.ai lets you dial the effort Claude spends on a taskDYNAMIC-WORKFLOWS — Dynamic workflows handle codebase-wide bug hunts, optimization audits, and parallel search with independent verificationORCHESTRATION — Multi-agent orchestration lets a lead agent delegate to specialists; Netflix now processes logs from hundreds of builds at once
Articles/Claude Code
Claude Code/2026-06-10Intermediate

When git add -A Sweeps Up .bak Backups — The Quiet Trap of In-Place Fix Scripts

How .bak backups left by sed -i and homegrown --fix scripts get silently swept into production by git add -A, why it is so easy to miss, and how scoped adds and .gitignore close the gap for good.

troubleshooting84error17fix12git13claude-code112automation55

One morning I was glancing over the diffs across three repositories and noticed a file I did not recognize sitting in each of them: next.config.ts.bak. Tracing the commit history, I found that the previous night's redirect-integrity pass had committed and pushed that backup right alongside the real .ts file.

The build was green. The sites were serving. And that is exactly why nothing stopped it.

I have been an indie developer since 2014, running apps and blogs in parallel, and at Dolice Labs I update four technical blogs every day through scripts driven by Claude Code. As you automate the "mechanical but scary if it breaks" chores — formatting, link fixes — this kind of silent contamination becomes the real hazard. Mistakes you make by hand are easy to catch. The junk that automation carries along, quietly, is not.

What actually slipped in — the backup that --fix leaves behind

The trigger was a homegrown integrity checker. It inspects whether redirects for deleted articles collide with the MDX of articles that have since come back, and I had it auto-correcting with a --fix flag.

Before rewriting, the script saves the original next.config.ts as next.config.ts.bak in the repo root. A safety backup. The problem was the commit step that ran right after it.

# What the tail of the pipeline was doing
python3 redirect_integrity.py "$REPO" --fix
git add -A          # ← this sweeps up the .bak too
git commit -m "Fix redirect integrity"
git push origin main

git add -A stages every change in the working tree. The freshly created next.config.ts.bak is, of course, a "new file," so it gets added with no warning at all. A .bak is harmless to a Next.js build, so neither CI nor Cloudflare lights up red. The result: backup files took up residence in three repos — gemilab, antigravitylab, and rorklab.

Why it is so hard to notice

The essence of this bug is that nothing breaks.

One extra .bak file, and the site still renders fine. Tests still pass. No error appears in the logs. The repo just gets slightly dirtier, and that dirt does not clean itself up — it only gets overwritten the next time the same --fix runs. It accumulates, quietly.

Worse, a .bak is almost identical to the original, so even staring at a diff later, it rarely jumps out as "wait, what is this?" I only caught it because I happened to be comparing diffs across three repos at once and saw the same filename lined up three times.

In automation, an exit code of 0 and a green log do not mean "correct." The fact that git push raised no error and the fact that the repository matches your intent are two different things. Seen as the same class of "silent failure," git push reporting success while nothing reaches the remote shares the same root: in automation, the green log is precisely the thing to distrust.

Reproducing it

You can reproduce it in a few seconds.

mkdir -p /tmp/bak-demo && cd /tmp/bak-demo
git init -q
echo "export const config = {}" > next.config.ts
git add next.config.ts && git commit -qm "init"
 
# A classic in-place tool that leaves a .bak
sed -i.bak 's/{}/{ trailingSlash: true }/' next.config.ts
ls
# -> next.config.ts  next.config.ts.bak
 
git add -A
git status --short
# A  next.config.ts.bak   <- staged without you asking
# M  next.config.ts

sed -i.bak is a common form on macOS, and it always leaves a .bak. GNU sed's sed -i (no suffix) makes no backup, but I genuinely see sed -i '' on BSD/macOS mistyped into sed -i.bak in the field all the time. Homegrown --fix scripts often write a backup for safety too, and they step on the same trap.

The fix — decide what to stage by addition, not subtraction

The most reliable move is to stop using git add -A and instead list exactly what you want to commit.

# Before: everything, including the .bak
git add -A
 
# After: add only the paths you are allowed to touch
git add next.config.ts content/
git status --short   # eyeball it for strays
git commit -m "Fix redirect integrity"

For a blog-update pipeline, name only the directory you generate or edit, like git add content/. For a process that touches a config file, write that filename directly. "Add everything, then remove the extras" causes far more accidents than "add only what you need."

If you have a real reason to keep git add -A, clean up the backup right after the fix tool and before staging.

python3 redirect_integrity.py "$REPO" --fix
rm -f "$REPO"/next.config.ts.bak   # remove the saved copy first
git add -A

Preventing recurrence — close it structurally with .gitignore

Cleanup and scoped adds are "breaks the moment you forget" rules. A human turning the crank by hand can remember; a pipeline that runs every day will quietly relapse the first time the rule slips. The proper fix is structural.

Put the backup extensions into the repo's .gitignore, and they will never be staged again, even if you use git add -A.

# Backups left by in-place fix tools
*.bak
*.orig
*.tmp
*~

Drop the same block into all four repos, and no matter which script leaves which backup, it is excluded from commits automatically. If you have ever wrestled with how ignore rules interact with file lookups, Claude Code reporting that a .gitignore'd file cannot be found is worth reading alongside this.

As an extra layer, add a guard that mechanically rejects "unexpected files" after staging and before commit. Dropping these few lines at the tail of the pipeline lets you halt the moment a saved file like a .bak sneaks in.

# Right before commit: abort if any backup is staged
if git diff --cached --name-only | grep -qE '\.(bak|orig|tmp)$|~$'; then
  echo "Stray backup file is staged. Aborting commit."
  git diff --cached --name-only | grep -E '\.(bak|orig|tmp)$|~$'
  exit 1
fi
git commit -m "Fix redirect integrity"

The less human oversight a process has, the more this kind of final checkpoint earns its keep. Since adding these lines, I have not repeated the same contamination once.

A .bak that already slipped in will not vanish just because you added it to .gitignore. You have to stop tracking it separately.

git rm --cached next.config.ts.bak   # keep history, drop tracking
git commit -m "Remove stray backup file from tracking"

Make "additive add" the default for automation

This contamination grew out of a single way of writing one commit command. git add -A is convenient when you are working interactively, but in an automated pipeline with no human in the loop, I believe an explicit git add <path> should be the default. Even when you wire up something that lets Claude Code generate commit messages, you want to keep your own hand on what gets staged (I touch on that design in operating auto-generated commit messages).

Automation faithfully repeats the right steps. By the same token, it faithfully repeats the wrong ones. The one decision I want to keep in my own hands, not hand to the machine, is what actually goes into the repository.

I hope this helps anyone wrestling with the same problem.

Share

Thank You for Reading

Claude 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.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Claude Code2026-06-03
When git push Says Success but Nothing Lands — the Silent commit Failure in Claude Code
git push prints Everything up-to-date and exits zero, yet your changes never reach the remote. Here is why commit silently fails on a fresh sandbox clone, and how to verify a real push with SHA comparison.
Claude Code2026-06-09
When Yesterday's Article Bleeds Into Today's — The Stale Fixed-Name Temp File Trap
In a Claude Code automation pipeline, a fixed-name temp file kept stale content from the previous run and leaked old body text into a completely different article. Here is why the write fails silently, and how unique names plus a post-write grep check prevent it.
Claude Code2026-05-02
Stop the 'old_string is not unique' Error in Claude Code's Edit Tool — A Practical Rewrite Strategy
When Claude Code's Edit tool throws 'old_string is not unique in the file', the root cause is usually that the prompt didn't guarantee a uniquely identifiable target. Here's how to design rewrites that actually stick.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →