Commit Graph

2983 Commits

Author SHA1 Message Date
Дмитрий e969b7700a chore(chat): убрать настройки Jivo — чат теперь свой; экскурсии выключены
Config: jivo_bot -> bot (убраны неиспользуемые webhook_secret/outbound_url/token
- транспорт Jivo уже удалён), jivosite убран целиком (виджет больше не нужен).
Env: JIVO_BOT_* -> BOT_*; JIVO_WIDGET_ID/VITE_JIVO_WIDGET_ID удалены.
tours_enabled выключен по умолчанию (BOT_TOURS_ENABLED=false) — экскурсии
«Показать на портале» уходили не по теме вопроса (живая проверка 13.07.2026),
чинить релевантность отдельно; сторож-тест не даёт включить незаметно.

Заодно убраны обнаруженные хвосты того же виджета, оставшиеся от прежних
задач: JivoLivenessProbe (класс+тест+регистрация в мониторинге внешних
сервисов), плитка «JivoSite» в админ-дашборде, встроенный скрипт виджета
в welcome.blade.php, осиротевший тест resources/js/.../JivoWidget.vue
(компонент уже был удалён ранее), declaration VITE_JIVO_WIDGET_ID.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 08:32:21 +03:00
Дмитрий ac0c8f74fc feat(chat): окошко чата на портале и лендинге вместо виджета Jivo 2026-07-13 08:09:51 +03:00
Дмитрий 5766e37e71 fix(chat): не читать свойство у пустой реплики при сборе письма
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:56:13 +03:00
Дмитрий dddf7fbd83 feat(chat): в письме владельцу вся переписка и откуда клиент 2026-07-13 07:54:37 +03:00
Дмитрий 06ad38ecb7 test(chat): сторож хранилища счётчиков ловит и пропажу ключа
Прежний сторож проверял только «не file»: без ключа config вернул бы null,
и проверка прошла бы впустую. Теперь ключ обязан быть redis или array,
и сам класс обязан брать именно это хранилище, а не общий кэш приложения.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 07:49:20 +03:00
Дмитрий 9e0614c8a5 fix(chat): счётчики переезжают на атомарный Redis — файловый кэш терял прибавки
Файловый Cache::increment() под параллельной нагрузкой (10 x 30) досчитал 77 из 300 —
защита от накрутки счёта (пауза/час/IP/сутки) продавливалась параллельным скриптом.
ChatRateLimiter теперь берёт своё хранилище (services.jivo_bot.limiter_store, по
умолчанию redis, в тестах array) и считает через атомарные add()+increment(), а не
через фасад RateLimiter на общем кэше. Ручная проверка на живом Redis: 300 из 300.
2026-07-13 07:47:20 +03:00
Дмитрий 09b980f5ac feat(chat): три уровня счётчиков + письмо-тревога о суточном потолке
Защита денег для публичного чата (лендинг открыт всему интернету,
каждый ответ бота ~0,44 руб): пауза 2 сек между репликами, лимит
вопросов в час (гость 20 / вошедший 60), потолок реплик на разговор
(40), лимит по IP в час (60) и общий суточный потолок ответов (1500)
с одноразовым письмом-тревогой владельцу при пробитии. Отказ всегда
вежливый — клиент получает текст, а не молчание.
2026-07-13 07:29:47 +03:00
Дмитрий b4c4900635 feat(chat): опрос ответов и восстановление переписки 2026-07-13 07:17:38 +03:00
Дмитрий cfd26e38ef feat(chat): свой приём сообщений вместо вебхука Jivo
Заменяет JivoBotController+JivoBotClient на ChatController (POST /api/chat/message)
и ProcessJivoMessageJob на ProcessChatMessageJob. Реплику клиента теперь пишет
контроллер и возвращает её номер (message_id); джоба получает этот номер и берёт
историю строго до него, не отправляя ответ во внешний Jivo — он ложится в bot_dialogs,
откуда его заберёт своё окошко (Задача 3). Мозг бота не менялся.

