WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-326 · Задача · — · web

Движок Gate F: production collaboration — многопользовательский realtime-синк (NO-GO, финальная цель)

Закрыт P2 ведёт: —
Суть
## Карта: ДВЕ лестницы движка (зафиксировано 23.08 после вопроса owner)
ЛЕСТНИЦА ГЕЙТОВ DocumentSession (доверие движку): Gate A контракт ✅ (document-session-contract-v1) → Gate B библиотека ✅ (F01-F06, 311+135 тестов, 8 раундов V4→V4Q) → Gate C legacy shadow ✅ (ЗАКРЫТ 23.08: пакет R22G-F4, вердикт аудита GO — браузерные пруфы selection/clipboard/edit-mirror, ON/OFF артефакты, WEB-089 CLOSED-VERIFIED) → Gate D controlled writer (NO-GO, ждёт интеграции+одобрения) → Gate E mutating AI (NO-GO) → Gate F production collaboration (NO-GO).
ПРОДУКТОВАЯ лестница этапов редактора (другая!): этап A P0-фиксы ✅ (в проде) → этап B docsession-веер фич (WEB-283) → этап C CodeMirror-гибрид для ТЯЖЁЛЫХ ФАЙЛОВ (WEB-086) → этап D спайк ProseMirror.
ВАЖНО: движок-библиотека живёт в ОТДЕЛЬНОМ дереве (wq/o3/docsession-*) и НЕ является предком прод-линии (WEB-304) — т.е. Gates B/C приняты аудитором, но этот код НЕ в serving. Первый шаг к проду = WEB-323 (интеграция ship-dark).

Ради этого строился детерминированный sync (монотонные sequence, one-in-flight, catch-up/resync, дожат в V4O→V4Q). После Gate E.

