8dcb50a07c
Предыдущий проход сделал правильным порядок «сначала долговременная запись, потом разрыв сессии» и правильно запретил откат при неудаче разрыва. Способа прийти к согласованному состоянию ПОТОМ он не дал: у двух операций повтор не работал вовсе. Импорт, заменивший auth_id: после неудавшегося /kick старое значение не хранится нигде, повтор того же файла читает из базы уже новое и рвёт его, а cron пропускал незнакомый authID молча — dao.ListPeer просто не возвращала строку. Живая сессия оставалась навсегда. Снижение maxDevices: повтор формы даёт 1 < 1 -> false, разрыва больше нет. Лимит устройств в политику доступа не входит и входить не должен — это свойство сессий, — поэтому механизма схождения у него не было. enforcePeerAccess стал сверкой живых сессий: обход идёт по каждому authID из /online. Нет строки в базе -> kick; peerAccessDenied -> kick; непригодный maxDevices -> kick; устройств больше разрешённого -> kick. Отказ базы при этом не рвёт ничего. Ни таблицы отложенных операций, ни очереди retry: список живых сессий уже есть, и это /online. Отдельно закрыт второй TOCTOU лимита устройств. Учёт выданных разрешений закрыл сравнение двух одинаковых снимков, но сетевой запрос выполнялся вне блокировки, поэтому снимки приходили в резервацию в произвольном порядке и устаревший откатывал lastOnline назад, возвращая уже занятое место. Это не data race — память защищена мьютексом, и -race здесь молчит принципиально. Последовательность «прочитать /online -> занять место» выполняется под замком по authId; глобальный замок не годится, внутри идёт сетевой запрос. Учёт разрешений больше не растёт бесконечно: запись снималась только на ветке отказа, поэтому в карте копились удалённые пиры и переписанные импортом идентификаторы. Уборка идёт по фактической картине подключений. Гейты приёмки доращены под все три инварианта и проверены в обе стороны. Go 1.26.7 -> 1.26.8. Документация приведена в соответствие в двух местах, где описывала снятую архитектуру. Разбор: docs/acceptance/2026-09-02-v1.0.0-rc3-preflight-findings.md
Проверки и приёмка HY2XS
Набор проверок разложен по слоям, на которых они выполняются. Раньше он был одним файлом на 117 КБ и 57 разделов; ориентироваться в нём приходилось поиском по строке.
Нумерация 11-* сохранена: это стабильный идентификатор документа, под
которым на него ссылаются CHANGELOG и релизные гейты.
| Документ | Слой | Что закрывает |
|---|---|---|
| 11-1-how-to-run.md | — | команды запуска всех наборов |
| 11-2-builder-layer.md | builder | резолвер Hysteria, контракт версий, юнит-тесты оркестратора и админки, гейты сборки |
| 11-3-target-and-runtime.md | target | установка на чистый хост, состояние сервисов, конфиг, share URI |
| 11-4-fault-injection.md | target | D0 на живом сервере, D1a-D1h — отказы и откат, D2 — устаревший DNS |
| 11-5-negative-and-matrix.md | target | негативные сценарии, production-матрица, критерии приёмки |
Отчёты о фактических прогонах
Проверки описывают, ЧТО должно выполняться. Результаты конкретных прогонов на конкретных сборках лежат отдельно — см. docs/acceptance/.
Разделение намеренное: документ проверок переживает релизы, а отчёт о прогоне относится к одному артефакту и одному хосту и после релиза не редактируется.