hermes-android/agy-work/TASK-2026-08-24-10-cleanup-and-release.md

132 lines
16 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.

# Task 10: Чистка мёртвого слоя и подготовка релиза (hermes-android)
**Repo:** `ochenstarik-ui/hermes-android`
**Assigned to:** Antigravity (режим оркестратора, два кодера)
**Priority:** HIGH (без этого приложение нельзя опубликовать)
**Date:** 2026-08-24
**Base SHA:** результат задания 09 — указать фактический SHA при выдаче
**Зависимость:** задания 0109 приняты. Это последнее задание цикла: оно закрывает остаток находок аудита и приводит проект к состоянию, пригодному для выпуска.
## Роли и протокол
| Роль | Модель | Что делает |
|---|---|---|
| Оркестратор | Antigravity | Разбивает работу, маршрутизирует, принимает результат |
| Кодер 1 | Gemini Flash 3.7 high | Реализация §Scope целиком |
| Кодер 2 | Gemini Pro high | Независимая проверка работы кодера 1 и доработка |
**Раунд 1 (кодер 1).** Пункты §Scope по порядку. Пункт 1 (удаление) выполняется **после** пункта 2, чтобы перенос зависимости не потерялся вместе с удаляемым файлом.
**Раунд 2 (кодер 2).** Независимая проверка: сборка релизного варианта из чистого клона, проход §Anti-checklist с явными отметками, доводка, findings по шкале.
**Раунд 3 (оркестратор).** Приёмка при собранном и установленном на устройство релизном APK с включённым R8 и при закрытии всех 63 находок аудита — итоговая таблица обязательна.
## Проблема
**1. Полторы тысячи строк прошлой архитектуры в сборке (`DEAD-01`).** Недостижимы из `MainActivity`: `feature/connections/ConnectionsScreen.kt` (435 строк), `feature/connections/ConnectionsViewModel.kt` (122), `feature/sessions/SessionsScreen.kt` (271), `feature/sessions/SessionsViewModel.kt`, `core/repository/HermesGatewayRepository.kt` (553), `core/repository/ConnectionRepository.kt`. Два репозитория с похожими именами путают при чтении и раздувают сборку.
Ловушка: `ConnectionRepository.kt:16` объявляет расширение `val Context.dataStore` — от него зависит `MigrationHelper`. Удаление файла без переноса этой строки ломает миграцию, исправленную заданием 08.
**2. Мелкий мёртвый код (`DEAD-02`).** Неиспользуемое поле `json` в `GatewayEvents.kt:178`; лишние импорты в `MainActivity`; `A2AContextBinding`, `HermesMessage`, `UnifiedMessageSource.A2A`, алиас `TokenVault.getAllConnectionIds` (`:19`), `HermesDatabase.createInMemory` — если он не был задействован заданием 04. `HermesApplication.onTerminate` (`:14-17`) на реальных устройствах не вызывается: код создаёт ложное впечатление, что `applicationScope` освобождается.
**3. Release-вариант непригоден к публикации (`BUILD-02`).** `app/build.gradle.kts:23-30``isMinifyEnabled = false`, `signingConfig` отсутствует; `app/proguard-rules.pro` содержит одну строку `-keepattributes`. Собрать подписанный релиз командой из README нельзя, R8 не работает, размер APK не оптимизируется.
**4. Приложение без иконки (`UI-05`).** `AndroidManifest.xml:12-14``android:icon="@android:drawable/sym_def_app_icon"`, системная заглушка. Ни adaptive-иконки, ни монохромной для Material You, ни каталога `mipmap`.
**5. Ни локализации, ни доступности (`UI-08`).** `stringResource` не используется ни разу — 59 текстов зашиты литералами, `strings.xml` мёртвый файл. `contentDescription = null` встречается 32 раза, в том числе на функциональных иконках переключения хоста и остановки выполнения: для TalkBack они невидимы.
**6. Тема конфликтует с edge-to-edge (`UI-09`).** `values/themes.xml` — родитель `android:Theme.Material.Light.NoActionBar` с жёстко заданными цветами системных полос, при том что `MainActivity.kt:44` вызывает `enableEdgeToEdge()`, а Compose-тема поддерживает тёмную схему. В тёмном режиме полосы и стартовое окно остаются светлыми.
**7. Экран настроек недостижим (`UI-10`).** `MainActivity.kt:156-162` объявляет маршрут `"settings"`, `navigate("settings")` не вызывается нигде. Содержимое — статический текст, утверждающий про «одноразовые WS-тикеты с TTL 30 с», хотя клиент это свойство не проверяет.
## Scope
**1. Перенос зависимости, затем удаление (`DEAD-01`).**
Сначала перенести объявление `Context.dataStore` в `MigrationHelper` (или в отдельный файл рядом с ним) и убедиться, что тест миграции задания 08 проходит.
Затем удалить шесть перечисленных файлов одним коммитом. Тесты, ссылающиеся на удаляемое, к этому моменту уже переписаны заданием 04 (`TEST-04`) — проверить, что не осталось ни одной ссылки.
**2. Мелкая чистка (`DEAD-02`).**
Удалить перечисленное. `onTerminate` убрать; если реакция на уход в фон нужна — опереться на `ProcessLifecycleOwner`, но только если это требуется заданием 08, иначе просто удалить.
`createInMemory` не трогать, если задание 04 сделало его используемым.
**3. Релизная сборка (`BUILD-02`).**
Включить `isMinifyEnabled = true` и `shrinkResources = true`. Правила ProGuard для kotlinx.serialization, Room и моделей, участвующих в сериализации, — с проверкой, что сериализация после R8 работает на устройстве, а не только компилируется.
`signingConfigs` из `keystore.properties` / переменных окружения; сам keystore в репозиторий не класть. Ключ и пароли предоставляет владелец — запросить, не генерировать самовольно ключ, которым потом будет подписан публичный релиз.
Добавить сборку релизного APK в пайплайн задания 04 (без подписи, если секретов в CI нет).
**4. Иконка (`UI-05`).**
`ic_launcher` с foreground/background/monochrome во всех плотностях. Заглушку из манифеста убрать.
**5. Строки и доступность (`UI-08`).**
Вынести тексты в `strings.xml`. `contentDescription` задать всем интерактивным иконкам; для декоративных оставить `null` осознанно, а не по умолчанию — список декоративных привести в отчёте.
Прогнать экран чата и список хостов через Accessibility Scanner, результат приложить.
**6. Тема (`UI-09`).**
Перейти на `Theme.Material3.DayNight.NoActionBar`, системные полосы прозрачные, добавить `values-night`. Проверить стартовое окно в тёмном режиме.
**7. Настройки (`UI-10`).**
Либо добавить вход в настройки и наполнить экран реальными переключателями (доступ к списку хостов, выход из аккаунта хоста, диагностика соединения, версия), либо удалить маршрут и экран. Выбор обосновать.
Утверждение про «одноразовые тикеты с TTL 30 с» либо подтвердить фактическим поведением, либо убрать: клиент не должен заявлять свойств, которых не проверяет.
**8. Итоговая сверка.**
Таблица «находка аудита → задание → статус» на все 63 позиции. Каждая — `закрыто` (с указанием задания и SHA), `перенесено` (куда и почему) или `UNVERIFIED` (что именно не удалось проверить). Пустых клеток быть не должно.
## Do not change
- Продуктовую логику заданий 0109: это задание чистит и упаковывает, а не правит поведение.
- Схему БД.
- Протокол сопряжения — задание 07.
- Тексты при переносе в ресурсы: перенос дословный, переформулировки отдельным заданием.
## Anti-checklist
1. `ConnectionRepository` удалён вместе с `Context.dataStore`, миграция сломана. Проверить тест миграции **после** удаления, а не до.
2. Файлы удалены, но остались ссылки в тестах — сборка тестов красная, «поправили» удалением теста.
3. `isMinifyEnabled = true` включён, сборка проходит, а на устройстве падает разбор JSON: правила R8 для сериализации неполны. Требование: проверка на устройстве, а не факт компиляции.
4. `shrinkResources` выкинул ресурсы, используемые через рефлексию. Проверить экраны на устройстве после R8.
5. Ключ подписи сгенерирован исполнителем самостоятельно.
6. `keystore.properties` попал в коммит. Проверить `git log -p` на секреты, а не только рабочее дерево.
7. Иконка добавлена, но `roundIcon` в манифесте всё ещё указывает на системную заглушку.
8. Строки вынесены механически, включая те, что собираются из подстановок, — получились битые форматные строки. Проверить экраны на устройстве.
9. `contentDescription` проставлен строкой «icon» / «button» вместо описания действия.
10. Экран настроек оставлен недостижимым, но «оформлен».
11. Итоговая таблица содержит `закрыто` для находки, по которой в отчёте задания стоит `UNVERIFIED`. Сверять с фактическими отчётами, а не по памяти.
12. В отчёте `green` для незапущенной команды (`AGENTS.md §3`).
## Definition of Done
- В дереве нет шести перечисленных файлов; ссылок на них не осталось; миграция работает.
- Мелкий мёртвый код удалён; сборка без предупреждений о неиспользуемом из изменённых файлов.
- `assembleRelease` даёт подписанный APK с включённым R8; приложение установлено на устройство, проходит сценарий: добавление хоста по QR → вход → отправка промпта → получение ответа со стримом → подтверждение команды.
- Иконка отображается в лаунчере, включая круглую и монохромную темы.
- Ни одного пользовательского текста в коде; Accessibility Scanner не показывает критичных замечаний на двух основных экранах.
- Тёмная тема применяется к системным полосам и стартовому окну.
- Экран настроек либо достижим и содержателен, либо удалён; ложных утверждений о свойствах протокола в UI нет.
- Итоговая таблица закрывает все 63 находки без пустых клеток.
- Все существующие тесты зелёные, ни один не удалён и не ослаблен.
## Required tests
`app/src/test/.../DeadReferenceTest` не имеет смысла — факт удаления проверяется сборкой. Доказательства:
1. `git ls-files` не содержит удалённых путей; `grep -r` по именам классов пуст;
2. полный прогон тестов и миграции после удаления — зелёный;
3. `assembleRelease` + установка на устройство + прохождение сценария из §DoD, с логом `adb logcat` без исключений;
4. отчёт Accessibility Scanner по двум экранам;
5. скриншоты лаунчера (обычная и круглая иконка) и стартового окна в тёмной теме.
Отдельный androidTest `ReleaseSerializationTest` — разбор и сериализация моделей после R8 на устройстве.
## Required verification
```text
./gradlew --no-daemon testDebugUnitTest
./gradlew --no-daemon connectedDebugAndroidTest
./gradlew --no-daemon lint
./gradlew --no-daemon assembleDebug
./gradlew --no-daemon assembleRelease
cd hermes-pair && cargo test --all-targets && cargo clippy -- -D warnings
```
`assembleRelease` без предоставленного владельцем ключа выполняется без подписи; это указывается явно, и пункт подписи остаётся открытым, а не помечается закрытым.
## Result
`agents/antigravity/done/TASK-2026-08-24-10-cleanup-and-release.md` — разделы `## Кодер 1`, `## Кодер 2 (review + доработка)`, `## Итоговая сверка по 63 находкам`, `## Вердикт оркестратора`, `## Вопросы владельцу` (ключ подписи, судьба экрана настроек).