hermes-hub/agents/inbox/2026-08-20-full-audit-and-improvement-backlog.md
Hermes Team 46a185395c docs(review): add independent audit, five review rounds, and remediation task
Full audit of the project (93 findings) plus the review record for rounds 1-5:
P0 release blockers, 65482e8, 42dfe2c, the v0.1.1 candidate verdict, and the
review of the previously unreviewed OAuth/wizard/quota work. Includes the
remediation task with acceptance criteria that 42eddb3 was verified against.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 22:22:57 +07:00

31 KiB
Raw Permalink Blame History

Полный аудит Hermes Hub и бэклог доработок

Дата аудита: 2026-08-20 Проверенная ревизия: origin/main @ 5ccfd48 (github.com/ochenstarik-ui/hermes-hub, private) Метод: статический разбор всех исходников + прогон тестов на чистом checkout origin/main в отдельном worktree Версия: 0.1.0 (pyproject.toml, compatibility.json) против 1.3.0 (about_view.py, gui_server.py) Объём: ~19 500 строк (Python ~15k, C# 0.8k, HTML 0.85k, PS1/BAT 0.25k, docs/config ~2.5k)

Первая редакция этого документа делалась по локальному снимку fdf9ecc (на 3 коммита старше). Все пункты ниже перепроверены по origin/main; раздел 2 фиксирует, что именно уже исправлено в 7609ad8.


1. Общая оценка

Ядро маршрутизации (router_engine, health_tracker, session_affinity, адаптеры) спроектировано грамотно: чистые слои, dataclass-модели, потокобезопасность, детерминированные тесты с моками. Коммит 7609ad8 («product stabilization») закрыл заметную часть проблем презентационного слоя — появился кэш сканирования, строгий resolver статусов, идентичность приложения в Windows, предпрогрев экранов.

Тем не менее продукт остаётся неработоспособным как приложение:

  • нативное приложение не запускается на чистой машине — ни один установщик не ставит зависимости UI;
  • failover для основного провайдера (Antigravity) не срабатывает никогда из-за формата ошибок agy_generate;
  • три пользовательских сценария падают с AttributeError/NameError: удаление credentials, подключение аккаунта по API-ключу, перераспределение ролей;
  • настройки не сохраняются и ни на что не влияют;
  • «roadmap»-модули (+819 строк) не подключены ни к одному кодовому пути.

Фактический прогон тестов на чистом checkout origin/main: 1 failed, 28 passed, 2 skipped + 1 ошибка сбора (test_ui_refinement.py не импортируется без customtkinter). Отчёт agents/done/…stabilization…md заявляет «35/35 PASSED» — это верно только на машине, где dist/ существует локально и зависимости доустановлены руками.

Зрелость: ядро — Beta, оболочка — Alpha, упаковка — не готова к дистрибуции.


2. Что уже исправлено в 7609ad8 (не требует работы)

Было в аудите Статус в origin/main
TeamView.update_data(app_state) требует аргумент → главный экран не обновляется Исправлено — сигнатура унифицирована (app_state=None) во всех view
scan_all() вызывается 910 раз за один refresh Исправлено — TTL-кэш 30 с (unified_health.py:248)
Ложные HEALTHY/QUOTA_EXHAUSTED у неподключённых слотов Исправлено — введён STATUS_NOT_CONFIGURED, устаревшие записи квот чистятся
Иконка Python в панели задач ИсправленоSetCurrentProcessExplicitAppUserModelID("HermesHub.Desktop") + multi-res .ico
Лаг переключения вкладок Исправлено — предпрогрев всех 8 экранов в __init__, переключение через pack_forget()
Установщик не копирует assets/branding Исправлено в новом installer/HermesHubSetup.py.cs и install.ps1 — нет)
Нет документации по разработке/безопасности/производительности Исправлено — добавлены docs/AUTH.md, DEVELOPMENT.md, PERFORMANCE.md, ROUTER.md, SECURITY.md
Нет адаптивности окна Исправлено — debounce-обработчик resize

3. P0 — Блокеры (перепроверено на origin/main, всё воспроизводится)

