Статусные борды показывают состояние, которое мутируют фоновые воркеры — sync-прогоны, curation-прогоны, бэкапы, прогоны агентов. Экран не мутирует то, что показывает, поэтому инвалидация кеша по действиям пользователя его не обновит, а поэкранные таймеры опроса не масштабируются. Все их заменяет один платформенный push-канал.
/events/stream — один SSE на вкладку
Канал едет на pub/sub cache-Redis — потерянный сигнал стоит одного отложенного обновления, никогда — данных. Публикация best-effort: сломанная шина не должна валить транзакцию самого воркера.
GET /api/v1/events/streamОдин мультиплексный поток на вкладку несёт все push-заботы; колокольчик уведомлений тоже едет в нём, так что платформа держит единственное постоянное соединение. Диспетчер публикует сигналы колокольчика ровно как раньше — общим стал только транспорт.
| Событие | Пейлоад | Смысл |
|---|---|---|
hello |
{boards: […]} |
открывает поток: борды, на которые подписан вызывающий. Клиент инвалидирует их все — reconnect нагоняет всё пропущенное. |
board |
{board} |
названный борд изменился; клиент инвалидирует его query-ключи по реестру. |
unread |
{count} |
счётчик колокольчика, рефетчится на сервере по сигналу колокольчика — поведение прежнее, транспорт новый. |
ping |
{} |
heartbeat после 25 с тишины — настоящий event-фрейм, не SSE-комментарий, чтобы клиентский watchdog видел живость. |
Доступ решается в момент подписки: при открытии потока роль вызывающего выбирает набор каналов — админские борды админам, канал агентов владельца каждому. Никакой пофреймовой фильтрации, никакого пер-юзерного fan-out для админских бордов.
| Борд | Кормится из | Аудитория |
|---|---|---|
harvester |
журнал sync-прогонов Harvester + постраничные счётчики прогресса | админы |
knowledge |
curation-прогоны · прогресс re-embed · бэкапы Knowledge Store | админы |
agents |
журнал прогонов Agent Engine, включая шаги | владелец агента — свой канал; админы — канал всех агентов |
Что публикует. Каждый закоммиченный переход прогона (queued → running → терминальный, отмена) и каждая закоммиченная запись прогресса по ходу — граница страницы синка, батч re-embed, шаг агента. Публикация идёт после commit, поэтому сигнал никогда не анонсирует состояние, которого рефетч ещё не увидит.
Что не публикует. Чистые heartbeat-касания — учёт живости не изменение состояния и не должен будить каждый открытый экран.
Коалесинг — забота подписчика. Воркеры публикуют свободно — publish в Redis почти бесплатен; каждое соединение потока коалесцирует по борду с минимальным зазором эмиссии 1.5 с, по задней кромке (всплеск даёт один фрейм сразу и один по истечении зазора). Горячий цикл воркера не заставит клиента рефетчить чаще зазора, а последнее состояние доедет всегда.
Шелл монтирует единственного потребителя потока с декларативным реестром
борд → префиксы query-ключей. Фрейм board инвалидирует
префиксы этого борда; рефетчатся только реально смонтированные запросы. Экраны
не несут никакого кода живых обновлений — ни таймеров, ни интервалов: экран
живой потому, что его ключи в реестре, а не потому, что он опрашивает.
hello при переоткрытии инвалидирует все
борды — разрыв нагоняется целиком.
staleTime 0 — каждый заход на экран рефетчит даже с лежащим
потоком, плюс штатный рефетч по фокусу окна и восстановлению сети.
Загрузка весов встроенного эмбеддера — единственный живой статус, живущий вне базы платформы: внутри embeddings-рантайма, который бэкенд лишь проксирует. Нашего перехода, из которого публиковать, нет — поэтому карточка эмбеддера сохраняет узкий опрос только пока грузятся веса. Всё остальное на каждом борде — push. Второй статус такого рода оправдал бы серверного наблюдателя; один — нет.