## ADDED Requirements

### Requirement: Синтез контекста распознавания из полей magnet

При приёме система SHALL извлекать из полей magnet-ссылки дополнительный
контекст и **дополнять** им контекст, пришедший из транспорта: факты из полей
SHALL добавляться к пользовательскому тексту (пользовательский текст —
первым), а при пустом тексте SHALL становиться единственным контекстом.
Синтез SHALL выполняться только из самой ссылки, без сетевых запросов.

В синтез SHALL включаться следующие поля, когда они присутствуют:

- `dn` (display name) — как строка названия релиза, **если** это содержательное
  имя, а не заглушка-идентификатор вида `*-topic-<id>` (например
  `rutracker-topic-6514485`); такие заглушки в контекст-название включаться
  SHALL NOT.
- `xl` (exact length) — как человекочитаемый размер (например «≈ 2.1 GiB»);
  нечисловое/некорректное значение игнорируется.
- `tr`/`xs` (трекеры / exact source) — как сигнал происхождения по домену
  (хост трекера/источника), помогающий определить язык и тип контента.
- `kt` (keyword topic) — как ключевые слова.

Обогащённый контекст система SHALL сохранять в `download.Context` — его читают
recognition (LLM-промпт) и веб-UI (страница загрузки). Синтез и слияние SHALL
выполняться до создания загрузки.

Синтезированные строки-факты (размер, домен трекера, ключевые слова) SHALL NOT
влиять на вывод отображаемого имени (`download.display_name` / параметр
`rename` qBittorrent): вход вывода имени остаётся прежним (пользовательский
текст и подсказка `dn`), см. требование «Отображаемое имя торрента из
контекста».

Приём **только по magnet** (пустой текст контекста) SHALL быть штатным
сценарием во всех транспортах (HTTP, Telegram, CLI).

#### Scenario: dn — содержательное имя релиза

- **WHEN** magnet содержит `dn` с релиз-именем (например
  `Dune.Part.Two.2024.2160p.BluRay`)
- **THEN** это имя добавляется в `download.Context`
- **AND** становится доступно recognition

#### Scenario: dn — заглушка-идентификатор темы

- **WHEN** `dn` имеет вид `*-topic-<id>` (например `rutracker-topic-6514485`)
- **THEN** система не включает его как строку-название в контекст
- **AND** остальные поля magnet (размер, домен трекера) всё равно синтезируются

#### Scenario: Размер из xl

- **WHEN** magnet содержит корректный числовой `xl`
- **THEN** в `download.Context` добавляется человекочитаемый размер загрузки

#### Scenario: Происхождение по домену трекера

- **WHEN** magnet содержит `tr` и/или `xs` с распознаваемым хостом
- **THEN** в `download.Context` добавляется сигнал происхождения (домен),
  пригодный как подсказка языка/типа контента

#### Scenario: Дополнение непустого пользовательского контекста

- **WHEN** транспорт передал непустой текст контекста, а magnet несёт поля
- **THEN** `download.Context` содержит и текст пользователя, и факты из полей
  magnet (текст пользователя не теряется)
- **AND** текст пользователя идёт первым

#### Scenario: Приём только по magnet

- **WHEN** magnet принят с пустым текстом контекста
- **THEN** приём проходит штатно
- **AND** `download.Context` синтезируется из полей magnet и сохраняется

#### Scenario: Строки-факты не становятся отображаемым именем

- **GIVEN** голый magnet с заглушкой `dn=rutracker-topic-<id>` и трекером, без
  пользовательского текста
- **WHEN** выполняется приём
- **THEN** `download.display_name` не выводится из строк-фактов (не равен
  `Трекер: …`/`Размер: …`)
- **AND** отображаемое имя определяется прежним путём (подсказка `dn` или его
  отсутствие → без `rename`)

#### Scenario: Синтез без сети

- **WHEN** выполняется извлечение контекста из полей magnet
- **THEN** не делается ни одного сетевого запроса (только разбор строки
  ссылки)

#### Scenario: Полей для контекста нет

- **WHEN** текст пользователя пуст и ни одно пригодное поле magnet не даёт
  содержательного контекста (нет `dn`-имени, `xl`, распознаваемого домена,
  `kt`)
- **THEN** `download.Context` остаётся пустым
- **AND** приём проходит штатно (пустой контекст допустим)
