ingest: закрыты мелочи приёма — вырожденное имя, контракт Result, корреляция add

- имя раздачи нормализуется на границе разбора: вырожденное `-`
  (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше
  не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки
  вывода имени
- контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и
  удерживается структурно; три транспорта перестали обещать идентификатор,
  которого нет, и коррелируют отказ по request_id
- scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах
  воркера — записи об отказе qBittorrent и метабаз получили download_id
  и infohash; граница разбора bencode записана в docs/research
This commit is contained in:
av
2026-08-06 18:20:12 +03:00
parent 52615e4e49
commit d081ef1d30
30 changed files with 2190 additions and 63 deletions
+29
View File
@@ -276,3 +276,32 @@ merge-раскладка при повторном добавлении разд
асимметрию признака владения. Сам дефект — задачей в беклоге, кандидат
`critical`; спека `state-reconciliation` в том же изменении перестала
утверждать, что уборка «данных пользователя не касается».
## 2026-08-06 — нормализация имени раздачи сама производила сентинел, который отбрасывала [пойман]
- **Где:** `internal/torrent/torrent.go`, функция `displayName` (введена
изменением `ingest-nits`). Норма — `openspec/specs/ingest/spec.md`, требование
«Вырожденное имя раздачи не считается именем».
- **Симптом:** найден на ревью изменения, до мерджа. В эксплуатации не был.
- **Причина:** сравнение с `metainfo.NoName` стояло **до** схлопывания
пробельного. Имя `" - "` сравнение не проходило, а `oneLine` превращал его
ровно в `-`, и вырожденное значение уезжало вниз по потоку всеми тремя
путями: строкой названия в контексте распознавания, в `source_ref` (фолбек на
имя присланного файла не срабатывал — строка непуста) и подсказкой вывода
отображаемого имени. Против `master` это **регрессия**: там стояло
`strings.TrimSpace(...)` перед сравнением с `-`, и фолбек работал.
- **Чем воспроизведён:** двумя независимыми падающими тестами — `adversary`
прогнал приём на входах `" - "`, `"-\n"`, `"\t-"`, `" -"` и получил
`source_ref = "-"` вместо `Dune.torrent`; `reimpl` принёс свой тест на разбор.
В дереве остался табличный `TestParseNoNameSentinelDropped`.
- **Почему не поймали раньше:** ловить было нечему — дефект внесён этим же
изменением и пойман тем же прогоном. Отмечено потому, что это **эвал-сет
наоборот**: случай, где верхняя ступень окупилась. Три прохода из семи
(`specs`, `adversary`, `reimpl`) нашли его независимо, и двое принесли оракул;
проход `code` (конвенции) и гейт его не видели — порядок двух операций внутри
функции не выражается ни правилом линтера, ни конвенцией.
- **Что меняем:** ничего в конвейере. Класс «нормализация и сравнение с
константой идут в неверном порядке» дешевле ловить тестом на границе разбора,
чем правилом; такой тест заведён. Наблюдение о самой библиотеке (что
`BestName()` сентинел **не** синтезирует — исходное основание нити было
неверным) записано в `research/torrent-bencode-limits.md`.