feat(disk): guard root disk space and reclaim build scratch on teardown - #30
Closed
knowttl wants to merge 2 commits into
Closed
feat(disk): guard root disk space and reclaim build scratch on teardown#30knowttl wants to merge 2 commits into
knowttl wants to merge 2 commits into
Conversation
Pooled build worktrees accumulate git-ignored install trees: `treehouse return` resets tracked content but leaves node_modules and build output in place, so every returned pool slot keeps the full install tree its last task built. Nothing watched free space either, so the shared disk filled silently until a build failed. Two independent safeguards: - bin/fm-reclaim-build-scratch.sh removes regenerable build scratch from a finished worktree, gated on the path being git-ignored, its basename being a known regenerable name, and the worktree having no modified tracked files. bin/fm-teardown.sh calls it only after its landed-work refusals have passed and the worktree's processes are reaped, and a refusal never blocks teardown. - bin/fm-guard.sh alarms, loudly and rate-limited per home, when the watched filesystem's free space drops below the configured headroom. It warns only: nothing is deleted and no spawn is paused. Threshold and watched path come from the gitignored config/disk-guard.
Owner
Author
|
Closing per captain's decision: not landing the durable disk-hygiene safeguards. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Intent
Deliver two durable, minimal disk-hygiene safeguards for firstmate, after a verified root cause: firstmate's pooled build worktrees live on the small root disk while Docker and the live DB live on a large network share. Every project worktree that builds a frontend gets its own full node_modules (~1.5G each), and these are NOT reclaimed when a crewmate's task tears down or the worktree returns to the pool - they accumulate (one secondmate's pool reached 18G across ~9 worktrees). There was no disk-space guard, so / filled silently until a live deploy failed at its build step; concurrent builds were the trigger.
SAFEGUARD 1 - reclaim build scratch on teardown. When firstmate tears down a completed, LANDED crewmate task (or a worktree is returned to the pool), clean that worktree's regenerable build scratch (the real patterns used across the fleet's projects: node_modules, dist, .next, build caches, etc.), which regenerates on the next install. HARD SAFETY: only ever remove REGENERABLE build scratch, and only for a worktree whose task has LANDED and is being torn down (or a genuinely idle returned pool slot with no live process). NEVER remove source, uncommitted or unlanded work, or anything under a still-live worktree. It must COMPOSE with the existing teardown landed-work test in bin/fm-teardown.sh - reclaim only after landing is confirmed, never a path that bypasses it. Fail closed: if unsure a worktree is safe, skip it.
SAFEGUARD 2 - root-disk-space guard in supervision. Surface a LOUD, rate-limited warning when the root filesystem free space drops below a configurable threshold (a sensible default headroom, overridable), so a fill is caught early instead of failing a build/deploy silently. Wire it into firstmate's existing guard surface (where the watcher-down / worktree-tangle warnings already surface) and/or session-start bootstrap detection. Keep it WARNING/ALARM only - deterministic and idempotent. Do NOT auto-delete caches or auto-pause spawns in this change; early detection is the goal. The threshold and the watched filesystem must be configurable via a gitignored config/ item, documented once in docs/configuration.md.
CONSTRAINTS accepted for this firstmate-repo task: follow the firstmate-coding-guidelines skill (knowledge-placement decision tree, one-owner rule, AGENTS.md size discipline, trigger hygiene, repo style). New config items are gitignored and documented once in their authoritative owner; mechanics live in the script header/--help, not AGENTS.md prose. Colocate tests in tests/ named .test.sh, exercising behavior through the public/executable interface (never asserting implementation bytes): cover the disk-guard threshold logic (warns below, quiet above) and the teardown-reclamation safety (never touches unlanded/live worktrees; only regenerable scratch). shellcheck-clean via bin/fm-lint.sh. One sentence per line in Markdown; plain dash; no agent co-author. Keep it MINIMAL - exactly the two safeguards, nothing more.
IMPLEMENTATION DECISIONS made while doing the work, which a reviewer reading only the diff would not know:
treehouse return --forceresets tracked content but leaves git-ignored trees in place, which is exactly why returned pool slots keep their install trees. The call is best effort: the script's own refusal, or any failure, never blocks teardown. Process-liveness safety is intentionally left owned by teardown's existing reap step rather than duplicated in the new script (one-owner rule).KNOWN, DELIBERATELY OUT OF SCOPE: bin/fm-test-run.sh --check-coverage fails on this host because its comm calls run under the ambient locale while its inputs are sorted with LC_ALL=C. That is pre-existing and unrelated to this change; it was reported rather than fixed, to keep the diff minimal.
What Changed
bin/fm-reclaim-build-scratch.sh, a fail-closed reclaimer that removes a finished worktree's regenerable build scratch (node_modules,dist,.next, tool caches, etc.), gated on three independent conditions: the worktree has no modified tracked files, git reports the path ignored, and its basename is on a fixed regenerable-name list; symlinks and paths resolving outside the worktree are skipped.bin/fm-teardown.shnow invokes it best-effort after every landed-work refusal and process reap have passed, before the worktree returns to the pool.bin/fm-guard.sh, checked independently of in-flight work alongside the tangle alarm: it prints a loud, rate-limited banner (once perFM_DISK_GUARD_REPEAT_SECS, default 3600, per home; re-armed on recovery) when the watched filesystem's free space falls below the configured headroom. It warns only and never deletes or pauses anything. Threshold and watched path come from the new gitignoredconfig/disk-guard(<min-free-gib> [<path>], default 20 GiB on/;0disables; an unusable threshold or unreadable path is reported rather than silently ignored).docs/configuration.md,docs/architecture.md,docs/scripts.md, and theconfig/disk-guardentry inAGENTS.md; added behavior suitestests/fm-guard-disk-space.test.shandtests/fm-reclaim-build-scratch.test.sh, plus teardown composition cases intests/fm-teardown.test.sh.Risk Assessment
✅ Low: The change is well-bounded and fail-closed: reclamation is triple-gated (clean tracked status, git-reported IGNORED, fixed regenerable-name list) with symlink/escape guards and composes after teardown's landed-work refusals and process reap, while the disk guard is warning-only, rate-limited, and correctly configured, all covered by behavior tests through executable interfaces.
Testing
Ran the three targeted suites covering both safeguards (reclaim behavior, disk-guard threshold/rate-limit, and the two teardown composition cases) - all pass - and produced two CLI transcripts as product-level evidence: the reclaim script clearing a landed frontend worktree's node_modules/dist/.next while preserving source and an ignored .env, then refusing a worktree with modified tracked work; and fm-guard.sh rendering its loud warning-only LOW-DISK banner below the configured headroom, staying silent above it, rate-limiting a repeat, and re-arming on recovery. This is a CLI/operational change with no UI surface, so the appropriate reviewer-visible evidence is the terminal transcripts rather than screenshots. The intent's declared out-of-scope --check-coverage locale failure was not exercised.
Evidence: Safeguard 1 reclaim end-to-end transcript
Source: Safeguard 1 reclaim end-to-end transcript
$ bin/fm-reclaim-build-scratch.sh frontend-worktree reclaim: removed 3 regenerable build-scratch path(s) under frontend-worktree exit=0 SURVIVED : src/app.js SURVIVED : .env SURVIVED : .gitignore RECLAIMED: node_modules RECLAIMED: dist RECLAIMED: .next --- with an UNCOMMITTED tracked edit --- reclaim: REFUSED: frontend-worktree has modified tracked files; leaving all scratch in place exit=1 CONFIRMED: node_modules left intact when tracked work is modifiedEvidence: Safeguard 2 low-disk alarm banner transcript
Source: Safeguard 2 low-disk alarm banner transcript
●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ● LOW DISK SPACE - BUILDS AND DEPLOYS ARE AT RISK ● / has 23.8G free, below the 999999999G headroom. ● A full disk fails a build or deploy with an error that names anything but the disk. ● Free space before dispatching more build work; finished worktrees are the usual hoard. ● This is a supervision warning only; the guarded operation WILL still run. ●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ guard-exit=0 (repeat inside window: quiet; recovery: CONFIRMED cleared the rate-limit marker)Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
✅ **Rebase** - passed
✅ No issues found.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-reclaim-build-scratch.test.sh- 6/6 ok (removes only ignored regenerable scratch, preserves .env/local-data/.venv/tracked, refuses dirty/non-worktree/missing-arg, no symlink escape, idempotent)bash tests/fm-guard-disk-space.test.sh- 5/5 ok (threshold decides alarm, config selects path/reports invalid threshold, rate-limit + re-arm on recovery, warn-only exit 0, read-only no state write)Ran the two new composition cases in tests/fm-teardown.test.sh in isolation (test_landed_teardown_reclaims_build_scratch_before_the_worktree_returns, test_refused_teardown_leaves_build_scratch_untouched) - both okManual E2E: ran bin/fm-reclaim-build-scratch.sh against a real landed frontend-shaped worktree (node_modules/dist/.next reclaimed, src/.env/.gitignore survived) and against the same worktree with an uncommitted tracked edit (REFUSED, exit 1, node_modules intact) -> /tmp/no-mistakes-evidence/01M0B0ARS8KR2D60DVDQDY1ARJ/reclaim-transcript.txtManual E2E: ran bin/fm-guard.sh against a scratch FM_HOME with config/disk-guard, capturing the silent-above-threshold case, the rendered LOW-DISK banner (exit 0), the rate-limited re-run, and the recovery re-arm -> /tmp/no-mistakes-evidence/01M0B0ARS8KR2D60DVDQDY1ARJ/disk-guard-transcript.txt✅ **Document** - passed
✅ No issues found.
✅ **Lint** - passed
✅ No issues found.
✅ **Push** - passed
✅ No issues found.