Skip to content

Status Line Complete Guide

Real-time visual indicators of system health and development activity rendered via the unified tmux status bar. All coding agents (Claude, CoPilot, etc.) are wrapped in tmux sessions; status-right invokes status-line-fast.cjs, a CommonJS fast-path reader that serves a per-pane pre-rendered cache (~60 ms) and spawns the full combined-status-line.js (CSL) renderer only when the cache is stale. The renderer pulls live state from the health coordinator at :3034 (Phase 33 single source of truth, replacing the retired host-side health-verifier daemon and .health/verification-status.json file). All five coordinator-derived badges share a single memoized probe per render — see Architecture → Shared coordinator probe.

Status Line Display

Reading the Status Line

Example Display

[🏥✅] [RA⚫C🟢] [🔒77% ⚙️IMP] [📚✅] [N:VPN] [P:ON] [📋18-19] 18:34

The current pane's project is rendered with an underline (#[underscore]…#[nounderscore]) so each parallel tmux window highlights its own project.

Component Breakdown

Component Example Description
System Health [🏥✅] Coordinator-derived health rollup (services + databases + container)
Active Sessions [RA⚫C🟢] Per-project abbreviations with graduated activity icons
Constraint [🔒77%] Code quality % (with optional 🟡N violations sub-segment when non-zero)
Knowledge Pipeline [📚✅] Observation/digest/insight pipeline freshness
Network Location [N:VPN] Network environment: VPN / CN / OPEN / ??
Proxy Status [P:ON] Local proxy daemon (proxydetox): ON / OFF
LSL Time Window [📋18-19] Session time range (HHMM-HHMM)
Time 18:34 Local HH:MM, anchored to the right edge

Complete Emoji Reference

System Health Indicators

The badge is derived live from the coordinator at :3034/health/state. There is no longer a host-side health-verifier daemon; the badge reflects the coordinator's rollup of probed services, database checks, and container healthcheck.

Display Meaning Action
[🏥✅] All systems healthy None needed
[🏥🟡] Non-critical issue (e.g. degraded service or GCM warning) Check dashboard for details
[🏥⏰] Stale — coordinator's generated_at >3 minutes old Coordinator may be down; check container
[🏥❌] Critical issue (downed service, unhealthy DB, container probe fail) Immediate attention required
[🏥💤] Coordinator unreachable Verify dashboard service is running

Session Activity Indicators

Sessions use a graduated color scheme based on time since last activity. All sessions are always displayed — sleeping sessions show as 💤, never hidden. Sessions are only removed when the agent process exits.

The "time since last activity" signal is the project's Claude .jsonl transcript mtime — i.e., the time of the last prompt boundary. A long-running agent turn (one prompt that takes 25 minutes) writes nothing to the transcript while it's in flight, so a project actively being worked on by an agent looks idle by mtime alone. To capture that, a heartbeat promotion rule overrides the transcript-derived band: if the project's ETM heartbeat (state.lsl[*].lastBeat) is fresh (< 5 min) and the transcript-derived icon would be anything other than 🟢, the icon is promoted to 🟢. The heartbeat is the canonical "agent/user is here right now" signal. The promotion also handles non-Claude sessions (OpenCode / Copilot) whose transcriptPath is not a real file — pure transcript-mtime logic would mis-bucket them.

Icon Status Time Since Activity Description
🟢 Active < 5 minutes Active session with recent activity
🟠 Cooling 5 - 30 minutes Session cooling down
🟤 Fading 30 min - 6 hours Session fading, still tracked
Inactive 6 - 24 hours Session inactive but tracked
💤 Sleeping > 24 hours Long-term dormant session
Error Any Health check failed or service crash

Visual progression: circles fade green → orange → brown → black, then 💤 once the session has been idle over a day.

🟢 Active → 🟠 Cooling → 🟤 Fading → ⚫ Inactive → 💤 Sleeping
   <5min      5-30min     30m-6hr     6-24hr       >24hr

Color choice rationale

🟡 (yellow) and 🔴 (red) are intentionally omitted from the lifecycle — they are reserved as health-state indicators (warning / critical) in the [🏥], [LSL], [📚] and [🧠] badges. Lifecycle icons must be distinguishable from health icons at a glance, so the lifecycle uses green → orange → brown → black exclusively.

