Пример формата в ContactCapture.php остался в истории коммита 6f72367b
и блокировал push. Не ПДн — образец для распознавания; в рабочем коде уже
замаскирован. Настоящие номера сторож ловит по-прежнему.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Все 250 пар «вопрос → ответ» с финальной конфигурацией бота: модель Claude
Haiku 4.5 через AITunnel, маска ПДн, чистка markdown, сторож ответа.
Итог: 239 ответил сам, 11 увёл к специалисту, 0 «не знаю», 0 запрещённых фраз.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Симптом: кластер ДФО дал Владивосток 11 фирм вместо реальных 60.
Причина: xfetch.render повторял только на ПУСТОМ ответе, а 2ГИС-антибот
отдаёт непустую SPA-оболочку без карточек — она проходила как успех,
и вся страница фирм терялась. Фикс: render(ok=...) повторяет пока ok(html)
ложно; fetcher требует наличие /firm/ (не просто непустоту), retries=4.
Живой контроль боевым fetcher: Владивосток 11 -> 60 (x5.5). 185 тестов зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
По личному ИНН директора: шаг 1 POST /2.0/inn -> ФИО+дата рождения
(юрлицо ООО отсекаем), шаг 2 POST /2.0/name_standart -> ВСЕ телефоны из
телефонных полей + ВСЕ почты, в исходном порядке, без сортировки/обрезки
(паспорт/СНИЛС/счета телефоном не считаем). Фильтр ИНН-эхо для fallback.
Проверено живьём: АСТРЕЯ дир. по личному ИНН -> 37 телефонов + личная почта;
по ИНН фирмы возвращается ООО -> 0 (вход = личный ИНН директора).
2 платных запроса на фирму, только по отмеченным галочкам. Тесты вычищены
от реальных ПДн. 179 тестов зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Выяснено живьём: сервис принимает POST с ключом+ИНН в теле, эндпоинт /2.0/inn
не inn_fl, ищет по 10-значному ИНН фирмы. GET с ключом в адресе давал 401,
inn_fl давал uncorrect format. Переделал contacts.py на POST с телом,
двухстрочный секрет адрес+тело, parse_config сам определяет POST/GET.
Поиск теперь по firm.inn, галочка активна при наличии ИНН фирмы.
Проверено сквозняком: Илатан 7709918259 -> реальные телефоны и почты в колонках.
167 тестов зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Галочки в списке на прозвон -> кнопка Собрать контакты -> по director_inn
тянем телефон и e-mail через сторонний inn_fl API, 2 новые колонки + Excel.
Домен/ключ API живут только в secrets/inn_fl_url.txt со шаблоном {inn} -
в коде не зашиты. Ответ парсим по образцу regex, ИНН/ОГРН телефоном не считаем.
ПДн: сбор только по явной отметке, последовательно, ошибка одной фирмы не роняет пакет.
165 тестов зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Владелец спросил «что значит ключ?» — «ключи» это жаргон Keys.so. По-человечески
это поисковые запросы, по которым фирма показывает объявления: чем их больше, тем
плотнее она сидит в рекламе. Переименовано в портале и в Excel, добавлено пояснение.
HANDOFF: актуальное состояние (портал, каталог 2ГИС, кластер, 7 починенных багов,
границы и запреты) + RESUME PROMPT для новой сессии.
146 тестов зелёных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Слой A (requisites.py): если ИНН нет на главной сайта — ищем страницы
реквизитов/политики конфиденциальности/лицензии/контактов и достаём ИНН
оттуда (по 152-ФЗ ИНН обязан быть на «Политике»). Надёжно, точно.
Слой B (dadata.enrich_by_name): если ИНН так и нет — ищем в ДаДате по
названию+городу с защитой best_match (только действующие с совпадением
города). Директор по названию ВСЕГДА помечается «проверьте» (совпадение
может быть чужой фирмой — напр. Чудо Доктор→Аптека Чудо Доктор).
cities.py: слаг города (латиница, для 2ГИС) → русское имя (для ДаДаты).
Реквизит-страницы тянем только быстрым requests (без медленного xfetch).
Слой C (бесплатные порталы по телефону) отклонён — проверен через xfetch,
контроль провалился, отдаётся нестабильно.
Проверено живьём: слой A дал ИНН+ФИО Доброму доктору, Дентал Фэнтези,
Белгравии и др. со своих сайтов; слой B — Дантистофф/Медлайн/СМ-Стоматология
с пометкой. 61 тест зелёный.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Портал на этой машине: рубрика 2ГИС + город → список фирм на прозвон
(кто рекламируется, ИНН, директор). Переиспользуем наработки (ChannelA/2ГИС,
ad-scan, Keys.so, build_portal, ДаДата), боевое не трогаем — только копии.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
По rls-ревью: соседние SaaS-supplier-таблицы (supplier_sync_runs/deferred_sync)
создаются через pgsql_supplier + GRANT SELECT,INSERT + USAGE на sequence для
crm_supplier_worker в DO-блоке с IF EXISTS. Дефолтный Schema::create оставлял
таблицу во владении crm_migrator без грантов → VerifySupplierOrderJob упал бы
permission denied на боевом Managed PG. Приведено к паттерну + SaaS-коммент + CHECK status.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- normalizeLive матчит наши строки по supplier_external_id (tag=регион, не _lidpotok) — иначе live всегда пуст
- shouldBeOff исключает ключи, активные по формуле (нет ложного should_be_off)
- recordRunSummary → insertGetId, проверка получает sync_run_id
- обновлён phpstan-baseline.neon: count mock() 2→3 (новый тест на баг 2)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Боевые шаги ранбука (флаг/данные через sudo -u postgres psql) после переезда 26.06
бьют в старую копию. Добавлен красный баннер вверху + правка Troubleshooting: данные/
миграции — через app/Eloquent или crm_migrator на живом кластере (§6b пайплайна).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Исправлены опасные устаревшие шаги: §0/§6b/§8 велели миграции через sudo -u postgres
на VM = мёртвая rollback-копия (после переезда 26.06 боевая = Managed PG). Теперь —
crm_migrator на rw-endpoint кластера. Плюс: катим из main (=прод с 09.07), хирургический
overlay, smoke по lk.liderra.ru, квирк 107 (optimize только www-data).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Причина фантомных сделок kdv1 (09.07.2026): CsvReconcileJob брал отчёт «Запрос
номеров» = ПУЛ собранных номеров (phones_cnt), а не ОТДАННОЕ (crms_cnt), и лепил
из пула сделки клиенту + выедал дневной лимит. Пример: пул 157, отдано 15,
у клиента 15 фантомов, а 15 реально отданных выбило лимитом.
Фикс: SupplierPortalClient::fetchDeliveredLeads() читает журнал «Мои сделки»
(index-visit, по vid) — только реально отгруженное. CsvReconcileJob сверяет по vid,
добор несёт настоящий vid (idx_supplier_leads_vid_unique -> идемпотентно с webhook).
Пул не трогается -> фантомы невозможны, лимит не забивается мусором.
Проверено: 12 новых тестов + 294 supplier зелёные, Pint/Larastan чисто. Выкачено и
проверено на проде (reconcile читает отданное, 0 фантомов за прогон).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Дизайн из брейншторма с владельцем: кнопка в подвале выбора, пакет из
группы конкурентов, одна дорожка + round-robin между тенантами,
денежный гейт всё-или-ничего с резервом под активные лид-проекты.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Раздел «5. Хранение и защита»: упоминание записи сессий Вебвизором с маскировкой
персональных данных контактов, обработка на серверах в РФ.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
router.afterEach: при входе на layout=app лениво грузит Метрику и шлёт hit(fullPath).
Вход/публичные/админка/портал продаж — без Метрики. + убраны лишние @ts-expect-error
в тесте (type-check чист).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
metrika.ts: читает id из <meta name=metrika-id>, один раз инжектит tag.js + init
webvisor:true (defer), hit(url) шлёт заход. Тесты tests/Frontend/metrika.spec.ts:
no-op без meta, идемпотентная загрузка, hit вызывает ym с id+url.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
По образцу JivoSite: config services.metrika.counter_id из env METRIKA_COUNTER_ID;
welcome.blade вставляет <meta name=metrika-id> только когда id задан. Скрипт грузится
лениво из роутера (кабинет), не тут — Вебвизор не пишет вход/админку.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Реализация дизайна 2026-07-08: конфиг счётчика (образец JivoSite), ленивый
загрузчик metrika.ts (TDD), hit только для layout=app, маскировка ym-hide-content/
ym-disable-keys в компонентах кабинета, строка в политику, CSP-правка блока
приложения + выкат с обязательной проверкой маскировки ПДн на стенде.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Утверждённый с владельцем дизайн: отдельный счётчик для lk.liderra.ru, запись
только в кабинете клиента, маскировка личных данных (Вариант 1), CSP-правка блока
приложения, строка в политике конфиденциальности. Выкат через тестовый стенд +
проверка маскировки как gate. Следующий шаг — план работ (writing-plans).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Симптом: отчёт «Посетители» полон, а Вебвизор пуст даже за дни с заходами.
Причина: строгий CSP резал скрытый blob-frame Вебвизора (базовый счётчик через
img-пиксель проходил → «Посетители» есть). Fix на боевом (nginx liderra.ru,
блок лендинга): +child-src/frame-src 'self' blob: https://mc.yandex.ru +домены
Метрики в frame-ancestors. Проверено: заголовок отдаётся, / и /login = 200.
Бэкап /etc/nginx/liderra.bak-webvisor-20260708.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Потоп ~116 писем на ops@ убран: FailoverProjectChannel без per-project писем +
Блок 6 spike-детектор в incidents:watch-failures. На проде lk.liderra.ru
(gitea 316bc2d8+9799c0eb). Подчищены 57 stale строк очереди + 1 autopodbor-джоб.
cspell: +непроливший, +флайт.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Блок 6: если за окно (10 мин) в supplier_manual_sync_queue упала пачка
(>= threshold-manual-queue, дефолт 10) — вероятное падение кабинета поставщика.
Инцидент дедупится 60 мин, письмо шлётся каждый прогон на kdv1@bk.ru + ops@liderra.ru
(громко при реальной аварии, но одним сводным письмом). Флаг sendMail в createIncident.
phpstan-baseline: +4 TestCall::artisan() (5→9) от новых тестов.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Убраны письма manual_required и failover_to_form (по проекту, без троттла) —
источник потопа ~116 писем на ops@ 07.07. Строка в ручной очереди остаётся
(запись видна в админке). Зависимость Mailer удалена из канала и DI-привязки.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Устраняет потоп ~116 писем на ops@ 07.07: убираем per-project письма
FailoverProjectChannel, добавляем аварийный spike-детектор ручной очереди
в incidents:watch-failures. batch остаётся рабочим режимом.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TDD-план на 9 задач: worktree от worktree-avtopodbor (там есть «Поле»),
useLayoutTier, AppBottomNav, AppMoreDrawer, rail-сайдбар, ярус-отступ AppLayout,
ResponsiveTable и пруф на «Сделках». Экраны Этапов 2-3 — отдельными планами.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Брейншторм с владельцем 07.07. Рамки: основной портал 9 экранов, телефон+планшет,
премиум-уровень, мобилка урезанная / десктоп полный. Подход А — сначала каркас, потом экраны.
Меню на телефоне: Поле, Проекты, Сделки, Биллинг, Канбан. База — ветка с «Полем» вне main.
Аудит: 4 реальных архетипа поломки сняты локально на 390px [findings/2026-07-07-portal-mobile-audit].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- на телефоне текст героя опущен ниже padding-top 22vh, свечение звезды мягче x0.68
- убран логотип ЛИДЕРРА из шапки сверху слева
- Вход ушёл в левый край, Попробовать бесплатно — в правый
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- счётчик Метрики вставлен в <head> лендинга landing-v5-claude.html
- на 3 кнопки перехода на портал повешен reachGoal GO_PORTAL (JS-событие)
- ранбук выката: CSP блока лендинга дополнен https://mc.yandex.ru
в script-src/img-src/connect-src — иначе строгий CSP блокирует счётчик
Выкачено на боевой liderra.ru 07.07: счётчик проверен живьём (заход уходит
в Яндекс), вход в кабинет /login не сломан. Цель GO_PORTAL создана в Метрике
(ID 579585431).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Пилот по стоматологиям: скан кода сайтов (коллтрекинг Calltouch/Comagic/
Callibri/Roistat/Mango + пиксели VK-myTarget/Google Ads/Meta) плюс Яндекс.Директ
через Keys.so API (ключи, объявления, бюджет), склейка в Excel. Две фазы:
сайты параллельно, затем чистый проход по API с повторами. Тесты 16/16.
Спека и план — docs/superpowers. Данные Спарка, результаты и токен Keys.so —
вне git (личная папка + .gitignore).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>