sprint: набран спринт 2026-08-06 под цель распознавания

- в наборе шесть задач: локаль TVDB и confidence-гейт под цель, плюс баги и
  техдолг помимо неё
- взятым дописаны разделы своего типа: «Затрагивает», критерии с оракулами,
  воспроизведение
- решены развилки: оба расхождения код↔спека правятся спекой (и потому стали
  chore), опрос qBittorrent тормозится бэкоффом до минутного потолка
This commit is contained in:
av
2026-08-06 13:57:33 +03:00
parent d2d386945e
commit f42db0a275
8 changed files with 197 additions and 65 deletions
+31 -12
View File
@@ -1,9 +1,8 @@
# 🐞 Не терять маркер `user_dismiss` при закрытии не-терминальной загрузки из веб-UI
# 🧹 Зафиксировать в спеке разделение Cancel и Dismiss по состояниям
- **Тип:** fix
- **Тип:** chore
- **Категория:** Ядро продукта
- **Зачем:** Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки _(аудит 2026-07-17)_
- **Теги:** goal:state-integrity
- **Зачем:** спека обещает dismiss из любого состояния, код осознанно даёт Cancel для активных и Dismiss для терминальных — расходится буква, а не поведение
Найдено аудитом capability **state-reconciliation** (сверка код↔спека).
Пред-существующее, вне scope пачки lifecycle-задач.
@@ -31,16 +30,36 @@
наблюдаемости (в аналитике/логах не отличить «пользователь закрыл активную» от
«пользователь отменил»). Отсюда низкий приоритет.
## Развилка (решить до кода)
## Решение (2026-08-06): B — привести спеку к коду
- **A — привести код к спеке:** веб-UI на не-терминальных тоже зовёт `Dismiss`
ради единого маркера `user_dismiss`; либо `Cancel` пишет `user_dismiss`.
- **B — привести спеку к коду:** зафиксировать осознанное разделение (`Cancel`
для активных, `Dismiss` для терминальных) — уточнить требование, что стоп-кран
на не-терминальных реализуется `Cancel`'ом, и определить, какой `error_code`
ожидается.
Разделение осознанное: `Cancel` — стоп-кран для активных состояний, `Dismiss`
закрытие терминальных. Требование «Ручное закрытие» уточняется: на
не-терминальных состояниях закрытие из интерфейса реализуется `Cancel`'ом, и
называется, какой `error_code` при этом ожидается.
Сначала решить, осознанно ли разделение Cancel/Dismiss; если да — вероятно B.
Вариант A (звать `Dismiss` из веб-UI на не-терминальных ради единого маркера)
отклонён: он меняет рабочее поведение ради маркера в диагностике.
Кода задача не трогает: наблюдаемое поведение остаётся прежним, меняется
заявленное.
## Затрагивает
- `openspec/specs/state-reconciliation/spec.md` — требование «Ручное закрытие»:
доступность `dismiss` по состояниям и ожидаемый `error_code`;
- дельта-спека change'а — сценарий закрытия не-терминальной загрузки из веб-UI;
- `internal/httpapi/download.go`, `web/templates/partials/download_main.html`
только чтение, правок не предполагается.
## Критерии приёмки
- Требование спеки различает `Cancel` и `Dismiss` по состояниям и называет
`error_code` для каждого пути закрытия (оракул: `openspec validate --strict`).
- В спеке есть сценарий «пользователь закрывает не-терминальную загрузку из
веб-UI» с исходом `cancelled` и названным `error_code` (оракул: тот же
прогон).
- Гейт `Dismissable` в коде и danger-zone шаблона остаются как есть (оракул:
`git diff --stat` в отчёте ревью — файлов под `internal/` и `web/` нет).
## Ссылки