Проект-предложение · разработчик → владельцу · июнь 2026

Mini App — это «вокруг» голосового чата.

Прогресс, библиотека ситуаций, повтор фраз, произношение — всё, что в чате физически не помещается, потому что чат линеен и всё уплывает вверх. Разговор остаётся в чате на 100%. TMA — панель, каталог и тренажёр вокруг него.

8фрикций аудита R2 — у каждой фичи есть номер
7экранов в 4 табах
4+1фазы доставки, v0 — за один заход
0новых сервисов-зависимостей: тот же Postgres, тот же compose

Основание: growth-аудит R2 (3 персоны × 91 ход + live-партия в Telegram) · TMA-VISION.md · JTBD.md. Правило документа: фича без наблюдённой фрикции на сайт не попала.

01

Проблема: бот работает, но молчит о главном

Ядро — разговор, recasting, подбор чанков — аудит признал сильным («коронка», «ровно JTBD»). Провал в другом: всё состояние продукта невидимо. Шесть фрикций ниже — не гипотезы, а наблюдения из 91 хода симуляций, live-партии и заметок владельца.

F1 · победы невидимы

Полная сцена кафе пройдена — система записала ноль

0 × WIN · can_do_count = 0

За 91 ход симуляций и полную живую сцену кафе (привет → заказ → цена → спасибо) — ни одного засчитанного умения. Бот празднует прозой, но система этого не записывает. Обещание «вижу свой рост» в продукте не существует.

/level работает, но это две строки: «Твой уровень: A0. Засчитано умений: 0» — прогресса не «видно»Аудит R2 · live 711772 · корень — S1/B2
F2 · чанки тонут

История чата — могила выученного

Выученные фразы уплывают вверх в скролле; durable-памяти нет — recent_chunks держит ~10 штук и перетирается. Перед аптекой достать «как там было „обезболивающее“?» — невозможно.

повтор живёт только внутри одного окна (recent_chunks ~10); поиск по истории Telegram бесполезен — чанки внутри длинных репликTMA-VISION F2 · аудит R2 §6
F4 + O1 · произношение без сигнала

«Поняли бы меня в padaria?» — ответа нет

Голосовое сообщение получает только текстовый recast. КАК произнесено — сигнала ноль. Хуже: на тяжёлом акценте STT выдаёт мусор, и бот уверенно сочиняет фразу, которой не было.

владелец: «итоговое сообщение не разбирается с точки зрения скоринга произношения»Owner-finding O1 · 2026-06-11
«Бундае и Юг верал лампы дикфей» → бот уверенно сочинил «Bom dia e você já almoçou?»Live 711800 · дыра R2
F3 · каталог невидим

Сценарии есть в коде — пользователь их не видит

В scenarios.py лежит каталог ситуаций с beat'ами — но выбрать «отрепетировать pediatra к завтра» неоткуда. А это ядро JTBD: подготовка к завтрашней реальной ситуации. Заодно сцены стартуют с «скажи bom dia» даже человеку с годом жизни в Бразилии.

scenarios.py есть, но пользователь не может его увидеть и выбрать; список из 6+ сценариев — это каталог, не сообщениеTMA-VISION F3 · аудит S3
F7 · тихое место

Очередь в банке — а бот шлёт войс

Discreet-режима нет. Контекст «я в людном месте» продукту неизвестен, переключить нечем.

«говорю тихо в очереди» → войс в ответLive 711811 · дыра U3
F5 + F8 · пульта нет

Настройки и профиль — слепая зона

Язык объяснений, доля иммерсии, строгость коррекции — без контролов вообще. Цели из онбординга нигде не показаны и не редактируются.

настройки разбросаны по slash-командам; каждый контрол = отдельная команда + FSMTMA-VISION F5/F8
F6 · онбординг в никуда

«Начнём прямо сейчас» — и ничего не начинается

Финал онбординга пассивен; цель не конвертируется в первую сцену. Голосовой ответ на вопрос цели — полная тишина: FSM проглатывает войс.

голосовой ответ на вопрос цели — полная тишина; финал «начнём прямо сейчас» ничего не начинаетLive-партия · дыра V1
02

Решение: семь экранов

Четыре таба: Home · Сценарии · Практика · Профиль («Практика» — хаб: повтор, произношение, история). Дизайн — на токенах Telegram themeParams: TMA выглядит частью Telegram, не вторым приложением. Контент в макетах — реальный, из транскриптов аудита.

