From 6d746e53bf730519862a0a9d1ce2d4d1053ba00a Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Tue, 13 Oct 2026 21:05:07 +0200 Subject: [PATCH] feat(cleanup): fold in the lessons of the first live run Sandboxed agents see only their own processes, so candidates need 24 hours without change. Selection runs as checklists followed by a radio-button confirmation; stopped containers and volumes get their own warned checklist and are never dropped silently. Image removal is followed by an age-filtered build cache prune, long deletions go through a checked list file, and a refused delete is put back to the operator instead of being routed around. Co-Authored-By: Claude Opus 5.5 (1M context) --- skills/cleanup/SKILL.md | 65 +++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 19 deletions(-) diff --git a/skills/cleanup/SKILL.md b/skills/cleanup/SKILL.md index e393aeb..297843b 100644 --- a/skills/cleanup/SKILL.md +++ b/skills/cleanup/SKILL.md @@ -46,13 +46,19 @@ Read-only, in parallel, sizes included: - **Docker**: `docker system df -v`; containers with state and compose project label, volumes with the containers using them (`docker ps -a --filter volume=`), dangling and unused images, build - cache, unused networks. + cache, unused networks. For every stopped container (`exited`, `created`) + record its volumes (`{{.Mounts}}`); for every volume no running container + uses, record size, `CreatedAt` (`docker volume inspect`) and compose project + label, anonymous ones included. - **Other agent dirs**: package and tool caches (pip, npm, pre-commit, act, gh, buildx), worktrees with `git status --short` and whether their branch is merged or pushed, Ansible and build temp dirs. - **Live use**: open handles (`fuser`, `lsof`) and running processes by name only (`ps -o pid,etime,comm`). Never print full command lines or - environments: process arguments carry credentials. + environments: process arguments carry credentials. A sandboxed agent often + sees only its own processes, so an empty handle list proves nothing there; + then only entries untouched for more than 24 hours (`find -mtime +0`) are + candidates, and newer ones stay as `keep (recent, possibly in use)`. - **Archive duties**: memory and project instructions that require copying something out before deletion (logs, reports, generated maps). Such an item is `archive first`, never a plain delete. @@ -75,9 +81,9 @@ worktree or compose project name, a container label, an open handle, or | Risk | Meaning | Typical items | | ---- | ------- | ------------- | -| low | regenerated automatically, cheap | stopped containers, dangling images, unused networks, scratch dirs and logs of finished sessions | +| low | regenerated automatically, cheap | stopped containers without volumes, dangling images, unused networks, scratch dirs and logs of finished sessions | | medium | regenerated, but slow, rate-limited or network-bound | unused tagged images (re-pull, registry limits), build cache (cold rebuilds), package caches | -| high | data loss or a broken run possible | volumes, running unclaimed containers or stacks, worktrees with uncommitted or unpushed work, unread reports, anything `unknown` | +| high | data loss or a broken run possible | volumes, stopped containers with volumes, running unclaimed containers or stacks, worktrees with uncommitted or unpushed work, unread reports, anything `unknown` | ## Step 4: propose @@ -88,22 +94,30 @@ One table, biggest first: Below it: reclaimable size per risk class, and every `keep` row grouped by reason, so the operator sees what was protected and why. -Then ask with `AskUserQuestion`, at most four questions per call: +Then ask with `AskUserQuestion` in two calls. -1. **Strategy** (`multiSelect: false`, radio buttons): `Conservative: low risk - only (Recommended)`, `Standard: low and medium`, `Custom: only what I tick - below`, `Abort`. Each description states the size it frees and its worst - case. -2. **/tmp** (`multiSelect: true`, checklist): the candidate groups, size and - risk in each description. -3. **Docker** (`multiSelect: true`): containers, images, build cache, - networks, volumes. -4. **Other dirs** (`multiSelect: true`): caches, worktrees, temp dirs. +**Call 1, the selection** (`multiSelect: true`, checklists, at most four +questions): `/tmp`, `Docker`, `Other dirs`, and a separate `High risk` +question that holds every high-risk candidate. Size, risk and worst case go +into each option's description. An area with more than four options is +grouped by owner or risk, or split into a further call. Only ticked items are +selected; nothing is preselected. -An area with more than four options is grouped by owner or risk, or asked in a -second call. A high-risk item is deleted only when it is ticked explicitly; -the strategy radio button alone never selects one. A checklist answer that -contradicts the strategy wins, since it is the more specific choice. +**Stopped containers and volumes** always get their own checklist +(`multiSelect: true`, header `⚠ Volumes`, in a further call when call 1 is +full). Every stopped container and every volume without a running container +appears there unless a peer claimed it; none is dropped as "not proposed", +`unknown` and anonymous ones included. The question text carries the warning: +`Deleting a volume is irreversible; its data does not come back unless the +description says how.` One option per stopped container together with its +volumes (removing the container alone frees nothing of them), one per +unattached volume, anonymous ones grouped. Each description gives size, +`CreatedAt`, owner or compose project, and what is lost. + +**Call 2, the confirmation** (`multiSelect: false`, radio buttons), computed +from the ticks: `Execute: items, (Recommended)`, `Revise the +selection`, `Abort`. The execute description names the worst case of the +ticked items. Revise repeats call 1; nothing is deleted before `Execute`. ## Step 5: execute @@ -111,7 +125,20 @@ contradicts the strategy wins, since it is the more specific choice. peer may have started something since the inventory. - Delete exactly the selected items, by name or path. No blanket prune (`docker system prune -a --volumes`, `rm -rf /tmp/*`) that sweeps beyond the - selection. + selection. For more than a handful of paths, write the list to a file under + `/tmp`, grep it against every claim once more, then delete with + `xargs -d '\n' rm -rf -- < `. +- Locally built images free little on their own: their layers stay behind as + build cache. After removing images, re-measure `docker system df`; the + layers show up as reclaimable build cache. Offer + `docker builder prune --filter until=24h` as a follow-up candidate, ask the + peers that claimed build cache first, and run it outside their running + builds. +- A host permission layer (an auto-mode classifier, a deny rule) may refuse a + delete. Never route around it with another command or a peer. Finish the + rest, then put the refused item to the operator again as its own option: an + explicit renewed selection may pass; if it is refused again, hand over the + exact command with its list file for the operator to run. - Running stacks go down through the project's teardown target, never by removing their containers or volumes underneath them. - Archive-first items: copy out, verify the copy, then delete.