Files
Maven/docs/language.md
T
claude 50130cdffb Move the reasoning out of CLAUDE.md and leave the rules (V-670)
490 lines still loads into every session, and most of them explained a
subsystem rather than constraining an agent. The owner's cap is 200. This
lands at exactly 200.

Four new living docs take what left:

  docs/deployment.md  the two boxes, the resident model, the embedder, STT,
                      the daemon table, who is in compose, the voice wire,
                      mavwaked on workpc, the web UI conventions
  docs/world.md       what replaced "never phones home", why Response.Empty()
                      is the whole gate, the timeouts, Kiwix
  docs/language.md    the LLM output contract and the three Russian mechanisms
  docs/workflow.md    the five stores, the doc tiers, Vikunja, the guards

CLAUDE.md keeps the pointer table and the rules. Every "do not do X", every
path and every owner's call stayed. What went is the before-and-after
narrative behind each one, which is what a living doc is for.

Verified rather than trusted. Every backticked literal in the old file was
diffed against the union of the new ones. Twenty-four came up missing and
three groups were facts rather than narrative, so they were restored:

  - the ecosystem client table (nexusClient, praxisClient, the vendored hexis
    client, the three config keys and their default URLs) into
    docs/ecosystem.md, which did not carry it
  - TestOnlyAGrammarMayDropTheBoundary and TestNamingRecallKeepsTheBoundary
    into docs/routing.md, since they pin the boundary rule in both directions
  - the ipc.Dial vs voice.Dial trap and docs/plans/17 into docs/deployment.md

diff-budget.sh blocked on the changed-line count again. It counts markdown,
which the repo's own pre-commit hook exempts, and this commit touches
nothing else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013ptwopxyo3Z2kwFckHkLvN
2026-08-09 13:10:13 +04:00

2.8 KiB

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:

[{"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.