Agent Age Cap

When an agent process (Claude, Copilot, OpenCode) is running, the displayed age is capped at the transcript monitor's uptime. This prevents a freshly started session in a project with old transcripts from immediately showing as dormant — the session starts green and naturally progresses through the cooling scheme based on how long the current session has been idle.

Not-Found Transcript Guard

Agents that don't produce Claude-compatible transcripts (e.g., OpenCode) have transcriptInfo.status: 'not_found'. The age cap logic skips these sessions — they correctly display as ⚫ inactive instead of falsely showing as 🟢 active.

Network & Proxy Indicators

Two ASCII-only badges reflect the network environment detected by the coordinator every 15 seconds. They avoid emoji to prevent cell-width issues in tmux.

Display Meaning Action
[N:VPN] Connected via corporate VPN (Cisco CLI or utun interface detected) Normal remote-work state
[N:CN] On the physical corporate network (BMW DNS resolves via dig, TCP latency <100 ms, no VPN interface) Normal on-site state
[N:OPEN] Home / public network — no VPN, no corporate LAN Expected off-VPN
[N:??] Network location unknown (coordinator just started or probe failed) Transient — clears within 15s
Display Meaning Action
[P:ON] Proxydetox daemon is running (port 3128 listening) Normal on VPN/CN
[P:OFF] Proxydetox daemon is stopped (port 3128 closed) Expected on OPEN; problem on VPN

P: is binary — no ERR state

Previous versions had a P:ERR state (proxy port listening but CONNECT tunnel fails). This was removed — the proxy is either ON (port up) or OFF (port down). If the proxy is up but non-functional, internet_reachable in the coordinator state will be false, and the system health badge [🏥] reflects the issue.

Proxy toggle via px

The px alias toggles proxydetox via launchctl unload/launchctl load (not stop/start, which was ineffective due to launchd socket activation). After toggling, px invalidates all status line caches and triggers an immediate coordinator re-probe via POST /health/refresh. The P: badge updates within ≤5 seconds (one tmux refresh cycle). See Network Configuration → Proxy Management for details.

VPN / network detection logic (3-signal approach)

The coordinator determines location using three independent signals. N never depends on proxy state.

Signal 1 — Cisco VPN CLI: runs /opt/cisco/secureclient/bin/vpn state. If the output contains "Connected" → vpn.

Signal 2 — utun interface detection: parses ifconfig for any utun* interface with an inet address. If found → vpn.

Signal 3 — BMW internal DNS: spawns dig +short muc.proxy-pac.bmwgroup.net (fresh subprocess — always uses current OS DNS servers, never stale). If resolution succeeds, measures TCP latency to the resolved address: latency <100 ms → corporate (on-site LAN); latency ≥100 ms → vpn (tunnelled). If resolution fails entirely → open (home / public network).

Signals are evaluated in order; the first match wins.

Knowledge Pipeline Indicators

