Commit Graph

693 Commits

Author SHA1 Message Date
Дмитрий e8e23bdbae docs(chat): снимок состояния — откат склейки починен, кадры на живых данных
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 05:22:46 +03:00
Дмитрий b059d86fc6 fix(guides): кадры пересняты на живых демо-данных — портал выглядит работающим 2026-07-14 05:07:48 +03:00
Дмитрий d0808ec09a docs(guides): итог дня — разборы 10/10, снимок состояния, план стройки
Гость получает верную ссылку на разбор по всем 10 частым вопросам; где
разбора нет — бот молчит, в пустоту не шлёт. 20 тем, 126 кадров, единый
демо-клиент. Отдельно: найден баг боевой функции — поиск дублей считал
ноль признаком совпадения (на проде он есть, фикс в ветке).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 04:14:58 +03:00
Дмитрий ed5f9ffd4a docs(guides): проверка разборов — бот, страница, ПДн, вес 2026-07-13 20:18:11 +03:00
Дмитрий 24fc6ecad4 fix(guides): семь тем про конкурентов пересняты на стоматологии — единый демо-клиент 2026-07-13 20:05:00 +03:00
Дмитрий cc63a7403f fix(guides): кадры показываются крупно — надписи в портале читаются 2026-07-13 19:20:36 +03:00
Дмитрий 1ee42a223b feat(guides): разборы по заявкам, отчётам, деньгам и настройкам проекта 2026-07-13 18:36:10 +03:00
Дмитрий 69a81a7f07 docs(plans): план учёта посетителей — 12 задач, TDD, выкат отдельным шагом
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:30:46 +03:00
Дмитрий dd50f3832f docs(specs): собственный учёт посетителей + раздел «Посетители» в админке
Точное разделение робот/браузер/человек (отклик страницы, а не запрос),
сквозная склейка лендинг→портал через куку на .liderra.ru, воронка и каналы
в админке. Метрику и Вебвизор не трогаем.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:17:21 +03:00
Дмитрий 9c668de390 feat(guides): разборы по Конкурентному полю и источникам 2026-07-13 18:01:33 +03:00
Дмитрий 379564e8c0 feat(guides): страница «Как это работает» — пошаговые разборы для гостей 2026-07-13 17:45:36 +03:00
Дмитрий c33e91b2a7 feat(guides): съёмщик разборов + тема «Как создать проект»
Playwright-съёмщик (record.mjs) логинится демо-клиентом, проходит сценарий
из topics/<slug>.mjs и рисует поверх каждого шага стрелку+рамку+подпись
(подписи — из app/resources/help/sozdat-proekt.md, без выдумок). Первая
тема — sozdat-proekt, 8 кадров.