# Дефект Файл Последствие
1 customtkinter и pillow не устанавливаются ни HermesHubSetup.py, ни HermesHubSetup.cs, ни install.ps1 все три установщика Запуск через pythonw.exe завершается молча: ни окна, ни ошибки. Проверено: в %LOCALAPPDATA%\hermes\hermes-agent\venv пакета нет
2 ProfileAuthManager.get_profile_dir не существует — это модульная функция, а не метод класса profile_manager.py:79 vs gui_server.py:216, hermes_hub_app.py:120, add_account_wizard.py:200 AttributeError при удалении credentials и при сохранении API-ключа
3 json не импортирован, но используется ui/add_account_wizard.py:200 NameErrorCodex и OpenCode Go нельзя подключить через UI вообще
4 AutoAssigner.auto_assign_all не существует hermes_hub_app.py:411, team_view.py:188,243 Кнопки «Перераспределить роли» и «⋮» падают
5 agy_generate возвращает ошибки как успешный chat.completion: текст ошибки в content, ключа error нет agy_subprocess.py:584 + router_engine.py RouterEngine проверяет if "error" in response → не срабатывает → mark_success, failover для Antigravity не работает никогда, пользователь получает текст ошибки вместо ответа модели
6 Мастер ждёт session.status == "error", сессия ставит "failed" add_account_wizard.py:243 vs profile_oauth.py При ошибке OAuth мастер молча висит 120 с
7 Кнопка «Назначить» шлёт action assign_role, обработчика нет accounts_view.py:244 vs hermes_hub_app._handle_action Кнопка не делает ничего, без обратной связи
8 Шаг 4 мастера не применяет выбранную роль — только пишет в журнал add_account_wizard.py:_finish Выбор пользователя игнорируется
9 Шаг 3 показывает «✓ Аккаунт успешно проверен» без всякой проверки для API-ключей; список моделей захардкожен (gpt-4.1, qwen-coder, deepseek-v2) add_account_wizard.py Ложноположительная валидация, вымышленные модели

4. P1 — Производительность и отзывчивость UI

# Проблема
10 _restore_status() вызывает get_system_readiness() в UI-потоке через after(6000, …). При холодном/истёкшем кэше — блокировка окна на время сетевого скана 16 профилей
11 При промахе кэша scan_all() опрашивает 16 профилей последовательно (таймаут 10 с на профиль) — нет параллелизма и общего бюджета времени
12 5 экранов (accounts, health, providers, routing, logs) полностью уничтожают и пересоздают виджеты через winfo_children() + destroy() — мерцание, потеря позиции скролла
13 Нет периодического автообновления: данные устаревают до ручного нажатия «Обновить» (таймер только для resize)
14 Тост затирает строку состояния и восстанавливается блокирующим вызовом

5. P1 — Логика роутера и корректность состояний

Файлы router_engine.py, health_tracker.py, agy_subprocess.py, profile_manager.py, router_config.py, session_affinity.py, hermes_plugin.py в 7609ad8 не менялись — все находки в силе.

# Проблема Файл
15 _CM_LOCK удерживается всё время subprocess agy (до 180+ с) → все Antigravity-профили сериализуются, параллельная работа команды невозможна antigravity_adapter.py
16 Профильные токены пишутся в глобальную запись gemini:antigravity Credential Manager и не восстанавливаются → «основной аккаунт Hermes» меняется как побочный эффект маршрутизации; гонки с самим agy и Hermes CLI antigravity_adapter.py
17 overall_state не сбрасывается при истечении cooldown (только в mark_success) → профиль остаётся «Квота исчерпана» бессрочно health_tracker.py:is_healthy
18 Роутер решает по состоянию семейства моделей, UI — по overall_state: роутер шлёт запросы в профиль, который UI показывает мёртвым health_tracker.py / unified_health.py
19 Session affinity без TTL и вытеснения — неограниченный рост памяти; session_affinity_ttl_seconds из шаблона конфига не реализован session_affinity.py
20 router_state.json пишется без межпроцессной блокировки — GUI и процесс Hermes затирают состояние друг друга health_tracker.py
21 ProfileHealthRecord.active_leases всегда 0, но выводится в CLI и API health_tracker.py, cli_commands.py
22 Категория TRANSIENT классифицируется, но обрабатывается как обычный отказ — без повтора и задержки router_engine.py
23 Классификация ошибок по подстрокам ("429" in err_lower) — ложные срабатывания на любом тексте с «429» все адаптеры
24 _safe_env() не используется: custom_env = dict(os.environ) передаёт в subprocess agy все ключи (OPENAI_API_KEY и пр.) agy_subprocess.py, antigravity_adapter.py
25 Нет стриминга ответов — Hermes получает только полный ответ router_engine.py
26 Нет учёта токенов, стоимости и latency между вызовами
27 В шаблоне router_profiles.example.yaml блок router: полностью игнорируется загрузчиком (ожидаются ключи верхнего уровня); max_failover_attempts, cooldown_base_seconds, cooldown_max_seconds, session_affinity_ttl_seconds отсутствуют в схеме router_config.py:load_router_config
28 save_router_config пишет другую структуру, чем шаблон → первое сохранение («назначить оркестратором») уничтожает комментарии и структуру пользовательского YAML router_config.py
29 max_concurrency=1 для Codex и OpenCode во встроенном дефолте, хотя это stateless REST (в шаблоне 2 и 3) — искусственное ограничение пропускной способности router_config.py
30 antigravity_llm_execution перехватывает все вызовы llm_execution независимо от провайдера и никогда не вызывает next_call; ошибка роутера логируется на уровне debug hermes_plugin.py
31 Каталог моделей захардкожен (gemini-3.7-flash, qwen3.8-max, kimi-k2.7-code…) и не сверяется с реальным router_config.py
32 discover_models() определяет каталог, вызывая agy с заведомо невалидной моделью и парся текст ошибки agy_subprocess.py
33 AutoAssigner.check_duplicate_identity сравнивает auth_data["email"], но profile_oauth сохраняет только {"token", "auth_method"}детектор дубликатов не срабатывает никогда auto_assigner.py

