morning, tasks, memory: say the summaries from the file (V-506)

The three callers now read their sentences out of summary_ru_v1.json: the
plan lines in morning.Plan.FormatRU, the list and reason words in
tasks.FormatRU, and the habit readouts in memory.Profile.

Two behaviour_test assertions moved from substring to say.IsS, because the
habit gaps have variants now and a substring pins one of them. The
"по {day} у тебя обычно" variant was dropped on sight: the activities are
verbs, so it read "у тебя обычно тренируешься".

The persona scorer covers the family, and a new test asserts every gap
variant still says she has not seen enough rather than that he has nothing.
This commit is contained in:
2026-08-04 01:47:52 +04:00
parent bad3fa4035
commit 42d7a39c49
7 changed files with 124 additions and 46 deletions
+19 -16
View File
@@ -19,8 +19,11 @@ package tasks
import (
"fmt"
"sort"
"strconv"
"strings"
"time"
"github.com/kami/maven/internal/say"
)
// Status values, mirroring internal/store so a caller can rank ipc.Task rows
@@ -117,21 +120,21 @@ func score(it Item, now time.Time) (float64, string) {
bonus = scoreOverdueCap
}
total += scoreOverdue + bonus
reason = "просрочено"
reason = say.S(say.ReasonOverdue, nil)
if late == 1 {
reason = "просрочено на день"
reason = say.S(say.ReasonOverdueDay, nil)
} else if late > 1 {
reason = fmt.Sprintf("просрочено на %d дн.", late)
reason = say.S(say.ReasonOverdueDays, map[string]string{"n": strconv.Itoa(late)})
}
case days == 0:
total += scoreDueToday
reason = "сегодня"
reason = say.S(say.ReasonToday, nil)
case days == 1:
total += scoreDueTomorrow
reason = "завтра"
reason = say.S(say.ReasonTomorrow, nil)
case days <= 7:
total += scoreDueWeek
reason = fmt.Sprintf("через %d дн.", days)
reason = say.S(say.ReasonInDays, map[string]string{"n": strconv.Itoa(days)})
default:
total += scoreDueLater
}
@@ -147,9 +150,9 @@ func score(it Item, now time.Time) (float64, string) {
// The rungs get their own words. The reason string is the one place
// the ranking explains itself, and reading "важно" back at a task
// he flagged "срочно" reports a word he did not say.
reason = "важно"
reason = say.S(say.ReasonImportant, nil)
if w >= MaxWeight {
reason = "срочно"
reason = say.S(say.ReasonUrgent, nil)
}
}
}
@@ -163,7 +166,7 @@ func score(it Item, now time.Time) (float64, string) {
}
total += age
if reason == "" && weeks >= 2 {
reason = "давно в списке"
reason = say.S(say.ReasonStale, nil)
}
}
}
@@ -210,22 +213,22 @@ func FormatRU(ranked []Ranked) string {
}
}
if len(open) == 0 && len(cands) == 0 {
return "задач нет."
return say.S(say.TasksNone, nil)
}
var b strings.Builder
if len(open) > 0 {
b.WriteString("сначала: ")
b.WriteString(joinRU(open, SpokenLimit, true))
b.WriteString(".")
b.WriteString(say.S(say.TasksFirst, map[string]string{
"items": joinRU(open, SpokenLimit, true),
}))
}
if len(cands) > 0 {
if b.Len() > 0 {
b.WriteString(" ")
}
b.WriteString("ещё я нашла, но ты не подтвердил: ")
b.WriteString(joinRU(cands, SpokenLimit, false))
b.WriteString(".")
b.WriteString(say.S(say.TasksCandidates, map[string]string{
"items": joinRU(cands, SpokenLimit, false),
}))
}
return b.String()
}