Мелкие находки по пути: приветственный тур перехватывал клики (глушим через
localStorage-флаг), первый проект нового клиента открывает шаг «Реквизиты»
(проходим тихо, без кадра), собранный фронт портала был устаревшим и тянул
старый виджет JivoSite поверх экрана (пересобрал npm run build — исходники
уже используют свой чат-виджет), подпись снизу могла перекрыть стрелку на
кнопках у нижнего края диалога (авто-перенос наверх).
2026-07-13 17:33:14 +03:00
Дмитрий bcffafd98c docs(tours): показы по порталу для гостей — два формата на выбор
Клипы по сути (сразу с нужного экрана, без входа и хождения по меню)
и тот же показ 2-3 картинками. Четыре темы: заявки, пополнение, создание
проекта, Конкурентное поле. Телефоны в кадре размыты. Страница сравнения —
tours-compare.html. На боевом сайте этого нет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 16:29:15 +03:00
Дмитрий 2392eb0a25 docs(chat): личные ответы вошедшему — прогон и снимок состояния
20 личных вопросов на тестовом клиенте — 20 правдивых ответов; границы
держит (паузу не ставит, ПДн не даёт, будущее не прогнозирует). Гостю —
вход в кабинет. Посторонний получает 404. Две находки прогона закрыты:
жёсткий отказ в замене ловится в любой форме, правда про ниши больше
не режется. Тесты 216/216. На проде этого нет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 13:49:20 +03:00
Дмитрий bb6df90810 docs(chat): план стройки личных ответов вошедшему клиенту
9 задач по TDD: ClientFacts (деньги/ступень/проекты/заявки под RLS),
сторож на выдуманные числа, карточка в контексте вошедшего, гостю вход
в кабинет, владелец разговора (404 чужому), ключ разговора на каждый вход,
живой прогон с судьями.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 11:02:40 +03:00
Дмитрий 09f0ea44df docs(chat): замысел личных ответов вошедшему клиенту
Карточка фактов (баланс/ступень/проекты/заявки) считается кодом и всегда
кладётся боту, когда клиент вошёл. Сторож режет числа не из карточки.
Переписка вошедшего закрывается от посторонних. Данные — строго под RLS
тенанта. Бот только рассказывает, ничего не меняет.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:55:32 +03:00
Дмитрий 2b13980af8 docs(chat): хвосты закрыты — экскурсии по теме и только из кабинета
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 10:15:38 +03:00
Дмитрий 60b0b855c5 docs(bot): в снимок состояния — правило ответственности за источник
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:50:30 +03:00
Дмитрий 34c6d68cfe docs(sales-finder): HANDOFF 13.07 — повтор сайта, ДФО-перегон #15, публикация отложена, Keys.so финал + новый resume-промт
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 09:44:31 +03:00
Дмитрий f6b2fbe262 docs(chat): отчёт о прогоне через свой чат + снимок состояния
Прогон 46 диалогами (250 реплик) через новый чат: 250/250 ответов,
медиана 2,58 с, вранья 0 — переезд чистый. Одна находка (бот выдумал срок
«перезвонят в течение рабочего дня») починена правилом по смыслу.

Отдельно: счётчики от накрутки счёта сидели на файловом кэше, который считает
не атомарно (300 прибавок -> досчиталось 77). Переведены на Redis, 300/300.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:20:16 +03:00
Дмитрий 45b0e46279 feat(landing): подключить своё окошко чата вместо Jivo
Тот же скрипт, что в кабинете: /chat-widget.js отдаётся приложением
(на liderra.ru лендингу принадлежит только «/», остальные пути — приложению).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 09:02:22 +03:00
Дмитрий 477a46b889 docs(chat): пошаговый план стройки своего чата
8 задач по TDD: журнал разговоров -> свой приём сообщений -> опрос ответов ->
счётчики от накрутки счёта -> письмо владельцу со всей перепиской -> окошко
на портале и лендинге -> вычистка Jivo из настроек -> прогон 46 диалогами.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 06:37:28 +03:00
Дмитрий 01789381ac docs(chat): замысел своего чата вместо Jivo + разворот в протоколе
Владелец 13.07.2026: уходим от Jivo (3142 руб/мес корпоративный тариф ради
Bot API, ответа их поддержки так и нет), пишем своё окошко чата.
Ключевое: в чужом виджете бот не знает, кто с ним говорит -> личные ответы
невозможны. Привязка к Jivo в коде - 94 строки; мозг бота, 49 статей и
130 тестов переезжают нетронутыми.

Согласовано по частям: чат без постоянного соединения (очередь + опрос),
кабинет + лендинг, эскалация письмом владельцу (живого оператора нет),
три уровня счётчиков от накрутки счёта (потолок 1500 ответов/сутки + тревога).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 06:27:29 +03:00
Дмитрий 6e40c0523f docs(bot): правило владельца — замены заявки нет, тему сглаживаем
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:59:29 +03:00
Дмитрий 204f7b50b8 docs(bot): снимок состояния ИИ-бота на 13.07.2026
Где что лежит, как поднять стенд, что держится кодом (не ослаблять),
что осталось открытым, порядок выката.

Главный вывод доводки: сторож на регулярках проигрывает гонку формулировок —
работает только правило по смыслу + запрет в промпте.

