WEB board

всеканоны и докиворкеры↗ iOS↗ Легаси
WEB-315 · Дефект · — · web

P1: «Скинути чат» не стирает серверную память — новый чат пересказывает историю до сброса (чинено ≥2 раза: PRIVACY-1400, CHAT-0886)

Закрыт P1 · важно ведёт: backup-opus
Суть
# «Скинути чат» не стирает память: новый чат пересказывает историю до сброса (repro owner 23.08, прод 26116cff)

## Репро (owner, 23.08 ~00:11 UTC, прод serving 26116cff)
1. Панель «Асистент» (агент «Універсал», Лицарі Ради Викл), чат с историей приветствий (hi/привет/привіт).
2. Меню → «Скинути чат» → чат визуально пуст.
3. В НОВОМ чате: «Hi» → ассистент отвечает: «Ми обмінялися привітаннями: ви писали “hi/привет/привіт”, а я відповідав» — пересказ истории ДО сброса.

## Доказательства
- Экспорт нового чата (Експорт у JSON): `~/Downloads/chat-json-2026-08-23T00-11-46-027Z.json` (Интел) — в ленте ТОЛЬКО 2 сообщения (Hi + ответ), но ответ цитирует стёртую историю. Полное содержимое (487 bytes):
```json
[{"id":"1787432300588","role":"user","content":"Hi","timestamp":1787432300588,"channel":"chat"},
 {"id":"1787432308979","role":"assistant","content":"Слава, добрий день. Коротко про те, що “є” в цьому чаті: Ми обмінялися привітаннями: ви писали “hi/привет/привіт”, а я відповідав…","timestamp":1787432308979,"channel":"chat"}]
```
- Скрин owner: Снимок 2026-08-23 в 01.12.28 (меню Скинути чат).

## История починок этого класса (owner прав: «чинили не раз»)
- PRIVACY-1400: GlobalChatModal.handleResetChat шлёт DELETE /api/runtime-profile, «recall does not survive the reset» (src/components/modals/GlobalChatModal.tsx:681+).
- CHAT-0886: ChatInterface.handleResetChat (панель Асистент, src/components/chat/ChatInterface.tsx:2529+): clearActiveChatTranscript() + per-notebook reset cutoff + «surgical server-side messages delete» + scoped DELETE /api/runtime-profile?notebookId=…&kind=… (строка 2549).
- Родственный открытый: WEB-057 (заглушки отказов переживают очистку).
- Код обеих починок ПРИСУТСТВУЕТ в serving-дереве (проверено грепом ~/livepush @ canon 26116cff) — и всё равно репродуцируется.

## Корневые гипотезы (для волны)
1. Скоуп-мисматч: DELETE чистит runtime-profile по notebookId/kind, а recall-инъекция читает другой скоуп (глобальный/агентский «Універсал»/channel=chat) — сброс в панели без notebookId уходит не туда.
2. Тихая ошибка (нарушение правила 10): оба fetch DELETE обёрнуты в catch(() => {}) без лога/маячка — если роут отвечает 401/404/500, сброс «удался» только визуально. Проверить фактический ответ DELETE на проде.
3. Другой источник памяти: пайплайн агента (Універсал) собирает контекст не из runtime-profile, а из персистентного message-store/саммари, который reset cutoff не покрывает.

## Что сделать (bounded волна, когда освободится слот)
1. Живьём повторить репро на QA-учётке с DevTools/network: зафиксировать фактический запрос DELETE (URL+status) при сбросе из панели Асистент.
2. Трейс recall-инъекции: откуда в промпт попадает пересказ (runtime-profile GET? messages history? summary?) — точный файл/строка.
3. Фикс с НЕГАТИВНЫМ тестом: сброс → новый чат → модель обязана НЕ знать историю; тест ломает вход (не чистим) и обязан упасть.
4. Убрать тихие catch{} на обоих reset-путях (лог + маячок) — правило 10.

Связано: эпик приёмки WEB-314 (посадки 30/31), WEB-057, WEB-060 (chat-global).

---

## WEB315 investigation / fix (2026-08-23)

Маркер: WEB315_BLOCKED_QA_AUTH

Корень подтверждён локальным trace: ChatInterface reset до фикса отправлял только notebookId+kind, тогда как recall read в actions.ts:2800-2815 формировал effective scope с sourceIds/noteIds. В runtime-profile/store.ts:202-224 это разные ключи: reset notebook:notebook:<nb> против recall notebook:sources:<sha1(...)>. Инъекция: actions.ts:3331 (recentTurns -> effectiveHistory), actions.ts:3414-3436 (archive/summary/recent), actions.ts:5284-5291 (providerMessages).

Фикс в commit d4327808: runtime profiles сохраняют scope metadata; clear удаляет exact и связанные source-set keys; ChatInterface передаёт активные sourceId; notebook messages DELETE ждёт clear перед 200; reset catches в ChatInterface, GlobalChatModal и useStore дают console.error + beaconDiagnostics с status/body/error.

Тесты: pre-fix negative regression rc=1 (старый source-set turn оставался и попадал в model history); post-fix focused clear-on-delete rc=0, 12/12; UI parity/static suite rc=0, 31/31; git diff --check rc=0. Деплой не выполнялся.

