Исправлены опасные устаревшие шаги: §0/§6b/§8 велели миграции через sudo -u postgres
на VM = мёртвая rollback-копия (после переезда 26.06 боевая = Managed PG). Теперь —
crm_migrator на rw-endpoint кластера. Плюс: катим из main (=прод с 09.07), хирургический
overlay, smoke по lk.liderra.ru, квирк 107 (optimize только www-data).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Причина фантомных сделок kdv1 (09.07.2026): CsvReconcileJob брал отчёт «Запрос
номеров» = ПУЛ собранных номеров (phones_cnt), а не ОТДАННОЕ (crms_cnt), и лепил
из пула сделки клиенту + выедал дневной лимит. Пример: пул 157, отдано 15,
у клиента 15 фантомов, а 15 реально отданных выбило лимитом.
Фикс: SupplierPortalClient::fetchDeliveredLeads() читает журнал «Мои сделки»
(index-visit, по vid) — только реально отгруженное. CsvReconcileJob сверяет по vid,
добор несёт настоящий vid (idx_supplier_leads_vid_unique -> идемпотентно с webhook).
Пул не трогается -> фантомы невозможны, лимит не забивается мусором.
Проверено: 12 новых тестов + 294 supplier зелёные, Pint/Larastan чисто. Выкачено и
проверено на проде (reconcile читает отданное, 0 фантомов за прогон).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Дизайн из брейншторма с владельцем: кнопка в подвале выбора, пакет из
группы конкурентов, одна дорожка + round-robin между тенантами,
денежный гейт всё-или-ничего с резервом под активные лид-проекты.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Админка показывала сырую ширину ступени (leads_in_tier: 250/500/1000...),
а кабинет клиента — накопительный диапазон (1-250, 251-750...). Одни и те же
данные, но выглядят как разные тарифы; в форме редактирования подпись
'Лидов в ступени' читается как потолок, а система понимает её как ширину -
ловушка при смене цен.
Логика денег не менялась. Изменён только показ в админке:
- колонка 'Диапазон у клиента' в активной сетке;
- живой предпросмотр диапазона в форме редактирования;
- расчёт диапазона вынесен в общий utils/pricingTiers.ts, чтобы админка
и кабинет больше не расходились.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Раздел «5. Хранение и защита»: упоминание записи сессий Вебвизором с маскировкой
персональных данных контактов, обработка на серверах в РФ.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
router.afterEach: при входе на layout=app лениво грузит Метрику и шлёт hit(fullPath).
Вход/публичные/админка/портал продаж — без Метрики. + убраны лишние @ts-expect-error
в тесте (type-check чист).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
metrika.ts: читает id из <meta name=metrika-id>, один раз инжектит tag.js + init
webvisor:true (defer), hit(url) шлёт заход. Тесты tests/Frontend/metrika.spec.ts:
no-op без meta, идемпотентная загрузка, hit вызывает ym с id+url.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
По образцу JivoSite: config services.metrika.counter_id из env METRIKA_COUNTER_ID;
welcome.blade вставляет <meta name=metrika-id> только когда id задан. Скрипт грузится
лениво из роутера (кабинет), не тут — Вебвизор не пишет вход/админку.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Реализация дизайна 2026-07-08: конфиг счётчика (образец JivoSite), ленивый
загрузчик metrika.ts (TDD), hit только для layout=app, маскировка ym-hide-content/
ym-disable-keys в компонентах кабинета, строка в политику, CSP-правка блока
приложения + выкат с обязательной проверкой маскировки ПДн на стенде.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Утверждённый с владельцем дизайн: отдельный счётчик для lk.liderra.ru, запись
только в кабинете клиента, маскировка личных данных (Вариант 1), CSP-правка блока
приложения, строка в политике конфиденциальности. Выкат через тестовый стенд +
проверка маскировки как gate. Следующий шаг — план работ (writing-plans).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Симптом: отчёт «Посетители» полон, а Вебвизор пуст даже за дни с заходами.
Причина: строгий CSP резал скрытый blob-frame Вебвизора (базовый счётчик через
img-пиксель проходил → «Посетители» есть). Fix на боевом (nginx liderra.ru,
блок лендинга): +child-src/frame-src 'self' blob: https://mc.yandex.ru +домены
Метрики в frame-ancestors. Проверено: заголовок отдаётся, / и /login = 200.
Бэкап /etc/nginx/liderra.bak-webvisor-20260708.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Потоп ~116 писем на ops@ убран: FailoverProjectChannel без per-project писем +
Блок 6 spike-детектор в incidents:watch-failures. На проде lk.liderra.ru
(gitea 316bc2d8+9799c0eb). Подчищены 57 stale строк очереди + 1 autopodbor-джоб.
cspell: +непроливший, +флайт.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Блок 6: если за окно (10 мин) в supplier_manual_sync_queue упала пачка
(>= threshold-manual-queue, дефолт 10) — вероятное падение кабинета поставщика.
Инцидент дедупится 60 мин, письмо шлётся каждый прогон на kdv1@bk.ru + ops@liderra.ru
(громко при реальной аварии, но одним сводным письмом). Флаг sendMail в createIncident.
phpstan-baseline: +4 TestCall::artisan() (5→9) от новых тестов.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Убраны письма manual_required и failover_to_form (по проекту, без троттла) —
источник потопа ~116 писем на ops@ 07.07. Строка в ручной очереди остаётся
(запись видна в админке). Зависимость Mailer удалена из канала и DI-привязки.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Устраняет потоп ~116 писем на ops@ 07.07: убираем per-project письма
FailoverProjectChannel, добавляем аварийный spike-детектор ручной очереди
в incidents:watch-failures. batch остаётся рабочим режимом.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TDD-план на 9 задач: worktree от worktree-avtopodbor (там есть «Поле»),
useLayoutTier, AppBottomNav, AppMoreDrawer, rail-сайдбар, ярус-отступ AppLayout,
ResponsiveTable и пруф на «Сделках». Экраны Этапов 2-3 — отдельными планами.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Брейншторм с владельцем 07.07. Рамки: основной портал 9 экранов, телефон+планшет,
премиум-уровень, мобилка урезанная / десктоп полный. Подход А — сначала каркас, потом экраны.
Меню на телефоне: Поле, Проекты, Сделки, Биллинг, Канбан. База — ветка с «Полем» вне main.
Аудит: 4 реальных архетипа поломки сняты локально на 390px [findings/2026-07-07-portal-mobile-audit].
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- на телефоне текст героя опущен ниже padding-top 22vh, свечение звезды мягче x0.68
- убран логотип ЛИДЕРРА из шапки сверху слева
- Вход ушёл в левый край, Попробовать бесплатно — в правый
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Снимок боевого: лендинг liderra.ru теперь со счётчиком Метрики, строгий фильтр
роботов ВКЛ, цель GO_PORTAL на CTA. CSP блока лендинга дополнен mc.yandex.ru.
+ Вебвизор в cspell-words.txt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- счётчик Метрики вставлен в <head> лендинга landing-v5-claude.html
- на 3 кнопки перехода на портал повешен reachGoal GO_PORTAL (JS-событие)
- ранбук выката: CSP блока лендинга дополнен https://mc.yandex.ru
в script-src/img-src/connect-src — иначе строгий CSP блокирует счётчик
Выкачено на боевой liderra.ru 07.07: счётчик проверен живьём (заход уходит
в Яндекс), вход в кабинет /login не сломан. Цель GO_PORTAL создана в Метрике
(ID 579585431).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Пилот по стоматологиям: скан кода сайтов (коллтрекинг Calltouch/Comagic/
Callibri/Roistat/Mango + пиксели VK-myTarget/Google Ads/Meta) плюс Яндекс.Директ
через Keys.so API (ключи, объявления, бюджет), склейка в Excel. Две фазы:
сайты параллельно, затем чистый проход по API с повторами. Тесты 16/16.
Спека и план — docs/superpowers. Данные Спарка, результаты и токен Keys.so —
вне git (личная папка + .gitignore).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- «Вход в личный кабинет» (nav a.navlogin) ПЕРЕД «Попробовать бесплатно»,
ghost-стиль; на мобиле сокращается до «Вход» (.lg-full/.lg-short)
- секции получили класс navsec (скрыты на мобиле), логин остаётся
- обе кнопки шапки + финальная CTA ведут на https://lk.liderra.ru/
(адрес-заглушка, подтвердить у владельца); у #cta убран preventDefault
- флоу: liderra.ru = лендинг, портал → lk.liderra.ru
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- телефон (≤760px) → flat-режим: секции в столбик, chap-cap сверху (order:-1),
карточки/чипы полной ширины; размеры/отступы под мобилку, «листайте» скрыт
- фон анимируется через лёгкий flatLoop (bg-only, цвет по общему скроллу,
портал у верха/низа)
- шапка: текст-меню скрыто, яркая кнопка «Попробовать бесплатно» остаётся
- fix: старый баг flat-режима — блок стартовал до объявления canvas-переменных
(init2D падал на undefined.style); перенёс перед boot
- проверено на 390×844: hero/поле/поток/CRM/финал, 0 ошибок консоли
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- новый экран между «Конкурентным полем» и CRM: «Один номер — один раз»,
летящие плашки-номера (новые светятся, дубли гаснут/зачёркнуты),
подпись конкурента-источника, живой счётчик, зелёно-мадженовая глава
- перенумерация глав 01–04, пункт меню «Поток»
- «Проба» → яркая кнопка-пилюля «Попробовать бесплатно»
- красный пик палитры остаётся на «Успехе» (crmP≈0.713, проверено)
- передача 2026-07-07-HANDOFF-landing-v5.md обновлена
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Пласт исследования про ИИ-вебмастера портала: наблюдаемость,
self-healing, ИИ-техподдержка, выбор LLM-моделей, идемпотентность в
multitenant. Семь протокол-документов плюс исходники Perplexity.
NB: LEFTHOOK_EXCLUDE=cspell,markdownlint — сырой внешний исследовательский
материал с чужой терминологией 249 незнакомых слов и 971 стилевой придиркой
markdownlint; орфо-словарь и стиль-линт проекта им не засоряем. Ложная
находка gitleaks Idempotency-Key в allowlist. Прочие проверки прошли.
User-approved bypass для этого коммита.
Co-Authored-By: Claude Opus 4.8 1M context <noreply@anthropic.com>
Структурированный разбор недочётов портала поставщика при взгляде
глазами обычного пользователя: баланс и тариф-блокировка, формат
телефона источника, регион вся РФ, потеря черновика реквизитов, вход
до подтверждения почты, обращение на ты в блокировке, пустые состояния
онбординга. Каждый раздел — ОПИСАНИЕ.md плюс скрины до и после.
Словарь cspell пополнен доменными словами.
Co-Authored-By: Claude Opus 4.8 1M context <noreply@anthropic.com>
Скриншоты проверок в корне, личные файлы, IDE-заглушки и локальные
демо-данные больше не висят в git. Файлы остаются на диске.
Co-Authored-By: Claude Opus 4.8 1M context <noreply@anthropic.com>
Решение заказчика 03.07.2026: два вида тарифа вместо трёх — суточный
оклад daily_salary и процент от пополнений topup_step. Убраны
percent_oborot и fix_per_client.
Миграция 2026_07_03_120000 меняет CHECK sales_tariffs.kind, освобождает
FK sales_users.current_tariff_id и sales_client_assignments.tariff_id,
удаляет тарифы уходящих видов. SalesEarningsService для уходящих видов
возвращает 0 по default-ветке. Снимки привязок не трогаются.
Обновлены контроллеры тарифов и дохода, сервисы Earnings и Metrics,
фронт SalesTariffsView и api/sales.ts, демо-сид, тесты бэка и фронта,
CHANGELOG схемы v8.61 и спека портала.
Не на проде: ветка feat/sales-portal-demo.
Проверка: Sales Feature 169/169, Vitest SalesTariffs 10/10, Larastan 0.
Co-Authored-By: Claude Opus 4.8 1M context <noreply@anthropic.com>
Task 8.2 — SalesDemoSeeder: начальник Орлов + 3 менеджера (Лебедев в
отпуске) + 4 тарифа (3 вида) + привязки (к существующим тенантам,
round-robin, снимок тарифа) + выплаты как в прототипе.
- Защита от прода: throw если app env = production.
- Идемпотентно (firstOrCreate/updateOrCreate; выплаты только если их нет —
append-only). DatabaseSeeder не трогается — запуск вручную
`php artisan db:seed --class=SalesDemoSeeder`.
NB: запускать в dev-БД, НЕ в liderra_testing (портит baseline dept-count
тестов). Larastan 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Производный статус тенанта (trial>suspended>overdue>active) был скопирован
в 3 контроллерах портала продаж. Вынесен в
App\Http\Controllers\Concerns\DerivesTenantStatus; контроллеры
(Clients/Dashboard/Managers) теперь используют трейт. Поведение не
изменено — sales-набор 28/28 затронутых, Larastan 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 7.1b — SalesManagersView (начальник, #page-managers) поверх
GET/POST /api/sales/managers.
- Форма «Новый менеджер» (имя, e-mail, пароль, роль) → POST → snackbar +
очистка + перезагрузка; ошибки (дубль email) через extractSalesErrorMessage.
- Список менеджеров: роль-чип, клиентов, выплачено всего, статус
(Активен/Отпуск).
- api/sales.ts: listSalesManagers/createSalesManager. Роут /sales/managers
со заглушки на экран.
Vitest 6/6, фронт-набор 1089 без регрессий, ESLint чист.
Фаза 7 завершена: начальник создаёт менеджеров (логин+пароль) и видит
список отдела.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 7.1a — SalesAccountService + store/index в SalesManagersController,
только head.
- store: создаёт sales_user (пароль Hash::make, created_by=head,
base_salary=0); email unique → 422; role in manager|head; password min:6;
в ответе нет password ($hidden).
- index: список всех sales_users (head-первыми) с clients_count и
выплачено всего.
Менеджер не может создавать/смотреть список (403).
Тесты 7/7 (вкл. проверку хеша пароля), sales-набор 174/174, Larastan 0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Task 6.3 — период уже проброшен во всех контроллерах (SalesPeriodResolver)
и экранах (salesPeriod store + watch). Добавлен проверочный тест на
/api/sales/overview: period=this даёт оборот 3000/лид, period=prev — 5000/лид,
а balance_sum_rub одинаков (1000) — баланс не зависит от периода.
Фаза 6 завершена: начальник видит весь отдел; период переключается везде.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>