Commit Graph

10 Commits

Author SHA1 Message Date
Дмитрий af12b1ceb4 fix реклама за показы: предел веса картинки проверен по-настоящему, обход модерации не срывается целиком, робот не несёт токен на чужой адрес
Мелочи приёмочного листа v12 §8. Каждая правка с тестом; где защита уже стояла
в коде — тест проверен вырезанием этой защиты.

Предел веса картинки. Две прежние проверки были пустышками: сравнивали константу
саму с собой и с тем же числом в ответе сервера. Вырезание правила max: оставляло
обе зелёными. Настоящий тест грузит перевес и ждёт отказа — это четвёртая найденная
пустышка за ветку.

Обход модерации. Объявление без статуса и причина отказа длиннее колонки роняли
запись в базу ВНЕ защиты, и обход обрывался на середине: остальные клиенты не узнавали,
приняли их рекламу или отклонили, а деньги за отклонённый набор не возвращались.
Запись ответа теперь под той же защитой, что и сеть; пустой статус не пишем вовсе,
причину храним обрезанной.

Робот. Адрес файла приходил в ответе сервера, а шли по нему со своим токеном без
всякой сверки. Теперь адрес обязан вести на портал. Папка снимков экрана росла
бесконечно, а на снимках видны логин и остаток счёта — старше двух недель убираются.

Ещё: порядок посредников служебного канала — токен раньше служебного соединения;
BannerGenerator больше не отдаёт молча файл тяжелее предела и берёт предел из общей
константы; нулевой номер креатива ловится намеренно, а не случайно нестрогим сравнением.

Портал 300/300, робот 60/60. Мест снятия заморозки денег по-прежнему четыре.

Не тронуто намеренно: цена за 1000 показов и бюджет приходят от клиента — но это
видимое поле мастера и принятое продуктовое решение, а не недосмотр. Решает владелец.
2026-07-28 07:21:02 +03:00
Дмитрий 9cc3f5e950 fix реклама за показы: пауза не врёт про остановку, цена запуска остаётся на кампании, робот не дерётся сам с собой
Хвосты денег Д1-Д6 и робота Р-х1-Р-х6 из приёмочного листа v12.

Д1 цена, по которой заморожены деньги, записывается на кампанию. Пока поле было
пустым, списание читало глобальную цену — админ менял её, и клиент платил больше
обещанного при запуске.

Д2 суточное списание берёт кампанию под замком строки. Ключ идемпотентности зависит
от числа показов, поэтому два одновременных прогона получали разные ключи и списали
бы клиента дважды.

Д3 пауза, не дошедшая до Директа, больше не считается паузой: отказ 409, заморозка
остаётся. Раньше реклама крутилась дальше, портал показывал паузу, а деньги были уже
свободны. У возобновления поведение намеренно прежнее — иначе понадобилось бы пятое
место разморозки, а их ровно четыре. Там же убрана мина строгого сравнения рубильника.

Д4 не трогали — это вопрос владельца.

Д5 рубильник Директа держит и служебный канал робота: выдача задания и приём отчёта
ходили в живой кабинет мимо него.

Д6 проверка рубильника приведена к общему виду: YANDEX_DIRECT_ENABLED=0 давало строку,
которую строгое сравнение читало как включено.

Р-х1 настройки читаются из .env робота, а не каталога запуска.

Р-х2 файл-замок robot.lock: проход и поддержание входа больше не дерутся за профиль
браузера. Занят — уходим молча, задание остаётся в очереди. Брошенный замок
перехватывается через полчаса.

Р-х3 письмо-алярм честно говорит, залиты ли уже креативы в кабинет. Побочно вскрылось,
что тексты писем не проверялись ни одним тестом — транспорт вынесен в src/smtp.js.

Р-х4 тест-пустышка про рабочую папку заменён настоящим: запуск из чужого каталога без
явной папки. Проверено вырезанием.

Р-х6 пустое значение в окружении читается как значение по умолчанию, мусор даёт внятную
ошибку вместо тихого NaN.

Портал 293/293, робот 57/57. Денежных выходов снятия заморозки по-прежнему четыре.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 06:36:11 +03:00
Дмитрий 0548fc0d8f fix реклама за показы: слепок креативов при выдаче, сверка размера перед объявлением, один баннер на слот
Хвосты портала П1-П8 из приёмочного листа v12.

П1 обрыв постановки задания больше не даёт клиенту голый 500 — 503 с человеческим
текстом и записью в журнал. Таймаут у HTTP-клиента Laravel уже был, эта половина
находки не подтвердилась.

П2 и П6 роботу отдаются только баннеры без номера креатива — тот же список
сопоставляется при отчёте. Раньше стороны расходились и опознание падало на
безупречной работе робота, плодя дубли в кабинете. Плюс постраничный обход описи
креативов: слепок обрывался на первой странице.

П3 слепок «до» снимается при выдаче задания, а не при постановке, и в той же
транзакции, что и перевод в работу. Два задания в очереди получали одинаковый
слепок, второе падало всегда. Постановка перестала зависеть от живости Яндекса.

П4 перед созданием объявлений сверяется настоящий размер каждого креатива одним
запросом. Не сошлось или креатива нет — запуск не идёт.

П5 уникальный индекс uq_ad_campaign_banner_slot, запись v9.09 в CHANGELOG схемы,
rls-reviewer GO.

П7 замок на правку расширен на сегмент Аудиторий — он создаётся раньше кампании.
Смежная находка: перезаливка картинки теперь обнуляет номер креатива.

