Writing / Claude Code & Git

Keep Your Coding Agent’s Ad-Hoc Files Out of Git

Plans, investigation notes, one-off SQL and review drafts used to end up wherever the agent put them. Now they go to one folder, sorted by time, that git never sees. Setup takes two minutes, and after that there’s nothing to remember.

By 7 min read
The short version

Add .reports/ to your global gitignore. Then tell the agent, in your user-level ~/.claude/CLAUDE.md, to write ad-hoc reports there with a YYMMDDHHMM_ prefix. No repository ever tracks the folder, and you never touch a shared file.

Problem

Agent files with nowhere to go

A day of work with a coding agent leaves behind files that aren't code. On a normal day in our product repo I end up with some mix of these:

  • an investigation write-up for a support ticket
  • a plan for a refactor, and a second agent's critique of it
  • a couple of .sql scripts I ran once against a test database
  • a PR review I still have to turn into comments

None of it belongs in the codebase, and I still want to keep all of it. I link these files from tickets, export them to PDF, and grep through them weeks later when someone asks why we chose one approach over another.

Before I had a convention, three things kept going wrong:

  • They landed anywhere. The repo root, docs/, a temp folder, or nowhere at all, left behind in the chat scrollback.
  • Some got committed. Several of my pull requests went up with a report attached. Each time, I only removed it after a teammate or a review agent pointed it out.
  • The names had no pattern. These are the four oldest files I kept:
.reports/ · ls
_2607160000prod-sync-timeout-report.md
2026-07-16-prod-sync-timeout-report.pdf
2026-07-28-patch-endpoint-null-handling.md
20260819_worker_crash_investigation.md

Four naming schemes in five weeks, and each one seemed reasonable on the day I picked it.

The fix

Set it up in three steps

Do them in this order. The ignore line goes in first, so the agent's first report can't land in a folder git still tracks.

1. Ignore .reports/ in every repository

Git can read a personal ignore file that applies to every repository on the machine. Point git at one and add the folder to it:

Git Bash, macOS or Linux
# Point git at a global ignore file (skip if you already have one)
git config --global core.excludesFile ~/.gitignore_global

# Add the folder to it
echo ".reports/" >> ~/.gitignore_global

In Windows PowerShell 5.1, >> writes UTF-16, which git can't read as an ignore file. Use Add-Content ~/.gitignore_global '.reports/' for that line instead. If you've never set core.excludesFile, git already reads ~/.config/git/ignore by default (git docs), so you can put the line there and skip the config command.

Check it from inside any repo. check-ignore -v names the file, and the line in it, that ignores a path:

bash
$ git check-ignore -v .reports/test.md
/home/me/.gitignore_global:4:.reports/	.reports/test.md

2. Tell the agent where reports go

Paste this into your user-level ~/.claude/CLAUDE.md, not the project's. Swap the skill names for the skills you actually use.

~/.claude/CLAUDE.md · markdown
## Transient reports

When the user asks for an ad-hoc report, analysis, summary, or scratch
markdown file NOT driven by a named skill (no `/command` invoked):

- Write the file to `.reports/` at the repo root
- Create the folder if it does not exist
- Prefix the file name with a `YYMMDDHHMM_` timestamp (2-digit year, month,
  day, hour, minute — 24h local time), e.g. `2608210935_failed-tests.md`
- `.reports/` is globally gitignored (via `~/.gitignore_global`), so the
  file will not be tracked or committed

### Exclusions — do NOT redirect these

Skills own their output paths. Do not redirect skill-driven artifacts to
`.reports/`. Examples of skills that write their own files:
`code-review`, `checkpoint`, `superpowers:writing-plans`, and any other
`/skill` invocation.

Also do not redirect:

- Durable repo docs: `README*`, `CHANGELOG*`, `docs/**`, `AGENTS.md`,
  `CLAUDE.md`, `.claude/**`
- Paths the user names explicitly in the request

### Scope

Rule fires only when the request is conversational and ad-hoc, e.g.:

- "write me a quick report on X"
- "summarize this in a markdown file"
- "create a note about the failed tests"

When in doubt, ask the user where to write the file rather than guessing.

Keep the "globally gitignored" bullet, even though it reads like a note to yourself. The reason is below.

3. Check that it works

Ask the agent for something throwaway, like "write a quick report on the failing tests", then look:

bash
$ ls .reports
2610311405_failing-tests.md

$ git status --ignored --short .reports
!! .reports/

!! means git sees the folder and ignores it. If you get ?? instead, git isn't reading your ignore file, so go back to step 1.

Design

Why it’s shaped this way

Each piece answers one of the three problems. The rule decides where files go, the timestamp decides what they're called, and the ignore line keeps them out of commits. None of it is clever, and none of it works alone: the rule without the ignore line still leaks files into commits, and the ignore line without the rule protects a folder nobody writes to.

Why the user-level CLAUDE.md

Claude Code reads instructions from more than one place. The project's CLAUDE.md is checked into the repo and shared with the team. ~/.claude/CLAUDE.md belongs to me and applies to every project I open. This convention is about how I work, not how the team works, so it goes in the personal file.

Why the last bullet matters

The bullet saying .reports/ is globally gitignored looks like a throwaway. Without it, a careful agent notices that .reports/ isn't in the project's .gitignore and offers to add it. That would be an edit to a shared file, made for one developer's habit, and somebody would have to review it.

