# Proposal ## Why Реальный инцидент: альбом устройства U2 синхронизировался в режиме переноса в серверный альбом A (фото загружены, локальные оригиналы заменены превью), затем альбом A удалили на сервере, а альбом устройства перепривязали к новому серверному альбому U2. В U2 уехало всего 1 фото: остальные на телефоне уже были превью, и загрузить их заново было нечем. Оригиналы остались только в корзине сервера, а приложение молча оставило телефон с превью. Три причины: - решение о разгрузке принимается по `serverPhotoId` в локальном индексе, без проверки, что это фото вообще есть в **текущем** целевом альбоме; - смена целевого альбома только перезаписывает связь: уже загруженные фото никто не переносит на сервере, а из превью повторно не загрузить; - потеря серверной копии — тихий тупик: `ORPHANED` виден, но нигде не сказано, что оригинал утрачен, и нет явного предупреждения. ## What Changes - **Доверие только к живой копии.** Записанный `serverPhotoId` считается действительным, только если это фото присутствует в целевом альбоме. Если копии нет: при локальном оригинале ассет загружается заново, при одном превью элемент помечается потерявшим копию. - **Разгрузка только под защитой.** Замена оригинала превью выполняется, только если копия подтверждена в целевом альбоме; разгрузка «по памяти индекса» запрещена. Автоматическая разгрузка в режиме переноса сохраняется. - **Смена цели переносит копии.** При смене целевого альбома приложение сначала переносит на сервере уже загруженные этим альбомом устройства фото в новый альбом и переключает связь только если перенос удался; иначе связь не меняется и пользователь получает отказ. - **Потеря копии видна.** Экран альбома устройства и карточка альбома в списке показывают, сколько фото осталось без серверной копии; никакого автоматического удаления превью. - **BREAKING**: нет. Меняются только защитные условия; успешные сценарии (загрузка, разгрузка, повторная синхронизация) сохраняют поведение. ## Capabilities ### New Capabilities _Нет — поведение относится к существующим способностям._ ### Modified Capabilities - `phone-album-sync`: разгрузка требует живой копии в текущем целевом альбоме; записанный `serverPhotoId` перепроверяется по целевому альбому; смена целевого альбома переносит уже загруженные фото и отклоняется, если перенести нельзя. - `android-client`: потеря серверной копии показывается на экране альбома устройства и на карточке в списке; автоматического удаления превью нет. ## Impact - **Код (Android):** `data/sync/SyncPlan` (доверие к живой копии), `data/sync/SyncCoordinator` (защищённая разгрузка, перенос при смене цели), `data/sync/OffloadManager` (условие разгрузки), `data/upload/UploadWorker` (передача живой копии после загрузки), `data/db/dao/SyncDao` и `data/repo/SyncRepository` (счёт потерянных копий), `ui/devicealbum/DeviceAlbumViewModel` (смена цели, разгрузка, предупреждение), `ui/devicealbum/DeviceAlbumScreen` (баннер), `domain/sync/AlbumSyncProgress` и `ui/albums/*` (сигнал на карточке). - **Схема Room:** без изменений; добавляются только запросы. - **API и бэкенд:** не затрагиваются (используется существующий `/api/photo-batch/move`). - **Веб-клиент:** не затрагивается. - **Не затрагивается:** ручные загрузки в серверный альбом; восстановление уже потерянных копий из корзины сервера (по решению пользователя это отдельная задача).