Files
jellybit/openspec/changes/archive/2026-08-06-ingest-nits/review/checkpoint-1-design.md
T
av d081ef1d30 ingest: закрыты мелочи приёма — вырожденное имя, контракт Result, корреляция add
- имя раздачи нормализуется на границе разбора: вырожденное `-`
  (metainfo.NoName) даёт пустое имя, пробельное схлопывается — сентинел больше
  не доходит ни до контекста распознавания, ни до source_ref, ни до подсказки
  вывода имени
- контракт «на любом пути ошибки приёма результат нулевой» объявлен в ingest и
  удерживается структурно; три транспорта перестали обещать идентификатор,
  которого нет, и коррелируют отказ по request_id
- scoped-логгер загрузки ставится до вызова внешнего сервиса в семи командах
  воркера — записи об отказе qBittorrent и метабаз получили download_id
  и infohash; граница разбора bencode записана в docs/research
2026-08-06 18:20:12 +03:00

13 KiB
Raw Blame History

Чекпоинт 1 — ревью предложения, профиль design

  • Change: ingest-nits
  • База диффа: master (5c79fdfffe9e2ddc78b7ebf9f4cdc3aa83560b2b); кода на ветке на момент прогона не было.
  • Профиль: design. Режим прогона: по графу (все три прохода одной волной — граф профиля плоский, машину не держит никто).
  • Дата: 2026-08-06.

Запущенные проходы и исход

Проход Исход
review-specs 6 находок (2 major, 4 minor) + 5 границ спеки
review-rubric (фаза 1) рубрика из 12 свойств, 8 находок (2 major, 6 minor), 5 promote-кандидатов
review-architecture 2 находки (обе major) + 3 пункта «дешевле переделать до мерджа»

Триаж на этом чекпоинте не запускается — так устроен профиль: находок единицы, каждая либо правит спеку, либо становится записанным вопросом. Сведение сделал оркестратор пайплайна.

Что отработано инлайн

Дедуплицировано по причине; в скобках — кто нашёл.

  1. Фактическое основание N1 было неверным (specs, major, оракул — прогон крафт-входов через metainfo.Load). Библиотека сентинел - не синтезирует: BestName() на раздаче без имени возвращает пустую строку, а NoName присваивается только при авторинге. Значит - доходит до нас лишь от раздачи, объявившей его полем name. Следствия: критерий приёмки постановки выполнялся до изменения; случай «раздача без name» не был покрыт вовсе; неверный комментарий в internal/torrent/torrent_test.go:224 и есть источник исходной ошибки ревью 2026-07-08. → требование, дизайн и предложение переписаны по наблюдению; два входа разведены сценариями и тестами; комментарий теста правится.
  2. Требование identity заявляло больше, чем change делает (architecture и specs, оба major): обязанность формулировалась как «до первого вызова, способного что-либо записать», и поимённо называла cancel/dismiss, которые change сознательно не трогает. Нормативная спека стала бы ложной в момент архивации. → обязанность сужена до вызова внешнего сервиса.
  3. request_id вменялся всем транспортам, а существует только на HTTP (architecture major, specs minor, rubric major). → требование называет ключ поимённо по транспортам; для Telegram зафиксировано отсутствие ключа как сегодняшнее состояние, вопрос записан в design.md → Open Questions. Регрессии нет: res.DownloadID уже сегодня всегда пуст.
  4. Telegram числился среди ext.*-клиентов, которых у него нет (specs, minor). → убран; перечень клиентов не дублируется, а отдан конвенции.
  5. Третий вызов приёма (handleUIAdd, веб-форма) отсутствовал в задачах (specs, minor). → добавлен пункт 2.4; иначе требование было бы выполнено на две трети.
  6. Заявленная однородность с фильтром magnet-заглушек ложна (specs, minor): у magnet заглушка dn фильтруется только в контексте, а в подсказку имени уходит. → различие названо явно, распространение на magnet объявлено отдельным изменением.
  7. Спека утверждала абсолют «ниже по потоку сентинел не встречается», а путь файла его выпускает (rubric, minor). → нормализуемые поля перечислены поимённо, про пути файлов сказано, почему они не нормализуются.
  8. Имя с переводом строки подменяет строки построчного контекста (rubric, major). → нормализация расширена на разделители строк и краевые пробелы тем же oneLine, что уже применён к комментарию. В требовании прямо сказано, что защитой от инъекции в промпт это не является: пользовательский текст многострочен по замыслу.
  9. Контракт «результат пуст» держался перечнем веток (rubric, minor). → удерживается структурно: именованный возврат + один defer; тест сравнивает результат с нулевым значением целиком.
  10. У третьего потребителя DisplayName (подсказка namer) не было оракула (rubric, minor). → добавлен пункт 1.5, тест в internal/worker.
  11. Тест записи о Fails. проверял только наличие полей (rubric, minor). → добавлена негативная половина: passkey отправленного magnet в записи не встречается. Инвариант «секреты не в логи» — major.
  12. Сценарий требовал infohash там, где тело требования смягчало до «когда известен» (specs, граница спеки). → в GIVEN добавлено «с известным инфохэшем».
  13. Каталог docs/research/ тихо расширялся с чужих данных на чужой код (architecture). → вводная README расширяется, записка получает условие устаревания.

