## Проблема ### Перманентные ошибки ретраятся как транзиентные `src/domain/send/mailer.ts:165-198`: при не-транзиентной ошибке внутренний retry-loop делает `break`, но внешний цикл **всё равно** перебирает все оставшиеся аккаунты, а затем `SendJobService` перепланирует с backoff до `maxAttempts`. Перманентный 5xx (плохой получатель, policy-reject, message-too-big) поэтому запускает `accounts × maxAttempts` полных SMTP-сессий перед dead-letter. ### Теряется причина ошибки `mailer.ts:201`: `throw new Error('All SMTP accounts failed')` отбрасывает исходную причину → `send_jobs.lastError` всегда эта generic-строка, бесполезно для разбора. ## Фикс 1. Смотреть `err.responseCode` (nodemailer его отдаёт); на `>= 500` бросать типизированный `PermanentSendError`, который `SendJobService.processClaimedJob` (`domain/queue/service.ts:103-129`) мапит прямо в `markDead` без ретраев. 2. Сохранять последнюю per-account ошибку: `throw new Error(\`All SMTP accounts failed; last: ${lastErr.message}\`, { cause: lastErr })`. ## Замечание Сокет-таймаут **после** приёма сообщения сервером даёт ретрансмит — at-least-once допускает дубликаты. Код консистентен с задокументированной crash-семантикой; просто нигде не обещать exactly-once.