Files
ds-galo/openspec/changes/phone-album-two-way-sync/tasks.md
T
iu d17f62f676 chore(openspec): предложить phone-album-two-way-sync и обновить спеку phone-album-sync
Новое изменение: двусторонний миррор альбома устройства и серверного альбома
(материализация серверных фото, признак происхождения, двусторонние удаления с
явным намерением, устойчивое выключение, первый проход, n:1). Purpose основной
спеки phone-album-sync обновлён под двустороннюю синхронизацию.

Change проходит `openspec validate --strict`.
2026-10-16 23:19:08 +03:00

7.5 KiB
Raw Blame History

Tasks

1. Модель индекса и провенанс

  • 1.1 Добавить в DeviceAssetEntity признак происхождения (OWN | MIRROR) и Room-миграцию с OWN для существующих строк; проверить миграционным тестом android/app/src/test (или instrumented), что старая база открывается без потери строк
  • 1.2 Добавить таблицу надгробий (photoId, момент, состояние) и watermark первого прохода в Room с миграцией; проверить тестом DAO на вставку/чтение/снятие надгробия
  • 1.3 Расширить ProvenanceStamp признаком происхождения; проверить юнит-тестом encode/decode и тестом SyncRecovery, что скачанный элемент восстанавливается как MIRROR, а не как OWN

2. Конвейер материализации

  • 2.1 Реализовать загрузчик объекта по подписанному URL (original_url для B, preview_url/medium_url для C, постер для видео); проверить юнит-тестом на выбор URL и обработку ошибок сети
  • 2.2 Реализовать запись файла в бакет альбома устройства (уникальное имя, DATE_TAKEN из taken_at, метка происхождения); проверить instrumented-тестом, что файл появляется в MediaStore нужного бакета с датой
  • 2.3 Реализовать дедуп перед вставкой: если содержимое уже на устройстве — связать элемент, не создавая файл; проверить тестом планировщика, что второго файла нет
  • 2.4 Реализовать правило n:1 — материализация ровно в один выбранный бакет; проверить тестом, что при двух связанных бакетах файл создаётся в одном

3. Двусторонний планировщик

  • 3.1 Реализовать дельту серверных фото (?since=/курсор) с учётом watermark и ограничения «только Wi-Fi»; проверить тестом, что при «только новое» существующий состав не скачивается, а новое — скачивается
  • 3.2 Реализовать планирование материализации по режиму (B — оригинал, C — превью); проверить тестами SyncPlanner: режим копий → оригинал, режим переноса → превью
  • 3.3 Реализовать двустороннее удаление с учётом прав и надгробий (локальное → в корзину сервера, серверное → снятие локального, 403 не роняет проход); проверить тестами: идемпотентность, эхо, отказ прав
  • 3.4 Реализовать рематериализацию при восстановлении серверного фото из корзины; проверить тестом, что элемент возвращается в альбом устройства
  • 3.5 Реализовать отвязку и выключение синхронизации так, чтобы серверные фото и скачанные файлы сохранялись; проверить тестом, что после отвязки файлы остаются и сервер не затронут

4. Интерфейс

  • 4.1 Включить скачанные элементы в сетку альбома устройства и обновлять её по завершении прохода; проверить UI-тестом, что новое серверное фото появляется, а удалённое исчезает без ручного действия
  • 4.2 Добавить при включении синхронизации выбор «только новое» / «скачать всё» с сохранением выбора; проверить UI-тестом, что выбор влияет на первый проход

5. Интеграция и документация

  • 5.1 Проверить сквозной сценарий: фото, добавленное в веб-интерфейсе, материализуется на устройстве; удаление с устройства убирает его на сервере; удаление на сервере убирает локальный элемент; проверить e2e-тестом на эмуляторе
  • 5.2 Обновить Purpose спеки phone-album-sync (двусторонний миррор вместо односторонней синхронизации) и убедиться, что openspec validate phone-album-two-way-sync --strict проходит

6. Устойчивость выключения синхронизации

  • 6.1 Записать явный признак «синхронизация выключена» при отвязке/выключении и блокировать им восстановление происхождения; повторное включение снимает признак; проверить unit-тестами SyncRepositoryTest и SyncRecoveryTest
  • 6.2 Добавить instrumented-тест всех режимов и переключений (A/B/C, B→C, C→B, C→A, B→A, A→B, восстановление) и прогнать на эмуляторе
  • 6.3 Вынести объёмную нагрузку (100 фото + 20 видео на тест) в отдельный SyncVolumeE2ETest, а функциональные тесты держать малыми; проверить на эмуляторе
  • 6.4 Очередь загрузки: одно дренирующее WorkManager-задание вместо задания на каждый файл (иначе JobScheduler перестаёт запускать оставшиеся задания); проверить заливкой 120 файлов на эмуляторе
  • 6.5 Производительность: дебаунс DeviceMediaObserver и кэш sha256 по (size, dateModified); проверить unit-набором и малыми наборами
  • 6.6 Корректность: неудачное чтение серверного списка не считается пустым альбомом (иначе удалялись локальные зеркала); проверить unit-тестом a failed album read does not delete local mirrors
  • 6.7 Удаление зеркал: намерением считается только явное удаление в приложении (надгробие → удаление на сервере); пропавшая локальная копия материализуется заново; проверить unit-тестами и instrumented-тестом explicitDeleteOfAMirrorRemovesTheServerPhoto

Workflow follow-up

  • Archive the change after the project's review requirements are satisfied.