Записанные вопросы

Оба — в design.md → Open Questions, оба с вариантами, ценой и рекомендацией.

  1. Нужен ли Telegram-транспорту собственный корреляционный ключ у отказа приёма. Рекомендация — поднять запись отказа разбора до INFO с infohash отдельной задачей, а не заводить второй канал корреляции.
  2. Приводить ли Cancel/Dismiss к форме ctx = w.scoped(…). Рекомендация — отдельной задачей-гигиеной, когда в них появится первый внешний вызов.

Урожай (заведение задач — не работа пайплайна)

  • Заглушка dn вида *-topic-<id> уходит в подсказку вывода имени, хотя из контекста отфильтрована (worker.sourceAddPartsmagnet.ParseDisplayName, internal/worker/worker.go:626). Симметрично N1, но на magnet-ветке. Правка требует изменения действующего требования «Синтез контекста распознавания из полей magnet» (сценарий «Строки-факты не становятся отображаемым именем»), поэтому в границы этой задачи не входит. Оракул: тест на подсказку для голого magnet с заглушкой. Провенанс: specs, change ingest-nits.
  • infohash в записи о внешнем вызове снимается со снимка загрузки, а не с того, с чем вызов ушёл (rubric, low). Сегодня значения совпадают; расхождение возможно у загрузки с несколькими хешами (v1+v2). Класс совпадает с журналом 2026-08-06 (признак снят с одной сущности, действие применено к другой), но стоит здесь только разбора, не данных.
  • Пустой source_ref у torrent-загрузки возможен: .torrent с именем - и без имени присланного файла. Действующее требование велит source_ref быть человекочитаемым референсом, но случая «нет ни того, ни другого» не описывает. Дыра не новая, но изменение проходит ровно по ней. Провенанс: specs, границы спеки.
  • Отказ разбора источника пишется на DEBUG (internal/ingest/ingest.go), то есть на боевом INFO не пишется вовсе — для Telegram-пути это означает, что у отказа приёма нет ни ключа у пользователя, ни записи в журнале. Связано с записанным вопросом 1.
  • Promote candidates (rubric): (а) «scoped-логгер загрузки строится один раз, на входе публичной команды воркера» → docs/conventions/logging.md плюс механизация тестом-перебором; (б) «нормализация недоверенного имени на границе — это вырожденные значения, краевые пробелы и разделители строк» → docs/review.md, «Парсер недоверенного входа»; (в) «у каждого транспорта назван свой корреляционный ключ публичного канала» → docs/conventions/errors.md; (г) «тест клиента внешнего сервиса проверяет и отсутствие секретосодержащих значений» → docs/conventions/logging.md.

Границы покрытия

  • Гейт на этом чекпоинте не запускался — кода на ветке не было; это устройство профиля design, а не пропуск.
  • Триаж не запускался — в профиле design его нет по построению; сведение и дедупликацию находок сделал оркестратор пайплайна, то есть тот же, кто писал предложение. Разведённости приёмщика и исполнителя на этом чекпоинте нет.
  • Не запускались проходы code, adversary, ops, reimpl — они не входят в профиль design и работают после apply (чекпоинт 2).
  • Замер аллокаций bencode на этом чекпоинте не воспроизводился ни одним проходом: specs проверил только провенанс ссылок по исходникам библиотеки. Сам замер — задача 4.1, его исход проверяет чекпоинт 2.
  • Ни один проход не открывал живой qBittorrent, LLM и метабазы — запрещено CLAUDE.md → «Запреты».
  • Ценность самой постановки («нужно ли закрывать эти четыре нити») ревью не оценивает ни в одном профиле.