Files
claude-projects/claude-config/config/prompts/voice/V3b_primary_topology_ready_gate.md
T
2026-10-02 00:19:54 -05:00

5.6 KiB

V3b — Voice Chat: Claude Code on PRIMARY + "ready gate" before speaking (spawned 2026-10-01, main session 771b2744)

Bounded background agent, personal_projects #214. Budget: --max-turns 60. Same tool call twice with identical arguments → STOP with a partial wrap-up.

STATE: V3 built ~/voice-chat on the LAPTOP (ssh administrator@192.168.1.89, key auth; Omarchy/Hyprland, Python 3.14, PipeWire, tmux) assuming Claude Code runs on the laptop. Read ~/voice-chat/README.md and code on the laptop first, and the build plan at /home/administrator/Desktop/claude/agent-builder/voice_chat_build_plan.md (primary-hosted topology). Servers: STT http://192.168.1.90:8300, TTS http://192.168.1.90:8004 (Chatterbox, jarvis.wav).

OWNER FACTS + DECISIONS (2026-10-01 22:52):

  • The owner runs Claude Code ON PRIMARY (192.168.1.88, host Daily-Driver-PC, user administrator), in a terminal on the laptop that is SSH'd into primary. Audio in/out happens on the LAPTOP.
  • D1 Input path: laptop push-to-talk (existing) → STT → text is typed into the Claude Code tmux pane ON PRIMARY via ssh administrator@192.168.1.88 tmux send-keys -t <target> -l <text> (no Enter unless auto_submit). Target discovery runs on primary (pane whose current command is claude). If the owner's Claude Code on primary is NOT in tmux today, document the one-time change (run claude inside tmux new -As claude) — do not change it yourself. Laptop→primary SSH: verify key auth from the laptop to primary works (ssh -o BatchMode=yes administrator@192.168.1.88 true from the laptop). If it doesn't, STAGE the exact key setup steps for the owner (no changes to authorized_keys by you).
  • D2 Output path: the Claude Code Stop hook runs ON PRIMARY (~/voice-chat-hook/ on primary, stdlib Python, venv not needed). It extracts the speakable summary (reuse V3's logic) and delivers it to the LAPTOP; the laptop fetches TTS and plays. Delivery = a tiny queue: primary appends a JSON line (text, session id, ts) to the laptop via ssh administrator@192.168.1.89 'voice-chat enqueue' (stdin) — non-blocking, never fails the hook (exit 0 always, short timeout), and falls back silently if the laptop is off. Primary→laptop SSH key auth exists (the main session uses it). No listening network ports.
  • D3 READY GATE (new owner requirement): the owner may be watching TV with headphones OFF. Replies must NOT play until the owner signals ready. Modes in config: gate = "ready_key" (default) | "immediate". In ready_key mode: when a reply is queued, the laptop shows a desktop notification "Claude has a reply (N waiting)" plus an optional short soft chime (config chime=false default) and waits. The owner presses a ready key (stage a Hyprland bind, e.g. SUPER+CTRL+K, calling voice-chat ready) → the queue plays in order. Additional commands: voice-chat skip (drop queued), voice-chat replay (repeat last), voice-chat gate immediate|ready_key. Queued items expire after a configurable time (default 30 min) but stay listed in voice-chat status. Push-to-talk itself also counts as ready (if the owner starts talking, queued replies play first? NO — decided: PTT does not auto-play; only the ready key plays).
  • D4 Keep everything user-level, no sudo, no daemons unless needed (a user systemd unit for the queue player on the laptop is acceptable if required; prefer none: voice-chat ready plays synchronously).
  • STAGE, do NOT activate: the primary Claude Code hook (~/.claude/settings.json ON PRIMARY — produce the snippet + an installer like V3's install_hook.py that the owner runs; do NOT run it on the real file; NOTE the main session's own Claude Code on primary uses that settings file, so activation must be the owner's choice), and the new Hyprland binds on the laptop (append-ready snippet). The laptop-side V3 hook (laptop ~/.claude) is no longer the primary path — keep it but mark it optional in the README. SELF-TESTS (no audio played aloud, no human voice):
  1. Input: Chatterbox-synthesised sentence → laptop STT → injected into a throwaway tmux session ON PRIMARY running cat (create it with tmux new -d -s voicechat-selftest cat on primary; kill it after) → assert text arrived.
  2. Output + gate: feed a sample Stop-hook JSON (documented schema; V3 already fetched it) to the primary hook → assert it returns < 0.2 s with exit 0 → assert the laptop queue has 1 item and nothing played → run voice-chat ready with a file sink (VOICE_CHAT_OUT) → assert a valid WAV of plausible length → queue empty. Also test immediate mode, skip, expiry (with a short test expiry), and laptop-offline behaviour (point delivery at an unreachable host → hook still exits 0 fast).
  3. Unit tests for queue/expiry/gate on the laptop; run V3's existing tests too. HARD RULES: no sudo (⏸ OWNER-COMMAND-REQUEST block if truly needed); do not touch the running Claude Code session, ~/.claude/settings.json on either host, Hyprland config, authorized_keys, or any server container; only write in ~/voice-chat (laptop), ~/voice-chat-hook (primary), and the README copy; no audio playback; no git. DELIVERABLES: updated laptop README + primary ~/voice-chat-hook/README.md; copy both to /opt/appdata/docker/docker-compose/voice-tts/ (LAPTOP_CLIENT_README.md, PRIMARY_HOOK_README.md). FINAL message = wrap-up JSON only: status, project "voice-chat", files_built[], staged[] (path, what, where, host), self_tests[] (name, result, numbers), owner_steps[] (exact, per host, in order), ssh_auth {laptop_to_primary, primary_to_laptop}, design_conflicts[], actions_taken[], actions_failed[], unverified_claims[], next_step, notes.