[object Object]

← back to Terminal Status

ticket_binding: a slow ps must degrade the label, not kill the paint

fea7539a60ffa0e53a2c008c07e0b7e3400314da · 2026-09-10 08:46:15 -0700 · Steve Abrams

Surfaced by the logging added in 29475ed: within minutes of the dot-floor hook
finally recording real failures, repaint was failing every 1-3 minutes on this
box with `Command '['ps', '-p', <~49 pids>]' timed out after 8 seconds`.

This call is a THIRD process-table read, in ticket_binding.py rather than
terminal_status.py, which is why an audit of the engine missed it. With ~49
live sessions the pid list is large and 8s was not survivable at load 53.78.

Worse than the timeout was the severity: it raised, which killed the ENTIRE
repaint, so no dot was painted at all. The block's own comment calls it "a
conservative fallback" for the ticket LABEL -- optional enrichment. An optional
enrichment must never take down the paint.

Raised to 60s to match the sibling process-table read, and made non-fatal: on
timeout the label is skipped, the blind spot is recorded, and the dot still
paints. Verified 5/5 consecutive clean hook runs with zero new failures logged.

Extends DTD 2026-09-10 verdict C to the same defect class in a third location.
Reversible: git revert.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PXNMS1TvMiVbE3ckhSaLeT

Files touched

Diff

commit fea7539a60ffa0e53a2c008c07e0b7e3400314da
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 08:46:15 2026 -0700

    ticket_binding: a slow ps must degrade the label, not kill the paint
    
    Surfaced by the logging added in 29475ed: within minutes of the dot-floor hook
    finally recording real failures, repaint was failing every 1-3 minutes on this
    box with `Command '['ps', '-p', <~49 pids>]' timed out after 8 seconds`.
    
    This call is a THIRD process-table read, in ticket_binding.py rather than
    terminal_status.py, which is why an audit of the engine missed it. With ~49
    live sessions the pid list is large and 8s was not survivable at load 53.78.
    
    Worse than the timeout was the severity: it raised, which killed the ENTIRE
    repaint, so no dot was painted at all. The block's own comment calls it "a
    conservative fallback" for the ticket LABEL -- optional enrichment. An optional
    enrichment must never take down the paint.
    
    Raised to 60s to match the sibling process-table read, and made non-fatal: on
    timeout the label is skipped, the blind spot is recorded, and the dot still
    paints. Verified 5/5 consecutive clean hook runs with zero new failures logged.
    
    Extends DTD 2026-09-10 verdict C to the same defect class in a third location.
    Reversible: git revert.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01PXNMS1TvMiVbE3ckhSaLeT
---
 ticket_binding.py | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/ticket_binding.py b/ticket_binding.py
index e8fa5c2..11d2957 100644
--- a/ticket_binding.py
+++ b/ticket_binding.py
@@ -57,14 +57,29 @@ def discover(root, rows, live, chain, argv=None):
     result = {tty: claims.get(tty, actions.get(tty)) for tty in live}
     # A unique ticket in the main process launch command is a conservative fallback.
     if argv is None and live:
-        output = subprocess.run(["ps", "-p", ",".join(str(o.pid) for o in live.values()),
-                                 "-o", "pid=,args="], capture_output=True, text=True,
-                                timeout=8)
+        # TK-11369: was timeout=8 and FATAL. With ~49 live sessions this pid list
+        # is large, and on a loaded box (53.78 today) it blew 8s -- which raised
+        # and killed the ENTIRE repaint, so no dot was painted at all. That is
+        # backwards: this block is an optional enrichment (see the comment above
+        # -- "a conservative fallback" for the ticket LABEL). A slow ps must
+        # degrade the label, never the paint. Raised to 60s to match the sibling
+        # process-table read, and made non-fatal.
         argv = {}
-        for line in output.stdout.splitlines():
-            parts = line.strip().split(None, 1)
-            if len(parts) == 2:
-                argv[int(parts[0])] = parts[1]
+        try:
+            output = subprocess.run(["ps", "-p", ",".join(str(o.pid) for o in live.values()),
+                                     "-o", "pid=,args="], capture_output=True, text=True,
+                                    timeout=60)
+            for line in output.stdout.splitlines():
+                parts = line.strip().split(None, 1)
+                if len(parts) == 2:
+                    argv[int(parts[0])] = parts[1]
+        except (OSError, subprocess.TimeoutExpired) as exc:
+            # Label enrichment unavailable; the dot itself still paints.
+            try:
+                from terminal_status import _record_enum_blind
+                _record_enum_blind("ticket_binding ps -p: " + type(exc).__name__)
+            except Exception:
+                pass
     for tty, owner in live.items():
         if result[tty]:
             continue

← 29475ed osascript enumeration: raise timeout to 30s and record blind  ·  back to Terminal Status  ·  lock: stop holding the per-tty lock across the 14.84s proces 6eae7e8 →