The badge reflects the freshness of the observation → digest → insight pipeline (the obs_api service backed by km-core GraphKMStore at .data/knowledge-graph/; the legacy .observations/observations.db SQLite store was archived 2026-06-05 under Phase 44 Plan 18). Verdict is driven by observation freshness only — digest and insight cadences are intentionally slower and don't gate the badge. Source: state.knowledge_pipeline at the coordinator's /health/state (populated by pollKnowledgePipeline, which calls obs_api's /api/consolidation/status).

Status Icon Meaning
Healthy [📚✅] Last observation written within 15 minutes — pipeline is ingesting
Fresh [📚🟢] Last observation 15 min – 1 hour ago
Aging [📚🟠] Last observation 1 – 3 hours ago
Fading [📚🟤] Last observation 3 – 6 hours ago
Dormant [📚⚫] Last observation 6 – 12 hours ago
Sleeping [📚💤] Last observation > 12 hours ago
Disabled [📚🔇] obs_api reachable but no rows in any pipeline table
Unknown [📚❓] Coordinator just started, slice not yet populated
Unreachable [📚🔴] obs_api unreachable, returning non-OK, or returning unparseable JSON

Red is reserved for pipeline failure

🔴 is shown only when obs_api is unreachable (confirmed pipeline failure). Time-based staleness uses a graduated fading scheme (🟢 → 🟠 → 🟤 → ⚫ → 💤) — red is never used for age alone.

Idle suppression is applied via CombinedStatusLine.isUserActive(), which checks state.lsl for any session whose lastBeat is within 5 min. When no session is heartbeating, the freshness-derived icons collapse to a single (idle). True error states (disabled, unknown, unreachable) are NOT suppressed.

Tooltip details (visible in the verbose status output) include observation/digest/insight ages, totals, and any in-flight consolidation.

Coordinator Health Endpoint

The statusline pulls all health-related signals live from the coordinator. Inspect raw state with:

curl -fs http://localhost:3034/health/state | jq .
Top-level key Meaning
container.healthcheck Docker coding-services container probe result
services List of probed services with status, last_seen, latency_ms, probe_error
databases LevelDB / Qdrant / Memgraph availability + lock state (sub-checks: leveldb_lock_check, qdrant_availability, graph_integrity — probed every tick and mapped to passed/failed)
network Network environment: internet_reachable, proxy_running, location (vpn / corporate / home / unknown)
lsl Per-session ETM heartbeats (sessionId, projectName, transcriptPath, lastBeat)
lsl_by_project 3-state rollup per project: healthy / degraded / stopped
processes Stale-PID check (probes for orphaned consolidation heartbeat files)
files Disk space, log file size, services-running file freshness
generated_at Coordinator's last refresh — drives the [🏥⏰] staleness check

Phase 33 retired the .logs/statusline-health-status.txt rollup and the .health/verification-status.json file; do not write or read those paths in new code.

Transcript Discovery Auto-Heal

The ETM (enhanced-transcript-monitor.js) detects broken transcript discovery — monitor running but unable to locate its project's transcript JSONL.

  • Detection: ETM heartbeat reports transcriptPath: null while uptime exceeds the discovery grace period
  • Remediation: ETM exits with non-zero status; the launcher / supervisor restarts it
  • Display: Project rolls up as degraded, surfaces as 🟡 in the sessions block
  • Path encoding: Claude Code replaces both / and _ with - (e.g. /_work/--work-)

Architecture

6-Layer Health System

Health System Classes

The StatusLineHealthMonitor (Layer 4) aggregates health from all other layers and outputs to the Combined Status Line display.

StatusLine Architecture

Tmux-Based Rendering

All coding agents are wrapped in tmux sessions via scripts/tmux-session-wrapper.sh. The wrapper:

  • Creates a tmux session named coding-{agent}-{PID}
  • Configures status-right to invoke status-line-fast.cjs (CJS fast-path cache reader, ~60ms)
  • Handles nesting guard (reuses existing tmux if already inside one)
  • Propagates environment variables (CODING_REPO, SESSION_ID, etc.)
  • Enables mouse forwarding for terminal interaction

This replaces the previous approach of using agent-specific status bar APIs (e.g., Claude's statusLine config), providing a unified rendering target that works identically for all agents.

Cache Fast-Path

status-line-fast.cjs is the primary entry point invoked by tmux status-right. It's a CommonJS reader (no ESM module-load penalty under load) that serves the per-pane pre-rendered cache in ~60 ms:

  • Cache file: .logs/combined-status-line-cache-<project>-w<paneWidth>.txt (one per (project, pane-width) tuple — width key prevents cross-pane contamination)
  • Cache TTL: 60 s in fast.cjs (a separate 30 s TTL inside CSL itself when CSL re-enters via its own cache check)
  • Fresh (<60 s): serves immediately, optionally patches lifecycle icons against current transcript mtimes, triggers background refresh if cache >20 s old or transcript activity is newer than cache
  • Stale or missing: spawns the full combined-status-line.js synchronously
  • Critical detail: fast.cjs calls child.stdin.end() immediately after spawning CSL. CSL's readStdinInput() (used by getRedirectStatus()) iterates over process.stdin and would otherwise hang the full 8 s SYS:TIMEOUT window waiting for EOF on any pane whose TRANSCRIPT_SOURCE_PROJECT falls outside the coding repo.
  • combined-status-line-wrapper.js is retained as a backup but is no longer the primary path.

Shared coordinator probe

Every render needs five different slices of state from the health coordinator (knowledge_pipeline, proxy, services rollup, lsl_by_project, generated_at staleness). Each was previously a separate synchronous probe via execSync('curl …'); five identical localhost HTTP calls per render added up to ~3 s under load and tripped the 8 s SYS:TIMEOUT during tmux refresh bursts.

The renderer now exposes a single getCoordinatorState() method memoized on the CombinedStatusLine instance (one instance per render):

  • Uses native fetch instead of execSync(curl) to skip subprocess-spawn overhead
  • 1.5 s per-attempt budget with one retry (150 ms gap)
  • All five getXxxStatus() methods await the shared result; one HTTP call serves the whole render
  • A single transient slow response no longer cascades every coordinator-derived badge (🏥 LSL 📚 🧠) to its unreachable state simultaneously

Right-edge stability (cell-width consistency)

The recurring trailing-digit residue at the right edge (07:538, 12:411, 07:158) traces to disagreement between the script's visibleCellWidth(), tmux's wcwidth, and the rendered font glyph. The fix is to only emit codepoints where all three measurements agree:

  • Warning states use 🟡 (U+1F7E1, EAW=Wide), not ⚠️ (U+26A0 + U+FE0F, EAW=Ambiguous). Earlier attempts to "promote" ⚠️ to 2 cells via a VS16 lookahead in visibleCellWidth() matched the font glyph width but disagreed with tmux's wcwidth (which doesn't honor VS16 for Ambiguous codepoints in non-CJK locales). The disagreement left 1 cell of the previous render exposed on every transition that involved ⚠️, and the font glyph visibly overlapped the closing ]. The VS16 lookahead logic is retained for the ⚠️ SYS:TIMEOUT / SYS:ERR fallback markers — error visibility there outweighs cell-perfect padding.
  • Padding is leading-spaces only to TMUX_PANE_WIDTH (or 200 if unset). Trailing characters get stripped by tmux's #(shell-cmd) substitution, so any trailing terminator (NBSP, space, etc.) doesn't survive the round-trip.
  • The earlier NBSP-terminator + 220-codepoint approach has been retired in favour of correct per-codepoint cell counting.

