diff --git a/CLAUDE.md b/CLAUDE.md index c040c39..3dd7d88 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,8 +77,8 @@ Pure-Go packages (`router`, `memory`, `mavweb`, …) run under a plain `go test | `mavweb` | HTTP UI + PWA (`/dash`, `/history`, `/trace`, `/notifications`, `/tools`); WebAuthn auth. Connects to mavend's socket. | | `mavsttd` | Speech-to-text (whisper.cpp, CGO). | | `mavttsd` | Text-to-speech (piper subprocess). | -| `mavwaked` | Wake-word / VAD gate. | -| `mavenclient` | Voice loop client (mic → stt → core → tts). | +| `mavwaked` | Wake-word / VAD gate. **Not on homesrv** — see below. | +| `mavenclient` | Voice loop client (mic → stt → core → tts). **Not on homesrv** — see below. | | `mavpoll` | Telegram long-poll reach. | | `mavcaldav` | CalDAV calendar sync. | | `mavmaild` | Mail reader (IMAP, read-only). Holds the IMAP password; core never sees it. | @@ -87,6 +87,15 @@ Daemons are wired socket-to-socket, not linked. `internal/ipc` is the client/ser protocol; the config in `deploy/mavend.json` (with `${VAR}` env expansion from gitignored `deploy/telegram.env`) sets socket paths, model paths, and the phraser/embedder blocks. +**Seven of the nine run on homesrv. `mavwaked` and `mavenclient` do not, and that is the +decision, not an oversight** (Vikunja #463, `docs/plans/17-where-the-voice-loop-runs.md`). +homesrv has a microphone — it is a laptop — but it is in the wrong room, so a wake-word +daemon there listens to nobody. They belong on a client machine where he is standing. +`ipc.Dial` already takes `tcp://host:port?token=...` through the netaddr seam, so nothing +needs building to allow it, but no such machine exists yet. **The consequence: the wake +word and the VAD gate are covered by unit tests and by nothing else, and no amount of +sitting at the box changes that.** Push-to-talk through `/dash` is what QA actually covers. + ## The ecosystem: Nexus, Praxis, Hexis Maven is one of four services. It owns conversation and personal memory. It does not diff --git a/docs/plans/17-where-the-voice-loop-runs.md b/docs/plans/17-where-the-voice-loop-runs.md new file mode 100644 index 0000000..e79fd6c --- /dev/null +++ b/docs/plans/17-where-the-voice-loop-runs.md @@ -0,0 +1,60 @@ +# Plan: Where mavwaked and mavenclient run + +**The decision Vikunja #463 asked for. Written 04-08-2026.** + +**Verdict: not in compose on homesrv. They run on a client machine in the room he is in.** +The transport for that already exists and nothing needs building to allow it. What needs +building is a way to check the wake path at all, which is a separate task. + +## The state that prompted this + +`docker-compose.yml` runs mavend, mavsttd, mavttsd, mavweb and mavpoll. `mavwaked` and +`mavenclient` appear in no compose file and run as no host process. Both build under +`make build`. So the wake word and the voice-activity gate are untested by construction: +QA session 1 step 2 covers push-to-talk from `/dash` only, and #287 (voice session +quality) can never be more than half-answered while this holds. + +## The reason is not hardware + +homesrv has a microphone. It is a Lenovo IdeaPad 5 Pro, and `/proc/asound/cards` lists +the ACP digital mic array with capture devices at `/dev/snd/pcmC1D0c` and +`/dev/snd/pcmC2D0c`. Adding `/dev/snd` to compose and joining the `audio` group would +work. + +It would also be pointless. A wake-word daemon is worth having in the room he is standing +in. homesrv is a server, so its microphone hears the room the server is in, which is not +where anybody talks to Maven. Wiring audio into a container to listen to an empty room is +work spent on a capability nobody can use. + +## The reason it belongs off-box + +`mavenclient` is a client by name and by design: microphone, then STT, then core, then +TTS. It is the one binary in the tree meant to run somewhere else. `mavwaked` is the gate +in front of it, so it goes wherever the microphone goes. + +Maven already has components that are not containers on homesrv. `mavupdate` is a +host-side tool. The resident model, STT and TTS prefer the workstation and fall back to +homesrv (`docs/offload.md`). Off-box is a shape this system already has. + +**And the wire already supports it.** `ipc.Dial` takes a netaddr seam address: +a bare path is the unix socket, and `tcp://host:port?token=...` reaches a core on another +host, with the token checked in `internal/netaddr` before `internal/ipc` sees the +connection. So a client machine reaching mavend over wg or the LAN needs no new protocol +work. It needs mavend to listen on TCP, which `deploy/mavend.json` does not currently ask +for. + +## What this means for the QA plan + +QA session 1 step 2 should say what it actually covers, which is push-to-talk through +`/dash`. It should not read as though it covers the voice loop. The wake path is checked +on the client machine or it is not checked, and today there is no client machine. + +That is the honest state, and it is worse than the task suggests: this is not a +configuration gap that a compose entry closes. Until a machine with a microphone runs +`mavwaked` and `mavenclient` against a TCP-listening mavend, `internal/wake` and +`cmd/mavwaked`'s VAD are covered by their unit tests and by nothing else. + +## What was wrong in CLAUDE.md + +The daemon table lists all nine binaries with no column for where they run, which is how +this went unnoticed for as long as it did. It now says which two are not on the box. diff --git a/docs/qa.md b/docs/qa.md index 500e9b6..293fb6f 100644 --- a/docs/qa.md +++ b/docs/qa.md @@ -105,9 +105,9 @@ turns look misaligned when they are not. **Passes** (02-08-2026, five turns): `я рада`, `поняла`, `помогла`, `проверила`, `записала`, `грустна`, `ты` throughout, no pet names. 2. Press push-to-talk on `/dash`. Say `привет`. Confirm a spoken reply comes - back. This is the only check that covers mic to STT to core to TTS to - speaker as one path. It is also the path the eleven-day outage most likely - broke. + back. This covers browser mic to STT to core to TTS as one path. It does + **not** cover the wake word or the voice-activity gate, and no step here + does — see below. 3. Say `тихий режим`. Expect `тихий режим включён. буду реже напоминать.` **Passes.** 4. Say `выключи тихий режим`. Expect `тихий режим выключен.` Negation must win. **Passes.** 5. Say `в комнате тихо`. Quiet mode must NOT flip. Confirm on `/history` that no @@ -129,11 +129,18 @@ turns look misaligned when they are not. and stitch unrelated topics. Asked whether he should move flats, she opened with the weather. That is 287, and it is a phrasing problem, not a loop problem. -**The wake path cannot be checked as deployed.** `mavwaked` and `mavenclient` -appear in no compose file and run as no host process. Step 2 covers only -push-to-talk, from `/dash` through mavsttd and mavttsd. Wake word and VAD -are untested by construction. Decide whether they belong in compose or on a -client machine, and say which in the deploy docs. Tracked as **463**. +**The wake path cannot be checked here, and that is now the decision rather +than a gap.** `mavwaked` and `mavenclient` appear in no compose file and run as +no host process. They are not going to. They belong on a client machine in the +room he is standing in, because homesrv's microphone is real and in the wrong +room — **463**, written up in `docs/plans/17-where-the-voice-loop-runs.md`. + +So the wake word and the VAD gate are covered by their unit tests and by +nothing else, and no session at this box changes that. Checking them needs a +machine with a microphone running both binaries against a TCP-listening mavend. +`ipc.Dial` already speaks `tcp://host:port?token=...`, so the work is a machine +and a config line, not protocol work. Until then, **287** can only be +half-answered, and step 2 above is push-to-talk, not the voice loop. **319's single-token bug is fixed** (01-08-2026). Single-word Russian utterances no longer come back as `не совсем поняла — можешь переформулировать?`. `привет` and `поужинал`