spec: finalize wealth-ledger SDD migration
Specs security / security (push) Successful in 8s
Specs verify / verify (push) Successful in 11s

This commit was merged in pull request #26.
This commit is contained in:
2026-10-09 21:14:22 +03:00
committed by ksv741
parent 90edab6e33
commit 329d66e767
97 changed files with 3444 additions and 244 deletions
@@ -0,0 +1,59 @@
## MODIFIED Requirements
### Requirement: Client Cached Snapshot остаётся доступным при обновлении Accounts и Portfolio
Frontend MUST сохранять Client Cached Snapshot на всех экранах с данными T-Bank: список Brokerage Accounts, Portfolio, операции, доходы, инструменты, котировки, свечи и выплаты. Снимок MUST сохраняться для того же пользователя, текущего подключения и набора данных, если ранее получен успешный результат с непустым `data`. Подтверждённые `data: []` или пустой полный Portfolio также MUST считаться успешным результатом. Начальная загрузка без такого ответа может показывать блокирующее
состояние загрузки. Client Cached Snapshot MUST оставаться видимым во время
фонового обновления и после его временной ошибки; он отличается от постоянно сохранённых серверных данных и MUST NOT изменять поле `Portfolio.isStale`. Клиент MUST независимо показывать свежесть серверного результата, полноту требуемых периодов, ход синхронизации и ошибку HTTP-refetch. Успешный HTTP-ответ с `meta.sync.state: failed` MUST NOT представляться успешным обновлением источника. Поля `meta.lastSuccessfulAt`, `meta.availability`, `meta.freshness`, `meta.sync` и при наличии `meta.coverage` MUST определять эти состояния по `tbank-data-sync`; клиент MUST NOT выводить их из возраста своего query-кэша. Ошибка ручного обновления ещё свежего источника MUST показываться отдельно от его свежести: клиент MUST NOT объявлять данные устаревшими до истечения серверного TTL либо изменять исходное успешное время.
Состояние `data: null` MUST показывать отсутствие загруженного результата и ход первичной загрузки либо её ошибку. Неполный период MUST явно отличаться от полного и MUST NOT представляться окончательным финансовым итогом или подтверждённым отсутствием операций. Неполное сравнение доходов MUST иметь собственное состояние и MUST NOT подменяться нулём. Статусы MUST быть доступны текстом, не только цветом; действия обновления и повтора MUST быть доступны с клавиатуры и сохранять читаемость на узком экране.
Действие «Обновить» MUST обходить серверную проверку давности для текущего экрана, его применённого периода и зависимостей, сохраняя доступные данные и фильтры. Обычное повторное получение HTTP-ответа MUST NOT считаться ручным принудительным обновлением. Наблюдение за уже запрошенной работой MUST прекращаться после её завершения, ошибки или ухода с экрана, MUST NOT запускать новую синхронизацию и MUST NOT создавать периодическое обновление источника. Уже начатая серверная работа MAY завершиться после ухода.
При подтверждённом `TBANK_NOT_CONNECTED`, `INVALID_TBANK_TOKEN`, недоступности счёта либо изменении пользователя/подключения frontend MUST скрыть личный Snapshot прежней границы. Поздние ответы данных и статусов прежнего подключения MUST NOT попадать в новое состояние; обычная временная ошибка MUST NOT менять эту границу.
#### Scenario: Список Brokerage Accounts обновляется в фоне
- **WHEN** экран Accounts уже показывает Client Cached Snapshot и начинается
фоновое обновление того же списка
- **THEN** frontend оставляет список видимым и показывает только
неблокирующий индикатор обновления
#### Scenario: Обновление Portfolio завершается ошибкой после успешного ответа
- **WHEN** экран Brokerage Account уже показывает Client Cached Snapshot
Portfolio, а последующее обновление этого же Portfolio завершается временной ошибкой
- **THEN** frontend сохраняет Portfolio видимым, показывает локализованное
неблокирующее предупреждение о показе последних полученных данных и
предоставляет повторную попытку обновления
#### Scenario: Server-side fallback и client cached snapshot различаются
- **WHEN** backend возвращает Portfolio с `isStale` либо фоновое обновление
Client Cached Snapshot завершается временной ошибкой
- **THEN** frontend отображает соответствующее server-side либо client-side
предупреждение независимо, не представляя один вид состояния другим
#### Scenario: Серверная синхронизация не завершена после успешного HTTP-ответа
- **WHEN** экран получает `200` с сохранёнными данными и `meta.sync.state: running`
- **THEN** он показывает данные и прежнее успешное время вместе с состоянием обновления, не считая HTTP-успех завершением синхронизации
#### Scenario: Первичная загрузка без данных
- **WHEN** любой экран T-Bank получает `data: null` с отсутствующим либо частичным покрытием
- **THEN** он показывает загрузку либо её ошибку без вымышленных нулей, окончательных итогов и утверждения об отсутствии операций
#### Scenario: Ошибка синхронизации при успешном HTTP-ответе
- **WHEN** экран получает `200` с сохранёнными данными, `meta.sync.state: failed` и безопасным кодом ошибки
- **THEN** данные и время их успешного получения остаются видимыми с отдельным предупреждением об ошибке обновления и действием повтора
#### Scenario: Ошибка обновления не подменяет свежесть
- **WHEN** экран получает ещё свежие данные с `meta.freshness: fresh` и `meta.sync.state: failed` после ручного обновления
- **THEN** он показывает ошибку обновления и сохранённое успешное время без предупреждения об устаревших данных только из-за этой ошибки
#### Scenario: Ручное обновление сохраняет текущий контекст
- **WHEN** User нажимает «Обновить» на экране с выбранным счётом, периодом и фильтрами
- **THEN** клиент запрашивает принудительное обновление этого экрана и зависимостей, сохраняет контекст и доступные данные и не запрашивает обновление всей накопленной истории
#### Scenario: Недействительный token скрывает личный снимок
- **WHEN** источник подтвердил недействительность текущего token и клиент получает `INVALID_TBANK_TOKEN` при наличии сохранённого личного Snapshot
- **THEN** клиент скрывает личные данные и показывает существующее действие настройки подключения вместо маскировки отказа устаревшими данными
#### Scenario: Наблюдение не превращается в расписание
- **WHEN** запрошенная синхронизация завершилась, завершилась ошибкой либо User ушёл со страницы
- **THEN** клиент прекращает наблюдение и не начинает новые периодические синхронизации; уже начатая серверная работа может завершиться