From b91280cbaace4d0ed6d66186c62465abeeda6032 Mon Sep 17 00:00:00 2001 From: Sergey Krylov Date: Mon, 24 Aug 2026 00:51:10 +0300 Subject: [PATCH] docs: document auth and client refresh resilience --- development/audit-remediation.md | 16 +++++++++------- features/authentication.md | 4 ++-- features/brokerage-accounts.md | 2 +- features/portfolio-view.md | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/development/audit-remediation.md b/development/audit-remediation.md index bd8e693..f4efb76 100644 --- a/development/audit-remediation.md +++ b/development/audit-remediation.md @@ -15,12 +15,14 @@ ## Текущее состояние workflow - Bootstrap change: [`2026-08-23-establish-audit-remediation-workflow`](../../specs/openspec/changes/archive/2026-08-23-establish-audit-remediation-workflow/) — `archived`; это process foundation и не закрывает ни одного Audit ID. -- Последний audit remediation change: [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) — `archived`; WL-P2-01, WL-P2-02 и WL-P2-03 закрыты. +- Последний завершённый audit remediation change: [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) — `archived`; WL-P2-01, WL-P2-02 и WL-P2-03 закрыты. +- Активный audit remediation change: [`align-api-contract-and-client-resilience`](../../specs/openspec/changes/align-api-contract-and-client-resilience/) — `active`; WL-P2-04, WL-P2-07 и WL-P2-08 не считаются закрытыми до archive и terminal evidence. -## Следующая задача +## Активная задача -Создать `align-api-contract-and-client-resilience` для WL-P2-04, WL-P2-07 и WL-P2-08: -выбрать единый auth success-status contract и устранить client-side refresh/query races. +`align-api-contract-and-client-resilience`: зафиксировать единый auth +success-status contract и устранить client-side refresh/query races. Новый +remediation change не создаётся, пока этот остаётся `active`. ## Реестр findings @@ -41,11 +43,11 @@ | WL-P2-01 | Ограничить размер и eviction operations cache. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; Node 22 lint, app/test typecheck и 56 unit tests passed. | | WL-P2-02 | Вернуть предсказуемый 409 при конкурентной регистрации. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; P2002 mapping and barrier regression test passed. | | WL-P2-03 | Сделать refresh rotation атомарной. | backend, specs, docs | done | [`2026-08-23-harden-auth-and-cache-concurrency`](../../specs/openspec/changes/archive/2026-08-23-harden-auth-and-cache-concurrency/) | — | 2026-08-23; backend `c77b3cf`, specs `6e20cdf`, docs `8568e7b`; CAS one-winner regression test passed. | -| WL-P2-04 | Синхронизировать runtime, OpenAPI и auth success statuses. | backend, frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Выбрать единый status contract. | — | +| WL-P2-04 | Синхронизировать runtime, OpenAPI и auth success statuses. | backend, frontend | active | [`align-api-contract-and-client-resilience`](../../specs/openspec/changes/align-api-contract-and-client-resilience/) | Выполнить согласованный `200` contract и regression coverage. | — | | WL-P2-05 | Восстановить runnable backend e2e gate. | backend | deferred | — | Пересмотреть после следующей stable Prisma: три чистых `migrate deploy` на Node 22 должны пройти подряд. | 2026-08-23; Prisma 7.9.1 на Node 22 воспроизводимо возвращает пустой schema-engine error даже для fresh SQLite DB; published latest также 7.9.1. | | WL-P2-06 | Добавить проходящий app/test TypeScript gate backend. | backend | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; backend `71d57d6`, specs `72444db`, docs `293df9c`; `npm run typecheck:app` и `npm run typecheck:test` — passed. | -| WL-P2-07 | Сохранять cached data во время background refetch. | frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Зафиксировать stale-data scenarios. | — | -| WL-P2-08 | Нормализовать cold-start refresh failure. | frontend | planned | `align-api-contract-and-client-resilience` (ещё не создан) | Добавить failure test. | — | +| WL-P2-07 | Сохранять cached data во время background refetch. | frontend | active | [`align-api-contract-and-client-resilience`](../../specs/openspec/changes/align-api-contract-and-client-resilience/) | Реализовать и проверить Client Cached Snapshot UX для Accounts и Portfolio. | — | +| WL-P2-08 | Нормализовать cold-start refresh failure. | frontend | active | [`align-api-contract-and-client-resilience`](../../specs/openspec/changes/align-api-contract-and-client-resilience/) | Реализовать и проверить общий controlled refresh failure. | — | | WL-P2-09 | Проверять backend runtime OpenAPI против frontend snapshot. | backend, frontend | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; frontend `7a3048c`, backend `71d57d6`, specs `72444db`, docs `293df9c`; runtime и snapshot SHA-256 совпали, frontend `npm run openapi:check` — passed. | | WL-P2-10 | Актуализировать onboarding и постоянную документацию. | docs, frontend, backend, specs | planned | `refresh-project-documentation-and-agent-entrypoints` (ещё не создан) | Сверить docs с delivered features. | — | | WL-P2-11 | Зафиксировать одинаковый local bootstrap frontend/backend. | frontend, backend, docs | done | [`2026-08-23-make-local-verification-agent-safe`](../../specs/openspec/changes/archive/2026-08-23-make-local-verification-agent-safe/) | — | 2026-08-23; frontend `7a3048c`, backend `71d57d6`, specs `72444db`, docs `293df9c`; Node 22 policy и `npm ci` guidance зафиксированы. | diff --git a/features/authentication.md b/features/authentication.md index 7cccc21..a4cb5b7 100644 --- a/features/authentication.md +++ b/features/authentication.md @@ -10,9 +10,9 @@ Access token живёт 15 минут только в памяти браузе ## API -`POST /api/v1/auth/register`, `POST /api/v1/auth/login` и `POST /api/v1/auth/refresh` возвращают профиль и access token. `POST /api/v1/auth/logout` отвечает `204`. `GET /api/v1/auth/me` требует Bearer access token. +`POST /api/v1/auth/register`, `POST /api/v1/auth/login` и `POST /api/v1/auth/refresh` возвращают профиль и access token с HTTP `200`. `POST /api/v1/auth/logout` отвечает `204`. `GET /api/v1/auth/me` требует Bearer access token. -Ошибки API имеют `application/problem+json` с полями `status` и `code`. Ошибки валидации содержат `errors` с JSON Pointer полей. +Ошибки API имеют `application/problem+json` с полями `status` и `code`. Ошибки валидации содержат `errors` с JSON Pointer полей. HTTP- и транспортная ошибка refresh одинаково завершают cold start готовым неавторизованным состоянием; при refresh после protected `401` frontend очищает in-memory session и React Query cache, сохраняя исходный `401` для существующего login flow. ## Limitations diff --git a/features/brokerage-accounts.md b/features/brokerage-accounts.md index 1505c3a..9ba456d 100644 --- a/features/brokerage-accounts.md +++ b/features/brokerage-accounts.md @@ -52,4 +52,4 @@ sequenceDiagram ## Ограничения MVP -Список запрашивается при открытии экрана без ручного обновления и без refetch при возврате фокуса. Frontend использует успешный React Query-кэш в течение 60 секунд, поэтому повторный вход в этот интервал не выполняет новый GET. Backend не хранит кэш и не передаёт метку свежести; после истечения frontend-кэша один upstream-вызов либо полностью заменяет список, либо показывает error-state. Балансы, доходность, графики, портфель, закрытые счета и операции не входят в feature. +Список запрашивается при открытии экрана без ручного обновления и без refetch при возврате фокуса. Frontend использует успешный React Query-кэш в течение 60 секунд, поэтому повторный вход в этот интервал не выполняет новый GET. Когда уже показанный список обновляется в фоне, он остаётся на экране: индикатор обновления и ошибка с повторной попыткой отображаются рядом с последним успешным ответом, включая пустой список. Это **Client Cached Snapshot**, а не server-side fallback. Backend не хранит кэш и не передаёт метку свежести. Балансы, доходность, графики, портфель, закрытые счета и операции не входят в feature. diff --git a/features/portfolio-view.md b/features/portfolio-view.md index 843b740..e4a65ca 100644 --- a/features/portfolio-view.md +++ b/features/portfolio-view.md @@ -52,7 +52,7 @@ Metadata инструмента ищется сначала по instrument UID, - Metadata по FIGI и подтверждённое отсутствие инструмента кэшируются в памяти 24 часа глобально; ошибки lookup не кэшируются. - `409 TBANK_NOT_CONNECTED` и `400 INVALID_TBANK_TOKEN` ведут в настройки; `404` показывает возврат к списку счетов; `503 TBANK_UNAVAILABLE` и неизвестная ошибка позволяют повторить запрос, если нет fallback Snapshot. -Клиент использует успешный React Query-кэш 30 секунд, без polling, automatic retry и refetch при возврате фокуса. После устаревания предыдущий результат скрывается до следующего ответа. Акции и облигации отображаются отдельными секциями с upstream-порядком внутри каждой; остальные активы остаются в отдельной секции. Position Card -- компактная плитка в адаптивном grid: одна колонка на узком экране, 2-3 на широком. Плитка показывает идентификатор, количество, текущую стоимость, абсолютный доход с цветовой индикацией знака (зелёный при положительном, красный при отрицательном), среднюю цену покупки, долю среди позиций той же валюты и ISIN. Доля -- display-only оценка, вычисляемая на клиенте как `currentValue` позиции к сумме `currentValue` позиций той же валюты, без Cash Balance; отсутствующие метрики скрываются, а не отображаются заглушкой. RUB форматируется как `₽`. Loaded view сохраняет адаптивное одноколоночное отображение на узком экране. +Клиент использует успешный React Query-кэш 30 секунд, без polling, automatic retry и refetch при возврате фокуса. При фоновом обновлении уже полученный Portfolio остаётся видимым как **Client Cached Snapshot**; отдельные индикатор обновления и предупреждение с повторной попыткой показывают его состояние. Это не server-side fallback: `Portfolio.isStale` сообщает только о fallback Snapshot от backend и может отображаться одновременно с client refresh warning. Акции и облигации отображаются отдельными секциями с upstream-порядком внутри каждой; остальные активы остаются в отдельной секции. Position Card -- компактная плитка в адаптивном grid: одна колонка на узком экране, 2-3 на широком. Плитка показывает идентификатор, количество, текущую стоимость, абсолютный доход с цветовой индикацией знака (зелёный при положительном, красный при отрицательном), среднюю цену покупки, долю среди позиций той же валюты и ISIN. Доля -- display-only оценка, вычисляемая на клиенте как `currentValue` позиции к сумме `currentValue` позиций той же валюты, без Cash Balance; отсутствующие метрики скрываются, а не отображаются заглушкой. RUB форматируется как `₽`. Loaded view сохраняет адаптивное одноколоночное отображение на узком экране. ## Ограничения MVP