Проверено исполнением на настоящем ~/.local/bin/agy:
- SHA-256 бинарника до и после определения состояния совпал (файл не тронут);
- на живом файле получено check_active «Проверка на месте», версия 1.1.24;
- сигнатуры детектора побайтово совпадают с гейтом open-antigravity-patcher;
- «определить не удалось» отделено от «не пропатчен»;
- опроса в цикле не добавлено (setInterval/setTimeout в диффе app.js нет);
- ruff чисто, 721 passed / 2 skipped (порог задания — 704).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G1hLjqc1n8grdmhb8JiDte
Владелец: «в программе нет аккаунтов. я добавил первый… аккаунты так и не
появились». При этом вход проходил, и agy отдавал одиннадцать моделей.
Список аккаунтов строится по конфигурации маршрутизатора. Вход через терминал
завершается своим путём, мимо add_account, и записи в конфигурации не создаёт:
учётные данные на диске, get_profile_status отвечает «подключён», а в интерфейсе
пусто. Воспроизведено исполнением — профиль с ключами agy и нулём записей в
конфигурации не виден нигде.
Теперь при успешном завершении входа профиль регистрируется, и только потом
идёт обновление состояния. Неудача регистрации возвращается отказом с причиной,
а не молчаливым успехом: аккаунт, о котором сказано «подключён», но которого
нигде нет, — та же слепота, что и зависший мастер.
Проверено исполнением до и после правки: было «профилей в конфиге: 0, видно:
ничего», стало «профилей: 1 [ag-5], видно: antigravity[ag-5]».
708 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец не нашёл настройку. Она была в разметке, но её не было на экране.
arrangeSettingsPanels пересобирает настройки по жёсткому списку
идентификаторов, переносит перечисленные строки в новые карточки, а исходную
удаляет целиком — вместе со всем, чего в списке нет. Новое поле попало под
удаление и просто перестало существовать.
Добавлена группа «Сеть и доступ» с полем прокси. Название уточнено до
«Прокси / VPN для провайдеров»: владелец называет это впном, и искать он будет
по этому слову.
Устройство, которое так теряет настройки, тоже исправлено: строки, не попавшие
ни в одну группу, собираются в карточку «Прочие настройки», а не выбрасываются.
Забыть настройку в списке всё ещё можно, потерять её с экрана — уже нет.
Обращение к отсутствующему элементу защищено: опечатка в списке больше не
роняет сборку экрана целиком.
Четыре теста закрывают это: каждый перечисленный идентификатор существует в
разметке, поле прокси сгруппировано, остаток забирается до удаления карточки.
704 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец: «справа постоянно выходят статусы, прям без остановки. я вообще
ничего не вижу за ними».
Это был не таймер, а замкнутый круг. Отрисовка настроек запускала опрос
состояния сжатия; executeAction на успехе вызывал fetchSnapshot; тот снова
перерисовывал настройки — и так без конца. Интерфейс сам себя кормил запросами
к серверу, показывая на каждом обороте два тоста: на запрос и на ответ. Тем же
путём заливал «poll_native_auth» во время входа.
Опросы, которые запускает сам интерфейс, а не владелец, теперь молчат и не
дёргают снапшот — второе и разрывает круг. Отказ опроса показывается там, где
его запросили: у мастера входа для этого своя область сообщений.
Состояние сжатия запрашивается при открытии экрана настроек и по кнопке, а не
при каждой отрисовке.
700 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Вход через терминал прошёл: agy запустился в изолированном каталоге ag-5 и
опознал аккаунт владельца. Отказал Google: «Eligibility check failed: not
currently available in your location». Проверка смотрит на адрес выхода.
У владельца есть узлы 3x-ui в разных странах. Поэтому обход делается выходом
через разрешённую страну, а не патчем чужого бинарника: ничего не ломается при
обновлении Antigravity и не выполняется сторонний код.
Адрес задаётся общий в настройках и отдельный на профиль — разным аккаунтам
может требоваться разная страна. Применяется к запросу каталога моделей, к
вызовам моделей и к сценарию входа в терминале.
Пишутся и заглавные, и строчные имена переменных: Go читает HTTPS_PROXY,
многие библиотеки — https_proxy; ALL_PROXY нужен для socks5.
Адрес проверяется по существу, а не по схеме: приписать socks5:// можно чему
угодно, и тогда мусор выглядел бы принятым, а обращения провайдера молча
ломались бы без внятной причины.
695 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Мастер честно сообщил: «Терминал /usr/bin/xfce4-terminal завершился сразу с
кодом 1, окно не открылось» — новая проверка запуска сработала.
Причина в моей же правке. Терминал запускался с HOME, подменённым на каталог
профиля, а клиенты X11 берут ключ авторизации из ~/.Xauthority: в
agy_profiles/ag-5 такого файла нет и быть не может, поэтому подключиться к
дисплею терминал не мог. Подменять HOME терминалу и не требуется — это делает
сценарий входа, уже внутри окна, перед самым запуском agy.
Заодно устранено побочное действие в запросе: сценарий входа создавался внутри
функции ПОИСКА терминала. Теперь его готовит start_native_agy_login, а поиск
только отвечает на вопрос и ничего не создаёт.
Владелец не мог понять, обновился ли хаб: на Linux строка сборки в боковой
панели была пуста, потому что заполнялась из панели обновлений, а та
подтягивается только при открытии. Теперь номер берётся из снапшота и
показывается вместе со временем запуска процесса.
Показывается running_commit — снятый ОДИН РАЗ при старте процесса. Поле commit
читается из манифеста на диске при каждом запросе, поэтому переживший
обновление процесс рапортует им свежий номер при старом поведении; на этом я
уже спотыкался при разборе окон консоли. Снятое при старте значение отвечает на
настоящий вопрос: какой код сейчас в памяти.
678 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Мастер сообщил «Графический дисплей не обнаружен», хотя окно хаба было открыто
на рабочем столе владельца. Причина: хаб запускается через nohup и наследует
окружение той оболочки, из которой его запустили. Запуск по SSH или службой
оставляет процесс без DISPLAY.
Наследование не единственный источник. Измерено на сервере владельца:
loginctl show-user ochenstarik -p Display даёт c1, а show-session c1 —
Type=x11, Display=:10, Active=yes. Спросить у системы честнее, чем сдаться.
Теперь дисплей ищется сначала в окружении, затем у systemd, и найденное
значение передаётся в окружение терминала — иначе окно не открылось бы даже
при верном обнаружении. Отказ остаётся правомерным, только когда графического
сеанса не знает и systemd; в сообщении перечисляется всё проверенное, включая
сам loginctl.
XAUTHORITY не выставляем: клиенты X11 по умолчанию берут ~/.Xauthority того же
пользователя, а хаб работает под ним же.
674 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец спросил, откуда 25 каталогов профилей, когда заводил единицы.
Проверено исполнением: обращение к get_profile_dir создавало каталог. Спросили
четыре пути — появились четыре каталога.
В коде зашит список «стандартных» слотов — ag-orch-primary, ag-orch-fallback,
ag-1..ag-20, ag-w1..ag-w10 — в двух местах: agy_subprocess и
model_discovery_service. Любой обход этого списка материализовал их все. Отсюда
ag-w1..ag-w4, ag-cold-*, ag-spare-* — владелец их не создавал.
Спросить, где профиль жил бы, и завести его — разные действия. Создание теперь
запрашивается явно, create=True, и это делают только те, кто действительно
заводит профиль.
Тесты и сценарий проверки, опиравшиеся на побочное создание, приведены к новому
поведению: они проверяют изоляцию пути, а не то, что каталог возник сам.
669 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец увидел «Терминал запущен (/usr/bin/x-terminal-emulator) для слота
ag-6», но окна не появилось. На сервере в это время висел зомби
[xfce4-terminal] <defunct>: терминал стартовал и немедленно умирал.
Причин три.
x-terminal-emulator на Ubuntu указывает на xfce4-terminal.wrapper, а
xfce4-terminal держит один процесс на сеанс: новый вызов передаёт задание уже
работающему экземпляру и завершается. Нужен --disable-server. Конкретные
эмуляторы теперь пробуются раньше обёртки над альтернативами.
При такой передаче команда выполняется в окружении СТАРОГО экземпляра, и
подменённый HOME не применяется — вход ушёл бы в настоящий домашний каталог
владельца мимо всей изоляции слотов. Теперь терминал запускает сценарий,
который задаёт HOME сам, а не полагается на наследование.
С ключом -e окно закрывается вместе с командой, и причину отказа прочесть
нельзя. Сценарий печатает код возврата agy и ждёт нажатия клавиши. На Windows
по той же причине cmd /k вместо /c.
Отдельно: возврат Popen об открытии окна не говорит ничего, а мастер выдавал
его за успех. Теперь запуск подтверждается тем, что процесс прожил хотя бы
секунду; мгновенное завершение сообщается с кодом возврата.
Тесты подменяли глобальный os.name, а его читает pathlib при выборе класса
пути: на Windows это роняло и проверяемый код, и сам pytest, как только в
ветке для Linux появилась работа с файлами. Заменено явной проверкой системы.
669 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено исполнением, а не по отчёту.
Работает. Профиль, где лежит только файл, записанный agy, признаётся
подключённым, и хаб этот файл не переписывает — главный критерий приёмки
выполнен. Поиск терминала честный: перечисляет проверенных кандидатов, а
DISPLAY, WAYLAND_DISPLAY, XAUTHORITY и DBUS_SESSION_BUS_ADDRESS внесены в
список разрешённых переменных, иначе окно терминала не открылось бы. На
сервере владельца найдены x-terminal-emulator, gnome-terminal,
xfce4-terminal, xterm.
Исправлено два дефекта.
1. Почта после входа искалась в google_accounts.json, auth.json и id_token.
У свежего слота первых двух нет, а antigravity-oauth-token у владельца
занимает 505 байт — id_token туда не помещается. Последняя попытка
разбирала токен доступа как JWT, но ya29-токен Google не JWT и claims не
несёт. Пустая почта отключает проверку двойников, и рост номеров слотов,
починенный в 9641957, вернулся бы. Теперь почта запрашивается у UserInfo —
тем же способом, каким её узнаёт браузерный вход. Отказ сети вход не
роняет: аккаунт подключён, почта Н/Д.
2. В существующем тесте test_seq_token_prevents_stale_refresh_clobber строгая
проверка была заменена на нестрогую. Прогнал исходную пять раз подряд и в
полном наборе — проходит. Ослабление было лишним, вернул; добавленную
исполнителем проверку seq оставил, она по делу.
Не выполнено исполнителем: живая проверка входа на сервере (P0-7.1) — вместо
неё двенадцать модульных тестов. Вход требует участия владельца, поэтому
проверить его сам не могу.
Неточности отчёта: «Release Gate 16/16» — это счёт внутри второго раздела, а
не итог гейта; обращения к UserInfo API в коде не было, оно добавлено здесь.
661 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено исполнением на сервере владельца, а не по отчёту.
Работает. Сжатие вызывается в настоящем пути запроса (local_adapter.py:168) —
разрыв, ради которого писалось задание, закрыт. Замер на живом компрессоре:
5667 токенов на входе, 624 на выходе, 0,11x, экономия 5043 токена за 127,6 с.
Исправлено четыре дефекта.
1. /props и /tokenize запрашивались по адресу с суффиксом /v1. У llama.cpp они
живут в корне: измерено, /props → 200, /v1/props → 404, то же с /tokenize.
Адаптер передаёт супервизору именно адрес с /v1, поэтому счёт токенов молча
падал на посимвольную оценку, и порог сжатия считался от выдуманного числа.
Адрес нормализуется.
2. Заявленные «сто процентов сохранения фактов» модель не даёт. Замер: 37 из 38,
97,4%, потерян 001cd1f. Сто процентов получались дописыванием недостающих
фактов списком — механизм верный, но измеренное число подменялось
исправленным, а «(100%)» было вписано в сообщение текстом. Теперь полнота
самой модели сохраняется отдельно и показывается владельцу: иначе ухудшение
модели осталось бы незамеченным.
3. Итог сжатия считался посимвольно (длина / 3.5) и подавался рядом с настоящим
числом токенов на входе. Теперь пересчитывается токенизатором сервера, а
недоступность токенизатора помечается признаком оценки.
4. Порт 8082 был зашит запасным адресом в двух местах вопреки прямому запрету в
задании. Профиль без адреса теперь даёт состояние «не настроен» с причиной,
а не молчаливый стук в 8082.
Не выполнено исполнителем: проверка на живом сервере (P0-6.1). Тест на неё
пропускается как негерметичный, замеры выше сделал ревьюер.
649 passed, 2 skipped; ruff чисто; релизный гейт пройден.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Реализован запуск agy в терминале с изолированным HOME (x-terminal-emulator, gnome-terminal, konsole, xfce4-terminal, tilix, alacritty, kitty, terminator, urxvt, foot, xterm / Windows wt, cmd)
- Опрос появления antigravity-oauth-token вместо ожидания процесса терминала
- Защита занятого слота от перезаписи без подтверждения
- Честное извлечение email без выдумывания identity и предотвращение дубликатов слотов
- Сохранён браузерный OAuth в качестве запасного пути
- Добавлены 12 тестов в test_a57_agy_native_login.py, 640 тестов проходят
В карточке владельца рядом стояли «Проверен: не работает — Please sign in» и
«Получено 11 моделей · 13:25:53». Источники разные: красная строка берётся из
состояния проверки, список — из кэша каталога, и обновляются они независимо.
Каталог был получен позже отказа, то есть провайдер с тех пор ответил, а
карточка продолжала утверждать обратное.
Теперь, если каталог получен без ошибки и позже неудачной проверки, вердикт
показывается как устаревший с предложением проверить заново. Объявлять аккаунт
рабочим не за что — проверка после этого не выполнялась, и выдумывать её
результат нельзя.
Отдельно установлено: поле commit в /api/health читается из файла на диске при
каждом запросе, поэтому устаревший процесс рапортует свежий коммит. Различать
сборки по нему нельзя.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Владелец видел «сохранение аккаунта и запуск проверки» и ждал. Зависанием это
не было: действие честно дожидалось проверки у провайдера. Для Antigravity она
идёт через CLI и в худшем случае складывается из 90 с на захват замка профиля,
65 на каталог моделей и 90 на пробный вызов — около четырёх минут молчания при
обещанной в интерфейсе «минуте на этап».
Сохранение учётных данных и назначение роли занимают миллисекунды. Теперь
действие возвращается сразу, а опрос провайдера ставится в фон; карточка
обновляется, когда он закончится, — снапшот и так опрашивается по таймеру.
Провайдеры с ключом поведения не меняют: их подключение проверяется
предварительной проверкой до сохранения и возвращается сразу, как требует A54.
Если фоновая служба не работает, проверка по-прежнему выполняется на месте —
иначе результата не будет вовсе.
Надпись в мастере исправлена: обещание «до минуты на этап» не соответствовало
действительности.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Установка на сервере владельца прервалась на проверке: «No module named
'fastapi'». Причин две, и обе в установщике.
Первая: запуск через sudo. Установка пользовательская — всё ложится в
$HOME/.hermes и $HOME/.local/bin, root не нужен. Под sudo домашним каталогом
становится /root, венв Hermes там не находится, и установщик уходит на
системный python. Копия при этом ложится в /root/.hermes, где владелец её не
видит. Теперь запуск через sudo распознаётся и отклоняется с объяснением;
осознанный обход остаётся через HERMES_ALLOW_ROOT=1.
Вторая: системный python в Ubuntu 24.04 помечен EXTERNALLY-MANAGED (PEP 668) и
отклоняет pip install — и обычный, и с --user. Установщик обе неудачи проглатывал
(|| true) и продолжал работу до отказа на проверке. Теперь при защищённом
системном python создаётся собственное окружение $HERMES_HOME/venv, а неудача
установки зависимостей прекращает установку с внятным кодом возврата.
--break-system-packages не применяется: имя флага не преувеличивает.
Пусковик научен той же ветке — иначе запуск уходил бы на системный python,
где зависимостей нет и быть не может.
Проверено исполнением на сервере под непривилегированным пользователем:
окружение создано, зависимости установлены, HERMES_HUB_LINUX_VERIFY_OK,
запуск через sudo отклонён с кодом 3.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
agy 2.0 читает учётные данные не из .gemini/oauth_creds.json — это формат
Gemini CLI. Свой токен он берёт из .gemini/antigravity-cli/antigravity-oauth-token
в виде {"auth_method": ..., "token": {...}}. Хаб этот файл никогда не создавал,
поэтому «Авторизация успешно завершена» соседствовала с ответом
«Please sign in to view available models».
Установлено сравнением рабочего профиля владельца с неработающим: оба имели
oauth_creds.json одинакового размера, различие было только в этом файле.
Значение auth_method («consumer») взято из рабочего профиля, а не выведено
из общих соображений.
Срок годности пишется в обоих видах: expiry_date в миллисекундах для формата
Node и expiry строкой RFC3339 для Go-шного oauth2.Token.
Заодно устранён рост номеров профилей: слот выбирался до входа, когда почта
ещё неизвестна, и повторный вход тем же аккаунтом занимал очередной свободный
слот — один аккаунт владельца расползся на ag-2, ag-3, ag-4. Теперь после
опознания почты учётные данные возвращаются в слот, который этот аккаунт уже
занимает. Функция проверки двойников в проекте была, но её никто не вызывал.
Версия поднята до 0.1.3, чтобы отличать сборки на глаз. Проверка версии в
тестах сверяется с исходником, а не с записанным числом.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено ревьюером исполнением: подключение с выбором роли кладёт
аккаунт в её цепочку (developer-1 → ['openrouter-1']). Раньше выбор роли
до цепочки не доходил, и ни один аккаунт владельца ни в одной цепочке не
состоял.
Роль на карточке берётся из живой цепочки: role_assignments.get(pid, [])
вместо подстановки догадки. Таблица DEFAULT_SLOT_ROLES удалена — ссылок
на неё в дереве не осталось.
На маршрутизации показывается «Сейчас ответит: <аккаунт>» либо «никто»
с причиной. В плагине Hermes учитываются вызовы мимо хаба
(record_bypass в четырёх местах, счётчик bypassed_calls_count).
Обратное чтение сделано честно: get_hermes_config_status читает
конфигурацию Hermes ТОЛЬКО на чтение, с отдельным тестом на это.
Разрешение конфликтов с A49 и A50 (ветка A51 отведена от 17b368a):
- action_handler: взят вариант A50 — он надмножество, содержит и
назначение в роль, и защиту слота от чужого провайдера;
- auto_assigner: взят вариант A51 — таблица-догадка удалена;
подпись llama.cpp от A50 сохранена, она вне конфликта;
- settings_service, unified_health, app.js: обе стороны, правки
дополняют друг друга;
- тест свежести памяти: взята строгая проверка, вариант A51 молча
пропускал отсутствие записанного коммита.
Две правки ревьюера по итогам слияния:
- в app.js при сложении потерялась закрывающая скобка блока настроек;
- в add_account сохранение учётных данных вызывалось дважды, и второй
вызов обращался к auth_data, которой у уже авторизованного аккаунта не
существует. Перевод аккаунта в другую роль падал с ошибкой, хотя ключ
вводить не требуется. Дубль убран, оба теста A51 проходят.
554 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Проверено ревьюером исполнением, все восемь замечаний владельца закрыты:
Чужой слот. Раньше add_account с profile_id=ag-w1 для nvidia возвращал
ok=True и клал аккаунт в слот Antigravity. Теперь отказ с причиной:
«Слот ag-w1 не принадлежит провайдеру nvidia». Свой слот принимается,
идентификатор выдаётся верный.
Группировка по провайдерам восстановлена: скрытие заголовков групп,
добавленное в A48 (display:contents + display:none), убрано.
Автоматическая проверка запускается при старте веб-сервера
(_start_background_refresh), состояние и списки моделей больше не ждут
ручного нажатия.
Облачные модели Ollama: эндпоинт не выдуман — ревьюер проверил запросом,
https://ollama.com/api/tags отвечает 200 и отдаёт 19 моделей
(gpt-oss:20b, kimi-k2.6, glm-5.1 и другие).
Локальный провайдер подписан llama.cpp вместо «Локальный сервер».
Показ хода при долгом опросе честный: «может занять до минуты на этап».
Конфликты с A49 разрешены сложением: правки дополняют друг друга —
поле пути к хранилищу Obsidian и поле периода проверки аккаунтов,
стили вкладки скиллов и стили групп провайдеров. В тесте свежести памяти
взят вариант A50: коммит извлекается из файла, а не зашит.
545 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A44: отчёт A40 приведён в порядок. Размер контекста замеров указан
(-c 32768), штатный режим владельца вынесен отдельно: 196608, 13,6 ток/с,
25 490 МиБ — совпадает с независимым замером ревьюера (25 488). Столбец
VRAM пересчитан на потребление процесса через --query-compute-apps.
llama-swap описан; ревьюер подтвердил, что он установлен и работает на
порту 8090 с десятью моделями. Огрызок nemotron убран.
A49: роль skill-doctor добавлена (ролей стало 14), вкладка «Скиллы»,
обнаружение хранилища Obsidian по каталогу .obsidian с проверкой доступа
на запись.
Правка ревьюера: gguf перенесён из основных зависимостей в дополнение
benchmarks. Он используется только стендом замеров и продуктом не
импортируется, а в основных зависимостях заставлял каждую установку хаба
тянуть библиотеку разбора GGUF. Тест стенда получил importorskip: без
gguf он ронял СБОР всех тестов, а не пропускал себя.
526 passed, 1 skipped, ruff clean, релизный гейт 10/10 и на конфигурации
владельца, и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A48 (Codex): вёрстка по макетам. style.css изменён на 178 строк, добавлен
workspace.js, приложены 79 скриншотов — до, после, макеты для сверки.
Проверено глазами: логотип, шапка с поиском, панель инструментов холста,
карточки узлов с моделью и аккаунтом, читаемые подписи связей, инспектор с
вкладками, три карточки внизу. Выдуманных чисел из макета нет, пустые
состояния честные.
A47: хранилище AI-Memory под git локально, память проекта приведена к
действительности, worklog заполнен, составлен перечень агентов сервера.
A45: три кандидата замерены. Файлы и размеры сверены по диску, контрольная
сумма Qwen3-Coder пересчитана независимо и совпала. Скорость 109,6 ток/с
на 64К независимо НЕ перемерена: свободно 1,9 ГБ видеопамяти, проверка
потребовала бы остановить рабочий кодер владельца.
A42: подключение OpenRouter, NVIDIA и Ollama, отдельные ветки обнаружения
моделей, отказ с причиной вместо мнимого успеха.
Конфликт A42 с A41 разрешён в пользу A41: ветка A42 отведена от e7194d3,
до слияния A41 в main, поэтому в ней не было динамических префиксов.
Сохранены _get_prefix и генерация слотов; зашитый provider_slots не взят.
Расширенный список ролей openrouter из A42 принят.
Ожидание теста A42 поправлено: nvidia и nvidia-nim — псевдонимы одного
провайдера с одним адаптером, слоты у них общие.
517 passed, ruff clean, релизный гейт 10/10 и на конфигурации владельца,
и на пустой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. P0-1: В action_handler.py в add_account реализовано реальное сохранение профилей
и учетных данных для openrouter, nvidia, nvidia-nim, ollama, local, claude, opencode-go.
Для некорректных действий возвращается честная ошибка ok: False вместо мнимого успеха.
В AutoAssigner добавлены слоты и возможности для openrouter и nvidia.
2. P0-2: В ModelDiscoveryService убраны зашитые списки PID. Добавлены ветки
openrouter, nvidia и выделенная ветка ollama (/api/tags и /v1/models). Ошибки серверов
сохраняются и передаются в интерфейс.
3. P0-3: В unified_health.py разделены статусы временного отката ошибки (STATUS_COOLDOWN)
и исчерпания квоты (STATUS_QUOTA_EXHAUSTED). RATE_LIMITED проверяется до кулдаунов.
В health_tracker.py исключена пометка всего аккаунта при пустом model_name.
В codex_adapter.py уточнена классификация ошибок.
4. tests/test_a42_provider_connect.py: 15 тестов, 500 passed, ruff чисто.
1. P0-1: get_default_router_config() возвращает чистую конфигурацию (0 профилей,
13 канонических ролей с пустыми цепочками). Миграция не внедряет фиктивные профили.
2. P0-2: Учетные данные (~/.hermes/*_profiles/, hub_settings.json) изолированы и
никогда не затрагиваются при сбросе или установке.
3. P0-3: Профили создаются динамически при подключении аккаунтов (ag-1, codex-1, etc.).
Пустые цепочки ролей являются нормальным рабочим состоянием.
4. P0-4: Добавлен экшен reset_router_config и кнопка «Начать настройку заново»
в настройках с подтверждением и созданием бэкапа router_profiles.yaml.bak_<timestamp>.
5. P0-5: scripts/verify_multi_provider_router.py адаптирован и проходит 10/10 PASS
как на пустой конфигурации, так и на заполненной.
6. tests/test_a41_clean_install.py: 6 тестов, 490 passed, ruff чисто.
- Все строки отчёта BENCHMARK_REPORT.md содержат реальный путь, размер файла по os.stat, sha256 первых 64M и имя из general.name
- Отозваны все гипотетические оценки A38; проведены реальные замеры Phi-4 (83.3%, 58.5 ток/с), DeepSeek-Coder-V2 (75.0%, 61.5 ток/с), Granite-4.2-8B (66.7%, 80.6 ток/с)
- Описано падение скорости Multi-head Latent Attention (MLA) DeepSeek на 32k контексте до 3.35 ток/с
- Восстановлены штатные службы владельца на портах 8081 (Qwen3.8-27B) и 8082 (Qwen3-4B)
На машине владельца задана ANTHROPIC_BASE_URL=https://api.anthropic.com, и
из-за неё test_claude_health_check_real_probe падал: адаптер строил
".../models" вместо ".../v1/models". Код при этом верен — подставлялось
значение из окружения вместо умолчания.
Проверено: со снятой переменной 486 passed, с заданной — одно падение.
Набор обязан давать одинаковый результат на любой машине; это то же
требование герметичности, ради которого делался A33.
Добавлена автоматическая фикстура, убирающая восемь переменных *_BASE_URL на
время каждого теста. После правки прогон с заданной переменной даёт
486 passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1. OllamaAdapter: поддержка локального инстанса по умолчанию и удаленного Ollama API
с кастомным base_url и опциональным Bearer токеном, discovery по /v1/models и /api/tags,
статус квоты «Без ограничений».
2. ClaudeAdapter: реальный health_check API probe и динамический discover_models.
3. OpenRouterAdapter: обязательные заголовки HTTP-Referer и X-OpenRouter-Title,
сбор метаданных моделей (context_length, display_name).
4. NvidiaAdapter: парсинг заголовка Retry-After и динамическая задержка при 429.
5. Экспорт лимитов: эндпоинт GET /api/quotas/export (JSON / CSV), экшен export_quotas
и кнопка выгрузки в веб-интерфейсе с маскированием секретов.
6. tests/test_api_providers_a32.py: 15 тестов, 434 passed, ruff чисто.
1. P0-1: Восстановлены все веб-обработчики в app.js (openAddAccountWizard,
handleNodeAccountChange, handleNodeModelChange, handleRefreshProviderModels,
checkUpdates), связаны с потоками startDeviceAuth и startRedirectAuth,
выбор слота обязателен и понятен пользователю.
2. P0-2: Добавлены адаптеры OpenRouter и NVIDIA NIM с поддержкой динамического
base_url, множественных аккаунтов без ограничений, GET /models и честным
отображением квот/«Н/Д».
3. P0-3: В мастере подключения локального провайдера реализована кнопка автопоиска
(discover_local_models), отображение серверов, ошибок портов и автозаполнение.
4. P0-4: Полностью удален устаревший десктопный интерфейс CustomTkinter
(router/ui/** 20 файлов, hermes_hub_app.py), зависимости customtkinter и pillow
убраны из pyproject.toml и инсталляторов, оставлен единый ярлык «Hermes Hub».
5. 418 passed, 1 skipped, 4 deselected, ruff чисто.