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 that42eddb3was verified against. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
9.4 KiB
Ревью, раунд 3: 925e6c6 → 42dfe2c (N1–N3, S2–S4, R1–R4)
База: 65482e8 → проверено: 42dfe2c (4 коммита)
Тесты: 50 passed, 2 skipped, 3 deselected. Гейт: PASSED (Candidate v0.1.1).
Вердикт: ✅ Заявленное сделано и проверено. ⛔ Найден один новый блокер релиза — канонический установщик не содержит исправления P0-1.
1. Закрыто (проверено исполнением)
N1 — «Только резерв». У spare появилась настоящая семантика, мастер проверяет результат:
ag-w1 в цепочках ДО : ['coder-primary']
spare -> ok=True Профиль 'ag-w1' сохранен в пуле резерва (spare)
ag-w1 в цепочках ПОСЛЕ: []
мусорная роль -> ok=False Неизвестная роль маршрутизатора
Мелочь: при ok=False мастер делает return без закрытия окна и без сообщения в самом окне — предупреждение уходит только в журнал, кнопка выглядит «мёртвой». Лучше показать текст ошибки в модалке.
N2 — двойной префикс. Antigravity error: individual quota reached — один префикс.
N3 / S4 — сканер секретов. AST-обход с раскрытием конкатенаций и ''.join(...), allowlist для документированных публичных констант. Проверил синтетическим файлом:
CLIENT_SECRET = "".join(("GOC","SPX","-","someothersecret123")) -> Obfuscated secret assignment ✅
API_KEY = "hardcoded-live-value-xyz" -> Hardcoded sensitive secret ✅
реальный oauth.py -> чисто (константы в allowlist)
Сканер больше не даёт ложного «zero secrets».
OAuth — деобфускация. Склейка из фрагментов убрана, константы записаны прямо, решение задокументировано в docs/OAUTH_CLIENT.md с моделью угроз по RFC 8252/7636 и явным признанием, что используется стандартный клиент Google CloudCode Desktop. Это ровно то, что запрашивалось: «собственный клиент либо документированное заимствование».
S3 — allowlist хостов. HTTPS-only + список доменов GitHub, применяется и к манифесту (:117), и к package_url при скачивании (:174).
R1 / S2 — канал обновлений. Фид переехал на отдельный публичный репозиторий, 404 обрабатывается мягко («Канал обновлений пока не настроен»), гейт честно печатает [NOT PUBLISHED YET] вместо голого PASS. Формулировка сменилась на «Ready for Candidate».
R4 — настройки влияют на роутер. settings_service.py + чтение в route_request. Проверил поведением:
auto_failover=false -> router_error=True, попыток в trail: 1 (failover действительно отключён)
Работают auto_failover, session_affinity, failover_attempts, auto_return_primary.
2. ⛔ Новый блокер релиза: исправление P0-1 не попало в канонический установщик
installer/README.md (новый файл) объявляет:
Canonical Binary:
HermesHubSetup.exe(built fromHermesHubSetup.cs)HermesHubSetup.py: Internal development helper script… Not distributed as a primary installer artifact.install.ps1: Deprecated legacy bootstrap script.
При этом проверка кода:
HermesHubSetup.cs (канонический) |
HermesHubSetup.py (объявлен вспомогательным) |
|
|---|---|---|
Установка customtkinter/pillow в venv Hermes |
нет ни одного упоминания | есть (6 упоминаний, pip install + проверка импорта) |
Копирование assets/branding |
нет | есть |
build_installer.ps1 собирает dist/HermesHubSetup.exe именно из .cs.
Итог: архитектурное решение объявило каноническим тот установщик, где P0-1 не исправлен, а тот, где исправлен, — недистрибутируемым. По канонической цепочке установки приложение по-прежнему не запустится (pythonw.exe молча завершится на import customtkinter), и логотипов не будет.
Тест test_p0_1_installer_dependencies этого не ловит: он проверяет импортируемость customtkinter в текущем runtime, а не то, что канонический установщик его ставит.
Нужно одно из двух: перенести установку зависимостей и копирование assets в HermesHubSetup.cs, либо объявить каноническим .py и пересобрать конвейер под него. Плюс тест, проверяющий именно канонический артефакт.
3. Заявлено, но кодом не подтверждено
S7. В таблице отчёта: «unit tests isolated from Windows Registry/Start Menu». Фактически tests/test_installer.py не менялся с раунда 1 и по-прежнему делает subprocess.run([SETUP_EXE, "/silent"]) (строки 53, 68). Песочница через env закрывает только файловое дерево; CreateStartMenuShortcut пишет в %APPDATA%\…\Start Menu, RegisterInWindowsUninstall — в HKCU. Это политика в installer/README.md, а не изоляция в коде. Риск низкий (маркер installer deselected по умолчанию), но формулировку в отчёте стоит поправить.
4. Мелкие замечания
M1. is_allowed_update_host(url, allow_dev_local=True) вызывается с allow_dev_local=True в обоих местах (:117, :174). Внутри: if url.startswith("file://") or Path(url).exists(): return allow_dev_local or os.environ.get("HERMES_HUB_DEV_MODE") == "1". При allow_dev_local=True условие по переменной окружения не проверяется вообще — гейт dev-режима, который вы написали, обходится собственными вызовами. Локальный путь в package_url пройдёт allowlist в продакшене.
M2. Гейт: заголовок «2. P0 Release Blockers (16/16)», сообщение — «12/12 P0 release blockers verified». Косметическое расхождение.
M3. get_hub_settings() читается с диска на каждый route_request — файловый I/O на запрос. Стоит кэшировать с инвалидацией по mtime.
M4. Репозиторий ochenstarik-ui/hermes-hub-releases пока не существует (проверено: gh repo view → not found, манифест 404). Ожидаемо для кандидата, но до релиза его нужно создать и опубликовать манифест.
5. Остаточное из прошлых раундов
- R3. Комментарии в
router_profiles.yamlпо-прежнему стираются при сохранении (5 → 0). Данные и структура целы. - R4 частично.
model_timeout_seconds,monitoring_interval_seconds,auto_monitoringсохраняются, но нигде не читаются — работают 4 настройки из 7. - Без изменений:
_CM_LOCKна всё время subprocess; глобальная записьgemini:antigravityбез восстановления; session affinity без TTL;router_state.jsonбез межпроцессной блокировки; roadmap-модули не подключены; веб-стек мёртв.
6. Рекомендация
- Merge — да. Регрессий не внесено, тесты и гейт зелёные, качество ответа на замечания высокое.
- Кандидатом v0.1.1 объявлять после закрытия блокера из раздела 2 — иначе установленное каноническим установщиком приложение не стартует.
- M1 — быстро и по делу (убрать
allow_dev_local=Trueиз продакшен-вызовов). M2, M3, S7-формулировка — в обычную очередь.