Пакеты фиксов · разработчик → владельцу · июнь 2026

Пять пакетов: от доверия к эксплуатации.

Каждая дыра из аудита попала ровно в один пакет. У каждого пакета — что входит, какие дыры закрывает, усилие, эффект на продукт и готовый промпт для новой сессии Claude Code: открыл свежее окно → вставил → работа пошла, без этого чата в памяти.

Обновление 11.06: решения владельца зафиксированы в docs/SPEC-v3-voice-workflow-and-fixes.md — единый голосовой воркфлоу (ответ на вопрос → diff|✓|«не расслышала» + 🎯-скор произношения → голос Линды), русская транскрипция для A0–A2, тесты только голосом, /settings. Каждый промпт ниже читает SPEC v3 ПЕРВЫМ — он дополняет пакеты и имеет приоритет.

5пакетов — каждый самостоятельная сессия
25+дыр аудита закрывается (все P0 и P1)
4×Mусилие: четыре средних + пакет мелочей
5промптов — скопировал и запустил
ПАКЕТЫ

Что чиним и в каком составе

Логика нарезки: один пакет = одна зона доверия, без пересечений по файлам, чтобы пакеты можно было делать независимо. Пакеты 1–3 — продукт (промпты + код-бэкстопы), пакет 4 — инфраструктура (параллелится), пакет 5 — 12 мелочей одним проходом.

1

Доверие к обратной связи + произношение

усилие M
  • R1 · recast-контракт: каждое сообщение получает ровно одно из трёх — diff <s>/<b> при реальной ошибке · verbatim + ✓ если верно · «не расслышала, повтори». Блок-лист разговорного бразильского (tá/tava/pra/num/te pedir), запрет трогать русский текст, эмодзи и обращение «Linda», few-shots.
  • R2 · кириллический голос по звукам: worked-examples («нао обригадо» → Não, obrigado ✓), правило фонетического совпадения с текущим чанком, «сохраняй числа как сказаны», escape-люк «не расслышала» вместо сочинённой фразы.
  • R3/O4 · код-бэкстопы: recast ≠ тексту юзера и нет <s> → снять ✓; HTML-санитайзер на recast-путь (закрывает D8).
  • O1 · скоринг произношения: Azure Pronunciation Assessment в том же STT-вызове → «🎯 произношение 86% · слабое: queijo» в 🎤-строке + пословный разбор в «Разборе».
Закрывает дыры
R1R2O4O1D5-хвостD8-хвостD10-хвостB1-хвост
«Я не боюсь говорить плохо» снова правда: бот перестаёт врать на самой доверенной поверхности — и впервые отвечает, КАК ты произнёс.
src/core/prompts.py (MODE_RECAST, MODE_RECAST_VOICE) · src/core/conversation.py (_recast) · src/core/voice.py (AzureTranscriber)
Готовый промпт для новой сессии самодостаточен · скопируй целиком
Ты — Claude Code в репозитории Linda AI (Telegram-бот-репетитор бразильского португальского; aiogram 3, Postgres, запуск через uv). Рабочая директория — корень репо. Это пакет фиксов №1 по итогам аудита R2: «Доверие к обратной связи + произношение». Прод-код менять можно и нужно.

ПРОЧИТАЙ ПЕРЕД НАЧАЛОМ (в этом порядке):
0. docs/SPEC-v3-voice-workflow-and-fixes.md — решения владельца 2026-06-11 (единый голосовой воркфлоу A→C, RU-транскрипция для A0–A2, тесты только голосом, /settings). ПРИОРИТЕТ над этим промптом.
1. CLAUDE.md — принципы продукта (recasting вместо correction, чанки, психологическая безопасность, max ~1 явная коррекция на 3 хода).
2. docs/testing/GROWTH-AUDIT-R2-2026-06-10.md — §4: пункты R1 и R2 (P0) и Owner-findings O1, O4; блок «Что работает (не сломать)».
3. Точные репро: tests/sim/out_r2/marina_NOTES.md (N1, N2), tests/sim/out_r2/anya_NOTES.md (N2, N3, N6), tests/sim/out_r2/daniel_NOTES.md (N1).
4. Код: src/core/prompts.py (MODE_RECAST ~стр.209, MODE_RECAST_VOICE ~стр.255), src/core/conversation.py (_recast ~стр.670), src/core/voice.py (AzureTranscriber ~стр.154).