Спека: docs/superpowers/specs/2026-07-13-own-chat-widget-design.md §5
2026-07-13 07:04:38 +03:00
Дмитрий 197e771a4f feat(chat): журнал разговоров переезжает с Jivo на свой чат
jivo_chat_id -> chat_id + добавлены source/user_id/ip (спека
2026-07-13-own-chat-widget-design §5). Историческая миграция создания
таблицы не тронута.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 06:45:54 +03:00
Дмитрий 9cd12f179f fix(bot): замены заявки НЕТ — недозвон не брак (правило владельца 13.07.2026)
В трёх статьях было написано «не берёт трубку → заменим». Это ошибка: замену
мы не делаем, и бот честно обещал её клиентам.

Тон тонкий, тему сглаживаем:
- не обещать замену/возврат («заменим», «вернём деньги») — AnswerGuard::promisesReplacement;
- но и не рубить дверью («замена не полагается», «деньги не возвращаются») — тоже режем;
- мягко: недозвон — обычное дело; совет СМС → звонок позже → мессенджер; спорный
  случай на support@liderra.ru, специалист разбирает индивидуально, без обещаний;
- не выдумывать срок ответа поддержки (она 24/7).

Статьи: zamena-zayavki.md переписана, поправлены kak-rabotat-s-nomerom.md и sdelki.md.
Два старых теста охраняли прежнее (неверное) правило — переписаны под новое.

Тесты 130/130. Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:59:11 +03:00
Дмитрий c320e095e1 fix(bot): ювелирная доводка после второго круга судей
- «скидка ЗА ОБЪЁМ» больше не считается просьбой скидки: бот отвечает про
  ступени тарифа, а не уводит к специалисту.
- «где посмотреть мою ступень / мой лимит» — вопрос про портал, а не про
  личные данные клиента: эскалация снята.
- Цена ↔ ниша/регион: правило по СМЫСЛУ (AnswerGuard::priceTiedToNiche).
  Враньё «цена зависит от ниши» возвращалось ПЯТЬЮ формулировками —
  регулярка на каждую не работает. Честное «цена НЕ зависит от региона»
  сохраняется.
- Вырезано: «на тысячу выйдет от 2 до 20 заявок» (реально максимум 4),
  «работаем не со всеми нишами» / «есть направления, с которыми мы не
  сотрудничаем» (ограничений по нишам НЕТ), «не берёт трубку — забей»
  (такую заявку МЕНЯЮТ), «баланс на паузе не потратится», выдуманные сроки
  зачисления по счёту.
- Промпт: цена только от ступени объёма; запретных ниш нет.
- Статьи: тронута ТОЛЬКО строка topics (синонимы поиска), тексты не менялись.

Тесты 126/126. Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 05:35:11 +03:00
Дмитрий bd918c1dce fix(bot): держим нить разговора и не теряем телефон клиента
Прогон 46 диалогами (250 реплик подряд в одном чате) вскрыл то, чего не видел
прогон отдельными вопросами:

- Клиент оставил телефон → бот не отвечал ВООБЩЕ: письмо в поддержку слалось
  синхронно внутри ответа, SMTP не отозвался → упала вся задача (tries=1).
  Теперь номер в чате = заявка на звонок: принимаем всегда (даже без просьбы
  бота), письмо уходит через очередь и не может уронить ответ.
- Бот «забывал» себя: сторож резал цену, взятую из истории чата, ответ пустел
  → эскалация. Числа из своих прошлых ответов теперь разрешены; память чата
  3 → 5 пар реплик; в эскалационную ветку тоже передаётся история.
- AnswerGuard: + телефон поддержки (его нет), + юр-гарантии в новых оборотах
  («Да, законно», «Штраф не прилетит»), + враньё «на паузе списаний нет»,
  + выдумки про 18:00/ночь, + «цена зависит от ниши»; снимает висящее «Но…»
  и голое «Правильно, не будут» после вырезанной лжи.
- Промпт: не повторять сказанное, не поддакивать клиенту, без нумерованных списков.
- pravilo-18-00.md: 18:00 — граница для ИЗМЕНЕНИЙ, а не конец приёма заявок.

