← back to Terminal Status
T2 (TK-11779): add dead-tty .dot/.json reaper (ps-liveness authority, tar-backed --apply)
df6f28ce369c8ec9ca926dee2db14fc2e151ad1f · 2026-09-15 13:34:54 -0700 · steve
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuGCNYd1owT7PaPwi9t9sQ
Files touched
A integrations/reap-dead-dots.sh
Diff
commit df6f28ce369c8ec9ca926dee2db14fc2e151ad1f
Author: steve <steve@designerwallcoverings.com>
Date: Tue Sep 15 13:34:54 2026 -0700
T2 (TK-11779): add dead-tty .dot/.json reaper (ps-liveness authority, tar-backed --apply)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JuGCNYd1owT7PaPwi9t9sQ
---
integrations/reap-dead-dots.sh | 54 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 54 insertions(+)
diff --git a/integrations/reap-dead-dots.sh b/integrations/reap-dead-dots.sh
new file mode 100755
index 0000000..7962446
--- /dev/null
+++ b/integrations/reap-dead-dots.sh
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# reap-dead-dots.sh — remove color-dot state files for DEAD ttys (TK-11779 T2).
+#
+# The legacy .dot mirror (~/.claude/tab-dots, ~/.codex/tab-dots) and the canonical
+# store (~/.local/state/abrams-terminal-status/ttysNNN.json) accumulate one file per
+# tty that EVER had a dot, and nothing prunes them: ~118 .dot + ~121 .json vs ~63 live
+# ttys. Stale files are mostly harmless (every live consumer already does a live-tty
+# check) but they inflate scans and confuse any raw-glob reader. This reaps only files
+# whose tty has NO process at all (authoritative liveness: `ps -o tty=`), so a live but
+# momentarily-unpainted tty is NEVER reaped. Its file is regenerated on the tty's next
+# paint, so reaping is reversible-tier; --apply additionally tars a backup first.
+#
+# Usage:
+# reap-dead-dots.sh # DRY RUN (default): list dead-tty files + counts
+# reap-dead-dots.sh --apply # tar a backup, then delete the dead-tty files
+set -uo pipefail
+
+APPLY=0; [ "${1:-}" = "--apply" ] && APPLY=1
+DIRS=("$HOME/.claude/tab-dots" "$HOME/.codex/tab-dots" "$HOME/.local/state/abrams-terminal-status")
+BACKDIR="$HOME/.local/state/abrams-terminal-status/reap-backups"
+
+# LIVE = every tty with at least one running process. A tty absent here has no session.
+LIVE="$(ps -o tty= -A 2>/dev/null | tr -d ' ' | grep -E '^ttys[0-9]+$' | sort -u)"
+is_live(){ printf '%s\n' "$LIVE" | grep -qxF "$1"; }
+
+CAND=()
+for d in "${DIRS[@]}"; do
+ [ -d "$d" ] || continue
+ for f in "$d"/ttys*.dot "$d"/ttys*.json; do
+ [ -f "$f" ] || continue
+ base="$(basename "$f")"; tty="${base%.*}" # ttysNNN
+ case "$tty" in ttys[0-9]*) : ;; *) continue;; esac
+ is_live "$tty" || CAND+=("$f")
+ done
+done
+
+echo "── reap-dead-dots — live ttys: $(printf '%s\n' "$LIVE" | grep -c .) · dead-tty files: ${#CAND[@]} ──"
+if [ "${#CAND[@]}" -eq 0 ]; then echo "nothing to reap."; exit 0; fi
+printf ' %s\n' "${CAND[@]}" | sed "s#$HOME#~#" | head -80
+[ "${#CAND[@]}" -gt 80 ] && echo " … and $(( ${#CAND[@]} - 80 )) more"
+
+if [ "$APPLY" -ne 1 ]; then
+ echo "DRY RUN — nothing deleted. Re-run with --apply to tar-backup then delete."
+ exit 0
+fi
+
+mkdir -p "$BACKDIR"
+STAMP="$(date -u +%Y%m%dT%H%M%SZ)"
+TAR="$BACKDIR/reap-$STAMP.tar"
+# Backup (reversible undo = `tar xf <TAR> -C /`), then delete.
+tar -cf "$TAR" "${CAND[@]}" 2>/dev/null || { echo "backup tar failed — aborting, nothing deleted."; exit 1; }
+rm -f "${CAND[@]}"
+echo "reaped ${#CAND[@]} dead-tty files. backup: ${TAR/#$HOME/~}"
+echo "undo: tar xf $TAR -C /"
← 1286106 terminal-status: retry transient EAGAIN in write_terminal so
·
back to Terminal Status
·
terminal-status: keep lightblue label+ticket consistent (TK- e0a025a →