ЗАДАЧА — 4 блока, каждый отдельным коммитом:

A. R1 — recast-контракт (промпты MODE_RECAST и MODE_RECAST_VOICE):
   - Единый контракт: каждое сообщение юзера получает РОВНО одно из трёх состояний:
     (1) diff <s>старое</s> <b>новое</b> — ТОЛЬКО при реальной грамматической ошибке;
     (2) verbatim-повтор + ✓ — если всё верно (✓ ЗАПРЕЩЁН при любом изменении текста; длинное верное сообщение → просто «💡 ✓» без эха);
     (3) «не расслышала, повтори» — если из транскрипта не складывается осмысленный португальский.
     Никаких четвёртых состояний и «улучшений стиля».
   - Блок-лист разговорного бразильского, который НЕ ошибки и не зачёркивается: tá/tava, pra, num/numa, проклитика te/me (te pedir), местоимения ele/ela как объект (esqueci ele), no/na с глаголами движения, tinha+particípio как условное.
   - Никогда не трогать: текст на родном языке юзера, эмодзи, обращения к Линде («Mas Linda…» — это не ошибка), фразы, которым ты сам научил в последних ходах (recent_chunks).
   - 3–4 few-shot примера, включая no/na-кейс и кириллический кейс.
B. R2 — кириллический голос по звукам (MODE_RECAST_VOICE + voice-указания в MODE_LESSON):
   - 2–3 worked-examples: «нао обригадо» → Não, obrigado ✓ («кириллический транскрипт — это ЗВУКИ, сопоставляй фонетически, не по буквам»).
   - Правило: транскрипт фонетически совпадает с текущим/недавним чанком → это ПРАВИЛЬНОЕ произнесение, подтверди; никогда не выдумывай разницу.
   - «Сохраняй числа, как сказаны»: «сейш сетента» = 6,70, а не 67.
   - Escape-люк: мусорный транскрипт → состояние (3) «не расслышала», НЕ сочинённая фраза.
C. R3/O4 — код-бэкстопы в src/core/conversation.py на recast-пути:
   - Если recast-текст отличается от текста юзера И в нём нет <s> → программно убрать ✓.
   - HTML-санитайзер recast-строки перед отправкой: стек-баланс <b>/<s>; при дисбалансе — стрип всех тегов (репро битого HTML: «<b>está</s>», anya banco T1).
D. O1 — скоринг произношения (src/core/voice.py): включить Azure Pronunciation Assessment В ТОМ ЖЕ STT-вызове (continuous assessment, +0 дополнительных запросов). На голосовое сообщение: компактный скор в 🎤-строке («🎯 произношение 86% · слабое: queijo»), пословный разбор — в «Разборе» голосовой реплики. Если оценка недоступна — строку не показывать, не падать.

DEFINITION OF DONE (репро, которые обязаны проходить):
1. Голосовой ход с транскриптом «нао обригадо» → recast «Não, obrigado» ✓, без выдуманной ошибки, бюджет коррекций не тратится.
2. «Linda, fui no mercado» → «no» НЕ зачёркнуто.
3. Мусорный кириллический транскрипт («Бундае и Юг верал лампы дикфей») → «не расслышала, повтори», бот НЕ сочиняет «Bom dia e você já almoçou?».
4. ✓ никогда не сопровождает изменённый текст — юнит-тест на бэкстоп.
5. Битый HTML в recast не уходит в Telegram — юнит-тест санитайзера.
6. Голосовое сообщение получает строку произношения с процентом.

