-`crm-bp-gr-audit-2026-05-04.md` — партии 1–11 (исходный аудит, не входит в архив v8.3++ optimized; хранится в предыдущей итерации).
-`audit-batch-12-2026-05-05.md` — Конверсия проектов + Напоминание + Досье.
-`audit-batch-13-2026-05-05.md` — Рекомендации источников + Настройки реестра + Просмотреть + Список звонков.
-`audit-batch-14-2026-05-05.md` — Редкие статусы + База знаний + Безопасность профиля.
-`audit-batch-15-2026-05-05.md` — Карточки внешних интеграций «API ▾».
> **⚠ Состояние на 05.05.2026 (v8.3++ optimized):** 4 файла `audit-batch-12..15-2026-05-05.md` объединены в единый документ **`Аудит_partii_12_15_originala_v8_3.md`** (части 12, 13, 14, 15). Содержимое полностью сохранено без изменений. Все ссылки в данном Прил. М вида «партия 12.2.4», «партия 15.3» и т. п. читать с учётом нового размещения: соответствующая «Часть N, раздел X.Y» в `Аудит_partii_12_15_originala_v8_3.md`.
**Назначение:** перевести наблюдения аудита в формальные архитектурные решения для платформы Лидерра. Закрыть пробелы между нашей моделью v8.3 и реальной структурой оригинала. Поднять перед заказчиком новые продуктовые вопросы, возникшие из аудита.
- v1.0 (04.05.2026) — исходный анализ по 11 партиям, 4 новых вопроса (Биз-10/11/12/13).
- v1.1 (05.05.2026) — добавлены результаты партий 12–15 (раздел 9), 3 новых вопроса (Биз-14/15/16), уточнения в §3 (новая модель `reminders`, расширение `suppliers` capabilities, новое поле `tenants.desired_daily_numbers` — §3.5), уточнение в §2.5 (Биз-10 переоткрыт), окончательный вердикт по outbound webhooks (§2.3).
Аудит crm.bp-gr.ru, проведённый в 15 партий за 04.05–05.05.2026 (11 исходных + 4 параллельные), подтвердил архитектурные решения Лидерры, обнаружил расхождения и выявил новые продуктовые вопросы.
**Главное расхождение №1:** в оригинале существует архитектурный слой **«Поставщик» (B1/B2/B3)** с**разными capabilities** для каждого (B1 — звонки/сайты, B2 — sms+sender_name+keyword, B3 — sms+только sender_name). У нас этого слоя не было — нужна таблица `suppliers`с полями `channel`, `supports_sender_name`, `supports_keyword` + связь `project_suppliers`.
**Главное расхождение №2:** в оригинале **дневной лимит проекта автоматически режется при нехватке баланса** (`effective_daily_limit = min(project.daily_limit, balance / lead_cost)`) — это бизнес-правило, без которого продуктовый паритет с оригиналом не достигается. У нас сейчас лимиты статичные.
**Главное расхождение №3 (новое в v1.1):** в оригинале множественные напоминания на сделку реализованы через массив `histories[].type='reminder'`, не через одиночное поле. У нас в schema v8.2 — поля `deals.reminder_text` + `deals.reminder_at` (одиночное). Нужна **отдельная таблица `reminders`**с FK на `deals` (поля минимального паритета: `text + remind_at + created_by + created_at`).
**Главное подтверждение:** в оригинале **исходящих webhook'ов нет** — окончательно, 7 независимых линий доказательств (партии 10 + 15). Уточнение: для Bitrix24 есть один hardcoded URL `prostats.info/bitrix24/webhook.php`, но это inbound-канал (Bitrix → prostats → CRM), не outbound CRM. Наш Уровень 1 (outbound webhook на MVP, OPEN-И-2) — это конкурентное преимущество.
- В DOM каждой страницы — **подтверждённая prompt injection-атака** с явным таргетингом на агентов-Claude (партия 10, повторно подтверждено в 12/13/14/15). Источник не установлен. Действия Claude в Chrome корректные во всех 4 партиях.
- На странице профиля **пароль пользователя виден в HTML-форме plaintext** (`<input type="text" name="User[password]">` — партия 14). У нас выделенная защищённая форма «старый/новый/подтверждение».
- В карточках интеграций **API-ключи и client_secret хранятся как `<input type="text">`** (Скорозвон, Unisender, Мои Звонки — партия 15). У нас должны быть `type="password"` + кнопка «показать».
-В оригинале **отсутствуют 2FA, журнал входов, список сессий, история паролей** (партия 14). У нас всё это есть в schema v8.2 (`auth_log`, `user_sessions`, `user_recovery_codes`) — это **сильное конкурентное преимущество**.
**3 новых продуктовых вопроса (Биз-14/15/16):** TTL удаления проектов, wizard рекомендаций источников, таргет `desired_daily_numbers`. Все с дефолтами, не блокеры. См. §9.5.
---
## 1. Контекст аудита
### 1.1. Что было сделано
Аудит провёл оператор Claude в Chrome в **read-only режиме** (без сохранений, удалений, изменений настроек). Аккаунт владельца с ролью админа. ПДн клиентов замаскированы.
**Аудит проведён в 2 захода — всего 15 партий:**
#### Заход 1 — 04.05.2026 (11 партий)
| № | Раздел | Что исследовано | Заметка |
|---|---|---|---|
| 1 | Каркас приложения | Меню, шапка, дашборд, реестр сделок, реестр проектов | Render error в первой версии 1.4 — переписан в 11 |
| 2 | Сделка и статусы | Карточка сделки, 14 статусов | Переписан в партии 2-bis (11) |
| **10** | **Карточка проекта (повторный заход)** | **3 типа карточек + форма создания + история действий за 5 месяцев** | **Раскрыта сущность «Поставщик»** |
| **11** | **Карточка сделки (повторный заход)** | **5 реальных сделок в 5 разных статусах** | **Подтверждена свободная state-machine** |
#### Заход 2 — 05.05.2026 (4 партии)
| № | Раздел | Что исследовано | Заметка |
|---|---|---|---|
| **12** | **Конверсия проектов + Напоминание + Досье** | Структура отчёта 17 колонок, форма напоминания (4 поля), 2 разные модалки «Досье» | **Биз-10 переоткрыт** — модель `histories[]` с массивом напоминаний |
| **13** | **Рекомендации источников + Настройки реестра + Просмотреть + Список звонков** | Wizard OSINT-разведки, аккордеон настроек, иконка «Просмотреть», агрегация звонков | **B1/B2/B3 жёстко подтверждены** — цитата из UI о capabilities; новое поле `desired_daily_numbers` |
| **14** | **Редкие статусы + KB + Безопасность профиля** | Косвенное подтверждение единой карточки во всех 14 статусах, внешняя KB HelpDeskEddy, /admin/user/account | **Ключевое:** в оригинале нет 2FA, нет auth_log, нет sessions — **наше преимущество**. Анти-паттерн: пароль plaintext в HTML. |
| **15** | **Карточки внешних интеграций «API ▾»** | 12 пунктов меню, 5 открытых карточек (Bitrix, amoCRM, Скорозвон, Unisender, Мои Звонки), JS-поиск 31 keyword, network-мониторинг | **Финальный вердикт:** outbound webhook'ов нет (7 линий доказательств). Только hardcoded receiver `prostats.info/bitrix24/webhook.php` (inbound). Анти-паттерн: API-key в `<input type="text">` |
### 1.2. Что НЕ исследовано (важно учитывать)
**После 15 партий пробелов осталось значительно меньше.** Закрыты партиями 12–15: форма «Напоминания» (партия 12), модалка «Досье» (партия 12), внешние интеграции «API ▾» (партия 15), безопасность профиля (партия 14). Косвенно подтверждены — единая структура карточки во всех 14 статусах (партия 14, через DOM фильтра).
**Остающиеся пробелы:**
1.**Биллинг полностью** — `/admin/site/pay-history` и `/admin/site/balance` отдают «Технические работы». Структуру транзакций, тарифной сетки, формы пополнения восстановить невозможно. **Следствие:** все наши решения по биллингу (§20–21) — не «реверс-инжиниринг», а наша собственная модель с оглядкой на видимый виджет «4 счётчика в шапке».
2.**Эмпирическая проверка карточки сделки в 9 редких статусах** (Новый, Оплачено, Горячий, На замену, Партнёрка, Ожидаем оплаты, Конечный недозвон, Тест драйв, База) — на текущем аккаунте 0 сделок во всех 14 статусах. Косвенное подтверждение единой структуры через DOM фильтра + партию 11 для 5 «частых» статусов. Окончательная эмпирическая проверка требует аккаунта с непустыми редкими статусами.
3.**Tab «Pr-cy» в модалке `visitdop`** — частично заблокирован render-fn, требует сделки с заполненным доп-разделом (`model.isVisitDop=true`). На MVP не реализуется (см. §9.1.4), поэтому пробел не критичен.
4.**Голосовой Робот (3 sub-страницы), Google Sheets, Chat GPT** — карточки не открыты в партии 15 как «низкоприоритетные для гипотезы outbound webhook». HTML серверным fetch проверен (0 webhook-keyword), что исключает основную гипотезу.
5.**Реальный TTL удалённых проектов** — нижняя граница ≥3 месяца (наблюдение 96 дней), верхняя не определяется без админ-доступа. Требует решения Биз-14.
### 1.3. Технические условия аудита
- **Outbound webhook поиск:** проверены 24 keyword'а (webhook, postback, колбэк, s2s, server-to-server, endpoint, ingress, вебхук, пиксел, signature, hmac, bearer, retry, timeout, payload, тестовая отправка и др.) — **ноль совпадений** в карточке проекта, форме создания, истории за 5 месяцев и 27 уникальных дат.
- **Real-time:** WebSocket / SSE — 0 каналов на всех исследованных страницах.
- **XHR:** 6 эндпоинтов в партии 10, все внутренние GET. Списки грузятся POST + CSRF.
- **Render error:** `TypeError: Cannot read properties of undefined (reading '0')` в Element UI повторяется 4× за каждую навигацию. Сломал отрисовку таблицы сделок в первой сессии — отсюда ошибочный вывод «0 сделок». **На самом деле в системе 12 876 сделок** (выборка `visit=rt`).
---
## 2. Главные архитектурные открытия
### 2.1. 🔴 Поставщики B1/B2/B3 — отдельная сущность над проектом
**Что было неясно до партии 10.** Префиксы `B1_`, `B2_`, `B3_` фигурировали в slug'ах статусов и тегах проектов с самого начала аудита, но смысл был непонятен. Гипотеза до аудита: «это просто префиксы названий».
**Что раскрыла партия 10.** B1/B2/B3 — это **три внутренних поставщика данных (источников номеров)** в оригинале, между которыми проект может выбирать:
- **B1** — основной поставщик для типов «Сайты» и «Звонки».
- **B2** — поддерживает «СМС» через связку «Наименование отправителя» + «Ключевое слово».
- **B3** — поддерживает «СМС» только по наименованию отправителя.
**Где видно.**В**форме создания нового проекта** B1/B2/B3 — это **три чекбокса (все checked по умолчанию)**. В**карточке существующего проекта** — readonly-индикаторы (см. партию 10.3, секция 3 структуры карточки).
**Что это означает для модели данных.**
Реальная цепочка не «Проект → Лид», а **«Поставщик → Проект → Лид»**, где у каждого поставщика свои правила приёма (тип данных, обязательные поля, формат payload).
-В`supplier_lead_costs` поле `supplier_code VARCHAR(50)` со значением по умолчанию `'crm_bp_gr'` — то есть **один абстрактный поставщик** для всей платформы.
-В`system_settings` константа `supplier_default_cost_rub` — одна цена для всех лидов.
-В`projects` нет связи с поставщиком вообще.
**Что нужно добавить (детально — §3.1).** Новая таблица `suppliers` (B1/B2/B3 как seed, расширяемо), связь m2m `project_suppliers`с per-supplier настройками, миграция `supplier_lead_costs.supplier_code` → `supplier_id BIGINT REFERENCES suppliers(id)`.
**Замечание.** Заказчик в реселлерской модели (Ю-2) покупает у crm.bp-gr.ru как у одного контрагента — то есть для нас **поставщик первого уровня = `crm_bp_gr`**, а B1/B2/B3 — это **«суб-поставщики» в рамках crm.bp-gr.ru**. Это важно для DDL: `suppliers.parent_id` или отдельный уровень `supplier_channels`. Решение в §3.1.
### 2.2. 🔴 Динамическая регулировка лимитов по балансу
**Что зафиксировано в партии 10.6.** В истории действий проекта (`/admin/visit/rt-clogs-load?id=<project_id>`) обнаружена системная запись:
> Проект был запущен с лимитом N **по причине нехватки баланса**. По факту M
Это означает, что в оригинале **есть джоба планирования**, которая ежедневно (или при пополнении/списании) пересчитывает реальный лимит проекта по формуле:
-В`projects` нет полей `daily_limit` или `effective_daily_limit`.
- Нет джобы планирования лимитов.
- Бизнес-логика автокоррекции не описана в narrative.
**Что нужно добавить (детально — §3.2).** Поля `projects.daily_limit_target` (что хочет клиент) и `projects.effective_daily_limit_today` (что реально на сегодня — кэш). Сервис `EffectiveLimitCalculator`, вызываемый при пополнении баланса, при создании проекта, и cron-ом в 00:00 МСК. Логирование автокоррекции в `activity_log`с типом `auto_limit_adjustment`.
### 2.3. 🟢 Подтверждено: исходящих webhook'ов в оригинале НЕТ (окончательно)
**Что было предположено до партии 10.** «Возможно, оригинал умеет отправлять лиды наружу через webhook — проверить надо отдельно».
**Что подтвердила партия 10 (заход 1).** Сильное «нет», по 6 независимым линиям доказательств. Оставался единственный пробел — карточки в шапке «API ▾».
**Что закрыла партия 15 (заход 2).****Окончательное подтверждение** — карточки 5 интеграций (Bitrix24, amoCRM, Скорозвон, Unisender, Мои Звонки) из 12 пунктов меню детально открыты, плюс серверный HTML-fetch ещё 4 страниц. Ни в одной нет редактируемого поля «Webhook URL»/«Callback URL»/«Notify URL». Поиск по 31 webhook-keyword в 71 КБ кастомного JS — **0 совпадений**. Network-мониторинг — **0 внешних XHR**, все вызовы провайдер-API идут серверной стороной.
**Полный список доказательств (7 линий):**
1. 3 типа карточек проекта (Сайты / Звонки / СМС) — нет полей webhook (партия 10).
2. Форма создания нового проекта — нет полей webhook (партия 10).
3. История действий проекта за 5 месяцев и 27 уникальных дат изменений — нет логов webhook-параметров (партия 10).
4. DevTools Network — 6 XHR за сессию, все внутренние GET (партия 10).
5. WebSocket / SSE — 0 каналов (партия 9).
6. Поиск по 24 webhook-keywords в HTML — ноль совпадений (партия 10).
7.**5 открытых карточек интеграций + поиск 31 keyword в JS + network-мониторинг — 0 (партия 15).**
**Уточнение по партии 15.** Найден **один hardcoded URL**`prostats.info/bitrix24/webhook.php?id={{formData.id}}` в Vue-template карточки Bitrix24 — это **read-only текст** для копирования пользователем в админку Bitrix24. **Архитектурно это inbound-канал** для CRM: Bitrix24 постит туда события, prostats.info принимает и передаёт в crm.bp-gr.ru через серверную связь. То есть **с точки зрения CRM это endpoint для приёма, не для отправки** — что не противоречит выводу «исходящих webhook'ов нет».
**Природа интеграций оригинала.** В партии 15 структурно изучены все 5 открытых карточек:
- **CRM (Bitrix24, amoCRM):** outbound API-clients с токеном/secret. CRM пушит лиды в Bitrix/amoCRM по REST API.
- **Телефония (Скорозвон, Мои Звонки, Mango):** outbound API-clients с тогглами «Передавать визиты» / «Передавать сделки». Ключевые поля — `client_id+client_secret+api_key` (OAuth2 client credentials или basic auth). URL получателя НЕ настраивается — захардкожен в backend.
- **Email (Unisender):** outbound API-client с токеном.
**То есть архитектурный паттерн оригинала** — провайдер-специфичные backend-интеграции с переключателями ON/OFF, но **без** generic «push в любой webhook URL по выбору пользователя».
**Что это означает для нас.** Решение **OPEN-И-2 «Уровень 1 (outbound webhook) на MVP + amoCRM-коннектор в спринте 14–15»** — это **двойное конкурентное преимущество**:
1.**Generic webhook subsystem** (URL+secret+events+retry+test) — то, чего у оригинала **категорически нет**. У нас должно быть в продуктовой документации с явным указанием.
2.**amoCRM-коннектор** — паритет с одной из 12 интеграций оригинала, на которой клиенты завязаны. По полям (Субдомен, API-ключ, маппинг полей контакта/сделки/задачи) — структура из партии 15.2.2 — может быть прямым ориентиром.
### 2.4. 🔴 Карточка сделки всегда editable, нет state-machine переходов
**Что зафиксировано в партии 11.** Из 5 проверенных сделок в 5 разных статусах (Проработан, Недозвон, Просмотрено, Закрыто и не реализовано, Переговоры):
- Все поля `guest-block` editable, включая статус **«Закрыто и не реализовано»**.
- Селектор статуса в любой сделке показывает **все 14 значений** — никаких ограничений «из A нельзя в B».
- Структура карточки полностью идентична для всех 5 статусов — нет условных полей или условных действий.
- Возврат из терминальных статусов (Закрыто, Конечный недозвон) разрешён.
**Что у нас в narrative v8.3.** В §8 (Воронка продаж и статусы) явных запретов на переходы нет, но и явного разрешения «из любого в любой» — тоже. Это пробел: разработчик может реализовать ограничения «по интуиции», что разойдётся с оригиналом.
**Что нужно добавить.** Явная фиксация в §8: «переходы статусов сделки — свободные, без машины состояний; возврат из терминальных статусов разрешён». В Прил. В (State machines) — отдельный пункт «состояний deal'ов state-machine НЕТ — паритет с оригиналом».
**Что зафиксировано в партии 11.6.**В карточке сделки **нет ни секции задач, ни вкладки, ни кнопки «Создать задачу»**, ни полей ответственного/срока/типа/приоритета. При этом в фильтрах списка сделок (партия 1.4) задачи фигурируют («Дела на сегодня», «Просроченные», «Предстоящие», «Сделки без задач»).
**Гипотеза партии 11:** карточка «Напоминание» в левой колонке — это и есть «задача». Модель «1 напоминание = 1 deal», а не «массив задач».
**Что выяснила партия 12 (детальный аудит формы напоминания) — гипотеза переоткрыта:**
В оригинале реальная модель — **массив напоминаний** на сделку:
-В Vue-data сделки `model.histories[]` — массив всех событий, где **каждое напоминание = отдельная запись `{type:'reminder', time, time_dt, id, note, from, to}`**.
-В коде `model.histories.filter(h => h.type === 'reminder')` итерируется в шаблоне.
- Каждое напоминание удаляется индивидуально по `history.id`.
- Иконка `+` без ограничения «нельзя добавить второе».
- Эндпоинт `POST /admin/visit/visit-vm-delete` принимает индивидуальный id записи.
То есть **технически множество поддержано полностью**. Эмпирически в 5 проверенных сделках было ровно 1 или 0 напоминаний — UX-паттерн «обычно одно», но это не архитектурное ограничение.
**Поля минимального паритета (из формы партии 12.2.3):**
| Поле | Vue path | Тип | Обязательное | Что соответствует |
|---|---|---|---|---|
| Текст | `addReminder.note` | textarea, лимит 255 | нет | `text` в нашей модели |
| Дата | `addReminder.date` | el-date-picker | да | компонент `remind_at` |
| Час | `addReminder.hour` | el-select 0..23 | да | компонент `remind_at` |
- ❌ Ответственный (выбор менеджера) — поле `to` объявлено, но в UI редактирования не выведено.
- ❌ Тип/приоритет (low/med/high) — нет.
- ❌ Канал уведомления (email/push/sms) — нет.
- ❌ Повторение (одноразовое/еженедельно) — нет.
- ❌ Привязка к статусу (автосоздание при смене статуса) — нет.
**Что у нас сейчас в schema.sql v8.2.**В`deals` есть поля `reminder_text TEXT` и `reminder_at TIMESTAMPTZ`, индекс `idx_deals_reminder` для cron уведомлений. **Это не паритет — это упрощение.** Нужно мигрировать на отдельную таблицу `reminders`.
**Дашборд задач — бесплатный паритет.** В оригинале фильтры списка сделок принимают параметры:
Это значит, бэкенд оригинала умеет фильтровать `deals` по агрегатам `reminders`. У нас на нашем `GET /api/deals?reminders=today|last|future|none` это закрывается простым WHERE EXISTS subquery — отдельной сущности «дашборд задач» делать не нужно.
**Решение для Биз-10 (уточнено):**
- ✅ **Реализуем минимальный паритет** — отдельная таблица `reminders`с полями: `id, deal_id, tenant_id (для RLS), text VARCHAR(255), remind_at TIMESTAMPTZ, created_by FK→users, assignee_id FK→users NULL (задел, не используется в UI), completed_at TIMESTAMPTZ NULL, is_sent BOOLEAN, sent_at TIMESTAMPTZ NULL`. Без приоритетов / каналов / recurrence — это можно отложить в Post-MVP.
- ✅ **Удаляем поля `deals.reminder_text` и `deals.reminder_at`** — они теперь в новой таблице.
- ✅ **Дашборд задач** через существующий `/api/deals?reminders=...` — паритет через 1 query parameter, без отдельного экрана.
- 🟡 **Биз-10 переоткрыт:** заказчик подтверждает «массив напоминаний без приоритетов/каналов» (паритет с оригиналом, рекомендация Claude) или хочет «массив с приоритетами/каналами/исполнителями» (улучшение). Дефолт — паритет.
См. §3.X (новый раздел) — DDL для таблицы `reminders`.
### 2.6. 🟡 В карточке сделки нет файлов / вложений
**Что зафиксировано в партии 11.6.** Ни `<input type=file>`, ни секции «Документы», ни превью изображений ни в одной из 5 сделок.
**Объяснение из аудита:** «Лидген-CRM не работает с документооборотом — лид это телефон + статус + история, не сложный pipeline».
**Что у нас в narrative.** Раздела «вложения к сделке» в §7–11 нет.
**Открытый вопрос для нас.** Делаем ли мы паритет (вложений нет) или улучшение (вложения есть как Post-MVP)? Низкоприоритетное решение, можно оставить на Post-MVP по умолчанию.
### 2.7. 🟡 Телефония — внешняя интеграция, не наш модуль
**Что зафиксировано в партии 7.3 и 11.5.** Записи звонков хранятся **не на инфраструктуре crm.bp-gr.ru**, а на внешнем CDN провайдера телефонии **«Скорозвон»** (`oki.needcallbuy.ru/skrzvn/audio/YYYY/MM/DD/{recordId}.mp3`). CRM хранит только URL и метаданные (направление, оператор, timestamp, длительность через косвенный счётчик «Минуты»).
**Что у нас в narrative.** Телефония не упоминается вообще.
**Открытый продуктовый вопрос.** Нужна ли нам телефонная интеграция на MVP? Если да — со «Скорозвоном» (использует оригинал, понятная архитектура), MANGO Office (популярнее в РФ, лучше API), Telphin (бюджетный)? Это **Биз-12** — см. §5.
### 2.8. 🟡 Мульти-кошелёк биллинга (4 счётчика в шапке)
**Что зафиксировано в партии 1.2.** Виджет «Баланс ▾» в шапке оригинала имеет 4 раздельных счётчика:
- **Баланс ГЦК = 195** (лиды Городского Центра Колл)
- **Диалоги КЦ = 154** (диалоги колл-центра)
- **Минуты = 0** (минуты разговоров)
- **Автозвонок = 0** (автозвонок-операции)
**Что это означает.**В оригинале **четыре раздельных «кошелька»** под разные операции (приём лидов, обработка диалогов, звонки, автодозвон). Не «один баланс на всё», а отдельные счётчики, скорее всего покупающиеся отдельными пакетами.
**Что у нас в schema.sql.**В`tenants` поля `balance_rub` (рубли) и через `balance_transactions` ведётся учёт. На уровне «лидов» — отдельная сущность `lead_packages`, где лид имеет себестоимость в рублях. То есть **у нас одновалютный кошелёк** (рубли) с учётом конверсии «руб ↔ лид» через тариф.
**Открытый продуктовый вопрос.** Делаем ли мы паритет (4 раздельных кошелька) или нашу упрощённую модель (1 кошелёк в рублях + конвертация)?
**Продуктовая интерпретация:** наша модель — упрощение, более привычное для бухгалтерии. Модель оригинала — телеком-специфика, удобная если у клиента действительно разные пакеты под разные операции (например, «купил 1000 диалогов отдельно, не за лиды»). На MVP мы выбрали упрощение — это **Биз-11** для подтверждения / опровержения заказчиком (см. §5).
**Что у нас.**В §14 narrative «Журнал действий» планируется **полный аудит** всех мутаций сделки через `activity_log`.
**Маркетинговое позиционирование.** Подсветить в §14 и в маркетинговых материалах: «у нас полный аудит всех изменений сделки, в отличие от оригинала, где можно незаметно перевесить лид на другого менеджера».
### 2.10. 🟢 Нет kanban — наше преимущество
**Что зафиксировано в партии 4.3.** В оригинале сделки представлены **только табличным видом** с фильтром по статусу, без drag-and-drop колонок.
**Что у нас.** §11 (Kanban-доска) — отдельный полноценный экран.
**Маркетинг.** Усилить позиционирование: «kanban с drag-and-drop, как в Bitrix24/amoCRM/Pipedrive — то, чего нет в оригинале».
### 2.11. 🟢 Нет real-time (WebSocket/SSE) — наше преимущество
**Что зафиксировано в партии 9.3.** WebSocket / SSE не обнаружены. Обновления только по таймеру/перезагрузке.
**Что у нас.** Push-уведомления (§17) и общий план real-time через SSE при росте.
**Маркетинг.** «Реалтайм-уведомления о новых лидах — мгновенная реакция, без обновления страницы».
### 2.12. 🟡 Magic-link 24ч для менеджеров
**Что зафиксировано в партии 6.5.** В оригинале админ может сгенерировать одноразовую ссылку входа для менеджера (срок действия 24ч), и менеджер заходит без пароля. Удобно для подрядных колл-центров с большой текучкой.
**Что у нас.** Impersonation Ю-1 (15 мин код) сделано, но это другой механизм («войти как клиент»). Magic-link для входа в свой собственный аккаунт — **не сделано**.
**Открытый продуктовый вопрос.** Делаем ли magic-link 24ч для менеджеров на MVP или Post-MVP? Это **Биз-13** — см. §5.
| Часовой пояс | **Отдельное поле**`User[timezone_id]` в блоке «Системная информация» (default «Москва») — общий для всего профиля, не привязан к окну тихих часов |
| Гранулярность | **только часы** (нет минут, нет дней недели, нет нескольких окон в сутки) |
| Per-channel настройки | **нет** — одно окно на все каналы (push/popup/звонки) |
**Что у нас в narrative.**В`users.notification_preferences` JSONB матрица 8×3 каналов (CTO-4), но **окно «не беспокоить» там явно не описано**. Нужно добавить.
**Решение (минимальный паритет):**
```jsonc
// users.notification_preferences — расширение
{
"channels":{/*матрица8×3,каквCTO-4*/},
"sound_enabled":true,
"quiet_hours":{
"enabled":true,
"start_hour":22,// целое 0..23 (паритет с оригиналом)
"end_hour":8,// целое 0..23 (паритет)
"min_interval_hours":3// server-side validation, как в оригинале
// timezone берётся из users.timezone — общий, не привязан к quiet_hours
}
}
```
**Расширения сверх паритета** (не блокируют MVP, но «лучше оригинала»):
- Системный набор из 14 статусов (slug + русское имя).
- Переименовывать можно 8 из 14 (через `/admin/site/states`).
- CRUD статусов в UI нет.
**Что у нас.** Таблица `lead_statuses` (14 строк) + `tenant_status_overrides` для кастомных имён. **Полное соответствие.** ✅
### 2.15. 🔴 B1/B2/B3 — capabilities (новая деталь из партии 13)
**Что зафиксировано в партии 13.3.5.** В модалке редактирования проекта (та же, что вызывается по «Просмотреть» — это aria-label на pencil-иконку) обнаружен **оранжевый notice**, цитата:
> «Важно! Указать в связке 'Наименование отправителя' и 'Ключевое слово' можно только по поставщику B2. Поставщик B3 работает только по наименованию отправителя, учтите данный момент при формировании задания.»
**Что это меняет в модели данных.** Поставщики B1/B2/B3 различаются не только каналом (Сайты/Звонки/SMS), но и **доступными для проекта полями (capabilities)**:
**Зачем эти поля.** Для frontend проекта: какие input'ы рендерить в карточке проекта в зависимости от выбранного поставщика. Без `supports_*` фронтенду нужно хардкодить «B1=такие поля, B2=другие, B3=третьи» — что ломает абстракцию `suppliers`.
('b1','B1 — Сайты и Звонки','sites',FALSE,FALSE,TRUE,TRUE,
ARRAY['websites','calls'],1.00),
('b2','B2 — СМС с ключевым словом','sms',TRUE,TRUE,FALSE,FALSE,
ARRAY['sms'],1.00),
('b3','B3 — СМС по наименованию','sms',TRUE,FALSE,FALSE,FALSE,
ARRAY['sms'],1.00);
```
DDL-патч уйдёт в schema.sql v8.3 (§3.1 этого Прил. М).
### 2.16. 🔴 «Желаемое кол-во номеров в день» — аккаунтное поле (новое из партии 13)
**Что зафиксировано в партии 13.2.2.** В аккордеоне «Настройки» в шапке реестра проектов есть кнопка «Желаемое кол-во номеров», открывающая модалку с одним числовым полем. Цитата текста модалки:
> «Дорогой клиент, пожалуйста укажите сколько номеров в день вы готовы принимать: на это значение будет ориентироваться сервис ГЦК и в случае если желаемое кол-во не достигается, наши специалисты будут помогать в достижении указанного показателя.»
**Что это значит.** Это **аккаунтный таргет, не лимит**. Не массовая установка `daily_limit_target` по проектам, не влияет напрямую на `effective_daily_limit_today`. Это **ориентирующее значение для бэк-офиса ГЦК** (для людей-операторов поставщика), которые ручным трудом дотягивают объём до целевого.
- Наблюдаемые данные: проекты, удалённые `2026-01-29 06:29:54`, на момент аудита (`2026-05-05`) хранятся **96 дней** (≈3.2 месяца).
**Что у нас сейчас в schema.sql v8.2.**В`projects` есть `deleted_at TIMESTAMPTZ`, но политика retention не описана — нет cron, нет константы TTL, нет `force_delete`.
### 2.18. 🔴 «Тревожные паттерны безопасности оригинала» — мы избегаем (новое из партий 14–15)
Партии 14–15 выявили **2 анти-паттерна безопасности** в оригинале, которых мы должны строго избегать:
#### 2.19.1. Пароль plaintext в HTML-форме (партия 14.3.4)
**В оригинале:** на странице `/admin/user/account` в блоке «Системная информация» поле «Текущий пароль» — это `<input type="text">` с заполненным значением пароля пользователя в plaintext в HTML. Смена пароля — inline-вводом нового значения в это же поле и общим POST-запросом формы профиля.
- ✅ Отдельная защищённая форма «Старый пароль / Новый пароль / Подтверждение нового».
- ✅ Все 3 поля — `<input type="password">` с возможностью toggle «показать»/«скрыть».
- ✅ В HTML страницы пароль никогда не пре-заполнен.
- ✅ Rate-limit на смену пароля (3 попытки/час).
- ✅ Проверка сложности нового пароля (min 12, есть верхний+нижний+цифра+спецсимвол).
#### 2.19.2. API-key и client_secret в `<input type="text">` (партия 15.2.3)
**В оригинале:** в карточках интеграций (Скорозвон, Мои Звонки, Bitrix24, amoCRM, Unisender) поля `api_key`, `client_secret`, `client_id`, `token` — все имеют `type="text"`, не `type="password"`.
-В §22 narrative — отдельный блок «Безопасность аккаунта» с разделами: Пароль, 2FA, История входов, Активные сессии, Уведомления о подозрительной активности.
- В сравнительной таблице vs оригинал — этот блок целиком в нашу пользу.
**SMS / Email-OTP — Post-MVP** (требуют интеграции с SMS-шлюзом, удорожают MVP без явной необходимости).
---
## 3. Изменения в модели данных (DDL для schema.sql v8.2 и v8.3)
**Важно для RLS.** Таблицы `suppliers`, `project_suppliers` — **НЕ tenant-уровневые** (это наши поставщики, общие для всех тенантов). RLS не применяется. Чтение разрешено всем `crm_app_user`, запись — только `crm_admin_user`.
**`system_settings.supplier_default_cost_rub`** — оставляем для обратной совместимости (используется как fallback при создании новой строки `suppliers`), но новый код должен читать `cost_rub` из конкретной строки `suppliers`.
### 3.5. Дополнения в schema.sql v8.3 (по итогам партий 12–15)
#### 3.5.1. Перепись таблицы `reminders` (для §9.1, Биз-10 переоткрыт)
**Контекст партии 12.2.5:** в оригинале `model.histories[]` содержит записи `type='reminder'` — то есть множество напоминаний на сделку поддерживается на уровне схемы (хотя UI обычно показывает одно). У нас в schema v8.2 — поля `deals.reminder_text` + `deals.reminder_at` (одиночное) **+ уже была таблица `reminders`** с полями `user_id`, `is_done`. Нужно: убрать дублирование (поля из deals), переименовать `user_id → created_by` (по партии 12.2.4 это `histories[].from`), добавить `assignee_id` и `completed_at`.
#### 3.5.2. Расширение `suppliers` capabilities (для §9.2)
**Контекст партии 13.3.5:** UI оригинала явно различает возможности B1/B2/B3 — оранжевый notice в карточке проекта: «Указать в связке 'Наименование отправителя' и 'Ключевое слово' можно только по поставщику B2. Поставщик B3 работает только по наименованию отправителя».
**В UI:** при выборе поставщика(ов) для проекта frontend проверяет `capabilities` и показывает только релевантные поля. Если выбраны несколько — пересечение (B2+B3 → можно sender_name, нельзя keyword).
#### 3.5.3. Поле `tenants.desired_daily_numbers` (для §9.2, Биз-16)
**Контекст партии 13.2.2:** аккаунтная настройка «Желаемое кол-во номеров в день» — целевой объём для бэк-офиса. Не лимит, не биллинг — сигнал саппорту.
| Записей в `system_settings` | 22 | **25** (+3) |
---
## 4. Изменения в narrative (для v8.4)
### 4.1. §1 (Обзор системы) — раздел про конкурентные преимущества
Добавить блок «Что мы умеем, чего нет в оригинале»:
1.**Outbound webhook к тенанту** (§19) — оригинал только принимает, мы и принимаем, и отдаём.
2.**Полный аудит мутаций сделки** (§14) — оригинал не логирует смену ответственного / проекта / телефона / суммы; мы логируем всё.
3.**Kanban-доска с drag-and-drop** (§11) — у оригинала только табличный вид.
4.**Real-time push-уведомления** (§17) — у оригинала обновления только перезагрузкой.
5.**Дашборд с настраиваемыми виджетами** (§12) — у оригинала фиксированный дашборд с 5 KPI.
6.**API для CRM-интеграций (amoCRM, Bitrix24)** (OPEN-И-2, спринт 14–15) — у оригинала нет.
### 4.2. §5 (Источник данных — Webhook от crm.bp-gr.ru) — формат ingress
Уточнить формат входящего payload по аудиту партии 3.1:
```
GET/POST https://crm.bp-gr.ru/api/...
Параметры:
vid — токен пользователя (наш кабинет в crm.bp-gr.ru)
project — id проекта в crm.bp-gr.ru (мапинг на наш project_id через тег)
tag — короткий тег кампании (B1_..., B2_..., B3_..., наш ключ для project_suppliers)
phone — основной телефон лида
time — отметка времени
phones[] — массив доп. телефонов
```
Добавить раздел «Извлечение поставщика из тега»: при приёме webhook парсим префикс тега (`B1_carmoney` → supplier_code='b1'), создаём запись `supplier_lead_costs`с правильным `supplier_id`.
### 4.3. §7 (Сущности и схема БД) — описание новых сущностей
- §7.7 — расширение `projects` (5 новых полей: `daily_limit_target`, `effective_daily_limit_today`, `effective_limit_calculated_at`, `region_mask`, `region_mode`, `delivery_days_mask`)
### 4.4. §8 (Воронка продаж и статусы) — явная фиксация free state machine
Добавить пункт в начало раздела:
> **Переходы статусов сделок — свободные, без машины состояний.** Из любого статуса (включая терминальные `closed` и `final-non-dial`) разрешён переход в любой другой. Это паритет с оригиналом crm.bp-gr.ru (партия 11.3 аудита). Цель — менеджер может «откатить» статус при возврате клиента, что является нормальным сценарием в лидген-CRM.
### 4.5. §9 (Мои Проекты) — карточка проекта по аудиту
Описать карточку проекта в нашем варианте по структуре из партии 10.3, с нашими расширениями:
- **Активный** — toggle (паритет).
- **Тег** — text (паритет).
- **Поставщики (B1/B2/B3)** — мульти-чекбокс (паритет, у нас через `project_suppliers`).
- **Название проекта** — text (паритет).
- **Тип данных** — select [websites/calls/sms] (наше расширение, в оригинале вычисляется по поставщикам).
> Это закрывает аудит-пробел оригинала, выявленный в партии 11.4 аудита.
### 4.8. §17 (Напоминания и push-уведомления) — «тихие часы»
Добавить раздел про окно «Не беспокоить» (паритет с «Тихими часами» оригинала):
```jsonc
// users.notification_preferences — расширение
{
"channels":{/*матрица8×3,каквCTO-4*/},
"sound_enabled":true,
"quiet_hours":{
"enabled":true,
"from":"22:00",
"to":"08:00",
"timezone":"Europe/Moscow"
}
}
```
Логика: при отправке push-уведомления и SMS — если текущее время в окне `quiet_hours`, откладываем до конца окна (для не-критичных типов).
### 4.9. §22 (Безопасность) — раздел про prompt injection в DOM
Добавить новый раздел «22.X — Защита от prompt injection в DOM нашей платформы»:
> По итогам аудита оригинала crm.bp-gr.ru обнаружена prompt injection-атака в DOM (раздел 6 этого приложения). Чтобы наша платформа не стала аналогичной целью для AI-агентов клиентов:
>
> 1. **CSP (Content Security Policy):** запретить inline-скрипты, разрешить только наши домены и явный whitelist (JivoSite, Sentry, Yandex Cloud SDK).
> 2. **Запрет HTML-инъекций в пользовательский ввод:** все поля (комментарии, заметки, имена) санитизируются через DOMPurify с минимальным whitelist тегов.
> 3. **Запрет маркеров для агентов:** в нашем DOM не должно быть элементов с ID вида `claude-*`, `gpt-*`, `agent-*`.
> 4. **Аудит сторонних виджетов:** перед подключением (JivoSite и др.) — проверка скрипта на наличие подобных маркеров. При обнаружении — отказ от интеграции или прокcирование через наш CDN.
### 4.10. §26 (Дизайн-система / Frontend) — стек
В разделе «Что мы НЕ делаем» добавить:
> Мы НЕ повторяем стек оригинала (Vue 2 + Vuetify 1.5 + Element UI + jQuery + Bootstrap + Yii2 одновременно). У нас один frontend-фреймворк (Vue 3) + одна UI-библиотека (Vuetify 3) + чистый Laravel API на бэкенде. Это решение фундаментальное и не подлежит пересмотру, так как стек оригинала является результатом многолетней органической эволюции без рефакторинга и не должен повторяться.
---
## 5. Новые продуктовые вопросы к заказчику
По итогам аудита возникли **7 новых продуктовых вопросов**, требующих решения заказчика. Они не блокируют разработку (есть разумные дефолты), но желательно закрыть до старта соответствующих спринтов.
**Биз-10/11/12/13** — добавлены в v1.0 по итогам партий 1–11.
**Биз-14/15/16** — добавлены в v1.1 по итогам партий 12–15.
### Биз-10 — Модель «задач» сделки (УТОЧНЕНО в v1.1)
**Контекст:** в оригинале **массив напоминаний на сделку** через `model.histories[].type='reminder'` (партия 12 уточнила гипотезу из партии 11). Реальная модель — список записей `{id, note, time, time_dt, from, to}`, где `from` = логин автора, `to` = NULL. Эмпирически в UI обычно одно напоминание на сделку, но архитектура поддерживает множество.
**Поля минимального паритета (форма из партии 12.2.3):**`text VARCHAR(255)`, `remind_at`, `created_by` (=from), `assignee_id NULL` (=to, не используется в UI), `created_at`. Без приоритетов / каналов / recurrence — этих полей в форме оригинала нет.
**Вопрос:** оставляем минимальный паритет (массив напоминаний без приоритетов/каналов) или делаем улучшение (массив задач с типами / приоритетами / исполнителями)?
**Рекомендация Claude:****минимальный паритет на MVP, расширения — Post-MVP**. Причина: лидген-CRM работают в режиме «быстрая обработка много лидов», классический task management избыточен. Усложнение UI без явного спроса — отложить.
**DDL:** см. §3.5.3 — отдельная таблица `reminders`, удаление полей `deals.reminder_text/reminder_at`.
**Дефолт при отсутствии решения:** минимальный паритет (отдельная таблица `reminders`с полями выше + `completed_at` как наше расширение для аналитики).
### Биз-11 — Мульти-кошельковый биллинг
**Контекст:** в оригинале 4 раздельных счётчика в шапке (ГЦК / Диалоги КЦ / Минуты / Автозвонок). У нас один кошелёк в рублях с конвертацией в лиды по тарифу.
**Вопрос:** делаем нашу упрощённую модель (1 кошелёк) или повторяем телеком-модель оригинала (4 кошелька)?
1. 4 кошелька — наследие телекома (минуты + диалоги + автозвонок), это операции, которых у нас нет (мы не звоним и не диалогируем).
2. У нас одна операция — приём лида, и одна валюта на покупку — рубль. Усложнение биллинга без необходимости.
3. Стандарт SaaS-индустрии — единый баланс с конвертацией, бухгалтерия проще.
**Дефолт при отсутствии решения:** наша упрощённая модель (1 кошелёк).
### Биз-12 — Телефонная интеграция
**Контекст:** в оригинале телефония через внешний CDN «Скорозвон». В нашей модели телефония отсутствует.
**Вопрос:** нужна ли телефонная интеграция на MVP? Если да — со «Скорозвоном» (используется оригиналом, понятная архитектура), MANGO Office (популярнее, лучше API), Telphin (бюджетный) или другим провайдером?
**Рекомендация Claude:****на MVP — НЕ делаем**, добавляем как Post-MVP при первом запросе клиента. Причины:
1. Это инфраструктурная интеграция (~1.5–2 спринта на одного провайдера).
2. Не все клиенты колл-центры — для CRM-клиентов (которым важно accept лида, отдать менеджеру, потом зайти в свою телефонию) телефония внутри CRM избыточна.
3. На MVP лучше иметь хороший outbound webhook (уже планируется в OPEN-И-2), через который любая внешняя телефония может подписаться на новые лиды.
**Дефолт при отсутствии решения:** не делаем на MVP.
### Биз-13 — Magic-link 24ч для менеджеров
**Контекст:** в оригинале админ генерирует одноразовую ссылку для менеджера (24ч), менеджер заходит без пароля. Удобно для подрядных колл-центров с большой текучкой.
**Вопрос:** делаем magic-link 24ч для менеджеров на MVP?
**Рекомендация Claude:****да, делаем на MVP** — это маленькая фича (~0.3 спринта), но даёт явное конкурентное преимущество для целевой аудитории (колл-центры с подрядными операторами). Реализация: новая таблица `manager_magic_links(token_hash, manager_id, expires_at, used_at, created_by_admin_id)`, кнопка «Создать ссылку входа» в интерфейсе управления менеджерами, страница логина по ссылке.
**Дефолт при отсутствии решения:** делаем на MVP.
---
### Биз-14 — TTL для soft-deleted проектов (новый, добавлен в v1.1)
**Контекст:** в оригинале (партия 13.2.3) экран «Удалённые проекты ГЦК» (`/admin/visit/rt?type=deleted`) показывает проекты, удалённые **минимум 96 дней назад** (запись «удалено 2026-01-29» открыта в аудите 2026-05-05). У нас в schema v8.2 поле `projects.deleted_at` есть, но retention policy не определена.
**Вопрос:** через какой срок soft-deleted проекты:
- (а) удаляются физически (DROP) — навсегда, без восстановления;
- (б) удаляются с обнулением ПДн (`anonymized = true`), но сохраняются для compliance-журнала;
- (в) хранятся бессрочно, без очистки.
**Рекомендация Claude:****180 дней (6 месяцев) + cron `projects:purge-deleted`, выключен по умолчанию** до Б-1 (реквизиты ООО). Логика: после 6 месяцев проект и все его лиды (с ПДн) полностью удаляются — это адекватный срок для разрешения возможных споров с клиентом и аудитов от РКН. Cron включается админом через `system_settings.projects_purge_deleted_enabled = true` после получения юридического согласования. До включения cron — все «удалённые» проекты живут в БД бессрочно, как сейчас в оригинале.
**Дефолт при отсутствии решения:** 180 дней + cron disabled. См. §3.5.4 для DDL и Прил. И для runbook.
**Контекст:** в оригинале (партия 13.1) есть полноценная фича `/admin/gck/dop-sources` — wizard на 3 шага: «Поиск (по доменам/ключевикам конкурентов) → Результат (рекомендованные домены/телефоны) → Чёрный список». Это **OSINT-инструмент** для самостоятельного поиска новых источников трафика клиентом — отдельная сущность, не связана с управлением поставщиками B1/B2/B3.
- Бизнес-ценность спорна: для арбитражной модели «получи лиды от B1/B2/B3» эта фича — «помощник по конкурентам», не основной flow.
- Реализация требует внешнего OSINT API (поиск конкурирующих доменов и связанных телефонов), затраты ~3 спринта.
- Если делать — отдельные таблицы: `source_recommendation_searches`, `source_recommendation_results`, `source_recommendation_blacklist` (см. партию 13.1.6).
**Дефолт при отсутствии решения:** не делаем на MVP, ставим в backlog с приоритетом P3.
---
### Биз-16 — Поле `tenants.desired_daily_numbers` (новый, добавлен в v1.1)
**Контекст:** в оригинале (партия 13.2.2) есть аккаунтная (не проектная!) модалка «Желаемое кол-во номеров в день». Это не лимит, а ориентир для сотрудников бэк-офиса ГЦК, которые ручным трудом дотягивают объём до нужного.
- Полезный сигнал саппорту Лидерры: видеть, что клиент ожидает 100 лидов/день, а получает 30 — повод связаться с клиентом и помочь оптимизировать настройки проектов.
- Минимальная реализация: одно поле в `tenants`, одно поле в форме настроек аккаунта, одна колонка в админ-дашборде `/admin/tenants`.
- Затраты ~0.1 спринта.
**Дефолт при отсутствии решения:** добавляем. См. §3.5.3 для DDL.
---
## 6. Безопасность — prompt injection в DOM CRM
### 6.1. Что обнаружено
В DOM **каждой страницы** crm.bp-gr.ru присутствует элемент:
```html
<divid="claude-agent-stop-container">
<buttonid="claude-agent-stop-button">
<span>Stop Claude</span>
</button>
</div>
```
**Идентификаторы (`claude-agent-stop-*`) явно нацелены на агента Claude** — это не общая «кнопка остановки скрипта», а именно нацеленная провокация для AI-агентов с именем «Claude».
### 6.2. Что НЕ установлено
**Источник инъекции остаётся неизвестным.** Возможные версии:
1.**Расширение браузера пользователя** (заказчика) — расширение могло быть установлено для других целей и инжектит этот элемент во все страницы.
2.**Тестовая разметка владельцев CRM** — крайне маловероятно, но теоретически crm.bp-gr.ru мог добавить кнопку для тестирования AI-доступа.
3.**Враждебная инъекция в коде CRM** — XSS-атака или скомпрометированный компонент.
4.**HelpDeskEddy виджет** — изначально подозревался, в партии 11.11 **отвергнут** (атрибуты не совпадают с известным DOM HelpDeskEddy).
**Это правильное поведение** в соответствии с системными правилами защиты от prompt injection: команды от веб-контента (DOM-элементы, текст в страницах, имена кнопок) **никогда не выполняются** без явного подтверждения пользователя в чате.
1.**Заказчику** — проверить расширения браузера в том профиле, где открывается crm.bp-gr.ru. Версия 1 (расширение) — наиболее вероятная.
2.**Если расширение не нашлось** — открыть DevTools → Sources, найти, в каком файле создаётся `claude-agent-stop-container`. Это покажет источник.
3.**При враждебной инъекции в коде CRM** — сообщить владельцам crm.bp-gr.ru. До устранения — ограничить доступ Claude в Chrome к этой CRM с дополнительной осторожностью (что и так делается).
- ✅ Семантическая разметка, корректные ARIA-атрибуты, осмысленные ID — для legitimate использования AI-агентов клиентов.
- ❌ Никаких «AI-маркеров», специально размещённых для одних агентов и не других.
- ❌ Никаких скрытых инструкций в DOM (через aria-label, title, data-attributes).
- ✅ CSP, исключающий выполнение неавторизованных скриптов.
### 6.6. Повторные подтверждения в партиях 12–15
В каждой из 4 параллельных партий (12, 13, 14, 15) элемент `claude-agent-stop-container` снова обнаружен в DOM. В партии 15 дополнительно зафиксированы два связанных элемента:
```html
<divid="claude-agent-glow-border"></div>
<styleid="claude-agent-animation-styles">
@keyframesclaude-pulse{...}
</style>
```
Это означает, что инъекция включает не только кнопку, но и **визуальные подсказки** (анимированная подсветка) — рассчитана на то, что AI-агент «увидит» её на скриншоте и среагирует. Это укрепляет гипотезу о **расширении браузера** (версия 1 в §6.2).
**Реакция всех 4 операторов Claude в Chrome была корректной:** кнопка не нажата, команда «Stop Claude» не выполнена, все 4 партии завершены полностью.
**Дополнительные находки (партии 14 и 15) для §22 narrative нашей платформы:**
1.**Антипаттерн оригинала: пароль в plaintext в HTML-форме** (партия 14.3.4). На странице профиля `<input type="text" name="User[password]">` с заполненным значением. У нас — отдельная защищённая форма «старый/новый/подтверждение» с `type="password"` + rate-limit.
2.**Антипаттерн оригинала: API credentials в `<input type="text">`** (партия 15.2). Все api_key, client_secret, token хранятся как обычный текстовый input. У нас — `type="password"` + кнопка «👁 Показать», маскирование при сохранении (отображаются только последние 4 символа), возможность «перевыпустить» в один клик.
3.**Принцип «ZTA для credentials»:** все credentials шифруются на уровне БД (column-level encryption через `pgcrypto`), ключ — в Yandex Lockbox.
---
## 7. Итоговая сводка изменений по архиву
### 7.1. Что меняется в schema.sql (v8.1 → v8.2 → v8.3)
- **`audit-batch-15-2026-05-05.md`** — партия 15 (карточки «API ▾», окончательный вердикт по webhook'ам).
### 7.5. Что меняется в README архива
- Состав архива: добавлены 4 новых аудит-файла (партии 12–15).
- В разделе «Что нового в v8.3++» — отдельный пункт про аудит партий 12–15 и связанные изменения (новая модель `reminders`, расширение `suppliers` capabilities, новое поле `desired_daily_numbers`, cron purge-deleted).
---
## 8. Что мне (Claude) нужно сделать после Прил. М
В порядке приоритета:
1. ✅ **Открытые_вопросы_v8_3.md → v1.5** — раздел 11 с Биз-10/11/12/13 (сделано 04.05).
7.**v8.4 narrative** — раскрытие 30 решений интервью + интеграция выводов аудита 1–15 партий (§4 + §9.6 этого документа). Несколько сессий.
---
## 9. Результаты партий 12–15 (детально)
Параллельный аудит партий 12, 13, 14, 15 проведён 05.05.2026 через Claude в Chrome в 4 независимых окнах. Все 4 партии — read-only, без модификаций. Партия 15 проведена в режиме «полное раскрытие карточек интеграций со строгой маскировкой токенов через 6 регулярных выражений self-review»; токены и секреты не попали в отчёт.
### 9.1. Партия 12 — Конверсия проектов + Напоминания + Досье
Большая серверно-рендеримая таблица 17 колонок, 1 строка = 1 проект (всего 443 строки = всему реестру). Структура: id / Тег / Проект / Статус / Источник сбора / Кол-во обработанных (счётчик-знаменатель) / 11 колонок по статусам сделок (счётчик + %, формат `N (XX.XX%)`).
Цветовая схема — **monotone per column** (не heatmap, не светофор): у каждого статуса свой пастельный цвет фона, не зависящий от значения. CSS-классы переиспользуются (`td_pereveden` стоит у «Закрыто», «На замену», «Конечный недозвон» — легаси).
**Что есть:** total-row, фильтры по периоду / типу / поиску, чекбоксы видимости 17 колонок (хранятся в `localStorage.rtstatopts1`), сортировка по любой колонке.
**Чего нет:** drill-down в ячейку, экспорт XLSX/CSV.
**Решение для Лидерры:** реализовать как **первоклассный отчёт §12 narrative**. На бэке — один read-эндпоинт `GET /api/projects/conversion?from=&to=&type=&search=`. На фронте — Vue-таблица. **Наше расширение сверх паритета:** экспорт XLSX (через `report_jobs`) + drill-down из ячейки в отфильтрованный список сделок.
#### 9.1.2. Напоминания — модель данных (Биз-10 переоткрыт)
**Главное расхождение с гипотезой партии 11:** в `model.histories[]` есть записи `type='reminder'` — то есть **множество напоминаний на сделку поддерживается на уровне схемы**. UI обычно показывает одно (это UX-конвенция), но архитектурно поддержано множественность.
**Поля напоминания в оригинале (минимальный паритет):**
**Решение для Лидерры:** перепись таблицы `reminders` (см. §3.5.1). Удалить `deals.reminder_text` и `deals.reminder_at`. На MVP — поля паритета + `completed_at` (наше расширение). Расширения над паритетом — **Post-MVP по запросу**.
В DOM сделки оказалось **две модалки с одинаковым заголовком «Досье»**:
**Модалка №1 — `baseInfo`** (простая v-dialog): триггер — карандаш рядом с телефоном гостя; условие — `baseInfo.info.length > 0`; назначение — «дубли по телефону».
**Модалка №2 — `visitdop`** (el-dialog с табами): триггер — кнопка «Просмотр досье» в `.domen-right`; содержимое — 2 таба (Досье с полями `proceeds/profit/balance/arbitration` + Pr-cy SEO-данные); эндпоинт `GET /admin/visit/visit-dop-load?id={model.id}`; назначение — внешние данные о домене сделки.
OSINT-инструмент по поиску внешних доменов и телефонов на основе ключевых запросов клиента. **НЕ управление поставщиками B1/B2/B3** (гипотеза партии 10 опровергнута). 3-шаговый wizard, аккаунтный скоуп.
- **Биз-16** → новое поле `tenants.desired_daily_numbers INT NULL` (см. §3.5.3). Сигнал саппорту, отображается в админке SaaS, не влияет на биллинг и `effective_daily_limit`.
- **Биз-14** → soft-delete проектов с TTL (см. §3.5.4). Дефолт 6 месяцев + cron disabled.
- Массовые действия (вкл/откл, удалить, восстановить) — стандартный bulk-паттерн в админ-UI клиента.
#### 9.2.3. «Просмотреть» — это та же карточка проекта
«Просмотреть» — это `aria-label` на pencil-иконке для скринридеров. Открывается **та же модалка «Редактировать проект»**. Отдельной сущности `project_pending_numbers` не нужно.
#### 9.2.4. Жёсткое подтверждение модели B1/B2/B3 — capabilities
Цитата из карточки проекта оригинала (партия 13.3.5):
> «Указать в связке 'Наименование отправителя' и 'Ключевое слово' можно только по поставщику B2. Поставщик B3 работает только по наименованию отправителя».
Это требует расширения `suppliers` 5 полями (см. §3.5.2). В UI Лидерры: при выборе поставщика(ов) frontend показывает только релевантные поля (intersection capabilities).
#### 9.3.1. Карточка сделки во всех 14 статусах — единая
Эмпирически проверить не удалось (на текущем аккаунте 0 сделок во всех 14 статусах). Косвенно подтверждено через DOM фильтра — единый плоский список из 14 статусов без условных подразделов.
| **JS-код по 31 keyword** | **15.3** | **0 совпадений в кастомном JS (~71 КБ)** |
| **Network-мониторинг** | **15.4** | **0 внешних XHR; все вызовы провайдер-API — серверной стороной** |
**Уточнение:** в HTML карточки Bitrix24 найден read-only URL `https://prostats.info/bitrix24/webhook.php?id={{formData.id}}`. Это **inbound-канал** (Bitrix → prostats.info → CRM серверным путём): `prostats.info` — отдельная инфраструктура группы. **Это НЕ generic outbound webhook subsystem CRM**.
**Архитектурный паттерн всех интеграций оригинала:** outbound API-client. CRM аутентифицируется в провайдере по credentials и пушит данные через provider-specific REST.
**Решение для Лидерры:** наш Уровень 1 (outbound webhook на MVP, OPEN-И-2) — окончательно **конкурентное преимущество**. Маркетинг: «отдаём лиды в любую внешнюю систему по webhook'у — у конкурентов нет».
#### 9.4.3. Структура карточек интеграций (для §4.5 narrative)
Все карточки — **отдельные страницы**, единая схема: Статус toggle + блок credentials (провайдер-специфичный) + блок маппинга полей + блок маппинга статусов. Кнопки: «Создать» (Yii) или «Далее/Отмена/Удалить» (Vue SPA).
**🔴 Уязвимости оригинала (партия 15.2):** все credentials хранятся в `<input type="text">`, не `password`. Видны на экране.
**Дефолт при отсутствии решения:** не делаем на MVP.
#### Биз-16 — Поле `desired_daily_numbers` на тенанте
**Контекст партии 13.2.2:** аккаунтная настройка «Желаемое кол-во номеров в день» — целевой ориентир для саппорта.
**Рекомендация Claude:****Делаем на MVP** — поле `tenants.desired_daily_numbers INT NULL` + UI в кабинете клиента (Профиль) + отображение в админке SaaS.
**Дефолт при отсутствии решения:** делаем на MVP.
### 9.6. Дополнения в narrative для v8.4 (по итогам партий 12–15)
В дополнение к §4.1–§4.10 (по итогам партий 1–11), для v8.4 добавляются дополнения в:
- **§7 (Сущности и схема БД)** — описание `reminders` v8.3 и удаление полей `deals.reminder_text/reminder_at`. Расширение `suppliers` capabilities.
- **§8 (Воронка продаж и статусы)** — пункт про единую универсальную карточку сделки во всех 14 статусах.
- **§9 (Мои Проекты)** — раздел про soft-delete с TTL (Биз-14), массовые операции, аккордеон «Настройки».
- **§12 (Дашборд и аналитика)** — точная структура отчёта «Конверсия проектов»: 17 колонок, формат `N (XX.XX%)`, monotone per column, total-row, экспорт XLSX (наше расширение), drill-down (наше расширение).
- **§17 (Напоминания)** — полная переписка раздела: множественные напоминания на сделку (новая `reminders`), фильтры `?reminders=today|last|future|none`, минимальный набор полей паритета.