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>
This commit is contained in:
Дмитрий
2026-08-02 16:14:54 +03:00
parent 6ae6457b34
commit 4eac8d8ff1
2 changed files with 313 additions and 4 deletions
@@ -1,9 +1,11 @@
# Промт следующей смене — Пачка 3: подсказки «?» на рекламных экранах
> ✅ **Работа этого промта ВЫПОЛНЕНА 02.08.2026** — подсказки стоят у 16 полей, тесты
> зелёные, на боевой не выкачено и глазами не принято. Итог — в разделе «Пачка 3» файла
> замечаний. Разделы «Что уже сделано», «Ловушки» и «Где всё стоит» ниже остаются
> действующими для пачки 4; задание в разделе «Работа смены» — уже история.
> ✅ **Работа этого промта ВЫПОЛНЕНА 02.08.2026** (коммит `6ae6457b3`) — подсказки стоят у
> 16 полей, тесты зелёные, на боевой не выкачено и глазами не принято. Итог — в разделе
> «Пачка 3» файла замечаний.
>
> 👉 **Действующий промт — [2026-08-05-PROMT-karkas-telegram-ekrana.md](2026-08-05-PROMT-karkas-telegram-ekrana.md)** (пачка 4).
> Этот файл держим только ради истории; замеры в нём устарели.
Поставлено 02.08.2026 поздним вечером, после закрытия пачки 2. Всё ниже — живые замеры.
@@ -0,0 +1,307 @@
# Промт следующей смене — Пачка 4: единый каркас телеграм-экрана
Поставлено 02.08.2026 ночью, после закрытия пачки 3. Всё ниже — живые замеры этой смены.
**Главный документ работы — [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 мёртв);
владелец **не программист** — простым русским, короткими абзацами.
---
## Работа смены — Пачка 4
Замечание владельца: два рекламных экрана выглядят как сделанные разными людьми. Яндекс —
опрятный кабинет, Телеграм — простыня на одной странице.
### Как устроены оба экрана сейчас — замер
**Яндекс** `AdvertisingYandexView.vue`**97 строк**, чистая обёртка:
```text
<h1>Яндекс Аудитория> → <AdWalletHeader/> → <v-tabs> «Мои кампании» | «Новая реклама»
→ <v-tabs-window> CampaignList | CampaignWizard
```
**Телеграм** `AdvertisingTelegramView.vue`**608 строк** простыни. Ориентиры на 02.08 ночь:
| Строка | Что там |
|---|---|
| 264 | `<h1>Реклама в Телеграме по своей базе</h1>` |
| 284 | `<TelegramAutoRulePanel />` — панель авто-рекламы вклинена ПЕРЕД формой |
| 288–289 | карточка «Новая кампания», деньги строкой подзаголовка: «Общий баланс: X ₽» |
| 430441 | кнопки `tg-calc` «Рассчитать» и `tg-launch` |
| 452–465 | «Мои кампании»: `tg-campaigns`, строки `tg-campaign-row` |
Вкладок нет, карточки денег нет, мастера шагов нет, кампании нарисованы простым списком
(`v-list-item`: заголовок — текст объявления, подзаголовок — статус и число кандидатов),
а у Яндекса — карточки с ярлыком, припиской и кнопками.
### 🔴 Мина первая: деньги у экранов РАЗНЫЕ
Проверено по коду, не по памяти:
- **Яндекс** берёт из рекламного кошелька — таблица `ad_wallets`, поля `balance_rub` /
`frozen_rub`, свободно = баланс − заморожено (`AdvertisingWalletController.php:34`).
- **Телеграм** берёт **общий баланс тенанта**`tenants.balance_rub`, а `frozen_rub`
отдаётся жёстко `'0.00'` (`Api/ClientTg/CampaignController.php:59-60`).
**Просто вставить `<AdWalletHeader />` в телеграм-экран НЕЛЬЗЯ** — он покажет клиенту деньги
рекламного кошелька Яндекса, а спишутся другие. Это враньё дороже, чем некрасивый экран.
Это не поломка: телеграм платит с общего баланса **по решению владельца** (коммит `6f469fb1`
«деньги на общем балансе»). Значит задача — **тот же вид при своих данных**: либо у
`AdWalletHeader` появляется вход «какой кошелёк показывать», либо рядом живёт свой такой же
на вид компонент. Обе дороги честные; первая меньше кода, вторая меньше риска задеть Яндекс.
Слова «Заморожено» на телеграм-карточке при жёстком нуле лучше не показывать вовсе, чем
показывать всегда ноль — иначе клиент решит, что деньги не резервируются, и это тоже враньё.
### 🔴 Мина вторая: живого счётчика охвата без сервера не будет
Владелец просил «живой счётчик охвата вместо кнопки Рассчитать». У Яндекса он работает
отдельной ручкой `GET /campaigns/{id}/audience-size` (`web.php:453`) — мастер дёргает её с
задержкой на каждое изменение поля.
**У телеграма такой ручки нет.** Все ручки модуля — восемь, вот они целиком
(`routes/web.php:101-112`): список кампаний, создать, показать, запустить, отменить,
пересдать, приложить медиа, плюс три на авто-правило. Охват возвращает **только**
`POST /campaigns`а она **создаёт черновик в базе**.
Значит дёргать её на каждый чих нельзя — базу засорит черновиками. Нужна **новая серверная
ручка оценки** (посчитать охват и смету, ничего не записывая). Это работа с бэкендом:
контроллер, маршрут, тесты Pest, права роли.
### Совет по объёму: разбить пачку 4 надвое
Работа заявлена как одна, но половины разные по цене и риску. Рекомендация — сделать так и
показать владельцу после первой половины:
- **4а — чистый фронт, без сервера:** вкладки «Мои кампании» / «Новая реклама», карточка
денег сверху в яндексовом виде (со своими данными, см. мину 1), панель авто-рекламы —
внутрь вкладки, а не поперёк экрана, карточки кампаний в одном виде с яндексовыми.
- **4б — мастер шагов + живой счётчик:** требует новой ручки оценки на сервере и переделки
двухшагового потока «Рассчитать → Запустить» (спека §5, комментарии в коде на строках
47, 140, 181).
Если браться за всё сразу — смена кончится посередине, а экран останется разобранным.
### Чем платим: тесты придётся править
На нынешнюю разметку телеграм-экрана завязаны **45 тестов**: `advertising-telegram-view`
(25), `telegram-auto-rule-panel` (12), `podskazki-na-ekranah` (8, из них 4 про телеграм).
Переделка каркаса их сломает — это нормально и ожидаемо, но заложи время.
🪤 **Правь тест только когда поменялся смысл, а не чтобы он позеленел.** Если тест ждёт
«кнопка Рассчитать есть», а мы её убрали — тест переписывается честно. Если тест ждёт
«запуск списывает деньги», а он покраснел — сломали дело, а не тест.
### Опорные точки, которые нельзя потерять
Метки, по которым тесты находят живое (ломать их = ломать проверки без пользы):
`tg-ad-text`, `tg-ad-link`, `tg-ad-headline`, `tg-audience-days`, `tg-phones`, `tg-budget-cap`,
`tg-ord`, `tg-media`, `tg-calc`, `tg-launch`, `tg-campaigns`, `tg-campaign-row`,
`tg-resubmit-*`, `tg-auto-*`, и `help-hint` — подсказки из пачки 3.
### Чем кончается смена
Либо «экраны похожи как два кабинета одной системы — вот что сделано, вот что осталось»,
либо честное «не успел, вот где остановился». Приёмка глазами — всё равно пачка 5.
---
## Что уже сделано
### ✅ Пачка 1 — коммит `7922bffb` (на боевой НЕ выкачена)
Заголовок объявления и картинка в **авто-правиле** (раньше их не было вовсе — авто со
ссылкой на сайт сгорело бы в кабинете уже после списания денег); проверка медиа по настоящим
требованиям МТС (`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»). Трогать его нельзя и в пачке 4;
- подписи собраны в один файл `composables/campaignStatusMeta.ts` (было ДВЕ копии, и они
уже разъехались); телеграмные — в `views/advertising/telegramStatusLabels.ts`.
### ✅ Пачка 3 — коммит `6ae6457b3` (на боевой НЕ выкачена)
Подсказки «?» у **16 полей**: телеграм-форма (дни сделок, лимит на объявление, ОРД,
картинка/видео), форма пересдачи (ОРД), панель авто-рекламы (порог 367, бюджет, дневной
лимит), мастер Яндекса (оба конца периода, срок показа, частота — на шаге и в сводке, цена
за тысячу), кошелёк (заморожено, свободно).
- тексты — **один файл** `composables/podskazkiReklamy.ts`, 13 штук; поля ОРД и «Частота
показов» стоят в разметке дважды, и копий текста больше нет по построению;
- про ОРД написано словами владельца из ответа З-4, дословно;
- на очевидные поля («Текст объявления», «Ссылка», «Кошелёк») подсказки **нет намеренно**,
и на это стоит отдельный тест — чтобы следующая смена не навешала шума;
- язык стерегут датчики: ни одной латинской буквы, запрет жаргона, длина 40–200 знаков,
два-три предложения.
🪤 **Подсказку в поле ввода вешать слотом `#append-inner`, а не `#label`** — метка у Vuetify
плавающая, при щелчке уезжает вверх и знак вопроса уехал бы с ней. Готовых образцов
«подсказка внутри поля» в проекте не было: все прежние стояли у чекбоксов и заголовков.
---
## Что дальше (пачка 5)
**Приёмка глазами обоих экранов подряд + выкат на боевой.** Смотреть в первую очередь:
- как «?» смотрится внутри поля рядом с крестиком очистки у «Картинка или видео»
(тест доказал, что подсказка там есть, но как она выглядит — глазами не видел никто);
- одинаково ли называются статусы на двух экранах;
- не появилось ли двух карточек денег на телеграм-экране после пачки 4.
---
## ⏸ Отложено владельцем — НЕ поднимать
**Портал не хранит числа показов телеграм-кампании** (колонка `actual_cost_rub` есть, но её
не пишет ни одна строка кода; доставленных показов в таблице нет вовсе). Владелец 02.08:
«показывает, надо только запустить и начнёт показывать — не обращай внимание пока».
В пачки 4–5 не входит, по своей инициативе не трогать.
---
## Где всё стоит — замер 02.08 ночью
**`main` = ветка `fix/tg-zagolovok-obyavleniya` = `6ae6457b3`**, неотправленного 0,
рабочая папка чистая.
### Боевой
- Заголовок объявления на разовой форме доехал (выкат 02.08 днём), приёмка глазами пройдена.
- 🟡 **Откат того выката ещё лежит:** каталог `public/build-old-zagolovok` и архив
`/tmp/front-backup-2026-08-02-zagolovok.tgz`. Сутки прошли — можно убирать.
- 🔴 **Пачки 1, 2 и 3 на боевой не поехали.** В пачке 1 миграция (`client_tg_auto_rule`:
`ad_headline`, `media_path`) — выкат только с отдельного «да» владельца.
- Фронтенд собирается прямо на боевом (node 20.20.2, ~2,4 с). Безопасный порядок: собрать
в `public/build-new`, проверить, подменить `mv`, потом `chown -R www-data:www-data`.
- Телеграм: 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 ₽** — фактический потолок трат.
⛔ Не ставить робота на сервер и не покупать прокси — обе дороги закрыты навсегда.
### Тесты и сторожа
- Экраны (их и трогает пачка 4): `cd app && npx vitest run`**1787 тестов, 0 падений**,
237 файлов, 3 пропущено, ~2,5 минуты. Замер этой смены.
- Реклама Яндекса: `cd app && DB_DATABASE=liderra_testing_zag php artisan test --filter=Advertising`
**435, 0 падений**. Телеграм-модуль: то же с `--filter=ClientTg`**281, 0 падений**.
Замер вечера 02.08; PHP в пачке 3 не трогали.
- Статанализ: `cd app && composer stan`**0**. Формат: `composer pint -- --dirty`.
- Проверка типов: `npm run type-check`**7 ошибок в чужих файлах** (автоподбор, проекты,
старые спеки рекламы), они были до нас. Смотреть, не добавилось ли **своего** файла.
- Робот: `cd bots/mts-telegram-ads && npm test`
🪤 Гонять из папки `app`, иначе «Could not open input file».
---
## Что остаётся за владельцем
1. 🔴 **Стена площадки: минимум 367 номеров**, у самого крупного клиента 112. Из сделок
порог не набрать — но клиент может загрузить **свой список**, и тогда пойдут живые деньги.
2. **Чтение вердикта модерации (`read-status`) не проверено ни разу.** Вскроется на первом
живом запуске: не прочитает робот ответ МТС — кампания провисит 48 часов и уйдёт в
«нужен разбор», деньги при этом не вернутся (реклама могла показываться).
3. **Токен робота засветился в выводе команды** 02.08 (в файлы и коммиты не попал).
Перевыпустить, когда будет удобно.
4. **Пачки 1, 2 и 3 надо принять глазами и решить про выкат.**
---
## Ловушки
### Правка кода
- 🪤 **Причёсывалка (`prettier --write`) переписывает ВЕСЬ файл**, если он не был причёсан
до тебя. 02.08 телеграм-экран так дал 296 изменённых строк вместо 24 — 250 из них чужое
форматирование, за которым пропадает своя правка. Датчик: сравнить `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` — причёсывалка меряет чужой меркой и врёт,
что не причёсано вообще всё. Замер 02.08: телеграм-экран, панель авто и мастер Яндекса
**не причёсаны**, кошелёк — причёсан.
- 🪤 **Итоги группировки — не модели.** Запрос с `groupBy` + `count(...) as total` через
модель Eloquent даёт строки, притворяющиеся моделью, и статанализ ругается на
несуществующие свойства. Лечение — `->toBase()` перед `->get()`.
- 🪤 **Тесты экранов лежат в `app/tests/Frontend/`**, а не рядом с кодом. Их 237 файлов.
- 🪤 **Имён колонок и таблиц наизусть не помнить.** Кампании Яндекса — `ad_campaigns`,
объявления — `ad_campaign_banners`, правило авто-телеграма — `client_tg_auto_rule`
(единственное число!), рекламный кошелёк — `ad_wallets`. Сперва `information_schema`.
- 🪤 **Переменные оболочки в путях временных файлов** дали мусор `app/$out` и `app/$TEMP/`
прямо в рабочей папке. Писать временное в каталог-черновик сессии, а не в `app/`.
### Гит и сторожа
- 🪤 **После НОВОГО тестового файла на PHP статанализ краснеет** — это шум `$this` в Pest,
гасится `app/phpstan-baseline.neon`. Пересобрать:
`php vendor/bin/phpstan analyse --memory-limit=512M --generate-baseline`, потом **глазами**
сверить diff: должны добавиться только твои файлы, удалений быть не должно.
- 🪤 **Номер записи в `db/CHANGELOG_schema.md` брать как максимум по ВСЕМ веткам плюс один.**
Последняя занятая — **v9.64** (пачка 1). Пачка 4а схему трогать не должна; 4б — только
если ручке оценки понадобится что-то в базе (скорее всего нет).
- 🪤 **Коммитить явными путями.** Машинный `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 секунд.
### Боевой
- 🔴 **Заливку скрипта и его запуск не соединять** одной командой — виснет на две минуты.
Два отдельных вызова: залить → выполнить → прочитать файл результата.
- 🔴 **Вывод писать в файл** (`file_put_contents`); `echo` из tinker не доезжает.
**Нет файла ⇒ скрипт УПАЛ**, а не «данных нет».
- 🔴 **`config:cache` / `optimize` — от `www-data`, НЕ от root.** От root портал ложился на 18 минут.
- 🔴 **Полный overlay на боевой ЗАПРЕЩЁН** — катить поимённым списком файлов.
- 🪤 `sudo -u postgres psql` на боевой машине — **СТАРАЯ копия**. Живая база — Managed PG.
- 🪤 Демо-вход `admin@demo.local` **не работает с 01.07** — тестовых учёток на бою нет, все
10 тенантов живые. Для приёмки глазами просить доступ у владельца.
---
Работа идёт **пачками**: после каждой владелец делает компакт. Не хвататься за всё сразу.