diff --git a/AGENTS.md b/AGENTS.md index 60e8468..4e2fca4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,7 @@ bash ../specs/scripts/verify-runtime-openapi.sh ## Границы - `adr/` - долгоживущие решения, `architecture/` - устройство системы, `development/` - процесс разработки, `features/` - фактическое поведение завершенных фич. +- Перед изменением фактического описания системы сверяться с [`architecture/system-overview.md`](architecture/system-overview.md) и [`architecture/domain-glossary.md`](architecture/domain-glossary.md); development-документы индексируются в [`development/README.md`](development/README.md). - Для диаграмм использовать Mermaid в Markdown. Feature-документация описывает пользовательский поток, API-сценарий и ограничения. - Не хранить OpenSpec-артефакты, code приложений или секреты; requirements, design, delta-specs и tasks остаются только в `../specs/openspec`. - Не создавать и не коммитить framework-specific planning/spec/task/design artifacts, включая `superpowers/plans`; `../references/**` остаётся read-only исключением. diff --git a/architecture/README.md b/architecture/README.md index a17ddf8..bdf2a6d 100644 --- a/architecture/README.md +++ b/architecture/README.md @@ -1,3 +1,8 @@ -# Architecture +# Архитектура -Здесь описывается фактическое устройство системы, общие границы компонентов и интеграций. Диаграммы Mermaid добавляются, когда они проясняют взаимодействие. +Здесь описывается фактическое устройство WealthLedger и границы компонентов. + +- [Обзор системы](system-overview.md) — поставленные возможности, локальные + границы и основные потоки данных. +- [Глоссарий домена](domain-glossary.md) — канонические значения терминов. +- [ADR](../adr/README.md) — долгоживущие решения и их последствия. diff --git a/architecture/domain-glossary.md b/architecture/domain-glossary.md new file mode 100644 index 0000000..64fd216 --- /dev/null +++ b/architecture/domain-glossary.md @@ -0,0 +1,119 @@ +# Глоссарий домена + +## T-Bank connection + +Единственное принадлежащее User подключение к T-Bank Invest, содержащее +зашифрованный token для server-side запросов. + +_Не использовать_: аккаунт T-Bank, авторизация T-Bank. + +## Brokerage Account + +Открытый поддерживаемый инвестиционный счёт T-Bank, доступный через T-Bank +connection: брокерский счёт, ИИС, Инвесткопилка или фонд денежного рынка. + +_Не использовать_: счёт T-Bank, банковский счёт. + +## Investment Account Type + +Нормализованная категория Brokerage Account, независимая от upstream enum +T-Bank. + +_Не использовать_: тип аккаунта T-Bank, protobuf type. + +## Portfolio + +Текущее нормализованное состояние инвестиций одного Brokerage Account, +включающее позиции, доступные денежные остатки и итоги по каждой валюте. + +_Не использовать_: общий портфель, баланс счёта. + +## Portfolio Position + +Ненулевой обычный инвестиционный актив в составе Portfolio с количеством и +доступными оценочными показателями. + +_Не использовать_: валютная позиция, виртуальная позиция. + +## Instrument + +Ценная бумага или иной торгуемый актив T-Bank, на который ссылается Portfolio +Position через FIGI или instrument UID. Metadata Instrument обогащают +отображение Position, но не определяют количество или стоимость. + +_Не использовать_: бумага, тикер, актив позиции. + +## Cash Balance + +Ненулевой доступный денежный остаток Portfolio в одной валюте; не является +Portfolio Position. + +_Не использовать_: валютная позиция, заблокированные средства. + +## Portfolio Snapshot + +Полный успешно полученный состав Portfolio на определённый момент времени, +который может быть свежим или устаревшим fallback-представлением. + +_Не использовать_: история портфеля, snapshot для графика. + +## Client Cached Snapshot + +Последний успешно полученный frontend-ответ React Query, который остаётся +видимым во время фонового обновления или после его ошибки. Не является +server-side fallback и не использует поле `Portfolio.isStale`. + +_Не использовать_: устаревший Portfolio Snapshot, fallback портфеля. + +## Account Ledger + +Упорядоченная read-only история исполненных операций одного Brokerage Account, +отражающая фактические денежные и инструментальные движения без рыночной +переоценки или расчёта остатка после операции. + +_Не использовать_: история Portfolio, баланс счёта. + +## Ledger Entry + +Нормализованное представление одной операции T-Bank в Account Ledger; хранит +исходные суммы и доступные детали операции, не создавая синтетических записей +для комиссии сделки. + +_Не использовать_: проводка, рассчитанная операция. + +## Ledger Page Cache + +Кэш страницы Account Ledger, отделённый от кэша отдельных операций. + +_Не использовать_: operations cache, кэш отдельных операций. + +## Ledger Page Cache Key + +Каноническая идентичность страницы Ledger Page Cache: User, Brokerage Account, +нормализованные диапазон и categories, opaque upstream cursor и limit. + +_Не использовать_: сырой URL query, ключ отдельной Ledger Entry. + +## Concurrent Refresh Attempt + +Два или более перекрывающихся запроса с одним ещё действительным refresh token +одной Session; один становится Refresh Winner, остальные — Refresh Loser и +получают `401`, не отзывая Session победителя. + +_Не использовать_: повторное использование refresh token, последовательный +refresh. + +## Superseded Refresh Token + +Refresh token, который перестал быть действительным после успешной ротации +Session; его предъявление получает `401`, но само по себе не отзывает Session. + +_Не использовать_: активный refresh token, признак взлома сессии. + +## Future Cash Event + +Прогнозируемое на ближайший период денежное или связанное корпоративное +событие одного Brokerage Account, рассчитанное по текущей Portfolio Position; +не является исполненной Ledger Entry. + +_Не использовать_: будущая операция, гарантированная выплата. diff --git a/architecture/system-overview.md b/architecture/system-overview.md new file mode 100644 index 0000000..6a5f2f7 --- /dev/null +++ b/architecture/system-overview.md @@ -0,0 +1,72 @@ +# Обзор системы + +WealthLedger — локальное приложение для просмотра инвестиционных данных через +безопасное server-side подключение к T-Bank Invest. + +## Поставленные возможности + +- [Аутентификация](../features/authentication.md): access/refresh-сессии и + защищённые маршруты. +- [Подключение T-Bank](../features/tbank-connection.md): один зашифрованный + token на User. +- [Брокерские счета](../features/brokerage-accounts.md), + [Portfolio](../features/portfolio-view.md), + [Account Ledger](../features/brokerage-account-ledger.md) и + [Future Cash Events](../features/brokerage-account-upcoming-events.md). +- [Frontend UI resilience](../features/frontend-ui-resilience.md): явные + loading, error, empty и cached-snapshot состояния. + +Термины этих возможностей определяет [глоссарий домена](domain-glossary.md), а +причины ключевых технических решений — [ADR](../adr/README.md). + +## Локальные границы + +- Frontend — React-клиент; он не вызывает T-Bank напрямую. +- Backend — NestJS API `/api/v1`, владеющий auth, SQLite-данными и T-Bank + gRPC boundary. +- OpenAPI backend — единственный контракт frontend; committed client + генерируется штатным workflow. +- Поставка остаётся локальной: deployment, Docker, CI/CD и облачные сервисы не + входят в текущие границы. +- Snapshot Portfolio и кэш Ledger существуют только в runtime-памяти; система + не предоставляет историческое хранилище портфеля. + +## Поток auth/session + +```mermaid +sequenceDiagram + participant U as User + participant F as Frontend + participant B as Backend + participant D as SQLite + U->>F: Регистрация или вход + F->>B: POST /api/v1/auth/* + B->>D: Создаёт User и Session + B-->>F: Access token + refresh cookie + F->>B: Защищённый запрос с Bearer token + B-->>F: Данные или Problem Details + F->>B: POST /api/v1/auth/refresh при истечении access token + B->>D: Ротирует refresh token + B-->>F: Новый access token + refresh cookie +``` + +## Поток инвестиционных данных + +```mermaid +sequenceDiagram + participant U as User + participant F as Frontend + participant B as Backend + participant D as SQLite + participant T as T-Bank gRPC + U->>F: Открывает счета или Portfolio + F->>B: GET /api/v1/brokerage-accounts/* (Bearer) + B->>D: Читает T-Bank connection + B->>T: Запрос через server-side gRPC boundary + T-->>B: Upstream-данные + B-->>F: Нормализованные данные или Problem Details + F-->>U: Экран с данными или локальным состоянием ошибки +``` + +Подробный статус локальных gates и deferred Prisma e2e находится в +[audit remediation registry](../development/audit-remediation.md). diff --git a/development/README.md b/development/README.md index 668c644..0950ad8 100644 --- a/development/README.md +++ b/development/README.md @@ -1,7 +1,11 @@ -# Development +# Разработка Здесь размещаются устойчивые правила разработки, локальной проверки и поставки. Требования и планы изменений остаются в `../specs/openspec`. +- [Frontend workflow](frontend-workflow.md) — Node 22, локальный запуск и + frontend verification. +- [Backend workflow](backend-workflow.md) — bootstrap backend, локальные + non-mutating gates и границы e2e. - [Audit remediation registry](audit-remediation.md) — текущий статус findings, следующий OpenSpec change и evidence завершённых улучшений. - [OpenSpec authority и lifecycle](openspec-authority-and-lifecycle.md) — единственный SDD store, границы постоянной документации и условия archive. - [Проверка runtime OpenAPI](runtime-openapi-verification.md) — отдельный read-only contract gate и его границы. diff --git a/development/backend-workflow.md b/development/backend-workflow.md new file mode 100644 index 0000000..8f6e5b6 --- /dev/null +++ b/development/backend-workflow.md @@ -0,0 +1,35 @@ +# Рабочий процесс backend + +## Предварительные условия + +Backend требует Node 22. Создайте `backend/.env` на основе +`backend/.env.example` и замените placeholder-значения `JWT_SECRET` и +`TOKEN_ENCRYPTION_KEY`; token T-Bank в этот файл не записывается. + +```bash +cd backend +cp .env.example .env +npm ci +``` + +## Локальная проверка + +Следующие команды не изменяют versioned-файлы: + +```bash +npm run lint +npm run typecheck:app +npm run typecheck:test +npm test -- --runInBand +``` + +`npm run lint:fix` — явная записывающая команда. Runtime OpenAPI сверяется с +frontend snapshot из workspace: + +```bash +bash specs/scripts/verify-runtime-openapi.sh +``` + +`npm run test:e2e` временно не является passing gate: Prisma schema engine +останавливает migration до Jest. Точная диагностика и trigger пересмотра +зафиксированы в [audit remediation registry](audit-remediation.md). diff --git a/features/README.md b/features/README.md index 3c40fb8..224b168 100644 --- a/features/README.md +++ b/features/README.md @@ -1,3 +1,14 @@ -# Features +# Возможности Здесь документируется фактическое поведение завершенных пользовательских фич: пользовательский поток, API-сценарий и ограничения. Не реализованные возможности сюда не добавляются. + +- [Аутентификация](authentication.md) — регистрация, сессии и refresh. +- [Подключение T-Bank](tbank-connection.md) — безопасное управление + подключением. +- [Брокерские счета](brokerage-accounts.md) — список поддерживаемых счетов. +- [Портфель](portfolio-view.md) — состав инвестиций и freshness states. +- [Account Ledger](brokerage-account-ledger.md) — исполненные операции счёта. +- [Future Cash Events](brokerage-account-upcoming-events.md) — прогнозируемые + события по текущим позициям. +- [Frontend UI resilience](frontend-ui-resilience.md) — устойчивые состояния + интерфейса. diff --git a/features/tbank-connection.md b/features/tbank-connection.md index 971d2d3..bdc2b5c 100644 --- a/features/tbank-connection.md +++ b/features/tbank-connection.md @@ -24,7 +24,7 @@ sequenceDiagram `INVALID_TBANK_TOKEN` означает, что T-Bank отклонил аутентификацию или права доступа. `TBANK_UNAVAILABLE` означает временную ошибку, превышение лимита, ожидание в очереди или другую ошибку внешнего сервиса. -## Configuration and limits +## Конфигурация и ограничения - `TOKEN_ENCRYPTION_KEY` - закодированный в base64 32-байтный ключ AES-256-GCM; обязателен при запуске. - `TBANK_GRPC_ENDPOINT` по умолчанию имеет значение `invest-public-api.tbank.ru:443`. @@ -35,6 +35,12 @@ FIFO-ограничитель на экземпляр запускает не б Портфель с большим числом ранее не кэшированных инструментов создаёт отдельные metadata lookup. Если в такой ситуации очередь переполняется и позиции получают FIGI вместо names, увеличьте `TBANK_RATE_LIMIT_PER_SECOND` в соответствии с разрешённой upstream-квотой; для локального портфеля из множества позиций использовалось значение `50`. -## MVP limits +## Ограничения -Реализована только проверка токена. Счета, портфели, кэш метаданных инструментов, ротация ключа, распределенное ограничение запросов и сериализация параллельного обновления подключения отложены. Вендорные protobuf-контракты взяты из `references/moex-vibe`; при необходимости обновляйте их вручную по официальной документации T-Bank Invest API. +Подключение используется поставленными сценариями Brokerage Accounts, +Portfolio, Account Ledger и Future Cash Events; frontend не обращается к +T-Bank напрямую. Token encryption key не ротируется, rate limit остаётся +per-instance, а обновление connection не сериализуется между распределёнными +экземплярами backend. Вендорные protobuf-контракты взяты из +`references/moex-vibe`; при необходимости обновляйте их вручную по официальной +документации T-Bank Invest API.