## ДЛЯ НУЛЕВОГО АГЕНТА (обновлено 2026-09-05 UTC)
- **Суть одной строкой:** Движок Gate F: production collaboration — многопользовательский realtime-синк (NO-GO, финальная цель)
- **Текущее состояние:** статус: done; ЛЕСТНИЦА ГЕЙТОВ DocumentSession (доверие движку): Gate A контракт ✅ (document-session-contract-v1) → Gate B библиотека ✅ (F01-F06, 311+135 тестов, 8 раундов V4→V4Q) → Gate C legacy shadow ✅ (ЗАКРЫТ 23.08: пакет R22G-F4, вердикт аудита GO — браузерные пруфы selection/clipboard/edit-mirror, ON/OFF … | ПРОДУКТОВАЯ лестница этапов редактора (другая!): этап A P0-фиксы ✅ (в проде) → этап B docsession-веер фич (WEB-283) → этап C CodeMirror-гибрид для ТЯЖЁЛЫХ ФАЙЛОВ (WEB-086) → этап D спайк ProseMirror. | ВАЖНО: движок-библиотека живёт в ОТДЕЛЬНОМ дереве (wq/o3/docsession-) и НЕ является предком прод-линии (WEB-304) — т.е. Gates B/C приняты аудитором, но этот код НЕ в serving. Первый шаг к проду = WEB-323 (интеграция ship-dark). | [2026-09-05 17:40Z] Ревизия доски (волна 2040 boardtriage, M1, luna): статус → done. Основание: есть подтверждение посадки в l71. Если работа жива — верни статус и напиши в тикет, какая волна её ведёт.
- **Кто работал:**
- 2026-09-05T17:35:28.710Z — fable-coordinator — [2026-09-05 17:40Z] Ревизия доски (волна 2040 boardtriage, M1, luna): статус → done. Основание: есть подтверждение посадки в l71. Если работа жива — верни статус и напиши в тикет, какая волна её ведёт.
- **Ветки/бандлы/отчёты:** нет данных в тикете
- **KNOWN ISSUES / ТРАБЛШУТИНГ:**
- Карта: ДВЕ лестницы движка (зафиксировано 23.08 после вопроса owner)
- ЛЕСТНИЦА ГЕЙТОВ DocumentSession (доверие движку): Gate A контракт ✅ (document-session-contract-v1) → Gate B библиотека ✅ (F01-F06, 311+135 тестов, 8 раундов V4→V4Q) → Gate C legacy shadow ✅ (ЗАКРЫТ 23.08: пакет R22G-F4, вердикт аудита GO — браузерные пруфы selection/clipboard/edit-mirror, ON/OFF …
- ПРОДУКТОВАЯ лестница этапов редактора (другая!): этап A P0-фиксы ✅ (в проде) → этап B docsession-веер фич (WEB-283) → этап C CodeMirror-гибрид для ТЯЖЁЛЫХ ФАЙЛОВ (WEB-086) → этап D спайк ProseMirror.
- **Эволюция:**
- 2026-09-05 → [2026-09-05 17:40Z] Ревизия доски (волна 2040 boardtriage, M1, luna): статус → done. Основание: есть подтверждение посадки в l71. Если работа жива — верни статус и напиши в тикет, какая волна её ведёт.
- Карта: ДВЕ лестницы движка (зафиксировано 23.08 после вопроса owner)
- ЛЕСТНИЦА ГЕЙТОВ DocumentSession (доверие движку): Gate A контракт ✅ (document-session-contract-v1) → Gate B библиотека ✅ (F01-F06, 311+135 тестов, 8 раундов V4→V4Q) → Gate C legacy shadow ✅ (ЗАКРЫТ 23.08: пакет R22G-F4, вердикт аудита GO — браузерные пруфы selection/clipboard/edit-mirror, ON/OFF …
- [2026-09-05 17:40Z] Ревизия доски (волна 2040 boardtriage, M1, luna): статус → done. Основание: есть подтверждение посадки в l71. Если работа жива — верни статус и напиши в тикет, какая волна её ведёт.
- **Следующий шаг:** Карта: ДВЕ лестницы движка (зафиксировано 23.08 после вопроса owner)
Доказательства

[2026-08-25 ~13:00] Gate F (collaboration/multi-user realtime) — финальная цель лестницы. Зависит от writer'а Gate D (есть, посадка 49) как общего пути записи. Можно СТРОИТЬ параллельно с Gate E (E=AI-мутации, F=многопользовательский merge — архитектурно разные оси поверх одного controlled writer). ВКЛЮЧЕНИЕ F — строго последним по лестнице C→D→E→F, NO-GO до закрытия E. Пуск волны — после/параллельно E при наличии слота. Родня: [[WEB-325]] Gate E, [[WEB-324]] Gate D, [[WEB-283]] эпик.

[2026-08-25 ~13:20] Gate F СТРОЙКА ЗАПУЩЕНА (каркас+скаут, owner-approved параллельно с E): волна на A1 (Luna xhigh), worktree wt-gate-f (ветка gate-f от 70ab8c1). Флаг NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB тёмный. Фаза 1 = дизайн-док GATE-F-DESIGN.md (как многопользовательский merge ляжет на controlled writer Gate D, CRDT vs OT, точка синка, телеметрия конвергенции) + каркас интерфейсов CollabSession + локальный двухклиентный тест сходимости БЕЗ сети. Боевой сетевой синк = фаза 2 после Gate E. Только tsc+node --test. Маркер GATE_F_DONE/BLOCKED. Owner (сайт sixbyy.com): «после репетиции откроем» — превью до T-0 не поднимаем.

[2026-08-25 ~13:35] GATE_F_DONE (W1 каркас+скаут, commit d1b71160, ветка gate-f). Дизайн GATE-F-DESIGN.md: решение OT-vs-CRDT, модель операций/конфликтов/порядка, точка синка, граница controlled-writer (Gate D), телеметрия конвергенции, исключения фазы 2. Код src/lib/document-session/collab.ts: флаг NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB fail-closed, типы CollabSession/операций/телеметрии, упорядоченный dispatch-seam, fail-closed транспорт-заглушка, детерминированный двухклиентный in-memory харнесс сходимости. Тесты gate-f-collab.test.ts 4/4 (флаг, заглушка, сходимость двух клиентов, инертность при OFF). ПРУФ целостности: замороженная 311-сюита 311/311 rc=0, tsc scoped rc=0, git diff --check rc=0, контракт V2.1.2 не тронут. Gate F остаётся NO-GO: боевой сетевой realtime-синк = фаза 2 после Gate E; флаг тёмный, в сборку не включён. Родня: [[WEB-325]] Gate E (фаза 2 F зависит от E), [[WEB-324]] Gate D.

[2026-08-25 ~16:50] Gate F (collaboration каркас) В ПРОДЕ ТЁМНЫМ — посадка 50. Флаг NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB OFF. Боевой сетевой синк = фаза 2 после включения E. Родня [[WEB-325]] [[WEB-324]].

[28.08 ~14:05Z Фабл] Gate E принята (ACCGATEE9 GO 28.08) → Gate F становится следующей ступенью. Свод сделанного:
- W1 каркас+скаут сдан 25.08 (wt-gate-f, commit d1b71160b6): GATE-F-DESIGN.md (OT-vs-CRDT, модель операций/конфликтов, controlled-writer граница из Gate D), collab.ts (флаг NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB fail-closed, двухклиентский harness), 4 W1-теста; замороженный корпус V2.1.2 не тронут. Отчёт: A1:/home/ubuntu/waves/GATE-F-REPORT.md.
- Решение владельца (голос 12:42 28.08): этап F посвящён мультиюзерам — подтверждено.
- Порядок: посадка E (выключенным) + визуальная приёмка → W2 Gate F (сетевой realtime-транспорт поверх W1).
- WEB-418 заведён по ошибке как дубль этого тикета и закрыт (урок ticket-search-by-epic повторился: поиск доски по «gate»/«мульти» WEB-326 не находил).

[28.08 18:05Z Фабл] Запущена волна W2 — **gatef2** (очередь 267): реальный сетевой транспорт по дизайну W1 поверх realtime-канала продукта; identity encoding из gatee9 (столкновения ключей уже закрыты — не изобретать свой); сходимость на реальном транспорте (конкурентные правки, старая эпоха, потеря/повтор, reconnect); обе болезни базы в брифе; флаг остаётся выключен. Отчёт будет: A1:/home/ubuntu/waves/GATEF2-REPORT.md.

[28.08 20:15Z Фабл] **gatef2 (W2) СДАН** — сетевой транспорт совместной работы. Заряжена приёмка accgatef2 (очередь 272): сходимость на реальном транспорте (конкурентные правки, старая эпоха, потеря/повтор, reconnect), identity из gatee9, корпус V2.1.2 нетронут, COLLAB выключен. Отчёты: GATEF2-REPORT.md, будет ACCGATEF2-REPORT.md.

[28.08 19:45Z Фабл] ЭВОЛЮЦИЯ W2: gatef2 (сетевой транспорт по GATE-F-DESIGN, identity encoding из gatee9) → ACCGATEF2 NO-GO (тихий catch; semantic-invalid wire-поля принимались) → gatef3 → ACCGATEF3 NO-GO → gatef4 → ACCGATEF4 NO-GO (N8-12: ложный synced обходит bounded recovery; socket emit → unhandled rejection; async join) → gatef5 ИДЁТ. W1 фундамент: wt-gate-f commit d1b71160b6, GATE-F-DESIGN.md. Отчёты: A1:GATEF*-REPORT.md, ACCGATEF*-REPORT.md. Флаг COLLAB выключен всюду.

[28.08 GATEF5 СДАН — этап F волна 2, приёмка ACCGATEF5 идёт]
Автор gatef5 (дерево /home/ubuntu/waves/wt-gatef2, отчёт /home/ubuntu/waves/GATEF5-REPORT.md): замечания N-8…N-12 закрыты; гейт-корпус 37/37 повторён трижды; независимые проверки 16/16; tsc, Prisma validate, git diff --check — PASS; next build не запускался, COLLAB выключен, прод не трогался.
Приёмка ACCGATEF5 (A1, очередь 282): дерево несёт состояние автора патчем от wt-gatef2 на базе l64. Требования: каждое замечание воспроизвести негативно самому + обязательный негативный тест (намеренно сломать свойство — гейт обязан покраснеть), три вопроса, проверка что при выключенном флаге collab фича действительно мертва, свой скан тихих catch. Отдельная строка вердикта: готов ли этап F к посадке (l65/l66) и что останется незакрытым.

[28.08 ~19:00Z ACCGATEF5: NO-GO — два новых блокирующих класса]
Отчёт: /home/ubuntu/waves/ACCGATEF5-REPORT.md.
ПОДТВЕРЖДЕНО приёмщиком с mutation-контролем: N-8…N-12 закрыты; гейт-корпус 37/37 трижды, в том числе в обратном порядке и с мусором в /tmp; независимый realtime-корпус 16/16; durable restart и конкурентные process contexts проверены на локальной PostgreSQL.
БЛОКЕР Б-1 (класс «флаг выключен ≠ фича мертва», он же «починка слоя ≠ починка фичи»): при выключенном NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB серверный collab-endpoint ВСЁ РАВНО отвечает synced на raw join. Выключение гасит только клиент.
БЛОКЕР Б-2 (класс «мусор читается как валидное»): JSON с дублирующимся ключом принимается после JSON.parse и попадает в durable journal (JSON.parse молча берёт последнее значение).
Круг 3 запущен: GATEF6 (очередь 288, база — ПОСАЖЕННАЯ l65): серверный источник истины для флага в одной точке границы (не NEXT_PUBLIC как единственный источник — он инлайнится в сборку), общий строгий разбор входа журнала с отказом на дублях/non-finite/NUL/невидимых, негативный тест на обе защиты, регресс 37/37 и 16/16.

[28.08 ~20:04Z ACCGATEF6_VERDICT=NO-GO — этап F, круг 4]
Отчёт: /home/ubuntu/waves/ACCGATEF6-REPORT.md. Проверялся коммит автора gatef6 ea0127d8 (база — линия l65 f87f61a7). Приёмщик перевёл своё дерево fast-forward прямо на коммит автора и об этом честно написал; авторское дерево не редактировалось; обе защиты ослаблялись только в приёмочном дереве и восстанавливались.
Найдено при закрытии двух прошлых классов: глубокий materialized JSONB-объект вызывает голый RangeError БЕЗ записи о повреждении в лог (то есть отказ не наблюдаем — это класс тихого отказа); production multi-process fan-out и document source подтвердить невозможно без прода.
Требуется следующий круг: превратить голый RangeError в наблюдаемый fail-closed отказ с записью, и отдельно решить, что делать с неподтверждаемой без прода частью (проверять после посадки на живом, как теперь требует правило проб платных путей).

[28.08 ~20:30Z СОСТОЯНИЕ ДВИЖКА ПЕРЕД КОМПАКТОМ]
СТУПЕНЬ E: посажена тёмной в l64 (сейчас на проде), но ★ ПОСМОТРЕТЬ ЕЁ ГЛАЗАМИ НА ПРОДЕ НЕВОЗМОЖНО. Визуальная приёмка EVISUAL2 разобрала код: в src/lib/document-session/mutating-ai.ts (L4-6, L14-24, L84-93) build-флаг — ПЕРВЫЙ гейт, а localStorage-ключ document-session-mutating-ai-kill-switch — АВАРИЙНЫЙ ВЫКЛЮЧАТЕЛЬ: значения 0/false/off гасят E, но 1 НЕ включает при выключенном build-флаге. Моё прежнее утверждение владельцу («посмотришь через браузерный оверрайд») ОПРОВЕРГНУТО. Чтобы владелец увидел ступень — нужна отдельная сборка с включённым build-флагом либо включение по конкретному пользователю. ВОПРОС ВЛАДЕЛЬЦУ ЗАДАН, ответа пока нет — это открытое решение для следующего агента.
СТУПЕНЬ F: круг 2 (gatef5) дал 37/37 трижды и независимые 16/16, но приёмка ACCGATEF5 нашла два новых класса: (Б-1) при выключенном NEXT_PUBLIC_DOCUMENT_SESSION_COLLAB серверный collab-endpoint всё равно отвечал synced на raw join — «флаг выключен ≠ фича мертва»; (Б-2) JSON с дублирующимся ключом принимался после JSON.parse и попадал в durable journal — «мусор читается как валидное».
Круг 3 (gatef6, коммит ea0127d8) оба класса закрыл, но приёмка ACCGATEF6 дала NO-GO по новой находке: глубокий materialized JSONB-объект вызывает голый RangeError БЕЗ записи о повреждении в лог — то есть отказ не наблюдаем (класс тихого отказа). Плюс production multi-process Socket.IO fan-out и document source без прода подтвердить нельзя.
ЧТО ДАЛЬШЕ: круг 4 — превратить голый RangeError в наблюдаемый fail-closed отказ с записью; неподтверждаемую без прода часть проверять ПОСЛЕ посадки на живом (по новому правилу проб платных путей). Заряжается следующим тиком.

---
## 2026-08-29 00:20Z — ACCGATEF9: GO (круг 6) → review
Эволюция линии движка F (для нулевого агента): голый RangeError на глубоком materialized JSONB без лога (ACC-круги 1-4, NO-GO) → F7 «bound and observe» 5556cc7a: порог+маяк, но приёмка ACCGATEF8 нашла НОВЫЕ неограниченные рекурсии в том же collab/journal пути (пятая находка класса → смена устройства) → GATEF8 c1276742: ВСЕ JSON boundary и generic-обходы сведены в единую serializeCollabJson, порог из одного источника; отказы typed, с reason и ТОЧНЫМ path до узла-нарушителя + контекст колонки/документа/строки в durable journal.
Приёмка: /home/ubuntu/waves/ACCGATEF9-REPORT.md (свой пересчёт всех точек рекурсии грепом, пробы граница/граница+1/гигант/цикл, негатив на 5556cc7a падает). Принятый коммит: c1276742 (wt-gatef8, база l65+5556cc7a). Кандидат l67.
Связанные доки: хендофф §4 (ДВИЖОК F) в OPS-STATUS-LIVE.md; ряд отчётов ACCGATEF2..F9 на A1 /home/ubuntu/waves/.

---
## 2026-08-29 04:15Z — доливается в l67 отдельной волной
Принятый коммит c1276742 (ACCGATEF9 GO) не попал в первый заход слияния: волна остановилась раньше, на грузе WEB-415AA (смысловой конфликт контрактов). Груз движка F от того конфликта НЕ зависит — запущена волна L67MERGE2, доливающая c1276742 и 12704970 (индикация) поверх текущей ветки l67. После этого l67 = гейт project_insights + Совет + ступень E + индикация + движок F.

[29.08 ЖИВАЯ ПРОВЕРКА GATE F НА ПРОДЕ l68 — НЕ РАБОТАЕТ, доказано руками и сверкой с БД]
Метод: сессия A — Chrome, сессия A2 — НЕЗАВИСИМЫЙ браузер со своим профилем и куками (sessionId разные: cmte2ldd3000gh8d2m9mstvna и cmte4ef0q0119rti7uwjp2gbp), обе под обычной не-админской учёткой. Вторая учётка qa-gatef-b@wool2.online заведена через обычную форму регистрации по неиспользованному инвайту (регистрация закрыта INVITE_GATE_ENABLED=true).
1. ОДИН ДОКУМЕНТ В ДВУХ СЕССИЯХ НЕВОЗМОЖЕН: пока документ открыт в одной сессии, клик по нему в другой МОЛЧА ничего не делает — ни модалки, ни тоста, ни объяснения. Воспроизведено и между вкладками Chrome, и между Chrome и полностью независимым браузером (значит запрет не клиентский). Обратный ход: закрыл редактор в первой — вторая открыла сразу (OPEN_ATTEMPT editorPresent=0 -> 1). Захват ЛИПКИЙ: после закрытия модалки документ остаётся занятым, освобождается только при закрытии вкладки/браузера.
2. ⚠️ P0 ПРАВКИ НЕ СОХРАНЯЮТСЯ ВООБЩЕ. Сверка с БД прода (только чтение): Source web421-src.txt len=176 updatedAt=08:36:17, маркеров MARKER-88 и MARKER-77 нет (pos=0); Document len=176 updatedAt=08:23:12; Source web326-big.txt len=400081 updatedAt=08:48:04, MARKER-99 нет. То есть запись в строку ПРОИСХОДИТ (updatedAt двигается), а текст НЕ пишется. Индикатор сохранения при этом отрабатывает полный цикл галочка->спиннер->галочка. Заряжена волна EDITLOST (очередь 456).
3. Таблицы движка совместной работы ПУСТЫЕ: DocumentCollabStream = 0 строк, DocumentCollabOperation = 0 строк.
4. МНОГОАККАУНТНОСТЬ: в интерфейсе нет способа дать второму аккаунту доступ к документу. Единственный шаринг — гостевая ссылка (Access -> Create link, 24ч), но по ней открывается «Untitled Notebook — guest access. Pick a channel to open a private chat» с кнопками мессенджеров; доступа к документу нет. Значок «Only you» не кликабелен, панели участников нет.
5. БОЛЬШОЙ ДОКУМЕНТ 400081 байт: загрузился без ошибок, открылся ~10 сек, честная плашка про урезанные возможности («Live text index, instant save commit, the full revision timeline and whole text visualization are limited so the editor stays responsive»), бейдж «совместная работа отключена» + «Only you». При дописывании ОДНОГО слова бейдж размера скакнул 509K -> 814K — редактор раздувает простой текст в HTML примерно на +60%.
6. Серверный лог: Sync error: Error: aborted { code: 'ECONNRESET' } на /api/sync. Клиент: [auth] isolation-timeout-release, isolation-session-settled.
НЕ ПРОВЕРЕНО (физически невозможно, пока не открываются две сессии): одновременная правка в разных местах, одновременная правка одного места, скорость доезда правки.

[29.08 ПОПРАВКА ВЛАДЕЛЬЦА — мой вывод про шаринг был НЕВЕРЕН]
Владелец (голосом 09:08Z): «Ты писал, что во время совместной работы нечем дать доступ второму пользователю к документу. На самом деле есть, у нас это спрятано в главном меню, там, где аватарка, там, где шестерёнка с настройками. Там есть пункт, где мы можем поделиться».
То есть строка предыдущей записи «в интерфейсе нет способа дать второму аккаунту доступ к документу» — ОШИБКА проверяющего: он искал через карточку документа и Access -> Create link, а нужный пункт лежит в ГЛАВНОМ МЕНЮ под аватаркой / шестерёнкой настроек. Кто будет перепроверять — идти туда, не тратить время на карточку документа.
Остальные факты той проверки в силе: два независимых браузера не открывают один документ одновременно; правки не сохраняются в БД вообще (P0, волна EDITLOST); DocumentCollabStream и DocumentCollabOperation пусты.
Порядок работ: сначала EDITLOST (без сохранения правок совместную работу доказывать нечем), затем повторная проверка на двух учётках через путь из главного меню.

[29.08 P0 ПОТЕРЯ ПРАВОК — КОРЕНЬ НАЙДЕН, коммит 4780fdf4, приёмка ACCEDITLOST заряжена (очередь 465)]
Два независимых пути потери:
1. Редактор считал успехом прохождение ЛОКАЛЬНОГО safety-gate, а не результат записи в /api/sync. Ошибки HTTP и транспорта внутри syncNotebook ПОГЛОЩАЛИСЬ — отсюда зелёная галочка при неудачной записи.
2. Поздняя auth-hydration могла заменить более новый локальный Source старым ответом сервера.
Объяснение наблюдения с updatedAt: клиентская правка сначала меняет Source.updatedAt и запускает sync; успешный stale-sync мог пересоздать строку с ПРЕЖНИМ content и НОВЫМ updatedAt, неуспешный — оставить набранное только в памяти. Document — отдельное хранилище: autosave обновляет Source, а Document и индекс обновляет отдельный updateDocument, поэтому старый Document.updatedAt цепочку не опровергает.
Gate D (gated-writer) корнем НЕ является — это отдельный фон.
ЧЕСТНАЯ ОГОВОРКА АВТОРА: тело живых запросов по read-only наблюдению получить нельзя, поэтому он не утверждает, что payload был пустым; доказаны две вещи — старое приложение могло показать успех без записи в облако, и поздний ответ мог заменить новый текст старым.

[29.08 ACCEDITLOST_VERDICT=NO-GO — круг 2 заряжен (очередь 469)]
ПРИНЯТО: основной путь стал ЖДАТЬ результат cloud-sync; для настоящих HTTP/транспортных отказов проводка верна (таймаут -> ok:false reason:transport; 401 -> ok:false reason:http status:401; текст сохранён; шапка показывает предупреждение и «Save failed — retry», 6/6).
БЛОКЕРЫ:
1. ⚠️ HTTP 200 С ТЕЛОМ-ОШИБКОЙ ДАЁТ ЗЕЛЁНУЮ ГАЛОЧКУ: {success:false,error:"write rejected"} при статусе 200 -> {ok:true,mode:"cloud"}. src/store/useStore.ts:4884 проверяет только res.ok, а :4952-4962 объявляет committed любой 2xx. Правило 10 нарушено.
2. Две вкладки одного пользователя молча затирают правку друг друга.
3. ⚠️ Source и Document/DocumentChunk РАСХОДЯТСЯ: на autosave /api/sync пишет Source/Notebook и НЕ вызывает documentStore/vectorStore, updateDocument там не зовётся. Source.content новый, Document.content и DocumentChunk.content старые. Полнотекстовый поиск по Source новое видит через триггер, а СЕМАНТИЧЕСКИЙ поиск и индекс, читающие DocumentChunk, — нет. Это прямо объясняет, почему найденное в документе может не находиться поиском.
Оговорка приёмки: полного круга до БД не делала (локально нет DATABASE_URL, прод запрещён) — расхождение доказано статическим графом вызовов.

[29.08 ⭐ P0 ЗАКРЫТ — ACCEDITLOST3_VERDICT=GO, коммит e2fd313cbb0d5b2c7d261d2bb18e178fce219231, круг 3]
Живой P0-круг закрыт полностью: текст доезжает при сохранении И возвращается после остановки процесса, запуска нового процесса и нового входа пользователя.
Доказано прогоном: до рестарта пользователь editlost3-user-20260829114913 сохранил три ноутбука; после рестарта GET /api/sync?mode=full вернул оба текста в правильные ноутбуки, пустой ноутбук отдал sources=[]; GET /api/sync и GET /api/sync?mode=incremental дали тот же полный readback. Новый тест «EDITLOST3 — full sync source readback» 2/2.
ПУТЬ ЛИНИИ (три круга): круг 1 нашёл два независимых пути потери — редактор считал успехом прохождение локального гейта, а не запись в облако (ошибки транспорта поглощались), плюс поздняя auth-hydration затирала свежий текст старым ответом. Круг 2 добавил ожидание результата и согласовал Source/Document/DocumentChunk, но readback после рестарта возвращал sources=[]. Круг 3 закрыл.
Кандидат в линию после l70.

[2026-08-29 14:40Z] РАЗБОР GATEFVERIFY (независимая волна, отчёт /home/ubuntu/waves/GATEFVERIFY-REPORT.md, база 3ec0f19d): ступень F на проде НЕ реализована. Причина пустых DocumentCollabStream/DocumentCollabOperation — не сбой БД, а ожидаемый факт: редактор к operation-based пути realtime:collab:* НЕ подключён. Реальный редактор ходит только по старому пути realtime:doc:change, который пересылает ЦЕЛЫЙ текст и заменяет room.documents последним снимком (src/lib/realtime/server.ts:1161-1177) — то есть OT, разрешения конфликтов и журнала в PostgreSQL в продуктовом пути нет; при одновременной правке одного места одна из правок может потеряться. OT и типизированные конфликты существуют только в серверном каркасе (:856-872) и его loopback-тестах. Документного захвата в коде нет вовсе (ни navigator.locks, ни BroadcastChannel, ни таблицы захватов) — значит утренний симптом «второй сеанс молча не открыл документ» объясняется не sticky-захватом; вероятный кандидат — предел editor sockets. Отдельно: для документов больше 500 000 символов collaborativeContentSyncEnabled по умолчанию false (src/lib/editor/largeDocument.ts:25-36,148-195), EditorModal подавляет отправку и приём текста, оставляя только presence и курсоры — то есть совместная работа на больших документах выключена по устройству. GATE-F-DESIGN.md:1-5,104-107,124-136 сам называет W1 каркасом и ставит NO-GO. Вывод для доски: F закрывать нельзя, живая проба на малом документе покажет presence и обмен снимками — это НЕ доказательство F.


[BOARDTRIAGE] Приёмщик: независимый collaboration-QA. Текущий verdict NO-GO: realtime:collab:* не подключён, DB streams/operations пусты, full-text path теряет concurrent edit risk; нужен новый live acceptance по полному критерию.

[29.08 эволюция линии, для нулевого агента]
GATEFVERIFY (статический разбор l70): серверный op-based каркас ЕСТЬ (realtime:collab:*, OT, журнал DocumentCollabStream/Operation в PG, loopback-тесты), но EditorModal шлёт целый текст по-старому — журнал вечно пуст, F недоказуема. → GATEF1 (70da57d7, wave/gatef1): контент-коммиты редактора конвертируются в каноническую replace-text операцию через realtime:collab:*, сервер OT — авторитет, за build/env-флагом (default OFF), ACL через существующий gateway. → ACCGATEF1 в полёте (сходимость двух вкладок, рестарт посреди сессии, IME/undo, серверный ACL).
Отчёты (A1): /home/ubuntu/waves/{GATEFVERIFY,GATEF1}-REPORT.md. Смежное: кнопка «Поделиться» существует в меню тетради (вывод «кнопки нет» был ошибкой тестера); вход из панели редактора — WEB-437.

[29.08 19:05Z] ACCGATEF1 NO-GO: многострочная правка A→A\nB отвергается validateCollabOperationEnvelope как invalid-operation (конверсия и валидация разошлись контрактом). → GATEF2 (queue/656, матрица входов: многострочные/IME/суррогаты/большие вставки, тесты приёмщика 99d3b144 as-is).

[29.08 19:35Z] GATEF2 сдан (7071269a): валидатор принимает полный диапазон правок. → ACCGATEF2 (queue/666, своя матрица: IME/суррогаты/100КБ/undo-redo).

[29.08 20:00Z] ACCGATEF2 GO (4ce33ac2): конверсия принята (матрица IME/суррогаты/100КБ/undo-redo). Проводка F (GATEF1 70da57d7 + GATEF2 7071269a) принята приёмками; за env-флагом, default OFF. Кандидат l71 ship-dark; живая проба двух вкладок — после посадки.

[30.08 07:00Z] 🛬 ПОСАЖЕН В l71 (артефакт ad094b26, commit 4a6b7ae5, посадка 30.08 00:53Z, обе службы, paidReady enforce_ready). Полный журнал посадки с 5 находками стражей — WEB-320 [30.08]. F-проводка за env-флагом (default OFF). Живая проба двух вкладок — после включения флага при пробах l72.
Лента
2026-09-05T17:35:28.710Z · fable-coordinator
[2026-09-05 17:40Z] Ревизия доски (волна 2040 boardtriage, M1, luna): статус → done. Основание: есть подтверждение посадки в l71. Если работа жива — верни статус и напиши в тикет, какая волна её ведёт.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-326","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-09-05T21:05:36.486Z