diff --git a/doc/call_audit_findings.txt b/doc/call_audit_findings.txt index 009b3b09..22b7c26d 100644 --- a/doc/call_audit_findings.txt +++ b/doc/call_audit_findings.txt @@ -11,9 +11,12 @@ call_audio уменьшали pending новой привязки и обход check202/202 PASS,proxy9/9,burst1/1,load8/8,176с. Актуальный результат — раздел10; история/план — разделы6–7, внедрение TX — раздел8, диагностика mesh — раздел9. Полная проверка аудио, видео и платформенных разделов не завершена. -При продолжении подтверждён дефект исходящей PCM-очереди call_headless: -переполнение оставляет header без тела и нарушает поток. Реализация ещё -не менялась; аудит остановлен перед исправлением. Подробности — раздел11. +При продолжении подтверждён и по запросу пользователя исправлен дефект +исходящей PCM-очереди call_headless: header+PCM принимаются/отбрасываются +целиком, partial send сохраняет хвост, потери имеют отдельный учёт/диагностику. +Новая постоянная регрессия11/11 и штатный headless e2e1/1 PASS; +native build и check203/203 PASS,proxy9/9,burst1/1,load8/8,188с. +Диагностика/план — раздел11, внедрение/актуальная проверка — раздел12. База проверки: f79b9261, чистое рабочее дерево после предыдущего push stream. 1. Подтверждённый дефект: call_start не проверяет обычную занятость @@ -697,3 +700,96 @@ Pressure завершается ожидаемым exit1; allocations0->0. Ло call_headless.h теперь[~]. Call0[V]/11[~]/10[ ], архитектурный документ[~], видео[ ] сохранены. Новый полный build/check, sanitizers, аппаратный GUI/Android, Windows/FreeBSD здесь не запускались. Исправления предыдущих разделов сохранены. + +12. Headless PCM: целый кадр в ограниченной очереди — 26.10.2026 + +Пользователь «исправляй» разрешил внедрение плана раздела11. Базаae9d3f01; +коммиты913ae531/7c4e6f99/0ad80f39. Изменены только call_headless.c/.h, +tests/test_call_headless_queue.c и его цель в tests/Makefile.am; остальные +изменения этого шага относятся к документации/чек-листу. Wire и публичный API +сохранены, реализация radio_headless и общий call_audio DSP не менялись. + +Реализация + +ca_write_frame собирает header11+PCM до единственного вызова ca_send_raw. +Буфер ограничен CA_PCM_FRAME_BYTES1920+CALL_AUDIO_HDR_SIZE11, находится +на стеке; oversize и ненулевой len с NULL отклоняются с ERROR до memcpy. +При ожидающем tail очередь проверяет место для целого нового кадра: +если его недостаточно, ни header, ни PCM не дописываются. Успешный partial +send сохраняет только неотправленный tail; следующие кадры стоят после него. +Нет новых динамических выделений, таймеров или дополнительных владельцев. + +Переполнение увеличивает pcm_dropped клиента и c_pcm_dropped сервиса; +decode-drop остаётся отдельным показателем. WARN показывает call_id/fd, +размер кадра, send_len/offset/capacity и оба счётчика. Первый drop логируется +сразу, следующие WARN ограничены одним в секунду (10000 timebase units). +При закрытии клиента и destroy сервиса INFO выводят полные итоги pcm_drop, +включая потери между WARN. Использованный prefix буфера до полного drain +не компактизируется: это прежняя политика ёмкости, целостность не нарушается. + +Постоянная регрессия + +tests/test_call_headless_queue.c включён в check_PROGRAMS для !OS_WINDOWS; +нужен GNU --wrap=send. Стенд включает production .c, как другие компонентные +тесты проекта. Один настоящий uasync на поток, свежий loopback TCP listener +с port0/клиент/HELLO и настоящие Opus encoder/decoder для каждого сценария. +Минимальный instance не создаёт call_ctx/роутер: media callback вызывается +напрямую в том же потоке. Полноценный звонок двух ядер сохраняет штатный e2e. +Поля очереди только наблюдаются, она заполняется обычными media callbacks. +Wrapper задаёт EAGAIN/short write только серверному fd; успешный prefix +действительно передаётся через TCP. Независимый reference decoder обрабатывает +все Opus-пакеты, включая отброшенные для TCP, сохраняя такое же состояние. +Сравнивается весь ожидаемый wire header+PCM байт в байт, без поиска похожего +следующего header. CHECK действует и при NDEBUG. + +11 сценариев PASS: +- normal:8 кадров без backpressure, затем следующий после drain; +- frame_boundary:34 попытки,33 принятых,1 целый drop; +- overflow:40 попыток,33 принятых,7 целых drops; +- partial_header:первые5 байт переданы TCP, далее EAGAIN;40/33/7; +- partial_pcm:первые113 байт переданы TCP, далее EAGAIN;40/33/7; +- exact_fit:первые118 байт переданы,34 кадра заполняют очередь ровно65536, + потерь нет; exact_overflow:35-я попытка отбрасывается целиком; +- chunked_write:prefix5, затем drain порциями не более127 байт; +- offset_overflow:после33 queued кадров отправлены118 байт, новая попытка + отбрасывается без изменения send_len/offset; оставшийся поток корректен; +- destroy_pending:8 queued кадров освобождаются без drain; +- invalid_output:oversize1921 и NULL+1920 дают ровно2 ожидаемых ERROR, + не изменяют очередь/переданные байты; следующий допустимый кадр проходит. +После каждого обычного drain проверен следующий кадр с непрерывным состоянием +Opus. В каждом сценарии проверяются точные PCM-счётчики закрытия/destroy, +наличие WARN, баланс uasync socket/timer и allocations0->0 после cleanup. +Лог /tmp/call-headless-fix-queue.log прочитан полностью:11/11 PASS; +6 первых overflow-WARN и2 намеренных ERROR, неожиданных ERROR нет. + +Чувствительность теста проверена без замены production/архивов/объектов: +временная копия регрессии включает старый instrumented .c из раздела11. +Normal PASS, на frame_boundary старый код оставляет header, queued63734, +отбрасывает PCM1920 при space1802. Проверка ожидаемой длины сразу FAIL, +ожидаемый returncode=-6 (CHECK/abort), core dump отключён для этого запуска. +До abort первый fixture освобождён с allocations0->0; cleanup второго +прерван и его баланс этим негативным запуском не заявляется. +/tmp/call-headless-fix-old-test.c/.mk, ...-old-build.log, ...-old-test.log. + +Проверка окончательного исходного кода + +./build.sh --no-gui -j4 PASS; после финальной правки0ad80f39 целевая +make-сборка PASS, новая регрессия11/11 и штатный test_call_headless1/1 PASS. +Логи /tmp/call-headless-fix-{build,target-build,queue,e2e}.log. +Штатный e2e использует JSON control, два настоящих ядра и TCP PCM; +он дополняет компонентную проверку очереди и остаётся однокадровым. + +Полный ./check.sh:203 passed,0 failed,0 skipped (203 total),exit0,188с. +Новая цель входит в этот запуск; proxy9/9,burst1/1 (8388608 байт), +load8/8 (134217728 байт), обе стороны rc0. Логи +/tmp/call-headless-fix-full-check.log, ...-netns.log, +tests/logs/test_call_headless_queue.log и test_call_headless.log. +Запуск в отдельном sudo unshare --net от user; IPv6/lo включены только там. +Настройки хоста all/default/lo disable_ipv6=1 сохранены и проверены. +После этих проверок менялись только документация/чек-лист. + +Исправление фрейминга закрыто. Отдельная задача по похожему шаблону +radio_headless остаётся на диагностику, runtime-дефект там не заявляется. +Аудит остальных call-контрактов/платформ остаётся частичным:call0[V]/11[~]/10[ ], +call_headless.h[~], архитектура[~],видео[ ]. Новые sanitizer/аппаратные GUI, +Android, Windows и FreeBSD проверки этим шагом не выполнялись. Push не выполнялся. diff --git a/doc/chat_call_arch.md b/doc/chat_call_arch.md index d9b2364e..097ac636 100644 --- a/doc/chat_call_arch.md +++ b/doc/chat_call_arch.md @@ -23,10 +23,12 @@ Реализация, проверенный план и границы — разделы6–8 отчёта; полный аудит call/ ещё не завершён. Проверка headless: TCP-сервис call_headless имеет собственные кодеки instance и выполняет callbacks в uasync; -он не использует DSP/jitter движка call_audio. Подтверждён дефект исходящего фрейминга при переполнении: -раздельные header/PCM могут оставить в очереди заголовок без тела. Компонентный стенд с TCP/Opus/uasync -воспроизводит сбой, обычный однокадровый e2e проходит. Реализация пока не менялась; -план атомарного добавления целого кадра — раздел11 отчёта. Проверка платформенных I/O остаётся впереди. +он не использует DSP/jitter движка call_audio. Исправлен разрыв фрейма при переполнении исходящей очереди: +header/PCM собираются до отправки, принимается или отбрасывается целый кадр, partial send сохраняет его хвост. +PCM-потери учитываются отдельно для клиента/сервиса с WARN и итогом при закрытии/destroy. +Постоянная регрессия с TCP/Opus/uasync:11/11 PASS, весь поток сравнивается с независимым decoder; +старый код выявляется на границе очереди. Штатный e2e1/1 и check203/203 PASS,proxy9/9,burst1/1,load8/8. +Реализация и границы проверки — раздел12 отчёта. Полная проверка платформенных I/O остаётся впереди. ## 1. Область diff --git a/doc/tasks.md b/doc/tasks.md index dc447b05..a8756724 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -4,17 +4,22 @@ ## Текущие задачи -[ ] **call_headless: сохранять целостность PCM-кадра при переполнении TCP-очереди** — подтверждено 26.10.2026. - ca_write_frame передаёт header/PCM двумя вызовами; ca_send_raw молча отбрасывает не поместившийся фрагмент. - При backpressure заголовок помещается, PCM нет: TCP-поток теряет границы следующих кадров. - Компонентный стенд с настоящими TCP/uasync/Opus и управляемым send(EAGAIN): control PASS, pressure ожидаемо FAIL, - allocations0->0. Очередь заполняется обычными вызовами media callback, её поля вручную не подменяются. - Штатный test_call_headless1/1 PASS передаёт один кадр и переполнение не покрывает. - План: один собранный кадр за один вызов отправки; при нехватке места drop целиком с логом/учётом. - Закрепить обычную/частичную отправку, EAGAIN, границу ёмкости и восстановление после drain постоянным regression. - Реализация не менялась; аудит остановлен перед исправлением по AGENTS.md:10. - Подробности: [call_audit_findings.txt](call_audit_findings.txt),раздел11; - /tmp/call-headless-audit-queue-{build,control,pressure}.log. Аппаратный GUI/Android не проверялись. +[+] **call_headless: целостность PCM-кадра при переполнении TCP-очереди** — исправлено 26.10.2026. + Header/PCM собираются до отправки; очередь принимает целый кадр или отбрасывает его целиком. + Частичная запись сохраняет хвост перед следующими кадрами. Потери учитываются отдельно для клиента/сервиса; + WARN содержит причину/состояние очереди, итоговые счётчики выводятся при закрытии/destroy. + Постоянный test_call_headless_queue:11/11 PASS — TCP/uasync/Opus, EAGAIN/short-write, точная ёмкость, + переполнение после частичного drain, следующий кадр, destroy с очередью и недопустимый размер/NULL. + Весь полученный поток сравнивается с независимым decoder, allocations0->0; старый код ожидаемо FAIL. + Штатный e2e1/1 PASS; native build PASS; check.sh203/203 PASS,0 failed/0 skipped, + proxy9/9,burst1/1,load8/8 (134217728 байт),exit0,188с. Полный аудит call остаётся частичным. + Подробности: [call_audit_findings.txt](call_audit_findings.txt),раздел12; + /tmp/call-headless-fix-{queue,e2e,full-check}.log. Аппаратный GUI/Android не проверялись. + +[ ] **radio_headless: проверить целостность PCM-кадра при переполнении TCP-очереди**. + При сравнении замечен сходный шаблон раздельной отправки header/PCM в rh_write_frame/rh_send_raw. + Runtime overflow отдельно не воспроизводился; нужны диагностика и компонентная проверка до решения об исправлении. + Наблюдение: [call_audit_findings.txt](call_audit_findings.txt),раздел11. Полный аудит рации не выполнен. [+] **feedback: исключение и отложенное удостоверение не блокируют запуск чата** — исправлено 26.10.2026. Исключённые/ожидающие авторы скрываются из результатов, Merkle и выдачи; записи, ver и отмены сохраняются.