Commit Graph

397 Commits

Author SHA1 Message Date
Дмитрий 40aa3e878a chore(security): разрешить сторожу демо-номера +7 999 000-XX-XX
Диапазон 999-000 у операторов не существует — заведён специально для
демо-данных и съёмки инструкций, чтобы в кадр не попал настоящий номер.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-14 04:17:37 +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
Дмитрий 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
Дмитрий bd5c16cfbe chore: gitleaks — исключение для примера телефона в докблоке бота
Пример формата в ContactCapture.php остался в истории коммита 6f72367b
и блокировал push. Не ПДн — образец для распознавания; в рабочем коде уже
замаскирован. Настоящие номера сторож ловит по-прежнему.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:54:16 +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
Дмитрий dfb7b8bc34 fix(supplier): миграция supplier_order_checks — pgsql_supplier + инлайн-GRANT crm_supplier_worker
По 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>
2026-07-10 05:16:08 +03:00
Дмитрий a68363894c chore(security): allowlist проверенных ложняков gitleaks/lychee (379→0)
gitleaks full-history давал 379 находок, ВСЕ ложняки (проверено):
- 360 RU-телефонов в тест-фикстурах 2ГИС, демо bylo-stanet, findings-доках, autopodbor-превью;
- 18 «api-key» = публичные фронт-токены 2ГИС в захваченных HTML-фикстурах;
- 1 «webhook-secret» = плейсхолдер в .env.example (шаблон);
- 2 = демо-номер Красноярска из docblock удалённого HtmlPhoneScanner (в текущем коде нет).
Дописаны path/regex allowlist (те же категории, что уже были: tests/seeders/factories/docs).

lychee: «support@liderra.ru» в кавычках-ёлочках ошибочно резолвился как file://-путь — исключён.

Настоящих утечек не было; история не переписывалась. Мои коммиты фичи gitleaks-чисты.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 04:58:47 +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
Дмитрий 4842c728c8 fix(supplier): правки по код-ревью — матч наших строк по external_id, shouldBeOff без активных, sync_run_id
- 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>
2026-07-09 20:55:47 +03:00
Дмитрий b94c4152f8 feat(supplier): сторож дедлайна 20:00/20:40 — письмо если робот не успел
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 20:36:16 +03:00
Дмитрий ee93ebf551 feat(supplier): SupplierOrderVerifier::diff — чистая сверка заказа с живым кабинетом
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 19:58:42 +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
Дмитрий 1db2fe6581 docs(legal): Яндекс.Метрика/Вебвизор в политике конфиденциальности (ПДн маскируются)
Раздел «5. Хранение и защита»: упоминание записи сессий Вебвизором с маскировкой
персональных данных контактов, обработка на серверах в РФ.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:46:57 +03:00
Дмитрий 2d56ad0d48 feat(metrika): ym-disable-keys — поля ввода телефона/имени в кабинете (диалог+фильтр)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:45:39 +03:00
Дмитрий 6b5fa09330 feat(metrika): маскировка ym-hide-content — имя+телефон в карточке сделки и канбане
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:45:21 +03:00
Дмитрий 899e166e88 feat(metrika): маскировка ym-hide-content — телефон+комментарий в таблице сделок; телефон убран из aria-label (id вместо ПДн)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:45:00 +03:00
Дмитрий 0e502c4072 feat(metrika): hit только для маршрутов кабинета (layout=app)
router.afterEach: при входе на layout=app лениво грузит Метрику и шлёт hit(fullPath).
Вход/публичные/админка/портал продаж — без Метрики. + убраны лишние @ts-expect-error
в тесте (type-check чист).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 04:38:45 +03:00
Дмитрий 05b2dff979 feat(metrika): ленивый загрузчик + hit с идемпотентностью (TDD, 3 теста)
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>
2026-07-08 04:35:44 +03:00
Дмитрий f95d572b98 feat(metrika): конфиг счётчика портала + meta в HTML-shell (условно)
По образцу 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 04:32:57 +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
Дмитрий 63feb1c09b docs(пилот): вечерний выкат 07.07 — сторож поставщиковых тревог понимает режим
Потоп ~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>
2026-07-07 19:50:55 +03:00
Дмитрий 9f0f5cfc59 feat(supplier): аварийный spike-детектор ручной очереди в incidents:watch-failures
Блок 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>
2026-07-07 19:07:41 +03:00
Дмитрий 46607d702a fix(supplier): FailoverProjectChannel не шлёт per-project CRITICAL письма
Убраны письма manual_required и failover_to_form (по проекту, без троттла) —
источник потопа ~116 писем на ops@ 07.07. Строка в ручной очереди остаётся
(запись видна в админке). Зависимость Mailer удалена из канала и DI-привязки.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 19:01:19 +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
Дмитрий 182f18e370 docs: спека сторожа поставщиковых тревог, понимающего режим
Устраняет потоп ~116 писем на ops@ 07.07: убираем per-project письма
FailoverProjectChannel, добавляем аварийный spike-детектор ручной очереди
в incidents:watch-failures. batch остаётся рабочим режимом.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:36:06 +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