9:41●●●● ⌁ ▮
Lindamini app
Oi, Daniel! 🇧🇷
A0 A1
3 из 5 умений до A1
Завтра у тебя
🩺 Поход к pediatra
«Ela está tossindo há três dias» забудется — отрепетируем за 10 минут?
Ты уже умеешь
Заказать кофе✅ ×2
🥖Купить хлеб в padaria✅ ×1
🛒Mercado◐ 2/4 шага
🩺Pediatra○ не начат
🏠Сосед на парковке○ не начат
🤫 Тихое место отвечать текстом, без войсов
эта неделя: 4 дня · 12 фраз · 1 🏆
Home
Сценарии
Практика
Профиль
ЭКРАН 2.1 · HOME / ДАШБОРД · фаза v0

Can-do карта: прогресс наконец-то видно

Прогресс — это состояние, а чат — поток: даже сообщение о победе само уплывает вверх. Дашборд держит на одном экране карту сцен (✅ закрыто / ◐ частично / ○ не начато), прогресс к уровню в can-do терминах («3 из 5 умений», не «A0.7») и CTA «ситуация на завтра» — ядро JTBD. Частичный проход тоже виден как ◐ 2/4 — ответ на «прошёл всю сцену в кафе и увидел ноль».

ЗачемF1: «за 91 ход + живую сцену кафе — 0 × WIN, can_do_count = 0»; «/level отвечает двумя строками» (live 711772). Тоггл «тихое место» на главной — F7: переключение должно быть в один тап, «прямо сейчас, я в очереди».
Под капотомкаталог scenarios.py × новая таблица scenario_completions + биты из scenario_state; прогресс уровня — существующий can_do_count против порога A1.
9:41●●●● ⌁ ▮
Lindamini app
Ситуации
🔍 найти ситуацию…
Выживание
🥖 Padaria — купить хлеб
✅ ×1
●● легко · 4 шага
cumprimentar → pedir → quantidade → pagar
🩺 Pediatra — приём у врача
●● средне · 5 шагов
«Жена ходит одна — пора пойти самому»
☕ Кафе🛒 Mercado🚕 Такси
Общение
🏠 Сосед📦 Доставщик в домофон🔄 Devolução — вернуть товар
✍️ Своя ситуация
«Завтра мне нужно…»
Home
Сценарии
Практика
Профиль
ЭКРАН 2.2 · БИБЛИОТЕКА СЦЕНАРИЕВ · фаза v1

Каталог «отрепетировать к завтра»

Карточка = сценарий + сложность + beat'ы человеческим языком + сколько раз закрыт. Кнопка [Отрепетировать] — deep-link в чат: бэкенд пишет ScenarioState и шлёт первое сообщение сцены, TMA закрывается — пользователь оказывается в чате, где сцена уже началась, а не в пассивном приглашении. Это же — посадочная точка финала онбординга (F6). «Своя ситуация» уходит в чат как freeform-goal.

ЗачемF3: «scenarios.py есть, но пользователь не может его увидеть и выбрать»; F6: «финал „начнём прямо сейчас“ ничего не начинает» (live-партия, V1).
Под капотомPOST /api/scenario/start → запись ScenarioState → сообщение через Bot API → WebApp.close(). Каталог v1 = существующие 6 сценариев + 2–3 новых (домофон, devolução) — расширение scenarios.py, не новая система.
9:41●●●● ⌁ ▮
Lindamini app
Произношение
история ▸
фраза для тренировки
«Quanto custa o pão?»
🔊 послушать🐢 медленно
🎤
зажми и говори
Последняя попытка 78/100
Quanto92 custa64 o88 pão41
custa — глуше «у» · pão — носовое «ão» 🔊 образец
попытки: 41 → 58 → 64 → 78 📈
Home
Сценарии
Практика
Профиль
ЭКРАН 2.3 · ТРЕНАЖЁР ПРОИЗНОШЕНИЯ · фаза v3

Единственное место, где есть ответ «меня поняли бы?»

Запись фразы → Azure Pronunciation Assessment → пословный скоринг, слабые слова подсвечены, история попыток показывает рост 41 → 78. Reference text задан, поэтому assessment работает даже с тяжёлым акцентом — Azure сравнивает с эталоном, а не угадывает (в чате STT-гарбление делает это невозможным). Очередь фраз — из копилки и ключевых фраз завтрашнего сценария. Важно: компактный скор появится и в чате (O1, «🎯 86% · слабое: queijo» в 🎤-строке) — тренажёр это deep-practice с retry-циклом, не единственная точка сигнала.