Тесты 120/120. Бот НЕ на проде.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 04:52:09 +03:00
Дмитрий 4ad819f79f chore(bot): замаскировать пример телефона в докблоке ContactCapture
Сторож секретов (gitleaks) видел в докблоке живой российский номер — это был
ПРИМЕР формата для распознавания, не ПДн. Заменён на +7 9XX XXX-XX-XX.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:53:01 +03:00
Дмитрий c5125e981e feat(bot): выбор модели + три замка безопасности + инструкция 51 статья
Прогнал 250 вопросов через настоящий вебхук шесть раз и сравнил четыре модели
вслепую силами судей-агентов. Итоги — в docs findings.

Модель. Появился переключатель services.jivo_bot.llm: yandex (данные не покидают
РФ) либо aitunnel (зарубежные модели через российский шлюз). По слепому сравнению
Claude Haiku 4.5 выиграл финал у Gemini 157:24 и у обеих моделей Яндекса: он
полнее, точнее по кнопкам и цифрам и говорит как человек, а не справочник.

Три замка, потому что живая модель = самоуверенная модель:
- PersonalDataMask — телефоны и почта вырезаются ДО отправки в любую модель
  (клиент оставляет номер в чате; за границу ПДн уходить не должны).
- ChatTextCleaner — markdown и смайлики (Jivo их не рисует): было 191 ответ
  с разметкой, стало 0.
- AnswerGuard — режет юридические гарантии («вас не оштрафуют», «мы гарантируем»,
  «не нарушаем закон») и ЛЮБУЮ цену, которой нет в выданных статьях. Пусто после
  чистки → зовём живого специалиста.

Поиск по инструкции переписан: выбрасываются вопросительные слова (из-за них
«какие у вас тарифы» находило выгрузку в Excel), ранжирование сперва по заголовку
и синонимам, выдача — три РАЗНЫЕ статьи и каждая целиком, плюс починка опечаток
(«каг сазадь праэкт») и учёт прошлой реплики в коротких «а это платно?».

Инструкция: 40 → 51 статья. Новые — по фактам владельца: бесплатный тест и
подарок 1000 ₽, «мы канал, а не реклама», СМС → через час звонок, средние
показатели (8–12 из 100 разговаривают, ещё 5–7 по СМС), номер не вернётся
30 дней, замена брака, вывод остатка, поддержка 24/7, удаление данных, ниши.
Вычищено враньё: минус баланса, «списали без заявки», пауза вечером, выходные,
«Вся РФ», статус «Отказ» (его нет — есть «Не реализовано»), amoCRM, платный
повторный сбор, нарастающий итог тарифных ступеней.

Тесты: 106 из 106. Каждая найденная тупость закреплена тестом.

НЕ НА ПРОДЕ. Выкат — только по прямой команде владельца.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 20:44:48 +03:00
Дмитрий beb0528612 fix(bot): продукт, 50% номеров и договор-оферта — по живым замечаниям владельца
1. 🔴 МОЙ КОСЯК: на «как получить номер человека, заходящего на сайт» бот отвечал
   «нельзя» — то есть отговаривал клиента от того, ЧТО МЫ И ПРОДАЁМ. Владелец:
   «как нельзя, а мы чем занимаемся?!». Теперь: «это ровно то, что делает Лидерра —
   создайте проект, укажите сайт, контакты пойдут в Сделки».
2. «Все ли номера с источника цепляет система?» — нет, ПРИМЕРНО ПОЛОВИНУ: передавать
   можно только абонентов с согласием (требование закона). Формулировка владельца.
3. «Как подписать договор с юрлицом?» — у нас публичная ОФЕРТА, подписывать нечего.
   Слово «договор» убрано из стоп-тем; новая статья: оферта (/legal/offer), политика,
   возврат + закрывающие документы (счёт → оплата → акт, УСН без НДС).
   Деньги/жалобы/претензии — по-прежнему живой специалист.

Все три проверены ЖИВЬЁМ через настоящий вебхук чата (а не в обход очереди).
Инструкция: 41 статья. Pest бот 58/58.

🪤 Урок: после правки кода бота ОБЯЗАТЕЛЬНО перезапускать queue:work — воркер
кэширует бут. Владелец видел старые ответы, пока я радовался зелёным тестам.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 16:13:34 +03:00
Дмитрий aa2ceb8a13 fix(bot): 9 тупостей из живого прогона владельца (журнал bot_dialogs 127-182)
Владелец прогнал бота вживую: «там тупежа жуть какая-то». Разобрал журнал —
каждая тупость закрыта тестом.

