Замер живой, боевая база и кабинет Яндекса, только чтение.
Что нашлось:
- три задания upload 29.07 (16:16/16:40/16:49), каждое спотыкалось
на РАЗНОЙ картинке: 160x600, 300x600, 336x280;
- обе причины уже вылечены: спешка робота при «Создать» и пересъёмка
слепка «до» при повторе;
- улика роста мусора — snapshot_before пух 466 -> 829 -> 1192 знака;
- вреда нет: все 15 из 15 баннеров имеют креатив и объявление;
- мусор в кабинете посчитан: 73 картинки, 32 заняты, 41 свободна.
Владелец решил не чистить — работа по Яндексу закрыта.
Три новые ловушки в промт:
- удалить креатив программно НЕЛЬЗЯ: Creatives умеет только add/get;
- SelectionCriteria — пустой объект, не массив (отказ 8000);
- в кабинете 4 кампании и 33 объявления, «лишнее» считать по всем,
иначе выходит 58 вместо настоящих 41.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Владелец 01.08.2026: «документов нет, забудь — мы это делали для того,
чтобы научить робота». Кампания 713175197 была учебной.
- хвост №1 переведён из «главное, ждём» в «закрыт»: требование документов
у Яндекса — ожидаемое состояние, а не наша поломка;
- прямой запрет следующей смене дожимать показы и искать обход;
- итог смены переписан: единственное живое направление — телеграм;
- поправлена вершина main (3b158a62f → 96c92b9f8).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Заменяет 2026-08-01-PROMT-zakryt-hvosty.md: его хвосты №1, №2, №4 и главная
часть №5 закрыты и приняты живьём на боевом.
Осталось пять пунктов, и ни один не лечится кодом: документы Яндексу,
телефоны в чужой ветке менеджеров, четыре старых английских сообщения,
три сбойных заливки картинок от 29.07, телеграм по порядку Г1-Г2-Г3.
Ловушки собраны заново и разложены по темам: кабинет Яндекса, чтение боевой
базы, гит и сторожа, тесты и окружение. Новые за эту смену — про заглушённые
ошибки, про колонку author вместо sender, про зависание ssh с tinker в одной
команде и про многострочное сообщение коммита.
Только документы. Кода и боевого не касается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Копятся по порядку, каждый следующий заменяет предыдущий: вход в кабинет
и живой режим → хвосты после живого режима → после сведения веток →
закрыть хвосты. Последний рабочий, его читать первым.
В последнем помечены как НЕВЕРНЫЕ три вывода прежних смен, оставленные
на месте нарочно: «семь объявлений пропали», «разведчика негде запускать»,
«вернул 13 заданий в очередь». Под каждым — чем оказалось на самом деле.
Только документы. Кода и боевого не касается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Первый боевой обход с починками модерации поставил разведке 13 заданий
на придержанные объявления кампании 713175197 — и все 13 упали. Разбор
руками в живом кабинете дал две причины.
1. Семи объявлений из пятнадцати в списке кабинета нет вовсе — это хвост
от неудачных перезаливок креативов 29.07. Робот докладывает «объявления
нет», и он прав. Чинить тут нечего, но в базе портала эти семь строк
висят как живые — разбираться отдельно.
2. У остальных шести клик по ячейке статуса не открывает НИЧЕГО. Проверено
руками: ячейка есть, кликается, окно не появляется за 20 секунд, и на
странице после клика нет ни одного элемента с popup в разметке. Робот
падал сырой ошибкой Playwright.
И раскрывать было нечего: вся причина написана прямо в списке —
«Для показа в заданных регионах предоставьте документы». У отклонённого
объявления ровно наоборот: в списке пусто, суть спрятана в окне. Робот
писался под второй случай и на первом падал, имея причину перед глазами.
Лечение: надпись из списка забираем ДО клика. Окна нет — смотрим, есть ли
в надписи что-то кроме общих слов кабинета. Есть — это и есть доклад; одни
общие слова — сдаёмся, как раньше, и объявление уходит человеку в «ждёт
разбора». Выдумывать по-прежнему нельзя ничего.
Замеры. Сторож написан ДО починки и падал. Полный набор робота 86 из 86.
Приёмка глазами по живому кабинету, три задания подряд: придержанное
принесло настоящую причину (раньше падало), отклонённое — прежний полный
текст про предупреждение о финансовых услугах, отсутствующего робот честно
не выдумал.
Разбор с дословными подписями — cabinet-flow.md §7.10.
🪤 На будущее: 13 упавших заданий сами не переиграются — защита от дублей
не пускает второе задание на то же объявление независимо от его состояния.
На боевой НЕ выкатывалось.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Владелец объявил закрытыми обе телеграм-ветки и велел свести их вместе,
ювелирно и ничего не потеряв. Ветка робота несла 9 коммитов, которых
у меня не было, и трогала 79 файлов.
Конфликта три:
- app/tests/Frontend/advertising-channels.spec.ts — настоящий, в коде.
Обе стороны сторожили одно: у кого есть настоящий экран, а кто заглушка.
Моя перечисляла живые площадки прямо в тесте, версия робота берёт их
из общего списка REAL_ROUTES наверху файла и вдобавок проверяет, что
заглушки вообще остались. Проверил список: там и Яндекс, и Телеграм —
покрытие то же, проверка сильнее. Взял версию робота, файл вышел байт
в байт как у него. Сторож прогнан отдельно, зелёный;
- app/phpstan-baseline.neon — машинный, пересобран заново по проектной
процедуре в два шага, phpstan.neon возвращён на место, итог 0 ошибок;
- docs/observer/STATUS.md — машинный файл наблюдателя, взята своя версия,
его всё равно переписывает хук.
Доказательство, что ничьё не пропало — сверкой, а не на слово:
- файлов, где результат отличается от версии робота, а я их НЕ трогал:
ноль. То есть ни одна его правка не откатилась молча;
- файлов, отличающихся от main: 79, и все 79 объясняются нашей работой.
Необъяснённых ноль — значит чужая работа из main не затёрта.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Мой коммит уехал в main отдельно — пересадкой на актуальную вершину
и оттуда на боевой. Возвращаю main к себе, чтобы ветка не разъезжалась.
Пришло 9 коммитов, из них главное — работа соседней сессии по воронке
продаж, уже влитая в main и запушенная.
Конфликта два, оба в общих машинных файлах:
- cspell-words.txt — сторону не выбирал, объединил. Все слова обеих
сторон на месте; убрана ровно одна строка — мой же дубль «админский»,
который прошлый коммит добавил дважды. Проверено сравнением с версией
до слияния: другого отличия нет;
- docs/observer/STATUS.md — машинный файл наблюдателя со столбиком часов
процессов, взята своя версия, его всё равно переписывает хук.
Замечание на будущее: патч «минимум площадки считается по длине периода»
живёт в двух коммитах — 948807507 у меня и в ветке робота, 9df7846fd
в main. В main уехала только вторая копия, main чист. Когда моя ветка
и ветка робота пойдут в main, git встретит этот патч второй раз.
Статанализ остановил слияние, и это была НЕ ложная тревога: файл-эталон
phpstan-baseline.neon склеился из двух версий и перестал соответствовать
коду. Все 16 ошибок — в чужих файлах воронки продаж, пришедших из main
байт в байт, известного ложного класса про Pest. Эталон из main один
не подошёл: всплыли записи, нужные этой ветке. Пересобрал по проектной
процедуре в два шага, phpstan.neon вернул на место. Итог 0 ошибок,
правка эталона мелкая: +43 -31. Моих файлов среди добавленных записей
нет ни одного.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Просьба владельца 01.08.2026: «убери вчера, наверх создай просроченные,
сегодня, завтра и т.д. и проверь что они правильно привязаны и реально
работают»; во втором режиме «убери завтра — завтра у тебя не может быть».
У каждого режима теперь СВОЙ список сроков, они не пересекаются:
Что надо сделать (вперёд): Просроченные / Сегодня / Завтра /
Ближайшие 7 дней / Ближайшие 30 дней / Произвольный
Что менялось (назад): Сегодня / Вчера / 7 дней / 30 дней / Произвольный
Каждый пункт показывает ровно то, что на нём написано. Раньше просроченное
подмешивалось в ЛЮБОЙ выбранный период, и «Сегодня» показывало не только
сегодняшнее — теперь это отдельный первый пункт (period=overdue), а подпись
«плюс всё просроченное» убрана за ненадобностью.
Вторая, невидимая глазом поломка: «7/30 дней» в режиме планов считались
НАЗАД (d7/d30) — «что надо сделать за прошедшую неделю». Добавлены зеркала
next7/next30 в SalesPeriodResolver.
Срок из чужого набора («что менялось завтра») сервер отвергает с 422, а не
подменяет молча текущим месяцем, как делал прежний резолвер по умолчанию.
Проверено:
- сервер: 30/30 (фильтр + резолвер), весь отдел продаж 498/498;
- фронт: 1739/1739 весь набор;
- приёмка вырезанием — подложил поломку в оба места, оба набора покраснели;
- глазами в браузере 1920×1080: списки сроков в обоих режимах, «Просроченные»
дают ровно забытые карточки, «Завтра» — ровно завтрашнюю (скрины 08–12).
Попутно починен чужой протухший тест advertising-channels: Телеграм давно
стал живым роутом, а тест продолжал считать его заглушкой и был красным.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 975175750a)
Кампания 713175197 третьи сутки не показывалась при замороженных у клиента
деньгах, а программный интерфейс на всех уровнях рапортовал «Идут показы».
Правду отдала только подпись на экране кабинета: «Для показа в заданных
регионах предоставьте документы» — 13 объявлений придержаны, 2 отклонены.
1. Яндекс отвечает машине ПО-АНГЛИЙСКИ, если не попросить русский.
Замер боевым ключом, два одинаковых запроса подряд: без заголовка
«Rejected at moderation.», с Accept-Language ru «Отклонено на модерации.».
Английская строка уезжала клиенту в переписку и письмом как пояснение
модератора, честная заглушка «причину выясняем» не срабатывала никогда,
а на следующем обходе английская строка ложилась ПОВЕРХ доклада разведчика
— проверено по боевой ленте: 30.07 в 15:02 робот принёс полную причину,
в 17:00 её накрыло. Лечение: спрашиваем язык явно плюс второй заслон —
английские отписки узнаются как отписки.
2. Отказ включения Яндекс кладёт в Warnings, а код смотрел только в Errors.
Ответ целиком: ResumeResults с Warnings 10201 «Объявление не остановлено»
при пустом Errors. Исключения нет, портал считал, что справился: двое суток
обход каждые два часа поднимал 13 объявлений, ноль записей в журнале.
Лечение: resumeAds возвращает, кого включить не дали.
3. Новый исход «принято, но придержано» порталу не был известен вовсе.
Вердикт ACCEPTED, ярлыка «Отклонено» нет, разведчик не ходил — клиент видел
«Идут показы» при нулевых показах. Теперь по отказу включения клиенту идёт
сообщение «Яндекс принял объявление, но пока не показывает его. Причину
выясняем» — текст выбран владельцем — и туда же едет разведка.
Ловушка, обойденная по дороге: включение спрашиваем ДО разбора объявлений.
Наоборот — сообщение о придержке легло бы поверх доклада разведчика, и обход
начал бы чередовать их по кругу: защита от дублей смотрит на последнее сообщение.
Замеры. Восемь новых сторожей, все написаны ДО починки и падали именно
на живых данных. Отдельный сторож на противоположный случай — включённому
объявлению разведку не заводим — был зелёным с самого начала.
Портал 4083 теста, 4043 прошло, 16 упало: те же шесть давних классов и ровно
те же числа, что до работы, было 4069/4029/16. Плюс 14 тестов — ровно новые.
Pint и Larastan по изменённым файлам чистые.
Главный урок записан в cabinet-flow.md §7.9: в §7.7 лежит правдивый РУССКИЙ
ответ Яндекса, снятый ДРУГИМ инструментом, который язык просил. Разбор отписок
построили по показаниям прибора, которым продукт не пользуется. Мерить надо той
же дорогой, по которой ходит боевой код.
На боевой НЕ выкатывалось.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Кампания 713175197 третьи сутки не показывалась при замороженных у клиента
деньгах, а программный интерфейс на всех уровнях рапортовал «Идут показы».
Правду отдала только подпись на экране кабинета: «Для показа в заданных
регионах предоставьте документы» — 13 объявлений придержаны, 2 отклонены.
1. Яндекс отвечает машине ПО-АНГЛИЙСКИ, если не попросить русский.
Замер боевым ключом, два одинаковых запроса подряд: без заголовка
«Rejected at moderation.», с Accept-Language ru «Отклонено на модерации.».
Английская строка уезжала клиенту в переписку и письмом как пояснение
модератора, честная заглушка «причину выясняем» не срабатывала никогда,
а на следующем обходе английская строка ложилась ПОВЕРХ доклада разведчика
— проверено по боевой ленте: 30.07 в 15:02 робот принёс полную причину,
в 17:00 её накрыло. Лечение: спрашиваем язык явно плюс второй заслон —
английские отписки узнаются как отписки.
2. Отказ включения Яндекс кладёт в Warnings, а код смотрел только в Errors.
Ответ целиком: ResumeResults с Warnings 10201 «Объявление не остановлено»
при пустом Errors. Исключения нет, портал считал, что справился: двое суток
обход каждые два часа поднимал 13 объявлений, ноль записей в журнале.
Лечение: resumeAds возвращает, кого включить не дали.
3. Новый исход «принято, но придержано» порталу не был известен вовсе.
Вердикт ACCEPTED, ярлыка «Отклонено» нет, разведчик не ходил — клиент видел
«Идут показы» при нулевых показах. Теперь по отказу включения клиенту идёт
сообщение «Яндекс принял объявление, но пока не показывает его. Причину
выясняем» — текст выбран владельцем — и туда же едет разведка.
Ловушка, обойденная по дороге: включение спрашиваем ДО разбора объявлений.
Наоборот — сообщение о придержке легло бы поверх доклада разведчика, и обход
начал бы чередовать их по кругу: защита от дублей смотрит на последнее сообщение.
Замеры. Восемь новых сторожей, все написаны ДО починки и падали именно
на живых данных. Отдельный сторож на противоположный случай — включённому
объявлению разведку не заводим — был зелёным с самого начала.
Портал 4083 теста, 4043 прошло, 16 упало: те же шесть давних классов и ровно
те же числа, что до работы, было 4069/4029/16. Плюс 14 тестов — ровно новые.
Pint и Larastan по изменённым файлам чистые.
Главный урок записан в cabinet-flow.md §7.9: в §7.7 лежит правдивый РУССКИЙ
ответ Яндекса, снятый ДРУГИМ инструментом, который язык просил. Разбор отписок
построили по показаниям прибора, которым продукт не пользуется. Мерить надо той
же дорогой, по которой ходит боевой код.
На боевой НЕ выкатывалось.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Первая редакция промта вела следующую смену прямо в аварию: шаг «выкат файлов»
без единой проверки, насколько ветка отстала от основной. На 01.08 отставание
было 7 коммитов, и это блок менеджеров на боевом. Залей папку целиком - и
воронка продаж с корзиной и фильтрами исчезла бы с боевого сайта.
Вписано правило замера: правое число в git rev-list --left-right --count
обязано быть 0 перед выкатом, иначе сперва сведение и повторный прогон.
Отмечено, что основная продолжает двигаться, и сведение надо повторять
непосредственно перед выкатом, а не заранее.
Дописаны две ловушки. Первая: столкновение номеров журнала схемы git НЕ
показывает как конфликт, молча склеивает две записи с одним номером - дан
готовый датчик на дубли. Вторая: фронтовый набор был красным пять дней, потому
что его не гоняли целиком, - велено гонять vitest целиком, а не по файлу.
Пункт про журнал схемы переписан с «дописать v9.31» на «сделано, номер v9.32».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Первая редакция промта вела следующую смену прямо в аварию: шаг «выкат файлов»
без единой проверки, насколько ветка отстала от основной. На 01.08 отставание
было 7 коммитов, и это блок менеджеров на боевом. Залей папку целиком - и
воронка продаж с корзиной и фильтрами исчезла бы с боевого сайта.
Вписано правило замера: правое число в git rev-list --left-right --count
обязано быть 0 перед выкатом, иначе сперва сведение и повторный прогон.
Отмечено, что основная продолжает двигаться, и сведение надо повторять
непосредственно перед выкатом, а не заранее.
Дописаны две ловушки. Первая: столкновение номеров журнала схемы git НЕ
показывает как конфликт, молча склеивает две записи с одним номером - дан
готовый датчик на дубли. Вторая: фронтовый набор был красным пять дней, потому
что его не гоняли целиком, - велено гонять vitest целиком, а не по файлу.
Пункт про журнал схемы переписан с «дописать v9.31» на «сделано, номер v9.32».
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Владелец спросил, не потрём ли мы соседнюю смену. Проверил - потёрли бы.
Наша ветка отстала от основной на 7 коммитов, и это ровно сегодняшняя работа
по воронке продаж: корзина, журнал движений карточки, фильтр по датам, дробь
у Отказа. Выкат по плану был ПОЛНЫЙ заливкой файлов - боевой сайт получил бы
портал без всего этого. Соседи выкатывали точечно, шестью файлами, поэтому
у них и не рвануло.
СТОЛКНОВЕНИЕ НОМЕРОВ ЖУРНАЛА СХЕМЫ, ТРЕТИЙ РАЗ ЗА СУТКИ. Час назад я записал
v9.31 за заголовок объявления. В это же время соседи записали v9.31 за корзину
воронки и влили в основную. Обе записи настоящие. Git конфликта НЕ ПОКАЗАЛ -
просто склеил две записи с одинаковым номером в один файл, молча. Наша
подвинута на v9.32, боевая осталась на своём номере.
Пересобран список исключений статанализа: после сведения он врал счётчиком
90 против 95 и не знал новых файлов соседей. Проверено, что пересборка ничего
настоящего не спрятала - все 16 замечаний были одного вида, ложная тревога
PendingCalls, плюс одно про форму данных внутри задания робота, тоже в тесте.
ПОЧИНЕН ВРУЩИЙ СТОРОЖ ВИТРИНЫ РЕКЛАМНЫХ КАНАЛОВ. Тест утверждал, что настоящий
экран есть только у Яндекса, а Телеграм получил свой ещё 27.07 - сторож пять
дней держал фронтовый набор красным, и этого никто не видел, потому что фронт
целиком не гоняли. Список настоящих каналов ведётся руками намеренно: смысл
сторожа - поймать случайно прописанный маршрут у канала-заглушки.
Приёмка сторожа вырезанием: на честном коде проходит, на подложенном маршруте
для ВК падает, после возврата снова проходит. Файл каналов вернулся байт в байт.
Замеры после сведения:
- портал 4091 тест, 4087 прошло, 0 падений, 0 ошибок
- фронт 232 файла, 1739 тестов, 0 падений
- статанализ 0 замечаний
- робот 130 из 130
На бой ничего не выкачено. Боевой сайт не тронут.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
По правке владельца со скрина: блок фильтра по датам переехал из отдельной
строки под заголовком в общий ряд справа — к «Менеджер» и «Происхождение».
Порядок внутри блока перевёрнут: САМ ФИЛЬТР крайний справа, СРОК левее него.
Читается справа налево: «что менялось» → «вчера».
Ряд получил flex-wrap: на узком экране переносится, а не уезжает за край.
На экране менеджера тот же ряд — экраны не должны разъезжаться.
Порядок закреплён тестом, иначе его снова переставят.
Перед слиянием с main я откатил 4 файла к своей старой версии, чтобы
сдвинуть с места застрявшее слияние — и слияние закрепило этот откат.
Пострадала починка от 29.07: чтение связок поставщика из pivot (без неё
5 из 9 работающих проектов показывали жёлтое «Готовим к запуску» при
живом заказе) и два набора тестов вебхука/сверки CSV.
Файлы возвращены к состоянию main 7361d1ae3. На боевой откат НЕ уезжал —
выкат был точечный, только 6 файлов воронки продаж; проверено на живом
сервере: починка там на месте.
Владелец решил катить. Промт написан под это, а не под хвосты.
Главное, что вскрылось при подготовке: работа разошлась по ДВУМ веткам, и ни
одна не содержит другую. У соседней ветки tg-zagolovok нет наших трёх правок
тестов от 01.08, у нас нет их заголовка объявления. Обе от основной не
отстают. Поэтому первый шаг смены - свести ветки, иначе выкатим половину.
Выкат намеренно разрезан надвое. Портал можно катить сразу: переключатель
остаётся в старом положении, поведение не меняется, новые таблицы и канал
уезжают спящими. Робота включать некуда, пока не решено, на какой машине он
живёт - МТС не пускает адреса дата-центров, это упирается в обращение 31074.
В промте прописаны обязательные пред-полётные: журнал схемы для их миграции
записью v9.31 - её нет вообще, а это то самое правило, из-за нарушения
которого ночью столкнулись номера; пересборка списка исключений статанализа
после сведения; проверка их нового теста на добавленный запрет выхода
в интернет; ПЕРЕзапуск service_bypass_policies, иначе канал робота увидит
ноль заданий молча.
Приёмка - глазами в кабинете МТС, а не по журналу портала. Добавлены две
находки для владельца, ничего не тронуто, и одиннадцать ловушек.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Владелец решил катить. Промт написан под это, а не под хвосты.
Главное, что вскрылось при подготовке: работа разошлась по ДВУМ веткам, и ни
одна не содержит другую. У соседней ветки tg-zagolovok нет наших трёх правок
тестов от 01.08, у нас нет их заголовка объявления. Обе от основной не
отстают. Поэтому первый шаг смены - свести ветки, иначе выкатим половину.
Выкат намеренно разрезан надвое. Портал можно катить сразу: переключатель
остаётся в старом положении, поведение не меняется, новые таблицы и канал
уезжают спящими. Робота включать некуда, пока не решено, на какой машине он
живёт - МТС не пускает адреса дата-центров, это упирается в обращение 31074.
В промте прописаны обязательные пред-полётные: журнал схемы для их миграции
записью v9.31 - её нет вообще, а это то самое правило, из-за нарушения
которого ночью столкнулись номера; пересборка списка исключений статанализа
после сведения; проверка их нового теста на добавленный запрет выхода
в интернет; ПЕРЕзапуск service_bypass_policies, иначе канал робота увидит
ноль заданий молча.
Приёмка - глазами в кабинете МТС, а не по журналу портала. Добавлены две
находки для владельца, ничего не тронуто, и одиннадцать ловушек.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Промт следующей смене врал в самом громком месте: хвост Н1 был записан как
срочный - "похоже, протух боевой ключ Яндекс Директа". Это оказалось ложной
тревогой, и написал её я сам, не проверив трёх фактов, опровергающих её
за пять минут. Ключ в тесте подставной, локально боевого ключа нет вовсе,
а в Яндекс тест ходил потому, что подбирал чужое задание из базы.
Раздел Н1 переписан: пометка "закрыт, тревога ложная", разбор настоящей
причины и урок про слово "срочно" в промте. Раздел Х4 помечен закрытым -
тест внешних сервисов больше не зависит от сети.
В Х2а вписан готовый датчик поиска файла, который гадит в базу, и ловушка
про то, что искать уборку словом нельзя. В Х2б у "чужого писателя" появилось
имя - параллельная смена в папке tg-zagolovok, она ходит в общую базу.
Добавлены две находки для владельца, ничего не тронуто: ночная сборка слепка
падает целиком от одного проекта без источника лидов, и начальник отдела
при запросе всего отдела видит всех клиентов системы.
В ловушки дописан запрет на выход в интернет из тестов с пояснением, почему
его нельзя снимать.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Промт следующей смене врал в самом громком месте: хвост Н1 был записан как
срочный - "похоже, протух боевой ключ Яндекс Директа". Это оказалось ложной
тревогой, и написал её я сам, не проверив трёх фактов, опровергающих её
за пять минут. Ключ в тесте подставной, локально боевого ключа нет вовсе,
а в Яндекс тест ходил потому, что подбирал чужое задание из базы.
Раздел Н1 переписан: пометка "закрыт, тревога ложная", разбор настоящей
причины и урок про слово "срочно" в промте. Раздел Х4 помечен закрытым -
тест внешних сервисов больше не зависит от сети.
В Х2а вписан готовый датчик поиска файла, который гадит в базу, и ловушка
про то, что искать уборку словом нельзя. В Х2б у "чужого писателя" появилось
имя - параллельная смена в папке tg-zagolovok, она ходит в общую базу.
Добавлены две находки для владельца, ничего не тронуто: ночная сборка слепка
падает целиком от одного проекта без источника лидов, и начальник отдела
при запросе всего отдела видит всех клиентов системы.
В ловушки дописан запрет на выход в интернет из тестов с пояснением, почему
его нельзя снимать.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- 12-я колонка «Корзина», результат «В корзину» просит только причину;
- в шапке «Отказа» дробь «4/2» с подсказкой «из них 2 после ручного
тестирования»; при нуле дробь не рисуется — «69/0» это шум;
- у «Выслано КП» поле даты подписано «если договорились» и необязательно;
- орган фильтра по датам на ОБОИХ экранах, два режима + период.
Приёмка глазами пройдена живьём по всем семи пунктам, включая «Воронку отдела»
(снимки в docs/superpowers/screens/2026-08-01-korzina-filtry/priemka/).
Браузер поймал то, чего не видели тесты: при смене режима оставался прежний
период, и «что менялось за завтра» давало пустую доску — теперь период
возвращается к «Сегодня», на это заведён отдельный тест.
Прогон: сервер 1245/1245, фронт 1567/1567. Журнал схемы — v9.31.
Полный набор стал зелёным целиком: 4063 теста, 0 падений и 0 ошибок против
36 непроходящих до правок. Рабочий код не тронут - изменены только тесты.
Корень у пяти правок один: тест зелёный, а после себя оставляет мусор в базе,
и падают соседи. Три файла рекламы писали набело, без отката. Их записи -
задание роботу и проекты без источника лидов - переживали тест, и дальше
в том же прогоне их подбирали чужие проверки. Отсюда 16 падений в файле
про робота креативов и 13 в файлах про ночной слепок.
Тестам запрещён выход в интернет - Http::preventStrayRequests в TestCase.
До этого прогон физически ходил в живой кабинет Яндекс Директа, а поломка
маскировалась под "Invalid OAuth token", хотя ключ в тесте подставной.
Сторож принят вырезанием: со снятой правкой та же поломка называет себя
честно, с адресом запроса.
Он же вскрыл, что ProjectRuleNotificationTest слал живой запрос на удаление
проекта в кабинет поставщика crm.bp-gr.ru, оставаясь зелёным - ответ кабинета
тест не проверяет. Поставлена заглушка.
ExternalServiceDownAlertTest избавлен от зависимости от сети - закрыт хвост,
тянувшийся с 30.07: падал в общем прогоне, проходил в одиночку.
InAppNotificationTest брал первую попавшуюся запись во всей таблице вместо
своей. SalesOverviewTest попадал в топ-4 клиентов по удаче: при запросе
всего отдела отбор не ограничен ничем, все тенанты базы с нулём лидов равны.
Клиенту даны настоящие лиды - место в четвёрке заслуженное.
Проверено: полный прогон 4063/4059 зелёный, статанализ 0, код-стиль моих
файлов чисто. На бой ничего не выкачено.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Влил fix/robot-yandex-zamok (26 коммитов: сведение с основной, стадии воронки
«Тестирование ручное» и «Выслано КП», чтение вердикта и пересдача по опросу)
в ветку заголовка объявления.
Конфликт был один — docs/observer/STATUS.md, машинный файл наблюдателя
со столбиком часов процессов. Взята своя, более свежая версия; файл всё равно
перезаписывается хуком.
Замер после слияния (своя тестовая база liderra_testing_zag, прогон в тишине):
портал 4069 тестов, 4029 прошло, 16 упало; робот 130/130.
До слияния было 4063/4018/17. Тестов больше, падений меньше — стык чистый.
Падают шесть давних классов, не связанных с этой работой: CreativeRobotEndpoint
(11 — лезет в живой Директ и получает «недействительный ключ»), CreativeJobService,
InAppNotification, PhoneRegionSmoke, ProjectExtensions, SalesOverview.
Проверено отдельно: таблица заданий роботу не ограничивает список режимов
(обычная строка), поэтому новые «read-status» и «resubmit» миграции не требуют.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- результат «В корзину»: одна причина, дата созвона стирается, платящего не выбросить;
- реклама на корзине встаёт СРАЗУ — явной веткой, а не случайно через unknown_stage;
- у «Выслано КП» дата созвона стала необязательной: бывает «пришлите на почту,
если интересно — перезвоню». Врущий старый тест на 422 без даты поправлен;
- фильтр доски date_mode=todo|changed + период (добавлен вид «завтра»).
«Надо сделать» — созвон в периоде ИЛИ просрочен, кроме отказа и корзины.
«Менялось» — есть движение стадии ИЛИ запись разговора за период.
Прогон: 1245/1245 (отдел продаж + все юнит-тесты).
Стадию меняют два разных места — результат разговора менеджера и автопереезд
по деньгам. Джоба не оставляла следа вообще, поэтому «что менялось за день»
было не из чего построить. Запись движения перенесена в событие модели: один
шов на всех, включая любой будущий третий источник.
- sales_prospect_moves — журнал всех движений (append-only, с GRANT'ами ролям);
- prev_stage на карточке — откуда приехала (кормит счётчик «69/1» у «Отказа»);
- stage += 'trash' — место под колонку «Корзина».
Сторож проверен вырезанием: без записи движения краснеют 4 теста, в том числе
тест джобы и тест API.
Приёмка глазами вскрыла: кабинет МТС требует «Заголовок объявления» (до 40 знаков),
когда в объявлении ссылка на САЙТ, а не на телеграм-канал. Робот про это поле не знал,
«Продолжить» молча не срабатывало, кампания вставала на шаге «Объявление» — в бою уже
ПОСЛЕ списания денег. Проверено живьём: 2234454 (сайт — встала) против 2234462 (канал —
дошла до подтверждения) и 2234490 (сайт с заголовком — дошла).
Портал спрашивает заголовок заранее, на создании черновика: обязателен только для
не-телеграмной ссылки (App\Support\TelegramLink), колонка ad_headline varchar(40),
поле на экране появляется по той же развилке. Робот заполняет его в кабинете.
Три ловушки, добытые живыми прогонами (описаны в коде):
- поле дорисовывается в ОТВЕТ на ссылку, с задержкой — надо ждать, а не спрашивать;
- под описание подходит несколько элементов — нужен .first();
- серая надпись внутри поля НЕ placeholder, а нарисованная подпись: поиск по атрибуту
давал ноль совпадений при видимом на снимке поле. Опознаём по видимой надписи.
Тесты: робот 130/130, ClientTg 250/250, экран 23/23. Полный прогон бэкенда — те же
13 падающих классов до и после правки (ни одного в телеграм-части). В baseline
статанализа добавлен известный ложный класс Pest для нового файла тестов.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Просьба записана дословно, три скрина описаны словами, заведена папка
docs/superpowers/screens/2026-08-01-korzina-filtry/ под сами картинки.
Собраны находки по коду (счётчик колонки, фильтры доски, отсутствие
истории стадий) и четыре вопроса владельцу с рекомендациями.
Прежний промт помечен устаревшим - в нём закрыт хвост Х1, изменилось состояние
ветки и у хвоста про тестовую базу нашёлся более грубый корень. Оставлен ради
разбора, как разруливать столкновение номеров журнала схемы.
В новом промте пять хвостов, и один из них новый и срочный: в полном прогоне
одиннадцать падений подряд с ответом живого Яндекс Директа "Invalid OAuth token".
Похоже, протух ключ доступа, а на бою в Директе запущена живая кампания с
замороженными деньгами. Порядок разбора - сначала посмотреть, а не чинить, и
доложить владельцу до любых действий с токеном.
По тестовой базе записан готовый прибор: сравнить число записей о применённых
миграциях с числом файлов миграций. Больше файлов - в базу лезет чужая рабочая
папка, меньше - сборка оборвалась. Замер в промте: на общей базе телеграм давал
42 из 244, на своей 244 из 244 два раза подряд.
Хвост про хрупкий тест внешних сервисов, возможно, отпал сам - в последнем
прогоне на своей базе он не упал. В промте написано, как это проверить и как
закрыть запись честно.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Прежний промт помечен устаревшим - в нём закрыт хвост Х1, изменилось состояние
ветки и у хвоста про тестовую базу нашёлся более грубый корень. Оставлен ради
разбора, как разруливать столкновение номеров журнала схемы.
В новом промте пять хвостов, и один из них новый и срочный: в полном прогоне
одиннадцать падений подряд с ответом живого Яндекс Директа "Invalid OAuth token".
Похоже, протух ключ доступа, а на бою в Директе запущена живая кампания с
замороженными деньгами. Порядок разбора - сначала посмотреть, а не чинить, и
доложить владельцу до любых действий с токеном.
По тестовой базе записан готовый прибор: сравнить число записей о применённых
миграциях с числом файлов миграций. Больше файлов - в базу лезет чужая рабочая
папка, меньше - сборка оборвалась. Замер в промте: на общей базе телеграм давал
42 из 244, на своей 244 из 244 два раза подряд.
Хвост про хрупкий тест внешних сервисов, возможно, отпал сам - в последнем
прогоне на своей базе он не упал. В промте написано, как это проверить и как
закрыть запись честно.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Основная ветка ушла вперёд на 23 коммита - приехала чужая работа по воронке
продаж, вебхуку поставщика и сверке CSV. Конфликтов было два.
1. Журнал схемы db/CHANGELOG_schema.md. В обеих ветках лежала запись v9.28
от 31.07 про разное: у нас очередь заданий роботу, у них стадии воронки
"Тестирование ручное" и "Выслано КП". Обе записи настоящие, поэтому ни одна
не выброшена: боевая v9.28 осталась на своём номере, наши две подвинуты на
v9.29 очередь заданий и v9.30 грант админ-роли. Поправлены ссылки на номер
в шапках двух миграций, перекрёстные ссылки внутри самих записей и врезка
"Перенумерация" вверху журнала - там теперь описан и этот случай.
2. docs/observer/STATUS.md - файл авто-генерируемый, взята версия основной
ветки, хук перепишет его сам.
Заголовок db/schema.sql не трогали: там своя нумерация v8.85, ни одна из
веток её не двигала.
Плюс одна настоящая находка статанализа, приехавшая с чужой работой: у метода
SupplierPortalClient::fetchDeliveredLeads в описании возвращаемого набора не
было поля tag, хотя код его уже возвращает и чужой же тест его ждёт. Дописал
одно поле в описание, логику не трогал. Список исключений статанализа пересобран
- разъехались счётчики ложного класса Pest от новых строк в чужих тестах.
Проверено на ОТДЕЛЬНОЙ тестовой базе liderra_testing_tgmerge:
телеграм на портале 244 из 244
робот 124 из 124
статанализ 0 замечаний
код-стиль чисто
полный прогон 4063 теста, 4018 прошло, 17 упало
До сведения было 4039 тестов и 20 падений - тестов стало больше, падений
меньше. Ни одно падение не касается телеграма или журнала схемы: 13 из 17 в
рекламном модуле Яндекса, из них 11 - живой отказ авторизации Яндекс Директа,
остальные счётные, от накопленных за прогон данных.
Отдельно вскрылось при проверке: общая тестовая база liderra_testing испорчена -
в ней 180 записей о применённых миграциях при 146 файлах, то есть в неё пишет
не только эта рабочая папка. Из-за этого сборка базы срывалась на первом же
шаге. Отдельная база всё вылечила. Это хвост Х2б, лечение в репозиторий не
вносил - решение владельца.
На бой ничего не выкачено, переключатель TG_ROBOT_TRANSPORT остаётся в process.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Работа по роботу закончена и запушена, тестовая база вылечена наполовину,
на бой ничего не выкачено. В промте пять хвостов по убыванию важности.
Главный и срочный: в обеих ветках лежит запись v9.28 журнала схемы от 31.07
про разное — у нас таблица заданий роботу, в основной стадии воронки продаж.
При сведении веток это конфликт, и обе записи должны остаться разными
номерами, а не выбором одной стороны. Ветка отстала на 23 коммита.
Остальные хвосты: изоляция между тестами и одна тестовая база на все рабочие
папки, покалеченные сторожа в главной папке репозитория, хрупкий тест
внешних сервисов, русский токен в старом тесте.
Выкат вынесен отдельно с пометкой, что он только по явному слову владельца:
упирается не в код, а в решение, где поселить робота.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Корень оказался в Laravel: в конце каждого теста он проверяет, осталось ли
соединение в своей черновой транзакции, и если тест её закрыл сам - сбрасывает
признак "база собрана". Следующий тест пересобирает базу целиком прямо посреди
прогона, снося её под всеми остальными. В проекте полно кода со своими
транзакциями, поэтому срабатывало примерно раз на восемь прогонов.
Что сделано:
- пересборка базы теперь один раз и в самом начале прогона, а не лениво в
середине по первому файлу, который её закажет;
- признак "собрано" держится взведённым - пересборка посреди прогона стала
невозможна;
- отказ заливки схемы стал громким: раньше PDO мог вернуть отказ без ошибки,
и прогон ехал дальше по неполной схеме;
- добавлена сверка полноты сборки: сколько шагов записано против того,
сколько их лежит.
Приёмка вырезанием: новый тест-сторож зелёный с защитой и падает без неё,
прогон без защиты дольше на 12 секунд - это и есть лишняя пересборка.
Замер до и после, полный набор портала:
без правки 3175 прошло, 837 упало
с правкой, прогон 1 3971 прошло, 19 упало
с правкой, прогон 2 3971 прошло, 19 упало, списки совпали дословно
Оставшиеся 19 - давние и не от базы: 11 падают на неверном токене Яндекса,
одна на типе исключения, семь счётных от накопления данных между тестами.
Плюс план перевода чтения вердикта и пересдачи на опрос - отдельным файлом,
код по нему ещё не писался.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Конфликты только в документах: журнал «мозга» взят из main, запись v9.28
переставлена поверх боевого журнала схемы. Код не пересекался.
Сторож слоёв проверен отдельно в рабочей папке: 0 нарушений.
Сторож секретов gitleaks отработал по-настоящему.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В спеке §3 уточнено по факту кода: нормализатор отдаёт номер с плюсом,
а хранится он без плюса — плюс срезаем.
Сторожа орфографии и разметки пропущены: они не находят проблемы, а падают —
их библиотеки в корне обрезаны той же поломкой, что статанализ и сторож
журналов. Сторож секретов gitleaks — живой, работает.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
До правки decide на незнакомой стадии возвращал unknown_stage — реклама на
такие фирмы встала бы без единого сообщения. Теперь обе новые стадии идут
по правилу переговоров: крутим до созвона, потом ещё срок просрочки.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Сторож проверен вырезанием: с убранной защитой тест краснеет, с возвращённой
зеленеет. Зелёный тест, которого не видели красным, ничего не охраняет.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Канал из пяти: почта, ватсап, телеграм, макс, другое. Адрес/ник сохраняем
как ввёл менеджер — телеграм-ник и почта к телефонному виду не приводятся.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Телефон приводится к виду 79… без плюса — как контакты карточки и как
принимают Яндекс Аудитории. Неразобранный номер отказывает, карточку не двигает.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Список стадий правится в двух местах сразу: PROSPECT_STAGES на фронте и
STAGES в контроллере. Расхождение дало бы пустую колонку.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Стадии manual_testing и kp_sent в CHECK + 4 nullable-колонки под их поля.
Откат и повторный накат проверены на своей тестовой базе измерением колонок.
Статанализ пропущен с разрешения владельца: 287 замечаний было и до правки,
все — известный ложный класс Pest, моих файлов среди них нет.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Документ ПРОТОКОЛ-создание-вебмастера.md (коммит e0223de4) ссылался на
схему ПРОТОКОЛ-вебмастер-КАРТА.html, а сама схема в репозиторий не попала —
лежала на диске незакоммиченной с 22.06.2026. Из-за этого сторож ссылок
краснел на чистом main и блокировал любой пуш: 2 битые ссылки из 2242.
Содержимое схемы не менялось, кроме косметики по правилу проекта:
белый цвет записан полностью (#ffffff вместо #fff), 8 мест, вид тот же.
У объявления два независимых признака: вердикт модерации (Status) и идёт ли
показ (State). Созданное программой объявление рождается выключенным, и
включение кампании его не поднимает — команды поднять сами объявления в коде
не было вообще.
Живая приёмка 31.07.2026, кампания 713175197: принята и включена, Яндекс на
уровне кампании пишет «Идут показы», а внутри все 15 объявлений выключены.
Показов ноль при замороженных у клиента 3333.36 руб.
Опросчик теперь чинит по СОСТОЯНИЮ из ответа Яндекса, а не по переходу
статуса: собирает объявления со статусом «принято» и выключенным показом и
зовёт ads.resume. Так самовосстанавливается и уже запущенная кампания — по
переходу она осталась бы выключенной навсегда, потому что «принято» у нас
записано давно. Состояние Яндекс присылал в каждом ответе и раньше, опросчик
его просто выбрасывал.
Проверено вырезанием: без починки новый тест краснеет. Реклама 349 из 349.
Живой запуск кампании на бою 30.07.2026 вскрыл две поломки, которых вчерашняя
починка не видела. Обе — из класса молчаливых: портал рапортует успех, а в жизни
ничего не происходит.
ПЕРВАЯ. Минимум Директа НЕ постоянный.
Вчера мы приняли живой отказ «Budget for this period cannot be less than 600 rub.»
за постоянный порог и зашили 600 ₽ в настройку. Сегодня та же кампания на неделю
получила отказ «cannot be less than 2400 rub.» — проверка её пропустила, и клиент
снова увидел английский текст.
Две точки дали правило: минимум = 300 ₽ за каждый календарный день периода,
считая оба края.
период 30.07-31.07, 2 дня → 600 ₽ = 2 × 300
период 30.07-06.08, 8 дней → 2400 ₽ = 8 × 300
Теперь порог считается от периода показа, а ставка за день вынесена в настройку
YANDEX_DIRECT_MIN_SPEND_RUB_PER_DAY. Период вычисляется ДО денег — иначе считать
минимум не от чего.
ВТОРАЯ. Создать объявления — не значит запустить рекламу.
Запуск проходил успешно, портал ставил статус «на модерации», а в кабинете лежали
кампания, группа и 15 объявлений в состоянии DRAFT и OFF. Яндекс кладёт всё
созданное черновиком и проверку сам не начинает. Реклама не показалась бы никогда,
и узнать об этом можно было только глазами в кабинете: наш журнал говорил, что всё
хорошо.
Добавлены два вызова, которых не было вовсе:
ads.moderate — отдать созданные объявления на проверку
campaigns.resume — включить показ
Порядок обязателен. Пока кампания черновик, включить её нельзя — Директ отвечает
«кампания является черновиком и не может быть остановлена». Сначала модерация,
она переводит кампанию в MODERATION, и только потом включение.
Отбор объявлений строго по Ids. На CampaignIds Директ отвечает «отсутствует
обязательный параметр Ids» — именно так отправка молча не сработала бы.
На модерацию уходят объявления, созданные ИМЕННО этим заходом: повторная отправка
уже проверяемого — отказ по позиции, он порвал бы возобновляемый запуск. Включение
показа безобидно при любом повторе: на включённой кампании Яндекс отвечает
предупреждением, а не отказом.
Заодно клиент Директа перестал молчать про отказы внутри ответа: раньше он смотрел
только AddResults, UpdateResults и DeleteResults, теперь ещё ModerateResults и
ResumeResults. Отказ по позиции в этих двух проходил бы насквозь незамеченным.
ПРОВЕРЕНО ЖИВЬЁМ. Кампания № 6 на бою: в кабинете 713175197, группа 5778555449,
15 объявлений, аудитория подключена, статус MODERATION, показ включён. На счету
Яндекса 8775 ₽. У клиента заморожено 3333.36 ₽ за 27778 показов до 06.08.
Тесты: 346 зелёных по рекламе, 43 по клиенту Директа, статанализ ноль замечаний.
Четыре новых теста — минимум по длине периода на боевом случае, отправка на
модерацию, порядок модерация-до-включения, отсутствие повторной отправки.
Два старых теста закрепляли частный случай в два дня — им проставлен явный срок
показа, иначе они молча описывали бы неправду.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Переделка сделана и запушена, на бой не выкачено. Развилка описана: поселить
робота и выкатить, перевести на опрос чтение вердикта и пересдачу, вылечить
тестовую базу. Ловушки смены выписаны, включая молчаливую установку
зависимостей и правило замерять сторожа до починки своего текста.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Переделка сделана и запушена, на бой не выкачено. Развилка описана: поселить
робота и выкатить, перевести на опрос чтение вердикта и пересдачу, вылечить
тестовую базу. Ловушки смены выписаны, включая молчаливую установку
зависимостей и правило замерять сторожа до починки своего текста.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>