P1 после зелёного main.
1. CI-матрица. Обе джобы стояли на windows-latest, и это дорого обошлось:
инвариант A37 не держался на Windows, а четыре теста молча предполагали
Linux. Прогон на одной системе не показывал ни того, ни другого. Проект
работает на Linux и активно получает Linux-правки — теперь обе системы
проверяются одинаковым набором.
2. Zip-slip из аудита НЕ ВОСПРОИЗВОДИТСЯ — измерено, а не принято на веру.
Архив с "../", с абсолютным путём и с записью-ссылкой распакован через
zipfile.extractall: ничего за пределы каталога не вышло, абсолютный путь
стал относительным, "../" схлопнулись, а запись-ссылка легла обычным
файлом. CPython санирует пути сам.
Но это свойство реализации, а не обещание формата, и распаковка идёт в
корень установки. Граница сделана собственным инвариантом: каждая запись
проверяется до записи на диск, отклоняются абсолютные пути, выход через
"..", ссылки и записи не-файлового типа. Инвариант закреплён тестом, а не
оставлен на усмотрение стандартной библиотеки.
Тесты: 776 -> 777 passed, 2 skipped, 4 deselected. ruff check . чисто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Preflight and updater tests no longer probe 8081/8082 or GitHub.
Hermetic runs fail-fast on non-loopback sockets. GUI helpers are
importable without customtkinter. CI installs the web extra, and
pytest-timeout plus a wall-clock wrapper bound the suite.
deepseek_adapter.py had never been importable: it referenced ProviderAdapter
and ProfileConfig, neither of which exists in src/, and profile.extra, which
RouterProfileConfig does not define. Rewritten against BaseProviderAdapter with
the three missing abstract methods and registered as "deepseek".
Adds tests/test_import_invariants.py, which walks every shipped module and
fails on broken internal references while skipping absent optional GUI extras.
It also asserts that test modules importing customtkinter call
pytest.importorskip, since a missing guard aborts collection of the whole
session and takes the release gate with it.
Adds a headless CI job that uninstalls customtkinter and runs the suite, so
that invariant is enforced rather than remembered.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>