Files
iu 3a44844ff9 chore(openspec): архивировать fix-album-sync-churn-and-degradation и синхронизировать спеки
В спеку phone-album-sync добавлены требования о роли локального файла,
подтверждении новой версии до удаления устаревшей и разгрузке без
незавершённой загрузки; уточнены синхронизация изменений, провенанс и
возврат оригиналов.
2026-10-20 20:35:52 +03:00

174 lines
13 KiB
Markdown

# Spec Delta
## ADDED Requirements
### Requirement: Роль локального файла не является содержимым
Приложение SHALL отличать превью, созданное им самим при разгрузке (в том числе по метке
провенанса), от новой версии оригинала: разгруженный элемент SHALL NOT загружаться повторно
и SHALL NOT вызывать удаление серверной копии, а расхождение состояния в локальном индексе
SHALL выправляться. Это SHALL относиться и к постеру, которым в режиме переноса заменяется
видео.
#### Scenario: Разгруженный элемент не создаёт новую версию
- **WHEN** серверная копия элемента есть в целевом альбоме, а на устройстве лежит созданное приложением превью, в том числе если состояние локального индекса ошибочно указывает оригинал
- **THEN** приложение не ставит загрузку и не удаляет серверную копию
#### Scenario: Состояние выправляется
- **WHEN** файл на устройстве является созданным приложением превью, а индекс считает его оригиналом
- **THEN** приложение помечает элемент как разгруженный и последующие проходы не создают загрузок
#### Scenario: Постер разгруженного видео
- **WHEN** видео разгружено (заменено постером под новым элементом MediaStore), а его метка указывает на исходное видео
- **THEN** приложение связывает элемент с серверным фото видео и не удаляет это фото на сервере, а постер не загружается как новое фото
#### Scenario: Настоящее изменение содержимого
- **WHEN** содержимое медиа заменено содержимым, не созданным приложением при разгрузке
- **THEN** приложение загружает новую версию элемента
### Requirement: Подтверждение новой версии до удаления устаревшей
Устаревшая собственная загрузка элемента SHALL удаляться только после подтверждения новой
версии: сообщённая сервером контрольная сумма принятого объекта SHALL совпадать с
отправленной. При расхождении приложение SHALL отклонить загрузку, удалив принятую копию, и
SHALL NOT считать элемент загруженным. Если сервер контрольную сумму не сообщил, загрузка
SHALL считаться состоявшейся, но устаревшая копия SHALL NOT удаляться, а замена SHALL
повториться при следующей синхронизации.
#### Scenario: Новая версия подтверждена
- **WHEN** новая версия загружена, а сообщённая сервером контрольная сумма совпала с отправленной
- **THEN** приложение удаляет устаревшую собственную загрузку (мягко, в корзину)
#### Scenario: Контрольная сумма не совпала
- **WHEN** сервер сообщил для принятого объекта контрольную сумму, отличную от отправленной
- **THEN** приложение удаляет принятую копию, не считает элемент загруженным и оставляет прежнюю серверную копию в альбоме
#### Scenario: Контрольная сумма не сообщена
- **WHEN** сервер не сообщил контрольную сумму принятого объекта
- **THEN** загрузка считается состоявшейся, прежняя серверная копия остаётся в альбоме, а замена повторяется при следующей синхронизации
#### Scenario: Загрузка не удалась
- **WHEN** загрузка новой версии завершилась ошибкой
- **THEN** прежняя серверная копия остаётся в альбоме
### Requirement: Разгрузка без незавершённой загрузки
Приложение SHALL NOT заменять оригинал превью, пока для этого элемента есть незавершённая
загрузка (в очереди или выполняющаяся), чтобы загрузка не отправила превью вместо оригинала.
#### Scenario: Загрузка в очереди
- **WHEN** для элемента есть задание загрузки в очереди или выполняющееся
- **THEN** оригинал на устройстве не заменяется превью
#### Scenario: Загрузка подтверждена
- **WHEN** загрузка элемента подтверждена сервером
- **THEN** приложение выполняет обычную разгрузку в режиме переноса
## MODIFIED Requirements
### Requirement: Синхронизация изменений с сервером
Для каждого отслеженного изменения приложение SHALL применить его к целевому
серверному альбому: загрузить новое содержимое, переименовать и перенести через
API. Изменение содержимого SHALL трактоваться как новая загрузка, а устаревшая
собственная загрузка SHALL удаляться после подтверждения новой версии и SHALL NOT
удаляться из-за смены локальной роли файла (оригинал ↔ превью).
#### Scenario: Новое содержимое
- **WHEN** в альбоме устройства появилось медиа, которого нет на сервере
- **THEN** приложение загружает его в целевой серверный альбом
#### Scenario: Переименование доходит до сервера
- **WHEN** медиа переименовано на устройстве и уже синхронизировано
- **THEN** приложение переименовывает соответствующее серверное фото
#### Scenario: Изменение содержимого
- **WHEN** содержимое медиа изменено на устройстве
- **THEN** приложение загружает новую версию и после её подтверждения удаляет устаревшую собственную загрузку
### Requirement: Провенанс разгруженных превью
Разгруженные превью, постеры видео и скачанные с сервера элементы SHALL содержать
метаданные провенанса, включая признак происхождения (загружено клиентом или скачано с
сервера), достаточные для восстановления связи с серверным альбомом и принадлежности
загрузок после переустановки приложения. Метка SHALL также служить признаком того, что
локальный файл является созданным приложением превью: элемент с такой меткой SHALL NOT
считаться содержащим оригинал, даже если состояние локального индекса утверждает обратное.
#### Scenario: Восстановление после переустановки
- **WHEN** приложение переустановлено или его данные очищены, а альбом устройства содержит разгруженные превью
- **THEN** приложение восстанавливает связь с серверным альбомом и принадлежность своих загрузок из метаданных превью
#### Scenario: Метка скачанного элемента
- **WHEN** метка провенанса восстанавливается у элемента, скачанного с сервера
- **THEN** элемент восстанавливается как скачанный, а не как собственная загрузка клиента
#### Scenario: Метка утрачена
- **WHEN** метаданные провенанса недоступны (например, файл перезаписан сторонним редактором)
- **THEN** элемент считается неопознанным, не удаляется и не загружается повторно
#### Scenario: Метка против состояния индекса
- **WHEN** файл несёт метку созданного приложением превью, а состояние индекса указывает оригинал
- **THEN** элемент считается разгруженным, а не новым оригиналом
### Requirement: Возврат оригиналов и отвязка
При выходе из режима C приложение SHALL предложить вернуть оригиналы на устройство
и SHALL предупредить, если пользователь отказывается. Отвязка альбома и выключение
синхронизации SHALL NOT удалять фото с сервера, а скачанные с сервера файлы SHALL
оставаться в галерее как обычные. Выключение синхронизации SHALL быть явным и
устойчивым: восстановление происхождения SHALL NOT возобновлять связь, пока
пользователь не включит синхронизацию снова.
#### Scenario: Возврат оригиналов
- **WHEN** пользователь выходит из режима C и соглашается вернуть оригиналы
- **THEN** приложение возвращает оригиналы на устройство вместо превью
#### Scenario: Отказ от возврата
- **WHEN** пользователь выходит из режима C и отказывается возвращать оригиналы
- **THEN** приложение предупреждает, что на устройстве останутся только превью
#### Scenario: Отвязка
- **WHEN** пользователь отвязывает альбом устройства от серверного
- **THEN** фото на сервере сохраняются, а скачанные файлы остаются в галерее как обычные
#### Scenario: Выключение синхронизации
- **WHEN** пользователь выключает синхронизацию альбома устройства
- **THEN** связь с серверным альбомом снимается, а фото на сервере сохраняются
#### Scenario: Выключение не отменяется восстановлением
- **WHEN** пользователь выключил синхронизацию (или отвязал альбом), и затем выполняется проверка с восстановлением происхождения по меткам превью
- **THEN** связь не восстанавливается, а альбом остаётся локальным с выбранным режимом выключено
#### Scenario: Повторное включение
- **WHEN** пользователь снова включает синхронизацию после выключения
- **THEN** связь создаётся заново, а восстановление происхождения снова действует по обычным правилам
#### Scenario: Возврат устойчив
- **WHEN** оригиналы возвращены на устройство после выхода из режима переноса
- **THEN** приложение не разгружает их повторно, пока альбом не переведён в режим переноса снова