Strings family 6: summaries and reports into summary_ru_v1.json #113

Closed
claude wants to merge 4 commits from task/506-strings-family-6-summaries-and-reports-i into task/504-strings-family-4-act-and-smart-home-repl
Contributor

Closes Vikunja #506.

Opened during an unattended overnight run: the diff-budget block was off (task overnight). Read the diff, not only the tests.

Acceptance criteria and quality gate are in TASK.md on this branch.
Review the review, not the diff — leave comments and the agent will apply them via task start 506.

Closes Vikunja #506. Opened during an unattended overnight run: the diff-budget block was off (`task overnight`). Read the diff, not only the tests. Acceptance criteria and quality gate are in `TASK.md` on this branch. Review the review, not the diff — leave comments and the agent will apply them via `task start 506`.
claude added 4 commits 2026-08-03 23:48:00 +02:00
The summaries family is spoken by internal/memory, internal/morning and
internal/tasks. internal/phraser already imports internal/memory, so the
deck cannot stay in phraser without a cycle.

internal/say is a leaf: embed, json, math/rand, strings, sync. The four
phraser families keep their files and their floors and now call say.Load,
*say.Deck, Text, Matches, Variants, RequirePlaceholder and RegisterFloor.
No copy changed and no behaviour changed.
summary_ru_v1.json: the morning plan, the ranked task list, and the habit
sentences read back out of behaviour records. Own schema_version.

The empty cases are the point. "I have not seen enough yet" and "there is
nothing there" are different claims about his life, and the habit entries
keep the first — three days of taps produce the same "обычно ты ..." as a
year of them. plan_rest_empty stays separate from plan_day_empty for the
same reason: a day that is over was not an empty day.

