Беклог: чистка четырёх закрытых задач (mobilnaya-kv, btn-ghost, F7–F10, lifecycle-minor)
Реализованы и влиты; MINOR-9 (I/O под глобальным w.mu) осознанно waived для one-user home-сервера. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,6 @@ Tududi (проект `jellybit`) больше **не** держит беклог
|
|||||||
- [Бэкап SQLite](backup-sqlite.md) — architecture
|
- [Бэкап SQLite](backup-sqlite.md) — architecture
|
||||||
- [Сигнал Jellyfin после отката и удаления файлов](jellyfin-skan-posle-udaleniya.md) — Скан шлётся только на done; после reverted/deleted Jellyfin держит битые записи. Клиент и гейт готовы, но скана нет в openspec-спеках
|
- [Сигнал Jellyfin после отката и удаления файлов](jellyfin-skan-posle-udaleniya.md) — Скан шлётся только на done; после reverted/deleted Jellyfin держит битые записи. Клиент и гейт готовы, но скана нет в openspec-спеках
|
||||||
- [Режиссёр в блоке «Распознано как» на странице загрузки](rezhisser-v-kartochke-zagruzki.md) — На /download/{id} режиссёр всегда прочерк (поля нет в шаблоне); экран ревью его уже выводит
|
- [Режиссёр в блоке «Распознано как» на странице загрузки](rezhisser-v-kartochke-zagruzki.md) — На /download/{id} режиссёр всегда прочерк (поля нет в шаблоне); экран ревью его уже выводит
|
||||||
- [Карточка информации о торренте вылезает за границу экрана на мобильном](mobilnaya-verstka-kv-infohash.md) — grid-blowout на .kv из-за неразрывного infohash — страница получает горизонтальный скролл
|
|
||||||
- [Глубокий healthcheck и статус зависимостей](healthcheck-zavisimosti.md) — /healthz проверяет только сам сервис
|
- [Глубокий healthcheck и статус зависимостей](healthcheck-zavisimosti.md) — /healthz проверяет только сам сервис
|
||||||
- [НФТ: масштаб до 100 одновременных загрузок (потолок — 1000)](masshtab-100-zagruzok.md) — Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг)
|
- [НФТ: масштаб до 100 одновременных загрузок (потолок — 1000)](masshtab-100-zagruzok.md) — Зафиксировать в НФТ ориентир 100/1000 загрузок + аудит узких мест (SQLite, воркер, поллинг)
|
||||||
- [Обучение на правках человека (few-shot из прошлых ревью)](obuchenie-na-pravkah.md) — Когда человек поправил матч, тип или нумерацию — сохранять это как пример и подмешивать…
|
- [Обучение на правках человека (few-shot из прошлых ревью)](obuchenie-na-pravkah.md) — Когда человек поправил матч, тип или нумерацию — сохранять это как пример и подмешивать…
|
||||||
@@ -43,7 +42,6 @@ Tududi (проект `jellybit`) больше **не** держит беклог
|
|||||||
|
|
||||||
## Низкий
|
## Низкий
|
||||||
|
|
||||||
- [Кнопки выглядят как кнопки: `btn-ghost` и рассогласование весов](unifikaciya-knopok-btn-ghost.md) — «Откатить» неотличима от текста: btn-ghost прозрачен, аффорданс только на hover; плюс одно действие имеет разный вес в списке и на странице
|
|
||||||
- [Ревью уведомлений в Telegram (аудит текстов и формата)](telegram-revyu-uvedomleniy.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи
|
- [Ревью уведомлений в Telegram (аудит текстов и формата)](telegram-revyu-uvedomleniy.md) — зонтичный проход по всем текстам бота: полнота карточек, единый язык, оформление; порождает под-задачи
|
||||||
- [Download id в Telegram моноширинным (code) для tap-to-copy](telegram-download-id-code.md) — слать id как `code`; требует включить parse mode (HTML) в send() + escape всех текстов
|
- [Download id в Telegram моноширинным (code) для tap-to-copy](telegram-download-id-code.md) — слать id как `code`; требует включить parse mode (HTML) в send() + escape всех текстов
|
||||||
- [Мгновенные обновления через SSE](sse-obnovleniya.md) — Живые обновления прогресса сейчас на htmx-поллинге (фаза 2 веб-UI) — просто и работает…
|
- [Мгновенные обновления через SSE](sse-obnovleniya.md) — Живые обновления прогресса сейчас на htmx-поллинге (фаза 2 веб-UI) — просто и работает…
|
||||||
@@ -61,6 +59,4 @@ Tududi (проект `jellybit`) больше **не** держит беклог
|
|||||||
- [Авторизация веб-UI (на будущее)](avtorizaciya-web-ui.md) — Решено для v1: без авторизации в доверенной LAN, опц
|
- [Авторизация веб-UI (на будущее)](avtorizaciya-web-ui.md) — Решено для v1: без авторизации в доверенной LAN, опц
|
||||||
- [Современный Web-UI как PWA](web-ui-pwa.md) — Переделать веб-интерфейс в современное PWA-приложение (устанавливаемое, отзывчивое…
|
- [Современный 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)_
|
- [Идентичность инфохэшей: split v1/v2 одного торрента + крафт-магнет отравляет владение (F4, F5)](review-f4-f5-infohash-identity.md) — split v1/v2 идентичность и крафт-магнет отравляют владение инфохэшами _(ревью 2026-07-08)_
|
||||||
- [Приём/UI: мелкие фиксы границ и парсинга (F7, F8, F9, F10, N2)](review-f7-f10-ingest-ui-fixes.md) — мелочи приёма/UI: oversized→500, гонки дедупа, парсинг magnet, cap контекста, руны _(ревью 2026-07-08)_
|
|
||||||
- [Жизненный цикл: мелкие находки (MINOR-8, MINOR-9, NIT-11, NIT-12)](review-lifecycle-minor.md) — claim-token распознавания, I/O под глобальным mutex, мелкие lookup/retry _(ревью 2026-07-08)_
|
|
||||||
- [Нити приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5)](review-ingest-nits.md) — косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_
|
- [Нити приёма: NoName в контексте, устаревшие комментарии, лог без причины, bencode-аллокации (N1, N3, N4, N5)](review-ingest-nits.md) — косметика приёма: NoName в контексте, устаревшие комментарии, лог, bencode-аллокации _(ревью 2026-07-08)_
|
||||||
|
|||||||
@@ -1,56 +0,0 @@
|
|||||||
# Карточка информации о торренте вылезает за границу экрана на мобильном
|
|
||||||
|
|
||||||
**Приоритет:** средний
|
|
||||||
|
|
||||||
На узком экране блок «Информация о торренте» на `/download/{id}` выталкивает
|
|
||||||
страницу за вьюпорт — появляется горизонтальный скролл всей страницы.
|
|
||||||
|
|
||||||
## Причина
|
|
||||||
|
|
||||||
Grid-blowout на `.kv` из-за неразрывного infohash:
|
|
||||||
|
|
||||||
- `web/templates/partials/download_main.html:83-86` — `<dl class="kv">` с
|
|
||||||
`<dd class="mono">{{.}}</dd>` для каждого инфохэша.
|
|
||||||
- `web/static/css/jellybit.css:448-451` — `.kv{grid-template-columns:auto 1fr}`,
|
|
||||||
у `.kv dd` **нет** `overflow-wrap`/`word-break`/`min-width:0`. Трек `1fr` =
|
|
||||||
`minmax(auto,1fr)`, его автоминимум = min-content = весь 40-символьный хэш.
|
|
||||||
|
|
||||||
Арифметика на 360px: доступно ≈302px, требуется ≈380px (`dt` «infohash» nowrap
|
|
||||||
≈52px + gap 16px + хэш ≈312px) → переполнение ~78px. `overflow-x:hidden` на
|
|
||||||
`body`/`html` нигде нет, поэтому блок физически ломает страницу.
|
|
||||||
|
|
||||||
Недавние коммиты про ellipsis (`44c5952` — только `.btn-file`) и `.type-ico`
|
|
||||||
(`3f80d36`) этот блок не трогали.
|
|
||||||
|
|
||||||
## Что уже защищено (не виновники)
|
|
||||||
|
|
||||||
- `jellybit.css:283-285` — `.src-full{word-break:break-all}` → magnet уже чинится.
|
|
||||||
- `jellybit.css:365-366` — `.table-wrap{overflow-x:auto}` + `table.tbl{min-width:560px}`
|
|
||||||
→ таблица раскладки скроллится внутри себя. Страницу не ломает, но 560px
|
|
||||||
принудительного горизонтального скролла на мобильном — отдельная заноза.
|
|
||||||
- `.fname`/`.link-to` (`:374`, `:378-379`), `.card-title` (`:205`),
|
|
||||||
`.review-title` (`:298`) — `word-break` есть.
|
|
||||||
|
|
||||||
## Развилки
|
|
||||||
|
|
||||||
- **(а) точечно:** `.kv dd{overflow-wrap:anywhere;min-width:0}` +
|
|
||||||
`.kv dd.mono{word-break:break-all}`.
|
|
||||||
- **(б) в медиа-запросе** перевести `.kv` в одну колонку
|
|
||||||
(`grid-template-columns:1fr`, `dd{text-align:left}`) — заодно снимает
|
|
||||||
проблему `dt{white-space:nowrap}`.
|
|
||||||
|
|
||||||
Учесть: `.kv` используется **и** в блоке «Распознано как»
|
|
||||||
(`download_main.html:34-43`) — правка затронет оба, то есть чинится и потенциально
|
|
||||||
длинный `RecTitle`. Страховку `body{overflow-x:hidden}` как единственную меру не
|
|
||||||
брать — маскирует, а не лечит.
|
|
||||||
|
|
||||||
Мобильных стилей почти нет: единственный `@media (max-width:640px)` —
|
|
||||||
`jellybit.css:477-492` (14 правил), ни `.kv`, ни `.table-wrap` в нём не упомянуты.
|
|
||||||
|
|
||||||
## Спеки
|
|
||||||
|
|
||||||
`openspec/specs/web-ui/spec.md:237-247` («Блок информации о торренте») требований
|
|
||||||
к адаптивности не содержит → либо чистый bugfix, либо дельта с требованием
|
|
||||||
переноса длинных значений.
|
|
||||||
|
|
||||||
Связано: [[web-ui-pwa]] (крупная переделка, этот фикс — точечный).
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Приём/UI: мелкие фиксы границ и парсинга (F7, F8, F9, F10, N2)
|
|
||||||
|
|
||||||
**Приоритет:** низкий · **Теги:** ingest, review-2026-07-08
|
|
||||||
|
|
||||||
Ревью Fable 2026-07-08 (приём). Пачка независимых простых фиксов.
|
|
||||||
|
|
||||||
F7 — oversized .torrent через веб → 500 вместо 400. ingest.go:148-151 отдаёт plain fmt.Errorf «torrent too large», classifyErr (httpapi.go:753-770) → default → 500. Web MaxBytesReader пропускает MaxTorrentSize+1MiB. Фикс: sentinel-ошибка размера → 400. (Telegram ок — pre-check doc.FileSize.)
|
|
||||||
|
|
||||||
F8 — fast-path attach гонка с cancel. ingest.go:85-90,205-217: FindActiveByInfohash (без tx) вернул catched, параллельно cancel → attached() отдаёт Deduplicated=true (stale «уже в работе»), ничего не активно. Фикс: убрать ранний return, дедуп-решение только в CreateDownloadIfNoActive (уже re-check под BEGIN IMMEDIATE).
|
|
||||||
|
|
||||||
F9 — magnet parsing. magnet.go:46-51: HasPrefix «urn:btih:» регистрозависим → magnet:?xt=URN:BTIH:… отклоняется (RFC 2141: URN регистронезависим; scheme уже матчится EqualFold :91). tgbot/parse.go:11: magnet:\?[^\s]+ приклеивает хвостовую пунктуацию (точка/скобка) → xt последним → длина 41 → отказ. Фикс: case-insensitive префикс; trim хвостовых .,;:)]}>» в ParseMessage.
|
|
||||||
|
|
||||||
F10 — cap размера context из веб-формы. httpapi.go:405,412-414: multipart-бюджет на всё тело; без файла поле context ~9MiB → download.context в БД, рендер, LLM-промпты. REST 64KiB, Telegram лимит подписи — открыт только web. Фикс: cap Context в Ingest (одно место) ~16KiB с маркером.
|
|
||||||
|
|
||||||
N2 — shorten() режет по байтам не рунам (httpapi.go:713-718): кириллические source_ref-заголовки (частый случай) рвутся посреди руны → U+FFFD. Фикс: рунобезопасная обрезка.
|
|
||||||
|
|
||||||
Вердикт: все простые фиксы.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Жизненный цикл: мелкие находки (MINOR-8, MINOR-9, NIT-11, NIT-12)
|
|
||||||
|
|
||||||
**Приоритет:** низкий · **Теги:** review-2026-07-08, lifecycle
|
|
||||||
|
|
||||||
Ревью Fable 2026-07-08 (жизненный цикл). Мелкие находки.
|
|
||||||
|
|
||||||
MINOR-8 — recognition claim-token отсутствует. review.go:160-177: задача в recognizing, LLM в полёте (вне w.mu); user Cancel→Relink (recognizing→cancelled→recognizing revive). Старый вызов завершается, finishRecognition re-check d.State==recognizing — true, но это НОВЫЙ claim → устаревший результат коммитится (recognizing→review); свежий прогон отбрасывает себя. Импакт низкий (входы почти идентичны), но потерянный LLM-вызов + результат приписан не той попытке. Фикс: claim-token (updated_at на момент claim, или id строки recognition).
|
|
||||||
|
|
||||||
MINOR-9 — I/O под глобальным w.mu. worker.go:677-734 (Retry: torrentByInfohash + qbt.Add под mu), review.go: ensureSourcePresent (сеть) в Relink/Rerecognize/Refine/SetType, Apply (torrentByInfohash + layouter.Apply FS I/O), Undo (FS I/O) — держат w.mu целиком. Нарушает своё же правило «медленные вызовы вне блокировки» (processCatched/recognizeOne его соблюдают). Медленный/зависший qBit → каждый клик ревью = глобальный стопор поллинга и всех команд транспортов. Корректность ок (lock даёт race-free preflight), только liveness. Для one-user home-сервера — можно осознанно waive; худший — Retry с qbt.Add под mu при зависшем qBit. Фикс если делать: probe-вне-lock + re-validate-под-lock (как processCatched).
|
|
||||||
|
|
||||||
NIT-11 — byHash индексирует и усечённый 40-hex t.Hash v2-only торрентов (worker.go:444-452). Хранение усечённых хешей отклоняется (discover.go:98-111), но lookup-мапа всё ещё ключует t.Hash → v1-хеш одной задачи теоретически == усечённый-v2 t.Hash другого торрента → матч не тому. Требует 160-бит коллизию (космологические шансы). Фикс: исключить t.Hash из мапы, когда есть infohash_v1/v2 (зеркалить torrentHashes).
|
|
||||||
|
|
||||||
NIT-12 — Retry на failed/qbit_error (missingFiles) с живым errored-торрентом: alive=true → без re-Add → downloading → следующий тик classErrored → снова failed (+ дебаунс уведомления). Честно, но user-hostile: retry выглядит сломанным, реальное лекарство (recheck/fix в qBit) не подсказано. Фикс: отклонять retry (или форсить recheck) когда живой торрент classErrored. (Связано с задачей retry/stall семантики.)
|
|
||||||
|
|
||||||
Вердикт: MINOR-8/NIT-11/NIT-12 — простые фиксы; MINOR-9 — change или осознанный waive.
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# Кнопки выглядят как кнопки: `btn-ghost` и рассогласование весов
|
|
||||||
|
|
||||||
**Приоритет:** низкий
|
|
||||||
|
|
||||||
Кнопка «Откатить» в списке загрузок неотличима от текста/ссылки. Это **не**
|
|
||||||
разметка — там уже `<button type="submit">` внутри `<form>`; проблема чисто
|
|
||||||
визуальная, в классе `btn-ghost`:
|
|
||||||
|
|
||||||
- `web/static/css/jellybit.css:165-166` — `.btn-ghost{background:transparent;
|
|
||||||
border-color:transparent;color:var(--text-dim)}`. Прозрачный фон + прозрачный
|
|
||||||
бордер + приглушённый текст = в покое нет аффорданса. Он появляется только на
|
|
||||||
`:hover`, а на тач-устройствах hover'а нет вообще.
|
|
||||||
|
|
||||||
## Инвентаризация
|
|
||||||
|
|
||||||
| Действие | Список (`card.html`) | Страница (`download_main.html`) |
|
|
||||||
|---|---|---|
|
|
||||||
| Ревью | `:28` `btn btn-primary btn-sm` | `:21` `btn btn-primary` |
|
|
||||||
| **Откатить** | `:29` **`btn btn-sm btn-ghost`** | `:23` **`btn btn-danger`** «↩ Откатить» |
|
|
||||||
| Привязать заново | `:30` `btn btn-sm` | `:24` `btn` |
|
|
||||||
| Повторить | `:31` `btn btn-sm` | `:25` `btn` |
|
|
||||||
| **Отменить/Отклонить** | `:32` **`btn btn-sm btn-danger`** | `:26` **`btn btn-ghost`** |
|
|
||||||
| Обновить имя | — | `:22` `btn btn-ghost` |
|
|
||||||
|
|
||||||
Расхождения:
|
|
||||||
|
|
||||||
1. `btn-ghost` — корень проблемы, 3 места: `card.html:29`,
|
|
||||||
`download_main.html:22`, `download_main.html:26`.
|
|
||||||
2. «Откатить» несогласован сам с собой: невидимая `btn-ghost` в списке против
|
|
||||||
красной `btn-danger` на странице.
|
|
||||||
3. «Отменить» инвертирован ровно наоборот: `btn-danger` в списке против
|
|
||||||
`btn-ghost` на странице. В списке Откатить тише Отменить, на странице — наоборот.
|
|
||||||
4. Эмодзи-префиксы несогласованы: `↩ Откатить` (страница) vs `Откатить` (список);
|
|
||||||
`review_main.html:76-78` — эмодзи на всех (`🕗/❌/✅`), `card.html` — ни одного.
|
|
||||||
5. Вероятно ок: `review_main.html:54,58`, `review_source_block.html:49`,
|
|
||||||
`index.html:32,65-67` (пагинация — ссылка-как-кнопка законно),
|
|
||||||
`download_main.html:112,124` (danger-zone).
|
|
||||||
6. `.copy-btn` (`card.html:9`, `download_main.html:15,90`) — отдельная система
|
|
||||||
классов (`jellybit.css:210-214`), не `.btn`. Формально вне скоупа, но при
|
|
||||||
унификации всплывёт.
|
|
||||||
|
|
||||||
База CSS — `jellybit.css:153-171`.
|
|
||||||
|
|
||||||
## Развилки
|
|
||||||
|
|
||||||
- **(а)** дать `.btn-ghost` видимый бордер/фон — одна строка, чинит все 3 места.
|
|
||||||
- **(б)** выкинуть `.btn-ghost` и назначить семантику явно: Откатить = `btn-danger`
|
|
||||||
везде, Отменить = `btn-danger` везде, Обновить имя = нейтральный `.btn`.
|
|
||||||
- **(в)** решить судьбу `↩` — оставляем или убираем.
|
|
||||||
|
|
||||||
Рекомендация — б+в: (а) чинит видимость, но оставляет пункты 2-4 (одно действие
|
|
||||||
имеет два разных визуальных веса на разных экранах).
|
|
||||||
|
|
||||||
**Подводный камень:** `.btn-ghost` портирован из макета (`tmp/design/index.html:109`
|
|
||||||
— тоже `btn btn-sm btn-ghost` для «Откатить», а `tmp/design/download.html:43` —
|
|
||||||
`btn btn-danger`). Макет сам непоследователен → либо чинить и `tmp/design/*`, либо
|
|
||||||
явно зафиксировать, что макет больше не источник истины (см. `tmp/design/handoff.md:96-97`).
|
|
||||||
|
|
||||||
## Спеки
|
|
||||||
|
|
||||||
`openspec/specs/web-ui/spec.md:249-264` («Действия соответствуют состоянию»)
|
|
||||||
описывает, какие действия показывать и имена полей форм; требований к визуальной
|
|
||||||
стилизации нет. «Откатить» упоминается только в htmx-сценариях (`:435`, `:444`).
|
|
||||||
→ чистый nit-фикс либо новая дельта, если хотим зафиксировать семантику весов.
|
|
||||||
Reference in New Issue
Block a user