Кусок B «витрина прогрева» (спека 2026-07-21 §6), решение владельца — полная летопись:
- B1: таблица sales_ad_audience_warming_episodes + модель + WarmingEpisodeRecorder
(open/close/record идемпотентно). Схема v8.85, бэкфилл из firm_channels(warming)
и боевых СМС, guard по источнику. rls-reviewer OK 8/8.
- B2: «Греть»/«Убрать» на площадке открывают/закрывают эпизоды канала.
- B4: warmingByProspect считает значки из летописи (live/count вместо массива каналов),
тип WarmingBadgeState в sales.ts.
- B5: единый компонент WarmingBadges.vue (идёт/грели раньше/×N) в канбане;
осиротевший WarmingChannelIcons удалён.
Уборка: убраны мёртвые скоупы forYandex/Vk/Mts + их импорт + тест (боевых вызовов нет).
cspell: +5 пре-существующих слов CHANGELOG в словарь (apk/cvtjpq/hgq/sar/sca).
Проверено: Sales 427/427, composer stan 0, pint/prettier чисто, весь Vue-набор зелёный.
B3 (СМС→эпизод) — отдельным коммитом (СМС-блок правит и параллельная сессия).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
После Фазы 2 «раздельные каналы» булевы ch_yandex/ch_vk/ch_mts на фирме
больше не заполняются — заезд и кнопка «Греть» пишут только строки
firm_channels. Но карточка воронки считала active/channels из ch_*, поэтому
у реально греющихся фирм значок оставался серым и без иконок каналов.
warmingByProspect теперь берёт членство и каналы из firm_channels со
status=warming, по образцу SalesAdAudienceController. sms — как прежде, по
факту отправленной рассылки.
Тесты ProspectWarmingBadgeTest переведены со старых ch_* на firm_channels
плюс добавлен новый тест-регресс. Sales-группа 412/412, composer stan 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Task 4 плана «три площадки прогрева» кусок B: GET /api/sales/prospects
теперь отдаёт warming.active/warming.channels на каждой карточке —
сопоставление SalesAdAudienceFirm.prospect_id в PHP (pgsql_supplier
против default connection), без cross-connection JOIN. Канал sms —
если номер фирмы попал в отправленную (status=sent) рассылку.