docs: make project documentation self-contained

This commit is contained in:
2026-09-12 13:09:37 +03:00
parent aa9afc15e4
commit 3a99af1a6e
36 changed files with 1006 additions and 703 deletions
+9
View File
@@ -1,5 +1,14 @@
{ {
"config": { "config": {
"MD013": false "MD013": false
},
"customRules": ["markdownlint-rule-relative-links"],
"overrides": [
{
// Immutable audit/evidence records keep original source paths, outside the standalone book.
"filter": ["audits/**", "development/completion-evidence.md", "development/audit-remediation.md"],
"config": { "relative-links": false },
"combine": "merge"
} }
]
} }
+11 -14
View File
@@ -1,17 +1,14 @@
# Docs # Документация WealthLedger
Здесь живут as-built факты, проектные решения и общие engineering conventions. Этот репозиторий описывает устройство системы, пользовательские возможности,
Работай напрямую по задаче пользователя; обязательного SDD lifecycle нет. ограничения и причины принятых решений. [Главная страница](README.md) и
[тематический индекс](AI_INDEX.md) дают вход в эти знания без доступа к исходникам.
Для поиска проектного источника начни с [AI_INDEX](AI_INDEX.md). Если задача [Правила ведения документации](development/documentation.md) определяют
уже указывает точный документ (например, правка опечатки), читай его напрямую. ответственность за актуальность, сохранение смысла и проверку самостоятельности.
Architecture/features описывают текущее поведение, Архитектурные решения сохраняются в ADR; аудиты и датированные свидетельства
ADR сохраняют rationale; audits и прежнее Completion Evidence — история. остаются историческими материалами и не заменяют описание текущего поведения.
Изменение утверждённого архитектурного решения фиксируется successor ADR,
но не требует создания OpenSpec artifacts.
Перед Node-командами: `nvm use --silent`. После `npm ci` запускай Локальные проверки документации охватывают Markdown, относительные ссылки
`npm run verify` (Markdown lint + `git diff --check`, без auto-fix). и якоря; смысловая согласованность проверяется отдельно. Синтаксическая
Проверь links/anchors затронутых документов отдельно. Новые документы должны проверка не гарантирует актуальность архитектурного или функционального факта.
быть доступны через соответствующий index. Runtime facts сверяй с code/config;
не копируй framework docs, secrets или один контракт в несколько owners.
+46 -41
View File
@@ -1,78 +1,83 @@
# Project knowledge index # Project knowledge index
Открывай раздел по конкретному вопросу, затем только нужный source. Карта проектных знаний по вопросам читателя. Все необходимые объяснения
Это карта фактов, не обязательное чтение всего docs. Текущий wire contract находятся внутри документации. Для первого знакомства откройте
генерируется backend; устаревший ADR/пример кода не переопределяет его. [главную страницу](README.md), для конкретного сценария —
[карту возможностей](features/README.md).
## HTTP API ## HTTP API
| Trigger | Source | | Вопрос | Документ |
| --- | --- | | --- | --- |
| Endpoint, DTO, validation, status, exception/error shape | [HTTP semantics](architecture/http-api.md) | | Как API проверяет запрос, подтверждает доступ и сообщает об ошибках? | [HTTP semantics](architecture/http-api.md) |
| Изменение публичного wire contract, snapshot/generated types | [Contract workflow](development/runtime-openapi-verification.md) | | Как сохраняется согласованность публичного контракта frontend и backend? | [Contract workflow](development/runtime-openapi-verification.md) |
| Почему Nest — источник OpenAPI | [ADR 002](adr/002-nest-generated-openapi-contract.md) | | Почему backend является источником OpenAPI? | [ADR 002](adr/002-nest-generated-openapi-contract.md) |
## Security ## Security
| Trigger | Source | | Вопрос | Документ |
| --- | --- | | --- | --- |
| Login/session/logout, cookie/access storage | [Authentication](features/authentication.md) | | Как работают вход, восстановление сессии, cookie и logout? | [Authentication](features/authentication.md) |
| Concurrent/superseded refresh | [ADR 009](adr/009-concurrent-refresh-rotation-policy.md), [термины](architecture/domain-glossary.md#concurrent-refresh-attempt) | | Что происходит при конкурентном refresh или повторном предъявлении заменённого token? | [ADR 009](adr/009-concurrent-refresh-rotation-policy.md), [термины](architecture/domain-glossary.md#concurrent-refresh-attempt) |
| T-Bank token encryption, connection generation/cache | [Connection](features/tbank-connection.md), [ADR 004](adr/004-encrypted-per-user-tbank-token.md) | | Как защищён T-Bank token и что меняется при замене подключения? | [Connection](features/tbank-connection.md), [ADR 004](adr/004-encrypted-per-user-tbank-token.md) |
## Data ## Data
Persistence source — [Prisma schema](../backend/prisma/schema.prisma), Локальная база хранит пользователей, сессии и зашифрованные подключения.
[migrations](../backend/prisma/migrations/). [ADR 001](adr/001-prisma-sqlite-local-mvp.md) Брокерские счета и финансовые снимки не сохраняются как долговременная история.
описывает исторический выбор SQLite, не текущий список таблиц. Принадлежность данных и границы хранения описаны в
Transaction/ownership/migration decisions — [backend data guide](../backend/.ai/conventions/backend-data.md). [обзоре системы](architecture/system-overview.md#хранимые-данные).
Причина выбора локальной базы — [ADR 001](adr/001-prisma-sqlite-local-mvp.md).
Точность и валюты — [Money](architecture/domain-glossary.md#money).
Сохранение данных при обновлении — [локальное окружение](development/local-workspace.md#подготовка-backend).
## Integrations ## Integrations
T-Bank/proto/TLS/retry/limits — [connection configuration](features/tbank-connection.md#конфигурация-и-ограничения) T-Bank, TLS, повторные попытки и лимиты —
[ограничения подключения](features/tbank-connection.md#конфигурация-и-ограничения)
и [server-side boundary](adr/003-tbank-server-side-grpc-boundary.md). и [server-side boundary](adr/003-tbank-server-side-grpc-boundary.md).
Cache generation — [connection cache](features/tbank-connection.md#граница-финансового-кэша). MOEX/НКР и происхождение показателей — [облигации](features/bond-detail.md#содержание)
Logging — [ADR 008](adr/008-pino-backend-logging.md) и и [внешние системы](architecture/system-overview.md#контекст-системы).
[реальный logger boundary](../backend/.ai/conventions/backend-architecture.md#logging). Смена подключения и финансовый кэш — [connection cache](features/tbank-connection.md#граница-финансового-кэша).
Логирование выполняется на инфраструктурной границе backend: события связываются
с HTTP-запросом, а секреты и полные запросы не попадают в журнал.
Принятые ограничения описаны в [ADR 008](adr/008-pino-backend-logging.md).
## Domain ## Domain
Начинай с точного термина в [глоссарии](architecture/domain-glossary.md): Определения и различия понятий — в [глоссарии](architecture/domain-glossary.md):
[Money](architecture/domain-glossary.md#money), [Money](architecture/domain-glossary.md#money),
[Portfolio Position](architecture/domain-glossary.md#portfolio-position), [Portfolio Position](architecture/domain-glossary.md#portfolio-position),
[Client Cached Snapshot](architecture/domain-glossary.md#client-cached-snapshot), [Client Cached Snapshot](architecture/domain-glossary.md#client-cached-snapshot),
[Ledger](architecture/domain-glossary.md#account-ledger), [Ledger](architecture/domain-glossary.md#account-ledger),
[Realized Income](architecture/domain-glossary.md#realized-income-analytics). [Realized Income](architecture/domain-glossary.md#realized-income-analytics).
Cross-area topology — [system overview](architecture/system-overview.md). Связи понятий и подсистем — [system overview](architecture/system-overview.md#предметная-модель).
## Features ## Features
| Предмет задачи | Current behavior | | Предмет вопроса | Текущее поведение |
| --- | --- | | --- | --- |
| Счета и их сводки | [Brokerage accounts](features/brokerage-accounts.md) | | Счета и сводки | [Brokerage accounts](features/brokerage-accounts.md) |
| Общий портфель и разделы по всем счетам | [All accounts](features/all-accounts.md) | | Общий портфель и разделы по всем счетам | [All accounts](features/all-accounts.md) |
| Страница акции, поиск, график и история одной бумаги | [Stock detail](features/stock-detail.md) | | Поиск, график и история акции | [Stock detail](features/stock-detail.md) |
| Страница облигации, поиск, купоны и переходы из портфеля | [Bond detail](features/bond-detail.md) | | Поиск, купоны и показатели облигации | [Bond detail](features/bond-detail.md) |
| Источники кредитных рейтингов облигаций | [Исследование источников](research/bond-credit-ratings.md) | | Покрытие источников кредитных рейтингов | [Исследование источников](research/bond-credit-ratings.md) |
| Позиции/суммы портфеля, backend cache | [Portfolio](features/portfolio-view.md) | | Позиции, суммы портфеля и свежесть данных | [Portfolio](features/portfolio-view.md) |
| Операции/фильтры/cursor | [Account ledger](features/brokerage-account-ledger.md) | | Операции, фильтры и пагинация | [Account ledger](features/brokerage-account-ledger.md) |
| Будущие выплаты | [Upcoming events](features/brokerage-account-upcoming-events.md) | | Будущие выплаты | [Upcoming events](features/brokerage-account-upcoming-events.md) |
| Реализованные доходы/периоды | [Income analytics](features/brokerage-account-income-analytics.md) | | Реализованные доходы и периоды | [Income analytics](features/brokerage-account-income-analytics.md) |
| UI recovery/error boundaries | [Frontend resilience](features/frontend-ui-resilience.md) | | Восстановление после ошибок интерфейса | [Frontend resilience](features/frontend-ui-resilience.md) |
Behavioral requirements при необходимости — через [specs index](../specs/AI_INDEX.md).
Это не запускает SDD lifecycle.
## UI ## UI
Theme/tokens/reuse — [design system](development/design-system.md). Визуальные роли и общие состояния — [дизайн-система](development/design-system.md).
Существующие components/stories и ограничения — [UI catalog](development/ui-catalog.md). Примеры экранов и ограничения их проверки — [UI-каталог](development/ui-catalog.md).
Query freshness/focus policy — [ADR 007](adr/007-react-query-fetch-policy.md). Обновление данных при возвращении в окно — [ADR 007](adr/007-react-query-fetch-policy.md).
## Engineering ## Engineering
Scope/abstraction/dependency/testing — [общие правила](development/engineering-governance.md). Принципы изменения и проверки системы — [engineering governance](development/engineering-governance.md).
Routing/recovery — [context discipline](development/agent-token-efficiency.md). Ведение знаний и разрешение расхождений — [документация](development/documentation.md).
Commands — [frontend](development/frontend-workflow.md) / [backend](development/backend-workflow.md). Локальное окружение и диагностика — [local workspace](development/local-workspace.md).
Известный Audit ID — найденная запись [registry](development/audit-remediation.md); Служебные материалы сопровождения и исторические результаты отделены от
читай её evidence только для этого вопроса. Historical audits не являются live rules. описания системы в [разделе разработки](development/README.md).
+59
View File
@@ -0,0 +1,59 @@
# WealthLedger: документация проекта
WealthLedger помогает частному инвестору понимать состав портфеля, исполненные
операции, полученные доходы и будущие выплаты по счетам T-Bank. Приложение
работает локально; данные брокера получает backend. Здесь объясняются понятия,
границы системы, ключевые сценарии и причины решений.
## Первое знакомство
1. Прочитайте [обзор системы](architecture/system-overview.md): пользователи,
внешние системы, исполняемые части и движение данных.
2. Откройте [карту возможностей](features/README.md): выберите предметную
область и найдите её поведение, ограничения и ответственность компонентов.
3. Сверяйте незнакомые понятия с [глоссарием](architecture/domain-glossary.md).
Особенно важны различия между User, Session, подключением и брокерским
счётом, между полученным доходом и прогнозом выплаты.
4. Изучите [локальное окружение](development/local-workspace.md) и один
сценарий: вход → подключение T-Bank → счёт → портфель → доходы или события.
После этого вы должны уметь объяснить, откуда приходит финансовое значение,
какой компонент им владеет и где проверить его ограничения.
## Найти ответ по задаче
| Вопрос | Начать здесь |
| --- | --- |
| Как части проекта работают вместе? | [Обзор и схемы](architecture/system-overview.md) |
| Как устроена конкретная возможность и кто за неё отвечает? | [Карта возможностей](features/README.md) |
| Что означает термин или финансовый показатель? | [Глоссарий](architecture/domain-glossary.md) |
| Почему выбраны эти архитектурные ограничения? | [Решения ADR](adr/README.md) |
| Какие условия нужны для работы и как понимать локальные ошибки? | [Окружение и восстановление](development/local-workspace.md) |
| Где точные API-типы, статусы и правила изменения контракта? | [HTTP API](architecture/http-api.md) и [генерация контракта](development/runtime-openapi-verification.md) |
| Как проверяются согласованность и качество системы? | [Сопровождение](development/README.md) |
| Как обновлять знания вместе с кодом? | [Ведение документации](development/documentation.md) |
[Тематический индекс](AI_INDEX.md) связывает вопросы с соответствующими
объяснениями. Читать весь репозиторий для ответа на один вопрос не требуется.
## Какому источнику доверять
Документация объясняет текущее устройство и поведение системы. Точный публичный
HTTP-контракт публикует backend в OpenAPI; ADR сохраняют причины принятых решений.
Требования и предложения изменений могут отдельно согласовываться в OpenSpec,
но для понимания описанных сценариев доступ к нему не нужен.
[Карта владельцев фактов](development/documentation.md#владельцы-фактов)
помогает различать назначение этих знаний и разрешать расхождения.
Текущие объяснения находятся в `architecture` и `features`; руководства — в
`development`. [Исследования](research/README.md) содержат проверенные наблюдения
и варианты, [аудиты](audits/README.md) — исторические свидетельства.
Наличие рекомендации в исследовании или требования в старом change ещё не
означает, что возможность реализована. Статус каждого ADR указан в
[журнале решений](adr/README.md).
Основная документация читается самостоятельно, без исходников других
репозиториев. [Исторические свидетельства](development/README.md#исторические-свидетельства)
сохраняют исходные технические адреса своего времени и не являются
обязательным чтением для понимания системы. Документы используют Markdown,
схемы — Mermaid.
+7
View File
@@ -13,3 +13,10 @@ Backend нуждается в структурированных JSON-логах
## Consequences ## Consequences
Backend получает новую production dependency и использует Pino API для application-событий. Collector, retention и alerting остаются за пределами приложения. Backend получает новую production dependency и использует Pino API для application-событий. Collector, retention и alerting остаются за пределами приложения.
Логирование сосредоточено на инфраструктурной границе backend и связывает
события с контекстом HTTP-запроса. В журнал передаются стабильное имя события
и разрешённые скалярные поля. Неизвестные поля отбрасываются; полные запросы,
тела ответов и секреты не являются допустимыми данными журнала. Фильтрация
полей не защищает секрет, помещённый в свободный текст сообщения или stack:
содержимое таких сообщений требует отдельной проверки.
@@ -14,15 +14,15 @@ SDD lifecycle. Native OpenSpec остаётся optional инструменто
[ADR 011](011-native-openspec-lifecycle-with-repository-gates.md) заменяет части [ADR 011](011-native-openspec-lifecycle-with-repository-gates.md) заменяет части
этого решения о schema v3, custom verifiers и dispatcher. Выбор native OpenSpec, этого решения о schema v3, custom verifiers и dispatcher. Выбор native OpenSpec,
независимых repository gates и исходное rationale сохраняются. Ниже оставлено независимых repository gates и исходное rationale сохраняются. Ниже оставлено
историческое описание прежней реализации; действующую процедуру определяют историческое описание прежней реализации; текущие принципы работы описаны
authorities, указанные в ADR 011. Version contract этим уточнением не меняется. в ADR 012. Version contract этим уточнением не меняется.
## Исторический контекст и решение ## Исторический контекст и решение
WealthLedger использует project-local schema `wealth-ledger-sdd` version 3 с На момент решения WealthLedger использовал проектную schema `wealth-ledger-sdd` version 3 с
последовательностью `Proposal → Delta Specs → Design → Tasks → Apply`, один exact последовательностью `Proposal → Delta Specs → Design → Tasks → Apply`, один exact
OpenSpec CLI contract из `.cli-version` и тонкие non-mutating verifiers. Это OpenSpec CLI contract и проверки без изменения проверяемых материалов. Это
сохраняет один lifecycle authority в `specs/openspec`, выражает approval сохраняло единый источник правил жизненного цикла в хранилище спецификаций, выражало approval
boundaries нативными extension points и не вводит второй workflow engine или boundaries нативными extension points и не вводит второй workflow engine или
monorepo control plane для четырёх независимых Git-репозиториев. monorepo control plane для четырёх независимых Git-репозиториев.
@@ -39,8 +39,8 @@ monorepo control plane для четырёх независимых Git-репо
OpenSpec CLI закреплён exact version contract; новая stable release не включает OpenSpec CLI закреплён exact version contract; новая stable release не включает
auto-upgrade, а смена contract является отдельным SDD change со staged auto-upgrade, а смена contract является отдельным SDD change со staged
compatibility fixtures. Root `.opencode` остаётся локальным non-authority проверками совместимости. Локальная точка входа оставалась навигацией,
entrypoint, а hard constraints проверяются через authority verifier. Planning а обязательные ограничения проверялись отдельно. Planning
verifier до approval проверяет только детерминированную структуру: capability verifier до approval проверяет только детерминированную структуру: capability
set, Delivery Map, rollback, task area и Requirement coverage. Он различает set, Delivery Map, rollback, task area и Requirement coverage. Он различает
artifact-validation, неверный invocation и failure окружения, чтобы agent не artifact-validation, неверный invocation и failure окружения, чтобы agent не
@@ -13,40 +13,46 @@ status: superseded
[ADR 010](010-native-openspec-sdd-and-local-orchestration.md) сохраняет rationale [ADR 010](010-native-openspec-sdd-and-local-orchestration.md) сохраняет rationale
выбора native OpenSpec и независимых repository gates. Его описание schema v3, выбора native OpenSpec и независимых repository gates. Его описание schema v3,
custom authority/planning verifiers и dispatcher относится к прежней реализации. custom authority/planning verifiers и dispatcher относится к прежней реализации.
Текущая project-local [schema wealth-ledger-sdd](../../specs/openspec/schemas/wealth-ledger-sdd/schema.yaml) На момент решения проектная schema `wealth-ledger-sdd`
имеет version 5 и граф `Proposal → Delta Specs → Design → Tasks → Apply`. имела version 5 и граф `Proposal → Delta Specs → Design → Tasks → Apply`.
Native instructions и общие authorities уже определяют действующую процедуру; Порядок работы определялся средствами OpenSpec и общими правилами проекта;
optional entrypoints направляют к ней. дополнительные точки входа служили навигацией по единой процедуре.
Отслеживаемый [`.cli-version`](../../specs/openspec/.cli-version) существует и Версия OpenSpec CLI была зафиксирована как `1.10.0`.
содержит `1.10.0`. Версия schema и CLI contract — разные сведения. Наличие этого Версия schema и CLI contract — разные сведения. Фиксация версии
файла не доказывает запуск прежних verifiers; это решение не меняет CLI pin, не доказывает запуск прежних проверок; это решение не меняет CLI pin,
version contract, schema или требования к tooling. version contract, schema или требования к tooling.
## Решение ## Решение
Частично заменить ADR 010 в части прежней schema version и orchestration. Частично заменить ADR 010 в части прежней schema version и orchestration.
Canonical SDD store остаётся `wealth-ledger-specs` в `specs/openspec`. Единым хранилищем согласования оставался `wealth-ledger-specs`.
Действующая процедура принадлежит существующим owners: Процедура разделяла следующие ответственности:
- [Engineering governance](../development/engineering-governance.md) — роли, отдельные approvals, Delivery Map, обязательность проверок и completion safeguards. - Согласование ролей, подтверждений, объёма поставки и обязательных проверок.
- [OpenSpec lifecycle](../development/openspec-authority-and-lifecycle.md) — native planning/apply, strict structural validation, semantic sync и completion procedure. - Планирование и применение изменений, структурную проверку и смысловое согласование требований.
- [Context discipline](../development/agent-token-efficiency.md) — выбор и повторная сверка inputs, fingerprints, recovery и evidence reuse. - Выбор и повторную сверку исходных знаний, восстановление контекста и повторное использование свидетельств.
Native OpenSpec получает structure и contextFiles текущего artifact или Apply. Native OpenSpec получает structure и contextFiles текущего artifact или Apply.
Каждый affected repository владеет своим gate через local AGENTS. Выполнение Каждое изменяемое приложение владело своей проверкой качества. Выполнение
процедуры не зависит от custom verifier, dispatcher, skill или session adapter; процедуры не зависит от custom verifier, dispatcher, skill или session adapter;
optional entrypoints остаются navigation, без второго workflow engine. optional entrypoints остаются navigation, без второго workflow engine.
## Последствия ## Последствия
Агент и человек используют один актуальный lifecycle. Отдельный порядок команд Люди и автоматизированные инструменты следовали единой процедуре, а каждый
в ADR не поддерживается: точные команды находятся у lifecycle и repository репозиторий отвечал за собственные проверки. ADR фиксировал причину этого
owners. Исходные альтернативы и rationale остаются в ADR 010; historical разделения ответственности. Исходные альтернативы и rationale остаются
archives, audits и evidence сохраняют описание своего времени. в ADR 010; historical archives, audits и evidence сохраняют описание своего времени.
Structural success не подтверждает решение Change Owner, semantic compliance Structural success не подтверждает решение Change Owner, semantic compliance
или независимый PASS. Approval bindings, полный применимый context/Design или независимый PASS. Approval bindings, полный применимый context/Design
package, независимая Validation и отдельная Finalization сохраняются. package, независимая Validation и отдельная Finalization сохраняются.
Замена orchestration не ослабляет gates и не разрешает изменение незатронутых Замена orchestration не ослабляет gates и не разрешает изменение незатронутых
main-spec requirements о tooling. main-spec requirements о tooling.
Текущие принципы сопровождения описаны в
[engineering governance](../development/engineering-governance.md), роль
сохранённых требований — в [OpenSpec](../development/openspec-authority-and-lifecycle.md).
Эти материалы отражают последующее упрощение процесса, а не восстанавливают
обязательность исторической процедуры.
@@ -11,10 +11,10 @@
## Decision ## Decision
Задача пользователя определяет результат. Root/local AGENTS задают короткий Задача пользователя определяет результат. Рабочий контекст выбирается по
рабочий процесс; CONVENTIONS выбирает темы; SOURCES и docs/specs indexes указывают предмету изменения: локальные правила разработки отделены от проектных фактов,
точных владельцев фактов. Frontend/backend guides содержат решения, безопасные а тематические источники объясняют соответствующую область. Общие принципы
пути, exceptions и реальные примеры. Общие rules и contract workflow не дублируются. и согласование контракта не дублируются в независимых описаниях.
[Engineering governance](../development/engineering-governance.md) и [Engineering governance](../development/engineering-governance.md) и
[context discipline](../development/agent-token-efficiency.md) описывают текущую [context discipline](../development/agent-token-efficiency.md) описывают текущую
@@ -28,6 +28,6 @@
операции. Правильность routing проверяется на задачах и counterexamples; качество операции. Правильность routing проверяется на задачах и counterexamples; качество
агента оценивается по наблюдаемым outcomes, а не по числу файлов или checkboxes. агента оценивается по наблюдаемым outcomes, а не по числу файлов или checkboxes.
Имена файлов и примерные context budgets из исследований адаптируются к Рекомендации исследований адаптируются к принятой архитектуре и существующим
WealthLedger: FSD без widgets, Nest как OpenAPI source, существующие локальные проверкам WealthLedger. Backend остаётся источником OpenAPI. Новые сервисы,
gates. Новые services, CI и checkers ради context architecture не добавляются. общая инфраструктура и проверки не вводятся только ради организации контекста.
+5 -7
View File
@@ -25,7 +25,7 @@ _Не использовать_: T-Bank сессия, browser session.
неприменимое денежное значение передаётся как `null`, а не как ноль и не как неприменимое денежное значение передаётся как `null`, а не как ноль и не как
раздельные `amount`/`currency`. раздельные `amount`/`currency`.
_Не использовать_: number amount, MoneyDto как доменный термин. _Не использовать_: сумма без валюты, транспортное представление как доменный термин.
## T-Bank connection ## T-Bank connection
@@ -138,9 +138,8 @@ _Не использовать_: история Portfolio, баланс счёт
Нормализованное представление одной операции T-Bank в Account Ledger; хранит Нормализованное представление одной операции T-Bank в Account Ledger; хранит
исходные суммы и доступные детали операции, не создавая синтетических записей исходные суммы и доступные детали операции, не создавая синтетических записей
для комиссии сделки. API/code сохраняют legacy names `BrokerageOperationDto`, для комиссии сделки. В публичном API история доступна через маршруты
`BrokerageOperationsPageDto` и `/operations`; в доменной документации primary `/operations`; в доменной документации запись называется Ledger Entry.
term — Ledger Entry.
_Не использовать_: проводка, рассчитанная операция. _Не использовать_: проводка, рассчитанная операция.
@@ -188,9 +187,8 @@ _Не использовать_: активный refresh token, признак
не является исполненной Ledger Entry. Прогнозная сумма события передаётся как не является исполненной Ledger Entry. Прогнозная сумма события передаётся как
nullable Money: известные coupon, dividend и maturity содержат Money в исходной nullable Money: известные coupon, dividend и maturity содержат Money в исходной
валюте, offer или неизвестные денежные условия передаются как `null`. валюте, offer или неизвестные денежные условия передаются как `null`.
API/code сохраняют legacy names `UpcomingEventDto`, `UpcomingEventsDto`, В публичном API прогноз доступен через маршруты `/events`; в доменной
`/events` и frontend `BrokerageEvent`; в доменной документации primary term — документации событие называется Future Cash Event.
Future Cash Event.
_Не использовать_: будущая операция, гарантированная выплата. _Не использовать_: будущая операция, гарантированная выплата.
+27 -26
View File
@@ -2,25 +2,24 @@
## Источник контракта ## Источник контракта
Nest controllers/DTO/Swagger metadata генерируют runtime OpenAPI. Настройка Backend генерирует OpenAPI из собственного публичного API. Frontend использует
приложения — [configureApp](../../backend/src/main.ts); frontend хранит derived производное описание контракта и типизированный клиент. Изменения проходят
snapshot и generated TS. Изменения проходят
[contract workflow](../development/runtime-openapi-verification.md). [contract workflow](../development/runtime-openapi-verification.md).
API имеет prefix `/api/v1`; Swagger публикуется для локальной разработки по API имеет prefix `/api/v1`; Swagger публикуется для локальной разработки по
environment policy, а не используется frontend в runtime. environment policy, а не используется frontend в runtime.
## Validation и identity ## Validation и identity
Global ValidationPipe использует `transform: true`, `whitelist: true`. Backend преобразует и проверяет входные данные на общей HTTP-границе.
Unknown properties удаляются, а их rejection не обещается без Неизвестные свойства запроса удаляются; сам факт их передачи не обещает отказа.
`forbidNonWhitelisted`. Runtime DTO validators и преобразования задают допустимый Проверка формата не заменяет бизнес-ограничения и проверку доступа к счёту.
вход; бизнес-ограничения и доступ к account проверяются service/access boundary. Аутентификация подтверждает личность пользователя; accountId из URL сам по себе
Guard подтверждает identity; accountId из URL сам по себе не доказывает доступ. не доказывает доступ к брокерским данным.
## Error contract ## Error contract
Общий [HttpExceptionFilter](../../backend/src/common/filters/http-exception.filter.ts) Общая граница обработки HTTP-ошибок возвращает `application/problem+json`
возвращает `application/problem+json` со `status` и публичным `code`. со `status` и публичным `code`.
Неожиданный exception возвращает `500` / `INTERNAL_SERVER_ERROR`, без исходного Неожиданный exception возвращает `500` / `INTERNAL_SERVER_ERROR`, без исходного
message/stack. Типичный ответ: message/stack. Типичный ответ:
@@ -30,21 +29,22 @@ message/stack. Типичный ответ:
Для input validation контракт требует `400`, `VALIDATION_ERROR` и `errors` как Для input validation контракт требует `400`, `VALIDATION_ERROR` и `errors` как
массив `{ pointer, detail }`, где pointer адресует поле запроса. Это требование массив `{ pointer, detail }`, где pointer адресует поле запроса. Это требование
не считается проверенным одним Swagger snapshot: direct controller call не не считается проверенным одним описанием OpenAPI: нужна проверка реального
запускает ValidationPipe. Текущий filter преобразует `ValidationError.constraints`, HTTP-ответа после всех стадий обработки запроса.
а default Nest pipe формирует массив строк; корректность field details в
configured HTTP pipeline нужно проверять отдельно и не копировать этот seam
как доказанно полный пример.
Документированный DTO — [ProblemDetailsDto](../../backend/src/common/decorators/api-safe-error-response.decorator.ts). **Требует ручной проверки:** согласованность деталей ошибок валидации полей
Filter не добавляет автоматически все поля из RFC (`type`, `title`, `instance`). в полном HTTP-потоке пока не подтверждена. Известно расхождение формата ошибок
Не вводи дополнительные обязательные поля без согласованного изменения wire contract. между стадиями валидации и формирования ответа; наличие описания в OpenAPI
Caller выбирает поведение по public code, не по внутреннему тексту exception. не доказывает передачу ожидаемых `pointer` и `detail`.
Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`).
Добавление обязательного поля является изменением публичного контракта.
Клиент выбирает поведение по публичному коду, а не по внутреннему тексту ошибки.
## Domain errors и auth ## Domain errors и auth
Специальный code задаётся ответственным use case; generic mapping и безопасный Специальный code определяется сценарием; общее преобразование ошибок и безопасная
envelope принадлежат filter. Auth endpoints используют свои status и cookie форма ответа принадлежат HTTP-границе. Auth endpoints используют свои status и cookie
operations: [Authentication](../features/authentication.md). operations: [Authentication](../features/authentication.md).
T-Bank token failure и временная недоступность имеют разные значения: T-Bank token failure и временная недоступность имеют разные значения:
[Connection API](../features/tbank-connection.md#api). Raw upstream/Prisma errors [Connection API](../features/tbank-connection.md#api). Raw upstream/Prisma errors
@@ -52,8 +52,9 @@ T-Bank token failure и временная недоступность имеют
## Граница доказательства ## Граница доказательства
OpenAPI integration проверяет равенство schemas, а не результат каждого request. Сравнение OpenAPI подтверждает согласованность схем, а не результат каждого
Filter unit test проверяет безопасный unexpected error. Для изменения validation, запроса. Безопасная обработка неожиданной ошибки проверяется отдельно.
guard, status/cookie или domain error нужны representative configured HTTP cases; Валидация, доступ, статусы и cookie требуют проверки полного HTTP-потока;
известный e2e environment blocker не превращает отсутствующую проверку в success. атомарность хранения — проверки с изолированной реальной базой.
Выбор уровня и команд — [backend testing](../../backend/.ai/conventions/backend-testing.md). Известное ограничение сквозных проверок описано в
[проверке backend](../development/backend-workflow.md#локальная-проверка).
+113 -22
View File
@@ -1,7 +1,9 @@
# Обзор системы # Обзор системы
WealthLedger — локальное приложение для просмотра инвестиционных данных через WealthLedger — локальное приложение для частного инвестора: оно связывает
безопасное server-side подключение к T-Bank Invest. состав портфеля, операции, полученный доход и будущие выплаты по счетам T-Bank.
Обзор объясняет систему сверху вниз. Детали сценариев и ответственность подсистем находятся
в [карте возможностей](../features/README.md).
## Поставленные возможности ## Поставленные возможности
@@ -18,13 +20,84 @@ WealthLedger — локальное приложение для просмотр
loading, error, empty и cached-snapshot состояния; initial и частичные loading, error, empty и cached-snapshot состояния; initial и частичные
loading-состояния показывают structural skeleton, а уже доступный контекст loading-состояния показывают structural skeleton, а уже доступный контекст
остаётся видимым при фоновом обновлении или действии пользователя. остаётся видимым при фоновом обновлении или действии пользователя.
- [Все счета](../features/all-accounts.md): общий портфель, журнал, доходы
и будущие выплаты с сохранением принадлежности к счетам.
- [Акции](../features/stock-detail.md) и [облигации](../features/bond-detail.md):
поиск, рыночные данные, личная позиция и события конкретного инструмента.
Термины этих возможностей определяет [глоссарий домена](domain-glossary.md), а Термины этих возможностей определяет [глоссарий домена](domain-glossary.md), а
причины ключевых технических решений — [ADR](../adr/README.md). причины ключевых технических решений — [ADR](../adr/README.md).
## Контекст системы
```mermaid
flowchart LR
U[Частный инвестор] -->|Смотрит инвестиции| W[WealthLedger]
W -->|Счета, позиции, операции и рынок| T[T-Bank Invest API]
W -->|Публичные данные облигаций| M[MOEX ISS]
W -->|Рейтинги выпусков| N[Реестр НКР]
W -->|Изображения логотипов| C[T-Bank Brands CDN]
```
T-Bank — источник личных брокерских данных и рынка. MOEX дополняет будущие
события и показатели облигаций, НКР — рейтинг конкретного выпуска. Публичный
рейтинг или календарь выпуска не подтверждает личное зачисление инвестору.
Отсутствие записи и недоступность источника имеют разный смысл; детали —
[Bond detail](../features/bond-detail.md).
## Исполняемые части
На уровне C4 containers здесь два приложения и локальная база. Каталоги
`docs` и `specs` не являются runtime-компонентами.
```mermaid
flowchart LR
subgraph Browser[Браузер пользователя]
F[React SPA]
end
subgraph Local[Локальное окружение]
B[NestJS API]
D[(SQLite)]
B -->|Prisma: identity, sessions, connection| D
end
F -->|HTTP JSON, Bearer и auth cookie| B
B -->|gRPC over TLS, token пользователя| T[T-Bank Invest]
B -->|HTTPS, публичные данные| M[MOEX ISS]
B -->|HTTPS, публичный HTML| N[НКР]
F -->|HTTPS, изображения| C[Brands CDN]
```
- **Frontend** владеет представлением, навигацией и клиентским query cache.
Access token существует в памяти; refresh-cookie обслуживает browser.
- **Backend** владеет identity, проверкой доступа, credentials брокера,
нормализацией, денежными расчётами и upstream-кэшем.
- **SQLite** хранит локального User, sessions и зашифрованное подключение.
- **Интеграции backend** обращаются к T-Bank, MOEX и НКР, сохраняя различия
личных данных брокера и публичных данных выпуска. Браузер получает
изображения логотипов непосредственно из публичного CDN.
Frontend связывает пользовательские экраны навигацией. Backend объединяет
аутентификацию, подключение брокера и инвестиционные сценарии через публичный API.
Согласование адресов и origin — [локальный запуск](../development/local-workspace.md).
## Хранимые данные
Локальная база содержит идентичность пользователя и hash пароля, состояние
его сессий со сроком действия, признаком отзыва и hash текущего refresh token,
а также одно зашифрованное подключение T-Bank с временем подключения.
Сессии и подключение принадлежат конкретному пользователю; plaintext пароля
или broker token не является сохраняемым представлением credentials.
Изменения связанных локальных данных требуют сохранения их целостности.
Ротация сессии атомарно заменяет текущий refresh token: конкурентные запросы
не могут оба выиграть. Сетевой вызов брокера и изменение локальной базы не
образуют одну общую транзакцию. Финансовые наблюдения хранятся в ограниченном
кэше, а не как долговременная история брокерских данных.
## Локальные границы ## Локальные границы
- Frontend — React-клиент; он не вызывает T-Bank напрямую. - Финансовые API-запросы frontend проходят backend; browser не получает
T-Bank credential или gRPC client. Загрузка публичных логотипов — отдельный поток.
- Backend — NestJS API `/api/v1`, владеющий auth, SQLite-данными и T-Bank - Backend — NestJS API `/api/v1`, владеющий auth, SQLite-данными и T-Bank
gRPC boundary. gRPC boundary.
- OpenAPI backend — единственный контракт frontend; committed client - OpenAPI backend — единственный контракт frontend; committed client
@@ -37,24 +110,32 @@ WealthLedger — локальное приложение для просмотр
долговременные агрегаты. Summary списка сохраняет исходные валюты без FX- долговременные агрегаты. Summary списка сохраняет исходные валюты без FX-
агрегации и может быть локально недоступна для отдельного счета. агрегации и может быть локально недоступна для отдельного счета.
## Предметная модель
Локальный [User](domain-glossary.md#user) владеет
[Session](domain-glossary.md#session) и одним
[T-Bank connection](domain-glossary.md#t-bank-connection). Через подключение
backend устанавливает доступ к [Brokerage Accounts](domain-glossary.md#brokerage-account).
Идентификатор счёта из URL сам по себе не подтверждает этот доступ.
[Portfolio](domain-glossary.md#portfolio) отвечает на вопрос «что есть сейчас»,
[Account Ledger](domain-glossary.md#account-ledger) — «что исполнено»,
[Realized Income](domain-glossary.md#realized-income-analytics) — «какие выплаты
уже получены», будущие события — «что ожидается». Эти представления нельзя
подменять друг другом. [Money](domain-glossary.md#money) сохраняет исходную
валюту и точность; неизвестное значение отличается от нуля.
Режим [«Все счета»](../features/all-accounts.md) агрегирует доступный состав,
сохраняя происхождение строк; он не создаёт новый брокерский счёт. Обновление
подключения меняет границу финансовых кэшей; перезапуск backend не восстанавливает
прежние in-memory snapshots и cursor continuations из SQLite.
## Поток auth/session ## Поток auth/session
```mermaid Вход создаёт локальную сессию; последующие запросы используют access token.
sequenceDiagram После перезагрузки SPA восстанавливает сессию через refresh-cookie. Схема,
participant U as User конкурентная ротация, logout и ограничения собраны у одного owner:
participant F as Frontend [Authentication](../features/authentication.md).
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
```
## Поток инвестиционных данных ## Поток инвестиционных данных
@@ -67,12 +148,22 @@ sequenceDiagram
participant T as T-Bank gRPC participant T as T-Bank gRPC
U->>F: Открывает счета или Portfolio U->>F: Открывает счета или Portfolio
F->>B: GET /api/v1/brokerage-accounts/* (Bearer) F->>B: GET /api/v1/brokerage-accounts/* (Bearer)
B->>D: Читает T-Bank connection B->>D: Читает подключение текущего User
B->>T: Проверяет доступ к счёту
B->>T: Запрос через server-side gRPC boundary B->>T: Запрос через server-side gRPC boundary
T-->>B: Upstream-данные T-->>B: Upstream-данные
B-->>F: Нормализованные данные или Problem Details B-->>F: Нормализованные данные или Problem Details
F-->>U: Экран с данными или локальным состоянием ошибки F-->>U: Экран с данными или локальным состоянием ошибки
``` ```
Подробный статус локальных gates и deferred Prisma e2e находится в Схема показывает запрос, которому нужны upstream-данные. Повторные чтения могут
[audit remediation registry](../development/audit-remediation.md). обслуживаться кэшем по правилам конкретной feature. Проверку connection/account
выполняет backend: запрос связывается с пользователем, его текущим подключением
и доступными через него счетами, включая выдачу данных из кэша.
Внешняя ошибка не становится нулевым балансом; допустимость stale snapshot и
локальное восстановление описывает соответствующий сценарий.
Причины server-side границы, шифрования и retry policy —
[ADR 003](../adr/003-tbank-server-side-grpc-boundary.md),
[ADR 004](../adr/004-encrypted-per-user-tbank-token.md) и
[ADR 005](../adr/005-tbank-upstream-resilience.md).
+20 -11
View File
@@ -1,13 +1,22 @@
# Разработка # Сопровождение системы
Работа начинается с задачи пользователя; обязательного OpenSpec/SDD lifecycle нет. Здесь описаны локальное окружение, согласованность компонентов, проверка
Проектные факты выбирайте через [AI_INDEX](../AI_INDEX.md). качества и поддержание проектных знаний. Материалы самостоятельны и не
требуют открытия внутренних файлов приложений.
- [Engineering governance](engineering-governance.md) — выбор решения/dependency, risk-based tests, local gates, UI и опасные изменения. - [Локальное окружение](local-workspace.md) — предпосылки работы, первый пользовательский сценарий и диагностика.
- [Context discipline](agent-token-efficiency.md) — semantic/path routing, sources, recovery и проверка эффективности. - [Frontend](frontend-workflow.md) / [backend](backend-workflow.md) — ответственность приложений и границы проверки.
- [Routing eval cases](agent-context-evals.md) — реальные decision cases и counterexamples для conventions. - [HTTP contract](runtime-openapi-verification.md) — направление генерации OpenAPI и проверка совместимости.
- [Frontend workflow](frontend-workflow.md) / [backend workflow](backend-workflow.md) — запуск и существующие checks. - [Дизайн-система](design-system.md) / [UI-каталог](ui-catalog.md) — визуальные роли, реальные состояния и ограничения примеров.
- [HTTP contract workflow](runtime-openapi-verification.md) — Nest OpenAPI → snapshot → generated types и контрактная проверка. - [Принципы изменений](engineering-governance.md) — связность системы, риск и доказательства результата.
- [Дизайн-система](design-system.md) / [UI-каталог](ui-catalog.md) — theme/tokens, реальные components/stories и ограничения. - [Ведение документации](documentation.md) — самостоятельные объяснения и их обновление вместе с системой.
- [Remediation registry](audit-remediation.md) — найденная запись Audit ID и historical evidence. - [Контекст знаний](agent-token-efficiency.md) / [проверочные вопросы](agent-context-evals.md) — поиск ответа и обнаружение пробелов документации.
- [OpenSpec](openspec-authority-and-lifecycle.md) — optional legacy workflow, только по явному запросу. - [OpenSpec](openspec-authority-and-lifecycle.md) — роль сохранённых требований и статус прежнего процесса.
## Исторические свидетельства
[Remediation registry](audit-remediation.md) и [Completion Evidence](completion-evidence.md)
сохраняют статусы, исходные адреса и результаты прежних проверок.
[Аудиты](../audits/README.md) — неизменяемые снимки своего времени. Эти материалы
не входят в самостоятельное описание текущей системы; их внутренние ссылки
могут требовать архивного окружения и не являются условием чтения документации.
+25 -39
View File
@@ -1,50 +1,36 @@
# Проверка agent context # Проверка полноты проектных знаний
Это небольшой corpus решений для настройки routing. Выполняйте cases по текущим Эти вопросы помогают оценить, объясняет ли документация систему без доступа
AGENTS/router/conventions: сначала определите решение и необходимые источники, к исходникам. Для каждого вопроса важны правильный ответ, существенное
затем сравните с ожиданием. Это не unit tests, повторяющие Markdown, и не новый ограничение и отсутствие ошибочного вывода из похожего сценария.
verification framework. Для проверки изменения правила нужны activating case,
допустимое исключение и unrelated counterexample.
## Cases ## Cases
| Задача | Необходимый контекст / ожидаемое решение | Что не должно активироваться без новой границы | | Вопрос | Объяснение | Существенная граница |
| --- | --- | --- | | --- | --- | --- |
| Изменить props и layout existing finance view | Frontend UI; props остаются presentation, derived collection values — у VM | API, Prisma, architecture, tests без risk/test change | | Кто подтверждает доступ к брокерскому счёту? | [Обзор](../architecture/system-overview.md#предметная-модель) | Сессия подтверждает пользователя, но идентификатор счёта не доказывает доступ |
| Исправить только продуктовую строку/Zod message | UI section «Строки и валидация»; locale/validationMessage, mandatory review | Остальные UI sections, HTTP/structure/testing | | Как меняются финансовые данные при замене token? | [Подключение](../features/tbank-connection.md#граница-финансового-кэша) | Данные прежнего поколения не становятся сохранённым снимком нового подключения |
| Добавить page-local фильтр без сети | Frontend architecture + UI; оставить у page, если нет independent capability | Новый shared abstraction/feature ради click; API | | Что происходит при конкурентном refresh? | [Authentication](../features/authentication.md), [ADR 009](../adr/009-concurrent-refresh-rotation-policy.md) | Один победитель; заменённый token не отзывает его сессию сам по себе |
| Добавить business summary component | Architecture + UI; business meaning и existing owner определяют placement | shared только из-за reuse; widgets layer | | Как восстанавливается интерфейс после ошибки обновления? | [UI resilience](../features/frontend-ui-resilience.md) | Фоновая ошибка отличается от первой загрузки и смены пользователя |
| Изменить query refetch/error recovery | Frontend data + testing; cached snapshot/current generation и exact feature source | Backend data при неизменном server behavior | | Чем будущая выплата отличается от дохода? | [События](../features/brokerage-account-upcoming-events.md), [доходы](../features/brokerage-account-income-analytics.md) | Прогноз не подтверждает личное зачисление |
| Добавить новый HTTP response field | Backend HTTP + общий contract workflow + применимые tests; Nest → snapshot → generated TS, affected client data rules | Второй editable OpenAPI в specs; ручной generated TS | | Можно ли складывать разные валюты или заменить неизвестное нулём? | [Money](../architecture/domain-glossary.md#money) | Точность, валюта и отсутствие значения сохраняются отдельно |
| Добавить account endpoint с новым RPC | Backend HTTP, architecture при новом use case, security/access, integrations, testing; TbankAccessService/TbankClient | Прямой gRPC из controller; доверие accountId без ownership | | Откуда рейтинг и доходность облигации? | [Bond detail](../features/bond-detail.md) | Рейтинг эмитента не заменяет рейтинг выпуска; рыночная доходность не равна личному результату |
| Исправить только Prisma query без HTTP/auth boundary | Backend data, testing при data-risk; existing PrismaService | Весь HTTP/security guide автоматически | | Доказывает ли OpenAPI правильность ошибки запроса? | [HTTP API](../architecture/http-api.md#error-contract) | Равенство схем не доказывает полный HTTP-поток |
| Атомарно обновить связанные записи | Data + testing; transaction/constraint у инварианта, isolated DB proof | Generic repository; mock как доказательство реальной atomicity | | Что гарантирует проверка хранения данных? | [Backend](backend-workflow.md#локальная-проверка) | Проверка с подставными ответами не доказывает атомарность реальной базы |
| Изменить конкурентный refresh | Security + data + testing; ADR 009; один winner, superseded не отзывает его session | Replay-history/grace window без задачи; новый coordinator | | Как ограничиваются обращения к внешнему сервису? | [T-Bank](../features/tbank-connection.md#конфигурация-и-ограничения) | Повторы, очередь и лимит ограничены; TLS остаётся включённым |
| Изменить DTO validation error | HTTP + testing + exact error source; configured HTTP case проверяет pipe/filter pair | Success по одному direct controller call/Swagger snapshot | | Защищает ли фильтрация логов любое сообщение? | [ADR 008](../adr/008-pino-backend-logging.md) | Секрет в свободном тексте требует отдельной проверки |
| Изменить gRPC retry/deadline | Integrations + testing; bounded existing client path, controlled upstream failure | Вложенный retry loop; реальная сеть/secret | | Есть ли восстановление пароля по письму? | [Ограничения auth](../features/authentication.md#limitations) | Пример будущего сценария не является поставленной возможностью |
| Добавить безопасное scalar log field | Architecture/logging; проверить allowlist и event | Весь auth guide без sensitive boundary; считать message автоматически redacted |
| Изменить только тест | Testing соответствующего repo; source guides только по проверяемой границе | Все production conventions из-за расширения `.ts` |
| Добавить dependency/adapter для одной задачи | Общий выбор/dependencies; existing owner/platform/library прежде нового кода | Новый interface только для hypothetical reuse |
| Исправить опечатку в общей документации | Docs AGENTS и затронутый source | Любые frontend/backend conventions |
| После UI начала появились mutation/schema/new files | Routing повторяется по staged/unstaged/new, новые границы читаются до решения | Продолжать по первоначальному UI-only scope |
| Отсутствует обязательный security source / источники конфликтуют | Остановить зависимое решение, восстановить факт/уточнить задачу | Догадка из соседнего code или жёсткий лимит на число документов |
## Что измерять ## Что измерять
Для startup/working-set измерения перечислите ровно прочитанные файлы/sections, Полезное объяснение позволяет найти ответ, назвать ограничение и связать его
инструмент подсчёта и unit (символы или tokenizer). Required source нельзя с другими сценариями. Оцениваются неверные выводы о доступе, деньгах, полноте
исключать только ради хорошего числа. Число доступных файлов на диске не является данных и источниках, а также число уточнений до правильного понимания.
стоимостью конкретной задачи. Время поиска и объём чтения имеют смысл только вместе с точностью ответа.
Для реальной first-pass оценки выполните одинаковые задачи с одинаковой моделью,
version/tools и comparable prompts: прежний context, только новые indexes,
затем полный routing. Считайте принятие без substantive correction, iterations,
ошибки placement/contract/security, лишние abstractions и пропущенные sources.
Routing review и зелёные application tests сами по себе такого улучшения не доказывают.
## Изменение правил ## Изменение правил
Если нужный source не прочитан — исправьте trigger. Если source прочитан, но Если знание есть, но его трудно найти, уточняется навигация. Если документ
не разрешает выбор — уточните decision/safe path/example. Если дефект найден, но не объясняет различие понятий или ограничение, уточняется объяснение.
детерминирован — используйте подходящий existing test/linter. Не добавляйте При конфликте источников сохраняется отметка о ручном решении; догадка не
новый always-on paragraph для каждого отдельного случая. подменяет установленный факт.
+28 -48
View File
@@ -1,62 +1,42 @@
# Context discipline # Контекст знаний о системе
Цель — получить правила, которые меняют текущее решение, и не читать остальные Цель навигации — дать читателю знания, необходимые для конкретного вопроса,
заранее. Это процедура прямой работы по задаче, без обязательных SDD artifacts, без предварительного чтения всей документации. Самостоятельное объяснение
ролей, approval fingerprints или подсчёта документов как delivery gate. связывает понятия, сценарии и ограничения, а не воспроизводит устройство кода.
## Routing context ## Routing context
1. Определи требуемое поведение, тип решения и affected repos до supporting reads. [Тематический индекс](../AI_INDEX.md) направляет к нужному предмету:
2. Прочитай root/local AGENTS, затем CONVENTIONS и SOURCES affected code repo. аутентификация связана с безопасностью и сессиями, денежный показатель —
3. Объедини сработавшие semantic, path и boundary triggers. Путь — сигнал ответственности, а не повод загружать всё по расширению `.tsx`/`.ts`. с его происхождением и валютой, ошибка интерфейса — с восстановлением данных.
4. Прочитай выбранные conventions или явно указанные sections до зависимого решения. При domain/API/security вопросе открой точный source через [docs index](../AI_INDEX.md). Вопрос о внешнем виде не требует автоматически изучать хранение данных.
5. Проследи ближайший существующий owner и подходящие код/тесты. Его поведение не заменяет canonical fact. При обнаружении новой существенной границы расширяется и нужный контекст.
6. Повтори routing при новой потребности и по финальным staged, unstaged и новым файлам. Содержимое работы важнее первоначального названия задачи.
Первый проход работает без diff. Auth + refresh, например, активирует API,
security и testing вместе. Отображение query state без изменения запросов не
добавляет transport rules. Docs-only опечатка не требует frontend/backend conventions.
Обычно хватает нескольких тем; это наблюдение, не лимит. Обязательный source
нельзя пропускать ради числа файлов или token budget.
Новая граница внутри задачи требует дочитать правила до работы. Расширение
самой задачи сначала сообщи пользователю; уже данное разрешение не запрашивай
повторно. Чужой concurrent diff учитывай при разграничении ownership.
## Источники и конфликты ## Источники и конфликты
Прямое указание пользователя определяет задачу. Текущий executable HTTP contract Публичный HTTP-контракт описывается OpenAPI backend; документация объясняет
генерируется backend; docs описывает проектные факты/решения; conventions задают систему, ADR сохраняют причины решений, исследования предлагают варианты.
выбор реализации; code показывает существующие аналоги. Generic framework advice Существующее поведение не отменяет согласованное требование, а рекомендация
используется после проектных источников. Research содержит рекомендации, которые из исследования не становится принятым решением автоматически.
нужно сверить с actual code/config, а не автоматически превращать в новый контракт.
Если задача намеренно меняет контракт, обновляй его source и зависимые материалы При расхождении возможны устаревшее объяснение, дефект реализации или
согласованно. Missing input, противоречие security/data правил или неоднозначный незавершённое изменение. Неустановленный смысл, особенно в безопасности,
контракт блокируют только зависимое решение до установления фактов/уточнения. данных и публичном контракте, явно отмечается для ручного решения.
История чата, случайный аналог и предполагаемый путь не заменяют источник. Карта ответственности за знания — [ведение документации](documentation.md).
## Recovery и handoff ## Recovery и handoff
Для продолжения достаточно текущей задачи, сделанных изменений, remaining work, Продолжение работы опирается на ожидаемый результат, выполненные изменения,
точных checks/results и blockers. Не создавай параллельную копию требований, оставшиеся вопросы, результаты проверок и ограничения. Дублирование требований
Design, state registry или обязательный handoff-файл. в нескольких независимых пересказах создаёт риск расхождения. Исторические
проверки сохраняют своё значение только для того состояния системы, в котором
В новом контексте восстанови необходимые sources; в том же переиспользуй уже они выполнялись; секреты не являются допустимым содержимым свидетельств.
прочитанные неизменившиеся inputs. Supporting reads добавляй по возникшему вопросу.
Git status/diff показывают staged и unstaged; новые файлы учитывай отдельно.
Relevant ignored/workspace-owned inputs (например `.env`, root AGENTS) не
теряются только потому, что Git их не показывает. Секреты в evidence не печатай.
## Проверка эффективности ## Проверка эффективности
Используй [набор routing cases](agent-context-evals.md) для настройки новых правил. [Проверочные вопросы](agent-context-evals.md) показывают, можно ли получить
Проверяй activating case, safe exception и unrelated counterexample по реальным правильную модель системы и отличить близкие понятия. Качество оценивается по
инструкциям. Source links и gates дополняют эту проверку, но не доказывают first-pass quality. верности ответа, найденным ограничениям и необходимости дополнительных уточнений.
Число документов, их краткость или успешная проверка Markdown сами по себе
Сравнивай startup context и реально выбранные sections, фиксируя способ измерения. не доказывают полезность знания. Сравнение результатов требует одинаковых
Не объявляй меньшее число файлов или зелёный application test улучшением качества вопросов и сопоставимых условий.
агента. Для first-pass acceptance нужны одинаковые реальные задачи/модель/tools,
наблюдённые ошибки ownership/contract/security и число исправлений.
Добавляй convention по повторяющейся decision error; уточняй trigger при missed
source; deterministic regression отдавай существующему test/linter, если он подходит.
+22 -25
View File
@@ -1,34 +1,31 @@
# Рабочий процесс backend # Backend: ответственность и проверка
## Предварительные условия ## Предварительные условия
Backend требует Node 22. Создайте `backend/.env` на основе Backend обслуживает публичный HTTP API, локальные сессии, хранение данных
`backend/.env.example` и замените placeholder-значения `JWT_SECRET` и и интеграции с внешними источниками. Локальная поставка использует Node 22
`TOKEN_ENCRYPTION_KEY`; token T-Bank в этот файл не записывается. и SQLite. Окружению нужны адрес frontend, адрес базы, секрет подписи сессий
и отдельный ключ шифрования подключения. T-Bank token вводится пользователем
через настройки и не является общим секретом окружения.
```bash Назначение параметров, согласование адресов и сохранение данных описаны в
cd backend [локальном окружении](local-workspace.md).
cp .env.example .env
npm ci
```
## Локальная проверка ## Локальная проверка
Canonical gate не изменяет versioned-файлы: Проверка backend объединяет статический анализ, проверку типов приложения
и тестов, автоматические сценарии и сборку. Она не изменяет исходные материалы;
автоматическое исправление форматирования не считается доказательством проверки.
Диагностика покрытия доступна отдельно, общего числового порога для backend нет.
```bash [Сравнение OpenAPI](runtime-openapi-verification.md) проверяет соответствие
npm run verify публичного контракта ожиданиям frontend. Валидация запросов, cookie, доступ
``` и ошибки требуют полного HTTP-потока; целостность и конкурентные изменения
данных — проверки с изолированной реальной базой.
Он последовательно запускает lint, application/test TypeScript, Jest и build. `npm run lint:fix` — явная **Известное ограничение:** движок Prisma 7.9.1 при применении миграций к новой
записывающая команда, а `npm run test:cov` — отдельная диагностика без общего SQLite-базе возвращает `Schema engine error` до запуска сквозных тестов.
числового threshold. Runtime OpenAPI сверяется с frontend snapshot из workspace: Это воспроизведено и при проверке локального окружения 12 сентября 2026 года.
Успех остальных проверок не подтверждает прохождение этой границы. Условие
```bash пересмотра — стабильная версия с тремя последовательными успешными применениями
cd backend миграций на новых базах под Node 22.
npm run test:openapi-contract
```
`npm run test:e2e` временно не является passing gate: Prisma schema engine
останавливает migration до Jest. Точная диагностика и trigger пересмотра
зафиксированы в [audit remediation registry](audit-remediation.md).
+53 -104
View File
@@ -1,135 +1,84 @@
# Дизайн-система WealthLedger # Дизайн-система WealthLedger
Общий фундамент находится в Приложение и UI-каталог используют одну визуальную основу: цветовые роли,
[`frontend/src/shared/theme`](../../frontend/src/shared/theme/index.ts). типографику, интервалы, радиусы, тени и адаптивные размеры. Общая тема основана
Его public API экспортирует `theme`, `cssVariablesResolver`, `colorRoles`, `designTokens` и на Mantine; её значения не дублируются отдельно в каталоге.
подключает базовые CSS. App владеет MantineProvider; Storybook подключает свой Поддерживается светлая схема, тёмная тема не заявлена.
изолированный provider к тому же API. Тема и значения не копируются в preview.
Оба runtime используют светлую схему; dark theme не заявлена как поддерживаемая.
## Тема и токены ## Тема и токены
[`tokens.json`](../../frontend/src/shared/theme/tokens.json) — единственный Опубликованная система включает восемь палитр по десять оттенков: blue, gray,
редактируемый источник опубликованных токенов WealthLedger. Он содержит восемь dark, red, green, yellow, grape и orange; базовые white/black, оттенки
палитр по десять оттенков: blue, gray, dark, red, green, yellow, grape и orange; по умолчанию и семантические роли. Общая гарнитура применяется и к заголовкам,
базовые white/black, оттенки по умолчанию, семантические роли с описаниями, стандартный радиус — средний. Настройки являются доверенной локальной частью
типографику, интервалы, радиусы, тени и breakpoints. Существующие значения приложения; загрузка внешних или пользовательских токенов не предусмотрена.
приняты из темы приложения и Mantine 9.5.1; теперь ими владеет JSON. Default
radius остаётся md, font family заголовков совпадает с общей.
[`theme.ts`](../../frontend/src/shared/theme/theme.ts) напрямую адаптирует JSON | Роль | Цветовая основа | Применение |
в Mantine. Файл — доверенная конфигурация репозитория; приложение не загружает
внешние или пользовательские токены и не проверяет JSON runtime-схемой.
Размеры px преобразуются native `rem`, сохраняя `--mantine-scale`, тени собираются
из слоёв с `rgba`, а breakpoints остаются em без масштабирования. Неопубликованные
палитры и настройки поведения компонентов остаются defaults Mantine.
Небольшой [`theme.test.ts`](../../frontend/src/shared/theme/theme.test.ts)
проверяет преобразования адаптера; корректность изменений локального JSON
проверяется при review, сборке и проверке внешнего вида.
Используйте props Mantine (`gap="md"`, `p="xl"`, `radius="md"`, `shadow="xs"`)
или `--mantine-spacing-*`, `--mantine-radius-*` и другие native CSS variables.
[Foundations](http://localhost:6006/?path=/story/design-system-foundations--colors)
читает эффективную тему и описания из JSON, показывает фактические значения
и образцы. Обновление Mantine не заменяет принятые шкалы автоматически и требует
повторной проверки адаптера и внешнего вида приложения.
`colorRoles` связывает смысл и native Mantine reference; resolver создаёт
`--wl-color-<role>`, ссылающийся на Mantine variable, без повторного hex-значения.
| Role | Native reference | Применение |
| --- | --- | --- | | --- | --- | --- |
| canvas | gray.2 | Фон приложения | | canvas | gray.2 | Фон приложения |
| surface | white | Paper и формы в светлой схеме | | surface | white | Поверхности и формы |
| subtle | gray.0 | Приглушённая поверхность | | subtle | gray.0 | Приглушённая поверхность |
| text | dark.7 | Основной текст страницы | | text | dark.7 | Основной текст |
| muted | gray.6 | Вторичный текст (dimmed светлой схемы) | | muted | gray.6 | Вторичный текст |
| border | gray.3 | Граница поверхности | | border | gray.3 | Граница поверхности |
| primary | blue filled | Основное действие и активная навигация | | primary | blue filled | Основное действие и активная навигация |
| info | blue.6 | Информация | | info | blue.6 | Информация |
| positive | green.6 | Положительный результат с текстом/знаком | | positive | green.6 | Положительный результат с текстом или знаком |
| negative | red.6 | Ошибка или отрицательный результат с текстом/знаком | | negative | red.6 | Ошибка или отрицательный результат с текстом или знаком |
| warning | yellow.6 | Предупреждение, не мелкий текст на белом | | warning | yellow.6 | Предупреждение; не мелкий текст на белом |
Базовые CSS используют canvas/text aliases. Остальные роли доступны для Цвет не заменяет подпись или знак. Палитра не гарантирует доступный контраст
постепенного использования; существующие локальные Mantine references не произвольного сочетания текста и фона. Дополнительные grape/orange и отдельные
переписаны массово. Цвет не заменяет подпись статуса. Палитра не гарантирует оформления карточек сохраняют предметный смысл соответствующего экрана;
доступный контраст произвольных сочетаний: для текста и фона проверяйте контекст. они не образуют универсальную финансовую классификацию всей системы.
Grape и orange используются Product; предметные значения задают их FSD-владельцы.
Особые card gradients/shadows, размеры страницы и финансовые цвета CSS остаются Общие шкалы основаны на принятых значениях приложения и Mantine 9.5.1.
у своих FSD-владельцев. Это сохранение существующего UI, а не завершённый редизайн Обновление библиотеки не заменяет их автоматически. Масштабируемые размеры,
или миграция всех literals в tokens. Новую общую шкалу стоит вводить только при тени и адаптивные границы требуют сохранения единиц и визуальной проверки.
реальном повторном использовании, если native Mantine scale не подходит. Отдельные градиенты, тени и размеры экранов остаются локальными визуальными
решениями; переход всех существующих значений в общую шкалу не завершён.
## Переносимый JSON ## Переносимый JSON
Файл использует собственный формат WealthLedger `formatVersion: 1`, без runtime Опубликованные токены представлены в собственном формате WealthLedger,
imports, CSS variables или исполняемого кода. Это контракт данных, а не заявление версия 1. Это данные без исполняемого кода и зависимостей от runtime приложения.
о совместимости с DTCG или importer конкретной системы. Совместимость с DTCG или импортом конкретного инструмента не заявлена.
| Данные | Представление | | Данные | Представление |
| --- | --- | | --- | --- |
| Цвета | Hex; palette — массив оттенков 0–9; base — именованные white/black. | | Цвета | Hex, палитры оттенков 0–9 и именованные базовые цвета |
| Семантическая роль | `color`, `shade`, `description`; ссылка на palette или base. Для palette `shade: null` означает `primaryShade.light`, для base оттенок не нужен. | | Семантические роли | Ссылка на цвет и оттенок, описание; отсутствие оттенка палитры означает основной оттенок светлой схемы |
| Типографика | CSS font stacks; размеры и headings в px, веса числовые, line heights без единиц. | | Типографика | Наборы шрифтов, размеры в px, числовые веса и безразмерная высота строки |
| Интервалы и радиусы | Именованные `values` xs–xl с `unit: "px"`; `radius.default` выбирает имя шкалы. | | Интервалы и радиусы | Именованные шкалы xs–xl в px и выбранный радиус по умолчанию |
| Тени | Упорядоченные слои: offsetX/offsetY/blur/spread в px, ссылка `color` на base и opacity 0–1. | | Тени | Упорядоченные слои: смещения, размытие и растяжение в px, базовый цвет и непрозрачность |
| Breakpoints | Именованные `values` xs–xl с `unit: "em"`, не зависят от масштаба UI. | | Адаптивные границы | Именованные размеры xs–xl в em, независимые от масштаба интерфейса |
Не используйте для палитр специальные имена Mantine `dimmed` и `bright`; Перенос в другую систему требует сопоставления единиц, шрифтов, слоёв теней
`white` и `black` принадлежат base colors. Задавайте вес шрифта в диапазоне и семантических ссылок. Само чтение JSON не обеспечивает такой совместимости.
CSS 1–1000. Это правила редактирования формата, а не runtime-проверки. Несовместимое изменение формата требует новой версии; внешняя интеграция,
Внешний потребитель читает raw JSON напрямую. Например, из workspace root,
без Node, React или Mantine:
```python
import json
from pathlib import Path
tokens = json.loads(Path("frontend/src/shared/theme/tokens.json").read_text())
assert tokens["formatVersion"] == 1
colors = tokens["color"]
role = colors["roles"]["primary"]
shade = role["shade"]
if shade is None:
shade = colors["primaryShade"]["light"]
primary_hex = colors["palettes"][role["color"]][shade]
```
`shared/theme/index.ts` предназначен для frontend: он подключает CSS и Mantine
runtime. При интеграции с другой системой сопоставьте единицы, font stacks,
слои теней и semantic references с её форматом; парсер JSON сам этого не делает.
Несовместимое изменение требует новой версии контракта. Внешняя интеграция,
генератор артефактов и публикация пакета пока не реализованы. генератор артефактов и публикация пакета пока не реализованы.
## Общие компоненты ## Общие компоненты
Public API — [`shared/ui`](../../frontend/src/shared/ui/index.ts). Минимальный | Представление | Поведение и ограничения |
каталог включает две существующие реализации, реально используемые приложением:
| Компонент | Контракт и правила |
| --- | --- | | --- | --- |
| StateView | Empty/error с title, text, необязательным action; error получает role alert. Кнопке нужен доступный текст; не использовать для initial loading. | | Пустой результат или ошибка | Заголовок, пояснение и необязательное действие; ошибка объявляется вспомогательным технологиям, кнопка имеет доступное название |
| LoadingSkeleton | Общая status/busy-область с доступной label; caller задаёт только декоративные shapes без фокуса. Структура skeleton принадлежит section/page. | | Загрузка | Доступная область состояния занятости с пояснением и декоративными фигурами без фокуса; структура отражает ожидаемый экран |
| Действия | Основной, вторичный и вспомогательный варианты; состояния выполнения и недоступности |
Они не импортируют API, auth, счета или позиции. Не создаются wrappers над всеми Общие представления не получают финансовые данные и не управляют
примитивами: для действий используется Mantine Button с общей темой. В каталоге аутентификацией. Состояние выполнения действия отличается от загрузки всей
показаны filled для основного действия, default для вторичного/retry и subtle страницы. При фоновом обновлении доступные данные сохраняются с локальным
для вспомогательного, а также loading и disabled. Provider не меняет defaults индикатором; правила восстановления описаны в
Button или Paper ради каталога. Loading действия не заменяет loading страницы; [UI resilience](../features/frontend-ui-resilience.md).
при refetch сохраняются данные и показывается локальное состояние.
Route-aware AppShell не входит в заявленный общий набор. Бизнес-компоненты Каркас с продуктовой навигацией не входит в заявленный общий набор.
и композиции остаются у entities/pages и показываются в Product; витрина не Предметные карточки и композиции представлены в [UI-каталоге](ui-catalog.md).
определяет FSD ownership. Подробности запуска, взаимодействий и coverage —
[UI-каталог Storybook](ui-catalog.md).
## Границы доказательств ## Границы доказательств
Каталог использует synthetic fixtures, локальные Actions и MemoryRouter, без Каталог использует вымышленные данные и локальные действия без входа,
application API/auth/storage runtime. Dependency-cruiser проверяет FSD и сохранённого пользовательского состояния и application API. Проверки отделяют
транзитивную runtime-изоляцию stories. Browser проверяет виды, размеры, focus и его от исполнения продуктовых запросов. Вид, размеры, фокус и взаимодействия
действия. Эти примеры не доказывают API recovery и прохождение инвестиционного проверяются в браузере; каталог не доказывает восстановление реального API
сценария. Продуктовый пилот остаётся отдельным change и здесь не проводился. или прохождение инвестиционного сценария.
+88
View File
@@ -0,0 +1,88 @@
# Ведение документации
Документация объясняет систему независимо от размещения исходного кода.
За актуальность затронутого объяснения отвечает автор изменения предметной
области; проверка сопоставляет изложение с фактическим поведением и принятыми
решениями. Ответственность подсистем описана в [карте возможностей](../features/README.md).
## Владельцы фактов
| Тип знания | Где объясняется | Граница достоверности |
| --- | --- | --- |
| Устройство и взаимодействие системы | [Обзор](../architecture/system-overview.md) | Описывает существующие подсистемы, данные и потоки |
| Наблюдаемое поведение | [Возможности](../features/README.md) | Сценарий, инвариант, ошибка и ограничение объясняются здесь полностью |
| Значение терминов | [Глоссарий](../architecture/domain-glossary.md) | Одно определение, ссылки из связанных сценариев |
| HTTP-контракт | [HTTP API](../architecture/http-api.md), публичный OpenAPI backend | Объяснение семантики не создаёт второй независимо редактируемый wire contract |
| Причины значимого решения | [ADR](../adr/README.md) | Контекст, решение и последствия относятся к указанному статусу решения |
| Окружение и восстановление | [Локальное окружение](local-workspace.md) | Предпосылки и ограничения описаны без знания устройства репозиториев |
| Принципы сопровождения | [Engineering governance](engineering-governance.md) | Объясняют согласованность и доказательства результата |
| Гипотезы и внешние наблюдения | [Исследования](../research/README.md) | Рекомендация не означает реализованную возможность |
| Результаты прежних проверок | [Исторические свидетельства](README.md#исторические-свидетельства) | Действительны для состояния и условий своего времени |
Поведенческие требования и предложения изменений могут отдельно согласовываться
в OpenSpec. Их роль описана [здесь](openspec-authority-and-lifecycle.md);
понимание текущего сценария не требует доступа к хранилищу спецификаций.
При конфликте возможны устаревший текст, нарушение договорённости реализацией
или незавершённое изменение. Текущее поведение не отменяет согласованное
требование автоматически. Неустановленный смысл явно отмечается для ручного
решения; особенно это важно для безопасности, данных и публичного контракта.
## Куда поместить знание
Объяснение принадлежит существующему документу о соответствующей области.
Понятие определяется в глоссарии; сценарий связывает его с поведением и
ограничениями; ADR сохраняет причину выбора; исследование — ещё не принятый
вариант и его источники. Новый документ нужен для самостоятельного вопроса,
которому нет подходящего места. Навигация делает его доступным из индекса.
Один факт имеет одно полное объяснение, на которое ссылаются остальные страницы.
Ссылка внутри документации уточняет контекст, а не скрывает необходимое условие
понимания за внутренним файлом другого репозитория.
## Содержание страницы
Связное объяснение содержит назначение, ответственность компонентов,
предпосылки и инварианты, основной поток, ошибки, восстановление и ограничения.
Диаграмма нужна, когда помогает понять взаимодействие. Это ориентир, а не
обязательный набор пустых разделов.
Вместо имён классов, внутренних вспомогательных функций и настроек разработки
излагается их архитектурный или функциональный смысл. Публичные API, единицы
показателей, внешние системы и принятые технологические решения остаются
частью объяснения, если нужны читателю. Детали разработки и инструкции
инструментам не входят в описание системы.
Статус понятен из вступления или индекса: текущее поведение, объяснение решения,
исследование или история. Дата наблюдения не гарантирует актуальность спустя
время. Для замены принятого решения сохраняются его прежний контекст и ссылка
на последующее решение.
## Обновление вместе с кодом
| Что изменилось в системе | Какое знание затронуто |
| --- | --- |
| Сценарий, инвариант, ошибка или восстановление | Объяснение возможности и её ограничений |
| Ответственность, внешний источник или поток данных | Обзор, схема и причина значимого решения |
| Значение понятия | Глоссарий и связанные сценарии |
| Окружение, диагностика или граница проверки | Материалы сопровождения |
| Новая возможность | Карта возможностей и тематический индекс |
| Только внутреннее устройство без изменения описанных знаний | Изменение документации может не требоваться |
Изменения нескольких компонентов относятся к общей задаче; объяснение
поставленного результата не должно обещать ещё отсутствующую часть.
Смысловая согласованность важнее совпадения названий задач и формальных отметок.
## Проверка
Проверка охватывает Markdown, внутренние ссылки и якоря, доступность страниц
через навигацию и чтение основной документации без соседних репозиториев.
Внешние URL и HTML-ссылки не проверяются правилом относительных ссылок.
Исторические аудиты, журнал устранения замечаний и свидетельства завершения
сохраняют исходные адреса отдельно от основной книги.
Смысловая проверка подтверждает, что читатель понимает ответственность,
инвариант и ограничение; будущая возможность не выдана за существующую;
API и архитектура не изменены редакторским пересказом. Непонятный смысл
внешней ссылки отмечается для ручной проверки, а не восполняется догадкой.
Успешная синтаксическая проверка не доказывает актуальность знания.
+56 -77
View File
@@ -1,103 +1,82 @@
# Engineering governance # Принципы изменения и проверки системы
Это общий owner решений о scope, dependencies, testing и проверке результата. Ожидаемый результат определяется задачей пользователя. Сопровождение сохраняет
Работа начинается с задачи пользователя и выполняется напрямую. OpenSpec/SDD ответственность подсистем, безопасность, точность данных и проверяемость результата.
не обязателен; прежняя [процедура](openspec-authority-and-lifecycle.md) доступна OpenSpec остаётся [необязательным средством согласования](openspec-authority-and-lifecycle.md);
только по явному запросу. Исторические artifacts не переписываются. исторические свидетельства не становятся обязательной процедурой новой задачи.
## Выбор реализации ## Выбор реализации
Читай этот раздел при новой abstraction/dependency, refactor, multi-module Связное поведение принадлежит ответственной подсистеме. Существующая возможность
изменении или неоднозначном ownership. используется повторно, если сохраняет нужный смысл и инварианты. Новая граница
оправдана самостоятельной ответственностью, общей потребностью нескольких
потребителей либо необходимостью отделить доступ и целостность данных.
1. Требуемое поведение уже существует — используй его. Простота оценивается по ясности всей системы и стоимости сопровождения,
2. У поведения есть подходящий owner/helper — расширь его, сохранив связность. а не по размеру изменения. Предполагаемая будущая возможность сама по себе
3. Задачу решает платформа, стандартная библиотека или принятая dependency — используй её. не оправдывает новую подсистему. Упрощение сохраняет валидацию, доступность,
4. Иначе добавь небольшую реализацию там, где принадлежит инвариант. обработку ошибок и защиту от потери данных.
5. Если текущая структура мешает правильному ownership, сделай необходимый связный refactor с проверкой сохранения поведения.
Оптимизируй ясность всей системы. Число файлов/строк само по себе не определяет
качество: coherent refactor лучше дублированного guard в нескольких callers.
**Безопасный путь:** при неопределённом будущем reuse оставь код у ближайшего
подходящего владельца. Выделение оправдано текущей общей ответственностью,
несколькими реальными consumers или необходимой trust/transaction boundary.
Один consumer не запрещает модуль с самостоятельным инвариантом, но не оправдывает
interface/factory/registry только для воображаемой расширяемости.
Примеры: фильтр одного экрана остаётся page-local; чистое денежное преобразование
использует существующий decimal helper; запрос Prisma не требует generic repository.
Security validation, доступность, error handling и защита данных сохраняются
даже в маленькой реализации.
## Dependencies ## Dependencies
Перед новой dependency проверь installed stack и существующий helper. Если При выборе внешней зависимости учитываются текущая потребность, совместимость,
подходящего решения нет, оцени поддерживаемую библиотеку, совместимость, поддержка, безопасность и стоимость сопровождения. Использование принятой
maintenance/security cost и точную недостающую возможность. Собственный код платформы может быть проще собственной реализации; зависимость ради одной
выбирай по совокупной стоимости, а не ради отсутствия dependency любой ценой. воображаемой будущей возможности не является самостоятельной потребностью.
Новый сервис или общая инфраструктура должны решать актуальную задачу.
Версии и scripts принадлежат package.json/lockfile. Frontend сохраняет exact
versions; backend использует свою текущую policy. Transitive package не считается
прямо принятой dependency. Не меняй весь lockfile или инструментарий ради
устаревшей рекомендации в Markdown. Новые сервисы, CI и browser mocking требуют
задачи, в которой они действительно нужны.
## Risk-based testing ## Risk-based testing
Автоматический тест обязателен для изменённого security/privacy поведения, Изменения безопасности и приватности, денежных расчётов, целостности данных,
денежных вычислений, data integrity/migration, concurrency, публичного контракта, миграций, конкурентного доступа, публичного контракта, критического сценария
критического journey и подтверждённой регрессии. и подтверждённой регрессии требуют автоматической проверки соответствующего
риска на наблюдаемой потребителем границе.
Выбирай самую дешёвую надёжную границу, наблюдаемую потребителем. Тест нужен Статические проверки не доказывают финансовую или функциональную корректность.
для собственного branching, преобразования или инварианта; не для каждого Покрытие frontend служит ограничителем регрессий; для backend общего числового
DTO, декларативной конфигурации, thin adapter или поведения библиотеки. порога нет. Проверки поведения библиотеки или проценты сами по себе не заменяют
Меняющийся тест тоже активирует testing convention, даже без production diff. проверку собственного инварианта системы.
Frontend coverage — anti-regression constraint; backend общего threshold не имеет.
Не пиши тесты только ради процента или повторения Markdown.
## Проверка результата ## Проверка результата
В итерации используй targeted check из применимого convention. Перед завершением Результат оценивается по исходной задаче: сохранены ли смысл данных и контракты,
запусти `npm run verify` каждого изменённого code/docs repo под его `.nvmrc` правильно ли разграничен доступ, учтены ли ошибки и доступность интерфейса.
после lockfile install. Для HTTP contract — дополнительно Проверки приложений и документации дополняются
[contract checks](runtime-openapi-verification.md). Общего CI сейчас нет. [согласованием HTTP-контракта](runtime-openapi-verification.md), если он меняется.
Общего CI сейчас нет.
Проверь actual staged/unstaged/new scope и результат против задачи: ownership, Успешная проверка применима к тому состоянию системы и тем предпосылкам,
сохранение данных/контрактов, ошибки, доступность, отсутствие лишних abstractions. на которых выполнена. При их изменении старое свидетельство не доказывает
Неизменившийся успешный gate можно использовать повторно, если известны команда, новый результат. Автоматическое исправление материалов и проверка без их
его полный relevant input scope и неизменные prerequisites; при сомнении выполни изменения — разные действия.
его снова. Auto-fix не заменяет non-mutating gate.
В ответе достаточно: что поставлено, как проверено, какие ограничения остались. [Влияние на документацию](documentation.md#обновление-вместе-с-кодом) входит
Не создавай role records, fingerprints всех repositories, Completion Evidence в оценку результата: описанные понятия, сценарии, решения и ограничения
или delivery commits только ради процесса. Существующие рабочие изменения обновляются вместе с системой. Отсутствие влияния может быть обоснованным
других задач сохраняются. Нужный review не заменяется количеством checkboxes. результатом. Отчёт о поставке различает выполненное, проверенное и остающиеся
ограничения; количество формальных отметок не заменяет смысловую проверку.
## Guardrails и local gates ## Guardrails и local gates
- **Автоматический invariant:** существующий deterministic checker блокирует нарушение в фактическом scope с документированными исключениями. Автоматические проверки гарантируют только свойства, охваченные их областью.
- **Обязательное semantic правило/процедура:** исполнитель и reviewer проверяют решение; отсутствие checker не переводит i18n, validation или authorization в пожелание. Смысловые требования — например, правильный владелец данных, авторизация,
- **Guidance:** необязательное предпочтение, например эстетический выбор между эквивалентными вариантами. локализация и полезность выбранных проверок — требуют отдельной оценки.
Предпочтение между равноценными вариантами отличается от обязательного
Не обещай enforcement, отсутствующий в config/tests. Новая автоматическая проверка инварианта. Новая автоматизация оправдана реальным риском или повторяющимся
оправдана реальным риском или повторяющимся дефектом, а не возможностью написать дефектом; её наличие само по себе не является целью.
ещё один checker. Механические правила принадлежат tools; выбор правильного owner
и полезного теста остаётся semantic judgement.
## UI-решения ## UI-решения
Для изменяемого UI определи ожидаемые экраны, states, interactions и responsive Ожидаемые экраны, состояния, взаимодействия и поведение на разных размерах
поведение из задачи. Используй существующий [каталог](ui-catalog.md) и экрана определяются пользовательским сценарием. [Каталог](ui-catalog.md)
[дизайн-систему](design-system.md). Сложный или неоднозначный вариант покажи и [дизайн-система](design-system.md) показывают существующую визуальную основу.
пользователю в конкретной форме до дорогой реализации; простой согласованный Неоднозначный вариант можно оценить по конкретному примеру до реализации.
вариант делай сразу. Визуальные материалы нужны по задаче, не как обязательный Успешная сборка каталога не доказывает доступность и визуальное соответствие.
SDD-пакет. Сборка Storybook не доказывает доступность или визуальное соответствие.
## Изменение опасного контракта ## Изменение опасного контракта
При public/security/data/billing или труднообратимом выборе заранее уточни Для публичного контракта, безопасности, данных, платежей и труднообратимого
существенную неопределённость. Для миграции определи trigger отката, действия, изменения существенная неопределённость разрешается до применения решения.
сохранность уже созданных данных и проверку восстановления. Сначала подготовь Для миграции важны условия отката, сохранность уже созданных данных и проверка
конкретное reviewable решение; обычные обратимые edits не требуют ритуального approval. восстановления. Порядок разрешения конфликтов знаний описан в
Источники и конфликтующие версии разрешай через [context discipline](agent-token-efficiency.md). [контексте системы](agent-token-efficiency.md#источники-и-конфликты).
+25 -42
View File
@@ -1,51 +1,34 @@
# Frontend Workflow # Frontend: ответственность и проверка
Frontend требует Node 22. В `frontend/` чистая установка и полный локальный quality gate выполняются так: Frontend предоставляет пользовательские экраны, навигацию и представление
финансовых данных. Он получает данные только через публичный API backend;
credentials T-Bank остаются на сервере. Состав приложения и потоки показаны
в [обзоре системы](../architecture/system-overview.md).
```bash ## Совместимость с backend
nvm use --silent
npm ci
npm run verify
```
`verify` проверяет OpenAPI drift, TypeScript, Biome, один coverage-backed запуск Vitest, Knip, Типизированный клиент frontend производится из опубликованного backend
FSD dependency rules, runtime graph stories и отдельные сборки приложения и OpenAPI. Согласованность клиента с сохранённым описанием API и согласованность
Storybook. Он работает без backend. Coverage этого описания с работающим backend проверяются отдельно.
остаётся anti-regression constraint; правила выбора тестов зафиксированы в Изменение публичного контракта требует обновления производного клиента;
[engineering governance](engineering-governance.md). подробности — [согласованность OpenAPI](runtime-openapi-verification.md).
Изолированные примеры реальных компонентов и композиций запускаются в Storybook ## Проверка интерфейса
через `npm run dev:catalog`. `npm run build:catalog` создаёт `storybook-static/`,
а `npm run preview:catalog` обслуживает эту статическую сборку. Структура каталога,
работа с Docs, Controls и Actions, доступность и ограничения описаны в
[UI-каталоге Storybook](ui-catalog.md).
Для UI задачи используйте каталог как reference существующего решения. Локальная проверка frontend охватывает корректность типов, статический анализ,
Ожидаемые экраны, states и interactions определите по [задаче](engineering-governance.md#ui-решения). автоматические сценарии, зависимости и сборку приложения и UI-каталога.
Storybook показывает реализацию; визуальное соответствие и доступность Она может выполняться без запущенного backend. Покрытие тестами ограничивает
проверяются отдельно от сборки. SDD-пакет для этого не обязателен. регрессии; само по себе число покрытых строк не доказывает правильность
финансового расчёта, доступность или прохождение пользовательского сценария.
Backend остаётся источником OpenAPI. После изменения опубликованного [Каталог](ui-catalog.md) показывает существующие визуальные решения.
backend-контракта разработчик обновляет в `frontend/` committed snapshot Соответствие задаче, широкий и узкий экран, клавиатурная навигация и восстановление
`src/shared/api/spec/openapi.json` копированием backend OpenAPI document, затем после ошибок проверяются в интерфейсе отдельно от успешной сборки.
запускает:
```bash
npm run openapi:generate
```
Команда использует только committed snapshot и регенерирует
`src/shared/api/generated/schema.d.ts` offline. Проверку соответствия backend
runtime contract и frontend snapshot выполняют отдельной backend-командой
`npm run test:openapi-contract`.
## Состояние тестового клиента ## Состояние тестового клиента
`frontend/src/test/query.tsx` предоставляет `createTestQueryClient` с отключёнными Независимые проверки получают изолированное состояние клиента. Обновление
test retries и query-only `createQueryWrapper`. Независимый case получает свой представления внутри одного сценария сохраняет его кэш; следующий сценарий
client до React wrapper render; rerender сохраняет тот же экземпляр и seeded не наследует сессию и ответы предыдущего. Повторные попытки в тестовой среде
cache. Auth и T-Bank tests используют эту же фабрику. Mantine, Router и могут быть отключены для предсказуемого результата. Это не меняет
AuthProvider остаются явно выбранными локальными providers. [политику обновления данных приложения](../adr/007-react-query-fetch-policy.md).
Общий teardown последовательно выполняет RTL cleanup, сброс auth client и
сброс MSW handlers. Production query defaults этой инфраструктурой не меняются.
+84
View File
@@ -0,0 +1,84 @@
# Локальное окружение и восстановление
WealthLedger поставляется как локальное приложение: frontend работает
в браузере, backend обслуживает API и обращается к SQLite и внешним источникам.
Это описание предпосылок и ограничений работы, без привязки к устройству
исходного кода. Production-развёртывание в текущую поставку не входит.
## Подготовка backend
Локальная серверная среда использует Node 22 и SQLite. Для работы нужны:
| Параметр | Назначение и ограничение |
| --- | --- |
| Адрес локальной базы | Одинаковая база используется при подготовке схемы и работе приложения; относительный адрес зависит от рабочей директории процесса |
| Разрешённый origin frontend | Определяет browser-источник, которому разрешён доступ к API с credentials |
| Порт API | По умолчанию 3000 |
| Секрет подписи сессий | Секрет не короче 32 символов; отличается от ключа шифрования подключения |
| Ключ шифрования подключения | 32 байта в base64; требуется для чтения сохранённого T-Bank token |
| Параметры интеграции | Адрес T-Bank, лимит запросов и при необходимости доверенный CA; [ограничения](../features/tbank-connection.md#конфигурация-и-ограничения) |
T-Bank token вводится пользователем через интерфейс после входа и не является
общим параметром окружения. Существующая база и её ключ шифрования сохраняются
при обновлении. До миграции необходима резервная копия; восстановление проверяется
на отдельной копии. Потеря или замена ключа не является безвредным сбросом:
прежнее подключение нельзя расшифровать новым ключом.
**Известное ограничение:** проверка новой временной SQLite-базы 12 сентября
2026 года под Node 22 воспроизвела `Schema engine error` до создания схемы.
Полный первый запуск на чистой базе пока не подтверждён; пользовательская база
в этой проверке не использовалась. Условия пересмотра —
[проверка backend](backend-workflow.md#локальная-проверка).
В development доступна [локальная Swagger UI](http://localhost:3000/api/docs)
при стандартном порте API. Она показывает контракт, но не доказывает доступность
T-Bank и не используется frontend как источник данных во время работы.
## Подготовка frontend
Стандартные адреса: API `http://localhost:3000`, frontend `http://localhost:5173`.
Адрес API в клиенте должен совпадать с работающим сервером, а разрешённый origin
backend — с origin браузера. Смена порта требует согласованного изменения
адресов. Смесь `localhost` и `127.0.0.1` меняет границу browser cookie и CORS.
Локальный HTTP предполагает development-режим backend: в других режимах
refresh-cookie получает Secure flag и требует защищённого соединения.
Поведение сессии описано в [Authentication](../features/authentication.md).
## Первый сценарий
1. Пользователь регистрируется или входит. Новый профиль получает сессию;
после перезагрузки приложение пытается её восстановить.
2. В настройках пользователь сохраняет собственный T-Bank Invest token.
Успех означает проверенный доступ к брокеру и сохранённое подключение.
3. В разделе «Счета» доступны отдельный и общий портфель. Отсутствие доступных
счетов является допустимым пустым результатом.
4. «Доходы» показывают полученные выплаты, «События» — будущие.
5. При выходе локальная сессия и её клиентский кэш очищаются.
Без T-Bank token доступны регистрация, вход и состояние отсутствующего
подключения. Это не подтверждает работу финансовых сценариев.
## Если запуск или сценарий не работает
| Симптом | Проверка и безопасное восстановление |
| --- | --- |
| Порт занят | Адрес действующего процесса или выбранный свободный порт должен совпадать с настройками клиента и разрешённым origin |
| Ошибка CORS или сессия теряется после reload | Согласованность адресов, hostname и режима cookie проверяется на стороне browser и backend |
| Ошибка подготовки базы | Установленный blocker не устраняется удалением пользовательской базы или обходом миграций; сохраняются версия среды и текст ошибки |
| `TBANK_NOT_CONNECTED` | Подключение создаётся в настройках текущего пользователя |
| `INVALID_TBANK_TOKEN` | После проверки token и прав у брокера подключение заменяется через настройки |
| `TBANK_UNAVAILABLE`, TLS или rate limit | Проверяются сеть, лимиты и доверенный CA; проверка TLS сохраняется |
| НКР/MOEX недоступен при работающих остальных секциях | Состояние источника и повторная попытка относятся к конкретной секции; недоступность не означает отсутствия рейтинга |
| Подключение не читается после смены ключа | Восстанавливается прежний ключ из защищённой копии; если он утрачен, собственный token вводится заново через настройки |
HTTP-запросы связываются с безопасными журналами через `X-Request-ID`.
Пароли, cookies, Authorization и broker token не включаются в диагностический
отчёт. Ограничения журналирования — [ADR 008](../adr/008-pino-backend-logging.md).
## Проверка первого изменения
Успешная сборка, запуск и прохождение сценария — разные доказательства.
Границы проверки описаны для [frontend](frontend-workflow.md),
[backend](backend-workflow.md) и [HTTP-контракта](runtime-openapi-verification.md).
Завершённость результата учитывает фактические проверки и остающиеся ограничения.
+17 -27
View File
@@ -1,35 +1,25 @@
# OpenSpec: optional legacy workflow # OpenSpec: роль требований и история процесса
Обязательный SDD lifecycle отменён для обычной разработки. Текущие правила — Обязательный SDD lifecycle отменён для обычной разработки согласно
[engineering governance](engineering-governance.md), [ADR 012](../adr/012-task-driven-development-with-scoped-context.md).
[context discipline](agent-token-efficiency.md) и root/local AGENTS. Задача пользователя определяет ожидаемый результат; дополнительные planning
Задача пользователя не требует Proposal/Delta/Design/Tasks, отдельных agent roles, artifacts и архивирование не являются условием её завершения.
Completion Evidence или archive как условия завершения.
## Существующие материалы ## Существующие материалы
`specs/openspec` сохраняет behavioral specs, active changes, schemas и archives. OpenSpec сохраняет поведенческие требования, предложенные изменения и историю
Существующий planning artifact описывает только свой прежний scope и не доказывает согласования. Существующее описание изменения относится только к своему объёму
готовность нового запроса. Датированные evidence/archives не переписываются. и не доказывает готовность другой возможности. Текущее поведение системы
Нужную behavioral spec выбирай через [specs index](../../specs/AI_INDEX.md). объясняется в [карте возможностей](../features/README.md).
## Если OpenSpec запрошен явно ## Если OpenSpec запрошен явно
Canonical store прежнего workflow — `wealth-ledger-specs`, физически OpenSpec остаётся необязательным средством согласования требований, дизайна
`specs/openspec`. Перед Node-командами выбери frontend `.nvmrc`, если у specs и задач. Его собственная модель определяет состав и порядок артефактов;
нет своей. Для чтения/изменения change всегда задавай store явно: подтверждение человеком и смысловая проверка результата не выводятся из
одного наличия файлов. Обновление требований и архивирование меняют историю,
поэтому относятся к явно выбранной работе с этой историей.
```bash Причины прежнего процесса и его упрощения сохранены в
openspec status --change <change> --json --store wealth-ledger-specs [ADR 010](../adr/010-native-openspec-sdd-and-local-orchestration.md) и
openspec instructions <artifact> --change <change> --json --store wealth-ledger-specs [ADR 011](../adr/011-native-openspec-lifecycle-with-repository-gates.md).
```
Native status/instructions определяют фактические artifacts и schema. Получай
разрешение на именно запрошенное изменение, не выводи approval из наличия файлов.
Sync/archive меняют existing specs/history и выполняются только в рамках такой
явной задачи, после проверки ожидаемого semantic diff и сохранности данных.
Structural validation не доказывает соответствие реализации задаче.
Прежнее rationale — [ADR 010](../adr/010-native-openspec-sdd-and-local-orchestration.md)
и [011](../adr/011-native-openspec-lifecycle-with-repository-gates.md);
текущий выбор прямой работы — [ADR 012](../adr/012-task-driven-development-with-scoped-context.md).
+25 -40
View File
@@ -2,55 +2,40 @@
## Назначение ## Назначение
`npm run test:openapi-contract` в `backend/` строит configured Nest application, Проверка сравнивает OpenAPI, сформированный настроенным backend-приложением,
создаёт runtime OpenAPI document и сверяет его с committed snapshot с описанием API, на котором основан frontend. Backend остаётся единственным
`frontend/src/shared/api/spec/openapi.json` после детерминированной canonicalization. источником исполняемого HTTP-контракта; frontend-клиент является производным.
Причины выбора описаны в [ADR 002](../adr/002-nest-generated-openapi-contract.md).
Это отдельный контрактный gate, а не общий verifier всех репозиториев. Он обязателен перед завершением задачи, меняющей Эта проверка подтверждает согласованность публичного контракта, но не заменяет
backend API или frontend transport contract и не заменяет проверки каждого приложения и наблюдаемого HTTP-поведения.
локальные lint/typecheck команды приложений.
## Выбор и направление изменения ## Выбор и направление изменения
1. Определите, меняется wire contract (route, request/response field, status, error shape) или только внутренний код/UI model. Во втором случае snapshot не меняется. Изменением контракта считается изменение маршрута, входного или выходного поля,
2. Прочитайте [HTTP semantics](../architecture/http-api.md) и нужный feature/domain source. Backend Nest controllers/DTO/Swagger metadata — единственный editable источник OpenAPI; specs repo не является вторым OpenAPI source. статуса либо формы ошибки. Внутреннее изменение представления без изменения
3. Измените этот source и проверьте representative HTTP behavior. При изменении контракта скопируйте generated runtime OpenAPI в committed frontend snapshot, затем в frontend выполните `npm run openapi:generate`. API не требует нового описания контракта.
4. Выполните frontend `npm run openapi:check` и backend `npm run test:openapi-contract`, а также verify affected repos. Эти команды проверяют разное: schema parity не доказывает validation/auth/error behavior.
Generated frontend TS и backend Prisma client вручную не редактируются. Направление согласования: публичный API backend → OpenAPI → производное
Frontend domain/UI type допустим, когда он отличается от transport DTO по смыслу. описание и типизированный клиент frontend. Две независимо редактируемые версии
Перед compatibility-sensitive изменением оцените существующих consumers и контракта создавали бы риск расхождения. Перед изменением, влияющим на
rollback/data constraints; одного обновления snapshot недостаточно. совместимость, учитываются существующие потребители и сохранность данных.
Семантика запросов и ошибок описана в [HTTP API](../architecture/http-api.md).
## Запуск
Из корня workspace:
```bash
cd backend
nvm use --silent
npm run test:openapi-contract
```
Перед первым запуском в backend выполняют `npm ci` под Node 22.
## Границы и результат ## Границы и результат
Команда: Сравнение выполняется на границе настроенного приложения, нормализует порядок
ключей JSON-объектов и сохраняет порядок массивов. Оно не обновляет сравниваемые
описания и клиент автоматически и не обращается к T-Bank или другим внешним
источникам. Несовпадение указывает на рассогласование контракта.
- использует configured Nest application на integration boundary; Равенство схем не доказывает фактическую работу валидации, авторизации,
- рекурсивно сортирует только ключи JSON objects и сохраняет порядок arrays; статусов, cookie и обработки ошибок: для этого нужны наблюдения полного
- не записывает snapshot или generated frontend types. HTTP-потока. Согласованность описаний и функциональная корректность —
разные доказательства.
Она не записывает committed snapshot или generated frontend types и не
обращается к T-Bank либо другому внешнему сервису. Если контракт действительно
изменён, snapshot обновляют явным копированием backend OpenAPI document в
`frontend/src/shared/api/spec/openapi.json`, затем в `frontend` запускают
`npm run openapi:generate` и снова выполняют эту проверку.
## Ограничение e2e ## Ограничение e2e
Проверка Swagger не запускает backend e2e tests. WL-P2-05 (runnable e2e) и Сравнение OpenAPI не запускает сквозные проверки с миграциями базы.
WL-P2-13 (единый workspace verdict) отложены из-за нестабильного Prisma 7.9.1 Известная ошибка движка миграций и условие пересмотра описаны в
migration gate; дата, rationale и trigger пересмотра находятся в [проверке backend](backend-workflow.md#локальная-проверка).
[audit remediation registry](audit-remediation.md).
+50 -98
View File
@@ -1,120 +1,72 @@
# UI-каталог Storybook # UI-каталог Storybook
Storybook 10.6.0 показывает реальные компоненты и композиции WealthLedger Storybook 10.6.0 показывает реальные представления WealthLedger на общей
на общей визуальной основе приложения без входа и подключения backend. визуальной основе приложения без входа и подключения backend.
## Запуск и сборка ## Запуск и сборка
Из `frontend/` выберите Node 22 и установите зависимости из lockfile: Каталог работает как отдельная локальная витрина по адресу
[localhost:6006](http://localhost:6006) либо как самостоятельная статическая
```bash сборка. Его примеры не входят в пользовательские маршруты приложения.
nvm use --silent Публикация отдельным сервисом не настроена.
npm ci
npm run dev:catalog
```
Storybook запускается на [localhost:6006](http://localhost:6006); браузер
автоматически не открывается. Отдельная статическая сборка и её preview:
```bash
npm run build:catalog
npm run preview:catalog
```
Результат находится в `frontend/storybook-static/`, вход — `index.html`.
Команда preview выводит адрес статической витрины в терминале. Обычный
`npm run build` сохраняет приложение в `frontend/dist/`; stories не входят
в его entrypoint и продуктовые маршруты. Публикация отдельным сервисом не настроена.
## Компоненты и работа с примерами ## Компоненты и работа с примерами
В дереве доступны два блока, 9 групп / 30 историй: Два блока каталога включают 9 групп и 30 примеров:
| Блок / группа | Что показывает | | Группа | Что показывает |
| --- | --- | | --- | --- |
| `Design System/Foundations` | Восемь палитр и семантические роли, типографику, интервалы, радиусы, тени и breakpoints из общей темы, адаптированной из tokens.json. | | Основы дизайн-системы | Палитры, семантические роли, типографику, интервалы, радиусы, тени и адаптивные размеры |
| `Design System/Components/StateView` | Четыре empty/error варианта с действием и без. | | Пустой результат и ошибки | Четыре варианта с действием и без |
| `Design System/Components/LoadingSkeleton` | Общую доступную busy-область с нейтральными structural shapes. | | Загрузка | Доступную область занятости с нейтральными фигурами |
| `Design System/Components/Button` | Прямой Mantine Button: primary, default, subtle, loading, disabled; локальное действие и keyboard interaction. | | Кнопки | Основное, вторичное и вспомогательное действие, выполнение и недоступность, работу с клавиатурой |
| `Product/BrokerageAccountList` | Сводки счетов в нескольких валютах, недоступную/пустую сводку и длинное название. | | Список счетов | Несколько валют, недоступную и пустую сводку, длинное название |
| `Product/PositionCard` | Акцию с положительным результатом, облигацию с отрицательным, прочий инструмент и позицию без оценки. | | Карточка позиции | Акцию с положительным результатом, облигацию с отрицательным, прочий инструмент и отсутствие оценки |
| `Product/RecentEvents` | Полученные события, loading/error/empty и неизвестную ожидаемую сумму. | | Последние события | Успешный, загружаемый, ошибочный и пустой результат; неизвестную сумму |
| `Product/PortfolioRefreshState` с `PortfolioTotals` | Сохранённые суммы при обновлении и ошибке, локальный retry. | | Обновление портфеля | Сохранённые суммы при обновлении и ошибке, локальную повторную попытку |
| `Product/AccountPageLoading` | Реальный skeleton заголовка, сводки и строк страницы счёта. | | Загрузка страницы счёта | Контур ожидаемых заголовка, сводки и строк |
Быстрые входы: [Design System — цвета](http://localhost:6006/?path=/story/design-system-foundations--colors) Каталог не перечисляет все элементы библиотеки или приложения. Отсутствие
и [Product — карточка позиции](http://localhost:6006/?path=/story/product-positioncard--stock-gain). представления в витрине не означает его отсутствия в продукте. Основы показывают
Все исходные 21 истории сохранены; 9 новых добавляют foundations и общие примеры. опубликованные шкалы и смысл ролей; особые размеры и тени конкретных экранов
Бизнес-компоненты остаются в entities/pages, а общие StateView и LoadingSkeleton не объявляются общими токенами. Границы описаны в [дизайн-системе](design-system.md).
— в shared/ui. Button показан напрямую: новое имя-wrapper не вводится.
Каталог не перечисляет весь Mantine или все компоненты приложения. AppShell Canvas показывает выбранный пример. Docs поясняет назначение и ограничения,
с product navigation не объявлен компонентом общего набора. Отсутствие компонента Controls меняет доступные свойства, Actions показывает локальные действия.
в каталоге не запрещает его использование. Foundations показывает опубликованные Вымышленные исходные данные не редактируются через Controls: другой набор
шкалы из `shared/theme/tokens.json`, описания ролей и эффективные Mantine значения. данных выбирается отдельным примером. Свойства можно сбросить, пример —
Локальные размеры и особые тени не объявляются глобальными токенами; смонтировать заново; изменение размера просмотра не сбрасывает выбранные значения.
неопубликованные defaults остаются у Mantine. [Дизайн-система](design-system.md)
описывает JSON-формат, внешнее чтение, ownership и правила расширения.
Выберите историю в дереве, чтобы открыть её состояние в Canvas. Docs описывает Витрина и приложение используют одну тему. Примеры показываются по-русски
назначение, ограничения и свойства группы, а также её примеры. Controls меняют в светлой схеме. Ошибка сохраняет доступное объявление, загрузка — состояние
разрешённые свойства; синтетические объекты и callbacks недоступны для редактирования. занятости; декоративные фигуры не получают фокус.
Например, `kind` у `PositionCard` меняет вариант представления фиксированной позиции,
а разные данные инструмента выбираются отдельной историей. Actions регистрирует
локальные callbacks кнопок и retry.
Для возврата изменённых свойств используйте штатный reset args в Controls;
для повторного монтирования примера — remount в панели Canvas. Размер preview
можно менять без сброса args. Изолированный iframe позволяет отдельно проверить
компонент на узком экране; навигацией самой витрины управляет Storybook.
Preview и приложение используют один public API `shared/theme`: theme,
CSSVariablesResolver и базовые styles вместе с Mantine CSS. Preview использует
русскую i18n и фиксированную светлую схему без чтения сохранённой темы.
Stories находятся рядом с существующими presentation components и импортируют
их реализации. Токены и описания их семантических ролей принадлежат
`shared/theme/tokens.json`; `theme.ts` напрямую адаптирует этот локальный файл
в Mantine, preview не хранит копию токенов. Error StateView сохраняет alert semantics,
skeleton — busy-область с декоративными фигурами без фокуса. Проверяйте доступность
действий и ссылок с клавиатуры вместе с визуальным состоянием.
## Границы и сопровождение ## Границы и сопровождение
Все счета, позиции, события и суммы вымышлены. Stories не читают application Все счета, позиции, события и суммы вымышлены. Каталог не читает сессии,
storage, cookies, секреты или реальные пользовательские данные. Внешние логотипы cookies, секреты и реальные пользовательские данные. Вместо внешних логотипов
не загружаются: примеры используют буквенный fallback существующего компонента. показываются буквенные заглушки. Сама витрина может сохранять свои настройки;
Storybook manager может сохранять собственные настройки витрины; telemetry отключена. телеметрия отключена.
Stories не подключают AuthProvider, QueryClient, API clients и исполняемые VM. Действия не обращаются к application API. Ссылки меняют только локальное
Действия не обращаются к application API. Ссылки меняют только локальный маршрут состояние примера, не открывают продуктовую страницу назначения и не меняют
MemoryRouter: страница назначения не отображается, URL приложения не меняется. адрес приложения. Повторная попытка демонстрирует локальное действие и не
Retry демонстрирует локальный callback и не доказывает восстановление API или доказывает восстановление реального запроса.
полный пользовательский сценарий. Loading/error относятся к выбранному примеру.
Композиция сохранённых сумм показывает представление Client Cached Snapshot; Сохранённые суммы иллюстрируют Client Cached Snapshot, но его жизненный цикл
сам query lifecycle не исполняется. Для запросных экранов действует здесь не исполняется. В приложении первичная загрузка показывает контур
[UI Pattern](../../frontend/.ai/conventions/frontend-ui.md#состояния-и-recovery): сохранение данных при refetch, ожидаемого содержимого, фоновое обновление сохраняет данные, а выполняемое
structural skeleton для initial loading и контекстный индикатор mutation. действие имеет контекстный индикатор. Подробности —
Skeleton конкретной страницы остаётся ответственностью страницы. [состояния интерфейса](../features/frontend-ui-resilience.md).
При изменении включённого компонента проверяйте вместе с ним его пример, Автоматические проверки frontend охватывают каталог и его изоляцию от
описание и ограничения. `npm run verify` включает Storybook в strict TypeScript, продуктовых запросов. Примеры и их пояснения обновляются вместе с включёнными
Biome, Vitest, Knip и dependency-cruiser, затем собирает приложение и статическую представлениями. Клавиатура, реальные размеры и визуальное соответствие
витрину. Основная проверка FSD сохраняется; отдельный `deps:storybook` проверяет проверяются в браузере; автоматической визуальной регрессии нет.
runtime graph stories и preview, запрещая достижимость application data.
Type-only ссылки на типы VM и API стираются и не запускают запросы.
Declarative stories не требуют искусственного unit coverage, а продуктовые
coverage floors сохраняются. Клавиатура, реальные размеры и визуальное
соответствие проверяются в браузере; автоматической visual regression нет.
## Использование в UI задаче ## Использование в UI задаче
Каталог помогает выбрать existing component, состояние и ограничения. Ожидаемое Каталог помогает сравнить существующие состояния с ожидаемым пользовательским
поведение определите из задачи: экраны, states, interactions и узкий viewport. сценарием. Он дополняет [принципы UI-решений](engineering-governance.md#ui-решения)
Для сложного варианта покажите конкретный макет/пример до дорогой реализации; конкретными примерами, но успешная сборка не доказывает доступность,
простой согласованный вариант реализуйте сразу по [UI policy](engineering-governance.md#ui-решения). визуальное соответствие и работу настоящего API.
Story — reference текущей реализации. Сборка не доказывает visual match,
keyboard accessibility или работу настоящего API. Проверяйте соответствие задаче
и применимые states в браузере. Использование установленного каталога не требует
повторного approval установки или OpenSpec Design artifact.
+52 -13
View File
@@ -1,16 +1,55 @@
# Возможности # Возможности
Здесь документируется фактическое поведение завершенных пользовательских фич: пользовательский поток, API-сценарий и ограничения. Не реализованные возможности сюда не добавляются. Карта текущих возможностей по предметным областям. Каждый сценарий описывает
поведение, взаимодействие компонентов и ограничения. Границы системы —
в [обзоре](../architecture/system-overview.md), значения терминов —
в [глоссарии](../architecture/domain-glossary.md).
- [Аутентификация](authentication.md) — регистрация, сессии и refresh. ## Доступ и подключение
- [Подключение T-Bank](tbank-connection.md) — безопасное управление
подключением. Аутентификация связывает локального пользователя с сессиями. Подключение
- [Брокерские счета](brokerage-accounts.md) — список поддерживаемых счетов. T-Bank обеспечивает доступ к брокеру; наличие сессии не заменяет проверку
- [Портфель](portfolio-view.md) — состав инвестиций и freshness states. подключения и доступа к конкретному счёту.
- [Account Ledger](brokerage-account-ledger.md) — исполненные операции счёта.
- [Future Cash Events](brokerage-account-upcoming-events.md) — прогнозируемые | Сценарий | Что объясняет | Ответственность компонентов |
события по текущим позициям. | --- | --- | --- |
- [Пассивный доход брокерского счёта](brokerage-account-income-analytics.md) — | [Аутентификация](authentication.md) | Вход, восстановление и завершение сессии | Backend проверяет credentials и управляет сессиями; frontend хранит текущее состояние входа и выбирает доступные экраны |
исполненные купоны и дивиденды. | [Подключение T-Bank](tbank-connection.md) | Защита token, замена подключения и сброс кэшей | Backend проверяет и шифрует token; frontend показывает подтверждённый статус и обновляет финансовые данные |
- [Frontend UI resilience](frontend-ui-resilience.md) — устойчивые состояния
интерфейса. ## Инвестиционные данные
Сценарии используют текущий состав доступных счетов. Денежные расчёты принадлежат
backend; frontend выбирает представление и формат. Исполненные операции,
полученные выплаты и будущие события имеют разные источники и смысл.
| Сценарий | Что объясняет | Ответственность компонентов |
| --- | --- | --- |
| [Брокерские счета](brokerage-accounts.md) | Доступный состав и отдельные сводки | Backend устанавливает доступ и собирает сводки; frontend показывает каждый счёт и состояние его данных |
| [Портфель](portfolio-view.md) | Позиции, остатки, точные суммы и свежесть | Подсистема портфеля нормализует снимки и рассчитывает итоги; frontend сохраняет различие актуальных и устаревших данных |
| [Все счета](all-accounts.md) | Агрегация, происхождение строк и общая пагинация | Backend объединяет доступные счета по правилам каждого раздела; frontend сохраняет выбранный раздел и фильтры при переключении |
| [Журнал операций](brokerage-account-ledger.md) | Исполненные операции, фильтры и пагинация | Backend получает и нормализует операции брокера; frontend управляет применёнными фильтрами и навигацией |
| [Пассивный доход](brokerage-account-income-analytics.md) | Полученные купоны/дивиденды, периоды и сравнения | Backend вычисляет доход по исполненным выплатам; frontend показывает суммы, периоды и состав расчёта |
| [Будущие выплаты](brokerage-account-upcoming-events.md) | Прогноз по текущим позициям и его ограничения | Backend сопоставляет календарь выплат и позиции; frontend отделяет прогноз от фактически полученного дохода |
## Инструменты
Рыночные данные выпуска отличаются от личной позиции и факта зачисления.
Страницы акций и облигаций используют общий подход к показу графика, позиции
и личных событий, сохраняя различия денежных и процентных шкал.
| Сценарий | Что объясняет | Ответственность компонентов |
| --- | --- | --- |
| [Акции](stock-detail.md) | Поиск, идентификация выпуска, котировки, дивиденды и личная история | Backend разрешает идентификатор и объединяет источники; frontend независимо показывает секции выбранной акции |
| [Облигации](bond-detail.md) | Цена в процентах, купоны, доходности и рейтинг выпуска | Backend сопоставляет данные T-Bank, MOEX и НКР; frontend сохраняет происхождение, время и единицы каждого показателя |
## Состояния интерфейса
[Frontend UI resilience](frontend-ui-resilience.md) объясняет первичную загрузку,
ошибки, пустые результаты и сохранённые клиентом данные. Frontend управляет
доступом к экранам, доступностью запросов и восстановлением отдельных разделов.
Ошибки фонового обновления не должны скрывать уже доступные данные текущего
пользователя; смена сессии или подключения очищает данные прежней границы.
Новая возможность описывается после реализации вместе с ответственными
подсистемами и ограничениями. Порядок обновления знаний —
[ведение документации](../development/documentation.md).
+3 -3
View File
@@ -33,7 +33,7 @@ Backend публикует статические endpoints под `/api/v1/brok
OpenAPI, snapshot и frontend types генерируются из него. OpenAPI, snapshot и frontend types генерируются из него.
- Портфель и доступные денежные остатки суммируются сервером точно, по исходной - Портфель и доступные денежные остатки суммируются сервером точно, по исходной
валюте, через существующую decimal-арифметику. FX-конвертации нет; правила валюте, с точной десятичной арифметикой. FX-конвертации нет; правила
неизвестных значений и состава сумм наследуются от [портфеля](portfolio-view.md). неизвестных значений и состава сумм наследуются от [портфеля](portfolio-view.md).
- Одинаковые бумаги разных счетов показаны одной карточкой с общим названием - Одинаковые бумаги разных счетов показаны одной карточкой с общим названием
и идентификатором. Внутри всегда видны строки счетов: ссылка на портфель, и идентификатором. Внутри всегда видны строки счетов: ссылка на портфель,
@@ -60,8 +60,8 @@ OpenAPI, snapshot и frontend types генерируются из него.
## Полнота, свежесть и доступ ## Полнота, свежесть и доступ
Каждый общий запрос получает состав счетов и единую generation через Для каждого общего запроса backend определяет состав доступных счетов
`TbankAccessService`. Получение данных использует эту же connection boundary; и единое поколение подключения. Получение данных использует эту же connection boundary;
проверки сессии, доступа и существующий лимитер внешних запросов сохраняются. проверки сессии, доступа и существующий лимитер внешних запросов сохраняются.
Финансовые данные не сохраняются в БД; новые зависимости не добавляются. Финансовые данные не сохраняются в БД; новые зависимости не добавляются.
+64 -3
View File
@@ -1,21 +1,82 @@
# Authentication # Authentication
Текущее объяснение локальной identity и сессий WealthLedger. Auth связывает
[User](../architecture/domain-glossary.md#user) с browser session; доступ к
брокеру принадлежит отдельному [T-Bank connection](tbank-connection.md).
Credentials и ротация принадлежат backend, auth state и route decisions — frontend.
## User flow ## User flow
Пользователь регистрируется или входит по email и паролю. Регистрация сразу создаёт первую сессию. Email сохраняется в нормализованном виде; пароль должен содержать от 12 до 72 UTF-8 байтов. Пользователь регистрируется или входит по email и паролю. Регистрация сразу создаёт первую сессию. Внешние пробелы email удаляются, буквы приводятся к нижнему регистру. Backend требует пароль не короче 12 символов и не длиннее 72 UTF-8 байтов.
**Требует ручного решения:** сохранённое требование регистрации и проверка
в форме frontend считают нижнюю границу в UTF-8 байтах, а backend — в символах.
Не-ASCII пароль может пройти форму и получить отказ API. Это описание
наблюдаемого рассогласования, а не принятое изменение password policy;
исправление требует отдельного согласованного security решения и HTTP-проверки.
Access token живёт 15 минут только в памяти браузера. Refresh token живёт 7 дней в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`; при восстановлении сессии он заменяется. После перезагрузки приложение восстанавливает access token через refresh-cookie до показа защищённых маршрутов. Пока результат восстановления неизвестен, frontend показывает нейтральный structural skeleton каркаса без public или protected route content; после завершения bootstrap сохраняются прежние decisions и redirects. Access token живёт 15 минут только в памяти браузера. Refresh token живёт 7 дней в cookie `refresh_token` с `httpOnly` и `SameSite=Lax`; при восстановлении сессии он заменяется. После перезагрузки приложение восстанавливает access token через refresh-cookie до показа защищённых маршрутов. Пока результат восстановления неизвестен, frontend показывает нейтральный structural skeleton каркаса без public или protected route content; после завершения bootstrap сохраняются прежние decisions и redirects.
Все маршруты, кроме `/login` и `/register`, требуют сессию. После входа пользователь возвращается на исходный внутренний маршрут, если он был. Logout доступен в настройках, очищает локальную сессию независимо от состояния сети и отзывает серверную сессию при доступном refresh-cookie. Все маршруты, кроме `/login` и `/register`, требуют сессию. После входа пользователь возвращается на исходный внутренний маршрут, если он был. Logout доступен в настройках, очищает локальную сессию независимо от состояния сети и отзывает серверную сессию при доступном refresh-cookie.
## Восстановление сессии
```mermaid
sequenceDiagram
participant F as React SPA
participant B as NestJS Auth
participant D as SQLite Session
Note over F: Reload: access token в памяти отсутствует
F->>B: Refresh с httpOnly cookie
B->>D: Проверить Session и текущий hash token
alt Действительная сессия, ротация выиграна
B->>D: Атомарно заменить hash и срок
B-->>F: Access token, профиль, новая refresh-cookie
F->>B: Protected request с Bearer
else Cookie нет, token истёк или уже заменён
B-->>F: 401 и очистка cookie
Note over F: Готовое неавторизованное состояние
end
```
В одном экземпляре frontend одновременные refresh используют один незавершённый запрос;
между вкладками используется Web Locks, когда browser его поддерживает.
Backend всё равно защищает ротацию атомарно: browser coordination не заменяет
server-side invariant. Обычный protected запрос с `401 UNAUTHORIZED` проходит
refresh и один повтор через общий transport; auth endpoints из этого цикла
исключены. Сам по себе истёкший access token не запускает фоновый таймер refresh.
## API ## API
`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. Endpoints, статусы и формы публичных ответов определяет backend через OpenAPI;
проверка snapshot описана в [contract workflow](../development/runtime-openapi-verification.md).
Причины хранения и сроков tokens — [ADR 006](../adr/006-auth-session-policy.md).
Refresh-cookie ограничена auth API. Признак Secure зависит от режима окружения:
локальный HTTP предполагает development mode, см.
[локальное окружение](../development/local-workspace.md).
Ошибки 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. Форма ошибок и граница её HTTP-проверки описаны у владельца
[HTTP API](../architecture/http-api.md#error-contract).
HTTP- и транспортная ошибка refresh одинаково завершают cold start готовым неавторизованным состоянием; при refresh после protected `401` frontend очищает in-memory session и React Query cache, сохраняя исходный `401` для существующего login flow.
## Limitations ## Limitations
MVP не включает подтверждение email, восстановление или смену пароля, смену email, удаление аккаунта, список устройств, logout всех устройств или rate limit попыток входа. Access token может оставаться действительным до 15 минут после logout. MVP не включает подтверждение email, восстановление или смену пароля, смену email, удаление аккаунта, список устройств, logout всех устройств или rate limit попыток входа. Access token может оставаться действительным до 15 минут после logout.
Если несколько клиентов одновременно предъявляют один действительный refresh token, сервер завершает одну ротацию успешно, а остальные запросы получают `401`; сессия победившего запроса остаётся действительной. Уже заменённый refresh token также даёт `401`, но сам по себе не отзывает сессию: MVP не хранит историю token или grace-window, чтобы отличить конкурентный запрос от replay. Если несколько клиентов одновременно предъявляют один действительный refresh token, сервер завершает одну ротацию успешно, а остальные запросы получают `401`; сессия победившего запроса остаётся действительной. Уже заменённый refresh token также даёт `401`, но сам по себе не отзывает сессию: MVP не хранит историю token или grace-window, чтобы отличить конкурентный запрос от replay.
Это ограничение и его rationale приняты в
[ADR 009](../adr/009-concurrent-refresh-rotation-policy.md). Refresh token имеет
server-side срок, но cookie не задаёт собственные `maxAge`/`expires`: сохранение
cookie после закрытия браузера не является обещанием семидневного входа.
## Ответственность и проверка
Backend проверяет credentials и tokens, хранит состояние Session и атомарно
выполняет ротацию. Frontend управляет состоянием входа, восстановлением после
reload и доступностью экранов; общий HTTP-транспорт передаёт Bearer token
и выполняет описанный выше refresh/retry.
Проверки охватывают конкурентную ротацию, состояние входа в браузере и поведение
транспорта. Изолированные проверки этих частей не заменяют полный HTTP-поток
и реальную базу при проверке нового изменения безопасности.
+3 -4
View File
@@ -59,7 +59,7 @@ FX-конвертация не выполняется, первоначальн
отсутствия рейтингов других агентств. Покрытие и альтернативы исследованы отсутствия рейтингов других агентств. Покрытие и альтернативы исследованы
в [обзоре источников](../research/bond-credit-ratings.md). в [обзоре источников](../research/bond-credit-ratings.md).
`NcrRatingsClient` разбирает HTML через Cheerio: штатного открытого API для Интеграция НКР читает публичную HTML-таблицу: штатного открытого API для
этой таблицы не подтверждено. Общий публичный реестр кэшируется на шесть часов, этой таблицы не подтверждено. Общий публичный реестр кэшируется на шесть часов,
незавершённые загрузки объединяются; повторные запросы после ошибки ограничены незавершённые загрузки объединяются; повторные запросы после ошибки ограничены
одной минутой. Изменение структуры таблицы, неоднозначные данные и ошибки одной минутой. Изменение структуры таблицы, неоднозначные данные и ошибки
@@ -98,8 +98,7 @@ FX-конвертация не выполняется, первоначальн
## Владельцы и восстановление ## Владельцы и восстановление
`BondController` и `BondService` принадлежат существующему Страницу облигации обслуживает инвестиционная подсистема backend. Все `/api/v1/bonds` endpoints требуют Bearer session
`TbankConnectionModule`. Все `/api/v1/bonds` endpoints требуют Bearer session
и актуального подключения Т-Банка: поиск, разрешение тикера, карточка по UID, и актуального подключения Т-Банка: поиск, разрешение тикера, карточка по UID,
`quote`, `candles`, `position`, `coupons`, `metrics`. Wire contract принадлежит runtime `quote`, `candles`, `position`, `coupons`, `metrics`. Wire contract принадлежит runtime
OpenAPI, frontend snapshot и types генерируются из него. OpenAPI, frontend snapshot и types генерируются из него.
@@ -124,4 +123,4 @@ Instruments client, сохраняющий отсутствие полей. Ры
Ошибка фонового обновления сохраняет последние данные. Смена подключения Ошибка фонового обновления сохраняет последние данные. Смена подключения
сбрасывает subtree `/bonds` вместе с остальными финансовыми запросами; сбрасывает subtree `/bonds` вместе с остальными финансовыми запросами;
logout использует общий session reset. Общие график, позиция и личные события logout использует общий session reset. Общие график, позиция и личные события
переиспользуются страницами акций и облигаций через `instrument-details`. имеют общее представление на страницах акций и облигаций.
@@ -43,7 +43,7 @@ skeleton. Пустой результат объясняет отсутстви
- Только точные upstream types coupon и dividend формируют результат. Суммы остаются exact decimal strings в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и dividend transfer не входят в доход. - Только точные upstream types coupon и dividend формируют результат. Суммы остаются exact decimal strings в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и dividend transfer не входят в доход.
- Итоги, сравнения, buckets и Instrument breakdown не смешивают валюты и не создают общий FX total. На странице показано только сравнение с годом ранее, когда календарная дата и история счёта позволяют. Предыдущий период остаётся в API response, но не отображается. - Итоги, сравнения, buckets и Instrument breakdown не смешивают валюты и не создают общий FX total. На странице показано только сравнение с годом ранее, когда календарная дата и история счёта позволяют. Предыдущий период остаётся в API response, но не отображается.
- Buckets по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые buckets. Страница дохода не строит из них график или текстовую динамику; API response не меняется. - Buckets по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые buckets. Страница дохода не строит из них график или текстовую динамику; API response не меняется.
- Раздел «Выплаты» показывает единый список LedgerEntryRow-карточек в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступная identity и signed Money видны в каждой карточке; валюты по-прежнему разделены в summary/comparisons. Дополнительный запрос за составом не нужен. - Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступная identity и signed Money видны в каждой карточке; валюты по-прежнему разделены в summary/comparisons. Дополнительный запрос за составом не нужен.
Раздел «Годом ранее» показывает цветные панели без общей белой подложки и Раздел «Годом ранее» показывает цветные панели без общей белой подложки и
внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей через внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей через
@@ -65,7 +65,7 @@ skeleton. Пустой результат объясняет отсутстви
серверным правилом; клиент воспринимает ключ как непрозрачную строку. серверным правилом; клиент воспринимает ключ как непрозрачную строку.
Общий Ledger endpoint не приобретает income-specific поле. Общий Ledger endpoint не приобретает income-specific поле.
Income VM проверяет принадлежность entries по полученному ключу до подготовки Frontend проверяет принадлежность выплат по полученному ключу до подготовки
плоского списка выплат. Отсутствующий, пустой или плоского списка выплат. Отсутствующий, пустой или
нестроковый ключ, неоднозначные instrument keys, неизвестная группа, другая нестроковый ключ, неоднозначные instrument keys, неизвестная группа, другая
валюта или несовпадение receipt count приводят к существующему локальному валюта или несовпадение receipt count приводят к существующему локальному
+1 -1
View File
@@ -38,7 +38,7 @@ filters и данные сохраняют существующую семант
- Запрашиваются только исполненные операции T-Bank. - Запрашиваются только исполненные операции T-Bank.
- Полный журнал фильтруется по периоду (два DateInput «С»/«По» с форматом DD.MM.YYYY и min/max-ограничением, русская локаль календаря) и категориям (MultiSelect с мультивыбором из trade, income, tax, fee, transfer, other). Фильтры имеют черновик в контролах и применённое состояние: изменение черновика не вызывает новый запрос к серверу; «Применить» коммитит черновик и сбрасывает cursor-пагинацию на первую страницу; «Сбросить» очищает черновик и немедленно применяет пустой фильтр (период по умолчанию и все категории). Cursor-пагинация («Назад»/«Далее») работает по последнему применённому фильтру, не по черновику. - Полный журнал фильтруется по периоду (два DateInput «С»/«По» с форматом DD.MM.YYYY и min/max-ограничением, русская локаль календаря) и категориям (MultiSelect с мультивыбором из trade, income, tax, fee, transfer, other). Фильтры имеют черновик в контролах и применённое состояние: изменение черновика не вызывает новый запрос к серверу; «Применить» коммитит черновик и сбрасывает cursor-пагинацию на первую страницу; «Сбросить» очищает черновик и немедленно применяет пустой фильтр (период по умолчанию и все категории). Cursor-пагинация («Назад»/«Далее») работает по последнему применённому фильтру, не по черновику.
- В полном журнале и пяти последних записях обзора одна и та же компактная строка показывает логотип инструмента либо буквенный fallback, описание операции, дату, инструмент и денежное движение. Денежная метка строки показывает подпись конкретного типа события: для income-записи — «Купон» или «Дивиденд» (по upstream-типу купона или дивиденда), для остальных — подпись категории («Сделка», «Налог», «Комиссия», «Перевод», «Прочее»). Подпись типа выводится один раз и не повторяется во вторичной метадате (дата · инструмент); направление передаётся знаком «+» / «−` и семантическим цветом, а не текстом. Записи без движения денег остаются нейтральными («Без движения денег»). - В полном журнале и пяти последних записях обзора одна и та же компактная строка показывает логотип инструмента либо буквенный fallback, описание операции, дату, инструмент и денежное движение. Денежная метка строки показывает подпись конкретного типа события: для income-записи — «Купон» или «Дивиденд» (по upstream-типу купона или дивиденда), для остальных — подпись категории («Сделка», «Налог», «Комиссия», «Перевод», «Прочее»). Подпись типа выводится один раз и не повторяется во вторичной метадате (дата · инструмент); направление передаётся знаком «+» / «−` и семантическим цветом, а не текстом. Записи без движения денег остаются нейтральными («Без движения денег»).
- Полный журнал не показывает цену, количество, НКД и комиссию записи. Страница акции переиспользует тот же `LedgerEntryRow` с дополнительными количеством, ценой и ссылкой на счёт. Даты имеют формат ДД.ММ.ГГГГ, время операций явно обозначено UTC. При отсутствии описания используется локализованный тип операции. - Полный журнал не показывает цену, количество, НКД и комиссию записи. Страница акции использует тот же формат строки с дополнительными количеством, ценой и ссылкой на счёт. Даты имеют формат ДД.ММ.ГГГГ, время операций явно обозначено UTC. При отсутствии описания используется локализованный тип операции.
- Backend хранит в памяти процесса до 100 страниц Account Ledger: свежая страница живёт до 30 секунд, а при переполнении вытесняется давно не использовавшаяся страница (LRU). Ключ включает непрозрачную generation текущего T-Bank connection, пользователя, счёт, нормализованные период и categories, cursor и limit; отсутствующий `to` приводится к текущему 30-секундному bucket. Проверка connection и доступа к счёту выполняется до выдачи кэшированной страницы. После успешного создания, замены или удаления connection сохранённая либо поздно завершившаяся страница предыдущей generation недоступна Operations, Income Summary и Income Analytics. Старые страницы удаляются обычными TTL/LRU limits; операции не сохраняются и не синхронизируются локально. - Backend хранит в памяти процесса до 100 страниц Account Ledger: свежая страница живёт до 30 секунд, а при переполнении вытесняется давно не использовавшаяся страница (LRU). Ключ включает непрозрачную generation текущего T-Bank connection, пользователя, счёт, нормализованные период и categories, cursor и limit; отсутствующий `to` приводится к текущему 30-секундному bucket. Проверка connection и доступа к счёту выполняется до выдачи кэшированной страницы. После успешного создания, замены или удаления connection сохранённая либо поздно завершившаяся страница предыдущей generation недоступна Operations, Income Summary и Income Analytics. Старые страницы удаляются обычными TTL/LRU limits; операции не сохраняются и не синхронизируются локально.
- Журнал не рассчитывает остаток после операции, суммарные доходы или налоги, историческую стоимость Portfolio, конвертацию валют и рыночные изменения цены. - Журнал не рассчитывает остаток после операции, суммарные доходы или налоги, историческую стоимость Portfolio, конвертацию валют и рыночные изменения цены.
- `409 TBANK_NOT_CONNECTED`, `400 INVALID_TBANK_TOKEN`, `404` недоступного счёта и `503 TBANK_UNAVAILABLE` используют существующую семантику ошибок T-Bank. - `409 TBANK_NOT_CONNECTED`, `400 INVALID_TBANK_TOKEN`, `404` недоступного счёта и `503 TBANK_UNAVAILABLE` используют существующую семантику ошибок T-Bank.
@@ -32,10 +32,10 @@ sequenceDiagram
заголовка и event rows вместо отдельного spinner. Placeholder не представляет заголовка и event rows вместо отдельного spinner. Placeholder не представляет
данные и не является интерактивным; при пустом результате фильтр остаётся доступным для изменения периода. данные и не является интерактивным; при пустом результате фильтр остаётся доступным для изменения периода.
Page VM владеет account route input, authenticated entity query, выбором Frontend определяет выбранный счёт по адресу страницы, запрашивает данные
loading/error/loaded, пустого результата и retry. Container вызывает один VM и передаёт в рамках текущей сессии и показывает загрузку, ошибку, пустой или успешный
данные существующим state views. Query без account ID остаётся отключённым; результат. Без идентификатора счёта запрос не выполняется. Повторная попытка
выделение VM не меняет порядок выбора состояний или поведение фонового refetch. и фоновое обновление сохраняют правила отображения уже доступных данных.
- По умолчанию период включает текущую календарную дату и следующие 30 дней. Полный список позволяет выбрать другой период полями «С» и «По», как в операциях: «Применить» сохраняет даты в URL и запрашивает события, «Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз на 30 дней. - По умолчанию период включает текущую календарную дату и следующие 30 дней. Полный список позволяет выбрать другой период полями «С» и «По», как в операциях: «Применить» сохраняет даты в URL и запрашивает события, «Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз на 30 дней.
- Суммы до налогов рассчитываются по текущему количеству бумаг на `calculatedAt`; они не учитывают будущие сделки или владение на дату реестра. - Суммы до налогов рассчитываются по текущему количеству бумаг на `calculatedAt`; они не учитывают будущие сделки или владение на дату реестра.
+1 -1
View File
@@ -33,7 +33,7 @@ sequenceDiagram
`GET /api/v1/brokerage-accounts/:accountId/portfolio` требует Bearer access token. Успешный ответ содержит Portfolio Snapshot: `lastSuccessfulAt` в RFC 3339 UTC, `isStale`, `totalsByCurrency`, `positions` и `cashBalances`. `GET /api/v1/brokerage-accounts/:accountId/portfolio` требует Bearer access token. Успешный ответ содержит Portfolio Snapshot: `lastSuccessfulAt` в RFC 3339 UTC, `isStale`, `totalsByCurrency`, `positions` и `cashBalances`.
Все количества и суммы передаются decimal strings без экспоненциальной записи. Деньги передаются как Money `{ currency, value }` с ISO-кодом валюты. Backend выполняет точные операции через `BigInt`; frontend форматирует строки для русской локали и не выполняет финансовую арифметику кроме display-only оценки доли позиции. Все количества и суммы передаются decimal strings без экспоненциальной записи. Деньги передаются как Money `{ currency, value }` с ISO-кодом валюты. Backend выполняет точные денежные операции; frontend форматирует строки для русской локали и не выполняет финансовую арифметику кроме display-only оценки доли позиции.
`Portfolio Position` -- ненулевой обычный актив из T-Bank portfolio. Position с type `CURRENCY` означает торгуемый валютный Instrument и не является Cash Balance. `currentValue` равен количеству, умноженному на текущую цену, когда оба значения и валюта доступны. `absoluteReturn` доступен только при одинаковой известной валюте текущей и средней цены. `instrumentUid` передаётся из upstream для точной идентификации и перехода на [страницу акции](stock-detail.md). `isin` и `ticker` берутся из уже разрешённой metadata Instrument и передаются nullable-строками. Невычислимые значения передаются `null`, а не нулём. Позиции остаются в upstream-порядке; виртуальные позиции не входят в ответ. `Portfolio Position` -- ненулевой обычный актив из T-Bank portfolio. Position с type `CURRENCY` означает торгуемый валютный Instrument и не является Cash Balance. `currentValue` равен количеству, умноженному на текущую цену, когда оба значения и валюта доступны. `absoluteReturn` доступен только при одинаковой известной валюте текущей и средней цены. `instrumentUid` передаётся из upstream для точной идентификации и перехода на [страницу акции](stock-detail.md). `isin` и `ticker` берутся из уже разрешённой metadata Instrument и передаются nullable-строками. Невычислимые значения передаются `null`, а не нулём. Позиции остаются в upstream-порядке; виртуальные позиции не входят в ответ.
+15 -12
View File
@@ -38,7 +38,7 @@ SBER не заменяется на SBERP. Однобуквенные тикер
Денежные показатели используют символ валюты; описание компании видно сразу Денежные показатели используют символ валюты; описание компании видно сразу
под логотипом и названием, перед ценой. Далее идут график, моя позиция, предстоящие под логотипом и названием, перед ценой. Далее идут график, моя позиция, предстоящие
дивиденды и личная история событий. дивиденды и личная история событий.
Логотип использует существующий CDN helper и буквенный fallback. Логотип загружается из публичного CDN; при отсутствии изображения показывается буквенная заглушка.
Позиция отсутствующей или полностью проданной бумаги не создаёт пустого блока; Позиция отсутствующей или полностью проданной бумаги не создаёт пустого блока;
рыночные данные и личная история остаются доступны. рыночные данные и личная история остаются доступны.
@@ -51,8 +51,7 @@ UTC, которое может быть старше времени получе
## API и владельцы ## API и владельцы
Все методы требуют Bearer session и текущее подключение Т-Банка. Все методы требуют Bearer session и текущее подключение Т-Банка.
Новые endpoints принадлежат `StockController` / `StockService` существующего Публичный API страницы акции обслуживает инвестиционная подсистема backend:
`TbankConnectionModule`:
| Метод | Содержимое | | Метод | Содержимое |
| --- | --- | | --- | --- |
@@ -76,7 +75,7 @@ UID включён в ключ кэша и подпись общего курс
восстанавливается переходом на первую страницу с повторным запросом. восстанавливается переходом на первую страницу с повторным запросом.
Сервис портфеля остаётся владельцем снимков и оценки позиций. Новая сводка Сервис портфеля остаётся владельцем снимков и оценки позиций. Новая сводка
фильтрует `STOCK` по точному UID и использует decimal/BigInt, без FX. фильтрует `STOCK` по точному UID и использует точную десятичную арифметику, без конвертации валют.
Средняя цена равна сумме количеств, умноженных на средние цены, делённой на Средняя цена равна сумме количеств, умноженных на средние цены, делённой на
общее количество; она округляется сервером до девяти знаков, проценты — до шести. общее количество; она округляется сервером до девяти знаков, проценты — до шести.
Неполные или разновалютные денежные итоги недоступны. Для нулевого количества Неполные или разновалютные денежные итоги недоступны. Для нулевого количества
@@ -134,9 +133,9 @@ TTM означает последние 12 месяцев, MRQ — послед
## Границы и проверка ## Границы и проверка
Новые RPC проходят существующий `TbankClient`: TLS, rate limit, очередь, Запросы T-Bank проходят общую серверную интеграцию с TLS, лимитом частоты,
deadline и retry. Доступ/расшифровка остаются у `TbankAccessService` и очередью, предельным временем ожидания и повторными попытками. Проверка доступа
`TbankTokenService`. Успешные ответы и совместные незавершённые запросы кэшируются и расшифровка token выполняются только backend. Успешные ответы и совместные незавершённые запросы кэшируются
в bounded LRU на 30 секунд по пользователю, поколению подключения и параметрам. в bounded LRU на 30 секунд по пользователю, поколению подключения и параметрам.
Для поиска `FindInstrument` дополняется сведениями `ShareBy` через тот же Для поиска `FindInstrument` дополняется сведениями `ShareBy` через тот же
кэш карточки инструмента и одним пакетным `GetLastPrices` для списка. Ошибка кэш карточки инструмента и одним пакетным `GetLastPrices` для списка. Ошибка
@@ -148,15 +147,19 @@ deadline и retry. Доступ/расшифровка остаются у `Tban
данными при изменении подключения; logout использует существующий session reset. данными при изменении подключения; logout использует существующий session reset.
Контракт генерируется из runtime Nest OpenAPI, затем обновляются frontend snapshot Контракт генерируется из runtime Nest OpenAPI, затем обновляются frontend snapshot
и generated types. Unit/HTTP/MSW tests проверяют точность, разделение инструментов, и generated types. Проверки отдельных подсистем и HTTP-взаимодействия проверяют точность, разделение инструментов,
фильтрацию и курсоры, валидацию, отсутствие позиции и локальные ошибки. фильтрацию и курсоры, валидацию, отсутствие позиции и локальные ошибки.
Браузерная проверка использует синтетические ответы и широкий/узкий viewport. Браузерная проверка использует синтетические ответы и широкий/узкий viewport.
Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и Полные даты в интерфейсе имеют формат ДД.ММ.ГГГГ; ISO-формат транспорта и
временные зоны расчётов не меняются. Личная история использует общий с портфелем временные зоны расчётов не меняются. Личная история использует общий с портфелем формат строки операции,
`LedgerEntryRow`, дополненный количеством, ценой и ссылкой на счёт. История дополненный количеством, ценой и ссылкой на счёт. История
дивидендов компании раскрывается в оформленном блоке с отдельными карточками дивидендов компании раскрывается в оформленном блоке с отдельными карточками
выплат, датой реестра, доступной датой выплаты и суммой на акцию. выплат, датой реестра, доступной датой выплаты и суммой на акцию.
Реальные ограничения источника описаны в Проверка реальных ответов источника выявила ограничения: отсутствие финансового
[результатах исследования](../../design/stock-detail-api-live-findings.md). поля нельзя считать нулём, время получения не подтверждает дату отчётности,
а налог нельзя однозначно связать с отдельным дивидендом по совпадению даты.
Алгоритм корректировки исторических цен после корпоративных событий не
подтверждён, поэтому точное наложение старых личных сделок на график не обещается.
Выборка проверенных бумаг не доказывает полноту данных всего рынка.
История открытых счетов не обещает полную историю владения, включая закрытые История открытых счетов не обещает полную историю владения, включая закрытые
счета, переводы и универсальные корпоративные события. счета, переводы и универсальные корпоративные события.
+6 -5
View File
@@ -36,7 +36,7 @@ API, logs или errors. Поэтому сохранённый или поздн
После успешного `PUT` или `DELETE` frontend сначала коммитит подтверждённый После успешного `PUT` или `DELETE` frontend сначала коммитит подтверждённый
статус connection, затем сбрасывает все generated GET query в статус connection, затем сбрасывает все generated GET query в
`/api/v1/brokerage-accounts` и `/api/v1/stocks` subtrees. Активные выборки запрашиваются заново, а `/api/v1/brokerage-accounts`, `/api/v1/stocks` и `/api/v1/bonds` subtrees. Активные выборки запрашиваются заново, а
неактивные загружаются при следующем использовании; данные предыдущей границы неактивные загружаются при следующем использовании; данные предыдущей границы
не остаются Client Cached Snapshot. Неуспешная mutation не меняет status, не остаются Client Cached Snapshot. Неуспешная mutation не меняет status,
backend generation или financial cache. Обычная ошибка фонового refetch backend generation или financial cache. Обычная ошибка фонового refetch
@@ -58,9 +58,10 @@ FIFO-ограничитель на экземпляр запускает не б
## Ограничения ## Ограничения
Подключение используется поставленными сценариями Brokerage Accounts, Подключение используется поставленными сценариями Brokerage Accounts,
Portfolio, Account Ledger, Future Cash Events и [страницы акции](stock-detail.md); frontend не обращается к Portfolio, Account Ledger, Future Cash Events, [страницы акции](stock-detail.md)
и [облигации](bond-detail.md); frontend не обращается к
T-Bank напрямую. Token encryption key не ротируется, rate limit остаётся T-Bank напрямую. Token encryption key не ротируется, rate limit остаётся
per-instance, а обновление connection не сериализуется между распределёнными per-instance, а обновление connection не сериализуется между распределёнными
экземплярами backend. Вендорные protobuf-контракты взяты из экземплярами backend. Интеграция использует protobuf-контракт T-Bank Invest API. Автоматическое
`references/moex-vibe`; при необходимости обновляйте их вручную по официальной обновление внешнего протокола не предусмотрено; совместимость новой версии
документации T-Bank Invest API. проверяется при отдельном обновлении интеграции.
+32 -1
View File
@@ -8,7 +8,8 @@
"name": "wealth-ledger-docs", "name": "wealth-ledger-docs",
"version": "1.0.0", "version": "1.0.0",
"devDependencies": { "devDependencies": {
"markdownlint-cli2": "0.23.2" "markdownlint-cli2": "0.23.2",
"markdownlint-rule-relative-links": "5.1.2"
}, },
"engines": { "engines": {
"node": "22.x" "node": "22.x"
@@ -601,6 +602,20 @@
"markdownlint-cli2": ">=0.0.4" "markdownlint-cli2": ">=0.0.4"
} }
}, },
"node_modules/markdownlint-rule-relative-links": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/markdownlint-rule-relative-links/-/markdownlint-rule-relative-links-5.1.2.tgz",
"integrity": "sha512-mskM3EOV7SdAPEfhaLzdtaZ/25S/i6ef0sX6+O2LqqD7JvHgaY1OLVgMb3ZfybD90DFLRhzry3LieF+W/uS3gA==",
"dev": true,
"license": "MIT",
"dependencies": {
"markdown-it": "14.3.0",
"mime": "4.1.0"
},
"engines": {
"node": ">=22.0.0"
}
},
"node_modules/mdurl": { "node_modules/mdurl": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz",
@@ -1168,6 +1183,22 @@
"node": ">=8.6" "node": ">=8.6"
} }
}, },
"node_modules/mime": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz",
"integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==",
"dev": true,
"funding": [
"https://github.com/sponsors/broofa"
],
"license": "MIT",
"bin": {
"mime": "bin/cli.js"
},
"engines": {
"node": ">=16"
}
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+2 -1
View File
@@ -10,6 +10,7 @@
"verify": "npm run lint && git diff --check" "verify": "npm run lint && git diff --check"
}, },
"devDependencies": { "devDependencies": {
"markdownlint-cli2": "0.23.2" "markdownlint-cli2": "0.23.2",
"markdownlint-rule-relative-links": "5.1.2"
} }
} }
+13
View File
@@ -0,0 +1,13 @@
# Исследования
Здесь хранятся наблюдения о внешних источниках и варианты решений. Дата и
ограничения относятся к проведённой проверке; рекомендация не становится
контрактом или реализованной возможностью автоматически.
- [Источники кредитных рейтингов облигаций](bond-credit-ratings.md) — покрытие,
ограничения и выбор первого источника. Текущая интеграция НКР описана в
[странице облигации](../features/bond-detail.md).
Принятые причины решений ищите в [ADR](../adr/README.md), текущее поведение —
в [карте возможностей](../features/README.md). Новое исследование добавляется
в этот индекс после проверки его источников и явного обозначения выводов.