Решили: кнопка «Начать забег» во дворе гильдии отправляет ГОЛОС по ключу run.start, а не
закрывает двор. Кнопка есть у обеих ролей; двор закрывается объявлением — у владельца по токену из
HubPresenter, у гостя по HubOpen=false.
Почему: это было последнее место, где роли расходились сознательно. Кнопку гостю не давали не по злобе, а по устройству: клик закрывал двор нажавшему, и гость ушёл бы со двора один, оставив напарника стоять. Общее согласие снимает саму причину, поэтому исключение исчезает, а не обставляется оговорками.
Сказано: «Пока ЕДИНСТВЕННОЕ что сознательно расходится (и то в будущем НЕ будет) - это нажатие на старт забега. Надо вообще его сделать когда кликают оба, как с готовностью.» (08.08.2026, сессия eab27015).
Грабли: кнопка перестала закрывать экран — и у ВЛАДЕЛЬЦА закрывать его стало нечем. У гостя
двор снимался объявлением и раньше, а владелец жил на резолве RouterResultScreen. Пришлось дать
запросу токен: HubPresenter отменяет его по согласию. Ловится это не тестом, а первым же запуском
соло — двор просто не закрылся бы.
Долг: двор показывается ДВУМЯ путями — петлёй владельца (OpenHubRequest) и IHubPresence у
гостя, — и это ровно та форма, из-за которой разъезжались экраны узла. Переезд двора на общий шов
(NodeStageScreens) сюда не вошёл сознательно: он тянет за собой снос HubOpen из ActivityState
вместе с форматом канала. Записано в леса захода.
Не покрыто тестом: HubPresenter резолвит согласие через SessionHost, который в EditMode не
поднять, а у GuestActivityFollower нет ни одного теста и тяжёлый конструктор. Проверяется живым
прогоном вдвоём.
Владелец правды: HubPresenter.cs, HubMessages.cs, IHubPresence.cs, MenuRouter.OpenHub.