ЗачемF4 + O1: владелец — «итоговое сообщение не разбирается с точки зрения скоринга произношения»; live 711800 — из акцентного мусора бот сочинил «você já almoçou?». Тон фидбека — recasting-дух: что улучшить, не «FAIL».
Под капотомMediaRecorder → ffmpeg (уже в стеке) → wav 16kHz → Azure PA (тот же Speech key, что STT) → JSONB в pronunciation_attempts. Синхронно, 2–4 сек.
9:41●●●● ⌁ ▮
Lindamini app
Повтор
7 на сегодня
как спросить:
«Кто последний
в очереди?»
Quem é o último? 🔊
из сцены: 🏥 очередь
← не помнюпомню →
не помню → встретится завтра
помню → через 4 дня
Home
Сценарии
Практика
Профиль
ЭКРАН 2.4 · ПОВТОР ЧАНКОВ (SR) · фаза v2

Спейсед-повтор, который не спамит чат кнопками

Карточки RU → PT (вспомнить фразу), свайп «знаю / не знаю», озвучка голосом пользователя. SR-очередь требует UI «знаю/не знаю» — в чате это спам кнопками, здесь — жест. Дважды «не помню» → кнопка «потренировать в разговоре»: чанк возвращается в чат, в живой контекст — contextual SR из LINGUISTIC.md, не зубрёжка. Дневной лимит ~10 карточек: 2–3 минуты, вписывается в «10 минут утром» из JTBD.

ЗачемF2: «повтор живёт только внутри одного окна (recent_chunks ~10)» — закрепление выученного как система, не как удача скролла.
Под капотомочередь = chunk_cards WHERE sr_due_at <= today; интервалы 1→4→10→30 дней (не FSRS — отложен ещё в SPEC v2, схема совместима); TTS — существующий OpenAI, голос из users.tts_voice.
9:41●●●● ⌁ ▮
Lindamini app
Мои фразы (47)
🔍 анальг
analgésico
обезболивающее
🩺 pediatra · 3 дня назад
🔊
Quem é o último?
Кто последний (в очереди)?
🏥 очередь · 5 дней назад
🔊
Eu te amo
Я тебя люблю
💬 свободный разговор · 8 дней назад
🔊
Um café para viagem
Один кофе с собой
☕ кафе · 12 дней назад
🔊
всепо сценарию ▾
Home
Сценарии
Практика
Профиль
ЭКРАН 2.5 · ИСТОРИЯ ФРАЗ · фаза v0

«Как там было „обезболивающее“?» — за 5 секунд перед аптекой

Все когда-либо выученные чанки: PT + перевод + 🔊 озвучка + источник (сценарий/разговор) + дата. Поиск по обоим языкам — это use-case «достать фразу в момент реальной ситуации», survival job из JTBD. ⭐ закрепляет наверху и поднимает приоритет в SR-очереди. Никакой новой логики: это read-витрина той же таблицы, что и повтор, — один источник данных, два представления.

ЗачемF2: «выученные чанки уплывают вверх; поиск по истории Telegram бесполезен — чанки внутри длинных реплик».
Под капотомSELECT из chunk_cards + TTS on-demand с кэшем в audio_ref; ILIKE-поиск по chunk и translation.
9:41●●●● ⌁ ▮
Lindamini app
Профиль
D
Daniel
🇷🇺 русский → 🇧🇷 português
уровень: A0 (самооценка при старте)
изменить
Моя цель
«Сам ходить к pediatra и болтать с соседями»
✏️
Цифры
🏆 Сцен закрыто3
💬 Фраз в копилке47
📅 Активных дней12
🎤 Лучший pron-скор78
вступил 28 мая · первая закрытая сцена — кафе, 2 июня.
«3 недели назад ты не мог заказать кофе»
Home
Сценарии
Практика
Профиль
ЭКРАН 2.6 · ПРОФИЛЬ · фаза v1

Цель из онбординга — впервые видима и редактируема

native_lang, уровень, goals — наконец на экране и правятся. Правка пишет в те же колонки users, бот подхватывает со следующего сообщения — профиль и так загружается per-message. Статистика — скромная и внизу, в духе JTBD-фильтра: цифры — hidden depth, не front-and-center, без guilt-механик. Хронология — «3 недели назад ты не мог заказать кофе» — это emotional job «вижу свой рост», не геймификация.

