Дефекты, найденные e2e-матрицей AGENT.md 11.6 (пункты 1, 2, 7, 12):
- ROLE_CREATE/ROLE_UPDATE уходили телом роли без guild_id, а клиент ждал
ссылку {guild_id, role_id} — событие отбрасывалось, роли у второго
участника не обновлялись. В rolePayload добавлено guild_id, parseRoleEvent
принимает и тело роли, и старую ссылку;
- выдача роли (MEMBER_UPDATE) и правки ролей не перечитывали список комнат,
поэтому приватная комната не появлялась в сайдбаре без F5 — теперь клиент
перечитывает комнаты по этим событиям;
- CHANNEL_CREATE/CHANNEL_UPDATE уходили телом комнаты с нулевым can_view
(права персональные), и клиент прятал комнату по этому признаку. Сервер
шлёт тело события без вычисленных прав плюс GUILD_CHANNELS_SYNC, а клиент
по событию комнаты перечитывает список;
- отзыв сессий (logout-all, смена пароля, админский сброс и «выйти везде»,
бан, удаление) не доходил до Gateway: второе устройство оставалось в
приложении. Добавлен gateway.InvalidateUser — клиент получает
INVALID_SESSION и закрывает соединение; клиент дополнительно обрабатывает
событие SESSION_INVALIDATED.
Тесты: gateway (отзыв сессий закрывает соединения и не задевает чужого),
web (событие SESSION_INVALIDATED, перечитывание комнат по CHANNEL_CREATE).
Сервер:
- store: оверрайды всех комнат сервера одним запросом (без N+1) и снятие
оверрайда с признаком «был ли он»;
- API: PUT/DELETE /guilds/{id}/channels/{cid}/overwrites/{role|user}/{tid}
(права именами через `|`, как у ролей), step-up на изменение, проверка что
роль принадлежит серверу, а участник состоит в нём;
- список комнат отдаёт permission_overwrites; после правки сбрасывается кэш
прав комнаты, пишется аудит (channel.overwrite_set/delete) и уходит событие
GUILD_CHANNELS_SYNC — видимость комнаты меняется у всех участников.
Клиент:
- редактор «Доступ к комнате»: приватность одним переключателем (запрет
VIEW_CHANNEL для @everyone), права просмотра/переписки/входа для ролей и
участников, подтверждение личности по требованию сервера;
- в настройках комнаты теперь и голосовые комнаты (фон и права), вебхуки —
только у текстовых; событие GUILD_CHANNELS_SYNC перечитывает список комнат.
Тесты: 2 Go-теста (скрытие и открытие комнаты ролями, проверка цели и прав) и
3 web-теста редактора (маски, step-up, скрытие без MANAGE_ROLES).
- миграция 00014: background_file_id у комнаты (AGENT.md 7.5, 7.7)
- API: POST/DELETE /channels/{id}/background под MANAGE_CHANNEL_BACKGROUND,
фон отдаётся в списке комнат и рассылается событием CHANNEL_UPDATE
- клиент: фон рисуется за лентой с затемнением, блок «Фон комнаты» в настройках
комнаты (загрузка, замена, удаление)
- AnimatedImage: при «уменьшить движение» показывает статичный первый кадр,
нарисованный на canvas, вместо анимации
- тесты: Go (загрузка, права, снятие) и Vitest (лента, настройки, reduced-motion)
- READY-снапшот больше не теряет banner/splash/accent: шапка сервера
показывает баннер сразу после подключения (AGENT.md 7.4)
- GUILD_UPDATE несёт всё оформление, клиент применяет его без перезагрузки
- кнопка приглашения в шапке сервера со ссылкой и копированием
- встроенные звуки интерфейса на Web Audio с разблокировкой по жесту
- object URL превью через безопасные обёртки (jsdom и урезанные webview)
Клиентская часть Фазы 3 (AGENT.md 7.13, 7.14): звуки сервера, локальные
звуки интерфейса и телеметрия медиа.
- саундборд: `api/sounds.ts` (лимиты 512 КБ и 30 на вид, multipart-загрузка,
play, `SOUND_EVENTS`), `lib/soundboard.ts` (blob-кэш `/files/{id}`,
наложение до 8 звуков, громкость = `outputVolume`, тишина при дефене),
попап в панели комнаты (`USE_SOUNDBOARD`), таймер на `rate_limited`,
подпись «включил звук» и защита от двойного воспроизведения;
- звуковая палитра: `SOUNDBOARD_PLAY` и события сервера (вход/выход
участника, упоминание, сообщение, вход/выход из голосовой, свои
микрофон/камера/экран, серверный мьют) → `playEventSound`;
- секция «Звуки» в настройках сервера (`MANAGE_SOUNDS`): два вида со
счётчиками, drag&drop, имя, эмодзи, событие палитры, переименование,
удаление и клиентские проверки файла;
- push-to-talk: режим и клавиша (`event.code`), хук в `AppLayout`, вход в
комнату с выключенным микрофоном, удержание включает публикацию, отпускание
и потеря фокуса выключают, серверный мьют приоритетнее;
- статистика: поповер с разрешением, fps, кодеком, битрейтом, RTT, потерями
и jitter (опрос раз в секунду, остановка при закрытии), выбор качества
публикации low/medium/high, пресеты шаринга экрана 1080p60…480p30;
- данные: `guilds[].sounds` из READY, `GUILD_SOUNDS_UPDATE`, селектор
`useGuildSounds`, тумблеры звуков в «Аудио-видео» (localStorage);
- тесты: `soundboard`, `soundSettings`, `pushToTalk`, `voiceStats` и
расширенный фейковый LiveKit SDK.
Установщик падал на 'TLS_ENABLED: unbound variable': расчёт LIVEKIT_PUBLIC_URL
стоял выше определения TLS_ENABLED. Значение перенесено после блока TLS,
dry-run проходит.
Аватар нельзя было показать в списке друзей: файл без комнаты был доступен
только загрузившему. Добавлено назначение файла (`files.purpose`: attachment |
avatar | banner, миграция 00006): аватары и баннеры отдаются любому
авторизованному пользователю, вложения по-прежнему проверяются по правам
комнаты, а непривязанные загрузки остаются приватными.
Полноценный текстовый чат в веб-клиенте:
- стор сообщений по комнатам (кэш 200, оптимистичная отправка с nonce,
применение всех событий Gateway, пагинация курсором, непрочитанные и
упоминания, сброс при логауте и INVALID_SESSION);
- лента: группировка подряд идущих сообщений одного автора, разделители дат,
разделитель «новые сообщения», подгрузка истории вверх, пустые состояния;
- разметка: жирный/курсив/код/блок кода/зачёркнутый/цитата/ссылки (только
http(s)) и упоминания <@id> — безопасный рендер React-узлами без
dangerouslySetInnerHTML;
- реакции чипами с подсветкой своих, быстрые эмодзи и пикер, панель действий и
контекстное меню (ответ, реакция, закрепление, правка, удаление, копирование);
- ответы с переходом к оригиналу, inline-правка, «печатает…» с POST /typing раз
в 3 с, пины в модалке, поиск по комнате с подсветкой и переходом;
- композер: автовысота, Enter/Shift+Enter, лимит 4000, drag&drop и скрепка
(POST /channels/{id}/files → attachment_ids), обработка 429 с таймером и
perm.denied с причиной, read-only без SEND_MESSAGES;
- бейджи непрочитанных и упоминаний в сайдбаре, авто-POST /ack при активном окне;
- 176 тестов Vitest (было 100), check/lint/test/build зелёные.
Клиент React 19 + TanStack Query + Zustand:
- вход (поле TOTP появляется на `auth.2fa_required`), регистрация с проверками
и учётом `registration_enabled`, онбординг первого входа с возможностью
пропустить, редирект `/` и страница `/status`;
- защита маршрутов: неавторизованных — на `/login`, без онбординга — на
`/onboarding`;
- оболочка `/app`: рейка серверов, сайдбар категорий и комнат, шапка сервера,
панель пользователя, модалки создания сервера и комнаты, экран «нет серверов»
со вступлением в главный сервер, заглушка комнаты до Фазы 2;
- настройки: профиль, безопасность (step-up, сессии, logout-all, 2FA с
локальным QR и кодами восстановления), внешний вид, админ-раздел инстанса;
- Gateway-клиент: HELLO/IDENTIFY/RESUME, heartbeat с ожиданием ACK,
экспоненциальное переподключение, разбор `INVALID_SESSION {reason,resumable}`;
- диспетчер событий: READY/RESUMED/USER_UPDATE/GUILD_UPDATE/GUILD_DELETE/
CHANNEL_* применяются к стору, GUILD_CREATE догружается по REST,
MEMBER_*/ROLE_* инвалидируют запросы участников, ролей и карточки сервера;
- i18n ru/en, 66 тестов Vitest, `check`/`lint`/`test`/`build` зелёные.
Серверные правки под клиент:
- `totp_enabled` в профиле (`GET /users/@me`, вход, регистрация) — клиенту
нужно знать, требовать ли код при step-up;
- `POST /auth/2fa/setup` отдаёт `otpauth_url` и `qr_png` (data-URI): QR-код
рисуется локально, внешние сервисы генерации QR не используются;
- права в ответах — имена (`VIEW_CHANNEL|SEND_MESSAGES`), клиент сверяет имена.