# Russian acts reach tools **06-08-2026. V-633.** Measured with `TestONNXBaseline`, 91-case RU routing fixture, classifier plus the ONNX embedder. No LLM arm in this run. ## What was wrong Three defects, tangled enough that fixing one alone would have looked like progress. **No Russian utterance could reach a tool.** `DefaultActMatcher` in `internal/router/slots.go` matched an exact English prefix, and `internal/tool.Matcher` delegated straight to it. Its comment claimed "the production matcher is fuzzy, this is the scaffold floor". There is no other matcher, and `DefaultGrammars` is the only place `Slots.Fn` is set at stage 0, so the floor was the ceiling. Measured with a throwaway matcher test over the seeds: ```text "покажи статус nginx" ok=false "restart nginx" ok=true fn=restart "сколько места на диске" ok=false "disk" ok=true fn=disk "свободная память" ok=false "uptime" ok=true fn=uptime "перезагрузи роутер" ok=false ``` 55 of the 69 lines in `models/seeds/act.txt` routed to `IntentAct` and then fell to `proposeGap`. Praxis was never affected: `PraxisGrammars` fills `Slots.Fn` itself. **Seven lines were duplicated inside `models/seeds/query.txt`.** A duplicate is a second identical vector, so it double-weights its region in nearest-neighbour scoring. ```text сколько человек дома кто сейчас дома какая загрузка процессора сколько свободного места на диске какой ip адрес у сервера какая версия софта сколько оперативной памяти свободно ``` **`как дела у сервера` carried two labels**, in `query.txt:13` and `system.txt:9`. One string, two identical vectors, disagreeing about the answer. ## The change Tools carry spoken aliases as config data, in `deploy/mavend.json`. They are not a Russian stem pattern in code, which CLAUDE.md forbids. They are not on the tool row either. An ad-hoc tool enabled through `/tools` has no aliases and needs none. Aliases and names compete in one table, longest phrase first, so "перезагрузи контейнер" beats "перезагрузи" and "docker-restart" is not shadowed by "restart". Matching is on exact leading tokens rather than lemmas. `перезагрузи роутер` is a command and `перезагрузил роутер` is a fact, and a lemma cannot tell the two apart. That is the trap `cmd/mavend/quiet_toggle.go` documents for `говори`. The seven duplicates are gone, and `как дела у сервера` stays in `query.txt` only. It left `system.txt` because system cannot answer it: `replySystem`'s память/загрузк/аптайм arm returns "системная статистика пока не подключена." and always did. That arm is a stub, not a mode, so the mode inventory now lists the shape as `act.tool.hoststats`. ## Result **69/91, 75.8% full, unchanged.** Clarify counts unchanged at 0 false and 8 missed. Nothing moved, and that is the honest number. The fixture holds no host-stat case and no Russian act that reaches a tool, so it cannot see either fix. The new coverage is `TestActMatcherAliases`, which asserts the twelve utterances above plus the two refusals. ## What this does not fix Argument quality. `статус sshd` reaches `systemctl status sshd`, but `логи nginx` reaches `journalctl -n 50 -u nginx` only because the tool's argv prefix ends in `-u`. An alias whose remainder is a Russian noun ("перезагрузи роутер") hands `systemctl restart роутер` a target that does not exist. Free text still reaches an argv, which is the resolution rule the ecosystem contract states for Hexis and not yet true here. The fixture cannot measure any of this. That is the observability gap V-629 is for.