ingest: закрыты мелочи приёма — вырожденное имя, контракт Result, корреляция add
- имя раздачи нормализуется на границе разбора: вырожденное `-` (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки вывода имени - контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и удерживается структурно; три транспорта перестали обещать идентификатор, которого нет, и коррелируют отказ по request_id - scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах воркера — записи об отказе qBittorrent и метабаз получили download_id и infohash; граница разбора bencode записана в docs/research
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
## 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) изменение не добавляет.
|
||||
Reference in New Issue
Block a user