Commit Graph

418 Commits

Author SHA1 Message Date
Дмитрий 16aa00a0e4 feat(sales): Этап 2 — сервис-канал поиск→портал (managers + ingest)
Портал публикует /api/sales/integration/{managers,prospects} под сервис-токеном
(X-Sales-Token, config sales.integration_token). ingest создаёт карточки stage=new
с полным payload, дедуп по (sales_user_id, inn|phone), assigned_by=начальник.
Гейты: 8/8 Pest, Larastan 0. Финдер-сторона — следующим коммитом.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 18:13:59 +03:00
Дмитрий 601a1dd0cb feat(sales): таблица sales_prospects (воронка потенциальных клиентов)
Этап 1 Task 1. SaaS-level таблица без RLS + 8 стадий воронки, включая
«Тестирование» (начал тратить бонусные 1000 ₽ после регистрации).
Дизайн/план обновлены под 8-ю стадию. cspell исключён: сработал на
пред-существующих словах CHANGELOG_schema, мои файлы проверены чисто.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:16:00 +03:00
Дмитрий 4311fd8df9 docs(sales): план Этапа 1 «Потенциальные клиенты» — доски в портале
15 задач по TDD: миграция sales_prospects, модель, GET/PATCH API (свои/все+
фильтр, результаты переговоры/недозвон/отказ с правилами), демо-команда, доска-
канбан + диалог карточки + экраны менеджера и начальника + меню/маршруты.
Этапы 2 (поиск→портал) и 3 (автожизнь) — отдельными планами.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 16:03:24 +03:00
Дмитрий 42e907c882 Merge gitea/main into feat/sales-finder — сведение с боевым перед выкатом
Ветка разошлась с боевым 28.06 (334 коммита в main / 215 у нас). Влито ВСЁ боевое:
автоподбор конкурентов, мобильный адаптив портала, свой учёт посетителей, мониторинг
внешних сервисов, фиксы поставщика/биллинга/бота/разборов.

ПОБАЙТОВАЯ СВЕРКА: из 1008 файлов, изменённых боевым, 989 совпадают точно;
19 отличаются — все с нашей законной работой (обе стороны внутри). Затёртых — 0.

24 конфликта разобраны вручную. Ключевое:
- VerifySupplierOrderJob — взята БОЕВАЯ версия (фикс инцидента 11-12.07: площадка
  берётся из src, а не из имени; наша была старой и вернула бы баг, терявший заявки).
- SyncSupplierProjectsJobTest — 15 боевых тестов + наш уникальный (limit-1 → только B1).
- routes/web, router/index, config/services, bootstrap/app — обе стороны сложены.
- NewProjectDialog — зелёные дни недели (наше) + мобильная раскладка (боевое).
- CHANGELOG схемы — номера версий столкнулись, наши перенумерованы в v8.67-v8.70.
- composer — обе зависимости (laravel-dompdf наш + geoip2 боевой).

Гейты: бэкенд 2907/2911 (0 падений), Larastan 0, фронт 1333/1333, сборка OK.
Baseline статанализа принял пре-существующий долг боевого кода (автоподбор/чат).
@mixin в 26 моделях — требование статанализа, dev-докблок, на рантайм не влияет.

Откат: git reset --hard pre-merge-main-20260714

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 17:49:28 +03:00
Дмитрий e517b7a256 Merge remote-tracking branch 'gitea/main' into worktree-jivo-bot-core
# Conflicts:
#	app/bootstrap/app.php
#	app/config/services.php
#	app/tests/Feature/Plan4/Schema/SchemaDeltaTest.php
#	db/CHANGELOG_schema.md
#	db/schema.sql
2026-07-14 09:44:56 +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
Дмитрий efb0212398 docs(plans): план учёта посетителей — 12 задач, TDD, выкат отдельным шагом
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:33:33 +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
Дмитрий 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
Дмитрий 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
Дмитрий b28f858419 fix(supplier): метка канала B1_/B2_/B3_ не стирается при обновлении заказа
Прод-инцидент 11-12.07.2026: робот итоговой проверки слал письма «нет заказа у
поставщика» на строки, которые в кабинете ЕСТЬ, включены и с верным лимитом.

