Files
Maven/internal/router/habit.go
T
claude 9e1958e7b0 one weekday matcher, and a stem list stops answering for sredstvo (V-581)
router.WeekdayIndex reads the lexicon and asks the dictionary about the case.
Four private lists go away: the habit declension map, the weekday block of the
day-plan refusal, the weekday and part-of-day entries of the weather guard, and
the stem list in ruwords.go.

The stem list was the real defect. mentionsUnknownDay matched sred, pyatnits
and subbot with strings.Contains, so sredi, sredstvo and sredniy all read as
Wednesday and a question carrying one was answered with onlyNearDaysReply
instead of a date. It matches whole tokens now.

The weather guard was a third copy of three closed sets that already exist.
It kept the rooms of the house, which are its own, and asks the lexicon for the
weekdays, the parts of the day and the words that follow v without naming a
place. Questions phrased v srede, v utra and v obshchem reached the geocoder as
cities before.

Full suite green under -race.
2026-08-06 03:33:01 +04:00

73 lines
2.8 KiB
Go

package router
import "time"
// Habit queries — "что я обычно делаю по вторникам?" (Vikunja #254).
//
// Deterministic matching, like the calendar and plan matchers: the LLM router
// classifies the intent, but WHICH weekday was asked about is a lookup, not a
// generation. A model that answers "по вторникам" for a question about Thursday
// gives a confidently wrong account of the owner's own life.
// HabitQuery — a parsed "what do I usually do" question. Weekday is set only
// when the utterance names one; otherwise the answer covers the whole week.
// Weekend is set for "по выходным", which names two days rather than one.
type HabitQuery struct {
Weekday time.Weekday
HasWeekday bool
Weekend bool
}
// habitMarkers — the words that make a question about habit rather than about
// today. Without one of these, "что я делаю" is a question about right now, and
// the recall path owns it.
var habitMarkers = []string{
"обычно", "обычное", "чаще", "постоянно", "привычки", "привычка", "привычках",
"регулярно", "каждый", "каждую", "каждое", "usually", "habits", "habit",
"typically", "normally",
}
// The weekday a habit question names comes from WeekdayIndex, not from a map
// here. This file used to keep its own declension table, which had "воскресеньях"
// and no "средах" — a list of forms is finished by whoever last thought of one,
// and a dictionary is not (V-581).
// weekendWords — the weekend as one unit. "что я обычно делаю по выходным?"
// has a habit marker and names days, but no weekday name is in it, so it used
// to fall through to the whole-week profile and answer about Tuesdays too.
var weekendWords = map[string]bool{
"выходным": true, "выходные": true, "выходных": true, "выходной": true,
"weekend": true, "weekends": true,
}
// ParseHabitQuery reports whether an utterance asks what the owner usually
// does, and on which weekday if it names one.
//
// A habit marker is required. "что я делаю в среду?" without one is a question
// about this coming Wednesday — the calendar's job — and answering it with a
// statistical average would be answering a different question.
func ParseHabitQuery(text string) (HabitQuery, bool) {
toks := planTokens(text)
marked := false
for _, t := range toks {
for _, m := range habitMarkers {
if t == m {
marked = true
break
}
}
}
if !marked {
return HabitQuery{}, false
}
for _, t := range toks {
if wd, ok := WeekdayIndex(t); ok {
return HabitQuery{Weekday: wd, HasWeekday: true}, true
}
if weekendWords[t] {
return HabitQuery{Weekend: true}, true
}
}
return HabitQuery{}, true
}