package router import ( "regexp" "strings" ) // BareCaptureGrammar — a capture verb with nothing after it is a fact she has // yet to hear, not a conversation (Vikunja #557). // // A bare "запиши" reached the resident model as chat, and the model answered by // agreeing to something nobody asked for: "Я поняла, теперь я буду говорить // «записала» или «записала заметку»." It read its own instruction block as the // subject of the turn. Whatever the routing, that reply is invented. // // The route this rule asks for is a fact with no key, which is a gap the clarify // path already has copy for ("Что записать?"). So the rule does not answer the // turn — it hands it to the one mechanism that asks. // // Wired before TaskCaptureGrammar, whose pattern needs an object, so it can // never claim this shape. There is nothing to disambiguate: the whole utterance // is one verb from a closed lexicon. func BareCaptureGrammar() []Grammar { return []Grammar{{ Name: "bare-capture", Pattern: bareCapturePattern, Build: bareCaptureBuild, }} } // Anchored at both ends, so only the verb and punctuation are in the utterance. // Trailing "-ка" and "пожалуйста" are the same request said politely. var bareCapturePattern = regexp.MustCompile(`(?i)^\s*([\p{L}]+)(?:-ка)?[,\s]*(?:пожалуйста)?[\s.!?]*$`) func bareCaptureBuild(m []string) (Decision, bool) { word := strings.ToLower(strings.TrimSpace(m[1])) for _, v := range captureVerbs { if word != v { continue } // No key, no text: she was told to record and not what. Confidence is // 1.0 about the shape, which is all stage 0 ever claims — the gap is // carried by the empty slots, not by a doubt. return Decision{ Stage: 0, Intent: IntentFact, Confidence: 1.0, }, true } return Decision{}, false }