ВЕРИФИКАЦИЯ:
- uv run pytest — зелёный (добавь тесты на C и на санитайзер).
- Симуляция (адаптивный харнесс, нужен OPENROUTER-ключ из .env): создай tests/sim/out_fix/ и прогони по ходу на кейс:
  uv run python -m tests.sim.run_adaptive marina tests/sim/out_fix/marina_recast.jsonl --user "Linda, fui no padaria hoje de manhã"
  uv run python -m tests.sim.run_adaptive daniel tests/sim/out_fix/daniel_voice.jsonl --voice --user "нао обригадо"
  Проверь в jsonl: recast соответствует контракту.
- Live-чек (бот запущен локально, см. logs/): голосовое сообщение → в 🎤-строке есть скор произношения.

НЕ СЛОМАЙ (из §4 аудита, блок «Что работает»): реконструкции смеси («кафе паровяжень» → «café para viagem»), Подсказку, бюджет коррекций, стрип глосс из озвучки. Прогони существующие тесты до и после.
2

Видимый прогресс

усилие M
  • S1 · гард record(): активный сценарий не перетегивается — переключение на id с префиксом текущего (cafe_order → cafe_order_complete) игнорируется, биты не сбрасываются.
  • S1 · бэкстоп победы: каталожный сценарий + steps_done ≥ числа битов → win=True даже без [[WIN]] от модели.
  • S1 · дефиниция BEAT_DONE: «только если юзер САМ произнёс целевое содержание бита» — не «ок», не подтверждение, не мета-вопрос.
  • U2 · /level: вместо двух строк — can-do список сцен (✅ / ◐ / ○) и «до следующего уровня осталось N».
Закрывает дыры
S1B2U2N4 BEAT_DONE
🎉, level-up и can_do_count впервые становятся наблюдаемы — обещание «вижу свой рост» начинает существовать в продукте, а не в прозе бота.
src/core/scenario.py (record) · src/core/scenarios.py (scene_beats_block) · src/adapters/telegram/handlers.py (/level)
Готовый промпт для новой сессии самодостаточен · скопируй целиком
Ты — Claude Code в репозитории Linda AI (Telegram-бот-репетитор португальского; aiogram 3, Postgres, запуск через uv). Рабочая директория — корень репо. Это пакет фиксов №2 по итогам аудита R2: «Видимый прогресс». Прод-код менять можно и нужно.

ПРОЧИТАЙ ПЕРЕД НАЧАЛОМ:
0. docs/SPEC-v3-voice-workflow-and-fixes.md — решения владельца 2026-06-11 (единый голосовой воркфлоу A→C, RU-транскрипция для A0–A2, тесты только голосом, /settings). ПРИОРИТЕТ над этим промптом.
1. CLAUDE.md — принципы (Can-Do milestones вместо CEFR; прогресс = «can order coffee»).
2. docs/testing/GROWTH-AUDIT-R2-2026-06-10.md — §4 пункт S1 (P0), §3 строка B2, §4 таблица P2 пункт U2.
3. Точные репро: tests/sim/out_r2/daniel_NOTES.md (N2 — механизм сброса стейта, N4 — шум BEAT_DONE), tests/sim/out_r2/marina_NOTES.md (N10).
4. Код: src/core/scenario.py (ScenarioState.record ~стр.55 — здесь баг), src/core/scenarios.py (scene_beats_block ~стр.80, render_scenario_catalog), src/adapters/telegram/handlers.py (хэндлер /level).

КОНТЕКСТ БАГА: за 91 ход симуляций + полную живую сцену кафе — 0 × WIN, can_do_count = 0. Механизм: модель мид-сценой эмитит свободный id ([[SCENARIO: cafe_order_complete]]) → record() считает это НОВЫМ сценарием и сбрасывает steps_done в 0 → финальный бит с WIN-инструкцией никогда не рендерится.

