Лиды не идут с 3 июня = намеренное решение владельца он сам выключил проекты подтверждено 23.06 не дефект. Развёрнута вчерашняя поправка ушедшая в крайность реальный инцидент. Новый файл-разбор 2026-06-23-LEADS-STOPPED-correction.md с доказательствами прода read-only SSH плюс баннеры во всех связанных доках. Телефон-источник замаскирован. Приёмка раунд-2 сверена живьём все 5 закрыты M-1 M-2 apiv1-rate на проде hash 21 schema v8.51 Раздел B тесты. Долги докалки проверены 23.06 всё ещё открыты deptrac 1 живое нарушение ProjectResource SupplierSnapshotGuard larastan env-квирк диагностируемость молчаливого дропа no_snapshot_skipped всё ещё Log info. Только docs прод и код не тронуты. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
21 KiB
Результат приёмочного теста боевого liderra.ru — 22.06.2026
Прогон: 22.06.2026, ~05:46–07:30 МСК, на боевом liderra.ru. Кодовая фраза стены: «роутер-наставник».
Исполнитель: сессия-исполнитель по 2026-06-21-acceptance-EXECUTOR-LAUNCH-prompt.md.
Метод: провижининг тест-популяции (SQL + портал), контролируемые инъекции вебхука с прод-сервера (vid TEST-диапазон 9e12..9.99e12), сверка в боевой БД (read-only), живой онбординг владельцем (реальная капча + код из письма), нагрузка с safety-стопом, полный teardown.
🟢 ВЕРДИКТ: GO
Деньги сходятся копейка-в-копейку, изоляция без утечек, аудит-цепочки зелёные после уборки, ядро (раздача→списание→сделка→изоляция) проходит сквозь на боевом, критдефектов нет. Омега (tenant 2, «Компания 1») за весь прогон не тронута: 1 835 400.00 ₽ / 1013 сделок — до и после. UX/мелкие находки GO не блокируют (список ниже).
Сводка по пунктам
| Этап / пункт | Статус | Заметка |
|---|---|---|
| R1 провижининг (7 клиентов / 16 проектов / 14 rt у поставщика) | PASS | rt созданы у crm.bp-gr.ru (реальные external_id, sync ok) |
| R2 инъекционный стенд (секрет/allowlist/слепок/вебхук) | PASS | инъекция с прод-сервера 127.0.0.1, vid TEST-диапазон |
| R3 CAP=3 (раздача ≤3 разным) | PASS | московский лид → ровно 3 региона-82 |
| R3 идемпотентность (повтор vid) | PASS | already_processed, без новых сделок/списаний |
| R3 лимит под локом + перелив фаза-2 | PASS | лимит 10 не пробит; перелив на всяРФ-клиента |
| R3 M-DOUBLEPROJ (клиент с 2 проектами) | PASS | 12 лидов = 12 списаний (не 24); субдомен→корень |
| R3 каскад фаза-1/2/3 + подмена | PASS | точный→всяРФ→подмена; город = РЕАЛЬНЫЙ (Краснодарский край) при subject_code=82 |
| R3 DIRECT (bare project) | PASS | platform=DIRECT, матч по типу+идентификатору |
| R3 M1 тройная запись + копейки | PASS | lead_charges+balance_transactions+supplier_lead_costs согласованы |
| R3 M-TIER (тарифная ступень) | PASS | 101-й лид → T2 65₽ (живой тариф сменился сегодня) |
| R3 M-INSUFF (нехватка → откат+автопауза) | PASS | баланс 50₽: 0 сделок, 0 списаний, проект на автопаузе |
| R3 изоляция (портал 404 + RLS-роль + негатив) | PASS | C1 не видит C2 (404); crm_app_user контекст 17 → 0 чужих |
| R3b онбординг (живая капча + код из письма) | PASS | tenant 24: pending→active, баланс 0→300₽; реальная Yandex-капча решена владельцем |
| R3b гейт реквизитов | PASS | новый аккаунт без реквизитов → проект создать нельзя |
| R3b колокольчик + дайджест | PASS | 266 in-app; дайджест-письмо ~250 сделок на kdv1@bk.ru — доставлено и подтверждено владельцем |
| R3b импорт CSV (не списывает) | PASS | 2 сделки, 0 списаний, баланс не дрогнул; нулевая historical_import строка |
| R3b отчёты (build + изоляция) | PASS | deals_export done, привязан к tenant |
| R3b G6 публичный API (изоляция + 401) | PASS | по ключу только свои сделки; без/битый ключ → 401; rate-limit 120/мин есть |
| R3b G7-A поддержка | PASS | заявка создана (RLS-scoped) |
| R3b G7-B impersonation | SKIP (код-верифик.) | админ-сайд, зона M-1; вживую не гонялся |
| R4 нагрузка/ёмкость | PASS | ~258 лид/мин (≈37× к цели 7/мин); память ~1.35ГБ своб; деньги под нагрузкой точны |
| R5 F1 деньги (копейки) | PASS | у всех тест-клиентов баланс = старт − Σсписаний, точно |
| R5 F2 изоляция финал | PASS | 0 утечек |
| R5 F3 аудит (после teardown) | PASS | 79 цепочек intact, 0 mismatch |
| R5 teardown (rt→слепки→тенанты→Redis) | PASS | до 21:00; 0 TEST-остатков; омега цела |
| R3 all-pairs 27 (полная матрица) | PARTIAL | покрыты представители всех осей (S×R×B×Lim×D) реальными инъекциями; полный перебор 27 пере-слепков — не гонялся (выборочно) |
| Часть F (CsvReconcile, freeze-sweep, D-PAUSE, D-MERGE, T-* UI edge) | PARTIAL/SKIP | ядро инвариантов покрыто; отдельные edge-карточки не гонялись вживую |
Деньги (F1)
- Каждый тест-клиент:
balance_rub == 100000 − Σ(lead_charges)/100— сходится точно (reconciles=t). - Пример: C1 — 16 списаний = 1110₽ → баланс 98890 (до импорта); под нагрузкой 200 лидов C1/C2/C6 −12990/−14000 — точно по ступеням.
- Тройная запись согласована; копейки (bcmath) не теряются; откат при нехватке — атомарный.
Изоляция (F2)
- Портал: чужая сделка → 404; свой счётчик = БД.
- RLS-роль
crm_app_userпод контекстом 17: свои 12, чужих 0. - Публичный API: по ключу только свой тенант.
- 0 утечек.
Аудит (F3)
- После teardown
audit:verify-chains— зелёный (79 intact, 0 mismatch), омега и глобальные цепочки целы.
Ёмкость (слой D)
- Один воркер @ 2 ГБ / 2 vCPU: ~258 лид/мин ≈ 370k/сутки теоретически против цели 10k/сутки → ~37× запас.
- Память под нагрузкой стабильна (~1.35 ГБ свободно), swap не тронут, SAFETY-стоп не понадобился.
- Узкое место на этом масштабе не достигнуто. Железа на старте хватает с большим запасом.
- ⚠️ Замер представительный (250 лидов), не полный ramp до 3000 — но насыщение явно >> 10k/сут.
Маржа (F4)
- Не считалась отдельно (тест-баланс; себестоимость поставщику в инъекции = справочная). Опускаю как информативную.
Находки (НЕ блокеры GO)
| ID | Важность | Суть |
|---|---|---|
| FN-RESET | 🔴 клиент-видимый, чинить ДО продажи | Сброс пароля полностью сломан. forgotPassword → Password::sendResetLink → дефолтное Laravel-уведомление строит URL через route('password.reset'), которого НЕТ (SPA-роут сброса = /reset/:token, имя reset-password). Итог: Route [password.reset] not defined → клиент видит «Произошла ошибка», письмо со ссылкой не уходит никому. Побочно ломает анти-перебор (существующий email → ошибка, несуществующий → нейтральный ответ = enumeration-вектор). Найдено владельцем post-run на info@lkomega.ru. Фикс: ResetPassword::createUrlUsing(...) в AppServiceProvider::boot() → ссылка на /reset/{token}?email=.... Не money/isolation → формальный вердикт GO не меняет, но обязателен до передачи продажникам (клиент не восстановит пароль). ✅ ИСПРАВЛЕНО в коде (подтверждено app/app/Providers/AppServiceProvider.php:88-92, проверка 22.06.2026), ✅ НА ПРОДЕ (проверено 22.06.2026). |
| FN-AUDIT | для разработчика | Во время прогона verify-chains краснеет на свежих app-строках (balance_transactions/activity_log/pd_processing_log/tenant_operations_log, текущий месяц) — по логике глобальной per-partition функции не должны; прогон записал transient-инциденты на проде. Самоизлечивается после teardown (хвост удалён → зелёный). Омега/глобальные (auth_log/saas_admin) никогда не задеты. Передать: почему свежие строки sharing-flow не верифицируются + чистка ложных инцидентов m06. |
| FN-1 | мелкая (supplier) | смс+слово создал у поставщика только B2, B3 группы не создан (по коду должно B2+B3). На деньги/изоляцию не влияет. |
| FN-2 | косметика | боевой дефолт users.notification_preferences содержит мёртвый ключ reminder (фича удалена) — расходится с db/schema.sql:795. ✅ ИСПРАВЛЕНО в коде (миграция app/database/migrations/2026_06_22_120000_drop_reminder_from_notif_prefs_default.php, проверка 22.06.2026), ✅ НА ПРОДЕ (проверено 22.06.2026). |
| FN-3 | мелкая | онбординг-confirm не ставит users.email_verified_at (аккаунт всё равно активен, вход работает). ✅ ИСПРАВЛЕНО в коде (подтверждено app/app/Services/Auth/RegistrationService.php:115, проверка 22.06.2026), ✅ НА ПРОДЕ (проверено 22.06.2026). |
| NB-DIRECT | к сведению | DIRECT-проекты синкаются к поставщику как site → дают 3 rt (R1 ожидал 0). На DIRECT-инъекцию не влияет. |
| M-1 / M-2 | из prep-review | M-1 (админ fail-open) и M-2 (капча) — по launch §2 выкачены/закрыты; M-2 подтверждён вживую (реальная капча решалась владельцем). M-1 — бизнес-риск к сведению владельцу (админ-зона на nginx). |
Находки из логов боевого (22.06, read-only post-run)
| ID | Важность | Суть |
|---|---|---|
| FN-SESSION | 🔴 операционная, ЖИВАЯ | Заход к поставщику через Playwright падает ~121×/день каждый день (14–21.06; 766 за 7 дней): SupplierAuthException: PlaywrightBridge exit code 1. Переустановка браузера 21.06 не вылечила. Ломает CsvReconcileJob (страховочную сверку, подбор пропущенных лидов) + проактивное обновление сессии. Вероятно связано с «лиды не идут» (страховка-сеть down). Разобрать запуск Playwright под www-data на проде (HOME/permissions/headless-shell версия). |
| FN-LOGIN-ROUTE | 🟡 тот же класс, что FN-RESET | Route [login] not defined (свежий, 21.06 + 08.06) — какой-то flow строит route('login'), которого в SPA нет. Чинить вместе с FN-RESET (общий шаблон: дефолтные Laravel-маршруты, которых SPA не объявляет). |
| FN-FAILEDJOBS-PILE | ✅ СДЕЛАНО 22.06.2026 | failed_jobs = 490к мёртвых строк DomainException: B1 platform does not support SMS — целиком 28–29.05 (буря вашиденьги24, корень уже исправлен майским UPDATE 292/293→sms). queue:flush выполнен на проде: было 494 191 → стало 0. |
| FN-ENC | 🟢 минор | 4× invalid byte sequence for encoding UTF8 — лид с битой кодировкой падал в очереди (исторически). |
| FN-RLS-CTX | 🟢 минор (истор.) | 2× unrecognized configuration parameter "app.current_tenant_id" (29.05) — путь запроса к projects без RLS-контекста. Старое, не воспроизводится свежо. |
Вывод по логам: основной шум (collision 2001× / B1+SMS 490k / routing_failed_permanently 667×) — исторический майский шторм (вашиденьги24), корень починен, нужна только уборка
failed_jobs. Единственная живая операционная проблема — FN-SESSION (Playwright-заход к поставщику валится 121×/день → CsvReconcile не работает). Почта/деньги/раздача сейчас здоровы.
Поправка от 22.06.2026 (разбор FN-SESSION)
Дописано после диагностики на проде (read-only). Запись выше сохранена как была; здесь — уточнения. Полный разбор: docs/superpowers/runbooks/2026-06-22-FN-SESSION-diagnosis-and-ops-fix.md.
- FN-SESSION: корень — отсутствовал исполняемый файл браузера в кэше www-data
(
chromium.launchпадал). Утверждение «переустановка 21.06 НЕ вылечила» — неверно: вылечила (ошибок «PlaywrightBridge exit code» после 21.06 12:36 нет). Дополнительно исправлен код-баг диагностируемости (launch вне try/catch → опасный exit-1-двойник «login rejected»); теперь отказ запуска = exit 4 + JSON. Остаётся ops-шаг владельца:playwright installпод www-data в деплое (профилактика рецидива). - «Лиды не идут с 3 июня» — не дефект: владелец сам выключил проекты (намеренно,
подтверждено 23.06.2026). По сути верно. Уточнения 23.06: (1) выключены проекты главного
клиента tenant 2, не только «омеги»; (2) FN-SESSION (сломанный браузер 4–21.06) — отдельная
реальная, уже починенная проблема того же периода, не первопричина; (3) если поставщик пришлёт
лид на заблокированный проект, тот падает МОЛЧА (
no_snapshot_skipped=Log::info, невидим при LOG_LEVEL>=warning) — мелкое необязательное улучшение наблюдаемости. Контекст: docs/superpowers/findings/2026-06-23-balance-block/2026-06-23-LEADS-STOPPED-correction.md. supplier.session.refreshed=0 в логе — артефакт уровня логирования (Log::infoотфильтрован, на продеLOG_LEVEL>=warning), не сбой: refresh работает.
Статус мелких находок (фикс-сессия 22.06)
- FN-3 — ✅ починен:
RegistrationService::confirmтеперь ставитusers.email_verified_at(черезforceFill— поле вне$fillable). TDDConfirmSetsEmailVerifiedAtTest. - FN-2 — ✅ починен: миграция
2026_06_19_130000забылаALTER COLUMN ... SET DEFAULT, реальный DB-дефолтnotification_preferencesоставался с мёртвымreminder. Новая миграция2026_06_22_120000+ TDDNotifPrefsDefaultNoReminderTest+ CHANGELOG v8.50. - FN-1 — ❎ не баг кода:
SupplierProjectGrouping::resolvePlatformsдля смс+слово уже возвращает['B2','B3'](корректно). Непоявление B3 — supplier-side «Doubles» (HTTP 200status=Doubles), которыйSyncSupplierProjectJobсам детектит как «incomplete platform set (transient miss: B3)» и ретраит. Отдельная supplier-диагностика, не локальный фикс. - FN-ENC — ✅ починен: один битый UTF-8-байт в CSV-выгрузке ронял весь импорт на
INSERT (PG «invalid byte sequence»).
CsvLeadsParser::parseтеперь чистит невалидный UTF-8 (mb_convert_encoding) до парсинга. TDDCsvLeadsParserUtf8Test+ руками подтверждено (PG отвергал raw-байт, принимает санитайзенное). - FN-RLS-CTX — ❎ no-action (уже разрешено): путь
projectsидёт черезtenant-middleware (ставитapp.current_tenant_id); старый no-authGET /api/projects?tenant_id(источник 29.05) заменён auth+tenant-версией (web.php §291–293). Не воспроизводится; менять RLS вслепую непропорционально.
Новые находки после прогона
- FN-INN-LOOKUP (🟡, найдено владельцем 22.06 на проде): Настройки → Реквизиты →
Тип лица «ЮРЛИЦО», кнопка «Найти по ИНН» не находит организацию по валидному ИНН
юрлица (пример:
2460090423— реальное ООО) → «Организация не найдена по ИНН». Это DaData party-lookup (TenantRequisitesController::lookupInn/PartyLookup, флагservices.dadata.party_enabled+ ключ DaData). Вероятно на проде lookup выключен или мисконфиг ключа/эндпоинта. Заполнение названия вручную работает — на гейт реквизитов не блокирует. Разобрать позже (read-only диагностика: флаг + ключ DaData на проде).
Критдефекты
- Нет.
Teardown
- Выполнен полностью до 21:00. rt у поставщика удалены (DeleteSupplierProjectJob), слепки/тест-тенанты/orphan по vid вычищены, Redis digest-ключи убраны.
- Чистота TD5: 0 TEST-тенантов, 0 тест-площадок, 0 тест-лидов.
- tenant 2 (омега) ЦЕЛ: 1 835 400.00 / 1013.
- tenant 24 (онбординг kdv1@bk.ru, 300₽) — ✅ УДАЛЁН 22.06.2026 (soft-delete тенант+пользователь, 0 сделок/транзакций; активных тенантов 1; обратимо, бэкап на проде).
auth_logтест-строки оставлены намеренно (N-3, глобальная цепочка) — это допустимая остаточность.
Эфир
14 карточек живого «телевизора» (live-demo/efir.html, шаги 1–14): старт→клиенты→проекты/заказ→первый лид→лимит/перелив→защита денег→каскад/тариф→API→импорт→нагрузка→деньги-итог→онбординг(рег→300₽)→колокольчик/дайджест.
Что осталось владельцу (follow-up, не блокирует продажу)
Решить судьбу tenant 24 (онбординг-аккаунт) — оставить или удалить.✅ УДАЛЁН 22.06.2026 (soft-delete).- Глянуть в админке crm.bp-gr.ru, что тест-rt
B*_test-*исчезли (подтверждение TD1 глазами). - FN-AUDIT → разработчику (почему verify краснеет на свежих строках + чистка transient-инцидентов m06).
- M-1 (админ fail-open) — бизнес-решение о defense-in-depth.