docs: документы подняты на канон 7
- review.md переведён на словарь меток: вопросы адресованы темам, триггеры профиля стали триггерами метки в три списка, quick/standard/wide → small/ medium/large, профиль deep упразднён - openspec/config.yaml переписан по канонической форме: адреса passport и CLAUDE.md вместо пересказа правил ревью и конвенций - разобраны находки doc-consistency и doc-code-drift: исключение инварианта сверено со спеками, единая точка времени и таблица classifyErr дополнены, MaxTorrentSize получил дом в database.md
This commit is contained in:
+92
-76
@@ -1,7 +1,7 @@
|
||||
# Ревью: настройка и журнал
|
||||
|
||||
Проектная часть конвейера ревью: чем jellybit отличается от абстрактного
|
||||
Go-сервиса и что здесь уже проскакивало. Устройство самого конвейера (профили,
|
||||
Go-сервиса и что здесь уже проскакивало. Устройство самого конвейера (метки,
|
||||
стадии, контракт находок) живёт в скилле, а не здесь.
|
||||
|
||||
## Как настроен конвейер
|
||||
@@ -93,126 +93,142 @@ Go-сервиса и что здесь уже проскакивало. Устр
|
||||
`original_title` заполняется всегда и при неуверенности дублирует `title` —
|
||||
это контракт capability `recognition`, а не недосмотр.
|
||||
|
||||
### Вопросы к проходам
|
||||
### Вопросы по темам
|
||||
|
||||
Форма: `<имя прохода>: <вопрос> (<провенанс>)`. Журнал дефектов пока пуст,
|
||||
поэтому провенанс у всех пунктов — инвариант или ADR, а не пойманный случай;
|
||||
по мере накопления журнала список должен смещаться в сторону реальных промахов.
|
||||
Форма: `<тема>: <вопрос> (<провенанс>)`. Адресуется теме, а не имени прохода:
|
||||
проход переезжает между метками и упраздняется, тема переезд переживает. Задаёт
|
||||
вопрос тот, кто закрывает тему на текущем прогоне.
|
||||
|
||||
- `adversary`: можно ли, управляя только именами файлов в раздаче и текстом
|
||||
- `security`: можно ли, управляя только именами файлов в раздаче и текстом
|
||||
контекста, добиться целевого пути вне `paths.movies`/`series` — включая путь
|
||||
через юникод, длину сверх лимита ФС и коллизию после нормализации?
|
||||
(инвариант «целевой путь строго под библиотекой», [security.md](security.md))
|
||||
- `adversary`: есть ли последовательность команд, после которой снимается
|
||||
- `security`: есть ли последовательность команд, после которой снимается
|
||||
**последняя** копия данных — с учётом `superseded`-ссылок и гонки со сверкой?
|
||||
(инвариант «источник неприкосновенен»)
|
||||
- `adversary`: что даёт крафт-магнет с чужим или подставным инфохэшем —
|
||||
- `security`: что даёт крафт-магнет с чужим или подставным инфохэшем —
|
||||
присоединение к чужой активной загрузке, отравление владения?
|
||||
(открытая задача про идентичность инфохэшей)
|
||||
- `adversary`: где признак «это наше» снимается с одной сущности, а действие
|
||||
- `security`: где признак «это наше» снимается с одной сущности, а действие
|
||||
применяется к другой — присутствие раздачи в qBittorrent против байтов на
|
||||
диске, запись в БД против файла, инфохэш против содержимого? (журнал,
|
||||
2026-08-06: уборка своего торрента сносила чужие файлы)
|
||||
- `ops`: что делает эта ветка, когда qBittorrent недоступен несколько минут
|
||||
подряд — сколько ERROR-строк в секунду и меняется ли состояние задач?
|
||||
- `operations`: что делает эта ветка, когда qBittorrent недоступен несколько
|
||||
минут подряд — сколько ERROR-строк в секунду и меняется ли состояние задач?
|
||||
(задача про ERROR-шторм фоновых циклов)
|
||||
- `ops`: как это ведёт себя при сотне загрузок в базе и десятках тысяч
|
||||
- `operations`: как это ведёт себя при сотне загрузок в базе и десятках тысяч
|
||||
`file_link` — есть ли запрос без индекса и полный проход по таблице?
|
||||
(задача про масштаб 100/1000, [database.md](database.md) → «Настройки»)
|
||||
- `ops`: что остаётся на диске и в базе, если процесс убит посреди раскладки
|
||||
батча? (состояние `linking` и его восстановление)
|
||||
- `code`: логирующий чекпоинт один на операцию — или ошибка залогирована и
|
||||
возвращена вверх, где залогирована снова?
|
||||
- `operations`: что остаётся на диске и в базе, если процесс убит посреди
|
||||
раскладки батча? (состояние `linking` и его восстановление)
|
||||
- `conventions`: логирующий чекпоинт один на операцию — или ошибка залогирована
|
||||
и возвращена вверх, где залогирована снова?
|
||||
([conventions/logging.md](conventions/logging.md))
|
||||
- `code`: новое поле конфига появилось в `config.example.toml` с описанием
|
||||
назначения, диапазона и единиц? ([conventions/config.md](conventions/config.md))
|
||||
- `specs`: не завелось ли поведение, которого спека не заказывала — тихий
|
||||
- `conventions`: новое поле конфига появилось в `config.example.toml` с
|
||||
описанием назначения, диапазона и единиц?
|
||||
([conventions/config.md](conventions/config.md))
|
||||
- `requirements`: не завелось ли поведение, которого спека не заказывала — тихий
|
||||
дефолт, проглоченная ошибка, ретрай «на всякий случай», отброшенное поле?
|
||||
- `architecture`: не появился ли второй способ делать то, что уже делается —
|
||||
второе место, где генерится время или id, второй парсер источника, вторая
|
||||
логика целевых имён мимо `naming`?
|
||||
([architecture.md](architecture.md) → «Единые точки проекта»)
|
||||
|
||||
### Триггеры профиля
|
||||
### Триггеры метки
|
||||
|
||||
Уточняет умолчания конвейера, не отменяет их. Рабочее умолчание — `standard`;
|
||||
миграция схемы и публичный контракт ступень **не** поднимают: их проверяют
|
||||
проходы, которые в `standard` и так есть.
|
||||
Уточняет умолчания конвейера, не отменяет их. Рабочее умолчание — `medium`;
|
||||
миграция схемы и публичный контракт метку **не** поднимают: их проверяют
|
||||
проходы, которые в `medium` и так есть. Списка три: два поднимают до `large`,
|
||||
по одному на ось, третий опускает до `small`.
|
||||
|
||||
**Новым понятием или структурной единицей здесь считается** (→ `wide`): новый
|
||||
пакет `internal/*`; новая capability в `openspec/specs/`; новый транспорт приёма
|
||||
или уведомлений рядом с REST, веб-UI, ботом и CLI; новый провайдер метабазы за
|
||||
существующим интерфейсом; новое состояние в графе переходов загрузки; перенос
|
||||
ответственности между `worker`, `recognition`, `layout` и `store`.
|
||||
**Крупное здесь** — про объём, сколько узлов и слоёв трогает изменение:
|
||||
|
||||
**Правила идентичности, слияния и разбора живут здесь** (→ `deep`): владение
|
||||
раздачей по инфохэшу и сверка с qBittorrent (`ident`, `internal/store`,
|
||||
`state-reconciliation`); построение целевых путей и санитизация имён
|
||||
(`internal/layout`, `naming`); разбор недоверенного входа — bencode, magnet,
|
||||
текст контекста, ответ LLM (`internal/torrent`, `internal/magnet`,
|
||||
`internal/tgbot/parse.go`, разбор ответа модели); выбор кандидата метабазы и
|
||||
слияние его полей с догадкой LLM (`internal/metadata`, `metadata-match`);
|
||||
merge-раскладка при повторном добавлении раздачи.
|
||||
- перенос ответственности между `worker`, `recognition`, `layout` и `store`;
|
||||
- новое состояние в графе переходов загрузки: оно тянет за собой воркер, спеку,
|
||||
отображение в веб-UI и боте и восстановление после рестарта;
|
||||
- правка, идущая насквозь по цепочке приём → распознавание → раскладка;
|
||||
- новый провайдер метабазы за существующим интерфейсом: клиент, поле конфига с
|
||||
образцом, слияние полей кандидата, ветка «провайдера нет».
|
||||
|
||||
**Незнакомое здесь** — про форму решения, которую предстоит нащупать по ходу:
|
||||
|
||||
- новый пакет `internal/*` или новая capability в `openspec/specs/`;
|
||||
- новый транспорт приёма или уведомлений рядом с REST, веб-UI, ботом и CLI;
|
||||
- заводится или меняется **правило идентичности, слияния или разбора**: ключ
|
||||
владения раздачей и сверка с qBittorrent (`ident`, `internal/store`,
|
||||
`state-reconciliation`); построение целевых путей и санитизация имён
|
||||
(`internal/layout`, `naming`); новый вид входа или новая ветка неоднозначности
|
||||
у разбора недоверенного — bencode, magnet, текст контекста, ответ LLM
|
||||
(`internal/torrent`, `internal/magnet`, `internal/tgbot/parse.go`); новый
|
||||
источник или новый победитель при конфликте в слиянии кандидата метабазы
|
||||
(`internal/metadata`, `metadata-match`); merge-раскладка при повторном
|
||||
добавлении раздачи.
|
||||
|
||||
- «Поведение, видимое снаружи» здесь включает **тексты и карточки Telegram** —
|
||||
для единственного пользователя это и есть интерфейс.
|
||||
|
||||
**Место из списка ступень не поднимает — поднимает правило.** Перечни выше
|
||||
отвечают «здесь такие правила водятся», а не «любая правка здесь идёт в `deep`».
|
||||
Ступень поднимает то, что даёт работу новому проходу: заводится ключ сравнения
|
||||
или меняется его состав; у разбора появляется новый вид входа или новая ветка
|
||||
неоднозначности; в слияние добавляется источник или меняется победитель при
|
||||
конфликте.
|
||||
**Место из перечня метку не поднимает — поднимает правило.** Перечни выше
|
||||
отвечают «здесь такие правила водятся», а не «любая правка здесь идёт в
|
||||
`large`». Метку поднимает то, что даёт работу новому проходу: заводится ключ
|
||||
сравнения или меняется его состав; у разбора появляется новый вид входа или
|
||||
новая ветка неоднозначности; в слияние добавляется источник или меняется
|
||||
победитель при конфликте.
|
||||
|
||||
**Отсекающие условия — проверяются до выбора профиля, любое сработавшее держит
|
||||
ступень внизу.** Перечень закрытый, каждый пункт проверяется взглядом на дифф и
|
||||
дельта-спеку:
|
||||
**Мелкое здесь** — опускает до `small`. Перечень закрытый, каждый пункт
|
||||
проверяется взглядом на дифф и дельта-спеку, любое сработавшее держит метку
|
||||
внизу:
|
||||
|
||||
- **дельта-спека называет исход поимённо.** Независимая реализация окупается
|
||||
выбором, которого спека не сделала. Если сценарий уже говорит, что даёт
|
||||
вырожденный вход, реализация повторит спеку, и дифф покажет расхождение в
|
||||
форме, а не в решении;
|
||||
- **дельта-спека называет исход поимённо** — сценарий уже говорит, что даёт
|
||||
вырожденный вход, и решать в коде нечего;
|
||||
- **новых сценариев в дельта-спеке нет** — изменение уточняет уже описанное
|
||||
поведение, а не заказывает новое;
|
||||
- **правка сообщения, комментария, записи журнала, имени или теста** в узле из
|
||||
перечня;
|
||||
перечней выше;
|
||||
- **сужение уже существующей нормализации** без нового вида входа: вход остался
|
||||
тот же, изменился исход на одном его значении.
|
||||
|
||||
**Ориентир частоты.** `standard` закрывает большинство задач, `wide` — редкий
|
||||
случай, `deep` — исключение на крупной функциональности, а не на уборке. Задача
|
||||
типа `chore` или `bugfix`, собранная из нитей прошлого ревью, идёт в `quick` или
|
||||
`standard`, даже когда трогает файл из перечня `deep`. Верхняя ступень чаще одной
|
||||
задачи на спринт означает ошибку в критерии, а не спринт из сложных задач.
|
||||
Отрицательный тест поверх перечня: что после мерджа не откатывается обратной
|
||||
правкой — миграция, формат на диске, публичный контракт, имя, — **не** `small`,
|
||||
каким бы маленьким ни был дифф.
|
||||
|
||||
**Ориентир частоты.** `medium` закрывает большинство задач, `large` рассчитана
|
||||
на 5–10% и приходится на крупную функциональность, а не на уборку: задача типа
|
||||
`chore` или `fix`, собранная из нитей прошлого ревью, идёт в `small` или
|
||||
`medium`, даже когда трогает файл из перечней выше. `large` чаще одной задачи на
|
||||
спринт означает ошибку в критерии, а не спринт из сложных задач.
|
||||
|
||||
### Недоступно проверке
|
||||
|
||||
**Не проверит ни один проход** — принципиальная граница, по факту промаха не
|
||||
пересматривается.
|
||||
|
||||
- История инцидентов на umbar и то, что уже ломалось в проде.
|
||||
- Поведение таблицы SQLite под реальным объёмом и профилем нагрузки: реального
|
||||
профиля нет ни у кого, кроме сервера.
|
||||
- Завязка внешних потребителей (Jellyfin, закладки, чужие ссылки) на текущее
|
||||
поведение.
|
||||
- Качество распознавания как таковое: правильно ли LLM определил фильм — вопрос
|
||||
тюнинга модели и промпта, а не ревью кода. Размеченный корпус, по которому это
|
||||
можно было бы судить числом, решено не собирать (`tasks/REJECTED.md`,
|
||||
2026-08-06).
|
||||
- Суждение «этой функциональности не должно существовать».
|
||||
- `operations`: история инцидентов на umbar и то, что уже ломалось в проде.
|
||||
- `operations`: поведение таблицы SQLite под реальным объёмом и профилем
|
||||
нагрузки — реального профиля нет ни у кого, кроме сервера.
|
||||
- `architecture`: завязка внешних потребителей (Jellyfin, закладки, чужие
|
||||
ссылки) на текущее поведение.
|
||||
- `architecture`: суждение «этой функциональности не должно существовать».
|
||||
- `requirements`: качество распознавания как таковое — правильно ли LLM
|
||||
определил фильм. Это вопрос тюнинга модели и промпта, а не ревью кода;
|
||||
размеченный корпус, по которому это можно было бы судить числом, решено не
|
||||
собирать (`tasks/REJECTED.md`, 2026-08-06).
|
||||
|
||||
**Перестали проверять сознательно** — пересматривается первым, как только
|
||||
что-то проскочило.
|
||||
|
||||
- **Идиоматичность Go — с 2026-08-04.** Проектный проход `idiom` (поимённая
|
||||
сверка с положениями Effective Go, Go Code Review Comments, стайлгайдов Uber
|
||||
и Google) удалён вместе с проектными копиями агентов при переезде на плагин
|
||||
`av-dev-pipeline`, который этот проход упразднил. Способные части переселены:
|
||||
эксперимент против поведения библиотеки и драйвера — в `ops`, «не изобретаем
|
||||
ли то, что уже есть в библиотеке» — в `architecture`. **Различение
|
||||
«идиоматично против распространено» теперь не спрашивает никто.** Класс
|
||||
обратимый: портит форму кода, не данные. Пересмотр — задача
|
||||
`quality-review-agents`.
|
||||
- `conventions`: **идиоматичность Go — с 2026-08-04.** Проектный проход `idiom`
|
||||
(поимённая сверка с положениями Effective Go, Go Code Review Comments,
|
||||
стайлгайдов Uber и Google) упразднён вместе с переездом конвейера в плагин
|
||||
([ADR-2026-08-04-review-pipeline-to-plugin](adr/ADR-2026-08-04-review-pipeline-to-plugin.md));
|
||||
способные части переселены — в тему `operations` (эксперимент против
|
||||
поведения библиотеки и драйвера) и в `architecture` («не изобретаем ли то,
|
||||
что уже есть в библиотеке»). **Различение «идиоматично против
|
||||
распространено» теперь не спрашивает никто.** Класс обратимый: портит форму
|
||||
кода, не данные. Пересмотр — задача `quality-review-agents`.
|
||||
- `security`, `operations`, `architecture`: на метках `small` и `medium` не
|
||||
проверяется ничто, требующее запуска, — построенных путей атаки, замеров и
|
||||
эксплуатационного постмортема там нет по устройству конвейера. Их даёт только
|
||||
`large`, а она приходится на 5–10% задач.
|
||||
|
||||
## Журнал дефектов
|
||||
|
||||
@@ -272,7 +288,7 @@ merge-раскладка при повторном добавлении разд
|
||||
проход не спрашивал про **асимметрию признака владения** — признак снимается
|
||||
с одной сущности (запись в qBittorrent), а действие применяется к другой
|
||||
(байты на диске). Враждебный проход до этой задачи на данном коде не гонялся.
|
||||
- **Что меняем:** вопрос `adversary` в разделе выше дополнен пунктом про
|
||||
- **Что меняем:** в «Вопросы по темам» добавлен вопрос темы `security` про
|
||||
асимметрию признака владения. Сам дефект — задачей в беклоге, кандидат
|
||||
`critical`; спека `state-reconciliation` в том же изменении перестала
|
||||
утверждать, что уборка «данных пользователя не касается».
|
||||
@@ -296,7 +312,7 @@ merge-раскладка при повторном добавлении разд
|
||||
В дереве остался табличный `TestParseNoNameSentinelDropped`.
|
||||
- **Почему не поймали раньше:** ловить было нечему — дефект внесён этим же
|
||||
изменением и пойман тем же прогоном. Отмечено потому, что это **эвал-сет
|
||||
наоборот**: случай, где верхняя ступень окупилась. Три прохода из семи
|
||||
наоборот**: случай, где старшая метка окупилась. Три прохода из семи
|
||||
(`specs`, `adversary`, `reimpl`) нашли его независимо, и двое принесли оракул;
|
||||
проход `code` (конвенции) и гейт его не видели — порядок двух операций внутри
|
||||
функции не выражается ни правилом линтера, ни конвенцией.
|
||||
|
||||
Reference in New Issue
Block a user