diff --git a/docs/superpowers/2026-08-08-PROMT-vykat-telegram-reklamy.md b/docs/superpowers/2026-08-08-PROMT-vykat-telegram-reklamy.md index 6011d04b..f9700a25 100644 --- a/docs/superpowers/2026-08-08-PROMT-vykat-telegram-reklamy.md +++ b/docs/superpowers/2026-08-08-PROMT-vykat-telegram-reklamy.md @@ -1,14 +1,16 @@ -# Промт следующей смене — выкат телеграм-рекламы, четыре находки закрыты +# Промт следующей смене — выкат телеграм-рекламы готов, ждёт слова владельца -Поставлено 02.08.2026 ночью. Всё ниже — живые замеры этой смены, а не память. +Поставлено 02.08.2026 ночью, **после** пуша и подготовки выката. Всё ниже — живые замеры +этой смены, а не память. Переписан целиком: прежняя редакция этого файла устарела за ночь. -**Главный документ работы — [2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md](2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md).** -Там замечания владельца дословно, разбор всех пачек и находок; находки 1, 3, 4, 5 и «срок -показа» помечены закрытыми с номерами коммитов. +**Ветка `fix/tg-zagolovok-obyavleniya` = `5a41d1b37`, отправлена в gitea, неотправленного 0.** -Прежний промт [2026-08-07-PROMT-resheniya-vladelca-i-vykat.md](2026-08-07-PROMT-resheniya-vladelca-i-vykat.md) -**отработан**: владелец решил по всем четырём находкам, всё сделано и закоммичено. Осталась -только его вторая часть — **выкат**. +Опорные документы: + +- **[2026-08-08-PLAN-vykata-telegram-reklamy.md](2026-08-08-PLAN-vykata-telegram-reklamy.md)** — + поимённый список 34 файлов, порядок шагов, откат, приёмка глазами. **Главное для выката.** +- [2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md](2026-08-02-ZAMECHANIYA-edinyy-vid-reklamnyh-ekranov.md) — + замечания владельца дословно, разбор всех пачек; находки помечены закрытыми. --- @@ -27,149 +29,122 @@ --- -## Что решил владелец 02.08 и что сделано +## 🔴 Смена начинается с развилки — спросить владельца ПЕРВЫМ ДЕЛОМ -| Находка | Решение владельца | Коммит | -|---|---|---| -| Значки, 23 имени вне рекламы | «Починить сейчас, заодно» | `b264e7e9` | -| Подпись «Выберите, кому показывать» | заодно | `b264e7e9` | -| Срок показа не написан нигде | «Дописать» | `b264e7e9` | -| «Моя база номеров» ведёт в никуда | **«Сделать загрузку базы сейчас»** | `00cc072e` | -| Цена не зависит от медиа | **«Развести цену»** | `3864f6f1` | +Ему задали вопрос в конце прошлой смены, ответа ещё нет: -🟢 **Запрет на выкат снят:** он держался на «Моей базе», она заработала. +> **Идти на боевой — или сперва разобрать телефоны на ветке СМС?** -### `3864f6f1` — цена показа по виду медиа +**Рекомендуемый ответ: сперва выкат.** Он готов, проверен и снимает уже работающую беду +(«Моя база» на бою ведёт в никуда). Телефоны на ветке СМС никуда не денутся и на бой не +влияют — та ветка не выкачена. -Работа была написана прошлой сменой, но **не проверена и не закоммичена** — лежала в рабочей -папке 17 изменёнными файлами. Эта смена её проверила, дописала недостающее и закрыла. +Не начинать ни то ни другое, пока владелец не ответил. -Замер вскрыл, что прежний тариф стоял на **двух неверных посылках**: +--- -1. **Скидки за объём у МТС НЕТ** — прайс кабинета, стр. 13: 0,48 ₽ за показ плоско. - Ступенчатую скидку давали мы, а нам её не давал никто: на объёме от 50 000 наценка 1.40 - превращалась в **5 %**. +## Дорога А — выкат на боевой + +**Всё готово, вести по [плану выката](2026-08-08-PLAN-vykata-telegram-reklamy.md).** Здесь — +только то, что нельзя пропустить. + +1. 🔴 **Пересчитать список файлов заново** — ветка могла уйти вперёд: + `git diff --name-only ad7a31271..HEAD -- app/ | grep -v '^app/tests/' | grep -v phpstan-baseline` +2. 🔴 **До заливки проверить право на счётчик.** В `client_tg_contacts` до 02.08 не вставляла + ни одна строка кода — право боем ни разу не проверялось: + `select has_sequence_privilege('crm_app_user', 'client_tg_contacts_id_seq', 'USAGE');` + Ответ `f` ⇒ `GRANT USAGE, SELECT ON SEQUENCE client_tg_contacts_id_seq TO crm_app_user;` +3. 🔴 **Полный overlay ЗАПРЕЩЁН** — поимённым списком. +4. 🔴 `config:cache` / `optimize` — **от `www-data`, НЕ от root**. От root портал ложился на 18 минут. +5. 🔴 Две миграции: **v9.64** и **v9.65**. Обе проверены прогоном туда и обратно. +6. Экраны собирать **у нас**, на боевой везти готовый `public/build`. + +**Приёмка глазами после выката — шесть пунктов в плане.** Тестовых учёток на бою нет с 01.07, +доступ у владельца. + +## Дорога Б — телефоны на ветке СМС + +🔴 **Сторож секретов находит 4 незамаскированных телефона** в +`docs/superpowers/2026-07-27-PRIEMKA-client-sms-fixes.md`, коммит `868a681cf` от 01.08. + +Замерено этой сменой: + +- коммит **только на ветке `feat/prospects-manual-testing-kp`**, в телеграм-ветке его нет; +- **на сервер не уезжал** — удалённой копии этой ветки нет; +- это **ПДн в коммите**, прямое нарушение §5 п.4 CLAUDE.md; +- пока он в истории, пуш той ветки будет упираться в сторожа. + +Правка истории — необратимая операция, **делать только с явного разрешения владельца** и +предложив ему выбор (переписать историю ветки / замаскировать и жить с исключением в +`.gitleaks.toml` / что-то ещё). Не решать за него. + +--- + +## Что сделано прошлой сменой — пять коммитов, все запушены + +| Коммит | Что | +|---|---| +| `3864f6f1` | цена показа по виду медиа вместо ступеней по объёму, запись схемы **v9.65** | +| `b264e7e9` | срок показа в сводке, подпись про номера, все 23 значка | +| `00cc072e` | **«Моя база номеров» заработала** — снимает запрет на выкат | +| `48fed405` | находки помечены закрытыми, промт | +| `5a41d1b3` | план выката | + +### Чего стоит не забыть про эти работы + +🔑 **Тариф стоял на двух неверных посылках, обе вскрыты замером:** + +1. **Скидки за объём у МТС НЕТ** — прайс кабинета, стр. 13: 0,48 ₽ за показ плоско. Скидку + давали мы, а нам её не давал никто: на объёме от 50 000 наценка 1.40 превращалась в **5 %**. 2. **Кабинет показывает CPM БЕЗ НДС** — колонка списка так и названа. Счёт кампании `2231134` сошёлся: 420 × 400 ₽/1000 × 1,2 = 201,60 ₽. Мы ИП на УСН, НДС не возмещается. -Тариф стал плоским по видам медиа: себестоимость с НДС **0.480 / 0.720 / 0.816 ₽** за показ. -Миграция `client_tg_cena_po_media`, запись схемы **v9.65**. Экран админки переделан: три -фиксированные строки, рядом цена за тысячу для сверки с кабинетом. +Себестоимость с НДС теперь плоская: **0.480 / 0.720 / 0.816 ₽** за показ. -**Что дописала эта смена:** тест экрана тарифов остался на старом договоре и падал — -переписан под новый и **проверен вырезанием** (сломал передачу `media_kind` — красный; -сломал расчёт за тысячу — красный). - -### `00cc072e` — «Моя база номеров» - -Читающая половина была готова с самого начала (`TelegramAudienceService::fromBase` умеет -нормализацию, дубли и стоп-лист) — не хватало только записи. Добавлены `TelegramBazaService`, -три ручки `GET/POST/DELETE /api/telegram/contacts` и блок заливки в шаге «Кому показываем». - -- заливка **заменяет** базу целиком, а не добавляет — написано на экране до нажатия; -- файл без единого годного номера отклоняется, старая база остаётся цела; -- потолок 200 000 номеров и 10 МБ; обрыв по потолку не молчит; -- наружу отдаются только счётчики — номера это ПДн, экрану они не нужны. +🔑 **«Моя база» вышла меньше, чем казалось:** читающая половина была готова с самого начала +(`TelegramAudienceService::fromBase` умеет нормализацию, дубли, стоп-лист) — не хватало +только записи. Заливка **заменяет** базу целиком, кривой файл отклоняется и старую базу не +трогает, наружу отдаются только счётчики. --- -## Работа смены: выкат на боевой — только с отдельного «да» владельца - -### 🔴 Список выката пересчитан — файлов 35, а не 24 - -Прежний промт называл **24 файла**. Эта смена посчитала командой от последнего известного -выката (`ad7a31271`, 02.08 11:29 МСК, «заголовок объявления на боевой»): - -```bash -git diff --name-only ad7a31271..HEAD -- app/ | grep -v '^app/tests/' -``` - -Получилось **35 файлов приложения** и **ДВЕ миграции**, а не одна: - -- `2026_08_02_000001_add_headline_and_media_to_client_tg_auto_rule.php` (запись **v9.64**) -- `2026_08_02_000002_client_tg_cena_po_media.php` (запись **v9.65**) - -🪤 Расхождение с прежним списком не разбиралось — возможно, там считали иначе. **Перед -выкатом список пересчитать командой заново**, а не брать ни один из двух на веру: между -этим промтом и выкатом ветка может уйти вперёд. - -🪤 В списке есть файлы, которых эта задача не касалась (`AdvertisingCampaignController.php`, -`Mp4Probe.php`, экраны Яндекса) — они приехали пачками 1–3 и слиянием. Это не ошибка списка, -но глазами их стоит просмотреть отдельно. - -### Порядок и красные линии выката - -- 🔴 **полный overlay ЗАПРЕЩЁН** — катить поимённым списком; -- 🔴 `config:cache` / `optimize` — **от `www-data`, НЕ от root**. От root портал ложился на 18 минут; -- 🔴 заливку скрипта и его запуск **не соединять** одной командой — виснет на две минуты; -- 🔴 вывод писать в файл; `echo` из tinker не доезжает. **Нет файла ⇒ скрипт УПАЛ**; -- 🪤 `sudo -u postgres psql` на боевой машине — **СТАРАЯ копия**. Живая база — Managed PG. - -### 🔴 Одна вещь, которую надо проверить на бою перед заливкой - -**Право `USAGE` на счётчике `client_tg_contacts_id_seq` для роли `crm_app_user`.** - -В эту таблицу до сегодня не вставляла ни одна строка кода — значит право ни разу не -проверялось боем. Миграция таблицы (27.07) выдала `SELECT, INSERT, DELETE` **на таблицу**, но -на счётчик не выдала ничего. - -Замер этой смены в тестовой базе: счётчик без права — **но ровно так же выглядит и -`client_tg_campaigns_id_seq`**, в который портал на бою пишет. То есть новых прав работа -скорее всего не требует (на бою счётчики закрывает общий `db/02_grants.sql`), но проверка -дешёвая, а пропущенный grant на бою даёт отказ, которого на dev не видно — на этом уже -обжигались. - -Проверить так: - -```sql -select has_sequence_privilege('crm_app_user', 'client_tg_contacts_id_seq', 'USAGE'); -``` - -Ответ `f` ⇒ выдать: - -```sql -GRANT USAGE, SELECT ON SEQUENCE client_tg_contacts_id_seq TO crm_app_user; -``` - -### Чем кончается смена - -Либо «выкатили, вот что смотрел глазами на бою» — и тогда обновить `ПИЛОТ.md` и написать -промт следующей смене. Либо честное «сделал столько, дальше упёрся вот во что». - ---- - -## Замеры этой смены — все командой, не по памяти +## Замеры прошлой смены — все командой - Телеграм-модуль: `cd app && DB_DATABASE=liderra_testing_zag php artisan test --filter=ClientTg` - — **325 тестов, 0 падений** (было 292: +12 базы номеров, +21 цены по медиа). + — **325 тестов, 0 падений** (было 292). - Экраны: `cd app && npx vitest run` — **244 файла, 1855 тестов, 0 падений**, 3 пропущено. -- Статанализ: `cd app && composer stan` — **0**. Формат: `composer pint -- --dirty` — чисто. -- Проверка типов: `npm run type-check` — **6 ошибок, все в чужих файлах**, столько же было до - работы. Смотреть, не добавилось ли **своего** файла. +- Статанализ `composer stan` — **0**. Формат `composer pint -- --dirty` — чисто. +- Проверка типов `npm run type-check` — **6 ошибок, все в чужих файлах**, столько же было до. +- Сборка `npm run build` — **прошла за 4,39 с**. +- Миграции: `migrate:fresh` — все с нуля; `migrate:rollback --step=2` + `migrate` — туда и + обратно, тариф после круга правильный. 🪤 Гонять из папки `app`, иначе «Could not open input file». -🪤 Один «Errors: 1» в прогоне экранов приходит из `AutopodborManualCompetitorError.spec.ts` — -оборванный сетевой запрос в чужом наборе, был до нас. В одиночку этот файл проходит. +🪤 Один «Errors: 1» в прогоне экранов — из `AutopodborManualCompetitorError.spec.ts`, +оборванный сетевой запрос в чужом наборе, был до нас. В одиночку файл проходит. --- ## Что осталось открытым -1. 🔴 **Числа 0.720 и 0.816 — вывод, а не замер.** Правило «кабинет пишет CPM без НДС» - подтверждено на кампании без медиа (счёт `2231134`), а счёт кампании **с картинкой** - живьём не снимали: шаг «Стоимость» недостижим без загрузки живых номеров. Замерите — - правится одной строкой в миграции и строкой в `client_tg_tariffs`. -2. 🟡 **Пузырь чата поддержки перекрывает кнопку «Дальше»** внизу мастера (координаты: - пузырь 838–894×793–849, кнопка 785–873×792–828). Виджет общий для всего портала — - решение владельца не спрашивали, находка 2 осталась открытой. -3. 🔴 **Стена площадки: минимум 367 номеров**, у самого крупного клиента 112. Теперь клиент - может залить **свою базу** и порог перешагнуть — а значит пойдут живые деньги. Раньше - этой дороги не было вовсе. +1. 🔴 **Числа 0.720 и 0.816 — вывод, а не замер.** Правило «кабинет пишет без НДС» + подтверждено на кампании **без** медиа. Счёт кампании **с картинкой** живьём не снимали: + шаг «Стоимость» недостижим без загрузки живых номеров. Ошибка тут стоит денег на каждой + медийной кампании. Замерите — правится одной строкой в миграции и строкой в таблице. +2. 🔴 **Порог 367 номеров теперь перешагиваем.** У самого крупного клиента 112 номеров из + сделок, но после выката он может **залить свою базу** — и пойдут живые деньги там, где + раньше дороги не было вовсе. +3. 🟡 **Пузырь чата поддержки перекрывает кнопку «Дальше»** внизу мастера (пузырь + 838–894×793–849, кнопка 785–873×792–828). Виджет общий для всего портала. Единственная + находка приёмки, по которой владельца **не спрашивали**. 4. **Чтение вердикта модерации (`read-status`) не проверено ни разу.** Вскроется на первом - живом запуске. + живом запуске: не прочитает робот ответ МТС — кампания провисит 48 часов и уйдёт в «нужен + разбор», деньги при этом не вернутся. 5. **Токен робота засветился в выводе команды** 02.08 (в файлы и коммиты не попал). Перевыпустить, когда будет удобно. +6. 🟡 **Битая ссылка в `CLAUDE.md`** на `лендинг/TZ_landing_v1_0.md` — файла нет ни в этой + ветке, ни в main. Сломано до нас, роняет сторожа ссылок на каждом пуше. ## ⏸ Отложено владельцем — НЕ поднимать @@ -180,60 +155,75 @@ GRANT USAGE, SELECT ON SEQUENCE client_tg_contacts_id_seq TO crm_app_user; ## Ловушки -### Главная ловушка этой работы +### Главные 🔴 **Зелёные тесты не увидели ни одной из пяти находок.** Все вскрыл взгляд на живой экран. Прежде чем говорить «работает», **открой и посмотри**. -🪤 **Незакоммиченная чужая работа выглядит как отсутствие работы.** Эта смена начала с -вопроса «куда делась работа» — а 17 файлов цены по медиа лежали в рабочей папке -`.claude/worktrees/tg-zagolovok`, не в основной. **Ветка стоит в отдельной рабочей папке** — -`git checkout` из корня репозитория на неё не переключится, скажет «already used by worktree». +🪤 **Ветка живёт в ОТДЕЛЬНОЙ рабочей папке** `.claude/worktrees/tg-zagolovok`. `git checkout` +из корня репозитория на неё не переключится — скажет «already used by worktree». Прошлая +смена начала с вопроса «куда делась вся работа», а работа была там. -🪤 **Тест, который сам собирает состояние, охраняет пустоту.** «Моя база номеров» была -покрыта зелёными тестами — каждый сам вписывал контакты через `Contact::create`. До состояния -«контакты появились» не доходил ни один, потому что дойти было нечем. Проверяя источник -данных, спрашивай: **а кто в него пишет?** +🪤 **Незакоммиченная чужая работа выглядит как её отсутствие.** 17 файлов цены по медиа +лежали в рабочей папке непроверенными. Начиная смену — **смотреть `git status` в worktree**, +а не только в корне. -### Правка кода +🪤 **Тест, который сам собирает состояние, охраняет пустоту.** «Моя база» была покрыта +зелёными тестами — каждый сам вписывал контакты. До состояния «контакты появились» не +доходил ни один, потому что дойти было нечем. Проверяя источник данных, спрашивай: **кто в +него пишет?** -- 🪤 **Значки: имени нет в карте — молча вопрос в кружке.** Сторож - `tests/Frontend/znachki-est-v-karte.spec.ts` теперь без единой поблажки: список - «известного долга» пуст. Новое имя `mdi-*` обязано быть в `lucideMap` - (`resources/js/plugins/vuetify.ts`). **Букв в Lucide нет** — `mdi-alpha-*` подбирать по - смыслу, искать точную букву бесполезно. -- 🪤 **После НОВОГО тестового файла на PHP статанализ краснеет** — шум `$this` в Pest, - гасится `app/phpstan-baseline.neon`. Пересобрать `--generate-baseline`, потом **глазами** - сверить diff: только добавления, удалений быть не должно (счётчик `count:` — исключение). -- 🪤 **Имён колонок наизусть не помнить.** Кампании Яндекса — `ad_campaigns`, объявления — - `ad_campaign_banners`, правило авто-телеграма — `client_tg_auto_rule` (единственное - число!), рекламный кошелёк — `ad_wallets`, контакты телеграма — `client_tg_contacts`. -- 🪤 **Статус модерации объявления пишется ПРОПИСНЫМИ** (`ACCEPTED` / `REJECTED`). +### Пуш -### Гит и сторожа +🪤 **Штатно падают два сторожа, оба не на нашей работе:** `gitleaks-full-history` (4 телефона +на ветке СМС, см. дорогу Б) и `lychee-links` (битая ссылка в CLAUDE.md). Отключать на **один** +пуш: `LEFTHOOK_EXCLUDE=gitleaks-full-history,lychee-links git push …` -- 🪤 **Номер записи в `db/CHANGELOG_schema.md` брать как максимум по ВСЕМ веткам плюс один.** - Последняя занятая — **v9.65**. -- 🪤 **Коммитить явными путями.** Машинный `docs/observer/STATUS.md` переписывается хуком - после каждого коммита и молча уезжает в следующий. Снимать `git restore --staged`. -- 🪤 **Штатно падают два сторожа, оба не на нашей работе:** `gitleaks-full-history` и - `lychee-links`. Отключать на **один** пуш: - `LEFTHOOK_EXCLUDE=gitleaks-full-history,lychee-links git push …`. -- 🪤 Сообщение коммита держать в **файле** и звать `git commit -F <файл>` — командная строка - должна быть без скобок. +🔴 **Но не обходить вслепую.** Прошлая смена сперва проверила, что находки чужие и старые: +`gitleaks detect --log-opts="<первый>^..<последний>"` по своим коммитам дал ноль. Обход без +такой проверки — это протаскивание секрета мимо сторожа. -### Опорные точки — добавились новые +🪤 **Коммитить явными путями.** Машинный `docs/observer/STATUS.md` переписывается хуком после +каждого коммита и молча уезжает в следующий. Снимать `git restore --staged`. Хук ещё и +дописывает СВОЙ коммит поверх — не пугаться, что указатель на сервере на один впереди. -К прежнему списку (`tg-tabs`, `tg-wizard`, `tg-reach`, `tg-svodka`, `tg-launch`, `tg-media`, -`help-hint` и остальные) прибавились: +🪤 Сообщение коммита держать в **файле** и звать `git commit -F <файл>` — командная строка +без скобок. + +### Код + +🪤 **Значки: имени нет в карте — молча вопрос в кружке.** Сторож +`tests/Frontend/znachki-est-v-karte.spec.ts` теперь **без единой поблажки**, список долга +пуст. Новое имя `mdi-*` обязано быть в `lucideMap` (`resources/js/plugins/vuetify.ts`). +**Букв в Lucide нет** — `mdi-alpha-*` подбирать по смыслу, точную букву искать бесполезно. + +🪤 **После НОВОГО тестового файла на PHP статанализ краснеет** — шум `$this` в Pest, гасится +`app/phpstan-baseline.neon` через `--generate-baseline`. Потом **глазами** сверить diff: +только добавления; единственное допустимое удаление — строка `count:`. + +🪤 **Имён колонок наизусть не помнить.** Кампании Яндекса — `ad_campaigns`, объявления — +`ad_campaign_banners`, правило авто-телеграма — `client_tg_auto_rule` (единственное число!), +рекламный кошелёк — `ad_wallets`, контакты телеграма — `client_tg_contacts`. + +🪤 **Статус модерации объявления пишется ПРОПИСНЫМИ** (`ACCEPTED` / `REJECTED`). + +🪤 **Номер записи в `db/CHANGELOG_schema.md`** — максимум по ВСЕМ веткам плюс один. +Последняя занятая — **v9.65**. + +### Опорные точки экранов — добавились новые `tg-svodka-srok`, `tg-baza`, `tg-baza-count`, `tg-baza-file`, `tg-baza-clear`, `tg-baza-itog`, `tg-baza-error`, `tg-tariff-kind-*`, `tg-tariff-price-*`, `tg-tariffs-save-error`. -🔴 **`tg-tariff-add` и `tg-tariff-remove-*` больше НЕ существуют** — ступени тарифа убраны, -строк ровно три и они фиксированы. +🔴 **`tg-tariff-add`, `tg-tariff-remove-*`, `tg-calc`, `tg-estimate` больше НЕ существуют.** --- +## Как проверять свою работу — правило этого проекта + +**Зелёный тест не доказывает ничего, пока не видел его КРАСНЫМ.** Прошлая смена проверяла +вырезанием: ломала передачу `media_kind` — красный; ломала расчёт за тысячу — красный; +опустошала список поблажек в стороже значков — красный ровно на 23 имени с адресами файлов. + Работа идёт **пачками**: после каждой владелец делает компакт. Не хвататься за всё сразу.