Status Line Update Flow

Status Line Hook Timing

Cache fast-path (normal operation):

  1. tmux status-interval: status-right fires every 5 s → status-line-fast.cjs
  2. Cache check: read .logs/combined-status-line-cache-<project>-w<paneWidth>.txt
  3. Fresh (<30 s): pass through to tmux (with lifecycle-icon patching against current transcript mtimes), trigger background refresh if cache age >10 s or any tracked transcript is newer than cache
  4. Stale or missing: spawn combined-status-line.js synchronously and child.stdin.end() immediately so CSL's readStdinInput() doesn't hang waiting for EOF

Full refresh:

  1. Shared coordinator probe: a single memoized fetch(:3034/health/state) per render (with one retry @ 1.5 s) feeds five getXxxStatus() methods — replaces the previous pattern of 5 independent execSync(curl) calls per render
  2. Per-project activity age: stat each lsl[*].transcriptPath mtime → bucket into the lifecycle (🟢 / 🟠 / 🟤 / ⚫ / 💤). A fresh ETM heartbeat (< 5 min) promotes a non-Active band to 🟢 (captures long-running agent turns and non-Claude sessions).
  3. Constraint compliance: separate call to constraint-monitor API (port 3031)
  4. Render: assemble parts, pad to paneWidth cells via leftPadToStableCellWidth() using VS16-aware visibleCellWidth() — see Right-edge stability above
  5. Cache write: save to .logs/combined-status-line-cache-<project>-w<paneWidth>.txt
  6. Failure logging: any 8 s SYS:TIMEOUT or fast.cjs spawn failure appends a JSON record to .logs/csl-failures.jsonl with per-step timings so future Claude sessions can see which sub-step blocked

Caching

Data Cache Duration
Pre-rendered status (fast-path) 60s TTL, 20s background refresh
Health status 5 minutes
Constraint compliance 1 minute
LSL status Read on every update

Spawn Storm Prevention

The supervision architecture includes guards to prevent runaway process spawning:

