3 Commits
Author SHA1 Message Date
ksv741 65c8bc14d0 chore: sync develop before complexity PR
Docs security / security (push) Failing after 11s
Docs verify / verify (push) Successful in 15s
Conventional commits / commits (pull_request) Successful in 13s
Docs security / security (pull_request) Failing after 10s
Docs verify / verify (pull_request) Successful in 15s
2026-10-03 17:52:07 +03:00
ksv741 e8e7fbd9fc docs: record minimal runtime coordination
Docs security / security (push) Failing after 9s
Docs verify / verify (push) Successful in 12s
Document accepted cross-tab reauthentication, on-demand candles,
native query/logging ownership and the complete deployment path.

Preserve historical ADR context and link amendments through ADR 018.
Related backend change: WealthLedger/backend@d698ed9
2026-10-03 11:41:24 +03:00
ksv741 2bfe87d0e4 docs: record code quality review and refactoring
Docs security / security (push) Successful in 8s
Docs verify / verify (push) Successful in 13s
Conventional commits / commits (pull_request) Successful in 14s
Docs security / security (pull_request) Successful in 7s
Docs verify / verify (pull_request) Successful in 13s
2026-09-27 17:42:35 +03:00
34 changed files with 670 additions and 829 deletions
-15
View File
@@ -1,15 +0,0 @@
---
name: Bug Report
about: Неверное поведение
---
# Bug Report
## Проблема и шаги воспроизведения
## Факт / ожидание
## Acceptance Criteria
- [ ]
## Окружение / ссылки (если применимо)
-13
View File
@@ -1,13 +0,0 @@
---
name: Engineering Task
about: CI, тесты, документация, процесс и прочее
---
# Engineering Task
## Контекст и результат
## Scope / связанные Issues (если есть)
## Acceptance Criteria
- [ ]
-15
View File
@@ -1,15 +0,0 @@
---
name: Feature Request
about: Новое наблюдаемое поведение
---
# Feature Request
## Зачем и какой результат
## Ожидаемое поведение
## Acceptance Criteria
- [ ]
## Зависимости / связанные Issues (если есть)
-15
View File
@@ -1,15 +0,0 @@
---
name: Refactoring / Technical Debt
about: Улучшение без нового поведения
---
# Refactoring / Technical Debt
## Проблема и результат
## Сохраняемые инварианты / границы
## Acceptance Criteria
- [ ]
## Зависимости / ссылки (если есть)
-19
View File
@@ -1,19 +0,0 @@
# Pull Request
## Summary
Краткий результат работы описывается здесь.
## Linked Issue
Refs `<owner/repo#number>` (заменить ссылкой на реальную Issue).
## Changes
Затронутые части и изменения.
## Verification
- Команда / результат / ограничения:
## Breaking Changes (если есть)
+2 -10
View File
@@ -2,11 +2,7 @@ name: Conventional commits
on: on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened, edited]
concurrency:
group: docs-commits-${{ gitea.event.pull_request.number }}
cancel-in-progress: true
jobs: jobs:
commits: commits:
@@ -27,8 +23,4 @@ jobs:
HEAD_SHA: ${{ github.event.pull_request.head.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: | run: |
printf '%s\n' "$PR_TITLE" | npm run commitlint -- --strict printf '%s\n' "$PR_TITLE" | npm run commitlint -- --strict
if [[ "$PR_TITLE" =~ ^chore\(release\):\ [0-9]+\.[0-9]+\.[0-9]+$ ]]; then npm run commitlint -- --strict --from "$BASE_SHA" --to "$HEAD_SHA"
npm run commitlint -- --strict --from "$HEAD_SHA^" --to "$HEAD_SHA"
else
npm run commitlint -- --strict --from "$BASE_SHA" --to "$HEAD_SHA"
fi
+2 -6
View File
@@ -2,16 +2,12 @@ name: Docs security
on: on:
push: push:
branches: [develop, main] branches:
- '**'
pull_request: pull_request:
types: [opened, synchronize, reopened]
schedule: schedule:
- cron: '17 5 * * *' - cron: '17 5 * * *'
concurrency:
group: docs-security-${{ gitea.event_name }}-${{ gitea.event.pull_request.number || gitea.ref }}
cancel-in-progress: true
jobs: jobs:
security: security:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+1 -6
View File
@@ -2,13 +2,8 @@ name: Docs verify
on: on:
push: push:
branches: [develop, main] branches: ['**']
pull_request: pull_request:
types: [opened, synchronize, reopened]
concurrency:
group: docs-verify-${{ gitea.event_name }}-${{ gitea.event.pull_request.number || gitea.ref }}
cancel-in-progress: true
jobs: jobs:
verify: verify:
-6
View File
@@ -1,6 +0,0 @@
{
"extends": ".markdownlint.json",
"MD024": {
"siblings_only": true
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"config": {
"MD013": false
},
"customRules": [
"markdownlint-rule-relative-links"
],
"overrides": [
{
"filter": [
"CHANGELOG.md"
],
"config": {
"MD024": {
"siblings_only": true
}
},
"combine": "merge"
}
]
}
-1
View File
@@ -1 +0,0 @@
module.exports = require('markdownlint-rule-relative-links').default;
-3
View File
@@ -1,3 +0,0 @@
{
"MD013": false
}
+2 -6
View File
@@ -3,9 +3,6 @@
Перед изменением прочитай [README](README.md) и [CONTRIBUTING](CONTRIBUTING.md), Перед изменением прочитай [README](README.md) и [CONTRIBUTING](CONTRIBUTING.md),
затем только затронутую тему. Docs объясняет систему; требования принадлежат затем только затронутую тему. Docs объясняет систему; требования принадлежат
основным спецификациям, реализация и coding conventions — соседним репозиториям. основным спецификациям, реализация и coding conventions — соседним репозиториям.
До edits найди/создай подходящую Gitea Issue, проверь AC и ветку; чтение
допускается без Issue. Общий маршрут — [Engineering Workflow](https://git.personal.ksv741.ru/WealthLedger/main/src/commit/03b3b59e6ff2ba391f9dfc42a039a96d0d84c1c9/.ai/conventions/engineering-workflow.md)
(main#28; после merge — актуальная версия `develop`).
Проверь существенные факты по локальным тематическим страницам и ADR; Проверь существенные факты по локальным тематическим страницам и ADR;
внешние источники используй как справочные свидетельства, когда они доступны. внешние источники используй как справочные свидетельства, когда они доступны.
@@ -23,8 +20,7 @@
## Ветки и релизы ## Ветки и релизы
Работа — в issue-linked ветке от `develop`, затем PR в `develop`; в `main` напрямую Работа — в отдельной ветке от `develop`, затем PR в `develop`; в `main` напрямую
не пушить. Release/hotfix и синхронизация веток — по [README](README.md#релизы). не пушить. Release/hotfix и синхронизация веток — по [README](README.md#релизы).
Commits и заголовки PR — Conventional Commits со ссылкой на Issue (кроме release); Commits и заголовки PR — Conventional Commits; для спецификаций допустим `spec:`.
для спецификаций допустим `spec:`.
Перед подготовкой релиза прочитай местный README; версии вручную не повышать. Перед подготовкой релиза прочитай местный README; версии вручную не повышать.
-36
View File
@@ -1,41 +1,5 @@
# Changelog # Changelog
## [0.1.4](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.3...v0.1.4) (2026-10-12)
### Bug Fixes
* align docs commitlint workflow (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([602eb43](https://git.personal.ksv741.ru/WealthLedger/docs/commit/602eb43294509274cbd9003be31f726b6e275a1e))
### Documentation
* clarify card summary totals ([df6f010](https://git.personal.ksv741.ru/WealthLedger/docs/commit/df6f0109ed696a529cf2622e23fceeab8133f932))
* document hourly attempt lifecycle semantics (WealthLedger/backend[#56](https://git.personal.ksv741.ru/WealthLedger/docs/issues/56)) ([1378ac9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/1378ac9fe2fcfd43bd71421e62b02f00069be928))
* document hourly investment reads ([f0cab44](https://git.personal.ksv741.ru/WealthLedger/docs/commit/f0cab445d7e7076bf92a61f1023b8bf51e094d33))
* document hourly sync history ([e0bceea](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e0bceea936239c08f4717e264be23d7f60552398))
* document stock screener ([2c34023](https://git.personal.ksv741.ru/WealthLedger/docs/commit/2c34023af8593f305f7c81e672b4e179c0992cf3))
* pin docs workflow reference (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([c24e4b9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/c24e4b990aaeffd02e05f7de21942579e452cecf))
### CI
* avoid duplicate PR checks ([4caa5e9](https://git.personal.ksv741.ru/WealthLedger/docs/commit/4caa5e92b7d2597e5c7725b2036007b71ece2abd))
* skip checks on PR metadata edits ([e6fd4f6](https://git.personal.ksv741.ru/WealthLedger/docs/commit/e6fd4f64b81dd73b33959b845a1babcfac1891c8))
### Maintenance
* align docs engineering workflow (WealthLedger/main[#27](https://git.personal.ksv741.ru/WealthLedger/docs/issues/27)) ([eaf7ede](https://git.personal.ksv741.ru/WealthLedger/docs/commit/eaf7ede8414f0df972f9ae7ab9e223a6350b5462))
## [0.1.3](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.2...v0.1.3) (2026-10-07)
### Bug Fixes
* **deps:** refresh docs security baseline ([256d49b](https://git.personal.ksv741.ru/WealthLedger/docs/commit/256d49b04591d55d980a22c49b413432c9bda746))
* **deps:** remove vulnerable docs tooling ([9477e68](https://git.personal.ksv741.ru/WealthLedger/docs/commit/9477e688d180783881abc92f8835517715880279))
### Documentation
* record code quality review and refactoring ([6fa62fb](https://git.personal.ksv741.ru/WealthLedger/docs/commit/6fa62fb8b51aba82766fb50b3e5871572af8be27))
* record minimal runtime coordination ([0ee54ca](https://git.personal.ksv741.ru/WealthLedger/docs/commit/0ee54ca952db6ca7264c0d7fe8cf5759b3187c5b))
## [0.1.2](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.1...v0.1.2) (2026-09-26) ## [0.1.2](https://git.personal.ksv741.ru/WealthLedger/docs/compare/v0.1.1...v0.1.2) (2026-09-26)
### Documentation ### Documentation
+3 -4
View File
@@ -177,10 +177,9 @@ Markdown-only правки не требуют тестов приложения
`security.yml` при push, PR и ежедневно проверяет всю Git-историю через `security.yml` при push, PR и ежедневно проверяет всю Git-историю через
Gitleaks 8.30.1 с redacted-выводом и все npm dependencies через Gitleaks 8.30.1 с redacted-выводом и все npm dependencies через
`npm audit --include=dev --audit-level=low`. Overrides `smol-toml` и `js-yaml` `npm audit --include=dev --audit-level=low`. Override `smol-toml` исправляет
исправляют DoS в parsers markdownlint-cli; overrides для release-it обновляют DoS в TOML parser markdownlint-cli2; его можно удалить, когда родительский
уязвимые basic-ftp и undici. Их можно удалить, когда родительские пакеты пакет примет исправленную версию.
примут исправленные версии.
Renovate объединяет minor/patch updates; major требует согласования в Renovate объединяет minor/patch updates; major требует согласования в
Dependency Dashboard, automerge не включён. Required checks включаются Dependency Dashboard, automerge не включён. Required checks включаются
+3 -5
View File
@@ -35,7 +35,7 @@ WealthLedger помогает частному инвестору понимат
| [Операции](domains/operations/overview.md) | Исполненные операции, фильтры и пагинация | | [Операции](domains/operations/overview.md) | Исполненные операции, фильтры и пагинация |
| [Доход](domains/income/overview.md) | Полученные купоны и дивиденды, периоды и состав расчёта | | [Доход](domains/income/overview.md) | Полученные купоны и дивиденды, периоды и состав расчёта |
| [Будущие выплаты](domains/income/upcoming-events.md) | Календарь по текущим позициям и ограничения прогноза | | [Будущие выплаты](domains/income/upcoming-events.md) | Календарь по текущим позициям и ограничения прогноза |
| [Скринер акций](domains/instruments/stocks.md) и [облигации](domains/instruments/bonds.md) | Подбор акций по параметрам, поиск выпуска, рыночные показатели и личная история | | [Акции](domains/instruments/stocks.md) и [облигации](domains/instruments/bonds.md) | Поиск выпуска, рыночные показатели и личная история |
## Найти материал по задаче ## Найти материал по задаче
@@ -72,11 +72,9 @@ WealthLedger помогает частному инвестору понимат
## Релизы ## Релизы
Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые Репозиторий выпускается независимо. `develop` — интеграция, `main` — принятые
релизы. Обычная работа: issue-linked ветка от актуального `develop`, PR в `develop`; релизы. Работа: `codex/<task>` от актуального `develop`, PR в `develop`.
общие правила — в Engineering Workflow по ссылке из AGENTS.
Обычные PR допускают squash; release и обратная синхронизация сохраняют историю. Обычные PR допускают squash; release и обратная синхронизация сохраняют историю.
Commits и PR titles — Conventional Commits со ссылкой на Issue (кроме release), Commits и PR titles — Conventional Commits, включая `spec:`. Версии вручную не повышай.
включая `spec:`. Версии вручную не повышай.
1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что 1. Получи `main`, `develop` и теги: `git fetch origin --tags`. Убедись, что
`develop` содержит последний релиз. Создай `release/<name>` от проверенного `develop` содержит последний релиз. Создай `release/<name>` от проверенного
@@ -1,8 +1,6 @@
# 017. Локальное чтение данных T-Bank и фоновая синхронизация # 017. Локальное чтение данных T-Bank и фоновая синхронизация
**Status:** Accepted — историческое архитектурное направление. Решения ниже о **Status:** Accepted — архитектурное направление согласовано.
request-driven запуске, ручном обновлении и отсутствии периодического обхода
заменены [ADR 019](019-hourly-reads-and-online-instrument-reference.md).
Prefetch policy ниже заменена [ADR 018](018-minimal-runtime-coordination.md): Prefetch policy ниже заменена [ADR 018](018-minimal-runtime-coordination.md):
свечи загружаются по запрошенному диапазону, без предварительной загрузки свечи загружаются по запрошенному диапазону, без предварительной загрузки
@@ -1,37 +0,0 @@
# 019. Часовой обход и онлайн-справочник инструментов
**Status:** Accepted. Заменяет request-driven, manual-refresh и
no-periodic-walk части [ADR 017](017-local-tbank-reads-and-background-sync.md)
и дополняет [ADR 018](018-minimal-runtime-coordination.md).
## Decision
- Личные T-Bank данные текущего подключения обновляются владельцами источников
через durable hourly walk; его результат ограничен этой connection boundary.
Обычный GET и retry читают только сохранённый результат и не запускают
personal work.
- Только первое открытие неподтверждённого исторического интервала operations
и первого неоткрытого диапазона candles может получить соответственно этот
интервал или диапазон. Это не manual sync; повторное чтение не расширяет и не
запускает такую работу заново.
- Portfolio, operations, forecasts и события сохраняют собственные complete
результаты и время последнего успешного чтения. Результат ресурса
(freshness, completeness, `lastSuccessfulAt`, availability) независим от
resource-relevant последней часовой попытки: поздняя partial/failed попытка
не заменяет сохранённый complete result. При его отсутствии `data: null`.
- Detail, company, catalog search и ticker lookup получают ответ онлайн через
существующие integration clients. Их ошибки не заменяют сохранённые
финансовые данные или подписи snapshot.
- Старые справочные строки не являются источником актуальности и удаляются
только отдельной совместимой миграцией после проверки FK и свечей.
- Клиент повторяет открытые инвестиционные GET не чаще одного раза в 30 минут;
retry повторяет чтение и не создаёт screen sync work. Наблюдение последней
попытки, включая Accounts `sync`, не является командой.
## Consequences
Карточки инструментов зависят от доступности публичного upstream в момент
чтения. Финансовые экраны сохраняют последний успешный snapshot, а статус
последнего hourly walk показывается отдельно. OpenAPI backend остаётся
единственным исполняемым источником HTTP-контракта; frontend использует
опубликованный snapshot.
@@ -1,42 +0,0 @@
# 020. Evidence for hourly sync read boundaries
**Status:** Accepted. Evidence for [ADR 019](019-hourly-reads-and-online-instrument-reference.md).
## Measurements
The backend persisted-read budget was measured with `npm run test:performance`
using a synthetic SQLite dataset: three accounts, 100,000 operations per
account, 200 instruments, and 5,000 candles per instrument. The run used Node
24.21.0 on Apple M1 Pro with 32 GiB RAM and a failing synthetic upstream after
population. It measured 100 reads per route family, excluding transport
limiter cost.
The generated evidence is written by the test to its temporary
`persistent-budget.json` file. This is a repeatable measurement, not a
production SLA: it does not include network latency, real T-Bank quotas, or
multi-process SQLite contention.
The successful run produced a 617,263,104-byte SQLite database and measured
zero upstream calls during the persisted-read phase. Backend p50/p95 response
times were approximately 1.3/2.3 ms for account and portfolio reads,
3.9/4.5 ms for account operations, 6.7/7.2 ms for aggregate operations,
40.0/41.5 ms for account income analytics, and 128.1/134.2 ms for aggregate
income analytics. These figures are local synthetic measurements only.
## Durability
The migration durability check passes against a separately built repository at
the pre-history migration prefix. It verifies preservation of users, sessions,
T-Bank connection data, bank-card data, operations, categories, archive state,
and replay behavior across migration and restart.
The executed command was `node test/bank-cards-durability.cjs` with a separately
built pre-history repository. It completed with `PASS durability`.
## Limits
The configured T-Bank start limiter is `TBANK_RATE_LIMIT_PER_SECOND`, default
`5` per backend instance. The hourly scheduler therefore remains a single
durable reservation owner; the measurement does not justify increasing this
limit or changing the hourly cadence. Any production tuning requires current
T-Bank documentation and a new measurement with the deployed topology.
-2
View File
@@ -23,5 +23,3 @@
- [016 — Версионируемый комплект развёртывания](016-versioned-deployment-bundle.md) - [016 — Версионируемый комплект развёртывания](016-versioned-deployment-bundle.md)
- [017 — Локальное чтение данных T-Bank и фоновая синхронизация](017-local-tbank-reads-and-background-sync.md) - [017 — Локальное чтение данных T-Bank и фоновая синхронизация](017-local-tbank-reads-and-background-sync.md)
- [018 — Минимальная runtime-координация](018-minimal-runtime-coordination.md) - [018 — Минимальная runtime-координация](018-minimal-runtime-coordination.md)
- [019 — Часовой обход и онлайн-справочник инструментов](019-hourly-reads-and-online-instrument-reference.md)
- [020 — Evidence for hourly sync read boundaries](020-hourly-sync-verification-evidence.md)
+3 -3
View File
@@ -195,9 +195,9 @@ access token и вращаемый refresh token. [Политика query-кэш
Авторизация доступа, недопущение token в публичный ответ и атомарность Авторизация доступа, недопущение token в публичный ответ и атомарность
ротации сессии — отдельные свойства; наличие типизированного клиента ротации сессии — отдельные свойства; наличие типизированного клиента
не доказывает их. Известное расхождение регистрации отмечено в не доказывает их. Известные расхождения регистрации и деталей валидации
[аутентификации](../domains/authentication/overview.md#регистрация-и-вход), отмечены в [аутентификации](../domains/authentication/overview.md#регистрация-и-вход)
а действующий формат ошибок описан в [HTTP API](http-api.md#error-contract). и [HTTP API](http-api.md#error-contract).
Поддерживается локальный Compose и ограниченная single-server topology с SQLite. Поддерживается локальный Compose и ограниченная single-server topology с SQLite.
Origin браузера, адрес API и cookie policy должны быть согласованы. Origin браузера, адрес API и cookie policy должны быть согласованы.
+35 -56
View File
@@ -1,72 +1,51 @@
# Свежесть данных и повторное использование # Свежесть данных и повторное использование
WealthLedger читает личные инвестиционные данные из SQLite. Обычный GET и его WealthLedger читает инвестиционные данные из SQLite. Обычный GET возвращает
HTTP retry возвращают сохранённый результат и не запускают личную работу с сохранённый результат и запрашивает фоновые зависимости, если они отсутствуют
T-Bank. Нормальным владельцем обновления служит часовой обход текущего или устарели. HTTP-ответ не ждёт T-Bank. Модель принята в
подключения: после успешного подключения он начинает первую попытку, затем [ADR 017](../../adr/017-local-tbank-reads-and-background-sync.md).
начинает следующую не ранее чем через 60 минут от начала предыдущей. После
простоя выполняется не более одна актуальная попытка, а перекрывающийся срок
пропускается, не ставится в очередь. Общие справочные данные инструментов
запрашиваются онлайн, когда это требуется карточке; финансовый HTTP-ответ не
ждёт T-Bank. Модель принята в
[ADR 019](../../adr/019-hourly-reads-and-online-instrument-reference.md).
## Клиентский и серверный снимки ## Клиентский и серверный снимки
[Client Cached Snapshot](../../glossary.md#client-cached-snapshot) — успешный [Client Cached Snapshot](../../glossary.md#client-cached-snapshot) — успешный
ответ в TanStack Query. Он сохраняет показанное содержимое при временной ошибке ответ в TanStack Query. Он сохраняет показанное содержимое при временной ошибке
HTTP-перечитывания в той же границе доступа. Это не серверный fallback и не запроса в той же границе доступа. SQLite сохраняет источники независимо от
состояние часового обхода. SQLite независимо от браузера хранит membership, браузера: membership, последний полный portfolio, исполненные операции,
последний полный portfolio, исполненные операции, прогнозы, общие сведения об общие сведения об инструментах, котировки, свечи и календари.
инструментах, котировки, свечи и календари.
[Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются. [Полнота периода](../../glossary.md#полнота-истории-счёта) и свежесть различаются.
Успешно проверенный пустой результат полон и доступен; последняя страница Успешно проверенный пустой интервал полон; последняя страница журнала сама
журнала сама по себе полноту не доказывает. Результат каждого личного ресурса по себе полноту не доказывает. Ошибка сверки сохраняет прежние данные и время.
в `{ data, meta }` описывают пять независимых значений: freshness, Старый полный портфель не удаляется по истечении TTL. `isStale` показывает
completeness, `lastSuccessfulAt`, `lastAttempt` и availability. Первые, возраст portfolio source, а не сам факт неудачного обновления.
полнота и `lastSuccessfulAt` относятся только к возвращённому подтверждённому
результату. `lastAttempt` описывает относящуюся к ресурсу часовую попытку:
`none`, `running`, `succeeded`, `partial` или `failed`.
Итог попытки выводится из фактических результатов её шагов: попытка, не | Источник | Когда обычный запрос требует обновления |
завершившая обязательный шаг, не обозначается полным успехом, а полученные
успешные результаты сохраняются. Ожидание каждого шага ограничено; зависший
шаг учитывается проблемным, а начатое обращение к T-Bank не отменяется.
Попытка, потерявшая выполнение после сбоя сохранения или остановки процесса,
закрывается частичным успехом либо неуспехом и не блокирует следующий обход
дольше обычного часового срока.
Поздняя неуспешная или частичная попытка сохраняет прежний полный результат и
его исходное время. Если полного результата нет, `data` равно `null`: состояние
availability объясняет первоначальную загрузку, временную недоступность либо
недоступность подключения. Пустой массив не означает отсутствие результата.
Старый полный portfolio не удаляется из-за возраста. `Portfolio.isStale`
сохраняет смысл возраста его snapshot, а не обозначает неуспех обхода или
клиентскую ошибку перечитывания.
| Источник | Владелец нормального обновления |
| --- | --- | | --- | --- |
| Membership, portfolio, изменяемый хвост операций и события | Часовой обход текущего подключения | | Portfolio, котировки, изменяемый хвост дневных свечей | Через 60 секунд |
| Котировки и изменяемый хвост дневных свечей | Установленная политика источника; не ordinary personal GET | | Membership и изменяемый хвост операций | Через 5 минут |
| Metadata, поиск каталога и company | Онлайн при чтении | | Metadata, поиск каталога, company и календари | Через сутки |
| Полные прошлые интервалы операций и завершённые свечи | Не обновляются часовым обходом или обычным GET | | Полные прошлые интервалы операций и завершённые свечи | При явном обновлении показанного периода |
Два исключения разрешают только первое получение отсутствующего результата: TTL не создаёт таймер импорта. Запрошенная работа может закончиться после
первое открытие неподтверждённого исторического интервала операций получает ухода со страницы. После перезапуска нет автоматического обхода intent:
только этот интервал и возвращает локальные строки с неполным покрытием; первое следующий подходящий запрос повторяет незавершённый интервал с первой страницы,
открытие диапазона свечей без подтверждённого локального результата получает переиспользуя полные интервалы. Cursor брокера не служит durable checkpoint.
только этот диапазон. Ни одно исключение не обновляет membership, portfolio,
события, другой интервал или диапазон. Их HTTP retry также только перечитывает
локальное состояние. Подтверждённый полный интервал или диапазон остаётся
локальным.
Часовой обход сохраняет успешные разделы независимо, а failed section не Ручное «Обновить» перечитывает зависимости текущего экрана и применённый
заменяет прежний complete result. Отменой query lifecycle владеет TanStack период независимо от TTL. Повторная сверка полного периода публикуется одной
Query. Открытый экран перечитывает ресурс не чаще одного раза в 30 минут; транзакцией после всех страниц, интервалов и счетов, включая `all`.
это локальный HTTP read. Совместимый параметр `sync=observe`, пока он есть в Сбой сохраняет весь прежний результат. Первая загрузка может публиковать
маршруте, также является только чтением и не представляет команду обходу. полные малые интервалы и показывать частичный период без окончательных сумм.
[HTTP API](../http-api.md) описывает исполняемый контракт envelope.
Клиент наблюдает только известную активную работу открытого экрана.
Status GET и data GET с `sync=observe` не запускают работу. После изменения
состояния ответ попадает в ту же query identity; после ухода или завершения
наблюдение прекращается. Поздний ответ не заменяет уже принятый новый.
[HTTP API](../http-api.md) объясняет envelope и экранный refresh.
Отменой query lifecycle владеет TanStack Query. Status не пересобирает
финансовые ответы и не передаёт hash notifications. Свечи нового диапазона
загружаются при его открытии; предзагрузка всей истории портфеля не выполняется.
## Смена подключения ## Смена подключения
+30 -37
View File
@@ -2,8 +2,7 @@
## Источник контракта ## Источник контракта
Backend генерирует OpenAPI из собственного публичного API. Этот сгенерированный Backend генерирует OpenAPI из собственного публичного API. Frontend использует
OpenAPI — исполняемый источник HTTP-контракта. Frontend использует
предоставленный OpenAPI snapshot как актуальный вход и локально генерирует предоставленный OpenAPI snapshot как актуальный вход и локально генерирует
типизированный клиент. Backend владеет контрактом, а подготовка и доставка типизированный клиент. Backend владеет контрактом, а подготовка и доставка
схемы находятся вне frontend. Это разделение принято в схемы находятся вне frontend. Это разделение принято в
@@ -21,40 +20,29 @@ artifact у себя и проверяет соответствие generated ty
API имеет prefix `/api/v1`; Swagger публикуется для локальной разработки по API имеет prefix `/api/v1`; Swagger публикуется для локальной разработки по
environment policy, а не используется frontend в runtime. environment policy, а не используется frontend в runtime.
## Скринер акций
`POST /api/v1/stocks/screener/search` всегда создаёт новый результат по
условиям скринера, включая пустой подтверждённый список. Ответ первой страницы
содержит непрозрачный `searchId`, время получения каталога и признак следующей
страницы. `GET /api/v1/stocks/screener/search/{searchId}?page=...` читает
последующую страницу того же результата, не запускает новый поиск и сохраняет
состав и время списка. Идентификатор результата принадлежит текущему пользователю
и его подключению; недоступный или чужой результат возвращает ошибку, а не
пустой список. Точные поля и статусы определяет backend-generated OpenAPI.
## Инвестиционные данные и синхронизация ## Инвестиционные данные и синхронизация
Затронутые GET инвестиционных ресурсов возвращают сгенерированный contract T-Bank data GET возвращают envelope `{ data, meta }`. `data` может быть `null`
`{ data, meta }`. `data` nullable: `null` означает отсутствие complete result, при missing или неполном финансовом агрегате; partial ledger/candles могут
а подтверждённый пустой массив является available complete result. `meta` содержать подтверждённые строки. `meta` разделяет availability, freshness,
независимо описывает freshness, completeness, `lastSuccessfulAt`, lastSuccessfulAt, sync, coverage и состояния зависимостей. `dataRevision`
resource-relevant `lastAttempt` и availability. `lastAttempt` имеет состояния описывает набор данных; пустое завершённое покрытие явно представлено в coverage.
`none`, `running`, `succeeded`, `partial` и `failed`; его неуспех не заменяет Публичной hash-версии состояния нет. Ни одно поле не раскрывает connection revision.
сохранённый complete result или его время. `Portfolio.isStale` остаётся
специфичным для snapshot полем, а не заменой состояния попытки.
Только Accounts дополнительно возвращает top-level `sync`: `null`, если у Обычный GET планирует missing/stale зависимости после проверки доступа.
текущего подключения ещё не было часовой попытки, иначе connection-wide GET с `sync=observe` только читает. `POST /api/v1/tbank-sync` принимает
наблюдаемый результат. Это наблюдение, не команда. Обычный GET и retry после валидированный screen descriptor и возвращает `202` с handle и нормализованным
проверки доступа читают локальное состояние и не начинают личную работу. scope. `GET /api/v1/tbank-sync/:id` наблюдает разрешённую работу; чужой или
Устаревший `sync=observe`, пока он сохранён для совместимости маршрута, также прежний handle возвращает `404`. Приём работы не означает её успех.
только читает. Request-started screen-sync command/status не является текущей
AS-IS моделью.
Безопасные ошибки завершённой часовой попытки передаются в `lastAttempt` и Status возвращает state/errorCode без данных ресурсов и численных progress
Accounts `sync`; HTTP transport/access/input errors сохраняют Problem Details. counters. Клиент перечитывает показанные resources через `sync=observe` во время
Старые данные при временной source failure сохраняют исходное успешное время. работы и после завершения, сохраняя generated query identity. Sync states ресурса:
Ни одно поле не раскрывает connection revision. Детали поведения — `idle`, `running`, `failed`; отдельного публичного `queued` нет.
Source failure принятой работы передаётся безопасным `meta.sync.errorCode`;
HTTP transport/access/input errors сохраняют Problem Details. Старые данные
при transient failure сохраняют исходное успешное время. Детали поведения —
[свежесть и полнота](cross-cutting/data-freshness.md). [свежесть и полнота](cross-cutting/data-freshness.md).
Envelope несовместим с прежними bare-array ответами: backend/frontend Envelope несовместим с прежними bare-array ответами: backend/frontend
@@ -82,11 +70,16 @@ message/stack. Типичный ответ:
Для input validation контракт требует `400`, `VALIDATION_ERROR` и `errors` как Для input validation контракт требует `400`, `VALIDATION_ERROR` и `errors` как
массив `{ pointer, detail }`, где pointer адресует поле запроса. Это требование массив `{ pointer, detail }`, где pointer адресует поле запроса. Это требование
проверяется реальным HTTP-ответом после всех стадий обработки запроса. Глобальная не считается проверенным одним описанием OpenAPI: нужна проверка реального
валидация передаёт фильтру структурированные ошибки без исходных значений и DTO, HTTP-ответа после всех стадий обработки запроса.
а фильтр формирует JSON Pointer, включая вложенные поля. HTTP suites подтверждают
детали ошибок регистрации, банковской карты и синхронизации; OpenAPI отдельно **Известное расхождение:** согласованная
фиксирует форму ответа. [спецификация аутентификации](https://git.personal.ksv741.ru/WealthLedger/specs/src/commit/9e227a563dda67620f0ad78228aa8a4c18bf35a0/openspec/specs/user-authentication/spec.md#requirement-стандартные-ответы-об-ошибках)
требует `pointer` и `detail` для невалидных полей. В текущей конфигурации
валидация формирует строковые сообщения, а фильтр ожидает структурированные
ошибки полей; поэтому корректность деталей `errors` не подтверждена.
Исправление требует отдельного изменения реализации и проверки полного
HTTP-потока. OpenAPI сам по себе этого расхождения не устраняет.
Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`). Ответ не включает автоматически все поля RFC (`type`, `title`, `instance`).
Добавление обязательного поля является изменением публичного контракта. Добавление обязательного поля является изменением публичного контракта.
+1 -9
View File
@@ -1,12 +1,4 @@
module.exports = { module.exports = {
extends: ["@commitlint/config-conventional"], extends: ["@commitlint/config-conventional"],
defaultIgnores: false, rules: { "type-enum": [2, "always", ["feat", "fix", "perf", "docs", "spec", "refactor", "build", "ci", "test", "style", "chore", "revert"]] },
ignores: [
message => /^chore\(release\): \d+\.\d+\.\d+$/.test(message.trim()),
message => /^Merge pull request ['"].+['"] \(#\d+\) from .+ into .+$/.test(message.trim()),
],
rules: {
"type-enum": [2, "always", ["feat", "fix", "perf", "docs", "spec", "refactor", "build", "ci", "test", "style", "chore", "revert"]],
"references-empty": [2, "never"],
},
}; };
+3 -2
View File
@@ -89,8 +89,9 @@ JavaScript Number. Отдельный ввод ограничен signed64: на
следующие страницы. Баланс при выборе прошлого периода остаётся текущим. следующие страницы. Баланс при выборе прошлого периода остаётся текущим.
Сводка учитывает все подходящие записи, включая не показанные на странице. Сводка учитывает все подходящие записи, включая не показанные на странице.
Сводка одной или всех карт отдельно показывает расходы, возвращённую сумму и Сводка одной или всех карт показывает одну итоговую сумму расходов с учётом
итог после возвратов, а также компактные строки категорий по убыванию итоговых расходов. Возврат уменьшает расходы месяца своего возвратов и компактные строки категорий по убыванию итоговых расходов. Отдельные показатели «Возвращено»
и «После возвратов» в интерфейсе скрыты; расчёт и операции возврата сохранены. Возврат уменьшает расходы месяца своего
получения: августовская покупка 1 000 ₽ и сентябрьский возврат 600 ₽ дают получения: августовская покупка 1 000 ₽ и сентябрьский возврат 600 ₽ дают
августовский итог 1 000 ₽ и сентябрьский −600 ₽ при отсутствии других трат. августовский итог 1 000 ₽ и сентябрьский −600 ₽ при отсутствии других трат.
Начальный остаток, пополнения и переводы в эти показатели не входят. Начальный остаток, пополнения и переводы в эти показатели не входят.
+23 -30
View File
@@ -40,10 +40,10 @@
## Период и список ## Период и список
Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней
после начала, обе даты включены. `from` и `to` принимают только строгие даты после начала, обе даты включены. Поля «С» и «По» позволяют изменить период:
`YYYY-MM-DD`; без `from` используется текущая дата UTC, без `to` — `from + 30` «Применить» сохраняет выбранные календарные даты в URL и запрашивает результат,
календарных дней. Поля «С» и «По» сохраняют выбранные календарные даты в URL, «Сбросить» возвращает период по умолчанию. Обзор счёта сохраняет прогноз
а «Сбросить» возвращает период по умолчанию. на текущую дату и следующие 30 дней.
События упорядочены по календарной дате. У каждой строки видны тип, События упорядочены по календарной дате. У каждой строки видны тип,
инструмент, дата и доступная сумма в исходной валюте. Список не имеет инструмент, дата и доступная сумма в исходной валюте. Список не имеет
@@ -52,13 +52,10 @@
отсутствия всех будущих выплат. Инструмент без поддерживаемых данных отсутствия всех будущих выплат. Инструмент без поддерживаемых данных
не получает искусственную строку «нет данных». не получает искусственную строку «нет данных».
Сохранённый forecast сохраняет свой `calculatedAt` и исходное включительное API принимает необязательные `from` и `to` в формате `YYYY-MM-DD` для
окно. GET для отдельного счёта или `all` возвращает только пересечение этого отдельного и общего счёта. Без `from` используется сегодня в UTC,
окна и запрошенного UTC-периода, не меняя ни `calculatedAt`, ни исходное окно. без `to` — 30 дней после начала. Неверный формат или несуществующая дата
Пустое пересечение или отсутствие событий в пересечении — успешный пустой дают `400`; обратный период — `400 INVALID_EVENTS_PERIOD`.
список с теми же metadata; это не отсутствие forecast. Неверный формат или
несуществующая дата дают `400`; обратный период — `400 INVALID_EVENTS_PERIOD`
без замены периода и без запуска личной работы.
Фильтрация на сервере предшествует сортировке и вычислению Фильтрация на сервере предшествует сортировке и вычислению
`incomeTotalsByCurrency`, включающего только купоны и дивиденды. `incomeTotalsByCurrency`, включающего только купоны и дивиденды.
Итоги остаются в ответе API; полный экран показывает список без общего итога. Итоги остаются в ответе API; полный экран показывает список без общего итога.
@@ -66,30 +63,26 @@
## Получение и отказы ## Получение и отказы
Запрос требует сессии, подключения и локально подтверждённого membership. Запрос требует сессии, подключения и локально подтверждённого membership.
Он читает сохранённый forecast и не запускает личную синхронизацию. Часовой Прогноз строится из личного сохранённого portfolio и общих календарей: T-Bank для
обход строит forecast из полного личного portfolio и общих календарей: T-Bank дивидендов, MOEX для bond events. Отдельного синхронного получения portfolio
для дивидендов, MOEX для bond events. Личные оценки сохраняются отдельно от нет. Личные оценки сохраняются отдельно от общих календарей.
общих календарей.
Последний полный forecast привязан к scope, исходному окну и membership. Если Последний полный forecast привязан к scope, периоду и membership.
часовой обход не может получить полный новый forecast, прежний полный прогноз, Если новый portfolio требует ещё недоступного календаря, прежний полный
его `calculatedAt` и исходное окно остаются читаемыми, в том числе после прогноз и его source times остаются, в том числе после рестарта. Новый
рестарта; `meta.lastAttempt` отдельно сообщает об ошибке обновления. Без полный результат заменяет его атомарно. Без fallback показывается
полного forecast `data: null` и metadata обозначает initial loading либо missing/partial, а не подтверждённое отсутствие выплат. Другой период или
временную недоступность, а не подтверждённое отсутствие выплат. В режиме `all` изменившийся membership не получают старый прогноз под новым названием.
результат complete только при полном вкладе каждого включённого счёта. Другой Source failure сообщается в metadata и не изменяет успешное время.
scope или изменившийся membership не получают старый forecast под новым
названием.
Успешный ответ MOEX без запрошенного блока означает отсутствие поддерживаемых Успешный ответ MOEX без запрошенного блока означает отсутствие поддерживаемых
событий этого источника; это отличается от transport failure и повреждённого событий этого источника; это отличается от transport failure и повреждённого
формата ответа. формата ответа.
При первом открытии без complete forecast показан каркас заголовка и строк, При первом открытии показан каркас заголовка и строк, без интерактивных
без интерактивных заглушек. Пустой успешный ответ сохраняет возможность заглушек. Пустой ответ сохраняет возможность изменить период.
изменить период. При failed HTTP reread уже полученный Client Cached Snapshot При обычном фоновом обновлении уже полученные данные остаются на экране,
остаётся на экране; это отличается от server-side failed hourly attempt. ошибка получает предупреждение и повторную попытку. Без выбранного счёта
Повторная попытка перечитывает только HTTP-ответ. Без выбранного счёта запрос запрос не выполняется. Смена сессии и подключения следует
не выполняется. Смена сессии и подключения следует
[общим правилам финансовых данных](../../architecture/cross-cutting/data-freshness.md). [общим правилам финансовых данных](../../architecture/cross-cutting/data-freshness.md).
Внешние границы объясняют интеграции [T-Bank](../../integrations/tbank.md) Внешние границы объясняют интеграции [T-Bank](../../integrations/tbank.md)
-6
View File
@@ -143,9 +143,3 @@ BondBy переиспользуется зависимостями выпуск
и [проверками](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond.spec.ts). и [проверками](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/bonds/application/bond.spec.ts).
Границы источников описаны в [разделе интеграций](../../integrations/README.md), Границы источников описаны в [разделе интеграций](../../integrations/README.md),
сходное разделение рыночных и личных данных — в [акциях](stocks.md). сходное разделение рыночных и личных данных — в [акциях](stocks.md).
## Online reference
Bond detail, search, and ticker lookup are read from the upstream integration
on each request. Saved quotes, candles, events, ratings, and portfolio labels
retain their own freshness and failure boundaries.
+19 -32
View File
@@ -28,29 +28,24 @@
на frontend. Общее для акций и облигаций восстановление справочных данных на frontend. Общее для акций и облигаций восстановление справочных данных
принадлежит [загрузке портфеля на backend](../portfolio/flows/portfolio-loading.md). принадлежит [загрузке портфеля на backend](../portfolio/flows/portfolio-loading.md).
Раздел «Акции» в боковой и мобильной навигации ведёт на `/stocks`: скринер Раздел «Акции» в боковой и мобильной навигации ведёт на `/stocks`: поиск
запускается кнопкой «Найти» с необязательным названием или тикером, сектором, по названию или тикеру через каталог Т-Банка, начиная с двух символов.
типом акции, валютой расчётов, площадкой, страной риска и максимальным размером Результаты разных выпусков, включая обыкновенные и привилегированные акции,
лота. Он использует полный полученный каталог и показывает только акции, для имеют отдельные UID. Карточка результата показывает логотип, последнюю цену
которых каталог подтверждает доступность покупки и торговли через API. Это не с её временем, тип бумаги, площадку, код режима, ISIN и доступность торгов через
гарантирует, что конкретная заявка пользователя будет исполнена прямо сейчас. API. Поиск показывает одну карточку на нормализованный ISIN. До ограничения
в 30 результатов выбирается представитель выпуска: доступность через API,
Скринер показывает одну карточку на выпуск с нормализованным ISIN, а выпуск точное совпадение тикера, затем режим TQBR; равные варианты упорядочены по UID.
без ISIN остаётся отдельной карточкой точного UID. Обыкновенные и Записи без ISIN сохраняются отдельно по UID. Обыкновенные и привилегированные
привилегированные акции с разными ISIN не объединяются. После отбора карточки акции с разными ISIN не объединяются. Цена и ссылка принадлежат выбранному
упорядочены по тикеру и UID, разделены на страницы по 30 и показывают единое UID; данные разных режимов не смешиваются. Прямые ссылки из портфеля сохраняют
время получения списка. Изменение условий не заменяет текущий список до
следующего нажатия «Найти». Повторный поиск получает новый каталог; страницы
одного результата не пересчитываются. Подтверждённый пустой результат отличается
от ошибки источника, для которой интерфейс предлагает повторить поиск.
Ссылка карточки принадлежит выбранному UID; данные разных режимов не смешиваются.
Прямые ссылки из портфеля сохраняют
исходный UID, даже если поиск выбрал другой режим этого выпуска; такая страница исходный UID, даже если поиск выбрал другой режим этого выпуска; такая страница
сохраняет UUID в адресе. Если один точный тикер относится к разным выпускам, сохраняет UUID в адресе. Если один точный тикер относится к разным выпускам,
сервер возвращает `409 STOCK_TICKER_AMBIGUOUS`. сервер возвращает `409 STOCK_TICKER_AMBIGUOUS`, а страница предлагает поиск.
Поиск тикера нечувствителен к регистру, но использует точное совпадение:
SBER не заменяется на SBERP. Однобуквенные тикеры также поддерживаются.
Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки Прямая ссылка не требует предварительного открытия портфеля. Хлебные крошки
«Акции → название» возвращают к скринеру. «Акции → название» возвращают к поиску.
## Содержание и состояния ## Содержание и состояния
@@ -149,11 +144,10 @@ TTM означает последние 12 месяцев, MRQ — послед
## Получение данных и ограничения ## Получение данных и ограничения
Общие metadata, discovery, company, quotes, свечи и календари сохраняются Общие metadata, discovery, company, quotes, свечи и календари сохраняются
в SQLite. Скринер получает каталог online и показывает время его получения; в SQLite. Поиск хранит подтверждённый набор UID для конкретного query;
временная недоступность каталога не подтверждает отсутствие подходящих акций. успешный SBER не подтверждает пустой GAZP. Отсутствующее enrichment не
Отсутствующее enrichment не скрывает найденную бумагу. Сохранённые общие скрывает найденную бумагу. Сохранённые общие источники переиспользуются
источники переиспользуются между пользователями, но личные позиции и выплаты между пользователями, но личные позиции и выплаты всегда проверяют доступ.
всегда проверяют доступ.
Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне
через существующий T-Bank limiter. Минутная свежесть применяется к quotes через существующий T-Bank limiter. Минутная свежесть применяется к quotes
@@ -190,10 +184,3 @@ scope. Частичные строки видны с предупреждени
[рыночным данным](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-market.service.ts), [рыночным данным](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-market.service.ts),
[выплатам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-income.service.ts) и [проверкам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock.spec.ts). [выплатам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock-income.service.ts) и [проверкам](https://git.personal.ksv741.ru/WealthLedger/backend/src/commit/016782657acfd35ff3fd755462007938fac35156/src/modules/investments/instruments/stocks/application/stock.spec.ts).
Сопоставление с облигациями — на [странице облигаций](bonds.md). Сопоставление с облигациями — на [странице облигаций](bonds.md).
## Online reference
Stock detail, company data, search, and ticker lookup are read from the
upstream integration on each request. Saved positions, quotes, candles, and
events remain independent local resources and are not replaced by a reference
read failure.
+31 -32
View File
@@ -1,47 +1,46 @@
# Загрузка портфеля # Загрузка портфеля
Экран `/accounts/:accountId` читает последний полный snapshot из SQLite после Экран `/accounts/:accountId` читает последний полный snapshot из SQLite
проверки сессии, подключения и локального membership. Если список счетов ещё после проверки сессии, подключения и локального membership. Если список
неизвестен, сервер сообщает о загрузке доступа без выдачи личных данных счетов ещё неизвестен, сервер сообщает о загрузке доступа без выдачи личных
неизвестного ID. Известный недоступный счёт возвращает `404`. данных неизвестного ID. Известный недоступный счёт возвращает `404`.
## Основной обмен ## Основной обмен
Обычный GET и retry возвращают read-only локальный `{ data, meta }` envelope: Обычный GET возвращает локальный envelope. Missing/stale зависимости
они не ждут и не вызывают T-Bank и не предлагают ручную синхронизацию. Часовой запрашиваются в фоне; чтение не ждёт T-Bank. Для нового полного snapshot
обход текущего подключения обновляет Portfolio независимо от экрана. Для нового нужны оба финансовых ответа `GetPortfolio` и `GetPositions`. Суммы и позиции
полного snapshot ему нужны оба финансовых ответа `GetPortfolio` и публикуются вместе; metadata дополняет представление независимо, с доступным
`GetPositions`; суммы и позиции публикуются вместе. Metadata инструмента UID/FIGI вместо имени до её загрузки.
дополняет представление независимо, с доступным UID/FIGI вместо имени до её
онлайн-загрузки.
Открытый экран выполняет локальное перечитывание не чаще одного раза в Свежесть portfolio — 60 секунд. Истечение срока не удаляет snapshot и не
30 минут; retry также повторяет только HTTP GET. `data: null` означает, что создаёт таймер. «Обновить» на обзоре перечитывает зависимости текущего экрана:
полного snapshot ещё нет, а не нулевой портфель. Пустой подтверждённый portfolio membership, portfolio, показанные операции, доход и будущие события.
остаётся available complete result. Секции могут быть готовы независимо. Секции могут быть готовы независимо. Подробнее —
Подробнее — [свежесть](../../../architecture/cross-cutting/data-freshness.md). [свежесть](../../../architecture/cross-cutting/data-freshness.md).
## Границы доступа ## Границы доступа
Публикация проверяет connection revision и attempt в той же транзакции. Замена Публикация проверяет connection revision и attempt в той же транзакции.
или удаление подключения не допускает позднюю запись прежней работы. Frontend Замена или удаление подключения не допускает позднюю запись прежней работы.
очищает недоступный personal view и отклоняет запоздалые ответы. В режиме Frontend очищает недоступный personal view и отклоняет запоздалые ответы.
[«Все счета»](../all-accounts.md#полнота-свежесть-и-доступ) состав accounts и В режиме [«Все счета»](../all-accounts.md#полнота-свежесть-и-доступ) состав
snapshots читается согласованно, но их source times могут различаться. accounts и snapshots читается согласованно, но их source times могут различаться.
## Ошибки и результат для пользователя ## Ошибки и результат для пользователя
Нет подключения — `409 TBANK_NOT_CONNECTED`, известный отказ токена — Нет подключения — `409 TBANK_NOT_CONNECTED`, известный отказ токена —
`400 INVALID_TBANK_TOKEN`, недоступный счёт — `404`. Они не маскируются `400 INVALID_TBANK_TOKEN`, недоступный счёт — `404`. Они не маскируются
запасным snapshot. Временная ошибка часового обхода оставляет прежний полный запасным snapshot. Transient source failure оставляет прежний полный портфель
portfolio и его исходное `lastSuccessfulAt`; `meta.lastAttempt` сообщает о и его исходное время с безопасным предупреждением. При отсутствии snapshot
failed или partial попытке отдельно. `Portfolio.isStale` сообщает возраст показывается missing/failed, без нулевых итогов. `503` означает ошибку
snapshot, а Client Cached Snapshot и ошибка браузерного HTTP-перечитывания обслуживания HTTP-запроса, а не принятой фоновой работы.
имеют независимый смысл. При отсутствии полного snapshot `data: null` и
metadata обозначает initial loading либо временную недоступность, без нулевых
итогов. `503` означает ошибку обслуживания самого HTTP-запроса, а не
завершившуюся фоновую работу.
Денежные правила находятся в [справочнике](../data-reference.md), проверка Observer запрашивает статус известной работы, затем `sync=observe` читает
расхождения — в [диагностике](../how-to/investigate-discrepancy.md), RPC/лимиты изменившийся ресурс без запуска синхронизации. При уходе и terminal outcome
— в [интеграции T-Bank](../../../integrations/tbank.md). наблюдение прекращается. Повторное открытие может запросить недостающее;
после рестарта сохранённый snapshot доступен сразу.
Денежные правила находятся в [справочнике](../data-reference.md),
проверка расхождения — в [диагностике](../how-to/investigate-discrepancy.md),
RPC/лимиты — в [интеграции T-Bank](../../../integrations/tbank.md).
-17
View File
@@ -128,14 +128,6 @@ Instrument обогащают отображение Position или Event, но
_Не использовать_: бумага, тикер, актив позиции. _Не использовать_: бумага, тикер, актив позиции.
## Скринер акций
Подбор выпусков акций по заданным условиям из каталога T-Bank с указанием
времени получения списка. Скринер отличается от текстового поиска конкретного
инструмента, личного портфеля и подтверждения исполнимости сделки.
_Не использовать_: гарантия покупки, личная проверка доступа к сделке.
## Instrument Logo ## Instrument Logo
Имя файла бренда T-Bank для отображения Instrument в интерфейсе; Имя файла бренда T-Bank для отображения Instrument в интерфейсе;
@@ -160,15 +152,6 @@ _Не использовать_: валютная позиция, заблоки
_Не использовать_: история портфеля, snapshot для графика. _Не использовать_: история портфеля, snapshot для графика.
## Точка истории стоимости портфеля
Полная оценка одного Brokerage Account в одной валюте на момент успешного
обновления Portfolio; последовательность таких точек описывает изменение
оценки во времени, включая неторговые дни. Не является Portfolio Snapshot
или инвестиционной доходностью; отсутствие цены позиции не приравнивается к нулю.
_Не использовать_: снимок состава портфеля, доходность.
## Client Cached Snapshot ## Client Cached Snapshot
Технический термин: последний успешно полученный frontend-ответ React Query, который остаётся Технический термин: последний успешно полученный frontend-ответ React Query, который остаётся
+1 -12
View File
@@ -20,12 +20,6 @@ T-Bank определяет исходные данные и права token. B
сессию, выбирает подключение её пользователя и устанавливает доступ к счёту. сессию, выбирает подключение её пользователя и устанавливает доступ к счёту.
Наличие идентификатора счёта в URL этого доступа не подтверждает. Наличие идентификатора счёта в URL этого доступа не подтверждает.
Скринер акций получает полный online-каталог акций T-Bank для каждого нового
поиска. В него входят только инструменты, для которых каталог подтверждает и
доступность покупки, и торговли через API. Эти признаки описывают полученный
снимок каталога: они не подтверждают личные права пользователя, состояние торгов
или исполнение будущей заявки. Скринер не использует личный портфель как каталог.
[MOEX](moex.md) и [НКР](nkr.md) дополняют публичные сведения об облигациях; они не заменяют [MOEX](moex.md) и [НКР](nkr.md) дополняют публичные сведения об облигациях; они не заменяют
брокера как источник личных позиций и исполненных выплат. Границы источников брокера как источник личных позиций и исполненных выплат. Границы источников
показаны в [контексте](../architecture/context.md), смысл дополнений — в показаны в [контексте](../architecture/context.md), смысл дополнений — в
@@ -97,12 +91,7 @@ sequenceDiagram
`INVALID_TBANK_TOKEN` означает отказ внешней аутентификации или прав доступа. `INVALID_TBANK_TOKEN` означает отказ внешней аутентификации или прав доступа.
`TBANK_UNAVAILABLE` сообщает о временной или иной upstream-ошибке, включая `TBANK_UNAVAILABLE` сообщает о временной или иной upstream-ошибке, включая
исчерпание очереди. Диагностические причины жизненного цикла часовой попытки исчерпание очереди. Ответы и ошибки не содержат token. Удаление подключения
(`INTERRUPTED` — прерывание до нормального завершения, `DEADLINE` — истечение
ограничения ожидания шага) не относятся к ошибкам T-Bank: они остаются
внутренней диагностикой и не раскрываются в hourly-read. Истечение ограничения
ожидания не отменяет уже начатые обращения к T-Bank; их поздние результаты
остаются в строках шагов. Ответы и ошибки не содержат token. Удаление подключения
идемпотентно: удаляются локальный секрет, личные datasets и work; поздняя идемпотентно: удаляются локальный секрет, личные datasets и work; поздняя
публикация прежней revision запрещена. Общие неперсональные источники могут публикация прежней revision запрещена. Общие неперсональные источники могут
сохраняться. Оно не описывает отзыв token на стороне T-Bank. сохраняться. Оно не описывает отзыв token на стороне T-Bank.
+1 -1
View File
@@ -24,7 +24,7 @@ WealthLedger — локальное приложение для частного
| Посчитать полученные выплаты | [Пассивный доход](domains/income/overview.md): исполненные купоны и дивиденды за календарный период | | Посчитать полученные выплаты | [Пассивный доход](domains/income/overview.md): исполненные купоны и дивиденды за календарный период |
| Посмотреть ожидаемые события | [Будущие выплаты](domains/income/upcoming-events.md): купоны, дивиденды, погашения и оферты по текущим позициям | | Посмотреть ожидаемые события | [Будущие выплаты](domains/income/upcoming-events.md): купоны, дивиденды, погашения и оферты по текущим позициям |
| Увидеть общий состав инвестиций | Режим [«Все счета»](domains/portfolio/all-accounts.md), сохраняющий принадлежность строк к счетам | | Увидеть общий состав инвестиций | Режим [«Все счета»](domains/portfolio/all-accounts.md), сохраняющий принадлежность строк к счетам |
| Изучить конкретный инструмент | [Скринер акций](domains/instruments/stocks.md) по параметрам каталога, страницы акций и поиск [облигаций](domains/instruments/bonds.md): рынок, личная позиция, история и события | | Изучить конкретный инструмент | Поиск и страницы [акций](domains/instruments/stocks.md) и [облигаций](domains/instruments/bonds.md): рынок, личная позиция, история и события |
## Как связаны представления ## Как связаны представления
+482 -336
View File
File diff suppressed because it is too large Load Diff
+6 -12
View File
@@ -1,12 +1,12 @@
{ {
"name": "wealth-ledger-docs", "name": "wealth-ledger-docs",
"version": "0.1.4", "version": "0.1.2",
"private": true, "private": true,
"engines": { "engines": {
"node": "24.21.0" "node": "24.21.0"
}, },
"scripts": { "scripts": {
"lint": "markdownlint --dot --rules .markdownlint-rule-relative-links.cjs --ignore node_modules --ignore CHANGELOG.md -- \"**/*.md\" && markdownlint --config .markdownlint-changelog.json --rules .markdownlint-rule-relative-links.cjs -- CHANGELOG.md", "lint": "markdownlint-cli2 \"**/*.md\" \"#node_modules\"",
"verify": "npm run lint && git diff --check", "verify": "npm run lint && git diff --check",
"release:preview": "release-it --dry-run --ci", "release:preview": "release-it --dry-run --ci",
"release:prepare": "release-it --ci", "release:prepare": "release-it --ci",
@@ -17,19 +17,13 @@
"@commitlint/config-conventional": "21.2.2", "@commitlint/config-conventional": "21.2.2",
"@release-it/conventional-changelog": "12.0.0", "@release-it/conventional-changelog": "12.0.0",
"conventional-changelog-conventionalcommits": "10.4.0", "conventional-changelog-conventionalcommits": "10.4.0",
"markdownlint-cli": "0.49.1", "markdownlint-cli2": "0.23.2",
"markdownlint-rule-relative-links": "5.1.3", "markdownlint-rule-relative-links": "5.1.2",
"release-it": "21.0.2" "release-it": "21.0.2"
}, },
"overrides": { "overrides": {
"katex": "0.19.0", "markdownlint-cli2@0.23.2": {
"markdownlint-cli@0.49.1": { "smol-toml": "1.8.0"
"js-yaml": "5.4.2",
"smol-toml": "1.9.0"
},
"release-it": {
"basic-ftp": "6.2.1",
"undici": "7.29.1"
} }
} }
} }