docs: document hourly investment reads
Docs security / security (push) Successful in 8s
Docs verify / verify (push) Successful in 13s
Conventional commits / commits (pull_request) Successful in 12s
Docs security / security (pull_request) Successful in 8s
Docs verify / verify (pull_request) Successful in 14s

This commit is contained in:
2026-10-09 19:56:40 +03:00
parent 3a272681c5
commit a45d3e77b9
6 changed files with 154 additions and 121 deletions
+47 -37
View File
@@ -1,54 +1,64 @@
# Свежесть данных и повторное использование
WealthLedger читает инвестиционные данные из SQLite. Обычный GET возвращает
сохранённый результат и не запускает личную синхронизацию. Общие справочные
данные инструментов запрашиваются онлайн, когда это требуется карточке;
финансовый HTTP-ответ не ждёт T-Bank. Модель принята в
[ADR 017](../../adr/017-local-tbank-reads-and-background-sync.md).
WealthLedger читает личные инвестиционные данные из SQLite. Обычный GET и его
HTTP retry возвращают сохранённый результат и не запускают личную работу с
T-Bank. Нормальным владельцем обновления служит часовой обход текущего
подключения: после успешного подключения он начинает первую попытку, затем
начинает следующую не ранее чем через 60 минут от начала предыдущей. После
простоя выполняется не более одна актуальная попытка, а перекрывающийся срок
пропускается, не ставится в очередь. Общие справочные данные инструментов
запрашиваются онлайн, когда это требуется карточке; финансовый HTTP-ответ не
ждёт T-Bank. Модель принята в
[ADR 019](../../adr/019-hourly-reads-and-online-instrument-reference.md).
## Клиентский и серверный снимки
[Client Cached Snapshot](../../glossary.md#client-cached-snapshot) — успешный
ответ в TanStack Query. Он сохраняет показанное содержимое при временной ошибке
запроса в той же границе доступа. SQLite сохраняет источники независимо от
браузера: membership, последний полный portfolio, исполненные операции,
общие сведения об инструментах, котировки, свечи и календари.
HTTP-перечитывания в той же границе доступа. Это не серверный fallback и не
состояние часового обхода. SQLite независимо от браузера хранит membership,
последний полный portfolio, исполненные операции, прогнозы, общие сведения об
инструментах, котировки, свечи и календари.
[Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются.
Успешно проверенный пустой интервал полон; последняя страница журнала сама
по себе полноту не доказывает. Ошибка сверки сохраняет прежние данные и время.
Старый полный портфель не удаляется по истечении TTL. `isStale` показывает
возраст portfolio source, а не сам факт неудачного обновления.
Успешно проверенный пустой результат полон и доступен; последняя страница
журнала сама по себе полноту не доказывает. Результат каждого личного ресурса
в `{ data, meta }` описывают пять независимых значений: freshness,
completeness, `lastSuccessfulAt`, `lastAttempt` и availability. Первые,
полнота и `lastSuccessfulAt` относятся только к возвращённому подтверждённому
результату. `lastAttempt` описывает относящуюся к ресурсу часовую попытку:
`none`, `running`, `succeeded`, `partial` или `failed`.
| Источник | Когда обычный запрос требует обновления |
Поздняя неуспешная или частичная попытка сохраняет прежний полный результат и
его исходное время. Если полного результата нет, `data` равно `null`: состояние
availability объясняет первоначальную загрузку, временную недоступность либо
недоступность подключения. Пустой массив не означает отсутствие результата.
Старый полный portfolio не удаляется из-за возраста. `Portfolio.isStale`
сохраняет смысл возраста его snapshot, а не обозначает неуспех обхода или
клиентскую ошибку перечитывания.
| Источник | Владелец нормального обновления |
| --- | --- |
| Portfolio, котировки, изменяемый хвост дневных свечей | Через 60 секунд |
| Membership и изменяемый хвост операций | Через 5 минут |
| Membership, portfolio, изменяемый хвост операций и события | Часовой обход текущего подключения |
| Котировки и изменяемый хвост дневных свечей | Установленная политика источника; не ordinary personal GET |
| Metadata, поиск каталога и company | Онлайн при чтении |
| Календари | Через часовой обход |
| Полные прошлые интервалы операций и завершённые свечи | При явном обновлении показанного периода |
| Полные прошлые интервалы операций и завершённые свечи | Не обновляются часовым обходом или обычным GET |
TTL не создаёт таймер импорта. Запрошенная работа может закончиться после
ухода со страницы. После перезапуска нет автоматического обхода intent:
следующий подходящий запрос повторяет незавершённый интервал с первой страницы,
переиспользуя полные интервалы. Cursor брокера не служит durable checkpoint.
Два исключения разрешают только первое получение отсутствующего результата:
первое открытие неподтверждённого исторического интервала операций получает
только этот интервал и возвращает локальные строки с неполным покрытием; первое
открытие диапазона свечей без подтверждённого локального результата получает
только этот диапазон. Ни одно исключение не обновляет membership, portfolio,
события, другой интервал или диапазон. Их HTTP retry также только перечитывает
локальное состояние. Подтверждённый полный интервал или диапазон остаётся
локальным.
Повторное чтение перечитывает общие справочные данные онлайн, а финансовые
ресурсы используют сохранённый результат и применяемую политику свежести.
Повторная сверка полного периода публикуется одной
транзакцией после всех страниц, интервалов и счетов, включая `all`.
Сбой сохраняет весь прежний результат. Первая загрузка может публиковать
полные малые интервалы и показывать частичный период без окончательных сумм.
Клиент наблюдает только известную активную работу открытого экрана.
Status GET и data GET с `sync=observe` не запускают работу. После изменения
состояния ответ попадает в ту же query identity; после ухода или завершения
наблюдение прекращается. Поздний ответ не заменяет уже принятый новый.
[HTTP API](../http-api.md) объясняет envelope и экранный refresh.
Отменой query lifecycle владеет TanStack Query. Status не пересобирает
финансовые ответы и не передаёт hash notifications. Свечи нового диапазона
загружаются при его открытии; предзагрузка всей истории портфеля не выполняется.
Часовой обход сохраняет успешные разделы независимо, а failed section не
заменяет прежний complete result. Отменой query lifecycle владеет TanStack
Query. Открытый экран перечитывает ресурс не чаще одного раза в 30 минут;
это локальный HTTP read. Совместимый параметр `sync=observe`, пока он есть в
маршруте, также является только чтением и не представляет команду обходу.
[HTTP API](../http-api.md) описывает исполняемый контракт envelope.
## Смена подключения
+21 -20
View File
@@ -2,7 +2,8 @@
## Источник контракта
Backend генерирует OpenAPI из собственного публичного API. Frontend использует
Backend генерирует OpenAPI из собственного публичного API. Этот сгенерированный
OpenAPI — исполняемый источник HTTP-контракта. Frontend использует
предоставленный OpenAPI snapshot как актуальный вход и локально генерирует
типизированный клиент. Backend владеет контрактом, а подготовка и доставка
схемы находятся вне frontend. Это разделение принято в
@@ -22,27 +23,27 @@ environment policy, а не используется frontend в runtime.
## Инвестиционные данные и синхронизация
T-Bank data GET возвращают envelope `{ data, meta }`. `data` может быть `null`
при missing или неполном финансовом агрегате; partial ledger/candles могут
содержать подтверждённые строки. `meta` разделяет availability, freshness,
lastSuccessfulAt, sync, coverage и состояния зависимостей. `dataRevision`
описывает набор данных; пустое завершённое покрытие явно представлено в coverage.
Публичной hash-версии состояния нет. Ни одно поле не раскрывает connection revision.
Затронутые GET инвестиционных ресурсов возвращают сгенерированный contract
`{ data, meta }`. `data` nullable: `null` означает отсутствие complete result,
а подтверждённый пустой массив является available complete result. `meta`
независимо описывает freshness, completeness, `lastSuccessfulAt`,
resource-relevant `lastAttempt` и availability. `lastAttempt` имеет состояния
`none`, `running`, `succeeded`, `partial` и `failed`; его неуспех не заменяет
сохранённый complete result или его время. `Portfolio.isStale` остаётся
специфичным для snapshot полем, а не заменой состояния попытки.
Обычный GET планирует missing/stale зависимости после проверки доступа.
GET с `sync=observe` только читает. `POST /api/v1/tbank-sync` принимает
валидированный screen descriptor и возвращает `202` с handle и нормализованным
scope. `GET /api/v1/tbank-sync/:id` наблюдает разрешённую работу; чужой или
прежний handle возвращает `404`. Приём работы не означает её успех.
Только Accounts дополнительно возвращает top-level `sync`: `null`, если у
текущего подключения ещё не было часовой попытки, иначе connection-wide
наблюдаемый результат. Это наблюдение, не команда. Обычный GET и retry после
проверки доступа читают локальное состояние и не начинают личную работу.
Устаревший `sync=observe`, пока он сохранён для совместимости маршрута, также
только читает. Request-started screen-sync command/status не является текущей
AS-IS моделью.
Status возвращает state/errorCode без данных ресурсов и численных progress
counters. Клиент перечитывает показанные resources через `sync=observe` во время
работы и после завершения, сохраняя generated query identity. Sync states ресурса:
`idle`, `running`, `failed`; отдельного публичного `queued` нет.
Source failure принятой работы передаётся безопасным `meta.sync.errorCode`;
HTTP transport/access/input errors сохраняют Problem Details. Старые данные
при transient failure сохраняют исходное успешное время. Детали поведения —
Безопасные ошибки завершённой часовой попытки передаются в `lastAttempt` и
Accounts `sync`; HTTP transport/access/input errors сохраняют Problem Details.
Старые данные при временной source failure сохраняют исходное успешное время.
Ни одно поле не раскрывает connection revision. Детали поведения —
[свежесть и полнота](cross-cutting/data-freshness.md).
Envelope несовместим с прежними bare-array ответами: backend/frontend