Phase 1 — Mistake Evidence, мобильная часть A0–A7Ветка: feature/recite-phase1-mistake-evidenceБаза: integration/app-ml @ 6d2d3bdHEAD: c4609fb (только локально, не запушено)Дата: 12.10.2026
Needs TestingLocally implemented, awaiting live validationВсё сделано и проверено офлайн. Проверки против реального сервера 8723 и на телефоне ещё не было.
Локальная запись сессии: те же байты, что ушли в сокет.
Карточка слова, лишние слова, экран «Ошибки», цвета, строгость.
Прежний протокол и boundary 1.7 не сломаны (тесты зелёные).
Verified
Что протестировано
TypeScript: ридер и RN — без ошибок.
Unit / component тесты: ридер 83 файла, RN 95 наборов.
Код приложения против stub-сервера ML Senior по настоящему WebSocket (не реальный ML).
Android release APK собран.
Needs Testing
Что ещё не подтверждено
Реальные ready / word_update / final от 8723.
Кадр word_update с words: [] (stub его не шлёт).
APK на телефоне: отрисовка, прослушивание, файлы.
iOS нативная сборка.
02Выполнение A0–A7
Принцип плана сохранён: всё, что решает «ошибка или нет» и «какая именно», остаётся на сервере. Приложение хранит аудио, рисует и проигрывает. Цвет — из state + reason, подсветка — из character_span / character_index, отрезок — из start_seconds / end_seconds.
A0
Protocol 1.6 integration
Stub verified · live pending
Реализовано
open объявляет protocol_version: "1.6" и выбранную строгость. Разбор новых полей слова и extra_words на ready / word_update / final, ready.session_id. word_update с words: [] и изменившимся списком лишних слов применяется. Ревизии: меньшая или равная игнорируется, замороженное слово не меняется. Список extra_words заменяется целиком. Неизвестный reason → unclear. Битые поля улик отбрасываются, не ломая кадр и вердикт.
Против stub: все кадры приняты без ошибок разбора, поля дошли до состояния приложения.
Проверка
Verified unit + stub Needs Testing 8723
A1
Session audio recording
Offline verified
Реализовано
Наблюдатель на RN-мосте пишет ровно те PCM-байты (16 бит LE, 16 кГц, моно), которые принял сокет, начиная с первого кадра после open. Смещение = секунды × 32 000. Папка recite-sessions/<session_id>/: audio.pcm, meta.json, final.json (кадр final как есть) или partial.json + статус «incomplete». На сервер ничего дополнительно не уходит. Удаление одной и всех сессий. Срок хранения 30 дней — сохраняемая настройка.
Stub-сессия: отправлено 163 840 байт = записано 163 840, побайтно идентично; 0,82 с → байт 26 240 попадает в нужный чанк. Прерванная сессия даёт partial.json.
Проверка
Verified unit + stub Needs Testing файлы на устройстве
A2
Word evidence index
Offline verified
Реализовано
По каждому событию сохраняются index, state, revision, frozen, reason, confidence, времена, vowel_errors, letter_errors, heard_text и extra_words. После final источником истины становится final.json. В списке подтверждённых ошибок — только frozen.
Компоненты
recite-ml-recording.ts (свёртка по правилам ревизий), parseRecordedSession, buildErrorRows
Результат
Сохранённая сессия читается тем же парсером, что и живой поток.
Проверка
Verified unit + stub Needs Testing 8723
A3
Mistake detail card
Component verified
Реализовано
Тап по отмеченному слову открывает карточку: причина, «Ожидалось» с подсветкой конкретной буквы (красный) или хараката (жёлтый), «Услышано». Индексы — кодовые точки полного текста аята (ready.uthmani). Подсветка по графемному кластеру, вставка — маркер между символами. Прослушивание max(0, start−0,3) … end+0,3; null-граница берётся у соседа; skipped — от конца предыдущего до начала следующего; нет времён — кнопки нет.
Пример контракта 1:5: отрезок [11,12) подсвечивает عْ; индекс 36 — касру в نَسْتَعِينُ.
Проверка
Verified unit + render-тесты Needs Testing звук на телефоне
A4
Extra words in Mushaf
Unit verified
Реализовано
Лишнее слово — отдельная красная вставка с heard_text между словом after_index и следующим (null — перед первым). Соседнее слово не перекрашивается. Незамороженная вставка приглушена и с пунктирной рамкой. Та же карточка и прослушивание.
Stub: ثُمَّ после слова 1 сначала unfrozen, затем frozen.
Проверка
Verified unit + stub Needs Testing вёрстка на телефоне
A5
Errors screen and history
Logic verified
Реализовано
Кнопка «Ошибки» рядом с ML-статусом. История по сессиям и группировка по суре. Источник — сохранённый final или partial (метка «Не завершена»). Только frozen-ошибки и лишние слова; unclear — отдельная группа «Не удалось разобрать». Карточка и прослушивание, удаление сессии и всех записей.
Компоненты
ReciteErrorsSheet.tsx, мост reciteRecordings (list / clip / delete / deleteAll)
Результат
Логика строк и разбор сохранённых сессий покрыты тестами. Сам экран рендер-тестом не покрыт.
Проверка
Verified логика Needs Testing экран на телефоне
A6
Mistake colors and reasons
Unit verified
Реализовано
wrong_letter и skipped — красный, wrong_vowel — жёлтый, unclear — серый, подпись причины под подтверждённым словом. До frozen оценка рисуется приглушённо, без подписи. ML-скоринг на клиенте не дублируется. Сервер без поля reason (до 1.6) получает прежний вид без изменений.
Компоненты
teacherMistakeAppearance, resolveMlEvidenceMark
Результат
Таблица отрисовки контракта закреплена тестами.
Проверка
Verified unit Needs Testing вид на телефоне
A7
Strictness setting
Stub verified
Реализовано
Мягкий / обычный / строгий. На проводе: gentle / standard / strict («normal» сервер не примет). Сохраняется на устройстве, передаётся в open, пишется в meta.json. Изменение действует со следующего аята. Серверная логика не менялась.
Код приложения ↔ contract_stub_server.py (локально, настоящий WebSocket)
Кадры без ошибок; запись побайтно совпала; прерванная сессия → partial
Verified(stub)
Android APK build
Release APK, endpoint 8723, тестовый ридер
BUILD SUCCESSFUL, 20 мин 33 с; URL 8723 в бандле
Verified(сборка)
JS-бандлы Android и iOS
Metro / Hermes собирает весь RN-код
Оба собраны
Verified
Реальный E2E с ML (8723)
Сессии с настоящей моделью
Не выполнялся: внутренняя сеть ML-сервера недоступна
Blocked
APK на телефоне
UI, звук, файлы на устройстве
Не устанавливался
Needs Testing
iOS native build
Xcode-сборка
Нет Mac в окружении
Blocked
Тесты против stub-сервера — это контрактные тесты, а не реальный E2E с ML. Stub отдаёт заранее записанную сессию аята 1:5 без модели.
* В Windows-окружении первый шаг скрипта (generate-quran-search-index --check) падает из-за autocrlf — это известная проблема, не связанная с Phase 1. Сами guard-проверки прогнаны отдельно без этого шага и пройдены.
05Ограничения
Blocked
8723 не проверен по реальной сети
Машина разработчика вне внутренней сети ML-сервера, порты 8720/8722/8723 недоступны. Нужна проверка из офисной сети.
Needs Testing
APK не протестирован на телефоне
Собран, но не установлен. Отрисовка, WebAudio-прослушивание и запись файлов на устройстве не подтверждены.
Blocked
iOS native build не подтверждён
Проверен только JS-бандл для iOS. Нативная сборка требует Mac.
Needs Testing
Нужна совместная проверка с ML Senior
Кадр words: [], реальные причины и времена, сценарий Тартиля.
Needs Testing
Хранение аудио
Записи лежат в cache-папке приложения: так они не попадают в облачный бэкап на обеих платформах без нативного кода. Но ОС может очистить cache при нехватке места раньше 30 дней. Полноценный isExcludedFromBackup / правила бэкапа Android требуют нативного модуля. Настройку срока хранения пока нельзя изменить из UI.
Needs Testing
UI
Лишнее слово — вставка между словами (не метка на поле). Переключатель строгости — в экране «Ошибки». Тексты подписей — заглушки на английском и индонезийском (языки ридера). Подписи под словами на длинных строках не проверены визуально.
Совместимость 1.6 и 1.7. «1.6» в задании ML Senior — это mistake evidence. В исследовательской ветке «1.6/1.7» означали boundary (boundary, capabilities, context_prefix_samples). Сервер 8723 собран без boundary-работы: capabilities игнорирует, boundary не присылает. Приложение при этом ничего не ломает и переходит к следующему аяту прежним способом, но без boundary replay при непрерывном чтении. Boundary-код в приложении не менялся.
06Что необходимо для завершения Phase 1
Мобильная часть A0–A7 реализована, unit / component / stub-тесты зелёные.
Android test APK собран с endpoint 8723.
Установить APK, проверить в Profile → ML server, что сохранённый override не перебивает 8723.
Живая сессия на 8723: ready → word_update → final без ошибок разбора.
Поймать word_update с words: [] и изменившимися extra_words.
У каждого красного, жёлтого и серого слова есть подпись.
Тап показывает подсвеченный символ, ожидалось / услышано и проигрывает свой голос в нужном месте.
Лишнее слово видно между словами, соседи не краснеют.
Аудио не покидает телефон; удаление работает.
Экран «Ошибки» показывает ошибки сессии и суры из сохранённых final.
Сценарий Тартиля: те же суры и намеренные ошибки на обоих приложениях.
Решения продакта: хранение (cache или Documents + исключение из бэкапа), форма лишнего слова, тексты подписей, показ unclear.
iOS: нативная сборка и проверка на устройстве.
Code review и решение о push / MR (пока всё локально).
07Информация для ML Senior
Подключение
Тестовый endpoint: ws://<внутренний тестовый сервер ML>:8723/stream (адрес у команды)
open.protocol_version: "1.6"
open.strictness: gentle | standard | strict (по умолчанию gentle)
Аудио: pcm_s16le, 16 000 Гц, моно, кадры по 320 мс
Приложение также шлёт capabilities: ["boundary_replay"] (8723 его игнорирует) и при наличии prior_tempo_baseline
word_update с words: [] при изменении только extra_words
extra_words — всегда полный список, заменяется целиком
Ревизия ≤ текущей игнорируется; frozen-слово не меняется
Что проверить вместе
Живая сессия на 8723 с APK, на нескольких аятах.
Кадр words: [] реально приходит.
ready.session_id присылается всегда (иначе запись получает локальное имя).
Времена слов совпадают с локальной записью при прослушивании.
Подсветка character_span на шадде, мадде, вставке.
Номер версии: объединить evidence и boundary в одну ветку.
Evidence 1.6 ≠ boundary 1.7. Сервер проверяет только мажорную версию, поэтому "1.6" принимают 8720, 8722 и 8723. Но boundary-поля на 8723 отсутствуют. Нужен единый номер версии после объединения веток.