From e8e3ef62854dae8448deaa66ac26e5b15d5c7d6a Mon Sep 17 00:00:00 2001 From: codex-bot Date: Sat, 17 Oct 2026 22:11:49 +0300 Subject: [PATCH] docs: describe quiet reading and visible source state (#51) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ADR 021 фиксирует решение о тихом чтении и видимом состоянии источника; ui-resilience и затронутые domains приведены к AS-IS. --- ...21-quiet-reading-and-rare-confirmations.md | 66 +++++++++++++++++++ adr/README.md | 1 + architecture/cross-cutting/ui-resilience.md | 36 +++++++--- domains/accounts/overview.md | 4 +- domains/income/overview.md | 7 +- domains/instruments/bonds.md | 3 +- domains/instruments/stocks.md | 13 ++-- .../how-to/investigate-discrepancy.md | 9 +-- domains/portfolio/overview.md | 11 ++-- 9 files changed, 120 insertions(+), 30 deletions(-) create mode 100644 adr/021-quiet-reading-and-rare-confirmations.md diff --git a/adr/021-quiet-reading-and-rare-confirmations.md b/adr/021-quiet-reading-and-rare-confirmations.md new file mode 100644 index 0000000..9012b7b --- /dev/null +++ b/adr/021-quiet-reading-and-rare-confirmations.md @@ -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`. diff --git a/adr/README.md b/adr/README.md index 2d1e314..433e4a1 100644 --- a/adr/README.md +++ b/adr/README.md @@ -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) diff --git a/architecture/cross-cutting/ui-resilience.md b/architecture/cross-cutting/ui-resilience.md index 207b5fc..10ba1ef 100644 --- a/architecture/cross-cutting/ui-resilience.md +++ b/architecture/cross-cutting/ui-resilience.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 diff --git a/domains/accounts/overview.md b/domains/accounts/overview.md index 8a52b2e..3c73766 100644 --- a/domains/accounts/overview.md +++ b/domains/accounts/overview.md @@ -62,8 +62,8 @@ - `409 TBANK_NOT_CONNECTED`: список не запрашивается у T-Bank, экран ведёт в настройки. - `400 INVALID_TBANK_TOKEN`: сохранённый токен не удаляется, экран ведёт в настройки для замены или удаления. -- `503 TBANK_UNAVAILABLE`: экран предлагает повторить загрузку или открыть настройки. -- Неизвестная ошибка: экран предлагает только повторить загрузку. +- `503 TBANK_UNAVAILABLE`: экран показывает спокойное состояние недоступности; чтение возобновляется автоматически. +- Неизвестная ошибка: то же состояние недоступности без действия повтора. - Успешный пустой ответ: штатное состояние отсутствия инвестиционных счетов. Карточка ведёт на `/accounts/:accountId` по всей области и доступна с клавиатуры. Карточки расположены в одну колонку при любой ширине. Карточка показывает название, локализованный тип, крупный валютный итог и вторую валюту при наличии, число остальных валют, дату открытия без времени и видимое действие открытия. Отдельные цветные блоки «Акции» и «Облигации» показывают количество наименований бумаг. Недоступная или пустая сводка отображается локально; отсутствие сумм не скрывает известные количества бумаг, а недоступная сводка не подменяет их нулями. Наведение и видимый клавиатурный фокус подчёркивают переход; настройка уменьшения движения отключает анимацию. diff --git a/domains/income/overview.md b/domains/income/overview.md index 28653fb..9ec0d21 100644 --- a/domains/income/overview.md +++ b/domains/income/overview.md @@ -39,10 +39,11 @@ При первой загрузке или новом периоде без сохранённых данных показан каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса -или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить». +или нарушение принадлежности выплат даёт локальное состояние недоступности +без действия повтора; чтение восстанавливается автоматически. При фоновом обновлении того же периода сохранённый ответ и черновик -остаются видимыми: загрузку обозначает индикатор, неуспешное обновление — -предупреждение с повторной попыткой. +остаются видимыми без индикации обновления и технического предупреждения. +Неполнота применённого периода обозначается состоянием самого результата. На узком экране поля и действия идут вертикально; все карточки выплат остаются в общем потоке страницы без раскрытия или внутренних областей прокрутки. diff --git a/domains/instruments/bonds.md b/domains/instruments/bonds.md index a5f2595..340a25d 100644 --- a/domains/instruments/bonds.md +++ b/domains/instruments/bonds.md @@ -131,7 +131,8 @@ BondBy переиспользуется зависимостями выпуск выплат. Наблюдение статуса не запускает повторный импорт. Политика источников и границы — в [модели свежести](../../architecture/cross-cutting/data-freshness.md). -Разделы загружаются независимо, показывают каркас загрузки и локальную повторную попытку. +Разделы загружаются независимо, показывают каркас загрузки и локальное +спокойное состояние недоступности без действия повтора. Ошибка фонового обновления сохраняет последние данные. Смена подключения сбрасывает поддерево клиентских запросов облигаций вместе с остальными финансовыми запросами; выход очищает кэш сессии. Общие график, позиция и личные события diff --git a/domains/instruments/stocks.md b/domains/instruments/stocks.md index 4f35923..fd4cd54 100644 --- a/domains/instruments/stocks.md +++ b/domains/instruments/stocks.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 очищают личное представление; поздние ответы не возвращают прежние данные. Детали — diff --git a/domains/portfolio/how-to/investigate-discrepancy.md b/domains/portfolio/how-to/investigate-discrepancy.md index 20da157..5b5e446 100644 --- a/domains/portfolio/how-to/investigate-discrepancy.md +++ b/domains/portfolio/how-to/investigate-discrepancy.md @@ -12,11 +12,12 @@ величины. Убедитесь, что сравниваемые значения относятся к одному составу счетов и инструменту. Название или тикер сами по себе не доказывают совпадение выпуска; используйте доступные UID, FIGI и ISIN. -2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и наличие - предупреждения о клиентском обновлении. Серверный снимок не обещает время +2. **Сопоставьте время.** Проверьте `lastSuccessfulAt`, `isStale` и показанный + исход последней часовой попытки. Серверный снимок не обещает время каждой котировки, а общий портфель может соединять разные моменты. - Если запрос завершился ошибкой, доступное действие «Повторить» должно - обновить результат или оставить объяснимую ошибку. Перезагрузка backend + Действия «Повторить» на экране нет: временный сбой чтения восстанавливается + автоматически, поэтому расхождение проверяйте по показанному состоянию + источника, а не по результату ручного обновления. Перезагрузка backend сохраняет последний полный снимок в SQLite, но не создаёт историю стоимости. 3. **Проверьте состав суммы.** Для одной валюты итог складывается из вычислимых стоимостей позиций и доступных денег. Не добавляйте деньги diff --git a/domains/portfolio/overview.md b/domains/portfolio/overview.md index c6d5314..21afefd 100644 --- a/domains/portfolio/overview.md +++ b/domains/portfolio/overview.md @@ -80,11 +80,12 @@ Backend получает данные через серверную сохраняются предыдущие суммы и время успешного получения в UTC. Серверный `isStale` обозначает возраст снимка старше часа: обновлением -владеет часовой обход, а не запрос экрана. -Клиентское предупреждение означает неудачную попытку обновить уже показанный -ответ. Эти состояния независимы и могут появиться одновременно. -Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку; -серверное предупреждение определяется новым `isStale`. +владеет часовой обход, а не запрос экрана. Он показывается рядом с суммами +вместе с временем последнего успешного результата и исходом последней часовой +попытки. Неудачная попытка клиента обновить уже показанный ответ о себе не +сообщает: эти состояния независимы, но пользователю видимо только состояние +источника. Успешное автоматическое перечитывание обновляет суммы и время; +обозначение устаревания определяется новым `isStale`. Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md). Отдельного чтения истории стоимости нет, но каждый полный снимок