Guard Component Mechanism
GPS heartbeat gate CombinedStatusLine ensure* functions skip when GPS heartbeat <60s old
OS-level dup check GlobalServiceCoordinator findRunningProcessesByScript() before every spawn
Orphan kill GlobalServiceCoordinator Kills spawned process if post-spawn health check fails
Cooldown GPS (5min), Coordinator (2min) Per-service cooldown between restart attempts
Rate limiting GPS (10/hr), Coordinator (6/hr) Maximum restarts per service per hour
OS-level re-registration GlobalProcessSupervisor Re-registers alive services instead of respawning

Service Lifecycle States

Service Lifecycle State

State Transitions

Health States (for [🏥...] indicator): - Coordinator reachable + 0 critical issues → Healthy (✅) - Coordinator reachable + ≥1 service degraded / GCM warning → Warning (🟡) - Coordinator reachable + critical failure (downed service, unhealthy DB, container probe fail) → Critical (❌) - Coordinator generated_at >3 min old → Stale (⏰) - Coordinator unreachable → Offline (💤)

Session States (graduated cooling scheme): - Driven by transcriptPath mtime in coordinator state, bucketed: 🟢 (<5 m) → 🟠 (<30 m) → 🟤 (<6 h) → ⚫ (<24 h) → 💤 (≥24 h) - Heartbeat-promotion override: if lsl[*].lastBeat is < 5 min, any non-🟢 band is overridden to 🟢. Captures long-running agent turns (one prompt that takes >5 min) and non-Claude sessions whose transcriptPath is not a real file - Sessions only removed when the project's ETM stops heartbeating, never hidden while alive


Session Discovery

Discovery Methods

  1. Running Monitor Detection: Checks ps aux for running enhanced-transcript-monitor.js processes
  2. Agent Process Detection: Scans for claude, copilot, and opencode processes via ps -eo pid,comm and resolves project from working directory via lsof
  3. Registry-based Discovery: Uses Global LSL Registry for registered sessions
  4. Dynamic Discovery: Scans Claude transcript directories for unregistered sessions
  5. Health File Validation: Uses centralized health files from .health/ directory

Key Behavior

  • Sessions with a running agent process use age capped at monitor uptime (graduated cooling from session start)
  • Sessions with running transcript monitors but no active agent use transcript-based activity icons
  • Sessions are only removed when the agent process has exited — never hidden
  • The Global Process Supervisor automatically restarts dead monitors within 30 seconds

Multi-Agent Support

Agent Binary Detection Method
Claude claude Exact match on ps -eo comm
Copilot copilot Path-ending match /copilot$
OpenCode opencode Path-ending match /opencode$

New agents can be added to the detection loop in statusline-health-monitor.jsgetRunningAgentSessions().

Smart Abbreviation Engine

Project names are automatically abbreviated:

Project Name Abbreviation
coding C
curriculum-alignment CA
nano-degree ND
project-management PM
user-interface UI

Algorithm Handles: - Single words: First letter (coding → C) - Hyphenated words: First letter of each part (curriculum-alignment → CA) - Camel case: Capital letters (projectManagement → PM)


Configuration

Status Line Configuration

The renderer reads a small set of environment variables and the coordinator endpoint; legacy config/status-line-config.json health_source / lsl_registry keys are no longer consulted.

Env var Purpose Default
HEALTH_COORDINATOR_URL Coordinator base URL http://localhost:3034
TMUX_PANE_PATH Per-pane current path (set by tmux)
TRANSCRIPT_SOURCE_PROJECT Override project path resolution
CODING_REPO Repo root for cache file location script's __dirname/..
CLAUDE_SESSION_ID / SESSION_ID Session identifier for per-pane lookups
Tunable Where Default
Cache TTL (fast-path) status-line-fast.cjs CACHE_TTL_MS 30 s
Background refresh threshold status-line-fast.cjs BG_REFRESH_THRESHOLD_MS 10 s
Tmux refresh interval ~/.tmux.conf status-interval 5 s
status-right-length ~/.tmux.conf 200
codepoint-widths ~/.tmux.conf see Tmux codepoint-widths below

Tmux codepoint-widths

