---
title: "26,745 Stale Session Folders and a 3am Scheduled Task"
canonical: https://dxdev.com/blog/self-feeding-tagger-filled-the-disk/
datePublished: 2026-10-15
---
G had about 11 GB free out of roughly 250 GB, and I was sure something was running away in there. I asked an agent to investigate. It started two background scans: folder sizes for each top-level folder, and any file over 200 MB changed in the last 10 days. The folder scan ran past 10 minutes, which the agent took to mean there was a lot of data to walk.

Then the agent corrected itself: "this scan found files over 200 MB from the last 10 days, so the first scan was broken." It had already told me it hadn't found the cause.

## The first answer was about leftovers

Later the agent reported that the space was going to batch loops of `claude -p` calls. Each call leaves a temp folder behind, and nothing deletes them or pages you about G. There were 39k logged calls. On Oct 13 at 4am one process made 999 calls in an hour, and Oct 6 had 2,195 calls in one run. It counted 26,000 empty folders left behind, though it said the counts matched without being traced. The real weight was 24 sessions that copied whole repos into scratch: 21 GB, with the biggest single one at 7.5 GB.

Two things then ran in the background. A Sonnet agent ran a delete script, with a dry run first, and G went from 9.74 GB free to 26.35 GB. It deleted 26,745 stale session dirs and 15 VS Code leftover dirs. A second session built seven layers of guards under a ticket. Layer 1 had each `claude -p` call remove its own temp folder. Layer 6 was a spawn guard with a hard limit of 600 calls per hour per process and an alarm at 200. The agent noted the Oct 13 loop ran at 999 an hour, so this would have stopped it.

G was holding at 24.5 GB free, seven layers were committed, and the session reported 23 tests passing.

## What the audit showed

A read-only audit of G: came back with a ranked table of consumers. It also explained a discrepancy: Python had said the Projects folder was 99 GB while du said 51 GB. The cause was Windows junctions that pnpm creates inside node_modules, which point back into the same tree. A walk that follows them counts files twice. Skipping them, the Projects total came to 53.5 GB, matching du.

In that table, `Caches\Temp` still held 6.6 GB after the cleanup, because the clones in it were skipped as "git dirty/unpushed" and layer 3 covered session dirs only. And the guard rules excluded `G:\claude-data`, where transcripts were growing.

## The loop

The agent then found the main runaway: the nightly transcript tagger, `enrich_threads`, run at 3am by a scheduled task. The sequence:

- Every background `claude -p` call writes its own transcript.
- The next night, the tagger runs Haiku on each new transcript, which is itself more `claude -p` calls.
- Those calls write transcripts, which get tagged the night after.
- Most of what it tags is junk: test threads and "reply ALIVE" health checks.

The result was 200 to 835 calls a night and about 62 hours of compute since Sep 12, around 37% of all calls. The agent flagged that it was inferring this from timing and from what the summaries contained, and that the fix session would confirm it before changing anything.

It did confirm it. 1,136 of the 1,500 newest transcripts were background one-shots that the tagger had then tagged. Two jobs, the session summarizer and the inbox sweep, kept writing transcripts that weren't needed.

## The fix

- Background transcripts are flagged and skipped by the tagger.
- The session summarizer and inbox sweep add `--no-session-persistence`. A live check showed the `claude_run` default leaves no transcript and `--persist` leaves one.
- Tagging is capped at 100 new enrichments a night.
- Every background call through the main wrapper needs a job name and a daily limit. An unnamed or over-limit call is refused, and one alert goes out per job per day. A deliberate bulk run raises its own limit.

The budgets had a gap: Claude processes started directly rather than through the wrapper went uncounted, including the bridge, browser tasks and theme builds. A follow-up added `job_budget.check_and_record(label, site)` at each spawn site, writing to `.runs/claude_spawns.jsonl`. Registry entries can be `enforce:false`, which records and alarms only, for the spawns a person drives.

## What I don't know yet

G sat at 47.9 GB free, up from 10 GB, but none of the call fixes had run on a real night when the session closed. The expected tagging load of under 100 calls a night, down from a median of about 293, is an estimate. The check is posted on the ticket: the 3am tagging run, with call counts compared against that 293 median.