6. P1 — Безопасность

# Проблема
34 CLIENT_ID/CLIENT_SECRET Google зашиты в код и склеены из фрагментов строк ("GOC" + "SPX" + …, oauth.py:26-28) — приём обхода сканеров секретов, чужой OAuth-клиент. Требует явного решения: собственный клиент или документированное заимствование. Отчёты «0 секретов при сканировании» проходят именно из-за склейки
35 ProfileAuthManager.save_profile_auth пишет токены plaintext без ограничения ACL. docs/SECURITY_MODEL.md заявляет изоляцию «правами текущего пользователя ОС», но код прав не выставляет (chmod есть только в CredentialStore и на Windows не работает)
36 access_token передаётся в query-строке при верификации (tokeninfo?access_token=…) — попадает в логи
37 CORSMiddleware(allow_origins=["*"], allow_credentials=True) на локальном API с эндпоинтами delete, set-main, oauth/start (gui_server.py)
38 Лаунчер генерирует и исполняет Python-скрипт %LOCALAPPDATA%\hermes\hermes_hub_entry.py при каждом запуске — вектор подмены между записью и стартом
39 Нет подписи установщика и лаунчера; dist/checksums.txt формируется вручную и не проверяется при установке
40 В hermes-hub.log пишутся тексты ошибок провайдеров без санитизации (могут содержать фрагменты токенов)

7. P1 — Тесты и качество

# Проблема
41 pytest tests/ падает на сборе: test_ui_refinement.pyhermes_hub_appcustomtkinter
42 test_setup_exe_exists падает на чистом checkout: dist/ в .gitignore, ещё 2 теста уходят в skip. Реальный результат — 1 failed, 28 passed, 2 skipped, а не заявленные 35/35
43 Тесты мутируют реальные пользовательские данные: do_set_main пишет router_active_profile.json, do_set_orchestrator перезаписывает router_profiles.yaml, get_profile_env_dir создаёт каталоги профилей. Нет фикстуры с временным HERMES_HOME
44 test_installer.py запускает реальный установщик: копирование в %LOCALAPPDATA%, ярлык, запись в реестр
45 Тесты ходят в сеть (Google tokeninfo, OpenCode /models) — недетерминированность
46 Нет тестов на: classify_error всех адаптеров, мастер подключения, YAML round-trip load↔save, CLI, логику установщиков
47 Нет CI (.github/workflows отсутствует, репозиторий приватный)
48 ruff в dev-зависимостях, но нет секции [tool.ruff] и запуска; нет mypy

8. P1 — Упаковка, установка, дистрибуция

