[object Object]

← back to Terminal Status

snapshot before child-session paint-guard fix

418fa0942294896ab7cfbbcb6b5f6660d8201eb9 · 2026-09-09 14:54:02 -0700 · Steve Abrams

Files touched

Diff

commit 418fa0942294896ab7cfbbcb6b5f6660d8201eb9
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed Sep 9 14:54:02 2026 -0700

    snapshot before child-session paint-guard fix
---
 configure_codex.py                                 |  62 +++++
 install.py                                         | 166 ++++++++++++++
 integrations/current-dot.sh                        |   3 +
 integrations/lightgreendot.sh                      |   9 +
 integrations/tab-ticket-set.sh                     |   5 +
 integrations/tab-ticket-title.sh                   |   4 +
 pin_codex_headers.py                               |  60 +++++
 terminal_status.py                                 | 158 +++++++++++--
 test_terminal_status.py                            |  88 ++++++++
 ticket_binding.py                                  |  79 +++++++
 .../after/.agents/skills/color/scripts/color.sh    | 170 ++++++++++++++
 .../.agents/skills/color/scripts/repaint-dot.sh    |   3 +
 .../.agents/skills/color/scripts/semantic-dot.sh   |   7 +
 .../install/after/.claude/hooks/dot-floor.sh       |   4 +
 .../.claude/skills/allcolordots/allcolordots.sh    | 114 ++++++++++
 .../after/.claude/skills/color/scripts/color.sh    | 249 +++++++++++++++++++++
 .../.claude/skills/color/scripts/paint-tab.sh      |  10 +
 .../.claude/skills/color/scripts/tab-state.sh      |  11 +
 .../.claude/skills/color/scripts/working-state.sh  |  10 +
 .../after/.claude/skills/colordots/paint-lib.sh    |  12 +
 .../install/after/.claude/skills/dot/current.sh    |   3 +
 .../after/.claude/skills/greendot/greendot.sh      |   3 +
 .../.claude/skills/lightgreendot/lightgreendot.sh  |   9 +
 .../after/.claude/skills/orangedot/orangedot.sh    |   3 +
 .../after/.claude/skills/pinkdot/pinkdot.sh        |   3 +
 .../after/.claude/skills/purpledot/purpledot.sh    |   3 +
 .../skills/tab-ticket/scripts/tab-ticket-set.sh    |   5 +
 .../skills/tab-ticket/scripts/tab-ticket-title.sh  |   4 +
 .../after/.claude/skills/yellowdot/yellowdot.sh    |   3 +
 .../before/.agents/skills/color/scripts/color.sh   | 167 ++++++++++++++
 .../.agents/skills/color/scripts/repaint-dot.sh    |  36 +++
 .../.agents/skills/color/scripts/semantic-dot.sh   |  70 ++++++
 .../install/before/.claude/hooks/dot-floor.sh      |  40 ++++
 .../.claude/skills/allcolordots/allcolordots.sh    | 196 ++++++++++++++++
 .../before/.claude/skills/color/scripts/color.sh   | 246 ++++++++++++++++++++
 .../.claude/skills/color/scripts/paint-tab.sh      |  94 ++++++++
 .../.claude/skills/color/scripts/tab-state.sh      | 170 ++++++++++++++
 .../.claude/skills/color/scripts/working-state.sh  | 219 ++++++++++++++++++
 .../before/.claude/skills/colordots/paint-lib.sh   |  75 +++++++
 .../install/before/.claude/skills/dot/current.sh   |  25 +++
 .../before/.claude/skills/greendot/greendot.sh     |  61 +++++
 .../.claude/skills/lightgreendot/lightgreendot.sh  |  72 ++++++
 .../before/.claude/skills/orangedot/orangedot.sh   |  98 ++++++++
 .../before/.claude/skills/pinkdot/pinkdot.sh       |  98 ++++++++
 .../before/.claude/skills/purpledot/purpledot.sh   |  98 ++++++++
 .../skills/tab-ticket/scripts/tab-ticket-set.sh    |  60 +++++
 .../skills/tab-ticket/scripts/tab-ticket-title.sh  | 115 ++++++++++
 .../before/.claude/skills/yellowdot/yellowdot.sh   |  94 ++++++++
 verify_live.py                                     |  77 +++++++
 verify_ui.py                                       |  28 +++
 50 files changed, 3381 insertions(+), 18 deletions(-)

diff --git a/configure_codex.py b/configure_codex.py
new file mode 100644
index 0000000..74c4c39
--- /dev/null
+++ b/configure_codex.py
@@ -0,0 +1,62 @@
+#!/usr/bin/env python3
+"""One-time local Codex title/default instructions; private preimage backups."""
+import hashlib
+import json
+from pathlib import Path
+import re
+from terminal_status import atomic_write
+
+root = Path.home()
+backup = root / '.local/state/abrams-terminal-status/config-backups'
+config = root / '.codex/config.toml'
+instructions = root / '.codex/AGENTS.md'
+changes = {}
+text = config.read_text()
+if re.search(r'^\[tui\]\s*$', text, re.M):
+    match = re.search(r'^\[tui\]\s*\n(?P<body>.*?)(?=^\[|\Z)', text, re.M | re.S)
+    body = match['body']
+    if re.search(r'^terminal_title\s*=', body, re.M):
+        body = re.sub(r'^terminal_title\s*=.*$', 'terminal_title = []', body, flags=re.M)
+    else:
+        body = 'terminal_title = []\n' + body
+    text = text[:match.start('body')] + body + text[match.end('body'):]
+else:
+    text += '\n# Shared TK/dot renderer owns terminal titles.\n[tui]\nterminal_title = []\n'
+changes[config] = text
+text = instructions.read_text()
+marker = '## Every Codex build displays its ticket and semantic dot'
+if marker not in text:
+    text += '''
+
+## Every Codex build displays its ticket and semantic dot
+
+At the start of every non-trivial build, use the canonical tickets MCP
+(CLI fallback ~/Projects/ticket-system/tk) to find/create/take its ticket.
+Immediately bind it to this session:
+
+    python3 ~/Projects/terminal-status/terminal_status.py ticket TK-NUMBER
+
+Keep the ticket visible in the terminal header, badge, and desktop dots menu.
+Set status using the existing Codex dot skills, all backed by this shared engine:
+green = working; yellow = direction needed; purple = approval gated;
+orange = pasted input needed; pink = parked. Put the short reason in the label.
+Generic WORKING/repaint hooks preserve the ticket; never write a competing
+status file or start a title-paint loop. For monitoring with a scheduled check,
+use set green "MONITORING · next TIME" --monitoring on the same engine.
+Update the binding when the main task changes. An unbound build must be
+visibly TK REQUIRED until claimed; never invent or reuse a stale ticket.
+Subagents inherit the ticket and must never paint their parent's terminal.
+Implementation and rollback: ~/Projects/terminal-status/README.md.
+'''
+changes[instructions] = text
+report = []
+for path, desired in changes.items():
+    original = path.read_text()
+    saved = backup / (path.name + '.before')
+    if not saved.exists():
+        atomic_write(saved, original)
+    if original != desired:
+        atomic_write(path, desired)
+    report.append({'path': str(path), 'backup': str(saved),
+                   'after_sha256': hashlib.sha256(desired.encode()).hexdigest()})
+print(json.dumps(report, indent=2))
diff --git a/install.py b/install.py
new file mode 100644
index 0000000..401ce17
--- /dev/null
+++ b/install.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python3
+"""Prepare, inspect, apply, or roll back a fixed list of local script adapters."""
+import argparse
+import hashlib
+import json
+import os
+from pathlib import Path
+import stat
+import subprocess
+import sys
+
+from terminal_status import atomic_write
+
+PROJECT = Path(__file__).resolve().parent
+USER_ROOT = Path.home()
+PLAN = PROJECT / "verification/install-plan.json"
+BUNDLE = PROJECT / "verification/install"
+
+
+def digest(data):
+    return hashlib.sha256(data).hexdigest()
+
+
+def replacements():
+    files = {}
+    for color in ("green", "yellow", "purple", "orange", "pink"):
+        path = f".claude/skills/{color}dot/{color}dot.sh"
+        files[path] = (
+            '#!/usr/bin/env bash\n'
+            '# Shared semantic status; see Projects/terminal-status/README.md.\n'
+            f'exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude {color} "$@"\n')
+    mapping = {
+        ".claude/skills/colordots/paint-lib.sh": "paint-lib.sh",
+        ".claude/hooks/dot-floor.sh": "dot-floor.sh",
+        ".claude/skills/color/scripts/working-state.sh": "working-state.sh",
+        ".claude/skills/color/scripts/paint-tab.sh": "paint-tab.sh",
+        ".claude/skills/color/scripts/tab-state.sh": "tab-state.sh",
+        ".agents/skills/color/scripts/semantic-dot.sh": "semantic-dot.sh",
+        ".agents/skills/color/scripts/repaint-dot.sh": "repaint-dot.sh",
+        ".claude/skills/tab-ticket/scripts/tab-ticket-set.sh": "tab-ticket-set.sh",
+        ".claude/skills/tab-ticket/scripts/tab-ticket-title.sh": "tab-ticket-title.sh",
+        ".claude/skills/dot/current.sh": "current-dot.sh",
+        ".claude/skills/lightgreendot/lightgreendot.sh": "lightgreendot.sh",
+    }
+    for target, source in mapping.items():
+        files[target] = (PROJECT / "integrations" / source).read_text()
+
+    scanner = ".claude/skills/allcolordots/allcolordots.sh"
+    baseline = BUNDLE / "before" / scanner
+    text = (baseline if baseline.exists() else USER_ROOT / scanner).read_text()
+    start = text.index("# Codex rewrites its title")
+    end = text.index("# ---- JUMP:")
+    text = text[:start] + text[end:]
+    start = text.index("# ---- collect normalized")
+    end = text.index("# ---- roster of ONE color")
+    text = text[:start] + (
+        '# ---- Shared state scanner: presentation never overrides canonical state ----\n'
+        'collect_rows(){\n'
+        '  python3 "$HOME/Projects/terminal-status/terminal_status.py" scan --tsv\n'
+        '}\n\n') + text[end:]
+    start = text.index("# Prefer semantic LIVE")
+    end = text.index("# Usage:")
+    text = text[:start] + (
+        '# Status comes from the shared session-bound terminal-status engine.\n'
+        '# Labels and dot files are presentation; exact main agent ownership is required.\n'
+        '#\n') + text[end:]
+    text = text.replace('CODEX_DOTS="$HOME/.codex/tab-dots"\n', '')
+    text = text.replace('set -u\n', 'set -u\nset -o pipefail\n')
+    text = "\n".join(line + ' || return $?' if 'local want=' in line and 'collect_rows' in line else line for line in text.split('\n'))
+    text = text.replace('print_roster yellow; exit 0', 'print_roster yellow; exit $?')
+    text = text.replace('print_roster "${2:-yellow}"; exit 0', 'print_roster "${2:-yellow}"; exit $?')
+    text = text.replace("    exit 0 ;;", "    exit $? ;;")
+    text = text.replace('rows="$(collect_rows)"', 'rows="$(collect_rows)" || exit $?')
+    files[scanner] = text
+
+    # Keep the existing SessionStart hook and initialize semantic status afterward.
+    for target in (".claude/skills/color/scripts/color.sh",
+                   ".agents/skills/color/scripts/color.sh"):
+        text = (USER_ROOT / target).read_text()
+        marker = "# Shared terminal status follows the cosmetic session color."
+        if marker not in text:
+            text += (
+                '\n' + marker + '\n'
+                'python3 "$HOME/Projects/terminal-status/terminal_status.py" start --quiet || true\n')
+        files[target] = text
+    return files
+
+
+def prepare(extend=False):
+    if PLAN.exists() and not extend:
+        raise SystemExit("Plan already exists; inspect it instead of replacing the baseline")
+    old = {item["relative"]: item for item in json.loads(PLAN.read_text())} if extend else {}
+    entries = []
+    for relative, content in replacements().items():
+        target = USER_ROOT / relative
+        if target.is_symlink():
+            raise SystemExit("Refusing to replace symlink: " + str(target))
+        original = target.read_bytes()
+        before = BUNDLE / "before" / relative
+        after = BUNDLE / "after" / relative
+        if relative in old:
+            if digest(original) not in (old[relative]["after_sha256"], old[relative]["before_sha256"], old[relative].get("upgrade_sha256")):
+                raise SystemExit("Concurrent adapter edit: " + relative)
+        else:
+            atomic_write(before, original.decode())
+        atomic_write(after, content)
+        subprocess.run(["bash", "-n", str(after)], check=True)
+        entries.append({
+            "relative": relative, "target": str(target),
+            "before": str(before), "after": str(after),
+            "before_sha256": digest(before.read_bytes()),
+            "after_sha256": digest(content.encode()),
+            "upgrade_sha256": digest(original),
+            "mode": stat.S_IMODE(target.stat().st_mode),
+        })
+    atomic_write(PLAN, json.dumps(entries, indent=2) + "\n")
+    print(f"Prepared {len(entries)} script changes: {PLAN}")
+
+
+def change(rollback=False):
+    entries = json.loads(PLAN.read_text())
+    expected = "after_sha256" if rollback else "before_sha256"
+    desired = "before_sha256" if rollback else "after_sha256"
+    source_key = "before" if rollback else "after"
+    # Check every preimage before the first mutation; preserve concurrent edits.
+    for item in entries:
+        target = Path(item["target"])
+        if target.is_symlink():
+            raise SystemExit("Refusing to replace symlink: " + str(target))
+        current = digest(target.read_bytes())
+        allowed = (item[expected], item[desired], item.get("upgrade_sha256")) if not rollback else (item[expected], item[desired])
+        if current not in allowed:
+            raise SystemExit("Concurrent edit; no changes applied: " + str(target))
+        if digest(Path(item[source_key]).read_bytes()) != item[desired]:
+            raise SystemExit("Changed install bundle; no changes applied")
+    for item in entries:
+        target = Path(item["target"])
+        if digest(target.read_bytes()) == item[desired]:
+            continue
+        atomic_write(target, Path(item[source_key]).read_text())
+        os.chmod(target, item["mode"])
+    print(("Restored" if rollback else "Installed") + f" {len(entries)} script adapters")
+
+
+def check():
+    failed = []
+    for item in json.loads(PLAN.read_text()):
+        if digest(Path(item["target"]).read_bytes()) != item["after_sha256"]:
+            failed.append(item["target"])
+    if failed:
+        print(json.dumps({"ok": False, "drift": failed}, indent=2))
+        return 1
+    print(json.dumps({"ok": True, "adapters": len(json.loads(PLAN.read_text()))}))
+    return 0
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description=__doc__)
+    parser.add_argument("action", choices=["prepare", "extend", "apply", "rollback", "check"])
+    args = parser.parse_args()
+    if args.action in ("prepare", "extend"):
+        prepare(extend=args.action == "extend")
+    elif args.action == "check":
+        sys.exit(check())
+    else:
+        change(rollback=args.action == "rollback")
diff --git a/integrations/current-dot.sh b/integrations/current-dot.sh
new file mode 100644
index 0000000..5c0a81a
--- /dev/null
+++ b/integrations/current-dot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+set -eu
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" status
diff --git a/integrations/lightgreendot.sh b/integrations/lightgreendot.sh
new file mode 100644
index 0000000..e8a9cf9
--- /dev/null
+++ b/integrations/lightgreendot.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Green monitoring variant with a scheduled check/deadline.
+set -eu
+engine="$HOME/Projects/terminal-status/terminal_status.py"
+case "${1:-}" in
+  --off) exec python3 "$engine" clear ;;
+  --all) exec python3 "$engine" set green MONITORING --monitoring --all claude ;;
+  *) exec python3 "$engine" set green "${1:-MONITORING}" --monitoring ;;
+esac
diff --git a/integrations/tab-ticket-set.sh b/integrations/tab-ticket-set.sh
new file mode 100644
index 0000000..660d549
--- /dev/null
+++ b/integrations/tab-ticket-set.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+# Ticket binding is tied to the actual main agent's PID, start time, and tty.
+set -eu
+[[ "${1:-}" == "--apply" ]] && shift
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" ticket "${1:?TK number required}"
diff --git a/integrations/tab-ticket-title.sh b/integrations/tab-ticket-title.sh
new file mode 100644
index 0000000..9c05047
--- /dev/null
+++ b/integrations/tab-ticket-title.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+# Existing launchd renderer; read-only, no independent state or background loop.
+set -eu
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" headers
diff --git a/pin_codex_headers.py b/pin_codex_headers.py
new file mode 100644
index 0000000..3b0f083
--- /dev/null
+++ b/pin_codex_headers.py
@@ -0,0 +1,60 @@
+#!/usr/bin/env python3
+"""One-time native bindings for currently live Codex windows; no timer installed."""
+import json
+from pathlib import Path
+import subprocess
+import terminal_status as ts
+
+live = ts.owners(ts.processes())
+codex = {tty for tty, owner in live.items() if owner.runtime == 'codex'}
+script = '''
+tell application "iTerm2"
+ set resultRows to ""
+ repeat with w in windows
+  repeat with ti from 1 to count of tabs of w
+   repeat with s in sessions of tab ti of w
+    set resultRows to resultRows & (id of w as text) & " " & ti & " " & (tty of s) & linefeed
+   end repeat
+  end repeat
+ end repeat
+ return resultRows
+end tell
+'''
+output = subprocess.run(['osascript', '-e', script], capture_output=True, text=True, check=True).stdout
+targets = set()
+for line in output.splitlines():
+    parts = line.split()
+    if len(parts) == 3 and parts[2].removeprefix('/dev/') in codex:
+        targets.add((int(parts[0]), int(parts[1])))
+evidence = []
+for window, tab in sorted(targets):
+    script = f'''
+tell application "iTerm2"
+ select window id {window}
+ select tab {tab} of window id {window}
+ activate
+end tell
+tell application "System Events" to tell process "iTerm2"
+ click menu item "Edit Tab Title" of menu 1 of menu item "Tab" of menu "Window" of menu bar 1
+ delay 0.2
+ if not (exists static text "Set Tab Title" of sheet 1 of window 1) then error "Unexpected tab dialog"
+ set oldTab to value of text field 1 of sheet 1 of window 1
+ set value of text field 1 of sheet 1 of window 1 to "\\\\(currentSession.user.abramsTitle)"
+ click button "OK" of sheet 1 of window 1
+ click menu item "Edit Window Title" of menu "Window" of menu bar 1
+ delay 0.2
+ if not (exists static text "Set Window Title" of window 1) then error "Unexpected window dialog"
+ set oldWindow to value of text field 1 of window 1
+ set value of text field 1 of window 1 to "\\\\(currentTab.currentSession.user.abramsTitle)"
+ click button "OK" of window 1
+end tell
+delay 0.2
+tell application "iTerm2"
+ return oldTab & linefeed & oldWindow & linefeed & (name of window id {window})
+end tell
+'''
+    result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True, check=True)
+    evidence.append({'window': window, 'tab': tab, 'result': result.stdout.strip()})
+path = Path.home() / '.local/state/abrams-terminal-status/native-title-bindings.json'
+ts.atomic_write(path, json.dumps(evidence, ensure_ascii=False, indent=2) + '\n')
+print(json.dumps(evidence, ensure_ascii=False, indent=2))
diff --git a/terminal_status.py b/terminal_status.py
index e8d53a7..32282df 100644
--- a/terminal_status.py
+++ b/terminal_status.py
@@ -18,6 +18,7 @@ import sys
 import tempfile
 import time
 import uuid
+import ticket_binding as tickets
 
 COLORS = {
     "green": ("🟢", (0, 200, 83), "WORKING"),
@@ -33,6 +34,34 @@ TTY = re.compile(r"ttys[0-9]+")
 VERSION = 1
 
 
+def issued_clock():
+    # Python <3.10 on macOS made monotonic_ns() process-relative. Read the
+    # underlying system-wide raw clock so queued events compare across runtimes.
+    return time.clock_gettime_ns(getattr(time, "CLOCK_MONOTONIC_RAW", time.CLOCK_MONOTONIC))
+
+
+def status_title(color, label, ticket=""):
+    if color == "none":
+        return ""
+    return COLORS[color][0] + " " + " · ".join(p for p in (ticket, label) if p)
+
+
+def label_ticket(label):
+    label = label.strip(" ·")
+    match = re.match(r"^(TK-\d+)(?=$|[ ·:-])", label, re.I)
+    ticket = match[1].upper() if match else ""
+    if ticket:
+        label = label[len(ticket):].lstrip(" ·:-")
+    return label, ticket
+
+
+def title_payload(title):
+    encoded = base64.b64encode(title.encode()).decode()
+    return (f"\x1b]1;{title}\x07\x1b]2;{title}\x07"
+            f"\x1b]1337;SetBadgeFormat={encoded}\x07"
+            f"\x1b]1337;SetUserVar=abramsTitle={encoded}\x07").encode()
+
+
 class StatusError(Exception):
     pass
 
@@ -148,21 +177,18 @@ def osc_payload(record):
     if record["state"] == "none":
         return b"\x1b]6;1;bg;*;default\x07\x1b]1;\x07\x1b]2;\x07\x1b]1337;SetBadgeFormat=\x07"
     title = record["title"]
-    rgb = COLORS[record["state"]][1]
+    rgb = (0, 150, 136) if record.get("variant") == "monitoring" else COLORS[record["state"]][1]
     text = "".join(
         f"\x1b]6;1;bg;{channel};brightness;{value}\x07"
         for channel, value in zip(("red", "green", "blue"), rgb))
-    badge = base64.b64encode(title.encode()).decode()
-    text += f"\x1b]1;{title}\x07\x1b]2;{title}\x07\x1b]1337;SetBadgeFormat={badge}\x07"
-    return text.encode()
+    return text.encode() + title_payload(title)
 
 
-def paint(owner, record):
+def write_terminal(owner, payload):
     fd = os.open("/dev/" + owner.tty, os.O_WRONLY | os.O_NOCTTY | os.O_NONBLOCK)
     try:
         if not stat.S_ISCHR(os.fstat(fd).st_mode) or not os.isatty(fd):
             raise StatusError("Paint target is not a terminal")
-        payload = osc_payload(record)
         # One write, no background loop, no terminal input injection.
         if os.write(fd, payload) != len(payload):
             raise StatusError("Incomplete terminal paint")
@@ -170,6 +196,10 @@ def paint(owner, record):
         os.close(fd)
 
 
+def paint(owner, record):
+    write_terminal(owner, osc_payload(record))
+
+
 class Store:
     def __init__(self, user_root=None, process_provider=processes, painter=paint):
         self.user_root = Path(user_root) if user_root else Path.home()
@@ -178,6 +208,22 @@ class Store:
                        for runtime in ("claude", "codex")}
         self.process_provider = process_provider
         self.painter = painter
