[object Object]

← back to Terminal Status

set: automatic paints yield to sticky semantic dots (orange/purple/yellow)

e6a426bbfeb783df18d98983311f612ae718852d · 2026-09-10 08:21:00 -0700 · Steve Abrams

A UserPromptSubmit hook fires `set green WORKING` on EVERY prompt, and green
overwrote whatever dot the tab held. So typing into a tab silently erased its
purple (gated memo), orange (paste waiting) or yellow (question waiting) and
replaced it with green = 'nothing needed'. Observed twice in one session.

PRIORITY (orange > purple > yellow > green > pink > none) already existed at
line 31 but was only ever used to sort scan() output, never to arbitrate a
paint. This makes set() consult it: a caller passing --deferential will not
overwrite a state ranked above green; it repaints the existing record and
returns it unchanged (no revision bump, no mirror churn).

Only the automatic per-prompt painter passes --deferential. Every EXPLICIT
paint stays authoritative, so /greendot, /color, /pinkdot and --off can still
clear a sticky dot and a tab can still be reused for new work. The teal
monitoring variant is green-family and yields too.

Verified with an in-process truth table (11/11): sticky states survive an
automatic green; green/pink do not; explicit green/pink/none still win;
revision does not bump when the guard yields.

TK-11378, DTD verdict A (6/7). Steve approved 2026-09-10.

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

Files touched

Diff

commit e6a426bbfeb783df18d98983311f612ae718852d
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Sep 10 08:21:00 2026 -0700

    set: automatic paints yield to sticky semantic dots (orange/purple/yellow)
    
    A UserPromptSubmit hook fires `set green WORKING` on EVERY prompt, and green
    overwrote whatever dot the tab held. So typing into a tab silently erased its
    purple (gated memo), orange (paste waiting) or yellow (question waiting) and
    replaced it with green = 'nothing needed'. Observed twice in one session.
    
    PRIORITY (orange > purple > yellow > green > pink > none) already existed at
    line 31 but was only ever used to sort scan() output, never to arbitrate a
    paint. This makes set() consult it: a caller passing --deferential will not
    overwrite a state ranked above green; it repaints the existing record and
    returns it unchanged (no revision bump, no mirror churn).
    
    Only the automatic per-prompt painter passes --deferential. Every EXPLICIT
    paint stays authoritative, so /greendot, /color, /pinkdot and --off can still
    clear a sticky dot and a tab can still be reused for new work. The teal
    monitoring variant is green-family and yields too.
    
    Verified with an in-process truth table (11/11): sticky states survive an
    automatic green; green/pink do not; explicit green/pink/none still win;
    revision does not bump when the guard yields.
    
    TK-11378, DTD verdict A (6/7). Steve approved 2026-09-10.
    
    Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01RwTfiTtWZNjLoowhjXdWV1
---
 terminal_status.py | 24 ++++++++++++++++++++++--
 1 file changed, 22 insertions(+), 2 deletions(-)

diff --git a/terminal_status.py b/terminal_status.py
index 4cab0a8..03068f0 100644
--- a/terminal_status.py
+++ b/terminal_status.py
@@ -329,7 +329,7 @@ class Store:
         return r, reason
 
     def set(self, owner, color, label="", *, issued_ns=None, ticket_update=None,
-            variant=""):
+            variant="", deferential=False):
         issued_ns = issued_ns if issued_ns is not None else issued_clock()
         if color not in COLORS or not valid_label(label) or variant not in ("", "monitoring"):
             raise StatusError("Invalid status or label")
@@ -345,6 +345,22 @@ class Store:
             previous, _ = self.load(owner)
             if previous and issued_ns <= previous["issued_ns"]:
                 raise StatusError("Obsolete status update; a newer event already won")
+            # TK-11378 (DTD verdict A) — an AUTOMATIC paint must never erase a pending
+            # request for Steve's attention. PRIORITY already encodes the ordering
+            # (orange > purple > yellow > green > pink > none) but until now was only
+            # used to sort scan() output, never to arbitrate a paint. "Sticky" = anything
+            # ranked above green, i.e. the three colors that mean a human is blocked.
+            # Only deferential callers (the per-prompt working-state hook) yield; every
+            # EXPLICIT paint (/greendot, /color, /pinkdot, --off) stays authoritative, so
+            # a session can still clear its own dot or reuse the tab for new work.
+            if (deferential and previous and color == "green"
+                    and PRIORITY.get(previous.get("state"), PRIORITY["none"]) < PRIORITY["green"]):
+                self.assert_owner(owner)
+                try:
+                    self.painter(owner, previous)
+                except (OSError, StatusError):
+                    pass  # keep the record authoritative even if the repaint fails
+                return previous
             ticket = self.ticket_info(owner, previous)
             if ticket_update is not None:
                 ticket = {"id": ticket_update, "at": time.time(), "source": "explicit"}
@@ -490,6 +506,9 @@ def main(argv=None):
             cmd.add_argument("label", nargs="?", default="")
             cmd.add_argument("--all", choices=["claude", "codex"])
             cmd.add_argument("--monitoring", action="store_true")
+            cmd.add_argument("--deferential", action="store_true",
+                             help="automatic paint: yield to a sticky semantic dot "
+                                  "(orange/purple/yellow) instead of overwriting it")
         if name == "ticket":
             cmd.add_argument("ticket")
         cmd.add_argument("--quiet", action="store_true")
@@ -596,7 +615,8 @@ def main(argv=None):
             record = store.repaint(owner)
         else:
             record = store.set(owner, color, label,
-                               variant="monitoring" if getattr(args, "monitoring", False) else "")
+                               variant="monitoring" if getattr(args, "monitoring", False) else "",
+                               deferential=getattr(args, "deferential", False))
         if not getattr(args, "quiet", False):
             print(f'/terminal-status → /dev/{owner.tty} {record["title"] or "CLEARED"}')
     return 0

← 63f3fae fix: raise process-table timeout so dot painting survives a  ·  back to Terminal Status  ·  osascript enumeration: raise timeout to 30s and record blind 29475ed →