ЗАДАЧА — 5 шагов, мелкими коммитами:
A. Код-гард в record(): если новый scenario-id начинается с id текущего активного сценария (cafe_order → cafe_order_complete, padaria → padaria_change) — игнорировать переключение, биты НЕ сбрасывать.
B. Промпт-гард в scene_beats_block (src/core/scenarios.py): «активный сценарий НЕ перетегивается — не эмить [[SCENARIO: …]] до тех пор, пока юзер сам не сменил тему».
C. Бэкстоп победы в record(): сценарий из каталога + steps_done >= len(beats) → вернуть win=True, даже если модель не эмитила [[WIN]].
D. Дефиниция BEAT_DONE в scene_beats_block: «[[BEAT_DONE]] ТОЛЬКО если юзер сам произнёс целевое содержание текущего бита на португальском (голосом или текстом) — никогда за „ок", подтверждение или мета-вопрос». (Репро шума: BEAT_DONE на «ок» без PT — daniel shy T2; пропуск реального закрытия — daniel cafe T4.)
E. U2 — /level в handlers.py: показать can-do список сцен из каталога со статусами (✅ закрыта / ◐ частично, n из m шагов / ○ не начата) и строку «до следующего уровня осталось N умений». Данные — из существующих scenario_state / can_do_count; если нужна новая таблица завершений — добавь миграцию alembic.

DEFINITION OF DONE:
1. Юнит-тест: record() при переключении cafe_order → cafe_order_complete НЕ сбрасывает steps_done.
2. Юнит-тест: каталожный сценарий, steps_done == len(beats) → win=True.
3. Сквозной прогон сцены кафе (привет → заказ → цена → спасибо) даёт WIN и can_do_count > 0.
4. /level выводит список сцен, а не «Твой уровень: A0. Засчитано умений: 0».

ВЕРИФИКАЦИЯ:
- uv run pytest — зелёный (новые тесты на A и C обязательны).
- Адаптивная симуляция сцены кафе (нужен OPENROUTER-ключ из .env), ход за ходом:
  uv run python -m tests.sim.run_adaptive daniel tests/sim/out_fix/daniel_cafe_win.jsonl --user "<реплика>"
  (повтори 4–5 раз с репликами, проходящими сцену; читай ответ бота перед следующей репликой). Проверь в transcript.jsonl: steps_done растёт монотонно, в конце win/level-сигнал.
- Live-чек: пройти сцену кафе в Telegram → 🎉 в чате, /level показывает первое умение.

НЕ СЛОМАЙ: session windowing (/new и 45-мин гэп), бюджет коррекций, существующее поведение каталога сценариев.
3

Слышит юзера и знает его уровень

усилие M
  • I1 · input-type: кириллица-доминантная реплика без PT-токенов → L1_UTTERANCE (код-бэкстоп в classify_input_hint); fallback в «Разборе» с PT_ATTEMPT на L1_UTTERANCE; эмоциональное раскрытие → подавлять 💡-карточку.
  • S3 · уровень в доктрине: «The student is A0» → параметр {level} + калибровка по бандам (A0 / A1–A2 / B1+: без «repita comigo», открытая продукция); A2+ пропускает приветственный бит сценария.
  • S2 · skip-if-done: реплика юзера уже закрывает текущий шаг → подтвердить, ответить на его вопрос и двигаться дальше; никогда не просить переделать.
Закрывает дыры
I1S3S2B3D1-хвостD7-хвост
Бот перестаёт учить русский русскому и дрессировать B1 как нулевого — «налог на спор» отменён, разблокирован A2/B1-сегмент.
src/core/conversation.py (classify_input_hint) · src/core/prompts.py (DOCTRINE_PT_IMMERSION, _EXPLAIN_DIRECTIVES) · src/core/scenarios.py
Готовый промпт для новой сессии самодостаточен · скопируй целиком
Ты — Claude Code в репозитории Linda AI (Telegram-бот-репетитор португальского; aiogram 3, Postgres, запуск через uv). Рабочая директория — корень репо. Это пакет фиксов №3 по итогам аудита R2: «Слышит юзера и знает его уровень». Прод-код менять можно и нужно.