+        self.ticket_evidence = {}
+        self.known_tickets = None
+
+    def ticket_info(self, owner, record=None):
+        record = record or {}
+        saved = {"id": record.get("ticket", ""), "at": record.get("ticket_at", 0),
+                 "source": record.get("ticket_source", "unbound")}
+        found = self.ticket_evidence.get(owner.tty, {})
+        if found.get("at", 0) > saved["at"]:
+            return found
+        # A migration record was already freshness/identity checked by read().
+        if not saved["at"] and not saved["id"] and "version" not in record:
+            _, legacy_ticket = label_ticket(record.get("label", ""))
+            if legacy_ticket and (self.known_tickets is None or legacy_ticket in self.known_tickets):
+                return {"id": legacy_ticket, "at": owner.epoch, "source": "fresh_legacy_label"}
+        return saved
 
     def path(self, owner):
         if not TTY.fullmatch(owner.tty):
@@ -222,7 +268,12 @@ class Store:
                 return None, "invalid"
             if r["owner"] != dataclasses.asdict(owner):
                 return None, "owner_changed"
-            expected = (COLORS[r["state"]][0] + " " + r["label"]).strip()
+            ticket = r.get("ticket", "")
+            if (ticket and not re.fullmatch(r"TK-\d+", ticket)) or not isinstance(r.get("ticket_at", 0), (int, float)):
+                return None, "invalid"
+            if r.get("variant", "") not in ("", "monitoring"):
+                return None, "invalid"
+            expected = status_title(r["state"], r["label"], ticket)
             if r["title"] != expected or (r["state"] == "none" and r["label"]):
                 return None, "invalid"
             return r, "canonical"
@@ -254,19 +305,32 @@ class Store:
         # A clear tombstone, corruption, or reused tty can never fall back.
         return r, reason
 
-    def set(self, owner, color, label="", *, issued_ns=None):
-        issued_ns = issued_ns if issued_ns is not None else time.monotonic_ns()
-        if color not in COLORS or not valid_label(label):
+    def set(self, owner, color, label="", *, issued_ns=None, ticket_update=None,
+            variant=""):
+        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")
+        label, embedded_ticket = label_ticket(label)
+        if embedded_ticket:
+            ticket_update = embedded_ticket
+        if ticket_update is not None:
+            if ticket_update and (not re.fullmatch(r"TK-\d+", ticket_update) or
+                    self.known_tickets is not None and ticket_update not in self.known_tickets):
+                raise StatusError("Ticket does not exist in the canonical ledger")
         label = "" if color == "none" else (label or COLORS[color][2])
         with self.lock(owner):
             previous, _ = self.load(owner)
             if previous and issued_ns <= previous["issued_ns"]:
                 raise StatusError("Obsolete status update; a newer event already won")
