diff --git a/scripts/claude-statusline-command.sh b/scripts/claude-statusline-command.sh index 69b26ea..f4b30d0 100755 --- a/scripts/claude-statusline-command.sh +++ b/scripts/claude-statusline-command.sh @@ -126,7 +126,7 @@ if [ -n "$cwd" ]; then fi fi -# --- althing: unread count (📬 N) + waiter-armed (🔔 armed / 🔕 not) --- +# --- althing: unread count (📬 N) + reachability (🔔 / 🔕 / 📵 outage) --- # v3 (the post office, 2026-08-28). ⚠ This block used to gate on # `command -v althing-cli`, which the v3 cutover DELETED -- so the whole segment, # badge and bell both, silently disappeared for every session on this box. That is @@ -164,16 +164,38 @@ if command -v postbox >/dev/null 2>&1; then # `timeout` is load-bearing, not belt-and-braces: v2 read a local SQLite file, # v3 makes an HTTP call. An unreachable post office must cost this segment two # seconds and nothing else — a statusline that hangs blocks the whole prompt. - unread=$(timeout 2 postbox --handle "$h" status --json 2>/dev/null /dev/null) + # ONE call, both fields. The status payload already carries `reachable`; + # the previous version fetched it and threw it away. + st=$(timeout 2 postbox --handle "$h" status --json 2>/dev/null /dev/null) case "${unread:-0}" in ''|0|*[!0-9]*) : ;; *) althing="📬 $unread" ;; esac - # v3 has ONE arming mechanism where v2 had three: `althing-listen` takes - # wake-listener-.lock. monitor-*.lock and light-monitor-*.lock belonged - # to binaries that no longer exist. kill -0 discards a crashed listener's lock. - mon="🔕" - lk="$HOME/.althing/wake-listener-$h.lock" - if [ -f "$lk" ]; then - pid=$(tr -dc '0-9' < "$lk" 2>/dev/null) - [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null && mon="🔔" + + # ⚠ ASK THE POST OFFICE WHETHER THIS SEAT IS REACHABLE. Do not infer it + # from a local artifact. + # + # This bell used to report 🔔 iff `wake-listener-.lock` named a + # live pid — one MECHANISM, not the property. That was right while the + # FIFO waiter was the only channel and became wrong the moment 3.3.0 + # added `cc`: this very seat is reachable over its Claude Code socket, + # has no waiter lock at all, and rendered 🔕 while the post office said + # push/reachable. A pane-routed seat was wrong the same way before that. + # + # Asking the post office means this segment knows nothing about althing's + # internals — no lock paths, no channel names — so adding a fourth channel + # cannot make it stale. The old form also used `kill -0`, which proves a + # pid exists and not which process it is; see the identity-vs-liveness note + # in docs/runbooks/althing-deploy.md. + # + # THREE states, because an outage is not an empty inbox: + # 📵 the post office did not answer (timeout, down, wrong address) + # 🔔 reachable — a poke will arrive + # 🔕 declared but not reachable, or pull-only + if [ -z "$st" ]; then + mon="📵" + elif [ "$(printf '%s' "$st" | jq -r '.reachable // false' 2>/dev/null)" = "true" ]; then + mon="🔔" + else + mon="🔕" fi fi fi