From 352b7344fe9994f03f8940217380ff38d87230ed Mon Sep 17 00:00:00 2001 From: gcaspar Date: Tue, 4 Aug 2026 12:05:16 +0200 Subject: [PATCH] fix(statusline): bound the /tmp pai-parallel scratch-dir leak Cleanup lived only on the exit path -- the rm -rf sits ~480 lines after the mkdir, past several network calls -- so any render Claude Code killed leaked its scratch dir permanently. At refreshInterval=1 across 4 sessions this reached 244,760 directories and exhausted the tmpfs inode table while df -h still showed 81% free, which kills every shell-based tool in the session. Adds two layers, because neither is sufficient alone: - Entry-path sweep of dirs older than a minute. The only layer that covers SIGKILL, which cannot be trapped. The trailing slash on /tmp/ is required or this is a silent no-op on macOS, where /tmp is a symlink find(1) will not descend into. - A trap, measured to fully close the group-SIGTERM path: 5/5 renders leaked without it, 0/5 with it. Per-PID dirs are kept deliberately; a fixed name races and concurrent sessions clobber each other's files mid-source. Follow-up to #1297, which was closed for architectural reasons before the installer layout landed. Co-Authored-By: Claude Opus 5 (1M context) --- LifeOS/install/LIFEOS/LIFEOS_StatusLine.sh | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/LifeOS/install/LIFEOS/LIFEOS_StatusLine.sh b/LifeOS/install/LIFEOS/LIFEOS_StatusLine.sh index b5e7bd48af..ebad59de53 100755 --- a/LifeOS/install/LIFEOS/LIFEOS_StatusLine.sh +++ b/LifeOS/install/LIFEOS/LIFEOS_StatusLine.sh @@ -582,7 +582,22 @@ sep() { # Results are sourced after `wait` at the end of the block. _parallel_tmp="/tmp/pai-parallel-$$" +# Two-layer cleanup. The rm -rf near the end of this script is not reachable on +# every invocation: Claude Code terminates a slow statusline, and at +# refreshInterval=1 each killed render leaves its scratch dir behind forever. +# +# 1. Entry-path sweep of dirs older than a minute. A live render finishes in well +# under a second, so anything older is orphaned. This is the only layer that +# covers SIGKILL, which cannot be trapped. Backgrounded so it never adds +# latency. The trailing slash on /tmp/ is required, not cosmetic: on macOS +# /tmp is a symlink, and find(1) will not descend into a symlinked start +# point, so without it this silently matches nothing. +# 2. A trap, which returns the dir promptly on the common signal path instead of +# leaving it for the next run to sweep. EXIT traps are reset in subshells, so +# the backgrounded blocks below cannot fire this and delete the dir mid-render. +find /tmp/ -maxdepth 1 -type d -name 'pai-parallel-*' -mmin +1 -exec rm -rf {} + 2>/dev/null & mkdir -p "$_parallel_tmp" +trap 'rm -rf "$_parallel_tmp" 2>/dev/null' EXIT INT TERM HUP NOW_EPOCH=$(date +%s) # --- PARALLEL BLOCK START ---