docs(hygiene v8.5): Часть IX конспекта + дельта v8.5 ER-диаграмм

Доделка консистентности архива после реализации v8.5
(коммиты aabf827 / 038a884 / 4ffc19a).

Объединённый_конспект.md:
- Новая Часть IX «v8.3.3 + v8.4 ФИНАЛ + МИГРАЦИЯ + АУДИТ C → v8.5
  (06–07.05.2026)» — хроника двух дней (8 подразделов: хронология,
  v8.4 финал, миграция на Server 2022, аудит C, реализация v8.5
  тремя коммитами, метрики, что осталось, 5 уроков).

Приложение_Б_В_БД_диаграммы_v8_3.md:
- Шапка обновлена: schema v8.5 (54/91/35/4 роли/12 триггеров/4 функции).
- Новая секция «Дельта v8.2 → v8.5» — компактная сводка изменений
  schema, поскольку Mermaid-диаграммы фиксируют срез v8.1.
  Перерисовка отложена до спринта 0/1 (следуем существующему
  disclaimer'у файла).
- Перечислены: +1 таблица (project_user_assignments), +26 колонок
  с разбивкой по таблицам и Биз/CTO/OPEN-И источникам, +5 индексов,
  +2 WITH CHECK + 1 новая RLS, +6 REVOKE, +1 роль (crm_audit_writer),
  +4 функции (audit_chain_hash/audit_block_mutation/
  report_jobs_log_export/calc_lead_score), +12 триггеров, ALTER
  api_keys.expires_at, закомментированный задел call_recordings.

cspell-words.txt: +KDV (старая машина), +коммита/коммитов.

Артефакт c--Users-KDV-Projects-lidpotok/ (~25 МБ JSONL старых сессий)
удалён из рабочей директории — был в .gitignore, в git не попадал,
коммит не затрагивает.

Lint+spell чистые.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-05-07 18:27:08 +03:00
parent 4ffc19a7d2
commit a4d15ee1d5
3 changed files with 232 additions and 4 deletions
+3
View File
@@ -691,3 +691,6 @@ BYTEA
trg
SPE
gethostbyname
KDV
коммита
коммитов
@@ -1359,3 +1359,150 @@ OPEN-И-12 ⏸ Где хранить контакты эскалации (P1,
*Часть VII добавлена 05.05.2026 (поздний вечер) по итогам сессии аудита связности архива и выпуска патча v8.3.1.*
*Часть VIII добавлена 05.05.2026 (поздний вечер, итерация 2) по итогам сессии закрытия бэклога 🟡-расхождений + закрытия Биз-10 переоткрытого + синхронизации narrative до v8.3.1 + устранения унаследованной арифметической ошибки сводки §0 в Прил. Е v1.8 → выпуск патча v8.3.2.*
---
# ЧАСТЬ IX. v8.3.3 + v8.4 ФИНАЛ + МИГРАЦИЯ + АУДИТ C → v8.5 (0607.05.2026)
> Часть IX добавлена 07.05.2026 (вечер) по итогам двух дней работы: финализации v8.4, миграции проекта на Server 2022, проведения аудита C двумя read-only агентами, закрытия 27 решений в реестре v1.12 и реализации v8.5 (schema + narrative + Прил. И + кросс-ссылки).
## 1. Хронология (0607.05.2026)
| Дата | Этап | Артефакты |
|---|---|---|
| 06.05.2026 (день) | **v8.3.3** — добавлен Прил. Н (реестр 28 инструментов в 4 фазах), создан корневой `CLAUDE.md`. Pravila_raboty_Claude → v1.2. Открытые_вопросы → v1.9 | 3 правки в файлах, 0 архитектурных изменений |
| 06.05.2026 (вечер) | **v8.4 финализация** — переписаны все 13 разделов плана v8.4 (§1, §5, §7, §8, §9, §12, §14, §17, §18.4, §19.10, §22, §23.10, §26). Главный narrative переименован `_v8_3.md``_v8_4.md`. Schema → v8.4 (+`outbound_webhook_subscriptions/deliveries`, +2 RLS, +5 индексов). Метрики: 51/81/31 → 53/86/33 | 2 коммита (`c8db9a2` + `4321251`); реестр → v1.10 |
| 06.05.2026 → 07.05.2026 | **Миграция на Server 2022** — заказчик сменил рабочую машину (Windows 10 KDV → Windows Server 2022 Administrator). Пересоздан node_modules, восстановлена git-история через GitHub API (после ошибочного force-push). 25 коммитов восстановлены, +`185e663` (gitignore для orphan-папок старой машины) | Перенос проекта без `.git`, восстановление через `POST /git/refs` + `git fetch` + `PATCH refs/heads/main` |
| 07.05.2026 (день) | **Аудит C** — два read-only general-purpose агента (C-1 архитектура HubSpot/Pipedrive comparison, C-2 security STRIDE). Найдено 27 новых открытых вопросов: 8 P0 + 12 P1 + 7 P2. Раздел 13 в реестре v1.11. Закрытий нет (правило §2.2) | Коммит `4d65d6a` |
| 07.05.2026 (вечер) | **Реализация v8.5** — заказчик решил «A везде» по всем 27. 3 коммита: `aabf827` реестр v1.12, `038a884` schema v8.5, `4ffc19a` narrative v8.5 + README архива + Прил. И Часть Г + кросс-ссылки | Состояние архива: 17 файлов в `docs/` + 2 в `db/` + CLAUDE.md + README.md |
## 2. v8.4 — финал narrative (06.05.2026)
Завершение плана v8.4, начатого в v8.3+. Все 13 разделов плана переписаны:
- **§1** — конкурентные преимущества vs оригинал (7 differentiator'ов: outbound webhook, аудит мутаций, Kanban DnD, real-time push, кастомизация дашборда, REST API, security-стек).
- **§5** — формат ingress (webhook/manual/CSV), 3 чекбокса click-wrap при регистрации.
- **§7** — синхронизация с фактическим schema.sql v8.4 (53 таблицы, 86 индексов, 33 RLS).
- **§8** — кастомизация статусов через `tenant_status_overrides` + Redis-кеш.
- **§9** — `EffectiveLimitCalculator` + `project_suppliers` + soft-delete TTL 180 дней.
- **§12** — расширенная аналитика, конверсия проектов (паритет с `/admin/visit/rt-stat`).
- **§14** — полный аудит мутаций (vs оригинал, который логирует только смену статуса/комментарий/звонок).
- **§17** — Тихие часы (паритет с оригиналом, на уровне тенанта).
- **§18.4** — security-differentiator (7 функций безопасности vs оригинал).
- **§19.10** — outbound webhook (10 подразделов: формат, HMAC SHA-256, retry-логика 7 попыток до 24ч, SSRF-защита).
- **§22** — расширенный CSP, защита от prompt injection в DOM, антипаттерны оригинала + code review checklist.
- **§23.10** — админка SaaS (10 подсекций включая «Желаемое × факт сегодня», workflow ПДн).
- **§26** — UX/Frontend.
## 3. Миграция на Server 2022 (0607.05.2026)
**Заказчик сменил рабочую машину.** Старый путь `c:\Users\KDV\Projects\lidpotok` (Win10) → `c:\моя\проекты\портал crm\Документация` (Server 2022 Administrator). Проект скопирован **без `.git`**.
**Установлено через Chocolatey 2.7.1** (winget на Server 2022 нет): git 2.54.0, Node.js 24.15.0 + npm 11.12.1, PHP 8.3.31 NTS x64, Composer 2.9.7. `node_modules` пересоздан `npm ci` (443 пакета, 54 сек).
**История git восстановлена через GitHub Git DB API.** В процессе была допущена критическая ошибка: при первом 404 от fine-grained PAT (Repository access не настроен) Claude ошибочно сделал `git push --force` поверх `4d65d6a`, уничтожив 24 коммита. Восстановлено через `POST /git/refs` (объекты на GitHub живут 30+ дней) + `git fetch` + `PATCH refs/heads/main` обратно на `4d65d6a`. Итого 25 коммитов на main + `185e663` (правка `.gitignore` для исключения orphan-папок).
**Урок зафиксирован в `feedback_environment.md`:** **404 от fine-grained PAT ≠ удалённый репо** — это Repository access не настроен. Никогда не делать force-push, не убедившись через 2-й независимый источник (curl с другим токеном, веб-интерфейс).
## 4. Аудит C (07.05.2026, день)
**Два независимых параллельных read-only агента:**
- **C-1 архитектурный** (general-purpose, ~100K токенов, 22 tool uses) — чтение narrative + schema + brandbook + реестр; сравнение с лучшими практиками HubSpot / Pipedrive / AmoCRM / Kommo / RetailCRM для pay-per-lead-РФ-сегмента.
- **C-2 security** (general-purpose, ~149K токенов, 21 tool use) — STRIDE для 4 потоков (логин админа, inbound webhook, биллинг, экспорт) + 5 осей (RLS-дыры, secret rotation, audit-логи, DDoS, PII).
**Найдено 27 новых вопросов с вариантами A/B/C** в `Открытые_вопросы_v8_3.md` v1.11 раздел 13:
- **Бизнес/продукт (8):** Биз-17 (автораспределение), Биз-18 (TTFR-SLA), Биз-19 (антифрод дублей) — все P0; Биз-20..22 — P1; Биз-23/24 — P2.
- **CTO (4):** CTO-13 (SET LOCAL+PgBouncer тест) — P0; CTO-14..16 — P1.
- **OPEN-И (14):** OPEN-И-13/14/15/16 — P0; OPEN-И-17..21 — P1; OPEN-И-22..26 — P2.
- **Юрист (1):** Ю-9 — P1.
**Что хорошо покрыто (из C-2):** 34/34 RLS, CSP без `unsafe-inline`, HMAC SHA-256+timestamp+replay, SSRF-фильтр, impersonation Ю-1, atomic billing, processing_restricted, four-eyes >50К ₽. Выпадает только DNS-rebinding (P1 OPEN-И-18).
**Закрытий нет** в этом коммите — по правилу §2.2.
## 5. Реализация v8.5 (07.05.2026, вечер)
**Решение заказчика:** «A везде» по всем 27 (рекомендованные варианты, минимально-достаточный baseline для pay-per-lead-сегмента + 152-ФЗ).
**Три коммита одной сессией:**
### 5.1. `aabf827` — реестр v1.11 → v1.12
- §13.10 «Закрытия аудита C — 27 решений» с тремя подразделами (P0/P1/P2). Каждое решение → ✅ + variant A + импакт на schema/narrative.
- Сводка §0: 32 ⏸ → 5 ⏸ (1 P0 = Б-1; 4 P1 ждут Б-1 или у Claude; 0 P2). 67/77 ✅ из 77 продуктовых.
- §13.10.4 «Итоговый импакт» — прогноз метрик schema v8.5.
### 5.2. `038a884` — schema.sql v8.4 → v8.5
| Параметр | v8.4 | v8.5 | Δ |
|---|---|---|---|
| Логических таблиц | 53 | **54** | +1 (`project_user_assignments`, CTO-16) |
| Индексов | 86 | **91** | +5 |
| RLS-политик | 34 | **35** | +1 + WITH CHECK на 2 |
| ENABLE RLS | 34 | **35** | +1 |
| Ролей БД | 3 | **4** | +1 (`crm_audit_writer`) |
| Триггеров | 0 | **12** | +12 |
| Функций | 0 | **4** | +4 |
| Колонок | — | +26 | (см. CHANGELOG_schema §Y.2) |
**Ключевые DDL-изменения:**
- **OPEN-И-15** (append-only audit hash chain): `log_hash BYTEA` на 5 audit-таблицах (`auth_log`, `activity_log`, `pd_processing_log`, `saas_admin_audit_log`, `balance_transactions`); 10 триггеров (5×2 = BEFORE INSERT для `audit_chain_hash()` + BEFORE UPDATE/DELETE для `audit_block_mutation()`); функция `audit_chain_hash()` через `digest(prev_hash || NEW::text, 'sha256')`; новая роль `crm_audit_writer` (только INSERT); REVOKE UPDATE/DELETE на этих 5 таблицах от других ролей.
- **OPEN-И-14** (RLS WITH CHECK + REVOKE): WITH CHECK добавлен к политикам `tenant_isolation` на `deal_tag_pivot` и `saas_invoice_items`; REVOKE ALL на 6 saas-таблицах от `crm_app_user`.
- **Биз-19** (антифрод дублей): `deals.duplicate_of_id BIGINT` (без FK — партиционированная); индекс `(duplicate_of_id) WHERE NOT NULL`.
- **CTO-14** (UTM): 4 × VARCHAR(100) на `deals` + индекс `(tenant_id, utm_source) WHERE NOT NULL`.
- **CTO-16** (skill-based routing): новая таблица `project_user_assignments(project_id, user_id, skills JSONB)` + RLS через JOIN на `projects.tenant_id`.
- **Биз-22** (lead scoring без ML): `suppliers.quality_score`, `deals.time_in_form_seconds/lead_score`, триггер `calc_lead_score()` BEFORE INSERT/UPDATE.
- **OPEN-И-13** (SSO): `saas_admin_users.sso_provider/is_break_glass`.
- **CTO-15+Ю-9** (two-person impersonation): `impersonation_tokens.second_approver_id/second_approval_at`.
- **OPEN-И-17/19** (TTL secrets): ALTER `api_keys.expires_at SET NOT NULL DEFAULT NOW()+365d`; `tenants.api_key_limit INT DEFAULT 5 CHECK (1..10)`.
- **OPEN-И-26** (call_recording задел Post-MVP): закомментированный DDL `call_recordings(...)` 10 строк в секции 17.
**CHANGELOG_schema.md** — новая запись Y (v8.4 → v8.5) с детальным разбором всех изменений по 10 подразделам.
### 5.3. `4ffc19a` — narrative v8.4 → v8.5
- Файл переименован `CRM_bp-gr_Инструкция_v8_4.md``_v8_5.md`. README архива тоже переименован (v8_4 → v8_5).
- Шапка обновлена + блок «Что нового в v8.5» (~60 пунктов решений с привязкой к секциям).
- **8 новых in-place подразделов:**
- §10.8 «Антифрод+routing+scoring+regions+escalation» — Биз-17/19/22/23, CTO-16, OPEN-И-25/26 (~150 строк).
- §12.5.5 — TTFR-SLA (Биз-18) + UTM-когорты (CTO-14).
- §14.8 — append-only audit hash chain (OPEN-И-15) + report_jobs export trigger (OPEN-И-20).
- §17.9 — Telegram-канал (Биз-20), эскалация-нотификации (OPEN-И-25), late waiting_payment alert (Биз-24).
- §19.10.1112 — DNS-rebinding pin-IP (OPEN-И-18), `marketing.conversion` event (Биз-21).
- §22.13 — SSO+SET LOCAL+Sentry+anti-DDoS+TTL+two-person+audit-chain (~250 строк, самый большой подраздел).
- §23.10.11 — break-glass dashboard, two-person UI, 152-ФЗ ст.21 hard-block UI.
- §7.1 — метрики schema обновлены под v8.5.
- **Прил. И → v0.3 (новая Часть Г)** — 9 операционных процедур: Г.1 RLS smoke-test (CTO-13, BLOCKER для фазы 1), Г.2 cron `audit:verify-chain` (OPEN-И-15), Г.3 `secrets:notify-expiring` (OPEN-И-17), Г.4 anti-DDoS компоненты, Г.5 per-tenant DEK + crypto-shred (OPEN-И-22), Г.6 `pg_anonymizer` для staging (OPEN-И-24), Г.7 Yandex 360 SSO setup (OPEN-И-13), Г.8 cron `leads:escalate-stale` (OPEN-И-25), Г.9 cron `payments:notify-stale` (Биз-24).
- Кросс-ссылки обновлены в `CLAUDE.md`, `README.md`, `web/index.html`, `db/schema.sql` шапке.
## 6. Метрики двух дней
- **5 коммитов** на main за 0607.05.2026: `c8db9a2` (v8.4 финал), `4321251` (README архива v8.4), `185e663` (gitignore migration), `4d65d6a` (audit C), `aabf827` (реестр v1.12), `038a884` (schema v8.5), `4ffc19a` (narrative v8.5) + восстановленные после force-push.
- **27 продуктовых решений закрыто** (8 P0 + 12 P1 + 7 P2).
- **+1 таблица, +26 колонок, +5 индексов, +12 триггеров, +4 функции, +1 роль, +2 WITH CHECK, +6 REVOKE.**
- **+~700 строк narrative** (8 новых подразделов + Часть Г Runbook + 2 шапки).
- **markdownlint+cspell чистые** на каждом коммите.
- **0 orphan-FK, 0 дубликатов CREATE TABLE, метрики совпадают с grep'ами** (self-review §8 CLAUDE.md).
## 7. Что осталось после v8.5
1. **HTML-прототипы 04..08** (Диз-1, у Claude). Перед стартом — уточнить с заказчиком возможное переоткрытие Диз-2 («дизайн не нравится» 06.05).
2. **Б-1** ждём — единственный реальный P0-блокер от заказчика. От него: Диз-3, DO-2, DO-4, OPEN-З-*.
3. **Триггер фазы 1 архитектурно разблокирован.** Финальный gate — RLS smoke-test (CTO-13) в спринте 1 (Прил. И §Г.1, BLOCKER без прохождения).
4. **ER-диаграммы Прил. Б+В** под v8.5 — обновлено в этой же сессии (см. отдельную правку файла).
5. **Ротация утёкшего PAT**`github_pat_11...` (утёк в чат 07.05 при восстановлении истории) ротирован 07.05 при push'е v8.5-коммитов.
## 8. Уроки сессии
1. **«A везде» — допустимый fast-track при качественном предварительном ранжировании.** Аудит C ранжировал варианты A как минимально-достаточные; «A везде» прошло без переоткрытий потому что агенты сделали homework. Если бы варианты A были компромиссными — fast-track мог бы загнать решения в рискованную зону.
2. **Schema-CHANGELOG записи Y/Z/A/B по обратной хронологии — рабочий паттерн.** При каждом bump'е новая запись в начало; старые остаются неизменными. Реалистично читается.
3. **«Что нового в vX.Y» блок в шапке narrative — критичная навигация.** При v8.5 добавлено 8 подразделов в разные секции — без сводного блока в шапке невозможно найти что изменилось без grep'а.
4. **Три коммита вместо одного — правильно.** Реестр (декларация решений) → schema (DDL) → narrative (документация) — три разных уровня абстракции, ревью по очереди легче, rollback каждого слоя возможен независимо.
5. **Триггеры audit-append-only + отдельная роль = реальная защита.** Один слой (только триггеры) — admin может сделать `ALTER TABLE … DISABLE TRIGGER` (если есть права). Один слой (только REVOKE на роль) — другая роль может писать с правами. Двойной слой = и admin не отключит триггер на чужой роли, и роль `crm_audit_writer` не имеет UPDATE/DELETE.
---
*Часть IX добавлена 07.05.2026 (вечер) по итогам двух дней работы 06–07.05.2026: финализация v8.4 (13/13 разделов плана) + миграция проекта на Server 2022 + аудит C двумя read-only агентами (27 новых вопросов) + реализация v8.5 (3 коммита: реестр v1.12, schema v8.5 с +1 таблицей/+26 колонками/+12 триггерами/+4 функциями/+1 ролью, narrative v8.5 с 8 новыми подразделами + Прил. И Часть Г).*
@@ -7,14 +7,92 @@
- **Часть Б** — ER-диаграмма (Mermaid `erDiagram` × 6 доменов + flowchart-карта + текстовый справочник 81 FK + рейтинг таблиц + дельта к v8.0).
- **Часть В** — State machine диаграммы (Mermaid `stateDiagram-v2` × 4: `saas_transactions`, `tariff_subscriptions`, `report_jobs`, `refund_requests`).
**Источник фактов для обеих частей:** `db/schema.sql` (актуальная — v8.4: 53 логических таблицы + 12 партиций, 86 индексов, 34 RLS-политики на 34 защищённых таблицах). При расхождении ER/state machines с этой версией — приоритет за `schema.sql`.
**Источник фактов для обеих частей:** `db/schema.sql` (актуальная — **v8.5**: 54 логических таблицы + 12 партиций, 91 индекс, 35 RLS-политик с WITH CHECK на 2, 35 ENABLE RLS, 4 роли, 12 триггеров, 4 функции). При расхождении ER/state machines с этой версией — приоритет за `schema.sql`.
**Замечания по версионированию:**
- ER-диаграмма (Часть Б) исходно от v8.1 (`ER_диаграмма_v8_1.md`). Изменения схемы v8.2 (`suppliers`, `project_suppliers`, расширение `projects`, `project_limit_adjustments`), v8.3 (переписанная `reminders`, capabilities у `suppliers`, `tenants.desired_daily_numbers`) и v8.4 (`outbound_webhook_subscriptions`, `outbound_webhook_deliveries`, RLS на `deal_tag_pivot`) **не отражены в ER-диаграммах** — перерисовка отложена до спринта 0/1, когда схема стабилизируется. До этого момента читать ER + сверяться с актуальной `db/schema.sql` v8.4 и `Прил_М_Analiz_originala_v8_3.md` §3.1, §3.5.
- State machines (Часть В) от v8.1 — изменений в state machines между v8.1 и v8.4 не было.
- ER-диаграмма (Часть Б) исходно от v8.1 (`ER_диаграмма_v8_1.md`). Изменения схемы v8.2/v8.3/v8.4/v8.5 **не отражены в Mermaid-диаграммах** — перерисовка отложена до спринта 0/1, когда схема стабилизируется. До этого момента **читать ER + сверяться с актуальной `db/schema.sql` v8.5 + блоком «Дельта v8.2 → v8.5» ниже** + `db/CHANGELOG_schema.md` (записи Y, Z, A, B) + `Прил_М_Analiz_originala_v8_3.md` §3.1, §3.5.
- State machines (Часть В) от v8.1 — изменений в state machines между v8.1 и v8.5 не было (один новый workflow v8.5 — pending-state у `impersonation_tokens` для two-person approval — описан текстом в narrative §22.13.7 без отдельной диаграммы; добавление в Часть В возможно в спринте 1).
**Связано:** `db/schema.sql` v8.4, `db/CHANGELOG_schema.md` (запись Z), `Прил_М_Analiz_originala_v8_3.md` v1.1 §3.
**Связано:** `db/schema.sql` v8.5, `db/CHANGELOG_schema.md` (записи Y=v8.5, Z=v8.4), `Прил_М_Analiz_originala_v8_3.md` v1.1 §3, `CRM_bp-gr_Инструкция_v8_5.md` §7.1.
---
## Дельта v8.2 → v8.5 (не перерисовано в Mermaid, читать вместе с диаграммами v8.1 ниже)
Поскольку Mermaid-диаграммы фиксируют срез v8.1 (47 таблиц, 81 FK), для актуальной картины их нужно дополнять перечнем изменений v8.2..v8.5. Ниже — компактная сводка, что нужно мысленно добавить к диаграммам.
### v8.2 (04.05.2026) — реселлерская модель + 152-ФЗ
**Новые таблицы (+5):** `suppliers` (B1/B2/B3), `project_suppliers` (M2M), `project_limit_adjustments`, `pd_subject_requests`, `incidents_log`. **Новые колонки в существующих:** `users.notification_preferences JSONB` (8×3), `tenants.chargeback_unrecovered_rub`, `pd_subject_requests.processing_restricted` (OPEN-Д-1 152-ФЗ ст.21), `refund_requests.source`. **+`projects` 6 полей:** `daily_limit_target`, `effective_daily_limit_today`, `region_mask`, `region_mode`, `delivery_days_mask`, и др.
### v8.3 (05.05.2026) — аудит партий 12–15
**Переписана `reminders`:** `created_by` (вместо `user_id`), `assignee_id NULL`, `completed_at` (вместо `is_done`). **Удалены `deals.reminder_text/reminder_at/idx_deals_reminder`** — множественные напоминания через таблицу `reminders`. **+`suppliers` 5 capabilities:** `channel`, `supports_sender_name/keyword/csv_upload/domains_list`. **+`tenants.desired_daily_numbers`** (Биз-16, сигнал саппорту).
### v8.4 (06.05.2026) — outbound webhook
**Новые таблицы (+2):** `outbound_webhook_subscriptions`, `outbound_webhook_deliveries`. **+RLS-политика для `deal_tag_pivot`** (hotfix через JOIN на `deal_tags.tenant_id`). **Метрики:** 51→53 табл, 81→86 инд, 31→34 RLS на 34 защищённых таблицах.
### v8.5 (07.05.2026) — реализация 27 решений аудита C
**Новые таблицы (+1):**
- **`project_user_assignments(project_id, user_id, skills JSONB, is_active)`** (CTO-16) — пул менеджеров проекта для `assignment_strategy IN ('round_robin','least_loaded')` Post-MVP. RLS через JOIN на `projects.tenant_id` (USING + WITH CHECK). PK = `(project_id, user_id)`. Индекс `idx_project_user_assignments_user(user_id) WHERE is_active`.
**Новые колонки (+26):**
| Таблица | Колонки | Источник |
|---|---|---|
| `suppliers` | `quality_score NUMERIC(3,2) DEFAULT 1.00` | Биз-22 |
| `saas_admin_users` | `sso_provider VARCHAR(32) DEFAULT 'yandex360'`, `is_break_glass BOOLEAN` | OPEN-И-13 |
| `impersonation_tokens` | `second_approver_id BIGINT REFERENCES saas_admin_users(id)`, `second_approval_at TIMESTAMPTZ` | CTO-15 + Ю-9 |
| `tenants` | `api_key_limit INT DEFAULT 5 CHECK (1..10)`, `telegram_bot_token TEXT` | OPEN-И-19, Биз-20 |
| `projects` | `assignment_strategy VARCHAR(32) DEFAULT 'manual'`, `ttfr_target_minutes INT DEFAULT 15` | Биз-17, Биз-18 |
| `users` | `telegram_user_id BIGINT` | Биз-20 |
| `deals` | `assigned_at TIMESTAMPTZ`, `escalated_count INT DEFAULT 0`, `duplicate_of_id BIGINT` (без FK), `utm_source/utm_medium/utm_campaign/utm_content VARCHAR(100)`, `region_code VARCHAR(8)`, `city VARCHAR(100)`, `time_in_form_seconds INT`, `lead_score NUMERIC(5,2)` | OPEN-И-25, Биз-19, CTO-14, Биз-23, Биз-22 |
| `auth_log`, `activity_log`, `pd_processing_log`, `saas_admin_audit_log`, `balance_transactions` | `log_hash BYTEA` | OPEN-И-15 |
**ALTER (1):** `api_keys.expires_at SET NOT NULL DEFAULT NOW() + INTERVAL '365 days'` (OPEN-И-17 + OPEN-И-19; миграция backfill всем NULL-ключам).
**Новые индексы (+5):** `(tenant_id, utm_source) WHERE NOT NULL`, `(tenant_id, region_code) WHERE NOT NULL`, `(duplicate_of_id) WHERE NOT NULL`, `(tenant_id, assigned_at) WHERE status NOT IN ('closed','rejected')`, `idx_project_user_assignments_user(user_id) WHERE is_active`.
**Новые политики RLS (+1):** `tenant_isolation` на `project_user_assignments` через JOIN на `projects.tenant_id` (USING + WITH CHECK).
**WITH CHECK добавлен к существующим политикам (+2):** `tenant_isolation` на `deal_tag_pivot` (через `deal_tags.tenant_id`) и `saas_invoice_items` (через `saas_invoices.tenant_id`). До v8.5 защита была только на USING — INSERT мог пройти при `tag_id`/`invoice_id` чужого тенанта.
**REVOKE ALL от `crm_app_user` (+6 таблиц):** `saas_admin_users`, `saas_admin_sessions`, `saas_admin_audit_log`, `incidents_log`, `pd_subject_requests`, `impersonation_tokens` (defense-in-depth, OPEN-И-14).
**Новые роли (+1):**
- **`crm_audit_writer`** — INSERT-only на 5 audit-таблицах. UPDATE/DELETE заблокированы и триггерами, и REVOKE'ом. Application пишет в audit под этой ролью через temporary `SET ROLE`.
**Новые функции (+4):**
- **`audit_chain_hash() RETURNS TRIGGER`** (OPEN-И-15) — заполняет `NEW.log_hash = sha256(prev_hash || NEW::text)` перед INSERT в audit-таблицы.
- **`audit_block_mutation() RETURNS TRIGGER`** (OPEN-И-15) — `RAISE EXCEPTION` при UPDATE/DELETE.
- **`report_jobs_log_export() RETURNS TRIGGER`** (OPEN-И-20) — auto-INSERT в `pd_processing_log` action='exported' при создании `report_jobs`.
- **`calc_lead_score() RETURNS TRIGGER`** (Биз-22) — `lead_score = supplier.quality_score × (time_in_form_seconds / 60)`, clamped в `[0, 99.99]`.
**Новые триггеры (+12):**
- **5 пар** на audit-таблицах (`auth_log`, `activity_log`, `pd_processing_log`, `saas_admin_audit_log`, `balance_transactions`): `BEFORE INSERT` (hash chain) + `BEFORE UPDATE OR DELETE` (block mutation) = 10 триггеров.
- **1 на `report_jobs`:** `AFTER INSERT``pd_processing_log`.
- **1 на `deals`:** `BEFORE INSERT OR UPDATE OF time_in_form_seconds, project_id` → расчёт `lead_score`.
**Закомментированный задел Post-MVP:**
- **`call_recordings(tenant_id, deal_id, call_started_at, duration_sec, direction, recording_path, transcript)`** (OPEN-И-26) — задел под Биз-12 (телефония + транскрипция), 10 строк закомментированного DDL в `db/schema.sql` секция 17. Активация — однострочный uncomment + миграция при первом клиентском запросе Post-MVP.
**Метрики schema v8.5:** 54 таблицы + 12 партиций, 91 индекс, 35 RLS-политик (из них 2 с WITH CHECK обновлены), 35 ENABLE RLS, **4 роли** (crm_app_user / crm_admin_user / crm_migrator / crm_audit_writer), **12 триггеров**, **4 функции**.
**Новые межсущностные связи v8.5 (для будущей перерисовки ER):**
- `project_user_assignments → projects (project_id)` + `→ users (user_id)` ON DELETE CASCADE (новая M2M).
- `impersonation_tokens.second_approver_id → saas_admin_users(id)` (новая FK).
- `deals.duplicate_of_id` (self-reference, **без FK** — партиционированная таблица).
> **Полный DDL v8.5**`db/schema.sql`. **Полный CHANGELOG v8.4 → v8.5**`db/CHANGELOG_schema.md` запись Y. **Narrative-обоснование v8.5**`CRM_bp-gr_Инструкция_v8_5.md` блок «Что нового в v8.5» + §10.8/§12.5.5/§14.8/§17.9/§19.10.1112/§22.13/§23.10.11. **Operational runbook v8.5**`Runbook_ekspluatatsii_v8_2.md` Часть Г (9 процедур).
---