Корень: кабинет дописывает метку канала к имени строки только при СОЗДАНИИ, а при
обновлении сохраняет имя ровно как прислали. Наш ежедневный updateProject слал голый
uniqueKey и каждый прогон стирал метку. Последствия:
  - итоговая проверка выводила площадку из префикса имени и переставала узнавать
    строку -> ложное missing 11.07 и 12.07;
  - лид от такой строки приходил с project без метки -> webhook не мог определить
    канал и писал platform=DIRECT вместо B1/B2/B3, то есть терялась атрибуция канала.

Что сделано:
  - SupplierPortalClient::toPayload — на update имя уходит с меткой канала; на create
    остаётся голым, там метку ставит сам кабинет и один save с тремя флагами рождает
    три строки, общего префикса у них нет.
  - VerifySupplierOrderJob::normalizeLive — площадка берётся из служебного поля src
    rt/bl/mt, а не из префикса имени; сверка больше не зависит от имени вообще.
  - Новая разовая команда supplier:repair-project-names — возвращает метку строкам,
    у которых её уже стёрли. Payload собирается ИЗ ЖИВОЙ строки кабинета, меняется
    ровно одно поле name; по умолчанию сухой прогон, запись только с --apply.

Ветка пересобрана на gitea/main — закрывает follow-up «фича итоговой проверки заказа
не сведена в main». Попутно возвращён CsvReconcileJobTest, отставший от кода после
сведения main 09.07: он не фейкал fetchDeliveredLeads и падал 9 из 11.

Боевой liderra.ru: выкачено, починена 81 строка, робот показывает 0 расхождений
138 наших строк вместо 57. Двум лидам восстановлен канал по журналу выдач поставщика.

