# ClippyAuto πŸ“ŽπŸš— β€” Final Spec (3 Rounds Complete) ## Vision A lightweight Android app purpose-built for voice-first interaction with Clippy via Matrix, optimized for Android Auto commute use. ## Core User Story > As Jeremy, I get in my car, Android Auto launches, I tap "Clippy" (or it auto-starts), and I'm in a live voice conversation with my AI Chief of Staff. I talk, Clippy responds in my car speakers within seconds. No typing, no "Hey Google," no Element. --- ## Locked Decisions | Decision | Resolution | Source | |---|---|---| | Language | Kotlin | Spirit R1 | | Matrix SDK | Raw REST API (Ktor) β€” no E2EE needed for private homeserver | Spirit R3 | | STT | Android SpeechRecognizer (Vosk offline fallback in v2) | Spirit R1 | | TTS | Android native TextToSpeech | Spirit R1 | | Audio focus | AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK / USAGE_ASSISTANT | Spirit R1 | | Auth | Access token in Android Keystore | Spirit R1 | | Push delivery | FCM via n8n + Matrix sync dual-path, first wins | Clippy R2 | | Music integration | None β€” Android handles ducking | Margaret R2 | | Private mode | Voice command "go quiet" / "speak" | Margaret R2 | | Interruption | 200ms TTS fade, activate STT, cancel queued chunks | Clippy R2 | | Delegation | Always confirm verbally before sending | Clippy R2 | | Audio cue (heard you) | Subtle chime at 2s silence mark, not voice | Clippy R2 | | Thinking indicator | Low ambient tone after 3s processing | Clippy R2 | | Session resume | < 15 min = resume, > 15 min = new session | Margaret R2, Clippy R2 | | Sentence chunking | v1 scope β€” each sentence as separate Matrix message | Clippy R2 | | Dedicated room | #clippy-voice with custom event fields | Margaret R2 | | E2EE | Skip for v1 (private homeserver, invite-only room) | Clippy R3 | --- ## Functional Requirements ### FR-1: Voice Input - On-device STT via Android SpeechRecognizer - Push-to-talk button on Android Auto screen (large, driver-safe) - 2-second silence threshold with subtle confirmation chime - Cancel-send if Jeremy resumes talking within 1s of chime ### FR-2: Matrix Integration - Raw REST API client (Ktor) β€” login, sync, send - Authenticates as Jeremy (dedicated device token) - Sends transcribed text to #clippy-voice room - Custom event fields: `farm.hobbs.clippy.source`, `farm.hobbs.clippy.session_id`, `farm.hobbs.clippy.signal` ### FR-3: Voice Output - Android native TextToSpeech - Plays through car audio via USAGE_ASSISTANT audio attributes - Queues chunked responses, plays sequentially - 200ms fade on interruption (not hard cut) ### FR-4: Android Auto Integration - CarAppService with custom Screen template - Minimal UI: status indicator (listening/thinking/speaking), talk button - Auto-launch option when Android Auto connects (FR-6) - Complies with driver distraction guidelines ### FR-5: Session Awareness - session_start signal on app open β†’ triggers arrival briefing - session_end signal on close/timeout β†’ triggers close summary - session_resume for reconnects < 15 min ### FR-6: Auto-Launch - Start ClippyAuto automatically when Android Auto connects ### FR-7: Session Context Injection - App sends `[ClippyAuto session started β€” commute mode, voice-first]` to Matrix room - Clippy shifts to debrief mode automatically ### FR-8: Debrief Summary - On session end, Clippy posts written summary (formatted, markdown OK) - Phone UI surfaces as notification: "Debrief summary ready" ### FR-9: Voice Commands | Command | Action | |---|---| | "Clippy, go quiet" | Mute TTS, switch to phone text display | | "Clippy, I'm back" / "speak" | Resume voice mode | | "Clippy, wrap it up" | Trigger session close + summary | | "Clippy, remind me [thing]" | Create reminder in memory | | "Clippy, tell [agent] [message]" | Delegation with verbal confirmation | ### FR-10: Silence Detection Tuning - 2s pause = processing trigger - Subtle confirmation chime at 2s mark - If speech resumes within 1s of chime, cancel send and keep listening ### FR-11: Response Length Guardrails - App-side: max 250 chars per TTS chunk - Split responses > 500 chars into segments with brief pause - Clippy enforces 2-sentence max per message in voice mode --- ## Non-Functional Requirements | ID | Requirement | Target | |---|---|---| | NFR-1 | Time to first audio | < 5 seconds (with FCM + chunking) | | NFR-2 | Offline grace | Queue messages, notify verbally, resume on reconnect | | NFR-3 | Battery | Minimal drain; only active in foreground | | NFR-4 | Privacy | All voice processing on-device; only text hits network | | NFR-5 | Eyes-free | Zero required visual interactions after launch | --- ## Architecture ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Android Auto Screen β”‚ β”‚ [ πŸŽ™οΈ Talk to Clippy ] [Status] β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ ClippyAuto App β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ STT Engineβ”‚ β”‚ TTS Engine β”‚ β”‚ β”‚ β”‚ (on-device)β”‚ β”‚ (on-device) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ text β”‚ text β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ Matrix REST Client (Ktor) β”‚ β”‚ β”‚ β”‚ Room: #clippy-voice β”‚ β”‚ β”‚ β”‚ + FCM receiver (fast path) β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”‚ mess.hobbs.farm:8008 β”‚ β”‚ Matrix Synapse β”‚ β”‚ β”‚ β”‚ β”‚ Push gateway β†’ n8n β”‚ β”‚ β”‚ β”‚ β”‚ β–Ό β”‚ β”‚ FCM data message β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–²β”€β”€β”€β”€β”˜ β”‚ β”‚ β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”‚ Clippy (OpenClaw Agent) β”‚ β”‚ Claude β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Push Path (Fast) ``` Clippy sends message β†’ Synapse push gateway β†’ n8n webhook β†’ n8n filters (sender == @clippy) β†’ FCM data message β†’ app β†’ TTS Latency: ~400-700ms from Clippy message to TTS start ``` ### Sync Path (Fallback) ``` Clippy sends message β†’ Matrix sync long-poll β†’ app β†’ TTS Latency: ~1-2s (depends on poll timing) ``` Both paths active; first to deliver wins; dedup via hash. --- ## Latency Budget | Step | Target | Notes | |---|---|---| | STT | 800ms | On-device | | Matrix send | 400ms | Cellular | | Claude to first sentence | 1.5-2.5s | Sentence chunking | | FCM delivery | 300ms | High priority data message | | TTS start | 300ms | Pre-initialized | | **Total to first audio** | **3.3-4.3s** | 🎯 Under 5 seconds | --- ## Voice State Machine ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ IDLE │◄──────────────────────┐ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ tap button β”‚ TTS complete β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β” β”‚LISTENING β”‚ β”‚ SPEAKING β”‚ β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β–²β”€β”€β”€β”€β”€β”˜ β”‚ 2s silence + chime β”‚ response received β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β” β”‚PROCESSING │────────────────►│ RECEIVING β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ sent to Matrix β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` --- ## Connectivity Handling | Scenario | Behavior | |---|---| | Tunnel / dead zone | Queue outgoing, TTS: "Signal dropped, I'll catch up." | | Clippy slow (>10s) | TTS: "Still thinking on that one..." | | Bluetooth disconnect | Pause session, resume on reconnect | | Resume < 15 min | session_resume, "Welcome back" | | Resume > 15 min | session_start, fresh arrival briefing | --- ## Clippy Voice Mode Prompt (appended to SOUL.md during voice sessions) ``` VOICE MODE RULES: 1. Max 2 sentences per message. Hard limit. 2. Send each sentence as a separate Matrix message (enables chunked TTS). 3. No markdown. No bullet points. No formatting. Plain speech only. 4. No emoji in voice responses. 5. Numbers: say "three" not "3". Say "two thirty" not "2:30 PM". 6. Names: full name first mention, first name after. 7. Don't say "let me think about that" β€” just think and respond. 8. Lists conversationally: "You've got three things: X, Y, and Z." ARRIVAL BRIEFING (on session_start): Message 1: "Hey Jeremy. [Most urgent/time-sensitive thing]." Message 2: "[One open loop from last session or overnight change]." Then: silence. SESSION CLOSE (on "wrap it up" or session_end): Message 1: "Alright, here's what I've got. [Action items conversationally]." Message 2: "[Anything unresolved carrying to next session]." Then: post written markdown summary to room. SESSION RESUME (on session_resume): Message 1: "Welcome back. [Where we left off, one sentence]." Then: silence. ``` --- ## Security | Concern | Mitigation | |---|---| | Token storage | Android Keystore (hardware-backed) | | Network | TLS to homeserver | | FCM payload | Data message only, no sensitive text in notification | | Voice data | Never stored; STT real-time, audio buffer discarded | | Phone screen | Locked during Android Auto; transcript after session | --- ## v1 MVP Scope **Ships:** - Push-to-talk Android Auto interface - On-device STT β†’ Matrix REST β†’ Clippy β†’ FCM β†’ TTS - Sentence-level chunked responses (sub-5s first audio) - Arrival briefing, session close summary - Voice commands (quiet/speak/wrap up/remind/delegate) - Private/passenger mode - Connectivity handling - Dedicated #clippy-voice Matrix room - Minimal phone UI (setup + session active) **v2 Backlog:** - Wake word ("Hey Clippy") via Porcupine - Google Calendar integration - Direct SSE streaming endpoint - Offline STT (Vosk fallback) - Full phone transcript review UI - Agent overnight digest system --- ## Build Estimate | Component | Days | |---|---| | Project setup + Android Auto scaffold | 2 | | Matrix REST client + auth | 1.5 | | Voice pipeline (STT + TTS + state machine) | 3 | | FCM push path (app side) | 1 | | n8n workflow (server side) | 0.5 | | Clippy voice mode prompt tuning | 1 | | Android Auto UI | 2 | | Phone UI (minimal) | 1 | | Testing + polish | 3 | | **Total** | **~15 days** | --- ## Open Questions for Jeremy 1. **Build approach** β€” Who builds this? Spirit scaffolds? Jeremy builds with our spec? Fork existing project? 2. **Wake word** β€” "Hey Clippy" for v2, or push-to-talk fine indefinitely? 3. **Which Google Calendar(s)** for v2? 4. **Agent overnight reports** β€” Want Margaret/Spirit posting daily summaries for Clippy to pull from? 5. **Homeserver external access** β€” Is mess.hobbs.farm already reachable via Traefik externally? 6. **Firebase project** β€” Existing one or create new? 7. **Testing** β€” Wired Android Auto first, or straight to wireless?