ПРОЧИТАЙ ПЕРЕД НАЧАЛОМ:
0. docs/SPEC-v3-voice-workflow-and-fixes.md — решения владельца 2026-06-11 (единый голосовой воркфлоу A→C, RU-транскрипция для A0–A2, тесты только голосом, /settings). ПРИОРИТЕТ над этим промптом.
1. CLAUDE.md — принципы (тёплый взрослый тон; «говори с умным взрослым»).
2. docs/testing/GROWTH-AUDIT-R2-2026-06-10.md — §4 пункты I1, S3, S2 (P1) и §3 строки B3, D1, D7.
3. Точные репро: tests/sim/out_r2/anya_NOTES.md (N1 — захардкоженный A0), tests/sim/out_r2/marina_NOTES.md (N3 — skip-if-done, N4 — bom dia для A2, N5 — None на смешанных), tests/sim/out_r2/daniel_NOTES.md (N3 — L1_UTTERANCE мёртв).
4. Код: src/core/conversation.py (classify_input_hint ~стр.226, _recast ~стр.670), src/core/prompts.py (DOCTRINE_PT_IMMERSION ~стр.80, MODE_RECAST ~стр.209, _EXPLAIN_DIRECTIVES ~стр.301 и fallback "PT_ATTEMPT" ~стр.458), src/core/scenarios.py.

ФАКТЫ ИЗ АУДИТА: L1_UTTERANCE не выстрелил НИ РАЗУ за 91 ход; 11/32 ходов Дани = input_type None → его русский зачёркивается recast'ом, «Разбор» глоссит русские слова. Доктрина пишет «The student is A0 (near-zero)» всем, при том что в том же промпте профиль говорит «Current level: B1» — модель выбирает доктрину.

ЗАДАЧА — 3 блока, каждый отдельным коммитом:
A. I1 — input-type:
   - В classify_input_hint: реплика кириллица-доминантна И нет португальских токенов → вернуть "L1_UTTERANCE" (не None).
   - В _EXPLAIN_DIRECTIVES fallback: дефолт с "PT_ATTEMPT" на "L1_UTTERANCE" (директива L1 деградирует мягко для PT-попыток; обратное — нет).
   - Эмоциональное раскрытие (личное признание, фрустрация) → 💡-карточку подавить или «💡 ✓» без разбора (репро: anya emotional T1 — грамматическая карточка поверх «quero desistir»).
B. S3 — уровень:
   - DOCTRINE_PT_IMMERSION: заменить литеральный «A0» на {level} и добавить пер-band калибровку: A0 (как сейчас) / A1–A2 (без алфавитных дриллов, транзакционное ядро) / B1+ (НИКАКИХ «repita comigo», не скриптовать фразы, которые юзер уже произвёл, открытая продукция, регистр/стиль). Прокинуть cefr_level из профиля в .format().
   - То же в MODE_RECAST: «the student is {level}; calibrate strictness to genuine errors».
   - Уровень-гейт входного бита: в сценариях A2+ пропускает приветственный бит («скажи bom dia») и стартует с транзакционного ядра.
C. S2 — слух сцены: правило рядом с инъекцией текущего бита (scene_beats_block): «если реплика юзера УЖЕ закрывает текущий шаг — подтверди, ответь на его вопрос (если был) и переходи к следующему биту; никогда не проси сделать заново» (репро: marina padaria T3 — просит спросить цену, которую она только что спросила).

DEFINITION OF DONE:
1. Юнит-тест classify_input_hint: «меня бесит, что все тараторят» → "L1_UTTERANCE"; «fui no mercado» → не L1.
2. Симуляция: чистая русская реплика → recast НЕ зачёркивает русский, «Разбор» не глоссит русские слова.
3. B1-профиль: в собранном системном промпте нет строки «The student is A0»; в ответах нет «Repita comigo».
4. A2-профиль в сцене НЕ получает входной бит «скажи bom dia».
5. Реплика, закрывающая текущий шаг сцены, не вызывает просьбу переделать.

