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

16 KiB
Raw Permalink Blame History

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-30isMinifyEnabled = false, signingConfig отсутствует; app/proguard-rules.pro содержит одну строку -keepattributes. Собрать подписанный релиз командой из README нельзя, R8 не работает, размер APK не оптимизируется.

4. Приложение без иконки (UI-05). AndroidManifest.xml:12-14android: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

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