1. 🔴 «откуда приходят номера телефонов?» — бот плёл про Конкурентное поле вместо
   честного ответа (операторы + согласие абонента). Статья усилена.
2. «как получить номер человека, зашедшего на сайт» — теперь прямой ответ: НЕЛЬЗЯ,
   мы не ставим счётчик на чужой сайт и не снимаем посетителей.
3. «законно ли брать телефоны с сайтов конкурентов» — да: это контакты фирм
   из открытых источников, не персональные данные граждан.
4. Формат сайта и номера в проекте — бот сдавался, хотя это в статье. Дописано явно.
5. Клиентское слово «выгрузка» (= поставка заявок) — новая статья «Когда пойдут
   заявки и когда прекратятся».
6. 🔴 «могут ли работать 2-3 человека?» — ЛОЖНАЯ эскалация: голое слово «человек»
   стояло в стоп-темах. Стоп-паттерн переписан: ловим просьбу позвать ЖИВОГО
   (позовите/соедините/живой человек), а не любое упоминание. Плюс статья про команду.
7. «а что ты вообще знаешь?» — бот нёс чушь про паузу проектов. Новая статья-оглавление.
8. «придут ли СМС?» — теперь прямо: СМС/Телеграм/пуш нет, только почта и колокольчик.
9. 🔴 ИИ начинал ответ с «в инструкции этого нет», а СЛЕДОМ отвечал верно — и мы
   глушили весь ответ эскалацией. Теперь отговорку выкидываем; в промпт добавлено:
   «нет» и «нельзя» — это ТОЖЕ ответ, а не отсутствие ответа.

Проверено вручную стоп-темы: «позовите живого человека», «какие операторы», «поставщик»
— по-прежнему эскалация. Pest бот 54/54 (12 новых тестов), Vitest каталога 8/8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 15:30:49 +03:00
Дмитрий 47afefbb10 feat(bot): цены бот тянет сам из настроек — вручную актуализировать больше не надо
Вопрос владельца: «а цены в нём надо актуализировать руками или он сам тянет?»
Ответ был неприятный: руками. Цифры (300/50 руб, тарифные ступени) стояли текстом
в статьях — поменяли бы цену в админке, бот продолжил бы врать про деньги.

Теперь в статьях стоят метки {{цена_подбора}}, {{цена_изучения}}, {{тарифы}},
а сервис LivePrices подставляет живые значения из system_settings и pricing_tiers
В МОМЕНТ ОТВЕТА — без переиндексации базы знаний. Нет цены в настройках — метка
не протекает клиенту, бот отсылает к экрану, где цена написана всегда.

Из текстов экскурсий цифры цен тоже убраны (фронт не знает БД): шаг подсвечивает
строку с ценой в самом окне — она всегда актуальная.

Проверено живьём: поменял цену в настройках на 450 — бот сразу назвал 450.
Pest 42/42 (3 новых теста), Vitest каталога 8/8.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 14:06:04 +03:00
Дмитрий fddba0a616 feat(bot): шаг «проверьте ссылки перед переносом в поле» + ревизия инструкции на грабли
Владелец: «клиентам надо подсказывать — от того, как они сформируют поле,
зависит весь дальнейший поиск». Плюс просьба вычитать инструкцию и экскурсии
на враньё и ловушки.

Добавлено предупреждение (и в экскурсии collect-competitors/proposals, и в статьи):
перед переносом конкурента в поле открыть сайт и справочник ссылками — тот ли это
бизнес, тот ли город, живой ли адрес. По грязной карточке мы найдём не те источники,
а изучение каждого конкурента стоит 50 руб.

Ревизия (агент сверил 36 статей и 31 экскурсию с кодом) — закрыто:
1. Вебхук обещал автоматическую отправку событий, которой НЕТ. Теперь честно:
   сейчас только тестовый запрос; заявки забирать по API-ключу или выгрузкой.
