Hand-written Russian nudge templates plus a picker

Nudge wording as data instead of generation. The wording lives in
internal/phraser/nudges_ru_v1.json (embedded), about 10 variants per rule:
water, meal, break, service_down, netdata_critical, routine:, morning:, plus
a contentless default. That JSON is long because it is data — the owner can
edit any line of Russian without touching Go.

The picker:
- random, but never the same variant twice in a row for the same rule
- deterministic when seeded (math/rand with an injectable source)
- fills {since} / {service} / {what} from the candidate, and skips any variant
  whose value is missing, so no raw placeholder can reach the piper voice
- {since} is spelled out in words ("полтора часа", "семь часов"), because
  "3 ч" is wrong in a Russian voice

Scores 15/15 on the existing nudge fixture, on every seed swept. Nothing is
wired yet — that is the next commit.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CGeSZxh1DCtRxmFVSYVGvJ
This commit is contained in:
kami
2026-07-31 18:18:55 +04:00
parent aa8f5b2ee2
commit 13e5170e9e
4 changed files with 618 additions and 0 deletions
+129
View File
@@ -0,0 +1,129 @@
{
"schema_version": 1,
"name": "russian nudge templates v1",
"notes": [
"Hand-written Russian nudges. Edit the wording here, no Go changes needed.",
"Rules: she is feminine about herself, he is a man addressed as ты. Never вы/вас/ваш, never plural imperatives (выпейте), never он/его about him.",
"One short sentence. No questions, no emoji, no pet names, no emotional support.",
"Placeholders: {since} how long it has been (only used when it is at least an hour), {service} the service name, {what} the routine name. A variant whose placeholder has no value is skipped, so every rule needs at least one variant with no placeholder. The exception is routine and morning: those only exist for rules like routine:таблетки that always carry a name, and a routine nudge that drops the name is useless.",
"mood must be one of: neutral, happy, thinking, tired, confused."
],
"rules": {
"water": {
"mood": "neutral",
"variants": [
"Ты не пил воду {since} — выпей стакан.",
"Пора выпить воды.",
"Стакан воды не помешает.",
"Воду ты не пил уже {since}.",
"Напоминаю про воду.",
"Сходи за водой, дела подождут.",
"Сделай глоток воды, пока помнишь.",
"Между делом выпей воды.",
"Вода — простое дело: выпей стакан.",
"Отвлекись на стакан воды."
]
},
"meal": {
"mood": "neutral",
"variants": [
"Ты не ел {since} — поешь.",
"Пора поесть, сделай перекус.",
"Еда важнее ещё одного часа за столом.",
"Без еды уже {since}, поешь.",
"Напоминаю про еду — поешь.",
"Возьми перерыв на обед.",
"Сделай себе перекус, это пять минут.",
"Поешь, потом вернёшься к работе.",
"Поешь нормально, а не на ходу.",
"Еды не было {since} — разогрей что-нибудь."
]
},
"break": {
"mood": "neutral",
"variants": [
"Ты за столом {since} — встань и разомнись.",
"Пора сделать перерыв.",
"Встань на пять минут.",
"{since} без перерыва — отойди от экрана.",
"Напоминаю про перерыв.",
"Разомни спину, потом продолжишь.",
"Короткая пауза не сорвёт дела.",
"Отойди от компьютера на минуту.",
"Сидишь без перерыва {since}.",
"Встань, пройдись, вернись."
]
},
"service_down": {
"mood": "neutral",
"variants": [
"Сервис {service} не отвечает.",
"{service} упал — сервис не отвечает.",
"{service} не отвечает, сервис нужно поднимать.",
"Сервис {service} недоступен.",
"Проверь {service}: сервис не отвечает.",
"Сервис перестал отвечать.",
"Сервис {service} лежит, нужно смотреть.",
"{service} не отвечает уже {since}.",
"Мониторинг сообщает: {service} лежит.",
"Сервис {service} не отвечает, посмотри логи."
]
},
"netdata_critical": {
"mood": "neutral",
"variants": [
"Netdata: критический алярм, проверь диск.",
"Критический алярм в netdata — посмотри диск.",
"Netdata поднял тревогу по диску.",
"Проверь диск: netdata ругается.",
"Алярм от netdata, критический.",
"Netdata: критический уровень, дело в диске.",
"Диск требует внимания — критический алярм в netdata.",
"Критический алярм: проверь место на диске.",
"Netdata сообщает о критической проблеме с диском.",
"Открой netdata: там критический алярм по диску."
]
},
"routine": {
"mood": "neutral",
"variants": [
"По распорядку: {what}.",
"Пора — {what}.",
"Напоминаю: {what}.",
"В списке на сейчас: {what}.",
"{what} — сейчас самое время.",
"Не пропусти: {what}.",
"{what}: пора сделать.",
"Сейчас по плану {what}.",
"Твой распорядок: {what}.",
"{what} — по распорядку сейчас."
]
},
"morning": {
"mood": "neutral",
"variants": [
"{what} — пора начать день.",
"{what}: пройди утренний список.",
"Начни {what} со списка.",
"{what}. Осталось пройти чеклист.",
"Утренний список ещё не пройден: {what}.",
"{what}: первый пункт списка за тобой.",
"{what} идёт, а список стоит.",
"{what}: не забудь про утренние дела.",
"По утреннему чеклисту ещё есть дела: {what}.",
"{what} — утренний список дел ещё ждёт."
]
},
"default": {
"mood": "neutral",
"variants": [
"Напоминаю: есть дело.",
"Пора вернуться к отложенному делу.",
"Одно дело ждёт тебя.",
"Напоминаю про дело из списка.",
"В списке осталось дело.",
"Дело всё ещё не сделано."
]
}
}
}