Files
jellybit/openspec/changes/archive/2026-08-10-long-title-to-review/review/report.md
T
av b9f0929d0c layout: непомещающееся целевое имя уводит задачу в review вместо failed
- предел длины компонента (255 байт) проверяется в BuildLinks до первой
  операции с ФС: ни каталога, ни ссылки при отказе не создаётся
- причина пустого предпросмотра считается на показе (ReviewData.PreviewError)
  и печатается в панели действий и в карточке Telegram: у задачи без
  записанной причины взять её больше неоткуда
2026-08-10 12:16:44 +03:00

162 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Ревью изменения `long-title-to-review` — триаж
Отчёт агента `review-triage`. Записан оркестратором: харнесс блокирует запись
файлов подагентами.
## Сводка
- **Размер:** 9 изменённых файлов (+180/−5) плюс новый
`internal/layout/name_length_test.go`. Продуктового кода ~47 строк.
- **Метка:** `medium`, назначена человеком; разметчик дал `small`. Режим — по графу.
- **Сигнал о заниженной метке:** от `review-code` — «`medium` адекватна, `small`
был бы занижен: дифф трогает четыре слоя». Триаж поддержал основанием, которого
сам сигнал не назвал: код причины `name_too_long` оседает в
`downloads.error_code` и обратной правкой после мерджа не откатывается.
- **Гейт:** зелёный целиком, подтверждён `autotests` независимо; ни одного `SKIP`,
`-race` реально прогонялся.
- **Находок на вход:** 14 плюс 1 promote-кандидат. **Осталось:** 5 (2 блокируют,
3 к исправлению), 3 понижены в гипотезы, 2 promote.
### План разметки с исходом
| тема | дом | глубина | кто закрывает | исход |
|---|---|---|---|---|
| requirements | `openspec/specs/` + дельты | разбор | specs | закрыта, 3 находки |
| autotests | `CLAUDE.md` → «Гейт» | — | autotests | закрыта, находок нет |
| conventions | `docs/conventions/` | разбор | code | закрыта, 5 находок (склеены в 3) |
| architecture | `docs/architecture.md` | разбор | basics | закрыта, 1 находка |
| security | `docs/security.md` | разбор | basics | закрыта, находок нет |
| operations | `docs/architecture.md` → «Эксплуатация» | разбор | basics | закрыта, 3 находки |
| темы проекта | — | — | — | своих тем нет |
Тем без отчёта нет.
## Блокирует мердж
### 1. На самом вероятном входе экран ревью не называет причину и теряет «Применить»
- Файл: `internal/worker/review.go:1085-1095`, `:240`,
`web/templates/partials/review_main.html:79`
- Severity: major. Confidence: high.
- Оракул: два дерева (`git archive HEAD` против того же среза с наложенным
`git diff HEAD`), один вход — задача в `review` без записанной причины,
название `strings.Repeat("ы", 200)`:
```
HEAD: preview_links=2 (HasLinks=true) → «Применить» показана
Apply → failed, msg=970 байт "…file name too long"
рабочее дерево: preview_links=0 (HasLinks=false) → формы /apply нет
StateError="" → «Подтверди источник…»
```
- Последствие: путь «распознавание без подтверждённого матча → `review`» —
штатный и самый частый вход в ревью. На нём экран советует подтвердить
источник, который ни при чём. Изменение, чья цель «человек узнаёт причину», на
этом входе делает диагностируемость хуже, чем была.
- **Действие: развилка.** Сюда же сняты два спутника той же причины: текст
печатается дважды (баннер + панель) и после смены источника в панели остаётся
причина от плана, которого больше нет.
- Найдено проходами: `specs`, `basics`; склеено триажем.
**Отработано:** причина пустого предпросмотра считается на показе и приезжает
отдельным полем (`ReviewData.PreviewError`), панель предпочитает её записанной.
Человек выбрал вариант (а) на повторном чекпоинте.
### 2. Карточка Telegram в `review` не называет причину вовсе
- Файл: `internal/tgbot/render.go:70-95`, `internal/tgbot/bot.go:396-415`
- Severity: major. Confidence: high.
- Оракул: рендер карточки на рабочем дереве, задача в `review` с записанной
причиной `name_too_long` и пустым предпросмотром — упоминает причину `false`,
кнопки «Применить» нет. Контроль: та же причина в состоянии `failed` (исход до
изменения) печатала текст и давала кнопку «Повтор».
- Последствие: владелец видит «Нужно подтверждение» без плана, без кнопки и без
слова о длине имени. `proposal.md` обещал обратное; `docs/architecture.md`
«Единые точки» называет обе границы трансляции, обновлена была одна.
- **Действие: развилка.**
**Отработано:** карточка печатает причину для любого случая (вариант (а)), плюс
своя ветка ответа в колбэке, как у коллизии.
## Стоит исправить сейчас
### 3. Штатный отказ краснит логи как сбой, и три предписанных реестра о новой ветке не знают
- Файл: `internal/worker/worker.go:962-968`, `docs/conventions/errors.md:81-97`,
`docs/database.md:210-214`
- Severity: minor. Confidence: high.
- Оракул: прогон ручного `Apply` с логгером Debug — `level=ERROR msg="command
failed"`. `docs/conventions/logging.md:154` относит штатный конфликт к `DEBUG`
и ставит `layout.ErrCollision` поимённо.
- **Действие: инлайн.** **Отработано** целиком: `logCmd`, таблица `errors.md`,
блок констант `database.md`, клауза 409 в дельте `review`.
### 4. Текст причины несёт непомещающееся имя целиком — 494 байта
- Файл: `internal/layout/name.go:29-31`
- Severity: minor. Confidence: high.
- Оракул: `Apply` с названием `strings.Repeat("ы", 200)` → `msg_len_bytes=494`.
- **Действие: инлайн.** **Отработано:** имя усекается серединой (`shorten`, 40
рун), точная длина остаётся числом.
### 5. Два сценария дельты не проверены ничем; комментарий у теста границы описывает не тот вход
- Файл: дельта `file-layout`, `internal/layout/name_length_test.go:109-113`
- Severity: minor. Confidence: high.
- **Действие: инлайн.** **Отработано, и по одному из сценариев — не так, как
предлагал триаж.** Попытка написать тест на «унаследованную базу» показала, что
случай **недостижим**: папка-якорь лежит на диске и потому ≤ предела, а хвост
имени файла (11 байт) не длиннее хвоста имени папки (≥ 11 байт). Сценарий
переписан на верное утверждение, свойство закреплено тестом
`TestApply_InheritedBaseAtLimitStillFits`. Комментарий у границы исправлен,
кириллическая граница добита.
## Гипотезы без доказательства
- Тупик с унаследованной базой не назван в тексте причины (`basics`, B-4).
**Снято разбором:** случай недостижим, см. находку 5.
- Рубрика R6 в `tasks.md` противоречит сценарию S6 дельты. **Снято:** после
переписывания сценария противоречия нет.
- Панель печатает любой `error_msg` без разбора кода, включая сырые тексты ошибок
хранилища. Не отработано: тот же текст уже печатал баннер до изменения, нового
канала не появилось.
## Promote candidates
- **Язык текстов sentinel-ошибок:** русский у тех, что уходят на операторскую
поверхность, английский у прочих; правила в `docs/conventions/errors.md` нет.
- **Внешний текст в персистентной диагностике — только усечённым.** В проекте
есть и `shorten`, и `naming.truncate`, но правила «то, что уезжает в
`error_msg`, усекается на границе» нет; отсюда находка 4.
## Границы покрытия
**Что запускалось.** Метка `medium`, режим по графу: `autotests`, `specs`,
`code`, `basics`, `triage`. Ревью дизайна (`specs` + `rubric`) отработало
отдельным чекпоинтом до кода.
**Что не запускалось и почему.** Всё, что требует запуска — построенные пути
атаки, замеры под нагрузкой, эксплуатационный постмортем, — даёт только метка
`large`. Живые qBittorrent, LLM, метабазы и Telegram не трогались; рабочая БД не
трогалась.
**Чего не мог каждый проход.** `autotests` — отличить исполнение строки от её
проверки. `specs` — судить о качестве распознавания. `basics` на `medium` —
строить пути и снимать замеры. `code` — сверять с руководствами по стилю языка.
Триаж — находить новое: он работает с чужими выводами.
**Потолки проходов.** Ни один проход не сообщил ни своего потолка, ни числа
находок за срезом. Это находка о прогоне: по молчанию нельзя отличить «показал
всё» от «показал верхушку».
**Не проверялось никем.** Решения проекта (`docs/adr/`) и записанные наблюдения
(`docs/research/`) — процессные документы, прогон их не открывает; расхождение с
записанным решением ловит `av-dev-docs:healthcheck`. Поимённая сверка с
руководствами по стилю Go никем не задавалась. Альтернативной реализации, с
которой можно сдиффить решения, у конвейера нет.
**Осталось на человеке.** История инцидентов на umbar; поведение SQLite под
реальным объёмом; завязка внешних потребителей на текущее поведение; суждение
«этой функциональности не должно существовать»; качество распознавания;
идиоматичность Go (проход упразднён 2026-08-04).