Беклог + чистка: две находки аудита в беклог, поправлен устаревший комментарий
Аудит capability после пачки lifecycle-задач вскрыл две пред-существующие находки (вне scope самих задач) — заведены в беклог: - catched-source-type-namer-okno (средний): addReq не пересобирается из свежего source_type в окне namer'а; самоисцеляется через magnet_timeout→Retry. - dismiss-cancel-user-dismiss-marker (низкий): веб-UI зовёт Cancel вместо Dismiss на не-терминальных, теряется маркер user_dismiss. Инлайн: finishRecognition — комментарий врал про «Ф3, авто-раскладки нет»; фактически авто-раскладка идёт при Decision.Auto. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,7 @@ Tududi (проект `jellybit`) больше **не** держит беклог
|
||||
- [Обучение на правках человека (few-shot из прошлых ревью)](obuchenie-na-pravkah.md) — Когда человек поправил матч, тип или нумерацию — сохранять это как пример и подмешивать…
|
||||
- [Confidence-гейт авто-раскладки: узаконить в спеке + сделать выключаемым (дефолт 0.7)](gate-confidence-spec-vs-code.md) — Решено (B): гейт оставляем как доп. проверку на ревью — выключаемый порог, дефолт 0.85→0.7, записать в спеку
|
||||
- [Внешние субтитры: пары VobSub и языковой суффикс](vneshnie-subtitry.md) — Привязка субтитр→серия уже работает; остались пары VobSub .idx+.sub и потеря Lang/Flags
|
||||
- [`addReq` не пересобирается из свежего `source_type` перед Add (окно namer'а)](catched-source-type-namer-okno.md) — При апгрейде magnet→.torrent в окне namer'а добавится magnet из устаревшего снимка; самоисцеляется через magnet_timeout→failed→Retry _(аудит 2026-07-17)_
|
||||
|
||||
## Низкий
|
||||
|
||||
@@ -56,3 +57,4 @@ Tududi (проект `jellybit`) больше **не** держит беклог
|
||||
- [Современный Web-UI как PWA](web-ui-pwa.md) — Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое…
|
||||
- [Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5)](review-f4-f5-infohash-identity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_
|
||||
- [Нити приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5)](review-ingest-nits.md) — косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_
|
||||
- [Веб-UI зовёт Cancel вместо Dismiss на не-терминальных → теряется `user_dismiss`](dismiss-cancel-user-dismiss-marker.md) — Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки _(аудит 2026-07-17)_
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# `addReq` не пересобирается из свежего `source_type` перед `Add` (окно namer'а)
|
||||
|
||||
**Приоритет:** средний · **Теги:** review-2026-07-17, lifecycle
|
||||
|
||||
Найдено аудитом capability **download-tracking** (сверка код↔спека после пачки
|
||||
lifecycle-задач). Пред-существующее, вне scope задачи F3/cancel-cleanup — T4
|
||||
осознанно вынес это за рамки и задокументировал в своём design.md.
|
||||
|
||||
## Суть
|
||||
|
||||
`processCatched` (`internal/worker/worker.go:442-517`) строит `addReq` из записи
|
||||
`cur`, перечитанной под замком на `:447-455`, **до** вызова namer'а (LLM, секунды,
|
||||
вне замка, `:463-478`). Затем на `:484-491` под замком перечитывается `before`,
|
||||
но `addReq` из него **не пересобирается** — проверяется только `state == catched`.
|
||||
|
||||
Если апгрейд пойманной magnet-задачи до `.torrent`
|
||||
(`UpgradeCatchedMagnetToTorrent`, `internal/store/download.go:392`) отработает
|
||||
именно в окне namer'а (приём принял `.torrent` с тем же infohash, `state`
|
||||
остаётся `catched`), воркер добавит **magnet-ссылку из устаревшего снимка**, хотя
|
||||
в БД уже `source_type=torrent`.
|
||||
|
||||
Спека (`openspec/specs/download-tracking/spec.md`, раздел про добавление по
|
||||
`source_type`) требует перечитывать `source_type` **под блокировкой переходов
|
||||
непосредственно перед добавлением** — сейчас это требование в окне namer'а
|
||||
нарушается.
|
||||
|
||||
## Насколько больно
|
||||
|
||||
Ограниченно и самоисцеляемо: на закрытом трекере magnet без метаданных зависнет
|
||||
в `metaDL` → предохранитель `magnet_timeout` → `failed`; ручной `Retry`
|
||||
перечитает актуальный `source_type` и добьёт. Данные не страдают, инвариант
|
||||
«источник неприкосновенен» не задет. Окно узкое (апгрейд должен лечь ровно в
|
||||
LLM-вызов по тому же infohash). Поэтому средний, не высокий.
|
||||
|
||||
## Развилка (решить до кода)
|
||||
|
||||
- **A — ужесточить код (соответствие букве спеки, закрыть окно):** после re-read
|
||||
`before` под замком (`:484`) пересобирать `addReq`/`hint` из `before`, если
|
||||
`source_type` изменился. Нюанс: `sourceAddParts` читает байты `.torrent` — это
|
||||
тяжёлый вызов, держать под замком нельзя (спека: тяжёлое — вне блокировки), плюс
|
||||
подсказка имени для `.torrent` иная (метаданные раздачи vs имя из magnet), т.е.
|
||||
при апгрейде корректно был бы и повторный namer. Не однострочник.
|
||||
- **B — смягчить спеку (принять реальность):** признать, что рациональ («не
|
||||
полагаться на снимок, снятый ранее вне блокировки») уже выполнен первым re-read
|
||||
под замком на `:447`, и переформулировать требование как «перечитывать
|
||||
`source_type` под блокировкой после тик-снимка», явно приняв узкое namer-окно
|
||||
как самоисцеляемое через `Retry`.
|
||||
|
||||
Рекомендация — начать с B (дёшево, отражает фактическое осознанное поведение), A
|
||||
завести только если узкое окно окажется реальной болью в эксплуатации.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- `internal/worker/worker.go:442-517` — `processCatched`
|
||||
- `internal/store/download.go:392` — `UpgradeCatchedMagnetToTorrent`
|
||||
- `openspec/specs/download-tracking/spec.md` — требование про `source_type`
|
||||
- Тест `TestProcessCatchedReReadsSourceTypeUnderLock` покрывает апгрейд между
|
||||
тик-снимком и re-read, но **не** окно namer'а.
|
||||
@@ -0,0 +1,47 @@
|
||||
# Веб-UI зовёт Cancel вместо Dismiss на не-терминальных → теряется `user_dismiss`
|
||||
|
||||
**Приоритет:** низкий · **Теги:** review-2026-07-17, state-reconciliation
|
||||
|
||||
Найдено аудитом capability **state-reconciliation** (сверка код↔спека).
|
||||
Пред-существующее, вне scope пачки lifecycle-задач.
|
||||
|
||||
## Суть
|
||||
|
||||
Спека `openspec/specs/state-reconciliation/spec.md` (требование «Ручное
|
||||
закрытие»): команда `dismiss` доступна из **любого** состояния кроме `deleted`
|
||||
**во всех транспортах**, и переход SHALL помечаться `error_code = user_dismiss`.
|
||||
|
||||
В веб-UI danger-zone «Закрыть» (dismiss) гейтится только для терминальных
|
||||
состояний: `Dismissable = IsTerminal() && !deleted && !cancelled`
|
||||
(`internal/httpapi/download.go:130`, шаблон
|
||||
`web/templates/partials/download_main.html:99-112`). Для НЕ-терминальных
|
||||
(`stuck`, `deferred`, `downloading`, `review`) закрытие в UI идёт кнопкой
|
||||
«Отменить» → `Cancel` (`internal/worker/worker.go:973`), которая пишет **пустой**
|
||||
`error_code`, а не `user_dismiss`.
|
||||
|
||||
## Насколько больно
|
||||
|
||||
Функционально сценарии проходят: `Cancel` тоже даёт `cancelled` и не трогает
|
||||
файлы/раздачу, семантика для пользователя идентична. Состояния без доступного
|
||||
«закрытия» нет (кроме `deleted`/`cancelled`). Теряется только маркер
|
||||
`user_dismiss` в `error_code` — расхождение с буквой спеки и небольшая потеря
|
||||
наблюдаемости (в аналитике/логах не отличить «пользователь закрыл активную» от
|
||||
«пользователь отменил»). Отсюда низкий приоритет.
|
||||
|
||||
## Развилка (решить до кода)
|
||||
|
||||
- **A — привести код к спеке:** веб-UI на не-терминальных тоже зовёт `Dismiss`
|
||||
ради единого маркера `user_dismiss`; либо `Cancel` пишет `user_dismiss`.
|
||||
- **B — привести спеку к коду:** зафиксировать осознанное разделение (`Cancel`
|
||||
для активных, `Dismiss` для терминальных) — уточнить требование, что стоп-кран
|
||||
на не-терминальных реализуется `Cancel`'ом, и определить, какой `error_code`
|
||||
ожидается.
|
||||
|
||||
Сначала решить, осознанно ли разделение Cancel/Dismiss; если да — вероятно B.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- `internal/httpapi/download.go:130` — гейт `Dismissable`
|
||||
- `web/templates/partials/download_main.html:99-112` — danger-zone
|
||||
- `internal/worker/worker.go:973` — `Cancel`; `:1001` — `Dismiss`
|
||||
- `openspec/specs/state-reconciliation/spec.md` — требование «Ручное закрытие»
|
||||
@@ -136,8 +136,9 @@ func (w *Worker) runRecognize(ctx context.Context, d store.Download) (recognize.
|
||||
return res, savePath, nil
|
||||
}
|
||||
|
||||
// finishRecognition сохраняет попытку распознавания и двигает задачу. В Ф3
|
||||
// метабазы выключены → авто-раскладки не делаем, всегда уходим в review.
|
||||
// finishRecognition сохраняет попытку распознавания и двигает задачу: при
|
||||
// уверенном матче (Decision.Auto) и чистой валидации — авто-раскладка, иначе —
|
||||
// в review (см. ветвление ниже).
|
||||
func (w *Worker) finishRecognition(ctx context.Context, id, claim string, res recognize.Result, savePath string) {
|
||||
log := logctx.From(ctx)
|
||||
planJSON, err := json.Marshal(res.Plan)
|
||||
|
||||
Reference in New Issue
Block a user