Код: ветка worktree-jivo-bot-core (004a0b17), тесты 126/126. Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:35:30 +03:00
Дмитрий fd9358104c docs(bot): отчёт прогона диалогами — 46 разговоров, 250 реплик
Что вскрылось (одиночные вопросы этого не видели): клиент оставил телефон →
бот молчал; бот забывал, что сам сказал; враньё «на паузе списаний нет»;
выдуманный телефон поддержки и юр-гарантии в новых оборотах.

Урок метода: машинный счётчик по запрещённым фразам показал 0 нарушений,
хотя враньё осталось другими словами — считать своими же регулярками нельзя.

Код и починка — в ветке worktree-jivo-bot-core (b037a216). Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:52:41 +03:00
Дмитрий 368bbf2000 docs(bot): отчёт прогона 250 вопросов — финальная сборка (Haiku + сторож)
Все 250 пар «вопрос → ответ» с финальной конфигурацией бота: модель Claude
Haiku 4.5 через AITunnel, маска ПДн, чистка markdown, сторож ответа.
Итог: 239 ответил сам, 11 увёл к специалисту, 0 «не знаю», 0 запрещённых фраз.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:48:11 +03:00
Дмитрий 17ab3fa3a3 fix(sales-finder): рендер 2ГИС молча терял фирмы — повтор до появления карточек
Симптом: кластер ДФО дал Владивосток 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>
2026-07-12 19:54:54 +03:00
Дмитрий d501a4fc79 feat(sales-finder): двухшаговый сбор контактов ИНН director -> name_standart
По личному ИНН директора: шаг 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>
2026-07-12 17:27:20 +03:00
Дмитрий 5a3746da9c fix(sales-finder): контакты по ИНН фирмы через POST /2.0/inn — живой запрос заработал
Выяснено живьём: сервис принимает 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>
2026-07-12 16:25:13 +03:00
Дмитрий da9d8cf00e feat(sales-finder): сбор контактов телефон+e-mail по личному ИНН директора
Галочки в списке на прозвон -> кнопка Собрать контакты -> по 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>
2026-07-12 15:55:32 +03:00
Дмитрий fe08b5afcd feat(sales-finder): «ключи» → «запросов в рекламе» + HANDOFF с промтом продолжения
Владелец спросил «что значит ключ?» — «ключи» это жаргон Keys.so. По-человечески
это поисковые запросы, по которым фирма показывает объявления: чем их больше, тем
плотнее она сидит в рекламе. Переименовано в портале и в Excel, добавлено пояснение.

HANDOFF: актуальное состояние (портал, каталог 2ГИС, кластер, 7 починенных багов,
границы и запреты) + RESUME PROMPT для новой сессии.

