@@ -12,91 +12,58 @@ rm -rf on the wrong one destroys a live repo on the host. And because
1212the workspace root is itself a host mount, local dirs still consume
1313host disk - usually the disk that is actually full.
1414
15- The run is two passes. Pass one triages everything into a dry-run plan
16- and stops for approval. Pass two executes only the approved tiers .
15+ Two passes. Pass one is deterministic and scripted; pass two executes
16+ only what got approved .
1717
1818## Pass one: triage
1919
20- ### 1. Map the mounts
21-
22- Read ` .deva ` for declared VOLUME=host: container [ : mode ] lines, then take
23- ground truth from the kernel - config drifts, /proc/mounts does not:
24-
25- grep "$(pwd)" /proc/mounts | awk '{print $2}' | sort
26-
27- The workspace root is usually itself a mount, so everything under it
28- that is not a separate mount is a local dir living on host disk.
29-
30- Done when every dir under the workspace is classified mount or local.
31- Mounts are off limits for the rest of the run.
32-
33- ### 2. Locate the pressure
34-
35- df -h / "$(pwd)"
36-
37- Overlay numbers can lie: on shared-VM runtimes (OrbStack and friends)
38- overlay df reports the whole VM disk. Container-local truth:
39-
40- sudo du -xh -d1 / | sort -rh | head
41-
42- Done when you know which disk is full. Host mount full: the workspace
43- is the target. Overlay genuinely full: container caches (~ /.cache,
44- package stores) are the target instead.
45-
46- ### 3. Size the candidates
47-
48- Candidates = the local dirs from step 1, plus stray files (shell-typo
49- artifacts like "&1"). Typical: trees from ` git worktree add ` , one-off
50- clones, node_modules, .pnpm-store.
51-
52- du -sh <candidates> | sort -rh
53-
54- Done when every candidate has a size.
55-
56- ### 4. Git-triage each candidate
57-
58- Four verdicts per candidate:
59-
60- - Type: ` .git ` file -> linked worktree; ` .git ` dir -> standalone repo;
61- neither -> plain dir.
62- - Dirty: ` git status --porcelain ` . Untracked generated files (lockfiles,
63- test output) are lower risk than modified source.
64- - Pushed: ` git branch -r --contains HEAD ` - empty output means the
65- commits exist nowhere but this machine. This is the only pushed test:
66- upstream config and ahead/behind counts are a trap (a branch created
67- off origin/develop tracks origin/develop and shows "ahead 1" while
68- never pushed itself).
69- - Freshness: remote refs are only as new as the last fetch. Check
70- ` stat .git/FETCH_HEAD ` on the parent repo; flag stale refs in the
71- plan.
72-
73- Inside keepers, node_modules / dist / coverage are separately prunable:
74- offer cache-prune as the low-risk alternative to removal. Also run
75- ` git worktree list ` on each parent repo - "prunable" entries are stale
76- metadata from already-deleted trees.
77-
78- Done when every candidate carries all four verdicts. The smallest dir
79- gets the same triage as the largest: a 4M worktree can hold the only
80- copy of uncommitted work.
81-
82- ### 5. Assemble the dry-run plan
83-
84- - Tier 1 (safe): clean + pushed. Sizes and exact commands.
85- - Tier 2 (confirm): pushed branch, dirty only with generated artifacts.
86- Offer full removal and cache-prune-only variants.
87- - Keep: unpushed commits or uncommitted diffs. Say exactly what would
88- be lost and suggest pushing the branches.
89- - Totals: reclaim per tier.
90-
91- The plan is pass one's deliverable. Present it and stop - pass two
92- starts only from explicit approval, tier by tier.
20+ Run the bundled script - read-only, emits the tiered plan, never
21+ deletes:
22+
23+ scripts/triage.sh [workspace] [--fetch]
24+
25+ It maps mounts from /proc/mounts (ground truth - .deva drifts), shows
26+ which disk is under pressure, walks the non-mount dirs, gives every
27+ candidate four verdicts (type, dirty, pushed, freshness), and finds
28+ stale worktree metadata in the mounted parent repos. Verdict semantics
29+ live in the script header; two encode traps worth knowing:
30+
31+ - The pushed test is live ls-remote SHA comparison plus ancestry in
32+ the default branch - never upstream config. A branch created off
33+ origin/develop shows "ahead 1" while never pushed itself.
34+ - MERGED is monotonic, provable even against stale refs. Absence is
35+ not: a branch absent on remote with stale refs is UNVERIFIED, not
36+ safe. Rerun with --fetch for a definitive verdict.
37+
38+ Then layer the judgment the script refuses to automate:
39+
40+ - Pressure: container overlay genuinely full (rare - on shared-VM
41+ runtimes overlay df reports the whole VM disk) means container
42+ caches (~ /.cache, package stores) are the target, not the workspace.
43+ - Stray files: the script walks dirs only; shell-typo artifacts like
44+ "&1" are yours to spot.
45+ - Promotions: KEEP entries flagged MODIFIED TRACKED FILES move to
46+ tier 2 only when you can name every dirty file as a generated
47+ artifact (lockfiles, test output) and say so in the plan.
48+ - Demotions: tier 1/2 entries that are functionally live - symlink
49+ targets, open-PR checkouts - move to keep.
50+ - Keepers: offer cache-prune (node_modules, dist, coverage) as the
51+ low-risk alternative; for LOCAL-ONLY work, say exactly what would
52+ be lost and suggest pushing it.
53+
54+ Done when every candidate sits in a tier you can defend. Present the
55+ plan and stop - pass two starts only from explicit approval, tier by
56+ tier.
9357
9458## Pass two: execute approved tiers
9559
9660- Linked worktrees: ` git -C <parent> worktree remove <path> ` is the
9761 only removal path. Its refusal on a dirty tree is the safety net, so
9862 no --force - and no rm -rf, which bypasses the net and leaves stale
9963 metadata behind.
64+ - Tier 2 trees: first delete the named generated files (restore
65+ tracked ones with ` git checkout -- ` ), so the tree is genuinely clean
66+ and the safety net stays armed.
10067- Stale metadata: ` git -C <parent> worktree prune ` .
10168- Standalone repos (pushed, clean) and plain dirs: rm -rf.
10269
@@ -105,5 +72,4 @@ branch refs live in the parent repo's .git and survive. Committed work
10572is never lost - only uncommitted files can be. The metadata edit inside
10673the parent's .git is normal bookkeeping, not touching the mount.
10774
108- Done when df shows the reclaim and ` git worktree list ` on each parent
109- has no leftovers.
75+ Done when df shows the reclaim and a triage rerun reports empty tiers.
0 commit comments