П8 и Р-х5 файл отдаётся под настоящим расширением и типом содержимого, имя от
номера баннера; робот берёт расширение из ответа портала.

Портал 287/287, робот 43/43. Денежных выходов снятия заморозки по-прежнему четыре.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 05:37:56 +03:00
Дмитрий 542b7d2d22 fix реклама за показы: очередь робота больше не встаёт колом, доклад об успехе не врёт, тихий ноль ловится
Р6 — задание, брошенное «в работе», держало очередь для всех клиентов навсегда.
Причина сбоя обрезается до 900 знаков, иначе портал отвергал отчёт о самом частом
виде поломки. Провал отчёта больше не глотается молча — едет в письмо человеку.
Приём отчёта ловит любую ошибку и закрывает задание сбойным вместо 500 роботу.
Новый сторож creative-jobs:reap каждые 10 минут разбирает пробку.

Р7 — обрыв на докладе «готово» объявлял провалом уже сделанную работу. Доклад
вынесен из-под обработчика сбоя, повторяется трижды, при неудаче только письмо.
Осечка ухода со страницы после «Создать» больше не считается провалом загрузки.

Р8 — пустой список размеров считался успехом, и робот заливал одни и те же файлы
по кругу, оставляя мусор в живом кабинете. Теперь громкий отказ до похода в Яндекс.

Портал 274/274, робот 41/41. Все защиты проверены вырезанием.
Выходов снятия заморозки денег по-прежнему четыре.
2026-07-27 20:42:23 +03:00
Дмитрий b8f75b2a0c fix реклама за показы: файл робота только по своему заданию, замок на баннерах, защита от двойного запуска
Р3 хвост. Адрес файла баннера стал /api/creative-robot/jobs/{jobId}/banners/{bannerId}/file.
Раньше портал подставлял «какое-нибудь задание в работе» — защита выдачи чужих картинок
держалась на внешнем условии, а не на самом запросе. Робота править не пришлось: он берёт
адрес из ответа портала как есть. Запись v9.08 в журнал схемы про частичный уникальный
индекс; rls-reviewer по миграции — GO.

Р4. Замок на наборе баннеров после заведения кампании в Яндексе: перезаливка, включение
и удаление отдают 409 по тому же признаку yandex_campaign_id, что и замок на параметрах.
Без него в портале была новая картинка, а в Яндексе крутилась старая, а удаление строки
с номером объявления заставляло возобновление завести второе объявление того же размера —
старое продолжало крутиться за деньги клиента. Баннер с номером объявления не удаляется
никогда.

Р5. Захват кампании под запуск: новый промежуточный статус launching, перевод под замком
строки в транзакции. Два одновременных нажатия «запустить» проходили проверку статуса оба
и заводили две CPM-кампании при одной заморозке денег. На любой ошибке прежний статус
возвращается, номера созданных в Яндексе сущностей уцелевают — возобновляемый запуск
не тронут. Брошенный захват старше пятнадцати минут перехватывается.

Тесты: портал 266/266, робот 35/35. Все новые защиты проверены вырезанием.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:06:57 +03:00
Дмитрий 20d9dc6d3a feat робот креативов: цикл работы, сторож, письма и описание для человека
Любой сбой даёт отчёт порталу и письмо со снимком экрана, кампания остаётся
черновиком. Отчёт порталу идёт ПЕРВЫМ: упавшая почта не должна подвешивать
задание в работе. Картинки клиента стираются при любом исходе.

Поправлены два расхождения плана с живым кодом: в кабинет уходят пути к файлам,
а не описания баннеров, и папки считаются от корня робота, а не от каталога
запуска - робота запускает расписание.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:22:35 +03:00
Дмитрий 812fed7772 feat робот креативов: браузер, проверка входа и загрузка креативов по живой разметке кабинета
Заглушка плана заменена настоящим кодом: разметка снята в задаче 11.

Прихожая — существующие кампания и группа из настроек: своей кампании у робота
в этот момент ещё нет, портал ставит задание до её создания в Яндексе.

Окно, не закрывшееся после Создать, провалом НЕ считается — оно может остаться
открытым и после удачной загрузки. Робот закрывает его крестиком и отдаёт слова
кабинета наружу, а приговор выносит портал по слепку креативов.

Кнопку Сохранить изменения робот не нажимает никогда — на это есть тест-сторож.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:18:39 +03:00
Дмитрий 602b8f5cb2 feat робот креативов: разговор с порталом — взять задание, скачать файлы, отчитаться
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:02:45 +03:00
Дмитрий c548fb73b7 feat робот креативов: скелет проекта и конфиг
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 18:02:30 +03:00
Дмитрий 5b8813b033 docs робот креативов: разметка экранов загрузки в кабинете Директа
Снято живьём вместе с владельцем в кабинете sasha261185, кампания 713051718.
Ничего не сохранялось и не создавалось: до и после — те же 15 объявлений,
кнопки «Сохранить изменения» и «Создать» не нажимались.

Ключевое для задачи 14: системное окно выбора файла роботу не нужно —
в форме есть input type=file с data-testid CreativeActionsMenu.FileInput,
принимает png/jpeg/gif и сразу несколько файлов. Весь путь до окна загрузки
проверен программным прогоном, окно открылось.

Подтверждено живьём: конструктор креативов закрыт, Яндекс пишет это сам;
у всех 15 креативов имя «Новый креатив», потому что поле имени одно на всю
загрузку — опознание по разнице слепков и размеру остаётся единственным
надёжным способом.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 17:41:56 +03:00