16 KiB
Task 10: Чистка мёртвого слоя и подготовка релиза (hermes-android)
Repo: ochenstarik-ui/hermes-android
Assigned to: Antigravity (режим оркестратора, два кодера)
Priority: HIGH (без этого приложение нельзя опубликовать)
Date: 2026-08-24
Base SHA: результат задания 09 — указать фактический SHA при выдаче
Зависимость: задания 01–09 приняты. Это последнее задание цикла: оно закрывает остаток находок аудита и приводит проект к состоянию, пригодному для выпуска.
Роли и протокол
| Роль | Модель | Что делает |
|---|---|---|
| Оркестратор | 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
- Продуктовую логику заданий 01–09: это задание чистит и упаковывает, а не правит поведение.
- Схему БД.
- Протокол сопряжения — задание 07.
- Тексты при переносе в ресурсы: перенос дословный, переформулировки отдельным заданием.
Anti-checklist
ConnectionRepositoryудалён вместе сContext.dataStore, миграция сломана. Проверить тест миграции после удаления, а не до.- Файлы удалены, но остались ссылки в тестах — сборка тестов красная, «поправили» удалением теста.
isMinifyEnabled = trueвключён, сборка проходит, а на устройстве падает разбор JSON: правила R8 для сериализации неполны. Требование: проверка на устройстве, а не факт компиляции.shrinkResourcesвыкинул ресурсы, используемые через рефлексию. Проверить экраны на устройстве после R8.- Ключ подписи сгенерирован исполнителем самостоятельно.
keystore.propertiesпопал в коммит. Проверитьgit log -pна секреты, а не только рабочее дерево.- Иконка добавлена, но
roundIconв манифесте всё ещё указывает на системную заглушку. - Строки вынесены механически, включая те, что собираются из подстановок, — получились битые форматные строки. Проверить экраны на устройстве.
contentDescriptionпроставлен строкой «icon» / «button» вместо описания действия.- Экран настроек оставлен недостижимым, но «оформлен».
- Итоговая таблица содержит
закрытодля находки, по которой в отчёте задания стоитUNVERIFIED. Сверять с фактическими отчётами, а не по памяти. - В отчёте
greenдля незапущенной команды (AGENTS.md §3).
Definition of Done
- В дереве нет шести перечисленных файлов; ссылок на них не осталось; миграция работает.
- Мелкий мёртвый код удалён; сборка без предупреждений о неиспользуемом из изменённых файлов.
assembleReleaseдаёт подписанный APK с включённым R8; приложение установлено на устройство, проходит сценарий: добавление хоста по QR → вход → отправка промпта → получение ответа со стримом → подтверждение команды.- Иконка отображается в лаунчере, включая круглую и монохромную темы.
- Ни одного пользовательского текста в коде; Accessibility Scanner не показывает критичных замечаний на двух основных экранах.
- Тёмная тема применяется к системным полосам и стартовому окну.
- Экран настроек либо достижим и содержателен, либо удалён; ложных утверждений о свойствах протокола в UI нет.
- Итоговая таблица закрывает все 63 находки без пустых клеток.
- Все существующие тесты зелёные, ни один не удалён и не ослаблен.
Required tests
app/src/test/.../DeadReferenceTest не имеет смысла — факт удаления проверяется сборкой. Доказательства:
git ls-filesне содержит удалённых путей;grep -rпо именам классов пуст;- полный прогон тестов и миграции после удаления — зелёный;
assembleRelease+ установка на устройство + прохождение сценария из §DoD, с логомadb logcatбез исключений;- отчёт Accessibility Scanner по двум экранам;
- скриншоты лаунчера (обычная и круглая иконка) и стартового окна в тёмной теме.
Отдельный androidTest ReleaseSerializationTest — разбор и сериализация моделей после R8 на устройстве.
Required verification
./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 находкам, ## Вердикт оркестратора, ## Вопросы владельцу (ключ подписи, судьба экрана настроек).