ВЕРИФИКАЦИЯ:
- uv run pytest (новый юнит-тест на classify_input_hint обязателен).
- Адаптивные симуляции по 2–3 хода (OPENROUTER-ключ из .env):
  uv run python -m tests.sim.run_adaptive daniel tests/sim/out_fix/daniel_l1.jsonl --user "меня бесит, что все тараторят" --probe explain
  uv run python -m tests.sim.run_adaptive anya tests/sim/out_fix/anya_level.jsonl --user "Oi Linda! Fui no banco hoje e resolvi tudo sozinha"
  Проверь input_type в jsonl и отсутствие A0-дрилла у anya.
- Live-чек: написать боту чистый русский — recast не зачёркивает.

НЕ СЛОМАЙ: иммерсию B1 (0 RU-пузырей — это работает), META_REQUEST-роутинг (D1 вылечен для тегированных входов), тёплый тон эмоциональных ответов.
4

Взрослая эксплуатация

усилие M–L · параллелится с 1–3
  • GitHub private repo: remote + push (сейчас у репозитория нет remote вообще — CI из RUNBOOK фикция).
  • Новый хост: бот + Postgres в docker-compose, перенос данных локальной БД.
  • CI-деплой: push в main → тесты → деплой → alembic upgrade head перед рестартом.
  • Fail-fast при schema drift: alembic current ≠ head → бот не стартует молча (именно так бот месяц прожил без памяти).
  • Sentry: подключить DSN, проверить тестовым эвентом.
Закрывает дыры
X1
Бот перестаёт жить на ноутбуке владельца; «молчаливая потеря памяти» становится технически невозможной.
git remote / GitHub Actions · хост (DO/Timeweb) · src/main.py (fail-fast) · docker-compose.yml
Готовый промпт для новой сессии самодостаточен · скопируй целиком
Ты — Claude Code в репозитории Linda AI (Telegram-бот; aiogram 3, Postgres, Docker, uv). Рабочая директория — корень репо. Это пакет фиксов №4 по итогам аудита R2: «Взрослая эксплуатация». ВАЖНО: секреты (.env, токены, ключи) НИКОГДА не коммитить — проверь .gitignore до первого push.

ПРОЧИТАЙ ПЕРЕД НАЧАЛОМ:
0. docs/SPEC-v3-voice-workflow-and-fixes.md — решения владельца 2026-06-11 (единый голосовой воркфлоу A→C, RU-транскрипция для A0–A2, тесты только голосом, /settings). ПРИОРИТЕТ над этим промптом.
1. CLAUDE.md — гвардрейлы (не коммитить секреты) и критерий «runs reliably 24/7».
2. docs/testing/GROWTH-AUDIT-R2-2026-06-10.md — §1 пункт 3 (TL;DR) и §4 пункт X1: у репо нет git remote; старый прод-инстанс поллил токен со старым кодом (токен уже ротирован, инстанс мёртв); миграции к локальной БД не были применены, и best-effort молча превратил бота в безпамятного.
3. Текущие Dockerfile, docker-compose.yml, Makefile, alembic.ini, migrations/, src/main.py.

ПЕРЕД СТАРТОМ СПРОСИ У ВЛАДЕЛЬЦА (не угадывай):
- Хостинг: DigitalOcean или Timeweb? Есть ли уже дроплет/сервер и SSH-доступ?
- GitHub: под каким аккаунтом создать private repo?
- Sentry: есть ли DSN (бесплатный план достаточен)?

ЗАДАЧА — 5 шагов, по коммиту/PR на шаг:
A. GitHub: проверить .gitignore (.env, logs/, downloads/, *.ogg — вне репо); создать private repo через gh repo create; git remote add origin; запушить main.
B. Хост: поднять сервер (Docker + docker-compose: бот + Postgres + volume); секреты — через .env на хосте (не в репо); перенести данные локальной Postgres (pg_dump → restore).
C. CI-деплой (GitHub Actions): на push в main — uv run pytest → ssh-деплой (docker compose pull/build + up) → ШАГ «alembic upgrade head» ДО рестарта бота. Секреты — в GitHub Secrets.
D. Fail-fast при schema drift: в старте бота (src/main.py) сверка alembic current == head; при расхождении — CRITICAL-лог + отказ стартовать. Убрать/обернуть best-effort пути, которые молча проглатывают ошибки записи истории (именно так бот потерял память с 06-08 без единого сигнала).
E. Sentry: подключить DSN (src/observability.py уже есть — проверь), отправить тестовый эвент, убедиться, что он виден в дашборде.

