- Раскладка docs/ приведена к канону 2: заведены passport/architecture/ database/security/review и research; docs/specs, drafts, backlog, review/ и BRIEF.md разобраны и удалены, беклог переехал в docs/tasks (34 задачи, 6 целей, слаги на английский). - Нарративы specs удалены как дубли openspec-спек после поимённой сверки; остаток заведён задачами (редактор маппинга ревью, крайние случаи именования), отказ от сущности title промоутнут в ADR. - Проектные копии агентов и скиллов ревью/пайплайна удалены в пользу плагинов av-dev-pm и av-dev-pipeline; в task gate добавлен шаг canon вместо er-schema.
3.4 KiB
Веб-UI зовёт Cancel вместо Dismiss на не-терминальных → теряется user_dismiss
- Секция: ядро продукта
- Зачем: Функционально ок (Cancel даёт cancelled), но маркер user_dismiss в error_code теряется; расхождение с буквой спеки (аудит 2026-07-17)
- Теги: goal:state-integrity
Найдено аудитом 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— требование «Ручное закрытие»