00cc072e2f076a3a0060f45cc24cee78b2bdc79e
1731 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ed409167e3 |
docs(реклама): мёртвую таблицу контактов прежний аудит благословил — записал как ловушку
Досчитался широкий поиск по всему репозиторию: писателей у client_tg_contacts нет нигде, находка подтверждена. Но всплыло важное — аудит телеграм-модуля от 28.07 (пять параллельных аудиторов, построчно) отнёс эту таблицу к разделу «✅ РЕАЛЬНОЕ (не трогать) — своя база». Поучительно то, что ТОТ ЖЕ аудит поймал такой же пробел с медиа («поле media_path есть в типе и в базе, а загрузки во фронте нет») — и его починила пачка 1. С контактами не поймал, потому что вопрос был другой: искали наследие СМС-модуля, а не спрашивали «кто сюда пишет». Правило записано в оба документа: «таблица есть» и «в таблицу что-то попадает» — разные вопросы, второй задавать отдельно поиском писателей. К готовым аудитам относиться как к гипотезе, а не к замеру. Ссылки на строки 13 и 84 аудита сверены с файлом. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
5a516e8a88 |
docs(реклама): промт следующей смене — четыре находки на решение владельца, потом выкат
Порядок в промте жёсткий: сперва спросить владельца по находкам, потом чинить, потом выкат. Начинать с выката нельзя — находка про «Мою базу номеров» делает его вредным: клиент выберет свою базу, увидит ноль и решит, что портал потерял его клиентов. К каждой находке — рекомендуемый ответ и ловушки починки. Значки (находка 1) решения не требуют, уже починены; по остальным трём решает владелец. Прежний промт 2026-08-06 помечен «отработан наполовину» со ссылкой на новый; в силе там остаётся поимённый список выката из 24 файлов. Проверено, а не записано по памяти: шесть хешей пачек сверены с журналом, все упомянутые файлы существуют, номер журнала схемы v9.64 замерен, числа тестов — прогоны этой смены (241/1832, 435, 292, статанализ 0). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
46e1adecba |
docs(реклама): ещё три находки приёмки глазами — мёртвый пункт «Моя база», минус на медиа, срок показа
Все три нашлись из вопросов владельца, глядя на живой экран. Ни одну не увидели 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> |
||
|
|
9e23c59e60 |
docs(реклама): в список выката добавлена карта значков — без неё вопросы в кружках останутся на бою
Приёмка глазами починила 20 значков в resources/js/plugins/vuetify.ts. Файла не было в поимённом списке выката: всё остальное поехало бы, а значки на боевом так и остались бы вопросами в кружке. Заодно выверены числа в списке — раньше заголовок говорил «12 файлов» над списком из 13. Сейчас: сервер 10 + экраны 14 = 24, посчитано командой, существование каждого файла проверено. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
3d728a2bab |
fix(реклама): пачка 5 — приёмка глазами: 20 значков рисовались вопросом в кружке
Приёмка обоих рекламных экранов на локальном портале. Экраны работают: живой счётчик охвата правда ходит на сервер, ручка оценки правда только читает (после её вызовов ноль черновиков в базе), заголовок объявления сам появляется при ссылке на сайт, «Запустить» не нажимается при охвате меньше 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> |
||
|
|
dbc79f15b3 |
docs(реклама): промт следующей смене — пачка 5, приёмка глазами и выкат пяти пачек
Пачка 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> |
||
|
|
76cb44b91d |
feat(телеграм-реклама): пачка 4б — мастер шагов, живой счётчик охвата и серверная ручка оценки
Замечание владельца: «мастер шагов вместо простыни» и «живой счётчик охвата вместо кнопки Рассчитать». Форма была одна длинная, охват узнавался только по нажатию кнопки. Экран: - мастер из четырёх шагов: Кому показываем → Объявление → Деньги → Проверка; - живой счётчик охвата на первом шаге, пересчитывается сам при смене источника людей, периода или списка номеров (с задержкой, чтобы не дёргать сервер на каждую букву); - кнопки «Рассчитать» больше нет: «Запустить» создаёт кампанию, прикладывает картинку и отправляет в кабинет одним нажатием; - на шаге «Деньги» рядом смета и остаток баланса — нехватка денег видна ДО запуска, а не отказом после; - сводка на последнем шаге повторяет всё выбранное. Сервер — новая ручка 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> |
||
|
|
bcbbb25b09 |
feat(телеграм-реклама): пачка 4а — единый каркас экрана: вкладки, карточка денег, кампании карточками
Замечание владельца: два рекламных экрана выглядят как сделанные разными людьми.
Телеграм был простынёй на одной странице — панель авто-рекламы поперёк экрана,
под ней форма, под ней список строчками. Теперь каркас тот же, что у Яндекса.
Что сделано:
- вкладки «Мои кампании» / «Новая реклама» / «Авто-реклама»; экран открывается
на своих кампаниях и сам возвращает к ним после запуска — как у Яндекса;
- панель авто-рекламы уехала в свою вкладку и больше не мешает читать форму;
- карточка денег сверху в яндексовом виде, сумма по-человечески («5 000 ₽»);
- кампании карточками: цветной ярлык со значком, номер, кандидаты, лимит, дата;
пустой список зовёт завести первую рекламу кнопкой.
🔴 Карточка денег — ОТДЕЛЬНЫЙ компонент, а не яндексовая шапка кошелька.
Экраны берут деньги из разных мест: Яндекс — из ad_wallets, телеграм — из общего
баланса тенанта (решение владельца, коммит
|
||
|
|
4eac8d8ff1 |
docs(реклама): промт следующей смене — пачка 4, единый каркас телеграм-экрана
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Разведка сделана живьём, а не по памяти, и вскрыла две мины, из-за которых задание владельца «сделать как у Яндекса» нельзя выполнить в лоб: 1. Деньги у экранов РАЗНЫЕ. Яндекс берёт из рекламного кошелька (ad_wallets, баланс/заморожено/свободно), телеграм — общий баланс тенанта, причём frozen_rub отдаётся жёстко '0.00'. Вставить AdWalletHeader в телеграм — значит показать клиенту чужие деньги. Нужен тот же вид при своих данных. 2. Живого счётчика охвата без сервера не будет. У телеграма всего восемь ручек, оценки среди них нет; охват возвращает только POST /campaigns, а она создаёт черновик в базе. Дёргать её на каждое изменение поля — засорить базу. Нужна новая серверная ручка оценки. Отсюда совет владельцу: разбить пачку 4 надвое — 4а чистый фронт (вкладки, карточка денег, единый вид карточек кампаний), 4б мастер шагов и живой счётчик (требует бэкенда). Иначе смена кончится посередине. Названа и цена: на нынешнюю разметку телеграм-экрана завязано 45 тестов. Ориентиры даны с номерами строк, сверенными с живыми файлами; список меток разметки проверен поимённо — одну (tg-headline) я написал по памяти неверно, настоящая tg-ad-headline, поправлено до коммита. Прежний промт помечен отработанным, с указателем на этот. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
6ae6457b34 |
feat(реклама): пачка 3 — подсказки «?» у 16 непонятных полей на обоих экранах
Замечание владельца: поля, смысл которых человеку взяться неоткуда (категория ОРД, порог 367, частота показов, «Заморожено»/«Свободно»), решают, сколько клиент заплатит, а объяснения к ним нет. Клиент либо гадает, либо не трогает. Подсказка стоит у 16 полей: телеграм-форма (дни сделок, лимит на объявление, ОРД, картинка/видео), форма пересдачи (ОРД), панель авто-рекламы (порог, бюджет, дневной лимит), мастер Яндекса (оба конца периода контактов, срок показа, частота — на шаге и в сводке, цена за тысячу), кошелёк (заморожено, свободно). Тексты собраны в ОДИН файл composables/podskazkiReklamy.ts — 13 подсказок. Поля ОРД и «Частота показов» стоят в разметке дважды, и в пачке 2 мы уже разгребали ровно этот класс беды: две копии подписей статусов разъехались, клиент читал на двух экранах разное. Теперь физически не разъедутся. Про ОРД написано словами владельца из З-4. На очевидные поля («Текст объявления», «Ссылка», «Кошелёк») подсказки нет намеренно — шум, из-за которого перестают читать нужные; на это стоит отдельный тест. Язык стерегут датчики: ни одной латинской буквы (значит ни CPM, ни имён из кода), запрет жаргона, длина 40–200 знаков, два-три предложения. В поле ввода подсказка вешается слотом append-inner, а не label: метка у Vuetify плавающая, при щелчке уезжает вверх и знак вопроса уехал бы с ней. Тесты: podskazki-reklamy.spec.ts (тексты, 8) + podskazki-na-ekranah.spec.ts (привязка к полям, 8). Привязка проверяется честно — селектором «подсказка внутри своего поля», а не «есть где-то на экране». Проверено: 237 файлов экранов, 1787 тестов, 0 падений; ESLint 0; проверка типов — те же 7 чужих ошибок, что и были. Глазами НЕ принято, на боевой НЕ выкачено. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
06a56e1932 |
docs(реклама): промт следующей смене — пачка 3, подсказки «?» на обоих экранах
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Новый промт 2026-08-04-PROMT-podskazki-na-ekranah.md: компонент HelpHint готов и уже живёт в отделе продаж, на рекламных экранах не подключён ни разу. Поля, у которых человеку смысл взяться неоткуда, названы поимённо со строками — телеграм- экран, панель авто-рекламы, мастер Яндекса, кошелёк; строки сверены с живыми файлами. Отдельно правило «не вешать вопрос на очевидное» — иначе перестают читать и нужные. Прежний промт (пачка 2) помечен отработанным с указателем на новый. В замечаниях: вопрос про числа показов телеграм-кампании переведён из открытого в отложенный — владелец 02.08: «показывает, надо только запустить и начнёт показывать — не обращай внимание пока». Помечено «по своей инициативе не трогать», чтобы следующая смена за этим не гналась. Новые ловушки из смены: причёсывалка переписывает ВЕСЬ файл, если он не был причёсан до тебя (телеграм-экран дал 296 строк вместо 24 — датчик `git diff -w`); итоги группировки не модели, нужен `->toBase()`. Код не тронут. Пачки 1 и 2 по-прежнему на боевой НЕ выкачены. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
a2496dfe61 |
feat(реклама): честные статусы кампаний — экран больше не зовёт «Крутится» рекламу с нулём показов
Замечание владельца З-6: «почему статус крутится, когда она отклонена?» Живой замер кампании #6 на бою: статус running, зелёное «Крутится», показов доставлено НОЛЬ, потрачено 0 ₽, заморожено 3 333,36 ₽ — третьи сутки. Главное, что вскрылось: экран физически не мог показать правду. Список кампаний отдавал estimated_impressions («сколько обещали») и не отдавал delivered_impressions («сколько было»), хотя колонка есть. Чисел принятых/отклонённых объявлений тоже не было. Поэтому чинили с сервера, а не с подписей. - сервер отдаёт факты: delivered_impressions + счётчики объявлений (всего/принято/ отклонено), ОДНИМ запросом на весь список — на N+1 поставлен отдельный датчик; те же счётчики доезжают и в отчёт по кампании; - считаются только включённые в показ: снятое галочкой в Яндекс не уезжает и в знаменателе «2 из 15» ему не место; - ярлык по фактам: running при нуле показов → «Принято, показов пока нет» нейтральным цветом; пошли показы → зелёное «Крутится»; часть отклонена → приписка «часть объявлений отклонена — 2 из 15»; все → красное «Отклонено»; - правило перехода статусов НЕ тронуто: на rejected висит возврат заморозки (AdWalletService::release, «ВЫХОД 2»). Чинили то, что видит человек; - подписи собраны в один файл (composables/campaignStatusMeta.ts). Их было ДВЕ копии — в списке и в отчёте — и они уже разъехались; разъехавшиеся копии и есть та разница между экранами, на которую жалуется владелец; - телеграм-экран: подписи вынесены отдельно и приведены к тем же словам («На модерации в МТС» ↔ «На модерации в Яндексе», «Отклонено» на обоих); голое «Запущена» → «Запущена в кабинете МТС». Граница честности: у телеграм-модуля нет счётчика показов ВООБЩЕ — actual_cost_rub в таблице есть, но её не пишет ни одна строка кода. Поэтому там нельзя сказать ни «крутится», ни «показов пока нет»: это была бы выдумка того же сорта. Записано открытым вопросом владельцу в файле замечаний. Проверено: 435 тестов рекламы Яндекса, 281 телеграма, 1771 тест экранов (235 файлов, 3 пропущено), статанализ 0, формат чист. Новых тестов 18. Глазами НЕ принято (приёмка — пачка 5), на боевой НЕ выкачено. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9d3dc73608 |
docs(реклама): промт следующей смене — пачка 2, честные статусы кампаний
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Отдельный файл вместо правки прежнего: тот отработан, на нём оставлен указатель. Главное, что нашёл при подготовке промта и чего в замечаниях не было: экран физически НЕ МОЖЕТ показать правду о показах. AdvertisingCampaignController:57 отдаёт списку кампаний estimated_impressions — сколько обещали, — а поля delivered_impressions в ответе нет вовсе, хотя в таблице ad_campaigns оно есть. Числа принятых и отклонённых объявлений тоже не уходят. Значит пачка 2 начинается с сервера, а не с подписей на кнопках. Отдельно записано, что правило в SyncCampaignModerationJob менять НЕЛЬЗЯ: на статусе rejected висит возврат заморозки клиенту. Чинить надо то, что видит человек, а не машину статусов. Ярлыки живут в трёх местах — CampaignList.vue, CampaignReportDialog.vue и STATUS_LABELS телеграм-экрана; все три названы поимённо, иначе получится ровно та разница между экранами, на которую владелец и жалуется. Из ловушек добавлена своя ошибка этой смены: тесты фронтенда лежат в app/tests/Frontend, а я искал их в resources и объявил, что тестов Vue нет вовсе. Их 232 файла. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
7922bffb6d |
feat(телеграм-реклама): заголовок и картинка в авто-режиме + честная проверка медиа по требованиям МТС
Пачка 1 замечаний владельца о едином виде рекламных экранов. Песочница выключена 02.08.2026 — каждая дыра ниже стоила живых денег. 1. Заголовок объявления в АВТО-правиле. Кабинет МТС требует его для рекламы сайта; утром 02.08 поле довезли до разовой формы, а в авто его не было вовсе — накопитель создавал кампанию без ad_headline, и она сгорела бы в кабинете уже после списания. Колонка client_tg_auto_rule.ad_headline, приём в контроллере (обязателен только при включённом авто и не-телеграмной ссылке), перенос в кампанию, поле на экране. 2. Картинка/видео в авто-правиле. Колонка media_path, отдельная загрузка POST /api/telegram/auto-rule/media, перенос в кампанию, поле на экране. 3. Проверка медиа переписана по настоящим требованиям кабинета, снятым глазами 02.08. Было mimes:png,jpg,jpeg,gif,mp4|max:51200 — врало по пяти пунктам: принимало GIF, пропускало вдвое больший вес, не смотрело пиксели и длительность, зря отказывало в mov/webm. Стало правило App\Rules\ClientTg\MtsMedia: картинка JPEG/PNG до 25 МБ и 640x360...5120x2880, видео до 20 МБ, 3-55 секунд, от 640x360. Формат картинки определяется по содержимому, длительность и кадр видео читает App\Support\Mp4Probe из контейнера — без внешних программ. Отказ человеческий: «Картинка слишком маленькая: 300x200. Нужна не меньше 640x360». 4. Цена показа с медиа — 600 руб. с картинкой, 680 с видео — теперь видна ДО загрузки файла, на обоих экранах. Сверх плана, найдено по дороге: 5. Предохранитель накопителя: правило, сохранённое до 02.08 со ссылкой на сайт и пустым заголовком, всё равно ушло бы в кабинет. Теперь такая пачка держится черновиком, в журнал пишется причина no_headline. 6. Отказ сервера доходит до клиента его словами. Оба экрана глушили ответ общей фразой «Не удалось рассчитать кампанию», и человек не понимал, что не так с файлом. Границы честности: длительность и размер кадра читаются только у mp4/mov/m4v; у webm/mkv/mpeg/wmv проверяются формат и вес — так и записано в Mp4Probe. Проверено: 281 тест телеграм-модуля, 1753 фронтенд-теста, статанализ 0, Pint чист. Глазами НЕ принимали — приёмка живьём в пачке 5. На боевой не выкачено. Журнал схемы: v9.64 — номер взят как максимум по всем веткам плюс один, чтобы не повторить столкновения 29.07 и 01.08. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
e74b08eca8 |
docs(реклама): замечания владельца о едином виде экранов + требования МТС к медиа живьём
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Владелец: телеграм и Яндекс выглядят по-разному, клиент не должен изучать десять интерфейсов; нужны подсказки «?» и требования к картинке с проверкой на нашей стороне. Собрано в файл замечаний: - ответы на три вопроса владельца (категория ОРД, картинка в авто, «Крутится» при отказе) — с живыми замерами из боевой базы; - требования МТС к медиа, снятые ГЛАЗАМИ в кабинете: картинка JPEG/PNG до 25 МБ, 640×360…5120×2880; видео до 20 МБ, 3–55 сек, от 640×360; показ с картинкой 600 ₽, с видео 680 ₽; - наша проверка неверна по пяти пунктам (принимаем GIF, которого МТС не берёт; 50 МБ вместо 25; не смотрим пиксели и длительность; зря отказываем в mov/webm); - найдена дыра: у авто-правила нет ни заголовка объявления, ни картинки — авто со ссылкой на сайт сгорит в кабинете, а песочница уже выключена; - сравнение экранов: Яндекс — мастер из четырёх шагов, телеграм — анкета; - план пачками (опасное → честные статусы → подсказки → единый каркас → приёмка). Промт следующей смене — вход в работу, состояние боевого и ловушки смены. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ad7a312714 |
docs(пилот): выкачен заголовок объявления на боевой + правка двух устаревших строк
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Записан выкат фронтенда телеграм-рекламы 02.08.2026: два файла экрана + пересобранный public/build (бандл app-Dp1fSF0n.js), приёмка глазами под info@lkomega.ru — поле «Заголовок объявления» появляется для рекламы сайта и не появляется для ссылки на канал. Указано, где лежит откат. Поправлены две строки, о которые споткнулся сам: - демо-вход admin@demo.local не работает с 01.07 (база чищена), тестовых учёток на бою нет вовсе — все 10 тенантов живые; - «на VM node_modules нет, фронт собирается локально» устарело: node/npm на месте, зависимости поставлены, сборка идёт прямо на боевом за ~2.4 с. Записан безопасный порядок — собирать в build-new и подменять mv. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
8712a33404 |
docs: промт следующей смене — заголовок есть в портале, но нет на экране клиента
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Замер 02.08 вскрыл дыру, ради которой и заводилась ветка. Бэкенд заголовка объявления выкачен на бой полностью: колонка ad_headline в базе есть, CampaignController ТРЕБУЕТ заголовок для рекламы сайта, робот его заполняет (проверено глазами в кабинете МТС, кампания 2234762). А экрана нет: - resources/js/views/advertising/AdvertisingTelegramView.vue на бою — 0 упоминаний заголовка, в main — 4; - resources/js/api/telegram.ts на бою без поля ad_headline; - собранный public/build на бою — 0 файлов со словом ad_headline. Значит клиент, рекламирующий свой САЙТ, получит отказ «заголовок обязателен» и не найдёт поля, куда его вписать. Пока не горит: телеграм-кампаний на бою 0, песочница включена. Сработает у первого же клиента. Заодно разобрано вчерашнее «58 файлов разошлись» — пугало зря. Из 55 расхождений по контрольным суммам: 47 файлов тестов (на бою не нужны), 1 phpstan-baseline, 1 различие в КОММЕНТАРИИ (SupplierPortalClient), 4 миграции (три по мелочи, одна отсутствует на бою штатно — так работает рунбук §6b: DDL накатывают psql'ем и дописывают строку в migrations), и 2 файла фронтенда — та самая дыра. 🪤 Записан ключевой приём: без --checksum сверка ВРЁТ. Свежий клон даёт 2433 «расхождения» по времени файла; с --checksum их 55. В промте также: рецепт повторения разбора, что закрыто и трогать не надо (приёмка Г1, обе починки осечки, Яндекс), три вещи за владельцем (выход из песочницы, стена 367 номеров, непроверенный read-status) и грабли смены. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
deb54ae1a4 |
fix(телеграм-робот): чиним саму осечку — робот путал «не отрисовалось» с «выкинуло»
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Прошлая правка лечила последствие: временный отказ возвращал задание в очередь.
Причина осталась. Она была в одной строке src/session.js:
await gotoStable(page, config.cabinetUrl, config); // ответ ВЫБРАСЫВАЛСЯ
gotoStable уже возвращал признак «страница так и не ожила» — его игнорировали,
а дальше отсутствие меню объявляли разлогином. Отсюда ложное «Вход слетел»
на живой сессии (приёмка 02.08, 1 прогон из 2).
Теперь checkSession различает четыре разных случая:
- in — меню кабинета на месте;
- out — видна страница входа: сессии правда нет. Повтор бесполезен,
сдаёмся СРАЗУ с «нужен вход руками» (retryable=false);
- ne-otrisovalos — ни меню, ни формы входа: SPA завис. Три попытки с паузой 5с,
потом временный отказ (retryable=true);
- zablokirovan — «Доступ … запрещён»: МТС не пускает адрес. Сдаёмся сразу,
со своей внятной причиной (retryable=false).
Пустую страницу про меню не спрашиваем вовсе — минус 10 секунд на попытку.
Побочно вылечен сторож входа bin/keepalive.js: он болел тем же и слал владельцу
письмо «Вход в кабинет слетел» на каждом зависании кабинета. Теперь в письме
стоит настоящая причина, а временная беда не считается падением сторожа.
Смена смысла: SessionLostError раньше был retryable=true (правка часом раньше).
Теперь настоящий разлогин НЕ повторяется — повтор его не лечит, нужен человек.
Повторяется только зависшая страница.
Тесты писались красными: робот 143/143 (+9 новых, новый файл session-check),
портал 254/254 без изменений — кода портала правка не касается.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
||
|
|
3c72560139 |
fix(телеграм): ложное «вход слетел» больше не хоронит кампанию клиента
Приёмка Г1 02.08.2026 прошла: задание прошло насквозь, робот создал кампанию 2234762 в кабинете МТС, заголовок объявления и ссылка проверены ГЛАЗАМИ на шаге «Объявление». Песочница, 445 номеров из 450 опознано, деньги не двигались. Следы пробы убраны, копия — app/storage/app/proba-priemki-2026-08-02.json. Приёмка вскрыла мину. Первое из двух заданий упало с «Вход слетел — нужен повторный логин», хотя вход был ЖИВ: тем же кодом робота минутой позже проверка ответила «да». Кабинет МТС завис на пустой странице, робот принял это за разлогин. Цена — одна осечка сразу хоронила кампанию: задание в failed, кампания в failed, второй попытки нет. На приёмке это 1 прогон из 2. Робот (bots/mts-telegram-ads): - ensureLoggedIn в src/session.js — три попытки с паузой 5с; сбой самой проверки считается попыткой, а не падением; - SessionLostError несёт признак retryable; - runner.js во всех трёх режимах (запуск, чтение вердикта, пересдача) зовёт ensureLoggedIn и протаскивает retryable в отчёт; - bin/poll.js протаскивает retryable из своей обёртки. Портал (app): - RobotResult читает retryable (только при ok:false); - TgRobotController::done на временный отказ возвращает задание в очередь (queued, taken_at=null) и кампанию НЕ трогает, пока attempts < max_attempts; - client_tg.robot.max_attempts, по умолчанию 3, env TG_ROBOT_MAX_ATTEMPTS. Считаются выдачи задания, а не отказы: тем же счётчиком attempts пользуется возврат по сроку аренды, поэтому бесконечного круга не выйдет. Тесты писались красными: портал 254/254 (720 проверок, +4 новых), робот 134/134 (+4 новых). Pint чист, статанализ 0 ошибок. В phpstan-baseline.neon дописаны 27 записей про $this в Pest-замыканиях — тем же порядком, что у соседнего TgRobotDoneTest. На боевой НЕ выкачено. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
0a113c7cda |
docs: промт по телеграму переписан — робот уже работает, стена с прокси снята
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Замеры 02.08.2026 00:16-00:20 опровергли две главные мысли первой редакции: 1. «Робот не настроен» — неверно. Померена была не та машина: робот живёт на машине владельца (C:\liderra\mts-telegram-robot) с 31.07, задача «Планировщика» идёт раз в минуту, последний запуск успешен, ручной проход отвечает «Работы нет.», вход в кабинет МТС живой, баланс 5010 руб. На рендер-сервере лежит брошенная копия без .env и таймера. 2. «Ждём ответа по обращению 31074» — неверно. Поставщик закрыл его 01.08, деньги вернул, домены МТС у него в блок-листе навсегда. Перепроверено живьём: через прокси ipify отдаёт 200, marketolog.mts.ru — обрыв. Ответ был записан ещё вчера в соседнем промте. Что осталось: сквозная приёмка Г1. В боевой базе 0 заданий за всю историю (таблица client_tg_robot_jobs, а не client_tg_jobs). Порог 367 номеров зашит числом в src/cabinet.js:65 — настройкой не обойти. Добавлены три ловушки: мерить надо ту машину; ответ мог быть записан раньше в другом файле; заливку скрипта на боевой и запуск не соединять через &&. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
965ba69b13 |
docs: промт по телеграму — портал уже на бою, дело за роботом
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Замеры живые, боевой и рендер-сервер, только чтение. Что выяснилось вопреки прежним промтам: - телеграм-портал УЖЕ выкачен на бой: 7 таблиц, 17 миграций, TG_ROBOT_TRANSPORT=poll, TG_SANDBOX=true, канал api/tg-robot отвечает 401 без токена, код совпадает с main байт в байт; - обе телеграм-ветки давно сведены в main, впереди на 0; - работа стоит на другом: робот на рендере лежит БЕЗ .env и без таймера, забирать задания некому; - стена прежняя — МТС не пускает дата-центры, обращение 31074 к proxy.market; - тесты телеграма 250/250, 702 проверки. Исправлена моя ошибка в промте ostalos-vladelcu: утверждение «на боевом нет ни одной таблицы телеграма» было неправдой — искал по маске tg_%, а таблицы зовутся client_tg_*. Пустой ответ принял за факт вместо того, чтобы усомниться в запросе. Записано ловушкой. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fbd2ff86d4 |
docs: лента клиента вычищена — 17 английских сообщений, а не 4
Accessibility (Pa11y live) / a11y (push) Has been cancelled
По явному «да» владельца, боевая база, ровно по списку id с дословной сверкой текста перед удалением. - их оказалось 17 (id 1-15, 18, 19), прежняя цифра «4» была ошибкой; - замер после: осталось 15 сообщений, английских 0, баннеров без единого сообщения 0 — русская причина есть у каждого; - копия удалённых строк на бою: storage/app/udalennye-soobshcheniya-2026-08-01.json Новая красная ловушка в промт: страховку перед необратимым ПРОВЕРЯТЬ. file_put_contents вернул false предупреждением, а не исключением — скрипт поехал дальше и удалил без копии. Спас отдельный снимок до удаления. Уточнение по телеграму: на боевом нет ни одной таблицы телеграма и робота МТС нет — Г1 упирается в выкат, а не в кампанию владельца. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
fe5ee09cf3 |
docs: разбор трёх сбойных заливок картинок — вреда нет, хвост №4 закрыт
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Замер живой, боевая база и кабинет Яндекса, только чтение. Что нашлось: - три задания 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> |
||
|
|
495ee62a4a |
docs: Яндекс закрыт решением владельца — документов не будет, робот обучен
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Владелец 01.08.2026: «документов нет, забудь — мы это делали для того, чтобы научить робота». Кампания 713175197 была учебной. - хвост №1 переведён из «главное, ждём» в «закрыт»: требование документов у Яндекса — ожидаемое состояние, а не наша поломка; - прямой запрет следующей смене дожимать показы и искать обход; - итог смены переписан: единственное живое направление — телеграм; - поправлена вершина main ( |
||
|
|
96c92b9f8d |
docs: промт следующей смене — реклама Яндекса доведена, дальше решает владелец
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Заменяет 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> |
||
|
|
3b158a62f4 |
docs: четыре промта смен 01-02.08 — цепочка передачи по рекламе Яндекса
Копятся по порядку, каждый следующий заменяет предыдущий: вход в кабинет и живой режим → хвосты после живого режима → после сведения веток → закрыть хвосты. Последний рабочий, его читать первым. В последнем помечены как НЕВЕРНЫЕ три вывода прежних смен, оставленные на месте нарочно: «семь объявлений пропали», «разведчика негде запускать», «вернул 13 заданий в очередь». Под каждым — чем оказалось на самом деле. Только документы. Кода и боевого не касается. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
97a13aef64 |
fix разведка Яндекса: у придержанного объявления окна причины не бывает
Accessibility (Pa11y live) / a11y (push) Has been cancelled
Первый боевой обход с починками модерации поставил разведке 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> |
||
|
|
6e7c99f5ab |
merge: свёл ветку робота телеграма со своей — обе закрыты, готовятся в main
Владелец объявил закрытыми обе телеграм-ветки и велел свести их вместе, ювелирно и ничего не потеряв. Ветка робота несла 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> |
||
|
|
62d81e33f7 |
Merge remote-tracking branch 'gitea/main' into fix/tg-zagolovok-obyavleniya
# Conflicts: # docs/observer/STATUS.md |
||
|
|
9ef688fa8d |
merge: подтянул main после выката починок модерации Яндекса
Мой коммит уехал в main отдельно — пересадкой на актуальную вершину и оттуда на боевой. Возвращаю main к себе, чтобы ветка не разъезжалась. Пришло 9 коммитов, из них главное — работа соседней сессии по воронке продаж, уже влитая в main и запушенная. Конфликта два, оба в общих машинных файлах: - cspell-words.txt — сторону не выбирал, объединил. Все слова обеих сторон на месте; убрана ровно одна строка — мой же дубль «админский», который прошлый коммит добавил дважды. Проверено сравнением с версией до слияния: другого отличия нет; - docs/observer/STATUS.md — машинный файл наблюдателя со столбиком часов процессов, взята своя версия, его всё равно переписывает хук. Замечание на будущее: патч «минимум площадки считается по длине периода» живёт в двух коммитах — |
||
|
|
947cb3403a |
fix(воронка-продаж): сроки фильтра по датам — «Просроченные» отдельным пунктом, планы смотрят вперёд
Просьба владельца 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
|
||
|
|
9552efc820 |
fix реклама Яндекса: три поломки модерации, вскрытые заходом в живой кабинет
Кампания 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> |
||
|
|
b7bfe5a46f |
fix реклама Яндекса: три поломки модерации, вскрытые заходом в живой кабинет
Кампания 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> |
||
|
|
f0a18f614f |
docs выкат: в промт вписана мина, которой в нём не было - полный выкат откатывает чужое
Первая редакция промта вела следующую смену прямо в аварию: шаг «выкат файлов» без единой проверки, насколько ветка отстала от основной. На 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> |
||
|
|
c6dca0d442 |
docs выкат: в промт вписана мина, которой в нём не было - полный выкат откатывает чужое
Первая редакция промта вела следующую смену прямо в аварию: шаг «выкат файлов» без единой проверки, насколько ветка отстала от основной. На 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> |
||
|
|
4efd3d3329 |
merge: подтянул воронку продаж с основной - иначе выкат откатил бы блок менеджеров
Владелец спросил, не потрём ли мы соседнюю смену. Проверил - потёрли бы. Наша ветка отстала от основной на 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> |
||
|
|
3fdf5df8ca |
fix(воронка продаж): фильтры одним рядом справа, срок левее самого фильтра
По правке владельца со скрина: блок фильтра по датам переехал из отдельной строки под заголовком в общий ряд справа — к «Менеджер» и «Происхождение». Порядок внутри блока перевёрнут: САМ ФИЛЬТР крайний справа, СРОК левее него. Читается справа налево: «что менялось» → «вчера». Ряд получил flex-wrap: на узком экране переносится, а не уезжает за край. На экране менеджера тот же ряд — экраны не должны разъезжаться. Порядок закреплён тестом, иначе его снова переставят. |
||
|
|
1bd33c09cf |
fix: возврат чужой работы, которую откатило моё сведение
Перед слиянием с main я откатил 4 файла к своей старой версии, чтобы
сдвинуть с места застрявшее слияние — и слияние закрепило этот откат.
Пострадала починка от 29.07: чтение связок поставщика из pivot (без неё
5 из 9 работающих проектов показывали жёлтое «Готовим к запуску» при
живом заказе) и два набора тестов вебхука/сверки CSV.
Файлы возвращены к состоянию main
|
||
|
|
bf03e6204e |
docs выкат: промт следующей смене на выкат телеграм-рекламы на бой
Владелец решил катить. Промт написан под это, а не под хвосты. Главное, что вскрылось при подготовке: работа разошлась по ДВУМ веткам, и ни одна не содержит другую. У соседней ветки tg-zagolovok нет наших трёх правок тестов от 01.08, у нас нет их заголовка объявления. Обе от основной не отстают. Поэтому первый шаг смены - свести ветки, иначе выкатим половину. Выкат намеренно разрезан надвое. Портал можно катить сразу: переключатель остаётся в старом положении, поведение не меняется, новые таблицы и канал уезжают спящими. Робота включать некуда, пока не решено, на какой машине он живёт - МТС не пускает адреса дата-центров, это упирается в обращение 31074. В промте прописаны обязательные пред-полётные: журнал схемы для их миграции записью v9.31 - её нет вообще, а это то самое правило, из-за нарушения которого ночью столкнулись номера; пересборка списка исключений статанализа после сведения; проверка их нового теста на добавленный запрет выхода в интернет; ПЕРЕзапуск service_bypass_policies, иначе канал робота увидит ноль заданий молча. Приёмка - глазами в кабинете МТС, а не по журналу портала. Добавлены две находки для владельца, ничего не тронуто, и одиннадцать ловушек. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
607b192a57 |
docs выкат: промт следующей смене на выкат телеграм-рекламы на бой
Владелец решил катить. Промт написан под это, а не под хвосты. Главное, что вскрылось при подготовке: работа разошлась по ДВУМ веткам, и ни одна не содержит другую. У соседней ветки tg-zagolovok нет наших трёх правок тестов от 01.08, у нас нет их заголовка объявления. Обе от основной не отстают. Поэтому первый шаг смены - свести ветки, иначе выкатим половину. Выкат намеренно разрезан надвое. Портал можно катить сразу: переключатель остаётся в старом положении, поведение не меняется, новые таблицы и канал уезжают спящими. Робота включать некуда, пока не решено, на какой машине он живёт - МТС не пускает адреса дата-центров, это упирается в обращение 31074. В промте прописаны обязательные пред-полётные: журнал схемы для их миграции записью v9.31 - её нет вообще, а это то самое правило, из-за нарушения которого ночью столкнулись номера; пересборка списка исключений статанализа после сведения; проверка их нового теста на добавленный запрет выхода в интернет; ПЕРЕзапуск service_bypass_policies, иначе канал робота увидит ноль заданий молча. Приёмка - глазами в кабинете МТС, а не по журналу портала. Добавлены две находки для владельца, ничего не тронуто, и одиннадцать ловушек. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
1acbaf9383 |
Merge remote-tracking branch 'gitea/main' into feat/prospects-manual-testing-kp
# Conflicts: # docs/observer/STATUS.md |
||
|
|
325241c21d |
docs тесты: промт приведён в соответствие - хвосты Н1 и Х4 закрыты
Промт следующей смене врал в самом громком месте: хвост Н1 был записан как срочный - "похоже, протух боевой ключ Яндекс Директа". Это оказалось ложной тревогой, и написал её я сам, не проверив трёх фактов, опровергающих её за пять минут. Ключ в тесте подставной, локально боевого ключа нет вовсе, а в Яндекс тест ходил потому, что подбирал чужое задание из базы. Раздел Н1 переписан: пометка "закрыт, тревога ложная", разбор настоящей причины и урок про слово "срочно" в промте. Раздел Х4 помечен закрытым - тест внешних сервисов больше не зависит от сети. В Х2а вписан готовый датчик поиска файла, который гадит в базу, и ловушка про то, что искать уборку словом нельзя. В Х2б у "чужого писателя" появилось имя - параллельная смена в папке tg-zagolovok, она ходит в общую базу. Добавлены две находки для владельца, ничего не тронуто: ночная сборка слепка падает целиком от одного проекта без источника лидов, и начальник отдела при запросе всего отдела видит всех клиентов системы. В ловушки дописан запрет на выход в интернет из тестов с пояснением, почему его нельзя снимать. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
ab381954df |
docs тесты: промт приведён в соответствие - хвосты Н1 и Х4 закрыты
Промт следующей смене врал в самом громком месте: хвост Н1 был записан как срочный - "похоже, протух боевой ключ Яндекс Директа". Это оказалось ложной тревогой, и написал её я сам, не проверив трёх фактов, опровергающих её за пять минут. Ключ в тесте подставной, локально боевого ключа нет вовсе, а в Яндекс тест ходил потому, что подбирал чужое задание из базы. Раздел Н1 переписан: пометка "закрыт, тревога ложная", разбор настоящей причины и урок про слово "срочно" в промте. Раздел Х4 помечен закрытым - тест внешних сервисов больше не зависит от сети. В Х2а вписан готовый датчик поиска файла, который гадит в базу, и ловушка про то, что искать уборку словом нельзя. В Х2б у "чужого писателя" появилось имя - параллельная смена в папке tg-zagolovok, она ходит в общую базу. Добавлены две находки для владельца, ничего не тронуто: ночная сборка слепка падает целиком от одного проекта без источника лидов, и начальник отдела при запросе всего отдела видит всех клиентов системы. В ловушки дописан запрет на выход в интернет из тестов с пояснением, почему его нельзя снимать. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|
|
9ee4e9a79f |
feat(воронка продаж): корзина, дробь у «Отказа», фильтр по датам — экраны
- 12-я колонка «Корзина», результат «В корзину» просит только причину; - в шапке «Отказа» дробь «4/2» с подсказкой «из них 2 после ручного тестирования»; при нуле дробь не рисуется — «69/0» это шум; - у «Выслано КП» поле даты подписано «если договорились» и необязательно; - орган фильтра по датам на ОБОИХ экранах, два режима + период. Приёмка глазами пройдена живьём по всем семи пунктам, включая «Воронку отдела» (снимки в docs/superpowers/screens/2026-08-01-korzina-filtry/priemka/). Браузер поймал то, чего не видели тесты: при смене режима оставался прежний период, и «что менялось за завтра» давало пустую доску — теперь период возвращается к «Сегодня», на это заведён отдельный тест. Прогон: сервер 1245/1245, фронт 1567/1567. Журнал схемы — v9.31. |
||
|
|
d9f22d1353 |
fix тесты: убрана грязь между тестами и закрыт выход в интернет из прогона
Полный набор стал зелёным целиком: 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> |
||
|
|
7aa54773c1 |
merge: свёл заголовок объявления с веткой робота — воронка продаж и опрос в одной ветке
Влил 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> |
||
|
|
016416502a |
feat(воронка продаж): корзина, КП без обязательной даты, фильтр по датам — сервер
- результат «В корзину»: одна причина, дата созвона стирается, платящего не выбросить; - реклама на корзине встаёт СРАЗУ — явной веткой, а не случайно через unknown_stage; - у «Выслано КП» дата созвона стала необязательной: бывает «пришлите на почту, если интересно — перезвоню». Врущий старый тест на 422 без даты поправлен; - фильтр доски date_mode=todo|changed + период (добавлен вид «завтра»). «Надо сделать» — созвон в периоде ИЛИ просрочен, кроме отказа и корзины. «Менялось» — есть движение стадии ИЛИ запись разговора за период. Прогон: 1245/1245 (отдел продаж + все юнит-тесты). |
||
|
|
53ebcd9fe0 |
feat(воронка продаж): журнал движений карточки + место под корзину
Стадию меняют два разных места — результат разговора менеджера и автопереезд по деньгам. Джоба не оставляла следа вообще, поэтому «что менялось за день» было не из чего построить. Запись движения перенесена в событие модели: один шов на всех, включая любой будущий третий источник. - sales_prospect_moves — журнал всех движений (append-only, с GRANT'ами ролям); - prev_stage на карточке — откуда приехала (кормит счётчик «69/1» у «Отказа»); - stage += 'trash' — место под колонку «Корзина». Сторож проверен вырезанием: без записи движения краснеют 4 теста, в том числе тест джобы и тест API. |
||
|
|
32df332901 |
fix(телеграм-реклама): заголовок объявления для рекламы сайта — кампания больше не встаёт
Приёмка глазами вскрыла: кабинет МТС требует «Заголовок объявления» (до 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> |
||
|
|
2248cb7b79 |
docs(воронка продаж): промт для следующей сессии по корзине и фильтрам
Готовый текст, который владелец копирует в новую сессию: четыре куска работы, порядок (вопросы → спека → план → код), мины и грабли окружения. |