Панель деталей сделки была фикс. ширины (inline 400px в «Сделках», drawer 480px
в Канбане) — на экране 390 она была шире экрана: в «Сделках» давала горизонтальный
скролл страницы и крошила карточки списка, в Канбане левый край уезжал за экран.
На телефоне:
- «Сделки» — inline-панель стала полноэкранным оверлеем (position:fixed inset:0).
- Канбан — ширина drawer = ширине экрана (useDisplay).
Десктоп/планшет — прежний master-detail 400px / drawer 480px, без изменений.
Тесты 103/103 зелёные.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Узкие центрированные модалки на телефоне резали содержимое (ряды кнопок,
вкладки, дни недели, сетку регионов — контент вылезал за края, текст обрезался
слева и справа). Диалоги с богатым содержимым теперь на телефоне открываются
на весь экран (:fullscreen по useLayoutTier + scrollable):
- Проект (создание/редактирование) — NewProjectDialog
- Новая сделка — NewDealDialog
- Пополнение баланса — TopupDialog
- Дни оптом — DaysBulkDialog (те же кнопки дней)
- Регионы оптом — RegionsBulkDialog
- Импорт: сопоставление статусов — UnknownStatusesDialog
Десктоп/планшет — прежний центрированный модал, без изменений.
Тесты диалогов 59/59 (+2 skip) зелёные.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
В карточках «На актуализацию» три кнопки (Добавить/Заменить/Отказаться)
были шире контента и заезжали за правый край карточки (на ~18px, «Отказаться»
упиралась в бортик). На телефоне (media <600px) кнопки теперь во всю ширину
карточки поровну (flex:1) — держатся внутри паддинга. Десктоп без изменений.
Проверено сканом всех 39 карточек: заездов за паддинг больше нет.
Тесты 71/71 зелёные.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
На телефоне (media <600px):
- Плашка «Лидерра подготовила N предложений… Разобрать предложения →»
раскладывается в столбик: текст переставал крошиться по слову, а ссылка
«Разобрать предложения →» больше не вылезает за край (была nowrap).
- Строка «Выбрать всех/все предложения» — flex-wrap: подсказка падает на
новую строку во всю ширину, не жмётся в узкую колонку.
Правки в общем field-shared.css — покрывают и «В поле», и «Предложения».
Тесты «Поля» 71/71 зелёные. Десктоп/планшет без изменений.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
При длинном заголовке («Конкурентное поле») кнопка «Поиск» стояла вплотную
к тексту (зазор 0). Добавлен margin-left 10px на телефоне (media <600px) —
теперь есть воздух. Десктоп/планшет без изменений.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Списания type=autopodbor_charge шли без описания и показывались как «—»
и в таблице (десктоп), и в карточках (телефон). Добавлена метка
«Списание за автоподбор» в общий словарь операций TransactionsTable.
Тесты транзакций 9/9 зелёные.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Сегмент-контрол «Тип лица» (Физическое лицо/ИП/Юридическое лицо) на телефоне
не влезал в строку и наезжал; теперь раскладывается в столбик во всю ширину
(media <600px, скруглены верх/низ группы). Остальные поля реквизитов уже
адаптивны. Десктоп/планшет — горизонтальный переключатель, без изменений.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
На телефоне фильтры (Тип/Статус/Регион/День/Сортировка/Поиск) перестали
обрезаться («Р…», «Д…») — раскладываются в столбик во всю ширину
(перебит inline max-width через media <600px). Десктоп/планшет без изменений;
карточки проектов уже адаптивны.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
На телефоне v-btn-toggle (Сегодня/7 дней/30 дней/Период) растянут на всю
ширину с равными кнопками — убрана внутренняя горизонтальная прокрутка.
Только media-запрос <600px; десктоп/планшет без изменений.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
На планшете (600–1279px) сайдбар сжимается в rail: только иконки пунктов,
подписи/поиск/бренд-текст скрыты, контент шире. Иконки берутся из mdi,
уже заложенных в данные пунктов; на десктопе иконки скрыты — текстовый
сайдбар остаётся прежним. Отступы топбара и контента разведены по ярусу
(десктоп 232/240px, планшет 64/72px, телефон 0). Гамбургер топбара теперь
показывается только на телефоне (d-sm-none) — на планшете/десктопе он не нужен.
Тесты каркаса 24/24 зелёные. Проверено на 820 и 1440px; десктоп не изменился.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
На телефоне 5 колонок больше не листаются вбок: сверху горизонтальный
переключатель этапов (чипсы со счётчиками), ниже — выбранная колонка на всю
ширину. Смена этапа сделки на телефоне — тапом по карточке через drawer
(drag вбок на узком экране неудобен). Планшет/десктоп — прежняя доска со всеми
колонками и перетаскиванием, без изменений.
Тесты Канбана 23/23 зелёные. Проверено на 390 и 1440px (+ переключение этапов).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
История транзакций обёрнута в ResponsiveTable: на десктопе/планшете —
v-data-table-server как прежде, на телефоне — карточки (операция, сумма
цветом, дата, #ID) + компактный пейджер по 20. Серверная пагинация сохранена.
Тесты биллинга 40/40 зелёные. Проверено на 390 и 1440px.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Обёртка ResponsiveTable (таблица на десктопе/планшете, карточки на телефоне
по ярусу useLayoutTier). Применена к реестру лидов «Сделки»: на телефоне —
карточки лида (чекбокс выбора, телефон, статус-плашка, источник·город,
комментарий, дата), тап открывает карточку сделки; на десктопе прежняя
v-data-table без изменений.
Тесты: ResponsiveTable.spec (2), «Сделки» 58/58 зелёные. Проверено на 390 и 1440px.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AppLayout: ярус-отступ (десктоп/планшет 232px, телефон 0 + низ под панель), рендер
AppBottomNav/AppMoreDrawer на телефоне, гамбургер открывает «Ещё». AppSidebar:
проп tier + скрытие на телефоне. Десктоп/планшет без изменений.
Те же выпадашки региона (новый проект, фильтр списка, массовое
add/remove регионов, карточка проекта) переведены на REGIONS_ALPHA —
единый порядок с автоподбором, республики по своему имени, а не кучей.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Регионы выводились в конституционном порядке (республики → края →
области), из-за чего список выглядел кашей (Дагестан → Донецкая
Народная → Ингушетия → Кабардино-Балкарская). Добавлен REGIONS_ALPHA:
сортировка по «настоящему» имени, слова-типы (Республика/область/край/
автономный округ) отбрасываются — Татарстан на «Т», Адыгея на «А».
Подставлен во все 7 выпадашек автоподбора. Тест regionsAlpha.spec.ts.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Кнопка на актуализации переименована «Удалить»→«Отказаться» (чтобы клиент не путал с удалением конкурента).
- CSV-выгрузка «Списаний» теперь включает автоподбор-строки (отдельным блоком, «Автоподбор конкурентов»);
тест на export. charges 11/11.
- schema.sql canon-sync v8.63: +колонка dismissed_actualize_keys у autopodbor_competitors + CHANGELOG
(заодно в цепочку шапки вписана пропущенная v8.62 CHECK autopodbor_charge).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Кнопка «Удалить» на карточках «На актуализацию» рядом с Добавить/Заменить.
Отклоняет ТОЛЬКО находку (новый сайт/адрес): фирма поля остаётся, находка → архив,
её новые ключи запоминаются у фирмы поля (новая колонка dismissed_actualize_keys jsonb).
Классификатор вычитает отклонённые ключи → та же находка больше не всплывает; реально
другой новый сайт даст новый ключ → покажется. Не глушим по имени, конкурента не убираем.
Бэк: миграция + модель-каст + ProposalClassifier (вычитание) + эндпоинт dismissActualize
+ роут. Фронт: api + store + кнопка «Удалить». Тесты: классификатор 8/8, ветка 185/185,
фронт 30/30. Canon-sync schema.sql (v8.62) — follow-up (миграция idempotent-guarded).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- «Списания» (TenantChargesController.index) теперь union lead_charges + autopodbor_charge
(balance_transactions) — клиент видит списания за прогоны, не только в «Истории транзакций».
Автоподбор-строки: kind=autopodbor, метка вместо сделки, «—» для tier. Фильтр charge_source
автоподбор не подмешивает. 2 теста, всего charges 10/10.
- Плашки «Предложений» разъезжались за экран на длинных URL (utm/рекламные хвосты): grid-элементы
min-width:auto не сжимались. Фикс: .ld-grid>* min-width:0 + перенос длинных ссылок.
- Фронт 16/16.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Баг: при autopodbor_max_competitors=0 джоба делала array_slice($unique,0,0)=[]
→ сохраняла 0 конкурентов, НО всё равно списывала 50₽ (chargeForRun после обрезки,
проверка empty стои́т ДО неё). Теперь $max>0?slice:все — 0 значит «сохранить всех».
Плюс авто-повтор findCompetitors при пустом сборе (search_retry_on_empty, деф. 2):
моргание 2ГИС/Яндекс/EXA не показывает клиенту «ничего не нашлось». Реально пустая
ниша → empty без списания. 2 теста; ветка автоподбора 176/176.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Джобы Search/Study/Resolve делали AutopodborRun::findOrFail() ДО set_config
app.current_tenant_id. На проде роль crm_app_user под FORCE RLS (на боевом
кластере ни одна роль не BYPASSRLS) не видит строку без tenant-контекста →
ModelNotFound, все три кнопки автоподбора падали. На dev маскировалось
суперюзером postgres. Теперь tenantId передаётся в dispatch и контекст
выставляется первым. 3 регресс-теста на порядок (set до чтения runs).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Групповое и одиночное создание теперь ТРЕБУЮТ регион (vsyaRf=false по умолчанию;
«Вся РФ» — явная галочка, дефолта больше нет — подтверждено владельцем). Тесты
были под старый дефолт «Вся РФ» → doCreate бэйлился на гейте региона. Добавлен
явный выбор «Вся РФ» перед doCreate. Плюс requisites_required теперь открывает
форму реквизитов (reqGate), а не тост — тест переведён на новое поведение,
reqGate добавлен в defineExpose для наблюдаемости. Спек 6/6 зелёный.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Стиль-only (braces_position, ordered_imports, fully_qualified_strict_types и т.п.),
поведение не меняется. Autopodbor 476/476 зелёный после причёсывания.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Скриншот-скрипт-времянка, случайно закоммичен ранее; при overlay-деплое
попал бы в app/scripts на прод. Мусор — удалён до выката.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SchemaDeltaTest жёстко ждал метрики v8.55 (74 таблицы/128 индексов/44 RLS),
но автоподбор добавил в schema.sql 4 таблицы (runs/competitors/sources/
merge_events) + 9 индексов + 4 RLS-политики → 78/137/48. Это был давно
красный сторож (не баг фичи). Обновлены ожидаемые числа + комментарий.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Тест «reverse = композит» жёстко ждал CompositePhoneReverse, но не пинил флаг
parallel — из-за dev-.env (AUTOPODBOR_PHONE_REVERSE_PARALLEL=true) собирался
ParallelPhoneReverse и тест падал. Явно пиним parallel=false: тест проверяет
именно композитную ветку, независимо от окружения. Autopodbor-набор 476/476.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Клик «Удалить» у предложения открывает окно со всеми предложениями,
найденными в том же месте (совпал url карточки/сайта в «где нашли»), с
галочками. По умолчанию отмечена только нажатая строка; клиент выбирает,
какие связанные строки удалить. «В работе» — прежнее окно. Массовое
удаление предложений — подтверждение со списком выбранных.
Только показ и порядок «сначала выбери, потом удали»; БД/бэкенд не тронуты.
Тесты: пул по url, строгое совпадение, дефолт-галочка, удаление отмеченных.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CachingPageFetcher — декоратор над загрузчиком карточек (SelfRenderClient/
XfetchClient): кэширует в общем Cache (Redis/file) по ключу URL на N дней ТОЛЬКО
карточки (/firm/ 2ГИС, /maps/org/ Яндекс). Поиск (/search/) и ленты — всегда мимо
кэша (свежее добывание новых фирм). Пустое не кэшируем — разовый сбой не отравит кэш.
Кэш ОБЩИЙ для шага 1 (поиск) и шага 2 (изучение+разворот) и повторных прогонов:
ключ по URL → любые экземпляры делят один кэш. Вшит в AutopodborServiceProvider
через wrapCache(); 2ГИС-база RoutingBatchFetcher и self-render card reader обёрнуты,
яндекс-Playwright/ленты — нет.
Флаг AUTOPODBOR_RENDER_CACHE (ВЫКЛ по умолчанию) + render_cache_ttl_days (5).
TDD: 8 тестов CachingPageFetcher + 4 теста проводки — зелёные. Живой замер ДСК:
тёплый прогон — 26 карточек из кэша, 0 обращений к 2ГИС/Яндексу, источников 11=11.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Качество. Привязанные клиентом карточки 2ГИС/Яндекс теперь используются как
«земля» и якорь холдинга (раньше выбрасывались). Слепой поиск по имени берётся
только при наличии якоря; AnchorGate: пустой якорь → drop (было keep-всё — корень
шума). Без сайта чужие фирмы по широкому имени («Притяжение» → стоматология,
салоны, эротический магазин) больше не попадают в карточку конкурента. Доказано на
живых данных (Сибагропромстрой): свежий полный сбор — 0 салонов, только номера
застройщика.
Скорость (за флагом AUTOPODBOR_PHONE_REVERSE_PARALLEL, ВЫКЛ по умолчанию).
ParallelPhoneReverse крутит 2ГИС- и Яндекс-разворот ОДНОВРЕМЕННО (итог по времени
= max, не сумма; оба источника отрабатывают полностью — ничего не режется).
Сверка последовательно vs параллельно: данные один в один, потерь нет. Таймаут
подпроцесса 600с (драйвер process иначе берёт дефолт Symfony 60с и убивал разворот).
Тесты: AnchorGate (пустой якорь → drop; телефон-якорь режет чужое), якорь
привязанной карточки режет салоны, ParallelPhoneReverse (слияние на sync-драйвере).
Весь Autopodbor 464/464, Pint чист.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Закрыт хвост §5.3: node-скриптовые точки Яндекса уведены на рендер-сервис, чтобы
под self_render ВЕСЬ Яндекс шёл через виртуалку+прокси (не крутил браузер на портале).
- YandexListSource (интерфейс) + LocalYandexListSource (xf4, локальный Playwright) +
RenderServiceYandexListSource (self-render, POST type=yandex-list на виртуалку).
- PlaywrightYandexDirectory (листинг шаг 1) и PlaywrightYandexPhoneSearch (разворот)
расшиты на YandexListSource — заодно стали тестируемы офлайн (6 новых тестов).
- PlaywrightYandexCardReader под self_render получает SelfRenderClient (был PageFetcher).
- Рендер-сервис: режим type=yandex-list (скролл ленты + сбор организаций → {orgs}),
проброс scrolls.
- Провайдер: yandexListSource() по флагу; разворот-Яндекс на виртуалку+прокси.
Итог: при self_render ВКЛ на xf4 и на локальном браузере портала не остаётся НИ ОДНОЙ
точки — 2ГИС и весь Яндекс через виртуалку+прокси. 459/459 тестов, Pint+Larastan чисто.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Флаг autopodbor.self_render переключает слой добычи 2ГИС/Яндекс пары фетчер+ридер:
ВКЛ → SelfRenderClient (виртуалка+прокси; 2ГИС→initialState) + TwoGisJsonReader;
ВЫКЛ → XfetchClient (xf4) + HtmlTwoGisReader (как сейчас).
SelfRenderClient один покрывает и 2ГИС (initialState), и Яндекс (html) — заменяет
и xfetch, и локальный Playwright-роутинг. DeepStudyCollector под self_render получает
yandexDir через виртуалку. 453/453 теста зелёные, Pint+Larastan чисто.
Известный хвост (follow-up): PlaywrightYandexDirectory (листинг Яндекс шаг 1) и
YandexPhoneReverse Playwright-классы остаются на ЛОКАЛЬНОМ Playwright (не xf4, но и
без прокси) — для полного прокси-покрытия Яндекса их надо развязать на PageFetcher.
Гарантия §5.7 «ни одной точки на xf4 при ВКЛ» соблюдена (локальный Playwright ≠ xf4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
HTTP-клиент к своей виртуалке (замена xf4). Реализует BatchPageFetcher (и
PageFetcher): для 2ГИС просит initialState, для Яндекса/сайтов — HTML. Секрет
в заголовке X-Render-Key (из .env). Без ключа/адреса — молча пусто (деградация).
Http::pool для батча, ретраи. 7/7 тестов, Pint чисто.
- config services.self_render (endpoint/key/concurrency, всё из .env).
- config autopodbor: флаги self_render + гранулярные self_render_2gis/_yandex
(наследуют master), дефолт ВЫКЛ.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Фазы 2-3 плана self-render. Только новые файлы — существующий код не тронут,
xf4-путь цел. 281/281 юнит-тестов зелёные, Pint чисто.
- TwoGisReader — интерфейс разбора 2ГИС (3 метода: listing/card/branchList),
единый контракт поверх HTML (xf4) ИЛИ initialState-JSON (self-render).
- HtmlTwoGisReader — xf4-путь: тонкий делегат к CategoryListingParser/
DirectoryParser, поведение 1:1 (4 теста контракт-идентичности на реальных
фикстурах 2gis-*-kraslombard).
- TwoGisJsonReader — self-render путь: разбор window.initialState по карте
путей спайка (pagination-список → entity.profile), фильтр type=branch
(фирма-в-фирму, без зданий/улиц). 5 тестов на реальных фикстурах: 12 фирм
листинга, телефон+сайт карточки, 2 пути поиска-по-телефону, деградация.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Разведка живого 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>
Разворот по телефонам 2ГИС и Яндекс больше не уходит цепочкой «до талого»:
по умолчанию берём только фирмы с ЛИЧНЫМ номером фирмы — глубина 1. Именно
неограниченная цепочка уводила в чужие фирмы, давая мусорные сайты в
предложениях холдинга. Многошаговый разворот сохранён за параметром maxDepth,
по умолчанию 1. Изолировано: работает только под флагом phone_reverse, основной
пул сбора не затрагивается. Бэкенд автоподбора 430 из 430.
Фронт: на вкладке «Предложения» добавлена кнопка массового удаления выбранных
источников — bulkDeleteProps через removeSource, покрыта тестами.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Раньше поиск, резолв и изучение делили одну очередь autopodbor — при одном
воркере быстрый шаг 2 (изучение) стоял за тяжёлым шагом 1 (поиск с ИИ) другого
клиента. Теперь два процесса:
• шаг 1 «собрать поле» (поиск + резолв по имени, ИИ+внешние) → autopodbor-find
• шаг 2 «дайте источники / изучение» (без ИИ) → autopodbor-study
Идут параллельно, друг друга не держат. Замок WithoutOverlapping по runId цел.
Запуск теперь двумя воркерами:
queue:work redis --queue=autopodbor-find --timeout=3600
queue:work redis --queue=autopodbor-study --timeout=3600
TDD: AutopodborQueueThrottleTest 4/4, автоподбор 434/434.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Сбор 2ГИС упирался в лимит xf4.ru ~3-4 запроса за раз на КЛЮЧ (выше 429).
XfetchClient теперь принимает список ключей и раскидывает запросы по кругу:
размер пачки = concurrency × число ключей, каждому URL — свой ключ. Живой залп
подтвердил: 9 параллельно (3 ключа × 3) без 429 → лимит per-key, ключи дают ×3.
Ключи — только в .env (XFETCH_API_KEYS через запятую), в код/гит не идут; пусто →
работает по-старому на одиночном XFETCH_API_KEY. Совместимость: конструктор
принимает и строку, и список. TDD: XfetchClientTest 11/11, автоподбор зелёный.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Слияние конкурентов больше не «чёрная дыра» (баг «САПС/Метрополис»: карточка
исчезла без следа, вернуть было нечем). Теперь каждое слияние пишет событие в
autopodbor_merge_events со СНИМКОМ поглощённых карточек (имя/сайт/телефоны/
справочники), кто и когда слил.
- Таблица autopodbor_merge_events (+RLS tenant_isolation, паттерн 1:1 с sources,
rls-ревью OK; nullOnDelete на survivor_id/user_id — история не рушится).
- AutopodborCompetitorMerger::merge($tenantId,$ids,$name,$userId) пишет событие.
- GET /field/merge-events — история; POST /field/merge-events/{id}/restore —
возврат поглощённых как предложения (защита от повторного возврата).
- Фронт: панель «🕘 История слияний» + «↩ Вернуть карточку».
Бэк 428/428, фронт 12/12, Pint чистый, build ок. schema.sql канон-синк всех
таблиц автоподбора — предвыкатный TODO (вся фича migration-only в этой ветке).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Баг «САПС/Метрополис»: клиент переименовал свежую карточку и слил — а выжила
ДРУГАЯ (с проектами/в поле), имя переименованной затёрлось вслепую, карточка
пропала. Теперь окно «Найти дубли» помечает карточку, которая ОСТАНЕТСЯ
(«· останется»), и берёт ЕЁ имя как имя объединённого по умолчанию — клиент
видит исход до подтверждения и не теряет карточку случайно.
Бэкенд (survivor-метка в duplicate-groups + AutopodborCompetitorMerger::survivorId)
уже в 853c5a00; здесь фронт: api-тип, окно, метка, тест. Фронт 11/11.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Две починки в «Конкурентном поле» + превью выжившего в окне склейки:
1. Переименование конкурента (PATCH competitors/{id}) не обновляло имена
уже созданных проектов — они носят имя-снимок конкурента. Теперь при
смене имени проекты синхронизируются (syncNamesForCompetitor): меняется
только отображаемое имя, signal_identifier поставщика не трогаем.
2. Слияние дублей не пересматривало ящик выжившего. Если выжившим по
возрасту/деньгам оказывалось предложение, объединённая карточка уезжала
в «Предложения», даже когда одна из склеиваемых была в «Поле». Теперь
объединённая получает сильнейший ящик (поле > предложение > архив) —
только повышаем, никогда не понижаем.
3. Окно «Почистить поле» показывает, какая карточка останется после склейки
(survivor во duplicateGroups) — не затираем имя вслепую.
Тесты: весь блок автоподбора зелёный (426). Larastan в этой рабочей копии
падает без вывода на любом файле — среда, не код.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Кейс «Образцово»: у застройщика (СМ.Сити) один номер отдела продаж легитимно
висит на всех его ЖК (277-99-01 на 9 проектах). Прежний порог dropCrowdedKeys
(>4 карточек → «рекламный», выкинуть) прятал это, и окно «Найти дубли» не
показывало проекты застройщика — systematically (6 таких номеров в данных).
Решение владельца 05.07: порог по ТЕЛЕФОНУ (p:) убрать — клиент подтверждает
склейку вручную («Это разные»), тихой ложной склейки нет; рекламные-подменыши
и так отсекаются на сборе. Порог по АДРЕСУ (a:, бизнес-центр) оставлен.
Проверено на AutoMoney: его номер у 3 карточек — все реально Автодом24, группа
верная, монстр-группы снятие порога не даёт.
Тесты переписаны со старого поведения на новое (unit + feature), 421/421.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Кнопка «Найти и объединить дубли» на экране поля теперь ловит и предложения,
дублирующие карточки поля (scope=cross): счётчик учитывает их, окно помечает
«· предложение», объединение сливает предложение в карточку поля (выживает поле,
merger не трогали). Бэкенд оставляет только группы с ≥1 карточкой поля — пары
чисто-предложений на экране поля не мусорят.
Файл-статус сессии — docs/superpowers/findings/2026-07-05-avtopodbor-session-status.md.
Feature 154/154, фронт 32/32, Pint чистый, билд собран.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>