DEFINITION OF DONE:
1. git remote -v показывает GitHub; push в main запускает CI и деплоит на хост.
2. Бот отвечает в Telegram с хоста при ВЫКЛЮЧЕННОМ локальном инстансе.
3. Искусственный schema drift (alembic downgrade -1 на хосте) → бот НЕ стартует, в логах CRITICAL.
4. Sentry получил тестовый эвент.
5. В репозитории нет ни одного секрета (проверь git log -p | grep -i token/key точечно или gitleaks, если доступен).

ВЕРИФИКАЦИЯ: CI-ран зелёный; docker compose ps на хосте; /start в Telegram при остановленном локальном боте; намеренная ошибка в коде → эвент в Sentry.

НЕ ТРОГАЙ: промпты и core-логику (src/core/*) — этот пакет чисто инфраструктурный и параллелится с пакетами 1–3.
5

Мелочи одним проходом

12 × S · хвостом
  • Онбординг и личность: голос-ответ на вопрос цели (V1) · гендер + фикс «ты» (P1) · «anywhere» в RU-каркасе (E1).
  • Кнопки: [Текст]/[Разбор] навсегда, не ретирятся (O2) · объяснение «🆕» при первом нажатии (O3).
  • Подача: чанк-глоссы одним буллетом (T1) · стены повторов (T2) · slow_phrase без русских скобок (V2) · стрип [[CHUNK]] (V3) · D9 ремарки из пузыря.
  • Сессии и контекст: «что мы обсуждали?» — одна строка из памяти (U1) · тихий режим: text-first по контексту (U3).
Закрывает дыры
V1P1O2O3T1T2V2V3D9U1U3E1
Дюжина заусенцев, каждый из которых по отдельности «мелочь», а вместе — разница между «прототип» и «продукт».
src/core/prompts.py · src/core/conversation.py · src/core/voice.py · src/adapters/telegram/handlers.py
Готовый промпт для новой сессии самодостаточен · скопируй целиком
Ты — Claude Code в репозитории Linda AI (Telegram-бот-репетитор португальского; aiogram 3, Postgres, запуск через uv). Рабочая директория — корень репо. Это пакет фиксов №5 по итогам аудита R2: «Мелочи одним проходом» — 12 небольших фиксов, каждый усилием S. Один коммит на фикс (или мелкими группами по поверхности).

ПРОЧИТАЙ ПЕРЕД НАЧАЛОМ:
0. docs/SPEC-v3-voice-workflow-and-fixes.md — решения владельца 2026-06-11 (единый голосовой воркфлоу A→C, RU-транскрипция для A0–A2, тесты только голосом, /settings). ПРИОРИТЕТ над этим промптом.
1. CLAUDE.md — принципы (чанки как единицы, тёплый взрослый тон, никакого AI-speak).
2. docs/testing/GROWTH-AUDIT-R2-2026-06-10.md — §4: P1-пункты V1 и P1, таблица P2 целиком, Owner-findings O2 и O3.
3. Репро: tests/sim/out_r2/daniel_NOTES.md (N5, N6, N7), marina_NOTES.md (N6, N7), anya_NOTES.md (D9-строка таблицы).
4. Код: src/core/prompts.py, src/core/conversation.py, src/core/voice.py, src/adapters/telegram/handlers.py (онбординг-FSM, кнопки), src/core/notices.py, src/core/session.py.

СПИСОК ФИКСОВ (что → критерий готовности):
1. V1 — онбординг и голос: голосовой ответ на вопрос цели НЕ игнорируется — прогнать через STT и принять как цель (или минимум: вежливый ответ «надиктуй текстом»). Финал онбординга предлагает первую сцену из названной цели («Подготовимся к педиатру прямо сейчас?»), а не «начнём прямо сейчас» без начала.
2. P1 — гендер и регистр: поле gender в профиле (вопрос в онбординге или эвристика по имени с возможностью поправить), промпт-проза о юзере дегендеризована, согласование в PT по полю; «ты» зафиксировано в RU-каркасе (не скачет на «вы»); системные нотисы от Линды — в женском роде («не смогла обработать»).
3. O2 — кнопки навсегда: [Текст] и [Разбор] не ретирятся со старых сообщений. Нужен lookup истории по сообщению: колонка tg_message_id в messages (миграция alembic) + восстановление контекста колбэка из БД вместо in-memory. Ретирить только контекстные кнопки (Подсказка / Новая ситуация).
4. O3 — «🆕 Новая ситуация»: при ПЕРВОМ нажатии — однострочное объяснение («начинаю новую тему; старая уходит из контекста, прогресс сохраняется»); упомянуть в финале онбординга.
5. T1 — анти-чанк глоссинг: в MODE_LESSON правило «фикс-выражения (por favor, para viagem, até mais) глоссировать ОДНИМ буллетом как одну единицу, не по словам».
6. T2 — стены повторов: «чанк из REUSE-списка → только 🇧🇷-строка, без повторной полной карточки глосс».
7. V2 — slow_phrase: при извлечении стрипать скобочные глоссы и не-латиницу (репро: slow_phrase='Tá bom! (Хорошо!)' озвучит русский).
8. V3 — стрип [[CHUNK: …]] из текста реплики перед отправкой (регекс в conversation.py, как для recast-маркеров; сам тег для стейта сохранить).
9. D9 — чистота пузыря: в доктрину «никаких сценических ремарок „(segurando o celular…)" и аудио-инструкций („Escute e repita") в текстовом чате»; расширить strip_bubble_meta.
10. U1 — «что мы обсуждали?»: при старте новой сессии после гэпа, если юзер спрашивает о прошлом — одна строка «начали с чистого листа; в прошлый раз готовились к X» (X из durable-памяти/последней сессии), не молчание.
11. U3 — тихий режим: если юзер сообщает контекст тишины («я в очереди», «говорю тихо») — отвечать text-first, без войса в ответ.
12. E1 — RU-каркас без EN: надж в MODE_LESSON/доктрину «русский каркас урока — без английских вкраплений („anywhere")».

DEFINITION OF DONE: каждый пункт воспроизводим по своему репро из NOTES/аудита и больше не воспроизводится после фикса. Для 3 — миграция накатывается чисто (alembic upgrade head на копии БД).

ВЕРИФИКАЦИЯ:
- uv run pytest — зелёный; добавь юнит-тесты на 7 (slow_phrase) и 8 (стрип CHUNK) — они чисто строковые.
- Точечные симуляции (OPENROUTER-ключ из .env): uv run python -m tests.sim.run_adaptive daniel tests/sim/out_fix/daniel_p5.jsonl --user "..." на кейсы 5, 6, 12.
- Live-чек в Telegram: пункты 1–4 руками (онбординг с голосовой целью; кнопки на старых сообщениях; первый тап «🆕»).

НЕ СЛОМАЙ: двухсообщенческий флоу (реплика + 💡), идемпотентность кнопок (⏳), стрип глосс из озвучки, session windowing.
ИТОГ

Рекомендованный порядок

Порядок — по риску оттока: сначала доверие к обратной связи (главный churn-риск голос-first юзера, подтверждён владельцем), потом видимый прогресс, потом уровень. Инфраструктура не трогает промпты — идёт параллельно. Мелочи — хвостом, когда крупное уже стоит.

1 Доверие + произношение 2 Видимый прогресс 3 Уровень и слух 4 · эксплуатация — параллельно 5 · мелочи — хвостом

После фиксов — TMA v0 (durable-чанки): бот начинает durable-писать выученные фразы — это предпосылка дашборда и повтора. Полный план — на странице TMA.