Аудит 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>
3.2 KiB
Веб-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— гейтDismissableweb/templates/partials/download_main.html:99-112— danger-zoneinternal/worker/worker.go:973—Cancel;:1001—Dismissopenspec/specs/state-reconciliation/spec.md— требование «Ручное закрытие»