Files
han-app/functional_blocks (business logic)/user-requirements.md
T

38 KiB
Raw Blame History

Бизнес-постановка: Пользователь (User)

Статус: v1 — консолидация принятых решений из HAN_chat_specification (arch-00arch-05, module-01, module-08); открытые вопросы зафиксированы в §17
Продукт: HAN Chat (клиентское приложение + api-backend + Keycloak)
Источники: архитектура HAN_chat_specification; макет Figma (не канон — только визуализация; при расхождении приоритет у этого ТЗ и arch-документов)
Связанный backlog: кнопка «Войти»; история устройств входа; дифференцированные ошибки OTP; debounce SMS; тестовый пользователь с фиксированным SMS-входом
Смежно: уведомления (контуры G/P), чат, согласия, UX-сессия, CRM Contact через bitrix-sync

Нормативная часть — §1–§16. §17 — ненормативный журнал решений и открытых вопросов; при расхождении с §1–§16 приоритет у §1–§16. При расхождении этого документа с arch/module после их обновления — приоритет у arch/module до синхронизации.


1. Цель

Дать клиенту устойчивую идентичность в HAN Chat: вход по подтверждённому телефону, локальную карточку пользователя в App DB, согласия, readonly-профиль в ЛК и связь с Contact в Bitrix24 — без смешения гостевого просмотра и персонального кабинета.

Гость изучает сервис без записи в App DB. Авторизованный клиент получает персональные данные, чат, персональные уведомления и профиль. Auth-идентичность принадлежит Keycloak; приложение владеет бизнес-карточкой пользователя, согласиями и кэшем профиля для UI.


2. Два режима клиента

