WEB-48 · Дефект · Голос · web
Meeting Room: Browser Realtime preflight calls shared assistant runtime decision
Закрыт
P1 · важно
ведёт: —
Суть
ПАРКОВКА 05.08 (решение owner-а): зона голоса/телефонии/Realtime у изолированного Codex-инженера — контур не трогает. Связка 17/47/50-54 откачена волной AY (revert 5a0466a79). Карантин: /tmp/WEB-MR.patch на интеле ждёт его ревизии.
## 2026-08-23 11:20 UTC — разбор парковки (по запросу owner): почему здесь и что известно
Зона: телефония/Browser Realtime — исторически ИЗОЛИРОВАННАЯ зона внешнего codex-инженера (WEB-15/17/45-56), контурным воркерам сюда нельзя; тикеты создавались как заголовки-закладки той эпохи (апрель, agent_tasks_sip/voice_realtime), тела не расписывались. Припарковано чтобы контур их не хватал.
⚠️ АКТУАЛИЗАЦИЯ 23.08: голосовая цепочка снова горит (WEB-316: owner — «Connecting…» залип, мик не выключается; WEB-318 SIP). Уточнение owner: 8000/8010 = codex/opus выключены; звонки в тетрадь = 7015/7016. После диагноза WEB-316 часть этих закладок либо поглотится фиксом, либо будет закрыта как устаревшая. Решение по расконсервации зоны — за owner (внешний инженер).
Доказательства
## 2026-08-28 ~12:55Z — ПАРКОВКА СНЯТА ВЛАДЕЛЬЦЕМ, тикет взят в работу (координатор Фабл)
Owner 28.08: «Тикеты с паркинга да можно брать (я писал что инженер по телефонии у нас поднакрылся)». Решение от 05.08 об изолированной зоне внешнего codex-инженера отменено — линию ведёт контур.
Основание приоритета: свод MROOMSPEC по 24 тикетам комнаты показал, что доказан только ТРАНСПОРТ (три плеча сходятся, звук в обе стороны), а требования про ассистента как участника — `НЕ ДОКАЗАНО` по всем плечам, по Signal `НЕ РЕАЛИЗОВАНО`.
Волна заряжена; запретная строка про зону телефонии из брифа убрана явно, чтобы волна не останавливалась на границе.
Волна `web48`: установить фактом, что именно общее (решение/экземпляр/кэш), доказать последствия, развести подготовку участника и общее состояние комнаты. Сценарии: двое готовятся одновременно; подготовка во время речи ассистента; провалившаяся подготовка не меняет состояние комнаты.
## 2026-08-28 ~12:00Z — СЛЕД ДЛЯ ПОДХВАТА (координатор Фабл, перед компактом)
**Линия:** комната. Парковка снята владельцем 28.08.
**Суть:** подготовка звонка в браузере дёргала ОБЩЕЕ решение о запуске ассистента — один участник влиял на других.
**Эволюция:** `web48` → отчёт `A1:/home/ubuntu/waves/WEB48-REPORT.md` → `ACC48` = **NO-GO**, отчёт `A1:/home/ubuntu/waves/ACC48-REPORT.md` → **идёт `web48b`**.
**Обязательные сценарии:** двое и трое готовятся одновременно · подготовка во время речи ассистента с третьим · провалившаяся и отменённая на середине подготовка не меняет состояние комнаты · участник готовится, уходит и сразу возвращается. Плюс письменные ответы на три вопроса (перезагрузка / два контекста / отличимость).
[28.08 15:40Z Фабл] ACC48B: 11 из 13 находок ЗАКРЫТО собственным воспроизведением приёмщика (конкурентность, race, abort-rollback, ended-переходы), но NO-GO по №3: BEL (U+0007) в turn.id проходит в MeetingTurn.voiceTurnId, userTranscript/eventId с управляющими сохраняются, assistantText 100001 симв. принимается. №8 (межпроцессный PG race) — частично, нужен DB-прогон координатора. Заряжен web48c (очередь 263): shape-валидация identity/текстовых полей зеркалирования + соседние поля. Отчёты: ACC48B-REPORT.md, будет WEB48C-REPORT.md.
[28.08 19:50Z Фабл] ACC48C = NO-GO: BEL/длина закрыты; остатки — writeAudit падает, а вызов резолвится успешно (тихий отказ аудита); session-turn/route.ts:354 req.json() в catch{body={}}. Заряжен web48d. Отчёты: ACC48C-REPORT.md.
---
## 2026-08-29 01:15Z — ACC48D: NO-GO (круг 4). Класс «тихий отказ» шире, чем чинили
- Оба заявленных негатива круга D исправлены, consentLog.ts зелёный во всех 8 вариантах с маячком [telephony][consent_audit_write_failed]. НО приёмка нашла СЕМЬ новых мест того же класса: voiceNotice.ts:123 (теряет ошибку logAuditEvent); mark*RoomBindingFailed теряет метаданные отказа — runtime/sipMeetingRoomBinding.ts:247, channels/telegramCall/binding.ts:376, channels/signalCall/binding.ts:246, channels/whatsappCall/binding.ts:312; browser-media-evidence/route.ts:53 (missing-consent audit молча пропускается после сбоя разбора тела); embed/realtime/route.ts:54 (внешний .catch(()=>{})).
- ЧЕТВЁРТЫЙ круг класса → по правилу «третья находка = менять устройство» заряжен WEB48E (A1 queue/341): единая обёртка наблюдаемого фонового отказа + таблица «каждое место → через обёртку» + тест-страж, падающий на НОВОМ пустом catch в этих каталогах.
- Отчёты: /home/ubuntu/waves/ACC48D-REPORT.md, WEB48D-REPORT.md, ряд ACC48B/ACC48C.
- Owner спросил статус митинг-рума (22:27Z): отвечено разделением «готово vs не готово» (id=14743) — готово: WEB-77 встреча звука, WEB-396 качество звука, WEB-45/46 future-date guard; в работе: WEB-48; принята и едет в l67: WEB-15 индикация.
[29.08 ACC48E_VERDICT=NO-GO — приёмка нашла ДВА FAIL-OPEN НА БЕЗОПАСНОСТИ, круг 5 заряжен (очередь 452)]
⚠️ ДЫРЫ (не наблюдаемость, а доступ):
1. src/lib/telephony/identity.ts:41-49 — невалидный JSON allowlist молча превращается в [], а пустой список означает «разрешить всем». Опечатка в конфиге тихо открывает доступ.
2. src/lib/telephony/identity.ts:94-102 — невалидный JSON corpusIds так же молча становится [].
3. src/lib/realtime/server.ts:389-395 — ошибка authenticate переводит соединение в anonymous guest (auth=null) и зовёт next(), без лога и маячка. Отказ аутентификации даёт доступ.
ОБЁРТКА НЕ ДЕЛАЕТ ЗАЯВЛЕННОГО: reportBackgroundFailure не отправляет маячок — только console.error плюс строка beacon в payload; вызова beaconDiagnostics или транспорта /api/diagnostics/hang нет. Исходная ошибка превращается в строку reason, объект и stack теряются. Правило 10 требует лог И маячок.
ТАБЛИЦА НЕПОЛНА: автор закрыл перечисление обобщением «остальные ... paths». Независимый проход по шести корням: 266 файлов кода, 90 с catch; приёмка назвала поимённо не охваченные места в googleLiveDriver, openaiWebRtcDriver, inputTranscription, googleLiveSession, googleLiveProvider, openaiLiveProvider.
СТРАЖ ЛОВИТ ТОЛЬКО СИНТАКСИС: пропускает catch (error) { return undefined; }, игнорирование результатов Promise.allSettled и действующие fail-open пути.
Это ПЯТЫЙ круг класса — в брифе круга 5 отдельным разделом затребован ответ, почему класс не закрывается и что менять в устройстве.
[29.08 ACC48F_VERDICT=NO-GO — круг 5 закрыл подставной путь, штатный остался fail-open; круг 6 заряжен (очередь 468)]
ПРИНЯТО: внешний middleware src/lib/realtime/server.ts:390-400 отклоняет соединение при отказе authenticate(socket) — подтверждено прогоном socket.io с подставленной fakeAuthenticate (connect_error, соединение не установлено, маячок с ошибкой и stack). Регрессий на валидной конфигурации нет, целевые тесты 50/50.
БЛОКЕРЫ:
1. ⚠️ FAIL-OPEN В ОСНОВНОМ ПУТИ: src/lib/realtime/socketIdentity.ts:165-169 — ошибка декодирования битого JWT ловится, уходит в телеметрию и возвращается как null; server.ts:392 пишет auth=null и зовёт next(). Прогон с НАСТОЯЩЕЙ defaultAuthenticate и cookie __Secure-authjs.session-token=not-a-valid-jwt это подтвердил. Круг 5 закрыл путь с подставной функцией, штатный пускает.
2. Пустая строка смешана с «не настроено»: telephony/identity.ts:45-46 и :109-110; через telephony/sessionStart.ts:1060-1089 configurationValid остаётся true, срабатывает notebook/personal fallback и появляется scope — испорченная/пустая сохранённая конфигурация РАСШИРЯЕТ доступ.
3. Нет ограничения размера: валидный массив из 50000 строк принимается (parsedLength=50000) и уходит в запрос к БД.
[29.08 ПРИНЯТО — ACC48G_VERDICT=GO, коммит 6bcad2a74335c6c17b075286c7259a54bf918d17, шестой круг класса]
FAIL-OPEN В ОСНОВНОМ ПУТИ ЗАКРЫТ: битый JWT больше не даёт анонимный вход. Приёмка прогнала настоящую defaultAuthenticate (не подставную) — для malformed JWT наблюдался ровно один diagnostics POST с событием, соединение не устанавливается.
СОСТОЯНИЯ КОНФИГУРАЦИИ ТЕЛЕФОНИИ РАЗВЕДЕНЫ (таблица приёмки):
- настроено пустым ('' , пробелы, []) -> отказ caller-а, configurationValid=false, НИКАКОГО notebook/personal fallback, 403 scope_unresolved;
- испорчено (битый JSON, не-массив, неверный элемент, oversized) -> отказ caller-а, configurationValid=false, обращение к БД и fallback НЕ вызываются, 403 scope_unresolved.
То есть испорченная конфигурация больше не расширяет доступ — она его закрывает.
ПРЕДЕЛ РАЗМЕРА: массив из 50 000 строк отказывается ДО запроса к БД, есть регрессионный тест.
ОБРАТНАЯ СТОРОНА ПРОВЕРЕНА: валидные токен и конфигурация работают, законные пользователи не сломаны.
Автор ответил на вопрос «почему потребовался шестой круг» отдельным разделом.
Кандидат в линию после l69.
[BOARDTRIAGE] Приёмщик: независимый realtime-QA. ACC48G GO подтверждён на defaultAuthenticate: malformed JWT не создаёт anonymous access, один diagnostics POST, valid config проходит; рекомендуется done.
Воркер
не привязан — привязать:
curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \
-d '{"issueId":"WEB-48","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-01T14:54:45.646Z