Files
Maven/internal/router/habit_test.go
T
kami c8444813e2 Answer "что я обычно делаю по вторникам?" by counting, not guessing (#254)
Behavioural memory, narrowed on purpose. internal/memory/behavior.go builds a
profile out of self-facts — distinct days per weekday, median time of day — and
reads it back in RU; router.ParseHabitQuery finds the weekday deterministically;
a `habits` query source answers the question.

Three things the plan doc asks for are deliberately absent, and the doc now
records why:

- The profile is COUNTED, not LLM-generated. A 1.7B asked to summarise a year of
  habits writes fluent claims about the owner's life that no row supports, and a
  wrong claim about him is the most expensive kind of wrong maven can be.
- No cached profile fact, so no "update on fact write" machinery. It is
  recomputed on the question; a cache that can disagree with its own rows is two
  truths.
- No proactive daily plan nudge. A dispatcher proposal at 08:00 every day is the
  definition of a nag. The path from "she noticed a pattern" to "she acts on it"
  already exists in internal/pattern with the proposal queue on /routines, and it
  goes through him.

A one-off is not a habit: an activity needs two distinct days before she will
call it usual, and until then she says she does not know yet. Only self-facts
count — env rows are the world, config rows are her own tuning state. The typical
time is a median so one 03:00 outlier cannot move a morning habit into the night.
An unrecognised fact key is read back verbatim rather than glossed into something
she made up.

The source sits before "calendar" in querySources, and its matcher requires a
habit marker, so "что я делаю в среду?" still reaches the calendar — answering a
question about this coming Wednesday with a statistical average would be
answering a different question.

Verified: make build and make test both exit 0.
2026-08-01 02:21:09 +04:00

46 lines
1.3 KiB
Go

package router
import (
"testing"
"time"
)
func TestParseHabitQuery(t *testing.T) {
tests := []struct {
in string
ok bool
wd time.Weekday
hasWD bool
}{
{"что я обычно делаю по вторникам?", true, time.Tuesday, true},
{"что я обычно делаю?", true, 0, false},
{"какие у меня привычки", true, 0, false},
{"что я каждую пятницу делаю", true, time.Friday, true},
{"what do i usually do on mondays?", true, time.Monday, true},
// No habit marker: this is a question about the coming Wednesday, and
// the calendar owns it. Answering with an average answers the wrong
// question.
{"что я делаю в среду?", false, 0, false},
{"что у меня сегодня?", false, 0, false},
{"какие планы на сегодня?", false, 0, false},
{"", false, 0, false},
}
for _, tt := range tests {
q, ok := ParseHabitQuery(tt.in)
if ok != tt.ok {
t.Errorf("ParseHabitQuery(%q) ok = %v, want %v", tt.in, ok, tt.ok)
continue
}
if !ok {
continue
}
if q.HasWeekday != tt.hasWD {
t.Errorf("ParseHabitQuery(%q) hasWeekday = %v, want %v", tt.in, q.HasWeekday, tt.hasWD)
continue
}
if q.HasWeekday && q.Weekday != tt.wd {
t.Errorf("ParseHabitQuery(%q) weekday = %v, want %v", tt.in, q.Weekday, tt.wd)
}
}
}