ЗачемF8: «цели из онбординга нигде не показаны и не редактируются»; «профиль нередактируем после» (аудит, ось 6).
Под капотомUPDATE тех же колонок users; «активных дней» — count distinct days по messages. Ноль новых таблиц.
9:41●●●● ⌁ ▮
Lindamini app
Настройки
Голос Линды
nova▶ послушать
ash▶ послушать
sage▶ послушать
ballad▶ послушать
🤫 Тихое местотекст вместо голосовых — для очередей и офиса
Язык объяснений
авто (по уровню)
всегда русский
всегда португальский
Сколько португальского в речи
меньшебольше
Строгость исправлений
мягко — почти не поправлять
сбалансированно
строже — поправлять чаще
🐢🐇
скорость голоса
Home
Сценарии
Практика
Профиль
ЭКРАН 2.7 · НАСТРОЙКИ · фаза v1 · показан в light-теме (themeParams)

Пульт управления вместо россыпи slash-команд

Голос — тот же users.tts_voice, что меняет /voice, но с прослушиванием: в чате preview = 4 голосовых подряд (спам), здесь — кнопка ▶. Тихое место — бот перестаёт слать войсы (дублируется тоглом на Home). Язык объяснений, доля иммерсии, строгость — три контрола, у которых сегодня нет никакого UI. Это параметры существующих механик (correction budget, доля PT в промпте), не новые механики. Этот макет — в светлой теме: TMA автоматически следует теме Telegram через themeParams.

ЗачемF5: «настройки разбросаны по slash-командам; язык объяснений, доля иммерсии, строгость коррекции — без контролов вообще»; F7: live 711811 — «говорю тихо в очереди» → войс в ответ.
Под капотомодин JSONB users.prefs (delivery_mode, explanation_lang, immersion_share, strictness, tts_speed); бот читает prefs перед отправкой ответа.
03

Три сценария дня

Как чат и TMA работают вместе. Заметь: ни один шаг разговора не переехал в WebView — TMA только готовит, фиксирует и достаёт.

а · утро, 10 минут после кофе

«Завтра идём к pediatra»

1
TMAОткрывает Библиотеку, карточка «🩺 Pediatra — приём у врача», тап [Отрепетировать].
2
ЧатTMA закрывается — сцена уже началась: «Bom dia! Vamos treinar…». Голосовая репетиция по beat'ам, recasting как обычно.
3
ЖизньНа приёме говорит сам: «Ela está tossindo há três dias».
4
TMAВечером дашборд показывает 🩺 Pediatra ✅ — +1 сцена на can-do карте, прогресс к A1 сдвинулся.
Закрывает F3 + F1: каталог видим, победа записана.
б · момент победы

Win в чате → прогресс на экране

1
ЧатДожал сцену кафе голосом — бот: «🎉 Ты только что полностью разыграл заказ в кафе» + кнопка [Посмотреть прогресс].
2
TMADeep-link ?startapp=home: ☕ Кафе ✅, полоса «3 из 5 умений до A1» выросла на глазах.
3
TMAТут же — «Завтра у тебя: 🩺 Pediatra» → следующая петля начинается сама.
Закрывает F1: «нечем гордиться» → постоянное место, где гордость живёт. Требует фикса S1 в боте (win сейчас не зажигается).
в · очередь в банке

Тихое место + фраза в момент нужды

1
TMAВ очереди: тоггл 🤫 Тихое место на Home — один тап.
2
ЧатШёпотом спрашивает, как занять очередь — бот отвечает текстом, без войса: «Quem é o último?».
3
ЖизньПроизносит фразу живому человеку. Сработало.
4
TMAФраза уже в Истории с переводом и 🔊 — перед следующей очередью достаётся поиском за 5 секунд.
Закрывает F7 + F2: live 711811 («говорю тихо — а бот отвечает голосом») и могилу чанков.
04

Архитектура: один бэкенд, одна база

TMA — не второй продукт, а второе окно в те же данные. Тонкий FastAPI импортирует существующие src/core и src/db напрямую: те же репозитории, тот же Postgres, ноль дублирования доменной логики.

