diff --git a/docs/superpowers/2026-07-27-HANDOFF-reklama-pokazy-chast4-yadro-STATE.md b/docs/superpowers/2026-07-27-HANDOFF-reklama-pokazy-chast4-yadro-STATE.md index 321b6380..0513ff76 100644 --- a/docs/superpowers/2026-07-27-HANDOFF-reklama-pokazy-chast4-yadro-STATE.md +++ b/docs/superpowers/2026-07-27-HANDOFF-reklama-pokazy-chast4-yadro-STATE.md @@ -42,12 +42,19 @@ ## 🔴 Открытые вопросы / риски (решить ДО реального списания на бою) -- **Заморозка не снимается при завершении/остановке.** `AdWalletService::release()` не зовётся - НИГДЕ в приложении (пред-существующая дыра модели кошелька, не из Части 4). Теперь, когда кампании - доходят до `completed` (фикс `paid_impressions`), «зависшая» заморозка стала заметной: `frozen_rub` - только растёт, свободный баланс клиента занижается. **Нужно решение владельца:** снимать HOLD при - `completed`/стопе/удалении (позвать `release`), где именно и по какой семантике. Отдельная небольшая - задача по модели кошелька. +- ~~**Заморозка не снимается при завершении/остановке.**~~ ✅ **ЗАКРЫТО 27.07.2026, коммит `dd894a52`.** + Разбор показал, что дыра была ШИРЕ и БЛОКИРУЮЩЕЙ: `charge()` уменьшал `balance_rub`, но не трогал + `frozen_rub` — одни и те же рубли считались дважды, свободный остаток (`balance − frozen`) уходил в + минус, а `AdWalletGate::isSolvent()` проверяется СРАЗУ после списания в `ChargeCampaignSpendJob` → + клиент объявлялся неплатёжеспособным после ПЕРВОГО суточного списания и `AdStopAll` глушил все его + кампании. Кампания умерла бы после первого дня показов даже при полном кошельке. + Исправлено: заморозка тает вместе со списанием + снимается на всех **4 выходах** — + `completed` (`CampaignImpressionCharger`), `rejected` (`SyncCampaignModerationJob`), + `stopped_no_funds` (`PauseCampaignsOnAdStop`), `paused` (контроллер); `resume` морозит + неоткрученный остаток сметы ДО обращения к Директу и отдаёт 409 при нехватке денег. + Решение владельца по паузе: на паузе деньги свободны, при возобновлении морозим снова. + Удаление кампании выходом НЕ является — `destroy` разрешён только для черновика. + Тесты рекламного модуля 156/156. Промт продолжения — `2026-07-27-PROMPT-restart-reklama-pokazy-v5.md`. - **Отчёт показов Директа** — контракт (TSV/async/кэш по ReportName) подтверждён только по документации, живьём проверяется в Задаче 9. - **Один адаптивный креатив** оформляется вручную в веб-конструкторе Яндекса; номер вписывает оператор diff --git a/docs/superpowers/2026-07-27-PROMPT-restart-reklama-pokazy-v5.md b/docs/superpowers/2026-07-27-PROMPT-restart-reklama-pokazy-v5.md new file mode 100644 index 00000000..3df8a6c7 --- /dev/null +++ b/docs/superpowers/2026-07-27-PROMPT-restart-reklama-pokazy-v5.md @@ -0,0 +1,123 @@ +# ПРОМТ перезапуска сессии — реклама «за показы», после закрытия заморозки (27.07.2026) + +Скопировать содержимое блока ниже целиком в новую сессию. + +--- + +``` +Ты — Claude Code в проекте Лидерры. 🔴 БОЕВОЙ ПРОД liderra.ru / lk.liderra.ru — ЖИВЫЕ +КЛИЕНТЫ И ДЕНЬГИ. Любой выкат, правка боевой БД, включение рубильников — ТОЛЬКО с +явного разрешения владельца. БД по умолчанию только чтение. + +🔴 ОБЯЗАТЕЛЬНО инвокируй подходящие скилы ПЕРВЫМИ (Pravila §12 hard-rule — skill +вызывается ДО работы, §9 «Отступления» к нему не применяется). Не «на глаз» без скилов. +Разговор с владельцем — простым русским, без программистских слов: он не программист. + +════════ СНАЧАЛА ПРОЧИТАЙ ════════ +1. 📌 ГЛАВНОЕ — состояние стройки: docs/superpowers/2026-07-27-HANDOFF-reklama-pokazy-chast4-yadro-STATE.md +2. Дизайн и решения по клиентскому Яндексу: + docs/superpowers/specs/2026-07-24-yandex-audience-dlya-klientov-РЕШЕНИЯ.md + docs/superpowers/specs/2026-07-24-yandex-audience-dlya-klientov-design.md +3. Хвосты прошлого слоя (реклама за клики, уже на бою): + docs/superpowers/findings/2026-07-25-reklama-modul-zamechaniya-zhivoy-test.md + docs/superpowers/plans/2026-07-25-yandex-reklama-ux-audit-fixes.md +4. Контракт медиа-API Директа: docs/superpowers/findings/2026-07-26-direct-media-api-contract.md + (раздел «⚠️ Проверить при go-live») +5. CLAUDE.md §0/§1/§3/§5 + docs/Plugin_stack_rules_v1.md (Vue 3 + Vuetify 3, Forest-палитра). +6. Память: project-reklama-modul-vykat-2026-07-25, feedback-worktree-laravel-windows, + feedback-brauzer-lovit-to-chego-ne-vidyat-testy, feedback_environment. + +════════ ГДЕ ЧТО ЛЕЖИТ ════════ +• Рабочая папка стройки: .claude/worktrees/reklama-pokazy, ветка feat/reklama-yandex-pokazy, + HEAD = dd894a52, ЗАПУШЕНА в gitea, незапушенных коммитов 0. +• main = a487c660 = gitea/main = боевой сервер БАЙТ В БАЙТ (598 файлов бэкенда сверены 27.07). +• 🔴 GitHub origin ЗАБЛОКИРОВАН (account suspended, 403) — пуш ТОЛЬКО в gitea. +• 🪤 Основная рабочая папка стоит на чужой ветке fix/warming-existing-manager-dedup + (8 незапушенных коммитов по телеграм-боту МТС + 255 новых файлов) — НЕ ТРОГАТЬ. +• 🟠 Ещё 6 веток нигде не сохранены, только на этой машине: feat/client-sms-broadcast 15, + fix/warming-existing-manager-dedup 8, feat/aleksandra-knowledge-base 86, + feat/external-services-online-monitoring 47, feat/extsvc-followup-2 60, + backup/before-pdn-clean-20260719 172. Предложить владельцу выгрузить в gitea. + +════════ СОСТОЯНИЕ НА 27.07.2026 ════════ +СЛОЙ 1-2 — реклама «за клики» + UX-правки: ВЫКАЧЕНЫ И ЖИВЫ на lk.liderra.ru. + Сделано 19 из 21 задачи UX-аудита. Не сделаны: T16 различимые имена черновиков, + T21 шаг 3 живая оплата картой (внешняя блокировка — go-live ЮKassa не завершён). +СЛОЙ 3 — реклама «за показы» (CPM): ГОТОВА в ветке, НЕ влита в main, НЕ на проде, + рубильник Директа ВЫКЛ. 23 коммита. Тесты рекламного модуля 156/156. + +ЧТО ЗАКРЫТО В СЕССИИ 27.07 (коммит dd894a52): + Заморозка рекламного кошелька. Была дыра: freeze ставился один раз при запуске на + всю смету, а release не вызывался НИГДЕ. Главное следствие было блокирующим — + charge уменьшал balance_rub, но не трогал frozen_rub, одни и те же рубли считались + дважды, свободный остаток (balance − frozen) уходил в минус, а isSolvent проверяется + сразу после списания в ChargeCampaignSpendJob → клиент объявлялся неплатёжеспособным + после ПЕРВОГО суточного списания и AdStopAll глушил все кампании. Кампания умерла бы + после первого дня показов при полном кошельке. + Исправлено: заморозка тает вместе со списанием + снимается на всех 4 выходах — + completed (CampaignImpressionCharger), rejected (SyncCampaignModerationJob), + stopped_no_funds (PauseCampaignsOnAdStop), paused (контроллер), а resume морозит + неоткрученный остаток сметы ДО обращения к Директу и отдаёт 409 при нехватке денег. + Решение владельца по паузе: на паузе деньги свободны, при возобновлении морозим снова. + Удаление кампании выходом НЕ является — destroy разрешён только для черновика. + +════════ ЧТО ОСТАЛОСЬ (по приоритету) ════════ +🔴 БЛОКИРУЮЩЕЕ (без этого реклама не работает): + 1. Рубильник Директа. В боевом .env есть YANDEX_DIRECT_TOKEN, но НЕТ + YANDEX_DIRECT_ENABLED (default false) и НЕТ YANDEX_DIRECT_BASE_URL + (default = ПЕСОЧНИЦА api-sandbox.direct.yandex.com). Доступ к API одобрен 26.07. + Переключить на https://api.direct.yandex.com + ENABLED=true, затем + php artisan config:cache ПОД www-data (квирк #107 — иначе портал ляжет). + 2. Задача 9 — контролируемый первый живой запуск: одна кампания, крошечный сегмент, + минимальный SpendLimit; 200 OK на каждом методе; тип CPM_BANNER_CAMPAIGN; id + проставлены; НЕМЕДЛЕННО suspendCampaign до начала открутки. Сверить реальные имена + enum/полей с контрактом (файл п.4) и поправить код при расхождении. + ⚠️ ТОЛЬКО по отдельному «go» владельца. + 3. Отчёт показов getCampaignImpressions вживую: формат TSV vs async/поллинг; что + уникальный ReportName даёт СВЕЖИЕ данные, а не кэш; что тело — одно число показов. + От этого зависит корректность списаний. + +🟠 КРУПНОЕ: + 4. Влить показы в main и выкатить по ранбуку + docs/superpowers/runbooks/2026-06-18-gitea-prod-deploy-pipeline.md. + ⚠️ При выкате: на боевом ad_settings сейчас колонка markup_percent = 30.00, а ветка + использует ad_margin_percent = 40% — проверить миграцию наценки, это ДЕНЬГИ. + ⚠️ При новых RLS-таблицах ПЕРЕзапустить db/03_service_bypass_policies.sql, иначе + джобы и админ-экран увидят тихий ноль (грабля srv_bypass). + 5. РОБОТ (Часть C) — робот-креативщик вместо ручного оформления макета в конструкторе + Яндекса, по образцу docs/superpowers/specs/2026-07-26-mts-telegram-ads-bot-design.md. + Сначала дизайн-спека → одобрение владельца → стройка. + 6. ВЫБОР (Часть A) — выбор клиенту в мастере: Яндекс сам нарежет 15 размеров из одной + картинки ИЛИ клиент грузит свои 15; плюс объяснение, что делает ИИ Яндекса. + +🟡 ХВОСТЫ СЛОЯ 2 (уже на бою): + 7. T16 — различимые имена черновиков (сейчас все «Реклама от 25.07.2026»). + 8. T21 шаг 3 — живая оплата картой, только с владельцем; без успешного платежа + «работает» НЕ заявлять. + 9. Админ-экран «Реклама — расход и маржа» живьём не пройден, нужен админ-доступ. +10. Pa11y по авторизованным рекламным экранам (сейчас конфиг бьёт только по публичным + localhost-страницам). + +⚪ УБОРКА: +11. AdvertisingCampaignEndpointTest идёт БЕЗ изоляции (нет RefreshDatabase/ + DatabaseTransactions) и копит строки в liderra_testing → при полном прогоне ложно + краснеют соседи. Изолировать. +12. Larastan в этом worktree не запускается (junction-vendor рушит bootstrap) — гонять + composer stan на обычном чекауте перед вливанием. + +════════ ГРАБЛИ ОКРУЖЕНИЯ ════════ +• Перед прогоном тестов: DB_DATABASE=liderra_testing php artisan migrate:fresh --force +• Прогон: vendor/bin/pest tests/Feature/Advertising (156/156 зелёные на 27.07). + Полный vendor/bin/pest — 3570 тестов, 3548 проходят; 22 падения НАКОПИТЕЛЬНЫЕ + (изолированно на чистой БД все зелёные), к рекламе отношения не имеют. +• Коммит: LEFTHOOK_EXCLUDE=larastan, сообщение БЕЗ круглых скобок, по эскейпу владельца, + явными путями (каталог /моя/ в .gitignore — git add пропускает молча). +• В worktree «Can't find lefthook in PATH» — безобидно. +• 🪤 В рабочей папке лежит ЧУЖАЯ незакоммиченная правка + app/tests/Feature/Advertising/CampaignImpressionChargerTest.php — НЕ коммитить как своё. + +════════ С ЧЕГО НАЧАТЬ ════════ +Прочитать п.1–4, показать владельцу короткий статус простым языком и спросить, что +берём: go-live Директа (пп.1–3, нужен его «go»), выкат показов (п.4), РОБОТ (п.5), +ВЫБОР (п.6) или хвосты. Ничего не выкатывать и рубильники не включать без разрешения. +```