2. Колонка «Город»: оговорка, что в редком запасном случае там регион проекта.
3. Отчёты: фильтры «Проект»/«Менеджер» пока только «Все» — сказано прямо.
4. Цены автоподбора: «актуальная цена всегда в самом окне».
5. Статьи «Портал на телефоне» и «Откуда заявки» больше не ведут на чужую
   экскурсию про сделки (у них своей нет — кнопки просто не будет).
6. Синонимы: «портал на телефоне» и «заявки в свою CRM» находили не то.

Pest 39/39, Vitest 1227/1227. Живой прогон вопросов — отвечает верно.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:50:19 +03:00
Дмитрий 3e9ce2091e fix(tours): карточка экскурсии не уезжает за экран и не тонет под виджетом чата
Владелец застрял на шаге «Цена» (экскурсия про сбор конкурентов): карточка встала
ниже видимой части окна и вдобавок была накрыта виджетом Jivo — кнопку «Далее»
нажать было нечем, экскурсия превращалась в тупик.

1. Позиция карточки прижимается к экрану (clampTop): она всегда целиком видна,
   какой бы низкой ни была цель шага.
2. z-index экскурсии поднят выше Jivo (виджет рисует себя поверх всего).

Оба бага сначала воспроизведены тестами. Vitest 1227/1227. Живьём: все 4 шага
экскурсии collect-competitors — карточка в экране, «Далее» кликабельна.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:18:18 +03:00
Дмитрий d1f3b0581e fix(tours): ссылка бота на экскурсию переживает вход и не перебивается приветственным туром
Владелец нажал «Показать на портале» в чате и не смог посмотреть: его выкинуло
на «Вход», после входа увело на дашборд (адрес экскурсии потерян), а сверху сел
приветственный тур «Добро пожаловать в Лидерру».

1. LoginView теперь возвращает клиента по redirect (роутер его уже сохранял) —
   /dashboard?tour=dupes открывается сразу после входа. Чужой адрес в redirect
   игнорируется (защита от подмены ссылки).
2. WelcomeTour молчит, если в адресе есть ?tour= — экскурсия из чата важнее.

Оба бага сначала воспроизведены тестами. Vitest 1225/1225. Проверено живьём:
гость → ссылка бота → форма входа → сразу «Шаг 1 из 4» нужной экскурсии.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 13:10:00 +03:00
Дмитрий 9ff19f3cc3 @
feat(bot): всеобъемлющая инструкция (36 статей) + 31 узкая экскурсия, которая открывает окна

Живой урок владельца 12.07.2026: «дубли чистятся в 2-х местах, а бот выдумал третье»,
«стыдно такого бота показывать», «видео показываем полное, а я спрашиваю про одно —
надо резать», «надо не только кнопку показывать, а и что за ней».

База знаний: 17 обзорных статей → 36 узких по темам. Портал обойдён целиком
(5 агентов): все экраны, окна, подсказки «(?)», тексты ошибок, внутренние правила.
Новое, чего бот не знал: дубли в ДВУХ местах (Поле и Предложения) + окно «источник
уже есть»; правило 18:00; почему приходит меньше лимита; оплата по счёту и акты;
списания; безопасность и 2FA; отчёты; массовые действия; регистрация.

Экскурсии: 15 → 31, каждая на свою тему (3–7 шагов). Раннер GuidedTour научен
шагу `open` — сам открывает окно/вкладку и подсвечивает то, что ВНУТРИ
(окно дублей, форму подбора, диалог пополнения, панель тарифов, карточку сделки).
99 новых якорей data-tour расставлено по всему порталу (4 агента).

Поиск: веса в tsvector (заголовок A / синонимы B / текст C) — иначе на «чистка
дублей» первой всплывала статья «Списания» из-за мимоходного упоминания «поля».

Тесты: Pest бот 39/39, Vitest 1222/1222. Новые сторожа: у каждой статьи есть
экскурсия; экскурсия не короче 3 и не длиннее 7 шагов; каждая цель экскурсии
реально существует в разметке; экскурсии про действия обязаны открывать окно.
Живая проверка в браузере: экскурсия про дубли сама открыла окно объединения
и перешла на второе место чистки.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@
2026-07-12 12:56:22 +03:00
Дмитрий 561096cc84 feat(tours): 15 полноценных экскурсий вместо 6 обрубленных + раннер водит по экранам
Претензия владельца 12.07.2026: «видео мало и они обрубленные».