Live QA authenticated repro заблокирован: пароль/сессия qa-screens-20260820@test.local отсутствуют. Контрольный unauthenticated DELETE https://nb.wool2.online/api/runtime-profile вернул status 401 и body Not authenticated; чужие cookies и chat-global не использовались. Полный отчёт: /home/ubuntu/waves/WEB315-REPORT.md.

## 2026-08-23 10:51 UTC — ЗАДЕПЛОЕНО в прод (посадка 32, v4-4482925)
Фикс в составе rel-4482925 (source 44829258a9398ebef6aedfeb9a959b7be4fbd4d3), аттестованный cutover, paidReady=true. Байт-маркеры фикса проверены в standalone до посадки (см. L32V2-REBUILD-REPORT.md на A1). Дальше: живая приёмка глазами в браузере против serving v4-4482925 (правило visual-acceptance) — после неё закрытие.


## 2026-08-23 11:07 UTC — ⚠️ ЖИВОЕ РЕПРО OWNER на v4-4482925: удаление истории НЕ чистит session-recall
Owner: удалил историю чата, спросил в чистом полотне — ассистент пересказал удалённое. Экспорт owner ~/Downloads/chat-json-2026-08-23T09-54-51-321Z.json ДОКАЗЫВАЕТ ответчика: routed {"provider":"system","model":"session-recall-truth","intent":"recall"} — recall читает хранилище, которое не инвалидируется удалением истории (в пересказе всплыл даже старый отказ платного пути «This paid call path is not registered…»). Т.е. посаженный фикс WEB-315 закрыл СВОЙ слой, но у фичи вторая стена (правило fix-one-layer≠fix-the-feature). Статус назад в in_progress. Фикс-волна web315r на A1 (Luna xhigh, ветка web315r от 0c3cdb4): чистка источника recall при удалении + негативный тест. Ожидаю WEB315R_READY.


## 2026-08-23 11:35 UTC — приёмка l32accept: ЗАБЛОКИРОВАНА 503 (WEB-322)
M4 (отчёт L32-ACCEPT-REPORT.md, L32_ACCEPT_DONE): оба сабмита в чат отклонены сервером ДО ответа модели — POST /api/chat-global 503 LLM_REQUEST_REJECTED/accounting_unhealthy → ни персистентность, ни негатив сброса проверить нельзя. Вердикт NEEDS-FIX (блокирован), скрины/сырьё в qa-screens/l32-live-raw.json. Новый блокер оформлен: WEB-322 (гонка admission-гейта на бёрсте). Поведенческая приёмка 315 повторится после разблокировки чата. Параллельно волна web315r (A1) пилит recall-хранилище.


## 2026-08-23 11:45 UTC — ФИКС ГОТОВ (A1 web315r, маркер WEB315R_READY)
КОРЕНЬ НАЙДЕН: recall («session-recall-truth», buildConversationRecallTextFromTurns) читает НЕ таблицу Message, а JSON UserSettings.settings.runtimeProfiles[scopeKey] (archiveHighlights+summaryTurns+recentTurns). Reset удалял Message-строки и профиль ТОЛЬКО с точным совпадением kind=notebook — профиль той же notebook с kind=active_notebook (realtime) выживал и пересказывал удалённое. Это ровно репро owner + WEB-057.
ФИКС: DELETE /api/notebooks/:id/messages теперь граница приватности notebook — сносит ВСЕ runtimeProfiles этого notebookId независимо от kind/source-set + отвязывает VoiceSession. Тесты 13/13 (позитив recall до удаления + НЕГАТИВ history→delete→recall для active_notebook), dialog-сюита 117/117, tsc rc=0. Коммит f3898a74 (ветка web315r от 0c3cdb4), патч web315r-fix.patch (sha cc26d2e6ab32…, копия на Интеле). Кандидат посадки 33. Живая приёмка после посадки (сейчас заблокирована WEB-322).


## 2026-08-23 14:57 UTC — ЗАДЕПЛОЕНО: посадка 33 (v4-00c402c) села с 1-й попытки
rel-00c402c (SHA 58af4a94…), аттестованный cutover c НОВЫМ пин-гейтом (все 7 env-пинов сверены с аттестацией ДО флипа — негативный гейт из урока посадки 32). paidReady=true, канон проштампован. Живая приёмка фикса — следующий шаг.
NB: 315 уже в review; фикс web315r в этой посадке — живой негатив (история→удаление→recall пуст) гнать при приёмке.

## 2026-08-23 16:12 UTC — ✅ OWNER ПОДТВЕРДИЛ ЖИВЬЁМ на v4-00c402c: память обнуляется после затирки
Экспорты owner (chat-json-2026-08-23T11-06-54 / 11-08-40): после удаления истории recall честно отвечает «У цій розмові ми ще майже нічого не обговорювали» / «я не вижу о чём говорили раньше». Это ровно негатив фикса web315r. Закрываю по owner-визуалу (правило visual-acceptance выполнено самим owner).
Замечен в сборке
26116cff
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-315","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-23T11:11:18.158Z