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>
This commit is contained in:
Дмитрий
2026-08-01 23:03:31 +03:00
parent 3b158a62f4
commit 96c92b9f8d
@@ -0,0 +1,220 @@
# Промт следующей смене — реклама Яндекса доведена, осталось решение владельца
Дата постановки: 02.08.2026, ночь. Всё ниже проверено живыми командами и заходом
в кабинет глазами. Этот файл **заменяет** `2026-08-01-PROMT-zakryt-hvosty.md`
(его хвосты №1, №2, №4 и главная часть №5 закрыты — см. ниже).
---
## 🔴 Красные линии
**Прод — живые клиенты и деньги.** Выкат, движение денег, включение показов, любая
правка боевой базы — только по явному «да» владельца. База по умолчанию **только чтение**.
**Пуш только в gitea**, GitHub мёртв (аккаунт заблокирован, `origin` отвечает 403).
**Логины, пароли, телефоны — не в файлы и не в коммиты.**
Владелец **не программист** — простым русским, короткими абзацами. «Готово» — только
если правда проверил, и лучше **глазами**, а не по коду.
---
## Где всё стоит на сейчас
**main = `3b158a62f`, в gitea.** Ветка `fix/tg-zagolovok-obyavleniya` совпадает с ним,
расхождение ноль. Сведение прошло **перемоткой**, без слияния — чужие ветки не задеты.
Что в main: телеграм-модуль целиком, три починки модерации Яндекса, две починки разведчика
и четыре промта прежних смен.
**Замеры перед сведением:** тесты рекламы портала **354/354**, сторожа робота **88/88**.
**Боевой сайт** (замер 02.08 ночью):
| Что | Состояние |
|---|---|
| Починенный файл портала | отпечаток совпадает с main байт в байт |
| Сайт | 200 |
| Очереди | все четыре живы |
| Робот-разведчик на `51.250.1.97` | код совпадает с main, оба расписания включены |
| Задания разведки | **15 из 15 выполнено, сбойных ноль** |
---
## ✅ Что закрыто и проверено живьём
**Разведчик работает сам.** Стоит на виртуалке-рендере `51.250.1.97`,
в `/opt/liderra-creative-robot`, под двумя systemd-таймерами: `run` раз в 2 минуты
и `keepalive` раз в 15. Доступ — `ssh -i ~/.ssh/liderra_deploy ubuntu@51.250.1.97`.
**Упавшее задание больше не сгорает.** Обход модерации поднимает сбойное задание обратно
в очередь, счётчик попыток копится, потолок — **3 попытки**
(`CreativeJobService::MAX_INSPECT_ATTEMPTS`, менять одной строкой).
**Список кабинета рисует только видимые строки.** Робот научен долистывать
(`dolistatDoObyavleniya`). Прежний вывод «семь объявлений пропали» был **ложью робота**:
`ads.get` отвечает, что живы все пятнадцать. Разбор — `bots/yandex-creatives/docs/cabinet-flow.md` §7.11.
**Живая приёмка 01.08 13:0013:30 UTC:** починка сама вернула 13 сбойных заданий в очередь,
робот закрыл все, в ленту клиента легли 12 настоящих причин от Яндекса. Ни одной строки
в боевой базе руками не правил.
🔑 **Урок дороже самой починки: доклад робота — не замер.** Ложная фраза робота за один шаг
стала ложным выводом о продукте и уехала в разбор и в промт. Спрашивать надо внешнюю
систему напрямую.
---
## Что осталось — всё упирается в владельца
### 🟡 №1. Документы Яндексу — **это главное**
Показов у кампании 713175197 не будет, **пока Яндекс не примет документы**: при рекламе
финансовых услуг закон требует прислать сведения об организации. Причина у всех тринадцати
принятых объявлений одна: «Для показа в заданных регионах предоставьте документы».
Два объявления вдобавок отклонены за «Нет предупреждения: финансовые услуги» — их
перепроверят сами, когда документы примут.
Бумаги владельца, кодом не лечится. Пока их нет, вся цепочка стоит впустую.
### 🔴 №2. Настоящие телефоны в чужой живой ветке
`gitleaks` по полной истории находит **4 незамаскированных российских телефона**:
- коммит `868a681cf` от 01.08.2026, автор Дмитрий,
- файл `docs/superpowers/2026-07-27-PRIEMKA-client-sms-fixes.md`,
- ветка **`feat/prospects-manual-testing-kp`** (живая, блок менеджеров).
**В main их НЕТ** — проверено `git merge-base --is-ancestor`. Чужую ветку не трогать.
🔴 **Не сводить эту ветку в main, пока телефоны не вычищены** — иначе ПДн уедут в общую
историю, и вычистить их будет уже переписыванием истории.
### 🟡 №3. Четыре старых английских сообщения в ленте клиента
«Rejected at moderation.» от 30.07 никуда не делись — новые сообщения просто легли поверх.
Вреда нет, кроме вида. Убрать — правка боевой базы, только с «да» владельца.
### 🟡 №4. Три сбойных задания на заливку картинок
В боевой базе висят `upload` со статусом `failed` от 29.07 («после загрузки не появился
креатив размера …»). К разведке отношения не имеют, показам не мешают. Разбирать —
отдельная работа, не срочная.
### 🟡 №5. Телеграм в main, но на бой не выкачен
Порядок выбран владельцем: сначала показы Яндекса, телеграм следом. Сейчас `TG_SANDBOX=true`
робот доводит кампанию **только до черновика**, деньги не двигаются.
- **Г1. Сквозная приёмка не пройдена.** Проверялось по частям. 🔴 На бою **ноль
телеграм-кампаний**, принять выкаченное чтение вердикта не на чем. Порядок: владелец
создаёт кампанию со ссылкой на сайт → смотрим, как робот забрал задание → **открываем
кабинет МТС глазами** и убеждаемся, что черновик есть и заголовок тот самый → ставим
задание `read-status` и убеждаемся, что вердикт доехал до портала.
- **Г2.** `TG_SANDBOX=false` — только по явному «да», с потолком денег и на одном клиенте
первым. Чистка черновиков — `_cleanup-delete-step1.mjs` / `-step2.mjs`, по одному за раз.
- **Г3. 🔴 Стена: минимум площадки — 367 «не МТС» номеров**, зашит в трёх местах
(`bots/mts-telegram-ads/src/cabinet.js:65`, настройки портала, проверка перед запуском).
У самого крупного клиента на бою **112** сделок. Хорошее: клиент получит понятный отказ
**до списания денег**. 🪤 Но считаются **наши загруженные** номера, а не найденные МТС
в телеграме: 400 выдуманных номеров кабинет принял как «аудитория 400».
---
## Ловушки, добытые кровью
### Про кабинет Яндекса
🪤 **У ПРИДЕРЖАННОГО объявления окна причины НЕ БЫВАЕТ.** Причина написана прямо в списке.
У ОТКЛОНЁННОГО ровно наоборот: в списке пусто, суть спрятана в окне, которое надо раскрыть.
Дословные подписи — `bots/yandex-creatives/docs/cabinet-flow.md` §7.10.
🪤 **Список кабинета рисует только видимые строки, и у него СВОЯ полоса прокрутки**
колесо мыши по странице её не двигает. §7.11.
🪤 **Программный адрес Яндекса — `<база>/json/v5/ads`, не `/v5/ads`.** Заголовок
`Accept-Language: ru` обязателен, иначе ответ придёт по-английски. Списывать форму запроса
с боевого клиента `YandexDirectClient`, а не сочинять.
🪤 **Логин кабинета** — не тот, под которым ходит программный доступ. Конкретные учётки
в файлы не пишем; смотреть `2026-08-01-PROMT-zakryt-hvosty.md` §хвост №1.
### Про чтение боевой базы (только чтение)
Кладём php-файл в `/tmp` heredoc'ом с кавычками `<<'PHPEOF'`, зовём
`sudo -u www-data php artisan tinker /tmp/файл.php` из `/var/www/liderra/app`,
потом файл удаляем. Соединение — `pgsql_admin`.
🪤 **Вывод писать в файл** (`file_put_contents`) или через `fwrite(STDERR, …)``echo`
из tinker не доезжает, а конвейер `| head` вешает команду до таймаута.
🪤 **Не глушить ошибки `2>/dev/null`.** Дважды за смену получил «пустой ответ» вместо
правды: первый раз tinker падал на несуществующей колонке, а я выбросил сообщение.
Датчик: нет файла с результатом ⇒ скрипт упал, а не «данных нет».
🪤 **Колонок наизусть не помнить.** В `ad_campaign_messages` автор называется `author`,
а не `sender`. Сначала спросить `information_schema.columns`, потом писать запрос.
🪤 **ssh + heredoc + tinker + `cat` одной командой вешаются** на две минуты и падают
по таймауту. Разбивать на отдельные вызовы: залить → выполнить фоном → прочитать.
🪤 **Файл, созданный от `www-data`, обычным пользователем не удаляется** — убирать
`sudo rm -f`, иначе останется мусор в `/tmp`.
Полезные таблицы: `ad_campaign_messages` (лента клиента), `ad_campaign_banners`
(статус и причина по объявлению), `ad_creative_jobs` (задания роботу), `ad_campaigns`.
### Про гит и сторожей
🪤 **Сторож упал — сперва замерь, было ли это ДО тебя.** Сейчас штатно падают два,
и оба не на нашей работе:
- `gitleaks-full-history` — на чужих телефонах в ветке менеджеров (см. хвост №2);
- `lychee-links` — на ссылке из `CLAUDE.md` в папку `лендинг/`, которой **в репозитории
нет вообще** (стоит с 15.06.2026, битая в любой рабочей копии).
Разбирать поимённо, убедиться, что находки не твои, и только тогда отключать
на **один** пуш: `LEFTHOOK_EXCLUDE=gitleaks-full-history,lychee-links git push …`.
🪤 **Многострочное сообщение коммита набирать heredoc'ом `-F -`**, а не как в PowerShell.
Смесь синтаксисов молча съедает текст: коммит создался с сообщением из одного символа,
файлы при этом легли правильно. Датчик — `git log -1 --format='%B'` сразу после коммита.
🪤 **Коммит с хуками идёт дольше двух минут** — гнать фоном и проверять `git log`,
а не ответ команды: хук уже отклонял коммит, вернув ноль.
🪤 **`docs/observer/STATUS.md` — машинный**, его переписывает хук. Не пре-стейджить,
при конфликте брать свою версию.
🪤 **Локальный слепок ветки врёт** — прежде чем судить «main отстал», `git fetch`.
🪤 **Локальный `main` может быть занят другой рабочей копией**`git branch -f main`
тогда откажет. Обходить не силой, а перемоткой в той копии: `git merge --ff-only`.
🪤 **Разрешая конфликт в общем словаре `cspell-words.txt` — не выбирать сторону**
и не чистить заодно чужие задвоения.
### Про тесты и окружение
🪤 **Своя тестовая база на копию:** `liderra_testing_zag`, включается через
`DB_DATABASE=liderra_testing_zag php artisan test`. Правку в `app/phpunit.xml` не коммитить.
🪤 **Сторожа робота гонять через `npm test`**, а не `node --test test/` — второе падает
общей ошибкой без объяснения.
🪤 **В свежем worktree нет ни `vendor`, ни `node_modules`, ни `bin/lychee.exe`** — сторожа
падают «Could not open input file». Это не поломка кода.
🪤 **Эталон статанализа после каждого слияния пересобирать** в два шага: закомментировать
`- phpstan-baseline.neon` в `app/phpstan.neon`, прогнать `--generate-baseline=…`,
вернуть строку.
---
## Чем кончается смена
Реклама Яндекса доведена до предела, до которого её можно довести кодом. Дальше либо
владелец присылает документы и мы смотрим, как пойдут показы, либо он даёт «да» на одну
из мелочей выше, либо берёмся за телеграм по порядку Г1 → Г2.
Если владелец молчит — не выдумывать себе работу на боевом. Лучше честное «ждём документы».