# Language: what the model emits, and how Russian is matched

*Last verified: 2026-08-09 @ a9b480a*

Two contracts live here. What a model call is allowed to return, and which
mechanism is allowed to recognise a Russian word.

## The LLM output contract

All phrasing paths emit `{"response":"...","mood":"..."}`. They fall back to
plain text when the model skips the JSON.

**One parser, `parseResponseMood` in `internal/phraser/parse.go`.** Every path
reaches it: the six `LLMPhraser` methods, `PhraseWorld`, and
`Replier.PhraseReply`. `cmd/mavend/replier_llm.go` wraps the last of those,
holds the stub fallback, and parses nothing itself. Mood is a fixed enum.

The router prompt is a separate contract:

```text
[{"intent":<enum>, key?, value?, text?, verb?}, ...]
```

over 7 intents: `fact, reminder, note, query, act, chat, system`.
`llm/check_prompt_parity.py` in the training workspace enforces that the Go
prompt and the relabelling prompt stay identical. They diverged once, and the
relabelled set then taught a head the Go router never asks for.

## Russian patterns: three mechanisms, no fourth

Hand-written Russian stem patterns were swept out on 2026-08-04 by the owner's
call. A regex whose output is a fact or a route is the defect. A regex over
structured input, such as HTML, MIME, JSON, a URL or an argv list, is not.

Before writing a Russian word list, pick one of these.

### `internal/lexicon`, for closed classes

`lexicon_ru_v1.json` holds interrogatives, capture verbs, reminder verbs,
cardinals, day offsets, parts of day, weekdays, months and spoken hours.
Editing a word is a data change and there is exactly one copy. Months used to
live in three files and drifted between them.

Cardinals carry the oblique forms, because a spoken time declines. `в семь` and
`к семи` are one hour.

### `internal/morph`, for grammar

From the vendored golem Russian dictionary. `IsVerbForm` and `SameWord`.

Lemma matching is BROADER than stem-plus-one-ending. A verb slot that means the
imperative must be matched exactly. `говори` and `говорил` share a
lemma and only one of them is a command
(`cmd/mavend/quiet_toggle.go`).

### `cmd/mavend/topics.go` and the embedder, for open sets

Use these when the question is what a turn is ABOUT. Frozen seeds per subject
plus a real `other` class, scored against the turn's own query vector.

Same shape as the personal boundary in `personalboundary.go`, with one
difference. A topic must clear the runner-up by `topicMargin`, because a false
claim here spends a network scan rather than one honest "не знаю". The old
keyword tests stay as the offline floor.

### The ecosystem trio

Use it when the answer is not in the utterance at all. Identity is Nexus's,
never a local pattern.

## Seeds are scoring data

Editing one moves a recogniser. Re-measure against the `TestONNX*` tests rather
than eyeballing the change.