146 тестов зелёных.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:06:44 +03:00
Дмитрий 4d937feb76 docs(sales-finder): HANDOFF — портал, каталог 2ГИС, кластер, 7 починенных багов, границы
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 13:51:02 +03:00
Дмитрий 9c01dc162d docs(sales-finder): HANDOFF — слои A+B добавлены, C отклонён, хвосты по скорости
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 09:46:17 +03:00
Дмитрий bdbf5f415c feat(sales-finder): слои A+B для охвата ИНН+директора
Слой 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>
2026-07-12 09:45:21 +03:00
Дмитрий 371f171ebd docs(sales-finder): HANDOFF-состояние v1 + resume-промт (2ГИС через xfetch работает)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 08:58:37 +03:00
Дмитрий b555931bde docs(sales-finder): брейншторм-заметки, дизайн и план портала поиска клиентов для отдела продаж
Портал на этой машине: рубрика 2ГИС + город → список фирм на прозвон
(кто рекламируется, ИНН, директор). Переиспользуем наработки (ChannelA/2ГИС,
ad-scan, Keys.so, build_portal, ДаДата), боевое не трогаем — только копии.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 07:28:03 +03:00
Дмитрий da7dc55add docs(supplier): спека + план итоговой проверки заказа и сторожа дедлайна
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 04:43:43 +03:00
Дмитрий 96fc2b7be6 docs(runbook): yookassa-enable — пометка что sudo -u postgres на VM = мёртвая копия (Managed PG)
Боевые шаги ранбука (флаг/данные через sudo -u postgres psql) после переезда 26.06
бьют в старую копию. Добавлен красный баннер вверху + правка Troubleshooting: данные/
миграции — через app/Eloquent или crm_migrator на живом кластере (§6b пайплайна).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 17:11:28 +03:00
Дмитрий 51ad7743d4 docs(runbook): выкат — миграции на ЖИВОЙ Managed-кластер (не мёртвая VM-копия), катим из main, smoke по lk
Исправлены опасные устаревшие шаги: §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>
2026-07-09 14:03:34 +03:00
Дмитрий 47dfc3f850 docs(handoff): CSV-reconcile vid-fix — прод-выкат, уборка kdv1, анти-затирание 2026-07-09 09:08:17 +03:00
Дмитрий 17a64bbd65 fix(supplier): CSV-reconcile сверяет журнал ОТДАННОГО (по vid), не пул
Причина фантомных сделок 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>
2026-07-09 08:56:44 +03:00
Дмитрий c2134457ea docs(autopodbor): HANDOFF-STATE пакетного сбора источников — готово в ветке, ждёт выката
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 05:54:45 +03:00
Дмитрий 219e64a88a docs(autopodbor): план — вписаны находки Задачи 0 (pgsql_supplier, web.php, FieldWorkspaceScreen)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:10:36 +03:00
Дмитрий e612673bae docs(autopodbor): план реализации пакетного сбора источников + честной очереди
TDD-задачи 0-9: миграция batch_id, AutopodborBudgetGate, AutopodborStudyScheduler,
startStudyBatch, tick в джобе, крон-страховка, endpoint, фронт-подвал, верификация.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:02:25 +03:00
Дмитрий 85908ce1c7 docs(autopodbor): спек пакетного сбора источников (шаг 2) + честная очередь между клиентами
Дизайн из брейншторма с владельцем: кнопка в подвале выбора, пакет из
группы конкурентов, одна дорожка + round-robin между тенантами,
денежный гейт всё-или-ничего с резервом под активные лид-проекты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 16:51:24 +03:00
Дмитрий a877785247 feat(ad-scan): волна 3 «лист обзвона» + классификатор телефонов Россвязи
- rossvyaz.py: тип/оператор/регион/часовой пояс по реестру Россвязи (бесплатно, офлайн);
  детект «виртуальный (переадресация)» — городской на мобильном операторе/вирт.АТС.
- wave3.py: зацепка, приоритет, личные почты и мессенджеры с сайта, лист «Памятка менеджеру».
- phones_rossvyaz.py: разбор телефонов рекламодателей, «есть прямой канал».
Файлы реестра Россвязи и локальный тест — вне git. Рунбук обновлён.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 05:11:19 +03:00
Дмитрий 564f4c0abd docs(plan): пошаговый план Метрика+Вебвизор на портале (8 задач, маскировка ПДн, gate)
Реализация дизайна 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>
2026-07-08 04:06:44 +03:00
Дмитрий 8280671de1 docs(spec): дизайн Метрика+Вебвизор на портале (кабинет клиента, маскировка ПДн)
Утверждённый с владельцем дизайн: отдельный счётчик для lk.liderra.ru, запись
только в кабинете клиента, маскировка личных данных (Вариант 1), CSP-правка блока
приложения, строка в политике конфиденциальности. Выкат через тестовый стенд +
проверка маскировки как gate. Следующий шаг — план работ (writing-plans).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 03:53:35 +03:00
Дмитрий e36b712e7d fix(landing): CSP child-src/frame-src blob: — Вебвизор не писал сессии
Симптом: отчёт «Посетители» полон, а Вебвизор пуст даже за дни с заходами.
Причина: строгий 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>
2026-07-08 03:28:46 +03:00