[object Object]

← 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

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 →