Feedback no threads with subprocess run

Symptom (2026-04-29): Justin sent /orient to coordinator bot. Got back:

⚠️ Action(s) failed:
  - Orient failed: Sonnet didn't return parseable JSON. Raw:
What I tried to say: Error: [Errno 8] Exec format error: '/home/justinwieb/.nvm/versions/node/v20.20.0/bin/claude'
Same claude binary called from CLI worked fine. Brain-CLI (no bot context) worked fine. Only failed in the running bot service.

Root cause: the bot used threading.Thread to refresh Telegram's typing indicator every 4s during long brain calls. The thread did requests.post(...). While that thread was inside requests / urllib3 / OpenSSL native code, the MAIN thread called subprocess.run([CLAUDE_BIN, ...]). Python's subprocess on Linux uses fork+exec; if the parent process has multiple threads, internal glibc / native-library mutexes can be in a state that confuses the kernel's exec at the moment fork lands. Result: ENOEXEC on a valid binary.

Fix: removed the heartbeat thread entirely. The 👀 reaction provides the persistent visual ack; typing indicator fades after 5s but that's acceptable. Code marked clearly so future sessions don't reintroduce.

Don't apply this pattern in forge: - threading.Thread + requests.post - threading.Thread + any native-extension I/O (psycopg, openssl, ssl-wrapped sockets) - ... while the main thread will subprocess.run anything

Safe alternatives if a heartbeat-style behavior is needed: - Spawn a separate process (multiprocessing.Process with start_method="spawn", or subprocess.Popen of a small pinger script). Process boundary isolates the fork race. - Use asyncio with a single-threaded event loop instead of threading. asyncio coroutines don't fork-trap because they all share one OS thread. - Just live without the refresh; UX cost is small.

Lookup hints for future debugging: if you ever see OSError: [Errno 8] Exec format error on a binary that works from the shell, suspect threading + fork. Diagnose by running the same code path single-threaded (CLI, removed threads). If it works there, you found it.

[Claude Code, bot-refiner-v2, 2026-04-29]