WEB board

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

chat-global отдаёт 403 model_not_allowed обычной учётке + UI врёт «Session expired»

Закрыт P1 · важно ведёт: backup-opus
Суть
## Найдено 20.08 ~17:30, qa-screens Pass 2, прод v4-bb1186a
Свежая учётка qa-screens-20260820@test.local (teamVoice, полные три поля) в глобальном чате: вопрос сохраняется, затем UI показывает «Your session expired. Sign in again to continue» при ВАЛИДНОЙ сессии (/api/auth/session 200). Network: 403 /api/chat-global, тело LLM_REQUEST_REJECTED, reason=model_not_allowed.
Скрины: qa-screens/gallery-20260820/69-web057-chat-history-after-answer.png, 71-retry-after-12s.png (M4).

## Гипотезы (проверять в порядке)
1) модель-дефолт новой учётки не в allowlist её tier-а; 2) rolePolicy/pilot-конфиг для не-owner учёток; 3) связь с ROLE_STRUCTURED_CLAIMS.
Отдельный дефект: маппинг 403→«session expired» в UI — ложь пользователю, класс правила 10 (тихая ошибка).

## Примечание
Owner-чат (_@wool2.online, тетрадный) работает — подтверждено owner-ом живьём. Ломается именно chat-global для обычных учёток.

## 20.08 ~19:55 ДИАГНОЗ ГОТОВ (волна web060, отчёт M4 WEB060-DIAGNOSIS.md)
КОРЕНЬ: не сессия, а модель-полиси. Новой учётке при регистрации персистится settings.chatModel=gpt-4o-mini / aiSettings.preferredModel=gpt-4o-mini, а релизный allowlist gpt-4o* НЕ содержит. Живые пробы под валидной сессией: без model → 403 model_not_allowed; gpt-4o-mini → 403; gpt-4o → 403; gpt-5.4-mini → 200 ✅; gpt-5.4-nano → 200 ✅; openrouter:… → 503 accounting_unhealthy (другой гейт).
Путь: chat-global/route.ts:2120-2126 (requestedModel || storedUserPrefs.preferredModel → resolveGlobalChatModel). Чтение префов: route.ts:231-243.
ФИКС (2 части): (а) дефолт новой учётки → актуальная модель релиза + миграция персиста gpt-4o* существующих; (б) UI-ложь «Session expired» на 403 model_not_allowed — маппинг ошибок чинить отдельно (волна нашла место: неверный fallback по коду ответа).
Примечание волны: 3 пробы с разрешёнными моделями прошли до провайдера (мелкий платный расход) — честно задекларировано.

## Трейл (21.08)
- Ветка посадки-30: `landing30` (worktree A1 /home/ubuntu/waves/wt-landing30, от serving f1e49bca), слито 6 веток / 88 файлов, проверка дубликатов ключей в словарях: 0.
- Живой статус смены: `~/Downloads/OPS-STATUS-LIVE.md` (ноутбук) + копии на M1 `~/livepush/` и M4 `/Users/milamarty/work/`.
- Паспорт проекта: https://bugs.wool2.online/passport (обновлён 21.08: serving arm64-f1e49bca, статы из свежей ветки, дельты в stats-history.json).
- Леджер ревизора: `/Volumes/M4Ext/ops/reviser-ledger/` (пакеты + аудит-бандлы, sha сверены).
- Диагноз: M4 `/Users/milamarty/work/WEB060-DIAGNOSIS.md` (живые пробы моделей: gpt-4o-mini → 403, gpt-5.4-mini/nano → 200).
- Фикс: ветка `l30chat`, коммит `f396f94e` (дефолт-модель из релизного allowlist; отказ перестал маскироваться под session-expired).
- Путь в коде: `src/app/api/chat-global/route.ts:2120-2126` (резолв модели), `:231-243` (чтение персиста), `src/lib/chat/globalChatModel.ts`, `globalChatOutcome.ts`.
- Первично найдено волной qa-screens Pass 2, скрины: M4 `qa-screens/gallery-20260820/69-*, 71-*`.

