Разговор обзвона, начатый при пустом счёте, теперь списывается целиком, счёт
уходит в минус, и долг остаётся за клиентом — решение владельца Р38, требование
Т61а. Числа из спеки: было 12 рублей, списали 102 — на счету минус 90, а в
летописи одна строка на 102.
Разрешение выдано поимённо, белым списком: только ai_call. У яндекса, СМС и
телеграма поведение не изменилось ни на копейку — списание по-прежнему упирается
в ноль. Доказано A/B на одном дереве: 424, 425 и 330 тестов трёх каналов дали
ровно те же числа и с правкой, и без неё.
Проверка платёжеспособности научилась называть причину остановки: долг по
обзвону и непокрытые заморозки — разные вещи. Сам вердикт isSolvent не изменён
ни байтом: по решению Р56 минус гасит клиенту всю рекламу, и письмо задачи З-1.7
обязано назвать обе остановки сразу. Различать нужно, чтобы назвать, а не чтобы
смягчить приговор.
Попутно, и это касается уже работающих денег рекламы: возврат refund был
единственным из пяти методов, кто не ставил контекст клиента. Замерено запуском,
а не чтением — под боевой ролью без контекста метод падал ModelNotFoundException,
а не молчал. Живым деньгам не грозило: единственный зовущий, PollClientSmsDelivery
Command, ставит контекст сам во внешней транзакции. Теперь ставит и сам метод.
Обрезка нулём для трёх старых каналов сохранена намеренно, но перестала быть
молчаливой: на ней пишется предупреждение ad_wallet.charge_clamped_to_zero. До
сих пор съеденная разница между летописью и остатком пропадала без следа.
Сторожа: tests/Feature/Obzvon/AdWalletMinusTest.php и AdWalletMinusRaceTest.php.
Все показаны красными пятью поломками. Гонка проверена параллельным прогоном
шести и четырёх настоящих процессов, а не рассуждением: без замка по строке
кошелька теряется пять списаний из шести.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>