# Проблема
49 Два параллельных установщика: installer/HermesHubSetup.cs (711 строк, GUI-мастер, silent-режим, реестр, коды 0/10/11/12) и installer/HermesHubSetup.py (101 строка, копирует assets, но без реестра, ярлыка, silent-кодов). Неясно, какой канонический; dist/HermesHubSetup.exe собран из .cs
50 Проверка совместимости фиктивна: compatibility.json не читается, MIN_HERMES_VERSION ни с чем не сравнивается, IsHermesCompatible = true всегда при наличии Python → код возврата 11 недостижим. То же в install.ps1 (версия печатается, но не проверяется)
51 installer/HermesHubSetup.cs и scripts/install.ps1 не копируют assets/branding (исправлено только в .py-варианте)
52 ui/assets.py:39 — захардкоженный fallback Path("E:/Agent projects/hermes-hub") в исходниках
53 build_launcher.ps1 не передаёт /win32icon:HermesHub.icoу самого .exe нет иконки (иконка ставится только в рантайме через iconbitmap)
54 install.ps1 копирует __pycache__
55 launcher/HermesHub.exe — собранный бинарник в git; собранный установщик, наоборот, не отслеживается
56 Нет single-instance guard — повторный клик по ярлыку открывает второе окно
57 Нет логирования запуска: при pythonw.exe любая ошибка импорта = полная тишина
58 Нет механизма обновления в самом приложении
59 Версия расходится: 0.1.0 (pyproject, compatibility.json, installer) против 1.3.0 (About, FastAPI)
60 scripts/launch_hermes_hub.bat запускает несуществующий веб-режим на :8765; CLI-аргументы hub --port/--no-browser парсятся и игнорируются

9. P2 — Мёртвый и неподключённый код

# Объект Строк Статус
61 supervisor/lifecycle_supervisor.py, supervisor/policies.py, capability/capability_matrix.py, skills/skill_registry.py, scheduler/task_safety.py, adapters/deepseek_adapter.py 819 Roadmap-модули из 7609ad8 не подключены ни к одному кодовому пути. Импортируются только тестами (task_safety и deepseek_adapter — вообще ничем). DeepSeekResponsesAdapter не зарегистрирован в _ADAPTERS → недостижим через get_adapter()
62 gui_server.py + gui_cockpit.html 1177 Параллельный веб-стек, не запускается ниоткуда; fastapi/uvicorn остаются обязательными зависимостями
63 runtime.generate_chat_completion, antigravity_client.py, transform.py, openai_compat.py, cloudcode.py ~590 Прямой API-путь к Antigravity, не используется (весь трафик через agy CLI)
64 ui/views/dashboard_view.py («Главная») 141 Не подключён к навигации
65 ui/splash.py (SplashScreen) 77 Не используется
66 HealthTracker.get_status_summary() Не вызывается ниоткуда
67 Резолвинг hermes_home Продублирован в 5 файлах с тремя разными реализациями (HERMES_HOME / LOCALAPPDATA / ~/.hermes)
68 do_set_main/do_test_profile/do_delete_credentials Дублируются между gui_server.py и hermes_hub_app.py

10. P2 — Документация

# Проблема
69 docs/ARCHITECTURE.md и CHANGELOG.md описывают «Edge App Mode», «FastAPI Backend :8765», «health check gate (HTTP 200)» — этой архитектуры больше нет
70 agents/AGENTS.md и отчёты в agents/done/ указывают Source of Truth E:\Agent projects\hermes-hub; ссылки вида file:///E:/Agent%20projects/... мертвы
71 docs/SECURITY_MODEL.md не описывает фактическую подмену глобальной записи gemini:antigravity при маршрутизации
72 Нет описания схемы router_profiles.yaml (единственный источник истины — код) и справочника CLI
73 Отчёты в agents/done/ содержат утверждения, не подтверждаемые кодом: «35/35 PASSED», «интерактивные переключатели параметров», «9/9 экранов», «иконка вшита в лаунчер». Требуется выравнивание с фактами
74 UI жёстко на русском, CLI на английском, строки не вынесены — i18n невозможен

11. P2P3 — Функциональные пробелы

# Пробел
75 Настройки не сохраняются: CTkSwitch/CTkOptionMenu не привязаны к self.settings (нет ни одного command=), _save_settings() пишет неизменённый словарь
76 Ни один параметр не влияет на роутер: failover_attempts, session_affinity, auto_failover, model_timeout_sec, monitoring_interval_min нигде не читаются
77 Пути в «Настройках» указывают на hermes\router_profiles.yaml и hermes\auth.json; реальные — hermes\config\router_profiles.yaml и hermes\agy_profiles\<id>\auth.json
78 Нет ручного назначения профиля на роль (только автоматическое)
79 Экран «Маршрутизация» read-only: нельзя изменить цепочку failover
80 Нельзя добавить или удалить слот — 16 профилей жёстко зашиты в коде
81 Кнопка «Обновить модели» не вызывает adapter.discover_models() — показываются preferred_models из конфига
82 Фоновый мониторинг здоровья отсутствует, хотя переключатель для него есть
83 Нет уведомлений (tray/toast) об исчерпании квоты и переключении на резерв
84 Нет экспорта/импорта конфигурации и резервного копирования профилей
85 Нет истории квот и использования
86 Журнал живёт в памяти (200 записей) и обнуляется при перезапуске; hermes-hub.log пишется, но не читается обратно
87 В журнале нет поиска и фильтра по уровню (только по категории)
88 Тема жёстко тёмная, нет клавиатурной навигации и доступности
89 compatibility.json заявляет linux/darwin, но Credential Manager, пути и лаунчер — только Windows

