docs(agents): задание A60 — обновление доводит себя до конца

A59 довёл обновление до экрана: окно при запуске, видимая загрузка, честное Н/Д,
отмена. Дальше механизм обрывается на самом простом — установщик снимает тот
процесс, который его запустил и ждёт результата, поэтому перезапуск не наступает
никогда.

Корень один на обеих системах. На Linux install-linux.sh снимает всё по шаблону,
никого не исключая. На Windows StopOwnedRuntime строит цепочку предков, но
проверяет её только для дочерних процессов, а сам процесс-цель снимает
безусловно — прочитано по коду, подтвердить исполнением.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EDLuenXmGjWaS2rs8E72En
This commit is contained in:
ochenstarik-ui 2026-09-02 19:19:14 +07:00
parent 30c283f705
commit d2307362b5

View file

@ -0,0 +1,190 @@
# Задание A60: обновление доводит себя до конца
## Дата поступления
2026-09-02
## База
`origin/main` **после вливания A59**. Ветка A59 принята с исправлениями ревьюера
(`antigravity/a59-visible-update`, `2e35e2d`); пока она не в `main`, за это
задание не браться — иначе показ и загрузка будут переписаны второй раз.
```
git fetch origin --prune
git checkout -b antigravity/a60-update-completes origin/main
```
В `main` напрямую не пушить.
## Порядок исполнения
Два прохода: **Flash** реализует, **Pro** проводит аудит. Пункт **P0-5** написан
для аудитора.
Зона: применение обновления и перезапуск. Показа и загрузки не касается — там всё
принято и проверено.
---
## Что уже сделано — переделывать не надо
A59 довёл обновление до экрана. Ревьюер проверил и принял:
```
окно при запуске, отказ по версии, молчание без обновлений работает
полоса хода, честное Н/Д без размера работает
отмена с удалением недокачанного файла работает
отмена принимается только на проверке и загрузке работает
SHA-256 обязательна, непроверенный файл не запускается работает
запись о применённом обновлении только после успеха работает
```
Ничего из этого не трогать.
---
## Задача
Владелец нажимает «Обновить сейчас». Пакет скачивается на глазах, сумма сходится,
начинается установка — и на этом всё кончается. Хаб не поднимается, окно гаснет,
владелец идёт ставить сборку руками. Ровно то, из-за чего писалось A59.
---
## Разрыв, и он один на обеих системах
**Установщик снимает тот процесс, который его запустил и ждёт.**
`install_latest_update` делает так:
```
stop_running_hub() свои процессы, кроме текущего
subprocess.run(["bash", installer], timeout=600) ЖДЁТ здесь
schedule_restart() сюда управление не доходит
```
**Linux.** `installer/install-linux.sh:53` в своём `stop_running_hub` берёт
`pgrep -u $(id -u) -f "antigravity_provider.router.web|hermes_hub_web_entry"` и не
исключает никого. Под шаблон попадает тот самый питон, который висит на
`subprocess.run`. Установщик снимает его, `schedule_restart` не выполняется
никогда, окно прогресса умирает вместе с процессом. Скрипт при этом доживает
сиротой и файлы обновляет — поэтому со стороны выглядит как «поставилось, но не
запустилось».
**Windows.** `installer/HermesHubSetup.cs:280` (`StopOwnedRuntime`) выглядит
аккуратнее: строит `$protected` — цепочку собственных предков, чтобы не снять
того, кто его запустил. Но проверка `-notin $protected` стоит **только на
дочерних** процессах внутри `Stop-HubBranch`. Сам процесс-цель снимается
безусловно, а хаб под шаблон `antigravity_provider\.router\.web` подходит.
Прочитано по коду — **подтвердить исполнением, а не поверить на слово.**
Отката на путях установщика нет вообще: он есть только для `.zip`
(`apply_update_sync`). Ни `.sh`, ни `.exe` при срыве на середине ничего не
возвращают.
---
## P0-1. Отсоединённый помощник
Порядок не выдумывать заново — он описан в docstring `schedule_restart`: сначала
отсоединённый помощник, потом выход текущего процесса. Лаунчер считает хаб
работающим, пока порт отвечает, поэтому поднимать новый, не освободив порт,
бесполезно.
1. **Хаб порождает помощника** (`setsid` или отдельная группа процессов на Linux,
`DETACHED_PROCESS` на Windows), передаёт ему путь к уже проверенному пакету и
**выходит сам**, освободив порт.
2. **Помощник**: дожидается освобождения порта → запускает установщик → поднимает
хаб → завершается.
3. **Помощник не наследует** ни stdout хаба, ни его рабочий каталог: хаб исчезнет
раньше, чем помощник закончит.
4. **Помощник пишет свой ход в файл** `~/.hermes/updates/apply-<время>.log`, чтобы
после неудачи было что показать. Пустой отказ без причины — уже было в A59,
второй раз не проходит.
## P0-2. Владелец видит, что происходит
1. **Перед выходом статус `restarting`** с текстом, что хаб сейчас закроется и
поднимется сам. Не «установлено» — установка ещё идёт.
2. **Интерфейс переживает разрыв.** Опрос `get_update_progress` получит отказ
соединения: это ожидаемое состояние, а не ошибка. Показывать «Hermes Hub
перезапускается», продолжать пробовать, при возврате — перечитать страницу.
3. **Не молчать бесконечно.** Не поднялся за отведённое время — сказать это прямо
и назвать путь к журналу помощника.
## P0-3. Откат на путях установщика
1. **Помощник снимает копию установленного** до запуска установщика.
2. **Установщик вернул не ноль или хаб не поднялся** за отведённое время — вернуть
прежнее и поднять его.
3. **Причина отказа**с кодом возврата и хвостом вывода — в журнал и на экран
при следующем старте.
4. **Программа обязана остаться работоспособной.** Это главное требование пункта:
неудачное обновление не имеет права оставить владельца без хаба.
## P0-4. Проверка исполнением
Тестами это не ловится: дело в живом переходе между версиями и в том, кто кого
снимает.
1. **Поставить `v0.1.2-b7`, обновиться на `v0.1.3-b1` через интерфейс.** Оба
релиза опубликованы, установщики и `checksums.txt` на месте. Скриншоты: окно,
полоса, экран после возврата.
2. **Замерить время** от нажатия до готовности.
3. **Проверить, что работает новый код** — по `running_commit`, снятому при старте
процесса, а не по номеру версии.
4. **Повторить на Windows.**
5. **Сорвать установку намеренно** (испорченный установщик) — проверить откат и
что хаб жив.
6. **Проверить, что старый процесс не остался** и порт занят новым.
## P0-5. Аудит вторым проходом
1. **Пройти обновление целиком на обеих системах.**
2. **Проверить, что помощник не снимает чужого** — только процессы хаба своего
пользователя.
3. **Проверить откат** при сорвавшейся установке и что после него хаб отвечает.
4. **Проверить, что интерфейс не объявляет успех раньше времени** — ни на выходе
хаба, ни на разрыве связи.
5. **Побочные изменения** объяснить.
6. **Пропущенный пункт назвать пропущенным.** В A59 живая проверка была пропущена
молча, при том что релиз для неё был опубликован за девять часов до сдачи.
---
## Ограничения
- Показ и загрузку из A59 не переделывать.
- Проверку SHA-256 и список разрешённых адресов не ослаблять.
- Показ работающего коммита и времени запуска не ломать.
- Фронтенд без npm, без сборки, без фреймворков — по `docs/web-api/CONTRACT.md` §1.
- **Правки ревьюера из `main` не откатывать — включая комментарии.** В A59 сняли
шесть блоков с объяснением прошлых регрессий, ревьюер возвращал их руками.
- Версию `0.1.3` не понижать.
- Правило честности без исключений: неизвестное — `Н/Д` с причиной, а не
правдоподобное число и не полоса во всю ширину.
## Критерии приёмки
1. Ветка в `origin`, `git status` чист.
2. Обновление, запущенное из интерфейса, доходит до работающего нового хаба **без
участия владельца** — на Linux и на Windows, подтверждено скриншотами.
3. После перезапуска `running_commit` соответствует новой сборке.
4. Сорвавшаяся установка откатывается, хаб остаётся работоспособным.
5. Старый процесс не остался, порт занят новым.
6. Журнал помощника пишется, и при отказе на него указывают.
7. `ruff check .` чисто; релизный гейт пройден; тестов не меньше **725**.
8. Отчёт: `START_HEAD`, `FINAL_HEAD`, `origin/main`, `git status`,
`X passed / Y skipped / Z failed`, тайминги, скриншоты.
## Главное
A59 сделал обновление видимым: владелец видит окно и видит загрузку. Дальше
механизм обрывается на самом простом — установщик снимает того, кто его запустил
и ждёт результата.
Задание про один шаг: чтобы после нажатия «Обновить сейчас» владелец больше
ничего не делал.
## Порядок сдачи
Передать точный `FINAL_COMMIT_SHA`.