Symptom
A host filled up: 468 GB volume at 100%, 1.5 GB free. The largest consumer was
~/.cache/devlaunch/ at 152 GB, and dl --prune reclaimed 1.3 GB of it.
The space is in agent git worktrees that live inside the workspace clones:
~/.cache/devlaunch/repos/<owner>/<repo>/<workspace-id>/.claude/worktrees/agent-<hash>/
Measured 2026-08-24 on one host, after a manual cleanup had already run:
|
|
agent-* worktree directories |
72 |
...carrying a full .pixi/envs/default |
18 |
| Total size |
104.5 GB |
repos/ tree total (single du, hardlinks deduped) |
127 GB |
| Worktrees as a share of the clone tree |
~82% |
One clone held 55 GB of them on its own. The envs are real copies, not hardlinks
into the shared pixi cache devlaunch bind-mounts at /var/tmp/devlaunch-pixi:
a sampled 217 MB locale-archive.tmpl had st_nlink=1, and du -shx vs
du -shxl over the whole tree differ by only ~13 GB, so this is not a
measurement artifact.
Why --prune cannot see it
dl --prune removes clone directories no workspace references (#159). Every one
of those 72 worktrees is inside a clone belonging to a live devpod workspace:
worktrees inside LIVE workspaces: 72 (104.5 GB)
worktrees inside ORPHANED clones: 0 (0.0 GB)
So the existing rule is not merely missing them — it must never fire here, because
firing would delete a live workspace's checkout. Reclaiming this needs its own rule,
scoped to .claude/worktrees/ within a clone that --prune otherwise leaves alone.
This is the same class as the orphaned-volume problem (#324): something creates
named state, nothing garbage-collects it, and devpod delete was never going to.
What makes it non-trivial
These are registered git worktrees, not stray directories, and they were created
from inside the container, so their registered paths are container paths that do not
resolve on the host:
$ git -C <clone> worktree list
/home/ags/.cache/.../devlaunch-fix-arch-pinivoti eb8a29d [fix_arch]
/workspaces/devlaunch-fix-arch-pinivoti/.claude/worktrees/agent-a49a 57955a3 [fix/git-refusal-classification] locked
/workspaces/devlaunch-fix-arch-pinivoti/.claude/worktrees/agent-a8da 5f9d40c [fix/aid-pty-flake] prunable
Across all clones on this host: 6 locked, 33 prunable registered entries against
72 directories on disk — so there are three distinct categories, with different
safety profiles:
- Unregistered leftovers (~33 dirs): git has already forgotten them, the directory
remains. Safe to delete outright.
- Registered and
prunable: git worktree prune will drop the metadata, then the
directory can go.
- Registered and
locked: git worktree prune deliberately skips these. Claude Code
locks a worktree so it is not collected mid-run, so a lock may mean in use right now
or may be a leftover from a killed session. These must not be removed on a timer.
And the branches matter. In the clone sampled, all 7 worktree branches had 0 unpushed
commits, but 3 of them did not exist on origin at all — so the commits were reachable
only from that worktree's branch ref. A blind rm -rf of a worktree whose branch is
unpushed and unmerged loses work, and it will not look like data loss until much later.
Asks
dl --prune reclaims .claude/worktrees/agent-* inside clones it keeps, for
categories 1 and 2 above.
locked worktrees are never removed implicitly. Require an explicit opt-in flag,
and say how many were skipped and why.
- Refuse — or require the same explicit flag — for any worktree whose branch has commits
not reachable from the default branch or any remote ref. Report those separately
rather than silently keeping or silently deleting them.
git worktree prune runs in the clone so git metadata and the directory do not drift
apart, and the container-path registrations get cleaned up too.
dl --ls --size attributes worktree bytes, so this is visible before it is a full disk.
It was invisible in --ls --size here, which is why it reached 100%.
- A dry run that prints what would go and what it would free, given the amounts involved.
Notes for whoever picks this up
- The container-path registrations (
/workspaces/<id>/...) mean git worktree remove
from the host will not resolve the path; either run it inside the container, or
git worktree prune the metadata and remove the directory from the host.
- The
agent-* naming comes from the agent harness, not from devlaunch, so match on the
.claude/worktrees/ container rather than the agent- prefix.
- Worth checking whether the per-worktree
pixi install can be pointed at the shared
cache so 18 envs are not 18 independent copies. That is a separate fix and probably a
separate issue, but it is the reason the number is 104 GB rather than 10.
Found while auditing devcontainer compatibility across the fleet; not caused by any
devcontainer change.
Symptom
A host filled up: 468 GB volume at 100%, 1.5 GB free. The largest consumer was
~/.cache/devlaunch/at 152 GB, anddl --prunereclaimed 1.3 GB of it.The space is in agent git worktrees that live inside the workspace clones:
Measured 2026-08-24 on one host, after a manual cleanup had already run:
agent-*worktree directories.pixi/envs/defaultrepos/tree total (singledu, hardlinks deduped)One clone held 55 GB of them on its own. The envs are real copies, not hardlinks
into the shared pixi cache devlaunch bind-mounts at
/var/tmp/devlaunch-pixi:a sampled 217 MB
locale-archive.tmplhadst_nlink=1, anddu -shxvsdu -shxlover the whole tree differ by only ~13 GB, so this is not ameasurement artifact.
Why
--prunecannot see itdl --pruneremoves clone directories no workspace references (#159). Every oneof those 72 worktrees is inside a clone belonging to a live devpod workspace:
So the existing rule is not merely missing them — it must never fire here, because
firing would delete a live workspace's checkout. Reclaiming this needs its own rule,
scoped to
.claude/worktrees/within a clone that--pruneotherwise leaves alone.This is the same class as the orphaned-volume problem (#324): something creates
named state, nothing garbage-collects it, and
devpod deletewas never going to.What makes it non-trivial
These are registered git worktrees, not stray directories, and they were created
from inside the container, so their registered paths are container paths that do not
resolve on the host:
Across all clones on this host: 6 locked, 33 prunable registered entries against
72 directories on disk — so there are three distinct categories, with different
safety profiles:
remains. Safe to delete outright.
prunable:git worktree prunewill drop the metadata, then thedirectory can go.
locked:git worktree prunedeliberately skips these. Claude Codelocks a worktree so it is not collected mid-run, so a lock may mean in use right now
or may be a leftover from a killed session. These must not be removed on a timer.
And the branches matter. In the clone sampled, all 7 worktree branches had 0 unpushed
commits, but 3 of them did not exist on
originat all — so the commits were reachableonly from that worktree's branch ref. A blind
rm -rfof a worktree whose branch isunpushed and unmerged loses work, and it will not look like data loss until much later.
Asks
dl --prunereclaims.claude/worktrees/agent-*inside clones it keeps, forcategories 1 and 2 above.
lockedworktrees are never removed implicitly. Require an explicit opt-in flag,and say how many were skipped and why.
not reachable from the default branch or any remote ref. Report those separately
rather than silently keeping or silently deleting them.
git worktree pruneruns in the clone so git metadata and the directory do not driftapart, and the container-path registrations get cleaned up too.
dl --ls --sizeattributes worktree bytes, so this is visible before it is a full disk.It was invisible in
--ls --sizehere, which is why it reached 100%.Notes for whoever picks this up
/workspaces/<id>/...) meangit worktree removefrom the host will not resolve the path; either run it inside the container, or
git worktree prunethe metadata and remove the directory from the host.agent-*naming comes from the agent harness, not from devlaunch, so match on the.claude/worktrees/container rather than theagent-prefix.pixi installcan be pointed at the sharedcache so 18 envs are not 18 independent copies. That is a separate fix and probably a
separate issue, but it is the reason the number is 104 GB rather than 10.
Found while auditing devcontainer compatibility across the fleet; not caused by any
devcontainer change.