chore(openspec): архивировать android-live-albums и fix-album-cover-readiness
Дельта android-live-albums: android-client (+3 требования: живое обновление списка, догон при переподключении, потеря доступа) и realtime (порядок списка теперь правило отображения клиента). Дельта fix-album-cover-readiness: realtime (+1 требование: album.updated при готовности вариантов фото-обложки).
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
schema: spec-driven
|
||||
created: 2026-10-16
|
||||
@@ -0,0 +1,65 @@
|
||||
# Design
|
||||
|
||||
## Context
|
||||
|
||||
См. `proposal.md` — мотивация. Что важно для подхода:
|
||||
|
||||
- Ссылки на обложку формируются из `photo_objects` (роли `thumb`/`medium`), которые
|
||||
заполняются асинхронно, поэтому в объекте альбома (`Store.GetByID` джойнит обложку) они
|
||||
появляются не в момент загрузки, а когда варианты готовы.
|
||||
- Загрузка фото вызывает `SetCoverIfEmpty`, то есть первое фото становится обложкой ещё до
|
||||
генерации вариантов, а `NotifyAlbumChanged` на этом шаге публикует `album.updated` уже с
|
||||
`cover_photo_id`, но с пустыми ссылками.
|
||||
- Готовность вариантов обрабатывает `Worker.notify` (`internal/photos/worker.go`): он
|
||||
публикует только `photo.updated` в канал альбома и персональные потоки не трогает.
|
||||
- `albums.Service` уже умеет перечитать альбом, обогатить обложку (`attachCover`) и веерно
|
||||
опубликовать `album.updated` (`NotifyAlbumChanged` → `PublishAlbumTo`).
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Доставить актуальную ссылку на обложку в персональные потоки, когда она появляется.
|
||||
- Не увеличивать поток событий при массовой загрузке.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Менять контракт `photo.updated` в канале альбома (открытый экран закрывает свой случай сам).
|
||||
- Клиентские обходные пути (рефетч списка по таймеру и т.п.).
|
||||
- Синхронная генерация вариантов при загрузке.
|
||||
|
||||
## Decisions
|
||||
|
||||
**1. Публикует `albums.Service`, а не воркер.**
|
||||
Решение «объект альбома изменился» принимает владелец альбомов: добавляется
|
||||
`NotifyCoverReady(ctx, albumID, photoID)`, который перечитывает альбом и публикует
|
||||
`album.updated`, только если `cover_photo_id == photoID` (иначе no-op), переиспользуя
|
||||
`NotifyAlbumChanged`. Воркер остаётся про фото и лишь сообщает о готовности.
|
||||
Альтернатива — строить `album.updated` прямо в воркере: тогда `photos` пришлось бы знать
|
||||
форму объекта альбома, обложку и аудиторию.
|
||||
|
||||
**2. Условие — совпадение с текущей обложкой.**
|
||||
Готовность вариантов обычного фото объект альбома не меняет. Без условия пришлось бы на
|
||||
каждое фото перечитывать альбом и веерно публиковать `album.updated` всем участникам — лишняя
|
||||
нагрузка при массовой загрузке и лишние события клиентам.
|
||||
Альтернатива — «публиковать всегда»: проще, но шумно; отвергнуто.
|
||||
|
||||
**3. Проверка по `cover_photo_id`, а не по «в альбоме ещё нет ссылки на обложку».**
|
||||
Так закрываются оба сценария: и автопоставленная обложка первой загрузкой, и явная установка
|
||||
обложки на фото, варианты которого ещё не готовы.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **Двойное чтение альбома** (`GetByID` для проверки обложки и внутри `PublishAlbumTo`) →
|
||||
дополнительное чтение точечное (только для готовности вариантов фото-обложки), принято; при
|
||||
необходимости схлопывается в один публикующий вызов.
|
||||
- **Гонка со сменой обложки** между публикацией `photo.created` и готовностью вариантов →
|
||||
проверка по актуальному `cover_photo_id` отсекает лишнюю публикацию, а новая обложка получит
|
||||
своё событие при готовности её вариантов.
|
||||
- **Аудитория недоступна** (ошибка в `members.Audience`) → событие не публикуется; клиент
|
||||
догоняет состояние при переподключении (`refresh`) — существующее поведение веера.
|
||||
|
||||
## Migration Plan
|
||||
|
||||
Изменение аддитивное: схема и API не меняются, нужен только перезапуск бэкенда (логика и
|
||||
статика в одном бинарнике). Откат — предыдущий бинарник.
|
||||
@@ -0,0 +1,41 @@
|
||||
# Proposal
|
||||
|
||||
## Why
|
||||
|
||||
Обложка альбома на карточке в списке не обновляется вживую: после загрузки фото счётчик
|
||||
`photos_count` приходит, а обложка остаётся заглушкой до ручного обновления. Ссылка на
|
||||
обложку (`cover_thumb_url` / `cover_medium_url`) появляется в объекте альбома только после
|
||||
генерации вариантов thumb/medium, но событие об этом в персональные потоки не публикуется.
|
||||
Дефект серверной стороны способности `realtime`, поэтому затрагивает и веб-клиент, и Android.
|
||||
|
||||
## What Changes
|
||||
|
||||
- Когда для фото, являющегося обложкой альбома, готовы варианты, система публикует
|
||||
`album.updated` с актуальным объектом альбома в персональные потоки владельца и
|
||||
участников — именно тогда объект впервые получает ссылку на обложку.
|
||||
- Готовность вариантов фото, которое обложкой не является, объект альбома не меняет и
|
||||
события не порождает: иначе массовая загрузка даёт лишний веер `album.updated` на каждый
|
||||
файл.
|
||||
- Контракт существующих событий и REST не меняется, новых эндпоинтов и миграций нет.
|
||||
- **BREAKING**: нет.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
_Нет — поведение расширяет существующую способность `realtime`._
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
- `realtime`: добавляется публикация `album.updated` в персональные потоки при готовности
|
||||
вариантов фото-обложки, поскольку только тогда объект альбома получает ссылку на обложку.
|
||||
|
||||
## Impact
|
||||
|
||||
- **Код (backend):** `internal/albums/service.go` (новая публикация), порт `AlbumAccess` в
|
||||
`internal/photos/service.go`, вызов из `internal/photos/worker.go` (готовность вариантов),
|
||||
стаб порта в `internal/photos/helpers_test.go`.
|
||||
- **Документация:** `project.md` (§11 «Real-time (SSE)», §12 «SSE — детали реализации»).
|
||||
- **Не затрагивается:** схема БД, миграции, REST, пер-альбомный контракт (`photo.updated`
|
||||
по-прежнему приходит в канал альбома), клиенты — веб и Android уже применяют
|
||||
`album.updated` из персонального потока.
|
||||
@@ -0,0 +1,17 @@
|
||||
# Spec Delta
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Обновление альбома при готовности вариантов обложки
|
||||
Когда для фото, являющегося обложкой альбома, сгенерированы варианты, система SHALL
|
||||
публиковать `album.updated` с актуальным объектом альбома в персональные потоки владельца и
|
||||
участников: только тогда объект получает ссылку на обложку. Готовность вариантов прочих фото
|
||||
событий не порождает.
|
||||
|
||||
#### Scenario: Варианты обложки готовы
|
||||
- **WHEN** для фото, которое является обложкой альбома, сгенерированы thumb и medium
|
||||
- **THEN** владелец и участники получают `album.updated` с непустой ссылкой на обложку
|
||||
|
||||
#### Scenario: Варианты обычного фото готовы
|
||||
- **WHEN** варианты готовы для фото, которое не является обложкой альбома
|
||||
- **THEN** персональные потоки не получают `album.updated`
|
||||
@@ -0,0 +1,20 @@
|
||||
# Tasks
|
||||
|
||||
## 1. Публикация при готовности вариантов обложки
|
||||
|
||||
- [x] 1.1 Добавить в `albums.Service` публикацию `album.updated` в персональные потоки, когда варианты готовы у фото, являющегося обложкой (проверка по актуальному `cover_photo_id`), переиспользуя существующий веер; проверить тестом: владелец и участник получают событие со ссылкой на обложку, а для фото, которое обложкой не является, публикации нет
|
||||
- [x] 1.2 Расширить порт `AlbumAccess` в `photos` и вызвать публикацию из `Worker.notify` после `photo.updated`, обновив стаб порта в тестах; проверить тестом, что готовность вариантов фото вызывает публикацию
|
||||
|
||||
## 2. Документация контракта
|
||||
|
||||
- [x] 2.1 Обновить `project.md` (§11 «Real-time (SSE)» и §12 «SSE — детали реализации»): `album.updated` публикуется также при готовности вариантов фото-обложки; проверить, что перечисленные триггеры совпадают с реализованными публикациями
|
||||
|
||||
## 3. Проверка
|
||||
|
||||
- [x] 3.1 Прогнать `cd backend && go test ./...` и зафиксировать, что проходит
|
||||
- [x] 3.2 Воспроизвести сценарий на временном инстансе: подписка на `/api/sse/me`, загрузка фото в альбом без обложки — после готовности вариантов приходит `album.updated` с непустой ссылкой на обложку; приложить вывод
|
||||
|
||||
## Workflow follow-up
|
||||
|
||||
- Заархивировать change после прохождения ревью проекта (`openspec archive fix-album-cover-readiness`).
|
||||
- Проверить, что после архивации требование вошло в `openspec/specs/realtime/spec.md`, а `openspec validate --strict fix-album-cover-readiness` не выдаёт ошибок.
|
||||
Reference in New Issue
Block a user