+            ticket = self.ticket_info(owner, previous)
+            if ticket_update is not None:
+                ticket = {"id": ticket_update, "at": time.time(), "source": "explicit"}
             record = {
                 "version": VERSION, "owner": dataclasses.asdict(owner),
                 "state": color, "label": label,
-                "title": (COLORS[color][0] + " " + label).strip(),
+                "title": status_title(color, label, ticket["id"]),
+                "ticket": ticket["id"], "ticket_at": ticket["at"],
+                "ticket_source": ticket["source"], "variant": variant if color == "green" else "",
                 "revision": previous["revision"] + 1 if previous else 1,
                 "event_id": uuid.uuid4().hex, "issued_ns": issued_ns,
                 "updated_at": dt.datetime.now(dt.timezone.utc).isoformat(),
@@ -295,7 +359,7 @@ class Store:
 
     def repaint(self, owner):
         with self.lock(owner):
-            record, reason = self.read(owner)
+            record, reason = self.load(owner)
             if record is None:
                 raise StatusError("Cannot repaint unknown status: " + reason)
             self.assert_owner(owner)
@@ -305,8 +369,10 @@ class Store:
 
     def row(self, owner, title=""):
         r, reason = self.read(owner, title)
+        ticket = self.ticket_info(owner, r)
         result = {"tty": owner.tty, "pid": str(owner.pid),
                   "runtime": owner.runtime, "live": True,
+                  "ticket": ticket["id"], "ticket_source": ticket["source"],
                   "status_source": reason, "owner": dataclasses.asdict(owner)}
         if r is None:
             labels = {
@@ -314,9 +380,10 @@ class Store:
                 "invalid": "Invalid status record — refresh dot",
                 "owner_changed": "New session — status not set",
             }
-            result.update(color="none", label="⚪ " + labels.get(reason, "Status not set"))
+            result.update(color="none", label="⚪ " + (ticket["id"] or "TK REQUIRED") + " · " + labels.get(reason, "Status not set"))
             return result
-        result.update(color=r["state"], label=r["title"] or "⚪ Status cleared",
+        label, _ = label_ticket(r["label"])
+        result.update(color=r["state"], label=status_title(r["state"], label, ticket["id"] or "TK REQUIRED") or "⚪ " + (ticket["id"] or "TK REQUIRED") + " · Status cleared",
                       updated_at=r.get("updated_at"), revision=r.get("revision"))
         warnings = []
         live_color = color_of(title)
@@ -334,6 +401,15 @@ class Store:
         result["warnings"] = warnings
         return result
 
+    def header(self, owner, writer=write_terminal):
+        with self.lock(owner):
+            row = self.row(owner)
+            minutes = max(0, int((time.time() - owner.epoch) / 60))
+            title = row["label"] + f" · up {minutes // 60}h {minutes % 60}m"
+            self.assert_owner(owner)
+            writer(owner, title_payload(title))
+            return title
+
 
 def iterm_sessions():
     script = """
@@ -384,12 +460,15 @@ def scan(store, rows=None, sessions=None):
 def main(argv=None):
     parser = argparse.ArgumentParser(description=__doc__)
     sub = parser.add_subparsers(dest="command", required=True)
-    for name in ("set", "clear", "repaint", "current", "start"):
+    for name in ("set", "clear", "repaint", "current", "start", "ticket", "status", "headers"):
         cmd = sub.add_parser(name)
         if name == "set":
             cmd.add_argument("color", choices=[c for c in COLORS if c != "none"])
             cmd.add_argument("label", nargs="?", default="")
             cmd.add_argument("--all", choices=["claude", "codex"])
+            cmd.add_argument("--monitoring", action="store_true")
+        if name == "ticket":
+            cmd.add_argument("ticket")
         cmd.add_argument("--quiet", action="store_true")
     legacy = sub.add_parser("paint-legacy")
     legacy.add_argument("tty")
@@ -405,6 +484,8 @@ def main(argv=None):
     args = parser.parse_args(argv)
     store = Store()
     rows = processes()
+    store.ticket_evidence, store.known_tickets = tickets.discover(
+        store.user_root, rows, owners(rows), ancestors)
     if args.command in ("scan", "audit"):
         data = scan(store, rows)
         if args.tsv:
@@ -416,11 +497,42 @@ def main(argv=None):
             for r in data:
                 notes = ", ".join(r.get("warnings", []))
                 print(f'{r["tty"]}  {r["label"]}  [{r["status_source"]}] {notes}')
+        if args.command == "audit" and any(
+                r.get("warnings") or r["status_source"] in
+                ("invalid", "owner_changed", "legacy_conflict") for r in data):
+            return 1
+        return 0
+    if args.command == "headers":
+        failures = []
+        for owner in owners(rows).values():
+            try:
+                title = store.header(owner)
+                if not args.quiet:
+                    print(owner.tty + " " + title)
+            except (StatusError, OSError) as exc:
+                failures.append(owner.tty + ": " + str(exc))
+        if failures:
+            raise StatusError("; ".join(failures))
         return 0
     caller = current_owner(rows)
     if args.command == "current":
         print(json.dumps(dataclasses.asdict(caller)))
         return 0
+    if args.command == "status":
+        print(json.dumps(store.row(caller), ensure_ascii=False))
+        return 0
+    if args.command == "ticket":
+        ticket = args.ticket.upper()
+        ticket = "" if ticket in ("CLEAR", "NONE") else ("TK-" + ticket if ticket.isdigit() else tickets.short(ticket))
+        if not ticket and args.ticket.upper() not in ("CLEAR", "NONE"):
+            raise StatusError("Expected a canonical TK number or clear")
+        previous, _ = store.read(caller)
+        record = store.set(caller, previous["state"] if previous else "none",
+                           label_ticket(previous["label"])[0] if previous else "",
+                           ticket_update=ticket, variant=previous.get("variant", "") if previous else "")
+        store.header(caller)
+        print(f'/terminal-ticket → {caller.tty} {record["ticket"] or "TK REQUIRED"}')
+        return 0
     targets = [caller]
     if args.command == "paint-legacy":
         tty = args.tty.removeprefix("/dev/")
@@ -441,17 +553,27 @@ def main(argv=None):
         color, label = "none", ""
     for owner in targets:
         if args.command == "start":
-            record, reason = store.read(owner)
+            record, reason = store.load(owner)
             if record is not None:
                 record = store.repaint(owner)
-            elif reason in ("missing", "legacy_stale", "legacy_empty", "owner_changed"):
+            elif reason == "missing":
+                sessions, api = iterm_sessions()
+                legacy, legacy_reason = store.legacy_status(owner, sessions.get(owner.tty, ""))
+                if legacy is not None and api == "available":
+                    record = store.set(owner, legacy["state"], legacy["label"])
+                elif legacy_reason in ("missing", "legacy_stale", "legacy_empty"):
+                    record = store.set(owner, "green")
+                else:
+                    raise StatusError("Ambiguous legacy state; set an explicit color")
+            elif reason == "owner_changed":
                 record = store.set(owner, "green")
             else:
                 raise StatusError("Start found ambiguous status; set an explicit color")
         elif args.command == "repaint":
             record = store.repaint(owner)
         else:
-            record = store.set(owner, color, label)
+            record = store.set(owner, color, label,
+                               variant="monitoring" if getattr(args, "monitoring", False) else "")
         if not getattr(args, "quiet", False):
             print(f'/terminal-status → /dev/{owner.tty} {record["title"] or "CLEARED"}')
     return 0
diff --git a/test_terminal_status.py b/test_terminal_status.py
index fe31f4d..908a42a 100644
--- a/test_terminal_status.py
+++ b/test_terminal_status.py
@@ -1,7 +1,11 @@
 import dataclasses
+import datetime as dt
+import json
 import os
 from pathlib import Path
 import tempfile
+import subprocess
+import sys
 import unittest
 from unittest.mock import patch
 
@@ -29,6 +33,71 @@ class StatusTests(unittest.TestCase):
         os.utime(path, (when, when))
         return path
 
+    def test_ticket_survives_every_generic_color_and_clear(self):
+        self.store.set(self.owner, "green", "TK-11317 · initial")
+        for color in ts.COLORS:
+            record = self.store.set(self.owner, color)
+            self.assertEqual(record["ticket"], "TK-11317")
+            self.assertEqual(record["title"].count("TK-11317"), 0 if color == "none" else 1)
+
+    def test_event_clock_is_comparable_between_python_processes(self):
+        before = ts.issued_clock()
+        child = int(subprocess.check_output([sys.executable, '-c',
+                     'import terminal_status; print(terminal_status.issued_clock())'], text=True))
+        after = ts.issued_clock()
+        self.assertLessEqual(before, child)
+        self.assertLessEqual(child, after)
+
+    def test_ticket_header_is_read_only_and_does_not_resurrect_clear(self):
+        self.store.set(self.owner, "none", ticket_update="TK-11317")
+        before = self.store.path(self.owner).read_bytes()
+        outputs = []
+        title = self.store.header(self.owner, lambda owner, data: outputs.append(data))
+        self.assertIn("⚪ TK-11317 · Status cleared", title)
+        self.assertEqual(before, self.store.path(self.owner).read_bytes())
+        self.assertNotIn(b']6;', outputs[0])
+
+    def test_new_assignment_wins_and_generic_hook_preserves_binding(self):
+        self.store.ticket_evidence = {self.owner.tty: {"id": "TK-1", "at": 10, "source": "ticket_ledger"}}
+        self.store.set(self.owner, "green")
+        self.store.ticket_evidence[self.owner.tty].update(id="TK-2", at=20)
+        self.assertIn("TK-2", self.store.row(self.owner)["label"])
+        r = self.store.set(self.owner, "purple")
+        self.assertEqual(r["ticket"], "TK-2")
+        self.store.ticket_evidence[self.owner.tty].update(id="TK-1", at=10)
+        self.assertEqual(self.store.set(self.owner, "green")["ticket"], "TK-2")
+
+    def test_ticket_validation_and_tty_reuse(self):
+        self.store.known_tickets = {"TK-11317"}
+        with self.assertRaises(ts.StatusError):
+            self.store.set(self.owner, "green", ticket_update="TK-99999999")
+        self.store.set(self.owner, "green", ticket_update="TK-11317")
+        self.process.started = "Wed Sep 9 09:35:08 2026"
+        self.assertEqual(self.store.row(self.process.owner())["ticket"], "")
+
+    def test_monitoring_shade_remains_green_and_survives_repaint(self):
+        record = self.store.set(self.owner, "green", "Next 10:30", variant="monitoring")
+        self.assertIn(b"green;brightness;150", ts.osc_payload(record))
+        self.store.repaint(self.owner)
+        self.assertEqual(self.paints[-1]["variant"], "monitoring")
+        self.assertEqual(self.store.row(self.owner)["color"], "green")
+
+    def test_ledger_discovery_rejects_reused_pid_and_nested_agent(self):
+        self.rows[124] = ts.Process(124, 123, "??", self.process.started, "/bin/node")
+        self.rows[125] = ts.Process(125, 123, "??", self.process.started, "/bin/codex")
+        self.rows[126] = ts.Process(126, 125, "??", self.process.started, "/bin/node")
+        path = self.root / ".claude/tickets/events.jsonl"
+        path.parent.mkdir(parents=True)
+        def event(pid, seconds, ticket):
+            return {"id": ticket, "type": "create", "correlation_id": f"create-abc-{pid}-xyz",
+                    "ts": dt.datetime.fromtimestamp(self.owner.epoch + seconds, dt.timezone.utc).isoformat()}
+        events = [event(124, -1, "TK-1-old"), event(124, 10, "TK-2-current"),
+                  event(126, 20, "TK-3-child"), event(999, 30, "TK-4-unrelated")]
+        path.write_text("\n".join(json.dumps(e) for e in events))
+        found, known = ts.tickets.discover(self.root, self.rows, ts.owners(self.rows), ts.ancestors, argv={})
+        self.assertEqual(found["ttys010"]["id"], "TK-2")
+        self.assertEqual(len(known), 4)
+
     def test_all_colors_write_canonical_and_identical_mirrors(self):
         for color in ts.COLORS:
             r = self.store.set(self.owner, color, "" if color == "none" else "Task")
@@ -111,6 +180,25 @@ class StatusTests(unittest.TestCase):
         self.assertEqual(row["status_source"], "legacy_conflict")
         self.assertFalse(self.store.path(self.owner).exists())
 
+    def test_repaint_never_adopts_unbound_legacy_state(self):
+        self.legacy("codex", "🟢 fresh but unbound")
+        with self.assertRaises(ts.StatusError):
+            self.store.repaint(self.owner)
+        self.assertFalse(self.paints)
+
+    def test_concurrent_writers_keep_the_newest_event_and_valid_json(self):
+        import concurrent.futures
+        def update(sequence):
+            try:
+                self.store.set(self.owner, "green", str(sequence), issued_ns=sequence)
+            except ts.StatusError as exc:
+                self.assertIn("Obsolete status update", str(exc))
+        with concurrent.futures.ThreadPoolExecutor(max_workers=6) as pool:
+            list(pool.map(update, [6, 2, 5, 1, 3, 4]))
+        record, reason = self.store.load(self.owner)
+        self.assertEqual(reason, "canonical")
+        self.assertEqual(record["label"], "6")
+
     def test_scan_survives_terminal_api_outage_and_drops_dead_sessions(self):
         self.store.set(self.owner, "purple", "Approval needed")
         result = ts.scan(self.store, sessions={})
diff --git a/ticket_binding.py b/ticket_binding.py
new file mode 100644
index 0000000..e8fa5c2
--- /dev/null
+++ b/ticket_binding.py
@@ -0,0 +1,79 @@
+"""Read the existing ticket ledger; never create a second ticket database."""
+import datetime as dt
+import json
+import re
+import subprocess
+
+SHORT = re.compile(r"^TK-\d+(?=$|-)", re.I)
+CID = re.compile(r"^(?:assign|create|action)-[a-z0-9]+-(\d+)-[a-z0-9]+$")
+
+
+def short(value):
+    match = SHORT.match(str(value))
+    return match.group().upper() if match else ""
+
+
+def timestamp(value):
+    try:
+        return dt.datetime.fromisoformat(value.replace("Z", "+00:00")).timestamp()
+    except (ValueError, AttributeError):
+        return 0
+
+
+def discover(root, rows, live, chain, argv=None):
+    """Attribute events only to a currently live main agent's own MCP process."""
+    known, claims, actions = set(), {}, {}
+    descendants = {}
+    for pid, p in rows.items():
+        nearest = next((a for a in chain(pid, rows) if a.runtime), None)
+        if nearest and live.get(nearest.tty) == nearest.owner():
+            descendants[pid] = (nearest.owner(), timestamp_start(p.started))
+    try:
+        stream = (root / ".claude/tickets/events.jsonl").open()
+    except OSError:
+        return {}, known
+    with stream:
+        for line in stream:
+            try:
+                event = json.loads(line)
+            except ValueError:
+                continue
+            ticket = short(event.get("id", ""))
+            if not ticket:
+                continue
+            if event.get("type") == "create":
+                known.add(ticket)
+            match = CID.fullmatch(event.get("correlation_id", ""))
+            if not match or int(match[1]) not in descendants:
+                continue
+            owner, born = descendants[int(match[1])]
+            at = timestamp(event.get("ts"))
+            if at < max(owner.epoch, born):
+                continue
+            target = claims if event.get("type") in ("assign", "create") else actions
+            if at > target.get(owner.tty, {}).get("at", 0):
+                target[owner.tty] = {"id": ticket, "at": at,
+                                     "source": "ticket_ledger", "correlation_id": match[0]}
+    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)
+        argv = {}
+        for line in output.stdout.splitlines():
+            parts = line.strip().split(None, 1)
+            if len(parts) == 2:
+                argv[int(parts[0])] = parts[1]
+    for tty, owner in live.items():
+        if result[tty]:
+            continue
+        ids = set(re.findall(r"\bTK-\d+\b", (argv or {}).get(owner.pid, ""), re.I))
+        if len(ids) == 1 and next(iter(ids)).upper() in known:
+            result[tty] = {"id": next(iter(ids)).upper(), "at": owner.epoch,
+                           "source": "main_process_argument"}
+    return {tty: value for tty, value in result.items() if value}, known
+
+
+def timestamp_start(value):
+    return dt.datetime.strptime(value, "%a %b %d %H:%M:%S %Y").timestamp()
diff --git a/verification/install/after/.agents/skills/color/scripts/color.sh b/verification/install/after/.agents/skills/color/scripts/color.sh
new file mode 100644
index 0000000..dfe4040
--- /dev/null
+++ b/verification/install/after/.agents/skills/color/scripts/color.sh
@@ -0,0 +1,170 @@
+#!/usr/bin/env bash
+# /color — give this Claude Code session a fresh, visually-distinct color and
+# apply it to the terminal tab so parallel Claude sessions are tellable apart.
+#
+# Mechanism: Claude Code's Bash tool runs detached with NO controlling tty
+# (verified: `tty` => "not a tty", /dev/tty => "device not configured"), so
+# OSC escapes on stdout/stderr never reach the terminal. The working path is
+# to locate the `claude` process's own pty by walking process ancestry and
+# write the OSC sequence straight to /dev/ttysNNN. OSC color/title codes are
+# non-destructive (no cursor moves, no cell writes) so this is glitch-safe.
+#
+# Colors: golden-angle (137.508 deg) hue rotation => every session maximally
+# far from the last on the hue wheel, never repeating. iTerm2 gets a tab tint;
+# every terminal (incl. macOS Terminal.app) gets an emoji + name tab title.
+set -u
+
+STATE_DIR="${HOME}/.claude"
+STATE="${STATE_DIR}/color-session-index"
+mkdir -p "$STATE_DIR"
+
+idx=0
+[[ -f "$STATE" ]] && idx="$(cat "$STATE" 2>/dev/null)"
+[[ "$idx" =~ ^[0-9]+$ ]] || idx=0
+session=$(( idx + 1 ))
+printf '%s\n' "$session" > "$STATE"
+
+# golden-angle hue -> HSL(90% sat, 58% light) -> RGB, pure awk (no python/flock dep)
+read -r R G B HUE < <(awk -v idx="$idx" '
+  function absf(v){ return v<0 ? -v : v }
+  BEGIN{
+    hue = idx*137.508; hue = hue - int(hue/360)*360
+    s=0.90; l=0.58
+    c=(1-absf(2*l-1))*s
+    hp=hue/60.0
+    m2=hp - int(hp/2)*2
+    x=c*(1-absf(m2-1))
+    if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+    else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+    else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+    m=l-c/2
+    printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+  }')
+
+# hue -> coarse name + nearest emoji square
+if   (( HUE < 20  )); then NAME=Red;     EMOJI=🟥
+elif (( HUE < 45  )); then NAME=Orange;  EMOJI=🟧
+elif (( HUE < 70  )); then NAME=Amber;   EMOJI=🟨
+elif (( HUE < 150 )); then NAME=Green;   EMOJI=🟩
+elif (( HUE < 215 )); then NAME=Teal;    EMOJI=🟦
+elif (( HUE < 255 )); then NAME=Blue;    EMOJI=🟦
+elif (( HUE < 290 )); then NAME=Indigo;  EMOJI=🟪
+elif (( HUE < 330 )); then NAME=Violet;  EMOJI=🟪
+else                       NAME=Magenta; EMOJI=🟥
+fi
+
+# locate the claude process's controlling pty by walking the ancestry
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+
+# Recall the session TOPIC (the user's first prompt) so re-coloring mid-session
+# does NOT clobber the topic with the color name — the color is the tint, the
+# text stays the topic. color-title.sh already wrote the topic onto the tmux
+# window name (with automatic-rename off), so read it straight back from there.
+# Ignore our own color-name placeholder ("<emoji> Indigo #359").
+topic=""
+if [[ -n "${TMUX_PANE:-}" ]] && command -v tmux >/dev/null 2>&1; then
+  cur="$(tmux display-message -p -t "$TMUX_PANE" '#{window_name}' 2>/dev/null)"
+  if [[ -n "$cur" && ! "$cur" =~ (Red|Orange|Amber|Green|Teal|Blue|Indigo|Violet|Magenta)\ \#[0-9]+$ ]]; then
+    topic="$cur"
+  fi
+fi
+
+# Tab/window TEXT: prefer the topic; until the first prompt sets one, show just
+# the emoji so the tab is visibly initialised without shouting the color name.
+if [[ -n "$topic" ]]; then label="$topic"; else label="${EMOJI}"; fi
+hex=$(printf '#%02X%02X%02X' "$R" "$G" "$B")
+
+# Detect tmux. Inside tmux, OSC escapes are swallowed and never reach
+# iTerm2's tab bar — so the user sees no color "at top". Surface the color
+# on tmux's own window/pane instead.
+inside_tmux=0
+[[ -n "${TMUX:-}" ]] && inside_tmux=1
+if (( ! inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  p2=$$
+  for _ in 1 2 3 4 5 6 7 8 9 10; do
+    line2="$(ps -o ppid=,comm= -p "$p2" 2>/dev/null)" || break
+    set -- $line2
+    pp2="${1:-}"; cm2="${2:-}"
+    [[ -z "$pp2" ]] && break
+    case "$cm2" in *tmux*) inside_tmux=1; break;; esac
+    p2="$pp2"
+  done
+fi
+
+# tmux swallows the DCS-wrapped iTerm2 tab-color OSC unless passthrough is on.
+# Default is OFF, so ensure it on every run — this is what makes the tab header
+# actually tint (otherwise the escape is dropped and the tab shows no color).
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  tmux set -g allow-passthrough on 2>/dev/null || true
+fi
+
+applied=0
+
+if [[ -n "$tty_path" && -w "$tty_path" ]]; then
+  if (( inside_tmux )); then
+    # Inside tmux, raw OSC is consumed by tmux. With allow-passthrough on,
+    # DCS-wrap (ESC P tmux ; <inner-with-doubled-ESC> ESC \) lets the
+    # iTerm2 tab-tint + title OSC reach the outer terminal.
+    {
+      printf '\033Ptmux;\033\033]6;1;bg;red;brightness;%d\007\033\\'   "$R"
+      printf '\033Ptmux;\033\033]6;1;bg;green;brightness;%d\007\033\\' "$G"
+      printf '\033Ptmux;\033\033]6;1;bg;blue;brightness;%d\007\033\\'  "$B"
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$label"
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$label"
+    } > "$tty_path" 2>/dev/null
+  else
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$label"
+      printf '\033]2;%s\007' "$label"
+    } > "$tty_path" 2>/dev/null
+  fi
+  applied=1
+fi
+
+# tmux indicator path — recolors the tmux window name + current pane border
+# so the color surfaces in tmux's status bar regardless of outer terminal.
+# Window TEXT = topic if captured, else the color label until first prompt.
+if [[ -n "$topic" ]]; then winname="$topic"; else winname="${EMOJI} ${NAME} #${session}"; fi
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  # scope to THIS pane via $TMUX_PANE — without it, plain `rename-window`
+  # targets the active window of the session, which is almost never the
+  # window the new claude session is actually running in.
+  tgt="${TMUX_PANE:-}"
+  if [[ -n "$tgt" ]]; then
+    # keep the topic sticky against tmux automatic-rename
+    [[ -n "$topic" ]] && tmux set-window-option -q -t "$tgt" automatic-rename off 2>/dev/null || true
+    tmux rename-window -t "$tgt" "$winname" 2>/dev/null || true
+    tmux set-window-option -q -t "$tgt" window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-border-style "fg=${hex}" 2>/dev/null || true
+  else
+    [[ -n "$topic" ]] && tmux set-window-option -q automatic-rename off 2>/dev/null || true
+    tmux rename-window "$winname" 2>/dev/null || true
+    tmux set-window-option -q window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q pane-border-style "fg=${hex}" 2>/dev/null || true
+  fi
+  applied=1
+fi
+
+if (( applied )); then
+  suffix=""
+  (( inside_tmux )) && suffix="  · tmux window+pane recolored"
+  echo "/color → ${NAME} ${EMOJI}  rgb(${R},${G},${B})  ${hex}  · session #${session}${suffix}"
+else
+  echo "/color → ${NAME} ${EMOJI} (session #${session}) — terminal pty not found, color not applied"
+fi
+
+# Shared terminal status follows the cosmetic session color.
+python3 "$HOME/Projects/terminal-status/terminal_status.py" start --quiet || true
diff --git a/verification/install/after/.agents/skills/color/scripts/repaint-dot.sh b/verification/install/after/.agents/skills/color/scripts/repaint-dot.sh
new file mode 100644
index 0000000..e9b2cfe
--- /dev/null
+++ b/verification/install/after/.agents/skills/color/scripts/repaint-dot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Rendering only: cannot overwrite a gate, refresh its age, or undo clear.
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" repaint --quiet
diff --git a/verification/install/after/.agents/skills/color/scripts/semantic-dot.sh b/verification/install/after/.agents/skills/color/scripts/semantic-dot.sh
new file mode 100644
index 0000000..0229210
--- /dev/null
+++ b/verification/install/after/.agents/skills/color/scripts/semantic-dot.sh
@@ -0,0 +1,7 @@
+#!/usr/bin/env bash
+# Existing Codex signature: state R G B emoji default-label [label|flag].
+set -eu
+color="${1:?state required}"
+default_label="${6:?default label required}"
+shift 6
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" codex "$color" "${1:-$default_label}"
diff --git a/verification/install/after/.claude/hooks/dot-floor.sh b/verification/install/after/.claude/hooks/dot-floor.sh
new file mode 100644
index 0000000..1de2cbf
--- /dev/null
+++ b/verification/install/after/.claude/hooks/dot-floor.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+# Stop hooks preserve deliberate status. Unknown is never guessed green.
+cat >/dev/null 2>&1 || true
+python3 "$HOME/Projects/terminal-status/terminal_status.py" repaint --quiet || true
diff --git a/verification/install/after/.claude/skills/allcolordots/allcolordots.sh b/verification/install/after/.claude/skills/allcolordots/allcolordots.sh
new file mode 100644
index 0000000..53698a3
--- /dev/null
+++ b/verification/install/after/.claude/skills/allcolordots/allcolordots.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+# /allcolordots — scan EVERY active terminal's color dot + label in one shot,
+# and (new) present a clickable single-window ROSTER per color that JUMPS focus
+# to any tab — the "gather my yellows in one place" navigator (path A).
+#
+# Status comes from the shared session-bound terminal-status engine.
+# Labels and dot files are presentation; exact main agent ownership is required.
+#
+# Usage:
+#   allcolordots.sh                 # grouped human table (orange > purple > yellow > green > pink)
+#   allcolordots.sh --json          # JSON array: [{tty,color,label,live,pid}]  (navbar app)
+#   allcolordots.sh --live          # table, but only ACTIVE (live) terminals
+#   allcolordots.sh --yellow        # numbered ROSTER of just the 🟡 needs-direction tabs
+#   allcolordots.sh --only <color>  # numbered roster of one color (yellow|orange|purple|green|pink)
+#   allcolordots.sh jump <ttyNNN|N> # focus that tab (N = row# from the last roster)
+set -u
+set -o pipefail
+# Emoji glob-matching (case *🟡*) needs a UTF-8 ctype; some launch contexts pass
+# LC_CTYPE= empty which silently breaks the match -> every dot reads "none".
+export LANG="${LANG:-en_US.UTF-8}" LC_ALL="en_US.UTF-8"
+DOTS="$HOME/.claude/tab-dots"
+ROSTER_CACHE="$DOTS/.last-roster"
+
+color_of(){ case "$1" in *🟢*) echo green;; *🟡*) echo yellow;; *🟣*) echo purple;; *🟠*) echo orange;; *🩷*) echo pink;; *) echo none;; esac; }
+emoji_of(){ case "$1" in orange) echo 🟠;; purple) echo 🟣;; yellow) echo 🟡;; green) echo 🟢;; pink) echo 🩷;; *) echo ⚪;; esac; }
+
+# ---- JUMP: focus the tab whose tty matches (select + activate; NOT a reparent) ----
+jump_to(){
+  local arg="$1" tty=""
+  # index into the last roster?
+  if printf '%s' "$arg" | grep -qE '^[0-9]+$' && [ -f "$ROSTER_CACHE" ]; then
+    tty="$(awk -F'\t' -v n="$arg" 'NR==n{print $1}' "$ROSTER_CACHE")"
+  fi
+  [ -z "$tty" ] && tty="$arg"
+  case "$tty" in /dev/*) :;; ttys*) tty="/dev/$tty";; *) tty="/dev/$tty";; esac
+  osascript 2>&1 <<OSA
+tell application "iTerm2"
+  repeat with w in windows
+    repeat with t in tabs of w
+      repeat with s in sessions of t
+        if (tty of s) is "$tty" then
+          select w
+          select t
+          select s
+          activate
+          return "jumped -> $tty"
+        end if
+      end repeat
+    end repeat
+  end repeat
+  return "not found: $tty"
+end tell
+OSA
+}
+
+# ---- Shared state scanner: presentation never overrides canonical state ----
+collect_rows(){
+  python3 "$HOME/Projects/terminal-status/terminal_status.py" scan --tsv
+}
+
+# ---- roster of ONE color: numbered, jump-ready, caches order for `jump N` ----
+print_roster(){
+  local want="$1" rows; rows="$(collect_rows | awk -F'\t' -v c="$want" '$2==c')" || return $?
+  : > "$ROSTER_CACHE"
+  local e; e="$(emoji_of "$want")"
+  local n; n="$(printf '%s\n' "$rows" | grep -c .)"
+  echo "=== $e $want roster ($n) — jump with:  allcolordots.sh jump <#|tty> ==="
+  [ "$n" = 0 ] && { echo "  (none)"; return 0; }
+  local i=0
+  printf '%s\n' "$rows" | while IFS=$'\t' read -r tty color live pid label; do
+    i=$((i+1))
+    printf '%s\t%s\t%s\n' "$tty" "$label" "$live" >> "$ROSTER_CACHE"
+    local flag=""; [ "$live" = true ] && flag="  ·live"
+    printf '  %2d) %-9s %s%s\n' "$i" "$tty" "$label" "$flag"
+  done
+}
+
+# ---------------- dispatch ----------------
+case "${1:-}" in
+  jump)   shift; jump_to "${1:-}"; exit $? ;;
+  --yellow) print_roster yellow; exit $? ;;
+  --only)   print_roster "${2:-yellow}"; exit $? ;;
+  --json)
+    collect_rows | python3 -c '
+import sys,json
+out=[]
+for line in sys.stdin:
+    line=line.rstrip("\n")
+    if not line: continue
+    tty,color,live,pid,label=(line.split("\t")+["","","","",""])[:5]
+    out.append({"tty":tty,"color":color,"live":live=="true","pid":pid,"label":label})
+pri={"orange":0,"purple":1,"yellow":2,"green":3,"pink":4,"none":5}
+out.sort(key=lambda r:(pri.get(r["color"],9), r["tty"]))
+print(json.dumps(out))
+'
+    exit $? ;;
+esac
+
+# default / --live : grouped human table
+LIVEONLY=0; [ "${1:-}" = "--live" ] && LIVEONLY=1
+rows="$(collect_rows)" || exit $?
+[ "$LIVEONLY" = 1 ] && rows="$(printf '%s\n' "$rows" | awk -F'\t' '$3=="true"')"
+total="$(printf '%s\n' "$rows" | grep -c . )"
+echo "=== active-terminal dots ($total) ==="
+for c in orange purple yellow green pink none; do
+  block="$(printf '%s\n' "$rows" | awk -F'\t' -v c="$c" '$2==c')"
+  [ -z "$block" ] && continue
+  n="$(printf '%s\n' "$block" | grep -c .)"
+  echo "$(emoji_of "$c") ${c} ($n)"
+  printf '%s\n' "$block" | while IFS=$'\t' read -r tty color live pid label; do
+    flag=""; [ "$live" = true ] && flag="  ·live"
+    printf '   %-9s %s%s\n' "$tty" "$label" "$flag"
+  done
+done
diff --git a/verification/install/after/.claude/skills/color/scripts/color.sh b/verification/install/after/.claude/skills/color/scripts/color.sh
new file mode 100644
index 0000000..9412388
--- /dev/null
+++ b/verification/install/after/.claude/skills/color/scripts/color.sh
@@ -0,0 +1,249 @@
+#!/usr/bin/env bash
+# /color — give this Claude Code session a fresh, visually-distinct color and
+# apply it to the terminal tab so parallel Claude sessions are tellable apart.
+#
+# Mechanism: Claude Code's Bash tool runs detached with NO controlling tty
+# (verified: `tty` => "not a tty", /dev/tty => "device not configured"), so
+# OSC escapes on stdout/stderr never reach the terminal. The working path is
+# to locate the `claude` process's own pty by walking process ancestry and
+# write the OSC sequence straight to /dev/ttysNNN. OSC color/title codes are
+# non-destructive (no cursor moves, no cell writes) so this is glitch-safe.
+#
+# Colors: golden-angle (137.508 deg) hue rotation => every session maximally
+# far from the last on the hue wheel, never repeating. iTerm2 gets a tab tint;
+# every terminal (incl. macOS Terminal.app) gets an emoji + name tab title.
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/color → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Streamline / lite mode — skip the AUTOMATIC SessionStart color assignment
+# fleet-wide (the hook calls with no args). Explicit invocations like
+# `/color --all` pass an arg and still run. rm the file to restore auto mode.
+[ -f "$HOME/.claude/hooks-lite" ] && [ $# -eq 0 ] && exit 0
+
+STATE_DIR="${HOME}/.claude"
+STATE="${STATE_DIR}/color-session-index"
+mkdir -p "$STATE_DIR"
+
+idx=0
+[[ -f "$STATE" ]] && idx="$(cat "$STATE" 2>/dev/null)"
+[[ "$idx" =~ ^[0-9]+$ ]] || idx=0
+session=$(( idx + 1 ))
+printf '%s\n' "$session" > "$STATE"
+
+rgb_for_idx() { # idx -> "R G B HUE" via golden-angle HSL(90%,58%)
+  awk -v idx="$1" '
+    function absf(v){ return v<0 ? -v : v }
+    BEGIN{
+      hue = idx*137.508; hue = hue - int(hue/360)*360
+      s=0.90; l=0.58
+      c=(1-absf(2*l-1))*s
+      hp=hue/60.0
+      m2=hp - int(hp/2)*2
+      x=c*(1-absf(m2-1))
+      if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+      else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+      else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+      m=l-c/2
+      printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+    }'
+}
+
+name_for_hue() { # hue -> "Name Emoji"
+  local h="$1"
+  if   (( h < 20  )); then echo "Red 🟥"
+  elif (( h < 45  )); then echo "Orange 🟧"
+  elif (( h < 70  )); then echo "Amber 🟨"
+  elif (( h < 150 )); then echo "Green 🟩"
+  elif (( h < 215 )); then echo "Teal 🟦"
+  elif (( h < 255 )); then echo "Blue 🟦"
+  elif (( h < 290 )); then echo "Indigo 🟪"
+  elif (( h < 330 )); then echo "Violet 🟪"
+  else                     echo "Magenta 🟥"
+  fi
+}
+
+# --all: paint EVERY open tab running a live `claude` session, each with its
+# own distinct golden-angle color. Claude processes report comm=node, so match
+# on the full command line and take each unique controlling tty.
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' \
+    | sort -u)"
+  if [[ -z "$ttys" ]]; then
+    echo "/color --all → no live claude ttys found"
+    exit 0
+  fi
+  n=0
+  for tt in $ttys; do
+    tp="/dev/$tt"
+    [[ -w "$tp" ]] || { echo "  $tt → not writable, skipped"; continue; }
+    aidx=$(( session + n ))
+    read -r R G B HUE < <(rgb_for_idx "$aidx")
+    read -r NAME EMOJI < <(name_for_hue "$HUE")
+    # NOTE: do NOT send a leading '6;1;bg;*;default' reset — on iTerm2 it swallows
+    # the color set, leaving the tab default (bug found 2026-09-02, same as pinkdot).
+    # Set RGB directly and write BOTH title channels (]1 icon/tab, ]2 window).
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$EMOJI"
+      printf '\033]2;%s\007' "$EMOJI"
+    } > "$tp" 2>/dev/null && echo "  $tt → ${NAME} ${EMOJI}  rgb(${R},${G},${B})"
+    n=$(( n + 1 ))
+  done
+  printf '%s\n' "$(( session + n ))" > "$STATE"
+  echo "/color --all → painted ${n} tab(s), counter now $(( session + n ))"
+  exit 0
+fi
+
+# golden-angle hue -> HSL(90% sat, 58% light) -> RGB, pure awk (no python/flock dep)
+read -r R G B HUE < <(awk -v idx="$idx" '
+  function absf(v){ return v<0 ? -v : v }
+  BEGIN{
+    hue = idx*137.508; hue = hue - int(hue/360)*360
+    s=0.90; l=0.58
+    c=(1-absf(2*l-1))*s
+    hp=hue/60.0
+    m2=hp - int(hp/2)*2
+    x=c*(1-absf(m2-1))
+    if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+    else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+    else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+    m=l-c/2
+    printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+  }')
+
+# hue -> coarse name + nearest emoji square
+if   (( HUE < 20  )); then NAME=Red;     EMOJI=🟥
+elif (( HUE < 45  )); then NAME=Orange;  EMOJI=🟧
+elif (( HUE < 70  )); then NAME=Amber;   EMOJI=🟨
+elif (( HUE < 150 )); then NAME=Green;   EMOJI=🟩
+elif (( HUE < 215 )); then NAME=Teal;    EMOJI=🟦
+elif (( HUE < 255 )); then NAME=Blue;    EMOJI=🟦
+elif (( HUE < 290 )); then NAME=Indigo;  EMOJI=🟪
+elif (( HUE < 330 )); then NAME=Violet;  EMOJI=🟪
+else                       NAME=Magenta; EMOJI=🟥
+fi
+
+# locate the claude process's controlling pty by walking the ancestry
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+
+# Recall the session TOPIC (the user's first prompt) so re-coloring mid-session
+# does NOT clobber the topic with the color name — the color is the tint, the
+# text stays the topic. color-title.sh already wrote the topic onto the tmux
+# window name (with automatic-rename off), so read it straight back from there.
+# Ignore our own color-name placeholder ("<emoji> Indigo #359").
+topic=""
+if [[ -n "${TMUX_PANE:-}" ]] && command -v tmux >/dev/null 2>&1; then
+  cur="$(tmux display-message -p -t "$TMUX_PANE" '#{window_name}' 2>/dev/null)"
+  if [[ -n "$cur" && ! "$cur" =~ (Red|Orange|Amber|Green|Teal|Blue|Indigo|Violet|Magenta)\ \#[0-9]+$ ]]; then
+    topic="$cur"
+  fi
+fi
+
+# Tab/window TEXT: prefer the topic; until the first prompt sets one, show just
+# the emoji so the tab is visibly initialised without shouting the color name.
+if [[ -n "$topic" ]]; then label="$topic"; else label="${EMOJI}"; fi
+hex=$(printf '#%02X%02X%02X' "$R" "$G" "$B")
+
+# Detect tmux. Inside tmux, OSC escapes are swallowed and never reach
+# iTerm2's tab bar — so the user sees no color "at top". Surface the color
+# on tmux's own window/pane instead.
+inside_tmux=0
+[[ -n "${TMUX:-}" ]] && inside_tmux=1
+if (( ! inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  p2=$$
+  for _ in 1 2 3 4 5 6 7 8 9 10; do
+    line2="$(ps -o ppid=,comm= -p "$p2" 2>/dev/null)" || break
+    set -- $line2
+    pp2="${1:-}"; cm2="${2:-}"
+    [[ -z "$pp2" ]] && break
+    case "$cm2" in *tmux*) inside_tmux=1; break;; esac
+    p2="$pp2"
+  done
+fi
+
+# tmux swallows the DCS-wrapped iTerm2 tab-color OSC unless passthrough is on.
+# Default is OFF, so ensure it on every run — this is what makes the tab header
+# actually tint (otherwise the escape is dropped and the tab shows no color).
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  tmux set -g allow-passthrough on 2>/dev/null || true
+fi
+
+applied=0
+
+if [[ -n "$tty_path" && -w "$tty_path" ]]; then
+  if (( inside_tmux )); then
+    # Inside tmux, raw OSC is consumed by tmux. With allow-passthrough on,
+    # DCS-wrap (ESC P tmux ; <inner-with-doubled-ESC> ESC \) lets the
+    # iTerm2 tab-tint + title OSC reach the outer terminal.
+    {
+      printf '\033Ptmux;\033\033]6;1;bg;red;brightness;%d\007\033\\'   "$R"
+      printf '\033Ptmux;\033\033]6;1;bg;green;brightness;%d\007\033\\' "$G"
+      printf '\033Ptmux;\033\033]6;1;bg;blue;brightness;%d\007\033\\'  "$B"
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$label"
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$label"
+    } > "$tty_path" 2>/dev/null
+  else
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$label"
+      printf '\033]2;%s\007' "$label"
+    } > "$tty_path" 2>/dev/null
+  fi
+  applied=1
+fi
+
+# tmux indicator path — recolors the tmux window name + current pane border
+# so the color surfaces in tmux's status bar regardless of outer terminal.
+# Window TEXT = topic if captured, else the color label until first prompt.
+if [[ -n "$topic" ]]; then winname="$topic"; else winname="${EMOJI} ${NAME} #${session}"; fi
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  # scope to THIS pane via $TMUX_PANE — without it, plain `rename-window`
+  # targets the active window of the session, which is almost never the
+  # window the new claude session is actually running in.
+  tgt="${TMUX_PANE:-}"
+  if [[ -n "$tgt" ]]; then
+    # keep the topic sticky against tmux automatic-rename
+    [[ -n "$topic" ]] && tmux set-window-option -q -t "$tgt" automatic-rename off 2>/dev/null || true
+    tmux rename-window -t "$tgt" "$winname" 2>/dev/null || true
+    tmux set-window-option -q -t "$tgt" window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-border-style "fg=${hex}" 2>/dev/null || true
+  else
+    [[ -n "$topic" ]] && tmux set-window-option -q automatic-rename off 2>/dev/null || true
+    tmux rename-window "$winname" 2>/dev/null || true
+    tmux set-window-option -q window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q pane-border-style "fg=${hex}" 2>/dev/null || true
+  fi
+  applied=1
+fi
+
+if (( applied )); then
+  suffix=""
+  (( inside_tmux )) && suffix="  · tmux window+pane recolored"
+  echo "/color → ${NAME} ${EMOJI}  rgb(${R},${G},${B})  ${hex}  · session #${session}${suffix}"
+else
+  echo "/color → ${NAME} ${EMOJI} (session #${session}) — terminal pty not found, color not applied"
+fi
+
+# Shared terminal status follows the cosmetic session color.
+python3 "$HOME/Projects/terminal-status/terminal_status.py" start --quiet || true
diff --git a/verification/install/after/.claude/skills/color/scripts/paint-tab.sh b/verification/install/after/.claude/skills/color/scripts/paint-tab.sh
new file mode 100644
index 0000000..bcf0b11
--- /dev/null
+++ b/verification/install/after/.claude/skills/color/scripts/paint-tab.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# Legacy one-shot painter routed through semantic status.
+set -eu
+engine="$HOME/Projects/terminal-status/terminal_status.py"
+case "${1:-waiting}" in
+  working|green) exec python3 "$engine" set green WORKING --quiet ;;
+  waiting|purple|solid-purple) exec python3 "$engine" set purple GATED --quiet ;;
+  stop) exec python3 "$engine" repaint --quiet ;;
+  *) echo "Unknown paint-tab state" >&2; exit 2 ;;
+esac
diff --git a/verification/install/after/.claude/skills/color/scripts/tab-state.sh b/verification/install/after/.claude/skills/color/scripts/tab-state.sh
new file mode 100644
index 0000000..7836290
--- /dev/null
+++ b/verification/install/after/.claude/skills/color/scripts/tab-state.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+# Legacy lifecycle entrypoint: one-shot status, no flashers.
+set -eu
+engine="$HOME/Projects/terminal-status/terminal_status.py"
+case "${1:-}" in
+  red) exec python3 "$engine" set pink "Closing session" --quiet ;;
+  flash-red) exec python3 "$engine" set green COMPACTING --quiet ;;
+  purple|flash-purple|waiting) exec python3 "$engine" set purple GATED --quiet ;;
+  green|flash-green|stop) exec python3 "$engine" repaint --quiet ;;
+  *) echo "Unknown tab-state mode" >&2; exit 2 ;;
+esac
diff --git a/verification/install/after/.claude/skills/color/scripts/working-state.sh b/verification/install/after/.claude/skills/color/scripts/working-state.sh
new file mode 100644
index 0000000..8369e45
--- /dev/null
+++ b/verification/install/after/.claude/skills/color/scripts/working-state.sh
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+# One status update per user prompt; no heat colors or background title writers.
+set -eu
+engine="$HOME/Projects/terminal-status/terminal_status.py"
+case "${1:-}" in
+  on) exec python3 "$engine" set green WORKING --quiet ;;
+  off) exec python3 "$engine" repaint --quiet ;;
+  _watch|_idle_watch) exit 0 ;;
+  *) echo "usage: working-state.sh {on|off}" >&2; exit 2 ;;
+esac
diff --git a/verification/install/after/.claude/skills/colordots/paint-lib.sh b/verification/install/after/.claude/skills/colordots/paint-lib.sh
new file mode 100644
index 0000000..22b60a6
--- /dev/null
+++ b/verification/install/after/.claude/skills/colordots/paint-lib.sh
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+# Compatibility API. Semantic state and terminal writes share one engine.
+TABDOTS_DIR="$HOME/.claude/tab-dots"
+tabdots_is_subagent() { [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; }
+tabdots_resolve_tty() {
+  python3 "$HOME/Projects/terminal-status/terminal_status.py" current |
+    python3 -c 'import json,sys; print("/dev/"+json.load(sys.stdin)["tty"])'
+}
+tabdots_stop_pulse() { :; }  # Current painters never create pulse processes.
+tabdots_paint() {
+  python3 "$HOME/Projects/terminal-status/terminal_status.py" paint-legacy "$@"
+}
diff --git a/verification/install/after/.claude/skills/dot/current.sh b/verification/install/after/.claude/skills/dot/current.sh
new file mode 100644
index 0000000..5c0a81a
--- /dev/null
+++ b/verification/install/after/.claude/skills/dot/current.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+set -eu
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" status
diff --git a/verification/install/after/.claude/skills/greendot/greendot.sh b/verification/install/after/.claude/skills/greendot/greendot.sh
new file mode 100644
index 0000000..797b6eb
--- /dev/null
+++ b/verification/install/after/.claude/skills/greendot/greendot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Shared semantic status; see Projects/terminal-status/README.md.
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude green "$@"
diff --git a/verification/install/after/.claude/skills/lightgreendot/lightgreendot.sh b/verification/install/after/.claude/skills/lightgreendot/lightgreendot.sh
new file mode 100644
index 0000000..e8a9cf9
--- /dev/null
+++ b/verification/install/after/.claude/skills/lightgreendot/lightgreendot.sh
@@ -0,0 +1,9 @@
+#!/usr/bin/env bash
+# Green monitoring variant with a scheduled check/deadline.
+set -eu
+engine="$HOME/Projects/terminal-status/terminal_status.py"
+case "${1:-}" in
+  --off) exec python3 "$engine" clear ;;
+  --all) exec python3 "$engine" set green MONITORING --monitoring --all claude ;;
+  *) exec python3 "$engine" set green "${1:-MONITORING}" --monitoring ;;
+esac
diff --git a/verification/install/after/.claude/skills/orangedot/orangedot.sh b/verification/install/after/.claude/skills/orangedot/orangedot.sh
new file mode 100644
index 0000000..21f6ba6
--- /dev/null
+++ b/verification/install/after/.claude/skills/orangedot/orangedot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Shared semantic status; see Projects/terminal-status/README.md.
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude orange "$@"
diff --git a/verification/install/after/.claude/skills/pinkdot/pinkdot.sh b/verification/install/after/.claude/skills/pinkdot/pinkdot.sh
new file mode 100644
index 0000000..b81ecc5
--- /dev/null
+++ b/verification/install/after/.claude/skills/pinkdot/pinkdot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Shared semantic status; see Projects/terminal-status/README.md.
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude pink "$@"
diff --git a/verification/install/after/.claude/skills/purpledot/purpledot.sh b/verification/install/after/.claude/skills/purpledot/purpledot.sh
new file mode 100644
index 0000000..9f26d48
--- /dev/null
+++ b/verification/install/after/.claude/skills/purpledot/purpledot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Shared semantic status; see Projects/terminal-status/README.md.
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude purple "$@"
diff --git a/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh b/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh
new file mode 100644
index 0000000..660d549
--- /dev/null
+++ b/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh
@@ -0,0 +1,5 @@
+#!/usr/bin/env bash
+# Ticket binding is tied to the actual main agent's PID, start time, and tty.
+set -eu
+[[ "${1:-}" == "--apply" ]] && shift
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" ticket "${1:?TK number required}"
diff --git a/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh b/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh
new file mode 100644
index 0000000..9c05047
--- /dev/null
+++ b/verification/install/after/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh
@@ -0,0 +1,4 @@
+#!/usr/bin/env bash
+# Existing launchd renderer; read-only, no independent state or background loop.
+set -eu
+exec python3 "$HOME/Projects/terminal-status/terminal_status.py" headers
diff --git a/verification/install/after/.claude/skills/yellowdot/yellowdot.sh b/verification/install/after/.claude/skills/yellowdot/yellowdot.sh
new file mode 100644
index 0000000..12340de
--- /dev/null
+++ b/verification/install/after/.claude/skills/yellowdot/yellowdot.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env bash
+# Shared semantic status; see Projects/terminal-status/README.md.
+exec bash "$HOME/Projects/terminal-status/integrations/dot.sh" claude yellow "$@"
diff --git a/verification/install/before/.agents/skills/color/scripts/color.sh b/verification/install/before/.agents/skills/color/scripts/color.sh
new file mode 100644
index 0000000..1cab0d7
--- /dev/null
+++ b/verification/install/before/.agents/skills/color/scripts/color.sh
@@ -0,0 +1,167 @@
+#!/usr/bin/env bash
+# /color — give this Claude Code session a fresh, visually-distinct color and
+# apply it to the terminal tab so parallel Claude sessions are tellable apart.
+#
+# Mechanism: Claude Code's Bash tool runs detached with NO controlling tty
+# (verified: `tty` => "not a tty", /dev/tty => "device not configured"), so
+# OSC escapes on stdout/stderr never reach the terminal. The working path is
+# to locate the `claude` process's own pty by walking process ancestry and
+# write the OSC sequence straight to /dev/ttysNNN. OSC color/title codes are
+# non-destructive (no cursor moves, no cell writes) so this is glitch-safe.
+#
+# Colors: golden-angle (137.508 deg) hue rotation => every session maximally
+# far from the last on the hue wheel, never repeating. iTerm2 gets a tab tint;
+# every terminal (incl. macOS Terminal.app) gets an emoji + name tab title.
+set -u
+
+STATE_DIR="${HOME}/.claude"
+STATE="${STATE_DIR}/color-session-index"
+mkdir -p "$STATE_DIR"
+
+idx=0
+[[ -f "$STATE" ]] && idx="$(cat "$STATE" 2>/dev/null)"
+[[ "$idx" =~ ^[0-9]+$ ]] || idx=0
+session=$(( idx + 1 ))
+printf '%s\n' "$session" > "$STATE"
+
+# golden-angle hue -> HSL(90% sat, 58% light) -> RGB, pure awk (no python/flock dep)
+read -r R G B HUE < <(awk -v idx="$idx" '
+  function absf(v){ return v<0 ? -v : v }
+  BEGIN{
+    hue = idx*137.508; hue = hue - int(hue/360)*360
+    s=0.90; l=0.58
+    c=(1-absf(2*l-1))*s
+    hp=hue/60.0
+    m2=hp - int(hp/2)*2
+    x=c*(1-absf(m2-1))
+    if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+    else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+    else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+    m=l-c/2
+    printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+  }')
+
+# hue -> coarse name + nearest emoji square
+if   (( HUE < 20  )); then NAME=Red;     EMOJI=🟥
+elif (( HUE < 45  )); then NAME=Orange;  EMOJI=🟧
+elif (( HUE < 70  )); then NAME=Amber;   EMOJI=🟨
+elif (( HUE < 150 )); then NAME=Green;   EMOJI=🟩
+elif (( HUE < 215 )); then NAME=Teal;    EMOJI=🟦
+elif (( HUE < 255 )); then NAME=Blue;    EMOJI=🟦
+elif (( HUE < 290 )); then NAME=Indigo;  EMOJI=🟪
+elif (( HUE < 330 )); then NAME=Violet;  EMOJI=🟪
+else                       NAME=Magenta; EMOJI=🟥
+fi
+
+# locate the claude process's controlling pty by walking the ancestry
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+
+# Recall the session TOPIC (the user's first prompt) so re-coloring mid-session
+# does NOT clobber the topic with the color name — the color is the tint, the
+# text stays the topic. color-title.sh already wrote the topic onto the tmux
+# window name (with automatic-rename off), so read it straight back from there.
+# Ignore our own color-name placeholder ("<emoji> Indigo #359").
+topic=""
+if [[ -n "${TMUX_PANE:-}" ]] && command -v tmux >/dev/null 2>&1; then
+  cur="$(tmux display-message -p -t "$TMUX_PANE" '#{window_name}' 2>/dev/null)"
+  if [[ -n "$cur" && ! "$cur" =~ (Red|Orange|Amber|Green|Teal|Blue|Indigo|Violet|Magenta)\ \#[0-9]+$ ]]; then
+    topic="$cur"
+  fi
+fi
+
+# Tab/window TEXT: prefer the topic; until the first prompt sets one, show just
+# the emoji so the tab is visibly initialised without shouting the color name.
+if [[ -n "$topic" ]]; then label="$topic"; else label="${EMOJI}"; fi
+hex=$(printf '#%02X%02X%02X' "$R" "$G" "$B")
+
+# Detect tmux. Inside tmux, OSC escapes are swallowed and never reach
+# iTerm2's tab bar — so the user sees no color "at top". Surface the color
+# on tmux's own window/pane instead.
+inside_tmux=0
+[[ -n "${TMUX:-}" ]] && inside_tmux=1
+if (( ! inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  p2=$$
+  for _ in 1 2 3 4 5 6 7 8 9 10; do
+    line2="$(ps -o ppid=,comm= -p "$p2" 2>/dev/null)" || break
+    set -- $line2
+    pp2="${1:-}"; cm2="${2:-}"
+    [[ -z "$pp2" ]] && break
+    case "$cm2" in *tmux*) inside_tmux=1; break;; esac
+    p2="$pp2"
+  done
+fi
+
+# tmux swallows the DCS-wrapped iTerm2 tab-color OSC unless passthrough is on.
+# Default is OFF, so ensure it on every run — this is what makes the tab header
+# actually tint (otherwise the escape is dropped and the tab shows no color).
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  tmux set -g allow-passthrough on 2>/dev/null || true
+fi
+
+applied=0
+
+if [[ -n "$tty_path" && -w "$tty_path" ]]; then
+  if (( inside_tmux )); then
+    # Inside tmux, raw OSC is consumed by tmux. With allow-passthrough on,
+    # DCS-wrap (ESC P tmux ; <inner-with-doubled-ESC> ESC \) lets the
+    # iTerm2 tab-tint + title OSC reach the outer terminal.
+    {
+      printf '\033Ptmux;\033\033]6;1;bg;red;brightness;%d\007\033\\'   "$R"
+      printf '\033Ptmux;\033\033]6;1;bg;green;brightness;%d\007\033\\' "$G"
+      printf '\033Ptmux;\033\033]6;1;bg;blue;brightness;%d\007\033\\'  "$B"
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$label"
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$label"
+    } > "$tty_path" 2>/dev/null
+  else
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$label"
+      printf '\033]2;%s\007' "$label"
+    } > "$tty_path" 2>/dev/null
+  fi
+  applied=1
+fi
+
+# tmux indicator path — recolors the tmux window name + current pane border
+# so the color surfaces in tmux's status bar regardless of outer terminal.
+# Window TEXT = topic if captured, else the color label until first prompt.
+if [[ -n "$topic" ]]; then winname="$topic"; else winname="${EMOJI} ${NAME} #${session}"; fi
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  # scope to THIS pane via $TMUX_PANE — without it, plain `rename-window`
+  # targets the active window of the session, which is almost never the
+  # window the new claude session is actually running in.
+  tgt="${TMUX_PANE:-}"
+  if [[ -n "$tgt" ]]; then
+    # keep the topic sticky against tmux automatic-rename
+    [[ -n "$topic" ]] && tmux set-window-option -q -t "$tgt" automatic-rename off 2>/dev/null || true
+    tmux rename-window -t "$tgt" "$winname" 2>/dev/null || true
+    tmux set-window-option -q -t "$tgt" window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-border-style "fg=${hex}" 2>/dev/null || true
+  else
+    [[ -n "$topic" ]] && tmux set-window-option -q automatic-rename off 2>/dev/null || true
+    tmux rename-window "$winname" 2>/dev/null || true
+    tmux set-window-option -q window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q pane-border-style "fg=${hex}" 2>/dev/null || true
+  fi
+  applied=1
+fi
+
+if (( applied )); then
+  suffix=""
+  (( inside_tmux )) && suffix="  · tmux window+pane recolored"
+  echo "/color → ${NAME} ${EMOJI}  rgb(${R},${G},${B})  ${hex}  · session #${session}${suffix}"
+else
+  echo "/color → ${NAME} ${EMOJI} (session #${session}) — terminal pty not found, color not applied"
+fi
diff --git a/verification/install/before/.agents/skills/color/scripts/repaint-dot.sh b/verification/install/before/.agents/skills/color/scripts/repaint-dot.sh
new file mode 100644
index 0000000..c8596dd
--- /dev/null
+++ b/verification/install/before/.agents/skills/color/scripts/repaint-dot.sh
@@ -0,0 +1,36 @@
+#!/usr/bin/env bash
+set -u
+
+tty_path=""
+process_id=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$process_id" 2>/dev/null)" || break
+  set -- $line
+  parent_id="${1:-}"
+  tty_name="${2:-}"
+  [[ -z "$parent_id" ]] && break
+  if [[ "$tty_name" == ttys* ]]; then tty_path="/dev/$tty_name"; break; fi
+  process_id="$parent_id"
+done
+[[ -n "$tty_path" ]] || exit 0
+
+state_file="${HOME}/.codex/tab-dots/$(basename "$tty_path").dot"
+[[ -f "$state_file" ]] || exit 0
+title="$(cat "$state_file")"
+case "$title" in
+  🟢*) rgb="0 200 83" ;;
+  🟡*) rgb="255 204 0" ;;
+  🟣*) rgb="148 0 211" ;;
+  🟠*) rgb="255 140 0" ;;
+  🩷*) rgb="255 105 180" ;;
+  *) exit 0 ;;
+esac
+set -- $rgb
+{
+  printf '\033]6;1;bg;red;brightness;%d\007' "$1"
+  printf '\033]6;1;bg;green;brightness;%d\007' "$2"
+  printf '\033]6;1;bg;blue;brightness;%d\007' "$3"
+  printf '\033]1;%s\007' "$title"
+  printf '\033]2;%s\007' "$title"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')"
+} > "$tty_path" 2>/dev/null
diff --git a/verification/install/before/.agents/skills/color/scripts/semantic-dot.sh b/verification/install/before/.agents/skills/color/scripts/semantic-dot.sh
new file mode 100644
index 0000000..5451693
--- /dev/null
+++ b/verification/install/before/.agents/skills/color/scripts/semantic-dot.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+set -u
+
+STATE="${1:?state required}"
+R="${2:?red required}"
+G="${3:?green required}"
+B="${4:?blue required}"
+DOT="${5:?dot required}"
+DEFAULT_LABEL="${6:?default label required}"
+shift 6
+
+paint() {
+  local tty_path="$1" label="${2:-$DEFAULT_LABEL}" off="${3:-0}"
+  [[ -w "$tty_path" ]] || return 1
+  local state_file="${HOME}/.codex/tab-dots/$(basename "$tty_path").dot"
+  if [[ "$off" == "1" ]]; then
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tty_path" 2>/dev/null
+    rm -f "$state_file" 2>/dev/null
+    return 0
+  fi
+  local title="$DOT $label"
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007' "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007' "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tty_path" 2>/dev/null
+  mkdir -p "${HOME}/.codex/tab-dots"
+  printf '%s' "$title" > "$state_file"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tty_path" 2>/dev/null
+}
+
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,comm=,command= 2>/dev/null | awk '$1 ~ /^ttys/ && $3 ~ /(^|\/)codex$/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/${STATE}dot --all → no live Codex ttys"; exit 0; }
+  count=0
+  for tty_name in $ttys; do
+    paint "/dev/$tty_name" "$DEFAULT_LABEL" 0 && count=$((count + 1))
+  done
+  echo "/${STATE}dot --all → marked ${count} Codex tab(s) $DOT $DEFAULT_LABEL"
+  exit 0
+fi
+
+OFF=0
+LABEL="${1:-$DEFAULT_LABEL}"
+[[ "${1:-}" == "--off" ]] && OFF=1
+
+tty_path=""
+process_id=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$process_id" 2>/dev/null)" || break
+  set -- $line
+  parent_id="${1:-}"
+  tty_name="${2:-}"
+  [[ -z "$parent_id" ]] && break
+  if [[ "$tty_name" == ttys* ]]; then tty_path="/dev/$tty_name"; break; fi
+  process_id="$parent_id"
+done
+
+if [[ -z "$tty_path" ]]; then
+  echo "/${STATE}dot → could not resolve this Codex session tty"
+  exit 1
+fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/${STATE}dot --off → cleared $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/${STATE}dot → $tty_path $DOT $LABEL rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/hooks/dot-floor.sh b/verification/install/before/.claude/hooks/dot-floor.sh
new file mode 100644
index 0000000..f53ec69
--- /dev/null
+++ b/verification/install/before/.claude/hooks/dot-floor.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+# dot-floor.sh — Stop-hook DOT FLOOR: guarantee this session's iTerm2 tab always shows a dot.
+#
+# Fires at the end of every turn (settings.json Stop hook). It is a SAFETY NET, not a judge:
+#   • If this tab already has a dot set (any color) → do NOTHING. The model's deliberate
+#     /dot choice (green/yellow/purple/orange/pink) is respected and never overridden here.
+#   • If this tab has NO dot (blank) → paint GREEN ("WORKING"). This closes the "no dot" gap
+#     (the exact bug greendot was born for) without trying to auto-guess the semantic color —
+#     purple/yellow are persistent semantic states only the model can judge, so a per-turn
+#     heuristic must NOT touch them.
+#
+# Zero model cost (no block / no extra turn). Always exits 0 — a Stop hook must never break the
+# session. Resolves the tab the same way the dot scripts do: walk process ancestry to the ttysNNN
+# (the hook runs as a child of the `claude` process, so the walk reaches the controlling pty).
+set -u
+
+# Drain stdin (Stop hook delivers JSON: session_id, transcript_path, stop_hook_active, cwd).
+# We don't need it for the floor — read-and-ignore so the pipe never blocks.
+cat >/dev/null 2>&1 || true
+
+# Resolve THIS session's controlling pty via ancestry (identical walk to greendot.sh / current.sh).
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [ -z "$pp" ] && break
+  case "$tt" in ttys*) tty_path="/dev/$tt"; break;; esac
+  p="$pp"
+done
+[ -z "$tty_path" ] && exit 0   # no ttysNNN in ancestry (launchd/pipe context) → nothing to paint
+
+dotfile="${HOME}/.claude/tab-dots/$(basename "$tty_path").dot"
+
+# FLOOR: only act when the tab is blank (no dot file, or empty). Respect any existing dot.
+if [ ! -s "$dotfile" ]; then
+  bash "${HOME}/.claude/skills/greendot/greendot.sh" >/dev/null 2>&1 || true
+fi
+
+exit 0
diff --git a/verification/install/before/.claude/skills/allcolordots/allcolordots.sh b/verification/install/before/.claude/skills/allcolordots/allcolordots.sh
new file mode 100644
index 0000000..a599ab2
--- /dev/null
+++ b/verification/install/before/.claude/skills/allcolordots/allcolordots.sh
@@ -0,0 +1,196 @@
+#!/usr/bin/env bash
+# /allcolordots — scan EVERY active terminal's color dot + label in one shot,
+# and (new) present a clickable single-window ROSTER per color that JUMPS focus
+# to any tab — the "gather my yellows in one place" navigator (path A).
+#
+# Prefer semantic LIVE iTerm2 names. If Codex overwrites the dot, recover the
+# newest valid ~/.claude or ~/.codex/tab-dots record from this agent lifetime.
+# "live" = a `claude`/`codex` process is running on that tty.
+#
+# Usage:
+#   allcolordots.sh                 # grouped human table (orange > purple > yellow > green > pink)
+#   allcolordots.sh --json          # JSON array: [{tty,color,label,live,pid}]  (navbar app)
+#   allcolordots.sh --live          # table, but only ACTIVE (live) terminals
+#   allcolordots.sh --yellow        # numbered ROSTER of just the 🟡 needs-direction tabs
+#   allcolordots.sh --only <color>  # numbered roster of one color (yellow|orange|purple|green|pink)
+#   allcolordots.sh jump <ttyNNN|N> # focus that tab (N = row# from the last roster)
+set -u
+# Emoji glob-matching (case *🟡*) needs a UTF-8 ctype; some launch contexts pass
+# LC_CTYPE= empty which silently breaks the match -> every dot reads "none".
+export LANG="${LANG:-en_US.UTF-8}" LC_ALL="en_US.UTF-8"
+DOTS="$HOME/.claude/tab-dots"
+CODEX_DOTS="$HOME/.codex/tab-dots"
+ROSTER_CACHE="$DOTS/.last-roster"
+
+color_of(){ case "$1" in *🟢*) echo green;; *🟡*) echo yellow;; *🟣*) echo purple;; *🟠*) echo orange;; *🩷*) echo pink;; *) echo none;; esac; }
+emoji_of(){ case "$1" in orange) echo 🟠;; purple) echo 🟣;; yellow) echo 🟡;; green) echo 🟢;; pink) echo 🩷;; *) echo ⚪;; esac; }
+
+# Codex rewrites its title while working. Recover its semantic state only from
+# this tty's files written during the current agent lifetime; old tty numbers
+# get reused. Read both painters' stores and choose by mtime, never by color.
+saved_label(){
+  [ -n "${2:-}" ] || return 0
+  python3 - "$1" "$2" "$DOTS" "$CODEX_DOTS" <<'PY'
+import datetime, pathlib, re, subprocess, sys
+tty, pid, *directories = sys.argv[1:]
+if not re.fullmatch(r"ttys[0-9]+", tty) or not pid.isdigit():
+    sys.exit(0)
+try:
+    started = subprocess.check_output(
+        ["ps", "-p", pid, "-o", "lstart="], text=True,
+        stderr=subprocess.DEVNULL).strip()
+    started = datetime.datetime.strptime(started, "%a %b %d %H:%M:%S %Y").timestamp()
+except (ValueError, OSError, subprocess.CalledProcessError):
+    sys.exit(0)
+eligible = []
+for directory in directories:
+    try:
+        path = pathlib.Path(directory) / (tty + ".dot")
+        stamp = path.stat().st_mtime
+        label = path.read_text(encoding="utf-8").strip()
+    except (OSError, UnicodeError):
+        continue
+    if (stamp >= started and label.startswith(("🟢", "🟡", "🟣", "🟠", "🩷"))
+            and not any(ord(c) < 32 or ord(c) == 127 for c in label)):
+        eligible.append((stamp, label))
+if eligible:
+    print(max(eligible, key=lambda item: item[0])[1])
+PY
+}
+
+# ---- LIVE session scan via iTerm2 (name carries the dot+label; freshest source) ----
+# Emits: tty<TAB>name  (one per session). Falls back silently to .dot files if osascript fails.
+live_sessions(){
+  # NOTE: inside `tell application "iTerm2"`, the word `tab` resolves to iTerm2's
+  # own `tab` object class — NOT the tab character — so `& tab &` emits the literal
+  # string "tab". Use an explicit ASCII 9 for the field separator.
+  osascript 2>/dev/null <<'OSA'
+set SEP to (ASCII character 9)
+tell application "iTerm2"
+  set out to ""
+  repeat with w in windows
+    repeat with t in tabs of w
+      repeat with s in sessions of t
+        set out to out & (tty of s) & SEP & (name of s) & linefeed
+      end repeat
+    end repeat
+  end repeat
+  return out
+end tell
+OSA
+}
+
+# ---- JUMP: focus the tab whose tty matches (select + activate; NOT a reparent) ----
+jump_to(){
+  local arg="$1" tty=""
+  # index into the last roster?
+  if printf '%s' "$arg" | grep -qE '^[0-9]+$' && [ -f "$ROSTER_CACHE" ]; then
+    tty="$(awk -F'\t' -v n="$arg" 'NR==n{print $1}' "$ROSTER_CACHE")"
+  fi
+  [ -z "$tty" ] && tty="$arg"
+  case "$tty" in /dev/*) :;; ttys*) tty="/dev/$tty";; *) tty="/dev/$tty";; esac
+  osascript 2>&1 <<OSA
+tell application "iTerm2"
+  repeat with w in windows
+    repeat with t in tabs of w
+      repeat with s in sessions of t
+        if (tty of s) is "$tty" then
+          select w
+          select t
+          select s
+          activate
+          return "jumped -> $tty"
+        end if
+      end repeat
+    end repeat
+  end repeat
+  return "not found: $tty"
+end tell
+OSA
+}
+
+# ---- collect normalized rows: tty \t color \t live \t pid \t label ----
+collect_rows(){
+  local live_raw; live_raw="$(live_sessions)"
+  if [ -n "$live_raw" ]; then
+    # LIVE path: iTerm2 names are the label; a claude/codex proc on the tty = live
+    printf '%s\n' "$live_raw" | while IFS=$'\t' read -r tty name; do
+      [ -z "$tty" ] && continue
+      local short="${tty#/dev/}"
+      local color; color="$(color_of "$name")"
+      local pid; pid="$(ps -axo pid=,tty=,command= 2>/dev/null | awk -v t="$short" '$2==t && (/claude/||/codex/) && !/awk/ {print $1; exit}')"
+      local live=false; [ -n "$pid" ] && live=true
+      if [ "$color" = none ] && [ "$live" = true ]; then
+        local saved; saved="$(saved_label "$short" "$pid")"
+        if [ -n "$saved" ]; then name="$saved"; color="$(color_of "$saved")"; fi
+      fi
+      printf '%s\t%s\t%s\t%s\t%s\n' "$short" "$color" "$live" "${pid:-}" "$name"
+    done
+  else
+    # FALLBACK path: persisted .dot files
+    for f in "$DOTS"/*.dot; do
+      [ -f "$f" ] || continue
+      local tty; tty="$(basename "$f" .dot)"
+      local label; label="$(cat "$f" 2>/dev/null)"
+      local color; color="$(color_of "$label")"
+      local pid; pid="$(ps -axo pid=,tty=,command= 2>/dev/null | awk -v t="$tty" '$2==t && (/claude/||/codex/) && !/awk/ {print $1; exit}')"
+      local live=false; [ -n "$pid" ] && live=true
+      printf '%s\t%s\t%s\t%s\t%s\n' "$tty" "$color" "$live" "${pid:-}" "$label"
+    done
+  fi
+}
+
+# ---- roster of ONE color: numbered, jump-ready, caches order for `jump N` ----
+print_roster(){
+  local want="$1" rows; rows="$(collect_rows | awk -F'\t' -v c="$want" '$2==c')"
+  : > "$ROSTER_CACHE"
+  local e; e="$(emoji_of "$want")"
+  local n; n="$(printf '%s\n' "$rows" | grep -c .)"
+  echo "=== $e $want roster ($n) — jump with:  allcolordots.sh jump <#|tty> ==="
+  [ "$n" = 0 ] && { echo "  (none)"; return 0; }
+  local i=0
+  printf '%s\n' "$rows" | while IFS=$'\t' read -r tty color live pid label; do
+    i=$((i+1))
+    printf '%s\t%s\t%s\n' "$tty" "$label" "$live" >> "$ROSTER_CACHE"
+    local flag=""; [ "$live" = true ] && flag="  ·live"
+    printf '  %2d) %-9s %s%s\n' "$i" "$tty" "$label" "$flag"
+  done
+}
+
+# ---------------- dispatch ----------------
+case "${1:-}" in
+  jump)   shift; jump_to "${1:-}"; exit $? ;;
+  --yellow) print_roster yellow; exit 0 ;;
+  --only)   print_roster "${2:-yellow}"; exit 0 ;;
+  --json)
+    collect_rows | python3 -c '
+import sys,json
+out=[]
+for line in sys.stdin:
+    line=line.rstrip("\n")
+    if not line: continue
+    tty,color,live,pid,label=(line.split("\t")+["","","","",""])[:5]
+    out.append({"tty":tty,"color":color,"live":live=="true","pid":pid,"label":label})
+pri={"orange":0,"purple":1,"yellow":2,"green":3,"pink":4,"none":5}
+out.sort(key=lambda r:(pri.get(r["color"],9), r["tty"]))
+print(json.dumps(out))
+'
+    exit 0 ;;
+esac
+
+# default / --live : grouped human table
+LIVEONLY=0; [ "${1:-}" = "--live" ] && LIVEONLY=1
+rows="$(collect_rows)"
+[ "$LIVEONLY" = 1 ] && rows="$(printf '%s\n' "$rows" | awk -F'\t' '$3=="true"')"
+total="$(printf '%s\n' "$rows" | grep -c . )"
+echo "=== active-terminal dots ($total) ==="
+for c in orange purple yellow green pink none; do
+  block="$(printf '%s\n' "$rows" | awk -F'\t' -v c="$c" '$2==c')"
+  [ -z "$block" ] && continue
+  n="$(printf '%s\n' "$block" | grep -c .)"
+  echo "$(emoji_of "$c") ${c} ($n)"
+  printf '%s\n' "$block" | while IFS=$'\t' read -r tty color live pid label; do
+    flag=""; [ "$live" = true ] && flag="  ·live"
+    printf '   %-9s %s%s\n' "$tty" "$label" "$flag"
+  done
+done
diff --git a/verification/install/before/.claude/skills/color/scripts/color.sh b/verification/install/before/.claude/skills/color/scripts/color.sh
new file mode 100644
index 0000000..4c76b91
--- /dev/null
+++ b/verification/install/before/.claude/skills/color/scripts/color.sh
@@ -0,0 +1,246 @@
+#!/usr/bin/env bash
+# /color — give this Claude Code session a fresh, visually-distinct color and
+# apply it to the terminal tab so parallel Claude sessions are tellable apart.
+#
+# Mechanism: Claude Code's Bash tool runs detached with NO controlling tty
+# (verified: `tty` => "not a tty", /dev/tty => "device not configured"), so
+# OSC escapes on stdout/stderr never reach the terminal. The working path is
+# to locate the `claude` process's own pty by walking process ancestry and
+# write the OSC sequence straight to /dev/ttysNNN. OSC color/title codes are
+# non-destructive (no cursor moves, no cell writes) so this is glitch-safe.
+#
+# Colors: golden-angle (137.508 deg) hue rotation => every session maximally
+# far from the last on the hue wheel, never repeating. iTerm2 gets a tab tint;
+# every terminal (incl. macOS Terminal.app) gets an emoji + name tab title.
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/color → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Streamline / lite mode — skip the AUTOMATIC SessionStart color assignment
+# fleet-wide (the hook calls with no args). Explicit invocations like
+# `/color --all` pass an arg and still run. rm the file to restore auto mode.
+[ -f "$HOME/.claude/hooks-lite" ] && [ $# -eq 0 ] && exit 0
+
+STATE_DIR="${HOME}/.claude"
+STATE="${STATE_DIR}/color-session-index"
+mkdir -p "$STATE_DIR"
+
+idx=0
+[[ -f "$STATE" ]] && idx="$(cat "$STATE" 2>/dev/null)"
+[[ "$idx" =~ ^[0-9]+$ ]] || idx=0
+session=$(( idx + 1 ))
+printf '%s\n' "$session" > "$STATE"
+
+rgb_for_idx() { # idx -> "R G B HUE" via golden-angle HSL(90%,58%)
+  awk -v idx="$1" '
+    function absf(v){ return v<0 ? -v : v }
+    BEGIN{
+      hue = idx*137.508; hue = hue - int(hue/360)*360
+      s=0.90; l=0.58
+      c=(1-absf(2*l-1))*s
+      hp=hue/60.0
+      m2=hp - int(hp/2)*2
+      x=c*(1-absf(m2-1))
+      if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+      else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+      else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+      m=l-c/2
+      printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+    }'
+}
+
+name_for_hue() { # hue -> "Name Emoji"
+  local h="$1"
+  if   (( h < 20  )); then echo "Red 🟥"
+  elif (( h < 45  )); then echo "Orange 🟧"
+  elif (( h < 70  )); then echo "Amber 🟨"
+  elif (( h < 150 )); then echo "Green 🟩"
+  elif (( h < 215 )); then echo "Teal 🟦"
+  elif (( h < 255 )); then echo "Blue 🟦"
+  elif (( h < 290 )); then echo "Indigo 🟪"
+  elif (( h < 330 )); then echo "Violet 🟪"
+  else                     echo "Magenta 🟥"
+  fi
+}
+
+# --all: paint EVERY open tab running a live `claude` session, each with its
+# own distinct golden-angle color. Claude processes report comm=node, so match
+# on the full command line and take each unique controlling tty.
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' \
+    | sort -u)"
+  if [[ -z "$ttys" ]]; then
+    echo "/color --all → no live claude ttys found"
+    exit 0
+  fi
+  n=0
+  for tt in $ttys; do
+    tp="/dev/$tt"
+    [[ -w "$tp" ]] || { echo "  $tt → not writable, skipped"; continue; }
+    aidx=$(( session + n ))
+    read -r R G B HUE < <(rgb_for_idx "$aidx")
+    read -r NAME EMOJI < <(name_for_hue "$HUE")
+    # NOTE: do NOT send a leading '6;1;bg;*;default' reset — on iTerm2 it swallows
+    # the color set, leaving the tab default (bug found 2026-09-02, same as pinkdot).
+    # Set RGB directly and write BOTH title channels (]1 icon/tab, ]2 window).
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$EMOJI"
+      printf '\033]2;%s\007' "$EMOJI"
+    } > "$tp" 2>/dev/null && echo "  $tt → ${NAME} ${EMOJI}  rgb(${R},${G},${B})"
+    n=$(( n + 1 ))
+  done
+  printf '%s\n' "$(( session + n ))" > "$STATE"
+  echo "/color --all → painted ${n} tab(s), counter now $(( session + n ))"
+  exit 0
+fi
+
+# golden-angle hue -> HSL(90% sat, 58% light) -> RGB, pure awk (no python/flock dep)
+read -r R G B HUE < <(awk -v idx="$idx" '
+  function absf(v){ return v<0 ? -v : v }
+  BEGIN{
+    hue = idx*137.508; hue = hue - int(hue/360)*360
+    s=0.90; l=0.58
+    c=(1-absf(2*l-1))*s
+    hp=hue/60.0
+    m2=hp - int(hp/2)*2
+    x=c*(1-absf(m2-1))
+    if(hp<1){r=c;g=x;b=0} else if(hp<2){r=x;g=c;b=0}
+    else if(hp<3){r=0;g=c;b=x} else if(hp<4){r=0;g=x;b=c}
+    else if(hp<5){r=x;g=0;b=c} else {r=c;g=0;b=x}
+    m=l-c/2
+    printf "%d %d %d %d", int((r+m)*255+0.5),int((g+m)*255+0.5),int((b+m)*255+0.5),int(hue)
+  }')
+
+# hue -> coarse name + nearest emoji square
+if   (( HUE < 20  )); then NAME=Red;     EMOJI=🟥
+elif (( HUE < 45  )); then NAME=Orange;  EMOJI=🟧
+elif (( HUE < 70  )); then NAME=Amber;   EMOJI=🟨
+elif (( HUE < 150 )); then NAME=Green;   EMOJI=🟩
+elif (( HUE < 215 )); then NAME=Teal;    EMOJI=🟦
+elif (( HUE < 255 )); then NAME=Blue;    EMOJI=🟦
+elif (( HUE < 290 )); then NAME=Indigo;  EMOJI=🟪
+elif (( HUE < 330 )); then NAME=Violet;  EMOJI=🟪
+else                       NAME=Magenta; EMOJI=🟥
+fi
+
+# locate the claude process's controlling pty by walking the ancestry
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+
+# Recall the session TOPIC (the user's first prompt) so re-coloring mid-session
+# does NOT clobber the topic with the color name — the color is the tint, the
+# text stays the topic. color-title.sh already wrote the topic onto the tmux
+# window name (with automatic-rename off), so read it straight back from there.
+# Ignore our own color-name placeholder ("<emoji> Indigo #359").
+topic=""
+if [[ -n "${TMUX_PANE:-}" ]] && command -v tmux >/dev/null 2>&1; then
+  cur="$(tmux display-message -p -t "$TMUX_PANE" '#{window_name}' 2>/dev/null)"
+  if [[ -n "$cur" && ! "$cur" =~ (Red|Orange|Amber|Green|Teal|Blue|Indigo|Violet|Magenta)\ \#[0-9]+$ ]]; then
+    topic="$cur"
+  fi
+fi
+
+# Tab/window TEXT: prefer the topic; until the first prompt sets one, show just
+# the emoji so the tab is visibly initialised without shouting the color name.
+if [[ -n "$topic" ]]; then label="$topic"; else label="${EMOJI}"; fi
+hex=$(printf '#%02X%02X%02X' "$R" "$G" "$B")
+
+# Detect tmux. Inside tmux, OSC escapes are swallowed and never reach
+# iTerm2's tab bar — so the user sees no color "at top". Surface the color
+# on tmux's own window/pane instead.
+inside_tmux=0
+[[ -n "${TMUX:-}" ]] && inside_tmux=1
+if (( ! inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  p2=$$
+  for _ in 1 2 3 4 5 6 7 8 9 10; do
+    line2="$(ps -o ppid=,comm= -p "$p2" 2>/dev/null)" || break
+    set -- $line2
+    pp2="${1:-}"; cm2="${2:-}"
+    [[ -z "$pp2" ]] && break
+    case "$cm2" in *tmux*) inside_tmux=1; break;; esac
+    p2="$pp2"
+  done
+fi
+
+# tmux swallows the DCS-wrapped iTerm2 tab-color OSC unless passthrough is on.
+# Default is OFF, so ensure it on every run — this is what makes the tab header
+# actually tint (otherwise the escape is dropped and the tab shows no color).
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  tmux set -g allow-passthrough on 2>/dev/null || true
+fi
+
+applied=0
+
+if [[ -n "$tty_path" && -w "$tty_path" ]]; then
+  if (( inside_tmux )); then
+    # Inside tmux, raw OSC is consumed by tmux. With allow-passthrough on,
+    # DCS-wrap (ESC P tmux ; <inner-with-doubled-ESC> ESC \) lets the
+    # iTerm2 tab-tint + title OSC reach the outer terminal.
+    {
+      printf '\033Ptmux;\033\033]6;1;bg;red;brightness;%d\007\033\\'   "$R"
+      printf '\033Ptmux;\033\033]6;1;bg;green;brightness;%d\007\033\\' "$G"
+      printf '\033Ptmux;\033\033]6;1;bg;blue;brightness;%d\007\033\\'  "$B"
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$label"
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$label"
+    } > "$tty_path" 2>/dev/null
+  else
+    {
+      printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+      printf '\033]1;%s\007' "$label"
+      printf '\033]2;%s\007' "$label"
+    } > "$tty_path" 2>/dev/null
+  fi
+  applied=1
+fi
+
+# tmux indicator path — recolors the tmux window name + current pane border
+# so the color surfaces in tmux's status bar regardless of outer terminal.
+# Window TEXT = topic if captured, else the color label until first prompt.
+if [[ -n "$topic" ]]; then winname="$topic"; else winname="${EMOJI} ${NAME} #${session}"; fi
+if (( inside_tmux )) && command -v tmux >/dev/null 2>&1; then
+  # scope to THIS pane via $TMUX_PANE — without it, plain `rename-window`
+  # targets the active window of the session, which is almost never the
+  # window the new claude session is actually running in.
+  tgt="${TMUX_PANE:-}"
+  if [[ -n "$tgt" ]]; then
+    # keep the topic sticky against tmux automatic-rename
+    [[ -n "$topic" ]] && tmux set-window-option -q -t "$tgt" automatic-rename off 2>/dev/null || true
+    tmux rename-window -t "$tgt" "$winname" 2>/dev/null || true
+    tmux set-window-option -q -t "$tgt" window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q -t "$tgt" pane-border-style "fg=${hex}" 2>/dev/null || true
+  else
+    [[ -n "$topic" ]] && tmux set-window-option -q automatic-rename off 2>/dev/null || true
+    tmux rename-window "$winname" 2>/dev/null || true
+    tmux set-window-option -q window-status-current-style "bg=${hex},fg=#000000,bold" 2>/dev/null || true
+    tmux set-option -p -q pane-active-border-style "fg=${hex},bold" 2>/dev/null || true
+    tmux set-option -p -q pane-border-style "fg=${hex}" 2>/dev/null || true
+  fi
+  applied=1
+fi
+
+if (( applied )); then
+  suffix=""
+  (( inside_tmux )) && suffix="  · tmux window+pane recolored"
+  echo "/color → ${NAME} ${EMOJI}  rgb(${R},${G},${B})  ${hex}  · session #${session}${suffix}"
+else
+  echo "/color → ${NAME} ${EMOJI} (session #${session}) — terminal pty not found, color not applied"
+fi
diff --git a/verification/install/before/.claude/skills/color/scripts/paint-tab.sh b/verification/install/before/.claude/skills/color/scripts/paint-tab.sh
new file mode 100644
index 0000000..e4e2f0e
--- /dev/null
+++ b/verification/install/before/.claude/skills/color/scripts/paint-tab.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+# paint-tab.sh <state>  — tint THIS Claude session's iTerm tab by state.
+#   working | green   -> SOLID green  (Claude is busy)  + stop any blinker
+#   waiting | purple  -> BLINKING purple (turn handed back to Steve — answer
+#                        needed). A background loop alternates purple<->default
+#                        until the next `working` call kills it.
+#   solid-purple      -> solid purple, no blink (internal/manual use)
+#   stop              -> kill the blinker only
+#
+# Why slot-prefix matching: the color hook/bash subprocess runs detached (tty
+# "??"), so walking process ancestry to find the tab pty is unreliable. And the
+# inherited ITERM_SESSION_ID's UUID half goes STALE on an iTerm reattach. The
+# w#t#p# (window/tab/pane) PREFIX is stable, so we match THAT against each live
+# `claude` process's env to resolve the correct /dev/ttysNNN. Verified 2026-06-26.
+set -u
+
+GREEN=(114 244 52)        # busy
+PURPLE=(168 85 247)       # awaiting Steve — bright phase
+PURPLE_DIM=(84 42 124)    # awaiting Steve — dim phase (PULSE, never blank)
+BLINK_SEC="${PAINT_BLINK_SEC:-0.7}"   # cadence of the idle pulse
+
+state="${1:-waiting}"
+
+# --- resolve THIS tab's pty by stable w#t#p# slot prefix --------------------
+[[ -z "${ITERM_SESSION_ID:-}" ]] && exit 0
+prefix="${ITERM_SESSION_ID%%:*}"
+tty=""
+for pid in $(pgrep -x claude 2>/dev/null); do
+  sid="$(ps eww -p "$pid" 2>/dev/null | grep -o 'ITERM_SESSION_ID=[^ ]*' | head -1 | cut -d= -f2-)"
+  [[ "${sid%%:*}" == "$prefix" ]] && tty="/dev/$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')"
+done
+[[ -n "$tty" && -w "$tty" ]] || exit 0
+
+# Per-tab blinker pidfile, keyed by slot prefix so one tab never kills another's.
+RUN_DIR="${HOME}/.claude/working-state"
+mkdir -p "$RUN_DIR" 2>/dev/null || true
+pidfile="${RUN_DIR}/blink-${prefix//[^A-Za-z0-9]/_}.pid"
+
+write_rgb() {  # write_rgb <tty> R G B
+  { printf '\033]6;1;bg;red;brightness;%d\007'   "$2"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$3"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$4"
+  } > "$1" 2>/dev/null
+}
+reset_tint() { printf '\033]6;1;bg;*;default\007' > "$1" 2>/dev/null; }
+
+stop_blink() {
+  [[ -f "$pidfile" ]] || return 0
+  kill "$(cat "$pidfile" 2>/dev/null)" 2>/dev/null || true
+  rm -f "$pidfile"
+}
+
+case "$state" in
+  green|working)
+    stop_blink
+    write_rgb "$tty" "${GREEN[@]}"
+    ;;
+  purple|waiting)
+    # blinking purple: stop any prior blinker, then start a fresh detached loop.
+    # tty is resolved HERE (parent) and handed to the loop — once backgrounded
+    # the loop may be reparented and lose the ability to re-resolve it.
+    stop_blink
+    ( trap 'exit 0' TERM INT
+      br="${PURPLE[0]}";     bg="${PURPLE[1]}";     bb="${PURPLE[2]}"      # bright
+      dr="${PURPLE_DIM[0]}"; dg="${PURPLE_DIM[1]}"; db="${PURPLE_DIM[2]}"  # dim
+      while :; do
+        # PULSE bright purple <-> dim purple — always a color, never blank.
+        { printf '\033]6;1;bg;red;brightness;%d\007'   "$br"
+          printf '\033]6;1;bg;green;brightness;%d\007' "$bg"
+          printf '\033]6;1;bg;blue;brightness;%d\007'  "$bb"
+        } > "$tty" 2>/dev/null
+        sleep "$BLINK_SEC"
+        { printf '\033]6;1;bg;red;brightness;%d\007'   "$dr"
+          printf '\033]6;1;bg;green;brightness;%d\007' "$dg"
+          printf '\033]6;1;bg;blue;brightness;%d\007'  "$db"
+        } > "$tty" 2>/dev/null
+        sleep "$BLINK_SEC"
+      done ) >/dev/null 2>&1 &
+    echo $! > "$pidfile"
+    disown 2>/dev/null || true
+    ;;
+  solid-purple)
+    stop_blink
+    write_rgb "$tty" "${PURPLE[@]}"
+    ;;
+  stop)
+    stop_blink
+    reset_tint "$tty"
+    ;;
+  *)
+    write_rgb "$tty" 128 128 128
+    ;;
+esac
+exit 0
diff --git a/verification/install/before/.claude/skills/color/scripts/tab-state.sh b/verification/install/before/.claude/skills/color/scripts/tab-state.sh
new file mode 100644
index 0000000..4007335
--- /dev/null
+++ b/verification/install/before/.claude/skills/color/scripts/tab-state.sh
@@ -0,0 +1,170 @@
+#!/usr/bin/env bash
+# tab-state.sh — drive the session-close visual state machine on the Claude
+# Code terminal tab. States:
+#   red          solid red   — session closing (/cs)
+#   flash-red    blinking red — compaction in progress (PreCompact hook)
+#   flash-orange blinking orange — STOPPED, waiting on a background result
+#               (e.g. a background task / DTD panel / long job to land)
+#   flash-purple blinking purple — STOPPED & WAITING ON STEVE (Stop hook): the
+#   (alias:      turn has been handed back and Claude is idle awaiting input.
+#    waiting)    Cleared by a `stop`/`green`/`red` (or the next UserPromptSubmit).
+#   purple       solid purple
+#   flash-green  green flash ×3 then solid green — compaction complete (SessionStart compact)
+#   green        solid green
+#   stop         kill any running flasher
+#
+# Reuses /color's mechanism: Claude Code's Bash tool has no controlling tty, so
+# OSC escapes on stdout never reach the terminal. Instead we locate the `claude`
+# process's own pty by walking process ancestry and write the OSC tab-tint code
+# straight to /dev/ttysNNN. OSC color codes are non-destructive (no cursor moves)
+# so writing them — even repeatedly from a background flasher — is glitch-safe.
+set -u
+
+MODE="${1:-}"
+
+# Streamline / lite mode — skip cosmetic tab-state painting fleet-wide, but
+# always allow 'stop' so a running flasher can still be cleaned up. rm to restore.
+case "$MODE" in stop) : ;; *) [ -f "$HOME/.claude/hooks-lite" ] && exit 0 ;; esac
+
+PIDFILE="${HOME}/.claude/tab-flash.pid"
+
+# Walk ancestry from $$ to find the controlling ttys* (same as color.sh).
+resolve_tty() {
+  local p=$$ line pp tt
+  for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
+    line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+    # shellcheck disable=SC2086
+    set -- $line; pp="${1:-}"; tt="${2:-}"
+    [[ -z "$pp" ]] && break
+    if [[ "$tt" == ttys* ]]; then echo "/dev/$tt"; return 0; fi
+    p="$pp"
+  done
+  return 1
+}
+
+# write_color <tty> <R> <G> <B>  — tint the iTerm2 tab (tmux-wrapped if needed)
+write_color() {
+  local tty="$1" R="$2" G="$3" B="$4"
+  [[ -w "$tty" ]] || return 1
+  if [[ -n "${TMUX:-}" ]]; then
+    { printf '\033Ptmux;\033\033]6;1;bg;red;brightness;%d\007\033\\'   "$R"
+      printf '\033Ptmux;\033\033]6;1;bg;green;brightness;%d\007\033\\' "$G"
+      printf '\033Ptmux;\033\033]6;1;bg;blue;brightness;%d\007\033\\'  "$B"
+    } > "$tty" 2>/dev/null
+  else
+    { printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+      printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+      printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    } > "$tty" 2>/dev/null
+  fi
+}
+
+# reset_color <tty> — clear the tab tint back to iTerm2 default (the "off" half
+# of a blink)
+reset_color() {
+  local tty="$1"
+  [[ -w "$tty" ]] || return 1
+  if [[ -n "${TMUX:-}" ]]; then
+    printf '\033Ptmux;\033\033]6;1;bg;*;default\007\033\\' > "$tty" 2>/dev/null
+  else
+    printf '\033]6;1;bg;*;default\007' > "$tty" 2>/dev/null
+  fi
+}
+
+stop_flasher() {
+  if [[ -f "$PIDFILE" ]]; then
+    kill "$(cat "$PIDFILE" 2>/dev/null)" 2>/dev/null || true
+    rm -f "$PIDFILE"
+  fi
+}
+
+RED=(255 0 0)
+GREEN=(0 200 0)
+PURPLE=(148 0 211)
+
+case "$MODE" in
+  red)
+    stop_flasher
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    write_color "$tty" "${RED[@]}" && echo "tab → solid RED (closing)"
+    ;;
+  green)
+    stop_flasher
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    write_color "$tty" "${GREEN[@]}" && echo "tab → solid GREEN (done)"
+    ;;
+  purple)
+    stop_flasher
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    write_color "$tty" "${PURPLE[@]}" && echo "tab → solid PURPLE (stopped, waiting on Steve)"
+    ;;
+  flash-red)
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    stop_flasher
+    # Resolve tty in the parent, then hand it to the detached loop — once
+    # backgrounded the loop may be reparented and lose the ancestry walk.
+    ( trap 'exit 0' TERM INT
+      while :; do
+        printf '\033]6;1;bg;red;brightness;255\007\033]6;1;bg;green;brightness;0\007\033]6;1;bg;blue;brightness;0\007' > "$tty" 2>/dev/null
+        sleep 0.5
+        printf '\033]6;1;bg;*;default\007' > "$tty" 2>/dev/null
+        sleep 0.5
+      done ) >/dev/null 2>&1 &
+    echo $! > "$PIDFILE"
+    disown 2>/dev/null || true
+    echo "tab → FLASHING RED (compacting) pid $(cat "$PIDFILE")"
+    ;;
+  flash-orange)
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    stop_flasher
+    # STOPPED, waiting on a background result. Blink orange (255,140,0) until
+    # something kills the flasher (a `stop`/`green`/`red`, or work resumes).
+    ( trap 'exit 0' TERM INT
+      while :; do
+        printf '\033]6;1;bg;red;brightness;255\007\033]6;1;bg;green;brightness;140\007\033]6;1;bg;blue;brightness;0\007' > "$tty" 2>/dev/null
+        sleep 0.5
+        printf '\033]6;1;bg;*;default\007' > "$tty" 2>/dev/null
+        sleep 0.5
+      done ) >/dev/null 2>&1 &
+    echo $! > "$PIDFILE"
+    disown 2>/dev/null || true
+    echo "tab → FLASHING ORANGE (stopped, waiting on background result) pid $(cat "$PIDFILE")"
+    ;;
+  flash-purple|waiting)
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    stop_flasher
+    # STOPPED & WAITING ON STEVE — the turn was handed back and Claude is idle
+    # awaiting input. Blink purple (148,0,211) until something kills the flasher
+    # (a `stop`/`green`/`red`, or the next UserPromptSubmit when Steve replies).
+    # Resolve tty in the parent, then hand it to the detached loop — once
+    # backgrounded the loop may be reparented and lose the ancestry walk.
+    ( trap 'exit 0' TERM INT
+      while :; do
+        printf '\033]6;1;bg;red;brightness;148\007\033]6;1;bg;green;brightness;0\007\033]6;1;bg;blue;brightness;211\007' > "$tty" 2>/dev/null
+        sleep 0.5
+        printf '\033]6;1;bg;*;default\007' > "$tty" 2>/dev/null
+        sleep 0.5
+      done ) >/dev/null 2>&1 &
+    echo $! > "$PIDFILE"
+    disown 2>/dev/null || true
+    echo "tab → FLASHING PURPLE (stopped, waiting on Steve) pid $(cat "$PIDFILE")"
+    ;;
+  flash-green)
+    stop_flasher
+    tty="$(resolve_tty)" || { echo "tab-state: no pty found"; exit 1; }
+    for _ in 1 2 3; do
+      write_color "$tty" "${GREEN[@]}"; sleep 0.3
+      reset_color "$tty"; sleep 0.3
+    done
+    write_color "$tty" "${GREEN[@]}"
+    echo "tab → GREEN flash ×3 then solid (compaction complete)"
+    ;;
+  stop)
+    stop_flasher
+    echo "tab-state: flasher stopped"
+    ;;
+  *)
+    echo "usage: tab-state.sh {red|green|purple|flash-red|flash-orange|flash-purple|waiting|flash-green|stop}"
+    exit 2
+    ;;
+esac
diff --git a/verification/install/before/.claude/skills/color/scripts/working-state.sh b/verification/install/before/.claude/skills/color/scripts/working-state.sh
new file mode 100644
index 0000000..4de7bb0
--- /dev/null
+++ b/verification/install/before/.claude/skills/color/scripts/working-state.sh
@@ -0,0 +1,219 @@
+#!/usr/bin/env bash
+# working-state.sh — "Claude is working" indicator in the iTerm2 window HEADER.
+#
+#   on   (UserPromptSubmit hook)  prepend a green-square bar to the header title
+#                                 and start a watcher that GROWS the bar and
+#                                 HEATS it (🟩 → 🟨 → 🟧 → 🟥) the longer the
+#                                 turn runs.
+#   off  (Stop hook)              stop the watcher, restore the plain topic title
+#
+# Why the header TITLE TEXT (not tab tint / OSC 9;4 progress): tested live on
+# iTerm2 3.6.10 — it ignores the OSC 9;4 progress bar AND won't background-tint
+# the title bar on single-tab windows. But the title TEXT always renders in the
+# header. So the indicator is a row of colored squares prepended to the title:
+# a growing, heating "progress bar + icon" that's visible no matter the layout.
+#
+# Bash tool has no controlling tty, but HOOKS run in the terminal's context, so
+# the ancestry walk (same as color-title.sh) resolves the pty here.
+set -u
+
+# --- tunables: elapsed seconds → square color + count -----------------------
+T_YELLOW=60     # after this many seconds the bar turns yellow
+T_ORANGE=120    # then orange
+T_RED=180       # then red
+RAMP_STEP=3     # re-paint cadence (s) while working
+IDLE_STEP="${IDLE_BLINK_SEC:-0.7}"   # blink cadence (s) while idle/waiting
+
+MODE="${1:-}"
+
+# Streamline / lite mode — one flag makes the working-state indicator no-op
+# fleet-wide (heavy load, autonomous officer-yolo loops, headless jobs). This
+# skips the per-hook pty resolution (ps eww over every claude proc) + the
+# repaint watcher loop — the O(sessions^2) cost that bogs down under many
+# parallel sessions. Existing watchers self-terminate via their owner_pid check.
+# Reversible: rm ~/.claude/hooks-lite. Internal watcher modes are exempt so a
+# running loop can still be told to exit cleanly.
+case "$MODE" in _watch|_idle_watch) : ;; *) [ -f "$HOME/.claude/hooks-lite" ] && exit 0 ;; esac
+
+TITLE_DIR="${HOME}/.claude/color-session-titles"
+RUN_DIR="${HOME}/.claude/working-state"
+mkdir -p "$RUN_DIR"
+
+# bar string for a given elapsed time: heat + growing count
+bar_for() {  # echoes the square bar
+  local e="$1" sq n i out=""
+  if   (( e < T_YELLOW )); then sq="🟩"; n=$(( e/20 + 1 )); (( n>3 )) && n=3
+  elif (( e < T_ORANGE )); then sq="🟨"; n=4
+  elif (( e < T_RED    )); then sq="🟧"; n=5
+  else                          sq="🟥"; n=6
+  fi
+  for ((i=0;i<n;i++)); do out+="$sq"; done
+  printf '%s' "$out"
+}
+
+# ---------------------------------------------------------------------------
+# internal watcher: _watch <tty> <inside_tmux> <session_id>
+# re-reads the topic file each tick so it picks up color-title.sh's title
+# ---------------------------------------------------------------------------
+if [[ "$MODE" == "_watch" ]]; then
+  tty_path="$2"; inside_tmux="$3"; sid="$4"; owner_pid="${5:-}"
+  set_title() {
+    if (( inside_tmux )); then
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+    else
+      printf '\033]1;%s\007' "$1" > "$tty_path" 2>/dev/null
+      printf '\033]2;%s\007' "$1" > "$tty_path" 2>/dev/null
+    fi
+  }
+  trap 'exit 0' TERM INT
+  elapsed=0
+  while :; do
+    # stop the moment our session dies or the pty is gone — never paint a
+    # REUSED tty (the leaked-watcher prompt-corruption bug).
+    [[ -n "$owner_pid" ]] && ! kill -0 "$owner_pid" 2>/dev/null && exit 0
+    [[ -w "$tty_path" ]] || exit 0
+    topic=""; [[ -f "${TITLE_DIR}/${sid}" ]] && topic="$(cat "${TITLE_DIR}/${sid}" 2>/dev/null)"
+    bar="$(bar_for "$elapsed")"
+    if [[ -n "$topic" ]]; then set_title "${bar} ${topic}"; else set_title "${bar} working…"; fi
+    sleep "$RAMP_STEP"
+    elapsed=$(( elapsed + RAMP_STEP ))
+  done
+fi
+
+# ---------------------------------------------------------------------------
+# internal idle watcher: _idle_watch <tty> <inside_tmux> <session_id>
+# Blinks PURPLE squares in the title while the turn is handed back to Steve.
+# Pulses 🟪🟪🟪 <-> 🟪 so there is ALWAYS at least one purple dot — it blinks
+# but never goes blank. Re-reads the topic each tick like the working watcher.
+# ---------------------------------------------------------------------------
+if [[ "$MODE" == "_idle_watch" ]]; then
+  tty_path="$2"; inside_tmux="$3"; sid="$4"; owner_pid="${5:-}"
+  set_title() {
+    if (( inside_tmux )); then
+      printf '\033Ptmux;\033\033]1;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+      printf '\033Ptmux;\033\033]2;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+    else
+      printf '\033]1;%s\007' "$1" > "$tty_path" 2>/dev/null
+      printf '\033]2;%s\007' "$1" > "$tty_path" 2>/dev/null
+    fi
+  }
+  trap 'exit 0' TERM INT
+  phase=0
+  while :; do
+    # stop the moment our session dies or the pty is gone — never paint a
+    # REUSED tty (the leaked-watcher prompt-corruption bug).
+    [[ -n "$owner_pid" ]] && ! kill -0 "$owner_pid" 2>/dev/null && exit 0
+    [[ -w "$tty_path" ]] || exit 0
+    topic=""; [[ -f "${TITLE_DIR}/${sid}" ]] && topic="$(cat "${TITLE_DIR}/${sid}" 2>/dev/null)"
+    if (( phase == 0 )); then bar="🟪🟪🟪"; else bar="🟪"; fi
+    if [[ -n "$topic" ]]; then set_title "${bar} ${topic}"; else set_title "${bar} idle…"; fi
+    phase=$(( 1 - phase ))
+    sleep "$IDLE_STEP"
+  done
+fi
+
+# --- main: resolve session + pty -------------------------------------------
+input="$(cat 2>/dev/null || true)"
+session_id="$(printf '%s' "$input" | sed -n 's/.*"session_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)"
+[[ -z "$session_id" ]] && session_id="default"
+pidfile="${RUN_DIR}/${session_id}.watcher.pid"
+
+# Resolve THIS session's OWN tab pty.
+# PRIMARY: match the stable w#t#p# slot prefix of ITERM_SESSION_ID against each
+# live `claude` process's env. The hook inherits ITERM_SESSION_ID from its claude
+# parent, so this pins the correct tab even across many parallel sessions.
+# The old ancestry walk landed on a NEIGHBOR tab (every session painted someone
+# else's tab) — kept only as a fallback when there's no ITERM_SESSION_ID.
+tty_path=""
+owner_pid=""
+if [[ -n "${ITERM_SESSION_ID:-}" ]]; then
+  _slot="${ITERM_SESSION_ID%%:*}"
+  for _cpid in $(pgrep -x claude 2>/dev/null); do
+    _csid="$(ps eww -p "$_cpid" 2>/dev/null | grep -o 'ITERM_SESSION_ID=[^ ]*' | head -1 | cut -d= -f2-)"
+    if [[ "${_csid%%:*}" == "$_slot" ]]; then
+      _ctty="$(ps -o tty= -p "$_cpid" 2>/dev/null | tr -d ' ')"
+      if [[ "$_ctty" == ttys* ]]; then tty_path="/dev/$_ctty"; owner_pid="$_cpid"; fi
+    fi
+  done
+fi
+if [[ -z "$tty_path" ]]; then
+  p=$$
+  for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
+    line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+    # shellcheck disable=SC2086
+    set -- $line; pp="${1:-}"; tt="${2:-}"
+    [[ -z "$pp" ]] && break
+    if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+    p="$pp"
+  done
+fi
+[[ -n "$tty_path" && -w "$tty_path" ]] || exit 0
+
+inside_tmux=0
+[[ -n "${TMUX:-}" ]] && inside_tmux=1
+
+set_title() {
+  if (( inside_tmux )); then
+    printf '\033Ptmux;\033\033]1;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+    printf '\033Ptmux;\033\033]2;%s\007\033\\' "$1" > "$tty_path" 2>/dev/null
+  else
+    printf '\033]1;%s\007' "$1" > "$tty_path" 2>/dev/null
+    printf '\033]2;%s\007' "$1" > "$tty_path" 2>/dev/null
+  fi
+}
+
+stop_watcher() {
+  [[ -f "$pidfile" ]] || return 0
+  kill "$(cat "$pidfile" 2>/dev/null)" 2>/dev/null || true
+  rm -f "$pidfile" "${pidfile}.owner"
+}
+
+# Reap watchers left behind by DEAD sessions: any pidfile whose recorded OWNER
+# claude PID is gone gets its watcher killed + files removed. Clears leaked
+# painters that survive a crash/kill-9 (no clean Stop hook) and would otherwise
+# keep writing OSC title escapes to a REUSED pty. Only reaps when the owner is
+# recorded AND confirmed dead — never guesses at unknown-owner (fallback-path)
+# watchers, which self-terminate via their own per-tick tty-writable guard.
+reap_orphans() {
+  local pf owner wpid
+  for pf in "${RUN_DIR}"/*.watcher.pid; do
+    [[ -e "$pf" ]] || continue
+    [[ "$pf" == "$pidfile" ]] && continue
+    owner=""; [[ -f "${pf}.owner" ]] && owner="$(cat "${pf}.owner" 2>/dev/null)"
+    [[ -z "$owner" ]] && continue
+    kill -0 "$owner" 2>/dev/null && continue   # owner alive → legit, leave it
+    wpid="$(cat "$pf" 2>/dev/null)"
+    [[ -n "$wpid" ]] && kill "$wpid" 2>/dev/null || true
+    rm -f "$pf" "${pf}.owner"
+  done
+}
+
+topic=""; [[ -f "${TITLE_DIR}/${session_id}" ]] && topic="$(cat "${TITLE_DIR}/${session_id}" 2>/dev/null)"
+
+case "$MODE" in
+  on)
+    stop_watcher
+    reap_orphans
+    set_title "🟩 ${topic:-working…}"
+    ( "$0" _watch "$tty_path" "$inside_tmux" "$session_id" "$owner_pid" ) >/dev/null 2>&1 &
+    echo $! > "$pidfile"
+    [[ -n "$owner_pid" ]] && echo "$owner_pid" > "${pidfile}.owner"
+    disown 2>/dev/null || true
+    ;;
+  off)
+    stop_watcher
+    reap_orphans
+    # idle/waiting: blink PURPLE dots in the title (never plain) until next turn
+    set_title "🟪🟪🟪 ${topic:-idle…}"
+    ( "$0" _idle_watch "$tty_path" "$inside_tmux" "$session_id" "$owner_pid" ) >/dev/null 2>&1 &
+    echo $! > "$pidfile"
+    [[ -n "$owner_pid" ]] && echo "$owner_pid" > "${pidfile}.owner"
+    disown 2>/dev/null || true
+    ;;
+  *)
+    echo "usage: working-state.sh {on|off}" >&2
+    exit 2
+    ;;
+esac
+exit 0
diff --git a/verification/install/before/.claude/skills/colordots/paint-lib.sh b/verification/install/before/.claude/skills/colordots/paint-lib.sh
new file mode 100644
index 0000000..0ed9d5e
--- /dev/null
+++ b/verification/install/before/.claude/skills/colordots/paint-lib.sh
@@ -0,0 +1,75 @@
+#!/usr/bin/env bash
+# paint-lib.sh — the ONE safe path for tinting an iTerm2 tab via an OSC write.
+#
+# Fixes the 2026-09-08 "dot payload lands in the Claude input field" bug. Two proven
+# root causes (reproduced in a throwaway iTerm window, not on a live pane):
+#
+#   1. SUBAGENT CROSS-WRITE. The Claude Bash tool runs with no controlling tty, so the
+#      old ancestry walk climbed past the tty-less `zsh -c` shells to the OWNING `claude`
+#      process's ttysNNN. From the top-level session that resolves to the right pane, but
+#      from a SPAWNED SUBAGENT (Agent tool) it resolves to the PARENT session's pty — so a
+#      subagent painting a dot wrote straight into Steve's live pane. A subagent has no tab
+#      of its own; it must never paint. (CLAUDE_CODE_CHILD_SESSION=1 marks a subagent.)
+#
+#   2. BACKGROUND PULSE LOOP. The dot painters started a background loop that rewrote the
+#      tab-color OSC every ~0.11s forever. Those rapid async writes interleave with the
+#      Claude TUI's own pty output; a torn/desynced sequence drops its leading ESC]6;
+#      framing and iTerm renders the tail ("1;bg;green;brightness;0") as literal text on the
+#      Claude screen — exactly the reported symptom. A SINGLE atomic write can't be torn.
+#
+# A clean single OSC write was verified to set the tab color AND to never enter a
+# raw-mode reader's stdin — so painting once, atomically, is both correct and leak-free.
+
+TABDOTS_DIR="${HOME}/.claude/tab-dots"
+
+# True (0) iff running inside a spawned subagent — MUST NOT paint (no tab of its own;
+# any write lands in the parent session's live pane).
+tabdots_is_subagent() { [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; }
+
+# Resolve THIS session's controlling pty by climbing ancestry to the owning `claude`
+# process's ttys. Prints "/dev/ttysNNN" on success. Returns non-zero (and prints nothing)
+# when called from a subagent or when no ttys is found — callers must treat that as "do not paint".
+tabdots_resolve_tty() {
+  tabdots_is_subagent && return 1
+  local p=$$ line pp tt
+  for _ in $(seq 1 12); do
+    line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+    # shellcheck disable=SC2086
+    set -- $line; pp="${1:-}"; tt="${2:-}"
+    [ -z "$pp" ] && break
+    case "$tt" in ttys*) printf '/dev/%s' "$tt"; return 0;; esac
+    p="$pp"
+  done
+  return 1
+}
+
+# Kill any legacy background pulse loop on a tty (the old corruption engine) and drop its pidfile.
+tabdots_stop_pulse() {
+  local tp="$1" pf
+  pf="${TABDOTS_DIR}/$(basename "$tp").pulse.pid"
+  [ -f "$pf" ] && { kill "$(cat "$pf" 2>/dev/null)" 2>/dev/null; rm -f "$pf"; }
+  return 0
+}
+
+# Paint a tab in ONE atomic write() so a concurrent writer can never tear it.
+#   $1 tty_path   $2 R   $3 G   $4 B   $5 title   $6 off(1=reset)
+# No background loop — solid color only. Refuses in a subagent context.
+tabdots_paint() {
+  local tp="$1" R="$2" G="$3" B="$4" title="$5" off="${6:-0}"
+  tabdots_is_subagent && return 2
+  [ -w "$tp" ] || return 1
+  tabdots_stop_pulse "$tp"
+  if [ "$off" = "1" ]; then
+    # Single write: reset tab color + clear badge.
+    printf '\033]6;1;bg;*;default\007\033]1337;SetBadgeFormat=\a' > "$tp" 2>/dev/null
+    rm -f "${TABDOTS_DIR}/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  local badge; badge="$(printf '%s' "$title" | base64 | tr -d '\n')"
+  # ONE write(): 3 tab-color OSCs + both title channels + the in-window badge, concatenated.
+  printf '\033]6;1;bg;red;brightness;%d\007\033]6;1;bg;green;brightness;%d\007\033]6;1;bg;blue;brightness;%d\007\033]1;%s\007\033]2;%s\007\033]1337;SetBadgeFormat=%s\a' \
+    "$R" "$G" "$B" "$title" "$title" "$badge" > "$tp" 2>/dev/null
+  mkdir -p "$TABDOTS_DIR"
+  printf '%s' "$title" > "${TABDOTS_DIR}/$(basename "$tp").dot"
+  return 0
+}
diff --git a/verification/install/before/.claude/skills/dot/current.sh b/verification/install/before/.claude/skills/dot/current.sh
new file mode 100644
index 0000000..b0085d5
--- /dev/null
+++ b/verification/install/before/.claude/skills/dot/current.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+# /dot helper — print THIS session's controlling tty + its current persisted dot label,
+# so the /dot review can report "was: <X>" and avoid a no-op re-apply.
+# Resolves the tty the same way the dot scripts do (walk process ancestry to the ttysNNN),
+# because the Claude Bash tool has no controlling tty of its own.
+set -u
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then
+  echo "(none)  — could not resolve this session's tty"; exit 0
+fi
+base="$(basename "$tty_path")"
+dotfile="${HOME}/.claude/tab-dots/${base}.dot"
+if [[ -f "$dotfile" ]]; then
+  printf '%s  %s\n' "$base" "$(cat "$dotfile")"
+else
+  printf '%s  (none)\n' "$base"
+fi
diff --git a/verification/install/before/.claude/skills/greendot/greendot.sh b/verification/install/before/.claude/skills/greendot/greendot.sh
new file mode 100644
index 0000000..289f34d
--- /dev/null
+++ b/verification/install/before/.claude/skills/greendot/greendot.sh
@@ -0,0 +1,61 @@
+#!/usr/bin/env bash
+# /greendot — mark a Claude session's iTerm2 tab GREEN = "WORKING — running or monitoring, nothing needed from Steve".
+# A fixed SEMANTIC color (not /color's rotating golden-angle hue): green always means
+# "this session is WORKING or monitoring — nothing is needed from Steve." The default resting state.
+# Steve, 2026-09-02: every window always shows exactly one dot; green closes the "no dot" gap.
+# Sibling of /yellowdot (needs direction), /purpledot (gated memo), /orangedot (paste in terminal),
+# /pinkdot (parked).
+#
+# Mechanism (shared with all dot painters via paint-lib.sh): the Claude Bash tool runs with NO
+# controlling tty, so OSC escapes on stdout never reach the terminal. We resolve the OWNING `claude`
+# process's pty by ancestry and write the OSC straight to /dev/ttysNNN — in a SINGLE atomic write so
+# it can never be torn mid-sequence. Two hard safety rails (see paint-lib.sh header for the proven
+# root cause): (1) refuse to paint from a subagent — a subagent's ancestry resolves to the PARENT
+# session's pty, so painting would corrupt Steve's live pane; (2) no background pulse loop — the old
+# rapid-fire loop is what leaked the OSC payload ("1;bg;green;brightness;0") into the Claude input.
+#
+# Usage:
+#   greendot.sh                 # green-dot THIS session (title "🟢 WORKING")
+#   greendot.sh "TK-11098 x3"   # green tint + custom title text (🟢 prefixed) — put the TK + count
+#   greendot.sh --all           # green-dot EVERY live claude tab (rare)
+#   greendot.sh --off           # clear the tint on THIS session
+set -u
+
+source "${HOME}/.claude/skills/colordots/paint-lib.sh"
+
+# HARD RAIL: a subagent has no tab of its own; painting would write to the parent session's
+# live pane (the input-field-pollution bug). Refuse and no-op.
+if tabdots_is_subagent; then
+  echo "/greendot → subagent context (CLAUDE_CODE_CHILD_SESSION set) — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Semantic green — "working / monitoring, nothing needed".
+R=0; G=200; B=83
+DOT="🟢"
+
+# --all: paint every live claude tab green (one atomic write each; no pulse loop).
+if [ "${1:-}" = "--all" ]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [ -z "$ttys" ] && { echo "/greendot --all → no live claude ttys"; exit 0; }
+  n=0
+  for tt in $ttys; do tabdots_paint "/dev/$tt" "$R" "$G" "$B" "$DOT WORKING" 0 && { echo "  $tt → 🟢 WORKING"; n=$((n+1)); }; done
+  echo "/greendot --all → marked ${n} tab(s) green (working)"
+  exit 0
+fi
+
+OFF=0; LABEL=""
+[ "${1:-}" = "--off" ] && OFF=1 || LABEL="${1:-}"
+
+tty_path="$(tabdots_resolve_tty)" || {
+  echo "/greendot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
+}
+
+if [ "$OFF" = "1" ]; then
+  tabdots_paint "$tty_path" "$R" "$G" "$B" "" 1 && echo "/greendot --off → cleared tint on $tty_path"
+else
+  title="$DOT ${LABEL:-WORKING}"
+  tabdots_paint "$tty_path" "$R" "$G" "$B" "$title" 0 \
+    && echo "/greendot → $tty_path 🟢 ${LABEL:-WORKING}  rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/skills/lightgreendot/lightgreendot.sh b/verification/install/before/.claude/skills/lightgreendot/lightgreendot.sh
new file mode 100644
index 0000000..def2005
--- /dev/null
+++ b/verification/install/before/.claude/skills/lightgreendot/lightgreendot.sh
@@ -0,0 +1,72 @@
+#!/usr/bin/env bash
+# /lightgreendot — mark a Claude session's iTerm2 tab LIGHT GREEN = "MONITORING with a SCHEDULED
+# next-check / DEADLINE". A shade-variant of /greendot (Steve, 2026-09-04): plain green = working/
+# monitoring with no specific wake time; LIGHT green = working/monitoring AND a wake is scheduled at
+# a known time (a ScheduleWakeup / next-monitor). So Steve can glance and see "this one has a deadline".
+# Pair with showing the next-check time 3x inline in the reply (memory: monitor-response-dot-and-3x-time).
+#
+# Mechanism identical to greendot.sh (resolve own pty by ancestry, write OSC straight to /dev/ttysNNN).
+# Usage:
+#   lightgreendot.sh "TK-NNNNN · monitoring · next 10:15"   # light-green tint + title (put the time!)
+#   lightgreendot.sh --all
+#   lightgreendot.sh --off
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/lightgreendot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Teal green — "monitoring with a scheduled deadline". Distinct from plain green (0,200,83).
+R=0; G=150; B=136
+DOT="🟢"
+
+paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
+  local tp="$1" label="${2:-}" off="${3:-0}"
+  [[ -w "$tp" ]] || return 1
+  if [[ "$off" == "1" ]]; then
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
+    rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  local title="$DOT ${label:-MONITORING}"
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tp" 2>/dev/null
+  mkdir -p "${HOME}/.claude/tab-dots"
+  printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
+}
+
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/lightgreendot --all → no live claude ttys"; exit 0; }
+  n=0; for tt in $ttys; do paint "/dev/$tt" "MONITORING" 0 && { echo "  $tt → 🟢 MONITORING"; n=$((n+1)); }; done
+  echo "/lightgreendot --all → marked ${n} tab(s) light green"; exit 0
+fi
+
+OFF=0; LABEL=""
+[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
+
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then echo "/lightgreendot → could not resolve this session's tty"; exit 1; fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/lightgreendot --off → cleared tint on $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/lightgreendot → $tty_path 🟢(light) ${LABEL:-MONITORING}  rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/skills/orangedot/orangedot.sh b/verification/install/before/.claude/skills/orangedot/orangedot.sh
new file mode 100644
index 0000000..78e332a
--- /dev/null
+++ b/verification/install/before/.claude/skills/orangedot/orangedot.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# /orangedot — mark a Claude session's iTerm2 tab ORANGE = "PASTE WAITING — Steve action required".
+# A fixed SEMANTIC color (not /color's rotating golden-angle hue): orange always means
+# "this session has handed Steve a `!` paste (or another one-step approval) and is waiting on it."
+# Steve, 2026-09-02: "when we see pastes or paste this: use one of the color dots to notify terminal".
+# Sibling of /pinkdot (pink = parked/idle) and /yellowdot (legacy idle). Lets Steve scan a wall of
+# tabs and see exactly which windows are blocked on HIM (orange) vs parked (pink) vs working (/color).
+#
+# Mechanism (identical to pinkdot.sh / color.sh): the Claude Bash tool runs with NO controlling
+# tty, so OSC escapes on stdout never reach the terminal. We resolve the `claude` process's own
+# pty by walking process ancestry and write the OSC straight to /dev/ttysNNN. OSC color/title
+# codes are non-destructive (no cursor moves) so this is glitch-safe.
+#
+# Usage:
+#   orangedot.sh                 # orange-dot THIS session (title "🟠 PASTE waiting")
+#   orangedot.sh "TK-11098 x3"   # orange tint + custom title text (🟠 prefixed) — put the TK + count
+#   orangedot.sh --all           # orange-dot EVERY live claude tab (rare; fleet-wide "all blocked on Steve")
+#   orangedot.sh --off           # clear the tint on THIS session (paste was run → back to /color or /pinkdot)
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/orangedot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Semantic orange — "action required from Steve". Distinct from pink (parked) and /color hues.
+R=255; G=140; B=0
+DOT="🟠"
+# Smooth pulsate helper — ACTIVE state, so we pulse this color (Steve, 2026-09-04:
+# "Pulsate the color whenever claude or codex is waiting for me or just running").
+# Best-effort: if it's missing or the tty isn't writable, painting the solid color still succeeds.
+PULSE="${HOME}/.claude/tab-dots/pulse.sh"
+
+paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
+  local tp="$1" label="${2:-}" off="${3:-0}"
+  [[ -w "$tp" ]] || return 1
+  if [[ "$off" == "1" ]]; then
+    # Stop any active pulse FIRST (before the reset) so the pulse loop can't repaint after we clear.
+    [[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
+    rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  local title="$DOT ${label:-PASTE waiting}"
+  # NOTE: no leading '*;default' reset — on iTerm2 it can swallow the color set (pinkdot bug,
+  # 2026-09-02). Set RGB directly and write BOTH title channels (]1; tab, ]2; window).
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tp" 2>/dev/null
+  # Persist the dot so the 60s tab-ticket repainter PREFIXES it into the window title
+  # (visible even when the tab bar is hidden), and show an in-window iTerm2 BADGE.
+  mkdir -p "${HOME}/.claude/tab-dots"
+  printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
+  # ACTIVE state → start a smooth pulse of this same color (pulse.sh's per-tty pidfile replaces
+  # any existing pulse on this tty, so pulses never stack). Best-effort, never fatal.
+  [[ -f "$PULSE" ]] && bash "$PULSE" "$R" "$G" "$B" "$tp" >/dev/null 2>&1 || true
+}
+
+# --all: paint every live claude tab orange
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/orangedot --all → no live claude ttys"; exit 0; }
+  n=0
+  for tt in $ttys; do paint "/dev/$tt" "PASTE waiting" 0 && { echo "  $tt → 🟠 PASTE waiting"; n=$((n+1)); }; done
+  echo "/orangedot --all → marked ${n} tab(s) orange (paste waiting on Steve)"
+  exit 0
+fi
+
+OFF=0; LABEL=""
+[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
+
+# Resolve THIS session's controlling pty by walking ancestry (same as color.sh).
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then
+  echo "/orangedot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
+fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/orangedot --off → cleared tint on $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/orangedot → $tty_path 🟠 ${LABEL:-PASTE waiting}  rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/skills/pinkdot/pinkdot.sh b/verification/install/before/.claude/skills/pinkdot/pinkdot.sh
new file mode 100644
index 0000000..f9bc796
--- /dev/null
+++ b/verification/install/before/.claude/skills/pinkdot/pinkdot.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# /pinkdot — mark a Claude session's iTerm2 tab PINK = "idle / parked / nothing left to progress".
+# A fixed SEMANTIC color (not /color's rotating golden-angle hue): pink always means
+# "this session finished its work and is holding." Supersedes /yellowdot as the parked marker
+# (Steve, 2026-09-02: "make parked terminals show pink dot"). Lets Steve see at a glance, across
+# a wall of tabs, which sessions are done/parked (pink) vs actively working (their /color tint).
+#
+# Mechanism (identical to color.sh / yellowdot.sh): the Claude Bash tool runs with NO controlling
+# tty, so OSC escapes on stdout never reach the terminal. We resolve the `claude` process's own
+# pty by walking process ancestry and write the OSC straight to /dev/ttysNNN. OSC color/title
+# codes are non-destructive (no cursor moves) so this is glitch-safe.
+#
+# Usage:
+#   pinkdot.sh              # pink-dot THIS session (title "🩷 idle")
+#   pinkdot.sh "label"      # pink tint + custom title text (🩷 prefixed)
+#   pinkdot.sh --all        # pink-dot EVERY live claude tab (mark whole fleet parked)
+#   pinkdot.sh --off        # clear the tint back to default on THIS session
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/pinkdot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Semantic pink (hot pink) — reads clearly as "idle/parked" and is distinct from /color hues.
+R=255; G=105; B=180
+DOT="🩷"
+# PARKED state stays SOLID — pinkdot never STARTS a pulse. But it must KILL any active-state
+# pulse (green/orange/yellow/purple) inherited on this tty so parking reads as a steady dot
+# (Steve, 2026-09-04). Best-effort: missing helper / unwritable tty never blocks the paint.
+PULSE="${HOME}/.claude/tab-dots/pulse.sh"
+
+paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
+  local tp="$1" label="${2:-}" off="${3:-0}"
+  [[ -w "$tp" ]] || return 1
+  if [[ "$off" == "1" ]]; then
+    # Stop any active pulse FIRST (before the reset) so the pulse loop can't repaint after we clear.
+    [[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
+    rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  # Parking → kill any active-state pulse on this tty BEFORE setting the solid color, so nothing
+  # repaints over the steady pink. pinkdot stays SOLID and never starts a pulse of its own.
+  [[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
+  local title="$DOT ${label:-idle}"
+  # NOTE: do NOT send a leading '*;default' reset here — on iTerm2 it can leave the tab
+  # at default and swallow the color set (bug found 2026-09-02). Set RGB directly and write
+  # BOTH title channels (]1; icon/tab, ]2; window) exactly like the proven /color path.
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tp" 2>/dev/null
+  # Persist the dot so the 60s tab-ticket repainter PREFIXES it into the window title
+  # (visible even when the tab bar is hidden), and show an in-window iTerm2 BADGE.
+  mkdir -p "${HOME}/.claude/tab-dots"
+  printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
+}
+
+# --all: paint every live claude tab pink
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/pinkdot --all → no live claude ttys"; exit 0; }
+  n=0
+  for tt in $ttys; do paint "/dev/$tt" "idle" 0 && { echo "  $tt → 🩷 idle"; n=$((n+1)); }; done
+  echo "/pinkdot --all → marked ${n} tab(s) pink (parked)"
+  exit 0
+fi
+
+OFF=0; LABEL=""
+[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
+
+# Resolve THIS session's controlling pty by walking ancestry (same as color.sh).
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then
+  echo "/pinkdot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
+fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/pinkdot --off → cleared tint on $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/pinkdot → $tty_path 🩷 ${LABEL:-idle}  rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/skills/purpledot/purpledot.sh b/verification/install/before/.claude/skills/purpledot/purpledot.sh
new file mode 100644
index 0000000..0267f89
--- /dev/null
+++ b/verification/install/before/.claude/skills/purpledot/purpledot.sh
@@ -0,0 +1,98 @@
+#!/usr/bin/env bash
+# /purpledot — mark a Claude session's iTerm2 tab PURPLE = "GATED — memo drafted to pending-approval, awaiting Steve's approve/reject in the viewer".
+# A fixed SEMANTIC color (not /color's rotating golden-angle hue): purple always means
+# "this session's remaining work is a gated action drafted to ~/.claude/yolo-queue/pending-approval/ and it waits on the viewer decision."
+# Steve, 2026-09-02: "and a color for gated" (companion to /orangedot = a direct ! paste in the terminal).
+# Sibling of /orangedot (orange = paste in terminal), /pinkdot (pink = parked). Lets Steve scan a wall of
+# tabs and see which windows wait on a VIEWER decision (purple) vs a terminal paste (orange) vs parked (pink) vs working (/color).
+#
+# Mechanism (identical to pinkdot.sh / color.sh): the Claude Bash tool runs with NO controlling
+# tty, so OSC escapes on stdout never reach the terminal. We resolve the `claude` process's own
+# pty by walking process ancestry and write the OSC straight to /dev/ttysNNN. OSC color/title
+# codes are non-destructive (no cursor moves) so this is glitch-safe.
+#
+# Usage:
+#   purpledot.sh                 # purple-dot THIS session (title "🟣 GATED")
+#   purpledot.sh "TK-11098 x3"   # purple tint + custom title text (🟣 prefixed) — put the TK + count
+#   purpledot.sh --all           # purple-dot EVERY live claude tab (rare; fleet-wide "all gated")
+#   purpledot.sh --off           # clear the tint on THIS session (memo approved/rejected → back to /color or /pinkdot)
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/purpledot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Semantic purple — "gated, awaiting viewer decision". Distinct from orange (paste), pink (parked), /color hues.
+R=148; G=0; B=211
+DOT="🟣"
+# Smooth pulsate helper — ACTIVE state, so we pulse this color (Steve, 2026-09-04:
+# "Pulsate the color whenever claude or codex is waiting for me or just running").
+# Best-effort: if it's missing or the tty isn't writable, painting the solid color still succeeds.
+PULSE="${HOME}/.claude/tab-dots/pulse.sh"
+
+paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
+  local tp="$1" label="${2:-}" off="${3:-0}"
+  [[ -w "$tp" ]] || return 1
+  if [[ "$off" == "1" ]]; then
+    # Stop any active pulse FIRST (before the reset) so the pulse loop can't repaint after we clear.
+    [[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
+    rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  local title="$DOT ${label:-GATED}"
+  # NOTE: no leading '*;default' reset — on iTerm2 it can swallow the color set (pinkdot bug,
+  # 2026-09-02). Set RGB directly and write BOTH title channels (]1; tab, ]2; window).
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tp" 2>/dev/null
+  # Persist the dot so the 60s tab-ticket repainter PREFIXES it into the window title
+  # (visible even when the tab bar is hidden), and show an in-window iTerm2 BADGE.
+  mkdir -p "${HOME}/.claude/tab-dots"
+  printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
+  # ACTIVE state → start a smooth pulse of this same color (pulse.sh's per-tty pidfile replaces
+  # any existing pulse on this tty, so pulses never stack). Best-effort, never fatal.
+  [[ -f "$PULSE" ]] && bash "$PULSE" "$R" "$G" "$B" "$tp" >/dev/null 2>&1 || true
+}
+
+# --all: paint every live claude tab purple
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/purpledot --all → no live claude ttys"; exit 0; }
+  n=0
+  for tt in $ttys; do paint "/dev/$tt" "GATED" 0 && { echo "  $tt → 🟣 GATED"; n=$((n+1)); }; done
+  echo "/purpledot --all → marked ${n} tab(s) purple (gated, awaiting approval)"
+  exit 0
+fi
+
+OFF=0; LABEL=""
+[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
+
+# Resolve THIS session's controlling pty by walking ancestry (same as color.sh).
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then
+  echo "/purpledot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
+fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/purpledot --off → cleared tint on $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/purpledot → $tty_path 🟣 ${LABEL:-GATED}  rgb(${R},${G},${B})"
+fi
diff --git a/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh b/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh
new file mode 100644
index 0000000..fd518e4
--- /dev/null
+++ b/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-set.sh
@@ -0,0 +1,60 @@
+#!/usr/bin/env bash
+# tab-ticket-set.sh TK-####  — declare THIS window's ticket.
+# Resolves the caller's own controlling ttysNNN (walking process ancestry, same
+# as color.sh) and writes the TK id to ~/.claude/tab-tickets/<ttyname>.txt so
+# tab-ticket-title.sh will render "TK-#### · up Xh Ym" on this window's title.
+#
+# Usage:  tab-ticket-set.sh TK-10921        # set this window's ticket
+#         tab-ticket-set.sh clear|none      # clear it (back to no-TK)
+#         tab-ticket-set.sh --apply TK-1234 # set AND immediately repaint titles
+set -u
+
+TICKET_DIR="${HOME}/.claude/tab-tickets"
+mkdir -p "$TICKET_DIR"
+SELF_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+APPLY=0
+if [[ "${1:-}" == "--apply" ]]; then APPLY=1; shift; fi
+
+TK="${1:-}"
+if [[ -z "$TK" ]]; then
+  echo "usage: tab-ticket-set.sh [--apply] TK-#### | clear"
+  exit 2
+fi
+
+# resolve caller's own controlling ttys* by walking ancestry from $$
+tty_path=""; ttyname=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10 11 12; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  # shellcheck disable=SC2086
+  set -- $line; pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then ttyname="$tt"; tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+
+if [[ -z "$ttyname" ]]; then
+  echo "tab-ticket-set → could not resolve my own tty; ticket not set"
+  exit 1
+fi
+
+tkfile="$TICKET_DIR/${ttyname}.txt"
+
+case "$TK" in
+  clear|none|CLEAR|NONE)
+    rm -f "$tkfile"
+    echo "tab-ticket-set → cleared ticket for ${ttyname} (now no-TK)"
+    ;;
+  *)
+    # normalise: accept "10921" or "tk-10921" → "TK-10921"; else store verbatim
+    norm="$TK"
+    if [[ "$TK" =~ ^[0-9]+$ ]]; then norm="TK-$TK"; fi
+    norm="$(printf '%s' "$norm" | sed -E 's/^tk-/TK-/I')"
+    printf '%s\n' "$norm" > "$tkfile"
+    echo "tab-ticket-set → ${ttyname} now working ${norm}"
+    ;;
+esac
+
+if (( APPLY )); then
+  bash "$SELF_DIR/tab-ticket-title.sh" >/dev/null 2>&1 || true
+fi
diff --git a/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh b/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh
new file mode 100644
index 0000000..5912803
--- /dev/null
+++ b/verification/install/before/.claude/skills/tab-ticket/scripts/tab-ticket-title.sh
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+# tab-ticket-title.sh — set EVERY open `claude` OR `codex` window/tab TITLE
+# (top of the window) to  "TK-#### · up Xh Ym"  so at a glance you know which
+# ticket each window is working AND how long it's been up. (2026-09-09: added
+# codex coverage — Steve runs claude+codex peer sessions side by side on the
+# same tickets, and codex ttys were silently falling back to iTerm's generic
+# "<dir> (codex)" auto-title with no ticket shown at all.)
+#
+# Mechanism (reuses /color's proven path): both tools' Bash/exec runs detached
+# with NO controlling tty, so OSC escapes on stdout never reach the terminal.
+# We enumerate every live `claude`/`codex` process's controlling ttysNNN (same
+# as color.sh --all) and write the OSC TITLE escape straight to /dev/ttysNNN.
+#
+# CRITICAL: the title OSC ( ESC ]0; / ]1; / ]2; ) is INDEPENDENT of the
+# color-tint OSC ( ESC ]6;1;bg;... ). Writing a title does NOT clear the tab
+# color — verified. So this can run every 60s without disturbing /color's tint.
+#
+# UPTIME  — derived per-tty from the claude process's OWN start time via
+#           `ps -o etime= -p <pid>`. No state file needed.
+# TICKET  — read from ~/.claude/tab-tickets/<ttyname>.txt (just the TK id).
+#           Declared by a session via tab-ticket-set.sh. Missing → "no-TK".
+set -u
+
+TICKET_DIR="${HOME}/.claude/tab-tickets"
+mkdir -p "$TICKET_DIR"
+
+# fmt_uptime <etime>  — turn ps etime ([[DD-]HH:]MM:SS) into "Xd Yh" / "Xh Ym" / "Xm"
+fmt_uptime() {
+  awk -v e="$1" 'BEGIN{
+    days=0; rest=e
+    n=split(e, a, "-")
+    if (n==2){ days=a[1]+0; rest=a[2] } else { rest=a[1] }
+    m=split(rest, t, ":")
+    if (m==3){ h=t[1]+0; mi=t[2]+0 }
+    else if (m==2){ h=0; mi=t[1]+0 }
+    else { h=0; mi=0 }
+    # total hours including days
+    th = days*24 + h
+    if (days > 0)      printf "up %dd %dh", days, h
+    else if (th > 0)   printf "up %dh %dm", th, mi
+    else               printf "up %dm", mi
+  }'
+}
+
+# Enumerate every live claude OR codex session's controlling tty. Both report
+# comm=node / their own binary, so match on the full command line (same as
+# color.sh --all). codex sessions never call tab-ticket-set.sh, so we also
+# pull a TK-#### straight out of the launch command line itself (it's right
+# there in argv for every ticket-driven run — "TK-10670-onboard-refresh-...")
+# as a fallback when no manually-declared ticket file exists. This covers
+# BOTH claude and codex ttys — claude tabs that never called tab-ticket-set.sh
+# now also pick up their ticket from argv instead of sitting on "no-TK".
+# Capture tty + etime + auto-detected-TK in one ps pass so we pick the RIGHT
+# process per tty. Written for bash 3.2 (macOS /bin/bash under launchd): NO
+# mapfile, NO associative arrays. One session may report multiple matching
+# rows on the same tty (helper procs); awk collapses to the FIRST row seen
+# per tty.
+ROWS="$(ps -axo tty=,pid=,etime=,command= 2>/dev/null \
+  | awk '$1 ~ /^ttys/ && $0 ~ /[c]laude|[c]odex/ && !seen[$1]++ {
+      tk = "";
+      if (match($0, /TK-[0-9]+/)) tk = substr($0, RSTART, RLENGTH);
+      print $1"\t"$3"\t"tk
+    }')"
+
+if [[ -z "$ROWS" ]]; then
+  echo "tab-ticket-title → no live claude/codex ttys found"
+  exit 0
+fi
+
+n=0
+while IFS=$'\t' read -r tt etime autotk; do
+  [[ -z "$tt" ]] && continue
+
+  tp="/dev/$tt"
+  [[ -w "$tp" ]] || continue
+
+  # ticket for this tty: manually-declared file wins, else fall back to
+  # whatever TK-#### was found in the process's own launch command.
+  tk="no-TK"
+  tkfile="$TICKET_DIR/${tt}.txt"
+  if [[ -f "$tkfile" ]]; then
+    v="$(tr -d ' \t\r\n' < "$tkfile" 2>/dev/null)"
+    [[ -n "$v" ]] && tk="$v"
+  fi
+  if [[ "$tk" == "no-TK" && -n "$autotk" ]]; then tk="$autotk"; fi
+
+  up="$(fmt_uptime "$etime")"
+  label="${tk} · ${up}"
+
+  # Semantic DOT prefix (2026-09-02): /orangedot /purpledot /yellowdot /pinkdot drop
+  # "<emoji> <label>" into ~/.claude/tab-dots/<tty>.dot; --off removes it. Prefixing
+  # it here means the dot SURVIVES this 60s repaint (before, the repaint erased it),
+  # and it shows in the WINDOW TITLE even when the tab bar is hidden (single-tab windows).
+  dotfile="${HOME}/.claude/tab-dots/${tt}.dot"
+  d=""
+  if [[ -f "$dotfile" ]]; then
+    d="$(head -c 80 "$dotfile" 2>/dev/null | tr -d '\r\n')"
+  fi
+  # DEFAULT DOT (2026-09-02): a live claude tab with NO explicit state file gets a
+  # neutral ⚪ so NO tab is ever dotless at the top (root fix for "where is my dot?" —
+  # a brand-new session had no ~/.claude/tab-dots/<tty>.dot yet). The semantic dot
+  # skills (/pinkdot /orangedot /yellowdot /purpledot) still override by writing the file.
+  [[ -z "$d" ]] && d="⚪"
+  label="${d} · ${label}"
+
+  # Title-only OSC — set icon-name(1), window-title(2), and generic title(0).
+  # These are INDEPENDENT of the ]6;bg color-tint OSC, so the tab COLOR survives.
+  {
+    printf '\033]1;%s\007' "$label"
+    printf '\033]2;%s\007' "$label"
+    printf '\033]0;%s\007' "$label"
+  } > "$tp" 2>/dev/null && n=$(( n + 1 ))
+done <<< "$ROWS"
+
+echo "tab-ticket-title → set title on ${n} claude window(s)"
diff --git a/verification/install/before/.claude/skills/yellowdot/yellowdot.sh b/verification/install/before/.claude/skills/yellowdot/yellowdot.sh
new file mode 100644
index 0000000..de8b57d
--- /dev/null
+++ b/verification/install/before/.claude/skills/yellowdot/yellowdot.sh
@@ -0,0 +1,94 @@
+#!/usr/bin/env bash
+# /yellowdot — mark a Claude session's iTerm2 tab YELLOW = "NEEDS DIRECTION — a question / decision fork is pending for Steve".
+# A fixed SEMANTIC color (not /color's rotating golden-angle hue): yellow always means
+# "this session finished its work and is holding." Lets Steve see at a glance, across a wall
+# of tabs, which sessions are waiting on his ANSWER (yellow) vs gated in the viewer (purple) vs a terminal paste (orange) vs parked (pink) vs working (/color).
+#
+# Mechanism (identical to color.sh): the Claude Bash tool runs with NO controlling tty, so
+# OSC escapes on stdout never reach the terminal. We resolve the `claude` process's own pty
+# by walking process ancestry and write the OSC straight to /dev/ttysNNN. OSC color/title
+# codes are non-destructive (no cursor moves) so this is glitch-safe.
+#
+# Usage:
+#   yellowdot.sh              # yellow-dot THIS session (title "🟡 DIRECTION?")
+#   yellowdot.sh "label"      # yellow tint + custom title text (🟡 prefixed)
+#   yellowdot.sh --all        # yellow-dot EVERY live claude tab (rare)
+#   yellowdot.sh --off        # clear the tint back to default on THIS session
+set -u
+
+# HARD RAIL (2026-09-08): a subagent has no tab of its own — its ancestry resolves to the
+# PARENT session's pty, so painting would corrupt Steve's live pane (the input-field bug). No-op.
+if [ -n "${CLAUDE_CODE_CHILD_SESSION:-}" ]; then
+  echo "/yellowdot → subagent context — refusing to paint (would write to the parent session's live pane). no-op."
+  exit 0
+fi
+
+# Semantic yellow (warm gold) — "needs direction". REPURPOSED 2026-09-02 (Steve: "where is color dot for direction"); idle/parked is now /pinkdot.
+R=255; G=204; B=0
+DOT="🟡"
+# Smooth pulsate helper — ACTIVE state, so we pulse this color (Steve, 2026-09-04:
+# "Pulsate the color whenever claude or codex is waiting for me or just running").
+# Best-effort: if it's missing or the tty isn't writable, painting the solid color still succeeds.
+PULSE="${HOME}/.claude/tab-dots/pulse.sh"
+
+paint() { # $1 = tty_path, $2 = label(optional), $3 = off(1=reset)
+  local tp="$1" label="${2:-}" off="${3:-0}"
+  [[ -w "$tp" ]] || return 1
+  if [[ "$off" == "1" ]]; then
+    # Stop any active pulse FIRST (before the reset) so the pulse loop can't repaint after we clear.
+    [[ -f "$PULSE" ]] && bash "$PULSE" --stop "$tp" >/dev/null 2>&1 || true
+    { printf '\033]6;1;bg;*;default\007'; printf '\033]1337;SetBadgeFormat=\a'; } > "$tp" 2>/dev/null
+    rm -f "${HOME}/.claude/tab-dots/$(basename "$tp").dot" 2>/dev/null
+    return 0
+  fi
+  local title="$DOT ${label:-DIRECTION?}"
+  {
+    printf '\033]6;1;bg;red;brightness;%d\007'   "$R"
+    printf '\033]6;1;bg;green;brightness;%d\007' "$G"
+    printf '\033]6;1;bg;blue;brightness;%d\007'  "$B"
+    printf '\033]1;%s\007' "$title"
+    printf '\033]2;%s\007' "$title"
+  } > "$tp" 2>/dev/null
+  # Persist the dot so the 60s tab-ticket repainter PREFIXES it into the window title
+  # (visible even when the tab bar is hidden), and show an in-window iTerm2 BADGE.
+  mkdir -p "${HOME}/.claude/tab-dots"
+  printf '%s' "$title" > "${HOME}/.claude/tab-dots/$(basename "$tp").dot"
+  printf '\033]1337;SetBadgeFormat=%s\a' "$(printf '%s' "$title" | base64 | tr -d '\n')" > "$tp" 2>/dev/null
+  # ACTIVE state → start a smooth pulse of this same color (pulse.sh's per-tty pidfile replaces
+  # any existing pulse on this tty, so pulses never stack). Best-effort, never fatal.
+  [[ -f "$PULSE" ]] && bash "$PULSE" "$R" "$G" "$B" "$tp" >/dev/null 2>&1 || true
+}
+
+# --all: paint every live claude tab yellow
+if [[ "${1:-}" == "--all" ]]; then
+  ttys="$(ps -axo tty=,command= 2>/dev/null \
+    | awk '$1 ~ /^ttys/ && $0 ~ /claude/ && $0 !~ /grep/ {print $1}' | sort -u)"
+  [[ -z "$ttys" ]] && { echo "/yellowdot --all → no live claude ttys"; exit 0; }
+  n=0
+  for tt in $ttys; do paint "/dev/$tt" "DIRECTION?" 0 && { echo "  $tt → 🟡 DIRECTION?"; n=$((n+1)); }; done
+  echo "/yellowdot --all → marked ${n} tab(s) yellow (needs direction)"
+  exit 0
+fi
+
+OFF=0; LABEL=""
+[[ "${1:-}" == "--off" ]] && OFF=1 || LABEL="${1:-}"
+
+# Resolve THIS session's controlling pty by walking ancestry (same as color.sh).
+tty_path=""; p=$$
+for _ in 1 2 3 4 5 6 7 8 9 10; do
+  line="$(ps -o ppid=,tty= -p "$p" 2>/dev/null)" || break
+  set -- $line
+  pp="${1:-}"; tt="${2:-}"
+  [[ -z "$pp" ]] && break
+  if [[ "$tt" == ttys* ]]; then tty_path="/dev/$tt"; break; fi
+  p="$pp"
+done
+if [[ -z "$tty_path" ]]; then
+  echo "/yellowdot → could not resolve this session's tty (no ttysNNN in ancestry)"; exit 1
+fi
+
+if [[ "$OFF" == "1" ]]; then
+  paint "$tty_path" "" 1 && echo "/yellowdot --off → cleared tint on $tty_path"
+else
+  paint "$tty_path" "$LABEL" 0 && echo "/yellowdot → $tty_path 🟡 ${LABEL:-DIRECTION?}  rgb(${R},${G},${B})"
+fi
diff --git a/verify_live.py b/verify_live.py
new file mode 100644
index 0000000..516770b
--- /dev/null
+++ b/verify_live.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python3
+"""Exercise the actual own-terminal Claude/Codex adapters and native header."""
+import datetime as dt
+import json
+from pathlib import Path
+import subprocess
+import sys
+import terminal_status as ts
+
+root = Path.home()
+project = Path(__file__).resolve().parent
+owner = ts.current_owner(ts.processes())
+assert owner.tty == 'ttys010', 'This recorded verification is scoped to TK-11317 only'
+store = ts.Store()
+checks = []
+
+
+def command(args):
+    return subprocess.run(args, text=True, capture_output=True, check=True, timeout=30).stdout
+
+
+def own_row():
+    data = json.loads(command(['bash', str(root / '.claude/skills/allcolordots/allcolordots.sh'), '--json']))
+    return next(r for r in data if r['tty'] == owner.tty)
+
+
+def native():
+    script = '''tell application "iTerm2"
+repeat with s in sessions of tab 1 of window id 152190
+ if tty of s is "/dev/ttys010" then
+  select s
+  exit repeat
+ end if
+end repeat
+delay 0.15
+return name of window id 152190
+end tell'''
+    return command(['osascript', '-e', script]).strip()
+
+
+try:
+    command([sys.executable, str(project / 'terminal_status.py'), 'ticket', 'TK-11317'])
+    for runtime in ('codex', 'claude'):
+        skills = root / ('.agents' if runtime == 'codex' else '.claude') / 'skills'
+        for color in ('green', 'orange', 'purple', 'yellow', 'pink'):
+            output = command(['bash', str(skills / f'{color}dot/{color}dot.sh'), 'E2E check'])
+            r, source = store.load(owner)
+            assert source == 'canonical' and r['state'] == color
+            assert r['ticket'] == 'TK-11317'
+            assert all((d / (owner.tty + '.dot')).read_text() == r['title'] for d in store.legacy.values())
+            row = own_row()
+            assert row['color'] == color and row['label'] == r['title']
+            title = native()
+            assert r['title'] in title, (r['title'], title)
+            checks.append({'check': f'{runtime}:{color}', 'verdict': 'PASS',
+                           'canonical': r['title'], 'topbar': row['label'], 'window': title})
+    command([sys.executable, str(project / 'terminal_status.py'), 'clear'])
+    before = store.path(owner).read_bytes()
+    command(['bash', str(root / '.agents/skills/color/scripts/repaint-dot.sh')])
+    assert before == store.path(owner).read_bytes()
+    assert own_row()['color'] == 'none'
+    command([sys.executable, str(project / 'terminal_status.py'), 'headers', '--quiet'])
+    assert 'TK-11317' in native() and 'Status cleared' in native()
+    checks.append({'check': 'clear + Stop repaint + native title refresh', 'verdict': 'PASS'})
+    command(['bash', str(root / '.claude/skills/color/scripts/working-state.sh'), 'on'])
+    assert own_row()['label'] == '🟢 TK-11317 · WORKING'
+    checks.append({'check': 'generic prompt hook preserves ticket', 'verdict': 'PASS'})
+    ts.atomic_write(store.legacy['claude'] / (owner.tty + '.dot'), '🟠 old stale orange')
+    assert own_row()['color'] == 'green'
+    checks.append({'check': 'topbar rejects stale orange mirror', 'verdict': 'PASS'})
+finally:
+    command(['bash', str(root / '.agents/skills/greendot/greendot.sh'), 'TK-11317 · WORKING'])
+    report = {'ticket': 'TK-11317', 'timestamp': dt.datetime.now(dt.timezone.utc).isoformat(),
+              'owner': ts.dataclasses.asdict(owner), 'checks': checks,
+              'cleanup': 'Own terminal restored green WORKING, bound to TK-11317; mirrors repaired.'}
+    ts.atomic_write(project / 'verification/live-journey.json', json.dumps(report, ensure_ascii=False, indent=2) + '\n')
+print(json.dumps(report, ensure_ascii=False, indent=2))
diff --git a/verify_ui.py b/verify_ui.py
new file mode 100644
index 0000000..4dec684
--- /dev/null
+++ b/verify_ui.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python3
+"""Capture only the native status menu, excluding terminal contents."""
+import json
+from pathlib import Path
+import subprocess
+import terminal_status as ts
+
+project = Path(__file__).resolve().parent
+script = '''tell application "System Events" to tell process "ticket-desktop-bar"
+ click button "◉" of window 2
+ delay 0.7
+ set m to menu 1 of button "◉" of window 2
+ set names to name of menu items of m
+ set AppleScript's text item delimiters to linefeed
+ set labels to names as text
+ set AppleScript's text item delimiters to ","
+ set rect to (position of m & size of m) as text
+ return rect & linefeed & labels
+end tell'''
+result = subprocess.run(['osascript', '-e', script], capture_output=True, text=True, check=True)
+rect, labels = result.stdout.split('\n', 1)
+coords = ','.join(str(int(x.strip())) for x in rect.split(','))
+assert '🟢 TK-11317 · WORKING' in labels
+image = project / 'verification/desktop-menu.png'
+subprocess.run(['screencapture', '-x', '-R', coords, str(image)], check=True)
+ts.atomic_write(project / 'verification/native-menu.json', json.dumps({
+    'verdict': 'PASS', 'labels': labels.splitlines(), 'capture': str(image)}, ensure_ascii=False, indent=2) + '\n')
+print('PASS: actual desktop menu contains TK-11317 and six Codex ticket labels; ' + str(image))

← 03cc6f9 auto-data-snapshot: 2026-09-09T11:09:48 (1 data files) — ver  ·  back to Terminal Status  ·  fix: paint guard resolves ancestry before refusing on CLAUDE 914a88d →