docs: decide where mavwaked and mavenclient run — not on homesrv (V-463)

They appear in no compose file and run as no host process, and the task
asked whether that is a gap to close or a decision to write down. It is a
decision.

The reason is not hardware. homesrv is a Lenovo laptop and
/proc/asound/cards lists its ACP mic array with capture devices, so
passing /dev/snd into a container would work. It would also listen to an
empty room. A wake-word daemon is worth having where he is standing, and
that is not where the server is.

mavenclient is a client by name and design, mavwaked is the gate in
front of it, and the wire already reaches off-box: ipc.Dial takes
tcp://host:port?token=... through the netaddr seam, with the token
checked before internal/ipc sees the connection. So this needs a machine
and a config line, not protocol work.

The honest consequence is worse than the task suggested, and both docs
now say it: the wake word and the VAD gate are covered by unit tests and
by nothing else. QA session 1 step 2 was reworded to claim only what it
checks, which is push-to-talk through /dash. CLAUDE.md listed all nine
binaries with no column for where they run, which is how this went
unnoticed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 06:11:16 +04:00
parent 554181ccbd
commit 86dcd99de2
3 changed files with 86 additions and 10 deletions
+11 -2
View File
@@ -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
@@ -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.
+15 -8
View File
@@ -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 `поужинал`