Local workspace disk hygiene
Use this runbook on the Windows development host. It separates online cleanup from maintenance that requires a Docker/WSL outage.
Canonical repository output paths
- Service publish output:
<repo>/tmp/build/stella-publish/ - Mirror seed and local mirror E2E output:
<repo>/tmp/build/mirror/ - Benchmark work, results, and caches:
<repo>/tmp/bench/stella-vs-others/
Do not direct repository builds to C:\temp, %TEMP%, /mnt/c/temp, or /c/temp. These canonical trees are ignored by Git and can be aged out without touching source or durable QA evidence.
The knowledge checker uses one owned directory under the current checkout’s tmp/. It honors TMPDIR when that path resolves inside this home, including paths with spaces; an inherited OS-temporary path falls back to tmp/scratch/. Normal and self-test modes remove only their owned directory. Recheck the lifecycle with:
python -B tools/scripts/validate/test_verify_knowledge_base_scratch.py
Measure first
pwsh -NoProfile -File tools/scripts/dev/disk-census.ps1 # add -Quick to skip worktree and bin/obj walks
The census deletes nothing. It reports drives, the Docker and WSL virtual disks, docker system df, PostgreSQL database sizes, the repository tmp/ classes and worktrees, %TEMP%, package caches, Downloads, side-by-side .NET versions, duplicate Windows, Claude and Codex installations, agent session stores and Windows system files. It ends with a signal list that routes each class to its cleanup path, and writes JSON to tmp/scratch/disk-census/. Database budget verdicts come from tools/scripts/validate/check-data-duplication.sh. Agents run the measure, reclaim and re-measure sequence as the reclaim-disk-space skill.
An empty loose-file enumeration at the repository tmp/ root reports Count=0 and MB=0. A failed or partial loose-file read stops before JSON publication; earlier printed sections are partial evidence. Empty filtered windowed, Docker-VHDX and duplicate-install collections also retain their existing report paths without a strict-mode aggregation error. A zero from an empty observed collection does not establish the size of an unavailable resource or reclaimable space.
Verified against source commit 8ba9d6ae49fcb40e25a93aa5c0a3f2e5586cb218. Recheck the actual collector, JSON consumer and nonempty arithmetic offline, using a fresh owned output directory:
pwsh -NoProfile -File tools/scripts/dev/test-disk-census-empty.ps1 -OutputDirectory tmp/scratch/disk-census-check
The DSC-1 receipt records the source qualification and one dated Quick report, including skipped and partial coverage.
OS temp root
The nightly task below runs this sweep. To run it by hand, delete every file older than 24 hours, at any depth, and the directories that this empties:
pwsh -NoProfile -File tools/scripts/dev/sweep-host-temp.ps1 # dry run
pwsh -NoProfile -File tools/scripts/dev/sweep-host-temp.ps1 -Apply
Agent harness scratch (%TEMP%\claude\<project>\<session>) is removed only as whole sessions whose newest write is older than 24 hours, because a running agent reads its own task output back. The sweep never follows a junction and skips files that a running process holds open. A directory created within the window stays even when it is empty. -SelfTest proves these rules on a fixture under tmp/scratch/. For C:\Windows\Temp, run it elevated with -Root C:\Windows\Temp.
Safe online nightly cleanup
One scheduled task, StellaOpsDockerHygiene (daily 04:00, runner tools/scripts/dev/invoke-docker-hygiene-nightly.ps1), runs three steps and logs each under tmp/scratch/docker-hygiene-nightly/. Each step runs even when an earlier one refuses, and the task result is the first nonzero step:
- Protected Docker retention (
latest.log). worktree_hygiene.py sweep --scratch-only --apply(scratch-sweep.log). It removestmp/scratch/<entry>idle for 24 hours and loose files attmp/root older than 24 hours, unless a running container references the path or the entry belongs to a DOING row. It never retires a worktree and keeps the scheduled-task log homes.sweep-host-temp.ps1over%TEMP%andC:\Windows\Temp(host-temp.log).
The runner refuses to start when its sources are uncommitted or modified.
With Docker and WSL running, use the protected-image inventory and its reviewed cleanup plan:
& 'C:\Program Files\Git\bin\bash.exe' tools/scripts/dev/docker-hygiene.sh
# After reviewing the keep-set and removal plan:
& 'C:\Program Files\Git\bin\bash.exe' tools/scripts/dev/docker-hygiene.sh --apply
Do not age-prune named PostgreSQL volumes, attached WSL VHDX files, tracked or mounted runtime plugin bundles, NuGet caches, or src/**/bin, src/**/obj, and node_modules.
Blanket docker image prune -af, including age-filtered variants, can remove a running container’s image record under the containerd store. The hygiene tool protects container images, committed pins and rollback inputs, checks inventory completeness and caps build cache separately. It is the only Docker cleanup path; the blanket-prune entrypoint and its schedule were removed. Install or inspect the protected nightly task from the primary checkout, after landing, through the installer:
pwsh tools/scripts/dev/install-docker-hygiene-task.ps1
The installer registers a stable PowerShell: the machine install, else the Store alias %LOCALAPPDATA%\Microsoft\WindowsApps\pwsh.exe. It refuses a runtime under a .cache directory, because agent tools ship private copies there that their updates delete.
Windows.old
Use the Windows cleanup UI so servicing metadata is removed correctly:
- Open Settings > System > Storage > Temporary files.
- Select Previous Windows installation(s).
- Select Remove files and confirm.
Equivalent elevated command-line cleanup can be initiated with cleanmgr.exe, but the Settings path is preferred because it shows the exact reclaimable category before deletion.
Docker VHDX compaction maintenance window
Deleting Docker objects frees space inside docker_data.vhdx; it does not shrink the host file while Docker is running. Compaction therefore requires a planned outage:
Use the existing elevated StellaOps-WslVhdxOptimize task, whose source is tools/scripts/deploy/maintenance/wsl-vhdx-optimize.ps1. Coordinate a host-wide Docker/WSL window and release every active no-restart hold before starting it. On 2026-10-07 its action was corrected to the moved source path, preserving the interactive principal, highest elevation and two-day trigger; the next recorded automatic run is 2026-10-09 05:00 Europe/Sofia.
Check the task action, execution time limit and the current container/mount census. Run
tools/scripts/validate/check-disk-floor.shandcheck-live-compose-chains.shfrom Git Bash. The repaired script fails before shutdown if its required local endpoint, disk, compaction cmdlet, Desktop executable or task-owner interactive session is unavailable.Trigger the registered task from an authorized maintenance window:
Start-ScheduledTask -TaskName 'StellaOps-WslVhdxOptimize'The script trims available target distributions, gracefully stops saved applications before PostgreSQL/Valkey, shuts Docker/WSL down, and requires every target VHDX to be released (an exclusive open succeeds; one
wsl --shutdown --forcefollows after 180 s) before it runsOptimize-VHD -Mode Full. Failure lines name the exception type, script line and message. Itsfinallyrecovery starts all pre-window running identities, gates PostgreSQL readiness and verifies completed healthchecks. Failed compaction is reported after recovery. It does not recreate containers or change images, configuration or volumes.Require task result 0 and the healthy-estate closing line in
tmp/scratch/wsl-vhdx-optimize/vhdx-optimize-<date>.log(UTF-8). Record before/after VHDX lengths, compare container identities/images and mounts independently, and verify the gateway real path. Empty/unreadable inspection, missing or stopped containers, health still starting, changed images and lost healthchecks cannot satisfy acceptance.
The 2026-10-07 one-off sequence reclaimed 166.71 GiB (601.68 to 434.97 GiB) without converting the Docker-managed data disk to sparse. sparseVhd=true does not establish that this existing data file is sparse. The repaired scheduled script still requires its own live forcing function; the earlier one-off receipt and offline controls do not prove that run. Operational requirements, negative-control commands and current acceptance state are in the maintenance directory README.
WSL swap
swap.vhdx is WSL’s configured swap backing store, not disposable temp output. Check current usage with wsl -d docker-desktop -- free -h. Change the swap= value in %USERPROFILE%\.wslconfig only during a maintenance window, then apply it with wsl --shutdown. Do not delete an attached swap VHDX.
