# 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 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 находкам`, `## Вердикт оркестратора`, `## Вопросы владельцу` (ключ подписи, судьба экрана настроек).