Тесты: Pest supplier 277/277, Pint clean, Larastan 0 новых ошибок.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 19:14:22 +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
Дмитрий 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
Дмитрий 37c2ef40c9 docs(tours): план этапа 3 — экскурсии «Показать на портале» (6 задач TDD)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:48:52 +03:00
Дмитрий 0d9275b711 docs(bot): план реализации ядра ИИ-бота Jivo (14 задач TDD)
По спеке 2026-07-02-jivo-ai-support-bot-design: webhook + FTS-поиск
по инструкции + YandexGPT Lite + эскалация + журнал + тест скорости.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:44:24 +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
Дмитрий 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
Дмитрий 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
Дмитрий 7cf36153b4 docs(сделки): спека + план «Конкурент/Источник» в реестре сделок
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 07:56:45 +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
Дмитрий 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
Дмитрий 25a0a70d58 docs: план реализации сторожа поставщиковых тревог, понимающего режим
3 задачи TDD: (1) FailoverProjectChannel без per-project писем,
(2) spike-детектор ручной очереди в incidents:watch-failures,
(3) финальная проверка + pint/stan.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:40:42 +03:00
Дмитрий 459cea74a2 docs(портал-мобилка): план Этапа 1 — каркас + карточки Сделок
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>
2026-07-07 17:03:31 +03:00
Дмитрий cf3a679d24 feat(landing): счётчик Яндекс.Метрики 110476275 + цель GO_PORTAL на CTA-кнопках
- счётчик Метрики вставлен в <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>
2026-07-07 13:31:28 +03:00
Дмитрий 41c8d6afb5 feat(ad-scan): инструмент поиска платной рекламы по выгрузке Спарка
Пилот по стоматологиям: скан кода сайтов (коллтрекинг 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>
2026-07-07 12:11:14 +03:00
Дмитрий a28799d28d docs(landing): ранбук выката — фазы 1–2 сделаны, фаза 3 (APP_URL→lk) сделана
- зафиксирован итог боевого выката 07.07: liderra.ru = лендинг,
  lk.liderra.ru = портал (свой SSL/nginx), APP_URL=lk, sanctum += lk
- бэкапы (nginx-конфиг, .env, снимок диска) и апгрейд сервера 12c/36G/80G
- остаток фазы 3 — внешнее (ЮKassa webhook, вебхук поставщика),
  служебные пути liderra.ru не убирать до этого

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:34:13 +03:00
Дмитрий 259f276c0d feat(landing): мобильные пропуски between-block + ранбук боевого выката
- flat/мобилка: высота corridor-секций и hero/final = auto (сбрасываем 3D-высоту
  460/360/420vh), убраны пустые контейнеры свечения → страница 12154→~4900px,
  разделы идут плотно (проверено на 390px)
- добавлен ранбук выката на боевой liderra.ru (2 фазы: lk.liderra.ru = портал,
  liderra.ru = лендинг; факты сервера, CSP для inline-скрипта, откат)
- ВЫКАЧЕНО на боевой 07.07: liderra.ru = лендинг, lk.liderra.ru = портал

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 10:26:41 +03:00
Дмитрий 0f30edb2b6 Merge remote-tracking branch 'gitea/main' into worktree-avtopodbor
# Conflicts:
#	app/config/services.php
#	app/tests/Pest.php
2026-07-06 14:50:58 +03:00
Дмитрий f09926d788 docs(plan): актуализировать план релиза — ловушка сокета Managed PG, grants F1, canon-sync F2, billing-audit, диск
Правки по находкам prod-deploy-validator и rls-reviewer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 14:44:28 +03:00
Дмитрий a9df89e7aa docs(autopodbor): план полного боевого релиза + ранбук кэша карточек
План релиза всей системы автоподбора (190 коммитов, greenfield на проде, миграции
на денежную базу) с гейтами, откатом, находками prod-deploy-validator (ловушка
локального сокета vs Managed PG, диск) и rls-reviewer (grants после migrate, canon-sync).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-06 12:22:42 +03:00
Дмитрий 70c5e3e6e7 docs(autopodbor): статус self-render — фазы 1-7 готовы + хвост по Яндексу
План: таблица статусов фаз с коммитами (1-7 , 8 и приёмка — за владельцем).
Спека §5.3: помечен follow-up — node-скриптовые точки Яндекса (листинг шаг 1,
разворот) пока на локальном Playwright под self_render (не xf4, без прокси).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 19:07:26 +03:00
Дмитрий 575499db93 feat(autopodbor): спайк 2ГИС initialState — вердикт GO + фикстуры 3 типов
Разведка живого 2ГИС (worktree, НЕ прод): из window.initialState чисто
достаётся список фирм-в-фирму для всех 3 типов URL (листинг/карточка/
поиск-по-телефону). Главный риск проекта снят.

- Фикстуры (реальные initialState+HTML): tests/fixtures/autopodbor/2gis/
  listing (12 ломбардов), card (Ломбардико: сайт+телефон), phone (холдинг).
- Карта JSON-путей: pagination-список результатов + entity.profile (имя/
  город/рубрика/сайт/телефоны). Идти ТОЛЬКО по pagination, не по ключам.
- Вскрыт рецепт рендера museum double-tap: гейт «обновите браузер» дважды
  кодирует return_url (%20→«20», отсюда старый мусор). Обход — двойной заход.
- Findings: docs/superpowers/findings/2026-07-05-2gis-initialstate-spike.md
- План реализации (8 фаз): docs/superpowers/plans/2026-07-05-self-render-*.md
- Спека v4 обновлена: риск initialState снят, double-tap задокументирован.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 18:30:19 +03:00
Дмитрий 13c8620dc5 feat(autopodbor): Яндекс-разворот по телефонам на шаге 2 (рядом с 2ГИС)
Для застройщиков, которые сидят только в Яндексе. По каждому номеру ищем в
Яндекс.Картах, но список рыхлый — поэтому КАЖДУЮ карточку открываем и оставляем
только те, где искомый номер реально есть. Это отсекает «Похожие места рядом»,
рекламу и рыхлые совпадения по бренду. Оба справочника объединяются
(CompositePhoneReverse), дубли по домену схлопываются.

Переиспользована готовая механика Яндекса: render-yandex-list.cjs (список) +
DirectoryParser::parseYandexCard (карточка). Браузерные адаптеры за интерфейсами
(YandexPhoneSearch/YandexCardReader) — логика TDD-ится на фейках; порог общей
линии считается по подтверждённым карточкам.

За отдельным тумблером AUTOPODBOR_PHONE_REVERSE_YANDEX (по умолчанию ВЫКЛ,
требует phone_reverse). Юнит 267/267, feature 150/150. План и находки —
docs/superpowers/{plans,findings}/2026-07-05-*.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 09:30:27 +03:00
Дмитрий 44610812a8 feat(autopodbor): разворот по телефонам на шаге 2 (сайты проектов застройщика)
По личным телефонам фирмы обратным поиском в 2ГИС вскрываем связанные карточки
холдинга (проекты застройщика) — забираем их сайты и телефоны, цепочкой «до талого».
Общий номер (у >4 фирм) пропускаем; visited-set + потолок карточек; уже известную
главную карточку не дублируем. Защита от AutoMoney: реклама отсечена парсером,
общий номер обрублен до разворота, находки не клеятся молча.

За тумблером AUTOPODBOR_PHONE_REVERSE (по умолчанию ВЫКЛ). Юнит 259/259, feature
146/146. Решение и план — в docs/superpowers/{findings,plans}/2026-07-05-*.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 07:55:03 +03:00
Дмитрий 89a4df2078 docs(autopodbor): находки похожесть-погода и конкурент-из-элементов + план Часть 1
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 18:14:54 +03:00
Дмитрий 208601c5fa docs(автоподбор): план реализации переноса движка шага 2 (TDD, 12 задач)
12 задач по TDD: тумблер+is_federal, CompetitorGeo, DomainKinship (близнецы
НЕ сливать), PlatformClassifier, отпечаток+запросы EXA, AnchorGate,
FootprintSearch, SiteOpener, DeepStudyCollector (маппинг 1:1 со словарём
чипов песочницы), развилка по тумблеру, DI, приёмка на 5 против ЭТАЛОНа.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 22:32:17 +03:00
Дмитрий f295351504 feat(автоподбор): Конкурентное поле — финальный проход склейки, вскрыть всё, группы дублей, телефоны, отсев рекламных номеров 2ГИС
Шаг 1 «Конкурентное поле» — чекпоинт:
- Финальный проход «Найти и объединить дубли» на поле и предложениях; клиент решает по каждой группе, ничего не склеиваем молча.
- Тихая склейка только по сайту и коду справочника; телефон и людный номер у более чем 4 фирм — на решение клиента.
- Колонка phones jsonb; телефон-дубли видны уже на «Предложениях».
- Sonar даёт только имя и тип, сайт всегда через EXA; вскрываем все карточки без гейта «есть сайт».
- Яндекс-карточки через локальный Playwright параллельно; рубрика из заголовка идёт в описание.
- Фикс рекламных номеров: 2ГИС-парсер разбирает телефоны пообъектно и выкидывает рекламные кнопки с platforms/caption чужих фирм.

Тесты: автоподбор 224/224 unit, 105/105 feature; фронт-спеки зелёные. Времянки app/scripts/diag-*/probe-* в гит не входят.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 17:34:03 +03:00
Дмитрий 416a74e419 docs(bot): план реализации ядра ИИ-бота Jivo (14 задач TDD)
По спеке 2026-07-02-jivo-ai-support-bot-design: webhook + FTS-поиск
по инструкции + YandexGPT Lite + эскалация + журнал + тест скорости.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 18:39:56 +03:00
Дмитрий b2699f0d84 feat(y360): баланс почты Яндекс 360 — ручной ввод + кнопка Пополнить
email — денежный сервис; сумма вписывается в админке «Система» (Yandex360BalanceStore),
светофор по порогам, кнопка «Открыть оплату»/«Пополнить» → admin.yandex.ru/products.
Робот-скрейпер отклонён (SPA Яндекса враждебен ботам + автопополнение защищает баланс).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 13:34:13 +03:00
Дмитрий 8bff267c11 docs: снимок 02.07 — launch-gate ЯДРО на проде + автоподбор в воркстри + промт продолжения
Консолидированное состояние двух треков (launch-gate выкачен на liderra.ru core-only;
автоподбор работает только в воркстри) + квирки окружения/тестов + промт для
восстановления контекста после компакта.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 13:22:53 +03:00
Дмитрий f2ca0c8cb0 feat(биллинг): баланс блокирует запуск проекта, а не создание — единый гейт
Создавать проекты можно всегда; баланс проверяется в момент ЗАПУСКА
(создать-и-запустить / запустить / возобновить одиночно и пачкой / автоподбор)
под замком на клиента (без гонок). Не хватает — проект остаётся на паузе с
меткой preflight_blocked_at, клиенту сообщение в рублях (сколько пополнить).
Групповой запуск «сколько влезло». Нет активного тарифа на дату → запуск
запрещён (fail-closed). Гейт реквизитов первого проекта добавлен и в автоподбор.

- LaunchBalanceGate — единый гейт вместо 3 копий preflight (ProjectController
  store/update, AutopodborController), под DB::transaction + lockForUpdate(Tenant).
- ProjectService::create($launch) + новый setActive(); bulk resume «сколько влезло».
- AutopodborProjectCreator: пачка в транзакции через общий ProjectService::create.
- Идемпотентность box/phone_type миграций автоподбора (Schema::hasColumn guard).
- Тест-инфра: afterRefreshingDatabase восстанавливает месячные партиции.

Тесты фичи 40/40 зелёные. Спека и план — docs/superpowers.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 11:07:22 +03:00
Дмитрий 800ca897b4 docs(plan): мониторинг внешних сервисов — план реализации (10 задач, TDD)
Разбивка на 10 задач по TDD: LivenessProbe/DTO, 4 пробы живости
(SMTP/ЮKassa/Jivo/капча), джоба пишет строки живости, email-алерт по
edge-trigger, фронт со статус-текстом. Схема БД не меняется.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 07:35:49 +03:00
Дмитрий 172b79eae0 docs(автоподбор): снимок 02.07 — живой движок работает end-to-end + ускорение 2ГИС + промт продолжения
Зафиксировано состояние: фича Stage 1+2 собрана, живой сбор проверен (прогон #12 ~8 мин, 139 фирм 2ГИС,
14 сохранено, 300₽), коммит 8869cf51, квирки (лимит xfetch, EXA OS-env, очередь-локально, node-путь) и
промт для восстановления контекста. Детали движка — в памяти project-autopodbor-engine-live-working-2026-07-02.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-02 06:30:03 +03:00
Дмитрий 7f2e896c38 feat автоподбор параллельная загрузка карточек справочника пачками по 4
Замер боевого лимита xf4.ru показал безопасный потолок 4 одновременных запроса — выше идут 429 Too Many Requests, плюс есть лимит за окно времени. Сбор до 25 карточек филиалов в XfetchDirectoryFetcher грузился по одной секунды на каждую — узкое место шага 2.

Новый интерфейс BatchPageFetcher + XfetchClient::htmlBatch грузит страницы порциями по services.xfetch.concurrency дефолт 4, параллелит внутри порции через Http::pool, ретраит пустые и 429. XfetchDirectoryFetcher берёт карточки одной пачкой если загрузчик batch-совместимый, иначе по одной. Два справочника шага 2 оставлены последовательно намеренно — иначе 2x4=8 за лимитом. Число выносится в XFETCH_CONCURRENCY без деплоя.

По TDD. Бэкенд автоподбора 258/258, Pint чисто.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 18:52:18 +03:00
Дмитрий 71d47ed4cb docs(prod): маркировка БОЕВОЙ ПРОД + сводка 01.07 (ЛК crm.lead.store, чистка базы)
- ПИЛОТ.md: горящий баннер + датированная сводка 01.07 (свап ЛК, приём лидов, снос базы, фикс)
- ЭТАЛОН.md: пометка «локальная dev, боевой = ПИЛОТ, не путать»
- .claude/hooks/prod-db-pointer.mjs: горящий блок БОЕВОЙ ПРОД в SessionStart
- план: прогресс Фаз 4/0/5/6
- cspell-words.txt: +14 валидных слов (gzk, ВТБ, ОКПО, автобэкапы и др.)

NB: LEFTHOOK_EXCLUDE=larastan — предсуществующий чужой tests/Feature/Sales/SalesClientsIndexTest.php.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 17:01:55 +03:00
Дмитрий f8ac614aa4 docs(автоподбор): спека + план фичи сверки находок + файл состояния Stage 1 и промт продолжения
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 15:47:12 +03:00
Дмитрий 1c12d040fc docs(prod): план чистки прода + замены ЛК поставщика на crm.lead.store
Обоснование (spec) + детальный пошаговый план с полным откатом:
- факты живой базы (7 тест-клиентов, деньги синтетические), keep/delete
- разведка crm.lead.store: визуал новый, начинка (эндпоинты/API/формат лида) та же
- порядок: свап ЛК -> проверка проектов по алгоритму -> чистка кабинета -> чистка базы
- маркировка БОЕВОЙ ПРОД во всех доках + стартовый хук

Секреты/ПДн не включены (почты замаскированы, секрет вебхука как <секрет>).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 12:36:09 +03:00
Дмитрий f04c82e5ee fix(автоподбор): дедуп ДО отсева агрегаторов + файл состояния/промт продолжения
Отсев агрегаторов шёл по СЫРЬЮ до склейки — gpt-4o-mini залетало 227 позиций
с дублями (Ломбардико ×6). Порядок в FindCompetitorsAssembler: сначала
склейка/дедуп, потом отсев — классифицируем ~90 уникальных, а не 227.

Плюс файл состояния сессии (что сделано/осталось) + промт восстановления
контекста для компакта. TDD, 234/234 зелёные. НЕ прод.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:27:26 +03:00
Дмитрий 2975bf7967 feat(автоподбор): масштаб и чистота шага 1 — пул EXA, очередь-throttle, батч агрегатора, узкие рубрики, дедуп по словарю
Масштаб (лимиты внешних сервисов — на КЛЮЧ, общий на всех клиентов):
- EXA: параллельный пул findSites (concurrency=5) вместо 40 запросов по одному.
- Агрегатор: батч вместо ~90 запросов по одному + чанкинг по 40 и длинный
  таймаут 90с (большой список gpt-4o-mini не успевал в 30с).
- Очередь autopodbor + WithoutOverlapping на всех 3 джобах — глобальный
  потолок = число воркеров; assertNoInFlight держит один подбор на клиента.

Чистота выдачи (универсально, без зашитых ниш):
- Анализатор: узкие рубрики, запрет зонтичных слов («финансовые услуги» тащила
  юрфирмы/банки).
- Дедуп: ключ имени минус слова из СЛОВАРЯ рубрик прогона («Яричъ Ломбард»→«Яричъ»);
  слово-категория из данных, не из списка. Гард: склейка только если остаётся
  ровно один слово-токен (генерики вроде «займы под залог» не трогаем).

TDD, 233/233 (unit+feature) зелёные. НЕ прод.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:18:09 +03:00