Every Unicode-10+ emoji the statusline can emit needs an explicit width override in tmux because tmux's bundled wcwidth table predates Unicode 10 and silently treats new codepoints as width=1, even when they are East-Asian-Width Wide. Each disagreement leaks one cell of the previous render through to the right edge, producing the recurring trailing-digit residue (07:538, 08:054, 16:3175). Add this to ~/.tmux.conf:

set -g codepoint-widths "U+26A0=2,U+FE0F=0,U+1F7E0=2,U+1F7E1=2,U+1F7E2=2,U+1F7E4=2,U+1F9E0=2,U+1F9EE=2,U+1F976=2"

After editing, run tmux source-file ~/.tmux.conf. New tmux sessions inherit it immediately. The full mapping:

Codepoint Glyph Block Reason for override
U+26A0=2 Misc Symbols (Unicode 4) EAW=Ambiguous — tmux counts 1, terminals render 2
U+FE0F=0 (VS16) Variation Selectors Variation selector; tmux counts 1, renderer treats as part of the previous codepoint
U+1F7E0=2 🟠 Geometric Shapes Ext (Unicode 12) Predates tmux's wcwidth table
U+1F7E1=2 🟡 Geometric Shapes Ext (Unicode 12) Predates tmux's wcwidth table
U+1F7E2=2 🟢 Geometric Shapes Ext (Unicode 12) Predates tmux's wcwidth table
U+1F7E4=2 🟤 Geometric Shapes Ext (Unicode 12) Predates tmux's wcwidth table
U+1F9E0=2 🧠 Supplemental Symbols (Unicode 10) Predates tmux's wcwidth table
U+1F9EE=2 🧮 Supplemental Symbols (Unicode 11) Predates tmux's wcwidth table
U+1F976=2 🥶 Supplemental Symbols (Unicode 11) Predates tmux's wcwidth table

Why fix in tmux, not the script. Several earlier attempts modified visibleCellWidth() in scripts/combined-status-line.js to compensate for the disagreement and introduced new disagreements each time. The script's count is correct for the codepoints it currently handles — the issue is downstream in tmux's wcwidth table, fixable only via codepoint-widths. Do not touch the script's width math. If a new Ambiguous emoji enters the statusline repertoire and residue returns, probe its tmux-vs-terminal width with the snippet in Troubleshooting → Right-edge residue and append it to the override.

Retired lifecycle emojis. 🌲 (Unicode 6, OK), 🫒 (U+1FAD2, Unicode 13), and 🪨 (U+1FAA8, Unicode 13) used to sit in the cooling/fading/dormant tiers. The Unicode-13 codepoints were too new for tmux's wcwidth table to know about even with codepoint-widths overrides (tmux 3.4 silently ignores codepoints it can't normalize). They have been replaced by the colored circles (🟠/🟤) listed above, which are all on stable Geometric-Shapes-Ext blocks that every renderer agrees about.


Terminal Title Broadcasting

How It Works

Every 15 seconds, the statusline-health-monitor broadcasts status to all Claude session terminals via ANSI escape codes:

Terminal Tab: "C🟢 | UT🟤 CA🟠"
              ↑          ↑
        Current     Other active sessions
        project     (all sessions shown)

Terminal Compatibility

Terminal Status Notes
iTerm2 ✅ Works Full OSC 0 support
Terminal.app ✅ Works Native macOS terminal
VS Code Terminal ❌ Limited Does not process OSC 0 from external TTY writes
tmux ✅ Works Primary rendering target — all agents run inside tmux

Troubleshooting

Status bar completely blank?

# Check cache file freshness
ls -la .logs/combined-status-line-cache.txt

# Test fast-path directly (should complete in <100ms)
time node scripts/status-line-fast.cjs

# Force full refresh
node scripts/combined-status-line.js

# Check for process spawn storm (should be <80 Node processes)
ps aux | grep node | wc -l

# If >100 processes, kill the coordinator and let GPS restart cleanly
ps aux | grep global-service-coordinator | grep -v grep

Status line not updating?

# Check the coordinator (Phase 33 SoT)
curl -fs http://localhost:3034/health/state | jq '.generated_at, .lsl_by_project'

# Trigger an explicit one-shot verifier run (writes a verify_run signal to coordinator)
node scripts/health-verifier.js verify

# Force a fresh render (clears the per-project cache)
rm -f .logs/combined-status-line-cache-*.txt
node scripts/combined-status-line.js

