86c560e0c4
Этап 3 «Жизненный цикл и модерация», задача 3.2. Живая отправка робота в МТС означает «ушло на модерацию», а не «запущено»: кампания получает статус `moderating`, а `launched` придёт позже, когда опросчик вердикта (задача 3.4) увидит одобрение. - Campaign.php: константа STATUS_MODERATING + переходы running→moderating, moderating→launched|rejected, rejected→queued (пересдача, задача 3.6). running→launched оставлен ради обратной совместимости. Миграция НЕ нужна: на колонке status нет CHECK-ограничения (машина статусов — в модели), `moderating` влезает в varchar(16). - RunTelegramCampaignJob.finalize(): живой успех (launched=true) → moderating вместо launched; песочница (черновик) по-прежнему → draft_ready. Тест-инфра (побочно, но необходимо для проверки): tests/TestCase.php получил `protected $dropTypes = true`. RefreshDatabase's migrate:fresh дропал таблицы, но НЕ типы Postgres; при заблокированном дропе таблицы её composite row-type переживал db:wipe, и перезагрузка db/schema.sql падала на дубле типа («legal_entities … уже существует»), оставляя ЧАСТИЧНУЮ схему — давний интермиттентный флак «migrate:fresh иногда прерывается» (site_events/ client_tg_tariffs случайно отсутствовали). Дроп типов на каждом refresh резко снизил флак (было 15–46/130 → стало 93–126/130). Только для APP_ENV=testing, на прод не влияет. Остаточный редкий обрыв — отдельная пред-существующая проблема, не этой задачи. TDD, робот замокан, песочница. Приёмка (моя область, чистый прогон): ModeratingStatusTest 8/8 + StatusMachineTest/RunCampaignJobTest/ExternalIdTest/ RobotRunnerTest 35/35 суммарно; phpstan (Campaign+Job) 0, deptrac 0, pint чисто. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>