- условие самообновления — доменный предикат store.State.IsObservable() вместо фазы catched; один поллер на поверхность, интервалы 5 с и 15 с - отказ тика отвечает 200 и самозавершающимся фрагментом с корневым id цели вместо 404/500, который htmx не свопит - заведён ADR-2026-08-10-observability-is-not-terminality, переписан раздел «Живой поллинг» в конвенции веб-UI
5.4 KiB
5.4 KiB
Why
Загрузка докачалась, воркер увёл её в распознавание и дальше в ревью — а в списке она по-прежнему «Загружается», без кнопки «Ревью →». Верное состояние появляется только после того, как человек сам перезагрузит страницу.
Живое обновление сегодня привязано к двум отдельным фазам: карточка опрашивает
себя, пока задача в catched, а прогресс — пока она в downloading. Выйдя из
downloading, задача не опрашивается ничем, хотя сменить состояние ей предстоит
ещё не раз (распознавание, ревью, раскладка) и часть этих смен идёт вообще без
участия того, кто смотрит на список: их делает воркер или человек из Telegram.
What Changes
- Самообновление карточки списка привязывается к нетерминальности задачи, а
не к фазе
catched: карточка обновляется, пока задача жива, и перестаёт — когда та встала окончательно. - У карточки остаётся один источник обновления. Сейчас в
downloadingих было бы два (сама карточка и вложенный фрагмент прогресса), и они опрашивали бы одно и то же дважды, подменяя разметку друг друга. Живые цифры прогресса приходят вместе с карточкой. - Страница
/download/{id}живёт по тому же правилу: самообновляется, пока задача нетерминальна. - Частота обновления перестаёт быть одинаковой: карточка с живыми цифрами (скорость, ETA) обновляется чаще, чем карточка, у которой меняется только состояние. Цена тика у второй поверхности выше — сборка страницы загрузки считает предпросмотр раскладки и ходит в файловую систему.
- Секция «Раздача» на странице загрузки перестаёт опрашивать сервер сама — она лежит внутри области, которую страница обновляет целиком, и два опроса на одну поверхность мешали бы друг другу.
- Тик самообновления, не сумевший прочитать задачу, перестаёт быть молчаливым: он объясняет положение дел и прекращает опрос вместо бесконечного стука в сервер.
- BREAKING для внутреннего контракта фрагментов: фрагменты прогресса и
раздачи перестают быть самостоятельными поллерами. Наружного API это не
касается —
/api/**не меняется.
Capabilities
New Capabilities
Новых нет.
Modified Capabilities
web-ui: требование «Отображение промежуточного состояния catched» обобщается — самообновление интерфейса перестаёт быть свойством одной фазы и становится свойством живой задачи; условие остановки — терминальное состояние.live-status: требование «Живой прогресс активных загрузок» — сценарий «Завершение останавливает поллинг» сегодня описывает наблюдаемый дефект как норму. Прекращаться должен показ живых цифр, а не обновление карточки.
Impact
internal/httpapi:toViewиbuildDownloadView(условие самообновления), обработчики фрагментов карточки и прогресса;web/templates/partials/card.html,progress.html,download_main.html;- нагрузка: число фоновых запросов на открытую страницу меняется — считается в
design.md; - вне scope: переход на SSE (задача
sse-live-updates), любые изменения/api/**и состава живой телеметрии; - вне scope и названо сознательно: экран
/review/{id}сохраняет фазовое самообновление, заказанное спекойreview(«пока загрузка вrecognizing»). Третья поверхность приводится к общему правилу отдельной задачей — иначе change тянет за собой ещё одну capability.