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>
This commit is contained in:
Дмитрий
2026-08-02 18:20:59 +03:00
parent 76cb44b91d
commit dbc79f15b3
2 changed files with 357 additions and 0 deletions
@@ -1,5 +1,10 @@
# Промт следующей смене — Пачка 4: единый каркас телеграм-экрана
> ⛔ **ОТРАБОТАН 02.08.2026 — работы больше не содержит.** Пачка 4 закрыта целиком, обе
> половины (коммиты `bcbbb25b0` и `76cb44b91`). Актуальный промт следующей смене —
> [2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md](2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md).
> Этот файл оставлен как снимок «что было до пачки 4» и разбор двух мин.
Поставлено 02.08.2026 ночью, после закрытия пачки 3. Всё ниже — живые замеры этой смены.
**Главный документ работы — [2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md](2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md).**
@@ -0,0 +1,352 @@
# Промт следующей смене — Пачка 5: приёмка глазами и выкат на боевой
Поставлено 02.08.2026 ночью, после закрытия пачки 4 (обеих половин). Всё ниже — живые замеры
этой смены, а не память.
**Главный документ работы — [2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md](2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md).**
Там замечания владельца дословно, требования МТС к медиа и разбор всех пяти пачек.
Этот промт — вход, состояние и работа одной смены.
---
## 🔴🔴🔴 Первое, что надо знать
**Песочница ВЫКЛЮЧЕНА 02.08.2026 в 07:40 МСК — телеграм-реклама на ЖИВЫХ ДЕНЬГАХ.**
Клиент жмёт «Запустить» → списывается его баланс → робот реально платит в кабинете МТС.
Откат: `TG_SANDBOX=true` в `.env` боевого → `sudo -u www-data php artisan config:cache`
`sudo systemctl restart liderra-queue`. Копия настроек:
`/home/ubuntu/deploy-backups/.env.bak-pered-vyhodom-iz-pesochnicy-20260802`.
Прочие красные линии: прод — только с явного «да» владельца, база по умолчанию **чтение**;
номера телефонов — ПДн, в задание роботу не класть; пуш **только в gitea** (GitHub мёртв);
владелец **не программист** — простым русским, короткими абзацами.
---
## Работа смены — Пачка 5
Две части: **сперва принять глазами, потом выкатить**. Между ними — «да» владельца.
### Часть 1. Приёмка глазами — обязательно ДО выката
Ни одна из пяти пачек глазами не принята. Тесты зелёные, но **зелёный тест не доказывает,
что экран выглядит и ведёт себя по-человечески** — за смену 30.07 этим уже обожглись
(15 объявлений лежали черновиками при 342 зелёных тестах).
🔴 **Демо-вход `admin@demo.local` не работает с 01.07** — тестовых учёток на бою нет, все
10 тенантов живые. **Доступ для приёмки просить у владельца.** Смотреть на локальном
`crm.bp-gr.ru` можно и нужно, но это не заменяет боевой.
Что смотреть на **телеграм-экране** (он переделан целиком):
1. **Карточка денег сверху.** Одна ли она? Не появилась ли рядом вторая, яндексовая?
Сумма человеческая («5 000 ₽», не «5000.00»)? Кнопка пополнения открывает пополнение
**общего** баланса, а не рекламного кошелька?
2. **Три вкладки** — «Мои кампании» / «Новая реклама» / «Авто-реклама». Открывается ли экран
на кампаниях? Не мигает ли содержимое при переключении?
3. **Карточки кампаний.** Похожи ли на яндексовые? Виден ли номер кампании рядом с текстом?
Не разъезжается ли карточка от длинного объявления? Цветной ярлык — того же цвета, что
у Яндекса для того же положения дел?
4. **Живой счётчик охвата** (шаг 1 мастера) — главное, что надо посмотреть живьём:
- не мигает ли, не «прыгает» ли число;
- успевает ли посчитать, пока человек печатает число дней (задержка 400 мс);
- что видно, пока считает, — крутилка, а не старое число;
- 🪤 **на своём списке номеров** счётчик не считает, пока список пуст — так задумано,
но глазами надо убедиться, что это не выглядит поломкой.
5. **Проход мастера целиком** на настоящем клиенте: четыре шага, «Назад»/«Дальше», сводка.
Особенно — **«Запустить» не нажимается, когда людей меньше 367**, и понятно ли объяснено.
6. **Шаг «Деньги»**: сходятся ли смета и остаток баланса с тем, что спишется на самом деле.
7. **Подсказки «?»** — как знак вопроса смотрится ВНУТРИ поля, особенно рядом с крестиком
очистки у «Картинка или видео». Тест доказал, что подсказка там есть; как она выглядит —
глазами не видел никто.
Что смотреть на **экране Яндекса** (его трогали только пачки 2 и 3):
- Ярлыки статусов — «Принято, показов пока нет» вместо зелёного «Крутится» при нуле;
приписка «часть объявлений отклонена — 2 из 15».
- Одинаково ли называются статусы на двух экранах, если открыть их подряд.
- Подсказки «?» в мастере Яндекса: период контактов, срок показа, частота, цена за тысячу.
**Нашёл беду — не чини молча.** Запиши в главный документ и скажи владельцу: часть находок
может оказаться «так и задумано».
### Часть 2. Выкат на боевой — только с отдельного «да» владельца
🔴 **В выкате есть изменение базы.** Миграция
`app/database/migrations/2026_08_02_000001_add_headline_and_media_to_client_tg_auto_rule.php`
добавляет в таблицу `client_tg_auto_rule` два столбца: `ad_headline` (40 знаков) и
`media_path` (500). Оба **необязательные**, старые строки не ломает, откат в миграции есть.
Номер журнала схемы — последний занятый **v9.64**.
🔴 **Полный overlay на боевой ЗАПРЕЩЁН — катить поимённым списком.** Вот он целиком, замер
`git diff --name-only 7922bffb~1..HEAD`, 24 файла (тесты не катятся):
**Сервер (PHP), 10 файлов:**
```text
app/app/Http/Controllers/Api/AdvertisingCampaignController.php
app/app/Http/Controllers/Api/ClientTg/AutoRuleController.php
app/app/Http/Controllers/Api/ClientTg/CampaignController.php
app/app/Models/ClientTg/AutoRule.php
app/app/Rules/ClientTg/MtsMedia.php
app/app/Services/ClientTg/TelegramAudienceService.php
app/app/Services/ClientTg/TelegramAutoAccumulator.php
app/app/Support/Mp4Probe.php
app/routes/web.php
app/database/migrations/2026_08_02_000001_add_headline_and_media_to_client_tg_auto_rule.php
```
**Экраны (исходники, собираются на бою), 12 файлов:**
```text
app/resources/js/api/advertising.ts
app/resources/js/api/telegram.ts
app/resources/js/components/advertising/AdWalletHeader.vue
app/resources/js/components/advertising/CampaignList.vue
app/resources/js/components/advertising/CampaignReportDialog.vue
app/resources/js/components/advertising/CampaignWizard.vue
app/resources/js/components/advertising/TelegramBalanceHeader.vue
app/resources/js/composables/campaignStatusMeta.ts
app/resources/js/composables/podskazkiReklamy.ts
app/resources/js/views/advertising/AdvertisingTelegramView.vue
app/resources/js/views/advertising/TelegramAutoRulePanel.vue
app/resources/js/views/advertising/TelegramCampaignWizard.vue
app/resources/js/views/advertising/telegramStatusLabels.ts
```
**`app/phpstan-baseline.neon` на боевой НЕ нужен** — он для разработки. В списке выше его нет
намеренно, хотя в diff он есть.
Безопасный порядок (проверен прошлыми выкатами):
1. Позвать агента `prod-deploy-validator` — он даёт вердикт ГО/НЕ ГО и ничего не меняет.
2. Бэкап базы **до** миграции.
3. Залить файлы поимённо → `php artisan migrate` → собрать фронт.
4. Фронт собирается прямо на боевом (node 20.20.2, ~2,4 с). Собрать в `public/build-new`,
проверить, подменить `mv`, потом `chown -R www-data:www-data`.
5. 🔴 `config:cache` / `optimize`**от `www-data`, НЕ от root**. От root портал ложился на
18 минут.
6. Перезапустить очередь: `sudo systemctl restart liderra-queue`.
7. Пройти телеграм-экран глазами ещё раз — уже на бою.
**Новая ручка оценки прав в базе не требует.** `POST /api/telegram/campaigns/estimate` читает
строго то же, что уже читает создание кампании (сделки, свою базу, отписки), и **не пишет
никуда**. Мина «на бою роль не смогла записать» тут не работает — записи нет вовсе.
🪤 **Но проверить srv_bypass всё равно стоит**, если миграция добавит что-то новое под RLS:
новым RLS-таблицам мало `tenant_isolation` + GRANT, надо **пере**запускать
`db/03_service_bypass_policies.sql`. Здесь новых таблиц нет — только два столбца в
существующей, — так что скорее всего не понадобится. Но глазами свериться дешевле, чем
получить тихую правку нуля строк.
### Чем кончается смена
Либо «принято глазами, выкачено, вот что смотрел» — и тогда обновить `ПИЛОТ.md` и написать
промт следующей смене. Либо честное «нашёл вот это, дальше не пошёл».
---
## Что уже сделано — пять пачек, все в gitea, ни одна НЕ на боевой
### ✅ Пачка 1 — коммит `7922bffb6`
Заголовок объявления и картинка в **авто-правиле** (раньше их не было вовсе — авто со ссылкой
на сайт сгорело бы в кабинете уже после списания денег); проверка медиа по настоящим
требованиям МТС (`app/app/Rules/ClientTg/MtsMedia.php`, длительность и кадр видео читает
`app/app/Support/Mp4Probe.php` без внешних программ); цена показа с медиа показана ДО загрузки
файла; накопитель не запускает пачку без заголовка при рекламе сайта.
Граница честности: длительность и кадр читаются только у mp4/mov/m4v; у webm/mkv/mpeg/wmv
проверяются формат и вес.
### ✅ Пачка 2 — коммит `a2496dfe6`
Замечание З-6: «почему статус крутится, когда она отклонена?» Сервер отдаёт
`delivered_impressions` и счётчики объявлений одним запросом (на N+1 стоит датчик); ярлык
считается по фактам: `running` при нуле показов → «Принято, показов пока нет», часть отклонена
→ «часть объявлений отклонена — 2 из 15».
🔴 **Правило перехода статусов НЕ тронуто** — на `rejected` висит возврат заморозки
(`AdWalletService::release`, «ВЫХОД 2»). Трогать его нельзя.
### ✅ Пачка 3 — коммит `6ae6457b3`
Подсказки «?» у **16 полей**. Тексты — один файл `composables/podskazkiReklamy.ts` (теперь их
14: в пачке 4а добавилась подсказка про общий баланс). На очевидные поля подсказки нет
**намеренно**, и на это стоит отдельный тест. Язык стерегут датчики: ни одной латинской буквы,
запрет жаргона, длина 40–200 знаков, два-три предложения.
### ✅ Пачка 4а — коммит `bcbbb25b0` — каркас
Вкладки, карточка денег сверху, панель авто-рекламы в своей вкладке, кампании карточками с
цветным ярлыком. После запуска экран сам возвращает к списку.
🔴 **Карточка денег — ОТДЕЛЬНЫЙ компонент `TelegramBalanceHeader.vue`, не яндексовая шапка.**
Экраны берут деньги из разных мест: Яндекс — из рекламного кошелька (`ad_wallets`), телеграм —
из **общего баланса** (`tenants.balance_rub`, решение владельца, коммит `6f469fb1`). Показать
одни деньги, а списать другие — хуже, чем некрасивый экран.
🔴 **Слова «Заморожено» на телеграм-карточке нет намеренно** — сервер отдаёт заморозку жёстким
нулём, и вечный ноль клиент прочитал бы как «деньги не резервируются».
### ✅ Пачка 4б — коммит `76cb44b91` — мастер и счётчик
Мастер из четырёх шагов (Кому показываем → Объявление → Деньги → Проверка и отправка), живой
счётчик охвата, **кнопки «Рассчитать» больше нет**. Смета и остаток баланса на шаге «Деньги».
🔴 **Заведена новая серверная ручка `POST /api/telegram/campaigns/estimate`.** Живой счётчик
считает на каждое изменение поля, а охват до сих пор возвращала только `POST /campaigns` — и
она **создаёт черновик в базе**. Новая ручка **только читает**. Считает тем же кодом, что и
создание кампании; отдельный тест сверяет два ответа между собой.
🔴 **Запуск запрещён, когда людей меньше 367** — правило площадки. Тот же предохранитель стоит
на сервере (`AudienceGateTest`).
---
## Где всё стоит — замер 02.08 ночью
**`main` = ветка `fix/tg-zagolovok-obyavleniya` = `76cb44b91`**, неотправленного 0,
рабочая папка чистая (кроме машинного `docs/observer/STATUS.md`).
### Боевой — замеры прошлой смены, перепроверить командой
- Заголовок объявления на разовой форме доехал (выкат 02.08 днём), приёмка глазами пройдена.
- 🟡 **Откат того выката ещё лежит:** каталог `public/build-old-zagolovok` и архив
`/tmp/front-backup-2026-08-02-zagolovok.tgz`. Сутки прошли — можно убирать.
- 🔴 **Пачки 1, 2, 3, 4а и 4б на боевой НЕ поехали.**
- Телеграм: 7 таблиц `client_tg_*`, `TG_ROBOT_TRANSPORT=poll`, **`TG_SANDBOX=false`**.
### Робот
Живёт на машине владельца: `C:\liderra\mts-telegram-robot`, свой Node 20
(`C:\liderra\node20\node.exe`), профиль браузера `C:\liderra\mts-bot-profile`. Стучится в
портал раз в минуту. Баланс МТС **5 010 ₽** — фактический потолок трат.
⛔ Не ставить робота на сервер и не покупать прокси — обе дороги закрыты навсегда.
### Тесты и сторожа — замеры ЭТОЙ смены
- Экраны: `cd app && npx vitest run`**240 файлов, 1829 тестов, 0 падений**, 3 пропущено,
~3 минуты.
- Реклама Яндекса: `cd app && DB_DATABASE=liderra_testing_zag php artisan test --filter=Advertising`
**435, 0 падений**.
- Телеграм-модуль: то же с `--filter=ClientTg`**292, 0 падений**.
- Статанализ: `cd app && composer stan`**0**. Формат: `composer pint -- --dirty` — чисто.
- Проверка типов: `npm run type-check`**6 ошибок в чужих файлах** (автоподбор, проекты,
старые спеки рекламы), они были до нас. Смотреть, не добавилось ли **своего** файла.
- Робот: `cd bots/mts-telegram-ads && npm test`
🪤 Гонять из папки `app`, иначе «Could not open input file».
---
## Опорные точки — проверены поимённо этой сменой
Метки, по которым тесты находят живое. Ломать их = ломать проверки без пользы:
`tg-tabs`, `tg-tab-campaigns`, `tg-tab-new`, `tg-tab-auto`, `tg-balance-header`,
`tg-balance-value`, `tg-balance-topup`, `tg-campaigns`, `tg-campaign-row`,
`tg-campaign-status-chip`, `tg-campaign-new-btn`, `tg-wizard`, `tg-wizard-steps`,
`tg-wizard-step-1…4`, `tg-reach`, `tg-reach-malo`, `tg-smeta`, `tg-svodka`, `tg-launch`,
`tg-ad-text`, `tg-ad-link`, `tg-ad-headline`, `tg-audience-days`, `tg-phones`,
`tg-budget-cap`, `tg-ord`, `tg-media`, `tg-auto-panel`, `tg-auto-*`, `tg-resubmit-*`,
`help-hint`.
🔴 **`tg-calc` и `tg-estimate` больше НЕ существуют** — проверено, ноль вхождений. Кнопку
«Рассчитать» убрала пачка 4б, охват теперь живой. Если увидишь их в чужом старом тексте —
это устаревший источник.
---
## Что остаётся за владельцем
1. 🔴 **Стена площадки: минимум 367 номеров**, у самого крупного клиента 112. Из сделок порог
не набрать — но клиент может загрузить **свой список**, и тогда пойдут живые деньги.
2. **Чтение вердикта модерации (`read-status`) не проверено ни разу.** Вскроется на первом
живом запуске: не прочитает робот ответ МТС — кампания провисит 48 часов и уйдёт в «нужен
разбор», деньги при этом не вернутся (реклама могла показываться).
3. **Токен робота засветился в выводе команды** 02.08 (в файлы и коммиты не попал).
Перевыпустить, когда будет удобно.
4. **Решить про выкат пяти пачек** — в первой изменение базы.
---
## ⏸ Отложено владельцем — НЕ поднимать
**Портал не хранит числа показов телеграм-кампании** (колонка `actual_cost_rub` есть, но её не
пишет ни одна строка кода; доставленных показов в таблице нет вовсе). Владелец 02.08:
«показывает, надо только запустить и начнёт показывать — не обращай внимание пока».
По своей инициативе не трогать.
---
## Ловушки
### Правка кода
- 🪤 **Причёсывалка (`prettier --write`) переписывает ВЕСЬ файл**, если он не был причёсан до
тебя. 02.08 телеграм-экран так дал 296 изменённых строк вместо 24. Датчик: сравнить
`git diff --stat` и `git diff --stat -w`; разошлись — почти всё пробелы. Лечение:
`git checkout HEAD -- <файл>` и применить только свою правку руками.
- 🪤 Проверять, был ли файл причёсан ДО тебя, надо **внутри `app/`**:
`cd app && git show HEAD:app/<путь> > proverka.vue && npx prettier --check proverka.vue && rm proverka.vue`.
Во временной папке нет `app/.prettierrc.json` — причёсывалка меряет чужой меркой и врёт.
- 🪤 **Датчик пробелов ложно срабатывает на переезде кусков кода.** Если строки удалены в
одном месте и добавлены в другом, `-w` спарит их иначе, и счётчики разойдутся на 2–3
строки. Прежде чем чинить — посмотреть diff обоих датчиков глазами; переезд это не шум.
- 🪤 **Итоги группировки — не модели.** `groupBy` + `count(...) as total` через Eloquent даёт
строки, притворяющиеся моделью, и статанализ ругается. Лечение — `->toBase()` перед `->get()`.
- 🪤 **Тесты экранов лежат в `app/tests/Frontend/`**, а не рядом с кодом. Их 240 файлов.
- 🪤 **Имён колонок и таблиц наизусть не помнить.** Кампании Яндекса — `ad_campaigns`,
объявления — `ad_campaign_banners`, правило авто-телеграма — `client_tg_auto_rule`
(единственное число!), рекламный кошелёк — `ad_wallets`. Сперва `information_schema`.
- 🪤 **Переменные оболочки в путях временных файлов** дали мусор `app/$out` и `app/$TEMP/`
прямо в рабочей папке. Писать временное в каталог-черновик сессии.
- 🪤 **Резать куски файла скриптом по номерам строк — опасно.** 02.08 так пропал открывающий
тег `<v-tabs-window>`: номера строк успели сдвинуться между замером и правкой. Резать
через Edit по точному тексту; если всё же скриптом — сразу перечитать место глазами.
### Тесты
- 🪤 **Тест, который ищет исчезнувшее поле, зеленеет по ложной причине.** 02.08 проверка «на
„Тексте объявления" подсказки НЕТ» прошла на экране, где этого поля не стало вовсе:
подсказки «нет» и у того, чего нет. Лечение — сперва убедиться, что поле на экране есть.
- 🪤 **Переносишь код — переноси проверки, а не выбрасывай.** Сперва переписать их на новом
месте и там прогнать, и только потом убирать со старого. Иначе «14 тестов удалено» ничем
не отличается от подгонки под зелёное.
- 🪤 **После НОВОГО тестового файла на PHP статанализ краснеет** — шум `$this` в Pest,
гасится `app/phpstan-baseline.neon`. Пересобрать:
`php vendor/bin/phpstan analyse --memory-limit=512M --generate-baseline`, потом **глазами**
сверить diff: должны добавиться только твои файлы, удалений быть не должно.
### Гит и сторожа
- 🪤 **Номер записи в `db/CHANGELOG_schema.md` брать как максимум по ВСЕМ веткам плюс один.**
Последняя занятая — **v9.64**.
- 🪤 **Коммитить явными путями.** Машинный `docs/observer/STATUS.md` переписывается хуком
после каждого коммита и молча уезжает в следующий. Снимать `git restore --staged`.
- 🪤 **Штатно падают два сторожа, оба не на нашей работе:** `gitleaks-full-history` и
`lychee-links`. Отключать на **один** пуш:
`LEFTHOOK_EXCLUDE=gitleaks-full-history,lychee-links git push …`.
- 🪤 Один «Errors: 1» в прогоне экранов приходит из `AutopodborManualCompetitorError.spec.ts`
— оборванный сетевой запрос в чужом наборе, был до нас.
- 🪤 Многострочное сообщение коммита — heredoc `-F -`. Коммит с хуками идёт ~12–41 секунду
(с PHP-файлами дольше: добавляются pint, larastan, deptrac).
### Боевой
- 🔴 **Заливку скрипта и его запуск не соединять** одной командой — виснет на две минуты.
Два отдельных вызова: залить → выполнить → прочитать файл результата.
- 🔴 **Вывод писать в файл** (`file_put_contents`); `echo` из tinker не доезжает.
**Нет файла ⇒ скрипт УПАЛ**, а не «данных нет».
- 🔴 **`config:cache` / `optimize` — от `www-data`, НЕ от root.**
- 🔴 **Полный overlay ЗАПРЕЩЁН** — катить поимённым списком (он выше).
- 🪤 `sudo -u postgres psql` на боевой машине — **СТАРАЯ копия**. Живая база — Managed PG.
- 🪤 Демо-вход `admin@demo.local` **не работает с 01.07** — для приёмки глазами просить
доступ у владельца.
---
Работа идёт **пачками**: после каждой владелец делает компакт. Не хвататься за всё сразу.