Причина «обрубленности» найдена живым прогоном: раннер НЕ переключал экраны —
шаг про «Проекты» показывался поверх «Биллинга». Фикс GuidedTour: при переходе
на шаг с другим route зовём router.push (TDD-тест «шаг на другом экране»).

Каталог: 6 → 15 экскурсий, все минимум по 3 шага (тест-сторож в TourCatalog.spec):
start (4 шага), create-project (6), top-up-balance (6), tariffs (4), balance-pause (4),
deals (4), kanban (3), konkurentnoe-pole (6), change-source (4), security (4),
notifications (3), import (4), api (3), dashboard (4), support (3).

Якоря data-tour расставлены по портале (3 агента): биллинг (кошелёк, запас,
ступени, вкладки, сумма, способ оплаты), проекты (фильтры, карточка, меню, тип
источника, лимит, регионы, дни), сделки (фильтры, таблица, экспорт), канбан,
импорт, дашборд, настройки (вкладки, 2FA, матрица уведомлений), Поле (собрать,
вручную, дубли, вкладки, карточка, легенда, изучение), помощь (FAQ, форма).

Каждая из 17 статей инструкции получила свою экскурсию (frontmatter tour:).

Vitest 1216/1216, Pest бот 46/46, живая проверка в браузере: экскурсия сама
переходит между экранами и подсвечивает цели.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:48:46 +03:00
Дмитрий f33d1a1638 feat(jivo-bot): эскалация = просьба телефона + письмо с контактом в поддержку
Правило владельца 12.07.2026: оператора в чате может не быть, поэтому бот НЕ
переводит диалог (INVITE_AGENT убран из авто-флоу). Не смог ответить (личный
вопрос, стоп-тема, незнание) → просит номер телефона → как только клиент его
оставил, контакт уходит письмом в поддержку, а клиенту — «Спасибо, передал».

- ContactCapture: распознаёт номер в любом формате (+7/8/скобки/дефисы) → 79161234567
- BotContactRequestMail + шаблон: телефон, вопрос-на-котором-спасовал, id чата,
  переписка целиком, время → services.support.email
- ProcessJivoMessageJob: ждём контакт, если прошлый ответ бота просил номер;
  номера нет (клиент передумал/спросил другое) — отвечаем как обычно
- тексты эскалации переписаны под сбор контакта

TDD: BotContactCaptureTest (4 новых) + обновлены 4 теста прежней механики. 46/46.
Живая проверка на стенде: вопрос → просьба → номер → письмо с полной перепиской.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:30:33 +03:00
Дмитрий 9b83b2e035 feat(jivo-bot): статья «Откуда берутся заявки и законно ли это» (формулировка владельца)
Владелец 12.07.2026 дал суть: номера передают операторы связи, и только по тем
абонентам, кто дал согласие на передачу данных третьим лицам и на рекламные
предложения. Раньше бот на «а где вы берёте эти номера?» честно эскалировал —
теперь отвечает сам, юридически корректно.

+ otkuda-zayavki.md: источник данных (операторы связи), согласие абонента как
основание, 152-ФЗ, никаких «слитых» баз; практика для клиента (звонить можно
и нужно быстро, вежливо завершать при отказе); честная оговорка «заявка —
интерес, не гарантия продажи»; юрвопросы → живой специалист.

Границы держатся (проверено живьём): «какой конкретно оператор — Билайн? МТС?» →
эскалация; «эти номера только мне достаются?» → эскалация (эксклюзивность —
жёсткая стоп-тема). Поставщик и модель распределения не раскрываются.

TDD: тест на РЕАЛЬНОЙ базе знаний (3 формулировки вопроса → статья про операторов).
42/42 зелёные.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:18:11 +03:00
Дмитрий 27c863aa73 feat(jivo-bot): статья «О сервисе» — зачем нужна Лидерра и чем отличается
Живой урок владельца 12.07.2026: на «зачем лидерра нужна и чем отличается
от конкурентов» бот понял «конкурентов» буквально и рассказал про раздел
«Конкурентное поле». Ценностного предложения в базе знаний просто не было.

