diff --git a/openspec/changes/fix-album-cover/.openspec.yaml b/openspec/changes/archive/2026-10-11-fix-album-cover/.openspec.yaml similarity index 100% rename from openspec/changes/fix-album-cover/.openspec.yaml rename to openspec/changes/archive/2026-10-11-fix-album-cover/.openspec.yaml diff --git a/openspec/changes/fix-album-cover/design.md b/openspec/changes/archive/2026-10-11-fix-album-cover/design.md similarity index 100% rename from openspec/changes/fix-album-cover/design.md rename to openspec/changes/archive/2026-10-11-fix-album-cover/design.md diff --git a/openspec/changes/fix-album-cover/proposal.md b/openspec/changes/archive/2026-10-11-fix-album-cover/proposal.md similarity index 100% rename from openspec/changes/fix-album-cover/proposal.md rename to openspec/changes/archive/2026-10-11-fix-album-cover/proposal.md diff --git a/openspec/changes/fix-album-cover/specs/albums/spec.md b/openspec/changes/archive/2026-10-11-fix-album-cover/specs/albums/spec.md similarity index 100% rename from openspec/changes/fix-album-cover/specs/albums/spec.md rename to openspec/changes/archive/2026-10-11-fix-album-cover/specs/albums/spec.md diff --git a/openspec/changes/fix-album-cover/specs/photos/spec.md b/openspec/changes/archive/2026-10-11-fix-album-cover/specs/photos/spec.md similarity index 100% rename from openspec/changes/fix-album-cover/specs/photos/spec.md rename to openspec/changes/archive/2026-10-11-fix-album-cover/specs/photos/spec.md diff --git a/openspec/changes/fix-album-cover/tasks.md b/openspec/changes/archive/2026-10-11-fix-album-cover/tasks.md similarity index 100% rename from openspec/changes/fix-album-cover/tasks.md rename to openspec/changes/archive/2026-10-11-fix-album-cover/tasks.md diff --git a/openspec/changes/fix-large-album-load/.openspec.yaml b/openspec/changes/archive/2026-10-11-fix-large-album-load/.openspec.yaml similarity index 100% rename from openspec/changes/fix-large-album-load/.openspec.yaml rename to openspec/changes/archive/2026-10-11-fix-large-album-load/.openspec.yaml diff --git a/openspec/changes/fix-large-album-load/design.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/design.md similarity index 100% rename from openspec/changes/fix-large-album-load/design.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/design.md diff --git a/openspec/changes/fix-large-album-load/proposal.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/proposal.md similarity index 100% rename from openspec/changes/fix-large-album-load/proposal.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/proposal.md diff --git a/openspec/changes/fix-large-album-load/specs/media-objects/spec.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/specs/media-objects/spec.md similarity index 100% rename from openspec/changes/fix-large-album-load/specs/media-objects/spec.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/specs/media-objects/spec.md diff --git a/openspec/changes/fix-large-album-load/specs/photos/spec.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/specs/photos/spec.md similarity index 100% rename from openspec/changes/fix-large-album-load/specs/photos/spec.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/specs/photos/spec.md diff --git a/openspec/changes/fix-large-album-load/specs/realtime/spec.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/specs/realtime/spec.md similarity index 100% rename from openspec/changes/fix-large-album-load/specs/realtime/spec.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/specs/realtime/spec.md diff --git a/openspec/changes/fix-large-album-load/tasks.md b/openspec/changes/archive/2026-10-11-fix-large-album-load/tasks.md similarity index 100% rename from openspec/changes/fix-large-album-load/tasks.md rename to openspec/changes/archive/2026-10-11-fix-large-album-load/tasks.md diff --git a/openspec/specs/albums/spec.md b/openspec/specs/albums/spec.md index 2be8109..d47ed17 100644 --- a/openspec/specs/albums/spec.md +++ b/openspec/specs/albums/spec.md @@ -54,10 +54,10 @@ - **WHEN** у альбома нет обложки или фото обложки удалено - **THEN** ответ не содержит URL обложки, и клиент показывает плейсхолдер - ### Requirement: Выбор обложки Владелец и админ SHALL иметь возможность назначить обложкой любое фото альбома или сбросить -обложку; после сброса обложка определяется заново по умолчанию. +обложку; после сброса обложка SHALL становиться не заданной (без возврата к значению по +умолчанию). #### Scenario: Назначение обложки - **WHEN** владелец выбирает фото альбома как обложку @@ -65,13 +65,12 @@ #### Scenario: Сброс обложки - **WHEN** админ сбрасывает обложку -- **THEN** система удаляет явный выбор, и обложка возвращается к значению по умолчанию +- **THEN** система снимает обложку, и альбом отображается без обложки, пока обложку не назначат снова #### Scenario: Нет прав на смену обложки - **WHEN** участник без права редактирования пытается сменить обложку - **THEN** система отклоняет запрос - ### Requirement: Счётчики альбома Данные альбома SHALL включать количество фото и участников, чтобы панель и карточки могли показать их без отдельных запросов. diff --git a/openspec/specs/media-objects/spec.md b/openspec/specs/media-objects/spec.md index f697120..f91c045 100644 --- a/openspec/specs/media-objects/spec.md +++ b/openspec/specs/media-objects/spec.md @@ -79,7 +79,8 @@ MIME-типом, размером, габаритами и состоянием; ### Requirement: Генерация вариантов Генерация thumb и preview SHALL выполняться через надёжную очередь заданий с однократным выполнением и повтором при сбое; готовый результат SHALL -транслироваться подписчикам альбома. +транслироваться подписчикам альбома. Очередь SHALL поддерживать настраиваемую +пропускную способность и SHALL обрабатывать более свежие фото раньше остальных. #### Scenario: Повтор после рестарта - **WHEN** процесс перезапускается с незавершёнными заданиями @@ -87,4 +88,12 @@ MIME-типом, размером, габаритами и состоянием; #### Scenario: Событие после генерации - **WHEN** варианты фото готовы -- **THEN** подписчикам альбома публикуется событие с этим фото +- **THEN** подписчикам альбома публикуется событие обновления этого фото + +#### Scenario: Приоритет свежих фото +- **WHEN** в очереди есть готовые к обработке задания разного возраста +- **THEN** система обрабатывает более свежие фото раньше + +#### Scenario: Настраиваемая пропускная способность +- **WHEN** задан уровень параллелизма генерации +- **THEN** система обрабатывает варианты с этим уровнем параллелизма diff --git a/openspec/specs/photos/spec.md b/openspec/specs/photos/spec.md index 7e63513..f8c174e 100644 --- a/openspec/specs/photos/spec.md +++ b/openspec/specs/photos/spec.md @@ -33,11 +33,17 @@ ### Requirement: Получение фото по требованию Клиент SHALL иметь возможность получить актуальные данные фото с подписанным URL, -обслуживаемым бэкендом, независимо от бэкенда хранения. +обслуживаемым бэкендом, независимо от бэкенда хранения. Подпись SHALL быть +детерминированной в пределах окна действия, чтобы повторные выдачи одного и того же +объекта давали идентичный URL. #### Scenario: Выдача свежего URL - **WHEN** клиент запрашивает фото по id -- **THEN** система возвращает свежий подписанный URL бэкенда (срок до 1 часа) для просмотра +- **THEN** система возвращает подписанный URL бэкенда для просмотра + +#### Scenario: Повторная выдача совпадает +- **WHEN** один и тот же объект запрашивается повторно в пределах окна действия подписи +- **THEN** система возвращает идентичный URL ### Requirement: Скачивание одиночного фото Любой участник, включая viewer, SHALL иметь возможность скачать одиночное фото. @@ -58,17 +64,26 @@ - **THEN** система отклоняет запрос ### Requirement: Обложка по умолчанию -При обработке загруженного фото система SHALL назначать его обложкой альбома только если -обложка ещё не задана. +При загрузке фото система SHALL назначать его обложкой альбома, если обложка ещё не задана, +то есть обложкой становится первое загруженное фото. Когда фото, выбранное обложкой, покидает +альбом (помещено в корзину или перенесено в другой альбом), система SHALL снять обложку. #### Scenario: Первое фото -- **WHEN** фото обработано, и у альбома нет обложки -- **THEN** система назначает это фото обложкой +- **WHEN** фото загружено, и у альбома нет обложки +- **THEN** система назначает это фото обложкой альбома #### Scenario: Обложка уже задана -- **WHEN** фото обработано, а обложка уже задана +- **WHEN** фото загружено, а обложка уже задана - **THEN** система не меняет обложку +#### Scenario: Фото-обложка в корзине +- **WHEN** фото, назначенное обложкой, помещается в корзину +- **THEN** система снимает обложку, и альбом отображается без обложки + +#### Scenario: Фото-обложка перенесено +- **WHEN** фото, назначенное обложкой, переносится в другой альбом +- **THEN** система снимает обложку исходного альбома + ### Requirement: Переименование фото Автор фото, владелец и админ альбома SHALL иметь возможность изменить имя файла фото; остальные SHALL получать отказ, а пустое имя SHALL отклоняться. @@ -171,3 +186,16 @@ #### Scenario: Снятие метки - **WHEN** пользователь убирает метку с фото - **THEN** метка отвязывается от фото + +### Requirement: Кэширование объектов +Доставка объектов SHALL помечать ответы как кэшируемые, чтобы браузер переиспользовал +уже полученные превью, а повторное открытие альбома не вызывало повторную загрузку +неизменившихся объектов. + +#### Scenario: Заголовки кэширования +- **WHEN** клиент получает объект (оригинал, превью или thumb) по подписанному URL +- **THEN** ответ содержит заголовок кэширования с ограниченным сроком годности + +#### Scenario: Повторный показ без перезагрузки +- **WHEN** сетка превью перерисовывается, а URL объектов не изменились +- **THEN** браузер отдаёт объекты из кэша без повторного запроса к бэкенду diff --git a/openspec/specs/realtime/spec.md b/openspec/specs/realtime/spec.md index 5235949..760b763 100644 --- a/openspec/specs/realtime/spec.md +++ b/openspec/specs/realtime/spec.md @@ -14,12 +14,19 @@ - **THEN** система устанавливает text/event-stream и отправляет heartbeat ### Requirement: События альбома -Система SHALL публиковать события photo.created, photo.deleted, comment.created, like.added, like.removed, member.joined, member.left, album.updated. +Система SHALL публиковать события photo.created, photo.updated, photo.deleted, +comment.created, like.added, like.removed, member.joined, member.left, album.updated. +Событие photo.created SHALL обозначать появление фото в альбоме, а готовность вариантов +существующего фото SHALL публиковаться как photo.updated. #### Scenario: Новое фото - **WHEN** в альбоме появляется фото - **THEN** подписчики получают событие photo.created с метаданными и URL превью +#### Scenario: Готовы варианты +- **WHEN** для существующего фото сгенерированы thumb и preview +- **THEN** подписчики получают событие photo.updated с этим фото + ### Requirement: Персональный поток Клиент SHALL иметь возможность подписаться на персональный поток (приглашения, уведомления). @@ -47,3 +54,15 @@ #### Scenario: Переподключение - **WHEN** клиент переподключается после разрыва - **THEN** клиент догоняет события через запрос фото с параметром since + +### Requirement: Различение добавления и обновления фото +Клиент SHALL различать добавление нового фото в альбом и обновление уже показанного: +обновление SHALL изменять фото на месте, не меняя порядок сетки. + +#### Scenario: Обновление отображаемого фото +- **WHEN** приходит событие обновления для фото, которое уже показано +- **THEN** клиент обновляет его на месте, порядок сетки не меняется + +#### Scenario: Обновление отсутствующего фото +- **WHEN** приходит событие обновления для фото, которого нет в текущем списке +- **THEN** клиент не добавляет его в сетку