524 lines
48 KiB
Plaintext
524 lines
48 KiB
Plaintext
Аудит документации src/stream — 26.10.2026, заголовки завершены
|
||
|
||
Текущий статус: deleted исправлен и покрыт штатным test_stream_member; сборка,
|
||
профиль7/7 и check.sh195/195 PASS относятся к внедрению этого исправления (раздел 5).
|
||
При продолжении аудита подтверждена потеря помещающегося Opus в publisher_block (раздел 6).
|
||
Исправление внедрено: сборка и штатный test_stream_publisher23/23 PASS; после
|
||
объединения origin/master профиль8/8 и check.sh200/200 PASS (раздел 7).
|
||
Остальные частичные/непроверенные контракты каталога не закрыты. Разделы 1–4
|
||
описывают первоначальную диагностику и проверку архитектуры до внедрения deleted.
|
||
В продолжении полностью прочитаны stream.c и stream_optimizer.c, уточнены их
|
||
заголовки; теперь6[V]/12[~]/0[ ]. В частично прочитанном network подтверждён
|
||
дефект истечения INFO_REQ (раздел8). Исправление и штатные проверки выполнены:
|
||
TTL4/4, обе реальные backpressure-сцены и check201/201 в отдельном IPv6 namespace PASS (раздел9).
|
||
Текущий итог продолжения:18[V]/0[~]/0[ ] для заголовков src/stream; полностью
|
||
прочитаны все16 реализаций. Раздел10 описывает финальную сверку контрактов.
|
||
doc/stream.txt остаётся[~] из-за неполной проверки платформ desktop/Android
|
||
и внешнего многопроцессного стенда. Старые показатели выше и в разделах1–9 — история отдельных этапов.
|
||
|
||
1. Подтверждённый дефект: deleted не учитывается stream_member_load
|
||
|
||
src/stream/stream_member.c:166–167 выбирает строку только по node_id. Условие
|
||
source=0 проверяется ниже, но столбец deleted не выбирается и не проверяется.
|
||
Проверка дерева приглашений и подписи профиля не заменяет проверку блока владельца:
|
||
после принятого deleted=yes эти подписи участника остаются корректными.
|
||
Поэтому loader возвращает STREAM_OK и публикует прежние stream-опции/ключ.
|
||
|
||
Это расходится с допуском CHAT (source=0, deleted=0), закреплённым в topo_group.h,
|
||
и с результатом topo_node_sqlite_member_allowed. stream_member_supernode уже
|
||
отбирает deleted=0. Существующий тест deleted проверяет только supernode lookup.
|
||
Обработчик member_changed в stream.c:539–546 использует stream_member_load:
|
||
данный результат не запускает ветку инвалидирования STREAM_CLOSE_MEMBER.
|
||
Для исходной реализации полный сетевой сценарий продолжения выдачи после удаления не проверялся;
|
||
обход сетевой авторизации этим воспроизведением не заявляется.
|
||
|
||
Изолированная проверка
|
||
|
||
Настоящее ядро с SQLite :memory:, отдельные ключи канала и тестового участника.
|
||
Join/update/tree подписи создаются штатными функциями и принимаются member_sync_put.
|
||
В профиле корректные stream_source/subscriptions. Участник отличается от локального
|
||
узла; никаких удалённых узлов или пользовательской БД проверка не затрагивает.
|
||
Затем через member_sync_put применяется подписанный ключом владельца блок
|
||
{"ver":"1","deleted":"yes"}; прямой UPDATE deleted для воспроизведения не используется.
|
||
|
||
Фактический лог:
|
||
before load=0 allowed=1; after load=0 allowed=0 published=1; expected load=-9
|
||
|
||
Тест завершился exit=1 на несовпадении с ожидаемым STREAM_ERR_NOT_MEMBER.
|
||
Стенд: /tmp/stream-audit-member-deleted.c.
|
||
Сборка через существующие Makefile/LIBUTUN/COMMON_LIBS и дополнительную цель:
|
||
make -C tests -f Makefile -f /tmp/stream-audit-member.mk /tmp/stream-audit-member-deleted
|
||
Логи: /tmp/stream-audit-member-build.log, /tmp/stream-audit-member-deleted.log.
|
||
Runtime выполнен вне sandbox из-за ограничений сокетов uasync.
|
||
|
||
Предлагаемый вариант исправления
|
||
|
||
Добавить deleted в список полей этого же SELECT и до проверки профиля вернуть
|
||
STREAM_ERR_NOT_MEMBER для удалённого участника, с отдельным логом причины и ID.
|
||
Вторая SQL-проверка и новый API не требуются. Вариант WHERE deleted=0 тоже
|
||
отсечёт запись, но существующий лог "member absent" не отличит удаление от отсутствия.
|
||
Сохранить различие отсутствующего/удалённого участника и неполного профиля (WAIT),
|
||
атомарность state/public_key при отказе.
|
||
Регрессия должна проверить подписанное удаление источника/локального участника/
|
||
поставщика, закрытие соответствующих handles и отсутствие выдачи новых DATA.
|
||
После исправления продолжить сверку оставшихся заголовков и сетевых контрактов.
|
||
Реализация не исправлялась: аудит остановлен по правилу AGENTS.md:10.
|
||
|
||
2. Выполненная сверка
|
||
|
||
Все 18 заголовков src/stream прочитаны предварительно. Полностью прочитаны
|
||
10 реализаций: stream_block/source/buffer/media/media_buffer/wire/member/metrics/
|
||
load/playback_clock. Другие реализации stream, включая playback_audio.cpp,
|
||
network/optimizer/publisher/viewer и stream.c целиком, ещё не проверены.
|
||
В stream.c просмотрена только затронутая ветка member_changed; полный аудит
|
||
жизненного цикла сервиса этим не заявляется.
|
||
|
||
Завершены и готовы к [V]: stream_proto.h, stream_block.h, stream_source.h,
|
||
stream_media.h. Исправлены описание полного размера части и области подписи,
|
||
предусловия BE helpers, Ed25519 seed/ключ, владение и ошибки codec/source,
|
||
нулевой write/accepted, отсутствие END в source_finish, ограничения uAV2,
|
||
пересчёт first_frame_offset и атомарность views/выходов при ошибке.
|
||
В этих четырёх заголовках изменены комментарии/оформление, C-токены сохранены.
|
||
|
||
Частично проверены [~]: stream_buffer.h, stream_load.h, stream_media_buffer.h,
|
||
stream_member.h, stream_metrics.h, stream_playback_clock.h, stream_wire.h.
|
||
Их реализации прочитаны, но оставшиеся уточнения контрактов не внесены после
|
||
остановки. В stream_member.h добавлено предупреждение о подтверждённом дефекте.
|
||
|
||
Остальные семь заголовков остаются [ ]: stream.h, stream_network.h,
|
||
stream_optimizer.h, stream_playback_audio.h, stream_priv.h, stream_publisher.h,
|
||
stream_viewer.h. Прочтение одного заголовка без реализации не закрывает аудит.
|
||
doc/stream.txt остаётся [~]: просмотрены начальные разделы о FEC, подписи,
|
||
кольце и выдачах; последующие сетевые/media/desktop/Android разделы не сверены.
|
||
Добавлен статус аудита.
|
||
Полные .h/doc контракты всего модуля ещё не закрыты.
|
||
|
||
3. Проверки и ограничения
|
||
|
||
Сборка диагностического стенда PASS; отрицательная проверка deleted воспроизвела дефект.
|
||
Профильные наборы stream, полный check.sh, санитайзеры, Windows/FreeBSD и
|
||
GUI/Android runtime не запускались. Неизменность C-токенов заголовков и
|
||
git diff --check — PASS; успешные сетевые/медиа тесты не заявляются.
|
||
Предыдущие изменения Local_transport и чужие записи чек-листа сохранены.
|
||
|
||
4. Архитектурная проверка предлагаемого исправления — 26.10.2026
|
||
|
||
Вывод: проверка deleted в stream_member_load архитектурно корректна. На этом
|
||
этапе проверка выполнялась на временной копии модуля без изменений .c проекта;
|
||
внедрение описано в разделе 5. Статусы полного аудита каталога этим не закрываются.
|
||
|
||
Граница ответственности
|
||
|
||
Блок A участника описывает личность/stream-опции. Блок B владельца определяет
|
||
в том числе исключение из канала. Корректный A не отменяет запрет из B.
|
||
member_apply_record проверяет подпись B и его версию; owner_put обновляет
|
||
adm_tags/adm_tags_sig/storage/deleted одним UPDATE. После успешного commit
|
||
member_committed посылает props-событие; Merkle-page уведомляет после commit
|
||
всей страницы. Поэтому достаточно прочитать принятую проекцию deleted в том же
|
||
SELECT, не вводя вторую криптопроверку блока B или отдельный SQL-запрос допуска.
|
||
|
||
STREAM_ERR_NOT_MEMBER уже обработан в stream_open_check, каталоге источников,
|
||
member_changed/service_reconcile, authorized и network_membership_changed.
|
||
Событие источника/self немедленно ставит invalidated, затем reconcile закрывает
|
||
сессию. session_find/handle_active/session_deliver учитывают invalidated до
|
||
отложенного освобождения. Закрытие сохраняет владение handles у caller до close.
|
||
Для исключённого поставщика закрываются его flow, а локальная сессия источника
|
||
может продолжать получать от другого поставщика. Сфера запрета — конкретный
|
||
канал; общий ETCP/NCD и владельцы других каналов не должны закрываться.
|
||
|
||
Результаты реального стенда
|
||
|
||
В /tmp/stream-arch-member.c добавлены только поле deleted в существующий SELECT
|
||
и ветка NOT_MEMBER с причиной в логе до разбора профиля. Копия связана перед
|
||
штатной libutun.a, остальные модули взяты из существующей сборки.
|
||
Стенд использует fixture test_stream_service: четыре живых ядра, два канала,
|
||
подписанные owner/member записи, настоящий Merkle, UDP/TCP и NCD.
|
||
|
||
Все четыре группы проверок PASS:
|
||
- Неполный профиль/placeholder остаётся WAIT; отсутствие и deleted дают
|
||
NOT_MEMBER; state/public_key не меняются на отказе. Неверная подпись владельца
|
||
отвергается, старая версия не снимает запрет, более свежая разрешает участие.
|
||
- Исключение источника сразу закрывает receive с MEMBER и инвалидирует reader;
|
||
новых DATA callback нет. Запрет через Merkle останавливает сам publisher.
|
||
Handle другого канала и транспорт с дополнительным NCD-владельцем сохраняются.
|
||
- Исключение поставщика закрывает только его локальную выдачу; reader источника,
|
||
другой канал и upstream другого ядра в этот момент остаются активными.
|
||
- Исключение self закрывает handles данного канала и запрещает новые publish/
|
||
subscribe. Другой канал остаётся активен. После разрешения новый open работает,
|
||
прежние отсоединённые handles не оживают. Новая публикация получает новую эпоху.
|
||
|
||
Итоговый exit=0, teardown flows=0 и ноль оставшихся tracked allocations.
|
||
Сборка: /tmp/stream-arch.mk, /tmp/stream-arch-build-final.log.
|
||
Кейсы: /tmp/stream-arch-cases.h; harness: /tmp/stream-arch-service.c.
|
||
Результат: /tmp/stream-arch-results-final.log.
|
||
Запуск: /tmp/stream-arch-service --architecture-only, вне sandbox.
|
||
|
||
Первый прогон /tmp/stream-arch-results.log неверно пытался повторно использовать
|
||
publisher после распространённого через Merkle запрета: старый publisher уже
|
||
правильно закрыт. Ожидание стенда исправлено на новый запуск после разрешения,
|
||
сама временная проверка deleted при повторе не менялась.
|
||
|
||
Гарантии и ограничения
|
||
|
||
Уведомления/освобождение остаются отложенными; нужен обычный uasync loop.
|
||
Проверено отсутствие новых DATA callback у исключённого reader. Уже принятые
|
||
ETCP пакеты не отзываются, удалённые узлы узнают запрет при его доставке через
|
||
Merkle. Архитектурная проверка не обещает мгновенного глобального отзыва данных.
|
||
Полный check.sh, санитайзеры и платформенные/GUI runtime не запускались.
|
||
На этапе архитектурной проверки C-код репозитория не менялся; последующее
|
||
внедрение и постоянная регрессия описаны ниже.
|
||
|
||
5. Внедрение согласованного исправления — 26.10.2026
|
||
|
||
В stream_member_load существующий SELECT дополнен deleted. Для удалённой записи
|
||
до crypto возвращается STREAM_ERR_NOT_MEMBER, с отдельным DEBUG_WARN о запрете
|
||
владельца, group_id/member_id. Другого SQL-запроса, нового API или формата нет.
|
||
Остальные результаты loader, включая WAIT, и атомарность выходов сохранены.
|
||
Предупреждение о неисправленном дефекте в stream_member.h заменено действующим
|
||
контрактом. Задача deleted в doc/tasks.md закрыта, текущий статус stream.txt уточнён.
|
||
|
||
Постоянная регрессия — test_stream_member в check_PROGRAMS. Тест использует
|
||
test_stream_service.c с теми же четырьмя ядрами/подписанными записями/сетью,
|
||
а STREAM_MEMBER_TEST_ONLY выбирает изолированный сценарий в отдельном процессе.
|
||
Fixture не дублируется, её изменённые member-версии/активность не влияют на
|
||
обычный test_stream_service. Сценарий доступен также через --member-only.
|
||
|
||
Проверены подписи/версии владельца, WAIT/NOT_MEMBER и сохранность выходов,
|
||
удаление источника/поставщика/self, причины закрытия, отсутствие новых DATA
|
||
callback после запрета, распространение через Merkle до остановки publisher,
|
||
сохранность чужого NCD-владения и работоспособность другого канала после
|
||
исключения self. Разрешение не оживляет старые handles; новая публикация
|
||
проверяется на новую эпоху. Ожидание удалённой остановки зависит от состояния,
|
||
а не фиксированного sleep; teardown подтверждает отсутствие tracked allocations.
|
||
|
||
./build.sh --no-gui -j4 PASS: /tmp/stream-member-fix-build.log.
|
||
Профиль7/7 PASS: stream_member, stream_service, stream_core, stream_media,
|
||
stream_playback, stream_tx_stats, android_stream.
|
||
Сводка /tmp/stream-member-fix-profile-results.log,
|
||
подробности /tmp/stream-member-fix-test_*.log.
|
||
Последующий перенос длинной строки лога не меняет содержимое сообщения;
|
||
обе test-stream цели пересобраны, test_stream_member повторно PASS:
|
||
/tmp/stream-member-fix-test-rebuild.log, /tmp/stream-member-fix-test_stream_member-final.log.
|
||
|
||
Полный Linux check.sh:195/195 PASS,0 failed/0 skipped; proxy9/9,burst PASS,
|
||
load8/8,134217728 байт,167 секунд. Лог /tmp/stream-member-fix-full-check.log.
|
||
git diff --check PASS. Sanitizers и нативные Windows/FreeBSD/GUI/Android runtime
|
||
в этом этапе не запускались; test_android_stream — тест общего C bridge на Linux.
|
||
Отметки полного аудита заголовков не расширены: устранение одного дефекта
|
||
не завершает оставшуюся сверку src/stream и всего doc/stream.txt.
|
||
|
||
6. Продолжение аудита: помещающийся Opus не упаковывается — 26.10.2026
|
||
|
||
Полностью прочитаны stream_playback_audio.cpp, stream_viewer.c, stream_publisher.c
|
||
и их заголовки. Всего прочитаны13 реализаций; полные stream.c/network/optimizer
|
||
пока не сверены. До исправления документов трёх новых модулей подтверждён дефект,
|
||
поэтому publisher.h/viewer.h/playback_audio.h отмечены[~], не[V].
|
||
Теперь4 заголовка[V],10[~],4[ ]; doc/stream.txt[~] сохраняется.
|
||
|
||
Причина
|
||
|
||
В publisher_block (stream_publisher.c:145) перед каждым Opus проверяется
|
||
used + STREAM_MEDIA_AUDIO_SIZE + packet_length + STREAM_MEDIA_VIDEO_SIZE >= capacity.
|
||
Резерв video descriptor добавляется всегда, включая полностью пустую video-очередь.
|
||
В результате допустимая audio-only запись, которая целиком помещается в uAV2,
|
||
не попадает в block. При отсутствии видео publisher_block возвращает WAIT,
|
||
actor повторяет попытки и после MEDIA_AGE_US=250000 удаляет принятый пакет.
|
||
Штатный лог говорит audio expired, не объясняя блокировку из-за лишнего резерва.
|
||
|
||
Фактическое воспроизведение
|
||
|
||
Один настоящий локальный publisher, отдельный автоматически созданный канал,
|
||
SQLite в памяти, stream_subblocks=1/stream_rs_redundant=0. Ёмкость содержимого
|
||
FEC-пачки1125 байт. Видео в очереди нет. Codec Opus48kHz stereo/20мс:
|
||
штатный opus_codec_encode плюс opus_packet_pad; оба тестовых пакета дают
|
||
opus_packet_get_nb_samples=960 и действительно декодируются в960 frames/channel.
|
||
Самостоятельный stream_media_encode успешно помещает оба блока в ту же ёмкость.
|
||
После publisher feed принимаются настоящие RS-пачки, проверяется подпись,
|
||
содержимое декодируется штатным stream_media_parse в DATA callback.
|
||
|
||
packet=1044 valid_samples=960 media_bytes=1092 capacity=1125 feed=1 delivered=1 queued=0 state=LIVE
|
||
packet=1045 valid_samples=960 media_bytes=1093 capacity=1125 feed=1 delivered=0 queued=0 state=LIVE
|
||
В логе между этими строками: publisher audio expired pts=1020000.
|
||
Результат: expected=2 received=1, exit=1; после destroy нет оставшихся tracked allocations.
|
||
Для каждой проверки отсчитано500мс: больше TTL и периода обновления TX snapshot.
|
||
Это не переполнение TX, не испорченный Opus и не нехватка места в полной пачке.
|
||
|
||
Стенд: /tmp/stream-audit-audio-pack.c, дополнительная цель /tmp/stream-audit-audio-pack.mk.
|
||
Сборка через штатные Makefile/LIBUTUN/COMMON_LIBS/test_clock.o PASS:
|
||
/tmp/stream-audit-audio-pack-build.log. Полный лог /tmp/stream-audit-audio-pack.log.
|
||
Первый диагностический запуск остановился на неверной константе capacity1133
|
||
в стенде; она заменена вычислением границы из фактической ёмкости1125.
|
||
Реализация проекта при диагностике не менялась.
|
||
|
||
Предлагаемый вариант исправления
|
||
|
||
Упаковка должна учитывать фактически добавляемые описания/данные и допускать
|
||
audio-only пачку, если целый Opus в неё помещается. Резерв под видео не должен
|
||
навсегда блокировать принятую audio-запись. Простой вариант — заполнять audio
|
||
до его реальной ёмкости, затем видео в оставшееся место; нехватка места под видео
|
||
означает следующую пачку, а не потерю audio. Wire и неделимость Opus сохраняются.
|
||
Если важно гарантировать часть видео в каждой смешанной пачке, нужен явный
|
||
переход к отдельной audio-only пачке для такой головы очереди; это более сложный вариант.
|
||
|
||
Проверить1044/1045 байтов, максимальное audio-only содержимое1077 для FEC1+0,
|
||
точное заполнение capacity, очередь видео/пустую очередь и прежний default8+0.
|
||
Отдельно различать запись, которая вообще не помещается в доступную FEC-пачку,
|
||
и запись, не поместившуюся только в текущий остаток: диагностика должна показывать
|
||
packet_length, остаток/полную ёмкость и причину, не маскируя всё как expiry.
|
||
Реализация не исправлялась; дальнейший аудит остановлен по AGENTS.md:10.
|
||
В publisher.h добавлено предупреждение, задача открыта в doc/tasks.md.
|
||
Новый полный build/check, sanitizers и Qt/Android runtime в этом продолжении
|
||
не запускались; прежний check195/195 из раздела5 новый дефект не покрывает.
|
||
|
||
Оставшиеся уточнения новых заголовков (не выполнены после остановки):
|
||
- playback_audio: create/destroy lifetime, producer/consumer дисциплина всех getters,
|
||
потребление через needs_input, feed0/1/-1/-2, PLC до100мс, prepare после finish,
|
||
EOF в empty означает извлечённый PCM, а не проигранный аппаратурой звук.
|
||
- viewer: единый callback manager, notify без reentry, асинхронный stop до освобождения ID,
|
||
пределы demand/video<=records/audio_ahead, нулевой снимок status до открытия,
|
||
уничтожение manager требует остановки callers и живого instance.
|
||
- publisher: чтение настроек FEC при actor-open, snapshot смешивает локальные очереди
|
||
текущего источника и общие ETCP-счётчики ядра, PTS/revision/KEY admission,
|
||
ошибки/состояния и условное сохранение кольца локальным readers после FIN.
|
||
|
||
7. Исправление упаковки и продвижения треков — 26.10.2026
|
||
|
||
Принят локальный вариант без изменения wire, публичного API и неделимости Opus.
|
||
Без ожидающего видео рассчитывается только фактическая стоимость audio: header32,
|
||
descriptor16, payload. Условие допуска допускает равенство полной ёмкости.
|
||
Для обычной смешанной пачки сохранён резерв descriptor32 и хотя бы байта видео.
|
||
Если первая audio-запись помещается только отдельно, публикуется audio-only пачка.
|
||
Локальный video_turn отдаёт второй слот того же actor видео; coalescing20ms в этом
|
||
слоте не применяется. Между actor-вызовами состояние не добавлено; лимит2 пачки,
|
||
mutex, порядок PTS и смещения фрагментов сохраняются. Публикуемые записи расходуются
|
||
только после успешных encode/publish; очередь при ошибке закрывается прежним путём.
|
||
|
||
Opus длиннее capacity-header32-descriptor16 actor удаляет с WARN, содержащим ID,
|
||
PTS, bytes, max и capacity. Это окончательный отказ конкретной записи, не WAIT
|
||
до истечения TTL. Следующая запись продолжает обрабатываться. Проверка находится
|
||
в actor: во время OPENING producer ещё не знает выбранную FEC-ёмкость.
|
||
feed=1 означает копирование в ограниченную очередь; асинхронный отказ и TTL
|
||
оговорены в publisher.h и stream.txt. Удаление ошибочной записи не меняет video KEY.
|
||
DEBUG показывает успешную отдельную аудиопачку и отложенное видео, TRACE — состав
|
||
каждой опубликованной пачки; WARN сообщает точную причину невозможности упаковки.
|
||
|
||
Практический масштаб
|
||
|
||
Воспроизведение использует валидный Opus, искусственно увеличенный opus_packet_pad.
|
||
Обычный desktop/Android encoder настроен AUDIO128kbit/s/20ms; средний бюджет320 байт
|
||
не является пределом VBR. Потеря обычного естественно закодированного звука не
|
||
подтверждена. При default8+0 capacity9833 одиночный Opus<=1275 помещается.
|
||
Ёмкость1125 относится ко всем конфигурациям с n-r=1 при штатном RS-size, включая2/1.
|
||
|
||
Проверки
|
||
|
||
Временная копия и реальный publisher/RS/uAV2:21/21 PASS, без tracked leaks;
|
||
/tmp/stream-plan-check.log. Старый бинарник проваливает границу1045:
|
||
/tmp/stream-plan-baseline.log. Исходники проекта при этой проверке плана не менялись.
|
||
|
||
Штатный tests/test_stream_publisher.c и цель tests/Makefile.am:23/23 PASS.
|
||
Границы1044/1045/1077/1078/1275, точная пачка из двух audio-записей, раздельные
|
||
пачки, крупная audio-очередь32 записи, unfit в начале/середине/подряд, video-only,
|
||
mixed и продолжения видео, OPENING, Stop/FIN, n/r1/0,2/1,2/0,8/0, лимит8 Opus.
|
||
Callback проверяет фактические PTS/длины, декодирует Opus960 frames/channel,
|
||
проверяет каждый байт/offset видео. Все ожидаемые данные получены до TTL250ms.
|
||
На Linux wrap uasync_set_timeout запрещает publisher_media timer после крупного
|
||
Opus и короткого P-кадра: оба должны публиковаться в том же actor. Проверка не
|
||
сравнивает задержки процессора с порогом5ms. Cleanup каждого ядра восстанавливает
|
||
tracked allocation count. Другие платформы сохраняют проверки содержимого/порядка.
|
||
|
||
./build.sh --no-gui -j4 PASS: /tmp/stream-publisher-fix-build.log.
|
||
./tests/test_stream_publisher PASS: /tmp/stream-publisher-fix-regression.log.
|
||
Перед итоговой проверкой origin/master8e746195 объединён без конфликтов в ced9dc23.
|
||
Повторная ./build.sh --no-gui -j4 PASS: /tmp/stream-publisher-fix-merged-build.log.
|
||
Профиль8/8 PASS: stream_publisher,core,media,playback,tx_stats,android_stream,service,member;
|
||
/tmp/stream-publisher-fix-profile-results.log и /tmp/stream-publisher-fix-test_*.log.
|
||
Полный ./check.sh PASS:200 passed,0 failed,0 skipped; новый publisher-тест764ms.
|
||
Proxy9/9,burst PASS,load8/8,134217728 байт; полное время175с.
|
||
Лог /tmp/stream-publisher-fix-full-check.log. git diff --check PASS.
|
||
Sanitizers, Qt GUI и Android на устройстве в этом исправлении не запускались.
|
||
Отметки аудита4[V]/10[~]/4[ ] не расширяются исправлением кода.
|
||
|
||
8. Аудит stream.c/optimizer и просроченный INFO_REQ — 26.10.2026
|
||
|
||
Полностью прочитаны stream.c1249 строк и stream_optimizer.c712 строк; теперь
|
||
15 из16 реализаций прочитаны полностью. В stream_network.c прочитаны строки1–1615;
|
||
оставшаяся часть, перегрузка/relay и общая сверка network ещё не завершены.
|
||
Уточнены stream.h и stream_optimizer.h: общая сессия/позиция raw событий,
|
||
независимость media cursors, detached ownership, FIN, режим manual/automatic,
|
||
условия ACTIVE и mobile-supernode, параметры/оценки/сроки и recovery selector.
|
||
Публичные структуры переформатированы с сохранением полей/порядка/типов.
|
||
stream.h и optimizer.h отмечены[V]; network.h и priv.h остаются частичными[~].
|
||
Итог6[V]/12[~]/0[ ]; doc/stream.txt[~]. Все остальные отметки сохранены.
|
||
|
||
Подтверждённое расхождение
|
||
|
||
prepare_requests формирует uint8_t probe[9]: probe_id8 + media_flag1.
|
||
Raw dispatch тоже требует INFO_REQ length9. В expire_controls (stream_network.c:1147)
|
||
для поиска связанного flow ошибочно оставлена проверка m.length != 8.
|
||
Поэтому любой штатный INFO_REQ, просроченный в управляющей FIFO, удаляется из
|
||
очереди после4000мс, но соответствующий flow не закрывается с OVERLOAD.
|
||
Его info_sent остаётся1, info_ready=0, state=INFO. Повтор INFO не ставится.
|
||
По network_tick он ждёт общий deadline5000мс и получает TRANSPORT; selector
|
||
обрабатывает такой результат иначе, включая peer backoff. Полный цикл
|
||
NCD/ETCP/selector и величина последующего backoff в этом воспроизведении не измерялись.
|
||
|
||
Диагностика
|
||
|
||
Стенд /tmp/stream-audit-info-expiry.c включает настоящую реализацию network,
|
||
использует uasync/FIFO, control_put, stream_wire_parse и expire_controls.
|
||
Это изолированная проверка очереди/flow без запуска сетевых подключений:
|
||
время передаётся непосредственно как queued_ms + TTL, не моделируется потеря реального ETCP.
|
||
До/на TTL4000мс все записи сохраняются; после4001мс:
|
||
INFO_REQ9 media0: queue0/stateINFO/reason0, ожидалось CLOSED/OVERLOAD — FAIL.
|
||
INFO_REQ9 media1: queue0/stateINFO/reason0, ожидалось CLOSED/OVERLOAD — FAIL.
|
||
INFO_REQ9 с прежним probe_id: queue0/stateINFO — PASS, новую пробу закрывать нельзя.
|
||
SUBSCRIBE8: queue0/stateCLOSED/reasonOVERLOAD — PASS.
|
||
Итог2 failed из4, exit1, после cleanup tracked allocations восстановлены.
|
||
|
||
Сборка через штатный Makefile/LIBUTUN/COMMON_LIBS и дополнение
|
||
/tmp/stream-audit-info-expiry.mk PASS; лог /tmp/stream-audit-info-expiry-build.log.
|
||
Runtime: /tmp/stream-audit-info-expiry.log. Включены stream=debug, video=warn,
|
||
debug=info; видно удаление control и отсутствие закрытия INFO-flow.
|
||
Временная копия /tmp/stream-network-info-expiry-checked.c отличается только
|
||
сверкой length9 вместо8. На ней тот же стенд4/4 PASS, exit0, tracked allocations
|
||
восстановлены: /tmp/stream-audit-info-expiry-checked-build.log и ...-checked.log.
|
||
C/C++ реализации проекта не менялись; исправлены только описания.
|
||
|
||
Предлагаемое исправление
|
||
|
||
Сверять полный INFO_REQ9 (ID8 + media flag1), оставив проверку конкретного probe_id.
|
||
Просроченная проба должна закрываться существующим flow_close(OVERLOAD,notify0);
|
||
новые состояния, протокол или дополнительный таймер не нужны. Старый probe_id
|
||
не должен закрывать новую попытку. Закрепить обе media-формы, SUBSCRIBE8 и границы
|
||
TTL отдельным штатным regression; дополнительно удержать настоящую ETCP-очередь
|
||
дольше4с и проверить CLOSED/OVERLOAD до общего connect deadline5с.
|
||
|
||
Аудит остановлен по AGENTS.md:10 до изменения реализации. Задача добавлена
|
||
в doc/tasks.md; текущий статус отражён в stream.txt и внешнем чек-листе.
|
||
Полная сборка/check и sanitizers в этом продолжении не запускались; прежний
|
||
check200/200 относится к исправлению publisher и новый дефект не покрывает.
|
||
|
||
9. Исправление INFO expiry и проверка ETCP backpressure — 26.10.2026
|
||
|
||
expire_controls теперь сопоставляет полный INFO_REQ9 (ID8 + media1) с текущей
|
||
пробой. Проверка probe_id сохранена; истёкший запрос закрывает flow через прежний
|
||
flow_close(OVERLOAD,notify0). Wire, состояния и таймеры не менялись.
|
||
Существующий WARN дополнен bytes/age_ms/flow_closed: видно удаление конкретного
|
||
control и наличие связанного закрытия. Код исправления c1483512, тесты b116b20b.
|
||
Во время работы история перенесена поверх GUI-изменений; отсутствие различий
|
||
в src/lib/tests относительно собранного d89c1efa проверено отдельно.
|
||
|
||
Штатный test_stream_control_expiry включён в общий check и напрямую использует
|
||
реальные control_put/wire parser/expire_controls. Проверены границы3999/4000/4001мс:
|
||
INFO9 media0/1 после TTL закрывают flow с OVERLOAD, несовпадающий probe_id
|
||
не закрывает текущую попытку, SUBSCRIBE8 сохраняет прежнюю обработку.
|
||
4/4 PASS; отслеживаемые allocations после cleanup восстановлены.
|
||
|
||
В test_stream_service добавлена проверка на четырёх настоящих CHAT-ядрах с Merkle,
|
||
NCD и живым ETCP. Она выполняется в общем наборе и отдельно через --control-expiry-only.
|
||
После прогрева NCD/INFO callback настоящей send_input_q временно снимается.
|
||
Первый INFO остаётся в ETCP, второй — в control FIFO. Через4200мс в обоих media-режимах:
|
||
expired_sub0 state=CLOSED reason=OVERLOAD; held_sub1 state=INFO; publication/subscription
|
||
активны, исходное ETCP-соединение initialized/links_up. После возврата callback
|
||
приходит INFO_RESP; просроченная проба переоткрывается и получает новый ответ через
|
||
то же ETCP-соединение. Cleanup всех четырёх ядер восстанавливает tracked allocations.
|
||
В общем check этот же сценарий также прошёл внутри полного test_stream_service.
|
||
|
||
Проверки и условия среды
|
||
|
||
./build.sh --no-gui -j4 PASS: /tmp/stream-info-expiry-fix-build.log.
|
||
test_stream_control_expiry4/4 PASS: /tmp/stream-info-expiry-fix-unit.log.
|
||
test_stream_service --control-expiry-only PASS: /tmp/stream-info-expiry-fix-integration.log.
|
||
В изолированном сетевом прогоне включён stream=debug; unit включает video=warn,
|
||
поэтому проверен также новый WARN с bytes9/flow_closed1 и несовпадением ID/flow_closed0.
|
||
В fixture остаются логи отрицательных reachability-проб TCP; потоковые asserts,
|
||
готовность транспорта, восстановление и счётчики ресурсов проверены отдельно.
|
||
|
||
Первый ./check.sh:198 passed,3 failed,0 skipped. Все три отказа вызваны bind(::1):
|
||
EADDRNOTAVAIL в test_ncd_faults, test_ipv6_sockets, test_route_connectivity.
|
||
IPv6 в основном namespace отключён (all/default/lo=1), ::1 отсутствует,
|
||
bind(::) работает. Сводка /tmp/stream-info-expiry-fix-full-check.log;
|
||
состояние среды /tmp/stream-info-expiry-host-ipv6.log. Код из-за этих отказов не менялся.
|
||
|
||
Создан отдельный временный network namespace; IPv6 включён только в нём,
|
||
поднят lo/::1, сами проверки выполнялись от user. Все три прежних отказа PASS,
|
||
затем ./check.sh:201 passed,0 failed,0 skipped. Новый unit32мс, service80173мс.
|
||
Proxy9/9,burst PASS,load8/8,134217728 байт; полное время175с.
|
||
Логи /tmp/stream-info-expiry-fix-ipv6-env.log,
|
||
/tmp/stream-info-expiry-fix-ipv6-full-check.log и /tmp/stream-info-expiry-ipv6-test_*.log.
|
||
Основной namespace сохранён: all/default/lo=1 и bind(::1) по-прежнему EADDRNOTAVAIL.
|
||
Необходимость явной IPv6-диагностики runner записана отдельной задачей в doc/tasks.md.
|
||
|
||
git diff --check PASS. Sanitizers, Qt GUI и Android на устройстве в этом исправлении
|
||
не запускались. Устранение дефекта не расширяет аудит:6[V]/12[~]/0[ ],doc/stream.txt[~].
|
||
|
||
10. Завершение сверки заголовков общего stream-ядра — 26.10.2026
|
||
|
||
Дочитан stream_network.c1616–2185: relay lifecycle, dispatch, общий tick,
|
||
перегрузка нескольких пиров, учёт кольца, позиции получателей и телеметрия.
|
||
Вместе с предыдущими этапами полностью прочитаны все15 C-реализаций и
|
||
stream_playback_audio.cpp (всего7775 строк), все18 заголовков.
|
||
Для уточнения контрактов повторно прочитаны полные buffer/media_buffer,
|
||
member/wire, load/metrics, playback_clock/audio, publisher/viewer.
|
||
|
||
Уточнения заголовков
|
||
|
||
- network/priv: принадлежность caller-owned handles после detach, manual/internal
|
||
ограничения, INFO nonce/повторы/сроки, аренда6с и RENEW2с, control TTL>4с,
|
||
пределы общих бюджетов, готовая INFO-проба без автоматического close,
|
||
первый DATA как структурная часть до проверки подписи, диапазоны/RTT/validity,
|
||
restart с сохранением consumer floor, relay READY/BOUND и сроки помощи.
|
||
- load/metrics: A/D/X/Q, атомарность add/remove, revision и окна роста/rates,
|
||
live-счётчики против последнего снимка rates, rebase и 16-битный timestamp,
|
||
первая пара для rate, строгий порог остановки, END и старение истории.
|
||
- buffer/media_buffer: размер слотов против фактических wire-байтов,
|
||
заимствованные данные/observer, поиск частей против проверки подписи,
|
||
неизменяемый набор k после decode, неподписанные timing/telemetry,
|
||
независимые позиции треков, generation/GAP/revision, status до KEY,
|
||
bounded MORE/WAIT, audio lookahead и продолжение последнего кадра после finish.
|
||
- member/wire: parse/format против доверенного load, BGP отдельно от членства,
|
||
собственный online, роль deleted=0, переключатель audio против codec эпохи,
|
||
конкретные размеры/предусловия writers/readers, путь, телеметрия и подпись END.
|
||
- playback_clock/audio: состояние регулятора/история и часы, допустимые входы,
|
||
producer/consumer и внешняя блокировка, 20-мс stereo output, PLC/разрыв,
|
||
needs_input/prepare/finish, hardware frame origin, empty до опустошения устройства.
|
||
- publisher/viewer: асинхронное открытие/stop, время занятия session ID,
|
||
KEY/revision/admission/TTL, feed как принятие очередью, mute без смены codec,
|
||
общие ETCP-счётчики instance, notify под lock/исключительный destroy,
|
||
владение батчем, диапазоны demand и нулевые снимки до обновления actor.
|
||
|
||
Последние изменения этих12 заголовков — комментарии/оформление. Сравнение
|
||
лексических C-токенов с сохранённой до правок базой проверяет имена, типы,
|
||
порядок полей, объявления и выражения; все12 совпали. Публичный API/ABI,
|
||
состояния, протокол и C/C++ логика в данном продолжении не менялись.
|
||
Новых подтверждённых дефектов реализации в этой части аудита не найдено.
|
||
|
||
В stream.txt согласованы native-разделы: текущая версия wire7, INFO_REQ9,
|
||
сроки и закрытие control, совместимость через NCD без собственного
|
||
route_connectivity probe-движка, структурная часть против подписанной пачки,
|
||
60-секундные rates, recovery без сброса media и отсутствие пропуска по одному
|
||
возрасту DATA. Уточнено, что relay может готовить media-индекс по want_media.
|
||
Старые результаты wire6/utun2 помечены историческими.
|
||
|
||
Покрытие:18[V]/0[~]/0[ ] в src/stream. doc/stream.txt[~]: ещё нужны
|
||
полная сверка desktop/Android capture/decoder/audio/UI и внешнего
|
||
tests/stream_netns, их аппаратные и старые ASAN-результаты здесь не перепроверены.
|
||
Завершение заголовков не объявляет эти внешние компоненты проверенными.
|
||
|
||
Проверка ./build.sh --no-gui -j4 PASS: /tmp/stream-header-audit-build.log.
|
||
Полный ./check.sh PASS:201 passed,0 failed,0 skipped; proxy9/9,burst PASS,
|
||
load8/8,134217728 байт. Полное время172с. Все9 профильных core/media/playback/
|
||
tx_stats/Android bridge/service/member/publisher/control_expiry наборов PASS.
|
||
Лог /tmp/stream-header-audit-check.log; условия /tmp/stream-header-audit-netns.log.
|
||
Прогон выполнен от user в отдельном network namespace с IPv6/lo/::1;
|
||
основной namespace сохранён (all/default/lo.disable_ipv6=1).
|
||
Неизменность C-токенов12/12, отсутствие правок C/C++ реализаций/lib/tests
|
||
относительно5259c746, строки<=150, diff --check и отметки чек-листа PASS:
|
||
/tmp/stream-header-audit-contract-check.log. После прогона менялись лишь
|
||
два комментария (Ed25519 public key и ненулевая база expected interval);
|
||
лексические токены проверены повторно, новый прогон не требовался.
|
||
Sanitizers, отдельные Qt/C++ audio tests, desktop GUI и Android на устройстве
|
||
в этом продолжении не запускались. Новых runtime-проверок многопроцессного
|
||
tests/stream_netns не было; тестовый namespace здесь служил только средой check.sh.
|