Count forms stay in Go. день/дня/дней and задача/задачи/задач are
morphology, and they arrive here through {word}. Loader in the next commit.
Same nil-safe shape as the four families in phraser: a floor holding the
exact literals that lived in Go, a load-time placeholder check on every
entry whose job is to read the aggregate back, and S/IsS for the callers
and their tests. No call site moved yet.
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.
kami reviewed 2026-08-04 12:43:24 +02:00
@@ -0,0 +1,141 @@
{
Owner

bugs first — and one of them is mine from the previous file.

json
"plan_rest_empty": { "fixed": true, "variants": ["на сегодня больше ничего не запланировано."] },
"plan_day_empty": { "fixed": true, "variants": ["на {date} ничего не запланировано."] },
"plan_day": { "fixed": true, "variants": ["план на {date}: {items}"] },
"plan_uncertain": { "fixed": true, "variants": ["похоже, {line}"] },

"tasks_none": { "fixed": true, "variants": ["задач нет."] },
"tasks_first": { "fixed": true, "variants": ["сначала: {items}"] },
"tasks_candidates": { "fixed": true, "variants": ["нашла ещё, но ты не подтверждал: {items}"] },

"reason_overdue": { "fixed": true, "variants": ["просрочено"] },
"reason_overdue_days": { "fixed": true, "variants": ["просрочено на {n} {word}"] },
"reason_today": { "fixed": true, "variants": ["сегодня"] },
"reason_tomorrow": { "fixed": true, "variants": ["завтра"] },
"reason_in_days": { "fixed": true, "variants": ["через {n} {word}"] },
"reason_important": { "fixed": true, "variants": ["важно"] },
"reason_urgent": { "fixed": true, "variants": ["срочно"] },
"reason_stale": { "fixed": true, "variants": ["давно в списке"] },

"habit_weekday": { "fixed": true, "variants": ["по {day} ты обычно {items}."] },
"habit_weekday_same": { "fixed": true, "variants": ["по {day} всё как обычно — то же, что и в остальные дни: {items}."] },
"habit_weekday_none": { "variants": ["по {day} я пока не вижу ничего постоянного.", "по {day} постоянного пока не вижу — записей мало."] },
"habit_weekend_both": { "fixed": true, "variants": ["по субботам ты обычно {items_sat}, по воскресеньям — {items_sun}."] },
"habit_weekend_sat": { "fixed": true, "variants": ["по субботам ты обычно {items}, а по воскресеньям постоянного нет."] },
"habit_weekend_sun": { "fixed": true, "variants": ["по воскресеньям ты обычно {items}, а по субботам постоянного нет."] },
"habit_weekend_same": { "fixed": true, "variants": ["по выходным всё как обычно — то же, что и в остальные дни: {items}."] },
"habit_weekend_none": { "variants": ["по выходным я пока не вижу ничего постоянного.", "по выходным постоянного пока не вижу — записей мало."] },
"habit_overall": { "fixed": true, "variants": ["обычно ты {items} — {span}."] },
"habit_overall_none": { "variants": ["записей пока мало, про привычки не скажу.", "пока мало записей, чтобы говорить о привычках."] },
"habit_span_today": { "fixed": true, "variants": ["по записям за сегодня"] },
"habit_span_days": { "fixed": true, "variants": ["по записям за последние {n} {word}"] },
"habit_unglossed": { "fixed": true, "variants": ["отмечаешь «{key}»"] },
"habit_at": { "fixed": true, "variants": ["{gloss} около {time}"] }

bug 1 — «дн.» is a written abbreviation and this thing talks. «просрочено на 5 дн.» through TTS reads as garbage or gets spelled out. you already built {word} for exactly this; the reason_* entries just don't use it. switching them also deletes reason_overdue_day as a separate key — «просрочено на 1 день» falls out of the helper, no special case needed.

and my correction: {temp}° in the query file has the same defect. i suggested it to dodge declension, which was right for a text UI and wrong for voice — ° reads as nothing on most engines. use {temp} {word} there too and let the helper handle градус/градуса/градусов. one helper, every count site.

bug 2 — five undeclared placeholders. {line}, {sat}, {sun}, {key}, {gloss}, {time}. worse, habit_weekend_both uses {sat}/{sun} while its two siblings use {items} for the same data — three parallel entries, two schemes, and the caller has to remember which. i renamed to {items_sat}/{items_sun} so the family reads consistently, but pick whatever matches the Go side and get all six into the notes.

bug 3 — fixedness is inconsistent across parallel entries. habit_weekday is fixed, habit_weekend_both isn't, and both have exactly one variant. same for habit_overall, both habit_span_*. per your own note that's a schema violation waiting on a linter — worth one test that asserts single-variant ⇒ fixed, across all five files.

bug 4 — plan_uncertain nests phraser output. «похоже, {line}» prepends onto another rendered string. if {line} ever arrives capitalised or already hedged you get «похоже, На сегодня…» or a double hedge. flat-out fine as a mechanism, fragile as a composition — assert lowercase-first on {line} at the join.

register cuts, same filters as the other files:

«у тебя нет ничего особенного» — that's a verdict on him, not on the data. «всё как обычно» says the same thing about records instead of about his life. this was the only editorialising line in the file.
«на привычки я так не сошлюсь» — bookish. «про привычки не скажу.» is the same claim in your register.
«ещё я нашла, но ты не подтвердил» — word order reads translated; «нашла ещё, но ты не подтверждал» is spoken. imperfective also softens it from an accusation to a note.
dropped «у тебя» where it was filler — «по {day} я пока не вижу ничего постоянного.» carries it already.
trailing periods after {items} removed on plan_day / tasks_first / tasks_candidates, since a joined list arrives with its own punctuation and you were getting «…: сделать X..» — the habit entries keep theirs because there the list is mid-sentence.

the hedging discipline in this file is the best of the five, for what it's worth. «пока не вижу» rather than «нет» is the same distinction as «вроде, оно» and it's load-bearing in a place where a confident wrong claim about his own habits would be genuinely irritating.

bugs first — and one of them is mine from the previous file. json "plan_rest_empty": { "fixed": true, "variants": ["на сегодня больше ничего не запланировано."] }, "plan_day_empty": { "fixed": true, "variants": ["на {date} ничего не запланировано."] }, "plan_day": { "fixed": true, "variants": ["план на {date}: {items}"] }, "plan_uncertain": { "fixed": true, "variants": ["похоже, {line}"] }, "tasks_none": { "fixed": true, "variants": ["задач нет."] }, "tasks_first": { "fixed": true, "variants": ["сначала: {items}"] }, "tasks_candidates": { "fixed": true, "variants": ["нашла ещё, но ты не подтверждал: {items}"] }, "reason_overdue": { "fixed": true, "variants": ["просрочено"] }, "reason_overdue_days": { "fixed": true, "variants": ["просрочено на {n} {word}"] }, "reason_today": { "fixed": true, "variants": ["сегодня"] }, "reason_tomorrow": { "fixed": true, "variants": ["завтра"] }, "reason_in_days": { "fixed": true, "variants": ["через {n} {word}"] }, "reason_important": { "fixed": true, "variants": ["важно"] }, "reason_urgent": { "fixed": true, "variants": ["срочно"] }, "reason_stale": { "fixed": true, "variants": ["давно в списке"] }, "habit_weekday": { "fixed": true, "variants": ["по {day} ты обычно {items}."] }, "habit_weekday_same": { "fixed": true, "variants": ["по {day} всё как обычно — то же, что и в остальные дни: {items}."] }, "habit_weekday_none": { "variants": ["по {day} я пока не вижу ничего постоянного.", "по {day} постоянного пока не вижу — записей мало."] }, "habit_weekend_both": { "fixed": true, "variants": ["по субботам ты обычно {items_sat}, по воскресеньям — {items_sun}."] }, "habit_weekend_sat": { "fixed": true, "variants": ["по субботам ты обычно {items}, а по воскресеньям постоянного нет."] }, "habit_weekend_sun": { "fixed": true, "variants": ["по воскресеньям ты обычно {items}, а по субботам постоянного нет."] }, "habit_weekend_same": { "fixed": true, "variants": ["по выходным всё как обычно — то же, что и в остальные дни: {items}."] }, "habit_weekend_none": { "variants": ["по выходным я пока не вижу ничего постоянного.", "по выходным постоянного пока не вижу — записей мало."] }, "habit_overall": { "fixed": true, "variants": ["обычно ты {items} — {span}."] }, "habit_overall_none": { "variants": ["записей пока мало, про привычки не скажу.", "пока мало записей, чтобы говорить о привычках."] }, "habit_span_today": { "fixed": true, "variants": ["по записям за сегодня"] }, "habit_span_days": { "fixed": true, "variants": ["по записям за последние {n} {word}"] }, "habit_unglossed": { "fixed": true, "variants": ["отмечаешь «{key}»"] }, "habit_at": { "fixed": true, "variants": ["{gloss} около {time}"] } bug 1 — «дн.» is a written abbreviation and this thing talks. «просрочено на 5 дн.» through TTS reads as garbage or gets spelled out. you already built {word} for exactly this; the reason_* entries just don't use it. switching them also deletes reason_overdue_day as a separate key — «просрочено на 1 день» falls out of the helper, no special case needed. and my correction: {temp}° in the query file has the same defect. i suggested it to dodge declension, which was right for a text UI and wrong for voice — ° reads as nothing on most engines. use {temp} {word} there too and let the helper handle градус/градуса/градусов. one helper, every count site. bug 2 — five undeclared placeholders. {line}, {sat}, {sun}, {key}, {gloss}, {time}. worse, habit_weekend_both uses {sat}/{sun} while its two siblings use {items} for the same data — three parallel entries, two schemes, and the caller has to remember which. i renamed to {items_sat}/{items_sun} so the family reads consistently, but pick whatever matches the Go side and get all six into the notes. bug 3 — fixedness is inconsistent across parallel entries. habit_weekday is fixed, habit_weekend_both isn't, and both have exactly one variant. same for habit_overall, both habit_span_*. per your own note that's a schema violation waiting on a linter — worth one test that asserts single-variant ⇒ fixed, across all five files. bug 4 — plan_uncertain nests phraser output. «похоже, {line}» prepends onto another rendered string. if {line} ever arrives capitalised or already hedged you get «похоже, На сегодня…» or a double hedge. flat-out fine as a mechanism, fragile as a composition — assert lowercase-first on {line} at the join. register cuts, same filters as the other files: «у тебя нет ничего особенного» — that's a verdict on him, not on the data. «всё как обычно» says the same thing about records instead of about his life. this was the only editorialising line in the file. «на привычки я так не сошлюсь» — bookish. «про привычки не скажу.» is the same claim in your register. «ещё я нашла, но ты не подтвердил» — word order reads translated; «нашла ещё, но ты не подтверждал» is spoken. imperfective also softens it from an accusation to a note. dropped «у тебя» where it was filler — «по {day} я пока не вижу ничего постоянного.» carries it already. trailing periods after {items} removed on plan_day / tasks_first / tasks_candidates, since a joined list arrives with its own punctuation and you were getting «…: сделать X..» — the habit entries keep theirs because there the list is mid-sentence. the hedging discipline in this file is the best of the five, for what it's worth. «пока не вижу» rather than «нет» is the same distinction as «вроде, оно» and it's load-bearing in a place where a confident wrong claim about his own habits would be genuinely irritating.
kami requested changes 2026-08-04 12:51:02 +02:00
kami left a comment
Owner

see comment for json.

see comment for json.
Author
Contributor

Landed transitively. This branch is already an ancestor of master, so there is nothing left to merge and Gitea did not close the pull request on its own.

Reviewed as part of a bottom-up pass over the open stack. The four open findings from that pass are worth a follow-up task, and none of them blocks anything here:

  • internal/router/stage0.go has two grammars named rest-of-day-query, and the second is dead.
  • internal/router/numwords.go holds ruNumerals, a second copy of the lexicon cardinals.
  • cmd/mavend/reminderbody.go and cmd/mavend/historyq.go still match Russian by hand.
  • internal/weather/openmeteo.go guesses declension by reversing endings, and bails under four runes.
Landed transitively. This branch is already an ancestor of `master`, so there is nothing left to merge and Gitea did not close the pull request on its own. Reviewed as part of a bottom-up pass over the open stack. The four open findings from that pass are worth a follow-up task, and none of them blocks anything here: - `internal/router/stage0.go` has two grammars named `rest-of-day-query`, and the second is dead. - `internal/router/numwords.go` holds `ruNumerals`, a second copy of the lexicon `cardinals`. - `cmd/mavend/reminderbody.go` and `cmd/mavend/historyq.go` still match Russian by hand. - `internal/weather/openmeteo.go` guesses declension by reversing endings, and bails under four runes.
claude closed this pull request 2026-08-04 18:35:06 +02:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kami/Maven#113