Files
ds-galo/openspec/changes/archive/2026-10-16-phone-album-two-way-sync/tasks.md
T
iu cc85d792ea chore(openspec): архивировать phone-album-two-way-sync и синхронизировать спеки
Дельта-спеки перенесены в основные: phone-album-sync (+8 требований, 6 изменено,
1 удалено — «Удаление только собственных загрузок»), android-client.

Change перемещён в openspec/changes/archive/2026-10-16-phone-album-two-way-sync.
2026-10-16 23:20:04 +03:00

47 lines
7.5 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.
# Tasks
## 1. Модель индекса и провенанс
- [x] 1.1 Добавить в `DeviceAssetEntity` признак происхождения (`OWN` | `MIRROR`) и Room-миграцию с `OWN` для существующих строк; проверить миграционным тестом `android/app/src/test` (или instrumented), что старая база открывается без потери строк
- [x] 1.2 Добавить таблицу надгробий (photoId, момент, состояние) и watermark первого прохода в Room с миграцией; проверить тестом DAO на вставку/чтение/снятие надгробия
- [x] 1.3 Расширить `ProvenanceStamp` признаком происхождения; проверить юнит-тестом encode/decode и тестом `SyncRecovery`, что скачанный элемент восстанавливается как `MIRROR`, а не как `OWN`
## 2. Конвейер материализации
- [x] 2.1 Реализовать загрузчик объекта по подписанному URL (`original_url` для B, `preview_url`/`medium_url` для C, постер для видео); проверить юнит-тестом на выбор URL и обработку ошибок сети
- [x] 2.2 Реализовать запись файла в бакет альбома устройства (уникальное имя, `DATE_TAKEN` из `taken_at`, метка происхождения); проверить instrumented-тестом, что файл появляется в MediaStore нужного бакета с датой
- [x] 2.3 Реализовать дедуп перед вставкой: если содержимое уже на устройстве — связать элемент, не создавая файл; проверить тестом планировщика, что второго файла нет
- [x] 2.4 Реализовать правило n:1 — материализация ровно в один выбранный бакет; проверить тестом, что при двух связанных бакетах файл создаётся в одном
## 3. Двусторонний планировщик
- [x] 3.1 Реализовать дельту серверных фото (`?since=`/курсор) с учётом watermark и ограничения «только Wi-Fi»; проверить тестом, что при «только новое» существующий состав не скачивается, а новое — скачивается
- [x] 3.2 Реализовать планирование материализации по режиму (B — оригинал, C — превью); проверить тестами `SyncPlanner`: режим копий → оригинал, режим переноса → превью
- [x] 3.3 Реализовать двустороннее удаление с учётом прав и надгробий (локальное → в корзину сервера, серверное → снятие локального, 403 не роняет проход); проверить тестами: идемпотентность, эхо, отказ прав
- [x] 3.4 Реализовать рематериализацию при восстановлении серверного фото из корзины; проверить тестом, что элемент возвращается в альбом устройства
- [x] 3.5 Реализовать отвязку и выключение синхронизации так, чтобы серверные фото и скачанные файлы сохранялись; проверить тестом, что после отвязки файлы остаются и сервер не затронут
## 4. Интерфейс
- [x] 4.1 Включить скачанные элементы в сетку альбома устройства и обновлять её по завершении прохода; проверить UI-тестом, что новое серверное фото появляется, а удалённое исчезает без ручного действия
- [x] 4.2 Добавить при включении синхронизации выбор «только новое» / «скачать всё» с сохранением выбора; проверить UI-тестом, что выбор влияет на первый проход
## 5. Интеграция и документация
- [x] 5.1 Проверить сквозной сценарий: фото, добавленное в веб-интерфейсе, материализуется на устройстве; удаление с устройства убирает его на сервере; удаление на сервере убирает локальный элемент; проверить e2e-тестом на эмуляторе
- [x] 5.2 Обновить Purpose спеки `phone-album-sync` (двусторонний миррор вместо односторонней синхронизации) и убедиться, что `openspec validate phone-album-two-way-sync --strict` проходит
## 6. Устойчивость выключения синхронизации
- [x] 6.1 Записать явный признак «синхронизация выключена» при отвязке/выключении и блокировать им восстановление происхождения; повторное включение снимает признак; проверить unit-тестами `SyncRepositoryTest` и `SyncRecoveryTest`
- [x] 6.2 Добавить instrumented-тест всех режимов и переключений (A/B/C, B→C, C→B, C→A, B→A, A→B, восстановление) и прогнать на эмуляторе
- [x] 6.3 Вынести объёмную нагрузку (100 фото + 20 видео на тест) в отдельный `SyncVolumeE2ETest`, а функциональные тесты держать малыми; проверить на эмуляторе
- [x] 6.4 Очередь загрузки: одно дренирующее WorkManager-задание вместо задания на каждый файл (иначе JobScheduler перестаёт запускать оставшиеся задания); проверить заливкой 120 файлов на эмуляторе
- [x] 6.5 Производительность: дебаунс `DeviceMediaObserver` и кэш sha256 по `(size, dateModified)`; проверить unit-набором и малыми наборами
- [x] 6.6 Корректность: неудачное чтение серверного списка не считается пустым альбомом (иначе удалялись локальные зеркала); проверить unit-тестом `a failed album read does not delete local mirrors`
- [x] 6.7 Удаление зеркал: намерением считается только явное удаление в приложении (надгробие → удаление на сервере); пропавшая локальная копия материализуется заново; проверить unit-тестами и instrumented-тестом `explicitDeleteOfAMirrorRemovesTheServerPhoto`
## Workflow follow-up
- Archive the change after the project's review requirements are satisfied.