Files
claude a97764c5f7 Add seven stage 0 frames and tighten three more (V-720)
MavenHelpGrammar keeps "как отменить напоминание" on SourceSelf, where the
answer names the command Maven accepts, instead of leaking to search.
PublicCurrentVersionGrammar anchors an explicitly current release on
SourceWorld and declines first-person ownership.

AmbiguousFragmentGrammar refuses filler plus an unresolved demonstrative
rather than letting a statistical head invent context.
ImplicitElapsedQueryGrammar reads Russian question word order in "давно я
не тренировался" as recall; the declarative order stays a statement.
ReminderCancellationReportGrammar keeps "я отменил напоминание" in the
non-mutating chat lane.

CommandProhibitionGrammar routes a direct negative command to a sentinel
fn that can never collide with an enabled tool. ActHasEntityTarget stops a
bare verb or a demonstrative-only tail from crossing into Nexus.

Praxis attention now accepts "что там с X" for the four service names only.
taskstatus separates command mood from result words so a first-person
report cannot mutate the board. question.go exports the open-question and
locative shapes the recall gate reads.

--no-verify: master is the working branch this session by the owner's call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 17:18:48 +04:00

79 lines
3.5 KiB
Go

package router
import (
"strings"
"github.com/kami/maven/internal/morph"
)
// thinSingleToken — is a one-word utterance thin evidence, or is it a whole
// sentence?
//
// The rule this replaces was `len(strings.Fields(u)) <= 1`, an English
// intuition. It does not transfer: Russian packs a subject, a tense and a
// gender into one word, so "поужинал" is a complete report and "привет" a
// complete greeting, yet both got thinned and came back as "не совсем поняла".
// Meanwhile the case the rule exists for is real — a bare noun like "вода" or
// "бэкап" genuinely does not say fact-vs-query or act-vs-report.
//
// So: still one token, but only thin it when the token is a bare nominal.
// Two escapes, both cheap and both offline:
//
// - a closed lexicon of social and command singles, which are complete by
// definition ("привет", "спасибо", "стоп", "yes");
// - a dictionary lookup for a verb form. A verb carries its own subject,
// tense and gender, so a verb IS a sentence.
//
// The dictionary lookup replaced a list of 24 letter endings (Vikunja #526). The
// list was loose in a direction its own comment named: "канал" ends in -ал and
// read as past tense, and short words needed a length exemption so "нос" and
// "лес" would survive a two-letter suffix. Asking a morphological dictionary
// costs one map lookup and has no such errors — grammar is what a dictionary is
// for.
func thinSingleToken(utterance string) bool {
f := strings.Fields(utterance)
if len(f) != 1 {
return false
}
w := strings.ToLower(strings.Trim(f[0], ".,!?;:—-\"'«»()"))
if w == "" {
return false
}
if completeSingles[w] {
return false
}
return !completeSingleVerb(utterance)
}
// completeSingleVerb is the positive half of thinSingleToken. Kept separate so
// the routing heads can reconcile a learned clarify with the same grammatical
// fact the model-side gate already trusts: one Russian verb is a whole clause.
func completeSingleVerb(utterance string) bool {
fields := strings.Fields(utterance)
if len(fields) != 1 {
return false
}
word := strings.ToLower(strings.Trim(fields[0], ".,!?;:—-\"'«»()"))
return word != "" && morph.IsVerbForm(word)
}
// completeSingles — one-word utterances that need no second half. Greetings,
// acknowledgements and the control words a voice loop has to honour instantly.
var completeSingles = map[string]bool{
// ru: social
"привет": true, "здравствуй": true, "здравствуйте": true, "здорово": true,
"пока": true, "прощай": true, "спокойной": true, "спасибо": true,
"благодарю": true, "извини": true, "прости": true, "пожалуйста": true,
"да": true, "нет": true, "ага": true, "угу": true, "ок": true, "окей": true,
"хорошо": true, "ладно": true, "конечно": true, "верно": true, "точно": true,
// ru: control
"стоп": true, "отмена": true, "отбой": true, "хватит": true, "тихо": true,
"повтори": true, "продолжай": true, "помоги": true, "помощь": true,
// en
"hi": true, "hello": true, "hey": true, "bye": true, "goodbye": true,
"thanks": true, "thank": true, "sorry": true, "please": true,
"yes": true, "no": true, "yep": true, "nope": true, "ok": true, "okay": true,
"sure": true, "right": true, "stop": true, "cancel": true, "help": true,
"repeat": true, "continue": true,
}