Режим Кто это Идентичность на бэкенде Что доступно
G. Гость Клиент без действующего JWT Нет UserIdentity; опциональный локальный guest_session_id только на устройстве UI + GET /api/v1/public/*; гостевые уведомления (контур G)
A. Авторизованный Клиент с валидным access token и выполненным bootstrap user_identities (keycloak_sub ↔ JWT sub) JWT API: чат, профиль, согласия, персональные уведомления, UX-сессия

Правила:

  1. До успешного OTP + bootstrap клиент — только гость. Write-endpoint (consents, session-start, чат, профиль и т.д.) требуют JWT.
  2. После авторизации гостевой контент не переносится в персональный (как в уведомлениях: G не мигрирует в P).
  3. guest_session_id не является auth и не открывает write API.
  4. Наличие refresh token в secure storage позволяет вернуться в режим A без OTP (§6.3); отсутствие/истечение refresh → снова гость до следующего защищённого действия.
  5. Access token проверяет api-backend; refresh выполняет только frontend через Keycloak. Backend refresh не делает.

3. Границы релиза

3.1. В scope

  • OTP-only вход по номеру телефона (Keycloak; mock до controlled SMS cutover).
  • OIDC Authorization Code + PKCE; refresh / logout; silent return без OTP при валидном refresh.
  • Локальный find-or-create UserIdentity + минимальный ClientProfile через POST /api/v1/auth/bootstrap.
  • Согласия: обязательные personal_data + user_agreement, опциональный marketing; фиксация версий в user_consents.
  • Readonly блочный профиль GET /api/v1/me; зарезервированный GET /api/v1/me/documents (MVP может быть пустым).
  • Аналитическая UxSession для авторизованного клиента (session-start, заголовок X-Ux-Session-Id).
  • Асинхронный map/create Contact в Bitrix24 по телефону через sync_queue (не блокирует вход).
  • Нормализация телефона в E.164; phone claim только из JWT, не из body клиента.
  • Ownership: все пользовательские ресурсы адресуются через user_id из JWT.

3.2. Вне scope

  • Пароль, email-OTP, social login, magic link.
  • Редактирование профиля клиентом (PATCH/PUT отсутствуют).
  • Доставка документов компании в блок «Документы» (post-MVP; API зарезервирован).
  • История устройств входа (backlog п.19) — модель и UI позже.
  • Смена телефона как продуктовый self-service flow (policy Keycloak есть; продуктовый UX — отдельно).
  • Merge/reassignment двух sub на один телефон — только administrative policy, не side effect login.
  • Гостевая запись согласий и UX-сессии в App DB.
  • Создание UserIdentity / ClientProfile сервисом bitrix-sync (sync не участвует в OTP-flow).

4. Изменения UI (относительно гостевого экрана)

Место Гость Авторизованный
Главная Публичный контент, гостевые уведомления Персональные уведомления, чат доступен
Отправка сообщения / популярный вопрос Сначала согласия → OTP → bootstrap → отправка отложенного текста Штатная отправка
Центр уведомлений Auth-gate «Авторизоваться» Список персональных
Профиль Недоступен / ведёт на вход Readonly блоки «Личные данные», «Документы»
Кнопка «Войти» Запускает поток авторизации Скрыта / заменена профилем (по макету)
Выход Очистка tokens → гостевой UI

Визуал — по Figma. Figma не канон поведения и состава полей профиля: канон — §5.5 и arch-01.


5. Бизнес-модель

5.1. Слои идентичности (не смешивать)

Слой Где живёт Что хранит Master
IdP user Keycloak (keycloak schema) Realm user, phone verified, sessions, tokens Keycloak
UserIdentity App DB user_identities Локальный user_id, связь keycloak_sub, кэш auth-телефона, last_login_at Keycloak для телефона/sub; App DB для бизнес-FK
ClientProfile App DB client_profiles Кэш полей UI без CRM-идентификаторов UI-поля — последнее успешно синхронизированное значение (входящий поток MVP — Bitrix24); auth-телефон инициирует sync, но master телефона — Keycloak
Bitrix Contact CRM Bitrix24 Карточка клиента в CRM Bitrix24 для CRM-полей; связь user_id ↔ b24_id хранится только в bitrix_sync.entity_external_mapping
Гость Только устройство UI-state, локальные согласия до OTP, опционально guest_session_id Нет серверной записи

Инвариант: один verified phone ↔ один active Keycloak sub. Один sub ↔ одна active UserIdentity. Один user_id ↔ один ClientProfile.

5.2. Связанные сущности (часть домена «пользователь», но не сам User)

Сущность Роль Когда появляется
UserConsent Факт принятия документа конкретной версии bootstrap или POST /consents
UxSession Аналитический период активности session-start только у авторизованного
Dialog / Message Чат с оператором После auth, лениво при первом сообщении
Notification (P) Персональные уведомления user_id NOT NULL

UxSession не является механизмом авторизации и не заменяет JWT.

5.3. Согласия

Тип Обязательность (seed) Документ / URL Версия
personal_data да (consent.personal_data.required=true) consent.personal_data.document_url (+ политика consent.privacy_policy.document_url) consent.personal_data.version
user_agreement да consent.user_agreement.document_url consent.user_agreement.version
marketing нет consent.marketing.document_url consent.marketing.version

Правила:

  1. Pop-up согласий показывается до OTP; до получения JWT факт принятия хранится только на клиенте.
  2. Серверная фиксация — в bootstrap (атомарно с созданием пользователя) или позже через POST /api/v1/consents при смене версий документов.
  3. Запись UserConsent immutable: unique (user_id, consent_type, document_version); исправление — новая версия документа или administrative action с audit.
  4. Обязательные согласия без accepted: true403 consents_required; вход в ЛК / write API с непринятыми актуальными обязательными версиями блокируется.
  5. Keycloak consent screen не заменяет продуктовые согласия API.

5.4. Auth-телефон

  • Единственный канал MVP: номер телефона + OTP.
  • Нормализация: libphonenumber → canonical E.164.
  • В App DB телефон пишется только из JWT claims при bootstrap / обновлении identity, никогда из body клиента.
  • Порядок claim: phone_number, иначе preferred_username только если значение валидно как E.164.
  • Отсутствие/невалидность при bootstrap → 400 phone_claim_missing.
  • Утечка существования номера запрещена на стороне Keycloak (одинаковый внешний ответ для нового/существующего).
  • В ClientProfile при создании копируется в russian_phone (минимальный профиль); дальнейшее обогащение — из CRM sync.

5.5. Профиль (UI)

Блочная модель. Редактирование клиентом недоступно.

Блок «Личные данные»:

Поле Источник отображения Примечание
ФИО (full_name) client_profiles Может быть null до sync из Bitrix24
Гражданство (citizenship) client_profiles null до заполнения
Телефон РФ (russian_phone) client_profiles При bootstrap = auth-телефон
Зарубежный телефон (foreign_phone) client_profiles Опционально
Email (email) client_profiles Опционально

Блок «Документы»:

  • перечень документов компании, дата, наименование, скачивание;
  • в MVP список может быть пустым; доставка из Bitrix24 — post-MVP;
  • API: GET /api/v1/me/documents, GET /api/v1/documents/{id}, .../download-url с audit.

Макет Figma может показывать дополнительные секции (патент, РВП и т.п.) — это не канон MVP-модели данных; расширение блоков — отдельное решение.

5.6. Жизненный цикл пользователя

Состояние Условие Что видит клиент
Гость Нет валидного JWT Публичный UI
OTP in progress Идёт challenge в Keycloak Экраны телефона / кода
Authenticated, bootstrap pending Есть JWT, нет local UserIdentity Frontend обязан вызвать bootstrap; прочие protected → 409 «bootstrap required»
Authenticated, ready Есть UserIdentity + актуальные обязательные согласия Полный ЛК
Soft-deleted record_status='D' на identity (админ) Доступ запрещён; детали — operational policy

Бизнес-«удаление аккаунта» клиентом в MVP не моделируется. Soft-delete — административный контур (arch-05).

5.7. Константы (app_settings)

Ключ Смысл Default / seed
auth.phone.enabled Вход по телефону true
auth.password.enabled Пароль false
otp.phone.max_send_attempts_per_24h Лимит отправок OTP 3
otp.phone.min_seconds_between_attempts Минимальный интервал между отправками 30
otp.phone.max_verify_attempts Лимит проверок кода 5
otp.phone.code_length Длина кода 6
otp.phone.ttl_seconds TTL кода 60
otp.phone.sms_order_timeout_ms Таймаут заказа SMS 3000
consent.* URL/версии/required флагов согласий см. arch-04
ux.session.idle_timeout_minutes Idle → новая UX-сессия 30

Счётчики OTP ведёт Keycloak/SPI, не api-backend. Продуктовые otp.phone.* Keycloak читает через settings bridge GET /internal/settings/v1/otp.


6. Поведение UI и сценарии

6.1. Гостевой режим

  1. Клиент открывает приложение → гостевой UI.
  2. Доступен только GET /api/v1/public/* (+ статика).
  3. Согласия и session-start в App DB не пишутся.
  4. Попытка защищённого действия (сообщение, Центр уведомлений, профиль) → поток авторизации.

6.2. Поток первой авторизации (OTP)

  1. Триггер: отправка сообщения / популярный вопрос / «Войти» / иное действие, требующее auth.
  2. Pop-up согласий; обязательные должны быть приняты локально.
  3. Форма телефона → Keycloak OTP-flow (mock или real SMS через sms-service).
  4. Успешная проверка OTP → tokens (Authorization Code + PKCE).
  5. POST /api/v1/auth/bootstrap с локальными согласиями и device metadata.
  6. POST /api/v1/analytics/session-start при необходимости новой UX-сессии.
  7. Триггер БД ставит contact.map_or_create в sync_queue (асинхронно; ошибка CRM не откатывает вход).
  8. Frontend продолжает исходное действие (в т.ч. отложенное сообщение / популярный вопрос).

UX-инвариант (backlog п.21): ошибка отправки отложенного сообщения после успешного bootstrap не должна выглядеть как «не удалось завершить вход». Вход завершён на шаге 5–6; ошибка Bitrix/чата показывается в контексте чата.

6.3. Возврат без OTP

  1. Есть валидный refresh token → Refresh Token Grant → access token.
  2. При необходимости — session-start.
  3. OTP не показывается.
  4. Нет/истёк refresh → гость до следующего защищённого действия.

6.4. Поддержание сессии (tokens)

  • Frontend проактивно обновляет access token (~60 с до exp), single-flight.
  • Успешный refresh не создаёт новую UX-сессию.
  • 401 от API → один refresh + retry исходного запроса; провал refresh → очистка tokens → гость.
  • То же для WebSocket /api/v1/realtime.

6.5. UX-сессия

Новая UxSession только при:

start_reason Когда
first_launch В памяти нет ux_session_id
cold_start Kill app / закрытие вкладки
idle_timeout Простой > ux.session.idle_timeout_minutes

ux_session_id хранится только в памяти (не в localStorage). Передаётся как X-Ux-Session-Id. Отсутствие заголовка API не блокирует (кроме endpoint, где id обязателен).

6.6. Профиль

  • Открывается только авторизованным.
  • Данные — GET /api/v1/me; поля могут быть частично пустыми до CRM sync.
  • Редактирование недоступно; изменение ФИО/email и т.п. — через процессы компании (Bitrix24 → sync).
  • Документы — отдельный блок; скачивание с audit.

6.7. Выход

  1. Frontend инициирует logout у Keycloak (revocation по policy модуля).
  2. Очищает access/refresh tokens и in-memory UX-сессию.
  3. UI переходит в гостевой режим.
  4. Локальный UserIdentity в App DB не удаляется.

7. Матрицы поведения

7.1. Что требует auth

Действие Гость Авторизованный
GET /api/v1/public/* да да
Просмотр главной / гостевых уведомлений да нет (после входа — только P)
Отправка сообщения / вложение нет → OTP да
POST /auth/bootstrap нет (нужен JWT после OTP) да (идемпотентно)
POST /consents, session-start нет да
GET /me, чат, персональные уведомления нет да
WS /api/v1/realtime нет да

7.2. Источник истины полей

Поле / факт Master Куда кэшируется
sub / существование IdP user Keycloak user_identities.keycloak_sub
Auth-телефон Keycloak user_identities.phone_number, seed client_profiles.russian_phone
Согласия (версия + accepted) App DB user_consents
ФИО, гражданство, email Последний успешный sync (MVP: Bitrix → App) client_profiles
foreign_phone Не синхронизируется в первом релизе client_profiles
Связь с Contact bitrix-sync Только bitrix_sync.entity_external_mapping; в App DB не кэшируется
Tokens / auth session Keycloak secure storage на клиенте
ux_session_id App DB + память клиента заголовок запросов

7.3. Bootstrap — идемпотентность

Повторный вызов Результат
Тот же sub, те же версии согласий 200, тот же user_id; last_login_at обновляется; дублей consent нет
Тот же sub, новые версии согласий Новые immutable строки consent + update identity
JWT без phone claim 400 phone_claim_missing
Обязательные consents не accepted 403 consents_required

Application-код не пишет в sync_queue: задачи создают триггеры на insert/update UserIdentity / ClientProfile.


8. Идентификация

Идентификатор Назначение
keycloak_sub Subject JWT; ключ find-or-create
user_id PK user_identities; FK всех персональных сущностей App DB
phone_number Auth-телефон E.164
guest_session_id Локальный UUID устройства; не auth
ux_session_id Аналитическая сессия
b24_id Внутренний идентификатор Contact; используется только bitrix-sync
device_id Opaque id устройства в bootstrap / session-start; в audit/log не копируется как PII

Публичные id — UUID (в App DB предпочтительно UUID v7, как в остальных доменах).


9. API

Общие конвенции — arch-02.

9.1. Клиентские (JWT)

Метод и путь Назначение
POST /api/v1/auth/bootstrap Find-or-create пользователя + согласия
POST /api/v1/consents Повторная фиксация версий согласий
POST /api/v1/analytics/session-start Новая UxSession
GET /api/v1/me Readonly блочный профиль
GET /api/v1/me/documents Список документов (MVP может быть пустым)
GET /api/v1/documents/{id} Metadata документа (owner only)
GET /api/v1/documents/{id}/download-url Presigned GET + audit

Auth у Keycloak: публичные OIDC endpoints через /auth/* (не часть api-backend).

9.2. Public (без JWT)

Метод и путь Назначение
GET /api/v1/public/settings (и связанные public) Флаги auth, URL/версии согласий, OTP UI-параметры по is_public

9.3. Internal (смежные)

Метод и путь Кто → кто Назначение
GET /internal/settings/v1/otp Keycloak SPI → api-backend Продуктовые OTP limits
POST /internal/sms/v1/send Keycloak → sms-service Заказ SMS OTP (real mode)

9.4. Ошибки (домен пользователя)

Код HTTP Когда
phone_claim_missing 400 Нет канонического телефона в JWT при bootstrap
validation_error 400 Невалидные версии/тело согласий или device
unauthorized 401 Нет/невалиден JWT
consents_required 403 Обязательные согласия не приняты
resource_state_conflict 409 Protected endpoint до bootstrap («bootstrap required»); открытый вопрос TBD-1 по унификации с 404 для consents
profile_not_found 404 Профиль не найден (по контракту envelope)
rate_limit_exceeded 429 Превышен лимит

Дифференцированные тексты ошибок OTP на UI (неверный код / истёк / лимит send / лимит verify) — backlog п.10; контракт Keycloak/frontend уточняется отдельно, в этом ТЗ фиксируется требование продукта.

9.5. Rate limiting

Зона Identity
Auth edge (nginx) IP
bootstrap / consents / session-start user + IP
OTP product limits phone (Keycloak counters)

10. Модель данных (схема han_app)

Общие правила — module-01 §9.1 / arch-05: UUID PK, timestamptz UTC, common fields, soft-delete A/D, FK ON DELETE RESTRICT.

10.1. user_identities

Поле Тип Описание
id uuid PK user_id
keycloak_sub varchar(255) NOT NULL UNIQUE JWT sub
phone_number varchar(32) NOT NULL E.164 из JWT
last_login_at timestamptz NOT NULL Обновляется на bootstrap
common fields обязательны

Индексы: unique keycloak_sub; index на phone_number для CRM map. Телефон в App DB не unique: identity master — Keycloak; временный конфликт при merge/миграции допустим на уровне данных, но продуктово один phone = один active sub.

10.2. user_consents

Поле Тип Описание
id uuid PK
user_id uuid FK
ux_session_id uuid NULL Если сессия уже есть
consent_type varchar personal_data | user_agreement | marketing
document_version varchar Версия из app_settings
accepted boolean
accepted_at timestamptz
client_ip inet
user_agent_hash varchar
device snapshot jsonb / поля По module-01 (device_json и т.п.)
common fields обязательны

Unique (user_id, consent_type, document_version). Записи immutable.

10.3. client_profiles

Поле Тип Описание
id uuid PK
user_id uuid UNIQUE FK 1:1 с identity
full_name varchar NULL
citizenship varchar NULL
russian_phone varchar NULL Seed из auth-телефона
foreign_phone varchar NULL
email varchar NULL
source_updated_at timestamptz NULL Метка источника sync
common fields обязательны

CRM Contact ID и mapping в client_profiles отсутствуют. PII не попадает в логи и generic audit payload.

10.4. ux_sessions

id = ux_session_id; user_id; start_reason; platform; app_version; device_id; started_at; common fields.

10.5. Триггеры sync

Событие task_type
Insert active UserIdentity / ClientProfile без mapping contact.map_or_create
Изменение tracked profile / auth-phone полей contact.update

Подавление эха: GUC han.sync_suppress при записи из bitrix-sync. Ошибка CRM не откатывает bootstrap и чат.


11. Фоновые и смежные процессы

Процесс Владелец Связь с пользователем
OTP challenge / counters / expiry Keycloak SPI До появления App user
SMS order / delivery journal sms-service Только доставка кода
contact.map_or_create / contact.update / contact.deactivate bitrix-sync После bootstrap / изменения телефона / деактивации
contact.rebind bitrix-sync Audited административное исправление ошибочного mapping
Token refresh / logout Frontend + Keycloak Не трогает App DB identity
Retention UX-сессий (если введён) ops / module Не удаляет UserIdentity

12. Audit и observability

event_type Actor Когда
auth.bootstrap user Успешный bootstrap
consent.recorded user Запись согласий (bootstrap или /consents)
session_start user Новая UX-сессия
document.download_url_issued user Скачивание документа профиля
OTP security events Keycloak Send/verify attempts (schema keycloak, phone HMAC/masked)

В audit нет: полного phone/email/name в свободном тексте логов общего контура, OTP raw code, tokens, presigned URL.

Метрики (минимум): число bootstrap/сутки, доля consents_required, доля phone_claim_missing. Latency map Contact и доля пользователей без active mapping спустя N минут считаются bitrix-sync по собственной схеме.


13. Хранение данных и PII

  • UserIdentity, ClientProfile, UserConsent — прикладные строки, soft-delete, физическое удаление запрещено (arch-05).
  • Auth-мастер PII телефона — Keycloak; App DB держит кэш для FK/CRM/UI.
  • Согласия хранятся бессрочно как юридически значимый журнал (immutable rows).
  • Гостевые локальные согласия на устройстве до OTP не являются серверным журналом и при сбое до bootstrap могут быть потеряны — клиент проходит согласия снова.
  • Right-to-erasure / удаление аккаунта клиентом — вне scope MVP; потребует отдельной политики по IdP + App DB + CRM.

14. Смежные сервисы

Компонент Ответственность в домене User
Frontend Гость/ЛК, согласия UI, OTP UX, tokens, refresh, bootstrap/session-start, профиль readonly, отложенное сообщение после входа
Keycloak IdP, OTP, phone uniqueness, tokens, sessions
api-backend Bootstrap, consents, me/profile, JWT validation, ownership, settings bridge OTP
sms-service Durable order SMS (real mode)
bitrix-sync Map/update Contact; не создаёт UserIdentity
nginx /auth/*, edge rate limit auth
App DB Таблицы §10, триггеры sync

Порядок работ (если дорабатывать домен): (1) Keycloak OTP + phone claims → (2) bootstrap + consents + identity/profile → (3) session-start → (4) me/profile UI → (5) CRM map → (6) documents post-MVP.


15. Влияние на arch-документы

Документ Статус относительно этой постановки
arch-00-glossary.md Термины UserIdentity, ClientProfile, UserConsent, UxSession, guest_session_id, keycloak_sub уже заданы
arch-01-system-architecture.md Потоки гостя, OTP, возврата, профиля — канон сценариев
arch-02-api-contracts.md Контракты bootstrap / consents / session-start / me
arch-04-settings-and-content.md auth.*, otp.phone.*, consent.*, ux.session.*
module-01-api-backend.md Таблицы и алгоритмы bootstrap
module-08-keycloak.md OTP-only phone flow
module-07-bitrix-sync.md Обработка contact.* задач

Этот документ не заменяет module-спеки; он собирает бизнес-смысл сущности «Пользователь» для аналитики и смежных фич (уведомления, чат, документы).


16. Критерии приёмки

  1. Гость видит только public API; write без JWT недоступен; guest_session_id не открывает API.
  2. Вход только по телефону + OTP; пароль/email/social отсутствуют.
  3. После OTP bootstrap создаёт/находит UserIdentity, минимальный ClientProfile, пишет согласия; телефон берётся из JWT, не из body.
  4. Повторный bootstrap идемпотентен; last_login_at обновляется.
  5. Без обязательных согласий — 403 consents_required; без phone claim — 400 phone_claim_missing.
  6. Protected endpoint до bootstrap — безопасный отказ (409 до закрытия TBD-1).
  7. Возврат с валидным refresh — без OTP; провал refresh — гостевой UI.
  8. session-start только с JWT; в гостевом режиме не вызывается; idle/cold/first_launch создают новую UX-сессию.
  9. GET /me отдаёт блочный readonly профиль; PATCH/PUT нет.
  10. Ошибка CRM sync не ломает вход; Contact мапится асинхронно.
  11. Один active phone ↔ один active sub на стороне Keycloak; merge не происходит молча при login.
  12. Отложенное сообщение / популярный вопрос после auth уходит штатно; ошибка доставки не маскируется под ошибку входа.
  13. Выход очищает tokens и возвращает в гостевой UI, не удаляя UserIdentity.
  14. PII не светится в обычных логах/audit payload; OTP code не логируется.

17. Журнал решений и открытых вопросов (ненормативно)

17.1. Принятые решения

# Решение Раздел / источник
D1 Два режима: гость и авторизованный; гостевые данные в App DB не пишутся §2, arch-01
D2 Слои IdP / UserIdentity / ClientProfile / Bitrix Contact разделены; master auth — Keycloak §5.1
D3 OTP-only phone; password disabled §3, module-08
D4 Согласия продуктовые в API; Keycloak их не заменяет; серверная запись только после JWT §5.3
D5 Телефон только из JWT claims §5.4, arch-02
D6 Профиль readonly и блочный; документы — отдельный блок, доставка post-MVP §5.5
D7 Bootstrap атомарный + идемпотентный; sync через триггеры БД §7.3
D8 UX-сессия ≠ auth; только для авторизованных; хранение id в памяти §6.5, arch-00
D9 CRM не блокирует авторизацию §6.2
D10 Один verified phone = один active sub module-08

17.2. Открытые вопросы

# Вопрос Предложение Влияние
Q1 Единый код для protected endpoint до bootstrap: 409 vs 404 (TBD-1 module-01) Оставить 409 resource_state_conflict OpenAPI, клиентский UX
Q2 Дифференцированные ошибки OTP на UI (backlog п.10) Зафиксировать словарь кодов Keycloak → frontend texts module-08 + frontend
Q3 История устройств входа (backlog п.19) Отдельная сущность/таблица, не смешивать с UxSession Новая постановка
Q4 Debounce/backoff SMS после интеграции провайдера (backlog п.11) Надстройка над otp.phone.min_seconds_between_attempts Keycloak SPI
Q5 Тестовый пользователь с фиксированным SMS (backlog п.16) Операционный allow-list / mock per-phone, не дырка в prod limits ops + module-08
Q6 Продуктовый self-service смены телефона Позже: re-auth + OTP нового номера + invalidate sessions Keycloak + bootstrap
Q7 Клиентское удаление аккаунта / right-to-erasure Вне MVP; отдельная юридическая и техническая постановка IdP + App + CRM
Q8 Расширение блоков профиля сверх «Личные данные» / «Документы» (как в Figma-моках) Только после продуктового решения; Figma не канон UI + client_profiles / новые таблицы

18. Связь с уведомлениями

Аспект Гость Авторизованный пользователь
Контур уведомлений G (guest_notifications) P (notifications.user_id)
Бейдж непрочитанных нет да
Центр уведомлений auth-gate список
Перенос G → P при логине запрещён

Домен User задаёт, кто видит контур P; домен Notification задаёт что показывается. Владелец персональных записей — всегда user_identities.id.