specs: в state-reconciliation разведены Cancel и Dismiss по состояниям
- требование «Ручное закрытие загрузки» приведено к коду: два пути закрытия, error_code на каждом, раскладка поверхностей — описательно, а не SHALL - уборка своего торрента после отмены названа исключением по состоянию, а не по команде; убрана ложная гарантия «данных пользователя не касается» - в docs/review.md записан проскочивший дефект гарда окна после add и новый вопрос проходу adversary про асимметрию признака владения
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
## Why
|
||||
|
||||
Требование «Ручное закрытие загрузки (стоп-кран)» обещает команду «Закрыть»
|
||||
(dismiss) «из **любого** состояния, кроме `deleted`, во всех транспортах» и
|
||||
метку `error_code = "user_dismiss"` на переходе. Доменная команда
|
||||
`worker.Dismiss` это выполняет, но **поверхности** её так не показывают:
|
||||
веб-UI держит «Закрыть» в danger-зоне только для терминальных состояний
|
||||
(`Dismissable = IsTerminal() && !deleted && !cancelled`), а закрытие
|
||||
не-терминальной загрузки идёт кнопкой «Отменить»/«Отклонить» → `worker.Cancel`,
|
||||
которая пишет **пустой** `error_code`. Telegram показывает «Закрыть» ещё по
|
||||
третьей раскладке — для `failed`/`stuck` и для `done`/`orphaned`/
|
||||
`target_missing`, а на `review`/`deferred` даёт «Отклонить» → `Cancel`.
|
||||
|
||||
Расходится буква, а не поведение: `Cancel` тоже даёт `cancelled`, файлы и
|
||||
раздачу не трогает, семантика для пользователя та же. Теряется маркер
|
||||
`user_dismiss` в диагностике — по `error_code` не отличить «пользователь закрыл
|
||||
активную» от «пользователь отменил». Пока спека утверждает единый путь закрытия,
|
||||
разрыв невидим и находится заново каждым аудитом capability.
|
||||
|
||||
## What Changes
|
||||
|
||||
Меняется **заявленное**, не наблюдаемое. Кода изменение не трогает.
|
||||
|
||||
- Требование «Ручное закрытие загрузки (стоп-кран)» разводит два уровня:
|
||||
**гейты доменных путей** (`Cancel` — не-терминальные, `Dismiss` — любое кроме
|
||||
`deleted`; это нормативно) и **раскладку кнопок по поверхностям** (описана как
|
||||
сегодняшнее состояние, нормативной не объявляется).
|
||||
- Требование называет `error_code` на каждом пути закрытия: `Dismiss` →
|
||||
`"user_dismiss"` (нормативно), `Cancel` (отмена активной / отклонение на
|
||||
ревью) → сегодня пустой (описательно — собственный код у `Cancel` не
|
||||
запрещён, вопрос открыт). Прежняя формулировка обещала `user_dismiss` на
|
||||
любом закрытии.
|
||||
- Требование называет гарантию, которая держится: страница загрузки веб-UI даёт
|
||||
путь закрытия из каждого не-`deleted` и не-`cancelled` состояния; какой именно
|
||||
путь — выбор поверхности, а не обязательство домена.
|
||||
- Отрицание побочных эффектов остаётся при `Dismiss`; для `Cancel` названо
|
||||
единственное исключение — уборка воркером собственного, только что
|
||||
добавленного торрента при отмене в окне после `add` (дом исключения —
|
||||
`download-tracking`).
|
||||
- Названо, что **оба** пути замещают прежнюю диагностику записи, а не только
|
||||
`Cancel`.
|
||||
- Добавляются сценарии закрытия не-терминальной загрузки из веб-UI, закрытия
|
||||
терминальной, замещения диагностики и расхождения маркера по поверхностям.
|
||||
- Названо известное ограничение: маркер сегодня кодирует поверхность, а не
|
||||
намерение; разрыв описан с ценой, а не замолчан.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
Нет.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `state-reconciliation`: требование «Ручное закрытие загрузки (стоп-кран)» —
|
||||
доступность закрытия по состояниям и поверхностям, `error_code` на каждом
|
||||
пути закрытия, сценарии закрытия не-терминальной загрузки.
|
||||
|
||||
## Impact
|
||||
|
||||
- `openspec/specs/state-reconciliation/spec.md` — одно требование и его
|
||||
сценарии.
|
||||
- `internal/httpapi/download.go`, `web/templates/partials/download_main.html`,
|
||||
`web/templates/partials/card.html`, `internal/tgbot/render.go`,
|
||||
`internal/worker/worker.go` — **только чтение**, правок не предполагается.
|
||||
Отсутствие правок под `internal/` и `web/` — критерий приёмки задачи.
|
||||
- Прецедент приёма — [ADR-2026-08-06-spec-follows-code-on-narrow-window](../../../docs/adr/ADR-2026-08-06-spec-follows-code-on-narrow-window.md);
|
||||
второй ADR о том же не заводится.
|
||||
Reference in New Issue
Block a user