# Наблюдаемость поверхности не выводится из терминальности задачи

- **Дата:** 2026-08-10
- **Источник:** openspec/changes/archive/2026-08-10-card-live-refresh/design.md

## Решение

Веб-UI обновляет себя, пока задача **наблюдаема** — то есть её состояние ещё
может измениться без участия человека, — а не пока она нетерминальна.
Предикат `store.State.IsObservable()` живёт в домене рядом с `IsTerminal()` и
даёт: все нетерминальные плюс `failed`, `target_missing`, `orphaned`. Замолкают
`done`, `cancelled`, `reverted`, `deleted`.

## Почему

Очевидный предикат — «обновляемся, пока задача не терминальна» — оказался
неверным, и это выяснилось на ревью дизайна, до кода. Цитата из источника:

> Терминальность в проекте значит «не активна», а не «навсегда»: фоновая сверка
> двигает часть терминальных сама — `ListRecoverable` возвращает в поток
> `failed`/`stuck` с кодами `magnet_timeout` и `stalled`, а `desyncStates`
> переоценивает `done`, `target_missing` и `orphaned`. Карточка, застывшая по
> `IsTerminal`, показывала бы «Ошибка» у задачи, которая уже качается, — ровно
> тот дефект, ради которого затеян change.

`done` в перечень наблюдаемых не вошёл, и это отдельное решение с ценой:

> Переход `done → target_missing`/`orphaned` означает, что файлы удалили руками
> мимо сервиса, — событие редкое, а карточек `done` в списке больше всех.
> Платить за редкий случай постоянным фоновым запросом на каждую разложенную
> задачу дороже, чем показать её новое состояние при следующем заходе.

## Рассмотренные варианты

- **Наблюдать только нетерминальные** (как задумывалось изначально) — проще
  всего и не заводит второго предиката. Отвергнут: задача, оживлённая сверкой из
  `failed`, висела бы на экране с надписью «Ошибка» до перезагрузки, причём
  соседние карточки при этом обновлялись бы — застывшая читалась бы как
  достоверная.
- **Наблюдать всё, терминальные — редким тиком** — снимает вопрос целиком.
  Отвергнут: список из сотни разложенных задач слал бы пустые запросы вечно, а
  критерий приёмки «завершённая карточка себя не опрашивает» пришлось бы
  отменить.

## Последствия

- `+` смена состояния становится видимой независимо от того, кто её сделал:
  воркер, веб-UI, Telegram или фоновая сверка.
- `+` условие обновления выражено одним доменным предикатом; второго перечня
  состояний в транспорте нет, и завести его нельзя не заметив.
- `−` в домене стало два перечня состояний вместо одного, и второй выведен из
  поведения воркера (`desyncStates`, `ListRecoverable`) вручную. Расширение
  сверки новым состоянием молча вернёт застывшую карточку — связки, которая бы
  это ловила, нет.
- `−` карточка `failed`, `target_missing` или `orphaned` опрашивает сервер, пока
  открыта вкладка: эти состояния живут долго и копятся (срока хранения нет).
