## Why

Ревью приёма 2026-07-08 оставило четыре нити, каждая из которых по отдельности
дёшева, а вместе они портят три разных наблюдаемых поверхности: контекст
распознавания (грязная строка), комментарии в коде (описывают контракт, которого
уже нет), журнал (запись о неуспешном добавлении без корреляции) и знание о
границе чужой библиотеки (не записано нигде). Пока нити открыты, каждый
следующий проход ревью тратит внимание на то, что уже разобрано.

## What Changes

- **N1 — вырожденное имя не покидает разборщик.** Раздача может объявить полем
  `name` значение `-` — конвенция «имени нет» (в библиотеке разбора это
  константа `metainfo.NoName`). Сейчас `.torrent`-приём отбрасывает `-` только
  для `source_ref`, а `torrent.Info.Context()` и подсказка имени для `namer`
  берут его как содержательное название. Нормализация переезжает на границу
  разбора: `DisplayName` пустеет прямо в `torrent.Parse`, и три места ниже по
  потоку перестают знать про вырожденное значение. Тем же местом схлопываются
  разделители строк в имени — иначе имя добавляет в построчный контекст строку,
  выглядящую как синтезированный нами факт.
  **Основание нити уточнено ревью предложения:** библиотека `-` не синтезирует
  (`BestName()` на раздаче без имени возвращает пустую строку), поэтому речь о
  разборе объявленного значения, а не о фильтре чужого сентинела; случай
  «раздача без поля `name`» уже отрабатывается верно и получает свой сценарий,
  чтобы впредь не путаться с первым.
- **N3 — комментарии описывают фактический контракт `Ingest`.** После
  fast-catch-рефактора `Ingest` возвращает нулевой `Result` на **каждом** пути
  ошибки, а комментарии в `httpapi` и `tgbot` до сих пор обещают непустой
  `DownloadID` «при сбое после создания задачи». Контракт закрепляется в
  доке `Ingest`, удерживается структурно (обнуление результата одним `defer`, а
  не аккуратностью каждой ветки), подтверждается тестом и перестаёт
  пересказываться неверно на транспортах. Вызовов приёма три, а не два:
  REST, веб-форма и Telegram.
- **N4 — запись о неуспешном добавлении в qBittorrent коррелируется.** Клиент
  `qbt` берёт логгер из `ctx` и своего инфохэша не знает (и знать не должен —
  разбор источника живёт в `magnet`/`torrent`). Путь `worker.Retry` не кладёт в
  `ctx` scoped-логгер загрузки, поэтому запись `Fails.` с этого пути уходит без
  `download_id`/`infohash`. Retry приводится к тому же виду, что и `Delete`.
  **Ревью изменения показало, что таких команд не одна, а семь** — те же
  `Relink`, `Rerecognize`, `Refine`, `ChooseCandidate`, `AddManualSource`,
  `SetProviderID` зовут qBittorrent или метабазу до постановки логгера.
  Починены все семь: иначе требование `identity` архивировалось бы ложным.
- **N5 — граница чужой библиотеки записывается наблюдением.** `anacrolix/torrent`
  аллоцирует объявленную bencode-строку до её чтения, с потолком ~128 MiB.
  Правке не подлежит (чужой код), поэтому исход — запись в `docs/research/` с
  провенансом и замером, а не изменение кода.

## Capabilities

### New Capabilities

Нет — изменение не вводит нового поведения и новых понятий.

### Modified Capabilities

- `ingest`: приём `.torrent` перестаёт считать вырожденное имя `-`
  содержательным — ни в синтезированном контексте, ни в подсказке имени, ни в
  `source_ref`; плюс фиксируется контракт «на ошибке приёма результат пуст» и
  поимённо называется корреляционный ключ отказа на каждом транспорте.
- `identity`: корреляция в журнале распространяется на записи о вызовах внешних
  сервисов, сделанных в контексте загрузки, — включая путь retry.

## Impact

- `internal/torrent` — нормализация `DisplayName` на границе разбора; тест на
  безымянной раздаче.
- `internal/ingest` — упрощение фильтра `source_ref`, дока и структурная
  гарантия контракта `Result`, тест «на ошибке результат нулевой».
- `internal/httpapi` (REST `handleAPIAdd` **и** веб-форма `handleUIAdd`),
  `internal/tgbot` — комментарии и передача пустого id в диагностику вместо
  заведомо пустого `res.DownloadID`.
- `internal/worker` — `Retry` кладёт scoped-логгер загрузки в `ctx`.
- `internal/qbt` — тест с подставным сервером на поля записи о неуспешном `add`.
- `docs/research/` — новая записка про границы разбора `.torrent` (предел
  `MaxStrLen` bencode + наблюдение о `BestName`/`NoName`); строка в индексе
  `docs/research/README.md` и полстроки в его вводной.
- Схема БД, конфиг, API и тексты Telegram не затрагиваются; нумерованных
  артефактов (миграций, ADR) изменение не добавляет.
