zapret-kvn/xray_fluent/application/selection_source.py
loop-uh 2a35bd050e
Some checks failed
Windows project source guards / test (push) Failing after 33s
Транзакция выбора сервера и единый итог перехода (архитектура вместо заплатки)
Корень ошибки из диагностики 31.10.2026 (0.8.51, Hysteria2 без UDP из МГТС) —
в механизме, а не в одном месте:

1. «Какой сервер» имел три протокола: родной пул писал selected_node_id до
   горячего свитча, Hysteria — до readiness нового sidecar, Amnezia держала
   выбор в pending. После отказа прежний runtime работал, а выбранным и
   сохранённым оставался отказавший сервер.
2. Каждый шаг перехода сам писал ошибку в статус — даже вытесненный новым
   кликом («Не удалось запустить sing-box proxy runtime»), а поздний общий
   текст затирал точную причину; фаза «error» ставилась при живом соединении.

Новые модули:
- application/server_selection.py — единственная транзакция выбора для всех
  протоколов и видов перехода: при живом подключении новый сервер — только
  цель (pending), выбранным становится в commit() готового runtime; неудача
  её сбрасывает (откатывать нечего), без подключения выбор фиксируется сразу.
- application/transition_outcome.py — шаги сообщают причину
  (_fail_transition), сильнейшая побеждает (SECURITY > CAUSE > CORE > STEP),
  публикует один раз _on_transition_runner_finished; вытесненный переход
  молчит; фаза выводится из того, что работает («Подключено» + «Соединение
  осталось на «…»»).
- diagnostics/transport_failure.py (из предыдущего коммита) даёт причину CAUSE.
- signature_service считает желаемый runtime по цели, а не по выбранному.

E2E tests/test_app_e2e_hysteria_udp_switch.py на win10: настоящие sing-box и
hysteria, рабочий сервер и UDP-«чёрная дыра». v0.8.51: 3/3 FAIL (выбран
отказавший сервер; ложная ошибка sing-box; нет причины). Новый код: 3/3 OK.
Полный набор Windows: 1831 OK.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 10:42:12 -04:00

44 lines
2.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Источник смены выбранного сервера.
Каждая смена ``state.selected_node_id`` называет свой источник и пишет в лог
одну строку ``[select] source=…``. Без этого явное действие пользователя
(кнопка «Следующий», трей, таблица) в диагностике неотличимо от сбоя
программы: инцидент 28.09.2026 — в логе был только ``[core-switch]`` без
причины.
Модуль-лист без импортов проекта: его импортируют и application-, и UI-слой.
"""
from __future__ import annotations
from enum import Enum
class SelectionSource(Enum):
"""Кто сменил выбранный сервер.
``manual`` — выбор сделал пользователь: он сбрасывает учёт анти-дребезга
авто-переключения и фиксирует сервер (manual hold). Автоматические
источники этот учёт не трогают.
"""
# Явные действия пользователя.
DASHBOARD_NEXT = ("dashboard-next", "кнопка «Следующий» на главной", True)
TRAY_NEXT = ("tray-next", "трей → «Следующий сервер»", True)
NODES_DOUBLE_CLICK = ("nodes-double-click", "двойной клик в таблице серверов", True)
NODES_ENTER = ("nodes-enter", "Enter в таблице серверов", True)
NODES_MENU = ("nodes-menu", "меню таблицы серверов → «Подключить»", True)
NODES_IMPORTED = ("nodes-imported", "импорт серверов: выбран первый новый", True)
# Автоматика.
AUTO_SWITCH = ("auto-switch", "авто-переключение: сервер недоступен", False)
SMART_SWITCH = ("smart-switch", "умная проверка: сервер медленный", False)
ROTATION = ("rotation", "ротация серверов", False)
HYSTERIA_RECOVERY = ("hysteria-recovery", "отказ Hysteria: резервный сервер", False)
# Выбранный сервер исчез из списка — выбран первый оставшийся.
NODE_REMOVED = ("node-removed", "выбранный сервер удалён", False)
SUBSCRIPTION = ("subscription", "подписка: выбранного сервера больше нет", False)
def __init__(self, code: str, label: str, manual: bool) -> None:
self.code = code
self.label = label
self.manual = manual