From 255853dd763bddbb9ba2b48559817b5f5be495f6 Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 22:47:06 +0200 Subject: [PATCH] Document complete poll protocol and verified recovery behavior --- doc/tasks.md | 14 +++++++++----- src/chat/merkle_sync_doc.md | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/doc/tasks.md b/doc/tasks.md index ef273047..18101458 100644 --- a/doc/tasks.md +++ b/doc/tasks.md @@ -9,10 +9,12 @@ детерминированный конфликт ver, атомарное применение страниц и восстановление индекса. Merkle v3 разделяет MEMBERS/FEEDBACK, курсор записи — 32 байта. `./check.sh`: 130 passed, 0 failed, 1 skipped; proxy/full, burst и load также PASS. - Осталось: включить производный message_id в чтение/API сообщений, определить - канонические данные набора эмодзи и ответа, формат неизменяемого poll-сообщения - (single/multiple, allow_custom_answer), проверку и подсчёт результатов при получении - исходного сообщения; подключить headless, desktop и Android интерфейсы. + Подключены message_id к БД/API, общий каталог 16 эмодзи, канонические форматы + poll/answer/reactions, single/multiple, allow_custom_answer, подсчёт результатов, + отложенное получение опроса, CLI, desktop и Android. Собраны desktop и Android APK. + Сетевой тест A ↔ B ↔ C прошёл, включая restart C и сохранение отмены. + Исправлен запуск начального member_sync после restart чата поверх уже UP-транспорта. + Осталось: финальная чистая сборка актуальных приложений и общий check.sh. План продолжения: 1. Сохранить индексируемый message_id в msg_, вернуть его через API сообщений. 2. Добавить общий каталог эмодзи и канонические кодеки poll/answer/reactions; @@ -27,7 +29,9 @@ `basic_1mb: 161949390ms` и `concurrent_20: -1612179507375916304ms`. В `tests/tcp_proxy_full/run_test.sh` длительность считается через `date +%s%3N`; проверить фактический формат времени и арифметику в окружении запуска. - Причина пока не установлена; результаты передачи и проверки байтов — PASS. + Причина установлена: date +%s%3N в окружении отдаёт 19 цифр наносекунд вместо миллисекунд. + run_test.sh переведён на Python time.monotonic_ns() // 1000000. + Осталось подтвердить длительности финальным интеграционным прогоном. [ ] **rlottie: разбор предупреждений GCC 15 в импортированных исходниках** — 05.10.2026: при сборке `tools/chatgui/rlottie_src` GCC сообщает `-Wuninitialized` в diff --git a/src/chat/merkle_sync_doc.md b/src/chat/merkle_sync_doc.md index bd287855..7408293d 100644 --- a/src/chat/merkle_sync_doc.md +++ b/src/chat/merkle_sync_doc.md @@ -126,3 +126,37 @@ Wire-запись: message_id:32, node_id:8, kind:1, ver:8, data_len:2, data, si Членство проверяется через подписи дерева приглашений и join, привязывающий Ed25519 к узлу. Слой feedback хранит подписанные байты независимо от наличия исходного сообщения; семантику выбранных пунктов и эмодзи проверяет вызывающий модуль до учёта результата. + +## Опросы и реакции + +`chat_poll_codec` задаёт общий каталог 16 эмодзи и канонические кодеки. +Реакции: count:1 и отсортированные уникальные номера; максимум 8 реакций автора. +Ответ: count:1, отсортированные уникальные номера пунктов, custom_len:2, UTF-8 текст. +Пустой набор/ответ кодируется нулевой длиной. Собственный текст — максимум 1024 байта. +Структурно неверные состояния не применяются; семантически неверные подписанные ответы +сохраняются, но исключаются из подсчёта, чтобы порядок получения опроса не менял Merkle-корень. + +Опрос — подписанное сообщение `content_type=poll`, data=`b64:` + URL-safe base64. +Wire v1: version:1=1, mode:1 (single=1/multiple=2), allow_custom_answer:1, option_count:1, +затем вопрос и варианты как length:2 + UTF-8. Числа big-endian, весь poll максимум 2048 байт. +Вопрос — максимум 512 байт, 2–32 разных непустых варианта по 256 байт. +Порядок вариантов неизменяем; номер варианта — индекс с нуля. +single разрешает один вариант либо собственный текст, multiple — варианты и необязательный текст. +Изменение ответа заменяет его целиком с ver+1; нулевой ответ отменяет голос. + +message_id сохраняется в msg_ с UNIQUE-индексом: +SHA256(`utun-chat-message-v1` || BE64(channel_id) || BE64(timestamp) || author_signature). +Этот ID одинаков на всех узлах и возвращается API сообщений и результатом публикации опроса. +Получение исходного сообщения повторно проверяет отложенные ответы. Пользовательские результаты +вычисляются по последним допустимым непустым состояниям; проценты — от числа ответивших. +При нескольких вариантах сумма процентов может превышать 100%. + +Команды `poll_create`, `vote`, `react`, `feedback`, `reaction_catalog` разбираются единым +chat_feedback_command в потоке ядра. Desktop и Android получают адресные события FEEDBACK_CHANGED +и коррелированные результаты FEEDBACK_RESULT; GUI-поток не пишет подписанные данные. +При старте чата запускается member_sync по известным онлайн-пирам, затем feedback sync: +это нужно и при restart поверх сохранившегося UP-транспорта, когда нового conn_up не будет. + +test_chat_poll проверяет пользовательский API, кодеки, ограничения, отмены и отложенный опрос. +test_chat_poll_network проверяет A ↔ B ↔ C, собственный Unicode-ответ, реакцию и восстановление C +после остановки чата без возвращения отменённых состояний.