## ADDED Requirements

### Requirement: Добавление пойманной загрузки в qBittorrent

Worker SHALL периодически (в поллинг-цикле, под единой блокировкой переходов)
подхватывать загрузки в состоянии `catched` и для каждой: вывести отображаемое
имя из контекста (см. `ingest` «Отображаемое имя торрента из контекста»),
добавить источник в qBittorrent (категория `qbittorrent.category`, savepath,
`rename`) и перевести загрузку `catched → downloading`. Отдельного состояния
между `catched` и `downloading` быть SHALL NOT — успешный `add` сразу переводит
в `downloading` (которое и означает «в qBit, возможно `metaDL`»).

Неуспешный `add` (qBittorrent недоступен и т.п.) SHALL оставлять загрузку в
`catched` для повторной попытки на следующем тике; переход в терминальное
состояние по единичному сбою происходить SHALL NOT (ретраи — естественными
тиками поллинга).

Медленные вызовы (вывод имени через LLM, `qbt.Add`) SHALL выполняться **вне**
блокировки сериализации переходов, чтобы не задерживать команды транспортов и
поллинг. Под блокировкой сериализуется только **запись перехода** `catched →
downloading` (см. «Переходы состояний сериализуются воркером»), с
ре-валидацией, что загрузка всё ещё в `catched` (иначе переход отклоняется —
например, при параллельной отмене).

#### Scenario: Пойманная загрузка добавляется в qBittorrent

- **GIVEN** загрузка в состоянии `catched`
- **WHEN** worker обрабатывает тик
- **THEN** выводится отображаемое имя, источник добавляется в qBittorrent с
  нашей категорией и `rename`
- **AND** загрузка переходит в `downloading`

#### Scenario: Временный сбой добавления — повтор

- **GIVEN** загрузка в `catched`, qBittorrent временно недоступен
- **WHEN** worker пытается добавить источник и `add` не удался
- **THEN** загрузка остаётся в `catched`
- **AND** на следующем тике попытка добавления повторяется

#### Scenario: Отмена во время добавления

- **GIVEN** загрузка в `catched`, worker выводит имя и добавляет её вне
  блокировки
- **WHEN** параллельно приходит команда отмены (`catched → cancelled`), а затем
  worker берёт блокировку для записи перехода
- **THEN** ре-валидация видит, что загрузка уже не в `catched`, и переход в
  `downloading` не применяется

### Requirement: Предохранитель зависшего catched

Система SHALL переводить загрузку, задержавшуюся в `catched` дольше
`catch_timeout` (конфигурируемый предохранитель, дефолт консервативный), в
`failed` (`error_code` `qbit_add`) и уведомлять автора. Возраст SHALL считать
от времени попадания в `catched` (создания загрузки). Предохранитель —
редкий страховочный механизм на случай устойчивой недоступности qBittorrent, а
не штатный путь.

#### Scenario: catched висит дольше таймаута

- **GIVEN** загрузка в `catched` дольше `catch_timeout`
- **WHEN** идёт тик поллинга
- **THEN** загрузка переходит в `failed` с `error_code` `qbit_add`
- **AND** автор загрузки уведомляется

### Requirement: catched не считается пропажей раздачи

Система SHALL исключать состояние `catched` из проверок «раздача не найдена в
qBittorrent» — как в поллинге активных загрузок, так и в сверке рассинхрона
(`state-reconciliation`). У пойманной загрузки раздачи в qBittorrent ещё нет по
дизайну, поэтому её отсутствие система SHALL NOT трактовать как рассинхрон,
`orphaned` или пропажу источника.

#### Scenario: Отсутствие раздачи у catched — не рассинхрон

- **GIVEN** загрузка в `catched` (раздачи в qBittorrent ещё нет)
- **WHEN** идёт тик поллинга и сверки
- **THEN** загрузка не считается пропавшей/рассинхронизированной и остаётся в
  `catched` (до добавления воркером или срабатывания `catch_timeout`)
