diff --git a/docs/superpowers/2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md b/docs/superpowers/2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md index 220b02e2..a59fecee 100644 --- a/docs/superpowers/2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md +++ b/docs/superpowers/2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md @@ -1,5 +1,12 @@ # Промт следующей смене — Пачка 5: приёмка глазами и выкат на боевой +> ⚠️ **ОТРАБОТАН НАПОЛОВИНУ.** Приёмка глазами (Часть 1) пройдена 02.08.2026 — она вскрыла +> четыре находки, три из них требуют решения владельца. Выкат (Часть 2) **не сделан** и +> заблокирован находкой про «Мою базу номеров». +> +> 🔴 **Новой смене читать [2026-08-07-PROMT-resheniya-vladelca-i-vykat.md](2026-08-07-PROMT-resheniya-vladelca-i-vykat.md).** +> Здесь остаётся в силе только **поимённый список выката** (Часть 2) — он выверен и дополнен. + Поставлено 02.08.2026 ночью, после закрытия пачки 4 (обеих половин). Всё ниже — живые замеры этой смены, а не память. diff --git a/docs/superpowers/2026-08-07-PROMT-resheniya-vladelca-i-vykat.md b/docs/superpowers/2026-08-07-PROMT-resheniya-vladelca-i-vykat.md new file mode 100644 index 00000000..1d0a29c6 --- /dev/null +++ b/docs/superpowers/2026-08-07-PROMT-resheniya-vladelca-i-vykat.md @@ -0,0 +1,354 @@ +# Промт следующей смене — решения владельца по четырём находкам, потом выкат + +Поставлено 02.08.2026 поздно вечером, после приёмки глазами (пачка 5). Всё ниже — живые замеры +этой смены, а не память. + +**Главный документ работы — [2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md](2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md).** +Там замечания владельца дословно, требования МТС к медиа, разбор всех пяти пачек и **полный +разбор четырёх находок приёмки** (раздел «Пачка 5»). Этот промт — вход, состояние и работа +одной смены. + +Прежний промт [2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md](2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md) +**отработан наполовину**: приёмка глазами пройдена, выкат — нет. Поимённый список выката +живёт там же и остаётся в силе (24 файла). + +--- + +## 🔴🔴🔴 Первое, что надо знать + +**Песочница ВЫКЛЮЧЕНА 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 мёртв); +владелец **не программист** — простым русским, короткими абзацами. + +--- + +## Работа смены + +**Порядок жёсткий: сперва спросить владельца по четырём находкам, потом чинить, потом +выкат.** Не начинать с выката: одна из находок делает выкат в нынешнем виде вредным. + +### Часть 1. Четыре находки — спросить владельца в начале смены + +Все четыре нашлись 02.08 при **приёмке глазами**, три из них — прямо из вопросов владельца, +глядя на живой экран. Ни одну не увидели 1832 теста экранов и 292 теста телеграм-модуля. +Полный разбор с числами, таблицами и ссылками на строки кода — в главном документе. + +#### Находка 1 — значки: ✅ УЖЕ ПОЧИНЕНО, решения не требует + +43 имени значков из 162 молча подменялись на вопрос в кружке. На рекламных экранах их было 20 +(включая заголовок телеграм-экрана и ярлыки статусов) — **все 20 починены**, заведён сторож +`app/tests/Frontend/znachki-est-v-karte.spec.ts`. + +**Осталось 23** сломанных имени на экранах вне рекламы (личный кабинет, отдел продаж, проекты, +счета) — записаны в стороже списком известного долга. **Спросить владельца:** чинить ли их +отдельной пачкой. Работа механическая, риск низкий, но трогает экраны, которых эта задача не +касалась. + +#### 🔴 Находка 2 — «Моя база номеров» ведёт в никуда — ЭТО БЛОКИРУЕТ ВЫКАТ + +Пункт читает таблицу `client_tg_contacts`, а в неё **не пишет ни одна строка кода**: ни экрана +загрузки, ни серверной ручки (в телеграм-модуле их 10, `routes/web.php:100–116`), ни переноса +из сделок. У **любого** клиента пункт всегда покажет ноль. + +Рабочих путей из трёх — два: «Свежие сделки за период» и «Свой список номеров». + +**Рекомендуемый ответ: убрать пункт с экрана** — быстро, честно, ничего не обещает впустую. +Загрузку базы сделать потом отдельной пачкой, если она вообще нужна. + +🔴 **Пока не решено — на боевой это катить нельзя:** клиент выберет «свою базу», увидит ноль и +решит, что портал потерял его клиентов. + +🪤 Убирая пункт, проверить **оба** места: мастер разовой кампании и вкладку «Авто-реклама» +(`TelegramAutoRulePanel.vue`). И серверную проверку `audience_kind` — если значение `base` +перестаёт быть допустимым, это правка и в `CampaignController`, и в `AutoRuleController`. + +#### 🔴 Находка 3 — с медиа наценка съедается, на видео уходит в минус + +Клиент платит ступенчатый тариф × наценку `TG_MARKUP=1.40` — **630 / 588 / 560 / 532 / 504 ₽ за +тысячу** по объёму. МТС берёт (замер живьём в кабинете 02.08): **с картинкой 600 ₽, с видео +680 ₽** за тысячу; без медиа — по комментарию в коде робота ≈400 ₽, **живьём не +переподтверждено**. + +`TelegramTariffService::clientEstimateRub()` принимает **только число показов** — медиа на цену +для клиента не влияет ни в оценке, ни в списании. + +| Объём | С картинкой | С видео | +|---|---|---| +| до 1 000 | +30 ₽ | **−50 ₽** | +| от 5 000 | **−40 ₽** | **−120 ₽** | +| от 50 000 | **−96 ₽** | **−176 ₽** | + +Чем крупнее кампания, тем больше убыток. Песочница выключена — деньги живые. + +**Рекомендуемый ответ: развести цену по медиа** — добавить в тариф ступени «без медиа / +картинка / видео» и передавать признак медиа в расчёт. Иначе первая же крупная кампания с видео +уйдёт в минус молча: портал нигде не сравнивает свою цену с ценой МТС. + +🪤 Правка тарифа трогает **и оценку, и списание, и авто-накопитель** — три места зовут +`clientEstimateRub()`. Разъедутся — экран покажет одно, спишется другое. + +🪤 Сперва **переподтвердить живьём базовую ставку без медиа**: число ≈400 взято из комментария +в коде, а не из замера. Строить тариф на невыверенном числе нельзя. + +#### 🟡 Находка 4 — срок показа нигде не объяснён + +Портал сроком **не управляет вовсе**: в задании роботу (`RunTelegramCampaignJob.php:109–120`) +нет ни одной даты. Срок ставит кабинет МТС по умолчанию, робот их не трогает +(`bots/mts-telegram-ads/src/cabinet.js:555–563`): **сегодня → +7 дней, каждому человеку один +показ**, автозапуск после модерации. + +Значит показов будет примерно столько же, сколько людей в аудитории, а «Лимит на объявление» — +**потолок-предохранитель**, а не длительность. + +**Рекомендуемый ответ: дописать одной строкой** в сводку шага «Проверка и отправка»: +«Показ идёт 7 дней, каждый человек увидит объявление один раз.» Правка на пять минут, снимает +целый класс вопросов «а сколько это будет крутиться». + +#### 🟡 Мелочи оттуда же — решать заодно + +- Пузырь чата поддержки **перекрывает кнопку «Дальше»** внизу мастера (проверено координатами: + пузырь 838–894×793–849, кнопка 785–873×792–828). Виджет общий для всего портала. +- Подпись «Выберите, кому показывать, — посчитаем охват» на пустом своём списке номеров: выбор + уже сделан, не хватает самих номеров. Фраза не про ту ситуацию. + +### Часть 2. Выкат на боевой — только с отдельного «да» владельца + +**И только после того, как закрыта находка 2.** + +Поимённый список из 24 файлов, порядок выката, предупреждение про миграцию и разбор прав — +в прежнем промте [2026-08-06](2026-08-06-PROMT-priyomka-i-vykat-reklamnyh-ekranov.md), +раздел «Часть 2». Он выверен: числа пересчитаны командой, существование каждого файла +проверено, карта значков в список добавлена. + +Коротко, чтобы не листать: + +- 🔴 в выкате есть **изменение базы** — миграция + `2026_08_02_000001_add_headline_and_media_to_client_tg_auto_rule.php`, два необязательных + столбца, откат есть. Последний занятый номер журнала схемы — **v9.64**. +- 🔴 **полный overlay запрещён** — катить поимённым списком; +- 🔴 `config:cache` / `optimize` — **от `www-data`, НЕ от root**; +- новая ручка `POST /api/telegram/campaigns/estimate` **новых прав в базе не требует** — читает + строго то же, что уже читает создание кампании, и не пишет никуда. + +### Чем кончается смена + +Либо «владелец решил так-то, починил, выкатили, вот что смотрел» — и тогда обновить `ПИЛОТ.md` +и написать промт следующей смене. Либо честное «сделал столько, дальше уперся вот во что». + +--- + +## Что уже сделано + +### Пять пачек — все в gitea, **ни одна НЕ на боевой** + +| Пачка | Коммит | Что | +|---|---|---| +| 1 | `7922bffb6` | заголовок и картинка в авто-правиле, проверка медиа по требованиям МТС, цена показа с медиа до загрузки | +| 2 | `a2496dfe6` | честные статусы: «Принято, показов пока нет», «часть объявлений отклонена — 2 из 15» | +| 3 | `6ae6457b3` | подсказки «?» у 16 полей, тексты в одном файле, язык под сторожами | +| 4а | `bcbbb25b0` | каркас телеграм-экрана: вкладки, карточка денег, кампании карточками | +| 4б | `76cb44b91` | мастер из четырёх шагов, живой счётчик охвата, ручка оценки | +| 5 | `3d728a2ba` | приёмка глазами + починка 20 значков + сторож | + +🔴 **Карточка денег на телеграм-экране — ОТДЕЛЬНЫЙ компонент, не яндексовая шапка.** Экраны +берут деньги из разных мест: Яндекс — из рекламного кошелька (`ad_wallets`), телеграм — из +**общего баланса** (`tenants.balance_rub`). Показать одни деньги, а списать другие — хуже, чем +некрасивый экран. Слова «Заморожено» на телеграм-карточке нет намеренно: сервер отдаёт +заморозку жёстким нулём. + +🔴 **Правило перехода статусов Яндекса НЕ тронуто** — на `rejected` висит возврат заморозки +(`AdWalletService::release`). Трогать нельзя. + +🔴 **Запуск запрещён, когда людей меньше 367** — правило площадки, предохранитель стоит и на +сервере (`AudienceGateTest`). + +### Что проверено ЖИВЬЁМ 02.08 (не тестом) + +- живой счётчик охвата **правда ходит на сервер** — два запроса `POST /campaigns/estimate`, + оба 200; +- ручка оценки **правда только читает**: после её вызовов `select count(*) from + client_tg_campaigns` = 0; +- «Заголовок объявления» **сам появляется**, как только ссылка становится сайтом; +- «Запустить» **не нажимается** при охвате меньше 367 (проверено свойством `disabled`); +- «Принято, показов пока нет» и «часть объявлений отклонена — 2 из 15» на месте; +- «Пополнить баланс» открывает пополнение **общего** баланса, а не рекламного кошелька; +- миграция пачки 1 прогоняется на базе чисто. + +--- + +## Где всё стоит — замер 02.08 поздно вечером + +**Ветка `fix/tg-zagolovok-obyavleniya` = `46e1adecb`**, неотправленного 0, рабочая папка чистая +(кроме машинного `docs/observer/STATUS.md`). + +### Как посмотреть экраны самому + +Приёмка шла на **локальном** портале: `php artisan serve --host=127.0.0.1 --port=8765` из папки +`app`, вход **`admin@demo.local` / `password`**, организация «Demo Tenant», баланс 1 000 ₽. + +🔴 Демо-вход **на боевом не работает с 01.07** — тестовых учёток там нет, все 10 тенантов живые. +Доступ для приёмки на бою просить у владельца. + +В **локальную** базу заведены напоказ: 4 телеграм-кампании (`launched` / `moderating` / +`rejected` / `needs_review`, одна с нарочно длинным текстом), 1 кампания Яндекса `running` с +нулём показов и 15 объявлениями, из них 2 `REJECTED`. Убрать: +`delete from client_tg_campaigns where tenant_id=1; delete from ad_campaigns where id=11;` + +🪤 Локальная база отставала на **две миграции** — прогнаны 02.08. Если экран ведёт себя +странно, первым делом `php artisan migrate:status`. + +### Боевой — замеры прошлой смены, перепроверить командой + +- 🔴 **Пачки 1, 2, 3, 4а, 4б и 5 на боевой НЕ поехали.** +- Заголовок объявления на разовой форме доехал (выкат 02.08 днём), приёмка глазами пройдена. +- 🟡 **Откат того выката ещё лежит:** каталог `public/build-old-zagolovok` и архив + `/tmp/front-backup-2026-08-02-zagolovok.tgz`. Сутки прошли — можно убирать. +- Телеграм: 7 таблиц `client_tg_*`, `TG_ROBOT_TRANSPORT=poll`, **`TG_SANDBOX=false`**. + +### Робот + +Живёт на машине владельца: `C:\liderra\mts-telegram-robot`, свой Node 20, профиль браузера +`C:\liderra\mts-bot-profile`. Стучится в портал раз в минуту. Баланс МТС **5 010 ₽** — +фактический потолок трат. + +⛔ Не ставить робота на сервер и не покупать прокси — обе дороги закрыты навсегда. + +### Тесты и сторожа — замеры ЭТОЙ смены + +- Экраны: `cd app && npx vitest run` — **241 файл, 1832 теста, 0 падений**, 3 пропущено, ~3,5 мин. +- Реклама Яндекса: `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. **Решить про выкат** — после находки 2. + +--- + +## ⏸ Отложено владельцем — НЕ поднимать + +**Портал не хранит числа показов телеграм-кампании** (колонка `actual_cost_rub` есть, но её не +пишет ни одна строка кода; доставленных показов в таблице нет вовсе). Владелец 02.08: +«показывает, надо только запустить и начнёт показывать — не обращай внимание пока». +По своей инициативе не трогать. + +--- + +## Ловушки + +### Главная ловушка этой работы + +🔴 **Зелёные тесты не увидели ни одной из четырёх находок.** Все четыре вскрыл взгляд на живой +экран, три — вопросы владельца. Прежде чем говорить «работает», **открой и посмотри**. + +🪤 Особый случай: **тест, который сам собирает состояние, охраняет пустоту.** «Моя база +номеров» покрыта тестами, и все зелёные — потому что каждый тест сам вписывает контакты. До +состояния «контакты появились» не доходит ни один, потому что дойти туда нечем. Проверяя +источник данных, спрашивай: **а кто в него пишет?** + +### Правка кода + +- 🪤 **Значки: имени нет в карте — молча вопрос в кружке.** Теперь это ловит сторож + `tests/Frontend/znachki-est-v-karte.spec.ts`. Новое имя `mdi-*` обязано быть в `lucideMap` + (`resources/js/plugins/vuetify.ts`). +- 🪤 **Причёсывалка (`prettier --write`) переписывает ВЕСЬ файл**, если он не был причёсан до + тебя. Датчик: сравнить `git diff --stat` и `git diff --stat -w`. +- 🪤 **Датчик пробелов ложно срабатывает на переезде кусков кода** — если строки удалены в одном + месте и добавлены в другом, счётчики разойдутся на 2–3 строки. Смотреть глазами, а не чинить. +- 🪤 **Резать куски файла скриптом по номерам строк — опасно**: номера сдвигаются между замером + и правкой. Резать через Edit по точному тексту. +- 🪤 **Итоги группировки — не модели.** `groupBy` + `count(...) as total` через Eloquent даёт + строки, притворяющиеся моделью. Лечение — `->toBase()` перед `->get()`. +- 🪤 **Имён колонок и таблиц наизусть не помнить.** Кампании Яндекса — `ad_campaigns`, + объявления — `ad_campaign_banners` (одно объявление на размер — уникальность по + `tenant_id, campaign_id, width, height`), правило авто-телеграма — `client_tg_auto_rule` + (единственное число!), рекламный кошелёк — `ad_wallets`, контакты телеграма — + `client_tg_contacts`. Сперва `information_schema`. +- 🪤 **Статус модерации объявления пишется ПРОПИСНЫМИ** (`ACCEPTED` / `REJECTED`). Строчными — + сервер насчитает ноль отклонённых и промолчит. + +### Тесты + +- 🪤 **Тест, который ищет исчезнувшее поле, зеленеет по ложной причине:** подсказки «нет» и у + того, чего нет. Сперва убедиться, что поле на экране есть. +- 🪤 **Переносишь код — переноси проверки, а не выбрасывай.** Сперва переписать их на новом + месте и там прогнать, только потом убирать со старого. +- 🪤 **После НОВОГО тестового файла на PHP статанализ краснеет** — шум `$this` в Pest, гасится + `app/phpstan-baseline.neon`. Пересобрать `--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 -`. + +### Работа с базой и оболочкой + +- 🪤 **Кириллица в SQL через оболочку ломается** («неверная последовательность байт для UTF8»). + Писать запрос файлом через Write и звать `psql -f`. +- 🪤 Файл, записанный **из-под Bash**, может молча исчезнуть к следующему вызову. Правки — + только Edit/Write. +- 🪤 Снимок **всей страницы** рисует прилипшие элементы (пузырь чата) не на своём месте. + Перекрытия проверять координатами, а не снимком. + +### Боевой + +- 🔴 **Заливку скрипта и его запуск не соединять** одной командой — виснет на две минуты. +- 🔴 **Вывод писать в файл**; `echo` из tinker не доезжает. **Нет файла ⇒ скрипт УПАЛ.** +- 🔴 **`config:cache` / `optimize` — от `www-data`, НЕ от root.** От root портал ложился на 18 минут. +- 🔴 **Полный overlay ЗАПРЕЩЁН** — катить поимённым списком. +- 🪤 `sudo -u postgres psql` на боевой машине — **СТАРАЯ копия**. Живая база — Managed PG. +- 🪤 Новым RLS-таблицам мало `tenant_isolation` + GRANT — надо **пере**запускать + `db/03_service_bypass_policies.sql`, иначе служебная роль тихо правит НОЛЬ строк. + +--- + +Работа идёт **пачками**: после каждой владелец делает компакт. Не хвататься за всё сразу.