hermes-hub/agents/inbox/2026-08-25-A27-in-app-updates.md
Hermes Team 7b7b527103 docs(agents): задание A27 — обновление из самой программы
Владелец обновляется вручную на трёх машинах. Механизм обновления в проекте
есть, но мёртв в каждом звене, и это проверено: веб-клиент check_updates не
вызывает вовсе (0 вхождений); DEFAULT_UPDATE_URL смотрит на заброшенный
второй репозиторий, чей манифест застыл на 0.1.1 от 21 августа; версия
захардкожена в 0.1.1 и подниматься не должна, поэтому сравнение по semver
никогда не скажет «есть обновление».

Рабочая часть — apply_update_sync с резервной копией, py_compile-проверкой и
откатом — сохраняется, переписывать её не нужно.

В задании принято решение, которое агентам не следует угадывать: признак
новизны — коммит, а не версия, источник — релизы основного репозитория, куда
поставка идёт на самом деле.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 17:39:26 +07:00

171 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Задание A27: обновление из самой программы
## Дата поступления
2026-08-25
## База
Проверочный HEAD на момент выдачи: **`a1e1db7`**.
## Ветка
`antigravity/in-app-updates`
## Порядок исполнения
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан для аудитора.
---
## Порядок работы с git
```
cd <каталог репозитория>; git fetch origin --prune; git status
git checkout main; git pull --ff-only origin main
git checkout -b antigravity/in-app-updates
git commit -m "..." <- сначала коммит
git push -u origin antigravity/in-app-updates
```
В `main` напрямую не пушить. В конце — push и проверка `git log --oneline -1`, `git status` чистый.
---
## Задача
Владелец: «а у нас реализовано обновление с программы? чтобы когда выходит новый ревью, в программе появлялось обновить? если нет, надо сделать».
Сейчас обновление ставится вручную: скачать установщик с релиза и запустить. На двух машинах и сервере это делается по несколько раз в день.
---
## Состояние на сегодня — проверено исполнением, заново не выясняйте
Механизм **существует**, но не работает ни в одном звене. Четыре причины, каждая подтверждена:
**1. Веб-интерфейс его не вызывает вообще.**
```
grep -c "check_updates" router/web/static/app.js -> 0
grep -c "check_updates" router/web/static/index.html -> 0
```
Кнопка была только в десктопе, который выводится из обращения.
**2. Источник обновлений — заброшенный второй репозиторий.**
`DEFAULT_UPDATE_URL` (`updater/update_manager.py:77`) указывает на
`ochenstarik-ui/hermes-hub-releases`. Репозиторий существует, манифест отдаёт `200`, но его содержимое:
```
version: 0.1.1
published_at: 2026-08-21T09:56:00Z
package_url: .../releases/download/v0.1.1/hermes-hub-0.1.1.zip
```
Это состояние **до** всей работы последних дней. Настоящая поставка давно идёт через релизы основного репозитория `ochenstarik-ui/hermes-hub` (тег `build-2026.08.25`), о которых обновлятор не знает.
**3. Версия не меняется и меняться не должна.**
`version.py:4``__version__ = "0.1.1"`, и в заданиях прямо запрещено создавать тег `v0.1.1`. Сборки в проекте различаются **коммитом**, а не semver: именно поэтому в `deployment_manifest.json` пишется `git_commit`. Сравнение версий в текущем виде **никогда** не скажет «есть обновление», даже если манифест обновить.
**4. Механизм применения при этом рабочий и его не надо переписывать.**
`UpdateManager.apply_update_sync` делает резервную копию `src/assets/config/launcher`, распаковывает пакет, проверяет каждый `.py` через `py_compile`, прогоняет дымовой импорт и **откатывается при любой ошибке**. Это ценная часть, сохраните её.
`paths.get_repo_root()` в установленной раскладке возвращает
`~/.hermes/plugins/antigravity-provider` (там есть `assets`), то есть цель применения верная.
---
## Решение, которое принято за вас
Чтобы не гадать: **признак новизны — коммит, а не версия.**
Источник — **релизы основного репозитория** `ochenstarik-ui/hermes-hub`. Второй репозиторий `hermes-hub-releases` из обращения выводится; трогать его не нужно, просто перестаньте на него смотреть.
Установленный коммит уже записан в `deployment_manifest.json` (`git_commit`), сборщики его туда кладут — и виндовый, и линуксовый через `BUILD_COMMIT`. Опубликованный коммит указан в заголовке и в описании релиза.
Если для сравнения удобнее отдельное поле — заведите его в описании релиза или в манифесте, но **выводите из реального коммита сборки**, а не подставляйте руками.
## P0-1. Определение «есть обновление»
Переписать `check_for_updates` на новый источник.
1. Опрашивается GitHub API релизов основного репозитория. Репозиторий публичный, токен не нужен; на анонимные запросы есть ограничение по частоте — учтите и не опрашивайте чаще, чем нужно.
2. Сравнивается **установленный коммит** с коммитом последнего релиза.
3. Совпали — «установлена последняя сборка». Разошлись — «доступно обновление» с датой релиза и описанием.
4. **Сеть недоступна — так и сказать.** Не «обновлений нет»: это разные утверждения, и путать их нельзя. Прежний код при `404` возвращал `update_available=False`, то есть отказ выглядел как «всё актуально».
Список разрешённых хостов (`ALLOWED_UPDATE_HOSTS`) сохраните и дополните, а не убирайте: качать код с произвольного адреса нельзя.
## P0-2. Кнопка в веб-интерфейсе
Сейчас её нет. Требуется:
1. **Тихая проверка при запуске** и далее по расписанию. Интервал — настройка, не константа.
2. Когда обновление есть — заметная, но не навязчивая отметка в шапке рядом с версией. Не модальное окно поверх работы.
3. По нажатию — что за сборка, когда опубликована, что изменилось, и кнопка установки.
4. Пока обновления нет — показывать установленную сборку и время последней проверки. Это и есть ответ на вопрос «а у меня свежее?».
Действие `check_updates` уже есть в `action_handler` (`action_handler.py:582`), второй реализации не заводить. Появятся новые — впишите в `docs/web-api/CONTRACT.md`.
## P0-3. Установка обновления на обеих платформах
Владелец работает на Windows и на Linux-сервере, где интерфейс открыт по сети. Обе поддерживаются.
Простой и честный путь: скачать **тот самый установщик из релиза**, который владелец сейчас качает руками, проверить контрольную сумму и запустить. Не изобретайте второй способ доставки — он немедленно разойдётся с первым.
Обязательно:
- **Проверка контрольной суммы до запуска.** Суммы публикуются в `checksums.txt` рядом с установщиками.
- **Сервер перезапускается сам** после установки, иначе владелец останется со старым процессом и решит, что обновление не сработало. На Linux запуск идёт через `~/.local/bin/hermes-hub-web`.
- **Откат при неудаче.** Механизм в `apply_update_sync` уже есть — используйте его, а не пишите заново.
- **Учётные данные и настройки не трогать.** `agy_profiles/`, `hub_settings.json`, `router_profiles.yaml` обязаны пережить обновление. Установщик это умеет («Preserving existing user router_profiles.yaml»), но проверьте отдельно и напишите в отчёте.
Если установку на какой-то платформе решите не автоматизировать — **так и напишите**, и покажите в интерфейсе готовую команду вместо кнопки. Молчаливо неработающая кнопка хуже честной команды.
## P0-4. Ограничение частоты и поведение при отказе
- Анонимный GitHub API ограничен по частоте. Упёрлись в предел — сообщить об этом прямо, а не выдавать за «обновлений нет».
- Проверка идёт в фоне и **не блокирует интерфейс**. Урок оплачен: `agy models` отвечал десятки секунд и подвешивал окно.
- Отказ проверки не должен мешать работе хаба.
## P0-5. Аудит вторым проходом
1. **Отсутствие данных не выдавать за результат.** Главный риск задания: «сеть недоступна» показать как «у вас последняя версия». Ровно эту ошибку делал прежний код при `404`.
2. **Проверить на настоящем релизе.** Не на заглушке: реальный запрос к API, реальное сравнение коммитов, оба исхода — «есть обновление» и «уже последняя».
3. **Проверить сохранность данных** после установки: аккаунты, настройки, цепочки ролей.
4. **Проверить откат**: подсунуть заведомо битый пакет и убедиться, что установка откатилась и хаб работает.
5. **Побочные изменения** объяснить.
6. **Пропущенный пункт назвать пропущенным.**
---
## Ограничения
- Десктоп (`router/ui/**`) не трогать.
- `apply_update_sync` и список разрешённых хостов не выбрасывать.
- Второй канал доставки не заводить: источник — релизы основного репозитория.
- Версию `0.1.1` не поднимать и тег `v0.1.1` не создавать.
- Правило честности без исключений.
## Критерии приёмки
1. Ветка в `origin`, `git status` чист.
2. `check_for_updates` смотрит на релизы основного репозитория и сравнивает коммиты; проверено настоящим запросом, вывод в отчёте.
3. Оба исхода показаны: «доступно обновление» и «установлена последняя сборка».
4. Недоступная сеть и упёртый предел частоты показываются как **отказ проверки**, а не как отсутствие обновлений; проверено.
5. В вебе видна установленная сборка и время последней проверки; при наличии обновления — отметка и описание.
6. Установка работает на Windows и на Linux **или** честно объявлена неавтоматизированной с показом команды.
7. Контрольная сумма проверяется до запуска установщика.
8. После обновления сервер поднят, аккаунты, настройки и цепочки ролей на месте; проверено.
9. Битый пакет вызывает откат, хаб остаётся работоспособным; проверено.
10. Проверка не блокирует интерфейс.
11. `ruff check .` чисто; релизный гейт не ухудшен.
12. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`, `X passed / Y skipped / Z failed`. На `main` сейчас **442 passed, 2 skipped**.
## Главное
Владелец обновляется вручную на трёх машинах по несколько раз в день. Нужно, чтобы программа сама сказала «вышла новая сборка» и поставила её, не потеряв аккаунты и не оставив владельца со старым процессом.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`. Сдано только после появления коммита в `origin`.