From 3a44844ff918d910138fd1ec6a59057c32d9bb2e Mon Sep 17 00:00:00 2001 From: Iu Date: Tue, 20 Oct 2026 20:35:52 +0300 Subject: [PATCH] =?UTF-8?q?chore(openspec):=20=D0=B0=D1=80=D1=85=D0=B8?= =?UTF-8?q?=D0=B2=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D1=82=D1=8C=20fix-album-sy?= =?UTF-8?q?nc-churn-and-degradation=20=D0=B8=20=D1=81=D0=B8=D0=BD=D1=85?= =?UTF-8?q?=D1=80=D0=BE=D0=BD=D0=B8=D0=B7=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20=D1=81=D0=BF=D0=B5=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit В спеку phone-album-sync добавлены требования о роли локального файла, подтверждении новой версии до удаления устаревшей и разгрузке без незавершённой загрузки; уточнены синхронизация изменений, провенанс и возврат оригиналов. --- .../.openspec.yaml | 0 .../design.md | 0 .../proposal.md | 0 .../specs/phone-album-sync/spec.md | 0 .../tasks.md | 0 openspec/specs/phone-album-sync/spec.md | 93 ++++++++++++++++++- 6 files changed, 89 insertions(+), 4 deletions(-) rename openspec/changes/{fix-album-sync-churn-and-degradation => archive/2026-10-20-fix-album-sync-churn-and-degradation}/.openspec.yaml (100%) rename openspec/changes/{fix-album-sync-churn-and-degradation => archive/2026-10-20-fix-album-sync-churn-and-degradation}/design.md (100%) rename openspec/changes/{fix-album-sync-churn-and-degradation => archive/2026-10-20-fix-album-sync-churn-and-degradation}/proposal.md (100%) rename openspec/changes/{fix-album-sync-churn-and-degradation => archive/2026-10-20-fix-album-sync-churn-and-degradation}/specs/phone-album-sync/spec.md (100%) rename openspec/changes/{fix-album-sync-churn-and-degradation => archive/2026-10-20-fix-album-sync-churn-and-degradation}/tasks.md (100%) diff --git a/openspec/changes/fix-album-sync-churn-and-degradation/.openspec.yaml b/openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/.openspec.yaml similarity index 100% rename from openspec/changes/fix-album-sync-churn-and-degradation/.openspec.yaml rename to openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/.openspec.yaml diff --git a/openspec/changes/fix-album-sync-churn-and-degradation/design.md b/openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/design.md similarity index 100% rename from openspec/changes/fix-album-sync-churn-and-degradation/design.md rename to openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/design.md diff --git a/openspec/changes/fix-album-sync-churn-and-degradation/proposal.md b/openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/proposal.md similarity index 100% rename from openspec/changes/fix-album-sync-churn-and-degradation/proposal.md rename to openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/proposal.md diff --git a/openspec/changes/fix-album-sync-churn-and-degradation/specs/phone-album-sync/spec.md b/openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/specs/phone-album-sync/spec.md similarity index 100% rename from openspec/changes/fix-album-sync-churn-and-degradation/specs/phone-album-sync/spec.md rename to openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/specs/phone-album-sync/spec.md diff --git a/openspec/changes/fix-album-sync-churn-and-degradation/tasks.md b/openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/tasks.md similarity index 100% rename from openspec/changes/fix-album-sync-churn-and-degradation/tasks.md rename to openspec/changes/archive/2026-10-20-fix-album-sync-churn-and-degradation/tasks.md diff --git a/openspec/specs/phone-album-sync/spec.md b/openspec/specs/phone-album-sync/spec.md index ebd6fd0..8ee204b 100644 --- a/openspec/specs/phone-album-sync/spec.md +++ b/openspec/specs/phone-album-sync/spec.md @@ -74,8 +74,9 @@ Для каждого отслеженного изменения приложение SHALL применить его к целевому серверному альбому: загрузить новое содержимое, переименовать и перенести через -API, а изменение содержимого SHALL трактовать как новую загрузку с удалением -устаревшей собственной загрузки. +API. Изменение содержимого SHALL трактоваться как новая загрузка, а устаревшая +собственная загрузка SHALL удаляться после подтверждения новой версии и SHALL NOT +удаляться из-за смены локальной роли файла (оригинал ↔ превью). #### Scenario: Новое содержимое @@ -90,7 +91,36 @@ API, а изменение содержимого SHALL трактовать к #### Scenario: Изменение содержимого - **WHEN** содержимое медиа изменено на устройстве -- **THEN** приложение загружает новую версию, а устаревшую собственную загрузку удаляет +- **THEN** приложение загружает новую версию и после её подтверждения удаляет устаревшую собственную загрузку + +### Requirement: Подтверждение новой версии до удаления устаревшей + +Устаревшая собственная загрузка элемента SHALL удаляться только после подтверждения новой +версии: сообщённая сервером контрольная сумма принятого объекта SHALL совпадать с +отправленной. При расхождении приложение SHALL отклонить загрузку, удалив принятую копию, и +SHALL NOT считать элемент загруженным. Если сервер контрольную сумму не сообщил, загрузка +SHALL считаться состоявшейся, но устаревшая копия SHALL NOT удаляться, а замена SHALL +повториться при следующей синхронизации. + +#### Scenario: Новая версия подтверждена + +- **WHEN** новая версия загружена, а сообщённая сервером контрольная сумма совпала с отправленной +- **THEN** приложение удаляет устаревшую собственную загрузку (мягко, в корзину) + +#### Scenario: Контрольная сумма не совпала + +- **WHEN** сервер сообщил для принятого объекта контрольную сумму, отличную от отправленной +- **THEN** приложение удаляет принятую копию, не считает элемент загруженным и оставляет прежнюю серверную копию в альбоме + +#### Scenario: Контрольная сумма не сообщена + +- **WHEN** сервер не сообщил контрольную сумму принятого объекта +- **THEN** загрузка считается состоявшейся, прежняя серверная копия остаётся в альбоме, а замена повторяется при следующей синхронизации + +#### Scenario: Загрузка не удалась + +- **WHEN** загрузка новой версии завершилась ошибкой +- **THEN** прежняя серверная копия остаётся в альбоме ### Requirement: Идентичность по контрольной сумме @@ -154,12 +184,29 @@ API, а изменение содержимого SHALL трактовать к - **WHEN** пользователь включает режим переноса - **THEN** приложение запрашивает широкий доступ к файлам, и разгрузка выполняется без системного диалога согласия +### Requirement: Разгрузка без незавершённой загрузки + +Приложение SHALL NOT заменять оригинал превью, пока для этого элемента есть незавершённая +загрузка (в очереди или выполняющаяся), чтобы загрузка не отправила превью вместо оригинала. + +#### Scenario: Загрузка в очереди + +- **WHEN** для элемента есть задание загрузки в очереди или выполняющееся +- **THEN** оригинал на устройстве не заменяется превью + +#### Scenario: Загрузка подтверждена + +- **WHEN** загрузка элемента подтверждена сервером +- **THEN** приложение выполняет обычную разгрузку в режиме переноса + ### Requirement: Провенанс разгруженных превью Разгруженные превью, постеры видео и скачанные с сервера элементы SHALL содержать метаданные провенанса, включая признак происхождения (загружено клиентом или скачано с сервера), достаточные для восстановления связи с серверным альбомом и принадлежности -загрузок после переустановки приложения. +загрузок после переустановки приложения. Метка SHALL также служить признаком того, что +локальный файл является созданным приложением превью: элемент с такой меткой SHALL NOT +считаться содержащим оригинал, даже если состояние локального индекса утверждает обратное. #### Scenario: Восстановление после переустановки @@ -176,6 +223,39 @@ API, а изменение содержимого SHALL трактовать к - **WHEN** метаданные провенанса недоступны (например, файл перезаписан сторонним редактором) - **THEN** элемент считается неопознанным, не удаляется и не загружается повторно +#### Scenario: Метка против состояния индекса + +- **WHEN** файл несёт метку созданного приложением превью, а состояние индекса указывает оригинал +- **THEN** элемент считается разгруженным, а не новым оригиналом + +### Requirement: Роль локального файла не является содержимым + +Приложение SHALL отличать превью, созданное им самим при разгрузке (в том числе по метке +провенанса), от новой версии оригинала: разгруженный элемент SHALL NOT загружаться повторно +и SHALL NOT вызывать удаление серверной копии, а расхождение состояния в локальном индексе +SHALL выправляться. Это SHALL относиться и к постеру, которым в режиме переноса заменяется +видео. + +#### Scenario: Разгруженный элемент не создаёт новую версию + +- **WHEN** серверная копия элемента есть в целевом альбоме, а на устройстве лежит созданное приложением превью, в том числе если состояние локального индекса ошибочно указывает оригинал +- **THEN** приложение не ставит загрузку и не удаляет серверную копию + +#### Scenario: Состояние выправляется + +- **WHEN** файл на устройстве является созданным приложением превью, а индекс считает его оригиналом +- **THEN** приложение помечает элемент как разгруженный и последующие проходы не создают загрузок + +#### Scenario: Постер разгруженного видео + +- **WHEN** видео разгружено (заменено постером под новым элементом MediaStore), а его метка указывает на исходное видео +- **THEN** приложение связывает элемент с серверным фото видео и не удаляет это фото на сервере, а постер не загружается как новое фото + +#### Scenario: Настоящее изменение содержимого + +- **WHEN** содержимое медиа заменено содержимым, не созданным приложением при разгрузке +- **THEN** приложение загружает новую версию элемента + ### Requirement: Возврат оригиналов и отвязка При выходе из режима C приложение SHALL предложить вернуть оригиналы на устройство @@ -215,6 +295,11 @@ API, а изменение содержимого SHALL трактовать к - **WHEN** пользователь снова включает синхронизацию после выключения - **THEN** связь создаётся заново, а восстановление происхождения снова действует по обычным правилам +#### Scenario: Возврат устойчив + +- **WHEN** оригиналы возвращены на устройство после выхода из режима переноса +- **THEN** приложение не разгружает их повторно, пока альбом не переведён в режим переноса снова + ### Requirement: Автоматическая фоновая синхронизация Синхронизация SHALL выполняться автоматически в обе стороны: при открытом приложении —