recognize: название из метабазы санитизируется перед попаданием в план
- чистка стоит на каждой точке входа значения метабазы в план — сборка матча, копия кандидата для ревью, набор закреплённых значений источника и его чтение: гарантия, поставленная только на запись, обходится данными, сохранёнными прежними версиями - название, непригодное как имя каталога (пустое или без единой буквы и цифры), не подставляется — раздача уходит в review с названной причиной - гейт подтверждения матча не сдвинут: сравнение с планом идёт по значениям провайдера, чистится только копия, уходящая дальше
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
# Отчёт ревью — `metadata-title-sanitize`
|
||||
|
||||
Стадия: ревью кода после apply, до archive. Отчёт триажа записан оркестратором
|
||||
(проход `review-triage` пишет только во временный каталог).
|
||||
|
||||
## Сводка
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Размер / сложность / метка | среднее / незнакомое / `large` (максимум по осям) |
|
||||
| Обоснование метки | триггер «заводится или меняется правило идентичности, слияния или разбора — построение целевых путей и санитизация имён» (`docs/review.md` → «Триггеры метки») |
|
||||
| Режим | по графу |
|
||||
| База диффа | `HEAD~1`, рабочее дерево |
|
||||
| Гейт | зелёный, прогнан триажом самостоятельно: 14 шагов `OK`, ни одного `SKIP`, `-race` реально выполнился, diff-coverage 46/46 строк (100%) |
|
||||
| Находок на входе | 16 (+1 замер): specs 4, code 3, architecture 3, adversary 3, ops 3, autotests 0 |
|
||||
| После дедупликации по причине | 11 причин + 1 найденная самим триажом |
|
||||
| Итог | 0 блокирующих, 1 «стоит исправить сейчас», 3 гипотезы, 2 promote |
|
||||
|
||||
Сигнал о заниженной метке: `review-code` возражений не подал. Второго,
|
||||
независимого от него корректора на этом прогоне не было — `basics` не
|
||||
запускался, своих тем проекта не размечено.
|
||||
|
||||
## План разметки и исход по каждой теме
|
||||
|
||||
| тема | дом | глубина | закрывает | исход |
|
||||
|---|---|---|---|---|
|
||||
| requirements | дельты change + `openspec/specs/{metadata-match,recognition}` | разбор | specs | закрыта, 4 находки (minor) |
|
||||
| autotests | `CLAUDE.md` → «Гейт» | — | autotests | закрыта, 0 находок |
|
||||
| conventions | `docs/conventions/*` | разбор | code | закрыта, 3 находки (minor), нарушений конвенций 0 |
|
||||
| architecture | `docs/architecture.md` + источник `docs/passport.md` | доказательство | architecture | закрыта, 3 находки (minor) |
|
||||
| security | `docs/security.md` | доказательство | adversary | закрыта, 2 построенных пути (major) + 1 свойство |
|
||||
| operations | `docs/architecture.md` «Эксплуатация» + источник `docs/database.md` | доказательство | ops | закрыта, 3 постмортема (2 major) + 1 замер |
|
||||
|
||||
**Тем без отчёта нет.** Тем без дома план не содержал.
|
||||
|
||||
## Что стало с каждой причиной
|
||||
|
||||
| # | причина | кто нашёл | статус |
|
||||
|---|---|---|---|
|
||||
| 1 | превью источника расходится с применением | architecture + adversary | закрыта: общий дом пинов `sourcePins`; `TestBuildSources_PreviewMatchesApply` |
|
||||
| 2 | кандидат, лежащий в БД грязным, пиннит несанитизированное название | specs + code + adversary | закрыта: чистка в `sourcePins`; `TestChooseCandidate_DirtyLegacyTitleSanitized` |
|
||||
| 3 | пин, закреплённый до деплоя, читается дословно | ops | закрыта: чистка на чтении в `applyOverrides`; `TestApplyOverrides_LegacyDirtyPinSanitized` |
|
||||
| 4 | предикат пригодности считается дважды из сырого значения | specs + code | закрыта: `decide` читает уже чистое `Match.Title` |
|
||||
| 5 | отказ закрепить название молчит | specs + code | закрыта: атрибут `title_pinned` |
|
||||
| 6 | `Match.Title` остаётся сырым, чистка в двух местах | architecture | закрыта: единственная точка сборки `buildMatch` |
|
||||
| 7 | `specs/review` обещает закрепление безусловно | specs | закрыта: заведена дельта `review` |
|
||||
| 8 | грязный каталог-якорь наследуется дальше | ops | не чинится сознательно, см. H1 |
|
||||
| 9 | название от LLM гейта пригодности не получает | adversary | вне объёма, см. P1 |
|
||||
| 10 | набор пинов пишется пятью транзакциями | ops | пре-существующее, см. H2 |
|
||||
| 11 | `resolveFolderBase` даёт `SCAN` по `recognition` | ops (замер) | пре-существующее, см. H3 |
|
||||
| 12 | режиссёр и название доезжают до карточки с RLO и zero-width | **триаж** | закрыта: `naming.sanitize` снимает Cf |
|
||||
|
||||
## Блокирует мердж
|
||||
|
||||
Пусто. Обе `major`-находки враждебного прохода и обе `major` эксплуатационного
|
||||
отработаны, каждая с падающим-без-правки тестом в дереве. Потолок не срабатывал.
|
||||
|
||||
## Стоит исправить сейчас — 1 находка (отработана)
|
||||
|
||||
### Название и режиссёр из метабазы доезжают до карточки Telegram, шапки веб-UI и имени раздачи с RLO и zero-width
|
||||
|
||||
- Файл: `internal/naming/naming.go`, `sanitize`
|
||||
- Severity: `minor` · Confidence: `high`
|
||||
- Оракул (прогон триажа): `director = "DenisVilleneuve"` →
|
||||
`label = "Dune (DenisVilleneuve, 2021)"`; `naming.sanitize` снимал только
|
||||
C0 (`r < 0x20`) и не трогал категорию Cf.
|
||||
- Последствие: дельта `recognition` этого изменения обосновывает отказ чистить
|
||||
`director` в плане тем, что «его очистка применяется при рендере отображаемого
|
||||
имени». Рендер этой гарантии для Cf не давал — то есть изменение записывало в
|
||||
спеку свойство, которого в коде нет. RLO из credits разворачивает отображение
|
||||
имени в карточке Telegram, которая для единственного пользователя и есть
|
||||
интерфейс.
|
||||
- Найдено проходом: триаж, при добыче оракула на утверждение дельты. Ни один
|
||||
проход этого не заявлял: тема `requirements` сверяла дельту с кодом изменения,
|
||||
а обоснование дельты уводило в пакет, которого дифф не касался.
|
||||
- **Отработано инлайн:** `naming.sanitize` снимает `unicode.Cf`, тест
|
||||
`TestSanitize_StripsFormatChars`.
|
||||
|
||||
## Гипотезы без доказательства
|
||||
|
||||
**H1. Грязный каталог-якорь наследует имя всем последующим раздачам того же
|
||||
матча.** Оракула нет (постмортем-рассуждение), severity снижена до `minor`.
|
||||
Предложение прохода — чистить базу, извлечённую из якоря, — дало бы вторую папку
|
||||
рядом с существующей, то есть исход, против которого затевалась задача.
|
||||
`planBase` и так гоняет `sanitizeComponent` по `FolderBase`, на уровне ФС база
|
||||
безопасна; речь только о невидимке внутри имени существующей папки. Задача не
|
||||
заводится: лечение — переименовать папку руками, записано риском в `design.md`.
|
||||
|
||||
**H2. Набор пинов пишется пятью отдельными транзакциями.** Пре-существующее,
|
||||
диффом не заведено (дифф добавил только присваивание переменной ради лог-атрибута).
|
||||
Окно — миллисекунды между пятью `UPSERT` в локальный SQLite, пути отказа никто не
|
||||
строил. Дом — цель `state-integrity`.
|
||||
|
||||
**H3. `resolveFolderBase` даёт `SCAN` по `recognition` без индекса на
|
||||
`(provider, provider_id)`.** Замер прохода `ops`: 12.9 мс при 100 загрузках и
|
||||
20000 `file_link`. Команды замера в выводе прохода нет, триаж его не
|
||||
воспроизводил — число несётся как заявленное, не как проверенное.
|
||||
Пре-существующее; дом есть — `tasks/items/scale-100-downloads.md`.
|
||||
|
||||
## Promote candidates
|
||||
|
||||
**P1. Гейт пригодности названия стоит у источников, а не на границе `layout`.**
|
||||
Путь построен триажом: `title="-"` → каталог `- (2021)`; `title="…"` → каталог
|
||||
`… (2021)`; `title="..."` → `layout: empty title after sanitization`;
|
||||
`title="/etc/passwd"` → каталог `/etc/passwd (2021)`. Инвариант «целевой путь
|
||||
строго под библиотекой» **не нарушен** (`underRoot` держит, полноширинный `/` не
|
||||
разделитель), «существующее не перезаписываем» тоже (коллизия → review). Отсюда
|
||||
`minor`. Претензия к правилу: `UsableTitle` зовут три вызывающих со стороны
|
||||
метабазы, а настоящая граница — вход в `layout`. Класс тот же, что у задачи
|
||||
`long-title-to-review`; расширить её, а не заводить новую.
|
||||
|
||||
**P2. `ident.Parse` на входных границах остаётся правилом без механизации.**
|
||||
Стоячий разрыв из `CLAUDE.md`, кандидат в `internal/archrules`.
|
||||
|
||||
## Отсев вкусовщины
|
||||
|
||||
По проектному списку «Типовые ложноположительные» не выброшено ничего — ни одна
|
||||
входящая находка под его пункты не подошла. По общим критериям выброшены две:
|
||||
«правило разложено по трём пакетам» (после переноса чистки последствия за
|
||||
пределами чтения не осталось, а отдельный дом правила у `layout` дельта
|
||||
`recognition` легитимизирует дословно) и «атрибут `title_pinned` конфлатит два
|
||||
случая» (исход в обоих один, последствия нет).
|
||||
|
||||
## Границы покрытия
|
||||
|
||||
**Запускалось:** `autotests`, `specs`, `code`, `architecture`, `adversary`, `ops`,
|
||||
`triage` — метка `large`, режим по графу. Стадия ревью дизайна прогонялась
|
||||
отдельно составом `specs`, `rubric`, `architecture`; её находки отработаны до
|
||||
реализации.
|
||||
|
||||
**Не запускалось:** `basics` — своих тем проекта не размечено, принимать нечего;
|
||||
следствие — второго, независимого от `code`, сигнала о заниженной метке не было.
|
||||
Проход независимой реализации в конвейере отсутствует (снят по стоимости),
|
||||
`idiom` упразднён 2026-08-04.
|
||||
|
||||
**Ни один из шести проходов не сообщил свой потолок и величину среза.** По
|
||||
молчанию «показал всё» неотличимо от «показал первые N» — это находка о прогоне.
|
||||
|
||||
**Правки оркестратора не видел ни один проход.** Все шесть отчётов сняты с
|
||||
состояния кода до исправлений; семь закрытых причин — правки, по которым ревью не
|
||||
проводилось. Их проверка сводится к зелёному гейту со 100% diff-coverage, трём
|
||||
новым падающим-без-правки тестам и чтению кода в точках единственности
|
||||
(`buildMatch`, `sourcePins`, `applyOverrides`). Находка №12 — прямое следствие:
|
||||
она живёт в пакете, которого дифф не касался.
|
||||
|
||||
**Не проверит ни один проход** (`docs/review.md` → «Недоступно проверке»):
|
||||
история инцидентов на umbar; поведение SQLite под реальным объёмом и профилем;
|
||||
завязка внешних потребителей (Jellyfin, закладки) на текущее поведение; суждение
|
||||
«этой функциональности не должно существовать»; качество распознавания как
|
||||
такового.
|
||||
|
||||
**Перестали проверять сознательно:** идиоматичность Go — с 2026-08-04, различение
|
||||
«идиоматично против распространено» не спрашивает никто; класс обратимый,
|
||||
пересмотр — задача `quality-review-agents`.
|
||||
|
||||
**Решения и наблюдения проекта прогон не читал:** `docs/adr/` и `docs/research/` —
|
||||
процессные документы. Расхождение изменения с записанным решением ловит сверка
|
||||
документации (`av-dev-docs:healthcheck`), а не ревью.
|
||||
|
||||
**Альтернативной реализации, с которой можно сдиффить решения, у конвейера нет.**
|
||||
На этом изменении — правило разбора и санитизации — именно такой проход на прошлом
|
||||
прогоне приносил независимый оракул.
|
||||
Reference in New Issue
Block a user