docs: describe quiet reading and visible source state (#51)
ADR 021 фиксирует решение: автоматическое чтение клиента не сообщает о себе, состояние источника из meta остаётся видимым, ошибки явных действий пользователя сохраняются. Приведены к AS-IS: ui-resilience, accounts, income, bonds, stocks, portfolio overview и how-to. ADR 017 не правился — он помечен историческим и заменён ADR 019/021.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# 021. Тихое чтение и редкие подтверждения
|
||||
|
||||
**Status:** Accepted. Заменяет manual-refresh часть
|
||||
[ADR 019](019-hourly-reads-and-online-instrument-reference.md) на стороне
|
||||
интерфейса и уточняет клиентскую часть
|
||||
[ADR 007](007-react-query-fetch-policy.md). Не меняет владельцев обновления
|
||||
и границы чтения из ADR 019.
|
||||
|
||||
## Context
|
||||
|
||||
Экраны просмотра перекладывали на пользователя контроль над получением данных:
|
||||
кнопки «Обновить» и «Повторить», индикатор фонового обновления и сообщение
|
||||
о неудачной синхронизации повторялись в разных разделах. Пользователь приходит
|
||||
смотреть финансы, а не обслуживать загрузку; восстановление после временного
|
||||
сбоя не должно зависеть от нажатия кнопки.
|
||||
|
||||
При этом состояние самих данных остаётся значимым: время последнего успешного
|
||||
результата источника, устаревание и исход часовой попытки влияют на доверие
|
||||
к показанным суммам и не могут быть просто скрыты.
|
||||
|
||||
## Decision
|
||||
|
||||
- Автоматическое чтение клиента не сообщает о себе. Экраны просмотра не
|
||||
показывают кнопок ручного обновления и повтора, индикаторов фонового
|
||||
перечитывания и технических предупреждений о собственном запросе.
|
||||
- Временные сбои GET восстанавливаются сами: ограниченный срок ожидания запроса
|
||||
и не более двух повторов с возрастающими паузами, затем возврат к штатной
|
||||
работе при появлении результата. Пользователь не инициирует восстановление.
|
||||
- Состояние источника из `meta` остаётся видимым. Время последнего успешного
|
||||
результата, признак устаревания, исход последней часовой попытки и неполнота
|
||||
показываются текстом либо доступно именованным элементом, а не только цветом.
|
||||
Это свойство данных, а не отчёт о техническом процессе, и оно не превращается
|
||||
в действие пользователя.
|
||||
- Доступный результат сохраняется при обновлении и временной ошибке. Неполнота
|
||||
и подтверждённая пустота остаются различимыми: неизвестная сумма не становится
|
||||
нулём, незавершённый расчёт не выдаётся за окончательный, незагруженный список
|
||||
не выдаётся за отсутствие записей.
|
||||
- Первичное ожидание ограничено по времени и показывает структуру будущего
|
||||
содержимого. По его истечении экран переходит в спокойную локальную
|
||||
недоступность вместо бесконечной загрузки и сам заменяет её содержимым после
|
||||
восстановления. Сбой одного блока не скрывает остальные доступные блоки.
|
||||
- Ошибки явных действий пользователя сохраняются и остаются видимыми. Решение
|
||||
касается только чтения; отправка формы, изменение данных и сброс фильтра
|
||||
по-прежнему сообщают результат, в том числе неуспешный.
|
||||
|
||||
## Consequences
|
||||
|
||||
Пользователь не может ускорить обновление вручную: нормальным владельцем
|
||||
обновления остаётся часовой обход из ADR 019, и отсутствие кнопки не меняет его
|
||||
срок. Если источник недоступен долго, экран показывает прежние данные с
|
||||
обозначением устаревания, а не приглашение повторить.
|
||||
|
||||
Автоматический повтор ограничен: он покрывает короткие сетевые сбои, но не
|
||||
длительную недоступность. Ошибки доступа, домена и отмены не повторяются,
|
||||
mutations не повторяются вовсе.
|
||||
|
||||
Состояние источника и состояние клиентского чтения описывают разные вещи и
|
||||
не подменяют друг друга. `Portfolio.isStale` сохраняет смысл возраста снимка;
|
||||
ошибка клиентского перечитывания его не меняет.
|
||||
|
||||
Детали поведения — в
|
||||
[ожидании и восстановлении интерфейса](../architecture/cross-cutting/ui-resilience.md)
|
||||
и [свежести данных](../architecture/cross-cutting/data-freshness.md).
|
||||
Согласованные требования принадлежат workspace-спецификациям
|
||||
`frontend-user-feedback`, `frontend-data-resilience` и
|
||||
`frontend-loading-presentation`.
|
||||
@@ -25,3 +25,4 @@
|
||||
- [018 — Минимальная runtime-координация](018-minimal-runtime-coordination.md)
|
||||
- [019 — Часовой обход и онлайн-справочник инструментов](019-hourly-reads-and-online-instrument-reference.md)
|
||||
- [020 — Evidence for hourly sync read boundaries](020-hourly-sync-verification-evidence.md)
|
||||
- [021 — Тихое чтение и редкие подтверждения](021-quiet-reading-and-rare-confirmations.md)
|
||||
|
||||
@@ -16,11 +16,17 @@
|
||||
пока неизвестно, доступен public или protected маршрут. При загрузке
|
||||
отложенного маршрута уже доступный каркас остаётся видимым.
|
||||
|
||||
Фоновое обновление и действие пользователя сохраняют видимые данные или
|
||||
элемент управления с компактной индикацией процесса. В частности,
|
||||
Фоновое чтение не сообщает о себе: оно сохраняет видимые данные без индикации
|
||||
процесса и без технического предупреждения. В частности,
|
||||
[клиентский снимок](data-freshness.md#клиентский-и-серверный-снимки) счетов или
|
||||
портфеля остаётся доступным после ошибки refetch; сообщение и повтор
|
||||
относятся к обновлению, а не к отсутствию инвестиций.
|
||||
портфеля остаётся доступным после ошибки refetch, и эта ошибка не означает
|
||||
отсутствие инвестиций. Действие пользователя сохраняет видимые данные или
|
||||
элемент управления и сообщает свой результат, включая неуспешный.
|
||||
|
||||
Первичное ожидание ограничено по времени. По его истечении область переходит
|
||||
в спокойную локальную недоступность вместо бесконечной загрузки и сама
|
||||
заменяет её содержимым после восстановления. Решение принято в
|
||||
[ADR 021](../../adr/021-quiet-reading-and-rare-confirmations.md).
|
||||
|
||||
Серверная полнота не выводится из query lifecycle. Missing показывает
|
||||
skeleton только отсутствующих секций, partial — подтверждённые строки и
|
||||
@@ -28,20 +34,30 @@ skeleton только отсутствующих секций, partial — по
|
||||
готовятся независимо. Только complete empty означает отсутствие операций.
|
||||
Время источника не заменяется временем получения HTTP-ответа.
|
||||
|
||||
Ручное обновление сохраняет показанный результат и applied filters; кнопка
|
||||
недоступна во время уже запрошенной работы. Ошибка transport отличается от
|
||||
Экраны просмотра не предлагают ручное обновление или повтор чтения: временные
|
||||
сбои GET восстанавливаются сами ограниченным числом повторов, сохраняя
|
||||
показанный результат и applied filters. Ошибка transport отличается от
|
||||
ошибки source work. Status observation прекращается после terminal outcome
|
||||
или ухода; само истечение TTL не запускает импорт. Известный access loss
|
||||
очищает personal view, включая защиту от позднего ответа.
|
||||
|
||||
Состояние источника при этом видимо: время последнего успешного результата,
|
||||
устаревание, исход последней часовой попытки и неполнота показаны текстом либо
|
||||
доступно именованным элементом рядом с данными, к которым относятся. Это
|
||||
свойство данных, а не отчёт о техническом процессе, и оно не требует действия.
|
||||
|
||||
## Известная ошибка сценария
|
||||
|
||||
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: рядом
|
||||
с затронутыми данными доступны предусмотренные сценарием retry и navigation
|
||||
actions. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
||||
портфель с предупреждением. Он не превращается в глобальный fallback.
|
||||
Ошибки счетов, портфеля и подключения T-Bank остаются локальными: затронутый
|
||||
блок показывает собственное состояние и не скрывает успешно отображённые
|
||||
соседние блоки. Устаревший серверный Portfolio Snapshot показывает сохранённый
|
||||
портфель с обозначением устаревания. Он не превращается в глобальный fallback.
|
||||
Ошибка источника не означает нулевой баланс или отсутствие операций.
|
||||
|
||||
Предусмотренные сценарием navigation actions сохраняются: подтверждённая потеря
|
||||
доступа предлагает восстановить подключение, а `404` — вернуться к списку
|
||||
счетов. Это навигация и явные действия пользователя, а не повтор чтения.
|
||||
|
||||
## Непойманная ошибка маршрута
|
||||
|
||||
Непойманная runtime-ошибка при отображении public или protected route
|
||||
|
||||
@@ -62,8 +62,8 @@
|
||||
|
||||
- `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки.
|
||||
- `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления.
|
||||
- `503 TBANK_UNAVAILABLE`: экран предлагает повторить загрузку или открыть настройки.
|
||||
- Неизвестная ошибка: экран предлагает только повторить загрузку.
|
||||
- `503 TBANK_UNAVAILABLE`: экран показывает спокойное состояние недоступности; чтение возобновляется автоматически.
|
||||
- Неизвестная ошибка: то же состояние недоступности без действия повтора.
|
||||
- Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов.
|
||||
|
||||
Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. Карточки расположены в одну колонку при любой ширине. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию.
|
||||
|
||||
@@ -39,10 +39,11 @@
|
||||
При первой загрузке или новом периоде без сохранённых данных показан
|
||||
каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и
|
||||
дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса
|
||||
или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить».
|
||||
или нарушение принадлежности выплат даёт локальное состояние недоступности
|
||||
без действия повтора; чтение восстанавливается автоматически.
|
||||
При фоновом обновлении того же периода сохранённый ответ и черновик
|
||||
остаются видимыми: загрузку обозначает индикатор, неуспешное обновление —
|
||||
предупреждение с повторной попыткой.
|
||||
остаются видимыми без индикации обновления и технического предупреждения.
|
||||
Неполнота применённого периода обозначается состоянием самого результата.
|
||||
|
||||
На узком экране поля и действия идут вертикально; все карточки выплат
|
||||
остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки.
|
||||
|
||||
@@ -131,7 +131,8 @@ BondBy переиспользуется зависимостями выпуск
|
||||
выплат. Наблюдение статуса не запускает повторный импорт. Политика источников
|
||||
и границы — в [модели свежести](../../architecture/cross-cutting/data-freshness.md).
|
||||
|
||||
Разделы загружаются независимо, показывают каркас загрузки и локальную повторную попытку.
|
||||
Разделы загружаются независимо, показывают каркас загрузки и локальное
|
||||
спокойное состояние недоступности без действия повтора.
|
||||
Ошибка фонового обновления сохраняет последние данные. Смена подключения
|
||||
сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами;
|
||||
выход очищает кэш сессии. Общие график, позиция и личные события
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
время получения списка. Изменение условий не заменяет текущий список до
|
||||
следующего нажатия «Найти». Повторный поиск получает новый каталог; страницы
|
||||
одного результата не пересчитываются. Подтверждённый пустой результат отличается
|
||||
от ошибки источника, для которой интерфейс предлагает повторить поиск.
|
||||
от ошибки источника: она сообщается рядом с полем поиска, а повторить запрос
|
||||
пользователь может обычным «Найти» — отдельного действия повтора чтения нет.
|
||||
|
||||
Ссылка карточки принадлежит выбранному UID; данные разных режимов не смешиваются.
|
||||
Прямые ссылки из портфеля сохраняют
|
||||
@@ -63,8 +64,9 @@
|
||||
рыночные данные и личная история остаются доступны.
|
||||
|
||||
Каждый раздел загружается независимо. Первичная загрузка показывает каркас
|
||||
раздела, ошибка — локальную повторную попытку. При ошибке фонового обновления
|
||||
сохраняются предыдущие данные и предупреждение. Цена содержит собственное время
|
||||
раздела, затянувшаяся недоступность — локальное спокойное состояние без
|
||||
повторной попытки. При ошибке фонового обновления сохраняются предыдущие
|
||||
данные без технического предупреждения. Цена содержит собственное время
|
||||
UTC, которое может быть старше времени получения ответа. Статус торгов не
|
||||
выводится из свежести цены. Отсутствующее значение не заменяется нулём.
|
||||
|
||||
@@ -166,8 +168,9 @@ TTM означает последние 12 месяцев, MRQ — послед
|
||||
Закрытые полные свечные интервалы, включая пустые, сохраняются после restart.
|
||||
Новый день не делает прежнюю незавершённую свечу полной.
|
||||
|
||||
«Обновить» передаёт текущий инструмент, выбранный график и personal-history
|
||||
scope. Частичные строки видны с предупреждением; неполные личные оценки
|
||||
Отдельного действия «Обновить» на странице нет: чтение текущего инструмента,
|
||||
выбранного графика и personal-history scope выполняется автоматически.
|
||||
Частичные строки видны с обозначением неполноты; неполные личные оценки
|
||||
не становятся нулями. Статус и `sync=observe` не создают новый импорт.
|
||||
Смена подключения и известный access loss очищают личное представление;
|
||||
поздние ответы не возвращают прежние данные. Детали —
|
||||
|
||||
@@ -12,11 +12,12 @@
|
||||
величины. Убедитесь, что сравниваемые значения относятся к одному составу
|
||||
счетов и инструменту. Название или тикер сами по себе не доказывают
|
||||
совпадение выпуска; используйте доступные UID, FIGI и ISIN.
|
||||
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и наличие
|
||||
предупреждения о клиентском обновлении. Серверный снимок не обещает время
|
||||
2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и показанный
|
||||
исход последней часовой попытки. Серверный снимок не обещает время
|
||||
каждой котировки, а общий портфель может соединять разные моменты.
|
||||
Если запрос завершился ошибкой, доступное действие «Повторить» должно
|
||||
обновить результат или оставить объяснимую ошибку. Перезагрузка backend
|
||||
Действия «Повторить» на экране нет: временный сбой чтения восстанавливается
|
||||
автоматически, поэтому расхождение проверяйте по показанному состоянию
|
||||
источника, а не по результату ручного обновления. Перезагрузка backend
|
||||
сохраняет последний полный снимок в SQLite, но не создаёт историю стоимости.
|
||||
3. **Проверьте состав суммы.** Для одной валюты итог складывается из
|
||||
вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги
|
||||
|
||||
@@ -80,11 +80,12 @@ Backend получает данные через серверную
|
||||
сохраняются предыдущие суммы и время успешного получения в UTC.
|
||||
|
||||
Серверный `isStale` обозначает возраст снимка старше часа: обновлением
|
||||
владеет часовой обход, а не запрос экрана.
|
||||
Клиентское предупреждение означает неудачную попытку обновить уже показанный
|
||||
ответ. Эти состояния независимы и могут появиться одновременно.
|
||||
Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку;
|
||||
серверное предупреждение определяется новым `isStale`.
|
||||
владеет часовой обход, а не запрос экрана. Он показывается рядом с суммами
|
||||
вместе с временем последнего успешного результата и исходом последней часовой
|
||||
попытки. Неудачная попытка клиента обновить уже показанный ответ о себе не
|
||||
сообщает: эти состояния независимы, но пользователю видимо только состояние
|
||||
источника. Успешное автоматическое перечитывание обновляет суммы и время;
|
||||
обозначение устаревания определяется новым `isStale`.
|
||||
Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md).
|
||||
|
||||
Отдельного чтения истории стоимости нет, но каждый полный снимок
|
||||
|
||||
Reference in New Issue
Block a user