fix(прогрев): новые стадии воронки не выключают рекламу молча

До правки decide на незнакомой стадии возвращал unknown_stage — реклама на
такие фирмы встала бы без единого сообщения. Теперь обе новые стадии идут
по правилу переговоров: крутим до созвона, потом ещё срок просрочки.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-31 11:15:42 +03:00
parent 569010318b
commit 79697217aa
3 changed files with 52 additions and 7 deletions
@@ -54,7 +54,10 @@ class AdAudienceScheduler
return $this->stopped('client_won');
}
if ($stage === 'negotiation') {
// Три стадии с обязательной датой созвона живут по одному правилу: крутим
// до созвона, потом ещё negotiation_overdue_days, дальше стоп. Без этой
// строки новые стадии попали бы в 'unknown_stage' и реклама МОЛЧА встала бы.
if (in_array($stage, ['negotiation', 'manual_testing', 'kp_sent'], true)) {
return $this->negotiation($firm, $now);
}
@@ -72,8 +75,8 @@ class AdAudienceScheduler
}
/**
* Переговоры единственная стадия, где дата созвона обязательна,
* поэтому логика владельца («просрочена ещё три дня стоп») тут полная.
* Правило для стадий с обязательной датой созвона «Переговоры», «Тестирование
* ручное» и «Выслано КП»: логика владельца («просрочена ещё три дня стоп»).
*
* @param array<string, mixed> $firm
* @return array{state: string, reason: ?string, resume_at: ?CarbonImmutable}
@@ -191,3 +191,45 @@ it('слушается изменённых настроек', function () {
'warmup_started_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
], ['warmup_days' => 30])['state'])->toBe('active');
});
it('на ручном тестировании крутит рекламу до созвона, как на переговорах', function () {
$result = decide([
'stage' => 'manual_testing',
'stage_changed_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
'next_call_at' => CarbonImmutable::parse('2026-08-03 12:00:00'),
]);
expect($result['state'])->toBe('active');
});
it('на ручном тестировании останавливает рекламу, когда созвон просрочен больше срока', function () {
$result = decide([
'stage' => 'manual_testing',
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
'next_call_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
]);
expect($result['state'])->toBe('stopped')
->and($result['reason'])->toBe('call_overdue');
});
it('на «выслано КП» крутит рекламу до созвона', function () {
$result = decide([
'stage' => 'kp_sent',
'stage_changed_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
'next_call_at' => CarbonImmutable::parse('2026-08-03 12:00:00'),
]);
expect($result['state'])->toBe('active');
});
it('на «выслано КП» останавливает рекламу, когда созвон просрочен больше срока', function () {
$result = decide([
'stage' => 'kp_sent',
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
'next_call_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
]);
expect($result['state'])->toBe('stopped')
->and($result['reason'])->toBe('call_overdue');
});
+4 -4
View File
@@ -1,6 +1,6 @@
# Brain Status (auto-generated)
Last updated: 2026-07-31T08:10:36.167Z
Last updated: 2026-07-31T08:13:53.842Z
| Контролёр | Состояние | Детали |
|---|---|---|
@@ -112,9 +112,9 @@ Episodes since last run: 542 / threshold: 10
| PID | Имя | CPU-время | Возраст |
|---|---|---|---|
| 3544 | MsMpEng | 17.45ч | 0.0ч |
| 23936 | Code | 5.12ч | NaNч |
| 4 | System | 2.65ч | 12285992.2ч |
| 3544 | MsMpEng | 17.50ч | 0.0ч |
| 23936 | Code | 5.15ч | NaNч |
| 4 | System | 2.66ч | NaNч |
⚠️ Проверь, не «осиротевшие» ли это процессы от завершённых Claude-сессий.