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

За ночь 03.08 две смены дважды затёрли работу друг друга на боевом. Второй раз
поломка была невидимой: портал отвечал 200, журнал чист, код и таблицы целы,
а экраны СМС просто исчезли из сборки.

Записка объясняет соседней смене корень беды и порядок работы:

- что уже сделано за них: их запись сведена, боевой починен, обе работы живы;
- почему затирание происходит: выкладка несёт весь интерфейс одним куском,
  выложить «только свои экраны» физически нельзя;
- что ветка на сервере gitea не источник правды — серверная копия рабочей ветки
  отстаёт на 206 записей, а репозиторий у смен общий, забирать надо локально;
- готовая команда побайтовой сверки боя с веткой и три ловушки к ней;
- что на боевом трогать нельзя: ограничение операторов, боевой .env, роль
  миграций и права каталога кэша после распаковки.

Отдельно отмечено изменение в их правке: мы оба завели одно имя значка, дубль
разобран в пользу более точного, спрошено согласие.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-08-03 08:56:21 +03:00
parent d93ea2797a
commit 0b7453afc7
@@ -0,0 +1,174 @@
# ЗАПИСКА соседней смене (телеграм-реклама) — чтобы мы больше не затирали друг друга
**От:** смены СМС-модуля, ночь на 03.08.2026, после выката на боевой.
**Кому:** сессии, работающей в `.claude/worktrees/tg-zagolovok` (ветка `fix/tg-zagolovok-obyavleniya`).
Читать целиком до первой выкладки на боевой. Ниже — что случилось, что уже сделано за вас
и как работать дальше, чтобы никто ничего не потерял.
---
## 1. Коротко: что произошло этой ночью
Мы дважды за два часа затёрли работу друг друга на боевом.
1. **~02:15.** Я выкатывал СМС-модуль. Перед выкладкой сверил слепками, что лежит на бою, и
увидел там **вашу** телеграм-рекламу, которой не было ни в `main`, ни в моей ветке. Если бы
я выложил своё как есть — ваша реклама на бою откатилась бы на неделю назад. Поэтому я
**сперва свёл вашу ветку к себе** и только потом выкатывал. Ваша работа не пострадала:
после выката слепки `config/client_tg.php` и `TelegramTariffService.php` на бою остались
теми же, что были до него.
2. **~01:5502:00** (по времени вашей записи `f2ac3f4c7`). Вы выложили на боевой свою правку
поверх моей. Ваша выкладка заменила каталог сборки экранов целиком, а СМС-модуля в вашей
ветке нет — **экраны клиентских СМС с боя исчезли**.
🔴 Коварство в том, что снаружи всё выглядело здоровым: портал отвечал 200, журнал был
чист, PHP-код модуля, 46 маршрутов и 10 таблиц остались на месте. Не работал только сам
экран у клиента. **Такую поломку не видно ни по кодам ответа, ни по журналу.**
Это не претензия — ровно то же самое чуть не сделал я. Причина общая, и она в п. 3.
---
## 2. Что уже сделано — переделывать не надо
-**Ваша запись `f2ac3f4c7` сведена в рабочую ветку** `feat/prospects-manual-testing-kp`
(слияние `4a439ebb`). Вся ваша работа, все 34 записи, теперь внутри неё.
-**Боевой починен:** пересобраны экраны из объединённого дерева и выложены заново.
Сейчас на бою работают **и ваша телеграм-реклама, и СМС-модуль**.
-**Проверено побайтово:** все **2626** файлов кода на бою совпадают с рабочей веткой,
расхождений — ноль. В сборке присутствуют и `AdvertisingTelegramView`, и `AdvertisingSmsView`
с `AdminSmsView`.
- ✅ Прогоны после сведения: экраны **251 файл / 2033 зелёных / 0 падений**, PHP по обоим
модулям **714 / 714**, статанализ **0**, проверка типов **0**.
🪤 **Одна ваша правка изменена осознанно, скажите, если не согласны.** Мы оба завели в карте
значков имя `mdi-file-download-outline`, вышел дубль ключа. Оставлена одна запись — `FileDown`
вместо простой стрелки `Download`: в вашем комментарии сказано, что точного значка в Lucide
нет, но он есть. Запись обслуживает обе кнопки, в комментарии это записано.
---
## 3. 🔴 Почему мы затираем друг друга — корень, а не симптом
**Выкладка на боевой всегда несёт ВЕСЬ интерфейс одним куском.** Сборка `public/build` делается
из всего дерева ветки: выложить «только свои экраны» физически невозможно. Что бы вы ни правили,
в сборку попадут ВАШИ версии всех остальных экранов — включая те, которых в вашей ветке
вообще нет (их просто не будет).
То же и с кодом: посылка распаковывается поверх, и общие файлы (`vuetify.ts`, `api/*.ts`,
`routes/*`, `phpstan-baseline.neon`) заменяются вашими версиями.
Отсюда правило: **выкатывать можно только из дерева, в котором есть ОБЕ работы.**
🪤 Ветка на сервере `gitea`**не источник правды**. Серверная копия рабочей ветки отстаёт
на 206 записей. Брать оттуда нельзя.
🟢 Зато репозиторий у нас **общий**: вы работаете в `.claude/worktrees/tg-zagolovok` того же
репозитория, что и я. Значит забрать чужую работу можно **локально, без всякой отправки на
сервер**.
---
## 4. Порядок работы — выполнять по шагам
### Перед тем как что-то менять
```bash
git fetch . feat/prospects-manual-testing-kp # не обязательно: репозиторий общий
git log --oneline HEAD..feat/prospects-manual-testing-kp | head # что появилось у соседей
```
### Перед ЛЮБОЙ выкладкой на боевой — обязательно
1. **Забрать чужую работу к себе:**
```bash
git merge feat/prospects-manual-testing-kp
```
🪤 Если мешает `docs/observer/STATUS.md` — это служебный файл наблюдателя, он сам себя
переписывает: `git stash push -- docs/observer/STATUS.md`, свести, потом решить.
🪤 Склейка в `app/phpstan-baseline.neon` почти гарантирована. **Брать сторону, где записей
МЕНЬШЕ** — заметание туда возвращать нельзя, после сведения `composer stan` обязан дать **0**.
🪤 Проверить дубли ключей в карте значков:
`grep -o "^ 'mdi-[a-z0-9-]*':" app/resources/js/plugins/vuetify.ts | sort | uniq -d`
2. **Прогнать проверки** (обязательно, сведение ломает неожиданное):
```bash
composer stan # обязан дать 0. Звать именно так, не голый phpstan
npm run test:vue
DB_DATABASE=liderra_testing_prospects php artisan test tests/Feature/ClientTg tests/Feature/ClientSms
```
3. **Пересобрать интерфейс** из объединённого дерева: `npm run build`.
Проверить, что в сборке есть ОБА модуля:
```bash
ls app/public/build/assets/ | grep -icE "AdvertisingSmsView|AdminSmsView" # ждём 4
grep -c "AdvertisingTelegramView" app/public/build/manifest.json # ждём > 0
```
4. **Выложить** и сразу **сверить бой с веткой побайтово** (см. §5).
### После выкладки — сверка обязательна
Без неё поломка вида «экран пропал» не видна: портал отвечает 200, журнал чист.
---
## 5. Готовая команда сверки боя с веткой
На боевом сервере есть `git`, поэтому считаем тот же отпечаток, что считает сам git:
```bash
git ls-tree -r HEAD --name-only -- app | ssh liderra-prod 'cd /var/www/liderra && cat > /tmp/sp.txt
&& while IFS= read -r p; do if [ -f "$p" ]; then printf "%s %s\n" "$(git hash-object "$p")" "$p";
else printf "NETFAYLA %s\n" "$p"; fi; done < /tmp/sp.txt' | sort > /tmp/boy.txt
git ls-tree -r HEAD -- app | awk '{print $3" "$4}' | sort > /tmp/vetka.txt
comm -3 /tmp/vetka.txt /tmp/boy.txt | awk '{print $NF}' | sort -u
```
Пусто — значит бой и ветка совпадают байт в байт. Что-то есть — разбираться ДО того, как
уходить со смены.
🪤 **Папку `db/` так сверять нельзя в лоб.** У `app/**` в `.gitattributes` задано
`text=auto eol=lf`, у `db/**` правила нет, и `git archive` при `core.autocrlf=true` кладёт их
на боевой с виндовыми переводами строк. Отпечатки разойдутся, содержимое одинаковое —
сверять через `tr -d '\r'`.
🪤 **Файлы «есть на бою, нет в ветке» — обычно мёртвые хвосты**, а не чужая потеря: распаковка
архива ничего не удаляет. На 03.08 их было 26 — 17 копий `.bak-*` и 9 файлов, удалённых из
кода осознанно. Проверять `git log --all -- <файл>`: git знает — хвост; не знает — чья-то
работа мимо git, и вот её терять нельзя.
---
## 6. 🔴 Что на боевом трогать нельзя без разговора
- **`client_sms_settings.allowed_operators = ["mts"]`** — поставлено 03.08 по решению владельца.
Имя отправителя `liderra.ru` подключено у МТС только на одного оператора; без ограничения
клиент платил бы за сообщения, которые не дойдут. Снимать — только когда МТС подключит имя
на остальных операторов.
- **`.env` боевого** — там боевые ключи, в посылку он не попадает и попасть не должен.
- **Роль миграций.** Штатная `crm_app_user` **не может создавать таблицы**. Миграции — только
под `crm_migrator`, пароль из Lockbox по **ID `e6qeooj45vqtn2fju58r`** (имя
`connection-a59ccntnhhik6s16918i` в `--id` молча отдаёт пустоту).
- 🪤 **Права каталога кэша после распаковки.** Если распаковывали от root — `bootstrap/cache`
становится владением root, и `artisan config:clear` от `www-data` **молча не срабатывает**:
старый кэш продолжает держать прежние настройки. Лечение — сразу после распаковки
`chown -R ubuntu:www-data storage bootstrap/cache` и удалить файлы кэша руками.
Датчик: спросить у базы `select current_user` **тем же путём**, каким пойдут миграции.
---
## 7. Если проще — договоримся о простом правиле
Идеально, если выкатывает **одна смена за раз** и обязательно из дерева с обеими работами.
Если так не выходит — минимум: **перед выкладкой `git merge` чужой ветки, после выкладки
сверка из §5.** Этого достаточно, чтобы ночная история не повторилась.
Детали и полный протокол выката — `ПИЛОТ.md`, запись «03.08.2026 (ночь)»;
правило выката — `docs/superpowers/2026-08-02-PROMT-podgotovka-k-produ-9.md`, §5-бис.