Telegram: чат с ботомголос, recasting, сцены — ядро, не трогаем
Telegram: TMA WebViewPreact + Vite, ~4KB, темы из themeParams
bot — aiogram 3существующий контейнер
webapp — FastAPI новый/api/* (REST для TMA) + /app/* (статика)
Postgres — существующийusers · messages · chunk_cards · pronunciation_attempts · scenario_completions
Caddyreverse-proxy, авто-TLS — Telegram требует HTTPS для TMA
Azure Speech + OpenAI TTSpron-assessment · озвучка/preview — ключи уже в стеке
Auth без паролей

Telegram WebApp SDK даёт initData; middleware проверяет HMAC-подпись bot-токеном, user.id = users.id. Invite-whitelist — тот же, что в боте.

Realtime не нужен — честно

Данные меняются от действий самого юзера (закрыл сцену в чате → открыл TMA через минуты). Refresh-on-open покрывает всё. Никаких WebSocket в v1.

Честное ограничение

Нужен домен + HTTPS — единственное новое внешнее требование. И это всё равно делать: аудит X1 показал, что прода сейчас нет (бот живёт на ноутбуке) — деплой-заход идёт параллельно.

05

Roadmap: сначала данные, потом стекло

Каждая фаза — юзабельный инкремент. Главная зависимость: бот должен начать durable-писать данные — сейчас выученные чанки живут в JSONB на ~10 штук и перетираются, а закрытия сцен не записываются вовсе. Без этого у TMA нет контента.

v0-данныеусилие M · бот

Пререквизит: бот начинает писать

Чанки → chunk_cards через sentinel-механизм (как [beat: done]); победы → scenario_completions на CanDoWin; фикс S1/B2 «win не зажигается» (свободные scenario-id сбрасывают стейт — корень найден); миграция 0004.

Убиваетфундамент F1 + F2 — без этого экраны пусты
v0 TMAусилие M

Скелет + Home + История фраз

FastAPI + initData-auth + Caddy/домен + Preact-каркас. Экраны: Home (can-do карта) и История фраз. Deep-link «🎉 → посмотреть прогресс». Самый болезненный сигнал аудита — «прошёл сцену, увидел ноль» — чинится самым дешёвым после персистенса способом.

УбиваетF1 победы невидимыF2 чанки тонут (просмотр)
v1усилие M–L

Библиотека + Настройки + Профиль

Библиотека (+2–3 новых сценария в каталог), TMA→чат анкеровка сцены, фикс финала онбординга, настройки (голос с preview, тихое место, язык объяснений, строгость, иммерсия), профиль с редактируемыми целями; бот читает prefs. Четыре фрикции одним дешёвым экраном — почти всё это колонки, которые уже есть.

УбиваетF3 каталогF5 настройкиF6 онбордингF7 тихое местоF8 профиль
v2усилие S–M

Повтор чанков (SR)

Очередь, свайпы, TTS. Намеренно после паузы: к этому моменту с v0 в chunk_cards накопится 50–100 фраз — очередь повтора сразу осмысленна, а не пуста.

УбиваетF2 чанки (закрепление)
v3усилие L

Тренажёр произношения

MediaRecorder → ffmpeg → Azure assessment → пословный UI, retry, история попыток. Дифференциатор (нет ни у Duolingo-чата, ни у Chatty) — но самый тяжёлый кусок: аудио-pipeline в WebView. Делаем, когда остальное уже приносит ценность ежедневно. Компактный скор в чате (O1) едет раньше — в пакете фиксов голосового recast.

УбиваетF4 произношение без сигнала
06

Что сознательно НЕ делаем

Anti-scope — такая же часть предложения, как и scope. Каждый пункт — решение, а не упущение.

Чат внутри TMA

Разговор живёт в Telegram-чате — дублировать значит расщепить продукт и историю. TMA только открывает чат deep-link'ом.

Стрики, XP, огоньки, лиги

JTBD прямо: streaks — generic и потенциально toxic. Прогресс = can-do («ты теперь умеешь»), не календарь вины. «Активных дней» — пассивная цифра внизу профиля, не механика.

CEFR-экзамены и тесты уровня

Anti-job из JTBD §7: уровень — побочный индикатор от can-do, не цель. Меряем «can order coffee», не «A1.2».

Лидерборды и соцмеханики

Один-два пользователя; job социального сравнения отсутствует.

FSRS в v1

Отложен ещё в SPEC v2. Простые интервалы 1→4→10→30; схема sr_interval / sr_due_at совместима с FSRS, если позже захочется.

Realtime (WebSocket / SSE)

Нечего стримить: данные меняются действиями самого юзера. Refresh-on-open. Единственный будущий кандидат — live-скор произношения — и тот живёт на request/response.

Отдельный хостинг фронта / CDN / SSR

Статику раздаёт сам FastAPI на том же хосте. Семь экранов для одного сегмента — SEO и SSR не существуют как задачи.

Свой дизайн-язык

Цвета из Telegram themeParams — TMA выглядит частью Telegram, не «вторым приложением». Светлый макет настроек выше — демонстрация именно этого.