docs: промт смене 23 — забрать четыре прогона и поймать возврат по сроку

Работа смены: снять урожай с четырёх запущенных прогонов приёмки, поймать
последнюю бесплатную карточку Я-Д5 по летописи проводок и провести Я5.

Что несёт промт помимо состояния:

  — пороги, на которых закрываются Я3 и Я4: 1667 и 834 показа, и правило
    мерить скорость последним часом, а не средним за сутки;
  — рычаг управляемого отказа модерации — два конкретных креатива, которые
    Яндекс забраковал дважды подряд; прежнее решение про текст не работает;
  — два способа добиться «впритык» без правки денежной летописи: долив до
    круглой суммы или временная кампания-подпорка;
  — предупреждение, что сторож зависимостей красный из-за недоделанных
    файлов соседней смены и чужое чинить не нужно;
  — четыре ложные тревоги смены 22 одного корня: вывод по косвенному
    признаку вместо вопроса той стороне, которая знает ответ. Главная —
    «робота картинок не устанавливали», а он работал на третьей машине,
    и журнал веб-сервера отвечал на это одной командой;
  — ловушка записи на бою: tinker молча глотает длинные и многострочные
    куски, нужен самостоятельный php-скрипт с загрузкой приложения.

Разметка чисто, 239 строк.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-08-05 16:11:11 +03:00
parent 81f2a77c04
commit 03e07cafd1
@@ -0,0 +1,239 @@
# Промт смене 23 — забрать четыре прогона и поймать возврат по сроку
**Составлен:** 05.08.2026, 13:05 UTC (16:05 МСК).
Продолжает [промт смены 22](2026-08-06-PROMT-smena-22-vozvrat-po-sroku-i-chto-dalshe.md),
а через него смены 21–17 — всё, что там про доступы, метод, ранбуки и церемонию записи,
**остаётся в силе**. Здесь только то, что изменилось.
🎯 **Работа смены: снять урожай с четырёх запущенных прогонов, поймать возврат по сроку
и провести последний оставшийся прогон Я5.**
🪤 **Не вываливай владельцу эти файлы.** Он их не читает — они для тебя. Спрашивай коротко,
человеческим языком, к каждому варианту цена и выгода, и говори, что советуешь сам.
---
## 0. ⏰ Первое — что случилось ночью
### 0.1 Возврат по сроку у #13 (карточка Я-Д5, вторая половина)
Срок #13**05.08 включительно**, закрытие произошло при первом часовом обходе после
полуночи МСК. Это **последняя бесплатная карточка приёмки** и ловится она один раз.
Проверить: статус стал `completed`; заморозка под #13 обнулилась; свободные выросли ровно
на возврат; `delivered_impressions` совпал с кабинетом; в `ad_wallet_transactions` есть
строка `release` по кампании 13.
🪤 К твоему приходу всё уже случилось — сверяй **по летописи проводок**, а не «глазами в
момент»: строка `release` с суммой и временем и есть доказательство.
### 0.2 Показы у #19 и #21 — сколько накапало
Оба прогона крутятся ради одного столбца: **показов**.
| Кампания | Что закрывает | Порог |
|---|---|---|
| **#21 «Я4»** | половина сметы | **834 показа** из 1667 |
| **#19 «Я3»** | вся смета | **1667 показов** |
🔴 **Скорость мерить ПОСЛЕДНИМ ЧАСОМ, а не средним за сутки.** Яндекс медийную кампанию
разгоняет: у #13 по часам было 6 показов, 5, 13, **25**. Смена 22 на этом ошиблась дважды
и брала слова назад. Часовые списания видно так:
```sql
select created_at, type, amount_rub from ad_wallet_transactions
where related_type='campaign' and related_id=19 order by id;
```
Цена показа 0,50 ₽ — делишь списание на 0,5 и получаешь показы за час.
🔴 **#19 и #21 НЕ ОСТАНАВЛИВАТЬ.** Им нужно открутиться. Владелец про это знает.
---
## 1. 🏆 Что сделала смена 22
**Приёмка Яндекса: из 29 карточек закрыты 28.** Осталась одна — Я-Д5, вторая половина (§0.1).
**Комбинации: закрыты 5 из 9** (Я1, Я6, Я7, Я8, Я9), четыре в работе.
**Четыре записи, все отправлены в gitea, две выкачены на боевой:**
| Запись | Что |
|---|---|
| `a0761a5c3` | отказ по деньгам говорил клиенту по-английски — **выкачено** |
| `1ceba0c56` | журнал: Я1 и Я2 запущены |
| `49ef80cd5` | возобновление выдавало непроверенную кампанию за крутящуюся — **выкачено** |
| `ad4a41fb9`, `a9de42ccb` | журнал: порог предупреждения разобран; Я3 и Я4 запущены |
**Две настоящие находки, обе тихие:**
1. 🔴 **Отказ по деньгам говорил по-английски.** Клиент видел
`Insufficient balance: price_kopecks=1500000, balance_rub=9122.76`.
`InsufficientBalanceException` наследует `RuntimeException`, а ручка запуска ловила его
общим уловителем и отдавала текст исключения наружу. Кнопка «Возобновить» этой болезнью
**не болела** — там свой уловитель, и он-то и показал, что сломан именно запуск.
2. 🔴 **Возобновление врало о состоянии.** Кампанию, которую Яндекс ещё проверяет, на паузу
поставить можно (задумано), а «Возобновить» ставило `running` **безусловно** — портал
зажигал зелёное «Крутится» рекламе, которой Яндекс показываться не разрешал.
**Плюс закрыт порог предупреждения о деньгах** (владелец сказал «забудь», см. §5) и
**включены письма о событиях кампаний** — раньше `SendWarnings` было `NO` у всех шести
наших кампаний, то есть об отказах модерации владелец не узнавал вовсе.
Прогоны на закрытие: **430 тестов рекламы**, статанализ **0**, pint чисто.
---
## 2. Сперва перемеряй
```bash
git rev-parse --abbrev-ref HEAD
git rev-list --left-right --count gitea/feat/prospects-manual-testing-kp...HEAD
git status --porcelain -- app/ db/ | grep -v '^??'
```
**На закрытие смены 22:** ветка `feat/prospects-manual-testing-kp`. Мои записи отправлены;
позади них соседняя смена дописала свои (обзвон), они могут быть неотправленными.
🔴 **В той же папке и той же ветке работает ВТОРАЯ смена** (обзвон, СМС, тарифы). Её записи
идут вперемешку с твоими.
🪤 **Сторож зависимостей `deptrac` красный НЕ из-за тебя.** Он смотрит весь код сразу, а у
соседней смены в работе лежат недоделанные `app/app/Models/ObzvonCall.php` и
`ObzvonTariff.php` — пять нарушений оттуда запирают ЛЮБУЮ запись. Чужое не чинить. Коммит —
с `LEFTHOOK_EXCLUDE=markdownlint,cspell,deptrac` **и только с разрешения владельца**;
остальные сторожа (gitleaks, pint, larastan) оставлять включёнными.
🪤 **В индексе может лежать ЧУЖОЕ.** Записывать **только своими путями**, никогда `-A`.
🕘 **Часы машины врут на 4–5 часов.** Время для замеров брать серверное (`ssh liderra-prod date`).
🪤 **Тестовая база — своя:** `DB_DATABASE=liderra_testing_s19 ./vendor/bin/pest ...`
---
## 3. 🧵 Что осталось
| Что | Состояние |
|---|---|
| **Я-Д5, вторая половина** | ловится по летописи, §0.1. Последняя карточка из 29 |
| **Я3 (#19), Я4 (#21)** | крутятся, ждут показов. Пороги — §0.2 |
| **Я5** | **не проводился, но теперь ВОЗМОЖЕН** — см. ниже |
| **Я2** | отказ модерации получен (#17). Осталось оформить недостижимые ячейки как `SKIP` |
### Как провести Я5 — «впритык + отклонили»
Условия: К=впритык, М=отклонили, П=вся смета (**недостижимо по построению** — отклонённая
реклама не показывается, писать `SKIP`), Д=ничего.
🔑 **Управляемый отказ модерации у нас ЕСТЬ, и это две конкретные картинки:** креативы
**1163668189 (320×50)** и **1163668188 (320×100)**. 05.08 Яндекс забраковал их дважды
подряд: в кампании только из них — полный отказ, в смеси с хорошими — частичный приём.
Прежнее решение «добывать отказ текстом про займы под залог» **не работает**: название
кампании до модерации не доезжает.
🔑 **Как добиться «впритык», не трогая летопись денег** — два способа, оба проверены живьём:
- свободных МЕНЬШЕ круглой суммы → **долить** до неё, смета ровно на неё
(показов = сумма × 1000 / цена за 1000; при 500 ₽ это вдвое больше рублей);
- свободных БОЛЬШЕ → **временно занять** лишнее кампанией-подпоркой: пока она на модерации,
показов не делает и денег не тратит. Запустить нужную, потом подпорку снять.
Условие К проверяется **в момент запуска**, поэтому приём честный. Прогон стоит **ноль**:
отклонённая кампания не показывается, заморозка возвращается целиком.
🪤 Подпорку потом **не удалить** — удаляются только черновики. Останется в кабинете на паузе.
---
## 4. Живое состояние на 13:05 UTC 05.08
| Кампания | Состояние | Показов | Баннеры |
|---|---|---|---|
| #6 | пауза, стоит из-за документов | 0 | 13 приняты, 2 отклонены |
| #11 | пауза, срок вышел | 2 | все приняты |
| #13 | **крутится**, закроется ночью | 101 | все приняты |
| #16 «Я1» | крутится, владелец остановит | 0 | все приняты |
| #17 «Я2» | **отклонена**, деньги вернулись целиком | 0 | обе отклонены |
| #18 «Я6» | пауза | 0 | на модерации |
| #19 «Я3» | **крутится, не трогать** | 0 | 13 приняты, 2 отклонены |
| #20 подпорка | пауза, служит «прихожей» роботу картинок | 0 | — |
| #21 «Я4» | **крутится, не трогать** | 0 | приняты |
**Кошелёк тенанта 2:** баланс 9 999,50 ₽, заперто 3 316,50 ₽, свободно 6 683,00 ₽.
Из баланса 77,24 ₽ — долив 05.08 под прогон «впритык».
**Счёт в Директе:** номер 712890032, логин `sasha261185`, на счету ~8 700 ₽.
---
## 5. ⚠️ Что владелец уже решил — не поднимать заново
- 🔕 **Порог предупреждения о деньгах** (20 ₽) — «забудь». Программой его менять нельзя:
у кампании потолок 50 ₽, у общего счёта ручка на запись отвечает «сервис временно
недоступен». Только руками в кабинете, владелец знает.
- 🔕 **Документы для показа в регионах** (из-за них стоит #6) — «забудь».
- 🔕 **Лиды от поставщика** — «забудь» (решение смены 21).
-**#16 и #17 владелец останавливает сам.**
-**Ночная сводка** оставлена включённой намеренно — придёт письмо ни о чём, это не сбой.
**Осталось нерешённым:**
1. **ЮKassa** — магазин 1392092, ключ на месте, тумблер включён, но **живой успешной оплаты
не было ни разу**. Путь «заплатил → зачислилось» в бою не проверен. Проверяется одной
оплатой на 100 ₽.
2. **Две смены в одной ветке и одной папке** — предложено развести, решения нет.
---
## 6. 🔴 Боевой: что трогать нельзя
Всё из §7 промтов смен 17–22 в силе. Коротко: потолок робота МТС **1 000 ₽** (в рабочем
`.env` `C:\liderra\mts-telegram-robot\`, не в репозиторном); `allowed_operators` только
`mts`; рубильник Директа **не выключать**; чужие кампании в кабинете только читать
(713051718 — эталон); стоп-лист СМС не чистить; миграции только под `crm_migrator`
(Lockbox `e6qeooj45vqtn2fju58r`); **кампании #6, #11, #13, #19, #21 не удалять**.
🪤 **Форматтер трогает чужое.** `composer pint` без списка файлов переписывает чужие файлы.
Звать **только со своими путями**, после — смотреть `git status`.
🔴 **ПДн.** Список 1693 номера — личные мобильные. В git не тащить, в переписку не выводить.
🔴 **В адресе gitea логин и пароль открытым текстом.** В переписку целиком не выводить.
**Откатные копии на бою:** `/tmp/otkat-s22` (контроллер рекламы до обеих правок дня),
`/tmp/otkat-s22-env` (боевой `.env` до правки даты прокси).
---
## 7. 🔑 Уроки смены 22 — четыре ложные тревоги одного корня
Все четыре — вывод **по косвенному признаку** вместо вопроса той стороне, которая знает
ответ. Если ловишь себя на «похоже, что…» — остановись и найди команду, дающую ответ.
1. **«Робота картинок никогда не устанавливали».** Проверил две машины из трёх — эту винду
и боевой сервер. У Лидерры их **ТРИ**: есть ещё **виртуалка-рендер 51.250.1.97**, и
робот живёт там, спрашивая работу каждые 2 минуты. Успел поставить вторую копию
с боевым токеном, прежде чем понял. 🔑 Ответ давала одна команда:
`sudo grep 'creative-robot' /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c`
— 365 обращений с виртуалки против 1 с моей машины.
2. **«Показов на полтора месяца».** Померил одной точкой: показы за сутки делить на 24.
Яндекс разгоняется — мерить последним часом.
3. **«Управляемого отказа модерации не добыть».** Сказал это владельцу, а через час те же
две картинки дали полный отказ. Гипотезу проверять запуском, а не рассуждением.
4. **Мост МТС «сломан»** — код ошибки в Планировщике был от перезапуска. 🔑 Датчик:
`curl` с боевого через туннель — ответ за 0,4 с.
🪤 **Отдельный урок про запись на бою:** `tinker` через stdin **молча глотает** длинные
и многострочные куски — отчитывается успехом и не делает ничего. Для любой работы сложнее
одной короткой строки писать самостоятельный php-скрипт с загрузкой приложения:
```php
<?php
require '/var/www/liderra/app/vendor/autoload.php';
$app = require '/var/www/liderra/app/bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
DB::statement("SELECT set_config('app.current_tenant_id', '2', false)"); // без этого RLS отдаёт пусто БЕЗ ошибки
```
Запуск: `scp файл liderra-prod:/tmp/ && ssh liderra-prod "cd /var/www/liderra/app && sudo -u www-data php /tmp/файл.php"`.