## 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** клиент прекращает наблюдение и не начинает новые периодические синхронизации; уже начатая серверная работа может завершиться