06c752b проходят (повторный прогон 12.10)6d2d3bd проходят (12.10)continuation_split + settled_close в логах 8722 за 23–24.09 (часть — скриптовые пробы)Раньше один аят = одна сессия. Когда человек читал аяты подряд, сервер узнавал о конце аята N только после паузы, а приложение — ещё на один сетевой шаг позже. К этому моменту чтец уже начинал аят N+1.
Начало N+1 при этом терялось дважды. В сессии N оно засчитывалось как лишние звуки на последнем слове N. В сессию N+1 оно не попадало вовсе, поэтому первое слово N+1 становилось пропущенным или ошибочным.
Protocol 1.7 решает это втроём:
06c752b; на него не указывает отдельный ref, он предок 7872427. APP: 6d2d3bd.4e7c7e2) тоже называет себя «1.6», но по смыслу это другая 1.6. Boundary-кода в ней нет.06c752b + 4e7c7e2 даёт конфликты в 9 файлах.f8d5650, docs/service-contract.md).verse_complete приходит только после закрывающей свёртки:
SILENCE_EVIDENCE_SECONDS = 0.45;WAQF_SECONDS = 1.0 (streaming.py).continuation.py, такие вставки «damning», то есть делают слово ошибкой.streaming.py).| Дата | Наблюдение | Метка |
|---|---|---|
| 22.09 | Первый раунд на телефоне. 3 перехода, все с boundary_fallback_reason=no_boundary, то есть все через старый переход. Время перехода 560.7 / 646.3 / 565.4 ms. | HISTORICAL |
| 22.09 | Live-захват на 8722 (1.6, до B2): 17 сессий, 12 final. 12 из 12 — boundary=None: обычное закрытие по тишине «выигрывало гонку» у split. | VERIFIED пересчёт лога |
| 22.09 | Офлайн-переигрывание 7 реальных стыков из этого захвата: базовая версия ошибается на 5 из 7 (الحمد — skipped, الرحمن — mistake). Следующая сессия начиналась с середины аята. | HISTORICAL |
last_split_reason=gate стоит у 12 из 12 final. Пересчёт лога даёт: gate 6, e1_placement 4, e3_no_split 2. Вывод «12/12 без boundary» верен; формулировка «всегда gate» — нет.context_prefix_samples — под номером 1.7 (23.09). Позже, 10–11.10, ML Senior выпустил Mistake Evidence тоже как 1.6. Подробно — раздел 8.open.prior_tempo_baselinef8d5650 (Merge PR #8, тег phase1-end). В неё уже входит 84545d0 (frozen-слова неизменяемы).capabilities, объект boundary, audio_samples. Тестовый APK собран. Первый раунд на телефоне. Включено тестовое логирование RECITE_SPLIT_DIAG. Live-захват: 12/12 без boundary.settled_closecontext_prefix_samples/health 1.7 (13:15Z). Хостовые тесты: Ran 997, errors=2 (нет jsonschema), skipped=99. Первые split'ы на 1.7, APK A3, прогоны на телефоне.final: 15 settled_close, 6 continuation_split. Тест рекордера диагностики — PASS.06c752b → 8f69fc8 → 7872427 запушены в research-ветку. APP 9ea8a2b + 6d2d3bd запушены в integration/app-ml. 8722 перезапущен (PID с 11:30Z, причина неизвестна).da5b7c8, e9094db → phase1/eval-harness @ 4e7c7e2.06c752b| Стадия | Что делает | Wire |
|---|---|---|
| A0 | Кадры CTC-эмиссии, модуль continuation.py, офлайн-калибратор. Только данные для решений. | нет |
| A1 | Issue continuation: начало N+1 не засчитывается как вставка на последнем слове N. | нет |
| A2 | Закрытие N на стыке с N+1 (continuation_split). | через B1 |
| B1 | Gate boundary_replay, объект boundary, audio_samples. | «1.6» |
| B2 | Граница при обычном закрытии (settled_close). | значение source |
| A3 | context_prefix_samples: префикс переотправки — контекст, а не звук N+1. | «1.7» |
06c752b сам пишет: «Not run locally… these tests have not been validated by this commit». Позже тесты прошли на хосте 23.09 и локально 12.10 (раздел 6).06c752b — весь протокол 1.7: 23 файла, +9063/−194.8f69fc8 — поле issues в debug_record. Только отладка, на 8722 не развёрнуто.7872427 — shadow vowel-rescue. RESEARCH ONLY, не для деплоя, на 8722 нет.9ea8a2b — клиент: 23 файла, +6717/−54. 6d2d3bd — штамп reader-бандла.continuation_split или settled_close) → проверка в приложении → переотправка → следующий аят. Если проверка не прошла или границы нет, используется прежний переход.ready.context_prefix_samples приложение 6d2d3bd не проверяет (раздел 9). Ключ context_prefix_samples отправляется только при continuation_split; при settled_close префикс равен 0 и ключ не отправляется.Пути ML указаны относительно корня репозитория на 06c752b, пути APP — относительно reader-web/src/app/ на 6d2d3bd. Числа тестов — это количество def test_ / it( в файле.
open.capabilities разбирает contract.parse_capabilities. Неизвестные строки игнорируются; не-массив строк вызывает protocol_violation.service.stream_continuation без capability возвращает (None, False). Тогда A1, A2 и B2 выключены, а клиент получает поведение до 1.6, плюс boundary: null и audio_samples.check_protocol_version. Минор не сравнивается, функции включаются только через capability.ML_PROTOCOL_VERSION = '1.1' намеренно не повышен. capabilities: ["boundary_replay"] отправляется в каждом open, где у захвата есть кольцо истории; в production-роутере кольцо есть всегда (lib/recite-ml-verse-session.ts, lib/recite-ml-wire.ts).tests/test_boundary_protocol.py — 33 теста, в том числе OldClientTests, GatePolicyTests, test_the_same_audio_without_the_capability_is_the_legacy_session.recite-ml-boundary-replay.test.ts — «NEW APP + OLD SERVER», «OLD APP + NEW SERVER».useReciteMlTeacherController.continuous.test.ts — «every verse opens with the 1.1 handshake plus the boundary_replay capability».boundary = { current_judgement_end_sample, next_replay_start_sample, source } или null в verse_complete и final.
contract.boundary_payload.Boundary пишется один раз (streaming.py), и оба кадра строятся из неё, поэтому они равны по построению.matched_phones, evidence, …) наружу не отправляются.parseBoundary: обе координаты — безопасные неотрицательные целые; при B > A — проблема; при A = B — допустимо.source даёт boundary: null и запись о проблеме. Кадр не отклоняется.no_boundary).WireTests и NullBoundaryTests. APP: «every way a boundary can be malformed is a reason, never a throw», «10b. an unknown settlement is refused; both known ones are not»._verse_complete вызывает _settled_close_boundary, и A = B = конец последней размещённой фонемы N.end_audio без завершения аята границу не создаёт.continuation_split, только с префиксом 0. Отдельный describe: «a settled_close boundary is executed exactly like a continuation_split one».tests/test_ordinary_close_boundary.py — 23 теста (обычное закрытие, waqf, независимость от сегмента, null-случаи, инварианты).correct — на 6 из 8. Более раннее «8/8» было артефактом округления и отозвано.streaming._split_at_join вызывается из _fold:
continuation.split_candidate).[start, A); звук после A для N не оценивается.CONTINUATION_MIN_PHONES = 6, MAX_COST_RATIO = 0.25, MAX_UNHEARD = 1, SPLIT_CONTEXT_SECONDS = 2.0.
SPLIT_CONTEXT_SECONDS — «an empirical bound from three real joins».scripts/calibrate_continuation.py работает только офлайн.context_prefix_samples = A − B.test_continuation_split.py — 28 тестов на реальных стыках 1:3 и 1:6.test_continuation.py — 47 тестов (A0).test_continuation_issue.py — 32 теста (A1).test_emission_frames.py — 14 тестов.test_verse_complete_arrives_three_passes_earlier_than_the_silence_close фиксирует: на реальном стыке 1:3 split завершает аят на проходе 2 (3.84 s), закрытие по тишине — на проходе 5 (7.68 s).contract.parse_context_prefix_samples: целое от 0 до 16000 (1.0 s, длина waqf). Ненулевое значение требует boundary_replay.protocol_violation; значение не обрезается.ready.context_prefix_samples.assess.measure_segment: распознаватель слышит весь сегмент, но размещение фонем начинается с A − B.audio_samples и новая граница по-прежнему от сэмпла 0 = B.contextPrefixOf = max(0, A − B) по границе из final. Ключ отправляется, только если есть переотправка и префикс > 0. Эхо в ready приложение не читает.tests/test_context_prefix.py — 49 тестов, в том числе test_the_emitted_boundary_satisfies_the_clients_formula.recite-ml-wire.test.ts — «a settled_close boundary declares no prefix, a continuation_split does».got 19203 (≈ 1.2 s > потолка 1.0 s), следующий open завершился ошибкой;next_reference на проводе нет. Это внутреннее значение сервера: фонемы аята (сура, аят+1) из кэша эталонов. Через суру оно не переходит.stream_continuation → StreamSession(next_reference=…) → SessionState.next_reference. Используется только A1 (защита вердикта) и A2 (поиск стыка). Не двигает cursor, live-позицию и verse_complete.final.audio_samples — число принятых сэмплов; отклонённый кусок не считается.lib/recite-ml-capture-history.ts.verse_complete оно закрепляется с G, на переключении копируется [G, F).final проверяются: та же граница, audio_samples === samplesSent, линейный поток.ready и до живого звука.no_boundary, audio_samples_mismatch, history_evicted, …recite-ml-boundary-replay.test.ts — 33;recite-ml-capture-history.test.ts — 19;recite-ml-capture-provenance.test.ts — 32;useReciteMlTeacherController.continuous.test.ts — 31.ProvenanceTests.84545d0, WordDiffer). Boundary-работа DIFF_FIELDS и WordDiffer не меняет.[start, A): слова N, включая последнее, замораживаются.applyMlWordDiff не перезаписывает frozen-слово даже при большей revision.DIAG OPEN/SPLIT/FINAL (почему split принят или отклонён). Включается переменной RECITE_SPLIT_DIAG=1. Этого кода нет в Git: только локальный патч и дерево 8722.RECITE_TRANSITION, только при import.meta.env.DEV.EXPO_PUBLIC_RECITE_ML_TEST=1 → vite --mode development). В production-бандле его нет.| Что | Где и как | Результат |
|---|---|---|
| ML, 7 модулей boundary | Чистый git archive 06c752b; Python 3.13.15, torch 2.13; PYTHONPATH=src, без сети | 226 / 226 OK |
| ML, полный набор | то же | 1010: 9 fail/error, skipped 2 |
| ML, полный набор базы | git archive f8d5650 (1.5) | 784: те же 9 |
| APP, Recite ML + контроллер + TeacherMode | Дерево reader-web, по содержимому равное 6d2d3bd (23/23 blob совпали); vitest | 24 файла, 1166 / 1166 |
| APP, весь ридер | то же, параллельно с ML-прогоном | 1695 / 1702 |
test_debug_record, одинаковые на базе и на 1.7, то есть не регрессия. INFERENCE Причина: на диске машины свободно 2.5 ГБ при пороге рекордера 5 ГБ, поэтому запись не включается.readerModel, quran-pages, rn-embed). При изолированном повторе они проходят: 55 / 55.Ran 997, errors=2, skipped=99. Обе ошибки и 97 пропусков — нет пакета jsonschema. Модули boundary выполнялись.Ran 1028, errors=2, skipped=99, та же причина.| Захват | Протокол | open | Итоги final | Префиксы |
|---|---|---|---|---|
| 22.09 | «1.6» (B1, до B2) | 17 | 12 × None | — |
| 23.09 | 1.7 | 46 | 8 × split, 29 × settled_close, 4 × None | 7 приняты (80–200 ms); 1 реальный отказ 19203; 5 негативных проб |
| 24.09 (утро) | 1.7 | 8 | 2 × split, 3 × settled_close | 8950, 7031 (559 / 439 ms) |
| 24.09 захват #2 | 1.7 | 26 | 6 × split, 15 × settled_close, 2 × None | 6 приняты (160–241 ms) |
correct — 6 из 8. Регрессия на тех же 12 записях до и после B2: всё идентично, кроме добавленной границы.POST /align: различаться может только переход mistake → correct. Результат: 1:2 flipped [1]; 7:15 flipped [0]; 114:2 flipped nothing; A/B: PASS.next_reference в пределах суры. Последний аят суры получает boundary: null.Код серверов — по последнему подтверждению 06.10.2026, кроме 8723. Сегодня (12.10) хост недоступен, поэтому текущее состояние UNKNOWN.
| Функция | 8720 | 8722 | 8732 | 8723 |
|---|---|---|---|---|
| Назначение | прежний сервер | boundary / контроль | boundary + madd-off | Mistake Evidence |
/health protocol_version | 1.5 | 1.7 | 1.7 | 1.6 не проверено нами |
| Код | UNKNOWN точный коммит; H f8d5650 | f8d5650 + незакоммиченные файлы = 06c752b + SPLIT_DIAG H 06.10 | 8722 + MADD_OFF_ON_8722.diff H 06.10 | H b43e1c9; на хосте UNKNOWN |
capabilities: boundary_replay | игнор (нет поля) | да | да | игнор (нет поля) |
boundary в verse_complete / final | нет | да | да | нет |
continuation_split (A1/A2) | нет | да | да | нет |
settled_close (B2) | нет | да | да | нет |
context_prefix_samples | нет | да, ≤ 16000 | да | нет |
final.audio_samples | нет | да | да | нет |
reason, letter_errors, heard_text, extra_words | нет | нет | нет | да |
word_update с words: [] | нет | нет | нет | только клиенту, объявившему ≥ 1.6 |
| Проверка мадда | вкл. | вкл. | выкл. (RECITE_TEST_DISABLE_MADD_CHECK=1) | вкл. |
RECITE_SPLIT_DIAG diag | нет | вкл. | вкл. | UNKNOWN |
Что получает APP 6d2d3bd | старый переход | переотправка границ | переотправка + madd-off | старый переход (no_boundary); evidence не разбирается |
Что получает APP c4609fb (Phase 1, локально) | старый переход | переотправка, без evidence | переотправка, без evidence | evidence, старый переход |
| Последнее подтверждение | 06.10 | 06.10 | 06.10 | ни одной live-сессии |
check_protocol_version проверяет только мажор. Поэтому номер версии в open ничего не говорит о функциях. Функции boundary включает capability, а пустые кадры evidence — объявленный минор ≥ 1.6.| Mistake Evidence (ML Senior) | Boundary / replay (наша) | |
|---|---|---|
| Вопрос | Почему слово ошибочно | Какому аяту принадлежит звук |
| Меняет | содержимое слова: reason, letter_errors, heard_text; extra_words | жизненный цикл сессии, границы, владение звуком |
| Gate | объявленный минор ≥ 1.6 (только пустые кадры) | capability boundary_replay |
| Remote | phase1/eval-harness @ 4e7c7e2 | 06c752b (предок 7872427) |
INFERENCE Без boundary evidence на непрерывном чтении работает хуже: начало N+1 снова будет теряться или помечаться ошибкой. Только теперь с «объяснением», то есть с убедительно выглядящей неверной причиной.
f8d5650: contract.py, streaming.py, decisions.py, api.py, service.py, assess.py, документацию, schema, stub и test_streaming.py.git merge-tree 06c752b 4e7c7e2) даёт конфликты в 9 файлах; assess.py сливается автоматически.extra_words;continuation (A1).extra_words аята N.DIFF_FIELDS в evidence расширены (letter_errors, heard_text, reason). Правило frozen-слов должно сохраниться и для пробной оценки E4.| Номер | В нашей линии (8722/8732) | В линии ML Senior (8723, remote) |
|---|---|---|
| 1.5 | общий: open.prior_tempo_baseline (f8d5650) | |
| 1.6 | capabilities, boundary, audio_samples | reason, letter_errors, heard_text, extra_words, пустые word_update |
| 1.7 | context_prefix_samples | — |
Что уже живёт с этими номерами:
/health 8722 и 8732 отвечает «1.7».6d2d3bd объявляет «1.1».c4609fb, локально) объявляет «1.6», чтобы получать пустые кадры evidence.Evidence остаётся 1.6. Boundary (B1 + B2) становится 1.7, context_prefix_samples — 1.8 (или вместе 1.7). В контракт добавляется таблица соответствия research-номеров.
Объединённый сервер объявляет новый минор, который включает evidence и boundary. Минус: на время перехода «1.7» на /health 8722 будет значить другое.
Минор остаётся монотонным, а сервер явно сообщает поддерживаемые функции (например, эхо capabilities в ready). Это дополнение контракта. Нужно сохранить смысл gate «минор ≥ 1.6» для пустых кадров evidence.
Мы не меняли ML-код и не выбираем окончательную версию.
| Риск | Факт | Серьёзность |
|---|---|---|
| Коллизия версий | Две несовместимые «1.6». Gate evidence зависит от минора, gate boundary — от capability. | BLOCKER для слияния |
| Нет live-проверки объединённого сервера | Объединённого кода нет. Взаимодействие A1 ↔ extra_words и prefix ↔ evidence не проверено. | IMPORTANT |
| Регрессия непрерывного чтения | Слияние конфликтует в 9 файлах. Если boundary-код потеряется, сломается только непрерывное чтение, а одиночные аяты продолжат работать — это легко пропустить. 226 тестов boundary должны остаться зелёными. | IMPORTANT |
| Незакоммиченная диагностика на GPU | На 8722 код лежит незакоммиченным поверх f8d5650. RECITE_SPLIT_DIAG (api +69, streaming +113/−10) есть только там и в локальном бандле. | IMPORTANT |
| Сохранность test-only патчей | diag-8722.patch, MADD_OFF_ON_8722.diff и бандлы лежат вне Git на одной машине. На 06c752b нет собственного ref. | IMPORTANT |
| Неизвестное текущее состояние GPU | Последнее подтверждение — 06.10; 12.10 хост недоступен. Был ли перезапуск или изменения — UNKNOWN. | IMPORTANT |
| Потолок префикса 1.0 s | Live 23.09: split с A − B ≈ 1.2 s, сервер отказал, следующая сессия не открылась. Приложение префикс не ограничивает. | IMPORTANT |
| Эхо префикса не проверяется | APP не читает ready.context_prefix_samples. Сервер, молча игнорирующий префикс (8720/8723), неотличим от 1.7. | MINOR |
| Устаревшие места в контракте |
| MINOR |
| Повторное распознавание без префикса | INFERENCE На свёртке с префиксом ограниченный повтор в A2 (хвост длиннее 2 s) распознаёт звук без контекста префикса. | MINOR |
Galamat-IT/qurany-reciteresearch/recite-boundary-diagnostics-20260925 @ 7872427 (remote, проверено 12.10)06c752b — A0–A3, B1–B2, контракт, schema, stub, тесты8f69fc8 — debug_record issues (только отладка)7872427 — shadow vowel-rescue, RESEARCH ONLYf8d5650 (тег phase1-end, protocol 1.5), включает 84545d0phase1/eval-harness @ 4e7c7e2 (da5b7c8, e9094db, PR #10/#11); feat/phase1-mistake-evidence @ b43e1c9src/obadx_pipeline/contract.py — история версий, boundary_payload, parse_capabilities, parse_context_prefix_samplessrc/obadx_pipeline/streaming.py — _split_at_join, _settled_close_boundary, WordDiffersrc/obadx_pipeline/continuation.py, decisions.py, assess.py, service.py, recognizer.pydocs/service-contract.md — §4.1, §4.2, §4.6, §4.9, §8; docs/service-contract-v1.schema.jsonscripts/calibrate_continuation.py, scripts/contract_stub_server.pytest_boundary_protocol (33), test_context_prefix (49), test_continuation (47), test_continuation_issue (32), test_continuation_split (28), test_emission_frames (14), test_ordinary_close_boundary (23)eidillum/qurany-rnintegration/app-ml @ 6d2d3bd (remote, проверено 12.10)9ea8a2b — feat(recite): replay ayah boundaries with capture provenance15fab36; локальная ветка Phase 1 feature/recite-phase1-mistake-evidence @ c4609fb (на remote нет)reader-web/src/app/)lib/recite-ml-wire.ts — parseBoundary, buildOpenFrame, contextPrefixOflib/recite-ml-verse-session.ts — candidate, verify, replayBoundarylib/recite-ml-capture-history.ts, lib/recite-ml-provenance.ts, lib/recite-ml-capture-router.ts, lib/recite-ml-verse-sequencer.tslib/recite-ml-transition-trace.ts (DEV-only)components/reader/TeacherMode.tsx — resolveTeacherMlActiveAyah.continuous (31)docs/recite-phase1-mistake-evidence.md (на c4609fb) — раздел о двух «1.6»diag-8722.patch (22.09), B2 и A3 (23.09), диагностика тестов A3 — на машине разработки, вне Gitbaseline-8722.sha256), MADD_OFF_ON_8722.diff и выводы шагов развёртывания 8732 (06.10)word_update.06c752b на 4e7c7e2 или merge. Конфликтующие файлы: contract.py, streaming.py, decisions.py, api.py, service.py, контракт, schema, stub, test_streaming.py.extra_words: при boundary_replay начало N+1 на последнем слове N не должно попадать в extra_words или letter_errors аята N. Без capability — решить, что ожидается.[0, A − B) не должны появляться extra_words, вставки и heard_text; after_index и время должны считаться от сэмпла 0 = B.84545d0 должно сохраниться с расширенными DIFF_FIELDS.final: boundary, audio_samples и extra_words вместе; schema (required, enum для source), stub-сервер, примеры.test_streaming (224) плюс тесты evidence. На хосте поставить jsonschema (сейчас 99 пропусков).settled_close, последний аят суры, старый клиент «1.1», клиент Phase 1 «1.6».06c752b; решить судьбу RECITE_SPLIT_DIAG (отдельный TEST-ONLY коммит или отказ); сверить дерево 8722 с манифестом и подтвердить коммит 8723.assess.py про emission_frames.