Files
jellybit/openspec/changes/archive/2026-08-06-cancel-dismiss-split-wording/proposal.md
T
av 0b02a8c224 specs: в state-reconciliation разведены Cancel и Dismiss по состояниям
- требование «Ручное закрытие загрузки» приведено к коду: два пути закрытия,
  error_code на каждом, раскладка поверхностей — описательно, а не SHALL
- уборка своего торрента после отмены названа исключением по состоянию, а не
  по команде; убрана ложная гарантия «данных пользователя не касается»
- в docs/review.md записан проскочивший дефект гарда окна после add и новый
  вопрос проходу adversary про асимметрию признака владения
2026-08-06 15:50:41 +03:00

69 lines
5.3 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.
## 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 о том же не заводится.