+ o-servise.md: оплата за результат (нет абонплаты), 7 ступеней, прозрачные
списания, управление лимитами/регионами/паузой, никаких обязательств, старт
за минуты + 1000₽ новичку, заявки сразу в CRM, кому подходит, честная оговорка
«заявка — это интерес, а не гарантированная продажа».

TDD: тест ищет на РЕАЛЬНОЙ базе знаний (help:rebuild-knowledge внутри теста) —
топ-1 по вопросу не должен быть «Конкурентное поле». 41/41 зелёные.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:11:24 +03:00
Дмитрий 1a686d07c8 feat(jivo-bot): всеобъемлющая клиентская инструкция — 15 статей + защита секретов
Разведка портала 5 агентами (экраны, биллинг/настройки, Конкурентное поле,
все подсказки (?), внутренние правила) → база знаний бота выросла 4 → 15 статей:
начало работы (что такое заявка, подарок 1000₽), проекты (лимиты/регионы/дни,
правило 18:00 МСК), тарифы (7 ступеней, списание по факту), пополнение (100₽,
карта/счёт/акт), баланс и пауза (разморозка «всё-или-ничего»), сделки и канбан
(5 статусов, экспорт), Конкурентное поле (сбор 300₽/изучение 50₽, очередь,
ручной режим, дубли, метки номеров), смена источника, настройки и 2FA,
уведомления, импорт CSV, API/webhook, мобильная версия, дашборд, поддержка.

Защита коммерческой тайны (проверено агентом-цензором — 15/15 статей чисто):
ни слова про поставщика/каналы/закупку и про то, что заявка может уйти не только
одному клиенту; вопросы об эксклюзивности и перепродаже — жёсткая стоп-тема
(бот не отвечает НИ В КАКУЮ сторону, зовёт живого специалиста), внутренние
движки не упоминаются, ложных обещаний (авто-возврат, «Команда») нет.

Стоп-темы разделены: HARD (деньги/скидки/юр/эксклюзив — всегда) и PERSONAL
(«мой баланс») с исключением HOWTO — «перенести мои заявки из амоцрм» больше
не уходит к человеку, бот рассказывает про импорт.

TDD: 40/40 зелёные. Живая проверка 20+ вопросов на стенде.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 11:02:23 +03:00
Дмитрий dd16edde8e fix(jivo-bot): «не знаю» без кнопки экскурсии + статья про объединение дублей
Живой урок владельца 12.07.2026 («а что такое объединить дубли?»):
- LLM признался «в инструкции этого нет» (слабое ИЛИ-совпадение по общим словам),
  а мы лепили кнопку «Показать на портале» → теперь такой ответ = честная
  эскалация без кнопок (регэксп-детектор признания)
- статья konkurentnoe-pole: «Найти и объединить дубли» 🧹 — пары-двойники,
  подтверждение клиента, бесплатно, счётчик на кнопке
- TDD: тест «не знаю без кнопки», 38/38 зелёные

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:24:01 +03:00
Дмитрий 185df2d810 feat(jivo-bot): память разговора + статья про ручное добавление конкурентов
Живой урок владельца 12.07.2026 («а сам руками я могу?» — бот талдычил про ИИ за 300₽):
- ProcessJivoMessageJob: последние 6 реплик чата → история для LLM (хвосты
  «👉 Показать…» срезаются), только СВОЙ чат
- BotAnswerService/YandexGptClient: история между system и текущим вопросом;
  пустой поиск досыпается прошлой репликой клиента
- статья konkurentnoe-pole: ручное добавление конкурента/точек/источников — бесплатно,
  кнопка «+ Добавить вручную» (фича существует, бот о ней не знал)
- TDD: 3 новых теста, все 37 зелёные

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 10:15:09 +03:00
Дмитрий e4ade68cc5 docs(jivo-bot): протокол — корп-тариф оплачен 12.07, письмо про Bot API отправлено
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 09:59:06 +03:00
Дмитрий f981f92e23 fix(jivo-bot): мягкий ИЛИ-поиск при пустом строгом + стоп-тема «у меня … баланс» с разрывом слов
Два живых бага демо 12.07.2026 (TDD, 34/34 зелёные):
- KnowledgeSearch: websearch_to_tsquery требует ВСЕ слова вопроса; «зачем он нужен»
  ронял поиск в пусто → добавлен мягкий запасной проход через ИЛИ (plainto | )