## 23.08 — вошёл в посадку, ждёт живой приёмки [эпик WEB-314]
- Груз в проде: 504af77f (посадка 30, 21.08 03:53 UTC; предок serving 26116cff); артефакт a3c1c9d6…b595; paidReady=true.
- Метод приёмки: живое тыкание chat-global обычной учёткой + скрин (не 403, не «session expired»).
- Порядок: ancestry+байты в артефакте → live-проба → скрин/raw run id в тикет; правила и доки-основания в WEB-314.

## 23.08 — evidence скрин-волны: чат открывается, но ответа НЕТ (FAIL_VISIBLE)
- Скрин 02-web060-chat-global.png: панель «Глобальний чат» открыта, поле ввода живое, вопрос набран — ответ не получен.
- Время совпадает с egress-блоками сторожа (см. WEB-316: unauthorized_vendor_egress_blocked на openrouter catalog в 01:13-01:14). Вероятен общий корень с realtime: vendor-вызовы мимо spend-gateway.
- Приёмка WEB-060 блокируется до фикса WEB-316 (волна web316srv уже работает на A1).

## 23.08 evidence-pass (l3031-evidence)
- Audit: GET before PATCH found no exact 23.08 evidence-pass section; existing body was preserved and this is the single appended section.
- Ancestry: git merge-base --is-ancestor f396f94e 504af77f rc=0; git merge-base --is-ancestor 504af77f 26116cff rc=0; exact source=26116cff032ad8f015e1e2d9cba302e1e8f7ec27.
- Bytes: exact 26116cff contains release-model allowlist/default migration and maps model_not_allowed away from session-expired. Raw source/ancestry SHA-256=5fd780dc8d9f4823ea318dfcbac27c76a8333d75e0a68a084073e4add1946db2.
- Live: no authenticated QA session: credentials callback HTTP 302, /api/auth/session=null. Existing unauth raw web060-chat-global-noauth.json SHA-256=f39b276ac5d639eed5759569c68389c85b2c222219d8c7f6abfb420f1013379c; login probe SHA-256=3020bdf182128451795610e09688faefe24211460f13f84648c1991ad7e29a55. No runId claimed; live ordinary-account chat remains open.
- Verdict proposal: NEEDS_LIVE_HAND.

## 23.08 00:48 UTC — owner: «сейчас чат отвечает»
- Симптом «ответ не приходит» (скрин 02-web060-chat-global.png, ~01:40 IST) владельцем НЕ воспроизводится к 00:48 UTC — чат отвечает.
- Трактовка: либо intermittent (совпадал с пачками egress-блоков каталога — WEB-316), либо скрин-волна поймала транзиент. Приёмку вести с учётом флаппинга: 3 пробы в разное время + корреляция с журналом сторожа; закрывать только при 3/3 зелёных.

## 23.08 09:40 UTC — ЖИВАЯ приёмка (волна l3031live, M4, рабочий логин 200/200/200). ACCEPT: /api/chat-global HTTP 200 обычной учёткой, ответ «Да, Париж — столица Франции», billed op_309b67dd; НЕ 403/model_not_allowed/Session-expired. Скрин web060-chat-global-answer.png. Закрыт живьём.
Починено в
504af77f
Лента
2026-08-23T08:30:21.587Z · codex-l3031-live
L3031 LIVE ACCEPT: обычная QA-учётка через global chat получила ответ HTTP 200, без 403/model_not_allowed и Session expired. Raw: qa-screens/l3031-web060-chat-admission.json; screenshot: shots-l3031-live/web060-chat-global-answer.png. Verdict ACCEPT. Предлагаемый статус: готов к done.
Воркер
не привязан — привязать: curl -X POST https://bugs.wool2.online/api/web/assign -H 'content-type: application/json' \ -d '{"issueId":"WEB-060","session":"<имя tmux-сессии>","host":"m4"}'
Обновлён
2026-08-23T08:32:55.902Z