Note: there is no longer a host-side health-verifier daemon. verify, status, and report are the only supported subcommands; start was removed in plan 33-04 when the coordinator at :3034 took over lifecycle. If you still see a monitoring:health-verifier STOPPED line on the dashboard, your supervisord config is pre-Phase-33 — the program block was retired alongside browser-access.

Wrong project showing as active?

# Check LSL registry
cat .lsl/global-registry.json | jq '.'

# Verify activity timestamps
cat .lsl/global-registry.json | jq '.sessions[] | {project, last_activity}'

Session not showing that should be?

# Check if agent process is detected (claude, copilot, opencode)
ps -eo pid,comm | awk '/claude$|copilot$|opencode$/ {print}'

# Check if the agent's cwd resolves to the right project
lsof -p <PID> 2>/dev/null | grep cwd

# Check if transcript monitor is running for that project
ps aux | grep enhanced-transcript-monitor | grep PROJECT_NAME

# Sessions show if: agent process running OR transcript monitor running

Right edge shows residual chars (e.g. 12:411, 13:0656)?

The persistent fix is set -g codepoint-widths "..." in ~/.tmux.conf — see Tmux codepoint-widths for the full mapping. Verify yours matches:

# Verify the override is loaded (should print 9 codepoints, all =2 or =0)
tmux show-options -g codepoint-widths

# If it's missing entirely, add the line from the docs and reload:
tmux source-file ~/.tmux.conf

If the override is present and residue still appears, a new emoji has entered the statusline repertoire whose width tmux doesn't know about. Probe its tmux-vs-terminal width with the snippet below — if truncate(1) returns the emoji itself (rather than empty), tmux is counting 1 cell and needs a U+XXXX=2 entry appended:

emoji_vs=$(node -e "process.stdout.write(String.fromCodePoint(0xXXXX) + String.fromCodePoint(0xFE0F) + 'XYZ')")
tmux set-environment -g TT "$emoji_vs"
for n in 1 2 3 4; do
  out=$(tmux display-message -p "#{=$n:TT}")
  printf "truncate(%d) = %q\n" "$n" "$out"
done

Do not modify scripts/combined-status-line.js's visibleCellWidth() to compensate — multiple past attempts (2026-05-09, 10, 12) introduced new disagreements. The script's count is correct for its repertoire; the fix is always in tmux config.

You can also verify the legacy script-side mitigations are still in place (they're defense-in-depth, not load-bearing now):

# Wrapper preserves trailing whitespace (must NOT do .trim())
grep -n 'rstrip\|trim()' scripts/combined-status-line-wrapper.js

# Producer pads to TMUX_PANE_WIDTH (or 200) via leftPadToStableCellWidth
grep -n 'leftPadToStableCellWidth' scripts/combined-status-line.js

# Cache file isn't truncated mid-codepoint
xxd .logs/combined-status-line-cache-coding-w*.txt | tail -1

Key Files

Core System:

File Purpose
scripts/tmux-session-wrapper.sh Wraps all agents in a tmux session with unified status bar
scripts/combined-status-line-wrapper.js Cache fast-path reader invoked by tmux status-right
scripts/combined-status-line.js Full status line renderer; writes per-project cache
scripts/health-coordinator.js Phase 33 SoT — collects signals at :3034, exposes /health/state
scripts/health-verifier.js Reporter-mode CLI: verify, status, report (no daemon)
scripts/enhanced-transcript-monitor.js Per-project ETM; POSTs lsl_heartbeat signals to coordinator
.logs/combined-status-line-cache-<project>-w<paneWidth>.txt Per-(pane, width) pre-rendered status cache

Retired (do not write/read):

File Replaced by
.health/verification-status.json Coordinator /health/state
.logs/statusline-health-status.txt Coordinator /health/state (sessions block)
.lsl/global-registry.json Coordinator lsl map
[program:health-verifier] supervisord block Removed in 33-04 — start subcommand no longer exists
[program:browser-access] supervisord block Removed; replaced by Playwright-via-CLI (/gsd-browser)

Configuration:

File Purpose
~/.tmux.conf status-right-length, status-interval, status-right invocation
config/live-logging-config.json Provider config