Why the exclusions

The first version of a rule like this always reaches too far. "Put every markdown file in .reports/" would break things, so the rule lists three kinds of file it leaves alone:

  • Skill output. A skill (a packaged /command workflow) already decides where its files go, and its later steps expect to find them there. Move them and the skill breaks.
  • Durable docs. An ADR in docs/adr/ is a real document, even when I ask for it in the same offhand way I'd ask for a scratch note.
  • Paths I name. If I say "write it to C:\temp\x.md", that's where it goes.

The closing line, when in doubt, ask, covers the rest. A question costs me five seconds. A file in the wrong place costs more, especially when the wrong place is tracked by git.

Why a timestamp prefix

format
YYMMDDHHMM_short-slug.ext
2610151721_1042-phase1-plan-review.md

I settled on this format for four reasons:

  1. 01

    It sorts by time. A plain ls shows files in the order I wrote them, with no -t flag. Modified times aren't a safe substitute, because a plain cp or a sync tool can reset them. The name stays put.

  2. 02

    It's short. Ten digits instead of a full ISO timestamp. A two-digit year is fine for files I don't expect to keep until 2100.

  3. 03

    The slug says what it is. Most of my slugs start with a ticket number, so ls .reports | grep 1042 lists the whole history of one feature, oldest first.

  4. 04

    Related files sit together. A plan, its review and the prompt that carried it out sort next to each other because I wrote them minutes apart.

This is one afternoon on a single ticket:

.reports/ · ls
2610151411_1042-implementation-prompt.md
2610151451_1042-phase1-plan.md
2610151642_1042-phase1-goal-summary.md
2610151721_1042-phase1-plan-review.md
2610151736_1042-phase1-execution-prompt.md

I didn't organize any of that. The names did.

Why a global ignore, not the project's .gitignore

  • It's my habit, not the team's. The shared .gitignore should describe the project. A line for one developer's tools means a PR, and explaining the folder to people who will never use it.
  • It covers every repo at once, including fresh clones and git worktrees, with nothing to set up per repo.
Results

What ten weeks looks like

I added the rule to my user-level CLAUDE.md at 08:45 on 21 August 2026. The first file with the new prefix appeared at 18:24 that evening:

.reports/ · ls
2608211824_stuck-job-incident-report.html

This is where that one repo stands today:

431
Files in .reports/
0
Tracked by git
44 MB
On disk
MeasureCount
Top-level files with the timestamp prefix90
Prefixed files per monthAug 19 · Sep 24 · Oct 47
Subfolders (mostly skill output)14

August only covers the last eleven days of the month, so the real jump is from September to October, where the count nearly doubled. I don't think I investigated twice as many problems in October. What changed is that asking for a file stopped costing me anything. I don't have to decide where it goes or worry about committing it, so I ask for one whenever a plan or a finding is worth keeping. Writing the plan to a file before the agent starts, and keeping it afterwards, has become one of the most useful habits I have with an agent.

Caveats

Before you copy this

  • Ignored means not backed up. .reports/ is never pushed. If the disk dies, everything in it goes too. Anything that turns out to matter has to be moved somewhere durable on purpose: docs/, a wiki page, a ticket comment.
  • git clean will delete it. git clean -fdX removes ignored files, and git clean -fdx removes untracked and ignored files. Both take .reports/ with them, so don't run either out of habit in a repo where you keep reports.
  • Teammates can't see your ignore file. If a colleague copies the CLAUDE.md rule but not the gitignore line, their agent writes reports into a folder that git does track. Share both halves.
  • It only grows. 44 MB in ten weeks is fine for now. I'll want a cleanup rule eventually, and the prefix makes one easy to write: delete everything that sorts before 2607.
  • "Ad hoc" is a judgement call. "Write up the plan" might mean scratch notes, or a design doc the team will review. The when in doubt, ask line covers this, but only if the agent actually asks.

One folder, one naming rule, one ignore line. Since I set it up, I haven't had to pull a scratch file out of a commit, and no reviewer has had to point one out.

FAQ

Common questions

Where should an AI coding agent save temporary reports and plans?

In one folder at the repo root that git ignores, such as .reports/. Put the rule in your user-level instruction file (~/.claude/CLAUDE.md for Claude Code) so it applies in every repository without changing any shared file.

How do I ignore a folder in every git repository on my machine?

Add the pattern to a global ignore file. Run git config --global core.excludesFile ~/.gitignore_global and put .reports/ in that file. If you have never set core.excludesFile, git already reads ~/.config/git/ignore, so you can use that file instead.

Should the folder go in the project's .gitignore instead?

Only if the whole team uses it. A folder that exists because of your own tooling belongs in your global ignore file. That keeps the shared .gitignore about the project, and it covers every clone and worktree you open with no per-repo setup.

Why name the files with a YYMMDDHHMM timestamp prefix?

Alphabetical order becomes chronological order. A plain ls lists the files in the order they were written, and that order doesn't depend on file modification times, which copies and sync tools don't always preserve.

Does git clean delete an ignored .reports folder?

Yes. git clean -fdX removes ignored files and git clean -fdx removes untracked and ignored files, so both delete .reports/. Plain git clean -fd leaves ignored files alone.