- BotAnswerService STOP_PATTERN: «какой у меня сейчас баланс» проскакивал стоп-тему
  (слова между «у меня» и «баланс») → допускаем до 3 слов разрыва, сч[её]т
- Протокол: журнал 12.07 (аккаунт Jivo, канал, цвет, ключ zofyNxmnRP, стенд-проверка)
- Скриншоты живой проверки виджета в findings

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 09:30:53 +03:00
Дмитрий e5f1ff9a4f fix(tours): карточка подсказки не уезжает за экран — справа/слева/снизу от цели
Найдено живым показом: у кнопки «Создать проект» (правый край) карточка
позиционировалась за пределами окна. Vitest GuidedTour 5/5.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 08:29:05 +03:00
Дмитрий 93f2307aec feat(bot): дообучение под нововведения — Конкурентное поле, счета, подарок 1000₽, новые Сделки, мобильный
Статьи: +konkurentnoe-pole (сбор 300₽/изучение/пакет), пополнение (счета+акт+подарок),
проект (второй путь через Поле, колонки Конкурент/Источник, мобильные вкладки).
Экскурсия konkurentnoe-pole (nav-autopodbor). Pest Bot|Tour|Help 49/49.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:01:29 +03:00
Дмитрий 7efa4742d7 fix(bot): schedule help:rebuild-knowledge без onOneServer — cache_locks нет (конвенция файла)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:53:44 +03:00
Дмитрий ac8e3277a7 docs(tours): этап 3 построен — протокол и спека синхронизированы 2026-07-10 08:49:42 +03:00
Дмитрий d4e70f58d2 test(tours): frontmatter tour статей сверяется с каталогом экскурсий 2026-07-10 08:49:41 +03:00
Дмитрий cd62806da9 feat(tours): data-tour якоря — кнопки создания проекта и пополнения 2026-07-10 08:49:41 +03:00
Дмитрий bf8174dbfc feat(tours): запуск экскурсии по ?tour= из ссылки бота 2026-07-10 08:49:40 +03:00
Дмитрий 9b5ddcb6cd feat(tours): GuidedTour — обобщённый раннер с ожиданием цели 2026-07-10 08:48:53 +03:00
Дмитрий 8526fa3389 feat(tours): каталог экскурсий — 5 стартовых сценариев 2026-07-10 08:48:53 +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
Дмитрий 4d3e568179 docs(bot): спека/протокол — FTS вместо pgvector, статьи в resources/help
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:48:51 +03:00
Дмитрий c639b3b651 style(bot): pint — неиспользуемые импорты в трёх bot-тестах
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 08:48:51 +03:00
Дмитрий 4172e73f49 test(bot): бюджет скорости — p95 нашей части тракта < 500мс 2026-07-10 08:48:50 +03:00
Дмитрий 67165de655 feat(bot): JivoBotController — webhook /api/webhook/jivo/{secret}, мгновенный ack 2026-07-10 08:48:50 +03:00
Дмитрий 43567133d8 feat(bot): ProcessJivoMessageJob — оркестратор, очередь bot, журнал+latency 2026-07-10 08:48:49 +03:00
Дмитрий e9dbb3f976 feat(bot): BotAnswerService — стоп-темы, строгий промпт, tour-ссылка под флагом 2026-07-10 08:48:49 +03:00
Дмитрий 264a6f8b5f feat(bot): JivoBotClient — BOT_MESSAGE/INVITE_AGENT, dev-режим без URL 2026-07-10 08:48:48 +03:00
Дмитрий 3c936a2ab0 feat(bot): YandexGptClient — completion Lite, таймаут 8с, null при беде 2026-07-10 08:48:47 +03:00
Дмитрий 5867d1f61f feat(bot): KnowledgeSearch — FTS russian top-N с ранжированием 2026-07-10 08:48:47 +03:00