fix(supervision): treat NBSP as whitespace in composer detection#78
fix(supervision): treat NBSP as whitespace in composer detection#78wyattwalter wants to merge 1 commit into
Conversation
The claude composer pads its prompt with a non-breaking space (U+00A0), which POSIX [:space:] does not match. An idle "❯<NBSP>" prompt therefore survived the trim in fm_tmux_composer_state, never matched the bare-prompt "empty" case, and classified as pending input. This wedged the away-mode daemon (the composer guard deferred every escalation) and tripped fm-send's swallowed-Enter check on every idle pane. Normalize U+00A0 to a space before trimming.
|
Note on portability — why this may not reproduce everywhere: The trim in
So on glibc, neither locale strips the NBSP. BSD libc (macOS) and some fuller locales may classify U+00A0 as whitespace, in which case the trim already catches it and the symptom never appears. The NBSP padding itself comes from the Claude TUI (the same binary across platforms), so the trigger is platform-independent; only whether the trim catches it varies by locale/libc. That asymmetry is probably why it can sit unnoticed depending on the dev environment. The fix is deliberately locale-independent: it replaces the literal UTF-8 bytes ( |
Problem
The composer-state detector (
fm_tmux_composer_stateinbin/fm-tmux-lib.sh) misclassifies an idle Claude composer as holding pending input. Two consequences:/afksilently delivers nothing for the entire away period (the durable queue preserves it, but the proactive notify path is dead). This is theafk-invx-i5failure mode reopening after a TUI rendering change.fm-send.shshares the detector, so it reports a false "Enter swallowed; text left in composer" on every steer into an idle pane, even when the message submitted fine.Root cause
The current Claude TUI pads its prompt with a non-breaking space (U+00A0) rather than an ASCII space, rendering the idle composer row as
❯<U+00A0>. The detector strips the box-border glyphs and then trims whitespace with POSIX[:space:], which does not match U+00A0. The trailing NBSP survives the trim, sostrippedis❯<U+00A0>rather than a bare❯, never matches the bare-prompt "empty" case, and falls through topending.Reproduced live:
fm_tmux_composer_statereturnspendingfor known-idle Claude panes; the stripped value's hex ise2 9d af c2 a0(❯+ U+00A0).Fix
Normalize U+00A0 to an ASCII space before the trim, alongside the existing border-glyph strips. After the fix, the same idle panes classify
empty.Minimal and harness-generic; does not affect busy detection or genuine pending input.