Кампания в состоянии «Готова к запуску» не двигалась ничем — ни человеком,
ни расписанием. Клиент собирал рекламу, заливал пятнадцать картинок,
отправлял заявку и упирался в тупик: на карточке только «Изменить» и
«Отчёт». Запуск был возможен лишь обращением, к которому в кабинете нет
кнопки. Поймано приёмкой 03.08.2026 на кампании #11.
Новых дыр кнопка не открывает: путь запуска и так лежал в клиентской группе,
все проверки — деньги, минимум площадки, годность аудитории — внутри самого
запуска.
Три ответа портала показываются по-разному: «готовим картинки» и «аудитория
ещё готовится» — спокойным синим сообщением, это нормальный ход, деньги не
тронуты и в кабинете ничего не создано; смета мала, не хватает денег, Яндекс
молчит — красным. Денежная плашка перечитывается только после удачного
запуска.
Отдельного «вы уверены?» намеренно нет: кнопка подписана, смета показов
стоит тут же на карточке, пауза возвращает заморозку целиком — проверено
живьём 03.08 на кампании #6. Вместо окна под кнопкой строчка «При запуске
1 020,60 руб. будут зарезервированы на кошельке»: клиент знает про деньги
до нажатия, а не узнаёт потом из кошелька.
Сторожа: кнопка есть только у «Готовы к запуску» и нет у работающей и у
черновика; удачный запуск перечитывает список и денежную плашку; «аудитория
ещё готовится» даёт спокойное сообщение и не тревожит плашку; отказ даёт
красное; под кнопкой сказано про резерв сметы.
Прогон: 2049 сторожей фронта зелёные, типы Vue чисты, стиль чист.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Конец периода считался «сегодня + run_days», а Яндекс берёт StartDate и
EndDate включительно. Заказанный один день превращался в период из двух,
неделя — из восьми. Следом раздувался минимум площадки, 300 руб. за каждый
календарный день: клиенту, заказавшему день, портал требовал 833.40 руб.
вместо честных 416.76 руб. Поймано приёмкой 03.08.2026 на кампании #11.
Стало: конец периода — «сегодня + run_days минус 1».
Сторож держит связь, а не число: «ставит в Директе период ровно на столько
дней, сколько заказал клиент» — три длины сразу, 1 день, неделя, месяц;
проверяются StartDate и EndDate в самом запросе к Директу.
Красный проверен вырезанием наоборот: сообщение Laravel «ожидаемого запроса
не было» не говорит, из-за чего именно, поэтому в сторожа временно вписали
нынешнее неверное поведение — он позеленел, доказав, что расходится ровно
дата конца. После доказательства сторож вернули как надо.
Числа трёх прежних сторожей пересчитаны от 300 руб. за день, а не подогнаны
под выданное: 6945/833.40 -> 3473/416.76; 27778/3333.36 -> 24306/2916.72;
сегодня+14 -> сегодня+13. Комментарии, закреплявшие ошибку словами,
переписаны.
На кампанию #11 починка подействует — её конец периода ещё не записан.
На #6 не подействует и не должна: её shows_until записан 30.07 и
переиспользуется.
Прогон: 378 сторожей блока рекламы зелёные, статанализ 0 ошибок, стиль чист.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Промт писался до собственной записи и называл 117 записей впереди main,
а стало 118. Число на перемере сверяет следующая смена — расхождение
подняло бы ложную тревогу. Заодно названы обе записи смены 17.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
В отчёт приёмки добавлен §6в — перемер мелочей по коду. Три из семи
не подтвердились, и две из них оказались промашками приёмщика,
принятыми за поломку портала:
1. Журнал «Яндекс не дал включить показ» — НЕ мусор, а единственный
машинный след того, что кампания стоит: по нему портал говорит клиенту
«принято, но пока не показывают» вместо вранья «идут показы».
На этом стоит сторож. Убирать нельзя.
2. Красная подпись про размер баннера — по коду снимается сама.
Похоже, описан собственный промах вечера 03.08. Нужен замер глазами.
3. Черновики-пустышки — защита уже стоит, больше одной пустышки
одновременно не накопится. #12 появился законно.
Отсюда правило для чтения отчёта: крупные дефекты подтверждены замерами
и запросами, мелочи — только глазами. Прежде чем чинить мелочь,
проверь её по коду.
Там же отмечено починенное 04.08 (запись 1908f841).
Новый файл — промт смене 18: запуск кампании #11 (не сделан, сегмент
сведён, жать после 10:00 МСК), затем два крупных дефекта — отсутствие
у клиента кнопки «Запустить» и лишний день в периоде, удваивающий
минимум площадки. У обоих расписаны замер, чем плохо и чем чинить,
с ловушками про существующий сторож на числах 6945/833.40 и про уже
записанный shows_until у #6 и #11.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Кампания 11 заведена и ждёт одного действия: сегмент Аудиторий 58389879 создан 03.08
около 18:44 МСК, Яндексу нужно ~15 часов на сведение, значит после ~10:00 МСК 04.08
можно запускать. Запуск заморозит 1 020,60 ₽ и заведёт кампанию в Директе.
В промте:
- срочное первым: как именно жать «Запустить», раз кнопки у клиента нет, что должно
произойти и что замерить сразу после;
- перемер числа записей и грабля с часами машины;
- доступ к боевому, к виртуалке робота-грузчика и готовый запрос по кошельку, броням
и летописи;
- что сделано и переделывать не надо: четыре починки кошелька одной записью и отчёт;
обе починки живут ТОЛЬКО в ветке, на боевой не выкачены;
- состав кампании 11 целиком, включая где лежат картинки;
- стены: «документы» обойдена сменой текста (гипотеза проверится завтра),
«полный отказ» осталась — Я-М1 недостижим;
- порядок работ: запуск, Я-Д1, Я-М2, Я-Д3/Д4/Д6, Я-Ф3, замер 07.08 по кампании 6,
починка лишнего дня в периоде, комбинации Я1-Я9;
- семь новых дефектов вечера с оценкой важности;
- что на боевом трогать нельзя, включая обращение с личными номерами;
- правила, церемония записи и решения владельца.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Решение владельца: стену с документами не ломать, а обойти — завести кампанию на обычном
тексте Лидерры, без финансовой темы. Сделано вечером 03.08, всё глазами через кабинет.
Кампания 11: разовый запуск, свой список 1693 номера (распознано 1693, отброшено 0),
аудитория ~1701 человек, частота 5, ~8505 показов, смета 1 020,60 ₽ (потолок поднят
владельцем с 1000 ₽). Баннеры 15 из 15, обычный текст Лидерры, палитра Forest.
Сегмент Аудиторий 58389879 создан, идёт сведение около 15 часов.
Деньги НЕ тронуты: кошелёк 9 991 / 3 333,36, брони под 11 нет.
Порядок, которым это далось: первый «Запустить» ставит задание роботу-грузчику; робот
залил 15 картинок за минуту; второй «Запустить» создаёт сегмент и честно отвечает
«аудитория ещё готовится». Сведение начинается ТОЛЬКО со второго захода, не с отправки
заявки — кто этого не знает, теряет сутки.
Подтвердилось живьём: размерный сторож баннеров отвергает файл не того размера; робот-
грузчик жив и быстр; отказ по минимуму площадки объясняется по-русски и с числом;
неудачный запуск денег не трогает.
Найдено новое:
1. «Показывать 1 день» превращается в период ДВУХ дней — конец периода ставится
«сегодня + срок», а Яндекс считает оба края. Минимум площадки удваивается: 600 ₽
вместо 300 ₽. Клиент заказал день, платит минимум за два. Касается денег каждого.
2. У клиента НЕТ кнопки «Запустить» — кампания висит «Готова к запуску» и ждёт оператора.
3. Про минимум узнаёшь только на запуске — мастер даёт собрать кампанию и залить
15 картинок, и лишь потом отказывает.
4. Счётчик аудитории не пересчитывается после загрузки списка: показывал 15 человек
при загруженных 1693. Клиент решит, что список не принялся.
5. Красная подпись про неверный размер остаётся на карточке и после загрузки верного файла.
6. Каждый заход во вкладку «Новая реклама» плодит черновик.
7. Пятнадцать картинок точных размеров — стена для обычного клиента: портал ничего
не рисует сам.
Дальше: завтра после ~10:00 МСК третий заход «Запустить» — он заморозит смету и заведёт
кампанию в Директе.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Отчёт приёмки Яндекса дополнен до полного. Добавлено:
- Честный счёт по карточкам: пройдено полностью 8, наполовину 6, не тронуто 15,
комбинационных прогонов Я1-Я9 сделано 0 из 9. Ранбук пройден примерно на треть.
- Две стены, о которые разобьётся следующий прогон:
1) «Займы под залог» НЕ дают полного отказа — Яндекс отклонил 2 объявления из 15,
остальные 13 принял. Отклоняет не за тему, а за отсутствие предупреждения на
картинке. Для Я-М1 нужен отказ ВСЕХ, значит нынешний текст не годится.
2) Показов не будет, пока Яндекс не получит документы. Кампания 6 работает пять
суток при 13 принятых объявлениях и нуле показов. Без показов недостижимы
Я-Д3, Я-Д4, Я-Д6 и половина Я-Ф3.
- Мелочи: кампания 6 морозилась трижды, две записи в десяти секундах друг от друга —
похоже на двойной запуск, карточка Я-Л1 не проверена; мастер пускает дальше при
нулевой аудитории; в журнале боевого чужая ошибка прав на миграции СМС от 02.08;
числа для расчёта платного прогона; рекламный кошелёк на бою ровно один;
браузер приёмки рвёт связь и оставляет процесс.
Промт смене 16: как попасть на боевой, обе стены, что уже сделано и не надо
переделывать, порядок работ, числа для расчёта, правила и церемония записи.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Полноценный платный запуск за смену не пройти: Яндекс сводит загруженный список
телефонов с людьми около 15 часов. Пауза и возобновление живой кампании проверяют
денежный оборот, не потратив ни рубля — показов у неё нет, списывать нечего.
Замерено на боевом, кампания 6, кошелёк тенанта 2:
- до: заморожено 3333.36, свободно 6657.64, кампания running
- пауза: заморожено 0.00, свободно 9991.00, бронь released, кампания paused
- возобновление: заморожено 3333.36, свободно 6657.64, бронь active, running
Закрыто: Я-Д5 (заморозка снимается целиком и до копейки, списаний нет),
Я-Л5 по деньгам (после возобновления бронь ОДНА, не две), Я-Б4.
Найдено: денежная плашка на экране врёт в обе стороны. После паузы показывает
старые 3333.36 замороженных, после возобновления — что свободно 9991. Значок
кампании обновляется правильно, не обновляется именно кошелёк; правда появляется
только после перезагрузки. Последствие: клиент считает новую кампанию на
несуществующие деньги и получает неожиданный отказ.
Ещё: снятие 3333.36 не оставило в летописи ни одной строки. Вместе с заморозкой,
которая пишется нулём, выходит, что летопись не фиксирует ни постановку, ни
снятие заморозки.
Боевое возвращено в исходное состояние, сверено запросом: все три числа и статус
совпали с замером до начала.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Приёмка блока Яндекса 03.08.2026 вскрыла неверный порядок в запуске кампании.
Заморозка стояла последней — уже после создания в Директе кампании, группы,
объявлений, отправки на модерацию и ВКЛЮЧЕНИЯ показа. Не хватило денег: клиент
получал отказ, кампания в портале оставалась черновиком, а в Яндексе крутилась
включённая реклама, за которую платила Лидерра.
Правильный порядок в проекте был известен — у кнопки «Возобновить» деньги
резервируются до обращения к Директу, и там об этом написано прямо. В запуске
он просто не был применён.
Сначала сторож, потом починка. CampaignLauncherTest → «не хватает денег — в
Директе ничего не создано»: кошелёк 100 руб, смета 12000 руб. На прежнем коде
упал с «записано неожиданное обращение». Держит именно ПОРЯДОК: пока деньги не
заморожены, Директ можно только читать — ни одного вызова с методом кроме get.
Проверка «деньги не списались» тут бесполезна, она зелёная и при поломке.
Новый порядок в CampaignLauncher::run:
1. годность аудитории — может ответить «зайдите позже», деньги ещё не тронуты;
2. сверка настоящих размеров креативов — это чтение, метод get;
3. заморозка денег;
4. и только теперь все записи в Директ.
Заодно закрылась вторая беда: сверка размеров стояла после создания кампании и
группы, и несовпадение размера оставляло в кабинете кампанию-сироту.
Сторож принят вырезанием: старый порядок — красный, починка — зелёный.
362 теста блока рекламы зелёные, статанализ 0 ошибок.
Отчёт приёмки с остальными находками — в docs/superpowers/runbooks/.
Боевого не касался: кошелёк и кампании остались как были.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Разложены на карточки все три рекламных блока — Яндекс, Телеграм, СМС. Метод тот же,
что дал результат на приёмке боевого 22.06: карточка уровня C (самодостаточный шаг),
комбинации all-pairs, матрица покрытия, фиксация «ожидали / получили».
Карточки строились НЕ по памяти: сперва разобран код каждого модуля, поэтому проверки
про настоящие деньги и настоящие статусы.
Яндекс (27 карточек): смета=заморозка копейка в копейку, списание по факту показов,
возврат остатка, наценка не протекает клиенту; отказ модерации возвращает всё, одобрение
не списывает вперёд, частичный приём не роняет кампанию; блокировка при нехватке денег
останавливает ВСЕ каналы; нарушения логики и обход портала запросом мимо экрана.
Телеграм: три звена вместо двух — портал, робот, кабинет МТС. Заморозка на запуске,
списание по факту кассы, возврат при отказе и при сбое робота; потолок траты; нечитаемая
сумма = отказ платить; сторож сессии; «создать» не равно «запустить».
СМС: поштучное списание за каждый отправленный номер, цена по накоплению за месяц;
ночная тишина по часу ПОЛУЧАТЕЛЯ и граница суток; три источника номеров порознь (03.08
два из трёх на бою не отправляли вообще); стоп-лист, отписка, имя отправителя, ПДн.
Комбинации: по девять прогонов на блок (L9) вместо 81 — каждая пара значений любых двух
признаков встречается ровно раз. Недостижимые сочетания помечены честно: пишутся SKIP с
причиной, а не выдаются за проверенные.
Как добывать исход модерации (решение владельца): отказ — текст про «займы под залог»
(отклоняют наверняка), одобрение — обычный текст Лидерры.
Промт смене 15: измеренное состояние, порядок блоков (Яндекс первым), что уже сломано
или не проверено ни разу, чего на бою нельзя касаться, пути отката, церемония записи.
Пароли и учётки в файлы не писать — только в переписке.
Промт смене 3 отменяет часть смены 2: там мозг Claude и одна Лена,
здесь GPT-4.1 и две — обзвонщица на 8090, приёмщица на 8091.
Мост most.py до сих пор жил в одном экземпляре на сервере, а сервер
03.08 дважды МОЛЧА откатил его к вчерашней версии. Второй откат
обесценил целое сравнение мозгов: три «разных мозга» оказались одним
Claude. Причина не найдена, поэтому рядом лёг сторож — сверяет слепок
раз в 10 секунд и записывает, кто был рядом в момент подмены.
Датчик тишины меряет мёртвый воздух по записи, а не по журналу: в
журнале пауза считается от решения поставщика «человек договорил»,
и время на само это решение не видно вообще.
Ключа Inworld ни в одном файле нет — мост читает его из ~/lena-inworld/.kluch.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Задач владелец ещё не назначил, поэтому промт — про измеренное состояние и
четыре открытых вопроса, каждый из которых требует его решения: отставший
боевой «Поиск клиентов», непроверенное живьём чтение вердикта модерации,
засветившийся токен робота, копящиеся по две сборки экранов.
Записаны уроки смены: состав выката считать командой, а не по списку из
промта; причину не объявлять до замера; приёмка на живых данных вскрывает то,
чего нет в тестовых; сторожа принимать вырезанием прямо на бою через подмену
настройки только в памяти процесса.
Разметка чистая, 0 замечаний.
Кошелёк и заморозка телеграм-рекламы выкачены на боевой из ветки
fix/tg-zagolovok-obyavleniya. Пока работа жила только там, рабочая ветка
расходилась с боевым на 29 файлов, и следующая сверка приняла бы это за
чужое затирание. Теперь в рабочей ветке есть всё, что стоит на бою.
Сведение без склеек. Проверки на объединённом дереве: статанализ 0,
телеграм плюс СМС 732 из 732, экраны 251 файл и 2034 теста, робот 160 из 160.
🪤 Статанализ сперва упал одной ошибкой на новой колонке mts_cost_rub, и
причина была не в коде: файл подсказок типов _ide_helper_models.php лежит вне
git, у каждой рабочей папки свой, и в этой он про колонку не знал, потому что
местная база разработки отставала на три миграции. Лечение: догнать местную
базу и пересобрать подсказки ключом -M -n. Собственный сторож проекта поймал,
что я пересобрал их неверным ключом, — без него анализатор упал бы молча.
Владелец решил отдать выкат другой смене, чтобы две смены снова не затёрли работу
друг друга на боевом.
Главное в промте — ПОРЯДОК, и он не косметический. Портал новый при старом роботе
даёт худший исход: запуск заморозит деньги клиента, робот по-старому не заплатит,
кампания станет «черновик готов», и заморозка повиснет навсегда — этот статус
ничего не отпускает. Обратный порядок терпим: реклама поедет, клиент переплатит
как раньше. Поэтому сперва два файла робота на машине владельца, потом портал.
Записано то, что нельзя брать по памяти: потолок траты робота BUDGET_CAP_RUB=5000
при балансе кабинета около 5010 ₽ — одна кампания способна съесть почти весь
баланс, менять или нет решает владелец.
Отдельным разделом — три вопроса к боевой базе ДО выката, все три про деньги живых
клиентов: есть ли уже пострадавшие (заплатили по старой модели и получили
«черновик готов»), хватит ли клиентам денег в НОВОМ месте (деньги телеграма
переехали на рекламный кошелёк, у кого они на общем балансе — запуск откажет), и
нет ли кампаний в полёте, которым правила поменяются посреди прогона. Решать за
владельца там нечего — только принести ответы.
Ловушки собраны из записки соседей и из этой ночи: сверка состава экранов до и
после, побайтовая сверка боя с веткой, db/ сверять со снятием \r, миграции только
под crm_migrator с паролем из Lockbox по ID, config:cache от www-data, права кэша
после распаковки от root, pkill -f по ssh убивает свою же команду.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Первая боевая СМС через портал дошла: рассылка 3, сообщение доставлено, списано
ровно 9 рублей с кошелька клиента. Та же проба вскрыла дыру: оператор номера не
проставляется нигде, кроме лидов от поставщика, поэтому рассылки по своей базе
и списком руками на бою не отправят ни одного сообщения.
В промте: доказательства дыры с точными местами в коде, объяснение почему её не
видят тесты (они проставляют оператора руками), чем чинить — справочник Россвязи
на 453 080 записей и сервис, к которому модуль ни разу не обращается.
Отдельно записано:
- след пробы в боевой базе и обход, который надо снять после починки;
- ловушка чтения боевой базы через tinker: защита строк вернула ноль клиентов
там, где живут десять;
- часы машины и владельца красноярские, ночью я принял их за московские;
- порядок выката и сверки после трёх взаимных затираний за сутки.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Владелец нащупал вопросом, замер подтвердил: клиент платит за РАЗМЕР СПИСКА, а МТС
берёт с нас за ФАКТИЧЕСКИЕ показы, и сверки между этим нет вообще.
Замерено, а не предположено:
- списание считается от числа кандидатов (CampaignController.php:212-217);
- счёт кампании 2231134 выставлен за 420 показов, а не за размер списка;
- в RunTelegramCampaignJob.php:152 прямая запись, что живое списание по факту
отложено «на Сессию 6, когда известен источник фактической цены».
Итог: у кого нет телеграма — реклама не покажется, а деньги списаны. Разница
остаётся у нас, это переплата клиента.
Главный вывод промта: одной заморозки МАЛО. Заморозить можно, но чтобы
разморозить лишнее, надо знать факт, а портал числа показов не хранит — это ровно
тот пункт, который владелец 02.08 велел отложить как косметику. Сделать заморозку
раньше факта значит разморозить ту же неправильную сумму. Отсюда порядок из трёх
шагов: сперва научиться узнавать факт, потом кошелёк с заморозкой, потом показать
клиенту строкой.
Записано, чтобы не переоткрывали: кошелёк убрали НАМЕРЕННО 29.07 (коммит
6f469fb13, таблицы ad_wallets целы), возврат по сальдо проводок сделан правильно и
его надо сохранить, Яндекс живёт на кошельке с заморозкой.
Состояние на бою и в ветке — замерами: вершина 0b7453afc содержит обе работы,
статанализ 0, экраны 251/2033, телеграм+СМС 714/714. На бою разъехались половинки:
сборка из объединённого дерева, серверный код нет, расходятся ровно 5 файлов, и
CampaignController.php ломает показ цен. Выкат владелец решил отдать соседней смене.
Ловушки собраны из этой ночи: сверять состав экранов ДО и ПОСЛЕ выкладки (при
первом выкате проверка была, при втором нет — с боя исчезли экраны СМС, и снаружи
всё выглядело здоровым), миграции только под crm_migrator с паролем из Lockbox по
ID, старые пароли в liderra-secrets.txt от прежней базы, сведение приносит новые
зависимости, цену в текст экрана не вбивать.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Самодостаточный промт: смена сможет работать, ничего не помня из этой сессии.
Порядок чтения, где стоим по замерам, пять вопросов владельцу с блокирующим
первым, шесть работ без владельца, чего не делать, восемь оплаченных грабель,
стенд с командами, состояние репозитория, порядок работ.
Главное, что отменяет прежний промт: ручки «предел длины ответа» у поставщика
НЕТ — она принимается и не применяется. Длину держит только текст наставления.
Живого звонка на телефон не было: это деньги и живой человек, ждёт слова
владельца.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
За ночь 03.08 две смены дважды затёрли работу друг друга на боевом. Второй раз
поломка была невидимой: портал отвечал 200, журнал чист, код и таблицы целы,
а экраны СМС просто исчезли из сборки.
Записка объясняет соседней смене корень беды и порядок работы:
- что уже сделано за них: их запись сведена, боевой починен, обе работы живы;
- почему затирание происходит: выкладка несёт весь интерфейс одним куском,
выложить «только свои экраны» физически нельзя;
- что ветка на сервере gitea не источник правды — серверная копия рабочей ветки
отстаёт на 206 записей, а репозиторий у смен общий, забирать надо локально;
- готовая команда побайтовой сверки боя с веткой и три ловушки к ней;
- что на боевом трогать нельзя: ограничение операторов, боевой .env, роль
миграций и права каталога кэша после распаковки.
Отдельно отмечено изменение в их правке: мы оба завели одно имя значка, дубль
разобран в пользу более точного, спрошено согласие.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
На сервере рендера построен мост между телефонией и Inworld: человек говорит
в трубку, Asterisk отдаёт звук мосту, мост разговаривает с Леной и возвращает
её речь обратно. Служба поднимается сама, в том числе после перезагрузки.
Дашина Лена НЕ ТРОНУТА — её вход цел, обе линии Манго зарегистрированы,
существующее не переписывалось, только дописано рядом.
Замерено:
- задержка ответа с самого сервера 0,95–1,26 с (с домашней машины было 1,3–1,5);
- длина реплики срезана вдвое: 23,3 с → 11,8 с;
- звук идёт A-law 8000 Гц в обе стороны — совпадает с Манго, не пережимается.
Два вывода, отменяющих вчерашние:
- ручки «предел длины ответа» у поставщика НЕТ. Она принимается и не
применяется: при потолке в 16 токенов ответ вышел 1144 знака целым текстом.
Вчерашний промт называл её «главным делом смены» — исправлено;
- «сильное правило лучше слабого» не подтвердилось: 232 против 236 знаков
при перехлёсте разбросов трёх прогонов.
Мина в базе знаний («откуда мой номер») сработала в каждом прогоне с этим
вопросом, включая живой голосовой разговор. Чинить надо в самой базе —
решение владельца, чужой текст не переписывал.
Живого звонка на телефон не было: это деньги и живой человек, ждёт слова
владельца. Команда готова, голос пока не выбран.
Ключей в файлах нет — проверено датчиком с контролем.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
За два часа ночи 03.08 две смены дважды затёрли работу друг друга на боевом.
Второй раз — невидимо: портал отвечал 200, журнал был чист, код и таблицы целы,
а экраны СМС просто исчезли из сборки, потому что соседняя выкладка несёт весь
фронт одним куском.
В промт добавлен раздел с готовой командой побайтовой сверки боя с веткой через
git hash-object, оговоркой про переводы строк в db и правилом отличать мёртвые
хвосты прошлых выкатов от чужой работы мимо git.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Дописано в handoff по Inworld:
- §17 два мозга: пересадка посреди разговора 0,2 с, у каждого мозга свой кэш,
он переживает возврат; экономия ~1 ₽ за звонок, смысл только в скорости.
- §18 филлер: разобран целиком (порог, язык, дыры, обрывки) и ОТКЛОНЁН владельцем.
Дыр между филлером и ответом нет — худшая 0,02 с. Обрывки речи ловятся
регуляркой строчная+заглавная, датчик пригодится и без филлера.
- §19 раздумья: вопрос закрыт текстом отказа поставщика — мозг Anthropic
думает только при температуре 1, у нас 0.45. Раздумья выключены физически.
- §20 ручка «когда начинать говорить»: не задавалась ни разу за все прогоны,
трогать только после укорачивания реплик.
- §21 обрывы соединения: три раза за день, только длинные звуковые разговоры.
Открытый риск, до боевого не выходить.
Поправлен файл ручек: убрана неправда про память как лекарство от предела
в байтах (замер показал обратное), раздел про раздумья и филлер переписан.
Новый файл — промт следующей смене: с чего начать, что доказано замером,
пять вопросов владельцу, чего не делать и шесть грабель этой смены.
Ключей в файлах нет — проверено датчиком с контролем.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Выкат состоялся в ночь на 03.08 с разрешения владельца: код, 36 миграций,
правила доступа, сборка интерфейса, перезапуск служб. Порталы отвечают 200,
журнал чист, службы активны.
ПИЛОТ.md — новая запись «03.08.2026 (ночь)»: что применено, чем проверено,
куда откатываться. Отдельно записаны две мины, которые стоили времени и
повторятся при следующем выкате:
- штатная роль crm_app_user не имеет права создавать таблицы, миграции идут
только под crm_migrator, и пароль в Lockbox берётся по номеру секрета,
а не по имени — с именем команда молча отдаёт пустоту;
- распаковка посылки от root сменила владельца каталога кэша, из-за чего
сброс кэша настроек молча не сработал и переопределение роли не действовало
вовсе. Датчик — спросить у базы, кто подключился, тем же путём, каким
пойдут миграции.
Главное, что выкат едва не сломал: на бою работал код второй смены, которого
не было ни в общей ветке, ни в нашей. Экраны собираются одним куском, поэтому
выкладка откатила бы их рекламу назад. Сведено заранее, после выката слепки
их файлов на бою те же, что были до него.
Ограничение операторов на бою поставлено по решению владельца: рассылки берут
только абонентов МТС, потому что имя отправителя подключено лишь на одного
оператора. Проверено вырезанием.
Промт переписан под смену 12: осталась одна работа — живой платный прогон
через сам портал после 10 утра, номер даёт владелец в момент прогона.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замер боевого перед выкатом СМС-модуля показал мину: на liderra.ru сейчас
работает код ветки fix/tg-zagolovok-obyavleniya — 33 записи телеграм-рекламы,
которых не было ни в main, ни в рабочей ветке. Сверено слепками файлов:
config/client_tg.php и TelegramTariffService.php на бою совпадают с их версией
и расходятся с нашей. Экраны портала собираются одним куском, поэтому выкат
СМС в прежнем виде откатил бы их живую рекламу назад. Решение владельца —
сперва забрать их работу к себе.
Разрешено пять склеек, все — сохранением обеих сторон:
- Tariff.php: их пояснение про закупочную цену плюс наша строка для подсказчика
типов;
- phpstan-baseline.neon: взята наша вычищенная версия. Их 260 строк заметания
не возвращены — статанализ после сведения дал 0 без них, потому что чинили мы
не baseline, а сам механизм, и он вылечил их новые тесты тоже;
- advertising-telegram-view.spec.ts: наш типизированный мок оставлен;
- CHANGELOG_schema.md: столкновения номеров НЕ было — у нас v9.33/v9.34, у них
v9.64/v9.65. Обе пары сохранены, ничего не двигали; в файл дописано, что дыра
v9.35–v9.63 это след их завышенного замера, а не потерянные записи;
- STATUS.md: служебный файл наблюдателя, взят свежий.
Что сведение вскрыло дополнительно:
- их сторож значков поймал НАШИ три имени с экранов СМС — mdi-file-sign,
mdi-account-badge-outline, mdi-file-download-outline в карте Lucide
отсутствовали и на бою рисовались бы вопросом в кружке. Добавлены по смыслу;
- проверка типов фронта: 1 ошибка стала 0. Образец имени отправителя в тесте
админки не задавал два поля, и Partial подмешивал в них undefined.
Проверено после сведения: PHP 4614 тестов, 4610 зелёных, 4 пропущено,
0 падений; экраны 250 файлов, 2018 зелёных, 3 пропущено, 0 падений;
статанализ 0 через composer stan; проверка типов 0.
Унаследованное, не мной внесённое и не тронутое: линтер фронта показывает
1 замечание на неразрывный пробел в advertising-sms-view.spec.ts — знак там
нужен по смыслу проверки, было до сведения.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ДЕНЕЖНЫЙ ПРОГОН СДЕЛАН — последний незакрытый блокер Х-2 снят.
Прогон вне тестов, на стенде, с настоящим движением денег. Пробный режим
выключен переменной окружения, файл настроек стенда не трогали вовсе.
Оператор — заглушка, отвечающая живой формой ответа МТС; вызовы шли по сети
тем же кодом канала, что и на бою. Операторская половина доказана отдельно
в тот же день на настоящем МТС: сообщение доставлено, списано 5,03 рубля.
Что доказано деньгами:
- списание 1000,00 на 984,91 — ровно три раза по 5,03, отдельной записью
на каждое сообщение с ключом;
- заморозка сметы снята, незакрытых не осталось;
- за неотправленное деньги вернулись: 984,91 на 989,94, отметка возврата
стоит только у неотправленного;
- двойного возврата нет: опрос прогнан ещё дважды, баланс и число возвратов
не изменились. Это мина 3 плана, самая денежная;
- досыл создаётся отдельной рассылкой с привязкой к родителю, двойной клик
отбит: второй оплаченной рассылки не завёл, деньги не тронул;
- джоб, вызванный напрямую в час ночи, отказался отправлять вне окна 10-20.
Честно не доказано: досыл до отправки не дошёл, ждёт разрешённого окна,
а окно сверяется по времени базы, не по подкрученным часам процесса.
Подделывать состояние в базе не стал — это голый шов. Закроется первым
дневным прогоном либо на бою.
Итоговая таблица Этапа 5 обновлена: строка 5.6 закрыта полностью, 5.7
закрыта по расчёту с оговоркой, что операторскую сторону надо брать из
баланса кабинета, а не из поля цены — оно у МТС всегда ноль.
Промт переписан: задача следующей смены — выкат. Заголовок, состояние
веток и обстановка приведены к правде после сведения; раздел про разъезд
кода и бумаг снят, он закрыт; добавлен рецепт повторения денежного прогона.
Записан выбор поставщика для обеих Лен и всё, чем он подтверждён.
Замерено живьём, не по рекламе:
- разговор одной линией, 7 ходов: 0,88 с до первого звука, ровно от начала
до конца. Втрое быстрее нынешней Александры и без просадки.
- кэш работает, 96-98 процентов попаданий, счётчик поставщик отдаёт.
Значит длина наставления по деньгам почти ничего не стоит — решение
владельца не резать базу оправдалось.
- звонок стоит около 6,4 рубля, из них 5,6 голос. Экономить надо на
длине реплик, а не на длине наставления.
- стена: лимит 20 000 байт считает весь разговор вместе с историей,
а не только наставление. На бесплатном тарифе разговор умирает
на пятом ходу. Оплата — условие любой осмысленной проверки.
Все ручки поставщика разобраны отдельным файлом, 15 проверены живым
опросом канала. Три опасных умолчания: предела длины ответа нет вовсе,
голос по умолчанию младшей модели, температура единица.
Мина на будущее: замер кэша сделан на чужом мозге, который кэширует сам.
Claude так не умеет, ему нужна отдельная ручка, а она по умолчанию
выключена. Проверить первым делом после оплаты.
Выгрузка из Даши — оба промта Лен и три документа базы знаний, дословно.
В kb-02 помечены две мины: ответ про происхождение номера противоречит
обоим промтам, и бесплатный тест описан двумя разными способами.
Ключей и персональных данных в файлах нет, проверено датчиком
с контрольной пробой.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: голосовой канал — выбран Inworld, замерены цена, скорость и все ручки
Записан выбор поставщика для обеих Лен и всё, чем он подтверждён.
Замерено живьём, не по рекламе:
- разговор одной линией, 7 ходов: 0,88 с до первого звука, ровно от начала
до конца. Втрое быстрее нынешней Александры и без просадки.
- кэш работает, 96-98 процентов попаданий, счётчик поставщик отдаёт.
Значит длина наставления по деньгам почти ничего не стоит — решение
владельца не резать базу оправдалось.
- звонок стоит около 6,4 рубля, из них 5,6 голос. Экономить надо на
длине реплик, а не на длине наставления.
- стена: лимит 20 000 байт считает весь разговор вместе с историей,
а не только наставление. На бесплатном тарифе разговор умирает
на пятом ходу. Оплата — условие любой осмысленной проверки.
Все ручки поставщика разобраны отдельным файлом, 15 проверены живым
опросом канала. Три опасных умолчания: предела длины ответа нет вовсе,
голос по умолчанию младшей модели, температура единица.
Мина на будущее: замер кэша сделан на чужом мозге, который кэширует сам.
Claude так не умеет, ему нужна отдельная ручка, а она по умолчанию
выключена. Проверить первым делом после оплаты.
Выгрузка из Даши — оба промта Лен и три документа базы знаний, дословно.
В kb-02 помечены две мины: ответ про происхождение номера противоречит
обоим промтам, и бесплатный тест описан двумя разными способами.
Ключей и персональных данных в файлах нет, проверено датчиком
с контрольной пробой.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@
Прежняя редакция устарела за ночь: писалась до пуша, до плана выката и до находки
с телефонами. Переписан целиком, чтобы не было двух конкурирующих промтов.
Что изменилось по существу:
- Смена начинается с развилки, на которую владелец ещё не ответил: идти на боевой
или сперва разобрать телефоны на ветке СМС. Рекомендуемый ответ дан — сперва
выкат, он готов и снимает уже работающую беду, а ветка СМС не выкачена.
- Появилась дорога Б: 4 незамаскированных телефона в приёмочном листе СМС,
коммит 868a681cf. Замерено, что он только на ветке prospects, на сервер не
уезжал, в телеграм-ветке его нет. Правка истории необратима — записано делать
только с разрешения владельца и предложив ему выбор, а не решать за него.
- Список файлов выката уточнён с 35 до 34: phpstan-baseline.neon на боевой не
едет, он только для проверки кода у нас.
- Добавлены замеры сборки и миграций туда-обратно, которых в прежней редакции не
было вовсе.
- Ловушка про обход сторожей дополнена главным: не обходить вслепую. Сперва
gitleaks по своим коммитам через --log-opts, и только увидев ноль — исключать.
- Записано, что ветка живёт в отдельной рабочей папке и git checkout из корня на
неё не переключится. Прошлая смена начала именно с этого вопроса.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Прежняя редакция устарела за ночь: писалась до пуша, до плана выката и до находки
с телефонами. Переписан целиком, чтобы не было двух конкурирующих промтов.
Что изменилось по существу:
- Смена начинается с развилки, на которую владелец ещё не ответил: идти на боевой
или сперва разобрать телефоны на ветке СМС. Рекомендуемый ответ дан — сперва
выкат, он готов и снимает уже работающую беду, а ветка СМС не выкачена.
- Появилась дорога Б: 4 незамаскированных телефона в приёмочном листе СМС,
коммит 868a681cf. Замерено, что он только на ветке prospects, на сервер не
уезжал, в телеграм-ветке его нет. Правка истории необратима — записано делать
только с разрешения владельца и предложив ему выбор, а не решать за него.
- Список файлов выката уточнён с 35 до 34: phpstan-baseline.neon на боевой не
едет, он только для проверки кода у нас.
- Добавлены замеры сборки и миграций туда-обратно, которых в прежней редакции не
было вовсе.
- Ловушка про обход сторожей дополнена главным: не обходить вслепую. Сперва
gitleaks по своим коммитам через --log-opts, и только увидев ноль — исключать.
- Записано, что ветка живёт в отдельной рабочей папке и git checkout из корня на
неё не переключится. Прошлая смена начала именно с этого вопроса.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Боевой не тронут. Это подготовка.
Что проверено прогоном, а не рассуждением:
- сборка экранов npm run build проходит за 4,39 с;
- все миграции ложатся С НУЛЯ через migrate:fresh, включая обе новые;
- обратный ход обеих миграций работает: rollback --step=2 откатил, migrate
накатил снова, тариф после круга правильный — 0.480 / 0.720 / 0.816;
- телеграм-модуль 325 тестов, экраны 244 файла и 1855 тестов, статанализ 0.
В плане: поимённый список 34 файлов приложения плюс собранные экраны, порядок
шагов, откат по каждому пункту и приёмка глазами шестью пунктами.
Отдельным шагом ДО заливки — проверить право USAGE на счётчике
client_tg_contacts_id_seq. В эту таблицу до сегодня не вставляла ни одна строка
кода, значит право боем ни разу не проверялось.
Вердикт GO по коду при двух условиях: явное «да» владельца и выполненный шаг с
правом на счётчик. Записано и то, чего выкат не закрывает: числа 0.720 и 0.816 —
вывод по правилу «кабинет пишет без НДС», а не замер.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Боевой не тронут. Это подготовка.
Что проверено прогоном, а не рассуждением:
- сборка экранов npm run build проходит за 4,39 с;
- все миграции ложатся С НУЛЯ через migrate:fresh, включая обе новые;
- обратный ход обеих миграций работает: rollback --step=2 откатил, migrate
накатил снова, тариф после круга правильный — 0.480 / 0.720 / 0.816;
- телеграм-модуль 325 тестов, экраны 244 файла и 1855 тестов, статанализ 0.
В плане: поимённый список 34 файлов приложения плюс собранные экраны, порядок
шагов, откат по каждому пункту и приёмка глазами шестью пунктами.
Отдельным шагом ДО заливки — проверить право USAGE на счётчике
client_tg_contacts_id_seq. В эту таблицу до сегодня не вставляла ни одна строка
кода, значит право боем ни разу не проверялось.
Вердикт GO по коду при двух условиях: явное «да» владельца и выполненный шаг с
правом на счётчик. Записано и то, чего выкат не закрывает: числа 0.720 и 0.816 —
вывод по правилу «кабинет пишет без НДС», а не замер.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Отмечено сделанным: ветка СМС в общей, общая сведена в рабочую. Записано,
как пришлось обходить занятость ветки чужой рабочей папкой и что откат
делается одной командой.
Предсказание прежней версии промта НЕ СБЫЛОСЬ и это сказано прямо: под
сторожем сети тесты СМС-модуля покраснеть были должны, но не покраснели.
Числа перемерены: 238 файлов экранов и 1906 зелёных, полный набор 4533
теста и 4529 зелёных, статанализ ноль.
В порядок выката добавлен новый первый шаг — установка зависимостей.
Модуль тянет phpoffice/phpword, на боевом её нет, и без установки он там
не заработает вовсе. Там же записано, что звать надо composer stan, а не
анализатор напрямую: перед командой проекта стоит сторож, который ловит
негодный справочник типов, и обойдя команду, обходишь и сторожа.
Про МТС: канал отправляет, цена сошлась до копейки, поле цены оператора
признано непригодным — сверять по балансу. Осталась одна незакрытая
строка приёмки: живое списание через портал, а оно требует выката.
Добавлен список из шести мест с миной «тест кладёт московское время
строкой без пояса». Сейчас они зелёные, чинить вслепую не стал.
Общая ветка переведена вперёд на ветку клиентских СМС — перемоткой, без
слияния, поэтому конфликтов там быть не могло. Затем общая сведена в рабочую
ветку, которая отставала на 79 записей.
Столкновение было одно и знакомое — словарь орфографии cspell-words.txt.
Разрешено правилом «обе стороны настоящие»: слова обеих веток сохранены,
ничего не выброшено. Журнал схемы БД на этот раз свёлся сам, столкновения
номеров не было.
СТОРОЖ ПДн ОСТАНОВИЛ ЗАПИСЬ И БЫЛ ПРАВ. В образце базы номеров, который
клиент скачивает перед своей первой рассылкой, стоял рабочий телефон
владельца — в двух видах. Это не утечка чужих данных: тот же номер публично
опубликован в реквизитах ИП по требованию ЮKassa. Но клиент заполняет этот
файл своими номерами и запускает рассылку — забытая строка означала бы СМС
владельцу за деньги клиента. Заменён на выдуманный из тестового диапазона.
Подсказки на экране рассылок («+7 999 123-45-67») выдуманы изначально;
разрешены в .gitleaks.toml ПО ЗНАЧЕНИЮ, а не по файлу, чтобы сами файлы
остались под охраной. Сторож проверен вырезанием: подложенный номер, не
подпадающий ни под одно разрешение, он поймал; после снятия подложки — чисто.
Служебный счётчик наблюдателя убран в тайник на время сведения и возвращён
после — он машинный и пересоздаётся хуками.
Файлы второй смены, которая работает в этой же папке, не тронуты и в
слияние не попали.
Проверки сведённой ветки: тесты 4533, зелёных 4529, падений нет; экраны
238 файлов и 1906 зелёных, сторож сети не сработал ни разу; статанализ 0;
формат чист.
Замечания: находки 1, 3, 4, 5 и «срок показа» помечены закрытыми с номерами
коммитов и с решением владельца по каждой. Находка 2 про пузырь чата осталась
открытой — её владельцу не задавали.
Промт следующей смене на выкат. Что в нём измерено заново, а не переписано:
- Список выката пересчитан командой от последнего известного выката ad7a31271:
получилось 35 файлов приложения и ДВЕ миграции, а прежний промт называл 24
файла и одну. Расхождение не разбиралось — записано прямо, с указанием
пересчитать список ещё раз перед самой заливкой.
- Отдельным пунктом — проверить право USAGE на счётчике client_tg_contacts_id_seq.
В эту таблицу до сегодня не вставляла ни одна строка кода, значит право ни разу
не проверялось боем. Замер в тестовой базе: счётчик без права, но так же
выглядит и счётчик кампаний, куда портал на бою пишет. Дано и как проверить, и
чем починить.
- Опорные точки дополнены новыми и отмечено, что tg-tariff-add и
tg-tariff-remove-* больше не существуют.
Записана ловушка этой смены: незакоммиченная чужая работа выглядит как её
отсутствие. Смена началась с вопроса «куда делась работа», а 17 файлов лежали в
отдельной рабочей папке .claude/worktrees/tg-zagolovok — из корня репозитория на
эту ветку не переключиться.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замечания: находки 1, 3, 4, 5 и «срок показа» помечены закрытыми с номерами
коммитов и с решением владельца по каждой. Находка 2 про пузырь чата осталась
открытой — её владельцу не задавали.
Промт следующей смене на выкат. Что в нём измерено заново, а не переписано:
- Список выката пересчитан командой от последнего известного выката ad7a31271:
получилось 35 файлов приложения и ДВЕ миграции, а прежний промт называл 24
файла и одну. Расхождение не разбиралось — записано прямо, с указанием
пересчитать список ещё раз перед самой заливкой.
- Отдельным пунктом — проверить право USAGE на счётчике client_tg_contacts_id_seq.
В эту таблицу до сегодня не вставляла ни одна строка кода, значит право ни разу
не проверялось боем. Замер в тестовой базе: счётчик без права, но так же
выглядит и счётчик кампаний, куда портал на бою пишет. Дано и как проверить, и
чем починить.
- Опорные точки дополнены новыми и отмечено, что tg-tariff-add и
tg-tariff-remove-* больше не существуют.
Записана ловушка этой смены: незакоммиченная чужая работа выглядит как её
отсутствие. Смена началась с вопроса «куда делась работа», а 17 файлов лежали в
отдельной рабочей папке .claude/worktrees/tg-zagolovok — из корня репозитория на
эту ветку не переключиться.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Сообщение 44850834 доставлено за 16 минут. Баланс кабинета ушёл
с 5 010,00 руб. на 5 004,97 руб. — списано ровно 5,03 руб., в точности та
цена, что вписана на боевой как SMS_MTS_PRICE_KOP=503.
В-211 закрыт, и ответ обратный ожидаемому: поле cost в ответе МТС
НЕПРИГОДНО. Оно ноль не только у неотправленных, но и у доставленного
сообщения, а признак типа трафика так и остаётся неопределённым. Считать
расход оператора по этому полю нельзя ни в каких единицах — сверка должна
смотреть на баланс, раздел «Расходы» кабинета либо на счёт.
Код при этом уже правильный и переделывать его не надо: provider_cost
хранится как есть и никуда не подставляется. Надо лишь знать, что это
всегда ноль, и не строить на нём тревогу.
Отдельно замечено: списание не мгновенное. Баланс уже уменьшился, а раздел
«Расходы — Сообщения» в тот же момент был ещё пуст.
Осталось одно: живой денежный прогон через портал, а он требует, чтобы
модуль стоял на боевом.
Х-5 закрыт, но с третьего захода. Причин было две.
Первая — не подключён тариф, найдена днём и верна. Вторая нашлась только
поздним вечером: имя отправителя liderra.ru подключено ТОЛЬКО НА ОДНОГО
оператора, а все пробы десять дней слались на номер владельца, который на
Tele2. МТС принимал сообщение, видел чужого оператора без согласованного
имени и молча клал в NotSent с пометкой «тип трафика не определён».
Прибор был негодным изначально, и это не заметили ни разу.
Доказано вырезанием: одно сообщение на живой номер МТС тем же боевым путём —
44850834, статус Sending, кабинет пишет «Отправлено». Больше ничего не меняли.
Три подряд неверных вывода об одной чужой системе записаны целиком в
приёмочный лист. Помогло единственное: повторить то же действие руками в
форме кабинета — она отказалась отправлять и написала причину словами,
тогда как API не сообщал её вовсе. Письмо в поддержку МТС было готово и
чуть не ушло третьей ошибкой подряд.
Правила, которые отсюда следуют, вписаны в промт следующей смены: отказ без
причины в API — повтори действие в интерфейсе чужой системы; проверяй канал
на том получателе, для которого он предназначен.
Осталось доспросить окончательный статус сообщения 44850834, чтобы увидеть
поле cost — единицы цены оператора. МТС хранит статусы трое суток, то есть
до 05.08.
В браузерную обвязку добавлена возможность держать ОДНО окно кабинета
открытым между шагами: _hold.mjs поднимает окно и живёт, _step.mjs цепляется
к нему и не закрывает. Кабинет — тяжёлый SPA, и открытие браузера на каждый
шаг стоило минуты.
Х-5 снова открыт. Запись 02.08 днём «канал ожил» была неверной и снята
тем же вечером: ответ OK на приёме приняли за доказательство отправки —
ровно мина «принял не значит отправил» В-212, ради которой и делается
Этап 5. Судьбу проб никто не спросил.
Замерено вечером глазами в кабинете МТС: тариф «Оплата по факту» подключён,
баланс 5010 руб. и зарезервировано 0, имя отправителя liderra.ru подключено
и стоит в .env боевого, чёрные списки пусты, API активно с токеном до 2027.
И всё равно три пробы 02.08 вернули NotSent с cost 0 — последняя через
5,5 часа после подключения тарифа. Дальше сторона владельца: письмо в
поддержку МТС с номерами сообщений 44846577 и 44849057.
Task 11 закрыт в части, не зависящей от МТС: тесты модуля 387 из 387,
сделки 17 из 17, фронт 237 файлов и 1899 зелёных при 3 пропущенных,
статанализ 0, pint чисто, vue-tsc 5 давних чужих. Права спрошены у базы
матрицей: счётчиков без USAGE нет, у служебной роли на журнале сообщений
есть UPDATE, обратная сторона тоже проверена. Денежная половина строк
5.6 и 5.7 и единицы поля cost остаются недоказанными — и это сказано
вслух, а не спрятано.
Х-1 снят замером: сторожа текста живы, 129 замечаний разметки в приёмочном
листе унаследованы — столько же даёт версия из git show HEAD.
Побочно доказано живьём: за неотправленное МТС денег не берёт.
Продолжает промт смены 9. В нём только живое: всё закрытое осталось в прежнем
файле.
Главное, что вскрылось при составлении и вынесено первым разделом: модуль
разложен по трём местам. Код — только на ветке client-sms. Приёмочный лист —
только на рабочей ветке, в папке СМС его не видно вообще. Шесть планов этапов
1-5 не записаны в git ни на одной ветке, это просто файлы на диске — одна
команда очистки, и документации модуля не станет.
Дальше по разделам: что уже готово и переделывать не надо, задача смены —
одиннадцатая задача, приёмка Этапа 5 с живым денежным прогоном, который до
02.08 был физически невозможен, и порядок выката с отдельным разрешением на
каждый шаг.
Числа перемерены командой: рабочая ветка 4 позади и 38 впереди общей, ветка СМС
0 позади и 75 впереди, статанализ 0, прогон ветки СМС 423 теста без падений.
Отдельно помечено, что запись Х-1 про мёртвых сторожей текста протухла: замер
показал, что орфография в папке СМС жива.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Досчитался широкий поиск по всему репозиторию: писателей у client_tg_contacts
нет нигде, находка подтверждена. Но всплыло важное — аудит телеграм-модуля
от 28.07 (пять параллельных аудиторов, построчно) отнёс эту таблицу к
разделу «✅ РЕАЛЬНОЕ (не трогать) — своя база».
Поучительно то, что ТОТ ЖЕ аудит поймал такой же пробел с медиа («поле
media_path есть в типе и в базе, а загрузки во фронте нет») — и его починила
пачка 1. С контактами не поймал, потому что вопрос был другой: искали
наследие СМС-модуля, а не спрашивали «кто сюда пишет».
Правило записано в оба документа: «таблица есть» и «в таблицу что-то
попадает» — разные вопросы, второй задавать отдельно поиском писателей.
К готовым аудитам относиться как к гипотезе, а не к замеру.
Ссылки на строки 13 и 84 аудита сверены с файлом.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Общая ветка принесла правки по разведке Яндекса: список рисует только видимые
строки, упавшее задание больше не сгорает, у придержанного объявления не бывает
окна причины, плюс четыре промта смен 01-02.08. СМС-кода эти правки не касаются.
Столкновение одно — словарь орфографии: обе стороны дописали слова в конец.
Обе стороны настоящие, склеены обе, ни одно слово не выброшено.
Проверки после сведения: статанализ 0, форматирование СМС и рекламы passed,
прогон 423 теста и все зелёные — тесты рекламы, которые принесла общая ветка,
плюс весь модуль клиентских СМС.
Ветка по-прежнему в main НЕ влита, не пушена, на боевой не выкачена.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Порядок в промте жёсткий: сперва спросить владельца по находкам, потом
чинить, потом выкат. Начинать с выката нельзя — находка про «Мою базу
номеров» делает его вредным: клиент выберет свою базу, увидит ноль и решит,
что портал потерял его клиентов.
К каждой находке — рекомендуемый ответ и ловушки починки. Значки (находка 1)
решения не требуют, уже починены; по остальным трём решает владелец.
Прежний промт 2026-08-06 помечен «отработан наполовину» со ссылкой на новый;
в силе там остаётся поимённый список выката из 24 файлов.
Проверено, а не записано по памяти: шесть хешей пачек сверены с журналом,
все упомянутые файлы существуют, номер журнала схемы v9.64 замерен, числа
тестов — прогоны этой смены (241/1832, 435, 292, статанализ 0).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Все три нашлись из вопросов владельца, глядя на живой экран. Ни одну не
увидели 1832 теста экранов и 292 теста телеграм-модуля.
1. «Моя база номеров» ведёт в никуда. Пункт читает client_tg_contacts, а в
эту таблицу не пишет ни одна строка кода: ни экрана загрузки, ни серверной
ручки, ни переноса из сделок. У ЛЮБОГО клиента пункт всегда покажет ноль.
Тесты зелёные потому, что каждый сам вписывает контакты перед проверкой, —
голый шов: состояние собирается руками, а дойти до него нечем.
2. С медиа наценка съедается, на видео уходит в минус. Клиент платит
тариф x1.40 (630/560/504 ₽ за тысячу по ступеням), МТС берёт 600 с
картинкой и 680 с видео. clientEstimateRub() принимает только число
показов — медиа на цену для клиента не влияет ни в оценке, ни в списании.
На кампании от 5000 показов с видео убыток 120 ₽ за тысячу. Песочница
выключена, деньги живые.
3. Срок показа нигде не объяснён. Портал сроком не управляет вовсе: в
задании роботу нет ни одной даты. Срок ставит кабинет МТС по умолчанию —
сегодня + 7 дней, каждому человеку один показ. «Лимит на объявление» —
потолок-предохранитель, а не длительность.
Ничего не чинил: все три — решения владельца, записаны с дорогами выбора.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Главный хвост проекта Х-5 закрыт, и прежний диагноз был неверным.
В кабинете «Рассылки по своей базе PRO» не был подключён тариф. История тарифов
содержала ровно одну запись за всё время: бесплатный тестовый пакет 10 SMS за 0
рублей от 23.07.2026 15:55, действующий до конца того же дня. Отсюда всё: 23.07
сообщения доходили, с 24.07 канал отвечал мгновенным отказом. Признак
trafficPatternType Unknown, который приняли за поломку у оператора, означал
буквально «тарифа нет». Раздел «Интеграции» писал это прямым текстом, при этом
наше API числилось активным, токен до 24.07.2027.
Приёмка вырезанием: подключили бесплатный тестовый пакет — живая проба тем же
боевым путём, через мост и боевым ключом, вернула code 0 и OK. Больше не меняли
ничего. Затем подключён постоянный тариф «Оплата по факту» и автоподключение
первого числа, после чего проба повторена и снова дала OK.
На бою задана цена SMS_MTS_PRICE_KOP=503. Кэш настроек пересобран по домашней
процедуре redeploy.sh, службы перезапущены. Проверено: price_kopecks 503,
портал отвечает 200.
Урок дороже задачи: есть доступ к чужой системе — открой её глазами. Десять дней
диагноз строился по ответам API, а живой профиль браузера с входом в кабинет всё
это время лежал в bots/mts-telegram-ads.
Файл приёмки записан с исключением сторожей текста — в нём 129 унаследованных
замечаний разметки, все были до этой правки. Автоправку не применял: она
перенумеровала бы списки, номера которых служат именами.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Приёмка глазами починила 20 значков в resources/js/plugins/vuetify.ts.
Файла не было в поимённом списке выката: всё остальное поехало бы, а значки
на боевом так и остались бы вопросами в кружке.
Заодно выверены числа в списке — раньше заголовок говорил «12 файлов» над
списком из 13. Сейчас: сервер 10 + экраны 14 = 24, посчитано командой,
существование каждого файла проверено.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Приёмка обоих рекламных экранов на локальном портале. Экраны работают:
живой счётчик охвата правда ходит на сервер, ручка оценки правда только
читает (после её вызовов ноль черновиков в базе), заголовок объявления сам
появляется при ссылке на сайт, «Запустить» не нажимается при охвате меньше
367, «Принято, показов пока нет» и «часть объявлений отклонена — 2 из 15»
на месте.
Находка, которой не видел ни один из 1829 зелёных тестов: значки в портале
не встроенные, имена mdi-* вручную сопоставлены с набором Lucide, а
незнакомое имя молча подменяется на HelpCircle. Клиент видит вопрос в
кружке вместо значка. Замер: 43 имени из 162 по всему порталу.
На рекламных экранах таких было 20 — включая заголовок телеграм-экрана,
ярлыки «Проверяем вручную» и «Запущена в кабинете МТС», поле «Картинка или
видео» и ярлык «Черновик» у Яндекса. Шесть из них добавил я в пачках 1–4,
четырнадцать ломались раньше. Все 20 починены, проверено глазами после
пересборки.
Заведён сторож tests/Frontend/znachki-est-v-karte.spec.ts: краснеет на
любом имени вне карты и называет файл-виновника. До починки был красным
ровно на тех 20 — проверено вырезанием. Оставшиеся 23 сломанных имени на
экранах вне рекламы записаны в нём списком известного долга; третья
проверка следит, чтобы список не протух.
Карта значков открыта наружу (export) ради сторожа — поведение не меняется.
Замеры: экраны 241 файл / 1832 теста / 0 падений (было 240/1829),
Advertising 435, ClientTg 292, статанализ 0, pint чисто, типы — те же 6
чужих ошибок. На боевой ничего не катал.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Пачка 4 закрыта целиком, дальше только приёмка и выкат. Промт содержит:
- 🔴 порядок «сперва глазами, потом выкат»: ни одна из пяти пачек глазами не
принята, а зелёный тест этого не доказывает (30.07 при 342 зелёных тестах
15 объявлений лежали черновиками);
- 9 точек приёмки телеграм-экрана и 3 яндексовского, с упором на живой счётчик
охвата — его никто не видел работающим;
- 🔴 ПОИМЁННЫЙ список выката: 22 файла кода (полный overlay запрещён), замер
git diff --name-only 7922bffb~1..HEAD; phpstan-baseline намеренно исключён
как ненужный на бою;
- 🔴 предупреждение про изменение базы в пачке 1 (два необязательных столбца
в client_tg_auto_rule, откат в миграции есть, журнал схемы на v9.64);
- разбор, почему новая ручка оценки НЕ требует новых прав в базе: читает то же,
что и создание кампании, и не пишет никуда;
- метки экрана проверены поимённо командой, а не по памяти; отдельно записано,
что tg-calc и tg-estimate больше НЕ существуют (ноль вхождений).
Три новые ловушки этой смены:
- датчик пробелов ложно срабатывает на переезде кусков кода — прежде чем чинить,
смотреть diff обоих датчиков глазами;
- резать файл скриптом по номерам строк опасно: так пропал открывающий тег
v-tabs-window, номера успели сдвинуться между замером и правкой;
- переносишь код — переноси проверки: сперва переписать на новом месте и там
прогнать, только потом убирать со старого.
Числа — свежие замеры: экраны 240 файлов / 1829 тестов, Яндекс 435, телеграм 292,
статанализ 0, типы 6 чужих ошибок. Промт 08-05 помечен отработанным.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Поле weekly_budget_rub объявлялось в описании ответа Campaign и стояло в 21
заготовке пяти файлов тестов интерфейса, но не читалось нигде — сервер
принимает и правит кампанию по budget_rub.
Поправка к прежней записи: утверждение «сервер его не возвращает» было
неточным. Список кампаний отдаётся узким набором столбцов без него, но
создание и правка возвращают всю строку целиком — поле приходит, всегда
пустое. Мёртвым оно было именно на фронте.
Серверная сторона намеренно не тронута: столбец ad_campaigns.weekly_budget_rub
в базе существует и его заполняют около шестидесяти мест в tests/Feature,
которые пишут в базу напрямую. Там поле настоящее, снос столбца — отдельная
работа с миграцией.
Приёмка: на фронте не осталось ни одного упоминания, проверка типов 0,
линтер 0, полный набор интерфейса 233 файла / 1750 тестов / 3 пропущено /
0 падений. Трём файлам вернул форматирование, которое сбилось от удаления
строк.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Замечание владельца: «мастер шагов вместо простыни» и «живой счётчик охвата
вместо кнопки Рассчитать». Форма была одна длинная, охват узнавался только по
нажатию кнопки.
Экран:
- мастер из четырёх шагов: Кому показываем → Объявление → Деньги → Проверка;
- живой счётчик охвата на первом шаге, пересчитывается сам при смене источника
людей, периода или списка номеров (с задержкой, чтобы не дёргать сервер
на каждую букву);
- кнопки «Рассчитать» больше нет: «Запустить» создаёт кампанию, прикладывает
картинку и отправляет в кабинет одним нажатием;
- на шаге «Деньги» рядом смета и остаток баланса — нехватка денег видна ДО
запуска, а не отказом после;
- сводка на последнем шаге повторяет всё выбранное.
Сервер — новая ручка POST /api/telegram/campaigns/estimate:
🔴 Живой счётчик обязан считать на каждое изменение поля, а охват до сих пор
возвращала только POST /campaigns — и она СОЗДАЁТ черновик в базе. Через неё
счётчик наплодил бы десятки кампаний-призраков в списке клиента. Новая ручка
только читает: ни записи, ни денег, ни робота. На это стоит отдельный тест.
Ручка считает ТЕМ ЖЕ кодом, что и создание кампании (общее тело выборки из
сделок вынесено на оба входа TelegramAudienceService). Отдельный тест сверяет
два ответа: разойдись они — клиент видел бы на экране одно число, а платил
по другому.
🔴 Запуск запрещён, когда людей меньше 367 — правило площадки, не наше. Тот же
предохранитель стоит на сервере (AudienceGateTest); на экране он объясняет
заранее, вместо отказа после оплаты.
Новых прав в базе ручка не требует: читает строго то же, что уже читает
создание кампании, и не пишет никуда.
Тесты: +EstimateApiTest (11 на PHP), +telegram-master-shagov (27 на экранах).
14 прежних проверок формы переехали в набор мастера вместе с кодом — сперва
переписаны на новом месте и там проверены, потом убраны со старого. Устарели
по смыслу только две («Рассчитать создаёт черновик», «правка сбрасывает
смету»): считать вручную больше нечего, устареть расчёту негде.
Замер: экраны 240 файлов, 1829 тестов, 0 падений (было 239/1813); телеграм-
модуль на PHP 292 теста, 0 падений (было 281); статанализ 0 (базовая линия
пересобрана — добавлен только новый Pest-файл, удалений нет); типы 6 чужих
ошибок вместо 7; pint чист.
На боевой НЕ выкачено. Глазами не принято — приёмка в пачке 5.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>