12. Рекомендуемый план работ

Этап 1 — Вернуть работоспособность (P0, ~2 дня)

  1. Устанавливать customtkinter/pillow в venv Hermes из установщика; проверять импорт после установки и падать с внятной ошибкой.
  2. Исправить get_profile_dir (сделать @staticmethod либо импортировать функцию), добавить import json в мастер.
  3. Реализовать AutoAssigner.auto_assign_all() либо убрать кнопки.
  4. Ввести маркер ошибки в agy_generate ({"error": …} или исключение) и восстановить failover для Antigravity; покрыть регрессионным тестом.
  5. Починить мастер: статус "failed", реальная валидация API-ключа, реальные модели, применение выбранной роли, обработчик assign_role.
  6. Прогонять pytest в изолированном HERMES_HOME; убрать запуск установщика и сетевые вызовы из тестов; сделать test_setup_exe_exists skip-ом при отсутствии dist/.

Этап 2 — Корректность состояний и отзывчивость (P1, ~3 дня)

  1. Все сетевые операции — только в фоне; after(0, …) получает готовые данные (включая _restore_status).
  2. Параллельный опрос профилей с общим бюджетом времени вместо последовательного.
  3. Обновление виджетов in-place вместо destroy()/пересоздания в 5 экранах.
  4. Сброс overall_state при истечении cooldown; единый источник истины по здоровью для роутера и UI.
  5. TTL и вытеснение в SessionAffinityTracker; файловая блокировка router_state.json.
  6. Сузить _CM_LOCK до операции подмены credential и восстанавливать прежнюю запись — либо перейти на изоляцию только через USERPROFILE.
  7. Связать settings_view с реальными параметрами роутера; выровнять пути.
  8. Починить check_duplicate_identity (сохранять email в auth.json при OAuth).

Этап 3 — Безопасность и упаковка (P1, ~2 дня)

  1. Решение по OAuth-клиенту; прекратить склейку секрета из фрагментов.
  2. ACL/DPAPI для auth.json; санитизация логов; убрать токен из query-строки.
  3. Убрать allow_origins=["*"] либо удалить веб-стек целиком.
  4. Выбрать один установщик; реальная проверка версии из compatibility.json (сделать код 11 достижимым); исключить __pycache__; убрать E:/… fallback; встроить иконку через /win32icon.
  5. Логирование запуска в файл до инициализации UI; single-instance guard.

Этап 4 — Долг и документация (P2, ~2 дня)

  1. Подключить roadmap-модули к реальным кодовым путям (как минимум зарегистрировать DeepSeekResponsesAdapter в _ADAPTERS) либо явно пометить их как экспериментальные.
  2. Удалить или вынести в legacy/ веб-стек и прямой API-путь; убрать fastapi/uvicorn из обязательных зависимостей.
  3. Единый модуль paths.py; устранить дублирование do_*.
  4. Синхронизировать версию (0.1.0), обновить ARCHITECTURE.md/CHANGELOG.md под нативную архитектуру, исправить пути E:\.
  5. Выровнять отчёты в agents/done/ с фактическим состоянием.
  6. Добавить CI (pytest + ruff) и конфигурацию ruff.

Этап 5 — Развитие (P3)

  1. Ручное назначение ролей и редактор цепочек failover.
  2. Динамические слоты, реальный discover_models.
  3. Фоновый мониторинг + уведомления о квотах.
  4. Персистентный журнал с поиском; статистика использования.
  5. Стриминг ответов, учёт токенов и стоимости.
  6. Экспорт/импорт конфигурации, бэкап профилей.

13. Сводка

Приоритет Пунктов Область
Исправлено в 7609ad8 8 Кэш сканирования, resolver статусов, идентичность Windows, предпрогрев, assets, docs
P0 — блокеры 9 Запуск приложения, failover Antigravity, падения UI, мастер подключения
P1 — критично 51 Отзывчивость, корректность состояний, безопасность, тесты, упаковка
P2 — важно 20 Неподключённый код, архитектурный долг, документация
P3 — развитие 9 Новая функциональность
Итого к работе 89