Files
gitdan-actions/scripts
claudeandClaude Fable 5.1 07ba53ca79 fix(prune): reclaim merged branches, and size the volume for the clone
Two assumptions in the eviction pass did not hold on this forge, and
between them a volume filled up three times in three days with nothing
reclaimed automatically. Both are replaced here; the pass also moves
ahead of the seed, which is the only order in which its work can help
the run performing it.

LIVENESS. Pass 1 evicted a cache only when its branch was gone from
origin. Gitea keeps a PR's branch after the merge unless the repo opts
into delete-on-merge, and zemyna does not — so ls-remote reports fifty
merged branches and the signal fires for none of them. A second signal
is added beside it: a branch still on origin whose tip is an ancestor of
a protected branch's tip holds no commit that branch does not, so its
cache will never be read again and goes in the same unconditional pass.

Ancestry is answered from the commits in the job's own checkout, so the
answer "cannot tell" exists and stays distinct from "not merged" at both
granularities. A shallow checkout withholds the signal entirely, since a
missing object is its normal case rather than evidence. A single branch
whose tip is not in the checkout is kept, with a warning naming it. A
squash or rebase merge leaves no ancestry and reads as live until the
branch is deleted. All three are missed reclamations, which cost disk;
the other direction costs a branch its cache mid-build.

HEADROOM. Passes 2 and 3 gated on a percentage of the volume, which
cannot express the failure they have to prevent: a clone runs out of
disk while unsharing its mutable paths, and how much that needs is a
property of the snapshot rather than of the disk. Staging failed at 34 G
free and passed at 74 G, so a 10% floor — 19 G here — never fired first.
The requirement is now measured per run off the very source the seed
will read, the pass evicts oldest-first until it is met and stops there,
and falling short of it fails with the shortfall and every directory it
kept, rather than letting the seed fail seconds later against a staging
path that names none of that. min-free-percent survives as an additional
floor, defaulting to 0, and falling short of that one is still a warning
and a self-clear.

ORDERING. The prune step ran after the seed, so each run freed space for
the next one. It now runs between resolve and seed. Two things that
makes newly reachable are closed: the source about to be cloned is
excluded from every pass by name, and a concurrent job's target dir
already carries its lock from the instant it appears under its final
name, so nothing is seen unlocked that is in use.

Red-proven: sixteen assertions across the four new scenarios fail
against the pre-fix scripts, including the zemyna layout evicting
nothing where it should evict exactly one directory, and the headroom
scenario exiting 0 where it should exit 1.

Refs #20.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXMQCJ5Eg5f9G9cfYzyh4Z
2026-09-07 00:21:12 -05:00
..